From abea61718efe34a17e2af74316dd777abeb9289c Mon Sep 17 00:00:00 2001 From: Andrzej Ratajczak Date: Tue, 8 Dec 2020 15:36:55 +0100 Subject: [PATCH] Add overrides path hint as a biref comment --- scala3doc-testcases/src/tests/givenDRI.scala | 2 +- scala3doc-testcases/src/tests/overrides.scala | 11 +++++ scala3doc/src/dotty/dokka/model/api/api.scala | 14 ++++-- .../dokka/model/api/internalExtensions.scala | 7 ++- .../dotty/dokka/tasty/ClassLikeSupport.scala | 25 +++++++---- scala3doc/src/dotty/dokka/tasty/SymOps.scala | 2 + .../ImplicitMembersExtensionTransformer.scala | 7 ++- .../dokka/translators/FilterAttributes.scala | 45 ++++++++----------- .../translators/ScalaContentBuilder.scala | 4 ++ .../dokka/translators/ScalaPageCreator.scala | 23 +++++----- .../test/dotty/dokka/linking/DriTest.scala | 2 +- 11 files changed, 85 insertions(+), 57 deletions(-) create mode 100644 scala3doc-testcases/src/tests/overrides.scala diff --git a/scala3doc-testcases/src/tests/givenDRI.scala b/scala3doc-testcases/src/tests/givenDRI.scala index 819aa228c53b..81a10854d7f4 100644 --- a/scala3doc-testcases/src/tests/givenDRI.scala +++ b/scala3doc-testcases/src/tests/givenDRI.scala @@ -31,4 +31,4 @@ class S: def a = 3 given R: A[Int] with - def a = 5 \ No newline at end of file + def a = 5 diff --git a/scala3doc-testcases/src/tests/overrides.scala b/scala3doc-testcases/src/tests/overrides.scala new file mode 100644 index 000000000000..edb28e76f469 --- /dev/null +++ b/scala3doc-testcases/src/tests/overrides.scala @@ -0,0 +1,11 @@ +package tests +package overrides + +class A: + def defInt: Int = 1 + +class B extends A: + override def defInt: Int = 2 + +class C extends B: + override def defInt: Int = 3 diff --git a/scala3doc/src/dotty/dokka/model/api/api.scala b/scala3doc/src/dotty/dokka/model/api/api.scala index 7bd113d64159..a33efd9a3939 100644 --- a/scala3doc/src/dotty/dokka/model/api/api.scala +++ b/scala3doc/src/dotty/dokka/model/api/api.scala @@ -71,11 +71,15 @@ enum Kind(val name: String){ } enum Origin: - case InheritedFrom(name: String, dri: DRI) case ImplicitlyAddedBy(name: String, dri: DRI) case ExtensionFrom(name: String, dri: DRI) case ExportedFrom(name: String, dri: Option[DRI]) - case DefinedWithin + case Overrides(overridenMembers: Seq[Overriden]) + case RegularlyDefined + +case class Overriden(name: String, dri: DRI) + +case class InheritedFrom(name: String, dri: DRI) case class Annotation(val dri: DRI, val params: List[Annotation.AnnotationParameter]) @@ -130,7 +134,8 @@ extension[T] (member: Member): def modifiers: Seq[dotty.dokka.model.api.Modifier] = memberExt.fold(Nil)(_.modifiers) def kind: Kind = memberExt.fold(Kind.Unknown)(_.kind) - def origin: Origin = memberExt.fold(Origin.DefinedWithin)(_.origin) + def origin: Origin = memberExt.fold(Origin.RegularlyDefined)(_.origin) + def inheritedFrom: Option[InheritedFrom] = memberExt.fold(None)(_.inheritedFrom) def annotations: List[Annotation] = memberExt.fold(Nil)(_.annotations) def sources: Option[TastyDocumentableSource] = memberExt.fold(None)(_.sources) def name = member.getName @@ -142,7 +147,8 @@ extension[T] (member: Member): def directParents: Seq[Signature] = compositeMemberExt.fold(Nil)(_.directParents) def knownChildren: Seq[LinkToType] = compositeMemberExt.fold(Nil)(_.knownChildren) - def membersBy(op: Member => Boolean): (Seq[Member], Seq[Member]) = allMembers.filter(op).partition(_.origin == Origin.DefinedWithin) + def membersBy(op: Member => Boolean): Seq[Member] = allMembers.filter(op) + def membersByWithInheritancePartition(op: Member => Boolean): (Seq[Member], Seq[Member]) = membersBy(op).partition(_.inheritedFrom.isEmpty) extension (module: DModule): diff --git a/scala3doc/src/dotty/dokka/model/api/internalExtensions.scala b/scala3doc/src/dotty/dokka/model/api/internalExtensions.scala index 861de2d75a46..4a568edcda65 100644 --- a/scala3doc/src/dotty/dokka/model/api/internalExtensions.scala +++ b/scala3doc/src/dotty/dokka/model/api/internalExtensions.scala @@ -27,7 +27,8 @@ private [model] case class MemberExtension( annotations: List[Annotation], signature: Signature, sources: Option[TastyDocumentableSource] = None, - origin: Origin = Origin.DefinedWithin, + origin: Origin = Origin.RegularlyDefined, + inheritedFrom: Option[InheritedFrom] = None, graph: HierarchyGraph = HierarchyGraph.empty, ) extends ExtraProperty[Documentable]: override def getKey = MemberExtension @@ -67,6 +68,10 @@ extension (member: Member): val ext = MemberExtension.getFrom(member).getOrElse(MemberExtension.empty).copy(origin = origin) putInMember(ext) + def withInheritedFrom(inheritedFrom: InheritedFrom): Member = + val ext = MemberExtension.getFrom(member).getOrElse(MemberExtension.empty).copy(inheritedFrom = Some(inheritedFrom)) + putInMember(ext) + def withKind(kind: Kind): Member = val ext = MemberExtension.getFrom(member).getOrElse(MemberExtension.empty).copy(kind = kind) putInMember(ext) diff --git a/scala3doc/src/dotty/dokka/tasty/ClassLikeSupport.scala b/scala3doc/src/dotty/dokka/tasty/ClassLikeSupport.scala index 18f9afafa149..2e9a0fea6111 100644 --- a/scala3doc/src/dotty/dokka/tasty/ClassLikeSupport.scala +++ b/scala3doc/src/dotty/dokka/tasty/ClassLikeSupport.scala @@ -186,7 +186,7 @@ trait ClassLikeSupport: private def parseInheritedMember(s: Tree): Option[Member] = processTreeOpt(s)(s match case c: ClassDef if c.symbol.shouldDocumentClasslike && !c.symbol.isGiven => Some(parseClasslike(c, signatureOnly = true)) case other => parseMember(other) - ).map(_.withOrigin(Origin.InheritedFrom(s.symbol.owner.normalizedName, s.symbol.owner.dri))) + ).map(_.withInheritedFrom(InheritedFrom(s.symbol.owner.normalizedName, s.symbol.owner.dri))) extension (c: ClassDef): def membersToDocument = c.body.filterNot(_.symbol.isHiddenByVisibility) @@ -320,6 +320,20 @@ trait ClassLikeSupport: val name = method.symbol.normalizedName + val memberExtension = MemberExtension( + methodSymbol.getVisibility(), + methodSymbol.getExtraModifiers(), + methodKind, + methodSymbol.getAnnotations(), + method.returnTpt.dokkaType.asSignature, + methodSymbol.source + ) + + val memberExtensionWithOrigin = + if methodSymbol.isOverriden then memberExtension.copy( + origin = Origin.Overrides(methodSymbol.allOverriddenSymbols.map(_.owner).map(s => Overriden(s.name, s.dri)).toSeq) + ) else memberExtension + new DFunction( methodSymbol.dri, name, @@ -337,14 +351,7 @@ trait ClassLikeSupport: /*isExpectActual =*/ false, PropertyContainer.Companion.empty() plus MethodExtension(paramLists.map(_.size)) - plus(MemberExtension( - methodSymbol.getVisibility(), - methodSymbol.getExtraModifiers(), - methodKind, - methodSymbol.getAnnotations(), - method.returnTpt.dokkaType.asSignature, - methodSymbol.source - )) + plus(memberExtensionWithOrigin) ) def parseArgument(argument: ValDef, prefix: Symbol => String, isExtendedSymbol: Boolean = false, isGrouped: Boolean = false): DParameter = diff --git a/scala3doc/src/dotty/dokka/tasty/SymOps.scala b/scala3doc/src/dotty/dokka/tasty/SymOps.scala index 23ffc93c7ca9..706a7a0c0d3f 100644 --- a/scala3doc/src/dotty/dokka/tasty/SymOps.scala +++ b/scala3doc/src/dotty/dokka/tasty/SymOps.scala @@ -84,6 +84,8 @@ class SymOps[Q <: Quotes](val q: Q): def isExported: Boolean = sym.flags.is(Flags.Exported) + def isOverriden: Boolean = sym.flags.is(Flags.Override) + def isExtensionMethod: Boolean = sym.flags.is(Flags.ExtensionMethod) def isLeftAssoc(d: Symbol): Boolean = !d.name.endsWith(":") diff --git a/scala3doc/src/dotty/dokka/transformers/ImplicitMembersExtensionTransformer.scala b/scala3doc/src/dotty/dokka/transformers/ImplicitMembersExtensionTransformer.scala index d3dff4cc1f81..037f235cb9dd 100644 --- a/scala3doc/src/dotty/dokka/transformers/ImplicitMembersExtensionTransformer.scala +++ b/scala3doc/src/dotty/dokka/transformers/ImplicitMembersExtensionTransformer.scala @@ -40,15 +40,14 @@ class ImplicitMembersExtensionTransformer(ctx: DokkaContext) extends Documentabl val MyDri = c.getDri def collectApplicableMembers(source: Member): Seq[Member] = source.allMembers.flatMap { - case m @ Member(_, _, _, Kind.Extension(ExtensionTarget(_, _, MyDri, _)), Origin.DefinedWithin) => + case m @ Member(_, _, _, Kind.Extension(ExtensionTarget(_, _, MyDri, _)), Origin.RegularlyDefined) => Seq(m.withOrigin(Origin.ExtensionFrom(source.name, source.dri)).withKind(Kind.Def)) - case m @ Member(_, _, _, conversionProvider: ImplicitConversionProvider, Origin.DefinedWithin) => + case m @ Member(_, _, _, conversionProvider: ImplicitConversionProvider, Origin.RegularlyDefined) => conversionProvider.conversion match case Some(ImplicitConversion(MyDri, to)) => classlikeMap.get(to).toSeq.flatMap { owner => val newMembers = owner.allMembers.filter(_.origin match - case Origin.DefinedWithin => true - case Origin.InheritedFrom(_, _) => true + case Origin.RegularlyDefined => true case _ => false ) newMembers.map(_.withOrigin(Origin.ImplicitlyAddedBy(m.name, m.dri))) diff --git a/scala3doc/src/dotty/dokka/translators/FilterAttributes.scala b/scala3doc/src/dotty/dokka/translators/FilterAttributes.scala index 0d68862e3b23..7598dce7c8ea 100644 --- a/scala3doc/src/dotty/dokka/translators/FilterAttributes.scala +++ b/scala3doc/src/dotty/dokka/translators/FilterAttributes.scala @@ -19,34 +19,27 @@ import dotty.dokka.model.api._ import dotty.dokka._ object FilterAttributes: - def attributesFor(documentable: Documentable): Map[String, String] = - val base = visibity(documentable) ++ visibity(documentable) ++ origin(documentable) ++ keywords(documentable) + def attributesFor(m: Member): Map[String, String] = + val base = visibity(m) ++ visibity(m) ++ origin(m) ++ keywords(m) ++ inheritedFrom(m) base.filter(_._2.nonEmpty) - private def keywords(documentable: Documentable): Map[String, String] = documentable match - case v: Member => - Map("keywords" -> v.modifiers.map(_.name).mkString(",")) - case null => - Map.empty - - - private def visibity(documentable: Documentable): Map[String, String] = documentable match - case v: Member => - Map("visibility" -> v.visibility.name) - case null => - Map.empty - - - private def origin(documentable: Documentable): Map[String, String] = documentable match - case v: Member => - v.origin match - case Origin.InheritedFrom(name, _) => Map("inherited" -> name) - 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 _ => Map.empty - case null => - Map.empty + private def keywords(m: Member): Map[String, String] = + Map("keywords" -> m.modifiers.map(_.name).mkString(",")) + + + private def visibity(m: Member): Map[String, String] = + Map("visibility" -> m.visibility.name) + + private def inheritedFrom(m: Member): Map[String, String] = m.inheritedFrom match + case Some(InheritedFrom(name, _)) => Map("inherited" -> name) + case _ => Map.empty + + 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 _ => Map.empty + def defaultValues = Map( "inherited" -> "Not inherited", diff --git a/scala3doc/src/dotty/dokka/translators/ScalaContentBuilder.scala b/scala3doc/src/dotty/dokka/translators/ScalaContentBuilder.scala index 2930ce07ae29..5411b569d59c 100644 --- a/scala3doc/src/dotty/dokka/translators/ScalaContentBuilder.scala +++ b/scala3doc/src/dotty/dokka/translators/ScalaContentBuilder.scala @@ -492,6 +492,10 @@ class ScalaPageContentBuilder( case Some(dri: DRI) => SLink(name, dri) case None => name Signature("Exported from ", signatureName) + case Origin.Overrides(overridenMembers) => + def intersperse(xs: Seq[SLink]): Seq[(String | SLink)] = + xs.flatMap(Seq(_, " -> ")).dropRight(1).asInstanceOf[Seq[(String | SLink)]] // dropRight returns `Seq[Object]` + Signature("Definition classes: ").join(Signature(intersperse(overridenMembers.map(SLink(_, _))):_*)) case _ => Nil } val styles: Set[Style] = if documentable.deprecated.isDefined then Set(TextStyle.Strikethrough) else Set.empty diff --git a/scala3doc/src/dotty/dokka/translators/ScalaPageCreator.scala b/scala3doc/src/dotty/dokka/translators/ScalaPageCreator.scala index a455677c267e..ec661c181a54 100644 --- a/scala3doc/src/dotty/dokka/translators/ScalaPageCreator.scala +++ b/scala3doc/src/dotty/dokka/translators/ScalaPageCreator.scala @@ -40,7 +40,7 @@ class ScalaPageCreator( page.modified(name, page.getChildren) private def pagesForMembers(p: Member): Seq[PageNode] = - p.allMembers.filter(_.origin == Origin.DefinedWithin).collect { + p.allMembers.filter(m => m.origin == Origin.RegularlyDefined && m.inheritedFrom.isEmpty).collect { case f: DFunction => updatePageNameForMember(pageForFunction(f), f) case c: DClass => updatePageNameForMember(pageForDClass(c), c) } @@ -365,14 +365,14 @@ class ScalaPageCreator( }.toSeq - val (definedMethods, inheritedMethods) = s.membersBy(_.kind == Kind.Def) - val (definedFields, inheritedFiles) = s.membersBy(m => m.kind == Kind.Val || m.kind == Kind.Var) - val (definedClasslikes, inheritedClasslikes) = s.membersBy(m => m.kind.isInstanceOf[Classlike]) - val (definedTypes, inheritedTypes) = s.membersBy(_.kind.isInstanceOf[Kind.Type]) - val (definedGivens, inheritedGives) = s.membersBy(_.kind.isInstanceOf[Kind.Given]) - val (definedExtensions, inheritedExtensions) = s.membersBy(_.kind.isInstanceOf[Kind.Extension]) - val exports = s.allMembers.filter(_.kind == Kind.Exported) - val (definedImplicits, inheritedImplicits) = s.membersBy(_.kind.isInstanceOf[Kind.Implicit]) + val (definedMethods, inheritedMethods) = s.membersByWithInheritancePartition(_.kind == Kind.Def) + val (definedFields, inheritedFiles) = s.membersByWithInheritancePartition(m => m.kind == Kind.Val || m.kind == Kind.Var) + val (definedClasslikes, inheritedClasslikes) = s.membersByWithInheritancePartition(m => m.kind.isInstanceOf[Classlike]) + val (definedTypes, inheritedTypes) = s.membersByWithInheritancePartition(_.kind.isInstanceOf[Kind.Type]) + val (definedGivens, inheritedGives) = s.membersByWithInheritancePartition(_.kind.isInstanceOf[Kind.Given]) + val (definedExtensions, inheritedExtensions) = s.membersByWithInheritancePartition(_.kind.isInstanceOf[Kind.Extension]) + val (definedExports, inheritedExports) = s.membersByWithInheritancePartition(_.kind == Kind.Exported) + val (definedImplicits, inheritedImplicits) = s.membersByWithInheritancePartition(_.kind.isInstanceOf[Kind.Implicit]) b .contentForComments(s) @@ -403,13 +403,14 @@ class ScalaPageCreator( DocumentableGroup(Some("Inherited implicits"), inheritedImplicits) ) .documentableTab("Exports")( - DocumentableGroup(Some("Defined exports"), exports) + DocumentableGroup(Some("Defined exports"), definedExports), + DocumentableGroup(Some("Inherited exports"), inheritedExports), ) def contentForEnum(c: DClass) = b.documentableTab("Enum entries")( - DocumentableGroup(None, c.membersBy(_.kind == Kind.EnumCase)._1) // Enum entries cannot be inherited + DocumentableGroup(None, c.membersBy(_.kind == Kind.EnumCase)) // Enum entries cannot be inherited ) diff --git a/scala3doc/test/dotty/dokka/linking/DriTest.scala b/scala3doc/test/dotty/dokka/linking/DriTest.scala index a4ec18540263..92ba7ca64d74 100644 --- a/scala3doc/test/dotty/dokka/linking/DriTest.scala +++ b/scala3doc/test/dotty/dokka/linking/DriTest.scala @@ -29,4 +29,4 @@ abstract class DriTest(testName: String) extends ScaladocTest(testName): extension (m: DModule) private def collectMembers = m.getPackages.asScala.toList.flatMap(collectFrom) private def collectFrom(m: Member): Seq[Member] = - m +: m.allMembers.filter(_.origin == Origin.DefinedWithin).flatMap(collectFrom) + m +: m.allMembers.filter(_.origin == Origin.RegularlyDefined).flatMap(collectFrom)