org.checkerframework.checker.index.lowerbound.LowerBoundVisitor Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of checker Show documentation
Show all versions of checker Show documentation
The Checker Framework enhances Java's type system to
make it more powerful and useful. This lets software developers
detect and prevent errors in their Java programs.
The Checker Framework includes compiler plug-ins ("checkers")
that find bugs or verify their absence. It also permits you to
write your own compiler plug-ins.
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 {
/* 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.hasPrimaryAnnotation(NonNegative.class)
|| indexType.hasPrimaryAnnotation(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.hasPrimaryAnnotation(NonNegative.class)
|| dimType.hasPrimaryAnnotation(Positive.class))) {
checker.reportError(dim, NEGATIVE_ARRAY, dimType.toString());
}
}
}
return super.visitNewArray(tree, type);
}
@Override
protected boolean 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.
boolean result = true;
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);
result = false;
}
}
result = super.commonAssignmentCheck(varTree, valueTree, errorKey, extraArgs) && result;
return result;
}
}