| package org.checkerframework.framework.type; |
| |
| // The imports from com.sun are all @jdk.Exported and therefore somewhat safe to use. |
| // Try to avoid using non-@jdk.Exported classes. |
| |
| import com.sun.source.tree.AnnotationTree; |
| import com.sun.source.tree.AssignmentTree; |
| import com.sun.source.tree.BinaryTree; |
| import com.sun.source.tree.ClassTree; |
| import com.sun.source.tree.CompilationUnitTree; |
| import com.sun.source.tree.CompoundAssignmentTree; |
| import com.sun.source.tree.ConditionalExpressionTree; |
| import com.sun.source.tree.ExpressionTree; |
| 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.Tree.Kind; |
| 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.Type; |
| import com.sun.tools.javac.tree.JCTree.JCNewClass; |
| import java.io.File; |
| import java.lang.annotation.Annotation; |
| import java.lang.annotation.ElementType; |
| import java.lang.annotation.Inherited; |
| import java.lang.annotation.Target; |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.Collection; |
| import java.util.Collections; |
| import java.util.EnumSet; |
| import java.util.HashMap; |
| import java.util.HashSet; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Set; |
| import java.util.StringJoiner; |
| import java.util.regex.Matcher; |
| import java.util.regex.Pattern; |
| 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.PackageElement; |
| import javax.lang.model.element.TypeElement; |
| import javax.lang.model.element.VariableElement; |
| import javax.lang.model.type.DeclaredType; |
| 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; |
| import org.checkerframework.checker.interning.qual.FindDistinct; |
| import org.checkerframework.checker.interning.qual.InternedDistinct; |
| import org.checkerframework.checker.nullness.qual.MonotonicNonNull; |
| import org.checkerframework.checker.nullness.qual.Nullable; |
| import org.checkerframework.checker.signature.qual.CanonicalName; |
| import org.checkerframework.checker.signature.qual.FullyQualifiedName; |
| import org.checkerframework.common.basetype.BaseTypeChecker; |
| import org.checkerframework.common.basetype.BaseTypeVisitor; |
| 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.reflection.qual.MethodVal; |
| import org.checkerframework.common.wholeprograminference.WholeProgramInference; |
| import org.checkerframework.common.wholeprograminference.WholeProgramInferenceImplementation; |
| import org.checkerframework.common.wholeprograminference.WholeProgramInferenceJavaParserStorage; |
| import org.checkerframework.common.wholeprograminference.WholeProgramInferenceScenesStorage; |
| import org.checkerframework.dataflow.qual.SideEffectFree; |
| import org.checkerframework.framework.qual.AnnotatedFor; |
| import org.checkerframework.framework.qual.EnsuresQualifier; |
| import org.checkerframework.framework.qual.EnsuresQualifierIf; |
| import org.checkerframework.framework.qual.FieldInvariant; |
| import org.checkerframework.framework.qual.FromStubFile; |
| import org.checkerframework.framework.qual.HasQualifierParameter; |
| import org.checkerframework.framework.qual.InheritedAnnotation; |
| import org.checkerframework.framework.qual.NoQualifierParameter; |
| import org.checkerframework.framework.qual.RequiresQualifier; |
| import org.checkerframework.framework.source.SourceChecker; |
| import org.checkerframework.framework.stub.AnnotationFileElementTypes; |
| 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.AnnotatedTypeCombiner; |
| import org.checkerframework.framework.type.visitor.SimpleAnnotatedTypeScanner; |
| import org.checkerframework.framework.util.AnnotatedTypes; |
| import org.checkerframework.framework.util.AnnotationFormatter; |
| import org.checkerframework.framework.util.CheckerMain; |
| import org.checkerframework.framework.util.DefaultAnnotationFormatter; |
| import org.checkerframework.framework.util.FieldInvariants; |
| import org.checkerframework.framework.util.TreePathCacher; |
| import org.checkerframework.framework.util.typeinference.DefaultTypeArgumentInference; |
| import org.checkerframework.framework.util.typeinference.TypeArgInferenceUtil; |
| import org.checkerframework.framework.util.typeinference.TypeArgumentInference; |
| import org.checkerframework.javacutil.AnnotationBuilder; |
| import org.checkerframework.javacutil.AnnotationProvider; |
| import org.checkerframework.javacutil.AnnotationUtils; |
| import org.checkerframework.javacutil.BugInCF; |
| import org.checkerframework.javacutil.CollectionUtils; |
| import org.checkerframework.javacutil.ElementUtils; |
| import org.checkerframework.javacutil.Pair; |
| import org.checkerframework.javacutil.TreePathUtil; |
| import org.checkerframework.javacutil.TreeUtils; |
| import org.checkerframework.javacutil.TypeKindUtils; |
| import org.checkerframework.javacutil.TypeSystemError; |
| import org.checkerframework.javacutil.TypesUtils; |
| import org.checkerframework.javacutil.UserError; |
| import org.checkerframework.javacutil.trees.DetachedVarSymbol; |
| import org.plumelib.util.CollectionsPlume; |
| import org.plumelib.util.StringsPlume; |
| import scenelib.annotations.el.AMethod; |
| import scenelib.annotations.el.ATypeElement; |
| |
| /** |
| * The methods of this class take an element or AST node, and return the annotated type as an {@link |
| * AnnotatedTypeMirror}. The methods are: |
| * |
| * <ul> |
| * <li>{@link #getAnnotatedType(ClassTree)} |
| * <li>{@link #getAnnotatedType(MethodTree)} |
| * <li>{@link #getAnnotatedType(Tree)} |
| * <li>{@link #getAnnotatedTypeFromTypeTree(Tree)} |
| * <li>{@link #getAnnotatedType(TypeElement)} |
| * <li>{@link #getAnnotatedType(ExecutableElement)} |
| * <li>{@link #getAnnotatedType(Element)} |
| * </ul> |
| * |
| * This implementation only adds qualifiers explicitly specified by the programmer. Subclasses |
| * override {@link #addComputedTypeAnnotations} to add defaults, flow-sensitive refinement, and |
| * type-system-specific rules. |
| * |
| * <p>Unless otherwise indicated, each public method in this class returns a "fully annotated" type, |
| * which is one that has an annotation in all positions. |
| * |
| * <p>Type system checker writers may need to subclass this class, to add default qualifiers |
| * according to the type system semantics. Subclasses should especially override {@link |
| * #addComputedTypeAnnotations(Element, AnnotatedTypeMirror)} and {@link |
| * #addComputedTypeAnnotations(Tree, AnnotatedTypeMirror)} to handle default annotations. (Also, |
| * {@link #addDefaultAnnotations(AnnotatedTypeMirror)} adds annotations, but that method is a |
| * workaround for <a href="https://github.com/typetools/checker-framework/issues/979">Issue |
| * 979</a>.) |
| * |
| * @checker_framework.manual #creating-a-checker How to write a checker plug-in |
| */ |
| public class AnnotatedTypeFactory implements AnnotationProvider { |
| |
| /** Whether to print verbose debugging messages about stub files. */ |
| private final boolean debugStubParser; |
| |
| /** 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. */ |
| public final Types types; |
| |
| /** The state of the visitor. */ |
| protected final VisitorState visitorState; |
| |
| /** The AnnotatedFor.value argument/element. */ |
| private final ExecutableElement annotatedForValueElement; |
| /** The EnsuresQualifier.expression field/element. */ |
| final ExecutableElement ensuresQualifierExpressionElement; |
| /** The EnsuresQualifier.List.value field/element. */ |
| final ExecutableElement ensuresQualifierListValueElement; |
| /** The EnsuresQualifierIf.expression field/element. */ |
| final ExecutableElement ensuresQualifierIfExpressionElement; |
| /** The EnsuresQualifierIf.result argument/element. */ |
| final ExecutableElement ensuresQualifierIfResultElement; |
| /** The EnsuresQualifierIf.List.value field/element. */ |
| final ExecutableElement ensuresQualifierIfListValueElement; |
| /** The FieldInvariant.field argument/element. */ |
| private final ExecutableElement fieldInvariantFieldElement; |
| /** The FieldInvariant.qualifier argument/element. */ |
| private final ExecutableElement fieldInvariantQualifierElement; |
| /** The HasQualifierParameter.value field/element. */ |
| private final ExecutableElement hasQualifierParameterValueElement; |
| /** The MethodVal.className argument/element. */ |
| public final ExecutableElement methodValClassNameElement; |
| /** The MethodVal.methodName argument/element. */ |
| public final ExecutableElement methodValMethodNameElement; |
| /** The MethodVal.params argument/element. */ |
| public final ExecutableElement methodValParamsElement; |
| /** The NoQualifierParameter.value field/element. */ |
| private final ExecutableElement noQualifierParameterValueElement; |
| /** The RequiresQualifier.expression field/element. */ |
| final ExecutableElement requiresQualifierExpressionElement; |
| /** The RequiresQualifier.List.value field/element. */ |
| final ExecutableElement requiresQualifierListValueElement; |
| |
| /** The RequiresQualifier type. */ |
| TypeMirror requiresQualifierTM; |
| /** The RequiresQualifier.List type. */ |
| TypeMirror requiresQualifierListTM; |
| /** The EnsuresQualifier type. */ |
| TypeMirror ensuresQualifierTM; |
| /** The EnsuresQualifier.List type. */ |
| TypeMirror ensuresQualifierListTM; |
| /** The EnsuresQualifierIf type. */ |
| TypeMirror ensuresQualifierIfTM; |
| /** The EnsuresQualifierIf.List type. */ |
| TypeMirror ensuresQualifierIfListTM; |
| |
| /** |
| * ===== 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. If null, whole-program inference is disabled. */ |
| private final @Nullable WholeProgramInference wholeProgramInference; |
| |
| /** |
| * This formatter is used for converting AnnotatedTypeMirrors to Strings. This formatter will be |
| * used by all AnnotatedTypeMirrors created by this factory 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; |
| |
| /** Holds the qualifier upper bounds for type uses. */ |
| protected QualifierUpperBounds qualifierUpperBounds; |
| |
| /** |
| * 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; |
| |
| /** |
| * Caches the supported type qualifier classes. Call {@link #getSupportedTypeQualifiers()} instead |
| * of using this field directly, as it may not have been initialized. |
| */ |
| private final Set<Class<? extends Annotation>> supportedQuals; |
| |
| /** |
| * Caches the fully-qualified names of the classes in {@link #supportedQuals}. Call {@link |
| * #getSupportedTypeQualifierNames()} instead of using this field directly, as it may not have |
| * been initialized. |
| */ |
| private final Set<@CanonicalName String> supportedQualNames; |
| |
| /** Parses stub files and stores annotations on public elements from stub files. */ |
| public final AnnotationFileElementTypes stubTypes; |
| |
| /** Parses ajava files and stores annotations on public elements from ajava files. */ |
| public final AnnotationFileElementTypes ajavaTypes; |
| |
| /** |
| * If type checking a Java file, stores annotations read from an ajava file for that class if one |
| * exists. Unlike {@link #ajavaTypes}, which only stores annotations on public elements, this |
| * stores annotations on all element locations such as in anonymous class bodies. |
| */ |
| public @Nullable AnnotationFileElementTypes currentFileAjavaTypes; |
| |
| /** |
| * A cache used to store elements whose declaration annotations have already been stored by |
| * calling the method {@link #getDeclAnnotations(Element)}. |
| */ |
| private final Map<Element, Set<AnnotationMirror>> 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<AnnotationMirror> inheritedAnnotations = AnnotationUtils.createAnnotationSet(); |
| |
| /** The checker to use for option handling and resource management. */ |
| protected final BaseTypeChecker checker; |
| |
| /** Map keys are canonical names of aliased annotations. */ |
| private final Map<@FullyQualifiedName String, Alias> aliases = new HashMap<>(); |
| |
| /** |
| * Information about one annotation alias. |
| * |
| * <p>The information is either an AnotationMirror that can be used directly, or information for a |
| * builder (name and fields not to copy); see checkRep. |
| */ |
| private static class Alias { |
| /** The canonical annotation (or null if copyElements == true). */ |
| AnnotationMirror canonical; |
| /** Whether elements should be copied over when translating to the canonical annotation. */ |
| boolean copyElements; |
| /** The canonical annotation name (or null if copyElements == false). */ |
| @CanonicalName String canonicalName; |
| /** Which elements should not be copied over (or null if copyElements == false). */ |
| String[] ignorableElements; |
| |
| /** |
| * Create an Alias with the given components. |
| * |
| * @param aliasName the alias name; only used for debugging |
| * @param canonical the canonical annotation |
| * @param copyElements whether elements should be copied over when translating to the canonical |
| * annotation |
| * @param canonicalName the canonical annotation name (or null if copyElements == false) |
| * @param ignorableElements elements that should not be copied over |
| */ |
| Alias( |
| String aliasName, |
| AnnotationMirror canonical, |
| boolean copyElements, |
| @CanonicalName String canonicalName, |
| String[] ignorableElements) { |
| this.canonical = canonical; |
| this.copyElements = copyElements; |
| this.canonicalName = canonicalName; |
| this.ignorableElements = ignorableElements; |
| checkRep(aliasName); |
| } |
| |
| /** |
| * Throw an exception if this object is malformed. |
| * |
| * @param aliasName the alias name; only used for diagnostic messages |
| */ |
| void checkRep(String aliasName) { |
| if (copyElements) { |
| if (!(canonical == null && canonicalName != null && ignorableElements != null)) { |
| throw new BugInCF( |
| "Bad Alias for %s: [canonical=%s] copyElements=%s canonicalName=%s" |
| + " ignorableElements=%s", |
| aliasName, canonical, copyElements, canonicalName, ignorableElements); |
| } |
| } else { |
| if (!(canonical != null && canonicalName == null && ignorableElements == null)) { |
| throw new BugInCF( |
| "Bad Alias for %s: canonical=%s copyElements=%s [canonicalName=%s" |
| + " ignorableElements=%s]", |
| aliasName, canonical, copyElements, canonicalName, ignorableElements); |
| } |
| } |
| } |
| } |
| |
| /** |
| * A map from the class of an annotation to the set of classes for annotations with the same |
| * meaning, as well as the annotation mirror that should be used. |
| */ |
| private final Map< |
| Class<? extends Annotation>, Pair<AnnotationMirror, Set<Class<? extends Annotation>>>> |
| 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; |
| |
| /** |
| * Object that is used to resolve reflective method calls, if reflection resolution is turned on. |
| */ |
| protected ReflectionResolver reflectionResolver; |
| |
| /** AnnotationClassLoader used to load type annotation classes via reflective lookup. */ |
| protected AnnotationClassLoader loader; |
| |
| /** |
| * Which whole-program inference output format to use, if doing whole-program inference. This |
| * variable would be final, but it is not set unless WPI is enabled. |
| */ |
| protected WholeProgramInference.OutputFormat wpiOutputFormat; |
| |
| /** |
| * 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 static final int DEFAULT_CACHE_SIZE = 300; |
| |
| /** Mapping from a Tree to its annotated type; defaults have been applied. */ |
| private final Map<Tree, AnnotatedTypeMirror> classAndMethodTreeCache; |
| |
| /** |
| * Mapping from an expression tree to its annotated type; before defaults are applied, just what |
| * the programmer wrote. |
| */ |
| protected final Map<Tree, AnnotatedTypeMirror> fromExpressionTreeCache; |
| |
| /** |
| * Mapping from a member tree to its annotated type; before defaults are applied, just what the |
| * programmer wrote. |
| */ |
| protected final Map<Tree, AnnotatedTypeMirror> fromMemberTreeCache; |
| |
| /** |
| * Mapping from a type tree to its annotated type; before defaults are applied, just what the |
| * programmer wrote. |
| */ |
| protected final Map<Tree, AnnotatedTypeMirror> fromTypeTreeCache; |
| |
| /** |
| * Mapping from an Element to its annotated type; before defaults are applied, just what the |
| * programmer wrote. |
| */ |
| private final Map<Element, AnnotatedTypeMirror> elementCache; |
| |
| /** Mapping from an Element to the source Tree of the declaration. */ |
| private final Map<Element, Tree> elementToTreeCache; |
| |
| /** Mapping from a Tree to its TreePath. Shared between all instances. */ |
| private final TreePathCacher treePathCache; |
| |
| /** Mapping from CFG-generated trees to their enclosing elements. */ |
| protected final Map<Tree, Element> artificialTreeToEnclosingElementMap; |
| |
| /** |
| * Whether to ignore uninferred type arguments. This is a temporary flag to work around Issue 979. |
| */ |
| public final boolean ignoreUninferredTypeArguments; |
| |
| /** The Object.getClass method. */ |
| protected final ExecutableElement objectGetClass; |
| |
| /** Size of the annotationClassNames cache. */ |
| private static final int ANNOTATION_CACHE_SIZE = 500; |
| |
| /** Maps classes representing AnnotationMirrors to their canonical names. */ |
| private final Map<Class<? extends Annotation>, @CanonicalName String> annotationClassNames; |
| |
| /** An annotated type of the declaration of {@link Iterable} without any annotations. */ |
| private AnnotatedDeclaredType iterableDeclType; |
| |
| /** |
| * 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.) |
| * |
| * <p>Root can be {@code null} if the factory does not operate on trees. |
| * |
| * <p>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.supportedQuals = new HashSet<>(); |
| this.supportedQualNames = new HashSet<>(); |
| this.stubTypes = new AnnotationFileElementTypes(this); |
| this.ajavaTypes = new AnnotationFileElementTypes(this); |
| this.currentFileAjavaTypes = null; |
| |
| this.cacheDeclAnnos = new HashMap<>(); |
| |
| this.artificialTreeToEnclosingElementMap = new HashMap<>(); |
| // get the shared instance from the checker |
| this.treePathCache = checker.getTreePathCacher(); |
| |
| this.shouldCache = !checker.hasOption("atfDoNotCache"); |
| if (shouldCache) { |
| int cacheSize = getCacheSize(); |
| this.classAndMethodTreeCache = CollectionUtils.createLRUCache(cacheSize); |
| this.fromExpressionTreeCache = CollectionUtils.createLRUCache(cacheSize); |
| this.fromMemberTreeCache = CollectionUtils.createLRUCache(cacheSize); |
| this.fromTypeTreeCache = CollectionUtils.createLRUCache(cacheSize); |
| this.elementCache = CollectionUtils.createLRUCache(cacheSize); |
| this.elementToTreeCache = CollectionUtils.createLRUCache(cacheSize); |
| this.annotationClassNames = |
| Collections.synchronizedMap(CollectionUtils.createLRUCache(ANNOTATION_CACHE_SIZE)); |
| } else { |
| this.classAndMethodTreeCache = null; |
| this.fromExpressionTreeCache = null; |
| this.fromMemberTreeCache = null; |
| this.fromTypeTreeCache = null; |
| this.elementCache = null; |
| this.elementToTreeCache = null; |
| this.annotationClassNames = null; |
| } |
| |
| this.typeFormatter = createAnnotatedTypeFormatter(); |
| this.annotationFormatter = createAnnotationFormatter(); |
| |
| if (checker.hasOption("infer")) { |
| checkInvalidOptionsInferSignatures(); |
| String inferArg = checker.getOption("infer"); |
| // No argument means "jaifs", for (temporary) backwards compatibility. |
| if (inferArg == null) { |
| inferArg = "jaifs"; |
| } |
| switch (inferArg) { |
| case "stubs": |
| wpiOutputFormat = WholeProgramInference.OutputFormat.STUB; |
| break; |
| case "jaifs": |
| wpiOutputFormat = WholeProgramInference.OutputFormat.JAIF; |
| break; |
| case "ajava": |
| wpiOutputFormat = WholeProgramInference.OutputFormat.AJAVA; |
| break; |
| default: |
| throw new UserError( |
| "Bad argument -Ainfer=" |
| + inferArg |
| + " should be one of: -Ainfer=jaifs, -Ainfer=stubs, -Ainfer=ajava"); |
| } |
| if (wpiOutputFormat == WholeProgramInference.OutputFormat.AJAVA) { |
| wholeProgramInference = |
| new WholeProgramInferenceImplementation<AnnotatedTypeMirror>( |
| this, new WholeProgramInferenceJavaParserStorage(this)); |
| } else { |
| wholeProgramInference = |
| new WholeProgramInferenceImplementation<ATypeElement>( |
| this, new WholeProgramInferenceScenesStorage(this)); |
| } |
| if (!checker.hasOption("warns")) { |
| // Without -Awarns, the inference output may be incomplete, because javac halts |
| // after issuing an error. |
| checker.message(Diagnostic.Kind.ERROR, "Do not supply -Ainfer without -Awarns"); |
| } |
| } else { |
| wholeProgramInference = null; |
| } |
| ignoreUninferredTypeArguments = !checker.hasOption("conservativeUninferredTypeArguments"); |
| |
| objectGetClass = TreeUtils.getMethod("java.lang.Object", "getClass", 0, processingEnv); |
| |
| this.debugStubParser = checker.hasOption("stubDebug"); |
| |
| annotatedForValueElement = TreeUtils.getMethod(AnnotatedFor.class, "value", 0, processingEnv); |
| ensuresQualifierExpressionElement = |
| TreeUtils.getMethod(EnsuresQualifier.class, "expression", 0, processingEnv); |
| ensuresQualifierListValueElement = |
| TreeUtils.getMethod(EnsuresQualifier.List.class, "value", 0, processingEnv); |
| ensuresQualifierIfExpressionElement = |
| TreeUtils.getMethod(EnsuresQualifierIf.class, "expression", 0, processingEnv); |
| ensuresQualifierIfResultElement = |
| TreeUtils.getMethod(EnsuresQualifierIf.class, "result", 0, processingEnv); |
| ensuresQualifierIfListValueElement = |
| TreeUtils.getMethod(EnsuresQualifierIf.List.class, "value", 0, processingEnv); |
| fieldInvariantFieldElement = |
| TreeUtils.getMethod(FieldInvariant.class, "field", 0, processingEnv); |
| fieldInvariantQualifierElement = |
| TreeUtils.getMethod(FieldInvariant.class, "qualifier", 0, processingEnv); |
| hasQualifierParameterValueElement = |
| TreeUtils.getMethod(HasQualifierParameter.class, "value", 0, processingEnv); |
| methodValClassNameElement = TreeUtils.getMethod(MethodVal.class, "className", 0, processingEnv); |
| methodValMethodNameElement = |
| TreeUtils.getMethod(MethodVal.class, "methodName", 0, processingEnv); |
| methodValParamsElement = TreeUtils.getMethod(MethodVal.class, "params", 0, processingEnv); |
| noQualifierParameterValueElement = |
| TreeUtils.getMethod(NoQualifierParameter.class, "value", 0, processingEnv); |
| requiresQualifierExpressionElement = |
| TreeUtils.getMethod(RequiresQualifier.class, "expression", 0, processingEnv); |
| requiresQualifierListValueElement = |
| TreeUtils.getMethod(RequiresQualifier.List.class, "value", 0, processingEnv); |
| |
| requiresQualifierTM = |
| ElementUtils.getTypeElement(processingEnv, RequiresQualifier.class).asType(); |
| requiresQualifierListTM = |
| ElementUtils.getTypeElement(processingEnv, RequiresQualifier.List.class).asType(); |
| ensuresQualifierTM = |
| ElementUtils.getTypeElement(processingEnv, EnsuresQualifier.class).asType(); |
| ensuresQualifierListTM = |
| ElementUtils.getTypeElement(processingEnv, EnsuresQualifier.List.class).asType(); |
| ensuresQualifierIfTM = |
| ElementUtils.getTypeElement(processingEnv, EnsuresQualifierIf.class).asType(); |
| ensuresQualifierIfListTM = |
| ElementUtils.getTypeElement(processingEnv, EnsuresQualifierIf.List.class).asType(); |
| } |
| |
| /** |
| * @throws BugInCF If supportedQuals is empty or if any of the support qualifiers has a @Target |
| * meta-annotation that contain something besides TYPE_USE or TYPE_PARAMETER. (@Target({}) is |
| * allowed.) |
| */ |
| private void checkSupportedQuals() { |
| if (supportedQuals.isEmpty()) { |
| throw new TypeSystemError("Found no supported qualifiers."); |
| } |
| for (Class<? extends Annotation> annotationClass : supportedQuals) { |
| // Check @Target values |
| ElementType[] targetValues = annotationClass.getAnnotation(Target.class).value(); |
| List<ElementType> badTargetValues = new ArrayList<>(); |
| for (ElementType element : targetValues) { |
| if (!(element == ElementType.TYPE_USE || element == ElementType.TYPE_PARAMETER)) { |
| // if there's an ElementType with an enumerated value of something other |
| // than TYPE_USE or TYPE_PARAMETER then it isn't a valid qualifier |
| badTargetValues.add(element); |
| } |
| } |
| if (!badTargetValues.isEmpty()) { |
| String msg = |
| "The @Target meta-annotation on type qualifier " |
| + annotationClass.toString() |
| + " must not contain " |
| + StringsPlume.conjunction("or", badTargetValues) |
| + "."; |
| throw new TypeSystemError(msg); |
| } |
| } |
| } |
| |
| /** |
| * This method is called only when {@code -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 conservative defaults. |
| * |
| * <p>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.useConservativeDefault("source") || checker.useConservativeDefault("bytecode")) { |
| throw new UserError( |
| "The option -Ainfer=... cannot be used together with conservative 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) { |
| throw new TypeSystemError( |
| "AnnotatedTypeFactory with null qualifier hierarchy not supported."); |
| } else if (!qualHierarchy.isValid()) { |
| throw new TypeSystemError( |
| "AnnotatedTypeFactory: invalid qualifier hierarchy: %s %s ", |
| qualHierarchy.getClass(), qualHierarchy); |
| } |
| this.typeHierarchy = createTypeHierarchy(); |
| this.typeVarSubstitutor = createTypeVariableSubstitutor(); |
| this.typeArgumentInference = createTypeArgumentInference(); |
| this.qualifierUpperBounds = createQualifierUpperBounds(); |
| |
| // TODO: is this the best location for declaring this alias? |
| addAliasedDeclAnnotation( |
| org.jmlspecs.annotation.Pure.class, |
| org.checkerframework.dataflow.qual.Pure.class, |
| AnnotationBuilder.fromClass(elements, org.checkerframework.dataflow.qual.Pure.class)); |
| |
| // Accommodate the inability to write @InheritedAnnotation on these annotations. |
| addInheritedAnnotation( |
| AnnotationBuilder.fromClass(elements, org.checkerframework.dataflow.qual.Pure.class)); |
| addInheritedAnnotation( |
| AnnotationBuilder.fromClass( |
| elements, org.checkerframework.dataflow.qual.SideEffectFree.class)); |
| addInheritedAnnotation( |
| AnnotationBuilder.fromClass( |
| elements, org.checkerframework.dataflow.qual.Deterministic.class)); |
| addInheritedAnnotation( |
| AnnotationBuilder.fromClass( |
| elements, org.checkerframework.dataflow.qual.TerminatesExecution.class)); |
| |
| initializeReflectionResolution(); |
| |
| if (this.getClass() == AnnotatedTypeFactory.class) { |
| this.parseAnnotationFiles(); |
| } |
| TypeMirror iterableTypeMirror = |
| ElementUtils.getTypeElement(processingEnv, Iterable.class).asType(); |
| this.iterableDeclType = |
| (AnnotatedDeclaredType) AnnotatedTypeMirror.createType(iterableTypeMirror, this, true); |
| } |
| |
| /** |
| * Returns the checker associated with this factory. |
| * |
| * @return the checker associated with this factory |
| */ |
| public BaseTypeChecker getChecker() { |
| return checker; |
| } |
| |
| /** |
| * Creates {@link QualifierUpperBounds} for this type factory. |
| * |
| * @return a new {@link QualifierUpperBounds} for this type factory |
| */ |
| protected QualifierUpperBounds createQualifierUpperBounds() { |
| return new QualifierUpperBounds(this); |
| } |
| |
| /** |
| * Return {@link QualifierUpperBounds} for this type factory. |
| * |
| * @return {@link QualifierUpperBounds} for this type factory |
| */ |
| public QualifierUpperBounds getQualifierUpperBounds() { |
| return qualifierUpperBounds; |
| } |
| |
| /** |
| * Returns the WholeProgramInference instance (may be null). |
| * |
| * @return the WholeProgramInference instance, or null |
| */ |
| public WholeProgramInference getWholeProgramInference() { |
| return wholeProgramInference; |
| } |
| |
| protected void initializeReflectionResolution() { |
| 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); |
| } |
| } |
| |
| /** |
| * Set the CompilationUnitTree that should be used. |
| * |
| * @param root the new compilation unit to use |
| */ |
| public void setRoot(@Nullable CompilationUnitTree root) { |
| if (root != null && wholeProgramInference != null) { |
| for (Tree typeDecl : root.getTypeDecls()) { |
| if (typeDecl.getKind() == Kind.CLASS) { |
| ClassTree classTree = (ClassTree) typeDecl; |
| wholeProgramInference.preprocessClassTree(classTree); |
| } |
| } |
| } |
| |
| this.root = root; |
| // Do not clear here. Only the primary checker should clear this cache. |
| // treePathCache.clear(); |
| |
| // setRoot in a GenericAnnotatedTypeFactory will clear this; |
| // if this isn't a GenericATF, then it must clear it itself. |
| if (!(this instanceof GenericAnnotatedTypeFactory)) { |
| artificialTreeToEnclosingElementMap.clear(); |
| } |
| |
| if (shouldCache) { |
| // Clear the caches with trees because once the compilation unit changes, |
| // the trees may be modified and lose type arguments. |
| elementToTreeCache.clear(); |
| fromExpressionTreeCache.clear(); |
| fromMemberTreeCache.clear(); |
| fromTypeTreeCache.clear(); |
| classAndMethodTreeCache.clear(); |
| |
| // There is no need to clear the following cache, it is limited by cache size and it |
| // contents won't change between compilation units. |
| // elementCache.clear(); |
| } |
| |
| if (root != null && checker.hasOption("ajava")) { |
| // Search for an ajava file with annotations for the current source file and the current |
| // checker. It will be in a directory specified by the "ajava" option in a subdirectory |
| // corresponding to this file's package. For example, a file in package a.b would be in a |
| // subdirectory a/b. The filename is ClassName-checker.qualified.name.ajava. If such a file |
| // exists, read its detailed annotation data, including annotations on private elements. |
| |
| String packagePrefix = |
| root.getPackageName() != null |
| ? TreeUtils.nameExpressionToString(root.getPackageName()) + "." |
| : ""; |
| |
| // The method getName() returns a path. |
| String className = root.getSourceFile().getName(); |
| // Extract the basename. |
| int lastSeparator = className.lastIndexOf(File.separator); |
| if (lastSeparator != -1) { |
| className = className.substring(lastSeparator + 1); |
| } |
| // Drop the ".java" extension. |
| if (className.endsWith(".java")) { |
| className = className.substring(0, className.length() - ".java".length()); |
| } |
| |
| String qualifiedName = packagePrefix + className; |
| |
| for (String ajavaLocation : checker.getOption("ajava").split(File.pathSeparator)) { |
| String ajavaPath = |
| ajavaLocation |
| + File.separator |
| + qualifiedName.replaceAll("\\.", "/") |
| + "-" |
| + checker.getClass().getCanonicalName() |
| + ".ajava"; |
| File ajavaFile = new File(ajavaPath); |
| if (ajavaFile.exists()) { |
| currentFileAjavaTypes = new AnnotationFileElementTypes(this); |
| currentFileAjavaTypes.parseAjavaFileWithTree(ajavaPath, root); |
| break; |
| } |
| } |
| } else { |
| currentFileAjavaTypes = null; |
| } |
| } |
| |
| @SideEffectFree |
| @Override |
| public String toString() { |
| return getClass().getSimpleName() + "#" + uid; |
| } |
| |
| /** |
| * Returns the {@link QualifierHierarchy} to be used by this checker. |
| * |
| * <p>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 NoElementQualifierHierarchy}. |
| * |
| * <p>Subclasses must override this method if their qualifiers have elements; the method must |
| * return an implementation of {@link QualifierHierarchy}, such as {@link |
| * ElementQualifierHierarchy}. |
| * |
| * @return a QualifierHierarchy for this type system |
| */ |
| protected QualifierHierarchy createQualifierHierarchy() { |
| return new NoElementQualifierHierarchy(this.getSupportedTypeQualifiers(), elements); |
| } |
| |
| /** |
| * 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() { |
| return qualHierarchy; |
| } |
| |
| /** |
| * To continue to use a subclass of {@link |
| * org.checkerframework.framework.util.MultiGraphQualifierHierarchy} or {@link |
| * org.checkerframework.framework.util.GraphQualifierHierarchy}, override this method so that it |
| * returns a new instance of the subclass. Then override {@link #createQualifierHierarchy()} so |
| * that it returns the result of a call to {@link |
| * org.checkerframework.framework.util.MultiGraphQualifierHierarchy#createMultiGraphQualifierHierarchy(AnnotatedTypeFactory)}. |
| * |
| * @param factory MultiGraphFactory |
| * @return QualifierHierarchy |
| * @deprecated Use either {@link ElementQualifierHierarchy}, {@link NoElementQualifierHierarchy}, |
| * or {@link MostlyNoElementQualifierHierarchy} instead. This method will be removed in a |
| * future release. |
| */ |
| @Deprecated // 2020-09-10 |
| public QualifierHierarchy createQualifierHierarchyWithMultiGraphFactory( |
| org.checkerframework.framework.util.MultiGraphQualifierHierarchy.MultiGraphFactory factory) { |
| throw new TypeSystemError( |
| "Checker must override AnnotatedTypeFactory#createQualifierHierarchyWithMultiGraphFactory" |
| + " when using AnnotatedTypeFactory#createMultiGraphQualifierHierarchy."); |
| } |
| |
| /** |
| * Creates the type hierarchy to be used by this factory. |
| * |
| * <p>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.getBooleanOption("ignoreRawTypeArguments", 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(this); |
| } |
| |
| public TypeArgumentInference getTypeArgumentInference() { |
| return typeArgumentInference; |
| } |
| |
| /** |
| * Factory method to easily change what {@link AnnotationClassLoader} is created to load type |
| * annotation classes. Subclasses can override this method and return a custom |
| * AnnotationClassLoader subclass to customize loading logic. |
| */ |
| protected AnnotationClassLoader createAnnotationClassLoader() { |
| return new AnnotationClassLoader(checker); |
| } |
| |
| /** |
| * Returns a mutable set of annotation classes that are supported by a checker. |
| * |
| * <p>Subclasses may override this method to return a mutable set of their supported type |
| * qualifiers through one of the 5 approaches shown below. |
| * |
| * <p>Subclasses should not call this method; they should call {@link #getSupportedTypeQualifiers} |
| * instead. |
| * |
| * <p>By default, a checker supports 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. |
| * |
| * <p>To support a different set of annotations than those in the {@literal qual} subdirectory, or |
| * that have other {@code ElementType} values, see examples below. |
| * |
| * <p>In total, there are 5 ways to indicate annotations that are supported by a checker: |
| * |
| * <ol> |
| * <li>Only support annotations located in a checker's {@literal qual} directory: |
| * <p>This is the default behavior. Simply place those annotations within the {@literal |
| * qual} directory. |
| * <li>Support annotations located in a checker's {@literal qual} directory and a list of other |
| * annotations: |
| * <p>Place those annotations within the {@literal qual} directory, and override {@link |
| * #createSupportedTypeQualifiers()} by calling {@link #getBundledTypeQualifiers(Class...)} |
| * with a varargs parameter list of the other annotations. Code example: |
| * <pre> |
| * {@code @Override protected Set<Class<? extends Annotation>> createSupportedTypeQualifiers() { |
| * return getBundledTypeQualifiers(Regex.class, PartialRegex.class, RegexBottom.class, UnknownRegex.class); |
| * } } |
| * </pre> |
| * <li>Supporting only annotations that are explicitly listed: Override {@link |
| * #createSupportedTypeQualifiers()} and return a mutable set of the supported annotations. |
| * Code example: |
| * <pre> |
| * {@code @Override protected Set<Class<? extends Annotation>> createSupportedTypeQualifiers() { |
| * return new HashSet<Class<? extends Annotation>>( |
| * Arrays.asList(A.class, B.class)); |
| * } } |
| * </pre> |
| * The set of qualifiers returned by {@link #createSupportedTypeQualifiers()} must be a |
| * fresh, mutable set. The methods {@link #getBundledTypeQualifiers(Class...)} must return a |
| * fresh, mutable set |
| * </ol> |
| * |
| * @return the type qualifiers supported this processor, or an empty set if none |
| */ |
| protected Set<Class<? extends Annotation>> createSupportedTypeQualifiers() { |
| return getBundledTypeQualifiers(); |
| } |
| |
| /** |
| * Loads all annotations contained in the qual directory of a checker via reflection; if a |
| * polymorphic type qualifier exists, and an explicit array of annotations to the set of |
| * annotation classes. |
| * |
| * <p>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 a mutable set of the loaded and listed annotation classes |
| */ |
| @SafeVarargs |
| protected final Set<Class<? extends Annotation>> getBundledTypeQualifiers( |
| Class<? extends Annotation>... explicitlyListedAnnotations) { |
| return loadTypeAnnotationsFromQualDir(explicitlyListedAnnotations); |
| } |
| |
| /** |
| * Instantiates the AnnotationClassLoader and 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). |
| * |
| * <p>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<Class<? extends Annotation>> loadTypeAnnotationsFromQualDir( |
| Class<? extends Annotation>... explicitlyListedAnnotations) { |
| loader = createAnnotationClassLoader(); |
| |
| Set<Class<? extends Annotation>> annotations = loader.getBundledAnnotationClasses(); |
| |
| // 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() { |
| boolean printVerboseGenerics = checker.hasOption("printVerboseGenerics"); |
| return new DefaultAnnotatedTypeFormatter( |
| printVerboseGenerics, |
| // -AprintVerboseGenerics implies -AprintAllQualifiers |
| printVerboseGenerics || checker.hasOption("printAllQualifiers")); |
| } |
| |
| public AnnotatedTypeFormatter getAnnotatedTypeFormatter() { |
| return typeFormatter; |
| } |
| |
| protected AnnotationFormatter createAnnotationFormatter() { |
| return new DefaultAnnotationFormatter(); |
| } |
| |
| public AnnotationFormatter getAnnotationFormatter() { |
| return annotationFormatter; |
| } |
| |
| /** |
| * Returns an immutable set of the classes corresponding to the type qualifiers supported by this |
| * checker. |
| * |
| * <p>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<Class<? extends Annotation>> getSupportedTypeQualifiers() { |
| if (this.supportedQuals.isEmpty()) { |
| supportedQuals.addAll(createSupportedTypeQualifiers()); |
| checkSupportedQuals(); |
| } |
| return Collections.unmodifiableSet(supportedQuals); |
| } |
| |
| /** |
| * Returns an immutable set of the fully qualified names of the type qualifiers supported by this |
| * checker. |
| * |
| * <p>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<@CanonicalName String> getSupportedTypeQualifierNames() { |
| if (this.supportedQualNames.isEmpty()) { |
| for (Class<?> clazz : getSupportedTypeQualifiers()) { |
| supportedQualNames.add(clazz.getCanonicalName()); |
| } |
| } |
| return Collections.unmodifiableSet(supportedQualNames); |
| } |
| |
| // ********************************************************************** |
| // Factories for annotated types that account for default qualifiers |
| // ********************************************************************** |
| |
| /** |
| * Returns the size for LRU caches. It is either the value supplied via the {@code -AatfCacheSize} |
| * option or the default cache size. |
| * |
| * @return cache size passed as argument to checker or DEFAULT_CACHE_SIZE |
| */ |
| protected int getCacheSize() { |
| String option = checker.getOption("atfCacheSize"); |
| if (option == null) { |
| return DEFAULT_CACHE_SIZE; |
| } |
| try { |
| return Integer.valueOf(option); |
| } catch (NumberFormatException ex) { |
| throw new UserError("atfCacheSize was not an integer: " + option); |
| } |
| } |
| |
| /** |
| * 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) { |
| throw new BugInCF("AnnotatedTypeFactory.getAnnotatedType: null element"); |
| } |
| // Annotations explicitly written in the source code, |
| // or obtained from bytecode. |
| AnnotatedTypeMirror type = fromElement(elt); |
| addComputedTypeAnnotations(elt, type); |
| return type; |
| } |
| |
| /** |
| * Returns an AnnotatedTypeMirror representing the annotated type of {@code clazz}. |
| * |
| * @param clazz a class |
| * @return the annotated type of {@code clazz} |
| */ |
| public AnnotatedTypeMirror getAnnotatedType(Class<?> clazz) { |
| return getAnnotatedType(elements.getTypeElement(clazz.getCanonicalName())); |
| } |
| |
| @Override |
| public @Nullable AnnotationMirror getAnnotationMirror( |
| Tree tree, Class<? extends Annotation> target) { |
| if (isSupportedQualifier(target)) { |
| 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) { |
| throw new BugInCF("AnnotatedTypeFactory.getAnnotatedType: null tree"); |
| } |
| if (shouldCache && classAndMethodTreeCache.containsKey(tree)) { |
| return classAndMethodTreeCache.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.withoutParens((ExpressionTree) tree); |
| type = fromExpression((ExpressionTree) tree); |
| } else { |
| throw new BugInCF( |
| "AnnotatedTypeFactory.getAnnotatedType: query of annotated type for tree " |
| + tree.getKind()); |
| } |
| |
| addComputedTypeAnnotations(tree, type); |
| |
| if (TreeUtils.isClassTree(tree) || tree.getKind() == Tree.Kind.METHOD) { |
| // Don't cache VARIABLE |
| if (shouldCache) { |
| classAndMethodTreeCache.put(tree, type.deepCopy()); |
| } |
| } else { |
| // No caching otherwise |
| } |
| |
| return type; |
| } |
| |
| /** |
| * Called by {@link BaseTypeVisitor#visitClass(ClassTree, Void)} before the classTree is type |
| * checked. |
| * |
| * @param classTree ClassTree on which to perform preprocessing |
| */ |
| public void preProcessClassTree(ClassTree classTree) {} |
| |
| /** |
| * Called by {@link BaseTypeVisitor#visitClass(ClassTree, Void)} after the ClassTree has been type |
| * checked. |
| * |
| * <p>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. |
| */ |
| public void postProcessClassTree(ClassTree tree) { |
| TypesIntoElements.store(processingEnv, this, tree); |
| DeclarationsIntoElements.store(processingEnv, this, tree); |
| if (wholeProgramInference != null) { |
| // Write out the results of whole-program inference, just once for each class. As soon as any |
| // class is finished processing, all modified scenes are written to files, in case this was |
| // the last class to be processed. Post-processing of subsequent classes might result in |
| // re-writing some of the scenes if new information has been written to them. |
| wholeProgramInference.writeResultsToFile(wpiOutputFormat, this.checker); |
| } |
| } |
| |
| /** |
| * Determines the annotated type from a type in tree form. |
| * |
| * <p>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) { |
| throw new BugInCF("AnnotatedTypeFactory.getAnnotatedTypeFromTypeTree: null tree"); |
| } |
| AnnotatedTypeMirror type = fromTypeTree(tree); |
| addComputedTypeAnnotations(tree, type); |
| return type; |
| } |
| |
| /** |
| * Returns the set of qualifiers that are the upper bounds for a use of the type. |
| * |
| * @param type a type whose upper bounds to obtain |
| */ |
| public Set<AnnotationMirror> getTypeDeclarationBounds(TypeMirror type) { |
| return qualifierUpperBounds.getBoundQualifiers(type); |
| } |
| |
| /** |
| * Returns the set of qualifiers that are the upper bound for a type use if no other bound is |
| * specified for the type. |
| * |
| * <p>This implementation returns the top qualifiers by default. Subclass may override to return |
| * different qualifiers. |
| * |
| * @return the set of qualifiers that are the upper bound for a type use if no other bound is |
| * specified for the type |
| */ |
| protected Set<? extends AnnotationMirror> getDefaultTypeDeclarationBounds() { |
| return qualHierarchy.getTopAnnotations(); |
| } |
| |
| /** |
| * Returns the type of the extends or implements clause. |
| * |
| * <p>The primary qualifier is either an explicit annotation on {@code clause}, or it is the |
| * qualifier upper bounds for uses of the type of the clause. |
| * |
| * @param clause tree that represents an extends or implements clause |
| * @return the type of the extends or implements clause |
| */ |
| public AnnotatedTypeMirror getTypeOfExtendsImplements(Tree clause) { |
| AnnotatedTypeMirror fromTypeTree = fromTypeTree(clause); |
| Set<AnnotationMirror> bound = getTypeDeclarationBounds(fromTypeTree.getUnderlyingType()); |
| fromTypeTree.addMissingAnnotations(bound); |
| return fromTypeTree; |
| } |
| |
| // ********************************************************************** |
| // Factories for annotated types that do not account for default 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. |
| * |
| * <p>Does not include default qualifiers. To obtain them, use {@link #getAnnotatedType(Element)}. |
| * |
| * <p>Does not include fake overrides from the stub file. |
| * |
| * @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 retrieved from the tree so that only explicit annotations are returned. |
| Tree decl = declarationFromElement(elt); |
| |
| if (decl == null) { |
| type = stubTypes.getAnnotatedTypeMirror(elt); |
| if (type == null) { |
| type = toAnnotatedType(elt.asType(), ElementUtils.isTypeDeclaration(elt)); |
| ElementAnnotationApplier.apply(type, elt, this); |
| } |
| } 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 { |
| throw new BugInCF( |
| "AnnotatedTypeFactory.fromElement: cannot be here. decl: " |
| + decl.getKind() |
| + " elt: " |
| + elt); |
| } |
| |
| type = mergeAnnotationFileAnnosIntoType(type, elt, ajavaTypes); |
| if (currentFileAjavaTypes != null) { |
| type = mergeAnnotationFileAnnosIntoType(type, elt, currentFileAjavaTypes); |
| } |
| |
| if (checker.hasOption("mergeStubsWithSource")) { |
| if (debugStubParser) { |
| System.out.printf("fromElement: mergeStubsIntoType(%s, %s)", type, elt); |
| } |
| type = mergeAnnotationFileAnnosIntoType(type, elt, stubTypes); |
| if (debugStubParser) { |
| System.out.printf(" => %s%n", type); |
| } |
| } |
| // Caching is disabled if annotation files are being parsed, because calls to this |
| // method before the annotation files are fully read can return incorrect results. |
| if (shouldCache |
| && !stubTypes.isParsing() |
| && !ajavaTypes.isParsing() |
| && (currentFileAjavaTypes == null || !currentFileAjavaTypes.isParsing())) { |
| elementCache.put(elt, type.deepCopy()); |
| } |
| return type; |
| } |
| |
| /** |
| * 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. |
| * |
| * <p>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. |
| * |
| * <p>The returned AnnotatedTypeMirror also contains explicitly written annotations from any ajava |
| * file and if {@code -AmergeStubsWithSource} is passed, it also merges any explicitly written |
| * annotations from stub files. |
| * |
| * @param tree MethodTree or VariableTree |
| * @return AnnotatedTypeMirror with explicit annotations from {@code tree} |
| */ |
| private final AnnotatedTypeMirror fromMember(Tree tree) { |
| if (!(tree instanceof MethodTree || tree instanceof VariableTree)) { |
| throw new BugInCF( |
| "AnnotatedTypeFactory.fromMember: not a method or variable declaration: " + tree); |
| } |
| if (shouldCache && fromMemberTreeCache.containsKey(tree)) { |
| return fromMemberTreeCache.get(tree).deepCopy(); |
| } |
| AnnotatedTypeMirror result = TypeFromTree.fromMember(this, tree); |
| |
| result = mergeAnnotationFileAnnosIntoType(result, tree, ajavaTypes); |
| if (currentFileAjavaTypes != null) { |
| result = mergeAnnotationFileAnnosIntoType(result, tree, currentFileAjavaTypes); |
| } |
| |
| if (checker.hasOption("mergeStubsWithSource")) { |
| if (debugStubParser) { |
| System.out.printf("fromClass: mergeStubsIntoType(%s, %s)", result, tree); |
| } |
| result = mergeAnnotationFileAnnosIntoType(result, tree, stubTypes); |
| if (debugStubParser) { |
| System.out.printf(" => %s%n", result); |
| } |
| } |
| |
| if (shouldCache) { |
| fromMemberTreeCache.put(tree, result.deepCopy()); |
| } |
| |
| return result; |
| } |
| |
| /** |
| * Merges types from annotation files for {@code tree} into {@code type} by taking the greatest |
| * lower bound of the annotations in both. |
| * |
| * @param type the type to apply annotation file types to |
| * @param tree the tree from which to read annotation file types |
| * @param source storage for current annotation file annotations |
| * @return the given type, side-effected to add the annotation file types |
| */ |
| private AnnotatedTypeMirror mergeAnnotationFileAnnosIntoType( |
| @Nullable AnnotatedTypeMirror type, Tree tree, AnnotationFileElementTypes source) { |
| Element elt = TreeUtils.elementFromTree(tree); |
| return mergeAnnotationFileAnnosIntoType(type, elt, source); |
| } |
| |
| /** |
| * A scanner used to combine annotations from two AnnotatedTypeMirrors. The scanner requires |
| * {@link #qualHierarchy}, which is set in {@link #postInit()} rather than the construtor, so |
| * lazily initialize this field before use. |
| */ |
| private @MonotonicNonNull AnnotatedTypeCombiner annotatedTypeCombiner = null; |
| |
| /** |
| * Merges types from annotation files for {@code elt} into {@code type} by taking the greatest |
| * lower bound of the annotations in both. |
| * |
| * @param type the type to apply annotation file types to |
| * @param elt the element from which to read annotation file types |
| * @param source storage for current annotation file annotations |
| * @return the type, side-effected to add the annotation file types |
| */ |
| protected AnnotatedTypeMirror mergeAnnotationFileAnnosIntoType( |
| @Nullable AnnotatedTypeMirror type, Element elt, AnnotationFileElementTypes source) { |
| AnnotatedTypeMirror typeFromFile = source.getAnnotatedTypeMirror(elt); |
| if (typeFromFile == null) { |
| return type; |
| } |
| if (type == null) { |
| return typeFromFile; |
| } |
| if (annotatedTypeCombiner == null) { |
| annotatedTypeCombiner = new AnnotatedTypeCombiner(qualHierarchy); |
| } |
| // Must merge (rather than only take the annotation file type if it is a subtype) to support |
| // WPI. |
| annotatedTypeCombiner.visit(typeFromFile, type); |
| return type; |
| } |
| |
| /** |
| * Creates an AnnotatedTypeMirror for an ExpressionTree. The AnnotatedTypeMirror contains explicit |
| * annotations written on the expression and for some expressions, annotations from |
| * sub-expressions that could have been explicitly written, defaulted, refined, or otherwise |
| * computed. (Expression whose type include annotations from sub-expressions are: ArrayAccessTree, |
| * ConditionalExpressionTree, IdentifierTree, MemberSelectTree, and MethodInvocationTree.) |
| * |
| * <p>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 && fromExpressionTreeCache.containsKey(tree)) { |
| return fromExpressionTreeCache.get(tree).deepCopy(); |
| } |
| |
| AnnotatedTypeMirror result = TypeFromTree.fromExpression(this, tree); |
| |
| if (shouldCache |
| && tree.getKind() != Tree.Kind.NEW_CLASS |
| && tree.getKind() != Kind.NEW_ARRAY |
| && tree.getKind() != Kind.CONDITIONAL_EXPRESSION) { |
| // Don't cache the type of some expressions, because incorrect annotations would be |
| // cached during dataflow analysis. See Issue #602. |
| fromExpressionTreeCache.put(tree, result.deepCopy()); |
| } |
| return result; |
| } |
| |
| /** |
| * Creates an AnnotatedTypeMirror for the tree. The AnnotatedTypeMirror contains annotations |
| * explicitly written on the tree. It also adds type arguments to raw types that include |
| * annotations from the element declaration of the type {@link #fromElement(Element)}. |
| * |
| * <p>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 && fromTypeTreeCache.containsKey(tree)) { |
| return fromTypeTreeCache.get(tree).deepCopy(); |
| } |
| |
| AnnotatedTypeMirror result = TypeFromTree.fromTypeTree(this, tree); |
| |
| if (shouldCache) { |
| fromTypeTreeCache.put(tree, result.deepCopy()); |
| } |
| return result; |
| } |
| |
| // ********************************************************************** |
| // Customization methods meant to be overridden by subclasses to include |
| // defaulted annotations |
| // ********************************************************************** |
| |
| /** |
| * Changes annotations on a type obtained from a {@link Tree}. By default, this method does |
| * nothing. GenericAnnotatedTypeFactory uses this method to implement defaulting and inference |
| * (flow-sensitive type refinement). Its subclasses usually override it only to customize default |
| * annotations. |
| * |
| * <p>Subclasses that override this method should also override {@link |
| * #addComputedTypeAnnotations(Element, AnnotatedTypeMirror)}. |
| * |
| * <p>In classes that extend {@link GenericAnnotatedTypeFactory}, override {@link |
| * GenericAnnotatedTypeFactory#addComputedTypeAnnotations(Tree, AnnotatedTypeMirror, boolean)} |
| * instead of this method. |
| * |
| * @param tree an AST node |
| * @param type the type obtained from {@code tree} |
| */ |
| protected void addComputedTypeAnnotations(Tree tree, AnnotatedTypeMirror type) { |
| // Pass. |
| } |
| |
| /** |
| * Changes annotations on a type obtained from an {@link Element}. By default, this method does |
| * nothing. GenericAnnotatedTypeFactory uses this method to implement defaulting. |
| * |
| * <p>Subclasses that override this method should also override {@link |
| * #addComputedTypeAnnotations(Tree, AnnotatedTypeMirror)}. |
| * |
| * @param elt an element |
| * @param type the type obtained from {@code elt} |
| */ |
| protected void addComputedTypeAnnotations(Element elt, AnnotatedTypeMirror type) { |
| // Pass. |
| } |
| |
| /** |
| * Adds default annotations to {@code type}. This method should only be used in places where the |
| * correct annotations cannot be computed because of uninferred type arguments. (See {@link |
| * AnnotatedWildcardType#isUninferredTypeArgument()}.) |
| * |
| * @param type annotated type to which default annotations are added |
| */ |
| public void addDefaultAnnotations(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. |
| * |
| * <p>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<? extends AnnotatedTypeMirror> supertypes) { |
| // Use the effective annotations here to get the correct annotations |
| // for type variables and wildcards. |
| Set<AnnotationMirror> 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) { |
| if (element.getKind() == ElementKind.FIELD) { |
| addAnnotationFromFieldInvariant(type, owner, (VariableElement) element); |
| } |
| addComputedTypeAnnotations(element, type); |
| } |
| |
| /** |
| * Adds the qualifier specified by a field invariant for {@code field} to {@code type}. |
| * |
| * @param type annotated type to which the annotation is added |
| * @param accessedVia the annotated type of the receiver of the accessing tree. (Only used to get |
| * the type element of the underling type.) |
| * @param field element representing the field |
| */ |
| protected void addAnnotationFromFieldInvariant( |
| AnnotatedTypeMirror type, AnnotatedTypeMirror accessedVia, VariableElement field) { |
| TypeMirror declaringType = accessedVia.getUnderlyingType(); |
| // Find the first upper bound that isn't a wildcard or type variable |
| while (declaringType.getKind() == TypeKind.WILDCARD |
| || declaringType.getKind() == TypeKind.TYPEVAR) { |
| if (declaringType.getKind() == TypeKind.WILDCARD) { |
| declaringType = TypesUtils.wildUpperBound(declaringType, processingEnv); |
| } else if (declaringType.getKind() == TypeKind.TYPEVAR) { |
| declaringType = ((TypeVariable) declaringType).getUpperBound(); |
| } |
| } |
| TypeElement typeElement = TypesUtils.getTypeElement(declaringType); |
| if (ElementUtils.enclosingTypeElement(field).equals(typeElement)) { |
| // If the field is declared in the accessedVia class, then the field in the invariant |
| // cannot be this field, even if the field has the same name. |
| return; |
| } |
| |
| FieldInvariants invariants = getFieldInvariants(typeElement); |
| if (invariants == null) { |
| return; |
| } |
| List<AnnotationMirror> invariantAnnos = invariants.getQualifiersFor(field.getSimpleName()); |
| type.replaceAnnotations(invariantAnnos); |
| } |
| |
| /** |
| * Returns the field invariants for the given class, as expressed by the user in {@link |
| * FieldInvariant @FieldInvariant} method annotations. |
| * |
| * <p>Subclasses may implement their own field invariant annotations if {@link |
| * FieldInvariant @FieldInvariant} is not expressive enough. They must override this method to |
| * properly create AnnotationMirror and also override {@link |
| * #getFieldInvariantDeclarationAnnotations()} to return their field invariants. |
| * |
| * @param element class for which to get invariants |
| * @return field invariants for {@code element} |
| */ |
| public FieldInvariants getFieldInvariants(TypeElement element) { |
| if (element == null) { |
| return null; |
| } |
| AnnotationMirror fieldInvarAnno = getDeclAnnotation(element, FieldInvariant.class); |
| if (fieldInvarAnno == null) { |
| return null; |
| } |
| List<String> fields = |
| AnnotationUtils.getElementValueArray( |
| fieldInvarAnno, fieldInvariantFieldElement, String.class); |
| List<@CanonicalName Name> classes = |
| AnnotationUtils.getElementValueClassNames(fieldInvarAnno, fieldInvariantQualifierElement); |
| List<AnnotationMirror> qualifiers = |
| CollectionsPlume.mapList( |
| (Name name) -> |
| // Calling AnnotationBuilder.fromName (which ignores elements/fields) is acceptable |
| // because @FieldInvariant does not handle classes with elements/fields. |
| AnnotationBuilder.fromName(elements, name), |
| classes); |
| if (qualifiers.size() == 1) { |
| while (fields.size() > qualifiers.size()) { |
| qualifiers.add(qualifiers.get(0)); |
| } |
| } |
| if (fields.size() != qualifiers.size()) { |
| // The user wrote a malformed @FieldInvariant annotation, so just return a malformed |
| // FieldInvariants object. The BaseTypeVisitor will issue an error. |
| return new FieldInvariants(fields, qualifiers); |
| } |
| |
| // Only keep qualifiers that are supported by this checker. (The other qualifiers cannot |
| // be checked by this checker, so they must be ignored.) |
| List<String> annotatedFields = new ArrayList<>(); |
| List<AnnotationMirror> supportedQualifiers = new ArrayList<>(); |
| for (int i = 0; i < fields.size(); i++) { |
| if (isSupportedQualifier(qualifiers.get(i))) { |
| annotatedFields.add(fields.get(i)); |
| supportedQualifiers.add(qualifiers.get(i)); |
| } |
| } |
| if (annotatedFields.isEmpty()) { |
| return null; |
| } |
| |
| return new FieldInvariants(annotatedFields, supportedQualifiers); |
| } |
| |
| /** |
| * Returns the AnnotationTree which is a use of one of the field invariant annotations (as |
| * specified via {@link #getFieldInvariantDeclarationAnnotations()}. If one isn't found, null is |
| * returned. |
| * |
| * @param annoTrees list of trees to search; the result is one of the list elements, or null |
| * @return the AnnotationTree that is a use of one of the field invariant annotations, or null if |
| * one isn't found |
| */ |
| public AnnotationTree getFieldInvariantAnnotationTree(List<? extends AnnotationTree> annoTrees) { |
| List<AnnotationMirror> annos = TreeUtils.annotationsFromTypeAnnotationTrees(annoTrees); |
| for (int i = 0; i < annos.size(); i++) { |
| for (Class<? extends Annotation> clazz : getFieldInvariantDeclarationAnnotations()) { |
| if (areSameByClass(annos.get(i), clazz)) { |
| return annoTrees.get(i); |
| } |
| } |
| } |
| return null; |
| } |
| |
| /** Returns the set of classes of field invariant annotations. */ |
| protected Set<Class<? extends Annotation>> getFieldInvariantDeclarationAnnotations() { |
| return Collections.singleton(FieldInvariant.class); |
| } |
| |
| /** |
| * 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 |
| * |
| * <pre>{@code class C<X extends @Peer Object> }</pre> |
| * |
| * then the instantiation |
| * |
| * <pre>{@code @Rep C<@Rep Object> }</pre> |
| * |
| * is legal. The upper bounds of class C have to be adapted by the main modifier. |
| * |
| * <p>An example of an adaptation follows. Suppose, I have a declaration: |
| * |
| * <pre>{@code class MyClass<E extends List<E>>}</pre> |
| * |
| * And an instantiation: |
| * |
| * <pre>{@code new MyClass<@NonNull String>()}</pre> |
| * |
| * <p>The upper bound of E adapted to the argument String, would be {@code List<@NonNull String>} |
| * and the lower bound would be an AnnotatedNullType. |
| * |
| * <p>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<AnnotatedTypeParameterBounds> typeVariablesFromUse( |
| AnnotatedDeclaredType type, TypeElement element) { |
| |
| AnnotatedDeclaredType generic = getAnnotatedType(element); |
| List<AnnotatedTypeMirror> targs = type.getTypeArguments(); |
| List<AnnotatedTypeMirror> 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<TypeVariable, AnnotatedTypeMirror> mapping = new HashMap<>(); |
| |
| AnnotatedDeclaredType enclosing = type; |
| while (enclosing != null) { |
| List<AnnotatedTypeMirror> enclosingTArgs = enclosing.getTypeArguments(); |
| AnnotatedDeclaredType declaredType = |
| getAnnotatedType((TypeElement) enclosing.getUnderlyingType().asElement()); |
| List<AnnotatedTypeMirror> enclosingTVars = declaredType.getTypeArguments(); |
| for (int i = 0; i < enclosingTArgs.size(); i++) { |
| AnnotatedTypeVariable enclosingTVar = (AnnotatedTypeVariable) enclosingTVars.get(i); |
| mapping.put(enclosingTVar.getUnderlyingType(), enclosingTArgs.get(i)); |
| } |
| enclosing = enclosing.getEnclosingType(); |
| } |
| |
| List<AnnotatedTypeParameterBounds> res = new ArrayList<>(tvars.size()); |
| |
| 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; |
| } |
| |
| /** |
| * 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<? extends AnnotationMirror> annotations) { |
| final AnnotatedTypeMirror.AnnotatedNullType nullType = |
| (AnnotatedNullType) toAnnotatedType(processingEnv.getTypeUtils().getNullType(), false); |
| nullType.addAnnotations(annotations); |
| return nullType; |
| } |
| |
| // ********************************************************************** |
| // Utilities method for getting specific types from trees or elements |
| // ********************************************************************** |
| |
| /** |
| * Return the implicit receiver type of an expression tree. |
| * |
| * <p>The result is null for expressions that don't have a receiver, e.g. for a local variable or |
| * method parameter access. The result is also null for expressions that have an explicit |
| * receiver. |
| * |
| * <p>Clients should generally call {@link #getReceiverType}. |
| * |
| * @param tree the expression that might have an implicit receiver |
| * @return the type of the implicit receiver |
| */ |
| protected @Nullable 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(); |
| |
| // Return null if the element kind has no receiver. |
| Element element = TreeUtils.elementFromTree(tree); |
| assert element != null : "Unexpected null element for tree: " + tree; |
| if (!ElementUtils.hasReceiver(element)) { |
| return null; |
| } |
| |
| // Return null if the receiver is explicit. |
| if (TreeUtils.getReceiverTree(tree) != null) { |
| return null; |
| } |
| |
| TypeElement elementOfImplicitReceiver = ElementUtils.enclosingTypeElement(element); |
| if (tree.getKind() == Kind.NEW_CLASS) { |
| if (elementOfImplicitReceiver.getEnclosingElement() != null) { |
| elementOfImplicitReceiver = |
| ElementUtils.enclosingTypeElement(elementOfImplicitReceiver.getEnclosingElement()); |
| } else { |
| elementOfImplicitReceiver = null; |
| } |
| if (elementOfImplicitReceiver == null) { |
| // If the typeElt does not have an enclosing class, then the NewClassTree |
| // does not have an implicit receiver. |
| return null; |
| } |
| } |
| |
| TypeMirror typeOfImplicitReceiver = elementOfImplicitReceiver.asType(); |
| AnnotatedDeclaredType thisType = getSelfType(tree); |
| if (thisType == null) { |
| return null; |
| } |
| // An implicit receiver is the first enclosing type that is a subtype of the type where |
| // element is declared. |
| while (!isSubtype(thisType.getUnderlyingType(), typeOfImplicitReceiver)) { |
| thisType = thisType.getEnclosingType(); |
| } |
| return thisType; |
| } |
| |
| /** |
| * Returns the type of {@code this} at the location of {@code tree}. Returns {@code null} if |
| * {@code tree} is in a location where {@code this} has no meaning, such as the body of a static |
| * method. |
| * |
| * <p>The parameter is an arbitrary tree and does not have to mention "this", neither explicitly |
| * nor implicitly. This method can be overridden for type-system specific behavior. |
| * |
| * @param tree location used to decide the type of {@code this} |
| * @return the type of {@code this} at the location of {@code tree} |
| */ |
| public @Nullable AnnotatedDeclaredType getSelfType(Tree tree) { |
| if (TreeUtils.isClassTree(tree)) { |
| return getAnnotatedType(TreeUtils.elementFromDeclaration((ClassTree) tree)); |
| } |
| |
| Tree enclosingTree = getEnclosingClassOrMethod(tree); |
| if (enclosingTree == null) { |
| // tree is inside an annotation, where "this" is not allowed. So, no self type exists. |
| return null; |
| } else if (enclosingTree.getKind() == Kind.METHOD) { |
| MethodTree enclosingMethod = (MethodTree) enclosingTree; |
| if (TreeUtils.isConstructor(enclosingMethod)) { |
| return (AnnotatedDeclaredType) getAnnotatedType(enclosingMethod).getReturnType(); |
| } else { |
| return getAnnotatedType(enclosingMethod).getReceiverType(); |
| } |
| } else if (TreeUtils.isClassTree(enclosingTree)) { |
| return (AnnotatedDeclaredType) getAnnotatedType(enclosingTree); |
| } |
| return null; |
| } |
| |
| /** A set containing class, method, and annotation tree kinds. */ |
| private static final Set<Tree.Kind> classMethodAnnotationKinds = |
| EnumSet.copyOf(TreeUtils.classTreeKinds()); |
| |
| static { |
| classMethodAnnotationKinds.add(Kind.METHOD); |
| classMethodAnnotationKinds.add(Kind.TYPE_ANNOTATION); |
| classMethodAnnotationKinds.add(Kind.ANNOTATION); |
| } |
| |
| /** |
| * Returns the innermost enclosing method or class tree of {@code tree}. If {@code tree} is |
| * artificial (that is, created by dataflow), then {@link #artificialTreeToEnclosingElementMap} is |
| * used to find the enclosing tree. |
| * |
| * <p>If the tree is inside an annotation, then {@code null} is returned. |
| * |
| * @param tree tree to whose innermost enclosing method or class to return |
| * @return the innermost enclosing method or class tree of {@code tree}, or {@code null} if {@code |
| * tree} is inside an annotation |
| */ |
| public @Nullable Tree getEnclosingClassOrMethod(Tree tree) { |
| TreePath path = getPath(tree); |
| Tree enclosing = TreePathUtil.enclosingOfKind(path, classMethodAnnotationKinds); |
| if (enclosing != null) { |
| if (enclosing.getKind() == Kind.ANNOTATION || enclosing.getKind() == Kind.TYPE_ANNOTATION) { |
| return null; |
| } |
| return enclosing; |
| } |
| Element e = getEnclosingElementForArtificialTree(tree); |
| if (e != null) { |
| Element enclosingMethodOrClass = e; |
| while (enclosingMethodOrClass != null |
| && enclosingMethodOrClass.getKind() != ElementKind.METHOD |
| && !enclosingMethodOrClass.getKind().isClass() |
| && !enclosingMethodOrClass.getKind().isInterface()) { |
| enclosingMethodOrClass = enclosingMethodOrClass.getEnclosingElement(); |
| } |
| return declarationFromElement(enclosingMethodOrClass); |
| } |
| return getCurrentClassTree(tree); |
| } |
| |
| /** |
| * Returns the {@link AnnotatedTypeMirror} of the enclosing type at the location of {@code tree} |
| * that is the same type as {@code typeElement}. |
| * |
| * @param typeElement type of the enclosing type to return |
| * @param tree location to use |
| * @return the enclosing type at the location of {@code tree} that is the same type as {@code |
| * typeElement} |
| */ |
| public AnnotatedDeclaredType getEnclosingType(TypeElement typeElement, Tree tree) { |
| AnnotatedDeclaredType thisType = getSelfType(tree); |
| while (!isSameType(thisType.getUnderlyingType(), typeElement.asType())) { |
| thisType = thisType.getEnclosingType(); |
| } |
| return thisType; |
| } |
| |
| /** |
| * Returns true if the erasure of {@code type1} is a subtype of the erasure of {@code type2}. |
| * |
| * @param type1 a type |
| * @param type2 a type |
| * @return true if the erasure of {@code type1} is a subtype of the erasure of {@code type2} |
| */ |
| private boolean isSubtype(TypeMirror type1, TypeMirror type2) { |
| return types.isSubtype(types.erasure(type1), types.erasure(type2)); |
| } |
| |
| /** |
| * Returns true if the erasure of {@code type1} is the same type as the erasure of {@code type2}. |
| * |
| * @param type1 a type |
| * @param type2 a type |
| * @return true if the erasure of {@code type1} is the same type as the erasure of {@code type2} |
| */ |
| private boolean isSameType(TypeMirror type1, TypeMirror type2) { |
| return types.isSameType(types.erasure(type1), types.erasure(type2)); |
| } |
| |
| /** |
| * Returns the receiver type of the expression tree, which might be the type of an implicit {@code |
| * this}. Returns null if the expression has no explicit or implicit receiver. |
| * |
| * @param expression the expression for which to determine the receiver type |
| * @return the type of the receiver of expression |
| */ |
| public final AnnotatedTypeMirror getReceiverType(ExpressionTree expression) { |
| ExpressionTree receiver = TreeUtils.getReceiverTree(expression); |
| if (receiver != null) { |
| return getAnnotatedType(receiver); |
| } |
| |
| Element element = TreeUtils.elementFromUse(expression); |
| if (element != null && ElementUtils.hasReceiver(element)) { |
| // The tree references an element that has a receiver, but the tree does not have an explicit |
| // receiver. So, the tree must have an implicit receiver of "this" or "Outer.this". |
| return getImplicitReceiverType(expression); |
| } else { |
| return null; |
| } |
| } |
| |
| /** The type for an instantiated generic method or constructor. */ |
| public static class ParameterizedExecutableType { |
| /** The method's/constructor's type. */ |
| public final AnnotatedExecutableType executableType; |
| /** The types of the generic type arguments. */ |
| public final List<AnnotatedTypeMirror> typeArgs; |
| /** Create a ParameterizedExecutableType. */ |
| public ParameterizedExecutableType( |
| AnnotatedExecutableType executableType, List<AnnotatedTypeMirror> typeArgs) { |
| this.executableType = executableType; |
| this.typeArgs = typeArgs; |
| } |
| |
| @Override |
| public String toString() { |
| if (typeArgs.isEmpty()) { |
| return executableType.toString(); |
| } else { |
| StringJoiner typeArgsString = new StringJoiner(",", "<", ">"); |
| for (AnnotatedTypeMirror atm : typeArgs) { |
| typeArgsString.add(atm.toString()); |
| } |
| return typeArgsString + " " + executableType.toString(); |
| } |
| } |
| } |
| |
| /** |
| * Determines the type of the invoked method based on the passed method invocation tree. |
| * |
| * <p>The returned method type has all type variables resolved, whether based on receiver type, |
| * passed type parameters if any, and method invocation parameter. |
| * |
| * <p>Subclasses may override this method to customize inference of types or qualifiers based on |
| * method invocation parameters. |
| * |
| * <p>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. |
| * |
| * <p>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} for the checks of type |
| * argument well-formedness. |
| * |
| * <p>Note that "this" and "super" constructor invocations are also handled by this method |
| * (explicit or implicit ones, at the beginning of a constructor). 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 ParameterizedExecutableType methodFromUse(MethodInvocationTree tree) { |
| ExecutableElement methodElt = TreeUtils.elementFromUse(tree); |
| AnnotatedTypeMirror receiverType = getReceiverType(tree); |
| if (receiverType == null && TreeUtils.isSuperConstructorCall(tree)) { |
| // super() calls don't have a receiver, but they should be view-point adapted as if |
| // "this" is the receiver. |
| receiverType = getSelfType(tree); |
| } |
| |
| ParameterizedExecutableType result = methodFromUse(tree, methodElt, receiverType); |
| if (checker.shouldResolveReflection() |
| && reflectionResolver.isReflectiveMethodInvocation(tree)) { |
| result = reflectionResolver.resolveReflectiveCall(this, tree, result); |
| } |
| |
| AnnotatedExecutableType method = result.executableType; |
| if (method.getReturnType().getKind() == TypeKind.WILDCARD |
| && ((AnnotatedWildcardType) method.getReturnType()).isUninferredTypeArgument()) { |
| // Get the correct Java type from the tree and use it as the upper bound of the wildcard. |
| TypeMirror tm = TreeUtils.typeOf(tree); |
| AnnotatedTypeMirror t = toAnnotatedType(tm, false); |
| |
| AnnotatedWildcardType wildcard = (AnnotatedWildcardType) method.getReturnType(); |
| if (ignoreUninferredTypeArguments) { |
| // Remove the annotations so that default annotations are used instead. |
| // (See call to addDefaultAnnotations below.) |
| t.clearAnnotations(); |
| } else { |
| t.replaceAnnotations(wildcard.getExtendsBound().getAnnotations()); |
| } |
| wildcard.setExtendsBound(t); |
| addDefaultAnnotations(wildcard); |
| } |
| |
| return result; |
| } |
| |
| /** |
| * Determines the type of the invoked method based on the passed expression tree, executable |
| * element, and receiver type. |
| * |
| * @param tree either a MethodInvocationTree or a MemberReferenceTree |
| * @param methodElt the element of the referenced method |
| * @param receiverType the type of the receiver |
| * @return the method type being invoked with tree and the (inferred) type arguments |
| * @see #methodFromUse(MethodInvocationTree) |
| */ |
| public ParameterizedExecutableType methodFromUse( |
| ExpressionTree tree, ExecutableElement methodElt, AnnotatedTypeMirror receiverType) { |
| |
| AnnotatedExecutableType memberTypeWithoutOverrides = |
| getAnnotatedType(methodElt); // get unsubstituted type |
| AnnotatedExecutableType memberTypeWithOverrides = |
| (AnnotatedExecutableType) |
| applyFakeOverrides(receiverType, methodElt, memberTypeWithoutOverrides); |
| methodFromUsePreSubstitution(tree, memberTypeWithOverrides); |
| |
| AnnotatedExecutableType methodType = |
| AnnotatedTypes.asMemberOf(types, this, receiverType, methodElt, memberTypeWithOverrides); |
| List<AnnotatedTypeMirror> typeargs = new ArrayList<>(methodType.getTypeVariables().size()); |
| |
| Map<TypeVariable, AnnotatedTypeMirror> typeVarMapping = |
| AnnotatedTypes.findTypeArguments(processingEnv, this, tree, methodElt, methodType); |
| |
| if (!typeVarMapping.isEmpty()) { |
| for (AnnotatedTypeVariable tv : methodType.getTypeVariables()) { |
| if (typeVarMapping.get(tv.getUnderlyingType()) == null) { |
| throw new BugInCF( |
| "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.isMethodInvocation(tree, objectGetClass, processingEnv)) { |
| adaptGetClassReturnTypeToReceiver(methodType, receiverType); |
| } |
| |
| return new ParameterizedExecutableType(methodType, typeargs); |
| } |
| |
| /** |
| * Given a member and its type, returns the type with fake overrides applied to it. |
| * |
| * @param receiverType the type of the class that contains member (or a subtype of it) |
| * @param member a type member, such as a method or field |
| * @param memberType the type of {@code member} |
| * @return {@code memberType}, adjusted according to fake overrides |
| */ |
| private AnnotatedTypeMirror applyFakeOverrides( |
| AnnotatedTypeMirror receiverType, Element member, AnnotatedTypeMirror memberType) { |
| // Currently, handle only methods, not fields. TODO: Handle fields. |
| if (memberType.getKind() != TypeKind.EXECUTABLE) { |
| return memberType; |
| } |
| |
| AnnotationFileElementTypes afet = stubTypes; |
| AnnotatedExecutableType methodType = |
| (AnnotatedExecutableType) afet.getFakeOverride(member, receiverType); |
| if (methodType == null) { |
| methodType = (AnnotatedExecutableType) memberType; |
| } |
| return methodType; |
| } |
| |
| /** |
| * A callback method for the AnnotatedTypeFactory subtypes to customize the handling of the |
| * declared method type before type variable substitution. |
| * |
| * @param tree either a method invocation or a member reference tree |
| * @param type declared method type before type variable substitution |
| */ |
| protected void methodFromUsePreSubstitution(ExpressionTree tree, AnnotatedExecutableType type) { |
| assert tree instanceof MethodInvocationTree || tree instanceof MemberReferenceTree; |
| } |
| |
| /** |
| * Java special-cases the return type of {@link java.lang.Class#getClass() 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. For example: |
| * |
| * <ul> |
| * <li>{@code x.getClass()} has the type {@code Class< ? extends erasure_of_x >} |
| * <li>{@code someInteger.getClass()} has the type {@code Class< ? extends Integer >} |
| * </ul> |
| * |
| * @param getClassType this must be a type representing a call to Object.getClass otherwise a |
| * runtime exception will be thrown. It is modified by side effect. |
| * @param receiverType the receiver type of the method invocation (not the declared receiver type) |
| */ |
| protected void adaptGetClassReturnTypeToReceiver( |
| final AnnotatedExecutableType getClassType, final AnnotatedTypeMirror receiverType) { |
| // TODO: should the receiver type ever be a declaration?? |
| // Work on removing the asUse() call. |
| final AnnotatedTypeMirror newBound = receiverType.getErased().asUse(); |
| |
| final AnnotatedTypeMirror returnType = getClassType.getReturnType(); |
| if (returnType == null |
| || !(returnType.getKind() == TypeKind.DECLARED) |
| || ((AnnotatedDeclaredType) returnType).getTypeArguments().size() != 1) { |
| throw new BugInCF( |
| "Unexpected type passed to AnnotatedTypes.adaptGetClassReturnTypeToReceiver%n" |
| + "getClassType=%s%nreceiverType=%s", |
| getClassType, receiverType); |
| } |
| |
| final AnnotatedDeclaredType returnAdt = (AnnotatedDeclaredType) getClassType.getReturnType(); |
| final List<AnnotatedTypeMirror> 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); |
| Set<AnnotationMirror> newAnnos = AnnotationUtils.createAnnotationSet(); |
| Set<AnnotationMirror> typeBoundAnnos = getTypeDeclarationBounds(newBound.getUnderlyingType()); |
| Set<AnnotationMirror> wildcardBoundAnnos = classWildcardArg.getExtendsBound().getAnnotations(); |
| for (AnnotationMirror typeBoundAnno : typeBoundAnnos) { |
| AnnotationMirror wildcardAnno = |
| qualHierarchy.findAnnotationInSameHierarchy(wildcardBoundAnnos, typeBoundAnno); |
| if (qualHierarchy.isSubtype(typeBoundAnno, wildcardAnno)) { |
| newAnnos.add(typeBoundAnno); |
| } else { |
| newAnnos.add(wildcardAnno); |
| } |
| } |
| newBound.replaceAnnotations(newAnnos); |
| |
| classWildcardArg.setExtendsBound(newBound); |
| } |
| |
| /** |
| * Return the element type of {@code expression}. This is usually the type of {@code |
| * expression.itertor().next()}. If {@code expression} is an array, it is the component type of |
| * the array. |
| * |
| * @param expression an expression whose type is an array or implements {@link Iterable} |
| * @return the type of {@code expression.itertor().next()} or if {@code expression} is an array, |
| * the component type of the array. |
| */ |
| public AnnotatedTypeMirror getIterableElementType(ExpressionTree expression) { |
| return getIterableElementType(expression, getAnnotatedType(expression)); |
| } |
| |
| /** |
| * Return the element type of {@code iterableType}. This is usually the type of {@code |
| * expression.itertor().next()}. If {@code expression} is an array, it is the component type of |
| * the array. |
| * |
| * @param expression an expression whose type is an array or implements {@link Iterable} |
| * @param iterableType the type of the expression |
| * @return the type of {@code expression.itertor().next()} or if {@code expression} is an array, |
| * the component type of the array. |
| */ |
| protected AnnotatedTypeMirror getIterableElementType( |
| ExpressionTree expression, AnnotatedTypeMirror iterableType) { |
| switch (iterableType.getKind()) { |
| case ARRAY: |
| return ((AnnotatedArrayType) iterableType).getComponentType(); |
| case WILDCARD: |
| return getIterableElementType( |
| expression, ((AnnotatedWildcardType) iterableType).getExtendsBound().deepCopy()); |
| case TYPEVAR: |
| return getIterableElementType( |
| expression, ((AnnotatedTypeVariable) iterableType).getUpperBound()); |
| case DECLARED: |
| AnnotatedDeclaredType dt = |
| AnnotatedTypes.asSuper(this, iterableType, this.iterableDeclType); |
| if (dt.getTypeArguments().isEmpty()) { |
| TypeElement e = ElementUtils.getTypeElement(processingEnv, Object.class); |
| return getAnnotatedType(e); |
| } else { |
| return dt.getTypeArguments().get(0); |
| } |
| |
| // TODO: Properly desugar Iterator.next(), which is needed if an annotated JDK has |
| // annotations on Iterator#next. |
| // The below doesn't work because methodFromUse() assumes that the expression tree |
| // matches the method element. |
| // TypeElement iteratorElement = |
| // ElementUtils.getTypeElement(processingEnv, Iterator.class); |
| // AnnotatedTypeMirror iteratorType = |
| // AnnotatedTypeMirror.createType(iteratorElement.asType(), this, false); |
| // Map<TypeVariable, AnnotatedTypeMirror> mapping = new HashMap<>(); |
| // mapping.put( |
| // (TypeVariable) iteratorElement.getTypeParameters().get(0).asType(), |
| // typeArg); |
| // iteratorType = typeVarSubstitutor.substitute(mapping, iteratorType); |
| // ExecutableElement next = |
| // TreeUtils.getMethod("java.util.Iterator", "next", 0, processingEnv); |
| // ParameterizedExecutableType m = methodFromUse(expression, next, iteratorType); |
| // return m.executableType.getReturnType(); |
| default: |
| throw new BugInCF( |
| "AnnotatedTypeFactory.getIterableElementType: not iterable type: " + iterableType); |
| } |
| } |
| |
| /** |
| * Determines the type of the invoked constructor based on the passed new class tree. |
| * |
| * <p>The returned method type has all type variables resolved, whether based on receiver type, |
| * passed type parameters if any, and constructor invocation parameter. |
| * |
| * <p>Subclasses may override this method to customize inference of types or qualifiers based on |
| * constructor invocation parameters. |
| * |
| * <p>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. |
| * |
| * <p>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} for the checks of type |
| * argument well-formedness. |
| * |
| * <p>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 ParameterizedExecutableType constructorFromUse(NewClassTree tree) { |
| AnnotatedTypeMirror type = fromNewClass(tree); |
| addComputedTypeAnnotations(tree, type); |
| |
| ExecutableElement ctor = TreeUtils.constructor(tree); |
| AnnotatedExecutableType con = getAnnotatedType(ctor); // get unsubstituted type |
| if (TreeUtils.hasSyntheticArgument(tree)) { |
| AnnotatedExecutableType t = |
| (AnnotatedExecutableType) getAnnotatedType(((JCNewClass) tree).constructor); |
| List<AnnotatedTypeMirror> p = new ArrayList<>(con.getParameterTypes().size() + 1); |
| p.add(t.getParameterTypes().get(0)); |
| p.addAll(1, con.getParameterTypes()); |
| t.setParameterTypes(p); |
| con = t; |
| } |
| |
| constructorFromUsePreSubstitution(tree, con); |
| |
| con = AnnotatedTypes.asMemberOf(types, this, type, ctor, con); |
| |
| Map<TypeVariable, AnnotatedTypeMirror> typeVarMapping = |
| AnnotatedTypes.findTypeArguments(processingEnv, this, tree, ctor, con); |
| |
| List<AnnotatedTypeMirror> typeargs = new ArrayList<>(con.getTypeVariables().size()); |
| if (typeVarMapping.isEmpty()) { |
| typeargs = Collections.emptyList(); |
| } else { |
| typeargs = |
| CollectionsPlume.mapList( |
| (AnnotatedTypeVariable tv) -> typeVarMapping.get(tv.getUnderlyingType()), |
| con.getTypeVariables()); |
| con = (AnnotatedExecutableType) typeVarSubstitutor.substitute(typeVarMapping, con); |
| } |
| |
| return new ParameterizedExecutableType(con, typeargs); |
| } |
| |
| /** |
| * A callback method for the AnnotatedTypeFactory subtypes to customize the handling of the |
| * declared constructor type before type variable substitution. |
| * |
| * @param tree a NewClassTree from constructorFromUse() |
| * @param type declared method type before type variable substitution |
| */ |
| protected void constructorFromUsePreSubstitution( |
| NewClassTree tree, AnnotatedExecutableType type) {} |
| |
| /** |
| * Returns the return type of the method {@code m}. |
| * |
| * @param m tree of a method declaration |
| * @return the return type of the method |
| */ |
| 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}. This |
| * implementation just calls {@link #getMethodReturnType(MethodTree)}, but subclasses may override |
| * this method to change the type based on the return statement. |
| * |
| * @param m tree of a method declaration |
| * @param r a return statement within method {@code m} |
| * @return the return type of the method {@code m} at the return statement {@code r} |
| */ |
| public AnnotatedTypeMirror getMethodReturnType(MethodTree m, ReturnTree r) { |
| return getMethodReturnType(m); |
| } |
| |
| /** |
| * Creates an AnnotatedDeclaredType for a NewClassTree. Only adds explicit annotations, unless |
| * newClassTree has a diamond operator. In that case, the annotations on the type arguments are |
| * inferred using the assignment context and contain defaults. |
| * |
| * <p>Also, fully annotates the enclosing type of the returned declared type. |
| * |
| * <p>(Subclass beside {@link GenericAnnotatedTypeFactory} should not override this method.) |
| * |
| * @param newClassTree NewClassTree |
| * @return AnnotatedDeclaredType |
| */ |
| public AnnotatedDeclaredType fromNewClass(NewClassTree newClassTree) { |
| |
| AnnotatedDeclaredType enclosingType = (AnnotatedDeclaredType) getReceiverType(newClassTree); |
| |
| // Diamond trees that are not anonymous classes. |
| if (TreeUtils.isDiamondTree(newClassTree) && newClassTree.getClassBody() == null) { |
| AnnotatedDeclaredType type = |
| (AnnotatedDeclaredType) toAnnotatedType(TreeUtils.typeOf(newClassTree), false); |
| if (((com.sun.tools.javac.code.Type) type.underlyingType) |
| .tsym |
| .getTypeParameters() |
| .nonEmpty()) { |
| Pair<Tree, AnnotatedTypeMirror> ctx = this.visitorState.getAssignmentContext(); |
| if (ctx != null) { |
| AnnotatedTypeMirror ctxtype = ctx.second; |
| fromNewClassContextHelper(type, ctxtype); |
| } else { |
| TreePath p = getPath(newClassTree); |
| AnnotatedTypeMirror ctxtype = TypeArgInferenceUtil.assignedTo(this, p); |
| if (ctxtype != null) { |
| fromNewClassContextHelper(type, ctxtype); |
| } else { |
| // give up trying and set to raw. |
| type.setWasRaw(); |
| } |
| } |
| } |
| AnnotatedDeclaredType fromTypeTree = |
| (AnnotatedDeclaredType) TypeFromTree.fromTypeTree(this, newClassTree.getIdentifier()); |
| type.replaceAnnotations(fromTypeTree.getAnnotations()); |
| type.setEnclosingType(enclosingType); |
| return type; |
| } else if (newClassTree.getClassBody() != null) { |
| AnnotatedDeclaredType type = |
| (AnnotatedDeclaredType) toAnnotatedType(TreeUtils.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<? extends AnnotationTree> annos = |
| newClassTree.getClassBody().getModifiers().getAnnotations(); |
| type.addAnnotations(TreeUtils.annotationsFromTypeAnnotationTrees(annos)); |
| type.setEnclosingType(enclosingType); |
| return type; |
| } else { |
| // If newClassTree does not create anonymous class, |
| // newClassTree.getIdentifier includes the explicit annotations in this location: |
| // new @HERE Class() |
| AnnotatedDeclaredType type = |
| (AnnotatedDeclaredType) TypeFromTree.fromTypeTree(this, newClassTree.getIdentifier()); |
| type.setEnclosingType(enclosingType); |
| return type; |
| } |
| } |
| |
| // 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<AnnotatedTypeMirror> oldArgs = type.getTypeArguments(); |
| List<AnnotatedTypeMirror> newArgs = adctx.getTypeArguments(); |
| for (int i = 0; i < type.getTypeArguments().size(); ++i) { |
| if (!types.isSubtype(newArgs.get(i).underlyingType, oldArgs.get(i).underlyingType)) { |
| // 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; |
| case WILDCARD: |
| // TODO: look at bounds of wildcard and see whether we can improve. |
| break; |
| default: |
| if (ctxtype.getKind().isPrimitive()) { |
| // See Issue 438. Ignore primitive types for diamond inference - a primitive |
| // type is never a suitable context anyway. |
| } else { |
| throw new BugInCF( |
| "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. |
| * |
| * <p>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; |
| } |
| |
| /** |
| * Return a primitive type: either the argument, or the result of unboxing it (which might affect |
| * its annotations). |
| * |
| * <p>Subclasses should override {@link #getUnboxedType} rather than this method. |
| * |
| * @param type a type: a primitive or boxed primitive |
| * @return the unboxed variant of the type |
| */ |
| public final AnnotatedPrimitiveType applyUnboxing(AnnotatedTypeMirror type) { |
| TypeMirror underlying = type.getUnderlyingType(); |
| if (TypesUtils.isPrimitive(underlying)) { |
| return (AnnotatedPrimitiveType) type; |
| } else if (TypesUtils.isBoxedPrimitive(underlying)) { |
| return getUnboxedType((AnnotatedDeclaredType) type); |
| } else { |
| throw new BugInCF("Bad argument to applyUnboxing: " + type); |
| } |
| } |
| |
| /** |
| * Returns the annotated primitive type of the given declared type if it is a boxed declared type. |
| * Otherwise, it throws <i>IllegalArgumentException</i> exception. |
| * |
| * <p>In the {@code AnnotatedTypeFactory} implementation, the returned type has the same primary |
| * annotations as the given type. Subclasses may override this behavior. |
| * |
| * @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 AnnotatedDeclaredType with underlying type String and annotations copied from type. |
| * Subclasses may change the annotations. |
| * |
| * @param type type to convert to String |
| * @return AnnotatedTypeMirror that results from converting type to a String type |
| */ |
| // TODO: Test that this is called in all the correct locations |
| // See Issue #715 |
| // https://github.com/typetools/checker-framework/issues/715 |
| public AnnotatedDeclaredType getStringType(AnnotatedTypeMirror type) { |
| TypeMirror stringTypeMirror = TypesUtils.typeFromClass(String.class, types, elements); |
| AnnotatedDeclaredType stringATM = |
| (AnnotatedDeclaredType) |
| AnnotatedTypeMirror.createType(stringTypeMirror, this, type.isDeclaration()); |
| stringATM.addAnnotations(type.getEffectiveAnnotations()); |
| return stringATM; |
| } |
| |
| /** |
| * Returns a widened type if applicable, otherwise returns its first argument. |
| * |
| * <p>Subclasses should override {@link #getWidenedAnnotations} rather than this method. |
| * |
| * @param exprType type to possibly widen |
| * @param widenedType type to possibly widen to; its annotations are ignored |
| * @return if widening is applicable, the result of converting {@code type} to the underlying type |
| * of {@code widenedType}; otherwise {@code type} |
| */ |
| public final AnnotatedTypeMirror getWidenedType( |
| AnnotatedTypeMirror exprType, AnnotatedTypeMirror widenedType) { |
| TypeKind exprKind = exprType.getKind(); |
| TypeKind widenedKind = widenedType.getKind(); |
| |
| if (!TypeKindUtils.isNumeric(widenedKind)) { |
| // The target type is not a numeric primitive, so primitive widening is not applicable. |
| return exprType; |
| } |
| |
| AnnotatedPrimitiveType exprPrimitiveType; |
| if (TypeKindUtils.isNumeric(exprKind)) { |
| exprPrimitiveType = (AnnotatedPrimitiveType) exprType; |
| } else if (TypesUtils.isNumericBoxed(exprType.getUnderlyingType())) { |
| exprPrimitiveType = getUnboxedType((AnnotatedDeclaredType) exprType); |
| } else { |
| return exprType; |
| } |
| |
| switch (TypeKindUtils.getPrimitiveConversionKind( |
| exprPrimitiveType.getKind(), widenedType.getKind())) { |
| case WIDENING: |
| return getWidenedPrimitive(exprPrimitiveType, widenedType.getUnderlyingType()); |
| case NARROWING: |
| return getNarrowedPrimitive(exprPrimitiveType, widenedType.getUnderlyingType()); |
| case SAME: |
| return exprType; |
| default: |
| throw new Error("unhandled PrimitiveConversionKind"); |
| } |
| } |
| |
| /** |
| * Applies widening if applicable, otherwise returns its first argument. |
| * |
| * <p>Subclasses should override {@link #getWidenedAnnotations} rather than this method. |
| * |
| * @param exprAnnos annotations to possibly widen |
| * @param exprTypeMirror type to possibly widen |
| * @param widenedType type to possibly widen to; its annotations are ignored |
| * @return if widening is applicable, the result of converting {@code type} to the underlying type |
| * of {@code widenedType}; otherwise {@code type} |
| */ |
| public final AnnotatedTypeMirror getWidenedType( |
| Set<AnnotationMirror> exprAnnos, TypeMirror exprTypeMirror, AnnotatedTypeMirror widenedType) { |
| AnnotatedTypeMirror exprType = toAnnotatedType(exprTypeMirror, false); |
| exprType.replaceAnnotations(exprAnnos); |
| return getWidenedType(exprType, widenedType); |
| } |
| |
| /** |
| * Returns an AnnotatedPrimitiveType with underlying type {@code widenedTypeMirror} and with |
| * annotations copied or adapted from {@code type}. |
| * |
| * @param type type to widen; a primitive or boxed primitive |
| * @param widenedTypeMirror underlying type for the returned type mirror; a primitive or boxed |
| * primitive (same boxing as {@code type}) |
| * @return result of converting {@code type} to {@code widenedTypeMirror} |
| */ |
| private AnnotatedPrimitiveType getWidenedPrimitive( |
| AnnotatedPrimitiveType type, TypeMirror widenedTypeMirror) { |
| AnnotatedPrimitiveType result = |
| (AnnotatedPrimitiveType) |
| AnnotatedTypeMirror.createType(widenedTypeMirror, this, type.isDeclaration()); |
| result.addAnnotations( |
| getWidenedAnnotations(type.getAnnotations(), type.getKind(), result.getKind())); |
| return result; |
| } |
| |
| /** |
| * Returns annotations applicable to type {@code narrowedTypeKind}, that are copied or adapted |
| * from {@code annos}. |
| * |
| * @param annos annotations to narrow, from a primitive or boxed primitive |
| * @param typeKind primitive type to narrow |
| * @param narrowedTypeKind target for the returned annotations; a primitive type that is narrower |
| * than {@code typeKind} (in the sense of JLS 5.1.3). |
| * @return result of converting {@code annos} from {@code typeKind} to {@code narrowedTypeKind} |
| */ |
| public Set<AnnotationMirror> getNarrowedAnnotations( |
| Set<AnnotationMirror> annos, TypeKind typeKind, TypeKind narrowedTypeKind) { |
| return annos; |
| } |
| |
| /** |
| * Returns annotations applicable to type {@code widenedTypeKind}, that are copied or adapted from |
| * {@code annos}. |
| * |
| * @param annos annotations to widen, from a primitive or boxed primitive |
| * @param typeKind primitive type to widen |
| * @param widenedTypeKind target for the returned annotations; a primitive type that is wider than |
| * {@code typeKind} (in the sense of JLS 5.1.2) |
| * @return result of converting {@code annos} from {@code typeKind} to {@code widenedTypeKind} |
| */ |
| public Set<AnnotationMirror> getWidenedAnnotations( |
| Set<AnnotationMirror> annos, TypeKind typeKind, TypeKind widenedTypeKind) { |
| return annos; |
| } |
| |
| /** |
| * Returns the types of the two arguments to the BinaryTree, accounting for widening and unboxing |
| * if applicable. |
| * |
| * @param node a binary tree |
| * @return the types of the two arguments |
| */ |
| public Pair<AnnotatedTypeMirror, AnnotatedTypeMirror> binaryTreeArgTypes(BinaryTree node) { |
| return binaryTreeArgTypes( |
| getAnnotatedType(node.getLeftOperand()), getAnnotatedType(node.getRightOperand())); |
| } |
| |
| /** |
| * Returns the types of the two arguments to the CompoundAssignmentTree, accounting for widening |
| * and unboxing if applicable. |
| * |
| * @param node a compound assignment tree |
| * @return the types of the two arguments |
| */ |
| public Pair<AnnotatedTypeMirror, AnnotatedTypeMirror> compoundAssignmentTreeArgTypes( |
| CompoundAssignmentTree node) { |
| return binaryTreeArgTypes( |
| getAnnotatedType(node.getVariable()), getAnnotatedType(node.getExpression())); |
| } |
| |
| /** |
| * Returns the types of the two arguments to a binarya operation, accounting for widening and |
| * unboxing if applicable. |
| * |
| * @param left the type of the left argument of a binary operation |
| * @param right the type of the right argument of a binary operation |
| * @return the types of the two arguments |
| */ |
| public Pair<AnnotatedTypeMirror, AnnotatedTypeMirror> binaryTreeArgTypes( |
| AnnotatedTypeMirror left, AnnotatedTypeMirror right) { |
| TypeKind resultTypeKind = |
| TypeKindUtils.widenedNumericType(left.getUnderlyingType(), right.getUnderlyingType()); |
| if (TypeKindUtils.isNumeric(resultTypeKind)) { |
| TypeMirror resultTypeMirror = types.getPrimitiveType(resultTypeKind); |
| AnnotatedPrimitiveType leftUnboxed = applyUnboxing(left); |
| AnnotatedPrimitiveType rightUnboxed = applyUnboxing(right); |
| AnnotatedPrimitiveType leftWidened = |
| (leftUnboxed.getKind() == resultTypeKind |
| ? leftUnboxed |
| : getWidenedPrimitive(leftUnboxed, resultTypeMirror)); |
| AnnotatedPrimitiveType rightWidened = |
| (rightUnboxed.getKind() == resultTypeKind |
| ? rightUnboxed |
| : getWidenedPrimitive(rightUnboxed, resultTypeMirror)); |
| return Pair.of(leftWidened, rightWidened); |
| } else { |
| return Pair.of(left, right); |
| } |
| } |
| |
| /** |
| * Returns AnnotatedPrimitiveType with underlying type {@code narrowedTypeMirror} and with |
| * annotations copied or adapted from {@code type}. |
| * |
| * <p>Currently this method is called only for primitives that are narrowed at assignments from |
| * literal ints, for example, {@code byte b = 1;}. All other narrowing conversions happen at |
| * typecasts. |
| * |
| * @param type type to narrow |
| * @param narrowedTypeMirror underlying type for the returned type mirror |
| * @return result of converting {@code type} to {@code narrowedTypeMirror} |
| */ |
| public AnnotatedPrimitiveType getNarrowedPrimitive( |
| AnnotatedPrimitiveType type, TypeMirror narrowedTypeMirror) { |
| AnnotatedPrimitiveType narrowed = |
| (AnnotatedPrimitiveType) |
| AnnotatedTypeMirror.createType(narrowedTypeMirror, this, type.isDeclaration()); |
| narrowed.addAnnotations(type.getAnnotations()); |
| return narrowed; |
| } |
| |
| /** |
| * Returns the VisitorState instance used by the factory to infer types. |
| * |
| * @return 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 {@link #getAnnotatedType(Tree)}. |
| * |
| * @see #getAnnotatedType(Tree) |
| */ |
| public final AnnotatedDeclaredType getAnnotatedType(ClassTree tree) { |
| return (AnnotatedDeclaredType) getAnnotatedType((Tree) tree); |
| } |
| |
| /** |
| * See {@link #getAnnotatedType(Tree)}. |
| * |
| * @see #getAnnotatedType(Tree) |
| */ |
| public final AnnotatedDeclaredType getAnnotatedType(NewClassTree tree) { |
| return (AnnotatedDeclaredType) getAnnotatedType((Tree) tree); |
| } |
| |
| /** |
| * See {@link #getAnnotatedType(Tree)}. |
| * |
| * @see #getAnnotatedType(Tree) |
| */ |
| public final AnnotatedArrayType getAnnotatedType(NewArrayTree tree) { |
| return (AnnotatedArrayType) getAnnotatedType((Tree) tree); |
| } |
| |
| /** |
| * See {@link #getAnnotatedType(Tree)}. |
| * |
| * @see #getAnnotatedType(Tree) |
| */ |
| public final AnnotatedExecutableType getAnnotatedType(MethodTree tree) { |
| return (AnnotatedExecutableType) getAnnotatedType((Tree) tree); |
| } |
| |
| /** |
| * See {@link #getAnnotatedType(Element)}. |
| * |
| * @see #getAnnotatedType(Element) |
| */ |
| public final AnnotatedDeclaredType getAnnotatedType(TypeElement elt) { |
| return (AnnotatedDeclaredType) getAnnotatedType((Element) elt); |
| } |
| |
| /** |
| * See {@link #getAnnotatedType(Element)}. |
| * |
| * @see #getAnnotatedType(Element) |
| */ |
| public final AnnotatedExecutableType getAnnotatedType(ExecutableElement elt) { |
| return (AnnotatedExecutableType) getAnnotatedType((Element) elt); |
| } |
| |
| /** |
| * See {@link #fromElement(Element)}. |
| * |
| * @see #fromElement(Element) |
| */ |
| public final AnnotatedDeclaredType fromElement(TypeElement elt) { |
| return (AnnotatedDeclaredType) fromElement((Element) elt); |
| } |
| |
| /** |
| * See {@link #fromElement(Element)}. |
| * |
| * @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 canonicalAnnotation 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 isSupportedQualifier(AnnotationUtils.annotationName(a)); |
| } |
| |
| /** |
| * Determines whether the given class is a part of the type system under which this type factory |
| * operates. |
| * |
| * @param clazz annotation class |
| * @return true if that class is a type qualifier in the type system under which this type factory |
| * operates, false otherwise |
| */ |
| public boolean isSupportedQualifier(Class<? extends Annotation> clazz) { |
| return getSupportedTypeQualifiers().contains(clazz); |
| } |
| |
| /** |
| * Determines whether the given class name is a part of the type system under which this type |
| * factory operates. |
| * |
| * @param className fully-qualified annotation class name |
| * @return true if that class name is a type qualifier in the type system under which this type |
| * factory operates, false otherwise |
| */ |
| public boolean isSupportedQualifier(String className) { |
| return getSupportedTypeQualifierNames().contains(className); |
| } |
| |
| /** |
| * Adds the annotation {@code aliasClass} as an alias for the canonical annotation {@code type} |
| * that will be used by the Checker Framework in the alias's place. |
| * |
| * <p>By specifying the alias/canonical relationship using this method, the elements of the alias |
| * are not preserved when the canonical annotation to use is constructed from the alias. If you |
| * want the elements to be copied over as well, use {@link #addAliasedTypeAnnotation(Class, Class, |
| * boolean, String...)}. |
| * |
| * @param aliasClass the class of the aliased annotation |
| * @param type the canonical annotation |
| * @deprecated use {@code addAliasedTypeAnnotation} |
| */ |
| @Deprecated // 2020-12-15 |
| protected void addAliasedAnnotation(Class<?> aliasClass, AnnotationMirror type) { |
| addAliasedTypeAnnotation(aliasClass, type); |
| } |
| |
| /** |
| * Adds the annotation {@code aliasClass} as an alias for the canonical annotation {@code |
| * canonicalAnno} that will be used by the Checker Framework in the alias's place. |
| * |
| * <p>By specifying the alias/canonical relationship using this method, the elements of the alias |
| * are not preserved when the canonical annotation to use is constructed from the alias. If you |
| * want the elements to be copied over as well, use {@link #addAliasedTypeAnnotation(Class, Class, |
| * boolean, String...)}. |
| * |
| * @param aliasClass the class of the aliased annotation |
| * @param canonicalAnno the canonical annotation |
| */ |
| protected void addAliasedTypeAnnotation(Class<?> aliasClass, AnnotationMirror canonicalAnno) { |
| if (getSupportedTypeQualifiers().contains(aliasClass)) { |
| throw new BugInCF( |
| "AnnotatedTypeFactory: alias %s should not be in type hierarchy for %s", |
| aliasClass, this.getClass().getSimpleName()); |
| } |
| addAliasedTypeAnnotation(aliasClass.getCanonicalName(), canonicalAnno); |
| } |
| |
| /** |
| * Adds the annotation, whose fully-qualified name is given by {@code aliasName}, as an alias for |
| * the canonical annotation {@code canonicalAnno} that will be used by the Checker Framework in |
| * the alias's place. |
| * |
| * <p>Use this method if the alias class is not necessarily on the classpath at Checker Framework |
| * compile and run time. Otherwise, use {@link #addAliasedTypeAnnotation(Class, AnnotationMirror)} |
| * which prevents the possibility of a typo in the class name. |
| * |
| * @param aliasName the canonical name of the aliased annotation |
| * @param canonicalAnno the canonical annotation |
| * @deprecated use {@link #addAliasedTypeAnnotation} |
| */ |
| // aliasName is annotated as @FullyQualifiedName because there is no way to confirm that the |
| // name of an external annotation is a canoncal name. |
| @Deprecated // 2020-12-15 |
| protected void addAliasedAnnotation( |
| @FullyQualifiedName String aliasName, AnnotationMirror canonicalAnno) { |
| addAliasedTypeAnnotation(aliasName, canonicalAnno); |
| } |
| |
| /** |
| * Adds the annotation, whose fully-qualified name is given by {@code aliasName}, as an alias for |
| * the canonical annotation {@code canonicalAnno} that will be used by the Checker Framework in |
| * the alias's place. |
| * |
| * <p>Use this method if the alias class is not necessarily on the classpath at Checker Framework |
| * compile and run time. Otherwise, use {@link #addAliasedTypeAnnotation(Class, AnnotationMirror)} |
| * which prevents the possibility of a typo in the class name. |
| * |
| * @param aliasName the canonical name of the aliased annotation |
| * @param canonicalAnno the canonical annotation |
| */ |
| // aliasName is annotated as @FullyQualifiedName because there is no way to confirm that the |
| // name of an external annotation is a canoncal name. |
| protected void addAliasedTypeAnnotation( |
| @FullyQualifiedName String aliasName, AnnotationMirror canonicalAnno) { |
| |
| aliases.put(aliasName, new Alias(aliasName, canonicalAnno, false, null, null)); |
| } |
| |
| /** |
| * Adds the annotation {@code aliasClass} as an alias for the canonical annotation {@code |
| * canonicalAnno} that will be used by the Checker Framework in the alias's place. |
| * |
| * <p>You may specify the copyElements flag to indicate whether you want the elements of the alias |
| * to be copied over when the canonical annotation is constructed as a copy of {@code |
| * canonicalAnno}. Be careful that the framework will try to copy the elements by name matching, |
| * so make sure that names and types of the elements to be copied over are exactly the same as the |
| * ones in the canonical annotation. Otherwise, an 'Couldn't find element in annotation' error is |
| * raised. |
| * |
| * <p>To facilitate the cases where some of the elements are ignored on purpose when constructing |
| * the canonical annotation, this method also provides a varargs {@code ignorableElements} for you |
| * to explicitly specify the ignoring rules. For example, {@code |
| * org.checkerframework.checker.index.qual.IndexFor} is an alias of {@code |
| * org.checkerframework.checker.index.qual.NonNegative}, but the element "value" of |
| * {@code @IndexFor} should be ignored when constructing {@code @NonNegative}. In the cases where |
| * all elements are ignored, we can simply use {@link #addAliasedTypeAnnotation(Class, |
| * AnnotationMirror)} instead. |
| * |
| * @param aliasClass the class of the aliased annotation |
| * @param canonical the canonical annotation |
| * @param copyElements a flag that indicates whether you want to copy the elements over when |
| * getting the alias from the canonical annotation |
| * @param ignorableElements a list of elements that can be safely dropped when the elements are |
| * being copied over |
| * @deprecated use {@code addAliasedTypeAnnotation} |
| */ |
| @Deprecated // 2020-12-15 |
| protected void addAliasedAnnotation( |
| Class<?> aliasClass, Class<?> canonical, boolean copyElements, String... ignorableElements) { |
| addAliasedTypeAnnotation(aliasClass, canonical, copyElements, ignorableElements); |
| } |
| |
| /** |
| * Adds the annotation {@code aliasClass} as an alias for the canonical annotation {@code |
| * canonicalClass} that will be used by the Checker Framework in the alias's place. |
| * |
| * <p>You may specify the copyElements flag to indicate whether you want the elements of the alias |
| * to be copied over when the canonical annotation is constructed as a copy of {@code |
| * canonicalClass}. Be careful that the framework will try to copy the elements by name matching, |
| * so make sure that names and types of the elements to be copied over are exactly the same as the |
| * ones in the canonical annotation. Otherwise, an 'Couldn't find element in annotation' error is |
| * raised. |
| * |
| * <p>To facilitate the cases where some of the elements are ignored on purpose when constructing |
| * the canonical annotation, this method also provides a varargs {@code ignorableElements} for you |
| * to explicitly specify the ignoring rules. For example, {@code |
| * org.checkerframework.checker.index.qual.IndexFor} is an alias of {@code |
| * org.checkerframework.checker.index.qual.NonNegative}, but the element "value" of |
| * {@code @IndexFor} should be ignored when constructing {@code @NonNegative}. In the cases where |
| * all elements are ignored, we can simply use {@link #addAliasedTypeAnnotation(Class, |
| * AnnotationMirror)} instead. |
| * |
| * @param aliasClass the class of the aliased annotation |
| * @param canonicalClass the class of the canonical annotation |
| * @param copyElements a flag that indicates whether you want to copy the elements over when |
| * getting the alias from the canonical annotation |
| * @param ignorableElements a list of elements that can be safely dropped when the elements are |
| * being copied over |
| */ |
| protected void addAliasedTypeAnnotation( |
| Class<?> aliasClass, |
| Class<?> canonicalClass, |
| boolean copyElements, |
| String... ignorableElements) { |
| if (getSupportedTypeQualifiers().contains(aliasClass)) { |
| throw new BugInCF( |
| "AnnotatedTypeFactory: alias %s should not be in type hierarchy for %s", |
| aliasClass, this.getClass().getSimpleName()); |
| } |
| addAliasedTypeAnnotation( |
| aliasClass.getCanonicalName(), canonicalClass, copyElements, ignorableElements); |
| } |
| |
| /** |
| * Adds the annotation, whose fully-qualified name is given by {@code aliasName}, as an alias for |
| * the canonical annotation {@code canonicalAnno} that will be used by the Checker Framework in |
| * the alias's place. |
| * |
| * <p>Use this method if the alias class is not necessarily on the classpath at Checker Framework |
| * compile and run time. Otherwise, use {@link #addAliasedTypeAnnotation(Class, Class, boolean, |
| * String[])} which prevents the possibility of a typo in the class name. |
| * |
| * @param aliasName the canonical name of the aliased class |
| * @param canonicalAnno the canonical annotation |
| * @param copyElements a flag that indicates whether we want to copy the elements over when |
| * getting the alias from the canonical annotation |
| * @param ignorableElements a list of elements that can be safely dropped when the elements are |
| * being copied over |
| */ |
| // aliasName is annotated as @FullyQualifiedName because there is no way to confirm that the |
| // name of an external annotation is a canoncal name. |
| protected void addAliasedTypeAnnotation( |
| @FullyQualifiedName String aliasName, |
| Class<?> canonicalAnno, |
| boolean copyElements, |
| String... ignorableElements) { |
| // The copyElements argument disambiguates overloading. |
| if (!copyElements) { |
| throw new BugInCF("Do not call with false"); |
| } |
| aliases.put( |
| aliasName, |
| new Alias( |
| aliasName, null, copyElements, canonicalAnno.getCanonicalName(), ignorableElements)); |
| } |
| |
| /** |
| * Returns the canonical annotation for the passed annotation. Returns null if the passed |
| * annotation is not an alias of a canonical one in the framework. |
| * |
| * <p>A canonical annotation is the internal annotation that will be used by the Checker Framework |
| * in the aliased annotation's place. |
| * |
| * @param a the qualifier to check for an alias |
| * @return the canonical annotation, or null if none exists |
| */ |
| public @Nullable AnnotationMirror canonicalAnnotation(AnnotationMirror a) { |
| TypeElement elem = (TypeElement) a.getAnnotationType().asElement(); |
| String qualName = elem.getQualifiedName().toString(); |
| Alias alias = aliases.get(qualName); |
| if (alias == null) { |
| return null; |
| } |
| if (alias.copyElements) { |
| AnnotationBuilder builder = new AnnotationBuilder(processingEnv, alias.canonicalName); |
| builder.copyElementValuesFromAnnotation(a, alias.ignorableElements); |
| return builder.build(); |
| } else { |
| return alias.canonical; |
| } |
| } |
| |
| /** |
| * Add the annotation {@code alias} as an alias for the declaration annotation {@code annotation}, |
| * where the annotation mirror {@code annotationToUse} will be used instead. If multiple calls are |
| * made with the same {@code annotation}, then the {@code annotationToUse} must be the same. |
| * |
| * <p>The point of {@code annotationToUse} is that it may include elements/fields. |
| */ |
| protected void addAliasedDeclAnnotation( |
| Class<? extends Annotation> alias, |
| Class<? extends Annotation> annotation, |
| AnnotationMirror annotationToUse) { |
| Pair<AnnotationMirror, Set<Class<? extends Annotation>>> pair = declAliases.get(annotation); |
| if (pair != null) { |
| if (!AnnotationUtils.areSame(annotationToUse, pair.first)) { |
| throw new BugInCF("annotationToUse should be the same: %s %s", pair.first, annotationToUse); |
| } |
| } else { |
| pair = Pair.of(annotationToUse, new HashSet<>()); |
| declAliases.put(annotation, pair); |
| } |
| Set<Class<? extends Annotation>> aliases = pair.second; |
| aliases.add(alias); |
| } |
| |
| /** |
| * 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. |
| * |
| * <p>Most users will want to use {@link #getAnnotatedType(Tree)} 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 (TreeUtils.typeOf(node) != null) { |
| AnnotatedTypeMirror result = toAnnotatedType(TreeUtils.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 + " [" + node.getClass().getSimpleName() + "]"; |
| |
| TypeMirror t = trees.getTypeMirror(path); |
| assert validType(t) : "Invalid type " + t + " for node " + t; |
| |
| AnnotatedTypeMirror result = toAnnotatedType(t, isDeclaration); |
| return result; |
| } |
| |
| /** |
| * Gets the declaration tree for the element, if the source is available. |
| * |
| * <p>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 TreePathUtil.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). |
| * |
| * <p>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) { |
| @SuppressWarnings("interning:assignment") // used for == test |
| @InternedDistinct MethodTree enclosingMethod = TreePathUtil.enclosingMethod(path); |
| ClassTree enclosingClass = TreePathUtil.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 = TreePathUtil.enclosingMethod(getPath(tree)); |
| return enclosingMethod != null && TreeUtils.isConstructor(enclosingMethod); |
| } |
| |
| /** |
| * Gets the path for the given {@link Tree} under the current root by checking from the visitor's |
| * current path, and using {@link Trees#getPath(CompilationUnitTree, Tree)} (which is much slower) |
| * only if {@code node} is not found on the current path. |
| * |
| * <p>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. Returns null if {@code node} is not |
| * within the current compilation unit. |
| */ |
| public final @Nullable TreePath getPath(@FindDistinct Tree node) { |
| assert root != null |
| : "AnnotatedTypeFactory.getPath(" |
| + node.getKind() |
| + "): root needs to be set when used on trees; factory: " |
| + this.getClass().getSimpleName(); |
| |
| if (node == null) { |
| return null; |
| } |
| |
| if (artificialTreeToEnclosingElementMap.containsKey(node)) { |
| return null; |
| } |
| |
| if (treePathCache.isCached(node)) { |
| return treePathCache.getPath(root, node); |
| } |
| |
| TreePath currentPath = visitorState.getPath(); |
| if (currentPath == null) { |
| TreePath path = TreePath.getPath(root, node); |
| treePathCache.addPath(node, path); |
| return path; |
| } |
| |
| // 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) { |
| treePathCache.addPath(node, currentPath); |
| 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(); |
| treePathCache.addPath(currentPath.getLeaf(), currentPath); |
| if (currentPath.getLeaf() == node) { |
| return currentPath; |
| } |
| if (currentPath.getParentPath() != null) { |
| currentPath = currentPath.getParentPath(); |
| treePathCache.addPath(currentPath.getLeaf(), currentPath); |
| if (currentPath.getLeaf() == node) { |
| return currentPath; |
| } |
| } |
| } |
| |
| final TreePath pathWithinSubtree = TreePath.getPath(currentPath, node); |
| if (pathWithinSubtree != null) { |
| treePathCache.addPath(node, pathWithinSubtree); |
| 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) { |
| treePathCache.addPath(current.getLeaf(), current); |
| 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. |
| * |
| * <p>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 getEnclosingElementForArtificialTree(Tree node) { |
| return artificialTreeToEnclosingElementMap.get(node); |
| } |
| |
| /** |
| * Adds the given mapping from a synthetic (generated) tree to its enclosing element. |
| * |
| * <p>See {@code |
| * org.checkerframework.framework.flow.CFCFGBuilder.CFCFGTranslationPhaseOne.handleArtificialTree(Tree)}. |
| * |
| * @param tree artifical tree |
| * @param enclosing element that encloses {@code tree} |
| */ |
| public final void setEnclosingElementForArtificialTree(Tree tree, Element enclosing) { |
| artificialTreeToEnclosingElementMap.put(tree, enclosing); |
| } |
| |
| /** |
| * 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; |
| } |
| 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 all annotation files in the following order: |
| * |
| * <ol> |
| * <li>jdk.astub in the same directory as the checker, if it exists and ignorejdkastub option is |
| * not supplied <br> |
| * <li>jdkN.astub, where N is the Java version in the same directory as the checker, if it |
| * exists and ignorejdkastub option is not supplied <br> |
| * <li>Stub files listed in @StubFiles annotation on the checker; must be in same directory as |
| * the checker<br> |
| * <li>Stub files provided via -Astubs compiler option |
| * <li>Ajava files provided via -Aajava compiler option |
| * </ol> |
| * |
| * <p>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. |
| * |
| * <p>The annotations are stored by side-effecting {@link #stubTypes} and {@link #ajavaTypes}. |
| */ |
| protected void parseAnnotationFiles() { |
| stubTypes.parseStubFiles(); |
| ajavaTypes.parseAjavaFiles(); |
| } |
| |
| /** |
| * Returns all of the declaration annotations whose name equals the passed annotation class (or is |
| * an alias for it) including annotations: |
| * |
| * <ul> |
| * <li>on the element |
| * <li>written in stubfiles |
| * <li>inherited from overriden methods, (see {@link InheritedAnnotation}) |
| * <li>inherited from superclasses or super interfaces (see {@link Inherited}) |
| * </ul> |
| * |
| * @see #getDeclAnnotationNoAliases |
| * @param elt the element to retrieve the declaration annotation from |
| * @param anno annotation class |
| * @return the annotation mirror for anno |
| */ |
| @Override |
| public final AnnotationMirror getDeclAnnotation(Element elt, Class<? extends Annotation> anno) { |
| return getDeclAnnotation(elt, anno, true); |
| } |
| |
| /** |
| * Returns the actual annotation mirror used to annotate this element, whose name equals the |
| * passed annotation class. Returns null if none exists. Does not check for aliases of the |
| * annotation class. |
| * |
| * <p>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 final AnnotationMirror getDeclAnnotationNoAliases( |
| Element elt, Class<? extends Annotation> anno) { |
| return getDeclAnnotation(elt, anno, 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 bytecode 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 ElementUtils.isElementFromByteCode(element); |
| } |
| |
| /** |
| * Returns true if redundancy between a stub file and bytecode should be reported. |
| * |
| * <p>For most type systems the default behavior of returning true is correct. For subcheckers, |
| * redundancy in one of the type hierarchies can be ok. Such implementations should return false. |
| * |
| * @return whether to warn about redundancy between a stub file and bytecode |
| */ |
| public boolean shouldWarnIfStubRedundantWithBytecode() { |
| return true; |
| } |
| |
| /** |
| * Returns the actual annotation mirror used to annotate this element, whose name equals the |
| * passed annotation class (or is an alias for it). Returns null if none exists. May return the |
| * canonical annotation that annotationName is an alias for. |
| * |
| * <p>This is the private implementation of the same-named, public method. |
| * |
| * <p>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 annoClass the class 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, Class<? extends Annotation> annoClass, boolean checkAliases) { |
| Set<AnnotationMirror> declAnnos = getDeclAnnotations(elt); |
| |
| for (AnnotationMirror am : declAnnos) { |
| if (areSameByClass(am, annoClass)) { |
| return am; |
| } |
| } |
| // Look through aliases. |
| if (checkAliases) { |
| Pair<AnnotationMirror, Set<Class<? extends Annotation>>> aliases = declAliases.get(annoClass); |
| if (aliases != null) { |
| for (Class<? extends Annotation> alias : aliases.second) { |
| for (AnnotationMirror am : declAnnos) { |
| if (areSameByClass(am, alias)) { |
| // TODO: need to copy over elements/fields |
| return aliases.first; |
| } |
| } |
| } |
| } |
| } |
| // Not found. |
| return null; |
| } |
| |
| /** |
| * Returns all of the declaration annotations on this element including annotations: |
| * |
| * <ul> |
| * <li>on the element |
| * <li>written in stubfiles |
| * <li>inherited from overriden methods, (see {@link InheritedAnnotation}) |
| * <li>inherited from superclasses or super interfaces (see {@link Inherited}) |
| * </ul> |
| * |
| * <p>This method returns the actual annotations not their aliases. {@link |
| * #getDeclAnnotation(Element, Class)} returns aliases. |
| * |
| * @param elt the element for which to determine annotations |
| * @return all of the declaration annotations on this element, written in stub files, or inherited |
| */ |
| public Set<AnnotationMirror> getDeclAnnotations(Element elt) { |
| Set<AnnotationMirror> cachedValue = cacheDeclAnnos.get(elt); |
| if (cachedValue != null) { |
| // Found in cache, return result. |
| return cachedValue; |
| } |
| |
| Set<AnnotationMirror> results = AnnotationUtils.createAnnotationSet(); |
| // Retrieving the annotations from the element. |
| // This includes annotations inherited from superclasses, but not superinterfaces or |
| // overriden methods. |
| List<? extends AnnotationMirror> fromEle = elements.getAllAnnotationMirrors(elt); |
| for (AnnotationMirror annotation : fromEle) { |
| try { |
| results.add(annotation); |
| } catch (com.sun.tools.javac.code.Symbol.CompletionFailure cf) { |
| // If a CompletionFailure occurs, issue a warning. |
| checker.reportWarning( |
| annotation.getAnnotationType().asElement(), |
| "annotation.not.completed", |
| ElementUtils.getQualifiedName(elt), |
| annotation); |
| } |
| } |
| |
| // If parsing annotation files, return only the annotations in the element. |
| if (stubTypes.isParsing() |
| || ajavaTypes.isParsing() |
| || (currentFileAjavaTypes != null && currentFileAjavaTypes.isParsing())) { |
| return results; |
| } |
| |
| // Add annotations from annotation files. |
| results.addAll(stubTypes.getDeclAnnotation(elt)); |
| results.addAll(ajavaTypes.getDeclAnnotation(elt)); |
| if (currentFileAjavaTypes != null) { |
| results.addAll(currentFileAjavaTypes.getDeclAnnotation(elt)); |
| } |
| |
| if (elt.getKind() == ElementKind.METHOD) { |
| // Retrieve the annotations from the overridden method's element. |
| inheritOverriddenDeclAnnos((ExecutableElement) elt, results); |
| } else if (ElementUtils.isTypeDeclaration(elt)) { |
| inheritOverriddenDeclAnnosFromTypeDecl(elt.asType(), results); |
| } |
| |
| // Add the element and its annotations to the cache. |
| cacheDeclAnnos.put(elt, results); |
| return results; |
| } |
| |
| /** |
| * Adds into {@code results} the inherited declaration annotations found in all elements of the |
| * super types of {@code typeMirror}. (Both superclasses and superinterfaces.) |
| * |
| * @param typeMirror type |
| * @param results set of AnnotationMirrors to which this method adds declarations annotations |
| */ |
| private void inheritOverriddenDeclAnnosFromTypeDecl( |
| TypeMirror typeMirror, Set<AnnotationMirror> results) { |
| List<? extends TypeMirror> superTypes = types.directSupertypes(typeMirror); |
| for (TypeMirror superType : superTypes) { |
| TypeElement elt = TypesUtils.getTypeElement(superType); |
| if (elt == null) { |
| continue; |
| } |
| Set<AnnotationMirror> superAnnos = getDeclAnnotations(elt); |
| for (AnnotationMirror annotation : superAnnos) { |
| List<? extends AnnotationMirror> 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.reportWarning( |
| annotation.getAnnotationType().asElement(), |
| "annotation.not.completed", |
| ElementUtils.getQualifiedName(elt), |
| annotation); |
| continue; |
| } |
| if (containsSameByClass(annotationsOnAnnotation, Inherited.class) |
| || AnnotationUtils.containsSameByName(inheritedAnnotations, annotation)) { |
| addOrMerge(results, annotation); |
| } |
| } |
| } |
| } |
| |
| /** |
| * 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<AnnotationMirror> results) { |
| Map<AnnotatedDeclaredType, ExecutableElement> overriddenMethods = |
| AnnotatedTypes.overriddenMethods(elements, this, elt); |
| |
| if (overriddenMethods != null) { |
| for (ExecutableElement superElt : overriddenMethods.values()) { |
| Set<AnnotationMirror> superAnnos = getDeclAnnotations(superElt); |
| |
| for (AnnotationMirror annotation : superAnnos) { |
| List<? extends AnnotationMirror> 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.reportWarning( |
| annotation.getAnnotationType().asElement(), |
| "annotation.not.completed", |
| ElementUtils.getQualifiedName(elt), |
| annotation); |
| continue; |
| } |
| if (containsSameByClass(annotationsOnAnnotation, InheritedAnnotation.class) |
| || AnnotationUtils.containsSameByName(inheritedAnnotations, annotation)) { |
| addOrMerge(results, annotation); |
| } |
| } |
| } |
| } |
| } |
| |
| private void addOrMerge(Set<AnnotationMirror> results, AnnotationMirror annotation) { |
| if (AnnotationUtils.containsSameByName(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.areSameByName(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 the element, which have a |
| * meta-annotation (i.e., an annotation on that annotation) with class {@code |
| * metaAnnotationClass}. |
| * |
| * @param element the element for which to determine annotations |
| * @param metaAnnotationClass the class of 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 (of type {@code |
| * metaAnnotationClass}) used to meta-annotate the declaration of {@code anno} |
| */ |
| public List<Pair<AnnotationMirror, AnnotationMirror>> getDeclAnnotationWithMetaAnnotation( |
| Element element, Class<? extends Annotation> metaAnnotationClass) { |
| List<Pair<AnnotationMirror, AnnotationMirror>> result = new ArrayList<>(); |
| Set<AnnotationMirror> annotationMirrors = getDeclAnnotations(element); |
| |
| for (AnnotationMirror candidate : annotationMirrors) { |
| List<? extends AnnotationMirror> metaAnnotationsOnAnnotation; |
| try { |
| metaAnnotationsOnAnnotation = |
| candidate.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.reportWarning( |
| candidate.getAnnotationType().asElement(), |
| "annotation.not.completed", |
| ElementUtils.getQualifiedName(element), |
| candidate); |
| continue; |
| } |
| // First call copier, if exception, continue normal modula laws. |
| for (AnnotationMirror ma : metaAnnotationsOnAnnotation) { |
| if (areSameByClass(ma, metaAnnotationClass)) { |
| // This candidate has the right kind of meta-annotation. |
| // It might be a real contract, or a list of contracts. |
| if (isListForRepeatedAnnotation(candidate)) { |
| @SuppressWarnings("deprecation") // concrete annotation class is not known |
| List<AnnotationMirror> wrappedCandidates = |
| AnnotationUtils.getElementValueArray( |
| candidate, "value", AnnotationMirror.class, false); |
| for (AnnotationMirror wrappedCandidate : wrappedCandidates) { |
| result.add(Pair.of(wrappedCandidate, ma)); |
| } |
| } else { |
| result.add(Pair.of(candidate, ma)); |
| } |
| } |
| } |
| } |
| return result; |
| } |
| |
| /** Cache for {@link #isListForRepeatedAnnotation}. */ |
| private final Map<DeclaredType, Boolean> isListForRepeatedAnnotationCache = new HashMap<>(); |
| |
| /** |
| * Returns true if the given annotation is a wrapper for multiple repeated annotations. |
| * |
| * @param a an annotation that might be a wrapper |
| * @return true if the argument is a wrapper for multiple repeated annotations |
| */ |
| private boolean isListForRepeatedAnnotation(AnnotationMirror a) { |
| DeclaredType annotationType = a.getAnnotationType(); |
| Boolean resultObject = isListForRepeatedAnnotationCache.get(annotationType); |
| if (resultObject != null) { |
| return resultObject; |
| } |
| boolean result = isListForRepeatedAnnotationImplementation(annotationType); |
| isListForRepeatedAnnotationCache.put(annotationType, result); |
| return result; |
| } |
| |
| /** |
| * Returns true if the annotation is a wrapper for multiple repeated annotations. |
| * |
| * @param annotationType the declaration of the annotation to test |
| * @return true if the annotation is a wrapper for multiple repeated annotations |
| */ |
| private boolean isListForRepeatedAnnotationImplementation(DeclaredType annotationType) { |
| TypeMirror enclosingType = annotationType.getEnclosingType(); |
| if (enclosingType == null) { |
| return false; |
| } |
| if (!annotationType.asElement().getSimpleName().contentEquals("List")) { |
| return false; |
| } |
| List<? extends Element> annoElements = annotationType.asElement().getEnclosedElements(); |
| if (annoElements.size() != 1) { |
| return false; |
| } |
| // TODO: should check that the type of the single element is: "array of enclosingType". |
| return true; |
| } |
| |
| /** |
| * 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 metaAnnotationClass}. |
| * |
| * @param element the element at which to look for annotations |
| * @param metaAnnotationClass the class of 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<Pair<AnnotationMirror, AnnotationMirror>> getAnnotationWithMetaAnnotation( |
| Element element, Class<? extends Annotation> metaAnnotationClass) { |
| |
| Set<AnnotationMirror> annotationMirrors = AnnotationUtils.createAnnotationSet(); |
| // Consider real annotations. |
| annotationMirrors.addAll(getAnnotatedType(element).getAnnotations()); |
| // Consider declaration annotations |
| annotationMirrors.addAll(getDeclAnnotations(element)); |
| |
| List<Pair<AnnotationMirror, AnnotationMirror>> result = new ArrayList<>(); |
| |
| // Go through all annotations found. |
| for (AnnotationMirror annotation : annotationMirrors) { |
| List<? extends AnnotationMirror> annotationsOnAnnotation = |
| annotation.getAnnotationType().asElement().getAnnotationMirrors(); |
| for (AnnotationMirror a : annotationsOnAnnotation) { |
| if (areSameByClass(a, metaAnnotationClass)) { |
| result.add(Pair.of(annotation, a)); |
| } |
| } |
| } |
| return result; |
| } |
| |
| /** |
| * Whether or not the {@code annotatedTypeMirror} has a qualifier parameter. |
| * |
| * @param annotatedTypeMirror AnnotatedTypeMirror to check |
| * @param top the top of the hierarchy to check |
| * @return true if the type has a qualifier parameter |
| */ |
| public boolean hasQualifierParameterInHierarchy( |
| AnnotatedTypeMirror annotatedTypeMirror, AnnotationMirror top) { |
| return AnnotationUtils.containsSame(getQualifierParameterHierarchies(annotatedTypeMirror), top); |
| } |
| |
| /** |
| * Whether or not the {@code element} has a qualifier parameter. |
| * |
| * @param element element to check |
| * @param top the top of the hierarchy to check |
| * @return true if the type has a qualifier parameter |
| */ |
| public boolean hasQualifierParameterInHierarchy(@Nullable Element element, AnnotationMirror top) { |
| if (element == null) { |
| return false; |
| } |
| return AnnotationUtils.containsSame(getQualifierParameterHierarchies(element), top); |
| } |
| |
| /** |
| * Returns whether the {@code HasQualifierParameter} annotation was explicitly written on {@code |
| * element} for the hierarchy given by {@code top}. |
| * |
| * @param element the Element to check |
| * @param top the top qualifier for the hierarchy to check |
| * @return whether the class given by {@code element} has been explicitly annotated with {@code |
| * HasQualifierParameter} for the given hierarchy |
| */ |
| public boolean hasExplicitQualifierParameterInHierarchy(Element element, AnnotationMirror top) { |
| return AnnotationUtils.containsSame( |
| getSupportedAnnotationsInElementAnnotation( |
| element, HasQualifierParameter.class, hasQualifierParameterValueElement), |
| top); |
| } |
| |
| /** |
| * Returns whether the {@code NoQualifierParameter} annotation was explicitly written on {@code |
| * element} for the hierarchy given by {@code top}. |
| * |
| * @param element the Element to check |
| * @param top the top qualifier for the hierarchy to check |
| * @return whether the class given by {@code element} has been explicitly annotated with {@code |
| * NoQualifierParameter} for the given hierarchy |
| */ |
| public boolean hasExplicitNoQualifierParameterInHierarchy(Element element, AnnotationMirror top) { |
| return AnnotationUtils.containsSame( |
| getSupportedAnnotationsInElementAnnotation( |
| element, NoQualifierParameter.class, noQualifierParameterValueElement), |
| top); |
| } |
| |
| /** |
| * Returns the set of top annotations representing all the hierarchies for which this type has a |
| * qualifier parameter. |
| * |
| * @param annotatedType AnnotatedTypeMirror to check |
| * @return the set of top annotations representing all the hierarchies for which this type has a |
| * qualifier parameter |
| */ |
| public Set<AnnotationMirror> getQualifierParameterHierarchies(AnnotatedTypeMirror annotatedType) { |
| while (annotatedType.getKind() == TypeKind.TYPEVAR |
| || annotatedType.getKind() == TypeKind.WILDCARD) { |
| if (annotatedType.getKind() == TypeKind.TYPEVAR) { |
| annotatedType = ((AnnotatedTypeVariable) annotatedType).getUpperBound(); |
| } else if (annotatedType.getKind() == TypeKind.WILDCARD) { |
| annotatedType = ((AnnotatedWildcardType) annotatedType).getSuperBound(); |
| } |
| } |
| |
| if (annotatedType.getKind() != TypeKind.DECLARED) { |
| return Collections.emptySet(); |
| } |
| |
| AnnotatedDeclaredType declaredType = (AnnotatedDeclaredType) annotatedType; |
| Element element = declaredType.getUnderlyingType().asElement(); |
| if (element == null) { |
| return Collections.emptySet(); |
| } |
| return getQualifierParameterHierarchies(element); |
| } |
| |
| /** |
| * Returns the set of top annotations representing all the hierarchies for which this element has |
| * a qualifier parameter. |
| * |
| * @param element the Element to check |
| * @return the set of top annotations representing all the hierarchies for which this element has |
| * a qualifier parameter |
| */ |
| public Set<AnnotationMirror> getQualifierParameterHierarchies(Element element) { |
| if (!ElementUtils.isTypeDeclaration(element)) { |
| return Collections.emptySet(); |
| } |
| |
| Set<AnnotationMirror> found = AnnotationUtils.createAnnotationSet(); |
| found.addAll( |
| getSupportedAnnotationsInElementAnnotation( |
| element, HasQualifierParameter.class, hasQualifierParameterValueElement)); |
| Set<AnnotationMirror> hasQualifierParameterTops = AnnotationUtils.createAnnotationSet(); |
| PackageElement packageElement = ElementUtils.enclosingPackage(element); |
| |
| // Traverse all packages containing this element. |
| while (packageElement != null) { |
| Set<AnnotationMirror> packageDefaultTops = |
| getSupportedAnnotationsInElementAnnotation( |
| packageElement, HasQualifierParameter.class, hasQualifierParameterValueElement); |
| hasQualifierParameterTops.addAll(packageDefaultTops); |
| |
| packageElement = ElementUtils.parentPackage(packageElement, elements); |
| } |
| |
| Set<AnnotationMirror> noQualifierParamClasses = |
| getSupportedAnnotationsInElementAnnotation( |
| element, NoQualifierParameter.class, noQualifierParameterValueElement); |
| for (AnnotationMirror anno : hasQualifierParameterTops) { |
| if (!AnnotationUtils.containsSame(noQualifierParamClasses, anno)) { |
| found.add(anno); |
| } |
| } |
| |
| return found; |
| } |
| |
| /** |
| * Returns a set of supported annotation mirrors corresponding to the annotation classes listed in |
| * the value element of an annotation with class {@code annoClass} on {@code element}. |
| * |
| * @param element the Element to check |
| * @param annoClass the class for an annotation that's written on elements, whose value element is |
| * a list of annotation classes. It is always HasQualifierParameter or NoQualifierParameter |
| * @param valueElement the {@code value} field/element of an annotation with class {@code |
| * annoClass} |
| * @return the set of supported annotations with classes listed in the value element of an |
| * annotation with class {@code annoClass} on the {@code element}. Returns an empty set if |
| * {@code annoClass} is not written on {@code element} or {@code element} is null. |
| */ |
| private Set<AnnotationMirror> getSupportedAnnotationsInElementAnnotation( |
| @Nullable Element element, |
| Class<? extends Annotation> annoClass, |
| ExecutableElement valueElement) { |
| if (element == null) { |
| return Collections.emptySet(); |
| } |
| // TODO: caching |
| AnnotationMirror annotation = getDeclAnnotation(element, annoClass); |
| if (annotation == null) { |
| return Collections.emptySet(); |
| } |
| |
| Set<AnnotationMirror> found = AnnotationUtils.createAnnotationSet(); |
| List<@CanonicalName Name> qualClasses = |
| AnnotationUtils.getElementValueClassNames(annotation, valueElement); |
| for (Name qual : qualClasses) { |
| AnnotationMirror annotationMirror = AnnotationBuilder.fromName(elements, qual); |
| if (isSupportedQualifier(annotationMirror)) { |
| found.add(annotationMirror); |
| } |
| } |
| return found; |
| } |
| |
| /** |
| * A scanner that replaces annotations in one type with annotations from another. Used by {@link |
| * #replaceAnnotations(AnnotatedTypeMirror, AnnotatedTypeMirror)} and {@link |
| * #replaceAnnotations(AnnotatedTypeMirror, AnnotatedTypeMirror, AnnotationMirror)}. |
| */ |
| private final AnnotatedTypeReplacer annotatedTypeReplacer = new AnnotatedTypeReplacer(); |
| |
| /** |
| * Replaces or adds all annotations from {@code from} to {@code to}. Annotations from {@code from} |
| * will be used everywhere they exist, but annotations in {@code to} will be kept anywhere that |
| * {@code from} is unannotated. |
| * |
| * @param from the annotated type mirror from which to take new annotations |
| * @param to the annotated type mirror to which the annotations will be added |
| */ |
| public void replaceAnnotations(final AnnotatedTypeMirror from, final AnnotatedTypeMirror to) { |
| annotatedTypeReplacer.visit(from, to); |
| } |
| |
| /** |
| * Replaces or adds annotations in {@code top}'s hierarchy from {@code from} to {@code to}. |
| * Annotations from {@code from} will be used everywhere they exist, but annotations in {@code to} |
| * will be kept anywhere that {@code from} is unannotated. |
| * |
| * @param from the annotated type mirror from which to take new annotations |
| * @param to the annotated type mirror to which the annotations will be added |
| * @param top the top type of the hierarchy whose annotations will be added |
| */ |
| public void replaceAnnotations( |
| final AnnotatedTypeMirror from, final AnnotatedTypeMirror to, final AnnotationMirror top) { |
| annotatedTypeReplacer.setTop(top); |
| annotatedTypeReplacer.visit(from, to); |
| annotatedTypeReplacer.setTop(null); |
| } |
| |
| /** The implementation of the visitor for #containsUninferredTypeArguments. */ |
| private final SimpleAnnotatedTypeScanner<Boolean, Void> uninferredTypeArgumentScanner = |
| new SimpleAnnotatedTypeScanner<>( |
| (type, p) -> |
| type.getKind() == TypeKind.WILDCARD |
| && ((AnnotatedWildcardType) type).isUninferredTypeArgument(), |
| Boolean::logicalOr, |
| false); |
| |
| /** |
| * Returns whether this type or any component type is a wildcard type for which Java 7 type |
| * inference is insufficient. See issue 979, or the documentation on AnnotatedWildcardType. |
| * |
| * @param type type to check |
| * @return whether this type or any component type is a wildcard type for which Java 7 type |
| * inference is insufficient |
| */ |
| public boolean containsUninferredTypeArguments(AnnotatedTypeMirror type) { |
| return uninferredTypeArgumentScanner.visit(type); |
| } |
| |
| /** |
| * Returns a wildcard type to be used as a type argument when the correct type could not be |
| * inferred. The wildcard will be marked as an uninferred wildcard so that {@link |
| * AnnotatedWildcardType#isUninferredTypeArgument()} returns true. |
| * |
| * <p>This method should only be used by type argument inference. |
| * org.checkerframework.framework.util.AnnotatedTypes.inferTypeArguments(ProcessingEnvironment, |
| * AnnotatedTypeFactory, ExpressionTree, ExecutableElement) |
| * |
| * @param typeVar TypeVariable which could not be inferred |
| * @return a wildcard that is marked as an uninferred type argument |
| */ |
| 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); |
| wctype.setTypeVariable(typeVar.getUnderlyingType()); |
| if (!intersectionType) { |
| wctype.setExtendsBound(typeVar.getUpperBound().deepCopy()); |
| } else { |
| wctype.getExtendsBound().addAnnotations(typeVar.getUpperBound().getAnnotations()); |
| } |
| wctype.setSuperBound(typeVar.getLowerBound().deepCopy()); |
| wctype.addAnnotations(typeVar.getAnnotations()); |
| addDefaultAnnotations(wctype); |
| wctype.setUninferredTypeArgument(); |
| return wctype; |
| } |
| |
| /** |
| * If {@code wildcard}'s upper bound is a super type of {@code annotatedTypeMirror}, this method |
| * returns an AnnotatedTypeMirror with the same qualifiers as {@code annotatedTypeMirror}, but the |
| * underlying Java type is the most specific base type of {@code annotatedTypeMirror} whose |
| * erasure type is equivalent to the upper bound of {@code wildcard}. |
| * |
| * <p>Otherwise, returns {@code annotatedTypeMirror} unmodified. |
| * |
| * <p>For example: |
| * |
| * <pre> |
| * wildcard := @NonNull ? extends @NonNull Object |
| * annotatedTypeMirror := @Nullable String |
| * |
| * widenToUpperBound(annotatedTypeMirror, wildcard) returns @Nullable Object |
| * </pre> |
| * |
| * 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: |
| * |
| * <pre> |
| * {@code class MyClass<T extends Number>} |
| * </pre> |
| * |
| * the following is legal: |
| * |
| * <pre> |
| * {@code MyClass<? extends Object>} |
| * </pre> |
| * |
| * This is sound because in Java the wildcard is capture converted to: {@code CAP#1 extends Number |
| * from capture of ? extends Object}. |
| * |
| * <p>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. |
| * |
| * <p>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. |
| * |
| * <p>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 wildcardUBTypeMirror = wildcard.getExtendsBound().getUnderlyingType(); |
| if (TypesUtils.isErasedSubtype(wildcardUBTypeMirror, toModifyTypeMirror, types)) { |
| return annotatedTypeMirror; |
| } else if (TypesUtils.isErasedSubtype(toModifyTypeMirror, wildcardUBTypeMirror, types)) { |
| return AnnotatedTypes.asSuper(this, annotatedTypeMirror, wildcard); |
| } else if (wildcardUBTypeMirror.getKind() == TypeKind.DECLARED |
| && TypesUtils.getTypeElement(wildcardUBTypeMirror).getKind().isInterface()) { |
| // If the Checker Framework implemented capture conversion, then in this case, then |
| // the upper bound of the capture converted wildcard would be an intersection type. |
| // See JLS 15.1.10 |
| // (https://docs.oracle.com/javase/specs/jls/se11/html/jls-5.html#jls-5.1.10) |
| |
| // For example: |
| // class MyClass<@A T extends @B Number> {} |
| // MyClass<@C ? extends @D Serializable> |
| // The upper bound of the captured wildcard: |
| // glb(@B Number, @D Serializable) = @B Number & @D Serializable |
| // The about upper bound must be a subtype of the declared upper bound: |
| // @B Number & @D Serializable <: @B Number, which is always true. |
| |
| // So, replace the upper bound at the declaration with the wildcard's upper bound so |
| // that the rest of the subtyping test pass. |
| return wildcard.getExtendsBound(); |
| } |
| |
| return annotatedTypeMirror; |
| } |
| |
| /** |
| * Returns the function type that this member reference targets. |
| * |
| * <p>The function type is the type of the single method declared in the functional interface |
| * adapted as if it were invoked using the functional interface as the receiver expression. |
| * |
| * <p>The target type of a member reference is the type to which it is assigned or casted. |
| * |
| * @param tree member reference tree |
| * @return the function type that this method reference targets |
| */ |
| public AnnotatedExecutableType getFunctionTypeFromTree(MemberReferenceTree tree) { |
| return getFnInterfaceFromTree(tree).second; |
| } |
| |
| /** |
| * Returns the function type that this lambda targets. |
| * |
| * <p>The function type is the type of the single method declared in the functional interface |
| * adapted as if it were invoked using the functional interface as the receiver expression. |
| * |
| * <p>The target type of a lambda is the type to which it is assigned or casted. |
| * |
| * @param tree lambda expression tree |
| * @return the function type that this lambda targets |
| */ |
| public AnnotatedExecutableType getFunctionTypeFromTree(LambdaExpressionTree tree) { |
| return getFnInterfaceFromTree(tree).second; |
| } |
| |
| /** |
| * Returns the functional interface and the function type that this lambda or member references |
| * targets. |
| * |
| * <p>The function type is the type of the single method declared in the functional interface |
| * adapted as if it were invoked using the functional interface as the receiver expression. |
| * |
| * <p>The target type of a lambda or a method reference is the type to which it is assigned or |
| * casted. |
| * |
| * @param tree lambda expression tree or member reference tree |
| * @return the functional interface and the function type that this method reference or lambda |
| * targets |
| */ |
| public Pair<AnnotatedTypeMirror, AnnotatedExecutableType> getFnInterfaceFromTree(Tree tree) { |
| |
| // Functional interface |
| AnnotatedTypeMirror functionalInterfaceType = getFunctionalInterfaceType(tree); |
| if (functionalInterfaceType.getKind() == TypeKind.DECLARED) { |
| makeGroundTargetType( |
| (AnnotatedDeclaredType) functionalInterfaceType, (DeclaredType) TreeUtils.typeOf(tree)); |
| } |
| |
| // Functional method |
| Element fnElement = TreeUtils.findFunction(tree, processingEnv); |
| |
| // Function type |
| AnnotatedExecutableType functionType = |
| (AnnotatedExecutableType) |
| AnnotatedTypes.asMemberOf(types, this, functionalInterfaceType, fnElement); |
| |
| return Pair.of(functionalInterfaceType, functionType); |
| } |
| |
| /** |
| * Get the AnnotatedDeclaredType for the FunctionalInterface from assignment context of the method |
| * reference or lambda expression which may be a variable assignment, a method call, or a cast. |
| * |
| * <p>The assignment context is not always correct, so we must search up the AST. It will |
| * recursively search for lambdas nested in lambdas. |
| * |
| * @param tree the tree of the lambda or method reference |
| * @return the functional interface type or an uninferred type argument |
| */ |
| private AnnotatedTypeMirror getFunctionalInterfaceType(Tree tree) { |
| |
| Tree parentTree = getPath(tree).getParentPath().getLeaf(); |
| switch (parentTree.getKind()) { |
| case PARENTHESIZED: |
| return getFunctionalInterfaceType(parentTree); |
| |
| case TYPE_CAST: |
| TypeCastTree cast = (TypeCastTree) parentTree; |
| assert isFunctionalInterface( |
| trees.getTypeMirror(getPath(cast.getType())), parentTree, tree); |
| AnnotatedTypeMirror castATM = getAnnotatedType(cast.getType()); |
| if (castATM.getKind() == TypeKind.INTERSECTION) { |
| AnnotatedIntersectionType itype = (AnnotatedIntersectionType) castATM; |
| for (AnnotatedTypeMirror t : itype.directSupertypes()) { |
| if (TypesUtils.isFunctionalInterface(t.getUnderlyingType(), getProcessingEnv())) { |
| return t; |
| } |
| } |
| // We should never reach here: isFunctionalInterface performs the same check |
| // and would have raised an error already. |
| throw new BugInCF( |
| "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, tree); |
| } |
| return castATM; |
| |
| case NEW_CLASS: |
| NewClassTree newClass = (NewClassTree) parentTree; |
| int indexOfLambda = newClass.getArguments().indexOf(tree); |
| ParameterizedExecutableType con = this.constructorFromUse(newClass); |
| AnnotatedTypeMirror constructorParam = |
| AnnotatedTypes.getAnnotatedTypeMirrorOfParameter(con.executableType, indexOfLambda); |
| assert isFunctionalInterface(constructorParam.getUnderlyingType(), parentTree, tree); |
| return constructorParam; |
| |
| case NEW_ARRAY: |
| NewArrayTree newArray = (NewArrayTree) parentTree; |
| AnnotatedArrayType newArrayATM = getAnnotatedType(newArray); |
| AnnotatedTypeMirror elementATM = newArrayATM.getComponentType(); |
| assert isFunctionalInterface(elementATM.getUnderlyingType(), parentTree, tree); |
| return elementATM; |
| |
| case METHOD_INVOCATION: |
| MethodInvocationTree method = (MethodInvocationTree) parentTree; |
| int index = method.getArguments().indexOf(tree); |
| ParameterizedExecutableType exe = this.methodFromUse(method); |
| AnnotatedTypeMirror param = |
| AnnotatedTypes.getAnnotatedTypeMirrorOfParameter(exe.executableType, index); |
| if (param.getKind() == TypeKind.WILDCARD) { |
| // param is an uninferred wildcard. |
| TypeMirror typeMirror = TreeUtils.typeOf(tree); |
| param = AnnotatedTypeMirror.createType(typeMirror, this, false); |
| addDefaultAnnotations(param); |
| } |
| assert isFunctionalInterface(param.getUnderlyingType(), parentTree, tree); |
| return param; |
| |
| case VARIABLE: |
| VariableTree varTree = (VariableTree) parentTree; |
| assert isFunctionalInterface(TreeUtils.typeOf(varTree), parentTree, tree); |
| return getAnnotatedType(varTree.getType()); |
| |
| case ASSIGNMENT: |
| AssignmentTree assignmentTree = (AssignmentTree) parentTree; |
| assert isFunctionalInterface(TreeUtils.typeOf(assignmentTree), parentTree, tree); |
| return getAnnotatedType(assignmentTree.getVariable()); |
| |
| case RETURN: |
| Tree enclosing = |
| TreePathUtil.enclosingOfKind( |
| getPath(parentTree), |
| new HashSet<>(Arrays.asList(Tree.Kind.METHOD, Tree.Kind.LAMBDA_EXPRESSION))); |
| |
| if (enclosing.getKind() == Tree.Kind.METHOD) { |
| MethodTree enclosingMethod = (MethodTree) enclosing; |
| return getAnnotatedType(enclosingMethod.getReturnType()); |
| } else { |
| LambdaExpressionTree enclosingLambda = (LambdaExpressionTree) enclosing; |
| AnnotatedExecutableType methodExe = getFunctionTypeFromTree(enclosingLambda); |
| return methodExe.getReturnType(); |
| } |
| |
| case LAMBDA_EXPRESSION: |
| LambdaExpressionTree enclosingLambda = (LambdaExpressionTree) parentTree; |
| AnnotatedExecutableType methodExe = getFunctionTypeFromTree(enclosingLambda); |
| return 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(this, trueType, falseType); |
| assert isFunctionalInterface(conditionalType.getUnderlyingType(), parentTree, tree); |
| return conditionalType; |
| |
| default: |
| throw new BugInCF( |
| "Could not find functional interface from assignment context. " |
| + "Unexpected tree type: " |
| + parentTree.getKind() |
| + " For lambda tree: " |
| + tree); |
| } |
| } |
| |
| private boolean isFunctionalInterface(TypeMirror typeMirror, Tree contextTree, Tree tree) { |
| if (typeMirror.getKind() == TypeKind.WILDCARD) { |
| // Ignore wildcards, because they are uninferred type arguments. |
| return true; |
| } |
| Type type = (Type) typeMirror; |
| |
| if (!TypesUtils.isFunctionalInterface(type, processingEnv)) { |
| if (type.getKind() == TypeKind.INTERSECTION) { |
| IntersectionType itype = (IntersectionType) type; |
| for (TypeMirror t : itype.getBounds()) { |
| if (TypesUtils.isFunctionalInterface(t, processingEnv)) { |
| // As long as any of the bounds is a functional interface |
| // we should be fine. |
| return true; |
| } |
| } |
| } |
| throw new BugInCF( |
| "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, tree); |
| } |
| return true; |
| } |
| |
| /** |
| * Create the ground target type of the functional interface. |
| * |
| * <p>Basically, it replaces the wildcards with their bounds doing a capture conversion like glb |
| * for extends bounds. |
| * |
| * @see "JLS 9.9" |
| * @param functionalType the functional interface type |
| * @param groundTargetJavaType the Java type as found by javac |
| */ |
| private void makeGroundTargetType( |
| AnnotatedDeclaredType functionalType, DeclaredType groundTargetJavaType) { |
| if (functionalType.getTypeArguments().isEmpty()) { |
| return; |
| } |
| |
| List<AnnotatedTypeParameterBounds> bounds = |
| this.typeVariablesFromUse( |
| functionalType, (TypeElement) functionalType.getUnderlyingType().asElement()); |
| |
| List<AnnotatedTypeMirror> newTypeArguments = new ArrayList<>(functionalType.getTypeArguments()); |
| boolean sizesDiffer = |
| functionalType.getTypeArguments().size() != groundTargetJavaType.getTypeArguments().size(); |
| |
| for (int i = 0; i < functionalType.getTypeArguments().size(); i++) { |
| AnnotatedTypeMirror argType = functionalType.getTypeArguments().get(i); |
| if (argType.getKind() == TypeKind.WILDCARD) { |
| AnnotatedWildcardType wildcardType = (AnnotatedWildcardType) argType; |
| |
| TypeMirror wildcardUbType = wildcardType.getExtendsBound().getUnderlyingType(); |
| |
| if (wildcardType.isUninferredTypeArgument()) { |
| // Keep the uninferred type so that it is ignored by later subtyping and containment |
| // checks. |
| newTypeArguments.set(i, wildcardType); |
| } else if (isExtendsWildcard(wildcardType)) { |
| TypeMirror correctArgType; |
| if (sizesDiffer) { |
| // The java type is raw. |
| TypeMirror typeParamUbType = bounds.get(i).getUpperBound().getUnderlyingType(); |
| correctArgType = |
| TypesUtils.greatestLowerBound( |
| typeParamUbType, wildcardUbType, this.checker.getProcessingEnvironment()); |
| } else { |
| correctArgType = groundTargetJavaType.getTypeArguments().get(i); |
| } |
| |
| final AnnotatedTypeMirror newArg; |
| if (types.isSameType(wildcardUbType, correctArgType)) { |
| newArg = wildcardType.getExtendsBound().deepCopy(); |
| } else if (correctArgType.getKind() == TypeKind.TYPEVAR) { |
| newArg = this.toAnnotatedType(correctArgType, false); |
| AnnotatedTypeVariable newArgAsTypeVar = (AnnotatedTypeVariable) newArg; |
| newArgAsTypeVar |
| .getUpperBound() |
| .replaceAnnotations(wildcardType.getExtendsBound().getAnnotations()); |
| newArgAsTypeVar |
| .getLowerBound() |
| .replaceAnnotations(wildcardType.getSuperBound().getAnnotations()); |
| } else { |
| newArg = this.toAnnotatedType(correctArgType, false); |
| newArg.replaceAnnotations(wildcardType.getExtendsBound().getAnnotations()); |
| } |
| newTypeArguments.set(i, newArg); |
| } else { |
| newTypeArguments.set(i, wildcardType.getSuperBound()); |
| } |
| } |
| } |
| functionalType.setTypeArguments(newTypeArguments); |
| |
| // When the groundTargetJavaType is different from the underlying type of functionalType, only |
| // the main annotations are copied. Add default annotations in places without annotations. |
| addDefaultAnnotations(functionalType); |
| } |
| |
| /** |
| * 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; |
| } |
| |
| /** Matches addition of a constant. */ |
| static final Pattern plusConstant = Pattern.compile(" *\\+ *(-?[0-9]+)$"); |
| /** Matches subtraction of a constant. */ |
| static final Pattern minusConstant = Pattern.compile(" *- *(-?[0-9]+)$"); |
| |
| /** Matches a string whose only parens are at the beginning and end of the string. */ |
| private static Pattern surroundingParensPattern = Pattern.compile("^\\([^()]\\)"); |
| |
| /** |
| * Given an expression, split it into a subexpression and a constant offset. For example: |
| * |
| * <pre>{@code |
| * "a" => <"a", "0"> |
| * "a + 5" => <"a", "5"> |
| * "a + -5" => <"a", "-5"> |
| * "a - 5" => <"a", "-5"> |
| * }</pre> |
| * |
| * There are methods that can only take as input an expression that represents a JavaExpression. |
| * The purpose of this is to pre-process expressions to make those methods more likely to succeed. |
| * |
| * @param expression an expression to remove a constant offset from |
| * @return a sub-expression and a constant offset. The offset is "0" if this routine is unable to |
| * splite the given expression |
| */ |
| // TODO: generalize. There is no reason this couldn't handle arbitrary addition and subtraction |
| // expressions, given the Index Checker's support for OffsetEquation. That might even make its |
| // implementation simpler. |
| public static Pair<String, String> getExpressionAndOffset(String expression) { |
| String expr = expression; |
| String offset = "0"; |
| |
| // Is this normalization necessary? |
| // Remove surrounding whitespace. |
| expr = expr.trim(); |
| // Remove surrounding parentheses. |
| if (surroundingParensPattern.matcher(expr).matches()) { |
| expr = expr.substring(1, expr.length() - 2).trim(); |
| } |
| |
| Matcher mPlus = plusConstant.matcher(expr); |
| Matcher mMinus = minusConstant.matcher(expr); |
| if (mPlus.find()) { |
| expr = expr.substring(0, mPlus.start()); |
| offset = mPlus.group(1); |
| } else if (mMinus.find()) { |
| expr = expr.substring(0, mMinus.start()); |
| offset = negateConstant(mMinus.group(1)); |
| } |
| |
| if (offset.equals("-0")) { |
| offset = "0"; |
| } |
| |
| expr = expr.intern(); |
| offset = offset.intern(); |
| |
| return Pair.of(expr, offset); |
| } |
| |
| /** |
| * Given an expression string, returns its negation. |
| * |
| * @param constantExpression a string representing an integer constant |
| * @return the negation of constantExpression |
| */ |
| // Also see Subsequence.negateString which is similar but more sophisticated. |
| public static String negateConstant(String constantExpression) { |
| if (constantExpression.startsWith("-")) { |
| return constantExpression.substring(1); |
| } else { |
| if (constantExpression.startsWith("+")) { |
| constantExpression = constantExpression.substring(1); |
| } |
| return "-" + constantExpression; |
| } |
| } |
| |
| /** |
| * Returns {@code null} or an annotated type mirror that type argument inference should assume |
| * {@code expressionTree} is assigned to. |
| * |
| * <p>If {@code null} is returned, inference proceeds normally. |
| * |
| * <p>If a type is returned, then inference assumes that {@code expressionTree} was asigned to it. |
| * This biases the inference algorithm toward the annotations in the returned type. In particular, |
| * if the annotations on type variables in invariant positions are a super type of the annotations |
| * inferred, the super type annotations are chosen. |
| * |
| * <p>This implementation returns null, but subclasses may override this method to return a type. |
| * |
| * @param expressionTree an expression which has no assignment context and for which type |
| * arguments need to be inferred |
| * @return {@code null} or an annotated type mirror that inferrence should pretend {@code |
| * expressionTree} is assigned to |
| */ |
| public @Nullable AnnotatedTypeMirror getDummyAssignedTo(ExpressionTree expressionTree) { |
| return null; |
| } |
| |
| /** |
| * Checks that the annotation {@code am} has the name of {@code annoClass}. Values are ignored. |
| * |
| * <p>This method is faster than {@link AnnotationUtils#areSameByClass(AnnotationMirror, Class)} |
| * because it caches the name of the class rather than computing it each time. |
| * |
| * @param am the AnnotationMirror whose class to compare |
| * @param annoClass the class to compare |
| * @return true if annoclass is the class of am |
| */ |
| public boolean areSameByClass(AnnotationMirror am, Class<? extends Annotation> annoClass) { |
| if (!shouldCache) { |
| return AnnotationUtils.areSameByName(am, annoClass.getCanonicalName()); |
| } |
| @SuppressWarnings("nullness") // assume getCanonicalName returns non-null |
| String canonicalName = annotationClassNames.computeIfAbsent(annoClass, Class::getCanonicalName); |
| return AnnotationUtils.areSameByName(am, canonicalName); |
| } |
| |
| /** |
| * Checks that the collection contains the annotation. Using Collection.contains does not always |
| * work, because it does not use areSame for comparison. |
| * |
| * <p>This method is faster than {@link AnnotationUtils#containsSameByClass(Collection, Class)} |
| * because is caches the name of the class rather than computing it each time. |
| * |
| * @param c a collection of AnnotationMirrors |
| * @param anno the annotation class to search for in c |
| * @return true iff c contains anno, according to areSameByClass |
| */ |
| public boolean containsSameByClass( |
| Collection<? extends AnnotationMirror> c, Class<? extends Annotation> anno) { |
| return getAnnotationByClass(c, anno) != null; |
| } |
| |
| /** |
| * Returns the AnnotationMirror in {@code c} that has the same class as {@code anno}. |
| * |
| * <p>This method is faster than {@link AnnotationUtils#getAnnotationByClass(Collection, Class)} |
| * because is caches the name of the class rather than computing it each time. |
| * |
| * @param c a collection of AnnotationMirrors |
| * @param anno the class to search for in c |
| * @return AnnotationMirror with the same class as {@code anno} iff c contains anno, according to |
| * areSameByClass; otherwise, {@code null} |
| */ |
| public @Nullable AnnotationMirror getAnnotationByClass( |
| Collection<? extends AnnotationMirror> c, Class<? extends Annotation> anno) { |
| for (AnnotationMirror an : c) { |
| if (areSameByClass(an, anno)) { |
| return an; |
| } |
| } |
| return null; |
| } |
| |
| /** |
| * Changes the type of {@code rhsATM} when being assigned to a field, for use by whole-program |
| * inference. The default implementation does nothing. |
| * |
| * @param lhsTree the tree for the field whose type will be changed |
| * @param element the element for the field whose type will be changed |
| * @param fieldName the name of the field whose type will be changed |
| * @param rhsATM the type of the expression being assigned to the field, which is side-effected by |
| * this method |
| */ |
| public void wpiAdjustForUpdateField( |
| Tree lhsTree, Element element, String fieldName, AnnotatedTypeMirror rhsATM) {} |
| |
| /** |
| * Changes the type of {@code rhsATM} when being assigned to anything other than a field, for use |
| * by whole-program inference. The default implementation does nothing. |
| * |
| * @param rhsATM the type of the rhs of the pseudo-assignment, which is side-effected by this |
| * method |
| */ |
| public void wpiAdjustForUpdateNonField(AnnotatedTypeMirror rhsATM) {} |
| |
| /** |
| * Side-effects the method or constructor annotations to make any desired changes before writing |
| * to an annotation file. |
| * |
| * @param methodAnnos the method or constructor annotations to modify |
| */ |
| public void prepareMethodForWriting(AMethod methodAnnos) { |
| // This implementation does nothing. |
| } |
| |
| /** |
| * Side-effects the method or constructor annotations to make any desired changes before writing |
| * to an ajava file. |
| * |
| * @param methodAnnos the method or constructor annotations to modify |
| */ |
| public void prepareMethodForWriting( |
| WholeProgramInferenceJavaParserStorage.CallableDeclarationAnnos methodAnnos) { |
| // This implementation does nothing. |
| } |
| |
| /** |
| * Does {@code anno}, which is an {@link org.checkerframework.framework.qual.AnnotatedFor} |
| * annotation, apply to this checker? |
| * |
| * @param annotatedForAnno an {@link AnnotatedFor} annotation |
| * @return whether {@code anno} applies to this checker |
| */ |
| public boolean doesAnnotatedForApplyToThisChecker(AnnotationMirror annotatedForAnno) { |
| List<String> annotatedForCheckers = |
| AnnotationUtils.getElementValueArray( |
| annotatedForAnno, annotatedForValueElement, String.class); |
| for (String annoForChecker : annotatedForCheckers) { |
| if (checker.getUpstreamCheckerNames().contains(annoForChecker) |
| || CheckerMain.matchesFullyQualifiedProcessor( |
| annoForChecker, checker.getUpstreamCheckerNames(), true)) { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| /** |
| * Get the {@code expression} field/element of the given contract annotation. |
| * |
| * @param contractAnno a {@link RequiresQualifier}, {@link EnsuresQualifier}, or {@link |
| * EnsuresQualifier} |
| * @return the {@code expression} field/element of the given annotation |
| */ |
| public List<String> getContractExpressions(AnnotationMirror contractAnno) { |
| DeclaredType annoType = contractAnno.getAnnotationType(); |
| if (types.isSameType(annoType, requiresQualifierTM)) { |
| return AnnotationUtils.getElementValueArray( |
| contractAnno, requiresQualifierExpressionElement, String.class); |
| } else if (types.isSameType(annoType, ensuresQualifierTM)) { |
| return AnnotationUtils.getElementValueArray( |
| contractAnno, ensuresQualifierExpressionElement, String.class); |
| } else if (types.isSameType(annoType, ensuresQualifierIfTM)) { |
| return AnnotationUtils.getElementValueArray( |
| contractAnno, ensuresQualifierIfExpressionElement, String.class); |
| } else { |
| throw new BugInCF("Not a contract annotation: " + contractAnno); |
| } |
| } |
| |
| /** |
| * Get the {@code value} field/element of the given contract list annotation. |
| * |
| * @param contractListAnno a {@link RequiresQualifier.List}, {@link EnsuresQualifier.List}, or |
| * {@link EnsuresQualifier.List} |
| * @return the {@code value} field/element of the given annotation |
| */ |
| public List<AnnotationMirror> getContractListValues(AnnotationMirror contractListAnno) { |
| DeclaredType annoType = contractListAnno.getAnnotationType(); |
| if (types.isSameType(annoType, requiresQualifierListTM)) { |
| return AnnotationUtils.getElementValueArray( |
| contractListAnno, requiresQualifierListValueElement, AnnotationMirror.class); |
| } else if (types.isSameType(annoType, ensuresQualifierListTM)) { |
| return AnnotationUtils.getElementValueArray( |
| contractListAnno, ensuresQualifierListValueElement, AnnotationMirror.class); |
| } else if (types.isSameType(annoType, ensuresQualifierIfListTM)) { |
| return AnnotationUtils.getElementValueArray( |
| contractListAnno, ensuresQualifierIfListValueElement, AnnotationMirror.class); |
| } else { |
| throw new BugInCF("Not a contract list annotation: " + contractListAnno); |
| } |
| } |
| } |