Skip to content

Commit b4f1ba5

Browse files
committed
ScalaDoc : type parameter in extension method is wrong
fixes scala#12813
1 parent c53cc91 commit b4f1ba5

File tree

7 files changed

+143
-70
lines changed

7 files changed

+143
-70
lines changed
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
package tests.extensionParams
2+
3+
extension [A](thiz: A)
4+
def toTuple2[B](that: B): (A, B) = thiz -> that
5+
6+
extension [A](a: A)(using Int)
7+
def f[B](b: B): (A, B) = ???
8+
9+
extension [A](a: A)(using Int)
10+
def ff(b: A): (A, A) = ???
11+
12+
extension [A](a: A)(using Int)
13+
def fff(using String)(b: A): (A, A) = ???
14+
15+
extension (a: Char)(using Int)
16+
def ffff(using String)(b: Int): Unit = ???
17+
18+
extension (a: Char)(using Int)
19+
def fffff[B](using String)(b: B): Unit = ???
20+
21+
extension [A <: List[Char]](a: A)(using Int)
22+
def ffffff[B](b: B): (A, B) = ???

scaladoc/src/dotty/tools/scaladoc/api.scala

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -41,12 +41,12 @@ enum Modifier(val name: String, val prefix: Boolean):
4141
case Opaque extends Modifier("opaque", true)
4242
case Open extends Modifier("open", true)
4343

44-
case class ExtensionTarget(name: String, signature: Signature, dri: DRI, position: Long)
44+
case class ExtensionTarget(name: String, typeParams: Seq[TypeParameter], argsLists: Seq[ParametersList], signature: Signature, dri: DRI, position: Long)
4545
case class ImplicitConversion(from: DRI, to: DRI)
4646
trait ImplicitConversionProvider { def conversion: Option[ImplicitConversion] }
4747
trait Classlike
4848

49-
enum Kind(val name: String){
49+
enum Kind(val name: String):
5050
case RootPackage extends Kind("")
5151
case Package extends Kind("package")
5252
case Class(typeParams: Seq[TypeParameter], argsLists: Seq[ParametersList])
@@ -70,7 +70,6 @@ enum Kind(val name: String){
7070
case Implicit(kind: Kind.Def | Kind.Val.type, conversion: Option[ImplicitConversion])
7171
extends Kind(kind.name) with ImplicitConversionProvider
7272
case Unknown extends Kind("Unknown")
73-
}
7473

7574
enum Origin:
7675
case ImplicitlyAddedBy(name: String, dri: DRI)

scaladoc/src/dotty/tools/scaladoc/renderers/MemberRenderer.scala

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -280,7 +280,14 @@ class MemberRenderer(signatureRenderer: SignatureRenderer)(using DocContext) ext
280280
case _ => None
281281
}.collect {
282282
case (Some(on), members) =>
283-
val sig = Signature(s"extension (${on.name}: ") ++ on.signature ++ Signature(")")
283+
val typeSig = InlineSignatureBuilder()
284+
.text("extension ")
285+
.generics(on.typeParams)
286+
.asInstanceOf[InlineSignatureBuilder].names.reverse
287+
val argsSig = InlineSignatureBuilder()
288+
.functionParameters(on.argsLists)
289+
.asInstanceOf[InlineSignatureBuilder].names.reverse
290+
val sig = typeSig ++ Signature(s"(${on.name}: ") ++ on.signature ++ Signature(")") ++ argsSig
284291
MGroup(span(sig.map(renderElement)), members.sortBy(_.name).toSeq, on.name)
285292
}.toSeq
286293

