Skip to content

Commit 3e85fca

Browse files
authored
Merge pull request #11070 from BarkingBad/scala3doc/type-parameters-fix
Fix presentation of signature types that may change in inherited classes due to different symbolic type names/partially applied types
2 parents f5e84f4 + 91fdbdb commit 3e85fca

File tree

6 files changed

+110
-37
lines changed

6 files changed

+110
-37
lines changed
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package tests
2+
package typeAppliance
3+
4+
trait AClass[A, B]:
5+
def funASD[C, D](f: B => C): AClass[A, C]
6+
7+
trait BClass[A, B] extends AClass[A, B]:
8+
override def funASD[X, D](f: B => X): BClass[A, X]
9+
val f: (=> B) => String
10+
= _ => "abc"
11+
12+
13+
abstract class CClass[U] extends BClass[Int, U]:
14+
def someFun(n: Int)(b: Int): Int
15+
= 1
16+

scala3doc/src/dotty/dokka/tasty/ClassLikeSupport.scala

Lines changed: 58 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ trait ClassLikeSupport:
2727
else Kind.Class(Nil, Nil)
2828

2929
private def kindForClasslike(classDef: ClassDef): Kind =
30-
def typeArgs = classDef.getTypeParams.map(mkTypeArgument)
30+
def typeArgs = classDef.getTypeParams.map(mkTypeArgument(_))
3131

3232
def parameterModifier(parameter: Symbol): String =
3333
val fieldSymbol = classDef.symbol.declaredField(parameter.normalizedName)
@@ -125,7 +125,7 @@ trait ClassLikeSupport:
125125
private def isDocumentableExtension(s: Symbol) =
126126
!s.isHiddenByVisibility && !s.isSyntheticFunc && s.isExtensionMethod
127127

