package org.checkerframework.common.value;
import com.sun.source.tree.AnnotationTree;
import com.sun.source.tree.ExpressionTree;
import com.sun.source.tree.LiteralTree;
import com.sun.source.tree.Tree.Kind;
import com.sun.source.tree.TypeCastTree;
import java.util.Collections;
import java.util.List;
import javax.lang.model.element.AnnotationMirror;
import org.checkerframework.common.basetype.BaseTypeChecker;
import org.checkerframework.common.basetype.BaseTypeVisitor;
import org.checkerframework.common.value.qual.ArrayLen;
import org.checkerframework.common.value.qual.ArrayLenRange;
import org.checkerframework.common.value.qual.BoolVal;
import org.checkerframework.common.value.qual.DoubleVal;
import org.checkerframework.common.value.qual.IntRange;
import org.checkerframework.common.value.qual.IntVal;
import org.checkerframework.common.value.qual.StringVal;
import org.checkerframework.framework.source.Result;
import org.checkerframework.javacutil.AnnotationUtils;
import org.checkerframework.javacutil.InternalUtils;
/**
* @author plvines
* <p>Visitor for the Constant Value type-system
*/
public class ValueVisitor extends BaseTypeVisitor<ValueAnnotatedTypeFactory> {
public ValueVisitor(BaseTypeChecker checker) {
super(checker);
}
/**
* @param exp an integral literal tree
* @return {@code exp}'s literal value
*/
private long getIntLiteralValue(LiteralTree exp) {
Object orgValue = exp.getValue();
switch (exp.getKind()) {
case INT_LITERAL:
case LONG_LITERAL:
return ((Number) orgValue).longValue();
case CHAR_LITERAL:
return (long) ((Character) orgValue);
default:
throw new IllegalArgumentException(
"exp is not an intergral literal (INT_LITERAL, LONG_LITERAL, CHAR_LITERAL)");
}
}
@Override
protected ValueAnnotatedTypeFactory createTypeFactory() {
return new ValueAnnotatedTypeFactory(checker);
}
/**
* Warns about malformed constant-value annotations.
*
* <p>Issues an error if any @IntRange annotation has its 'from' value greater than 'to' value.
*
* <p>Issues a warning if any constant-value annotation has > MAX_VALUES arguments.
*/
@Override
public Void visitAnnotation(AnnotationTree node, Void p) {
List<? extends ExpressionTree> args = node.getArguments();
if (args.isEmpty()) {
// Nothing to do if there are no annotation arguments.
return super.visitAnnotation(node, p);
}
AnnotationMirror anno = InternalUtils.annotationFromAnnotationTree(node);
if (AnnotationUtils.areSameByClass(anno, IntRange.class)) {
// 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 = AnnotationUtils.getElementValue(anno, "from", Long.class, true);
long to = AnnotationUtils.getElementValue(anno, "to", Long.class, true);
if (from > to) {
checker.report(Result.failure("from.greater.than.to"), node);
return null;
}
}
} else if (AnnotationUtils.areSameByClass(anno, ArrayLen.class)
|| AnnotationUtils.areSameByClass(anno, BoolVal.class)
|| AnnotationUtils.areSameByClass(anno, DoubleVal.class)
|| AnnotationUtils.areSameByClass(anno, IntVal.class)
|| AnnotationUtils.areSameByClass(anno, StringVal.class)) {
List<Object> values =
AnnotationUtils.getElementValueArray(anno, "value", Object.class, true);
if (values.isEmpty()) {
checker.report(Result.warning("no.values.given"), node);
return null;
} else if (values.size() > ValueAnnotatedTypeFactory.MAX_VALUES) {
checker.report(
Result.warning(
(AnnotationUtils.areSameByClass(anno, IntVal.class)
? "too.many.values.given.int"
: "too.many.values.given"),
ValueAnnotatedTypeFactory.MAX_VALUES),
node);
return null;
} else if (AnnotationUtils.areSameByClass(anno, ArrayLen.class)) {
List<Integer> arrayLens =
AnnotationUtils.getElementValueArray(anno, "value", Integer.class, true);
if (Collections.min(arrayLens) < 0) {
checker.report(
Result.warning("negative.arraylen", Collections.min(arrayLens)), node);
return null;
}
}
} else if (AnnotationUtils.areSameByClass(anno, ArrayLenRange.class)) {
int from = AnnotationUtils.getElementValue(anno, "from", Integer.class, true);
int to = AnnotationUtils.getElementValue(anno, "to", Integer.class, true);
if (from > to) {
checker.report(Result.failure("from.greater.than.to"), node);
return null;
} else if (from < 0) {
checker.report(Result.warning("negative.arraylen", from), node);
return null;
}
}
return super.visitAnnotation(node, p);
}
@Override
public Void visitTypeCast(TypeCastTree node, Void p) {
if (node.getExpression().getKind() == Kind.NULL_LITERAL) {
return null;
}
return super.visitTypeCast(node, p);
}
}