blob: d986ccefb1b907bdfadec34410c733004429d057 [file] [log] [blame]
package org.checkerframework.checker.fenum;
import com.sun.source.tree.BinaryTree;
import com.sun.source.tree.CaseTree;
import com.sun.source.tree.ExpressionTree;
import com.sun.source.tree.NewClassTree;
import com.sun.source.tree.SwitchTree;
import com.sun.source.tree.Tree;
import java.util.Collections;
import java.util.Set;
import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.element.ExecutableElement;
import org.checkerframework.common.basetype.BaseTypeChecker;
import org.checkerframework.common.basetype.BaseTypeVisitor;
import org.checkerframework.framework.type.AnnotatedTypeMirror;
import org.checkerframework.framework.type.AnnotatedTypeMirror.AnnotatedDeclaredType;
import org.checkerframework.framework.type.AnnotatedTypeMirror.AnnotatedExecutableType;
import org.checkerframework.framework.type.QualifierHierarchy;
import org.checkerframework.javacutil.TreeUtils;
public class FenumVisitor extends BaseTypeVisitor<FenumAnnotatedTypeFactory> {
public FenumVisitor(BaseTypeChecker checker) {
super(checker);
}
@Override
public Void visitBinary(BinaryTree node, Void p) {
if (!TreeUtils.isStringConcatenation(node)) {
// TODO: ignore string concatenations
// The Fenum Checker is only concerned with primitive types, so just check that
// the primary annotations are equivalent.
AnnotatedTypeMirror lhsAtm = atypeFactory.getAnnotatedType(node.getLeftOperand());
AnnotatedTypeMirror rhsAtm = atypeFactory.getAnnotatedType(node.getRightOperand());
Set<AnnotationMirror> lhs = lhsAtm.getEffectiveAnnotations();
Set<AnnotationMirror> rhs = rhsAtm.getEffectiveAnnotations();
QualifierHierarchy qualHierarchy = atypeFactory.getQualifierHierarchy();
if (!(qualHierarchy.isSubtype(lhs, rhs) || qualHierarchy.isSubtype(rhs, lhs))) {
checker.reportError(node, "binary", lhsAtm, rhsAtm);
}
}
return super.visitBinary(node, p);
}
@Override
public Void visitSwitch(SwitchTree node, Void p) {
ExpressionTree expr = node.getExpression();
AnnotatedTypeMirror exprType = atypeFactory.getAnnotatedType(expr);
for (CaseTree caseExpr : node.getCases()) {
ExpressionTree realCaseExpr = caseExpr.getExpression();
if (realCaseExpr != null) {
AnnotatedTypeMirror caseType = atypeFactory.getAnnotatedType(realCaseExpr);
// There is currently no "switch" message key, so it is treated
// identically to "type.incompatible".
this.commonAssignmentCheck(exprType, caseType, caseExpr, "type.incompatible");
}
}
return super.visitSwitch(node, p);
}
@Override
protected void checkConstructorInvocation(
AnnotatedDeclaredType dt, AnnotatedExecutableType constructor, NewClassTree src) {
// Ignore the default annotation on the constructor
}
@Override
protected void checkConstructorResult(
AnnotatedExecutableType constructorType, ExecutableElement constructorElement) {
// Skip this check
}
@Override
protected Set<? extends AnnotationMirror> getExceptionParameterLowerBoundAnnotations() {
return Collections.singleton(atypeFactory.FENUM_UNQUALIFIED);
}
// TODO: should we require a match between switch expression and cases?
@Override
public boolean isValidUse(
AnnotatedDeclaredType declarationType, AnnotatedDeclaredType useType, Tree tree) {
// The checker calls this method to compare the annotation used in a type to the modifier it
// adds to the class declaration. As our default modifier is FenumBottom, this results in an
// error when a non-subtype is used. Can we use FenumTop as default instead?
return true;
}
}