src/jdk.compiler/share/classes/com/sun/tools/javac/tree/TreeInfo.java
changeset 47216 71c04702a3d5
parent 45220 7c775294b6f1
child 47268 48ec75306997
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/TreeInfo.java	Tue Sep 12 19:03:39 2017 +0200
@@ -0,0 +1,1205 @@
+/*
+ * Copyright (c) 1999, 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package com.sun.tools.javac.tree;
+
+
+
+import com.sun.source.tree.Tree;
+import com.sun.source.util.TreePath;
+import com.sun.tools.javac.code.*;
+import com.sun.tools.javac.comp.AttrContext;
+import com.sun.tools.javac.comp.Env;
+import com.sun.tools.javac.tree.JCTree.*;
+import com.sun.tools.javac.tree.JCTree.JCPolyExpression.*;
+import com.sun.tools.javac.util.*;
+import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition;
+
+import static com.sun.tools.javac.code.Flags.*;
+import static com.sun.tools.javac.code.Kinds.Kind.*;
+import static com.sun.tools.javac.code.TypeTag.BOT;
+import static com.sun.tools.javac.tree.JCTree.Tag.*;
+import static com.sun.tools.javac.tree.JCTree.Tag.BLOCK;
+import static com.sun.tools.javac.tree.JCTree.Tag.SYNCHRONIZED;
+
+import javax.tools.JavaFileObject;
+
+import java.util.function.ToIntFunction;
+
+import static com.sun.tools.javac.tree.JCTree.JCOperatorExpression.OperandPos.LEFT;
+import static com.sun.tools.javac.tree.JCTree.JCOperatorExpression.OperandPos.RIGHT;
+
+/** Utility class containing inspector methods for trees.
+ *
+ *  <p><b>This is NOT part of any supported API.
+ *  If you write code that depends on this, you do so at your own risk.
+ *  This code and its internal interfaces are subject to change or
+ *  deletion without notice.</b>
+ */
+public class TreeInfo {
+
+    public static List<JCExpression> args(JCTree t) {
+        switch (t.getTag()) {
+            case APPLY:
+                return ((JCMethodInvocation)t).args;
+            case NEWCLASS:
+                return ((JCNewClass)t).args;
+            default:
+                return null;
+        }
+    }
+
+    /** Is tree a constructor declaration?
+     */
+    public static boolean isConstructor(JCTree tree) {
+        if (tree.hasTag(METHODDEF)) {
+            Name name = ((JCMethodDecl) tree).name;
+            return name == name.table.names.init;
+        } else {
+            return false;
+        }
+    }
+
+    public static boolean isReceiverParam(JCTree tree) {
+        if (tree.hasTag(VARDEF)) {
+            return ((JCVariableDecl)tree).nameexpr != null;
+        } else {
+            return false;
+        }
+    }
+
+    /** Is there a constructor declaration in the given list of trees?
+     */
+    public static boolean hasConstructors(List<JCTree> trees) {
+        for (List<JCTree> l = trees; l.nonEmpty(); l = l.tail)
+            if (isConstructor(l.head)) return true;
+        return false;
+    }
+
+    public static boolean isMultiCatch(JCCatch catchClause) {
+        return catchClause.param.vartype.hasTag(TYPEUNION);
+    }
+
+    /** Is statement an initializer for a synthetic field?
+     */
+    public static boolean isSyntheticInit(JCTree stat) {
+        if (stat.hasTag(EXEC)) {
+            JCExpressionStatement exec = (JCExpressionStatement)stat;
+            if (exec.expr.hasTag(ASSIGN)) {
+                JCAssign assign = (JCAssign)exec.expr;
+                if (assign.lhs.hasTag(SELECT)) {
+                    JCFieldAccess select = (JCFieldAccess)assign.lhs;
+                    if (select.sym != null &&
+                        (select.sym.flags() & SYNTHETIC) != 0) {
+                        Name selected = name(select.selected);
+                        if (selected != null && selected == selected.table.names._this)
+                            return true;
+                    }
+                }
+            }
+        }
+        return false;
+    }
+
+    /** If the expression is a method call, return the method name, null
+     *  otherwise. */
+    public static Name calledMethodName(JCTree tree) {
+        if (tree.hasTag(EXEC)) {
+            JCExpressionStatement exec = (JCExpressionStatement)tree;
+            if (exec.expr.hasTag(APPLY)) {
+                Name mname = TreeInfo.name(((JCMethodInvocation) exec.expr).meth);
+                return mname;
+            }
+        }
+        return null;
+    }
+
+    /** Is this a call to this or super?
+     */
+    public static boolean isSelfCall(JCTree tree) {
+        Name name = calledMethodName(tree);
+        if (name != null) {
+            Names names = name.table.names;
+            return name==names._this || name==names._super;
+        } else {
+            return false;
+        }
+    }
+
+    /** Is this a call to super?
+     */
+    public static boolean isSuperCall(JCTree tree) {
+        Name name = calledMethodName(tree);
+        if (name != null) {
+            Names names = name.table.names;
+            return name==names._super;
+        } else {
+            return false;
+        }
+    }
+
+    /** Is this a constructor whose first (non-synthetic) statement is not
+     *  of the form this(...)?
+     */
+    public static boolean isInitialConstructor(JCTree tree) {
+        JCMethodInvocation app = firstConstructorCall(tree);
+        if (app == null) return false;
+        Name meth = name(app.meth);
+        return meth == null || meth != meth.table.names._this;
+    }
+
+    /** Return the first call in a constructor definition. */
+    public static JCMethodInvocation firstConstructorCall(JCTree tree) {
+        if (!tree.hasTag(METHODDEF)) return null;
+        JCMethodDecl md = (JCMethodDecl) tree;
+        Names names = md.name.table.names;
+        if (md.name != names.init) return null;
+        if (md.body == null) return null;
+        List<JCStatement> stats = md.body.stats;
+        // Synthetic initializations can appear before the super call.
+        while (stats.nonEmpty() && isSyntheticInit(stats.head))
+            stats = stats.tail;
+        if (stats.isEmpty()) return null;
+        if (!stats.head.hasTag(EXEC)) return null;
+        JCExpressionStatement exec = (JCExpressionStatement) stats.head;
+        if (!exec.expr.hasTag(APPLY)) return null;
+        return (JCMethodInvocation)exec.expr;
+    }
+
+    /** Return true if a tree represents a diamond new expr. */
+    public static boolean isDiamond(JCTree tree) {
+        switch(tree.getTag()) {
+            case TYPEAPPLY: return ((JCTypeApply)tree).getTypeArguments().isEmpty();
+            case NEWCLASS: return isDiamond(((JCNewClass)tree).clazz);
+            case ANNOTATED_TYPE: return isDiamond(((JCAnnotatedType)tree).underlyingType);
+            default: return false;
+        }
+    }
+
+    /** Return true if the given tree represents a type elided anonymous class instance creation. */
+    public static boolean isAnonymousDiamond(JCTree tree) {
+        switch(tree.getTag()) {
+            case NEWCLASS:  {
+                JCNewClass nc = (JCNewClass)tree;
+                return nc.def != null && isDiamond(nc.clazz);
+            }
+            case ANNOTATED_TYPE: return isAnonymousDiamond(((JCAnnotatedType)tree).underlyingType);
+            default: return false;
+        }
+    }
+
+    public static boolean isEnumInit(JCTree tree) {
+        switch (tree.getTag()) {
+            case VARDEF:
+                return (((JCVariableDecl)tree).mods.flags & ENUM) != 0;
+            default:
+                return false;
+        }
+    }
+
+    /** set 'polyKind' on given tree */
+    public static void setPolyKind(JCTree tree, PolyKind pkind) {
+        switch (tree.getTag()) {
+            case APPLY:
+                ((JCMethodInvocation)tree).polyKind = pkind;
+                break;
+            case NEWCLASS:
+                ((JCNewClass)tree).polyKind = pkind;
+                break;
+            case REFERENCE:
+                ((JCMemberReference)tree).refPolyKind = pkind;
+                break;
+            default:
+                throw new AssertionError("Unexpected tree: " + tree);
+        }
+    }
+
+    /** set 'varargsElement' on given tree */
+    public static void setVarargsElement(JCTree tree, Type varargsElement) {
+        switch (tree.getTag()) {
+            case APPLY:
+                ((JCMethodInvocation)tree).varargsElement = varargsElement;
+                break;
+            case NEWCLASS:
+                ((JCNewClass)tree).varargsElement = varargsElement;
+                break;
+            case REFERENCE:
+                ((JCMemberReference)tree).varargsElement = varargsElement;
+                break;
+            default:
+                throw new AssertionError("Unexpected tree: " + tree);
+        }
+    }
+
+    /** Return true if the tree corresponds to an expression statement */
+    public static boolean isExpressionStatement(JCExpression tree) {
+        switch(tree.getTag()) {
+            case PREINC: case PREDEC:
+            case POSTINC: case POSTDEC:
+            case ASSIGN:
+            case BITOR_ASG: case BITXOR_ASG: case BITAND_ASG:
+            case SL_ASG: case SR_ASG: case USR_ASG:
+            case PLUS_ASG: case MINUS_ASG:
+            case MUL_ASG: case DIV_ASG: case MOD_ASG:
+            case APPLY: case NEWCLASS:
+            case ERRONEOUS:
+                return true;
+            default:
+                return false;
+        }
+    }
+
+    /** Return true if the tree corresponds to a statement */
+    public static boolean isStatement(JCTree tree) {
+        return (tree instanceof JCStatement) &&
+                !tree.hasTag(CLASSDEF) &&
+                !tree.hasTag(Tag.BLOCK) &&
+                !tree.hasTag(METHODDEF);
+    }
+
+    /**
+     * Return true if the AST corresponds to a static select of the kind A.B
+     */
+    public static boolean isStaticSelector(JCTree base, Names names) {
+        if (base == null)
+            return false;
+        switch (base.getTag()) {
+            case IDENT:
+                JCIdent id = (JCIdent)base;
+                return id.name != names._this &&
+                        id.name != names._super &&
+                        isStaticSym(base);
+            case SELECT:
+                return isStaticSym(base) &&
+                    isStaticSelector(((JCFieldAccess)base).selected, names);
+            case TYPEAPPLY:
+            case TYPEARRAY:
+                return true;
+            case ANNOTATED_TYPE:
+                return isStaticSelector(((JCAnnotatedType)base).underlyingType, names);
+            default:
+                return false;
+        }
+    }
+    //where
+        private static boolean isStaticSym(JCTree tree) {
+            Symbol sym = symbol(tree);
+            return (sym.kind == TYP || sym.kind == PCK);
+        }
+
+    /** Return true if a tree represents the null literal. */
+    public static boolean isNull(JCTree tree) {
+        if (!tree.hasTag(LITERAL))
+            return false;
+        JCLiteral lit = (JCLiteral) tree;
+        return (lit.typetag == BOT);
+    }
+
+    /** Return true iff this tree is a child of some annotation. */
+    public static boolean isInAnnotation(Env<?> env, JCTree tree) {
+        TreePath tp = TreePath.getPath(env.toplevel, tree);
+        if (tp != null) {
+            for (Tree t : tp) {
+                if (t.getKind() == Tree.Kind.ANNOTATION)
+                    return true;
+            }
+        }
+        return false;
+    }
+
+    public static String getCommentText(Env<?> env, JCTree tree) {
+        DocCommentTable docComments = (tree.hasTag(JCTree.Tag.TOPLEVEL))
+                ? ((JCCompilationUnit) tree).docComments
+                : env.toplevel.docComments;
+        return (docComments == null) ? null : docComments.getCommentText(tree);
+    }
+
+    public static DCTree.DCDocComment getCommentTree(Env<?> env, JCTree tree) {
+        DocCommentTable docComments = (tree.hasTag(JCTree.Tag.TOPLEVEL))
+                ? ((JCCompilationUnit) tree).docComments
+                : env.toplevel.docComments;
+        return (docComments == null) ? null : docComments.getCommentTree(tree);
+    }
+
+    /** The position of the first statement in a block, or the position of
+     *  the block itself if it is empty.
+     */
+    public static int firstStatPos(JCTree tree) {
+        if (tree.hasTag(BLOCK) && ((JCBlock) tree).stats.nonEmpty())
+            return ((JCBlock) tree).stats.head.pos;
+        else
+            return tree.pos;
+    }
+
+    /** The end position of given tree, if it is a block with
+     *  defined endpos.
+     */
+    public static int endPos(JCTree tree) {
+        if (tree.hasTag(BLOCK) && ((JCBlock) tree).endpos != Position.NOPOS)
+            return ((JCBlock) tree).endpos;
+        else if (tree.hasTag(SYNCHRONIZED))
+            return endPos(((JCSynchronized) tree).body);
+        else if (tree.hasTag(TRY)) {
+            JCTry t = (JCTry) tree;
+            return endPos((t.finalizer != null) ? t.finalizer
+                          : (t.catchers.nonEmpty() ? t.catchers.last().body : t.body));
+        } else
+            return tree.pos;
+    }
+
+
+    /** Get the start position for a tree node.  The start position is
+     * defined to be the position of the first character of the first
+     * token of the node's source text.
+     * @param tree  The tree node
+     */
+    public static int getStartPos(JCTree tree) {
+        if (tree == null)
+            return Position.NOPOS;
+
+        switch(tree.getTag()) {
+            case MODULEDEF: {
+                JCModuleDecl md = (JCModuleDecl)tree;
+                return md.mods.annotations.isEmpty() ? md.pos :
+                       md.mods.annotations.head.pos;
+            }
+            case PACKAGEDEF: {
+                JCPackageDecl pd = (JCPackageDecl)tree;
+                return pd.annotations.isEmpty() ? pd.pos :
+                       pd.annotations.head.pos;
+            }
+            case APPLY:
+                return getStartPos(((JCMethodInvocation) tree).meth);
+            case ASSIGN:
+                return getStartPos(((JCAssign) tree).lhs);
+            case BITOR_ASG: case BITXOR_ASG: case BITAND_ASG:
+            case SL_ASG: case SR_ASG: case USR_ASG:
+            case PLUS_ASG: case MINUS_ASG: case MUL_ASG:
+            case DIV_ASG: case MOD_ASG:
+            case OR: case AND: case BITOR:
+            case BITXOR: case BITAND: case EQ:
+            case NE: case LT: case GT:
+            case LE: case GE: case SL:
+            case SR: case USR: case PLUS:
+            case MINUS: case MUL: case DIV:
+            case MOD:
+            case POSTINC:
+            case POSTDEC:
+                return getStartPos(((JCOperatorExpression) tree).getOperand(LEFT));
+            case CLASSDEF: {
+                JCClassDecl node = (JCClassDecl)tree;
+                if (node.mods.pos != Position.NOPOS)
+                    return node.mods.pos;
+                break;
+            }
+            case CONDEXPR:
+                return getStartPos(((JCConditional) tree).cond);
+            case EXEC:
+                return getStartPos(((JCExpressionStatement) tree).expr);
+            case INDEXED:
+                return getStartPos(((JCArrayAccess) tree).indexed);
+            case METHODDEF: {
+                JCMethodDecl node = (JCMethodDecl)tree;
+                if (node.mods.pos != Position.NOPOS)
+                    return node.mods.pos;
+                if (node.typarams.nonEmpty()) // List.nil() used for no typarams
+                    return getStartPos(node.typarams.head);
+                return node.restype == null ? node.pos : getStartPos(node.restype);
+            }
+            case SELECT:
+                return getStartPos(((JCFieldAccess) tree).selected);
+            case TYPEAPPLY:
+                return getStartPos(((JCTypeApply) tree).clazz);
+            case TYPEARRAY:
+                return getStartPos(((JCArrayTypeTree) tree).elemtype);
+            case TYPETEST:
+                return getStartPos(((JCInstanceOf) tree).expr);
+            case ANNOTATED_TYPE: {
+                JCAnnotatedType node = (JCAnnotatedType) tree;
+                if (node.annotations.nonEmpty()) {
+                    if (node.underlyingType.hasTag(TYPEARRAY) ||
+                            node.underlyingType.hasTag(SELECT)) {
+                        return getStartPos(node.underlyingType);
+                    } else {
+                        return getStartPos(node.annotations.head);
+                    }
+                } else {
+                    return getStartPos(node.underlyingType);
+                }
+            }
+            case NEWCLASS: {
+                JCNewClass node = (JCNewClass)tree;
+                if (node.encl != null)
+                    return getStartPos(node.encl);
+                break;
+            }
+            case VARDEF: {
+                JCVariableDecl node = (JCVariableDecl)tree;
+                if (node.mods.pos != Position.NOPOS) {
+                    return node.mods.pos;
+                } else if (node.vartype == null) {
+                    //if there's no type (partially typed lambda parameter)
+                    //simply return node position
+                    return node.pos;
+                } else {
+                    return getStartPos(node.vartype);
+                }
+            }
+            case ERRONEOUS: {
+                JCErroneous node = (JCErroneous)tree;
+                if (node.errs != null && node.errs.nonEmpty())
+                    return getStartPos(node.errs.head);
+            }
+        }
+        return tree.pos;
+    }
+
+    /** The end position of given tree, given  a table of end positions generated by the parser
+     */
+    public static int getEndPos(JCTree tree, EndPosTable endPosTable) {
+        if (tree == null)
+            return Position.NOPOS;
+
+        if (endPosTable == null) {
+            // fall back on limited info in the tree
+            return endPos(tree);
+        }
+
+        int mapPos = endPosTable.getEndPos(tree);
+        if (mapPos != Position.NOPOS)
+            return mapPos;
+
+        switch(tree.getTag()) {
+            case BITOR_ASG: case BITXOR_ASG: case BITAND_ASG:
+            case SL_ASG: case SR_ASG: case USR_ASG:
+            case PLUS_ASG: case MINUS_ASG: case MUL_ASG:
+            case DIV_ASG: case MOD_ASG:
+            case OR: case AND: case BITOR:
+            case BITXOR: case BITAND: case EQ:
+            case NE: case LT: case GT:
+            case LE: case GE: case SL:
+            case SR: case USR: case PLUS:
+            case MINUS: case MUL: case DIV:
+            case MOD:
+            case POS:
+            case NEG:
+            case NOT:
+            case COMPL:
+            case PREINC:
+            case PREDEC:
+                return getEndPos(((JCOperatorExpression) tree).getOperand(RIGHT), endPosTable);
+            case CASE:
+                return getEndPos(((JCCase) tree).stats.last(), endPosTable);
+            case CATCH:
+                return getEndPos(((JCCatch) tree).body, endPosTable);
+            case CONDEXPR:
+                return getEndPos(((JCConditional) tree).falsepart, endPosTable);
+            case FORLOOP:
+                return getEndPos(((JCForLoop) tree).body, endPosTable);
+            case FOREACHLOOP:
+                return getEndPos(((JCEnhancedForLoop) tree).body, endPosTable);
+            case IF: {
+                JCIf node = (JCIf)tree;
+                if (node.elsepart == null) {
+                    return getEndPos(node.thenpart, endPosTable);
+                } else {
+                    return getEndPos(node.elsepart, endPosTable);
+                }
+            }
+            case LABELLED:
+                return getEndPos(((JCLabeledStatement) tree).body, endPosTable);
+            case MODIFIERS:
+                return getEndPos(((JCModifiers) tree).annotations.last(), endPosTable);
+            case SYNCHRONIZED:
+                return getEndPos(((JCSynchronized) tree).body, endPosTable);
+            case TOPLEVEL:
+                return getEndPos(((JCCompilationUnit) tree).defs.last(), endPosTable);
+            case TRY: {
+                JCTry node = (JCTry)tree;
+                if (node.finalizer != null) {
+                    return getEndPos(node.finalizer, endPosTable);
+                } else if (!node.catchers.isEmpty()) {
+                    return getEndPos(node.catchers.last(), endPosTable);
+                } else {
+                    return getEndPos(node.body, endPosTable);
+                }
+            }
+            case WILDCARD:
+                return getEndPos(((JCWildcard) tree).inner, endPosTable);
+            case TYPECAST:
+                return getEndPos(((JCTypeCast) tree).expr, endPosTable);
+            case TYPETEST:
+                return getEndPos(((JCInstanceOf) tree).clazz, endPosTable);
+            case WHILELOOP:
+                return getEndPos(((JCWhileLoop) tree).body, endPosTable);
+            case ANNOTATED_TYPE:
+                return getEndPos(((JCAnnotatedType) tree).underlyingType, endPosTable);
+            case ERRONEOUS: {
+                JCErroneous node = (JCErroneous)tree;
+                if (node.errs != null && node.errs.nonEmpty())
+                    return getEndPos(node.errs.last(), endPosTable);
+            }
+        }
+        return Position.NOPOS;
+    }
+
+
+    /** A DiagnosticPosition with the preferred position set to the
+     *  end position of given tree, if it is a block with
+     *  defined endpos.
+     */
+    public static DiagnosticPosition diagEndPos(final JCTree tree) {
+        final int endPos = TreeInfo.endPos(tree);
+        return new DiagnosticPosition() {
+            public JCTree getTree() { return tree; }
+            public int getStartPosition() { return TreeInfo.getStartPos(tree); }
+            public int getPreferredPosition() { return endPos; }
+            public int getEndPosition(EndPosTable endPosTable) {
+                return TreeInfo.getEndPos(tree, endPosTable);
+            }
+        };
+    }
+
+    public enum PosKind {
+        START_POS(TreeInfo::getStartPos),
+        FIRST_STAT_POS(TreeInfo::firstStatPos),
+        END_POS(TreeInfo::endPos);
+
+        final ToIntFunction<JCTree> posFunc;
+
+        PosKind(ToIntFunction<JCTree> posFunc) {
+            this.posFunc = posFunc;
+        }
+
+        int toPos(JCTree tree) {
+            return posFunc.applyAsInt(tree);
+        }
+    }
+
+    /** The position of the finalizer of given try/synchronized statement.
+     */
+    public static int finalizerPos(JCTree tree, PosKind posKind) {
+        if (tree.hasTag(TRY)) {
+            JCTry t = (JCTry) tree;
+            Assert.checkNonNull(t.finalizer);
+            return posKind.toPos(t.finalizer);
+        } else if (tree.hasTag(SYNCHRONIZED)) {
+            return endPos(((JCSynchronized) tree).body);
+        } else {
+            throw new AssertionError();
+        }
+    }
+
+    /** Find the position for reporting an error about a symbol, where
+     *  that symbol is defined somewhere in the given tree. */
+    public static int positionFor(final Symbol sym, final JCTree tree) {
+        JCTree decl = declarationFor(sym, tree);
+        return ((decl != null) ? decl : tree).pos;
+    }
+
+    /** Find the position for reporting an error about a symbol, where
+     *  that symbol is defined somewhere in the given tree. */
+    public static DiagnosticPosition diagnosticPositionFor(final Symbol sym, final JCTree tree) {
+        JCTree decl = declarationFor(sym, tree);
+        return ((decl != null) ? decl : tree).pos();
+    }
+
+    /** Find the declaration for a symbol, where
+     *  that symbol is defined somewhere in the given tree. */
+    public static JCTree declarationFor(final Symbol sym, final JCTree tree) {
+        class DeclScanner extends TreeScanner {
+            JCTree result = null;
+            public void scan(JCTree tree) {
+                if (tree!=null && result==null)
+                    tree.accept(this);
+            }
+            public void visitTopLevel(JCCompilationUnit that) {
+                if (that.packge == sym) result = that;
+                else super.visitTopLevel(that);
+            }
+            public void visitModuleDef(JCModuleDecl that) {
+                if (that.sym == sym) result = that;
+                // no need to scan within module declaration
+            }
+            public void visitPackageDef(JCPackageDecl that) {
+                if (that.packge == sym) result = that;
+                else super.visitPackageDef(that);
+            }
+            public void visitClassDef(JCClassDecl that) {
+                if (that.sym == sym) result = that;
+                else super.visitClassDef(that);
+            }
+            public void visitMethodDef(JCMethodDecl that) {
+                if (that.sym == sym) result = that;
+                else super.visitMethodDef(that);
+            }
+            public void visitVarDef(JCVariableDecl that) {
+                if (that.sym == sym) result = that;
+                else super.visitVarDef(that);
+            }
+            public void visitTypeParameter(JCTypeParameter that) {
+                if (that.type != null && that.type.tsym == sym) result = that;
+                else super.visitTypeParameter(that);
+            }
+        }
+        DeclScanner s = new DeclScanner();
+        tree.accept(s);
+        return s.result;
+    }
+
+    public static Env<AttrContext> scopeFor(JCTree node, JCCompilationUnit unit) {
+        return scopeFor(pathFor(node, unit));
+    }
+
+    public static Env<AttrContext> scopeFor(List<JCTree> path) {
+        // TODO: not implemented yet
+        throw new UnsupportedOperationException("not implemented yet");
+    }
+
+    public static List<JCTree> pathFor(final JCTree node, final JCCompilationUnit unit) {
+        class Result extends Error {
+            static final long serialVersionUID = -5942088234594905625L;
+            List<JCTree> path;
+            Result(List<JCTree> path) {
+                this.path = path;
+            }
+        }
+        class PathFinder extends TreeScanner {
+            List<JCTree> path = List.nil();
+            public void scan(JCTree tree) {
+                if (tree != null) {
+                    path = path.prepend(tree);
+                    if (tree == node)
+                        throw new Result(path);
+                    super.scan(tree);
+                    path = path.tail;
+                }
+            }
+        }
+        try {
+            new PathFinder().scan(unit);
+        } catch (Result result) {
+            return result.path;
+        }
+        return List.nil();
+    }
+
+    /** Return the statement referenced by a label.
+     *  If the label refers to a loop or switch, return that switch
+     *  otherwise return the labelled statement itself
+     */
+    public static JCTree referencedStatement(JCLabeledStatement tree) {
+        JCTree t = tree;
+        do t = ((JCLabeledStatement) t).body;
+        while (t.hasTag(LABELLED));
+        switch (t.getTag()) {
+        case DOLOOP: case WHILELOOP: case FORLOOP: case FOREACHLOOP: case SWITCH:
+            return t;
+        default:
+            return tree;
+        }
+    }
+
+    /** Skip parens and return the enclosed expression
+     */
+    public static JCExpression skipParens(JCExpression tree) {
+        while (tree.hasTag(PARENS)) {
+            tree = ((JCParens) tree).expr;
+        }
+        return tree;
+    }
+
+    /** Skip parens and return the enclosed expression
+     */
+    public static JCTree skipParens(JCTree tree) {
+        if (tree.hasTag(PARENS))
+            return skipParens((JCParens)tree);
+        else
+            return tree;
+    }
+
+    /** Return the types of a list of trees.
+     */
+    public static List<Type> types(List<? extends JCTree> trees) {
+        ListBuffer<Type> ts = new ListBuffer<>();
+        for (List<? extends JCTree> l = trees; l.nonEmpty(); l = l.tail)
+            ts.append(l.head.type);
+        return ts.toList();
+    }
+
+    /** If this tree is an identifier or a field or a parameterized type,
+     *  return its name, otherwise return null.
+     */
+    public static Name name(JCTree tree) {
+        switch (tree.getTag()) {
+        case IDENT:
+            return ((JCIdent) tree).name;
+        case SELECT:
+            return ((JCFieldAccess) tree).name;
+        case TYPEAPPLY:
+            return name(((JCTypeApply) tree).clazz);
+        default:
+            return null;
+        }
+    }
+
+    /** If this tree is a qualified identifier, its return fully qualified name,
+     *  otherwise return null.
+     */
+    public static Name fullName(JCTree tree) {
+        tree = skipParens(tree);
+        switch (tree.getTag()) {
+        case IDENT:
+            return ((JCIdent) tree).name;
+        case SELECT:
+            Name sname = fullName(((JCFieldAccess) tree).selected);
+            return sname == null ? null : sname.append('.', name(tree));
+        default:
+            return null;
+        }
+    }
+
+    public static Symbol symbolFor(JCTree node) {
+        Symbol sym = symbolForImpl(node);
+
+        return sym != null ? sym.baseSymbol() : null;
+    }
+
+    private static Symbol symbolForImpl(JCTree node) {
+        node = skipParens(node);
+        switch (node.getTag()) {
+        case TOPLEVEL:
+            JCCompilationUnit cut = (JCCompilationUnit) node;
+            JCModuleDecl moduleDecl = cut.getModuleDecl();
+            if (isModuleInfo(cut) && moduleDecl != null)
+                return symbolFor(moduleDecl);
+            return cut.packge;
+        case MODULEDEF:
+            return ((JCModuleDecl) node).sym;
+        case PACKAGEDEF:
+            return ((JCPackageDecl) node).packge;
+        case CLASSDEF:
+            return ((JCClassDecl) node).sym;
+        case METHODDEF:
+            return ((JCMethodDecl) node).sym;
+        case VARDEF:
+            return ((JCVariableDecl) node).sym;
+        case IDENT:
+            return ((JCIdent) node).sym;
+        case SELECT:
+            return ((JCFieldAccess) node).sym;
+        case REFERENCE:
+            return ((JCMemberReference) node).sym;
+        case NEWCLASS:
+            return ((JCNewClass) node).constructor;
+        case APPLY:
+            return symbolFor(((JCMethodInvocation) node).meth);
+        case TYPEAPPLY:
+            return symbolFor(((JCTypeApply) node).clazz);
+        case ANNOTATION:
+        case TYPE_ANNOTATION:
+        case TYPEPARAMETER:
+            if (node.type != null)
+                return node.type.tsym;
+            return null;
+        default:
+            return null;
+        }
+    }
+
+    public static boolean isDeclaration(JCTree node) {
+        node = skipParens(node);
+        switch (node.getTag()) {
+        case PACKAGEDEF:
+        case CLASSDEF:
+        case METHODDEF:
+        case VARDEF:
+            return true;
+        default:
+            return false;
+        }
+    }
+
+    /** If this tree is an identifier or a field, return its symbol,
+     *  otherwise return null.
+     */
+    public static Symbol symbol(JCTree tree) {
+        tree = skipParens(tree);
+        switch (tree.getTag()) {
+        case IDENT:
+            return ((JCIdent) tree).sym;
+        case SELECT:
+            return ((JCFieldAccess) tree).sym;
+        case TYPEAPPLY:
+            return symbol(((JCTypeApply) tree).clazz);
+        case ANNOTATED_TYPE:
+            return symbol(((JCAnnotatedType) tree).underlyingType);
+        case REFERENCE:
+            return ((JCMemberReference) tree).sym;
+        default:
+            return null;
+        }
+    }
+
+    /** Return true if this is a nonstatic selection. */
+    public static boolean nonstaticSelect(JCTree tree) {
+        tree = skipParens(tree);
+        if (!tree.hasTag(SELECT)) return false;
+        JCFieldAccess s = (JCFieldAccess) tree;
+        Symbol e = symbol(s.selected);
+        return e == null || (e.kind != PCK && e.kind != TYP);
+    }
+
+    /** If this tree is an identifier or a field, set its symbol, otherwise skip.
+     */
+    public static void setSymbol(JCTree tree, Symbol sym) {
+        tree = skipParens(tree);
+        switch (tree.getTag()) {
+        case IDENT:
+            ((JCIdent) tree).sym = sym; break;
+        case SELECT:
+            ((JCFieldAccess) tree).sym = sym; break;
+        default:
+        }
+    }
+
+    /** If this tree is a declaration or a block, return its flags field,
+     *  otherwise return 0.
+     */
+    public static long flags(JCTree tree) {
+        switch (tree.getTag()) {
+        case VARDEF:
+            return ((JCVariableDecl) tree).mods.flags;
+        case METHODDEF:
+            return ((JCMethodDecl) tree).mods.flags;
+        case CLASSDEF:
+            return ((JCClassDecl) tree).mods.flags;
+        case BLOCK:
+            return ((JCBlock) tree).flags;
+        default:
+            return 0;
+        }
+    }
+
+    /** Return first (smallest) flag in `flags':
+     *  pre: flags != 0
+     */
+    public static long firstFlag(long flags) {
+        long flag = 1;
+        while ((flag & flags & ExtendedStandardFlags) == 0)
+            flag = flag << 1;
+        return flag;
+    }
+
+    /** Return flags as a string, separated by " ".
+     */
+    public static String flagNames(long flags) {
+        return Flags.toString(flags & ExtendedStandardFlags).trim();
+    }
+
+    /** Operator precedences values.
+     */
+    public static final int
+        notExpression = -1,   // not an expression
+        noPrec = 0,           // no enclosing expression
+        assignPrec = 1,
+        assignopPrec = 2,
+        condPrec = 3,
+        orPrec = 4,
+        andPrec = 5,
+        bitorPrec = 6,
+        bitxorPrec = 7,
+        bitandPrec = 8,
+        eqPrec = 9,
+        ordPrec = 10,
+        shiftPrec = 11,
+        addPrec = 12,
+        mulPrec = 13,
+        prefixPrec = 14,
+        postfixPrec = 15,
+        precCount = 16;
+
+
+    /** Map operators to their precedence levels.
+     */
+    public static int opPrec(JCTree.Tag op) {
+        switch(op) {
+        case POS:
+        case NEG:
+        case NOT:
+        case COMPL:
+        case PREINC:
+        case PREDEC: return prefixPrec;
+        case POSTINC:
+        case POSTDEC:
+        case NULLCHK: return postfixPrec;
+        case ASSIGN: return assignPrec;
+        case BITOR_ASG:
+        case BITXOR_ASG:
+        case BITAND_ASG:
+        case SL_ASG:
+        case SR_ASG:
+        case USR_ASG:
+        case PLUS_ASG:
+        case MINUS_ASG:
+        case MUL_ASG:
+        case DIV_ASG:
+        case MOD_ASG: return assignopPrec;
+        case OR: return orPrec;
+        case AND: return andPrec;
+        case EQ:
+        case NE: return eqPrec;
+        case LT:
+        case GT:
+        case LE:
+        case GE: return ordPrec;
+        case BITOR: return bitorPrec;
+        case BITXOR: return bitxorPrec;
+        case BITAND: return bitandPrec;
+        case SL:
+        case SR:
+        case USR: return shiftPrec;
+        case PLUS:
+        case MINUS: return addPrec;
+        case MUL:
+        case DIV:
+        case MOD: return mulPrec;
+        case TYPETEST: return ordPrec;
+        default: throw new AssertionError();
+        }
+    }
+
+    static Tree.Kind tagToKind(JCTree.Tag tag) {
+        switch (tag) {
+        // Postfix expressions
+        case POSTINC:           // _ ++
+            return Tree.Kind.POSTFIX_INCREMENT;
+        case POSTDEC:           // _ --
+            return Tree.Kind.POSTFIX_DECREMENT;
+
+        // Unary operators
+        case PREINC:            // ++ _
+            return Tree.Kind.PREFIX_INCREMENT;
+        case PREDEC:            // -- _
+            return Tree.Kind.PREFIX_DECREMENT;
+        case POS:               // +
+            return Tree.Kind.UNARY_PLUS;
+        case NEG:               // -
+            return Tree.Kind.UNARY_MINUS;
+        case COMPL:             // ~
+            return Tree.Kind.BITWISE_COMPLEMENT;
+        case NOT:               // !
+            return Tree.Kind.LOGICAL_COMPLEMENT;
+
+        // Binary operators
+
+        // Multiplicative operators
+        case MUL:               // *
+            return Tree.Kind.MULTIPLY;
+        case DIV:               // /
+            return Tree.Kind.DIVIDE;
+        case MOD:               // %
+            return Tree.Kind.REMAINDER;
+
+        // Additive operators
+        case PLUS:              // +
+            return Tree.Kind.PLUS;
+        case MINUS:             // -
+            return Tree.Kind.MINUS;
+
+        // Shift operators
+        case SL:                // <<
+            return Tree.Kind.LEFT_SHIFT;
+        case SR:                // >>
+            return Tree.Kind.RIGHT_SHIFT;
+        case USR:               // >>>
+            return Tree.Kind.UNSIGNED_RIGHT_SHIFT;
+
+        // Relational operators
+        case LT:                // <
+            return Tree.Kind.LESS_THAN;
+        case GT:                // >
+            return Tree.Kind.GREATER_THAN;
+        case LE:                // <=
+            return Tree.Kind.LESS_THAN_EQUAL;
+        case GE:                // >=
+            return Tree.Kind.GREATER_THAN_EQUAL;
+
+        // Equality operators
+        case EQ:                // ==
+            return Tree.Kind.EQUAL_TO;
+        case NE:                // !=
+            return Tree.Kind.NOT_EQUAL_TO;
+
+        // Bitwise and logical operators
+        case BITAND:            // &
+            return Tree.Kind.AND;
+        case BITXOR:            // ^
+            return Tree.Kind.XOR;
+        case BITOR:             // |
+            return Tree.Kind.OR;
+
+        // Conditional operators
+        case AND:               // &&
+            return Tree.Kind.CONDITIONAL_AND;
+        case OR:                // ||
+            return Tree.Kind.CONDITIONAL_OR;
+
+        // Assignment operators
+        case MUL_ASG:           // *=
+            return Tree.Kind.MULTIPLY_ASSIGNMENT;
+        case DIV_ASG:           // /=
+            return Tree.Kind.DIVIDE_ASSIGNMENT;
+        case MOD_ASG:           // %=
+            return Tree.Kind.REMAINDER_ASSIGNMENT;
+        case PLUS_ASG:          // +=
+            return Tree.Kind.PLUS_ASSIGNMENT;
+        case MINUS_ASG:         // -=
+            return Tree.Kind.MINUS_ASSIGNMENT;
+        case SL_ASG:            // <<=
+            return Tree.Kind.LEFT_SHIFT_ASSIGNMENT;
+        case SR_ASG:            // >>=
+            return Tree.Kind.RIGHT_SHIFT_ASSIGNMENT;
+        case USR_ASG:           // >>>=
+            return Tree.Kind.UNSIGNED_RIGHT_SHIFT_ASSIGNMENT;
+        case BITAND_ASG:        // &=
+            return Tree.Kind.AND_ASSIGNMENT;
+        case BITXOR_ASG:        // ^=
+            return Tree.Kind.XOR_ASSIGNMENT;
+        case BITOR_ASG:         // |=
+            return Tree.Kind.OR_ASSIGNMENT;
+
+        // Null check (implementation detail), for example, __.getClass()
+        case NULLCHK:
+            return Tree.Kind.OTHER;
+
+        case ANNOTATION:
+            return Tree.Kind.ANNOTATION;
+        case TYPE_ANNOTATION:
+            return Tree.Kind.TYPE_ANNOTATION;
+
+        case EXPORTS:
+            return Tree.Kind.EXPORTS;
+        case OPENS:
+            return Tree.Kind.OPENS;
+
+        default:
+            return null;
+        }
+    }
+
+    /**
+     * Returns the underlying type of the tree if it is an annotated type,
+     * or the tree itself otherwise.
+     */
+    public static JCExpression typeIn(JCExpression tree) {
+        switch (tree.getTag()) {
+        case ANNOTATED_TYPE:
+            return ((JCAnnotatedType)tree).underlyingType;
+        case IDENT: /* simple names */
+        case TYPEIDENT: /* primitive name */
+        case SELECT: /* qualified name */
+        case TYPEARRAY: /* array types */
+        case WILDCARD: /* wild cards */
+        case TYPEPARAMETER: /* type parameters */
+        case TYPEAPPLY: /* parameterized types */
+        case ERRONEOUS: /* error tree TODO: needed for BadCast JSR308 test case. Better way? */
+            return tree;
+        default:
+            throw new AssertionError("Unexpected type tree: " + tree);
+        }
+    }
+
+    /* Return the inner-most type of a type tree.
+     * For an array that contains an annotated type, return that annotated type.
+     * TODO: currently only used by Pretty. Describe behavior better.
+     */
+    public static JCTree innermostType(JCTree type) {
+        JCTree lastAnnotatedType = null;
+        JCTree cur = type;
+        loop: while (true) {
+            switch (cur.getTag()) {
+            case TYPEARRAY:
+                lastAnnotatedType = null;
+                cur = ((JCArrayTypeTree)cur).elemtype;
+                break;
+            case WILDCARD:
+                lastAnnotatedType = null;
+                cur = ((JCWildcard)cur).inner;
+                break;
+            case ANNOTATED_TYPE:
+                lastAnnotatedType = cur;
+                cur = ((JCAnnotatedType)cur).underlyingType;
+                break;
+            default:
+                break loop;
+            }
+        }
+        if (lastAnnotatedType!=null) {
+            return lastAnnotatedType;
+        } else {
+            return cur;
+        }
+    }
+
+    private static class TypeAnnotationFinder extends TreeScanner {
+        public boolean foundTypeAnno = false;
+
+        @Override
+        public void scan(JCTree tree) {
+            if (foundTypeAnno || tree == null)
+                return;
+            super.scan(tree);
+        }
+
+        public void visitAnnotation(JCAnnotation tree) {
+            foundTypeAnno = foundTypeAnno || tree.hasTag(TYPE_ANNOTATION);
+        }
+    }
+
+    public static boolean containsTypeAnnotation(JCTree e) {
+        TypeAnnotationFinder finder = new TypeAnnotationFinder();
+        finder.scan(e);
+        return finder.foundTypeAnno;
+    }
+
+    public static boolean isModuleInfo(JCCompilationUnit tree) {
+        return tree.sourcefile.isNameCompatible("module-info", JavaFileObject.Kind.SOURCE)
+                && tree.getModuleDecl() != null;
+    }
+
+    public static JCModuleDecl getModule(JCCompilationUnit t) {
+        if (t.defs.nonEmpty()) {
+            JCTree def = t.defs.head;
+            if (def.hasTag(MODULEDEF))
+                return (JCModuleDecl) def;
+        }
+        return null;
+    }
+
+    public static boolean isPackageInfo(JCCompilationUnit tree) {
+        return tree.sourcefile.isNameCompatible("package-info", JavaFileObject.Kind.SOURCE);
+    }
+}