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.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.base/share/classes/java/util/Comparator.java b/src/java.base/share/classes/java/util/Comparator.java index 2e1c1e030151..115b025326f7 100644 --- a/src/java.base/share/classes/java/util/Comparator.java +++ b/src/java.base/share/classes/java/util/Comparator.java @@ -228,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) -> { @@ -253,6 +254,7 @@ default Comparator thenComparing(Comparator other) { * @see #thenComparing(Comparator) * @since 1.8 */ + @SideEffectFree default Comparator thenComparing( Function keyExtractor, Comparator keyComparator) @@ -277,6 +279,7 @@ default Comparator thenComparing( * @see #thenComparing(Comparator) * @since 1.8 */ + @SideEffectFree default > Comparator thenComparing( Function keyExtractor) { @@ -298,6 +301,7 @@ default > Comparator thenComparing( * @see #thenComparing(Comparator) * @since 1.8 */ + @SideEffectFree default Comparator thenComparingInt(ToIntFunction keyExtractor) { return thenComparing(comparingInt(keyExtractor)); } @@ -317,6 +321,7 @@ default Comparator thenComparingInt(ToIntFunction keyExtractor) { * @see #thenComparing(Comparator) * @since 1.8 */ + @SideEffectFree default Comparator thenComparingLong(ToLongFunction keyExtractor) { return thenComparing(comparingLong(keyExtractor)); } @@ -336,6 +341,7 @@ default Comparator thenComparingLong(ToLongFunction keyExtractor) * @see #thenComparing(Comparator) * @since 1.8 */ + @SideEffectFree default Comparator thenComparingDouble(ToDoubleFunction keyExtractor) { return thenComparing(comparingDouble(keyExtractor)); } @@ -353,6 +359,7 @@ default Comparator thenComparingDouble(ToDoubleFunction keyExtract * @see Comparable * @since 1.8 */ + @Pure public static > Comparator reverseOrder() { return Collections.reverseOrder(); } @@ -370,6 +377,7 @@ public static > Comparator reverseOrder() { * @see Comparable * @since 1.8 */ + @Pure @SuppressWarnings("unchecked") public static > Comparator naturalOrder() { return (Comparator) Comparators.NaturalOrderComparator.INSTANCE; @@ -392,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); } @@ -413,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); } @@ -444,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) @@ -479,6 +490,7 @@ public static Comparator comparing( * @throws NullPointerException if the argument is null * @since 1.8 */ + @SideEffectFree public static > Comparator comparing( Function keyExtractor) { @@ -502,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) @@ -523,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) @@ -544,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) 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..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 @@ -25,6 +25,9 @@ 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; @@ -43,6 +46,7 @@ public interface AnnotationMirror { /** * {@return the type of this annotation} */ + @Pure DeclaredType getAnnotationType(); /** @@ -66,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 6e0cba8ba6c6..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 @@ -25,6 +25,8 @@ 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; @@ -68,6 +70,7 @@ public interface Element extends javax.lang.model.AnnotatedConstruct { * @see TypeParameterElement#asType * @see VariableElement#asType */ + @Pure TypeMirror asType(); /** @@ -232,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 d4c9c881f3b6..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 @@ -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(); /** @@ -129,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/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..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; @@ -111,6 +112,7 @@ public interface TypeMirror extends javax.lang.model.AnnotatedConstruct { * * */ + @Pure TypeKind getKind(); /** @@ -143,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/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); 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/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(); } 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()) {