framework.src.org.checkerframework.framework.type.AnnotatedTypeFactory Maven / Gradle / Ivy
Show all versions of checker Show documentation
package org.checkerframework.framework.type;
/*>>>
import org.checkerframework.checker.interning.qual.*;
import org.checkerframework.checker.nullness.qual.Nullable;
*/
// The imports from com.sun, but they are all
// @jdk.Exported and therefore somewhat safe to use.
// Try to avoid using [email protected] classes.
import org.checkerframework.common.basetype.BaseTypeChecker;
import org.checkerframework.common.reflection.DefaultReflectionResolver;
import org.checkerframework.common.reflection.MethodValAnnotatedTypeFactory;
import org.checkerframework.common.reflection.MethodValChecker;
import org.checkerframework.common.reflection.ReflectionResolver;
import org.checkerframework.common.wholeprograminference.WholeProgramInference;
import org.checkerframework.common.wholeprograminference.WholeProgramInferenceScenes;
import org.checkerframework.dataflow.qual.SideEffectFree;
import org.checkerframework.framework.qual.FromByteCode;
import org.checkerframework.framework.qual.FromStubFile;
import org.checkerframework.framework.qual.InheritedAnnotation;
import org.checkerframework.framework.qual.PolyAll;
import org.checkerframework.framework.qual.PolymorphicQualifier;
import org.checkerframework.framework.qual.StubFiles;
import org.checkerframework.framework.qual.SubtypeOf;
import org.checkerframework.framework.source.SourceChecker;
import org.checkerframework.framework.stub.StubParser;
import org.checkerframework.framework.stub.StubResource;
import org.checkerframework.framework.stub.StubUtil;
import org.checkerframework.framework.type.AnnotatedTypeMirror.AnnotatedArrayType;
import org.checkerframework.framework.type.AnnotatedTypeMirror.AnnotatedDeclaredType;
import org.checkerframework.framework.type.AnnotatedTypeMirror.AnnotatedExecutableType;
import org.checkerframework.framework.type.AnnotatedTypeMirror.AnnotatedIntersectionType;
import org.checkerframework.framework.type.AnnotatedTypeMirror.AnnotatedNullType;
import org.checkerframework.framework.type.AnnotatedTypeMirror.AnnotatedPrimitiveType;
import org.checkerframework.framework.type.AnnotatedTypeMirror.AnnotatedTypeVariable;
import org.checkerframework.framework.type.AnnotatedTypeMirror.AnnotatedWildcardType;
import org.checkerframework.framework.type.visitor.AnnotatedTypeScanner;
import org.checkerframework.framework.util.AnnotatedTypes;
import org.checkerframework.framework.util.AnnotationFormatter;
import org.checkerframework.framework.util.CFContext;
import org.checkerframework.framework.util.DefaultAnnotationFormatter;
import org.checkerframework.framework.util.GraphQualifierHierarchy;
import org.checkerframework.framework.util.MultiGraphQualifierHierarchy;
import org.checkerframework.framework.util.MultiGraphQualifierHierarchy.MultiGraphFactory;
import org.checkerframework.framework.util.TreePathCacher;
import org.checkerframework.framework.util.typeinference.DefaultTypeArgumentInference;
import org.checkerframework.framework.util.typeinference.TypeArgumentInference;
import org.checkerframework.javacutil.AnnotationProvider;
import org.checkerframework.javacutil.AnnotationUtils;
import org.checkerframework.javacutil.CollectionUtils;
import org.checkerframework.javacutil.ElementUtils;
import org.checkerframework.javacutil.ErrorReporter;
import org.checkerframework.javacutil.InternalUtils;
import org.checkerframework.javacutil.Pair;
import org.checkerframework.javacutil.TreeUtils;
import org.checkerframework.javacutil.trees.DetachedVarSymbol;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.lang.annotation.Annotation;
import java.lang.annotation.ElementType;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.annotation.processing.ProcessingEnvironment;
import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.element.Element;
import javax.lang.model.element.ElementKind;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.Name;
import javax.lang.model.element.TypeElement;
import javax.lang.model.element.TypeParameterElement;
import javax.lang.model.element.VariableElement;
import javax.lang.model.type.IntersectionType;
import javax.lang.model.type.PrimitiveType;
import javax.lang.model.type.TypeKind;
import javax.lang.model.type.TypeMirror;
import javax.lang.model.type.TypeVariable;
import javax.lang.model.type.WildcardType;
import javax.lang.model.util.Elements;
import javax.lang.model.util.Types;
import javax.tools.Diagnostic.Kind;
import com.sun.source.tree.AnnotationTree;
import com.sun.source.tree.AssignmentTree;
import com.sun.source.tree.ClassTree;
import com.sun.source.tree.CompilationUnitTree;
import com.sun.source.tree.ConditionalExpressionTree;
import com.sun.source.tree.ExpressionTree;
import com.sun.source.tree.IdentifierTree;
import com.sun.source.tree.LambdaExpressionTree;
import com.sun.source.tree.MemberReferenceTree;
import com.sun.source.tree.MethodInvocationTree;
import com.sun.source.tree.MethodTree;
import com.sun.source.tree.NewArrayTree;
import com.sun.source.tree.NewClassTree;
import com.sun.source.tree.ReturnTree;
import com.sun.source.tree.Tree;
import com.sun.source.tree.TypeCastTree;
import com.sun.source.tree.VariableTree;
import com.sun.source.util.TreePath;
import com.sun.source.util.Trees;
import com.sun.tools.javac.code.Symbol.MethodSymbol;
import com.sun.tools.javac.code.Type;
import com.sun.tools.javac.processing.JavacProcessingEnvironment;
import com.sun.tools.javac.util.Context;
/**
* The methods of this class take an element or AST node, and return the
* annotated type as an {@link AnnotatedTypeMirror}. The methods are:
*
*
* - {@link #getAnnotatedType(ClassTree)}
* - {@link #getAnnotatedType(MethodTree)}
* - {@link #getAnnotatedType(Tree)}
* - {@link #getAnnotatedTypeFromTypeTree(Tree)}
* - {@link #getAnnotatedType(TypeElement)}
* - {@link #getAnnotatedType(ExecutableElement)}
* - {@link #getAnnotatedType(Element)}
*
*
* 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#addComputedTypeAnnotations(Element, AnnotatedTypeMirror)}
* and {@link #addComputedTypeAnnotations(Tree, AnnotatedTypeMirror)}.
*
* @checker_framework.manual #writing-a-checker How to write a checker plug-in
*/
public class AnnotatedTypeFactory implements AnnotationProvider {
/** 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. */
// TODO: when should root be null? What are the use cases?
// None of the existing test checkers has a null root.
// Should not be modified between calls to "visit".
protected /*@Nullable*/ CompilationUnitTree root;
/** The processing environment to use for accessing compiler internals. */
protected final ProcessingEnvironment processingEnv;
/** Utility class for working with {@link Element}s. */
protected final Elements elements;
/** Utility class for working with {@link TypeMirror}s. */
protected final Types types;
/** The state of the visitor. **/
protected final VisitorState visitorState;
/** ===== postInit initialized fields ====
* Note: qualHierarchy and typeHierarchy are both initialized in the postInit.
* @see #postInit()
* This means, they cannot be final and cannot be referred to in any subclass
* constructor or method until after postInit is called.
*/
/** Represent the annotation relations. **/
protected QualifierHierarchy qualHierarchy;
/** Represent the type relations. */
protected TypeHierarchy typeHierarchy;
/** performs whole program inference */
private WholeProgramInference wholeProgramInference;
/**
* This formatter is used for converting AnnotatedTypeMirrors to Strings.
* This formatter will be passed to all AnnotatedTypeMirrors created by this
* factory and will be used in their toString methods.
*/
protected final AnnotatedTypeFormatter typeFormatter;
/**
* Annotation formatter is used to format AnnotationMirrors. It is primarily
* used by SourceChecker when generating error messages.
*/
private final AnnotationFormatter annotationFormatter;
/**
* Provides utility method to substitute arguments for their type variables.
* Field should be final, but can only be set in postInit, because subtypes
* might need other state to be initialized first.
*/
protected TypeVariableSubstitutor typeVarSubstitutor;
/**
* Provides utility method to infer type arguments
*/
protected TypeArgumentInference typeArgumentInference;
/** To cache the supported type qualifiers. */
private final Set> supportedQuals;
/** Types read from stub files (but not those from the annotated JDK jar file). */
// Initially null, then assigned in postInit(). Caching is enabled as
// soon as this is non-null, so it should be first set to its final
// value, not initialized to an empty map that is incrementally filled.
private Map typesFromStubFiles;
/**
* Declaration annotations read from stub files (but not those from the annotated JDK jar file).
* Map keys cannot be Element, because a different Element appears
* in the stub files than in the real files. So, map keys are the
* verbose element name, as returned by ElementUtils.getVerboseName.
*/
// Not final, because it is assigned in postInit().
private Map> declAnnosFromStubFiles;
/**
* A cache used to store elements whose declaration annotations
* have already been stored by calling the method {@link #getDeclAnnotations(Element)}.
*/
private final Map> cacheDeclAnnos;
/**
* A set containing declaration annotations that should be inherited.
* A declaration annotation will be inherited if it is in this set,
* or if it has the meta-annotation @InheritedAnnotation.
*/
private final Set inheritedAnnotations =
AnnotationUtils.createAnnotationSet();
/**
* The checker to use for option handling and resource management.
*/
protected final BaseTypeChecker checker;
/**
* Map from class name (canonical name) of an annotation, to the
* annotation in the Checker Framework that will be used in its place.
*/
private final Map aliases = new HashMap();
/**
* A map from the class name (canonical name) of an annotation to the set of
* class names (canonical names) for annotations with the same meaning
* (i.e., aliases), as well as the annotation mirror that should be used.
*/
private final Map>> declAliases = new HashMap<>();
/** Unique ID counter; for debugging purposes. */
private static int uidCounter = 0;
/** Unique ID of the current object; for debugging purposes. */
public final int uid;
/** Annotation added to every method defined in a class file
* that is not in a stub file.
*/
private final AnnotationMirror fromByteCode;
/** Annotation added to every method defined in a stub file.
*/
private final AnnotationMirror fromStubFile;
/**
* Object that is used to resolve reflective method calls, if reflection
* resolution is turned on.
*/
protected ReflectionResolver reflectionResolver;
/**
* Annotated Type Loader used to load annotation classes via reflective lookup
*/
protected AnnotationClassLoader loader;
/**
* Indicates that the whole-program inference is on.
*/
private final boolean infer;
/**
* Should results be cached?
* This means that ATM.deepCopy() will be called.
* ATM.deepCopy() used to (and perhaps still does) side effect the ATM being copied.
* So setting this to false is not equivalent to setting shouldReadCache to false. */
public boolean shouldCache;
/** Size of LRU cache if one isn't specified using the atfCacheSize option. */
private final static int DEFAULT_CACHE_SIZE = 300;
/** Mapping from a Tree to its annotated type; implicits have been applied. */
private final Map treeCache;
/** Mapping from a Tree to its annotated type; before implicits are applied,
* just what the programmer wrote. */
protected final Map fromTreeCache;
/** Mapping from an Element to its annotated type; before implicits are applied,
* just what the programmer wrote. */
private final Map elementCache;
/** Mapping from an Element to the source Tree of the declaration. */
private final Map elementToTreeCache;
/**
* 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.
*
* A subclass must call postInit at the end of its constructor.
* postInit must be the last call in the constructor or else types
* from stub files may not be created as expected.
*
* @param checker the {@link SourceChecker} to which this factory belongs
* @throws IllegalArgumentException if either argument is {@code null}
*/
public AnnotatedTypeFactory(BaseTypeChecker checker) {
uid = ++uidCounter;
this.processingEnv = checker.getProcessingEnvironment();
// this.root = root;
this.checker = checker;
this.trees = Trees.instance(processingEnv);
this.elements = processingEnv.getElementUtils();
this.types = processingEnv.getTypeUtils();
this.visitorState = new VisitorState();
this.loader = new AnnotationClassLoader(checker);
this.supportedQuals = createSupportedTypeQualifiers();
this.fromByteCode = AnnotationUtils.fromClass(elements, FromByteCode.class);
this.fromStubFile = AnnotationUtils.fromClass(elements, FromStubFile.class);
this.cacheDeclAnnos = new HashMap>();
this.shouldCache = !checker.hasOption("atfDoNotCache");
if (shouldCache) {
int cacheSize = getCacheSize();
this.treeCache = CollectionUtils.createLRUCache(cacheSize);
this.fromTreeCache = CollectionUtils.createLRUCache(cacheSize);
this.elementCache = CollectionUtils.createLRUCache(cacheSize);
this.elementToTreeCache = CollectionUtils.createLRUCache(cacheSize);
} else {
this.treeCache = null;
this.fromTreeCache = null;
this.elementCache = null;
this.elementToTreeCache = null;
}
this.typeFormatter = createAnnotatedTypeFormatter();
this.annotationFormatter = createAnnotationFormatter();
infer = checker.hasOption("infer");
if (infer) {
checkInvalidOptionsInferSignatures();
wholeProgramInference = new WholeProgramInferenceScenes(
!"NullnessAnnotatedTypeFactory".equals(this.getClass().getSimpleName()));
}
}
/**
* This method is called only when -Ainfer is passed as an option.
* It checks if another option that should not occur simultaneously with
* the whole-program inference is also passed as argument, and
* aborts the process if that is the case. For example, the whole-program
* inference process was not designed to work with unchecked code defaults.
* (Subclasses may override this method to add more options.)
*/
protected void checkInvalidOptionsInferSignatures() {
// See Issue 683
// https://github.com/typetools/checker-framework/issues/683
if (checker.useUncheckedCodeDefault("source") || checker.useUncheckedCodeDefault("bytecode")) {
ErrorReporter.errorAbort("The option -Ainfer cannot be" +
" used together with unchecked code defaults.");
}
}
/**
* Actions that logically belong in the constructor, but need to run
* after the subclass constructor has completed. In particular,
* parseStubFiles() may try to do type resolution with this
* AnnotatedTypeFactory.
*/
protected void postInit() {
this.qualHierarchy = createQualifierHierarchy();
if (qualHierarchy == null) {
ErrorReporter.errorAbort("AnnotatedTypeFactory with null qualifier hierarchy not supported.");
}
this.typeHierarchy = createTypeHierarchy();
this.typeVarSubstitutor = createTypeVariableSubstitutor();
this.typeArgumentInference = createTypeArgumentInference();
// TODO: is this the best location for declaring this alias?
addAliasedDeclAnnotation(org.jmlspecs.annotation.Pure.class,
org.checkerframework.dataflow.qual.Pure.class,
AnnotationUtils.fromClass(elements, org.checkerframework.dataflow.qual.Pure.class));
addInheritedAnnotation(AnnotationUtils.fromClass(elements,
org.checkerframework.dataflow.qual.Pure.class));
addInheritedAnnotation(AnnotationUtils.fromClass(elements,
org.checkerframework.dataflow.qual.SideEffectFree.class));
addInheritedAnnotation(AnnotationUtils.fromClass(elements,
org.checkerframework.dataflow.qual.Deterministic.class));
addInheritedAnnotation(AnnotationUtils.fromClass(elements,
org.checkerframework.dataflow.qual.TerminatesExecution.class));
initilizeReflectionResolution();
if (this.getClass().equals(AnnotatedTypeFactory.class)) {
this.parseStubFiles();
}
}
/**
* Returns the WholeProgramInference instance.
*/
public WholeProgramInference getWholeProgramInference() {
return wholeProgramInference;
}
protected void initilizeReflectionResolution() {
if (checker.shouldResolveReflection()) {
boolean debug = "debug".equals(checker.getOption("resolveReflection"));
MethodValChecker methodValChecker = checker
.getSubchecker(MethodValChecker.class);
assert methodValChecker != null : "AnnotatedTypeFactory: reflection resolution was requested, but MethodValChecker isn't a subchecker.";
MethodValAnnotatedTypeFactory methodValATF = (MethodValAnnotatedTypeFactory) methodValChecker
.getAnnotationProvider();
reflectionResolver = new DefaultReflectionResolver(checker,
methodValATF, debug);
}
}
// TODO: document
// Set the CompilationUnitTree that should be used.
// What's a better name? Maybe "reset" or "start"?
public void setRoot(/*@Nullable*/ CompilationUnitTree root) {
this.root = root;
treePathCache.clear();
pathHack.clear();
// There is no need to clear the following caches, they
// are all limited by CACHE_SIZE.
/*
treeCache.clear();
fromTreeCache.clear();
elementCache.clear();
elementToTreeCache.clear();
*/
}
@SideEffectFree
@Override
public String toString() {
return getClass().getSimpleName() + "#" + uid;
}
/** Factory method to easily change what Factory is used to
* create a QualifierHierarchy.
*/
protected MultiGraphQualifierHierarchy.MultiGraphFactory createQualifierHierarchyFactory() {
return new MultiGraphQualifierHierarchy.MultiGraphFactory(this);
}
/** Factory method to easily change what QualifierHierarchy is
* created.
* Needs to be public only because the GraphFactory must be able to call this method.
* No external use of this method is necessary.
*/
public QualifierHierarchy createQualifierHierarchy(MultiGraphFactory factory) {
return new GraphQualifierHierarchy(factory, null);
}
/**
* Returns the type qualifier hierarchy graph to be used by this processor.
*
* The implementation builds the type qualifier hierarchy for the
* {@link #getSupportedTypeQualifiers()} using the
* meta-annotations found in them. The current implementation returns an
* instance of {@code GraphQualifierHierarchy}.
*
* Subclasses may override this method to express any relationships that
* cannot be inferred using meta-annotations (e.g. due to lack of
* meta-annotations).
*
* @return an annotation relation tree representing the supported qualifiers
*/
protected QualifierHierarchy createQualifierHierarchy() {
Set> supportedTypeQualifiers = getSupportedTypeQualifiers();
MultiGraphQualifierHierarchy.MultiGraphFactory factory = this.createQualifierHierarchyFactory();
return createQualifierHierarchy(elements, supportedTypeQualifiers, factory);
}
/**
* Returns the type qualifier hierarchy graph for a given set of type qualifiers and a factory.
*
* The implementation builds the type qualifier hierarchy for the
* {@code supportedTypeQualifiers}. The current implementation returns an
* instance of {@code GraphQualifierHierarchy}.
*
* @return an annotation relation tree representing the supported qualifiers
*/
protected static QualifierHierarchy createQualifierHierarchy(
Elements elements,
Set> supportedTypeQualifiers,
MultiGraphFactory factory) {
for (Class typeQualifier : supportedTypeQualifiers) {
AnnotationMirror typeQualifierAnno = AnnotationUtils.fromClass(elements, typeQualifier);
assert typeQualifierAnno != null : "Loading annotation \"" + typeQualifier + "\" failed!";
factory.addQualifier(typeQualifierAnno);
// Polymorphic qualifiers can't declare their supertypes.
// An error is raised if one is present.
if (typeQualifier.getAnnotation(PolymorphicQualifier.class) != null) {
if (typeQualifier.getAnnotation(SubtypeOf.class) != null) {
// This is currently not supported. At some point we might add
// polymorphic qualifiers with upper and lower bounds.
ErrorReporter.errorAbort("AnnotatedTypeFactory: " + typeQualifier + " is polymorphic and specifies super qualifiers. " +
"Remove the @org.checkerframework.framework.qual.SubtypeOf or @org.checkerframework.framework.qual.PolymorphicQualifier annotation from it.");
}
continue;
}
if (typeQualifier.getAnnotation(SubtypeOf.class) == null) {
ErrorReporter.errorAbort("AnnotatedTypeFactory: " + typeQualifier + " does not specify its super qualifiers. " +
"Add an @org.checkerframework.framework.qual.SubtypeOf annotation to it.");
}
Class[] superQualifiers =
typeQualifier.getAnnotation(SubtypeOf.class).value();
for (Class superQualifier : superQualifiers) {
if (!supportedTypeQualifiers.contains(superQualifier)) {
continue;
}
AnnotationMirror superAnno = null;
superAnno = AnnotationUtils.fromClass(elements, superQualifier);
factory.addSubtype(typeQualifierAnno, superAnno);
}
}
QualifierHierarchy hierarchy = factory.build();
if (!hierarchy.isValid()) {
ErrorReporter.errorAbort("AnnotatedTypeFactory: invalid qualifier hierarchy: " +
hierarchy.getClass() + " " + hierarchy);
}
return hierarchy;
}
/**
* Returns the type qualifier hierarchy graph to be used by this processor.
*
* @see #createQualifierHierarchy()
*
* @return the {@link QualifierHierarchy} for this checker
*/
public final QualifierHierarchy getQualifierHierarchy() {
// if (qualHierarchy == null)
// qualHierarchy = createQualifierHierarchy();
return qualHierarchy;
}
/**
* Creates the type subtyping checker using the current type qualifier
* hierarchy.
*
* Subclasses may override this method to specify new type-checking
* rules beyond the typical java subtyping rules.
*
* @return the type relations class to check type subtyping
*/
protected TypeHierarchy createTypeHierarchy() {
return new DefaultTypeHierarchy(checker, getQualifierHierarchy(),
checker.getOption("ignoreRawTypeArguments", "true").equals("true"),
checker.hasOption("invariantArrays"));
}
public final TypeHierarchy getTypeHierarchy() {
return typeHierarchy;
}
/**
* TypeVariableSubstitutor provides a method to replace type parameters with
* their arguments.
*/
protected TypeVariableSubstitutor createTypeVariableSubstitutor() {
return new TypeVariableSubstitutor();
}
public TypeVariableSubstitutor getTypeVarSubstitutor() {
return typeVarSubstitutor;
}
/**
* TypeArgumentInference infers the method type arguments when
* they are not explicitly written.
*/
protected TypeArgumentInference createTypeArgumentInference() {
return new DefaultTypeArgumentInference();
}
public TypeArgumentInference getTypeArgumentInference() {
return typeArgumentInference;
}
/**
* Returns an immutable set of annotation classes that are supported by a checker
*
* Subclasses may override this method and to return an immutable set
* of their supported type qualifiers through one of the 5 approaches shown below.
*
* Subclasses should not call this method; they should call
* {@link #getSupportedTypeQualifiers} instead.
*
* By default, a checker supports {@link PolyAll}, and all annotations located
* in a subdirectory called {@literal qual} that's located in the same directory
* as the checker. Note that only annotations defined with the
* {@code @Target({ElementType.TYPE_USE})} meta-annotation (and optionally with
* the additional value of {@code ElementType.TYPE_PARAMETER}, but no other
* {@code ElementType} values) are automatically considered as supported
* annotations.
*
* Annotations located outside the {@literal qual} subdirectory, or has other
* {@code ElementType} values must be explicitly listed in code by overriding
* the
* {@link #createSupportedTypeQualifiers()}
* method, as shown below.
*
* Lastly, for checkers that do not want to support {@link PolyAll}, it must
* also be explicitly written in code, as shown below.
*
* In total, there are 5 ways to indicate annotations that are supported by a
* checker:
*
* -
* Only support annotations located in a checker's {@literal qual} directory,
* and {@link PolyAll}:
*
* This is the default behavior. Simply place those annotations within the
* {@literal qual} directory.
*
* -
* Support annotations located in a checker's {@literal qual} directory, but
* without {@link PolyAll}:
*
* Place those annotations within the {@literal qual} directory, and override
* {@link #createSupportedTypeQualifiers()} by calling
* {@link #getBundledTypeQualifiersWithPolyAll(Class...)} with no
* parameters passed in. Code example:
*
* {@code @Override protected Set> createSupportedTypeQualifiers() {
* return getBundledTypeQualifiersWithoutPolyAll();
* } }
*
*
* -
* Support annotations located in a checker's {@literal qual} directory,
* {@link PolyAll}, and a list of other annotations:
*
* Place those annotations within the {@literal qual} directory, and override
* {@link #createSupportedTypeQualifiers()} by calling
* {@link #getBundledTypeQualifiersWithPolyAll(Class...)} with a
* varargs parameter list of the other annotations. Code example:
*
*
* {@code @Override protected Set> createSupportedTypeQualifiers() {
* return getBundledTypeQualifiersWithPolyAll(Regex.class, PartialRegex.class, RegexBottom.class, UnknownRegex.class);
* } }
*
*
* -
* Support annotations located in a checker's {@literal qual} directory and a
* list of other annotations, but without supporting {@link PolyAll}:
*
* Place those annotations within the {@literal qual} directory, and override
* {@link #createSupportedTypeQualifiers()} by calling
* {@link #getBundledTypeQualifiersWithoutPolyAll(Class...)} with a
* varargs parameter list of the other annotations. Code example:
*
* {@code @Override protected Set> createSupportedTypeQualifiers() {
* return getBundledTypeQualifiersWithoutPolyAll(UnknownFormat.class, FormatBottom.class);
* } }
*
*
* -
* Supporting only annotations that are explicitly listed:
* Override
* {@link #createSupportedTypeQualifiers()} and return an immutable
* set of the supported annotations. Code example:
*
* {@code @Override protected Set> createSupportedTypeQualifiers() {
* return Collections.unmodifiableSet(
* new HashSet>(
* Arrays.asList(A.class, B.class)));
* } }
*
*
* The set of qualifiers returned by
* {@link #createSupportedTypeQualifiers()} must be an immutable
* set. The methods
* {@link #getBundledTypeQualifiersWithoutPolyAll(Class...)} and
* {@link #getBundledTypeQualifiersWithPolyAll(Class...)} each
* return an immutable set.
*
*
*
* @return the type qualifiers supported this processor, or an empty set if
* none
*/
protected Set> createSupportedTypeQualifiers() {
// by default support PolyAll
return getBundledTypeQualifiersWithPolyAll();
}
/**
* Loads all annotations contained in the qual directory of a checker via
* reflection, and adds {@link PolyAll} and an explicit array of annotations
* to the set of annotation classes.
*
* This method can be called in the overridden versions of
* {@link #createSupportedTypeQualifiers()} in each checker.
*
* @param explicitlyListedAnnotations
* a varargs array of explicitly listed annotation classes to be
* added to the returned set. For example, it is used frequently
* to add Bottom qualifiers.
* @return an immutable set of the loaded and listed annotation classes, as
* well as {@link PolyAll}.
*/
@SafeVarargs
protected final Set> getBundledTypeQualifiersWithPolyAll(Class... explicitlyListedAnnotations) {
Set> annotations = loadTypeAnnotationsFromQualDir(explicitlyListedAnnotations);
annotations.add(PolyAll.class);
return Collections.unmodifiableSet(annotations);
}
/**
* Loads all annotations contained in the qual directory of a checker via
* reflection, and an explicit list of annotations to the set of annotation
* classes.
*
* This method can be called in the overridden versions of
* {@link #createSupportedTypeQualifiers()} in each checker.
*
* @param explicitlyListedAnnotations
* a varargs array of explicitly listed annotation classes to be
* added to the returned set. For example, it is used frequently
* to add Bottom qualifiers.
* @return an immutable set of the loaded, and listed annotation classes
*/
@SafeVarargs
protected final Set> getBundledTypeQualifiersWithoutPolyAll(Class... explicitlyListedAnnotations) {
return Collections.unmodifiableSet(loadTypeAnnotationsFromQualDir(explicitlyListedAnnotations));
}
/**
* Loads all annotations contained in the qual directory of a checker via
* reflection, and has the option to include an explicitly stated list of
* annotations (eg ones found in a different directory than qual).
*
* The annotations that are automatically loaded must have the
* {@link java.lang.annotation.Target Target} meta-annotation with the value
* of {@link ElementType#TYPE_USE} (and optionally
* {@link ElementType#TYPE_PARAMETER}). If it has other {@link ElementType}
* values, it won't be loaded. Other annotation classes must be explicitly
* listed even if they are in the same directory as the checker's qual
* directory.
*
* @param explicitlyListedAnnotations
* a set of explicitly listed annotation classes to be added to
* the returned set, for example, it is used frequently to add
* Bottom qualifiers
* @return a set of annotation class instances
*/
@SafeVarargs
@SuppressWarnings("varargs")
private final Set> loadTypeAnnotationsFromQualDir(Class... explicitlyListedAnnotations) {
// add the loaded annotations to the annotation set
Set> annotations = loader.getLoadedAnnotationClasses();
// add in all explicitly Listed qualifiers
if (explicitlyListedAnnotations != null) {
annotations.addAll(Arrays.asList(explicitlyListedAnnotations));
}
return annotations;
}
/**
* Creates the AnnotatedTypeFormatter used by this type factory and all AnnotatedTypeMirrors
* it creates. The AnnotatedTypeFormatter is used in AnnotatedTypeMirror.toString and
* will affect the error messages printed for checkers that use this type factory.
* @return the AnnotatedTypeFormatter to pass to all instantiated AnnotatedTypeMirrors
*/
protected AnnotatedTypeFormatter createAnnotatedTypeFormatter() {
return new DefaultAnnotatedTypeFormatter(checker.hasOption("printVerboseGenerics"), checker.hasOption("printAllQualifiers"));
}
protected AnnotationFormatter createAnnotationFormatter() {
return new DefaultAnnotationFormatter();
}
public AnnotationFormatter getAnnotationFormatter() {
return annotationFormatter;
}
/**
* Returns an immutable set of the type qualifiers supported by this
* checker.
*
* Subclasses cannot override this method; they should override
* {@link #createSupportedTypeQualifiers createSupportedTypeQualifiers}
* instead.
*
* @see #createSupportedTypeQualifiers()
*
* @return an immutable set of the supported type qualifiers, or an
* empty set if no qualifiers are supported
*/
public final Set> getSupportedTypeQualifiers() {
return supportedQuals;
}
// **********************************************************************
// Factories for annotated types that account for implicit qualifiers
// **********************************************************************
/** Mapping from a Tree to its TreePath **/
private final TreePathCacher treePathCache = new TreePathCacher();
/**
* Returns the int supplied to the checker via the atfCacheSize option or
* the default cache size.
* @return cache size passed as argument to checker or DEFAULT_CACHE_SIZE
*/
private int getCacheSize() {
String option = checker.getOption("atfCacheSize");
if (option == null) {
return DEFAULT_CACHE_SIZE;
}
try {
return Integer.valueOf(option);
} catch (NumberFormatException ex) {
ErrorReporter.errorAbort("atfCacheSize was not an integer: " + option);
return 0; // dead code
}
}
/**
* Returns an AnnotatedTypeMirror representing the annotated type of {@code elt}.
*
* @param elt the element
* @return the annotated type of {@code elt}
*/
public AnnotatedTypeMirror getAnnotatedType(Element elt) {
if (elt == null) {
ErrorReporter.errorAbort("AnnotatedTypeFactory.getAnnotatedType: null element");
return null; // dead code
}
// Annotations explicitly written in the source code,
// or obtained from bytecode.
AnnotatedTypeMirror type = fromElement(elt);
// Implicits due to writing annotation on the class declaration.
annotateInheritedFromClass(type);
addComputedTypeAnnotations(elt, type);
return type;
}
@Override
public AnnotationMirror getAnnotationMirror(Tree tree, Class target) {
AnnotationMirror mirror = AnnotationUtils.fromClass(elements, target);
if (isSupportedQualifier(mirror)) {
AnnotatedTypeMirror atm = getAnnotatedType(tree);
return atm.getAnnotation(target);
}
return null;
}
/**
* Returns an AnnotatedTypeMirror representing the annotated type of {@code tree}.
*
* @param tree the AST node
* @return the annotated type of {@code tree}
*/
public AnnotatedTypeMirror getAnnotatedType(Tree tree) {
if (tree == null) {
ErrorReporter.errorAbort("AnnotatedTypeFactory.getAnnotatedType: null tree");
return null; // dead code
}
if (shouldCache && treeCache.containsKey(tree)) {
return treeCache.get(tree).deepCopy();
}
AnnotatedTypeMirror type;
if (TreeUtils.isClassTree(tree)) {
type = fromClass((ClassTree)tree);
} else if (tree.getKind() == Tree.Kind.METHOD ||
tree.getKind() == Tree.Kind.VARIABLE) {
type = fromMember(tree);
} else if (TreeUtils.isExpressionTree(tree)) {
tree = TreeUtils.skipParens((ExpressionTree)tree);
type = fromExpression((ExpressionTree) tree);
} else {
ErrorReporter.errorAbort(
"AnnotatedTypeFactory.getAnnotatedType: query of annotated type for tree " + tree.getKind());
type = null; // dead code
}
addComputedTypeAnnotations(tree, type);
if (TreeUtils.isClassTree(tree) ||
tree.getKind() == Tree.Kind.METHOD) {
// Don't cache VARIABLE
if (shouldCache) {
treeCache.put(tree, type.deepCopy());
}
} else {
// No caching otherwise
}
if (TreeUtils.isClassTree(tree)) {
postProcessClassTree((ClassTree) tree);
}
// System.out.println("AnnotatedTypeFactory::getAnnotatedType(Tree) result: " + type);
return type;
}
/**
* Called by getAnnotatedType(Tree) for each ClassTree after determining the type.
* The default implementation uses this to store the defaulted AnnotatedTypeMirrors
* and inherited declaration annotations back into the corresponding Elements.
* Subclasses might want to override this method if storing defaulted types is
* not desirable.
*/
protected void postProcessClassTree(ClassTree tree) {
TypesIntoElements.store(processingEnv, this, tree);
DeclarationsIntoElements.store(processingEnv, this, tree);
if (checker.hasOption("infer") && wholeProgramInference != null) {
// Write scenes into .jaif files. In order to perform the write
// operation only once for each .jaif file, the best location to
// do so is here.
wholeProgramInference.saveResults();
}
}
/**
* Determines the annotated type from a type in tree form.
*
* Note that we cannot decide from a Tree whether it is
* a type use or an expression.
* TreeUtils.isTypeTree is only an under-approximation.
* For example, an identifier can be either a type or an expression.
*
* @param tree the type tree
* @return the annotated type of the type in the AST
*/
public AnnotatedTypeMirror getAnnotatedTypeFromTypeTree(Tree tree) {
if (tree == null) {
ErrorReporter.errorAbort("AnnotatedTypeFactory.getAnnotatedTypeFromTypeTree: null tree");
return null; // dead code
}
AnnotatedTypeMirror type = fromTypeTree(tree);
addComputedTypeAnnotations(tree, type);
return type;
}
// **********************************************************************
// Factories for annotated types that do not account for implicit qualifiers.
// They only include qualifiers explicitly inserted by the user.
// **********************************************************************
/**
* Creates an AnnotatedTypeMirror for {@code elt} that includes:
* annotations explicitly written on the element and annotations from stub files
*
* @param elt the element
* @return AnnotatedTypeMirror of the element with explicitly-written and stub file annotations
*/
public AnnotatedTypeMirror fromElement(Element elt) {
if (shouldCache && elementCache.containsKey(elt)) {
return elementCache.get(elt).deepCopy();
}
if (elt.getKind() == ElementKind.PACKAGE) {
return toAnnotatedType(elt.asType(), false);
}
AnnotatedTypeMirror type;
// Because of a bug in Java 8, annotations on type parameters are not stored in elements,
// so get explicit annotations from the tree. (This bug has been fixed in Java 9.)
// Also, since annotations computed by the AnnotatedTypeFactory are stored in the element,
// the annotations have to be retrived from the tree so that only explicit annotations are returned.
Tree decl = declarationFromElement(elt);
if (decl == null && typesFromStubFiles != null && typesFromStubFiles.containsKey(elt)) {
type = typesFromStubFiles.get(elt).deepCopy();
} else if (decl == null && (typesFromStubFiles == null || !typesFromStubFiles.containsKey(elt))) {
type = toAnnotatedType(elt.asType(), ElementUtils.isTypeDeclaration(elt));
ElementAnnotationApplier.apply(type, elt, this);
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 if (decl.getKind() == Tree.Kind.TYPE_PARAMETER) {
type = fromTypeTree(decl);
} else {
ErrorReporter.errorAbort("AnnotatedTypeFactory.fromElement: cannot be here! decl: " + decl.getKind() +
" elt: " + elt, null);
type = null; // dead code
}
// Caching is disabled if typesFromStubFiles == null, because calls to this
// method before the stub files are fully read can return incorrect
// results.
if (shouldCache && typesFromStubFiles != null) {
elementCache.put(elt, type.deepCopy());
}
return type;
}
/**
* Adds @FromByteCode to methods, constructors, and fields declared in class files
* that are not already annotated with @FromStubFile
*/
private void addFromByteCode(Element elt) {
if (declAnnosFromStubFiles == null) { // || trees.getTree(elt) != null) {
// Parsing stub files, don't add @FromByteCode
return;
}
if (elt.getKind() == ElementKind.CONSTRUCTOR ||
elt.getKind() == ElementKind.METHOD || elt.getKind() == ElementKind.FIELD) {
// Only add @FromByteCode to methods, constructors, and fields
if (ElementUtils.isElementFromByteCode(elt)) {
Set annos = declAnnosFromStubFiles.get(ElementUtils
.getVerboseName(elt));
if (annos == null) {
annos = AnnotationUtils.createAnnotationSet();
declAnnosFromStubFiles.put(ElementUtils.getVerboseName(elt), annos);
}
if (!AnnotationUtils.containsSameIgnoringValues(annos, fromStubFile)) {
annos.add(fromByteCode);
}
}
}
}
/**
* Returns an AnnotatedDeclaredType with explicit annotations from the
* ClassTree {@code tree}.
*
* @param tree the class declaration
* @return AnnotatedDeclaredType with explicit annotations from {@code tree}
*/
private AnnotatedDeclaredType fromClass(ClassTree tree) {
return TypeFromTree.fromClassTree(this, tree);
}
/**
* Creates an AnnotatedTypeMirror for a variable or method declaration tree.
* The AnnotatedTypeMirror contains annotations explicitly written on the tree
* and annotations inherited from the class declarations
* {@link #annotateInheritedFromClass(AnnotatedTypeMirror)}.
*
* If a VariableTree is a parameter to a lambda, this method also adds
* annotations from the declared type of the functional interface and the executable
* type of its method.
*
* @param tree MethodTree or VariableTree
* @return AnnotatedTypeMirror with explicit annotations from {@code tree}
* and annotations inherited from class declarations
*/
private final AnnotatedTypeMirror fromMember(Tree tree) {
if (!(tree instanceof MethodTree || tree instanceof VariableTree)) {
ErrorReporter.errorAbort("AnnotatedTypeFactory.fromMember: not a method or variable declaration: " + tree);
return null; // dead code
}
if (shouldCache && fromTreeCache.containsKey(tree)) {
return fromTreeCache.get(tree).deepCopy();
}
AnnotatedTypeMirror result = TypeFromTree.fromMember(this, tree);
annotateInheritedFromClass(result);
if (shouldCache) {
fromTreeCache.put(tree, result.deepCopy());
}
return result;
}
/**
* Creates an AnnotatedTypeMirror for an ExpressionTree.
* The AnnotatedTypeMirror contains explicit annotations written with on the expression,
* annotations inherited from class declarations, and for some expressions, annotations
* from sub-expressions that could have been explicitly written, implicited, defaulted,
* refined, or otherwise computed. (Expression whose type include annotations from sub-
* expressions are: ArrayAccessTree, ConditionalExpressionTree, IdentifierTree,
* MemberSelectTree, and MethodInvocationTree.)
*
* For example, the AnnotatedTypeMirror returned for an array access expression is the
* fully annotated type of the array component of the array being accessed.
*
* @param tree an expression
* @return AnnotatedTypeMirror of the expressions either fully-annotated or partially
* annotated depending on the kind of expression
*
* @see TypeFromExpressionVisitor
*/
private AnnotatedTypeMirror fromExpression(ExpressionTree tree) {
if (shouldCache && fromTreeCache.containsKey(tree)) {
return fromTreeCache.get(tree).deepCopy();
}
AnnotatedTypeMirror result = TypeFromTree.fromExpression(this, tree);
annotateInheritedFromClass(result);
if (shouldCache) {
fromTreeCache.put(tree, result.deepCopy());
}
return result;
}
/**
* Creates an AnnotatedTypeMirror for the tree. The AnnotatedTypeMirror contains
* annotations explicitly written on the tree and annotations inherited from class
* declarations {@link #annotateInheritedFromClass(AnnotatedTypeMirror)}.
* It also adds type arguments to raw types that include annotations from the element
* declaration of the type {@link #fromElement(Element)}.
*
* Called on the following trees: AnnotatedTypeTree, ArrayTypeTree, ParameterizedTypeTree,
* PrimitiveTypeTree, TypeParameterTree, WildcardTree, UnionType,
* IntersectionTypeTree, and IdentifierTree, MemberSelectTree.
*
* @param tree the type tree
* @return the (partially) annotated type of the type in the AST
*/
/*package private*/ final AnnotatedTypeMirror fromTypeTree(Tree tree) {
if (shouldCache && fromTreeCache.containsKey(tree)) {
return fromTreeCache.get(tree).deepCopy();
}
AnnotatedTypeMirror result = TypeFromTree.fromTypeTree(this, tree);
// treat Raw as generic!
// TODO: This doesn't handle recursive type parameter
// e.g. class Pair> { ... }
// Type argument inference for raw types can be improved. See Issue 635.
// https://github.com/typetools/checker-framework/issues/635
if (result.getKind() == TypeKind.DECLARED) {
AnnotatedDeclaredType dt = (AnnotatedDeclaredType) result;
if (dt.wasRaw()) {
List typeArgs = new ArrayList();
AnnotatedDeclaredType declaration = fromElement((TypeElement) dt.getUnderlyingType().asElement());
for (AnnotatedTypeMirror typeParam : declaration.getTypeArguments()) {
AnnotatedWildcardType wct = getUninferredWildcardType((AnnotatedTypeVariable) typeParam);
typeArgs.add(wct);
}
dt.setTypeArguments(typeArgs);
}
}
annotateInheritedFromClass(result);
if (shouldCache) {
fromTreeCache.put(tree, result.deepCopy());
}
return result;
}
// **********************************************************************
// Customization methods meant to be overridden 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 addComputedTypeAnnotations(Tree tree, 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 addComputedTypeAnnotations(Element elt, 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.
*
* @param type the type whose supertypes are desired
* @param supertypes
* the supertypes as specified by the base AnnotatedTypeFactory
*/
protected void postDirectSuperTypes(AnnotatedTypeMirror type,
List supertypes) {
// Use the effective annotations here to get the correct annotations
// for type variables and wildcards.
Set annotations = type.getEffectiveAnnotations();
for (AnnotatedTypeMirror supertype : supertypes) {
if (!annotations.equals(supertype.getEffectiveAnnotations())) {
supertype.clearAnnotations();
// TODO: is this correct for type variables and wildcards?
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
*/
public void postAsMemberOf(AnnotatedTypeMirror type,
AnnotatedTypeMirror owner, Element element) {
addComputedTypeAnnotations(element, type);
}
/**
* A callback method for the AnnotatedTypeFactory subtypes to customize
* AnnotatedTypeMirror.substitute().
*
* @param varDecl a declaration of a type variable
* @param varUse a use of the same type variable
* @param value the new type to substitute in for the type variable
*/
public void postTypeVarSubstitution(AnnotatedTypeVariable varDecl,
AnnotatedTypeVariable varUse, AnnotatedTypeMirror value) {
if (!varUse.getAnnotationsField().isEmpty()
&& !AnnotationUtils.areSame(varUse.getAnnotationsField(), varDecl.getAnnotationsField())) {
value.replaceAnnotations(varUse.getAnnotationsField());
}
}
/**
* Adapt the upper bounds of the type variables of a class relative
* to the type instantiation.
* In some type systems, the upper bounds depend on the instantiation
* of the class. For example, in the Generic Universe Type system,
* consider a class declaration
* class C<X extends @Peer Object>
* then the instantiation
* @Rep C<@Rep Object>
* is legal. The upper bounds of class C have to be adapted
* by the main modifier.
*
* An example of an adaptation follows. Suppose, I have a declaration:
* class MyClass<E extends List<E>>
* And an instantiation:
* new MyClass<@NonNull String>()
*
* The upper bound of E adapted to the argument String, would be List<@NonNull String>
* and the lower bound would be an AnnotatedNullType.
*
* TODO: ensure that this method is consistently used instead
* of directly querying the type variables.
*
* @param type the use of the type
* @param element the corresponding element
* @return the adapted bounds of the type parameters
*/
public List typeVariablesFromUse(
AnnotatedDeclaredType type, TypeElement element) {
AnnotatedDeclaredType generic = getAnnotatedType(element);
List targs = type.getTypeArguments();
List tvars = generic.getTypeArguments();
assert targs.size() == tvars.size() : "Mismatch in type argument size between " + type + " and " + generic;
// System.err.printf("TVFU\n type: %s\n generic: %s\n", type, generic);
Map mapping = new HashMap<>();
for (int i = 0; i < targs.size(); ++i) {
mapping.put(((AnnotatedTypeVariable)tvars.get(i)).getUnderlyingType(), targs.get(i));
}
List res = new LinkedList<>();
for (AnnotatedTypeMirror atm : tvars) {
AnnotatedTypeVariable atv = (AnnotatedTypeVariable)atm;
AnnotatedTypeMirror upper = typeVarSubstitutor.substitute(mapping, atv.getUpperBound());
AnnotatedTypeMirror lower = typeVarSubstitutor.substitute(mapping, atv.getLowerBound());
res.add(new AnnotatedTypeParameterBounds(upper, lower));
}
return res;
}
/**
* 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.
*
* The class type is found using {@link #fromElement(Element)}
*
* @param type the type for which class annotations will be inherited if
* there are no annotations already present
*/
protected void annotateInheritedFromClass(AnnotatedTypeMirror type) {
InheritedFromClassAnnotator.INSTANCE.visit(type, this);
}
/**
* Callback to determine what to do with the annotations from a class declaration.
*/
protected void annotateInheritedFromClass(AnnotatedTypeMirror type,
Set fromClass) {
type.addMissingAnnotations(fromClass);
}
/**
* Creates and returns an AnnotatedNullType qualified with {@code annotations}.
* @param annotations set of AnnotationMirrors to qualify the returned type with
* @return AnnotatedNullType qualified with {@code annotations}
*/
public AnnotatedNullType getAnnotatedNullType(Set annotations) {
final AnnotatedTypeMirror.AnnotatedNullType nullType = (AnnotatedNullType)
toAnnotatedType(processingEnv.getTypeUtils().getNullType(), false);
nullType.addAnnotations(annotations);
return nullType;
}
/**
* A singleton utility class for pulling annotations down from a class
* type.
*
* @see #annotateInheritedFromClass
*/
protected static class InheritedFromClassAnnotator
extends AnnotatedTypeScanner {
/** 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.
// Also skip constructor return types (which somewhat act like
// the receiver).
MethodSymbol methodElt = (MethodSymbol)type.getElement();
if (methodElt == null || !methodElt.isConstructor()) {
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 from that hierarchy already on the type.
if (classElt != null) {
AnnotatedTypeMirror classType = p.fromElement(classElt);
assert classType != null : "Unexpected null type for class element: " + classElt;
p.annotateInheritedFromClass(type, classType.getAnnotations());
}
return super.visitDeclared(type, p);
}
private final Map visited =
new HashMap();
@Override
public Void visitTypeVariable(AnnotatedTypeVariable type, AnnotatedTypeFactory p) {
TypeParameterElement tpelt = (TypeParameterElement) type.getUnderlyingType().asElement();
if (!visited.containsKey(tpelt)) {
visited.put(tpelt, type);
if (type.getAnnotations().isEmpty() &&
type.getUpperBound().getAnnotations().isEmpty() &&
tpelt.getEnclosingElement().getKind() != ElementKind.TYPE_PARAMETER) {
ElementAnnotationApplier.apply(type, tpelt, p);
}
super.visitTypeVariable(type, p);
visited.remove(tpelt);
}
return null;
}
@Override
public void reset() {
visited.clear();
super.reset();
}
}
// **********************************************************************
// Utilities method for getting specific types from trees or elements
// **********************************************************************
/**
* Return the implicit receiver type of an expression tree.
*
* The result is null for expressions that don't have a receiver,
* e.g. for a local variable or method parameter access.
*
* @param tree the expression that might have an implicit receiver
* @return the type of the receiver
*/
/*
* TODO: receiver annotations on outer this.
* TODO: Better document the difference between getImplicitReceiverType and getSelfType?
* TODO: this method assumes that the tree is within the current
* Compilation Unit. This assumption fails in testcase Bug109_A/B, where
* a chain of dependencies leads into a different compilation unit.
* I didn't find a way how to handle this better and conservatively
* return null. See TODO comment below.
*
*/
protected AnnotatedDeclaredType getImplicitReceiverType(ExpressionTree 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) : "Unexpected tree kind: " + tree.getKind();
Element element = InternalUtils.symbol(tree);
assert element != null : "Unexpected null element for tree: " + tree;
// Return null if the element kind has no receiver.
if (!ElementUtils.hasReceiver(element)) {
return null;
}
ExpressionTree receiver = TreeUtils.getReceiverTree(tree);
if (receiver==null) {
if (isMostEnclosingThisDeref(tree)) {
// TODO: problem with ambiguity with implicit receivers.
// We need a way to find the correct class. We cannot use the
// element, as generics might have to be substituted in a subclass.
// See GenericsEnclosing test case.
// TODO: is this fixed?
return getSelfType(tree);
} else {
TreePath path = getPath(tree);
if (path == null) {
// The path is null if the field is in a compilation unit we haven't
// processed yet. TODO: is there a better way?
// This only arises in the Nullness Checker when substituting rawness.
return null;
}
TypeElement typeElt = ElementUtils.enclosingClass(element);
if (typeElt == null) {
ErrorReporter.errorAbort("AnnotatedTypeFactory.getImplicitReceiver: enclosingClass()==null for element: " + element);
}
// TODO: method receiver annotations on outer this
return getEnclosingType(typeElt, tree);
}
}
Element rcvelem = InternalUtils.symbol(receiver);
assert rcvelem != null : "Unexpected null element for receiver: " + receiver;
if (!ElementUtils.hasReceiver(rcvelem)) {
return null;
}
if (receiver.getKind() == Tree.Kind.IDENTIFIER
&& ((IdentifierTree)receiver).getName().contentEquals("this")) {
// TODO: also "super"?
return this.getSelfType(tree);
}
TypeElement typeElt = ElementUtils.enclosingClass(rcvelem);
if (typeElt == null) {
ErrorReporter.errorAbort("AnnotatedTypeFactory.getImplicitReceiver: enclosingClass()==null for element: " + rcvelem);
}
AnnotatedDeclaredType type = getAnnotatedType(typeElt);
// TODO: go through _all_ enclosing methods to see whether any of them has a
// receiver annotation of the correct type.
// TODO: Can we reuse getSelfType for outer this accesses?
AnnotatedDeclaredType methodReceiver = getCurrentMethodReceiver(tree);
if (shouldTakeFromReceiver(methodReceiver)) {
// TODO: this only takes the main annotations.
// What about other annotations (annotations on the type argument, outer types, ...)
type.clearAnnotations();
type.addAnnotations(methodReceiver.getAnnotations());
}
return type;
}
// Determine whether we should take annotations from the given method receiver.
private boolean shouldTakeFromReceiver(AnnotatedDeclaredType methodReceiver) {
return methodReceiver != null;
}
/**
* Determine whether the tree dereferences the most enclosing "this" object.
* That is, we have an expression like "f.g" and want to know whether it is
* an access "this.f.g" or whether e.g. f is a field of an outer class or
* e.g. f is a local variable.
*
* @param tree the tree to check
* @return true, iff the tree is an explicit or implicit reference to the
* most enclosing "this"
*/
public final boolean isMostEnclosingThisDeref(ExpressionTree tree) {
if (!isAnyEnclosingThisDeref(tree)) {
return false;
}
Element element = TreeUtils.elementFromUse(tree);
TypeElement typeElt = ElementUtils.enclosingClass(element);
ClassTree enclosingClass = getCurrentClassTree(tree);
if (enclosingClass != null && isSubtype(TreeUtils.elementFromDeclaration(enclosingClass), typeElt)) {
return true;
}
// ran out of options
return false;
}
/**
* Does this expression have (the innermost or an outer) "this" as receiver?
* Note that the receiver can be either explicit or implicit.
*
* @param tree the tree to test
* @return true, iff the expression uses (the innermost or an outer) "this" as receiver
*/
public final boolean isAnyEnclosingThisDeref(ExpressionTree tree) {
if (!TreeUtils.isUseOfElement(tree)) {
return false;
}
ExpressionTree recv = TreeUtils.getReceiverTree(tree);
if (recv == null) {
Element element = TreeUtils.elementFromUse(tree);
if (!ElementUtils.hasReceiver(element)) {
return false;
}
tree = TreeUtils.skipParens(tree);
if (tree.getKind() == Tree.Kind.IDENTIFIER) {
Name n = ((IdentifierTree)tree).getName();
if ("this".contentEquals(n) ||
"super".contentEquals(n)) {
// An explicit reference to "this"/"super" has no receiver.
return false;
}
}
// Must be some access through this.
return true;
} else if (!TreeUtils.isUseOfElement(recv)) {
// The receiver is e.g. a String literal.
return false;
// TODO: I think this:
// (i==9 ? this : this).toString();
// is not a use of an element, as the receiver is an
// expression. How should this be handled?
}
Element element = TreeUtils.elementFromUse(recv);
if (!ElementUtils.hasReceiver(element)) {
return false;
}
return TreeUtils.isExplicitThisDereference(recv);
}
/**
* 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).
*
* The parameter is an arbitrary tree and does not have to mention "this",
* neither explicitly nor implicitly.
* This method should be overridden for type-system specific behavior.
*
* TODO: in 1.8.2, handle all receiver type annotations.
* TODO: handle enclosing classes correctly.
*/
public AnnotatedDeclaredType getSelfType(Tree tree) {
TreePath path = getPath(tree);
ClassTree enclosingClass = TreeUtils.enclosingClass(path);
if (enclosingClass == null) {
// I hope this only happens when tree is a fake tree that
// we created, e.g. when desugaring enhanced-for-loops.
enclosingClass = getCurrentClassTree(tree);
}
AnnotatedDeclaredType type = getAnnotatedType(enclosingClass);
MethodTree enclosingMethod = TreeUtils.enclosingMethod(path);
if (enclosingClass.getSimpleName().length() != 0 &&
enclosingMethod != null) {
AnnotatedDeclaredType methodReceiver;
if (TreeUtils.isConstructor(enclosingMethod)) {
methodReceiver = (AnnotatedDeclaredType) getAnnotatedType(enclosingMethod).getReturnType();
} else {
methodReceiver = getAnnotatedType(enclosingMethod).getReceiverType();
}
if (shouldTakeFromReceiver(methodReceiver)) {
// TODO what about all annotations on the receiver?
// Code is also duplicated above.
type.clearAnnotations();
type.addAnnotations(methodReceiver.getAnnotations());
}
}
return type;
}
/**
* Determine the type of the most enclosing class of the given tree that
* is a subtype of the given element. Receiver type annotations of an
* enclosing method are considered, similarly return type annotations of an
* enclosing constructor.
*/
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)) {
// Static methods should use type of enclosing class,
// by simply taking another turn in the loop.
} else if (method.getKind() == ElementKind.CONSTRUCTOR) {
// return getSelfType(this.declarationFromElement(method));
return (AnnotatedDeclaredType) getAnnotatedType(method).getReturnType();
} else {
return getAnnotatedType(method).getReceiverType();
}
}
} else if (enclosingElt instanceof TypeElement) {
if (isSubtype((TypeElement)enclosingElt, element)) {
return (AnnotatedDeclaredType) getAnnotatedType(enclosingElt);
}
}
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:
*
* - Array Access
*
- Identifiers (whose receivers are usually self type)
*
- Method Invocation Trees
*
- Member Select Trees
*
*
* @param expression the expression for which to determine the receiver type
* @return the type of the receiver of this expression
*/
public final AnnotatedTypeMirror getReceiverType(ExpressionTree expression) {
if (this.isAnyEnclosingThisDeref(expression)) {
return getImplicitReceiverType(expression);
}
ExpressionTree receiver = TreeUtils.getReceiverTree(expression);
if (receiver != null) {
return getAnnotatedType(receiver);
} else {
// E.g. local variables
return null;
}
}
/**
* 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(Types, AnnotatedTypeFactory, AnnotatedTypeMirror, Element)},
* and customization based on receiver type should be in accordance to its
* specification.
*
* The return type is a pair of the type of the invoked method and
* the (inferred) type arguments.
* Note that neither the explicitly passed nor the inferred type arguments
* are guaranteed to be subtypes of the corresponding upper bounds.
* See method
* {@link org.checkerframework.common.basetype.BaseTypeVisitor#checkTypeArguments(Tree, List, List, List)}
* for the checks of type argument well-formedness.
*
* Note that "this" and "super" constructor invocations are also handled by this
* method. Method {@link #constructorFromUse(NewClassTree)} is only used for a constructor
* invocation in a "new" expression.
*
* @param tree the method invocation tree
* @return the method type being invoked with tree and the (inferred) type arguments
*/
public Pair> methodFromUse(MethodInvocationTree tree) {
ExecutableElement methodElt = TreeUtils.elementFromUse(tree);
AnnotatedTypeMirror receiverType = getReceiverType(tree);
Pair> mfuPair = methodFromUse(tree, methodElt, receiverType);
if (checker.shouldResolveReflection() && reflectionResolver.isReflectiveMethodInvocation(tree)) {
mfuPair = reflectionResolver.resolveReflectiveCall(this, tree, mfuPair);
}
return mfuPair;
}
public Pair> methodFromUse(ExpressionTree tree,
ExecutableElement methodElt, AnnotatedTypeMirror receiverType) {
AnnotatedExecutableType methodType = AnnotatedTypes.asMemberOf(types, this, receiverType, methodElt);
List typeargs = new LinkedList();
Map typeVarMapping =
AnnotatedTypes.findTypeArguments(processingEnv, this, tree, methodElt, methodType);
if (!typeVarMapping.isEmpty()) {
for (AnnotatedTypeVariable tv : methodType.getTypeVariables()) {
if (typeVarMapping.get(tv.getUnderlyingType()) == null) {
ErrorReporter.errorAbort("AnnotatedTypeFactory.methodFromUse:" +
"mismatch between declared method type variables and the inferred method type arguments! " +
"Method type variables: " + methodType.getTypeVariables() + "; " +
"Inferred method type arguments: " + typeVarMapping);
}
typeargs.add(typeVarMapping.get(tv.getUnderlyingType()));
}
methodType = (AnnotatedExecutableType) typeVarSubstitutor.substitute(typeVarMapping, methodType);
}
if (tree.getKind() == Tree.Kind.METHOD_INVOCATION &&
TreeUtils.isGetClassInvocation((MethodInvocationTree) tree)) {
adaptGetClassReturnTypeToReceiver(methodType, receiverType);
}
return Pair.of(methodType, typeargs);
}
/**
* Java special cases the return type of getClass. Though the method has a return type of {@code Class},
* the compiler special cases this return type and changes the bound of the type argument to the
* erasure of the receiver type. e.g.,
*
* x.getClass() has the type {@code Class< ? extends erasure_of_x >}
* someInteger.getClass() has the type {@code Class< ? extends Integer >}
*
* @param getClassType this must be a type representing a call to Object.getClass otherwise
* a runtime exception will be thrown
* @param receiverType the receiver type of the method invocation (not the declared receiver type)
*/
protected static void adaptGetClassReturnTypeToReceiver(final AnnotatedExecutableType getClassType,
final AnnotatedTypeMirror receiverType) {
final AnnotatedTypeMirror newBound = receiverType.getErased();
final AnnotatedTypeMirror returnType = getClassType.getReturnType();
if ( returnType == null || !(returnType.getKind() == TypeKind.DECLARED)
|| ((AnnotatedDeclaredType) returnType).getTypeArguments().size() != 1 ) {
ErrorReporter.errorAbort(
"Unexpected type passed to AnnotatedTypes.adaptGetClassReturnTypeToReceiver\n"
+ "getClassType=" + getClassType + "\n"
+ "receiverType=" + receiverType);
}
final AnnotatedDeclaredType returnAdt = (AnnotatedDeclaredType) getClassType.getReturnType();
final List typeArgs = returnAdt.getTypeArguments();
// usually, the only locations that will add annotations to the return type are getClass in stub files
// defaults and propagation tree annotator. Since getClass is final they cannot come from source code.
// Also, since the newBound is an erased type we have no type arguments. So, we just copy the annotations
// from the bound of the declared type to the new bound.
final AnnotatedWildcardType classWildcardArg = (AnnotatedWildcardType) typeArgs.get(0);
newBound.replaceAnnotations(classWildcardArg.getExtendsBound().getAnnotations());
classWildcardArg.setExtendsBound(newBound);
}
/**
* Determines the type of the invoked constructor based on the passed new
* class tree.
*
* The returned method type has all type variables resolved, whether based
* on receiver type, passed type parameters if any, and constructor invocation
* parameter.
*
* Subclasses may override this method to customize inference of types
* or qualifiers based on constructor invocation parameters.
*
* As an implementation detail, this method depends on
* {@link AnnotatedTypes#asMemberOf(Types, AnnotatedTypeFactory, AnnotatedTypeMirror, Element)},
* and customization based on receiver type should be in accordance with its
* specification.
*
* The return type is a pair of the type of the invoked constructor and
* the (inferred) type arguments.
* Note that neither the explicitly passed nor the inferred type arguments
* are guaranteed to be subtypes of the corresponding upper bounds.
* See method
* {@link org.checkerframework.common.basetype.BaseTypeVisitor#checkTypeArguments(Tree, List, List, List)}
* for the checks of type argument well-formedness.
*
* Note that "this" and "super" constructor invocations are handled by
* method {@link #methodFromUse}. This method only handles constructor invocations
* in a "new" expression.
*
* @param tree the constructor invocation tree
* @return the annotated type of the invoked constructor (as an executable
* type) and the (inferred) type arguments
*/
public Pair> constructorFromUse(NewClassTree tree) {
ExecutableElement ctor = InternalUtils.constructor(tree);
AnnotatedTypeMirror type = fromNewClass(tree);
addComputedTypeAnnotations(tree.getIdentifier(), type);
AnnotatedExecutableType con = AnnotatedTypes.asMemberOf(types, this, type, ctor);
if (tree.getArguments().size() == con.getParameterTypes().size() + 1
&& isSyntheticArgument(tree.getArguments().get(0))) {
// happens for anonymous constructors of inner classes
List actualParams = new ArrayList();
actualParams.add(getAnnotatedType(tree.getArguments().get(0)));
actualParams.addAll(con.getParameterTypes());
con.setParameterTypes(actualParams);
}
List typeargs = new LinkedList();
Map typeVarMapping =
AnnotatedTypes.findTypeArguments(processingEnv, this, tree, ctor, con);
if (!typeVarMapping.isEmpty()) {
for (AnnotatedTypeVariable tv : con.getTypeVariables()) {
typeargs.add(typeVarMapping.get(tv.getUnderlyingType()));
}
con = (AnnotatedExecutableType) typeVarSubstitutor.substitute(typeVarMapping, con);
}
return Pair.of(con, typeargs);
}
/**
* Returns the return type of the method {@code m}.
*/
public AnnotatedTypeMirror getMethodReturnType(MethodTree m) {
AnnotatedExecutableType methodType = getAnnotatedType(m);
AnnotatedTypeMirror ret = methodType.getReturnType();
return ret;
}
/**
* Returns the return type of the method {@code m} at the return statement {@code r}.
*/
public AnnotatedTypeMirror getMethodReturnType(MethodTree m, ReturnTree r) {
AnnotatedExecutableType methodType = getAnnotatedType(m);
AnnotatedTypeMirror ret = methodType.getReturnType();
return ret;
}
private boolean isSyntheticArgument(Tree tree) {
return tree.toString().contains("<*nullchk*>");
}
/**
* Creates an AnnotatedDeclaredType for a NewClassTree. Adds explicit annotations
* and annotations inherited from class declarations {@link #annotateInheritedFromClass(AnnotatedTypeMirror)}.
*
* If the NewClassTree has type arguments, then any explicit (or inherited from class) annotations
* on those type arguments are included. If the NewClassTree has a diamond operator, then
* the annotations on the type arguments are inferred using the assignment context.
*
* @param newClassTree NewClassTree
* @return AnnotatedDeclaredType
*/
public final AnnotatedDeclaredType fromNewClass(NewClassTree newClassTree) {
if (TreeUtils.isDiamondTree(newClassTree)) {
AnnotatedDeclaredType type = (AnnotatedDeclaredType)
toAnnotatedType(InternalUtils.typeOf(newClassTree), false);
if (((com.sun.tools.javac.code.Type)type.actualType).tsym.getTypeParameters().nonEmpty()) {
Pair ctx = this.visitorState.getAssignmentContext();
if (ctx != null) {
AnnotatedTypeMirror ctxtype = ctx.second;
fromNewClassContextHelper(type, ctxtype);
}
}
return type;
} else if (newClassTree.getClassBody() != null) {
AnnotatedDeclaredType type =
(AnnotatedDeclaredType) toAnnotatedType(InternalUtils.typeOf(newClassTree), false);
// If newClassTree creates an anonymous class, then annotations in this location:
// new @HERE Class() {}
// are on not on the identifier newClassTree, but rather on the modifier newClassTree.
List annos = newClassTree.getClassBody().getModifiers().getAnnotations();
type.addAnnotations(InternalUtils.annotationsFromTypeAnnotationTrees(annos));
return type;
} else {
// If newClassTree does not create anonymous class,
// newClassTree.getIdentifier includes the explicit annotations in this location:
// new @HERE Class()
return (AnnotatedDeclaredType) fromTypeTree(newClassTree.getIdentifier());
}
}
// This method extracts the ugly hacky parts.
// This method should be rewritten and in particular diamonds should be
// implemented cleanly.
// See Issue 289.
private void fromNewClassContextHelper(AnnotatedDeclaredType type, AnnotatedTypeMirror ctxtype) {
switch (ctxtype.getKind()) {
case DECLARED:
AnnotatedDeclaredType adctx = (AnnotatedDeclaredType) ctxtype;
if (type.getTypeArguments().size() == adctx.getTypeArguments().size()) {
// Try to simply take the type arguments from LHS.
List oldArgs = type.getTypeArguments();
List newArgs = adctx.getTypeArguments();
for (int i = 0; i < type.getTypeArguments().size(); ++i) {
if (!types.isSameType(oldArgs.get(i).actualType, newArgs.get(i).actualType)) {
// One of the underlying types doesn't match. Give up.
return;
}
}
type.setTypeArguments(newArgs);
/* It would be nice to call isSubtype for a basic sanity check.
* However, the type might not have been completely initialized yet,
* so isSubtype might fail.
*
if (!typeHierarchy.isSubtype(type, ctxtype)) {
// Simply taking the newArgs didn't result in a valid subtype.
// Give up and simply use the inferred types.
type.setTypeArguments(oldArgs);
}
*/
} else {
// TODO: Find a way to determine annotated type arguments.
// Look at what Attr and Resolve are doing and rework this whole method.
}
break;
case ARRAY:
// This new class is in the initializer of an array.
// The array being created can't have a generic component type,
// so nothing to be done.
break;
case TYPEVAR:
// TODO: this should NOT be necessary.
// org.checkerframework.dataflow.cfg.node.MethodAccessNode.MethodAccessNode(ExpressionTree, Node)
// Uses an ExecutableElement, which did not substitute type variables.
break;
default:
if (ctxtype.getKind().isPrimitive()) {
// See Issue 438. Ignore primitive types for diamond inference - a primitive type
// is never a suitable context anyways.
} else {
ErrorReporter.errorAbort("AnnotatedTypeFactory.fromNewClassContextHelper: unexpected context: " +
ctxtype + " (" + ctxtype.getKind() + ")");
}
}
}
/**
* 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 behavior.
*
* @param type the primitive 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
* IllegalArgumentException 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 IllegalArgumentException if the type given has no unbox conversion
*/
public AnnotatedPrimitiveType getUnboxedType(AnnotatedDeclaredType type)
throws IllegalArgumentException {
PrimitiveType primitiveType =
types.unboxedType(type.getUnderlyingType());
AnnotatedPrimitiveType pt = (AnnotatedPrimitiveType)
AnnotatedTypeMirror.createType(primitiveType, this, false);
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);
}
/**
* @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 #fromElement(Element)
*/
public final AnnotatedDeclaredType fromElement(TypeElement elt) {
return (AnnotatedDeclaredType)fromElement((Element)elt);
}
/**
* @see #fromElement(Element)
*/
public final AnnotatedExecutableType fromElement(ExecutableElement elt) {
return (AnnotatedExecutableType)fromElement((Element)elt);
}
// **********************************************************************
// Helper methods for this classes
// **********************************************************************
/**
* Determines whether the given annotation is a part of the type system
* under which this type factory operates.
* Null is never a supported qualifier; the parameter is nullable to
* allow the result of aliasedAnnotation to be passed in directly.
*
* @param a any annotation
* @return true if that annotation is part of the type system under which
* this type factory operates, false otherwise
*/
public boolean isSupportedQualifier(/*@Nullable*/ AnnotationMirror a) {
if (a == null) return false;
return AnnotationUtils.containsSameIgnoringValues(this.getQualifierHierarchy().getTypeQualifiers(), a);
}
/** Add the annotation clazz as an alias for the annotation type. */
protected void addAliasedAnnotation(Class alias, AnnotationMirror type) {
aliases.put(alias.getCanonicalName(), type);
}
/**
* 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
*
* @param a the qualifier to check for an alias
* @return the alias or null if none exists
*/
public /*@Nullable*/ AnnotationMirror aliasedAnnotation(AnnotationMirror a) {
TypeElement elem = (TypeElement) a.getAnnotationType().asElement();
String qualName = elem.getQualifiedName().toString();
return aliases.get(qualName);
}
/**
* Add the annotation {@code alias} as an alias for the declaration
* annotation {@code annotation}, where the annotation mirror
* {@code annoationToUse} will be used instead. If multiple calls are made
* with the same {@code annotation}, then the {@code anontationToUse} must
* be the same.
*/
protected void addAliasedDeclAnnotation(Class alias,
Class annotation,
AnnotationMirror annotationToUse) {
String aliasName = alias.getCanonicalName();
/*@Interned*/ String annotationName = annotation.getCanonicalName();
Set set = new HashSet<>();
if (declAliases.containsKey(annotationName)) {
set.addAll(declAliases.get(annotationName).second);
}
set.add(aliasName.intern());
declAliases.put(annotationName, Pair.of(annotationToUse, set));
}
/**
* Adds the annotation {@code annotation} in the set of declaration
* annotations that should be inherited. A declaration annotation
* will be inherited if it is in this list, or if it has the
* meta-annotation @InheritedAnnotation.
* The meta-annotation @InheritedAnnotation should be used instead of this
* method, if possible.
*/
protected void addInheritedAnnotation(AnnotationMirror annotation) {
inheritedAnnotations.add(annotation);
}
/**
* A convenience method that converts a {@link TypeMirror} to an empty {@link
* AnnotatedTypeMirror} using {@link AnnotatedTypeMirror#createType}.
*
* @param t the {@link TypeMirror}
* @param declaration true if the result should be marked as a type declaration
* @return an {@link AnnotatedTypeMirror} that has {@code t} as its
* underlying type
*/
protected final AnnotatedTypeMirror toAnnotatedType(TypeMirror t, boolean declaration) {
return AnnotatedTypeMirror.createType(t, this, declaration);
}
/**
* 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.
*
* Most users will want to use getAnnotatedType instead; this method
* is mostly for internal use.
*
* @param node the tree to analyze
* @return the type of {@code node}, without any annotations
*/
protected final AnnotatedTypeMirror type(Tree node) {
boolean isDeclaration = TreeUtils.isTypeDeclaration(node);
// Attempt to obtain the type via JCTree.
if (InternalUtils.typeOf(node) != null) {
AnnotatedTypeMirror result = toAnnotatedType(InternalUtils.typeOf(node), isDeclaration);
return result;
}
// Attempt to obtain the type via TreePath (slower).
TreePath path = this.getPath(node);
assert path != null : "No path or type in tree: " + node;
TypeMirror t = trees.getTypeMirror(path);
assert validType(t) : "Invalid type " + t + " for node " + t;
return toAnnotatedType(t, isDeclaration);
}
/**
* Gets the declaration tree for the element, if the source is available.
*
* TODO: would be nice to move this to InternalUtils/TreeUtils.
*
* @param elt an element
* @return the tree declaration of the element if found
*/
public final Tree declarationFromElement(Element elt) {
// if root is null, we cannot find any declaration
if (root == null) {
return null;
}
if (shouldCache && elementToTreeCache.containsKey(elt)) {
return elementToTreeCache.get(elt);
}
// Check for new declarations, outside of the AST.
if (elt instanceof DetachedVarSymbol) {
return ((DetachedVarSymbol) elt).getDeclaration();
}
// 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 = com.sun.tools.javac.tree.TreeInfo.declarationFor((com.sun.tools.javac.code.Symbol) elt,
(com.sun.tools.javac.tree.JCTree) root);
break;
}
if (shouldCache) {
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 ClassTree getCurrentClassTree(Tree tree) {
if (visitorState.getClassTree() != null) {
return visitorState.getClassTree();
}
return TreeUtils.enclosingClass(getPath(tree));
}
protected final AnnotatedDeclaredType getCurrentClassType(Tree tree) {
return getAnnotatedType(getCurrentClassTree(tree));
}
/**
* Returns the receiver type of the current method being visited, and
* returns null if the visited tree is not within a method or if that
* method has no receiver (e.g. a static 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 /*@Nullable*/ AnnotatedDeclaredType getCurrentMethodReceiver(Tree tree) {
AnnotatedDeclaredType res = visitorState.getMethodReceiver();
if (res == null) {
TreePath path = getPath(tree);
if (path != null) {
MethodTree enclosingMethod = TreeUtils.enclosingMethod(path);
ClassTree enclosingClass = TreeUtils.enclosingClass(path);
boolean found = false;
for (Tree member : enclosingClass.getMembers()) {
if (member.getKind() == Tree.Kind.METHOD) {
if (member == enclosingMethod) {
found = true;
}
}
}
if (found && enclosingMethod != null) {
AnnotatedExecutableType method = getAnnotatedType(enclosingMethod);
res = method.getReceiverType();
// TODO: three tests fail if one adds the following, which would make
// sense, or not?
// visitorState.setMethodReceiver(res);
} else {
// We are within an anonymous class or field initializer
res = this.getAnnotatedType(enclosingClass);
}
}
}
return res;
}
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);
if (path == null) {
ErrorReporter.errorAbort(String.format("AnnotatedTypeFactory.getMostInnerClassOrMethod: getPath(tree)=>null%n TreePath.getPath(root, tree)=>%s\n for tree (%s) = %s%n root=%s",
TreePath.getPath(root, tree), tree.getClass(), tree, root));
return null; // dead code
}
for (Tree pathTree : path) {
if (pathTree instanceof MethodTree) {
return TreeUtils.elementFromDeclaration((MethodTree)pathTree);
} else if (pathTree instanceof ClassTree) {
return TreeUtils.elementFromDeclaration((ClassTree)pathTree);
}
}
ErrorReporter.errorAbort("AnnotatedTypeFactory.getMostInnerClassOrMethod: cannot be here!");
return null; // dead code
}
private final Map pathHack = new HashMap<>();
public final void setPathHack(Tree node, Element enclosing) {
pathHack.put(node, enclosing);
}
/**
* 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.
*
* Note that the given Tree has to be within the current compilation unit,
* otherwise null will be returned.
*
* @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 : "AnnotatedTypeFactory.getPath: root needs to be set when used on trees; factory: " + this.getClass();
if (node == null) return null;
if (treePathCache.isCached(node)) {
return treePathCache.getPath(root, node);
};
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. Use the cache to look up.
return treePathCache.getPath(root, node);
}
/**
* Gets the {@link Element} representing the declaration of the
* method enclosing a tree node. This feature is used to record
* the enclosing methods of {@link Tree}s that are created
* internally by the checker.
*
* TODO: Find a better way to store information about enclosing
* Trees.
*
* @param node the {@link Tree} to get the enclosing method for
* @return the method {@link Element} enclosing the argument, or
* null if none has been recorded
*/
public final Element getEnclosingMethod(Tree node) {
return pathHack.get(node);
}
/**
* 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.
*
* @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;
default:
return true;
}
}
/**
* Parses the stub files in the following order:
* 1. jdk.astub in the same directory as the checker, if it exists and ignorejdkastub option is not supplied
* 2. flow.astub in the same directory as BaseTypeChecker
* 3. Stub files listed in @Stubfiles annotation on the checker; must be in same directory as the checker
* 4. Stub files provide via stubs system property
* 5. Stub files provide via stubs environment variable
* 6. Stub files provide via stubs compiler option
*
* If a type is annotated with a qualifier from the same hierarchy in more than one stub file, the qualifier
* in the last stub file is applied.
*
* Sets typesFromStubFiles and declAnnosFromStubFiles by side effect, just before returning.
*/
protected void parseStubFiles() {
if (this.typesFromStubFiles != null || this.declAnnosFromStubFiles != null) {
ErrorReporter.errorAbort("AnnotatedTypeFactory.parseStubFiles called more than once");
}
Map typesFromStubFiles
= new HashMap();
Map> declAnnosFromStubFiles
= new HashMap>();
// 1. jdk.astub
if (!checker.hasOption("ignorejdkastub")) {
InputStream in = null;
in = checker.getClass().getResourceAsStream("jdk.astub");
if (in != null) {
StubParser stubParser = new StubParser("jdk.astub", in, this, processingEnv);
stubParser.parse(typesFromStubFiles, declAnnosFromStubFiles);
}
}
// 2. flow.astub
// stub file for type-system independent annotations
InputStream input = BaseTypeChecker.class.getResourceAsStream("flow.astub");
if (input != null) {
StubParser stubParser = new StubParser("flow.astub", input, this, processingEnv);
stubParser.parse(typesFromStubFiles, declAnnosFromStubFiles);
}
// Stub files specified via stubs compiler option, stubs system property,
// stubs env. variable, or @Stubfiles
List allStubFiles = new ArrayList<>();
// 3. Stub files listed in @Stubfiles annotation on the checker
StubFiles stubFilesAnnotation = checker.getClass().getAnnotation(StubFiles.class);
if (stubFilesAnnotation != null) {
Collections.addAll(allStubFiles, stubFilesAnnotation.value());
}
// 4. Stub files provide via stubs system property
String stubsProperty = System.getProperty("stubs");
if (stubsProperty != null) {
Collections.addAll(allStubFiles, stubsProperty.split(File.pathSeparator));
}
// 5. Stub files provide via stubs environment variable
String stubEnvVar = System.getenv("stubs");
if (stubEnvVar != null) {
Collections.addAll(allStubFiles, stubEnvVar.split(File.pathSeparator));
}
// 6. Stub files provide via stubs option
String stubsOption = checker.getOption("stubs");
if (stubsOption != null) {
Collections.addAll(allStubFiles, stubsOption.split(File.pathSeparator));
}
if (allStubFiles.isEmpty()) {
this.typesFromStubFiles = typesFromStubFiles;
this.declAnnosFromStubFiles = declAnnosFromStubFiles;
return;
}
// Parse stub files specified via stubs compiler option, stubs system property,
// stubs env. variable, or @Stubfiles
for (String stubPath : allStubFiles) {
if (stubPath == null || stubPath.isEmpty()) {
continue;
}
// Handle case when running in jtreg
String base = System.getProperty("test.src");
String stubPathFull = stubPath;
if (base != null) {
stubPathFull = base + "/" + stubPath;
}
List stubs = StubUtil.allStubFiles(stubPathFull);
if (stubs.size() == 0) {
InputStream in = null;
in = checker.getClass().getResourceAsStream(stubPath);
if (in != null) {
StubParser stubParser = new StubParser(stubPath, in, this, processingEnv);
stubParser.parse(typesFromStubFiles, declAnnosFromStubFiles);
// We could handle the stubPath -> continue.
continue;
}
// We couldn't handle the stubPath -> error message.
checker.message(Kind.NOTE,
"Did not find stub file or files within directory: " + stubPath + " " + new File(stubPath).getAbsolutePath());
}
for (StubResource resource : stubs) {
InputStream stubStream;
try {
stubStream = resource.getInputStream();
} catch (IOException e) {
checker.message(Kind.NOTE,
"Could not read stub resource: " + resource.getDescription());
continue;
}
StubParser stubParser = new StubParser(resource.getDescription(), stubStream, this, processingEnv);
stubParser.parse(typesFromStubFiles, declAnnosFromStubFiles);
}
}
this.typesFromStubFiles = typesFromStubFiles;
this.declAnnosFromStubFiles = declAnnosFromStubFiles;
}
/**
* Returns the actual annotation mirror used to annotate this element,
* whose name equals the passed annotation class, if one exists, or null otherwise.
*
* @see #getDeclAnnotationNoAliases
*
* @param elt the element to retrieve the declaration annotation from
* @param anno annotation class
* @return the annotation mirror for anno
*/
@Override
public AnnotationMirror getDeclAnnotation(Element elt,
Class anno) {
String annoName = anno.getCanonicalName().intern();
return getDeclAnnotation(elt, annoName, true);
}
/**
* Returns the actual annotation mirror used to annotate this element,
* whose name equals the passed annotation class, if one exists, or null otherwise.
* Does not check for aliases of the annotation class.
*
*
* Call this method from a checker that needs to alias annotations for one purpose
* and not for another. For example, in the Lock Checker, {@code @LockingFree} and
* {@code @ReleasesNoLocks} are both aliases of {@code @SideEffectFree} since they are
* all considered side-effect-free with regard to the set of locks held before
* and after the method call. However, a {@code synchronized} block is permitted inside
* a {@code @ReleasesNoLocks} method but not inside a {@code @LockingFree} or
* {@code @SideEffectFree} method.
*
* @see #getDeclAnnotation
*
* @param elt the element to retrieve the declaration annotation from
* @param anno annotation class
* @return the annotation mirror for anno
*/
public AnnotationMirror getDeclAnnotationNoAliases(Element elt,
Class anno) {
String annoName = anno.getCanonicalName().intern();
return getDeclAnnotation(elt, annoName, false);
}
/**
* Returns true if the element appears in a stub file
* (Currently only works for methods, constructors, and fields)
*/
public boolean isFromStubFile(Element element) {
return this.getDeclAnnotation(element, FromStubFile.class) != null;
}
/**
* Returns true if the element is from byte code
* and the if the element did not appear in a stub file
* (Currently only works for methods, constructors, and fields)
*/
public boolean isFromByteCode(Element element) {
if (isFromStubFile(element)) return false;
return this.getDeclAnnotation(element, FromByteCode.class) != null;
}
/**
* Returns the actual annotation mirror used to annotate this type, whose
* name equals the passed annotationName if one exists, null otherwise. This
* is the private implementation of the same-named, public method.
*
* An option is provided to not to check for aliases of annotations.
* For example, an annotated type factory may use aliasing for a pair of
* annotations for convenience while needing in some cases to determine
* a strict ordering between them, such as when determining whether
* the annotations on an overrider method are more specific than the
* annotations of an overridden method.
*
* @param elt the element to retrieve the annotation from
* @param annoName the class name of the annotation to retrieve
* @param checkAliases whether to return an annotation mirror for an alias of the requested annotation class name
* @return the annotation mirror for the requested annotation or null if not found
*/
private AnnotationMirror getDeclAnnotation(Element elt,
/*@Interned*/ String annoName, boolean checkAliases) {
Set declAnnos = getDeclAnnotations(elt);
for (AnnotationMirror am : declAnnos) {
if (AnnotationUtils.areSameByName(am, annoName)) {
return am;
}
}
// Look through aliases.
if (checkAliases) {
Pair> aliases =
declAliases.get(annoName);
if (aliases != null) {
for (String alias : aliases.second) {
AnnotationMirror declAnnotation = getDeclAnnotation(elt,
alias, false);
if (declAnnotation != null) {
return aliases.first;
}
}
}
}
// Not found.
return null;
}
/**
* Returns all of the actual annotation mirrors used to annotate this element
* (includes stub files and declaration annotations from overridden methods).
*
* @param elt
* The element for which to determine annotations.
*/
public Set getDeclAnnotations(Element elt) {
if (cacheDeclAnnos.containsKey(elt)) {
// Found in cache, return result.
return cacheDeclAnnos.get(elt);
}
Set results = AnnotationUtils.createAnnotationSet();
// Retrieving the annotations from the element.
results.addAll(elt.getAnnotationMirrors());
// If declAnnosFromStubFiles == null, return the annotations in the element.
if (declAnnosFromStubFiles != null) {
// Adding @FromByteCode annotation to declAnnosFromStubFiles entry with key
// elt, if elt is from bytecode.
addFromByteCode(elt);
// Retrieving annotations from stub files.
String eltName = ElementUtils.getVerboseName(elt);
Set stubAnnos = declAnnosFromStubFiles.get(eltName);
if (stubAnnos != null) {
results.addAll(stubAnnos);
}
if (elt.getKind() == ElementKind.METHOD) {
// Retrieve the annotations from the overridden method's element.
inheritOverriddenDeclAnnos((ExecutableElement) elt, results);
}
// Add the element and its annotations to the cache.
cacheDeclAnnos.put(elt, results);
}
return results;
}
/**
* Adds into {@code results} the declaration annotations found in all
* elements that the method element {@code elt} overrides.
*
* @param elt
* Method element.
* @param results
* {@code elt} local declaration annotations. The ones found
* in stub files and in the element itself.
*/
private void inheritOverriddenDeclAnnos(ExecutableElement elt,
Set results) {
Map overriddenMethods =
AnnotatedTypes.overriddenMethods(elements, this, elt);
if (overriddenMethods != null) {
for ( ExecutableElement superElt : overriddenMethods.values()) {
Set superAnnos = getDeclAnnotations(superElt);
for (AnnotationMirror annotation : superAnnos) {
List annotationsOnAnnotation;
try {
annotationsOnAnnotation =
annotation.getAnnotationType().asElement().getAnnotationMirrors();
} catch (com.sun.tools.javac.code.Symbol.CompletionFailure cf) {
// Fix for Issue 348: If a CompletionFailure occurs,
// issue a warning.
checker.message(Kind.WARNING, annotation.getAnnotationType().asElement(),
"annotation.not.completed", ElementUtils
.getVerboseName(elt), annotation);
continue;
}
if (AnnotationUtils.containsSameByClass(
annotationsOnAnnotation, InheritedAnnotation.class)
|| AnnotationUtils.containsSameIgnoringValues(
inheritedAnnotations, annotation)) {
addOrMerge(results, annotation);
}
}
}
}
}
private void addOrMerge(Set results,
AnnotationMirror annotation) {
if (AnnotationUtils.containsSameIgnoringValues(results, annotation)) {
/*
* TODO: feature request: figure out a way to merge multiple annotations
* of the same kind. For some annotations this might mean merging some
* arrays, for others it might mean converting a single annotation into a
* container annotation. We should define a protected method for subclasses
* to adapt the behavior.
* For now, do nothing and just take the first, most concrete, annotation.
AnnotationMirror prev = null;
for (AnnotationMirror an : results) {
if (AnnotationUtils.areSameIgnoringValues(an, annotation)) {
prev = an;
break;
}
}
results.remove(prev);
AnnotationMirror merged = ...;
results.add(merged);
*/
} else {
results.add(annotation);
}
}
/**
* Returns a list of all declaration annotations used to annotate this element,
* which have a meta-annotation (i.e., an annotation on that annotation)
* with class {@code metaAnnotation}.
*
* @param element
* The element for which to determine annotations.
* @param metaAnnotation
* The meta-annotation that needs to be present.
* @return a list of pairs {@code (anno, metaAnno)} where {@code anno} is
* the annotation mirror at {@code element}, and {@code metaAnno} is
* the annotation mirror used to annotate {@code anno}.
*/
public List> getDeclAnnotationWithMetaAnnotation(
Element element, Class metaAnnotation) {
List> result = new ArrayList<>();
Set annotationMirrors = getDeclAnnotations(element);
// Go through all annotations found.
for (AnnotationMirror annotation : annotationMirrors) {
List annotationsOnAnnotation;
try {
annotationsOnAnnotation = annotation.getAnnotationType().asElement().getAnnotationMirrors();
} catch (com.sun.tools.javac.code.Symbol.CompletionFailure cf) {
// Fix for Issue 309: If a CompletionFailure occurs, issue a warning.
// I didn't find a nicer alternative to check whether the Symbol can be completed.
// The completer field of a Symbol might be non-null also in successful cases.
// Issue a warning (exception only happens once) and continue.
checker.message(Kind.WARNING, annotation.getAnnotationType().asElement(),
"annotation.not.completed", ElementUtils.getVerboseName(element), annotation);
continue;
}
// First call copier, if exception, continue normal modula laws.
for (AnnotationMirror a : annotationsOnAnnotation) {
if (AnnotationUtils.areSameByClass(a, metaAnnotation)) {
result.add(Pair.of(annotation, a));
}
}
}
return result;
}
/**
* Returns a list of all annotations used to annotate this element,
* which have a meta-annotation (i.e., an annotation on that annotation)
* with class {@code metaAnnotation}.
*
* @param element
* The element at which to look for annotations.
* @param metaAnnotation
* The meta-annotation that needs to be present.
* @return a list of pairs {@code (anno, metaAnno)} where {@code anno} is
* the annotation mirror at {@code element}, and {@code metaAnno} is
* the annotation mirror used to annotate {@code anno}.
*/
public List> getAnnotationWithMetaAnnotation(
Element element, Class metaAnnotation) {
List> result = new ArrayList<>();
Set annotationMirrors = AnnotationUtils.createAnnotationSet();
// Consider real annotations.
annotationMirrors.addAll(getAnnotatedType(element).getAnnotations());
// Consider declaration annotations
annotationMirrors.addAll(getDeclAnnotations(element));
// Go through all annotations found.
for (AnnotationMirror annotation : annotationMirrors) {
List annotationsOnAnnotation = annotation
.getAnnotationType().asElement().getAnnotationMirrors();
for (AnnotationMirror a : annotationsOnAnnotation) {
if (AnnotationUtils.areSameByClass(a, metaAnnotation)) {
result.add(Pair.of(annotation, a));
}
}
}
return result;
}
/**
* This method is a hack to use when a method type argument
* could not be inferred automatically or if a raw type is used.
* The only use should be:
* org.checkerframework.framework.util.AnnotatedTypes.inferTypeArguments(ProcessingEnvironment, AnnotatedTypeFactory, ExpressionTree, ExecutableElement)
* org.checkerframework.framework.type.AnnotatedTypeFactory.fromTypeTree(Tree)
*/
public AnnotatedWildcardType getUninferredWildcardType(AnnotatedTypeVariable typeVar) {
final boolean intersectionType;
final TypeMirror boundType;
if (typeVar.getUpperBound().getKind() == TypeKind.INTERSECTION) {
boundType =
typeVar.getUpperBound()
.directSuperTypes()
.get(0)
.getUnderlyingType();
intersectionType = true;
} else {
boundType = typeVar.getUnderlyingType().getUpperBound();
intersectionType = false;
}
WildcardType wc = types.getWildcardType(boundType, null);
AnnotatedWildcardType wctype = (AnnotatedWildcardType) AnnotatedTypeMirror.createType(wc, this, false);
if (!intersectionType) {
wctype.setExtendsBound(typeVar.getUpperBound().deepCopy());
} else {
//TODO: This probably doesn't work if the type has a type argument
wctype.getExtendsBound().addAnnotations(typeVar.getUpperBound().getAnnotations());
}
wctype.setSuperBound(typeVar.getLowerBound().deepCopy());
wctype.addAnnotations(typeVar.getAnnotations());
wctype.setTypeArgHack();
return wctype;
}
/**
* If {@code wildcard}'s upper bound is a super type of {@code annotatedTypeMirror},
* this method returns an AnnotatedTypeMirror with the same qualfiers as {@code annotatedTypeMirror}, but
* the underlying Java type is the the most specific base type of {@code annotatedTypeMirror} whose erasure type
* is equivalent to the upper bound of {@code wildcard}.
*
* Otherwise, returns {@code annotatedTypeMirror} unmodified.
*
* For example:
*
* wildcard := @NonNull ? extends @NonNull Object
* annotatedTypeMirror := @Nullable String
*
* widenToUpperBound(annotatedTypeMirror, wildcard) returns @Nullable Object
*
* This method is needed because,
* the Java compiler allows wildcards to have upper bounds above the type variable upper bounds
* for which they are type arguments. For example, given the following parametric type:
*
* {@code class MyClass}
*
* the following is legal:
*
* {@code MyClass}
*
* This is sound because in Java the wildcard is capture converted to:
* {@code CAP#1 extends Number from capture of ? extends Object}.
*
* Because the Checker Framework does not implement capture conversion, wildcard upper bounds may
* cause spurious errors in subtyping checks. This method prevents those errors by
* widening the upper bound of the type parameter.
*
* This method widens the underlying Java type of the upper bound of the type parameter rather
* than narrowing the bound of the wildcard in order to avoid issuing an error with an upper
* bound that is not in source code.
*
* The widened type should only be used for typing checks that require it. Using the widened type
* elsewhere would cause confusing error messages with types not in the source code.
*
* @param annotatedTypeMirror AnnotatedTypeMirror to widen
* @param wildcard AnnotatedWildcardType whose upper bound is used to widen
* @return {@code annotatedTypeMirror} widen to the upper bound of {@code wildcard}
*/
public AnnotatedTypeMirror widenToUpperBound(final AnnotatedTypeMirror annotatedTypeMirror,
final AnnotatedWildcardType wildcard) {
/**
*/
final TypeMirror toModifyTypeMirror = annotatedTypeMirror.getUnderlyingType();
final TypeMirror wildcardUpperBoundTypeMirror = wildcard.getExtendsBound().getUnderlyingType();
if (!types.isSubtype(wildcardUpperBoundTypeMirror, toModifyTypeMirror)
&& types.isSubtype(toModifyTypeMirror, wildcardUpperBoundTypeMirror)) {
return AnnotatedTypes.asSuper(types, this, annotatedTypeMirror, wildcard);
}
return annotatedTypeMirror;
}
public Pair getFnInterfaceFromTree(MemberReferenceTree tree) {
return getFnInterfaceFromTree((Tree)tree);
}
public Pair getFnInterfaceFromTree(LambdaExpressionTree tree) {
return getFnInterfaceFromTree((Tree) tree);
}
/**
* Find the declared type of the functional interface and the executable type for its method for a given
* MemberReferenceTree or LambdaExpressionTree.
*
* @param tree the MemberReferenceTree or LambdaExpressionTree
* @return the declared type of the functional interface and the executable type
*/
private Pair getFnInterfaceFromTree(Tree tree) {
Context ctx = ((JavacProcessingEnvironment) getProcessingEnv()).getContext();
com.sun.tools.javac.code.Types javacTypes = com.sun.tools.javac.code.Types.instance(ctx);
// ========= Overridden Type =========
AnnotatedDeclaredType functionalInterfaceType = getFunctionalInterfaceType(tree, javacTypes);
makeGroundTargetType(functionalInterfaceType);
// ========= Overridden Executable =========
Element fnElement = javacTypes.findDescriptorSymbol(
((Type) functionalInterfaceType.getUnderlyingType()).asElement());
// The method viewed from the declared type
AnnotatedExecutableType methodExe = (AnnotatedExecutableType)AnnotatedTypes.asMemberOf(
types, this, functionalInterfaceType, fnElement);
return Pair.of(functionalInterfaceType, methodExe);
}
/**
* Get the AnnotatedDeclaredType for the FunctionalInterface from assignment context of the method reference
* which may be a variable assignment, a method call, or a cast.
*
* The assignment context is not always correct, so we must search up the AST. It will recursively search
* for lambdas nested in lambdas.
*
* @param lambdaTree the tree of the lambda or method reference
* @return the functional interface type
*/
private AnnotatedDeclaredType getFunctionalInterfaceType(Tree lambdaTree,
com.sun.tools.javac.code.Types javacTypes) {
Tree parentTree = TreePath.getPath(this.root, lambdaTree).getParentPath().getLeaf();
switch (parentTree.getKind()) {
case PARENTHESIZED:
return getFunctionalInterfaceType(parentTree, javacTypes);
case TYPE_CAST:
TypeCastTree cast = (TypeCastTree) parentTree;
assertFunctionalInterface(javacTypes, (Type) trees.getTypeMirror(getPath(cast.getType())), parentTree, lambdaTree);
AnnotatedTypeMirror castATM = getAnnotatedType(cast.getType());
if (castATM.getKind() == TypeKind.INTERSECTION) {
AnnotatedIntersectionType itype = (AnnotatedIntersectionType) castATM;
for (AnnotatedTypeMirror t : itype.directSuperTypes()) {
if (javacTypes.isFunctionalInterface((Type) t.getUnderlyingType())) {
return (AnnotatedDeclaredType) t;
}
}
// We should never reach here: assertFunctionalInterface performs the same check and
// would have raised an error already.
ErrorReporter.errorAbort(String.format(
"Expected the type of a cast tree in an assignment context to contain a functional interface bound. " +
"Found type: %s for tree: %s in lambda tree: %s",
castATM, cast, lambdaTree));
}
return (AnnotatedDeclaredType) castATM;
case NEW_CLASS:
NewClassTree newClass = (NewClassTree) parentTree;
int indexOfLambda = newClass.getArguments().indexOf(lambdaTree);
Pair> con = this.constructorFromUse(newClass);
AnnotatedTypeMirror constructorParam = AnnotatedTypes.getAnnotatedTypeMirrorOfParameter(con.first, indexOfLambda);
assertFunctionalInterface(javacTypes, (Type) constructorParam.getUnderlyingType(), parentTree, lambdaTree);
return (AnnotatedDeclaredType) constructorParam;
case METHOD_INVOCATION:
MethodInvocationTree method = (MethodInvocationTree) parentTree;
int index = method.getArguments().indexOf(lambdaTree);
Pair> exe = this.methodFromUse(method);
AnnotatedTypeMirror param = AnnotatedTypes.getAnnotatedTypeMirrorOfParameter(exe.first, index);
assertFunctionalInterface(javacTypes, (Type)param.getUnderlyingType(), parentTree, lambdaTree);
return (AnnotatedDeclaredType) param;
case VARIABLE:
VariableTree varTree = (VariableTree) parentTree;
assertFunctionalInterface(javacTypes, (Type)InternalUtils.typeOf(varTree), parentTree, lambdaTree);
return (AnnotatedDeclaredType) getAnnotatedType(varTree.getType());
case ASSIGNMENT:
AssignmentTree assignmentTree = (AssignmentTree) parentTree;
assertFunctionalInterface(javacTypes, (Type)InternalUtils.typeOf(assignmentTree), parentTree, lambdaTree);
return (AnnotatedDeclaredType) getAnnotatedType(assignmentTree.getVariable());
case RETURN:
Tree enclosing = TreeUtils.enclosingOfKind(TreePath.getPath(this.root, parentTree),
new HashSet<>(Arrays.asList(Tree.Kind.METHOD, Tree.Kind.LAMBDA_EXPRESSION)));
if (enclosing.getKind() == Tree.Kind.METHOD) {
MethodTree enclosingMethod = (MethodTree) enclosing;
return (AnnotatedDeclaredType) getAnnotatedType(enclosingMethod.getReturnType());
} else {
LambdaExpressionTree enclosingLambda = (LambdaExpressionTree) enclosing;
Pair result = getFnInterfaceFromTree(enclosingLambda);
AnnotatedExecutableType methodExe = result.second;
return (AnnotatedDeclaredType) methodExe.getReturnType();
}
case LAMBDA_EXPRESSION:
LambdaExpressionTree enclosingLambda = (LambdaExpressionTree) parentTree;
Pair result = getFnInterfaceFromTree(enclosingLambda);
AnnotatedExecutableType methodExe = result.second;
return (AnnotatedDeclaredType) methodExe.getReturnType();
case CONDITIONAL_EXPRESSION:
ConditionalExpressionTree conditionalExpressionTree = (ConditionalExpressionTree) parentTree;
final AnnotatedTypeMirror falseType = getAnnotatedType(conditionalExpressionTree.getFalseExpression());
final AnnotatedTypeMirror trueType = getAnnotatedType(conditionalExpressionTree.getTrueExpression());
// Known cases where we must use LUB because falseType/trueType will not be equal:
// a) when one of the types is a type variable that extends a functional interface
// or extends a type variable that extends a functional interface
// b) When one of the two sides of the expression is a reference to a sub-interface.
// e.g. interface ConsumeStr {
// public void consume(String s)
// }
// interface SubConsumer extends ConsumeStr {
// default void someOtherMethod() { ... }
// }
// SubConsumer s = ...;
// ConsumeStr stringConsumer = (someCondition) ? s : System.out::println;
AnnotatedTypeMirror conditionalType = AnnotatedTypes.leastUpperBound(processingEnv, this, trueType, falseType);
assertFunctionalInterface(javacTypes, (Type) conditionalType.getUnderlyingType(), parentTree, lambdaTree);
return (AnnotatedDeclaredType) conditionalType;
default:
ErrorReporter.errorAbort("Could not find functional interface from assignment context. " +
"Unexpected tree type: " + parentTree.getKind() +
" For lambda tree: " + lambdaTree);
return null;
}
}
private void assertFunctionalInterface(com.sun.tools.javac.code.Types javacTypes,
Type type, Tree contextTree, Tree lambdaTree) {
if (!javacTypes.isFunctionalInterface(type)) {
if (type.getKind() == TypeKind.INTERSECTION) {
IntersectionType itype = (IntersectionType) type;
for (TypeMirror t : itype.getBounds()) {
if (javacTypes.isFunctionalInterface((Type) t)) {
// As long as any of the bounds is a functional interface
// we should be fine.
return;
}
}
}
ErrorReporter.errorAbort(String.format(
"Expected the type of %s tree in assignment context to be a functional interface. " +
"Found type: %s for tree: %s in lambda tree: %s",
contextTree.getKind(), type, contextTree, lambdaTree));
}
}
/**
* Create the ground target type of the functional interface.
*
* Basically, it replaces the wildcards with their bounds
* doing a capture conversion like glb for extends bounds.
*
* @see "JLS 9.9"
* @param overriddenType the functional interface type
*/
private void makeGroundTargetType(AnnotatedDeclaredType overriddenType) {
if (overriddenType.getTypeArguments().size() > 0) {
List bounds = this.typeVariablesFromUse(overriddenType, (TypeElement)overriddenType.getUnderlyingType().asElement());
List newTypeArguments = new ArrayList<>(overriddenType.getTypeArguments());
for (int i = 0 ; i < overriddenType.getTypeArguments().size() ; i ++) {
AnnotatedTypeMirror argType = overriddenType.getTypeArguments().get(i);
if (argType.getKind() == TypeKind.WILDCARD) {
AnnotatedWildcardType wildcardType = (AnnotatedWildcardType) argType;
final TypeMirror wilcardUbType = wildcardType.getExtendsBound().getUnderlyingType();
final TypeMirror typeParamUbType = bounds.get(i).getUpperBound().getUnderlyingType();
if (isExtendsWildcard(wildcardType)) {
TypeMirror glbType =
InternalUtils.greatestLowerBound(this.checker.getProcessingEnvironment(),
typeParamUbType, wilcardUbType);
// checkTypeArgs now enforces that wildcard annotation bounds MUST be within
// the bounds of the type parameter. Therefore, the wildcard's upper bound
// should ALWAYS be more specific than the upper bound of the type parameter
// That said, the Java type does NOT have to be.
// Add the annotations from the wildcard to the lub type.
final AnnotatedTypeMirror newArg;
if (types.isSameType(wilcardUbType, glbType)) {
newArg = wildcardType.getExtendsBound().deepCopy();
} else {
newArg = this.toAnnotatedType(glbType, false);
newArg.replaceAnnotations(wildcardType.getExtendsBound().getAnnotations());
}
newTypeArguments.set(i, newArg);
} else {
newTypeArguments.set(i, wildcardType.getSuperBound());
}
}
}
overriddenType.setTypeArguments(newTypeArguments);
}
}
/**
* Check that a wildcard is an extends wildcard
*
* @param awt the wildcard type
* @return true if awt is an extends wildcard
*/
private boolean isExtendsWildcard(AnnotatedWildcardType awt) {
return awt.getUnderlyingType().getSuperBound() == null;
}
/** Accessor for the element utilities.
*/
public Elements getElementUtils() {
return this.elements;
}
/** Accessor for the tree utilities.
*/
public Trees getTreeUtils() {
return this.trees;
}
/** Accessor for the processing environment.
*/
public ProcessingEnvironment getProcessingEnv() {
return this.processingEnv;
}
/** Accessor for the {@link CFContext}.
*/
public CFContext getContext() {
return checker;
}
}