-
Notifications
You must be signed in to change notification settings - Fork 3
Implements SideEffectsOnly checking. #216
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from 95 commits
427c55a
0ad09bb
15ca2db
2857929
21dc0a4
ef59d5e
40fdfd8
ac6d8ca
df56c0d
48d2d42
595de2c
9347508
1459a9c
39edb6d
b3ecb7e
7300132
8c3a428
c34dd98
1c9d4ad
dcc0390
a2dd514
34c456b
5eb932a
5fd464f
a0442b4
ce77c26
171090d
6877a37
8a04d7f
245984b
d7a6c6a
837128d
ec10c52
201d09b
39f0044
835712a
9bbe372
3e0fbe7
5f03c88
d1dc0cb
9344756
5afaf07
237e037
f93a6dc
4590c3d
810a45a
6c3560e
d4d967d
9b85e87
d9cd13d
4712584
9275dee
3d2caa1
9750682
875f638
0541f0f
8ea0e1e
3092354
4a11c51
bf86930
dee4cd3
6e5c391
87bd9a7
cbc6afb
7e0337c
94176a4
d5c2fea
a2c749e
0d5d7b4
2abdc8c
a8cad8c
1cc560e
b446e3c
8e62d1e
533f1eb
7e6f352
9f19380
0288bdf
e09d7ea
42c6748
0e478f5
72647aa
f91ac19
fc91585
4bcbb3b
ebe7036
e6a61f5
990e6e2
9220596
fccd5e6
6d2d9fa
e5c48be
3c8878c
a4439a1
15b3a08
8dd7d94
1264b07
5b0dbc5
1ecd0fe
a984534
177c221
eb71099
a1115c7
30f4258
b630298
d1b69c4
d9e3336
9632766
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,29 @@ | ||
| package org.checkerframework.dataflow.qual; | ||
|
|
||
| import java.lang.annotation.Documented; | ||
| import java.lang.annotation.ElementType; | ||
| import java.lang.annotation.Retention; | ||
| import java.lang.annotation.RetentionPolicy; | ||
| import java.lang.annotation.Target; | ||
| import org.checkerframework.framework.qual.JavaExpression; | ||
|
|
||
| /** | ||
| * A method annotated with the declaration annotation {@code @SideEffectsOnly(A, B)} performs | ||
| * side-effects on at most the expressions A and B. All other expressions have the same value before | ||
| * and after a call. | ||
| * | ||
| * @checker_framework.manual #type-refinement-purity Specifying side effects | ||
| */ | ||
| @Documented | ||
| @Retention(RetentionPolicy.RUNTIME) | ||
| @Target({ElementType.METHOD, ElementType.CONSTRUCTOR}) | ||
| public @interface SideEffectsOnly { | ||
| /** | ||
| * An upper bound on the expressions that this method side effects. | ||
| * | ||
| * @return Java expressions that the annotated method might side-effect | ||
| * @checker_framework.manual #java-expressions-as-arguments Syntax of Java expressions | ||
| */ | ||
| @JavaExpression | ||
| public String[] value(); | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,198 @@ | ||
| package org.checkerframework.common.basetype; | ||
|
|
||
| import com.sun.source.tree.AssignmentTree; | ||
| import com.sun.source.tree.CatchTree; | ||
| import com.sun.source.tree.CompoundAssignmentTree; | ||
| import com.sun.source.tree.MethodInvocationTree; | ||
| import com.sun.source.tree.NewClassTree; | ||
| import com.sun.source.tree.Tree; | ||
| import com.sun.source.tree.UnaryTree; | ||
| import com.sun.source.util.TreePath; | ||
| import com.sun.source.util.TreePathScanner; | ||
| import java.util.ArrayList; | ||
| import java.util.List; | ||
| import javax.annotation.processing.ProcessingEnvironment; | ||
| import javax.lang.model.element.AnnotationMirror; | ||
| import javax.lang.model.element.Element; | ||
| import javax.lang.model.element.ExecutableElement; | ||
| import org.checkerframework.dataflow.expression.JavaExpression; | ||
| import org.checkerframework.dataflow.qual.Pure; | ||
| import org.checkerframework.dataflow.qual.SideEffectFree; | ||
| import org.checkerframework.dataflow.qual.SideEffectsOnly; | ||
| import org.checkerframework.framework.util.JavaExpressionParseUtil; | ||
| import org.checkerframework.framework.util.StringToJavaExpression; | ||
| import org.checkerframework.javacutil.AnnotationProvider; | ||
| import org.checkerframework.javacutil.AnnotationUtils; | ||
| import org.checkerframework.javacutil.Pair; | ||
| import org.checkerframework.javacutil.TreeUtils; | ||
|
|
||
| /** | ||
| * For methods annotated with {@link SideEffectsOnly}, computes expressions that are side-effected | ||
| * but not permitted by the annotation. | ||
| */ | ||
| public class SideEffectsOnlyAnnoChecker { | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm curious why this is a brand-new class rather than a modification to the existing mutation checker.
Owner
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I am not aware of a mutation checker and couldn’t find it in the manual either. Could you please point me to that class?
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. By "mutation checker" I meant Thank you!
Owner
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thank you for the suggestions. I tried merging the two functionalities into |
||
| /** | ||
| * Returns the computed {@code SideEffectsOnlyResult}. | ||
| * | ||
| * @param statement TreePath | ||
| * @param annoProvider AnnotationProvider | ||
t-rasmud marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| * @param sideEffectsOnlyExpressions List of JavaExpression | ||
| * @param processingEnv ProcessingEnvironment | ||
| * @param checker BaseTypeChecker | ||
t-rasmud marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| * @return SideEffectsOnlyResult | ||
| */ | ||
| public static SideEffectsOnlyResult checkSideEffectsOnly( | ||
| TreePath statement, | ||
| AnnotationProvider annoProvider, | ||
| List<JavaExpression> sideEffectsOnlyExpressions, | ||
| ProcessingEnvironment processingEnv, | ||
| BaseTypeChecker checker) { | ||
| SideEffectsOnlyCheckerHelper helper = | ||
| new SideEffectsOnlyCheckerHelper( | ||
| annoProvider, sideEffectsOnlyExpressions, processingEnv, checker); | ||
| helper.scan(statement, null); | ||
| return helper.sideEffectsOnlyResult; | ||
| } | ||
|
|
||
| /** SideEffectsOnlyResult. */ | ||
t-rasmud marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| public static class SideEffectsOnlyResult { | ||
| /** | ||
| * List of expressions a method side-effects that are not specified in the list of arguments to | ||
| * {@link SideEffectsOnly}. | ||
| */ | ||
| protected final List<Pair<Tree, JavaExpression>> seOnlyIncorrectExprs = new ArrayList<>(1); | ||
|
|
||
| /** | ||
| * Adds {@code t} and {@code javaExpr} as a Pair to seOnlyIncorrectExprs. | ||
| * | ||
| * @param t Tree | ||
| * @param javaExpr JavaExpression | ||
| */ | ||
| public void addNotSEOnlyExpr(Tree t, JavaExpression javaExpr) { | ||
t-rasmud marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| seOnlyIncorrectExprs.add(Pair.of(t, javaExpr)); | ||
| } | ||
|
|
||
| /** | ||
| * Returns {@code seOnlyIncorrectExprs}. | ||
| * | ||
| * @return seOnlyIncorrectExprs | ||
| */ | ||
| public List<Pair<Tree, JavaExpression>> getSeOnlyResult() { | ||
| return seOnlyIncorrectExprs; | ||
| } | ||
| } | ||
|
|
||
| /** SideEffectsOnlyCheckerHelper. */ | ||
t-rasmud marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| protected static class SideEffectsOnlyCheckerHelper extends TreePathScanner<Void, Void> { | ||
| /** Result computed by SideEffectsOnlyCheckerHelper. */ | ||
| SideEffectsOnlyResult sideEffectsOnlyResult = new SideEffectsOnlyResult(); | ||
| /** | ||
| * List of expressions specified as annotation arguments in {@link SideEffectsOnly} annotation. | ||
| */ | ||
| List<JavaExpression> sideEffectsOnlyExpressions; | ||
|
|
||
| /** AnnotationProvider. */ | ||
| protected final AnnotationProvider annoProvider; | ||
| /** Processing Environment. */ | ||
| ProcessingEnvironment processingEnv; | ||
| /** BaseTypeChecker. */ | ||
| BaseTypeChecker checker; | ||
t-rasmud marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| /** | ||
| * Constructor for SideEffectsOnlyCheckerHelper. | ||
| * | ||
| * @param annoProvider AnnotationProvider | ||
| * @param sideEffectsOnlyExpressions List of JavaExpressions | ||
| * @param processingEnv ProcessingEnvironment | ||
| * @param checker BaseTypeChecker | ||
| */ | ||
| public SideEffectsOnlyCheckerHelper( | ||
| AnnotationProvider annoProvider, | ||
| List<JavaExpression> sideEffectsOnlyExpressions, | ||
| ProcessingEnvironment processingEnv, | ||
| BaseTypeChecker checker) { | ||
| this.annoProvider = annoProvider; | ||
| this.sideEffectsOnlyExpressions = sideEffectsOnlyExpressions; | ||
| this.processingEnv = processingEnv; | ||
| this.checker = checker; | ||
| } | ||
|
|
||
| @Override | ||
| public Void visitCatch(CatchTree node, Void aVoid) { | ||
| return super.visitCatch(node, aVoid); | ||
| } | ||
|
|
||
| @Override | ||
| public Void visitMethodInvocation(MethodInvocationTree node, Void aVoid) { | ||
| Element treeElem = TreeUtils.elementFromTree(node); | ||
| AnnotationMirror pureAnno = annoProvider.getDeclAnnotation(treeElem, Pure.class); | ||
| AnnotationMirror sideEffectFreeAnno = | ||
| annoProvider.getDeclAnnotation(treeElem, SideEffectFree.class); | ||
| // If the invoked method is annotated as @Pure or @SideEffectFree, nothing to do. | ||
| if (pureAnno != null || sideEffectFreeAnno != null) { | ||
| return super.visitMethodInvocation(node, aVoid); | ||
| } | ||
|
|
||
| AnnotationMirror sideEffectsOnlyAnno = | ||
| annoProvider.getDeclAnnotation(treeElem, SideEffectsOnly.class); | ||
| // If the invoked method is not annotated with @SideEffectsOnly, | ||
| // add those arguments to seOnlyIncorrectExprs | ||
| // that are not present in sideEffectsOnlyExpressions. | ||
| if (sideEffectsOnlyAnno == null) { | ||
| JavaExpression receiverExpr = JavaExpression.getReceiver(node); | ||
| if (!sideEffectsOnlyExpressions.contains(receiverExpr)) { | ||
| sideEffectsOnlyResult.addNotSEOnlyExpr(node, receiverExpr); | ||
t-rasmud marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| } | ||
| } else { | ||
| // If the invoked method is annotated with @SideEffectsOnly, | ||
| // add annotation values to seOnlyIncorrectExprs | ||
| // that are not present in sideEffectsOnlyExpressions. | ||
| ExecutableElement sideEffectsOnlyValueElement = | ||
| TreeUtils.getMethod(SideEffectsOnly.class, "value", 0, processingEnv); | ||
| List<String> sideEffectsOnlyExpressionStrings = | ||
| AnnotationUtils.getElementValueArray( | ||
| sideEffectsOnlyAnno, sideEffectsOnlyValueElement, String.class); | ||
| List<JavaExpression> sideEffectsOnlyExprInv = new ArrayList<>(); | ||
| for (String st : sideEffectsOnlyExpressionStrings) { | ||
| try { | ||
| JavaExpression exprJe = StringToJavaExpression.atMethodInvocation(st, node, checker); | ||
| sideEffectsOnlyExprInv.add(exprJe); | ||
| } catch (JavaExpressionParseUtil.JavaExpressionParseException ex) { | ||
| checker.report(st, ex.getDiagMessage()); | ||
| } | ||
| } | ||
|
|
||
| for (JavaExpression expr : sideEffectsOnlyExprInv) { | ||
| if (!sideEffectsOnlyExpressions.contains(expr)) { | ||
| sideEffectsOnlyResult.addNotSEOnlyExpr(node, expr); | ||
| } | ||
| } | ||
| } | ||
| return super.visitMethodInvocation(node, aVoid); | ||
| } | ||
|
|
||
| @Override | ||
| public Void visitNewClass(NewClassTree node, Void aVoid) { | ||
| return super.visitNewClass(node, aVoid); | ||
| } | ||
|
|
||
| @Override | ||
| public Void visitAssignment(AssignmentTree node, Void aVoid) { | ||
| JavaExpression javaExpr = JavaExpression.fromTree(node.getVariable()); | ||
| if (!sideEffectsOnlyExpressions.contains(javaExpr)) { | ||
| sideEffectsOnlyResult.addNotSEOnlyExpr(node, javaExpr); | ||
| } | ||
| return super.visitAssignment(node, aVoid); | ||
| } | ||
|
|
||
| @Override | ||
| public Void visitUnary(UnaryTree node, Void aVoid) { | ||
| return super.visitUnary(node, aVoid); | ||
| } | ||
|
|
||
| @Override | ||
| public Void visitCompoundAssignment(CompoundAssignmentTree node, Void aVoid) { | ||
| return super.visitCompoundAssignment(node, aVoid); | ||
| } | ||
| } | ||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.