diff --git a/scaladoc-testcases/src/tests/exports1.scala b/scaladoc-testcases/src/tests/exports1.scala index 8c47ddc94b79..542880b3a590 100644 --- a/scaladoc-testcases/src/tests/exports1.scala +++ b/scaladoc-testcases/src/tests/exports1.scala @@ -14,18 +14,18 @@ class A: //unexpected = 1 var aVar1: 1 = 1 - type HKT[T[_], X] //expected: final type HKT = [T[_], X] =>> HKT[T, X] + type HKT[T[_], X] //expected: final type HKT = [T[_], X] =>> a.HKT[T, X] = T[X] - type SomeRandomType = (List[_] | Seq[_]) & String //expected: final type SomeRandomType = SomeRandomType - def x[T[_], X](x: X): HKT[T, X] + type SomeRandomType = (List[_] | Seq[_]) & String //expected: final type SomeRandomType = a.SomeRandomType + def x[T[_], X](x: X): HKT[T, X] //expected: def x[T[_], X](x: X): A.this.HKT[T, X] = ??? def fn[T, U]: T => U = ??? object Object //expected: val Obj: Object.type - val x: HKT[List, Int] + val x: HKT[List, Int] //expected: val x: A.this.HKT[List, Int] = ??? - class Class(val a: Int, val b: Int) extends Serializable //expected: final type Class = Class - enum Enum: //expected: final type Enum = Enum + class Class(val a: Int, val b: Int) extends Serializable //expected: final type Class = a.Class + enum Enum: //expected: final type Enum = a.Enum case A case B(i: Int) case C[T]() extends Enum diff --git a/scaladoc-testcases/src/tests/functionTypeSignatures.scala b/scaladoc-testcases/src/tests/functionTypeSignatures.scala index 0c456b9526c6..7b06c645fe31 100644 --- a/scaladoc-testcases/src/tests/functionTypeSignatures.scala +++ b/scaladoc-testcases/src/tests/functionTypeSignatures.scala @@ -4,7 +4,13 @@ type A = ((Int, Int)) => Int type B = (Int | String) => Int +type B1 = Int | String => Int //expected: type B1 = (Int | String) => Int + type C = (Int & String) => Int +type C1 = Int & String => Int //expected: type C1 = (Int & String) => Int + +type D = Int | (String => Int) + type E = (A => B) => B diff --git a/scaladoc-testcases/src/tests/infixTypes.scala b/scaladoc-testcases/src/tests/infixTypes.scala new file mode 100644 index 000000000000..acfc9044e2eb --- /dev/null +++ b/scaladoc-testcases/src/tests/infixTypes.scala @@ -0,0 +1,141 @@ +package tests +package infixTypes + +import annotation.showAsInfix + +@showAsInfix +trait SomeTrait[A, B] + +trait SomeTrait2[A, B] + +def someTrait1[C, D]: C SomeTrait D + = ??? + +def someTrait2[E, F]: SomeTrait[E, F] //expected: def someTrait2[E, F]: E SomeTrait F + = ??? + +def someTrait3[G, H]: G SomeTrait2 H //expected: def someTrait3[G, H]: SomeTrait2[G, H] + = ??? + +trait +++[A, B] + +trait ++*[A, B] + +trait ++:[A, B] + +trait +*:[A, B] + +trait ***[A, B] + +trait **:[A, B] + +def foo[A, B, C, D]: (A SomeTrait B) +++ (C SomeTrait2 D) //expected: def foo[A, B, C, D]: (A SomeTrait B) +++ SomeTrait2[C, D] + = ??? + +// left-associative, same precedence + +def a0[X, Y, Z]: X +++ Y +++ Z + = a1 + +def a1[X, Y, Z]: (X +++ Y) +++ Z //expected: def a1[X, Y, Z]: X +++ Y +++ Z + = a0 + +def a2[X, Y, Z]: X +++ (Y +++ Z) + = ??? + +def a0x[X, Y, Z]: X +++ Y ++* Z //expected: def a0x[X, Y, Z]: (X +++ Y) ++* Z + = a1x + +def a1x[X, Y, Z]: (X +++ Y) ++* Z + = a0x + +def a2x[X, Y, Z]: X +++ (Y ++* Z) + = ??? + +// right-associative, same precedence + +def a3[X, Y, Z]: X ++: Y ++: Z + = a5 + +def a4[X, Y, Z]: (X ++: Y) ++: Z + = ??? + +def a5[X, Y, Z]: X ++: (Y ++: Z) //expected: def a5[X, Y, Z]: X ++: Y ++: Z + = a3 + +def a3x[X, Y, Z]: X ++: Y +*: Z //expected: def a3x[X, Y, Z]: X ++: (Y +*: Z) + = a5x + +def a4x[X, Y, Z]: (X ++: Y) +*: Z + = ??? + +def a5x[X, Y, Z]: X ++: (Y +*: Z) + = a3x + +// left and right associative, same precedence + +def a6[X, Y, Z]: (X +++ Y) ++: Z + = ??? + +def a7[X, Y, Z]: X +++ (Y ++: Z) + = ??? + +// left-associative, mixed precedence + +def b0[X, Y, Z]: X +++ Y *** Z //expected: def b0[X, Y, Z]: X +++ (Y *** Z) + = ??? + +def b1[X, Y, Z]: (X +++ Y) *** Z + = ??? + +def b2[X, Y, Z]: X +++ (Y *** Z) + = ??? + +def b3[X, Y, Z]: X *** Y +++ Z //expected: def b3[X, Y, Z]: (X *** Y) +++ Z + = ??? + +def b4[X, Y, Z]: (X *** Y) +++ Z + = ??? + +def b5[X, Y, Z]: X *** (Y +++ Z) + = ??? + +// right-associative, mixed precedence + +def c0[X, Y, Z]: X ++: Y **: Z //expected: def c0[X, Y, Z]: X ++: (Y **: Z) + = ??? + +def c1[X, Y, Z]: (X ++: Y) **: Z + = ??? + +def c2[X, Y, Z]: X ++: (Y **: Z) + = ??? + +def c3[X, Y, Z]: X **: Y ++: Z //expected: def c3[X, Y, Z]: (X **: Y) ++: Z + = ??? + +def c4[X, Y, Z]: (X **: Y) ++: Z + = ??? + +def c5[X, Y, Z]: X **: (Y ++: Z) + = ??? + +// left and right associative, mixed precedence + +def d0[X, Y, Z]: X +++ Y **: Z //expected: def d0[X, Y, Z]: X +++ (Y **: Z) + = ??? + +def d1[X, Y, Z]: (X +++ Y) **: Z + = ??? + +def d2[X, Y, Z]: X +++ (Y **: Z) + = ??? + +def d3[X, Y, Z]: X *** Y ++: Z //expected: def d3[X, Y, Z]: (X *** Y) ++: Z + = ??? + +def d4[X, Y, Z]: (X *** Y) ++: Z + = ??? + +def d5[X, Y, Z]: X *** (Y ++: Z) + = ??? diff --git a/scaladoc-testcases/src/tests/matchTypeTuple.scala b/scaladoc-testcases/src/tests/matchTypeTuple.scala new file mode 100644 index 000000000000..59dd630cb7a6 --- /dev/null +++ b/scaladoc-testcases/src/tests/matchTypeTuple.scala @@ -0,0 +1,10 @@ +package tests +package matchTypeTuple + +// issue 16084 + +sealed trait TupleTest[Take[_, _], Drop[_, _]]: + type Split[T <: Tuple, N <: Int] = (Take[T, N], Drop[T, N]) + + inline def splitAt[This <: Tuple](n: Int): Split[This, n.type] + = ??? diff --git a/scaladoc-testcases/src/tests/pathDependentTypes.scala b/scaladoc-testcases/src/tests/pathDependentTypes.scala new file mode 100644 index 000000000000..aa4708738195 --- /dev/null +++ b/scaladoc-testcases/src/tests/pathDependentTypes.scala @@ -0,0 +1,20 @@ +package tests +package pathDependentTypes + +import deriving.Mirror.ProductOf + +// issue 16143 + +trait Foo[A]: + type Out + +trait Bar[A]: + type Out + +def foo[A](using f: Foo[A])(using b: Bar[f.Out]): b.Out + = ??? + +// issue 16057 + +def fromProductTyped[P <: Product](p: P)(using m: ProductOf[P]): m.MirroredElemTypes + = ??? diff --git a/scaladoc-testcases/src/tests/supertypeParamsSubstitution.scala b/scaladoc-testcases/src/tests/supertypeParamsSubstitution.scala new file mode 100644 index 000000000000..f80f92afafa2 --- /dev/null +++ b/scaladoc-testcases/src/tests/supertypeParamsSubstitution.scala @@ -0,0 +1,20 @@ +package tests +package supertypeParamsSubstitution + +class MyIter[A, CC[_], C]: + def foo: A + = ??? + def bar: CC[CC[A]] + = ??? + def baz: C + = ??? + +class MyList[T] extends MyIter[T, MyList, MyList[T]] +//expected: def foo: T +//expected: def bar: MyList[MyList[T]] +//expected: def baz: MyList[T] + +class MyListInt extends MyList[Int] +//expected: def foo: Int +//expected: def bar: MyList[MyList[Int]] +//expected: def baz: MyList[Int] diff --git a/scaladoc-testcases/src/tests/thisType.scala b/scaladoc-testcases/src/tests/thisType.scala new file mode 100644 index 000000000000..942e50af86ec --- /dev/null +++ b/scaladoc-testcases/src/tests/thisType.scala @@ -0,0 +1,8 @@ +package tests +package thisType + +// issue 16024 + +class X[Map[_, _[_]]]: + inline def map[F[_]](f: [t] => t => F[t]): Map[this.type, F] = //expected: inline def map[F[_]](f: [t] => (x$1: t) => F[t]): Map[this.type, F] + ??? diff --git a/scaladoc/src/dotty/tools/scaladoc/tasty/ClassLikeSupport.scala b/scaladoc/src/dotty/tools/scaladoc/tasty/ClassLikeSupport.scala index 2c7017f76636..bf04b72328b8 100644 --- a/scaladoc/src/dotty/tools/scaladoc/tasty/ClassLikeSupport.scala +++ b/scaladoc/src/dotty/tools/scaladoc/tasty/ClassLikeSupport.scala @@ -30,7 +30,7 @@ trait ClassLikeSupport: else Kind.Class(Nil, Nil) private def kindForClasslike(classDef: ClassDef): Kind = - def typeArgs = classDef.getTypeParams.map(mkTypeArgument(_)) + def typeArgs = classDef.getTypeParams.map(mkTypeArgument(_, classDef)) def parameterModifier(parameter: Symbol): String = val fieldSymbol = classDef.symbol.declaredField(parameter.normalizedName) @@ -47,9 +47,13 @@ trait ClassLikeSupport: Some(classDef.constructor.symbol) .filter(s => s.exists && !s.isHiddenByVisibility) .map( _.tree.asInstanceOf[DefDef]) - constr.fold(Nil)( - _.termParamss.map(pList => api.TermParameterList(pList.params.map(p => mkParameter(p, parameterModifier)), paramListModifier(pList.params))) + + constr.fold(Nil)(_.termParamss.map(pList => + api.TermParameterList( + pList.params.map(p => mkParameter(p, classDef, parameterModifier)), + paramListModifier(pList.params), ) + )) if classDef.symbol.flags.is(Flags.Module) then Kind.Object else if classDef.symbol.flags.is(Flags.Trait) then @@ -85,7 +89,7 @@ trait ClassLikeSupport: def getSupertypesGraph(link: LinkToType, to: Seq[Tree]): Seq[(LinkToType, LinkToType)] = to.flatMap { case tree => val symbol = if tree.symbol.isClassConstructor then tree.symbol.owner else tree.symbol - val signature = signatureWithName(tree.asSignature) + val signature = signatureWithName(tree.asSignature(classDef)) val superLink = LinkToType(signature, symbol.dri, bareClasslikeKind(symbol)) val nextTo = unpackTreeToClassDef(tree).parents if symbol.isHiddenByVisibility then getSupertypesGraph(link, nextTo) @@ -96,16 +100,16 @@ trait ClassLikeSupport: .filterNot((s, t) => s.isHiddenByVisibility) .map { case (symbol, tpe) => - val signature = signatureWithName(tpe.asSignature) + val signature = signatureWithName(tpe.asSignature(classDef)) LinkToType(signature, symbol.dri, bareClasslikeKind(symbol)) } val selfType = classDef.self.map { (valdef: ValDef) => val symbol = valdef.symbol val tpe = valdef.tpt.tpe - val signature = signatureWithName(tpe.asSignature) + val signature = signatureWithName(tpe.asSignature(classDef)) LinkToType(signature, symbol.dri, Kind.Type(false, false, Seq.empty)) } - val selfSignature: DSignature = signatureWithName(typeForClass(classDef).asSignature) + val selfSignature: DSignature = signatureWithName(typeForClass(classDef).asSignature(classDef)) val graph = HierarchyGraph.withEdges( getSupertypesGraph(LinkToType(selfSignature, classDef.symbol.dri, bareClasslikeKind(classDef.symbol)), unpackTreeToClassDef(classDef).parents) @@ -148,19 +152,19 @@ trait ClassLikeSupport: case dd: DefDef if isDocumentableExtension(dd.symbol) => dd.symbol.extendedSymbol.map { extSym => val memberInfo = unwrapMemberInfo(c, dd.symbol) - val typeParams = dd.symbol.extendedTypeParams.map(mkTypeArgument(_, memberInfo.genericTypes)) + val typeParams = dd.symbol.extendedTypeParams.map(mkTypeArgument(_, c, memberInfo.genericTypes)) val termParams = dd.symbol.extendedTermParamLists.zipWithIndex.flatMap { case (termParamList, index) => memberInfo.termParamLists(index) match case MemberInfo.EvidenceOnlyParameterList => None case MemberInfo.RegularParameterList(info) => - Some(api.TermParameterList(termParamList.params.map(mkParameter(_, memberInfo = info)), paramListModifier(termParamList.params))) + Some(api.TermParameterList(termParamList.params.map(mkParameter(_, c, memberInfo = info)), paramListModifier(termParamList.params))) case _ => assert(false, "memberInfo.termParamLists contains a type parameter list !") } val target = ExtensionTarget( extSym.symbol.normalizedName, typeParams, termParams, - extSym.tpt.asSignature, + extSym.tpt.asSignature(c), extSym.tpt.symbol.dri, extSym.symbol.pos.get.start ) @@ -190,7 +194,7 @@ trait ClassLikeSupport: Some(parseMethod(c, dd.symbol)) case td: TypeDef if !td.symbol.flags.is(Flags.Synthetic) && (!td.symbol.flags.is(Flags.Case) || !td.symbol.flags.is(Flags.Enum)) => - Some(parseTypeDef(td)) + Some(parseTypeDef(td, c)) case vd: ValDef if !isSyntheticField(vd.symbol) && (!vd.symbol.flags.is(Flags.Case) || !vd.symbol.flags.is(Flags.Enum)) => Some(parseValDef(c, vd)) @@ -268,7 +272,7 @@ trait ClassLikeSupport: def getParentsAsLinkToTypes: List[LinkToType] = c.getParentsAsTreeSymbolTuples.map { - (tree, symbol) => LinkToType(tree.asSignature, symbol.dri, bareClasslikeKind(symbol)) + (tree, symbol) => LinkToType(tree.asSignature(c), symbol.dri, bareClasslikeKind(symbol)) } def getParentsAsTreeSymbolTuples: List[(Tree, Symbol)] = @@ -324,7 +328,7 @@ trait ClassLikeSupport: val enumTypes = companion.membersToDocument.collect { case td: TypeDef if !td.symbol.flags.is(Flags.Synthetic) && td.symbol.flags.is(Flags.Enum) && td.symbol.flags.is(Flags.Case) => td - }.toList.map(parseTypeDef) + }.toList.map(parseTypeDef(_, classDef)) val enumNested = companion.membersToDocument.collect { case c: ClassDef if c.symbol.flags.is(Flags.Case) && c.symbol.flags.is(Flags.Enum) => processTree(c)(parseClasslike(c)) @@ -351,7 +355,7 @@ trait ClassLikeSupport: val memberInfo = unwrapMemberInfo(c, methodSymbol) - val unshuffledMemberInfoParamLists = + val unshuffledMemberInfoParamLists = if methodSymbol.isExtensionMethod && methodSymbol.isRightAssoc then // Taken from RefinedPrinter.scala // If you change the names of the clauses below, also change them in right-associative-extension-methods.md @@ -367,7 +371,7 @@ trait ClassLikeSupport: rightTyParams ::: rightParam ::: rest6 else memberInfo.paramLists // it wasn't a binary operator, after all. - else + else memberInfo.paramLists val croppedUnshuffledMemberInfoParamLists = unshuffledMemberInfoParamLists.takeRight(paramLists.length) @@ -377,10 +381,10 @@ trait ClassLikeSupport: case (_: TermParamClause, MemberInfo.EvidenceOnlyParameterList) => Nil case (pList: TermParamClause, MemberInfo.RegularParameterList(info)) => Some(Left(api.TermParameterList(pList.params.map( - mkParameter(_, paramPrefix, memberInfo = info)), paramListModifier(pList.params) + mkParameter(_, c, paramPrefix, memberInfo = info)), paramListModifier(pList.params) ))) case (TypeParamClause(genericTypeList), MemberInfo.TypeParameterList(memInfoTypes)) => - Some(Right(genericTypeList.map(mkTypeArgument(_, memInfoTypes, memberInfo.contextBounds)))) + Some(Right(genericTypeList.map(mkTypeArgument(_, c, memInfoTypes, memberInfo.contextBounds)))) case (_,_) => assert(false, s"croppedUnshuffledMemberInfoParamLists and SymOps.nonExtensionParamLists disagree on whether this clause is a type or term one") } @@ -388,7 +392,7 @@ trait ClassLikeSupport: val methodKind = if methodSymbol.isClassConstructor then Kind.Constructor(basicDefKind) - else if methodSymbol.flags.is(Flags.Implicit) then + else if methodSymbol.flags.is(Flags.Implicit) then val termParamLists: List[TermParamClause] = methodSymbol.nonExtensionTermParamLists extractImplicitConversion(method.returnTpt.tpe) match case Some(conversion) if termParamLists.size == 0 || (termParamLists.size == 1 && termParamLists.head.params.size == 0) => @@ -402,7 +406,7 @@ trait ClassLikeSupport: )) case _ => Kind.Implicit(basicDefKind, None) - else if methodSymbol.flags.is(Flags.Given) then Kind.Given(basicDefKind, Some(method.returnTpt.tpe.asSignature), extractImplicitConversion(method.returnTpt.tpe)) + else if methodSymbol.flags.is(Flags.Given) then Kind.Given(basicDefKind, Some(method.returnTpt.tpe.asSignature(c)), extractImplicitConversion(method.returnTpt.tpe)) else specificKind(basicDefKind) val origin = if !methodSymbol.isOverridden then Origin.RegularlyDefined else @@ -418,7 +422,7 @@ trait ClassLikeSupport: mkMember( methodSymbol, methodKind, - method.returnTpt.tpe.asSignature + method.returnTpt.tpe.asSignature(c), )( modifiers = modifiers, origin = origin, @@ -428,28 +432,31 @@ trait ClassLikeSupport: def mkParameter( argument: ValDef, + classDef: ClassDef, prefix: Symbol => String = _ => "", isExtendedSymbol: Boolean = false, isGrouped: Boolean = false, - memberInfo: Map[String, TypeRepr] = Map.empty) = - val inlinePrefix = if argument.symbol.flags.is(Flags.Inline) then "inline " else "" - val nameIfNotSynthetic = Option.when(!argument.symbol.flags.is(Flags.Synthetic))(argument.symbol.normalizedName) - val name = argument.symbol.normalizedName - api.TermParameter( - argument.symbol.getAnnotations(), - inlinePrefix + prefix(argument.symbol), - nameIfNotSynthetic, - argument.symbol.dri, - memberInfo.get(name).fold(argument.tpt.asSignature)(_.asSignature), - isExtendedSymbol, - isGrouped - ) + memberInfo: Map[String, TypeRepr] = Map.empty, + ) = + val inlinePrefix = if argument.symbol.flags.is(Flags.Inline) then "inline " else "" + val nameIfNotSynthetic = Option.when(!argument.symbol.flags.is(Flags.Synthetic))(argument.symbol.normalizedName) + val name = argument.symbol.normalizedName + api.TermParameter( + argument.symbol.getAnnotations(), + inlinePrefix + prefix(argument.symbol), + nameIfNotSynthetic, + argument.symbol.dri, + memberInfo.get(name).fold(argument.tpt.asSignature(classDef))(_.asSignature(classDef)), + isExtendedSymbol, + isGrouped + ) def mkTypeArgument( argument: TypeDef, + classDef: ClassDef, memberInfo: Map[String, TypeBounds] = Map.empty, - contextBounds: Map[String, DSignature] = Map.empty - ): TypeParameter = + contextBounds: Map[String, DSignature] = Map.empty, + ): TypeParameter = val variancePrefix: "+" | "-" | "" = if argument.symbol.flags.is(Flags.Covariant) then "+" else if argument.symbol.flags.is(Flags.Contravariant) then "-" @@ -457,7 +464,7 @@ trait ClassLikeSupport: val name = argument.symbol.normalizedName val normalizedName = if name.matches("_\\$\\d*") then "_" else name - val boundsSignature = memberInfo.get(name).fold(argument.rhs.asSignature)(_.asSignature) + val boundsSignature = memberInfo.get(name).fold(argument.rhs.asSignature(classDef))(_.asSignature(classDef)) val signature = contextBounds.get(name) match case None => boundsSignature case Some(contextBoundsSignature) => @@ -471,14 +478,14 @@ trait ClassLikeSupport: signature ) - def parseTypeDef(typeDef: TypeDef): Member = + def parseTypeDef(typeDef: TypeDef, classDef: ClassDef): Member = def isTreeAbstract(typ: Tree): Boolean = typ match { case TypeBoundsTree(_, _) => true case LambdaTypeTree(params, body) => isTreeAbstract(body) case _ => false } val (generics, tpeTree) = typeDef.rhs match - case LambdaTypeTree(params, body) => (params.map(mkTypeArgument(_)), body) + case LambdaTypeTree(params, body) => (params.map(mkTypeArgument(_, classDef)), body) case tpe => (Nil, tpe) val defaultKind = Kind.Type(!isTreeAbstract(typeDef.rhs), typeDef.symbol.isOpaque, generics).asInstanceOf[Kind.Type] @@ -492,19 +499,19 @@ trait ClassLikeSupport: Some(Link(l.tpe.typeSymbol.owner.name, l.tpe.typeSymbol.owner.dri)) case _ => None } - mkMember(typeDef.symbol, Kind.Exported(kind), tpeTree.asSignature)( + mkMember(typeDef.symbol, Kind.Exported(kind), tpeTree.asSignature(classDef))( deprecated = typeDef.symbol.isDeprecated(), origin = Origin.ExportedFrom(origin), experimental = typeDef.symbol.isExperimental() ) } - else mkMember(typeDef.symbol, kind, tpeTree.asSignature)(deprecated = typeDef.symbol.isDeprecated()) + else mkMember(typeDef.symbol, kind, tpeTree.asSignature(classDef))(deprecated = typeDef.symbol.isDeprecated()) def parseValDef(c: ClassDef, valDef: ValDef): Member = def defaultKind = if valDef.symbol.flags.is(Flags.Mutable) then Kind.Var else Kind.Val val memberInfo = unwrapMemberInfo(c, valDef.symbol) val kind = if valDef.symbol.flags.is(Flags.Implicit) then Kind.Implicit(Kind.Val, extractImplicitConversion(valDef.tpt.tpe)) - else if valDef.symbol.flags.is(Flags.Given) then Kind.Given(Kind.Val, Some(memberInfo.res.asSignature), extractImplicitConversion(valDef.tpt.tpe)) + else if valDef.symbol.flags.is(Flags.Given) then Kind.Given(Kind.Val, Some(memberInfo.res.asSignature(c)), extractImplicitConversion(valDef.tpt.tpe)) else if valDef.symbol.flags.is(Flags.Enum) then Kind.EnumCase(Kind.Val) else defaultKind @@ -514,7 +521,7 @@ trait ClassLikeSupport: .filterNot(m => m == Modifier.Lazy || m == Modifier.Final) case _ => valDef.symbol.getExtraModifiers() - mkMember(valDef.symbol, kind, memberInfo.res.asSignature)( + mkMember(valDef.symbol, kind, memberInfo.res.asSignature(c))( modifiers = modifiers, deprecated = valDef.symbol.isDeprecated(), experimental = valDef.symbol.isExperimental() @@ -552,7 +559,7 @@ trait ClassLikeSupport: contextBounds: Map[String, DSignature] = Map.empty, ){ val genericTypes: Map[String, TypeBounds] = paramLists.collect{ case MemberInfo.TypeParameterList(types) => types }.headOption.getOrElse(Map()) - + val termParamLists: List[MemberInfo.ParameterList] = paramLists.filter(_.isTerm) } @@ -562,7 +569,7 @@ trait ClassLikeSupport: case EvidenceOnlyParameterList extends ParameterList(isTerm = true, isUsing = false) case RegularParameterList(m: Map[String, TypeRepr])(isUsing: Boolean) extends ParameterList(isTerm = true, isUsing) case TypeParameterList(m: Map[String, TypeBounds]) extends ParameterList(isTerm = false, isUsing = false) - + export ParameterList.{RegularParameterList, EvidenceOnlyParameterList, TypeParameterList} @@ -604,13 +611,18 @@ trait ClassLikeSupport: val (paramsThatLookLikeContextBounds, contextBounds) = evidences.partitionMap { case (_, AppliedType(tpe, List(typeParam: ParamRef))) => - Right(nameForRef(typeParam) -> tpe.asSignature) + Right(nameForRef(typeParam) -> tpe.asSignature(c)) case (name, original) => findParamRefs(original) match case Nil => Left((name, original)) case typeParam :: _ => val name = nameForRef(typeParam) - val signature = Seq(Plain("(["), dotty.tools.scaladoc.Type(name, None), Plain("]"), Keyword(" =>> ")) ++ original.asSignature ++ Seq(Plain(")")) + val signature = Seq( + Plain("(["), + dotty.tools.scaladoc.Type(name, None), + Plain("]"), + Keyword(" =>> "), + ) ++ original.asSignature(c) ++ Seq(Plain(")")) Right(name -> signature.toList) } @@ -619,7 +631,7 @@ trait ClassLikeSupport: val termParamList = if newParams.isEmpty && contextBounds.nonEmpty then MemberInfo.EvidenceOnlyParameterList else MemberInfo.RegularParameterList(newParams)(isUsing) - + MemberInfo(memberInfo.paramLists :+ termParamList, methodType.resType, contextBounds.toMap) diff --git a/scaladoc/src/dotty/tools/scaladoc/tasty/TastyParser.scala b/scaladoc/src/dotty/tools/scaladoc/tasty/TastyParser.scala index cd1bed42f485..3e75cc32a2d0 100644 --- a/scaladoc/src/dotty/tools/scaladoc/tasty/TastyParser.scala +++ b/scaladoc/src/dotty/tools/scaladoc/tasty/TastyParser.scala @@ -126,7 +126,10 @@ case class ScaladocTastyInspector()(using ctx: DocContext) extends DocTastyInspe if ctx.args.documentSyntheticTypes then import parser.qctx.reflect._ val intrinsicTypeDefs = parser.intrinsicTypeDefs.toSeq.map { s => - "scala" -> parser.parseTypeDef(s.tree.asInstanceOf[TypeDef]) + "scala" -> parser.parseTypeDef( + s.tree.asInstanceOf[TypeDef], + defn.AnyClass.tree.asInstanceOf[ClassDef], + ) } val intrinsicClassDefs = parser.intrinsicClassDefs.toSeq.map { s => "scala" -> parser.parseClasslike(s.tree.asInstanceOf[ClassDef]) @@ -160,7 +163,10 @@ case class ScaladocTastyInspector()(using ctx: DocContext) extends DocTastyInspe import parser.qctx.reflect._ val javaLangObjectDef = defn.ObjectClass.tree.asInstanceOf[ClassDef] val objectMembers = parser.extractPatchedMembers(javaLangObjectDef) - val aM = parser.parseTypeDef(defn.AnyRefClass.tree.asInstanceOf[TypeDef]) + val aM = parser.parseTypeDef( + defn.AnyRefClass.tree.asInstanceOf[TypeDef], + defn.AnyClass.tree.asInstanceOf[ClassDef], + ) "scala" -> aM.copy( kind = Kind.Class(Nil, Nil), members = objectMembers diff --git a/scaladoc/src/dotty/tools/scaladoc/tasty/TypesSupport.scala b/scaladoc/src/dotty/tools/scaladoc/tasty/TypesSupport.scala index c94eda9409b2..35cf1cb6eec3 100644 --- a/scaladoc/src/dotty/tools/scaladoc/tasty/TypesSupport.scala +++ b/scaladoc/src/dotty/tools/scaladoc/tasty/TypesSupport.scala @@ -13,41 +13,19 @@ trait TypesSupport: type SSignature = List[SignaturePart] - def getGivenInstance(method: qctx.reflect.DefDef): Option[SSignature] = - import qctx.reflect._ - given qctx.type = qctx - - def extractTypeSymbol(t: Tree): Option[Symbol] = t match - case tpeTree: TypeTree => - inner(tpeTree.tpe) - case other => None - - def inner(tpe: TypeRepr): Option[Symbol] = tpe match - case ThisType(tpe) => inner(tpe) - case AnnotatedType(tpe, _) => inner(tpe) - case AppliedType(tpe, _) => inner(tpe) - case tp @ TermRef(qual, typeName) => Some(tp.termSymbol) - case tp @ TypeRef(qual, typeName) => Some(tp.typeSymbol) - - val typeSymbol = extractTypeSymbol(method.returnTpt) - - typeSymbol.map(_.tree).collect { - case c: ClassDef => c.getTreeOfFirstParent - case _ => Some(method.returnTpt) - }.flatten.map(_.asSignature) - given TreeSyntax: AnyRef with extension (using Quotes)(tpeTree: reflect.Tree) - def asSignature: SSignature = + def asSignature(elideThis: reflect.ClassDef): SSignature = import reflect._ tpeTree match - case TypeBoundsTree(low, high) => typeBoundsTreeOfHigherKindedType(low.tpe, high.tpe) - case tpeTree: TypeTree => topLevelProcess(tpeTree.tpe) - case term: Term => topLevelProcess(term.tpe) + case TypeBoundsTree(low, high) => typeBoundsTreeOfHigherKindedType(low.tpe, high.tpe)(using elideThis) + case tpeTree: TypeTree => topLevelProcess(tpeTree.tpe)(using elideThis) + case term: Term => topLevelProcess(term.tpe)(using elideThis) given TypeSyntax: AnyRef with extension (using Quotes)(tpe: reflect.TypeRepr) - def asSignature: SSignature = topLevelProcess(tpe) + def asSignature(elideThis: reflect.ClassDef): SSignature = + topLevelProcess(tpe)(using elideThis) private def plain(str: String): SignaturePart = Plain(str) @@ -58,13 +36,15 @@ trait TypesSupport: private def tpe(str: String): SignaturePart = dotty.tools.scaladoc.Type(str, None) + private def inParens(s: SSignature, wrap: Boolean = true) = + if wrap then plain("(").l ++ s ++ plain(")").l else s + extension (on: SignaturePart) def l: List[SignaturePart] = List(on) private def tpe(using Quotes)(symbol: reflect.Symbol): SSignature = import SymOps._ - val suffix = if symbol.isValDef || symbol.flags.is(reflect.Flags.Module) then plain(".type").l else Nil val dri: Option[DRI] = Option(symbol).filterNot(_.isHiddenByVisibility).map(_.dri) - dotty.tools.scaladoc.Type(symbol.normalizedName, dri) :: suffix + dotty.tools.scaladoc.Type(symbol.normalizedName, dri).l private def commas(lists: List[SSignature]) = lists match case List(single) => single @@ -86,25 +66,47 @@ trait TypesSupport: case _ => false case _ => false - private def topLevelProcess(using Quotes)(tp: reflect.TypeRepr): SSignature = + private def topLevelProcess(using Quotes)(tp: reflect.TypeRepr)(using elideThis: reflect.ClassDef): SSignature = import reflect._ tp match - case ThisType(tpe) => inner(tpe) :+ plain(".this.type") + case ThisType(tpe) => + val suffix = List(keyword("this"), plain("."), keyword("type")) + if skipPrefix(tp, elideThis) then suffix + else inner(tpe) ++ plain(".").l ++ suffix case tpe => inner(tpe) // TODO #23 add support for all types signatures that makes sense - private def inner(using Quotes)(tp: reflect.TypeRepr)(using indent: Int = 0): SSignature = + private def inner( + using Quotes, + )( + tp: reflect.TypeRepr, + )(using + elideThis: reflect.ClassDef, + indent: Int = 0, + skipTypeSuffix: Boolean = false, + ): SSignature = import reflect._ def noSupported(name: String): SSignature = println(s"WARN: Unsupported type: $name: ${tp.show}") plain(s"Unsupported[$name]").l tp match - case OrType(left, right) => inner(left) ++ keyword(" | ").l ++ inner(right) - case AndType(left, right) => inner(left) ++ keyword(" & ").l ++ inner(right) + case OrType(left, right) => + inParens(inner(left), shouldWrapInParens(left, tp, true)) + ++ keyword(" | ").l + ++ inParens(inner(right), shouldWrapInParens(right, tp, false)) + case AndType(left, right) => + inParens(inner(left), shouldWrapInParens(left, tp, true)) + ++ keyword(" & ").l + ++ inParens(inner(right), shouldWrapInParens(right, tp, false)) case ByNameType(tpe) => keyword("=> ") :: inner(tpe) case ConstantType(constant) => plain(constant.show).l - case ThisType(tpe) => inner(tpe) + case ThisType(tpe) => + val prefix = findSupertype(elideThis, tpe.typeSymbol) match + case Some(_) => Nil + case None => inner(tpe) ++ plain(".").l + val suffix = if skipTypeSuffix then Nil else List(plain("."), keyword("type")) + prefix ++ keyword("this").l ++ suffix case AnnotatedType(AppliedType(_, Seq(tpe)), annotation) if isRepeatedAnnotation(annotation) => inner(tpe) :+ plain("*") case AppliedType(repeatedClass, Seq(tpe)) if isRepeated(repeatedClass) => @@ -191,12 +193,18 @@ trait TypesSupport: } case t @ AppliedType(tpe, typeList) => import dotty.tools.dotc.util.Chars._ - if !t.typeSymbol.name.forall(isIdentifierPart) && typeList.size == 2 then - inner(typeList.head) + if defn.isTupleClass(tpe.typeSymbol) && typeList.length != 1 then + typeList match + case Nil => Nil + case args => inParens(commas(args.map(inner(_)))) + else if isInfix(t) then + val lhs = typeList.head + val rhs = typeList.last + inParens(inner(lhs), shouldWrapInParens(lhs, t, true)) ++ plain(" ").l ++ inner(tpe) ++ plain(" ").l - ++ inner(typeList.last) + ++ inParens(inner(rhs), shouldWrapInParens(rhs, t, false)) else if t.isFunctionType then val arrow = if t.isContextFunctionType then " ?=> " else " => " typeList match @@ -205,74 +213,56 @@ trait TypesSupport: case Seq(rtpe) => plain("()").l ++ keyword(arrow).l ++ inner(rtpe) case Seq(arg, rtpe) => - def withParentheses(tpe: TypeRepr) = plain("(").l ++ inner(tpe) ++ plain(")").l val partOfSignature = arg match - case tpe @ (_:TermRef | _:TypeRef | _:ConstantType | _: ParamRef) => inner(arg) - case tpe: AppliedType if !tpe.isFunctionType && !tpe.isTupleN => inner(arg) - case _ => withParentheses(arg) + case _: TermRef | _: TypeRef | _: ConstantType | _: ParamRef => inner(arg) + case _ => inParens(inner(arg)) partOfSignature ++ keyword(arrow).l ++ inner(rtpe) case args => - plain("(").l ++ commas(args.init.map(inner)) ++ plain(")").l ++ keyword(arrow).l ++ inner(args.last) - else if t.isTupleN then - typeList match - case Nil => - Nil - case args => - plain("(").l ++ commas(args.map(inner)) ++ plain(")").l + plain("(").l ++ commas(args.init.map(inner(_))) ++ plain(")").l ++ keyword(arrow).l ++ inner(args.last) else inner(tpe) ++ plain("[").l ++ commas(typeList.map { t => t match case _: TypeBounds => keyword("_").l ++ inner(t) - case _ => inner(t) + case _ => topLevelProcess(t) }) ++ plain("]").l case tp @ TypeRef(qual, typeName) => qual match { case r: RecursiveThis => tpe(s"this.$typeName").l - case _: TypeRepr => tpe(tp.typeSymbol) + case t if skipPrefix(t, elideThis) => + tpe(tp.typeSymbol) + case _: TermRef | _: ParamRef => + val suffix = if tp.typeSymbol == Symbol.noSymbol then tpe(typeName).l else tpe(tp.typeSymbol) + inner(qual)(using skipTypeSuffix = true) ++ plain(".").l ++ suffix + case ThisType(tr) => + findSupertype(elideThis, tr.typeSymbol) match + case Some((sym, AppliedType(tr2, args))) => + sym.tree.asInstanceOf[ClassDef].constructor.paramss.headOption match + case Some(TypeParamClause(tpc)) => + tpc.zip(args).collectFirst { + case (TypeDef(name, _), arg) if name == typeName => arg + } match + case Some(tr) => inner(tr) + case None => tpe(tp.typeSymbol) + case _ => tpe(tp.typeSymbol) + case Some(_) => tpe(tp.typeSymbol) + case None => + val sig = inParens(inner(qual)(using skipTypeSuffix = true), shouldWrapInParens(qual, tp, true)) + sig ++ plain(".").l ++ tpe(tp.typeSymbol) + case _ => + val sig = inParens(inner(qual), shouldWrapInParens(qual, tp, true)) + sig ++ keyword("#").l ++ tpe(tp.typeSymbol) } - // convertTypeOrBoundsToReference(reflect)(qual) match { - // case TypeReference(label, link, xs, _) => TypeReference(typeName, link + "/" + label, xs, true) - // case EmptyReference => TypeReference(typeName, "", Nil, true) - // case _ if tp.typeSymbol.exists => - // tp.typeSymbol match { - // // NOTE: Only TypeRefs can reference ClassDefSymbols - // case sym if sym.isClassDef => //Need to be split because these types have their own file - // convertTypeOrBoundsToReference(reflect)(qual) match { - // case TypeReference(label, link, xs, _) => TypeReference(sym.name, link + "/" + label, xs, true) - // case EmptyReference if sym.name == "" | sym.name == "_root_" => EmptyReference - // case EmptyReference => TypeReference(sym.name, "", Nil, true) - // case _ => throw Exception("Match error in SymRef/TypeOrBounds/ClassDef. This should not happen, please open an issue. " + convertTypeOrBoundsToReference(reflect)(qual)) - // } - - // // NOTE: This branch handles packages, which are now TypeRefs - // case sym if sym.isTerm || sym.isTypeDef => - // convertTypeOrBoundsToReference(reflect)(qual) match { - // case TypeReference(label, link, xs, _) => TypeReference(sym.name, link + "/" + label, xs) - // case EmptyReference if sym.name == "" | sym.name == "_root_" => EmptyReference - // case EmptyReference => TypeReference(sym.name, "", Nil) - // case _ => throw Exception("Match error in SymRef/TypeOrBounds/Other. This should not happen, please open an issue. " + convertTypeOrBoundsToReference(reflect)(qual)) - // } - // case sym => throw Exception("Match error in SymRef. This should not happen, please open an issue. " + sym) - // } - // case _ => - // throw Exception("Match error in TypeRef. This should not happen, please open an issue. " + convertTypeOrBoundsToReference(reflect)(qual)) - // } + case tr @ TermRef(qual, typeName) => - tr.termSymbol.tree match - case vd: ValDef => inner(vd.tpt.tpe) - case _ => tpe(tr.termSymbol) - - - // convertTypeOrBoundsToReference(reflect)(qual) match { - // case TypeReference(label, link, xs, _) => TypeReference(typeName + "$", link + "/" + label, xs) - // case EmptyReference => TypeReference(typeName, "", Nil) - // case _ => throw Exception("Match error in TermRef. This should not happen, please open an issue. " + convertTypeOrBoundsToReference(reflect)(qual)) - // } - - // NOTE: old SymRefs are now either TypeRefs or TermRefs - the logic here needs to be moved into above branches - // NOTE: _.symbol on *Ref returns its symbol - // case SymRef(symbol, typeOrBounds) => symbol match { - // } - // case _ => throw Exception("No match for type in conversion to Reference. This should not happen, please open an issue. " + tp) + val prefix = qual match + case t if skipPrefix(t, elideThis) => Nil + case tp => inner(tp)(using skipTypeSuffix = true) ++ plain(".").l + val suffix = if skipTypeSuffix then Nil else List(plain("."), keyword("type")) + val typeSig = tr.termSymbol.tree match + case vd: ValDef if tr.termSymbol.flags.is(Flags.Module) => + inner(vd.tpt.tpe) + case _ => plain(typeName).l + prefix ++ typeSig ++ suffix + case TypeBounds(low, hi) => if(low == hi) keyword(" = ").l ++ inner(low) else typeBoundsTreeOfHigherKindedType(low, hi) @@ -290,7 +280,9 @@ trait TypesSupport: } inner(sc) ++ keyword(" match ").l ++ plain("{\n").l ++ casesTexts ++ plain(spaces + "}").l - case ParamRef(m: MethodType, i) => tpe(m.paramNames(i)).l ++ plain(".type").l + case ParamRef(m: MethodType, i) => + val suffix = if skipTypeSuffix then Nil else List(plain("."), keyword("type")) + tpe(m.paramNames(i)).l ++ suffix case ParamRef(binder: LambdaType, i) => tpe(binder.paramNames(i)).l @@ -310,29 +302,86 @@ trait TypesSupport: s"${tpe.show(using Printer.TypeReprStructure)}" throw MatchError(msg) - private def typeBound(using Quotes)(t: reflect.TypeRepr, low: Boolean) = + private def typeBound(using Quotes)(t: reflect.TypeRepr, low: Boolean)(using elideThis: reflect.ClassDef) = import reflect._ val ignore = if (low) t.typeSymbol == defn.NothingClass else t.typeSymbol == defn.AnyClass val prefix = keyword(if low then " >: " else " <: ") t match { - case l: TypeLambda => prefix :: plain("(").l ++ inner(l) ++ plain(")").l - case p: ParamRef => prefix :: inner(p) - case other if !ignore => prefix :: inner(other) + case l: TypeLambda => prefix :: inParens(inner(l)(using elideThis)) + case p: ParamRef => prefix :: inner(p)(using elideThis) + case other if !ignore => prefix :: topLevelProcess(other)(using elideThis) case _ => Nil } - private def typeBoundsTreeOfHigherKindedType(using Quotes)(low: reflect.TypeRepr, high: reflect.TypeRepr) = + private def typeBoundsTreeOfHigherKindedType(using Quotes)(low: reflect.TypeRepr, high: reflect.TypeRepr)(using elideThis: reflect.ClassDef) = import reflect._ def regularTypeBounds(low: TypeRepr, high: TypeRepr) = - if low == high then keyword(" = ").l ++ inner(low) - else typeBound(low, low = true) ++ typeBound(high, low = false) + if low == high then keyword(" = ").l ++ inner(low)(using elideThis) + else typeBound(low, low = true)(using elideThis) ++ typeBound(high, low = false)(using elideThis) high.match case TypeLambda(params, paramBounds, resType) => if resType.typeSymbol == defn.AnyClass then plain("[").l ++ commas(params.zip(paramBounds).map { (name, typ) => val normalizedName = if name.matches("_\\$\\d*") then "_" else name - tpe(normalizedName).l ++ inner(typ) + tpe(normalizedName).l ++ inner(typ)(using elideThis) }) ++ plain("]").l else regularTypeBounds(low, high) case _ => regularTypeBounds(low, high) + + private def findSupertype(using Quotes)(c: reflect.ClassDef, sym: reflect.Symbol) = + getSupertypes(c).find((s, t) => s == sym) + + private def skipPrefix(using Quotes)(tr: reflect.TypeRepr, elideThis: reflect.ClassDef) = + import reflect._ + + def collectOwners(owners: Set[Symbol], sym: Symbol): Set[Symbol] = + if sym.flags.is(Flags.Package) then owners + else collectOwners(owners + sym, sym.owner) + val owners = collectOwners(Set.empty, elideThis.symbol) + + tr match + case NoPrefix() => true + case ThisType(tp) if owners(tp.typeSymbol) => true + case tp if owners(tp.typeSymbol) => true + case _ => + val flags = tr.typeSymbol.flags + flags.is(Flags.Module) || flags.is(Flags.Package) + + private def shouldWrapInParens(using Quotes)(inner: reflect.TypeRepr, outer: reflect.TypeRepr, isLeft: Boolean) = + import reflect._ + + (inner, outer) match + case (_: AndType, _: TypeRef) => true + case (_: OrType, _: TypeRef) => true + case (t: AppliedType, _: TypeRef) => isInfix(t) + + case (_: AndType, _: AndType) => false + case (_: AndType, _: OrType) => false + case (_: OrType, _: AndType) => true + case (_: OrType, _: OrType) => false + + case (at: AppliedType, _: AndType) => at.isFunctionType || isInfix(at) + case (at: AppliedType, _: OrType) => at.isFunctionType || isInfix(at) + case (_: AndType, at: AppliedType) => isInfix(at) + case (_: OrType, at: AppliedType) => isInfix(at) + case (at1: AppliedType, at2: AppliedType) => + val leftAssoc = !at1.tycon.typeSymbol.name.endsWith(":") + isInfix(at2) && (at1.isFunctionType || isInfix(at1) && ( + at1.tycon.typeSymbol != at2.tycon.typeSymbol || leftAssoc != isLeft + )) + case _ => false + + private def isInfix(using Quotes)(at: reflect.AppliedType) = + import dotty.tools.dotc.util.Chars.isIdentifierPart + import reflect._ + + def infixAnnot = + at.tycon.typeSymbol.getAnnotation(Symbol.requiredClass("scala.annotation.showAsInfix")) match + case Some(Apply(_, args)) => + args.collectFirst { + case Literal(BooleanConstant(false)) => false + }.getOrElse(true) + case _ => false + + at.args.size == 2 && (!at.typeSymbol.name.forall(isIdentifierPart) || infixAnnot) diff --git a/scaladoc/test/dotty/tools/scaladoc/signatures/SignatureTest.scala b/scaladoc/test/dotty/tools/scaladoc/signatures/SignatureTest.scala index d5b7a0b9b6f8..bdedc3f14ce0 100644 --- a/scaladoc/test/dotty/tools/scaladoc/signatures/SignatureTest.scala +++ b/scaladoc/test/dotty/tools/scaladoc/signatures/SignatureTest.scala @@ -71,7 +71,7 @@ abstract class SignatureTest( // e.g. to remove '(0)' from object IAmACaseObject extends CaseImplementThis/*<-*/(0)/*->*/ private val commentRegex = raw"\/\*<-\*\/[^\/]+\/\*->\*\/".r private val whitespaceRegex = raw"\s+".r - private val expectedRegex = raw".+//expected: (.+)".r + private val expectedRegex = raw".*//expected: (.+)".r private val unexpectedRegex = raw"(.+)//unexpected".r private val identifierRegex = raw"^\s*(`.*`|(?:\w+)(?:_[^\[\(\s]+)|\w+|[^\[\(\s]+)".r @@ -94,7 +94,7 @@ abstract class SignatureTest( private def signaturesFromSources(source: Source, kinds: Seq[String]): Seq[SignatureRes] = source.getLines.map(_.trim) .filterNot(_.isEmpty) - .filterNot(_.startWithAnyOfThese("=",":","{","}", "//")) + .filterNot(l => l.startWithAnyOfThese("=",":","{","}", "//") && !l.startsWith("//expected:")) .toSeq .flatMap { case unexpectedRegex(signature) => findName(signature, kinds).map(Unexpected(_)) diff --git a/scaladoc/test/dotty/tools/scaladoc/signatures/TranslatableSignaturesTestCases.scala b/scaladoc/test/dotty/tools/scaladoc/signatures/TranslatableSignaturesTestCases.scala index a09234be5512..63d6c897ad38 100644 --- a/scaladoc/test/dotty/tools/scaladoc/signatures/TranslatableSignaturesTestCases.scala +++ b/scaladoc/test/dotty/tools/scaladoc/signatures/TranslatableSignaturesTestCases.scala @@ -109,4 +109,14 @@ class ImplicitMembers extends SignatureTest( class NonScala3Parent extends SignatureTest("nonScala3Parent", SignatureTest.all) +class SupertypeParamsSubstitution extends SignatureTest("supertypeParamsSubstitution", SignatureTest.all) + +class ThisType extends SignatureTest("thisType", SignatureTest.all) + +class PathDependentTypes extends SignatureTest("pathDependentTypes", SignatureTest.all) + +class MatchTypeTuple extends SignatureTest("matchTypeTuple", SignatureTest.all) + +class InfixTypes extends SignatureTest("infixTypes", SignatureTest.all) + class ExtendsCall extends SignatureTest("extendsCall", SignatureTest.all)