scaladoc/src/dotty/tools/scaladoc/tasty/ClassLikeSupport.scala

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -176,7 +176,7 @@ trait ClassLikeSupport:
176176
val sgn = Inkuire.ExternalSignature(
177177
signature = Inkuire.Signature(
178178
receiver = receiver,
179-
arguments = methodSymbol.nonExtensionParamLists.collect {
179+
arguments = methodSymbol.nonExtensionTermParamLists.collect {
180180
case tpc@TermParamClause(params) if !tpc.isImplicit && !tpc.isGiven => params //TODO [Inkuire] Implicit parameters
181181
}.flatten.map(_.tpt.asInkuire(vars)),
182182
result = defdef.returnTpt.asInkuire(vars),
@@ -242,11 +242,18 @@ trait ClassLikeSupport:
242242
private def isDocumentableExtension(s: Symbol) =
243243
!s.isHiddenByVisibility && !s.isSyntheticFunc && s.isExtensionMethod
244244

245-
private def parseMember(c: ClassDef)(s: Tree): Option[Member] = processTreeOpt(s)(s match
245+
private def parseMember(c: ClassDef)(s: Tree): Option[Member] = processTreeOpt(s) { s match
246246
case dd: DefDef if isDocumentableExtension(dd.symbol) =>
247247
dd.symbol.extendedSymbol.map { extSym =>
248+
val memberInfo = unwrapMemberInfo(c, dd.symbol)
249+
val typeParams = dd.symbol.extendedTypeParams.map(mkTypeArgument(_, memberInfo.genericTypes))
250+
val termParams = dd.symbol.extendedTermParamLists.zipWithIndex.map { case (paramList, index) =>
251+
ParametersList(paramList.params.map(mkParameter(_, memberInfo = memberInfo.paramLists(index))), paramListModifier(paramList.params))
252+
}
248253
val target = ExtensionTarget(
249254
extSym.symbol.normalizedName,
255+
typeParams,
256+
termParams,
250257
extSym.tpt.asSignature,
251258
extSym.tpt.symbol.dri,
252259
extSym.symbol.pos.get.start
@@ -302,7 +309,7 @@ trait ClassLikeSupport:
302309
Some(parseClasslike(c))
303310

304311
case _ => None
305-
)
312+
}
306313

307314
private def parseGivenClasslike(c: ClassDef): Member = {
308315
val parsedClasslike = parseClasslike(c)
@@ -470,8 +477,8 @@ trait ClassLikeSupport:
470477
specificKind: (Kind.Def => Kind) = identity
471478
): Member =
472479
val method = methodSymbol.tree.asInstanceOf[DefDef]
473-
val paramLists: List[TermParamClause] = methodSymbol.nonExtensionParamLists
474-
val genericTypes = if (methodSymbol.isClassConstructor) Nil else method.leadingTypeParams
480+
val paramLists: List[TermParamClause] = methodSymbol.nonExtensionTermParamLists
481+
val genericTypes: List[TypeDef] = if (methodSymbol.isClassConstructor) Nil else methodSymbol.nonExtensionLeadingTypeParams
475482

476483
val memberInfo = unwrapMemberInfo(c, methodSymbol)
477484

scaladoc/src/dotty/tools/scaladoc/tasty/SymOps.scala

Lines changed: 43 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -141,14 +141,52 @@ object SymOps:
141141
else termParamss(1).params(0)
142142
}
143143

144-
def nonExtensionParamLists: List[reflect.TermParamClause] =
144+
def extendedTypeParams: List[reflect.TypeDef] =
145+
import reflect.*
146+
val method = sym.tree.asInstanceOf[DefDef]
147+
method.leadingTypeParams
148+
149+
def extendedTermParamLists: List[reflect.TermParamClause] =
150+
import reflect.*
151+
if sym.nonExtensionLeadingTypeParams.nonEmpty then
152+
sym.nonExtensionParamLists.takeWhile {
153+
case _: TypeParamClause => false
154+
case _ => true
155+
}.collect {
156+
case tpc: TermParamClause => tpc
157+
}
158+
else
159+
List.empty
160+
161+
def nonExtensionTermParamLists: List[reflect.TermParamClause] =
162+
import reflect.*
163+
if sym.nonExtensionLeadingTypeParams.nonEmpty then
164+
sym.nonExtensionParamLists.dropWhile {
165+
case _: TypeParamClause => false
166+
case _ => true
167+
}.drop(1).collect {
168+
case tpc: TermParamClause => tpc
169+
}
170+
else
171+
sym.nonExtensionParamLists.collect {
172+
case tpc: TermParamClause => tpc
173+
}
174+
175+
def nonExtensionParamLists: List[reflect.ParamClause] =
145176
import reflect.*
146177
val method = sym.tree.asInstanceOf[DefDef]
147178
if sym.isExtensionMethod then
148-
val params = method.termParamss
149-
if sym.isLeftAssoc || params.size == 1 then params.tail
150-
else params.head :: params.tail.drop(1)
151-
else method.termParamss
179+
val params = method.paramss
180+
val toDrop = if method.leadingTypeParams.nonEmpty then 2 else 1
181+
if sym.isLeftAssoc || params.size == 1 then params.drop(toDrop)
182+
else params.head :: params.tail.drop(toDrop)
183+
else method.paramss
184+
185+
def nonExtensionLeadingTypeParams: List[reflect.TypeDef] =
186+
import reflect.*
187+
sym.nonExtensionParamLists.collectFirst {
188+
case TypeParamClause(params) => params
189+
}.toList.flatten
152190

153191
end extension
154192

scaladoc/src/dotty/tools/scaladoc/transformers/ImplicitMembersExtensionTransformer.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ class ImplicitMembersExtensionTransformer(using DocContext) extends(Module => Mo
2626

2727
val MyDri = c.dri
2828
def collectApplicableMembers(source: Member): Seq[Member] = source.members.flatMap {
29-
case m @ Member(_, _, _, Kind.Extension(ExtensionTarget(_, _, MyDri, _), _), Origin.RegularlyDefined) =>
29+
case m @ Member(_, _, _, Kind.Extension(ExtensionTarget(_, _, _, _, MyDri, _), _), Origin.RegularlyDefined) =>
3030
val kind = m.kind match
3131
case d: Kind.Def => d
3232
case _ => Kind.Def(Nil, Nil)

scaladoc/src/dotty/tools/scaladoc/translators/ScalaSignatureUtils.scala

Lines changed: 55 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -24,74 +24,74 @@ trait SignatureBuilder extends ScalaSignatureUtils {
2424
def memberName(name: String, dri: DRI) = text(name)
2525

2626
def list[E](
27-
elements: Seq[E],
28-
prefix: String = "",
29-
suffix: String = "",
30-
separator: String = ", ",
31-
)(
32-
elemOp: (SignatureBuilder, E) => SignatureBuilder
33-
): SignatureBuilder = elements match {
34-
case Nil => this
35-
case head :: tail =>
36-
tail.foldLeft(elemOp(text(prefix), head))((b, e) => elemOp(b.text(separator), e)).text(suffix)
37-
}
27+
elements: Seq[E],
28+
prefix: String = "",
29+
suffix: String = "",
30+
separator: String = ", ",
31+
)(
32+
elemOp: (SignatureBuilder, E) => SignatureBuilder
33+
): SignatureBuilder = elements match {
34+
case Nil => this
35+
case head :: tail =>
36+
tail.foldLeft(elemOp(text(prefix), head))((b, e) => elemOp(b.text(separator), e)).text(suffix)
37+
}
3838

3939
def annotationsBlock(d: Member): SignatureBuilder =
40-
d.annotations.foldLeft(this){ (bdr, annotation) => bdr.buildAnnotation(annotation)}
41-
42-
def annotationsInline(d: Parameter): SignatureBuilder =
43-
d.annotations.foldLeft(this){ (bdr, annotation) => bdr.buildAnnotation(annotation) }
40+
d.annotations.foldLeft(this){ (bdr, annotation) => bdr.buildAnnotation(annotation)}
4441

45-
def annotationsInline(t: TypeParameter): SignatureBuilder =
46-
t.annotations.foldLeft(this){ (bdr, annotation) => bdr.buildAnnotation(annotation) }
42+
def annotationsInline(d: Parameter): SignatureBuilder =
43+
d.annotations.foldLeft(this){ (bdr, annotation) => bdr.buildAnnotation(annotation) }
4744

48-
private def buildAnnotation(a: Annotation): SignatureBuilder =
49-
text("@").driLink(a.dri.location.split('.').last, a.dri).buildAnnotationParams(a).text(" ")
45+
def annotationsInline(t: TypeParameter): SignatureBuilder =
46+
t.annotations.foldLeft(this){ (bdr, annotation) => bdr.buildAnnotation(annotation) }
5047

51-
private def buildAnnotationParams(a: Annotation): SignatureBuilder =
52-
if !a.params.isEmpty then
53-
val params = a.params.filterNot {
54-
case Annotation.LinkParameter(_, _, text) => text == "_"
55-
case _ => false
56-
}
57-
list(params, "(", ")", ", "){ (bdr, param) => bdr.buildAnnotationParameter(param)}
58-
else this
48+
private def buildAnnotation(a: Annotation): SignatureBuilder =
49+
text("@").driLink(a.dri.location.split('.').last, a.dri).buildAnnotationParams(a).text(" ")
5950

60-
private def addParameterName(txt: Option[String]): SignatureBuilder = txt match {
61-
case Some(name) => this.text(s"$name = ")
62-
case _ => this
51+
private def buildAnnotationParams(a: Annotation): SignatureBuilder =
52+
if !a.params.isEmpty then
53+
val params = a.params.filterNot {
54+
case Annotation.LinkParameter(_, _, text) => text == "_"
55+
case _ => false
6356
}
57+
list(params, "(", ")", ", "){ (bdr, param) => bdr.buildAnnotationParameter(param)}
58+
else this
6459

65-
private def buildAnnotationParameter(a: Annotation.AnnotationParameter): SignatureBuilder = a match {
66-
case Annotation.PrimitiveParameter(name, value) =>
67-
addParameterName(name).text(value)
68-
case Annotation.LinkParameter(name, dri, text) =>
69-
addParameterName(name).driLink(text, dri)
70-
case Annotation.UnresolvedParameter(name, value) =>
71-
addParameterName(name).text(value)
72-
}
60+
private def addParameterName(txt: Option[String]): SignatureBuilder = txt match {
61+
case Some(name) => this.text(s"$name = ")
62+
case _ => this
63+
}
7364

74-
def modifiersAndVisibility(t: Member, kind: String) =
75-
val (prefixMods, suffixMods) = t.modifiers.partition(_.prefix)
76-
val all = prefixMods.map(_.name) ++ Seq(t.visibility.asSignature) ++ suffixMods.map(_.name)
65+
private def buildAnnotationParameter(a: Annotation.AnnotationParameter): SignatureBuilder = a match {
66+
case Annotation.PrimitiveParameter(name, value) =>
67+
addParameterName(name).text(value)
68+
case Annotation.LinkParameter(name, dri, text) =>
69+
addParameterName(name).driLink(text, dri)
70+
case Annotation.UnresolvedParameter(name, value) =>
71+
addParameterName(name).text(value)
72+
}
7773

78-
text(all.toSignatureString()).text(kind + " ")
74+
def modifiersAndVisibility(t: Member, kind: String) =
75+
val (prefixMods, suffixMods) = t.modifiers.partition(_.prefix)
76+
val all = prefixMods.map(_.name) ++ Seq(t.visibility.asSignature) ++ suffixMods.map(_.name)
7977

80-
def generics(on: Seq[TypeParameter]) = list(on.toList, "[", "]"){ (bdr, e) =>
81-
bdr.annotationsInline(e).text(e.variance).memberName(e.name, e.dri).signature(e.signature)
82-
}
78+
text(all.toSignatureString()).text(kind + " ")
8379

84-
def functionParameters(params: Seq[ParametersList]) =
85-
if params.isEmpty then this.text("")
86-
else if params.size == 1 && params(0).parameters == Nil then this.text("()")
87-
else this.list(params, separator = ""){ (bld, pList) =>
88-
bld.list(pList.parameters, s"(${pList.modifiers}", ")"){ (bld, p) =>
89-
val annotationsAndModifiers = bld.annotationsInline(p)
90-
.text(p.modifiers)
91-
val name = p.name.fold(annotationsAndModifiers)(annotationsAndModifiers.memberName(_, p.dri).text(": "))
92-
name.signature(p.signature)
93-
}
80+
def generics(on: Seq[TypeParameter]) = list(on.toList, "[", "]"){ (bdr, e) =>
81+
bdr.annotationsInline(e).text(e.variance).memberName(e.name, e.dri).signature(e.signature)
82+
}
83+
84+
def functionParameters(params: Seq[ParametersList]) =
85+
if params.isEmpty then this.text("")
86+
else if params.size == 1 && params(0).parameters == Nil then this.text("()")
87+
else this.list(params, separator = ""){ (bld, pList) =>
88+
bld.list(pList.parameters, s"(${pList.modifiers}", ")"){ (bld, p) =>
89+
val annotationsAndModifiers = bld.annotationsInline(p)
90+
.text(p.modifiers)
91+
val name = p.name.fold(annotationsAndModifiers)(annotationsAndModifiers.memberName(_, p.dri).text(": "))
92+
name.signature(p.signature)
9493
}
94+
}
9595
}
9696

9797
trait ScalaSignatureUtils:

0 commit comments

Comments
 (0)