Skip to content

Commit 55025c2

Browse files
authored
Scaladoc: improve refined function types rendering (#20333)
Fixes #19967 Improves rendering of refined function types - unneeded parameters names are omitted when possible
1 parent 6d29951 commit 55025c2

File tree

5 files changed

+80
-9
lines changed

5 files changed

+80
-9
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
package tests
2+
package refinedFunctionTypes
3+
4+
import annotation.experimental
5+
6+
@experimental
7+
infix type $throws[R, +E <: Exception] = CanThrow[E] ?=> R
8+
9+
@experimental
10+
infix type $throws2[+E <: Exception] = (c: CanThrow[E]) ?=> c.type
11+
12+
@experimental
13+
infix type $throws3[+E <: Exception] = [T] => (c: CanThrow[E]) ?=> c.type
14+
15+
@experimental
16+
infix type $throws4[+E <: Exception] = [T] => (c: CanThrow[E]) ?=> T //expected: infix type $throws4[+E <: Exception] = [T] => CanThrow[E] ?=> T
17+
18+
type TA1 = (a: Int, b: (Boolean, String)) => List[(a.type, b.type)]
19+
20+
type TA2 = (a: Int, b: (Boolean, String)) ?=> List[Boolean]
21+
22+
@experimental
23+
type TB0 = [R, E <: Exception] =>> PolyFunction { def apply[T](c: CanThrow[E]): R; } //expected: type TB0[R, E <: Exception] = [T] => CanThrow[E] => R
24+
25+
@experimental
26+
type TB1 = [R, E <: Exception] =>> PolyFunction { def apply[T](c: CanThrow[E], y: c.type): R; } //expected: type TB1[R, E <: Exception] = [T] => (c: CanThrow[E], y: c.type) => R
27+
28+
@experimental
29+
type TB2 = [R, E <: Exception] =>> PolyFunction { def apply[T](using c: CanThrow[E]): c.type; } //expected: type TB2[R, E <: Exception] = [T] => (c: CanThrow[E]) ?=> c.type
30+
31+
type TC1 = [T] => (a: T) => T //expected: type TC1 = [T] => T => T
32+
33+
type TC2 = [T] => (a: T) ?=> T //expected: type TC2 = [T] => T ?=> T
34+
35+
type TC3 = [T] => (a: T) => a.type
36+
37+
type TC4 = [T] => (a: T) ?=> a.type

scaladoc-testcases/src/tests/thisType.scala

+2-2
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,5 @@ package thisType
44
// issue 16024
55

66
class X[Map[_, _[_]]]:
7-
inline def map[F[_]](f: [t] => t => F[t]): Map[this.type, F] = //expected: inline def map[F[_]](f: [t] => (x$1: t) => F[t]): Map[this.type, F]
8-
???
7+
inline def map[F[_]](f: [t] => t => F[t]): Map[this.type, F]
8+
= ???

scaladoc-testcases/src/tests/typesSignatures.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ class Base
2828
// Tests do not support multiline signatures
2929
type Elem[X] = X match { case String => Char case Array[t] => t case Iterable[t] => t }
3030

31-
type F = [X] => (x: X) => List[X]
31+
type F = [X] => (x: X) => List[X] //expected: type F = [X] => X => List[X]
3232

3333
type G = Int => Int
3434

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

+38-6
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ package tasty
44
import scala.jdk.CollectionConverters._
55

66
import scala.quoted._
7+
import scala.util.control.NonFatal
78

89
import NameNormalizer._
910
import SyntheticsSupport._
@@ -124,6 +125,12 @@ trait TypesSupport:
124125
++ keyword(" =>> ").l
125126
++ inner(resType)
126127

128+
case Refinement(parent, "apply", mt : MethodType) if isPolyOrEreased(parent) =>
129+
val isCtx = isContextualMethod(mt)
130+
val sym = defn.FunctionClass(mt.paramTypes.length, isCtx)
131+
val at = sym.typeRef.appliedTo(mt.paramTypes :+ mt.resType)
132+
inner(Refinement(at, "apply", mt))
133+
127134
case r: Refinement => { //(parent, name, info)
128135
def getRefinementInformation(t: TypeRepr): List[TypeRepr] = t match {
129136
case r: Refinement => getRefinementInformation(r.parent) :+ r
@@ -164,16 +171,22 @@ trait TypesSupport:
164171
case t: PolyType =>
165172
val paramBounds = getParamBounds(t)
166173
val method = t.resType.asInstanceOf[MethodType]
167-
val paramList = getParamList(method)
168-
val resType = inner(method.resType)
169-
plain("[").l ++ paramBounds ++ plain("]").l ++ keyword(" => ").l ++ paramList ++ keyword(" => ").l ++ resType
174+
val rest = parseDependentFunctionType(method)
175+
plain("[").l ++ paramBounds ++ plain("]").l ++ keyword(" => ").l ++ rest
170176
case other => noSupported(s"Not supported type in refinement $info")
171177
}
172178

173179
def parseDependentFunctionType(info: TypeRepr): SSignature = info match {
174180
case m: MethodType =>
175-
val paramList = getParamList(m)
176-
paramList ++ keyword(" => ").l ++ inner(m.resType)
181+
val isCtx = isContextualMethod(m)
182+
if isDependentMethod(m) then
183+
val paramList = getParamList(m)
184+
val arrow = keyword(if isCtx then " ?=> " else " => ").l
185+
val resType = inner(m.resType)
186+
paramList ++ arrow ++ resType
187+
else
188+
val sym = defn.FunctionClass(m.paramTypes.length, isCtx)
189+
inner(sym.typeRef.appliedTo(m.paramTypes :+ m.resType))
177190
case other => noSupported("Dependent function type without MethodType refinement")
178191
}
179192

@@ -213,8 +226,9 @@ trait TypesSupport:
213226
case Seq(rtpe) =>
214227
plain("()").l ++ keyword(arrow).l ++ inner(rtpe)
215228
case Seq(arg, rtpe) =>
216-
val partOfSignature = arg match
229+
val partOfSignature = stripAnnotated(arg) match
217230
case _: TermRef | _: TypeRef | _: ConstantType | _: ParamRef => inner(arg)
231+
case at: AppliedType if !isInfix(at) && !at.isFunctionType && !at.isTupleN => inner(arg)
218232
case _ => inParens(inner(arg))
219233
partOfSignature ++ keyword(arrow).l ++ inner(rtpe)
220234
case args =>
@@ -385,3 +399,21 @@ trait TypesSupport:
385399
case _ => false
386400

387401
at.args.size == 2 && (!at.typeSymbol.name.forall(isIdentifierPart) || infixAnnot)
402+
403+
private def isPolyOrEreased(using Quotes)(tr: reflect.TypeRepr) =
404+
Set("scala.PolyFunction", "scala.runtime.ErasedFunction")
405+
.contains(tr.typeSymbol.fullName)
406+
407+
private def isContextualMethod(using Quotes)(mt: reflect.MethodType) =
408+
mt.asInstanceOf[dotty.tools.dotc.core.Types.MethodType].isContextualMethod
409+
410+
private def isDependentMethod(using Quotes)(mt: reflect.MethodType) =
411+
val method = mt.asInstanceOf[dotty.tools.dotc.core.Types.MethodType]
412+
try method.isParamDependent || method.isResultDependent
413+
catch case NonFatal(_) => true
414+
415+
private def stripAnnotated(using Quotes)(tr: reflect.TypeRepr): reflect.TypeRepr =
416+
import reflect.*
417+
tr match
418+
case AnnotatedType(tr, _) => stripAnnotated(tr)
419+
case other => other

scaladoc/test/dotty/tools/scaladoc/signatures/TranslatableSignaturesTestCases.scala

+2
Original file line numberDiff line numberDiff line change
@@ -120,3 +120,5 @@ class MatchTypeTuple extends SignatureTest("matchTypeTuple", SignatureTest.all)
120120
class InfixTypes extends SignatureTest("infixTypes", SignatureTest.all)
121121

122122
class ExtendsCall extends SignatureTest("extendsCall", SignatureTest.all)
123+
124+
class RefinedFunctionTypes extends SignatureTest("refinedFunctionTypes", SignatureTest.all)

0 commit comments

Comments
 (0)