128-
private def parseMember(s: Tree): Option[Member] = processTreeOpt(s)(s match
128+
private def parseMember(c: ClassDef)(s: Tree): Option[Member] = processTreeOpt(s)(s match
129129
case dd: DefDef if isDocumentableExtension(dd.symbol) =>
130130
dd.symbol.extendedSymbol.map { extSym =>
131131
val target = ExtensionTarget(
@@ -134,14 +134,14 @@ trait ClassLikeSupport:
134134
extSym.tpt.symbol.dri,
135135
extSym.symbol.pos.get.start
136136
)
137-
parseMethod(dd.symbol,specificKind = Kind.Extension(target, _))
137+
parseMethod(c, dd.symbol,specificKind = Kind.Extension(target, _))
138138
}
139139
// TODO check given methods?
140140
case dd: DefDef if !dd.symbol.isHiddenByVisibility && dd.symbol.isGiven =>
141141
Some(dd.symbol.owner.memberType(dd.name))
142142
.filterNot(_.exists)
143143
.map { _ =>
144-
parseMethod(dd.symbol, specificKind =
144+
parseMethod(c, dd.symbol, specificKind =
145145
Kind.Given(_, getGivenInstance(dd).map(_.asSignature), None)
146146
)
147147
}
@@ -160,11 +160,11 @@ trait ClassLikeSupport:
160160
case s: Select if s.symbol.isDefDef => s.symbol.dri
161161
}.orElse(exportedTarget.map(_.qualifier.tpe.typeSymbol.dri))
162162

163-
Some(parseMethod(dd.symbol, specificKind = Kind.Exported(_))
163+
Some(parseMethod(c, dd.symbol, specificKind = Kind.Exported(_))
164164
.withOrigin(Origin.ExportedFrom(s"$instanceName.$functionName", dri)))
165165

166166
case dd: DefDef if !dd.symbol.isHiddenByVisibility && !dd.symbol.isGiven && !dd.symbol.isSyntheticFunc && !dd.symbol.isExtensionMethod =>
167-
Some(parseMethod(dd.symbol))
167+
Some(parseMethod(c, dd.symbol))
168168

169169
case td: TypeDef if !td.symbol.flags.is(Flags.Synthetic) && (!td.symbol.flags.is(Flags.Case) || !td.symbol.flags.is(Flags.Enum)) =>
170170
Some(parseTypeDef(td))
@@ -173,10 +173,10 @@ trait ClassLikeSupport:
173173
&& (!vd.symbol.flags.is(Flags.Case) || !vd.symbol.flags.is(Flags.Enum))
174174
&& vd.symbol.isGiven =>
175175
val classDef = Some(vd.tpt.tpe).flatMap(_.classSymbol.map(_.tree.asInstanceOf[ClassDef]))
176-
Some(classDef.filter(_.symbol.flags.is(Flags.Module)).fold[Member](parseValDef(vd))(parseGivenClasslike(_)))
176+
Some(classDef.filter(_.symbol.flags.is(Flags.Module)).fold[Member](parseValDef(c, vd))(parseGivenClasslike(_)))
177177

178178
case vd: ValDef if !isSyntheticField(vd.symbol) && (!vd.symbol.flags.is(Flags.Case) || !vd.symbol.flags.is(Flags.Enum)) =>
179-
Some(parseValDef(vd))
179+
Some(parseValDef(c, vd))
180180

181181
case c: ClassDef if c.symbol.owner.memberMethod(c.name).exists(_.flags.is(Flags.Given)) =>
182182
Some(parseGivenClasslike(c))
@@ -210,9 +210,9 @@ trait ClassLikeSupport:
210210
)
211211
}
212212

213-
private def parseInheritedMember(s: Tree): Option[Member] = processTreeOpt(s)(s match
213+
private def parseInheritedMember(c: ClassDef)(s: Tree): Option[Member] = processTreeOpt(s)(s match
214214
case c: ClassDef if c.symbol.shouldDocumentClasslike && !c.symbol.isGiven => Some(parseClasslike(c, signatureOnly = true))
215-
case other => parseMember(other)
215+
case other => parseMember(c)(other)
216216
).map(_.withInheritedFrom(InheritedFrom(s.symbol.owner.normalizedName, s.symbol.owner.dri)))
217217

218218
extension (c: ClassDef)
@@ -228,8 +228,8 @@ trait ClassLikeSupport:
228228
case dd: DefDef if !dd.symbol.isClassConstructor && !(dd.symbol.isSuperBridgeMethod || dd.symbol.isDefaultHelperMethod) => dd
229229
case other => other
230230
}
231-
c.membersToDocument.flatMap(parseMember) ++
232-
inherited.flatMap(s => parseInheritedMember(s))
231+
c.membersToDocument.flatMap(parseMember(c)) ++
232+
inherited.flatMap(s => parseInheritedMember(c)(s))
233233
}
234234

235235
/** Extracts members while taking Dotty logic for patching the stdlib into account. */
@@ -240,7 +240,7 @@ trait ClassLikeSupport:
240240
val ownMemberDRIs = ownMembers.iterator.map(_.name).toSet + "experimental$"
241241
sym.tree.asInstanceOf[ClassDef]
242242
.membersToDocument.filterNot(m => ownMemberDRIs.contains(m.symbol.name))
243-
.flatMap(parseMember)
243+
.flatMap(parseMember(c))
244244
}
245245
c.symbol.fullName match {
246246
case "scala.Predef$" =>
@@ -302,7 +302,7 @@ trait ClassLikeSupport:
302302

303303
val enumVals = companion.membersToDocument.collect {
304304
case vd: ValDef if !isSyntheticField(vd.symbol) && vd.symbol.flags.is(Flags.Enum) && vd.symbol.flags.is(Flags.Case) => vd
305-
}.toList.map(parseValDef(_))
305+
}.toList.map(parseValDef(classDef, _))
306306

