All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.checkerframework.framework.util.AnnotatedTypes Maven / Gradle / Ivy

Go to download

The Checker Framework enhances Java's type system to make it more powerful and useful. This lets software developers detect and prevent errors in their Java programs. The Checker Framework includes compiler plug-ins ("checkers") that find bugs or verify their absence. It also permits you to write your own compiler plug-ins.

There is a newer version: 3.43.0
Show newest version
package org.checkerframework.framework.util;

/*>>>
import org.checkerframework.checker.nullness.qual.*;
*/

import com.sun.source.tree.ExpressionTree;
import com.sun.source.tree.MemberReferenceTree;
import com.sun.source.tree.MethodInvocationTree;
import com.sun.source.tree.NewClassTree;
import com.sun.source.tree.Tree;
import com.sun.tools.javac.code.Attribute;
import com.sun.tools.javac.code.Symbol;
import com.sun.tools.javac.code.Type;
import com.sun.tools.javac.code.Type.WildcardType;
import java.lang.annotation.ElementType;
import java.lang.annotation.Target;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Deque;
import java.util.HashMap;
import java.util.HashSet;
import java.util.IdentityHashMap;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
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.element.TypeElement;
import javax.lang.model.element.TypeParameterElement;
import javax.lang.model.type.TypeKind;
import javax.lang.model.type.TypeMirror;
import javax.lang.model.type.TypeVariable;
import javax.lang.model.util.ElementFilter;
import javax.lang.model.util.Elements;
import javax.lang.model.util.Types;
import org.checkerframework.framework.qual.PolyAll;
import org.checkerframework.framework.type.AnnotatedTypeFactory;
import org.checkerframework.framework.type.AnnotatedTypeMirror;
import org.checkerframework.framework.type.AnnotatedTypeMirror.AnnotatedArrayType;
import org.checkerframework.framework.type.AnnotatedTypeMirror.AnnotatedDeclaredType;
import org.checkerframework.framework.type.AnnotatedTypeMirror.AnnotatedExecutableType;
import org.checkerframework.framework.type.AnnotatedTypeMirror.AnnotatedIntersectionType;
import org.checkerframework.framework.type.AnnotatedTypeMirror.AnnotatedTypeVariable;
import org.checkerframework.framework.type.AnnotatedTypeMirror.AnnotatedWildcardType;
import org.checkerframework.framework.type.AsSuperVisitor;
import org.checkerframework.framework.type.QualifierHierarchy;
import org.checkerframework.framework.type.SyntheticArrays;
import org.checkerframework.javacutil.AnnotationUtils;
import org.checkerframework.javacutil.ElementUtils;
import org.checkerframework.javacutil.ErrorReporter;
import org.checkerframework.javacutil.Pair;
import org.checkerframework.javacutil.TypesUtils;

/**
 * Utility methods for operating on {@code AnnotatedTypeMirror}. This class mimics the class {@link
 * Types}.
 */
public class AnnotatedTypes {
    // Class cannot be instantiated.
    private AnnotatedTypes() {
        throw new AssertionError("Class AnnotatedTypes cannot be instantiated.");
    }

    private static AsSuperVisitor asSuperVisitor;

