Search in sources :

Example 1 with LocalVariable

use of org.checkerframework.dataflow.expression.LocalVariable in project checker-framework by typetools.

the class MustCallConsistencyAnalyzer method updateObligationsWithInvocationResult.

/**
 * Given a node representing a method or constructor call, updates the set of Obligations to
 * account for the result, which is treated as a new resource alias. Adds the new resource alias
 * to the set of an Obligation in {@code obligations}: either an existing Obligation if the result
 * is definitely resource-aliased with it, or a new Obligation if not.
 *
 * @param obligations the currently-tracked Obligations. This is always side-effected: either a
 *     new resource alias is added to the resource alias set of an existing Obligation, or a new
 *     Obligation with a single-element resource alias set is created and added.
 * @param node the invocation node whose result is to be tracked; must be {@link
 *     MethodInvocationNode} or {@link ObjectCreationNode}
 */
private void updateObligationsWithInvocationResult(Set<Obligation> obligations, Node node) {
    Tree tree = node.getTree();
    // Only track the result of the call if there is a temporary variable for the call node
    // (because if there is no temporary, then the invocation must produce an untrackable value,
    // such as a primitive type).
    LocalVariableNode tmpVar = typeFactory.getTempVarForNode(node);
    if (tmpVar == null) {
        return;
    }
    // `mustCallAliases` is a (possibly-empty) list of arguments passed in a MustCallAlias position.
    List<Node> mustCallAliases = getMustCallAliasArgumentNodes(node);
    // If call returns @This, add the receiver to mustCallAliases.
    if (node instanceof MethodInvocationNode && typeFactory.returnsThis((MethodInvocationTree) tree)) {
        mustCallAliases.add(removeCastsAndGetTmpVarIfPresent(((MethodInvocationNode) node).getTarget().getReceiver()));
    }
    if (mustCallAliases.isEmpty()) {
        // If mustCallAliases is an empty List, add tmpVarAsResourceAlias to a new set.
        ResourceAlias tmpVarAsResourceAlias = new ResourceAlias(new LocalVariable(tmpVar), tree);
        obligations.add(new Obligation(ImmutableSet.of(tmpVarAsResourceAlias)));
    } else {
        for (Node mustCallAlias : mustCallAliases) {
            if (mustCallAlias instanceof FieldAccessNode) {
            // Do not track the call result if the MustCallAlias argument is a field.  Handling of
            // @Owning fields is a completely separate check, and there is never a need to track an
            // alias of a non-@Owning field, as by definition such a field does not have must-call
            // obligations!
            } else if (mustCallAlias instanceof LocalVariableNode) {
                // If mustCallAlias is a local variable already being tracked, add tmpVarAsResourceAlias
                // to the set containing mustCallAlias.
                Obligation obligationContainingMustCallAlias = getObligationForVar(obligations, (LocalVariableNode) mustCallAlias);
                if (obligationContainingMustCallAlias != null) {
                    ResourceAlias tmpVarAsResourceAlias = new ResourceAlias(new LocalVariable(tmpVar), tree, obligationContainingMustCallAlias.derivedFromMustCallAlias());
                    Set<ResourceAlias> newResourceAliasSet = FluentIterable.from(obligationContainingMustCallAlias.resourceAliases).append(tmpVarAsResourceAlias).toSet();
                    obligations.remove(obligationContainingMustCallAlias);
                    obligations.add(new Obligation(newResourceAliasSet));
                // It is not an error if there is no Obligation containing the must-call alias. In that
                // case, what has usually happened is that no Obligation was created in the first place.
                // For example, when checking the invocation of a "wrapper stream" constructor, if the
                // argument in the must-call alias position is some stream with no must-call obligations
                // like a ByteArrayInputStream, then no Obligation object will have been created for it
                // and therefore obligationContainingMustCallAlias will be null.
                }
            }
        }
    }
}
Also used : ImmutableSet(com.google.common.collect.ImmutableSet) Set(java.util.Set) HashSet(java.util.HashSet) LinkedHashSet(java.util.LinkedHashSet) MethodInvocationNode(org.checkerframework.dataflow.cfg.node.MethodInvocationNode) MethodInvocationTree(com.sun.source.tree.MethodInvocationTree) TypeCastNode(org.checkerframework.dataflow.cfg.node.TypeCastNode) ObjectCreationNode(org.checkerframework.dataflow.cfg.node.ObjectCreationNode) LocalVariableNode(org.checkerframework.dataflow.cfg.node.LocalVariableNode) ThisNode(org.checkerframework.dataflow.cfg.node.ThisNode) AssignmentNode(org.checkerframework.dataflow.cfg.node.AssignmentNode) NullLiteralNode(org.checkerframework.dataflow.cfg.node.NullLiteralNode) FieldAccessNode(org.checkerframework.dataflow.cfg.node.FieldAccessNode) ReturnNode(org.checkerframework.dataflow.cfg.node.ReturnNode) MethodInvocationNode(org.checkerframework.dataflow.cfg.node.MethodInvocationNode) Node(org.checkerframework.dataflow.cfg.node.Node) LocalVariable(org.checkerframework.dataflow.expression.LocalVariable) MethodInvocationTree(com.sun.source.tree.MethodInvocationTree) MethodTree(com.sun.source.tree.MethodTree) VariableTree(com.sun.source.tree.VariableTree) Tree(com.sun.source.tree.Tree) FieldAccessNode(org.checkerframework.dataflow.cfg.node.FieldAccessNode) LocalVariableNode(org.checkerframework.dataflow.cfg.node.LocalVariableNode)

