org.checkerframework.common.value.ValueVisitor Maven / Gradle / Ivy
Show all versions of checker Show documentation
package org.checkerframework.common.value;
import com.sun.source.tree.AnnotationTree;
import com.sun.source.tree.ExpressionTree;
import com.sun.source.tree.MethodTree;
import com.sun.source.tree.Tree;
import com.sun.source.tree.TypeCastTree;
import org.checkerframework.checker.compilermsgs.qual.CompilerMessageKey;
import org.checkerframework.checker.formatter.qual.FormatMethod;
import org.checkerframework.common.basetype.BaseTypeChecker;
import org.checkerframework.common.basetype.BaseTypeVisitor;
import org.checkerframework.common.value.qual.IntRangeFromGTENegativeOne;
import org.checkerframework.common.value.qual.IntRangeFromNonNegative;
import org.checkerframework.common.value.qual.IntRangeFromPositive;
import org.checkerframework.common.value.qual.IntVal;
import org.checkerframework.common.value.qual.StaticallyExecutable;
import org.checkerframework.common.value.util.NumberUtils;
import org.checkerframework.common.value.util.Range;
import org.checkerframework.dataflow.qual.Pure;
import org.checkerframework.framework.type.AnnotatedTypeMirror;
import org.checkerframework.framework.type.AnnotatedTypeMirror.AnnotatedDeclaredType;
import org.checkerframework.framework.type.visitor.AnnotatedTypeScanner;
import org.checkerframework.javacutil.AnnotationMirrorSet;
import org.checkerframework.javacutil.AnnotationUtils;
import org.checkerframework.javacutil.ElementUtils;
import org.checkerframework.javacutil.TreeUtils;
import org.checkerframework.javacutil.TypeKindUtils;
import org.checkerframework.javacutil.TypesUtils;
import org.plumelib.util.CollectionsPlume;
import java.util.Collections;
import java.util.List;
import java.util.TreeSet;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;
import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.VariableElement;
import javax.lang.model.type.ArrayType;
import javax.lang.model.type.TypeKind;
import javax.lang.model.type.TypeMirror;
/** Visitor for the Constant Value type system. */
public class ValueVisitor extends BaseTypeVisitor {
public ValueVisitor(BaseTypeChecker checker) {
super(checker);
}
/**
* ValueVisitor overrides this method so that it does not have to check variables annotated with
* the {@link IntRangeFromPositive} annotation, the {@link IntRangeFromNonNegative} annotation,
* or the {@link IntRangeFromGTENegativeOne} annotation. This annotation is only introduced by
* the Index Checker's lower bound annotations. It is safe to defer checking of these values to
* the Index Checker because this is only introduced for explicitly-written {@code
* org.checkerframework.checker.index.qual.Positive}, explicitly-written {@code
* org.checkerframework.checker.index.qual.NonNegative}, and explicitly-written {@code
* org.checkerframework.checker.index.qual.GTENegativeOne} annotations, which must be checked by
* the Lower Bound Checker.
*
* @param varType the annotated type of the lvalue (usually a variable)
* @param valueExp the AST node for the rvalue (the new value)
* @param errorKey the error message key to use if the check fails
* @param extraArgs arguments to the error message key, before "found" and "expected" types
* @return true if the check succeeds, false if an error message was issued
*/
@Override
protected boolean commonAssignmentCheck(
AnnotatedTypeMirror varType,
ExpressionTree valueExp,
@CompilerMessageKey String errorKey,
Object... extraArgs) {
replaceSpecialIntRangeAnnotations(varType);
return super.commonAssignmentCheck(varType, valueExp, errorKey, extraArgs);
}
@Override
@FormatMethod
protected boolean commonAssignmentCheck(
AnnotatedTypeMirror varType,
AnnotatedTypeMirror valueType,
Tree valueTree,
@CompilerMessageKey String errorKey,
Object... extraArgs) {
replaceSpecialIntRangeAnnotations(varType);
if (valueType.getKind() == TypeKind.CHAR
&& valueType.hasAnnotation(getTypeFactory().UNKNOWNVAL)) {
valueType.addAnnotation(
getTypeFactory().createIntRangeAnnotation(Range.CHAR_EVERYTHING));
}
return super.commonAssignmentCheck(varType, valueType, valueTree, errorKey, extraArgs);
}
/**
* Return types for methods that are annotated with {@code @IntRangeFromX} annotations need to
* be replaced with {@code @UnknownVal}. See the documentation on {@link
* #commonAssignmentCheck(AnnotatedTypeMirror, ExpressionTree, String, Object[])
* commonAssignmentCheck}.
*
* A separate override is necessary because checkOverride doesn't actually use the
* commonAssignmentCheck.
*/
@Override
protected boolean checkOverride(
MethodTree overriderTree,
AnnotatedTypeMirror.AnnotatedExecutableType overrider,
AnnotatedTypeMirror.AnnotatedDeclaredType overridingType,
AnnotatedTypeMirror.AnnotatedExecutableType overridden,
AnnotatedTypeMirror.AnnotatedDeclaredType overriddenType) {
replaceSpecialIntRangeAnnotations(overrider);
replaceSpecialIntRangeAnnotations(overridden);
return super.checkOverride(
overriderTree, overrider, overridingType, overridden, overriddenType);
}
/**
* Replaces any {@code IntRangeFromX} annotations with {@code @UnknownVal}. This is used to
* prevent these annotations from being required on the left hand side of assignments.
*
* @param varType an annotated type mirror that may contain IntRangeFromX annotations, which
* will be used on the lhs of an assignment or pseudo-assignment
*/
private void replaceSpecialIntRangeAnnotations(AnnotatedTypeMirror varType) {
AnnotatedTypeScanner replaceSpecialIntRangeAnnotations =
new AnnotatedTypeScanner() {
@Override
protected Void scan(AnnotatedTypeMirror type, Void p) {
if (type.hasAnnotation(IntRangeFromPositive.class)
|| type.hasAnnotation(IntRangeFromNonNegative.class)
|| type.hasAnnotation(IntRangeFromGTENegativeOne.class)) {
type.replaceAnnotation(atypeFactory.UNKNOWNVAL);
}
return super.scan(type, p);
}
@Override
public Void visitDeclared(AnnotatedDeclaredType type, Void p) {
// Don't call super so that the type arguments are not visited.
if (type.getEnclosingType() != null) {
scan(type.getEnclosingType(), p);
}
return null;
}
};
replaceSpecialIntRangeAnnotations.visit(varType);
}
@Override
protected ValueAnnotatedTypeFactory createTypeFactory() {
return new ValueAnnotatedTypeFactory(checker);
}
/**
* Warns about malformed constant-value annotations.
*
* Issues an error if any @IntRange annotation has its 'from' value greater than 'to' value.
*
*
Issues an error if any constant-value annotation has no arguments.
*
*
Issues a warning if any constant-value annotation has > MAX_VALUES arguments.
*
*
Issues a warning if any @ArrayLen/@ArrayLenRange annotations contain a negative array
* length.
*
*
Issues a warning if any {@literal @}MatchesRegex or {@literal @}DoesNotMatchRegex
* annotation contains an invalid regular expression.
*/
/* Implementation note: the ValueTypeAnnotator replaces such invalid annotations with valid ones.
* Therefore, the usual validation in #validateType cannot perform this validation.
* These warnings cannot be issued in the ValueAnnotatedTypeFactory, because the conversions
* might happen multiple times.
* On the other hand, not all validations can happen here, because only the annotations are
* available, not the full types.
* Therefore, some validation is still done in #validateType below.
*/
@Override
public Void visitAnnotation(AnnotationTree tree, Void p) {
List extends ExpressionTree> args = tree.getArguments();
if (args.isEmpty()) {
// Nothing to do if there are no annotation arguments.
return super.visitAnnotation(tree, p);
}
AnnotationMirror anno = TreeUtils.annotationFromAnnotationTree(tree);
switch (AnnotationUtils.annotationName(anno)) {
case ValueAnnotatedTypeFactory.INTRANGE_NAME:
// If there are 2 arguments, issue an error if from.greater.than.to.
// If there are fewer than 2 arguments, we needn't worry about this problem because
// the other argument will be defaulted to Long.MIN_VALUE or Long.MAX_VALUE
// accordingly.
if (args.size() == 2) {
long from = getTypeFactory().getIntRangeFromValue(anno);
long to = getTypeFactory().getIntRangeToValue(anno);
if (from > to) {
checker.reportError(tree, "from.greater.than.to");
return null;
}
}
break;
case ValueAnnotatedTypeFactory.ARRAYLEN_NAME:
case ValueAnnotatedTypeFactory.BOOLVAL_NAME:
case ValueAnnotatedTypeFactory.DOUBLEVAL_NAME:
case ValueAnnotatedTypeFactory.INTVAL_NAME:
case ValueAnnotatedTypeFactory.STRINGVAL_NAME:
@SuppressWarnings("deprecation") // concrete annotation class is not known
List