use of org.checkerframework.dataflow.expression.JavaExpression in project checker-framework by typetools.
the class BaseTypeVisitor method parseAndLocalizeContracts.
/**
* Localizes some contracts -- that is, viewpoint-adapts them to some method body, according to
* the value of {@link #methodTree}.
*
* <p>The input is a set of {@link Contract}s, each of which contains an expression string and an
* annotation. In a {@link Contract}, Java expressions are exactly as written in source code, not
* standardized or viewpoint-adapted.
*
* <p>The output is a set of pairs of {@link JavaExpression} (parsed expression string) and
* standardized annotation (with respect to the path of {@link #methodTree}. This method discards
* any contract whose expression cannot be parsed into a JavaExpression.
*
* @param contractSet a set of contracts
* @param methodType the type of the method that the contracts are for
* @return pairs of (expression, AnnotationMirror), which are localized contracts
*/
private Set<Pair<JavaExpression, AnnotationMirror>> parseAndLocalizeContracts(Set<? extends Contract> contractSet, AnnotatedExecutableType methodType) {
if (contractSet.isEmpty()) {
return Collections.emptySet();
}
// This is the path to a place where the contract is being used, which might or might not be
// where the contract was defined. For example, methodTree might be an overriding
// definition, and the contract might be for a superclass.
MethodTree methodTree = this.methodTree;
StringToJavaExpression stringToJavaExpr = expression -> {
JavaExpression javaExpr = StringToJavaExpression.atMethodDecl(expression, methodType.getElement(), checker);
// viewpoint-adapt it to methodTree.
return javaExpr.atMethodBody(methodTree);
};
Set<Pair<JavaExpression, AnnotationMirror>> result = new HashSet<>(contractSet.size());
for (Contract p : contractSet) {
String expressionString = p.expressionString;
AnnotationMirror annotation = p.viewpointAdaptDependentTypeAnnotation(atypeFactory, stringToJavaExpr, methodTree);
JavaExpression exprJe;
try {
// TODO: currently, these expressions are parsed many times.
// This could be optimized to store the result the first time.
// (same for other annotations)
exprJe = stringToJavaExpr.toJavaExpression(expressionString);
} catch (JavaExpressionParseException e) {
// report errors here
checker.report(methodTree, e.getDiagMessage());
continue;
}
result.add(Pair.of(exprJe, annotation));
}
return result;
}
use of org.checkerframework.dataflow.expression.JavaExpression in project checker-framework by typetools.
the class BaseTypeVisitor method checkContractsSubset.
/**
* Checks that {@code mustSubset} is a subset of {@code set} in the following sense: For every
* expression in {@code mustSubset} there must be the same expression in {@code set}, with the
* same (or a stronger) annotation.
*
* <p>This uses field {@link #methodTree} to determine where to issue an error message.
*
* @param overriderType the subtype
* @param overriddenType the supertype
* @param mustSubset annotations that should be weaker
* @param set anontations that should be stronger
* @param messageKey message key for error messages
*/
private void checkContractsSubset(AnnotatedTypeMirror overriderType, AnnotatedDeclaredType overriddenType, Set<Pair<JavaExpression, AnnotationMirror>> mustSubset, Set<Pair<JavaExpression, AnnotationMirror>> set, @CompilerMessageKey String messageKey) {
for (Pair<JavaExpression, AnnotationMirror> weak : mustSubset) {
boolean found = false;
for (Pair<JavaExpression, AnnotationMirror> strong : set) {
// are we looking at a contract of the same receiver?
if (weak.first.equals(strong.first)) {
// check subtyping relationship of annotations
QualifierHierarchy qualifierHierarchy = atypeFactory.getQualifierHierarchy();
if (qualifierHierarchy.isSubtype(strong.second, weak.second)) {
found = true;
break;
}
}
}
if (!found) {
String overriddenTypeString = overriddenType.getUnderlyingType().asElement().toString();
String overriderTypeString;
if (overriderType.getKind() == TypeKind.DECLARED) {
DeclaredType overriderTypeMirror = ((AnnotatedDeclaredType) overriderType).getUnderlyingType();
overriderTypeString = overriderTypeMirror.asElement().toString();
} else {
overriderTypeString = overriderType.toString();
}
// weak.second is the AnnotationMirror that is too strong. It might be from the
// precondition or the postcondition.
// These are the annotations that are too weak.
StringJoiner strongRelevantAnnos = new StringJoiner(" ").setEmptyValue("no information");
for (Pair<JavaExpression, AnnotationMirror> strong : set) {
if (weak.first.equals(strong.first)) {
strongRelevantAnnos.add(strong.second.toString());
}
}
Object overriddenAnno;
Object overriderAnno;
if (messageKey.contains(".precondition.")) {
overriddenAnno = strongRelevantAnnos;
overriderAnno = weak.second;
} else {
overriddenAnno = weak.second;
overriderAnno = strongRelevantAnnos;
}
checker.reportError(methodTree, messageKey, weak.first, methodTree.getName(), overriddenTypeString, overriddenAnno, overriderTypeString, overriderAnno);
}
}
}
use of org.checkerframework.dataflow.expression.JavaExpression in project checker-framework by typetools.
the class UpperBoundTransfer method visitNumericalSubtraction.
/**
* If some Node a is known to be less than the length of some sequence x, then the type of a - b
* is @LTLengthOf(value="x", offset="b"). If b is known to be less than the length of some other
* sequence, this doesn't add any information about the type of a - b. But, if b is non-negative
* or positive, then a - b should keep the types of a. This corresponds to cases 16 and 17.
*/
@Override
public TransferResult<CFValue, CFStore> visitNumericalSubtraction(NumericalSubtractionNode n, TransferInput<CFValue, CFStore> in) {
UBQualifier left = getUBQualifier(n.getLeftOperand(), in);
UBQualifier leftWithOffset = left.plusOffset(n.getRightOperand(), atypeFactory);
if (atypeFactory.hasLowerBoundTypeByClass(n.getRightOperand(), NonNegative.class) || atypeFactory.hasLowerBoundTypeByClass(n.getRightOperand(), Positive.class)) {
// annotations should be kept.
if (left.isLessThanLengthQualifier()) {
leftWithOffset = left.glb(leftWithOffset);
}
}
if (leftWithOffset.isLessThanLengthQualifier()) {
LessThanLengthOf subtractionResult = (LessThanLengthOf) leftWithOffset;
for (String b : subtractionResult.getSequences()) {
if (subtractionResult.hasSequenceWithOffset(b, -1) || subtractionResult.hasSequenceWithOffset(b, 0)) {
TreePath currentPath = this.atypeFactory.getPath(n.getTree());
JavaExpression je;
try {
je = UpperBoundVisitor.parseJavaExpressionString(b, atypeFactory, currentPath);
} catch (NullPointerException npe) {
// optional, but useful elsewhere, catching this NPE here and returning is always safe.
return createTransferResult(n, in, leftWithOffset);
}
Subsequence subsequence = Subsequence.getSubsequenceFromReceiver(je, atypeFactory);
if (subsequence != null) {
String from = subsequence.from;
String to = subsequence.to;
String a = subsequence.array;
JavaExpression leftOp = JavaExpression.fromNode(n.getLeftOperand());
JavaExpression rightOp = JavaExpression.fromNode(n.getRightOperand());
if (rightOp.toString().equals(from)) {
LessThanAnnotatedTypeFactory lessThanAtypeFactory = atypeFactory.getLessThanAnnotatedTypeFactory();
AnnotationMirror lessThanType = lessThanAtypeFactory.getAnnotatedType(n.getLeftOperand().getTree()).getAnnotation(LessThan.class);
if (lessThanType != null && lessThanAtypeFactory.isLessThan(lessThanType, to)) {
UBQualifier ltlA = UBQualifier.createUBQualifier(a, "0");
leftWithOffset = leftWithOffset.glb(ltlA);
} else if (leftOp.toString().equals(to) || (lessThanType != null && lessThanAtypeFactory.isLessThanOrEqual(lessThanType, to))) {
// It's necessary to check if leftOp == to because LessThan doesn't
// infer that things are less than or equal to themselves.
UBQualifier ltelA = UBQualifier.createUBQualifier(a, "-1");
leftWithOffset = leftWithOffset.glb(ltelA);
}
}
}
}
}
}
return createTransferResult(n, in, leftWithOffset);
}
use of org.checkerframework.dataflow.expression.JavaExpression in project checker-framework by typetools.
the class UpperBoundTransfer method propagateToAdditionOperand.
/**
* Refines the type of {@code operand} to {@code typeOfAddition} plus {@code other}. If {@code
* other} is non-negative, then {@code operand} also less than the length of the arrays in {@code
* typeOfAddition}. If {@code other} is positive, then {@code operand} also less than the length
* of the arrays in {@code typeOfAddition} plus 1. These are cases 5, 6, and 7.
*
* @param typeOfAddition type of {@code operand + other}
* @param operand the Node to refine
* @param other the Node added to {@code operand}
* @param in a TransferInput
* @param store location to store the refined types
*/
private void propagateToAdditionOperand(LessThanLengthOf typeOfAddition, Node operand, Node other, TransferInput<CFValue, CFStore> in, CFStore store) {
UBQualifier operandQual = getUBQualifier(operand, in);
UBQualifier newQual = operandQual.glb(typeOfAddition.plusOffset(other, atypeFactory));
// If the node is NonNegative, add an LTEL to the qual. If Positive, add an LTL.
if (atypeFactory.hasLowerBoundTypeByClass(other, Positive.class)) {
newQual = newQual.glb(typeOfAddition.plusOffset(1));
} else if (atypeFactory.hasLowerBoundTypeByClass(other, NonNegative.class)) {
newQual = newQual.glb(typeOfAddition);
}
JavaExpression operandJe = JavaExpression.fromNode(operand);
store.insertValue(operandJe, atypeFactory.convertUBQualifierToAnnotation(newQual));
}
use of org.checkerframework.dataflow.expression.JavaExpression in project checker-framework by typetools.
the class UpperBoundTransfer method refineNeqSequenceLength.
/**
* If lengthAccess node is an sequence length field or method access (optionally with a constant
* offset subtracted) and the other node is less than or equal to that sequence length (minus the
* offset), then refine the other node's type to less than the sequence length (minus the offset).
* This is case 12.
*/
private void refineNeqSequenceLength(Node lengthAccess, Node otherNode, AnnotationMirror otherNodeAnno, CFStore store) {
// If lengthAccess is "receiver.length - c" where c is an integer constant,
// then lengthOffset is "c".
int lengthOffset = 0;
if (lengthAccess instanceof NumericalSubtractionNode) {
NumericalSubtractionNode subtraction = (NumericalSubtractionNode) lengthAccess;
Node offsetNode = subtraction.getRightOperand();
Long offsetValue = ValueCheckerUtils.getExactValue(offsetNode.getTree(), atypeFactory.getValueAnnotatedTypeFactory());
if (offsetValue != null && offsetValue > Integer.MIN_VALUE && offsetValue <= Integer.MAX_VALUE) {
lengthOffset = offsetValue.intValue();
lengthAccess = subtraction.getLeftOperand();
} else {
// Subtraction of non-constant expressions is not supported
return;
}
}
JavaExpression receiver = null;
if (NodeUtils.isArrayLengthFieldAccess(lengthAccess)) {
FieldAccess fa = (FieldAccess) JavaExpression.fromNodeFieldAccess((FieldAccessNode) lengthAccess);
receiver = fa.getReceiver();
} else if (atypeFactory.getMethodIdentifier().isLengthOfMethodInvocation(lengthAccess)) {
JavaExpression ma = JavaExpression.fromNode(lengthAccess);
if (ma instanceof MethodCall) {
receiver = ((MethodCall) ma).getReceiver();
}
}
if (receiver != null && !receiver.containsUnknown()) {
UBQualifier otherQualifier = UBQualifier.createUBQualifier(otherNodeAnno, (UpperBoundChecker) atypeFactory.getChecker());
String sequence = receiver.toString();
// Check if otherNode + c - 1 < receiver.length
if (otherQualifier.hasSequenceWithOffset(sequence, lengthOffset - 1)) {
// Add otherNode + c < receiver.length
UBQualifier newQualifier = UBQualifier.createUBQualifier(sequence, Integer.toString(lengthOffset));
otherQualifier = otherQualifier.glb(newQualifier);
for (Node internal : splitAssignments(otherNode)) {
JavaExpression leftJe = JavaExpression.fromNode(internal);
store.insertValue(leftJe, atypeFactory.convertUBQualifierToAnnotation(otherQualifier));
}
}
}
}
Aggregations