Skip to content

Commit baeb5a0

Browse files
committed
add comments, simplify code and add missing cases
1 parent 7627258 commit baeb5a0

File tree

2 files changed

+60
-19
lines changed

2 files changed

+60
-19
lines changed

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

Lines changed: 25 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -106,31 +106,43 @@ object Signatures {
106106

107107
private def extractParamTypess(resultType: Type)(using Context): List[List[Type]] =
108108
resultType match {
109+
// Reference to a type which is not a type class
109110
case ref: TypeRef if !ref.symbol.isPrimitiveValueClass =>
110-
if (
111-
ref.symbol.asClass.baseClasses.contains(ctx.definitions.ProductClass) &&
112-
!ref.symbol.is(Flags.CaseClass)
113-
) || ref.symbol.isStaticOwner then
114-
val productAccessors = ref.memberDenots(
115-
underscoreMembersFilter,
116-
(name, buf) => buf += ref.member(name).asSingleDenotation
117-
)
118-
List(productAccessors.map(_.info.finalResultType).toList)
119-
else
120-
ref.symbol.primaryConstructor.paramInfo.paramInfoss
111+
getExtractorMembers(ref)
112+
// Option or Some applied type. There is special syntax for multiple returned arguments:
113+
// Option[TupleN] and Option[Seq],
114+
// We are not intrested in them, instead we extract proper type parameters from the Option type parameter.
121115
case AppliedType(TypeRef(_, cls), (appliedType @ AppliedType(tycon, args)) :: Nil)
122116
if (cls == ctx.definitions.OptionClass || cls == ctx.definitions.SomeClass) =>
123117
tycon match
124118
case TypeRef(_, cls) if cls == ctx.definitions.SeqClass => List(List(appliedType))
125119
case _ => List(args)
126-
case AppliedType(_, args) =>
127-
List(args)
120+
// Applied type extractor. We must extract from applied type to retain type parameters
121+
case appliedType: AppliedType => getExtractorMembers(appliedType)
122+
// This is necessary to extract proper result type as unapply can return other methods eg. apply
128123
case MethodTpe(_, _, resultType) =>
129124
extractParamTypess(resultType.widenDealias)
130125
case _ =>
131126
Nil
132127
}
133128

129+
// Returns extractors from given type. In case if there are no extractor methods it fallbacks to get method
130+
private def getExtractorMembers(resultType: Type)(using Context): List[List[Type]] =
131+
val productAccessors = resultType.memberDenots(
132+
underscoreMembersFilter,
133+
(name, buf) => buf += resultType.member(name).asSingleDenotation
134+
)
135+
val availableExtractors = if productAccessors.isEmpty then
136+
List(resultType.member(core.Names.termName("get")))
137+
else
138+
productAccessors
139+
List(availableExtractors.map(_.info.finalResultType.stripAnnots).toList)
140+
141+
object underscoreMembersFilter extends NameFilter {
142+
def apply(pre: Type, name: Name)(using Context): Boolean = name.startsWith("_")
143+
def isStable = true
144+
}
145+
134146
def toSignature(denot: SingleDenotation)(using Context): Option[Signature] = {
135147
val symbol = denot.symbol
136148
val docComment = ParsedComment.docOf(symbol)
@@ -216,11 +228,6 @@ object Signatures {
216228
}
217229
}
218230

219-
object underscoreMembersFilter extends NameFilter {
220-
def apply(pre: Type, name: Name)(using Context): Boolean = name.startsWith("_")
221-
def isStable = true
222-
}
223-
224231
/**
225232
* The number of parameters that are applied in `tree`.
226233
*

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

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,24 @@ class SignatureHelpTest {
154154
.signatureHelp(m2, List(signature), Some(0), 1)
155155
}
156156

157+
@Test def productTypeClassMatch: Unit = {
158+
val signature = S("", Nil, List(List(P("", "String"), P("", "String"))), None)
159+
160+
code"""class FirstChars[A](s: A) extends Product:
161+
| def _1 = s
162+
| def _2 = s
163+
|
164+
|object FirstChars:
165+
| def unapply(s: String): FirstChars[String] = new FirstChars(s)
166+
|
167+
|object Test:
168+
| "Hi!" match
169+
| case FirstChars(ch${m1}, ch${m2}) => ???
170+
"""
171+
.signatureHelp(m1, List(signature), Some(0), 0)
172+
.signatureHelp(m2, List(signature), Some(0), 1)
173+
}
174+
157175
@Test def nameBasedMatch: Unit = {
158176
val signature = S("", Nil, List(List(P("", "Int"), P("", "String"))), None)
159177

@@ -173,6 +191,22 @@ class SignatureHelpTest {
173191
.signatureHelp(m2, List(signature), Some(0), 1)
174192
}
175193

194+
@Test def getObjectMatch: Unit = {
195+
val signature = S("", Nil, List(List(P("", "String"))), None)
196+
197+
code"""object ProdEmpty:
198+
| def isEmpty = true
199+
| def unapply(s: String): this.type = this
200+
| def get: String = ""
201+
|
202+
|object Test:
203+
| "" match
204+
| case ProdEmpty(${m1}) => ???
205+
| case _ => ()
206+
"""
207+
.signatureHelp(m1, List(signature), Some(0), 0)
208+
}
209+
176210
@Test def sequenceMatch: Unit = {
177211
val signature = S("", Nil, List(List(P("", "Seq[Char]"))), None)
178212

@@ -212,7 +246,7 @@ class SignatureHelpTest {
212246
}
213247

214248
@Test def productSequenceMatchForCaseClass: Unit = {
215-
val signature = S("", Nil, List(List(P("name", "String"), P("children", "Int*"))), None)
249+
val signature = S("", Nil, List(List(P("name", "String"), P("children", "Seq[Int]"))), None)
216250

217251
code"""case class Foo(val name: String, val children: Int*)
218252
|

0 commit comments

Comments
 (0)