Skip to content

Commit 85af83d

Browse files
committed
Scaladoc: Detect if CC was imported
1 parent 7658cba commit 85af83d

File tree

7 files changed

+76
-16
lines changed

7 files changed

+76
-16
lines changed

local/project/dummy/arrows.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package dummy
22

33
import language.experimental.captureChecking
4+
import language.experimental.pureFunctions
45
import caps.*
56

67
trait Nested:

local/project/dummy/nocc.scala

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
package dummy
2+
3+
trait NoCaptureChecking:
4+
def byName(f: => Int): Int
5+
def impure(f: Int => Int): Int
6+
def context(f: Int ?=> Int): Int

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ enum Modifier(val name: String, val prefix: Boolean):
4444
case Transparent extends Modifier("transparent", true)
4545
case Infix extends Modifier("infix", true)
4646
case AbsOverride extends Modifier("abstract override", true)
47+
case Mut extends Modifier("mut", true)
4748

4849
case class ExtensionTarget(name: String, typeParams: Seq[TypeParameter], argsLists: Seq[TermParameterList], signature: Signature, dri: DRI, position: Long)
4950
case class ImplicitConversion(from: DRI, to: DRI)

scaladoc/src/dotty/tools/scaladoc/cc/CaptureOps.scala

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,20 @@ object CaptureDefs:
3636
qctx.reflect.Symbol.requiredClass("scala.annotation.internal.readOnlyCapability")
3737
def RequiresCapabilityAnnot(using qctx: Quotes) =
3838
qctx.reflect.Symbol.requiredClass("scala.annotation.internal.requiresCapability")
39+
40+
def LanguageExperimental(using qctx: Quotes) =
41+
qctx.reflect.Symbol.requiredPackage("scala.language.experimental")
42+
43+
def ImpureFunction1(using qctx: Quotes) =
44+
qctx.reflect.Symbol.requiredClass("scala.ImpureFunction1")
45+
46+
def ImpureContextFunction1(using qctx: Quotes) =
47+
qctx.reflect.Symbol.requiredClass("scala.ImpureContextFunction1")
48+
49+
def Function1(using qctx: Quotes) =
50+
qctx.reflect.Symbol.requiredClass("scala.Function1")
51+
52+
val ccImportSelector = "captureChecking"
3953
end CaptureDefs
4054

4155
extension (using qctx: Quotes)(ann: qctx.reflect.Symbol)
@@ -53,8 +67,28 @@ end extension
5367

5468
extension (using qctx: Quotes)(tpe: qctx.reflect.TypeRepr)
5569
def isCaptureRoot: Boolean = tpe.termSymbol == CaptureDefs.captureRoot
70+
71+
def isImpureFunction1: Boolean = tpe.derivesFrom(CaptureDefs.ImpureFunction1)
72+
73+
def isImpureContextFunction1: Boolean = tpe.derivesFrom(CaptureDefs.ImpureContextFunction1)
74+
75+
def isFunction1: Boolean = tpe.derivesFrom(CaptureDefs.Function1)
5676
end extension
5777

78+
/** Matches `import scala.language.experimental.captureChecking` */
79+
object CCImport:
80+
def unapply(using qctx: Quotes)(tree: qctx.reflect.Tree): Boolean =
81+
import qctx.reflect._
82+
tree match
83+
case imprt: Import if imprt.expr.tpe.termSymbol == CaptureDefs.LanguageExperimental =>
84+
imprt.selectors.exists {
85+
case SimpleSelector(s) if s == CaptureDefs.ccImportSelector => true
86+
case _ => false
87+
}
88+
case _ => false
89+
end unapply
90+
end CCImport
91+
5892
object ReachCapability:
5993
def unapply(using qctx: Quotes)(ty: qctx.reflect.TypeRepr): Option[qctx.reflect.TypeRepr] =
6094
import qctx.reflect._

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

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ import scala.jdk.CollectionConverters._
55

66
import SymOps._
77

8+
import dotty.tools.scaladoc.cc.CCImport
9+
810
trait PackageSupport:
911
self: TastyParser =>
1012
import qctx.reflect._
@@ -13,6 +15,11 @@ trait PackageSupport:
1315

1416
def parsePackage(pck: PackageClause): (String, Member) =
1517
val name = pck.symbol.fullName
18+
ccFlag = false // FIXME: would be better if we had access to the tasty attribute
19+
pck.stats.foreach {
20+
case CCImport() => ccFlag = true
21+
case _ =>
22+
}
1623
(name, Member(name, "", pck.symbol.dri, Kind.Package))
1724

