Skip to content

Add side effect annotations#278

Merged
mernst merged 4 commits into
typetools:masterfrom
mernst:side-effects
May 26, 2026
Merged

Add side effect annotations#278
mernst merged 4 commits into
typetools:masterfrom
mernst:side-effects

Conversation

@mernst
Copy link
Copy Markdown
Member

@mernst mernst commented May 26, 2026

No description provided.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 26, 2026

Review Change Stack

Warning

Review limit reached

@mernst, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 37 minutes and 15 seconds. Learn how PR review limits work.

Your organization has run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: ff750649-e62d-4d2f-b50f-287d696805de

📥 Commits

Reviewing files that changed from the base of the PR and between 7b82ab1 and 2a483d4.

📒 Files selected for processing (3)
  • src/java.base/share/classes/java/lang/StrictMath.java
  • src/java.base/share/classes/java/util/ReverseOrderDequeView.java
  • src/java.base/share/classes/java/util/ReverseOrderSortedSetView.java
📝 Walkthrough

Walkthrough

This pull request adds Checker Framework semantic annotations to the Java base library to document operation purity, side-effects, and collection state contracts. The changes span three independent annotation strategies: adding @StaticallyExecutable and @Pure to Math and StrictMath floating-point operations; marking BigInteger arithmetic as @SideEffectFree and queries as @Pure; and extensively annotating collection classes with @EnsuresNonEmpty("this") postconditions on insertion methods. Additionally, NavigableSet query methods are marked @Pure, and sorting algorithm implementations receive // @SideEffectsOnly`` comment directives. No functional behavior or API signatures are modified—all changes are metadata annotations for static analysis.

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed Docstring coverage is 89.18% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (3)
src/java.base/share/classes/java/util/TreeSet.java (1)

529-534: ⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

Remove @EnsuresNonEmpty("this") from methods that always throw exceptions.

Both addFirst and addLast unconditionally throw UnsupportedOperationException and never return normally. The @EnsuresNonEmpty("this") postcondition annotation is incorrect because:

  • The postcondition can never be established since the methods never complete normally
  • Static analysis tools may incorrectly assume the set is non-empty after these methods are called in try-catch blocks
  • The annotation misrepresents the actual behavior of these methods

Postcondition annotations should only be applied to methods that can return normally and actually establish the stated postcondition.

🔧 Proposed fix
-    `@EnsuresNonEmpty`("this")
     // `@SideEffectsOnly`("this")
     `@DoesNotUnrefineReceiver`("modifiability")
     public void addFirst(E e) {
         throw new UnsupportedOperationException();
     }
-    `@EnsuresNonEmpty`("this")
     // `@SideEffectsOnly`("this")
     `@DoesNotUnrefineReceiver`("modifiability")
     public void addLast(E e) {
         throw new UnsupportedOperationException();
     }

Also applies to: 544-549

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/java.base/share/classes/java/util/TreeSet.java` around lines 529 - 534,
The `@EnsuresNonEmpty`("this") postcondition is incorrect on methods that always
throw and never return; remove the `@EnsuresNonEmpty`("this") annotation from the
addFirst(E e) and addLast(E e) methods in class TreeSet so their declarations
only reflect that they throw UnsupportedOperationException (leave other
annotations like `@DoesNotUnrefineReceiver` if applicable) to avoid misleading
static analyzers and incorrect postcondition assumptions.
src/java.base/share/classes/java/util/SortedSet.java (1)

297-302: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Misleading @EnsuresNonEmpty("this") on a method that always throws.

The addFirst default implementation unconditionally throws UnsupportedOperationException and never returns normally. While @EnsuresNonEmpty("this") is vacuously true (postcondition doesn't need to hold if method doesn't return), it misleadingly suggests the method might succeed and ensure non-emptiness. Consider removing this annotation from methods that always throw.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/java.base/share/classes/java/util/SortedSet.java` around lines 297 - 302,
Remove the misleading `@EnsuresNonEmpty`("this") annotation from the default
addFirst(E e) method in SortedSet (the method currently throws
UnsupportedOperationException and never returns); locate the addFirst method
declaration and delete the `@EnsuresNonEmpty`("this") annotation (and any similar
postcondition annotations on other default methods that unconditionally throw)
so annotations only appear on methods that can return normally and establish the
postcondition.
src/java.base/share/classes/java/util/ReverseOrderSortedSetView.java (1)

