blob: 2e8ec5a1855124d2431cbb5337981d771c043183 [file] [log] [blame]
package org.checkerframework.framework.type;
import com.sun.source.tree.LambdaExpressionTree;
import com.sun.source.tree.Tree;
import com.sun.source.tree.Tree.Kind;
import com.sun.source.tree.VariableTree;
import com.sun.tools.javac.code.Symbol;
import java.util.List;
import javax.lang.model.element.Element;
import javax.lang.model.element.ElementKind;
import javax.lang.model.element.TypeElement;
import javax.lang.model.element.TypeParameterElement;
import javax.lang.model.element.VariableElement;
import org.checkerframework.framework.type.AnnotatedTypeMirror.AnnotatedDeclaredType;
import org.checkerframework.framework.type.AnnotatedTypeMirror.AnnotatedTypeVariable;
import org.checkerframework.framework.type.visitor.AnnotatedTypeScanner;
import org.checkerframework.framework.util.element.ClassTypeParamApplier;
import org.checkerframework.framework.util.element.ElementAnnotationUtil.ErrorTypeKindException;
import org.checkerframework.framework.util.element.ElementAnnotationUtil.UnexpectedAnnotationLocationException;
import org.checkerframework.framework.util.element.MethodApplier;
import org.checkerframework.framework.util.element.MethodTypeParamApplier;
import org.checkerframework.framework.util.element.ParamApplier;
import org.checkerframework.framework.util.element.SuperTypeApplier;
import org.checkerframework.framework.util.element.TypeDeclarationApplier;
import org.checkerframework.framework.util.element.TypeVarUseApplier;
import org.checkerframework.framework.util.element.VariableApplier;
import org.checkerframework.javacutil.BugInCF;
import org.checkerframework.javacutil.ElementUtils;
import org.checkerframework.javacutil.Pair;
/**
* Utility methods for adding the annotations that are stored in an Element to the type that
* represents that element (or a use of that Element).
*
* <p>In a way, this class is a hack: the Type representation for the Elements should contain all
* annotations that we want. However, due to <a
* href="http://mail.openjdk.java.net/pipermail/type-annotations-dev/2013-December/001449.html">javac
* bugs</a> decoding the type annotations from the Element is necessary.
*
* <p>Even once these bugs are fixed, this class might be useful: in TypesIntoElements it is easy to
* add additional annotations to the element and have them stored in the bytecode by the compiler.
* It would be more work (and might not work in the end) to instead modify the Type directly. The
* interaction between TypeFromElement and TypesIntoElements allows us to write the defaulted
* annotations into the Element and have them read later by other parts.
*/
public class ElementAnnotationApplier {
/**
* Add all of the relevant annotations stored in Element to type. This includes both top-level
* primary annotations and nested annotations. For the most part the TypeAnnotationPosition of the
* element annotations are used to locate the annotation in the right AnnotatedTypeMirror location
* though the individual applier classes may have special rules (such as those for upper and lower
* bounds and intersections).
*
* <p>Note: Element annotations come from two sources.
*
* <ol>
* <li>Annotations found on elements may represent those in source code or bytecode; these are
* added to the element by the compiler.
* <li>The annotations may also represent those that were inferred or defaulted by the Checker
* Framework after a previous call to this method. The Checker Framework will store any
* annotations on declarations back into the elements that represent them (see {@link
* org.checkerframework.framework.type.TypesIntoElements}). Subsequent, calls to apply will
* encounter these annotations on the provided element.
* </ol>
*
* Note: This is not the ONLY place that annotations are explicitly added to types. See {@link
* org.checkerframework.framework.type.TypeFromTree}.
*
* @param type the type to which we wish to apply the element's annotations
* @param element an element that possibly contains annotations
* @param typeFactory the typeFactory used to create the given type
*/
public static void apply(
final AnnotatedTypeMirror type,
final Element element,
final AnnotatedTypeFactory typeFactory) {
try {
try {
applyInternal(type, element, typeFactory);
} catch (UnexpectedAnnotationLocationException e) {
reportInvalidLocation(element, typeFactory);
}
// Also copy annotations from type parameters to their uses.
new TypeVarAnnotator().visit(type, typeFactory);
} catch (ErrorTypeKindException e) {
// Do nothing if an ERROR TypeKind was found.
// This is triggered by Issue #244.
}
}
/** Issues an "invalid.annotation.location.bytecode warning. */
private static void reportInvalidLocation(Element element, AnnotatedTypeFactory typeFactory) {
Element report = element;
if (element.getEnclosingElement().getKind() == ElementKind.METHOD) {
report = element.getEnclosingElement();
}
// There's a bug in Java 8 compiler that creates bad bytecode such that an
// annotation on a lambda parameter is applied to a method parameter. (This bug has
// been fixed in Java 9.) If this happens, then the location could refer to a
// location, such as a type argument, that doesn't exist. Since Java 8 bytecode
// might be on the classpath, catch this exception and ignore the type.
// TODO: Issue an error if this annotation is from Java 9+ bytecode.
if (!typeFactory.checker.hasOption("ignoreInvalidAnnotationLocations")) {
typeFactory.checker.reportWarning(
element, "invalid.annotation.location.bytecode", ElementUtils.getQualifiedName(report));
}
}
/** Same as apply except that annotations aren't copied from type parameter declarations. */
private static void applyInternal(
final AnnotatedTypeMirror type, final Element element, final AnnotatedTypeFactory typeFactory)
throws UnexpectedAnnotationLocationException {
if (element == null) {
throw new BugInCF("ElementAnnotationUtil.apply: element cannot be null");
} else if (TypeVarUseApplier.accepts(type, element)) {
TypeVarUseApplier.apply(type, element, typeFactory);
} else if (VariableApplier.accepts(type, element)) {
if (element.getKind() != ElementKind.LOCAL_VARIABLE) {
// For local variables we have the source code,
// so there is no need to look at the Element.
// This is needed to avoid a bug in the JDK:
// https://github.com/eisop/checker-framework/issues/14
VariableApplier.apply(type, element);
}
} else if (MethodApplier.accepts(type, element)) {
MethodApplier.apply(type, element, typeFactory);
} else if (TypeDeclarationApplier.accepts(type, element)) {
TypeDeclarationApplier.apply(type, element, typeFactory);
} else if (ClassTypeParamApplier.accepts(type, element)) {
ClassTypeParamApplier.apply((AnnotatedTypeVariable) type, element, typeFactory);
} else if (MethodTypeParamApplier.accepts(type, element)) {
MethodTypeParamApplier.apply((AnnotatedTypeVariable) type, element, typeFactory);
} else if (ParamApplier.accepts(type, element)) {
ParamApplier.apply(type, element, typeFactory);
} else if (isCaptureConvertedTypeVar(element)) {
// Types resulting from capture conversion cannot have explicit annotations
} else {
throw new BugInCF(
"ElementAnnotationUtil.apply: illegal argument: "
+ element
+ " ["
+ element.getKind()
+ "]"
+ " with type "
+ type);
}
}
/**
* Annotate the list of supertypes using the annotations on the TypeElement representing a class
* or interface.
*
* @param supertypes types representing supertype declarations of TypeElement
* @param subtypeElement an element representing the declaration of the class which is a subtype
* of supertypes
*/
public static void annotateSupers(
List<AnnotatedDeclaredType> supertypes, TypeElement subtypeElement) {
try {
SuperTypeApplier.annotateSupers(supertypes, subtypeElement);
} catch (UnexpectedAnnotationLocationException e) {
reportInvalidLocation(subtypeElement, supertypes.get(0).atypeFactory);
}
}
/**
* Helper method to get the lambda tree for ParamApplier. Ideally, this method would be located in
* ElementAnnotationUtil but since AnnotatedTypeFactory.declarationFromElement is protected, it
* has been placed here.
*
* @param varEle the element that may represent a lambda's parameter
* @return a LambdaExpressionTree if the varEle represents a parameter in a lambda expression,
* otherwise null
*/
public static Pair<VariableTree, LambdaExpressionTree> getParamAndLambdaTree(
VariableElement varEle, AnnotatedTypeFactory typeFactory) {
VariableTree paramDecl = (VariableTree) typeFactory.declarationFromElement(varEle);
if (paramDecl != null) {
final Tree parentTree = typeFactory.getPath(paramDecl).getParentPath().getLeaf();
if (parentTree != null && parentTree.getKind() == Kind.LAMBDA_EXPRESSION) {
return Pair.of(paramDecl, (LambdaExpressionTree) parentTree);
}
}
return null;
}
/**
* Was the type passed in generated by capture conversion.
*
* @param element the element which type represents
* @return true if type was generated via capture conversion false otherwise
*/
private static boolean isCaptureConvertedTypeVar(final Element element) {
final Element enclosure = element.getEnclosingElement();
return (((Symbol) enclosure).kind == com.sun.tools.javac.code.Kinds.Kind.NIL);
}
/**
* Annotates uses of type variables with annotation written explicitly on the type parameter
* declaration and/or its upper bound.
*/
private static class TypeVarAnnotator extends AnnotatedTypeScanner<Void, AnnotatedTypeFactory> {
@Override
public Void visitTypeVariable(AnnotatedTypeVariable type, AnnotatedTypeFactory factory) {
TypeParameterElement tpelt = (TypeParameterElement) type.getUnderlyingType().asElement();
if (type.getAnnotations().isEmpty()
&& type.getUpperBound().getAnnotations().isEmpty()
&& tpelt.getEnclosingElement().getKind() != ElementKind.TYPE_PARAMETER) {
try {
ElementAnnotationApplier.applyInternal(type, tpelt, factory);
} catch (UnexpectedAnnotationLocationException e) {
// The above is the second call to applyInternal on this type and element, so any errors
// were already reported by the first call. (See the only use of this class.)
}
}
return super.visitTypeVariable(type, factory);
}
}
}