scala.reflect.internal.TreeInfo.scala Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of scala-reflect Show documentation
Show all versions of scala-reflect Show documentation
Compiler for the Scala Programming Language
/* NSC -- new Scala compiler
* Copyright 2005-2011 LAMP/EPFL
* @author Martin Odersky
*/
package scala.reflect
package internal
import Flags._
/** This class ...
*
* @author Martin Odersky
* @version 1.0
*/
abstract class TreeInfo {
val global: SymbolTable
import global._
import definitions.{ isVarArgsList, isCastSymbol, ThrowableClass, TupleClass }
/* Does not seem to be used. Not sure what it does anyway.
def isOwnerDefinition(tree: Tree): Boolean = tree match {
case PackageDef(_, _)
| ClassDef(_, _, _, _)
| ModuleDef(_, _, _)
| DefDef(_, _, _, _, _, _)
| Import(_, _) => true
case _ => false
}
*/
// def isDefinition(tree: Tree): Boolean = tree.isDef
/** Is tree a declaration or type definition?
*/
def isDeclarationOrTypeDef(tree: Tree): Boolean = tree match {
case x: ValOrDefDef => x.rhs eq EmptyTree
case _ => tree.isInstanceOf[TypeDef]
}
/** Is tree legal as a member definition of an interface?
*/
def isInterfaceMember(tree: Tree): Boolean = tree match {
case EmptyTree => true
case Import(_, _) => true
case TypeDef(_, _, _, _) => true
case DefDef(mods, _, _, _, _, __) => mods.isDeferred
case ValDef(mods, _, _, _) => mods.isDeferred
case _ => false
}
/** Is tree a pure (i.e. non-side-effecting) definition?
*/
def isPureDef(tree: Tree): Boolean = tree match {
case EmptyTree
| ClassDef(_, _, _, _)
| TypeDef(_, _, _, _)
| Import(_, _)
| DefDef(_, _, _, _, _, _) =>
true
case ValDef(mods, _, _, rhs) =>
!mods.isMutable && isExprSafeToInline(rhs)
case _ =>
false
}
/** Is tree an expression which can be inlined without affecting program semantics?
*
* Note that this is not called "isExprSafeToInline" since purity (lack of side-effects)
* is not the litmus test. References to modules and lazy vals are side-effecting,
* both because side-effecting code may be executed and because the first reference
* takes a different code path than all to follow; but they are safe to inline
* because the expression result from evaluating them is always the same.
*/
def isExprSafeToInline(tree: Tree): Boolean = tree match {
case EmptyTree
| This(_)
| Super(_, _)
| Literal(_) =>
true
case Ident(_) =>
tree.symbol.isStable
// this case is mostly to allow expressions like -5 and +7, but any
// member of an anyval should be safely pure
case Select(Literal(const), name) =>
const.isAnyVal && (const.tpe.member(name) != NoSymbol)
case Select(qual, _) =>
tree.symbol.isStable && isExprSafeToInline(qual)
case TypeApply(fn, _) =>
isExprSafeToInline(fn)
case Apply(fn, List()) =>
/* Note: After uncurry, field accesses are represented as Apply(getter, Nil),
* so an Apply can also be pure.
* However, before typing, applications of nullary functional values are also
* Apply(function, Nil) trees. To prevent them from being treated as pure,
* we check that the callee is a method. */
fn.symbol.isMethod && !fn.symbol.isLazy && isExprSafeToInline(fn)
case Typed(expr, _) =>
isExprSafeToInline(expr)
case Block(stats, expr) =>
(stats forall isPureDef) && isExprSafeToInline(expr)
case _ =>
false
}
@deprecated("Use isExprSafeToInline instead", "2.10.0")
def isPureExpr(tree: Tree) = isExprSafeToInline(tree)
def zipMethodParamsAndArgs(params: List[Symbol], args: List[Tree]): List[(Symbol, Tree)] =
mapMethodParamsAndArgs(params, args)((param, arg) => ((param, arg)))
def mapMethodParamsAndArgs[R](params: List[Symbol], args: List[Tree])(f: (Symbol, Tree) => R): List[R] = {
val b = List.newBuilder[R]
foreachMethodParamAndArg(params, args)((param, arg) => b += f(param, arg))
b.result
}
def foreachMethodParamAndArg(params: List[Symbol], args: List[Tree])(f: (Symbol, Tree) => Unit): Boolean = {
val plen = params.length
val alen = args.length
def fail() = {
global.debugwarn(
"Mismatch trying to zip method parameters and argument list:\n" +
" params = " + params + "\n" +
" args = " + args + "\n"
)
false
}
if (plen == alen) foreach2(params, args)(f)
else if (params.isEmpty) return fail
else if (isVarArgsList(params)) {
val plenInit = plen - 1
if (alen == plenInit) {
if (alen == 0) Nil // avoid calling mismatched zip
else foreach2(params.init, args)(f)
}
else if (alen < plenInit) return fail
else {
foreach2(params.init, args take plenInit)(f)
val remainingArgs = args drop plenInit
foreach2(List.fill(remainingArgs.size)(params.last), remainingArgs)(f)
}
}
else return fail
true
}
/**
* Selects the correct parameter list when there are nested applications.
* Given Apply(fn, args), args might correspond to any of fn.symbol's parameter
* lists. To choose the correct one before uncurry, we have to unwrap any
* applies: for instance Apply(fn @ Apply(Apply(_, _), _), args) implies args
* correspond to the third parameter list.
*
* The argument fn is the function part of the apply node being considered.
*
* Also accounts for varargs.
*/
private def applyMethodParameters(fn: Tree): List[Symbol] = {
val depth = applyDepth(fn)
// There could be applies which go beyond the parameter list(s),
// being applied to the result of the method call.
// !!! Note that this still doesn't seem correct, although it should
// be closer than what it replaced.
if (depth < fn.symbol.paramss.size) fn.symbol.paramss(depth)
else if (fn.symbol.paramss.isEmpty) Nil
else fn.symbol.paramss.last
}
def zipMethodParamsAndArgs(t: Tree): List[(Symbol, Tree)] = t match {
case Apply(fn, args) => zipMethodParamsAndArgs(applyMethodParameters(fn), args)
case _ => Nil
}
def foreachMethodParamAndArg(t: Tree)(f: (Symbol, Tree) => Unit): Unit = t match {
case Apply(fn, args) => foreachMethodParamAndArg(applyMethodParameters(fn), args)(f)
case _ =>
}
/** Is symbol potentially a getter of a variable?
*/
def mayBeVarGetter(sym: Symbol): Boolean = sym.info match {
case NullaryMethodType(_) => sym.owner.isClass && !sym.isStable
case PolyType(_, NullaryMethodType(_)) => sym.owner.isClass && !sym.isStable
case mt @ MethodType(_, _) => mt.isImplicit && sym.owner.isClass && !sym.isStable
case _ => false
}
/** Is tree a mutable variable, or the getter of a mutable field?
*/
def isVariableOrGetter(tree: Tree) = {
def sym = tree.symbol
def isVar = sym.isVariable
def isGetter = mayBeVarGetter(sym) && sym.owner.info.member(nme.getterToSetter(sym.name.toTermName)) != NoSymbol
tree match {
case Ident(_) => isVar
case Select(_, _) => isVar || isGetter
case _ =>
methPart(tree) match {
case Select(qual, nme.apply) => qual.tpe.member(nme.update) != NoSymbol
case _ => false
}
}
}
/** Is tree a self constructor call this(...)? I.e. a call to a constructor of the
* same object?
*/
def isSelfConstrCall(tree: Tree): Boolean = methPart(tree) match {
case Ident(nme.CONSTRUCTOR)
| Select(This(_), nme.CONSTRUCTOR) => true
case _ => false
}
/** Is tree a super constructor call?
*/
def isSuperConstrCall(tree: Tree): Boolean = methPart(tree) match {
case Select(Super(_, _), nme.CONSTRUCTOR) => true
case _ => false
}
/**
* Named arguments can transform a constructor call into a block, e.g.
* (b = foo, a = bar)
* is transformed to
* { val x$1 = foo
* val x$2 = bar
* (x$2, x$1)
* }
*/
def stripNamedApplyBlock(tree: Tree) = tree match {
case Block(stats, expr) if stats.forall(_.isInstanceOf[ValDef]) =>
expr
case _ =>
tree
}
/** Is tree a self or super constructor call? */
def isSelfOrSuperConstrCall(tree: Tree) = {
// stripNamedApply for SI-3584: adaptToImplicitMethod in Typers creates a special context
// for implicit search in constructor calls, adaptToImplicitMethod(isSelfOrConstrCall)
val tree1 = stripNamedApplyBlock(tree)
isSelfConstrCall(tree1) || isSuperConstrCall(tree1)
}
/** Is tree a variable pattern? */
def isVarPattern(pat: Tree): Boolean = pat match {
case x: Ident => !x.isBackquoted && isVariableName(x.name)
case _ => false
}
def isDeprecatedIdentifier(tree: Tree): Boolean = tree match {
case x: Ident => !x.isBackquoted && nme.isDeprecatedIdentifierName(x.name)
case _ => false
}
/** The first constructor definitions in `stats` */
def firstConstructor(stats: List[Tree]): Tree = stats find {
case x: DefDef => nme.isConstructorName(x.name)
case _ => false
} getOrElse EmptyTree
/** The arguments to the first constructor in `stats`. */
def firstConstructorArgs(stats: List[Tree]): List[Tree] = firstConstructor(stats) match {
case DefDef(_, _, _, args :: _, _, _) => args
case _ => Nil
}
/** The value definitions marked PRESUPER in this statement sequence */
def preSuperFields(stats: List[Tree]): List[ValDef] =
stats collect { case vd: ValDef if isEarlyValDef(vd) => vd }
def isEarlyDef(tree: Tree) = tree match {
case TypeDef(mods, _, _, _) => mods hasFlag PRESUPER
case ValDef(mods, _, _, _) => mods hasFlag PRESUPER
case _ => false
}
def isEarlyValDef(tree: Tree) = tree match {
case ValDef(mods, _, _, _) => mods hasFlag PRESUPER
case _ => false
}
def isEarlyTypeDef(tree: Tree) = tree match {
case TypeDef(mods, _, _, _) => mods hasFlag PRESUPER
case _ => false
}
/** Is tpt a vararg type of the form T* ? */
def isRepeatedParamType(tpt: Tree) = tpt match {
case TypeTree() => definitions.isRepeatedParamType(tpt.tpe)
case AppliedTypeTree(Select(_, tpnme.REPEATED_PARAM_CLASS_NAME), _) => true
case AppliedTypeTree(Select(_, tpnme.JAVA_REPEATED_PARAM_CLASS_NAME), _) => true
case _ => false
}
/** The parameter ValDefs of a method definition that have vararg types of the form T*
*/
def repeatedParams(tree: Tree): List[ValDef] = tree match {
case DefDef(_, _, _, vparamss, _, _) => vparamss.flatten filter (vd => isRepeatedParamType(vd.tpt))
case _ => Nil
}
/** Is tpt a by-name parameter type of the form => T? */
def isByNameParamType(tpt: Tree) = tpt match {
case TypeTree() => definitions.isByNameParamType(tpt.tpe)
case AppliedTypeTree(Select(_, tpnme.BYNAME_PARAM_CLASS_NAME), _) => true
case _ => false
}
/** Is name a left-associative operator? */
def isLeftAssoc(operator: Name) = operator.nonEmpty && (operator.endChar != ':')
private val reserved = Set[Name](nme.false_, nme.true_, nme.null_)
/** Is name a variable name? */
def isVariableName(name: Name): Boolean = {
val first = name(0)
((first.isLower && first.isLetter) || first == '_') && !reserved(name)
}
/** Is tree a `this` node which belongs to `enclClass`? */
def isSelf(tree: Tree, enclClass: Symbol): Boolean = tree match {
case This(_) => tree.symbol == enclClass
case _ => false
}
/** a Match(Typed(_, tpt), _) is unchecked if isUncheckedAnnotation(tpt.tpe) */
def isUncheckedAnnotation(tpe: Type) = tpe hasAnnotation definitions.UncheckedClass
/** a Match(Typed(_, tpt), _) must be translated into a switch if isSwitchAnnotation(tpt.tpe) */
def isSwitchAnnotation(tpe: Type) = tpe hasAnnotation definitions.SwitchClass
/** can this type be a type pattern */
def mayBeTypePat(tree: Tree): Boolean = tree match {
case CompoundTypeTree(Template(tps, _, Nil)) => tps exists mayBeTypePat
case Annotated(_, tp) => mayBeTypePat(tp)
case AppliedTypeTree(constr, args) => mayBeTypePat(constr) || args.exists(_.isInstanceOf[Bind])
case SelectFromTypeTree(tp, _) => mayBeTypePat(tp)
case _ => false
}
/** Is this tree comprised of nothing but identifiers,
* but possibly in bindings or tuples? For instance
*
* foo @ (bar, (baz, quux))
*
* is a variable pattern; if the structure matches,
* then the remainder is inevitable.
*/
def isVariablePattern(tree: Tree): Boolean = tree match {
case Bind(name, pat) => isVariablePattern(pat)
case Ident(name) => true
case Apply(sel, args) =>
( isReferenceToScalaMember(sel, TupleClass(args.size).name.toTermName)
&& (args forall isVariablePattern)
)
case _ => false
}
/** Is this argument node of the form : _* ?
*/
def isWildcardStarArg(tree: Tree): Boolean = tree match {
case Typed(_, Ident(tpnme.WILDCARD_STAR)) => true
case _ => false
}
/** If this tree represents a type application (after unwrapping
* any applies) the first type argument. Otherwise, EmptyTree.
*/
def firstTypeArg(tree: Tree): Tree = tree match {
case Apply(fn, _) => firstTypeArg(fn)
case TypeApply(_, targ :: _) => targ
case _ => EmptyTree
}
/** If this tree has type parameters, those. Otherwise Nil.
*/
def typeParameters(tree: Tree): List[TypeDef] = tree match {
case DefDef(_, _, tparams, _, _, _) => tparams
case ClassDef(_, _, tparams, _) => tparams
case TypeDef(_, _, tparams, _) => tparams
case _ => Nil
}
/** Does this argument list end with an argument of the form : _* ? */
def isWildcardStarArgList(trees: List[Tree]) =
trees.nonEmpty && isWildcardStarArg(trees.last)
/** Is the argument a wildcard argument of the form `_` or `x @ _`?
*/
def isWildcardArg(tree: Tree): Boolean = unbind(tree) match {
case Ident(nme.WILDCARD) => true
case _ => false
}
/** Is this pattern node a catch-all (wildcard or variable) pattern? */
def isDefaultCase(cdef: CaseDef) = cdef match {
case CaseDef(pat, EmptyTree, _) => isWildcardArg(pat)
case _ => false
}
/** Does this CaseDef catch Throwable? */
def catchesThrowable(cdef: CaseDef) = catchesAllOf(cdef, ThrowableClass.tpe)
/** Does this CaseDef catch everything of a certain Type? */
def catchesAllOf(cdef: CaseDef, threshold: Type) =
isDefaultCase(cdef) || (cdef.guard.isEmpty && (unbind(cdef.pat) match {
case Typed(Ident(nme.WILDCARD), tpt) => (tpt.tpe != null) && (threshold <:< tpt.tpe)
case _ => false
}))
/** Is this pattern node a catch-all or type-test pattern? */
def isCatchCase(cdef: CaseDef) = cdef match {
case CaseDef(Typed(Ident(nme.WILDCARD), tpt), EmptyTree, _) =>
isSimpleThrowable(tpt.tpe)
case CaseDef(Bind(_, Typed(Ident(nme.WILDCARD), tpt)), EmptyTree, _) =>
isSimpleThrowable(tpt.tpe)
case _ =>
isDefaultCase(cdef)
}
private def isSimpleThrowable(tp: Type): Boolean = tp match {
case TypeRef(pre, sym, args) =>
(pre == NoPrefix || pre.widen.typeSymbol.isStatic) &&
(sym isNonBottomSubClass ThrowableClass) && /* bq */ !sym.isTrait
case _ =>
false
}
/* If we have run-time types, and these are used for pattern matching,
we should replace this by something like:
tp match {
case TypeRef(pre, sym, args) =>
args.isEmpty && (sym.owner.isPackageClass || isSimple(pre))
case NoPrefix =>
true
case _ =>
false
}
*/
/** Is this pattern node a sequence-valued pattern? */
def isSequenceValued(tree: Tree): Boolean = unbind(tree) match {
case Alternative(ts) => ts exists isSequenceValued
case ArrayValue(_, _) | Star(_) => true
case _ => false
}
/** The underlying pattern ignoring any bindings */
def unbind(x: Tree): Tree = x match {
case Bind(_, y) => unbind(y)
case y => y
}
/** Is this tree a Star(_) after removing bindings? */
def isStar(x: Tree) = unbind(x) match {
case Star(_) => true
case _ => false
}
/** The method part of an application node
*/
def methPart(tree: Tree): Tree = tree match {
case Apply(fn, _) => methPart(fn)
case TypeApply(fn, _) => methPart(fn)
case AppliedTypeTree(fn, _) => methPart(fn)
case _ => tree
}
/** The depth of the nested applies: e.g. Apply(Apply(Apply(_, _), _), _)
* has depth 3. Continues through type applications (without counting them.)
*/
def applyDepth(tree: Tree): Int = tree match {
case Apply(fn, _) => 1 + applyDepth(fn)
case TypeApply(fn, _) => applyDepth(fn)
case AppliedTypeTree(fn, _) => applyDepth(fn)
case _ => 0
}
def firstArgument(tree: Tree): Tree = tree match {
case Apply(fn, args) =>
val f = firstArgument(fn)
if (f == EmptyTree && !args.isEmpty) args.head else f
case _ =>
EmptyTree
}
/** Does list of trees start with a definition of
* a class of module with given name (ignoring imports)
*/
def firstDefinesClassOrObject(trees: List[Tree], name: Name): Boolean = trees match {
case Import(_, _) :: xs => firstDefinesClassOrObject(xs, name)
case Annotated(_, tree1) :: Nil => firstDefinesClassOrObject(List(tree1), name)
case ModuleDef(_, `name`, _) :: Nil => true
case ClassDef(_, `name`, _, _) :: Nil => true
case _ => false
}
/** Is this file the body of a compilation unit which should not
* have Predef imported?
*/
def noPredefImportForUnit(body: Tree) = {
// Top-level definition whose leading imports include Predef.
def containsLeadingPredefImport(defs: List[Tree]): Boolean = defs match {
case PackageDef(_, defs1) :: _ => containsLeadingPredefImport(defs1)
case Import(expr, _) :: rest => isReferenceToPredef(expr) || containsLeadingPredefImport(rest)
case _ => false
}
// Compilation unit is class or object 'name' in package 'scala'
def isUnitInScala(tree: Tree, name: Name) = tree match {
case PackageDef(Ident(nme.scala_), defs) => firstDefinesClassOrObject(defs, name)
case _ => false
}
( isUnitInScala(body, nme.Predef)
|| containsLeadingPredefImport(List(body)))
}
def isAbsTypeDef(tree: Tree) = tree match {
case TypeDef(_, _, _, TypeBoundsTree(_, _)) => true
case TypeDef(_, _, _, rhs) => rhs.tpe.isInstanceOf[TypeBounds]
case _ => false
}
def isAliasTypeDef(tree: Tree) = tree match {
case TypeDef(_, _, _, _) => !isAbsTypeDef(tree)
case _ => false
}
/** Some handy extractors for spotting trees through the
* the haze of irrelevant braces: i.e. Block(Nil, SomeTree)
* should not keep us from seeing SomeTree.
*/
abstract class SeeThroughBlocks[T] {
protected def unapplyImpl(x: Tree): T
def unapply(x: Tree): T = x match {
case Block(Nil, expr) => unapply(expr)
case _ => unapplyImpl(x)
}
}
object IsTrue extends SeeThroughBlocks[Boolean] {
protected def unapplyImpl(x: Tree): Boolean = x match {
case Literal(Constant(true)) => true
case _ => false
}
}
object IsFalse extends SeeThroughBlocks[Boolean] {
protected def unapplyImpl(x: Tree): Boolean = x match {
case Literal(Constant(false)) => true
case _ => false
}
}
object IsIf extends SeeThroughBlocks[Option[(Tree, Tree, Tree)]] {
protected def unapplyImpl(x: Tree) = x match {
case If(cond, thenp, elsep) => Some((cond, thenp, elsep))
case _ => None
}
}
def isApplyDynamicName(name: Name) = (name == nme.updateDynamic) || (name == nme.selectDynamic) || (name == nme.applyDynamic) || (name == nme.applyDynamicNamed)
class DynamicApplicationExtractor(nameTest: Name => Boolean) {
def unapply(tree: Tree) = tree match {
case Apply(TypeApply(Select(qual, oper), _), List(Literal(Constant(name)))) if nameTest(oper) => Some((qual, name))
case Apply(Select(qual, oper), List(Literal(Constant(name)))) if nameTest(oper) => Some((qual, name))
case Apply(Ident(oper), List(Literal(Constant(name)))) if nameTest(oper) => Some((EmptyTree, name))
case _ => None
}
}
object DynamicUpdate extends DynamicApplicationExtractor(_ == nme.updateDynamic)
object DynamicApplication extends DynamicApplicationExtractor(isApplyDynamicName)
object DynamicApplicationNamed extends DynamicApplicationExtractor(_ == nme.applyDynamicNamed)
}