Skip to content

Commit b344ee0

Browse files
authored
Merge pull request #13910 from dotty-staging/scaladoc/backport-bugfixes-311
Backport Scaladoc bugfixes to 3.1.1
2 parents 9e7b57f + e38a04c commit b344ee0

17 files changed

+113
-94
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
package tests.externalStubs
2+
3+
trait \/
4+
5+
trait /\

scaladoc-testcases/src/tests/externalLocations/scaladoc2.scala

+5-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
1-
package tests.externalScaladoc2
1+
package tests
2+
package externalScaladoc2
23

34
import scala.util.matching.*
5+
import externalStubs._
46

57
class Test {
68
def a: String = ???
@@ -10,5 +12,7 @@ class Test {
1012
def c: Regex.Match = ???
1113
}
1214

15+
class Test2 extends \/ with /\
16+
1317
abstract class MySeq[T] extends scala.collection.immutable.Seq[T]
1418

scaladoc-testcases/src/tests/externalLocations/scaladoc3.scala

+5-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
1-
package tests.externalScaladoc3
1+
package tests
2+
package externalScaladoc3
23

34
import scala.util.matching.*
5+
import externalStubs._
46

57
class Test {
68
def a: String = ???
@@ -10,3 +12,5 @@ class Test {
1012
def c: Regex.Match = ???
1113
}
1214

15+
class Test2 extends \/ with /\
16+

scaladoc-testcases/src/tests/givenSignatures.scala

+8-2
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,19 @@ package givenSignatures
44

55
object Obj
66

7-
given Seq[String] = Nil
7+
given Seq[String] = Nil //expected: given given_Seq_String: Seq[String]
88

9-
given GivenType = GivenType()
9+
given GivenType = GivenType() //expected: given given_GivenType: GivenType
1010

1111
class GivenType
1212

1313
trait Ord[T]
1414

1515
given listOrd[T](using ord: Ord[T]): Ord[List[T]]
1616
= ???
17+
18+
trait Foo[A]
19+
20+
given listOrd: Foo[String] with { val i: Int = 1 } //expected: given listOrd: listOrd.type
21+
22+
trait Placeholder //expected: object listOrd extends Foo[String]

scaladoc-testcases/src/tests/inheritedMembers1.scala

+5-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,11 @@ class A
1010
= ???
1111
object X
1212
trait Z
13-
given B with {}
13+
given B with { val x = 1 }//expected: given given_B: given_B.type
14+
trait Placeholder//expected: object given_B extends B
15+
16+
object Y extends Z
17+
1418
type I = Int
1519
/*<-*/extension (a: A) /*->*/def extension: String
1620
= ???
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
package tests
2+
package slashMembers
3+
4+
class A
5+
6+
trait \/
7+
8+
trait /\

scaladoc/src/dotty/tools/scaladoc/ScaladocSettings.scala

+1-5
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,8 @@ import dotty.tools.dotc.core.Contexts._
1818

1919
class ScaladocSettings extends SettingGroup with AllScalaSettings:
2020
val unsupportedSettings = Seq(
21-
// Options that we like to support
22-
extdirs, javabootclasspath, encoding,
2321
// Needed for plugin architecture
24-
plugin,disable,require, pluginsDir, pluginOptions,
25-
// we need support for sourcepath and sourceroot
26-
sourcepath, sourceroot
22+
plugin, disable, require, pluginsDir, pluginOptions,
2723
)
2824

2925

scaladoc/src/dotty/tools/scaladoc/api.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ enum Kind(val name: String):
6767
case Exported(m: Kind.Def) extends Kind("export")
6868
case Type(concreate: Boolean, opaque: Boolean, typeParams: Seq[TypeParameter])
6969
extends Kind("type") // should we handle opaque as modifier?
70-
case Given(kind: Def | Class, as: Option[Signature], conversion: Option[ImplicitConversion])
70+
case Given(kind: Def | Class | Val.type, as: Option[Signature], conversion: Option[ImplicitConversion])
7171
extends Kind("given") with ImplicitConversionProvider
7272
case Implicit(kind: Kind.Def | Kind.Val.type, conversion: Option[ImplicitConversion])
7373
extends Kind(kind.name) with ImplicitConversionProvider

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

+1
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import java.nio.file.Path
1313
import java.nio.file.Files
1414
import java.io.File
1515
import scala.util.matching._
16+
import dotty.tools.scaladoc.util.Escape._
1617

1718
val UnresolvedLocationLink = "#"
1819

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

+12-52
Original file line numberDiff line numberDiff line change
@@ -97,15 +97,16 @@ trait ClassLikeSupport:
9797
val baseMember = mkMember(classDef.symbol, kindForClasslike(classDef), selfSignature)(
9898
modifiers = modifiers,
9999
graph = graph,
100-
deprecated = classDef.symbol.isDeprecated()
100+
deprecated = classDef.symbol.isDeprecated(),
101+
).copy(
102+
directParents = classDef.getParentsAsLinkToTypes,
103+
parents = supertypes,
101104
)
102105

103106
if summon[DocContext].args.generateInkuire then doInkuireStuff(classDef)
104107

105108
if signatureOnly then baseMember else baseMember.copy(
106109
members = classDef.extractPatchedMembers.sortBy(m => (m.name, m.kind.name)),
107-
directParents = classDef.getParentsAsLinkToTypes,
108-
parents = supertypes,
109110
selfType = selfType,
110111
companion = classDef.getCompanion
111112
)
@@ -144,15 +145,6 @@ trait ClassLikeSupport:
144145
)
145146
parseMethod(c, dd.symbol,specificKind = Kind.Extension(target, _))
146147
}
147-
// TODO check given methods?
148-
case dd: DefDef if !dd.symbol.isHiddenByVisibility && dd.symbol.isGiven && !dd.symbol.isArtifact =>
149-
Some(dd.symbol.owner.typeMember(dd.name))
150-
.filterNot(_.exists)
151-
.map { _ =>
152-
parseMethod(c, dd.symbol, specificKind =
153-
Kind.Given(_, getGivenInstance(dd), None)
154-
)
155-
}
156148