    /**
     * Copies annotations from {@code type} to a copy of {@code superType} where the type variables
     * of {@code superType} have been substituted. How the annotations are copied depends on the
     * kinds of AnnotatedTypeMirrors given. Generally, if {@code type} and {@code superType} are
     * both declared types, asSuper is called recursively on the direct super types, see {@link
     * AnnotatedTypeMirror#directSuperTypes()}, of {@code type} until {@code type}'s erased Java
     * type is the same as {@code superType}'s erased super type. Then {@code type is returned}. For
     * compound types, asSuper is call recursively on components.
     *
     * 

Preconditions:
* {@code superType} may have annotations, but they are ignored.
* {@code type} may not be an instanceof AnnotatedNullType, because if {@code superType} is a * compound type, the annotations on the component types are undefined.
* The underlying {@code type} (ie the Java type) of {@code type} should be a subtype (or the * same type) of the underlying type of {@code superType}. Except for these cases: * *

    *
  • If {@code type} is a primitive, then the boxed type of {@code type} must be subtype of * {@code superType}. *
  • If {@code superType} is a primitive, then {@code type} must be convertible to {@code * superType}. *
  • If {@code superType} is a type variable or wildcard without a lower bound, then {@code * type} must be a subtype of the upper bound of {@code superType}. (This relaxed rule is * used during type argument inference where the type variable or wildcard is the type * argument that was inferred.) *
  • If {@code superType} is a wildcard with a lower bound, then {@code type} must be a * subtype of the lower bound of {@code superType}. *
* *

Postconditions: {@code type} and {@code superType} are not modified. * * @param atypeFactory {@link AnnotatedTypeFactory} * @param type type from which to copy annotations * @param superType a type whose erased Java type is a supertype of {@code type}'s erased Java * type. * @return {@code superType} with annotations copied from {@code type} and type variables * substituted from {@code type}. */ public static T asSuper( AnnotatedTypeFactory atypeFactory, AnnotatedTypeMirror type, T superType) { if (asSuperVisitor == null || !asSuperVisitor.sameAnnotatedTypeFactory(atypeFactory)) { asSuperVisitor = new AsSuperVisitor(atypeFactory); } return asSuperVisitor.asSuper(type, superType); } /** This method identifies wildcard types that are unbound. */ public static boolean hasNoExplicitBound(final AnnotatedTypeMirror wildcard) { return ((Type.WildcardType) wildcard.getUnderlyingType()).isUnbound(); } /** * This method identifies wildcard types that have an explicit super bound. NOTE: * Type.WildcardType.isSuperBound will return true for BOTH unbound and super bound wildcards * which necessitates this method */ public static boolean hasExplicitSuperBound(final AnnotatedTypeMirror wildcard) { final Type.WildcardType wildcardType = (Type.WildcardType) wildcard.getUnderlyingType(); return wildcardType.isSuperBound() && !((WildcardType) wildcard.getUnderlyingType()).isUnbound(); } /** * This method identifies wildcard types that have an explicit extends bound. NOTE: * Type.WildcardType.isExtendsBound will return true for BOTH unbound and extends bound * wildcards which necessitates this method */ public static boolean hasExplicitExtendsBound(final AnnotatedTypeMirror wildcard) { final Type.WildcardType wildcardType = (Type.WildcardType) wildcard.getUnderlyingType(); return wildcardType.isExtendsBound() && !((WildcardType) wildcard.getUnderlyingType()).isUnbound(); } /** * Return the base type of type or any of its outer types that starts with the given type. If * none exists, return null. * * @param type a type * @param superType a type */ private static AnnotatedTypeMirror asOuterSuper( Types types, AnnotatedTypeFactory atypeFactory, AnnotatedTypeMirror type, AnnotatedTypeMirror superType) { if (type.getKind() == TypeKind.DECLARED) { AnnotatedDeclaredType dt = (AnnotatedDeclaredType) type; AnnotatedDeclaredType enclosingType = dt; TypeMirror superTypeMirror = types.erasure(superType.getUnderlyingType()); while (enclosingType != null) { TypeMirror enclosingTypeMirror = types.erasure(enclosingType.getUnderlyingType()); if (types.isSubtype(enclosingTypeMirror, superTypeMirror)) { dt = enclosingType; break; } enclosingType = enclosingType.getEnclosingType(); } if (enclosingType == null) { // TODO: https://github.com/typetools/checker-framework/issues/724 // testcase javacheck -processor nullness src/java/util/AbstractMap.java // SourceChecker checker = atypeFactory.getContext().getChecker(); // String msg = (String.format("OuterAsSuper did not find outer // class. type: %s superType: %s", type, superType)); // checker.message(Kind.WARNING, msg); return superType; } return asSuper(atypeFactory, dt, superType); } return asSuper(atypeFactory, type, superType); } /** @see #asMemberOf(Types, AnnotatedTypeFactory, AnnotatedTypeMirror, Element) */ public static AnnotatedExecutableType asMemberOf( Types types, AnnotatedTypeFactory atypeFactory, AnnotatedTypeMirror t, ExecutableElement elem) { return (AnnotatedExecutableType) asMemberOf(types, atypeFactory, t, (Element) elem); } /** * Returns the type of an element when that element is viewed as a member of, or otherwise * directly contained by, a given type. * *

For example, when viewed as a member of the parameterized type {@code Set<@NonNull * String>}, the {@code Set.add} method is an {@code ExecutableType} whose parameter is of type * {@code @NonNull String}. * *

The result is customized according to the type system semantics, according to {@link * AnnotatedTypeFactory#postAsMemberOf( AnnotatedTypeMirror, AnnotatedTypeMirror, Element)}. * *

Note that this method does not currently return (top level) captured types for type * parameters, parameters, and return types. Instead, the original wildcard is returned, or * sometimes inferring type arguments will create a wildcard type which is returned. The bounds * of an inferred wildcard may itself have captures. * *

To prevent unsoundness, the rest of the checker framework must expect wildcard in places * where captures should appear (like type arguments). This should just involve the bounds of * the wildcard where the bounds of the capture would have been used. * * @param t a type * @param elem an element */ public static AnnotatedTypeMirror asMemberOf( Types types, AnnotatedTypeFactory atypeFactory, AnnotatedTypeMirror t, Element elem) { // asMemberOf is only for fields, variables, and methods! // Otherwise, simply use fromElement. switch (elem.getKind()) { case PACKAGE: case INSTANCE_INIT: case OTHER: case STATIC_INIT: case TYPE_PARAMETER: return atypeFactory.fromElement(elem); default: AnnotatedTypeMirror type = asMemberOfImpl(types, atypeFactory, t, elem); if (!ElementUtils.isStatic(elem)) { atypeFactory.postAsMemberOf(type, t, elem); } return type; } } private static AnnotatedTypeMirror asMemberOfImpl( final Types types, final AnnotatedTypeFactory atypeFactory, final AnnotatedTypeMirror of, final Element member) { final AnnotatedTypeMirror memberType = atypeFactory.getAnnotatedType(member); if (ElementUtils.isStatic(member)) { return memberType; } switch (of.getKind()) { case ARRAY: // Method references like String[]::clone should have a return type of String[] // rather than Object if (SyntheticArrays.isArrayClone(of, member)) { return SyntheticArrays.replaceReturnType(member, (AnnotatedArrayType) of); } return memberType; case TYPEVAR: return asMemberOf( types, atypeFactory, ((AnnotatedTypeVariable) of).getUpperBound(), member); case WILDCARD: return asMemberOf( types, atypeFactory, ((AnnotatedWildcardType) of).getExtendsBound().deepCopy(), member); case INTERSECTION: case UNION: case DECLARED: return substituteTypeVariables(types, atypeFactory, of, member, memberType); default: ErrorReporter.errorAbort("asMemberOf called on unexpected type.\nt: " + of); return memberType; // dead code } } private static AnnotatedTypeMirror substituteTypeVariables( Types types, AnnotatedTypeFactory atypeFactory, AnnotatedTypeMirror of, Element member, AnnotatedTypeMirror memberType) { // Basic Algorithm: // 1. Find the enclosingClassOfMember of the element // 2. Find the base type of enclosingClassOfMember (e.g. type of enclosingClassOfMember as // supertype of passed type) // 3. Substitute for type variables if any exist TypeElement enclosingClassOfMember = ElementUtils.enclosingClass(member); final Map mappings = new HashMap<>(); // Look for all enclosing classes that have type variables // and collect type to be substituted for those type variables while (enclosingClassOfMember != null) { addTypeVarMappings(types, atypeFactory, of, enclosingClassOfMember, mappings); enclosingClassOfMember = ElementUtils.enclosingClass(enclosingClassOfMember.getEnclosingElement()); } if (!mappings.isEmpty()) { memberType = atypeFactory.getTypeVarSubstitutor().substitute(mappings, memberType); } return memberType; } private static void addTypeVarMappings( Types types, AnnotatedTypeFactory atypeFactory, AnnotatedTypeMirror t, TypeElement enclosingClassOfElem, Map mappings) { if (enclosingClassOfElem.getTypeParameters().isEmpty()) { return; } AnnotatedDeclaredType enclosingType = atypeFactory.getAnnotatedType(enclosingClassOfElem); AnnotatedDeclaredType base = (AnnotatedDeclaredType) asOuterSuper(types, atypeFactory, t, enclosingType); final List ownerParams = new ArrayList<>(enclosingType.getTypeArguments().size()); for (final AnnotatedTypeMirror typeParam : enclosingType.getTypeArguments()) { if (typeParam.getKind() != TypeKind.TYPEVAR) { ErrorReporter.errorAbort( "Type arguments of a declaration should be type variables\n" + "enclosingClassOfElem=" + enclosingClassOfElem + "\n" + "enclosingType=" + enclosingType + "\n" + "typeMirror=" + t); } ownerParams.add((AnnotatedTypeVariable) typeParam); } List baseParams = base.getTypeArguments(); if (ownerParams.size() != baseParams.size() && !base.wasRaw()) { ErrorReporter.errorAbort( "Unexpected number of parameters.\n" + "enclosingType=" + enclosingType + "\n" + "baseType=" + base); } if (!ownerParams.isEmpty() && baseParams.isEmpty() && base.wasRaw()) { List newBaseParams = new ArrayList<>(); for (AnnotatedTypeVariable arg : ownerParams) { // If base type was raw and the type arguments are missing, // set them to the erased type of the type variable. // (which is the erased type of the upper bound.) newBaseParams.add(arg.getErased()); } baseParams = newBaseParams; } for (int i = 0; i < ownerParams.size(); ++i) { mappings.put(ownerParams.get(i).getUnderlyingType(), baseParams.get(i)); } } /** * Returns the iterated type of the passed iterable type, and throws {@link * IllegalArgumentException} if the passed type is not iterable. * *

The iterated type is the component type of an array, and the type argument of {@link * Iterable} for declared types. * * @param iterableType the iterable type (either array or declared) * @return the types of elements in the iterable type */ public static AnnotatedTypeMirror getIteratedType( ProcessingEnvironment processingEnv, AnnotatedTypeFactory atypeFactory, AnnotatedTypeMirror iterableType) { if (iterableType.getKind() == TypeKind.ARRAY) { return ((AnnotatedArrayType) iterableType).getComponentType(); } // For type variables and wildcards take the effective upper bound. if (iterableType.getKind() == TypeKind.WILDCARD) { return getIteratedType( processingEnv, atypeFactory, ((AnnotatedWildcardType) iterableType).getExtendsBound().deepCopy()); } if (iterableType.getKind() == TypeKind.TYPEVAR) { return getIteratedType( processingEnv, atypeFactory, ((AnnotatedTypeVariable) iterableType).getUpperBound()); } if (iterableType.getKind() != TypeKind.DECLARED) { ErrorReporter.errorAbort( "AnnotatedTypes.getIteratedType: not iterable type: " + iterableType); return null; // dead code } TypeElement iterableElement = processingEnv.getElementUtils().getTypeElement("java.lang.Iterable"); AnnotatedDeclaredType iterableElmType = atypeFactory.getAnnotatedType(iterableElement); AnnotatedDeclaredType dt = asSuper(atypeFactory, iterableType, iterableElmType); if (dt.getTypeArguments().isEmpty()) { TypeElement e = processingEnv.getElementUtils().getTypeElement("java.lang.Object"); AnnotatedDeclaredType t = atypeFactory.getAnnotatedType(e); return t; } else { return dt.getTypeArguments().get(0); } } /** * Returns all the super types of the given declared type. * * @param type a declared type * @return all the supertypes of the given type */ public static Set getSuperTypes(AnnotatedDeclaredType type) { Set supertypes = new LinkedHashSet<>(); if (type == null) { return supertypes; } // Set up a stack containing the type mirror of subtype, which // is our starting point. Deque stack = new ArrayDeque<>(); stack.push(type); while (!stack.isEmpty()) { AnnotatedDeclaredType current = stack.pop(); // For each direct supertype of the current type, if it // hasn't already been visited, push it onto the stack and // add it to our supertypes set. for (AnnotatedDeclaredType supertype : current.directSuperTypes()) { if (!supertypes.contains(supertype)) { stack.push(supertype); supertypes.add(supertype); } } } return Collections.unmodifiableSet(supertypes); } /** * A utility method that takes a Method element and returns a set of all elements that this * method overrides (as {@link ExecutableElement}s) * * @param method the overriding method * @return an unmodifiable set of {@link ExecutableElement}s representing the elements that * method overrides */ public static Map overriddenMethods( Elements elements, AnnotatedTypeFactory atypeFactory, ExecutableElement method) { final TypeElement elem = (TypeElement) method.getEnclosingElement(); final AnnotatedDeclaredType type = atypeFactory.getAnnotatedType(elem); final Collection supertypes = getSuperTypes(type); return overriddenMethods(elements, method, supertypes); } /** * A utility method that takes the element for a method and the set of all supertypes of the * method's containing class and returns the set of all elements that method overrides (as * {@link ExecutableElement}s). * * @param method the overriding method * @param supertypes the set of supertypes to check for methods that are overridden by {@code * method} * @return an unmodified set of {@link ExecutableElement}s representing the elements that {@code * method} overrides among {@code supertypes} */ public static Map overriddenMethods( Elements elements, ExecutableElement method, Collection supertypes) { Map overrides = new LinkedHashMap<>(); for (AnnotatedDeclaredType supertype : supertypes) { /*@Nullable*/ TypeElement superElement = (TypeElement) supertype.getUnderlyingType().asElement(); assert superElement != null; /*nninvariant*/ // For all method in the supertype, add it to the set if // it overrides the given method. for (ExecutableElement supermethod : ElementFilter.methodsIn(superElement.getEnclosedElements())) { if (elements.overrides(method, supermethod, superElement)) { overrides.put(supertype, supermethod); break; } } } return Collections.unmodifiableMap(overrides); } /** * Given a method or constructor invocation, return a mapping of the type variables to their * type arguments, if any exist. * *

It uses the method or constructor invocation type arguments if they were specified and * otherwise it infers them based on the passed arguments or the return type context, according * to JLS 15.12.2. * * @param atypeFactory the annotated type factory * @param expr the method or constructor invocation tree; the passed argument has to be a * subtype of MethodInvocationTree or NewClassTree * @param elt the element corresponding to the tree * @param preType the (partially annotated) type corresponding to the tree - the result of * AnnotatedTypes.asMemberOf with the receiver and elt. * @return the mapping of the type variables to type arguments for this method or constructor * invocation */ public static Map findTypeArguments( final ProcessingEnvironment processingEnv, final AnnotatedTypeFactory atypeFactory, final ExpressionTree expr, final ExecutableElement elt, final AnnotatedExecutableType preType) { // Is the method a generic method? if (elt.getTypeParameters().isEmpty()) { return Collections.emptyMap(); } List targs; if (expr instanceof MethodInvocationTree) { targs = ((MethodInvocationTree) expr).getTypeArguments(); } else if (expr instanceof NewClassTree) { targs = ((NewClassTree) expr).getTypeArguments(); } else if (expr instanceof MemberReferenceTree) { targs = ((MemberReferenceTree) expr).getTypeArguments(); if (targs == null) { return new HashMap<>(); } } else { // This case should never happen. ErrorReporter.errorAbort("AnnotatedTypes.findTypeArguments: unexpected tree: " + expr); return null; // dead code } // Has the user supplied type arguments? if (!targs.isEmpty()) { List tvars = preType.getTypeVariables(); Map typeArguments = new HashMap<>(); for (int i = 0; i < elt.getTypeParameters().size(); ++i) { AnnotatedTypeVariable typeVar = tvars.get(i); AnnotatedTypeMirror typeArg = atypeFactory.getAnnotatedTypeFromTypeTree(targs.get(i)); // TODO: the call to getTypeParameterDeclaration shouldn't be necessary - typeVar // already should be a declaration. typeArguments.put(typeVar.getUnderlyingType(), typeArg); } return typeArguments; } else { return atypeFactory .getTypeArgumentInference() .inferTypeArgs(atypeFactory, expr, elt, preType); } } /** * Returns the lub of two annotated types. * * @param atypeFactory AnnotatedTypeFactory * @param type1 annotated type * @param type2 annotated type * @return the lub of type1 and type2 */ public static AnnotatedTypeMirror leastUpperBound( AnnotatedTypeFactory atypeFactory, AnnotatedTypeMirror type1, AnnotatedTypeMirror type2) { TypeMirror lub = TypesUtils.leastUpperBound( type1.getUnderlyingType(), type2.getUnderlyingType(), atypeFactory.getProcessingEnv()); return leastUpperBound(atypeFactory, type1, type2, lub); } /** * Returns the lub, whose underlying type is {@code lubTypeMirror} of two annotated types. * * @param atypeFactory AnnotatedTypeFactory * @param type1 annotated type whose underlying type must be a subtype or convertible to * lubTypeMirror * @param type2 annotated type whose underlying type must be a subtype or convertible to * lubTypeMirror * @param lubTypeMirror underlying type of the returned lub * @return the lub of type1 and type2 with underlying type lubTypeMirror */ public static AnnotatedTypeMirror leastUpperBound( AnnotatedTypeFactory atypeFactory, AnnotatedTypeMirror type1, AnnotatedTypeMirror type2, TypeMirror lubTypeMirror) { return new AtmLubVisitor(atypeFactory).lub(type1, type2, lubTypeMirror); } /** * Returns the method parameters for the invoked method, with the same number of arguments * passed in the methodInvocation tree. * *

If the invoked method is not a vararg method or it is a vararg method but the invocation * passes an array to the vararg parameter, it would simply return the method parameters. * *

Otherwise, it would return the list of parameters as if the vararg is expanded to match * the size of the passed arguments. * * @param method the method's type * @param args the arguments to the method invocation * @return the types that the method invocation arguments need to be subtype of */ public static List expandVarArgs( AnnotatedTypeFactory atypeFactory, AnnotatedExecutableType method, List args) { List parameters = method.getParameterTypes(); if (!method.getElement().isVarArgs()) { return parameters; } AnnotatedArrayType varargs = (AnnotatedArrayType) parameters.get(parameters.size() - 1); if (parameters.size() == args.size()) { // Check if one sent an element or an array AnnotatedTypeMirror lastArg = atypeFactory.getAnnotatedType(args.get(args.size() - 1)); if (lastArg.getKind() == TypeKind.ARRAY && getArrayDepth(varargs) == getArrayDepth((AnnotatedArrayType) lastArg)) { return parameters; } } parameters = new ArrayList<>(parameters.subList(0, parameters.size() - 1)); for (int i = args.size() - parameters.size(); i > 0; --i) { parameters.add(varargs.getComponentType()); } return parameters; } public static List expandVarArgsFromTypes( AnnotatedExecutableType method, List args) { List parameters = method.getParameterTypes(); if (!method.getElement().isVarArgs()) { return parameters; } AnnotatedArrayType varargs = (AnnotatedArrayType) parameters.get(parameters.size() - 1); if (parameters.size() == args.size()) { // Check if one sent an element or an array AnnotatedTypeMirror lastArg = args.get(args.size() - 1); if (lastArg.getKind() == TypeKind.ARRAY && (getArrayDepth(varargs) == getArrayDepth((AnnotatedArrayType) lastArg) // If the array depths don't match, but the component type of the vararg // is a type variable, then that type variable might later be // substituted for an array. || varargs.getComponentType().getKind() == TypeKind.TYPEVAR)) { return parameters; } } parameters = new ArrayList<>(parameters.subList(0, parameters.size() - 1)); for (int i = args.size() - parameters.size(); i > 0; --i) { parameters.add(varargs.getComponentType()); } return parameters; } /** * Given an AnnotatedExecutableType of a method or constructor declaration, get the parameter * type expect at the indexth position (unwrapping var args if necessary). * * @param methodType AnnotatedExecutableType of method or constructor containing parameter to * return * @param index position of parameter type to return * @return if that parameter is a varArgs, return the component of the var args and NOT the * array type. Otherwise, return the exact type of the parameter in the index position. */ public static AnnotatedTypeMirror getAnnotatedTypeMirrorOfParameter( AnnotatedExecutableType methodType, int index) { List parameterTypes = methodType.getParameterTypes(); boolean hasVarArg = methodType.getElement().isVarArgs(); final int lastIndex = parameterTypes.size() - 1; final AnnotatedTypeMirror lastType = parameterTypes.get(lastIndex); final boolean parameterBeforeVarargs = index < lastIndex; if (!parameterBeforeVarargs && lastType instanceof AnnotatedArrayType) { final AnnotatedArrayType arrayType = (AnnotatedArrayType) lastType; if (hasVarArg) { return arrayType.getComponentType(); } } return parameterTypes.get(index); } /** * Return a list of the AnnotatedTypeMirror of the passed expression trees, in the same order as * the trees. * * @param paramTypes the parameter types to use as assignment context * @param trees the AST nodes * @return a list with the AnnotatedTypeMirror of each tree in trees */ public static List getAnnotatedTypes( AnnotatedTypeFactory atypeFactory, List paramTypes, List trees) { assert paramTypes.size() == trees.size() : "AnnotatedTypes.getAnnotatedTypes: size mismatch! " + "Parameter types: " + paramTypes + " Arguments: " + trees; List types = new ArrayList<>(); Pair preAssCtxt = atypeFactory.getVisitorState().getAssignmentContext(); try { for (int i = 0; i < trees.size(); ++i) { AnnotatedTypeMirror param = paramTypes.get(i); atypeFactory.getVisitorState().setAssignmentContext(Pair.of((Tree) null, param)); ExpressionTree arg = trees.get(i); types.add(atypeFactory.getAnnotatedType(arg)); } } finally { atypeFactory.getVisitorState().setAssignmentContext(preAssCtxt); } return types; } /** @deprecated use AnnotatedTypeMirror.equals() */ @Deprecated // remove after release 2.2.3 public static boolean areSame(AnnotatedTypeMirror t1, AnnotatedTypeMirror t2) { return t1.equals(t2); } /** * Returns the depth of the array type of the provided array. * * @param array the type of the array * @return the depth of the provided array */ public static int getArrayDepth(AnnotatedArrayType array) { int counter = 0; AnnotatedTypeMirror type = array; while (type.getKind() == TypeKind.ARRAY) { counter++; type = ((AnnotatedArrayType) type).getComponentType(); } return counter; } // The innermost *array* type. public static AnnotatedTypeMirror innerMostType(AnnotatedTypeMirror t) { AnnotatedTypeMirror inner = t; while (inner.getKind() == TypeKind.ARRAY) inner = ((AnnotatedArrayType) inner).getComponentType(); return inner; } /** * Checks whether type contains the given modifier, also recursively in type arguments and * arrays. This method might be easier to implement directly as instance method in * AnnotatedTypeMirror; it corresponds to a "deep" version of {@link * AnnotatedTypeMirror#hasAnnotation(AnnotationMirror)}. * * @param type the type to search * @param modifier the modifier to search for * @return whether the type contains the modifier */ public static boolean containsModifier(AnnotatedTypeMirror type, AnnotationMirror modifier) { return containsModifierImpl(type, modifier, new LinkedList()); } /* * For type variables we might hit the same type again. We keep a list of visited types. */ private static boolean containsModifierImpl( AnnotatedTypeMirror type, AnnotationMirror modifier, List visited) { boolean found = type.hasAnnotation(modifier); boolean vis = visited.contains(type); visited.add(type); if (!found && !vis) { if (type.getKind() == TypeKind.DECLARED) { AnnotatedDeclaredType declaredType = (AnnotatedDeclaredType) type; for (AnnotatedTypeMirror typeMirror : declaredType.getTypeArguments()) { found |= containsModifierImpl(typeMirror, modifier, visited); if (found) { break; } } } else if (type.getKind() == TypeKind.ARRAY) { AnnotatedArrayType arrayType = (AnnotatedArrayType) type; found = containsModifierImpl(arrayType.getComponentType(), modifier, visited); } else if (type.getKind() == TypeKind.TYPEVAR) { AnnotatedTypeVariable atv = (AnnotatedTypeVariable) type; if (atv.getUpperBound() != null) { found = containsModifierImpl(atv.getUpperBound(), modifier, visited); } if (!found && atv.getLowerBound() != null) { found = containsModifierImpl(atv.getLowerBound(), modifier, visited); } } else if (type.getKind() == TypeKind.WILDCARD) { AnnotatedWildcardType awc = (AnnotatedWildcardType) type; if (awc.getExtendsBound() != null) { found = containsModifierImpl(awc.getExtendsBound(), modifier, visited); } if (!found && awc.getSuperBound() != null) { found = containsModifierImpl(awc.getSuperBound(), modifier, visited); } } } return found; } private static Map isTypeAnnotationCache = new IdentityHashMap<>(); public static boolean isTypeAnnotation(AnnotationMirror anno, Class cls) { TypeElement elem = (TypeElement) anno.getAnnotationType().asElement(); if (isTypeAnnotationCache.containsKey(elem)) { return isTypeAnnotationCache.get(elem); } // the annotation is a type annotation if it has the proper ElementTypes in the @Target // meta-annotation boolean result = hasTypeQualifierElementTypes(elem.getAnnotation(Target.class).value(), cls); isTypeAnnotationCache.put(elem, result); return result; } /** * Sees if the passed in array of {@link ElementType} values have the correct set of values * which defines a type qualifier * * @param elements an array of {@link ElementType} values * @param cls the annotation class being tested; used for diagnostic messages only * @throws RuntimeException if the array contains both {@link ElementType#TYPE_USE} and * something besides {@link ElementType#TYPE_PARAMETER} */ public static boolean hasTypeQualifierElementTypes(ElementType[] elements, Class cls) { boolean hasTypeUse = false; ElementType otherElementType = null; for (ElementType element : elements) { if (element.equals(ElementType.TYPE_USE)) { // valid annotations have to have TYPE_USE hasTypeUse = true; } else if (!element.equals(ElementType.TYPE_PARAMETER)) { // if there's an ElementType with a enumerated value of something other than // TYPE_USE or TYPE_PARAMETER then it isn't a valid annotation otherElementType = element; } if (hasTypeUse && otherElementType != null) { ErrorReporter.errorAbort( "@Target meta-annotation should not contain both TYPE_USE and " + otherElementType + ", for annotation " + cls.getName()); } } return hasTypeUse; } /** * Returns true if the given {@link AnnotatedTypeMirror} passed a set of well-formedness checks. * The method will never return false for valid types, but might not catch all invalid types. * *

Currently, the following is checked: * *

    *
  1. There should not be multiple annotations from the same hierarchy. *
  2. There should not be more annotations than the width of the qualifier hierarchy. *
  3. If the type is not a type variable, then the number of annotations should be the same * as the width of the qualifier hierarchy. *
  4. These properties should also hold recursively for component types of arrays, as wells * as bounds of type variables and wildcards. *
*/ public static boolean isValidType( QualifierHierarchy qualifierHierarchy, AnnotatedTypeMirror type) { boolean res = isValidType(qualifierHierarchy, type, Collections.emptySet()); return res; } private static boolean isValidType( QualifierHierarchy qualifierHierarchy, AnnotatedTypeMirror type, Set v) { if (type == null) { return false; } Set visited = new HashSet<>(v); if (visited.contains(type)) { return true; // prevent infinite recursion } visited.add(type); // multiple annotations from the same hierarchy Set annotations = type.getAnnotations(); Set seenTops = AnnotationUtils.createAnnotationSet(); int n = 0; for (AnnotationMirror anno : annotations) { if (QualifierPolymorphism.isPolyAll(anno)) { // ignore PolyAll when counting annotations continue; } n++; AnnotationMirror top = qualifierHierarchy.getTopAnnotation(anno); if (AnnotationUtils.containsSame(seenTops, top)) { return false; } seenTops.add(top); } // too many annotations int expectedN = qualifierHierarchy.getWidth(); if (n > expectedN) { return false; } // treat types that have polyall like type variables boolean hasPolyAll = type.hasAnnotation(PolyAll.class); boolean canHaveEmptyAnnotationSet = QualifierHierarchy.canHaveEmptyAnnotationSet(type) || hasPolyAll; // wrong number of annotations if (!canHaveEmptyAnnotationSet && n != expectedN) { return false; } // recurse for composite types if (type instanceof AnnotatedArrayType) { AnnotatedArrayType at = (AnnotatedArrayType) type; if (!isValidType(qualifierHierarchy, at.getComponentType(), visited)) { return false; } } else if (type instanceof AnnotatedTypeVariable) { AnnotatedTypeVariable at = (AnnotatedTypeVariable) type; AnnotatedTypeMirror lowerBound = at.getLowerBound(); AnnotatedTypeMirror upperBound = at.getUpperBound(); if (lowerBound != null && !isValidType(qualifierHierarchy, lowerBound, visited)) { return false; } if (upperBound != null && !isValidType(qualifierHierarchy, upperBound, visited)) { return false; } } else if (type instanceof AnnotatedWildcardType) { AnnotatedWildcardType at = (AnnotatedWildcardType) type; AnnotatedTypeMirror extendsBound = at.getExtendsBound(); AnnotatedTypeMirror superBound = at.getSuperBound(); if (extendsBound != null && !isValidType(qualifierHierarchy, extendsBound, visited)) { return false; } if (superBound != null && !isValidType(qualifierHierarchy, superBound, visited)) { return false; } } // TODO: the recursive checks on type arguments are currently skipped, because // this breaks various tests. it seems that checking the validity changes the // annotations on some types. // } else if (type instanceof AnnotatedDeclaredType) { // AnnotatedDeclaredType at = (AnnotatedDeclaredType) type; // for (AnnotatedTypeMirror typeArgument : at.getTypeArguments()) { // if (!isValidType(qualifierHierarchy, typeArgument, visited)) { // return false; // } // } // } return true; } private static String annotationClassName = java.lang.annotation.Annotation.class.getCanonicalName(); /** @return true if the underlying type of this atm is a java.lang.annotation.Annotation */ public static boolean isJavaLangAnnotation(final AnnotatedTypeMirror atm) { return TypesUtils.isDeclaredOfName(atm.getUnderlyingType(), annotationClassName); } /** * @return true if atm is an Annotation interface, i.e. an implementation of * java.lang.annotation.Annotation e.g. @interface MyAnno - implementsAnnotation would * return true when called on an AnnotatedDeclaredType representing a use of MyAnno */ public static boolean implementsAnnotation(final AnnotatedTypeMirror atm) { if (atm.getKind() != TypeKind.DECLARED) { return false; } final AnnotatedTypeMirror.AnnotatedDeclaredType declaredType = (AnnotatedTypeMirror.AnnotatedDeclaredType) atm; Symbol.ClassSymbol classSymbol = (Symbol.ClassSymbol) declaredType.getUnderlyingType().asElement(); for (final Type iface : classSymbol.getInterfaces()) { if (TypesUtils.isDeclaredOfName(iface, annotationClassName)) { return true; } } return false; } public static boolean isEnum(final AnnotatedTypeMirror typeMirror) { if (typeMirror.getKind() == TypeKind.DECLARED) { final AnnotatedDeclaredType adt = (AnnotatedDeclaredType) typeMirror; return TypesUtils.isDeclaredOfName( adt.getUnderlyingType(), java.lang.Enum.class.getName()); } return false; } public static boolean isDeclarationOfJavaLangEnum( final Types types, final Elements elements, final AnnotatedTypeMirror typeMirror) { if (isEnum(typeMirror)) { return elements.getTypeElement("java.lang.Enum") .equals(((AnnotatedDeclaredType) typeMirror).getUnderlyingType().asElement()); } return false; } /** @return true if the typeVar1 and typeVar2 are two uses of the same type variable */ public static boolean haveSameDeclaration( Types types, final AnnotatedTypeVariable typeVar1, final AnnotatedTypeVariable typeVar2) { return types.isSameType(typeVar1.getUnderlyingType(), typeVar2.getUnderlyingType()); } /** * When overriding a method, you must include the same number of type parameters as the base * method. By index, these parameters are considered equivalent to the type parameters of the * overridden method. Necessary conditions: Both type variables are defined in methods One of * the two methods overrides the other Within their method declaration, both types have the same * type parameter index * * @return returns true if type1 and type2 are corresponding type variables (that is, either one * "overrides" the other) */ public static boolean areCorrespondingTypeVariables( Elements elements, AnnotatedTypeVariable type1, AnnotatedTypeVariable type2) { final TypeParameterElement type1ParamElem = (TypeParameterElement) type1.getUnderlyingType().asElement(); final TypeParameterElement type2ParamElem = (TypeParameterElement) type2.getUnderlyingType().asElement(); if (type1ParamElem.getGenericElement() instanceof ExecutableElement && type2ParamElem.getGenericElement() instanceof ExecutableElement) { final ExecutableElement type1Executable = (ExecutableElement) type1ParamElem.getGenericElement(); final ExecutableElement type2Executable = (ExecutableElement) type2ParamElem.getGenericElement(); final TypeElement type1Class = (TypeElement) type1Executable.getEnclosingElement(); final TypeElement type2Class = (TypeElement) type2Executable.getEnclosingElement(); boolean methodIsOverriden = elements.overrides(type1Executable, type2Executable, type1Class) || elements.overrides(type2Executable, type1Executable, type2Class); if (methodIsOverriden) { boolean haveSameIndex = type1Executable.getTypeParameters().indexOf(type1ParamElem) == type2Executable.getTypeParameters().indexOf(type2ParamElem); return haveSameIndex; } } return false; } /** * When comparing types against the bounds of a type variable, we may encounter other type * variables, wildcards, and intersections in those bounds. This method traverses the bounds * until it finds a concrete type from which it can pull an annotation. * * @param top the top of the hierarchy for which you are searching * @return the AnnotationMirror that represents the type of toSearch in the hierarchy of top */ public static AnnotationMirror findEffectiveAnnotationInHierarchy( final QualifierHierarchy qualifierHierarchy, final AnnotatedTypeMirror toSearch, final AnnotationMirror top) { return findEffectiveAnnotationInHierarchy(qualifierHierarchy, toSearch, top, false); } /** * When comparing types against the bounds of a type variable, we may encounter other type * variables, wildcards, and intersections in those bounds. This method traverses the bounds * until it finds a concrete type from which it can pull an annotation. * * @param top the top of the hierarchy for which you are searching * @param canBeEmpty whether or not the effective type can have NO annotation in the hierarchy * specified by top If this param is false, an exception will be thrown if no annotation is * found Otherwise the result is null * @return the AnnotationMirror that represents the type of toSearch in the hierarchy of top */ public static AnnotationMirror findEffectiveAnnotationInHierarchy( final QualifierHierarchy qualifierHierarchy, final AnnotatedTypeMirror toSearch, final AnnotationMirror top, final boolean canBeEmpty) { AnnotatedTypeMirror source = toSearch; while (source.getAnnotationInHierarchy(top) == null) { switch (source.getKind()) { case TYPEVAR: source = ((AnnotatedTypeVariable) source).getUpperBound(); break; case WILDCARD: source = ((AnnotatedWildcardType) source).getExtendsBound(); break; case INTERSECTION: // if there are multiple conflicting annotations, choose the lowest final AnnotationMirror glb = glbOfBoundsInHierarchy( (AnnotatedIntersectionType) source, top, qualifierHierarchy); if (glb == null) { ErrorReporter.errorAbort( "AnnotatedIntersectionType has no annotation in hierarchy " + "on any of its supertypes!\n" + "intersectionType=" + source); } return glb; default: if (canBeEmpty) { return null; } ErrorReporter.errorAbort( "Unexpected AnnotatedTypeMirror with no primary annotation!\n" + "toSearch=" + toSearch + "\n" + "top=" + top + "\n" + "source=" + source); return null; } } return source.getAnnotationInHierarchy(top); } /** * When comparing types against the bounds of a type variable, we may encounter other type * variables, wildcards, and intersections in those bounds. This method traverses the lower * bounds until it finds a concrete type from which it can pull an annotation. This occurs for * every hierarchy in QualifierHierarchy * * @return the set of effective annotation mirrors in all hierarchies */ public static Set findEffectiveLowerBoundAnnotations( final QualifierHierarchy qualifierHierarchy, final AnnotatedTypeMirror toSearch) { AnnotatedTypeMirror source = toSearch; TypeKind kind = source.getKind(); while (kind == TypeKind.TYPEVAR || kind == TypeKind.WILDCARD || kind == TypeKind.INTERSECTION) { switch (source.getKind()) { case TYPEVAR: source = ((AnnotatedTypeVariable) source).getLowerBound(); break; case WILDCARD: source = ((AnnotatedWildcardType) source).getSuperBound(); break; case INTERSECTION: // if there are multiple conflicting annotations, choose the lowest final Set glb = glbOfBounds((AnnotatedIntersectionType) source, qualifierHierarchy); return glb; default: ErrorReporter.errorAbort( "Unexpected AnnotatedTypeMirror with no primary annotation!" + "toSearch=" + toSearch + "source=" + source); } kind = source.getKind(); } return source.getAnnotations(); } /** * When comparing types against the bounds of a type variable, we may encounter other type * variables, wildcards, and intersections in those bounds. This method traverses the bounds * until it finds a concrete type from which it can pull an annotation. This occurs for every * hierarchy in QualifierHierarchy * * @return the set of effective annotation mirrors in all hierarchies */ public static Set findEffectiveAnnotations( final QualifierHierarchy qualifierHierarchy, final AnnotatedTypeMirror toSearch) { AnnotatedTypeMirror source = toSearch; TypeKind kind = source.getKind(); while (kind == TypeKind.TYPEVAR || kind == TypeKind.WILDCARD || kind == TypeKind.INTERSECTION) { switch (source.getKind()) { case TYPEVAR: source = ((AnnotatedTypeVariable) source).getUpperBound(); break; case WILDCARD: source = ((AnnotatedWildcardType) source).getExtendsBound(); break; case INTERSECTION: // if there are multiple conflicting annotations, choose the lowest final Set glb = glbOfBounds((AnnotatedIntersectionType) source, qualifierHierarchy); return glb; default: ErrorReporter.errorAbort( "Unexpected AnnotatedTypeMirror with no primary annotation!" + "toSearch=" + toSearch + "source=" + source); } kind = source.getKind(); } return source.getAnnotations(); } private static AnnotationMirror glbOfBoundsInHierarchy( final AnnotatedIntersectionType isect, final AnnotationMirror top, final QualifierHierarchy qualifierHierarchy) { AnnotationMirror anno = isect.getAnnotationInHierarchy(top); for (final AnnotatedTypeMirror supertype : isect.directSuperTypes()) { final AnnotationMirror superAnno = supertype.getAnnotationInHierarchy(top); if (superAnno != null && (anno == null || qualifierHierarchy.isSubtype(superAnno, anno))) { anno = superAnno; } } return anno; } /** * Get's the lowest primary annotation of all bounds in the intersection * * @param isect the intersection for which we are glbing bounds * @param qualifierHierarchy the qualifier used to get the hierarchies in which to glb * @return a set of annotations representing the glb of the intersection's bounds */ public static Set glbOfBounds( final AnnotatedIntersectionType isect, final QualifierHierarchy qualifierHierarchy) { Set result = AnnotationUtils.createAnnotationSet(); for (final AnnotationMirror top : qualifierHierarchy.getTopAnnotations()) { final AnnotationMirror glbAnno = glbOfBoundsInHierarchy(isect, top, qualifierHierarchy); if (glbAnno != null) { result.add(glbAnno); } } return result; } // For Wildcards, isSuperBound and isExtendsBound will return true if isUnbound does. public static boolean isExplicitlySuperBounded(final AnnotatedWildcardType wildcardType) { return ((Type.WildcardType) wildcardType.getUnderlyingType()).isSuperBound() && !((Type.WildcardType) wildcardType.getUnderlyingType()).isUnbound(); } /** Returns true if wildcard type was explicitly unbounded. */ public static boolean isExplicitlyExtendsBounded(final AnnotatedWildcardType wildcardType) { return ((Type.WildcardType) wildcardType.getUnderlyingType()).isExtendsBound() && !((Type.WildcardType) wildcardType.getUnderlyingType()).isUnbound(); } /** Returns true if this type is super bounded or unbounded. */ public static boolean isUnboundedOrSuperBounded(final AnnotatedWildcardType wildcardType) { return ((Type.WildcardType) wildcardType.getUnderlyingType()).isSuperBound(); } /** Returns true if this type is extends bounded or unbounded. */ public static boolean isUnboundedOrExtendsBounded(final AnnotatedWildcardType wildcardType) { return ((Type.WildcardType) wildcardType.getUnderlyingType()).isExtendsBound(); } /** * Copies explicit annotations and annotations resulting from resolution of polymorphic * qualifiers from {@code constructor} to {@code returnType}. If {@code returnType} has an * annotation in the same hierarchy of an annotation to be copied, that annotation is not * copied. * * @param atypeFactory type factory * @param returnType return type to copy annotations to * @param constructor the ATM for the constructor */ public static void copyOnlyExplicitConstructorAnnotations( AnnotatedTypeFactory atypeFactory, AnnotatedDeclaredType returnType, AnnotatedExecutableType constructor) { // TODO: There will be a nicer way to access this in 308 soon. List decall = ((Symbol) constructor.getElement()).getRawTypeAttributes(); Set decret = AnnotationUtils.createAnnotationSet(); for (Attribute.TypeCompound da : decall) { if (da.position.type == com.sun.tools.javac.code.TargetType.METHOD_RETURN) { decret.add(da); } } // Collect all polymorphic qualifiers; we should substitute them. Set polys = AnnotationUtils.createAnnotationSet(); for (AnnotationMirror anno : returnType.getAnnotations()) { if (QualifierPolymorphism.isPolymorphicQualified(anno)) { polys.add(anno); } } for (AnnotationMirror cta : constructor.getReturnType().getAnnotations()) { AnnotationMirror ctatop = atypeFactory.getQualifierHierarchy().getTopAnnotation(cta); if (atypeFactory.isSupportedQualifier(cta) && !returnType.isAnnotatedInHierarchy(cta)) { for (AnnotationMirror fromDecl : decret) { if (atypeFactory.isSupportedQualifier(fromDecl) && AnnotationUtils.areSame( ctatop, atypeFactory .getQualifierHierarchy() .getTopAnnotation(fromDecl))) { returnType.addAnnotation(cta); break; } } } // Go through the polymorphic qualifiers and see whether // there is anything left to replace. for (AnnotationMirror pa : polys) { if (AnnotationUtils.areSame( ctatop, atypeFactory.getQualifierHierarchy().getTopAnnotation(pa))) { returnType.replaceAnnotation(cta); break; } } } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy