@@ -9,6 +9,7 @@ import core.Denotations.{SingleDenotation, Denotation}
99import core .Flags
1010import core .NameOps .isUnapplyName
1111import core .Names ._
12+ import core .NameKinds
1213import core .Types ._
1314import core .Symbols .NoSymbol
1415import interactive .Interactive
@@ -54,7 +55,8 @@ object Signatures {
5455 *
5556 * @param path The path to the function application
5657 * @param span The position of the cursor
57- * @return A triple containing the index of the parameter being edited, the index of functeon
58+ *
59+ * @return A triple containing the index of the parameter being edited, the index of functeon
5860 * being called, the list of overloads of this function).
5961 */
6062 def signatureHelp (path : List [tpd.Tree ], pos : Span )(using Context ): (Int , Int , List [Signature ]) =
@@ -65,7 +67,8 @@ object Signatures {
6567 *
6668 * @param path The path to the function application
6769 * @param span The position of the cursor
68- * @return A triple containing the index of the parameter being edited, the index of the function
70+ *
71+ * @return A triple containing the index of the parameter being edited, the index of the function
6972 * being called, the list of overloads of this function).
7073 */
7174 @ deprecated(
@@ -82,49 +85,76 @@ object Signatures {
8285 *
8386 * @param path The path to the function application
8487 * @param span The position of the cursor
88+ *
8589 * @return A triple containing the index of the parameter being edited, the index of the function
8690 * being called, the list of overloads of this function).
8791 */
8892 def computeSignatureHelp (path : List [tpd.Tree ], span : Span )(using Context ): (Int , Int , List [Signature ]) =
8993 findEnclosingApply(path, span) match
90- case tpd.EmptyTree => (0 , 0 , Nil )
9194 case Apply (fun, params) => applyCallInfo(span, params, fun)
9295 case UnApply (fun, _, patterns) => unapplyCallInfo(span, fun, patterns)
96+ case appliedTypeTree @ AppliedTypeTree (_, types) => appliedTypeTreeCallInfo(appliedTypeTree, types)
97+ case tp @ TypeApply (fun, types) => applyCallInfo(span, types, fun, true )
98+ case _ => (0 , 0 , Nil )
9399
94100 /**
95101 * Finds enclosing application from given `path` for `span`.
96102 *
97103 * @param path The path to the function application
98104 * @param span The position of the cursor
105+ *
99106 * @return Tree which encloses closest application containing span.
100107 * In case if cursor is pointing on closing parenthesis and
101108 * next subsequent application exists, it returns the latter
102109 */
103110 private def findEnclosingApply (path : List [tpd.Tree ], span : Span )(using Context ): tpd.Tree =
104111 path.filterNot {
105- case apply @ Apply (fun, _) => fun.span.contains(span) || isTuple(apply)
106- case unapply @ UnApply (fun, _, _) => fun.span.contains(span) || isTuple(unapply)
112+ case apply @ Apply (fun, _) => fun.span.contains(span) || isValid(apply)
113+ case unapply @ UnApply (fun, _, _) => fun.span.contains(span) || isValid(unapply)
114+ case typeTree @ AppliedTypeTree (fun, _) => fun.span.contains(span) || isValid(typeTree)
115+ case typeApply @ TypeApply (fun, _) => fun.span.contains(span) || isValid(typeApply)
107116 case _ => true
108117 } match {
109118 case Nil => tpd.EmptyTree
110- case direct :: enclosing :: _ if direct.source(span.end - 1 ) == ')' => enclosing
119+ case direct :: enclosing :: _ if isClosingSymbol( direct.source(span.end - 1 )) => enclosing
111120 case direct :: _ => direct
112121 }
113122
123+ private def isClosingSymbol (ch : Char ) = ch == ')' || ch == ']'
124+
114125 /**
115- * Extracts call information for a function application.
126+ * Extracts call information for applied type tree:
116127 *
117- * @param span The position of the cursor
118- * @param params Current function parameters
119- * @param fun Function tree which is being applied
128+ * @param types Currently applied function type parameters
129+ * @param fun Function tree which is being applied
130+ */
131+ private def appliedTypeTreeCallInfo (
132+ fun : tpd.Tree ,
133+ types : List [tpd.Tree ]
134+ )(using Context ): (Int , Int , List [Signature ]) =
135+ val typeName = fun.symbol.name.show
136+ val typeParams = fun.symbol.typeRef.typeParams.map(_.paramName.show)
137+ val denot = fun.denot.asSingleDenotation
138+ val activeParameter = (types.length - 1 ) max 0
139+
140+ val signature = Signature (typeName, typeParams, Nil , Some (typeName) , None , Some (denot))
141+ (activeParameter, 0 , List (signature))
142+
143+ /**
144+ * Extracts call information for a function application and type application.
120145 *
146+ * @param span The position of the cursor
147+ * @param params Current function parameters
148+ * @param fun Function tree which is being applied
149+ * @param isTypeApply Is a type application
121150 * @return A triple containing the index of the parameter being edited, the index of the function
122151 * being called, the list of overloads of this function).
123152 */
124153 private def applyCallInfo (
125154 span : Span ,
126155 params : List [tpd.Tree ],
127- fun : tpd.Tree
156+ fun : tpd.Tree ,
157+ isTypeApply : Boolean = false
128158 )(using Context ): (Int , Int , List [Signature ]) =
129159 def treeQualifier (tree : tpd.Tree ): tpd.Tree = tree match
130160 case Apply (qual, _) => treeQualifier(qual)
@@ -154,10 +184,11 @@ object Signatures {
154184
155185 if alternativeIndex < alternatives.length then
156186 val curriedArguments = countParams(fun, alternatives(alternativeIndex))
157- val paramIndex = params.indexWhere(_.span.contains(span)) match {
158- case - 1 => (params.length - 1 max 0 ) + curriedArguments
159- case n => n + curriedArguments
160- }
187+ // We have to shift all arguments by number of type parameters to properly show activeParameter index
188+ val typeParamsShift = if ! isTypeApply then fun.symbol.denot.paramSymss.flatten.filter(_.isType).length else 0
189+ val paramIndex = params.indexWhere(_.span.contains(span)) match
190+ case - 1 => (params.length - 1 max 0 ) + curriedArguments + typeParamsShift
191+ case n => n + curriedArguments + typeParamsShift
161192
162193 val pre = treeQualifier(fun)
163194 val alternativesWithTypes = alternatives.map(_.asSeenFrom(pre.tpe.widenTermRefExpr))
@@ -170,9 +201,9 @@ object Signatures {
170201 /**
171202 * Extracts call informatioin for function in unapply context.
172203 *
173- * @param span The position of the cursor
204+ * @param span The position of the cursor
174205 * @param params Current function parameters
175- * @param fun Unapply function tree
206+ * @param fun Unapply function tree
176207 *
177208 * @return A triple containing the index of the parameter being edited, the index of the function
178209 * being called, the list of overloads of this function).
@@ -202,7 +233,8 @@ object Signatures {
202233 * Extract parameter names from `resultType` only if `resultType` is case class and `denot` is synthetic.
203234 *
204235 * @param resultType Function result type
205- * @param denot Function denotation
236+ * @param denot Function denotation
237+ *
206238 * @return List of lists of names of parameters if `resultType` is case class without overriden unapply
207239 */
208240 private def extractParamNamess (resultType : Type , denot : Denotation )(using Context ): List [List [Name ]] =
@@ -214,9 +246,10 @@ object Signatures {
214246 /**
215247 * Extract parameter types from `resultType` in unapply context.
216248 *
217- * @param resultType Function result type
218- * @param denot Function denotation
249+ * @param resultType Function result type
250+ * @param denot Function denotation
219251 * @param patternsSize Number of pattern trees present in function tree
252+ *
220253 * @return List of lists of types present in unapply clause
221254 */
222255 private def extractParamTypess (
@@ -263,13 +296,19 @@ object Signatures {
263296 case (- 1 , pos) => - 1 // there are patterns, we must be outside range so we set no active parameter
264297 case _ => (maximumParams - 1 ) min patternPosition max 0 // handle unapplySeq to always highlight Seq[A] on elements
265298
266- private def isTuple (tree : tpd.Tree )(using Context ): Boolean =
267- tree.symbol != NoSymbol && ctx.definitions.isTupleClass(tree.symbol.owner.companionClass)
299+ /**
300+ * Checks if tree is valid for signatureHelp. Skipped trees are either tuple type or function type
301+ *
302+ * @param tree tree to validate
303+ */
304+ private def isValid (tree : tpd.Tree )(using Context ): Boolean =
305+ ctx.definitions.isTupleNType(tree.tpe) || ctx.definitions.isFunctionType(tree.tpe)
268306
269307 /**
270308 * Get unapply method result type omiting unknown types and another method calls.
271309 *
272310 * @param fun Unapply tree
311+ *
273312 * @return Proper unapply method type after extracting result from method types and omiting unknown types.
274313 */
275314 private def unapplyMethodResult (fun : tpd.Tree )(using Context ): Type =
@@ -292,9 +331,10 @@ object Signatures {
292331 *
293332 * @see [[https://docs.scala-lang.org/scala3/reference/changed-features/pattern-matching.html ]]
294333 *
295- * @param resultType Final result type for unapply
334+ * @param resultType Final result type for unapply
296335 * @param patternCount Currently applied patterns to unapply function
297336 * @param isUnapplySeq true if unapply name equals "unapplySeq", false otherwise
337+ *
298338 * @return List of List of types dependent on option less extractor type.
299339 */
300340 private def mapOptionLessUnapply (
@@ -333,40 +373,63 @@ object Signatures {
333373 * Creates signature for apply method.
334374 *
335375 * @param denot Function denotation for which apply signature is returned.
376+ *
336377 * @return Signature if denot is a function, None otherwise
337378 */
338379 private def toApplySignature (denot : SingleDenotation )(using Context ): Option [Signature ] = {
339380 val symbol = denot.symbol
340381 val docComment = ParsedComment .docOf(symbol)
341382
342- def toParamss (tp : MethodType )(using Context ): List [List [Param ]] = {
343- val rest = tp.resType match {
344- case res : MethodType =>
345- // Hide parameter lists consisting only of DummyImplicit,
346- if (res.resultType.isParameterless &&
347- res.isImplicitMethod &&
348- res.paramInfos.forall(info =>
349- info.classSymbol.derivesFrom(ctx.definitions.DummyImplicitClass )))
350- Nil
351- else
352- toParamss(res)
383+ def isDummyImplicit (res : MethodType ): Boolean =
384+ res.resultType.isParameterless &&
385+ res.isImplicitMethod &&
386+ res.paramInfos.forall(info =>
387+ info.classSymbol.derivesFrom(ctx.definitions.DummyImplicitClass ))
388+
389+ def toParamss (tp : Type )(using Context ): List [List [Param ]] =
390+ val rest = tp.resultType match
391+ case res : MethodType => if isDummyImplicit(res) then Nil else toParamss(res)
392+ case _ => Nil
393+
394+ val currentParams = (tp.paramNamess, tp.paramInfoss) match
395+ case (params :: _, infos :: _) => params zip infos
353396 case _ => Nil
354- }
355- val params = tp.paramNames.zip(tp.paramInfos).map { case (name, info) =>
356- Signatures .Param (
357- name.show,
358- info.widenTermRefExpr.show,
359- docComment.flatMap(_.paramDoc(name)),
360- isImplicit = tp.isImplicitMethod)
361- }
362- params :: rest
363- }
397+
398+ val params = currentParams.map { (name, info) =>
399+ Signatures .Param (
400+ name.show,
401+ info.widenTermRefExpr.show,
402+ docComment.flatMap(_.paramDoc(name)),
403+ isImplicit = tp.isImplicitMethod
404+ )
405+ }
406+
407+ (params :: rest)
408+
409+ def isSyntheticEvidence (name : String ) =
410+ if ! name.startsWith(NameKinds .EvidenceParamName .separator) then false else
411+ symbol.paramSymss.flatten.find(_.name.show == name).exists(_.flags.is(Flags .Implicit ))
364412
365413 denot.info.stripPoly match
366- case tpe : MethodType =>
367- val paramss = toParamss(tpe)
414+ case tpe : (MethodType | AppliedType | TypeRef | TypeParamRef ) =>
415+ val paramss = toParamss(tpe).map(_.filterNot(param => isSyntheticEvidence(param.name)))
416+ val evidenceParams = (tpe.paramNamess.flatten zip tpe.paramInfoss.flatten).flatMap {
417+ case (name, AppliedType (tpe, (ref : TypeParamRef ) :: _)) if isSyntheticEvidence(name.show) =>
418+ Some (ref.paramName -> tpe)
419+ case _ => None
420+ }
421+
368422 val typeParams = denot.info match
369- case poly : PolyType => poly.paramNames.zip(poly.paramInfos).map { case (x, y) => x.show + y.show }
423+ case poly : PolyType =>
424+ val tparams = poly.paramNames.zip(poly.paramInfos)
425+ tparams.map { (name, info) =>
426+ evidenceParams.find((evidenceName : TypeName , _ : Type ) => name == evidenceName).flatMap {
427+ case (_, tparam) => tparam.show.split('.' ).lastOption
428+ } match {
429+ case Some (evidenceTypeName) => s " ${name.show}: ${evidenceTypeName}"
430+ case None => name.show + info.show
431+ }
432+ }
370433 case _ => Nil
371434 val (name, returnType) =
372435 if (symbol.isConstructor) then
@@ -400,10 +463,11 @@ object Signatures {
400463 * where _$n is only present for synthetic product extractors such as case classes.
401464 * In rest of cases signature skips them resulting in pattern (T1, T2, T3, Tn)
402465 *
403- * @param denot Unapply denotation
466+ * @param denot Unapply denotation
404467 * @param paramNames Parameter names for unapply final result type.
405- * It non empty only when unapply returns synthetic product as for case classes.
468+ * It non empty only when unapply returns synthetic product as for case classes.
406469 * @param paramTypes Parameter types for unapply final result type.
470+ *
407471 * @return Signature if paramTypes is non empty, None otherwise
408472 */
409473 private def toUnapplySignature (denot : SingleDenotation , paramNames : List [Name ], paramTypes : List [Type ])(using Context ): Option [Signature ] =
@@ -425,17 +489,17 @@ object Signatures {
425489 * `countParams` should be 3. It also takes into considerations unapplied arguments so for `foo(1)(3)`
426490 * we will still get 3, as first application `foo(1)` takes 2 parameters with currently only 1 applied.
427491 *
428- * @param tree The tree to inspect.
429- * @param denot Denotation of function we are trying to apply
492+ * @param tree The tree to inspect.
493+ * @param denot Denotation of function we are trying to apply
430494 * @param alreadyCurried Number of subsequent Apply trees before current tree
495+ *
431496 * @return The number of parameters that are passed.
432497 */
433498 private def countParams (tree : tpd.Tree , denot : SingleDenotation , alreadyCurried : Int = 0 )(using Context ): Int =
434- tree match {
499+ tree match
435500 case Apply (fun, params) =>
436501 countParams(fun, denot, alreadyCurried + 1 ) + denot.symbol.paramSymss(alreadyCurried).length
437502 case _ => 0
438- }
439503
440504 /**
441505 * Inspect `err` to determine, if it is an error related to application of an overloaded
@@ -447,6 +511,7 @@ object Signatures {
447511 *
448512 * @param err The error message to inspect.
449513 * @param params The parameters that were given at the call site.
514+ *
450515 * @return A pair composed of the index of the best alternative (0 if no alternatives
451516 * were found), and the list of alternatives.
452517 */
@@ -460,20 +525,18 @@ object Signatures {
460525 // If the user writes `foo(bar, <cursor>)`, the typer will insert a synthetic
461526 // `null` parameter: `foo(bar, null)`. This may influence what's the "best"
462527 // alternative, so we discard it.
463- val userParams = params match {
528+ val userParams = params match
464529 case xs :+ (nul @ Literal (Constant (null ))) if nul.span.isZeroExtent => xs
465530 case _ => params
466- }
467531 val userParamsTypes = userParams.map(_.tpe)
468532
469533 // Assign a score to each alternative (how many parameters are correct so far), and
470534 // use that to determine what is the current active signature.
471535 val alternativesScores = alternatives.map { alt =>
472- alt.info.stripPoly match {
536+ alt.info.stripPoly match
473537 case tpe : MethodType =>
474538 userParamsTypes.zip(tpe.paramInfos).takeWhile{ case (t0, t1) => t0 <:< t1 }.size
475539 case _ => 0
476- }
477540 }
478541 val bestAlternative =
479542 if (alternativesScores.isEmpty) 0
0 commit comments