From f47614d0da610e01a518ab702c8023c981487365 Mon Sep 17 00:00:00 2001 From: Suzanne Millstein Date: Fri, 18 Sep 2026 10:28:14 -0700 Subject: [PATCH 1/4] Add purity annotations to Comparator. --- .../share/classes/java/util/Comparator.java | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/java.base/share/classes/java/util/Comparator.java b/src/java.base/share/classes/java/util/Comparator.java index 53c07a37638a..115b025326f7 100644 --- a/src/java.base/share/classes/java/util/Comparator.java +++ b/src/java.base/share/classes/java/util/Comparator.java @@ -161,6 +161,7 @@ public interface Comparator { * @throws ClassCastException if the arguments' types prevent them from * being compared by this comparator. */ + @Pure int compare(T o1, T o2); /** @@ -227,6 +228,7 @@ default Comparator reversed() { * @throws NullPointerException if the argument is null. * @since 1.8 */ + @SideEffectFree default Comparator thenComparing(Comparator other) { Objects.requireNonNull(other); return (Comparator & Serializable) (c1, c2) -> { @@ -252,6 +254,7 @@ default Comparator thenComparing(Comparator other) { * @see #thenComparing(Comparator) * @since 1.8 */ + @SideEffectFree default Comparator thenComparing( Function keyExtractor, Comparator keyComparator) @@ -276,6 +279,7 @@ default Comparator thenComparing( * @see #thenComparing(Comparator) * @since 1.8 */ + @SideEffectFree default > Comparator thenComparing( Function keyExtractor) { @@ -297,6 +301,7 @@ default > Comparator thenComparing( * @see #thenComparing(Comparator) * @since 1.8 */ + @SideEffectFree default Comparator thenComparingInt(ToIntFunction keyExtractor) { return thenComparing(comparingInt(keyExtractor)); } @@ -316,6 +321,7 @@ default Comparator thenComparingInt(ToIntFunction keyExtractor) { * @see #thenComparing(Comparator) * @since 1.8 */ + @SideEffectFree default Comparator thenComparingLong(ToLongFunction keyExtractor) { return thenComparing(comparingLong(keyExtractor)); } @@ -335,6 +341,7 @@ default Comparator thenComparingLong(ToLongFunction keyExtractor) * @see #thenComparing(Comparator) * @since 1.8 */ + @SideEffectFree default Comparator thenComparingDouble(ToDoubleFunction keyExtractor) { return thenComparing(comparingDouble(keyExtractor)); } @@ -352,6 +359,7 @@ default Comparator thenComparingDouble(ToDoubleFunction keyExtract * @see Comparable * @since 1.8 */ + @Pure public static > Comparator reverseOrder() { return Collections.reverseOrder(); } @@ -369,6 +377,7 @@ public static > Comparator reverseOrder() { * @see Comparable * @since 1.8 */ + @Pure @SuppressWarnings("unchecked") public static > Comparator naturalOrder() { return (Comparator) Comparators.NaturalOrderComparator.INSTANCE; @@ -391,6 +400,7 @@ public static > Comparator reverseOrder() { * {@code Comparator}. * @since 1.8 */ + @SideEffectFree public static Comparator<@Nullable T> nullsFirst(@Nullable Comparator comparator) { return new Comparators.NullComparator<>(true, comparator); } @@ -412,6 +422,7 @@ public static > Comparator reverseOrder() { * {@code Comparator}. * @since 1.8 */ + @SideEffectFree public static Comparator<@Nullable T> nullsLast(@Nullable Comparator comparator) { return new Comparators.NullComparator<>(false, comparator); } @@ -443,6 +454,7 @@ public static > Comparator reverseOrder() { * @throws NullPointerException if either argument is null * @since 1.8 */ + @SideEffectFree public static Comparator comparing( Function keyExtractor, Comparator keyComparator) @@ -478,6 +490,7 @@ public static Comparator comparing( * @throws NullPointerException if the argument is null * @since 1.8 */ + @SideEffectFree public static > Comparator comparing( Function keyExtractor) { @@ -501,6 +514,7 @@ public static > Comparator comparing( * @throws NullPointerException if the argument is null * @since 1.8 */ + @SideEffectFree public static Comparator comparingInt(ToIntFunction keyExtractor) { Objects.requireNonNull(keyExtractor); return (Comparator & Serializable) @@ -522,6 +536,7 @@ public static Comparator comparingInt(ToIntFunction keyExtract * @throws NullPointerException if the argument is null * @since 1.8 */ + @SideEffectFree public static Comparator comparingLong(ToLongFunction keyExtractor) { Objects.requireNonNull(keyExtractor); return (Comparator & Serializable) @@ -543,6 +558,7 @@ public static Comparator comparingLong(ToLongFunction keyExtra * @throws NullPointerException if the argument is null * @since 1.8 */ + @SideEffectFree public static Comparator comparingDouble(ToDoubleFunction keyExtractor) { Objects.requireNonNull(keyExtractor); return (Comparator & Serializable) From 0f6c584953e9c2ea0f08fc617921d536f48cdb82 Mon Sep 17 00:00:00 2001 From: Suzanne Millstein Date: Mon, 21 Sep 2026 11:19:01 -0700 Subject: [PATCH 2/4] Add purity annotations needed by the Checker Framework's javacutil The Checker Framework's `checkPurity` task now verifies purity annotations rather than trusting them, because the Purity Checker implies -AcheckPurityAnnotations. That requires purity annotations on the javac and javax.lang.model methods that javacutil calls. Every method annotated here returns a stored field or reads a flag, so @Pure is sound. Methods that allocate on each call are deliberately left unannotated or left as @SideEffectFree, because the object they return is not ==-stable: Collection.iterator(), Map.keySet()/entrySet()/values(), and AnnotationValue.getValue(). ExecutableElement.getParameters() carries a @CFComment explaining why @Pure is justified even though javac's MethodSymbol builds the list lazily: the list is cached in a private field, so repeated calls return the identical List and no externally-visible state changes. Co-Authored-By: Claude Opus 5 --- .../javax/lang/model/element/AnnotationMirror.java | 3 +++ .../share/classes/javax/lang/model/element/Element.java | 2 ++ .../javax/lang/model/element/ExecutableElement.java | 8 ++++++++ .../classes/javax/lang/model/element/TypeElement.java | 2 ++ .../share/classes/javax/lang/model/type/DeclaredType.java | 2 ++ .../classes/javax/lang/model/type/ExecutableType.java | 2 ++ .../share/classes/javax/lang/model/type/TypeMirror.java | 1 + .../classes/com/sun/source/tree/ArrayAccessTree.java | 3 +++ .../share/classes/com/sun/source/tree/AssignmentTree.java | 3 +++ .../classes/com/sun/source/tree/MethodInvocationTree.java | 3 +++ .../classes/com/sun/source/tree/ParenthesizedTree.java | 3 +++ .../share/classes/com/sun/source/tree/TypeCastTree.java | 3 +++ .../share/classes/com/sun/tools/javac/tree/JCTree.java | 3 +++ .../share/classes/com/sun/tools/javac/tree/TreeInfo.java | 2 ++ 14 files changed, 40 insertions(+) diff --git a/src/java.compiler/share/classes/javax/lang/model/element/AnnotationMirror.java b/src/java.compiler/share/classes/javax/lang/model/element/AnnotationMirror.java index 908d171e9c7f..c23d591dc4d7 100644 --- a/src/java.compiler/share/classes/javax/lang/model/element/AnnotationMirror.java +++ b/src/java.compiler/share/classes/javax/lang/model/element/AnnotationMirror.java @@ -25,6 +25,8 @@ package javax.lang.model.element; +import org.checkerframework.dataflow.qual.Pure; + import java.util.Map; import javax.lang.model.type.DeclaredType; @@ -43,6 +45,7 @@ public interface AnnotationMirror { /** * {@return the type of this annotation} */ + @Pure DeclaredType getAnnotationType(); /** diff --git a/src/java.compiler/share/classes/javax/lang/model/element/Element.java b/src/java.compiler/share/classes/javax/lang/model/element/Element.java index 6e0cba8ba6c6..6bf3ca147857 100644 --- a/src/java.compiler/share/classes/javax/lang/model/element/Element.java +++ b/src/java.compiler/share/classes/javax/lang/model/element/Element.java @@ -25,6 +25,7 @@ package javax.lang.model.element; +import org.checkerframework.dataflow.qual.Pure; import java.lang.annotation.Annotation; import java.lang.annotation.AnnotationTypeMismatchException; @@ -68,6 +69,7 @@ public interface Element extends javax.lang.model.AnnotatedConstruct { * @see TypeParameterElement#asType * @see VariableElement#asType */ + @Pure TypeMirror asType(); /** diff --git a/src/java.compiler/share/classes/javax/lang/model/element/ExecutableElement.java b/src/java.compiler/share/classes/javax/lang/model/element/ExecutableElement.java index d4c9c881f3b6..16ee6e4a4d46 100644 --- a/src/java.compiler/share/classes/javax/lang/model/element/ExecutableElement.java +++ b/src/java.compiler/share/classes/javax/lang/model/element/ExecutableElement.java @@ -25,6 +25,9 @@ package javax.lang.model.element; +import org.checkerframework.dataflow.qual.Pure; +import org.checkerframework.framework.qual.CFComment; + import java.util.List; import javax.lang.model.type.*; @@ -73,6 +76,10 @@ public interface ExecutableElement extends Element, Parameterizable { * @return the formal parameters, * or an empty list if there are none */ + @CFComment( + "purity: javac's MethodSymbol caches the list in a private field, so repeated calls" + + " return the identical List and no externally-visible state changes") + @Pure List getParameters(); /** @@ -102,6 +109,7 @@ public interface ExecutableElement extends Element, Parameterizable { * {@return {@code true} if this method or constructor accepts a variable * number of arguments and returns {@code false} otherwise} */ + @Pure boolean isVarArgs(); /** diff --git a/src/java.compiler/share/classes/javax/lang/model/element/TypeElement.java b/src/java.compiler/share/classes/javax/lang/model/element/TypeElement.java index 6ecd43333fb7..47f9ba91628e 100644 --- a/src/java.compiler/share/classes/javax/lang/model/element/TypeElement.java +++ b/src/java.compiler/share/classes/javax/lang/model/element/TypeElement.java @@ -26,6 +26,7 @@ package javax.lang.model.element; import org.checkerframework.checker.signature.qual.CanonicalNameOrEmpty; +import org.checkerframework.dataflow.qual.Pure; import jdk.internal.javac.PreviewFeature; @@ -169,6 +170,7 @@ public interface TypeElement extends Element, Parameterizable, QualifiedNameable * @jls 6.7 Fully Qualified Names and Canonical Names * @jls 7.3 Compilation Units */ + @Pure @CanonicalNameOrEmpty Name getQualifiedName(); /** diff --git a/src/java.compiler/share/classes/javax/lang/model/type/DeclaredType.java b/src/java.compiler/share/classes/javax/lang/model/type/DeclaredType.java index ae799981a6af..054413d433bd 100644 --- a/src/java.compiler/share/classes/javax/lang/model/type/DeclaredType.java +++ b/src/java.compiler/share/classes/javax/lang/model/type/DeclaredType.java @@ -25,6 +25,7 @@ package javax.lang.model.type; +import org.checkerframework.dataflow.qual.Pure; import java.util.List; @@ -57,6 +58,7 @@ public interface DeclaredType extends ReferenceType { /** * {@return the element corresponding to this type} */ + @Pure Element asElement(); /** diff --git a/src/java.compiler/share/classes/javax/lang/model/type/ExecutableType.java b/src/java.compiler/share/classes/javax/lang/model/type/ExecutableType.java index 19953ff5e4a1..b47198cbfe41 100644 --- a/src/java.compiler/share/classes/javax/lang/model/type/ExecutableType.java +++ b/src/java.compiler/share/classes/javax/lang/model/type/ExecutableType.java @@ -25,6 +25,7 @@ package javax.lang.model.type; +import org.checkerframework.dataflow.qual.Pure; import java.util.List; @@ -69,6 +70,7 @@ public interface ExecutableType extends TypeMirror { * @return the types of this executable's formal parameters, * or an empty list if there are none */ + @Pure List getParameterTypes(); /** diff --git a/src/java.compiler/share/classes/javax/lang/model/type/TypeMirror.java b/src/java.compiler/share/classes/javax/lang/model/type/TypeMirror.java index 879e7e673f24..0cc74266bf7a 100644 --- a/src/java.compiler/share/classes/javax/lang/model/type/TypeMirror.java +++ b/src/java.compiler/share/classes/javax/lang/model/type/TypeMirror.java @@ -111,6 +111,7 @@ public interface TypeMirror extends javax.lang.model.AnnotatedConstruct { * * */ + @Pure TypeKind getKind(); /** diff --git a/src/jdk.compiler/share/classes/com/sun/source/tree/ArrayAccessTree.java b/src/jdk.compiler/share/classes/com/sun/source/tree/ArrayAccessTree.java index 8fa8c3194381..3ae6558d5e17 100644 --- a/src/jdk.compiler/share/classes/com/sun/source/tree/ArrayAccessTree.java +++ b/src/jdk.compiler/share/classes/com/sun/source/tree/ArrayAccessTree.java @@ -25,6 +25,8 @@ package com.sun.source.tree; +import org.checkerframework.dataflow.qual.Pure; + /** * A tree node for an array access expression. * @@ -44,6 +46,7 @@ public interface ArrayAccessTree extends ExpressionTree { * Returns the expression for the array being accessed. * @return the array */ + @Pure ExpressionTree getExpression(); /** diff --git a/src/jdk.compiler/share/classes/com/sun/source/tree/AssignmentTree.java b/src/jdk.compiler/share/classes/com/sun/source/tree/AssignmentTree.java index 4e5568de23c5..c197e4323193 100644 --- a/src/jdk.compiler/share/classes/com/sun/source/tree/AssignmentTree.java +++ b/src/jdk.compiler/share/classes/com/sun/source/tree/AssignmentTree.java @@ -25,6 +25,8 @@ package com.sun.source.tree; +import org.checkerframework.dataflow.qual.Pure; + /** * A tree node for an assignment expression. * @@ -44,6 +46,7 @@ public interface AssignmentTree extends ExpressionTree { * Returns the variable being assigned to. * @return the variable */ + @Pure ExpressionTree getVariable(); /** diff --git a/src/jdk.compiler/share/classes/com/sun/source/tree/MethodInvocationTree.java b/src/jdk.compiler/share/classes/com/sun/source/tree/MethodInvocationTree.java index 864a85837dbd..3197ab79776f 100644 --- a/src/jdk.compiler/share/classes/com/sun/source/tree/MethodInvocationTree.java +++ b/src/jdk.compiler/share/classes/com/sun/source/tree/MethodInvocationTree.java @@ -25,6 +25,8 @@ package com.sun.source.tree; +import org.checkerframework.dataflow.qual.Pure; + import java.util.List; /** @@ -54,6 +56,7 @@ public interface MethodInvocationTree extends ExpressionTree { * Returns the expression identifying the method to be invoked. * @return the method selection expression */ + @Pure ExpressionTree getMethodSelect(); /** diff --git a/src/jdk.compiler/share/classes/com/sun/source/tree/ParenthesizedTree.java b/src/jdk.compiler/share/classes/com/sun/source/tree/ParenthesizedTree.java index ee558e803434..6218644e381a 100644 --- a/src/jdk.compiler/share/classes/com/sun/source/tree/ParenthesizedTree.java +++ b/src/jdk.compiler/share/classes/com/sun/source/tree/ParenthesizedTree.java @@ -25,6 +25,8 @@ package com.sun.source.tree; +import org.checkerframework.dataflow.qual.Pure; + /** * A tree node for a parenthesized expression. Note: parentheses * not be preserved by the parser. @@ -45,5 +47,6 @@ public interface ParenthesizedTree extends ExpressionTree { * Returns the expression within the parentheses. * @return the expression */ + @Pure ExpressionTree getExpression(); } diff --git a/src/jdk.compiler/share/classes/com/sun/source/tree/TypeCastTree.java b/src/jdk.compiler/share/classes/com/sun/source/tree/TypeCastTree.java index cd46e8e8187f..a5b42022b3f3 100644 --- a/src/jdk.compiler/share/classes/com/sun/source/tree/TypeCastTree.java +++ b/src/jdk.compiler/share/classes/com/sun/source/tree/TypeCastTree.java @@ -25,6 +25,8 @@ package com.sun.source.tree; +import org.checkerframework.dataflow.qual.Pure; + /** * A tree node for a type cast expression. * @@ -50,5 +52,6 @@ public interface TypeCastTree extends ExpressionTree { * Returns the expression being cast. * @return the expression */ + @Pure ExpressionTree getExpression(); } diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/JCTree.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/JCTree.java index 8607703ce367..0eefd898055a 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/JCTree.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/JCTree.java @@ -25,6 +25,8 @@ package com.sun.tools.javac.tree; +import org.checkerframework.dataflow.qual.Pure; + import java.io.IOException; import java.io.StringWriter; import java.util.*; @@ -1822,6 +1824,7 @@ public List getTypeArguments() { return typeargs; } @DefinedBy(Api.COMPILER_TREE) + @Pure public JCExpression getMethodSelect() { return meth; } @DefinedBy(Api.COMPILER_TREE) public List getArguments() { diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/TreeInfo.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/TreeInfo.java index 30ed5fb97052..41d9615b5009 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/TreeInfo.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/TreeInfo.java @@ -894,6 +894,7 @@ public static Name fullName(JCTree tree) { } } + @Pure public static Symbol symbolFor(JCTree node) { Symbol sym = symbolForImpl(node); @@ -958,6 +959,7 @@ public static boolean isDeclaration(JCTree node) { /** If this tree is an identifier or a field, return its symbol, * otherwise return null. */ + @Pure public static Symbol symbol(JCTree tree) { tree = skipParens(tree); switch (tree.getTag()) { From 816a214a6cf16a69719a4f9906de3764ccdca97e Mon Sep 17 00:00:00 2001 From: Suzanne Millstein Date: Tue, 22 Sep 2026 08:16:15 -0700 Subject: [PATCH 3/4] Add purity annotations needed by the Checker Framework's dataflow Collections.singleton and Collections.singletonList allocate a fresh immutable collection, so they are side-effect-free but not deterministic. TypeMirror.toString() follows the rule that the @CFComment on Object.toString() records: a method that builds its result returns a string that differs according to ==, and @Deterministic requires that two calls return == results. LiteralTree.getValue() is @SideEffectFree rather than @Pure because javac's JCLiteral.getValue() boxes BOOLEAN and CHAR values, and Character.valueOf caches only below 128, so two calls on a char literal >= 128 do not return == values. --- src/java.base/share/classes/java/util/Collections.java | 2 ++ .../share/classes/javax/lang/model/type/TypeMirror.java | 2 ++ .../share/classes/com/sun/source/tree/LiteralTree.java | 3 +++ 3 files changed, 7 insertions(+) diff --git a/src/java.base/share/classes/java/util/Collections.java b/src/java.base/share/classes/java/util/Collections.java index b62d99fe5b40..ecd8c36344f9 100644 --- a/src/java.base/share/classes/java/util/Collections.java +++ b/src/java.base/share/classes/java/util/Collections.java @@ -5581,6 +5581,7 @@ private Object readResolve() { * @param o the sole object to be stored in the returned set. * @return an immutable set containing only the specified object. */ + @SideEffectFree public static Set singleton(T o) { return new SingletonSet<>(o); } @@ -5723,6 +5724,7 @@ public int hashCode() { * @return an immutable list containing only the specified object. * @since 1.3 */ + @SideEffectFree public static @ArrayLen(1) List singletonList(T o) { return new SingletonList<>(o); } diff --git a/src/java.compiler/share/classes/javax/lang/model/type/TypeMirror.java b/src/java.compiler/share/classes/javax/lang/model/type/TypeMirror.java index 0cc74266bf7a..5baccdf3779c 100644 --- a/src/java.compiler/share/classes/javax/lang/model/type/TypeMirror.java +++ b/src/java.compiler/share/classes/javax/lang/model/type/TypeMirror.java @@ -28,6 +28,7 @@ import org.checkerframework.checker.nullness.qual.Nullable; import org.checkerframework.checker.signature.qual.FullyQualifiedName; import org.checkerframework.dataflow.qual.Pure; +import org.checkerframework.dataflow.qual.SideEffectFree; import java.lang.annotation.Annotation; import java.util.List; @@ -144,6 +145,7 @@ public interface TypeMirror extends javax.lang.model.AnnotatedConstruct { * * @return a string representation of this type */ + @SideEffectFree @FullyQualifiedName String toString(); /** diff --git a/src/jdk.compiler/share/classes/com/sun/source/tree/LiteralTree.java b/src/jdk.compiler/share/classes/com/sun/source/tree/LiteralTree.java index 622e45887037..29826b628b8b 100644 --- a/src/jdk.compiler/share/classes/com/sun/source/tree/LiteralTree.java +++ b/src/jdk.compiler/share/classes/com/sun/source/tree/LiteralTree.java @@ -25,6 +25,8 @@ package com.sun.source.tree; +import org.checkerframework.dataflow.qual.SideEffectFree; + /** * A tree node for a literal expression. * Use {@link #getKind getKind} to determine the kind of literal. @@ -46,5 +48,6 @@ public interface LiteralTree extends ExpressionTree { * The value will be a boxed primitive value, a String, or {@code null}. * @return the value */ + @SideEffectFree Object getValue(); } From 0a4715220bae5d9ddf48454ce93b775996b181f9 Mon Sep 17 00:00:00 2001 From: Suzanne Millstein Date: Wed, 23 Sep 2026 09:35:51 -0700 Subject: [PATCH 4/4] Add purity annotations to readers, Record, and javax.lang.model methods These are needed for the Checker Framework's javacutil and framework-test to pass the Purity Checker. --- src/java.base/share/classes/java/io/BufferedReader.java | 2 ++ src/java.base/share/classes/java/io/LineNumberReader.java | 4 ++++ src/java.base/share/classes/java/lang/Record.java | 2 ++ .../classes/javax/lang/model/element/AnnotationMirror.java | 2 ++ .../classes/javax/lang/model/element/AnnotationValue.java | 3 +++ .../share/classes/javax/lang/model/element/Element.java | 2 ++ .../classes/javax/lang/model/element/ExecutableElement.java | 1 + .../share/classes/javax/lang/model/util/ElementFilter.java | 3 +++ 8 files changed, 19 insertions(+) diff --git a/src/java.base/share/classes/java/io/BufferedReader.java b/src/java.base/share/classes/java/io/BufferedReader.java index f2b2f2f38e23..d46c2b986874 100644 --- a/src/java.base/share/classes/java/io/BufferedReader.java +++ b/src/java.base/share/classes/java/io/BufferedReader.java @@ -450,6 +450,7 @@ private String implReadLine(boolean ignoreLF, boolean[] term) throws IOException * * @see java.nio.file.Files#readAllLines */ + @SideEffectsOnly("this") public @Nullable String readLine(@GuardSatisfied BufferedReader this) throws IOException { return readLine(false, null); } @@ -633,6 +634,7 @@ private void implReset() throws IOException { skipLF = markedSkipLF; } + @SideEffectsOnly("this") public void close(@GuardSatisfied BufferedReader this) throws IOException { Object lock = this.lock; if (lock instanceof InternalLock locker) { diff --git a/src/java.base/share/classes/java/io/LineNumberReader.java b/src/java.base/share/classes/java/io/LineNumberReader.java index 70a747084e5f..36c2be75cac0 100644 --- a/src/java.base/share/classes/java/io/LineNumberReader.java +++ b/src/java.base/share/classes/java/io/LineNumberReader.java @@ -33,6 +33,8 @@ import org.checkerframework.checker.lock.qual.GuardSatisfied; import org.checkerframework.checker.mustcall.qual.MustCallAlias; import org.checkerframework.checker.nullness.qual.Nullable; +import org.checkerframework.dataflow.qual.Pure; +import org.checkerframework.dataflow.qual.SideEffectsOnly; import org.checkerframework.framework.qual.AnnotatedFor; /** @@ -126,6 +128,7 @@ public void setLineNumber(@GuardSatisfied LineNumberReader this, @NonNegative in * * @see #setLineNumber */ + @Pure public @NonNegative int getLineNumber(@GuardSatisfied LineNumberReader this) { return lineNumber; } @@ -255,6 +258,7 @@ public int read(@GuardSatisfied LineNumberReader this) throws IOException { * @throws IOException * If an I/O error occurs */ + @SideEffectsOnly("this") public @Nullable String readLine(@GuardSatisfied LineNumberReader this) throws IOException { synchronized (lock) { boolean[] term = new boolean[1]; diff --git a/src/java.base/share/classes/java/lang/Record.java b/src/java.base/share/classes/java/lang/Record.java index 27a574de0e9c..6d5200670e34 100644 --- a/src/java.base/share/classes/java/lang/Record.java +++ b/src/java.base/share/classes/java/lang/Record.java @@ -28,6 +28,7 @@ import org.checkerframework.checker.nullness.qual.Nullable; import org.checkerframework.checker.signedness.qual.UnknownSignedness; import org.checkerframework.dataflow.qual.Pure; +import org.checkerframework.dataflow.qual.SideEffectFree; /** * This is the common base class of all Java language record classes. @@ -96,6 +97,7 @@ public abstract class Record { /** * Constructor for record classes to call. */ + @SideEffectFree protected Record() {} /** diff --git a/src/java.compiler/share/classes/javax/lang/model/element/AnnotationMirror.java b/src/java.compiler/share/classes/javax/lang/model/element/AnnotationMirror.java index c23d591dc4d7..3e3f972cca79 100644 --- a/src/java.compiler/share/classes/javax/lang/model/element/AnnotationMirror.java +++ b/src/java.compiler/share/classes/javax/lang/model/element/AnnotationMirror.java @@ -26,6 +26,7 @@ package javax.lang.model.element; import org.checkerframework.dataflow.qual.Pure; +import org.checkerframework.dataflow.qual.SideEffectFree; import java.util.Map; import javax.lang.model.type.DeclaredType; @@ -69,5 +70,6 @@ public interface AnnotationMirror { * @return the values of this annotation's elements, * or an empty map if there are none */ + @SideEffectFree Map getElementValues(); } diff --git a/src/java.compiler/share/classes/javax/lang/model/element/AnnotationValue.java b/src/java.compiler/share/classes/javax/lang/model/element/AnnotationValue.java index 77521dac1916..8e45505c34a4 100644 --- a/src/java.compiler/share/classes/javax/lang/model/element/AnnotationValue.java +++ b/src/java.compiler/share/classes/javax/lang/model/element/AnnotationValue.java @@ -25,6 +25,8 @@ package javax.lang.model.element; +import org.checkerframework.dataflow.qual.SideEffectFree; + /** * Represents a value of an annotation interface element. * A value is of one of the following types: @@ -44,6 +46,7 @@ public interface AnnotationValue { /** * {@return the value} */ + @SideEffectFree Object getValue(); /** diff --git a/src/java.compiler/share/classes/javax/lang/model/element/Element.java b/src/java.compiler/share/classes/javax/lang/model/element/Element.java index 6bf3ca147857..edaffdcdbe27 100644 --- a/src/java.compiler/share/classes/javax/lang/model/element/Element.java +++ b/src/java.compiler/share/classes/javax/lang/model/element/Element.java @@ -26,6 +26,7 @@ package javax.lang.model.element; import org.checkerframework.dataflow.qual.Pure; +import org.checkerframework.dataflow.qual.SideEffectFree; import java.lang.annotation.Annotation; import java.lang.annotation.AnnotationTypeMismatchException; @@ -234,6 +235,7 @@ public interface Element extends javax.lang.model.AnnotatedConstruct { * @jls 8.10 Record Classes * @revised 9 */ + @SideEffectFree List getEnclosedElements(); /** diff --git a/src/java.compiler/share/classes/javax/lang/model/element/ExecutableElement.java b/src/java.compiler/share/classes/javax/lang/model/element/ExecutableElement.java index 16ee6e4a4d46..808b5624a1d4 100644 --- a/src/java.compiler/share/classes/javax/lang/model/element/ExecutableElement.java +++ b/src/java.compiler/share/classes/javax/lang/model/element/ExecutableElement.java @@ -137,6 +137,7 @@ public interface ExecutableElement extends Element, Parameterizable { * * @return the default value, or {@code null} if none */ + @Pure AnnotationValue getDefaultValue(); /** diff --git a/src/java.compiler/share/classes/javax/lang/model/util/ElementFilter.java b/src/java.compiler/share/classes/javax/lang/model/util/ElementFilter.java index 0dfd86caf1a9..8713ec581360 100644 --- a/src/java.compiler/share/classes/javax/lang/model/util/ElementFilter.java +++ b/src/java.compiler/share/classes/javax/lang/model/util/ElementFilter.java @@ -25,6 +25,8 @@ package javax.lang.model.util; +import org.checkerframework.dataflow.qual.SideEffectFree; + import java.util.Collections; import java.util.List; import java.util.Set; @@ -145,6 +147,7 @@ private ElementFilter() {} // Do not instantiate. * {@return a list of methods in {@code elements}} * @param elements the elements to filter */ + @SideEffectFree public static List methodsIn(Iterable elements) { return listFilter(elements, METHOD_KIND, ExecutableElement.class);