307307
val enumTypes = companion.membersToDocument.collect {
308308
case td: TypeDef if !td.symbol.flags.is(Flags.Synthetic) && td.symbol.flags.is(Flags.Enum) && td.symbol.flags.is(Flags.Case) => td
@@ -321,6 +321,7 @@ trait ClassLikeSupport:
321321
classlikie.withNewMembers(cases).asInstanceOf[DClass]
322322

323323
def parseMethod(
324+
c: ClassDef,
324325
methodSymbol: Symbol,
325326
emptyParamsList: Boolean = false,
326327
paramPrefix: Symbol => String = _ => "",
@@ -336,9 +337,13 @@ trait ClassLikeSupport:
336337
else method.paramss
337338
val genericTypes = if (methodSymbol.isClassConstructor) Nil else method.typeParams
338339

340+
val memberInfo = unwrapMemberInfo(c, methodSymbol)
341+
339342
val basicKind: Kind.Def = Kind.Def(
340-
genericTypes.map(mkTypeArgument),
341-
paramLists.map(pList => ParametersList(pList.map(mkParameter(_, paramPrefix)), if isUsingModifier(pList) then "using " else ""))
343+
genericTypes.map(mkTypeArgument(_, memberInfo.genericTypes)),
344+
paramLists.zipWithIndex.map { (pList, index) =>
345+
ParametersList(pList.map(mkParameter(_, paramPrefix, memberInfo = memberInfo.paramLists(index))), if isUsingModifier(pList) then "using " else "")
346+
}
342347
)
343348

344349
val methodKind =
@@ -368,7 +373,7 @@ trait ClassLikeSupport:
368373
methodSymbol.getExtraModifiers(),
369374
methodKind,
370375
methodSymbol.getAnnotations(),
371-
method.returnTpt.dokkaType.asSignature,
376+
memberInfo.res.dokkaType.asSignature,
372377
methodSymbol.source(using qctx),
373378
origin
374379
)
@@ -377,32 +382,33 @@ trait ClassLikeSupport:
377382
def mkParameter(argument: ValDef,
378383
prefix: Symbol => String = _ => "",
379384
isExtendedSymbol: Boolean = false,
380-
isGrouped: Boolean = false) =
385+
isGrouped: Boolean = false,
386+
memberInfo: Map[String, TypeRepr] = Map.empty) =
381387
val inlinePrefix = if argument.symbol.flags.is(Flags.Inline) then "inline " else ""
382-
val name = Option.when(!argument.symbol.flags.is(Flags.Synthetic))(argument.symbol.normalizedName)
383-
388+
val nameIfNotSynthetic = Option.when(!argument.symbol.flags.is(Flags.Synthetic))(argument.symbol.normalizedName)
389+
val name = argument.symbol.normalizedName
384390
Parameter(
385391
argument.symbol.getAnnotations(),
386392
inlinePrefix + prefix(argument.symbol),
387-
name,
393+
nameIfNotSynthetic,
388394
argument.symbol.dri,
389-
argument.tpt.dokkaType.asSignature,
395+
memberInfo.get(name).fold(argument.tpt.dokkaType.asSignature)(_.dokkaType.asSignature),
390396
isExtendedSymbol,
391397
isGrouped
392398
)
393399

394-
def mkTypeArgument(argument: TypeDef): TypeParameter =
400+
def mkTypeArgument(argument: TypeDef, memberInfo: Map[String, TypeBounds] = Map.empty): TypeParameter =
395401
val variancePrefix: "+" | "-" | "" =
396402
if argument.symbol.flags.is(Flags.Covariant) then "+"
397403
else if argument.symbol.flags.is(Flags.Contravariant) then "-"
398404
else ""
399-
405+
val name = argument.symbol.normalizedName
400406
TypeParameter(
401407
argument.symbol.getAnnotations(),
402408
variancePrefix,
403-
argument.symbol.normalizedName,
409+
name,
404410
argument.symbol.dri,
405-
argument.rhs.dokkaType.asSignature
411+
memberInfo.get(name).fold(argument.rhs.dokkaType.asSignature)(_.dokkaType.asSignature)
406412
)
407413

408414
def parseTypeDef(typeDef: TypeDef): Member =
@@ -413,7 +419,7 @@ trait ClassLikeSupport:
413419
}
414420

415421
val (generics, tpeTree) = typeDef.rhs match
416-
case LambdaTypeTree(params, body) => (params.map(mkTypeArgument), body)
422+
case LambdaTypeTree(params, body) => (params.map(mkTypeArgument(_)), body)
417423
case tpe => (Nil, tpe)
418424

419425
mkMember(
@@ -428,8 +434,9 @@ trait ClassLikeSupport:
428434
)
429435
)
430436