Example 2 with LocalVariable

use of org.checkerframework.dataflow.expression.LocalVariable in project checker-framework by typetools.

the class MustCallConsistencyAnalyzer method updateObligationsForPseudoAssignment.

/**
 * Update a set of tracked Obligations to account for a (pseudo-)assignment to some variable, as
 * in a gen-kill dataflow analysis problem. That is, add ("gen") and remove ("kill") resource
 * aliases from Obligations in the {@code obligations} set as appropriate based on the
 * (pseudo-)assignment performed by {@code node}. This method may also remove an Obligation
 * entirely if the analysis concludes that its resource alias set is empty because the last
 * tracked alias to it has been overwritten (including checking that the must-call obligations
 * were satisfied before the assignment).
 *
 * <p>Pseudo-assignments may include operations that "assign" to a temporary variable, exposing
 * the possible value flow into the variable. E.g., for a ternary expression {@code b ? x : y}
 * whose temporary variable is {@code t}, this method may process "assignments" {@code t = x} and
 * {@code t = y}, thereby capturing the two possible values of {@code t}.
 *
 * @param obligations the tracked Obligations, which will be side-effected
 * @param node the node performing the pseudo-assignment; it is not necessarily an assignment node
 * @param lhsVar the left-hand side variable for the pseudo-assignment
 * @param rhs the right-hand side for the pseudo-assignment, which must have been converted to a
 *     temporary variable (via a call to {@link
 *     ResourceLeakAnnotatedTypeFactory#getTempVarForNode})
 */
