Class DisallowedSideEffects

java.lang.Object
com.sun.source.util.TreeScanner<Void,Void>
com.sun.source.util.TreePathScanner<Void,Void>
org.checkerframework.common.basetype.DisallowedSideEffects
All Implemented Interfaces:
TreeVisitor<Void,Void>

public class DisallowedSideEffects extends TreePathScanner<Void,Void>
Scanner that collects the expressions a method side-effects, beyond those listed in its SideEffectsOnly annotation, and reports an error for each one.

Clients use the static method checkSideEffectsOnly(com.sun.source.util.TreePath, java.util.List<org.checkerframework.dataflow.expression.JavaExpression>, org.checkerframework.common.basetype.BaseTypeChecker, com.sun.source.tree.MethodTree, boolean, boolean).

  • Field Details

    • disallowedSideEffects

      protected final List<org.plumelib.util.IPair<Tree,JavaExpression>> disallowedSideEffects
      Expressions the method side-effects that are not in its SideEffectsOnly annotation.
    • sideEffectsOnlyExpressionsFromAnnotation

      protected final List<JavaExpression> sideEffectsOnlyExpressionsFromAnnotation
      List of expressions specified as annotation arguments in the SideEffectsOnly annotation of the method being checked.
    • freshLocals

      protected final Set<VariableElement> freshLocals
      The local variables that always hold an object that the method being checked created. Modifying such an object is not a side effect that is visible to the caller.
    • checker

      protected final BaseTypeChecker checker
      The checker to use.
    • assumeSideEffectFree

      protected final boolean assumeSideEffectFree
      True if "-AassumeSideEffectFree" or "-AassumePure" was passed on the command line.
    • assumePureGetters

      protected final boolean assumePureGetters
      True if "-AassumePureGetters" was passed on the command line.
  • Constructor Details

    • DisallowedSideEffects

      protected DisallowedSideEffects(List<JavaExpression> sideEffectsOnlyExpressions, Set<VariableElement> freshLocals, BaseTypeChecker checker, boolean assumeSideEffectFree, boolean assumePureGetters)
      Creates a new DisallowedSideEffects.
      Parameters:
      sideEffectsOnlyExpressions - the arguments/values of the SideEffectsOnly annotation of the method being checked
      freshLocals - the local variables that always hold an object that the method being checked created
      checker - the checker to use
      assumeSideEffectFree - true if every method should be assumed to be side-effect-free
      assumePureGetters - true if every getter should be assumed to be side-effect-free
  • Method Details

    • checkSideEffectsOnly

      public static void checkSideEffectsOnly(TreePath statement, List<JavaExpression> sideEffectsOnlyExpressions, BaseTypeChecker checker, MethodTree methodTree, boolean assumeSideEffectFree, boolean assumePureGetters)
      Issues warnings about side effects in statement beyond the @SideEffectsOnly annotation.
      Parameters:
      statement - the statement to check; currently, at the only call site it is a method body
      sideEffectsOnlyExpressions - the values in the SideEffectsOnly annotation
      checker - the checker to use
      methodTree - the method that contains statement
      assumeSideEffectFree - true if every method should be assumed to be side-effect-free
      assumePureGetters - true if every getter should be assumed to be side-effect-free
    • checkImplicitSuperCall

      protected void checkImplicitSuperCall(Tree node, ExecutableElement constructorElt)
      Checks the call to the superclass's no-argument constructor that the compiler inserts in a constructor that contains no explicit this(...) or super(...) call, and records any side effect of it that is beyond what the SideEffectsOnly annotation of the constructor being checked permits.
      Parameters:
      node - the tree to report an error at
      constructorElt - the constructor being checked
    • checkSideEffectsOnly

      public static void checkSideEffectsOnly(TreePath statement, List<JavaExpression> sideEffectsOnlyExpressions, BaseTypeChecker checker, CharSequence methodName, boolean assumeSideEffectFree, boolean assumePureGetters)
      Issues warnings about side effects beyond the given expressions, which come from a @SideEffectsOnly annotation.

      Unlike the other overload, this one does not treat the code as a constructor body: the caller has already put every permitted expression in sideEffectsOnlyExpressions.

      Parameters:
      statement - the statement to check; a method body or the body of a lambda
      sideEffectsOnlyExpressions - the expressions that the code may side-effect, written in terms of the code being checked
      checker - the checker to use
      methodName - the name to use in diagnostics for the code being checked
      assumeSideEffectFree - true if every method should be assumed to be side-effect-free
      assumePureGetters - true if every getter should be assumed to be side-effect-free
    • report

      protected void report(CharSequence methodName)
      Reports an error for each side effect that this scanner found and that the SideEffectsOnly annotation does not permit.
      Parameters:
      methodName - the name to use in diagnostics for the code that was checked
    • expressionFromTree

      protected static JavaExpression expressionFromTree(ExpressionTree tree)
      Returns the JavaExpression for the given tree, with every use of super replaced by this. super.f and this.f are the same location, so they must be compared alike against the SideEffectsOnly annotation, which cannot mention super.
      Parameters:
      tree - an expression tree
      Returns:
      the JavaExpression for the tree, written in terms of this rather than super
    • visitMethodInvocation

      public Void visitMethodInvocation(MethodInvocationTree node, Void aVoid)
      Specified by:
      visitMethodInvocation in interface TreeVisitor<Void,Void>
      Overrides:
      visitMethodInvocation in class TreeScanner<Void,Void>
    • checkMethodInvocation

      protected void checkMethodInvocation(MethodInvocationTree node)
      Records the disallowed side effects of the given method invocation, and reports an error if the callee has no side-effect annotation. Does not scan the subtrees of the invocation.
      Parameters:
      node - a method invocation
    • modifiesNothing

      protected boolean modifiesNothing(ExecutableElement elt)
      Returns true if the given method promises to modify nothing that existed before it was called.
      Parameters:
      elt - a method or constructor
      Returns:
      true if the given method modifies nothing
    • calleeSideEffectedExpressions

      protected List<JavaExpression> calleeSideEffectedExpressions(MethodInvocationTree node, ExecutableElement invokedElem, Map<ExecutableElement,List<String>> seOnlyExpressionStrings)
      Returns the expressions that the invoked method may side-effect: the arguments/elements of its SideEffectsOnly annotation, viewpoint-adapted to the given call site.

      An expression that denotes an object that the call site allocates, in the sense of isNewObjectTree(com.sun.source.tree.ExpressionTree), is omitted from the result.

      Parameters:
      node - a call to a method to which a SideEffectsOnly annotation applies
      invokedElem - the invoked method
      seOnlyExpressionStrings - the SideEffectsOnly expressions that apply to invokedElem, indexed by the method whose declaration contains them
      Returns:
      the expressions that the invoked method side-effects, viewpoint-adapted to node
    • callSiteTree

      protected static @Nullable ExpressionTree callSiteTree(JavaExpression atDeclaration, ExecutableElement invokedElem, @Nullable ExpressionTree receiverTree, List<? extends ExpressionTree> argTrees)
      Returns the tree at the given call site that the given expression denotes in its entirety: the receiver if the expression is this, or the corresponding argument if the expression is a formal parameter. Returns null if the expression is neither, or if there is no such tree.

      A larger expression that merely contains this or a formal parameter, such as this.f, has no such tree: this.f denotes a different object than this does, and that object may have existed before the call.

      Parameters:
      atDeclaration - an expression written at the declaration of the invoked method
      invokedElem - the invoked method or constructor
      receiverTree - the receiver at the call site, or null if there is none
      argTrees - the arguments at the call site
      Returns:
      the tree that atDeclaration denotes at the call site, or null if there is none
    • isNewObjectTree

      protected static boolean isNewObjectTree(ExpressionTree tree)
      Returns true if the given tree is a new expression for an object, so its value is an object that the code being checked just created. Modifying such an object is not a side effect that is visible to the caller.

      This test is made on the tree rather than on the JavaExpression, because JavaExpression.fromTree(com.sun.source.tree.ExpressionTree) maps such a tree to Unknown, which does not record that the object is freshly allocated. A new expression for an array needs no such treatment: fromTree maps it to an ArrayCreation, which isFreshlyAllocated(org.checkerframework.dataflow.expression.JavaExpression) recognizes.

      Parameters:
      tree - an expression tree
      Returns:
      true if the given tree is a new expression for an object
    • visitEnhancedForLoop

      public Void visitEnhancedForLoop(EnhancedForLoopTree node, Void aVoid)
      Specified by:
      visitEnhancedForLoop in interface TreeVisitor<Void,Void>
      Overrides:
      visitEnhancedForLoop in class TreeScanner<Void,Void>
    • visitTry

      public Void visitTry(TryTree node, Void aVoid)
      Specified by:
      visitTry in interface TreeVisitor<Void,Void>
      Overrides:
      visitTry in class TreeScanner<Void,Void>
    • checkImplicitCall

      protected void checkImplicitCall(Tree node, ExecutableElement invokedElem, @Nullable JavaExpression receiver)
      Checks a call that the compiler introduces when it desugars the source code, and records any side effect of it that is beyond what the SideEffectsOnly annotation of the method being checked permits.
      Parameters:
      node - the tree to report an error at
      invokedElem - the implicitly invoked method or constructor, which takes no arguments
      receiver - the receiver of the call, or null if the receiver is an object that no caller can refer to: one that the desugaring created, or one that a new expression in the code being checked created
    • withReceiver

      protected static JavaExpression withReceiver(JavaExpression expr, JavaExpression receiver)
      Returns the given expression with every use of this replaced by the given receiver. This is viewpoint adaptation for a call that takes no arguments, so no formal parameter needs to be replaced.
      Parameters:
      expr - an expression written at a method's declaration
      receiver - the receiver of a call to that method
      Returns:
      the expression, written at the call site
    • noArgumentMethod

      protected @Nullable ExecutableElement noArgumentMethod(TypeMirror receiverType, String methodName)
      Returns the no-argument method with the given name that a call on an expression of the given type invokes, or null if there is no such method.
      Parameters:
      receiverType - the type of the receiver of the call
      methodName - the name of the method
      Returns:
      the invoked method, or null if the type has no such method
    • visitNewClass

      public Void visitNewClass(NewClassTree node, Void aVoid)
      Specified by:
      visitNewClass in interface TreeVisitor<Void,Void>
      Overrides:
      visitNewClass in class TreeScanner<Void,Void>
    • constructorSideEffectedExpressions

      protected List<JavaExpression> constructorSideEffectedExpressions(NewClassTree node, ExecutableElement constructorElt, AnnotationMirror seOnlyAnnotation)
      Returns the expressions that the invoked constructor side-effects: the arguments/elements of its SideEffectsOnly annotation, viewpoint-adapted to the given call site.

      The expression this is omitted from the result. In a constructor's annotation, this is the object being constructed, which did not exist before the call, so modifying it is not a side effect that is visible to the caller. A larger expression that merely contains this, such as this.f, gets no such exemption: its value may be an object that existed before the call, as it does for a constructor whose body contains this.f = p; where p is a formal parameter.

      An expression that denotes an object that the call site allocates, in the sense of isNewObjectTree(com.sun.source.tree.ExpressionTree), is also omitted.

      If an expression cannot be parsed, this reports purity.unparseable.sideeffectsonly and returns an empty list, just as calleeSideEffectedExpressions(com.sun.source.tree.MethodInvocationTree, javax.lang.model.element.ExecutableElement, java.util.Map<javax.lang.model.element.ExecutableElement, java.util.List<java.lang.String>>) does.

      Parameters:
      node - a call to a constructor that is annotated with SideEffectsOnly
      constructorElt - the invoked constructor
      seOnlyAnnotation - the invoked constructor's SideEffectsOnly annotation
      Returns:
      the expressions that the invoked constructor side-effects, viewpoint-adapted to node
    • isDisallowedSideEffectedExpression

      protected boolean isDisallowedSideEffectedExpression(JavaExpression expr)
      Returns true if the given expression is a side-effected expression beyond what is listed in the SideEffectsOnly annotation. That is, all of the following hold:

      Use this for an expression whose value is mutated, such as an expression that a callee modifies (e.g., one of the arguments). For an expression that is assigned to, use isDisallowedAssignmentTarget(org.checkerframework.dataflow.expression.JavaExpression).

      Parameters:
      expr - the expression to check for side-effecting
      Returns:
      true if the given expression is a side-effected expression beyond what is listed in the SideEffectsOnly annotation
    • isDisallowedAssignmentTarget

      protected boolean isDisallowedAssignmentTarget(JavaExpression expr)
      Returns true if assigning to the given expression is a side effect beyond what is listed in the SideEffectsOnly annotation. That is, all of the following hold:
      Parameters:
      expr - the expression that is assigned to
      Returns:
      true if assigning to the given expression is a side effect beyond what is listed in the SideEffectsOnly annotation
    • isFreshlyAllocated

      protected boolean isFreshlyAllocated(JavaExpression expr)
      Returns true if the given expression always evaluates to an object that the method being checked created: an array creation expression, or a local variable that always holds such an object. The object did not exist before the call, so modifying it is not a side effect that is visible to the caller.

      If the object escapes -- if the method stores it into pre-existing state -- then that store is itself a side effect, which is reported unless the annotation covers it. If it is covered, then so is every modification of the object, because the object is then reached through a listed expression.

      A new expression for an object, rather than for an array, has no JavaExpression representation; see isNewObjectTree(com.sun.source.tree.ExpressionTree).

      Parameters:
      expr - an expression
      Returns:
      true if the given expression evaluates to an object that this method created
    • isPartOfFreshlyAllocated

      protected boolean isPartOfFreshlyAllocated(JavaExpression expr)
      Returns true if the given expression is a field or an array element of an object that the method being checked created. Assigning to it is not visible to the caller.

      Only the object's own fields and elements qualify. A field of a field does not: fresh.f may be an object that existed before the call, so assigning to fresh.f.g is visible to the caller.

      Parameters:
      expr - an expression
      Returns:
      true if the given expression is a field or array element of an object that this method created
    • isCoveredByAnnotation

      protected boolean isCoveredByAnnotation(JavaExpression expr)
      Returns true if the given expression is listed in the SideEffectsOnly annotation or is reached through one of the listed expressions.
      Parameters:
      expr - the expression to look for
      Returns:
      true if the given expression is covered by the SideEffectsOnly annotation
    • visitLambdaExpression

      public Void visitLambdaExpression(LambdaExpressionTree node, Void aVoid)
      Specified by:
      visitLambdaExpression in interface TreeVisitor<Void,Void>
      Overrides:
      visitLambdaExpression in class TreeScanner<Void,Void>
    • visitClass

      public Void visitClass(ClassTree node, Void aVoid)
      Specified by:
      visitClass in interface TreeVisitor<Void,Void>
      Overrides:
      visitClass in class TreeScanner<Void,Void>
    • visitAnnotation

      public Void visitAnnotation(AnnotationTree node, Void aVoid)
      Specified by:
      visitAnnotation in interface TreeVisitor<Void,Void>
      Overrides:
      visitAnnotation in class TreeScanner<Void,Void>
    • visitAssignment

      public Void visitAssignment(AssignmentTree node, Void aVoid)
      Specified by:
      visitAssignment in interface TreeVisitor<Void,Void>
      Overrides:
      visitAssignment in class TreeScanner<Void,Void>
    • visitUnary

      public Void visitUnary(UnaryTree node, Void aVoid)
      Specified by:
      visitUnary in interface TreeVisitor<Void,Void>
      Overrides:
      visitUnary in class TreeScanner<Void,Void>
    • visitCompoundAssignment

      public Void visitCompoundAssignment(CompoundAssignmentTree node, Void aVoid)
      Specified by:
      visitCompoundAssignment in interface TreeVisitor<Void,Void>
      Overrides:
      visitCompoundAssignment in class TreeScanner<Void,Void>
    • freshLocals

      protected static Set<VariableElement> freshLocals(List<? extends Tree> trees)
      Returns the local variables that always hold an object that the given code created: those that are assigned only new expressions.
      Parameters:
      trees - the code being checked: the body of the method, plus the instance initializers if the method is a constructor
      Returns:
      the local variables that always hold an object that trees created