From 73a7ac51a59c53843f6a8e8df29b6268cfac9e72 Mon Sep 17 00:00:00 2001
From: katrinafyi <39479354+katrinafyi@users.noreply.github.com>
Date: Tue, 12 Aug 2025 18:07:08 +1000
Subject: [PATCH 1/3] scaladoc: indicate optional parameters with `= ...`
(#23676)
currently, there is no indication in the scaladoc when parameters may be
optional. this leads to [long and overwhelming signatures][1] which is
not at all user-friendly. users are forced to first intuit that this
method *might* have some optional parameters, then manually find [the
source code][2] to learn which parameters are optional.
[1]:
https://javadoc.io/static/com.lihaoyi/os-lib_3/0.11.5/os/proc.html#call-fffff910
[2]:
https://github.com/com-lihaoyi/os-lib/blob/0.11.5/os/src/ProcessOps.scala#L192-L205
this PR suffixes `= ...` after the type signature of optional
parameters. this makes it possible to tell that these parameters are
optional and may be omitted.
this applies to both method parameters and class parameters. the format
is intentionally similar to the definition of such optional parameters
in code:
```scala
// new scaladoc display:
def f(x: Int, s: String = ...): Nothing
// code:
def f(x: Int, s: String = "a"): Nothing
```
of course, the `...` term is different. i think this is a reasonable
choice because (1) ellipsis commonly represents something present but
omitted, and (2) it is not valid Scala, so there is no risk someone will
think this is denoting a literal default value of `...`. a proper
ellipsis character (rather than 3 periods) could also be considered, but
i found that looked out of place amongst the monospace signature.
about displaying the default value itself, this PR does not display the
default value. this is because of anticipated difficulties around
displaying an expression. this could be re-visited in future, but i
think it should not hold up this PR. i believe that this PR alone is
already a substantial improvement for the documentation of optional
parameters.
finally, here is a screenshot of the scaladoc from the new
optionalParams.scala test case:
this might be related to https://github.com/scala/scala3/issues/13424
---
.../src/tests/extendsCall.scala | 2 +-
.../src/tests/optionalParams.scala | 23 +++++++++++++++++++
.../scaladoc/tasty/ClassLikeSupport.scala | 3 ++-
.../tools/scaladoc/tasty/TypesSupport.scala | 4 ++--
.../TranslatableSignaturesTestCases.scala | 2 ++
5 files changed, 30 insertions(+), 4 deletions(-)
create mode 100644 scaladoc-testcases/src/tests/optionalParams.scala
diff --git a/scaladoc-testcases/src/tests/extendsCall.scala b/scaladoc-testcases/src/tests/extendsCall.scala
index b90af8162e15..3ccd70de4216 100644
--- a/scaladoc-testcases/src/tests/extendsCall.scala
+++ b/scaladoc-testcases/src/tests/extendsCall.scala
@@ -3,4 +3,4 @@ package extendsCall
class Impl() extends Base(Seq.empty, c = "-") //expected: class Impl() extends Base
-class Base(val a: Seq[String], val b: String = "", val c: String = "") //expected: class Base(val a: Seq[String], val b: String, val c: String)
+class Base(val a: Seq[String], val b: String = "", val c: String = "") //expected: class Base(val a: Seq[String], val b: String = ..., val c: String = ...)
diff --git a/scaladoc-testcases/src/tests/optionalParams.scala b/scaladoc-testcases/src/tests/optionalParams.scala
new file mode 100644
index 000000000000..551e14f7f811
--- /dev/null
+++ b/scaladoc-testcases/src/tests/optionalParams.scala
@@ -0,0 +1,23 @@
+package tests
+package optionalParams
+
+class C(val a: Seq[String], val b: String = "", var c: String = "") //expected: class C(val a: Seq[String], val b: String = ..., var c: String = ...)
+{
+ def m(x: Int, s: String = "a"): Nothing //expected: def m(x: Int, s: String = ...): Nothing
+ = ???
+}
+
+def f(x: Int, s: String = "a"): Nothing //expected: def f(x: Int, s: String = ...): Nothing
+ = ???
+
+extension (y: Int)
+ def ext(x: Int = 0): Int //expected: def ext(x: Int = ...): Int
+ = 0
+
+def byname(s: => String = "a"): Int //expected: def byname(s: => String = ...): Int
+ = 0
+
+enum E(val x: Int = 0) //expected: enum E(val x: Int = ...)
+{
+ case E1(y: Int = 10) extends E(y) //expected: final case class E1(y: Int = ...) extends E
+}
diff --git a/scaladoc/src/dotty/tools/scaladoc/tasty/ClassLikeSupport.scala b/scaladoc/src/dotty/tools/scaladoc/tasty/ClassLikeSupport.scala
index 4514cb42c9c3..840ec0ac4c0b 100644
--- a/scaladoc/src/dotty/tools/scaladoc/tasty/ClassLikeSupport.scala
+++ b/scaladoc/src/dotty/tools/scaladoc/tasty/ClassLikeSupport.scala
@@ -443,12 +443,13 @@ trait ClassLikeSupport:
val inlinePrefix = if argument.symbol.flags.is(Flags.Inline) then "inline " else ""
val nameIfNotSynthetic = Option.when(!argument.symbol.flags.is(Flags.Synthetic))(argument.symbol.normalizedName)
val name = argument.symbol.normalizedName
+ val defaultValue = Option.when(argument.symbol.flags.is(Flags.HasDefault))(Plain(" = ..."))
api.TermParameter(
argument.symbol.getAnnotations(),
inlinePrefix + prefix(argument.symbol),
nameIfNotSynthetic,
argument.symbol.dri,
- memberInfo.get(name).fold(argument.tpt.asSignature(classDef))(_.asSignature(classDef)),
+ memberInfo.get(name).fold(argument.tpt.asSignature(classDef))(_.asSignature(classDef)) :++ defaultValue,
isExtendedSymbol,
isGrouped
)
diff --git a/scaladoc/src/dotty/tools/scaladoc/tasty/TypesSupport.scala b/scaladoc/src/dotty/tools/scaladoc/tasty/TypesSupport.scala
index 8cef8af12604..1d5aa4e3a43a 100644
--- a/scaladoc/src/dotty/tools/scaladoc/tasty/TypesSupport.scala
+++ b/scaladoc/src/dotty/tools/scaladoc/tasty/TypesSupport.scala
@@ -233,7 +233,7 @@ trait TypesSupport:
tpe(tp.typeSymbol)
case _: TermRef | _: ParamRef =>
val suffix = if tp.typeSymbol == Symbol.noSymbol then tpe(typeName).l else tpe(tp.typeSymbol)
- inner(qual)(using skipTypeSuffix = true) ++ plain(".").l ++ suffix
+ inner(qual)(using indent = indent, skipTypeSuffix = true) ++ plain(".").l ++ suffix
case ThisType(tr) =>
findSupertype(elideThis, tr.typeSymbol) match
case Some((sym, AppliedType(tr2, args))) =>
@@ -250,7 +250,7 @@ trait TypesSupport:
val sig = inParens(inner(qual)(using indent = indent, skipTypeSuffix = true), wrapping)
sig ++ plain(".").l ++ tpe(tp.typeSymbol)
case _ =>
- val sig = inParens(inner(qual, skipThisTypePrefix), wrapping)
+ val sig = inParens(inner(qual), wrapping)
sig ++ keyword("#").l ++ tpe(tp.typeSymbol)
}
diff --git a/scaladoc/test/dotty/tools/scaladoc/signatures/TranslatableSignaturesTestCases.scala b/scaladoc/test/dotty/tools/scaladoc/signatures/TranslatableSignaturesTestCases.scala
index 78de0ce67124..5469c06c8eb1 100644
--- a/scaladoc/test/dotty/tools/scaladoc/signatures/TranslatableSignaturesTestCases.scala
+++ b/scaladoc/test/dotty/tools/scaladoc/signatures/TranslatableSignaturesTestCases.scala
@@ -124,3 +124,5 @@ class InfixTypes extends SignatureTest("infixTypes", SignatureTest.all)
class ExtendsCall extends SignatureTest("extendsCall", SignatureTest.all)
class RightAssocExtension extends SignatureTest("rightAssocExtension", SignatureTest.all)
+
+class OptionalParams extends SignatureTest("optionalParams", SignatureTest.all)
From 1e637e9356535ecb3db530c75eda326382d1891a Mon Sep 17 00:00:00 2001
From: Tomasz Godzik
Date: Tue, 12 Aug 2025 20:34:45 +0200
Subject: [PATCH 2/3] scaladoc: indicate optional parameters with `= ...`
(#23676)
currently, there is no indication in the scaladoc when parameters may be
optional. this leads to [long and overwhelming signatures][1] which is
not at all user-friendly. users are forced to first intuit that this
method *might* have some optional parameters, then manually find [the
source code][2] to learn which parameters are optional.
[1]:
https://javadoc.io/static/com.lihaoyi/os-lib_3/0.11.5/os/proc.html#call-fffff910
[2]:
https://github.com/com-lihaoyi/os-lib/blob/0.11.5/os/src/ProcessOps.scala#L192-L205
this PR suffixes `= ...` after the type signature of optional
parameters. this makes it possible to tell that these parameters are
optional and may be omitted.
this applies to both method parameters and class parameters. the format
is intentionally similar to the definition of such optional parameters
in code:
```scala
// new scaladoc display:
def f(x: Int, s: String = ...): Nothing
// code:
def f(x: Int, s: String = "a"): Nothing
```
of course, the `...` term is different. i think this is a reasonable
choice because (1) ellipsis commonly represents something present but
omitted, and (2) it is not valid Scala, so there is no risk someone will
think this is denoting a literal default value of `...`. a proper
ellipsis character (rather than 3 periods) could also be considered, but
i found that looked out of place amongst the monospace signature.
about displaying the default value itself, this PR does not display the
default value. this is because of anticipated difficulties around
displaying an expression. this could be re-visited in future, but i
think it should not hold up this PR. i believe that this PR alone is
already a substantial improvement for the documentation of optional
parameters.
finally, here is a screenshot of the scaladoc from the new
optionalParams.scala test case:
this might be related to https://github.com/scala/scala3/issues/13424
[Cherry-picked d2404688091a6a40b80e83df72072efa4fea8f22][modified]
From c7d96898163dcb717fe7e62e077dbc84c8527fde Mon Sep 17 00:00:00 2001
From: Tomasz Godzik
Date: Tue, 12 Aug 2025 20:49:46 +0200
Subject: [PATCH 3/3] bugfix: Fix tests using newer given syntax
---
tests/neg/i19414.scala | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/tests/neg/i19414.scala b/tests/neg/i19414.scala
index 8843441e81f2..bb275ad943b7 100644
--- a/tests/neg/i19414.scala
+++ b/tests/neg/i19414.scala
@@ -9,7 +9,7 @@ class Printer
given Writer[JsValue] = ???
given Writer[JsObject] = ???
-given [B: Writer] => (printer: Printer = new Printer) => BodySerializer[B] = ???
+given [B: Writer](using printer: Printer = new Printer): BodySerializer[B] = ???
def f: Unit =
summon[BodySerializer[JsObject]] // error: Ambiguous given instances