Skip to content
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

Add @SideEffectsOnly annotation #5582

Draft
wants to merge 173 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from 147 commits
Commits
Show all changes
173 commits
Select commit Hold shift + click to select a range
427c55a
temp commit
t-rasmud Dec 3, 2020
0ad09bb
side effects only checker
t-rasmud Dec 3, 2020
15ca2db
side effects only for this and field values
t-rasmud Dec 4, 2020
2857929
more tests
t-rasmud Dec 4, 2020
21dc0a4
add null checks
t-rasmud Dec 4, 2020
ef59d5e
side effects only javadoc
t-rasmud Dec 4, 2020
40fdfd8
purity suggestions test
t-rasmud Dec 4, 2020
ac6d8ca
nullness check
t-rasmud Dec 4, 2020
df56c0d
javadoc
t-rasmud Dec 4, 2020
48d2d42
remove sideeffectsonly from purity
t-rasmud Dec 5, 2020
595de2c
remove side_effects_only from purity kinds
t-rasmud Dec 5, 2020
9347508
Merge branch 'master' of github.com:t-rasmud/checker-framework into s…
t-rasmud Dec 6, 2020
1459a9c
remove unnecessary
t-rasmud Dec 6, 2020
39edb6d
add documentation
t-rasmud Dec 6, 2020
b3ecb7e
documentation for side effects only
t-rasmud Dec 6, 2020
7300132
fix formatting
t-rasmud Dec 6, 2020
8c3a428
fix side effects only for receiver
t-rasmud Dec 8, 2020
c34dd98
checher-qual after merging with master
t-rasmud Feb 16, 2021
1c9d4ad
remove unrelated
t-rasmud Feb 16, 2021
dcc0390
Merge branch 'master' of github.com:t-rasmud/checker-framework into s…
t-rasmud Feb 17, 2021
a2dd514
refactor qual
t-rasmud Feb 17, 2021
34c456b
Merge github.com:typetools/checker-framework
t-rasmud Feb 23, 2021
5eb932a
Merge github.com:typetools/checker-framework
t-rasmud Feb 23, 2021
5fd464f
Merge branch 'master' of github.com:t-rasmud/checker-framework into s…
t-rasmud Mar 2, 2021
a0442b4
remove unrelated test file
t-rasmud Mar 2, 2021
ce77c26
documenting toy checker
t-rasmud Mar 2, 2021
171090d
fix documentation and refactoring
t-rasmud Mar 2, 2021
6877a37
fix SideEffectsOnly documentation
t-rasmud Mar 2, 2021
8a04d7f
improves documentation for SideEffectsOnly
t-rasmud Mar 2, 2021
245984b
more documentation for SideEffectsOnly
t-rasmud Mar 2, 2021
d7a6c6a
Merge github.com:typetools/checker-framework
t-rasmud Mar 4, 2021
837128d
Merge branch 'master' of github.com:t-rasmud/checker-framework into s…
t-rasmud Mar 4, 2021
ec10c52
addressing review
t-rasmud Mar 4, 2021
201d09b
addressing review: entryset() to keyset()
t-rasmud Mar 4, 2021
39f0044
give meaningful qualifier names
t-rasmud Mar 4, 2021
835712a
address review
t-rasmud Mar 4, 2021
9bbe372
JavaExpression instead of String and clean-up
t-rasmud Mar 5, 2021
3e0fbe7
test case: change parameter names
t-rasmud Mar 5, 2021
5f03c88
fix documentation
t-rasmud Mar 5, 2021
d1dc0cb
Merge github.com:typetools/checker-framework
t-rasmud Mar 5, 2021
9344756
Merge branch 'master' of github.com:t-rasmud/checker-framework into s…
t-rasmud Mar 5, 2021
5afaf07
Merge branch 'master' of github.com:t-rasmud/checker-framework into s…
t-rasmud Mar 5, 2021
237e037
fix checker-qual
t-rasmud Mar 5, 2021
f93a6dc
checker-qual signedness fix
t-rasmud Mar 5, 2021
4590c3d
Merge ../checker-framework-branch-master into side-effects-only
mernst Mar 6, 2021
810a45a
fix documentation for SideEffectsOnly
t-rasmud Mar 9, 2021
6c3560e
SideEffectsOnly empty test
t-rasmud Mar 9, 2021
d4d967d
SideEffectsOnly documentation fixes
t-rasmud Mar 9, 2021
9b85e87
Merge branch 'side-effects-only' of github.com:t-rasmud/checker-frame…
mernst Mar 11, 2021
d9cd13d
Merge ../checker-framework-branch-master into side-effects-only
mernst Mar 18, 2021
4712584
Documentation improvements
mernst Mar 18, 2021
9275dee
Tweaks
mernst Mar 18, 2021
3d2caa1
Fix error and test case for SideEffectsOnly field
t-rasmud Mar 22, 2021
9750682
Merge branch 'master' of github.com:t-rasmud/checker-framework into s…
t-rasmud Mar 22, 2021
875f638
SideEffects relationship documentatio
t-rasmud Mar 22, 2021
0541f0f
Merge ../checker-framework-branch-master into side-effects-only
mernst Mar 23, 2021
8ea0e1e
Merge ../checker-framework-branch-master into side-effects-only
mernst Mar 24, 2021
3092354
Undo whitespace change
mernst Mar 24, 2021
4a11c51
Tweak documentation
mernst Mar 24, 2021
bf86930
clean imports
t-rasmud Mar 30, 2021
dee4cd3
report error correctly
t-rasmud Mar 30, 2021
6e5c391
address review comment
t-rasmud Mar 30, 2021
87bd9a7
reintroduce incorrectly deleted code
t-rasmud Mar 30, 2021
cbc6afb
merge with master; check; skip pre-commit
t-rasmud Mar 31, 2021
7e0337c
fix updateForMethodCall after merge
t-rasmud Mar 31, 2021
94176a4
fix imports
t-rasmud Apr 2, 2021
d5c2fea
Merge branch 'master' of github.com:t-rasmud/checker-framework into s…
t-rasmud Apr 3, 2021
a2c749e
fix deprecated method
t-rasmud Apr 6, 2021
0d5d7b4
javadoc fixes
t-rasmud Apr 7, 2021
2abdc8c
test case for empty @SideEffectsOnly
t-rasmud Apr 8, 2021
a8cad8c
temp commit: experimenting
t-rasmud Apr 9, 2021
1cc560e
SideEffectsOnly checker: reports error on some assignments
t-rasmud Apr 12, 2021
b446e3c
merge with master
t-rasmud May 19, 2021
8e62d1e
imports refactoring
t-rasmud May 20, 2021
533f1eb
Merge branch 'master' of github.com:t-rasmud/checker-framework into s…
t-rasmud May 31, 2021
7e6f352
Merge branch 'master' of github.com:t-rasmud/checker-framework into s…
t-rasmud Jun 25, 2021
9f19380
merge
t-rasmud Jul 8, 2021
0288bdf
handling method invocations
t-rasmud Jul 11, 2021
e09d7ea
SEOnly checking at method invocations
t-rasmud Jul 14, 2021
42c6748
Merge ../checker-framework-branch-master into side-effects-only
mernst Aug 3, 2021
0e478f5
Merge branch 'side-effects-only' of github.com:t-rasmud/checker-frame…
t-rasmud Aug 9, 2021
72647aa
Merge branch 'master' of github.com:t-rasmud/checker-framework into s…
t-rasmud Aug 9, 2021
f91ac19
fix package name
t-rasmud Aug 9, 2021
fc91585
test case: Check SideEffectsOnly
t-rasmud Aug 12, 2021
4bcbb3b
comments
t-rasmud Aug 15, 2021
ebe7036
Merge branch 'master' of github.com:t-rasmud/checker-framework into s…
t-rasmud Aug 15, 2021
e6a61f5
builds
t-rasmud Aug 23, 2021
990e6e2
Merge branch 'master' of github.com:t-rasmud/checker-framework into s…
t-rasmud Oct 14, 2021
9220596
SideEffectsOnlyAnnoChecker
t-rasmud Oct 14, 2021
fccd5e6
test case fixes
t-rasmud Oct 14, 2021
6d2d9fa
javadoc
t-rasmud Oct 18, 2021
e5c48be
Merge branch 'master' of github.com:t-rasmud/checker-framework into s…
t-rasmud Oct 18, 2021
3c8878c
documentation for -AcheckSideEffectsOnlyAnnotation
t-rasmud Oct 20, 2021
a4439a1
Merge branch 'master' of github.com:t-rasmud/checker-framework into s…
t-rasmud Oct 21, 2021
15b3a08
Documentation tweaks
mernst Oct 21, 2021
8dd7d94
Tweak comment
mernst Oct 27, 2021
1264b07
change method name
t-rasmud Nov 2, 2021
5b0dbc5
fix error message key
t-rasmud Nov 2, 2021
1ecd0fe
checkPurityAnnotations flag checks SideEffectsOnly
t-rasmud Nov 2, 2021
a984534
report error if invoked method not @SideEffectsOnly
t-rasmud Nov 6, 2021
177c221
report error if invoked method not @SideEffectsOnly
t-rasmud Nov 6, 2021
eb71099
adding test case
t-rasmud Nov 8, 2021
a1115c7
fixing javadoc
t-rasmud Nov 8, 2021
30f4258
fixing javadoc
t-rasmud Nov 8, 2021
b630298
testing CheckSideEffectsOnly functionality in Purity Checker
t-rasmud Nov 14, 2021
d1b69c4
minor
t-rasmud Nov 14, 2021
d9e3336
fix
t-rasmud Nov 15, 2021
9632766
Merge branch 'master' of github.com:t-rasmud/checker-framework into s…
t-rasmud Nov 22, 2021
b0a4dd0
Merge ../checker-framework-branch-master into side-effects-only
mernst Jan 5, 2023
2d33f32
Tweaks
mernst Jan 5, 2023
5a1fc70
Merge ../checker-framework-branch-master into side-effects-only
mernst Jan 8, 2023
dfcd7a9
Merge ../checker-framework-branch-master into side-effects-only
mernst Jan 26, 2023
f595e57
Merge ../checker-framework-branch-master into side-effects-only
mernst Jan 26, 2023
58a847e
Assign `body` once
mernst Jan 26, 2023
b35a2d7
Merge branch 'master' into side-effects-only
mernst Jan 26, 2023
b4ed608
Add default constructors as required by JDK 19
mernst Jan 26, 2023
4c2764b
Merge ../checker-framework-branch-master into side-effects-only
mernst Jan 26, 2023
c88351c
Remove unneeded option
mernst Jan 26, 2023
0161dfb
Tweak comments
mernst Jan 26, 2023
8d97f20
Rename
mernst Jan 26, 2023
f639ba2
Add documentation
mernst Jan 26, 2023
1463fff
Rename variable
mernst Jan 26, 2023
67c0e46
Merge branch 'side-effects-only' of github.com:mernst/checker-framewo…
mernst Jan 26, 2023
3b6c532
Merge ../checker-framework-branch-master into side-effects-only
mernst Feb 2, 2023
d74c8a4
Checkpoint
mernst Feb 3, 2023
ca34d95
Merge ../checker-framework-branch-master into side-effects-only
mernst Feb 3, 2023
6c5d10c
Merge branch 'master' into side-effects-only
mernst Feb 3, 2023
9d1f067
Merge ../checker-framework-branch-master into side-effects-only
mernst Feb 20, 2023
8b5e726
Merge branch 'master' into side-effects-only
mernst Mar 14, 2023
0832758
Tweak definition of `@SideEffectsOnly`
mernst Jan 22, 2024
03bca3f
Reduce diffs with upstream
mernst Jan 22, 2024
d52132e
Merge ../checker-framework-branch-master into side-effects-only
mernst Jan 22, 2024
98607fe
Merge ../checker-framework-branch-master into side-effects-only
mernst Apr 1, 2024
275e806
Merge branch 'typetools:master' into side-effects-only
jyoo980 Apr 4, 2024
8c56e18
Issue warning on `@SideEffectsOnly({})`
jyoo980 Apr 5, 2024
cf0f995
Merge branch 'master' into side-effects-only
jyoo980 Apr 5, 2024
31d1b43
Update manual guidance for `@SideEffectsOnly({})`
jyoo980 Apr 5, 2024
3a69228
Merge branch 'side-effects-only' of github.com:mernst/checker-framewo…
jyoo980 Apr 5, 2024
a9fccf6
Add test case for empty `@SideEffectsOnly`
jyoo980 Apr 8, 2024
647c9b9
Merge branch 'master' into side-effects-only
jyoo980 Apr 8, 2024
98c1b5d
Update tests
jyoo980 Apr 8, 2024
ed83772
Merge branch 'master' into side-effects-only
jyoo980 Apr 10, 2024
3afb830
Apply lint
jyoo980 Apr 11, 2024
2f7355b
Refactor `SideEffectsOnlyChecker#visitMethodInvocation` and pass more…
jyoo980 Apr 12, 2024
b2fb30b
Fix failing test
jyoo980 Apr 12, 2024
9f9e005
Make behaviour consistent with manual when `@SideEffectsOnly` appears…
jyoo980 Apr 12, 2024
3bea5d8
Add Javadoc
jyoo980 Apr 12, 2024
7b1f256
Add naive implementation for aliasing
jyoo980 Apr 13, 2024
b7c8187
Improve error message
jyoo980 Apr 13, 2024
73c45c0
Correct Javadoc
jyoo980 Apr 13, 2024
01ff6f8
"Unrefine" fields in abstract store depending on declared side-effects
jyoo980 Apr 14, 2024
4fe3a6e
Update Javadoc
jyoo980 Apr 14, 2024
61909fd
Remove unnecessary print line
jyoo980 Apr 15, 2024
a4f3ec1
Correct logic for field CFStore updates
jyoo980 Apr 15, 2024
7ec0b2f
Update naming
jyoo980 Apr 15, 2024
da51f1b
Merge branch 'master' into side-effects-only
jyoo980 Apr 15, 2024
9f81866
Merge branch 'master' into side-effects-only
jyoo980 Apr 15, 2024
d4230fe
Merge branch 'master' into side-effects-only
jyoo980 Apr 15, 2024
64a6561
Make refinement happen for local variables (THIS BREAKS TESTS)
jyoo980 Apr 16, 2024
d5347bd
Merge branch 'master' into side-effects-only
jyoo980 Apr 17, 2024
e02124d
Merge branch 'master' into side-effects-only
jyoo980 Apr 20, 2024
d55c9d8
Update logic for removing information from method calls
jyoo980 Apr 23, 2024
906c8b5
Merge branch 'master' into side-effects-only
jyoo980 Apr 23, 2024
dcede5d
Merge branch 'master' into side-effects-only
jyoo980 Apr 23, 2024
820002a
Add test case
jyoo980 Apr 23, 2024
090ec33
Avoid CF crash when invalid expression is passed to `@SideEffectsOnly`
jyoo980 Apr 23, 2024
4f46ad4
Merge branch 'master' into side-effects-only
jyoo980 Apr 23, 2024
5911bfd
Possible fix to forEach lambda
jyoo980 Apr 24, 2024
44c5020
Merge branch 'side-effects-only' of github.com:mernst/checker-framewo…
jyoo980 Apr 24, 2024
7e9e02c
Clean up test case
jyoo980 Apr 24, 2024
a39c61d
Merge branch 'master' into side-effects-only
jyoo980 Apr 26, 2024
17c0ce6
Removing `sideEffectsUnrefineAliases` flag
jyoo980 Apr 27, 2024
e7cf745
Merge branch 'master' into side-effects-only
jyoo980 Apr 29, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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")} changes the
* value of at most the expressions A and B. All other expressions have the same value before and
* after a call to the method.
*
* @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 might change the value of.
*
* @return the Java expressions that the annotated method might side-effect
* @checker_framework.manual #java-expressions-as-arguments Syntax of Java expressions
*/
@JavaExpression
public String[] value();
}
26 changes: 24 additions & 2 deletions docs/manual/advanced-features.tex
Original file line number Diff line number Diff line change
Expand Up @@ -1019,7 +1019,8 @@
the refined type, because the method might assign a field.
The \refqualclass{dataflow/qual}{SideEffectFree} annotation indicates that
the method has no side effects, so calling it does not invalidate any
dataflow facts.
dataflow facts. Alternately, the \refqualclass{dataflow/qual}{SideEffectsOnly}
annotation specifies all the expressions that the method might side-effect.

