Skip to content

Commit be120fa

Browse files
authored
Scaladoc: new heuristic for extension method parameter extraction (#14810)
Before this changes, it wouldn't generate due to incorrect extension parameters extractor functions. It also implements new heuristic to distinct extensions and method parameters based on definition position. Also fixed type parameter coloring by adding new css rule, to match doc style. Based on #14321 , it should be closed after this PR is merged. Documentation for test `tests.extensionParams` ![image](https://user-images.githubusercontent.com/48657087/160649612-ac5b1883-0e28-4acb-9886-c9108b376665.png)
2 parents 4a11252 + 6a12242 commit be120fa

File tree

7 files changed

+88
-40
lines changed

7 files changed

+88
-40
lines changed

Diff for: scaladoc-testcases/src/tests/extensionParams.scala

+40-8
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,54 @@
11
package tests.extensionParams
22

3+
trait Animal
4+
35
extension [A](thiz: A)
4-
def toTuple2[B](that: B): (A, B) = thiz -> that
6+
def toTuple2[B](that: B): (A, B)
7+
= thiz -> that
58

69
extension [A](a: A)(using Int)
7-
def f[B](b: B): (A, B) = ???
10+
def f1[B](b: B): (A, B)
11+
= ???
812

913
extension [A](a: A)(using Int)
10-
def ff(b: A): (A, A) = ???
14+
def f2(b: A): (A, A)
15+
= ???
1116

1217
extension [A](a: A)(using Int)
13-
def fff(using String)(b: A): (A, A) = ???
18+
def f3(using String)(b: A): (A, A)
19+
= ???
1420

1521
extension (a: Char)(using Int)
16-
def ffff(using String)(b: Int): Unit = ???
22+
def f4(using String)(b: Int): Unit
23+
= ???
1724

1825
extension (a: Char)(using Int)
19-
def fffff[B](using String)(b: B): Unit = ???
26+
def f5[B](using String)(b: B): Unit
27+
= ???
28+
29+
extension [A <: List[Char]](a: Int)(using Int)
30+
def f6[B](b: B): (A, B)
31+
= ???
32+
33+
extension [A <: List[Char]](using String)(using Unit)(a: A)(using Int)(using Number)
34+
def f7[B, C](b: B)(c: C): (A, B)
35+
= ???
36+
37+
extension [A <: List[Char]](using String)(using Unit)(a: A)(using Int)(using Number)
38+
def f8(b: Any)(c: Any): Any
39+
= ???
40+
41+
extension [A <: List[Char]](using String)(using Unit)(a: A)(using Int)(using Number)
42+
def f9[B, C](using Int)(b: B)(c: C): (A, B)
43+
= ???
44+
45+
extension [A <: List[Char]](using String)(using Unit)(a: A)(using Int)(using Number)
46+
def f10(using Int)(b: Any)(c: Any): Any
47+
= ???
48+
49+
def f12(using Int)(b: A)(c: String): Number
50+
= ???
2051

21-
extension [A <: List[Char]](a: A)(using Int)
22-
def ffffff[B](b: B): (A, B) = ???
52+
extension (using String)(using Unit)(a: Animal)(using Int)(using Number)
53+
def f11(b: Any)(c: Any): Any
54+
= ???

Diff for: scaladoc/resources/dotty_res/styles/scalastyle.css

+1-2
Original file line numberDiff line numberDiff line change
@@ -962,8 +962,7 @@ footer .socials {
962962
color: var(--type);
963963
}
964964

965-
.signature *[t="t"] {
966-
/* Types with links */
965+
.signature *[t="t"] { /* Types with links */
967966
color: var(--type-link);
968967
}
969968

Diff for: scaladoc/src/dotty/tools/scaladoc/renderers/MemberRenderer.scala

+2-2
Original file line numberDiff line numberDiff line change
@@ -415,8 +415,8 @@ class MemberRenderer(signatureRenderer: SignatureRenderer)(using DocContext) ext
415415
.functionParameters(on.argsLists)
416416
.content
417417
val sig = typeSig ++ Signature(Plain(s"(${on.name}: ")) ++ on.signature ++ Signature(Plain(")")) ++ argsSig
418-
MGroup(span(cls := "groupHeader")(sig.map(renderElement(_))), members.sortBy(_.name).toSeq, on.name)
419-
}.toSeq
418+
MGroup(span(cls := "groupHeader")(sig.map(renderElement(_))), members.sortBy(_.name).toSeq, on.name) -> on.position
419+
}.toSeq.sortBy(_._2).map(_._1)
420420