133-136: ⚠️ Potential issue | 🟠 Major | ⚖️ Poor tradeoff

Pre-existing bug: add() should return the result from base.add(e).

Line 135 unconditionally returns true, violating the Set.add() contract, which requires returning false if the element was already present. This causes callers to incorrectly believe the element was always added.

Note: This bug exists in the base code and is not introduced by this PR's annotation changes.

🐛 Proposed fix
 public boolean add(E e) {
-    base.add(e);
-    return true;
+    return base.add(e);
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/java.base/share/classes/java/util/ReverseOrderSortedSetView.java` around
lines 133 - 136, The add method in class ReverseOrderSortedSetView
unconditionally returns true which violates Set.add() semantics; change the
implementation of public boolean add(E e) in ReverseOrderSortedSetView to return
the boolean result produced by base.add(e) (i.e., return base.add(e)) so callers
correctly receive false when the element was already present.
♻️ Duplicate comments (1)
src/java.base/share/classes/java/util/SortedSet.java (1)

315-320: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Misleading @EnsuresNonEmpty("this") on a method that always throws.

Same issue as addFirst: the default implementation always throws, making the @EnsuresNonEmpty("this") annotation vacuously true but misleading.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/java.base/share/classes/java/util/SortedSet.java` around lines 315 - 320,
The `@EnsuresNonEmpty`("this") on the default SortedSet.addLast(E) is misleading
because the default always throws; remove the `@EnsuresNonEmpty`("this")
annotation from the default addLast method in class SortedSet (and mirror the
same change made for addFirst if present) so the contract does not falsely claim
the receiver becomes non-empty for a method that never succeeds.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/java.base/share/classes/java/util/ComparableTimSort.java`:
- Around line 47-48: There are two consecutive declarations of the same
top-level class (the annotated "`@UsesObjectEquals` class ComparableTimSort"
followed immediately by "class ComparableTimSort"), which breaks compilation;
remove the duplicate unannotated declaration so only the single annotated class
ComparableTimSort remains (keep the "`@UsesObjectEquals` class ComparableTimSort"
form and delete the extra "class ComparableTimSort" token), ensuring the class
body and members remain unchanged.

In `@src/java.base/share/classes/java/util/concurrent/ConcurrentSkipListSet.java`:
- Around line 459-464: Remove the misleading postcondition by deleting the
`@EnsuresNonEmpty`("this") annotation from the ConcurrentSkipListSet.addFirst(E e)
declaration; leave the method body that unconditionally throws
UnsupportedOperationException and retain the other annotations (e.g.,
`@DoesNotUnrefineReceiver`("modifiability")) so the signature still indicates it
never returns normally without implying the set becomes non-empty.
- Around line 474-479: Remove the misleading postcondition by deleting the
`@EnsuresNonEmpty`("this") annotation from the addLast(E e) method; since addLast
always throws UnsupportedOperationException and never returns normally, drop the
annotation (leave `@DoesNotUnrefineReceiver` and any other valid annotations
intact) so the method’s signature no longer implies it guarantees a non-empty
set on completion.

In `@src/java.base/share/classes/java/util/DualPivotQuicksort.java`:
- Around line 185-186: The `@SideEffectsOnly` annotation on the int-array overload
incorrectly references the sorter parameter (`#1`); change it to target the
mutated array parameter (`#2`) so the signature static void sort(Sorter sorter,
int[] a, int bits, `@IndexOrHigh`({"`#1`"}) int low, `@IndexOrHigh`({"`#1`"}) int high)
uses // `@SideEffectsOnly`("`#2`") to match the long/float/double overloads and
correctly document that the array 'a' is modified.

In `@src/java.base/share/classes/java/util/ListIterator.java`:
- Around line 222-225: The `@EnsuresNonEmpty`("this") annotation on
ListIterator.add is incorrect because it claims the iterator becomes non-empty
in Checker Framework terms (which affects next()/hasNext() reasoning) even when
add(E) at the end does not make hasNext()==true; remove the
`@EnsuresNonEmpty`("this") annotation from the add(`@GuardSatisfied` ListIterator<E>
this, E e) declaration (or replace it with a safer, documented postcondition
that does not assert non-empty cursor state) and update any comment to explain
that add does not guarantee hasNext() becomes true after insertion.

---

Outside diff comments:
In `@src/java.base/share/classes/java/util/ReverseOrderSortedSetView.java`:
- Around line 133-136: The add method in class ReverseOrderSortedSetView
unconditionally returns true which violates Set.add() semantics; change the
implementation of public boolean add(E e) in ReverseOrderSortedSetView to return
the boolean result produced by base.add(e) (i.e., return base.add(e)) so callers
correctly receive false when the element was already present.

In `@src/java.base/share/classes/java/util/SortedSet.java`:
- Around line 297-302: Remove the misleading `@EnsuresNonEmpty`("this") annotation
from the default addFirst(E e) method in SortedSet (the method currently throws
UnsupportedOperationException and never returns); locate the addFirst method
declaration and delete the `@EnsuresNonEmpty`("this") annotation (and any similar
postcondition annotations on other default methods that unconditionally throw)
so annotations only appear on methods that can return normally and establish the
postcondition.

In `@src/java.base/share/classes/java/util/TreeSet.java`:
- Around line 529-534: The `@EnsuresNonEmpty`("this") postcondition is incorrect
on methods that always throw and never return; remove the
`@EnsuresNonEmpty`("this") annotation from the addFirst(E e) and addLast(E e)
methods in class TreeSet so their declarations only reflect that they throw
UnsupportedOperationException (leave other annotations like
`@DoesNotUnrefineReceiver` if applicable) to avoid misleading static analyzers and
incorrect postcondition assumptions.

---

Duplicate comments:
In `@src/java.base/share/classes/java/util/SortedSet.java`:
- Around line 315-320: The `@EnsuresNonEmpty`("this") on the default
SortedSet.addLast(E) is misleading because the default always throws; remove the
`@EnsuresNonEmpty`("this") annotation from the default addLast method in class
SortedSet (and mirror the same change made for addFirst if present) so the
contract does not falsely claim the receiver becomes non-empty for a method that
never succeeds.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 5fd8b48c-6cae-4daa-afb6-cd14df63f33b

📥 Commits

Reviewing files that changed from the base of the PR and between 300678c and 47ca0c6.

📒 Files selected for processing (32)
  • src/java.base/share/classes/java/lang/Math.java
  • src/java.base/share/classes/java/lang/StrictMath.java
  • src/java.base/share/classes/java/math/BigInteger.java
  • src/java.base/share/classes/java/util/AbstractCollection.java
  • src/java.base/share/classes/java/util/AbstractList.java
  • src/java.base/share/classes/java/util/AbstractSequentialList.java
  • src/java.base/share/classes/java/util/ArrayDeque.java
  • src/java.base/share/classes/java/util/ArrayList.java
  • src/java.base/share/classes/java/util/Arrays.java
  • src/java.base/share/classes/java/util/BitSet.java
  • src/java.base/share/classes/java/util/Collections.java
  • src/java.base/share/classes/java/util/ComparableTimSort.java
  • src/java.base/share/classes/java/util/DualPivotQuicksort.java
  • src/java.base/share/classes/java/util/HashSet.java
  • src/java.base/share/classes/java/util/LinkedHashSet.java
  • src/java.base/share/classes/java/util/LinkedList.java
  • src/java.base/share/classes/java/util/List.java
  • src/java.base/share/classes/java/util/ListIterator.java
  • src/java.base/share/classes/java/util/NavigableSet.java
  • src/java.base/share/classes/java/util/ReverseOrderDequeView.java
  • src/java.base/share/classes/java/util/ReverseOrderListView.java
  • src/java.base/share/classes/java/util/ReverseOrderSortedSetView.java
  • src/java.base/share/classes/java/util/SequencedCollection.java
  • src/java.base/share/classes/java/util/Set.java
  • src/java.base/share/classes/java/util/SortedSet.java
  • src/java.base/share/classes/java/util/TreeSet.java
  • src/java.base/share/classes/java/util/Vector.java
  • src/java.base/share/classes/java/util/concurrent/BlockingDeque.java
  • src/java.base/share/classes/java/util/concurrent/ConcurrentLinkedDeque.java
  • src/java.base/share/classes/java/util/concurrent/ConcurrentSkipListSet.java
  • src/java.base/share/classes/java/util/concurrent/CopyOnWriteArrayList.java
  • src/java.base/share/classes/java/util/concurrent/LinkedBlockingDeque.java
💤 Files with no reviewable changes (1)
  • src/java.base/share/classes/java/util/HashSet.java

Comment thread src/java.base/share/classes/java/util/ComparableTimSort.java Outdated
Comment thread src/java.base/share/classes/java/util/DualPivotQuicksort.java Outdated
Comment thread src/java.base/share/classes/java/util/ListIterator.java Outdated
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/java.base/share/classes/java/util/ListIterator.java (1)

31-31: 🧹 Nitpick | 🔵 Trivial | 💤 Low value

Consider removing unused import.

The EnsuresNonEmpty import appears unused (the add method uses @CFComment to explain why it's not used, and only EnsuresNonEmptyIf from this package is actually applied).

♻️ Optional cleanup
-import org.checkerframework.checker.nonempty.qual.EnsuresNonEmpty;
 import org.checkerframework.checker.nonempty.qual.EnsuresNonEmptyIf;
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/java.base/share/classes/java/util/ListIterator.java` at line 31, The
import org.checkerframework.checker.nonempty.qual.EnsuresNonEmpty is unused;
remove that import line and keep the actual applied annotation EnsuresNonEmptyIf
and the CFComment on the add method as-is so behavior and documentation stay
unchanged—locate the unused import statement and delete it, leaving other
annotations and the add method untouched.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/java.base/share/classes/java/util/ListIterator.java`:
- Line 38: Remove the commented-out import for SideEffectsOnly (the line "//
import org.checkerframework.dataflow.qual.SideEffectsOnly;") from
ListIterator.java; if the SideEffectsOnly annotation and its commented usages
elsewhere in the file are not needed, delete this commented import (and
optionally any remaining commented annotation usages) to clean up the file and
avoid confusion.

---

Outside diff comments:
In `@src/java.base/share/classes/java/util/ListIterator.java`:
- Line 31: The import org.checkerframework.checker.nonempty.qual.EnsuresNonEmpty
is unused; remove that import line and keep the actual applied annotation
EnsuresNonEmptyIf and the CFComment on the add method as-is so behavior and
documentation stay unchanged—locate the unused import statement and delete it,
leaving other annotations and the add method untouched.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 311d735a-635a-4053-930f-87e6e2cf2c02

📥 Commits

Reviewing files that changed from the base of the PR and between 47ca0c6 and 7b82ab1.

📒 Files selected for processing (4)
  • src/java.base/share/classes/java/util/ComparableTimSort.java
  • src/java.base/share/classes/java/util/DualPivotQuicksort.java
  • src/java.base/share/classes/java/util/ListIterator.java
  • src/java.base/share/classes/java/util/TreeMap.java

Comment thread src/java.base/share/classes/java/util/ListIterator.java
@mernst mernst merged commit f8737ba into typetools:master May 26, 2026
49 checks passed
@mernst mernst deleted the side-effects branch May 26, 2026 20:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant