11package dotty .tools .scaladoc .tasty
22
3- import scala .jdk .CollectionConverters ._
43import dotty .tools .scaladoc ._
54import dotty .tools .scaladoc .{Signature => DSignature }
6- import dotty .tools .scaladoc .Inkuire
75
86import scala .quoted ._
97
@@ -148,18 +146,44 @@ trait ClassLikeSupport:
148146 private def isDocumentableExtension (s : Symbol ) =
149147 ! s.isHiddenByVisibility && ! s.isSyntheticFunc && s.isExtensionMethod
150148
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+
151179 private def parseMember (c : ClassDef )(s : Tree ): Option [Member ] = processTreeOpt(s) { s match
152180 case dd : DefDef if isDocumentableExtension(dd.symbol) =>
153181 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+
163187 val target = ExtensionTarget (
164188 extSym.symbol.normalizedName,
165189 typeParams,
@@ -351,45 +375,20 @@ trait ClassLikeSupport:
351375 specificKind : (Kind .Def => Kind ) = identity
352376 ): Member =
353377 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))))
393392 )
394393
395394 val methodKind =
@@ -456,8 +455,7 @@ trait ClassLikeSupport:
456455 def mkTypeArgument (
457456 argument : TypeDef ,
458457 classDef : ClassDef ,
459- memberInfo : Map [String , TypeBounds ] = Map .empty,
460- contextBounds : Map [String , DSignature ] = Map .empty,
458+ contextBounds : List [TypeRepr ] = Nil ,
461459 ): TypeParameter =
462460 val variancePrefix : " +" | " -" | " " =
463461 if argument.symbol.flags.is(Flags .Covariant ) then " +"
@@ -466,11 +464,13 @@ trait ClassLikeSupport:
466464
467465 val name = argument.symbol.normalizedName
468466 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+ )
474474
475475 TypeParameter (
476476 argument.symbol.getAnnotations(),
@@ -511,9 +511,9 @@ trait ClassLikeSupport:
511511
512512 def parseValDef (c : ClassDef , valDef : ValDef ): Member =
513513 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 )
515515 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))
517517 else if valDef.symbol.flags.is(Flags .Enum ) then Kind .EnumCase (Kind .Val )
518518 else defaultKind
519519
@@ -523,7 +523,7 @@ trait ClassLikeSupport:
523523 .filterNot(m => m == Modifier .Lazy || m == Modifier .Final )
524524 case _ => valDef.symbol.getExtraModifiers()
525525
526- mkMember(valDef.symbol, kind, memberInfo.res.asSignature(c) )(
526+ mkMember(valDef.symbol, kind, sig )(
527527 modifiers = modifiers,
528528 deprecated = valDef.symbol.isDeprecated(),
529529 experimental = valDef.symbol.isExperimental()
@@ -554,102 +554,6 @@ trait ClassLikeSupport:
554554 experimental = experimental
555555 )
556556
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-
653557 private def paramListModifier (parameters : Seq [ValDef ]): String =
654558 if parameters.size > 0 then
655559 if parameters(0 ).symbol.flags.is(Flags .Given ) then " using "
0 commit comments