Skip to content

Commit 6f8e56d

Browse files
committed
WIP: add heuristic to support Context Bounds
1 parent 2260aa2 commit 6f8e56d

File tree

4 files changed

+89
-7
lines changed

4 files changed

+89
-7
lines changed
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
package tests
2+
package contextBounds
3+
4+
import scala.reflect.ClassTag
5+
6+
class A:
7+
def basic[A : ClassTag]: A
8+
= ???
9+
10+
trait Build[X, Y]
11+
trait From[A, B]
12+
def b[T : ([T] =>> Build[From[T, T], T])](t: T): T
13+
= t
14+
15+
trait Build2[X[_], Y]
16+
trait From2[A, B]
17+
18+
def b2[T : ([T] =>> Build2[[Y] =>> From2[T, Y], T])](t: T): T
19+
= t
20+
21+
// Tests not support multiline signatures
22+
def a[T <: String | Int : ([T] =>> T match { case String => A case Int => B })](t: T): T
23+
= t

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

Lines changed: 61 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -476,9 +476,14 @@ trait ClassLikeSupport:
476476
val memberInfo = unwrapMemberInfo(c, methodSymbol)
477477

478478
val basicKind: Kind.Def = Kind.Def(
479-
genericTypes.map(mkTypeArgument(_, memberInfo.genericTypes)),
480-
paramLists.zipWithIndex.map { (pList, index) =>
481-
ParametersList(pList.params.map(mkParameter(_, paramPrefix, memberInfo = memberInfo.paramLists(index))), paramListModifier(pList.params))
479+
genericTypes.map(mkTypeArgument(_, memberInfo.genericTypes, memberInfo.contextBounds)),
480+
paramLists.zipWithIndex.flatMap { (pList, index) =>
481+
memberInfo.paramLists(index) match
482+
case EvidenceOnlyParameterList => Nil
483+
case info: RegularParameterList =>
484+
Seq(ParametersList(pList.params.map(
485+
mkParameter(_, paramPrefix, memberInfo = info)), paramListModifier(pList.params)
486+
))
482487
}
483488
)
484489

@@ -523,20 +528,30 @@ trait ClassLikeSupport:
523528
isGrouped
524529
)
525530

526-
def mkTypeArgument(argument: TypeDef, memberInfo: Map[String, TypeBounds] = Map.empty): TypeParameter =
531+
def mkTypeArgument(
532+
argument: TypeDef,
533+
memberInfo: Map[String, TypeBounds] = Map.empty,
534+
contextBounds: Map[String, DSignature] = Map.empty
535+
): TypeParameter =
527536
val variancePrefix: "+" | "-" | "" =
528537
if argument.symbol.flags.is(Flags.Covariant) then "+"
529538
else if argument.symbol.flags.is(Flags.Contravariant) then "-"
530539
else ""
531540

532541
val name = argument.symbol.normalizedName
533542
val normalizedName = if name.matches("_\\$\\d*") then "_" else name
543+
val boundsSignature = memberInfo.get(name).fold(argument.rhs.asSignature)(_.asSignature)
544+
val signature = contextBounds.get(name) match
545+
case None => boundsSignature
546+
case Some(contextBoundsSignature) =>
547+
boundsSignature ++ DSignature(" : ") ++ contextBoundsSignature
548+
534549
TypeParameter(
535550
argument.symbol.getAnnotations(),
536551
variancePrefix,
537552
normalizedName,
538553
argument.symbol.dri,
539-
memberInfo.get(name).fold(argument.rhs.asSignature)(_.asSignature)
554+
signature
540555
)
541556

542557
def parseTypeDef(typeDef: TypeDef): Member =
@@ -586,7 +601,18 @@ trait ClassLikeSupport:
586601
deprecated = deprecated
587602
)
588603

