Skip to content

Commit 48fe985

Browse files
committed
add new heuristic to better split extension methods with implicits
1 parent e54a934 commit 48fe985

File tree

7 files changed

+67
-41
lines changed

7 files changed

+67
-41
lines changed
Lines changed: 36 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,51 @@
11
package tests.extensionParams
22

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

68
extension [A](a: A)(using Int)
7-
def f[B](b: B): (A, B) = ???
9+
def f1[B](b: B): (A, B)
10+
= ???
811

912
extension [A](a: A)(using Int)
10-
def ff(b: A): (A, A) = ???
13+
def f2(b: A): (A, A)
14+
= ???
1115

1216
extension [A](a: A)(using Int)
13-
def fff(using String)(b: A): (A, A) = ???
17+
def f3(using String)(b: A): (A, A)
18+
= ???
1419

1520
extension (a: Char)(using Int)
16-
def ffff(using String)(b: Int): Unit = ???
21+
def f4(using String)(b: Int): Unit
22+
= ???
1723

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

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

scaladoc/resources/dotty_res/styles/scalastyle.css

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -964,6 +964,10 @@ footer .socials {
964964
color: var(--type);
965965
}
966966

967+
.groupHeader *[t="t"]:not([href]) { /* Types without links in group headers */
968+
color: var(--type);
969+
}
970+
967971
.signature *[t="t"] { /* Types with links */
968972
color: var(--type-link);
969973
}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ enum Modifier(val name: String, val prefix: Boolean):
4444
case Transparent extends Modifier("transparent", true)
4545
case Infix extends Modifier("infix", true)
4646

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

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -287,9 +287,9 @@ class MemberRenderer(signatureRenderer: SignatureRenderer)(using DocContext) ext
287287
val argsSig = InlineSignatureBuilder()
288288
.functionParameters(on.argsLists)
289289
.asInstanceOf[InlineSignatureBuilder].names.reverse
290-
val sig = typeSig ++ Signature(Plain(s"(${on.name}: ")) ++ on.signature ++ Signature(Plain(")")) ++ argsSig
291-
MGroup(span(cls := "groupHeader")(sig.map(renderElement)), members.sortBy(_.name).toSeq, on.name)
292-
}.toSeq
290+
val sig = typeSig ++ argsSig
291+
MGroup(span(cls := "groupHeader")(sig.map(renderElement)), members.sortBy(_.name).toSeq, on.name) -> on.position
292+
}.toSeq.sortBy(_._2).map(_._1)
293293

294294
div(cls := "membersList")(renderTabs(
295295
singleSelection = false,

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

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -134,14 +134,13 @@ trait ClassLikeSupport:
134134
val termParams = dd.symbol.extendedTermParamLists.zipWithIndex.flatMap { case (paramList, index) =>
135135
memberInfo.paramLists(index) match
136136
case EvidenceOnlyParameterList => Nil
137-
case info: RegularParameterList =>
137+
case info: RegularParameterList =>
138138
Seq(ParametersList(paramList.params.map(mkParameter(_, memberInfo = info)), paramListModifier(paramList.params)))
139139
}
140140
val target = ExtensionTarget(
141141
extSym.symbol.normalizedName,
142142
typeParams,
143143
termParams,
144-
extSym.tpt.asSignature,
145144
extSym.tpt.symbol.dri,
146145
extSym.symbol.pos.get.start
147146
)
@@ -495,14 +494,13 @@ trait ClassLikeSupport:
495494
// Documenting method slightly different then its definition is withing the 'undefiend behaviour'.
496495
symbol.paramSymss.flatten.find(_.name == name).exists(_.flags.is(Flags.Implicit))
497496

498-
def handlePolyType(polyType: PolyType): MemberInfo =
499-
MemberInfo(polyType.paramNames.zip(polyType.paramBounds).toMap, List.empty, polyType.resType)
497+
def handlePolyType(memberInfo: MemberInfo, polyType: PolyType): MemberInfo =
498+
MemberInfo(polyType.paramNames.zip(polyType.paramBounds).toMap, memberInfo.paramLists, polyType.resType)
500499

501500
def handleMethodType(memberInfo: MemberInfo, methodType: MethodType): MemberInfo =
502501
val rawParams = methodType.paramNames.zip(methodType.paramTypes).toMap
503502
val (evidences, notEvidences) = rawParams.partition(e => isSyntheticEvidence(e._1))
504503

505-
506504
def findParamRefs(t: TypeRepr): Seq[ParamRef] = t match
507505
case paramRef: ParamRef => Seq(paramRef)
508506
case AppliedType(_, args) => args.flatMap(findParamRefs)
@@ -539,7 +537,7 @@ trait ClassLikeSupport:
539537
MemberInfo(memberInfo.genericTypes, memberInfo.paramLists, byNameType.underlying)
540538

541539
def recursivelyCalculateMemberInfo(memberInfo: MemberInfo): MemberInfo = memberInfo.res match
542-
case p: PolyType => recursivelyCalculateMemberInfo(handlePolyType(p))
540+
case p: PolyType => recursivelyCalculateMemberInfo(handlePolyType(memberInfo, p))
543541
case m: MethodType => recursivelyCalculateMemberInfo(handleMethodType(memberInfo, m))
544542
case b: ByNameType => handleByNameType(memberInfo, b)
545543
case _ => memberInfo
@@ -551,4 +549,4 @@ trait ClassLikeSupport:
551549
if parameters(0).symbol.flags.is(Flags.Given) then "using "
552550
else if parameters(0).symbol.flags.is(Flags.Implicit) then "implicit "
553551
else ""
554-
else ""
552+
else ""

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

Lines changed: 17 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -146,11 +146,19 @@ object SymOps:
146146

147147
def extendedSymbol: Option[reflect.ValDef] =
148148
import reflect.*
149-
Option.when(sym.isExtensionMethod){
150-
val termParamss = sym.tree.asInstanceOf[DefDef].termParamss
151-
if sym.isLeftAssoc || termParamss.size == 1 then termParamss(0).params(0)
152-
else termParamss(1).params(0)
153-
}
149+
if sym.isExtensionMethod then
150+
sym.extendedTermParamLists.find(param => !param.isImplicit && !param.isGiven).flatMap(_.params.headOption)
151+
else None
152+
153+
def splitExtensionParamList: (List[reflect.ParamClause], List[reflect.ParamClause]) =
154+
import reflect.*
155+
val method = sym.tree.asInstanceOf[DefDef]
156+
(for {
157+
defPosition <- method.symbol.pos
158+
defStart <- scala.util.Try(defPosition.start).toOption
159+
} yield {
160+
method.paramss.partition(_.params.headOption.map(_.symbol.pos.get.start < defStart).getOrElse(false))
161+
}).getOrElse(List.empty, List.empty)
154162

155163
def extendedTypeParams: List[reflect.TypeDef] =
156164
import reflect.*
@@ -159,15 +167,9 @@ object SymOps:
159167

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

172174
def nonExtensionTermParamLists: List[reflect.TermParamClause] =
173175
import reflect.*
@@ -184,14 +186,7 @@ object SymOps:
184186
}
185187

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

196191
def nonExtensionLeadingTypeParams: List[reflect.TypeDef] =
197192
import reflect.*

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)

0 commit comments

Comments
 (0)