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.TypeElement;
import javax.lang.model.element.VariableElement;
import org.checkerframework.framework.type.AnnotatedTypeMirror.AnnotatedDeclaredType;
import org.checkerframework.framework.type.AnnotatedTypeMirror.AnnotatedTypeVariable;
import org.checkerframework.framework.util.element.ClassTypeParamApplier;
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.ErrorReporter;
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 javac bugs
* http://mail.openjdk.java.net/pipermail/type-annotations-dev/2013-December/001449.html 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) {
if (element == null) {
ErrorReporter.errorAbort("ElementAnnotationUtil.apply: element cannot be null");
} else if (TypeVarUseApplier.accepts(type, element)) {
TypeVarUseApplier.apply(type, element, typeFactory);
} else if (VariableApplier.accepts(type, element)) {
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 {
ErrorReporter.errorAbort(
"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) {
SuperTypeApplier.annotateSupers(supertypes, subtypeElement);
}
/**
* 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.NIL);
}
}