package checkers.javari;
import javax.lang.model.element.*;
import javax.lang.model.type.TypeKind;
import com.sun.source.tree.*;
import checkers.source.*;
import checkers.basetype.*;
import checkers.javari.quals.Assignable;
import checkers.types.*;
import checkers.types.AnnotatedTypeMirror.*;
import checkers.util.TreeUtils;
/**
* A type-checking visitor for the Javari mutability annotations
* ({@code @ReadOnly}, {@code @Mutable} and {@code @Assignable}) that
* extends BaseTypeVisitor.
*
* @see BaseTypeVisitor
*/
public class JavariVisitor extends BaseTypeVisitor<Void, Void> {
final private AnnotationMirror READONLY, MUTABLE, POLYREAD, QREADONLY;
/**
* Creates a new visitor for type-checking the Javari mutability
* annotations.
*
* @param checker the {@link JavariChecker} to use
* @param root the root of the input program's AST to check
*/
public JavariVisitor(JavariChecker checker, CompilationUnitTree root) {
super(checker, root);
READONLY = checker.READONLY;
MUTABLE = checker.MUTABLE;
POLYREAD = checker.POLYREAD;
QREADONLY = checker.QREADONLY;
}
/**
* Ensures the class type is not {@code @PolyRead} outside a
* {@code @PolyRead} context.
*/
@Override
public Void visitClass(ClassTree node, Void p) {
if (atypeFactory.fromClass(node).hasAnnotation(POLYREAD)
&& !atypeFactory.getSelfType(node).hasAnnotation(POLYREAD))
checker.report(Result.failure("polyread.type"), node);
return super.visitClass(node, p);
}
/**
* Checks whether the variable represented by the given type and
* tree can be assigned, causing a checker error otherwise.
*/
@Override
protected void checkAssignability(AnnotatedTypeMirror varType, Tree varTree) {
if (!(varTree instanceof ExpressionTree)) return;
Element varElt = varType.getElement();
if (varElt != null && varElt.getAnnotation(Assignable.class) != null)
return;
boolean variableLocalField =
TreeUtils.isSelfAccess((ExpressionTree) varTree)
&& varElt != null
&& varElt.getKind().isField();
// visitState.getMethodTree() is null when in static initializer block
boolean inConstructor = visitorState.getMethodTree() == null
|| TreeUtils.isConstructor(visitorState.getMethodTree());
AnnotatedDeclaredType mReceiver = atypeFactory.getSelfType(varTree);
if (variableLocalField && !inConstructor && !mReceiver.hasAnnotation(MUTABLE))
checker.report(Result.failure("ro.field"), varTree);
if (varTree.getKind() == Tree.Kind.MEMBER_SELECT
&& !TreeUtils.isSelfAccess((ExpressionTree)varTree)) {
AnnotatedTypeMirror receiver = atypeFactory.getReceiver((ExpressionTree)varTree);
if (receiver != null && !receiver.hasAnnotation(MUTABLE))
checker.report(Result.failure("ro.field"), varTree);
}
}
/**
* Tests whether the tree expressed by the passed type tree
* contains a qualified primitive type on its qualified type, and
* if so emits an error.
*
* @param tree the AST type supplied by the user
*/
@Override
public void validateTypeOf(Tree tree) {
AnnotatedTypeMirror type;
// It's quite annoying that there is no TypeTree
switch (tree.getKind()) {
case PRIMITIVE_TYPE:
case PARAMETERIZED_TYPE:
case TYPE_PARAMETER:
case ARRAY_TYPE:
case UNBOUNDED_WILDCARD:
case EXTENDS_WILDCARD:
case SUPER_WILDCARD:
type = atypeFactory.getAnnotatedTypeFromTypeTree(tree);
break;
default:
type = atypeFactory.getAnnotatedType(tree);
}
// Here we simply test for primitive types
// they can only occur at raw or array most inner component type
while (type.getKind() == TypeKind.ARRAY)
type = ((AnnotatedArrayType)type).getComponentType();
if (type.getKind().isPrimitive()) {
if (type.hasAnnotation(QREADONLY)
|| type.hasAnnotation(READONLY)
|| type.hasAnnotation(POLYREAD)) {
checker.report(Result.failure("primitive.ro"), tree);
}
}
super.validateTypeOf(tree);
}
}