private void updateObligationsForPseudoAssignment(Set<Obligation> obligations, Node node, LocalVariableNode lhsVar, Node rhs) {
    // Replacements to eventually perform in Obligations.  This map is kept to avoid a
    // ConcurrentModificationException in the loop below.
    Map<Obligation, Obligation> replacements = new LinkedHashMap<>();
    // Cache to re-use on subsequent iterations.
    ResourceAlias aliasForAssignment = null;
    for (Obligation obligation : obligations) {
        // This is a non-null value iff the resource alias set for obligation needs to
        // change because of the pseudo-assignment. The value of this variable is the new
        // alias set for `obligation` if it is non-null.
        Set<ResourceAlias> newResourceAliasesForObligation = null;
        // Always kill the lhs var if it is present in the resource alias set for this Obligation
        // by removing it from the resource alias set.
        ResourceAlias aliasForLhs = obligation.getResourceAlias(lhsVar);
        if (aliasForLhs != null) {
            newResourceAliasesForObligation = new LinkedHashSet<>(obligation.resourceAliases);
            newResourceAliasesForObligation.remove(aliasForLhs);
        }
        // by adding it to the resource alias set.
        if (rhs instanceof LocalVariableNode && obligation.canBeSatisfiedThrough((LocalVariableNode) rhs)) {
            LocalVariableNode rhsVar = (LocalVariableNode) rhs;
            if (newResourceAliasesForObligation == null) {
                newResourceAliasesForObligation = new LinkedHashSet<>(obligation.resourceAliases);
            }
            if (aliasForAssignment == null) {
                // It is possible to observe assignments to temporary variables, e.g.,
                // synthetic assignments to ternary expression variables in the CFG.  For such
                // cases, use the tree associated with the temp var for the resource alias,
                // as that is the tree where errors should be reported.
                Tree treeForAlias = typeFactory.isTempVar(lhsVar) ? typeFactory.getTreeForTempVar(lhsVar) : node.getTree();
                aliasForAssignment = new ResourceAlias(new LocalVariable(lhsVar), treeForAlias);
            }
            newResourceAliasesForObligation.add(aliasForAssignment);
            // Remove temp vars from tracking once they are assigned to another location.
            if (typeFactory.isTempVar(rhsVar)) {
                ResourceAlias aliasForRhs = obligation.getResourceAlias(rhsVar);
                if (aliasForRhs != null) {
                    newResourceAliasesForObligation.remove(aliasForRhs);
                }
            }
        }
        // Obligation.
        if (newResourceAliasesForObligation == null) {
            continue;
        }
        if (newResourceAliasesForObligation.isEmpty()) {
            // Because the last reference to the resource has been overwritten, check the must-call
            // obligation.
            MustCallAnnotatedTypeFactory mcAtf = typeFactory.getTypeFactoryOfSubchecker(MustCallChecker.class);
            checkMustCall(obligation, typeFactory.getStoreBefore(node), mcAtf.getStoreBefore(node), "variable overwritten by assignment " + node.getTree());
            replacements.put(obligation, null);
        } else {
            replacements.put(obligation, new Obligation(newResourceAliasesForObligation));
        }
    }
    // Finally, update the set of Obligations according to the replacements.
    for (Map.Entry<Obligation, Obligation> entry : replacements.entrySet()) {
        obligations.remove(entry.getKey());
        if (entry.getValue() != null && !entry.getValue().resourceAliases.isEmpty()) {
            obligations.add(entry.getValue());
        }
    }
}
Also used : LocalVariable(org.checkerframework.dataflow.expression.LocalVariable) MethodInvocationTree(com.sun.source.tree.MethodInvocationTree) MethodTree(com.sun.source.tree.MethodTree) VariableTree(com.sun.source.tree.VariableTree) Tree(com.sun.source.tree.Tree) MustCallAnnotatedTypeFactory(org.checkerframework.checker.mustcall.MustCallAnnotatedTypeFactory) Map(java.util.Map) LinkedHashMap(java.util.LinkedHashMap) LocalVariableNode(org.checkerframework.dataflow.cfg.node.LocalVariableNode) LinkedHashMap(java.util.LinkedHashMap)

Example 3 with LocalVariable

use of org.checkerframework.dataflow.expression.LocalVariable in project checker-framework by typetools.

the class MustCallConsistencyAnalyzer method computeOwningParameters.

/**
 * Finds {@link Owning} formal parameters for the method corresponding to a CFG.
 *
 * @param cfg the CFG
 * @return the owning formal parameters of the method that corresponds to the given cfg, or an
 *     empty set if the given CFG doesn't correspond to a method body
 */
private Set<Obligation> computeOwningParameters(ControlFlowGraph cfg) {
    // TODO what about lambdas?
    if (cfg.getUnderlyingAST().getKind() == Kind.METHOD) {
        MethodTree method = ((UnderlyingAST.CFGMethod) cfg.getUnderlyingAST()).getMethod();
        Set<Obligation> result = new LinkedHashSet<>(1);
        for (VariableTree param : method.getParameters()) {
            Element paramElement = TreeUtils.elementFromDeclaration(param);
            boolean hasMustCallAlias = typeFactory.hasMustCallAlias(paramElement);
            if (hasMustCallAlias || (typeFactory.declaredTypeHasMustCall(param) && !checker.hasOption(MustCallChecker.NO_LIGHTWEIGHT_OWNERSHIP) && paramElement.getAnnotation(Owning.class) != null)) {
                result.add(new Obligation(ImmutableSet.of(new ResourceAlias(new LocalVariable(paramElement), param, hasMustCallAlias))));
                // Increment numMustCall for each @Owning parameter tracked by the enclosing method.
                incrementNumMustCall(paramElement);
            }
        }
        return result;
    }
    return Collections.emptySet();
}
Also used : LinkedHashSet(java.util.LinkedHashSet) MethodTree(com.sun.source.tree.MethodTree) TypeElement(javax.lang.model.element.TypeElement) Element(javax.lang.model.element.Element) VariableElement(javax.lang.model.element.VariableElement) ExecutableElement(javax.lang.model.element.ExecutableElement) VariableTree(com.sun.source.tree.VariableTree) LocalVariable(org.checkerframework.dataflow.expression.LocalVariable)