157149
case dd: DefDef if !dd.symbol.isHiddenByVisibility && dd.symbol.isExported && !dd.symbol.isArtifact =>
158150
val exportedTarget = dd.rhs.collect {
@@ -171,58 +163,25 @@ trait ClassLikeSupport:
171163
Some(parseMethod(c, dd.symbol, specificKind = Kind.Exported(_))
172164
.withOrigin(Origin.ExportedFrom(s"$instanceName.$functionName", dri)))
173165

174-
case dd: DefDef if !dd.symbol.isHiddenByVisibility && !dd.symbol.isGiven && !dd.symbol.isSyntheticFunc && !dd.symbol.isExtensionMethod && !dd.symbol.isArtifact =>
166+
case dd: DefDef if !dd.symbol.isHiddenByVisibility && !dd.symbol.isSyntheticFunc && !dd.symbol.isExtensionMethod && !dd.symbol.isArtifact =>
175167
Some(parseMethod(c, dd.symbol))
176168

177169
case td: TypeDef if !td.symbol.flags.is(Flags.Synthetic) && (!td.symbol.flags.is(Flags.Case) || !td.symbol.flags.is(Flags.Enum)) =>
178170
Some(parseTypeDef(td))
179171

180-
case vd: ValDef if !isSyntheticField(vd.symbol)
181-
&& (!vd.symbol.flags.is(Flags.Case) || !vd.symbol.flags.is(Flags.Enum))
182-
&& vd.symbol.isGiven =>
183-
val classDef = Some(vd.tpt.tpe).flatMap(_.classSymbol.map(_.tree.asInstanceOf[ClassDef]))
184-
Some(classDef.filter(_.symbol.flags.is(Flags.Module)).fold[Member](parseValDef(c, vd))(parseGivenClasslike(_)))
185-
186172
case vd: ValDef if !isSyntheticField(vd.symbol) && (!vd.symbol.flags.is(Flags.Case) || !vd.symbol.flags.is(Flags.Enum)) =>
187173
Some(parseValDef(c, vd))
188174

189-
case c: ClassDef if c.symbol.owner.methodMember(c.name).exists(_.flags.is(Flags.Given)) =>
190-
Some(parseGivenClasslike(c))
191-
192-
case c: ClassDef if c.symbol.shouldDocumentClasslike && !c.symbol.isGiven =>
175+
case c: ClassDef if c.symbol.shouldDocumentClasslike =>
193176
Some(parseClasslike(c))
194177

195178
case _ => None
196179
}
197180

