package checkers.flow; import checkers.basetype.BaseTypeChecker; import checkers.nullness.quals.Pure; import checkers.source.SourceChecker; import checkers.types.*; import checkers.types.AnnotatedTypeMirror.*; import checkers.util.*; import java.io.PrintStream; import java.util.*; import com.sun.source.tree.*; import com.sun.source.util.*; import javax.annotation.processing.ProcessingEnvironment; import javax.lang.model.element.*; import javax.lang.model.type.*; import javax.lang.model.util.Elements; /** * Provides a generalized flow-sensitive qualifier inference for the checkers * framework. * * <p> * * This implementation is based largely on {@code javac}'s dataflow analysis * module, which may be found in {@code com.sun.tools.javac.comp.Flow} from 13 * Sep 2007. It differs from that class in two ways: * * <ol> * <li value="1"> * It performs a GEN-KILL analysis for qualifiers that is similar to the * initialization/uninitialization analysis in {@code javac}'s {@code Flow}. * It does not perform exception analysis, and performs liveness analysis only * to the extent required for the GEN-KILL analysis. * * <li value="2"> * Whenever possible, this implementation prefers the use of classes in the * public API ({@link BitSet}, the Compiler Tree API) over those in * {@code com.sun.tools.javac} (for these reasons, examining a diff against that * class would not be particularly enlightening). * </ol> * * As in {@code javac}'s {@code Flow} class, methods named "visit*" perform * analysis for a particular type of tree node, while methods named "scan*" * perform the analysis for a general, higher-level class of structural element. * Typically "visit*" methods delegate to "scan*" methods. As an example, * {@link #visitIf}, {@link #visitWhileLoop}, {@link #visitAssert}, and * {@link #visitConditionalExpression} all use {@link #scanCond} for analyzing * conditions and handling branching. * * <p> * * A separate instance of the analysis must be created for each compilation * unit. */ public class Flow extends TreePathScanner<Void, Void> { /** Where to print debugging messages; set via {@link #setDebug}. */ private PrintStream debug = null; /** The checker to which this instance belongs. */ protected final SourceChecker checker; /** The processing environment to use. */ protected final ProcessingEnvironment env; /** The file that's being analyzed. */ protected final CompilationUnitTree root; /** * The annotations (qualifiers) to infer. The relationship among them is * determined using {@link BaseTypeChecker#getQualifierHierarchy()}. By * consulting the hierarchy, the analysis will only infer a qualifier on a * type if it is more restrictive (i.e. a subtype) than the existing * qualifier for that type. */ protected final Set<AnnotationMirror> annotations; /** Utility class for determining annotated types. */ protected final AnnotatedTypeFactory factory; /** Utility class for operations on annotated types. */ protected final AnnotatedTypes atypes; /** * Maps variables to a bit index. This index is also used as the bit index * to determine a variable's annotatedness using * annos/annosWhenTrue/annosWhenFalse. */ protected final List<VariableElement> vars; /** Stores the results of the analysis (source location to qualifier). */ protected final Map<Tree, AnnotationMirror> flowResults; /** * Tracks the annotated state of each variable during flow. Bit indices * correspond exactly to indices in {@link #vars}. This field is set to * null immediately after splitting for a branch, and is set to some * combination (usually boolean "and") of {@link #annosWhenTrue} and * {@link #annosWhenFalse} after merging. Since it is used when visiting the * true and false branches, however, it may be non-null concurrently with * {@link #annosWhenTrue} and {@link #annosWhenFalse}. */ protected GenKillBits<AnnotationMirror> annos; /** * Tracks the annotated state of each variable in a true branch. As in * {@code javac}'s {@code Flow}, saving/restoring via local variables * handles nested branches. Bit indices correspond exactly to indices in * {@link #vars}. This field is copied from {@link #annos} when splitting * for a branch and is set to null immediately after merging. * * @see #annos */ protected GenKillBits<AnnotationMirror> annosWhenTrue; /** * Tracks the annotated state of each variable in a false branch. As in * {@code javac}'s {@code Flow}, saving/restoring via local variables * handles nested branches. Bit indices correspond exactly to indices in * {@link #vars}. This field is copied from {@link #annos} when splitting * for a branch and is set to null immediately after merging. * * @see #annos */ protected GenKillBits<AnnotationMirror> annosWhenFalse; /** * Stores the result of liveness analysis, required by the GEN-KILL analysis * for proper handling of jumps (break, return, throw, etc.). */ protected boolean alive = true; /** Tracks annotations in try blocks to support exceptions. */ private final Deque<GenKillBits<AnnotationMirror>> tryBits; /** Visitor state; tracking is required for checking receiver types. */ private final VisitorState visitorState; /** The hierarchy for the type qualifiers that this class infers. */ protected final QualifierHierarchy annoRelations; /** Utilities for {@link Element}s. */ protected final Elements elements; /** Memoization for {@link #varDefHasAnnotation(AnnotationMirror, Element)}. */ private Map<Element, Boolean> annotatedVarDefs = new HashMap<Element, Boolean>(); /** * Creates a new analysis. The analysis will use the given {@link * AnnotatedTypeFactory} to obtain annotated types. * * @param checker the current checker * @param root the compilation unit that will be scanned * @param annotations the annotations to track * @param factory the factory class that will be used to get annotated * types, or {@code null} if the default factory should be used */ public Flow(BaseTypeChecker checker, CompilationUnitTree root, Set<AnnotationMirror> annotations, AnnotatedTypeFactory factory) { this.checker = checker; this.env = checker.getProcessingEnvironment(); this.root = root; this.annotations = annotations; if (factory == null) this.factory = new AnnotatedTypeFactory(checker, root); else this.factory = factory; this.atypes = new AnnotatedTypes(env, factory); this.visitorState = factory.getVisitorState(); this.vars = new ArrayList<VariableElement>(); this.flowResults = new IdentityHashMap<Tree, AnnotationMirror>(); this.annos = new GenKillBits<AnnotationMirror>(this.annotations); this.annosWhenTrue = null; this.annosWhenFalse = null; this.tryBits = new LinkedList<GenKillBits<AnnotationMirror>>(); this.annoRelations = checker.getQualifierHierarchy(); elements = env.getElementUtils(); } /** * Sets the {@link PrintStream} for printing debug messages, such as * {@link System#out} or {@link System#err}, or null if no debugging output * should be emitted. */ public void setDebug(PrintStream debug) { this.debug = debug; } @Override public Void scan(Tree tree, Void p) { if (tree != null && tree.getKind() == Tree.Kind.COMPILATION_UNIT) return scan(checker.currentPath, p); if (tree != null && getCurrentPath() != null) this.visitorState.setPath(new TreePath(getCurrentPath(), tree)); return super.scan(tree, p); } /** * Determines the inference result for tree. * * @param tree the tree to test * @return the annotation inferred for a tree, or null if no annotation was * inferred for that tree */ public AnnotationMirror test(Tree tree) { while (tree.getKind() == Tree.Kind.ASSIGNMENT) tree = ((AssignmentTree)tree).getVariable(); if (!flowResults.containsKey(tree)) return null; // a hack needs to be fixed // always follow variable declarations AnnotationMirror flowResult = flowResults.get(tree); return flowResult; } /** * Registers a new variable for flow tracking. * * @param tree the variable to register */ void newVar(VariableTree tree) { VariableElement var = TreeUtils.elementFromDeclaration(tree); assert var != null : "no symbol from tree"; if (vars.contains(var)) { if (debug != null) debug.println("Flow: newVar(" + tree + ") reusing index"); return; } int idx = vars.size(); vars.add(var); AnnotatedTypeMirror type = factory.getAnnotatedType(tree); assert type != null : "no type from symbol"; if (debug != null) debug.println("Flow: newVar(" + tree + ") -- " + type); // Determine the initial status of the variable by checking its // annotated type. for (AnnotationMirror annotation : annotations) { if (hasAnnotation(type, annotation)) annos.set(annotation, idx); else annos.clear(annotation, idx); } } /** * Determines whether a type has an annotation. If the type is not a * wildcard, it checks the type directly; if it is a wildcard, it checks the * wildcard's "extends" bound (if it has one). * * @param type the type to check * @param annotation the annotation to check for * @return true if the (non-wildcard) type has the annotation or, if a * wildcard, the type has the annotation on its extends bound */ private boolean hasAnnotation(AnnotatedTypeMirror type, AnnotationMirror annotation) { if (!(type instanceof AnnotatedWildcardType)) return type.hasAnnotation(annotation); AnnotatedWildcardType wc = (AnnotatedWildcardType) type; AnnotatedTypeMirror bound = wc.getExtendsBound(); if (bound != null && bound.hasAnnotation(annotation)) return true; return false; } /** * Moves bits as assignments are made. * * <p> * * If only type information (and not a {@link Tree}) is available, use * {@link #propagateFromType(Tree, AnnotatedTypeMirror)} instead. * * @param lhs the left-hand side of the assignment * @param rhs the right-hand side of the assignment */ void propagate(Tree lhs, ExpressionTree rhs) { if (debug != null) debug.println("Flow: try propagate from " + rhs); // Skip assignment to arrays. if (lhs.getKind() == Tree.Kind.ARRAY_ACCESS) return; // Get the element for the left-hand side. Element elt = InternalUtils.symbol(lhs); assert elt != null; AnnotatedTypeMirror eltType = factory.getAnnotatedType(elt); // Get the annotated type of the right-hand side. AnnotatedTypeMirror type = factory.getAnnotatedType(rhs); if (TreeUtils.skipParens(rhs).getKind() == Tree.Kind.ARRAY_ACCESS) { propagateFromType(lhs, type); return; } assert type != null; int idx = vars.indexOf(elt); if (idx < 0) return; // Get the element for the right-hand side. Element rElt = InternalUtils.symbol(rhs); int rIdx = vars.indexOf(rElt); if (eltType.isAnnotated() && type.isAnnotated() && !annoRelations.isSubtype(type.getAnnotations(), eltType.getAnnotations())) return; for (AnnotationMirror annotation : annotations) { // Propagate/clear the annotation if it's annotated or an annotation // had been inferred previously. if (hasAnnotation(type, annotation) && annoRelations.isSubtype(type.getAnnotations(), eltType.getAnnotations())) annos.set(annotation, idx); else if (rIdx >= 0 && annos.get(annotation, rIdx)) annos.set(annotation, idx); else annos.clear(annotation, idx); } } /** * Moves bits in an assignment using a type instead of a tree. * * <p> * * {@link #propagate(Tree, Tree)} is preferred, since it is able to use * extra information about the right-hand side (such as its element). This * method should only be used when a type (and nothing else) is available, * such as when checking the variable in an enhanced for loop against the * iterated type (which is the type argument of an {@link Iterable}). * * @param lhs the left-hand side of the assignment * @param rhs the type of the right-hand side of the assignment */ void propagateFromType(Tree lhs, AnnotatedTypeMirror rhs) { if (lhs.getKind() == Tree.Kind.ARRAY_ACCESS) return; Element elt = InternalUtils.symbol(lhs); int idx = vars.indexOf(elt); if (idx < 0) return; for (AnnotationMirror annotation : annotations) { if (hasAnnotation(rhs, annotation)) annos.set(annotation, idx); else annos.clear(annotation, idx); } } /** * Split the bitset before a conditional branch. */ protected void split() { annosWhenFalse = GenKillBits.copy(annos); annosWhenTrue = annos; annos = null; } /** * Merge the bitset after a conditional branch. */ protected void merge() { annos = GenKillBits.copy(annos); merge(annos, annosWhenFalse); annosWhenTrue = annosWhenFalse = null; } protected void merge(GenKillBits<AnnotationMirror> b1, GenKillBits<AnnotationMirror> b2) { b1.and(b2); } /** * @param path the path to check * @return true if the path leaf is part of an expression used as an lvalue */ private boolean isLValue(TreePath path) { Tree last = null; // In the following loop, "tree" refers to the current path element // and "last" refers to the most recent path element (which is a child // of "tree". The loop determines whether the path leaf is a variable // immediately enclosed by an assignment or compound assignment. for (Tree tree : path) { if (tree.getKind() == Tree.Kind.IDENTIFIER) { break; } // TODO: do nothing else if (tree instanceof AssignmentTree) return last == ((AssignmentTree)tree).getVariable(); else if (tree instanceof CompoundAssignmentTree) return last == ((CompoundAssignmentTree)tree).getVariable(); if (last != null) break; last = tree; } return false; } /** * Record the value of the annotation bit for the given usage of a * variable, so that a type-checker may use its value after the analysis * has finished. * * @param path */ protected void recordBits(TreePath path) { if (isLValue(path)) return; Tree tree = path.getLeaf(); Element elt; if (tree instanceof MemberSelectTree) elt = TreeUtils.elementFromUse((MemberSelectTree)tree); else if (tree instanceof IdentifierTree) elt = TreeUtils.elementFromUse((IdentifierTree)tree); else if (tree instanceof VariableTree) elt = TreeUtils.elementFromDeclaration((VariableTree)tree); else return; int idx = vars.indexOf(elt); // If the variable has not been previously encountered, add it to the // list of variables. (We can't use newVar here since we don't have the // declaration tree.) if (idx < 0 && elt instanceof VariableElement) { idx = vars.size(); vars.add((VariableElement)elt); } if (idx >= 0) { for (AnnotationMirror annotation : annotations) { if (debug != null) debug.println("Flow: recordBits(" + tree + ") + " + annotation + " " + annos.get(annotation, idx) + " as " + tree.getKind()); if (annos.get(annotation, idx)) { AnnotationMirror existing = flowResults.get(tree); // Don't replace the existing annotation unless the current // annotation is *more* specific than the existing one. if (existing == null || annoRelations.isSubtype(existing, annotation)) flowResults.put(tree, annotation); } else if (flowResults.get(tree) == annotation) { // We inferred an annotation in this location that is not // applicable anymore // occurs in loop where an assignment invalidates the // condition in the next round flowResults.remove(tree); } } } } // ********************************************************************** /** * Called whenever a definition is scanned. * * @param tree the definition being scanned */ protected void scanDef(Tree tree) { alive = true; scan(tree, null); } /** * Called whenever a statement is scanned. * * @param tree the statement being scanned */ protected void scanStat(StatementTree tree) { alive = true; scan(tree, null); } /** * Called whenever a block of statements is scanned. * * @param trees the statements being scanned */ protected void scanStats(List<? extends StatementTree> trees) { scan(trees, null); } /** * Called whenever a conditional expression is scanned. * * @param tree the condition being scanned */ protected void scanCond(Tree tree) { alive = true; if (tree != null) scan(tree, null); if (annos != null) split(); annos = null; } /** * Called whenever an expression is scanned. * * @param tree the expression being scanned */ protected void scanExpr(ExpressionTree tree) { alive = true; scan(tree, null); if (annos == null) merge(); } // ********************************************************************** @Override public Void visitClass(ClassTree node, Void p) { AnnotatedDeclaredType preClassType = visitorState.getClassType(); ClassTree preClassTree = visitorState.getClassTree(); AnnotatedDeclaredType preAMT = visitorState.getMethodReceiver(); MethodTree preMT = visitorState.getMethodTree(); visitorState.setClassType(factory.getAnnotatedType(node)); visitorState.setClassTree(node); visitorState.setMethodReceiver(null); visitorState.setMethodTree(null); try { scan(node.getModifiers(), p); scan(node.getTypeParameters(), p); scan(node.getExtendsClause(), p); scan(node.getImplementsClause(), p); // Ensure that all fields are scanned before scanning methods. for (Tree t : node.getMembers()) { if (t.getKind() == Tree.Kind.METHOD) continue; scan(t, p); } for (Tree t : node.getMembers()) { if (t.getKind() != Tree.Kind.METHOD) continue; scan(t, p); } return null; } finally { this.visitorState.setClassType(preClassType); this.visitorState.setClassTree(preClassTree); this.visitorState.setMethodReceiver(preAMT); this.visitorState.setMethodTree(preMT); } } @Override public Void visitImport(ImportTree tree, Void p) { return null; } @Override public Void visitTypeCast(TypeCastTree node, Void p) { super.visitTypeCast(node, p); if (factory.fromTypeTree(node.getType()).isAnnotated()) return null; AnnotatedTypeMirror t = factory.getAnnotatedType(node.getExpression()); for (AnnotationMirror a : annotations) if (hasAnnotation(t, a)) flowResults.put(node, a); return null; } @Override public Void visitAnnotation(AnnotationTree tree, Void p) { return null; } @Override public Void visitIdentifier(IdentifierTree node, Void p) { super.visitIdentifier(node, p); recordBits(getCurrentPath()); return null; } @Override public Void visitMemberSelect(MemberSelectTree node, Void p) { super.visitMemberSelect(node, p); recordBits(getCurrentPath()); return null; } @Override public Void visitVariable(VariableTree node, Void p) { newVar(node); ExpressionTree init = node.getInitializer(); if (init != null) { scanExpr(init); VariableElement elem = TreeUtils.elementFromDeclaration(node); AnnotatedTypeMirror type = factory.fromMember(node); if (!isNonFinalField(elem) && !type.isAnnotated()) { propagate(node, init); recordBits(getCurrentPath()); } } return null; } @Override public Void visitAssignment(AssignmentTree node, Void p) { ExpressionTree var = node.getVariable(); ExpressionTree expr = node.getExpression(); if (!(var instanceof IdentifierTree)) scanExpr(var); scanExpr(expr); propagate(var, expr); if (var instanceof IdentifierTree) this.scan(var, p); return null; } // This is an exact copy of visitAssignment() @Override public Void visitCompoundAssignment(CompoundAssignmentTree node, Void p) { ExpressionTree var = node.getVariable(); ExpressionTree expr = node.getExpression(); scanExpr(var); scanExpr(expr); // TODO: Handle cases of compound assignment if (var.getKind() != Tree.Kind.ARRAY_ACCESS) clearAnnos(var); return null; } private void clearAnnos(Tree tree) { Element elt = InternalUtils.symbol(tree); if (elt == null) return; int idx = vars.indexOf(elt); if (idx >= 0) { for (AnnotationMirror anno : annotations) { annos.clear(anno, idx); } } } @Override public Void visitEnhancedForLoop(EnhancedForLoopTree node, Void p) { scan(node.getVariable(), p); VariableTree var = node.getVariable(); newVar(var); ExpressionTree expr = node.getExpression(); scanExpr(expr); AnnotatedTypeMirror rhs = factory.getAnnotatedType(expr); AnnotatedTypeMirror iter = atypes.getIteratedType(rhs); if (iter != null) propagateFromType(var, iter); // only visit statement. skip variable and expression.. // visited variable and expression already scanStat(node.getStatement()); return null; } protected static boolean containsKey(Tree tree, Collection<String> keys) { if (tree == null) return false; String treeStr = tree.toString(); for (String key : keys) { if (treeStr.contains(key)) return true; } return false; } protected void pushNewLevel() { } protected void popLastLevel() { } @Override public Void visitAssert(AssertTree node, Void p) { boolean inferFromAsserts = containsKey(node.getDetail(), checker.getSuppressWarningsKey()); GenKillBits<AnnotationMirror> annosAfterAssert = GenKillBits.copy(annos); pushNewLevel(); scanCond(node.getCondition()); if (inferFromAsserts) annosAfterAssert = GenKillBits.copy(annosWhenTrue); annos = GenKillBits.copy(annosWhenFalse); scanExpr(node.getDetail()); annos = annosAfterAssert; popLastLevel(); return null; } protected void whenConditionFalse(ExpressionTree condition, Void p) { } @Override public Void visitIf(IfTree node, Void p) { pushNewLevel(); scanCond(node.getCondition()); GenKillBits<AnnotationMirror> beforeElse = annosWhenFalse; annos = annosWhenTrue; boolean aliveBefore = alive; scanStat(node.getThenStatement()); popLastLevel(); pushNewLevel(); StatementTree elseStmt = node.getElseStatement(); if (elseStmt != null) { whenConditionFalse(node.getCondition(), p); boolean aliveAfter = alive; alive = aliveBefore; GenKillBits<AnnotationMirror> after = GenKillBits.copy(annos); annos = beforeElse; scanStat(elseStmt); if (!alive) { alive = aliveAfter; after.or(annos); annos = GenKillBits.copy(after); } else if (!aliveAfter) { annos = annos; // NOOP } else { // both branches are alive alive = true; merge(annos, after); } } else { if (!alive) annos = GenKillBits.copy(beforeElse); else merge(annos, beforeElse); } popLastLevel(); return null; } @Override public Void visitConditionalExpression(ConditionalExpressionTree node, Void p) { // Split and merge as for an if/else. scanCond(node.getCondition()); GenKillBits<AnnotationMirror> before = annosWhenFalse; annos = annosWhenTrue; scanExpr(node.getTrueExpression()); GenKillBits<AnnotationMirror> after = GenKillBits.copy(annos); annos = before; scanExpr(node.getFalseExpression()); merge(annos, after); return null; } @Override public Void visitWhileLoop(WhileLoopTree node, Void p) { boolean pass = false; GenKillBits<AnnotationMirror> annoCond; GenKillBits<AnnotationMirror> beforeTrue = GenKillBits.copy(annosWhenTrue); GenKillBits<AnnotationMirror> beforeFalse = GenKillBits.copy(annosWhenFalse); do { GenKillBits<AnnotationMirror> annoEntry = GenKillBits.copy(annos); scanCond(node.getCondition()); annoCond = annosWhenFalse; annos = annosWhenTrue; scanStat(node.getStatement()); if (pass) break; merge(annosWhenTrue, annoEntry); merge(annos, annoEntry); pass = true; } while (true); annos = annoCond; annosWhenTrue = beforeTrue; annosWhenFalse = beforeFalse; return null; } @Override public Void visitDoWhileLoop(DoWhileLoopTree node, Void p) { boolean pass = false; GenKillBits<AnnotationMirror> annoCond; do { GenKillBits<AnnotationMirror> annoEntry = GenKillBits.copy(annos); scanStat(node.getStatement()); scanCond(node.getCondition()); annoCond = annosWhenFalse; annos = annosWhenTrue; if (pass) break; merge(annosWhenTrue, annoEntry); pass = true; } while (true); annos = annoCond; return null; } @Override public Void visitForLoop(ForLoopTree node, Void p) { boolean pass = false; for (StatementTree initalizer : node.getInitializer()) scanStat(initalizer); GenKillBits<AnnotationMirror> annoCond; do { GenKillBits<AnnotationMirror> annoEntry = GenKillBits.copy(annos); scanCond(node.getCondition()); annoCond = annosWhenFalse; annos = annosWhenTrue; scanStat(node.getStatement()); for (StatementTree tree : node.getUpdate()) scanStat(tree); if (pass) break; merge(annosWhenTrue, annoEntry); merge(annos, annoEntry); pass = true; } while (true); annos = annoCond; return null; } @Override public Void visitBreak(BreakTree node, Void p) { alive = false; //alive = true; return null; } @Override public Void visitContinue(ContinueTree node, Void p) { alive = false; //alive = true; return null; } @Override public Void visitReturn(ReturnTree node, Void p) { if (node.getExpression() != null) scanExpr(node.getExpression()); alive = false; return null; } @Override public Void visitThrow(ThrowTree node, Void p) { scanExpr(node.getExpression()); alive = false; return null; } @Override public Void visitTry(TryTree node, Void p) { tryBits.push(GenKillBits.copy(annos)); scan(node.getBlock(), p); GenKillBits<AnnotationMirror> annoAfterBlock = GenKillBits.copy(annos); pushNewLevel(); GenKillBits<AnnotationMirror> result = tryBits.pop(); merge(annos, result); popLastLevel(); if (node.getCatches() != null) { boolean catchAlive = false; for (CatchTree ct : node.getCatches()) { scan(ct, p); catchAlive |= alive; } // Conservative: only if there's no finally if (!catchAlive && node.getFinallyBlock() == null) annos = GenKillBits.copy(annoAfterBlock); } scan(node.getFinallyBlock(), p); return null; } @Override public Void visitMethodInvocation(MethodInvocationTree node, Void p) { super.visitMethodInvocation(node, p); ExecutableElement method = TreeUtils.elementFromUse(node); if (method.getSimpleName().contentEquals("exit") && method.getEnclosingElement().getSimpleName().contentEquals("System")) alive = false; final String methodPackage = elements.getPackageOf(method).getQualifiedName().toString(); boolean isJDKMethod = methodPackage.startsWith("java") || methodPackage.startsWith("com.sun"); boolean isPure = method.getAnnotation(Pure.class) != null; for (int i = 0; i < vars.size(); i++) { Element var = vars.get(i); for (AnnotationMirror a : annotations) if (!isJDKMethod && isNonFinalField(var) && !varDefHasAnnotation(a, var) && !isPure) annos.clear(a, i); } List<? extends TypeMirror> thrown = method.getThrownTypes(); if (!thrown.isEmpty() && TreeUtils.enclosingOfKind(getCurrentPath(), Tree.Kind.TRY) != null) { if (!tryBits.isEmpty()) merge(tryBits.peek(), annos); } return null; } @Override public Void visitBlock(BlockTree node, Void p) { if (node.isStatic()) { pushNewLevel(); GenKillBits<AnnotationMirror> prev = GenKillBits.copy(annos); try { super.visitBlock(node, p); return null; } finally { annos = prev; popLastLevel(); } } return super.visitBlock(node, p); } @Override public Void visitMethod(MethodTree node, Void p) { AnnotatedDeclaredType preMRT = visitorState.getMethodReceiver(); MethodTree preMT = visitorState.getMethodTree(); visitorState.setMethodReceiver( factory.getAnnotatedType(node).getReceiverType()); visitorState.setMethodTree(node); // Intraprocedural, so save and restore bits. GenKillBits<AnnotationMirror> prev = GenKillBits.copy(annos); try { super.visitMethod(node, p); return null; } finally { annos = prev; visitorState.setMethodReceiver(preMRT); visitorState.setMethodTree(preMT); } } // ********************************************************************** /** * Determines whether a variable definition has been annotated. * * @param annotation the annotation to check for * @param var the variable to check * @return true if the variable has the given annotation, false otherwise */ private boolean varDefHasAnnotation(AnnotationMirror annotation, Element var) { if (annotatedVarDefs.containsKey(var)) return annotatedVarDefs.get(var); boolean result = hasAnnotation(factory.getAnnotatedType(var), annotation); annotatedVarDefs.put(var, result); return result; } /** * Tests whether the element is of a non-final field * * @return true iff element is a non-final field */ private static final boolean isNonFinalField(Element element) { return (element.getKind().isField() && !ElementUtils.isFinal(element)); } public Set<AnnotationMirror> getAnnotations() { return annotations; } }