Skip to content

Commit 1238df7

Browse files
committed
fix: use asSeenFrom to correctly instantiate type parameters in Signatures.scala
1 parent e78c5de commit 1238df7

File tree

3 files changed

+144
-207
lines changed

3 files changed

+144
-207
lines changed

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

Lines changed: 63 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -246,8 +246,14 @@ object Signatures {
246246

247247
val pre = treeQualifier(fun)
248248
val alternativesWithTypes = alternatives.map(_.asSeenFrom(pre.tpe))
249+
val typeArgs = extractTypeArgs(fun)
250+
def isBoring(tp: Type) =
251+
val t = tp.widenTermRefExpr
252+
t.isRef(ctx.definitions.AnyClass) || t.isRef(ctx.definitions.NothingClass) || t.isRef(ctx.definitions.NullClass)
253+
254+
val usefulTypeArgs = if typeArgs.flatten.exists(!isBoring(_)) then typeArgs else Nil
249255
val alternativeSignatures = alternativesWithTypes
250-
.flatMap(toApplySignature(_, findOutermostCurriedApply(untpdPath), safeParamssListIndex))
256+
.flatMap(denot => toApplySignature(denot, findOutermostCurriedApply(untpdPath), usefulTypeArgs))
251257

252258
val totalParamCount = alternativeSymbol.paramSymss.foldLeft(0)(_ + _.length)
253259
val finalParamIndex =
@@ -471,12 +477,9 @@ object Signatures {
471477
*
472478
* @return Signature if denot is a function, None otherwise
473479
*/
474-
private def toApplySignature(
475-
denot: SingleDenotation,
476-
untpdFun: Option[untpd.GenericApply],
477-
paramssIndex: Int
478-
)(using Context): Option[Signature] = {
480+
private def toApplySignature(denot: SingleDenotation, untpdFun: Option[untpd.GenericApply], typeArgs: List[List[Type]] = Nil)(using Context): Option[Signatures.Signature] = {
479481
val symbol = denot.symbol
482+
val info = denot.info
480483
val docComment = ParsedComment.docOf(symbol)
481484

482485
def isDummyImplicit(res: MethodType): Boolean =
@@ -495,11 +498,6 @@ object Signatures {
495498
case untpd.GenericApply(fun: untpd.GenericApply, args) => toApplyList(fun) :+ tree
496499
case _ => List(tree)
497500

498-
def toMethodTypeList(tpe: Type): List[Type] =
499-
tpe.resultType match
500-
case res: MethodOrPoly => toMethodTypeList(res) :+ tpe
501-
case res => List(tpe)
502-
503501
def isSyntheticEvidence(name: String) =
504502
name.startsWith(NameKinds.ContextBoundParamName.separator)
505503
&& symbol.paramSymss.flatten.find(_.name.show == name).exists(_.flags.isOneOf(Flags.GivenOrImplicit))
@@ -518,33 +516,49 @@ object Signatures {
518516
case Some(evidenceTypeName) => TypeParam(s"${name.show}: ${evidenceTypeName}")
519517
case None => TypeParam(name.show + info.show)
520518

521-
def toParamss(tp: Type, fun: Option[untpd.GenericApply])(using Context): List[List[Param]] =
519+
def toParamss(tp: Type, fun: Option[untpd.GenericApply], typeArgs: List[List[Type]])(using Context): List[List[Param]] =
522520
val paramSymss = symbol.paramSymss
521+
val applies = fun.map(toApplyList).getOrElse(Nil)
523522

524-
def reduceToParamss(applies: List[untpd.Tree], types: List[Type], paramList: Int = 0): List[List[Param]] =
525-
applies -> types match
526-
case ((_: untpd.TypeApply) :: restTrees, (poly: PolyType) :: restTypes) =>
527-
toTypeParam(poly) :: reduceToParamss(restTrees, restTypes, paramList + 1)
528-
case (restTrees, (poly: PolyType) :: restTypes) =>
529-
toTypeParam(poly) :: reduceToParamss(restTrees, restTypes, paramList + 1)
530-
case ((apply: untpd.GenericApply) :: other, tpe :: otherType) =>
531-
toParams(tpe, Some(apply), paramList) :: reduceToParamss(other, otherType, paramList + 1)
532-
case (other, (tpe @ MethodTpe(names, _, _)) :: otherType) if !isDummyImplicit(tpe) =>
533-
toParams(tpe, None, paramList) :: reduceToParamss(other, otherType, paramList + 1)
534-
case _ => Nil
523+
def loop(tp: Type, applies: List[untpd.Tree], typeArgs: List[List[Type]], paramListIndex: Int): List[List[Param]] = tp match
524+
case pt: PolyType =>
525+
val (visualParams, nextTp, nextTypeArgs) = typeArgs match
526+
case head :: tail =>
527+
(head.map(arg => TypeParam(arg.show)), pt.appliedTo(head), tail)
528+
case _ =>
529+
(toTypeParam(pt), pt.resType, Nil)
530+
531+
val nextApplies = applies match
532+
case (head: untpd.TypeApply) :: tail => tail
533+
case _ => applies
534+
535+
visualParams :: loop(nextTp, nextApplies, nextTypeArgs, paramListIndex + 1)
536+
537+
case mt: MethodType =>
538+
if isDummyImplicit(mt) then
539+
loop(mt.resType, applies, typeArgs, paramListIndex + 1)
540+
else
541+
val (currentApply, nextApplies) = applies match
542+
case (head: untpd.GenericApply) :: tail => (Some(head), tail)
543+
case _ => (None, applies)
544+
545+
toParams(mt, currentApply, paramListIndex) :: loop(mt.resType, nextApplies, typeArgs, paramListIndex + 1)
546+
547+
case _ => Nil
535548

536549
def toParams(tp: Type, apply: Option[untpd.GenericApply], paramList: Int)(using Context): List[Param] =
537550
val currentParams = (paramSymss.lift(paramList), tp.paramInfoss.headOption) match
538551
case (Some(params), Some(infos)) => params zip infos
539552
case _ => Nil
540553

541-
val params = currentParams.map: (symbol, info) =>
554+
val params = currentParams.zipWithIndex.map: (p, index) =>
555+
val (symbol, info) = p
542556
// TODO after we migrate ShortenedTypePrinter into the compiler, it should rely on its api
543557
val name = if symbol.isAllOf(Flags.Given | Flags.Param) && symbol.name.startsWith("x$") then nme.EMPTY else symbol.name.asTermName
544558

545559
Signatures.MethodParam(
546560
name.show,
547-
info.widenTermRefExpr.show,
561+
tp.paramInfoss.head(index).widenTermRefExpr.show,
548562
docComment.flatMap(_.paramDoc(name)),
549563
isImplicit = tp.isImplicitMethod,
550564
)
@@ -586,23 +600,42 @@ object Signatures {
586600
finalParams.getOrElse(params)
587601
end toParams
588602

589-
val applies = untpdFun.map(toApplyList).getOrElse(Nil)
590-
val types = toMethodTypeList(tp).reverse
603+
loop(tp, applies, typeArgs, 0)
604+
end toParamss
605+
606+
val paramss = toParamss(info, untpdFun, typeArgs)
607+
608+
// Compute instantiated return type
609+
var instantiatedInfo = info
610+
var remainingTypeArgs = typeArgs
611+
while (remainingTypeArgs.nonEmpty && instantiatedInfo.isInstanceOf[PolyType]) {
612+
instantiatedInfo = instantiatedInfo.asInstanceOf[PolyType].appliedTo(remainingTypeArgs.head)
613+
remainingTypeArgs = remainingTypeArgs.tail
614+
}
591615

592-
reduceToParamss(applies, types)
593616

594-
val paramss = toParamss(denot.info, untpdFun)
595617
val (name, returnType) =
596618
if (symbol.isConstructor) then
597619
(symbol.owner.name.show, None)
598620
else
599621
denot.symbol.defTree match
600622
// if there is an error in denotation type, we will fallback to source tree
601623
case defn: tpd.DefDef if denot.info.isErroneous => (denot.name.show, Some(defn.tpt.show))
602-
case _ => (denot.name.show, Some(denot.info.finalResultType.widenTermRefExpr.show))
624+
case _ => (denot.name.show, Some(instantiatedInfo.finalResultType.widenTermRefExpr.show))
603625
Some(Signatures.Signature(name, paramss, returnType, docComment.map(_.mainDoc), Some(denot)))
604626
}
605627

628+
private def extractTypeArgs(tree: tpd.Tree)(using Context): List[List[Type]] = {
629+
tree match {
630+
case tpd.TypeApply(fun, args) =>
631+
extractTypeArgs(fun) :+ args.map(_.tpe)
632+
case tpd.Apply(fun, _) =>
633+
extractTypeArgs(fun)
634+
case _ =>
635+
Nil
636+
}
637+
}
638+
606639
/**
607640
* Creates signature for unapply method. It is different from apply one as it should not show function name,
608641
* return type and type parameters. Instead we show function in the following pattern (_$1: T1, _$2: T2, _$n: Tn),

presentation-compiler/src/main/dotty/tools/pc/SignatureHelpProvider.scala

Lines changed: 1 addition & 98 deletions
Original file line numberDiff line numberDiff line change
@@ -48,9 +48,7 @@ object SignatureHelpProvider:
4848
.setPrinterFn(_ => ShortenedTypePrinter(search, IncludeDefaultParam.Never)(using indexedContext))
4949

5050
val (paramN, callableN, alternatives) = Signatures.signatureHelp(path, pos.span)
51-
val refinedAlternatives = refineSignatures(alternatives, path, pos.span)(using driver.currentCtx)
52-
53-
val infos = refinedAlternatives.flatMap: signature =>
51+
val infos = alternatives.flatMap: signature =>
5452
signature.denot.map(signature -> _)
5553

5654
val signatureInfos = infos.map { case (signature, denot) =>
@@ -168,99 +166,4 @@ object SignatureHelpProvider:
168166
markup.setValue(content.trim())
169167
markup
170168

171-
private def refineSignatures(
172-
help: List[Signatures.Signature],
173-
path: List[tpd.Tree],
174-
span: Span
175-
)(using Context): List[Signatures.Signature] =
176-
val enclosingApply = path.find {
177-
case tpd.Apply(fun, _) => !fun.span.contains(span)
178-
case _ => false
179-
}
180-
181-
enclosingApply match
182-
case Some(tpd.Apply(fun, _)) =>
183-
help.map { signature =>
184-
val matches = signature.denot.exists(_.symbol == fun.symbol)
185-
if matches then
186-
val (head, actions) = unwind(fun)
187-
var currType = head.tpe.widenTermRefExpr
188-
var actionIndex = 0
189-
190-
val newParamss = signature.paramss.map { paramList =>
191-
paramList match
192-
case (p: Signatures.MethodParam) :: _ =>
193-
// Handle PolyTypes if they were skipped in signature but exist in type
194-
while currType.isInstanceOf[dotty.tools.dotc.core.Types.PolyType] do
195-
if actionIndex < actions.size && actions(actionIndex).isInstanceOf[tpd.TypeApply] then
196-
val targs = actions(actionIndex).asInstanceOf[tpd.TypeApply].args.map(_.tpe)
197-
currType = currType.appliedTo(targs)
198-
actionIndex += 1
199-
else
200-
// Fallback: strip aliases or stop if strict matching fails
201-
currType = currType.resultType
202-
203-
if currType.isInstanceOf[MethodType] then
204-
val mt = currType.asInstanceOf[MethodType]
205-
val paramNames = mt.paramNames.map(_.show)
206-
val res = paramList.map {
207-
case p: Signatures.MethodParam =>
208-
val idx = paramNames.indexOf(p.name)
209-
val shown = if idx >= 0 then mt.paramInfos(idx).widenTermRefExpr.show else p.tpe
210-
if shown.contains("Any") || shown.contains("Nothing") || shown.contains("error") then p
211-
else p.copy(tpe = shown)
212-
case other => other
213-
}
214-
currType = mt.resultType
215-
if actionIndex < actions.size && actions(actionIndex).isInstanceOf[tpd.Apply] then
216-
actionIndex += 1
217-
res
218-
else paramList
219-
220-
case (p: Signatures.TypeParam) :: _ =>
221-
if currType.isInstanceOf[dotty.tools.dotc.core.Types.PolyType] then
222-
if actionIndex < actions.size && actions(actionIndex).isInstanceOf[tpd.TypeApply] then
223-
val targs = actions(actionIndex).asInstanceOf[tpd.TypeApply].args.map(_.tpe)
224-
val newParams = paramList.zip(targs).map {
225-
case (p: Signatures.TypeParam, targ) =>
226-
val shown = targ.show
227-
if shown.contains("Any") || shown.contains("Nothing") || shown.contains("error") then p
228-
else p.copy(tpe = shown)
229-
case (p, _) => p
230-
}
231-
currType = currType.appliedTo(targs)
232-
actionIndex += 1
233-
newParams
234-
else paramList
235-
else paramList
236-
237-
case _ => paramList
238-
}
239-
// Update return type if we have consumed all actions and ended up with a result type
240-
val finalType = currType.widen.show
241-
val returnTypeLabel =
242-
if actionIndex == actions.size && !finalType.contains("error") && !finalType.contains("Any") && !finalType.contains("Nothing") then Some(finalType)
243-
else signature.returnType
244-
245-
signature.copy(paramss = newParamss, returnType = returnTypeLabel)
246-
else signature
247-
}
248-
case _ => help
249-
250-
private def unwind(tree: tpd.Tree): (tpd.Tree, List[tpd.Tree]) =
251-
tree match
252-
case tpd.Apply(fn, _) =>
253-
val (head, actions) = unwind(fn)
254-
(head, actions :+ tree)
255-
case tpd.TypeApply(fn, _) =>
256-
val (head, actions) = unwind(fn)
257-
(head, actions :+ tree)
258-
case _ => (tree, Nil)
259-
260-
private def countParams(tree: tpd.Tree): Int =
261-
tree match
262-
case tpd.Apply(fun, _) => 1 + countParams(fun)
263-
case tpd.TypeApply(fun, _) => 1 + countParams(fun)
264-
case _ => 0
265-
266169
end SignatureHelpProvider

0 commit comments

Comments
 (0)