| 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); |
| } |
| } |
| } |