431-
def parseValDef(valDef: ValDef): Member =
437+
def parseValDef(c: ClassDef, valDef: ValDef): Member =
432438
def defaultKind = if valDef.symbol.flags.is(Flags.Mutable) then Kind.Var else Kind.Val
439+
val memberInfo = unwrapMemberInfo(c, valDef.symbol)
433440
val kind = if valDef.symbol.flags.is(Flags.Implicit) then
434441
Kind.Implicit(Kind.Val, extractImplicitConversion(valDef.tpt.tpe))
435442
else defaultKind
@@ -441,7 +448,7 @@ trait ClassLikeSupport:
441448
valDef.symbol.getExtraModifiers(),
442449
kind,
443450
valDef.symbol.getAnnotations(),
444-
valDef.tpt.tpe.dokkaType.asSignature,
451+
memberInfo.res.dokkaType.asSignature,
445452
valDef.symbol.source(using qctx)
446453
)
447454
)
@@ -472,5 +479,27 @@ trait ClassLikeSupport:
472479
PropertyContainer.Companion.empty().plus(member.copy(rawDoc = symbol.documentation)).plus(compositeExt)
473480
)
474481

482+
case class MemberInfo(genericTypes: Map[String, TypeBounds], paramLists: List[Map[String, TypeRepr]], res: TypeRepr)
483+
484+
def unwrapMemberInfo(c: ClassDef, symbol: Symbol): MemberInfo =
485+
val baseTypeRepr = memberInfo(c, symbol)
486+
487+
def handlePolyType(polyType: PolyType): MemberInfo =
488+
MemberInfo(polyType.paramNames.zip(polyType.paramBounds).toMap, List.empty, polyType.resType)
489+
490+
def handleMethodType(memberInfo: MemberInfo, methodType: MethodType): MemberInfo =
491+
MemberInfo(memberInfo.genericTypes, memberInfo.paramLists ++ List(methodType.paramNames.zip(methodType.paramTypes).toMap), methodType.resType)
492+
493+
def handleByNameType(memberInfo: MemberInfo, byNameType: ByNameType): MemberInfo =
494+
MemberInfo(memberInfo.genericTypes, memberInfo.paramLists, byNameType.underlying)
495+
496+
def recursivelyCalculateMemberInfo(memberInfo: MemberInfo): MemberInfo = memberInfo.res match
497+
case p: PolyType => recursivelyCalculateMemberInfo(handlePolyType(p))
498+
case m: MethodType => recursivelyCalculateMemberInfo(handleMethodType(memberInfo, m))
499+
case b: ByNameType => handleByNameType(memberInfo, b)
500+
case _ => memberInfo
501+
502+
recursivelyCalculateMemberInfo(MemberInfo(Map.empty, List.empty, baseTypeRepr))
503+
475504
private def isUsingModifier(parameters: Seq[ValDef]): Boolean =
476505
parameters.size > 0 && parameters(0).symbol.flags.is(Flags.Given)

scala3doc/src/dotty/dokka/tasty/SyntheticSupport.scala

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,3 +105,12 @@ trait SyntheticsSupport:
105105
given dotc.core.Contexts.Context = qctx.asInstanceOf[scala.quoted.runtime.impl.QuotesImpl].ctx
106106
val cSym = c.symbol.asInstanceOf[dotc.core.Symbols.Symbol]
107107
cSym.typeRef.appliedTo(cSym.typeParams.map(_.typeRef)).asInstanceOf[TypeRepr]
108+
109+
def memberInfo(c: ClassDef, symbol: Symbol): TypeRepr =
110+
import qctx.reflect._
111+
import dotty.tools.dotc
112+
given dotc.core.Contexts.Context = qctx.asInstanceOf[scala.quoted.runtime.impl.QuotesImpl].ctx
113+
typeForClass(c).asInstanceOf[dotc.core.Types.Type]
114+
.memberInfo(symbol.asInstanceOf[dotc.core.Symbols.Symbol])
115+
.asInstanceOf[TypeRepr]
116+

scala3doc/src/dotty/dokka/tasty/TypesSupport.scala

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -68,10 +68,19 @@ trait TypesSupport:
6868
case List(single) => single
6969
case other => other.reduce((r, e) => r ++ texts(", ") ++ e)
7070

