blob: e0d1e7739a89b101f183371c99c7842bf3a11b54 [file] [log] [blame]
package org.checkerframework.checker.index.lowerbound;
import com.sun.source.tree.ArrayAccessTree;
import com.sun.source.tree.ExpressionTree;
import com.sun.source.tree.NewArrayTree;
import com.sun.source.tree.Tree;
import javax.lang.model.element.AnnotationMirror;
import org.checkerframework.checker.compilermsgs.qual.CompilerMessageKey;
import org.checkerframework.checker.index.Subsequence;
import org.checkerframework.checker.index.qual.NonNegative;
import org.checkerframework.checker.index.qual.Positive;
import org.checkerframework.common.basetype.BaseTypeChecker;
import org.checkerframework.common.basetype.BaseTypeVisitor;
import org.checkerframework.framework.type.AnnotatedTypeMirror;
import org.checkerframework.framework.util.JavaExpressionParseUtil;
/**
* Implements the actual checks to make sure that array accesses aren't too low. Will issue a
* warning if a variable that can't be proved to be either "NonNegative" (i.e. ≥ 0) or "Positive"
* (i.e. ≥ 1) is used as an array index.
*/
public class LowerBoundVisitor extends BaseTypeVisitor<LowerBoundAnnotatedTypeFactory> {
/* This is a key into the messages.properties file in the same
* directory, which includes the actual text of the warning.
*/
private static final @CompilerMessageKey String LOWER_BOUND = "array.access.unsafe.low";
private static final @CompilerMessageKey String NEGATIVE_ARRAY = "array.length.negative";
private static final @CompilerMessageKey String FROM_NOT_NN = "from.not.nonnegative";
public LowerBoundVisitor(BaseTypeChecker checker) {
super(checker);
}
@Override
public Void visitArrayAccess(ArrayAccessTree tree, Void type) {
ExpressionTree index = tree.getIndex();
String arrName = tree.getExpression().toString();
AnnotatedTypeMirror indexType = atypeFactory.getAnnotatedType(index);
if (!(indexType.hasAnnotation(NonNegative.class) || indexType.hasAnnotation(Positive.class))) {
checker.reportError(index, LOWER_BOUND, indexType.toString(), arrName);
}
return super.visitArrayAccess(tree, type);
}
@Override
public Void visitNewArray(NewArrayTree tree, Void type) {
if (!tree.getDimensions().isEmpty()) {
for (ExpressionTree dim : tree.getDimensions()) {
AnnotatedTypeMirror dimType = atypeFactory.getAnnotatedType(dim);
if (!(dimType.hasAnnotation(NonNegative.class) || dimType.hasAnnotation(Positive.class))) {
checker.reportError(dim, NEGATIVE_ARRAY, dimType.toString());
}
}
}
return super.visitNewArray(tree, type);
}
@Override
protected void commonAssignmentCheck(
Tree varTree,
ExpressionTree valueTree,
@CompilerMessageKey String errorKey,
Object... extraArgs) {
// check that when an assignment to a variable declared as @HasSubsequence(a, from, to)
// occurs, from is non-negative.
Subsequence subSeq = Subsequence.getSubsequenceFromTree(varTree, atypeFactory);
if (subSeq != null) {
AnnotationMirror anm;
try {
anm =
atypeFactory.getAnnotationMirrorFromJavaExpressionString(
subSeq.from, varTree, getCurrentPath());
} catch (JavaExpressionParseUtil.JavaExpressionParseException e) {
anm = null;
}
if (anm == null
|| !(atypeFactory.areSameByClass(anm, NonNegative.class)
|| atypeFactory.areSameByClass(anm, Positive.class))) {
checker.reportError(
valueTree, FROM_NOT_NN, subSeq.from, anm == null ? "@LowerBoundUnknown" : anm);
}
}
super.commonAssignmentCheck(varTree, valueTree, errorKey, extraArgs);
}
}