package checkers.types;
import static javax.lang.model.util.ElementFilter.methodsIn;
import java.util.*;
import javax.annotation.processing.ProcessingEnvironment;
import javax.lang.model.element.*;
import javax.lang.model.type.TypeKind;
import javax.lang.model.type.WildcardType;
import javax.lang.model.util.Types;
import com.sun.source.tree.*;
import com.sun.source.util.TreePath;
import checkers.types.AnnotatedTypeMirror.*;
import checkers.types.visitors.SimpleAnnotatedTypeVisitor;
import checkers.util.ElementUtils;
import checkers.util.InternalUtils;
import checkers.util.TreeUtils;
import checkers.util.TypesUtils;
import checkers.nullness.quals.*;
/**
* Utility methods for operating on {@code AnnotatedTypeMirror}. This
* class mimics the class {@link Types}.
*/
public class AnnotatedTypes {
private ProcessingEnvironment env;
private AnnotatedTypeFactory factory;
static int uidCounter = 0;
int uid;
/**
* Constructor for {@code AnnotatedTypes}
*
* @param env the processing environment for this round
*/
public AnnotatedTypes(ProcessingEnvironment env, AnnotatedTypeFactory factory) {
this.env = env;
this.factory = factory;
uid = ++uidCounter;
}
@Override
public String toString() {
return getClass().getSimpleName() + "#" + uid;
}
/**
* 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 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 AnnotatedTypeMirror asSuper(AnnotatedTypeMirror t,
AnnotatedTypeMirror superType) {
return asSuper.visit(t, superType);
}
private SimpleAnnotatedTypeVisitor<AnnotatedTypeMirror, AnnotatedTypeMirror> asSuper =
new SimpleAnnotatedTypeVisitor<AnnotatedTypeMirror, AnnotatedTypeMirror>() {
@Override
protected AnnotatedTypeMirror defaultAction(AnnotatedTypeMirror type, AnnotatedTypeMirror p) {
return type;
}
@Override
public AnnotatedTypeMirror visitPrimitive(AnnotatedPrimitiveType type, AnnotatedTypeMirror p) {
if (!p.getKind().isPrimitive())
return visit(factory.getBoxedType(type), p);
AnnotatedPrimitiveType pt = (AnnotatedPrimitiveType)p;
AnnotatedPrimitiveType st = pt.getCopy(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 upper bound
return asSuper(type.getUpperBound(), p);
}
@Override
public AnnotatedTypeMirror visitWildcard(AnnotatedWildcardType type, AnnotatedTypeMirror p) {
if (p.getKind() == TypeKind.WILDCARD)
return type;
// Operate on the upper bound
return asSuper(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(st, p);
if (x != null)
return isErased(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())
return visit(factory.getUnboxedType(type), p);
if (shouldStop(p, type))
return type;
// Visit the superclass first!
for (AnnotatedDeclaredType st : type.directSuperTypes()) {
if (st.getKind() == TypeKind.DECLARED) {
AnnotatedDeclaredType x = (AnnotatedDeclaredType) asSuper(st, p);
if (x != null)
return x;
}
}
return null;
}
};
/**
* Return the base type of t or any of its outer types that starts
* with the given type. If none exists, return null.
*
* @param t a type
* @param elem a type
*/
private AnnotatedTypeMirror asOuterSuper(AnnotatedTypeMirror t,
AnnotatedTypeMirror elem) {
switch (t.getKind()) {
case DECLARED:
do {
// Search among supers for a desired supertype
AnnotatedTypeMirror s = asSuper(t, elem);
if (s != null)
return s;
// if not found immediately, try enclosing type
// like A in A.B
t = t.getEnclosingType();
} while (t != null && t.getKind() == TypeKind.DECLARED);
return null;
case ARRAY: // intentional follow-through
case TYPEVAR: // intentional follow-through
case WILDCARD:
return asSuper(t, elem);
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 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 boolean isErased(AnnotatedTypeMirror t1, AnnotatedTypeMirror t2) {
Types types = env.getTypeUtils();
return types.isSameType(
types.erasure(t1.getUnderlyingType()), t2.getUnderlyingType());
}
/**
* @see #asMemberOf(AnnotatedTypeMirror, Element)
*/
public AnnotatedExecutableType asMemberOf(AnnotatedTypeMirror t,
ExecutableElement elem) {
return (AnnotatedExecutableType)asMemberOf(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)}.
*
* @param t a type
* @param elem an element
*/
public AnnotatedTypeMirror asMemberOf(AnnotatedTypeMirror t, Element elem) {
// as Member of 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 factory.fromElement(elem);
}
AnnotatedTypeMirror type = asMemberOfImpl(t, elem);
if (!ElementUtils.isStatic(elem))
factory.postAsMemberOf(type, t, elem);
return type;
}
private AnnotatedTypeMirror asMemberOfImpl(AnnotatedTypeMirror t, Element elem) {
if (ElementUtils.isStatic(elem))
return factory.getAnnotatedType(elem);
// For Type Variable, operate on the upper
if (t.getKind() == TypeKind.TYPEVAR &&
((AnnotatedTypeVariable)t).getUpperBound() != null)
return asMemberOf(((AnnotatedTypeVariable) t).getUpperBound(),
elem);
if (t.getKind() == TypeKind.ARRAY
&& elem.getKind() == ElementKind.METHOD
&& elem.getSimpleName().contentEquals("clone")) {
AnnotatedExecutableType method = (AnnotatedExecutableType)factory.getAnnotatedType(elem);
return method.substitute(Collections.singletonMap(method.getReturnType(), t));
}
// I cannot think of why it wouldn't be a declared type!
// Defensive Programming
if (t.getKind() != TypeKind.DECLARED) {
return factory.getAnnotatedType(elem);
}
//
// Basic Algorithm:
// 1. Find the owner of the element
// 2. Find the base type of owner (e.g. type of owner as supertype
// of passed type)
// 3. Subsitute for type variables if any exist
TypeElement owner = ElementUtils.enclosingClass(elem);
// TODO: Potential bug if Raw type is used
if (ElementUtils.isStatic(elem) || owner.getTypeParameters().isEmpty())
return factory.getAnnotatedType(elem);
AnnotatedDeclaredType ownerType = factory.getAnnotatedType(owner);
AnnotatedDeclaredType base =
(AnnotatedDeclaredType) asOuterSuper(t, ownerType);
if (base == null)
return factory.getAnnotatedType(elem);
List<? extends AnnotatedTypeMirror> ownerParams =
ownerType.getTypeArguments();
List<? extends AnnotatedTypeMirror> baseParams =
base.getTypeArguments();
if (!ownerParams.isEmpty()) {
if (baseParams.isEmpty()) {
List<AnnotatedTypeMirror> baseParamsEr = new ArrayList<AnnotatedTypeMirror>();
for (AnnotatedTypeMirror arg : ownerParams)
baseParamsEr.add(arg.getErased());
return subst(factory.getAnnotatedType(elem), ownerParams, baseParamsEr);
}
return subst(factory.getAnnotatedType(elem), ownerParams, baseParams);
}
return factory.getAnnotatedType(elem);
}
/**
* Returns a new type, a copy of the passed {@code t}, with all
* instances of {@code from} type substituted with their correspondents
* in {@code to}.
*
* @param t the type
* @param from the from types
* @param to the to types
* @return the new type after substitutions
*/
public AnnotatedTypeMirror subst(AnnotatedTypeMirror t,
List<? extends AnnotatedTypeMirror> from,
List<? extends AnnotatedTypeMirror> to) {
Map<AnnotatedTypeMirror, AnnotatedTypeMirror> mappings =
new HashMap<AnnotatedTypeMirror, AnnotatedTypeMirror>();
for (int i = 0; i < from.size(); ++i) {
mappings.put(from.get(i), to.get(i));
}
return t.substitute(mappings);
}
/**
* Returns a deep copy of the passed type
*
* @param type the annotated type to be copied
* @return a deep copy of the passed type
*/
public AnnotatedTypeMirror deepCopy(AnnotatedTypeMirror type) {
// TODO: Test this, specify behaviour
return type.substitute(Collections.<AnnotatedTypeMirror,
AnnotatedTypeMirror>emptyMap());
}
/**
* 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 AnnotatedTypeMirror getIteratedType(AnnotatedTypeMirror iterableType) {
if (iterableType.getKind() == TypeKind.ARRAY) {
return ((AnnotatedArrayType) iterableType).getComponentType();
}
if (iterableType.getKind() == TypeKind.WILDCARD)
return getIteratedType(((AnnotatedWildcardType) iterableType).getExtendsBound());
if (iterableType.getKind() == TypeKind.TYPEVAR)
return getIteratedType(((AnnotatedTypeVariable) iterableType).getUpperBound());
if (iterableType.getKind() != TypeKind.DECLARED)
throw new IllegalArgumentException("Not iterable type: " + iterableType);
TypeElement iterableElement = env.getElementUtils().getTypeElement("java.lang.Iterable");
AnnotatedDeclaredType iterableElmType = factory.getAnnotatedType(iterableElement);
AnnotatedDeclaredType dt = (AnnotatedDeclaredType) asSuper(iterableType, iterableElmType);
if (dt == null)
throw new IllegalArgumentException("Not iterable type: " + iterableType);
else if (dt.getTypeArguments().isEmpty()) {
TypeElement e = env.getElementUtils().getTypeElement("java.lang.Object");
AnnotatedDeclaredType t = factory.fromElement(e);
t.clearAnnotations();
factory.annotateImplicit(e, t);
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 Set<AnnotatedDeclaredType> getSuperTypes(AnnotatedDeclaredType type) {
Set<AnnotatedDeclaredType> supertypes = new HashSet<AnnotatedDeclaredType>();
if (type == null)
return supertypes;
// Set up a stack containing the type mirror of subtype, which
// is our starting point.
Deque<AnnotatedDeclaredType> stack = new ArrayDeque<AnnotatedDeclaredType>();
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.<AnnotatedDeclaredType>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 Map<AnnotatedDeclaredType, ExecutableElement> overriddenMethods(
ExecutableElement method) {
final TypeElement elem = (TypeElement) method.getEnclosingElement();
final AnnotatedDeclaredType type = factory.getAnnotatedType(elem);
final Collection<AnnotatedDeclaredType> supertypes = getSuperTypes(type);
return overriddenMethods(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
* overriden by {@code method}
* @return an unmodified set of {@link ExecutableElement}s
* representing the elements that {@code method} overrides
* among {@code supertypes}
*/
public Map<AnnotatedDeclaredType, ExecutableElement> overriddenMethods(
ExecutableElement method, Collection<AnnotatedDeclaredType> supertypes) {
Map<AnnotatedDeclaredType, ExecutableElement> overrides =
new HashMap<AnnotatedDeclaredType, ExecutableElement>();
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 : methodsIn(superElement
.getEnclosedElements())) {
if (env.getElementUtils().overrides(method, supermethod,
superElement)) {
overrides.put(supertype, supermethod);
break;
}
}
}
return Collections.</*@NonNull*/ AnnotatedDeclaredType,
/*@NonNull*/ ExecutableElement>unmodifiableMap(overrides);
}
/**
* Given a method method invocation, it returns a mapping of the type variables
* to their parameters if any exist.
*
* It uses the method invocation type parameters if specified, otherwise
* it infers them based the passed arguments or the return type context,
* according to JLS 15.12.2.
*
* @param methodInvocation the method invocation tree
* @return the mapping of the type variables for this method invocation
*/
public Map<AnnotatedTypeVariable, AnnotatedTypeMirror>
findTypeArguments(MethodInvocationTree methodInvocation) {
Map<AnnotatedTypeVariable, AnnotatedTypeMirror> typeArguments =
new HashMap<AnnotatedTypeVariable, AnnotatedTypeMirror>();
ExecutableElement methodElt = TreeUtils.elementFromUse(methodInvocation);
// Is the method a generic method?
if (methodElt.getTypeParameters().isEmpty())
return typeArguments;
// Has the user supplied type arguments?
if (!methodInvocation.getTypeArguments().isEmpty()) {
for (int i = 0; i < methodElt.getTypeParameters().size(); ++i) {
AnnotatedTypeVariable typeVar = (AnnotatedTypeVariable)
factory.getAnnotatedType(methodElt.getTypeParameters().get(i));
AnnotatedTypeMirror typeArg =
factory.getAnnotatedTypeFromTypeTree(methodInvocation.getTypeArguments().get(i));
typeArguments.put(typeVar, typeArg);
}
return typeArguments;
}
return inferTypeArguments(methodInvocation);
}
/**
* It uses the method invocation type parameters if specified, otherwise
* it infers them based the passed arguments or the return type context,
* according to JLS 15.12.2.
*/
// Note that this implementation is buggy as it only infers arguments
// that make it to the return type. So it would fail for invocations to
// <T> void test(T arg1, T arg2)
// in such cases, T is infered to be '? extends T.upperBound'
private Map<AnnotatedTypeVariable, AnnotatedTypeMirror>
inferTypeArguments(MethodInvocationTree methodInvocation) {
//
// The basic algorithm used here, for each type variable:
// 1. Find the un-annotated least upper bound for the type variable
// by finding the type bound to variable within the return type
// 2. Infer the type argument annotations using the passed parameters
// 3. If the type variable is not used within a parameter, find
// the assignment context and infer the type argument using it
// 4. if not within an assignment context, then bind it to the extend bound.
Map<AnnotatedTypeVariable, AnnotatedTypeMirror> typeArguments =
new HashMap<AnnotatedTypeVariable, AnnotatedTypeMirror>();
ExecutableElement methodElt = TreeUtils.elementFromUse(methodInvocation);
// Find the un-annotated type
AnnotatedTypeMirror returnType = factory.type(methodInvocation);
factory.annotateImplicit(methodInvocation, returnType);
AnnotatedExecutableType methodType =
asMemberOf(factory.getReceiver(methodInvocation), methodElt);
for (TypeParameterElement var : methodElt.getTypeParameters()) {
// Find the un-annotated binding for the type variable
AnnotatedTypeVariable typeVar = (AnnotatedTypeVariable) factory.getAnnotatedType(var);
AnnotatedTypeMirror argument =
inferTypeArgUsingParams(typeVar, returnType, methodType, methodInvocation);
if (argument == null) {
// Using assignment context
assert factory.root != null : "root needs to be set when used on trees";
AnnotatedTypeMirror assigned =
assignedTo(TreePath.getPath(factory.root, methodInvocation));
if (assigned != null) {
AnnotatedTypeMirror returnTypeBase = asSuper(methodType.getReturnType(), assigned);
List<AnnotatedTypeMirror> lst =
new TypeResolutionFinder(typeVar).visit(returnTypeBase, assigned);
if (lst != null && !lst.isEmpty())
argument = lst.get(0);
}
}
if (argument == null) {
// should really be '? extends typeVar.getUpperBound()'
AnnotatedTypeMirror upperBound = typeVar.getUpperBound();
while (upperBound.getKind() == TypeKind.TYPEVAR)
upperBound = ((AnnotatedTypeVariable)upperBound).getUpperBound();
WildcardType wc = env.getTypeUtils().getWildcardType(upperBound.getUnderlyingType(), null);
@SuppressWarnings("deprecation")
AnnotatedWildcardType wctype = new AnnotatedWildcardType(wc, env, factory);
wctype.setElement(typeVar.getElement());
wctype.setExtendsBound(upperBound);
argument = wctype;
}
if (argument != null)
typeArguments.put(typeVar, argument);
}
return typeArguments;
}
private AnnotatedTypeMirror inferTypeArgUsingParams(AnnotatedTypeVariable typeVar,
AnnotatedTypeMirror returnType, AnnotatedExecutableType methodType,
MethodInvocationTree methodInvocation) {
TypeResolutionFinder finder = new TypeResolutionFinder(typeVar);
List<AnnotatedTypeMirror> lubForVar = finder.visit(methodType.getReturnType(), returnType);
// This may introduce a bug, but I don't want to deal with it right now
if (lubForVar.isEmpty())
return null;
// find parameter arguments beneficial for inference
List<AnnotatedTypeMirror> requiredParams =
expandVarArgs(methodType, methodInvocation.getArguments());
List<AnnotatedTypeMirror> passedArgs = new ArrayList<AnnotatedTypeMirror>();
for (int i = 0; i < requiredParams.size(); ++i) {
AnnotatedTypeMirror passedArg = factory.getAnnotatedType(
methodInvocation.getArguments().get(i));
AnnotatedTypeMirror requiredArg = requiredParams.get(i);
if (asSuper(passedArg, requiredArg) != null)
passedArg = asSuper(passedArg, requiredArg);
passedArgs.addAll(finder.visit(requiredArg, passedArg));
}
if (passedArgs.isEmpty())
return null;
// Found arguments! Great!
AnnotatedTypeMirror[] argsArray = passedArgs.toArray(new AnnotatedTypeMirror[0]);
annotateAsLub(lubForVar.get(0), argsArray);
return lubForVar.get(0);
}
private class TypeResolutionFinder
extends SimpleAnnotatedTypeVisitor<List<AnnotatedTypeMirror>, AnnotatedTypeMirror> {
private AnnotatedTypeVariable typeToFind;
public TypeResolutionFinder(AnnotatedTypeVariable typeToFind) {
this.typeToFind = typeToFind;
}
List<AnnotatedTypeMirror> visit(List<AnnotatedTypeMirror> types,
List<AnnotatedTypeMirror> other) {
List<AnnotatedTypeMirror> found = new ArrayList<AnnotatedTypeMirror>();
assert types.size() == other.size();
for (int i = 0; i < types.size(); ++i) {
List<AnnotatedTypeMirror> foundHere = visit(types.get(i), other.get(i));
found.addAll(foundHere);
}
return found;
}
@Override
public List<AnnotatedTypeMirror>
visitArray(AnnotatedArrayType type, AnnotatedTypeMirror p) {
if (p.getKind() == TypeKind.NULL)
return Collections.emptyList();
assert type.getKind() == p.getKind();
AnnotatedArrayType pArray = (AnnotatedArrayType) p;
AnnotatedTypeMirror typeToLookIn;
if (pArray.getComponentType().getKind().isPrimitive())
typeToLookIn = pArray;
else
typeToLookIn = pArray.getComponentType();
return visit(type.getComponentType(), typeToLookIn);
}
@Override
public List<AnnotatedTypeMirror>
visitDeclared(AnnotatedDeclaredType type, AnnotatedTypeMirror p) {
if (p.getKind() == TypeKind.NULL) {
return Collections.singletonList(p);
} else if (p.getKind() == TypeKind.WILDCARD) {
AnnotatedTypeMirror bound = ((AnnotatedWildcardType)p).getExtendsBound();
if (bound == null) return Collections.emptyList();
else return visitDeclared(type, bound);
}
AnnotatedDeclaredType pDeclared = (AnnotatedDeclaredType) asSuper(p, type);
// if one of them is erased do nothing
if (type.getTypeArguments().isEmpty()
|| pDeclared == null || pDeclared.getTypeArguments().isEmpty())
return Collections.emptyList();
return visit(type.getTypeArguments(), pDeclared.getTypeArguments());
}
@Override
public List<AnnotatedTypeMirror>
visitExecutable(AnnotatedExecutableType type, AnnotatedTypeMirror p) {
assert type.getKind() == p.getKind();
AnnotatedExecutableType pExecutable = (AnnotatedExecutableType)p;
// Do do return nor type variables
return visit(type.getParameterTypes(), pExecutable.getParameterTypes());
}
@Override
public List<AnnotatedTypeMirror>
visitTypeVariable(AnnotatedTypeVariable type, AnnotatedTypeMirror p) {
Element elem = type.getUnderlyingType().asElement();
if (elem.equals(typeToFind.getUnderlyingType().asElement())) {
return Collections.singletonList(p);
}
return Collections.emptyList();
}
@Override
public List<AnnotatedTypeMirror>
visitWildcard(AnnotatedWildcardType type, AnnotatedTypeMirror p) {
List<AnnotatedTypeMirror> types = new ArrayList<AnnotatedTypeMirror>();
if (type.getExtendsBound() != null)
types.addAll(visit(type.getExtendsBound(), p));
if (type.getSuperBound() != null)
types.addAll(visit(type.getSuperBound(), p));
return types;
}
@Override
public List<AnnotatedTypeMirror>
defaultAction(AnnotatedTypeMirror type, AnnotatedTypeMirror p) {
return Collections.emptyList();
}
}
/**
* Returns the annotated type that the leaf of path is assigned to, if it
* is within an assignment context
* Returns the annotated type that the method invocation at the leaf
* is assigned to.
*
* @param path
* @return type that it path leaf is assigned to
*/
public AnnotatedTypeMirror assignedTo(TreePath path) {
Tree assignmentContext = TreeUtils.getAssignmentContext(path);
if (assignmentContext == null) {
return null;
} else if (assignmentContext instanceof AssignmentTree) {
ExpressionTree variable = ((AssignmentTree)assignmentContext).getVariable();
return factory.getAnnotatedType(variable);
} else if (assignmentContext instanceof CompoundAssignmentTree) {
ExpressionTree variable =
((CompoundAssignmentTree)assignmentContext).getExpression();
return factory.getAnnotatedType(variable);
} else if (assignmentContext instanceof MethodInvocationTree) {
MethodInvocationTree methodInvocation = (MethodInvocationTree)assignmentContext;
// TODO move to getAssignmentContext
if (methodInvocation.getMethodSelect() instanceof MemberSelectTree
&& ((MemberSelectTree)methodInvocation.getMethodSelect()).getExpression() == path.getLeaf())
return null;
ExecutableElement methodElt = TreeUtils.elementFromUse(methodInvocation);
AnnotatedTypeMirror receiver = factory.getReceiver(methodInvocation);
AnnotatedExecutableType method = asMemberOf(receiver, methodElt);
int treeIndex = -1;
for (int i = 0; i < method.getParameterTypes().size(); ++i) {
if (TreeUtils.skipParens(methodInvocation.getArguments().get(i)) == path.getLeaf()) {
treeIndex = i;
break;
}
}
if (treeIndex == -1) return null;
return method.getParameterTypes().get(treeIndex);
} else if (assignmentContext instanceof NewArrayTree) {
// FIXME: This may cause infinite loop
AnnotatedTypeMirror type =
factory.getAnnotatedType((NewArrayTree)assignmentContext);
while (type.getKind() == TypeKind.ARRAY)
type = ((AnnotatedArrayType)type).getComponentType();
return type;
} else if (assignmentContext instanceof NewClassTree) {
// This need to be basically like MethodTree
NewClassTree newClassTree = (NewClassTree) assignmentContext;
ExecutableElement constructorElt = InternalUtils.constructor(newClassTree);
AnnotatedExecutableType constructor =
factory.getAnnotatedType(constructorElt);
int treeIndex = -1;
for (int i = 0; i < constructor.getParameterTypes().size(); ++i) {
if (TreeUtils.skipParens(newClassTree.getArguments().get(i)) == path.getLeaf()) {
treeIndex = i;
break;
}
}
if (treeIndex == -1) return null;
return constructor.getParameterTypes().get(treeIndex);
} else if (assignmentContext instanceof ReturnTree) {
MethodTree method = TreeUtils.enclosingMethod(path);
return (factory.getAnnotatedType(method)).getReturnType();
} else if (assignmentContext instanceof VariableTree) {
return factory.getAnnotatedType((VariableTree)assignmentContext);
}
throw new AssertionError("Shouldn't be here!");
}
/**
* Determines if the type is for an anonymous type or not
*
* @param type type to be checked
* @return true iff type is an anonymous type
*/
public boolean isAnonymousType(AnnotatedTypeMirror type) {
return TypesUtils.isAnonymousType(type.getUnderlyingType());
}
/**
* Determines if the type is for an intersect type or not
*
* @param type type to be checked
* @return true iff type is an intersect type
*/
public boolean isIntersectType(AnnotatedTypeMirror type) {
return isAnonymousType(type) &&
type.getUnderlyingType().toString().contains("&");
}
/**
* Annotate the lub type as if it is the least upper bound of the rest of
* the types. This is a useful method for the 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 void annotateAsLub(AnnotatedTypeMirror lub,
AnnotatedTypeMirror ...types) {
// It it anonoymous
if (isAnonymousType(lub)) {
// Find the intersect types
AnnotatedDeclaredType adt = (AnnotatedDeclaredType)lub;
for (AnnotatedDeclaredType adts : adt.directSuperTypes()) {
AnnotatedTypeMirror[] subtypes = new AnnotatedTypeMirror[types.length];
for (int i = 0; i < types.length; ++i) {
subtypes[i] = asSuper(types[i], adts);
}
addAnnotations(adts, subtypes);
this.addAnnotations(lub, adts);
}
} else {
AnnotatedTypeMirror[] subtypes = new AnnotatedTypeMirror[types.length];
for (int i = 0; i < types.length; ++i) {
AnnotatedTypeMirror type = types[i];
if (type.getKind() == TypeKind.WILDCARD &&
((AnnotatedWildcardType)type).getSuperBound() != null)
type = ((AnnotatedWildcardType)type).getSuperBound();
if (type == null)
return;
if (type.getKind() == TypeKind.WILDCARD)
subtypes[i] = lub.getCopy(true);
else if (asSuper(type, lub) == null)
subtypes[i] = lub.getCopy(true);
else
subtypes[i] = asSuper(type, lub);
}
if (subtypes.length > 0)
lub.clearAnnotations();
addAnnotations(lub, subtypes);
}
}
/**
* Add the 'intersection' of the types provided to alub. This is a similar
* method to the one provided
*/
private void addAnnotations(AnnotatedTypeMirror alub,
AnnotatedTypeMirror ...types) {
// types may contain a null in the context of unchecked cast
// TODO: fix this
boolean isFirst = true;
// get rid of wildcards
if (alub.getKind() == TypeKind.WILDCARD)
alub = ((AnnotatedWildcardType)alub).getExtendsBound();
for (int i = 0; i < types.length; ++i) {
if (types[i] == null)
continue; // TODO: fix this
if (types[i].getKind() == TypeKind.WILDCARD) {
AnnotatedWildcardType wildcard = (AnnotatedWildcardType)types[i];
if (wildcard.getExtendsBound() != null)
types[i] = wildcard.getExtendsBound();
else if (wildcard.getSuperBound() != null)
types[i] = wildcard.getSuperBound();
}
}
Collection<AnnotationMirror> unification = Collections.emptySet();
for (AnnotatedTypeMirror type : types) {
if (type == null)
continue; // TODO: fix this
if (type.getKind() == TypeKind.NULL && !type.isAnnotated()) continue;
if (isFirst)
unification = type.getAnnotations();
else
unification = factory.unify(unification, type.getAnnotations());
isFirst = false;
}
alub.addAnnotations(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);
AnnotatedTypeMirror[] dTypesArg = new AnnotatedTypeMirror[types.length];
for (int j = 0; j < types.length; ++j) {
if (types[j].getKind() == TypeKind.NULL)
dTypesArg[j] = types[j];
else
dTypesArg[j] = ((AnnotatedDeclaredType)types[j]).getTypeArguments().get(i);
}
addAnnotations(adtArg, dTypesArg);
}
} else if (alub.getKind() == TypeKind.ARRAY) {
AnnotatedArrayType aat = (AnnotatedArrayType) alub;
AnnotatedTypeMirror[] compTypes = new AnnotatedTypeMirror[types.length];
for (int i = 0; i < types.length; ++i) {
if (types[i].getKind() == TypeKind.NULL)
compTypes[i] = types[i];
else
compTypes[i] = ((AnnotatedArrayType)types[i]).getComponentType();
}
addAnnotations(aat.getComponentType(), compTypes);
}
}
/**
* 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 int getArrayDepth(AnnotatedArrayType array) {
int counter = 0;
AnnotatedTypeMirror type = array;
while (type.getKind() == TypeKind.ARRAY) {
counter++;
type = ((AnnotatedArrayType)type).getComponentType();
}
return counter;
}
/**
* 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 List<AnnotatedTypeMirror> expandVarArgs(AnnotatedExecutableType method,
List<? extends ExpressionTree> args) {
List<AnnotatedTypeMirror> 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 = factory.getAnnotatedType(args.get(args.size() - 1));
if (lastArg.getKind() == TypeKind.ARRAY &&
getArrayDepth(varargs) == getArrayDepth((AnnotatedArrayType)lastArg))
return parameters;
}
parameters = new ArrayList<AnnotatedTypeMirror>(parameters.subList(0, parameters.size() - 1));
for (int i = args.size() - parameters.size(); i > 0; --i)
parameters.add(varargs.getComponentType());
return parameters;
}
/**
* Return a list of the AnnotatedTypeMirror of the passed
* expression trees, in the same order as the trees.
*
* @param trees the AST nodes
* @return a list with the AnnotatedTypeMirror of each tree in trees.
*/
public List<AnnotatedTypeMirror> getAnnotatedTypes(
Iterable<? extends ExpressionTree> trees) {
List<AnnotatedTypeMirror> types =
new ArrayList<AnnotatedTypeMirror>();
for (ExpressionTree tree : trees)
types.add(factory.getAnnotatedType(tree));
return types;
}
public boolean areSame(AnnotatedTypeMirror t1, AnnotatedTypeMirror t2) {
return t1.toString().equals(t2.toString());
}
public static AnnotatedTypeMirror innerMostType(AnnotatedTypeMirror t) {
AnnotatedTypeMirror inner = t;
while (inner.getKind() == TypeKind.ARRAY)
inner = ((AnnotatedArrayType)inner).getComponentType();
return inner;
}
}