framework.src.org.checkerframework.framework.util.AnnotatedTypes Maven / Gradle / Ivy
Show all versions of checker Show documentation
package org.checkerframework.framework.util;
/*>>>
import org.checkerframework.checker.nullness.qual.*;
*/
import org.checkerframework.framework.flow.util.LubTypeVariableAnnotator;
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.AnnotatedPrimitiveType;
import org.checkerframework.framework.type.AnnotatedTypeMirror.AnnotatedTypeVariable;
import org.checkerframework.framework.type.AnnotatedTypeMirror.AnnotatedWildcardType;
import org.checkerframework.framework.type.QualifierHierarchy;
import org.checkerframework.framework.type.SyntheticArrays;
import org.checkerframework.framework.type.visitor.SimpleAnnotatedTypeVisitor;
import org.checkerframework.javacutil.AnnotationUtils;
import org.checkerframework.javacutil.ElementUtils;
import org.checkerframework.javacutil.ErrorReporter;
import org.checkerframework.javacutil.InternalUtils;
import org.checkerframework.javacutil.Pair;
import org.checkerframework.javacutil.TypesUtils;
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 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;
/**
* 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.");}
/**
* Returns the most specific base type of {@code t} whose erasure type
* is {@code superType}. It returns null if {@code t} is not a subtype
* of {@code superType}.
*
* @param types the type utilities to use
* @param atypeFactory the type factory to use
* @param t a type
* @param superType a type that is a supertype of {@code t}
* @return the base type of t of the given element
*/
public static AnnotatedTypeMirror asSuper(Types types, AnnotatedTypeFactory atypeFactory,
AnnotatedTypeMirror t,
AnnotatedTypeMirror superType) {
if (asSuper == null ||
asSuper.types != types ||
asSuper.atypeFactory != atypeFactory) {
asSuper = new AsSuperTypeVisitor(types, atypeFactory);
}
AnnotatedTypeMirror result = asSuper.visit(t, superType);
return result;
}
private static AsSuperTypeVisitor asSuper;
private static class AsSuperTypeVisitor extends SimpleAnnotatedTypeVisitor {
private final Types types;
private final AnnotatedTypeFactory atypeFactory;
AsSuperTypeVisitor(Types types, AnnotatedTypeFactory atypeFactory) {
this.types = types;
this.atypeFactory = atypeFactory;
}
@Override
protected AnnotatedTypeMirror defaultAction(AnnotatedTypeMirror type, AnnotatedTypeMirror p) {
return type;
}
@Override
public AnnotatedTypeMirror visitPrimitive(AnnotatedPrimitiveType type, AnnotatedTypeMirror p) {
if (!p.getKind().isPrimitive()) {
return visit(atypeFactory.getBoxedType(type), p);
}
AnnotatedPrimitiveType pt = (AnnotatedPrimitiveType)p;
AnnotatedPrimitiveType st = pt.shallowCopy(false);
st.addAnnotations(type.getAnnotations());
return st;
}
@Override
public AnnotatedTypeMirror visitTypeVariable(AnnotatedTypeVariable type, AnnotatedTypeMirror p) {
if (p.getKind() == TypeKind.TYPEVAR) {
return type;
}
// Operate on the effective upper bound
AnnotatedTypeMirror res = asSuper(types, atypeFactory, type.getUpperBound(), p);
if (res != null) {
res.addMissingAnnotations(atypeFactory.getQualifierHierarchy().getTopAnnotations());
// TODO: or should it be the default?
// Test MultiBoundTypeVar fails otherwise.
// Is there a better place for this?
}
return res;
}
@Override
public AnnotatedTypeMirror visitWildcard(AnnotatedWildcardType type, AnnotatedTypeMirror p) {
if (p.getKind() == TypeKind.WILDCARD) {
return type;
}
return asSuper(types, atypeFactory, type.getExtendsBound(), p);
}
@Override
public AnnotatedTypeMirror visitArray(AnnotatedArrayType type, AnnotatedTypeMirror p) {
// Check if array component is subtype of the element
// first
if (shouldStop(p, type)) {
return type;
}
for (AnnotatedTypeMirror st : type.directSuperTypes()) {
AnnotatedTypeMirror x = asSuper(types, atypeFactory, st, p);
if (x != null) {
return isErased(types, x, p) ? x.getErased() : x;
}
}
return null;
}
@Override
public AnnotatedTypeMirror visitDeclared(AnnotatedDeclaredType type, AnnotatedTypeMirror p) {
// If visited Element is the desired one, we are done
if (p.getKind().isPrimitive()) {
if (TypesUtils.isBoxedPrimitive(type.getUnderlyingType())) {
return visit(atypeFactory.getUnboxedType(type), p);
} else {
// TODO: is there something better we could do?
// See tests/framework/Unboxing.java
return null;
}
}
if (shouldStop(p, type)) {
return type;
}
// Visit the superclass first!
for (AnnotatedDeclaredType st : type.directSuperTypes()) {
if (st.getKind() == TypeKind.DECLARED) {
AnnotatedDeclaredType x = (AnnotatedDeclaredType) asSuper(types, atypeFactory, st, p);
if (x != null) {
return x;
}
}
}
if (p.getKind() == TypeKind.TYPEVAR) {
return asSuper(types, atypeFactory, type, ((AnnotatedTypeVariable)p).getUpperBound());
}
if (p.getKind() == TypeKind.WILDCARD) {
return asSuper(types, atypeFactory, type, ((AnnotatedWildcardType)p).getExtendsBound().deepCopy());
}
return null;
}
@Override
public AnnotatedTypeMirror visitIntersection(AnnotatedIntersectionType type, AnnotatedTypeMirror p) {
if (shouldStop(p, type)) {
return type;
}
for (AnnotatedDeclaredType st : type.directSuperTypes()) {
AnnotatedDeclaredType x = (AnnotatedDeclaredType) asSuper(types, atypeFactory, st, p);
if (x != null) {
return x;
}
}
return null;
}
};
/**
* 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) {
switch (type.getKind()) {
case DECLARED:
AnnotatedDeclaredType declaredType = (AnnotatedDeclaredType) type;
do {
// Search among supers for a desired supertype
AnnotatedTypeMirror s = asSuper(types, atypeFactory, declaredType, superType);
if (s != null) {
return s;
}
// if not found immediately, try enclosing type
// like A in A.B
// TODO: AnnotatedDeclaredType.getEnclosingType() return an ATM with missing annotations.
// See Issue 724:
// https://github.com/typetools/checker-framework/issues/724
declaredType = declaredType.getEnclosingType();
} while (declaredType != null);
return null;
case ARRAY: // intentional follow-through
case TYPEVAR: // intentional follow-through
case WILDCARD:
return asSuper(types, atypeFactory, type, superType);
default:
return null;
}
}
/*
* Returns true if sup and sub are the same type.
* Returns false otherwise (including if sub cannot be a subtype of sup).
*/
private static boolean shouldStop(AnnotatedTypeMirror sup, AnnotatedTypeMirror sub) {
// Check if it's the same type
// if sup is primitive, but not sub
if (sup.getKind().isPrimitive() && !sub.getKind().isPrimitive()) {
/// XXX shouldn't this be "return false"?
return true;
}
if (sup.getKind().isPrimitive() && sub.getKind().isPrimitive()) {
return sup.getKind() == sub.getKind();
}
// if both are declared
if (sup.getKind() == TypeKind.DECLARED && sub.getKind() == TypeKind.DECLARED) {
AnnotatedDeclaredType supdt = (AnnotatedDeclaredType) sup;
AnnotatedDeclaredType subdt = (AnnotatedDeclaredType) sub;
// Check if it's the same name
if (!supdt.getUnderlyingType().asElement().equals(
subdt.getUnderlyingType().asElement()))
return false;
return true;
}
if (sup.getKind() == TypeKind.ARRAY && sub.getKind() == TypeKind.ARRAY) {
AnnotatedArrayType supat = (AnnotatedArrayType) sup;
AnnotatedArrayType subat = (AnnotatedArrayType) sub;
return shouldStop(supat.getComponentType(), subat.getComponentType());
}
// horrible horrible hack
// Types.isSameType() doesn't work for type variables or wildcards
return sup.getUnderlyingType().toString().equals(sub.getUnderlyingType().toString());
}
/**
* Tests that t2 is the erased type of t2
*
* @return true iff t2 is erased type of t1
*/
private static boolean isErased(Types types, AnnotatedTypeMirror t1, AnnotatedTypeMirror t2) {
return types.isSameType(
types.erasure(t1.getUnderlyingType()), t2.getUnderlyingType());
}
/**
* @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()) {
return 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);
if (base == null) {
// asSuper should not return null, but currently does in some cases.
// See Issue 717
// https://github.com/typetools/checker-framework/issues/717
return;
}
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 = (AnnotatedDeclaredType) asSuper(processingEnv.getTypeUtils(), atypeFactory, iterableType, iterableElmType);
if (dt == null) {
ErrorReporter.errorAbort("AnnotatedTypes.getIteratedType: not an iterable type: " + iterableType);
return null; // dead code
} else 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) {
//TODO: TEMPORARY KLUDGE
atypeFactory.getTypeArgumentInference().adaptMethodType(atypeFactory, expr, 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);
}
}
private static Set wildcards = Collections.newSetFromMap(new IdentityHashMap());
// TODO: compare to leastUpperBound method that is in comments further
// below and see how to incorporate the logic.
// Also see CFAbstractValue for other methods that should be in
// a better location.
public static AnnotatedTypeMirror leastUpperBound(ProcessingEnvironment processingEnv, AnnotatedTypeFactory atypeFactory,
AnnotatedTypeMirror a, AnnotatedTypeMirror b) {
List list = new ArrayList<>(2);
list.add(a);
list.add(b);
// It would be nice to use the following
// return leastUpperBound(processingEnv, atypeFactory, list);
// see commented-out version below.
TypeMirror lubType = InternalUtils.leastUpperBound(processingEnv, a.getUnderlyingType(), b.getUnderlyingType());
AnnotatedTypeMirror res = AnnotatedTypeMirror.createType(lubType, atypeFactory, false);
wildcards.clear();
annotateAsLub(processingEnv, atypeFactory, res, list);
wildcards.clear();
return res;
}
/* TODO: Add version that takes arbitrary number of arguments.
* At the moment, InternalUtils.leastUpperBound only exists for two arguments.
public static AnnotatedTypeMirror leastUpperBound(ProcessingEnvironment processingEnv, AnnotatedTypeFactory atypeFactory,
Collection types) {
com.sun.tools.javac.util.List utypes = com.sun.tools.javac.util.List.nil();
for (AnnotatedTypeMirror atm : types) {
utypes = utypes.append((Type) atm.getUnderlyingType());
}
JavacProcessingEnvironment javacEnv = (JavacProcessingEnvironment) processingEnv;
com.sun.tools.javac.code.Types jctypes = com.sun.tools.javac.code.Types.instance(javacEnv.getContext());
// jctypes.lub doesn't do the special handling of NULL that we have in InternalUtils.leastUpperBound
// Add that tu InternalUtils and use it here. Using the jctypes version crashes.
TypeMirror lubType = jctypes.lub(utypes);
AnnotatedTypeMirror res = AnnotatedTypeMirror.createType(lubType, atypeFactory, false);
annotateAsLub(processingEnv, atypeFactory, res, types);
return res;
}
*/
/**
* Annotate the lub type as if it is the least upper bound of the rest of
* the types. This is a useful method for finding conditional expression
* types.
*
* All the types need to be subtypes of lub.
*
* @param lub the type to be the least upper bound
* @param types the type arguments
*/
public static void annotateAsLub(ProcessingEnvironment processingEnv, AnnotatedTypeFactory atypeFactory,
AnnotatedTypeMirror lub, Collection types) {
Types typeutils = processingEnv.getTypeUtils();
Elements elements = processingEnv.getElementUtils();
// Is it anonymous?
if (lub.getKind() == TypeKind.INTERSECTION) {
// Find the intersect types
AnnotatedIntersectionType adt = (AnnotatedIntersectionType) lub;
for (AnnotatedDeclaredType adts : adt.directSuperTypes()) {
ArrayList subtypes = new ArrayList<>(types.size());
for (AnnotatedTypeMirror type : types) {
AnnotatedTypeMirror sup = asSuper(typeutils, atypeFactory, type, adts);
if (sup != null) {
subtypes.add(sup);
}
}
if (subtypes.size() > 0) {
adts.clearAnnotations();
}
addAnnotations(elements, atypeFactory, adts, subtypes);
ArrayList adtslist = new ArrayList();
adtslist.add(adts);
addAnnotations(elements, atypeFactory, lub, adtslist);
}
} else {
ArrayList subtypes = new ArrayList<>(types.size());
// TODO: This code needs some more serious thought.
if (lub.getKind() == TypeKind.WILDCARD) {
subtypes.add(lub.deepCopy());
} else {
for (AnnotatedTypeMirror type : types) {
if (type == null) {
continue;
}
AnnotatedTypeMirror ass = asSuper(typeutils, atypeFactory, type, lub);
if (ass == null) {
subtypes.add(type.deepCopy());
} else {
subtypes.add(ass);
}
}
}
if (subtypes.size() > 0) {
if (!findEffectiveAnnotations(atypeFactory.getQualifierHierarchy(), lub).isEmpty()) {
// I believe the only place this can happen is within recursive types and
// if we already have annotations than the type has been visited
return;
}
}
if (lub.getKind() == TypeKind.TYPEVAR) {
//TODO: TERRIBLE HACK UNTIL WE FIX LUB
final AnnotatedTypeVariable lubAtv = (AnnotatedTypeVariable) lub;
final List subtypesAsTvs =
LubTypeVariableAnnotator.getSubtypesAsTypevars(lubAtv, subtypes);
if (subtypesAsTvs != null) {
LubTypeVariableAnnotator.annotateTypeVarAsLub(lubAtv, subtypesAsTvs, atypeFactory);
} else {
addAnnotations(elements, atypeFactory, lub, subtypes);
}
} else {
addAnnotations(elements, atypeFactory, lub, subtypes);
}
}
}
/**
* Add the 'intersection' of the types provided to alub. This is a similar
* method to the one provided
* TODO: the above sentence should be finished somehow...
*/
private static void addAnnotations(Elements elements, AnnotatedTypeFactory atypeFactory,
AnnotatedTypeMirror alub,
ArrayList types) {
Set visited = Collections.newSetFromMap(new IdentityHashMap());
addAnnotationsImpl(elements, atypeFactory, alub, visited, types);
}
private static void addAnnotationsImpl(Elements elements, AnnotatedTypeFactory atypeFactory,
AnnotatedTypeMirror alub,
Set visited,
ArrayList types) {
// System.out.println("AnnotatedTypes.addAnnotationsImpl: alub: " + alub +
// "\n visited: " + visited +
// "\n types: " + types);
final AnnotatedTypeMirror origalub = alub;
boolean shouldAnnoOrig = false;
Set putOnOrig = AnnotationUtils.createAnnotationSet();
// get rid of wildcards and type variables
if (alub.getKind() == TypeKind.WILDCARD) {
final QualifierHierarchy qualifierHierarchy = atypeFactory.getQualifierHierarchy();
//TODO: TYPEVAR handling of LUB doesn't make much sense, as a stop gap for unannotated
//TODO: LOWER/UPPER bounds we'll glb the annotations and add them to the lower bound
//TODO: this will not handle component annotations if we have compound types as the
//TODO: lower bound (which for type variables would only happen on a capture)
Set lowerBounds = new HashSet<>(qualifierHierarchy.getTopAnnotations().size());
for (AnnotatedTypeMirror type : types) {
final Set annos = findEffectiveLowerBoundAnnotations(qualifierHierarchy, type);
if (lowerBounds.isEmpty()) {
lowerBounds = annos;
} else if (!annos.isEmpty()) { // for some reason this algorithm some times adds the lub itself
// into the list of type which may lead to an empty annos set
lowerBounds = qualifierHierarchy.greatestLowerBounds(lowerBounds, annos);
}
}
((AnnotatedWildcardType)alub).getSuperBound().replaceAnnotations(lowerBounds);
//TODO: AGAIN, ALL LUB CODE SHOULD BE EXTRACTED OUT TO ITS OWN CLASS OR THE TYPE HIERARCHY
//TODO: AND REWRITTEN
boolean allWildcards = true;
for (int i = 0; i < types.size() && allWildcards; i++) {
if (types.get(i).getKind() != TypeKind.WILDCARD) {
allWildcards = false;
}
}
if (allWildcards) {
if (wildcards.contains(alub.getUnderlyingType())) {
return;
}
wildcards.add(alub.getUnderlyingType());
final List upperBounds = new ArrayList<>(types.size());
for (final AnnotatedTypeMirror type : types) {
upperBounds.add(((AnnotatedWildcardType)type).getExtendsBound());
}
alub = ((AnnotatedWildcardType) alub).getExtendsBound();
annotateAsLub(atypeFactory.getProcessingEnv(), atypeFactory, alub, upperBounds);
return;
} else {
// old behavior
alub = ((AnnotatedWildcardType) alub).getExtendsBound();
}
// TODO using the getEffective versions copies objects, losing side-effects.
}
while (alub.getKind() == TypeKind.TYPEVAR) {
//TODO: TYPEVAR handling of LUB doesn't make much sense, as a stop gap for unannotated
//TODO: LOWER/UPPER bounds we'll glb the annotations and add them to the lower bound
//TODO: this will not handle component annotations if we have compound types as the
//TODO: lower bound (which for type variables would only happen on a capture)
Set glb = glbAll(atypeFactory.getQualifierHierarchy(), types);
((AnnotatedTypeVariable)alub).getLowerBound().replaceAnnotations(glb);
alub = ((AnnotatedTypeVariable)alub).getUpperBound();
}
if (visited.contains(alub)) {
return;
}
visited.add(alub);
for (int i = 0; i < types.size(); ++i) {
final AnnotatedTypeMirror typei = types.get(i);
if (!(typei.getAnnotations().isEmpty() ||
bottomsOnly(elements, atypeFactory, typei.getAnnotations()))) {
shouldAnnoOrig = true;
}
if (typei.getKind() == TypeKind.WILDCARD) {
putOnOrig.addAll(typei.getAnnotations());
AnnotatedWildcardType wildcard = (AnnotatedWildcardType) typei;
if (wildcard.getExtendsBound() != null) {
types.set(i, wildcard.getExtendsBound().deepCopy());
} else if (wildcard.getSuperBound() != null) {
types.set(i, wildcard.getSuperBound().deepCopy());
}
}
if (typei.getKind() == TypeKind.TYPEVAR) {
putOnOrig.addAll(typei.getAnnotations());
AnnotatedTypeVariable typevar = (AnnotatedTypeVariable) types.get(i);
if (typevar.getUpperBound() != null) {
types.set(i, typevar.getUpperBound());
} else if (typevar.getLowerBound() != null) {
types.set(i, typevar.getLowerBound());
}
}
}
Collection unification = Collections.emptySet();
boolean isFirst = true;
for (AnnotatedTypeMirror type : types) {
if (type.getAnnotations().isEmpty()) {
continue;
}
// TODO: unification fails with an empty set of annotations.
// Why are they sometimes empty, e.g. in the FlowNegation test case.
if (isFirst) {
unification = type.getAnnotations();
} else {
unification = atypeFactory.getQualifierHierarchy().leastUpperBounds(unification, type.getAnnotations());
}
isFirst = false;
}
// Remove a previously existing unqualified annotation on the type.
alub.replaceAnnotations(unification);
if (alub.getKind() == TypeKind.DECLARED) {
AnnotatedDeclaredType adt = (AnnotatedDeclaredType) alub;
for (int i = 0; i < adt.getTypeArguments().size(); ++i) {
AnnotatedTypeMirror adtArg = adt.getTypeArguments().get(i);
ArrayList dTypesArg = new ArrayList<>();
for (int j = 0; j < types.size(); ++j) {
if (types.get(j).getKind() == TypeKind.DECLARED) {
AnnotatedDeclaredType adtypej = (AnnotatedDeclaredType) types.get(j);
if (adtypej.getTypeArguments().size() == adt.getTypeArguments().size()) {
dTypesArg.add(adtypej.getTypeArguments().get(i));
} else {
// TODO: actually not just the number of type arguments should match, but
// the base types should be equal. See test case framework/GenericTest1
// for when this test fails.
}
}
}
//TODO: If we have a list of raw types, or null/raw types this code can create an empty set of
//TODO: type arguments (dTypesArg) below. This will then cause an exception.
//TODO: To test this: make the conditional always true and run the nullness checker on
//TODO: jdk/nullness/src/java/lang/ref/ReferenceQueue.java
//TODO: I think this will only happen when we "fix-up" the lub type with a wildcard
//TODO: in which case, the type annotator will add the annotation from the bound of
//TODO: the type parameter for which the wildcard is an argument and we will NOT have an
//TODO: unannotated type. That said, we might want to just call that here to ensure
//TODO: that when this is called in places that are not followed by annotate implicit
//TODO: the type is fully annotated
if (!dTypesArg.isEmpty()) {
addAnnotationsImpl(elements, atypeFactory, adtArg, visited, dTypesArg);
}
}
} else if (alub.getKind() == TypeKind.ARRAY) {
AnnotatedArrayType aat = (AnnotatedArrayType) alub;
ArrayList compTypes = new ArrayList<>();
for (AnnotatedTypeMirror atype : types) {
if (atype.getKind() == TypeKind.ARRAY) {
compTypes.add(((AnnotatedArrayType)atype).getComponentType());
}
}
if (aat.getComponentType().getKind() == TypeKind.TYPEVAR) {
//TODO: TERRIBLE HACK UNTIL WE FIX LUB
final AnnotatedTypeVariable lubAtv = (AnnotatedTypeVariable) aat.getComponentType();
final List subtypesAsTvs =
LubTypeVariableAnnotator.getSubtypesAsTypevars(lubAtv, compTypes);
if (subtypesAsTvs != null) {
LubTypeVariableAnnotator.annotateTypeVarAsLub(lubAtv, subtypesAsTvs, atypeFactory);
} else {
addAnnotationsImpl(elements, atypeFactory, aat.getComponentType(), visited, compTypes);
}
} else {
addAnnotationsImpl(elements, atypeFactory, aat.getComponentType(), visited, compTypes);
}
}
if (alub != origalub && shouldAnnoOrig) {
// These two are not the same if origalub is a wildcard or type variable.
// In that case, add the found annotations to the type variable also.
// Do not put the annotations inferred for the declared type
// on a type variable/wildcard.
// origalub.replaceAnnotations(alub.getAnnotations());
// Instead, keep track of the annotations that originally
// existed on the type variable, stored in putOnOrig, and
// put them back on now.
origalub.replaceAnnotations(putOnOrig);
}
}
/*
* Return true if all the qualifiers are bottom qualifiers. Allow fewer
* qualifiers to be present, which can happen for type variables and
* wildcards.
*/
private static boolean bottomsOnly(Elements elements, AnnotatedTypeFactory atypeFactory,
Set annotations) {
Set bots = AnnotationUtils.createAnnotationSet();
bots.addAll(atypeFactory.getQualifierHierarchy().getBottomAnnotations());
for (AnnotationMirror am : annotations) {
if (!bots.remove(am)) {
return false;
}
}
return true;
}
/* TODO: This least upper bound computation was originally
* in org.checkerframework.framework.flow.CFAbstractValue.
* It should be checked to make sure the implementation here is consistent.
* Afterwards it can be removed.
*
* Computes and returns the least upper bound of two
* {@link AnnotatedTypeMirror}.
*
*
* TODO: The code in this method is rather similar to
* {@link CFAbstractValue#mostSpecific(CFAbstractValue, CFAbstractValue)}.
* Can code be reused?
public AnnotatedTypeMirror leastUpperBound(AnnotatedTypeMirror type,
AnnotatedTypeMirror otherType) {
GenericAnnotatedTypeFactory factory = analysis.getTypeFactory();
ProcessingEnvironment processingEnv = factory.getProcessingEnv();
QualifierHierarchy qualifierHierarchy = factory.getQualifierHierarchy();
AnnotatedTypeMirror lubAnnotatedType;
if (type.getKind() == TypeKind.ARRAY
&& otherType.getKind() == TypeKind.ARRAY) {
// for arrays, we have:
// lub(@A1 A @A2[],@B1 B @B2[]) = lub(@A1 A, @B1 B) lub(@A2,@B2) []
AnnotatedArrayType a = (AnnotatedArrayType) type;
AnnotatedArrayType b = (AnnotatedArrayType) otherType;
AnnotatedTypeMirror componentLub = leastUpperBound(
a.getComponentType(), b.getComponentType());
if (componentLub.getUnderlyingType().getKind() == TypeKind.NONE) {
// If the components do not have an upper bound, then Object
// is still an upper bound of the array types.
Elements elements = analysis.getEnv().getElementUtils();
TypeMirror underlyingType = elements.getTypeElement(
"java.lang.Object").asType();
lubAnnotatedType = AnnotatedTypeMirror.createType(
underlyingType, factory, false);
} else {
TypeMirror underlyingType = TypesUtils.createArrayType(
analysis.getTypes(), componentLub.getUnderlyingType());
lubAnnotatedType = AnnotatedTypeMirror.createType(
underlyingType, factory, false);
AnnotatedArrayType aLubAnnotatedType = (AnnotatedArrayType) lubAnnotatedType;
aLubAnnotatedType.setComponentType(componentLub);
}
} else {
TypeMirror lubType = InternalUtils.leastUpperBound(processingEnv,
type.getUnderlyingType(), otherType.getUnderlyingType());
lubAnnotatedType = AnnotatedTypeMirror.createType(lubType, factory, false);
}
Set annos1;
Set annos2;
if (QualifierHierarchy.canHaveEmptyAnnotationSet(lubAnnotatedType)) {
annos1 = type.getAnnotations();
annos2 = otherType.getAnnotations();
} else {
annos1 = type.getEffectiveAnnotations();
annos2 = otherType.getEffectiveAnnotations();
}
lubAnnotatedType.addAnnotations(qualifierHierarchy.leastUpperBounds(
type, otherType, annos1, annos2));
TypeKind kind = lubAnnotatedType.getKind();
if (kind == TypeKind.WILDCARD) {
AnnotatedWildcardType wLubAnnotatedType = (AnnotatedWildcardType) lubAnnotatedType;
AnnotatedTypeMirror extendsBound = wLubAnnotatedType
.getExtendsBound();
extendsBound.clearAnnotations();
Collection extendsBound1 = getUpperBound(type);
Collection extendsBound2 = getUpperBound(otherType);
extendsBound.addAnnotations(qualifierHierarchy.leastUpperBounds(
extendsBound1, extendsBound2));
} else if (kind == TypeKind.TYPEVAR) {
AnnotatedTypeVariable tLubAnnotatedType = (AnnotatedTypeVariable) lubAnnotatedType;
AnnotatedTypeMirror upperBound = tLubAnnotatedType.getUpperBound();
Collection upperBound1 = getUpperBound(type);
Collection upperBound2 = getUpperBound(otherType);
// TODO: how is it possible that uppBound1 or 2 does not have any
// annotations?
if (upperBound1.size() != 0 && upperBound2.size() != 0) {
upperBound.clearAnnotations();
upperBound.addAnnotations(qualifierHierarchy.leastUpperBounds(
upperBound1, upperBound2));
}
// if only one of the input types were type variables, then we want
// the effective annotations and take the lub of them
if (type.getKind() != TypeKind.TYPEVAR || otherType.getKind() != TypeKind.TYPEVAR) {
// TODO Why the special treatment for NULL?
if (otherType.getKind() == TypeKind.NULL) {
// TODO Why the flipping between the two?
if (type.getKind() != TypeKind.TYPEVAR) {
AnnotatedTypeMirror tmp = otherType;
otherType = type;
type = tmp;
}
// Do these hold?
// assert type.getKind() == TypeKind.TYPEVAR ||
// type.getKind() == TypeKind.WILDCARD : "Unexpected type: " + type;
// assert otherType.getKind() != TypeKind.TYPEVAR : "Unexpected type variable: " + otherType;
lubAnnotatedType.clearAnnotations();
lubAnnotatedType.addAnnotations(type.getAnnotations());
for (AnnotationMirror top : qualifierHierarchy.getTopAnnotations()) {
AnnotationMirror o = otherType.getAnnotationInHierarchy(top);
assert o != null : "null should have all annotations.";
if (AnnotationUtils.areSame(o,
qualifierHierarchy.getBottomAnnotation(top))) {
// if the annotation on 'null' is the bottom
// annotation, take whatever is present on the type
// variable (even if it is nothing)...
// (already done)
} else {
// ... otherwise, take the LUB of the effective
// annotations.
lubAnnotatedType.replaceAnnotation(
qualifierHierarchy.leastUpperBound(o,
type.getEffectiveAnnotationInHierarchy(top)));
}
}
}
}
} else if (kind == TypeKind.ARRAY
&& !(type.getKind() == TypeKind.ARRAY && otherType.getKind() == TypeKind.ARRAY)) {
AnnotatedArrayType aLubAnnotatedType = (AnnotatedArrayType) lubAnnotatedType;
// lub(a,b) is an array, but not both a and b are arrays -> either a
// or b must be the null type.
AnnotatedArrayType array;
if (type.getKind() == TypeKind.ARRAY) {
assert otherType.getKind() == TypeKind.NULL;
array = (AnnotatedArrayType) type;
} else {
assert otherType.getKind() == TypeKind.ARRAY;
assert type.getKind() == TypeKind.NULL;
array = (AnnotatedArrayType) otherType;
}
// copy over annotations
copyArrayComponentAnnotations(array, aLubAnnotatedType);
}
return lubAnnotatedType;
}
*/
/**
* 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)) {
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;
}
// TODO: can't we do better than comparing the strings?
public static boolean areSame(AnnotatedTypeMirror t1, AnnotatedTypeMirror t2) {
return t1.toString().equals(t2.toString());
}
/**
* 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:
*
* - There should not be multiple annotations from the same hierarchy.
*
- There should not be more annotations than the width of the qualifier
* hierarchy.
*
- If the type is not a type variable, then the number of annotations
* should be the same as the width of the qualifier hierarchy.
*
- 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 (seenTops.contains(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();
}
/**
* @return the greatest lower bound of the primary annotations on the input types
*/
private static Set glbAll(final QualifierHierarchy qualifierHierarchy,
final Collection types) {
final Set result = AnnotationUtils.createAnnotationSet();
Map intermediate = AnnotationUtils.createAnnotationMap();
if (types.size() == 0) {
return result;
}
final Set tops = qualifierHierarchy.getTopAnnotations();
for (AnnotatedTypeMirror type : types) {
for (AnnotationMirror top : tops) {
final AnnotationMirror newAnno = type.getAnnotationInHierarchy(top);
final AnnotationMirror prevGlb = intermediate.get(top);
if (newAnno == null) {
continue;
} // else
if (prevGlb == null) {
intermediate.put(top, newAnno);
} else {
intermediate.put(top, qualifierHierarchy.greatestLowerBound(newAnno, prevGlb));
}
}
}
result.addAll(intermediate.values());
return result;
}
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;
}
}
}
}
}