Skip to content

Commit 4124ce0

Browse files
authored
Merge pull request #11383 from pikinier20/top-level-bug
Scaladoc: Fix links to members inside package objects, links containing hashes and links containg backticks
2 parents e0e2263 + ccc77d4 commit 4124ce0

File tree

10 files changed

+46
-18
lines changed

10 files changed

+46
-18
lines changed

scaladoc-testcases/src/tests/complexNames.scala

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,20 +7,21 @@ abstract class A:
77
def +:(other: Int): A
88
def :+(other: Int): A
99

10-
// scaladoc has problems with names in backticks
11-
// def `multi word name`: Int
12-
// def `*** name with arbitrary chars ^%`: Int
13-
// def `mischievous(param:Int)`(otherParam: Int): String
14-
// def withMischievousParams(`param: String, param2`: String): String
10+
def `multi word name`: Int
11+
def `*** name with arbitrary chars ^%`: Int
12+
def `mischievous(param:Int)`(otherParam: Int): String
13+
def withMischievousParams(`param: String, param23`: String): String
1514

1615
def complexName_^*(param: String): A
1716

1817
def `completelyUnnecessaryBackticks`: Int //expected: def completelyUnnecessaryBackticks: Int
1918
def `+++:`(other: Int): A //expected: def +++:(other: Int): A
2019
def `:+++`(other: Int): A //expected: def :+++(other: Int): A
2120

22-
def `abc_^^_&&`: A //expected: def abc_^^_&&: A
21+
def `abc_^^_&&`: A
2322
def `abc_def`: A //expected: def abc_def: A
2423
def `abc_def_++`: A //expected: def abc_def_++: A
25-
// def `++_abc`: A
26-
// def `abc_++_--`: A
24+
def `++_abc`: A
25+
def `abc_++_--`: A
26+
27+
class `class with backticks to check links`

scaladoc-testcases/src/tests/functionDRI.scala

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,3 +31,9 @@ class E:
3131
def b = 10
3232
object F:
3333
def b = 11
34+
35+
object #:: {
36+
def #:: = 10
37+
}
38+
39+

scaladoc/src/dotty/tools/scaladoc/DRI.scala

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package dotty.tools.scaladoc
22

33
import java.nio.file.Path
4+
import dotty.tools.scaladoc.util.Escape._
45

56
val staticFileSymbolUUID = "___staticFile___"
67

@@ -17,7 +18,7 @@ final case class DRI(
1718

1819
def isStaticFile = symbolUUID == staticFileSymbolUUID
1920

20-
def asFileLocation: String = location.replace(".","/")
21+
def asFileLocation: String = escapeUrl(location).replace(".", "/")
2122

2223
object DRI:
2324
def forPath(path: Path) =

scaladoc/src/dotty/tools/scaladoc/renderers/HtmlRenderer.scala

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ class HtmlRenderer(rootPackage: Member, val members: Map[DRI, Member])(using ctx
2828
private val args = summon[DocContext].args
2929
val staticSite = summon[DocContext].staticSiteContext
3030

31+
val effectiveMembers = members.filter( (dri, member) => member.origin == Origin.RegularlyDefined && member.inheritedFrom.isEmpty)
32+
3133
private def needsOwnPage(member: Member): Boolean =
3234
def properKind(kind: Kind): Boolean = kind match
3335
case Kind.Package => true

scaladoc/src/dotty/tools/scaladoc/renderers/Locations.scala

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ import scala.util.matching._
1717
val UnresolvedLocationLink = "#"
1818

1919
trait Locations(using ctx: DocContext):
20-
def members: Map[DRI, Member]
20+
def effectiveMembers: Map[DRI, Member]
2121

2222
var cache = new JHashMap[DRI, Seq[String]]()
2323

@@ -48,7 +48,7 @@ trait Locations(using ctx: DocContext):
4848
UnresolvedLocationLink
4949

5050
def pathToPage(from: DRI, to: DRI): String =
51-
if to.isStaticFile || members.contains(to) then
51+
if to.isStaticFile || effectiveMembers.contains(to) then
5252
val anchor = if to.anchor.isEmpty then "" else "#" + to.anchor
5353
pathToRaw(rawLocation(from), rawLocation(to)) +".html" + anchor
5454
else
@@ -63,6 +63,7 @@ trait Locations(using ctx: DocContext):
6363

6464

6565
def pathToRaw(from: Seq[String], to: Seq[String]): String =
66+
import dotty.tools.scaladoc.util.Escape._
6667
val fromDir = from.dropRight(1)
6768
val commonPaths = to.zip(fromDir).takeWhile{ case (a, b) => a == b }.size
6869

@@ -72,7 +73,7 @@ trait Locations(using ctx: DocContext):
7273
case Nil => to.lastOption.fold(Seq("index"))(".." :: _ :: Nil)
7374
case l => l
7475

75-
(contextPath ++ nodePath).mkString("/")
76+
escapeUrl((contextPath ++ nodePath).mkString("/"))
7677

7778
def resolveRoot(from: Seq[String], to: String): String =
7879
pathToRaw(from, to.split("/").toList)
Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,26 @@
11
package dotty.tools.scaladoc.tasty
22

33
import dotty.tools.scaladoc._
4+
import dotty.tools.dotc.core.StdNames.nme.keywords
5+
import dotty.tools.dotc.core.Names.termName
46

57
trait NameNormalizer { self: TastyParser =>
68
import qctx.reflect._
79
extension (s: Symbol) def normalizedName: String = {
810
val withoutGivenPrefix = if s.isGiven then s.name.stripPrefix("given_") else s.name
911
val withoutObjectSuffix = if s.flags.is(Flags.Module) then withoutGivenPrefix.stripSuffix("$") else withoutGivenPrefix
1012
val constructorNormalizedName = if s.isClassConstructor then "this" else withoutObjectSuffix
11-
constructorNormalizedName
13+
val escaped = escapedName(constructorNormalizedName)
14+
escaped
1215
}
16+
17+
private val ignoredKeywords: Set[String] = Set("this")
18+
19+
private def escapedName(name: String) =
20+
val simpleIdentifierRegex = raw"(?:\w+_[^\[\(\s_]+)|\w+|[^\[\(\s\w_]+".r
21+
name match
22+
case n if ignoredKeywords(n) => n
23+
case n if keywords(termName(n)) => s"`$n`"
24+
case simpleIdentifierRegex() => name
25+
case n => s"`$n`"
1326
}

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ class SymOps[Q <: Quotes](val q: Q):
1616
def className: Option[String] =
1717
if (sym.isClassDef && !sym.flags.is(Flags.Package)) Some(
1818
Some(sym.maybeOwner).filter(s => s.exists).flatMap(_.className).fold("")(cn => cn + "$") + sym.name
19-
)
19+
).filterNot(_.contains("package$"))
2020
else if (sym.isPackageDef) None
2121
else sym.maybeOwner.className
2222

@@ -117,8 +117,8 @@ class SymOps[Q <: Quotes](val q: Q):
117117
val csym = sym.asInstanceOf[dotc.core.Symbols.Symbol]
118118
Option(csym.associatedFile).fold("")(_.path)
119119
}
120-
// We want package object to point to package
121-
val className = sym.className.filter(_ != "package$")
120+
121+
val className = sym.className
122122

123123
DRI(
124124
className.fold(sym.packageName)(cn => s"${sym.packageName}.${cn}"),
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
package dotty.tools.scaladoc.util
2+
3+
object Escape:
4+
def escapeUrl(url: String) = url.replace("#","%23")

scaladoc/test/dotty/tools/scaladoc/SignatureTestCases.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ class InheritanceLoop extends SignatureTest("inheritanceLoop", SignatureTest.all
5858
class InheritedMembers extends SignatureTest("inheritedMembers2", SignatureTest.all.filter(_ != "class"),
5959
sourceFiles = List("inheritedMembers1", "inheritedMembers2"))
6060

61-
class ComplexNames extends SignatureTest("complexNames", Seq("def"))
61+
class ComplexNames extends SignatureTest("complexNames", Seq("def", "class"))
6262

6363
class WrongDocumentationLinks extends SignatureTest("links", Seq("def"))
6464

scaladoc/test/dotty/tools/scaladoc/renderers/LocationTests.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import dotty.tools.scaladoc.util.HTML._
88
class LocationTests:
99
given DocContext = testDocContext()
1010
object locations extends Locations:
11-
val members = Map.empty
11+
val effectiveMembers = Map.empty
1212

1313
@Test
1414
def testPathToRoot() =

0 commit comments

Comments
 (0)