421421
div(cls := "membersList expand")(
422422
renderTabs(

Diff for: scaladoc/src/dotty/tools/scaladoc/renderers/Resources.scala

+9-1
Original file line numberDiff line numberDiff line change
@@ -177,7 +177,15 @@ trait Resources(using ctx: DocContext) extends Locations, Writer:
177177

178178
def extensionTarget(member: Member): String =
179179
member.kind match
180-
case Kind.Extension(on, _) => flattenToText(on.signature)
180+
case Kind.Extension(on, _) =>
181+
val typeSig = SignatureBuilder()
182+
.keyword("extension ")
183+
.generics(on.typeParams)
184+
.content
185+
val argsSig = SignatureBuilder()
186+
.functionParameters(on.argsLists)
187+
.content
188+
flattenToText(typeSig ++ argsSig)
181189
case _ => ""
182190

183191
def docPartRenderPlain(d: DocPart): String =

Diff for: scaladoc/src/dotty/tools/scaladoc/tasty/ClassLikeSupport.scala

+3-4
Original file line numberDiff line numberDiff line change
@@ -539,14 +539,13 @@ trait ClassLikeSupport:
539539
// Documenting method slightly different then its definition is withing the 'undefiend behaviour'.
540540
symbol.paramSymss.flatten.find(_.name == name).exists(_.flags.is(Flags.Implicit))
541541

542-
def handlePolyType(polyType: PolyType): MemberInfo =
543-
MemberInfo(polyType.paramNames.zip(polyType.paramBounds).toMap, List.empty, polyType.resType)
542+
def handlePolyType(memberInfo: MemberInfo, polyType: PolyType): MemberInfo =
543+
MemberInfo(polyType.paramNames.zip(polyType.paramBounds).toMap, memberInfo.paramLists, polyType.resType)
544544

545545
def handleMethodType(memberInfo: MemberInfo, methodType: MethodType): MemberInfo =
546546
val rawParams = methodType.paramNames.zip(methodType.paramTypes).toMap
547547
val (evidences, notEvidences) = rawParams.partition(e => isSyntheticEvidence(e._1))
548548

549-
550549
def findParamRefs(t: TypeRepr): Seq[ParamRef] = t match
551550
case paramRef: ParamRef => Seq(paramRef)
552551
case AppliedType(_, args) => args.flatMap(findParamRefs)
@@ -583,7 +582,7 @@ trait ClassLikeSupport:
583582
MemberInfo(memberInfo.genericTypes, memberInfo.paramLists, byNameType.underlying)
584583

585584
def recursivelyCalculateMemberInfo(memberInfo: MemberInfo): MemberInfo = memberInfo.res match
586-
case p: PolyType => recursivelyCalculateMemberInfo(handlePolyType(p))
585+
case p: PolyType => recursivelyCalculateMemberInfo(handlePolyType(memberInfo, p))
587586
case m: MethodType => recursivelyCalculateMemberInfo(handleMethodType(memberInfo, m))
588587
case b: ByNameType => handleByNameType(memberInfo, b)
589588
case _ => memberInfo

Diff for: scaladoc/src/dotty/tools/scaladoc/tasty/SymOps.scala

+31-23
Original file line numberDiff line numberDiff line change
@@ -147,28 +147,43 @@ object SymOps:
147147

148148
def extendedSymbol: Option[reflect.ValDef] =
149149
import reflect.*
150-
Option.when(sym.isExtensionMethod){
151-
val termParamss = sym.tree.asInstanceOf[DefDef].termParamss
152-
if sym.isLeftAssoc || termParamss.size == 1 then termParamss(0).params(0)
153-
else termParamss(1).params(0)
150+
if sym.isExtensionMethod then
151+
sym.extendedTermParamLists.find(param => !param.isImplicit && !param.isGiven).flatMap(_.params.headOption)
152+
else None
153+
154+
def splitExtensionParamList: (List[reflect.ParamClause], List[reflect.ParamClause]) =
155+
import reflect.*
156+
157+
def getPositionStartOption(pos: Option[Position]): Option[Int] = pos.flatMap {
158+
case dotty.tools.dotc.util.NoSourcePosition => None
159+
case pos: Position => Some(pos.start)
154160
}
155161

162+
def comparePositionStarts(posA: Option[Position], posB: Option[Position]): Option[Boolean] =
163+
for {
164+
startA <- getPositionStartOption(posA)
165+
startB <- getPositionStartOption(posB)
166+
} yield startA < startB
167+
168+
sym.tree match
169+
case tree: DefDef =>
170+
tree.paramss.partition(_.params.headOption.flatMap(param =>
171+
comparePositionStarts(param.symbol.pos, tree.symbol.pos)).getOrElse(false)
172+
)
173+
case _ => Nil -> Nil
174+
156175
def extendedTypeParams: List[reflect.TypeDef] =
157176
import reflect.*
158-
val method = sym.tree.asInstanceOf[DefDef]
159-
method.leadingTypeParams
177+
sym.tree match
178+
case tree: DefDef =>
179+
tree.leadingTypeParams
180+
case _ => Nil
160181

161182
def extendedTermParamLists: List[reflect.TermParamClause] =
162183
import reflect.*
163-
if sym.nonExtensionLeadingTypeParams.nonEmpty then
164-
sym.nonExtensionParamLists.takeWhile {
165-
case _: TypeParamClause => false
166-
case _ => true
167-
}.collect {
168-
case tpc: TermParamClause => tpc
169-
}
170-
else
171-
List.empty
184+
sym.splitExtensionParamList._1.collect {
185+
case tpc: TermParamClause => tpc
186+
}
172187

173188
def nonExtensionTermParamLists: List[reflect.TermParamClause] =
174189
import reflect.*
@@ -185,14 +200,7 @@ object SymOps:
185200
}
186201

187202
def nonExtensionParamLists: List[reflect.ParamClause] =
188-
import reflect.*
189-
val method = sym.tree.asInstanceOf[DefDef]
190-
if sym.isExtensionMethod then
191-
val params = method.paramss
192-
val toDrop = if method.leadingTypeParams.nonEmpty then 2 else 1
193-
if sym.isLeftAssoc || params.size == 1 then params.drop(toDrop)
194-
else params.head :: params.tail.drop(toDrop)
195-
else method.paramss
203+
sym.splitExtensionParamList._2
196204

197205
def nonExtensionLeadingTypeParams: List[reflect.TypeDef] =
198206
import reflect.*

Diff for: scaladoc/test/dotty/tools/scaladoc/signatures/TranslatableSignaturesTestCases.scala

+2
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,8 @@ class MergedPackageSignatures extends SignatureTest("mergedPackage", SignatureTe
4141

4242
class ExtensionMethodSignature extends SignatureTest("extensionMethodSignatures", SignatureTest.all)
4343

44+
class ExtensionMethodParamsSignature extends SignatureTest("extensionParams", SignatureTest.all)
45+
4446
class ClassModifiers extends SignatureTest("classModifiers", SignatureTest.classlikeKinds)
4547

4648
class EnumSignatures extends SignatureTest("enumSignatures", SignatureTest.all)

0 commit comments

Comments
 (0)