package checkers.interning;
import javax.lang.model.element.*;
import checkers.basetype.BaseTypeChecker;
import checkers.interning.quals.*;
import checkers.javari.quals.Mutable;
import checkers.quals.DefaultQualifier;
import checkers.quals.ImplicitFor;
import checkers.types.*;
import checkers.util.TreeUtils;
import checkers.util.ElementUtils;
import static checkers.types.AnnotatedTypeMirror.*;
import com.sun.source.tree.*;
/**
* An {@link AnnotatedTypeFactory} that accounts for the properties of the
* Interned type system. This type factory will add the {@link Interned}
* annotation to a type if the input:
*
* <ol>
* <li value="1">is a String literal
* <li value="2">is a class literal
* <li value="3">has an enum type
* <li value="4">has a primitive type
* <li value="5">has the type java.lang.Class
* </ol>
*
* This factory extends {@link BasicAnnotatedTypeFactory} and inherits its
* functionality, including: flow-sensitive qualifier inference, qualifier
* polymorphism (of {@link PolyInterned}), implicit annotations via
* {@link ImplicitFor} on {@link Interned} (to handle cases 1, 2, 4), and
* user-specified defaults via {@link DefaultQualifier}.
* Case 5 is handled by the stub library.
*/
public class InterningAnnotatedTypeFactory extends BasicAnnotatedTypeFactory<InterningChecker> {
/** The {@link Interned} annotation. */
final AnnotationMirror INTERNED;
/**
* Creates a new {@link InterningAnnotatedTypeFactory} that operates on a
* particular AST.
*
* @param checker the checker to use
* @param root the AST on which this type factory operates
*/
public InterningAnnotatedTypeFactory(InterningChecker checker,
CompilationUnitTree root) {
super(checker, root);
this.INTERNED = annotations.fromClass(Interned.class);
}
@Override
protected TreeAnnotator createTreeAnnotator(InterningChecker checker) {
return new InterningTreeAnnotator(checker);
}
@Override
protected TypeAnnotator createTypeAnnotator(InterningChecker checker) {
return new InterningTypeAnnotator(checker);
}
@Override
protected void annotateImplicit(Element element, AnnotatedTypeMirror type) {
if (!type.isAnnotated() && ElementUtils.isCompileTimeConstant(element))
type.addAnnotation(INTERNED);
super.annotateImplicit(element, type);
}
/**
* A class for adding annotations based on tree
*/
private class InterningTreeAnnotator extends TreeAnnotator {
InterningTreeAnnotator(BaseTypeChecker checker) {
super(checker, InterningAnnotatedTypeFactory.this);
}
@Override
public Void visitBinary(BinaryTree node, AnnotatedTypeMirror type) {
if (TreeUtils.isCompileTimeString(node)) {
type.addAnnotation(INTERNED);
}
return super.visitBinary(node, type);
}
}
/**
* A class for adding annotations to a type after initial type resolution.
*/
private class InterningTypeAnnotator extends TypeAnnotator {
/** Creates an {@link InterningTypeAnnotator} for the given checker. */
InterningTypeAnnotator(BaseTypeChecker checker) {
super(checker);
}
@Override
public Void visitDeclared(AnnotatedDeclaredType t, ElementKind p) {
// case 3: Enum types, and the Enum class itself, are interned
Element elt = t.getUnderlyingType().asElement();
assert elt != null;
if (elt.getKind() == ElementKind.ENUM) {
t.addAnnotation(INTERNED);
}
return super.visitDeclared(t, p);
}
}
@Override
public AnnotatedPrimitiveType getUnboxedType(AnnotatedDeclaredType type) {
AnnotatedPrimitiveType primitive = super.getUnboxedType(type);
primitive.addAnnotation(INTERNED);
return primitive;
}
protected void annotateInheritedFromClass(@Mutable AnnotatedTypeMirror type) {
InheritedFromClassAnnotator.INSTANCE.visit(type, this);
}
}