Skip to content

Commit 6deff5e

Browse files
BarkingBadabgruszecki
authored andcommitted
Scaladoc: resolve looked up inherited symbols to correct DRIs
1 parent ebdbdc4 commit 6deff5e

File tree

6 files changed

+65
-24
lines changed

6 files changed

+65
-24
lines changed

scaladoc-testcases/src/tests/links.scala

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,7 @@ object AnObject:
99
/**
1010
* Broken link, that should result a warning not break compilation
1111
* [[tests.links.AnObject]]
12-
1312
*/
1413
class LinksTest:
1514
def verifyIfLinksTestIsGenerated(b: Int): Int
16-
= 123
15+
= 123

scaladoc/src/dotty/tools/scaladoc/tasty/SymOps.scala

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,3 +190,8 @@ class SymOps[Q <: Quotes](val q: Q) extends JavadocAnchorCreator with Scaladoc2A
190190
// For some reason it contains `$$$` instrad of symbol name
191191
s"${sym.name}${sym.fullName}/${sym.signature.resultSig}/[${sym.signature.paramSigs.mkString("/")}]"
192192
)
193+
194+
def driInContextOfInheritingParent(par: Symbol)(using dctx: DocContext): DRI = sym.dri.copy(
195+
location = par.dri.location,
196+
externalLink = None
197+
)