Calling a method twice might have different results, so facts known about
one call cannot be relied upon at another call.
Expand Down Expand Up @@ -1059,7 +1060,7 @@
}
\end{Verbatim}

There are three ways to express that \<computeValue> does not set
There are four ways to express that \<computeValue> does not set
\<myField> to \<null>, and thus to prevent the Nullness Checker from
issuing a warning about the call \<myField.toString()>.

Expand All @@ -1073,6 +1074,15 @@
int computeValue() { ... }
\end{Verbatim}

\item
If \<computeValue> has side effects, but they do not affect \<myField>,
declare the method as \refqualclass{dataflow/qual}{SideEffectsOnly}:

\begin{Verbatim}
@SideEffectsOnly({"someOtherVariable1", "someOtherVariable1"})
int computeValue() { ... }
\end{Verbatim}

\noindent
The Nullness Checker issues no warnings, because it can reason that
the second occurrence of \code{myField} has the same (non-null) value as
Expand Down Expand Up @@ -1126,6 +1136,18 @@
\end{itemize}


\subsubsectionAndLabel{Relationship between \<@SideEffectFree> and \<@SideEffectsOnly>}{side-effects-relationship}

A method cannot be annotated with both \refqualclass{dataflow/qual}{SideEffectFree}
and \refqualclass{dataflow/qual}{SideEffectsOnly}.
The annotation \<@SideEffectsOnly(\{\})> (i.e., a \<@SideEffectsOnly> annotation
with an empty list of expressions) is equivalent to \<@SideEffectFree>.
Programmers should favor writing \<@SideEffectFree> in this case for clarity.
If a method has no \refqualclass{dataflow/qual}{SideEffectsOnly}
or \refqualclass{dataflow/qual}{SideEffectFree} annotation, then the Checker Framework
assumes that the method may side-effect any expressions (including fields and arguments).