198-
private def parseGivenClasslike(c: ClassDef): Member = {
199-
val parsedClasslike = parseClasslike(c)
200-
201-
val parentTpe = c.parents(0) match {
202-
case t: TypeTree => Some(t.tpe)
203-
case t: Term => Some(t.tpe)
204-
case _ => None
205-
}
206-
207-
val givenParents = parsedClasslike.directParents.headOption
208-
val cls: Kind.Class = parsedClasslike.kind match
209-
case Kind.Object => Kind.Class(Nil, Nil)
210-
case Kind.Trait(tps, args) => Kind.Class(tps, args)
211-
case cls: Kind.Class => cls
212-
case other =>
213-
report.warning("Unrecoginzed kind for given: $other", c.pos)
214-
Kind.Class(Nil, Nil)
215-
216-
parsedClasslike.withKind(
217-
Kind.Given(cls, givenParents.map(_.signature), parentTpe.flatMap(extractImplicitConversion))
218-
)
219-
}
220-
221181
private def parseInheritedMember(c: ClassDef)(s: Tree): Option[Member] =
222182
def inheritance = Some(InheritedFrom(s.symbol.owner.normalizedName, s.symbol.dri))
223183
processTreeOpt(s)(s match
224-
case c: ClassDef if c.symbol.shouldDocumentClasslike && !c.symbol.isGiven => Some(parseClasslike(c, signatureOnly = true))
225-
case c: ClassDef if c.symbol.owner.methodMember(c.name).exists(_.flags.is(Flags.Given)) => Some(parseGivenClasslike(c))
184+
case c: ClassDef if c.symbol.shouldDocumentClasslike => Some(parseClasslike(c, signatureOnly = true))
226185
case other => {
227186
val parsed = parseMember(c)(other)
228187
parsed.map(p =>
@@ -392,6 +351,7 @@ trait ClassLikeSupport:
392351
))
393352
case _ =>
394353
Kind.Implicit(basicKind, None)
354+
else if methodSymbol.flags.is(Flags.Given) then Kind.Given(basicKind, Some(method.returnTpt.tpe.asSignature), extractImplicitConversion(method.returnTpt.tpe))
395355
else specificKind(basicKind)
396356

397357
val origin = if !methodSymbol.isOverridden then Origin.RegularlyDefined else
@@ -463,10 +423,10 @@ trait ClassLikeSupport:
463423
def parseValDef(c: ClassDef, valDef: ValDef): Member =
464424
def defaultKind = if valDef.symbol.flags.is(Flags.Mutable) then Kind.Var else Kind.Val
465425
val memberInfo = unwrapMemberInfo(c, valDef.symbol)
466-
val kind = if valDef.symbol.flags.is(Flags.Implicit) then
467-
Kind.Implicit(Kind.Val, extractImplicitConversion(valDef.tpt.tpe))
468-
else if valDef.symbol.flags.is(Flags.Enum) then Kind.EnumCase(Kind.Val)
469-
else defaultKind
426+
val kind = if valDef.symbol.flags.is(Flags.Implicit) then Kind.Implicit(Kind.Val, extractImplicitConversion(valDef.tpt.tpe))
427+
else if valDef.symbol.flags.is(Flags.Given) then Kind.Given(Kind.Val, Some(memberInfo.res.asSignature), extractImplicitConversion(valDef.tpt.tpe))
428+
else if valDef.symbol.flags.is(Flags.Enum) then Kind.EnumCase(Kind.Val)
429+
else defaultKind
470430

471431
mkMember(valDef.symbol, kind, memberInfo.res.asSignature)(deprecated = valDef.symbol.isDeprecated())
472432

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

+8-8
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,14 @@ import SymOps._
99

1010
object NameNormalizer {
1111

12-
extension (using Quotes)(s: reflect.Symbol) def normalizedName: String = {
13-
import reflect.*
14-
val withoutGivenPrefix = if s.isGiven then s.name.stripPrefix("given_") else s.name
15-
val withoutObjectSuffix = if s.flags.is(Flags.Module) then withoutGivenPrefix.stripSuffix("$") else withoutGivenPrefix
16-
val constructorNormalizedName = if s.isClassConstructor then "this" else withoutObjectSuffix
17-
val escaped = escapedName(constructorNormalizedName)
18-
escaped
19-
}
12+
extension (using Quotes)(s: reflect.Symbol)
13+
def normalizedName: String = {
14+
import reflect.*
15+
val withoutObjectSuffix = if s.flags.is(Flags.Module) then s.name.stripSuffix("$") else s.name
16+
val constructorNormalizedName = if s.isClassConstructor then "this" else withoutObjectSuffix
17+
val escaped = escapedName(constructorNormalizedName)
18+
escaped
19+
}
2020

2121
private val ignoredKeywords: Set[String] = Set("this")
2222

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

+1-3
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,6 @@ object SymOps:
115115
!sym.isHiddenByVisibility
116116
&& !sym.flags.is(Flags.Synthetic)
117117
&& (!sym.flags.is(Flags.Case) || !sym.flags.is(Flags.Enum))
118-
&& !(sym.companionModule.flags.is(Flags.Given))
119118

120119
def getCompanionSymbol: Option[reflect.Symbol] = Some(sym.companionClass).filter(_.exists)
121120

@@ -249,7 +248,6 @@ class SymOpsWithLinkCache:
249248
def dri(using dctx: DocContext): DRI =
250249
import reflect.*
251250
if sym == Symbol.noSymbol then topLevelDri
252-
else if sym.isValDef && sym.moduleClass.exists then sym.moduleClass.dri
253251
else
254252
val method =
255253
if (sym.isDefDef) Some(sym)
@@ -261,7 +259,7 @@ class SymOpsWithLinkCache:
261259
else
262260
(sym.className, sym.anchor)
263261

264-
val location = sym.packageNameSplitted ++ className
262+
val location = (sym.packageNameSplitted ++ className).map(escapeFilename(_))
265263

266264
val externalLink = {
267265
import reflect._

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ trait TypesSupport:
6161
extension (on: SignaturePart) def l: List[SignaturePart] = List(on)
6262

6363
private def tpe(using Quotes)(symbol: reflect.Symbol): SSignature =
64-
val suffix = if symbol.isValDef then plain(".type").l else Nil
64+
val suffix = if symbol.isValDef || symbol.flags.is(reflect.Flags.Module) then plain(".type").l else Nil
6565
dotty.tools.scaladoc.Type(symbol.normalizedName, Some(symbol.dri)) :: suffix
6666

6767
private def commas(lists: List[SSignature]) = lists match

scaladoc/src/dotty/tools/scaladoc/translators/ScalaSignatureProvider.scala

+17-1
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ object ScalaSignatureProvider:
2222
givenClassSignature(documentable, cls, builder)
2323
case Kind.Given(d: Kind.Def, _, _) =>
2424
givenMethodSignature(documentable, d, builder)
25+
case Kind.Given(Kind.Val, _, _) =>
26+
givenValSignature(documentable, builder)
2527
case cls: Kind.Class =>
2628
classSignature(documentable, cls, builder)
2729
case enm: Kind.Enum =>
@@ -129,10 +131,24 @@ object ScalaSignatureProvider:
129131
case Kind.Given(_, Some(instance), _) =>
130132
builder.keyword("given ")
131133
.name(method.name, method.dri)
134+
.generics(body.typeParams)
135+
.functionParameters(body.argsLists)
132136
.plain(": ")
133137
.signature(instance)
134138
case _ =>
135-
builder.keyword("given ").name(method.name, method.dri)
139+
builder.keyword("given ")
140+
.name(method.name, method.dri)
141+
.generics(body.typeParams)
142+
.functionParameters(body.argsLists)
143+
144+
private def givenValSignature(field: Member, builder: SignatureBuilder): SignatureBuilder = field.kind match
145+
case Kind.Given(_, Some(instance), _) =>
146+
builder.keyword("given ")
147+
.name(field.name, field.dri)
148+
.plain(": ")
149+
.signature(instance)
150+
case _ =>
151+
builder.keyword("given ").name(field.name, field.dri)
136152

137153
private def methodSignature(method: Member, cls: Kind.Def, builder: SignatureBuilder): SignatureBuilder =
138154
val bdr = builder
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,11 @@
11
package dotty.tools.scaladoc.util
22

33
object Escape:
4-
def escapeUrl(url: String) = url.replace("#","%23")
4+
def escapeUrl(url: String) = url
5+
.replace("#","%23")
6+
7+
def escapeFilename(filename: String) =
8+
val escaped = filename
9+
.replace("/", "$div")
10+
.replace("\\", "$bslash")
11+
if escaped != filename then escaped + "$" else escaped

scaladoc/test/dotty/tools/scaladoc/ExternalLocationProviderIntegrationTest.scala

+14-4
Original file line numberDiff line numberDiff line change
@@ -25,23 +25,33 @@ class JavadocExternalLocationProviderIntegrationTest extends ExternalLocationPro
2525

2626
class Scaladoc2ExternalLocationProviderIntegrationTest extends ExternalLocationProviderIntegrationTest(
2727
"externalScaladoc2",
28-
List(".*scala.*::scaladoc2::https://www.scala-lang.org/api/current/"),
28+
List(
29+
".*scala/.*::scaladoc2::https://www.scala-lang.org/api/current/",
30+
".*externalStubs.*::scaladoc2::https://external.stubs/api/"
31+
),
2932
List(
3033
"https://www.scala-lang.org/api/current/scala/util/matching/Regex$$Match.html",
3134
"https://www.scala-lang.org/api/current/scala/Predef$.html#String",
3235
"https://www.scala-lang.org/api/current/scala/collection/immutable/Map.html",
3336
"https://www.scala-lang.org/api/current/scala/collection/IterableOnceOps.html#addString(b:StringBuilder,start:String,sep:String,end:String):StringBuilder",
34-
"https://www.scala-lang.org/api/current/scala/collection/IterableOnceOps.html#mkString(start:String,sep:String,end:String):String"
37+
"https://www.scala-lang.org/api/current/scala/collection/IterableOnceOps.html#mkString(start:String,sep:String,end:String):String",
38+
"https://external.stubs/api/tests/externalStubs/$div$bslash$.html",
39+
"https://external.stubs/api/tests/externalStubs/$bslash$div$.html"
3540
)
3641
)
3742

3843
class Scaladoc3ExternalLocationProviderIntegrationTest extends ExternalLocationProviderIntegrationTest(
3944
"externalScaladoc3",
40-
List(".*scala.*::scaladoc3::https://dotty.epfl.ch/api/"),
45+
List(
46+
".*scala/.*::scaladoc3::https://dotty.epfl.ch/api/",
47+
".*externalStubs.*::scaladoc3::https://external.stubs/api/"
48+
),
4149
List(
4250
"https://dotty.epfl.ch/api/scala/collection/immutable/Map.html",
4351
"https://dotty.epfl.ch/api/scala/Predef$.html#String-0",
44-
"https://dotty.epfl.ch/api/scala/util/matching/Regex$$Match.html"
52+
"https://dotty.epfl.ch/api/scala/util/matching/Regex$$Match.html",
53+
"https://external.stubs/api/tests/externalStubs/$div$bslash$.html",
54+
"https://external.stubs/api/tests/externalStubs/$bslash$div$.html"
4555
)
4656
)
4757

0 commit comments

Comments
 (0)