Skip to content

Commit 054b78d

Browse files
committed
Scaladoc: Detect if CC was imported
1 parent 3cc68a1 commit 054b78d

File tree

7 files changed

+72
-14
lines changed

7 files changed

+72
-14
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: 20 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -115,10 +115,9 @@ trait TypesSupport:
115115
++ keyword(" & ").l
116116
++ inParens(inner(right), shouldWrapInParens(right, tp, false))
117117
case ByNameType(CapturingType(tpe, refs)) =>
118-
renderCaptureArrow(using qctx)(refs, false) ++ (plain(" ") :: inner(tpe))
118+
renderFunctionArrow(using qctx)(refs, true, false) ++ (plain(" ") :: inner(tpe))
119119
case ByNameType(tpe) =>
120-
tpe.typeSymbol.pos.map(p => report.warning(s"Pure ByNameType at ${p}"))
121-
keyword("-> ") :: inner(tpe) // FIXME need to check if cc is enabled in current file first!!!
120+
(if ccEnabled then keyword("-> ") else keyword("=> ")):: inner(tpe)
122121
case ConstantType(constant) =>
123122
plain(constant.show).l
124123
case ThisType(tpe) =>
@@ -248,7 +247,8 @@ trait TypesSupport:
248247
++ plain(" ").l
249248
++ inParens(inner(rhs), shouldWrapInParens(rhs, t, false))
250249

251-
case t @ AppliedType(tpe, args) if t.isFunctionType => functionType(t, tpe, args)
250+
case t @ AppliedType(tpe, args) if t.isFunctionType =>
251+
functionType(t, tpe, args)
252252

253253
case t @ AppliedType(tpe, typeList) =>
254254
inner(tpe) ++ plain("[").l ++ commas(typeList.map { t => t match
@@ -346,7 +346,7 @@ trait TypesSupport:
346346
Some(List(CaptureDefs.captureRoot.termRef))
347347
else
348348
inCC
349-
val arrow = plain(" ") :: (renderCaptureArrow(using qctx)(refs, t.isContextFunctionType) ++ plain(" ").l)
349+
val arrow = plain(" ") :: (renderFunctionArrow(using qctx)(refs, t.isFunction1, t.isContextFunctionType) ++ plain(" ").l)
350350
given Option[List[TypeRepr]] = None // FIXME: this is ugly
351351
args match
352352
case Nil => Nil
@@ -485,17 +485,23 @@ trait TypesSupport:
485485
import reflect._
486486
Keyword("^") :: renderCaptureSet(refs)
487487

488-
private def renderCaptureArrow(using Quotes)(refs: List[reflect.TypeRepr], isImplicitFun: Boolean)(using elideThis: reflect.ClassDef): SSignature =
488+
private def renderFunctionArrow(using Quotes)(refs: List[reflect.TypeRepr], isPureFun: Boolean, isImplicitFun: Boolean)(using elideThis: reflect.ClassDef): SSignature =
489489
import reflect._
490490
val prefix = if isImplicitFun then "?" else ""
491-
refs match
492-
case Nil => List(Keyword(prefix + "->")) // FIXME need to check if cc is enabled in current file first!!!
493-
case List(ref) if ref.isCaptureRoot => List(Keyword(prefix + "=>"))
494-
case refs => Keyword(prefix + "->") :: renderCaptureSet(refs)
491+
if !ccEnabled then
492+
List(Keyword(prefix + "=>"))
493+
else
494+
refs match
495+
case Nil => if isPureFun then List(Keyword(prefix + "->")) else List(Keyword(prefix + "=>"))
496+
case List(ref) if ref.isCaptureRoot => List(Keyword(prefix + "=>"))
497+
case refs => Keyword(prefix + "->") :: renderCaptureSet(refs)
495498

496-
private def renderCaptureArrow(using qctx: Quotes)(refs: Option[List[reflect.TypeRepr]], isImplicitFun: Boolean)(using elideThis: reflect.ClassDef): SSignature =
499+
private def renderFunctionArrow(using qctx: Quotes)(refs: Option[List[reflect.TypeRepr]], isPureFun: Boolean, isImplicitFun: Boolean)(using elideThis: reflect.ClassDef): SSignature =
497500
import reflect._
498501
val prefix = if isImplicitFun then "?" else ""
499-
refs match
500-
case None => List(Keyword(prefix + "->")) // FIXME need to check if cc is enabled in current file first!!!
501-
case Some(refs) => renderCaptureArrow(using qctx)(refs, isImplicitFun)
502+
if !ccEnabled then
503+
List(Keyword(prefix + "=>"))
504+
else
505+
refs match
506+
case None => if isPureFun then List(Keyword(prefix + "->")) else List(Keyword(prefix + "=>"))
507+
case Some(refs) => renderFunctionArrow(using qctx)(refs, isPureFun, isImplicitFun)

0 commit comments

Comments
 (0)