diff --git a/scaladoc-testcases/src/tests/exports.scala b/scaladoc-testcases/src/tests/exports.scala deleted file mode 100644 index 4e1267680575..000000000000 --- a/scaladoc-testcases/src/tests/exports.scala +++ /dev/null @@ -1,23 +0,0 @@ -package tests -package exports - -class A: - def aDefInt: Int = 1 - def aDef1: 1 = 1 - val aValInt: Int = 1 - val aVal1: 1 = 1 - var aVarInt: Int = 1 - var aVar1: 1 = 1 - -object X: - def xDefInt: Int = 1 - def xDef1: 1 = 1 - val xValInt: Int = 1 - val xVal1: 1 = 1 - var xVarInt: Int = 1 - var xVar1: 1 = 1 - -class B: - val a = new A - export a._ - export X._ diff --git a/scaladoc-testcases/src/tests/exports1.scala b/scaladoc-testcases/src/tests/exports1.scala new file mode 100644 index 000000000000..4e96e4ab8922 --- /dev/null +++ b/scaladoc-testcases/src/tests/exports1.scala @@ -0,0 +1,44 @@ +package tests +package exports1 + +class A: //unexpected + def aDefInt: Int + = 1 + def aDef1: 1 + = 1 + val aValInt: Int + = 1 + val aVal1: 1 + = 1 + var aVarInt: Int + = 1 + var aVar1: 1 + = 1 + type HKT[T[_], X] //expected: final type HKT = [T[_], X] =>> HKT[T, X] + = T[X] + def x[T[_], X](x: X): HKT[T, X] + = ??? + def fn[T, U]: T => U + = ??? + object Object //expected: val Obj: Object.type + val x: 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 + case A + case B(i: Int) + case C[T]() extends Enum + +object X: //unexpected + def xDefInt: Int + = 1 + def xDef1: 1 + = 1 + val xValInt: Int + = 1 + val xVal1: 1 + = 1 + var xVarInt: Int + = 1 + var xVar1: 1 + = 1 \ No newline at end of file diff --git a/scaladoc-testcases/src/tests/exports2.scala b/scaladoc-testcases/src/tests/exports2.scala new file mode 100644 index 000000000000..c6450265ab1b --- /dev/null +++ b/scaladoc-testcases/src/tests/exports2.scala @@ -0,0 +1,12 @@ +package tests +package exports2 + +import exports1._ + +class B: + val a: A + = new A + export a.{Object => Obj, _} + export X._ + def obj: Obj.type + = Obj diff --git a/scaladoc/src/dotty/tools/scaladoc/api.scala b/scaladoc/src/dotty/tools/scaladoc/api.scala index f7285a9da6ec..314dcc3c7261 100644 --- a/scaladoc/src/dotty/tools/scaladoc/api.scala +++ b/scaladoc/src/dotty/tools/scaladoc/api.scala @@ -64,7 +64,7 @@ enum Kind(val name: String): case Constructor(base: Kind.Def) extends Kind("def") case Var extends Kind("var") case Val extends Kind("val") - case Exported(m: Kind.Def) extends Kind("export") + case Exported(base: Kind) extends Kind("export") case Type(concreate: Boolean, opaque: Boolean, typeParams: Seq[TypeParameter]) extends Kind("type") // should we handle opaque as modifier? case Given(kind: Def | Class | Val.type, as: Option[Signature], conversion: Option[ImplicitConversion]) @@ -76,7 +76,7 @@ enum Kind(val name: String): enum Origin: case ImplicitlyAddedBy(name: String, dri: DRI) case ExtensionFrom(name: String, dri: DRI) - case ExportedFrom(name: String, dri: Option[DRI]) + case ExportedFrom(link: Option[Link]) case Overrides(overriddenMembers: Seq[Overridden]) case RegularlyDefined @@ -186,8 +186,12 @@ extension[T] (member: Member) def asLink: LinkToType = LinkToType(member.signature, member.dri, member.kind) def membersBy(op: Member => Boolean): Seq[Member] = member.members.filter(op) + def withDRI(dri: DRI): Member = member.copy(dri = dri) + def withMembers(newMembers: Seq[Member]): Member = member.copy(members = newMembers) + def withName(name: String): Member = member.copy(name = name) + def updateRecusivly(op: Member => Member): Member = val newMembers = member.members.map(_.updateRecusivly(op)) op(member).withMembers(newMembers) diff --git a/scaladoc/src/dotty/tools/scaladoc/renderers/MemberRenderer.scala b/scaladoc/src/dotty/tools/scaladoc/renderers/MemberRenderer.scala index 506a5051eb8b..19dd4e4b7403 100644 --- a/scaladoc/src/dotty/tools/scaladoc/renderers/MemberRenderer.scala +++ b/scaladoc/src/dotty/tools/scaladoc/renderers/MemberRenderer.scala @@ -120,10 +120,9 @@ class MemberRenderer(signatureRenderer: SignatureRenderer)(using DocContext) ext Seq("Implicitly added by ", renderLink(name, dri)) case Origin.ExtensionFrom(name, dri) => Seq("Extension method from ", renderLink(name, dri)) - case Origin.ExportedFrom(name, dri) => - val signatureName: TagArg = dri match - case Some(dri: DRI) => renderLink(name, dri) - case None => name + case Origin.ExportedFrom(Some(link)) => + val signatureName: TagArg = link match + case Link(name, dri) => renderLink(name, dri) Seq("Exported from ", signatureName) case _ => Nil } @@ -132,7 +131,7 @@ class MemberRenderer(signatureRenderer: SignatureRenderer)(using DocContext) ext val depStyle = if member.deprecated.isEmpty then "" else "deprecated" val nameClasses = cls := s"documentableName $depStyle" - val rawBuilder = ScalaSignatureProvider.rawSignature(member, InlineSignatureBuilder()) + val rawBuilder = ScalaSignatureProvider.rawSignature(member, InlineSignatureBuilder())() val inlineBuilder = rawBuilder.asInstanceOf[InlineSignatureBuilder] val kind :: modifiersRevered = inlineBuilder.preName val signature = inlineBuilder.names.reverse diff --git a/scaladoc/src/dotty/tools/scaladoc/renderers/Resources.scala b/scaladoc/src/dotty/tools/scaladoc/renderers/Resources.scala index 3cb97316ad2f..7857c1bbd06a 100644 --- a/scaladoc/src/dotty/tools/scaladoc/renderers/Resources.scala +++ b/scaladoc/src/dotty/tools/scaladoc/renderers/Resources.scala @@ -145,7 +145,7 @@ trait Resources(using ctx: DocContext) extends Locations, Writer: case m: Member if m.kind != Kind.RootPackage => val descr = m.dri.asFileLocation def processMember(member: Member): Seq[JSON] = - val signatureBuilder = ScalaSignatureProvider.rawSignature(member, InlineSignatureBuilder()).asInstanceOf[InlineSignatureBuilder] + val signatureBuilder = ScalaSignatureProvider.rawSignature(member, InlineSignatureBuilder())().asInstanceOf[InlineSignatureBuilder] val sig = Signature(Plain(s"${member.kind.name} "), Plain(member.name)) ++ signatureBuilder.names.reverse val entry = mkEntry(member.dri, member.name, flattenToText(sig), descr, member.kind.name) val children = member diff --git a/scaladoc/src/dotty/tools/scaladoc/tasty/ClassLikeSupport.scala b/scaladoc/src/dotty/tools/scaladoc/tasty/ClassLikeSupport.scala index 0ff21cda60c1..2ebc98a94848 100644 --- a/scaladoc/src/dotty/tools/scaladoc/tasty/ClassLikeSupport.scala +++ b/scaladoc/src/dotty/tools/scaladoc/tasty/ClassLikeSupport.scala @@ -149,21 +149,23 @@ trait ClassLikeSupport: } case dd: DefDef if !dd.symbol.isHiddenByVisibility && dd.symbol.isExported && !dd.symbol.isArtifact => - val exportedTarget = dd.rhs.collect { - case a: Apply => a.fun.asInstanceOf[Select] - case s: Select => s + dd.rhs.map { + case TypeApply(rhs, _) => rhs + case Apply(TypeApply(rhs, _), _) => rhs + case rhs => rhs + }.map(_.tpe.termSymbol).filter(_.exists).map(_.tree).map { + case v: ValDef if v.symbol.flags.is(Flags.Module) && !v.symbol.flags.is(Flags.Synthetic) => + v.symbol.owner -> Symbol.newVal(c.symbol, dd.name, v.tpt.tpe, Flags.Final, Symbol.noSymbol).tree + case other => other.symbol.owner -> other + }.flatMap { (originalOwner, tree) => + parseMember(c)(tree) + .map { m => m + .withDRI(dd.symbol.dri) + .withName(dd.symbol.normalizedName) + .withKind(Kind.Exported(m.kind)) + .withOrigin(Origin.ExportedFrom(Some(Link(originalOwner.normalizedName, originalOwner.dri)))) + } } - val functionName = exportedTarget.fold("function")(_.name) - val instanceName = exportedTarget.collect { - case Select(qualifier: Select, _) => qualifier.name - case Select(qualifier: Ident, _) => qualifier.tpe.typeSymbol.normalizedName - }.getOrElse("instance") - val dri = dd.rhs.collect { - case s: Select if s.symbol.isDefDef => s.symbol.dri - }.orElse(exportedTarget.map(_.qualifier.tpe.typeSymbol.dri)) - - Some(parseMethod(c, dd.symbol, specificKind = Kind.Exported(_)) - .withOrigin(Origin.ExportedFrom(s"$instanceName.$functionName", dri))) case dd: DefDef if !dd.symbol.isHiddenByVisibility && !dd.symbol.isSyntheticFunc && !dd.symbol.isExtensionMethod && !dd.symbol.isArtifact => Some(parseMethod(c, dd.symbol)) @@ -423,7 +425,10 @@ trait ClassLikeSupport: val defaultKind = Kind.Type(!isTreeAbstract(typeDef.rhs), typeDef.symbol.isOpaque, generics).asInstanceOf[Kind.Type] val kind = if typeDef.symbol.flags.is(Flags.Enum) then Kind.EnumCase(defaultKind) else defaultKind - mkMember(typeDef.symbol, kind, tpeTree.asSignature)(deprecated = typeDef.symbol.isDeprecated()) + + if typeDef.symbol.flags.is(Flags.Exported) + then mkMember(typeDef.symbol, Kind.Exported(kind), tpeTree.asSignature)(deprecated = typeDef.symbol.isDeprecated(), origin = Origin.ExportedFrom(None)) + else mkMember(typeDef.symbol, kind, tpeTree.asSignature)(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 diff --git a/scaladoc/src/dotty/tools/scaladoc/tasty/TypesSupport.scala b/scaladoc/src/dotty/tools/scaladoc/tasty/TypesSupport.scala index d825b7ab6c31..faa8b61f807f 100644 --- a/scaladoc/src/dotty/tools/scaladoc/tasty/TypesSupport.scala +++ b/scaladoc/src/dotty/tools/scaladoc/tasty/TypesSupport.scala @@ -305,7 +305,8 @@ trait TypesSupport: private def typeBoundsTreeOfHigherKindedType(using Quotes)(low: reflect.TypeRepr, high: reflect.TypeRepr) = import reflect._ def regularTypeBounds(low: TypeRepr, high: TypeRepr) = - typeBound(low, low = true) ++ typeBound(high, low = false) + if low == high then keyword(" = ").l ++ inner(low) + else typeBound(low, low = true) ++ typeBound(high, low = false) high.match case TypeLambda(params, paramBounds, resType) => if resType.typeSymbol == defn.AnyClass then diff --git a/scaladoc/src/dotty/tools/scaladoc/translators/FilterAttributes.scala b/scaladoc/src/dotty/tools/scaladoc/translators/FilterAttributes.scala index 0a1b62dcb4d6..1202e76a548c 100644 --- a/scaladoc/src/dotty/tools/scaladoc/translators/FilterAttributes.scala +++ b/scaladoc/src/dotty/tools/scaladoc/translators/FilterAttributes.scala @@ -20,7 +20,7 @@ object FilterAttributes: private def origin(m: Member): Map[String, String] = m.origin match case Origin.ImplicitlyAddedBy(name, _) => Map("implicitly" -> s"by $name") case Origin.ExtensionFrom(name, _) => Map("extension" -> s"from $name") - case Origin.ExportedFrom(name, _) => Map("export" -> s"from $name") + case Origin.ExportedFrom(Some(link)) => Map("export" -> s"from ${link.name}}") case _ => Map.empty diff --git a/scaladoc/src/dotty/tools/scaladoc/translators/ScalaSignatureProvider.scala b/scaladoc/src/dotty/tools/scaladoc/translators/ScalaSignatureProvider.scala index d8f80f332b6d..0639b5af09bd 100644 --- a/scaladoc/src/dotty/tools/scaladoc/translators/ScalaSignatureProvider.scala +++ b/scaladoc/src/dotty/tools/scaladoc/translators/ScalaSignatureProvider.scala @@ -2,12 +2,12 @@ package dotty.tools.scaladoc package translators object ScalaSignatureProvider: - def rawSignature(documentable: Member, builder: SignatureBuilder): SignatureBuilder = - documentable.kind match + def rawSignature(documentable: Member, builder: SignatureBuilder)(kind: Kind = documentable.kind): SignatureBuilder = + kind match case Kind.Extension(_, m) => extensionSignature(documentable, m, builder) case Kind.Exported(d) => - methodSignature(documentable, d, builder) + rawSignature(documentable, builder)(d) case d: Kind.Def => methodSignature(documentable, d, builder) case Kind.Constructor(d) => @@ -33,7 +33,7 @@ object ScalaSignatureProvider: case trt: Kind.Trait => traitSignature(documentable, trt, builder) case Kind.Val | Kind.Var | Kind.Implicit(Kind.Val, _) => - fieldSignature(documentable, documentable.kind.name, builder) + fieldSignature(documentable, kind.name, builder) case tpe: Kind.Type => typeSignature(tpe, documentable, builder) case Kind.Package => diff --git a/scaladoc/test/dotty/tools/scaladoc/signatures/TranslatableSignaturesTestCases.scala b/scaladoc/test/dotty/tools/scaladoc/signatures/TranslatableSignaturesTestCases.scala index 9aced84c96d0..d85d2d960d2c 100644 --- a/scaladoc/test/dotty/tools/scaladoc/signatures/TranslatableSignaturesTestCases.scala +++ b/scaladoc/test/dotty/tools/scaladoc/signatures/TranslatableSignaturesTestCases.scala @@ -88,3 +88,5 @@ class SpecializedSignature extends SignatureTest("specializedSignature", Signatu class ContextBounds extends SignatureTest("contextBounds", SignatureTest.all) class FBoundedTypeParameters extends SignatureTest("fboundedTypeParameters", SignatureTest.all) + +class Exports extends SignatureTest("exports2", SignatureTest.all, sourceFiles = List("exports1", "exports2"))