package checkers.util; import java.util.ArrayList; import java.util.Collections; import java.util.List; import checkers.nullness.quals.*; import javax.lang.model.element.*; import javax.lang.model.type.TypeMirror; import com.sun.source.tree.*; import com.sun.source.util.*; import com.sun.tools.javac.code.*; import com.sun.tools.javac.tree.*; import com.sun.tools.javac.tree.JCTree.*; /** * Static utility methods used by annotation abstractions in this package. Some * methods in this class depend on the use of Sun javac internals; any procedure * in the Checker Framework that uses a non-public API should be placed here. */ public class InternalUtils { private InternalUtils() { // Cannot be instantiated. } /** * Gets the {@link Element} ("symbol") for the given Tree API node. * * @param tree * the {@link Tree} node to get the symbol for * @throws IllegalArgumentException * if {@code tree} is null or is not a valid javac-internal tree * (JCTree) * @return the {@code {@link Symbol}} for the given tree, or null if one * could not be found */ public static /*@Nullable*/ Element symbol(/*@Nullable*/ Tree tree) { if (tree == null) throw new IllegalArgumentException("tree is null"); if (!(tree instanceof JCTree)) throw new IllegalArgumentException("tree is not a valid Javac tree"); tree = TreeUtils.skipParens(tree); switch (tree.getKind()) { case VARIABLE: case METHOD: case CLASS: return TreeInfo.symbolFor((JCTree) tree); // symbol() only works on MethodSelects, so we need to get it manually // for method invocations. case METHOD_INVOCATION: return TreeInfo.symbol(((JCMethodInvocation) tree) .getMethodSelect()); case ASSIGNMENT: return TreeInfo.symbol((JCTree)((AssignmentTree)tree).getVariable()); case ARRAY_ACCESS: return symbol(((ArrayAccessTree)tree).getExpression()); default: return TreeInfo.symbol((JCTree) tree); } } /** * Determines whether or not the node referred to by the given * {@link TreePath} is an anonymous constructor (the constructor for an * anonymous class. * * @param method the {@link TreePath} for a node that may be an anonymous * constructor * @return true if the given path points to an anonymous constructor, false * if it does not */ public static boolean isAnonymousConstructor(final MethodTree method) { /*@Nullable*/ Element e = InternalUtils.symbol(method); if (e == null || !(e instanceof Symbol)) return false; if ((((/*@NonNull*/ Symbol)e).flags() & Flags.ANONCONSTR) != 0) return true; return false; } /** * indicates whether it should return the constructor that gets invoked * in cases of anonymous classes */ private static final boolean RETURN_INVOKE_CONSTRUCTOR = true; /** * Determines the symbol for a constructor given an invocation via * {@code new}. * * If the tree is a declaration of an anonymous class, then method returns * constructor that gets invoked in the extended class, rather than the * anonymous constructor implicitly added by the constructor (JLS 15.9.5.1) * * @param tree the constructor invocation * @return the {@link ExecutableElement} corresponding to the constructor * call in {@code tree} */ public static ExecutableElement constructor(NewClassTree tree) { if (!(tree instanceof JCTree.JCNewClass)) throw new IllegalArgumentException("not a javac internal tree"); JCNewClass newClassTree = (JCNewClass)tree; if (RETURN_INVOKE_CONSTRUCTOR && tree.getClassBody() != null) { // anonymous constructor bodies should contain exactly one statement // in the form: // super(arg1, ...) // or // o.super(arg1, ...) // // which is a method invocation (!) to the actual constructor // the method call is guaranteed to return nonnull JCMethodDecl anonConstructor = (JCMethodDecl) TreeInfo.declarationFor(newClassTree.constructor, newClassTree); assert anonConstructor != null; assert anonConstructor.body.stats.size() == 1; JCExpressionStatement stmt = (JCExpressionStatement) anonConstructor.body.stats.head; JCTree.JCMethodInvocation superInvok = (JCMethodInvocation)stmt.expr; return (ExecutableElement)TreeInfo.symbol(superInvok.meth); } Element e = newClassTree.constructor; assert e instanceof ExecutableElement; return (ExecutableElement)e; } public final static List<AnnotationMirror> annotationsFromTypeAnnotationTrees(List<? extends AnnotationTree> annos) { List<AnnotationMirror> annotations = new ArrayList<AnnotationMirror>(annos.size()); for (AnnotationTree anno : annos) annotations.add(((JCTypeAnnotation)anno).attribute_field); return annotations; } public final static List<? extends AnnotationMirror> annotationsFromTree(AnnotatedTypeTree node) { return annotationsFromTypeAnnotationTrees(((JCAnnotatedType)node).annotations); } public final static List<? extends AnnotationMirror> annotationsFromTree(TypeParameterTree node) { return annotationsFromTypeAnnotationTrees(((JCTypeParameter)node).annotations); } public final static List<? extends AnnotationMirror> annotationsFromArrayCreation(NewArrayTree node, int level) { assert node instanceof JCNewArray; final JCNewArray newArray = ((JCNewArray)node); if (level == -1) { return annotationsFromTypeAnnotationTrees(newArray.annotations); } if (newArray.dimAnnotations.length() > 0 && (level >= 0) && (level < newArray.dimAnnotations.size())) return annotationsFromTypeAnnotationTrees(newArray.dimAnnotations.get(level)); return Collections.emptyList(); } public static TypeMirror typeOf(Tree tree) { return ((JCTree)tree).type; } }