From 950f3912d991c8306f6ab3bc6e5fc8d23ad8e3ff Mon Sep 17 00:00:00 2001 From: Vicente Romero Date: Thu, 17 Apr 2025 08:58:19 -0400 Subject: [PATCH 01/34] 8354556: Expand value-based class warnings to java.lang.ref API --- .../tools/symbolgenerator/CreateSymbols.java | 1 + .../share/classes/java/lang/ref/Cleaner.java | 2 +- .../java/lang/ref/PhantomReference.java | 4 +- .../classes/java/lang/ref/Reference.java | 2 +- .../classes/java/lang/ref/ReferenceQueue.java | 2 +- .../classes/java/lang/ref/SoftReference.java | 6 +- .../classes/java/lang/ref/WeakReference.java | 6 +- .../share/classes/java/util/WeakHashMap.java | 4 +- .../jdk/internal/RequiresIdentity.java | 51 +++++++++++++++ .../classes/jdk/internal/ValueBased.java | 1 - .../com/sun/tools/javac/code/Flags.java | 9 ++- .../com/sun/tools/javac/code/Lint.java | 6 ++ .../com/sun/tools/javac/code/Symtab.java | 4 ++ .../com/sun/tools/javac/code/Types.java | 64 +++++++++++++++++++ .../com/sun/tools/javac/comp/Annotate.java | 6 ++ .../com/sun/tools/javac/comp/Attr.java | 55 ++++++++++++++-- .../com/sun/tools/javac/jvm/ClassReader.java | 9 ++- .../tools/javac/resources/compiler.properties | 8 +++ .../AttemptToSynchronizeOnInstanceOfVbc.java | 2 +- .../tools/javac/lint/ExternalAbuseOfVbc.java | 9 ++- .../javac/lint/RequiresIdentityHelper.java | 14 ++++ .../javac/lint/RequiresIdentityTest.java | 28 ++++++++ .../tools/javac/lint/RequiresIdentityTest.out | 13 ++++ 23 files changed, 278 insertions(+), 28 deletions(-) create mode 100644 src/java.base/share/classes/jdk/internal/RequiresIdentity.java create mode 100644 test/langtools/tools/javac/lint/RequiresIdentityHelper.java create mode 100644 test/langtools/tools/javac/lint/RequiresIdentityTest.java create mode 100644 test/langtools/tools/javac/lint/RequiresIdentityTest.out diff --git a/make/langtools/src/classes/build/tools/symbolgenerator/CreateSymbols.java b/make/langtools/src/classes/build/tools/symbolgenerator/CreateSymbols.java index db8924c79fbf4..edb0ea9483af5 100644 --- a/make/langtools/src/classes/build/tools/symbolgenerator/CreateSymbols.java +++ b/make/langtools/src/classes/build/tools/symbolgenerator/CreateSymbols.java @@ -2202,6 +2202,7 @@ private boolean readAttribute(FeatureDescription feature, Attribute attr) { chd.permittedSubclasses = a.permittedSubclasses().stream().map(ClassEntry::asInternalName).collect(Collectors.toList()); } case ModuleMainClassAttribute a -> ((ModuleHeaderDescription) feature).moduleMainClass = a.mainClass().asInternalName(); + case RuntimeVisibleTypeAnnotationsAttribute a -> {/* do nothing for now */} default -> throw new IllegalArgumentException("Unhandled attribute: " + attr.attributeName()); // Do nothing } diff --git a/src/java.base/share/classes/java/lang/ref/Cleaner.java b/src/java.base/share/classes/java/lang/ref/Cleaner.java index c5a163831c893..20a964be21da9 100644 --- a/src/java.base/share/classes/java/lang/ref/Cleaner.java +++ b/src/java.base/share/classes/java/lang/ref/Cleaner.java @@ -219,7 +219,7 @@ public static Cleaner create(ThreadFactory threadFactory) { * @param action a {@code Runnable} to invoke when the object becomes phantom reachable * @return a {@code Cleanable} instance */ - public Cleanable register(Object obj, Runnable action) { + public Cleanable register(@jdk.internal.RequiresIdentity Object obj, Runnable action) { Objects.requireNonNull(obj, "obj"); Objects.requireNonNull(action, "action"); return new CleanerImpl.PhantomCleanableRef(obj, this, action); diff --git a/src/java.base/share/classes/java/lang/ref/PhantomReference.java b/src/java.base/share/classes/java/lang/ref/PhantomReference.java index 3f01144afe618..a6e144dc7b6e2 100644 --- a/src/java.base/share/classes/java/lang/ref/PhantomReference.java +++ b/src/java.base/share/classes/java/lang/ref/PhantomReference.java @@ -51,7 +51,7 @@ * @since 1.2 */ -public non-sealed class PhantomReference extends Reference { +public non-sealed class PhantomReference<@jdk.internal.RequiresIdentity T> extends Reference { /** * Returns this reference object's referent. Because the referent of a @@ -101,7 +101,7 @@ void clearImpl() { * @param q the queue with which the reference is to be registered, * or {@code null} if registration is not required */ - public PhantomReference(T referent, ReferenceQueue q) { + public PhantomReference(@jdk.internal.RequiresIdentity T referent, ReferenceQueue q) { super(referent, q); } diff --git a/src/java.base/share/classes/java/lang/ref/Reference.java b/src/java.base/share/classes/java/lang/ref/Reference.java index 13ba76e5dd2f2..954129ca17ccd 100644 --- a/src/java.base/share/classes/java/lang/ref/Reference.java +++ b/src/java.base/share/classes/java/lang/ref/Reference.java @@ -44,7 +44,7 @@ * @sealedGraph */ -public abstract sealed class Reference +public abstract sealed class Reference<@jdk.internal.RequiresIdentity T> permits PhantomReference, SoftReference, WeakReference, FinalReference { /* The state of a Reference object is characterized by two attributes. It diff --git a/src/java.base/share/classes/java/lang/ref/ReferenceQueue.java b/src/java.base/share/classes/java/lang/ref/ReferenceQueue.java index 07110a19fa132..33cfc9c1af603 100644 --- a/src/java.base/share/classes/java/lang/ref/ReferenceQueue.java +++ b/src/java.base/share/classes/java/lang/ref/ReferenceQueue.java @@ -47,7 +47,7 @@ * @since 1.2 */ -public class ReferenceQueue { +public class ReferenceQueue<@jdk.internal.RequiresIdentity T> { private static class Null extends ReferenceQueue { @Override boolean enqueue(Reference r) { diff --git a/src/java.base/share/classes/java/lang/ref/SoftReference.java b/src/java.base/share/classes/java/lang/ref/SoftReference.java index 925f894fdabaa..0bc372eb105d3 100644 --- a/src/java.base/share/classes/java/lang/ref/SoftReference.java +++ b/src/java.base/share/classes/java/lang/ref/SoftReference.java @@ -62,7 +62,7 @@ * @since 1.2 */ -public non-sealed class SoftReference extends Reference { +public non-sealed class SoftReference<@jdk.internal.RequiresIdentity T> extends Reference { /** * Timestamp clock, updated by the garbage collector @@ -82,7 +82,7 @@ public non-sealed class SoftReference extends Reference { * * @param referent object the new soft reference will refer to */ - public SoftReference(T referent) { + public SoftReference(@jdk.internal.RequiresIdentity T referent) { super(referent); this.timestamp = clock; } @@ -96,7 +96,7 @@ public SoftReference(T referent) { * or {@code null} if registration is not required * */ - public SoftReference(T referent, ReferenceQueue q) { + public SoftReference(@jdk.internal.RequiresIdentity T referent, ReferenceQueue q) { super(referent, q); this.timestamp = clock; } diff --git a/src/java.base/share/classes/java/lang/ref/WeakReference.java b/src/java.base/share/classes/java/lang/ref/WeakReference.java index 1a733bb32b97d..4b4f834485e99 100644 --- a/src/java.base/share/classes/java/lang/ref/WeakReference.java +++ b/src/java.base/share/classes/java/lang/ref/WeakReference.java @@ -46,7 +46,7 @@ * @since 1.2 */ -public non-sealed class WeakReference extends Reference { +public non-sealed class WeakReference<@jdk.internal.RequiresIdentity T> extends Reference { /** * Creates a new weak reference that refers to the given object. The new @@ -54,7 +54,7 @@ public non-sealed class WeakReference extends Reference { * * @param referent object the new weak reference will refer to */ - public WeakReference(T referent) { + public WeakReference(@jdk.internal.RequiresIdentity T referent) { super(referent); } @@ -66,7 +66,7 @@ public WeakReference(T referent) { * @param q the queue with which the reference is to be registered, * or {@code null} if registration is not required */ - public WeakReference(T referent, ReferenceQueue q) { + public WeakReference(@jdk.internal.RequiresIdentity T referent, ReferenceQueue q) { super(referent, q); } diff --git a/src/java.base/share/classes/java/util/WeakHashMap.java b/src/java.base/share/classes/java/util/WeakHashMap.java index 276e8731d847d..7f2960eb2ad39 100644 --- a/src/java.base/share/classes/java/util/WeakHashMap.java +++ b/src/java.base/share/classes/java/util/WeakHashMap.java @@ -132,7 +132,7 @@ * @see java.util.HashMap * @see java.lang.ref.WeakReference */ -public class WeakHashMap +public class WeakHashMap<@jdk.internal.RequiresIdentity K,V> extends AbstractMap implements Map { @@ -457,7 +457,7 @@ Entry getEntry(Object key) { * (A {@code null} return can also indicate that the map * previously associated {@code null} with {@code key}.) */ - public V put(K key, V value) { + public V put(@jdk.internal.RequiresIdentity K key, V value) { Object k = maskNull(key); int h = hash(k); Entry[] tab = getTable(); diff --git a/src/java.base/share/classes/jdk/internal/RequiresIdentity.java b/src/java.base/share/classes/jdk/internal/RequiresIdentity.java new file mode 100644 index 0000000000000..5b54debd095c2 --- /dev/null +++ b/src/java.base/share/classes/jdk/internal/RequiresIdentity.java @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.internal; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import static java.lang.annotation.ElementType.PARAMETER; +import static java.lang.annotation.ElementType.TYPE_PARAMETER; + +/** + * Indicates that the annotated parameter or type parameter is not expected to be a + * Value Based class. + * Using a parameter or type parameter of a value-based classes + * should produce warnings about behavior that is inconsistent with identity based semantics. + * + * Note this internal annotation is handled specially by the javac compiler. + * To work properly with {@code --release older-release}, it requires special + * handling in {@code make/langtools/src/classes/build/tools/symbolgenerator/CreateSymbols.java} + * and {@code src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassReader.java}. + * + * @since 25 + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(value={PARAMETER, TYPE_PARAMETER}) +public @interface RequiresIdentity { +} diff --git a/src/java.base/share/classes/jdk/internal/ValueBased.java b/src/java.base/share/classes/jdk/internal/ValueBased.java index d27ab73c01f68..160ffce874a1b 100644 --- a/src/java.base/share/classes/jdk/internal/ValueBased.java +++ b/src/java.base/share/classes/jdk/internal/ValueBased.java @@ -46,4 +46,3 @@ @Target(value={TYPE}) public @interface ValueBased { } - diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Flags.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Flags.java index 53abf99ed7d2b..639ae92fdda22 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Flags.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Flags.java @@ -409,6 +409,11 @@ public static EnumSet asFlagSet(long flags) { */ public static final long NON_SEALED = 1L<<63; // ClassSymbols + /** + * Flag to indicate that the type should not be value based + */ + public static final long REQUIRES_IDENTITY = 1<<20; // TypeSymbols + /** * Describe modifier flags as they might appear in source code, i.e., * separated by spaces and in the order suggested by JLS 8.1.1. @@ -560,7 +565,9 @@ public enum Flag { public String toString() { return "non-sealed"; } - }; + }, + REQUIRES_IDENTITY(Flags.REQUIRES_IDENTITY) + ; Flag(long flag) { this.value = flag; diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Lint.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Lint.java index fb1b782d87e39..4f94508e1ee3d 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Lint.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Lint.java @@ -174,6 +174,7 @@ private void initializeRootIfNeeded() { values.add(LintCategory.PREVIEW); } values.add(LintCategory.SYNCHRONIZATION); + values.add(LintCategory.IDENTITY); values.add(LintCategory.INCUBATING); } @@ -368,6 +369,11 @@ public enum LintCategory { */ SYNCHRONIZATION("synchronization"), + /** + * Warn about uses of @ValueBased classes where an identity class is expected. + */ + IDENTITY("identity"), + /** * Warn about issues relating to use of text blocks * diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Symtab.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Symtab.java index cfe4ef662e5cb..287d7b6a581b6 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Symtab.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Symtab.java @@ -228,6 +228,8 @@ public static Symtab instance(Context context) { public final Type constantBootstrapsType; public final Type valueBasedType; public final Type valueBasedInternalType; + public final Type requiresIdentityType; + public final Type requiresIdentityInternalType; public final Type classDescType; public final Type enumDescType; public final Type ioType; @@ -611,6 +613,8 @@ public R accept(ElementVisitor v, P p) { constantBootstrapsType = enterClass("java.lang.invoke.ConstantBootstraps"); valueBasedType = enterClass("jdk.internal.ValueBased"); valueBasedInternalType = enterSyntheticAnnotation("jdk.internal.ValueBased+Annotation"); + requiresIdentityType = enterClass("jdk.internal.RequiresIdentity"); + requiresIdentityInternalType = enterSyntheticAnnotation("jdk.internal.RequiresIdentity+Annotation"); classDescType = enterClass("java.lang.constant.ClassDesc"); enumDescType = enterClass("java.lang.Enum$EnumDesc"); ioType = enterClass("java.io.IO"); diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Types.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Types.java index 1bc1e078c1a97..92f0bee619363 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Types.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Types.java @@ -5331,6 +5331,70 @@ public Type constantType(LoadableConstant c) { } // + // + public boolean isValueBased(Type t) { + return t != null && t.tsym != null && (t.tsym.flags() & VALUE_BASED) != 0; + } + + public boolean needsRequiresIdentityWarning(Type t) { + RequiresIdentityVisitor requiresIdentityVisitor = new RequiresIdentityVisitor(); + requiresIdentityVisitor.visit(t); + return requiresIdentityVisitor.requiresWarning; + } + + // where + class RequiresIdentityVisitor extends Types.UnaryVisitor { + boolean requiresWarning = false; + + @Override + public Void visitType(Type t, Void _unused) { + return null; + } + + @Override + public Void visitWildcardType(WildcardType t, Void _unused) { + return visit(t.type); + } + + @Override + public Void visitTypeVar(TypeVar t, Void aVoid) { + return null; + } + + @Override + public Void visitArrayType(ArrayType t, Void _unused) { + return visit(t.elemtype); + } + + @Override + public Void visitClassType(ClassType t, Void _unused) { + if (t != null && t.tsym != null) { + SymbolMetadata sm = t.tsym.getMetadata(); + if (sm != null) { + for (Attribute.TypeCompound ta: sm.getTypeAttributes()) { + if (ta.type.tsym == syms.requiresIdentityType.tsym) { + TypeAnnotationPosition tap = ta.position; + if (tap.type == TargetType.CLASS_TYPE_PARAMETER) { + int index = tap.parameter_index; + Type tparam = t.typarams_field.get(index); + if (isValueBased(tparam)) { + requiresWarning = true; + return null; + } + } + } + } + } + } + visit(t.getEnclosingType()); + for (Type targ : t.getTypeArguments()) { + visit(targ); + } + return null; + } + } + // + public void newRound() { descCache._map.clear(); isDerivedRawCache.clear(); diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Annotate.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Annotate.java index 49f5a7472aa74..9be65f2441f65 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Annotate.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Annotate.java @@ -379,6 +379,12 @@ private void annotateNow(Symbol toAnnotate, && types.isSameType(c.type, syms.restrictedType)) { toAnnotate.flags_field |= Flags.RESTRICTED; } + + if (!c.type.isErroneous() + && (toAnnotate.kind == TYP || toAnnotate.kind == VAR) + && types.isSameType(c.type, syms.requiresIdentityType)) { + toAnnotate.flags_field |= Flags.REQUIRES_IDENTITY; + } } List buf = List.nil(); diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java index 6dc0238f87e54..de22e7d5961e2 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java @@ -784,6 +784,24 @@ KindSelector attribArgs(KindSelector initialKind, List trees, Env< return kind; } + private boolean requiresIdentity(Type t) { + return t != null && t.tsym != null && (t.tsym.flags() & REQUIRES_IDENTITY) != 0; + } + + private boolean requiresIdentity(VarSymbol vsym) { + if (vsym != null) { + SymbolMetadata sm = vsym.getMetadata(); + if (sm != null) { + for (Attribute.Compound ca: sm.getDeclarationAttributes()) { + if (ca.type.tsym == syms.requiresIdentityType.tsym) { + return true; + } + } + } + } + return false; + } + /** Attribute a type argument list, returning a list of types. * Caller is responsible for calling checkRefTypes. */ @@ -1328,6 +1346,9 @@ public void visitVarDef(JCVariableDecl tree) { log.error(tree, Errors.IllegalRecordComponentName(v)); } } + if (types.needsRequiresIdentityWarning(tree.vartype.type)) { + env.info.lint.logIfEnabled(tree.vartype.pos(), LintWarnings.AttemptToUseValueBasedWhereIdentityExpected); + } } finally { chk.setLint(prevLint); @@ -1945,17 +1966,16 @@ private Symbol enumConstant(JCTree tree, Type enumType) { public void visitSynchronized(JCSynchronized tree) { chk.checkRefType(tree.pos(), attribExpr(tree.lock, env)); - if (isValueBased(tree.lock.type)) { - env.info.lint.logIfEnabled(tree.pos(), LintWarnings.AttemptToSynchronizeOnInstanceOfValueBasedClass); + if (types.isValueBased(tree.lock.type)) { + if (env.info.lint.isEnabled(LintCategory.SYNCHRONIZATION)) { + env.info.lint.logIfEnabled(tree.pos(), LintWarnings.AttemptToSynchronizeOnInstanceOfValueBasedClass); + } else { + env.info.lint.logIfEnabled(tree.pos(), LintWarnings.AttemptToSynchronizeOnInstanceOfValueBasedClass2); + } } attribStat(tree.body, env); result = null; } - // where - private boolean isValueBased(Type t) { - return t != null && t.tsym != null && (t.tsym.flags() & VALUE_BASED) != 0; - } - public void visitTry(JCTry tree) { // Create a new local environment with a local @@ -2667,10 +2687,31 @@ public void visitApply(JCMethodInvocation tree) { // current context. Also, capture the return type Type capturedRes = resultInfo.checkContext.inferenceContext().cachedCapture(tree, restype, true); result = check(tree, capturedRes, KindSelector.VAL, resultInfo); + checkIfRequireIdentity(tree, msym); } chk.validate(tree.typeargs, localEnv); } //where + void checkIfRequireIdentity(JCMethodInvocation tree, Symbol sym) { + List argExps = tree.args; + if (sym instanceof MethodSymbol ms && ms.params != null) { + VarSymbol lastParam = ms.params.head; + for (VarSymbol param: ms.params) { + if (requiresIdentity(param) && types.isValueBased(argExps.head.type)) { + env.info.lint.logIfEnabled(argExps.head.pos(), LintWarnings.AttemptToUseValueBasedWhereIdentityExpected); + } + lastParam = param; + argExps = argExps.tail; + } + while (argExps != null && !argExps.isEmpty()) { + if (requiresIdentity(lastParam) && types.isValueBased(argExps.head.type)) { + env.info.lint.logIfEnabled(argExps.head.pos(), LintWarnings.AttemptToUseValueBasedWhereIdentityExpected); + } + argExps = argExps.tail; + } + } + } + Type adjustMethodReturnType(Symbol msym, Type qualifierType, Name methodName, List argtypes, Type restype) { if (msym != null && (msym.owner == syms.objectType.tsym || msym.owner.isInterface()) && diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassReader.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassReader.java index 6f55903f489ab..f8f45096cbce0 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassReader.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassReader.java @@ -1627,8 +1627,13 @@ void attachTypeAnnotations(final Symbol sym) { int numAttributes = nextChar(); if (numAttributes != 0) { ListBuffer proxies = new ListBuffer<>(); - for (int i = 0; i < numAttributes; i++) - proxies.append(readTypeAnnotation()); + for (int i = 0; i < numAttributes; i++) { + TypeAnnotationProxy proxy = readTypeAnnotation(); + proxies.append(proxy); + if (proxy.compound.type.tsym == syms.requiresIdentityType.tsym) { + sym.flags_field |= REQUIRES_IDENTITY; + } + } annotate.normal(new TypeAnnotationCompleter(sym, proxies.toList())); } } diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler.properties b/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler.properties index 79b6a4fd1da4a..0ffc050790b55 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler.properties +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler.properties @@ -4249,6 +4249,14 @@ compiler.warn.declared.using.preview=\ compiler.warn.attempt.to.synchronize.on.instance.of.value.based.class=\ attempt to synchronize on an instance of a value-based class +# lint: identity +compiler.warn.attempt.to.synchronize.on.instance.of.value.based.class2=\ + attempt to synchronize on an instance of a value-based class + +# lint: identity +compiler.warn.attempt.to.use.value.based.where.identity.expected=\ + attempt to use a value-based type where an identity is expected + # 0: type compiler.err.enclosing.class.type.non.denotable=\ enclosing class type: {0}\n\ diff --git a/test/langtools/tools/javac/diags/examples/AttemptToSynchronizeOnInstanceOfVbc.java b/test/langtools/tools/javac/diags/examples/AttemptToSynchronizeOnInstanceOfVbc.java index 9d63a2af0a430..e677c4b3b8a07 100644 --- a/test/langtools/tools/javac/diags/examples/AttemptToSynchronizeOnInstanceOfVbc.java +++ b/test/langtools/tools/javac/diags/examples/AttemptToSynchronizeOnInstanceOfVbc.java @@ -22,7 +22,7 @@ */ // key: compiler.warn.attempt.to.synchronize.on.instance.of.value.based.class -// options: -Xlint:synchronization +// options: -Xlint:identity class AttemptToSynchronizeOnInstanceOfVbc { void foo(Integer i) { diff --git a/test/langtools/tools/javac/lint/ExternalAbuseOfVbc.java b/test/langtools/tools/javac/lint/ExternalAbuseOfVbc.java index 53de855fb0105..8b26d6af3345a 100644 --- a/test/langtools/tools/javac/lint/ExternalAbuseOfVbc.java +++ b/test/langtools/tools/javac/lint/ExternalAbuseOfVbc.java @@ -4,9 +4,12 @@ * @summary lint should warn when an instance of a value based class is synchronized upon * @compile/fail/ref=ExternalAbuseOfVbc.out -XDrawDiagnostics -Werror -Xlint ExternalAbuseOfVbc.java * @compile/fail/ref=ExternalAbuseOfVbc.out -XDrawDiagnostics -Werror -Xlint:all ExternalAbuseOfVbc.java - * @compile/fail/ref=ExternalAbuseOfVbc.out -XDrawDiagnostics -Werror -Xlint:synchronization ExternalAbuseOfVbc.java - * @compile/fail/ref=ExternalAbuseOfVbc.out --release 16 -XDrawDiagnostics -Werror -Xlint:synchronization ExternalAbuseOfVbc.java - * @compile/ref=LintModeOffAbuseOfVbc.out -XDrawDiagnostics -Werror -Xlint:-synchronization ExternalAbuseOfVbc.java + * @compile/fail/ref=ExternalAbuseOfVbc.out -XDrawDiagnostics -Werror -Xlint:synchronized ExternalAbuseOfVbc.java + * @compile/fail/ref=ExternalAbuseOfVbc.out -XDrawDiagnostics -Werror -Xlint:identity ExternalAbuseOfVbc.java + * @compile/fail/ref=ExternalAbuseOfVbc.out --release 16 -XDrawDiagnostics -Werror -Xlint:synchronized ExternalAbuseOfVbc.java + * @compile/fail/ref=ExternalAbuseOfVbc.out --release 16 -XDrawDiagnostics -Werror -Xlint:identity ExternalAbuseOfVbc.java + * @compile/ref=LintModeOffAbuseOfVbc.out -XDrawDiagnostics -Werror -Xlint:-synchronized ExternalAbuseOfVbc.java + * @compile/ref=LintModeOffAbuseOfVbc.out -XDrawDiagnostics -Werror -Xlint:-identity ExternalAbuseOfVbc.java */ public final class ExternalAbuseOfVbc { diff --git a/test/langtools/tools/javac/lint/RequiresIdentityHelper.java b/test/langtools/tools/javac/lint/RequiresIdentityHelper.java new file mode 100644 index 0000000000000..e07fd10ffa2bd --- /dev/null +++ b/test/langtools/tools/javac/lint/RequiresIdentityHelper.java @@ -0,0 +1,14 @@ +/* /nodynamiccopyright/ */ + +package java.lang; + +public class RequiresIdentityHelper<@jdk.internal.RequiresIdentity T> { + public RequiresIdentityHelper() {} +} + +class RequiresIdentity2 { + public RequiresIdentity2() {} + public void foo(@jdk.internal.RequiresIdentity Object o) {} + public void bar(@jdk.internal.RequiresIdentity Object... o) {} + public void gg(@jdk.internal.RequiresIdentity T ri) {} +} diff --git a/test/langtools/tools/javac/lint/RequiresIdentityTest.java b/test/langtools/tools/javac/lint/RequiresIdentityTest.java new file mode 100644 index 0000000000000..1e668abef0e9e --- /dev/null +++ b/test/langtools/tools/javac/lint/RequiresIdentityTest.java @@ -0,0 +1,28 @@ +/* + * @test /nodynamiccopyright/ + * @bug 8354556 + * @summary Expand value-based class warnings to java.lang.ref API + * @compile/fail/ref=RequiresIdentityTest.out --patch-module java.base=${test.src} -Werror -XDrawDiagnostics -Xlint:identity RequiresIdentityHelper.java RequiresIdentityTest.java + */ + +package java.lang; + +public class RequiresIdentityTest { + class Box {} + + RequiresIdentityHelper field; // should warn + RequiresIdentityHelper[] field2; // should warn + Box> field3; // should warn + Box> field4; // should warn + + public RequiresIdentityTest() {} + + void test(RequiresIdentity2 ri, Integer i) { + RequiresIdentityHelper localVar; // should warn + RequiresIdentityHelper[] localVar2; // should warn + // there should be warnings for the invocations below + ri.foo(i); + ri.bar(i, i); // two for this one + ri.gg(i); + } +} diff --git a/test/langtools/tools/javac/lint/RequiresIdentityTest.out b/test/langtools/tools/javac/lint/RequiresIdentityTest.out new file mode 100644 index 0000000000000..a53484680e8b9 --- /dev/null +++ b/test/langtools/tools/javac/lint/RequiresIdentityTest.out @@ -0,0 +1,13 @@ +RequiresIdentityTest.java:13:27: compiler.warn.attempt.to.use.value.based.where.identity.expected +RequiresIdentityTest.java:14:36: compiler.warn.attempt.to.use.value.based.where.identity.expected +RequiresIdentityTest.java:15:8: compiler.warn.attempt.to.use.value.based.where.identity.expected +RequiresIdentityTest.java:16:8: compiler.warn.attempt.to.use.value.based.where.identity.expected +RequiresIdentityTest.java:21:31: compiler.warn.attempt.to.use.value.based.where.identity.expected +RequiresIdentityTest.java:22:40: compiler.warn.attempt.to.use.value.based.where.identity.expected +RequiresIdentityTest.java:24:16: compiler.warn.attempt.to.use.value.based.where.identity.expected +RequiresIdentityTest.java:25:16: compiler.warn.attempt.to.use.value.based.where.identity.expected +RequiresIdentityTest.java:25:19: compiler.warn.attempt.to.use.value.based.where.identity.expected +RequiresIdentityTest.java:26:15: compiler.warn.attempt.to.use.value.based.where.identity.expected +- compiler.err.warnings.and.werror +1 error +10 warnings From 4c4aebe2701da822fb3583401fd5dda138f2c16d Mon Sep 17 00:00:00 2001 From: Vicente Romero Date: Mon, 21 Apr 2025 10:29:02 -0400 Subject: [PATCH 02/34] minor fixes --- .../share/classes/com/sun/tools/javac/code/Types.java | 2 +- .../share/classes/com/sun/tools/javac/comp/Attr.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Types.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Types.java index 92f0bee619363..a404b15edcbfd 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Types.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Types.java @@ -5358,7 +5358,7 @@ public Void visitWildcardType(WildcardType t, Void _unused) { @Override public Void visitTypeVar(TypeVar t, Void aVoid) { - return null; + return visit(t.getUpperBound()); } @Override diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java index de22e7d5961e2..43cafb77a44de 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java @@ -1346,7 +1346,7 @@ public void visitVarDef(JCVariableDecl tree) { log.error(tree, Errors.IllegalRecordComponentName(v)); } } - if (types.needsRequiresIdentityWarning(tree.vartype.type)) { + if (tree.vartype.type != null && types.needsRequiresIdentityWarning(tree.vartype.type)) { env.info.lint.logIfEnabled(tree.vartype.pos(), LintWarnings.AttemptToUseValueBasedWhereIdentityExpected); } } From d35ef5062c174f68f6966384c89ce85cb4042ad4 Mon Sep 17 00:00:00 2001 From: Vicente Romero Date: Mon, 21 Apr 2025 14:12:57 -0400 Subject: [PATCH 03/34] fixing bug --- .../com/sun/tools/javac/code/Types.java | 10 ++----- .../com/sun/tools/javac/comp/Attr.java | 4 ++- .../tools/javac/resources/javac.properties | 3 ++ .../tools/javac/diags/examples.not-yet.txt | 2 ++ .../diags/examples/RequiresIdentity.java | 29 +++++++++++++++++++ .../tools/javac/lint/ExternalAbuseOfVbc.java | 7 ++--- .../tools/javac/lint/ExternalAbuseOfVbc.out | 2 +- 7 files changed, 44 insertions(+), 13 deletions(-) create mode 100644 test/langtools/tools/javac/diags/examples/RequiresIdentity.java diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Types.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Types.java index a404b15edcbfd..86403eebffdbf 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Types.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Types.java @@ -5356,11 +5356,6 @@ public Void visitWildcardType(WildcardType t, Void _unused) { return visit(t.type); } - @Override - public Void visitTypeVar(TypeVar t, Void aVoid) { - return visit(t.getUpperBound()); - } - @Override public Void visitArrayType(ArrayType t, Void _unused) { return visit(t.elemtype); @@ -5376,8 +5371,9 @@ public Void visitClassType(ClassType t, Void _unused) { TypeAnnotationPosition tap = ta.position; if (tap.type == TargetType.CLASS_TYPE_PARAMETER) { int index = tap.parameter_index; - Type tparam = t.typarams_field.get(index); - if (isValueBased(tparam)) { + // ClassType::getTypeArguments() can be empty for raw types + Type tparam = t.getTypeArguments().isEmpty() ? null : t.getTypeArguments().get(index); + if (tparam != null && isValueBased(tparam)) { requiresWarning = true; return null; } diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java index 43cafb77a44de..8baf5608527de 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java @@ -1346,7 +1346,9 @@ public void visitVarDef(JCVariableDecl tree) { log.error(tree, Errors.IllegalRecordComponentName(v)); } } - if (tree.vartype.type != null && types.needsRequiresIdentityWarning(tree.vartype.type)) { + if (tree.vartype != null && + tree.vartype.type != null && + types.needsRequiresIdentityWarning(tree.vartype.type)) { env.info.lint.logIfEnabled(tree.vartype.pos(), LintWarnings.AttemptToUseValueBasedWhereIdentityExpected); } } diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/javac.properties b/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/javac.properties index 28a89ee3072f5..50c31aba6afe2 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/javac.properties +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/javac.properties @@ -294,6 +294,9 @@ javac.opt.Xlint.desc.restricted=\ javac.opt.Xlint.desc.synchronization=\ Warn about synchronization attempts on instances of value-based classes. +javac.opt.Xlint.desc.identity=\ + Warn about uses of value-based classes where an identity class is expected. + javac.opt.Xdoclint=\ Enable recommended checks for problems in javadoc comments # L10N: do not localize: all none diff --git a/test/langtools/tools/javac/diags/examples.not-yet.txt b/test/langtools/tools/javac/diags/examples.not-yet.txt index 39bcec336544e..f54ef1c4a8bb2 100644 --- a/test/langtools/tools/javac/diags/examples.not-yet.txt +++ b/test/langtools/tools/javac/diags/examples.not-yet.txt @@ -222,3 +222,5 @@ compiler.warn.restricted.method # Pending removal compiler.note.implicit.annotation.processing compiler.warn.proc.use.proc.or.implicit + +compiler.warn.attempt.to.synchronize.on.instance.of.value.based.class2 # synchronization and identity are aliases diff --git a/test/langtools/tools/javac/diags/examples/RequiresIdentity.java b/test/langtools/tools/javac/diags/examples/RequiresIdentity.java new file mode 100644 index 0000000000000..005de46ab7c82 --- /dev/null +++ b/test/langtools/tools/javac/diags/examples/RequiresIdentity.java @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +// key: compiler.warn.attempt.to.use.value.based.where.identity.expected +// options: -Xlint:identity --add-exports java.base/jdk.internal=ALL-UNNAMED + +class RequiresIdentity<@jdk.internal.RequiresIdentity T> { + RequiresIdentity field; // should warn +} diff --git a/test/langtools/tools/javac/lint/ExternalAbuseOfVbc.java b/test/langtools/tools/javac/lint/ExternalAbuseOfVbc.java index 8b26d6af3345a..d68932774a276 100644 --- a/test/langtools/tools/javac/lint/ExternalAbuseOfVbc.java +++ b/test/langtools/tools/javac/lint/ExternalAbuseOfVbc.java @@ -4,12 +4,11 @@ * @summary lint should warn when an instance of a value based class is synchronized upon * @compile/fail/ref=ExternalAbuseOfVbc.out -XDrawDiagnostics -Werror -Xlint ExternalAbuseOfVbc.java * @compile/fail/ref=ExternalAbuseOfVbc.out -XDrawDiagnostics -Werror -Xlint:all ExternalAbuseOfVbc.java - * @compile/fail/ref=ExternalAbuseOfVbc.out -XDrawDiagnostics -Werror -Xlint:synchronized ExternalAbuseOfVbc.java + * @compile/fail/ref=ExternalAbuseOfVbc.out -XDrawDiagnostics -Werror -Xlint:synchronization ExternalAbuseOfVbc.java * @compile/fail/ref=ExternalAbuseOfVbc.out -XDrawDiagnostics -Werror -Xlint:identity ExternalAbuseOfVbc.java - * @compile/fail/ref=ExternalAbuseOfVbc.out --release 16 -XDrawDiagnostics -Werror -Xlint:synchronized ExternalAbuseOfVbc.java + * @compile/fail/ref=ExternalAbuseOfVbc.out --release 16 -XDrawDiagnostics -Werror -Xlint:synchronization ExternalAbuseOfVbc.java * @compile/fail/ref=ExternalAbuseOfVbc.out --release 16 -XDrawDiagnostics -Werror -Xlint:identity ExternalAbuseOfVbc.java - * @compile/ref=LintModeOffAbuseOfVbc.out -XDrawDiagnostics -Werror -Xlint:-synchronized ExternalAbuseOfVbc.java - * @compile/ref=LintModeOffAbuseOfVbc.out -XDrawDiagnostics -Werror -Xlint:-identity ExternalAbuseOfVbc.java + * @compile/ref=LintModeOffAbuseOfVbc.out -XDrawDiagnostics -Werror -Xlint:-synchronization,-identity ExternalAbuseOfVbc.java */ public final class ExternalAbuseOfVbc { diff --git a/test/langtools/tools/javac/lint/ExternalAbuseOfVbc.out b/test/langtools/tools/javac/lint/ExternalAbuseOfVbc.out index b042a2c213979..d03e33e06d50c 100644 --- a/test/langtools/tools/javac/lint/ExternalAbuseOfVbc.out +++ b/test/langtools/tools/javac/lint/ExternalAbuseOfVbc.out @@ -1,4 +1,4 @@ -ExternalAbuseOfVbc.java:19:13: compiler.warn.attempt.to.synchronize.on.instance.of.value.based.class +ExternalAbuseOfVbc.java:21:13: compiler.warn.attempt.to.synchronize.on.instance.of.value.based.class - compiler.err.warnings.and.werror 1 error 1 warning From ed64f22c71e4aaaed8375109bb769e609f153612 Mon Sep 17 00:00:00 2001 From: Vicente Romero Date: Mon, 21 Apr 2025 21:39:28 -0400 Subject: [PATCH 04/34] removing the REQUIRES_IDENTITY flag --- .../share/classes/com/sun/tools/javac/code/Flags.java | 8 +------- .../share/classes/com/sun/tools/javac/comp/Annotate.java | 6 ------ .../share/classes/com/sun/tools/javac/comp/Attr.java | 4 ---- .../classes/com/sun/tools/javac/jvm/ClassReader.java | 3 --- 4 files changed, 1 insertion(+), 20 deletions(-) diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Flags.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Flags.java index 639ae92fdda22..c2bb8c673ebc9 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Flags.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Flags.java @@ -409,11 +409,6 @@ public static EnumSet asFlagSet(long flags) { */ public static final long NON_SEALED = 1L<<63; // ClassSymbols - /** - * Flag to indicate that the type should not be value based - */ - public static final long REQUIRES_IDENTITY = 1<<20; // TypeSymbols - /** * Describe modifier flags as they might appear in source code, i.e., * separated by spaces and in the order suggested by JLS 8.1.1. @@ -565,8 +560,7 @@ public enum Flag { public String toString() { return "non-sealed"; } - }, - REQUIRES_IDENTITY(Flags.REQUIRES_IDENTITY) + } ; Flag(long flag) { diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Annotate.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Annotate.java index 35f08d97b865b..35672b2b1bc93 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Annotate.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Annotate.java @@ -384,12 +384,6 @@ private void annotateNow(Symbol toAnnotate, && types.isSameType(c.type, syms.restrictedType)) { toAnnotate.flags_field |= Flags.RESTRICTED; } - - if (!c.type.isErroneous() - && (toAnnotate.kind == TYP || toAnnotate.kind == VAR) - && types.isSameType(c.type, syms.requiresIdentityType)) { - toAnnotate.flags_field |= Flags.REQUIRES_IDENTITY; - } } List buf = List.nil(); diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java index 8baf5608527de..1d785f541a9a2 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java @@ -784,10 +784,6 @@ KindSelector attribArgs(KindSelector initialKind, List trees, Env< return kind; } - private boolean requiresIdentity(Type t) { - return t != null && t.tsym != null && (t.tsym.flags() & REQUIRES_IDENTITY) != 0; - } - private boolean requiresIdentity(VarSymbol vsym) { if (vsym != null) { SymbolMetadata sm = vsym.getMetadata(); diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassReader.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassReader.java index f8f45096cbce0..38b66cbe691d2 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassReader.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassReader.java @@ -1630,9 +1630,6 @@ void attachTypeAnnotations(final Symbol sym) { for (int i = 0; i < numAttributes; i++) { TypeAnnotationProxy proxy = readTypeAnnotation(); proxies.append(proxy); - if (proxy.compound.type.tsym == syms.requiresIdentityType.tsym) { - sym.flags_field |= REQUIRES_IDENTITY; - } } annotate.normal(new TypeAnnotationCompleter(sym, proxies.toList())); } From e6c17f7bb9c6652ad5315288632f17c3050aac93 Mon Sep 17 00:00:00 2001 From: Vicente Romero Date: Mon, 21 Apr 2025 21:44:04 -0400 Subject: [PATCH 05/34] removing requiresIdentityInternalType --- .../share/classes/com/sun/tools/javac/code/Symtab.java | 2 -- .../share/classes/com/sun/tools/javac/comp/Attr.java | 2 ++ 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Symtab.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Symtab.java index 287d7b6a581b6..d8c2308279ba8 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Symtab.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Symtab.java @@ -229,7 +229,6 @@ public static Symtab instance(Context context) { public final Type valueBasedType; public final Type valueBasedInternalType; public final Type requiresIdentityType; - public final Type requiresIdentityInternalType; public final Type classDescType; public final Type enumDescType; public final Type ioType; @@ -614,7 +613,6 @@ public R accept(ElementVisitor v, P p) { valueBasedType = enterClass("jdk.internal.ValueBased"); valueBasedInternalType = enterSyntheticAnnotation("jdk.internal.ValueBased+Annotation"); requiresIdentityType = enterClass("jdk.internal.RequiresIdentity"); - requiresIdentityInternalType = enterSyntheticAnnotation("jdk.internal.RequiresIdentity+Annotation"); classDescType = enterClass("java.lang.constant.ClassDesc"); enumDescType = enterClass("java.lang.Enum$EnumDesc"); ioType = enterClass("java.io.IO"); diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java index 1d785f541a9a2..9c55775fcdd6b 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java @@ -784,6 +784,8 @@ KindSelector attribArgs(KindSelector initialKind, List trees, Env< return kind; } + /* vsym is expected to be a method argument + */ private boolean requiresIdentity(VarSymbol vsym) { if (vsym != null) { SymbolMetadata sm = vsym.getMetadata(); From d40e28573993c86f1044f5973070a951a136f8b5 Mon Sep 17 00:00:00 2001 From: Vicente Romero Date: Mon, 21 Apr 2025 22:16:06 -0400 Subject: [PATCH 06/34] removing unnecessary changes --- .../share/classes/com/sun/tools/javac/code/Flags.java | 3 +-- .../share/classes/com/sun/tools/javac/jvm/ClassReader.java | 6 ++---- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Flags.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Flags.java index c2bb8c673ebc9..53abf99ed7d2b 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Flags.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Flags.java @@ -560,8 +560,7 @@ public enum Flag { public String toString() { return "non-sealed"; } - } - ; + }; Flag(long flag) { this.value = flag; diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassReader.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassReader.java index 38b66cbe691d2..6f55903f489ab 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassReader.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassReader.java @@ -1627,10 +1627,8 @@ void attachTypeAnnotations(final Symbol sym) { int numAttributes = nextChar(); if (numAttributes != 0) { ListBuffer proxies = new ListBuffer<>(); - for (int i = 0; i < numAttributes; i++) { - TypeAnnotationProxy proxy = readTypeAnnotation(); - proxies.append(proxy); - } + for (int i = 0; i < numAttributes; i++) + proxies.append(readTypeAnnotation()); annotate.normal(new TypeAnnotationCompleter(sym, proxies.toList())); } } From 5a0b6bfc42ab2d3b4e3789aff5086ecbaf129b9d Mon Sep 17 00:00:00 2001 From: Vicente Romero Date: Tue, 22 Apr 2025 13:14:49 -0400 Subject: [PATCH 07/34] fixing bug --- .../classes/com/sun/tools/javac/comp/Attr.java | 15 ++++++++------- .../tools/javac/lint/RequiresIdentityHelper.java | 1 + .../tools/javac/lint/RequiresIdentityTest.java | 5 ++++- .../tools/javac/lint/RequiresIdentityTest.out | 15 ++++++++------- 4 files changed, 21 insertions(+), 15 deletions(-) diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java index 9c55775fcdd6b..b6468720d2bd0 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java @@ -2573,6 +2573,7 @@ public void visitApply(JCMethodInvocation tree) { methName == names._this || methName == names._super; ListBuffer argtypesBuf = new ListBuffer<>(); + Symbol msym = null; if (isConstructorCall) { // Attribute arguments, yielding list of argument types. @@ -2639,17 +2640,17 @@ public void visitApply(JCMethodInvocation tree) { boolean selectSuperPrev = localEnv.info.selectSuper; localEnv.info.selectSuper = true; localEnv.info.pendingResolutionPhase = null; - Symbol sym = rs.resolveConstructor( + msym = rs.resolveConstructor( tree.meth.pos(), localEnv, site, argtypes, typeargtypes); localEnv.info.selectSuper = selectSuperPrev; // Set method symbol to resolved constructor... - TreeInfo.setSymbol(tree.meth, sym); + TreeInfo.setSymbol(tree.meth, msym); // ...and check that it is legal in the current context. // (this will also set the tree's type) Type mpt = newMethodTemplate(resultInfo.pt, argtypes, typeargtypes); - checkId(tree.meth, site, sym, localEnv, + checkId(tree.meth, site, msym, localEnv, new ResultInfo(kind, mpt)); } else if (site.hasTag(ERROR) && tree.meth.hasTag(SELECT)) { attribExpr(((JCFieldAccess) tree.meth).selected, localEnv, site); @@ -2678,7 +2679,7 @@ public void visitApply(JCMethodInvocation tree) { Type qualifier = (tree.meth.hasTag(SELECT)) ? ((JCFieldAccess) tree.meth).selected.type : env.enclClass.sym.type; - Symbol msym = TreeInfo.symbol(tree.meth); + msym = TreeInfo.symbol(tree.meth); restype = adjustMethodReturnType(msym, qualifier, methName, argtypes, restype); chk.checkRefTypes(tree.typeargs, typeargtypes); @@ -2687,14 +2688,14 @@ public void visitApply(JCMethodInvocation tree) { // current context. Also, capture the return type Type capturedRes = resultInfo.checkContext.inferenceContext().cachedCapture(tree, restype, true); result = check(tree, capturedRes, KindSelector.VAL, resultInfo); - checkIfRequireIdentity(tree, msym); } + checkIfRequireIdentity(tree, msym); chk.validate(tree.typeargs, localEnv); } //where void checkIfRequireIdentity(JCMethodInvocation tree, Symbol sym) { List argExps = tree.args; - if (sym instanceof MethodSymbol ms && ms.params != null) { + if (!argExps.isEmpty() && sym instanceof MethodSymbol ms && ms.params != null) { VarSymbol lastParam = ms.params.head; for (VarSymbol param: ms.params) { if (requiresIdentity(param) && types.isValueBased(argExps.head.type)) { @@ -2703,7 +2704,7 @@ void checkIfRequireIdentity(JCMethodInvocation tree, Symbol sym) { lastParam = param; argExps = argExps.tail; } - while (argExps != null && !argExps.isEmpty()) { + while (argExps != null && !argExps.isEmpty() && lastParam != null) { if (requiresIdentity(lastParam) && types.isValueBased(argExps.head.type)) { env.info.lint.logIfEnabled(argExps.head.pos(), LintWarnings.AttemptToUseValueBasedWhereIdentityExpected); } diff --git a/test/langtools/tools/javac/lint/RequiresIdentityHelper.java b/test/langtools/tools/javac/lint/RequiresIdentityHelper.java index e07fd10ffa2bd..afe3fb4968d03 100644 --- a/test/langtools/tools/javac/lint/RequiresIdentityHelper.java +++ b/test/langtools/tools/javac/lint/RequiresIdentityHelper.java @@ -4,6 +4,7 @@ public class RequiresIdentityHelper<@jdk.internal.RequiresIdentity T> { public RequiresIdentityHelper() {} + public RequiresIdentityHelper(@jdk.internal.RequiresIdentity Object o) {} } class RequiresIdentity2 { diff --git a/test/langtools/tools/javac/lint/RequiresIdentityTest.java b/test/langtools/tools/javac/lint/RequiresIdentityTest.java index 1e668abef0e9e..62912bd88d2a2 100644 --- a/test/langtools/tools/javac/lint/RequiresIdentityTest.java +++ b/test/langtools/tools/javac/lint/RequiresIdentityTest.java @@ -7,7 +7,7 @@ package java.lang; -public class RequiresIdentityTest { +public class RequiresIdentityTest extends RequiresIdentityHelper { class Box {} RequiresIdentityHelper field; // should warn @@ -16,6 +16,9 @@ class Box {} Box> field4; // should warn public RequiresIdentityTest() {} + public RequiresIdentityTest(Integer i) { + super(i); // should warn + } void test(RequiresIdentity2 ri, Integer i) { RequiresIdentityHelper localVar; // should warn diff --git a/test/langtools/tools/javac/lint/RequiresIdentityTest.out b/test/langtools/tools/javac/lint/RequiresIdentityTest.out index a53484680e8b9..34db95bdfee11 100644 --- a/test/langtools/tools/javac/lint/RequiresIdentityTest.out +++ b/test/langtools/tools/javac/lint/RequiresIdentityTest.out @@ -2,12 +2,13 @@ RequiresIdentityTest.java:13:27: compiler.warn.attempt.to.use.value.based.where. RequiresIdentityTest.java:14:36: compiler.warn.attempt.to.use.value.based.where.identity.expected RequiresIdentityTest.java:15:8: compiler.warn.attempt.to.use.value.based.where.identity.expected RequiresIdentityTest.java:16:8: compiler.warn.attempt.to.use.value.based.where.identity.expected -RequiresIdentityTest.java:21:31: compiler.warn.attempt.to.use.value.based.where.identity.expected -RequiresIdentityTest.java:22:40: compiler.warn.attempt.to.use.value.based.where.identity.expected -RequiresIdentityTest.java:24:16: compiler.warn.attempt.to.use.value.based.where.identity.expected -RequiresIdentityTest.java:25:16: compiler.warn.attempt.to.use.value.based.where.identity.expected -RequiresIdentityTest.java:25:19: compiler.warn.attempt.to.use.value.based.where.identity.expected -RequiresIdentityTest.java:26:15: compiler.warn.attempt.to.use.value.based.where.identity.expected +RequiresIdentityTest.java:20:15: compiler.warn.attempt.to.use.value.based.where.identity.expected +RequiresIdentityTest.java:24:31: compiler.warn.attempt.to.use.value.based.where.identity.expected +RequiresIdentityTest.java:25:40: compiler.warn.attempt.to.use.value.based.where.identity.expected +RequiresIdentityTest.java:27:16: compiler.warn.attempt.to.use.value.based.where.identity.expected +RequiresIdentityTest.java:28:16: compiler.warn.attempt.to.use.value.based.where.identity.expected +RequiresIdentityTest.java:28:19: compiler.warn.attempt.to.use.value.based.where.identity.expected +RequiresIdentityTest.java:29:15: compiler.warn.attempt.to.use.value.based.where.identity.expected - compiler.err.warnings.and.werror 1 error -10 warnings +11 warnings From 434dda71256307a51891d714680eeb13835a2a68 Mon Sep 17 00:00:00 2001 From: Vicente Romero Date: Tue, 22 Apr 2025 13:26:23 -0400 Subject: [PATCH 08/34] only do extra analysis if lint warning enable --- .../com/sun/tools/javac/comp/Attr.java | 37 ++++++++++--------- 1 file changed, 20 insertions(+), 17 deletions(-) diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java index b6468720d2bd0..7d830aa70a02b 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java @@ -784,22 +784,6 @@ KindSelector attribArgs(KindSelector initialKind, List trees, Env< return kind; } - /* vsym is expected to be a method argument - */ - private boolean requiresIdentity(VarSymbol vsym) { - if (vsym != null) { - SymbolMetadata sm = vsym.getMetadata(); - if (sm != null) { - for (Attribute.Compound ca: sm.getDeclarationAttributes()) { - if (ca.type.tsym == syms.requiresIdentityType.tsym) { - return true; - } - } - } - } - return false; - } - /** Attribute a type argument list, returning a list of types. * Caller is responsible for calling checkRefTypes. */ @@ -1346,6 +1330,7 @@ public void visitVarDef(JCVariableDecl tree) { } if (tree.vartype != null && tree.vartype.type != null && + env.info.lint.isEnabled(LintCategory.IDENTITY) && types.needsRequiresIdentityWarning(tree.vartype.type)) { env.info.lint.logIfEnabled(tree.vartype.pos(), LintWarnings.AttemptToUseValueBasedWhereIdentityExpected); } @@ -2689,7 +2674,9 @@ public void visitApply(JCMethodInvocation tree) { Type capturedRes = resultInfo.checkContext.inferenceContext().cachedCapture(tree, restype, true); result = check(tree, capturedRes, KindSelector.VAL, resultInfo); } - checkIfRequireIdentity(tree, msym); + if (env.info.lint.isEnabled(LintCategory.IDENTITY)) { + checkIfRequireIdentity(tree, msym); + } chk.validate(tree.typeargs, localEnv); } //where @@ -2713,6 +2700,22 @@ void checkIfRequireIdentity(JCMethodInvocation tree, Symbol sym) { } } + /* vsym is expected to be a method argument + */ + private boolean requiresIdentity(VarSymbol vsym) { + if (vsym != null) { + SymbolMetadata sm = vsym.getMetadata(); + if (sm != null) { + for (Attribute.Compound ca: sm.getDeclarationAttributes()) { + if (ca.type.tsym == syms.requiresIdentityType.tsym) { + return true; + } + } + } + } + return false; + } + Type adjustMethodReturnType(Symbol msym, Type qualifierType, Name methodName, List argtypes, Type restype) { if (msym != null && (msym.owner == syms.objectType.tsym || msym.owner.isInterface()) && From 9f0407ab8570474a61ce5ef298ed48babc0d82e4 Mon Sep 17 00:00:00 2001 From: Vicente Romero Date: Tue, 22 Apr 2025 14:04:45 -0400 Subject: [PATCH 09/34] minor chantes to CreateSymbol --- .../build/tools/symbolgenerator/CreateSymbols.java | 13 ++++++++++++- .../classes/com/sun/tools/javac/code/Symtab.java | 2 ++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/make/langtools/src/classes/build/tools/symbolgenerator/CreateSymbols.java b/make/langtools/src/classes/build/tools/symbolgenerator/CreateSymbols.java index edb0ea9483af5..854a7b144fa17 100644 --- a/make/langtools/src/classes/build/tools/symbolgenerator/CreateSymbols.java +++ b/make/langtools/src/classes/build/tools/symbolgenerator/CreateSymbols.java @@ -304,13 +304,18 @@ public void createSymbols(String ctDescriptionFileExtra, String ctDescriptionFil "Ljdk/internal/ValueBased;"; private static final String VALUE_BASED_ANNOTATION_INTERNAL = "Ljdk/internal/ValueBased+Annotation;"; + private static final String REQUIRES_IDENTITY_ANNOTATION = + "Ljdk/internal/RequieresIdentity;"; + private static final String REQUIRES_IDENTITY_ANNOTATION_INTERNAL = + "Ljdk/internal/RequiresIdentity+Annotation;"; public static final Set HARDCODED_ANNOTATIONS = new HashSet<>( List.of("Ljdk/Profile+Annotation;", "Lsun/Proprietary+Annotation;", PREVIEW_FEATURE_ANNOTATION_OLD, PREVIEW_FEATURE_ANNOTATION_NEW, VALUE_BASED_ANNOTATION, - RESTRICTED_ANNOTATION)); + RESTRICTED_ANNOTATION, + REQUIRES_IDENTITY_ANNOTATION)); private void stripNonExistentAnnotations(LoadDescriptions data) { Set allClasses = data.classes.name2Class.keySet(); @@ -1021,6 +1026,12 @@ private Annotation createAnnotation(AnnotationDescription desc) { annotationType = VALUE_BASED_ANNOTATION_INTERNAL; } + if (REQUIRES_IDENTITY_ANNOTATION.equals(annotationType)) { + //the non-public RequiresIdentity annotation will not be available in ct.sym, + //replace with purely synthetic javac-internal annotation: + annotationType = REQUIRES_IDENTITY_ANNOTATION_INTERNAL; + } + if (RESTRICTED_ANNOTATION.equals(annotationType)) { //the non-public Restricted annotation will not be available in ct.sym, //replace with purely synthetic javac-internal annotation: diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Symtab.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Symtab.java index d8c2308279ba8..287d7b6a581b6 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Symtab.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Symtab.java @@ -229,6 +229,7 @@ public static Symtab instance(Context context) { public final Type valueBasedType; public final Type valueBasedInternalType; public final Type requiresIdentityType; + public final Type requiresIdentityInternalType; public final Type classDescType; public final Type enumDescType; public final Type ioType; @@ -613,6 +614,7 @@ public R accept(ElementVisitor v, P p) { valueBasedType = enterClass("jdk.internal.ValueBased"); valueBasedInternalType = enterSyntheticAnnotation("jdk.internal.ValueBased+Annotation"); requiresIdentityType = enterClass("jdk.internal.RequiresIdentity"); + requiresIdentityInternalType = enterSyntheticAnnotation("jdk.internal.RequiresIdentity+Annotation"); classDescType = enterClass("java.lang.constant.ClassDesc"); enumDescType = enterClass("java.lang.Enum$EnumDesc"); ioType = enterClass("java.io.IO"); From 2697858b493d5bb7d6b3aedcecebb1782ad79cd6 Mon Sep 17 00:00:00 2001 From: Vicente Romero Date: Wed, 23 Apr 2025 09:44:03 -0400 Subject: [PATCH 10/34] additional coverage for other uses of types --- .../com/sun/tools/javac/code/Types.java | 61 +--------- .../com/sun/tools/javac/comp/Attr.java | 65 ++++++++-- .../com/sun/tools/javac/comp/Check.java | 112 ++++++++++++++++++ .../javac/lint/RequiresIdentityHelper.java | 18 ++- .../javac/lint/RequiresIdentityTest.java | 50 +++++++- .../tools/javac/lint/RequiresIdentityTest.out | 42 +++++-- 6 files changed, 259 insertions(+), 89 deletions(-) diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Types.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Types.java index 86403eebffdbf..597cf5a6f367a 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Types.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Types.java @@ -51,6 +51,7 @@ import com.sun.tools.javac.comp.Env; import com.sun.tools.javac.comp.LambdaToMethod; import com.sun.tools.javac.jvm.ClassFile; +import com.sun.tools.javac.resources.CompilerProperties; import com.sun.tools.javac.util.*; import static com.sun.tools.javac.code.BoundKind.*; @@ -5331,66 +5332,6 @@ public Type constantType(LoadableConstant c) { } // - // - public boolean isValueBased(Type t) { - return t != null && t.tsym != null && (t.tsym.flags() & VALUE_BASED) != 0; - } - - public boolean needsRequiresIdentityWarning(Type t) { - RequiresIdentityVisitor requiresIdentityVisitor = new RequiresIdentityVisitor(); - requiresIdentityVisitor.visit(t); - return requiresIdentityVisitor.requiresWarning; - } - - // where - class RequiresIdentityVisitor extends Types.UnaryVisitor { - boolean requiresWarning = false; - - @Override - public Void visitType(Type t, Void _unused) { - return null; - } - - @Override - public Void visitWildcardType(WildcardType t, Void _unused) { - return visit(t.type); - } - - @Override - public Void visitArrayType(ArrayType t, Void _unused) { - return visit(t.elemtype); - } - - @Override - public Void visitClassType(ClassType t, Void _unused) { - if (t != null && t.tsym != null) { - SymbolMetadata sm = t.tsym.getMetadata(); - if (sm != null) { - for (Attribute.TypeCompound ta: sm.getTypeAttributes()) { - if (ta.type.tsym == syms.requiresIdentityType.tsym) { - TypeAnnotationPosition tap = ta.position; - if (tap.type == TargetType.CLASS_TYPE_PARAMETER) { - int index = tap.parameter_index; - // ClassType::getTypeArguments() can be empty for raw types - Type tparam = t.getTypeArguments().isEmpty() ? null : t.getTypeArguments().get(index); - if (tparam != null && isValueBased(tparam)) { - requiresWarning = true; - return null; - } - } - } - } - } - } - visit(t.getEnclosingType()); - for (Type targ : t.getTypeArguments()) { - visit(targ); - } - return null; - } - } - // - public void newRound() { descCache._map.clear(); isDerivedRawCache.clear(); diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java index 7d830aa70a02b..5ff4f6f8c1908 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java @@ -1009,6 +1009,11 @@ public void visitMethodDef(JCMethodDecl tree) { localEnv.info.lint = lint; attribStats(tree.typarams, localEnv); + if (env.info.lint.isEnabled(LintCategory.IDENTITY)) { + for (JCTypeParameter tp : tree.typarams) { + chk.checkIfIdentityIsRequired(tp.pos(), tp.type, env); + } + } // If we override any other methods, check that we do so properly. // JLS ??? @@ -1047,8 +1052,10 @@ public void visitMethodDef(JCMethodDecl tree) { chk.validate(tree.typarams, localEnv); // Check that result type is well-formed. - if (tree.restype != null && !tree.restype.type.hasTag(VOID)) + if (tree.restype != null && !tree.restype.type.hasTag(VOID)) { chk.validate(tree.restype, localEnv); + chk.checkIfIdentityIsRequired(tree.restype.pos(), tree.restype.type, env); + } // Check that receiver type is well-formed. if (tree.recvparam != null) { @@ -1328,11 +1335,8 @@ public void visitVarDef(JCVariableDecl tree) { log.error(tree, Errors.IllegalRecordComponentName(v)); } } - if (tree.vartype != null && - tree.vartype.type != null && - env.info.lint.isEnabled(LintCategory.IDENTITY) && - types.needsRequiresIdentityWarning(tree.vartype.type)) { - env.info.lint.logIfEnabled(tree.vartype.pos(), LintWarnings.AttemptToUseValueBasedWhereIdentityExpected); + if (notACanonicalConstructorArg(v)) { + chk.checkIfIdentityIsRequired(tree.vartype.pos(), tree.vartype.type, env); } } finally { @@ -1340,6 +1344,15 @@ public void visitVarDef(JCVariableDecl tree) { } } + /* we don't want to warn twice so if this variable is a compiler generated parameter to + * a canonical record constructor, we don't want to issue a warning as we will warn the + * corresponding compiler generated private record field + */ + boolean notACanonicalConstructorArg(VarSymbol vs) { + return (vs.flags_field & RECORD) == 0 || + (vs.flags_field & ~(Flags.PARAMETER | RECORD | GENERATED_MEMBER)) != 0; + } + private void doQueueScanTreeAndTypeAnnotateForVarInit(JCVariableDecl tree, Env env) { if (tree.init != null && (tree.mods.flags & Flags.FIELD_INIT_TYPE_ANNOTATIONS_QUEUED) == 0 && @@ -1951,7 +1964,7 @@ private Symbol enumConstant(JCTree tree, Type enumType) { public void visitSynchronized(JCSynchronized tree) { chk.checkRefType(tree.pos(), attribExpr(tree.lock, env)); - if (types.isValueBased(tree.lock.type)) { + if (chk.isValueBased(tree.lock.type)) { if (env.info.lint.isEnabled(LintCategory.SYNCHRONIZATION)) { env.info.lint.logIfEnabled(tree.pos(), LintWarnings.AttemptToSynchronizeOnInstanceOfValueBasedClass); } else { @@ -2685,19 +2698,25 @@ void checkIfRequireIdentity(JCMethodInvocation tree, Symbol sym) { if (!argExps.isEmpty() && sym instanceof MethodSymbol ms && ms.params != null) { VarSymbol lastParam = ms.params.head; for (VarSymbol param: ms.params) { - if (requiresIdentity(param) && types.isValueBased(argExps.head.type)) { + if (requiresIdentity(param) && chk.isValueBased(argExps.head.type)) { env.info.lint.logIfEnabled(argExps.head.pos(), LintWarnings.AttemptToUseValueBasedWhereIdentityExpected); } lastParam = param; argExps = argExps.tail; } while (argExps != null && !argExps.isEmpty() && lastParam != null) { - if (requiresIdentity(lastParam) && types.isValueBased(argExps.head.type)) { + if (requiresIdentity(lastParam) && chk.isValueBased(argExps.head.type)) { env.info.lint.logIfEnabled(argExps.head.pos(), LintWarnings.AttemptToUseValueBasedWhereIdentityExpected); } argExps = argExps.tail; } } + ListBuffer typeParamTypes = new ListBuffer<>(); + for (JCExpression targ: tree.typeargs) { + chk.checkIfIdentityIsRequired(targ.pos(), targ.type, env); + typeParamTypes.add(targ.type); + } + chk.checkIfTypeParamsRequiresIdentity(sym.getMetadata(), typeParamTypes.toList(), tree.pos(), env.info.lint, true); } /* vsym is expected to be a method argument @@ -2834,6 +2853,8 @@ public void visitNewClass(final JCNewClass tree) { env.info.isAnonymousNewClass = false; } + chk.checkIfIdentityIsRequired(tree.clazz.pos(), clazztype, env); + clazztype = chk.checkDiamond(tree, clazztype); chk.validate(clazz, localEnv); if (tree.encl != null) { @@ -3824,6 +3845,14 @@ public void visitReference(final JCMemberReference that) { checkReferenceCompatible(that, desc, refType, resultInfo.checkContext, isSpeculativeRound); if (!isSpeculativeRound) { checkAccessibleTypes(that, localEnv, resultInfo.checkContext.inferenceContext(), desc, currentTarget); + chk.checkIfIdentityIsRequired(that.expr.pos(), currentTarget, localEnv); + if (that.typeargs != null) { + ListBuffer lbTypes = new ListBuffer<>(); + for (JCExpression targ : that.typeargs) { + lbTypes.add(targ.type); + } + chk.checkIfTypeParamsRequiresIdentity(refSym.getMetadata(), lbTypes.toList(), that.pos(), localEnv.info.lint, true); + } } result = check(that, currentTarget, KindSelector.VAL, resultInfo); } catch (Types.FunctionDescriptorLookupError ex) { @@ -4128,6 +4157,7 @@ public void visitBinary(JCBinary tree) { public void visitTypeCast(final JCTypeCast tree) { Type clazztype = attribType(tree.clazz, env); chk.validate(tree.clazz, env, false); + chk.checkIfIdentityIsRequired(tree.clazz.pos(), clazztype, env); //a fresh environment is required for 292 inference to work properly --- //see Infer.instantiatePolymorphicSignatureInstance() Env localEnv = env.dup(tree); @@ -4269,6 +4299,9 @@ public void visitBindingPattern(JCBindingPattern tree) { } else { matchBindings = new MatchBindings(List.of(v), List.nil()); } + if (tree.var.vartype != null) { + chk.checkIfIdentityIsRequired(tree.var.vartype.pos(), tree.var.vartype.type, env); + } } @Override @@ -5566,6 +5599,17 @@ void attribClass(ClassSymbol c) throws CompletionFailure { log.error(env.tree.pos(), Errors.EnumTypesNotExtensible); } + if (st != null && + // no need to recheck j.l.Object, shortcut, + st.tsym != syms.objectType.tsym && + // this one could be null, no explicit extends + env.enclClass.extending != null) { + chk.checkIfIdentityIsRequired(env.enclClass.extending.pos(), st, env); + } + for (JCExpression intrface: env.enclClass.implementing) { + chk.checkIfIdentityIsRequired(intrface.pos(), intrface.type, env); + } + if (rs.isSerializable(c.type)) { env.info.isSerializable = true; } @@ -5621,6 +5665,9 @@ private void attribClassBody(Env env, ClassSymbol c) { chk.validate(tree.extending, env); chk.validate(tree.implementing, env); } + for (JCTypeParameter tp : tree.typarams) { + chk.checkIfIdentityIsRequired(tp.pos(), tp.type, env); + } c.markAbstractIfNeeded(types); diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Check.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Check.java index 10f0b22268414..52140ee9072ee 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Check.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Check.java @@ -46,6 +46,7 @@ import com.sun.tools.javac.code.Source.Feature; import com.sun.tools.javac.comp.Annotate.AnnotationTypeMetadata; import com.sun.tools.javac.jvm.*; +import com.sun.tools.javac.resources.CompilerProperties; import com.sun.tools.javac.resources.CompilerProperties.Errors; import com.sun.tools.javac.resources.CompilerProperties.Fragments; import com.sun.tools.javac.resources.CompilerProperties.Warnings; @@ -5667,4 +5668,115 @@ private Void runUnderLint(E symbol, JCClassDecl p, BiConsume } + /** Check if a type required an identity class + */ + boolean checkIfIdentityIsRequired(DiagnosticPosition pos, Type t, Env env) { + if (t != null && + env.info.lint != null && + env.info.lint.isEnabled(LintCategory.IDENTITY) && + needsRequiresIdentityWarning(t)) { + env.info.lint.logIfEnabled(pos, LintWarnings.AttemptToUseValueBasedWhereIdentityExpected); + return true; + } + return false; + } + + public boolean isValueBased(Type t) { + return t != null && t.tsym != null && (t.tsym.flags() & VALUE_BASED) != 0; + } + + public boolean needsRequiresIdentityWarning(Type t) { + if (t != null) { + RequiresIdentityVisitor requiresIdentityVisitor = new RequiresIdentityVisitor(); + // we need to avoid self referencing type vars or captures + Set seen = new HashSet<>(); + requiresIdentityVisitor.visit(t, seen); + return requiresIdentityVisitor.requiresWarning; + } + return false; + } + + public boolean checkIfTypeParamsRequiresIdentity(SymbolMetadata sm, List typeParams) { + return checkIfTypeParamsRequiresIdentity(sm, typeParams, null, null, false); + } + + public boolean checkIfTypeParamsRequiresIdentity(SymbolMetadata sm, + List typeParams, + JCDiagnostic.DiagnosticPosition pos, + Lint lint, + boolean warn) { + boolean result = false; + if (sm != null) { + for (Attribute.TypeCompound ta: sm.getTypeAttributes()) { + if (ta.type.tsym == syms.requiresIdentityType.tsym) { + TypeAnnotationPosition tap = ta.position; + if (tap.type == TargetType.CLASS_TYPE_PARAMETER || tap.type == TargetType.METHOD_TYPE_PARAMETER) { + int index = tap.parameter_index; + // ClassType::getTypeArguments() can be empty for raw types + Type tparam = typeParams.isEmpty() ? null : typeParams.get(index); + if (tparam != null && isValueBased(tparam)) { + if (warn) { + lint.logIfEnabled(pos, + CompilerProperties.LintWarnings.AttemptToUseValueBasedWhereIdentityExpected); + } + result = true; + } + } + } + } + } + return result; + } + + // where + class RequiresIdentityVisitor extends Types.SimpleVisitor> { + boolean requiresWarning = false; + + @Override + public Void visitType(Type t, Set seen) { + return null; + } + + @Override + public Void visitWildcardType(WildcardType t, Set seen) { + return visit(t.type, seen); + } + + @Override + public Void visitTypeVar(TypeVar t, Set seen) { + if (seen.add(t)) { + visit(t.getUpperBound(), seen); + } + return null; + } + + @Override + public Void visitCapturedType(CapturedType t, Set seen) { + if (seen.add(t)) { + visit(t.getUpperBound(), seen); + visit(t.getLowerBound(), seen); + } + return null; + } + + @Override + public Void visitArrayType(ArrayType t, Set seen) { + return visit(t.elemtype, seen); + } + + @Override + public Void visitClassType(ClassType t, Set seen) { + if (t != null && t.tsym != null) { + if (checkIfTypeParamsRequiresIdentity(t.tsym.getMetadata(), t.getTypeArguments())) { + requiresWarning = true; + return null; + } + } + visit(t.getEnclosingType(), seen); + for (Type targ : t.getTypeArguments()) { + visit(targ, seen); + } + return null; + } + } } diff --git a/test/langtools/tools/javac/lint/RequiresIdentityHelper.java b/test/langtools/tools/javac/lint/RequiresIdentityHelper.java index afe3fb4968d03..22bd2dfb4e4e4 100644 --- a/test/langtools/tools/javac/lint/RequiresIdentityHelper.java +++ b/test/langtools/tools/javac/lint/RequiresIdentityHelper.java @@ -5,11 +5,17 @@ public class RequiresIdentityHelper<@jdk.internal.RequiresIdentity T> { public RequiresIdentityHelper() {} public RequiresIdentityHelper(@jdk.internal.RequiresIdentity Object o) {} -} -class RequiresIdentity2 { - public RequiresIdentity2() {} - public void foo(@jdk.internal.RequiresIdentity Object o) {} - public void bar(@jdk.internal.RequiresIdentity Object... o) {} - public void gg(@jdk.internal.RequiresIdentity T ri) {} + class RequiresIdentity2 { + public RequiresIdentity2() {} + public void foo(@jdk.internal.RequiresIdentity Object o) {} + public void bar(@jdk.internal.RequiresIdentity Object... o) {} + public void gg(@jdk.internal.RequiresIdentity TT ri) {} + } + + interface RequiresIdentityInt<@jdk.internal.RequiresIdentity T> {} + + interface MyIntFunction<@jdk.internal.RequiresIdentity R> { + R apply(int value); + } } diff --git a/test/langtools/tools/javac/lint/RequiresIdentityTest.java b/test/langtools/tools/javac/lint/RequiresIdentityTest.java index 62912bd88d2a2..831047d6c8433 100644 --- a/test/langtools/tools/javac/lint/RequiresIdentityTest.java +++ b/test/langtools/tools/javac/lint/RequiresIdentityTest.java @@ -7,25 +7,69 @@ package java.lang; -public class RequiresIdentityTest extends RequiresIdentityHelper { +public class RequiresIdentityTest extends RequiresIdentityHelper // should warn + implements RequiresIdentityHelper.RequiresIdentityInt { // should warn class Box {} RequiresIdentityHelper field; // should warn RequiresIdentityHelper[] field2; // should warn Box> field3; // should warn Box> field4; // should warn + RequiresIdentityHelper field5 = new RequiresIdentityHelper(); // two warnings here public RequiresIdentityTest() {} public RequiresIdentityTest(Integer i) { super(i); // should warn } - void test(RequiresIdentity2 ri, Integer i) { + void test(RequiresIdentity2 ri, Integer i) { // warn on the first argument due to its enclosing type: RequiresIdentityHelper RequiresIdentityHelper localVar; // should warn RequiresIdentityHelper[] localVar2; // should warn // there should be warnings for the invocations below ri.foo(i); - ri.bar(i, i); // two for this one + ri.bar(i, // warn here + i); // and here too ri.gg(i); } + + interface I extends RequiresIdentityHelper.RequiresIdentityInt {} // should warn + + void m(Object o) { + RequiresIdentityHelper ri = (RequiresIdentityHelper) o; // should warn + } + + RequiresIdentityHelper test() { // warn + return null; + } + + // two warns here one for the type parameter and one for the result type + > T test2() { return null; } + + class SomeClass> {} // warn + + record R(RequiresIdentityHelper c) {} // warn + record RR(R r) {} + + void m1(RequiresIdentityInt ri) { // warn here + if (ri instanceof RequiresIdentityInt rii) {} // and here + } + + void m2(RR rr) { + if (rr instanceof RR(R(RequiresIdentityHelper rii))) {} + } + + void m3() {} + void m4() { + this.>m3(); + } + + MyIntFunction field6 = Integer::new; // two warnings here + + class Run { + public <@jdk.internal.RequiresIdentity K> void run() {} + } + void m5(Runnable r) {} + void m6() { + m5(new Run()::run); + } } diff --git a/test/langtools/tools/javac/lint/RequiresIdentityTest.out b/test/langtools/tools/javac/lint/RequiresIdentityTest.out index 34db95bdfee11..335241f2cb307 100644 --- a/test/langtools/tools/javac/lint/RequiresIdentityTest.out +++ b/test/langtools/tools/javac/lint/RequiresIdentityTest.out @@ -1,14 +1,34 @@ -RequiresIdentityTest.java:13:27: compiler.warn.attempt.to.use.value.based.where.identity.expected -RequiresIdentityTest.java:14:36: compiler.warn.attempt.to.use.value.based.where.identity.expected -RequiresIdentityTest.java:15:8: compiler.warn.attempt.to.use.value.based.where.identity.expected +RequiresIdentityTest.java:10:65: compiler.warn.attempt.to.use.value.based.where.identity.expected +RequiresIdentityTest.java:11:88: compiler.warn.attempt.to.use.value.based.where.identity.expected +RequiresIdentityTest.java:14:27: compiler.warn.attempt.to.use.value.based.where.identity.expected +RequiresIdentityTest.java:15:36: compiler.warn.attempt.to.use.value.based.where.identity.expected RequiresIdentityTest.java:16:8: compiler.warn.attempt.to.use.value.based.where.identity.expected -RequiresIdentityTest.java:20:15: compiler.warn.attempt.to.use.value.based.where.identity.expected -RequiresIdentityTest.java:24:31: compiler.warn.attempt.to.use.value.based.where.identity.expected -RequiresIdentityTest.java:25:40: compiler.warn.attempt.to.use.value.based.where.identity.expected -RequiresIdentityTest.java:27:16: compiler.warn.attempt.to.use.value.based.where.identity.expected -RequiresIdentityTest.java:28:16: compiler.warn.attempt.to.use.value.based.where.identity.expected -RequiresIdentityTest.java:28:19: compiler.warn.attempt.to.use.value.based.where.identity.expected -RequiresIdentityTest.java:29:15: compiler.warn.attempt.to.use.value.based.where.identity.expected +RequiresIdentityTest.java:17:8: compiler.warn.attempt.to.use.value.based.where.identity.expected +RequiresIdentityTest.java:18:72: compiler.warn.attempt.to.use.value.based.where.identity.expected +RequiresIdentityTest.java:18:27: compiler.warn.attempt.to.use.value.based.where.identity.expected +RequiresIdentityTest.java:22:15: compiler.warn.attempt.to.use.value.based.where.identity.expected +RequiresIdentityTest.java:25:32: compiler.warn.attempt.to.use.value.based.where.identity.expected +RequiresIdentityTest.java:26:31: compiler.warn.attempt.to.use.value.based.where.identity.expected +RequiresIdentityTest.java:27:40: compiler.warn.attempt.to.use.value.based.where.identity.expected +RequiresIdentityTest.java:29:16: compiler.warn.attempt.to.use.value.based.where.identity.expected +RequiresIdentityTest.java:30:16: compiler.warn.attempt.to.use.value.based.where.identity.expected +RequiresIdentityTest.java:31:16: compiler.warn.attempt.to.use.value.based.where.identity.expected +RequiresIdentityTest.java:32:15: compiler.warn.attempt.to.use.value.based.where.identity.expected +RequiresIdentityTest.java:35:67: compiler.warn.attempt.to.use.value.based.where.identity.expected +RequiresIdentityTest.java:38:63: compiler.warn.attempt.to.use.value.based.where.identity.expected +RequiresIdentityTest.java:41:27: compiler.warn.attempt.to.use.value.based.where.identity.expected +RequiresIdentityTest.java:46:6: compiler.warn.attempt.to.use.value.based.where.identity.expected +RequiresIdentityTest.java:46:49: compiler.warn.attempt.to.use.value.based.where.identity.expected +RequiresIdentityTest.java:48:21: compiler.warn.attempt.to.use.value.based.where.identity.expected +RequiresIdentityTest.java:50:36: compiler.warn.attempt.to.use.value.based.where.identity.expected +RequiresIdentityTest.java:53:32: compiler.warn.attempt.to.use.value.based.where.identity.expected +RequiresIdentityTest.java:54:46: compiler.warn.attempt.to.use.value.based.where.identity.expected +RequiresIdentityTest.java:58:54: compiler.warn.attempt.to.use.value.based.where.identity.expected +RequiresIdentityTest.java:61:37: compiler.warn.has.been.deprecated.for.removal: java.lang.Integer(int), java.lang.Integer +RequiresIdentityTest.java:61:37: compiler.warn.attempt.to.use.value.based.where.identity.expected +RequiresIdentityTest.java:61:18: compiler.warn.attempt.to.use.value.based.where.identity.expected - compiler.err.warnings.and.werror +- compiler.note.unchecked.filename: RequiresIdentityTest.java +- compiler.note.unchecked.recompile 1 error -11 warnings +29 warnings From b4f53297f8c227c95456a742f7cdb8193b0c8aa7 Mon Sep 17 00:00:00 2001 From: Vicente Romero Date: Wed, 23 Apr 2025 09:44:03 -0400 Subject: [PATCH 11/34] additional coverage for other uses of types --- .../com/sun/tools/javac/code/Types.java | 62 ---------- .../com/sun/tools/javac/comp/Attr.java | 65 ++++++++-- .../com/sun/tools/javac/comp/Check.java | 112 ++++++++++++++++++ .../javac/lint/RequiresIdentityHelper.java | 18 ++- .../javac/lint/RequiresIdentityTest.java | 50 +++++++- .../tools/javac/lint/RequiresIdentityTest.out | 44 +++++-- 6 files changed, 260 insertions(+), 91 deletions(-) diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Types.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Types.java index 86403eebffdbf..1ada16f50b128 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Types.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Types.java @@ -42,14 +42,12 @@ import com.sun.tools.javac.code.Attribute.RetentionPolicy; import com.sun.tools.javac.code.Lint.LintCategory; -import com.sun.tools.javac.code.Source.Feature; import com.sun.tools.javac.code.Type.UndetVar.InferenceBound; import com.sun.tools.javac.code.TypeMetadata.Annotations; import com.sun.tools.javac.comp.AttrContext; import com.sun.tools.javac.comp.Check; import com.sun.tools.javac.comp.Enter; import com.sun.tools.javac.comp.Env; -import com.sun.tools.javac.comp.LambdaToMethod; import com.sun.tools.javac.jvm.ClassFile; import com.sun.tools.javac.util.*; @@ -5331,66 +5329,6 @@ public Type constantType(LoadableConstant c) { } // - // - public boolean isValueBased(Type t) { - return t != null && t.tsym != null && (t.tsym.flags() & VALUE_BASED) != 0; - } - - public boolean needsRequiresIdentityWarning(Type t) { - RequiresIdentityVisitor requiresIdentityVisitor = new RequiresIdentityVisitor(); - requiresIdentityVisitor.visit(t); - return requiresIdentityVisitor.requiresWarning; - } - - // where - class RequiresIdentityVisitor extends Types.UnaryVisitor { - boolean requiresWarning = false; - - @Override - public Void visitType(Type t, Void _unused) { - return null; - } - - @Override - public Void visitWildcardType(WildcardType t, Void _unused) { - return visit(t.type); - } - - @Override - public Void visitArrayType(ArrayType t, Void _unused) { - return visit(t.elemtype); - } - - @Override - public Void visitClassType(ClassType t, Void _unused) { - if (t != null && t.tsym != null) { - SymbolMetadata sm = t.tsym.getMetadata(); - if (sm != null) { - for (Attribute.TypeCompound ta: sm.getTypeAttributes()) { - if (ta.type.tsym == syms.requiresIdentityType.tsym) { - TypeAnnotationPosition tap = ta.position; - if (tap.type == TargetType.CLASS_TYPE_PARAMETER) { - int index = tap.parameter_index; - // ClassType::getTypeArguments() can be empty for raw types - Type tparam = t.getTypeArguments().isEmpty() ? null : t.getTypeArguments().get(index); - if (tparam != null && isValueBased(tparam)) { - requiresWarning = true; - return null; - } - } - } - } - } - } - visit(t.getEnclosingType()); - for (Type targ : t.getTypeArguments()) { - visit(targ); - } - return null; - } - } - // - public void newRound() { descCache._map.clear(); isDerivedRawCache.clear(); diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java index 7d830aa70a02b..5ff4f6f8c1908 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java @@ -1009,6 +1009,11 @@ public void visitMethodDef(JCMethodDecl tree) { localEnv.info.lint = lint; attribStats(tree.typarams, localEnv); + if (env.info.lint.isEnabled(LintCategory.IDENTITY)) { + for (JCTypeParameter tp : tree.typarams) { + chk.checkIfIdentityIsRequired(tp.pos(), tp.type, env); + } + } // If we override any other methods, check that we do so properly. // JLS ??? @@ -1047,8 +1052,10 @@ public void visitMethodDef(JCMethodDecl tree) { chk.validate(tree.typarams, localEnv); // Check that result type is well-formed. - if (tree.restype != null && !tree.restype.type.hasTag(VOID)) + if (tree.restype != null && !tree.restype.type.hasTag(VOID)) { chk.validate(tree.restype, localEnv); + chk.checkIfIdentityIsRequired(tree.restype.pos(), tree.restype.type, env); + } // Check that receiver type is well-formed. if (tree.recvparam != null) { @@ -1328,11 +1335,8 @@ public void visitVarDef(JCVariableDecl tree) { log.error(tree, Errors.IllegalRecordComponentName(v)); } } - if (tree.vartype != null && - tree.vartype.type != null && - env.info.lint.isEnabled(LintCategory.IDENTITY) && - types.needsRequiresIdentityWarning(tree.vartype.type)) { - env.info.lint.logIfEnabled(tree.vartype.pos(), LintWarnings.AttemptToUseValueBasedWhereIdentityExpected); + if (notACanonicalConstructorArg(v)) { + chk.checkIfIdentityIsRequired(tree.vartype.pos(), tree.vartype.type, env); } } finally { @@ -1340,6 +1344,15 @@ public void visitVarDef(JCVariableDecl tree) { } } + /* we don't want to warn twice so if this variable is a compiler generated parameter to + * a canonical record constructor, we don't want to issue a warning as we will warn the + * corresponding compiler generated private record field + */ + boolean notACanonicalConstructorArg(VarSymbol vs) { + return (vs.flags_field & RECORD) == 0 || + (vs.flags_field & ~(Flags.PARAMETER | RECORD | GENERATED_MEMBER)) != 0; + } + private void doQueueScanTreeAndTypeAnnotateForVarInit(JCVariableDecl tree, Env env) { if (tree.init != null && (tree.mods.flags & Flags.FIELD_INIT_TYPE_ANNOTATIONS_QUEUED) == 0 && @@ -1951,7 +1964,7 @@ private Symbol enumConstant(JCTree tree, Type enumType) { public void visitSynchronized(JCSynchronized tree) { chk.checkRefType(tree.pos(), attribExpr(tree.lock, env)); - if (types.isValueBased(tree.lock.type)) { + if (chk.isValueBased(tree.lock.type)) { if (env.info.lint.isEnabled(LintCategory.SYNCHRONIZATION)) { env.info.lint.logIfEnabled(tree.pos(), LintWarnings.AttemptToSynchronizeOnInstanceOfValueBasedClass); } else { @@ -2685,19 +2698,25 @@ void checkIfRequireIdentity(JCMethodInvocation tree, Symbol sym) { if (!argExps.isEmpty() && sym instanceof MethodSymbol ms && ms.params != null) { VarSymbol lastParam = ms.params.head; for (VarSymbol param: ms.params) { - if (requiresIdentity(param) && types.isValueBased(argExps.head.type)) { + if (requiresIdentity(param) && chk.isValueBased(argExps.head.type)) { env.info.lint.logIfEnabled(argExps.head.pos(), LintWarnings.AttemptToUseValueBasedWhereIdentityExpected); } lastParam = param; argExps = argExps.tail; } while (argExps != null && !argExps.isEmpty() && lastParam != null) { - if (requiresIdentity(lastParam) && types.isValueBased(argExps.head.type)) { + if (requiresIdentity(lastParam) && chk.isValueBased(argExps.head.type)) { env.info.lint.logIfEnabled(argExps.head.pos(), LintWarnings.AttemptToUseValueBasedWhereIdentityExpected); } argExps = argExps.tail; } } + ListBuffer typeParamTypes = new ListBuffer<>(); + for (JCExpression targ: tree.typeargs) { + chk.checkIfIdentityIsRequired(targ.pos(), targ.type, env); + typeParamTypes.add(targ.type); + } + chk.checkIfTypeParamsRequiresIdentity(sym.getMetadata(), typeParamTypes.toList(), tree.pos(), env.info.lint, true); } /* vsym is expected to be a method argument @@ -2834,6 +2853,8 @@ public void visitNewClass(final JCNewClass tree) { env.info.isAnonymousNewClass = false; } + chk.checkIfIdentityIsRequired(tree.clazz.pos(), clazztype, env); + clazztype = chk.checkDiamond(tree, clazztype); chk.validate(clazz, localEnv); if (tree.encl != null) { @@ -3824,6 +3845,14 @@ public void visitReference(final JCMemberReference that) { checkReferenceCompatible(that, desc, refType, resultInfo.checkContext, isSpeculativeRound); if (!isSpeculativeRound) { checkAccessibleTypes(that, localEnv, resultInfo.checkContext.inferenceContext(), desc, currentTarget); + chk.checkIfIdentityIsRequired(that.expr.pos(), currentTarget, localEnv); + if (that.typeargs != null) { + ListBuffer lbTypes = new ListBuffer<>(); + for (JCExpression targ : that.typeargs) { + lbTypes.add(targ.type); + } + chk.checkIfTypeParamsRequiresIdentity(refSym.getMetadata(), lbTypes.toList(), that.pos(), localEnv.info.lint, true); + } } result = check(that, currentTarget, KindSelector.VAL, resultInfo); } catch (Types.FunctionDescriptorLookupError ex) { @@ -4128,6 +4157,7 @@ public void visitBinary(JCBinary tree) { public void visitTypeCast(final JCTypeCast tree) { Type clazztype = attribType(tree.clazz, env); chk.validate(tree.clazz, env, false); + chk.checkIfIdentityIsRequired(tree.clazz.pos(), clazztype, env); //a fresh environment is required for 292 inference to work properly --- //see Infer.instantiatePolymorphicSignatureInstance() Env localEnv = env.dup(tree); @@ -4269,6 +4299,9 @@ public void visitBindingPattern(JCBindingPattern tree) { } else { matchBindings = new MatchBindings(List.of(v), List.nil()); } + if (tree.var.vartype != null) { + chk.checkIfIdentityIsRequired(tree.var.vartype.pos(), tree.var.vartype.type, env); + } } @Override @@ -5566,6 +5599,17 @@ void attribClass(ClassSymbol c) throws CompletionFailure { log.error(env.tree.pos(), Errors.EnumTypesNotExtensible); } + if (st != null && + // no need to recheck j.l.Object, shortcut, + st.tsym != syms.objectType.tsym && + // this one could be null, no explicit extends + env.enclClass.extending != null) { + chk.checkIfIdentityIsRequired(env.enclClass.extending.pos(), st, env); + } + for (JCExpression intrface: env.enclClass.implementing) { + chk.checkIfIdentityIsRequired(intrface.pos(), intrface.type, env); + } + if (rs.isSerializable(c.type)) { env.info.isSerializable = true; } @@ -5621,6 +5665,9 @@ private void attribClassBody(Env env, ClassSymbol c) { chk.validate(tree.extending, env); chk.validate(tree.implementing, env); } + for (JCTypeParameter tp : tree.typarams) { + chk.checkIfIdentityIsRequired(tp.pos(), tp.type, env); + } c.markAbstractIfNeeded(types); diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Check.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Check.java index 10f0b22268414..52140ee9072ee 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Check.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Check.java @@ -46,6 +46,7 @@ import com.sun.tools.javac.code.Source.Feature; import com.sun.tools.javac.comp.Annotate.AnnotationTypeMetadata; import com.sun.tools.javac.jvm.*; +import com.sun.tools.javac.resources.CompilerProperties; import com.sun.tools.javac.resources.CompilerProperties.Errors; import com.sun.tools.javac.resources.CompilerProperties.Fragments; import com.sun.tools.javac.resources.CompilerProperties.Warnings; @@ -5667,4 +5668,115 @@ private Void runUnderLint(E symbol, JCClassDecl p, BiConsume } + /** Check if a type required an identity class + */ + boolean checkIfIdentityIsRequired(DiagnosticPosition pos, Type t, Env env) { + if (t != null && + env.info.lint != null && + env.info.lint.isEnabled(LintCategory.IDENTITY) && + needsRequiresIdentityWarning(t)) { + env.info.lint.logIfEnabled(pos, LintWarnings.AttemptToUseValueBasedWhereIdentityExpected); + return true; + } + return false; + } + + public boolean isValueBased(Type t) { + return t != null && t.tsym != null && (t.tsym.flags() & VALUE_BASED) != 0; + } + + public boolean needsRequiresIdentityWarning(Type t) { + if (t != null) { + RequiresIdentityVisitor requiresIdentityVisitor = new RequiresIdentityVisitor(); + // we need to avoid self referencing type vars or captures + Set seen = new HashSet<>(); + requiresIdentityVisitor.visit(t, seen); + return requiresIdentityVisitor.requiresWarning; + } + return false; + } + + public boolean checkIfTypeParamsRequiresIdentity(SymbolMetadata sm, List typeParams) { + return checkIfTypeParamsRequiresIdentity(sm, typeParams, null, null, false); + } + + public boolean checkIfTypeParamsRequiresIdentity(SymbolMetadata sm, + List typeParams, + JCDiagnostic.DiagnosticPosition pos, + Lint lint, + boolean warn) { + boolean result = false; + if (sm != null) { + for (Attribute.TypeCompound ta: sm.getTypeAttributes()) { + if (ta.type.tsym == syms.requiresIdentityType.tsym) { + TypeAnnotationPosition tap = ta.position; + if (tap.type == TargetType.CLASS_TYPE_PARAMETER || tap.type == TargetType.METHOD_TYPE_PARAMETER) { + int index = tap.parameter_index; + // ClassType::getTypeArguments() can be empty for raw types + Type tparam = typeParams.isEmpty() ? null : typeParams.get(index); + if (tparam != null && isValueBased(tparam)) { + if (warn) { + lint.logIfEnabled(pos, + CompilerProperties.LintWarnings.AttemptToUseValueBasedWhereIdentityExpected); + } + result = true; + } + } + } + } + } + return result; + } + + // where + class RequiresIdentityVisitor extends Types.SimpleVisitor> { + boolean requiresWarning = false; + + @Override + public Void visitType(Type t, Set seen) { + return null; + } + + @Override + public Void visitWildcardType(WildcardType t, Set seen) { + return visit(t.type, seen); + } + + @Override + public Void visitTypeVar(TypeVar t, Set seen) { + if (seen.add(t)) { + visit(t.getUpperBound(), seen); + } + return null; + } + + @Override + public Void visitCapturedType(CapturedType t, Set seen) { + if (seen.add(t)) { + visit(t.getUpperBound(), seen); + visit(t.getLowerBound(), seen); + } + return null; + } + + @Override + public Void visitArrayType(ArrayType t, Set seen) { + return visit(t.elemtype, seen); + } + + @Override + public Void visitClassType(ClassType t, Set seen) { + if (t != null && t.tsym != null) { + if (checkIfTypeParamsRequiresIdentity(t.tsym.getMetadata(), t.getTypeArguments())) { + requiresWarning = true; + return null; + } + } + visit(t.getEnclosingType(), seen); + for (Type targ : t.getTypeArguments()) { + visit(targ, seen); + } + return null; + } + } } diff --git a/test/langtools/tools/javac/lint/RequiresIdentityHelper.java b/test/langtools/tools/javac/lint/RequiresIdentityHelper.java index afe3fb4968d03..22bd2dfb4e4e4 100644 --- a/test/langtools/tools/javac/lint/RequiresIdentityHelper.java +++ b/test/langtools/tools/javac/lint/RequiresIdentityHelper.java @@ -5,11 +5,17 @@ public class RequiresIdentityHelper<@jdk.internal.RequiresIdentity T> { public RequiresIdentityHelper() {} public RequiresIdentityHelper(@jdk.internal.RequiresIdentity Object o) {} -} -class RequiresIdentity2 { - public RequiresIdentity2() {} - public void foo(@jdk.internal.RequiresIdentity Object o) {} - public void bar(@jdk.internal.RequiresIdentity Object... o) {} - public void gg(@jdk.internal.RequiresIdentity T ri) {} + class RequiresIdentity2 { + public RequiresIdentity2() {} + public void foo(@jdk.internal.RequiresIdentity Object o) {} + public void bar(@jdk.internal.RequiresIdentity Object... o) {} + public void gg(@jdk.internal.RequiresIdentity TT ri) {} + } + + interface RequiresIdentityInt<@jdk.internal.RequiresIdentity T> {} + + interface MyIntFunction<@jdk.internal.RequiresIdentity R> { + R apply(int value); + } } diff --git a/test/langtools/tools/javac/lint/RequiresIdentityTest.java b/test/langtools/tools/javac/lint/RequiresIdentityTest.java index 62912bd88d2a2..831047d6c8433 100644 --- a/test/langtools/tools/javac/lint/RequiresIdentityTest.java +++ b/test/langtools/tools/javac/lint/RequiresIdentityTest.java @@ -7,25 +7,69 @@ package java.lang; -public class RequiresIdentityTest extends RequiresIdentityHelper { +public class RequiresIdentityTest extends RequiresIdentityHelper // should warn + implements RequiresIdentityHelper.RequiresIdentityInt { // should warn class Box {} RequiresIdentityHelper field; // should warn RequiresIdentityHelper[] field2; // should warn Box> field3; // should warn Box> field4; // should warn + RequiresIdentityHelper field5 = new RequiresIdentityHelper(); // two warnings here public RequiresIdentityTest() {} public RequiresIdentityTest(Integer i) { super(i); // should warn } - void test(RequiresIdentity2 ri, Integer i) { + void test(RequiresIdentity2 ri, Integer i) { // warn on the first argument due to its enclosing type: RequiresIdentityHelper RequiresIdentityHelper localVar; // should warn RequiresIdentityHelper[] localVar2; // should warn // there should be warnings for the invocations below ri.foo(i); - ri.bar(i, i); // two for this one + ri.bar(i, // warn here + i); // and here too ri.gg(i); } + + interface I extends RequiresIdentityHelper.RequiresIdentityInt {} // should warn + + void m(Object o) { + RequiresIdentityHelper ri = (RequiresIdentityHelper) o; // should warn + } + + RequiresIdentityHelper test() { // warn + return null; + } + + // two warns here one for the type parameter and one for the result type + > T test2() { return null; } + + class SomeClass> {} // warn + + record R(RequiresIdentityHelper c) {} // warn + record RR(R r) {} + + void m1(RequiresIdentityInt ri) { // warn here + if (ri instanceof RequiresIdentityInt rii) {} // and here + } + + void m2(RR rr) { + if (rr instanceof RR(R(RequiresIdentityHelper rii))) {} + } + + void m3() {} + void m4() { + this.>m3(); + } + + MyIntFunction field6 = Integer::new; // two warnings here + + class Run { + public <@jdk.internal.RequiresIdentity K> void run() {} + } + void m5(Runnable r) {} + void m6() { + m5(new Run()::run); + } } diff --git a/test/langtools/tools/javac/lint/RequiresIdentityTest.out b/test/langtools/tools/javac/lint/RequiresIdentityTest.out index 34db95bdfee11..a5e1d8ec4d0cb 100644 --- a/test/langtools/tools/javac/lint/RequiresIdentityTest.out +++ b/test/langtools/tools/javac/lint/RequiresIdentityTest.out @@ -1,14 +1,36 @@ -RequiresIdentityTest.java:13:27: compiler.warn.attempt.to.use.value.based.where.identity.expected -RequiresIdentityTest.java:14:36: compiler.warn.attempt.to.use.value.based.where.identity.expected -RequiresIdentityTest.java:15:8: compiler.warn.attempt.to.use.value.based.where.identity.expected +RequiresIdentityTest.java:10:65: compiler.warn.attempt.to.use.value.based.where.identity.expected +RequiresIdentityTest.java:11:88: compiler.warn.attempt.to.use.value.based.where.identity.expected +RequiresIdentityTest.java:14:27: compiler.warn.attempt.to.use.value.based.where.identity.expected +RequiresIdentityTest.java:15:36: compiler.warn.attempt.to.use.value.based.where.identity.expected RequiresIdentityTest.java:16:8: compiler.warn.attempt.to.use.value.based.where.identity.expected -RequiresIdentityTest.java:20:15: compiler.warn.attempt.to.use.value.based.where.identity.expected -RequiresIdentityTest.java:24:31: compiler.warn.attempt.to.use.value.based.where.identity.expected -RequiresIdentityTest.java:25:40: compiler.warn.attempt.to.use.value.based.where.identity.expected -RequiresIdentityTest.java:27:16: compiler.warn.attempt.to.use.value.based.where.identity.expected -RequiresIdentityTest.java:28:16: compiler.warn.attempt.to.use.value.based.where.identity.expected -RequiresIdentityTest.java:28:19: compiler.warn.attempt.to.use.value.based.where.identity.expected -RequiresIdentityTest.java:29:15: compiler.warn.attempt.to.use.value.based.where.identity.expected +RequiresIdentityTest.java:17:8: compiler.warn.attempt.to.use.value.based.where.identity.expected +RequiresIdentityTest.java:18:72: compiler.warn.attempt.to.use.value.based.where.identity.expected +RequiresIdentityTest.java:18:27: compiler.warn.attempt.to.use.value.based.where.identity.expected +RequiresIdentityTest.java:22:15: compiler.warn.attempt.to.use.value.based.where.identity.expected +RequiresIdentityTest.java:25:32: compiler.warn.attempt.to.use.value.based.where.identity.expected +RequiresIdentityTest.java:26:31: compiler.warn.attempt.to.use.value.based.where.identity.expected +RequiresIdentityTest.java:27:40: compiler.warn.attempt.to.use.value.based.where.identity.expected +RequiresIdentityTest.java:29:16: compiler.warn.attempt.to.use.value.based.where.identity.expected +RequiresIdentityTest.java:30:16: compiler.warn.attempt.to.use.value.based.where.identity.expected +RequiresIdentityTest.java:31:16: compiler.warn.attempt.to.use.value.based.where.identity.expected +RequiresIdentityTest.java:32:15: compiler.warn.attempt.to.use.value.based.where.identity.expected +RequiresIdentityTest.java:35:67: compiler.warn.attempt.to.use.value.based.where.identity.expected +RequiresIdentityTest.java:38:63: compiler.warn.attempt.to.use.value.based.where.identity.expected +RequiresIdentityTest.java:41:27: compiler.warn.attempt.to.use.value.based.where.identity.expected +RequiresIdentityTest.java:46:6: compiler.warn.attempt.to.use.value.based.where.identity.expected +RequiresIdentityTest.java:46:49: compiler.warn.attempt.to.use.value.based.where.identity.expected +RequiresIdentityTest.java:48:21: compiler.warn.attempt.to.use.value.based.where.identity.expected +RequiresIdentityTest.java:50:36: compiler.warn.attempt.to.use.value.based.where.identity.expected +RequiresIdentityTest.java:53:32: compiler.warn.attempt.to.use.value.based.where.identity.expected +RequiresIdentityTest.java:54:46: compiler.warn.attempt.to.use.value.based.where.identity.expected +RequiresIdentityTest.java:58:54: compiler.warn.attempt.to.use.value.based.where.identity.expected +RequiresIdentityTest.java:63:37: compiler.warn.attempt.to.use.value.based.where.identity.expected +RequiresIdentityTest.java:66:37: compiler.warn.has.been.deprecated.for.removal: java.lang.Integer(int), java.lang.Integer +RequiresIdentityTest.java:66:37: compiler.warn.attempt.to.use.value.based.where.identity.expected +RequiresIdentityTest.java:66:18: compiler.warn.attempt.to.use.value.based.where.identity.expected +RequiresIdentityTest.java:73:12: compiler.warn.attempt.to.use.value.based.where.identity.expected - compiler.err.warnings.and.werror +- compiler.note.unchecked.filename: RequiresIdentityTest.java +- compiler.note.unchecked.recompile 1 error -11 warnings +31 warnings From 5491f39e192301e36ba75a35313b5334fcae5251 Mon Sep 17 00:00:00 2001 From: Vicente Romero Date: Thu, 24 Apr 2025 22:20:29 -0400 Subject: [PATCH 12/34] covering more cases plus refactoring in order to simplify the code --- .../com/sun/tools/javac/code/Type.java | 4 + .../com/sun/tools/javac/comp/Attr.java | 90 ++--------- .../com/sun/tools/javac/comp/Check.java | 145 ++++++++++++------ 3 files changed, 111 insertions(+), 128 deletions(-) diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Type.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Type.java index bd2ea7048311b..dd96ba3bcfc5e 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Type.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Type.java @@ -665,6 +665,10 @@ public boolean isFinal() { return (tsym.flags() & FINAL) != 0; } + public boolean isValueBased() { + return (tsym.flags_field & VALUE_BASED) != 0; + } + /** * Does this type contain occurrences of type t? */ diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java index 5ff4f6f8c1908..c1e61ba2cb688 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java @@ -1011,7 +1011,7 @@ public void visitMethodDef(JCMethodDecl tree) { attribStats(tree.typarams, localEnv); if (env.info.lint.isEnabled(LintCategory.IDENTITY)) { for (JCTypeParameter tp : tree.typarams) { - chk.checkIfIdentityIsRequired(tp.pos(), tp.type, env); + chk.checkIfIdentityIsExpected(tp.pos(), tp.type, env.info.lint); } } @@ -1054,7 +1054,7 @@ public void visitMethodDef(JCMethodDecl tree) { // Check that result type is well-formed. if (tree.restype != null && !tree.restype.type.hasTag(VOID)) { chk.validate(tree.restype, localEnv); - chk.checkIfIdentityIsRequired(tree.restype.pos(), tree.restype.type, env); + chk.checkIfIdentityIsExpected(tree.restype.pos(), tree.restype.type, env.info.lint); } // Check that receiver type is well-formed. @@ -1335,24 +1335,13 @@ public void visitVarDef(JCVariableDecl tree) { log.error(tree, Errors.IllegalRecordComponentName(v)); } } - if (notACanonicalConstructorArg(v)) { - chk.checkIfIdentityIsRequired(tree.vartype.pos(), tree.vartype.type, env); - } + chk.checkRequiresIdentity(tree, env.info.lint); } finally { chk.setLint(prevLint); } } - /* we don't want to warn twice so if this variable is a compiler generated parameter to - * a canonical record constructor, we don't want to issue a warning as we will warn the - * corresponding compiler generated private record field - */ - boolean notACanonicalConstructorArg(VarSymbol vs) { - return (vs.flags_field & RECORD) == 0 || - (vs.flags_field & ~(Flags.PARAMETER | RECORD | GENERATED_MEMBER)) != 0; - } - private void doQueueScanTreeAndTypeAnnotateForVarInit(JCVariableDecl tree, Env env) { if (tree.init != null && (tree.mods.flags & Flags.FIELD_INIT_TYPE_ANNOTATIONS_QUEUED) == 0 && @@ -1964,7 +1953,7 @@ private Symbol enumConstant(JCTree tree, Type enumType) { public void visitSynchronized(JCSynchronized tree) { chk.checkRefType(tree.pos(), attribExpr(tree.lock, env)); - if (chk.isValueBased(tree.lock.type)) { + if (tree.lock.type.isValueBased()) { if (env.info.lint.isEnabled(LintCategory.SYNCHRONIZATION)) { env.info.lint.logIfEnabled(tree.pos(), LintWarnings.AttemptToSynchronizeOnInstanceOfValueBasedClass); } else { @@ -2688,53 +2677,11 @@ public void visitApply(JCMethodInvocation tree) { result = check(tree, capturedRes, KindSelector.VAL, resultInfo); } if (env.info.lint.isEnabled(LintCategory.IDENTITY)) { - checkIfRequireIdentity(tree, msym); + chk.checkIfRequireIdentity(tree, msym, env.info.lint); } chk.validate(tree.typeargs, localEnv); } //where - void checkIfRequireIdentity(JCMethodInvocation tree, Symbol sym) { - List argExps = tree.args; - if (!argExps.isEmpty() && sym instanceof MethodSymbol ms && ms.params != null) { - VarSymbol lastParam = ms.params.head; - for (VarSymbol param: ms.params) { - if (requiresIdentity(param) && chk.isValueBased(argExps.head.type)) { - env.info.lint.logIfEnabled(argExps.head.pos(), LintWarnings.AttemptToUseValueBasedWhereIdentityExpected); - } - lastParam = param; - argExps = argExps.tail; - } - while (argExps != null && !argExps.isEmpty() && lastParam != null) { - if (requiresIdentity(lastParam) && chk.isValueBased(argExps.head.type)) { - env.info.lint.logIfEnabled(argExps.head.pos(), LintWarnings.AttemptToUseValueBasedWhereIdentityExpected); - } - argExps = argExps.tail; - } - } - ListBuffer typeParamTypes = new ListBuffer<>(); - for (JCExpression targ: tree.typeargs) { - chk.checkIfIdentityIsRequired(targ.pos(), targ.type, env); - typeParamTypes.add(targ.type); - } - chk.checkIfTypeParamsRequiresIdentity(sym.getMetadata(), typeParamTypes.toList(), tree.pos(), env.info.lint, true); - } - - /* vsym is expected to be a method argument - */ - private boolean requiresIdentity(VarSymbol vsym) { - if (vsym != null) { - SymbolMetadata sm = vsym.getMetadata(); - if (sm != null) { - for (Attribute.Compound ca: sm.getDeclarationAttributes()) { - if (ca.type.tsym == syms.requiresIdentityType.tsym) { - return true; - } - } - } - } - return false; - } - Type adjustMethodReturnType(Symbol msym, Type qualifierType, Name methodName, List argtypes, Type restype) { if (msym != null && (msym.owner == syms.objectType.tsym || msym.owner.isInterface()) && @@ -2853,8 +2800,6 @@ public void visitNewClass(final JCNewClass tree) { env.info.isAnonymousNewClass = false; } - chk.checkIfIdentityIsRequired(tree.clazz.pos(), clazztype, env); - clazztype = chk.checkDiamond(tree, clazztype); chk.validate(clazz, localEnv); if (tree.encl != null) { @@ -2976,6 +2921,9 @@ else if (!skipNonDiamondPath) { } } + chk.checkIfIdentityIsExpected(tree.clazz.pos(), clazztype, env.info.lint); + // check here also the arguments and type arguments + if (cdef != null) { visitAnonymousClassDefinition(tree, clazz, clazztype, cdef, localEnv, argtypes, typeargtypes, pkind); return; @@ -3845,7 +3793,7 @@ public void visitReference(final JCMemberReference that) { checkReferenceCompatible(that, desc, refType, resultInfo.checkContext, isSpeculativeRound); if (!isSpeculativeRound) { checkAccessibleTypes(that, localEnv, resultInfo.checkContext.inferenceContext(), desc, currentTarget); - chk.checkIfIdentityIsRequired(that.expr.pos(), currentTarget, localEnv); + chk.checkIfIdentityIsExpected(that.expr.pos(), currentTarget, localEnv.info.lint); if (that.typeargs != null) { ListBuffer lbTypes = new ListBuffer<>(); for (JCExpression targ : that.typeargs) { @@ -4157,7 +4105,7 @@ public void visitBinary(JCBinary tree) { public void visitTypeCast(final JCTypeCast tree) { Type clazztype = attribType(tree.clazz, env); chk.validate(tree.clazz, env, false); - chk.checkIfIdentityIsRequired(tree.clazz.pos(), clazztype, env); + chk.checkIfIdentityIsExpected(tree.clazz.pos(), clazztype, env.info.lint); //a fresh environment is required for 292 inference to work properly --- //see Infer.instantiatePolymorphicSignatureInstance() Env localEnv = env.dup(tree); @@ -4300,7 +4248,7 @@ public void visitBindingPattern(JCBindingPattern tree) { matchBindings = new MatchBindings(List.of(v), List.nil()); } if (tree.var.vartype != null) { - chk.checkIfIdentityIsRequired(tree.var.vartype.pos(), tree.var.vartype.type, env); + chk.checkIfIdentityIsExpected(tree.var.vartype.pos(), tree.var.vartype.type, env.info.lint); } } @@ -5599,17 +5547,6 @@ void attribClass(ClassSymbol c) throws CompletionFailure { log.error(env.tree.pos(), Errors.EnumTypesNotExtensible); } - if (st != null && - // no need to recheck j.l.Object, shortcut, - st.tsym != syms.objectType.tsym && - // this one could be null, no explicit extends - env.enclClass.extending != null) { - chk.checkIfIdentityIsRequired(env.enclClass.extending.pos(), st, env); - } - for (JCExpression intrface: env.enclClass.implementing) { - chk.checkIfIdentityIsRequired(intrface.pos(), intrface.type, env); - } - if (rs.isSerializable(c.type)) { env.info.isSerializable = true; } @@ -5665,9 +5602,8 @@ private void attribClassBody(Env env, ClassSymbol c) { chk.validate(tree.extending, env); chk.validate(tree.implementing, env); } - for (JCTypeParameter tp : tree.typarams) { - chk.checkIfIdentityIsRequired(tp.pos(), tp.type, env); - } + + chk.checkRequiresIdentity(tree, env.info.lint); c.markAbstractIfNeeded(types); diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Check.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Check.java index 52140ee9072ee..2cf493cad33df 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Check.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Check.java @@ -5668,64 +5668,49 @@ private Void runUnderLint(E symbol, JCClassDecl p, BiConsume } - /** Check if a type required an identity class - */ - boolean checkIfIdentityIsRequired(DiagnosticPosition pos, Type t, Env env) { - if (t != null && - env.info.lint != null && - env.info.lint.isEnabled(LintCategory.IDENTITY) && - needsRequiresIdentityWarning(t)) { - env.info.lint.logIfEnabled(pos, LintWarnings.AttemptToUseValueBasedWhereIdentityExpected); - return true; + void checkRequiresIdentity(JCTree tree, Lint lint) { + if (tree instanceof JCClassDecl classDecl) { + Type st = types.supertype(classDecl.sym.type); + if (st != null && + // no need to recheck j.l.Object, shortcut, + st.tsym != syms.objectType.tsym && + // this one could be null, no explicit extends + classDecl.extending != null) { + checkIfIdentityIsExpected(classDecl.extending.pos(), st, lint); + } + for (JCExpression intrface: classDecl.implementing) { + checkIfIdentityIsExpected(intrface.pos(), intrface.type, lint); + } + for (JCTypeParameter tp : classDecl.typarams) { + checkIfIdentityIsExpected(tp.pos(), tp.type, lint); + } + } else if (tree instanceof JCVariableDecl variableDecl) { + /* we don't want to warn twice so if this variable is a compiler generated parameter of + * a canonical record constructor, we don't want to issue a warning as we will warn the + * corresponding compiler generated private record field anyways + */ + if ((variableDecl.sym.flags_field & RECORD) == 0 || + (variableDecl.sym.flags_field & ~(Flags.PARAMETER | RECORD | GENERATED_MEMBER)) != 0) { + checkIfIdentityIsExpected(variableDecl.vartype.pos(), variableDecl.vartype.type, lint); + } } - return false; - } - - public boolean isValueBased(Type t) { - return t != null && t.tsym != null && (t.tsym.flags() & VALUE_BASED) != 0; } - public boolean needsRequiresIdentityWarning(Type t) { - if (t != null) { + /** Check if a type required an identity class + */ + boolean checkIfIdentityIsExpected(DiagnosticPosition pos, Type t, Lint lint) { + if (t != null && + lint != null && + lint.isEnabled(LintCategory.IDENTITY)) { RequiresIdentityVisitor requiresIdentityVisitor = new RequiresIdentityVisitor(); // we need to avoid self referencing type vars or captures - Set seen = new HashSet<>(); - requiresIdentityVisitor.visit(t, seen); - return requiresIdentityVisitor.requiresWarning; - } - return false; - } - - public boolean checkIfTypeParamsRequiresIdentity(SymbolMetadata sm, List typeParams) { - return checkIfTypeParamsRequiresIdentity(sm, typeParams, null, null, false); - } - - public boolean checkIfTypeParamsRequiresIdentity(SymbolMetadata sm, - List typeParams, - JCDiagnostic.DiagnosticPosition pos, - Lint lint, - boolean warn) { - boolean result = false; - if (sm != null) { - for (Attribute.TypeCompound ta: sm.getTypeAttributes()) { - if (ta.type.tsym == syms.requiresIdentityType.tsym) { - TypeAnnotationPosition tap = ta.position; - if (tap.type == TargetType.CLASS_TYPE_PARAMETER || tap.type == TargetType.METHOD_TYPE_PARAMETER) { - int index = tap.parameter_index; - // ClassType::getTypeArguments() can be empty for raw types - Type tparam = typeParams.isEmpty() ? null : typeParams.get(index); - if (tparam != null && isValueBased(tparam)) { - if (warn) { - lint.logIfEnabled(pos, - CompilerProperties.LintWarnings.AttemptToUseValueBasedWhereIdentityExpected); - } - result = true; - } - } - } + requiresIdentityVisitor.visit(t, new HashSet<>()); + if (requiresIdentityVisitor.requiresWarning) { + lint.logIfEnabled(pos, LintWarnings.AttemptToUseValueBasedWhereIdentityExpected); + return true; } } - return result; + return false; } // where @@ -5779,4 +5764,62 @@ public Void visitClassType(ClassType t, Set seen) { return null; } } + + private boolean checkIfTypeParamsRequiresIdentity(SymbolMetadata sm, List typeParams) { + return checkIfTypeParamsRequiresIdentity(sm, typeParams, null, null, false); + } + + public boolean checkIfTypeParamsRequiresIdentity(SymbolMetadata sm, + List typeParams, + JCDiagnostic.DiagnosticPosition pos, + Lint lint, + boolean warn) { + boolean result = false; + if (sm != null) { + for (Attribute.TypeCompound ta: sm.getTypeAttributes()) { + if (ta.type.tsym == syms.requiresIdentityType.tsym) { + TypeAnnotationPosition tap = ta.position; + if (tap.type == TargetType.CLASS_TYPE_PARAMETER || tap.type == TargetType.METHOD_TYPE_PARAMETER) { + int index = tap.parameter_index; + // ClassType::getTypeArguments() can be empty for raw types + Type tparam = typeParams.isEmpty() ? null : typeParams.get(index); + if (tparam != null && tparam.isValueBased()) { + if (warn) { + lint.logIfEnabled(pos, + CompilerProperties.LintWarnings.AttemptToUseValueBasedWhereIdentityExpected); + } + result = true; + } + } + } + } + } + return result; + } + + public void checkIfRequireIdentity(JCMethodInvocation tree, Symbol sym, Lint lint) { + List argExps = tree.args; + if (!argExps.isEmpty() && sym instanceof MethodSymbol ms && ms.params != null) { + VarSymbol lastParam = ms.params.head; + for (VarSymbol param: ms.params) { + if (param.attribute(syms.requiresIdentityType.tsym) != null && argExps.head.type.isValueBased()) { + lint.logIfEnabled(argExps.head.pos(), LintWarnings.AttemptToUseValueBasedWhereIdentityExpected); + } + lastParam = param; + argExps = argExps.tail; + } + while (argExps != null && !argExps.isEmpty() && lastParam != null) { + if (lastParam.attribute(syms.requiresIdentityType.tsym) != null && argExps.head.type.isValueBased()) { + lint.logIfEnabled(argExps.head.pos(), LintWarnings.AttemptToUseValueBasedWhereIdentityExpected); + } + argExps = argExps.tail; + } + } + ListBuffer typeParamTypes = new ListBuffer<>(); + for (JCExpression targ: tree.typeargs) { + checkIfIdentityIsExpected(targ.pos(), targ.type, lint); + typeParamTypes.add(targ.type); + } + checkIfTypeParamsRequiresIdentity(sym.getMetadata(), typeParamTypes.toList(), tree.pos(), lint, true); + } } From eb98145373e6bac20c2c61e2689b2b3b2c9647f3 Mon Sep 17 00:00:00 2001 From: Jan Lahoda Date: Fri, 25 Apr 2025 14:16:53 +0200 Subject: [PATCH 13/34] First support for runtime visible and invisible type annotations in CreateSymbols. --- .../tools/symbolgenerator/CreateSymbols.java | 196 +++++++++++++++++- .../javac/processing/PrintingProcessor.java | 4 +- .../javac/platform/RequiresIdentityTest.java | 18 ++ .../javac/platform/RequiresIdentityTest.out | 4 + .../createsymbols/CreateSymbolsTestImpl.java | 53 ++++- 5 files changed, 266 insertions(+), 9 deletions(-) create mode 100644 test/langtools/tools/javac/platform/RequiresIdentityTest.java create mode 100644 test/langtools/tools/javac/platform/RequiresIdentityTest.out diff --git a/make/langtools/src/classes/build/tools/symbolgenerator/CreateSymbols.java b/make/langtools/src/classes/build/tools/symbolgenerator/CreateSymbols.java index 854a7b144fa17..2ecd8cc816b73 100644 --- a/make/langtools/src/classes/build/tools/symbolgenerator/CreateSymbols.java +++ b/make/langtools/src/classes/build/tools/symbolgenerator/CreateSymbols.java @@ -46,6 +46,8 @@ import java.io.StringWriter; import java.io.Writer; import java.lang.classfile.*; +import java.lang.classfile.TypeAnnotation.TargetInfo; +import java.lang.classfile.TypeAnnotation.TypePathComponent; import java.lang.classfile.attribute.*; import java.lang.classfile.constantpool.ClassEntry; import java.lang.classfile.constantpool.ConstantPoolBuilder; @@ -991,6 +993,12 @@ private void addGenericAttributes(FeatureDescription desc, Consumer createAnnotations(List desc) { @@ -1066,6 +1074,44 @@ private AnnotationValue createAttributeValue(Object value) { default -> throw new IllegalArgumentException(value.getClass().getName()); }; } + + private List createTypeAnnotations(List desc) { + return desc.stream().map(this::createTypeAnnotation).collect(Collectors.toList()); + } + + private TypeAnnotation createTypeAnnotation(TypeAnnotationDescription desc) { + Annotation baseAnn = createAnnotation(desc.annotation); + TargetInfo targetInfo = switch ((String) desc.targetInfo.get("targetType")) { + case "CLASS_TYPE_PARAMETER" -> //TODO: test! + TargetInfo.ofClassTypeParameter((int) desc.targetInfo.get("typeParameterIndex")); + case "METHOD_TYPE_PARAMETER" -> + TargetInfo.ofMethodTypeParameter((int) desc.targetInfo.get("typeParameterIndex")); + case "CLASS_EXTENDS" -> + TargetInfo.ofClassExtends((int) desc.targetInfo.get("supertypeIndex")); + case "CLASS_TYPE_PARAMETER_BOUND" -> + TargetInfo.ofClassTypeParameterBound((int) desc.targetInfo.get("typeParameterIndex"), + (int) desc.targetInfo.get("boundIndex")); + case "METHOD_TYPE_PARAMETER_BOUND" -> + TargetInfo.ofMethodTypeParameterBound((int) desc.targetInfo.get("typeParameterIndex"), + (int) desc.targetInfo.get("boundIndex")); + case "METHOD_RETURN" -> + TargetInfo.ofMethodReturn(); + case "METHOD_RECEIVER" -> + TargetInfo.ofMethodReceiver(); + case "METHOD_FORMAL_PARAMETER" -> + TargetInfo.ofMethodFormalParameter((int) desc.targetInfo.get("formalParameterIndex")); + case "THROWS" -> + TargetInfo.ofThrows((int) desc.targetInfo.get("throwsTargetIndex")); + case "FIELD" -> + TargetInfo.ofField(); + case String targetType -> + throw new IllegalStateException("Unsupported targetType: " + targetType); + }; + + List typePath = desc.typePath.stream().map(d -> TypePathComponent.of(TypePathComponent.Kind.valueOf(d.tag()), d.index())).toList(); + + return TypeAnnotation.of(targetInfo, typePath, baseAnn); + } // // @@ -2213,7 +2259,10 @@ private boolean readAttribute(FeatureDescription feature, Attribute attr) { chd.permittedSubclasses = a.permittedSubclasses().stream().map(ClassEntry::asInternalName).collect(Collectors.toList()); } case ModuleMainClassAttribute a -> ((ModuleHeaderDescription) feature).moduleMainClass = a.mainClass().asInternalName(); - case RuntimeVisibleTypeAnnotationsAttribute a -> {/* do nothing for now */} + case RuntimeInvisibleTypeAnnotationsAttribute a -> + feature.classTypeAnnotations = typeAnnotations2Descriptions(a.annotations()); + case RuntimeVisibleTypeAnnotationsAttribute a -> + feature.runtimeTypeAnnotations = typeAnnotations2Descriptions(a.annotations()); default -> throw new IllegalArgumentException("Unhandled attribute: " + attr.attributeName()); // Do nothing } @@ -2270,6 +2319,31 @@ private AnnotationDescription annotation2Description(java.lang.classfile.Annotat return new AnnotationDescription(annotationType, values); } + + private List typeAnnotations2Descriptions(List annos) { + return annos.stream().map(ta -> { + TypeAnnotationDescription desc = new TypeAnnotationDescription(); + desc.annotation = annotation2Description(ta.annotation()); + desc.targetInfo = new HashMap<>(); + desc.targetInfo.put("targetType", ta.targetInfo().targetType().name()); + switch (ta.targetInfo()) { + case TypeAnnotation.TypeParameterTarget tpt -> desc.targetInfo.put("typeParameterIndex", tpt.typeParameterIndex()); + case TypeAnnotation.SupertypeTarget st -> desc.targetInfo.put("supertypeIndex", st.supertypeIndex()); + case TypeAnnotation.TypeParameterBoundTarget tpbt -> { + desc.targetInfo.put("typeParameterIndex", tpbt.typeParameterIndex()); + desc.targetInfo.put("boundIndex", tpbt.boundIndex()); + } + case TypeAnnotation.EmptyTarget _ -> { + // nothing to write + } + case TypeAnnotation.FormalParameterTarget fpt -> desc.targetInfo.put("formalParameterIndex", fpt.formalParameterIndex()); + case TypeAnnotation.ThrowsTarget tt -> desc.targetInfo.put("throwsTargetIndex", tt.throwsTargetIndex()); + default -> throw new IllegalStateException(ta.targetInfo().targetType().name()); + } + desc.typePath = ta.targetPath().stream().map(tpc -> new TypeAnnotationDescription.TypePathComponentDesc(tpc.typePathKind().name(), tpc.typeArgumentIndex())).toList(); + return desc; + }).toList(); + } // protected boolean includeEffectiveAccess(ClassList classes, ClassDescription clazz) { @@ -2391,6 +2465,8 @@ static abstract class FeatureDescription { String versions = ""; List classAnnotations; List runtimeAnnotations; + List classTypeAnnotations; + List runtimeTypeAnnotations; protected void writeAttributes(Appendable output) throws IOException { if (flags != 0) @@ -2413,6 +2489,18 @@ protected void writeAttributes(Appendable output) throws IOException { output.append(quote(a.toString(), false)); } } + if (classTypeAnnotations != null && !classTypeAnnotations.isEmpty()) { + output.append(" classTypeAnnotations "); + for (TypeAnnotationDescription a : classTypeAnnotations) { + output.append(quote(a.toString(), false)); + } + } + if (runtimeTypeAnnotations != null && !runtimeTypeAnnotations.isEmpty()) { + output.append(" runtimeTypeAnnotations "); + for (TypeAnnotationDescription a : runtimeTypeAnnotations) { + output.append(quote(a.toString(), false)); + } + } } protected boolean shouldIgnore(String baselineVersion, String version) { @@ -2442,6 +2530,14 @@ protected void readAttributes(LineBasedReader reader) { if (inRuntimeAnnotations != null) { runtimeAnnotations = parseAnnotations(inRuntimeAnnotations, new int[1]); } + String inClassTypeAnnotations = reader.attributes.get("classTypeAnnotations"); + if (inClassTypeAnnotations != null) { + classTypeAnnotations = parseTypeAnnotations(inClassTypeAnnotations, new int[1]); + } + String inRuntimeTypeAnnotations = reader.attributes.get("runtimeTypeAnnotations"); + if (inRuntimeTypeAnnotations != null) { + runtimeTypeAnnotations = parseTypeAnnotations(inRuntimeTypeAnnotations, new int[1]); + } } public abstract boolean read(LineBasedReader reader) throws IOException; @@ -2454,6 +2550,8 @@ public int hashCode() { hash = 89 * hash + Objects.hashCode(this.signature); hash = 89 * hash + listHashCode(this.classAnnotations); hash = 89 * hash + listHashCode(this.runtimeAnnotations); + hash = 89 * hash + listHashCode(this.classTypeAnnotations); + hash = 89 * hash + listHashCode(this.runtimeTypeAnnotations); return hash; } @@ -2481,6 +2579,12 @@ public boolean equals(Object obj) { if (!listEquals(this.runtimeAnnotations, other.runtimeAnnotations)) { return false; } + if (!listEquals(this.classTypeAnnotations, other.classTypeAnnotations)) { + return false; + } + if (!listEquals(this.runtimeTypeAnnotations, other.runtimeTypeAnnotations)) { + return false; + } return true; } @@ -3636,6 +3740,40 @@ private static String dumpAnnotationValue(Object value) { } } + static final class TypeAnnotationDescription { + AnnotationDescription annotation; + Map targetInfo; + List typePath; + + public TypeAnnotationDescription() { + } + + public TypeAnnotationDescription(AnnotationDescription annotation, Map targetInfo, List typePath) { + this.annotation = annotation; + this.targetInfo = targetInfo; + this.typePath = typePath; + } + + @Override + public String toString() { + return annotation.toString() + "{" + targetInfo.entrySet().stream().map(e -> e.getKey() + "=" + quote(printValue(e.getValue()), false)).collect(Collectors.joining(",")) + "}" + + (!typePath.isEmpty() ? "[" + typePath.stream().map(desc -> desc.tag + ":" + desc.index).collect(Collectors.joining(",")) + "]" : ""); + } + + private String printValue(Object obj) { + if (obj instanceof String s) { + return "\"" + s + "\""; + } else if (obj instanceof Integer i) { + return "I" + String.valueOf(i); + } else { + throw new IllegalStateException("Unsupported value: " + obj.getClass()); + } + } + + //TODO: path + record TypePathComponentDesc(String tag, int index) {} + } + static final class EnumConstant { String type; String constant; @@ -3975,23 +4113,69 @@ public static List parseAnnotations(String encoded, int[] private static AnnotationDescription parseAnnotation(String value, int[] valuePointer) { String className = className(value, valuePointer); - Map attribute2Value = new HashMap<>(); + Map attribute2Value = Map.of(); if (valuePointer[0] < value.length() && value.charAt(valuePointer[0]) == '(') { - while (value.charAt(valuePointer[0]) != ')') { + attribute2Value = parseMap(value, valuePointer, ')'); + } + + return new AnnotationDescription(className, attribute2Value); + } + + private static Map parseMap(String value, int[] valuePointer, char endBracket) { + Map attribute2Value = new HashMap<>(); + + while (value.charAt(valuePointer[0]) != endBracket) { + int nameStart = ++valuePointer[0]; + + while (value.charAt(valuePointer[0]++) != '='); + + String name = value.substring(nameStart, valuePointer[0] - 1); + + attribute2Value.put(name, parseAnnotationValue(value, valuePointer)); + } + + valuePointer[0]++; + + return attribute2Value; + } + + public static List parseTypeAnnotations(String encoded, int[] pointer) { + List result = new ArrayList<>(); + + while (pointer[0] < encoded.length() && encoded.charAt(pointer[0]) == '@') { + pointer[0]++; + result.add(parseTypeAnnotation(encoded, pointer)); + } + + return result; + } + + private static TypeAnnotationDescription parseTypeAnnotation(String value, int[] valuePointer) { + AnnotationDescription ann = parseAnnotation(value, valuePointer); + Map targetInfo = Map.of(); + + if (valuePointer[0] < value.length() && value.charAt(valuePointer[0]) == '{') { + targetInfo = parseMap(value, valuePointer, '}'); + } + + List typePath = new ArrayList<>(); + + if (valuePointer[0] < value.length() && value.charAt(valuePointer[0]) == '[') { + while (value.charAt(valuePointer[0]) != ']') { int nameStart = ++valuePointer[0]; - while (value.charAt(valuePointer[0]++) != '='); + while (value.charAt(valuePointer[0]++) != ':'); String name = value.substring(nameStart, valuePointer[0] - 1); - attribute2Value.put(name, parseAnnotationValue(value, valuePointer)); + typePath.add(new TypeAnnotationDescription.TypePathComponentDesc(name, Integer.parseInt(readDigits(value, valuePointer)))); } valuePointer[0]++; } - return new AnnotationDescription(className, attribute2Value); + return new TypeAnnotationDescription(ann, targetInfo, typePath); } // diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/processing/PrintingProcessor.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/processing/PrintingProcessor.java index 7aca585f37ef5..950ad49236a1c 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/processing/PrintingProcessor.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/processing/PrintingProcessor.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -524,7 +524,7 @@ private void printFormalTypeParameters(Parameterizable e, List typeParams = e.getTypeParameters(); if (!typeParams.isEmpty()) { writer.print(typeParams.stream() - .map(tpe -> annotationsToString(tpe) + tpe.toString()) + .map(tpe -> annotationsToString(tpe) + tpe.toString() + " extends " + tpe.getBounds().stream().map(t -> t.toString()).collect(Collectors.joining(" & "))) .collect(Collectors.joining(", ", "<", ">"))); if (pad) writer.print(" "); diff --git a/test/langtools/tools/javac/platform/RequiresIdentityTest.java b/test/langtools/tools/javac/platform/RequiresIdentityTest.java new file mode 100644 index 0000000000000..9a47ec3281974 --- /dev/null +++ b/test/langtools/tools/javac/platform/RequiresIdentityTest.java @@ -0,0 +1,18 @@ +/* + * @test /nodynamiccopyright/ + * @bug 8354556 + * @summary Expand value-based class warnings to java.lang.ref API + * @compile/fail/ref=RequiresIdentityTest.out -Werror -XDrawDiagnostics -Xlint:identity RequiresIdentityTest.java + * @compile/fail/ref=RequiresIdentityTest.out -Werror -XDrawDiagnostics -Xlint:identity --release ${jdk.version} RequiresIdentityTest.java + */ + +import java.lang.ref.Reference; +import java.util.Optional; + + +public class RequiresIdentityTest { + + void test() { + Reference> r; + } +} diff --git a/test/langtools/tools/javac/platform/RequiresIdentityTest.out b/test/langtools/tools/javac/platform/RequiresIdentityTest.out new file mode 100644 index 0000000000000..7b93ce830b708 --- /dev/null +++ b/test/langtools/tools/javac/platform/RequiresIdentityTest.out @@ -0,0 +1,4 @@ +RequiresIdentityTest.java:16:18: compiler.warn.attempt.to.use.value.based.where.identity.expected +- compiler.err.warnings.and.werror +1 error +1 warning diff --git a/test/langtools/tools/javac/platform/createsymbols/CreateSymbolsTestImpl.java b/test/langtools/tools/javac/platform/createsymbols/CreateSymbolsTestImpl.java index c66cd08852a21..13e1c1cb206d9 100644 --- a/test/langtools/tools/javac/platform/createsymbols/CreateSymbolsTestImpl.java +++ b/test/langtools/tools/javac/platform/createsymbols/CreateSymbolsTestImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -908,6 +908,57 @@ public static class OtherNested {} """); } + @Test + void testTypeAnnotations() throws Exception { + doPrintElementTest(""" + package t; + public class T { + } + """, + """ + package t; + import java.lang.annotation.*; + import java.util.*; + public class T<@AnnInvisible @AnnVisible E extends @AnnInvisible @AnnVisible ArrayList<@AnnInvisible @AnnVisible ArrayList>> extends @AnnInvisible @AnnVisible ArrayList { + public @AnnInvisible @AnnVisible List<@AnnInvisible @AnnVisible E> field; + public <@AnnInvisible @AnnVisible M extends @AnnInvisible @AnnVisible ArrayList<@AnnInvisible @AnnVisible ArrayList>> @AnnInvisible @AnnVisible List<@AnnInvisible @AnnVisible M> convert(@AnnInvisible @AnnVisible T this, @AnnInvisible @AnnVisible M e1, @AnnInvisible @AnnVisible List<@AnnInvisible @AnnVisible E> e2) throws @AnnInvisible @AnnVisible IllegalStateException, @AnnInvisible @AnnVisible IllegalArgumentException { + return null; + } + } + @Retention(RetentionPolicy.RUNTIME) + @Target(ElementType.TYPE_USE) + @interface AnnVisible { + } + @Retention(RetentionPolicy.CLASS) + @Target(ElementType.TYPE_USE) + @interface AnnInvisible { + } + """, + "t.T", + """ + package t; + + public class T { + + public T(); + } + """, + "t.T", + """ + package t; + + public class T<@t.AnnInvisible @t.AnnVisible E extends java.util.@t.AnnInvisible @t.AnnVisible ArrayList> extends java.util.@t.AnnInvisible @t.AnnVisible ArrayList { + public java.util.@t.AnnInvisible @t.AnnVisible List<@t.AnnInvisible @t.AnnVisible E> field; + + public T(); + + public <@t.AnnInvisible @t.AnnVisible M extends java.util.@t.AnnInvisible @t.AnnVisible ArrayList> java.util.@t.AnnInvisible @t.AnnVisible List<@t.AnnInvisible @t.AnnVisible M> convert(@t.AnnInvisible @t.AnnVisible M arg0, + java.util.@t.AnnInvisible @t.AnnVisible List<@t.AnnInvisible @t.AnnVisible E> arg1) throws java.lang.@t.AnnInvisible @t.AnnVisible IllegalStateException,\s + java.lang.@t.AnnInvisible @t.AnnVisible IllegalArgumentException; + } + """); + } + void doTestData(String data, String... code) throws Exception { String testClasses = System.getProperty("test.classes"); From 09b833188780eb6cfb9228fb0813ec844bb93463 Mon Sep 17 00:00:00 2001 From: Vicente Romero Date: Fri, 25 Apr 2025 10:00:07 -0400 Subject: [PATCH 14/34] refactoring --- .../com/sun/tools/javac/comp/Attr.java | 27 +-- .../com/sun/tools/javac/comp/Check.java | 161 +++++++++++------- 2 files changed, 106 insertions(+), 82 deletions(-) diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java index c1e61ba2cb688..96e69e98ba725 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java @@ -1009,11 +1009,6 @@ public void visitMethodDef(JCMethodDecl tree) { localEnv.info.lint = lint; attribStats(tree.typarams, localEnv); - if (env.info.lint.isEnabled(LintCategory.IDENTITY)) { - for (JCTypeParameter tp : tree.typarams) { - chk.checkIfIdentityIsExpected(tp.pos(), tp.type, env.info.lint); - } - } // If we override any other methods, check that we do so properly. // JLS ??? @@ -1054,8 +1049,8 @@ public void visitMethodDef(JCMethodDecl tree) { // Check that result type is well-formed. if (tree.restype != null && !tree.restype.type.hasTag(VOID)) { chk.validate(tree.restype, localEnv); - chk.checkIfIdentityIsExpected(tree.restype.pos(), tree.restype.type, env.info.lint); } + chk.checkRequiresIdentity(tree, env.info.lint); // Check that receiver type is well-formed. if (tree.recvparam != null) { @@ -2677,7 +2672,7 @@ public void visitApply(JCMethodInvocation tree) { result = check(tree, capturedRes, KindSelector.VAL, resultInfo); } if (env.info.lint.isEnabled(LintCategory.IDENTITY)) { - chk.checkIfRequireIdentity(tree, msym, env.info.lint); + chk.checkRequiresIdentity(tree, env.info.lint); } chk.validate(tree.typeargs, localEnv); } @@ -2921,8 +2916,7 @@ else if (!skipNonDiamondPath) { } } - chk.checkIfIdentityIsExpected(tree.clazz.pos(), clazztype, env.info.lint); - // check here also the arguments and type arguments + chk.checkRequiresIdentity(tree, env.info.lint); if (cdef != null) { visitAnonymousClassDefinition(tree, clazz, clazztype, cdef, localEnv, argtypes, typeargtypes, pkind); @@ -3793,15 +3787,8 @@ public void visitReference(final JCMemberReference that) { checkReferenceCompatible(that, desc, refType, resultInfo.checkContext, isSpeculativeRound); if (!isSpeculativeRound) { checkAccessibleTypes(that, localEnv, resultInfo.checkContext.inferenceContext(), desc, currentTarget); - chk.checkIfIdentityIsExpected(that.expr.pos(), currentTarget, localEnv.info.lint); - if (that.typeargs != null) { - ListBuffer lbTypes = new ListBuffer<>(); - for (JCExpression targ : that.typeargs) { - lbTypes.add(targ.type); - } - chk.checkIfTypeParamsRequiresIdentity(refSym.getMetadata(), lbTypes.toList(), that.pos(), localEnv.info.lint, true); - } } + chk.checkRequiresIdentity(that, localEnv.info.lint); result = check(that, currentTarget, KindSelector.VAL, resultInfo); } catch (Types.FunctionDescriptorLookupError ex) { JCDiagnostic cause = ex.getDiagnostic(); @@ -4105,7 +4092,7 @@ public void visitBinary(JCBinary tree) { public void visitTypeCast(final JCTypeCast tree) { Type clazztype = attribType(tree.clazz, env); chk.validate(tree.clazz, env, false); - chk.checkIfIdentityIsExpected(tree.clazz.pos(), clazztype, env.info.lint); + chk.checkRequiresIdentity(tree, env.info.lint); //a fresh environment is required for 292 inference to work properly --- //see Infer.instantiatePolymorphicSignatureInstance() Env localEnv = env.dup(tree); @@ -4247,9 +4234,7 @@ public void visitBindingPattern(JCBindingPattern tree) { } else { matchBindings = new MatchBindings(List.of(v), List.nil()); } - if (tree.var.vartype != null) { - chk.checkIfIdentityIsExpected(tree.var.vartype.pos(), tree.var.vartype.type, env.info.lint); - } + chk.checkRequiresIdentity(tree, env.info.lint); } @Override diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Check.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Check.java index 2cf493cad33df..c7e6a5359c272 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Check.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Check.java @@ -5669,36 +5669,107 @@ private Void runUnderLint(E symbol, JCClassDecl p, BiConsume } void checkRequiresIdentity(JCTree tree, Lint lint) { - if (tree instanceof JCClassDecl classDecl) { - Type st = types.supertype(classDecl.sym.type); - if (st != null && - // no need to recheck j.l.Object, shortcut, - st.tsym != syms.objectType.tsym && - // this one could be null, no explicit extends - classDecl.extending != null) { - checkIfIdentityIsExpected(classDecl.extending.pos(), st, lint); + switch (tree) { + case JCClassDecl classDecl : { + Type st = types.supertype(classDecl.sym.type); + if (st != null && + // no need to recheck j.l.Object, shortcut, + st.tsym != syms.objectType.tsym && + // this one could be null, no explicit extends + classDecl.extending != null) { + checkIfIdentityIsExpected(classDecl.extending.pos(), st, lint); + } + for (JCExpression intrface: classDecl.implementing) { + checkIfIdentityIsExpected(intrface.pos(), intrface.type, lint); + } + for (JCTypeParameter tp : classDecl.typarams) { + checkIfIdentityIsExpected(tp.pos(), tp.type, lint); + } + break; + } + case JCVariableDecl variableDecl : { + if (variableDecl.vartype != null && + (variableDecl.sym.flags_field & RECORD) == 0 || + (variableDecl.sym.flags_field & ~(Flags.PARAMETER | RECORD | GENERATED_MEMBER)) != 0) { + /* we don't want to warn twice so if this variable is a compiler generated parameter of + * a canonical record constructor, we don't want to issue a warning as we will warn the + * corresponding compiler generated private record field anyways + */ + checkIfIdentityIsExpected(variableDecl.vartype.pos(), variableDecl.vartype.type, lint); + } + break; + } + case JCTypeCast typeCast : { + checkIfIdentityIsExpected(typeCast.clazz.pos(), typeCast.clazz.type, lint); + break; } - for (JCExpression intrface: classDecl.implementing) { - checkIfIdentityIsExpected(intrface.pos(), intrface.type, lint); + case JCBindingPattern bindingPattern : { + if (bindingPattern.var.vartype != null) { + checkIfIdentityIsExpected(bindingPattern.var.vartype.pos(), bindingPattern.var.vartype.type, lint); + } + break; + } + case JCMemberReference mref : { + checkIfIdentityIsExpected(mref.expr.pos(), mref.target, lint); + if (mref.typeargs != null) { + ListBuffer lbTypes = new ListBuffer<>(); + for (JCExpression targ : mref.typeargs) { + lbTypes.add(targ.type); + } + checkIfTypeParamsRequiresIdentity(mref.sym.getMetadata(), lbTypes.toList(), mref.pos(), lint, true); + } + break; } - for (JCTypeParameter tp : classDecl.typarams) { - checkIfIdentityIsExpected(tp.pos(), tp.type, lint); + case JCMethodDecl methodDecl : { + for (JCTypeParameter tp : methodDecl.typarams) { + checkIfIdentityIsExpected(tp.pos(), tp.type, lint); + } + if (methodDecl.restype != null && !methodDecl.restype.type.hasTag(VOID)) { + checkIfIdentityIsExpected(methodDecl.restype.pos(), methodDecl.restype.type, lint); + } + break; } - } else if (tree instanceof JCVariableDecl variableDecl) { - /* we don't want to warn twice so if this variable is a compiler generated parameter of - * a canonical record constructor, we don't want to issue a warning as we will warn the - * corresponding compiler generated private record field anyways - */ - if ((variableDecl.sym.flags_field & RECORD) == 0 || - (variableDecl.sym.flags_field & ~(Flags.PARAMETER | RECORD | GENERATED_MEMBER)) != 0) { - checkIfIdentityIsExpected(variableDecl.vartype.pos(), variableDecl.vartype.type, lint); + case JCNewClass newClass : { + checkIfIdentityIsExpected(newClass.clazz.pos(), newClass.clazz.type, lint); + // check here also the arguments and type arguments + break; } + case JCMethodInvocation apply : { + List argExps = apply.args; + Symbol msym = TreeInfo.symbol(apply.meth); + if (msym != null) { + if (!argExps.isEmpty() && msym instanceof MethodSymbol ms && ms.params != null) { + VarSymbol lastParam = ms.params.head; + for (VarSymbol param: ms.params) { + if (param.attribute(syms.requiresIdentityType.tsym) != null && argExps.head.type.isValueBased()) { + lint.logIfEnabled(argExps.head.pos(), LintWarnings.AttemptToUseValueBasedWhereIdentityExpected); + } + lastParam = param; + argExps = argExps.tail; + } + while (argExps != null && !argExps.isEmpty() && lastParam != null) { + if (lastParam.attribute(syms.requiresIdentityType.tsym) != null && argExps.head.type.isValueBased()) { + lint.logIfEnabled(argExps.head.pos(), LintWarnings.AttemptToUseValueBasedWhereIdentityExpected); + } + argExps = argExps.tail; + } + } + ListBuffer typeParamTypes = new ListBuffer<>(); + for (JCExpression targ: apply.typeargs) { + checkIfIdentityIsExpected(targ.pos(), targ.type, lint); + typeParamTypes.add(targ.type); + } + checkIfTypeParamsRequiresIdentity(msym.getMetadata(), typeParamTypes.toList(), tree.pos(), lint, true); + } + break; + } + default: throw new AssertionError("unexpected tree " + tree); } } /** Check if a type required an identity class */ - boolean checkIfIdentityIsExpected(DiagnosticPosition pos, Type t, Lint lint) { + private boolean checkIfIdentityIsExpected(DiagnosticPosition pos, Type t, Lint lint) { if (t != null && lint != null && lint.isEnabled(LintCategory.IDENTITY)) { @@ -5763,63 +5834,31 @@ public Void visitClassType(ClassType t, Set seen) { } return null; } - } + } // RequiresIdentityVisitor private boolean checkIfTypeParamsRequiresIdentity(SymbolMetadata sm, List typeParams) { return checkIfTypeParamsRequiresIdentity(sm, typeParams, null, null, false); } - public boolean checkIfTypeParamsRequiresIdentity(SymbolMetadata sm, + private boolean checkIfTypeParamsRequiresIdentity(SymbolMetadata sm, List typeParams, JCDiagnostic.DiagnosticPosition pos, Lint lint, boolean warn) { boolean result = false; - if (sm != null) { + if (sm != null && !typeParams.isEmpty()) { for (Attribute.TypeCompound ta: sm.getTypeAttributes()) { if (ta.type.tsym == syms.requiresIdentityType.tsym) { - TypeAnnotationPosition tap = ta.position; - if (tap.type == TargetType.CLASS_TYPE_PARAMETER || tap.type == TargetType.METHOD_TYPE_PARAMETER) { - int index = tap.parameter_index; - // ClassType::getTypeArguments() can be empty for raw types - Type tparam = typeParams.isEmpty() ? null : typeParams.get(index); - if (tparam != null && tparam.isValueBased()) { - if (warn) { - lint.logIfEnabled(pos, - CompilerProperties.LintWarnings.AttemptToUseValueBasedWhereIdentityExpected); - } - result = true; + if (typeParams.get(ta.position.parameter_index).isValueBased()) { + if (warn) { + lint.logIfEnabled(pos, + CompilerProperties.LintWarnings.AttemptToUseValueBasedWhereIdentityExpected); } + result = true; } } } } return result; } - - public void checkIfRequireIdentity(JCMethodInvocation tree, Symbol sym, Lint lint) { - List argExps = tree.args; - if (!argExps.isEmpty() && sym instanceof MethodSymbol ms && ms.params != null) { - VarSymbol lastParam = ms.params.head; - for (VarSymbol param: ms.params) { - if (param.attribute(syms.requiresIdentityType.tsym) != null && argExps.head.type.isValueBased()) { - lint.logIfEnabled(argExps.head.pos(), LintWarnings.AttemptToUseValueBasedWhereIdentityExpected); - } - lastParam = param; - argExps = argExps.tail; - } - while (argExps != null && !argExps.isEmpty() && lastParam != null) { - if (lastParam.attribute(syms.requiresIdentityType.tsym) != null && argExps.head.type.isValueBased()) { - lint.logIfEnabled(argExps.head.pos(), LintWarnings.AttemptToUseValueBasedWhereIdentityExpected); - } - argExps = argExps.tail; - } - } - ListBuffer typeParamTypes = new ListBuffer<>(); - for (JCExpression targ: tree.typeargs) { - checkIfIdentityIsExpected(targ.pos(), targ.type, lint); - typeParamTypes.add(targ.type); - } - checkIfTypeParamsRequiresIdentity(sym.getMetadata(), typeParamTypes.toList(), tree.pos(), lint, true); - } } From 9d6af787861f054cf54206ecf77dad30620cf12d Mon Sep 17 00:00:00 2001 From: Vicente Romero Date: Fri, 25 Apr 2025 11:27:18 -0400 Subject: [PATCH 15/34] refactoring --- .../com/sun/tools/javac/comp/Check.java | 39 +++++++++++-------- 1 file changed, 22 insertions(+), 17 deletions(-) diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Check.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Check.java index c7e6a5359c272..5ee19edd97505 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Check.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Check.java @@ -5709,6 +5709,15 @@ void checkRequiresIdentity(JCTree tree, Lint lint) { } break; } + case JCMethodDecl methodDecl : { + for (JCTypeParameter tp : methodDecl.typarams) { + checkIfIdentityIsExpected(tp.pos(), tp.type, lint); + } + if (methodDecl.restype != null && !methodDecl.restype.type.hasTag(VOID)) { + checkIfIdentityIsExpected(methodDecl.restype.pos(), methodDecl.restype.type, lint); + } + break; + } case JCMemberReference mref : { checkIfIdentityIsExpected(mref.expr.pos(), mref.target, lint); if (mref.typeargs != null) { @@ -5720,23 +5729,16 @@ void checkRequiresIdentity(JCTree tree, Lint lint) { } break; } - case JCMethodDecl methodDecl : { - for (JCTypeParameter tp : methodDecl.typarams) { - checkIfIdentityIsExpected(tp.pos(), tp.type, lint); - } - if (methodDecl.restype != null && !methodDecl.restype.type.hasTag(VOID)) { - checkIfIdentityIsExpected(methodDecl.restype.pos(), methodDecl.restype.type, lint); + case JCPolyExpression poly when (poly instanceof JCNewClass || poly instanceof JCMethodInvocation): { + if (poly instanceof JCNewClass newClass) { + checkIfIdentityIsExpected(newClass.clazz.pos(), newClass.clazz.type, lint); } - break; - } - case JCNewClass newClass : { - checkIfIdentityIsExpected(newClass.clazz.pos(), newClass.clazz.type, lint); - // check here also the arguments and type arguments - break; - } - case JCMethodInvocation apply : { - List argExps = apply.args; - Symbol msym = TreeInfo.symbol(apply.meth); + List argExps = poly instanceof JCNewClass ? + ((JCNewClass)poly).args : + ((JCMethodInvocation)poly).args; + Symbol msym = poly instanceof JCNewClass ? + ((JCNewClass)poly).constructor : + TreeInfo.symbol(((JCMethodInvocation)poly).meth); if (msym != null) { if (!argExps.isEmpty() && msym instanceof MethodSymbol ms && ms.params != null) { VarSymbol lastParam = ms.params.head; @@ -5755,7 +5757,10 @@ void checkRequiresIdentity(JCTree tree, Lint lint) { } } ListBuffer typeParamTypes = new ListBuffer<>(); - for (JCExpression targ: apply.typeargs) { + List typeargs = poly instanceof JCNewClass ? + ((JCNewClass)poly).typeargs : + ((JCMethodInvocation)poly).typeargs; + for (JCExpression targ: typeargs) { checkIfIdentityIsExpected(targ.pos(), targ.type, lint); typeParamTypes.add(targ.type); } From 4a6b9bca6531b05599525344e20c61c710921e63 Mon Sep 17 00:00:00 2001 From: Vicente Romero Date: Fri, 25 Apr 2025 12:28:00 -0400 Subject: [PATCH 16/34] more refactorings and tests --- .../com/sun/tools/javac/comp/Check.java | 59 ++++++++++--------- .../javac/lint/RequiresIdentityHelper.java | 2 +- .../javac/lint/RequiresIdentityTest.java | 6 ++ .../tools/javac/lint/RequiresIdentityTest.out | 8 ++- 4 files changed, 44 insertions(+), 31 deletions(-) diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Check.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Check.java index 5ee19edd97505..aab42132c7e3e 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Check.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Check.java @@ -5720,16 +5720,11 @@ void checkRequiresIdentity(JCTree tree, Lint lint) { } case JCMemberReference mref : { checkIfIdentityIsExpected(mref.expr.pos(), mref.target, lint); - if (mref.typeargs != null) { - ListBuffer lbTypes = new ListBuffer<>(); - for (JCExpression targ : mref.typeargs) { - lbTypes.add(targ.type); - } - checkIfTypeParamsRequiresIdentity(mref.sym.getMetadata(), lbTypes.toList(), mref.pos(), lint, true); - } + checkIfTypeParamsRequiresIdentity(mref.sym.getMetadata(), mref.typeargs, lint); break; } - case JCPolyExpression poly when (poly instanceof JCNewClass || poly instanceof JCMethodInvocation): { + case JCPolyExpression poly + when (poly instanceof JCNewClass || poly instanceof JCMethodInvocation) : { if (poly instanceof JCNewClass newClass) { checkIfIdentityIsExpected(newClass.clazz.pos(), newClass.clazz.type, lint); } @@ -5756,15 +5751,12 @@ case JCPolyExpression poly when (poly instanceof JCNewClass || poly instanceof J argExps = argExps.tail; } } - ListBuffer typeParamTypes = new ListBuffer<>(); - List typeargs = poly instanceof JCNewClass ? - ((JCNewClass)poly).typeargs : - ((JCMethodInvocation)poly).typeargs; - for (JCExpression targ: typeargs) { - checkIfIdentityIsExpected(targ.pos(), targ.type, lint); - typeParamTypes.add(targ.type); - } - checkIfTypeParamsRequiresIdentity(msym.getMetadata(), typeParamTypes.toList(), tree.pos(), lint, true); + checkIfTypeParamsRequiresIdentity( + msym.getMetadata(), + poly instanceof JCNewClass ? + ((JCNewClass)poly).typeargs : + ((JCMethodInvocation)poly).typeargs, + lint); } break; } @@ -5842,24 +5834,35 @@ public Void visitClassType(ClassType t, Set seen) { } // RequiresIdentityVisitor private boolean checkIfTypeParamsRequiresIdentity(SymbolMetadata sm, List typeParams) { - return checkIfTypeParamsRequiresIdentity(sm, typeParams, null, null, false); - } - - private boolean checkIfTypeParamsRequiresIdentity(SymbolMetadata sm, - List typeParams, - JCDiagnostic.DiagnosticPosition pos, - Lint lint, - boolean warn) { boolean result = false; if (sm != null && !typeParams.isEmpty()) { for (Attribute.TypeCompound ta: sm.getTypeAttributes()) { if (ta.type.tsym == syms.requiresIdentityType.tsym) { if (typeParams.get(ta.position.parameter_index).isValueBased()) { - if (warn) { - lint.logIfEnabled(pos, + result = true; + } + } + } + } + return result; + } + + private boolean checkIfTypeParamsRequiresIdentity(SymbolMetadata sm, + List typeParamTrees, + Lint lint) { + boolean result = false; + if (typeParamTrees != null && !typeParamTrees.isEmpty()) { + for (JCExpression targ: typeParamTrees) { + checkIfIdentityIsExpected(targ.pos(), targ.type, lint); + } + if (sm != null) { + for (Attribute.TypeCompound ta: sm.getTypeAttributes()) { + if (ta.type.tsym == syms.requiresIdentityType.tsym) { + if (typeParamTrees.get(ta.position.parameter_index).type.isValueBased()) { + lint.logIfEnabled(typeParamTrees.get(ta.position.parameter_index).pos(), CompilerProperties.LintWarnings.AttemptToUseValueBasedWhereIdentityExpected); + result = true; } - result = true; } } } diff --git a/test/langtools/tools/javac/lint/RequiresIdentityHelper.java b/test/langtools/tools/javac/lint/RequiresIdentityHelper.java index 22bd2dfb4e4e4..abc50d6d446ba 100644 --- a/test/langtools/tools/javac/lint/RequiresIdentityHelper.java +++ b/test/langtools/tools/javac/lint/RequiresIdentityHelper.java @@ -4,7 +4,7 @@ public class RequiresIdentityHelper<@jdk.internal.RequiresIdentity T> { public RequiresIdentityHelper() {} - public RequiresIdentityHelper(@jdk.internal.RequiresIdentity Object o) {} + public <@jdk.internal.RequiresIdentity TT> RequiresIdentityHelper(@jdk.internal.RequiresIdentity Object o) {} class RequiresIdentity2 { public RequiresIdentity2() {} diff --git a/test/langtools/tools/javac/lint/RequiresIdentityTest.java b/test/langtools/tools/javac/lint/RequiresIdentityTest.java index 831047d6c8433..a4109771ff0bb 100644 --- a/test/langtools/tools/javac/lint/RequiresIdentityTest.java +++ b/test/langtools/tools/javac/lint/RequiresIdentityTest.java @@ -72,4 +72,10 @@ void m5(Runnable r) {} void m6() { m5(new Run()::run); } + + void m7(Integer i, Object o) { + RequiresIdentityHelper var1 = new RequiresIdentityHelper(i); + RequiresIdentityHelper var2 = new RequiresIdentityHelper(o); + RequiresIdentityHelper var3 = new RequiresIdentityHelper(o); + } } diff --git a/test/langtools/tools/javac/lint/RequiresIdentityTest.out b/test/langtools/tools/javac/lint/RequiresIdentityTest.out index a5e1d8ec4d0cb..febd738c2e4b2 100644 --- a/test/langtools/tools/javac/lint/RequiresIdentityTest.out +++ b/test/langtools/tools/javac/lint/RequiresIdentityTest.out @@ -28,9 +28,13 @@ RequiresIdentityTest.java:63:37: compiler.warn.attempt.to.use.value.based.where. RequiresIdentityTest.java:66:37: compiler.warn.has.been.deprecated.for.removal: java.lang.Integer(int), java.lang.Integer RequiresIdentityTest.java:66:37: compiler.warn.attempt.to.use.value.based.where.identity.expected RequiresIdentityTest.java:66:18: compiler.warn.attempt.to.use.value.based.where.identity.expected -RequiresIdentityTest.java:73:12: compiler.warn.attempt.to.use.value.based.where.identity.expected +RequiresIdentityTest.java:73:32: compiler.warn.attempt.to.use.value.based.where.identity.expected +RequiresIdentityTest.java:77:90: compiler.warn.attempt.to.use.value.based.where.identity.expected +RequiresIdentityTest.java:78:52: compiler.warn.attempt.to.use.value.based.where.identity.expected +RequiresIdentityTest.java:79:82: compiler.warn.attempt.to.use.value.based.where.identity.expected +RequiresIdentityTest.java:79:31: compiler.warn.attempt.to.use.value.based.where.identity.expected - compiler.err.warnings.and.werror - compiler.note.unchecked.filename: RequiresIdentityTest.java - compiler.note.unchecked.recompile 1 error -31 warnings +35 warnings From 1c3cd61f0adf177d3c40fb8c77b2620add252672 Mon Sep 17 00:00:00 2001 From: Vicente Romero Date: Fri, 25 Apr 2025 15:02:47 -0400 Subject: [PATCH 17/34] more refactorings --- .../com/sun/tools/javac/comp/Check.java | 64 +++++++---------- .../tools/javac/resources/compiler.properties | 3 +- .../tools/javac/lint/RequiresIdentityTest.out | 68 +++++++++---------- 3 files changed, 61 insertions(+), 74 deletions(-) diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Check.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Check.java index aab42132c7e3e..a4a3898140ac3 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Check.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Check.java @@ -5731,22 +5731,20 @@ void checkRequiresIdentity(JCTree tree, Lint lint) { List argExps = poly instanceof JCNewClass ? ((JCNewClass)poly).args : ((JCMethodInvocation)poly).args; - Symbol msym = poly instanceof JCNewClass ? - ((JCNewClass)poly).constructor : - TreeInfo.symbol(((JCMethodInvocation)poly).meth); + Symbol msym = TreeInfo.symbolFor(poly); if (msym != null) { if (!argExps.isEmpty() && msym instanceof MethodSymbol ms && ms.params != null) { VarSymbol lastParam = ms.params.head; for (VarSymbol param: ms.params) { if (param.attribute(syms.requiresIdentityType.tsym) != null && argExps.head.type.isValueBased()) { - lint.logIfEnabled(argExps.head.pos(), LintWarnings.AttemptToUseValueBasedWhereIdentityExpected); + lint.logIfEnabled(argExps.head.pos(), LintWarnings.AttemptToUseValueBasedWhereIdentityExpected(argExps.head.type)); } lastParam = param; argExps = argExps.tail; } while (argExps != null && !argExps.isEmpty() && lastParam != null) { if (lastParam.attribute(syms.requiresIdentityType.tsym) != null && argExps.head.type.isValueBased()) { - lint.logIfEnabled(argExps.head.pos(), LintWarnings.AttemptToUseValueBasedWhereIdentityExpected); + lint.logIfEnabled(argExps.head.pos(), LintWarnings.AttemptToUseValueBasedWhereIdentityExpected(argExps.head.type)); } argExps = argExps.tail; } @@ -5771,10 +5769,10 @@ private boolean checkIfIdentityIsExpected(DiagnosticPosition pos, Type t, Lint l lint != null && lint.isEnabled(LintCategory.IDENTITY)) { RequiresIdentityVisitor requiresIdentityVisitor = new RequiresIdentityVisitor(); - // we need to avoid self referencing type vars or captures + // we need to avoid recursion due to self referencing type vars or captures, this is why we need a set requiresIdentityVisitor.visit(t, new HashSet<>()); if (requiresIdentityVisitor.requiresWarning) { - lint.logIfEnabled(pos, LintWarnings.AttemptToUseValueBasedWhereIdentityExpected); + lint.logIfEnabled(pos, LintWarnings.AttemptToUseValueBasedWhereIdentityExpected(t)); return true; } } @@ -5820,9 +5818,15 @@ public Void visitArrayType(ArrayType t, Set seen) { @Override public Void visitClassType(ClassType t, Set seen) { if (t != null && t.tsym != null) { - if (checkIfTypeParamsRequiresIdentity(t.tsym.getMetadata(), t.getTypeArguments())) { - requiresWarning = true; - return null; + SymbolMetadata sm = t.tsym.getMetadata(); + if (sm != null && !t.getTypeArguments().isEmpty()) { + for (Attribute.TypeCompound ta: sm.getTypeAttributes().stream() + .filter(ta -> ta.type.tsym == syms.requiresIdentityType.tsym).toList()) { + if (t.getTypeArguments().get(ta.position.parameter_index).isValueBased()) { + requiresWarning = true; + return null; + } + } } } visit(t.getEnclosingType(), seen); @@ -5833,40 +5837,22 @@ public Void visitClassType(ClassType t, Set seen) { } } // RequiresIdentityVisitor - private boolean checkIfTypeParamsRequiresIdentity(SymbolMetadata sm, List typeParams) { - boolean result = false; - if (sm != null && !typeParams.isEmpty()) { - for (Attribute.TypeCompound ta: sm.getTypeAttributes()) { - if (ta.type.tsym == syms.requiresIdentityType.tsym) { - if (typeParams.get(ta.position.parameter_index).isValueBased()) { - result = true; - } - } - } - } - return result; - } - - private boolean checkIfTypeParamsRequiresIdentity(SymbolMetadata sm, + private void checkIfTypeParamsRequiresIdentity(SymbolMetadata sm, List typeParamTrees, Lint lint) { - boolean result = false; if (typeParamTrees != null && !typeParamTrees.isEmpty()) { - for (JCExpression targ: typeParamTrees) { + for (JCExpression targ : typeParamTrees) { checkIfIdentityIsExpected(targ.pos(), targ.type, lint); } - if (sm != null) { - for (Attribute.TypeCompound ta: sm.getTypeAttributes()) { - if (ta.type.tsym == syms.requiresIdentityType.tsym) { - if (typeParamTrees.get(ta.position.parameter_index).type.isValueBased()) { - lint.logIfEnabled(typeParamTrees.get(ta.position.parameter_index).pos(), - CompilerProperties.LintWarnings.AttemptToUseValueBasedWhereIdentityExpected); - result = true; - } - } - } - } + if (sm != null) + for (Attribute.TypeCompound ta : sm.getTypeAttributes().stream() + .filter(ta -> ta.type.tsym == syms.requiresIdentityType.tsym).toList()) + if (typeParamTrees.get(ta.position.parameter_index).type.isValueBased()) + lint.logIfEnabled( + typeParamTrees.get(ta.position.parameter_index).pos(), + CompilerProperties.LintWarnings + .AttemptToUseValueBasedWhereIdentityExpected(typeParamTrees.get(ta.position.parameter_index).type) + ); } - return result; } } diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler.properties b/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler.properties index 0ffc050790b55..531b5f2b3e280 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler.properties +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler.properties @@ -4253,9 +4253,10 @@ compiler.warn.attempt.to.synchronize.on.instance.of.value.based.class=\ compiler.warn.attempt.to.synchronize.on.instance.of.value.based.class2=\ attempt to synchronize on an instance of a value-based class +# 0: type # lint: identity compiler.warn.attempt.to.use.value.based.where.identity.expected=\ - attempt to use a value-based type where an identity is expected + attempt to use a value-based type, ''{0}'', where an identity type is expected # 0: type compiler.err.enclosing.class.type.non.denotable=\ diff --git a/test/langtools/tools/javac/lint/RequiresIdentityTest.out b/test/langtools/tools/javac/lint/RequiresIdentityTest.out index febd738c2e4b2..ff490a2017bd8 100644 --- a/test/langtools/tools/javac/lint/RequiresIdentityTest.out +++ b/test/langtools/tools/javac/lint/RequiresIdentityTest.out @@ -1,38 +1,38 @@ -RequiresIdentityTest.java:10:65: compiler.warn.attempt.to.use.value.based.where.identity.expected -RequiresIdentityTest.java:11:88: compiler.warn.attempt.to.use.value.based.where.identity.expected -RequiresIdentityTest.java:14:27: compiler.warn.attempt.to.use.value.based.where.identity.expected -RequiresIdentityTest.java:15:36: compiler.warn.attempt.to.use.value.based.where.identity.expected -RequiresIdentityTest.java:16:8: compiler.warn.attempt.to.use.value.based.where.identity.expected -RequiresIdentityTest.java:17:8: compiler.warn.attempt.to.use.value.based.where.identity.expected -RequiresIdentityTest.java:18:72: compiler.warn.attempt.to.use.value.based.where.identity.expected -RequiresIdentityTest.java:18:27: compiler.warn.attempt.to.use.value.based.where.identity.expected -RequiresIdentityTest.java:22:15: compiler.warn.attempt.to.use.value.based.where.identity.expected -RequiresIdentityTest.java:25:32: compiler.warn.attempt.to.use.value.based.where.identity.expected -RequiresIdentityTest.java:26:31: compiler.warn.attempt.to.use.value.based.where.identity.expected -RequiresIdentityTest.java:27:40: compiler.warn.attempt.to.use.value.based.where.identity.expected -RequiresIdentityTest.java:29:16: compiler.warn.attempt.to.use.value.based.where.identity.expected -RequiresIdentityTest.java:30:16: compiler.warn.attempt.to.use.value.based.where.identity.expected -RequiresIdentityTest.java:31:16: compiler.warn.attempt.to.use.value.based.where.identity.expected -RequiresIdentityTest.java:32:15: compiler.warn.attempt.to.use.value.based.where.identity.expected -RequiresIdentityTest.java:35:67: compiler.warn.attempt.to.use.value.based.where.identity.expected -RequiresIdentityTest.java:38:63: compiler.warn.attempt.to.use.value.based.where.identity.expected -RequiresIdentityTest.java:41:27: compiler.warn.attempt.to.use.value.based.where.identity.expected -RequiresIdentityTest.java:46:6: compiler.warn.attempt.to.use.value.based.where.identity.expected -RequiresIdentityTest.java:46:49: compiler.warn.attempt.to.use.value.based.where.identity.expected -RequiresIdentityTest.java:48:21: compiler.warn.attempt.to.use.value.based.where.identity.expected -RequiresIdentityTest.java:50:36: compiler.warn.attempt.to.use.value.based.where.identity.expected -RequiresIdentityTest.java:53:32: compiler.warn.attempt.to.use.value.based.where.identity.expected -RequiresIdentityTest.java:54:46: compiler.warn.attempt.to.use.value.based.where.identity.expected -RequiresIdentityTest.java:58:54: compiler.warn.attempt.to.use.value.based.where.identity.expected -RequiresIdentityTest.java:63:37: compiler.warn.attempt.to.use.value.based.where.identity.expected +RequiresIdentityTest.java:10:65: compiler.warn.attempt.to.use.value.based.where.identity.expected: java.lang.RequiresIdentityHelper +RequiresIdentityTest.java:11:88: compiler.warn.attempt.to.use.value.based.where.identity.expected: java.lang.RequiresIdentityHelper.RequiresIdentityInt +RequiresIdentityTest.java:14:27: compiler.warn.attempt.to.use.value.based.where.identity.expected: java.lang.RequiresIdentityHelper +RequiresIdentityTest.java:15:36: compiler.warn.attempt.to.use.value.based.where.identity.expected: java.lang.RequiresIdentityHelper[] +RequiresIdentityTest.java:16:8: compiler.warn.attempt.to.use.value.based.where.identity.expected: java.lang.RequiresIdentityTest.Box> +RequiresIdentityTest.java:17:8: compiler.warn.attempt.to.use.value.based.where.identity.expected: java.lang.RequiresIdentityTest.Box> +RequiresIdentityTest.java:18:72: compiler.warn.attempt.to.use.value.based.where.identity.expected: java.lang.RequiresIdentityHelper +RequiresIdentityTest.java:18:27: compiler.warn.attempt.to.use.value.based.where.identity.expected: java.lang.RequiresIdentityHelper +RequiresIdentityTest.java:22:15: compiler.warn.attempt.to.use.value.based.where.identity.expected: java.lang.Integer +RequiresIdentityTest.java:25:32: compiler.warn.attempt.to.use.value.based.where.identity.expected: java.lang.RequiresIdentityHelper.RequiresIdentity2 +RequiresIdentityTest.java:26:31: compiler.warn.attempt.to.use.value.based.where.identity.expected: java.lang.RequiresIdentityHelper +RequiresIdentityTest.java:27:40: compiler.warn.attempt.to.use.value.based.where.identity.expected: java.lang.RequiresIdentityHelper[] +RequiresIdentityTest.java:29:16: compiler.warn.attempt.to.use.value.based.where.identity.expected: java.lang.Integer +RequiresIdentityTest.java:30:16: compiler.warn.attempt.to.use.value.based.where.identity.expected: java.lang.Integer +RequiresIdentityTest.java:31:16: compiler.warn.attempt.to.use.value.based.where.identity.expected: java.lang.Integer +RequiresIdentityTest.java:32:15: compiler.warn.attempt.to.use.value.based.where.identity.expected: java.lang.Integer +RequiresIdentityTest.java:35:67: compiler.warn.attempt.to.use.value.based.where.identity.expected: java.lang.RequiresIdentityHelper.RequiresIdentityInt +RequiresIdentityTest.java:38:63: compiler.warn.attempt.to.use.value.based.where.identity.expected: java.lang.RequiresIdentityHelper +RequiresIdentityTest.java:41:27: compiler.warn.attempt.to.use.value.based.where.identity.expected: java.lang.RequiresIdentityHelper +RequiresIdentityTest.java:46:6: compiler.warn.attempt.to.use.value.based.where.identity.expected: T +RequiresIdentityTest.java:46:49: compiler.warn.attempt.to.use.value.based.where.identity.expected: T +RequiresIdentityTest.java:48:21: compiler.warn.attempt.to.use.value.based.where.identity.expected: T +RequiresIdentityTest.java:50:36: compiler.warn.attempt.to.use.value.based.where.identity.expected: java.lang.RequiresIdentityHelper +RequiresIdentityTest.java:53:32: compiler.warn.attempt.to.use.value.based.where.identity.expected: java.lang.RequiresIdentityHelper.RequiresIdentityInt +RequiresIdentityTest.java:54:46: compiler.warn.attempt.to.use.value.based.where.identity.expected: java.lang.RequiresIdentityHelper.RequiresIdentityInt +RequiresIdentityTest.java:58:54: compiler.warn.attempt.to.use.value.based.where.identity.expected: java.lang.RequiresIdentityHelper +RequiresIdentityTest.java:63:37: compiler.warn.attempt.to.use.value.based.where.identity.expected: java.lang.RequiresIdentityHelper RequiresIdentityTest.java:66:37: compiler.warn.has.been.deprecated.for.removal: java.lang.Integer(int), java.lang.Integer -RequiresIdentityTest.java:66:37: compiler.warn.attempt.to.use.value.based.where.identity.expected -RequiresIdentityTest.java:66:18: compiler.warn.attempt.to.use.value.based.where.identity.expected -RequiresIdentityTest.java:73:32: compiler.warn.attempt.to.use.value.based.where.identity.expected -RequiresIdentityTest.java:77:90: compiler.warn.attempt.to.use.value.based.where.identity.expected -RequiresIdentityTest.java:78:52: compiler.warn.attempt.to.use.value.based.where.identity.expected -RequiresIdentityTest.java:79:82: compiler.warn.attempt.to.use.value.based.where.identity.expected -RequiresIdentityTest.java:79:31: compiler.warn.attempt.to.use.value.based.where.identity.expected +RequiresIdentityTest.java:66:37: compiler.warn.attempt.to.use.value.based.where.identity.expected: java.lang.RequiresIdentityHelper.MyIntFunction +RequiresIdentityTest.java:66:18: compiler.warn.attempt.to.use.value.based.where.identity.expected: java.lang.RequiresIdentityHelper.MyIntFunction +RequiresIdentityTest.java:73:32: compiler.warn.attempt.to.use.value.based.where.identity.expected: java.lang.Integer +RequiresIdentityTest.java:77:90: compiler.warn.attempt.to.use.value.based.where.identity.expected: java.lang.Integer +RequiresIdentityTest.java:78:52: compiler.warn.attempt.to.use.value.based.where.identity.expected: java.lang.Integer +RequiresIdentityTest.java:79:82: compiler.warn.attempt.to.use.value.based.where.identity.expected: java.lang.RequiresIdentityHelper +RequiresIdentityTest.java:79:31: compiler.warn.attempt.to.use.value.based.where.identity.expected: java.lang.RequiresIdentityHelper - compiler.err.warnings.and.werror - compiler.note.unchecked.filename: RequiresIdentityTest.java - compiler.note.unchecked.recompile From 84b52a64bf5778e972db05eea8c6beb1e12f7cb4 Mon Sep 17 00:00:00 2001 From: Vicente Romero Date: Fri, 25 Apr 2025 15:43:16 -0400 Subject: [PATCH 18/34] removing unneeded changes --- .../share/classes/com/sun/tools/javac/code/Types.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Types.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Types.java index 0720414555104..1bc1e078c1a97 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Types.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Types.java @@ -42,14 +42,15 @@ import com.sun.tools.javac.code.Attribute.RetentionPolicy; import com.sun.tools.javac.code.Lint.LintCategory; +import com.sun.tools.javac.code.Source.Feature; import com.sun.tools.javac.code.Type.UndetVar.InferenceBound; import com.sun.tools.javac.code.TypeMetadata.Annotations; import com.sun.tools.javac.comp.AttrContext; import com.sun.tools.javac.comp.Check; import com.sun.tools.javac.comp.Enter; import com.sun.tools.javac.comp.Env; +import com.sun.tools.javac.comp.LambdaToMethod; import com.sun.tools.javac.jvm.ClassFile; -import com.sun.tools.javac.resources.CompilerProperties; import com.sun.tools.javac.util.*; import static com.sun.tools.javac.code.BoundKind.*; From 70e0311d408004bc6f6b664a8602e82b9b1824da Mon Sep 17 00:00:00 2001 From: Vicente Romero Date: Fri, 25 Apr 2025 16:49:28 -0400 Subject: [PATCH 19/34] additional refactorings --- .../com/sun/tools/javac/comp/Attr.java | 9 ++- .../javac/lint/RequiresIdentityTest.java | 2 + .../tools/javac/lint/RequiresIdentityTest.out | 68 +++++++++---------- 3 files changed, 40 insertions(+), 39 deletions(-) diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java index 96e69e98ba725..9c4e241f41404 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java @@ -2555,7 +2555,6 @@ public void visitApply(JCMethodInvocation tree) { methName == names._this || methName == names._super; ListBuffer argtypesBuf = new ListBuffer<>(); - Symbol msym = null; if (isConstructorCall) { // Attribute arguments, yielding list of argument types. @@ -2622,17 +2621,17 @@ public void visitApply(JCMethodInvocation tree) { boolean selectSuperPrev = localEnv.info.selectSuper; localEnv.info.selectSuper = true; localEnv.info.pendingResolutionPhase = null; - msym = rs.resolveConstructor( + Symbol sym = rs.resolveConstructor( tree.meth.pos(), localEnv, site, argtypes, typeargtypes); localEnv.info.selectSuper = selectSuperPrev; // Set method symbol to resolved constructor... - TreeInfo.setSymbol(tree.meth, msym); + TreeInfo.setSymbol(tree.meth, sym); // ...and check that it is legal in the current context. // (this will also set the tree's type) Type mpt = newMethodTemplate(resultInfo.pt, argtypes, typeargtypes); - checkId(tree.meth, site, msym, localEnv, + checkId(tree.meth, site, sym, localEnv, new ResultInfo(kind, mpt)); } else if (site.hasTag(ERROR) && tree.meth.hasTag(SELECT)) { attribExpr(((JCFieldAccess) tree.meth).selected, localEnv, site); @@ -2661,7 +2660,7 @@ public void visitApply(JCMethodInvocation tree) { Type qualifier = (tree.meth.hasTag(SELECT)) ? ((JCFieldAccess) tree.meth).selected.type : env.enclClass.sym.type; - msym = TreeInfo.symbol(tree.meth); + Symbol msym = TreeInfo.symbol(tree.meth); restype = adjustMethodReturnType(msym, qualifier, methName, argtypes, restype); chk.checkRefTypes(tree.typeargs, typeargtypes); diff --git a/test/langtools/tools/javac/lint/RequiresIdentityTest.java b/test/langtools/tools/javac/lint/RequiresIdentityTest.java index a4109771ff0bb..eb9a9306e3026 100644 --- a/test/langtools/tools/javac/lint/RequiresIdentityTest.java +++ b/test/langtools/tools/javac/lint/RequiresIdentityTest.java @@ -2,6 +2,8 @@ * @test /nodynamiccopyright/ * @bug 8354556 * @summary Expand value-based class warnings to java.lang.ref API + * @compile --patch-module java.base=${test.src} RequiresIdentityHelper.java + * @compile/fail/ref=RequiresIdentityTest.out --patch-module java.base=${test.src} -Werror -XDrawDiagnostics -Xlint:identity RequiresIdentityTest.java * @compile/fail/ref=RequiresIdentityTest.out --patch-module java.base=${test.src} -Werror -XDrawDiagnostics -Xlint:identity RequiresIdentityHelper.java RequiresIdentityTest.java */ diff --git a/test/langtools/tools/javac/lint/RequiresIdentityTest.out b/test/langtools/tools/javac/lint/RequiresIdentityTest.out index ff490a2017bd8..45f5b78f2b251 100644 --- a/test/langtools/tools/javac/lint/RequiresIdentityTest.out +++ b/test/langtools/tools/javac/lint/RequiresIdentityTest.out @@ -1,38 +1,38 @@ -RequiresIdentityTest.java:10:65: compiler.warn.attempt.to.use.value.based.where.identity.expected: java.lang.RequiresIdentityHelper -RequiresIdentityTest.java:11:88: compiler.warn.attempt.to.use.value.based.where.identity.expected: java.lang.RequiresIdentityHelper.RequiresIdentityInt -RequiresIdentityTest.java:14:27: compiler.warn.attempt.to.use.value.based.where.identity.expected: java.lang.RequiresIdentityHelper -RequiresIdentityTest.java:15:36: compiler.warn.attempt.to.use.value.based.where.identity.expected: java.lang.RequiresIdentityHelper[] -RequiresIdentityTest.java:16:8: compiler.warn.attempt.to.use.value.based.where.identity.expected: java.lang.RequiresIdentityTest.Box> -RequiresIdentityTest.java:17:8: compiler.warn.attempt.to.use.value.based.where.identity.expected: java.lang.RequiresIdentityTest.Box> -RequiresIdentityTest.java:18:72: compiler.warn.attempt.to.use.value.based.where.identity.expected: java.lang.RequiresIdentityHelper -RequiresIdentityTest.java:18:27: compiler.warn.attempt.to.use.value.based.where.identity.expected: java.lang.RequiresIdentityHelper -RequiresIdentityTest.java:22:15: compiler.warn.attempt.to.use.value.based.where.identity.expected: java.lang.Integer -RequiresIdentityTest.java:25:32: compiler.warn.attempt.to.use.value.based.where.identity.expected: java.lang.RequiresIdentityHelper.RequiresIdentity2 -RequiresIdentityTest.java:26:31: compiler.warn.attempt.to.use.value.based.where.identity.expected: java.lang.RequiresIdentityHelper -RequiresIdentityTest.java:27:40: compiler.warn.attempt.to.use.value.based.where.identity.expected: java.lang.RequiresIdentityHelper[] -RequiresIdentityTest.java:29:16: compiler.warn.attempt.to.use.value.based.where.identity.expected: java.lang.Integer -RequiresIdentityTest.java:30:16: compiler.warn.attempt.to.use.value.based.where.identity.expected: java.lang.Integer +RequiresIdentityTest.java:12:65: compiler.warn.attempt.to.use.value.based.where.identity.expected: java.lang.RequiresIdentityHelper +RequiresIdentityTest.java:13:88: compiler.warn.attempt.to.use.value.based.where.identity.expected: java.lang.RequiresIdentityHelper.RequiresIdentityInt +RequiresIdentityTest.java:16:27: compiler.warn.attempt.to.use.value.based.where.identity.expected: java.lang.RequiresIdentityHelper +RequiresIdentityTest.java:17:36: compiler.warn.attempt.to.use.value.based.where.identity.expected: java.lang.RequiresIdentityHelper[] +RequiresIdentityTest.java:18:8: compiler.warn.attempt.to.use.value.based.where.identity.expected: java.lang.RequiresIdentityTest.Box> +RequiresIdentityTest.java:19:8: compiler.warn.attempt.to.use.value.based.where.identity.expected: java.lang.RequiresIdentityTest.Box> +RequiresIdentityTest.java:20:72: compiler.warn.attempt.to.use.value.based.where.identity.expected: java.lang.RequiresIdentityHelper +RequiresIdentityTest.java:20:27: compiler.warn.attempt.to.use.value.based.where.identity.expected: java.lang.RequiresIdentityHelper +RequiresIdentityTest.java:24:15: compiler.warn.attempt.to.use.value.based.where.identity.expected: java.lang.Integer +RequiresIdentityTest.java:27:32: compiler.warn.attempt.to.use.value.based.where.identity.expected: java.lang.RequiresIdentityHelper.RequiresIdentity2 +RequiresIdentityTest.java:28:31: compiler.warn.attempt.to.use.value.based.where.identity.expected: java.lang.RequiresIdentityHelper +RequiresIdentityTest.java:29:40: compiler.warn.attempt.to.use.value.based.where.identity.expected: java.lang.RequiresIdentityHelper[] RequiresIdentityTest.java:31:16: compiler.warn.attempt.to.use.value.based.where.identity.expected: java.lang.Integer -RequiresIdentityTest.java:32:15: compiler.warn.attempt.to.use.value.based.where.identity.expected: java.lang.Integer -RequiresIdentityTest.java:35:67: compiler.warn.attempt.to.use.value.based.where.identity.expected: java.lang.RequiresIdentityHelper.RequiresIdentityInt -RequiresIdentityTest.java:38:63: compiler.warn.attempt.to.use.value.based.where.identity.expected: java.lang.RequiresIdentityHelper -RequiresIdentityTest.java:41:27: compiler.warn.attempt.to.use.value.based.where.identity.expected: java.lang.RequiresIdentityHelper -RequiresIdentityTest.java:46:6: compiler.warn.attempt.to.use.value.based.where.identity.expected: T -RequiresIdentityTest.java:46:49: compiler.warn.attempt.to.use.value.based.where.identity.expected: T -RequiresIdentityTest.java:48:21: compiler.warn.attempt.to.use.value.based.where.identity.expected: T -RequiresIdentityTest.java:50:36: compiler.warn.attempt.to.use.value.based.where.identity.expected: java.lang.RequiresIdentityHelper -RequiresIdentityTest.java:53:32: compiler.warn.attempt.to.use.value.based.where.identity.expected: java.lang.RequiresIdentityHelper.RequiresIdentityInt -RequiresIdentityTest.java:54:46: compiler.warn.attempt.to.use.value.based.where.identity.expected: java.lang.RequiresIdentityHelper.RequiresIdentityInt -RequiresIdentityTest.java:58:54: compiler.warn.attempt.to.use.value.based.where.identity.expected: java.lang.RequiresIdentityHelper -RequiresIdentityTest.java:63:37: compiler.warn.attempt.to.use.value.based.where.identity.expected: java.lang.RequiresIdentityHelper -RequiresIdentityTest.java:66:37: compiler.warn.has.been.deprecated.for.removal: java.lang.Integer(int), java.lang.Integer -RequiresIdentityTest.java:66:37: compiler.warn.attempt.to.use.value.based.where.identity.expected: java.lang.RequiresIdentityHelper.MyIntFunction -RequiresIdentityTest.java:66:18: compiler.warn.attempt.to.use.value.based.where.identity.expected: java.lang.RequiresIdentityHelper.MyIntFunction -RequiresIdentityTest.java:73:32: compiler.warn.attempt.to.use.value.based.where.identity.expected: java.lang.Integer -RequiresIdentityTest.java:77:90: compiler.warn.attempt.to.use.value.based.where.identity.expected: java.lang.Integer -RequiresIdentityTest.java:78:52: compiler.warn.attempt.to.use.value.based.where.identity.expected: java.lang.Integer -RequiresIdentityTest.java:79:82: compiler.warn.attempt.to.use.value.based.where.identity.expected: java.lang.RequiresIdentityHelper -RequiresIdentityTest.java:79:31: compiler.warn.attempt.to.use.value.based.where.identity.expected: java.lang.RequiresIdentityHelper +RequiresIdentityTest.java:32:16: compiler.warn.attempt.to.use.value.based.where.identity.expected: java.lang.Integer +RequiresIdentityTest.java:33:16: compiler.warn.attempt.to.use.value.based.where.identity.expected: java.lang.Integer +RequiresIdentityTest.java:34:15: compiler.warn.attempt.to.use.value.based.where.identity.expected: java.lang.Integer +RequiresIdentityTest.java:37:67: compiler.warn.attempt.to.use.value.based.where.identity.expected: java.lang.RequiresIdentityHelper.RequiresIdentityInt +RequiresIdentityTest.java:40:63: compiler.warn.attempt.to.use.value.based.where.identity.expected: java.lang.RequiresIdentityHelper +RequiresIdentityTest.java:43:27: compiler.warn.attempt.to.use.value.based.where.identity.expected: java.lang.RequiresIdentityHelper +RequiresIdentityTest.java:48:6: compiler.warn.attempt.to.use.value.based.where.identity.expected: T +RequiresIdentityTest.java:48:49: compiler.warn.attempt.to.use.value.based.where.identity.expected: T +RequiresIdentityTest.java:50:21: compiler.warn.attempt.to.use.value.based.where.identity.expected: T +RequiresIdentityTest.java:52:36: compiler.warn.attempt.to.use.value.based.where.identity.expected: java.lang.RequiresIdentityHelper +RequiresIdentityTest.java:55:32: compiler.warn.attempt.to.use.value.based.where.identity.expected: java.lang.RequiresIdentityHelper.RequiresIdentityInt +RequiresIdentityTest.java:56:46: compiler.warn.attempt.to.use.value.based.where.identity.expected: java.lang.RequiresIdentityHelper.RequiresIdentityInt +RequiresIdentityTest.java:60:54: compiler.warn.attempt.to.use.value.based.where.identity.expected: java.lang.RequiresIdentityHelper +RequiresIdentityTest.java:65:37: compiler.warn.attempt.to.use.value.based.where.identity.expected: java.lang.RequiresIdentityHelper +RequiresIdentityTest.java:68:37: compiler.warn.has.been.deprecated.for.removal: java.lang.Integer(int), java.lang.Integer +RequiresIdentityTest.java:68:37: compiler.warn.attempt.to.use.value.based.where.identity.expected: java.lang.RequiresIdentityHelper.MyIntFunction +RequiresIdentityTest.java:68:18: compiler.warn.attempt.to.use.value.based.where.identity.expected: java.lang.RequiresIdentityHelper.MyIntFunction +RequiresIdentityTest.java:75:32: compiler.warn.attempt.to.use.value.based.where.identity.expected: java.lang.Integer +RequiresIdentityTest.java:79:90: compiler.warn.attempt.to.use.value.based.where.identity.expected: java.lang.Integer +RequiresIdentityTest.java:80:52: compiler.warn.attempt.to.use.value.based.where.identity.expected: java.lang.Integer +RequiresIdentityTest.java:81:82: compiler.warn.attempt.to.use.value.based.where.identity.expected: java.lang.RequiresIdentityHelper +RequiresIdentityTest.java:81:31: compiler.warn.attempt.to.use.value.based.where.identity.expected: java.lang.RequiresIdentityHelper - compiler.err.warnings.and.werror - compiler.note.unchecked.filename: RequiresIdentityTest.java - compiler.note.unchecked.recompile From 5e3acc9289cdde75287cd3e92c298bf5a21e816d Mon Sep 17 00:00:00 2001 From: Vicente Romero Date: Fri, 25 Apr 2025 17:55:03 -0400 Subject: [PATCH 20/34] final adjustments --- .../com/sun/tools/javac/comp/Check.java | 11 ++- .../tools/javac/resources/compiler.properties | 3 +- .../tools/javac/lint/RequiresIdentityTest.out | 68 +++++++++---------- 3 files changed, 40 insertions(+), 42 deletions(-) diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Check.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Check.java index a4a3898140ac3..e8c2ddfb8c8a3 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Check.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Check.java @@ -5737,14 +5737,14 @@ void checkRequiresIdentity(JCTree tree, Lint lint) { VarSymbol lastParam = ms.params.head; for (VarSymbol param: ms.params) { if (param.attribute(syms.requiresIdentityType.tsym) != null && argExps.head.type.isValueBased()) { - lint.logIfEnabled(argExps.head.pos(), LintWarnings.AttemptToUseValueBasedWhereIdentityExpected(argExps.head.type)); + lint.logIfEnabled(argExps.head.pos(), LintWarnings.AttemptToUseValueBasedWhereIdentityExpected); } lastParam = param; argExps = argExps.tail; } while (argExps != null && !argExps.isEmpty() && lastParam != null) { if (lastParam.attribute(syms.requiresIdentityType.tsym) != null && argExps.head.type.isValueBased()) { - lint.logIfEnabled(argExps.head.pos(), LintWarnings.AttemptToUseValueBasedWhereIdentityExpected(argExps.head.type)); + lint.logIfEnabled(argExps.head.pos(), LintWarnings.AttemptToUseValueBasedWhereIdentityExpected); } argExps = argExps.tail; } @@ -5772,7 +5772,7 @@ private boolean checkIfIdentityIsExpected(DiagnosticPosition pos, Type t, Lint l // we need to avoid recursion due to self referencing type vars or captures, this is why we need a set requiresIdentityVisitor.visit(t, new HashSet<>()); if (requiresIdentityVisitor.requiresWarning) { - lint.logIfEnabled(pos, LintWarnings.AttemptToUseValueBasedWhereIdentityExpected(t)); + lint.logIfEnabled(pos, LintWarnings.AttemptToUseValueBasedWhereIdentityExpected); return true; } } @@ -5780,7 +5780,7 @@ private boolean checkIfIdentityIsExpected(DiagnosticPosition pos, Type t, Lint l } // where - class RequiresIdentityVisitor extends Types.SimpleVisitor> { + private class RequiresIdentityVisitor extends Types.SimpleVisitor> { boolean requiresWarning = false; @Override @@ -5850,8 +5850,7 @@ private void checkIfTypeParamsRequiresIdentity(SymbolMetadata sm, if (typeParamTrees.get(ta.position.parameter_index).type.isValueBased()) lint.logIfEnabled( typeParamTrees.get(ta.position.parameter_index).pos(), - CompilerProperties.LintWarnings - .AttemptToUseValueBasedWhereIdentityExpected(typeParamTrees.get(ta.position.parameter_index).type) + CompilerProperties.LintWarnings.AttemptToUseValueBasedWhereIdentityExpected ); } } diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler.properties b/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler.properties index 531b5f2b3e280..7b0b011d2c7fe 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler.properties +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler.properties @@ -4253,10 +4253,9 @@ compiler.warn.attempt.to.synchronize.on.instance.of.value.based.class=\ compiler.warn.attempt.to.synchronize.on.instance.of.value.based.class2=\ attempt to synchronize on an instance of a value-based class -# 0: type # lint: identity compiler.warn.attempt.to.use.value.based.where.identity.expected=\ - attempt to use a value-based type, ''{0}'', where an identity type is expected + a value-based class has been used where an identity type is expected # 0: type compiler.err.enclosing.class.type.non.denotable=\ diff --git a/test/langtools/tools/javac/lint/RequiresIdentityTest.out b/test/langtools/tools/javac/lint/RequiresIdentityTest.out index 45f5b78f2b251..e18d6f6196fa6 100644 --- a/test/langtools/tools/javac/lint/RequiresIdentityTest.out +++ b/test/langtools/tools/javac/lint/RequiresIdentityTest.out @@ -1,38 +1,38 @@ -RequiresIdentityTest.java:12:65: compiler.warn.attempt.to.use.value.based.where.identity.expected: java.lang.RequiresIdentityHelper -RequiresIdentityTest.java:13:88: compiler.warn.attempt.to.use.value.based.where.identity.expected: java.lang.RequiresIdentityHelper.RequiresIdentityInt -RequiresIdentityTest.java:16:27: compiler.warn.attempt.to.use.value.based.where.identity.expected: java.lang.RequiresIdentityHelper -RequiresIdentityTest.java:17:36: compiler.warn.attempt.to.use.value.based.where.identity.expected: java.lang.RequiresIdentityHelper[] -RequiresIdentityTest.java:18:8: compiler.warn.attempt.to.use.value.based.where.identity.expected: java.lang.RequiresIdentityTest.Box> -RequiresIdentityTest.java:19:8: compiler.warn.attempt.to.use.value.based.where.identity.expected: java.lang.RequiresIdentityTest.Box> -RequiresIdentityTest.java:20:72: compiler.warn.attempt.to.use.value.based.where.identity.expected: java.lang.RequiresIdentityHelper -RequiresIdentityTest.java:20:27: compiler.warn.attempt.to.use.value.based.where.identity.expected: java.lang.RequiresIdentityHelper -RequiresIdentityTest.java:24:15: compiler.warn.attempt.to.use.value.based.where.identity.expected: java.lang.Integer -RequiresIdentityTest.java:27:32: compiler.warn.attempt.to.use.value.based.where.identity.expected: java.lang.RequiresIdentityHelper.RequiresIdentity2 -RequiresIdentityTest.java:28:31: compiler.warn.attempt.to.use.value.based.where.identity.expected: java.lang.RequiresIdentityHelper -RequiresIdentityTest.java:29:40: compiler.warn.attempt.to.use.value.based.where.identity.expected: java.lang.RequiresIdentityHelper[] -RequiresIdentityTest.java:31:16: compiler.warn.attempt.to.use.value.based.where.identity.expected: java.lang.Integer -RequiresIdentityTest.java:32:16: compiler.warn.attempt.to.use.value.based.where.identity.expected: java.lang.Integer -RequiresIdentityTest.java:33:16: compiler.warn.attempt.to.use.value.based.where.identity.expected: java.lang.Integer -RequiresIdentityTest.java:34:15: compiler.warn.attempt.to.use.value.based.where.identity.expected: java.lang.Integer -RequiresIdentityTest.java:37:67: compiler.warn.attempt.to.use.value.based.where.identity.expected: java.lang.RequiresIdentityHelper.RequiresIdentityInt -RequiresIdentityTest.java:40:63: compiler.warn.attempt.to.use.value.based.where.identity.expected: java.lang.RequiresIdentityHelper -RequiresIdentityTest.java:43:27: compiler.warn.attempt.to.use.value.based.where.identity.expected: java.lang.RequiresIdentityHelper -RequiresIdentityTest.java:48:6: compiler.warn.attempt.to.use.value.based.where.identity.expected: T -RequiresIdentityTest.java:48:49: compiler.warn.attempt.to.use.value.based.where.identity.expected: T -RequiresIdentityTest.java:50:21: compiler.warn.attempt.to.use.value.based.where.identity.expected: T -RequiresIdentityTest.java:52:36: compiler.warn.attempt.to.use.value.based.where.identity.expected: java.lang.RequiresIdentityHelper -RequiresIdentityTest.java:55:32: compiler.warn.attempt.to.use.value.based.where.identity.expected: java.lang.RequiresIdentityHelper.RequiresIdentityInt -RequiresIdentityTest.java:56:46: compiler.warn.attempt.to.use.value.based.where.identity.expected: java.lang.RequiresIdentityHelper.RequiresIdentityInt -RequiresIdentityTest.java:60:54: compiler.warn.attempt.to.use.value.based.where.identity.expected: java.lang.RequiresIdentityHelper -RequiresIdentityTest.java:65:37: compiler.warn.attempt.to.use.value.based.where.identity.expected: java.lang.RequiresIdentityHelper +RequiresIdentityTest.java:12:65: compiler.warn.attempt.to.use.value.based.where.identity.expected +RequiresIdentityTest.java:13:88: compiler.warn.attempt.to.use.value.based.where.identity.expected +RequiresIdentityTest.java:16:27: compiler.warn.attempt.to.use.value.based.where.identity.expected +RequiresIdentityTest.java:17:36: compiler.warn.attempt.to.use.value.based.where.identity.expected +RequiresIdentityTest.java:18:8: compiler.warn.attempt.to.use.value.based.where.identity.expected +RequiresIdentityTest.java:19:8: compiler.warn.attempt.to.use.value.based.where.identity.expected +RequiresIdentityTest.java:20:72: compiler.warn.attempt.to.use.value.based.where.identity.expected +RequiresIdentityTest.java:20:27: compiler.warn.attempt.to.use.value.based.where.identity.expected +RequiresIdentityTest.java:24:15: compiler.warn.attempt.to.use.value.based.where.identity.expected +RequiresIdentityTest.java:27:32: compiler.warn.attempt.to.use.value.based.where.identity.expected +RequiresIdentityTest.java:28:31: compiler.warn.attempt.to.use.value.based.where.identity.expected +RequiresIdentityTest.java:29:40: compiler.warn.attempt.to.use.value.based.where.identity.expected +RequiresIdentityTest.java:31:16: compiler.warn.attempt.to.use.value.based.where.identity.expected +RequiresIdentityTest.java:32:16: compiler.warn.attempt.to.use.value.based.where.identity.expected +RequiresIdentityTest.java:33:16: compiler.warn.attempt.to.use.value.based.where.identity.expected +RequiresIdentityTest.java:34:15: compiler.warn.attempt.to.use.value.based.where.identity.expected +RequiresIdentityTest.java:37:67: compiler.warn.attempt.to.use.value.based.where.identity.expected +RequiresIdentityTest.java:40:63: compiler.warn.attempt.to.use.value.based.where.identity.expected +RequiresIdentityTest.java:43:27: compiler.warn.attempt.to.use.value.based.where.identity.expected +RequiresIdentityTest.java:48:6: compiler.warn.attempt.to.use.value.based.where.identity.expected +RequiresIdentityTest.java:48:49: compiler.warn.attempt.to.use.value.based.where.identity.expected +RequiresIdentityTest.java:50:21: compiler.warn.attempt.to.use.value.based.where.identity.expected +RequiresIdentityTest.java:52:36: compiler.warn.attempt.to.use.value.based.where.identity.expected +RequiresIdentityTest.java:55:32: compiler.warn.attempt.to.use.value.based.where.identity.expected +RequiresIdentityTest.java:56:46: compiler.warn.attempt.to.use.value.based.where.identity.expected +RequiresIdentityTest.java:60:54: compiler.warn.attempt.to.use.value.based.where.identity.expected +RequiresIdentityTest.java:65:37: compiler.warn.attempt.to.use.value.based.where.identity.expected RequiresIdentityTest.java:68:37: compiler.warn.has.been.deprecated.for.removal: java.lang.Integer(int), java.lang.Integer -RequiresIdentityTest.java:68:37: compiler.warn.attempt.to.use.value.based.where.identity.expected: java.lang.RequiresIdentityHelper.MyIntFunction -RequiresIdentityTest.java:68:18: compiler.warn.attempt.to.use.value.based.where.identity.expected: java.lang.RequiresIdentityHelper.MyIntFunction -RequiresIdentityTest.java:75:32: compiler.warn.attempt.to.use.value.based.where.identity.expected: java.lang.Integer -RequiresIdentityTest.java:79:90: compiler.warn.attempt.to.use.value.based.where.identity.expected: java.lang.Integer -RequiresIdentityTest.java:80:52: compiler.warn.attempt.to.use.value.based.where.identity.expected: java.lang.Integer -RequiresIdentityTest.java:81:82: compiler.warn.attempt.to.use.value.based.where.identity.expected: java.lang.RequiresIdentityHelper -RequiresIdentityTest.java:81:31: compiler.warn.attempt.to.use.value.based.where.identity.expected: java.lang.RequiresIdentityHelper +RequiresIdentityTest.java:68:37: compiler.warn.attempt.to.use.value.based.where.identity.expected +RequiresIdentityTest.java:68:18: compiler.warn.attempt.to.use.value.based.where.identity.expected +RequiresIdentityTest.java:75:32: compiler.warn.attempt.to.use.value.based.where.identity.expected +RequiresIdentityTest.java:79:90: compiler.warn.attempt.to.use.value.based.where.identity.expected +RequiresIdentityTest.java:80:52: compiler.warn.attempt.to.use.value.based.where.identity.expected +RequiresIdentityTest.java:81:82: compiler.warn.attempt.to.use.value.based.where.identity.expected +RequiresIdentityTest.java:81:31: compiler.warn.attempt.to.use.value.based.where.identity.expected - compiler.err.warnings.and.werror - compiler.note.unchecked.filename: RequiresIdentityTest.java - compiler.note.unchecked.recompile From 27104b43092ba4aa87b282fd69824ef5d5276139 Mon Sep 17 00:00:00 2001 From: Jan Lahoda Date: Tue, 29 Apr 2025 15:08:32 +0200 Subject: [PATCH 21/34] Improving the behavior of the PrintingProcessor. --- .../javac/processing/PrintingProcessor.java | 30 ++++++++-- ...XprintTypeAnnotationsAndTypeVarBounds.java | 59 +++++++++++++++++++ .../XprintTypeAnnotationsAndTypeVarBounds.out | 44 ++++++++++++++ 3 files changed, 129 insertions(+), 4 deletions(-) create mode 100644 test/langtools/tools/javac/processing/options/XprintTypeAnnotationsAndTypeVarBounds.java create mode 100644 test/langtools/tools/javac/processing/options/XprintTypeAnnotationsAndTypeVarBounds.out diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/processing/PrintingProcessor.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/processing/PrintingProcessor.java index 950ad49236a1c..117809273a4dd 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/processing/PrintingProcessor.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/processing/PrintingProcessor.java @@ -259,9 +259,7 @@ public PrintingElementVisitor visitType(TypeElement e, Boolean p) { if (kind == CLASS) { TypeMirror supertype = e.getSuperclass(); if (supertype.getKind() != TypeKind.NONE) { - TypeElement e2 = (TypeElement) - ((DeclaredType) supertype).asElement(); - if (e2.getSuperclass().getKind() != TypeKind.NONE) + if (!isUnimportantObjectType(supertype)) writer.print(" extends " + supertype); } } @@ -524,13 +522,29 @@ private void printFormalTypeParameters(Parameterizable e, List typeParams = e.getTypeParameters(); if (!typeParams.isEmpty()) { writer.print(typeParams.stream() - .map(tpe -> annotationsToString(tpe) + tpe.toString() + " extends " + tpe.getBounds().stream().map(t -> t.toString()).collect(Collectors.joining(" & "))) + .map(tpe -> annotationsToString(tpe) + tpe.toString() + printTypeVariableBoundsIfNeeded(tpe)) .collect(Collectors.joining(", ", "<", ">"))); if (pad) writer.print(" "); } } + private String printTypeVariableBoundsIfNeeded(TypeParameterElement tpe) { + List printableBounds = + tpe.getBounds() + .stream() + .filter(type -> !isUnimportantObjectType(type)) + .toList(); + + if (printableBounds.isEmpty()) { + return ""; + } + + return " extends " + printableBounds.stream() + .map(t -> t.toString()) + .collect(Collectors.joining(" & ")); + } + private String annotationsToString(Element e) { List annotations = e.getAnnotationMirrors(); return annotations.isEmpty() ? @@ -770,5 +784,13 @@ private void indent() { writer.print(spaces[indentation]); } + private boolean isUnimportantObjectType(TypeMirror type) { + if (!type.getAnnotationMirrors().isEmpty()) { + return false; + } + TypeElement e2 = (TypeElement) + ((DeclaredType) type).asElement(); + return e2.getSuperclass().getKind() == TypeKind.NONE; + } } } diff --git a/test/langtools/tools/javac/processing/options/XprintTypeAnnotationsAndTypeVarBounds.java b/test/langtools/tools/javac/processing/options/XprintTypeAnnotationsAndTypeVarBounds.java new file mode 100644 index 0000000000000..94de4a30a29db --- /dev/null +++ b/test/langtools/tools/javac/processing/options/XprintTypeAnnotationsAndTypeVarBounds.java @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 9999999 + * @summary Verify annotated supertypes and type variable bounds are printed properly + * @compile/ref=XprintTypeAnnotationsAndTypeVarBounds.out -Xprint XprintTypeAnnotationsAndTypeVarBounds.java + */ + +import java.lang.annotation.*; + +class AnnotatedObjectSuperType extends @TA Object { +} + +class UnannotatedObjectSuperType extends Object { +} + +class TypeVariableWithAnnotation1<@TA T> { +} + +class TypeVariableWithAnnotation2<@TA T extends Object> { +} + +class TypeVariableWithBound1 { +} + +class TypeVariableWithBound2 { +} + +class TypeVariableWithBound3 { +} + +class TypeVariableWithBound4 { +} + +@Target(ElementType.TYPE_USE) +@interface TA { +} diff --git a/test/langtools/tools/javac/processing/options/XprintTypeAnnotationsAndTypeVarBounds.out b/test/langtools/tools/javac/processing/options/XprintTypeAnnotationsAndTypeVarBounds.out new file mode 100644 index 0000000000000..7ac6db575ad2b --- /dev/null +++ b/test/langtools/tools/javac/processing/options/XprintTypeAnnotationsAndTypeVarBounds.out @@ -0,0 +1,44 @@ + +class AnnotatedObjectSuperType extends java.lang.@TA Object { + + AnnotatedObjectSuperType(); +} + +class UnannotatedObjectSuperType { + + UnannotatedObjectSuperType(); +} + +class TypeVariableWithAnnotation1<@TA T> { + + TypeVariableWithAnnotation1(); +} + +class TypeVariableWithAnnotation2<@TA T> { + + TypeVariableWithAnnotation2(); +} + +class TypeVariableWithBound1 { + + TypeVariableWithBound1(); +} + +class TypeVariableWithBound2 { + + TypeVariableWithBound2(); +} + +class TypeVariableWithBound3 { + + TypeVariableWithBound3(); +} + +class TypeVariableWithBound4 { + + TypeVariableWithBound4(); +} + +@java.lang.annotation.Target({TYPE_USE}) +@interface TA { +} From 1134ffeed9521bf75a891235588efe3961e904ea Mon Sep 17 00:00:00 2001 From: Vicente Romero Date: Fri, 2 May 2025 15:28:31 -0400 Subject: [PATCH 22/34] updating test --- test/langtools/tools/javac/lint/RequiresIdentityTest.java | 2 +- test/langtools/tools/javac/lint/RequiresIdentityTest.out | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/test/langtools/tools/javac/lint/RequiresIdentityTest.java b/test/langtools/tools/javac/lint/RequiresIdentityTest.java index eb9a9306e3026..d88b48b0a44aa 100644 --- a/test/langtools/tools/javac/lint/RequiresIdentityTest.java +++ b/test/langtools/tools/javac/lint/RequiresIdentityTest.java @@ -64,7 +64,7 @@ void m3() {} void m4() { this.>m3(); } - + @SuppressWarnings("removal") MyIntFunction field6 = Integer::new; // two warnings here class Run { diff --git a/test/langtools/tools/javac/lint/RequiresIdentityTest.out b/test/langtools/tools/javac/lint/RequiresIdentityTest.out index e18d6f6196fa6..32226dbe63a2b 100644 --- a/test/langtools/tools/javac/lint/RequiresIdentityTest.out +++ b/test/langtools/tools/javac/lint/RequiresIdentityTest.out @@ -25,7 +25,6 @@ RequiresIdentityTest.java:55:32: compiler.warn.attempt.to.use.value.based.where. RequiresIdentityTest.java:56:46: compiler.warn.attempt.to.use.value.based.where.identity.expected RequiresIdentityTest.java:60:54: compiler.warn.attempt.to.use.value.based.where.identity.expected RequiresIdentityTest.java:65:37: compiler.warn.attempt.to.use.value.based.where.identity.expected -RequiresIdentityTest.java:68:37: compiler.warn.has.been.deprecated.for.removal: java.lang.Integer(int), java.lang.Integer RequiresIdentityTest.java:68:37: compiler.warn.attempt.to.use.value.based.where.identity.expected RequiresIdentityTest.java:68:18: compiler.warn.attempt.to.use.value.based.where.identity.expected RequiresIdentityTest.java:75:32: compiler.warn.attempt.to.use.value.based.where.identity.expected @@ -37,4 +36,4 @@ RequiresIdentityTest.java:81:31: compiler.warn.attempt.to.use.value.based.where. - compiler.note.unchecked.filename: RequiresIdentityTest.java - compiler.note.unchecked.recompile 1 error -35 warnings +34 warnings From 60f1f53b18d065f110936e71b7430d7e365881d4 Mon Sep 17 00:00:00 2001 From: Vicente Romero Date: Fri, 2 May 2025 15:40:45 -0400 Subject: [PATCH 23/34] changes to test --- test/langtools/tools/javac/lint/RequiresIdentityTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/langtools/tools/javac/lint/RequiresIdentityTest.java b/test/langtools/tools/javac/lint/RequiresIdentityTest.java index d88b48b0a44aa..eb9a9306e3026 100644 --- a/test/langtools/tools/javac/lint/RequiresIdentityTest.java +++ b/test/langtools/tools/javac/lint/RequiresIdentityTest.java @@ -64,7 +64,7 @@ void m3() {} void m4() { this.>m3(); } - @SuppressWarnings("removal") + MyIntFunction field6 = Integer::new; // two warnings here class Run { From ad7f617d0229a17405246a04c81f36325e8f4c25 Mon Sep 17 00:00:00 2001 From: Vicente Romero Date: Mon, 5 May 2025 12:24:04 -0400 Subject: [PATCH 24/34] addressing review comment --- .../share/classes/com/sun/tools/javac/code/Type.java | 2 +- .../share/classes/com/sun/tools/javac/comp/Attr.java | 2 +- .../share/classes/com/sun/tools/javac/comp/Check.java | 9 ++++++--- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Type.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Type.java index dd96ba3bcfc5e..fa0220b1e1fdd 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Type.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Type.java @@ -666,7 +666,7 @@ public boolean isFinal() { } public boolean isValueBased() { - return (tsym.flags_field & VALUE_BASED) != 0; + return tsym != null && (tsym.flags_field & VALUE_BASED) != 0; } /** diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java index 9c4e241f41404..5b4d1e7d58498 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java @@ -1948,7 +1948,7 @@ private Symbol enumConstant(JCTree tree, Type enumType) { public void visitSynchronized(JCSynchronized tree) { chk.checkRefType(tree.pos(), attribExpr(tree.lock, env)); - if (tree.lock.type.isValueBased()) { + if (tree.lock.type != null && tree.lock.type.isValueBased()) { if (env.info.lint.isEnabled(LintCategory.SYNCHRONIZATION)) { env.info.lint.logIfEnabled(tree.pos(), LintWarnings.AttemptToSynchronizeOnInstanceOfValueBasedClass); } else { diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Check.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Check.java index 519c01c67c881..b4c363566a7d9 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Check.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Check.java @@ -5822,7 +5822,8 @@ public Void visitClassType(ClassType t, Set seen) { if (sm != null && !t.getTypeArguments().isEmpty()) { for (Attribute.TypeCompound ta: sm.getTypeAttributes().stream() .filter(ta -> ta.type.tsym == syms.requiresIdentityType.tsym).toList()) { - if (t.getTypeArguments().get(ta.position.parameter_index).isValueBased()) { + Type type = t.getTypeArguments().get(ta.position.parameter_index); + if (type != null && type.isValueBased()) { requiresWarning = true; return null; } @@ -5846,12 +5847,14 @@ private void checkIfTypeParamsRequiresIdentity(SymbolMetadata sm, } if (sm != null) for (Attribute.TypeCompound ta : sm.getTypeAttributes().stream() - .filter(ta -> ta.type.tsym == syms.requiresIdentityType.tsym).toList()) - if (typeParamTrees.get(ta.position.parameter_index).type.isValueBased()) + .filter(ta -> ta.type.tsym == syms.requiresIdentityType.tsym).toList()) { + Type paramType = typeParamTrees.get(ta.position.parameter_index).type; + if (paramType != null && paramType.isValueBased()) lint.logIfEnabled( typeParamTrees.get(ta.position.parameter_index).pos(), CompilerProperties.LintWarnings.AttemptToUseValueBasedWhereIdentityExpected ); + } } } } From d52ec33b48bd49da6e02b8c9fe83cd831c85a6c9 Mon Sep 17 00:00:00 2001 From: Vicente Romero Date: Thu, 8 May 2025 18:15:12 -0400 Subject: [PATCH 25/34] documentation and adding alias to lint categories --- .../com/sun/tools/javac/code/Lint.java | 50 +++++++------- .../com/sun/tools/javac/comp/Attr.java | 6 +- .../tools/javac/resources/compiler.properties | 6 +- .../share/classes/module-info.java | 1 + src/jdk.compiler/share/man/javac.md | 3 + .../T8003967/DetectMutableStaticFields.java | 1 + .../javac/lint/RequiresIdentityTest.java | 3 + .../tools/javac/lint/RequiresIdentityTest.out | 66 +++++++++---------- .../javac/lint/RequiresIdentityTest2.out | 2 + 9 files changed, 73 insertions(+), 65 deletions(-) create mode 100644 test/langtools/tools/javac/lint/RequiresIdentityTest2.out diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Lint.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Lint.java index 4f94508e1ee3d..7b6cb7443c251 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Lint.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Lint.java @@ -262,6 +262,11 @@ public enum LintCategory { */ FINALLY("finally"), + /** + * Warn about uses of @ValueBased classes where an identity class is expected. + */ + IDENTITY("identity"), + /** * Warn about use of incubating modules. * @@ -367,12 +372,7 @@ public enum LintCategory { /** * Warn about synchronization attempts on instances of @ValueBased classes. */ - SYNCHRONIZATION("synchronization"), - - /** - * Warn about uses of @ValueBased classes where an identity class is expected. - */ - IDENTITY("identity"), + SYNCHRONIZATION("synchronization", IDENTITY), /** * Warn about issues relating to use of text blocks @@ -417,9 +417,22 @@ public enum LintCategory { } LintCategory(String option, boolean annotationSuppression) { + this(option, annotationSuppression, null); + } + + LintCategory(String option, LintCategory alias) { + this(option, true, alias); + } + + LintCategory(String option, boolean annotationSuppression, LintCategory alias) { this.option = option; this.annotationSuppression = annotationSuppression; + this.alias = alias; map.put(option, this); + // we need to do this as forward references are not allowed + if (alias != null) { + alias.alias = this; + } } /** @@ -441,6 +454,8 @@ public static EnumSet newEmptySet() { /** Does this category support being suppressed by the {@code @SuppressWarnings} annotation? */ public final boolean annotationSuppression; + + public LintCategory alias; } /** @@ -450,7 +465,7 @@ public static EnumSet newEmptySet() { */ public boolean isEnabled(LintCategory lc) { initializeRootIfNeeded(); - return values.contains(lc); + return values.contains(lc) || (lc.alias != null && values.contains(lc.alias)); } /** @@ -461,7 +476,7 @@ public boolean isEnabled(LintCategory lc) { */ public boolean isSuppressed(LintCategory lc) { initializeRootIfNeeded(); - return suppressedValues.contains(lc); + return suppressedValues.contains(lc) || (lc.alias != null && suppressedValues.contains(lc.alias)); } /** @@ -502,20 +517,6 @@ public EnumSet suppressionsFrom(Symbol symbol) { return suppressions; } - /** - * Retrieve the recognized lint categories suppressed by the given @SuppressWarnings annotation. - * - * @param annotation @SuppressWarnings annotation, or null - * @return set of lint categories, possibly empty but never null - */ - private EnumSet suppressionsFrom(JCAnnotation annotation) { - initializeSymbolsIfNeeded(); - if (annotation == null) - return LintCategory.newEmptySet(); - Assert.check(annotation.attribute.type.tsym == syms.suppressWarningsType.tsym); - return suppressionsFrom(Stream.of(annotation).map(anno -> anno.attribute)); - } - // Find the @SuppressWarnings annotation in the given stream and extract the recognized suppressions private EnumSet suppressionsFrom(Stream attributes) { initializeSymbolsIfNeeded(); @@ -537,6 +538,11 @@ private EnumSet suppressionsFrom(Attribute.Compound suppressWarnin .filter(lc -> lc.annotationSuppression) .ifPresent(result::add); } + for (LintCategory lc : result) { + if (lc.alias != null) { + result.add(lc.alias); + } + } return result; } diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java index 4f094f52e9718..0888e8264d36e 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java @@ -1949,11 +1949,7 @@ private Symbol enumConstant(JCTree tree, Type enumType) { public void visitSynchronized(JCSynchronized tree) { chk.checkRefType(tree.pos(), attribExpr(tree.lock, env)); if (tree.lock.type != null && tree.lock.type.isValueBased()) { - if (env.info.lint.isEnabled(LintCategory.SYNCHRONIZATION)) { - env.info.lint.logIfEnabled(tree.pos(), LintWarnings.AttemptToSynchronizeOnInstanceOfValueBasedClass); - } else { - env.info.lint.logIfEnabled(tree.pos(), LintWarnings.AttemptToSynchronizeOnInstanceOfValueBasedClass2); - } + env.info.lint.logIfEnabled(tree.pos(), LintWarnings.AttemptToSynchronizeOnInstanceOfValueBasedClass); } attribStat(tree.body, env); result = null; diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler.properties b/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler.properties index 1ab824fa256f0..264977a5d5c44 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler.properties +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler.properties @@ -4245,12 +4245,8 @@ compiler.err.incorrect.number.of.nested.patterns=\ compiler.warn.declared.using.preview=\ {0} {1} is declared using a preview feature, which may be removed in a future release. -# lint: synchronization -compiler.warn.attempt.to.synchronize.on.instance.of.value.based.class=\ - attempt to synchronize on an instance of a value-based class - # lint: identity -compiler.warn.attempt.to.synchronize.on.instance.of.value.based.class2=\ +compiler.warn.attempt.to.synchronize.on.instance.of.value.based.class=\ attempt to synchronize on an instance of a value-based class # lint: identity diff --git a/src/jdk.compiler/share/classes/module-info.java b/src/jdk.compiler/share/classes/module-info.java index 60a9ae0e476b3..d32bf3d49f5d6 100644 --- a/src/jdk.compiler/share/classes/module-info.java +++ b/src/jdk.compiler/share/classes/module-info.java @@ -164,6 +164,7 @@ * {@code fallthrough} falling through from one case of a {@code switch} statement to * the next * {@code finally} {@code finally} clauses that do not terminate normally + * {@code identity} use of a value-based class where an identity class is expected * {@code incubating} use of incubating modules * {@code lossy-conversions} possible lossy conversions in compound assignment * {@code missing-explicit-ctor} missing explicit constructors in public and protected classes diff --git a/src/jdk.compiler/share/man/javac.md b/src/jdk.compiler/share/man/javac.md index f951ea0def3aa..45d8e19538a16 100644 --- a/src/jdk.compiler/share/man/javac.md +++ b/src/jdk.compiler/share/man/javac.md @@ -596,6 +596,9 @@ file system locations may be directories, JAR files or JMOD files. - `finally`: Warns about `finally` clauses that do not terminate normally. + - `identity`: Warns about use of a value-based class where an identity + class is expected + - `incubating`: Warns about the use of incubating modules. - `lossy-conversions`: Warns about possible lossy conversions diff --git a/test/langtools/tools/javac/T8003967/DetectMutableStaticFields.java b/test/langtools/tools/javac/T8003967/DetectMutableStaticFields.java index ef3e400481d8a..4c49cbdb4eaf6 100644 --- a/test/langtools/tools/javac/T8003967/DetectMutableStaticFields.java +++ b/test/langtools/tools/javac/T8003967/DetectMutableStaticFields.java @@ -93,6 +93,7 @@ private static void ignore(String className, String... fields) { ignore("com/sun/tools/javac/util/JavacMessages", "defaultBundle", "defaultMessages"); ignore("com/sun/tools/javac/file/JRTIndex", "sharedInstance"); ignore("com/sun/tools/javac/main/JavaCompiler", "versionRB"); + ignore("com/sun/tools/javac/code/Lint$LintCategory", "alias"); ignore("com/sun/tools/javac/code/Type", "moreInfo"); ignore("com/sun/tools/javac/util/SharedNameTable", "freelist"); ignore("com/sun/tools/javac/util/Log", "useRawMessages"); diff --git a/test/langtools/tools/javac/lint/RequiresIdentityTest.java b/test/langtools/tools/javac/lint/RequiresIdentityTest.java index eb9a9306e3026..9d319f539ea0e 100644 --- a/test/langtools/tools/javac/lint/RequiresIdentityTest.java +++ b/test/langtools/tools/javac/lint/RequiresIdentityTest.java @@ -4,11 +4,14 @@ * @summary Expand value-based class warnings to java.lang.ref API * @compile --patch-module java.base=${test.src} RequiresIdentityHelper.java * @compile/fail/ref=RequiresIdentityTest.out --patch-module java.base=${test.src} -Werror -XDrawDiagnostics -Xlint:identity RequiresIdentityTest.java + * @compile/ref=RequiresIdentityTest2.out --patch-module java.base=${test.src} -Werror -XDrawDiagnostics -Xlint:-identity RequiresIdentityTest.java * @compile/fail/ref=RequiresIdentityTest.out --patch-module java.base=${test.src} -Werror -XDrawDiagnostics -Xlint:identity RequiresIdentityHelper.java RequiresIdentityTest.java + * @compile/ref=RequiresIdentityTest2.out --patch-module java.base=${test.src} -Werror -XDrawDiagnostics -Xlint:-identity RequiresIdentityHelper.java RequiresIdentityTest.java */ package java.lang; +@SuppressWarnings("deprecation") public class RequiresIdentityTest extends RequiresIdentityHelper // should warn implements RequiresIdentityHelper.RequiresIdentityInt { // should warn class Box {} diff --git a/test/langtools/tools/javac/lint/RequiresIdentityTest.out b/test/langtools/tools/javac/lint/RequiresIdentityTest.out index 32226dbe63a2b..abc3ebda4f585 100644 --- a/test/langtools/tools/javac/lint/RequiresIdentityTest.out +++ b/test/langtools/tools/javac/lint/RequiresIdentityTest.out @@ -1,37 +1,37 @@ -RequiresIdentityTest.java:12:65: compiler.warn.attempt.to.use.value.based.where.identity.expected -RequiresIdentityTest.java:13:88: compiler.warn.attempt.to.use.value.based.where.identity.expected -RequiresIdentityTest.java:16:27: compiler.warn.attempt.to.use.value.based.where.identity.expected -RequiresIdentityTest.java:17:36: compiler.warn.attempt.to.use.value.based.where.identity.expected -RequiresIdentityTest.java:18:8: compiler.warn.attempt.to.use.value.based.where.identity.expected -RequiresIdentityTest.java:19:8: compiler.warn.attempt.to.use.value.based.where.identity.expected -RequiresIdentityTest.java:20:72: compiler.warn.attempt.to.use.value.based.where.identity.expected -RequiresIdentityTest.java:20:27: compiler.warn.attempt.to.use.value.based.where.identity.expected -RequiresIdentityTest.java:24:15: compiler.warn.attempt.to.use.value.based.where.identity.expected -RequiresIdentityTest.java:27:32: compiler.warn.attempt.to.use.value.based.where.identity.expected -RequiresIdentityTest.java:28:31: compiler.warn.attempt.to.use.value.based.where.identity.expected -RequiresIdentityTest.java:29:40: compiler.warn.attempt.to.use.value.based.where.identity.expected -RequiresIdentityTest.java:31:16: compiler.warn.attempt.to.use.value.based.where.identity.expected -RequiresIdentityTest.java:32:16: compiler.warn.attempt.to.use.value.based.where.identity.expected -RequiresIdentityTest.java:33:16: compiler.warn.attempt.to.use.value.based.where.identity.expected -RequiresIdentityTest.java:34:15: compiler.warn.attempt.to.use.value.based.where.identity.expected -RequiresIdentityTest.java:37:67: compiler.warn.attempt.to.use.value.based.where.identity.expected -RequiresIdentityTest.java:40:63: compiler.warn.attempt.to.use.value.based.where.identity.expected -RequiresIdentityTest.java:43:27: compiler.warn.attempt.to.use.value.based.where.identity.expected -RequiresIdentityTest.java:48:6: compiler.warn.attempt.to.use.value.based.where.identity.expected -RequiresIdentityTest.java:48:49: compiler.warn.attempt.to.use.value.based.where.identity.expected -RequiresIdentityTest.java:50:21: compiler.warn.attempt.to.use.value.based.where.identity.expected -RequiresIdentityTest.java:52:36: compiler.warn.attempt.to.use.value.based.where.identity.expected -RequiresIdentityTest.java:55:32: compiler.warn.attempt.to.use.value.based.where.identity.expected -RequiresIdentityTest.java:56:46: compiler.warn.attempt.to.use.value.based.where.identity.expected -RequiresIdentityTest.java:60:54: compiler.warn.attempt.to.use.value.based.where.identity.expected -RequiresIdentityTest.java:65:37: compiler.warn.attempt.to.use.value.based.where.identity.expected +RequiresIdentityTest.java:15:65: compiler.warn.attempt.to.use.value.based.where.identity.expected +RequiresIdentityTest.java:16:88: compiler.warn.attempt.to.use.value.based.where.identity.expected +RequiresIdentityTest.java:19:27: compiler.warn.attempt.to.use.value.based.where.identity.expected +RequiresIdentityTest.java:20:36: compiler.warn.attempt.to.use.value.based.where.identity.expected +RequiresIdentityTest.java:21:8: compiler.warn.attempt.to.use.value.based.where.identity.expected +RequiresIdentityTest.java:22:8: compiler.warn.attempt.to.use.value.based.where.identity.expected +RequiresIdentityTest.java:23:72: compiler.warn.attempt.to.use.value.based.where.identity.expected +RequiresIdentityTest.java:23:27: compiler.warn.attempt.to.use.value.based.where.identity.expected +RequiresIdentityTest.java:27:15: compiler.warn.attempt.to.use.value.based.where.identity.expected +RequiresIdentityTest.java:30:32: compiler.warn.attempt.to.use.value.based.where.identity.expected +RequiresIdentityTest.java:31:31: compiler.warn.attempt.to.use.value.based.where.identity.expected +RequiresIdentityTest.java:32:40: compiler.warn.attempt.to.use.value.based.where.identity.expected +RequiresIdentityTest.java:34:16: compiler.warn.attempt.to.use.value.based.where.identity.expected +RequiresIdentityTest.java:35:16: compiler.warn.attempt.to.use.value.based.where.identity.expected +RequiresIdentityTest.java:36:16: compiler.warn.attempt.to.use.value.based.where.identity.expected +RequiresIdentityTest.java:37:15: compiler.warn.attempt.to.use.value.based.where.identity.expected +RequiresIdentityTest.java:40:67: compiler.warn.attempt.to.use.value.based.where.identity.expected +RequiresIdentityTest.java:43:63: compiler.warn.attempt.to.use.value.based.where.identity.expected +RequiresIdentityTest.java:46:27: compiler.warn.attempt.to.use.value.based.where.identity.expected +RequiresIdentityTest.java:51:6: compiler.warn.attempt.to.use.value.based.where.identity.expected +RequiresIdentityTest.java:51:49: compiler.warn.attempt.to.use.value.based.where.identity.expected +RequiresIdentityTest.java:53:21: compiler.warn.attempt.to.use.value.based.where.identity.expected +RequiresIdentityTest.java:55:36: compiler.warn.attempt.to.use.value.based.where.identity.expected +RequiresIdentityTest.java:58:32: compiler.warn.attempt.to.use.value.based.where.identity.expected +RequiresIdentityTest.java:59:46: compiler.warn.attempt.to.use.value.based.where.identity.expected +RequiresIdentityTest.java:63:54: compiler.warn.attempt.to.use.value.based.where.identity.expected RequiresIdentityTest.java:68:37: compiler.warn.attempt.to.use.value.based.where.identity.expected -RequiresIdentityTest.java:68:18: compiler.warn.attempt.to.use.value.based.where.identity.expected -RequiresIdentityTest.java:75:32: compiler.warn.attempt.to.use.value.based.where.identity.expected -RequiresIdentityTest.java:79:90: compiler.warn.attempt.to.use.value.based.where.identity.expected -RequiresIdentityTest.java:80:52: compiler.warn.attempt.to.use.value.based.where.identity.expected -RequiresIdentityTest.java:81:82: compiler.warn.attempt.to.use.value.based.where.identity.expected -RequiresIdentityTest.java:81:31: compiler.warn.attempt.to.use.value.based.where.identity.expected +RequiresIdentityTest.java:71:37: compiler.warn.attempt.to.use.value.based.where.identity.expected +RequiresIdentityTest.java:71:18: compiler.warn.attempt.to.use.value.based.where.identity.expected +RequiresIdentityTest.java:78:32: compiler.warn.attempt.to.use.value.based.where.identity.expected +RequiresIdentityTest.java:82:90: compiler.warn.attempt.to.use.value.based.where.identity.expected +RequiresIdentityTest.java:83:52: compiler.warn.attempt.to.use.value.based.where.identity.expected +RequiresIdentityTest.java:84:82: compiler.warn.attempt.to.use.value.based.where.identity.expected +RequiresIdentityTest.java:84:31: compiler.warn.attempt.to.use.value.based.where.identity.expected - compiler.err.warnings.and.werror - compiler.note.unchecked.filename: RequiresIdentityTest.java - compiler.note.unchecked.recompile diff --git a/test/langtools/tools/javac/lint/RequiresIdentityTest2.out b/test/langtools/tools/javac/lint/RequiresIdentityTest2.out new file mode 100644 index 0000000000000..55a7ccdeb8427 --- /dev/null +++ b/test/langtools/tools/javac/lint/RequiresIdentityTest2.out @@ -0,0 +1,2 @@ +- compiler.note.unchecked.filename: RequiresIdentityTest.java +- compiler.note.unchecked.recompile From 1ff2f2c67538796009a072a40051d5f806945fba Mon Sep 17 00:00:00 2001 From: Vicente Romero Date: Fri, 9 May 2025 12:23:09 -0400 Subject: [PATCH 26/34] additional documentation changes and bug fixes --- .../com/sun/tools/javac/code/Lint.java | 24 +++++++ .../tools/javac/resources/javac.properties | 5 +- .../share/classes/module-info.java | 5 +- src/jdk.compiler/share/man/javac.md | 4 +- .../tools/javac/diags/examples.not-yet.txt | 2 - .../javac/lint/RequiresIdentityTest.java | 4 ++ .../tools/javac/lint/RequiresIdentityTest.out | 66 +++++++++---------- 7 files changed, 72 insertions(+), 38 deletions(-) diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Lint.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Lint.java index 7b6cb7443c251..3e7c0369986b2 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Lint.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Lint.java @@ -80,6 +80,12 @@ public Lint augment(Symbol sym) { Lint lint = new Lint(this); lint.values.removeAll(suppressions); lint.suppressedValues.addAll(suppressions); + for (LintCategory suppressed : suppressions) { + if (suppressed.alias != null) { + lint.values.remove(suppressed.alias); + lint.suppressedValues.add(suppressed.alias); + } + } return lint; } return this; @@ -93,6 +99,12 @@ public Lint enable(LintCategory... lc) { Lint l = new Lint(this); l.values.addAll(Arrays.asList(lc)); l.suppressedValues.removeAll(Arrays.asList(lc)); + for (LintCategory lintCategory : lc) { + if (lintCategory.alias != null) { + l.values.add(lintCategory.alias); + l.suppressedValues.remove(lintCategory.alias); + } + } return l; } @@ -104,6 +116,12 @@ public Lint suppress(LintCategory... lc) { Lint l = new Lint(this); l.values.removeAll(Arrays.asList(lc)); l.suppressedValues.addAll(Arrays.asList(lc)); + for (LintCategory lintCategory : lc) { + if (lintCategory.alias != null) { + l.values.remove(lintCategory.alias); + l.suppressedValues.add(lintCategory.alias); + } + } return l; } @@ -182,8 +200,14 @@ private void initializeRootIfNeeded() { for (LintCategory lc : LintCategory.values()) { if (options.isSet(Option.XLINT_CUSTOM, lc.option)) { values.add(lc); + if (lc.alias != null) { + values.add(lc.alias); + } } else if (options.isSet(Option.XLINT_CUSTOM, "-" + lc.option)) { values.remove(lc); + if (lc.alias != null) { + values.remove(lc.alias); + } } } diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/javac.properties b/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/javac.properties index c7c9d3a4748e9..7901441699b40 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/javac.properties +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/javac.properties @@ -292,7 +292,10 @@ javac.opt.Xlint.desc.restricted=\ Warn about use of restricted methods. javac.opt.Xlint.desc.synchronization=\ - Warn about synchronization attempts on instances of value-based classes. + Warn about synchronization attempts on instances of value-based classes.\n\ + Superseded by the `identity` warning category which has the same uses and\n\ + effects. Users are encouraged to use the `identity` category for all future\n\ + an existing uses of `synchronization`. javac.opt.Xlint.desc.identity=\ Warn about uses of value-based classes where an identity class is expected. diff --git a/src/jdk.compiler/share/classes/module-info.java b/src/jdk.compiler/share/classes/module-info.java index d32bf3d49f5d6..552967159b262 100644 --- a/src/jdk.compiler/share/classes/module-info.java +++ b/src/jdk.compiler/share/classes/module-info.java @@ -187,7 +187,10 @@ * and interfaces * {@code static} accessing a static member using an instance * {@code strictfp} unnecessary use of the {@code strictfp} modifier - * {@code synchronization} synchronization attempts on instances of value-based classes + * {@code synchronization} synchronization attempts on instances of value-based classes, + * superseded by the {@code identity} warning category which has the same + * uses and effects. Users are encouraged to use the {@code identity} + * category for all future an existing uses of {@code synchronization} * {@code text-blocks} inconsistent white space characters in text block indentation * {@code this-escape} superclass constructor leaking {@code this} before subclass initialized * {@code try} issues relating to use of {@code try} blocks diff --git a/src/jdk.compiler/share/man/javac.md b/src/jdk.compiler/share/man/javac.md index 45d8e19538a16..3732598b862e4 100644 --- a/src/jdk.compiler/share/man/javac.md +++ b/src/jdk.compiler/share/man/javac.md @@ -649,7 +649,9 @@ file system locations may be directories, JAR files or JMOD files. - `strictfp`: Warns about unnecessary use of the `strictfp` modifier. - `synchronization`: Warns about synchronization attempts on instances - of value-based classes. + of value-based classes. Superseded by the `identity` warning category + which has the same uses and effects. Users are encouraged to use the + `identity` category for all future an existing uses of `synchronization`. - `text-blocks`: Warns about inconsistent white space characters in text block indentation. diff --git a/test/langtools/tools/javac/diags/examples.not-yet.txt b/test/langtools/tools/javac/diags/examples.not-yet.txt index 98ca039a7f840..b29f20e5ebbb8 100644 --- a/test/langtools/tools/javac/diags/examples.not-yet.txt +++ b/test/langtools/tools/javac/diags/examples.not-yet.txt @@ -221,5 +221,3 @@ compiler.warn.restricted.method # Pending removal compiler.note.implicit.annotation.processing compiler.warn.proc.use.proc.or.implicit - -compiler.warn.attempt.to.synchronize.on.instance.of.value.based.class2 # synchronization and identity are aliases diff --git a/test/langtools/tools/javac/lint/RequiresIdentityTest.java b/test/langtools/tools/javac/lint/RequiresIdentityTest.java index 9d319f539ea0e..ddc25c8d31153 100644 --- a/test/langtools/tools/javac/lint/RequiresIdentityTest.java +++ b/test/langtools/tools/javac/lint/RequiresIdentityTest.java @@ -4,9 +4,13 @@ * @summary Expand value-based class warnings to java.lang.ref API * @compile --patch-module java.base=${test.src} RequiresIdentityHelper.java * @compile/fail/ref=RequiresIdentityTest.out --patch-module java.base=${test.src} -Werror -XDrawDiagnostics -Xlint:identity RequiresIdentityTest.java + * @compile/fail/ref=RequiresIdentityTest.out --patch-module java.base=${test.src} -Werror -XDrawDiagnostics -Xlint:synchronization RequiresIdentityTest.java * @compile/ref=RequiresIdentityTest2.out --patch-module java.base=${test.src} -Werror -XDrawDiagnostics -Xlint:-identity RequiresIdentityTest.java + * @compile/ref=RequiresIdentityTest2.out --patch-module java.base=${test.src} -Werror -XDrawDiagnostics -Xlint:-synchronization RequiresIdentityTest.java * @compile/fail/ref=RequiresIdentityTest.out --patch-module java.base=${test.src} -Werror -XDrawDiagnostics -Xlint:identity RequiresIdentityHelper.java RequiresIdentityTest.java + * @compile/fail/ref=RequiresIdentityTest.out --patch-module java.base=${test.src} -Werror -XDrawDiagnostics -Xlint:synchronization RequiresIdentityHelper.java RequiresIdentityTest.java * @compile/ref=RequiresIdentityTest2.out --patch-module java.base=${test.src} -Werror -XDrawDiagnostics -Xlint:-identity RequiresIdentityHelper.java RequiresIdentityTest.java + * @compile/ref=RequiresIdentityTest2.out --patch-module java.base=${test.src} -Werror -XDrawDiagnostics -Xlint:-synchronization RequiresIdentityHelper.java RequiresIdentityTest.java */ package java.lang; diff --git a/test/langtools/tools/javac/lint/RequiresIdentityTest.out b/test/langtools/tools/javac/lint/RequiresIdentityTest.out index abc3ebda4f585..ddcd4c91354bf 100644 --- a/test/langtools/tools/javac/lint/RequiresIdentityTest.out +++ b/test/langtools/tools/javac/lint/RequiresIdentityTest.out @@ -1,37 +1,37 @@ -RequiresIdentityTest.java:15:65: compiler.warn.attempt.to.use.value.based.where.identity.expected -RequiresIdentityTest.java:16:88: compiler.warn.attempt.to.use.value.based.where.identity.expected -RequiresIdentityTest.java:19:27: compiler.warn.attempt.to.use.value.based.where.identity.expected -RequiresIdentityTest.java:20:36: compiler.warn.attempt.to.use.value.based.where.identity.expected -RequiresIdentityTest.java:21:8: compiler.warn.attempt.to.use.value.based.where.identity.expected -RequiresIdentityTest.java:22:8: compiler.warn.attempt.to.use.value.based.where.identity.expected -RequiresIdentityTest.java:23:72: compiler.warn.attempt.to.use.value.based.where.identity.expected +RequiresIdentityTest.java:19:65: compiler.warn.attempt.to.use.value.based.where.identity.expected +RequiresIdentityTest.java:20:88: compiler.warn.attempt.to.use.value.based.where.identity.expected RequiresIdentityTest.java:23:27: compiler.warn.attempt.to.use.value.based.where.identity.expected -RequiresIdentityTest.java:27:15: compiler.warn.attempt.to.use.value.based.where.identity.expected -RequiresIdentityTest.java:30:32: compiler.warn.attempt.to.use.value.based.where.identity.expected -RequiresIdentityTest.java:31:31: compiler.warn.attempt.to.use.value.based.where.identity.expected -RequiresIdentityTest.java:32:40: compiler.warn.attempt.to.use.value.based.where.identity.expected -RequiresIdentityTest.java:34:16: compiler.warn.attempt.to.use.value.based.where.identity.expected -RequiresIdentityTest.java:35:16: compiler.warn.attempt.to.use.value.based.where.identity.expected -RequiresIdentityTest.java:36:16: compiler.warn.attempt.to.use.value.based.where.identity.expected -RequiresIdentityTest.java:37:15: compiler.warn.attempt.to.use.value.based.where.identity.expected -RequiresIdentityTest.java:40:67: compiler.warn.attempt.to.use.value.based.where.identity.expected -RequiresIdentityTest.java:43:63: compiler.warn.attempt.to.use.value.based.where.identity.expected -RequiresIdentityTest.java:46:27: compiler.warn.attempt.to.use.value.based.where.identity.expected -RequiresIdentityTest.java:51:6: compiler.warn.attempt.to.use.value.based.where.identity.expected -RequiresIdentityTest.java:51:49: compiler.warn.attempt.to.use.value.based.where.identity.expected -RequiresIdentityTest.java:53:21: compiler.warn.attempt.to.use.value.based.where.identity.expected -RequiresIdentityTest.java:55:36: compiler.warn.attempt.to.use.value.based.where.identity.expected -RequiresIdentityTest.java:58:32: compiler.warn.attempt.to.use.value.based.where.identity.expected -RequiresIdentityTest.java:59:46: compiler.warn.attempt.to.use.value.based.where.identity.expected -RequiresIdentityTest.java:63:54: compiler.warn.attempt.to.use.value.based.where.identity.expected -RequiresIdentityTest.java:68:37: compiler.warn.attempt.to.use.value.based.where.identity.expected -RequiresIdentityTest.java:71:37: compiler.warn.attempt.to.use.value.based.where.identity.expected -RequiresIdentityTest.java:71:18: compiler.warn.attempt.to.use.value.based.where.identity.expected -RequiresIdentityTest.java:78:32: compiler.warn.attempt.to.use.value.based.where.identity.expected -RequiresIdentityTest.java:82:90: compiler.warn.attempt.to.use.value.based.where.identity.expected -RequiresIdentityTest.java:83:52: compiler.warn.attempt.to.use.value.based.where.identity.expected -RequiresIdentityTest.java:84:82: compiler.warn.attempt.to.use.value.based.where.identity.expected -RequiresIdentityTest.java:84:31: compiler.warn.attempt.to.use.value.based.where.identity.expected +RequiresIdentityTest.java:24:36: compiler.warn.attempt.to.use.value.based.where.identity.expected +RequiresIdentityTest.java:25:8: compiler.warn.attempt.to.use.value.based.where.identity.expected +RequiresIdentityTest.java:26:8: compiler.warn.attempt.to.use.value.based.where.identity.expected +RequiresIdentityTest.java:27:72: compiler.warn.attempt.to.use.value.based.where.identity.expected +RequiresIdentityTest.java:27:27: compiler.warn.attempt.to.use.value.based.where.identity.expected +RequiresIdentityTest.java:31:15: compiler.warn.attempt.to.use.value.based.where.identity.expected +RequiresIdentityTest.java:34:32: compiler.warn.attempt.to.use.value.based.where.identity.expected +RequiresIdentityTest.java:35:31: compiler.warn.attempt.to.use.value.based.where.identity.expected +RequiresIdentityTest.java:36:40: compiler.warn.attempt.to.use.value.based.where.identity.expected +RequiresIdentityTest.java:38:16: compiler.warn.attempt.to.use.value.based.where.identity.expected +RequiresIdentityTest.java:39:16: compiler.warn.attempt.to.use.value.based.where.identity.expected +RequiresIdentityTest.java:40:16: compiler.warn.attempt.to.use.value.based.where.identity.expected +RequiresIdentityTest.java:41:15: compiler.warn.attempt.to.use.value.based.where.identity.expected +RequiresIdentityTest.java:44:67: compiler.warn.attempt.to.use.value.based.where.identity.expected +RequiresIdentityTest.java:47:63: compiler.warn.attempt.to.use.value.based.where.identity.expected +RequiresIdentityTest.java:50:27: compiler.warn.attempt.to.use.value.based.where.identity.expected +RequiresIdentityTest.java:55:6: compiler.warn.attempt.to.use.value.based.where.identity.expected +RequiresIdentityTest.java:55:49: compiler.warn.attempt.to.use.value.based.where.identity.expected +RequiresIdentityTest.java:57:21: compiler.warn.attempt.to.use.value.based.where.identity.expected +RequiresIdentityTest.java:59:36: compiler.warn.attempt.to.use.value.based.where.identity.expected +RequiresIdentityTest.java:62:32: compiler.warn.attempt.to.use.value.based.where.identity.expected +RequiresIdentityTest.java:63:46: compiler.warn.attempt.to.use.value.based.where.identity.expected +RequiresIdentityTest.java:67:54: compiler.warn.attempt.to.use.value.based.where.identity.expected +RequiresIdentityTest.java:72:37: compiler.warn.attempt.to.use.value.based.where.identity.expected +RequiresIdentityTest.java:75:37: compiler.warn.attempt.to.use.value.based.where.identity.expected +RequiresIdentityTest.java:75:18: compiler.warn.attempt.to.use.value.based.where.identity.expected +RequiresIdentityTest.java:82:32: compiler.warn.attempt.to.use.value.based.where.identity.expected +RequiresIdentityTest.java:86:90: compiler.warn.attempt.to.use.value.based.where.identity.expected +RequiresIdentityTest.java:87:52: compiler.warn.attempt.to.use.value.based.where.identity.expected +RequiresIdentityTest.java:88:82: compiler.warn.attempt.to.use.value.based.where.identity.expected +RequiresIdentityTest.java:88:31: compiler.warn.attempt.to.use.value.based.where.identity.expected - compiler.err.warnings.and.werror - compiler.note.unchecked.filename: RequiresIdentityTest.java - compiler.note.unchecked.recompile From ea6311613cbc434a684347f248881bd9975b926b Mon Sep 17 00:00:00 2001 From: Vicente Romero Date: Fri, 9 May 2025 16:22:32 -0400 Subject: [PATCH 27/34] fixing bugs, removing dead code --- .../com/sun/tools/javac/code/Lint.java | 55 ++++--------------- .../com/sun/tools/javac/main/Option.java | 11 ++++ .../tools/javac/diags/CheckResourceKeys.java | 2 +- 3 files changed, 23 insertions(+), 45 deletions(-) diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Lint.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Lint.java index 3e7c0369986b2..c770d6ae4e51f 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Lint.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Lint.java @@ -80,12 +80,6 @@ public Lint augment(Symbol sym) { Lint lint = new Lint(this); lint.values.removeAll(suppressions); lint.suppressedValues.addAll(suppressions); - for (LintCategory suppressed : suppressions) { - if (suppressed.alias != null) { - lint.values.remove(suppressed.alias); - lint.suppressedValues.add(suppressed.alias); - } - } return lint; } return this; @@ -99,12 +93,6 @@ public Lint enable(LintCategory... lc) { Lint l = new Lint(this); l.values.addAll(Arrays.asList(lc)); l.suppressedValues.removeAll(Arrays.asList(lc)); - for (LintCategory lintCategory : lc) { - if (lintCategory.alias != null) { - l.values.add(lintCategory.alias); - l.suppressedValues.remove(lintCategory.alias); - } - } return l; } @@ -116,12 +104,6 @@ public Lint suppress(LintCategory... lc) { Lint l = new Lint(this); l.values.removeAll(Arrays.asList(lc)); l.suppressedValues.addAll(Arrays.asList(lc)); - for (LintCategory lintCategory : lc) { - if (lintCategory.alias != null) { - l.values.remove(lintCategory.alias); - l.suppressedValues.add(lintCategory.alias); - } - } return l; } @@ -191,23 +173,18 @@ private void initializeRootIfNeeded() { if (!options.isSet(Option.PREVIEW)) { values.add(LintCategory.PREVIEW); } - values.add(LintCategory.SYNCHRONIZATION); values.add(LintCategory.IDENTITY); values.add(LintCategory.INCUBATING); } // Look for specific overrides for (LintCategory lc : LintCategory.values()) { - if (options.isSet(Option.XLINT_CUSTOM, lc.option)) { + if (options.isSet(Option.XLINT_CUSTOM, lc.option) || + (lc.alias != null && options.isSet(Option.XLINT_CUSTOM, lc.alias))) { values.add(lc); - if (lc.alias != null) { - values.add(lc.alias); - } - } else if (options.isSet(Option.XLINT_CUSTOM, "-" + lc.option)) { + } else if (options.isSet(Option.XLINT_CUSTOM, "-" + lc.option) || + (lc.alias != null && options.isSet(Option.XLINT_CUSTOM, "-" + lc.alias))) { values.remove(lc); - if (lc.alias != null) { - values.remove(lc.alias); - } } } @@ -289,7 +266,7 @@ public enum LintCategory { /** * Warn about uses of @ValueBased classes where an identity class is expected. */ - IDENTITY("identity"), + IDENTITY("identity", "synchronization"), /** * Warn about use of incubating modules. @@ -393,11 +370,6 @@ public enum LintCategory { */ STRICTFP("strictfp"), - /** - * Warn about synchronization attempts on instances of @ValueBased classes. - */ - SYNCHRONIZATION("synchronization", IDENTITY), - /** * Warn about issues relating to use of text blocks * @@ -444,18 +416,18 @@ public enum LintCategory { this(option, annotationSuppression, null); } - LintCategory(String option, LintCategory alias) { + LintCategory(String option, String alias) { this(option, true, alias); } - LintCategory(String option, boolean annotationSuppression, LintCategory alias) { + LintCategory(String option, boolean annotationSuppression, String alias) { this.option = option; this.annotationSuppression = annotationSuppression; this.alias = alias; map.put(option, this); // we need to do this as forward references are not allowed if (alias != null) { - alias.alias = this; + map.put(alias, this); } } @@ -479,7 +451,7 @@ public static EnumSet newEmptySet() { /** Does this category support being suppressed by the {@code @SuppressWarnings} annotation? */ public final boolean annotationSuppression; - public LintCategory alias; + public String alias; } /** @@ -489,7 +461,7 @@ public static EnumSet newEmptySet() { */ public boolean isEnabled(LintCategory lc) { initializeRootIfNeeded(); - return values.contains(lc) || (lc.alias != null && values.contains(lc.alias)); + return values.contains(lc); } /** @@ -500,7 +472,7 @@ public boolean isEnabled(LintCategory lc) { */ public boolean isSuppressed(LintCategory lc) { initializeRootIfNeeded(); - return suppressedValues.contains(lc) || (lc.alias != null && suppressedValues.contains(lc.alias)); + return suppressedValues.contains(lc); } /** @@ -562,11 +534,6 @@ private EnumSet suppressionsFrom(Attribute.Compound suppressWarnin .filter(lc -> lc.annotationSuppression) .ifPresent(result::add); } - for (LintCategory lc : result) { - if (lc.alias != null) { - result.add(lc.alias); - } - } return result; } diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/main/Option.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/main/Option.java index e0fdd30c2427f..d8399fd22442f 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/main/Option.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/main/Option.java @@ -499,6 +499,13 @@ public void process(OptionHelper helper, String option) throws InvalidValueExcep lc.option, log.localize(PrefixKind.JAVAC, "opt.Xlint.desc." + lc.option))); + if (lc.alias != null) { + log.printRawLines(WriterKind.STDOUT, + String.format(LINT_KEY_FORMAT, + lc.alias, + log.localize(PrefixKind.JAVAC, + "opt.Xlint.desc." + lc.alias))); + } } log.printRawLines(WriterKind.STDOUT, String.format(LINT_KEY_FORMAT, @@ -1368,6 +1375,10 @@ private static Set getXLintChoices() { for (Lint.LintCategory c : Lint.LintCategory.values()) { choices.add(c.option); choices.add("-" + c.option); + if (c.alias != null) { + choices.add(c.alias); + choices.add("-" + c.alias); + } } choices.add("none"); return choices; diff --git a/test/langtools/tools/javac/diags/CheckResourceKeys.java b/test/langtools/tools/javac/diags/CheckResourceKeys.java index 9f6f37dc23d0b..8675875bd00e8 100644 --- a/test/langtools/tools/javac/diags/CheckResourceKeys.java +++ b/test/langtools/tools/javac/diags/CheckResourceKeys.java @@ -178,7 +178,7 @@ void findDeadKeys(Set codeStrings, Set resourceKeys) { boolean found = false; for (LintCategory lc : LintCategory.values()) { - if (option.equals(lc.option)) + if (option.equals(lc.option) || (lc.alias != null && option.equals(lc.alias))) found = true; } From a9a537831bcb2840775892748d4f11acf0be2a3d Mon Sep 17 00:00:00 2001 From: Vicente Romero Date: Sat, 10 May 2025 11:27:40 -0400 Subject: [PATCH 28/34] integrating code from Archie --- .../com/sun/tools/javac/code/Lint.java | 57 +++++++++++-------- .../com/sun/tools/javac/comp/Modules.java | 2 +- .../com/sun/tools/javac/main/Arguments.java | 5 +- .../com/sun/tools/javac/main/Option.java | 54 ++++++++++++------ .../tools/javac/resources/javac.properties | 6 -- .../com/sun/tools/javac/util/Options.java | 53 +++++++++++++++++ .../tools/javac/diags/CheckResourceKeys.java | 2 +- 7 files changed, 126 insertions(+), 53 deletions(-) diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Lint.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Lint.java index c770d6ae4e51f..e03273ddbe4c8 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Lint.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Lint.java @@ -25,8 +25,11 @@ package com.sun.tools.javac.code; +import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; import java.util.EnumSet; +import java.util.List; import java.util.Map; import java.util.Optional; import java.util.concurrent.ConcurrentHashMap; @@ -149,10 +152,10 @@ private void initializeRootIfNeeded() { return; // Initialize enabled categories based on "-Xlint" flags - if (options.isSet(Option.XLINT) || options.isSet(Option.XLINT_CUSTOM, "all")) { + if (options.isSet(Option.XLINT) || options.isSet(Option.XLINT_CUSTOM, Option.LINT_CUSTOM_ALL)) { // If -Xlint or -Xlint:all is given, enable all categories by default values = EnumSet.allOf(LintCategory.class); - } else if (options.isSet(Option.XLINT_CUSTOM, "none")) { + } else if (options.isSet(Option.XLINT_CUSTOM, Option.LINT_CUSTOM_NONE)) { // if -Xlint:none is given, disable all categories by default values = LintCategory.newEmptySet(); } else { @@ -179,11 +182,9 @@ private void initializeRootIfNeeded() { // Look for specific overrides for (LintCategory lc : LintCategory.values()) { - if (options.isSet(Option.XLINT_CUSTOM, lc.option) || - (lc.alias != null && options.isSet(Option.XLINT_CUSTOM, lc.alias))) { + if (options.isExplicitlyEnabled(Option.XLINT, lc)) { values.add(lc); - } else if (options.isSet(Option.XLINT_CUSTOM, "-" + lc.option) || - (lc.alias != null && options.isSet(Option.XLINT_CUSTOM, "-" + lc.alias))) { + } else if (options.isExplicitlyDisabled(Option.XLINT, lc)) { values.remove(lc); } } @@ -266,7 +267,7 @@ public enum LintCategory { /** * Warn about uses of @ValueBased classes where an identity class is expected. */ - IDENTITY("identity", "synchronization"), + IDENTITY("identity", true, "synchronization"), /** * Warn about use of incubating modules. @@ -412,23 +413,14 @@ public enum LintCategory { this(option, true); } - LintCategory(String option, boolean annotationSuppression) { - this(option, annotationSuppression, null); - } - - LintCategory(String option, String alias) { - this(option, true, alias); - } - - LintCategory(String option, boolean annotationSuppression, String alias) { + LintCategory(String option, boolean annotationSuppression, String... aliases) { this.option = option; this.annotationSuppression = annotationSuppression; - this.alias = alias; - map.put(option, this); - // we need to do this as forward references are not allowed - if (alias != null) { - map.put(alias, this); - } + ArrayList optionList = new ArrayList<>(1 + aliases.length); + optionList.add(option); + Stream.of(aliases).forEach(optionList::add); + this.optionList = Collections.unmodifiableList(optionList); + this.optionList.forEach(string -> map.put(string, this)); } /** @@ -445,13 +437,14 @@ public static EnumSet newEmptySet() { return EnumSet.noneOf(LintCategory.class); } - /** Get the string representing this category in @SuppressAnnotations and -Xlint options. */ + /** Get the "canonical" string representing this category in @SuppressAnnotations and -Xlint options. */ public final String option; + /** Get a list containing "option" followed by zero or more aliases. */ + public final List optionList; + /** Does this category support being suppressed by the {@code @SuppressWarnings} annotation? */ public final boolean annotationSuppression; - - public String alias; } /** @@ -513,6 +506,20 @@ public EnumSet suppressionsFrom(Symbol symbol) { return suppressions; } + /** + * Retrieve the recognized lint categories suppressed by the given @SuppressWarnings annotation. + * + * @param annotation @SuppressWarnings annotation, or null + * @return set of lint categories, possibly empty but never null + */ + private EnumSet suppressionsFrom(JCAnnotation annotation) { + initializeSymbolsIfNeeded(); + if (annotation == null) + return LintCategory.newEmptySet(); + Assert.check(annotation.attribute.type.tsym == syms.suppressWarningsType.tsym); + return suppressionsFrom(Stream.of(annotation).map(anno -> anno.attribute)); + } + // Find the @SuppressWarnings annotation in the given stream and extract the recognized suppressions private EnumSet suppressionsFrom(Stream attributes) { initializeSymbolsIfNeeded(); diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Modules.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Modules.java index 596c5704cbef3..a159793fe327f 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Modules.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Modules.java @@ -205,7 +205,7 @@ protected Modules(Context context) { allowAccessIntoSystem = options.isUnset(Option.RELEASE); - lintOptions = options.isUnset(Option.XLINT_CUSTOM, "-" + LintCategory.OPTIONS.option); + lintOptions = !options.isExplicitlyDisabled(Option.XLINT, LintCategory.OPTIONS); multiModuleMode = fileManager.hasLocation(StandardLocation.MODULE_SOURCE_PATH); ClassWriter classWriter = ClassWriter.instance(context); diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/main/Arguments.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/main/Arguments.java index 14b42bc01c5d3..9e3a978c3bc9a 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/main/Arguments.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/main/Arguments.java @@ -503,8 +503,7 @@ public boolean validate() { } } else { // single-module or legacy mode - boolean lintPaths = options.isUnset(Option.XLINT_CUSTOM, - "-" + LintCategory.PATH.option); + boolean lintPaths = !options.isExplicitlyDisabled(Option.XLINT, LintCategory.PATH); if (lintPaths) { Path outDirParent = outDir.getParent(); if (outDirParent != null && Files.exists(outDirParent.resolve("module-info.class"))) { @@ -577,7 +576,7 @@ public boolean validate() { reportDiag(Errors.SourcepathModulesourcepathConflict); } - boolean lintOptions = options.isUnset(Option.XLINT_CUSTOM, "-" + LintCategory.OPTIONS.option); + boolean lintOptions = !options.isExplicitlyDisabled(Option.XLINT, LintCategory.OPTIONS); if (lintOptions && source.compareTo(Source.DEFAULT) < 0 && !options.isSet(Option.RELEASE)) { if (fm instanceof BaseFileManager baseFileManager) { if (source.compareTo(Source.JDK8) <= 0) { diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/main/Option.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/main/Option.java index d8399fd22442f..035ae0396c97d 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/main/Option.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/main/Option.java @@ -39,6 +39,7 @@ import java.util.EnumSet; import java.util.Iterator; import java.util.LinkedHashSet; +import java.util.List; import java.util.Locale; import java.util.ServiceLoader; import java.util.Set; @@ -46,6 +47,7 @@ import java.util.TreeSet; import java.util.regex.Pattern; import java.util.stream.Collectors; +import java.util.stream.Stream; import java.util.stream.StreamSupport; import javax.lang.model.SourceVersion; @@ -491,7 +493,7 @@ public void process(OptionHelper helper, String option) throws InvalidValueExcep log.printRawLines(WriterKind.STDOUT, log.localize(PrefixKind.JAVAC, "opt.help.lint.header")); log.printRawLines(WriterKind.STDOUT, String.format(LINT_KEY_FORMAT, - "all", + LINT_CUSTOM_ALL, log.localize(PrefixKind.JAVAC, "opt.Xlint.all"))); for (LintCategory lc : LintCategory.values()) { log.printRawLines(WriterKind.STDOUT, @@ -499,17 +501,18 @@ public void process(OptionHelper helper, String option) throws InvalidValueExcep lc.option, log.localize(PrefixKind.JAVAC, "opt.Xlint.desc." + lc.option))); - if (lc.alias != null) { + int numOptions = lc.optionList.size(); + if (numOptions > 1) { + String aliases = lc.optionList.subList(1, numOptions).stream().collect(Collectors.joining(", ")); log.printRawLines(WriterKind.STDOUT, - String.format(LINT_KEY_FORMAT, - lc.alias, - log.localize(PrefixKind.JAVAC, - "opt.Xlint.desc." + lc.alias))); + String.format(LINT_KEY_FORMAT, + "", + (numOptions > 2 ? "Aliases" : "Alias") + ": " + aliases)); } } log.printRawLines(WriterKind.STDOUT, String.format(LINT_KEY_FORMAT, - "none", + LINT_CUSTOM_NONE, log.localize(PrefixKind.JAVAC, "opt.Xlint.none"))); super.process(helper, option); } @@ -842,6 +845,16 @@ private Option[] getSupportedRuntimeOptions() { } }; + /** + * Special lint category key meaning "all lint categories". + */ + public static final String LINT_CUSTOM_ALL = "all"; + + /** + * Special lint category key meaning "no lint categories". + */ + public static final String LINT_CUSTOM_NONE = "none"; + /** * This exception is thrown when an invalid value is given for an option. * The detail string gives a detailed, localized message, suitable for use @@ -1088,6 +1101,17 @@ public OptionKind getKind() { return kind; } + /** + * If this option is named {@code FOO}, obtain the option named {@code FOO_CUSTOM}. + * + * @param option regular option + * @return corresponding custom option + * @throws IllegalArgumentException if no such option exists + */ + public Option getCustom() { + return Option.valueOf(name() + "_CUSTOM"); + } + public boolean isInBasicOptionGroup() { return group == BASIC; } @@ -1371,16 +1395,12 @@ public static PkgInfo get(Options options) { private static Set getXLintChoices() { Set choices = new LinkedHashSet<>(); - choices.add("all"); - for (Lint.LintCategory c : Lint.LintCategory.values()) { - choices.add(c.option); - choices.add("-" + c.option); - if (c.alias != null) { - choices.add(c.alias); - choices.add("-" + c.alias); - } - } - choices.add("none"); + choices.add(LINT_CUSTOM_ALL); + Stream.of(Lint.LintCategory.values()) + .flatMap(lc -> lc.optionList.stream()) + .flatMap(ident -> Stream.of(ident, "-" + ident)) + .forEach(choices::add); + choices.add(LINT_CUSTOM_NONE); return choices; } diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/javac.properties b/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/javac.properties index 7901441699b40..aa4add81d342f 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/javac.properties +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/javac.properties @@ -291,12 +291,6 @@ javac.opt.Xlint.desc.preview=\ javac.opt.Xlint.desc.restricted=\ Warn about use of restricted methods. -javac.opt.Xlint.desc.synchronization=\ - Warn about synchronization attempts on instances of value-based classes.\n\ - Superseded by the `identity` warning category which has the same uses and\n\ - effects. Users are encouraged to use the `identity` category for all future\n\ - an existing uses of `synchronization`. - javac.opt.Xlint.desc.identity=\ Warn about uses of value-based classes where an identity class is expected. diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/util/Options.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/util/Options.java index e7a07a57a8f7f..63f5b0ca75abb 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/util/Options.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/util/Options.java @@ -29,6 +29,7 @@ import java.util.function.Consumer; import java.util.function.Supplier; +import com.sun.tools.javac.code.Lint.LintCategory; import com.sun.tools.javac.main.Option; import static com.sun.tools.javac.main.Option.*; @@ -170,6 +171,58 @@ public boolean isUnset(Option option, String value) { return !isSet(option, value); } + /** + * Check whether the given lint category is explicitly enabled or disabled. + * + *