scaladoc/src/dotty/tools/scaladoc/tasty/TastyParser.scala

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -80,8 +80,12 @@ case class ScaladocTastyInspector()(using ctx: DocContext) extends DocTastyInspe
8080
def driFor(link: String): Option[DRI] =
8181
val symOps = new SymOps[q.type](q)
8282
import symOps._
83-
Try(QueryParser(link).readQuery()).toOption.flatMap(q =>
84-
MemberLookup.lookupOpt(q, None).map{ case (sym, _) => sym.dri}
83+
Try(QueryParser(link).readQuery()).toOption.flatMap(query =>
84+
MemberLookup.lookupOpt(query, None).map {
85+
case (sym, _, inheritingParent) => inheritingParent match
86+
case Some(parent) => sym.driInContextOfInheritingParent(parent)
87+
case None => sym.dri
88+
}
8589
)
8690

8791
ctx.staticSiteContext.foreach(_.memberLinkResolver = driFor)

scaladoc/src/dotty/tools/scaladoc/tasty/comments/Comments.scala

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@ abstract class MarkupConversion[T](val repr: Repr)(using DocContext) {
8383

8484
object SymOps extends SymOps[qctx.type](qctx)
8585
export SymOps.dri
86+
export SymOps.driInContextOfInheritingParent
8687

8788
def resolveLink(queryStr: String): DocLink =
8889
if SchemeUri.matches(queryStr) then DocLink.ToURL(queryStr)
@@ -94,8 +95,11 @@ abstract class MarkupConversion[T](val repr: Repr)(using DocContext) {
9495
DocLink.UnresolvedDRI(queryStr, msg)
9596
case Right(query) =>
9697
MemberLookup.lookup(using qctx)(query, owner) match
97-
case Some((sym, targetText)) =>
98-
DocLink.ToDRI(sym.dri, targetText)
98+
case Some((sym, targetText, inheritingParent)) =>
99+
var dri = inheritingParent match
100+
case Some(parent) => sym.driInContextOfInheritingParent(parent)
101+
case None => sym.dri
102+
DocLink.ToDRI(dri, targetText)
99103
case None =>
100104
val txt = s"No DRI found for query"
101105
val msg = s"$txt: $queryStr"

scaladoc/src/dotty/tools/scaladoc/tasty/comments/MemberLookup.scala

Lines changed: 30 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,22 @@ import scala.quoted._
55

66
trait MemberLookup {
77

8+
def memberLookupResult(using Quotes)(
9+
symbol: quotes.reflect.Symbol,
10+
label: String,
11+
inheritingParent: Option[quotes.reflect.Symbol] = None
12+
): (quotes.reflect.Symbol, String, Option[quotes.reflect.Symbol]) =
13+
(symbol, label, inheritingParent)
14+
815
def lookup(using Quotes, DocContext)(
916
query: Query,
1017
owner: quotes.reflect.Symbol,
11-
): Option[(quotes.reflect.Symbol, String)] = lookupOpt(query, Some(owner))
18+
): Option[(quotes.reflect.Symbol, String, Option[quotes.reflect.Symbol])] = lookupOpt(query, Some(owner))
1219

1320
def lookupOpt(using Quotes, DocContext)(
1421
query: Query,
1522
ownerOpt: Option[quotes.reflect.Symbol],
16-
): Option[(quotes.reflect.Symbol, String)] =
23+
): Option[(quotes.reflect.Symbol, String, Option[quotes.reflect.Symbol])] =
1724
try
1825
import quotes.reflect._
1926

@@ -26,7 +33,7 @@ trait MemberLookup {
2633
def nearestMembered(sym: Symbol): Symbol =
2734
if sym.isClassDef || sym.flags.is(Flags.Package) then sym else nearestMembered(sym.owner)
2835

29-
val res: Option[(Symbol, String)] = {
36+
val res: Option[(Symbol, String, Option[Symbol])] = {
3037
def toplevelLookup(querystrings: List[String]) =
3138
downwardLookup(querystrings, defn.PredefModule.moduleClass)
3239
.orElse(downwardLookup(querystrings, defn.ScalaPackage))
@@ -38,7 +45,7 @@ trait MemberLookup {
3845
val nearest = nearestMembered(owner)
3946
val nearestCls = nearestClass(owner)
4047
val nearestPkg = nearestPackage(owner)
41-
def relativeLookup(querystrings: List[String], owner: Symbol): Option[Symbol] = {
48+
def relativeLookup(querystrings: List[String], owner: Symbol): Option[(Symbol, Option[Symbol])] = {
4249
val isMeaningful =
4350
owner.exists
4451
// those are just an optimisation, they can be dropped if problems show up
@@ -56,20 +63,20 @@ trait MemberLookup {
5663

5764
query match {
5865
case Query.StrictMemberId(id) =>
59-
downwardLookup(List(id), nearest).map(_ -> id)
66+
downwardLookup(List(id), nearest).map(memberLookupResult(_, id, _))
6067
case Query.QualifiedId(Query.Qual.This, _, rest) =>
61-
downwardLookup(rest.asList, nearestCls).map(_ -> rest.join)
68+
downwardLookup(rest.asList, nearestCls).map(memberLookupResult(_, rest.join, _))
6269
case Query.QualifiedId(Query.Qual.Package, _, rest) =>
63-
downwardLookup(rest.asList, nearestPkg).map(_ -> rest.join)
70+
downwardLookup(rest.asList, nearestPkg).map(memberLookupResult(_, rest.join, _))
6471
case query =>
6572
val ql = query.asList
6673
toplevelLookup(ql)
6774
.orElse(relativeLookup(ql, nearest))
68-
.map(_ -> query.join)
75+
.map(memberLookupResult(_, query.join, _))
6976
}
7077

7178
case None =>
72-
toplevelLookup(query.asList).map(_ -> query.join)
79+
toplevelLookup(query.asList).map(memberLookupResult(_, query.join, _))
7380
}
7481
}
7582

@@ -165,13 +172,15 @@ trait MemberLookup {
165172
}
166173
}
167174

168-
private def downwardLookup(using Quotes)(query: List[String], owner: quotes.reflect.Symbol): Option[quotes.reflect.Symbol] = {
175+
private def downwardLookup(using Quotes)(
176+
query: List[String], owner: quotes.reflect.Symbol
177+
): Option[(quotes.reflect.Symbol, Option[quotes.reflect.Symbol])] = {
169178
import quotes.reflect._
170179
query match {
171180
case Nil => None
172181
case q :: Nil =>
173182
val sel = MemberLookup.Selector.fromString(q)
174-
sel.kind match {
183+
val res = sel.kind match {
175184
case MemberLookup.SelectorKind.NoForce =>
176185
val lookedUp = localLookup(sel, owner).toSeq
177186
// note: those flag lookups are necessary b/c for objects we return their classes
@@ -180,7 +189,16 @@ trait MemberLookup {
180189
)
181190
case _ =>
182191
localLookup(sel, owner).nextOption
183-
192+
}
193+
res match {
194+
case None => None
195+
case Some(sym) =>
196+
val externalOwner: Option[quotes.reflect.Symbol] =
197+
if owner eq sym.owner then None
198+
else if owner.flags.is(Flags.Module) then Some(owner.moduleClass)
199+
else if owner.isClassDef then Some(owner)
200+
else None
201+
Some(sym -> externalOwner)
184202
}
185203
case q :: qs =>
186204
val sel = MemberLookup.Selector.fromString(q)

scaladoc/test/dotty/tools/scaladoc/tasty/comments/MemberLookupTests.scala

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -38,16 +38,27 @@ class LookupTestCases[Q <: Quotes](val q: Quotes) {
3838

3939
"java.util.AbstractCollection" -> cls("java.util.AbstractCollection"),
4040
"java.lang.String" -> cls("java.lang.String"),
41+
42+
"tests.lookupInheritedMembers.pack1.A.x" ->
43+
cls("tests.lookupInheritedMembers.pack1.A").fun("x"),
44+
45+
"tests.lookupInheritedMembers.pack2.B.x" ->
46+
cls("tests.lookupInheritedMembers.pack1.A").fun("x"),
4147
)
4248

43-
cases.foreach { case (query, Sym(sym)) =>
44-
val lookupRes = MemberLookup.lookupOpt(parseQuery(query), None)
45-
assertTrue(s"Couldn't look up: $query", lookupRes.nonEmpty)
46-
val Some((lookedUp, _)) = lookupRes
47-
assertSame(query, sym, lookedUp)
49+
cases.foreach { case (query, sym) =>
50+
testOwnerlessLookup(query, sym)
4851
}
4952
}
5053

54+
def testOwnerlessLookup(query: String, wrappedTarget: Sym): Unit = {
55+
val target = wrappedTarget.symbol
56+
val lookupRes = MemberLookup.lookupOpt(parseQuery(query), None)
57+
assertTrue(s"Couldn't look up: $query", lookupRes.nonEmpty)
58+
val Some((lookedUp, _, _)) = lookupRes
59+
assertSame(query, target, lookedUp)
60+
}
61+
5162
def testOwnedLookup(): Unit = {
5263
val cases = List[((Sym, String), Sym)](
5364
cls("tests.A") -> "tests.Methods.simple" -> cls("tests.Methods").fun("simple"),
@@ -102,7 +113,7 @@ class LookupTestCases[Q <: Quotes](val q: Quotes) {
102113
)
103114

104115
cases.foreach { case ((Sym(owner), query), Sym(target)) =>
105-
val Some((lookedUp, _)) = MemberLookup.lookup(parseQuery(query), owner)
116+
val Some((lookedUp, _, _)) = MemberLookup.lookup(parseQuery(query), owner)
106117
assertSame(s"$owner / $query", target, lookedUp)
107118
}
108119
}

0 commit comments

Comments
 (0)