Example 4 with LocalVariable

use of org.checkerframework.dataflow.expression.LocalVariable in project checker-framework by typetools.

the class StringToJavaExpression method atLambdaParameter.

/**
 * Parses a string as if it were written at one of the parameters of {@code lambdaTree}.
 * Parameters of the lambda are expressed as {@link LocalVariable}s.
 *
 * @param expression a Java expression to parse
 * @param lambdaTree the lambda tree
 * @param parentPath path to the parent of {@code lambdaTree}; required because the expression can
 *     reference final local variables of the enclosing method
 * @param checker checker used to get the {@link
 *     javax.annotation.processing.ProcessingEnvironment} and current {@link
 *     com.sun.source.tree.CompilationUnitTree}
 * @return a {@code JavaExpression} for {@code expression}
 * @throws JavaExpressionParseException if {@code expression} cannot be parsed
 */
static JavaExpression atLambdaParameter(String expression, LambdaExpressionTree lambdaTree, TreePath parentPath, SourceChecker checker) throws JavaExpressionParseException {
    TypeMirror enclosingType = TreeUtils.typeOf(TreePathUtil.enclosingClass(parentPath));
    JavaExpression receiver = JavaExpression.getPseudoReceiver(parentPath, enclosingType);
    // If receiver isn't a ThisReference, then the lambda is in a static context and "this"
    // cannot be referenced in the expression.
    ThisReference thisReference = receiver instanceof ThisReference ? (ThisReference) receiver : null;
    List<JavaExpression> paramsAsLocals = new ArrayList<>(lambdaTree.getParameters().size());
    List<FormalParameter> parameters = new ArrayList<>(lambdaTree.getParameters().size());
    int oneBasedIndex = 1;
    for (VariableTree arg : lambdaTree.getParameters()) {
        LocalVariable param = (LocalVariable) JavaExpression.fromVariableTree(arg);
        paramsAsLocals.add(param);
        parameters.add(new FormalParameter(oneBasedIndex, (VariableElement) param.getElement()));
        oneBasedIndex++;
    }
    JavaExpression javaExpr = JavaExpressionParseUtil.parse(expression, enclosingType, thisReference, parameters, parentPath, checker.getPathToCompilationUnit(), checker.getProcessingEnvironment());
    return ViewpointAdaptJavaExpression.viewpointAdapt(javaExpr, paramsAsLocals);
}
Also used : FormalParameter(org.checkerframework.dataflow.expression.FormalParameter) JavaExpression(org.checkerframework.dataflow.expression.JavaExpression) ViewpointAdaptJavaExpression(org.checkerframework.dataflow.expression.ViewpointAdaptJavaExpression) TypeMirror(javax.lang.model.type.TypeMirror) ArrayList(java.util.ArrayList) VariableTree(com.sun.source.tree.VariableTree) LocalVariable(org.checkerframework.dataflow.expression.LocalVariable) VariableElement(javax.lang.model.element.VariableElement) ThisReference(org.checkerframework.dataflow.expression.ThisReference)

Example 5 with LocalVariable

use of org.checkerframework.dataflow.expression.LocalVariable in project checker-framework by typetools.

the class InitializationVisitor method checkContract.

