package checkers.types;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.*;
import javax.annotation.processing.ProcessingEnvironment;
import javax.lang.model.element.*;
import javax.lang.model.type.*;
import javax.lang.model.util.*;
import checkers.basetype.BaseTypeChecker;
import checkers.javari.quals.*;
import checkers.nullness.quals.Nullable;
import checkers.source.SourceChecker;
import checkers.types.AnnotatedTypeMirror.*;
import checkers.types.visitors.AnnotatedTypeScanner;
import checkers.util.*;
import checkers.util.stub.StubParser;
import checkers.util.stub.StubUtil;
import static checkers.types.TypeFromTree.*;
import com.sun.source.tree.*;
import com.sun.source.util.*;
import com.sun.tools.javac.code.Symbol;
import com.sun.tools.javac.tree.*;
/**
* The methods of this class take an element or AST node, and return the
* annotated type as an {@link AnnotatedTypeMirror}. The methods are:
*
* <ul>
* <li>{@link #getAnnotatedType(ClassTree)}</li>
* <li>{@link #getAnnotatedType(MethodTree)}</li>
* <li>{@link #getAnnotatedType(Tree)}</li>
* <li>{@link #getAnnotatedTypeFromTypeTree(Tree)}</li>
* <li>{@link #getAnnotatedType(TypeElement)}</li>
* <li>{@link #getAnnotatedType(ExecutableElement)}</li>
* <li>{@link #getAnnotatedType(Element)}</li>
* </ul>
*
* This implementation only adds qualifiers explicitly specified by the
* programmer.
*
* Type system checker writers may need to subclass this class, to add implicit
* and default qualifiers according to the type system semantics. Subclasses
* should especially override
* {@link AnnotatedTypeFactory#annotateImplicit(Element, AnnotatedTypeMirror)}
* and {@link #annotateImplicit(Tree, AnnotatedTypeMirror)}.
*
* @checker.framework.manual #writing-a-checker How to write a checker plug-in
*/
public class AnnotatedTypeFactory {
/** The {@link Trees} instance to use for tree node path finding. */
protected final Trees trees;
/** optional! The AST of the source file being operated on */
protected final /*@Nullable*/ CompilationUnitTree root;
/** The processing environment to use for accessing compiler internals. */
protected final ProcessingEnvironment env;
/** The factory to use for creating annotations. */
protected final AnnotationUtils annotations;
/** Utility class for working with {@link Element}s. */
protected final Elements elements;
/** Utility class for working with {@link TypeMirror}s. */
protected final Types types;
/** Utility class for manipulating annotated types. */
protected final AnnotatedTypes atypes;
/** the state of the visitor **/
final protected VisitorState visitorState;
/** Represent the annotation relations **/
protected final @Nullable QualifierHierarchy qualHierarchy;
private final Map<Element, AnnotatedTypeMirror> indexTypes;
private Class<? extends SourceChecker> checkerClass;
private final boolean annotatedTypeParams;
private static int uidCounter = 0;
public final int uid;
/**
* Constructs a factory from the given {@link ProcessingEnvironment}
* instance and syntax tree root. (These parameters are required so that
* the factory may conduct the appropriate annotation-gathering analyses on
* certain tree types.)
*
* Root can be {@code null} if the factory does not operate on trees.
*
* @param checker the {@link SourceChecker} to which this factory belongs
* @param root the root of the syntax tree that this factory produces
* annotated types for
* @throws IllegalArgumentException if either argument is {@code null}
*/
public AnnotatedTypeFactory(SourceChecker checker, @Nullable CompilationUnitTree root) {
this(checker.getProcessingEnvironment(),
(checker instanceof BaseTypeChecker) ? ((BaseTypeChecker)checker).getQualifierHierarchy() : null,
root,
checker == null ? null : checker.getClass());
}
public AnnotatedTypeFactory(ProcessingEnvironment env,
@Nullable QualifierHierarchy qualHierarchy, @Nullable CompilationUnitTree root,
Class<? extends SourceChecker> checkerClass) {
uid = ++uidCounter;
this.env = env;
this.root = root;
this.checkerClass = checkerClass;
this.trees = Trees.instance(env);
this.annotations = AnnotationUtils.getInstance(env);
this.elements = env.getElementUtils();
this.types = env.getTypeUtils();
this.atypes = new AnnotatedTypes(env, this);
this.visitorState = new VisitorState();
this.qualHierarchy = qualHierarchy;
this.supportedQuals = getSupportedQualifiers();
this.indexTypes = buildIndexTypes();
this.annotatedTypeParams = true; // env.getOptions().containsKey("annotatedTypeParams");
}
@Override
public String toString() {
return getClass().getSimpleName() + "#" + uid;
}
/**
* For an annotated type parameter or wildcard (e.g.
* {@code <@Nullable T>}, it returns
* {@code true} if the annotation should target the type parameter itself,
* otherwise the annotation should target the extends clause, i.e.
* the declaration should be treated as {@code <T extends @Nullable Object>}
*/
public boolean canHaveAnnotatedTypeParameters() {
return this.annotatedTypeParams;
}
// **********************************************************************
// Factories for annotated types that account for implicit qualifiers
// **********************************************************************
/** Should cache? disable for better debugging */
private final static boolean SHOULD_CACHE = true;
/** Various Caches **/
/** Size of LRU cache **/
private final static int CACHE_SIZE = 50;
private Map<Tree, AnnotatedTypeMirror> treeCache = createLRUCache(CACHE_SIZE);
private Map<Element, AnnotatedTypeMirror> elementCache = createLRUCache(CACHE_SIZE);
private Map<Element, Tree> elementToTreeCache = createLRUCache(CACHE_SIZE);
/**
* Determines the annotated type of an element using
* {@link #fromElement(Element)}.
*
* @param elt the element
* @return the annotated type of {@code elt}
* @throws IllegalArgumentException if {@code elt} is null
*
* @see #fromElement(Element)
*/
public AnnotatedTypeMirror getAnnotatedType(Element elt) {
if (elt == null)
throw new IllegalArgumentException("null element");
AnnotatedTypeMirror type = fromElement(elt);
annotateImplicit(elt, type);
return type;
}
/**
* Determines the annotated type of an AST node.
*
* <p>
*
* The type is determined as follows:
* <ul>
* <li>if {@code tree} is a class declaration, determine its type via
* {@link #fromClass}</li>
* <li>if {@code tree} is a method or variable declaration, determine its
* type via {@link #fromMember(Tree)}</li>
* <li>if {@code tree} is an {@link ExpressionTree}, determine its type
* via {@link #fromExpression(ExpressionTree)}</li>
* <li>otherwise, throw an {@link UnsupportedOperationException}</li>
* </ul>
*
* @param tree the AST node
* @return the annotated type of {@code tree}
* @throws UnsupportedOperationException if an annotated type cannot be
* obtained from {@code tree}
* @throws IllegalArgumentException if {@code tree} is null
*
* @see #fromClass(ClassTree)
* @see #fromMember(Tree)
* @see #fromExpression(ExpressionTree)
*/
// I wish I could make this method protected
public AnnotatedTypeMirror getAnnotatedType(Tree tree) {
if (tree == null)
throw new IllegalArgumentException("null tree");
if (treeCache.containsKey(tree))
return atypes.deepCopy(treeCache.get(tree));
AnnotatedTypeMirror type;
switch (tree.getKind()) {
case CLASS:
type = fromClass((ClassTree)tree); break;
case METHOD:
case VARIABLE:
type = fromMember(tree); break;
default:
if (tree instanceof ExpressionTree)
type = fromExpression((ExpressionTree)tree);
else throw new UnsupportedOperationException(
"query of annotated type for tree " + tree.getKind());
}
annotateImplicit(TreeUtils.skipParens(tree), type);
switch (tree.getKind()) {
case CLASS:
case METHOD:
// case VARIABLE:
if (SHOULD_CACHE)
treeCache.put(tree, atypes.deepCopy(type));
}
return type;
}
/**
* Determines the annotated type from a type in tree form.
*
* @param tree the type tree
* @return the annotated type of the type in the AST
*/
public AnnotatedTypeMirror getAnnotatedTypeFromTypeTree(Tree tree) {
if (tree == null)
throw new IllegalArgumentException("null tree");
AnnotatedTypeMirror type = fromTypeTree(tree);
annotateImplicit(tree, type);
return type;
}
// **********************************************************************
// Factories for annotated types that do not account for implicit qualifiers
// They only include qualifiers explicitly inserted by the user
// **********************************************************************
/**
* Determines the annotated type of an element.
*
* @param elt the element
* @return the annotated type of the element
*/
public AnnotatedTypeMirror fromElement(Element elt) {
if (elementCache.containsKey(elt))
return atypes.deepCopy(elementCache.get(elt));
if (elt.getKind() == ElementKind.PACKAGE)
return toAnnotatedType(elt.asType());
AnnotatedTypeMirror type;
Tree decl = declarationFromElement(elt);
if (decl == null && indexTypes != null && indexTypes.containsKey(elt)) {
type = indexTypes.get(elt);
} else if (decl == null && (indexTypes == null || !indexTypes.containsKey(elt))) {
type = toAnnotatedType(elt.asType());
type.setElement(elt);
TypeFromElement.annotate(type, elt);
if (elt instanceof ExecutableElement
|| elt instanceof VariableElement) {
annotateInheritedFromClass(type);
}
} else if (decl instanceof ClassTree) {
type = fromClass((ClassTree)decl);
} else if (decl instanceof VariableTree) {
type = fromMember(decl);
} else if (decl instanceof MethodTree) {
type = fromMember(decl);
} else
throw new AssertionError("Cannot be here " + decl.getKind() +
" " + elt);
if (SHOULD_CACHE && indexTypes != null)
elementCache.put(elt, atypes.deepCopy(type));
return type;
}
/**
* Determines the annotated type of a class from its declaration.
*
* @param tree the class declaration
* @return the annotated type of the class being declared
*/
public AnnotatedDeclaredType fromClass(ClassTree tree) {
AnnotatedDeclaredType result = (AnnotatedDeclaredType)
fromTreeWithVisitor(TypeFromClass.INSTANCE, tree);
return result;
}
protected Map<Tree, AnnotatedTypeMirror> fromTreeCache = createLRUCache(CACHE_SIZE);
/**
* Determines the annotated type of a variable or method declaration.
*
* @param tree the variable or method declaration
* @return the annotated type of the variable or method being declared
* @throws IllegalArgumentException if {@code tree} is not a method or
* variable declaration
*/
public AnnotatedTypeMirror fromMember(Tree tree) {
if (!(tree instanceof MethodTree || tree instanceof VariableTree))
throw new IllegalArgumentException("not a method or variable declaration");
if (fromTreeCache.containsKey(tree))
return atypes.deepCopy(fromTreeCache.get(tree));
AnnotatedTypeMirror result = fromTreeWithVisitor(
TypeFromMember.INSTANCE, tree);
annotateInheritedFromClass(result);
if (SHOULD_CACHE)
fromTreeCache.put(tree, atypes.deepCopy(result));
return result;
}
/**
* Determines the annotated type of an expression.
*
* @param tree an expression
* @return the annotated type of the expression
*/
public AnnotatedTypeMirror fromExpression(ExpressionTree tree) {
if (fromTreeCache.containsKey(tree))
return atypes.deepCopy(fromTreeCache.get(tree));
AnnotatedTypeMirror result = fromTreeWithVisitor(
TypeFromExpression.INSTANCE, tree);
annotateInheritedFromClass(result);
if (SHOULD_CACHE)
fromTreeCache.put(tree, atypes.deepCopy(result));
return result;
}
/**
* Determines the annotated type from a type in tree form. This method
* does not add implicit annotations
*
* @param tree the type tree
* @return the annotated type of the type in the AST
*/
public AnnotatedTypeMirror fromTypeTree(Tree tree) {
if (fromTreeCache.containsKey(tree))
return atypes.deepCopy(fromTreeCache.get(tree));
AnnotatedTypeMirror result = fromTreeWithVisitor(
TypeFromTypeTree.INSTANCE, tree);
// treat Raw as generic!
// TODO: This doesn't handle recursive type parameter
// e.g. class Pair<Y extends List<Y>> { ... }
if (result.getKind() == TypeKind.DECLARED) {
AnnotatedDeclaredType dt = (AnnotatedDeclaredType)result;
if (dt.getTypeArguments().isEmpty()
&& !((TypeElement)dt.getUnderlyingType().asElement()).getTypeParameters().isEmpty()) {
List<AnnotatedTypeMirror> typeArgs = new ArrayList<AnnotatedTypeMirror>();
AnnotatedDeclaredType declaration = fromElement((TypeElement)dt.getUnderlyingType().asElement());
for (AnnotatedTypeMirror typeParam : declaration.getTypeArguments()) {
AnnotatedTypeVariable typeParamVar = (AnnotatedTypeVariable)typeParam;
AnnotatedTypeMirror upperBound = typeParamVar.getUpperBound();
while (upperBound.getKind() == TypeKind.TYPEVAR)
upperBound = ((AnnotatedTypeVariable)upperBound).getUpperBound();
typeArgs.add(upperBound.getCopy(false));
}
dt.setTypeArguments(typeArgs);
}
}
annotateInheritedFromClass(result);
if (SHOULD_CACHE)
fromTreeCache.put(tree, atypes.deepCopy(result));
return result;
}
/**
* A convenience method that takes any visitor for converting trees to
* annotated types, and applies the visitor to the tree, add implicit
* annotations, etc.
*
* @param converter the tree-to-type-converting visitor
* @param tree the tree to convert
* @param type the converted annotated type
*/
private AnnotatedTypeMirror fromTreeWithVisitor(TypeFromTree converter, Tree tree) {
if (tree == null)
throw new IllegalArgumentException("null tree");
if (converter == null)
throw new IllegalArgumentException("null visitor");
AnnotatedTypeMirror result = converter.visit(tree, this);
checkRep(result);
return result;
}
// **********************************************************************
// Customization methods meant to be overriden by subclasses to include
// implicit annotations
// **********************************************************************
/**
* Adds implicit annotations to a type obtained from a {@link Tree}. By
* default, this method does nothing. Subclasses should use this method to
* implement implicit annotations specific to their type systems.
*
* @param tree an AST node
* @param type the type obtained from {@code tree}
*/
protected void annotateImplicit(Tree tree, @Mutable AnnotatedTypeMirror type) {
// Pass.
}
/**
* Adds implicit annotations to a type obtained from a {@link Element}. By
* default, this method does nothing. Subclasses should use this method to
* implement implicit annotations specific to their type systems.
*
* @param elt an element
* @param type the type obtained from {@code elt}
*/
protected void annotateImplicit(Element elt, @Mutable AnnotatedTypeMirror type) {
// Pass.
}
/**
* A callback method for the AnnotatedTypeFactory subtypes to customize
* directSuperTypes(). Overriding methods should merely change the
* annotations on the supertypes, without adding or removing new types
*
* The default provided implementation adds {@code type} annotations to
* {@code supertypes}. This allows the {@code type} and its supertypes
* to have the qualifiers, e.g. the supertypes of an {@code Immutable}
* type are also {@code Immutable}.
*
* @param type the type whose supertypes are desired
* @param supertypes
* the supertypes as specified by the base AnnotatedTypeFactory
*
*/
protected void postDirectSuperTypes(AnnotatedTypeMirror type,
List<? extends AnnotatedTypeMirror> supertypes) {
Set<AnnotationMirror> annotations = type.getAnnotations();
for (AnnotatedTypeMirror supertype : supertypes) {
if (!annotations.equals(supertype.getAnnotations())) {
supertype.clearAnnotations();
supertype.addAnnotations(annotations);
}
}
}
/**
* A callback method for the AnnotatedTypeFactory subtypes to customize
* AnnotatedTypes.asMemberOf(). Overriding methods should merely change
* the annotations on the subtypes, without changing the types.
*
* @param type the annotated type of the element
* @param owner the annotated type of the receiver of the accessing tree
* @param element the element of the field or method
*/
protected void postAsMemberOf(AnnotatedTypeMirror type,
AnnotatedTypeMirror owner, Element element) {
annotateImplicit(element, type);
}
/**
* Adds annotations to the type based on the annotations from its class
* type if and only if no annotations are already present on the type.
*
* @param type the type for which class annotations will be inherited if
* there are no annotations already present
*/
protected void annotateInheritedFromClass(@Mutable AnnotatedTypeMirror type) {
InheritedFromClassAnnotator.INSTANCE.visit(type, this);
}
/**
* A singleton utility class for pulling annotations down from a class
* type.
*
* @see #annotateInheritedFromClass
*/
protected static class InheritedFromClassAnnotator
extends AnnotatedTypeScanner<Void, AnnotatedTypeFactory> {
/** The singleton instance. */
public static final InheritedFromClassAnnotator INSTANCE
= new InheritedFromClassAnnotator();
private InheritedFromClassAnnotator() {}
@Override
public Void visitExecutable(AnnotatedExecutableType type, AnnotatedTypeFactory p) {
// When visiting an executable type, skip the receiver so we
// never inherit class annotations there.
scan(type.getReturnType(), p);
scanAndReduce(type.getParameterTypes(), p, null);
scanAndReduce(type.getThrownTypes(), p, null);
scanAndReduce(type.getTypeVariables(), p, null);
return null;
}
@Override
public Void visitDeclared(AnnotatedDeclaredType type, AnnotatedTypeFactory p) {
Element classElt = type.getUnderlyingType().asElement();
// Only add annotations from the class declaration if there
// are no annotations already on the type.
if (classElt != null && !type.isAnnotated()) {
AnnotatedTypeMirror classType = p.fromElement(classElt);
assert classType != null;
for (AnnotationMirror anno : classType.getAnnotations()) {
if (AnnotationUtils.hasInheritiedMeta(anno)) {
type.addAnnotation(anno);
}
}
}
return super.visitDeclared(type, p);
}
}
// **********************************************************************
// Utilities method for getting specific types from trees or elements
// **********************************************************************
protected AnnotatedDeclaredType getImplicitReceiverType(Tree tree) {
assert (tree.getKind() == Tree.Kind.IDENTIFIER
|| tree.getKind() == Tree.Kind.MEMBER_SELECT
|| tree.getKind() == Tree.Kind.METHOD_INVOCATION
|| tree.getKind() == Tree.Kind.NEW_CLASS);
Element element = InternalUtils.symbol(tree);
assert element != null;
if (ElementUtils.isStatic(element)
|| element.getKind() == ElementKind.PACKAGE)
return null;
if (isMostEnclosingThisDeref(tree))
return getSelfType(tree);
TypeElement typeElt = ElementUtils.enclosingClass(element);
assert typeElt != null;
return getEnclosingType(typeElt, tree);
}
protected final boolean isMostEnclosingThisDeref(Tree tree) {
// check local variables or expressions
Element element = InternalUtils.symbol(tree);
if (element == null)
return true;
if (ElementUtils.isStatic(element))
return false;
if (element.getKind() == ElementKind.LOCAL_VARIABLE
|| element.getKind() == ElementKind.PARAMETER)
return !ElementUtils.isStatic(element.getEnclosingElement());
if (isThisDereference(tree))
return true;
TypeElement typeElt = ElementUtils.enclosingClass(element);
ClassTree enclosingClass = visitorState.getClassTree();
if (enclosingClass == null)
enclosingClass = TreeUtils.enclosingClass(getPath(tree));
if (isSubtype(TreeUtils.elementFromDeclaration(enclosingClass), typeElt))
return true;
// ran out of options
return false;
}
private final boolean isThisDereference(Tree tree) {
if (tree.getKind() != Tree.Kind.MEMBER_SELECT)
return false;
MemberSelectTree memSelTree = (MemberSelectTree)tree;
return (memSelTree.getExpression().getKind() == Tree.Kind.IDENTIFIER
&& ((IdentifierTree)memSelTree.getExpression()).getName().contentEquals("this"));
}
/**
* Returns the type of {@code this} in the current location, which can
* be used if {@code this} has a special semantics (e.g. {@code this}
* is non-null)
*/
public AnnotatedDeclaredType getSelfType(Tree tree) {
AnnotatedDeclaredType type = getCurrentClassType(tree);
AnnotatedDeclaredType methodReceiver = getCurrentMethodReceiver(tree);
if (methodReceiver != null) {
type.clearAnnotations();
type.addAnnotations(methodReceiver.getAnnotations());
}
return type;
}
public AnnotatedDeclaredType getEnclosingType(TypeElement element, Tree tree) {
Element enclosingElt = getMostInnerClassOrMethod(tree);
while (enclosingElt != null) {
if (enclosingElt instanceof ExecutableElement) {
ExecutableElement method = (ExecutableElement)enclosingElt;
if (method.asType() != null // XXX: hack due to a compiler bug
&& isSubtype((TypeElement)method.getEnclosingElement(), element))
if (ElementUtils.isStatic(method))
return null;
else
return getAnnotatedType(method).getReceiverType();
} else
if (enclosingElt instanceof TypeElement) {
if (isSubtype((TypeElement)enclosingElt, element))
return getAnnotatedType(element);
}
enclosingElt = enclosingElt.getEnclosingElement();
}
return null;
}
private boolean isSubtype(TypeElement a1, TypeElement a2) {
return (a1.equals(a2)
|| types.isSubtype(types.erasure(a1.asType()),
types.erasure(a2.asType())));
}
/**
* Returns the receiver type of the expression tree, or null if it does not exist.
*
* The only trees that could potentially have a receiver are:
* <ul>
* <li> Array Access
* <li> Identifiers (whose receivers are usually self type)
* <li> Method Invocation Trees
* <li> Member Select Trees
* </ul>
*
* @param expression
* @return the type of the receiver of this expression
*/
public final AnnotatedTypeMirror getReceiver(ExpressionTree expression) {
if (!(expression.getKind() == Tree.Kind.METHOD_INVOCATION
|| expression.getKind() == Tree.Kind.MEMBER_SELECT
|| expression.getKind() == Tree.Kind.IDENTIFIER
|| expression.getKind() == Tree.Kind.ARRAY_ACCESS))
// No receiver type for those
return null;
if (expression.getKind() == Tree.Kind.IDENTIFIER
&& "this".equals(expression.toString()))
return null;
ExpressionTree receiver = TreeUtils.skipParens(expression);
if (receiver.getKind() == Tree.Kind.ARRAY_ACCESS)
return getAnnotatedType(((ArrayAccessTree)receiver).getExpression());
Element elem = InternalUtils.symbol(expression);
if (elem == null || ElementUtils.isStatic(elem))
return null;
// Avoid int.class
if (expression.getKind() == Tree.Kind.MEMBER_SELECT &&
((MemberSelectTree)expression).getExpression() instanceof PrimitiveTypeTree)
return null;
if (TreeUtils.isSelfAccess(expression)) {
return getImplicitReceiverType(expression);
}
//
// Trying to handle receiver calls to trees of the form
// ((m).getArray())
// returns the type of 'm' in this case
if (receiver.getKind() == Tree.Kind.METHOD_INVOCATION)
receiver = ((MethodInvocationTree)receiver).getMethodSelect();
receiver = TreeUtils.skipParens(receiver);
assert receiver.getKind() == Tree.Kind.MEMBER_SELECT;
if (receiver.getKind() == Tree.Kind.MEMBER_SELECT)
receiver = ((MemberSelectTree)receiver).getExpression();
return getAnnotatedType(receiver);
}
/**
* Determines the type of the invoked method based on the passed method
* invocation tree.
*
* The returned method type has all type variables resolved, whether based
* on receiver type, passed type parameters if any, and method invocation
* parameter.
*
* Subclasses may override this method to customize inference of types
* or qualifiers based on method invocation parameters.
*
* As an implementation detail, this method depends on
* {@link AnnotatedTypes#asMemberOf(AnnotatedTypeMirror, Element)}, and
* customization based on receiver type should be in accordance to its
* specification.
*
* @param tree the method invocation tree
* @return the method type being invoked with tree
*/
public AnnotatedExecutableType methodFromUse(MethodInvocationTree tree) {
ExecutableElement methodElt = TreeUtils.elementFromUse(tree);
AnnotatedTypeMirror type = getReceiver(tree);
AnnotatedExecutableType methodType =
atypes.asMemberOf(type, methodElt);
Map<AnnotatedTypeVariable, AnnotatedTypeMirror> typeVarMapping =
atypes.findTypeArguments(tree);
if (!typeVarMapping.isEmpty()) {
methodType = methodType.substitute(typeVarMapping);
}
return methodType;
}
/**
* Determines the {@link AnnotatedExecutableType} of a constructor
* invocation. Note that this is different than calling
* {@link #getAnnotatedType(Tree)} or
* {@link #fromExpression(ExpressionTree)} on the constructor invocation;
* those determine the type of the <i>result</i> of invoking the
* constructor, which is probably an {@link AnnotatedDeclaredType}.
*
* @param tree a constructor invocation
* @return the annotated type of the invoked constructor (as an executable
* type)
*/
public AnnotatedExecutableType constructorFromUse(NewClassTree tree) {
ExecutableElement ctor = InternalUtils.constructor(tree);
AnnotatedTypeMirror type = fromNewClass(tree);
annotateImplicit(tree.getIdentifier(), type);
AnnotatedExecutableType con = atypes.asMemberOf(type, ctor);
if (tree.getArguments().size() == con.getParameterTypes().size() + 1
&& isSyntheticArgument(tree.getArguments().get(0))) {
// happens for anonymous constructors of inner classes
List<AnnotatedTypeMirror> actualParams = new ArrayList<AnnotatedTypeMirror>();
actualParams.add(getAnnotatedType(tree.getArguments().get(0)));
actualParams.addAll(con.getParameterTypes());
con.setParameterTypes(actualParams);
}
return con;
}
private boolean isSyntheticArgument(Tree tree) {
return tree.toString().contains("<*nullchk*>");
}
public AnnotatedDeclaredType fromNewClass(NewClassTree tree) {
if (!TreeUtils.isDiamondTree(tree))
return (AnnotatedDeclaredType)fromTypeTree(tree.getIdentifier());
AnnotatedDeclaredType type = (AnnotatedDeclaredType)toAnnotatedType(((JCTree)tree).type);
if (tree.getIdentifier().getKind() == Tree.Kind.ANNOTATED_TYPE)
type.addAnnotations(InternalUtils.annotationsFromTree((AnnotatedTypeTree)tree));
return type;
}
/**
* returns the annotated boxed type of the given primitive type.
* The returned type would only have the annotations on the given type.
*
* Subclasses may override this method safely to override this behavour.
*
* @param type the primitivate type
* @return the boxed declared type of the passed primitive type
*/
public AnnotatedDeclaredType getBoxedType(AnnotatedPrimitiveType type) {
TypeElement typeElt = types.boxedClass(type.getUnderlyingType());
AnnotatedDeclaredType dt = fromElement(typeElt);
dt.addAnnotations(type.getAnnotations());
return dt;
}
/**
* returns the annotated primitive type of the given declared type
* if it is a boxed declared type. Otherwise, it throws
* <i>IllegalArgumentException</i> exception.
*
* The returned type would have the annotations on the given type and
* nothing else.
*
* @param type the declared type
* @return the unboxed primitive type
* @throws IllegalArgmentExceptionif the type given has no unbox conversion
*/
public AnnotatedPrimitiveType getUnboxedType(AnnotatedDeclaredType type)
throws IllegalArgumentException {
PrimitiveType primitiveType =
env.getTypeUtils().unboxedType(type.getUnderlyingType());
AnnotatedPrimitiveType pt = (AnnotatedPrimitiveType)
AnnotatedTypeMirror.createType(primitiveType, env, this);
pt.addAnnotations(type.getAnnotations());
return pt;
}
/**
* Returns the VisitorState instance used by the factory to infer types
*/
public VisitorState getVisitorState() {
return this.visitorState;
}
// **********************************************************************
// random methods wrapping #getAnnotatedType(Tree) and #fromElement(Tree)
// with appropriate casts to reduce casts on the client side
// **********************************************************************
/**
* @see #getAnnotatedType(Tree)
*/
public final AnnotatedDeclaredType getAnnotatedType(ClassTree tree) {
return (AnnotatedDeclaredType)getAnnotatedType((Tree)tree);
}
/**
* @see #getAnnotatedType(Tree)
*/
public final AnnotatedDeclaredType getAnnotatedType(NewClassTree tree) {
return (AnnotatedDeclaredType)getAnnotatedType((Tree)tree);
}
/**
* @see #getAnnotatedType(Tree)
*/
public final AnnotatedArrayType getAnnotatedType(NewArrayTree tree) {
return (AnnotatedArrayType)getAnnotatedType((Tree)tree);
}
/**
* @see #getAnnotatedType(Tree)
*/
public final AnnotatedExecutableType getAnnotatedType(MethodTree tree) {
return (AnnotatedExecutableType)getAnnotatedType((Tree)tree);
}
public final AnnotatedTypeMirror getAnnotatedType(VariableTree tree) {
return getAnnotatedType((Tree)tree);
}
public final AnnotatedTypeMirror getAnnotatedType(ExpressionTree tree) {
return getAnnotatedType((Tree)tree);
}
/**
* @see #getAnnotatedType(Element)
*/
public final AnnotatedDeclaredType getAnnotatedType(TypeElement elt) {
return (AnnotatedDeclaredType)getAnnotatedType((Element)elt);
}
/**
* @see #getAnnotatedType(Element)
*/
public final AnnotatedExecutableType getAnnotatedType(ExecutableElement elt) {
return (AnnotatedExecutableType)getAnnotatedType((Element)elt);
}
/**
* @see #getAnnotatedType(Element)
*/
public final AnnotatedDeclaredType fromElement(TypeElement elt) {
return (AnnotatedDeclaredType)fromElement((Element)elt);
}
/**
* @see #getAnnotatedType(Element)
*/
public final AnnotatedExecutableType fromElement(ExecutableElement elt) {
return (AnnotatedExecutableType)fromElement((Element)elt);
}
// **********************************************************************
// Helper methods for this classes
// **********************************************************************
/** Memoization for isRecognizedAnnotation(). set by the constructor */
private final Set<Name> supportedQuals;
/**
* Creates the set of the names of the recognized type qualifiers in the
* current checker.
*
* @return a set of the name of the recognized qualifier.
*/
private Set<Name> getSupportedQualifiers() {
if (qualHierarchy != null)
return qualHierarchy.getTypeQualifiers();
return Collections.emptySet();
}
/**
* Determines whether the given annotation is a part of the type system
* under which this type factory operates.
*
* @param a any annotation
* @return true if that annotation is part of the type system under which
* this type factory operates, false otherwise
*/
/*package-scope*/ boolean isSupportedQualifier(AnnotationMirror a) {
if (supportedQuals.isEmpty()) {
// Only include with retention
TypeElement elt = (TypeElement)a.getAnnotationType().asElement();
Retention retention = elt.getAnnotation(Retention.class);
return (retention == null) || retention.value() != RetentionPolicy.SOURCE;
}
Name name = AnnotationUtils.annotationName(a);
return supportedQuals.contains(name);
}
/**
* Returns the canonical annotation for the passed annotation if it is
* an alias of a canonical one in the framework. If it is not an alias,
* the method returns null.
*
*
* Returns an aliased type of the current one
*/
protected AnnotationMirror aliasedAnnotation(AnnotationMirror a) {
return null;
}
/**
* A convenience method that converts a {@link TypeMirror} to an {@link
* AnnotatedTypeMirror} using {@link AnnotatedTypeMirror#create}.
*
* @param t the {@link TypeMirror}
* @return an {@link AnnotatedTypeMirror} that has {@code t} as its
* underlying type
*/
/*package-scope*/ final AnnotatedTypeMirror toAnnotatedType(TypeMirror t) {
return AnnotatedTypeMirror.createType(t, env, this);
}
/**
* Determines an empty annotated type of the given tree. In other words,
* finds the {@link TypeMirror} for the tree and converts that into an
* {@link AnnotatedTypeMirror}, but does not add any annotations to the
* result.
*
* @param node
* @return the type of {@code node}, without any annotations
*/
/*package-scope*/ AnnotatedTypeMirror type(Tree node) {
// Attempt to obtain the type via JCTree.
if (((JCTree)node).type != null) {
AnnotatedTypeMirror result = toAnnotatedType(((JCTree)node).type);
return result;
}
// Attempt to obtain the type via TreePath (slower).
TreePath path = this.getPath(node);
assert path != null : "no path or type in tree";
TypeMirror t = trees.getTypeMirror(path);
assert validType(t) : node + " --> " + t;
return toAnnotatedType(t);
}
/**
* Returns the type qualifiers that are least upper bound for c1 and c2
* qualifiers.
*
* In most cases, this is simply the intersection of the collections.
* However, if a type system specifies more than one type qualifier,
* this needs to return the least restrictive type qualifiers.
*
* Examples:
* For NonNull, unify('Nullable', 'NonNull') ==> Nullable
* For IGJ, unify('Immutable', 'Mutable') ==> ReadOnly
*
* Delegates the call to
* {@link QualifierHierarchy#leastUpperBound(Collection, Collection)}.
*
* @param c1 type qualifiers for the first type
* @param c2 tyep qualifiers for the second type
* @return the least restrictive qualifiers for both types
*/
protected Collection<AnnotationMirror> unify(Collection<AnnotationMirror> c1,
Collection<AnnotationMirror> c2) {
if (qualHierarchy == null) {
// return the intersection
Set<AnnotationMirror> intersection = AnnotationUtils.createAnnotationSet();
intersection.addAll(c1);
intersection.retainAll(c2);
return intersection;
}
return qualHierarchy.leastUpperBound(c1, c2);
}
public QualifierHierarchy getQualifierHierarchy() {
return this.qualHierarchy;
}
/**
* Gets the declaration tree for the element, if the source is available.
*
* @param elt an element
* @return the tree declaration of the element if found
*/
protected final Tree declarationFromElement(Element elt) {
// if root is null, we cannot find any declaration
if (root == null)
return null;
if (elementToTreeCache.containsKey(elt))
return elementToTreeCache.get(elt);
// TODO: handle type parameter declarations?
Tree fromElt;
// Prevent calling declarationFor on elements we know we don't have
// the tree for
switch (elt.getKind()) {
case CLASS:
case ENUM:
case INTERFACE:
case ANNOTATION_TYPE:
case FIELD:
case ENUM_CONSTANT:
case METHOD:
case CONSTRUCTOR:
fromElt = trees.getTree(elt);
break;
default:
fromElt = TreeInfo.declarationFor((Symbol)elt, (JCTree)root);
break;
}
if (SHOULD_CACHE)
elementToTreeCache.put(elt, fromElt);
return fromElt;
}
/**
* Returns the current class type being visited by the visitor. The method
* uses the parameter only if the most enclosing class cannot be found
* directly.
*
* @return type of the most enclosing class being visited
*/
// This method is used to wrap access to visitorState
protected final AnnotatedDeclaredType getCurrentClassType(Tree tree) {
if (visitorState.getClassType() != null) {
return visitorState.getClassType();
}
ClassTree enclosingClass = TreeUtils.enclosingClass(getPath(tree));
return getAnnotatedType(enclosingClass);
}
/**
* Returns the receiver type of the current method being visited, and
* returns null if the visited tree is not within a method.
*
* The method uses the parameter only if the most enclosing method cannot
* be found directly.
*
* @return receiver type of the most enclosing method being visited.
*/
protected final AnnotatedDeclaredType getCurrentMethodReceiver(Tree tree) {
if (visitorState.getClassType() != null) {
return visitorState.getMethodReceiver();
}
MethodTree enclosingMethod = TreeUtils.enclosingMethod(getPath(tree));
if (enclosingMethod == null)
return null;
AnnotatedExecutableType method = getAnnotatedType(enclosingMethod);
return method.getReceiverType();
}
protected final boolean isWithinConstructor(Tree tree) {
if (visitorState.getClassType() != null)
return visitorState.getMethodTree() != null
&& TreeUtils.isConstructor(visitorState.getMethodTree());
MethodTree enclosingMethod = TreeUtils.enclosingMethod(getPath(tree));
return enclosingMethod != null && TreeUtils.isConstructor(enclosingMethod);
}
private final Element getMostInnerClassOrMethod(Tree tree) {
if (visitorState.getMethodTree() != null)
return TreeUtils.elementFromDeclaration(visitorState.getMethodTree());
if (visitorState.getClassTree() != null)
return TreeUtils.elementFromDeclaration(visitorState.getClassTree());
TreePath path = getPath(tree);
for (Tree pathTree : path) {
if (pathTree instanceof MethodTree)
return TreeUtils.elementFromDeclaration((MethodTree)pathTree);
else if (pathTree instanceof ClassTree)
return TreeUtils.elementFromDeclaration((ClassTree)pathTree);
}
throw new AssertionError("Cannot be here!");
}
/**
* Gets the path for the given {@link Tree} under the current root by
* checking from the visitor's current path, and only using
* {@link Trees#getPath(CompilationUnitTree, Tree)} (which is much slower)
* only if {@code node} is not found on the current path.
*
* @param node the {@link Tree} to get the path for
* @return the path for {@code node} under the current root
*/
public final TreePath getPath(Tree node) {
assert root != null : "root needs to be set when used on trees";
if (node == null) return null;
TreePath currentPath = visitorState.getPath();
if (currentPath == null)
return TreePath.getPath(root, node);
// This method uses multiple heuristics to avoid calling
// TreePath.getPath()
// If the current path you are visiting is for this node we are done
if (currentPath.getLeaf() == node) {
return currentPath;
}
//
// When running on daikon, we noticed that a lot of calls happened
// within a small subtree containing the node we are currently visiting
// When testing on daikon, two steps resulted in the best performance
if (currentPath.getParentPath() != null)
currentPath = currentPath.getParentPath();
if (currentPath.getLeaf() == node) {
return currentPath;
}
if (currentPath.getParentPath() != null)
currentPath = currentPath.getParentPath();
if (currentPath.getLeaf() == node) {
return currentPath;
}
final TreePath pathWithinSubtree = TreePath.getPath(currentPath, node);
if (pathWithinSubtree != null) {
return pathWithinSubtree;
}
// climb the current path till we see that
// Works when getPath called on the enclosing method, enclosing
// class
TreePath current = currentPath;
while (current != null) {
if (current.getLeaf() == node)
return current;
current = current.getParentPath();
}
// OK, we give up. do a full scan
return TreePath.getPath(root, node);
}
/**
* Ensures that a type has been constructed properly.
*
* @param type the type to check
*/
private void checkRep(AnnotatedTypeMirror type) {
new AnnotatedTypeScanner<Void, Void>() {
@Override
public Void visitDeclared(AnnotatedDeclaredType type, Void p) {
//assert type.getElement() != null;
return super.visitDeclared(type, p);
}
@Override
public Void visitExecutable(AnnotatedExecutableType type, Void p) {
assert type.getElement() != null;
return super.visitExecutable(type, p);
}
}.visit(type);
}
/**
* Assert that the type is a type of valid type mirror, i.e. not an ERROR
* or OTHER type.
*
* @param type an annotated type
* @return true if the type is a valid annotated type, false otherwise
*/
static final boolean validAnnotatedType(AnnotatedTypeMirror type) {
if (type == null) return false;
if (type.getUnderlyingType() == null)
return true; // e.g., for receiver types
return validType(type.getUnderlyingType());
}
/**
* Used for asserting that a type is valid for converting to an annotated
* type.
*
* @param type
* @return true if {@code type} can be converted to an annotated type, false
* otherwise
*/
private static final boolean validType(TypeMirror type) {
if (type == null) return false;
switch (type.getKind()) {
case ERROR:
case OTHER:
case PACKAGE:
return false;
}
return true;
}
/**
* A Utility method for creating LRU cache
* @param size size of the cache
* @return a new cache with the provided size
*/
protected static <K, V> Map<K, V> createLRUCache(final int size) {
return new LinkedHashMap<K, V>() {
private static final long serialVersionUID = 5261489276168775084L;
@Override
protected boolean removeEldestEntry(Map.Entry<K, V> entry) {
return size() > size;
}
};
}
private Map<Element, AnnotatedTypeMirror> buildIndexTypes() {
Map<Element, AnnotatedTypeMirror> result =
new HashMap<Element, AnnotatedTypeMirror>();
InputStream in = null;
if (checkerClass != null)
in = checkerClass.getResourceAsStream("jdk.astub");
if (in != null) {
StubParser stubParser = new StubParser(in, this, env);
stubParser.parse(result);
}
String stubFiles = env.getOptions().get("stubs");
if (stubFiles == null)
stubFiles = System.getProperty("stubs");
if (stubFiles == null)
stubFiles = System.getenv("stubs");
if (stubFiles == null)
return result;
String[] stubArray = stubFiles.split(File.pathSeparator);
for (String stubPath : stubArray) {
try {
// Handle case when running in jtreg
String base = System.getProperty("test.src");
if (base != null)
stubPath = base + "/" + stubPath;
List<File> stubs = StubUtil.allStubFiles(stubPath);
for (File f : stubs) {
InputStream stubStream = new FileInputStream(f);
StubParser stubParser = new StubParser(stubStream, this, env);
stubParser.parse(result);
}
} catch (FileNotFoundException e) {
System.err.println("Couldn't find stub file named: " + stubPath);
}
}
return result;
}
}