Skip to content

Commit db8b2a3

Browse files
committed
fix unapplySeq parameter position
1 parent 4c05795 commit db8b2a3

File tree

2 files changed

+89
-48
lines changed

2 files changed

+89
-48
lines changed

compiler/src/dotty/tools/dotc/util/Signatures.scala

Lines changed: 58 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ import core.Names._
1111
import core.Types._
1212
import util.Spans.Span
1313
import reporting._
14-
import dotty.tools.dotc.core.NameKinds
1514

1615

1716
object Signatures {
@@ -49,11 +48,12 @@ object Signatures {
4948
* being called, the list of overloads of this function).
5049
*/
5150
def callInfo(path: List[tpd.Tree], span: Span)(using Context): (Int, Int, List[SingleDenotation]) =
52-
val enclosingApply = path.find {
53-
case Apply(fun, _) => !fun.span.contains(span)
54-
case UnApply(fun, _, _) => !fun.span.contains(span)
55-
case _ => false
56-
}
51+
val enclosingApply = path.dropWhile {
52+
case Apply(fun, _) => fun.span.contains(span)
53+
case unapply @ UnApply(fun, _, _) =>
54+
fun.span.contains(span) || ctx.definitions.isTupleClass(unapply.fun.symbol.owner.companionClass)
55+
case _ => true
56+
}.headOption
5757

5858
enclosingApply.map {
5959
case UnApply(fun, _, patterns) => unapplyCallInfo(span, fun, patterns)
@@ -85,12 +85,50 @@ object Signatures {
8585

8686
(paramIndex, alternativeIndex, alternatives)
8787

88-
private def unapplyCallInfo(span: Span,
88+
private def unapplyCallInfo(
89+
span: Span,
8990
fun: tpd.Tree,
9091
patterns: List[tpd.Tree]
9192
)(using Context): (Int, Int, List[SingleDenotation]) =
92-
val paramIndex = patterns.indexWhere(_.span.contains(span)) max 0
93-
(paramIndex, 0, fun.symbol.asSingleDenotation.mapInfo(_ => fun.tpe) :: Nil)
93+
val patternPosition = patterns.indexWhere(_.span.contains(span))
94+
val activeParameter = extractParamTypess(fun.tpe.finalResultType.widen).headOption.map { params =>
95+
if patternPosition == -1 then
96+
if patterns.length > 0 then
97+
(params.size - 1)
98+
else
99+
0
100+
else
101+
(params.size - 1) min patternPosition
102+
}.getOrElse(patternPosition)
103+
104+
(activeParameter, 0, fun.symbol.asSingleDenotation.mapInfo(_ => fun.tpe) :: Nil)
105+
106+
private def extractParamTypess(resultType: Type)(using Context): List[List[Type]] =
107+
resultType match {
108+
case ref: TypeRef if !ref.symbol.isPrimitiveValueClass =>
109+
if (
110+
ref.symbol.asClass.baseClasses.contains(ctx.definitions.ProductClass) &&
111+
!ref.symbol.is(Flags.CaseClass)
112+
) || ref.symbol.isStaticOwner then
113+
val productAccessors = ref.memberDenots(
114+
underscoreMembersFilter,
115+
(name, buf) => buf += ref.member(name).asSingleDenotation
116+
)
117+
List(productAccessors.map(_.info.finalResultType).toList)
118+
else
119+
ref.symbol.primaryConstructor.paramInfo.paramInfoss
120+
case AppliedType(TypeRef(_, cls), (appliedType @ AppliedType(tycon, args)) :: Nil)
121+
if (cls == ctx.definitions.OptionClass || cls == ctx.definitions.SomeClass) =>
122+
tycon match
123+
case TypeRef(_, cls) if cls == ctx.definitions.SeqClass => List(List(appliedType))
124+
case _ => List(args)
125+
case AppliedType(_, args) =>
126+
List(args)
127+
case MethodTpe(_, _, resultType) =>
128+
extractParamTypess(resultType.widenDealias)
129+
case _ =>
130+
Nil
131+
}
94132

95133
def toSignature(denot: SingleDenotation)(using Context): Option[Signature] = {
96134
val symbol = denot.symbol
@@ -111,7 +149,8 @@ object Signatures {
111149
Nil
112150
}
113151
val params = tp.paramNames.zip(tp.paramInfos).map { case (name, info) =>
114-
Signatures.Param(name.show,
152+
Signatures.Param(
153+
name.show,
115154
info.widenTermRefExpr.show,
116155
docComment.flatMap(_.paramDoc(name)),
117156
isImplicit = tp.isImplicitMethod)
@@ -123,45 +162,22 @@ object Signatures {
123162
/**
124163
* This function is a hack which allows Signatures API to remain unchanged
125164
*
126-
* @return true if denot is "unapply", false otherwise
165+
* @return true if denot is "unapply" or "unapplySeq", false otherwise
127166
*/
128-
def isUnapplyDenotation: Boolean = List(core.Names.termName("unapply"), core.Names.termName("unapplySeq")) contains denot.name
167+
def isUnapplyDenotation: Boolean =
168+
List(core.Names.termName("unapply"), core.Names.termName("unapplySeq")) contains denot.name
129169

130170
def extractParamNamess(resultType: Type): List[List[Name]] =
131-
if resultType.resultType.widen.typeSymbol.flags.is(Flags.CaseClass) && symbol.flags.is(Flags.Synthetic) then
132-
resultType.resultType.widen.typeSymbol.primaryConstructor.paramInfo.paramNamess
171+
if resultType.typeSymbol.flags.is(Flags.CaseClass) && symbol.flags.is(Flags.Synthetic) then
172+
resultType.typeSymbol.primaryConstructor.paramInfo.paramNamess
133173
else
134174
Nil
135175

136-
def extractParamTypess(resultType: Type): List[List[Type]] =
137-
resultType match {
138-
case ref: TypeRef if !ref.symbol.isPrimitiveValueClass =>
139-
if (
140-
ref.symbol.asClass.baseClasses.contains(ctx.definitions.ProductClass) &&
141-
!ref.symbol.is(Flags.CaseClass)
142-
) || ref.symbol.isStaticOwner then
143-
val productAccessors = ref.memberDenots(
144-
underscoreMembersFilter,
145-
(name, buf) => buf += ref.member(name).asSingleDenotation
146-
)
147-
List(productAccessors.map(_.info.finalResultType).toList)
148-
else
149-
ref.symbol.primaryConstructor.paramInfo.paramInfoss
150-
case AppliedType(TypeRef(_, cls), (appliedType @ AppliedType(tycon, args)) :: Nil)
151-
if (cls == ctx.definitions.OptionClass || cls == ctx.definitions.SomeClass) =>
152-
tycon match
153-
case TypeRef(_, cls) if cls == ctx.definitions.SeqClass => List(List(appliedType))
154-
case _ => List(args)
155-
case AppliedType(_, args) =>
156-
List(args)
157-
case MethodTpe(_, _, resultType) =>
158-
extractParamTypess(resultType.widenDealias)
159-
case _ =>
160-
Nil
161-
}
162-
163176
def toUnapplyParamss(method: Type)(using Context): List[Param] = {
164-
val resultType = method.finalResultType.widenDealias
177+
val resultType = method.finalResultType.widenDealias match
178+
case methodType: MethodType => methodType.resultType.widen
179+
case other => other
180+
165181
val paramNames = extractParamNamess(resultType).flatten
166182
val paramTypes = extractParamTypess(resultType).flatten
167183

language-server/test/dotty/tools/languageserver/SignatureHelpTest.scala

Lines changed: 31 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -146,15 +146,15 @@ class SignatureHelpTest {
146146
| println("Expected *exactly* 7 characters!")
147147
"""
148148
.signatureHelp(m1, List(signature), Some(0), 0)
149-
.signatureHelp(m2, List(signature), Some(0), 1)
150-
.signatureHelp(m3, List(signature), Some(0), 2)
151-
.signatureHelp(m4, List(signature), Some(0), 6)
149+
.signatureHelp(m2, List(signature), Some(0), 0)
150+
.signatureHelp(m3, List(signature), Some(0), 0)
151+
.signatureHelp(m4, List(signature), Some(0), 0)
152152
}
153153

154154
@Test def productSequenceMatch: Unit = {
155155
val signature = S("", Nil, List(List(P("", "String"), P("", "Seq[Int]"))), None)
156156

157-
code"""class Foo(val name: String, val children: Int *)
157+
code"""class Foo(val name: String, val children: Int*)
158158
|object Foo:
159159
| def unapplySeq(f: Foo): Option[(String, Seq[Int])] =
160160
| Some((f.name, f.children))
@@ -167,10 +167,26 @@ class SignatureHelpTest {
167167
.signatureHelp(m2, List(signature), Some(0), 1)
168168
.signatureHelp(m3, List(signature), Some(0), 0)
169169
.signatureHelp(m4, List(signature), Some(0), 1)
170-
.signatureHelp(m5, List(signature), Some(0), 2)
171-
.signatureHelp(m6, List(signature), Some(0), 3)
170+
.signatureHelp(m5, List(signature), Some(0), 1)
171+
.signatureHelp(m6, List(signature), Some(0), 1)
172172
}
173173

174+
@Test def productSequenceMatchForCaseClass: Unit = {
175+
val signature = S("", Nil, List(List(P("name", "String"), P("children", "Int*"))), None)
176+
177+
code"""case class Foo(val name: String, val children: Int*)
178+
|
179+
|def foo(f: Foo) = f match
180+
| case Foo(na${m1}e, n${m2} : _*) =>
181+
| case Foo(nam${m3}e, ${m4}x, ${m5}y, n${m6}s : _*) =>
182+
"""
183+
.signatureHelp(m1, List(signature), Some(0), 0)
184+
.signatureHelp(m2, List(signature), Some(0), 1)
185+
.signatureHelp(m3, List(signature), Some(0), 0)
186+
.signatureHelp(m4, List(signature), Some(0), 1)
187+
.signatureHelp(m5, List(signature), Some(0), 1)
188+
.signatureHelp(m6, List(signature), Some(0), 1)
189+
}
174190

175191
@Test def unapplyManyType: Unit = {
176192
val signature = S("", Nil, List(List(P("", "Int"), P("", "String"))), None)
@@ -204,6 +220,15 @@ class SignatureHelpTest {
204220
.signatureHelp(m2, List(signature), Some(0), 1)
205221
}
206222

223+
@Test def noUnapplyForTuple: Unit = {
224+
code"""object Main {
225+
| (1, 2) match
226+
| case (x${m1}, ${m2}) =>
227+
|}"""
228+
.signatureHelp(m1, Nil, Some(0), 0)
229+
.signatureHelp(m2, Nil, Some(0), 0)
230+
}
231+
207232
@Test def unapplyCaseClass: Unit = {
208233
val signature = S("", Nil, List(List(P("a", "Int"), P("b", "String"))), None)
209234

0 commit comments

Comments
 (0)