Skip to content

Commit

Permalink
Scaladoc: improve refined function types rendering (#20333)
Browse files Browse the repository at this point in the history
Fixes #19967
Improves rendering of refined function types - unneeded parameters names
are omitted when possible
[Cherry-picked 55025c2]
  • Loading branch information
Florian3k authored and WojciechMazur committed Jul 6, 2024
1 parent 94fa4f7 commit ab64751
Show file tree
Hide file tree
Showing 5 changed files with 80 additions and 9 deletions.
37 changes: 37 additions & 0 deletions scaladoc-testcases/src/tests/refinedFunctionTypes.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package tests
package refinedFunctionTypes

import annotation.experimental

@experimental
infix type $throws[R, +E <: Exception] = CanThrow[E] ?=> R

@experimental
infix type $throws2[+E <: Exception] = (c: CanThrow[E]) ?=> c.type

@experimental
infix type $throws3[+E <: Exception] = [T] => (c: CanThrow[E]) ?=> c.type

@experimental
infix type $throws4[+E <: Exception] = [T] => (c: CanThrow[E]) ?=> T //expected: infix type $throws4[+E <: Exception] = [T] => CanThrow[E] ?=> T

type TA1 = (a: Int, b: (Boolean, String)) => List[(a.type, b.type)]

type TA2 = (a: Int, b: (Boolean, String)) ?=> List[Boolean]

@experimental
type TB0 = [R, E <: Exception] =>> PolyFunction { def apply[T](c: CanThrow[E]): R; } //expected: type TB0[R, E <: Exception] = [T] => CanThrow[E] => R

@experimental
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

@experimental
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

type TC1 = [T] => (a: T) => T //expected: type TC1 = [T] => T => T

type TC2 = [T] => (a: T) ?=> T //expected: type TC2 = [T] => T ?=> T

type TC3 = [T] => (a: T) => a.type

type TC4 = [T] => (a: T) ?=> a.type
4 changes: 2 additions & 2 deletions scaladoc-testcases/src/tests/thisType.scala
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,5 @@ package thisType
// issue 16024

class X[Map[_, _[_]]]:
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]
???
inline def map[F[_]](f: [t] => t => F[t]): Map[this.type, F]
= ???
2 changes: 1 addition & 1 deletion scaladoc-testcases/src/tests/typesSignatures.scala
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ class Base
// Tests do not support multiline signatures
type Elem[X] = X match { case String => Char case Array[t] => t case Iterable[t] => t }

type F = [X] => (x: X) => List[X]
type F = [X] => (x: X) => List[X] //expected: type F = [X] => X => List[X]

type G = Int => Int

Expand Down
44 changes: 38 additions & 6 deletions scaladoc/src/dotty/tools/scaladoc/tasty/TypesSupport.scala
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ package tasty
import scala.jdk.CollectionConverters._

import scala.quoted._
import scala.util.control.NonFatal

import NameNormalizer._
import SyntheticsSupport._
Expand Down Expand Up @@ -124,6 +125,12 @@ trait TypesSupport:
++ keyword(" =>> ").l
++ inner(resType)

case Refinement(parent, "apply", mt : MethodType) if isPolyOrEreased(parent) =>
val isCtx = isContextualMethod(mt)
val sym = defn.FunctionClass(mt.paramTypes.length, isCtx)
val at = sym.typeRef.appliedTo(mt.paramTypes :+ mt.resType)
inner(Refinement(at, "apply", mt))

case r: Refinement => { //(parent, name, info)
def getRefinementInformation(t: TypeRepr): List[TypeRepr] = t match {
case r: Refinement => getRefinementInformation(r.parent) :+ r
Expand Down Expand Up @@ -164,16 +171,22 @@ trait TypesSupport:
case t: PolyType =>
val paramBounds = getParamBounds(t)
val method = t.resType.asInstanceOf[MethodType]
val paramList = getParamList(method)
val resType = inner(method.resType)
plain("[").l ++ paramBounds ++ plain("]").l ++ keyword(" => ").l ++ paramList ++ keyword(" => ").l ++ resType
val rest = parseDependentFunctionType(method)
plain("[").l ++ paramBounds ++ plain("]").l ++ keyword(" => ").l ++ rest
case other => noSupported(s"Not supported type in refinement $info")
}

def parseDependentFunctionType(info: TypeRepr): SSignature = info match {
case m: MethodType =>
val paramList = getParamList(m)
paramList ++ keyword(" => ").l ++ inner(m.resType)
val isCtx = isContextualMethod(m)
if isDependentMethod(m) then
val paramList = getParamList(m)
val arrow = keyword(if isCtx then " ?=> " else " => ").l
val resType = inner(m.resType)
paramList ++ arrow ++ resType
else
val sym = defn.FunctionClass(m.paramTypes.length, isCtx)
inner(sym.typeRef.appliedTo(m.paramTypes :+ m.resType))
case other => noSupported("Dependent function type without MethodType refinement")
}

Expand Down Expand Up @@ -213,8 +226,9 @@ trait TypesSupport:
case Seq(rtpe) =>
plain("()").l ++ keyword(arrow).l ++ inner(rtpe)
case Seq(arg, rtpe) =>
val partOfSignature = arg match
val partOfSignature = stripAnnotated(arg) match
case _: TermRef | _: TypeRef | _: ConstantType | _: ParamRef => inner(arg)
case at: AppliedType if !isInfix(at) && !at.isFunctionType && !at.isTupleN => inner(arg)
case _ => inParens(inner(arg))
partOfSignature ++ keyword(arrow).l ++ inner(rtpe)
case args =>
Expand Down Expand Up @@ -385,3 +399,21 @@ trait TypesSupport:
case _ => false

at.args.size == 2 && (!at.typeSymbol.name.forall(isIdentifierPart) || infixAnnot)

private def isPolyOrEreased(using Quotes)(tr: reflect.TypeRepr) =
Set("scala.PolyFunction", "scala.runtime.ErasedFunction")
.contains(tr.typeSymbol.fullName)

private def isContextualMethod(using Quotes)(mt: reflect.MethodType) =
mt.asInstanceOf[dotty.tools.dotc.core.Types.MethodType].isContextualMethod

private def isDependentMethod(using Quotes)(mt: reflect.MethodType) =
val method = mt.asInstanceOf[dotty.tools.dotc.core.Types.MethodType]
try method.isParamDependent || method.isResultDependent
catch case NonFatal(_) => true

private def stripAnnotated(using Quotes)(tr: reflect.TypeRepr): reflect.TypeRepr =
import reflect.*
tr match
case AnnotatedType(tr, _) => stripAnnotated(tr)
case other => other
Original file line number Diff line number Diff line change
Expand Up @@ -120,3 +120,5 @@ class MatchTypeTuple extends SignatureTest("matchTypeTuple", SignatureTest.all)
class InfixTypes extends SignatureTest("infixTypes", SignatureTest.all)

class ExtendsCall extends SignatureTest("extendsCall", SignatureTest.all)

class RefinedFunctionTypes extends SignatureTest("refinedFunctionTypes", SignatureTest.all)

0 comments on commit ab64751

Please sign in to comment.