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 CFAbstractStore method removeConflicting.
/**
* Remove any information in the store that might not be true any more after {@code arrayAccess}
* has been assigned a new value (with the abstract value {@code val}). This includes the
* following steps (assume that {@code arrayAccess} is of the form <em>a[i]</em> for some
* <em>a</em>.
*
* <ol>
* <li value="1">Remove any abstract value for other array access <em>b[j]</em> where <em>a</em>
* and <em>b</em> can be aliases, or where either <em>b</em> or <em>j</em> contains a
* modifiable alias of <em>a[i]</em>.
* <li value="2">Remove any abstract values for field accesses <em>b.g</em> where <em>a[i]</em>
* might alias any expression in the receiver <em>b</em> and there is an array expression
* somewhere in the receiver.
* <li value="3">Remove any information about method calls.
* </ol>
*
* @param val the abstract value of the value assigned to {@code n} (or {@code null} if the
* abstract value is not known).
*/
protected void removeConflicting(ArrayAccess arrayAccess, @Nullable V val) {
final Iterator<Map.Entry<ArrayAccess, V>> arrayValuesIterator = arrayValues.entrySet().iterator();
while (arrayValuesIterator.hasNext()) {
Map.Entry<ArrayAccess, V> entry = arrayValuesIterator.next();
ArrayAccess otherArrayAccess = entry.getKey();
// case 1:
if (otherArrayAccess.containsModifiableAliasOf(this, arrayAccess)) {
// remove information completely
arrayValuesIterator.remove();
} else if (canAlias(arrayAccess.getArray(), otherArrayAccess.getArray())) {
// TODO: one could be less strict here, and only raise the abstract
// value for all array expressions with potentially aliasing receivers.
// remove information completely
arrayValuesIterator.remove();
}
}
// case 2:
final Iterator<Map.Entry<FieldAccess, V>> fieldValuesIterator = fieldValues.entrySet().iterator();
while (fieldValuesIterator.hasNext()) {
Map.Entry<FieldAccess, V> entry = fieldValuesIterator.next();
FieldAccess otherFieldAccess = entry.getKey();
JavaExpression otherReceiver = otherFieldAccess.getReceiver();
if (otherReceiver.containsModifiableAliasOf(this, arrayAccess) && otherReceiver.containsOfClass(ArrayAccess.class)) {
// remove information completely
fieldValuesIterator.remove();
}
}
// case 3:
methodValues.clear();
}
use of org.checkerframework.dataflow.expression.JavaExpression in project checker-framework by typetools.
the class MustCallConsistencyAnalyzer method checkCreatesMustCallForInvocation.
/**
* Checks that an invocation of a CreatesMustCallFor method is valid.
*
* <p>Such an invocation is valid if any of the conditions in {@link
* #isValidCreatesMustCallForExpression(Set, JavaExpression, TreePath)} is true. If none of these
* conditions are true, this method issues a reset.not.owning error.
*
* <p>For soundness, this method also guarantees that if any of the expressions in the
* CreatesMustCallFor annotation has a tracked Obligation, any tracked resource aliases of it will
* be removed (lest the analysis conclude that it is already closed because one of these aliases
* was closed before the method was invoked). Aliases created after the CreatesMustCallFor method
* is invoked are still permitted.
*
* @param obligations the currently-tracked Obligations; this value is side-effected if there is
* an Obligation in it which tracks any expression from the CreatesMustCallFor annotation as
* one of its resource aliases
* @param node a method invocation node, invoking a method with a CreatesMustCallFor annotation
*/
private void checkCreatesMustCallForInvocation(Set<Obligation> obligations, MethodInvocationNode node) {
TreePath currentPath = typeFactory.getPath(node.getTree());
List<JavaExpression> cmcfExpressions = CreatesMustCallForElementSupplier.getCreatesMustCallForExpressions(node, typeFactory, typeFactory);
List<JavaExpression> missing = new ArrayList<>(0);
for (JavaExpression expression : cmcfExpressions) {
if (!isValidCreatesMustCallForExpression(obligations, expression, currentPath)) {
missing.add(expression);
}
}
if (missing.isEmpty()) {
// All expressions matched one of the rules, so the invocation is valid.
return;
}
String missingStrs = StringsPlume.join(", ", missing);
checker.reportError(node.getTree(), "reset.not.owning", node.getTarget().getMethod().getSimpleName().toString(), missingStrs);
}
use of org.checkerframework.dataflow.expression.JavaExpression in project checker-framework by typetools.
the class MustCallConsistencyAnalyzer method isValidCreatesMustCallForExpression.
/**
* Checks the validity of the given expression from an invoked method's {@link
* org.checkerframework.checker.mustcall.qual.CreatesMustCallFor} annotation. Helper method for
* {@link #checkCreatesMustCallForInvocation(Set, MethodInvocationNode)}.
*
* <p>An expression is valid if one of the following conditions is true: 1) the expression is an
* owning pointer, 2) the expression already has a tracked Obligation (i.e. there is already a
* resource alias in some Obligation's resource alias set that refers to the expression), or 3)
* the method in which the invocation occurs also has an @CreatesMustCallFor annotation, with the
* same expression.
*
* @param obligations the currently-tracked Obligations; this value is side-effected if there is
* an Obligation in it which tracks {@code expression} as one of its resource aliases
* @param expression an element of a method's @CreatesMustCallFor annotation
* @param path the path to the invocation of the method from whose @CreateMustCallFor annotation
* {@code expression} came
* @return true iff the expression is valid, as defined above
*/
private boolean isValidCreatesMustCallForExpression(Set<Obligation> obligations, JavaExpression expression, TreePath path) {
if (expression instanceof FieldAccess) {
Element elt = ((FieldAccess) expression).getField();
if (!checker.hasOption(MustCallChecker.NO_LIGHTWEIGHT_OWNERSHIP) && typeFactory.getDeclAnnotation(elt, Owning.class) != null) {
// The expression is an Owning field. This satisfies case 1.
return true;
}
} else if (expression instanceof LocalVariable) {
Element elt = ((LocalVariable) expression).getElement();
if (!checker.hasOption(MustCallChecker.NO_LIGHTWEIGHT_OWNERSHIP) && typeFactory.getDeclAnnotation(elt, Owning.class) != null) {
// This satisfies case 1.
return true;
} else {
Obligation toRemove = null;
Obligation toAdd = null;
for (Obligation obligation : obligations) {
ResourceAlias alias = obligation.getResourceAlias(expression);
if (alias != null) {
// This satisfies case 2 above. Remove all its aliases, then return below.
if (toRemove != null) {
throw new TypeSystemError("tried to remove multiple sets containing a reset expression at once");
}
toRemove = obligation;
toAdd = new Obligation(ImmutableSet.of(alias));
}
}
if (toRemove != null) {
obligations.remove(toRemove);
obligations.add(toAdd);
// This satisfies case 2.
return true;
}
}
}
// TODO: Getting this every time is inefficient if a method has many @CreatesMustCallFor
// annotations, but that should be rare.
MethodTree enclosingMethodTree = TreePathUtil.enclosingMethod(path);
if (enclosingMethodTree == null) {
return false;
}
ExecutableElement enclosingMethodElt = TreeUtils.elementFromDeclaration(enclosingMethodTree);
MustCallAnnotatedTypeFactory mcAtf = typeFactory.getTypeFactoryOfSubchecker(MustCallChecker.class);
List<String> enclosingCmcfValues = ResourceLeakVisitor.getCreatesMustCallForValues(enclosingMethodElt, mcAtf, typeFactory);
if (enclosingCmcfValues.isEmpty()) {
return false;
}
for (String enclosingCmcfValue : enclosingCmcfValues) {
JavaExpression enclosingTarget;
try {
enclosingTarget = StringToJavaExpression.atMethodBody(enclosingCmcfValue, enclosingMethodTree, checker);
} catch (JavaExpressionParseException e) {
// Do not issue an error here, because it would be a duplicate.
// The error will be issued by the Transfer class of the checker,
// via the CreatesMustCallForElementSupplier interface.
enclosingTarget = null;
}
if (areSame(expression, enclosingTarget)) {
// This satisfies case 3.
return true;
}
}
return false;
}
use of org.checkerframework.dataflow.expression.JavaExpression in project checker-framework by typetools.
the class ResourceLeakTransfer method handleCreatesMustCallFor.
/**
* Clears the called-methods store of all information about the target if an @CreatesMustCallFor
* method is invoked and the type factory can create obligations. Otherwise, does nothing.
*
* @param n a method invocation
* @param result the transfer result whose stores should be cleared of information
*/
private void handleCreatesMustCallFor(MethodInvocationNode n, TransferResult<CFValue, CFStore> result) {
if (!rlTypeFactory.canCreateObligations()) {
return;
}
List<JavaExpression> targetExprs = CreatesMustCallForElementSupplier.getCreatesMustCallForExpressions(n, rlTypeFactory, rlTypeFactory);
AnnotationMirror defaultType = rlTypeFactory.top;
for (JavaExpression targetExpr : targetExprs) {
CFValue defaultTypeValue = analysis.createSingleAnnotationValue(defaultType, targetExpr.getType());
if (result.containsTwoStores()) {
result.getThenStore().replaceValue(targetExpr, defaultTypeValue);
result.getElseStore().replaceValue(targetExpr, defaultTypeValue);
} else {
result.getRegularStore().replaceValue(targetExpr, defaultTypeValue);
}
}
}
Aggregations