From 028309e177314d9f8cc55c13e7f03c07e6a998bf Mon Sep 17 00:00:00 2001 From: Srikanth Adayapalam Date: Tue, 6 Oct 2020 15:49:09 +0530 Subject: [PATCH] 8252183: Old patch brought forwarded, passes all tests --- .../share/classes/java/lang/Identityful.java | 39 ++ .../share/classes/java/lang/ValueBased.java | 38 ++ .../com/sun/tools/javac/code/Flags.java | 6 + .../com/sun/tools/javac/code/Lint.java | 5 + .../com/sun/tools/javac/code/Symbol.java | 7 + .../com/sun/tools/javac/code/Symtab.java | 31 ++ .../com/sun/tools/javac/code/Types.java | 4 + .../com/sun/tools/javac/comp/Attr.java | 5 + .../com/sun/tools/javac/comp/Check.java | 24 +- .../tools/javac/comp/ValueBasisChecker.java | 398 ++++++++++++++++++ .../com/sun/tools/javac/jvm/ClassReader.java | 2 + .../tools/javac/resources/compiler.properties | 39 ++ .../tools/javac/resources/javac.properties | 3 + .../doclets/formats/html/ClassWriterImpl.java | 25 ++ .../doclets/formats/html/Contents.java | 4 + .../html/resources/standard.properties | 4 + .../internal/doclets/toolkit/ClassWriter.java | 7 + .../toolkit/builders/ClassBuilder.java | 10 + .../internal/doclets/toolkit/util/Utils.java | 10 + .../testValueBased/TestValueBasedClass.java | 78 ++++ .../pkg/NotAValueBasedClass.java | 28 ++ .../testValueBased/pkg/ValueBasedClass.java | 30 ++ .../tools/javac/diags/examples.not-yet.txt | 12 + .../lint/ValueBasisChecks/BadValueBased.java | 22 + .../lint/ValueBasisChecks/BadValueBased.out | 5 + .../CheckAccessibleConstructor.java | 22 + .../CheckAccessibleConstructor.out | 6 + .../ValueBasisChecks/CheckEqualityIdiom.java | 33 ++ .../ValueBasisChecks/CheckEqualityIdiom.out | 4 + .../lint/ValueBasisChecks/CheckEquals.java | 21 + .../lint/ValueBasisChecks/CheckEquals.out | 5 + .../lint/ValueBasisChecks/CheckExtends.java | 20 + .../lint/ValueBasisChecks/CheckExtends.out | 4 + .../lint/ValueBasisChecks/CheckFinal.java | 39 ++ .../lint/ValueBasisChecks/CheckFinal.out | 6 + .../CheckHashCodeEqualsToString.java | 10 + .../CheckHashCodeEqualsToString.out | 6 + .../CheckIdentityFreedom.java | 69 +++ .../ValueBasisChecks/CheckIdentityFreedom.out | 13 + .../ValueBasisChecks/CheckIdentityHash.java | 30 ++ .../ValueBasisChecks/CheckIdentityHash.out | 7 + .../ValueBasisChecks/CheckIdentityHash01.java | 36 ++ .../ValueBasisChecks/CheckIdentityHash01.out | 11 + .../ValueBasisChecks/CheckLintDisabling.java | 41 ++ .../ValueBasisChecks/CheckLintDisabling.out | 5 + .../ValueBasisChecks/CheckNullMixing.java | 44 ++ .../lint/ValueBasisChecks/CheckNullMixing.out | 17 + .../lint/ValueBasisChecks/CheckSync.java | 56 +++ .../javac/lint/ValueBasisChecks/CheckSync.out | 18 + .../ValueBasisChecks/CheckSynchronized.java | 29 ++ .../ValueBasisChecks/CheckSynchronized.out | 6 + .../ValueBasisChecks/CheckVarargsCall.java | 33 ++ .../ValueBasisChecks/CheckVarargsCall.out | 11 + .../ValueBasisChecks/ClassFileReaderTest.java | 13 + .../ValueBasisChecks/ClassFileReaderTest.out | 4 + .../javac/lint/ValueBasisChecks/Point.java | 28 ++ 56 files changed, 1474 insertions(+), 9 deletions(-) create mode 100644 src/java.base/share/classes/java/lang/Identityful.java create mode 100644 src/java.base/share/classes/java/lang/ValueBased.java create mode 100644 src/jdk.compiler/share/classes/com/sun/tools/javac/comp/ValueBasisChecker.java create mode 100644 test/langtools/jdk/javadoc/doclet/testValueBased/TestValueBasedClass.java create mode 100644 test/langtools/jdk/javadoc/doclet/testValueBased/pkg/NotAValueBasedClass.java create mode 100644 test/langtools/jdk/javadoc/doclet/testValueBased/pkg/ValueBasedClass.java create mode 100644 test/langtools/tools/javac/lint/ValueBasisChecks/BadValueBased.java create mode 100644 test/langtools/tools/javac/lint/ValueBasisChecks/BadValueBased.out create mode 100644 test/langtools/tools/javac/lint/ValueBasisChecks/CheckAccessibleConstructor.java create mode 100644 test/langtools/tools/javac/lint/ValueBasisChecks/CheckAccessibleConstructor.out create mode 100644 test/langtools/tools/javac/lint/ValueBasisChecks/CheckEqualityIdiom.java create mode 100644 test/langtools/tools/javac/lint/ValueBasisChecks/CheckEqualityIdiom.out create mode 100644 test/langtools/tools/javac/lint/ValueBasisChecks/CheckEquals.java create mode 100644 test/langtools/tools/javac/lint/ValueBasisChecks/CheckEquals.out create mode 100644 test/langtools/tools/javac/lint/ValueBasisChecks/CheckExtends.java create mode 100644 test/langtools/tools/javac/lint/ValueBasisChecks/CheckExtends.out create mode 100644 test/langtools/tools/javac/lint/ValueBasisChecks/CheckFinal.java create mode 100644 test/langtools/tools/javac/lint/ValueBasisChecks/CheckFinal.out create mode 100644 test/langtools/tools/javac/lint/ValueBasisChecks/CheckHashCodeEqualsToString.java create mode 100644 test/langtools/tools/javac/lint/ValueBasisChecks/CheckHashCodeEqualsToString.out create mode 100644 test/langtools/tools/javac/lint/ValueBasisChecks/CheckIdentityFreedom.java create mode 100644 test/langtools/tools/javac/lint/ValueBasisChecks/CheckIdentityFreedom.out create mode 100644 test/langtools/tools/javac/lint/ValueBasisChecks/CheckIdentityHash.java create mode 100644 test/langtools/tools/javac/lint/ValueBasisChecks/CheckIdentityHash.out create mode 100644 test/langtools/tools/javac/lint/ValueBasisChecks/CheckIdentityHash01.java create mode 100644 test/langtools/tools/javac/lint/ValueBasisChecks/CheckIdentityHash01.out create mode 100644 test/langtools/tools/javac/lint/ValueBasisChecks/CheckLintDisabling.java create mode 100644 test/langtools/tools/javac/lint/ValueBasisChecks/CheckLintDisabling.out create mode 100644 test/langtools/tools/javac/lint/ValueBasisChecks/CheckNullMixing.java create mode 100644 test/langtools/tools/javac/lint/ValueBasisChecks/CheckNullMixing.out create mode 100644 test/langtools/tools/javac/lint/ValueBasisChecks/CheckSync.java create mode 100644 test/langtools/tools/javac/lint/ValueBasisChecks/CheckSync.out create mode 100644 test/langtools/tools/javac/lint/ValueBasisChecks/CheckSynchronized.java create mode 100644 test/langtools/tools/javac/lint/ValueBasisChecks/CheckSynchronized.out create mode 100644 test/langtools/tools/javac/lint/ValueBasisChecks/CheckVarargsCall.java create mode 100644 test/langtools/tools/javac/lint/ValueBasisChecks/CheckVarargsCall.out create mode 100644 test/langtools/tools/javac/lint/ValueBasisChecks/ClassFileReaderTest.java create mode 100644 test/langtools/tools/javac/lint/ValueBasisChecks/ClassFileReaderTest.out create mode 100644 test/langtools/tools/javac/lint/ValueBasisChecks/Point.java diff --git a/src/java.base/share/classes/java/lang/Identityful.java b/src/java.base/share/classes/java/lang/Identityful.java new file mode 100644 index 00000000000..8e1f5c25e2b --- /dev/null +++ b/src/java.base/share/classes/java/lang/Identityful.java @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2020, 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 java.lang; + +import java.lang.annotation.*; + +/** + * An informative annotation type used to indicate that the domain of values of the + * type to which it is applied excludes identityless entities such as + * value-based class instances. + */ + +@Documented +@Retention(RetentionPolicy.CLASS) +@Target({ElementType.TYPE_USE, ElementType.TYPE_PARAMETER}) +public @interface Identityful {} diff --git a/src/java.base/share/classes/java/lang/ValueBased.java b/src/java.base/share/classes/java/lang/ValueBased.java new file mode 100644 index 00000000000..b513c8d6df0 --- /dev/null +++ b/src/java.base/share/classes/java/lang/ValueBased.java @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2020, 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 java.lang; + +import java.lang.annotation.*; + +/** + * An informative annotation type used to indicate that a class to which it is applied is + * a value-based class. + */ + +@Documented +@Retention(RetentionPolicy.CLASS) +@Target(ElementType.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 aebbc78da36..192b181bba5 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 @@ -122,6 +122,11 @@ public class Flags { */ public static final int HASINIT = 1<<18; + /** + * Flag that marks a value based class. + */ + public static final long VALUE_BASED = 1L<<19; + /** Flag is set for compiler-generated anonymous method symbols * that `own' an initializer block. */ @@ -470,6 +475,7 @@ public class Flags { ANNOTATION(Flags.ANNOTATION), DEPRECATED(Flags.DEPRECATED), HASINIT(Flags.HASINIT), + VALUEBASED(Flags.VALUE_BASED), BLOCK(Flags.BLOCK), ENUM(Flags.ENUM), MANDATED(Flags.MANDATED), 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 2d61cf5fb0b..4e8c88cc82d 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 @@ -297,6 +297,11 @@ public class Lint */ UNCHECKED("unchecked"), + /** + * Warn about violations of contract of value based classes. + */ + VALUES("values"), + /** * Warn about potentially unsafe vararg methods */ 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 bcce5026a60..fae877cc6a2 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 @@ -458,6 +458,13 @@ public abstract class Symbol extends AnnoConstruct implements PoolConstant, Elem return name == name.table.names.init; } + /** + * Is this a value based class ? + */ + public boolean isValueBased() { + return (flags() & VALUE_BASED) != 0; // triggers completion - OK ?? + } + public boolean isDynamic() { return false; } 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 5a5d78be3c8..e8dc49342d9 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 @@ -220,6 +220,9 @@ public class Symtab { public final Type previewFeatureInternalType; public final Type typeDescriptorType; public final Type recordType; + public final Type valueBasedType; + public final Type identityfulType; + /** The symbol representing the length field of an array. */ @@ -324,6 +327,31 @@ public class Symtab { }; } + public void synthesizeValueBasedTypeIfMissing(final Type type) { + final Completer completer = type.tsym.completer; + type.tsym.completer = new Completer() { + public void complete(Symbol sym) throws CompletionFailure { + try { + completer.complete(sym); + } catch (CompletionFailure e) { + ClassType ctype = (ClassType) type; + sym.flags_field = PUBLIC|ACYCLIC|ANNOTATION|INTERFACE; + sym.erasure_field = ctype; + ((ClassSymbol) sym).members_field = WriteableScope.create(sym); + ctype.typarams_field = List.nil(); + ctype.allparams_field = List.nil(); + ctype.supertype_field = annotationType; + ctype.interfaces_field = List.nil(); + } + } + + @Override + public boolean isTerminal() { + return completer.isTerminal(); + } + }; + } + public void synthesizeBoxTypeIfMissing(final Type type) { ClassSymbol sym = enterClass(java_base, boxedName[type.getTag().ordinal()]); final Completer completer = sym.completer; @@ -584,6 +612,8 @@ public class Symtab { previewFeatureInternalType = enterSyntheticAnnotation("jdk.internal.PreviewFeature+Annotation"); typeDescriptorType = enterClass("java.lang.invoke.TypeDescriptor"); recordType = enterClass("java.lang.Record"); + valueBasedType = enterClass("java.lang.ValueBased"); + identityfulType = enterClass("java.lang.Identityful"); synthesizeEmptyInterfaceIfMissing(autoCloseableType); synthesizeEmptyInterfaceIfMissing(cloneableType); @@ -594,6 +624,7 @@ public class Symtab { synthesizeBoxTypeIfMissing(doubleType); synthesizeBoxTypeIfMissing(floatType); synthesizeBoxTypeIfMissing(voidType); + synthesizeValueBasedTypeIfMissing(valueBasedType); // Enter a synthetic class that is used to mark internal // proprietary classes in ct.sym. This class does not have a 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 aa9b2305412..cf28ed71366 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 @@ -2012,6 +2012,10 @@ public class Types { }; // + public boolean isValueBased(Type t) { + return t != null && t.tsym != null && t.tsym.isValueBased(); + } + // public boolean isArray(Type t) { while (t.hasTag(WILDCARD)) 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 a12c7e81ee5..a99c35cd42e 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 @@ -121,6 +121,7 @@ public class Attr extends JCTree.Visitor { final ArgumentAttr argumentAttr; final MatchBindingsComputer matchBindingsComputer; final AttrRecover attrRecover; + private final ValueBasisChecker valueBasisChecker; public static Attr instance(Context context) { Attr instance = context.get(attrKey); @@ -159,6 +160,7 @@ public class Attr extends JCTree.Visitor { argumentAttr = ArgumentAttr.instance(context); matchBindingsComputer = MatchBindingsComputer.instance(context); attrRecover = AttrRecover.instance(context); + valueBasisChecker = ValueBasisChecker.instance(context); Options options = Options.instance(context); @@ -5005,6 +5007,9 @@ public class Attr extends JCTree.Visitor { try { annotate.flush(); attribClass(c); + final Env env = typeEnvs.get(c); + if (c.owner.kind == PCK && env != null && env.info.lint != null && env.info.lint.isEnabled(LintCategory.VALUES)) + valueBasisChecker.scan(env.tree); } catch (CompletionFailure ex) { chk.completionError(pos, ex); } 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 f1725857891..328327d73ec 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 @@ -2920,7 +2920,7 @@ public class Check { boolean appliesToRecords = false; for (Name target : targets) { appliesToRecords = - target == names.FIELD || + target == names.FIELD || target == names.PARAMETER || target == names.METHOD || target == names.TYPE_USE || @@ -2935,7 +2935,7 @@ public class Check { /* lets now find the annotations in the field that are targeted to record components and append them to * the corresponding record component */ - ClassSymbol recordClass = (ClassSymbol) s.owner; + ClassSymbol recordClass = (ClassSymbol)s.owner; RecordComponent rc = recordClass.getRecordComponent((VarSymbol)s); SymbolMetadata metadata = rc.getMetadata(); if (metadata == null || metadata.isEmpty()) { @@ -3009,14 +3009,14 @@ public class Check { if (applicableTargets.isEmpty() || isRecordMemberWithNonApplicableDeclAnno) { if (isRecordMemberWithNonApplicableDeclAnno) { - /* so we have found an annotation that is not applicable to a record member that was generated by the - * compiler. This was intentionally done at TypeEnter, now is the moment strip away the annotations - * that are not applicable to the given record member - */ + /* so we have found an annotation that is not applicable to a record member that was generated by the + * compiler. This was intentionally done at TypeEnter, now is the moment strip away the annotations + * that are not applicable to the given record member + */ JCModifiers modifiers = TreeInfo.getModifiers(declarationTree); - /* lets first remove the annotation from the modifier if it is not applicable, we have to check again as - * it could be a type annotation - */ + /* lets first remove the annotation from the modifier if it is not applicable, we have to check again as + * it could be a type annotation + */ if (modifiers != null && applicableTargets.isEmpty()) { ListBuffer newAnnotations = new ListBuffer<>(); for (JCAnnotation anno : modifiers.annotations) { @@ -3041,6 +3041,12 @@ public class Check { } else if (!s.isInterface() || (s.flags() & ANNOTATION) != 0) { log.error(a.pos(), Errors.BadFunctionalIntfAnno1(Fragments.NotAFunctionalIntf(s))); } + } else if (a.annotationType.type.tsym == syms.valueBasedType.tsym) { + if (s.isInterface() || s.isEnum()) { + log.error(a.pos(), Errors.BadValueBasedAnno); + } else { + s.flags_field |= Flags.VALUE_BASED; + } } } diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/ValueBasisChecker.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/ValueBasisChecker.java new file mode 100644 index 00000000000..a685bfc92bd --- /dev/null +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/ValueBasisChecker.java @@ -0,0 +1,398 @@ +/* + * Copyright (c) 2020, 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 com.sun.tools.javac.comp; + +import com.sun.tools.javac.code.*; +import static com.sun.tools.javac.code.Flags.BAD_OVERRIDE; +import static com.sun.tools.javac.code.Flags.SIGNATURE_POLYMORPHIC; +import static com.sun.tools.javac.code.Flags.STATIC; +import static com.sun.tools.javac.code.Kinds.Kind.*; + +import com.sun.tools.javac.code.Attribute.TypeCompound; +import com.sun.tools.javac.code.Lint.LintCategory; +import com.sun.tools.javac.code.Symbol.MethodSymbol; +import com.sun.tools.javac.code.Type.ArrayType; +import com.sun.tools.javac.resources.CompilerProperties.Warnings; +import com.sun.tools.javac.tree.*; +import com.sun.tools.javac.tree.JCTree.*; +import com.sun.tools.javac.util.*; + +/** + * Lint mode for Value Based classes + * + * Process classes annotated with @ValueBased annotation, looking for contract violations. + * + * This is implemented as a separate pipeline stage so as to isolate and centralize the + * relevent checks in one place rather than have them be scattered all over the place + * + * We get here only if Xlint:values is opted for (default off) + * + *

This is NOT part of any supported API. + * If you write code that depends on this, you do so at your own risk. + * This code and its internal interfaces are subject to change or + * deletion without notice. + */ + +public class ValueBasisChecker extends TreeScanner { + + protected static final Context.Key valueBasisChecker = new Context.Key<>(); + + private final Log log; + private final Names names; + private final Symtab syms; + private final Types types; + private final Lint lint; + private final boolean complainOnValueNullMix; + + private JCMethodDecl currentMethod; + private boolean inValueClass; + + public static ValueBasisChecker instance(Context context) { + ValueBasisChecker instance = context.get(valueBasisChecker); + if (instance == null) + instance = new ValueBasisChecker(context); + return instance; + } + + protected ValueBasisChecker(Context context) { + context.put(valueBasisChecker, this); + log = Log.instance(context); + names = Names.instance(context); + syms = Symtab.instance(context); + types = Types.instance(context); + lint = Lint.instance(context); + complainOnValueNullMix = Options.instance(context).isSet("ComplainOnValueNullMix"); + } + + public void visitClassDef(JCClassDecl tree) { + + if (tree.sym == null || lint.augment(tree.sym).isSuppressed(LintCategory.VALUES)) + return; + + boolean oldInValue = inValueClass; + + try { + + inValueClass = tree.sym.isValueBased(); + + if (inValueClass) { + + // Value based classes are final and immutable - immutablity is asserted in visitVarDef + if ((tree.mods.flags & Flags.FINAL) == 0) { + log.warning(LintCategory.VALUES, tree.pos(), Warnings.ValueBasedClassMustBeFinal); + } + + /* ValueBased.html does not actually spell out that a VBC may not expressly extend a class. + But the migration path will lead there. So warn if a VBC gets "referency" by mentioning + a superclass. + */ + if (tree.extending != null) { + log.warning(LintCategory.VALUES, tree.extending.pos(), Warnings.ValueBasedClassMayNotExtend); + } + + /* A VBC must provide implementations of hashCode, equals and toString, cannot inherit these from + jlO as those methods use identity operations. + */ + Name [] mustDefines = { names.equals, names.hashCode, names.toString }; + for (Name mustDefine: mustDefines) { + MethodSymbol method = (MethodSymbol) syms.objectType.tsym.members().findFirst(mustDefine); + if (types.implementation(method, + tree.sym, false, s -> MethodSymbol.implementation_filter.accepts(s) && + (s.flags() & BAD_OVERRIDE) == 0).owner != tree.sym) { + log.warning(LintCategory.VALUES, tree.pos(), Warnings.ValueBasedClassMustDefineMethod(mustDefine)); + } + } + } + + super.visitClassDef(tree); + + } finally { + inValueClass = oldInValue; + } + } + + public void visitMethodDef(JCMethodDecl tree) { + + if (tree.sym == null || lint.augment(tree.sym).isSuppressed(LintCategory.VALUES)) + return; + + JCMethodDecl previousMethod = currentMethod; + + try { + + currentMethod = tree; + + if (tree.sym.owner.isValueBased()) { + + /* VBCs do not define accessible constructors, but are instead instantiated through + factory methods which make no committment as to the identity of returned instances; + */ + if (tree.sym.isConstructor() && (tree.sym.flags() & Flags.PRIVATE) == 0) { + log.warning(LintCategory.VALUES, tree.pos(), Warnings.ValueBasedClassWithAccessibleConstructor); + } + + /* VBCs may not define instance methods that are synchronized */ + if ((tree.sym.flags() & (Flags.SYNCHRONIZED | Flags.STATIC)) == Flags.SYNCHRONIZED) { + log.warning(LintCategory.VALUES, tree.pos(), Warnings.AttemptToSynchronizeOnInstanceOfValueBasedClass); + } + } + + super.visitMethodDef(tree); + + } finally { + currentMethod = previousMethod; + } + } + + + // Common utility routine to check domain compatibility between lhs and rhs in assignment-like situations. + private void checkForDomainCompatibility(Type lhsType, List rhsExpressions) { + if (complainOnValueNullMix) { + // Check for null pollution via assignment, initialization, return expressions + if (types.isValueBased(lhsType)) { + for (JCExpression rhs : rhsExpressions) { + if (rhs != null && rhs.type != null && rhs.type.hasTag(TypeTag.BOT)) { + log.warning(LintCategory.VALUES, rhs.pos(), Warnings.NullMixedWithValueBasedClass); + } + } + } + } + + for (TypeCompound annotationMirror : lhsType.getAnnotationMirrors()) { + if (annotationMirror.type != null && annotationMirror.type.tsym == syms.identityfulType.tsym) { + for (JCExpression rhs : rhsExpressions) { + if (rhs != null && types.isValueBased(rhs.type)) { + log.warning(LintCategory.VALUES, rhs.pos(), Warnings.NoIdentityForValueBasedClassInstance); + } + } + } + } + + } + + public void visitVarDef(JCVariableDecl tree) { + + if (tree.sym == null || lint.augment(tree.sym).isSuppressed(LintCategory.VALUES)) + return; + + // All instance fields of a VBC must be immutable. + if (tree.sym.owner.kind == TYP && tree.sym.owner.isValueBased()) { + if ((tree.mods.flags & (Flags.FINAL | Flags.STATIC)) == 0) { + log.warning(LintCategory.VALUES, tree.pos(), Warnings.FieldOfValueBasedClassMustBeFinal); + } + } + + checkForDomainCompatibility(tree.vartype.type, List.of(tree.init)); + super.visitVarDef(tree); + } + + @Override + public void visitNewArray(JCNewArray tree) { + // Don't allow nulls to sneak through from list of initial values. + if (tree.type != null) + checkForDomainCompatibility(((ArrayType) tree.type).elemtype, tree.elems); + super.visitNewArray(tree); + } + + @Override + public void visitAssign(JCAssign tree) { + checkForDomainCompatibility(tree.lhs.type, List.of(tree.rhs)); + super.visitAssign(tree); + } + + @Override + public void visitReturn(JCReturn tree) { + if (currentMethod != null && currentMethod.restype != null) { + checkForDomainCompatibility(currentMethod.restype.type, List.of(tree.expr)); + } + super.visitReturn(tree); + } + + @Override + public void visitTypeCast(JCTypeCast tree) { + /* A type cast is really an assignment to a nameless temporary instance of the target type + that lives on TOS. + */ + checkForDomainCompatibility(tree.clazz.type, List.of(tree.expr)); + + final Type exprType = tree.expr.type; + if (exprType != null && types.isValueBased(tree.clazz.type)) { + if (exprType.isReference() && !types.isValueBased(exprType)) { + if (complainOnValueNullMix) + log.warning(LintCategory.VALUES, tree.expr.pos(), Warnings.NullableCastedToValueBasedClass); + } + } + super.visitTypeCast(tree); + } + + public void visitBinary(JCBinary tree) { + + /* Mute complaints on legacy idiom for equality: i.e we don't want to warn on + if (vbci1 == vbci2 || vbci1.equals(vbci2)) + */ + if (tree.getTag() == Tag.OR && tree.lhs.getTag() == Tag.EQ && tree.rhs.getTag() == Tag.APPLY) { + JCMethodInvocation method = (JCMethodInvocation) tree.rhs; + boolean isEqualsCall = + method.type != null + && !method.type.isErroneous() // kosher call. + && TreeInfo.name(method.meth).equals(names.equals) + && (TreeInfo.symbol(method.meth).flags() & STATIC) == 0 + && method.meth.type.getParameterTypes().size() == 1 + && method.meth.type.getParameterTypes().head.tsym == syms.objectType.tsym; + + if (isEqualsCall) { + + JCBinary eqOp = (JCBinary) tree.lhs; + + Symbol lhsSymbol = TreeInfo.symbol(eqOp.lhs); + Symbol rhsSymbol = TreeInfo.symbol(eqOp.rhs); + + Symbol argSymbol = TreeInfo.symbol(method.args.head); + + boolean equalityIdiom = false; + if (lhsSymbol != null && types.isValueBased(lhsSymbol.type) && + rhsSymbol != null && types.isValueBased(rhsSymbol.type)) { + switch (method.meth.getTag()) { + case IDENT: + if ((lhsSymbol.name == names._this && rhsSymbol == argSymbol) || + (rhsSymbol.name == names._this && lhsSymbol == argSymbol)) + equalityIdiom = true; + break; + case SELECT: + Symbol recvSymbol = TreeInfo.symbol(((JCFieldAccess) method.meth).selected); + if ((recvSymbol == lhsSymbol && rhsSymbol == argSymbol) || + (recvSymbol == rhsSymbol && lhsSymbol == argSymbol)) + equalityIdiom = true; + break; + } + } + if (equalityIdiom) { + super.visitApply(method); + return; // do not descend into the == subtree. + } + } + } + + Type lType = tree.lhs.type; + Type rType = tree.rhs.type; + Symbol operator = tree.operator; + + if (operator != null && operator.kind == MTH && + lType != null && !lType.isErroneous() && + rType != null && !rType.isErroneous()) { + if (operator.name.contentEquals("==") || operator.name.contentEquals("!=")) { + if (types.isValueBased(lType) || types.isValueBased(rType)) + log.warning(LintCategory.VALUES, tree.pos(), Warnings.ValueBasedClassInstancesShouldNotBeComparedWith(tree.operator.name)); + } + } + super.visitBinary(tree); + } + + @Override + public void visitSynchronized(JCSynchronized tree) { + if (types.isValueBased(tree.lock.type)) { + log.warning(LintCategory.VALUES, tree.pos(), Warnings.AttemptToSynchronizeOnInstanceOfValueBasedClass); + } + super.visitSynchronized(tree); + } + + public void visitApply(JCMethodInvocation tree) { + final Symbol method = TreeInfo.symbolFor(tree); + if (method != null && method.kind != ERR) { + if (method.name.contentEquals("identityHashCode") && method.owner.type == syms.systemType) { + if ((tree.args.length() == 1) && types.isValueBased(tree.args.head.type)) { + log.warning(LintCategory.VALUES, tree.pos(), Warnings.NoIdentityForValueBasedClassInstance); + } + } + + if (method.name != names.init && method.owner.type == syms.objectType) { + boolean receiverIsValue = false; + switch (tree.meth.getTag()) { + case IDENT: + receiverIsValue = inValueClass; + break; + case SELECT: + final Symbol symbol = TreeInfo.symbol(((JCFieldAccess)tree.meth).selected); + receiverIsValue = symbol != null && + (symbol.name == names._super ? inValueClass : types.isValueBased(symbol.type)); + break; + } + if (receiverIsValue) { + switch(method.name.toString()) { + case "wait": + case "notify": + case "notifyAll": + log.warning(LintCategory.VALUES, tree.pos(), Warnings.AttemptToSynchronizeOnInstanceOfValueBasedClass); + break; + case "hashCode": + case "equals": + case "toString": + log.warning(LintCategory.VALUES, tree.pos(), Warnings.MethodShouldNotBeInvokedOnInstanceOfValueBasedClass(method.name)); + break; + } + } + } + + validateArguments(method, tree.args, + tree.varargsElement != null || (method.flags() & SIGNATURE_POLYMORPHIC) != 0); + } + super.visitApply(tree); + } + + private void validateArguments(Symbol method, List arguments, boolean varArgsCall) { + + final List parameterTypes = method.type.getParameterTypes(); + final int paramCount = parameterTypes.size(); + final int argCount = arguments.size(); + + for (int i = 0; i < argCount; i++) { + final JCExpression arg = arguments.get(i); + final Type formalType = (!varArgsCall || i < paramCount - 1) ? + parameterTypes.get(i) : + types.elemtype(parameterTypes.get(paramCount - 1)); + checkForDomainCompatibility(formalType, List.of(arg)); + } + } + + @Override + public void visitNewClass(JCNewClass tree) { + + final Symbol ctor = tree.constructor; + if (ctor != null && ctor.kind != ERR) { + validateArguments(ctor, tree.args, tree.varargsElement != null); + } + + super.visitNewClass(tree); + } + + @Override + public void visitTypeApply(JCTypeApply tree) { + if (tree.type != null && !tree.type.isErroneous()) { + // type annotations don't seem to appear on type variables as of now. + } + super.visitTypeApply(tree); + } +} \ No newline at end of file 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 a0085a3e6e1..d6649074cbc 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 @@ -1437,6 +1437,8 @@ public class ClassReader { target = proxy; } else if (proxy.type.tsym == syms.repeatableType.tsym) { repeatable = proxy; + } else if (sym.kind == TYP && proxy.type.tsym == syms.valueBasedType.tsym) { + sym.flags_field |= VALUE_BASED; } else if (proxy.type.tsym == syms.deprecatedType.tsym) { sym.flags_field |= (DEPRECATED | DEPRECATED_ANNOTATION); setFlagIfAttributeTrue(proxy, sym, names.forRemoval, DEPRECATED_REMOVAL); 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 01edf5eb90a..493300e3f2d 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 @@ -3742,3 +3742,42 @@ compiler.err.preview.not.latest=\ compiler.err.preview.without.source.or.release=\ --enable-preview must be used with either -source or --release + +compiler.warn.value.based.class.must.be.final=\ + value based class must be declared final + +compiler.warn.value.based.class.may.not.extend=\ + value based class may not explicitly extend another class + +# 0: name (of method) +compiler.warn.value.based.class.must.define.method=\ + value based class must define an implementation of {0} + +compiler.warn.field.of.value.based.class.must.be.final=\ + instance fields of value based classes must be declared final + +compiler.warn.attempt.to.synchronize.on.instance.of.value.based.class=\ + attempt to synchronize on an instance of a value based class + +# 0: name (of method) +compiler.warn.method.should.not.be.invoked.on.instance.of.value.based.class=\ + the method {0} of java.lang.Object class cannot be invoked on a value based class instance + +# 0: name (of operator) +compiler.warn.value.based.class.instances.should.not.be.compared.with=\ + value based class instances should not be compared with {0} + +compiler.warn.value.based.class.with.accessible.constructor=\ + value based class should not provide an accessible constructor (implicit or explicit) + +compiler.warn.no.identity.for.value.based.class.instance=\ + instances of a value based class do not have identity > + +compiler.warn.null.mixed.with.value.based.class=\ + suspicious mixing of ''null'' with an instance of a value based class + +compiler.warn.nullable.casted.to.value.based.class=\ + suspicious cast of a nullable instance to a value based class + +compiler.err.bad.value.based.anno=\ + Unexpected @ValueBased annotation 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 bbca9cae250..558ead407ca 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 @@ -261,6 +261,9 @@ javac.opt.Xlint.desc.varargs=\ javac.opt.Xlint.desc.preview=\ Warn about use of preview language features +javac.opt.Xlint.desc.values=\ + Warn about violations of contract for value based classes. + javac.opt.Xdoclint=\ Enable recommended checks for problems in javadoc comments # L10N: do not localize: all none diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/ClassWriterImpl.java b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/ClassWriterImpl.java index cb8acb741ff..d6ea2f438cd 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/ClassWriterImpl.java +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/ClassWriterImpl.java @@ -523,6 +523,31 @@ public class ClassWriterImpl extends SubWriterHolderWriter implements ClassWrite } } + /** + * {@inheritDoc} + */ + @Override + public void addValueBasedClassInfo (Content classInfoTree) { + if (isValueBasedClass()) { + HtmlTree dl = HtmlTree.DL(HtmlStyle.notes); + dl.add(HtmlTree.DT(contents.valueBasedClass)); + Content dd = new HtmlTree(TagName.DD); + dd.add(contents.valueBasedClassMessage); + dl.add(dd); + classInfoTree.add(dl); + } + } + + public boolean isValueBasedClass() { + List annotationMirrors = ((Element) typeElement).getAnnotationMirrors(); + for (AnnotationMirror anno : annotationMirrors) { + if (utils.isValueBasedClass(anno)) { + return true; + } + } + return false; + } + /** * Get links to the given classes. * diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/Contents.java b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/Contents.java index fbcae00e948..74001b7c947 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/Contents.java +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/Contents.java @@ -107,6 +107,8 @@ public class Contents { public final Content fromLabel; public final Content functionalInterface; public final Content functionalInterfaceMessage; + public final Content valueBasedClass; + public final Content valueBasedClassMessage; public final Content helpLabel; public final Content hierarchyForAllPackages; public final Content implementation; @@ -243,6 +245,8 @@ public class Contents { fromLabel = getContent("doclet.From"); functionalInterface = getContent("doclet.Functional_Interface"); functionalInterfaceMessage = getContent("doclet.Functional_Interface_Message"); + valueBasedClass = getContent("doclet.Value_Based_Class"); + valueBasedClassMessage = getContent("doclet.Value_Based_Class_Message"); helpLabel = getContent("doclet.Help"); hierarchyForAllPackages = getContent("doclet.Hierarchy_For_All_Packages"); implementation = getContent("doclet.Implementation"); diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/standard.properties b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/standard.properties index a3356bdb1fa..08564f039f6 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/standard.properties +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/standard.properties @@ -107,6 +107,10 @@ doclet.Functional_Interface=Functional Interface: doclet.Functional_Interface_Message=\ This is a functional interface and can therefore be used as the assignment target for a lambda \ expression or method reference. +doclet.Value_Based_Class=Value Based Class: +doclet.Value_Based_Class_Message=\ + This is a value based class. Instances of this class are immutable, identityless values that are \ + considered equal solely based on equals() and are freely substitutable when equal. doclet.also=also doclet.Frames=Frames doclet.No_Frames=No Frames diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/ClassWriter.java b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/ClassWriter.java index 413023492c9..b76c497aa8b 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/ClassWriter.java +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/ClassWriter.java @@ -119,6 +119,13 @@ public interface ClassWriter { */ void addFunctionalInterfaceInfo(Content classInfoTree); + /** + * If this is a value based class, display appropriate message. + * + * @param classInfoTree content tree to which the documentation will be added + */ + void addValueBasedClassInfo(Content classInfoTree); + /** * If this is an inner class or interface, add the enclosing class or * interface. diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/builders/ClassBuilder.java b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/builders/ClassBuilder.java index 3bdd623f9da..234e717927e 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/builders/ClassBuilder.java +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/builders/ClassBuilder.java @@ -179,6 +179,7 @@ public class ClassBuilder extends AbstractBuilder { buildInterfaceUsageInfo(classInfoTree); buildNestedClassInfo(classInfoTree); buildFunctionalInterfaceInfo(classInfoTree); + buildValueBasedClassInfo(classInfoTree); buildClassSignature(classInfoTree); buildDeprecationInfo(classInfoTree); buildClassDescription(classInfoTree); @@ -250,6 +251,15 @@ public class ClassBuilder extends AbstractBuilder { writer.addFunctionalInterfaceInfo(classInfoTree); } + /** + * If this is a value based class, display appropriate message. + * + * @param classInfoTree the content tree to which the documentation will be added + */ + protected void buildValueBasedClassInfo(Content classInfoTree) { + writer.addValueBasedClassInfo(classInfoTree); + } + /** * If this class is deprecated, build the appropriate information. * diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/util/Utils.java b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/util/Utils.java index 2c5b0177130..f194f373f0a 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/util/Utils.java +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/util/Utils.java @@ -203,6 +203,10 @@ public class Utils { return getSymbol("java.lang.FunctionalInterface"); } + public TypeMirror getValueBasedClass() { + return getSymbol("java.lang.ValueBased"); + } + /** * Return array of class members whose documentation is to be generated. * If the member is deprecated do not include such a member in the @@ -584,6 +588,12 @@ public class Utils { .compareTo(SourceVersion.RELEASE_8) >= 0; } + public boolean isValueBasedClass(AnnotationMirror amirror) { + return amirror.getAnnotationType().equals(getValueBasedClass()) && + configuration.docEnv.getSourceVersion() + .compareTo(SourceVersion.RELEASE_11) >= 0; + } + public boolean isNoType(TypeMirror t) { return t.getKind() == NONE; } diff --git a/test/langtools/jdk/javadoc/doclet/testValueBased/TestValueBasedClass.java b/test/langtools/jdk/javadoc/doclet/testValueBased/TestValueBasedClass.java new file mode 100644 index 00000000000..428d65d1c7f --- /dev/null +++ b/test/langtools/jdk/javadoc/doclet/testValueBased/TestValueBasedClass.java @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2020, 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 + * @summary Make sure that value based classes are recognized by javadoc. + * @library /tools/lib ../../lib + * @modules jdk.javadoc/jdk.javadoc.internal.tool + * @build toolbox.ToolBox javadoc.tester.* + * @run main TestValueBasedClass + */ + +import javadoc.tester.JavadocTester; + +public class TestValueBasedClass extends JavadocTester { + + public static void main(String... args) throws Exception { + TestValueBasedClass tester = new TestValueBasedClass(); + tester.runTests(); + } + + @Test + public void testDefault() { + javadoc("-d", "out-default", + "-sourcepath", testSrc, + "pkg"); + checkExit(Exit.OK); + + checkOutput("pkg/ValueBasedClass.html", true, + "

\n" + + "
Value Based Class:
\n" + + "
This is a value based class. Instances of this class are immutable, " + + "identityless values that are considered equal solely based on equals() " + + "and are freely substitutable when equal.
\n" + + "
"); + + checkOutput("pkg/NotAValueBasedClass.html", false, + "
\n" + + "
Value Based Class:
\n" + + "
This is a value based class. Instances of this class are immutable, " + + "identityless values that are considered equal solely based on equals() " + + "and are freely substitutable when equal.
\n" + + "
"); + } + + @Test + public void testSource10() { + javadoc("-d", "out-10", + "-sourcepath", testSrc, + "-source", "1.10", + "pkg"); + checkExit(Exit.OK); + + checkOutput("pkg/ValueBasedClass.html", false, + "
\n" + + "
Value Based Class:
"); + } +} diff --git a/test/langtools/jdk/javadoc/doclet/testValueBased/pkg/NotAValueBasedClass.java b/test/langtools/jdk/javadoc/doclet/testValueBased/pkg/NotAValueBasedClass.java new file mode 100644 index 00000000000..22c07e5c21c --- /dev/null +++ b/test/langtools/jdk/javadoc/doclet/testValueBased/pkg/NotAValueBasedClass.java @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2020, 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. + */ + +package pkg; + +public class NotAValueBasedClass { + +} diff --git a/test/langtools/jdk/javadoc/doclet/testValueBased/pkg/ValueBasedClass.java b/test/langtools/jdk/javadoc/doclet/testValueBased/pkg/ValueBasedClass.java new file mode 100644 index 00000000000..9ceec2f2f3e --- /dev/null +++ b/test/langtools/jdk/javadoc/doclet/testValueBased/pkg/ValueBasedClass.java @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2020, 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. + */ + +package pkg; + +@ValueBased +public class ValueBasedClass { + +} + diff --git a/test/langtools/tools/javac/diags/examples.not-yet.txt b/test/langtools/tools/javac/diags/examples.not-yet.txt index 78b4eafb7e9..aca28ae9d76 100644 --- a/test/langtools/tools/javac/diags/examples.not-yet.txt +++ b/test/langtools/tools/javac/diags/examples.not-yet.txt @@ -203,3 +203,15 @@ compiler.warn.source.target.conflict compiler.warn.target.default.source.conflict compiler.err.preview.not.latest compiler.err.preview.without.source.or.release +compiler.warn.value.based.class.must.be.final +compiler.warn.value.based.class.may.not.extend +compiler.warn.value.based.class.must.define.method +compiler.warn.field.of.value.based.class.must.be.final +compiler.warn.attempt.to.synchronize.on.instance.of.value.based.class +compiler.warn.method.should.not.be.invoked.on.instance.of.value.based.class +compiler.warn.value.based.class.instances.should.not.be.compared.with +compiler.warn.value.based.class.with.accessible.constructor +compiler.warn.no.identity.for.value.based.class.instance +compiler.warn.null.mixed.with.value.based.class +compiler.warn.nullable.casted.to.value.based.class +compiler.err.bad.value.based.anno diff --git a/test/langtools/tools/javac/lint/ValueBasisChecks/BadValueBased.java b/test/langtools/tools/javac/lint/ValueBasisChecks/BadValueBased.java new file mode 100644 index 00000000000..7759f9ce1fd --- /dev/null +++ b/test/langtools/tools/javac/lint/ValueBasisChecks/BadValueBased.java @@ -0,0 +1,22 @@ +/* + * @test /nodynamiccopyright/ + * @summary Check that improper application of "@ValueBased" annotation are caught + * @compile/fail/ref=BadValueBased.out -XDrawDiagnostics -XDdev BadValueBased.java + */ +@SuppressWarnings("values") +public class BadValueBased { + @ValueBased + interface X {} + + @ValueBased + @interface A {} + + @ValueBased + enum E {} + + @ValueBased + class Y { + @ValueBased int x; + } +} + diff --git a/test/langtools/tools/javac/lint/ValueBasisChecks/BadValueBased.out b/test/langtools/tools/javac/lint/ValueBasisChecks/BadValueBased.out new file mode 100644 index 00000000000..1fb44c8ff77 --- /dev/null +++ b/test/langtools/tools/javac/lint/ValueBasisChecks/BadValueBased.out @@ -0,0 +1,5 @@ +BadValueBased.java:8:5: compiler.err.bad.value.based.anno +BadValueBased.java:11:5: compiler.err.bad.value.based.anno +BadValueBased.java:14:5: compiler.err.bad.value.based.anno +BadValueBased.java:19:9: compiler.err.annotation.type.not.applicable +4 errors diff --git a/test/langtools/tools/javac/lint/ValueBasisChecks/CheckAccessibleConstructor.java b/test/langtools/tools/javac/lint/ValueBasisChecks/CheckAccessibleConstructor.java new file mode 100644 index 00000000000..096b2d663e6 --- /dev/null +++ b/test/langtools/tools/javac/lint/ValueBasisChecks/CheckAccessibleConstructor.java @@ -0,0 +1,22 @@ +/* + * @test /nodynamiccopyright/ + * @summary value based classes should not define accessible constructors. + * @compile/fail/ref=CheckAccessibleConstructor.out -XDrawDiagnostics -Werror -Xlint:values CheckAccessibleConstructor.java + */ + +@java.lang.ValueBased +final class CheckAccessibleConstructor { + CheckAccessibleConstructor() {} // Error + public CheckAccessibleConstructor(int x) {} // Error + protected CheckAccessibleConstructor(int x, int y) {} // Error + private CheckAccessibleConstructor(int x, int y, int z) {} // OK. + public int hashCode() { + return 0; + } + public boolean equals(Object o) { + return true; + } + public String toString() { + return "blah"; + } +} diff --git a/test/langtools/tools/javac/lint/ValueBasisChecks/CheckAccessibleConstructor.out b/test/langtools/tools/javac/lint/ValueBasisChecks/CheckAccessibleConstructor.out new file mode 100644 index 00000000000..7b0f873412a --- /dev/null +++ b/test/langtools/tools/javac/lint/ValueBasisChecks/CheckAccessibleConstructor.out @@ -0,0 +1,6 @@ +CheckAccessibleConstructor.java:9:5: compiler.warn.value.based.class.with.accessible.constructor +CheckAccessibleConstructor.java:10:12: compiler.warn.value.based.class.with.accessible.constructor +CheckAccessibleConstructor.java:11:15: compiler.warn.value.based.class.with.accessible.constructor +- compiler.err.warnings.and.werror +1 error +3 warnings diff --git a/test/langtools/tools/javac/lint/ValueBasisChecks/CheckEqualityIdiom.java b/test/langtools/tools/javac/lint/ValueBasisChecks/CheckEqualityIdiom.java new file mode 100644 index 00000000000..c850a705d83 --- /dev/null +++ b/test/langtools/tools/javac/lint/ValueBasisChecks/CheckEqualityIdiom.java @@ -0,0 +1,33 @@ +/* + * @test /nodynamiccopyright/ + * @summary Ensure that the compiler recognizes equality idioms and does not complain about them. + * @compile/fail/ref=CheckEqualityIdiom.out -XDrawDiagnostics -Werror -Xlint:values CheckEqualityIdiom.java + */ + +@java.lang.ValueBased +final class CheckEqualityIdiom { + + private CheckEqualityIdiom() {} + + void foo(CheckEqualityIdiom vbci1, CheckEqualityIdiom vbci2) { + + // The snippets below should not trigger a warning + if (this == vbci1 || equals(vbci1)) {} + if (this == vbci1 || this.equals(vbci1)) {} + if (this == vbci1 || vbci1.equals(this)) {} + if (vbci1 == this || vbci1.equals(this)) {} + if (vbci1 == this || equals(vbci1)) {} + if (vbci1 == this || this.equals(vbci1)) {} + if (vbci1 == vbci2 || vbci1.equals(vbci2)) {} + if (vbci1 == vbci2 || vbci2.equals(vbci1)) {} + + // The snippet below is not an equality idiom. + if (this == vbci1 || vbci1.equals(vbci2)) { + } + + } + + public int hashCode() { return 0; } + public String toString() { return ""; } + public boolean equals(Object o) { return true; } +} diff --git a/test/langtools/tools/javac/lint/ValueBasisChecks/CheckEqualityIdiom.out b/test/langtools/tools/javac/lint/ValueBasisChecks/CheckEqualityIdiom.out new file mode 100644 index 00000000000..d139bcaf5d2 --- /dev/null +++ b/test/langtools/tools/javac/lint/ValueBasisChecks/CheckEqualityIdiom.out @@ -0,0 +1,4 @@ +CheckEqualityIdiom.java:25:18: compiler.warn.value.based.class.instances.should.not.be.compared.with: == +- compiler.err.warnings.and.werror +1 error +1 warning diff --git a/test/langtools/tools/javac/lint/ValueBasisChecks/CheckEquals.java b/test/langtools/tools/javac/lint/ValueBasisChecks/CheckEquals.java new file mode 100644 index 00000000000..4cfc34d76f6 --- /dev/null +++ b/test/langtools/tools/javac/lint/ValueBasisChecks/CheckEquals.java @@ -0,0 +1,21 @@ +/* + * @test /nodynamiccopyright/ + * @summary Value based class instances should not be compared with == or != + * @compile/fail/ref=CheckEquals.out -XDrawDiagnostics -Werror -Xlint:values CheckEquals.java + */ +@java.lang.ValueBased +final class CheckEquals { + boolean foo(CheckEquals a, CheckEquals b) { + return (a == b) || (a != b); + } + private CheckEquals() {} + public int hashCode() { + return 0; + } + public boolean equals(Object o) { + return true; + } + public String toString() { + return "blah"; + } +} diff --git a/test/langtools/tools/javac/lint/ValueBasisChecks/CheckEquals.out b/test/langtools/tools/javac/lint/ValueBasisChecks/CheckEquals.out new file mode 100644 index 00000000000..29cd68f2fa0 --- /dev/null +++ b/test/langtools/tools/javac/lint/ValueBasisChecks/CheckEquals.out @@ -0,0 +1,5 @@ +CheckEquals.java:9:19: compiler.warn.value.based.class.instances.should.not.be.compared.with: == +CheckEquals.java:9:31: compiler.warn.value.based.class.instances.should.not.be.compared.with: != +- compiler.err.warnings.and.werror +1 error +2 warnings diff --git a/test/langtools/tools/javac/lint/ValueBasisChecks/CheckExtends.java b/test/langtools/tools/javac/lint/ValueBasisChecks/CheckExtends.java new file mode 100644 index 00000000000..ee0930b280c --- /dev/null +++ b/test/langtools/tools/javac/lint/ValueBasisChecks/CheckExtends.java @@ -0,0 +1,20 @@ +/* + * @test /nodynamiccopyright/ + * @summary A Value based class may not explicitly extend a class + * @compile/fail/ref=CheckExtends.out -XDrawDiagnostics -Werror -Xlint:values CheckExtends.java + */ + +@java.lang.ValueBased +final class CheckExtends extends Object { + public int hashCode() { + return 0; + } + public boolean equals(Object o) { + return true; + } + public String toString() { + return "blah"; + } + private CheckExtends() {} +} + diff --git a/test/langtools/tools/javac/lint/ValueBasisChecks/CheckExtends.out b/test/langtools/tools/javac/lint/ValueBasisChecks/CheckExtends.out new file mode 100644 index 00000000000..3e291c15cde --- /dev/null +++ b/test/langtools/tools/javac/lint/ValueBasisChecks/CheckExtends.out @@ -0,0 +1,4 @@ +CheckExtends.java:8:34: compiler.warn.value.based.class.may.not.extend +- compiler.err.warnings.and.werror +1 error +1 warning diff --git a/test/langtools/tools/javac/lint/ValueBasisChecks/CheckFinal.java b/test/langtools/tools/javac/lint/ValueBasisChecks/CheckFinal.java new file mode 100644 index 00000000000..29aa7bdf53f --- /dev/null +++ b/test/langtools/tools/javac/lint/ValueBasisChecks/CheckFinal.java @@ -0,0 +1,39 @@ +/* + * @test /nodynamiccopyright/ + * @summary Verify that VBCs are final and immutable + * @compile/fail/ref=CheckFinal.out -XDrawDiagnostics -Werror -Xlint:values CheckFinal.java + */ + +@java.lang.ValueBased +class CheckFinal { // <- error + int x; // <- error + void f(int x) { // <- ok + int y; // <- ok + @java.lang.ValueBased + final class CheckLocalFinal { + int x; // <- error. + private CheckLocalFinal() {} + public int hashCode() { + return 0; + } + public boolean equals(Object o) { + return true; + } + public String toString() { + return "blah"; + } + } + } + final Object o = new Object() { int i; }; // <- ok + static int xs; // OK. + private CheckFinal() {} + public int hashCode() { + return 0; + } + public boolean equals(Object o) { + return true; + } + public String toString() { + return "blah"; + } +} diff --git a/test/langtools/tools/javac/lint/ValueBasisChecks/CheckFinal.out b/test/langtools/tools/javac/lint/ValueBasisChecks/CheckFinal.out new file mode 100644 index 00000000000..d49bd3557b6 --- /dev/null +++ b/test/langtools/tools/javac/lint/ValueBasisChecks/CheckFinal.out @@ -0,0 +1,6 @@ +CheckFinal.java:8:1: compiler.warn.value.based.class.must.be.final +CheckFinal.java:9:9: compiler.warn.field.of.value.based.class.must.be.final +CheckFinal.java:14:17: compiler.warn.field.of.value.based.class.must.be.final +- compiler.err.warnings.and.werror +1 error +3 warnings diff --git a/test/langtools/tools/javac/lint/ValueBasisChecks/CheckHashCodeEqualsToString.java b/test/langtools/tools/javac/lint/ValueBasisChecks/CheckHashCodeEqualsToString.java new file mode 100644 index 00000000000..99c41b93079 --- /dev/null +++ b/test/langtools/tools/javac/lint/ValueBasisChecks/CheckHashCodeEqualsToString.java @@ -0,0 +1,10 @@ +/* + * @test /nodynamiccopyright/ + * @summary Verify that VBCs don't inherit hashCode, equals and toString + * @compile/fail/ref=CheckHashCodeEqualsToString.out -XDrawDiagnostics -Werror -Xlint:values CheckHashCodeEqualsToString.java + */ + +@java.lang.ValueBased +final class CheckHashCodeEqualsToString { + private CheckHashCodeEqualsToString() {} +} diff --git a/test/langtools/tools/javac/lint/ValueBasisChecks/CheckHashCodeEqualsToString.out b/test/langtools/tools/javac/lint/ValueBasisChecks/CheckHashCodeEqualsToString.out new file mode 100644 index 00000000000..9a639025899 --- /dev/null +++ b/test/langtools/tools/javac/lint/ValueBasisChecks/CheckHashCodeEqualsToString.out @@ -0,0 +1,6 @@ +CheckHashCodeEqualsToString.java:8:7: compiler.warn.value.based.class.must.define.method: equals +CheckHashCodeEqualsToString.java:8:7: compiler.warn.value.based.class.must.define.method: hashCode +CheckHashCodeEqualsToString.java:8:7: compiler.warn.value.based.class.must.define.method: toString +- compiler.err.warnings.and.werror +1 error +3 warnings diff --git a/test/langtools/tools/javac/lint/ValueBasisChecks/CheckIdentityFreedom.java b/test/langtools/tools/javac/lint/ValueBasisChecks/CheckIdentityFreedom.java new file mode 100644 index 00000000000..77cfb97fde1 --- /dev/null +++ b/test/langtools/tools/javac/lint/ValueBasisChecks/CheckIdentityFreedom.java @@ -0,0 +1,69 @@ +/* + * @test /nodynamiccopyright/ + * @summary Value based class instances cannot be supplied where identity is assumed + * @compile/fail/ref=CheckIdentityFreedom.out -XDrawDiagnostics -Werror -Xlint:values CheckIdentityFreedom.java + */ + +@java.lang.ValueBased +public final class CheckIdentityFreedom { + + class Y { + Y (@Identityful Object o) {} + } + + class Z<@Identityful T> { + T get(T t) { return null; } + } + + private CheckIdentityFreedom() {} + + void foo(@Identityful Object @Identityful ... o) { + } + + void zoo(Object @Identityful ... o) { + } + + void boo(@Identityful Object ... o) { + } + + @Identityful Object goo() { + + foo(null); // nonvarargs call. + foo(this); // warn no identity. + foo(this, this); // warn no identity. + + new Y(this); // warn, ctor arg is expected to be Identityful + + // no warning, it is the array that is Identityful, not the elements + zoo(this); + zoo(this, this); + + // warn below, it is the array elements that are Identityful + boo(this); + boo(this, this); + + @Identityful Object o = this; // warn + + // The following two lines fail to trigger a warning due to a bug + // in type annotation handling. We don't see the element type as being + // Identityful due to type annotations seemingly being dropped. + + @Identityful Object [] oa = { this }; // warn + @Identityful Object [] oaa = new Object [] { this }; // warn + + // The following line fail to trigger a warning due to a bug + // in type annotation handling. We don't see the type variable's + // type as being Identityful due to type annotations seemingly being + // dropped. + Z zx = null; // warn. + + Z<@Identityful Object> zo = null; + zo.get(this); // no warning ??? + + return this; // warn. + } + + public int hashCode() { return 0; } + public String toString() { return ""; } + public boolean equals(Object o) { return true; } +} diff --git a/test/langtools/tools/javac/lint/ValueBasisChecks/CheckIdentityFreedom.out b/test/langtools/tools/javac/lint/ValueBasisChecks/CheckIdentityFreedom.out new file mode 100644 index 00000000000..e98cdc040c8 --- /dev/null +++ b/test/langtools/tools/javac/lint/ValueBasisChecks/CheckIdentityFreedom.out @@ -0,0 +1,13 @@ +CheckIdentityFreedom.java:31:13: compiler.warn.inexact.non-varargs.call: @java.lang.Identityful java.lang.Object, @java.lang.Identityful java.lang.Object @java.lang.Identityful [] +CheckIdentityFreedom.java:32:13: compiler.warn.no.identity.for.value.based.class.instance +CheckIdentityFreedom.java:33:13: compiler.warn.no.identity.for.value.based.class.instance +CheckIdentityFreedom.java:33:19: compiler.warn.no.identity.for.value.based.class.instance +CheckIdentityFreedom.java:35:15: compiler.warn.no.identity.for.value.based.class.instance +CheckIdentityFreedom.java:42:13: compiler.warn.no.identity.for.value.based.class.instance +CheckIdentityFreedom.java:43:13: compiler.warn.no.identity.for.value.based.class.instance +CheckIdentityFreedom.java:43:19: compiler.warn.no.identity.for.value.based.class.instance +CheckIdentityFreedom.java:45:33: compiler.warn.no.identity.for.value.based.class.instance +CheckIdentityFreedom.java:63:16: compiler.warn.no.identity.for.value.based.class.instance +- compiler.err.warnings.and.werror +1 error +10 warnings diff --git a/test/langtools/tools/javac/lint/ValueBasisChecks/CheckIdentityHash.java b/test/langtools/tools/javac/lint/ValueBasisChecks/CheckIdentityHash.java new file mode 100644 index 00000000000..8ff5a0cc8f8 --- /dev/null +++ b/test/langtools/tools/javac/lint/ValueBasisChecks/CheckIdentityHash.java @@ -0,0 +1,30 @@ +/* + * @test /nodynamiccopyright/ + * @summary Value based types do not support identityHashCode + * + * @compile/fail/ref=CheckIdentityHash.out -XDrawDiagnostics -Werror -Xlint:values CheckIdentityHash.java + */ +@java.lang.ValueBased +final class CheckIdentityHash { + int identityHashCode(CheckIdentityHash x) { + return 0; + } + void test(CheckIdentityHash v) { + this.identityHashCode(v); // <- ok + System.identityHashCode(v); // <- error + System.identityHashCode(this); // <- error + java.lang.System.identityHashCode(v); // <- error + java.lang.System.identityHashCode(this); // <- error + } + private CheckIdentityHash() {} + public int hashCode() { + return 0; + } + public boolean equals(Object o) { + return true; + } + public String toString() { + return "blah"; + } +} + diff --git a/test/langtools/tools/javac/lint/ValueBasisChecks/CheckIdentityHash.out b/test/langtools/tools/javac/lint/ValueBasisChecks/CheckIdentityHash.out new file mode 100644 index 00000000000..d77c50fa64f --- /dev/null +++ b/test/langtools/tools/javac/lint/ValueBasisChecks/CheckIdentityHash.out @@ -0,0 +1,7 @@ +CheckIdentityHash.java:14:32: compiler.warn.no.identity.for.value.based.class.instance +CheckIdentityHash.java:15:32: compiler.warn.no.identity.for.value.based.class.instance +CheckIdentityHash.java:16:42: compiler.warn.no.identity.for.value.based.class.instance +CheckIdentityHash.java:17:42: compiler.warn.no.identity.for.value.based.class.instance +- compiler.err.warnings.and.werror +1 error +4 warnings diff --git a/test/langtools/tools/javac/lint/ValueBasisChecks/CheckIdentityHash01.java b/test/langtools/tools/javac/lint/ValueBasisChecks/CheckIdentityHash01.java new file mode 100644 index 00000000000..24080c25c46 --- /dev/null +++ b/test/langtools/tools/javac/lint/ValueBasisChecks/CheckIdentityHash01.java @@ -0,0 +1,36 @@ +/* + * @test /nodynamiccopyright/ + * @summary Value based types do not support identityHashCode + * @compile/fail/ref=CheckIdentityHash01.out -XDrawDiagnostics -Werror -Xlint:values CheckIdentityHash01.java + */ + +import static java.lang.System.*; +@java.lang.ValueBased +final class CheckIdentityHash01 { + void test(CheckIdentityHash01 v) { + + identityHashCode(v); // <- error + identityHashCode(this); // <- error + + System system = null; + system.identityHashCode(v); // <- error + system.identityHashCode(this); // <- error + + System.identityHashCode(v); // <- error + System.identityHashCode(this); // <- error + + java.lang.System.identityHashCode(v); // <- error + java.lang.System.identityHashCode(this); // <- error + } + private CheckIdentityHash01() {} + public int hashCode() { + return 0; + } + public boolean equals(Object o) { + return true; + } + public String toString() { + return "blah"; + } +} + diff --git a/test/langtools/tools/javac/lint/ValueBasisChecks/CheckIdentityHash01.out b/test/langtools/tools/javac/lint/ValueBasisChecks/CheckIdentityHash01.out new file mode 100644 index 00000000000..bd62e086232 --- /dev/null +++ b/test/langtools/tools/javac/lint/ValueBasisChecks/CheckIdentityHash01.out @@ -0,0 +1,11 @@ +CheckIdentityHash01.java:12:25: compiler.warn.no.identity.for.value.based.class.instance +CheckIdentityHash01.java:13:25: compiler.warn.no.identity.for.value.based.class.instance +CheckIdentityHash01.java:16:32: compiler.warn.no.identity.for.value.based.class.instance +CheckIdentityHash01.java:17:32: compiler.warn.no.identity.for.value.based.class.instance +CheckIdentityHash01.java:19:32: compiler.warn.no.identity.for.value.based.class.instance +CheckIdentityHash01.java:20:32: compiler.warn.no.identity.for.value.based.class.instance +CheckIdentityHash01.java:22:42: compiler.warn.no.identity.for.value.based.class.instance +CheckIdentityHash01.java:23:42: compiler.warn.no.identity.for.value.based.class.instance +- compiler.err.warnings.and.werror +1 error +8 warnings diff --git a/test/langtools/tools/javac/lint/ValueBasisChecks/CheckLintDisabling.java b/test/langtools/tools/javac/lint/ValueBasisChecks/CheckLintDisabling.java new file mode 100644 index 00000000000..bb34140b725 --- /dev/null +++ b/test/langtools/tools/javac/lint/ValueBasisChecks/CheckLintDisabling.java @@ -0,0 +1,41 @@ +/* + * @test /nodynamiccopyright/ + * @summary Verify that no VBC warnings are emitted when -Xlint:values is NOT used or when suppressed. + * @compile/fail/ref=CheckLintDisabling.out -XDrawDiagnostics -Werror -Xlint:values CheckLintDisabling.java + * @compile -XDrawDiagnostics -Werror -Xlint:-values CheckLintDisabling.java + */ + +@java.lang.ValueBased +class CheckLintDisabling { // <- error + int x; // <- error + void f(int x) { // <- ok + int y; // <- ok + @java.lang.ValueBased + final class CheckLocalFinal { + @SuppressWarnings("values") + int x; // <- error but suppressed + @SuppressWarnings("values") + public CheckLocalFinal() {} // error, but suppressed + public int hashCode() { + return 0; + } + public boolean equals(Object o) { + return true; + } + public String toString() { + return "blah"; + } + } + } + private CheckLintDisabling() {} + public int hashCode() { + return 0; + } + public boolean equals(Object o) { + return true; + } + public String toString() { + return "blah"; + } +} + diff --git a/test/langtools/tools/javac/lint/ValueBasisChecks/CheckLintDisabling.out b/test/langtools/tools/javac/lint/ValueBasisChecks/CheckLintDisabling.out new file mode 100644 index 00000000000..c8572ceed88 --- /dev/null +++ b/test/langtools/tools/javac/lint/ValueBasisChecks/CheckLintDisabling.out @@ -0,0 +1,5 @@ +CheckLintDisabling.java:9:1: compiler.warn.value.based.class.must.be.final +CheckLintDisabling.java:10:9: compiler.warn.field.of.value.based.class.must.be.final +- compiler.err.warnings.and.werror +1 error +2 warnings diff --git a/test/langtools/tools/javac/lint/ValueBasisChecks/CheckNullMixing.java b/test/langtools/tools/javac/lint/ValueBasisChecks/CheckNullMixing.java new file mode 100644 index 00000000000..2717939324d --- /dev/null +++ b/test/langtools/tools/javac/lint/ValueBasisChecks/CheckNullMixing.java @@ -0,0 +1,44 @@ +/* + * @test /nodynamiccopyright/ + * @summary Check that mixing of nulls with value based class instances triggers warnings + * @compile/fail/ref=CheckNullMixing.out -XDrawDiagnostics -Werror -Xlint:values -XDComplainOnValueNullMix CheckNullMixing.java + */ + +@java.lang.ValueBased +final class CheckNullMixing { + + CheckNullMixing foo(CheckNullMixing cna, Object o) { + cna = null; // Warn + cna = (CheckNullMixing) null; // Warn + foo(null, null); // Warn + if (cna == null) {} // Warn + if (cna != null) {} // Warn + if (null == cna) {} // Warn + if (null != cna) {} // Warn + cna = (CheckNullMixing) o; // Warn + return null; // Warn + } + + final CheckNullMixing cnaf = null; // Warn + final CheckNullMixing [] cna = null; // OK + final CheckNullMixing [] cnav = { null }; // Warn + final CheckNullMixing [][] cnaa = { null }; // Ok. + final CheckNullMixing [][] cnaav = { { null } }; // Warn + final CheckNullMixing [] cnav2 = new CheckNullMixing [] { null }; // Warn + + { + cna[0] = null; // Warn + } + + private CheckNullMixing() {} + + public int hashCode() { + return 0; + } + public boolean equals(Object o) { + return true; + } + public String toString() { + return "blah"; + } +} diff --git a/test/langtools/tools/javac/lint/ValueBasisChecks/CheckNullMixing.out b/test/langtools/tools/javac/lint/ValueBasisChecks/CheckNullMixing.out new file mode 100644 index 00000000000..41843a4184b --- /dev/null +++ b/test/langtools/tools/javac/lint/ValueBasisChecks/CheckNullMixing.out @@ -0,0 +1,17 @@ +CheckNullMixing.java:11:15: compiler.warn.null.mixed.with.value.based.class +CheckNullMixing.java:12:33: compiler.warn.null.mixed.with.value.based.class +CheckNullMixing.java:13:13: compiler.warn.null.mixed.with.value.based.class +CheckNullMixing.java:14:17: compiler.warn.value.based.class.instances.should.not.be.compared.with: == +CheckNullMixing.java:15:17: compiler.warn.value.based.class.instances.should.not.be.compared.with: != +CheckNullMixing.java:16:18: compiler.warn.value.based.class.instances.should.not.be.compared.with: == +CheckNullMixing.java:17:18: compiler.warn.value.based.class.instances.should.not.be.compared.with: != +CheckNullMixing.java:18:33: compiler.warn.nullable.casted.to.value.based.class +CheckNullMixing.java:19:16: compiler.warn.null.mixed.with.value.based.class +CheckNullMixing.java:22:34: compiler.warn.null.mixed.with.value.based.class +CheckNullMixing.java:24:39: compiler.warn.null.mixed.with.value.based.class +CheckNullMixing.java:26:44: compiler.warn.null.mixed.with.value.based.class +CheckNullMixing.java:27:63: compiler.warn.null.mixed.with.value.based.class +CheckNullMixing.java:30:18: compiler.warn.null.mixed.with.value.based.class +- compiler.err.warnings.and.werror +1 error +14 warnings diff --git a/test/langtools/tools/javac/lint/ValueBasisChecks/CheckSync.java b/test/langtools/tools/javac/lint/ValueBasisChecks/CheckSync.java new file mode 100644 index 00000000000..a7fcdd23991 --- /dev/null +++ b/test/langtools/tools/javac/lint/ValueBasisChecks/CheckSync.java @@ -0,0 +1,56 @@ +/* + * @test /nodynamiccopyright/ + * @summary May not synchronize on value based types + * + * @compile/fail/ref=CheckSync.out -XDrawDiagnostics -Werror -Xlint:values CheckSync.java + */ + +@java.lang.ValueBased +public final class CheckSync { + @java.lang.ValueBased + final class Val { + private Val() {} + void foo() { + // All calls below are bad. + wait(); + wait(10); + wait(10, 10); + notify(); + notifyAll(); + } + public int hashCode() { + return 0; + } + public boolean equals(Object o) { + return true; + } + public String toString() { + return "blah"; + } + } + + final Val val = new Val(); + + void test() throws InterruptedException { + // All calls below are bad. + val.wait(); + val.wait(10); + val.wait(new Integer(10)); + val.wait(new Long(10)); + val.wait(10L); + val.wait(10L, 10); + val.wait("Hello"); + val.notify(); + val.notifyAll(); + } + private CheckSync() {} + public int hashCode() { + return 0; + } + public boolean equals(Object o) { + return true; + } + public String toString() { + return "blah"; + } +} diff --git a/test/langtools/tools/javac/lint/ValueBasisChecks/CheckSync.out b/test/langtools/tools/javac/lint/ValueBasisChecks/CheckSync.out new file mode 100644 index 00000000000..b21d7995bf6 --- /dev/null +++ b/test/langtools/tools/javac/lint/ValueBasisChecks/CheckSync.out @@ -0,0 +1,18 @@ +CheckSync.java:42:12: compiler.err.cant.apply.symbols: kindname.method, wait, java.lang.String,{(compiler.misc.inapplicable.method: kindname.method, java.lang.Object, wait(), (compiler.misc.arg.length.mismatch)),(compiler.misc.inapplicable.method: kindname.method, java.lang.Object, wait(long), (compiler.misc.no.conforming.assignment.exists: (compiler.misc.inconvertible.types: java.lang.String, long))),(compiler.misc.inapplicable.method: kindname.method, java.lang.Object, wait(long,int), (compiler.misc.arg.length.mismatch))} +CheckSync.java:15:17: compiler.warn.attempt.to.synchronize.on.instance.of.value.based.class +CheckSync.java:16:17: compiler.warn.attempt.to.synchronize.on.instance.of.value.based.class +CheckSync.java:17:17: compiler.warn.attempt.to.synchronize.on.instance.of.value.based.class +CheckSync.java:18:19: compiler.warn.attempt.to.synchronize.on.instance.of.value.based.class +CheckSync.java:19:22: compiler.warn.attempt.to.synchronize.on.instance.of.value.based.class +CheckSync.java:36:17: compiler.warn.attempt.to.synchronize.on.instance.of.value.based.class +CheckSync.java:37:17: compiler.warn.attempt.to.synchronize.on.instance.of.value.based.class +CheckSync.java:38:17: compiler.warn.attempt.to.synchronize.on.instance.of.value.based.class +CheckSync.java:39:17: compiler.warn.attempt.to.synchronize.on.instance.of.value.based.class +CheckSync.java:40:17: compiler.warn.attempt.to.synchronize.on.instance.of.value.based.class +CheckSync.java:41:17: compiler.warn.attempt.to.synchronize.on.instance.of.value.based.class +CheckSync.java:43:19: compiler.warn.attempt.to.synchronize.on.instance.of.value.based.class +CheckSync.java:44:22: compiler.warn.attempt.to.synchronize.on.instance.of.value.based.class +- compiler.note.deprecated.filename: CheckSync.java +- compiler.note.deprecated.recompile +1 error +13 warnings diff --git a/test/langtools/tools/javac/lint/ValueBasisChecks/CheckSynchronized.java b/test/langtools/tools/javac/lint/ValueBasisChecks/CheckSynchronized.java new file mode 100644 index 00000000000..db8821a933d --- /dev/null +++ b/test/langtools/tools/javac/lint/ValueBasisChecks/CheckSynchronized.java @@ -0,0 +1,29 @@ +/* + * @test /nodynamiccopyright/ + * @summary Check behavior of synzhronized key word on value based class instances and methods. + * + * @compile/fail/ref=CheckSynchronized.out -XDrawDiagnostics -Werror -Xlint:values CheckSynchronized.java + */ +@java.lang.ValueBased +final class CheckSynchronized { + synchronized void foo() { // <<-- ERROR, no monitor associated with `this' + } + void goo() { + synchronized(this) {} // <<-- ERROR, no monitor associated with `this' + } + synchronized static void zoo(CheckSynchronized cs) { // OK, static method. + synchronized(cs) { // <<-- ERROR, no monitor associated with value instance. + } + } + private CheckSynchronized() {} + public int hashCode() { + return 0; + } + public boolean equals(Object o) { + return true; + } + public String toString() { + return "blah"; + } +} + diff --git a/test/langtools/tools/javac/lint/ValueBasisChecks/CheckSynchronized.out b/test/langtools/tools/javac/lint/ValueBasisChecks/CheckSynchronized.out new file mode 100644 index 00000000000..fd23afc85e5 --- /dev/null +++ b/test/langtools/tools/javac/lint/ValueBasisChecks/CheckSynchronized.out @@ -0,0 +1,6 @@ +CheckSynchronized.java:9:23: compiler.warn.attempt.to.synchronize.on.instance.of.value.based.class +CheckSynchronized.java:12:9: compiler.warn.attempt.to.synchronize.on.instance.of.value.based.class +CheckSynchronized.java:15:9: compiler.warn.attempt.to.synchronize.on.instance.of.value.based.class +- compiler.err.warnings.and.werror +1 error +3 warnings diff --git a/test/langtools/tools/javac/lint/ValueBasisChecks/CheckVarargsCall.java b/test/langtools/tools/javac/lint/ValueBasisChecks/CheckVarargsCall.java new file mode 100644 index 00000000000..b60a478a216 --- /dev/null +++ b/test/langtools/tools/javac/lint/ValueBasisChecks/CheckVarargsCall.java @@ -0,0 +1,33 @@ +/* + * @test /nodynamiccopyright/ + * @summary Check passing of nulls to a varargs call + * @compile/fail/ref=CheckVarargsCall.out -XDrawDiagnostics -Werror -Xlint:values -XDComplainOnValueNullMix CheckVarargsCall.java + */ + +@java.lang.ValueBased +final class CheckVarargsCall { + + void foo(CheckVarargsCall vbc, CheckVarargsCall ... vbcv) { + } + + void goo() { + foo(this); // no warning + foo(null); // 1 warning + foo(this, this); // no warning + foo(null, null); // Only 1 warning - this is a nonvarargs call + foo(null, (CheckVarargsCall) null); // 2 warnings - a varargs call + foo(null, null, null); // 3 warnings + } + + public int hashCode() { + return 0; + } + public boolean equals(Object o) { + return true; + } + public String toString() { + return ""; + } + private CheckVarargsCall() {} +} + diff --git a/test/langtools/tools/javac/lint/ValueBasisChecks/CheckVarargsCall.out b/test/langtools/tools/javac/lint/ValueBasisChecks/CheckVarargsCall.out new file mode 100644 index 00000000000..e12f2620097 --- /dev/null +++ b/test/langtools/tools/javac/lint/ValueBasisChecks/CheckVarargsCall.out @@ -0,0 +1,11 @@ +CheckVarargsCall.java:17:19: compiler.warn.inexact.non-varargs.call: CheckVarargsCall, CheckVarargsCall[] +CheckVarargsCall.java:15:13: compiler.warn.null.mixed.with.value.based.class +CheckVarargsCall.java:17:13: compiler.warn.null.mixed.with.value.based.class +CheckVarargsCall.java:18:13: compiler.warn.null.mixed.with.value.based.class +CheckVarargsCall.java:18:38: compiler.warn.null.mixed.with.value.based.class +CheckVarargsCall.java:19:13: compiler.warn.null.mixed.with.value.based.class +CheckVarargsCall.java:19:19: compiler.warn.null.mixed.with.value.based.class +CheckVarargsCall.java:19:25: compiler.warn.null.mixed.with.value.based.class +- compiler.err.warnings.and.werror +1 error +8 warnings diff --git a/test/langtools/tools/javac/lint/ValueBasisChecks/ClassFileReaderTest.java b/test/langtools/tools/javac/lint/ValueBasisChecks/ClassFileReaderTest.java new file mode 100644 index 00000000000..82f572de0a3 --- /dev/null +++ b/test/langtools/tools/javac/lint/ValueBasisChecks/ClassFileReaderTest.java @@ -0,0 +1,13 @@ +/* + * @test /nodynamiccopyright/ + * @summary Verify that the class reader flags Value based classes appropriately. + * + * @compile Point.java + * @compile/fail/ref=ClassFileReaderTest.out -XDrawDiagnostics -Werror -Xlint:values ClassFileReaderTest.java + */ + +public class ClassFileReaderTest { + void foo(Point point) { + synchronized(point) {}; + } +} diff --git a/test/langtools/tools/javac/lint/ValueBasisChecks/ClassFileReaderTest.out b/test/langtools/tools/javac/lint/ValueBasisChecks/ClassFileReaderTest.out new file mode 100644 index 00000000000..ab243b8bc05 --- /dev/null +++ b/test/langtools/tools/javac/lint/ValueBasisChecks/ClassFileReaderTest.out @@ -0,0 +1,4 @@ +ClassFileReaderTest.java:11:9: compiler.warn.attempt.to.synchronize.on.instance.of.value.based.class +- compiler.err.warnings.and.werror +1 error +1 warning diff --git a/test/langtools/tools/javac/lint/ValueBasisChecks/Point.java b/test/langtools/tools/javac/lint/ValueBasisChecks/Point.java new file mode 100644 index 00000000000..9f2fd043529 --- /dev/null +++ b/test/langtools/tools/javac/lint/ValueBasisChecks/Point.java @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2020, 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. + */ + +@java.lang.ValueBased +final class Point { + final int x = 0; + final int y = 0; +} -- 2.17.1