1825
def parsePackageObject(pckObj: ClassDef): (String, Member) =

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,9 @@ case class TastyParser(
187187

188188
private given qctx.type = qctx
189189

190+
protected var ccFlag: Boolean = false
191+
def ccEnabled: Boolean = ccFlag
192+
190193
val intrinsicClassDefs = Set(
191194
defn.AnyClass,
192195
defn.MatchableClass,

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

Lines changed: 24 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ import dotty.tools.scaladoc.cc.*
1111
import NameNormalizer._
1212
import SyntheticsSupport._
1313

14+
private case class FunKind(isPure: Boolean, isImplicit: Boolean)
15+
1416
trait TypesSupport:
1517
self: TastyParser =>
1618

@@ -122,10 +124,9 @@ trait TypesSupport:
122124
++ keyword(" & ").l
123125
++ inParens(inner(right, skipThisTypePrefix), shouldWrapInParens(right, tp, false))
124126
case ByNameType(CapturingType(tpe, refs)) =>
125-
renderCaptureArrow(using q)(refs, false, skipThisTypePrefix) ++ (plain(" ") :: inner(tpe, skipThisTypePrefix))
127+
renderFunctionArrow(using q)(refs, FunKind(true, false), skipThisTypePrefix) ++ (plain(" ") :: inner(tpe, skipThisTypePrefix))
126128
case ByNameType(tpe) =>
127-
tpe.typeSymbol.pos.map(p => report.warning(s"Pure ByNameType at ${p}"))
128-
keyword("-> ") :: inner(tpe, skipThisTypePrefix) // FIXME need to check if cc is enabled in current file first!!!
129+
(if ccEnabled then keyword("-> ") else keyword("=> ")):: inner(tpe, skipThisTypePrefix)
129130
case ConstantType(constant) =>
130131
plain(constant.show).l
131132
case ThisType(tpe) =>
@@ -256,7 +257,8 @@ trait TypesSupport:
256257
++ plain(" ").l
257258
++ inParens(inner(rhs, skipThisTypePrefix), shouldWrapInParens(rhs, t, false))
258259

259-
case t @ AppliedType(tpe, args) if t.isFunctionType => functionType(t, tpe, args, skipThisTypePrefix)
260+
case t @ AppliedType(tpe, args) if t.isFunctionType =>
261+
functionType(t, tpe, args, skipThisTypePrefix)
260262

261263
case t @ AppliedType(tpe, typeList) =>
262264
inner(tpe, skipThisTypePrefix) ++ plain("[").l ++ commas(typeList.map { t => t match
@@ -356,7 +358,7 @@ trait TypesSupport:
356358
Some(List(CaptureDefs.captureRoot.termRef))
357359
else
358360
inCC
359-
val arrow = plain(" ") :: (renderCaptureArrow(using q)(refs, t.isContextFunctionType, skipThisTypePrefix) ++ plain(" ").l)
361+
val arrow = plain(" ") :: (renderFunctionArrow(using q)(refs, FunKind(isPure = t.isFunction1, isImplicit = t.isContextFunctionType), skipThisTypePrefix) ++ plain(" ").l)
360362
given Option[List[TypeRepr]] = None // FIXME: this is ugly
361363
args match
362364
case Nil => Nil
@@ -505,21 +507,27 @@ trait TypesSupport:
505507
import reflect._
506508
Keyword("^") :: renderCaptureSet(refs, skipThisTypePrefix)
507509

508-
private def renderCaptureArrow(using q: Quotes)(refs: List[reflect.TypeRepr], isImplicitFun: Boolean, skipThisTypePrefix: Boolean)(
510+
private def renderFunctionArrow(using q: Quotes)(refs: List[reflect.TypeRepr], fun: FunKind, skipThisTypePrefix: Boolean)(
509511
using elideThis: reflect.ClassDef, originalOwner: reflect.Symbol
510512
): SSignature =
511513
import reflect._
512-
val prefix = if isImplicitFun then "?" else ""
513-
refs match
514-
case Nil => List(Keyword(prefix + "->")) // FIXME need to check if cc is enabled in current file first!!!
515-
case List(ref) if ref.isCaptureRoot => List(Keyword(prefix + "=>"))
516-
case refs => Keyword(prefix + "->") :: renderCaptureSet(using q)(refs, skipThisTypePrefix)
514+
val prefix = if fun.isImplicit then "?" else ""
515+
if !ccEnabled then
516+
List(Keyword(prefix + "=>"))
517+
else
518+
refs match
519+
case Nil => if fun.isPure then List(Keyword(prefix + "->")) else List(Keyword(prefix + "=>"))
520+
case List(ref) if ref.isCaptureRoot => List(Keyword(prefix + "=>"))
521+
case refs => Keyword(prefix + "->") :: renderCaptureSet(using q)(refs, skipThisTypePrefix)
517522

518-
private def renderCaptureArrow(using q: Quotes)(refs: Option[List[reflect.TypeRepr]], isImplicitFun: Boolean, skipThisTypePrefix: Boolean)(
523+
private def renderFunctionArrow(using q: Quotes)(refs: Option[List[reflect.TypeRepr]], fun: FunKind, skipThisTypePrefix: Boolean)(
519524
using elideThis: reflect.ClassDef, originalOwner: reflect.Symbol
520525
): SSignature =
521526
import reflect._
522-
val prefix = if isImplicitFun then "?" else ""
523-
refs match
524-
case None => List(Keyword(prefix + "->")) // FIXME need to check if cc is enabled in current file first!!!
525-
case Some(refs) => renderCaptureArrow(using q)(refs, isImplicitFun, skipThisTypePrefix)
527+
val prefix = if fun.isImplicit then "?" else ""
528+
if !ccEnabled then
529+
List(Keyword(prefix + "=>"))
530+
else
531+
refs match
532+
case None => if fun.isPure then List(Keyword(prefix + "->")) else List(Keyword(prefix + "=>"))
533+
case Some(refs) => renderFunctionArrow(using q)(refs, fun, skipThisTypePrefix)

0 commit comments

Comments
 (0)