package checkers.types;
import java.lang.annotation.Annotation;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.element.Element;
import javax.lang.model.element.ElementKind;
import javax.lang.model.type.DeclaredType;
import javax.lang.model.type.TypeKind;
import checkers.basetype.BaseTypeChecker;
import checkers.flow.Flow;
import checkers.quals.DefaultLocation;
import checkers.quals.DefaultQualifier;
import checkers.quals.ImplicitFor;
import checkers.quals.DefaultQualifierInHierarchy;
import checkers.types.AnnotatedTypeMirror.AnnotatedExecutableType;
import checkers.util.AnnotationUtils;
import checkers.util.InternalUtils;
import checkers.util.QualifierDefaults;
import checkers.util.QualifierPolymorphism;
import com.sun.source.tree.CompilationUnitTree;
import com.sun.source.tree.MethodInvocationTree;
import com.sun.source.tree.Tree;
/**
* A factory that extends {@link AnnotatedTypeFactory} to optionally use
* flow-sensitive qualifier inference, qualifier polymorphism, implicit annotations
* via {@link ImplicitFor}, and user-specified defaults via {@link DefaultQualifier}
*
* @see Flow
*/
public class BasicAnnotatedTypeFactory<Checker extends BaseTypeChecker> extends AnnotatedTypeFactory {
/** should use flow by default */
protected static boolean FLOW_BY_DEFAULT = true;
/** to annotate types based on the given tree */
protected final TypeAnnotator typeAnnotator;
/** to annotate types based on the given un-annotated types */
protected final TreeAnnotator treeAnnotator;
/** to handle any polymorphic types */
protected final QualifierPolymorphism poly;
/** to handle defaults specified by the user */
protected final QualifierDefaults defaults;
//// Flow related fields
/** Should use flow analysis? */
protected boolean useFlow;
/** Flow sensitive instance */
protected final Flow flow;
/**
* Creates a type factory for checking the given compilation unit with
* respect to the given annotation.
*
* @param checker the checker to which this type factory belongs
* @param root the compilation unit to scan
* @param useFlow whether flow analysis should be performed
*/
public BasicAnnotatedTypeFactory(Checker checker, CompilationUnitTree root, boolean useFlow) {
super(checker, root);
this.treeAnnotator = createTreeAnnotator(checker);
this.typeAnnotator = createTypeAnnotator(checker);
this.useFlow = useFlow;
this.poly = new QualifierPolymorphism(checker, this);
Set<AnnotationMirror> flowQuals = createFlowQualifiers(checker);
this.flow = useFlow ? createFlow(checker, root, flowQuals) : null;
this.defaults = new QualifierDefaults(this, this.annotations);
for (Class<? extends Annotation> qual : checker.getSupportedTypeQualifiers()) {
if (qual.getAnnotation(DefaultQualifierInHierarchy.class) != null) {
defaults.setAbsoluteDefaults(this.annotations.fromClass(qual),
Collections.singleton(DefaultLocation.ALL));
break;
}
}
}
/**
* Creates a type factory for checking the given compilation unit with
* respect to the given annotation.
*
* @param checker the checker to which this type factory belongs
* @param root the compilation unit to scan
*/
public BasicAnnotatedTypeFactory(Checker checker, CompilationUnitTree root) {
this(checker, root, FLOW_BY_DEFAULT);
}
// **********************************************************************
// Factory Methods for the appropriate annotator classes
// **********************************************************************
/**
* Returns a {@link TreeAnnotator} that adds annotations to a type based
* on the contents of a tree.
*
* Subclasses may override this method to specify more appriopriate
* {@link TreeAnnotator}
*
* @return a tree annotator
*/
protected TreeAnnotator createTreeAnnotator(Checker checker) {
return new TreeAnnotator(checker, this);
}
/**
* Returns a {@link TypeAnnotator} that adds annotations to a type based
* on the content of the type itself.
*
* @return a type annotator
*/
protected TypeAnnotator createTypeAnnotator(Checker checker) {
return new TypeAnnotator(checker);
}
/**
* Returns a {@link Flow} instance that performs flow sensitive analysis
* to infer qualifiers on unqualified types.
*
* @param checker the checker
* @param root the compilation unit associated with this factory
* @param flowQuals the qualifiers to infer
* @return the flow analysis class
*/
protected Flow createFlow(Checker checker, CompilationUnitTree root,
Set<AnnotationMirror> flowQuals) {
return new Flow(checker, root, flowQuals, this);
}
// **********************************************************************
// Factory Methods for the appropriate annotator classes
// **********************************************************************
@Override
protected void postDirectSuperTypes(AnnotatedTypeMirror type,
List<? extends AnnotatedTypeMirror> supertypes) {
super.postDirectSuperTypes(type, supertypes);
if (type.getKind() == TypeKind.DECLARED)
for (AnnotatedTypeMirror supertype : supertypes)
// FIXME: Recursive initialization for defaults fields
if (defaults != null)
defaults.annotate(((DeclaredType)supertype.getUnderlyingType()).asElement(), supertype);
}
// Indicate whether flow has performed the analysis or not
boolean scanned = false;
boolean finishedScanning = false;
@Override
protected void annotateImplicit(Tree tree, AnnotatedTypeMirror type) {
assert root != null : "root needs to be set when used on trees";
if (useFlow && !scanned) {
// Perform the flow analysis at the first invocation of
// annotateImplicit. note that flow may call .getAnnotatedType
// so scanned is set to true before flow.scan
scanned = true;
// Apply flow-sensitive qualifier inference.
flow.scan(root, null);
super.fromTreeCache.clear();
finishedScanning = true;
}
treeAnnotator.visit(tree, type);
if (useFlow) {
final AnnotationMirror inferred = flow.test(tree);
if (inferred != null) {
for (AnnotationMirror a : flow.getAnnotations()) {
type.removeAnnotation(a);
}
type.addAnnotation(inferred);
}
}
// TODO: This is quite ugly
if (!useFlow || finishedScanning
|| tree.getKind() == Tree.Kind.METHOD
|| tree.getKind() == Tree.Kind.CLASS
|| tree.getKind() == Tree.Kind.METHOD_INVOCATION) {
Element elt = InternalUtils.symbol(tree);
typeAnnotator.visit(type, elt != null ? elt.getKind() : ElementKind.OTHER);
defaults.annotate(tree, type);
}
}
@Override
protected void annotateImplicit(Element elt, AnnotatedTypeMirror type) {
typeAnnotator.visit(type, elt.getKind());
defaults.annotate(elt, type);
}
@Override
public AnnotatedExecutableType methodFromUse(MethodInvocationTree tree) {
AnnotatedExecutableType method = super.methodFromUse(tree);
poly.annotate(tree, method);
poly.annotate(method.getElement(), method);
return method;
}
// **********************************************************************
// Helper method
// **********************************************************************
/**
* Returns the set of annotations to be inferred in flow analysis
*/
protected Set<AnnotationMirror> createFlowQualifiers(Checker checker) {
AnnotationUtils annoFactory = AnnotationUtils.getInstance(env);
Set<AnnotationMirror> flowQuals = new HashSet<AnnotationMirror>();
for (Class<? extends Annotation> cl : checker.getSupportedTypeQualifiers()) {
flowQuals.add(annoFactory.fromClass(cl));
}
return flowQuals;
}
}