\subsubsectionAndLabel{Deterministic methods}{type-refinement-determinism}

Consider the following declaration and uses:
Expand Down
8 changes: 6 additions & 2 deletions docs/manual/called-methods-checker.tex
Original file line number Diff line number Diff line change
Expand Up @@ -218,15 +218,19 @@
\end{Verbatim}

If \<m()> might have side-effects (i.e., it is not annotated as
\refqualclass{dataflow/qual}{SideEffectFree} or \refqualclass{dataflow/qual}{Pure}),
\refqualclass{dataflow/qual}{SideEffectFree},
\refqualclass{dataflow/qual}{SideEffectsOnly}, or
\refqualclass{dataflow/qual}{Pure}),
then the Called Methods Checker issues an error because
it cannot make any assumptions about the call to \<m()>, and therefore assumes
the worst: that all information it knows about in-scope variables (including
that \<close()> was called on \<sock>) is stale and must be discarded.
There are two possible fixes:

\begin{itemize}
\item add a \<@SideEffectFree> or \<@Pure> annotation to \<m()>, if \<m()> is
\item add a \refqualclass{dataflow/qual}{SideEffectFree},
\refqualclass{dataflow/qual}{SideEffectsOnly}, or
\refqualclass{dataflow/qual}{Pure} annotation to \<m()>, if \<m()> is
in fact side-effect free or pure; or
\item re-order the calls to \<sock.close()> and \<m()> so that the call
to \<sock.close()> appears last in \<closeSocket()>.
Expand Down
5 changes: 5 additions & 0 deletions docs/manual/introduction.tex
Original file line number Diff line number Diff line change
Expand Up @@ -608,6 +608,7 @@
\item \<-AcheckPurityAnnotations>
Check the bodies of methods marked
\refqualclass{dataflow/qual}{SideEffectFree},
\refqualclass{dataflow/qual}{SideEffectsOnly},
\refqualclass{dataflow/qual}{Deterministic},
and \refqualclass{dataflow/qual}{Pure}
to ensure the method satisfies the annotation. By default,
Expand Down Expand Up @@ -1554,6 +1555,10 @@
If your proof includes ``method \<m> has no side effects'',
then annotate \<m>'s declaration with
\refqualclass{dataflow/qual}{SideEffectFree}.
\item
If your proof includes ``method \<m> has limited side effects'',
then annotate \<m>'s declaration with
\refqualclass{dataflow/qual}{SideEffectsOnly}.
\item
If your proof includes ``each call to method \<m> returns the same value'',
then annotate \<m>'s declaration with
Expand Down
3 changes: 2 additions & 1 deletion docs/manual/nullness-checker.tex
Original file line number Diff line number Diff line change
Expand Up @@ -239,7 +239,8 @@
arbitrary external method calls that have access to the given field.
By contrast, for a \<@Nullable> field, the Nullness Checker assumes that
most method calls might set it to \<null>. (Exceptions are calls to
methods that are \refqualclass{dataflow/qual}{SideEffectFree} or that
methods that are \refqualclass{dataflow/qual}{SideEffectFree} or
\refqualclass{dataflow/qual}{SideEffectsOnly}, or that
have an \refqualclass{checker/nullness/qual}{EnsuresNonNull} or
\refqualclass{checker/nullness/qual}{EnsuresNonNullIf} annotation.)
\end{sloppypar}
Expand Down
6 changes: 5 additions & 1 deletion docs/manual/purity-checker.tex
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@
\item[\refqualclass{dataflow/qual}{SideEffectFree}]
indicates that the method has no externally-visible side effects.

