package checkers.types;
import java.lang.annotation.ElementType;
import java.lang.annotation.Target;
import java.util.*;
import javax.lang.model.element.*;
import javax.lang.model.type.*;
import javax.swing.event.ListSelectionEvent;
import checkers.types.AnnotatedTypeMirror.*;
import checkers.util.*;
import com.sun.source.tree.*;
import com.sun.source.util.*;
import java.lang.annotation.Annotation;
/**
* A utility class used to abstract common functionality from tree-to-type
* converters. By default, when visiting a tree for which a visitor method has
* not explicitly been provided, the visitor will throw an
* {@link UnsupportedOperationException}; when visiting a null tree, it will
* throw an {@link IllegalArgumentException}.
*/
abstract class TypeFromTree extends
SimpleTreeVisitor<AnnotatedTypeMirror, AnnotatedTypeFactory> {
@Override
public AnnotatedTypeMirror defaultAction(Tree node, AnnotatedTypeFactory f) {
if (node == null)
throw new IllegalArgumentException("null tree");
throw new UnsupportedOperationException(
"conversion undefined for tree type " + node.getKind());
}
static void addAnnotationsToElt(AnnotatedTypeMirror type,
List<? extends AnnotationMirror> annotations) {
// Annotate the inner most array
AnnotatedTypeMirror innerType = AnnotatedTypes.innerMostType(type);
for (AnnotationMirror anno : annotations) {
if (isTypeAnnotation(anno)) {
innerType.addAnnotation(anno);
} else
type.addAnnotation(anno);
}
}
private static Map<TypeElement, Boolean> isTypeCache = new IdentityHashMap<TypeElement, Boolean>();
private static boolean isTypeAnnotation(AnnotationMirror anno) {
TypeElement elem = (TypeElement)anno.getAnnotationType().asElement();
if (isTypeCache.containsKey(elem))
return isTypeCache.get(elem);
boolean result = isTypeAnnotationImpl(elem);
isTypeCache.put(elem, result);
return result;
}
private static boolean isTypeAnnotationImpl(TypeElement type) {
Target target = type.getAnnotation(Target.class);
if (target == null)
return true;
for (ElementType et : target.value()) {
if (et == ElementType.TYPE_USE)
return true;
}
return false;
}
/**
* A singleton that obtains the annotated type of an {@link ExpressionTree}.
*
* <p>
*
* All subtypes of {@link ExpressionTree} are supported, except:
* <ul>
* <li>{@link AnnotationTree}</li>
* <li>{@link ErroneousTree}</li>
* </ul>
*
* @see AnnotatedTypeFactory#fromExpression(ExpressionTree)
*/
static class TypeFromExpression extends TypeFromTree {
/** The singleton instance. */
public static final TypeFromExpression INSTANCE
= new TypeFromExpression();
private TypeFromExpression() {}
@Override
public AnnotatedTypeMirror visitAnnotatedType(AnnotatedTypeTree node,
AnnotatedTypeFactory f) {
return f.fromTypeTree(node);
}
@Override
public AnnotatedTypeMirror visitArrayAccess(ArrayAccessTree node,
AnnotatedTypeFactory f) {
AnnotatedTypeMirror type = f.getAnnotatedType(node.getExpression());
assert type instanceof AnnotatedArrayType;
return ((AnnotatedArrayType)type).getComponentType();
}
@Override
public AnnotatedTypeMirror visitAssignment(AssignmentTree node,
AnnotatedTypeFactory f) {
// Recurse on the type of the variable.
return visit(node.getVariable(), f);
}
@Override
public AnnotatedTypeMirror visitBinary(BinaryTree node,
AnnotatedTypeFactory f) {
return f.type(node);
}
@Override
public AnnotatedTypeMirror visitCompoundAssignment(
CompoundAssignmentTree node, AnnotatedTypeFactory f) {
// Recurse on the type of the variable.
return visit(node.getVariable(), f);
}
@Override
public AnnotatedTypeMirror visitConditionalExpression(
ConditionalExpressionTree node, AnnotatedTypeFactory f) {
AnnotatedTypes annoTypes = f.atypes;
AnnotatedTypeMirror trueType
= f.getAnnotatedType(node.getTrueExpression());
AnnotatedTypeMirror falseType
= f.getAnnotatedType(node.getFalseExpression());
if (trueType.equals(falseType))
return trueType;
// If one of them is null, return the other
if (trueType.getKind() == TypeKind.NULL) {
Collection<AnnotationMirror> alub = f.qualHierarchy.leastUpperBound(trueType.getAnnotations(), falseType.getAnnotations());
falseType.clearAnnotations();
falseType.addAnnotations(alub);
return falseType;
} else if (falseType.getKind() == TypeKind.NULL) {
Collection<AnnotationMirror> alub = f.qualHierarchy.leastUpperBound(trueType.getAnnotations(), falseType.getAnnotations());
trueType.clearAnnotations();
trueType.addAnnotations(alub);
return trueType;
}
AnnotatedTypeMirror alub = f.type(node);
TypeMirror lub = alub.getUnderlyingType();
// It it anonoymous
if (TypesUtils.isAnonymousType(lub)) {
// Find the intersect types
f.atypes.annotateAsLub(alub, trueType, falseType);
} else {
trueType = annoTypes.asSuper(trueType, alub);
falseType = annoTypes.asSuper(falseType, alub);
if (trueType.equals(falseType))
return trueType;
f.atypes.annotateAsLub(alub, trueType, falseType);
}
return alub;
}
@Override
public AnnotatedTypeMirror visitIdentifier(IdentifierTree node,
AnnotatedTypeFactory f) {
Element elt = TreeUtils.elementFromUse(node);
if (node.getName().contentEquals("this")
&& elt.getKind() != ElementKind.CONSTRUCTOR)
return f.getSelfType(node);
AnnotatedTypeMirror selfType = f.getImplicitReceiverType(node);
if (selfType != null)
return f.atypes.asMemberOf(selfType, elt);
return f.getAnnotatedType(elt);
// return f.fromElement(elt);
}
@Override
public AnnotatedTypeMirror visitInstanceOf(InstanceOfTree node,
AnnotatedTypeFactory f) {
return f.type(node);
}
@Override
public AnnotatedTypeMirror visitLiteral(LiteralTree node,
AnnotatedTypeFactory f) {
return f.type(node);
}
@Override
public AnnotatedTypeMirror visitMemberSelect(MemberSelectTree node,
AnnotatedTypeFactory f) {
Element elt = TreeUtils.elementFromUse(node);
if (elt.getKind().isClass() || elt.getKind().isInterface())
return f.fromElement(elt);
// The expression might be a primitive type (as in "int.class").
if (!(node.getExpression() instanceof PrimitiveTypeTree)) {
if (node.getIdentifier().contentEquals("this"))
return f.getEnclosingType((TypeElement)InternalUtils.symbol(node.getExpression()), node);
// We need the original t with the implicit annotations
AnnotatedTypeMirror t = f.getAnnotatedType(node.getExpression());
if (t instanceof AnnotatedDeclaredType)
return f.atypes.asMemberOf(t, elt);
}
return f.fromElement(elt);
}
@Override
public AnnotatedTypeMirror visitMethodInvocation(
MethodInvocationTree node, AnnotatedTypeFactory f) {
AnnotatedExecutableType ex = f.methodFromUse(node);
return ex.getReturnType();
}
@Override
public AnnotatedTypeMirror visitNewArray(NewArrayTree node,
AnnotatedTypeFactory f) {
// Don't use fromTypeTree here, because node.getType() is not an
// array type!
AnnotatedArrayType result = (AnnotatedArrayType)f.type(node);
if (node.getType() == null) // e.g., byte[] b = {(byte)1, (byte)2};
return result;
annotateArrayAsArray(result, node, f);
return result;
}
private AnnotatedTypeMirror descendBy(AnnotatedTypeMirror type, int depth) {
AnnotatedTypeMirror result = type;
while (depth > 0) {
result = ((AnnotatedArrayType)result).getComponentType();
depth--;
}
return result;
}
private void annotateArrayAsArray(AnnotatedArrayType result, NewArrayTree node, AnnotatedTypeFactory f) {
// Copy annotations from the type.
AnnotatedTypeMirror treeElem = f.fromTypeTree(node.getType());
boolean hasInit = node.getInitializers() != null;
AnnotatedTypeMirror typeElem = descendBy(result,
hasInit ? 1 : node.getDimensions().size());
while (true) {
typeElem.addAnnotations(treeElem.getAnnotations());
if (!(treeElem instanceof AnnotatedArrayType)) break;
assert typeElem instanceof AnnotatedArrayType;
treeElem = ((AnnotatedArrayType)treeElem).getComponentType();
typeElem = ((AnnotatedArrayType)typeElem).getComponentType();
}
// Add all dimension annotations.
//int idx = arrayDim(result) - 1;
int idx = 0;
AnnotatedTypeMirror level = result;
while (level.getKind() == TypeKind.ARRAY) {
AnnotatedArrayType array = (AnnotatedArrayType)level;
List<? extends AnnotationMirror> annos = InternalUtils.annotationsFromArrayCreation(node, idx++);
array.addAnnotations(annos);
level = array.getComponentType();
}
// Add top-level annotations.
result.addAnnotations(InternalUtils.annotationsFromArrayCreation(node, -1));
}
private int arrayDim(AnnotatedArrayType array) {
AnnotatedTypeMirror type = array;
int result = 0;
while (type.getKind() == TypeKind.ARRAY) {
type = ((AnnotatedArrayType)type).getComponentType();
result++;
}
return result;
}
private void annotateArrayAsCanonical(AnnotatedArrayType result, NewArrayTree node, AnnotatedTypeFactory f) {
annotateArrayAsArray(result, node, f);
}
@Override
public AnnotatedTypeMirror visitNewClass(NewClassTree node,
AnnotatedTypeFactory f) {
// Use the annotated type of the part between "new" and the
// constructor arguments.
AnnotatedDeclaredType type = f.fromNewClass(node);
if (node.getClassBody() != null) {
DeclaredType dt = (DeclaredType)InternalUtils.typeOf(node);
AnnotatedDeclaredType anonType =
(AnnotatedDeclaredType)AnnotatedTypeMirror.createType(dt, f.env, f);
anonType.setElement(type.getElement());
if (type.isAnnotated()) {
List<AnnotatedDeclaredType> supertypes = Collections.singletonList(type);
anonType.setDirectSuperTypes(supertypes);
anonType.addAnnotations(type.getAnnotations());
f.postDirectSuperTypes(anonType, supertypes);
}
type = anonType;
}
return type;
}
@Override
public AnnotatedTypeMirror visitParenthesized(ParenthesizedTree node,
AnnotatedTypeFactory f) {
// Recurse on the expression inside the parens.
return visit(node.getExpression(), f);
}
@Override
public AnnotatedTypeMirror visitTypeCast(TypeCastTree node,
AnnotatedTypeFactory f) {
// Use the annotated type of the type in the cast.
return f.fromTypeTree(node.getType());
}
@Override
public AnnotatedTypeMirror visitUnary(UnaryTree node,
AnnotatedTypeFactory f) {
// TODO: why not visit(node.getExpression(), f)
return f.type(node);
}
@Override
public AnnotatedTypeMirror visitWildcard(WildcardTree node,
AnnotatedTypeFactory f) {
AnnotatedTypeMirror bound = visit(node.getBound(), f);
AnnotatedTypeMirror result = f.type(node);
assert result instanceof AnnotatedWildcardType;
if (node.getKind() == Tree.Kind.SUPER_WILDCARD)
((AnnotatedWildcardType)result).setSuperBound(bound);
else if (node.getKind() == Tree.Kind.EXTENDS_WILDCARD)
((AnnotatedWildcardType)result).setExtendsBound(bound);
return result;
}
@Override
public AnnotatedTypeMirror visitPrimitiveType(PrimitiveTypeTree node,
AnnotatedTypeFactory f) {
// for e.g. "int.class"
return f.fromTypeTree(node);
}
@Override
public AnnotatedTypeMirror visitArrayType(ArrayTypeTree node,
AnnotatedTypeFactory f) {
// for e.g. "int[].class"
return f.fromTypeTree(node);
}
}
/**
* A singleton that obtains the annotated type of a method or variable from
* its declaration.
*
* @see AnnotatedTypeFactory#fromMember(Tree)
*/
static class TypeFromMember extends TypeFromTree {
/** The singleton instance. */
public static final TypeFromMember INSTANCE
= new TypeFromMember();
private TypeFromMember() {}
@Override
public AnnotatedTypeMirror visitVariable(VariableTree node,
AnnotatedTypeFactory f) {
AnnotatedTypeMirror result = f.fromTypeTree(node.getType());
Element elt = TreeUtils.elementFromDeclaration(node);
result.setElement(elt);
addAnnotationsToElt(result, elt.getAnnotationMirrors());
return result;
}
@Override
public AnnotatedTypeMirror visitMethod(MethodTree node,
AnnotatedTypeFactory f) {
ExecutableElement elt = TreeUtils.elementFromDeclaration(node);
AnnotatedExecutableType result =
(AnnotatedExecutableType)f.toAnnotatedType(elt.asType());
result.setElement(elt);
// Annotate the parameter types.
List<AnnotatedTypeMirror> paramTypes
= new LinkedList<AnnotatedTypeMirror>();
for (Tree t : node.getParameters())
paramTypes.add(visit(t, f));
result.setParameterTypes(paramTypes);
// Annotate the return type.
if (node.getReturnType() == null)
result.setReturnType(f.toAnnotatedType(f.types.getNoType(TypeKind.VOID)));
else
result.setReturnType(f.fromTypeTree(node.getReturnType()));
/* We should not add all annotations to the return type. Method annotations are not applicable
* @author Slawek Rudnicki
*/
List<AnnotationMirror> returnTypeAnnotations = new LinkedList<AnnotationMirror>();
for (AnnotationMirror m : elt.getAnnotationMirrors()) {
if (!AnnotationUtils.hasTarget(m, ElementType.METHOD)
&& !AnnotationUtils.hasTarget(m, ElementType.CONSTRUCTOR))
returnTypeAnnotations.add(m);
else
result.addAnnotation(m);
}
addAnnotationsToElt(result.getReturnType(), returnTypeAnnotations);
// Annotate the receiver.
List<AnnotationMirror> receiverAnnos =
InternalUtils.annotationsFromTypeAnnotationTrees(node.getReceiverAnnotations());
if (ElementUtils.isStatic(elt))
// TODO maybe it should be TypeKind.NONE
result.setReceiverType(null);
else {
AnnotatedDeclaredType enclosing;
{
Element enclElt = ElementUtils.enclosingClass(elt);
AnnotatedTypeMirror t = f.fromElement(enclElt);
assert t instanceof AnnotatedDeclaredType : t;
enclosing = (AnnotatedDeclaredType)t;
}
enclosing.clearAnnotations();
enclosing.addAnnotations(receiverAnnos);
if (TreeUtils.isConstructor(node)) {
for (AnnotationMirror a : elt.getAnnotationMirrors()) {
if (!AnnotationUtils.hasTarget(a, ElementType.CONSTRUCTOR)) {
enclosing.addAnnotation(a);
}
}
}
result.setReceiverType(enclosing);
}
// Annotate the type parameters.
List<AnnotatedTypeVariable> typeParams
= new LinkedList<AnnotatedTypeVariable>();
for (Tree t : node.getTypeParameters()) {
AnnotatedTypeMirror type = f.fromTypeTree(t);
assert type instanceof AnnotatedTypeVariable;
typeParams.add((AnnotatedTypeVariable)type);
}
result.setTypeVariables(typeParams);
// Annotate throws types.
List<AnnotatedTypeMirror> throwsTypes
= new LinkedList<AnnotatedTypeMirror>();
for (Tree t : node.getThrows())
throwsTypes.add(f.fromTypeTree(t));
result.setThrownTypes(throwsTypes);
return result;
}
}
/**
* A singleton that obtains the annotated type of a class from its
* declaration.
*
* @see AnnotatedTypeFactory#fromClass(ClassTree)
*/
static class TypeFromClass extends TypeFromTree {
/** The singleton instance. */
public static final TypeFromClass INSTANCE
= new TypeFromClass();
private TypeFromClass() {}
Map<ClassTree, AnnotatedDeclaredType> visited =
new HashMap<ClassTree, AnnotatedDeclaredType>();
@Override
public AnnotatedTypeMirror visitClass(ClassTree node,
AnnotatedTypeFactory f) {
if (visited.containsKey(node))
return visited.get(node);
TypeElement elt = TreeUtils.elementFromDeclaration(node);
AnnotatedTypeMirror result = f.toAnnotatedType(elt.asType());
result.setElement(elt);
result.addAnnotations(elt.getAnnotationMirrors());
assert result instanceof AnnotatedDeclaredType;
AnnotatedDeclaredType dt = (AnnotatedDeclaredType)result;
visited.put(node, dt);
// Get annotations on type parameters.
List<AnnotatedTypeMirror> params = new LinkedList<AnnotatedTypeMirror>();
for (Tree tree : node.getTypeParameters()) {
params.add(f.fromTypeTree(tree));
}
dt.setTypeArguments(params);
visited.remove(node);
return result;
}
}
/**
* A singleton that obtains the annotated type of a type in tree form.
*
* @see AnnotatedTypeFactory#fromTypeTree(Tree)
*/
static class TypeFromTypeTree extends TypeFromTree {
/** The singleton instance. */
public static final TypeFromTypeTree INSTANCE
= new TypeFromTypeTree();
private TypeFromTypeTree() {}
private Map<Tree, AnnotatedTypeMirror> visitedBounds
= new HashMap<Tree, AnnotatedTypeMirror>();
@Override
public AnnotatedTypeMirror visitAnnotatedType(AnnotatedTypeTree node,
AnnotatedTypeFactory f) {
AnnotatedTypeMirror type = visit(node.getUnderlyingType(), f);
if (type == null) // e.g., for receiver type
type = f.toAnnotatedType(f.types.getNoType(TypeKind.NONE));
assert AnnotatedTypeFactory.validAnnotatedType(type);
type.addAnnotations(InternalUtils.annotationsFromTree(node));
return type;
}
@Override
public AnnotatedTypeMirror visitArrayType(ArrayTypeTree node,
AnnotatedTypeFactory f) {
AnnotatedTypeMirror component = visit(node.getType(), f);
AnnotatedTypeMirror result = f.type(node);
assert result instanceof AnnotatedArrayType;
((AnnotatedArrayType)result).setComponentType(component);
return result;
}
@Override
public AnnotatedTypeMirror visitParameterizedType(
ParameterizedTypeTree node, AnnotatedTypeFactory f) {
List<AnnotatedTypeMirror> args = new LinkedList<AnnotatedTypeMirror>();
for (Tree t : node.getTypeArguments())
args.add(visit(t, f));
AnnotatedTypeMirror result = f.type(node); // use creator?
AnnotatedTypeMirror atype = visit(node.getType(), f);
result.addAnnotations(atype.getAnnotations());
// new ArrayList<>() type is AnnotatedExecutableType for some reason
if (result instanceof AnnotatedDeclaredType) {
assert result instanceof AnnotatedDeclaredType : node + " --> " + result;
((AnnotatedDeclaredType)result).setTypeArguments(args);
}
return result;
}
@Override
public AnnotatedTypeMirror visitPrimitiveType(PrimitiveTypeTree node,
AnnotatedTypeFactory f) {
return f.type(node);
}
@Override
public AnnotatedTypeMirror visitTypeParameter(TypeParameterTree node,
AnnotatedTypeFactory f) {
List<AnnotatedTypeMirror> bounds = new LinkedList<AnnotatedTypeMirror>();
for (Tree t : node.getBounds()) {
AnnotatedTypeMirror bound;
if (visitedBounds.containsKey(t) && f == visitedBounds.get(t).typeFactory)
bound = visitedBounds.get(t);
else {
visitedBounds.put(t, f.type(t));
bound = visit(t, f);
visitedBounds.put(t, bound);
}
bounds.add(bound);
}
AnnotatedTypeVariable result = (AnnotatedTypeVariable)f.type(node);
List<? extends AnnotationMirror> annotations = InternalUtils.annotationsFromTree(node);
if (f.canHaveAnnotatedTypeParameters())
result.addAnnotations(annotations);
result.getUpperBound().addAnnotations(annotations);
assert result instanceof AnnotatedTypeVariable;
switch (bounds.size()) {
case 0: break;
case 1:
result.setUpperBound(bounds.get(0));
break;
default:
AnnotatedDeclaredType upperBound = (AnnotatedDeclaredType)result.getUpperBound();
assert TypesUtils.isAnonymousType(upperBound.getUnderlyingType());
@SuppressWarnings({"unchecked", "rawtypes"})
List<AnnotatedDeclaredType> superBounds = (List)bounds;
upperBound.setDirectSuperTypes(superBounds);
}
return result;
}
@Override
public AnnotatedTypeMirror visitWildcard(WildcardTree node,
AnnotatedTypeFactory f) {
AnnotatedTypeMirror bound = visit(node.getBound(), f);
AnnotatedTypeMirror result = f.type(node);
assert result instanceof AnnotatedWildcardType;
if (node.getKind() == Tree.Kind.SUPER_WILDCARD)
((AnnotatedWildcardType)result).setSuperBound(bound);
else if (node.getKind() == Tree.Kind.EXTENDS_WILDCARD)
((AnnotatedWildcardType)result).setExtendsBound(bound);
return result;
}
private AnnotatedTypeMirror forTypeVariable(AnnotatedTypeMirror type,
AnnotatedTypeFactory f) {
if (type.getKind() != TypeKind.TYPEVAR)
throw new IllegalArgumentException();
TypeParameterElement tpe = (TypeParameterElement)
((TypeVariable)type.getUnderlyingType()).asElement();
Element elt = tpe.getGenericElement();
if (elt instanceof TypeElement) {
TypeElement typeElt = (TypeElement)elt;
int idx = typeElt.getTypeParameters().indexOf(tpe);
ClassTree cls = (ClassTree)f.declarationFromElement(typeElt);
AnnotatedTypeMirror result = visit(cls.getTypeParameters().get(idx), f);
return result;
} else if (elt instanceof ExecutableElement) {
ExecutableElement exElt = (ExecutableElement)elt;
int idx = exElt.getTypeParameters().indexOf(tpe);
MethodTree meth = (MethodTree)f.declarationFromElement(exElt);
AnnotatedTypeMirror result = visit(meth.getTypeParameters().get(idx), f);
return result;
} else throw new AssertionError();
}
@Override
public AnnotatedTypeMirror visitIdentifier(IdentifierTree node,
AnnotatedTypeFactory f) {
AnnotatedTypeMirror type = f.type(node);
if (type.getKind() == TypeKind.TYPEVAR)
return forTypeVariable(type, f);
return type;
}
@Override
public AnnotatedTypeMirror visitMemberSelect(MemberSelectTree node,
AnnotatedTypeFactory f) {
AnnotatedTypeMirror type = f.type(node);
if (type.getKind() == TypeKind.TYPEVAR)
return forTypeVariable(type, f);
return type;
}
}
}