+ * If the category is neither enabled nor disabled, return the given default value. + * + * @param option the plain (non-custom) option + * @param lc the {@link LintCategory} in question + * @param defaultValue presumed default value + * @return true if {@code lc} would be included + */ + public boolean isSet(Option option, LintCategory lc, boolean defaultValue) { + Option customOption = option.getCustom(); + if (lc.optionList.stream().anyMatch(alias -> isSet(customOption, alias))) { + return true; + } + if (lc.optionList.stream().anyMatch(alias -> isSet(customOption, "-" + alias))) { + return false; + } + if (isSet(option) || isSet(customOption, Option.LINT_CUSTOM_ALL)) { + return true; + } + if (isSet(customOption, Option.LINT_CUSTOM_NONE)) { + return false; + } + return defaultValue; + } + + /** + * Determine if a specific {@link LintCategory} was explicitly enabled via a custom option flag + * of the form {@code -Flag:all} or {@code -Flag:key}. + * + * @param option the option + * @param lc the {@link LintCategory} in question + * @return true if {@code lc} has been explicitly enabled + */ + public boolean isExplicitlyEnabled(Option option, LintCategory lc) { + return isSet(option, lc, false); + } + + /** + * Determine if a specific {@link LintCategory} was explicitly disabled via a custom option flag + * of the form {@code -Flag:none} or {@code -Flag:-key}. + * + * @param option the option + * @param lc the {@link LintCategory} in question + * @return true if {@code lc} has been explicitly disabled + */ + public boolean isExplicitlyDisabled(Option option, LintCategory lc) { + return !isSet(option, lc, true); + } + public void put(String name, String value) { values.put(name, value); initialized = true; diff --git a/test/langtools/tools/javac/diags/CheckResourceKeys.java b/test/langtools/tools/javac/diags/CheckResourceKeys.java index 8675875bd00e8..9f6f37dc23d0b 100644 --- a/test/langtools/tools/javac/diags/CheckResourceKeys.java +++ b/test/langtools/tools/javac/diags/CheckResourceKeys.java @@ -178,7 +178,7 @@ void findDeadKeys(Set codeStrings, Set resourceKeys) { boolean found = false; for (LintCategory lc : LintCategory.values()) { - if (option.equals(lc.option) || (lc.alias != null && option.equals(lc.alias))) + if (option.equals(lc.option)) found = true; } From 3c6dacb026a5526bf15a4b995f6d588972b80810 Mon Sep 17 00:00:00 2001 From: Vicente Romero Date: Sat, 10 May 2025 12:02:50 -0400 Subject: [PATCH 29/34] removing dead code --- .../classes/com/sun/tools/javac/code/Lint.java | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Lint.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Lint.java index e03273ddbe4c8..dd12bb91a4c82 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Lint.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Lint.java @@ -506,20 +506,6 @@ public EnumSet suppressionsFrom(Symbol symbol) { return suppressions; } - /** - * Retrieve the recognized lint categories suppressed by the given @SuppressWarnings annotation. - * - * @param annotation @SuppressWarnings annotation, or null - * @return set of lint categories, possibly empty but never null - */ - private EnumSet suppressionsFrom(JCAnnotation annotation) { - initializeSymbolsIfNeeded(); - if (annotation == null) - return LintCategory.newEmptySet(); - Assert.check(annotation.attribute.type.tsym == syms.suppressWarningsType.tsym); - return suppressionsFrom(Stream.of(annotation).map(anno -> anno.attribute)); - } - // Find the @SuppressWarnings annotation in the given stream and extract the recognized suppressions private EnumSet suppressionsFrom(Stream attributes) { initializeSymbolsIfNeeded(); From 973307ebea3bedd73877cd9b86040d37f6281d72 Mon Sep 17 00:00:00 2001 From: Vicente Romero Date: Sat, 10 May 2025 12:34:33 -0400 Subject: [PATCH 30/34] additional changes from Archie --- .../com/sun/tools/javac/code/Lint.java | 14 ++++++++--- .../com/sun/tools/javac/main/Option.java | 23 ++++--------------- .../tools/javac/resources/javac.properties | 6 +++++ .../share/classes/module-info.java | 9 ++++---- src/jdk.compiler/share/man/javac.md | 4 ++-- .../T8003967/DetectMutableStaticFields.java | 1 - .../tools/javac/diags/CheckResourceKeys.java | 10 ++------ 7 files changed, 31 insertions(+), 36 deletions(-) diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Lint.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Lint.java index dd12bb91a4c82..535ddde45161a 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Lint.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Lint.java @@ -29,10 +29,11 @@ import java.util.Arrays; import java.util.Collections; import java.util.EnumSet; +import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Optional; -import java.util.concurrent.ConcurrentHashMap; +import java.util.Set; import java.util.stream.Stream; import com.sun.tools.javac.main.Option; @@ -122,7 +123,7 @@ public Lint suppress(LintCategory... lc) { private EnumSet values; private EnumSet suppressedValues; - private static final Map map = new ConcurrentHashMap<>(20); + private static final Map map = new LinkedHashMap<>(40); @SuppressWarnings("this-escape") protected Lint(Context context) { @@ -420,7 +421,7 @@ public enum LintCategory { optionList.add(option); Stream.of(aliases).forEach(optionList::add); this.optionList = Collections.unmodifiableList(optionList); - this.optionList.forEach(string -> map.put(string, this)); + this.optionList.forEach(ident -> map.put(ident, this)); } /** @@ -433,6 +434,13 @@ public static Optional get(String option) { return Optional.ofNullable(map.get(option)); } + /** + * Get all lint category option strings and aliases. + */ + public static Set options() { + return Collections.unmodifiableSet(map.keySet()); + } + public static EnumSet newEmptySet() { return EnumSet.noneOf(LintCategory.class); } diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/main/Option.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/main/Option.java index 035ae0396c97d..f4122cebb64af 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/main/Option.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/main/Option.java @@ -39,7 +39,6 @@ import java.util.EnumSet; import java.util.Iterator; import java.util.LinkedHashSet; -import java.util.List; import java.util.Locale; import java.util.ServiceLoader; import java.util.Set; @@ -495,21 +494,10 @@ public void process(OptionHelper helper, String option) throws InvalidValueExcep String.format(LINT_KEY_FORMAT, LINT_CUSTOM_ALL, log.localize(PrefixKind.JAVAC, "opt.Xlint.all"))); - for (LintCategory lc : LintCategory.values()) { - log.printRawLines(WriterKind.STDOUT, - String.format(LINT_KEY_FORMAT, - lc.option, - log.localize(PrefixKind.JAVAC, - "opt.Xlint.desc." + lc.option))); - int numOptions = lc.optionList.size(); - if (numOptions > 1) { - String aliases = lc.optionList.subList(1, numOptions).stream().collect(Collectors.joining(", ")); - log.printRawLines(WriterKind.STDOUT, - String.format(LINT_KEY_FORMAT, - "", - (numOptions > 2 ? "Aliases" : "Alias") + ": " + aliases)); - } - } + LintCategory.options().forEach(ident -> log.printRawLines(WriterKind.STDOUT, + String.format(LINT_KEY_FORMAT, + ident, + log.localize(PrefixKind.JAVAC, "opt.Xlint.desc." + ident)))); log.printRawLines(WriterKind.STDOUT, String.format(LINT_KEY_FORMAT, LINT_CUSTOM_NONE, @@ -1396,8 +1384,7 @@ public static PkgInfo get(Options options) { private static Set getXLintChoices() { Set choices = new LinkedHashSet<>(); choices.add(LINT_CUSTOM_ALL); - Stream.of(Lint.LintCategory.values()) - .flatMap(lc -> lc.optionList.stream()) + Lint.LintCategory.options().stream() .flatMap(ident -> Stream.of(ident, "-" + ident)) .forEach(choices::add); choices.add(LINT_CUSTOM_NONE); diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/javac.properties b/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/javac.properties index aa4add81d342f..d74f5f14d15ef 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/javac.properties +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/javac.properties @@ -291,6 +291,12 @@ javac.opt.Xlint.desc.preview=\ javac.opt.Xlint.desc.restricted=\ Warn about use of restricted methods. +javac.opt.Xlint.desc.synchronization=\ + Warn about synchronization attempts on instances of value-based classes.\n\ +\ This key is a deprecated alias for ''identity'', which has the same uses and\n\ +\ effects. Users are encouraged to use the ''identity'' category for all future\n\ +\ and existing uses of ''synchronization''. + javac.opt.Xlint.desc.identity=\ Warn about uses of value-based classes where an identity class is expected. diff --git a/src/jdk.compiler/share/classes/module-info.java b/src/jdk.compiler/share/classes/module-info.java index 552967159b262..33cff9379f2fa 100644 --- a/src/jdk.compiler/share/classes/module-info.java +++ b/src/jdk.compiler/share/classes/module-info.java @@ -187,10 +187,11 @@ * and interfaces * {@code static} accessing a static member using an instance * {@code strictfp} unnecessary use of the {@code strictfp} modifier - * {@code synchronization} synchronization attempts on instances of value-based classes, - * superseded by the {@code identity} warning category which has the same - * uses and effects. Users are encouraged to use the {@code identity} - * category for all future an existing uses of {@code synchronization} + * {@code synchronization} synchronization attempts on instances of value-based classes; + * this key is a deprecated alias for {@code identity}, which has + * the same uses and effects. Users are encouraged to use the + * {@code identity} category for all future and existing uses of + * {@code synchronization} * {@code text-blocks} inconsistent white space characters in text block indentation * {@code this-escape} superclass constructor leaking {@code this} before subclass initialized * {@code try} issues relating to use of {@code try} blocks diff --git a/src/jdk.compiler/share/man/javac.md b/src/jdk.compiler/share/man/javac.md index 3732598b862e4..997023487b0ae 100644 --- a/src/jdk.compiler/share/man/javac.md +++ b/src/jdk.compiler/share/man/javac.md @@ -649,9 +649,9 @@ file system locations may be directories, JAR files or JMOD files. - `strictfp`: Warns about unnecessary use of the `strictfp` modifier. - `synchronization`: Warns about synchronization attempts on instances - of value-based classes. Superseded by the `identity` warning category + of value-based classes. This key is a deprecated alias for `identity`, which has the same uses and effects. Users are encouraged to use the - `identity` category for all future an existing uses of `synchronization`. + `identity` category for all future and existing uses of `synchronization`. - `text-blocks`: Warns about inconsistent white space characters in text block indentation. diff --git a/test/langtools/tools/javac/T8003967/DetectMutableStaticFields.java b/test/langtools/tools/javac/T8003967/DetectMutableStaticFields.java index 4c49cbdb4eaf6..ef3e400481d8a 100644 --- a/test/langtools/tools/javac/T8003967/DetectMutableStaticFields.java +++ b/test/langtools/tools/javac/T8003967/DetectMutableStaticFields.java @@ -93,7 +93,6 @@ private static void ignore(String className, String... fields) { ignore("com/sun/tools/javac/util/JavacMessages", "defaultBundle", "defaultMessages"); ignore("com/sun/tools/javac/file/JRTIndex", "sharedInstance"); ignore("com/sun/tools/javac/main/JavaCompiler", "versionRB"); - ignore("com/sun/tools/javac/code/Lint$LintCategory", "alias"); ignore("com/sun/tools/javac/code/Type", "moreInfo"); ignore("com/sun/tools/javac/util/SharedNameTable", "freelist"); ignore("com/sun/tools/javac/util/Log", "useRawMessages"); diff --git a/test/langtools/tools/javac/diags/CheckResourceKeys.java b/test/langtools/tools/javac/diags/CheckResourceKeys.java index 9f6f37dc23d0b..1675d99275e4b 100644 --- a/test/langtools/tools/javac/diags/CheckResourceKeys.java +++ b/test/langtools/tools/javac/diags/CheckResourceKeys.java @@ -32,6 +32,7 @@ import java.io.*; import java.util.*; import java.util.regex.*; +import java.util.stream.Stream; import javax.tools.*; import java.lang.classfile.*; import java.lang.classfile.constantpool.*; @@ -175,14 +176,7 @@ void findDeadKeys(Set codeStrings, Set resourceKeys) { //check lint description keys: if (s.startsWith("opt.Xlint.desc.")) { String option = s.substring(15); - boolean found = false; - - for (LintCategory lc : LintCategory.values()) { - if (option.equals(lc.option)) - found = true; - } - - if (found) + if (LintCategory.options().contains(option)) continue; } From 63fd151f1d47f79629e9489163fab503877685ca Mon Sep 17 00:00:00 2001 From: Vicente Romero Zaldivar <62155190+vicente-romero-oracle@users.noreply.github.com> Date: Sat, 10 May 2025 16:23:55 -0400 Subject: [PATCH 31/34] Update src/jdk.compiler/share/classes/com/sun/tools/javac/code/Lint.java Co-authored-by: Chen Liang --- .../share/classes/com/sun/tools/javac/code/Lint.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Lint.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Lint.java index 535ddde45161a..ede2511f35c48 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Lint.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Lint.java @@ -419,7 +419,7 @@ public enum LintCategory { this.annotationSuppression = annotationSuppression; ArrayList optionList = new ArrayList<>(1 + aliases.length); optionList.add(option); - Stream.of(aliases).forEach(optionList::add); + Collections.addAll(optionList, aliases); this.optionList = Collections.unmodifiableList(optionList); this.optionList.forEach(ident -> map.put(ident, this)); } From 700c150c6d4c28062b8d333cf700d80e246b3e2e Mon Sep 17 00:00:00 2001 From: Jan Lahoda Date: Wed, 14 May 2025 13:36:03 +0200 Subject: [PATCH 32/34] Adjustments, annotation filtering, adding more tests --- .../tools/symbolgenerator/CreateSymbols.java | 10 +- .../com/sun/tools/javac/code/Flags.java | 5 + .../com/sun/tools/javac/code/Symbol.java | 3 +- .../com/sun/tools/javac/code/Symtab.java | 10 +- .../com/sun/tools/javac/comp/Annotate.java | 5 + .../com/sun/tools/javac/comp/Check.java | 12 +- .../com/sun/tools/javac/jvm/ClassReader.java | 11 +- .../com/sun/tools/javac/util/Names.java | 6 + .../javac/platform/RequiresIdentityTest.java | 168 ++++++++++++++++-- .../createsymbols/CreateSymbolsTest.java | 13 -- .../createsymbols/CreateSymbolsTestImpl.java | 65 +++++++ 11 files changed, 274 insertions(+), 34 deletions(-) diff --git a/make/langtools/src/classes/build/tools/symbolgenerator/CreateSymbols.java b/make/langtools/src/classes/build/tools/symbolgenerator/CreateSymbols.java index 2ecd8cc816b73..1bb3feed89cc8 100644 --- a/make/langtools/src/classes/build/tools/symbolgenerator/CreateSymbols.java +++ b/make/langtools/src/classes/build/tools/symbolgenerator/CreateSymbols.java @@ -307,7 +307,7 @@ public void createSymbols(String ctDescriptionFileExtra, String ctDescriptionFil private static final String VALUE_BASED_ANNOTATION_INTERNAL = "Ljdk/internal/ValueBased+Annotation;"; private static final String REQUIRES_IDENTITY_ANNOTATION = - "Ljdk/internal/RequieresIdentity;"; + "Ljdk/internal/RequiresIdentity;"; private static final String REQUIRES_IDENTITY_ANNOTATION_INTERNAL = "Ljdk/internal/RequiresIdentity+Annotation;"; public static final Set HARDCODED_ANNOTATIONS = new HashSet<>( @@ -3389,6 +3389,8 @@ public int hashCode() { hash = 59 * hash + Objects.hashCode(this.descriptor); hash = 59 * hash + Objects.hashCode(this.thrownTypes); hash = 59 * hash + Objects.hashCode(this.annotationDefaultValue); + hash = 59 * hash + Objects.hashCode(this.classParameterAnnotations); + hash = 59 * hash + Objects.hashCode(this.runtimeParameterAnnotations); return hash; } @@ -3413,6 +3415,12 @@ public boolean equals(Object obj) { if (!Objects.equals(this.annotationDefaultValue, other.annotationDefaultValue)) { return false; } + if (!Objects.equals(this.classParameterAnnotations, other.classParameterAnnotations)) { + return false; + } + if (!Objects.equals(this.runtimeParameterAnnotations, other.runtimeParameterAnnotations)) { + return false; + } return true; } diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Flags.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Flags.java index 53abf99ed7d2b..2d1ebcfebe8c5 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Flags.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Flags.java @@ -399,6 +399,11 @@ public static EnumSet asFlagSet(long flags) { */ public static final long RESTRICTED = 1L<<62; // MethodSymbols + /** + * Flag to indicate parameters that require identity. + */ + public static final long REQUIRES_IDENTITY = 1L<<62; // VarSymbols (parameters) + /** * Flag to indicate type annotations have been queued for field initializers. */ diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Symbol.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Symbol.java index 54b3fe8a56220..e5eac0786f665 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Symbol.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Symbol.java @@ -969,7 +969,8 @@ public Attribute.Compound getAttribute(Class annoType) boolean isCurrentSymbolsAnnotation(Attribute.TypeCompound anno, int index) { return (anno.position.type == TargetType.CLASS_TYPE_PARAMETER || anno.position.type == TargetType.METHOD_TYPE_PARAMETER) && - anno.position.parameter_index == index; + anno.position.parameter_index == index && + anno.type.tsym.flatName() != name.table.names.requiresIdentityInternal; } diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Symtab.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Symtab.java index 2079532eb2c2e..762932fe90463 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Symtab.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Symtab.java @@ -384,9 +384,15 @@ public boolean isTerminal() { // Enter a synthetic class that is used to mark classes in ct.sym. // This class does not have a class file. private Type enterSyntheticAnnotation(String name) { + return enterSyntheticAnnotation(names.fromString(name)); + } + + // Enter a synthetic class that is used to mark classes in ct.sym. + // This class does not have a class file. + private Type enterSyntheticAnnotation(Name name) { // for now, leave the module null, to prevent problems from synthesizing the // existence of a class in any specific module, including noModule - ClassType type = (ClassType)enterClass(java_base, names.fromString(name)).type; + ClassType type = (ClassType)enterClass(java_base, name).type; ClassSymbol sym = (ClassSymbol)type.tsym; sym.completer = Completer.NULL_COMPLETER; sym.flags_field = PUBLIC|ACYCLIC|ANNOTATION|INTERFACE; @@ -613,7 +619,7 @@ public R accept(ElementVisitor v, P p) { valueBasedType = enterClass("jdk.internal.ValueBased"); valueBasedInternalType = enterSyntheticAnnotation("jdk.internal.ValueBased+Annotation"); requiresIdentityType = enterClass("jdk.internal.RequiresIdentity"); - requiresIdentityInternalType = enterSyntheticAnnotation("jdk.internal.RequiresIdentity+Annotation"); + requiresIdentityInternalType = enterSyntheticAnnotation(names.requiresIdentityInternal); classDescType = enterClass("java.lang.constant.ClassDesc"); enumDescType = enterClass("java.lang.Enum$EnumDesc"); // For serialization lint checking diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Annotate.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Annotate.java index 35672b2b1bc93..5bddac636ea53 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Annotate.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Annotate.java @@ -384,6 +384,11 @@ private void annotateNow(Symbol toAnnotate, && types.isSameType(c.type, syms.restrictedType)) { toAnnotate.flags_field |= Flags.RESTRICTED; } + + if (!c.type.isErroneous() + && types.isSameType(c.type, syms.requiresIdentityType)) { + toAnnotate.flags_field |= Flags.REQUIRES_IDENTITY; + } } List buf = List.nil(); diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Check.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Check.java index b4c363566a7d9..f3e51ba46a6ab 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Check.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Check.java @@ -5736,14 +5736,14 @@ void checkRequiresIdentity(JCTree tree, Lint lint) { if (!argExps.isEmpty() && msym instanceof MethodSymbol ms && ms.params != null) { VarSymbol lastParam = ms.params.head; for (VarSymbol param: ms.params) { - if (param.attribute(syms.requiresIdentityType.tsym) != null && argExps.head.type.isValueBased()) { + if ((param.flags_field & REQUIRES_IDENTITY) != 0 && argExps.head.type.isValueBased()) { lint.logIfEnabled(argExps.head.pos(), LintWarnings.AttemptToUseValueBasedWhereIdentityExpected); } lastParam = param; argExps = argExps.tail; } while (argExps != null && !argExps.isEmpty() && lastParam != null) { - if (lastParam.attribute(syms.requiresIdentityType.tsym) != null && argExps.head.type.isValueBased()) { + if ((lastParam.flags_field & REQUIRES_IDENTITY) != 0 && argExps.head.type.isValueBased()) { lint.logIfEnabled(argExps.head.pos(), LintWarnings.AttemptToUseValueBasedWhereIdentityExpected); } argExps = argExps.tail; @@ -5821,7 +5821,7 @@ public Void visitClassType(ClassType t, Set seen) { SymbolMetadata sm = t.tsym.getMetadata(); if (sm != null && !t.getTypeArguments().isEmpty()) { for (Attribute.TypeCompound ta: sm.getTypeAttributes().stream() - .filter(ta -> ta.type.tsym == syms.requiresIdentityType.tsym).toList()) { + .filter(ta -> isRequiresIdentityAnnotation(ta.type.tsym)).toList()) { Type type = t.getTypeArguments().get(ta.position.parameter_index); if (type != null && type.isValueBased()) { requiresWarning = true; @@ -5847,7 +5847,7 @@ private void checkIfTypeParamsRequiresIdentity(SymbolMetadata sm, } if (sm != null) for (Attribute.TypeCompound ta : sm.getTypeAttributes().stream() - .filter(ta -> ta.type.tsym == syms.requiresIdentityType.tsym).toList()) { + .filter(ta -> isRequiresIdentityAnnotation(ta.type.tsym)).toList()) { Type paramType = typeParamTrees.get(ta.position.parameter_index).type; if (paramType != null && paramType.isValueBased()) lint.logIfEnabled( @@ -5857,4 +5857,8 @@ private void checkIfTypeParamsRequiresIdentity(SymbolMetadata sm, } } } + private boolean isRequiresIdentityAnnotation(TypeSymbol annoType) { + return annoType == syms.requiresIdentityType.tsym || + annoType.flatName() == syms.requiresIdentityInternalType.tsym.flatName(); + } } diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassReader.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassReader.java index 0173ab109d7d3..cf751ff6b3097 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassReader.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassReader.java @@ -1556,6 +1556,9 @@ else if (proxy.type.tsym.flatName() == syms.profileType.tsym.flatName()) { } else if (proxy.type.tsym.flatName() == syms.restrictedInternalType.tsym.flatName()) { Assert.check(sym.kind == MTH); sym.flags_field |= RESTRICTED; + } else if (proxy.type.tsym.flatName() == syms.requiresIdentityInternalType.tsym.flatName()) { + Assert.check(sym.kind == VAR); + sym.flags_field |= REQUIRES_IDENTITY; } else { if (proxy.type.tsym == syms.annotationTargetType.tsym) { target = proxy; @@ -1572,6 +1575,9 @@ else if (proxy.type.tsym.flatName() == syms.profileType.tsym.flatName()) { } else if (proxy.type.tsym == syms.restrictedType.tsym) { Assert.check(sym.kind == MTH); sym.flags_field |= RESTRICTED; + } else if (proxy.type.tsym == syms.requiresIdentityType.tsym) { + Assert.check(sym.kind == VAR); + sym.flags_field |= REQUIRES_IDENTITY; } proxies.append(proxy); } @@ -2809,9 +2815,8 @@ void setParameters(MethodSymbol sym, Type jvmType) { params.append(param); if (parameterAnnotations != null) { ParameterAnnotations annotations = parameterAnnotations[annotationIndex]; - if (annotations != null && annotations.proxies != null - && !annotations.proxies.isEmpty()) { - annotate.normal(new AnnotationCompleter(param, annotations.proxies)); + if (annotations != null && annotations.proxies != null) { + attachAnnotations(param, annotations.proxies); } } nameIndexLvt += Code.width(t); diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/util/Names.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/util/Names.java index d9aec7c459229..f5df8baddbd30 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/util/Names.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/util/Names.java @@ -228,6 +228,9 @@ public static Names instance(Context context) { public final Name enumSwitch; public final Name enumConstant; + // special annotation names + public final Name requiresIdentityInternal; + public final Name.Table table; @SuppressWarnings("this-escape") @@ -412,6 +415,9 @@ record = fromString("record"); typeSwitch = fromString("typeSwitch"); enumSwitch = fromString("enumSwitch"); enumConstant = fromString("enumConstant"); + + // special annotations: + requiresIdentityInternal = fromString("jdk.internal.RequiresIdentity+Annotation"); } protected Name.Table createTable(Options options) { diff --git a/test/langtools/tools/javac/platform/RequiresIdentityTest.java b/test/langtools/tools/javac/platform/RequiresIdentityTest.java index 9a47ec3281974..f10d6e8054cea 100644 --- a/test/langtools/tools/javac/platform/RequiresIdentityTest.java +++ b/test/langtools/tools/javac/platform/RequiresIdentityTest.java @@ -1,18 +1,166 @@ /* - * @test /nodynamiccopyright/ - * @bug 8354556 - * @summary Expand value-based class warnings to java.lang.ref API - * @compile/fail/ref=RequiresIdentityTest.out -Werror -XDrawDiagnostics -Xlint:identity RequiresIdentityTest.java - * @compile/fail/ref=RequiresIdentityTest.out -Werror -XDrawDiagnostics -Xlint:identity --release ${jdk.version} RequiresIdentityTest.java + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. */ -import java.lang.ref.Reference; -import java.util.Optional; +/** + * @test + * @bug 8356894 + * @summary Verify source level checks are performed properly + * @library /tools/lib + * @modules jdk.compiler/com.sun.tools.javac.api + * jdk.compiler/com.sun.tools.javac.main + * jdk.compiler/com.sun.tools.javac.util + * @build toolbox.ToolBox toolbox.JavacTask + * @run main RequiresIdentityTest +*/ +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.List; -public class RequiresIdentityTest { +import toolbox.TestRunner; +import toolbox.JavacTask; +import toolbox.Task; +import toolbox.ToolBox; - void test() { - Reference> r; +public class RequiresIdentityTest extends TestRunner { + + ToolBox tb; + + public static void main(String... args) throws Exception { + new RequiresIdentityTest().runTests(); + } + + RequiresIdentityTest() { + super(System.err); + tb = new ToolBox(); + } + + public void runTests() throws Exception { + runTests(m -> new Object[] { Paths.get(m.getName()) }); } + + @Test + public void testReleaseWorksAsCurrentVersion(Path base) throws Exception { + Path src = base.resolve("src"); + Path classes = base.resolve("classes"); + tb.writeJavaFiles(src, + """ + import java.util.WeakHashMap; + import java.util.Optional; + + public class Test { + void test() { + WeakHashMap, Object> m = null; + m.put(Optional.empty(), 1); + } + } + """); + + Files.createDirectories(classes); + + var expectedErrors = List.of( + "Test.java:6:20: compiler.warn.attempt.to.use.value.based.where.identity.expected", + "Test.java:7:29: compiler.warn.attempt.to.use.value.based.where.identity.expected", + "2 warnings" + ); + + { + var actualErrors = + new JavacTask(tb) + .options("-XDrawDiagnostics") + .outdir(classes) + .files(tb.findJavaFiles(src)) + .run() + .writeAll() + .getOutputLines(Task.OutputKind.DIRECT); + if (!expectedErrors.equals(actualErrors)) { + throw new AssertionError("Incorrect errors, expected: " + List.of(expectedErrors) + + ", actual: " + actualErrors); + } + } + + { + var actualErrors = + new JavacTask(tb) + .options("--release", System.getProperty("java.specification.version"), + "-XDrawDiagnostics") + .outdir(classes) + .files(tb.findJavaFiles(src)) + .run() + .writeAll() + .getOutputLines(Task.OutputKind.DIRECT); + if (!expectedErrors.equals(actualErrors)) { + throw new AssertionError("Incorrect errors, expected: " + List.of(expectedErrors) + + ", actual: " + actualErrors); + } + } + } + + @Test + public void testModel(Path base) throws Exception { + { + List printed = + new JavacTask(tb) + .options("-Xprint") + .classes("java.util.WeakHashMap") + .run() + .writeAll() + .getOutputLines(Task.OutputKind.STDOUT); + + printed.removeIf(l -> !l.contains("put(") && !l.contains("class WeakHashMap<")); + + List expected = List.of( + "public class WeakHashMap<@jdk.internal.RequiresIdentity K, V> extends java.util.AbstractMap implements java.util.Map {", + " public V put(@jdk.internal.RequiresIdentity sealed K key," + ); + if (!expected.equals(printed)) { + throw new AssertionError("Expected: " + expected + + ", but got: " + printed); + } + } + + { + List printed = + new JavacTask(tb) + .options("--release", System.getProperty("java.specification.version"), + "-Xprint") + .classes("java.util.WeakHashMap") + .run() + .writeAll() + .getOutputLines(Task.OutputKind.STDOUT); + + printed.removeIf(l -> !l.contains("put(") && !l.contains("class WeakHashMap<")); + + List expected = List.of( + "public class WeakHashMap extends java.util.AbstractMap implements java.util.Map {", + " public V put(sealed K arg0," + ); + if (!expected.equals(printed)) { + throw new AssertionError("Expected: " + expected + + ", but got: " + printed); + } + } + } + } diff --git a/test/langtools/tools/javac/platform/createsymbols/CreateSymbolsTest.java b/test/langtools/tools/javac/platform/createsymbols/CreateSymbolsTest.java index 76c13582b73cd..ac42c26488adb 100644 --- a/test/langtools/tools/javac/platform/createsymbols/CreateSymbolsTest.java +++ b/test/langtools/tools/javac/platform/createsymbols/CreateSymbolsTest.java @@ -21,19 +21,6 @@ * questions. */ -/** - * @test - * @bug 8072480 8277106 8331027 - * @summary Unit test for CreateSymbols - * @modules java.compiler - * jdk.compiler/com.sun.tools.javac.api - * jdk.compiler/com.sun.tools.javac.jvm - * jdk.compiler/com.sun.tools.javac.main - * jdk.compiler/com.sun.tools.javac.util - * @clean * - * @run main/othervm CreateSymbolsTest - */ - import java.io.IOException; import java.net.URL; import java.net.URLClassLoader; diff --git a/test/langtools/tools/javac/platform/createsymbols/CreateSymbolsTestImpl.java b/test/langtools/tools/javac/platform/createsymbols/CreateSymbolsTestImpl.java index 13e1c1cb206d9..2680ca6488ff0 100644 --- a/test/langtools/tools/javac/platform/createsymbols/CreateSymbolsTestImpl.java +++ b/test/langtools/tools/javac/platform/createsymbols/CreateSymbolsTestImpl.java @@ -21,6 +21,19 @@ * questions. */ +/** + * @test + * @bug 8072480 8277106 8331027 + * @summary Unit test for CreateSymbols + * @modules java.compiler + * jdk.compiler/com.sun.tools.javac.api + * jdk.compiler/com.sun.tools.javac.jvm + * jdk.compiler/com.sun.tools.javac.main + * jdk.compiler/com.sun.tools.javac.util + * @clean * + * @run main/othervm CreateSymbolsTest + */ + import java.io.File; import java.io.InputStream; import java.io.Writer; @@ -959,6 +972,58 @@ public class T<@t.AnnInvisible @t.AnnVisible E extends java.util.@t.AnnInvisible """); } + @Test + void testParameterAnnotations() throws Exception { + doPrintElementTest(""" + package t; + public class T { + public void test(int p1, int p2) { + } + } + """, + """ + package t; + import java.lang.annotation.*; + import java.util.*; + public class T { + public void test(@AnnVisible int p1, @AnnInvisible int p2) { + } + } + @Retention(RetentionPolicy.RUNTIME) + @Target(ElementType.PARAMETER) + @interface AnnVisible { + } + @Retention(RetentionPolicy.CLASS) + @Target(ElementType.PARAMETER) + @interface AnnInvisible { + } + """, + "t.T", + """ + package t; + + public class T { + + public T(); + + public void test(int arg0, + int arg1); + } + """, + "t.T", + """ + package t; + + public class T { + + public T(); + + public void test(@t.AnnVisible int arg0, + @t.AnnInvisible int arg1); + } + """); + } + void doTestData(String data, String... code) throws Exception { String testClasses = System.getProperty("test.classes"); From 456ff2558994d580b2f4ececfd0309b32515b1dd Mon Sep 17 00:00:00 2001 From: Jan Lahoda Date: Wed, 14 May 2025 14:30:25 +0200 Subject: [PATCH 33/34] Fixing build --- .../share/classes/com/sun/tools/javac/comp/Annotate.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Annotate.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Annotate.java index 5bddac636ea53..62cb91ce4099c 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Annotate.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Annotate.java @@ -386,7 +386,9 @@ private void annotateNow(Symbol toAnnotate, } if (!c.type.isErroneous() + && toAnnotate.kind == VAR && types.isSameType(c.type, syms.requiresIdentityType)) { + System.err.println("toAnnotate: " + toAnnotate); toAnnotate.flags_field |= Flags.REQUIRES_IDENTITY; } } From daed20ffe0e29db24e68eb5dc4abb42af331c7c2 Mon Sep 17 00:00:00 2001 From: Jan Lahoda Date: Wed, 14 May 2025 16:37:13 +0200 Subject: [PATCH 34/34] Cleanup. --- .../share/classes/com/sun/tools/javac/comp/Annotate.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Annotate.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Annotate.java index 62cb91ce4099c..3da832484364e 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Annotate.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Annotate.java @@ -388,7 +388,6 @@ private void annotateNow(Symbol toAnnotate, if (!c.type.isErroneous() && toAnnotate.kind == VAR && types.isSameType(c.type, syms.requiresIdentityType)) { - System.err.println("toAnnotate: " + toAnnotate); toAnnotate.flags_field |= Flags.REQUIRES_IDENTITY; } }