\item[\refqualclass{dataflow/qual}{SideEffectsOnly}]
indicates that the method has limited externally-visible side effects.

\item[\refqualclass{dataflow/qual}{Deterministic}]
indicates that if the method is called multiple times with identical
arguments, then it returns the identical result according to \<==>
Expand All @@ -42,7 +45,8 @@

By default, purity annotations are trusted. Purity annotations on called
methods affect type-checking of client code. However, you can make a
mistake by writing \<@SideEffectFree> on the declaration of a method that
mistake by writing \<@SideEffectFree> or \<@SideEffectsOnly> on the
declaration of a method that
is not actually side-effect-free or by writing \<@Deterministic> on the
declaration of a method that is not actually deterministic.

Expand Down
1 change: 1 addition & 0 deletions docs/manual/troubleshooting.tex
Original file line number Diff line number Diff line change
Expand Up @@ -367,6 +367,7 @@
\<isCompleted()> does not set the field \<currentOutgoing> to \<null>, you can use
\<\refqualclass{dataflow/qual}{Pure}>,
\<\refqualclass{dataflow/qual}{SideEffectFree}>,
\<\refqualclass{dataflow/qual}{SideEffectsOnly}>,
or \<\refqualclass{checker/nullness/qual}{EnsuresNonNull}> on the
declaration of \<isCompleted()>; see Sections~\ref{type-refinement-purity}
and~\ref{nullness-method-annotations}.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@
import org.checkerframework.dataflow.qual.Impure;
import org.checkerframework.dataflow.qual.Pure;
import org.checkerframework.dataflow.qual.SideEffectFree;
import org.checkerframework.dataflow.qual.SideEffectsOnly;
import org.checkerframework.dataflow.util.PurityChecker;
import org.checkerframework.dataflow.util.PurityChecker.PurityResult;
import org.checkerframework.dataflow.util.PurityUtils;
Expand Down Expand Up @@ -128,6 +129,7 @@
import org.checkerframework.framework.util.Contract.Precondition;
import org.checkerframework.framework.util.ContractsFromMethod;
import org.checkerframework.framework.util.FieldInvariants;
import org.checkerframework.framework.util.JavaExpressionParseUtil;
import org.checkerframework.framework.util.JavaExpressionParseUtil.JavaExpressionParseException;
import org.checkerframework.framework.util.JavaParserUtil;
import org.checkerframework.framework.util.StringToJavaExpression;
Expand Down Expand Up @@ -240,6 +242,9 @@ public class BaseTypeVisitor<Factory extends GenericAnnotatedTypeFactory<?, ?, ?
/** The {@code when} element/field of the @Unused annotation. */
protected final ExecutableElement unusedWhenElement;

