mirror of
https://github.com/openjdk/jdk.git
synced 2026-01-28 03:58:21 +00:00
8354556: Expand value-based class warnings to java.lang.ref API
Co-authored-by: Archie Cobbs <acobbs@openjdk.org> Reviewed-by: jlahoda
This commit is contained in:
parent
fbeea1daef
commit
637e9d16dd
@ -304,13 +304,18 @@ public class CreateSymbols {
|
||||
"Ljdk/internal/ValueBased;";
|
||||
private static final String VALUE_BASED_ANNOTATION_INTERNAL =
|
||||
"Ljdk/internal/ValueBased+Annotation;";
|
||||
private static final String REQUIRES_IDENTITY_ANNOTATION =
|
||||
"Ljdk/internal/RequiresIdentity;";
|
||||
private static final String REQUIRES_IDENTITY_ANNOTATION_INTERNAL =
|
||||
"Ljdk/internal/RequiresIdentity+Annotation;";
|
||||
public static final Set<String> 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<String> allClasses = data.classes.name2Class.keySet();
|
||||
@ -1021,6 +1026,12 @@ public class CreateSymbols {
|
||||
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:
|
||||
@ -2202,6 +2213,7 @@ public class CreateSymbols {
|
||||
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
|
||||
}
|
||||
|
||||
|
||||
@ -219,7 +219,7 @@ public final class Cleaner {
|
||||
* @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);
|
||||
|
||||
@ -51,7 +51,7 @@ import jdk.internal.vm.annotation.IntrinsicCandidate;
|
||||
* @since 1.2
|
||||
*/
|
||||
|
||||
public non-sealed class PhantomReference<T> extends Reference<T> {
|
||||
public non-sealed class PhantomReference<@jdk.internal.RequiresIdentity T> extends Reference<T> {
|
||||
|
||||
/**
|
||||
* Returns this reference object's referent. Because the referent of a
|
||||
@ -101,7 +101,7 @@ public non-sealed class PhantomReference<T> extends Reference<T> {
|
||||
* @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<? super T> q) {
|
||||
public PhantomReference(@jdk.internal.RequiresIdentity T referent, ReferenceQueue<? super T> q) {
|
||||
super(referent, q);
|
||||
}
|
||||
|
||||
|
||||
@ -44,7 +44,7 @@ import jdk.internal.ref.Cleaner;
|
||||
* @sealedGraph
|
||||
*/
|
||||
|
||||
public abstract sealed class Reference<T>
|
||||
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
|
||||
|
||||
@ -46,7 +46,7 @@ import jdk.internal.vm.ContinuationSupport;
|
||||
* @since 1.2
|
||||
*/
|
||||
|
||||
public class ReferenceQueue<T> {
|
||||
public class ReferenceQueue<@jdk.internal.RequiresIdentity T> {
|
||||
private static class Null extends ReferenceQueue<Object> {
|
||||
@Override
|
||||
boolean enqueue(Reference<?> r) {
|
||||
|
||||
@ -62,7 +62,7 @@ package java.lang.ref;
|
||||
* @since 1.2
|
||||
*/
|
||||
|
||||
public non-sealed class SoftReference<T> extends Reference<T> {
|
||||
public non-sealed class SoftReference<@jdk.internal.RequiresIdentity T> extends Reference<T> {
|
||||
|
||||
/**
|
||||
* Timestamp clock, updated by the garbage collector
|
||||
@ -82,7 +82,7 @@ public non-sealed class SoftReference<T> extends Reference<T> {
|
||||
*
|
||||
* @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 non-sealed class SoftReference<T> extends Reference<T> {
|
||||
* or {@code null} if registration is not required
|
||||
*
|
||||
*/
|
||||
public SoftReference(T referent, ReferenceQueue<? super T> q) {
|
||||
public SoftReference(@jdk.internal.RequiresIdentity T referent, ReferenceQueue<? super T> q) {
|
||||
super(referent, q);
|
||||
this.timestamp = clock;
|
||||
}
|
||||
|
||||
@ -46,7 +46,7 @@ package java.lang.ref;
|
||||
* @since 1.2
|
||||
*/
|
||||
|
||||
public non-sealed class WeakReference<T> extends Reference<T> {
|
||||
public non-sealed class WeakReference<@jdk.internal.RequiresIdentity T> extends Reference<T> {
|
||||
|
||||
/**
|
||||
* Creates a new weak reference that refers to the given object. The new
|
||||
@ -54,7 +54,7 @@ public non-sealed class WeakReference<T> extends Reference<T> {
|
||||
*
|
||||
* @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 non-sealed class WeakReference<T> extends Reference<T> {
|
||||
* @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<? super T> q) {
|
||||
public WeakReference(@jdk.internal.RequiresIdentity T referent, ReferenceQueue<? super T> q) {
|
||||
super(referent, q);
|
||||
}
|
||||
|
||||
|
||||
@ -132,7 +132,7 @@ import java.util.function.Consumer;
|
||||
* @see java.util.HashMap
|
||||
* @see java.lang.ref.WeakReference
|
||||
*/
|
||||
public class WeakHashMap<K,V>
|
||||
public class WeakHashMap<@jdk.internal.RequiresIdentity K,V>
|
||||
extends AbstractMap<K,V>
|
||||
implements Map<K,V> {
|
||||
|
||||
@ -457,7 +457,7 @@ public class WeakHashMap<K,V>
|
||||
* (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<K,V>[] tab = getTable();
|
||||
|
||||
@ -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 <a href="../lang/doc-files/ValueBased.html">value-based classes</a>
|
||||
* 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 {
|
||||
}
|
||||
@ -46,4 +46,3 @@ import static java.lang.annotation.ElementType.TYPE;
|
||||
@Target(value={TYPE})
|
||||
public @interface ValueBased {
|
||||
}
|
||||
|
||||
|
||||
@ -25,11 +25,15 @@
|
||||
|
||||
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.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;
|
||||
@ -119,7 +123,7 @@ public class Lint {
|
||||
private EnumSet<LintCategory> values;
|
||||
private EnumSet<LintCategory> suppressedValues;
|
||||
|
||||
private static final Map<String, LintCategory> map = new ConcurrentHashMap<>(20);
|
||||
private static final Map<String, LintCategory> map = new LinkedHashMap<>(40);
|
||||
|
||||
@SuppressWarnings("this-escape")
|
||||
protected Lint(Context context) {
|
||||
@ -149,10 +153,10 @@ public class Lint {
|
||||
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 {
|
||||
@ -173,15 +177,15 @@ public class Lint {
|
||||
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.isExplicitlyEnabled(Option.XLINT, lc)) {
|
||||
values.add(lc);
|
||||
} else if (options.isSet(Option.XLINT_CUSTOM, "-" + lc.option)) {
|
||||
} else if (options.isExplicitlyDisabled(Option.XLINT, lc)) {
|
||||
values.remove(lc);
|
||||
}
|
||||
}
|
||||
@ -261,6 +265,11 @@ public class Lint {
|
||||
*/
|
||||
FINALLY("finally"),
|
||||
|
||||
/**
|
||||
* Warn about uses of @ValueBased classes where an identity class is expected.
|
||||
*/
|
||||
IDENTITY("identity", true, "synchronization"),
|
||||
|
||||
/**
|
||||
* Warn about use of incubating modules.
|
||||
*
|
||||
@ -363,11 +372,6 @@ public class Lint {
|
||||
*/
|
||||
STRICTFP("strictfp"),
|
||||
|
||||
/**
|
||||
* Warn about synchronization attempts on instances of @ValueBased classes.
|
||||
*/
|
||||
SYNCHRONIZATION("synchronization"),
|
||||
|
||||
/**
|
||||
* Warn about issues relating to use of text blocks
|
||||
*
|
||||
@ -410,10 +414,14 @@ public class Lint {
|
||||
this(option, true);
|
||||
}
|
||||
|
||||
LintCategory(String option, boolean annotationSuppression) {
|
||||
LintCategory(String option, boolean annotationSuppression, String... aliases) {
|
||||
this.option = option;
|
||||
this.annotationSuppression = annotationSuppression;
|
||||
map.put(option, this);
|
||||
ArrayList<String> optionList = new ArrayList<>(1 + aliases.length);
|
||||
optionList.add(option);
|
||||
Collections.addAll(optionList, aliases);
|
||||
this.optionList = Collections.unmodifiableList(optionList);
|
||||
this.optionList.forEach(ident -> map.put(ident, this));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -426,13 +434,23 @@ public class Lint {
|
||||
return Optional.ofNullable(map.get(option));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all lint category option strings and aliases.
|
||||
*/
|
||||
public static Set<String> options() {
|
||||
return Collections.unmodifiableSet(map.keySet());
|
||||
}
|
||||
|
||||
public static EnumSet<LintCategory> 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<String> optionList;
|
||||
|
||||
/** Does this category support being suppressed by the {@code @SuppressWarnings} annotation? */
|
||||
public final boolean annotationSuppression;
|
||||
}
|
||||
@ -496,20 +514,6 @@ public class Lint {
|
||||
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<LintCategory> 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<LintCategory> suppressionsFrom(Stream<Attribute.Compound> attributes) {
|
||||
initializeSymbolsIfNeeded();
|
||||
|
||||
@ -228,6 +228,8 @@ public class Symtab {
|
||||
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;
|
||||
|
||||
@ -610,6 +612,8 @@ public class Symtab {
|
||||
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");
|
||||
// For serialization lint checking
|
||||
|
||||
@ -665,6 +665,10 @@ public abstract class Type extends AnnoConstruct implements TypeMirror, PoolCons
|
||||
return (tsym.flags() & FINAL) != 0;
|
||||
}
|
||||
|
||||
public boolean isValueBased() {
|
||||
return tsym != null && (tsym.flags_field & VALUE_BASED) != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Does this type contain occurrences of type t?
|
||||
*/
|
||||
|
||||
@ -1047,8 +1047,10 @@ public class Attr extends JCTree.Visitor {
|
||||
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.checkRequiresIdentity(tree, env.info.lint);
|
||||
|
||||
// Check that receiver type is well-formed.
|
||||
if (tree.recvparam != null) {
|
||||
@ -1328,6 +1330,7 @@ public class Attr extends JCTree.Visitor {
|
||||
log.error(tree, Errors.IllegalRecordComponentName(v));
|
||||
}
|
||||
}
|
||||
chk.checkRequiresIdentity(tree, env.info.lint);
|
||||
}
|
||||
finally {
|
||||
chk.setLint(prevLint);
|
||||
@ -1949,17 +1952,12 @@ public class Attr extends JCTree.Visitor {
|
||||
|
||||
public void visitSynchronized(JCSynchronized tree) {
|
||||
chk.checkRefType(tree.pos(), attribExpr(tree.lock, env));
|
||||
if (isValueBased(tree.lock.type)) {
|
||||
if (tree.lock.type != null && tree.lock.type.isValueBased()) {
|
||||
env.info.lint.logIfEnabled(tree.pos(), LintWarnings.AttemptToSynchronizeOnInstanceOfValueBasedClass);
|
||||
}
|
||||
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
|
||||
@ -2672,6 +2670,7 @@ public class Attr extends JCTree.Visitor {
|
||||
Type capturedRes = resultInfo.checkContext.inferenceContext().cachedCapture(tree, restype, true);
|
||||
result = check(tree, capturedRes, KindSelector.VAL, resultInfo);
|
||||
}
|
||||
chk.checkRequiresIdentity(tree, env.info.lint);
|
||||
chk.validate(tree.typeargs, localEnv);
|
||||
}
|
||||
//where
|
||||
@ -2914,6 +2913,8 @@ public class Attr extends JCTree.Visitor {
|
||||
}
|
||||
}
|
||||
|
||||
chk.checkRequiresIdentity(tree, env.info.lint);
|
||||
|
||||
if (cdef != null) {
|
||||
visitAnonymousClassDefinition(tree, clazz, clazztype, cdef, localEnv, argtypes, typeargtypes, pkind);
|
||||
return;
|
||||
@ -3793,6 +3794,7 @@ public class Attr extends JCTree.Visitor {
|
||||
if (!isSpeculativeRound) {
|
||||
checkAccessibleTypes(that, localEnv, resultInfo.checkContext.inferenceContext(), desc, currentTarget);
|
||||
}
|
||||
chk.checkRequiresIdentity(that, localEnv.info.lint);
|
||||
result = check(that, currentTarget, KindSelector.VAL, resultInfo);
|
||||
} catch (Types.FunctionDescriptorLookupError ex) {
|
||||
JCDiagnostic cause = ex.getDiagnostic();
|
||||
@ -4096,6 +4098,7 @@ public class Attr extends JCTree.Visitor {
|
||||
public void visitTypeCast(final JCTypeCast tree) {
|
||||
Type clazztype = attribType(tree.clazz, env);
|
||||
chk.validate(tree.clazz, env, false);
|
||||
chk.checkRequiresIdentity(tree, env.info.lint);
|
||||
//a fresh environment is required for 292 inference to work properly ---
|
||||
//see Infer.instantiatePolymorphicSignatureInstance()
|
||||
Env<AttrContext> localEnv = env.dup(tree);
|
||||
@ -4237,6 +4240,7 @@ public class Attr extends JCTree.Visitor {
|
||||
} else {
|
||||
matchBindings = new MatchBindings(List.of(v), List.nil());
|
||||
}
|
||||
chk.checkRequiresIdentity(tree, env.info.lint);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -5590,6 +5594,8 @@ public class Attr extends JCTree.Visitor {
|
||||
chk.validate(tree.implementing, env);
|
||||
}
|
||||
|
||||
chk.checkRequiresIdentity(tree, env.info.lint);
|
||||
|
||||
c.markAbstractIfNeeded(types);
|
||||
|
||||
// If this is a non-abstract class, check that it has no abstract
|
||||
|
||||
@ -28,6 +28,7 @@ package com.sun.tools.javac.comp;
|
||||
import java.util.*;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.BiPredicate;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.function.ToIntBiFunction;
|
||||
@ -46,6 +47,7 @@ import com.sun.tools.javac.code.Directive.RequiresDirective;
|
||||
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 +5669,180 @@ public class Check {
|
||||
|
||||
}
|
||||
|
||||
void checkRequiresIdentity(JCTree tree, Lint 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);
|
||||
}
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
case JCTypeCast typeCast -> checkIfIdentityIsExpected(typeCast.clazz.pos(), typeCast.clazz.type, lint);
|
||||
case JCBindingPattern bindingPattern -> {
|
||||
if (bindingPattern.var.vartype != null) {
|
||||
checkIfIdentityIsExpected(bindingPattern.var.vartype.pos(), bindingPattern.var.vartype.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);
|
||||
}
|
||||
}
|
||||
case JCMemberReference mref -> {
|
||||
checkIfIdentityIsExpected(mref.expr.pos(), mref.target, lint);
|
||||
checkIfTypeParamsRequiresIdentity(mref.sym.getMetadata(), mref.typeargs, lint);
|
||||
}
|
||||
case JCPolyExpression poly
|
||||
when (poly instanceof JCNewClass || poly instanceof JCMethodInvocation) -> {
|
||||
if (poly instanceof JCNewClass newClass) {
|
||||
checkIfIdentityIsExpected(newClass.clazz.pos(), newClass.clazz.type, lint);
|
||||
}
|
||||
List<JCExpression> argExps = poly instanceof JCNewClass ?
|
||||
((JCNewClass)poly).args :
|
||||
((JCMethodInvocation)poly).args;
|
||||
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);
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
checkIfTypeParamsRequiresIdentity(
|
||||
msym.getMetadata(),
|
||||
poly instanceof JCNewClass ?
|
||||
((JCNewClass)poly).typeargs :
|
||||
((JCMethodInvocation)poly).typeargs,
|
||||
lint);
|
||||
}
|
||||
}
|
||||
default -> throw new AssertionError("unexpected tree " + tree);
|
||||
}
|
||||
}
|
||||
|
||||
/** Check if a type required an identity class
|
||||
*/
|
||||
private 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 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);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// where
|
||||
private class RequiresIdentityVisitor extends Types.SimpleVisitor<Void, Set<Type>> {
|
||||
boolean requiresWarning = false;
|
||||
|
||||
@Override
|
||||
public Void visitType(Type t, Set<Type> seen) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void visitWildcardType(WildcardType t, Set<Type> seen) {
|
||||
return visit(t.type, seen);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void visitTypeVar(TypeVar t, Set<Type> seen) {
|
||||
if (seen.add(t)) {
|
||||
visit(t.getUpperBound(), seen);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void visitCapturedType(CapturedType t, Set<Type> seen) {
|
||||
if (seen.add(t)) {
|
||||
visit(t.getUpperBound(), seen);
|
||||
visit(t.getLowerBound(), seen);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void visitArrayType(ArrayType t, Set<Type> seen) {
|
||||
return visit(t.elemtype, seen);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void visitClassType(ClassType t, Set<Type> seen) {
|
||||
if (t != null && t.tsym != null) {
|
||||
SymbolMetadata sm = t.tsym.getMetadata();
|
||||
if (sm != null && !t.getTypeArguments().isEmpty()) {
|
||||
if (sm.getTypeAttributes().stream()
|
||||
.filter(ta -> ta.type.tsym == syms.requiresIdentityType.tsym &&
|
||||
t.getTypeArguments().get(ta.position.parameter_index) != null &&
|
||||
t.getTypeArguments().get(ta.position.parameter_index).isValueBased()).findAny().isPresent()) {
|
||||
requiresWarning = true;
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
visit(t.getEnclosingType(), seen);
|
||||
for (Type targ : t.getTypeArguments()) {
|
||||
visit(targ, seen);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
} // RequiresIdentityVisitor
|
||||
|
||||
private void checkIfTypeParamsRequiresIdentity(SymbolMetadata sm,
|
||||
List<JCExpression> typeParamTrees,
|
||||
Lint lint) {
|
||||
if (typeParamTrees != null && !typeParamTrees.isEmpty()) {
|
||||
for (JCExpression targ : typeParamTrees) {
|
||||
checkIfIdentityIsExpected(targ.pos(), targ.type, lint);
|
||||
}
|
||||
if (sm != null)
|
||||
sm.getTypeAttributes().stream()
|
||||
.filter(ta -> (ta.type.tsym == syms.requiresIdentityType.tsym) &&
|
||||
typeParamTrees.get(ta.position.parameter_index).type != null &&
|
||||
typeParamTrees.get(ta.position.parameter_index).type.isValueBased())
|
||||
.forEach(ta -> lint.logIfEnabled(typeParamTrees.get(ta.position.parameter_index).pos(),
|
||||
CompilerProperties.LintWarnings.AttemptToUseValueBasedWhereIdentityExpected));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -205,7 +205,7 @@ public class Modules extends JCTree.Visitor {
|
||||
|
||||
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);
|
||||
|
||||
@ -503,8 +503,7 @@ public class Arguments {
|
||||
}
|
||||
} 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 class Arguments {
|
||||
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) {
|
||||
|
||||
@ -46,6 +46,7 @@ import java.util.StringJoiner;
|
||||
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,18 +492,15 @@ public enum Option {
|
||||
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,
|
||||
String.format(LINT_KEY_FORMAT,
|
||||
lc.option,
|
||||
log.localize(PrefixKind.JAVAC,
|
||||
"opt.Xlint.desc." + lc.option)));
|
||||
}
|
||||
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,
|
||||
"none",
|
||||
LINT_CUSTOM_NONE,
|
||||
log.localize(PrefixKind.JAVAC, "opt.Xlint.none")));
|
||||
super.process(helper, option);
|
||||
}
|
||||
@ -835,6 +833,16 @@ public enum Option {
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 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
|
||||
@ -1081,6 +1089,17 @@ public enum Option {
|
||||
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;
|
||||
}
|
||||
@ -1364,12 +1383,11 @@ public enum Option {
|
||||
|
||||
private static Set<String> getXLintChoices() {
|
||||
Set<String> choices = new LinkedHashSet<>();
|
||||
choices.add("all");
|
||||
for (Lint.LintCategory c : Lint.LintCategory.values()) {
|
||||
choices.add(c.option);
|
||||
choices.add("-" + c.option);
|
||||
}
|
||||
choices.add("none");
|
||||
choices.add(LINT_CUSTOM_ALL);
|
||||
Lint.LintCategory.options().stream()
|
||||
.flatMap(ident -> Stream.of(ident, "-" + ident))
|
||||
.forEach(choices::add);
|
||||
choices.add(LINT_CUSTOM_NONE);
|
||||
return choices;
|
||||
}
|
||||
|
||||
|
||||
@ -4248,10 +4248,14 @@ 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
|
||||
# lint: identity
|
||||
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.use.value.based.where.identity.expected=\
|
||||
use of a value-based class with an operation that expects reliable identity
|
||||
|
||||
# 0: type
|
||||
compiler.err.enclosing.class.type.non.denotable=\
|
||||
enclosing class type: {0}\n\
|
||||
|
||||
@ -292,7 +292,13 @@ 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\
|
||||
\ 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.
|
||||
|
||||
javac.opt.Xdoclint=\
|
||||
Enable recommended checks for problems in javadoc comments
|
||||
|
||||
@ -29,6 +29,7 @@ import java.util.*;
|
||||
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 class Options {
|
||||
return !isSet(option, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether the given lint category is explicitly enabled or disabled.
|
||||
*
|
||||
* <p>
|
||||
* 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;
|
||||
|
||||
@ -164,6 +164,7 @@ import javax.tools.StandardLocation;
|
||||
* <tr><th scope="row">{@code fallthrough} <td>falling through from one case of a {@code switch} statement to
|
||||
* the next
|
||||
* <tr><th scope="row">{@code finally} <td>{@code finally} clauses that do not terminate normally
|
||||
* <tr><th scope="row">{@code identity} <td>use of a value-based class where an identity class is expected
|
||||
* <tr><th scope="row">{@code incubating} <td>use of incubating modules
|
||||
* <tr><th scope="row">{@code lossy-conversions} <td>possible lossy conversions in compound assignment
|
||||
* <tr><th scope="row">{@code missing-explicit-ctor} <td>missing explicit constructors in public and protected classes
|
||||
@ -186,7 +187,11 @@ import javax.tools.StandardLocation;
|
||||
* and interfaces
|
||||
* <tr><th scope="row">{@code static} <td>accessing a static member using an instance
|
||||
* <tr><th scope="row">{@code strictfp} <td>unnecessary use of the {@code strictfp} modifier
|
||||
* <tr><th scope="row">{@code synchronization} <td>synchronization attempts on instances of value-based classes
|
||||
* <tr><th scope="row">{@code synchronization} <td>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}
|
||||
* <tr><th scope="row">{@code text-blocks} <td>inconsistent white space characters in text block indentation
|
||||
* <tr><th scope="row">{@code this-escape} <td>superclass constructor leaking {@code this} before subclass initialized
|
||||
* <tr><th scope="row">{@code try} <td>issues relating to use of {@code try} blocks
|
||||
|
||||
@ -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
|
||||
@ -646,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. 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 and existing uses of `synchronization`.
|
||||
|
||||
- `text-blocks`: Warns about inconsistent white space characters in text
|
||||
block indentation.
|
||||
|
||||
@ -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 @@ public class CheckResourceKeys {
|
||||
//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;
|
||||
}
|
||||
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -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<Integer> field; // should warn
|
||||
}
|
||||
@ -5,8 +5,10 @@
|
||||
* @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 -XDrawDiagnostics -Werror -Xlint:identity 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 --release 16 -XDrawDiagnostics -Werror -Xlint:identity ExternalAbuseOfVbc.java
|
||||
* @compile/ref=LintModeOffAbuseOfVbc.out -XDrawDiagnostics -Werror -Xlint:-synchronization,-identity ExternalAbuseOfVbc.java
|
||||
*/
|
||||
|
||||
public final class ExternalAbuseOfVbc {
|
||||
|
||||
@ -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
|
||||
|
||||
21
test/langtools/tools/javac/lint/RequiresIdentityHelper.java
Normal file
21
test/langtools/tools/javac/lint/RequiresIdentityHelper.java
Normal file
@ -0,0 +1,21 @@
|
||||
/* /nodynamiccopyright/ */
|
||||
|
||||
package java.lang;
|
||||
|
||||
public class RequiresIdentityHelper<@jdk.internal.RequiresIdentity T> {
|
||||
public RequiresIdentityHelper() {}
|
||||
public <@jdk.internal.RequiresIdentity TT> RequiresIdentityHelper(@jdk.internal.RequiresIdentity Object o) {}
|
||||
|
||||
class RequiresIdentity2<TT> {
|
||||
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);
|
||||
}
|
||||
}
|
||||
90
test/langtools/tools/javac/lint/RequiresIdentityTest.java
Normal file
90
test/langtools/tools/javac/lint/RequiresIdentityTest.java
Normal file
@ -0,0 +1,90 @@
|
||||
/*
|
||||
* @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: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;
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
public class RequiresIdentityTest extends RequiresIdentityHelper<Integer> // should warn
|
||||
implements RequiresIdentityHelper.RequiresIdentityInt<Integer> { // should warn
|
||||
class Box<T> {}
|
||||
|
||||
RequiresIdentityHelper<Integer> field; // should warn
|
||||
RequiresIdentityHelper<Integer>[] field2; // should warn
|
||||
Box<? extends RequiresIdentityHelper<Integer>> field3; // should warn
|
||||
Box<? super RequiresIdentityHelper<Integer>> field4; // should warn
|
||||
RequiresIdentityHelper<Integer> field5 = new RequiresIdentityHelper<Integer>(); // two warnings here
|
||||
|
||||
public RequiresIdentityTest() {}
|
||||
public RequiresIdentityTest(Integer i) {
|
||||
super(i); // should warn
|
||||
}
|
||||
|
||||
void test(RequiresIdentity2<Object> ri, Integer i) { // warn on the first argument due to its enclosing type: RequiresIdentityHelper<Integer>
|
||||
RequiresIdentityHelper<Integer> localVar; // should warn
|
||||
RequiresIdentityHelper<Integer>[] localVar2; // should warn
|
||||
// there should be warnings for the invocations below
|
||||
ri.foo(i);
|
||||
ri.bar(i, // warn here
|
||||
i); // and here too
|
||||
ri.gg(i);
|
||||
}
|
||||
|
||||
interface I extends RequiresIdentityHelper.RequiresIdentityInt<Integer> {} // should warn
|
||||
|
||||
void m(Object o) {
|
||||
RequiresIdentityHelper<?> ri = (RequiresIdentityHelper<Integer>) o; // should warn
|
||||
}
|
||||
|
||||
RequiresIdentityHelper<Integer> test() { // warn
|
||||
return null;
|
||||
}
|
||||
|
||||
// two warns here one for the type parameter and one for the result type
|
||||
<T extends RequiresIdentityHelper<Integer>> T test2() { return null; }
|
||||
|
||||
class SomeClass<T extends RequiresIdentityHelper<Integer>> {} // warn
|
||||
|
||||
record R(RequiresIdentityHelper<Integer> c) {} // warn
|
||||
record RR(R r) {}
|
||||
|
||||
void m1(RequiresIdentityInt<Integer> ri) { // warn here
|
||||
if (ri instanceof RequiresIdentityInt<Integer> rii) {} // and here
|
||||
}
|
||||
|
||||
void m2(RR rr) {
|
||||
if (rr instanceof RR(R(RequiresIdentityHelper<Integer> rii))) {}
|
||||
}
|
||||
|
||||
<T> void m3() {}
|
||||
void m4() {
|
||||
this.<RequiresIdentityHelper<Integer>>m3();
|
||||
}
|
||||
|
||||
MyIntFunction<Integer> field6 = Integer::new; // two warnings here
|
||||
|
||||
class Run<T> {
|
||||
public <@jdk.internal.RequiresIdentity K> void run() {}
|
||||
}
|
||||
void m5(Runnable r) {}
|
||||
void m6() {
|
||||
m5(new Run<Object>()::<Integer>run);
|
||||
}
|
||||
|
||||
void m7(Integer i, Object o) {
|
||||
RequiresIdentityHelper<Object> var1 = new <Object>RequiresIdentityHelper<Object>(i);
|
||||
RequiresIdentityHelper<Object> var2 = new <Integer>RequiresIdentityHelper<Object>(o);
|
||||
RequiresIdentityHelper<Integer> var3 = new <Object>RequiresIdentityHelper<Integer>(o);
|
||||
}
|
||||
}
|
||||
39
test/langtools/tools/javac/lint/RequiresIdentityTest.out
Normal file
39
test/langtools/tools/javac/lint/RequiresIdentityTest.out
Normal file
@ -0,0 +1,39 @@
|
||||
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: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
|
||||
1 error
|
||||
34 warnings
|
||||
@ -0,0 +1,2 @@
|
||||
- compiler.note.unchecked.filename: RequiresIdentityTest.java
|
||||
- compiler.note.unchecked.recompile
|
||||
Loading…
x
Reference in New Issue
Block a user