71-
private def isRepeated(tpeAnnotation: Term) =
72-
// For some reason annotation.tpe.typeSymbol != defn.RepeatedParamClass
73-
// annotation.tpe.typeSymbol prints 'class Repeated' and defn.RepeatedParamClass prints 'class <repeated>'
74-
tpeAnnotation.tpe.typeSymbol.toString == "class Repeated"
71+
private def isRepeatedAnnotation(term: Term) =
72+
term.tpe match
73+
case t: TypeRef => t.name == "Repeated" && t.qualifier.match
74+
case ThisType(tref: TypeRef) if tref.name == "internal" => true
75+
case _ => false
76+
case _ => false
77+
78+
private def isRepeated(typeRepr: TypeRepr) =
79+
typeRepr match
80+
case t: TypeRef => t.name == "<repeated>" && t.qualifier.match
81+
case ThisType(tref: TypeRef) if tref.name == "scala" => true
82+
case _ => false
83+
case _ => false
7584

7685
// TODO #23 add support for all types signatures that makes sense
7786
private def inner(tp: TypeRepr): List[JProjection] =
@@ -86,7 +95,9 @@ trait TypesSupport:
8695
case ConstantType(constant) =>
8796
texts(constant.show)
8897
case ThisType(tpe) => inner(tpe)
89-
case AnnotatedType(AppliedType(_, Seq(tpe)), annotation) if isRepeated(annotation) =>
98+
case AnnotatedType(AppliedType(_, Seq(tpe)), annotation) if isRepeatedAnnotation(annotation) =>
99+
inner(tpe) :+ text("*")
100+
case AppliedType(repeatedClass, Seq(tpe)) if isRepeated(repeatedClass) =>
90101
inner(tpe) :+ text("*")
91102
case AnnotatedType(tpe, _) =>
92103
inner(tpe)
@@ -168,7 +179,10 @@ trait TypesSupport:
168179
case Seq(rtpe) =>
169180
text("() => ") :: inner(rtpe)
170181
case Seq(arg, rtpe) =>
171-
inner(arg) ++ texts(" => ") ++ inner(rtpe)
182+
val partOfSignature = arg match
183+
case byName: ByNameType => texts("(") ++ inner(byName) ++ texts(")")
184+
case _ => inner(arg)
185+
partOfSignature ++ texts(" => ") ++ inner(rtpe)
172186
case args =>
173187
texts("(") ++ commas(args.init.map(inner)) ++ texts(") => ") ++ inner(args.last)
174188
else if t.isTupleType then

scala3doc/test/dotty/dokka/SignatureTest.scala

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,10 @@ abstract class SignatureTest(
9898
case unexpectedRegex(signature) => findName(signature, kinds).map(Unexpected(_))
9999
case expectedRegex(signature) => findName(signature, kinds).map(Expected(_, signature))
100100
case signature =>
101-
findName(signature, kinds).map(Expected(_, commentRegex.replaceAllIn(signature, "").compactWhitespaces))
101+
findName(signature, kinds).map(
102+
Expected(_, commentRegex.replaceAllIn(signature, "")
103+
.compactWhitespaces.reverse.dropWhile(List('{', ':').contains(_)).reverse)
104+
)
102105
}
103106

104107
private def signaturesFromDocumentation(root: PageNode)(using DocContext): Seq[String] =

scala3doc/test/dotty/dokka/SignatureTestCases.scala

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@ class FieldsSignatures extends SignatureTest("fieldsSignatures", SignatureTest.a
3131

3232
class NestedSignatures extends SignatureTest("nested", SignatureTest.all)
3333

34+
class TypeAppliacneSignatures extends SignatureTest("typeAppliance", SignatureTest.all)
35+
3436
class CompanionObjectSignatures extends SignatureTest("companionObjectSignatures", SignatureTest.all)
3537

3638
class PackageSymbolSignatures extends SignatureTest("packageSymbolSignatures", SignatureTest.all)
@@ -84,4 +86,4 @@ class ImplicitConversionsTest3 extends SignatureTest(
8486
filterFunc = _.toString.endsWith("ClassWithConversionWithProperType.html")
8587
)
8688

87-
class SpecializedSignature extends SignatureTest("specializedSignature", SignatureTest.all)
89+
class SpecializedSignature extends SignatureTest("specializedSignature", SignatureTest.all)

0 commit comments

Comments
 (0)