diff options
author | Felix Mulder <felix.mulder@gmail.com> | 2016-11-02 11:08:28 +0100 |
---|---|---|
committer | Guillaume Martres <smarter@ubuntu.com> | 2016-11-22 01:35:07 +0100 |
commit | 8a61ff432543a29234193cd1f7c14abd3f3d31a0 (patch) | |
tree | a8147561d307af862c295cfc8100d271063bb0dd /compiler/src/dotty/tools/dotc/ast/TreeInfo.scala | |
parent | 6a455fe6da5ff9c741d91279a2dc6fe2fb1b472f (diff) | |
download | dotty-8a61ff432543a29234193cd1f7c14abd3f3d31a0.tar.gz dotty-8a61ff432543a29234193cd1f7c14abd3f3d31a0.tar.bz2 dotty-8a61ff432543a29234193cd1f7c14abd3f3d31a0.zip |
Move compiler and compiler tests to compiler dir
Diffstat (limited to 'compiler/src/dotty/tools/dotc/ast/TreeInfo.scala')
-rw-r--r-- | compiler/src/dotty/tools/dotc/ast/TreeInfo.scala | 733 |
1 files changed, 733 insertions, 0 deletions
diff --git a/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala b/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala new file mode 100644 index 000000000..d1e6bd38a --- /dev/null +++ b/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala @@ -0,0 +1,733 @@ +package dotty.tools +package dotc +package ast + +import core._ +import Flags._, Trees._, Types._, Contexts._ +import Names._, StdNames._, NameOps._, Decorators._, Symbols._ +import util.HashSet +import typer.ConstFold + +trait TreeInfo[T >: Untyped <: Type] { self: Trees.Instance[T] => + import TreeInfo._ + + // Note: the <: Type constraint looks necessary (and is needed to make the file compile in dotc). + // But Scalac accepts the program happily without it. Need to find out why. + + def unsplice[T >: Untyped](tree: Trees.Tree[T]): Trees.Tree[T] = tree.asInstanceOf[untpd.Tree] match { + case untpd.TypedSplice(tree1) => tree1.asInstanceOf[Trees.Tree[T]] + case _ => tree + } + + def isDeclarationOrTypeDef(tree: Tree): Boolean = unsplice(tree) match { + case DefDef(_, _, _, _, EmptyTree) + | ValDef(_, _, EmptyTree) + | TypeDef(_, _) => true + case _ => false + } + + /** The largest subset of {NoInits, PureInterface} that a + * trait enclosing this statement can have as flags. + * Does tree contain an initialization part when seen as a member of a class or trait? + */ + def defKind(tree: Tree): FlagSet = unsplice(tree) match { + case EmptyTree | _: Import => NoInitsInterface + case tree: TypeDef => if (tree.isClassDef) NoInits else NoInitsInterface + case tree: DefDef => if (tree.unforcedRhs == EmptyTree) NoInitsInterface else NoInits + case tree: ValDef => if (tree.unforcedRhs == EmptyTree) NoInitsInterface else EmptyFlags + case _ => EmptyFlags + } + + def isOpAssign(tree: Tree) = unsplice(tree) match { + case Apply(fn, _ :: _) => + unsplice(fn) match { + case Select(_, name) if name.isOpAssignmentName => true + case _ => false + } + case _ => false + } + + class MatchingArgs(params: List[Symbol], args: List[Tree])(implicit ctx: Context) { + def foreach(f: (Symbol, Tree) => Unit): Boolean = { + def recur(params: List[Symbol], args: List[Tree]): Boolean = params match { + case Nil => args.isEmpty + case param :: params1 => + if (param.info.isRepeatedParam) { + for (arg <- args) f(param, arg) + true + } else args match { + case Nil => false + case arg :: args1 => + f(param, args.head) + recur(params1, args1) + } + } + recur(params, args) + } + def zipped: List[(Symbol, Tree)] = map((_, _)) + def map[R](f: (Symbol, Tree) => R): List[R] = { + val b = List.newBuilder[R] + foreach(b += f(_, _)) + b.result + } + } + + /** The method part of an application node, possibly enclosed in a block + * with only valdefs as statements. the reason for also considering blocks + * is that named arguments can transform a call into a block, e.g. + * <init>(b = foo, a = bar) + * is transformed to + * { val x$1 = foo + * val x$2 = bar + * <init>(x$2, x$1) + * } + */ + def methPart(tree: Tree): Tree = stripApply(tree) match { + case TypeApply(fn, _) => methPart(fn) + case AppliedTypeTree(fn, _) => methPart(fn) // !!! should not be needed + case Block(stats, expr) => methPart(expr) + case mp => mp + } + + /** If this is an application, its function part, stripping all + * Apply nodes (but leaving TypeApply nodes in). Otherwise the tree itself. + */ + def stripApply(tree: Tree): Tree = unsplice(tree) match { + case Apply(fn, _) => stripApply(fn) + case _ => tree + } + + /** The number of arguments in an application */ + def numArgs(tree: Tree): Int = unsplice(tree) match { + case Apply(fn, args) => numArgs(fn) + args.length + case TypeApply(fn, _) => numArgs(fn) + case Block(_, expr) => numArgs(expr) + case _ => 0 + } + + /** The (last) list of arguments of an application */ + def arguments(tree: Tree): List[Tree] = unsplice(tree) match { + case Apply(_, args) => args + case TypeApply(fn, _) => arguments(fn) + case Block(_, expr) => arguments(expr) + case _ => Nil + } + + /** 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 + } + + def isSuperSelection(tree: untpd.Tree) = unsplice(tree) match { + case Select(Super(_, _), _) => true + case _ => false + } + + def isSelfOrSuperConstrCall(tree: Tree): Boolean = methPart(tree) match { + case Ident(nme.CONSTRUCTOR) + | Select(This(_), nme.CONSTRUCTOR) + | Select(Super(_, _), nme.CONSTRUCTOR) => true + case _ => false + } + + /** Is tree a variable pattern? */ + def isVarPattern(pat: untpd.Tree): Boolean = unsplice(pat) match { + case x: BackquotedIdent => false + case x: Ident => x.name.isVariableName + case _ => false + } + + /** The first constructor definition in `stats` */ + def firstConstructor(stats: List[Tree]): Tree = stats match { + case (meth: DefDef) :: _ if meth.name.isConstructorName => meth + case stat :: stats => firstConstructor(stats) + case nil => 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 + } + + /** Is tpt a vararg type of the form T* or => T*? */ + def isRepeatedParamType(tpt: Tree)(implicit ctx: Context): Boolean = tpt match { + case ByNameTypeTree(tpt1) => isRepeatedParamType(tpt1) + case tpt: TypeTree => tpt.typeOpt.isRepeatedParam + case AppliedTypeTree(Select(_, tpnme.REPEATED_PARAM_CLASS), _) => true + case _ => false + } + + /** Is name a left-associative operator? */ + def isLeftAssoc(operator: Name) = operator.nonEmpty && (operator.last != ':') + + /** can this type be a type pattern? */ + def mayBeTypePat(tree: untpd.Tree): Boolean = unsplice(tree) match { + case AndTypeTree(tpt1, tpt2) => mayBeTypePat(tpt1) || mayBeTypePat(tpt2) + case OrTypeTree(tpt1, tpt2) => mayBeTypePat(tpt1) || mayBeTypePat(tpt2) + case RefinedTypeTree(tpt, refinements) => mayBeTypePat(tpt) || refinements.exists(_.isInstanceOf[Bind]) + case AppliedTypeTree(tpt, args) => mayBeTypePat(tpt) || args.exists(_.isInstanceOf[Bind]) + case Select(tpt, _) => mayBeTypePat(tpt) + case Annotated(tpt, _) => mayBeTypePat(tpt) + case _ => false + } + + /** Is this argument node of the form <expr> : _* ? + */ + def isWildcardStarArg(tree: Tree)(implicit ctx: Context): Boolean = unbind(tree) match { + case Typed(Ident(nme.WILDCARD_STAR), _) => true + case Typed(_, Ident(tpnme.WILDCARD_STAR)) => true + case Typed(_, tpt: TypeTree) => tpt.hasType && tpt.tpe.isRepeatedParam + case _ => false + } + + /** 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 <expr> : _* ? */ + def isWildcardStarArgList(trees: List[Tree])(implicit ctx: Context) = + 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 + } + + /** Does this list contain a named argument tree? */ + def hasNamedArg(args: List[Any]) = args exists isNamedArg + val isNamedArg = (arg: Any) => arg.isInstanceOf[Trees.NamedArg[_]] + + /** 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 + } + + /** Is this pattern node a synthetic catch-all case, added during PartialFuction synthesis before we know + * whether the user provided cases are exhaustive. */ + def isSyntheticDefaultCase(cdef: CaseDef) = unsplice(cdef) match { + case CaseDef(Bind(nme.DEFAULT_CASE, _), EmptyTree, _) => true + case _ => false + } + + /** Does this CaseDef catch Throwable? */ + def catchesThrowable(cdef: CaseDef)(implicit ctx: Context) = + catchesAllOf(cdef, defn.ThrowableType) + + /** Does this CaseDef catch everything of a certain Type? */ + def catchesAllOf(cdef: CaseDef, threshold: Type)(implicit ctx: Context) = + isDefaultCase(cdef) || + cdef.guard.isEmpty && { + unbind(cdef.pat) match { + case Typed(Ident(nme.WILDCARD), tpt) => threshold <:< tpt.typeOpt + case _ => false + } + } + + /** Is this case guarded? */ + def isGuardedCase(cdef: CaseDef) = cdef.guard ne EmptyTree + + /** The underlying pattern ignoring any bindings */ + def unbind(x: Tree): Tree = unsplice(x) match { + case Bind(_, y) => unbind(y) + case y => y + } + + /** Checks whether predicate `p` is true for all result parts of this expression, + * where we zoom into Ifs, Matches, and Blocks. + */ + def forallResults(tree: Tree, p: Tree => Boolean): Boolean = tree match { + case If(_, thenp, elsep) => forallResults(thenp, p) && forallResults(elsep, p) + case Match(_, cases) => cases forall (c => forallResults(c.body, p)) + case Block(_, expr) => forallResults(expr, p) + case _ => p(tree) + } +} + +trait UntypedTreeInfo extends TreeInfo[Untyped] { self: Trees.Instance[Untyped] => + import TreeInfo._ + import untpd._ + + /** True iff definition is a val or def with no right-hand-side, or it + * is an abstract typoe declaration + */ + def lacksDefinition(mdef: MemberDef)(implicit ctx: Context) = mdef match { + case mdef: ValOrDefDef => + mdef.unforcedRhs == EmptyTree && !mdef.name.isConstructorName && !mdef.mods.is(ParamAccessor) + case mdef: TypeDef => + def isBounds(rhs: Tree): Boolean = rhs match { + case _: TypeBoundsTree => true + case PolyTypeTree(_, body) => isBounds(body) + case _ => false + } + mdef.rhs.isEmpty || isBounds(mdef.rhs) + case _ => false + } + + def isFunctionWithUnknownParamType(tree: Tree) = tree match { + case Function(args, _) => + args.exists { + case ValDef(_, tpt, _) => tpt.isEmpty + case _ => false + } + case _ => false + } + + // todo: fill with other methods from TreeInfo that only apply to untpd.Tree's +} + +trait TypedTreeInfo extends TreeInfo[Type] { self: Trees.Instance[Type] => + import TreeInfo._ + import tpd._ + + /** The purity level of this statement. + * @return pure if statement has no side effects + * idempotent if running the statement a second time has no side effects + * impure otherwise + */ + private def statPurity(tree: Tree)(implicit ctx: Context): PurityLevel = unsplice(tree) match { + case EmptyTree + | TypeDef(_, _) + | Import(_, _) + | DefDef(_, _, _, _, _) => + Pure + case vdef @ ValDef(_, _, _) => + if (vdef.symbol.flags is Mutable) Impure else exprPurity(vdef.rhs) + case _ => + Impure + // TODO: It seem like this should be exprPurity(tree) + // But if we do that the repl/vars test break. Need to figure out why that's the case. + } + + /** The purity level of this expression. + * @return pure if expression has no side effects + * idempotent if running the expression a second time has no side effects + * impure otherwise + * + * Note that purity and idempotency are different. References to modules and lazy + * vals are impure (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 idempotent + * because running the expression a second time gives the cached result. + */ + private def exprPurity(tree: Tree)(implicit ctx: Context): PurityLevel = unsplice(tree) match { + case EmptyTree + | This(_) + | Super(_, _) + | Literal(_) + | Closure(_, _, _) => + Pure + case Ident(_) => + refPurity(tree) + case Select(qual, _) => + refPurity(tree).min(exprPurity(qual)) + case TypeApply(fn, _) => + exprPurity(fn) +/* + * Not sure we'll need that. Comment out until we find out + case Apply(Select(free @ Ident(_), nme.apply), _) if free.symbol.name endsWith nme.REIFY_FREE_VALUE_SUFFIX => + // see a detailed explanation of this trick in `GenSymbols.reifyFreeTerm` + free.symbol.hasStableFlag && isIdempotentExpr(free) +*/ + case Apply(fn, args) => + def isKnownPureOp(sym: Symbol) = + sym.owner.isPrimitiveValueClass || sym.owner == defn.StringClass + // Note: After uncurry, field accesses are represented as Apply(getter, Nil), + // so an Apply can also be pure. + if (args.isEmpty && fn.symbol.is(Stable)) exprPurity(fn) + else if (tree.tpe.isInstanceOf[ConstantType] && isKnownPureOp(tree.symbol)) + // A constant expression with pure arguments is pure. + minOf(exprPurity(fn), args.map(exprPurity)) + else Impure + case Typed(expr, _) => + exprPurity(expr) + case Block(stats, expr) => + minOf(exprPurity(expr), stats.map(statPurity)) + case _ => + Impure + } + + private def minOf(l0: PurityLevel, ls: List[PurityLevel]) = (l0 /: ls)(_ min _) + + def isPureExpr(tree: Tree)(implicit ctx: Context) = exprPurity(tree) == Pure + def isIdempotentExpr(tree: Tree)(implicit ctx: Context) = exprPurity(tree) >= Idempotent + + /** The purity level of this reference. + * @return + * pure if reference is (nonlazy and stable) or to a parameterized function + * idempotent if reference is lazy and stable + * impure otherwise + * @DarkDimius: need to make sure that lazy accessor methods have Lazy and Stable + * flags set. + */ + private def refPurity(tree: Tree)(implicit ctx: Context): PurityLevel = + if (!tree.tpe.widen.isParameterless) Pure + else if (!tree.symbol.isStable) Impure + else if (tree.symbol.is(Lazy)) Idempotent // TODO add Module flag, sinxce Module vals or not Lazy from the start. + else Pure + + def isPureRef(tree: Tree)(implicit ctx: Context) = + refPurity(tree) == Pure + def isIdempotentRef(tree: Tree)(implicit ctx: Context) = + refPurity(tree) >= Idempotent + + /** If `tree` is a constant expression, its value as a Literal, + * or `tree` itself otherwise. + * + * Note: Demanding idempotency instead of purity in literalize is strictly speaking too loose. + * Example + * + * object O { final val x = 42; println("43") } + * O.x + * + * Strictly speaking we can't replace `O.x` with `42`. But this would make + * most expressions non-constant. Maybe we can change the spec to accept this + * kind of eliding behavior. Or else enforce true purity in the compiler. + * The choice will be affected by what we will do with `inline` and with + * Singleton type bounds (see SIP 23). Presumably + * + * object O1 { val x: Singleton = 42; println("43") } + * object O2 { inline val x = 42; println("43") } + * + * should behave differently. + * + * O1.x should have the same effect as { println("43"); 42 } + * + * whereas + * + * O2.x = 42 + * + * Revisit this issue once we have implemented `inline`. Then we can demand + * purity of the prefix unless the selection goes to an inline val. + * + * Note: This method should be applied to all term tree nodes that are not literals, + * that can be idempotent, and that can have constant types. So far, only nodes + * of the following classes qualify: + * + * Ident + * Select + * TypeApply + */ + def constToLiteral(tree: Tree)(implicit ctx: Context): Tree = { + val tree1 = ConstFold(tree) + tree1.tpe.widenTermRefExpr match { + case ConstantType(value) if isIdempotentExpr(tree1) => Literal(value) + case _ => tree1 + } + } + + /** Is symbol potentially a getter of a mutable variable? + */ + def mayBeVarGetter(sym: Symbol)(implicit ctx: Context): Boolean = { + def maybeGetterType(tpe: Type): Boolean = tpe match { + case _: ExprType | _: ImplicitMethodType => true + case tpe: PolyType => maybeGetterType(tpe.resultType) + case _ => false + } + sym.owner.isClass && !sym.isStable && maybeGetterType(sym.info) + } + + /** Is tree a reference to a mutable variable, or to a potential getter + * that has a setter in the same class? + */ + def isVariableOrGetter(tree: Tree)(implicit ctx: Context) = { + def sym = tree.symbol + def isVar = sym is Mutable + def isGetter = + mayBeVarGetter(sym) && sym.owner.info.member(sym.name.asTermName.setterName).exists + + unsplice(tree) match { + case Ident(_) => isVar + case Select(_, _) => isVar || isGetter + case Apply(_, _) => + methPart(tree) match { + case Select(qual, nme.apply) => qual.tpe.member(nme.update).exists + case _ => false + } + case _ => false + } + } + + /** Is tree a `this` node which belongs to `enclClass`? */ + def isSelf(tree: Tree, enclClass: Symbol)(implicit ctx: Context): Boolean = unsplice(tree) match { + case This(_) => tree.symbol == enclClass + case _ => false + } + + /** Strips layers of `.asInstanceOf[T]` / `_.$asInstanceOf[T]()` from an expression */ + def stripCast(tree: Tree)(implicit ctx: Context): Tree = { + def isCast(sel: Tree) = sel.symbol == defn.Any_asInstanceOf + unsplice(tree) match { + case TypeApply(sel @ Select(inner, _), _) if isCast(sel) => + stripCast(inner) + case Apply(TypeApply(sel @ Select(inner, _), _), Nil) if isCast(sel) => + stripCast(inner) + case t => + t + } + } + + /** Decompose a call fn[targs](vargs_1)...(vargs_n) + * into its constituents (where targs, vargss may be empty) + */ + def decomposeCall(tree: Tree): (Tree, List[Tree], List[List[Tree]]) = tree match { + case Apply(fn, args) => + val (meth, targs, argss) = decomposeCall(fn) + (meth, targs, argss :+ args) + case TypeApply(fn, targs) => + val (meth, Nil, Nil) = decomposeCall(fn) + (meth, targs, Nil) + case _ => + (tree, Nil, Nil) + } + + /** An extractor for closures, either contained in a block or standalone. + */ + object closure { + def unapply(tree: Tree): Option[(List[Tree], Tree, Tree)] = tree match { + case Block(_, Closure(env, meth, tpt)) => Some(env, meth, tpt) + case Closure(env, meth, tpt) => Some(env, meth, tpt) + case _ => None + } + } + + /** If tree is a closure, its body, otherwise tree itself */ + def closureBody(tree: Tree)(implicit ctx: Context): Tree = tree match { + case Block((meth @ DefDef(nme.ANON_FUN, _, _, _, _)) :: Nil, Closure(_, _, _)) => meth.rhs + case _ => tree + } + + /** The variables defined by a pattern, in reverse order of their appearance. */ + def patVars(tree: Tree)(implicit ctx: Context): List[Symbol] = { + val acc = new TreeAccumulator[List[Symbol]] { + def apply(syms: List[Symbol], tree: Tree)(implicit ctx: Context) = tree match { + case Bind(_, body) => apply(tree.symbol :: syms, body) + case _ => foldOver(syms, tree) + } + } + acc(Nil, tree) + } + + /** Is this pattern node a catch-all or type-test pattern? */ + def isCatchCase(cdef: CaseDef)(implicit ctx: Context) = 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)(implicit ctx: Context): Boolean = tp match { + case tp @ TypeRef(pre, _) => + (pre == NoPrefix || pre.widen.typeSymbol.isStatic) && + (tp.symbol derivesFrom defn.ThrowableClass) && !(tp.symbol is Trait) + case _ => + false + } + + /** The symbols defined locally in a statement list */ + def localSyms(stats: List[Tree])(implicit ctx: Context): List[Symbol] = + for (stat <- stats if stat.isDef && stat.symbol.exists) yield stat.symbol + + /** If `tree` is a DefTree, the symbol defined by it, otherwise NoSymbol */ + def definedSym(tree: Tree)(implicit ctx: Context): Symbol = + if (tree.isDef) tree.symbol else NoSymbol + + /** Going from child to parent, the path of tree nodes that starts + * with a definition of symbol `sym` and ends with `root`, or Nil + * if no such path exists. + * Pre: `sym` must have a position. + */ + def defPath(sym: Symbol, root: Tree)(implicit ctx: Context): List[Tree] = ctx.debugTraceIndented(s"defpath($sym with position ${sym.pos}, ${root.show})") { + require(sym.pos.exists) + object accum extends TreeAccumulator[List[Tree]] { + def apply(x: List[Tree], tree: Tree)(implicit ctx: Context): List[Tree] = { + if (tree.pos.contains(sym.pos)) + if (definedSym(tree) == sym) tree :: x + else { + val x1 = foldOver(x, tree) + if (x1 ne x) tree :: x1 else x1 + } + else x + } + } + accum(Nil, root) + } + + + /** The top level classes in this tree, including only those module classes that + * are not a linked class of some other class in the result. + */ + def topLevelClasses(tree: Tree)(implicit ctx: Context): List[ClassSymbol] = tree match { + case PackageDef(_, stats) => stats.flatMap(topLevelClasses) + case tdef: TypeDef if tdef.symbol.isClass => tdef.symbol.asClass :: Nil + case _ => Nil + } + + /** The tree containing only the top-level classes and objects matching either `cls` or its companion object */ + def sliceTopLevel(tree: Tree, cls: ClassSymbol)(implicit ctx: Context): List[Tree] = tree match { + case PackageDef(pid, stats) => + cpy.PackageDef(tree)(pid, stats.flatMap(sliceTopLevel(_, cls))) :: Nil + case tdef: TypeDef => + val sym = tdef.symbol + assert(sym.isClass) + if (cls == sym || cls == sym.linkedClass) tdef :: Nil + else Nil + case vdef: ValDef => + val sym = vdef.symbol + assert(sym is Module) + if (cls == sym.companionClass || cls == sym.moduleClass) vdef :: Nil + else Nil + case tree => + tree :: Nil + } + + /** The statement sequence that contains a definition of `sym`, or Nil + * if none was found. + * For a tree to be found, The symbol must have a position and its definition + * tree must be reachable from come tree stored in an enclosing context. + */ + def definingStats(sym: Symbol)(implicit ctx: Context): List[Tree] = + if (!sym.pos.exists || (ctx eq NoContext) || ctx.compilationUnit == null) Nil + else defPath(sym, ctx.compilationUnit.tpdTree) match { + case defn :: encl :: _ => + def verify(stats: List[Tree]) = + if (stats exists (definedSym(_) == sym)) stats else Nil + encl match { + case Block(stats, _) => verify(stats) + case encl: Template => verify(encl.body) + case PackageDef(_, stats) => verify(stats) + case _ => Nil + } + case nil => + Nil + } +} + +object TreeInfo { + class PurityLevel(val x: Int) extends AnyVal { + def >= (that: PurityLevel) = x >= that.x + def min(that: PurityLevel) = new PurityLevel(x min that.x) + } + + val Pure = new PurityLevel(2) + val Idempotent = new PurityLevel(1) + val Impure = new PurityLevel(0) +} + + /** a Match(Typed(_, tpt), _) must be translated into a switch if isSwitchAnnotation(tpt.tpe) + def isSwitchAnnotation(tpe: Type) = tpe hasAnnotation defn.SwitchClass + */ + + /** 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 isLeadingPredefImport(defn: Tree): Boolean = defn match { + case PackageDef(_, defs1) => defs1 exists isLeadingPredefImport + case Import(expr, _) => isReferenceToPredef(expr) + 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) || isLeadingPredefImport(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 + } + } + + object MacroImplReference { + private def refPart(tree: Tree): Tree = tree match { + case TypeApply(fun, _) => refPart(fun) + case ref: RefTree => ref + case _ => EmptyTree() + } + + def unapply(tree: Tree) = refPart(tree) match { + case ref: RefTree => Some((ref.qualifier.symbol, ref.symbol, dissectApplied(tree).targs)) + case _ => None + } + } + + def isNullaryInvocation(tree: Tree): Boolean = + tree.symbol != null && tree.symbol.isMethod && (tree match { + case TypeApply(fun, _) => isNullaryInvocation(fun) + case tree: RefTree => true + case _ => false + })*/ + + + |