@Override
protected boolean checkContract(JavaExpression expr, AnnotationMirror necessaryAnnotation, AnnotationMirror inferredAnnotation, CFAbstractStore<?, ?> store) {
    // also use the information about initialized fields to check contracts
    final AnnotationMirror invariantAnno = atypeFactory.getFieldInvariantAnnotation();
    if (!atypeFactory.getQualifierHierarchy().isSubtype(invariantAnno, necessaryAnnotation) || !(expr instanceof FieldAccess)) {
        return super.checkContract(expr, necessaryAnnotation, inferredAnnotation, store);
    }
    if (expr.containsUnknown()) {
        return false;
    }
    FieldAccess fa = (FieldAccess) expr;
    if (fa.getReceiver() instanceof ThisReference || fa.getReceiver() instanceof ClassName) {
        @SuppressWarnings("unchecked") Store s = (Store) store;
        if (s.isFieldInitialized(fa.getField())) {
            AnnotatedTypeMirror fieldType = atypeFactory.getAnnotatedType(fa.getField());
            // is this an invariant-field?
            if (AnnotationUtils.containsSame(fieldType.getAnnotations(), invariantAnno)) {
                return true;
            }
        }
    } else {
        @SuppressWarnings("unchecked") Value value = (Value) store.getValue(fa.getReceiver());
        Set<AnnotationMirror> receiverAnnoSet;
        if (value != null) {
            receiverAnnoSet = value.getAnnotations();
        } else if (fa.getReceiver() instanceof LocalVariable) {
            Element elem = ((LocalVariable) fa.getReceiver()).getElement();
            AnnotatedTypeMirror receiverType = atypeFactory.getAnnotatedType(elem);
            receiverAnnoSet = receiverType.getAnnotations();
        } else {
            // Is there anything better we could do?
            return false;
        }
        boolean isReceiverInitialized = false;
        for (AnnotationMirror anno : receiverAnnoSet) {
            if (atypeFactory.isInitialized(anno)) {
                isReceiverInitialized = true;
            }
        }
        AnnotatedTypeMirror fieldType = atypeFactory.getAnnotatedType(fa.getField());
        // has the invariant type.
        if (isReceiverInitialized && AnnotationUtils.containsSame(fieldType.getAnnotations(), invariantAnno)) {
            return true;
        }
    }
    return super.checkContract(expr, necessaryAnnotation, inferredAnnotation, store);
}
Also used : AnnotationMirror(javax.lang.model.element.AnnotationMirror) ExecutableElement(javax.lang.model.element.ExecutableElement) Element(javax.lang.model.element.Element) ClassName(org.checkerframework.dataflow.expression.ClassName) CFAbstractValue(org.checkerframework.framework.flow.CFAbstractValue) FieldInitialValue(org.checkerframework.framework.flow.CFAbstractAnalysis.FieldInitialValue) LocalVariable(org.checkerframework.dataflow.expression.LocalVariable) CFAbstractStore(org.checkerframework.framework.flow.CFAbstractStore) FieldAccess(org.checkerframework.dataflow.expression.FieldAccess) ThisReference(org.checkerframework.dataflow.expression.ThisReference) AnnotatedTypeMirror(org.checkerframework.framework.type.AnnotatedTypeMirror)

Aggregations

LocalVariable (org.checkerframework.dataflow.expression.LocalVariable)17 FieldAccess (org.checkerframework.dataflow.expression.FieldAccess)11 ClassName (org.checkerframework.dataflow.expression.ClassName)8 ThisReference (org.checkerframework.dataflow.expression.ThisReference)7 Element (javax.lang.model.element.Element)6 ExecutableElement (javax.lang.model.element.ExecutableElement)6 VariableElement (javax.lang.model.element.VariableElement)6 MethodCall (org.checkerframework.dataflow.expression.MethodCall)6 MethodTree (com.sun.source.tree.MethodTree)5 VariableTree (com.sun.source.tree.VariableTree)5 Map (java.util.Map)5 TypeElement (javax.lang.model.element.TypeElement)5 JavaExpression (org.checkerframework.dataflow.expression.JavaExpression)5 HashMap (java.util.HashMap)4 ArrayAccess (org.checkerframework.dataflow.expression.ArrayAccess)4 MethodInvocationTree (com.sun.source.tree.MethodInvocationTree)3 Tree (com.sun.source.tree.Tree)3 ArrayList (java.util.ArrayList)3 JavaExpressionParseException (org.checkerframework.framework.util.JavaExpressionParseUtil.JavaExpressionParseException)3 StringToJavaExpression (org.checkerframework.framework.util.StringToJavaExpression)3