589-
case class MemberInfo(genericTypes: Map[String, TypeBounds], paramLists: List[Map[String, TypeRepr]], res: TypeRepr)
604+
object EvidenceOnlyParameterList
605+
type RegularParameterList = Map[String, TypeRepr]
606+
type ParameterList = RegularParameterList | EvidenceOnlyParameterList.type
607+
608+
case class MemberInfo(
609+
genericTypes: Map[String, TypeBounds],
610+
paramLists: List[ParameterList],
611+
res: TypeRepr,
612+
contextBounds: Map[String, DSignature] = Map.empty,
613+
)
614+
615+
def isSyntheticEvidence(name: String) = name.startsWith("evidence$")
590616

591617
def unwrapMemberInfo(c: ClassDef, symbol: Symbol): MemberInfo =
592618
val baseTypeRepr = memberInfo(c, symbol)
@@ -595,7 +621,35 @@ trait ClassLikeSupport:
595621
MemberInfo(polyType.paramNames.zip(polyType.paramBounds).toMap, List.empty, polyType.resType)
596622

597623
def handleMethodType(memberInfo: MemberInfo, methodType: MethodType): MemberInfo =
598-
MemberInfo(memberInfo.genericTypes, memberInfo.paramLists ++ List(methodType.paramNames.zip(methodType.paramTypes).toMap), methodType.resType)
624+
val rawParams = methodType.paramNames.zip(methodType.paramTypes).toMap
625+
val (evidences, newParams) = rawParams.partition(e => isSyntheticEvidence(e._1))
626+
val newLists: List[ParameterList] = if newParams.isEmpty && evidences.nonEmpty
627+
then memberInfo.paramLists ++ Seq(EvidenceOnlyParameterList)
628+
else memberInfo.paramLists ++ Seq(newParams)
629+
630+
def findParamRefs(t: TypeRepr): Seq[ParamRef] = t match
631+
case paramRef: ParamRef => Seq(paramRef)
632+
case AppliedType(_, args) => args.flatMap(findParamRefs)
633+
case MatchType(bound, scrutinee, cases) =>
634+
findParamRefs(bound) ++ findParamRefs(scrutinee)
635+
case _ => Nil
636+
637+
def nameForRef(ref: ParamRef): String =
638+
val PolyType(names, _, _) = ref.binder
639+
names(ref.paramNum)
640+
641+
val contextBounds =
642+
evidences.collect {
643+
case (_, AppliedType(tpe, List(typeParam: ParamRef))) =>
644+
nameForRef(typeParam) -> tpe.asSignature
645+
case (_, original) =>
646+
val typeParam = findParamRefs(original).head // TODO throw nicer error!
647+
val name = nameForRef(typeParam)
648+
val signature = Seq(s"([$name] =>> ") ++ original.asSignature ++ Seq(")")
649+
name -> signature
650+
}
651+
652+
MemberInfo(memberInfo.genericTypes, newLists , methodType.resType, contextBounds.toMap)
599653

600654
def handleByNameType(memberInfo: MemberInfo, byNameType: ByNameType): MemberInfo =
601655
MemberInfo(memberInfo.genericTypes, memberInfo.paramLists, byNameType.underlying)

scaladoc/test/dotty/tools/scaladoc/signatures/SignatureTest.scala

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,9 @@ abstract class SignatureTest(
5050
(s"Not documented signatures:\n${expectedButNotFound.mkString("\n")}")
5151
val unexpectedReport = Option.when(!unexpected.isEmpty)
5252
(s"Unexpectedly documented signatures:\n${unexpected.mkString("\n")}")
53+
54+
println("Expecting following signatures: " + expectedFromSources)
55+
5356
val reports = missingReport ++ unexpectedReport
5457

5558
if !reports.isEmpty then

scaladoc/test/dotty/tools/scaladoc/signatures/TranslatableSignaturesTestCases.scala

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,3 +84,5 @@ class ImplicitConversionsTest3 extends SignatureTest(
8484
)
8585

8686
class SpecializedSignature extends SignatureTest("specializedSignature", SignatureTest.all)
87+
88+
class ContextBounds extends SignatureTest("contextBounds", SignatureTest.all)

0 commit comments

Comments
 (0)