Skip to content

Commit 525f4a5

Browse files
authored
Merge pull request #13191 from KacperFKorban/scaladoc/fix-12813
ScalaDoc : type parameter in extension method is wrong
2 parents 4e740c5 + 642304a commit 525f4a5

File tree

7 files changed

+144
-71
lines changed

7 files changed

+144
-71
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
@@ -182,7 +182,7 @@ trait ClassLikeSupport:
182182
val sgn = Inkuire.ExternalSignature(
183183
signature = Inkuire.Signature(
184184
receiver = receiver,
185-
arguments = methodSymbol.nonExtensionParamLists.collect {
185+
arguments = methodSymbol.nonExtensionTermParamLists.collect {
186186
case tpc@TermParamClause(params) if !tpc.isImplicit && !tpc.isGiven => params //TODO [Inkuire] Implicit parameters
187187
}.flatten.map(_.tpt.asInkuire(vars)),
188188
result = defdef.returnTpt.asInkuire(vars),
@@ -249,11 +249,18 @@ trait ClassLikeSupport:
249249
private def isDocumentableExtension(s: Symbol) =
250250
!s.isHiddenByVisibility && !s.isSyntheticFunc && s.isExtensionMethod
251251

252-
private def parseMember(c: ClassDef)(s: Tree): Option[Member] = processTreeOpt(s)(s match
252+
private def parseMember(c: ClassDef)(s: Tree): Option[Member] = processTreeOpt(s) { s match
253253
case dd: DefDef if isDocumentableExtension(dd.symbol) =>
254254
dd.symbol.extendedSymbol.map { extSym =>
255+
val memberInfo = unwrapMemberInfo(c, dd.symbol)
256+
val typeParams = dd.symbol.extendedTypeParams.map(mkTypeArgument(_, memberInfo.genericTypes))
257+
val termParams = dd.symbol.extendedTermParamLists.zipWithIndex.map { case (paramList, index) =>
258+
ParametersList(paramList.params.map(mkParameter(_, memberInfo = memberInfo.paramLists(index))), paramListModifier(paramList.params))
259+
}
255260
val target = ExtensionTarget(
256261
extSym.symbol.normalizedName,
262+
typeParams,
263+
termParams,
257264
extSym.tpt.asSignature,
258265
extSym.tpt.symbol.dri,
259266
extSym.symbol.pos.get.start
@@ -309,7 +316,7 @@ trait ClassLikeSupport:
309316
Some(parseClasslike(c))
310317

311318
case _ => None
312-
)
319+
}
313320

314321
private def parseGivenClasslike(c: ClassDef): Member = {
315322
val parsedClasslike = parseClasslike(c)
@@ -477,8 +484,8 @@ trait ClassLikeSupport:
477484
specificKind: (Kind.Def => Kind) = identity
478485
): Member =
479486
val method = methodSymbol.tree.asInstanceOf[DefDef]
480-
val paramLists: List[TermParamClause] = methodSymbol.nonExtensionParamLists
481-
val genericTypes = if (methodSymbol.isClassConstructor) Nil else method.leadingTypeParams
487+
val paramLists: List[TermParamClause] = methodSymbol.nonExtensionTermParamLists
488+
val genericTypes: List[TypeDef] = if (methodSymbol.isClassConstructor) Nil else methodSymbol.nonExtensionLeadingTypeParams
482489

483490
val memberInfo = unwrapMemberInfo(c, methodSymbol)
484491

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: 56 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -24,75 +24,75 @@ 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-
forcePrefixAndSuffix: Boolean = false
32-
)(
33-
elemOp: (SignatureBuilder, E) => SignatureBuilder
34-
): SignatureBuilder = elements match {
35-
case Nil => if forcePrefixAndSuffix then this.text(prefix + suffix) else this
36-
case head :: tail =>
37-
tail.foldLeft(elemOp(text(prefix), head))((b, e) => elemOp(b.text(separator), e)).text(suffix)
38-
}
27+
elements: Seq[E],
28+
prefix: String = "",
29+
suffix: String = "",
30+
separator: String = ", ",
31+
forcePrefixAndSuffix: Boolean = false
32+
)(
33+
elemOp: (SignatureBuilder, E) => SignatureBuilder
34+
): SignatureBuilder = elements match {
35+
case Nil => if forcePrefixAndSuffix then this.text(prefix + suffix) else this
36+
case head :: tail =>
37+
tail.foldLeft(elemOp(text(prefix), head))((b, e) => elemOp(b.text(separator), e)).text(suffix)
38+
}
3939

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

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

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

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

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

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

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

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

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

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

9898
trait ScalaSignatureUtils:

0 commit comments

Comments
 (0)