summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Analyzer.scala1
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/AnalyzerPlugins.scala225
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Contexts.scala2
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Infer.scala4
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Namers.scala28
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Typers.scala33
-rw-r--r--src/continuations/plugin/scala/tools/selectivecps/CPSAnnotationChecker.scala31
-rw-r--r--src/continuations/plugin/scala/tools/selectivecps/SelectiveCPSPlugin.scala1
-rw-r--r--src/reflect/scala/reflect/internal/AnnotationCheckers.scala179
-rw-r--r--test/files/run/analyzerPlugins.check197
-rw-r--r--test/files/run/analyzerPlugins.scala121
11 files changed, 691 insertions, 131 deletions
diff --git a/src/compiler/scala/tools/nsc/typechecker/Analyzer.scala b/src/compiler/scala/tools/nsc/typechecker/Analyzer.scala
index 78175f393a..b50486306d 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Analyzer.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Analyzer.scala
@@ -25,6 +25,7 @@ trait Analyzer extends AnyRef
with TypeDiagnostics
with ContextErrors
with StdAttachments
+ with AnalyzerPlugins
{
val global : Global
import global._
diff --git a/src/compiler/scala/tools/nsc/typechecker/AnalyzerPlugins.scala b/src/compiler/scala/tools/nsc/typechecker/AnalyzerPlugins.scala
new file mode 100644
index 0000000000..28f620dbb5
--- /dev/null
+++ b/src/compiler/scala/tools/nsc/typechecker/AnalyzerPlugins.scala
@@ -0,0 +1,225 @@
+/* NSC -- new Scala compiler
+ * Copyright 2005-2013 LAMP/EPFL
+ * @author Martin Odersky
+ */
+
+package scala.tools.nsc
+package typechecker
+
+/**
+ * @author Lukas Rytz
+ * @version 1.0
+ */
+trait AnalyzerPlugins { self: Analyzer =>
+ import global._
+
+
+ trait AnalyzerPlugin {
+ /**
+ * Selectively activate this analyzer plugin, e.g. according to the compiler phase.
+ *
+ * Note that the current phase can differ from the global compiler phase (look for `enteringPhase`
+ * invocations in the compiler). For instance, lazy types created by the UnPickler are completed
+ * at the phase in which their symbol is created. Observations show that this can even be the
+ * parser phase. Since symbol completion can trigger subtyping, typing etc, your plugin might
+ * need to be active also in phases other than namer and typer.
+ *
+ * Typically, this method can be implemented as
+ *
+ * global.phase.id < global.currentRun.picklerPhase.id
+ */
+ def isActive(): Boolean = true
+
+ /**
+ * Let analyzer plugins change the expected type before type checking a tree.
+ */
+ def pluginsPt(pt: Type, typer: Typer, tree: Tree, mode: Int): Type = pt
+
+ /**
+ * Let analyzer plugins modify the type that has been computed for a tree.
+ *
+ * @param tpe The type inferred by the type checker, initially (for first plugin) `tree.tpe`
+ * @param typer The yper that type checked `tree`
+ * @param tree The type-checked tree
+ * @param mode Mode that was used for typing `tree`
+ * @param pt Expected type that was used for typing `tree`
+ */
+ def pluginsTyped(tpe: Type, typer: Typer, tree: Tree, mode: Int, pt: Type): Type = tpe
+
+ /**
+ * Let analyzer plugins change the types assigned to definitions. For definitions that have
+ * an annotated type, the assigned type is obtained by typing that type tree. Otherwise, the
+ * type is inferred by typing the definition's righthand side.
+ *
+ * In order to know if the type was inferred, you can query the `wasEmpty` field in the `tpt`
+ * TypeTree of the definition (for DefDef and ValDef).
+ *
+ * (*) If the type of a method or value is inferred, the type-checked tree is stored in the
+ * `analyzer.transformed` hash map, indexed by the definition's rhs tree.
+ *
+ * NOTE: Invoking the type checker can lead to cyclic reference errors. For instance, if this
+ * method is called from the type completer of a recursive method, type checking the mehtod
+ * rhs will invoke the same completer again. It might be possible to avoid this situation by
+ * assigning `tpe` to `defTree.symbol` (untested) - the final type computed by this method
+ * will then be assigned to the definition's symbol by monoTypeCompleter (in Namers).
+ *
+ * The hooks into `typeSig` allow analyzer plugins to add annotations to (or change the types
+ * of) definition symbols. This cannot not be achieved by using `pluginsTyped`: this method
+ * is only called during type checking, so changing the type of a symbol at this point is too
+ * late: references to the symbol might already be typed and therefore obtain the the original
+ * type assigned during naming.
+ *
+ * @param defTree is the definition for which the type was computed. The different cases are
+ * outlined below. Note that this type is untyped (for methods and values with inferred type,
+ * the typed rhs trees are available in analyzer.transformed).
+ *
+ * Case defTree: Template
+ * - tpe : A ClassInfoType for the template
+ * - typer: The typer for template members, i.e. expressions and definitions of defTree.body
+ * - pt : WildcardType
+ * - the class symbol is accessible through typer.context.owner
+ *
+ * Case defTree: ClassDef
+ * - tpe : A ClassInfoType, or a PolyType(params, ClassInfoType) for polymorphic classes.
+ * The class type is the one computed by templateSig, i.e. through the above case
+ * - typer: The typer for the class. Note that this typer has a different context than the
+ * typer for the template.
+ * - pt : WildcardType
+ *
+ * Case defTree: ModuleDef
+ * - tpe : A ClassInfoType computed by templateSig
+ * - typer: The typer for the module. context.owner of this typer is the module class symbol
+ * - pt : WildcardType
+ *
+ * Case defTree: DefDef
+ * - tpe : The type of the method (MethodType, PolyType or NullaryMethodType). (*)
+ * - typer: The typer the rhs of this method
+ * - pt : If tpt.isEmpty, either the result type from the overridden method, or WildcardType.
+ * Otherwise the type obtained from typing tpt.
+ * - Note that for constructors, pt is the class type which the constructor creates. To type
+ * check the rhs of the constructor however, the expected type has to be WildcardType (see
+ * Typers.typedDefDef)
+ *
+ * Case defTree: ValDef
+ * - tpe : The type of this value. (*)
+ * - typer: The typer for the rhs of this value
+ * - pt : If tpt.isEmpty, WildcardType. Otherwise the type obtained from typing tpt.
+ * - Note that pluginsTypeSig might be called multiple times for the same ValDef since it is
+ * used to compute the types of the accessor methods (see `pluginsTypeSigAccessor`)
+ *
+ * Case defTree: TypeDef
+ * - tpe : The type obtained from typing rhs (PolyType if the TypeDef defines a polymorphic type)
+ * - typer: The typer for the rhs of this type
+ * - pt : WildcardType
+ */
+ def pluginsTypeSig(tpe: Type, typer: Typer, defTree: Tree, pt: Type): Type = tpe
+
+ /**
+ * Modify the types of field accessors. The namer phase creates method types for getters and
+ * setters based on the type of the corresponding field.
+ *
+ * Note: in order to compute the method type of an accessor, the namer calls `typeSig` on the
+ * `ValDef` tree of the corresponding field. This implies that the `pluginsTypeSig` method
+ * is potentially called multiple times for the same ValDef tree.
+ *
+ * @param tpe The method type created by the namer for the accessor
+ * @param typer The typer for the ValDef (not for the rhs)
+ * @param tree The ValDef corresponding to the accessor
+ * @param sym The accessor method symbol (getter, setter, beanGetter or beanSetter)
+ */
+ def pluginsTypeSigAccessor(tpe: Type, typer: Typer, tree: ValDef, sym: Symbol): Type = tpe
+
+ /**
+ * Decide whether this analyzer plugin can adapt a tree that has an annotated type to the
+ * given type tp, taking into account the given mode (see method adapt in trait Typers).
+ */
+ def canAdaptAnnotations(tree: Tree, typer: Typer, mode: Int, pt: Type): Boolean = false
+
+ /**
+ * Adapt a tree that has an annotated type to the given type tp, taking into account the given
+ * mode (see method adapt in trait Typers).
+ *
+ * An implementation cannot rely on canAdaptAnnotations being called before. If the implementing
+ * class cannot do the adapting, it should return the tree unchanged.
+ */
+ def adaptAnnotations(tree: Tree, typer: Typer, mode: Int, pt: Type): Tree = tree
+
+ /**
+ * Modify the type of a return expression. By default, return expressions have type
+ * NothingClass.tpe.
+ *
+ * @param tpe The type of the return expression
+ * @param typer The typer that was used for typing the return tree
+ * @param tree The typed return expression tree
+ * @param pt The return type of the enclosing method
+ */
+ def pluginsTypedReturn(tpe: Type, typer: Typer, tree: Return, pt: Type): Type = tpe
+ }
+
+
+
+ /** A list of registered analyzer plugins */
+ private var analyzerPlugins: List[AnalyzerPlugin] = Nil
+
+ /** Registers a new analyzer plugin */
+ def addAnalyzerPlugin(plugin: AnalyzerPlugin) {
+ if (!analyzerPlugins.contains(plugin))
+ analyzerPlugins = plugin :: analyzerPlugins
+ }
+
+
+ /** @see AnalyzerPlugin.pluginsPt */
+ def pluginsPt(pt: Type, typer: Typer, tree: Tree, mode: Int): Type =
+ if (analyzerPlugins.isEmpty) pt
+ else analyzerPlugins.foldLeft(pt)((pt, plugin) =>
+ if (!plugin.isActive()) pt else plugin.pluginsPt(pt, typer, tree, mode))
+
+ /** @see AnalyzerPlugin.pluginsTyped */
+ def pluginsTyped(tpe: Type, typer: Typer, tree: Tree, mode: Int, pt: Type): Type = {
+ // support deprecated methods in annotation checkers
+ val annotCheckersTpe = addAnnotations(tree, tpe)
+ if (analyzerPlugins.isEmpty) annotCheckersTpe
+ else analyzerPlugins.foldLeft(annotCheckersTpe)((tpe, plugin) =>
+ if (!plugin.isActive()) tpe else plugin.pluginsTyped(tpe, typer, tree, mode, pt))
+ }
+
+ /** @see AnalyzerPlugin.pluginsTypeSig */
+ def pluginsTypeSig(tpe: Type, typer: Typer, defTree: Tree, pt: Type): Type =
+ if (analyzerPlugins.isEmpty) tpe
+ else analyzerPlugins.foldLeft(tpe)((tpe, plugin) =>
+ if (!plugin.isActive()) tpe else plugin.pluginsTypeSig(tpe, typer, defTree, pt))
+
+ /** @see AnalyzerPlugin.pluginsTypeSigAccessor */
+ def pluginsTypeSigAccessor(tpe: Type, typer: Typer, tree: ValDef, sym: Symbol): Type =
+ if (analyzerPlugins.isEmpty) tpe
+ else analyzerPlugins.foldLeft(tpe)((tpe, plugin) =>
+ if (!plugin.isActive()) tpe else plugin.pluginsTypeSigAccessor(tpe, typer, tree, sym))
+
+ /** @see AnalyzerPlugin.canAdaptAnnotations */
+ def canAdaptAnnotations(tree: Tree, typer: Typer, mode: Int, pt: Type): Boolean = {
+ // support deprecated methods in annotation checkers
+ val annotCheckersExists = global.canAdaptAnnotations(tree, mode, pt)
+ annotCheckersExists || {
+ if (analyzerPlugins.isEmpty) false
+ else analyzerPlugins.exists(plugin =>
+ plugin.isActive() && plugin.canAdaptAnnotations(tree, typer, mode, pt))
+ }
+ }
+
+ /** @see AnalyzerPlugin.adaptAnnotations */
+ def adaptAnnotations(tree: Tree, typer: Typer, mode: Int, pt: Type): Tree = {
+ // support deprecated methods in annotation checkers
+ val annotCheckersTree = global.adaptAnnotations(tree, mode, pt)
+ if (analyzerPlugins.isEmpty) annotCheckersTree
+ else analyzerPlugins.foldLeft(annotCheckersTree)((tree, plugin) =>
+ if (!plugin.isActive()) tree else plugin.adaptAnnotations(tree, typer, mode, pt))
+ }
+
+ /** @see AnalyzerPlugin.pluginsTypedReturn */
+ def pluginsTypedReturn(tpe: Type, typer: Typer, tree: Return, pt: Type): Type = {
+ val annotCheckersType = adaptTypeOfReturn(tree.expr, pt, tpe)
+ if (analyzerPlugins.isEmpty) annotCheckersType
+ else analyzerPlugins.foldLeft(annotCheckersType)((tpe, plugin) =>
+ if (!plugin.isActive()) tpe else plugin.pluginsTypedReturn(tpe, typer, tree, pt))
+ }
+}
diff --git a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala
index 579eacb08d..620665126e 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala
@@ -35,7 +35,7 @@ trait Contexts { self: Analyzer =>
val completeList = JavaLangPackage :: ScalaPackage :: PredefModule :: Nil
}
- private val startContext = {
+ private lazy val startContext = {
NoContext.make(
Template(List(), emptyValDef, List()) setSymbol global.NoSymbol setType global.NoType,
rootMirror.RootClass,
diff --git a/src/compiler/scala/tools/nsc/typechecker/Infer.scala b/src/compiler/scala/tools/nsc/typechecker/Infer.scala
index 581f9f3bfa..0fad744506 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Infer.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Infer.scala
@@ -257,8 +257,8 @@ trait Infer extends Checkable {
tp1 // @MAT aliases already handled by subtyping
}
- private val stdErrorClass = rootMirror.RootClass.newErrorClass(tpnme.ERROR)
- private val stdErrorValue = stdErrorClass.newErrorValue(nme.ERROR)
+ private lazy val stdErrorClass = rootMirror.RootClass.newErrorClass(tpnme.ERROR)
+ private lazy val stdErrorValue = stdErrorClass.newErrorValue(nme.ERROR)
/** The context-dependent inferencer part */
class Inferencer(context: Context) extends InferencerContextErrors with InferCheckable {
diff --git a/src/compiler/scala/tools/nsc/typechecker/Namers.scala b/src/compiler/scala/tools/nsc/typechecker/Namers.scala
index 833a606565..c728185d4e 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Namers.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Namers.scala
@@ -755,10 +755,9 @@ trait Namers extends MethodSynthesis {
def accessorTypeCompleter(tree: ValDef, isSetter: Boolean) = mkTypeCompleter(tree) { sym =>
logAndValidate(sym) {
sym setInfo {
- if (isSetter)
- MethodType(List(sym.newSyntheticValueParam(typeSig(tree))), UnitClass.tpe)
- else
- NullaryMethodType(typeSig(tree))
+ val tp = if (isSetter) MethodType(List(sym.newSyntheticValueParam(typeSig(tree))), UnitClass.tpe)
+ else NullaryMethodType(typeSig(tree))
+ pluginsTypeSigAccessor(tp, typer, tree, sym)
}
}
}
@@ -903,7 +902,8 @@ trait Namers extends MethodSynthesis {
for (cda <- module.attachments.get[ConstructorDefaultsAttachment]) {
cda.companionModuleClassNamer = templateNamer
}
- ClassInfoType(parents, decls, clazz)
+ val classTp = ClassInfoType(parents, decls, clazz)
+ pluginsTypeSig(classTp, templateNamer.typer, templ, WildcardType)
}
private def classSig(cdef: ClassDef): Type = {
@@ -913,17 +913,18 @@ trait Namers extends MethodSynthesis {
val resultType = templateSig(impl)
val res = GenPolyType(tparams0, resultType)
+ val pluginsTp = pluginsTypeSig(res, typer, cdef, WildcardType)
// Already assign the type to the class symbol (monoTypeCompleter will do it again).
// Allows isDerivedValueClass to look at the info.
- clazz setInfo res
+ clazz setInfo pluginsTp
if (clazz.isDerivedValueClass) {
log("Ensuring companion for derived value class " + cdef.name + " at " + cdef.pos.show)
clazz setFlag FINAL
// Don't force the owner's info lest we create cycles as in SI-6357.
enclosingNamerWithScope(clazz.owner.rawInfo.decls).ensureCompanionObject(cdef)
}
- res
+ pluginsTp
}
private def moduleSig(mdef: ModuleDef): Type = {
@@ -931,9 +932,10 @@ trait Namers extends MethodSynthesis {
// The info of both the module and the moduleClass symbols need to be assigned. monoTypeCompleter assigns
// the result of typeSig to the module symbol. The module class info is assigned here as a side-effect.
val result = templateSig(mdef.impl)
+ val pluginsTp = pluginsTypeSig(result, typer, mdef, WildcardType)
// Assign the moduleClass info (templateSig returns a ClassInfoType)
val clazz = moduleSym.moduleClass
- clazz setInfo result
+ clazz setInfo pluginsTp
// clazz.tpe returns a `ModuleTypeRef(clazz)`, a typeRef that links to the module class `clazz`
// (clazz.info would the ClassInfoType, which is not what should be assigned to the module symbol)
clazz.tpe
@@ -1134,7 +1136,7 @@ trait Namers extends MethodSynthesis {
typer.computeMacroDefType(ddef, resTpFromOverride)
}
- thisMethodType({
+ val res = thisMethodType({
val rt = (
if (!tpt.isEmpty) {
methResTp
@@ -1150,6 +1152,7 @@ trait Namers extends MethodSynthesis {
rt.withAnnotation(AnnotationInfo(uncheckedVarianceClass.tpe, List(), List()))
else rt
})
+ pluginsTypeSig(res, typer, ddef, methResTp)
}
/**
@@ -1286,7 +1289,7 @@ trait Namers extends MethodSynthesis {
private def valDefSig(vdef: ValDef) = {
val ValDef(_, _, tpt, rhs) = vdef
- if (tpt.isEmpty) {
+ val result = if (tpt.isEmpty) {
if (rhs.isEmpty) {
MissingParameterOrValTypeError(tpt)
ErrorType
@@ -1295,6 +1298,8 @@ trait Namers extends MethodSynthesis {
} else {
typer.typedType(tpt).tpe
}
+ pluginsTypeSig(result, typer, vdef, if (tpt.isEmpty) WildcardType else result)
+
}
//@M! an abstract type definition (abstract type member/type parameter)
@@ -1328,7 +1333,8 @@ trait Namers extends MethodSynthesis {
// However, separate compilation requires the symbol info to be
// loaded to do this check, but loading the info will probably
// lead to spurious cyclic errors. So omit the check.
- GenPolyType(tparamSyms, tp)
+ val res = GenPolyType(tparamSyms, tp)
+ pluginsTypeSig(res, typer, tdef, WildcardType)
}
private def importSig(imp: Import) = {
diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala
index 1154173d85..a910d0b608 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala
@@ -872,7 +872,9 @@ trait Typers extends Modes with Adaptations with Tags {
case _ =>
debuglog("fallback on implicits: " + tree + "/" + resetAllAttrs(original))
val tree1 = typed(resetAllAttrs(original), mode, WildcardType)
- tree1.tpe = addAnnotations(tree1, tree1.tpe)
+ // Q: `typed` already calls `pluginsTyped` and `adapt`. the only difference here is that
+ // we pass `EmptyTree` as the `original`. intended? added in 2009 (53d98e7d42) by martin.
+ tree1.tpe = pluginsTyped(tree1.tpe, this, tree1, mode, pt)
if (tree1.isEmpty) tree1 else adapt(tree1, mode, pt, EmptyTree)
}
else
@@ -1077,8 +1079,8 @@ trait Typers extends Modes with Adaptations with Tags {
// begin adapt
tree.tpe match {
- case atp @ AnnotatedType(_, _, _) if canAdaptAnnotations(tree, mode, pt) => // (-1)
- adaptAnnotations(tree, mode, pt)
+ case atp @ AnnotatedType(_, _, _) if canAdaptAnnotations(tree, this, mode, pt) => // (-1)
+ adaptAnnotations(tree, this, mode, pt)
case ct @ ConstantType(value) if inNoModes(mode, TYPEmode | FUNmode) && (ct <:< pt) && !forScaladoc && !forInteractive => // (0)
val sym = tree.symbol
if (sym != null && sym.isDeprecated) {
@@ -1182,8 +1184,8 @@ trait Typers extends Modes with Adaptations with Tags {
Select(tree, "to" + sym.name)
}
}
- case AnnotatedType(_, _, _) if canAdaptAnnotations(tree, mode, pt) => // (13)
- return typed(adaptAnnotations(tree, mode, pt), mode, pt)
+ case AnnotatedType(_, _, _) if canAdaptAnnotations(tree, this, mode, pt) => // (13)
+ return typed(adaptAnnotations(tree, this, mode, pt), mode, pt)
case _ =>
}
if (!context.undetparams.isEmpty) {
@@ -4467,8 +4469,9 @@ trait Typers extends Modes with Adaptations with Tags {
if (typed(expr).tpe.typeSymbol != UnitClass)
unit.warning(tree.pos, "enclosing method " + name + " has result type Unit: return value discarded")
}
- treeCopy.Return(tree, checkDead(expr1)).setSymbol(enclMethod.owner)
- .setType(adaptTypeOfReturn(expr1, restpt.tpe, NothingClass.tpe))
+ val res = treeCopy.Return(tree, checkDead(expr1)).setSymbol(enclMethod.owner)
+ val tp = pluginsTypedReturn(NothingClass.tpe, this, res, restpt.tpe)
+ res.setType(tp)
}
}
}
@@ -5666,11 +5669,13 @@ trait Typers extends Modes with Adaptations with Tags {
lastTreeToTyper = tree
indentTyping()
+ val ptPlugins = pluginsPt(pt, this, tree, mode)
+
val startByType = if (Statistics.canEnable) Statistics.pushTimer(byTypeStack, byTypeNanos(tree.getClass)) else null
if (Statistics.canEnable) Statistics.incCounter(visitsByType, tree.getClass)
try {
if (context.retyping &&
- (tree.tpe ne null) && (tree.tpe.isErroneous || !(tree.tpe <:< pt))) {
+ (tree.tpe ne null) && (tree.tpe.isErroneous || !(tree.tpe <:< ptPlugins))) {
tree.tpe = null
if (tree.hasSymbol) tree.symbol = NoSymbol
}
@@ -5678,7 +5683,7 @@ trait Typers extends Modes with Adaptations with Tags {
val alreadyTyped = tree.tpe ne null
var tree1: Tree = if (alreadyTyped) tree else {
printTyping(
- ptLine("typing %s: pt = %s".format(ptTree(tree), pt),
+ ptLine("typing %s: pt = %s".format(ptTree(tree), ptPlugins),
"undetparams" -> context.undetparams,
"implicitsEnabled" -> context.implicitsEnabled,
"enrichmentEnabled" -> context.enrichmentEnabled,
@@ -5687,7 +5692,7 @@ trait Typers extends Modes with Adaptations with Tags {
"context.owner" -> context.owner
)
)
- typed1(tree, mode, dropExistential(pt))
+ typed1(tree, mode, dropExistential(ptPlugins))
}
// Can happen during erroneous compilation - error(s) have been
// reported, but we need to avoid causing an NPE with this tree
@@ -5701,12 +5706,12 @@ trait Typers extends Modes with Adaptations with Tags {
)
}
- tree1.tpe = addAnnotations(tree1, tree1.tpe)
- val result = if (tree1.isEmpty) tree1 else adapt(tree1, mode, pt, tree)
+ tree1.tpe = pluginsTyped(tree1.tpe, this, tree1, mode, ptPlugins)
+ val result = if (tree1.isEmpty) tree1 else adapt(tree1, mode, ptPlugins, tree)
if (!alreadyTyped) {
printTyping("adapted %s: %s to %s, %s".format(
- tree1, tree1.tpe.widen, pt, context.undetparamsString)
+ tree1, tree1.tpe.widen, ptPlugins, context.undetparamsString)
) //DEBUG
}
if (!isPastTyper) signalDone(context.asInstanceOf[analyzer.Context], tree, result)
@@ -5721,7 +5726,7 @@ trait Typers extends Modes with Adaptations with Tags {
setError(tree)
case ex: Exception =>
if (settings.debug.value) // @M causes cyclic reference error
- Console.println("exception when typing "+tree+", pt = "+pt)
+ Console.println("exception when typing "+tree+", pt = "+ptPlugins)
if (context != null && context.unit.exists && tree != null)
logError("AT: " + (tree.pos).dbgString, ex)
throw ex
diff --git a/src/continuations/plugin/scala/tools/selectivecps/CPSAnnotationChecker.scala b/src/continuations/plugin/scala/tools/selectivecps/CPSAnnotationChecker.scala
index 15025f85e3..00c72cf423 100644
--- a/src/continuations/plugin/scala/tools/selectivecps/CPSAnnotationChecker.scala
+++ b/src/continuations/plugin/scala/tools/selectivecps/CPSAnnotationChecker.scala
@@ -9,6 +9,7 @@ import scala.tools.nsc.MissingRequirementError
abstract class CPSAnnotationChecker extends CPSUtils with Modes {
val global: Global
import global._
+ import analyzer.{AnalyzerPlugin, Typer}
import definitions._
//override val verbose = true
@@ -18,12 +19,12 @@ abstract class CPSAnnotationChecker extends CPSUtils with Modes {
* Checks whether @cps annotations conform
*/
object checker extends AnnotationChecker {
- private def addPlusMarker(tp: Type) = tp withAnnotation newPlusMarker()
- private def addMinusMarker(tp: Type) = tp withAnnotation newMinusMarker()
+ private[CPSAnnotationChecker] def addPlusMarker(tp: Type) = tp withAnnotation newPlusMarker()
+ private[CPSAnnotationChecker] def addMinusMarker(tp: Type) = tp withAnnotation newMinusMarker()
- private def cleanPlus(tp: Type) =
+ private[CPSAnnotationChecker] def cleanPlus(tp: Type) =
removeAttribs(tp, MarkerCPSAdaptPlus, MarkerCPSTypes)
- private def cleanPlusWith(tp: Type)(newAnnots: AnnotationInfo*) =
+ private[CPSAnnotationChecker] def cleanPlusWith(tp: Type)(newAnnots: AnnotationInfo*) =
cleanPlus(tp) withAnnotations newAnnots.toList
/** Check annotations to decide whether tpe1 <:< tpe2 */
@@ -116,8 +117,13 @@ abstract class CPSAnnotationChecker extends CPSUtils with Modes {
} else
bounds
}
+ }
+
+ object plugin extends AnalyzerPlugin {
+
+ import checker._
- override def canAdaptAnnotations(tree: Tree, mode: Int, pt: Type): Boolean = {
+ override def canAdaptAnnotations(tree: Tree, typer: Typer, mode: Int, pt: Type): Boolean = {
if (!cpsEnabled) return false
vprintln("can adapt annotations? " + tree + " / " + tree.tpe + " / " + Integer.toHexString(mode) + " / " + pt)
@@ -183,7 +189,7 @@ abstract class CPSAnnotationChecker extends CPSUtils with Modes {
} else false
}
- override def adaptAnnotations(tree: Tree, mode: Int, pt: Type): Tree = {
+ override def adaptAnnotations(tree: Tree, typer: Typer, mode: Int, pt: Type): Tree = {
if (!cpsEnabled) return tree
vprintln("adapt annotations " + tree + " / " + tree.tpe + " / " + modeString(mode) + " / " + pt)
@@ -239,14 +245,15 @@ abstract class CPSAnnotationChecker extends CPSUtils with Modes {
* is in tail position. Therefore, we are making sure that only the types of return expressions
* are adapted which will either be removed, or lead to an error.
*/
- override def adaptTypeOfReturn(tree: Tree, pt: Type, default: => Type): Type = {
+ override def pluginsTypedReturn(default: Type, typer: Typer, tree: Return, pt: Type): Type = {
+ val expr = tree.expr
// only adapt if method's result type (pt) is cps type
val annots = cpsParamAnnotation(pt)
if (annots.nonEmpty) {
- // return type of `tree` without plus marker, but only if it doesn't have other cps annots
- if (hasPlusMarker(tree.tpe) && !hasCpsParamTypes(tree.tpe))
- tree.setType(removeAttribs(tree.tpe, MarkerCPSAdaptPlus))
- tree.tpe
+ // return type of `expr` without plus marker, but only if it doesn't have other cps annots
+ if (hasPlusMarker(expr.tpe) && !hasCpsParamTypes(expr.tpe))
+ expr.setType(removeAttribs(expr.tpe, MarkerCPSAdaptPlus))
+ expr.tpe
} else default
}
@@ -393,7 +400,7 @@ abstract class CPSAnnotationChecker extends CPSUtils with Modes {
/** Modify the type that has thus far been inferred
* for a tree. All this should do is add annotations. */
- override def addAnnotations(tree: Tree, tpe: Type): Type = {
+ override def pluginsTyped(tpe: Type, typer: Typer, tree: Tree, mode: Int, pt: Type): Type = {
import scala.util.control._
if (!cpsEnabled) {
if (Exception.failAsValue(classOf[MissingRequirementError])(false)(hasCpsParamTypes(tpe)))
diff --git a/src/continuations/plugin/scala/tools/selectivecps/SelectiveCPSPlugin.scala b/src/continuations/plugin/scala/tools/selectivecps/SelectiveCPSPlugin.scala
index 8a500d6c4d..237159795a 100644
--- a/src/continuations/plugin/scala/tools/selectivecps/SelectiveCPSPlugin.scala
+++ b/src/continuations/plugin/scala/tools/selectivecps/SelectiveCPSPlugin.scala
@@ -33,6 +33,7 @@ class SelectiveCPSPlugin(val global: Global) extends Plugin {
val global: SelectiveCPSPlugin.this.global.type = SelectiveCPSPlugin.this.global
}
global.addAnnotationChecker(checker.checker)
+ global.analyzer.addAnalyzerPlugin(checker.plugin)
global.log("instantiated cps plugin: " + this)
diff --git a/src/reflect/scala/reflect/internal/AnnotationCheckers.scala b/src/reflect/scala/reflect/internal/AnnotationCheckers.scala
index 5318d3e540..1ab975b233 100644
--- a/src/reflect/scala/reflect/internal/AnnotationCheckers.scala
+++ b/src/reflect/scala/reflect/internal/AnnotationCheckers.scala
@@ -16,7 +16,15 @@ trait AnnotationCheckers {
/** An additional checker for annotations on types.
* Typically these are registered by compiler plugins
* with the addAnnotationChecker method. */
- abstract class AnnotationChecker {
+ trait AnnotationChecker {
+
+ /**
+ * Selectively activate this annotation checker. When using both an annotation checker
+ * and an analyzer plugin, it is common to run both of them only during selected
+ * compiler phases. See documentation in AnalyzerPlugin.isActive.
+ */
+ def isActive(): Boolean = true
+
/** Check the annotations on two types conform. */
def annotationsConform(tpe1: Type, tpe2: Type): Boolean
@@ -29,39 +37,51 @@ trait AnnotationCheckers {
def annotationsGlb(tp: Type, ts: List[Type]): Type = tp
/** Refine the bounds on type parameters to the given type arguments. */
- def adaptBoundsToAnnotations(bounds: List[TypeBounds],
- tparams: List[Symbol], targs: List[Type]): List[TypeBounds] = bounds
+ def adaptBoundsToAnnotations(bounds: List[TypeBounds], tparams: List[Symbol],
+ targs: List[Type]): List[TypeBounds] = bounds
- /** Modify the type that has thus far been inferred
- * for a tree. All this should do is add annotations. */
+ /**
+ * Modify the type that has thus far been inferred for a tree. All this should
+ * do is add annotations.
+ */
+ @deprecated("Create an AnalyzerPlugin and use pluginsTyped", "2.10.1")
def addAnnotations(tree: Tree, tpe: Type): Type = tpe
- /** Decide whether this annotation checker can adapt a tree
- * that has an annotated type to the given type tp, taking
- * into account the given mode (see method adapt in trait Typers).*/
+ /**
+ * Decide whether this analyzer plugin can adapt a tree that has an annotated type to the
+ * given type tp, taking into account the given mode (see method adapt in trait Typers).
+ */
+ @deprecated("Create an AnalyzerPlugin and use canAdaptAnnotations", "2.10.1")
def canAdaptAnnotations(tree: Tree, mode: Int, pt: Type): Boolean = false
- /** Adapt a tree that has an annotated type to the given type tp,
- * taking into account the given mode (see method adapt in trait Typers).
- * An implementation cannot rely on canAdaptAnnotations being called
- * before. If the implementing class cannot do the adaptiong, it
- * should return the tree unchanged.*/
+ /**
+ * Adapt a tree that has an annotated type to the given type tp, taking into account the given
+ * mode (see method adapt in trait Typers).
+ *
+ * An implementation cannot rely on canAdaptAnnotations being called before. If the implementing
+ * class cannot do the adaptiong, it should return the tree unchanged.
+ */
+ @deprecated("Create an AnalyzerPlugin and use adaptAnnotations", "2.10.1")
def adaptAnnotations(tree: Tree, mode: Int, pt: Type): Tree = tree
- /** Adapt the type of a return expression. The decision of an annotation checker
- * whether the type should be adapted is based on the type of the expression
- * which is returned, as well as the result type of the method (pt).
- * By default, this method simply returns the passed `default` type.
+ /**
+ * Adapt the type of a return expression. The decision of a typer plugin whether the type
+ * should be adapted is based on the type of the expression which is returned, as well as the
+ * result type of the method (pt).
+ *
+ * By default, this method simply returns the passed `default` type.
*/
+ @deprecated("Create an AnalyzerPlugin and use pluginsTypedReturn. Note: the 'tree' argument here is\n"+
+ "the 'expr' of a Return tree; 'pluginsTypedReturn' takes the Return tree itself as argument", "2.10.1")
def adaptTypeOfReturn(tree: Tree, pt: Type, default: => Type): Type = default
}
// Syncnote: Annotation checkers inaccessible to reflection, so no sync in var necessary.
+
/** The list of annotation checkers that have been registered */
private var annotationCheckers: List[AnnotationChecker] = Nil
- /** Register an annotation checker. Typically these
- * are added by compiler plugins. */
+ /** Register an annotation checker. Typically these are added by compiler plugins. */
def addAnnotationChecker(checker: AnnotationChecker) {
if (!(annotationCheckers contains checker))
annotationCheckers = checker :: annotationCheckers
@@ -72,76 +92,53 @@ trait AnnotationCheckers {
annotationCheckers = Nil
}
- /** Check that the annotations on two types conform. To do
- * so, consult all registered annotation checkers. */
- def annotationsConform(tp1: Type, tp2: Type): Boolean = {
- /* Finish quickly if there are no annotations */
- if (tp1.annotations.isEmpty && tp2.annotations.isEmpty)
- true
- else
- annotationCheckers.forall(
- _.annotationsConform(tp1,tp2))
- }
-
- /** Refine the computed least upper bound of a list of types.
- * All this should do is add annotations. */
- def annotationsLub(tpe: Type, ts: List[Type]): Type = {
- annotationCheckers.foldLeft(tpe)((tpe, checker) =>
- checker.annotationsLub(tpe, ts))
- }
-
- /** Refine the computed greatest lower bound of a list of types.
- * All this should do is add annotations. */
- def annotationsGlb(tpe: Type, ts: List[Type]): Type = {
- annotationCheckers.foldLeft(tpe)((tpe, checker) =>
- checker.annotationsGlb(tpe, ts))
- }
-
- /** Refine the bounds on type parameters to the given type arguments. */
- def adaptBoundsToAnnotations(bounds: List[TypeBounds],
- tparams: List[Symbol], targs: List[Type]): List[TypeBounds] = {
- annotationCheckers.foldLeft(bounds)((bounds, checker) =>
- checker.adaptBoundsToAnnotations(bounds, tparams, targs))
- }
-
- /** Let all annotations checkers add extra annotations
- * to this tree's type. */
- def addAnnotations(tree: Tree, tpe: Type): Type = {
- annotationCheckers.foldLeft(tpe)((tpe, checker) =>
- checker.addAnnotations(tree, tpe))
- }
-
- /** Find out whether any annotation checker can adapt a tree
- * to a given type. Called by Typers.adapt. */
- def canAdaptAnnotations(tree: Tree, mode: Int, pt: Type): Boolean = {
- annotationCheckers.exists(_.canAdaptAnnotations(tree, mode, pt))
- }
-
- /** Let registered annotation checkers adapt a tree
- * to a given type (called by Typers.adapt). Annotation checkers
- * that cannot do the adaption should pass the tree through
- * unchanged. */
- def adaptAnnotations(tree: Tree, mode: Int, pt: Type): Tree = {
- annotationCheckers.foldLeft(tree)((tree, checker) =>
- checker.adaptAnnotations(tree, mode, pt))
- }
-
- /** Let a registered annotation checker adapt the type of a return expression.
- * Annotation checkers that cannot do the adaptation should simply return
- * the `default` argument.
- *
- * Note that the result is undefined if more than one annotation checker
- * returns an adapted type which is not a subtype of `default`.
- */
- def adaptTypeOfReturn(tree: Tree, pt: Type, default: => Type): Type = {
- val adaptedTypes = annotationCheckers flatMap { checker =>
- val adapted = checker.adaptTypeOfReturn(tree, pt, default)
- if (!(adapted <:< default)) List(adapted)
- else List()
- }
- adaptedTypes match {
- case fst :: _ => fst
- case List() => default
- }
- }
+ /** @see AnnotationChecker.annotationsConform */
+ def annotationsConform(tp1: Type, tp2: Type): Boolean =
+ if (annotationCheckers.isEmpty || (tp1.annotations.isEmpty && tp2.annotations.isEmpty)) true
+ else annotationCheckers.forall(checker => {
+ !checker.isActive() || checker.annotationsConform(tp1,tp2)
+ })
+
+ /** @see AnnotationChecker.annotationsLub */
+ def annotationsLub(tpe: Type, ts: List[Type]): Type =
+ if (annotationCheckers.isEmpty) tpe
+ else annotationCheckers.foldLeft(tpe)((tpe, checker) =>
+ if (!checker.isActive()) tpe else checker.annotationsLub(tpe, ts))
+
+ /** @see AnnotationChecker.annotationsGlb */
+ def annotationsGlb(tpe: Type, ts: List[Type]): Type =
+ if (annotationCheckers.isEmpty) tpe
+ else annotationCheckers.foldLeft(tpe)((tpe, checker) =>
+ if (!checker.isActive()) tpe else checker.annotationsGlb(tpe, ts))
+
+ /** @see AnnotationChecker.adaptBoundsToAnnotations */
+ def adaptBoundsToAnnotations(bounds: List[TypeBounds], tparams: List[Symbol],
+ targs: List[Type]): List[TypeBounds] =
+ if (annotationCheckers.isEmpty) bounds
+ else annotationCheckers.foldLeft(bounds)((bounds, checker) =>
+ if (!checker.isActive()) bounds else checker.adaptBoundsToAnnotations(bounds, tparams, targs))
+
+
+ /* The following methods will be removed with the deprecated methods is AnnotationChecker. */
+
+ def addAnnotations(tree: Tree, tpe: Type): Type =
+ if (annotationCheckers.isEmpty) tpe
+ else annotationCheckers.foldLeft(tpe)((tpe, checker) =>
+ if (!checker.isActive()) tpe else checker.addAnnotations(tree, tpe))
+
+ def canAdaptAnnotations(tree: Tree, mode: Int, pt: Type): Boolean =
+ if (annotationCheckers.isEmpty) false
+ else annotationCheckers.exists(checker => {
+ checker.isActive() && checker.canAdaptAnnotations(tree, mode, pt)
+ })
+
+ def adaptAnnotations(tree: Tree, mode: Int, pt: Type): Tree =
+ if (annotationCheckers.isEmpty) tree
+ else annotationCheckers.foldLeft(tree)((tree, checker) =>
+ if (!checker.isActive()) tree else checker.adaptAnnotations(tree, mode, pt))
+
+ def adaptTypeOfReturn(tree: Tree, pt: Type, default: => Type): Type =
+ if (annotationCheckers.isEmpty) default
+ else annotationCheckers.foldLeft(default)((tpe, checker) =>
+ if (!checker.isActive()) tpe else checker.adaptTypeOfReturn(tree, pt, tpe))
}
diff --git a/test/files/run/analyzerPlugins.check b/test/files/run/analyzerPlugins.check
new file mode 100644
index 0000000000..8856fef5b3
--- /dev/null
+++ b/test/files/run/analyzerPlugins.check
@@ -0,0 +1,197 @@
+adaptBoundsToAnnots(List( <: Int), List(type T), List(Int @testAnn)) [2]
+annotationsConform(Boolean @testAnn, Boolean) [1]
+annotationsConform(Boolean(false), Boolean @testAnn) [1]
+annotationsConform(Int @testAnn, ?A) [1]
+annotationsConform(Int @testAnn, Any) [1]
+annotationsConform(Int @testAnn, Int) [2]
+annotationsConform(Int(1) @testAnn, Int) [1]
+annotationsConform(Int(1), Int @testAnn) [1]
+annotationsConform(Nothing, Int @testAnn) [2]
+annotationsConform(String @testAnn, String) [1]
+canAdaptAnnotations(Trees$Ident, String) [1]
+canAdaptAnnotations(Trees$Select, ?) [1]
+canAdaptAnnotations(Trees$Select, Boolean @testAnn) [1]
+canAdaptAnnotations(Trees$Select, Boolean) [1]
+canAdaptAnnotations(Trees$Select, String @testAnn) [1]
+canAdaptAnnotations(Trees$TypeTree, ?) [10]
+canAdaptAnnotations(Trees$Typed, ?) [3]
+canAdaptAnnotations(Trees$Typed, Any) [1]
+canAdaptAnnotations(Trees$Typed, Int) [1]
+lub(List(Int @testAnn, Int)) [1]
+pluginsPt(?, Trees$Annotated) [7]
+pluginsPt(?, Trees$Apply) [8]
+pluginsPt(?, Trees$ApplyImplicitView) [2]
+pluginsPt(?, Trees$Assign) [7]
+pluginsPt(?, Trees$Block) [4]
+pluginsPt(?, Trees$ClassDef) [2]
+pluginsPt(?, Trees$DefDef) [14]
+pluginsPt(?, Trees$Ident) [51]
+pluginsPt(?, Trees$If) [2]
+pluginsPt(?, Trees$Literal) [16]
+pluginsPt(?, Trees$New) [5]
+pluginsPt(?, Trees$PackageDef) [1]
+pluginsPt(?, Trees$Return) [1]
+pluginsPt(?, Trees$Select) [51]
+pluginsPt(?, Trees$Super) [2]
+pluginsPt(?, Trees$This) [20]
+pluginsPt(?, Trees$TypeApply) [3]
+pluginsPt(?, Trees$TypeBoundsTree) [2]
+pluginsPt(?, Trees$TypeDef) [1]
+pluginsPt(?, Trees$TypeTree) [38]
+pluginsPt(?, Trees$Typed) [1]
+pluginsPt(?, Trees$ValDef) [21]
+pluginsPt(Any, Trees$Literal) [2]
+pluginsPt(Any, Trees$Typed) [1]
+pluginsPt(Array[Any], Trees$ArrayValue) [1]
+pluginsPt(Boolean @testAnn, Trees$Literal) [1]
+pluginsPt(Boolean @testAnn, Trees$Select) [1]
+pluginsPt(Boolean, Trees$Apply) [1]
+pluginsPt(Boolean, Trees$Ident) [1]
+pluginsPt(Boolean, Trees$Literal) [1]
+pluginsPt(Double, Trees$Select) [1]
+pluginsPt(Int @testAnn, Trees$Literal) [1]
+pluginsPt(Int, Trees$Apply) [1]
+pluginsPt(Int, Trees$Ident) [2]
+pluginsPt(Int, Trees$If) [1]
+pluginsPt(Int, Trees$Literal) [5]
+pluginsPt(Int, Trees$Select) [3]
+pluginsPt(List, Trees$Apply) [1]
+pluginsPt(List[Any], Trees$Select) [1]
+pluginsPt(String @testAnn, Trees$Select) [1]
+pluginsPt(String, Trees$Apply) [1]
+pluginsPt(String, Trees$Block) [2]
+pluginsPt(String, Trees$Ident) [4]
+pluginsPt(String, Trees$Literal) [1]
+pluginsPt(String, Trees$Select) [1]
+pluginsPt(String, Trees$Typed) [1]
+pluginsPt(Unit, Trees$Assign) [1]
+pluginsPt(scala.annotation.Annotation, Trees$Apply) [5]
+pluginsTypeSig(<none>, Trees$Template) [2]
+pluginsTypeSig(class A, Trees$ClassDef) [1]
+pluginsTypeSig(class testAnn, Trees$ClassDef) [1]
+pluginsTypeSig(constructor A, Trees$DefDef) [2]
+pluginsTypeSig(constructor testAnn, Trees$DefDef) [1]
+pluginsTypeSig(method foo, Trees$DefDef) [1]
+pluginsTypeSig(method method, Trees$DefDef) [1]
+pluginsTypeSig(method nested, Trees$DefDef) [1]
+pluginsTypeSig(type T, Trees$TypeDef) [2]
+pluginsTypeSig(value annotField, Trees$ValDef) [2]
+pluginsTypeSig(value f, Trees$ValDef) [1]
+pluginsTypeSig(value inferField, Trees$ValDef) [2]
+pluginsTypeSig(value lub1, Trees$ValDef) [2]
+pluginsTypeSig(value lub2, Trees$ValDef) [2]
+pluginsTypeSig(value param, Trees$ValDef) [2]
+pluginsTypeSig(value str, Trees$ValDef) [1]
+pluginsTypeSig(value x, Trees$ValDef) [4]
+pluginsTypeSig(value y, Trees$ValDef) [4]
+pluginsTypeSig(variable count, Trees$ValDef) [3]
+pluginsTypeSigAccessor(value annotField) [1]
+pluginsTypeSigAccessor(value inferField) [1]
+pluginsTypeSigAccessor(value lub1) [1]
+pluginsTypeSigAccessor(value lub2) [1]
+pluginsTypeSigAccessor(value x) [1]
+pluginsTypeSigAccessor(value y) [1]
+pluginsTypeSigAccessor(variable count) [2]
+pluginsTyped( <: Int, Trees$TypeBoundsTree) [2]
+pluginsTyped(()Object, Trees$Select) [1]
+pluginsTyped(()String, Trees$Ident) [1]
+pluginsTyped(()String, Trees$TypeApply) [1]
+pluginsTyped(()scala.annotation.Annotation, Trees$Select) [1]
+pluginsTyped(()testAnn, Trees$Select) [10]
+pluginsTyped((str: String)A <and> (param: Double)A, Trees$Select) [1]
+pluginsTyped((x$1: Any)Boolean <and> (x: Double)Boolean <and> (x: Float)Boolean <and> (x: Long)Boolean <and> (x: Int)Boolean <and> (x: Char)Boolean <and> (x: Short)Boolean <and> (x: Byte)Boolean, Trees$Select) [1]
+pluginsTyped((x$1: Int)Unit, Trees$Select) [1]
+pluginsTyped((x: Double)Double <and> (x: Float)Float <and> (x: Long)Long <and> (x: Int)Int <and> (x: Char)Int <and> (x: Short)Int <and> (x: Byte)Int <and> (x: String)String, Trees$Select) [1]
+pluginsTyped((x: String)scala.collection.immutable.StringOps, Trees$Select) [2]
+pluginsTyped((xs: Array[Any])scala.collection.mutable.WrappedArray[Any], Trees$TypeApply) [1]
+pluginsTyped(<empty>.type, Trees$Ident) [1]
+pluginsTyped(<error>, Trees$Select) [1]
+pluginsTyped(<notype>, Trees$ClassDef) [2]
+pluginsTyped(<notype>, Trees$DefDef) [14]
+pluginsTyped(<notype>, Trees$PackageDef) [1]
+pluginsTyped(<notype>, Trees$TypeDef) [1]
+pluginsTyped(<notype>, Trees$ValDef) [21]
+pluginsTyped(<root>, Trees$Ident) [1]
+pluginsTyped(=> Boolean @testAnn, Trees$Select) [1]
+pluginsTyped(=> Double, Trees$Select) [4]
+pluginsTyped(=> Int, Trees$Select) [5]
+pluginsTyped(=> Int, Trees$TypeApply) [1]
+pluginsTyped(=> String @testAnn, Trees$Select) [1]
+pluginsTyped(A, Trees$Apply) [1]
+pluginsTyped(A, Trees$Ident) [2]
+pluginsTyped(A, Trees$This) [8]
+pluginsTyped(A, Trees$TypeTree) [4]
+pluginsTyped(A.super.type, Trees$Super) [1]
+pluginsTyped(A.this.type, Trees$This) [11]
+pluginsTyped(Any, Trees$TypeTree) [1]
+pluginsTyped(AnyRef, Trees$Select) [4]
+pluginsTyped(Array[Any], Trees$ArrayValue) [1]
+pluginsTyped(Boolean @testAnn, Trees$Select) [1]
+pluginsTyped(Boolean @testAnn, Trees$TypeTree) [4]
+pluginsTyped(Boolean(false), Trees$Literal) [2]
+pluginsTyped(Boolean, Trees$Apply) [1]
+pluginsTyped(Boolean, Trees$Select) [4]
+pluginsTyped(Char('c'), Trees$Literal) [2]
+pluginsTyped(Double, Trees$Select) [6]
+pluginsTyped(Int @testAnn, Trees$TypeTree) [2]
+pluginsTyped(Int @testAnn, Trees$Typed) [2]
+pluginsTyped(Int(0), Trees$Literal) [3]
+pluginsTyped(Int(1) @testAnn, Trees$Typed) [1]
+pluginsTyped(Int(1), Trees$Literal) [8]
+pluginsTyped(Int(2), Trees$Literal) [1]
+pluginsTyped(Int, Trees$Apply) [1]
+pluginsTyped(Int, Trees$Ident) [2]
+pluginsTyped(Int, Trees$If) [2]
+pluginsTyped(Int, Trees$Select) [15]
+pluginsTyped(Int, Trees$TypeTree) [13]
+pluginsTyped(List, Trees$Apply) [1]
+pluginsTyped(List, Trees$Select) [1]
+pluginsTyped(List[Any], Trees$Apply) [1]
+pluginsTyped(List[Any], Trees$Select) [1]
+pluginsTyped(List[Any], Trees$TypeTree) [3]
+pluginsTyped(Nothing, Trees$Return) [1]
+pluginsTyped(Nothing, Trees$Select) [2]
+pluginsTyped(Object, Trees$Apply) [1]
+pluginsTyped(String @testAnn, Trees$Ident) [1]
+pluginsTyped(String @testAnn, Trees$Select) [1]
+pluginsTyped(String @testAnn, Trees$TypeTree) [4]
+pluginsTyped(String(""), Trees$Literal) [2]
+pluginsTyped(String("huhu"), Trees$Literal) [1]
+pluginsTyped(String("str") @testAnn, Trees$Typed) [1]
+pluginsTyped(String("str"), Trees$Literal) [1]
+pluginsTyped(String("str"), Trees$Typed) [1]
+pluginsTyped(String("two"), Trees$Literal) [2]
+pluginsTyped(String, Trees$Apply) [2]
+pluginsTyped(String, Trees$Block) [2]
+pluginsTyped(String, Trees$Ident) [1]
+pluginsTyped(String, Trees$Select) [9]
+pluginsTyped(String, Trees$TypeTree) [7]
+pluginsTyped(Unit, Trees$Apply) [2]
+pluginsTyped(Unit, Trees$Assign) [8]
+pluginsTyped(Unit, Trees$Block) [4]
+pluginsTyped(Unit, Trees$If) [1]
+pluginsTyped(Unit, Trees$Literal) [5]
+pluginsTyped(Unit, Trees$TypeTree) [1]
+pluginsTyped([A](xs: A*)List[A], Trees$Select) [1]
+pluginsTyped([T <: Int]=> Int, Trees$Select) [1]
+pluginsTyped([T0 >: ? <: ?]()T0, Trees$Select) [1]
+pluginsTyped([T](xs: Array[T])scala.collection.mutable.WrappedArray[T], Trees$Select) [1]
+pluginsTyped(annotation.type, Trees$Select) [4]
+pluginsTyped(math.type, Trees$Select) [9]
+pluginsTyped(scala.annotation.Annotation, Trees$Apply) [1]
+pluginsTyped(scala.annotation.TypeConstraint, Trees$Select) [4]
+pluginsTyped(scala.annotation.TypeConstraint, Trees$TypeTree) [2]
+pluginsTyped(scala.collection.immutable.List.type, Trees$Select) [2]
+pluginsTyped(scala.collection.immutable.StringOps, Trees$ApplyImplicitView) [2]
+pluginsTyped(scala.collection.mutable.WrappedArray[Any], Trees$Apply) [1]
+pluginsTyped(scala.type, Trees$Ident) [1]
+pluginsTyped(scala.type, Trees$Select) [1]
+pluginsTyped(str.type, Trees$Ident) [3]
+pluginsTyped(testAnn, Trees$Apply) [5]
+pluginsTyped(testAnn, Trees$Ident) [5]
+pluginsTyped(testAnn, Trees$New) [5]
+pluginsTyped(testAnn, Trees$This) [1]
+pluginsTyped(testAnn, Trees$TypeTree) [2]
+pluginsTyped(testAnn.super.type, Trees$Super) [1]
+pluginsTyped(type, Trees$Select) [1]
+pluginsTypedReturn(return f, String) [1]
diff --git a/test/files/run/analyzerPlugins.scala b/test/files/run/analyzerPlugins.scala
new file mode 100644
index 0000000000..daef83fa30
--- /dev/null
+++ b/test/files/run/analyzerPlugins.scala
@@ -0,0 +1,121 @@
+import scala.tools.partest._
+import scala.tools.nsc._
+
+object Test extends DirectTest {
+
+ override def extraSettings: String = "-usejavacp"
+
+ def code = """
+ class testAnn extends annotation.TypeConstraint
+
+ class A(param: Double) extends { val x: Int = 1; val y = "two"; type T = A } with AnyRef {
+ val inferField = ("str": @testAnn)
+ val annotField: Boolean @testAnn = false
+
+ val lub1 = List('c', (1: Int @testAnn), "")
+ val lub2 = if (annotField) (1: @testAnn) else 2
+
+ def foo[T <: Int] = 0
+ foo[Int @testAnn]
+
+ var count = 0
+
+ math.random // some statement
+
+ def method: String = {
+ math.random
+ val f = inferField
+
+ def nested(): String = {
+ if(count == 1)
+ return f
+ "huhu"
+ }
+ nested()
+ }
+
+ def this(str: String) {
+ this(str.toDouble)
+ math.random
+ count += 1
+ }
+ }
+ """.trim
+
+
+ def show() {
+ val global = newCompiler()
+ import global._
+ import analyzer._
+
+ val output = collection.mutable.ListBuffer[String]()
+
+ object annotChecker extends AnnotationChecker {
+ def hasTestAnn(tps: Type*) = {
+ tps exists (_.annotations.map(_.toString) contains "testAnn")
+ }
+
+ def annotationsConform(tpe1: Type, tpe2: Type): Boolean = {
+ if (hasTestAnn(tpe1, tpe2))
+ output += s"annotationsConform($tpe1, $tpe2)"
+ true
+ }
+
+ override def annotationsLub(tp: Type, ts: List[Type]): Type = {
+ if (hasTestAnn(ts: _*))
+ output += s"lub($ts)"
+ tp
+ }
+
+ override def adaptBoundsToAnnotations(bounds: List[TypeBounds], tparams: List[Symbol], targs: List[Type]): List[TypeBounds] = {
+ if (hasTestAnn(targs: _*))
+ output += s"adaptBoundsToAnnots($bounds, $tparams, $targs)"
+ bounds
+ }
+ }
+
+ object analyzerPlugin extends AnalyzerPlugin {
+ def treeClass(t: Tree) = t.getClass.toString.split('.').last
+
+ override def pluginsPt(pt: Type, typer: Typer, tree: Tree, mode: Int): Type = {
+ output += s"pluginsPt($pt, ${treeClass(tree)})"
+ pt
+ }
+
+ override def pluginsTyped(tpe: Type, typer: Typer, tree: Tree, mode: Int, pt: Type): Type = {
+ output += s"pluginsTyped($tpe, ${treeClass(tree)})"
+ tpe
+ }
+
+ override def pluginsTypeSig(tpe: Type, typer: Typer, defTree: Tree, pt: Type): Type = {
+ output += s"pluginsTypeSig(${defTree.symbol}, ${treeClass(defTree)})"
+ tpe
+ }
+
+ override def pluginsTypeSigAccessor(tpe: Type, typer: Typer, tree: ValDef, sym: Symbol): Type = {
+ output += s"pluginsTypeSigAccessor(${tree.symbol})"
+ tpe
+ }
+
+
+ override def canAdaptAnnotations(tree: Tree, typer: Typer, mode: Int, pt: Type): Boolean = {
+ output += s"canAdaptAnnotations(${treeClass(tree)}, $pt)"
+ false
+ }
+
+ override def pluginsTypedReturn(tpe: Type, typer: Typer, tree: Return, pt: Type): Type = {
+ output += s"pluginsTypedReturn($tree, $pt)"
+ tpe
+ }
+
+ }
+
+ addAnnotationChecker(annotChecker)
+ addAnalyzerPlugin(analyzerPlugin)
+ compileString(global)(code)
+
+ val res = output.groupBy(identity).mapValues(_.size).map { case (k,v) => s"$k [$v]" }.toList.sorted
+ println(res.mkString("\n"))
+ }
+
+}