| package org.checkerframework.javacutil; |
| |
| import java.util.ArrayList; |
| import java.util.Collections; |
| import java.util.List; |
| |
| import javax.annotation.processing.ProcessingEnvironment; |
| import javax.lang.model.element.AnnotationMirror; |
| import javax.lang.model.element.Element; |
| import javax.lang.model.element.ExecutableElement; |
| import javax.lang.model.type.TypeKind; |
| import javax.lang.model.type.TypeMirror; |
| import javax.lang.model.type.TypeVariable; |
| import javax.lang.model.type.WildcardType; |
| import javax.lang.model.util.Elements; |
| |
| import com.sun.source.tree.AnnotatedTypeTree; |
| import com.sun.source.tree.AnnotationTree; |
| import com.sun.source.tree.ArrayAccessTree; |
| import com.sun.source.tree.AssignmentTree; |
| import com.sun.source.tree.ExpressionTree; |
| import com.sun.source.tree.MethodTree; |
| import com.sun.source.tree.NewArrayTree; |
| import com.sun.source.tree.NewClassTree; |
| import com.sun.source.tree.Tree; |
| import com.sun.source.tree.TypeParameterTree; |
| import com.sun.source.util.TreePath; |
| import com.sun.tools.javac.code.Flags; |
| import com.sun.tools.javac.code.Symbol; |
| import com.sun.tools.javac.code.Symbol.TypeSymbol; |
| import com.sun.tools.javac.code.Type; |
| import com.sun.tools.javac.code.Types; |
| import com.sun.tools.javac.processing.JavacProcessingEnvironment; |
| import com.sun.tools.javac.tree.JCTree; |
| import com.sun.tools.javac.tree.JCTree.JCAnnotatedType; |
| import com.sun.tools.javac.tree.JCTree.JCAnnotation; |
| import com.sun.tools.javac.tree.JCTree.JCExpressionStatement; |
| import com.sun.tools.javac.tree.JCTree.JCMemberReference; |
| import com.sun.tools.javac.tree.JCTree.JCMethodDecl; |
| import com.sun.tools.javac.tree.JCTree.JCMethodInvocation; |
| import com.sun.tools.javac.tree.JCTree.JCNewArray; |
| import com.sun.tools.javac.tree.JCTree.JCNewClass; |
| import com.sun.tools.javac.tree.JCTree.JCTypeParameter; |
| import com.sun.tools.javac.tree.TreeInfo; |
| import com.sun.tools.javac.util.Context; |
| |
| /*>>> |
| import org.checkerframework.checker.nullness.qual.*; |
| */ |
| |
| /** |
| * 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 { |
| |
| // Class cannot be instantiated. |
| private InternalUtils() { |
| throw new AssertionError("Class 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(Tree tree) { |
| if (tree == null) { |
| ErrorReporter.errorAbort("InternalUtils.symbol: tree is null"); |
| return null; // dead code |
| } |
| |
| if (!(tree instanceof JCTree)) { |
| ErrorReporter.errorAbort("InternalUtils.symbol: tree is not a valid Javac tree"); |
| return null; // dead code |
| } |
| |
| if (TreeUtils.isExpressionTree(tree)) { |
| tree = TreeUtils.skipParens((ExpressionTree) tree); |
| } |
| |
| switch (tree.getKind()) { |
| case VARIABLE: |
| case METHOD: |
| case CLASS: |
| case ENUM: |
| case INTERFACE: |
| case ANNOTATION_TYPE: |
| case TYPE_PARAMETER: |
| 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()); |
| |
| case NEW_CLASS: |
| return ((JCNewClass)tree).constructor; |
| |
| case MEMBER_REFERENCE: |
| // TreeInfo.symbol, which is used in the default case, didn't handle |
| // member references until JDK8u20. So handle it here. |
| return ((JCMemberReference) tree).sym; |
| |
| 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)) { |
| ErrorReporter.errorAbort("InternalUtils.constructor: not a javac internal tree"); |
| return null; // dead code |
| } |
| |
| 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(((JCAnnotation)anno).attribute); |
| 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; |
| } |
| |
| /** |
| * Returns whether a TypeVariable represents a captured type. |
| */ |
| public static boolean isCaptured(TypeVariable typeVar) { |
| return ((Type.TypeVar) typeVar).isCaptured(); |
| } |
| |
| /** |
| * Returns whether a TypeMirror represents a class type. |
| */ |
| public static boolean isClassType(TypeMirror type) { |
| return (type instanceof Type.ClassType); |
| } |
| |
| /** |
| * Returns the least upper bound of two {@link TypeMirror}s. |
| * |
| * @param processingEnv The {@link ProcessingEnvironment} to use. |
| * @param tm1 A {@link TypeMirror}. |
| * @param tm2 A {@link TypeMirror}. |
| * @return The least upper bound of {@code tm1} and {@code tm2}. |
| */ |
| public static TypeMirror leastUpperBound( |
| ProcessingEnvironment processingEnv, TypeMirror tm1, TypeMirror tm2) { |
| Type t1 = (Type) tm1; |
| Type t2 = (Type) tm2; |
| JavacProcessingEnvironment javacEnv = (JavacProcessingEnvironment) processingEnv; |
| Types types = Types.instance(javacEnv.getContext()); |
| if (types.isSameType(t1, t2)) { |
| // Special case if the two types are equal. |
| return t1; |
| } |
| // Handle the 'null' type manually (not done by types.lub). |
| if (t1.getKind() == TypeKind.NULL) { |
| return t2; |
| } |
| if (t2.getKind() == TypeKind.NULL) { |
| return t1; |
| } |
| // Special case for primitives. |
| if (TypesUtils.isPrimitive(t1) || TypesUtils.isPrimitive(t2)) { |
| if (types.isAssignable(t1, t2)) { |
| return t2; |
| } else if (types.isAssignable(t2, t1)) { |
| return t1; |
| } else { |
| return processingEnv.getTypeUtils().getNoType(TypeKind.NONE); |
| } |
| } |
| if (t1.getKind() == TypeKind.WILDCARD) { |
| WildcardType wc1 = (WildcardType) t1; |
| Type bound = (Type) wc1.getExtendsBound(); |
| if (bound == null) { |
| // Implicit upper bound of java.lang.Object |
| Elements elements = processingEnv.getElementUtils(); |
| return elements.getTypeElement("java.lang.Object").asType(); |
| } |
| t1 = bound; |
| } |
| if (t2.getKind() == TypeKind.WILDCARD) { |
| WildcardType wc2 = (WildcardType) t2; |
| Type bound = (Type) wc2.getExtendsBound(); |
| if (bound == null) { |
| // Implicit upper bound of java.lang.Object |
| Elements elements = processingEnv.getElementUtils(); |
| return elements.getTypeElement("java.lang.Object").asType(); |
| } |
| t2 = bound; |
| } |
| return types.lub(t1, t2); |
| } |
| |
| /** |
| * Returns the greatest lower bound of two {@link TypeMirror}s. |
| * |
| * @param processingEnv The {@link ProcessingEnvironment} to use. |
| * @param tm1 A {@link TypeMirror}. |
| * @param tm2 A {@link TypeMirror}. |
| * @return The greatest lower bound of {@code tm1} and {@code tm2}. |
| */ |
| public static TypeMirror greatestLowerBound( |
| ProcessingEnvironment processingEnv, TypeMirror tm1, TypeMirror tm2) { |
| Type t1 = (Type) tm1; |
| Type t2 = (Type) tm2; |
| JavacProcessingEnvironment javacEnv = (JavacProcessingEnvironment) processingEnv; |
| Types types = Types.instance(javacEnv.getContext()); |
| if (types.isSameType(t1, t2)) { |
| // Special case if the two types are equal. |
| return t1; |
| } |
| // Handle the 'null' type manually. |
| if (t1.getKind() == TypeKind.NULL) { |
| return t1; |
| } |
| if (t2.getKind() == TypeKind.NULL) { |
| return t2; |
| } |
| // Special case for primitives. |
| if (TypesUtils.isPrimitive(t1) || TypesUtils.isPrimitive(t2)) { |
| if (types.isAssignable(t1, t2)) { |
| return t1; |
| } else if (types.isAssignable(t2, t1)) { |
| return t2; |
| } else { |
| // Javac types.glb returns TypeKind.Error when the GLB does |
| // not exist, but we can't create one. Use TypeKind.NONE |
| // instead. |
| return processingEnv.getTypeUtils().getNoType(TypeKind.NONE); |
| } |
| } |
| if (t1.getKind() == TypeKind.WILDCARD) { |
| return t2; |
| } |
| if (t2.getKind() == TypeKind.WILDCARD) { |
| return t1; |
| } |
| return types.glb(t1, t2); |
| } |
| |
| /** |
| * Returns the return type of a method, where the "raw" return type of that |
| * method is given (i.e., the return type might still contain unsubstituted |
| * type variables), given the receiver of the method call. |
| */ |
| public static TypeMirror substituteMethodReturnType(TypeMirror methodType, |
| TypeMirror substitutedReceiverType) { |
| if (methodType.getKind() != TypeKind.TYPEVAR) { |
| return methodType; |
| } |
| // TODO: find a nicer way to substitute type variables |
| String t = methodType.toString(); |
| Type finalReceiverType = (Type) substitutedReceiverType; |
| int i = 0; |
| for (TypeSymbol typeParam : finalReceiverType.tsym.getTypeParameters()) { |
| if (t.equals(typeParam.toString())) { |
| return finalReceiverType.getTypeArguments().get(i); |
| } |
| i++; |
| } |
| assert false; |
| return null; |
| } |
| |
| /** Helper function to extract the javac Context from the |
| * javac processing environment. |
| * |
| * @param env the processing environment |
| * @return the javac Context |
| */ |
| public static Context getJavacContext(ProcessingEnvironment env) { |
| return ((JavacProcessingEnvironment)env).getContext(); |
| } |
| } |