1
1
package dotty .tools .scaladoc .tasty
2
2
3
- import scala .jdk .CollectionConverters ._
4
3
import dotty .tools .scaladoc ._
5
4
import dotty .tools .scaladoc .{Signature => DSignature }
6
- import dotty .tools .scaladoc .Inkuire
7
5
8
6
import scala .quoted ._
9
7
@@ -148,18 +146,44 @@ trait ClassLikeSupport:
148
146
private def isDocumentableExtension (s : Symbol ) =
149
147
! s.isHiddenByVisibility && ! s.isSyntheticFunc && s.isExtensionMethod
150
148
149
+ private def isEvidence (tpc : TermParamClause ) =
150
+ (tpc.isGiven || tpc.isImplicit) && tpc.params.forall(_.name.startsWith(NameKinds .ContextBoundParamName .separator))
151
+
152
+ private def extractEvidences (tpcs : List [TermParamClause ]): (Map [Symbol , List [TypeRepr ]], List [TermParamClause ]) =
153
+ val (evidenceParams, termParams) = tpcs.partition(isEvidence)
154
+ val evidenceMap = evidenceParams.flatMap(_.params).map(p => (p.tpt, p.tpt.tpe)).collect {
155
+ case (Applied (bound, List (arg : TypeTree )), _) => (arg.tpe.typeSymbol, bound.tpe)
156
+ case (_, AppliedType (bound, List (arg))) => (arg.typeSymbol, bound)
157
+ // It seems like here we could do:
158
+ // (...).map(_.tpt.tpe).collect {
159
+ // case AppliedType(bound, List(arg)) => (arg.typeSymbol, bound)
160
+ // or:
161
+ // (...).map(_.tpt).collect {
162
+ // case Applied(bound, List(arg: TypeTree)) => (arg.tpe.typeSymbol, bound.tpe)
163
+ //
164
+ // First one doesn't always work because .tpe in some cases causes type lambda reductions, eg:
165
+ // def foo[T : ([X] =>> String)]
166
+ // after desugaring:
167
+ // def foo[T](implicit ecidence$1 : ([X] =>> String)[T])
168
+ // tree for this evidence looks like: ([X] =>> String)[T]
169
+ // but type repr looks like: String
170
+ // (see scaladoc-testcases/src/tests/contextBounds.scala)
171
+ //
172
+ // Second one doesn't always work, because the tree is sometimes `Inferred`
173
+ // (see toArray inherited in scaladoc-testcases/src/tests/classSignatureTestSource.scala)
174
+ //
175
+ // TODO: check if those two cases can occur at the same time
176
+ }.groupMap(_._1)(_._2).withDefaultValue(Nil )
177
+ (evidenceMap, termParams)
178
+
151
179
private def parseMember (c : ClassDef )(s : Tree ): Option [Member ] = processTreeOpt(s) { s match
152
180
case dd : DefDef if isDocumentableExtension(dd.symbol) =>
153
181
dd.symbol.extendedSymbol.map { extSym =>
154
- val memberInfo = unwrapMemberInfo(c, dd.symbol)
155
- val typeParams = dd.symbol.extendedTypeParams.map(mkTypeArgument(_, c, memberInfo.genericTypes))
156
- val termParams = dd.symbol.extendedTermParamLists.zipWithIndex.flatMap { case (termParamList, index) =>
157
- memberInfo.termParamLists(index) match
158
- case MemberInfo .EvidenceOnlyParameterList => None
159
- case MemberInfo .RegularParameterList (info) =>
160
- Some (api.TermParameterList (termParamList.params.map(mkParameter(_, c, memberInfo = info)), paramListModifier(termParamList.params)))
161
- case _ => assert(false , " memberInfo.termParamLists contains a type parameter list !" )
162
- }
182
+ val (evidenceMap, termParamClauses) = extractEvidences(dd.symbol.extendedTermParamLists)
183
+ val termParams = termParamClauses.map: tpc =>
184
+ api.TermParameterList (tpc.params.map(mkParameter(_, c)), paramListModifier(tpc.params))
185
+ val typeParams = dd.symbol.extendedTypeParams.map(td => mkTypeArgument(td, c, evidenceMap(td.symbol)))
186
+
163
187
val target = ExtensionTarget (
164
188
extSym.symbol.normalizedName,
165
189
typeParams,
@@ -351,45 +375,20 @@ trait ClassLikeSupport:
351
375
specificKind : (Kind .Def => Kind ) = identity
352
376
): Member =
353
377
val method = methodSymbol.tree.asInstanceOf [DefDef ]
354
- val paramLists = methodSymbol.nonExtensionParamLists
355
-
356
- val memberInfo = unwrapMemberInfo(c, methodSymbol)
357
-
358
- val unshuffledMemberInfoParamLists =
359
- if methodSymbol.isExtensionMethod && methodSymbol.isRightAssoc then
360
- // Taken from RefinedPrinter.scala
361
- // If you change the names of the clauses below, also change them in right-associative-extension-methods.md
362
- val (leftTyParams, rest1) = memberInfo.paramLists match
363
- case fst :: tail if fst.isType => (List (fst), tail)
364
- case other => (List (), other)
365
- val (leadingUsing, rest2) = rest1.span(_.isUsing)
366
- val (rightTyParams, rest3) = rest2.span(_.isType)
367
- val (rightParam, rest4) = rest3.splitAt(1 )
368
- val (leftParam, rest5) = rest4.splitAt(1 )
369
- val (trailingUsing, rest6) = rest5.span(_.isUsing)
370
- if leftParam.nonEmpty then
371
- // leftTyParams ::: leadingUsing ::: leftParam ::: trailingUsing ::: rightTyParams ::: rightParam ::: rest6
372
- // because of takeRight after, this is equivalent to the following:
373
- rightTyParams ::: rightParam ::: rest6
374
- else
375
- memberInfo.paramLists // it wasn't a binary operator, after all.
376
- else
377
- memberInfo.paramLists
378
-
379
- val croppedUnshuffledMemberInfoParamLists = unshuffledMemberInfoParamLists.takeRight(paramLists.length)
380
-
381
- val basicDefKind : Kind .Def = Kind .Def (
382
- paramLists.zip(croppedUnshuffledMemberInfoParamLists).flatMap{
383
- case (_ : TermParamClause , MemberInfo .EvidenceOnlyParameterList ) => Nil
384
- case (pList : TermParamClause , MemberInfo .RegularParameterList (info)) =>
385
- Some (Left (api.TermParameterList (pList.params.map(
386
- mkParameter(_, c, paramPrefix, memberInfo = info)), paramListModifier(pList.params)
387
- )))
388
- case (TypeParamClause (genericTypeList), MemberInfo .TypeParameterList (memInfoTypes)) =>
389
- Some (Right (genericTypeList.map(mkTypeArgument(_, c, memInfoTypes, memberInfo.contextBounds))))
390
- case (_,_) =>
391
- assert(false , s " croppedUnshuffledMemberInfoParamLists and SymOps.nonExtensionParamLists disagree on whether this clause is a type or term one " )
392
- }
378
+ val paramLists = methodSymbol.nonExtensionParamLists.filter:
379
+ case TypeParamClause (_) => true
380
+ case tpc@ TermParamClause (_) => ! isEvidence(tpc)
381
+
382
+ val evidenceMap = extractEvidences(method.termParamss)._1
383
+
384
+ val basicDefKind : Kind .Def = Kind .Def (paramLists.map:
385
+ case TermParamClause (vds) =>
386
+ Left (api.TermParameterList (
387
+ vds.map(mkParameter(_, c, paramPrefix)),
388
+ paramListModifier(vds)
389
+ ))
390
+ case TypeParamClause (genericTypeList) =>
391
+ Right (genericTypeList.map(td => mkTypeArgument(td, c, evidenceMap(td.symbol))))
393
392
)
394
393
395
394
val methodKind =
@@ -456,8 +455,7 @@ trait ClassLikeSupport:
456
455
def mkTypeArgument (
457
456
argument : TypeDef ,
458
457
classDef : ClassDef ,
459
- memberInfo : Map [String , TypeBounds ] = Map .empty,
460
- contextBounds : Map [String , DSignature ] = Map .empty,
458
+ contextBounds : List [TypeRepr ] = Nil ,
461
459
): TypeParameter =
462
460
val variancePrefix : " +" | " -" | " " =
463
461
if argument.symbol.flags.is(Flags .Covariant ) then " +"
@@ -466,11 +464,13 @@ trait ClassLikeSupport:
466
464
467
465
val name = argument.symbol.normalizedName
468
466
val normalizedName = if name.matches(" _\\ $\\ d*" ) then " _" else name
469
- val boundsSignature = memberInfo.get(name).fold(argument.rhs.asSignature(classDef))(_.asSignature(classDef))
470
- val signature = contextBounds.get(name) match
471
- case None => boundsSignature
472
- case Some (contextBoundsSignature) =>
473
- boundsSignature ++ DSignature (Plain (" : " )) ++ contextBoundsSignature
467
+ val boundsSignature = argument.rhs.asSignature(classDef)
468
+ val signature = boundsSignature ++ contextBounds.flatMap(tr =>
469
+ val wrap = tr match
470
+ case _ : TypeLambda => true
471
+ case _ => false
472
+ Plain (" : " ) +: inParens(tr.asSignature(classDef), wrap)
473
+ )
474
474
475
475
TypeParameter (
476
476
argument.symbol.getAnnotations(),
@@ -511,9 +511,9 @@ trait ClassLikeSupport:
511
511
512
512
def parseValDef (c : ClassDef , valDef : ValDef ): Member =
513
513
def defaultKind = if valDef.symbol.flags.is(Flags .Mutable ) then Kind .Var else Kind .Val
514
- val memberInfo = unwrapMemberInfo(c, valDef.symbol )
514
+ val sig = valDef.tpt.tpe.asSignature(c )
515
515
val kind = if valDef.symbol.flags.is(Flags .Implicit ) then Kind .Implicit (Kind .Val , extractImplicitConversion(valDef.tpt.tpe))
516
- else if valDef.symbol.flags.is(Flags .Given ) then Kind .Given (Kind .Val , Some (memberInfo.res.asSignature(c) ), extractImplicitConversion(valDef.tpt.tpe))
516
+ else if valDef.symbol.flags.is(Flags .Given ) then Kind .Given (Kind .Val , Some (sig ), extractImplicitConversion(valDef.tpt.tpe))
517
517
else if valDef.symbol.flags.is(Flags .Enum ) then Kind .EnumCase (Kind .Val )
518
518
else defaultKind
519
519
@@ -523,7 +523,7 @@ trait ClassLikeSupport:
523
523
.filterNot(m => m == Modifier .Lazy || m == Modifier .Final )
524
524
case _ => valDef.symbol.getExtraModifiers()
525
525
526
- mkMember(valDef.symbol, kind, memberInfo.res.asSignature(c) )(
526
+ mkMember(valDef.symbol, kind, sig )(
527
527
modifiers = modifiers,
528
528
deprecated = valDef.symbol.isDeprecated(),
529
529
experimental = valDef.symbol.isExperimental()
@@ -554,102 +554,6 @@ trait ClassLikeSupport:
554
554
experimental = experimental
555
555
)
556
556
557
-
558
- case class MemberInfo (
559
- paramLists : List [MemberInfo .ParameterList ],
560
- res : TypeRepr ,
561
- contextBounds : Map [String , DSignature ] = Map .empty,
562
- ){
563
- val genericTypes : Map [String , TypeBounds ] = paramLists.collect{ case MemberInfo .TypeParameterList (types) => types }.headOption.getOrElse(Map ())
564
-
565
- val termParamLists : List [MemberInfo .ParameterList ] = paramLists.filter(_.isTerm)
566
- }
567
-
568
- object MemberInfo :
569
- enum ParameterList (val isTerm : Boolean , val isUsing : Boolean ):
570
- inline def isType = ! isTerm
571
- case EvidenceOnlyParameterList extends ParameterList (isTerm = true , isUsing = false )
572
- case RegularParameterList (m : Map [String , TypeRepr ])(isUsing : Boolean ) extends ParameterList (isTerm = true , isUsing)
573
- case TypeParameterList (m : Map [String , TypeBounds ]) extends ParameterList (isTerm = false , isUsing = false )
574
-
575
- export ParameterList .{RegularParameterList , EvidenceOnlyParameterList , TypeParameterList }
576
-
577
-
578
-
579
- def unwrapMemberInfo (c : ClassDef , symbol : Symbol ): MemberInfo =
580
- val qualTypeRepr = if c.symbol.isClassDef then This (c.symbol).tpe else typeForClass(c)
581
- val baseTypeRepr = qualTypeRepr.memberType(symbol)
582
-
583
- def isSyntheticEvidence (name : String ) =
584
- if ! name.startsWith(NameKinds .ContextBoundParamName .separator) then false else
585
- // This assumes that every parameter that starts with `evidence$` and is implicit is generated by compiler to desugar context bound.
586
- // Howrever, this is just a heuristic, so
587
- // `def foo[A](evidence$1: ClassTag[A]) = 1`
588
- // will be documented as
589
- // `def foo[A: ClassTag] = 1`.
590
- // Scala spec states that `$` should not be used in names and behaviour may be undefiend in such case.
591
- // Documenting method slightly different then its definition is withing the 'undefiend behaviour'.
592
- symbol.paramSymss.flatten.find(_.name == name).exists(p =>
593
- p.flags.is(Flags .Given ) || p.flags.is(Flags .Implicit ))
594
-
595
- def handlePolyType (memberInfo : MemberInfo , polyType : PolyType ): MemberInfo =
596
- val typeParamList = MemberInfo .TypeParameterList (polyType.paramNames.zip(polyType.paramBounds).toMap)
597
- MemberInfo (memberInfo.paramLists :+ typeParamList, polyType.resType)
598
-
599
- def handleMethodType (memberInfo : MemberInfo , methodType : MethodType ): MemberInfo =
600
- val rawParams = methodType.paramNames.zip(methodType.paramTypes).toMap
601
- val isUsing = methodType.isImplicit
602
- val (evidences, notEvidences) = rawParams.partition(e => isSyntheticEvidence(e._1))
603
-
604
- def findParamRefs (t : TypeRepr ): Seq [ParamRef ] = t match
605
- case paramRef : ParamRef => Seq (paramRef)
606
- case AppliedType (_, args) => args.flatMap(findParamRefs)
607
- case MatchType (bound, scrutinee, cases) =>
608
- findParamRefs(bound) ++ findParamRefs(scrutinee)
609
- case _ => Nil
610
-
611
- def nameForRef (ref : ParamRef ): String =
612
- val PolyType (names, _, _) = ref.binder: @ unchecked
613
- names(ref.paramNum)
614
-
615
- val (paramsThatLookLikeContextBounds, contextBounds) =
616
- evidences.partitionMap {
617
- case (_, AppliedType (tpe, List (typeParam : ParamRef ))) =>
618
- Right (nameForRef(typeParam) -> tpe.asSignature(c))
619
- case (name, original) =>
620
- findParamRefs(original) match
621
- case Nil => Left ((name, original))
622
- case typeParam :: _ =>
623
- val name = nameForRef(typeParam)
624
- val signature = Seq (
625
- Plain (" ([" ),
626
- dotty.tools.scaladoc.Type (name, None ),
627
- Plain (" ]" ),
628
- Keyword (" =>> " ),
629
- ) ++ original.asSignature(c) ++ Seq (Plain (" )" ))
630
- Right (name -> signature.toList)
631
- }
632
-
633
- val newParams = notEvidences ++ paramsThatLookLikeContextBounds
634
-
635
- val termParamList = if newParams.isEmpty && contextBounds.nonEmpty
636
- then MemberInfo .EvidenceOnlyParameterList
637
- else MemberInfo .RegularParameterList (newParams)(isUsing)
638
-
639
-
640
- MemberInfo (memberInfo.paramLists :+ termParamList, methodType.resType, contextBounds.toMap)
641
-
642
- def handleByNameType (memberInfo : MemberInfo , byNameType : ByNameType ): MemberInfo =
643
- MemberInfo (memberInfo.paramLists, byNameType.underlying)
644
-
645
- def recursivelyCalculateMemberInfo (memberInfo : MemberInfo ): MemberInfo = memberInfo.res match
646
- case p : PolyType => recursivelyCalculateMemberInfo(handlePolyType(memberInfo, p))
647
- case m : MethodType => recursivelyCalculateMemberInfo(handleMethodType(memberInfo, m))
648
- case b : ByNameType => handleByNameType(memberInfo, b)
649
- case _ => memberInfo
650
-
651
- recursivelyCalculateMemberInfo(MemberInfo (List .empty, baseTypeRepr))
652
-
653
557
private def paramListModifier (parameters : Seq [ValDef ]): String =
654
558
if parameters.size > 0 then
655
559
if parameters(0 ).symbol.flags.is(Flags .Given ) then " using "
0 commit comments