/** The {@code value} element/field of the @{@link SideEffectsOnly} annotation. */
ExecutableElement sideEffectsOnlyValueElement;

/** True if "-Ashowchecks" was passed on the command line. */
protected final boolean showchecks;

Expand Down Expand Up @@ -304,6 +309,8 @@ protected BaseTypeVisitor(BaseTypeChecker checker, @Nullable Factory typeFactory
atypeFactory.fromElement(elements.getTypeElement(Vector.class.getCanonicalName()));
targetValueElement = TreeUtils.getMethod(Target.class, "value", 0, env);
unusedWhenElement = TreeUtils.getMethod(Unused.class, "when", 0, env);
sideEffectsOnlyValueElement =
TreeUtils.getMethod(SideEffectsOnly.class, "value", 0, checker.getProcessingEnvironment());
showchecks = checker.hasOption("showchecks");
infer = checker.hasOption("infer");
suggestPureMethods = checker.hasOption("suggestPureMethods") || infer;
Expand Down Expand Up @@ -1075,24 +1082,29 @@ protected boolean shouldPerformContractInference() {
* AnnotatedTypeMirror.AnnotatedDeclaredType, AnnotatedTypeMirror.AnnotatedExecutableType,
* AnnotatedTypeMirror.AnnotatedDeclaredType)}.
*
* <p>If the method {@code tree} is annotated with {@link SideEffectsOnly}, check that the method
* side-effects a subset of the expressions specified as annotation arguments/elements to {@link
* SideEffectsOnly}.
*
* @param tree the method tree to check
*/
@SuppressWarnings("AlreadyChecked") // TEMPORARY, must fix
protected void checkPurityAnnotations(MethodTree tree) {
if (!checkPurityAnnotations) {
return;
}

if (!suggestPureMethods && !PurityUtils.hasPurityAnnotation(atypeFactory, tree)) {
// There is nothing to check.
return;
}
// if (!suggestPureMethods && !PurityUtils.hasPurityAnnotation(atypeFactory, tree) &&
// !checkPurityAnnotations) {
// // There is nothing to check.
// return;
// }

// `body` is lazily assigned.
TreePath body = null;
boolean bodyAssigned = false;

if (suggestPureMethods || PurityUtils.hasPurityAnnotation(atypeFactory, tree)) {

// check "no" purity
EnumSet<Pure.Kind> kinds = PurityUtils.getPurityKinds(atypeFactory, tree);
// @Deterministic makes no sense for a void method or constructor
Expand Down Expand Up @@ -1159,6 +1171,77 @@ protected void checkPurityAnnotations(MethodTree tree) {
}
}

if (checkPurityAnnotations) {
if (bodyAssigned == false) {
body = atypeFactory.getPath(tree.getBody());
bodyAssigned = true;
}
if (body == null) {
return;
}
@Nullable Element methodDeclElem = TreeUtils.elementFromDeclaration(tree);
AnnotationMirror sefOnlyAnnotation =
atypeFactory.getDeclAnnotation(methodDeclElem, SideEffectsOnly.class);
if (sefOnlyAnnotation == null) {
return;
}
AnnotationMirror pureOrSideEffectFreeAnnotation =
getPureOrSideEffectFreeAnnotation(methodDeclElem);
if (pureOrSideEffectFreeAnnotation != null) {
// It is an error if a @SideEffectsOnly annotation appears with a @Pure or @SideEffectFree
// annotation
checker.reportError(
tree,
"purity.incorrect.annotation.conflict",
tree.getName(),
pureOrSideEffectFreeAnnotation);
return;
}
List<String> sideEffectsOnlyExpressionStrings =
AnnotationUtils.getElementValueArray(
sefOnlyAnnotation, sideEffectsOnlyValueElement, String.class);
List<JavaExpression> sideEffectsOnlyExpressions =
new ArrayList<>(sideEffectsOnlyExpressionStrings.size());
for (String st : sideEffectsOnlyExpressionStrings) {
try {
JavaExpression exprJe = StringToJavaExpression.atMethodBody(st, tree, checker);
sideEffectsOnlyExpressions.add(exprJe);
} catch (JavaExpressionParseUtil.JavaExpressionParseException ex) {
checker.report(st, ex.getDiagMessage());
return;
}
}

if (sideEffectsOnlyExpressions.isEmpty()) {
// A @SideEffectsOnly annotation with an empty expression array is equivalent to
// a @SideEffectFree annotation.
checker.reportWarning(tree, "purity.more.sideeffectfree", tree.getName());
return;
}

SideEffectsOnlyChecker.ExtraSideEffects sefOnlyResult =
SideEffectsOnlyChecker.checkSideEffectsOnly(
body,
atypeFactory,
sideEffectsOnlyExpressions,
atypeFactory.getProcessingEnv(),
checker);

// System.out.printf("sefOnlyResult = %s%n", sefOnlyResult);

List<IPair<Tree, JavaExpression>> seOnlyIncorrectExprs = sefOnlyResult.getExprs();
// System.out.printf("seOnlyIncorrectExprs = %s%n", seOnlyIncorrectExprs);

if (!seOnlyIncorrectExprs.isEmpty()) {
for (IPair<Tree, JavaExpression> s : seOnlyIncorrectExprs) {
if (!sideEffectsOnlyExpressions.contains(s.second)) {
// System.out.printf("Error 2%n");
checker.reportError(s.first, "purity.incorrect.sideeffectsonly", s.second.toString());
}
}
}
}

// There will be code here that *may* use `body` (and may set `body` before using it).
// The below is just a placeholder so `bodyAssigned` is not a dead variable.
// ...
Expand All @@ -1169,6 +1252,22 @@ protected void checkPurityAnnotations(MethodTree tree) {
// ...
}

/**
* Return either the {@link Pure} or {@link SideEffectFree} annotation (in that order) if either
* appears on a method declaration.
*
* @param methodDeclaration the method declaration
* @return either the {@link Pure} or {@link SideEffectFree} annotation (in that order) if either
* appears on a method declaration
*/
private @Nullable AnnotationMirror getPureOrSideEffectFreeAnnotation(Element methodDeclaration) {
AnnotationMirror pureAnnotation = atypeFactory.getDeclAnnotation(methodDeclaration, Pure.class);
if (pureAnnotation != null) {
return pureAnnotation;
}
return atypeFactory.getDeclAnnotation(methodDeclaration, SideEffectFree.class);
}

/**
* Infer a purity annotation for {@code elt} by converting {@code kinds} into a method annotation.
*
Expand Down