diff --git a/make/CompileToolsJdk.gmk b/make/CompileToolsJdk.gmk index c291dbdba0a76..d825caca1c238 100644 --- a/make/CompileToolsJdk.gmk +++ b/make/CompileToolsJdk.gmk @@ -47,7 +47,7 @@ $(eval $(call SetupJavaCompilation, BUILD_TOOLS_JDK, \ build/tools/jigsaw \ build/tools/depend, \ BIN := $(BUILDTOOLS_OUTPUTDIR)/jdk_tools_classes, \ - DISABLED_WARNINGS := dangling-doc-comments options, \ + DISABLED_WARNINGS := dangling-doc-comments options suppression, \ JAVAC_FLAGS := \ --add-exports java.desktop/sun.awt=ALL-UNNAMED \ --add-exports java.base/sun.text=ALL-UNNAMED \ @@ -81,7 +81,7 @@ $(eval $(call SetupJavaCompilation, COMPILE_DEPEND, \ SRC := $(TOPDIR)/make/jdk/src/classes, \ INCLUDES := build/tools/depend, \ BIN := $(BUILDTOOLS_OUTPUTDIR)/depend, \ - DISABLED_WARNINGS := options, \ + DISABLED_WARNINGS := options suppression, \ JAVAC_FLAGS := \ --add-exports jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED \ --add-exports jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED \ diff --git a/make/GenerateLinkOptData.gmk b/make/GenerateLinkOptData.gmk index 6f6e1f29b4c16..27e34a81305c4 100644 --- a/make/GenerateLinkOptData.gmk +++ b/make/GenerateLinkOptData.gmk @@ -40,7 +40,7 @@ $(eval $(call SetupJavaCompilation, CLASSLIST_JAR, \ SMALL_JAVA := false, \ SRC := $(TOPDIR)/make/jdk/src/classes, \ INCLUDES := build/tools/classlist, \ - DISABLED_WARNINGS := dangling-doc-comments, \ + DISABLED_WARNINGS := dangling-doc-comments suppression, \ BIN := $(BUILDTOOLS_OUTPUTDIR)/classlist_classes, \ JAR := $(SUPPORT_OUTPUTDIR)/classlist.jar, \ )) diff --git a/make/JrtfsJar.gmk b/make/JrtfsJar.gmk index 54e2b094318fa..e7b93757bcbbd 100644 --- a/make/JrtfsJar.gmk +++ b/make/JrtfsJar.gmk @@ -51,7 +51,7 @@ JIMAGE_PKGS := \ # ends up in the image, this will ensure reproducible classes $(eval $(call SetupJavaCompilation, BUILD_JRTFS, \ COMPILER := interim, \ - DISABLED_WARNINGS := options, \ + DISABLED_WARNINGS := options suppression, \ TARGET_RELEASE := $(TARGET_RELEASE_JDK8), \ SRC := $(TOPDIR)/src/java.base/share/classes, \ EXCLUDE_FILES := module-info.java, \ diff --git a/make/modules/java.base/Java.gmk b/make/modules/java.base/Java.gmk index fc09137745662..27eec17ea25d3 100644 --- a/make/modules/java.base/Java.gmk +++ b/make/modules/java.base/Java.gmk @@ -28,8 +28,8 @@ # The base module should be built with all warnings enabled. When a # new warning is added to javac, it can be temporarily added to the # disabled warnings list. -# -# DISABLED_WARNINGS_java += + +DISABLED_WARNINGS_java += suppression DOCLINT += -Xdoclint:all/protected \ '-Xdoclint/package:java.*,javax.*' diff --git a/make/modules/java.desktop/Java.gmk b/make/modules/java.desktop/Java.gmk index bab6186fb0d0b..626571633fcc4 100644 --- a/make/modules/java.desktop/Java.gmk +++ b/make/modules/java.desktop/Java.gmk @@ -25,7 +25,7 @@ ################################################################################ -DISABLED_WARNINGS_java += dangling-doc-comments lossy-conversions this-escape +DISABLED_WARNINGS_java += dangling-doc-comments lossy-conversions this-escape suppression DOCLINT += -Xdoclint:all/protected \ '-Xdoclint/package:java.*,javax.*' COPY += .gif .png .wav .txt .xml .css .pf diff --git a/make/modules/java.management/Java.gmk b/make/modules/java.management/Java.gmk index 44e3f328c7fc9..f43c27df595ac 100644 --- a/make/modules/java.management/Java.gmk +++ b/make/modules/java.management/Java.gmk @@ -25,7 +25,7 @@ ################################################################################ -DISABLED_WARNINGS_java += dangling-doc-comments this-escape +DISABLED_WARNINGS_java += dangling-doc-comments this-escape suppression DOCLINT += -Xdoclint:all/protected \ '-Xdoclint/package:java.*,javax.*' diff --git a/make/modules/java.naming/Java.gmk b/make/modules/java.naming/Java.gmk index 1c7a2a1668a71..13d8247a5e7a9 100644 --- a/make/modules/java.naming/Java.gmk +++ b/make/modules/java.naming/Java.gmk @@ -25,7 +25,7 @@ ################################################################################ -DISABLED_WARNINGS_java += dangling-doc-comments this-escape +DISABLED_WARNINGS_java += dangling-doc-comments this-escape suppression DOCLINT += -Xdoclint:all/protected \ '-Xdoclint/package:java.*,javax.*' diff --git a/make/modules/java.prefs/Java.gmk b/make/modules/java.prefs/Java.gmk index 6e5ea0e2c733b..fb542fe10848c 100644 --- a/make/modules/java.prefs/Java.gmk +++ b/make/modules/java.prefs/Java.gmk @@ -25,6 +25,8 @@ ################################################################################ +DISABLED_WARNINGS_java += suppression + DOCLINT += -Xdoclint:all/protected \ '-Xdoclint/package:java.*,javax.*' diff --git a/make/modules/java.rmi/Java.gmk b/make/modules/java.rmi/Java.gmk index 4f24edb6f677b..3c11b0c9608f2 100644 --- a/make/modules/java.rmi/Java.gmk +++ b/make/modules/java.rmi/Java.gmk @@ -25,7 +25,7 @@ ################################################################################ -DISABLED_WARNINGS_java += this-escape +DISABLED_WARNINGS_java += this-escape suppression DOCLINT += -Xdoclint:all/protected \ '-Xdoclint/package:java.*,javax.*' diff --git a/make/modules/java.sql.rowset/Java.gmk b/make/modules/java.sql.rowset/Java.gmk index ecfe3e6e6412d..0c27ee35102b5 100644 --- a/make/modules/java.sql.rowset/Java.gmk +++ b/make/modules/java.sql.rowset/Java.gmk @@ -25,7 +25,7 @@ ################################################################################ -DISABLED_WARNINGS_java += dangling-doc-comments +DISABLED_WARNINGS_java += dangling-doc-comments suppression DOCLINT += -Xdoclint:all/protected \ '-Xdoclint/package:java.*,javax.*' diff --git a/make/modules/java.sql/Java.gmk b/make/modules/java.sql/Java.gmk index 44e3f328c7fc9..f43c27df595ac 100644 --- a/make/modules/java.sql/Java.gmk +++ b/make/modules/java.sql/Java.gmk @@ -25,7 +25,7 @@ ################################################################################ -DISABLED_WARNINGS_java += dangling-doc-comments this-escape +DISABLED_WARNINGS_java += dangling-doc-comments this-escape suppression DOCLINT += -Xdoclint:all/protected \ '-Xdoclint/package:java.*,javax.*' diff --git a/make/modules/java.xml.crypto/Java.gmk b/make/modules/java.xml.crypto/Java.gmk index 68db8ed817a23..0940e25a36294 100644 --- a/make/modules/java.xml.crypto/Java.gmk +++ b/make/modules/java.xml.crypto/Java.gmk @@ -25,7 +25,7 @@ ################################################################################ -DISABLED_WARNINGS_java += dangling-doc-comments this-escape +DISABLED_WARNINGS_java += dangling-doc-comments this-escape suppression DOCLINT += -Xdoclint:all/protected \ '-Xdoclint/package:java.*,javax.*' diff --git a/make/modules/java.xml/Java.gmk b/make/modules/java.xml/Java.gmk index 35f66238a7a96..e6896dc85d791 100644 --- a/make/modules/java.xml/Java.gmk +++ b/make/modules/java.xml/Java.gmk @@ -25,7 +25,7 @@ ################################################################################ -DISABLED_WARNINGS_java += dangling-doc-comments lossy-conversions this-escape +DISABLED_WARNINGS_java += dangling-doc-comments lossy-conversions this-escape suppression DOCLINT += -Xdoclint:all/protected \ '-Xdoclint/package:$(call CommaList, javax.xml.catalog javax.xml.datatype \ javax.xml.transform javax.xml.validation javax.xml.xpath)' diff --git a/make/modules/jdk.attach/Java.gmk b/make/modules/jdk.attach/Java.gmk new file mode 100644 index 0000000000000..de42fd80b6b02 --- /dev/null +++ b/make/modules/jdk.attach/Java.gmk @@ -0,0 +1,30 @@ +# +# 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. +# + +################################################################################ + +DISABLED_WARNINGS_java += suppression + +################################################################################ diff --git a/make/modules/jdk.dynalink/Java.gmk b/make/modules/jdk.dynalink/Java.gmk index f95e59bf36e26..52efb935420cf 100644 --- a/make/modules/jdk.dynalink/Java.gmk +++ b/make/modules/jdk.dynalink/Java.gmk @@ -25,6 +25,8 @@ ################################################################################ +DISABLED_WARNINGS_java += suppression + CLEAN += .properties ################################################################################ diff --git a/make/modules/jdk.internal.le/Java.gmk b/make/modules/jdk.internal.le/Java.gmk index 27c6eaf5f7f96..77439b114d758 100644 --- a/make/modules/jdk.internal.le/Java.gmk +++ b/make/modules/jdk.internal.le/Java.gmk @@ -25,7 +25,7 @@ ################################################################################ -DISABLED_WARNINGS_java += dangling-doc-comments this-escape +DISABLED_WARNINGS_java += dangling-doc-comments this-escape suppression COPY += .properties .caps .txt diff --git a/make/modules/jdk.jartool/Java.gmk b/make/modules/jdk.jartool/Java.gmk index 806975d1e1832..71e0ac9c5f1a8 100644 --- a/make/modules/jdk.jartool/Java.gmk +++ b/make/modules/jdk.jartool/Java.gmk @@ -25,6 +25,8 @@ ################################################################################ +DISABLED_WARNINGS_java += suppression + JAVAC_FLAGS += -XDstringConcat=inline ################################################################################ diff --git a/make/modules/jdk.jdeps/Java.gmk b/make/modules/jdk.jdeps/Java.gmk index f2da87aeadec9..a1bbecc389fee 100644 --- a/make/modules/jdk.jdeps/Java.gmk +++ b/make/modules/jdk.jdeps/Java.gmk @@ -25,6 +25,8 @@ ################################################################################ +DISABLED_WARNINGS_java += suppression + COPY += .txt CLEAN_FILES += $(wildcard \ diff --git a/make/modules/jdk.jfr/Java.gmk b/make/modules/jdk.jfr/Java.gmk index 5fabda8a4b777..d9d7c0b56f9fa 100644 --- a/make/modules/jdk.jfr/Java.gmk +++ b/make/modules/jdk.jfr/Java.gmk @@ -25,7 +25,7 @@ ################################################################################ -DISABLED_WARNINGS_java += dangling-doc-comments exports +DISABLED_WARNINGS_java += dangling-doc-comments exports suppression COPY := .xsd .xml .dtd .ini JAVAC_FLAGS := -XDstringConcat=inline diff --git a/make/modules/jdk.jlink/Java.gmk b/make/modules/jdk.jlink/Java.gmk index 4ddd1eab03d92..7fc7da7c5f2be 100644 --- a/make/modules/jdk.jlink/Java.gmk +++ b/make/modules/jdk.jlink/Java.gmk @@ -29,4 +29,6 @@ # upgrade_files_.conf files COPY += .conf +DISABLED_WARNINGS_java += suppression + ################################################################################ diff --git a/make/modules/jdk.jpackage/Java.gmk b/make/modules/jdk.jpackage/Java.gmk index da66fc1400901..f3434f2ebab21 100644 --- a/make/modules/jdk.jpackage/Java.gmk +++ b/make/modules/jdk.jpackage/Java.gmk @@ -25,7 +25,7 @@ ################################################################################ -DISABLED_WARNINGS_java += dangling-doc-comments +DISABLED_WARNINGS_java += dangling-doc-comments suppression COPY += .gif .png .txt .spec .script .prerm .preinst \ .postrm .postinst .list .sh .desktop .copyright .control .plist .template \ diff --git a/make/modules/jdk.jshell/Java.gmk b/make/modules/jdk.jshell/Java.gmk index f4194b23af7e3..1b9bf5b36b0a0 100644 --- a/make/modules/jdk.jshell/Java.gmk +++ b/make/modules/jdk.jshell/Java.gmk @@ -25,6 +25,8 @@ ################################################################################ +DISABLED_WARNINGS_java += suppression + COPY += .jsh .properties ################################################################################ diff --git a/make/modules/jdk.management.agent/Java.gmk b/make/modules/jdk.management.agent/Java.gmk new file mode 100644 index 0000000000000..de42fd80b6b02 --- /dev/null +++ b/make/modules/jdk.management.agent/Java.gmk @@ -0,0 +1,30 @@ +# +# 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. +# + +################################################################################ + +DISABLED_WARNINGS_java += suppression + +################################################################################ diff --git a/make/modules/jdk.management.jfr/Java.gmk b/make/modules/jdk.management.jfr/Java.gmk new file mode 100644 index 0000000000000..de42fd80b6b02 --- /dev/null +++ b/make/modules/jdk.management.jfr/Java.gmk @@ -0,0 +1,30 @@ +# +# 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. +# + +################################################################################ + +DISABLED_WARNINGS_java += suppression + +################################################################################ diff --git a/make/modules/jdk.management/Java.gmk b/make/modules/jdk.management/Java.gmk index aca47fc97f704..640c9a10f2e4e 100644 --- a/make/modules/jdk.management/Java.gmk +++ b/make/modules/jdk.management/Java.gmk @@ -25,6 +25,6 @@ ################################################################################ -DISABLED_WARNINGS_java += this-escape +DISABLED_WARNINGS_java += this-escape suppression ################################################################################ diff --git a/make/modules/jdk.naming.rmi/Java.gmk b/make/modules/jdk.naming.rmi/Java.gmk new file mode 100644 index 0000000000000..de42fd80b6b02 --- /dev/null +++ b/make/modules/jdk.naming.rmi/Java.gmk @@ -0,0 +1,30 @@ +# +# 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. +# + +################################################################################ + +DISABLED_WARNINGS_java += suppression + +################################################################################ diff --git a/make/test/BuildFailureHandler.gmk b/make/test/BuildFailureHandler.gmk index b4f3d690b0d75..2e039260c0440 100644 --- a/make/test/BuildFailureHandler.gmk +++ b/make/test/BuildFailureHandler.gmk @@ -47,7 +47,7 @@ $(eval $(call SetupJavaCompilation, BUILD_FAILURE_HANDLER, \ TARGET_RELEASE := $(TARGET_RELEASE_BOOTJDK), \ SRC := $(FH_BASEDIR)/src/share/classes $(FH_BASEDIR)/src/share/conf, \ BIN := $(FH_SUPPORT)/classes, \ - DISABLED_WARNINGS := options serial try this-escape, \ + DISABLED_WARNINGS := options serial try this-escape suppression, \ COPY := .properties, \ CLASSPATH := $(JTREG_JAR) $(TOOLS_JAR), \ JAR := $(FH_JAR), \ diff --git a/make/test/BuildMicrobenchmark.gmk b/make/test/BuildMicrobenchmark.gmk index 347ca44d25f39..84c7700258e7d 100644 --- a/make/test/BuildMicrobenchmark.gmk +++ b/make/test/BuildMicrobenchmark.gmk @@ -84,7 +84,7 @@ $(eval $(call SetupJavaCompilation, BUILD_JDK_MICROBENCHMARK, \ CLASSPATH := $(JMH_COMPILE_JARS), \ CREATE_API_DIGEST := true, \ DISABLED_WARNINGS := restricted this-escape processing rawtypes removal cast \ - serial preview dangling-doc-comments, \ + serial preview dangling-doc-comments suppression, \ SRC := $(MICROBENCHMARK_SRC), \ BIN := $(MICROBENCHMARK_CLASSES), \ JAVAC_FLAGS := \ diff --git a/make/test/BuildTestLib.gmk b/make/test/BuildTestLib.gmk index dc5e0a9bd64bc..eb25784b2b24d 100644 --- a/make/test/BuildTestLib.gmk +++ b/make/test/BuildTestLib.gmk @@ -63,6 +63,7 @@ $(eval $(call SetupJavaCompilation, BUILD_TEST_LIB_JAR, \ BIN := $(TEST_LIB_SUPPORT)/test-lib_classes, \ HEADERS := $(TEST_LIB_SUPPORT)/test-lib_headers, \ JAR := $(TEST_LIB_SUPPORT)/test-lib.jar, \ + DISABLED_WARNINGS := suppression, \ JAVAC_FLAGS := --add-exports java.base/sun.security.util=ALL-UNNAMED \ --add-exports java.base/jdk.internal.classfile=ALL-UNNAMED \ --add-exports java.base/jdk.internal.classfile.attribute=ALL-UNNAMED \ diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/api/JavacTaskImpl.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/api/JavacTaskImpl.java index 980ce060c3395..65ce640ef761f 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/api/JavacTaskImpl.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/api/JavacTaskImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -412,6 +412,7 @@ public void process(Env env) { f.run(compiler.todo, classes); } } finally { + compiler.log.reportOutstandingWarnings(); compiler.log.flush(); } return results; @@ -483,8 +484,10 @@ public void process(Env env) { } } finally { - if (compiler != null) + if (compiler != null) { + compiler.log.reportOutstandingWarnings(); compiler.log.flush(); + } } return results; } diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/api/JavacTaskPool.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/api/JavacTaskPool.java index 87bc64c4e1976..65772cdccb3f0 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/api/JavacTaskPool.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/api/JavacTaskPool.java @@ -55,6 +55,7 @@ import com.sun.source.util.TaskListener; import com.sun.source.util.TreeScanner; import com.sun.tools.javac.code.Kinds; +import com.sun.tools.javac.code.LintMapper; import com.sun.tools.javac.code.Preview; import com.sun.tools.javac.code.Symbol; import com.sun.tools.javac.code.Symtab; @@ -268,12 +269,11 @@ void clear() { if (ht.get(Log.logKey) instanceof ReusableLog) { //log already inited - not first round Log.instance(this).clear(); + LintMapper.instance(this).clear(); Enter.instance(this).newRound(); ((ReusableJavaCompiler)ReusableJavaCompiler.instance(this)).clear(); Types.instance(this).newRound(); Check.instance(this).newRound(); - Check.instance(this).clear(); //clear mandatory warning handlers - Preview.instance(this).clear(); //clear mandatory warning handlers Modules.instance(this).newRound(); Annotate.instance(this).newRound(); CompileStates.instance(this).clear(); diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/api/JavacTrees.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/api/JavacTrees.java index 753d6e4ff3ddc..b1c8b9639f173 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/api/JavacTrees.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/api/JavacTrees.java @@ -360,9 +360,14 @@ public TypeMirror getType(DocTreePath path) { Log.DeferredDiagnosticHandler deferredDiagnosticHandler = log.new DeferredDiagnosticHandler(); try { Env env = getAttrContext(path.getTreePath()); - Type t = attr.attribType(dcReference.qualifierExpression, env); - if (t != null && !t.isErroneous()) { - return t; + JavaFileObject prevSource = log.useSource(env.toplevel.sourcefile); + try { + Type t = attr.attribType(dcReference.qualifierExpression, env); + if (t != null && !t.isErroneous()) { + return t; + } + } finally { + log.useSource(prevSource); } } catch (Abort e) { // may be thrown by Check.completionError in case of bad class file return null; @@ -388,6 +393,7 @@ private Symbol attributeDocReference(TreePath path, DCReference ref) { return null; } Log.DeferredDiagnosticHandler deferredDiagnosticHandler = log.new DeferredDiagnosticHandler(); + JavaFileObject prevSource = log.useSource(env.toplevel.sourcefile); try { final TypeSymbol tsym; final Name memberName; @@ -509,6 +515,7 @@ private Symbol attributeDocReference(TreePath path, DCReference ref) { } catch (Abort e) { // may be thrown by Check.completionError in case of bad class file return null; } finally { + log.useSource(prevSource); log.popDiagnosticHandler(deferredDiagnosticHandler); } } diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/DeferredLintHandler.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/DeferredLintHandler.java deleted file mode 100644 index 11544b92b7f6f..0000000000000 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/DeferredLintHandler.java +++ /dev/null @@ -1,176 +0,0 @@ -/* - * Copyright (c) 2011, 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 com.sun.tools.javac.code; - -import java.util.ArrayDeque; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.Optional; -import java.util.function.Consumer; - -import com.sun.tools.javac.tree.JCTree; -import com.sun.tools.javac.tree.JCTree.Tag; -import com.sun.tools.javac.util.Assert; -import com.sun.tools.javac.util.Context; - -/** - * Holds pending {@link Lint} warnings until the {@lint Lint} instance associated with the containing - * module, package, class, method, or variable declaration is known so that {@link @SupressWarnings} - * suppressions may be applied. - * - *

- * Warnings are regsistered at any time prior to attribution via {@link #report}. The warning will be - * associated with the declaration placed in context by the most recent invocation of {@link #push push()} - * not yet {@link #pop}'d. Warnings are actually emitted later, during attribution, via {@link #flush}. - * - *

- * There is also an "immediate" mode, where warnings are emitted synchronously; see {@link #pushImmediate}. - * - *

- * Deferred warnings are grouped by the innermost containing module, package, class, method, or variable - * declaration (represented by {@link JCTree} nodes), so that the corresponding {@link Lint} configuration - * can be applied when the warning is eventually generated. - * - *

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 DeferredLintHandler { - - protected static final Context.Key deferredLintHandlerKey = new Context.Key<>(); - - public static DeferredLintHandler instance(Context context) { - DeferredLintHandler instance = context.get(deferredLintHandlerKey); - if (instance == null) - instance = new DeferredLintHandler(context); - return instance; - } - - /** - * Registered {@link LintLogger}s grouped by the innermost containing module, package, class, - * method, or variable declaration. - */ - private final HashMap> deferralMap = new HashMap<>(); - - /** - * The current "reporter" stack, reflecting calls to {@link #push} and {@link #pop}. - * - *

- * The top of the stack determines how calls to {@link #report} are handled. - */ - private final ArrayDeque> reporterStack = new ArrayDeque<>(); - - @SuppressWarnings("this-escape") - protected DeferredLintHandler(Context context) { - context.put(deferredLintHandlerKey, this); - Lint rootLint = Lint.instance(context); - pushImmediate(rootLint); // default to "immediate" mode - } - -// LintLogger - - /**An interface for deferred lint reporting - loggers passed to - * {@link #report(LintLogger) } will be called when - * {@link #flush(DiagnosticPosition) } is invoked. - */ - public interface LintLogger { - - /** - * Generate a warning if appropriate. - * - * @param lint the applicable lint configuration - */ - void report(Lint lint); - } - -// Reporter Stack - - /** - * Defer {@link #report}ed warnings until the given declaration is flushed. - * - * @param decl module, package, class, method, or variable declaration - * @see #pop - */ - public void push(JCTree decl) { - Assert.check(decl.getTag() == Tag.MODULEDEF - || decl.getTag() == Tag.PACKAGEDEF - || decl.getTag() == Tag.CLASSDEF - || decl.getTag() == Tag.METHODDEF - || decl.getTag() == Tag.VARDEF); - reporterStack.push(logger -> deferralMap - .computeIfAbsent(decl, s -> new ArrayList<>()) - .add(logger)); - } - - /** - * Enter "immediate" mode so that {@link #report}ed warnings are emitted synchonously. - * - * @param lint lint configuration to use for reported warnings - */ - public void pushImmediate(Lint lint) { - reporterStack.push(logger -> logger.report(lint)); - } - - /** - * Revert to the previous configuration in effect prior to the most recent invocation - * of {@link #push} or {@link #pushImmediate}. - * - * @see #pop - */ - public void pop() { - Assert.check(reporterStack.size() > 1); // the bottom stack entry should never be popped - reporterStack.pop(); - } - - /** - * Report a warning. - * - *

- * In immediate mode, the warning is emitted synchronously. Otherwise, the warning is emitted later - * when the current declaration is flushed. - */ - public void report(LintLogger logger) { - Assert.check(!reporterStack.isEmpty()); - reporterStack.peek().accept(logger); - } - -// Warning Flush - - /** - * Emit deferred warnings encompassed by the given declaration. - * - * @param decl module, package, class, method, or variable declaration - * @param lint lint configuration corresponding to {@code decl} - */ - public void flush(JCTree decl, Lint lint) { - Optional.of(decl) - .map(deferralMap::remove) - .stream() - .flatMap(ArrayList::stream) - .forEach(logger -> logger.report(lint)); - } -} 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 ede2511f35c48..9f05f42c12541 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 @@ -42,7 +42,6 @@ import com.sun.tools.javac.util.Context; import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition; import com.sun.tools.javac.util.JCDiagnostic.LintWarning; -import com.sun.tools.javac.util.Log; import com.sun.tools.javac.util.Names; import com.sun.tools.javac.util.Options; @@ -80,8 +79,9 @@ public static Lint instance(Context context) { */ public Lint augment(Symbol sym) { EnumSet suppressions = suppressionsFrom(sym); + suppressions.removeIf(lc -> !lc.annotationSuppression); // ignore categories that don't support @SuppressWarnings if (!suppressions.isEmpty()) { - Lint lint = new Lint(this); + Lint lint = new Lint(this, sym); lint.values.removeAll(suppressions); lint.suppressedValues.addAll(suppressions); return lint; @@ -94,7 +94,7 @@ public Lint augment(Symbol sym) { * @param lc one or more categories to be enabled */ public Lint enable(LintCategory... lc) { - Lint l = new Lint(this); + Lint l = new Lint(this, symbol); l.values.addAll(Arrays.asList(lc)); l.suppressedValues.removeAll(Arrays.asList(lc)); return l; @@ -105,15 +105,14 @@ public Lint enable(LintCategory... lc) { * @param lc one or more categories to be suppressed */ public Lint suppress(LintCategory... lc) { - Lint l = new Lint(this); + Lint l = new Lint(this, symbol); l.values.removeAll(Arrays.asList(lc)); l.suppressedValues.addAll(Arrays.asList(lc)); return l; } private final Context context; - private final Options options; - private final Log log; + private final LintMapper lintMapper; // These are initialized lazily to avoid dependency loops private Symtab syms; @@ -123,22 +122,27 @@ public Lint suppress(LintCategory... lc) { private EnumSet values; private EnumSet suppressedValues; + // The symbol corresponding to some declaration, or null for the root instance + private final Symbol symbol; + + // LintCategory lookup by option string private static final Map map = new LinkedHashMap<>(40); + // Instantiate the root instance @SuppressWarnings("this-escape") protected Lint(Context context) { this.context = context; context.put(lintKey, this); - options = Options.instance(context); - log = Log.instance(context); + symbol = null; + lintMapper = LintMapper.instance(context); } // Instantiate a non-root ("symbol scoped") instance - protected Lint(Lint other) { - other.initializeRootIfNeeded(); + protected Lint(Lint other, Symbol symbol) { + Assert.check(symbol != null); + this.symbol = symbol; this.context = other.context; - this.options = other.options; - this.log = other.log; + this.lintMapper = other.lintMapper; this.syms = other.syms; this.names = other.names; this.values = other.values.clone(); @@ -153,6 +157,7 @@ private void initializeRootIfNeeded() { return; // Initialize enabled categories based on "-Xlint" flags + Options options = Options.instance(context); 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); @@ -196,7 +201,11 @@ private void initializeRootIfNeeded() { @Override public String toString() { initializeRootIfNeeded(); - return "Lint:[enable" + values + ",suppress" + suppressedValues + "]"; + return "Lint[" + + (symbol != null ? "sym=" + symbol : "ROOT") + + ",enable" + values + + ",suppress" + suppressedValues + + "]"; } /** @@ -268,7 +277,7 @@ public enum LintCategory { /** * Warn about uses of @ValueBased classes where an identity class is expected. */ - IDENTITY("identity", true, "synchronization"), + IDENTITY("identity", true, true, "synchronization"), /** * Warn about use of incubating modules. @@ -302,9 +311,10 @@ public enum LintCategory { * Warn about issues relating to use of command line options. * *

- * This category is not supported by {@code @SuppressWarnings}. + * This category is not supported by {@code @SuppressWarnings} + * and is not tracked for unnecessary suppression. */ - OPTIONS("options", false), + OPTIONS("options", false, false), /** * Warn when any output file is written to more than once. @@ -328,9 +338,10 @@ public enum LintCategory { * Warn about invalid path elements on the command line. * *

- * This category is not supported by {@code @SuppressWarnings}. + * This category is not supported by {@code @SuppressWarnings} + * and is not tracked for unnecessary suppression. */ - PATH("path", false), + PATH("path", false, false), /** * Warn about issues regarding annotation processing. @@ -373,12 +384,17 @@ public enum LintCategory { STRICTFP("strictfp"), /** - * Warn about issues relating to use of text blocks + * Warn about recognized {@code @SuppressWarnings} lint categories that don't actually suppress any warnings. * *

- * This category is not supported by {@code @SuppressWarnings} (yet - see JDK-8224228). + * This category is not tracked for unnecessary suppression. + */ + SUPPRESSION("suppression", true, false), + + /** + * Warn about issues relating to use of text blocks */ - TEXT_BLOCKS("text-blocks", false), + TEXT_BLOCKS("text-blocks"), /** * Warn about possible 'this' escapes before subclass instance is fully initialized. @@ -414,9 +430,14 @@ public enum LintCategory { this(option, true); } - LintCategory(String option, boolean annotationSuppression, String... aliases) { + LintCategory(String option, boolean annotationSuppression) { + this(option, annotationSuppression, true); + } + + LintCategory(String option, boolean annotationSuppression, boolean suppressionTracking, String... aliases) { this.option = option; this.annotationSuppression = annotationSuppression; + this.suppressionTracking = suppressionTracking; ArrayList optionList = new ArrayList<>(1 + aliases.length); optionList.add(option); Collections.addAll(optionList, aliases); @@ -453,15 +474,42 @@ public static EnumSet newEmptySet() { /** Does this category support being suppressed by the {@code @SuppressWarnings} annotation? */ public final boolean annotationSuppression; + + /** Does the {@code "suppression"} category track suppressions in this category? */ + public final boolean suppressionTracking; + } + + /** + * Determine whether warnings in the given category should be calculated, because either + * (a) the category is enabled, or (b) lint category {@code "suppression"} is enabled. + * + *

+ * Use of this method is never required; it simply helps avoid potentially useless work. + */ + public boolean isActive(LintCategory lc) { + initializeRootIfNeeded(); + return values.contains(lc) || needsSuppressionTracking(lc); } /** * Checks if a warning category is enabled. A warning category may be enabled * on the command line, or by default, and can be temporarily disabled with * the SuppressWarnings annotation. + * + *

+ * This method also optionally validates any warning suppressions currently in scope. + * If you just want to know the configuration of this instance, set {@code validate} to false. + * If you are using the result of this method to control whether a warning is actually + * generated, then set {@code validate} to true to ensure that any suppression of the + * category in scope is validated (i.e., determined to actually be suppressing something). + * + * @param lc lint category + * @param validateSuppression true to also validate any suppression of the category */ - public boolean isEnabled(LintCategory lc) { + public boolean isEnabled(LintCategory lc, boolean validateSuppression) { initializeRootIfNeeded(); + if (validateSuppression) + validateSuppression(lc); return values.contains(lc); } @@ -470,31 +518,22 @@ public boolean isEnabled(LintCategory lc) { * of the SuppressWarnings annotation, or, in the case of the deprecated * category, whether it has been implicitly suppressed by virtue of the * current entity being itself deprecated. - */ - public boolean isSuppressed(LintCategory lc) { - initializeRootIfNeeded(); - return suppressedValues.contains(lc); - } - - /** - * Helper method. Log a lint warning if its lint category is enabled. * - * @param warning key for the localized warning message - */ - public void logIfEnabled(LintWarning warning) { - logIfEnabled(null, warning); - } - - /** - * Helper method. Log a lint warning if its lint category is enabled. + *

+ * This method also optionally validates any warning suppressions currently in scope. + * If you just want to know the configuration of this instance, set {@code validate} to false. + * If you are using the result of this method to control whether a warning is actually + * generated, then set {@code validate} to true to ensure that any suppression of the + * category in scope is validated (i.e., determined to actually be suppressing something). * - * @param pos source position at which to report the warning - * @param warning key for the localized warning message + * @param lc lint category + * @param validateSuppression true to also validate any suppression of the category */ - public void logIfEnabled(DiagnosticPosition pos, LintWarning warning) { - if (isEnabled(warning.getLintCategory())) { - log.warning(pos, warning); - } + public boolean isSuppressed(LintCategory lc, boolean validateSuppression) { + initializeRootIfNeeded(); + if (validateSuppression) + validateSuppression(lc); + return suppressedValues.contains(lc); } /** @@ -514,6 +553,20 @@ public EnumSet suppressionsFrom(Symbol symbol) { return suppressions; } + /** + * Retrieve the recognized lint categories suppressed by the given @SuppressWarnings annotation. + * + * @param annotation @SuppressWarnings annotation, or null + * @return set of lint categories, possibly empty but never null + */ + public EnumSet suppressionsFrom(JCAnnotation annotation) { + initializeSymbolsIfNeeded(); + if (annotation == null) + return LintCategory.newEmptySet(); + Assert.check(annotation.attribute.type.tsym == syms.suppressWarningsType.tsym); + return suppressionsFrom(Stream.of(annotation).map(anno -> anno.attribute)); + } + // Find the @SuppressWarnings annotation in the given stream and extract the recognized suppressions private EnumSet suppressionsFrom(Stream attributes) { initializeSymbolsIfNeeded(); @@ -532,12 +585,42 @@ private EnumSet suppressionsFrom(Attribute.Compound suppressWarnin for (Attribute value : values.values) { Optional.of((String)((Attribute.Constant)value).value) .flatMap(LintCategory::get) - .filter(lc -> lc.annotationSuppression) .ifPresent(result::add); } return result; } + /** + * Validate any suppression of the given category currently in scope. + * + *

+ * Such a suppression will therefore not be declared as unnecessary by the + * {@code "suppression"} warning. + * + * @param lc the lint category to be validated + * @return this instance + */ + public Lint validateSuppression(LintCategory lc) { + if (needsSuppressionTracking(lc)) + lintMapper.validateSuppression(symbol, lc); + return this; + } + + /** + * Determine whether we should bother tracking suppression validation for the given lint category. + * + *

+ * We need to track validation of suppression of a lint category if: + *

    + *
  • It's supported by {@code "suppression"} suppression tracking + *
  • Category {@code "suppression"} is currently enabled + *
+ */ + private boolean needsSuppressionTracking(LintCategory lc) { + initializeRootIfNeeded(); + return lc.suppressionTracking && values.contains(LintCategory.SUPPRESSION); + } + private void initializeSymbolsIfNeeded() { if (syms == null) { syms = Symtab.instance(context); diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/LintMapper.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/LintMapper.java new file mode 100644 index 0000000000000..b44c1606d747c --- /dev/null +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/LintMapper.java @@ -0,0 +1,610 @@ +/* + * 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 com.sun.tools.javac.code; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Comparator; +import java.util.EnumSet; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import java.util.function.Consumer; +import java.util.function.Function; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import javax.tools.DiagnosticListener; +import javax.tools.JavaFileObject; + +import com.sun.tools.javac.code.Lint.LintCategory; +import com.sun.tools.javac.code.Symbol.VarSymbol; +import com.sun.tools.javac.main.Option; +import com.sun.tools.javac.resources.CompilerProperties.LintWarnings; +import com.sun.tools.javac.tree.EndPosTable; +import com.sun.tools.javac.tree.JCTree; +import com.sun.tools.javac.tree.JCTree.*; +import com.sun.tools.javac.tree.TreeInfo; +import com.sun.tools.javac.tree.TreeScanner; +import com.sun.tools.javac.util.Assert; +import com.sun.tools.javac.util.Context; +import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition; +import com.sun.tools.javac.util.Log; +import com.sun.tools.javac.util.Names; +import com.sun.tools.javac.util.Options; + +import static com.sun.tools.javac.code.Lint.LintCategory.DEPRECATION; +import static com.sun.tools.javac.code.Lint.LintCategory.OPTIONS; +import static com.sun.tools.javac.code.Lint.LintCategory.SUPPRESSION; + +/** + * Maps source code positions to the applicable {@link Lint} instance. + * + *

+ * Because {@code @SuppressWarnings} is a Java symbol, in general this mapping can't be be + * calculated until after attribution. As each top-level declaration (class, package, or module) + * is attributed, this singleton is notified and the {@link Lint}s that apply to every source + * position within that top-level declaration are calculated. + * + *

+ * The method {@link #lintAt} returns the {@link Lint} instance applicable to source position; + * if it can't be determined yet, an empty {@link Optional} is returned. + * + *

+ * This class also tracks which {@code @SuppressWarnings} suppressions actually suppress something. + * Those that don't are unnecessary and trigger warnings in the {@code "suppression"} lint category. + * For this to work, this class must be notified any time a warning that is currently suppressed would + * have been reported; this is termed the "validation" of the suppression. That notification happens + * via {@link #validateSuppression}. + * + *

+ * Validation events "bubble up" the source tree until they are "caught" by a {@code @SuppressWarnings} + * annotation or they escape the file entirely. Being "caught" validates that suppression. + * A suppression that is never validated is unnecessary. + * + *

+ * Additional observations and corner cases: + *

    + *
  • Lint warnings can be suppressed at a module, package, class, method, or variable declaration + * (via {@code @SuppressWarnings}), or globally (via {@code -Xlint:-key}). + *
  • Consequently, an unnecessary suppression warning can only be emitted at one of those declarations, + * or globally at the end of compilation (the latter warning is possible in the future). + *
  • Some categories (e.g., {@code classfile}) don't support suppression via {@code @SuppressWarnings}. + * These can only generate warnings at the global level (and therefore any {@code @SuppressWarnings} + * annotation is always unnecessary). + *
  • Some categories are never tracked for suppression, e.g., {@code options}, {@code path}, and the + * suppression category {@code "suppression"} itself. + *
  • {@code @SuppressWarnings("suppression")} is perfectly valid: it means unnecessary suppression + * warnings will never be reported for any lint category suppressed by that annotation or by any + * {@code @SuppressWarnings} annotation nested within the scope of its declaration. + *
+ * + *

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 LintMapper { + + // The key for the context singleton + private static final Context.Key CONTEXT_KEY = new Context.Key<>(); + + // Per-source file lint information + private final Map fileInfoMap = new HashMap<>(); + + // Validations of "-Xlint:-foo" suppressions + private final EnumSet optionFlagValidations = LintCategory.newEmptySet(); + + // Compiler context + private final Context context; + + // These are initialized lazily; see initializeIfNeeded() + private Log log; + private Lint rootLint; + private Symtab syms; + private Names names; + private Options options; + + /** + * Obtain the {@link LintMapper} context singleton. + */ + public static LintMapper instance(Context context) { + LintMapper instance = context.get(CONTEXT_KEY); + if (instance == null) + instance = new LintMapper(context); + return instance; + } + + /** + * Constructor. + */ + @SuppressWarnings("this-escape") + protected LintMapper(Context context) { + context.put(CONTEXT_KEY, this); + this.context = context; + } + + // Lazy initialization to avoid dependency loops + private void initializeIfNeeded() { + if (rootLint == null) { + log = Log.instance(context); + rootLint = Lint.instance(context); + syms = Symtab.instance(context); + names = Names.instance(context); + options = Options.instance(context); + } + } + +// Lint Operations + + /** + * Determine if the given file is known to this instance. + * + * @param sourceFile source file + * @return true if file is recognized + */ + public boolean isKnown(JavaFileObject sourceFile) { + return fileInfoMap.containsKey(sourceFile); + } + + /** + * Obtain the {@link Lint} configuration that applies at the given position, if known. + * + * @param sourceFile source file + * @param pos source position + * @return the applicable {@link Lint}, if known + */ + public Optional lintAt(JavaFileObject sourceFile, DiagnosticPosition pos) { + initializeIfNeeded(); + return Optional.of(sourceFile) + .map(fileInfoMap::get) + .flatMap(fileInfo -> fileInfo.lintAt(pos)); + } + + /** + * Calculate {@lint Lint} configurations for all positions within the given top-level declaration. + * + * @param sourceFile source file + * @param tree top-level declaration (class, package, or module) + */ + public void calculateLints(JavaFileObject sourceFile, JCTree tree, EndPosTable endPositions) { + Assert.check(rootLint != null); + fileInfoMap.get(sourceFile).afterAttr(tree, endPositions); + } + + /** + * Reset this instance. + */ + public void clear() { + fileInfoMap.clear(); + optionFlagValidations.clear(); + } + +// Parsing Notifications + + /** + * Invoked when file parsing starts to create an entry for the new file. + */ + public void startParsingFile(JavaFileObject sourceFile) { + initializeIfNeeded(); + fileInfoMap.put(sourceFile, new FileInfo()); + } + + /** + * Invoked when file parsing completes to identify the top-level declarations. + */ + public void finishParsingFile(JCCompilationUnit tree) { + Assert.check(rootLint != null); + fileInfoMap.get(tree.sourcefile).afterParse(tree); + } + +// Suppression Tracking + + /** + * Validate the given lint category within the scope of the given symbol's declaration (or globally if symbol is null). + * + *

+ * This is to indicate that, if the category is being suppressed, a warning would have otherwise been generated. + * + * @param symbol innermost {@code @SuppressWarnings}-annotated symbol in scope, or null for global scope + * @param category lint category to validate + */ + public void validateSuppression(Symbol symbol, LintCategory category) { + EnumSet validations = symbol != null ? + fileInfoMap.get(log.currentSourceFile()).validationsFor(symbol) : optionFlagValidations; + validations.add(category); + } + + /** + * Warn about unnecessary {@code @SuppressWarnings} suppressions within the given tree. + * + *

+ * This step must be done after the given source file has been warned about. + * + * @param sourceFile source file + * @param tree top level declaration + */ + public void reportUnnecessarySuppressionAnnotations(JavaFileObject sourceFile, JCTree tree) { + initializeIfNeeded(); + FileInfo fileInfo = fileInfoMap.get(sourceFile); + DeclNode topNode = fileInfo.findTopNode(tree.pos()); + + // Propagate validations in this top-level declaration to determine which suppressions never got validated + propagateValidations(fileInfo, topNode); + + // Report them if needed + if (rootLint.isEnabled(SUPPRESSION, false)) { + topNode.stream() + .filter(node -> node.lint.isEnabled(SUPPRESSION, false)) + .forEach(node -> report(node.unvalidated, name -> "\"" + name + "\"", + names -> log.warning(node.annotation.pos(), LintWarnings.UnnecessaryWarningSuppression(names)))); + } + } + + private void report(EnumSet unvalidated, Function formatter, Consumer logger) { + String names = unvalidated.stream() + .filter(lc -> lc.suppressionTracking) + .map(category -> category.option) + .map(formatter) + .collect(Collectors.joining(", ")); + if (!names.isEmpty()) + logger.accept(names); + } + + // Propagate validations in the given top-level declaration; any that escape validate the corresponding "Xlint" suppression + private void propagateValidations(FileInfo fileInfo, DeclNode topNode) { + optionFlagValidations.addAll(fileInfo.propagateValidations(topNode)); + } + +// FileInfo + + /** + * Holds {@link Lint} information for one source file. + * + *

+ * Instances evolve through three states: + *

    + *
  • Before the file has been completely parsed, {@link #topSpans} is null. + *
  • Immediately after the file has been parsed, {@link #topSpans} contains zero or more {@link Span}s + * corresponding to the top-level declarations in the file, and {@code rootNode} has no children. + *
  • When a top-level declaration is attributed, a corresponding {@link DeclNode} child matching one + * of the {@link Span}s in {@link #topSpans} is created and added to {@link #rootNode}. + *
+ */ + private class FileInfo { + + List topSpans; // the spans of all top level declarations + final DeclNode rootNode = new DeclNode(rootLint); // tree of file's "interesting" declaration nodes + final Map> validationsMap // maps declaration symbol to validations therein + = new HashMap<>(); + + // Find the Lint that applies to the given position, if known + Optional lintAt(DiagnosticPosition pos) { + if (topSpans == null) // has the file been parsed yet? + return Optional.empty(); // -> no, we don't know yet + if (!findTopSpan(pos).isPresent()) // is the position within some top-level declaration? + return Optional.of(rootLint); // -> no, use the root lint + DeclNode topNode = findTopNode(pos); + if (topNode == null) // has that declaration been attributed yet? + return Optional.empty(); // -> no, we don't know yet + DeclNode node = topNode.find(pos); // find the best matching node (it must exist) + return Optional.of(node.lint); // use its Lint + } + + // Obtain the validation state for the given symbol + EnumSet validationsFor(Symbol symbol) { + return validationsMap.computeIfAbsent(symbol, s -> LintCategory.newEmptySet()); + } + + // Combine the validation sets for two variable symbols that are declared together + void mergeValidations(VarSymbol symbol1, VarSymbol symbol2) { + EnumSet validations1 = validationsFor(symbol1); + EnumSet validations2 = validationsFor(symbol2); + Assert.check(validations1.equals(validations2)); + validationsMap.put(symbol2, validations1); // now the two symbols share the same validation set + } + + // Propagate validations in the given top-level node + EnumSet propagateValidations(DeclNode topNode) { + Assert.check(rootNode.children.contains(topNode)); + return topNode.propagateValidations(validationsMap); + } + + void afterParse(JCCompilationUnit tree) { + Assert.check(topSpans == null, "source already parsed"); + topSpans = tree.defs.stream() + .filter(this::isTopLevelDecl) + .map(decl -> new Span(decl, tree.endPositions)) + .collect(Collectors.toList()); + } + + void afterAttr(JCTree tree, EndPosTable endPositions) { + Assert.check(topSpans != null, "source not parsed"); + Assert.check(findTopNode(tree.pos()) == null, "duplicate call"); + new DeclNodeTreeBuilder(this, rootNode, endPositions).scan(tree); + } + + Optional findTopSpan(DiagnosticPosition pos) { + return topSpans.stream() + .filter(span -> span.contains(pos)) + .findFirst(); + } + + DeclNode findTopNode(DiagnosticPosition pos) { + return rootNode.children.stream() + .filter(node -> node.contains(pos)) + .findFirst() + .orElse(null); + } + + boolean isTopLevelDecl(JCTree tree) { + return tree.getTag() == Tag.MODULEDEF + || tree.getTag() == Tag.PACKAGEDEF + || tree.getTag() == Tag.CLASSDEF; + } + } + +// Span + + /** + * Represents a lexical range in a file. + */ + private static class Span { + + final int startPos; + final int endPos; + + Span(int startPos, int endPos) { + this.startPos = startPos; + this.endPos = endPos; + } + + Span(JCTree tree, EndPosTable endPositions) { + this(TreeInfo.getStartPos(tree), TreeInfo.getEndPos(tree, endPositions)); + } + + boolean contains(int pos) { + return pos == startPos || (pos > startPos && pos < endPos); + } + + boolean contains(DiagnosticPosition pos) { + return contains(pos.getLintPosition()); + } + + boolean contains(Span that) { + return this.startPos <= that.startPos && this.endPos >= that.endPos; + } + + @Override + public String toString() { + return String.format("Span[%d-%d]", startPos, endPos); + } + } + +// DeclNode + + /** + * Represents a declaration and the {@link Lint} configuration that applies within its lexical range. + * + *

+ * For each file, there is a root node represents the entire source file. At the next level down are + * nodes representing the top-level declarations in the file, and so on. + */ + private static class DeclNode extends Span { + + final Symbol symbol; // the symbol declared by this declaration (null for root) + final DeclNode parent; // the immediately containing declaration (null for root) + final List children = new ArrayList<>(); // the immediately next level down declarations under this node + final Lint lint; // the Lint configuration that applies at this declaration + final JCAnnotation annotation; // the @SuppressWarnings on this declaration, if any + final EnumSet suppressions; // categories suppressed by @SuppressWarnings, if any + final EnumSet unvalidated; // categories in "suppressions" that were never validated + + // Create a root node representing the entire file + DeclNode(Lint rootLint) { + super(Integer.MIN_VALUE, Integer.MAX_VALUE); + this.symbol = null; + this.parent = null; + this.lint = rootLint; + this.annotation = null; + this.suppressions = LintCategory.newEmptySet(); // you can't put @SuppressWarnings on a file + this.unvalidated = EnumSet.copyOf(suppressions); + } + + // Create a normal declaration node + DeclNode(Symbol symbol, DeclNode parent, JCTree tree, EndPosTable endPositions, + Lint lint, JCAnnotation annotation, EnumSet suppressions) { + super(tree, endPositions); + this.symbol = symbol; + this.parent = parent; + this.lint = lint; + this.annotation = annotation; + this.suppressions = suppressions; + this.unvalidated = EnumSet.copyOf(suppressions); + parent.children.add(this); + } + + // Find the narrowest node in this tree that contains the given position, if any + DeclNode find(DiagnosticPosition pos) { + return children.stream() + .map(child -> child.find(pos)) + .filter(Objects::nonNull) + .reduce((a, b) -> a.contains(b) ? b : a) + .orElseGet(() -> contains(pos) ? this : null); + } + + // Stream this node and all descendents via pre-order recursive descent + Stream stream() { + return Stream.concat(Stream.of(this), children.stream().flatMap(DeclNode::stream)); + } + + // Calculate the unvalidated suppressions in the subtree rooted at this node. We do this by recursively + // propagating validations upward until they are "caught" by some matching suppression; this validates + // the suppression. Validations that are not caught are returned to the caller. + public EnumSet propagateValidations(Map> validationsMap) { + + // Recurse on subtrees first and gather their uncaught validations + EnumSet validations = LintCategory.newEmptySet(); + children.stream() + .map(child -> child.propagateValidations(validationsMap)) + .forEach(validations::addAll); + + // Add in the validations that occurred at this node, if any + Optional.of(symbol) + .map(validationsMap::get) + .ifPresent(validations::addAll); + + // Apply (and then discard) validations that match any of this node's suppressions + validations.removeIf(category -> { + if (suppressions.contains(category)) { + unvalidated.remove(category); + return true; + } + return false; + }); + + // Propagate the remaining validations that weren't caught upward + return validations; + } + + @Override + public String toString() { + String label = symbol != null ? "sym=" + symbol : "ROOT"; + return String.format("DeclNode[%s,lint=%s,suppressions=%s]", label, lint, suppressions); + } + } + +// DeclNodeTreeBuilder + + /** + * Builds a tree of {@link DeclNode}s starting from a top-level declaration. + */ + private class DeclNodeTreeBuilder extends TreeScanner { + + // Variables declared together (separated by commas) share their @SuppressWarnings annotation, so they must also share + // the set of validated suppressions: the suppression of a category is valid if *any* of the variables validates it. + // We detect that situation using this map and, when found, invoke FileInfo.mergeValidations(). + private final Map annotationRepresentativeSymbolMap = new HashMap<>(); + + private final FileInfo fileInfo; + private final EndPosTable endPositions; + + private DeclNode parent; + private Lint lint; + + DeclNodeTreeBuilder(FileInfo fileInfo, DeclNode rootNode, EndPosTable endPositions) { + this.fileInfo = fileInfo; + this.endPositions = endPositions; + this.parent = rootNode; + this.lint = rootNode.lint; // i.e, rootLint + } + + @Override + public void visitModuleDef(JCModuleDecl tree) { + scanDecl(tree, tree.sym, findAnnotation(tree.mods), super::visitModuleDef); + } + + @Override + public void visitPackageDef(JCPackageDecl tree) { + scanDecl(tree, tree.packge, findAnnotation(tree.annotations), super::visitPackageDef); + } + + @Override + public void visitClassDef(JCClassDecl tree) { + scanDecl(tree, tree.sym, findAnnotation(tree.mods), super::visitClassDef); + } + + @Override + public void visitMethodDef(JCMethodDecl tree) { + scanDecl(tree, tree.sym, findAnnotation(tree.mods), super::visitMethodDef); + } + + @Override + public void visitVarDef(JCVariableDecl tree) { + scanDecl(tree, tree.sym, findAnnotation(tree.mods), super::visitVarDef); + } + + private void scanDecl(T tree, Symbol symbol, JCAnnotation annotation, Consumer recursion) { + + // "symbol" can be null if there were earlier errors; skip this declaration if so + if (symbol == null) { + recursion.accept(tree); + return; + } + + // Update the current Lint in effect; note lint.augment() returns the same instance if there's no change + Lint previousLint = lint; + lint = lint.augment(symbol); + + // Get the lint categories explicitly suppressed at this symbol's declaration by @SuppressedWarnings + EnumSet suppressed = Optional.ofNullable(annotation) + .map(anno -> rootLint.suppressionsFrom(anno)) + .orElseGet(LintCategory::newEmptySet); + + // Merge validation sets for variables that share the same declaration (and therefore the same @SuppressedWarnings) + if (annotation != null && symbol instanceof VarSymbol varSym) { + annotationRepresentativeSymbolMap.merge(annotation, varSym, (oldSymbol, newSymbol) -> { + fileInfo.mergeValidations(oldSymbol, newSymbol); + return oldSymbol; + }); + } + + // If this declaration is not "interesting", we don't need to create a DeclNode for it + if (lint == previousLint && parent.parent != null && suppressed.isEmpty()) { + recursion.accept(tree); + return; + } + + // Add a DeclNode here + DeclNode node = new DeclNode(symbol, parent, tree, endPositions, lint, annotation, suppressed); + parent = node; + try { + recursion.accept(tree); + } finally { + parent = node.parent; + lint = previousLint; + } + } + + // Retrieve the @SuppressWarnings annotation, if any, from the given modifiers + private JCAnnotation findAnnotation(JCModifiers mods) { + return Optional.ofNullable(mods) + .map(m -> m.annotations) + .map(this::findAnnotation) + .orElse(null); + } + + // Retrieve the @SuppressWarnings annotation, if any, from the given list of annotations + private JCAnnotation findAnnotation(Collection annotations) { + return Optional.ofNullable(annotations) + .stream() + .flatMap(Collection::stream) + .filter(a -> a.attribute.type.tsym == syms.suppressWarningsType.tsym) + .findFirst() + .orElse(null); + } + } +} diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Preview.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Preview.java index d1e64e90a07cc..9152d3df843cc 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Preview.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Preview.java @@ -34,13 +34,13 @@ import com.sun.tools.javac.resources.CompilerProperties.Warnings; import com.sun.tools.javac.util.Assert; import com.sun.tools.javac.util.Context; +import com.sun.tools.javac.util.JCDiagnostic.DiagnosticFlag; import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition; import com.sun.tools.javac.util.JCDiagnostic.Error; import com.sun.tools.javac.util.JCDiagnostic.LintWarning; import com.sun.tools.javac.util.JCDiagnostic.SimpleDiagnosticPosition; import com.sun.tools.javac.util.JCDiagnostic.Warning; import com.sun.tools.javac.util.Log; -import com.sun.tools.javac.util.MandatoryWarningHandler; import com.sun.tools.javac.util.Names; import com.sun.tools.javac.util.Options; @@ -68,12 +68,6 @@ public class Preview { /** flag: are preview features enabled */ private final boolean enabled; - /** flag: is the "preview" lint category enabled? */ - private final boolean verbose; - - /** the diag handler to manage preview feature usage diagnostics */ - private final MandatoryWarningHandler previewHandler; - /** test flag: should all features be considered as preview features? */ private final boolean forcePreview; @@ -104,8 +98,6 @@ protected Preview(Context context) { enabled = options.isSet(PREVIEW); log = Log.instance(context); source = Source.instance(context); - verbose = Lint.instance(context).isEnabled(LintCategory.PREVIEW); - previewHandler = new MandatoryWarningHandler(log, source, verbose, true, LintCategory.PREVIEW); forcePreview = options.isSet("forcePreview"); majorVersionToSource = initMajorVersionToSourceMap(); } @@ -176,9 +168,11 @@ public void warnPreview(DiagnosticPosition pos, Feature feature) { Assert.check(isEnabled()); Assert.check(isPreview(feature)); markUsesPreview(pos); - previewHandler.report(pos, feature.isPlural() ? + log.mandatoryWarning(pos, + feature.isPlural() ? LintWarnings.PreviewFeatureUsePlural(feature.nameFragment()) : - LintWarnings.PreviewFeatureUse(feature.nameFragment())); + LintWarnings.PreviewFeatureUse(feature.nameFragment()), + DiagnosticFlag.AGGREGATE); } /** @@ -188,10 +182,8 @@ public void warnPreview(DiagnosticPosition pos, Feature feature) { */ public void warnPreview(JavaFileObject classfile, int majorVersion) { Assert.check(isEnabled()); - if (verbose) { - log.mandatoryWarning(null, - LintWarnings.PreviewFeatureUseClassfile(classfile, majorVersionToSource.get(majorVersion).name)); - } + log.warning(LintWarnings.PreviewFeatureUseClassfile(classfile, majorVersionToSource.get(majorVersion).name), + DiagnosticFlag.MANDATORY); // make it mandatory but don't include DiagnosticFlag.DEFAULT_ENABLED } /** @@ -203,10 +195,6 @@ public void markUsesPreview(DiagnosticPosition pos) { sourcesWithPreviewFeatures.add(log.currentSourceFile()); } - public void reportPreviewWarning(DiagnosticPosition pos, LintWarning warnKey) { - previewHandler.report(pos, warnKey); - } - public boolean usesPreview(JavaFileObject file) { return sourcesWithPreviewFeatures.contains(file); } @@ -269,25 +257,13 @@ public boolean declaredUsingPreviewFeature(Symbol sym) { return false; } - /** - * Report any deferred diagnostics. - */ - public void reportDeferredDiagnostics() { - previewHandler.reportDeferredDiagnostic(); - } - - public void clear() { - previewHandler.clear(); - } - public void checkSourceLevel(DiagnosticPosition pos, Feature feature) { if (isPreview(feature) && !isEnabled()) { //preview feature without --preview flag, error - log.error(JCDiagnostic.DiagnosticFlag.SOURCE_LEVEL, pos, disabledError(feature)); + log.error(DiagnosticFlag.SOURCE_LEVEL, pos, disabledError(feature)); } else { if (!feature.allowedInSource(source)) { - log.error(JCDiagnostic.DiagnosticFlag.SOURCE_LEVEL, pos, - feature.error(source.name)); + log.error(DiagnosticFlag.SOURCE_LEVEL, pos, feature.error(source.name)); } if (isEnabled() && isPreview(feature)) { warnPreview(pos, feature); diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Annotate.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Annotate.java index 3da832484364e..9f56bec4cca2d 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Annotate.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Annotate.java @@ -89,9 +89,7 @@ public static Annotate instance(Context context) { private final Attr attr; private final Check chk; private final ConstFold cfolder; - private final DeferredLintHandler deferredLintHandler; private final Enter enter; - private final Lint lint; private final Log log; private final Names names; private final Resolve resolve; @@ -110,10 +108,8 @@ protected Annotate(Context context) { attr = Attr.instance(context); chk = Check.instance(context); cfolder = ConstFold.instance(context); - deferredLintHandler = DeferredLintHandler.instance(context); enter = Enter.instance(context); log = Log.instance(context); - lint = Lint.instance(context); make = TreeMaker.instance(context); names = Names.instance(context); resolve = Resolve.instance(context); @@ -235,10 +231,8 @@ public void afterTypes(Runnable a) { * @param annotations the list of JCAnnotations to attribute and enter * @param localEnv the enclosing env * @param s the Symbol on which to enter the annotations - * @param deferDecl enclosing declaration for DeferredLintHandler, or null for no deferral */ - public void annotateLater(List annotations, Env localEnv, - Symbol s, JCTree deferDecl) + public void annotateLater(List annotations, Env localEnv, Symbol s) { if (annotations.isEmpty()) { return; @@ -256,8 +250,6 @@ public void annotateLater(List annotations, Env local // been handled, meaning that the set of annotations pending completion is now empty. Assert.check(s.kind == PCK || s.annotationsPendingCompletion()); JavaFileObject prev = log.useSource(localEnv.toplevel.sourcefile); - Assert.check(deferDecl != null); - deferredLintHandler.push(deferDecl); try { if (s.hasAnnotations() && annotations.nonEmpty()) log.error(annotations.head.pos, Errors.AlreadyAnnotated(Kinds.kindName(s), s)); @@ -268,7 +260,6 @@ public void annotateLater(List annotations, Env local // never called for a type parameter annotateNow(s, annotations, localEnv, false, false); } finally { - deferredLintHandler.pop(); log.useSource(prev); } }); @@ -285,16 +276,13 @@ public void annotateLater(List annotations, Env local /** Queue processing of an attribute default value. */ - public void annotateDefaultValueLater(JCExpression defaultValue, Env localEnv, - MethodSymbol m, JCTree deferDecl) + public void annotateDefaultValueLater(JCExpression defaultValue, Env localEnv, MethodSymbol m) { normal(() -> { JavaFileObject prev = log.useSource(localEnv.toplevel.sourcefile); - deferredLintHandler.push(deferDecl); try { enterDefaultValue(defaultValue, localEnv, m); } finally { - deferredLintHandler.pop(); log.useSource(prev); } }); @@ -682,7 +670,7 @@ private Attribute getAnnotationPrimitiveValue(Type expectedElementType, JCExpres // Scan the annotation element value and then attribute nested annotations if present if (tree.type != null && tree.type.tsym != null) { - queueScanTreeAndTypeAnnotate(tree, env, tree.type.tsym, null); + queueScanTreeAndTypeAnnotate(tree, env, tree.type.tsym); } result = cfolder.coerce(result, expectedElementType); @@ -1034,20 +1022,14 @@ private T makeContainerAnnotation(List toBeRep /** * Attribute the list of annotations and enter them onto s. */ - public void enterTypeAnnotations(List annotations, Env env, - Symbol s, JCTree deferDecl, boolean isTypeParam) + public void enterTypeAnnotations(List annotations, Env env, Symbol s, boolean isTypeParam) { Assert.checkNonNull(s, "Symbol argument to actualEnterTypeAnnotations is nul/"); JavaFileObject prev = log.useSource(env.toplevel.sourcefile); - if (deferDecl != null) { - deferredLintHandler.push(deferDecl); - } try { annotateNow(s, annotations, env, true, isTypeParam); } finally { - if (deferDecl != null) - deferredLintHandler.pop(); log.useSource(prev); } } @@ -1055,10 +1037,10 @@ public void enterTypeAnnotations(List annotations, Env env, Symbol sym, JCTree deferDecl) + public void queueScanTreeAndTypeAnnotate(JCTree tree, Env env, Symbol sym) { Assert.checkNonNull(sym); - normal(() -> tree.accept(new TypeAnnotate(env, sym, deferDecl))); + normal(() -> tree.accept(new TypeAnnotate(env, sym))); } /** @@ -1093,32 +1075,30 @@ public void annotateTypeParameterSecondStage(JCTree tree, List ann private class TypeAnnotate extends TreeScanner { private final Env env; private final Symbol sym; - private JCTree deferDecl; - public TypeAnnotate(Env env, Symbol sym, JCTree deferDecl) { + public TypeAnnotate(Env env, Symbol sym) { this.env = env; this.sym = sym; - this.deferDecl = deferDecl; } @Override public void visitAnnotatedType(JCAnnotatedType tree) { - enterTypeAnnotations(tree.annotations, env, sym, deferDecl, false); + enterTypeAnnotations(tree.annotations, env, sym, false); scan(tree.underlyingType); } @Override public void visitTypeParameter(JCTypeParameter tree) { - enterTypeAnnotations(tree.annotations, env, sym, deferDecl, true); + enterTypeAnnotations(tree.annotations, env, sym, true); scan(tree.bounds); } @Override public void visitNewArray(JCNewArray tree) { - enterTypeAnnotations(tree.annotations, env, sym, deferDecl, false); + enterTypeAnnotations(tree.annotations, env, sym, false); for (List dimAnnos : tree.dimAnnotations) - enterTypeAnnotations(dimAnnos, env, sym, deferDecl, false); + enterTypeAnnotations(dimAnnos, env, sym, false); scan(tree.elemtype); scan(tree.elems); } @@ -1137,19 +1117,13 @@ public void visitMethodDef(JCMethodDecl tree) { @Override public void visitVarDef(JCVariableDecl tree) { - JCTree prevDecl = deferDecl; - deferDecl = tree; - try { - if (sym != null && sym.kind == VAR) { - // Don't visit a parameter once when the sym is the method - // and once when the sym is the parameter. - scan(tree.mods); - scan(tree.vartype); - } - scan(tree.init); - } finally { - deferDecl = prevDecl; + if (sym != null && sym.kind == VAR) { + // Don't visit a parameter once when the sym is the method + // and once when the sym is the parameter. + scan(tree.mods); + scan(tree.vartype); } + scan(tree.init); } @Override diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java index 9c9672151c7da..e12da575036cb 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 @@ -41,6 +41,7 @@ import com.sun.source.util.SimpleTreeVisitor; import com.sun.tools.javac.code.*; import com.sun.tools.javac.code.Lint.LintCategory; +import com.sun.tools.javac.code.LintMapper; import com.sun.tools.javac.code.Scope.WriteableScope; import com.sun.tools.javac.code.Source.Feature; import com.sun.tools.javac.code.Symbol.*; @@ -99,6 +100,7 @@ public class Attr extends JCTree.Visitor { final Names names; final Log log; + final LintMapper lintMapper; final Symtab syms; final Resolve rs; final Operators operators; @@ -117,7 +119,6 @@ public class Attr extends JCTree.Visitor { final Preview preview; final JCDiagnostic.Factory diags; final TypeAnnotations typeAnnotations; - final DeferredLintHandler deferredLintHandler; final TypeEnvs typeEnvs; final Dependencies dependencies; final Annotate annotate; @@ -138,6 +139,7 @@ protected Attr(Context context) { names = Names.instance(context); log = Log.instance(context); + lintMapper = LintMapper.instance(context); syms = Symtab.instance(context); rs = Resolve.instance(context); operators = Operators.instance(context); @@ -157,7 +159,6 @@ protected Attr(Context context) { diags = JCDiagnostic.Factory.instance(context); annotate = Annotate.instance(context); typeAnnotations = TypeAnnotations.instance(context); - deferredLintHandler = DeferredLintHandler.instance(context); typeEnvs = TypeEnvs.instance(context); dependencies = Dependencies.instance(context); argumentAttr = ArgumentAttr.instance(context); @@ -854,7 +855,6 @@ public Object attribLazyConstantValue(Env env, Env enclosingEnv, JCVariableDecl variable, Type type) { - deferredLintHandler.push(variable); final JavaFileObject prevSource = log.useSource(env.toplevel.sourcefile); try { doQueueScanTreeAndTypeAnnotateForVarInit(variable, enclosingEnv); @@ -870,7 +870,6 @@ public Object attribLazyConstantValue(Env env, } } finally { log.useSource(prevSource); - deferredLintHandler.pop(); } } @@ -999,7 +998,6 @@ public void visitMethodDef(JCMethodDecl tree) { Assert.check(!env.info.ctorPrologue); MethodSymbol prevMethod = chk.setMethod(m); try { - deferredLintHandler.flush(tree, lint); chk.checkDeprecatedAnnotation(tree.pos(), m); @@ -1234,7 +1232,7 @@ public void visitMethodDef(JCMethodDecl tree) { } // Attribute all type annotations in the body - annotate.queueScanTreeAndTypeAnnotate(tree.body, localEnv, m, null); + annotate.queueScanTreeAndTypeAnnotate(tree.body, localEnv, m); annotate.flush(); // Start of constructor prologue @@ -1298,7 +1296,6 @@ public void visitVarDef(JCVariableDecl tree) { try { v.getConstValue(); // ensure compile-time constant initializer is evaluated - deferredLintHandler.flush(tree, lint); chk.checkDeprecatedAnnotation(tree.pos(), v); if (tree.init != null) { @@ -1343,7 +1340,7 @@ private void doQueueScanTreeAndTypeAnnotateForVarInit(JCVariableDecl tree, Env env, Type resou if (close.kind == MTH && close.overrides(syms.autoCloseableClose, resource.tsym, types, true) && chk.isHandled(syms.interruptedExceptionType, types.memberType(resource, close).getThrownTypes())) { - env.info.lint.logIfEnabled(pos, LintWarnings.TryResourceThrowsInterruptedExc(resource)); + log.warning(pos, LintWarnings.TryResourceThrowsInterruptedExc(resource)); } } } @@ -4229,9 +4226,9 @@ public void visitBindingPattern(JCBindingPattern tree) { setSyntheticVariableType(tree.var, type == Type.noType ? syms.errType : type); } - annotate.annotateLater(tree.var.mods.annotations, env, v, tree.var); + annotate.annotateLater(tree.var.mods.annotations, env, v); if (!tree.var.isImplicitlyTyped()) { - annotate.queueScanTreeAndTypeAnnotate(tree.var.vartype, env, v, tree.var); + annotate.queueScanTreeAndTypeAnnotate(tree.var.vartype, env, v); } annotate.flush(); result = tree.type; @@ -4469,7 +4466,7 @@ public void visitSelect(JCFieldAccess tree) { sym.kind == MTH && sym.name.equals(names.close) && sym.overrides(syms.autoCloseableClose, sitesym.type.tsym, types, true)) { - env.info.lint.logIfEnabled(tree, LintWarnings.TryExplicitCloseCall); + log.warning(tree, LintWarnings.TryExplicitCloseCall); } // Disallow selecting a type from an expression @@ -4496,9 +4493,9 @@ public void visitSelect(JCFieldAccess tree) { // If the qualified item is not a type and the selected item is static, report // a warning. Make allowance for the class of an array type e.g. Object[].class) if (!sym.owner.isAnonymous()) { - chk.lint.logIfEnabled(tree, LintWarnings.StaticNotQualifiedByType(sym.kind.kindName(), sym.owner)); + log.warning(tree, LintWarnings.StaticNotQualifiedByType(sym.kind.kindName(), sym.owner)); } else { - chk.lint.logIfEnabled(tree, LintWarnings.StaticNotQualifiedByType2(sym.kind.kindName())); + log.warning(tree, LintWarnings.StaticNotQualifiedByType2(sym.kind.kindName())); } } @@ -5303,6 +5300,9 @@ public void attrib(Env env) { } annotate.flush(); + + // Now that this tree is attributed, we can calculate the Lint configuration everywhere within it + lintMapper.calculateLints(env.toplevel.sourcefile, env.tree, env.toplevel.endPositions); } public void attribPackage(DiagnosticPosition pos, PackageSymbol p) { @@ -5345,7 +5345,6 @@ private void attribWithLint(TypeSymbol sym, Consumer> attrib) { JavaFileObject prev = log.useSource(env.toplevel.sourcefile); try { - deferredLintHandler.flush(env.tree, lint); attrib.accept(env); } finally { log.useSource(prev); @@ -5529,7 +5528,6 @@ void attribClass(ClassSymbol c) throws CompletionFailure { } } - deferredLintHandler.flush(env.tree, env.info.lint); env.info.returnResult = null; // java.lang.Enum may not be subclassed by a non-enum if (st.tsym == syms.enumSym && @@ -5575,11 +5573,9 @@ public void visitModuleDef(JCModuleDecl tree) { ModuleSymbol msym = tree.sym; Lint lint = env.outer.info.lint = env.outer.info.lint.augment(msym); Lint prevLint = chk.setLint(lint); - chk.checkModuleName(tree); - chk.checkDeprecatedAnnotation(tree, msym); - try { - deferredLintHandler.flush(tree, lint); + chk.checkModuleName(tree); + chk.checkDeprecatedAnnotation(tree, msym); } finally { chk.setLint(prevLint); } @@ -5688,7 +5684,7 @@ private void attribClassBody(Env env, ClassSymbol c) { // Check for proper use of serialVersionUID and other // serialization-related fields and methods - if (env.info.lint.isEnabled(LintCategory.SERIAL) + if (env.info.lint.isActive(LintCategory.SERIAL) && rs.isSerializable(c.type) && !c.isAnonymous()) { chk.checkSerialStructure(tree, c); 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 89ab5b13dd145..490f17fb6c7f6 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 @@ -164,20 +164,6 @@ protected Check(Context context) { profile = Profile.instance(context); preview = Preview.instance(context); - boolean verboseDeprecated = lint.isEnabled(LintCategory.DEPRECATION); - boolean verboseRemoval = lint.isEnabled(LintCategory.REMOVAL); - boolean verboseUnchecked = lint.isEnabled(LintCategory.UNCHECKED); - boolean enforceMandatoryWarnings = true; - - deprecationHandler = new MandatoryWarningHandler(log, null, verboseDeprecated, - enforceMandatoryWarnings, LintCategory.DEPRECATION, "deprecated"); - removalHandler = new MandatoryWarningHandler(log, null, verboseRemoval, - enforceMandatoryWarnings, LintCategory.REMOVAL); - uncheckedHandler = new MandatoryWarningHandler(log, null, verboseUnchecked, - enforceMandatoryWarnings, LintCategory.UNCHECKED); - - deferredLintHandler = DeferredLintHandler.instance(context); - allowModules = Feature.MODULES.allowedInSource(source); allowRecords = Feature.RECORDS.allowedInSource(source); allowSealed = Feature.SEALED_CLASSES.allowedInSource(source); @@ -192,22 +178,6 @@ protected Check(Context context) { */ private Map,ClassSymbol> compiled = new HashMap<>(); - /** A handler for messages about deprecated usage. - */ - private MandatoryWarningHandler deprecationHandler; - - /** A handler for messages about deprecated-for-removal usage. - */ - private MandatoryWarningHandler removalHandler; - - /** A handler for messages about unchecked or unsafe usage. - */ - private MandatoryWarningHandler uncheckedHandler; - - /** A handler for deferred lint warnings. - */ - private DeferredLintHandler deferredLintHandler; - /** Are modules allowed */ private final boolean allowModules; @@ -253,21 +223,15 @@ MethodSymbol setMethod(MethodSymbol newMethod) { * @param sym The deprecated symbol. */ void warnDeprecated(DiagnosticPosition pos, Symbol sym) { - if (sym.isDeprecatedForRemoval()) { - if (!lint.isSuppressed(LintCategory.REMOVAL)) { - if (sym.kind == MDL) { - removalHandler.report(pos, LintWarnings.HasBeenDeprecatedForRemovalModule(sym)); - } else { - removalHandler.report(pos, LintWarnings.HasBeenDeprecatedForRemoval(sym, sym.location())); - } - } - } else if (!lint.isSuppressed(LintCategory.DEPRECATION)) { - if (sym.kind == MDL) { - deprecationHandler.report(pos, LintWarnings.HasBeenDeprecatedModule(sym)); - } else { - deprecationHandler.report(pos, LintWarnings.HasBeenDeprecated(sym, sym.location())); - } - } + Assert.check(!importSuppression); + LintWarning warningKey = sym.isDeprecatedForRemoval() ? + (sym.kind == MDL ? + LintWarnings.HasBeenDeprecatedForRemovalModule(sym) : + LintWarnings.HasBeenDeprecatedForRemoval(sym, sym.location())) : + (sym.kind == MDL ? + LintWarnings.HasBeenDeprecatedModule(sym) : + LintWarnings.HasBeenDeprecated(sym, sym.location())); + log.mandatoryWarning(pos, warningKey, DiagnosticFlag.AGGREGATE); } /** Log a preview warning. @@ -275,16 +239,9 @@ void warnDeprecated(DiagnosticPosition pos, Symbol sym) { * @param msg A Warning describing the problem. */ public void warnPreviewAPI(DiagnosticPosition pos, LintWarning warnKey) { - if (!importSuppression && !lint.isSuppressed(LintCategory.PREVIEW)) - preview.reportPreviewWarning(pos, warnKey); - } - - /** Log a preview warning. - * @param pos Position to be used for error reporting. - * @param msg A Warning describing the problem. - */ - public void warnRestrictedAPI(DiagnosticPosition pos, Symbol sym) { - lint.logIfEnabled(pos, LintWarnings.RestrictedMethod(sym.enclClass(), sym)); + if (!importSuppression) { + log.mandatoryWarning(pos, warnKey, DiagnosticFlag.AGGREGATE); + } } /** Warn about unchecked operation. @@ -292,26 +249,15 @@ public void warnRestrictedAPI(DiagnosticPosition pos, Symbol sym) { * @param msg A string describing the problem. */ public void warnUnchecked(DiagnosticPosition pos, LintWarning warnKey) { - if (!lint.isSuppressed(LintCategory.UNCHECKED)) - uncheckedHandler.report(pos, warnKey); + log.mandatoryWarning(pos, warnKey, DiagnosticFlag.AGGREGATE); } - /** - * Report any deferred diagnostics. - */ - public void reportDeferredDiagnostics() { - deprecationHandler.reportDeferredDiagnostic(); - removalHandler.reportDeferredDiagnostic(); - uncheckedHandler.reportDeferredDiagnostic(); - } - - /** Report a failure to complete a class. * @param pos Position to be used for error reporting. * @param ex The failure to report. */ public Type completionError(DiagnosticPosition pos, CompletionFailure ex) { - log.error(JCDiagnostic.DiagnosticFlag.NON_DEFERRABLE, pos, Errors.CantAccess(ex.sym, ex.getDetailValue())); + log.error(DiagnosticFlag.NON_DEFERRABLE, pos, Errors.CantAccess(ex.sym, ex.getDetailValue())); return syms.errType; } @@ -474,12 +420,6 @@ public void newRound() { localClassNameIndexes.clear(); } - public void clear() { - deprecationHandler.clear(); - removalHandler.clear(); - uncheckedHandler.clear(); - } - public void putCompiled(ClassSymbol csym) { compiled.put(Pair.of(csym.packge().modle, csym.flatname), csym); } @@ -645,9 +585,7 @@ public void checkRedundantCast(Env env, final JCTypeCast tree) { && types.isSameType(tree.expr.type, tree.clazz.type) && !(ignoreAnnotatedCasts && TreeInfo.containsTypeAnnotation(tree.clazz)) && !is292targetTypeCast(tree)) { - deferredLintHandler.report(_l -> { - lint.logIfEnabled(tree.pos(), LintWarnings.RedundantCast(tree.clazz.type)); - }); + log.warning(tree.pos(), LintWarnings.RedundantCast(tree.clazz.type)); } } //where @@ -951,7 +889,7 @@ void checkVarargsMethodDecl(Env env, JCMethodDecl tree) { } } else if (hasTrustMeAnno && varargElemType != null && types.isReifiable(varargElemType)) { - lint.logIfEnabled(tree, LintWarnings.VarargsRedundantTrustmeAnno( + log.warning(tree.pos(), LintWarnings.VarargsRedundantTrustmeAnno( syms.trustMeType.tsym, diags.fragment(Fragments.VarargsTrustmeOnReifiableVarargs(varargElemType)))); } @@ -1210,7 +1148,7 @@ else if ((sym.owner.flags_field & INTERFACE) != 0) mask = MethodFlags; } if ((flags & STRICTFP) != 0) { - warnOnExplicitStrictfp(tree); + log.warning(tree.pos(), LintWarnings.Strictfp); } // Imply STRICTFP if owner has STRICTFP set. if (((flags|implicit) & Flags.ABSTRACT) == 0 || @@ -1254,7 +1192,7 @@ else if ((sym.owner.flags_field & INTERFACE) != 0) implicit |= FINAL; } if ((flags & STRICTFP) != 0) { - warnOnExplicitStrictfp(tree); + log.warning(tree.pos(), LintWarnings.Strictfp); } // Imply STRICTFP if owner has STRICTFP set. implicit |= sym.owner.flags_field & STRICTFP; @@ -1318,16 +1256,6 @@ && checkDisjoint(pos, flags, return flags & (mask | ~ExtendedStandardFlags) | implicit; } - private void warnOnExplicitStrictfp(JCTree tree) { - deferredLintHandler.push(tree); - try { - deferredLintHandler.report(_ -> lint.logIfEnabled(tree.pos(), LintWarnings.Strictfp)); - } finally { - deferredLintHandler.pop(); - } - } - - /** Determine if this enum should be implicitly final. * * If the enum has no specialized enum constants, it is final. @@ -1540,7 +1468,7 @@ void checkRaw(JCTree tree, Env env) { !TreeInfo.isDiamond(tree) && !withinAnonConstr(env) && tree.type.isRaw()) { - lint.logIfEnabled(tree.pos(), LintWarnings.RawClassUse(tree.type, tree.type.tsym.type)); + log.warning(tree.pos(), LintWarnings.RawClassUse(tree.type, tree.type.tsym.type)); } } //where @@ -1864,7 +1792,7 @@ else if (unhandledUnerased.nonEmpty()) { // Optional warning if varargs don't agree if ((((m.flags() ^ other.flags()) & Flags.VARARGS) != 0)) { - lint.logIfEnabled(TreeInfo.diagnosticPositionFor(m, tree), + log.warning(TreeInfo.diagnosticPositionFor(m, tree), ((m.flags() & Flags.VARARGS) != 0) ? LintWarnings.OverrideVarargsMissing(varargsOverrides(m, other)) : LintWarnings.OverrideVarargsExtra(varargsOverrides(m, other))); @@ -1878,12 +1806,7 @@ else if (unhandledUnerased.nonEmpty()) { // Warn if a deprecated method overridden by a non-deprecated one. if (!isDeprecatedOverrideIgnorable(other, origin)) { - Lint prevLint = setLint(lint.augment(m)); - try { - checkDeprecated(() -> TreeInfo.diagnosticPositionFor(m, tree), m, other); - } finally { - setLint(prevLint); - } + checkDeprecated(() -> TreeInfo.diagnosticPositionFor(m, tree), m, other); } } // where @@ -2198,7 +2121,7 @@ public void checkClassOverrideEqualsAndHashIfNeeded(DiagnosticPosition pos, private void checkClassOverrideEqualsAndHash(DiagnosticPosition pos, ClassSymbol someClass) { - if (lint.isEnabled(LintCategory.OVERRIDES)) { + if (lint.isActive(LintCategory.OVERRIDES)) { MethodSymbol equalsAtObject = (MethodSymbol)syms.objectType .tsym.members().findFirst(names.equals); MethodSymbol hashCodeAtObject = (MethodSymbol)syms.objectType @@ -2250,7 +2173,7 @@ public void checkHasMain(DiagnosticPosition pos, ClassSymbol c) { public void checkModuleName (JCModuleDecl tree) { Name moduleName = tree.sym.name; Assert.checkNonNull(moduleName); - if (lint.isEnabled(LintCategory.MODULE)) { + if (lint.isActive(LintCategory.MODULE)) { JCExpression qualId = tree.qualId; while (qualId != null) { Name componentName; @@ -2721,7 +2644,7 @@ public boolean test(Symbol s) { void checkPotentiallyAmbiguousOverloads(JCClassDecl tree, Type site) { // Skip if warning not enabled - if (!lint.isEnabled(LintCategory.OVERLOADS)) + if (!lint.isActive(LintCategory.OVERLOADS)) return; // Gather all of site's methods, including overridden methods, grouped by name (except Object methods) @@ -2734,10 +2657,6 @@ void checkPotentiallyAmbiguousOverloads(JCClassDecl tree, Type site) { // Now remove overridden methods from each group, leaving only site's actual members methodGroups.forEach(list -> removePreempted(list, (m1, m2) -> m1.overrides(m2, site.tsym, types, false))); - // Allow site's own declared methods (only) to apply @SuppressWarnings("overloads") - methodGroups.forEach(list -> list.removeIf( - m -> m.owner == site.tsym && !lint.augment(m).isEnabled(LintCategory.OVERLOADS))); - // Warn about ambiguous overload method pairs for which site is responsible methodGroups.forEach(list -> compareAndRemove(list, (m1, m2) -> { @@ -2745,6 +2664,16 @@ void checkPotentiallyAmbiguousOverloads(JCClassDecl tree, Type site) { if (!potentiallyAmbiguousOverload(site, m1, m2) || !responsible.test(m1, m2)) return 0; + // Allow the site's own declared methods (only) to apply @SuppressWarnings("overloads"). + // Treat both methods equally so they "share" the validation of the warning suppression, + // but also verify an annotation actually exists on a method before doing that, because + // otherwise we could incorrectly validate an outer annotation. + Predicate methodSuppresses = m -> m.owner == site.tsym && + m.attribute(syms.suppressWarningsType.tsym) != null && + lint.augment(m).isSuppressed(LintCategory.OVERLOADS, true); + if (methodSuppresses.test(m1) | methodSuppresses.test(m2)) // use "|" to avoid an artificial preference + return FIRST | SECOND; + // Locate the warning at one of the methods, if possible DiagnosticPosition pos = m1.owner == site.tsym ? TreeInfo.diagnosticPositionFor(m1, tree) : @@ -2952,42 +2881,34 @@ boolean potentiallyAmbiguousOverload(Type site, MethodSymbol msym1, MethodSymbol // Apply special flag "-XDwarnOnAccessToMembers" which turns on just this particular warning for all types of access void checkAccessFromSerializableElement(final JCTree tree, boolean isLambda) { - final Lint prevLint = setLint(warnOnAnyAccessToMembers ? lint.enable(LintCategory.SERIAL) : lint); - try { - if (warnOnAnyAccessToMembers || isLambda) - checkAccessFromSerializableElementInner(tree, isLambda); - } finally { - setLint(prevLint); - } + if (warnOnAnyAccessToMembers || isLambda) + checkAccessFromSerializableElementInner(tree, isLambda); } private void checkAccessFromSerializableElementInner(final JCTree tree, boolean isLambda) { - if (lint.isEnabled(LintCategory.SERIAL)) { - Symbol sym = TreeInfo.symbol(tree); - if (!sym.kind.matches(KindSelector.VAL_MTH)) { - return; - } + Symbol sym = TreeInfo.symbol(tree); + if (!sym.kind.matches(KindSelector.VAL_MTH)) { + return; + } - if (sym.kind == VAR) { - if ((sym.flags() & PARAMETER) != 0 || - sym.isDirectlyOrIndirectlyLocal() || - sym.name == names._this || - sym.name == names._super) { - return; - } + if (sym.kind == VAR) { + if ((sym.flags() & PARAMETER) != 0 || + sym.isDirectlyOrIndirectlyLocal() || + sym.name == names._this || + sym.name == names._super) { + return; } + } - if (!types.isSubtype(sym.owner.type, syms.serializableType) && - isEffectivelyNonPublic(sym)) { - if (isLambda) { - if (belongsToRestrictedPackage(sym)) { - log.warning(tree.pos(), - LintWarnings.AccessToMemberFromSerializableLambda(sym)); - } - } else { - log.warning(tree.pos(), - LintWarnings.AccessToMemberFromSerializableElement(sym)); + if (!types.isSubtype(sym.owner.type, syms.serializableType) && isEffectivelyNonPublic(sym)) { + DiagnosticFlag[] flags = warnOnAnyAccessToMembers ? + new DiagnosticFlag[] { DiagnosticFlag.DEFAULT_ENABLED } : new DiagnosticFlag[0]; + if (isLambda) { + if (belongsToRestrictedPackage(sym)) { + log.warning(tree.pos(), LintWarnings.AccessToMemberFromSerializableLambda(sym), flags); } + } else { + log.warning(tree.pos(), LintWarnings.AccessToMemberFromSerializableElement(sym), flags); } } } @@ -3765,17 +3686,16 @@ boolean validateTargetAnnotationValue(JCAnnotation a) { } void checkDeprecatedAnnotation(DiagnosticPosition pos, Symbol s) { - if (lint.isEnabled(LintCategory.DEP_ANN) && s.isDeprecatableViaAnnotation() && + if (lint.isActive(LintCategory.DEP_ANN) && s.isDeprecatableViaAnnotation() && (s.flags() & DEPRECATED) != 0 && !syms.deprecatedType.isErroneous() && s.attribute(syms.deprecatedType.tsym) == null) { log.warning(pos, LintWarnings.MissingDeprecatedAnnotation); } // Note: @Deprecated has no effect on local variables, parameters and package decls. - if (lint.isEnabled(LintCategory.DEPRECATION) && !s.isDeprecatableViaAnnotation()) { + if (lint.isActive(LintCategory.DEPRECATION) && !s.isDeprecatableViaAnnotation()) { if (!syms.deprecatedType.isErroneous() && s.attribute(syms.deprecatedType.tsym) != null) { - log.warning(pos, - LintWarnings.DeprecatedAnnotationHasNoEffect(Kinds.kindName(s))); + log.warning(pos, LintWarnings.DeprecatedAnnotationHasNoEffect(Kinds.kindName(s))); } } } @@ -3789,15 +3709,13 @@ void checkDeprecated(Supplier pos, final Symbol other, final && (s.isDeprecatedForRemoval() || s.isDeprecated() && !other.isDeprecated()) && (s.outermostClass() != other.outermostClass() || s.outermostClass() == null) && s.kind != Kind.PCK) { - deferredLintHandler.report(_l -> warnDeprecated(pos.get(), s)); + warnDeprecated(pos.get(), s); } } void checkSunAPI(final DiagnosticPosition pos, final Symbol s) { if ((s.flags() & PROPRIETARY) != 0) { - deferredLintHandler.report(_l -> { - log.mandatoryWarning(pos, Warnings.SunProprietary(s)); - }); + log.mandatoryWarning(pos, Warnings.SunProprietary(s)); } } @@ -3854,7 +3772,7 @@ void checkPreview(DiagnosticPosition pos, Symbol other, Type site, Symbol s) { void checkRestricted(DiagnosticPosition pos, Symbol s) { if (s.kind == MTH && (s.flags() & RESTRICTED) != 0) { - deferredLintHandler.report(_l -> warnRestrictedAPI(pos, s)); + log.warning(pos, LintWarnings.RestrictedMethod(s.enclClass(), s)); } } @@ -4126,7 +4044,7 @@ void checkDivZero(final DiagnosticPosition pos, Symbol operator, Type operand) { int opc = ((OperatorSymbol)operator).opcode; if (opc == ByteCodes.idiv || opc == ByteCodes.imod || opc == ByteCodes.ldiv || opc == ByteCodes.lmod) { - deferredLintHandler.report(_ -> lint.logIfEnabled(pos, LintWarnings.DivZero)); + log.warning(pos, LintWarnings.DivZero); } } } @@ -4139,8 +4057,7 @@ void checkDivZero(final DiagnosticPosition pos, Symbol operator, Type operand) { */ void checkLossOfPrecision(final DiagnosticPosition pos, Type found, Type req) { if (found.isNumeric() && req.isNumeric() && !types.isAssignable(found, req)) { - deferredLintHandler.report(_ -> - lint.logIfEnabled(pos, LintWarnings.PossibleLossOfPrecision(found, req))); + log.warning(pos, LintWarnings.PossibleLossOfPrecision(found, req)); } } @@ -4149,7 +4066,7 @@ void checkLossOfPrecision(final DiagnosticPosition pos, Type found, Type req) { */ void checkEmptyIf(JCIf tree) { if (tree.thenpart.hasTag(SKIP) && tree.elsepart == null) { - lint.logIfEnabled(tree.thenpart.pos(), LintWarnings.EmptyIf); + log.warning(tree.thenpart.pos(), LintWarnings.EmptyIf); } } @@ -4296,8 +4213,7 @@ void checkForBadAuxiliaryClassAccess(DiagnosticPosition pos, Env en rs.isAccessible(env, c) && !fileManager.isSameFile(c.sourcefile, env.toplevel.sourcefile)) { - lint.logIfEnabled(pos, - LintWarnings.AuxiliaryClassAccessedFromOutsideOfItsSourceFile(c, c.sourcefile)); + log.warning(pos, LintWarnings.AuxiliaryClassAccessedFromOutsideOfItsSourceFile(c, c.sourcefile)); } } @@ -4305,7 +4221,7 @@ void checkForBadAuxiliaryClassAccess(DiagnosticPosition pos, Env en * Check for a default constructor in an exported package. */ void checkDefaultConstructor(ClassSymbol c, DiagnosticPosition pos) { - if (lint.isEnabled(LintCategory.MISSING_EXPLICIT_CTOR) && + if (lint.isActive(LintCategory.MISSING_EXPLICIT_CTOR) && ((c.flags() & (ENUM | RECORD)) == 0) && !c.isAnonymous() && ((c.flags() & (PUBLIC | PROTECTED)) != 0) && @@ -4339,8 +4255,7 @@ void checkDefaultConstructor(ClassSymbol c, DiagnosticPosition pos) { // Warning may be suppressed by // annotations; check again for being // enabled in the deferred context. - deferredLintHandler.report(_ -> - lint.logIfEnabled(pos, LintWarnings.MissingExplicitCtor(c, pkg, modle))); + log.warning(pos, LintWarnings.MissingExplicitCtor(c, pkg, modle)); } else { return; } @@ -4376,7 +4291,7 @@ public void warn(LintCategory lint) { method.attribute(syms.trustMeType.tsym) != null && isTrustMeAllowedOnMethod(method) && !types.isReifiable(method.type.getParameterTypes().last())) { - Check.this.lint.logIfEnabled(pos(), LintWarnings.VarargsUnsafeUseVarargsParam(method.params.last())); + log.warning(pos(), LintWarnings.VarargsUnsafeUseVarargsParam(method.params.last())); } break; default: @@ -4524,7 +4439,7 @@ public void visitMethodDef(JCMethodDecl tree) { Lint prevLint = lint; try { lint = lint.augment(tree.sym); - if (lint.isEnabled(LintCategory.EXPORTS)) { + if (lint.isActive(LintCategory.EXPORTS)) { super.visitMethodDef(tree); } } finally { @@ -4538,7 +4453,7 @@ public void visitVarDef(JCVariableDecl tree) { Lint prevLint = lint; try { lint = lint.augment(tree.sym); - if (lint.isEnabled(LintCategory.EXPORTS)) { + if (lint.isActive(LintCategory.EXPORTS)) { scan(tree.mods); scan(tree.vartype); } @@ -4557,7 +4472,7 @@ public void visitClassDef(JCClassDecl tree) { Lint prevLint = lint; try { lint = lint.augment(tree.sym); - if (lint.isEnabled(LintCategory.EXPORTS)) { + if (lint.isActive(LintCategory.EXPORTS)) { scan(tree.mods); scan(tree.typarams); try { @@ -4674,28 +4589,24 @@ private void checkVisible(DiagnosticPosition pos, Symbol what, PackageSymbol inP void checkModuleExists(final DiagnosticPosition pos, ModuleSymbol msym) { if (msym.kind != MDL) { - deferredLintHandler.report(_ -> - lint.logIfEnabled(pos, LintWarnings.ModuleNotFound(msym))); + log.warning(pos, LintWarnings.ModuleNotFound(msym)); } } void checkPackageExistsForOpens(final DiagnosticPosition pos, PackageSymbol packge) { if (packge.members().isEmpty() && ((packge.flags() & Flags.HAS_RESOURCE) == 0)) { - deferredLintHandler.report(_ -> - lint.logIfEnabled(pos, LintWarnings.PackageEmptyOrNotFound(packge))); + log.warning(pos, LintWarnings.PackageEmptyOrNotFound(packge)); } } void checkModuleRequires(final DiagnosticPosition pos, final RequiresDirective rd) { if ((rd.module.flags() & Flags.AUTOMATIC_MODULE) != 0) { - deferredLintHandler.report(_ -> { - if (rd.isTransitive() && lint.isEnabled(LintCategory.REQUIRES_TRANSITIVE_AUTOMATIC)) { - log.warning(pos, LintWarnings.RequiresTransitiveAutomatic); - } else { - lint.logIfEnabled(pos, LintWarnings.RequiresAutomatic); - } - }); + if (rd.isTransitive()) { // see comment in Log.applyLint() for special logic that applies + log.warning(pos, LintWarnings.RequiresTransitiveAutomatic); + } else { + log.warning(pos, LintWarnings.RequiresAutomatic); + } } } @@ -4960,6 +4871,14 @@ private class SerialTypeVisitor extends ElementKindVisitor14 Lint lint; + // Because runUnderLint() uses "augment" to customize the current Lint instance, + // we must check if the warning category is enabled manually before logging a warning. + private void warning(DiagnosticPosition pos, LintWarning warningKey) { + if (lint.isEnabled(warningKey.getLintCategory(), true)) { + log.warning(pos, warningKey); + } + } + @Override public Void defaultAction(Element e, JCClassDecl p) { throw new IllegalArgumentException(Objects.requireNonNullElse(e.toString(), "")); @@ -4991,7 +4910,7 @@ public Void visitTypeAsClass(TypeElement e, } if (svuidSym == null) { - log.warning(p.pos(), LintWarnings.MissingSVUID(c)); + warning(p.pos(), LintWarnings.MissingSVUID(c)); } // Check for serialPersistentFields to gate checks for @@ -5018,9 +4937,8 @@ public Void visitTypeAsClass(TypeElement e, // Note per JLS arrays are // serializable even if the // component type is not. - log.warning( - TreeInfo.diagnosticPositionFor(enclosed, tree), - LintWarnings.NonSerializableInstanceField); + warning(TreeInfo.diagnosticPositionFor(enclosed, tree), + LintWarnings.NonSerializableInstanceField); } else if (varType.hasTag(ARRAY)) { ArrayType arrayType = (ArrayType)varType; Type elementType = arrayType.elemtype; @@ -5029,9 +4947,8 @@ public Void visitTypeAsClass(TypeElement e, elementType = arrayType.elemtype; } if (!canBeSerialized(elementType)) { - log.warning( - TreeInfo.diagnosticPositionFor(enclosed, tree), - LintWarnings.NonSerializableInstanceFieldArray(elementType)); + warning(TreeInfo.diagnosticPositionFor(enclosed, tree), + LintWarnings.NonSerializableInstanceFieldArray(elementType)); } } } @@ -5114,8 +5031,7 @@ private void checkCtorAccess(JCClassDecl tree, ClassSymbol c) { } } } - log.warning(tree.pos(), - LintWarnings.ExternalizableMissingPublicNoArgCtor); + warning(tree.pos(), LintWarnings.ExternalizableMissingPublicNoArgCtor); } else { // Approximate access to the no-arg constructor up in // the superclass chain by checking that the @@ -5142,8 +5058,8 @@ private void checkCtorAccess(JCClassDecl tree, ClassSymbol c) { // Handle nested classes and implicit this$0 (supertype.getNestingKind() == NestingKind.MEMBER && ((supertype.flags() & STATIC) == 0))) - log.warning(tree.pos(), - LintWarnings.SerializableMissingAccessNoArgCtor(supertype.getQualifiedName())); + warning(tree.pos(), + LintWarnings.SerializableMissingAccessNoArgCtor(supertype.getQualifiedName())); } } } @@ -5161,43 +5077,31 @@ private void checkSerialVersionUID(JCClassDecl tree, Element e, VarSymbol svuid) // fields. if ((svuid.flags() & (STATIC | FINAL)) != (STATIC | FINAL)) { - log.warning( - TreeInfo.diagnosticPositionFor(svuid, tree), - LintWarnings.ImproperSVUID((Symbol)e)); + warning(TreeInfo.diagnosticPositionFor(svuid, tree), LintWarnings.ImproperSVUID((Symbol)e)); } // check svuid has type long if (!svuid.type.hasTag(LONG)) { - log.warning( - TreeInfo.diagnosticPositionFor(svuid, tree), - LintWarnings.LongSVUID((Symbol)e)); + warning(TreeInfo.diagnosticPositionFor(svuid, tree), LintWarnings.LongSVUID((Symbol)e)); } if (svuid.getConstValue() == null) - log.warning( - TreeInfo.diagnosticPositionFor(svuid, tree), - LintWarnings.ConstantSVUID((Symbol)e)); + warning(TreeInfo.diagnosticPositionFor(svuid, tree), LintWarnings.ConstantSVUID((Symbol)e)); } private void checkSerialPersistentFields(JCClassDecl tree, Element e, VarSymbol spf) { // To be effective, serialPersisentFields must be private, static, and final. if ((spf.flags() & (PRIVATE | STATIC | FINAL)) != (PRIVATE | STATIC | FINAL)) { - log.warning( - TreeInfo.diagnosticPositionFor(spf, tree), - LintWarnings.ImproperSPF); + warning(TreeInfo.diagnosticPositionFor(spf, tree), LintWarnings.ImproperSPF); } if (!types.isSameType(spf.type, OSF_TYPE)) { - log.warning( - TreeInfo.diagnosticPositionFor(spf, tree), - LintWarnings.OSFArraySPF); + warning(TreeInfo.diagnosticPositionFor(spf, tree), LintWarnings.OSFArraySPF); } if (isExternalizable((Type)(e.asType()))) { - log.warning( - TreeInfo.diagnosticPositionFor(spf, tree), - LintWarnings.IneffectualSerialFieldExternalizable); + warning(TreeInfo.diagnosticPositionFor(spf, tree), LintWarnings.IneffectualSerialFieldExternalizable); } // Warn if serialPersistentFields is initialized to a @@ -5207,8 +5111,7 @@ private void checkSerialPersistentFields(JCClassDecl tree, Element e, VarSymbol JCVariableDecl variableDef = (JCVariableDecl) spfDecl; JCExpression initExpr = variableDef.init; if (initExpr != null && TreeInfo.isNull(initExpr)) { - log.warning(initExpr.pos(), - LintWarnings.SPFNullInit); + warning(initExpr.pos(), LintWarnings.SPFNullInit); } } } @@ -5286,24 +5189,19 @@ private void checkReadExternalRecord(JCClassDecl tree, Element e, MethodSymbol m private void checkExternMethodRecord(JCClassDecl tree, Element e, MethodSymbol method, Type argType, boolean isExtern) { if (isExtern && isExternMethod(tree, e, method, argType)) { - log.warning( - TreeInfo.diagnosticPositionFor(method, tree), - LintWarnings.IneffectualExternalizableMethodRecord(method.getSimpleName().toString())); + warning(TreeInfo.diagnosticPositionFor(method, tree), + LintWarnings.IneffectualExternalizableMethodRecord(method.getSimpleName().toString())); } } void checkPrivateNonStaticMethod(JCClassDecl tree, MethodSymbol method) { var flags = method.flags(); if ((flags & PRIVATE) == 0) { - log.warning( - TreeInfo.diagnosticPositionFor(method, tree), - LintWarnings.SerialMethodNotPrivate(method.getSimpleName())); + warning(TreeInfo.diagnosticPositionFor(method, tree), LintWarnings.SerialMethodNotPrivate(method.getSimpleName())); } if ((flags & STATIC) != 0) { - log.warning( - TreeInfo.diagnosticPositionFor(method, tree), - LintWarnings.SerialMethodStatic(method.getSimpleName())); + warning(TreeInfo.diagnosticPositionFor(method, tree), LintWarnings.SerialMethodStatic(method.getSimpleName())); } } @@ -5326,18 +5224,14 @@ public Void visitTypeAsEnum(TypeElement e, case FIELD -> { var field = (VarSymbol)enclosed; if (serialFieldNames.contains(name)) { - log.warning( - TreeInfo.diagnosticPositionFor(field, tree), - LintWarnings.IneffectualSerialFieldEnum(name)); + warning(TreeInfo.diagnosticPositionFor(field, tree), LintWarnings.IneffectualSerialFieldEnum(name)); } } case METHOD -> { var method = (MethodSymbol)enclosed; if (serialMethodNames.contains(name)) { - log.warning( - TreeInfo.diagnosticPositionFor(method, tree), - LintWarnings.IneffectualSerialMethodEnum(name)); + warning(TreeInfo.diagnosticPositionFor(method, tree), LintWarnings.IneffectualSerialMethodEnum(name)); } if (isExtern) { @@ -5375,9 +5269,8 @@ private void checkReadExternalEnum(JCClassDecl tree, Element e, MethodSymbol met private void checkExternMethodEnum(JCClassDecl tree, Element e, MethodSymbol method, Type argType) { if (isExternMethod(tree, e, method, argType)) { - log.warning( - TreeInfo.diagnosticPositionFor(method, tree), - LintWarnings.IneffectualExternMethodEnum(method.getSimpleName().toString())); + warning(TreeInfo.diagnosticPositionFor(method, tree), + LintWarnings.IneffectualExternMethodEnum(method.getSimpleName().toString())); } } @@ -5407,9 +5300,7 @@ public Void visitTypeAsInterface(TypeElement e, name = field.getSimpleName().toString(); switch(name) { case "serialPersistentFields" -> { - log.warning( - TreeInfo.diagnosticPositionFor(field, tree), - LintWarnings.IneffectualSerialFieldInterface); + warning(TreeInfo.diagnosticPositionFor(field, tree), LintWarnings.IneffectualSerialFieldInterface); } case "serialVersionUID" -> { @@ -5447,9 +5338,7 @@ private void checkPrivateMethod(JCClassDecl tree, Element e, MethodSymbol method) { if ((method.flags() & PRIVATE) == 0) { - log.warning( - TreeInfo.diagnosticPositionFor(method, tree), - LintWarnings.NonPrivateMethodWeakerAccess); + warning(TreeInfo.diagnosticPositionFor(method, tree), LintWarnings.NonPrivateMethodWeakerAccess); } } @@ -5457,9 +5346,7 @@ private void checkDefaultIneffective(JCClassDecl tree, Element e, MethodSymbol method) { if ((method.flags() & DEFAULT) == DEFAULT) { - log.warning( - TreeInfo.diagnosticPositionFor(method, tree), - LintWarnings.DefaultIneffective); + warning(TreeInfo.diagnosticPositionFor(method, tree), LintWarnings.DefaultIneffective); } } @@ -5504,9 +5391,7 @@ public Void visitTypeAsRecord(TypeElement e, var field = (VarSymbol)enclosed; switch(name) { case "serialPersistentFields" -> { - log.warning( - TreeInfo.diagnosticPositionFor(field, tree), - LintWarnings.IneffectualSerialFieldRecord); + warning(TreeInfo.diagnosticPositionFor(field, tree), LintWarnings.IneffectualSerialFieldRecord); } case "serialVersionUID" -> { @@ -5528,9 +5413,8 @@ public Void visitTypeAsRecord(TypeElement e, default -> { if (serialMethodNames.contains(name)) { - log.warning( - TreeInfo.diagnosticPositionFor(method, tree), - LintWarnings.IneffectualSerialMethodRecord(name)); + warning(TreeInfo.diagnosticPositionFor(method, tree), + LintWarnings.IneffectualSerialMethodRecord(name)); } }} }}}); @@ -5542,9 +5426,8 @@ void checkConcreteInstanceMethod(JCClassDecl tree, Element enclosing, MethodSymbol method) { if ((method.flags() & (STATIC | ABSTRACT)) != 0) { - log.warning( - TreeInfo.diagnosticPositionFor(method, tree), - LintWarnings.SerialConcreteInstanceMethod(method.getSimpleName())); + warning(TreeInfo.diagnosticPositionFor(method, tree), + LintWarnings.SerialConcreteInstanceMethod(method.getSimpleName())); } } @@ -5559,9 +5442,8 @@ private void checkReturnType(JCClassDecl tree, // checking. Type rtype = method.getReturnType(); if (!types.isSameType(expectedReturnType, rtype)) { - log.warning( - TreeInfo.diagnosticPositionFor(method, tree), - LintWarnings.SerialMethodUnexpectedReturnType(method.getSimpleName(), + warning(TreeInfo.diagnosticPositionFor(method, tree), + LintWarnings.SerialMethodUnexpectedReturnType(method.getSimpleName(), rtype, expectedReturnType)); } } @@ -5575,17 +5457,15 @@ private void checkOneArg(JCClassDecl tree, var parameters= method.getParameters(); if (parameters.size() != 1) { - log.warning( - TreeInfo.diagnosticPositionFor(method, tree), - LintWarnings.SerialMethodOneArg(method.getSimpleName(), parameters.size())); + warning(TreeInfo.diagnosticPositionFor(method, tree), + LintWarnings.SerialMethodOneArg(method.getSimpleName(), parameters.size())); return; } Type parameterType = parameters.get(0).asType(); if (!types.isSameType(parameterType, expectedType)) { - log.warning( - TreeInfo.diagnosticPositionFor(method, tree), - LintWarnings.SerialMethodParameterType(method.getSimpleName(), + warning(TreeInfo.diagnosticPositionFor(method, tree), + LintWarnings.SerialMethodParameterType(method.getSimpleName(), expectedType, parameterType)); } @@ -5604,18 +5484,16 @@ private boolean hasExactlyOneArgWithType(JCClassDecl tree, private void checkNoArgs(JCClassDecl tree, Element enclosing, MethodSymbol method) { var parameters = method.getParameters(); if (!parameters.isEmpty()) { - log.warning( - TreeInfo.diagnosticPositionFor(parameters.get(0), tree), - LintWarnings.SerialMethodNoArgs(method.getSimpleName())); + warning(TreeInfo.diagnosticPositionFor(parameters.get(0), tree), + LintWarnings.SerialMethodNoArgs(method.getSimpleName())); } } private void checkExternalizable(JCClassDecl tree, Element enclosing, MethodSymbol method) { // If the enclosing class is externalizable, warn for the method if (isExternalizable((Type)enclosing.asType())) { - log.warning( - TreeInfo.diagnosticPositionFor(method, tree), - LintWarnings.IneffectualSerialMethodExternalizable(method.getSimpleName())); + warning(TreeInfo.diagnosticPositionFor(method, tree), + LintWarnings.IneffectualSerialMethodExternalizable(method.getSimpleName())); } return; } @@ -5642,10 +5520,8 @@ private void checkExceptions(JCClassDecl tree, } } if (!declared) { - log.warning( - TreeInfo.diagnosticPositionFor(method, tree), - LintWarnings.SerialMethodUnexpectedException(method.getSimpleName(), - thrownType)); + warning(TreeInfo.diagnosticPositionFor(method, tree), + LintWarnings.SerialMethodUnexpectedException(method.getSimpleName(), thrownType)); } } } @@ -5657,7 +5533,7 @@ private Void runUnderLint(E symbol, JCClassDecl p, BiConsume try { lint = lint.augment((Symbol) symbol); - if (lint.isEnabled(LintCategory.SERIAL)) { + if (lint.isActive(LintCategory.SERIAL)) { task.accept(symbol, p); } @@ -5730,14 +5606,14 @@ void checkRequiresIdentity(JCTree tree, Lint lint) { VarSymbol lastParam = ms.params.head; for (VarSymbol param: ms.params) { if ((param.flags_field & REQUIRES_IDENTITY) != 0 && argExps.head.type.isValueBased()) { - lint.logIfEnabled(argExps.head.pos(), LintWarnings.AttemptToUseValueBasedWhereIdentityExpected); + log.warning(argExps.head.pos(), LintWarnings.AttemptToUseValueBasedWhereIdentityExpected); } lastParam = param; argExps = argExps.tail; } while (argExps != null && !argExps.isEmpty() && lastParam != null) { if ((lastParam.flags_field & REQUIRES_IDENTITY) != 0 && argExps.head.type.isValueBased()) { - lint.logIfEnabled(argExps.head.pos(), LintWarnings.AttemptToUseValueBasedWhereIdentityExpected); + log.warning(argExps.head.pos(), LintWarnings.AttemptToUseValueBasedWhereIdentityExpected); } argExps = argExps.tail; } @@ -5759,12 +5635,12 @@ void checkRequiresIdentity(JCTree tree, Lint lint) { private boolean checkIfIdentityIsExpected(DiagnosticPosition pos, Type t, Lint lint) { if (t != null && lint != null && - lint.isEnabled(LintCategory.IDENTITY)) { + lint.isActive(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); + log.warning(pos, LintWarnings.AttemptToUseValueBasedWhereIdentityExpected); return true; } } @@ -5841,7 +5717,7 @@ private void checkIfTypeParamsRequiresIdentity(SymbolMetadata sm, .filter(ta -> isRequiresIdentityAnnotation(ta.type.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(), + .forEach(ta -> log.warning(typeParamTrees.get(ta.position.parameter_index).pos(), CompilerProperties.LintWarnings.AttemptToUseValueBasedWhereIdentityExpected)); } } diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Flow.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Flow.java index e685f139b680c..3bbd007c66a65 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Flow.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Flow.java @@ -214,7 +214,6 @@ public class Flow { private final Resolve rs; private final JCDiagnostic.Factory diags; private Env attrEnv; - private Lint lint; private final Infer infer; public static Flow instance(Context context) { @@ -337,7 +336,6 @@ protected Flow(Context context) { syms = Symtab.instance(context); types = Types.instance(context); chk = Check.instance(context); - lint = Lint.instance(context); infer = Infer.instance(context); rs = Resolve.instance(context); diags = JCDiagnostic.Factory.instance(context); @@ -562,10 +560,8 @@ public void visitClassDef(JCClassDecl tree) { if (tree.sym == null) return; Liveness alivePrev = alive; ListBuffer pendingExitsPrev = pendingExits; - Lint lintPrev = lint; pendingExits = new ListBuffer<>(); - lint = lint.augment(tree.sym); try { // process all the nested classes @@ -596,30 +592,22 @@ public void visitClassDef(JCClassDecl tree) { } finally { pendingExits = pendingExitsPrev; alive = alivePrev; - lint = lintPrev; } } public void visitMethodDef(JCMethodDecl tree) { if (tree.body == null) return; - Lint lintPrev = lint; - - lint = lint.augment(tree.sym); Assert.check(pendingExits.isEmpty()); - try { - alive = Liveness.ALIVE; - scanStat(tree.body); - tree.completesNormally = alive != Liveness.DEAD; + alive = Liveness.ALIVE; + scanStat(tree.body); + tree.completesNormally = alive != Liveness.DEAD; - if (alive == Liveness.ALIVE && !tree.sym.type.getReturnType().hasTag(VOID)) - log.error(TreeInfo.diagEndPos(tree.body), Errors.MissingRetStmt); + if (alive == Liveness.ALIVE && !tree.sym.type.getReturnType().hasTag(VOID)) + log.error(TreeInfo.diagEndPos(tree.body), Errors.MissingRetStmt); - clearPendingExits(true); - } finally { - lint = lintPrev; - } + clearPendingExits(true); } private void clearPendingExits(boolean inMethod) { @@ -634,15 +622,7 @@ private void clearPendingExits(boolean inMethod) { } public void visitVarDef(JCVariableDecl tree) { - if (tree.init != null) { - Lint lintPrev = lint; - lint = lint.augment(tree.sym); - try{ - scan(tree.init); - } finally { - lint = lintPrev; - } - } + scan(tree.init); } public void visitBlock(JCBlock tree) { @@ -724,8 +704,7 @@ public void visitSwitch(JCSwitch tree) { // Warn about fall-through if lint switch fallthrough enabled. if (alive == Liveness.ALIVE && c.stats.nonEmpty() && l.tail.nonEmpty()) - lint.logIfEnabled(l.tail.head.pos(), - LintWarnings.PossibleFallThroughIntoCase); + log.warning(l.tail.head.pos(), LintWarnings.PossibleFallThroughIntoCase); } tree.isExhaustive = tree.hasUnconditionalPattern || TreeInfo.isErrorEnumSwitch(tree.selector, tree.cases); @@ -1232,7 +1211,7 @@ public void visitTry(JCTry tree) { scanStat(tree.finalizer); tree.finallyCanCompleteNormally = alive != Liveness.DEAD; if (alive == Liveness.DEAD) { - lint.logIfEnabled(TreeInfo.diagEndPos(tree.finalizer), + log.warning(TreeInfo.diagEndPos(tree.finalizer), LintWarnings.FinallyCannotComplete); } else { while (exits.nonEmpty()) { @@ -1453,7 +1432,6 @@ public void visitClassDef(JCClassDecl tree) { List thrownPrev = thrown; List caughtPrev = caught; ListBuffer pendingExitsPrev = pendingExits; - Lint lintPrev = lint; boolean anonymousClass = tree.name == names.empty; pendingExits = new ListBuffer<>(); if (!anonymousClass) { @@ -1461,7 +1439,6 @@ public void visitClassDef(JCClassDecl tree) { } classDef = tree; thrown = List.nil(); - lint = lint.augment(tree.sym); try { // process all the nested classes @@ -1510,7 +1487,6 @@ public void visitClassDef(JCClassDecl tree) { pendingExits = pendingExitsPrev; caught = caughtPrev; classDef = classDefPrev; - lint = lintPrev; } } @@ -1519,9 +1495,6 @@ public void visitMethodDef(JCMethodDecl tree) { List caughtPrev = caught; List mthrown = tree.sym.type.getThrownTypes(); - Lint lintPrev = lint; - - lint = lint.augment(tree.sym); Assert.check(pendingExits.isEmpty()); @@ -1554,20 +1527,11 @@ else if ((tree.sym.flags() & (BLOCK | STATIC)) != BLOCK) } } finally { caught = caughtPrev; - lint = lintPrev; } } public void visitVarDef(JCVariableDecl tree) { - if (tree.init != null) { - Lint lintPrev = lint; - lint = lint.augment(tree.sym); - try{ - scan(tree.init); - } finally { - lint = lintPrev; - } - } + scan(tree.init); } public void visitBlock(JCBlock tree) { @@ -2387,82 +2351,76 @@ public void visitClassDef(JCClassDecl tree) { return; } - Lint lintPrev = lint; - lint = lint.augment(tree.sym); - try { - JCClassDecl classDefPrev = classDef; - int firstadrPrev = firstadr; - int nextadrPrev = nextadr; - ListBuffer pendingExitsPrev = pendingExits; + JCClassDecl classDefPrev = classDef; + int firstadrPrev = firstadr; + int nextadrPrev = nextadr; + ListBuffer pendingExitsPrev = pendingExits; - pendingExits = new ListBuffer<>(); - if (tree.name != names.empty) { - firstadr = nextadr; - } - classDef = tree; - try { - // define all the static fields - for (List l = tree.defs; l.nonEmpty(); l = l.tail) { - if (l.head.hasTag(VARDEF)) { - JCVariableDecl def = (JCVariableDecl)l.head; - if ((def.mods.flags & STATIC) != 0) { - VarSymbol sym = def.sym; - if (trackable(sym)) { - newVar(def); - } + pendingExits = new ListBuffer<>(); + if (tree.name != names.empty) { + firstadr = nextadr; + } + classDef = tree; + try { + // define all the static fields + for (List l = tree.defs; l.nonEmpty(); l = l.tail) { + if (l.head.hasTag(VARDEF)) { + JCVariableDecl def = (JCVariableDecl)l.head; + if ((def.mods.flags & STATIC) != 0) { + VarSymbol sym = def.sym; + if (trackable(sym)) { + newVar(def); } } } + } - // process all the static initializers - forEachInitializer(tree, true, def -> { - scan(def); - clearPendingExits(false); - }); + // process all the static initializers + forEachInitializer(tree, true, def -> { + scan(def); + clearPendingExits(false); + }); - // verify all static final fields got initialized - for (int i = firstadr; i < nextadr; i++) { - JCVariableDecl vardecl = vardecls[i]; - VarSymbol var = vardecl.sym; - if (var.owner == classDef.sym && var.isStatic()) { - checkInit(TreeInfo.diagnosticPositionFor(var, vardecl), var); - } + // verify all static final fields got initialized + for (int i = firstadr; i < nextadr; i++) { + JCVariableDecl vardecl = vardecls[i]; + VarSymbol var = vardecl.sym; + if (var.owner == classDef.sym && var.isStatic()) { + checkInit(TreeInfo.diagnosticPositionFor(var, vardecl), var); } + } - // define all the instance fields - for (List l = tree.defs; l.nonEmpty(); l = l.tail) { - if (l.head.hasTag(VARDEF)) { - JCVariableDecl def = (JCVariableDecl)l.head; - if ((def.mods.flags & STATIC) == 0) { - VarSymbol sym = def.sym; - if (trackable(sym)) { - newVar(def); - } + // define all the instance fields + for (List l = tree.defs; l.nonEmpty(); l = l.tail) { + if (l.head.hasTag(VARDEF)) { + JCVariableDecl def = (JCVariableDecl)l.head; + if ((def.mods.flags & STATIC) == 0) { + VarSymbol sym = def.sym; + if (trackable(sym)) { + newVar(def); } } } + } - // process all the methods - for (List l = tree.defs; l.nonEmpty(); l = l.tail) { - if (l.head.hasTag(METHODDEF)) { - scan(l.head); - } + // process all the methods + for (List l = tree.defs; l.nonEmpty(); l = l.tail) { + if (l.head.hasTag(METHODDEF)) { + scan(l.head); } + } - // process all the nested classes - for (List l = tree.defs; l.nonEmpty(); l = l.tail) { - if (l.head.hasTag(CLASSDEF)) { - scan(l.head); - } + // process all the nested classes + for (List l = tree.defs; l.nonEmpty(); l = l.tail) { + if (l.head.hasTag(CLASSDEF)) { + scan(l.head); } - } finally { - pendingExits = pendingExitsPrev; - nextadr = nextadrPrev; - firstadr = firstadrPrev; - classDef = classDefPrev; } } finally { - lint = lintPrev; + pendingExits = pendingExitsPrev; + nextadr = nextadrPrev; + firstadr = firstadrPrev; + classDef = classDefPrev; } } @@ -2477,87 +2435,81 @@ public void visitMethodDef(JCMethodDecl tree) { return; } - Lint lintPrev = lint; - lint = lint.augment(tree.sym); + final Bits initsPrev = new Bits(inits); + final Bits uninitsPrev = new Bits(uninits); + int nextadrPrev = nextadr; + int firstadrPrev = firstadr; + int returnadrPrev = returnadr; + + Assert.check(pendingExits.isEmpty()); + boolean isConstructorPrev = isConstructor; try { - final Bits initsPrev = new Bits(inits); - final Bits uninitsPrev = new Bits(uninits); - int nextadrPrev = nextadr; - int firstadrPrev = firstadr; - int returnadrPrev = returnadr; - - Assert.check(pendingExits.isEmpty()); - boolean isConstructorPrev = isConstructor; - try { - isConstructor = TreeInfo.isConstructor(tree); + isConstructor = TreeInfo.isConstructor(tree); - // We only track field initialization inside constructors - if (!isConstructor) { - firstadr = nextadr; - } + // We only track field initialization inside constructors + if (!isConstructor) { + firstadr = nextadr; + } - // Mark all method parameters as DA - for (List l = tree.params; l.nonEmpty(); l = l.tail) { - JCVariableDecl def = l.head; - scan(def); - Assert.check((def.sym.flags() & PARAMETER) != 0, "Method parameter without PARAMETER flag"); - /* If we are executing the code from Gen, then there can be - * synthetic or mandated variables, ignore them. - */ - initParam(def); - } - // else we are in an instance initializer block; - // leave caught unchanged. - scan(tree.body); + // Mark all method parameters as DA + for (List l = tree.params; l.nonEmpty(); l = l.tail) { + JCVariableDecl def = l.head; + scan(def); + Assert.check((def.sym.flags() & PARAMETER) != 0, "Method parameter without PARAMETER flag"); + /* If we are executing the code from Gen, then there can be + * synthetic or mandated variables, ignore them. + */ + initParam(def); + } + // else we are in an instance initializer block; + // leave caught unchanged. + scan(tree.body); - boolean isCompactOrGeneratedRecordConstructor = (tree.sym.flags() & Flags.COMPACT_RECORD_CONSTRUCTOR) != 0 || - (tree.sym.flags() & (GENERATEDCONSTR | RECORD)) == (GENERATEDCONSTR | RECORD); - if (isConstructor) { - boolean isSynthesized = (tree.sym.flags() & - GENERATEDCONSTR) != 0; - for (int i = firstadr; i < nextadr; i++) { - JCVariableDecl vardecl = vardecls[i]; - VarSymbol var = vardecl.sym; - if (var.owner == classDef.sym && !var.isStatic()) { - // choose the diagnostic position based on whether - // the ctor is default(synthesized) or not - if (isSynthesized && !isCompactOrGeneratedRecordConstructor) { - checkInit(TreeInfo.diagnosticPositionFor(var, vardecl), - var, Errors.VarNotInitializedInDefaultConstructor(var)); - } else if (isCompactOrGeneratedRecordConstructor) { - boolean isInstanceRecordField = var.enclClass().isRecord() && - (var.flags_field & (Flags.PRIVATE | Flags.FINAL | Flags.GENERATED_MEMBER | Flags.RECORD)) != 0 && - var.owner.kind == TYP; - if (isInstanceRecordField) { - boolean notInitialized = !inits.isMember(var.adr); - if (notInitialized && uninits.isMember(var.adr) && tree.completesNormally) { - /* this way we indicate Lower that it should generate an initialization for this field - * in the compact constructor - */ - var.flags_field |= UNINITIALIZED_FIELD; - } else { - checkInit(TreeInfo.diagEndPos(tree.body), var); - } + boolean isCompactOrGeneratedRecordConstructor = (tree.sym.flags() & Flags.COMPACT_RECORD_CONSTRUCTOR) != 0 || + (tree.sym.flags() & (GENERATEDCONSTR | RECORD)) == (GENERATEDCONSTR | RECORD); + if (isConstructor) { + boolean isSynthesized = (tree.sym.flags() & + GENERATEDCONSTR) != 0; + for (int i = firstadr; i < nextadr; i++) { + JCVariableDecl vardecl = vardecls[i]; + VarSymbol var = vardecl.sym; + if (var.owner == classDef.sym && !var.isStatic()) { + // choose the diagnostic position based on whether + // the ctor is default(synthesized) or not + if (isSynthesized && !isCompactOrGeneratedRecordConstructor) { + checkInit(TreeInfo.diagnosticPositionFor(var, vardecl), + var, Errors.VarNotInitializedInDefaultConstructor(var)); + } else if (isCompactOrGeneratedRecordConstructor) { + boolean isInstanceRecordField = var.enclClass().isRecord() && + (var.flags_field & (Flags.PRIVATE | Flags.FINAL | Flags.GENERATED_MEMBER | Flags.RECORD)) != 0 && + var.owner.kind == TYP; + if (isInstanceRecordField) { + boolean notInitialized = !inits.isMember(var.adr); + if (notInitialized && uninits.isMember(var.adr) && tree.completesNormally) { + /* this way we indicate Lower that it should generate an initialization for this field + * in the compact constructor + */ + var.flags_field |= UNINITIALIZED_FIELD; } else { - checkInit(TreeInfo.diagnosticPositionFor(var, vardecl), var); + checkInit(TreeInfo.diagEndPos(tree.body), var); } } else { - checkInit(TreeInfo.diagEndPos(tree.body), var); + checkInit(TreeInfo.diagnosticPositionFor(var, vardecl), var); } + } else { + checkInit(TreeInfo.diagEndPos(tree.body), var); } } } - clearPendingExits(true); - } finally { - inits.assign(initsPrev); - uninits.assign(uninitsPrev); - nextadr = nextadrPrev; - firstadr = firstadrPrev; - returnadr = returnadrPrev; - isConstructor = isConstructorPrev; } + clearPendingExits(true); } finally { - lint = lintPrev; + inits.assign(initsPrev); + uninits.assign(uninitsPrev); + nextadr = nextadrPrev; + firstadr = firstadrPrev; + returnadr = returnadrPrev; + isConstructor = isConstructorPrev; } } @@ -2585,21 +2537,15 @@ protected void initParam(JCVariableDecl def) { } public void visitVarDef(JCVariableDecl tree) { - Lint lintPrev = lint; - lint = lint.augment(tree.sym); - try{ - boolean track = trackable(tree.sym); - if (track && (tree.sym.owner.kind == MTH || tree.sym.owner.kind == VAR)) { - newVar(tree); - } - if (tree.init != null) { - scanExpr(tree.init); - if (track) { - letInit(tree.pos(), tree.sym); - } + boolean track = trackable(tree.sym); + if (track && (tree.sym.owner.kind == MTH || tree.sym.owner.kind == VAR)) { + newVar(tree); + } + if (tree.init != null) { + scanExpr(tree.init); + if (track) { + letInit(tree.pos(), tree.sym); } - } finally { - lint = lintPrev; } } @@ -2851,8 +2797,7 @@ public void visitTry(JCTry tree) { final Bits uninitsEnd = new Bits(uninits); int nextadrCatch = nextadr; - if (!resourceVarDecls.isEmpty() && - lint.isEnabled(Lint.LintCategory.TRY)) { + if (!resourceVarDecls.isEmpty()) { for (JCVariableDecl resVar : resourceVarDecls) { if (unrefdResources.includes(resVar.sym) && !resVar.sym.isUnnamedVariable()) { log.warning(resVar.pos(), diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/MemberEnter.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/MemberEnter.java index b726cc7a61d8b..d63ba1677d6a0 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/MemberEnter.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/MemberEnter.java @@ -66,7 +66,6 @@ public class MemberEnter extends JCTree.Visitor { private final Annotate annotate; private final Types types; private final Names names; - private final DeferredLintHandler deferredLintHandler; public static MemberEnter instance(Context context) { MemberEnter instance = context.get(memberEnterKey); @@ -87,7 +86,6 @@ protected MemberEnter(Context context) { types = Types.instance(context); source = Source.instance(context); names = Names.instance(context); - deferredLintHandler = DeferredLintHandler.instance(context); } /** Construct method type from method signature. @@ -194,16 +192,11 @@ public void visitMethodDef(JCMethodDecl tree) { } Env localEnv = methodEnv(tree, env); - deferredLintHandler.push(tree); - try { - // Compute the method type - m.type = signature(m, tree.typarams, tree.params, - tree.restype, tree.recvparam, - tree.thrown, - localEnv); - } finally { - deferredLintHandler.pop(); - } + // Compute the method type + m.type = signature(m, tree.typarams, tree.params, + tree.restype, tree.recvparam, + tree.thrown, + localEnv); if (types.isSignaturePolymorphic(m)) { m.flags_field |= SIGNATURE_POLYMORPHIC; @@ -227,14 +220,14 @@ public void visitMethodDef(JCMethodDecl tree) { enclScope.enter(m); } - annotate.annotateLater(tree.mods.annotations, localEnv, m, tree); + annotate.annotateLater(tree.mods.annotations, localEnv, m); // Visit the signature of the method. Note that // TypeAnnotate doesn't descend into the body. - annotate.queueScanTreeAndTypeAnnotate(tree, localEnv, m, tree); + annotate.queueScanTreeAndTypeAnnotate(tree, localEnv, m); if (tree.defaultValue != null) { m.defaultValue = annotate.unfinishedDefaultValue(); // set it to temporary sentinel for now - annotate.annotateDefaultValueLater(tree.defaultValue, localEnv, m, tree); + annotate.annotateDefaultValueLater(tree.defaultValue, localEnv, m); } } @@ -263,18 +256,13 @@ public void visitVarDef(JCVariableDecl tree) { localEnv = env.dup(tree, env.info.dup()); localEnv.info.staticLevel++; } - deferredLintHandler.push(tree); - try { - if (TreeInfo.isEnumInit(tree)) { - attr.attribIdentAsEnumType(localEnv, (JCIdent)tree.vartype); - } else if (!tree.isImplicitlyTyped()) { - attr.attribType(tree.vartype, localEnv); - if (TreeInfo.isReceiverParam(tree)) - checkReceiver(tree, localEnv); - } - } finally { - deferredLintHandler.pop(); + if (TreeInfo.isEnumInit(tree)) { + attr.attribIdentAsEnumType(localEnv, (JCIdent)tree.vartype); + } else if (!tree.isImplicitlyTyped()) { + attr.attribType(tree.vartype, localEnv); + if (TreeInfo.isReceiverParam(tree)) + checkReceiver(tree, localEnv); } if ((tree.mods.flags & VARARGS) != 0) { @@ -315,9 +303,9 @@ public void visitVarDef(JCVariableDecl tree) { } } - annotate.annotateLater(tree.mods.annotations, localEnv, v, tree); + annotate.annotateLater(tree.mods.annotations, localEnv, v); if (!tree.isImplicitlyTyped()) { - annotate.queueScanTreeAndTypeAnnotate(tree.vartype, localEnv, v, tree); + annotate.queueScanTreeAndTypeAnnotate(tree.vartype, localEnv, v); } v.pos = tree.pos; diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Modules.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Modules.java index a159793fe327f..1591773e6d4a7 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Modules.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Modules.java @@ -52,7 +52,6 @@ import javax.tools.StandardLocation; import com.sun.source.tree.ModuleTree.ModuleKind; -import com.sun.tools.javac.code.DeferredLintHandler; import com.sun.tools.javac.code.Directive; import com.sun.tools.javac.code.Directive.ExportsDirective; import com.sun.tools.javac.code.Directive.ExportsFlag; @@ -103,6 +102,7 @@ import com.sun.tools.javac.tree.TreeInfo; import com.sun.tools.javac.util.Assert; import com.sun.tools.javac.util.Context; +import com.sun.tools.javac.util.JCDiagnostic.DiagnosticFlag; import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition; import com.sun.tools.javac.util.List; import com.sun.tools.javac.util.ListBuffer; @@ -141,7 +141,6 @@ public class Modules extends JCTree.Visitor { private final Attr attr; private final Check chk; private final Preview preview; - private final DeferredLintHandler deferredLintHandler; private final TypeEnvs typeEnvs; private final Types types; private final JavaFileManager fileManager; @@ -169,8 +168,6 @@ public class Modules extends JCTree.Visitor { private final String moduleVersionOpt; private final boolean sourceLauncher; - private final boolean lintOptions; - private Set rootModules = null; private final Set warnedMissing = new HashSet<>(); @@ -193,7 +190,6 @@ protected Modules(Context context) { attr = Attr.instance(context); chk = Check.instance(context); preview = Preview.instance(context); - deferredLintHandler = DeferredLintHandler.instance(context); typeEnvs = TypeEnvs.instance(context); moduleFinder = ModuleFinder.instance(context); types = Types.instance(context); @@ -205,8 +201,6 @@ protected Modules(Context context) { allowAccessIntoSystem = options.isUnset(Option.RELEASE); - lintOptions = !options.isExplicitlyDisabled(Option.XLINT, LintCategory.OPTIONS); - multiModuleMode = fileManager.hasLocation(StandardLocation.MODULE_SOURCE_PATH); ClassWriter classWriter = ClassWriter.instance(context); classWriter.multiModuleMode = multiModuleMode; @@ -746,7 +740,6 @@ public void complete(Symbol sym) throws CompletionFailure { ModuleVisitor v = new ModuleVisitor(); JavaFileObject prev = log.useSource(tree.sourcefile); JCModuleDecl moduleDecl = tree.getModuleDecl(); - deferredLintHandler.push(moduleDecl); try { moduleDecl.accept(v); @@ -754,7 +747,6 @@ public void complete(Symbol sym) throws CompletionFailure { checkCyclicDependencies(moduleDecl); } finally { log.useSource(prev); - deferredLintHandler.pop(); msym.flags_field &= ~UNATTRIBUTED; } } @@ -991,13 +983,11 @@ public Completer getUsesProvidesCompleter() { UsesProvidesVisitor v = new UsesProvidesVisitor(msym, env); JavaFileObject prev = log.useSource(env.toplevel.sourcefile); JCModuleDecl decl = env.toplevel.getModuleDecl(); - deferredLintHandler.push(decl); try { decl.accept(v); } finally { log.useSource(prev); - deferredLintHandler.pop(); } }; } @@ -1263,12 +1253,9 @@ private void setupAllModules() { } observable = computeTransitiveClosure(limitMods, rootModules, null); observable.addAll(rootModules); - if (lintOptions) { - for (ModuleSymbol msym : limitMods) { - if (!observable.contains(msym)) { - log.warning( - LintWarnings.ModuleForOptionNotFound(Option.LIMIT_MODULES, msym)); - } + for (ModuleSymbol msym : limitMods) { + if (!observable.contains(msym)) { + log.warning(LintWarnings.ModuleForOptionNotFound(Option.LIMIT_MODULES, msym), DiagnosticFlag.DEFAULT_ENABLED); } } } @@ -1365,7 +1352,7 @@ private void setupAllModules() { .forEach(result::add); } - if (lint.isEnabled(LintCategory.INCUBATING)) { + if (lint.isActive(LintCategory.INCUBATING)) { String incubatingModules = filterAlreadyWarnedIncubatorModules(result.stream() .filter(msym -> msym.resolutionFlags.contains(ModuleResolutionFlags.WARN_INCUBATING)) .map(msym -> msym.name.toString())) @@ -1721,10 +1708,7 @@ private boolean isKnownModule(ModuleSymbol msym, Set unknownModule } if (!unknownModules.contains(msym)) { - if (lintOptions) { - log.warning( - LintWarnings.ModuleForOptionNotFound(Option.ADD_EXPORTS, msym)); - } + log.warning(LintWarnings.ModuleForOptionNotFound(Option.ADD_EXPORTS, msym), DiagnosticFlag.DEFAULT_ENABLED); unknownModules.add(msym); } return false; @@ -1760,9 +1744,7 @@ private void initAddReads() { ModuleSymbol msym = syms.enterModule(names.fromString(sourceName)); if (!allModules.contains(msym)) { - if (lintOptions) { - log.warning(LintWarnings.ModuleForOptionNotFound(Option.ADD_READS, msym)); - } + log.warning(LintWarnings.ModuleForOptionNotFound(Option.ADD_READS, msym), DiagnosticFlag.DEFAULT_ENABLED); continue; } @@ -1780,9 +1762,8 @@ private void initAddReads() { continue; targetModule = syms.enterModule(names.fromString(targetName)); if (!allModules.contains(targetModule)) { - if (lintOptions) { - log.warning(LintWarnings.ModuleForOptionNotFound(Option.ADD_READS, targetModule)); - } + log.warning(LintWarnings.ModuleForOptionNotFound(Option.ADD_READS, targetModule), + DiagnosticFlag.DEFAULT_ENABLED); continue; } } diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/ThisEscapeAnalyzer.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/ThisEscapeAnalyzer.java index 9119278660f32..df6894e654027 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/ThisEscapeAnalyzer.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/ThisEscapeAnalyzer.java @@ -45,6 +45,7 @@ import com.sun.tools.javac.code.Directive; import com.sun.tools.javac.code.Flags; import com.sun.tools.javac.code.Lint; +import com.sun.tools.javac.code.LintMapper; import com.sun.tools.javac.code.Symbol; import com.sun.tools.javac.code.Symbol.*; import com.sun.tools.javac.code.Symtab; @@ -57,6 +58,7 @@ import com.sun.tools.javac.tree.TreeScanner; import com.sun.tools.javac.util.Assert; import com.sun.tools.javac.util.Context; +import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition; import com.sun.tools.javac.util.JCDiagnostic.LintWarning; import com.sun.tools.javac.util.List; import com.sun.tools.javac.util.Log; @@ -67,6 +69,7 @@ import static com.sun.tools.javac.code.Lint.LintCategory.THIS_ESCAPE; import static com.sun.tools.javac.code.TypeTag.*; import static com.sun.tools.javac.tree.JCTree.Tag.*; +import static com.sun.tools.javac.util.Position.NOPOS; /** * Looks for possible 'this' escapes and generates corresponding warnings. @@ -156,7 +159,7 @@ public class ThisEscapeAnalyzer extends TreeScanner { private final Types types; private final Resolve rs; private final Log log; - private Lint lint; + private final LintMapper lintMapper; // These fields are scoped to the entire compilation unit @@ -168,10 +171,6 @@ public class ThisEscapeAnalyzer extends TreeScanner { */ private final Map methodMap = new LinkedHashMap<>(); - /** Contains symbols of fields and constructors that have warnings suppressed. - */ - private final Set suppressed = new HashSet<>(); - /** Contains classes whose outer instance (if any) is non-public. */ private final Set nonPublicOuters = new HashSet<>(); @@ -231,7 +230,7 @@ protected ThisEscapeAnalyzer(Context context) { syms = Symtab.instance(context); types = Types.instance(context); rs = Resolve.instance(context); - lint = Lint.instance(context); + lintMapper = LintMapper.instance(context); } // @@ -262,8 +261,8 @@ private void doAnalyzeTree(Env env) { Assert.check(checkInvariants(false, false)); Assert.check(methodMap.isEmpty()); // we are not prepared to be used more than once - // Short circuit if warnings are totally disabled - if (!lint.isEnabled(THIS_ESCAPE)) + // Short circuit if this calculation is unnecessary + if (!lintMapper.lintAt(env.toplevel.sourcefile, env.tree.pos()).get().isActive(THIS_ESCAPE)) return; // Determine which packages are exported by the containing module, if any. @@ -278,11 +277,9 @@ private void doAnalyzeTree(Env env) { // Build a mapping from symbols of methods to their declarations. // Classify all ctors and methods as analyzable and/or invokable. - // Track which constructors and fields have warnings suppressed. // Record classes whose outer instance (if any) is non-public. new TreeScanner() { - private Lint lint = ThisEscapeAnalyzer.this.lint; private JCClassDecl currentClass; private boolean nonPublicOuter; @@ -290,8 +287,6 @@ private void doAnalyzeTree(Env env) { public void visitClassDef(JCClassDecl tree) { JCClassDecl currentClassPrev = currentClass; boolean nonPublicOuterPrev = nonPublicOuter; - Lint lintPrev = lint; - lint = lint.augment(tree.sym); try { currentClass = tree; @@ -306,57 +301,29 @@ public void visitClassDef(JCClassDecl tree) { } finally { currentClass = currentClassPrev; nonPublicOuter = nonPublicOuterPrev; - lint = lintPrev; - } - } - - @Override - public void visitVarDef(JCVariableDecl tree) { - Lint lintPrev = lint; - lint = lint.augment(tree.sym); - try { - - // Track warning suppression of fields - if (tree.sym.owner.kind == TYP && !lint.isEnabled(THIS_ESCAPE)) - suppressed.add(tree.sym); - - // Recurse - super.visitVarDef(tree); - } finally { - lint = lintPrev; } } @Override public void visitMethodDef(JCMethodDecl tree) { - Lint lintPrev = lint; - lint = lint.augment(tree.sym); - try { - - // Track warning suppression of constructors - if (TreeInfo.isConstructor(tree) && !lint.isEnabled(THIS_ESCAPE)) - suppressed.add(tree.sym); - // Gather some useful info - boolean constructor = TreeInfo.isConstructor(tree); - boolean extendableClass = currentClassIsExternallyExtendable(); - boolean nonPrivate = (tree.sym.flags() & (Flags.PUBLIC | Flags.PROTECTED)) != 0; - boolean finalish = (tree.mods.flags & (Flags.STATIC | Flags.PRIVATE | Flags.FINAL)) != 0; + // Gather some useful info + boolean constructor = TreeInfo.isConstructor(tree); + boolean extendableClass = currentClassIsExternallyExtendable(); + boolean nonPrivate = (tree.sym.flags() & (Flags.PUBLIC | Flags.PROTECTED)) != 0; + boolean finalish = (tree.mods.flags & (Flags.STATIC | Flags.PRIVATE | Flags.FINAL)) != 0; - // Determine if this is a constructor we should analyze - boolean analyzable = extendableClass && constructor && nonPrivate; + // Determine if this is a constructor we should analyze + boolean analyzable = extendableClass && constructor && nonPrivate; - // Determine if it's safe to "invoke" the method in an analysis (i.e., it can't be overridden) - boolean invokable = !extendableClass || constructor || finalish; + // Determine if it's safe to "invoke" the method in an analysis (i.e., it can't be overridden) + boolean invokable = !extendableClass || constructor || finalish; - // Add this method or constructor to our map - methodMap.put(tree.sym, new MethodInfo(currentClass, tree, constructor, analyzable, invokable)); + // Add this method or constructor to our map + methodMap.put(tree.sym, new MethodInfo(currentClass, tree, constructor, analyzable, invokable)); - // Recurse - super.visitMethodDef(tree); - } finally { - lint = lintPrev; - } + // Recurse + super.visitMethodDef(tree); } // Determines if the current class could be extended in some other package/module @@ -375,7 +342,7 @@ private boolean currentClassIsExternallyExtendable() { .filter(MethodInfo::analyzable) .forEach(this::analyzeConstructor); - // Manually apply any Lint suppression + // Manually apply (and validate) any Lint suppression filterWarnings(warning -> !warning.isSuppressed()); // Field intitializers and initialization blocks will generate a separate warning for each primary constructor. @@ -401,7 +368,7 @@ private boolean currentClassIsExternallyExtendable() { for (Warning warning : warningList) { LintWarning key = LintWarnings.PossibleThisEscape; for (StackFrame frame : warning.stack) { - log.warning(frame.site.pos(), key); + log.warning(frame.warningPos(), key); key = LintWarnings.PossibleThisEscapeLocation; } } @@ -1746,9 +1713,16 @@ private class StackFrame { this.suppressible = initializer != null || (method.constructor && method.declaringClass == targetClass); } + DiagnosticPosition warningPos() { + return site.pos().withLintPosition(NOPOS); // disable normal Lint suppression + } + + Lint lint() { + return lintMapper.lintAt(topLevelEnv.toplevel.sourcefile, site.pos()).get(); + } + boolean isSuppressed() { - return suppressible && - suppressed.contains(initializer instanceof JCVariableDecl v ? v.sym : method.declaration.sym); + return suppressible && !lint().isEnabled(THIS_ESCAPE, true); } int comparePos(StackFrame that) { @@ -1813,12 +1787,12 @@ static int sortByStackFrames(Warning warning1, Warning warning2) { } } - // Determine whether this warning is suppressed. A single "this-escape" warning involves multiple source code - // positions, so we must determine suppression manually. We do this as follows: A warning is suppressed if - // "this-escape" is disabled at any position in the stack where that stack frame corresponds to a constructor - // or field initializer in the target class. That means, for example, @SuppressWarnings("this-escape") annotations - // on regular methods are ignored. Here we work our way back up the call stack from the point of the leak until - // we encounter a suppressible stack frame. + // Determine whether this warning is suppressed and, if so, validate that suppression. A single "this-escape" + // warning involves multiple source code positions, so we must determine and validate suppression manually. + // We do this as follows: A warning is suppressed if "this-escape" is disabled at any position in the stack + // where that stack frame corresponds to a constructor or field initializer in the target class. That means, + // for example, @SuppressWarnings("this-escape") annotations on regular methods are ignored. We work our way + // back up the call stack from the point of the leak until we encounter a suppressible stack frame. boolean isSuppressed() { for (int index = stack.size() - 1; index >= 0; index--) { if (stack.get(index).isSuppressed()) diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/TypeEnter.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/TypeEnter.java index eb4ab96dfd2b7..8648b929a04fc 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/TypeEnter.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/TypeEnter.java @@ -34,7 +34,6 @@ import com.sun.tools.javac.code.*; import com.sun.tools.javac.code.Directive.ExportsDirective; import com.sun.tools.javac.code.Directive.RequiresDirective; -import com.sun.tools.javac.code.Lint.LintCategory; import com.sun.tools.javac.code.Scope.ImportFilter; import com.sun.tools.javac.code.Scope.NamedImportScope; import com.sun.tools.javac.code.Scope.StarImportScope; @@ -108,8 +107,6 @@ public class TypeEnter implements Completer { private final Annotate annotate; private final TypeAnnotations typeAnnotations; private final Types types; - private final DeferredLintHandler deferredLintHandler; - private final Lint lint; private final TypeEnvs typeEnvs; private final Dependencies dependencies; @@ -135,8 +132,6 @@ protected TypeEnter(Context context) { annotate = Annotate.instance(context); typeAnnotations = TypeAnnotations.instance(context); types = Types.instance(context); - deferredLintHandler = DeferredLintHandler.instance(context); - lint = Lint.instance(context); typeEnvs = TypeEnvs.instance(context); dependencies = Dependencies.instance(context); Source source = Source.instance(context); @@ -274,7 +269,6 @@ protected void doCompleteEnvs(List> envs) { queue.add(env); JavaFileObject prev = log.useSource(env.toplevel.sourcefile); - deferredLintHandler.push(tree); try { dependencies.push(env.enclClass.sym, phaseName); runPhase(env); @@ -282,7 +276,6 @@ protected void doCompleteEnvs(List> envs) { chk.completionError(tree.pos(), ex); } finally { dependencies.pop(); - deferredLintHandler.pop(); log.useSource(prev); } } @@ -351,8 +344,6 @@ private void resolveImports(JCCompilationUnit tree, Env env) { ImportFilter prevStaticImportFilter = staticImportFilter; ImportFilter prevTypeImportFilter = typeImportFilter; - deferredLintHandler.pushImmediate(lint); - Lint prevLint = chk.setLint(lint); Env prevEnv = this.env; try { this.env = env; @@ -376,20 +367,13 @@ private void resolveImports(JCCompilationUnit tree, Env env) { handleImports(tree.getImports()); if (decl != null) { - deferredLintHandler.push(decl); - try { - //check @Deprecated: - markDeprecated(decl.sym, decl.mods.annotations, env); - } finally { - deferredLintHandler.pop(); - } + //check for @Deprecated annotations + markDeprecated(decl.sym, decl.mods.annotations, env); // process module annotations - annotate.annotateLater(decl.mods.annotations, env, env.toplevel.modle, decl); + annotate.annotateLater(decl.mods.annotations, env, env.toplevel.modle); } } finally { this.env = prevEnv; - chk.setLint(prevLint); - deferredLintHandler.pop(); this.staticImportFilter = prevStaticImportFilter; this.typeImportFilter = prevTypeImportFilter; } @@ -422,7 +406,7 @@ private void checkClassPackageClash(JCPackageDecl tree) { } } // process package annotations - annotate.annotateLater(tree.annotations, env, env.toplevel.packge, tree); + annotate.annotateLater(tree.annotations, env, env.toplevel.packge); } private void doImport(JCImport tree, boolean fromModuleImport) { @@ -914,9 +898,9 @@ protected void runPhase(Env env) { Env baseEnv = baseEnv(tree, env); if (tree.extending != null) - annotate.queueScanTreeAndTypeAnnotate(tree.extending, baseEnv, sym, tree); + annotate.queueScanTreeAndTypeAnnotate(tree.extending, baseEnv, sym); for (JCExpression impl : tree.implementing) - annotate.queueScanTreeAndTypeAnnotate(impl, baseEnv, sym, tree); + annotate.queueScanTreeAndTypeAnnotate(impl, baseEnv, sym); annotate.flush(); attribSuperTypes(env, baseEnv); @@ -931,11 +915,11 @@ protected void runPhase(Env env) { chk.checkNotRepeated(iface.pos(), types.erasure(it), interfaceSet); } - annotate.annotateLater(tree.mods.annotations, baseEnv, sym, tree); + annotate.annotateLater(tree.mods.annotations, baseEnv, sym); attr.attribTypeVariables(tree.typarams, baseEnv, false); for (JCTypeParameter tp : tree.typarams) - annotate.queueScanTreeAndTypeAnnotate(tp, baseEnv, sym, tree); + annotate.queueScanTreeAndTypeAnnotate(tp, baseEnv, sym); // check that no package exists with same fully qualified name, // but admit classes in the unnamed package which have the same diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/WarningAnalyzer.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/WarningAnalyzer.java index 00d1de386dbc3..3dbc385682cac 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/WarningAnalyzer.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/WarningAnalyzer.java @@ -25,6 +25,7 @@ package com.sun.tools.javac.comp; +import com.sun.tools.javac.code.LintMapper; import com.sun.tools.javac.util.Context; import com.sun.tools.javac.util.Log; @@ -42,6 +43,7 @@ public class WarningAnalyzer { private final Log log; private final ThisEscapeAnalyzer thisEscapeAnalyzer; + private final LintMapper lintMapper; public static WarningAnalyzer instance(Context context) { WarningAnalyzer instance = context.get(contextKey); @@ -55,9 +57,13 @@ protected WarningAnalyzer(Context context) { context.put(contextKey, this); log = Log.instance(context); thisEscapeAnalyzer = ThisEscapeAnalyzer.instance(context); + lintMapper = LintMapper.instance(context); } public void analyzeTree(Env env) { thisEscapeAnalyzer.analyzeTree(env); + + // This one should go last + lintMapper.reportUnnecessarySuppressionAnnotations(env.toplevel.sourcefile, env.tree); } } diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/file/BaseFileManager.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/file/BaseFileManager.java index 73f8623971381..0c96d62ce8f48 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/file/BaseFileManager.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/file/BaseFileManager.java @@ -92,7 +92,7 @@ public void setContext(Context context) { options = Options.instance(context); // Initialize locations - locations.update(log, lint, FSInfo.instance(context)); + locations.update(log, FSInfo.instance(context)); // Apply options options.whenReady(this::applyOptions); @@ -519,7 +519,7 @@ protected static Collection nullCheck(Collection it) { synchronized void newOutputToPath(Path path) throws IOException { // Is output file clash detection enabled? - if (!lint.isEnabled(LintCategory.OUTPUT_FILE_CLASH)) + if (!lint.isActive(LintCategory.OUTPUT_FILE_CLASH)) return; // Get the "canonical" version of the file's path; we are assuming diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/file/Locations.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/file/Locations.java index 5ff55d4be3aa2..7661e4331e40d 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/file/Locations.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/file/Locations.java @@ -77,14 +77,11 @@ import javax.tools.StandardJavaFileManager.PathFactory; import javax.tools.StandardLocation; -import com.sun.tools.javac.code.Lint; -import com.sun.tools.javac.resources.CompilerProperties.LintWarnings; import jdk.internal.jmod.JmodFile; -import com.sun.tools.javac.code.Lint; -import com.sun.tools.javac.code.Lint.LintCategory; import com.sun.tools.javac.main.Option; import com.sun.tools.javac.resources.CompilerProperties.Errors; +import com.sun.tools.javac.resources.CompilerProperties.LintWarnings; import com.sun.tools.javac.resources.CompilerProperties.Warnings; import com.sun.tools.javac.util.DefinedBy; import com.sun.tools.javac.util.DefinedBy.Api; @@ -127,11 +124,6 @@ public class Locations { */ private FSInfo fsInfo; - /** - * The root {@link Lint} instance. - */ - private Lint lint; - private ModuleNameReader moduleNameReader; private PathFactory pathFactory = Paths::get; @@ -172,9 +164,8 @@ public void close() throws IOException { } } - void update(Log log, Lint lint, FSInfo fsInfo) { + void update(Log log, FSInfo fsInfo) { this.log = log; - this.lint = lint; this.fsInfo = fsInfo; } @@ -225,7 +216,7 @@ private Iterable getPathEntries(String searchPath, Path emptyPathDefault) try { entries.add(getPath(s)); } catch (IllegalArgumentException e) { - lint.logIfEnabled(LintWarnings.InvalidPath(s)); + log.warning(LintWarnings.InvalidPath(s)); } } } @@ -319,7 +310,7 @@ public SearchPath addDirectories(String dirs) { private void addDirectory(Path dir, boolean warn) { if (!Files.isDirectory(dir)) { if (warn) { - lint.logIfEnabled(LintWarnings.DirPathElementNotFound(dir)); + log.warning(LintWarnings.DirPathElementNotFound(dir)); } return; } @@ -364,7 +355,7 @@ public void addFile(Path file, boolean warn) { if (!fsInfo.exists(file)) { /* No such file or directory exists */ if (warn) { - lint.logIfEnabled(LintWarnings.PathElementNotFound(file)); + log.warning(LintWarnings.PathElementNotFound(file)); } super.add(file); return; @@ -386,12 +377,12 @@ public void addFile(Path file, boolean warn) { try { FileSystems.newFileSystem(file, (ClassLoader)null).close(); if (warn) { - lint.logIfEnabled(LintWarnings.UnexpectedArchiveFile(file)); + log.warning(LintWarnings.UnexpectedArchiveFile(file)); } } catch (IOException | ProviderNotFoundException e) { // FIXME: include e.getLocalizedMessage in warning if (warn) { - lint.logIfEnabled(LintWarnings.InvalidArchiveFile(file)); + log.warning(LintWarnings.InvalidArchiveFile(file)); } return; } @@ -1654,7 +1645,7 @@ private boolean isSeparator(char ch) { void add(Map> map, Path prefix, Path suffix) { if (!Files.isDirectory(prefix)) { - lint.logIfEnabled(Files.exists(prefix) ? + log.warning(Files.exists(prefix) ? LintWarnings.DirPathElementNotDirectory(prefix) : LintWarnings.DirPathElementNotFound(prefix)); return; 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 cf751ff6b3097..6f1ad28586d2f 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 @@ -51,7 +51,6 @@ import com.sun.tools.javac.comp.Annotate.AnnotationTypeCompleter; import com.sun.tools.javac.code.*; import com.sun.tools.javac.code.Directive.*; -import com.sun.tools.javac.code.Lint.LintCategory; import com.sun.tools.javac.code.Scope.WriteableScope; import com.sun.tools.javac.code.Symbol.*; import com.sun.tools.javac.code.Symtab; @@ -139,9 +138,6 @@ public class ClassReader { /** The symbol table. */ Symtab syms; - /** The root Lint config. */ - Lint lint; - Types types; /** The name table. */ @@ -303,8 +299,6 @@ protected ClassReader(Context context) { typevars = WriteableScope.create(syms.noSymbol); - lint = Lint.instance(context); - initAttributeReaders(); } @@ -854,8 +848,7 @@ protected boolean accepts(AttributeKind kind) { if (!warnedAttrs.contains(name)) { JavaFileObject prev = log.useSource(currentClassFile); try { - lint.logIfEnabled( - LintWarnings.FutureAttr(name, version.major, version.minor, majorVersion, minorVersion)); + log.warning(LintWarnings.FutureAttr(name, version.major, version.minor, majorVersion, minorVersion)); } finally { log.useSource(prev); } @@ -1609,7 +1602,7 @@ void readParameterAnnotations(Symbol meth) { } else if (parameterAnnotations.length != numParameters) { //the RuntimeVisibleParameterAnnotations and RuntimeInvisibleParameterAnnotations //provide annotations for a different number of parameters, ignore: - lint.logIfEnabled(LintWarnings.RuntimeVisibleInvisibleParamAnnotationsMismatch(currentClassFile)); + log.warning(LintWarnings.RuntimeVisibleInvisibleParamAnnotationsMismatch(currentClassFile)); for (int pnum = 0; pnum < numParameters; pnum++) { readAnnotations(); } @@ -2075,9 +2068,9 @@ MethodSymbol findAccessMethod(Type container, Name name) { JavaFileObject prevSource = log.useSource(requestingOwner.classfile); try { if (failure == null) { - lint.logIfEnabled(LintWarnings.AnnotationMethodNotFound(container, name)); + log.warning(LintWarnings.AnnotationMethodNotFound(container, name)); } else { - lint.logIfEnabled(LintWarnings.AnnotationMethodNotFoundReason(container, + log.warning(LintWarnings.AnnotationMethodNotFoundReason(container, name, failure.getDetailValue()));//diagnostic, if present } @@ -2954,7 +2947,7 @@ void adjustParameterAnnotations(MethodSymbol sym, Type methodDescriptor, private void dropParameterAnnotations() { parameterAnnotations = null; - lint.logIfEnabled(LintWarnings.RuntimeInvisibleParameterAnnotations(currentClassFile)); + log.warning(LintWarnings.RuntimeInvisibleParameterAnnotations(currentClassFile)); } /** * Creates the parameter at the position {@code mpIndex} in the parameter list of the owning method. diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/launcher/MemoryContext.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/launcher/MemoryContext.java index 5fa76c14d3f8f..3f3f5e2f27b67 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/launcher/MemoryContext.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/launcher/MemoryContext.java @@ -28,11 +28,12 @@ import com.sun.source.util.TaskEvent; import com.sun.source.util.TaskListener; import com.sun.tools.javac.api.JavacTool; -import com.sun.tools.javac.code.Preview; +import com.sun.tools.javac.code.Lint.LintCategory; import com.sun.tools.javac.file.JavacFileManager; import com.sun.tools.javac.resources.LauncherProperties.Errors; import com.sun.tools.javac.util.Context; import com.sun.tools.javac.util.Context.Factory; +import com.sun.tools.javac.util.Log; import javax.tools.JavaFileManager; import javax.tools.JavaFileObject; @@ -120,8 +121,11 @@ void compileProgram() throws Fault { } var opts = options.forProgramCompilation(); var context = new Context(); - MemoryPreview.registerInstance(context); var task = compiler.getTask(out, memoryFileManager, null, opts, null, units, context); + + // This suppresses diagnostics like "Note: Recompile with -Xlint:preview for details." + Log.instance(context).suppressAggregatedWarningNotes(LintCategory.PREVIEW); + var ok = task.call(); if (!ok) { throw new Fault(Errors.CompilationFailed); @@ -269,19 +273,4 @@ private void enableNativeAccess(ModuleLayer.Controller controller, boolean shoul controller.enableNativeAccess(module); } } - - static class MemoryPreview extends Preview { - static void registerInstance(Context context) { - context.put(previewKey, (Factory)MemoryPreview::new); - } - - MemoryPreview(Context context) { - super(context); - } - - @Override - public void reportDeferredDiagnostics() { - // suppress diagnostics like "Note: Recompile with -Xlint:preview for details." - } - } } diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/main/Arguments.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/main/Arguments.java index 9e3a978c3bc9a..376e9c5399be0 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/main/Arguments.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/main/Arguments.java @@ -52,7 +52,6 @@ import javax.tools.StandardLocation; import com.sun.tools.doclint.DocLint; -import com.sun.tools.javac.code.Lint.LintCategory; import com.sun.tools.javac.code.Source; import com.sun.tools.javac.file.BaseFileManager; import com.sun.tools.javac.file.JavacFileManager; @@ -67,6 +66,7 @@ import com.sun.tools.javac.resources.CompilerProperties.Warnings; import com.sun.tools.javac.util.Context; import com.sun.tools.javac.util.JCDiagnostic; +import com.sun.tools.javac.util.JCDiagnostic.DiagnosticFlag; import com.sun.tools.javac.util.JCDiagnostic.DiagnosticInfo; import com.sun.tools.javac.util.JCDiagnostic.Fragment; import com.sun.tools.javac.util.List; @@ -503,12 +503,9 @@ public boolean validate() { } } else { // single-module or legacy mode - boolean lintPaths = !options.isExplicitlyDisabled(Option.XLINT, LintCategory.PATH); - if (lintPaths) { - Path outDirParent = outDir.getParent(); - if (outDirParent != null && Files.exists(outDirParent.resolve("module-info.class"))) { - log.warning(LintWarnings.OutdirIsInExplodedModule(outDir)); - } + Path outDirParent = outDir.getParent(); + if (outDirParent != null && Files.exists(outDirParent.resolve("module-info.class"))) { + log.warning(LintWarnings.OutdirIsInExplodedModule(outDir), DiagnosticFlag.DEFAULT_ENABLED); } } } @@ -576,15 +573,16 @@ public boolean validate() { reportDiag(Errors.SourcepathModulesourcepathConflict); } - boolean lintOptions = !options.isExplicitlyDisabled(Option.XLINT, LintCategory.OPTIONS); - if (lintOptions && source.compareTo(Source.DEFAULT) < 0 && !options.isSet(Option.RELEASE)) { + if (source.compareTo(Source.DEFAULT) < 0 && !options.isSet(Option.RELEASE)) { if (fm instanceof BaseFileManager baseFileManager) { if (source.compareTo(Source.JDK8) <= 0) { - if (baseFileManager.isDefaultBootClassPath()) - log.warning(LintWarnings.SourceNoBootclasspath(source.name, releaseNote(source, targetString))); - } else { - if (baseFileManager.isDefaultSystemModulesPath()) - log.warning(LintWarnings.SourceNoSystemModulesPath(source.name, releaseNote(source, targetString))); + if (baseFileManager.isDefaultBootClassPath()) { + log.warning(LintWarnings.SourceNoBootclasspath(source.name, releaseNote(source, targetString)), + DiagnosticFlag.DEFAULT_ENABLED); + } + } else if (baseFileManager.isDefaultSystemModulesPath()) { + log.warning(LintWarnings.SourceNoSystemModulesPath(source.name, releaseNote(source, targetString)), + DiagnosticFlag.DEFAULT_ENABLED); } } } @@ -593,15 +591,15 @@ public boolean validate() { if (source.compareTo(Source.MIN) < 0) { log.error(Errors.OptionRemovedSource(source.name, Source.MIN.name)); - } else if (source == Source.MIN && lintOptions) { - log.warning(LintWarnings.OptionObsoleteSource(source.name)); + } else if (source == Source.MIN) { + log.warning(LintWarnings.OptionObsoleteSource(source.name), DiagnosticFlag.DEFAULT_ENABLED); obsoleteOptionFound = true; } if (target.compareTo(Target.MIN) < 0) { log.error(Errors.OptionRemovedTarget(target, Target.MIN)); - } else if (target == Target.MIN && lintOptions) { - log.warning(LintWarnings.OptionObsoleteTarget(target)); + } else if (target == Target.MIN) { + log.warning(LintWarnings.OptionObsoleteTarget(target), DiagnosticFlag.DEFAULT_ENABLED); obsoleteOptionFound = true; } @@ -634,8 +632,8 @@ public boolean validate() { log.error(Errors.ProcessorpathNoProcessormodulepath); } - if (obsoleteOptionFound && lintOptions) { - log.warning(LintWarnings.OptionObsoleteSuppression); + if (obsoleteOptionFound) { + log.warning(LintWarnings.OptionObsoleteSuppression, DiagnosticFlag.DEFAULT_ENABLED); } SourceVersion sv = Source.toSourceVersion(source); @@ -645,8 +643,8 @@ public boolean validate() { validateLimitModules(sv); validateDefaultModuleForCreatedFiles(sv); - if (lintOptions && options.isSet(Option.ADD_OPENS)) { - log.warning(LintWarnings.AddopensIgnored); + if (options.isSet(Option.ADD_OPENS)) { + log.warning(LintWarnings.AddopensIgnored, DiagnosticFlag.DEFAULT_ENABLED); } return !errors && (log.nerrors == 0); diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/main/JavaCompiler.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/main/JavaCompiler.java index 52979ab16b412..927c62e6e21eb 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/main/JavaCompiler.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/main/JavaCompiler.java @@ -55,10 +55,10 @@ import com.sun.source.util.TaskEvent; import com.sun.tools.javac.api.MultiTaskListener; import com.sun.tools.javac.code.*; -import com.sun.tools.javac.code.Lint.LintCategory; import com.sun.tools.javac.code.Source.Feature; import com.sun.tools.javac.code.Symbol.ClassSymbol; import com.sun.tools.javac.code.Symbol.CompletionFailure; +import com.sun.tools.javac.code.Symbol.ModuleSymbol; import com.sun.tools.javac.code.Symbol.PackageSymbol; import com.sun.tools.javac.comp.*; import com.sun.tools.javac.comp.CompileStates.CompileState; @@ -85,10 +85,6 @@ import static com.sun.tools.javac.code.Kinds.Kind.*; -import com.sun.tools.javac.code.Lint; -import com.sun.tools.javac.code.Lint.LintCategory; -import com.sun.tools.javac.code.Symbol.ModuleSymbol; - import com.sun.tools.javac.resources.CompilerProperties.Errors; import com.sun.tools.javac.resources.CompilerProperties.Fragments; import com.sun.tools.javac.resources.CompilerProperties.Notes; @@ -262,6 +258,10 @@ else if (option.equals("class")) */ protected JNIWriter jniWriter; + /** The Lint mapper. + */ + protected LintMapper lintMapper; + /** The module for the symbol table entry phases. */ protected Enter enter; @@ -274,10 +274,6 @@ else if (option.equals("class")) */ protected Source source; - /** The preview language version. - */ - protected Preview preview; - /** The module for code generation. */ protected Gen gen; @@ -392,6 +388,7 @@ public JavaCompiler(Context context) { names = Names.instance(context); log = Log.instance(context); + lintMapper = LintMapper.instance(context); diagFactory = JCDiagnostic.Factory.instance(context); finder = ClassFinder.instance(context); reader = ClassReader.instance(context); @@ -413,7 +410,6 @@ public JavaCompiler(Context context) { log.error(Errors.CantAccess(ex.sym, ex.getDetailValue())); } source = Source.instance(context); - preview = Preview.instance(context); attr = Attr.instance(context); analyzer = Analyzer.instance(context); chk = Check.instance(context); @@ -584,6 +580,7 @@ protected boolean shouldStop(CompileState cs) { /** The number of errors reported so far. */ public int errorCount() { + log.reportOutstandingWarnings(); if (werror && log.nerrors == 0 && log.nwarnings > 0) { log.error(Errors.WarningsAndWerror); } @@ -634,6 +631,7 @@ protected JCCompilationUnit parse(JavaFileObject filename, CharSequence content) private JCCompilationUnit parse(JavaFileObject filename, CharSequence content, boolean silent) { long msec = now(); JCCompilationUnit tree = make.TopLevel(List.nil()); + lintMapper.startParsingFile(filename); if (content != null) { if (verbose) { log.printVerbose("parsing.started", filename); @@ -653,6 +651,7 @@ private JCCompilationUnit parse(JavaFileObject filename, CharSequence content, b } tree.sourcefile = filename; + lintMapper.finishParsingFile(tree); if (content != null && !taskListener.isEmpty() && !silent) { TaskEvent e = new TaskEvent(TaskEvent.Kind.PARSE, tree); @@ -1852,8 +1851,8 @@ public void reportDeferredDiagnostics() { else log.warning(Warnings.ProcUseProcOrImplicit); } - chk.reportDeferredDiagnostics(); - preview.reportDeferredDiagnostics(); + log.reportOutstandingWarnings(); + log.reportOutstandingNotes(); if (log.compressedOutput) { log.mandatoryNote(null, Notes.CompressedDiags); } @@ -1926,6 +1925,7 @@ public void close() { attr = null; chk = null; gen = null; + lintMapper = null; flow = null; transTypes = null; lower = null; diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/JavaTokenizer.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/JavaTokenizer.java index d799975a76aef..4951dba9a6638 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/JavaTokenizer.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/JavaTokenizer.java @@ -25,8 +25,6 @@ package com.sun.tools.javac.parser; -import com.sun.tools.javac.code.Lint; -import com.sun.tools.javac.code.Lint.LintCategory; import com.sun.tools.javac.code.Preview; import com.sun.tools.javac.code.Source; import com.sun.tools.javac.code.Source.Feature; @@ -83,7 +81,7 @@ public class JavaTokenizer extends UnicodeReader { /** * The log to be used for error reporting. Copied from scanner factory. */ - private final Log log; + protected final Log log; /** * The token factory. Copied from scanner factory. @@ -135,13 +133,6 @@ public class JavaTokenizer extends UnicodeReader { */ protected boolean hasEscapeSequences; - /** - * The set of lint options currently in effect. It is initialized - * from the context, and then is set/reset as needed by Attr as it - * visits all the various parts of the trees during attribution. - */ - protected final Lint lint; - /** * Construct a Java token scanner from the input character buffer. * @@ -168,7 +159,6 @@ protected JavaTokenizer(ScannerFactory fac, char[] array, int length) { this.source = fac.source; this.preview = fac.preview; this.enableLineDocComments = fac.enableLineDocComments; - this.lint = fac.lint; this.sb = new StringBuilder(256); } @@ -218,17 +208,6 @@ protected void lexError(DiagnosticFlag flags, int pos, JCDiagnostic.Error key) { errPos = pos; } - /** - * Report a warning at the given position using the provided arguments. - * - * @param pos position in input buffer. - * @param key error key to report. - */ - protected void lexWarning(int pos, JCDiagnostic.LintWarning key) { - DiagnosticPosition dp = new SimpleDiagnosticPosition(pos) ; - log.warning(dp, key); - } - /** * Add a character to the literal buffer. * @@ -1073,17 +1052,12 @@ public Token readToken() { // If a text block. if (isTextBlock) { // Verify that the incidental indentation is consistent. - if (lint.isEnabled(LintCategory.TEXT_BLOCKS)) { - Set checks = - TextBlockSupport.checkWhitespace(string); - if (checks.contains(TextBlockSupport.WhitespaceChecks.INCONSISTENT)) { - lexWarning(pos, - LintWarnings.InconsistentWhiteSpaceIndentation); - } - if (checks.contains(TextBlockSupport.WhitespaceChecks.TRAILING)) { - lexWarning(pos, - LintWarnings.TrailingWhiteSpaceWillBeRemoved); - } + Set checks = TextBlockSupport.checkWhitespace(string); + if (checks.contains(TextBlockSupport.WhitespaceChecks.INCONSISTENT)) { + log.warning(pos, LintWarnings.InconsistentWhiteSpaceIndentation); + } + if (checks.contains(TextBlockSupport.WhitespaceChecks.TRAILING)) { + log.warning(pos, LintWarnings.TrailingWhiteSpaceWillBeRemoved); } // Remove incidental indentation. try { diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/JavacParser.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/JavacParser.java index 7ebcd0c844b2a..bc10b10d5874f 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/JavacParser.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/JavacParser.java @@ -116,8 +116,6 @@ public class JavacParser implements Parser { /** A map associating "other nearby documentation comments" * with the preferred documentation comment for a declaration. */ protected Map> danglingComments = new HashMap<>(); - /** Handler for deferred diagnostics. */ - protected final DeferredLintHandler deferredLintHandler; // Because of javac's limited lookahead, some contexts are ambiguous in // the presence of type annotations even though they are not ambiguous @@ -190,7 +188,6 @@ protected JavacParser(ParserFactory fac, this.names = fac.names; this.source = fac.source; this.preview = fac.preview; - this.deferredLintHandler = fac.deferredLintHandler; this.allowStringFolding = fac.options.getBoolean("allowStringFolding", true); this.keepDocComments = keepDocComments; this.parseModuleInfo = parseModuleInfo; @@ -216,7 +213,6 @@ protected JavacParser(JavacParser parser, this.names = parser.names; this.source = parser.source; this.preview = parser.preview; - this.deferredLintHandler = parser.deferredLintHandler; this.allowStringFolding = parser.allowStringFolding; this.keepDocComments = parser.keepDocComments; this.parseModuleInfo = false; @@ -591,8 +587,7 @@ protected void checkNoMods(int pos, long mods) { * 4. When the tree node for the declaration is finally * available, and the primary comment, if any, * is "attached", (in {@link #attach}) any related - * dangling comments are also attached to the tree node - * by registering them using the {@link #deferredLintHandler}. + * dangling comments are reported to the log as warnings. * 5. (Later) Warnings may be generated for the dangling * comments, subject to the {@code -Xlint} and * {@code @SuppressWarnings}. @@ -653,32 +648,22 @@ protected T attach(T tree, Comment dc) { void reportDanglingComments(JCTree tree, Comment dc) { var list = danglingComments.remove(dc); if (list != null) { - deferredLintHandler.push(tree); - try { - list.forEach(this::reportDanglingDocComment); - } finally { - deferredLintHandler.pop(); - } + list.forEach(c -> reportDanglingDocComment(tree, c)); } } /** - * Reports an individual dangling comment using the {@link #deferredLintHandler}. + * Reports an individual dangling comment as a warning to the log. * The comment may or not may generate an actual diagnostic, depending on * the settings for {@code -Xlint} and/or {@code @SuppressWarnings}. * * @param c the comment */ - void reportDanglingDocComment(Comment c) { + void reportDanglingDocComment(JCTree tree, Comment c) { var pos = c.getPos(); - if (pos != null) { - deferredLintHandler.report(lint -> { - if (lint.isEnabled(Lint.LintCategory.DANGLING_DOC_COMMENTS) && - !shebang(c, pos)) { - log.warning( - pos, LintWarnings.DanglingDocComment); - } - }); + if (pos != null && !shebang(c, pos)) { + pos = pos.withLintPosition(tree.getStartPosition()); + S.lintWarning(pos, LintWarnings.DanglingDocComment); } } diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/Lexer.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/Lexer.java index bff4e027db75c..1d1e08194f7f1 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/Lexer.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/Lexer.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -28,6 +28,8 @@ import java.util.Queue; import com.sun.tools.javac.parser.Tokens.*; +import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition; +import com.sun.tools.javac.util.JCDiagnostic.LintWarning; import com.sun.tools.javac.util.Position.LineMap; /** @@ -103,4 +105,12 @@ public interface Lexer { * token. */ Queue getDocComments(); + + /** + * Report a warning that is subject to possible suppression by {@code @SuppressWarnings}. + * + * @param pos the lexical position at which the warning occurs + * @param key the warning to report + */ + void lintWarning(DiagnosticPosition pos, LintWarning key); } diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/ParserFactory.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/ParserFactory.java index c873c6f31b7d3..f9e187315ba6e 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/ParserFactory.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/ParserFactory.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 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 @@ -28,7 +28,6 @@ import java.util.Locale; import com.sun.tools.javac.api.JavacTrees; -import com.sun.tools.javac.code.DeferredLintHandler; import com.sun.tools.javac.code.Lint; import com.sun.tools.javac.code.Preview; import com.sun.tools.javac.code.Source; @@ -70,7 +69,6 @@ public static ParserFactory instance(Context context) { final Options options; final ScannerFactory scannerFactory; final Locale locale; - final DeferredLintHandler deferredLintHandler; private final JavacTrees trees; @@ -88,7 +86,6 @@ protected ParserFactory(Context context) { this.options = Options.instance(context); this.scannerFactory = ScannerFactory.instance(context); this.locale = context.get(Locale.class); - this.deferredLintHandler = DeferredLintHandler.instance(context); this.trees = JavacTrees.instance(context); } diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/Scanner.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/Scanner.java index a24e73a41410a..7fcb87eac7a8e 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/Scanner.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/Scanner.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 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 @@ -31,6 +31,8 @@ import java.util.ArrayList; import java.util.Queue; +import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition; +import com.sun.tools.javac.util.JCDiagnostic.LintWarning; import com.sun.tools.javac.util.Position.LineMap; import static com.sun.tools.javac.parser.Tokens.*; @@ -150,6 +152,11 @@ public Queue getDocComments() { return docComments; } + @Override + public void lintWarning(DiagnosticPosition pos, LintWarning key) { + tokenizer.log.warning(pos, key); + } + public int errPos() { return tokenizer.errPos(); } diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/ScannerFactory.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/ScannerFactory.java index 188fe838b187a..66f2b66fabfa1 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/ScannerFactory.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/ScannerFactory.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 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 @@ -27,7 +27,6 @@ import java.nio.CharBuffer; -import com.sun.tools.javac.code.Lint; import com.sun.tools.javac.code.Preview; import com.sun.tools.javac.code.Source; import com.sun.tools.javac.main.Option; @@ -62,7 +61,6 @@ public static ScannerFactory instance(Context context) { final Source source; final Preview preview; final Tokens tokens; - final Lint lint; final boolean enableLineDocComments; /** Create a new scanner factory. */ @@ -74,7 +72,6 @@ protected ScannerFactory(Context context) { this.source = Source.instance(context); this.preview = Preview.instance(context); this.tokens = Tokens.instance(context); - this.lint = Lint.instance(context); var options = Options.instance(context); this.enableLineDocComments = !options.isSet(Option.DISABLE_LINE_DOC_COMMENTS); } diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/VirtualParser.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/VirtualParser.java index ec3a373ab4ec2..2a819152eed3d 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/VirtualParser.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/VirtualParser.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, 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 @@ -30,6 +30,7 @@ import com.sun.tools.javac.tree.JCTree.JCErroneous; import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition; import com.sun.tools.javac.util.JCDiagnostic.Error; +import com.sun.tools.javac.util.JCDiagnostic.LintWarning; import com.sun.tools.javac.util.List; import com.sun.tools.javac.util.Position.LineMap; @@ -167,10 +168,9 @@ public LineMap getLineMap() { return S.getLineMap(); } - public void commit() { - for (int i = 0 ; i < offset ; i++) { - S.nextToken(); // advance underlying lexer until position matches - } + @Override + public void lintWarning(DiagnosticPosition pos, LintWarning key) { + // ignore } } diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/processing/JavacFiler.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/processing/JavacFiler.java index 012ac628ecd89..dc25de30b36fc 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/processing/JavacFiler.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/processing/JavacFiler.java @@ -51,7 +51,6 @@ import static javax.tools.StandardLocation.SOURCE_OUTPUT; import static javax.tools.StandardLocation.CLASS_OUTPUT; -import com.sun.tools.javac.code.Lint; import com.sun.tools.javac.code.Symbol.ClassSymbol; import com.sun.tools.javac.code.Symbol.ModuleSymbol; import com.sun.tools.javac.code.Symtab; @@ -62,7 +61,6 @@ import com.sun.tools.javac.util.*; import com.sun.tools.javac.util.DefinedBy.Api; -import static com.sun.tools.javac.code.Lint.LintCategory.PROCESSING; import com.sun.tools.javac.code.Symbol.PackageSymbol; import com.sun.tools.javac.main.Option; @@ -338,7 +336,6 @@ public synchronized void close() throws IOException { JavaFileManager fileManager; JavacElements elementUtils; Log log; - Lint lint; Modules modules; Names names; Symtab syms; @@ -421,8 +418,6 @@ public synchronized void close() throws IOException { aggregateGeneratedClassNames = new LinkedHashSet<>(); initialClassNames = new LinkedHashSet<>(); - lint = Lint.instance(context); - Options options = Options.instance(context); defaultTargetModule = options.get(Option.DEFAULT_MODULE_FOR_CREATED_FILES); @@ -486,14 +481,12 @@ private Pair checkOrInferModule(CharSequence moduleAndPkg) private JavaFileObject createSourceOrClassFile(ModuleSymbol mod, boolean isSourceFile, String name, Element... originatingElements) throws IOException { Assert.checkNonNull(mod); - if (lint.isEnabled(PROCESSING)) { - int periodIndex = name.lastIndexOf("."); - if (periodIndex != -1) { - String base = name.substring(periodIndex); - String extn = (isSourceFile ? ".java" : ".class"); - if (base.equals(extn)) - log.warning(LintWarnings.ProcSuspiciousClassName(name, extn)); - } + int periodIndex = name.lastIndexOf("."); + if (periodIndex != -1) { + String base = name.substring(periodIndex); + String extn = (isSourceFile ? ".java" : ".class"); + if (base.equals(extn)) + log.warning(LintWarnings.ProcSuspiciousClassName(name, extn)); } checkNameAndExistence(mod, name, isSourceFile); Location loc = (isSourceFile ? SOURCE_OUTPUT : CLASS_OUTPUT); @@ -707,7 +700,7 @@ private void checkName(String name) throws FilerException { private void checkName(String name, boolean allowUnnamedPackageInfo) throws FilerException { if (!SourceVersion.isName(name) && !isPackageInfo(name, allowUnnamedPackageInfo)) { - lint.logIfEnabled(LintWarnings.ProcIllegalFileName(name)); + log.warning(LintWarnings.ProcIllegalFileName(name)); throw new FilerException("Illegal name " + name); } } @@ -735,11 +728,11 @@ private void checkNameAndExistence(ModuleSymbol mod, String typename, boolean al initialClassNames.contains(typename) || containedInInitialInputs(typename); if (alreadySeen) { - lint.logIfEnabled(LintWarnings.ProcTypeRecreate(typename)); + log.warning(LintWarnings.ProcTypeRecreate(typename)); throw new FilerException("Attempt to recreate a file for type " + typename); } if (existing != null) { - lint.logIfEnabled(LintWarnings.ProcTypeAlreadyExists(typename)); + log.warning(LintWarnings.ProcTypeAlreadyExists(typename)); } if (!mod.isUnnamed() && !typename.contains(".")) { throw new FilerException("Attempt to create a type in unnamed package of a named module: " + typename); @@ -768,7 +761,7 @@ private boolean containedInInitialInputs(String typename) { */ private void checkFileReopening(FileObject fileObject, boolean forWriting) throws FilerException { if (isInFileObjectHistory(fileObject, forWriting)) { - lint.logIfEnabled(LintWarnings.ProcFileReopening(fileObject.getName())); + log.warning(LintWarnings.ProcFileReopening(fileObject.getName())); throw new FilerException("Attempt to reopen a file for path " + fileObject.getName()); } if (forWriting) diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/processing/JavacProcessingEnvironment.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/processing/JavacProcessingEnvironment.java index 7bc538a1d1e81..b28f19bd3aff8 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/processing/JavacProcessingEnvironment.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/processing/JavacProcessingEnvironment.java @@ -123,7 +123,6 @@ public class JavacProcessingEnvironment implements ProcessingEnvironment, Closea private final Modules modules; private final Types types; private final Annotate annotate; - private final Lint lint; /** * Holds relevant state history of which processors have been @@ -206,7 +205,6 @@ protected JavacProcessingEnvironment(Context context) { printProcessorInfo = options.isSet(Option.XPRINTPROCESSORINFO); printRounds = options.isSet(Option.XPRINTROUNDS); verbose = options.isSet(Option.VERBOSE); - lint = Lint.instance(context); compiler = JavaCompiler.instance(context); if (options.isSet(Option.PROC, "only") || options.isSet(Option.XPRINT)) { compiler.shouldStopPolicyIfNoError = CompileState.PROCESS; @@ -626,7 +624,7 @@ static class ProcessorState { private Set supportedOptionNames; ProcessorState(Processor p, Log log, Source source, DeferredCompletionFailureHandler dcfh, - boolean allowModules, ProcessingEnvironment env, Lint lint) { + boolean allowModules, ProcessingEnvironment env) { processor = p; contributed = false; @@ -647,10 +645,9 @@ static class ProcessorState { boolean patternAdded = supportedAnnotationStrings.add(annotationPattern); supportedAnnotationPatterns. - add(importStringToPattern(allowModules, annotationPattern, - processor, log, lint)); + add(importStringToPattern(allowModules, annotationPattern, processor, log)); if (!patternAdded) { - lint.logIfEnabled(LintWarnings.ProcDuplicateSupportedAnnotation(annotationPattern, + log.warning(LintWarnings.ProcDuplicateSupportedAnnotation(annotationPattern, p.getClass().getName())); } } @@ -663,7 +660,7 @@ static class ProcessorState { // and "foo.bar.*". if (supportedAnnotationPatterns.contains(MatchingUtils.validImportStringToPattern("*")) && supportedAnnotationPatterns.size() > 1) { - lint.logIfEnabled(LintWarnings.ProcRedundantTypesWithWildcard(p.getClass().getName())); + log.warning(LintWarnings.ProcRedundantTypesWithWildcard(p.getClass().getName())); } supportedOptionNames = new LinkedHashSet<>(); @@ -671,8 +668,7 @@ static class ProcessorState { if (checkOptionName(optionName, log)) { boolean optionAdded = supportedOptionNames.add(optionName); if (!optionAdded) { - lint.logIfEnabled(LintWarnings.ProcDuplicateOptionName(optionName, - p.getClass().getName())); + log.warning(LintWarnings.ProcDuplicateOptionName(optionName, p.getClass().getName())); } } } @@ -759,8 +755,7 @@ public ProcessorState next() { ProcessorState ps = new ProcessorState(psi.processorIterator.next(), log, source, dcfh, Feature.MODULES.allowedInSource(source), - JavacProcessingEnvironment.this, - lint); + JavacProcessingEnvironment.this); psi.procStateList.add(ps); return ps; } else @@ -888,7 +883,7 @@ private void discoverAndRunProcs(Set annotationsPresent, } unmatchedAnnotations.remove(""); - if (lint.isEnabled(PROCESSING) && unmatchedAnnotations.size() > 0) { + if (unmatchedAnnotations.size() > 0) { // Remove annotations processed by javac unmatchedAnnotations.keySet().removeAll(platformAnnotations); if (unmatchedAnnotations.size() > 0) { @@ -1649,7 +1644,7 @@ public Set getSpecifiedPackages() { * regex matching that string. If the string is not a valid * import-style string, return a regex that won't match anything. */ - private static Pattern importStringToPattern(boolean allowModules, String s, Processor p, Log log, Lint lint) { + private static Pattern importStringToPattern(boolean allowModules, String s, Processor p, Log log) { String module; String pkg; int slash = s.indexOf('/'); @@ -1662,7 +1657,7 @@ private static Pattern importStringToPattern(boolean allowModules, String s, Pro } else { String moduleName = s.substring(0, slash); if (!SourceVersion.isName(moduleName)) { - return warnAndNoMatches(s, p, log, lint); + return warnAndNoMatches(s, p, log); } module = Pattern.quote(moduleName + "/"); // And warn if module is specified if modules aren't supported, conditional on -Xlint:proc? @@ -1671,12 +1666,12 @@ private static Pattern importStringToPattern(boolean allowModules, String s, Pro if (MatchingUtils.isValidImportString(pkg)) { return Pattern.compile(module + MatchingUtils.validImportStringToPatternString(pkg)); } else { - return warnAndNoMatches(s, p, log, lint); + return warnAndNoMatches(s, p, log); } } - private static Pattern warnAndNoMatches(String s, Processor p, Log log, Lint lint) { - lint.logIfEnabled(LintWarnings.ProcMalformedSupportedString(s, p.getClass().getName())); + private static Pattern warnAndNoMatches(String s, Processor p, Log log) { + log.warning(LintWarnings.ProcMalformedSupportedString(s, p.getClass().getName())); return noMatches; // won't match any valid identifier } 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 ead48ee62bf73..319b962198e70 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 @@ -2249,6 +2249,11 @@ compiler.warn.requires.automatic=\ compiler.warn.requires.transitive.automatic=\ requires transitive directive for an automatic module +# 0: string +# lint: suppression +compiler.warn.unnecessary.warning.suppression=\ + unnecessary warning suppression: {0} + # Warnings related to annotation processing # 0: string compiler.warn.proc.package.does.not.exist=\ 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 d74f5f14d15ef..596fbbd779157 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 @@ -210,6 +210,9 @@ javac.opt.Xlint.desc.empty=\ javac.opt.Xlint.desc.exports=\ Warn about issues regarding module exports. +javac.opt.Xlint.desc.suppression=\ + Warn about recognized @SuppressWarnings values that don''t actually suppress any warnings. + javac.opt.Xlint.desc.fallthrough=\ Warn about falling through from one case of a switch statement to the next. diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/util/AbstractLog.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/util/AbstractLog.java index 59730448bf53a..2e9da31b0204b 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/util/AbstractLog.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/util/AbstractLog.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 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 @@ -25,6 +25,7 @@ package com.sun.tools.javac.util; +import java.util.EnumSet; import java.util.HashMap; import java.util.Map; import javax.tools.JavaFileObject; @@ -159,35 +160,45 @@ public void error(DiagnosticFlag flag, int pos, Error errorKey) { * maximum number of warnings has been reached. * * @param warningKey The key for the localized warning message. + * @param flags Any additional flags required */ - public void warning(Warning warningKey) { - report(diags.warning(source, null, warningKey)); + public void warning(Warning warningKey, DiagnosticFlag... flags) { + warning(null, warningKey, flags); } /** Report a warning, unless suppressed by the -nowarn option or the * maximum number of warnings has been reached. * @param pos The source position at which to report the warning. * @param warningKey The key for the localized warning message. + * @param flags Any additional flags required */ - public void warning(DiagnosticPosition pos, Warning warningKey) { - report(diags.warning(source, pos, warningKey)); + public void warning(int pos, Warning warningKey, DiagnosticFlag... flags) { + warning(wrap(pos), warningKey, flags); } /** Report a warning, unless suppressed by the -nowarn option or the * maximum number of warnings has been reached. * @param pos The source position at which to report the warning. * @param warningKey The key for the localized warning message. + * @param flags Any additional flags required */ - public void warning(int pos, Warning warningKey) { - report(diags.warning(source, wrap(pos), warningKey)); + public void warning(DiagnosticPosition pos, Warning warningKey, DiagnosticFlag... flags) { + EnumSet flagSet = EnumSet.noneOf(DiagnosticFlag.class); + for (DiagnosticFlag flag : flags) + flagSet.add(flag); + report(diags.create(flagSet, source, pos, warningKey)); } - /** Report a warning. + /** Report a mandatory warning. * @param pos The source position at which to report the warning. * @param warningKey The key for the localized warning message. + * @param flags Any additional flags required */ - public void mandatoryWarning(DiagnosticPosition pos, Warning warningKey) { - report(diags.mandatoryWarning(source, pos, warningKey)); + public void mandatoryWarning(DiagnosticPosition pos, Warning warningKey, DiagnosticFlag... flags) { + EnumSet flagSet = EnumSet.of(DiagnosticFlag.MANDATORY, DiagnosticFlag.DEFAULT_ENABLED); + for (DiagnosticFlag flag : flags) + flagSet.add(flag); + report(diags.create(flagSet, source, pos, warningKey)); } /** Provide a non-fatal notification, unless suppressed by the -nowarn option. diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/util/JCDiagnostic.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/util/JCDiagnostic.java index 220ec856d62fd..25f3297436c35 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/util/JCDiagnostic.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/util/JCDiagnostic.java @@ -115,33 +115,6 @@ public JCDiagnostic error( return diag; } - /** - * Create a warning diagnostic that will not be hidden by the -nowarn or -Xlint:none options. - * @param lc The lint category for the diagnostic - * @param source The source of the compilation unit, if any, in which to report the warning. - * @param pos The source position at which to report the warning. - * @param key The key for the localized warning message. - * @param args Fields of the warning message. - * @see MandatoryWarningHandler - */ - public JCDiagnostic mandatoryWarning( - LintCategory lc, - DiagnosticSource source, DiagnosticPosition pos, String key, Object... args) { - return mandatoryWarning(source, pos, warningKey(lc, key, args)); - } - - /** - * Create a warning diagnostic that will not be hidden by the -nowarn or -Xlint:none options. - * @param source The source of the compilation unit, if any, in which to report the warning. - * @param pos The source position at which to report the warning. - * @param warningKey The key for the localized warning message. - * @see MandatoryWarningHandler - */ - public JCDiagnostic mandatoryWarning( - DiagnosticSource source, DiagnosticPosition pos, Warning warningKey) { - return create(EnumSet.of(DiagnosticFlag.MANDATORY), source, pos, warningKey); - } - /** * Create a warning diagnostic. * @param lc The lint category for the diagnostic @@ -410,6 +383,36 @@ public static interface DiagnosticPosition { * the end position of the tree node. Otherwise, just returns the * same as getPreferredPosition(). */ int getEndPosition(EndPosTable endPosTable); + /** Get the position that determines which Lint configuration applies. */ + default int getLintPosition() { + return getStartPosition(); + } + /** Create a new instance from this instance and the given lint position. */ + default DiagnosticPosition withLintPosition(int lintPos) { + DiagnosticPosition orig = this; + return new DiagnosticPosition() { + @Override + public JCTree getTree() { + return orig.getTree(); + } + @Override + public int getStartPosition() { + return orig.getStartPosition(); + } + @Override + public int getPreferredPosition() { + return orig.getPreferredPosition(); + } + @Override + public int getEndPosition(EndPosTable endPosTable) { + return orig.getEndPosition(endPosTable); + } + @Override + public int getLintPosition() { + return lintPos; + } + }; + } } /** @@ -447,6 +450,13 @@ public enum DiagnosticFlag { RECOVERABLE, NON_DEFERRABLE, COMPRESSED, + /** Flag for lint diagnostics that should be emitted even when their category + * is not explicitly enabled, as long as it is not explicitly suppressed. + */ + DEFAULT_ENABLED, + /** Flags mandatory warnings that should pass through a mandatory warning aggregator. + */ + AGGREGATE, /** Flag for diagnostics that were reported through API methods. */ API, diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/util/Log.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/util/Log.java index fd51729bf2139..88235edfeb24f 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/util/Log.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/util/Log.java @@ -30,26 +30,48 @@ import java.util.Arrays; import java.util.Comparator; import java.util.EnumMap; +import java.util.EnumSet; +import java.util.HashMap; import java.util.HashSet; +import java.util.LinkedHashMap; +import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Optional; import java.util.Set; +import java.util.concurrent.atomic.AtomicBoolean; import java.util.function.Predicate; import javax.tools.DiagnosticListener; import javax.tools.JavaFileObject; import com.sun.tools.javac.api.DiagnosticFormatter; +import com.sun.tools.javac.code.Flags; +import com.sun.tools.javac.code.Lint; +import com.sun.tools.javac.code.Lint.LintCategory; +import com.sun.tools.javac.code.LintMapper; +import com.sun.tools.javac.code.Source; +import com.sun.tools.javac.code.Symbol; +import com.sun.tools.javac.comp.AttrContext; +import com.sun.tools.javac.comp.Env; import com.sun.tools.javac.main.Main; import com.sun.tools.javac.main.Option; import com.sun.tools.javac.tree.EndPosTable; -import com.sun.tools.javac.util.JCDiagnostic.DiagnosticFlag; +import com.sun.tools.javac.tree.JCTree; +import com.sun.tools.javac.tree.JCTree.*; +import com.sun.tools.javac.tree.TreeInfo; +import com.sun.tools.javac.tree.TreeScanner; import com.sun.tools.javac.util.JCDiagnostic.DiagnosticInfo; import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition; import com.sun.tools.javac.util.JCDiagnostic.DiagnosticType; +import com.sun.tools.javac.util.JCDiagnostic.LintWarning; import static com.sun.tools.javac.main.Option.*; +import static com.sun.tools.javac.util.JCDiagnostic.DiagnosticFlag.*; +import static com.sun.tools.javac.code.Lint.LintCategory.*; +import static com.sun.tools.javac.resources.CompilerProperties.LintWarnings.RequiresAutomatic; +import static com.sun.tools.javac.resources.CompilerProperties.LintWarnings.RequiresTransitiveAutomatic; +import static com.sun.tools.javac.tree.JCTree.Tag.*; /** A class for error logs. Reports errors and warnings, and * keeps track of error numbers and positions. @@ -98,6 +120,11 @@ public abstract class DiagnosticHandler { */ protected final DiagnosticHandler prev; + /** + * Diagnostics waiting for an applicable {@link Lint} instance. + */ + protected Map> lintWaitersMap = new LinkedHashMap<>(); + /** * Install this diagnostic handler as the current one, * recording the previous one. @@ -108,9 +135,106 @@ protected DiagnosticHandler() { } /** - * Handle a diagnostic. + * Step 1: Handle a diagnostic for which the applicable Lint instance (if any) may not be known yet. + */ + public final void report(JCDiagnostic diag) { + Lint lint = null; + LintCategory category = diag.getLintCategory(); + if (category != null) { // this is a lint warning; find the applicable Lint + DiagnosticPosition pos = diag.getDiagnosticPosition(); + if (pos != null && category.annotationSuppression) { // we should apply the Lint from the warning's position + if ((lint = lintFor(diag)) == null) { + addLintWaiter(currentSourceFile(), diag); // ...but we don't know it yet, so defer + return; + } + } else // we should apply the root Lint + lint = rootLint(); + } + reportWithLint(diag, lint); + } + + /** + * Step 2: Handle a diagnostic for which the applicable Lint instance (if any) is known and provided. + */ + public final void reportWithLint(JCDiagnostic diag, Lint lint) { + + // Apply hackery for REQUIRES_TRANSITIVE_AUTOMATIC (see also Check.checkModuleRequires()) + if (diag.getCode().equals(RequiresTransitiveAutomatic.key()) && !lint.isEnabled(REQUIRES_TRANSITIVE_AUTOMATIC, true)) { + reportWithLint(diags.warning(diag.getDiagnosticSource(), diag.getDiagnosticPosition(), RequiresAutomatic), lint); + return; + } + + // Apply the lint configuration (if any) and discard the warning if it gets filtered out + if (lint != null) { + + // Gather information + LintCategory category = diag.getLintCategory(); + boolean emit = !diag.isFlagSet(DEFAULT_ENABLED) ? // is the warning not enabled by default? + lint.isEnabled(category, false) : // then emit if the category is enabled + category.annotationSuppression ? // else emit if the category is not suppressed, where + !lint.isSuppressed(category, false) : // ...suppression happens via @SuppressWarnings + !options.isDisabled(Option.XLINT, category); // ...suppression happens via -Xlint:-category + if (!emit) { + validateSuppression(new SuppressionValidation(lint, diag)); // validate any suppression + return; + } + } + + // Proceed + reportReady(diag); + } + + /** + * Step 3: Handle a diagnostic to which the applicable Lint instance (if any) has been applied. + */ + protected abstract void reportReady(JCDiagnostic diag); + + /** + * Validate a lint suppression. + */ + protected abstract void validateSuppression(SuppressionValidation validation); + + protected void addLintWaiter(JavaFileObject sourceFile, JCDiagnostic diagnostic) { + lintWaitersMap.computeIfAbsent(sourceFile, s -> new LinkedList<>()).add(diagnostic); + } + + /** + * Flush any lint waiters whose {@link Lint} configurations are now known. */ - public abstract void report(JCDiagnostic diag); + public void flushLintWaiters() { + lintWaitersMap.entrySet().removeIf(entry -> { + + // Is the source file no longer recognized? If so, discard warnings (e.g., this can happen with JShell) + JavaFileObject sourceFile = entry.getKey(); + if (!lintMapper.isKnown(sourceFile)) + return true; + + // Flush those diagnostics for which we now know the applicable Lint + List diagnosticList = entry.getValue(); + JavaFileObject prevSourceFile = useSource(sourceFile); + try { + diagnosticList.removeIf(diag -> { + Lint lint = lintFor(diag); + if (lint != null) { + reportWithLint(diag, lint); + return true; + } + return false; + }); + } finally { + useSource(prevSourceFile); + } + + // Discard list if empty + return diagnosticList.isEmpty(); + }); + } + + protected record SuppressionValidation(Lint lint, JCDiagnostic diag) { + void validate() { + lint.validateSuppression(diag.getLintCategory()); + } + } } /** @@ -119,7 +243,13 @@ protected DiagnosticHandler() { public class DiscardDiagnosticHandler extends DiagnosticHandler { @Override - public void report(JCDiagnostic diag) { } + protected void addLintWaiter(JavaFileObject sourceFile, JCDiagnostic diagnostic) { } + + @Override + protected void reportReady(JCDiagnostic diag) { } + + @Override + protected void validateSuppression(SuppressionValidation validation) { } } /** @@ -131,6 +261,7 @@ public void report(JCDiagnostic diag) { } */ public class DeferredDiagnosticHandler extends DiagnosticHandler { private List deferred = new ArrayList<>(); + private List validatedSuppressions = new ArrayList<>(); private final Predicate filter; private final boolean passOnNonDeferrable; @@ -148,15 +279,33 @@ public DeferredDiagnosticHandler(Predicate filter, boolean passOnN } private boolean deferrable(JCDiagnostic diag) { - return !(diag.isFlagSet(DiagnosticFlag.NON_DEFERRABLE) && passOnNonDeferrable) && filter.test(diag); + return !(diag.isFlagSet(NON_DEFERRABLE) && passOnNonDeferrable) && filter.test(diag); } @Override - public void report(JCDiagnostic diag) { + protected void reportReady(JCDiagnostic diag) { if (deferrable(diag)) { deferred.add(diag); } else { - prev.report(diag); + prev.reportReady(diag); + } + } + + @Override + protected void addLintWaiter(JavaFileObject sourceFile, JCDiagnostic diag) { + if (deferrable(diag)) { + super.addLintWaiter(sourceFile, diag); + } else { + prev.addLintWaiter(sourceFile, diag); + } + } + + @Override + protected void validateSuppression(SuppressionValidation validation) { + if (deferrable(validation.diag)) { + validatedSuppressions.add(validation); + } else { + prev.validateSuppression(validation); } } @@ -177,6 +326,19 @@ public void reportDeferredDiagnostics(Predicate accepter) { .filter(accepter) .forEach(prev::report); deferred = null; // prevent accidental ongoing use + + // Flush matching Lint waiters to the previous handler + lintWaitersMap.forEach( + (sourceFile, diagnostics) -> diagnostics.stream() + .filter(accepter) + .forEach(diagnostic -> prev.addLintWaiter(sourceFile, diagnostic))); + lintWaitersMap = null; // prevent accidental ongoing use + + // Flush matching suppression validations to the previous handler + validatedSuppressions.stream() + .filter(vs -> accepter.test(vs.diag)) + .forEach(prev::validateSuppression); + validatedSuppressions = null; // prevent accidental ongoing use } /** Report all deferred diagnostics in the specified order. */ @@ -237,6 +399,26 @@ public enum WriterKind { NOTICE, WARNING, ERROR, STDOUT, STDERR } */ private JavacMessages messages; + /** + * The compilation context. + */ + private final Context context; + + /** + * The {@link Options} singleton. + */ + private final Options options; + + /** + * The lint positions table. + */ + private final LintMapper lintMapper; + + /** + * The root {@link Lint} singleton. + */ + private Lint rootLint; + /** * Handler for initial dispatch of diagnostics. */ @@ -334,6 +516,9 @@ private static Map initWriters(PrintWriter out, PrintWr private Log(Context context, Map writers) { super(JCDiagnostic.Factory.instance(context)); context.put(logKey, this); + this.context = context; + this.options = Options.instance(context); + this.lintMapper = LintMapper.instance(context); this.writers = writers; @SuppressWarnings("unchecked") // FIXME @@ -353,7 +538,6 @@ private Log(Context context, Map writers) { this.diagFormatter = new BasicDiagnosticFormatter(messages); // Once Options is ready, complete the initialization - final Options options = Options.instance(context); options.whenReady(this::initOptions); } // where @@ -517,7 +701,7 @@ private boolean shouldReport(JCDiagnostic d) { if (!shouldReport(file, d.getIntPosition())) return false; - if (!d.isFlagSet(DiagnosticFlag.SOURCE_LEVEL)) + if (!d.isFlagSet(SOURCE_LEVEL)) return true; Pair> coords = new Pair<>(file, getCode(d)); @@ -681,7 +865,64 @@ public void strictWarning(DiagnosticPosition pos, String key, Object ... args) { @Override public void report(JCDiagnostic diagnostic) { diagnosticHandler.report(diagnostic); - } + } + +// Deferred Lint Calculation + + /** + * Report unreported lint warnings for which the applicable {@link Lint} configuration is now known. + */ + public void reportOutstandingWarnings() { + diagnosticHandler.flushLintWaiters(); + } + + // Get the Lint config for the given warning (if known) + private Lint lintFor(JCDiagnostic diag) { + Assert.check(diag.getLintCategory() != null); + return lintMapper.lintAt(diag.getSource(), diag.getDiagnosticPosition()).orElse(null); + } + + // Obtain root Lint singleton lazily to avoid init loops + private Lint rootLint() { + if (rootLint == null) + rootLint = Lint.instance(context); + return rootLint; + } + +// Mandatory Warnings + + private final EnumMap aggregators = new EnumMap<>(LintCategory.class); + + private final EnumSet suppressedDeferredMandatory = EnumSet.noneOf(LintCategory.class); + + /** + * Suppress aggregated mandatory warning notes for the specified category. + */ + public void suppressAggregatedWarningNotes(LintCategory category) { + suppressedDeferredMandatory.add(category); + } + + /** + * Report any remaining unreported aggregated mandatory warning notes. + */ + public void reportOutstandingNotes() { + aggregators.entrySet().stream() + .filter(entry -> !suppressedDeferredMandatory.contains(entry.getKey())) + .map(Map.Entry::getValue) + .map(MandatoryWarningAggregator::aggregationNotes) + .flatMap(List::stream) + .forEach(this::report); + aggregators.clear(); + } + + private MandatoryWarningAggregator aggregatorFor(LintCategory lc) { + return switch (lc) { + case PREVIEW -> aggregators.computeIfAbsent(lc, c -> new MandatoryWarningAggregator(this, Source.instance(context), c)); + case DEPRECATION -> aggregators.computeIfAbsent(lc, c -> new MandatoryWarningAggregator(this, null, c, "deprecated")); + case REMOVAL, UNCHECKED -> aggregators.computeIfAbsent(lc, c -> new MandatoryWarningAggregator(this, null, c)); + case null, default -> null; + }; + } /** * Reset the state of this instance. @@ -695,16 +936,21 @@ public void clear() { nsuppressedwarns = 0; while (diagnosticHandler.prev != null) popDiagnosticHandler(diagnosticHandler); + aggregators.values().forEach(MandatoryWarningAggregator::clear); + suppressedDeferredMandatory.clear(); } +// DefaultDiagnosticHandler + /** * Common diagnostic handling. * The diagnostic is counted, and depending on the options and how many diagnostics have been * reported so far, the diagnostic may be handed off to writeDiagnostic. */ private class DefaultDiagnosticHandler extends DiagnosticHandler { + @Override - public void report(JCDiagnostic diagnostic) { + protected void reportReady(JCDiagnostic diagnostic) { if (expectDiagKeys != null) expectDiagKeys.remove(diagnostic.getCode()); @@ -727,6 +973,21 @@ public void report(JCDiagnostic diagnostic) { break; case WARNING: + + // Apply the appropriate mandatory warning aggregator, if needed + if (diagnostic.isFlagSet(AGGREGATE)) { + LintCategory category = diagnostic.getLintCategory(); + Lint lint = lintFor(diagnostic); + boolean verbose = lint.isEnabled(category, false); + if (!aggregatorFor(category).aggregate(diagnostic, verbose)) { + + // Aggregation effectively suppresses the warning, so validate that suppression + validateSuppression(new SuppressionValidation(lint, diagnostic)); + return; + } + } + + // Emit warning unless not mandatory and warnings are disabled if (emitWarnings || diagnostic.isMandatory()) { if (nwarnings < MaxWarnings) { writeDiagnostic(diagnostic); @@ -738,8 +999,7 @@ public void report(JCDiagnostic diagnostic) { break; case ERROR: - if (diagnostic.isFlagSet(DiagnosticFlag.API) || - shouldReport(diagnostic)) { + if (diagnostic.isFlagSet(API) || shouldReport(diagnostic)) { if (nerrors < MaxErrors) { writeDiagnostic(diagnostic); nerrors++; @@ -749,10 +1009,15 @@ public void report(JCDiagnostic diagnostic) { } break; } - if (diagnostic.isFlagSet(JCDiagnostic.DiagnosticFlag.COMPRESSED)) { + if (diagnostic.isFlagSet(COMPRESSED)) { compressedOutput = true; } } + + @Override + protected void validateSuppression(SuppressionValidation validation) { + validation.validate(); + } } /** diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/util/MandatoryWarningHandler.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/util/MandatoryWarningAggregator.java similarity index 72% rename from src/jdk.compiler/share/classes/com/sun/tools/javac/util/MandatoryWarningHandler.java rename to src/jdk.compiler/share/classes/com/sun/tools/javac/util/MandatoryWarningAggregator.java index 046740ddc3028..b6130d384aa5d 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/util/MandatoryWarningHandler.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/util/MandatoryWarningAggregator.java @@ -25,11 +25,14 @@ package com.sun.tools.javac.util; +import java.util.ArrayList; import java.util.HashSet; +import java.util.List; import java.util.Objects; import java.util.Set; import javax.tools.JavaFileObject; +import com.sun.tools.javac.code.Lint; import com.sun.tools.javac.code.Lint.LintCategory; import com.sun.tools.javac.code.Source; import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition; @@ -39,9 +42,10 @@ /** - * A handler to process mandatory warnings, setting up a deferred diagnostic + * An aggregator for mandatory warnings, setting up a deferred diagnostic * to be printed at the end of the compilation if some warnings get suppressed - * because too many warnings have already been generated. + * because the lint category is not enabled or too many warnings have already + * been generated. * *

* Note that the SuppressWarnings annotation can be used to suppress warnings @@ -58,7 +62,7 @@ * This code and its internal interfaces are subject to change or * deletion without notice. */ -public class MandatoryWarningHandler { +class MandatoryWarningAggregator { /** * The kinds of different deferred diagnostics that might be generated @@ -105,64 +109,51 @@ private enum DeferredDiagnosticKind { /** - * Create a handler for mandatory warnings. + * Create an aggregator for mandatory warnings. * * @param log The log on which to generate any diagnostics * @param source Associated source file, or null for none - * @param verbose Specify whether or not detailed messages about - * individual instances should be given, or whether an aggregate - * message should be generated at the end of the compilation. - * Typically set via -Xlint:option. - * @param enforceMandatory - * True if mandatory warnings and notes are being enforced. * @param lc The lint category for all warnings */ - public MandatoryWarningHandler(Log log, Source source, boolean verbose, boolean enforceMandatory, LintCategory lc) { - this(log, source, verbose, enforceMandatory, lc, null); + public MandatoryWarningAggregator(Log log, Source source, LintCategory lc) { + this(log, source, lc, null); } /** - * Create a handler for mandatory warnings. + * Create an aggregator for mandatory warnings. * * @param log The log on which to generate any diagnostics * @param source Associated source file, or null for none - * @param verbose Specify whether or not detailed messages about - * individual instances should be given, or whether an aggregate - * message should be generated at the end of the compilation. - * Typically set via -Xlint:option. - * @param enforceMandatory - * True if mandatory warnings and notes are being enforced. * @param lc The lint category for all warnings * @param prefix A common prefix for the set of message keys for the messages * that may be generated, or null to infer from the lint category. */ - public MandatoryWarningHandler(Log log, Source source, boolean verbose, boolean enforceMandatory, LintCategory lc, String prefix) { + public MandatoryWarningAggregator(Log log, Source source, LintCategory lc, String prefix) { this.log = log; this.source = source; - this.verbose = verbose; this.prefix = prefix != null ? prefix : lc.option; - this.enforceMandatory = enforceMandatory; this.lintCategory = lc; } /** - * Report a mandatory warning. + * Aggregate a mandatory warning and determine whether to emit it. * - * @param pos source code position - * @param warnKey lint warning + * @param diagnostic the mandatory warning + * @param verbose whether the warning's lint category is enabled + * @return true if diagnostic should be emitted, otherwise false */ - public void report(DiagnosticPosition pos, LintWarning warnKey) { + public boolean aggregate(JCDiagnostic diagnostic, boolean verbose) { + Assert.check(diagnostic.isMandatory()); + Assert.check(diagnostic.getLintCategory() == lintCategory); JavaFileObject currentSource = log.currentSourceFile(); - Assert.check(warnKey.getLintCategory() == lintCategory); - if (verbose) { if (sourcesWithReportedWarnings == null) sourcesWithReportedWarnings = new HashSet<>(); - if (log.nwarnings < log.MaxWarnings) { // generate message and remember the source file - logMandatoryWarning(pos, warnKey); sourcesWithReportedWarnings.add(currentSource); + anyWarningEmitted = true; + return true; } else if (deferredDiagnosticKind == null) { // set up deferred message if (sourcesWithReportedWarnings.contains(currentSource)) { @@ -194,30 +185,36 @@ public void report(DiagnosticPosition pos, LintWarning warnKey) { deferredDiagnosticArg = null; } } + return false; } /** - * Report any diagnostic that might have been deferred by previous calls of report(). + * Build and return any accumulated aggregation notes. */ - public void reportDeferredDiagnostic() { + public List aggregationNotes() { + List list = new ArrayList<>(2); if (deferredDiagnosticKind != null) { if (deferredDiagnosticArg == null) { if (source != null) { - logMandatoryNote(deferredDiagnosticSource, deferredDiagnosticKind.getKey(prefix), source); + addNote(list, deferredDiagnosticSource, deferredDiagnosticKind.getKey(prefix), source); } else { - logMandatoryNote(deferredDiagnosticSource, deferredDiagnosticKind.getKey(prefix)); + addNote(list, deferredDiagnosticSource, deferredDiagnosticKind.getKey(prefix)); } } else { if (source != null) { - logMandatoryNote(deferredDiagnosticSource, deferredDiagnosticKind.getKey(prefix), deferredDiagnosticArg, source); + addNote(list, deferredDiagnosticSource, deferredDiagnosticKind.getKey(prefix), deferredDiagnosticArg, source); } else { - logMandatoryNote(deferredDiagnosticSource, deferredDiagnosticKind.getKey(prefix), deferredDiagnosticArg); + addNote(list, deferredDiagnosticSource, deferredDiagnosticKind.getKey(prefix), deferredDiagnosticArg); } } - - if (!verbose) - logMandatoryNote(deferredDiagnosticSource, prefix + ".recompile"); + if (!anyWarningEmitted) + addNote(list, deferredDiagnosticSource, prefix + ".recompile"); } + return list; + } + + private void addNote(List list, JavaFileObject file, String msg, Object... args) { + list.add(log.diags.mandatoryNote(log.getSource(file), new Note("compiler", msg, args))); } /** @@ -226,12 +223,6 @@ public void reportDeferredDiagnostic() { private final Log log; private final Source source; - /** - * Whether or not to report individual warnings, or simply to report a - * single aggregate warning at the end of the compilation. - */ - private final boolean verbose; - /** * The common prefix for all I18N message keys generated by this handler. */ @@ -268,9 +259,10 @@ public void reportDeferredDiagnostic() { private Object deferredDiagnosticArg; /** - * True if mandatory warnings and notes are being enforced. + * Whether we have actually emitted a warning or just deferred everything. + * In the latter case, the "recompile" notice is included in the summary. */ - private final boolean enforceMandatory; + private boolean anyWarningEmitted; /** * A LintCategory to be included in point-of-use diagnostics to indicate @@ -278,28 +270,6 @@ public void reportDeferredDiagnostic() { */ private final LintCategory lintCategory; - /** - * Reports a mandatory warning to the log. If mandatory warnings - * are not being enforced, treat this as an ordinary warning. - */ - private void logMandatoryWarning(DiagnosticPosition pos, LintWarning warnKey) { - if (enforceMandatory) - log.mandatoryWarning(pos, warnKey); - else - log.warning(pos, warnKey); - } - - /** - * Reports a mandatory note to the log. If mandatory notes are - * not being enforced, treat this as an ordinary note. - */ - private void logMandatoryNote(JavaFileObject file, String msg, Object... args) { - if (enforceMandatory) - log.mandatoryNote(file, new Note("compiler", msg, args)); - else - log.note(file, new Note("compiler", msg, args)); - } - public void clear() { sourcesWithReportedWarnings = null; deferredDiagnosticKind = null; diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/util/Options.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/util/Options.java index 63f5b0ca75abb..cbc69feb421ab 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/util/Options.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/util/Options.java @@ -172,55 +172,72 @@ public boolean isUnset(Option option, String value) { } /** - * Check whether the given lint category is explicitly enabled or disabled. + * Determine if a specific {@link LintCategory} is enabled via a custom + * option flag of the form {@code -Flag}, {@code -Flag:all}, or {@code -Flag:key}. * *

- * If the category is neither enabled nor disabled, return the given default value. + * Note: It's possible the category was also disabled; this method does not check that. * - * @param option the plain (non-custom) option + * @param option the plain (non-custom) version of the option (e.g., {@link Option#XLINT}) * @param lc the {@link LintCategory} in question - * @param defaultValue presumed default value - * @return true if {@code lc} would be included + * @return true if {@code lc} has been enabled */ - 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; + public boolean isEnabled(Option option, LintCategory lc) { + Option custom = option.getCustom(); + return isExplicitlyEnabled(option, lc) || isSet(custom) || isSet(custom, Option.LINT_CUSTOM_ALL); } /** - * Determine if a specific {@link LintCategory} was explicitly enabled via a custom option flag - * of the form {@code -Flag:all} or {@code -Flag:key}. + * Determine if a specific {@link LintCategory} is disabled via a custom + * option flag of the form {@code -Flag:none} or {@code -Flag:-key}. + * + *

+ * Note: It's possible the category was also enabled; this method does not check that. * - * @param option the option + * @param option the plain (non-custom) version of the option (e.g., {@link Option#XLINT}) + * @param lc the {@link LintCategory} in question + * @return true if {@code lc} has been disabled + */ + public boolean isDisabled(Option option, LintCategory lc) { + return isExplicitlyDisabled(option, lc) || isSet(option.getCustom(), Option.LINT_CUSTOM_NONE); + } + + /** + * Determine if a specific {@link LintCategory} is explicitly enabled via a custom + * option flag of the form {@code -Flag:key}. + * + *

+ * Note: This does not check for option flags of the form {@code -Flag} or {@code -Flag:all}. + * + *

+ * Note: It's possible the category was also disabled; this method does not check that. + * + * @param option the plain (non-custom) version of the option (e.g., {@link Option#XLINT}) * @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); + Option customOption = option.getCustom(); + return lc.optionList.stream().anyMatch(alias -> isSet(customOption, alias)); } /** - * Determine if a specific {@link LintCategory} was explicitly disabled via a custom option flag - * of the form {@code -Flag:none} or {@code -Flag:-key}. + * Determine if a specific {@link LintCategory} is explicitly disabled via a custom + * option flag of the form {@code -Flag:-key}. * - * @param option the option + *

+ * Note: This does not check for an option flag of the form {@code -Flag:none}. + * + *

+ * Note: It's possible the category was also enabled; this method does not check that. + * + * @param option the plain (non-custom) version of the option (e.g., {@link Option#XLINT}) * @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); + Option customOption = option.getCustom(); + return lc.optionList.stream().anyMatch(alias -> isSet(customOption, "-" + alias)); } public void put(String name, String value) { diff --git a/src/jdk.compiler/share/classes/module-info.java b/src/jdk.compiler/share/classes/module-info.java index 33cff9379f2fa..13186a9c1afab 100644 --- a/src/jdk.compiler/share/classes/module-info.java +++ b/src/jdk.compiler/share/classes/module-info.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 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 @@ -187,6 +187,7 @@ * and interfaces * {@code static} accessing a static member using an instance * {@code strictfp} unnecessary use of the {@code strictfp} modifier + * {@code suppression} unnecessary suppressions in {@code @SuppressWarnings} annotations * {@code synchronization} synchronization attempts on instances of value-based classes; * this key is a deprecated alias for {@code identity}, which has * the same uses and effects. Users are encouraged to use the diff --git a/src/jdk.compiler/share/man/javac.md b/src/jdk.compiler/share/man/javac.md index 997023487b0ae..34071992647a7 100644 --- a/src/jdk.compiler/share/man/javac.md +++ b/src/jdk.compiler/share/man/javac.md @@ -1,5 +1,5 @@ --- -# Copyright (c) 1994, 2024, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 1994, 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 @@ -648,6 +648,9 @@ file system locations may be directories, JAR files or JMOD files. - `strictfp`: Warns about unnecessary use of the `strictfp` modifier. + - `suppression`: Warns about recognized `@SuppressWarnings` values that + don't actually suppress any warnings. + - `synchronization`: Warns about synchronization attempts on instances 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 diff --git a/test/langtools/tools/javac/6304921/TestLog.java b/test/langtools/tools/javac/6304921/TestLog.java index 41695554a8800..8f00a14b9e237 100644 --- a/test/langtools/tools/javac/6304921/TestLog.java +++ b/test/langtools/tools/javac/6304921/TestLog.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -41,7 +41,7 @@ import com.sun.tools.javac.file.JavacFileManager; import com.sun.tools.javac.parser.Parser; import com.sun.tools.javac.parser.ParserFactory; -import com.sun.tools.javac.resources.CompilerProperties.LintWarnings; +import com.sun.tools.javac.resources.CompilerProperties.Warnings; import com.sun.tools.javac.tree.EndPosTable; import com.sun.tools.javac.tree.JCTree; import com.sun.tools.javac.tree.TreeScanner; @@ -130,10 +130,11 @@ public void visitIf(JCTree.JCIf tree) { log.error(tree.pos(), Errors.NotStmt); log.error(nil, Errors.NotStmt); - log.warning(LintWarnings.DivZero); - log.warning(tree.pos, LintWarnings.DivZero); - log.warning(tree.pos(), LintWarnings.DivZero); - log.warning(nil, LintWarnings.DivZero); + // some warnings that will be emitted during parsing + log.warning(Warnings.ExtraneousSemicolon); + log.warning(tree.pos, Warnings.ExtraneousSemicolon); + log.warning(tree.pos(), Warnings.ExtraneousSemicolon); + log.warning(nil, Warnings.ExtraneousSemicolon); } private Log log; diff --git a/test/langtools/tools/javac/ImportModule.java b/test/langtools/tools/javac/ImportModule.java index f088dbef6582f..d26b73c02895f 100644 --- a/test/langtools/tools/javac/ImportModule.java +++ b/test/langtools/tools/javac/ImportModule.java @@ -730,8 +730,8 @@ public class C {} .getOutputLines(Task.OutputKind.DIRECT); List expectedErrors = List.of( - "module-info.java:3:18: compiler.warn.module.not.found: M1", "module-info.java:6:9: compiler.err.cant.resolve: kindname.class, A, , ", + "module-info.java:3:18: compiler.warn.module.not.found: M1", "1 error", "1 warning" ); diff --git a/test/langtools/tools/javac/OverrideChecks/6400189/T6400189a.out b/test/langtools/tools/javac/OverrideChecks/6400189/T6400189a.out index 2a12111fe4041..23dcddc1e930b 100644 --- a/test/langtools/tools/javac/OverrideChecks/6400189/T6400189a.out +++ b/test/langtools/tools/javac/OverrideChecks/6400189/T6400189a.out @@ -1,4 +1,4 @@ -T6400189a.java:14:35: compiler.warn.unchecked.call.mbr.of.raw.type: getAnnotation(java.lang.Class), java.lang.reflect.Constructor T6400189a.java:14:35: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: java.lang.annotation.Annotation, java.lang.annotation.Documented) +T6400189a.java:14:35: compiler.warn.unchecked.call.mbr.of.raw.type: getAnnotation(java.lang.Class), java.lang.reflect.Constructor 1 error 1 warning diff --git a/test/langtools/tools/javac/OverrideChecks/6400189/T6400189b.out b/test/langtools/tools/javac/OverrideChecks/6400189/T6400189b.out index 904cd3e677f22..91e34b5becad6 100644 --- a/test/langtools/tools/javac/OverrideChecks/6400189/T6400189b.out +++ b/test/langtools/tools/javac/OverrideChecks/6400189/T6400189b.out @@ -1,4 +1,4 @@ -T6400189b.java:24:24: compiler.warn.unchecked.call.mbr.of.raw.type: m(T6400189b), T6400189b.B T6400189b.java:24:24: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: java.lang.Object, java.lang.Integer) +T6400189b.java:24:24: compiler.warn.unchecked.call.mbr.of.raw.type: m(T6400189b), T6400189b.B 1 error 1 warning diff --git a/test/langtools/tools/javac/danglingDocComments/DanglingDocCommentsClass.enabled.out b/test/langtools/tools/javac/danglingDocComments/DanglingDocCommentsClass.enabled.out index 78285aa45d996..b2dde3ff0ef31 100644 --- a/test/langtools/tools/javac/danglingDocComments/DanglingDocCommentsClass.enabled.out +++ b/test/langtools/tools/javac/danglingDocComments/DanglingDocCommentsClass.enabled.out @@ -1,7 +1,7 @@ +DanglingDocCommentsClass.java:15:5: compiler.warn.dangling.doc.comment +DanglingDocCommentsClass.java:19:5: compiler.warn.dangling.doc.comment DanglingDocCommentsClass.java:10:1: compiler.warn.dangling.doc.comment DanglingDocCommentsClass.java:13:1: compiler.warn.dangling.doc.comment DanglingDocCommentsClass.java:14:8: compiler.warn.dangling.doc.comment DanglingDocCommentsClass.java:14:69: compiler.warn.dangling.doc.comment -DanglingDocCommentsClass.java:15:5: compiler.warn.dangling.doc.comment -DanglingDocCommentsClass.java:19:5: compiler.warn.dangling.doc.comment 6 warnings diff --git a/test/langtools/tools/javac/danglingDocComments/DanglingDocCommentsClass_Line.enabled.out b/test/langtools/tools/javac/danglingDocComments/DanglingDocCommentsClass_Line.enabled.out index 3ed89c5f5bc23..e97bd630ad8b0 100644 --- a/test/langtools/tools/javac/danglingDocComments/DanglingDocCommentsClass_Line.enabled.out +++ b/test/langtools/tools/javac/danglingDocComments/DanglingDocCommentsClass_Line.enabled.out @@ -1,7 +1,7 @@ +DanglingDocCommentsClass_Line.java:21:5: compiler.warn.dangling.doc.comment +DanglingDocCommentsClass_Line.java:26:5: compiler.warn.dangling.doc.comment DanglingDocCommentsClass_Line.java:11:1: compiler.warn.dangling.doc.comment DanglingDocCommentsClass_Line.java:15:1: compiler.warn.dangling.doc.comment DanglingDocCommentsClass_Line.java:17:5: compiler.warn.dangling.doc.comment DanglingDocCommentsClass_Line.java:19:9: compiler.warn.dangling.doc.comment -DanglingDocCommentsClass_Line.java:21:5: compiler.warn.dangling.doc.comment -DanglingDocCommentsClass_Line.java:26:5: compiler.warn.dangling.doc.comment 6 warnings diff --git a/test/langtools/tools/javac/danglingDocComments/DanglingDocCommentsClass_Mixed.enabled.out b/test/langtools/tools/javac/danglingDocComments/DanglingDocCommentsClass_Mixed.enabled.out index 1eda729da194d..13e1549357963 100644 --- a/test/langtools/tools/javac/danglingDocComments/DanglingDocCommentsClass_Mixed.enabled.out +++ b/test/langtools/tools/javac/danglingDocComments/DanglingDocCommentsClass_Mixed.enabled.out @@ -1,4 +1,4 @@ -DanglingDocCommentsClass_Mixed.java:13:1: compiler.warn.dangling.doc.comment DanglingDocCommentsClass_Mixed.java:17:5: compiler.warn.dangling.doc.comment DanglingDocCommentsClass_Mixed.java:21:5: compiler.warn.dangling.doc.comment +DanglingDocCommentsClass_Mixed.java:13:1: compiler.warn.dangling.doc.comment 3 warnings diff --git a/test/langtools/tools/javac/danglingDocComments/DanglingDocCommentsEnum.enabled.out b/test/langtools/tools/javac/danglingDocComments/DanglingDocCommentsEnum.enabled.out index ddf1b2964de73..33938e86078f8 100644 --- a/test/langtools/tools/javac/danglingDocComments/DanglingDocCommentsEnum.enabled.out +++ b/test/langtools/tools/javac/danglingDocComments/DanglingDocCommentsEnum.enabled.out @@ -1,8 +1,8 @@ +DanglingDocCommentsEnum.java:16:5: compiler.warn.dangling.doc.comment +DanglingDocCommentsEnum.java:22:5: compiler.warn.dangling.doc.comment +DanglingDocCommentsEnum.java:28:5: compiler.warn.dangling.doc.comment DanglingDocCommentsEnum.java:10:1: compiler.warn.dangling.doc.comment DanglingDocCommentsEnum.java:13:1: compiler.warn.dangling.doc.comment DanglingDocCommentsEnum.java:14:8: compiler.warn.dangling.doc.comment DanglingDocCommentsEnum.java:14:67: compiler.warn.dangling.doc.comment -DanglingDocCommentsEnum.java:16:5: compiler.warn.dangling.doc.comment -DanglingDocCommentsEnum.java:22:5: compiler.warn.dangling.doc.comment -DanglingDocCommentsEnum.java:28:5: compiler.warn.dangling.doc.comment 7 warnings diff --git a/test/langtools/tools/javac/diags/examples/WarnUnnecessaryLintSuppression.java b/test/langtools/tools/javac/diags/examples/WarnUnnecessaryLintSuppression.java new file mode 100644 index 0000000000000..fa20afe37646d --- /dev/null +++ b/test/langtools/tools/javac/diags/examples/WarnUnnecessaryLintSuppression.java @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +// key: compiler.warn.unnecessary.warning.suppression +// options: -Xlint:suppression + +@SuppressWarnings("unchecked") +class X { +} diff --git a/test/langtools/tools/javac/generics/diamond/7188968/T7188968.out b/test/langtools/tools/javac/generics/diamond/7188968/T7188968.out index efceb84c8c7fb..3c93e1711d64c 100644 --- a/test/langtools/tools/javac/generics/diamond/7188968/T7188968.out +++ b/test/langtools/tools/javac/generics/diamond/7188968/T7188968.out @@ -1,11 +1,11 @@ T7188968.java:20:20: compiler.err.cant.resolve.location: kindname.variable, unknown, , , (compiler.misc.location: kindname.class, T7188968, null) -T7188968.java:20:9: compiler.warn.unchecked.call.mbr.of.raw.type: T7188968.Foo(java.util.List,java.lang.Object), T7188968.Foo T7188968.java:21:20: compiler.err.cant.resolve.location: kindname.variable, unknown, , , (compiler.misc.location: kindname.class, T7188968, null) -T7188968.java:21:29: compiler.warn.unchecked.call.mbr.of.raw.type: T7188968.Foo(java.util.List,java.lang.Object), T7188968.Foo T7188968.java:22:22: compiler.err.cant.resolve.location: kindname.variable, unknown, , , (compiler.misc.location: kindname.class, T7188968, null) +T7188968.java:23:24: compiler.err.cant.resolve.location: kindname.variable, unknown, , , (compiler.misc.location: kindname.class, T7188968, null) +T7188968.java:20:9: compiler.warn.unchecked.call.mbr.of.raw.type: T7188968.Foo(java.util.List,java.lang.Object), T7188968.Foo +T7188968.java:21:29: compiler.warn.unchecked.call.mbr.of.raw.type: T7188968.Foo(java.util.List,java.lang.Object), T7188968.Foo T7188968.java:22:9: compiler.warn.unchecked.meth.invocation.applied: kindname.constructor, , java.util.List,java.lang.Object, java.util.List,unknown, kindname.class, T7188968.Foo T7188968.java:22:19: compiler.warn.prob.found.req: (compiler.misc.unchecked.assign), java.util.List, java.util.List -T7188968.java:23:24: compiler.err.cant.resolve.location: kindname.variable, unknown, , , (compiler.misc.location: kindname.class, T7188968, null) T7188968.java:23:20: compiler.warn.unchecked.meth.invocation.applied: kindname.method, makeFoo, java.util.List,java.lang.Object, java.util.List,unknown, kindname.class, T7188968.Foo T7188968.java:23:21: compiler.warn.prob.found.req: (compiler.misc.unchecked.assign), java.util.List, java.util.List 4 errors diff --git a/test/langtools/tools/javac/lambda/TargetType22.out b/test/langtools/tools/javac/lambda/TargetType22.out index d94b2cc60b386..c19aef2411fc7 100644 --- a/test/langtools/tools/javac/lambda/TargetType22.out +++ b/test/langtools/tools/javac/lambda/TargetType22.out @@ -1,4 +1,4 @@ -TargetType22.java:29:21: compiler.warn.unchecked.varargs.non.reifiable.type: A TargetType22.java:40:9: compiler.err.ref.ambiguous: call, kindname.method, call(TargetType22.Sam1), TargetType22, kindname.method, call(TargetType22.SamX), TargetType22 +TargetType22.java:29:21: compiler.warn.unchecked.varargs.non.reifiable.type: A 1 error 1 warning diff --git a/test/langtools/tools/javac/lint/LexicalLintNesting.java b/test/langtools/tools/javac/lint/LexicalLintNesting.java new file mode 100644 index 0000000000000..f167921df816c --- /dev/null +++ b/test/langtools/tools/javac/lint/LexicalLintNesting.java @@ -0,0 +1,170 @@ +/* + * @test /nodynamiccopyright/ + * @bug 8224228 + * @summary Verify lexical lint warnings handle nested declarations with SuppressWarnings correctly + * @compile/fail/ref=LexicalLintNesting.out -XDrawDiagnostics -Xlint:text-blocks -Werror LexicalLintNesting.java + */ + +//@SuppressWarnings("text-blocks") +public class LexicalLintNesting { + + //@SuppressWarnings("text-blocks") + /* WARNING HERE */ String s1 = """ + trailing space here:\u0020 + """; + + @SuppressWarnings("text-blocks") + String s2 = """ + trailing space here:\u0020 + """; + + //@SuppressWarnings("text-blocks") + public static class Nested1 { + + @SuppressWarnings("text-blocks") + String s3 = """ + trailing space here:\u0020 + """; + + //@SuppressWarnings("text-blocks") + /* WARNING HERE */ String s4 = """ + trailing space here:\u0020 + """; + + @SuppressWarnings("text-blocks") + public static class Nested1A { + + //@SuppressWarnings("text-blocks") + String s5 = """ + trailing space here:\u0020 + """; + + @SuppressWarnings("text-blocks") + String s6 = """ + trailing space here:\u0020 + """; + + } + + @SuppressWarnings("text-blocks") + String s7 = """ + trailing space here:\u0020 + """; + + //@SuppressWarnings("text-blocks") + /* WARNING HERE */ String s8 = """ + trailing space here:\u0020 + """; + + //@SuppressWarnings("text-blocks") + public static class Nested1B { + + @SuppressWarnings("text-blocks") + String s9 = """ + trailing space here:\u0020 + """; + + //@SuppressWarnings("text-blocks") + /* WARNING HERE */ String s10 = """ + trailing space here:\u0020 + """; + + } + + @SuppressWarnings("text-blocks") + String s11 = """ + trailing space here:\u0020 + """; + + //@SuppressWarnings("text-blocks") + /* WARNING HERE */ String s12 = """ + trailing space here:\u0020 + """; + + } + + @SuppressWarnings("text-blocks") + String s13 = """ + trailing space here:\u0020 + """; + + //@SuppressWarnings("text-blocks") + /* WARNING HERE */ String s14 = """ + trailing space here:\u0020 + """; + + @SuppressWarnings("text-blocks") + public static class Nested2 { + + @SuppressWarnings("text-blocks") + String s15 = """ + trailing space here:\u0020 + """; + + //@SuppressWarnings("text-blocks") + String s16 = """ + trailing space here:\u0020 + """; + + @SuppressWarnings("text-blocks") + public static class Nested2A { + + //@SuppressWarnings("text-blocks") + String s17 = """ + trailing space here:\u0020 + """; + + @SuppressWarnings("text-blocks") + String s18 = """ + trailing space here:\u0020 + """; // SHOULD NOT get a warning here + + } + + @SuppressWarnings("text-blocks") + String s19 = """ + trailing space here:\u0020 + """; + + //@SuppressWarnings("text-blocks") + String s20 = """ + trailing space here:\u0020 + """; + + //@SuppressWarnings("text-blocks") + public static class Nested2B { + + @SuppressWarnings("text-blocks") + String s21 = """ + trailing space here:\u0020 + """; + + //@SuppressWarnings("text-blocks") + String s22 = """ + trailing space here:\u0020 + """; + + } + + @SuppressWarnings("text-blocks") + String s23 = """ + trailing space here:\u0020 + """; + + //@SuppressWarnings("text-blocks") + String s24 = """ + trailing space here:\u0020 + """; + + } + + //@SuppressWarnings("text-blocks") + /* WARNING HERE */ String s25 = """ + trailing space here:\u0020 + """; + + @SuppressWarnings("text-blocks") + String s26 = """ + trailing space here:\u0020 + """; +} diff --git a/test/langtools/tools/javac/lint/LexicalLintNesting.out b/test/langtools/tools/javac/lint/LexicalLintNesting.out new file mode 100644 index 0000000000000..b16db47cf528e --- /dev/null +++ b/test/langtools/tools/javac/lint/LexicalLintNesting.out @@ -0,0 +1,10 @@ +LexicalLintNesting.java:12:36: compiler.warn.trailing.white.space.will.be.removed +LexicalLintNesting.java:30:40: compiler.warn.trailing.white.space.will.be.removed +LexicalLintNesting.java:55:40: compiler.warn.trailing.white.space.will.be.removed +LexicalLintNesting.java:68:45: compiler.warn.trailing.white.space.will.be.removed +LexicalLintNesting.java:80:41: compiler.warn.trailing.white.space.will.be.removed +LexicalLintNesting.java:92:37: compiler.warn.trailing.white.space.will.be.removed +LexicalLintNesting.java:162:37: compiler.warn.trailing.white.space.will.be.removed +- compiler.err.warnings.and.werror +1 error +7 warnings diff --git a/test/langtools/tools/javac/lint/LintOptions.java b/test/langtools/tools/javac/lint/LintOptions.java new file mode 100644 index 0000000000000..10def72a31a5e --- /dev/null +++ b/test/langtools/tools/javac/lint/LintOptions.java @@ -0,0 +1,38 @@ +/* + * 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. + */ + +/* + * @test + * @bug 8359596 + * @summary Verify behavior when both "-Xlint:options" and "-Xlint:-options" are given + * @compile/fail/ref=LintOptions.out -Werror -XDrawDiagnostics -source 21 -target 21 LintOptions.java + * @compile/fail/ref=LintOptions.out -Werror -XDrawDiagnostics -source 21 -target 21 -Xlint:options LintOptions.java + * @compile -Werror -XDrawDiagnostics -source 21 -target 21 -Xlint:-options LintOptions.java + * @compile -Werror -XDrawDiagnostics -source 21 -target 21 -Xlint:options -Xlint:-options LintOptions.java + * @compile -Werror -XDrawDiagnostics -source 21 -target 21 -Xlint:none LintOptions.java + * @compile -Werror -XDrawDiagnostics -source 21 -target 21 -Xlint:options -Xlint:none LintOptions.java + */ +class LintOptions { +} diff --git a/test/langtools/tools/javac/lint/LintOptions.out b/test/langtools/tools/javac/lint/LintOptions.out new file mode 100644 index 0000000000000..020c626ee5c20 --- /dev/null +++ b/test/langtools/tools/javac/lint/LintOptions.out @@ -0,0 +1,4 @@ +- compiler.warn.source.no.system.modules.path: 21, (compiler.misc.source.no.system.modules.path.with.target: 21, 21) +- compiler.err.warnings.and.werror +1 error +1 warning diff --git a/test/langtools/tools/javac/lint/SuppressionWarningTest.java b/test/langtools/tools/javac/lint/SuppressionWarningTest.java new file mode 100644 index 0000000000000..71841d7c7ebfa --- /dev/null +++ b/test/langtools/tools/javac/lint/SuppressionWarningTest.java @@ -0,0 +1,981 @@ +/* + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/** + * @test + * @bug 8344159 + * @summary Test "suppression" lint warnings + * @library /tools/lib + * @modules + * jdk.compiler/com.sun.tools.javac.api + * jdk.compiler/com.sun.tools.javac.code + * jdk.compiler/com.sun.tools.javac.main + * @build toolbox.ToolBox toolbox.JavacTask toolbox.JarTask + * @run main SuppressionWarningTest + */ + +import java.io.File; +import java.io.IOException; +import java.io.PrintWriter; +import java.io.StringWriter; +import java.lang.reflect.Method; +import java.nio.file.FileVisitResult; +import java.nio.file.Files; +import java.nio.file.LinkOption; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.SimpleFileVisitor; +import java.nio.file.attribute.BasicFileAttributes; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.EnumMap; +import java.util.EnumSet; +import java.util.List; +import java.util.Objects; +import java.util.Optional; +import java.util.Set; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import com.sun.tools.javac.code.Lint; +import com.sun.tools.javac.code.Lint.LintCategory; +import com.sun.tools.javac.code.Source; + +import toolbox.JarTask; +import toolbox.JavacTask; +import toolbox.Task.Mode; +import toolbox.Task; +import toolbox.TestRunner; +import toolbox.ToolBox; + +import static com.sun.tools.javac.code.Lint.LintCategory.*; + +public class SuppressionWarningTest extends TestRunner { + + // Test cases for testSuppressWarnings() + public static final List SUPPRESS_WARNINGS_TEST_CASES = Stream.of(LintCategory.values()) + .filter(category -> category.suppressionTracking) + .map(category -> switch (category) { + case AUXILIARYCLASS -> new SuppressTest(category, + "compiler.warn.auxiliary.class.accessed.from.outside.of.its.source.file", + null, + """ + public class Class1 { } + class AuxClass { } + """, + """ + @OUTER@ + public class Class2 { + @INNER@ + public Object obj = new AuxClass(); + } + """ + ); + + case CAST -> new SuppressTest(category, + "compiler.warn.redundant.cast", + null, + """ + @OUTER@ + public class Test { + @INNER@ + public Object obj = (Object)new Object(); + } + """ + ); + + case CLASSFILE -> null; // skip, too hard to simluate + + case DANGLING_DOC_COMMENTS -> new SuppressTest(category, + "compiler.warn.dangling.doc.comment", + null, + """ + @OUTER@ + public class Test { + /** Dangling comment */ + /** Javadoc comment */ + @INNER@ + public void foo() { + } + } + """ + ); + + case DEPRECATION -> new SuppressTest(category, + "compiler.warn.has.been.deprecated", + null, + """ + public class Super { + @Deprecated + public void foo() { } + } + """, + """ + @OUTER@ + public class Sub extends Super { + @INNER@ + @Override + public void foo() { } + } + """ + ); + + case DEP_ANN -> new SuppressTest(category, + "compiler.warn.missing.deprecated.annotation", + null, + """ + @OUTER@ + public class Test { + @INNER@ + public class TestSub { + /** @deprecated */ + public void method() { } + } + } + """ + ); + + case DIVZERO -> new SuppressTest(category, + "compiler.warn.div.zero", + null, + """ + @OUTER@ + public class Test { + @INNER@ + public int method() { + return 1/0; + } + } + """ + ); + + case EMPTY -> new SuppressTest(category, + "compiler.warn.empty.if", + null, + """ + @OUTER@ + public class Test { + @INNER@ + public void method(boolean x) { + if (x); + } + } + """ + ); + + case EXPORTS -> new SuppressTest(category, + "compiler.warn.leaks.not.accessible", + null, + """ + module mod { + exports pkg1; + } + """, + """ + // @MODULE@:mod + package pkg1; + @OUTER@ + public class Class1 { + @INNER@ + public pkg2.Class2 obj2; // warning here + } + """, + """ + // @MODULE@:mod + package pkg2; + public class Class2 { + } + """ + ); + + case FALLTHROUGH -> new SuppressTest(category, + "compiler.warn.possible.fall-through.into.case", + null, + """ + @OUTER@ + public class Test { + @INNER@ + public void method(int x) { + switch (x) { + case 1: + System.out.println(1); + default: + System.out.println(0); + } + } + } + """ + ); + + case FINALLY -> new SuppressTest(category, + "compiler.warn.finally.cannot.complete", + null, + """ + @OUTER@ + public class Test { + @INNER@ + public void method(int x) { + try { + System.out.println(x); + } finally { + throw new RuntimeException(); + } + } + } + """ + ); + + case INCUBATING -> null; // skip, too hard to simluate reliably over time + + case LOSSY_CONVERSIONS -> new SuppressTest(category, + "compiler.warn.possible.loss.of.precision", + null, + """ + @OUTER@ + public class Test { + @INNER@ + public void method() { + long b = 1L; + b += 0.1 * 3L; + } + } + """ + ); + + case MISSING_EXPLICIT_CTOR -> new SuppressTest(category, + "compiler.warn.missing-explicit-ctor", + null, + """ + module mod { + exports pkg1; + } + """, + """ + package pkg1; + @OUTER@ + public class Class1 { + public Class1(int x) { + } + @INNER@ + public static class Sub { + } + } + """ + ); + + case MODULE -> new SuppressTest(category, + "compiler.warn.poor.choice.for.module.name", + null, + """ + @OUTER@ + module mod0 { + } + """ + ); + + case OPENS -> new SuppressTest(category, + "compiler.warn.package.empty.or.not.found", + null, + """ + @OUTER@ + module mod { + opens pkg1; + } + """ + ); + + // This test case only works on MacOS + case OUTPUT_FILE_CLASH -> + System.getProperty("os.name").startsWith("Mac") ? + new SuppressTest(category, + "compiler.warn.output.file.clash", + null, + """ + @OUTER@ + public class Test { + interface Cafe\u0301 { // macos normalizes "e" + U0301 -> U00e9 + } + interface Caf\u00e9 { + } + } + """ + ) : null; + + case OVERLOADS -> new SuppressTest(category, + "compiler.warn.potentially.ambiguous.overload", + null, + """ + import java.util.function.*; + @OUTER@ + public class Super { + public void foo(IntConsumer c) { + } + @INNER@ + public void foo(Consumer c) { + } + } + """ + ); + + case OVERRIDES -> new SuppressTest(category, + "compiler.warn.override.equals.but.not.hashcode", + null, + """ + @OUTER@ + public class Test { + @INNER@ + public class Test2 { + public boolean equals(Object obj) { + return false; + } + } + } + """ + ); + + case PROCESSING -> null; // skip for now + + case RAW -> new SuppressTest(category, + "compiler.warn.raw.class.use", + null, + """ + @OUTER@ + public class Test { + @INNER@ + public void foo() { + Iterable i = null; + } + } + """ + ); + + case REMOVAL -> new SuppressTest(category, + "compiler.warn.has.been.deprecated.for.removal", + null, + """ + public class Super { + @Deprecated(forRemoval = true) + public void foo() { } + } + """, + """ + @OUTER@ + public class Sub extends Super { + @INNER@ + @Override + public void foo() { } + } + """ + ); + + // This test case requires special support; see testSuppressWarnings() + case REQUIRES_AUTOMATIC -> new SuppressTest(category, + "compiler.warn.requires.automatic", + null, + """ + @OUTER@ + module m1x { + requires randomjar; + } + """ + ); + + // This test case requires special support; see testSuppressWarnings() + case REQUIRES_TRANSITIVE_AUTOMATIC -> new SuppressTest(category, + "compiler.warn.requires.transitive.automatic", + null, + """ + @OUTER@ + module m1x { + requires transitive randomjar; + } + """ + ); + + case SERIAL -> new SuppressTest(category, + "compiler.warn.missing.SVUID", + null, + """ + @OUTER@ + public class Test { + @INNER@ + public static class Inner implements java.io.Serializable { + public int x; + } + } + """ + ); + + case STATIC -> new SuppressTest(category, + "compiler.warn.static.not.qualified.by.type", + null, + """ + @OUTER@ + public class Test { + public static void foo() { + } + @INNER@ + public void bar() { + this.foo(); + } + } + """ + ); + + case STRICTFP -> new SuppressTest(category, + "compiler.warn.strictfp", + null, + """ + @OUTER@ + public class Test { + @INNER@ + public strictfp void foo() { + } + } + """ + ); + + case IDENTITY -> new SuppressTest(category, + "compiler.warn.attempt.to.synchronize.on.instance.of.value.based.class", + null, + """ + @OUTER@ + public class Outer { + @INNER@ + public void foo() { + Integer i = 42; + synchronized (i) { + } + } + } + """ + ); + + case TEXT_BLOCKS -> new SuppressTest(category, + "compiler.warn.trailing.white.space.will.be.removed", + null, + """ + @OUTER@ + public class Test { + public void foo() { + String s = + \"\"\" + add trailing spaces here: + \"\"\"; + } + } + """.replaceAll("add trailing spaces here:", "$0 ") + ); + + case THIS_ESCAPE -> new SuppressTest(category, + "compiler.warn.possible.this.escape", + null, + """ + @OUTER@ + public class Outer { + @INNER@ + public static class Inner { + public Inner() { + leak(); + } + public void leak() { } + } + } + """ + ); + + case TRY -> new SuppressTest(category, + "compiler.warn.try.explicit.close.call", + null, + """ + import java.io.*; + @OUTER@ + public class Outer { + @INNER@ + public void foo() throws IOException { + try (InputStream in = new FileInputStream("x")) { + in.close(); + } + } + } + """ + ); + + case UNCHECKED -> new SuppressTest(category, + "compiler.warn.prob.found.req: (compiler.misc.unchecked.cast.to.type)", + null, + """ + @OUTER@ + public class Test { + public void foo() { + Iterable c = null; + @INNER@ + Iterable t = (Iterable)c, s = null; + } + } + """ + ); + + case VARARGS -> new SuppressTest(category, + "compiler.warn.varargs.unsafe.use.varargs.param", + null, + """ + @OUTER@ + public class Test { + @INNER@ + @SafeVarargs + public static void bar(final T... barArgs) { + baz(barArgs); + } + public static void baz(final T[] bazArgs) { + } + } + """ + ); + + // This test case requires special support; see testSuppressWarnings() + case PREVIEW -> new SuppressTest(category, + "compiler.warn.preview.feature.use", + new String[] { + "--enable-preview", + "-XDforcePreview" + }, + """ + @OUTER@ + public class Test { + @INNER@ + public Test(Object x) { + int value = x instanceof Integer i ? i : -1; + } + } + """ + ); + + case RESTRICTED -> new SuppressTest(category, + "compiler.warn.restricted.method", + null, + """ + @OUTER@ + public class Test { + @INNER@ + public void foo() { + System.load(""); + } + } + """ + ); + + default -> throw new AssertionError("missing test case for " + category); + + }) + .filter(Objects::nonNull) // skip categories with no test case defined + .collect(Collectors.toList()); + + protected final ToolBox tb; + + public SuppressionWarningTest() { + super(System.err); + tb = new ToolBox(); + } + + public static void main(String... args) throws Exception { + SuppressionWarningTest test = new SuppressionWarningTest(); + + // Run parameterized tests + test.runTestsMulti(m -> switch (m.getName()) { + case "testSuppressWarnings" -> SUPPRESS_WARNINGS_TEST_CASES.stream() + .map(testCase -> new Object[] { testCase }); + case "testUselessAnnotation" -> Stream.of(LintCategory.values()) + .filter(category -> category.suppressionTracking) + .map(category -> new Object[] { category }); + case "testSelfSuppression" -> Stream.of(RAW, SUPPRESSION) + .map(category -> new Object[] { category }); + case "testOverloads" -> Stream.of(new Object[0]); // no parameters for this test + case "testThisEscape" -> Stream.of(new Object[0]); // no parameters for this test + default -> throw new AssertionError("missing params for " + m); + }); + } + + // We are testing all combinations of nested @SuppressWarning annotations and lint flags + @Test + public void testSuppressWarnings(SuppressTest test) throws Exception { + + // Setup directories + Path base = Paths.get("testSuppressWarnings"); + resetCompileDirectories(base); + + // Detect if any modules are being compiled; if so we need to create an extra source directory level + Pattern moduleDecl = Pattern.compile("module\\s+(\\S*).*"); + Set moduleNames = test.sources().stream() + .flatMap(source -> Stream.of(source.split("\\n"))) + .map(moduleDecl::matcher) + .filter(Matcher::matches) + .map(matcher -> matcher.group(1)) + .collect(Collectors.toSet()); + + // Special JAR file support for REQUIRES_AUTOMATIC and REQUIRES_TRANSITIVE_AUTOMATIC + Path modulePath = base.resolve("modules"); + resetDirectory(modulePath); + LintCategory category = test.category(); + switch (category) { + case REQUIRES_AUTOMATIC: + case REQUIRES_TRANSITIVE_AUTOMATIC: + + // Compile a simple automatic module (randomjar-1.0) + Path randomJarBase = base.resolve("randomjar"); + tb.writeJavaFiles(getSourcesDir(randomJarBase), "package api; public class Api {}"); + List log = compile(randomJarBase, Task.Expect.SUCCESS, "-Werror"); + if (!log.isEmpty()) { + throw new AssertionError(String.format( + "non-empty log output:%n %s", log.stream().collect(Collectors.joining("\n ")))); + } + + // JAR it up + Path automaticJar = modulePath.resolve("randomjar-1.0.jar"); + new JarTask(tb, automaticJar) + .baseDir(getClassesDir(randomJarBase)) + .files("api/Api.class") + .run(); + break; + + default: + modulePath = null; + break; + }; + + // Create a @SuppressWarnings annotation + String annotation = String.format("@SuppressWarnings(\"%s\")", category.option); + + // See which annotation substitutions this test supports + boolean hasOuterAnnotation = test.sources().stream().anyMatch(source -> source.contains("@OUTER@")); + boolean hasInnerAnnotation = test.sources().stream().anyMatch(source -> source.contains("@INNER@")); + + // Try all combinations of inner and outer @SuppressWarnings + boolean[] booleans = new boolean[] { false, true }; + for (boolean outerAnnotation : booleans) { for (boolean innerAnnotation : booleans) { + + // Skip this scenario if not supported by test case + if ((outerAnnotation && !hasOuterAnnotation) || (innerAnnotation && !hasInnerAnnotation)) + continue; + + // Insert or comment out the @SuppressWarnings annotations in the source templates + String[] sources = test.sources().stream() + .map(source -> source.replace("@OUTER@", + String.format("%s@SuppressWarnings(\"%s\")", outerAnnotation ? "" : "//", category.option))) + .map(source -> source.replace("@INNER@", + String.format("%s@SuppressWarnings(\"%s\")", innerAnnotation ? "" : "//", category.option))) + .toArray(String[]::new); + for (String source : sources) { + Path pkgRoot = getSourcesDir(base); + String moduleName = Optional.of("@MODULE@:(\\S+)") + .map(Pattern::compile) + .map(p -> p.matcher(source)) + .filter(Matcher::find) + .map(m -> m.group(1)) + .orElse(null); + if (moduleName != null) { // add an extra directory for module + if (!moduleNames.contains(moduleName)) + throw new AssertionError(String.format("unknown module \"%s\" in %s", moduleName, category)); + pkgRoot = pkgRoot.resolve(moduleName); + } + tb.writeJavaFiles(pkgRoot, source); + } + + // Try all combinations of lint flags + for (boolean enableCategory : booleans) { // [-]category + for (boolean enableSuppression : booleans) { // [-]suppression + + // Should we expect the warning to be emitted? + boolean expectCategoryWarning = category.annotationSuppression ? + enableCategory && !outerAnnotation && !innerAnnotation : enableCategory; + + // Should we expect the SUPPRESSION warning to be emitted? + boolean expectSuppressionWarning = category.annotationSuppression ? + enableSuppression && outerAnnotation && innerAnnotation : // only if both, outer is redundant + enableSuppression && (outerAnnotation || innerAnnotation); // either one is always redundant + + // Prepare command line flags + ArrayList flags = new ArrayList<>(); + if (modulePath != null) { + flags.add("--module-path"); + flags.add(modulePath.toString()); + } + flags.add("--release"); + flags.add(Source.DEFAULT.name); + flags.addAll(test.compileFlags()); + + ArrayList lints = new ArrayList<>(); + lints.add(String.format("%s%s", enableCategory ? "" : "-", category.option)); + if (enableSuppression) + lints.add(SUPPRESSION.option); + if (!lints.isEmpty()) + flags.add("-Xlint:" + lints.stream().collect(Collectors.joining(","))); + + // Test case description + String description = String.format("[%s] outer=%s inner=%s enable=%s flags=\"%s\"", + category, outerAnnotation, innerAnnotation, enableCategory, + flags.stream().collect(Collectors.joining(" "))); + + // Only print log if test case fails + StringWriter buf = new StringWriter(); + PrintWriter log = new PrintWriter(buf); + try { + + // Logging + log.println(String.format(">>> Test START: %s", description)); + Stream.of(sources).forEach(log::println); + log.println(String.format(">>> expectCategoryWarning=%s", expectCategoryWarning)); + log.println(String.format(">>> expectSuppressionWarning=%s", expectSuppressionWarning)); + + // Compile sources and get log output + List output = compile(base, Task.Expect.SUCCESS, flags.toArray(new String[0])); + + // Scrub insignificant log output + output.removeIf(line -> line.matches("[0-9]+ (error|warning)s?")); + output.removeIf(line -> line.contains("compiler.err.warnings.and.werror")); + output.removeIf(line -> line.matches("- compiler\\.note\\..*")); // mandatory warning "recompile" etc. + + // See which warnings appeared + boolean foundSuppressionWarning = output.removeIf( + line -> line.contains("compiler.warn.unnecessary.warning.suppression")); + boolean foundCategoryWarning = output.removeIf(line -> line.contains(test.warningKey())); + + // Compare that vs. expectations + if (foundCategoryWarning != expectCategoryWarning) { + throw new AssertionError(String.format("%s: category warning: found=%s but expected=%s", + description, foundCategoryWarning, expectCategoryWarning)); + } + if (foundSuppressionWarning != expectSuppressionWarning) { + throw new AssertionError(String.format("%s: \"%s\" warning: found=%s but expected=%s", + description, SUPPRESSION.option, foundSuppressionWarning, expectSuppressionWarning)); + } + + // There shouldn't be any other warnings + if (!output.isEmpty()) { + throw new AssertionError(String.format( + "%s: %d unexpected warning(s): %s", description, output.size(), output)); + } + + // Done + log.println(String.format("<<< Test PASSED: %s", description)); + } catch (AssertionError e) { + log.println(String.format("<<< Test FAILED: %s", description)); + log.flush(); + out.print(buf); + throw e; + } + } + } + } } + } + + // Test a @SuppressWarning annotation that suppresses nothing + @Test + public void testUselessAnnotation(LintCategory category) throws Exception { + compileAndExpectWarning( + "compiler.warn.unnecessary.warning.suppression", + String.format( + """ + @SuppressWarnings(\"%s\") + public class Test { } + """, + category.option), + String.format("-Xlint:%s", SUPPRESSION.option)); + } + + // Test the suppression of SUPPRESSION itself, which should always work, + // even when the same annotation uselessly suppresses some other category. + @Test + public void testSelfSuppression(LintCategory category) throws Exception { + + // Test category and SUPPRESSION in the same annotation + compileAndExpectSuccess( + String.format( + """ + @SuppressWarnings({ \"%s\", \"%s\" }) + public class Test { + } + """, + category.option, // this is actually a useless suppression + SUPPRESSION.option), // but this prevents us from reporting it + String.format("-Xlint:%s", SUPPRESSION.option)); + + // Test category and SUPPRESSION in nested annotations + compileAndExpectSuccess( + String.format( + """ + @SuppressWarnings(\"%s\") // suppress useless suppression warnings + public class Test { + @SuppressWarnings(\"%s\") // a useless suppression + public class Sub { } + } + """, + SUPPRESSION.option, // this prevents us from reporting the nested useless suppression + category.option), // this is a useless suppression + String.format("-Xlint:%s", SUPPRESSION.option)); + } + + // Test OVERLOADS which has tricky "either-or" suppression + @Test + public void testOverloads() throws Exception { + compileAndExpectSuccess( + """ + import java.util.function.*; + public class Super { + @SuppressWarnings("overloads") + public void foo(IntConsumer c) { + } + @SuppressWarnings("overloads") + public void foo(Consumer c) { + } + } + """, + String.format("-Xlint:%s", OVERLOADS.option), + String.format("-Xlint:%s", SUPPRESSION.option)); + } + + // Test THIS_ESCAPE which has tricky control-flow based suppression + @Test + public void testThisEscape() throws Exception { + compileAndExpectSuccess( + """ + public class Test { + public Test() { + this(0); + } + @SuppressWarnings("this-escape") + private Test(int x) { + this.leak(); + } + protected void leak() { } + } + """, + String.format("-Xlint:%s", THIS_ESCAPE.option), + String.format("-Xlint:%s", SUPPRESSION.option)); + } + + public void compileAndExpectWarning(String errorKey, String source, String... flags) throws Exception { + + // Setup source & destination diretories + Path base = Paths.get("compileAndExpectWarning"); + resetCompileDirectories(base); + + // Write source file + tb.writeJavaFiles(getSourcesDir(base), source); + + // Compile sources and verify we got the warning + List log = compile(base, Task.Expect.FAIL, addWerror(flags)); + if (log.stream().noneMatch(line -> line.contains(errorKey))) { + throw new AssertionError(String.format( + "did not find \"%s\" in log output:%n %s", + errorKey, log.stream().collect(Collectors.joining("\n ")))); + } + } + + public void compileAndExpectSuccess(String source, String... flags) throws Exception { + + // Setup source & destination diretories + Path base = Paths.get("compileAndExpectSuccess"); + resetCompileDirectories(base); + + // Write source file + tb.writeJavaFiles(getSourcesDir(base), source); + + // Compile sources and verify there is no log output + List log = compile(base, Task.Expect.SUCCESS, addWerror(flags)); + if (!log.isEmpty()) { + throw new AssertionError(String.format( + "non-empty log output:%n %s", log.stream().collect(Collectors.joining("\n ")))); + } + } + + private List compile(Path base, Task.Expect expectation, String... flags) throws Exception { + ArrayList options = new ArrayList<>(); + options.add("-XDrawDiagnostics"); + Stream.of(flags).forEach(options::add); + List log; + try { + log = new JavacTask(tb, Mode.CMDLINE) + .options(options.toArray(new String[0])) + .files(tb.findJavaFiles(getSourcesDir(base))) + .outdir(getClassesDir(base)) + .run(expectation) + .writeAll() + .getOutputLines(Task.OutputKind.DIRECT); + } catch (Task.TaskError e) { + throw new AssertionError(String.format( + "compile in %s failed: %s", getSourcesDir(base), e.getMessage()), e); + } + log.removeIf(line -> line.trim().isEmpty()); + return log; + } + + private Path getSourcesDir(Path base) { + return base.resolve("sources"); + } + + private Path getClassesDir(Path base) { + return base.resolve("classes"); + } + + private void resetCompileDirectories(Path base) throws IOException { + for (Path dir : List.of(getSourcesDir(base), getClassesDir(base))) + resetDirectory(dir); + } + + private void resetDirectory(Path dir) throws IOException { + if (Files.exists(dir, LinkOption.NOFOLLOW_LINKS)) + Files.walkFileTree(dir, new Deleter()); + Files.createDirectories(dir); + } + + private String[] addWerror(String[] flags) { + return Stream.concat(Stream.of(flags), Stream.of("-Werror")).toArray(String[]::new); + } + +// SuppressTest + + private record SuppressTest( + LintCategory category, // The Lint category being tested + String warningKey, // Expected warning message key in compiler.properties + List compileFlags, // Any required compilation flags + List sources // Source files with @MODULE@, @OUTER@ and @INNER@ placeholders + ) { + SuppressTest(LintCategory category, String warningKey, String[] compileFlags, String... sources) { + this(category, warningKey, List.of(compileFlags != null ? compileFlags : new String[0]), List.of(sources)); + } + } + +// Deleter + + private static class Deleter extends SimpleFileVisitor { + + @Override + public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { + Files.delete(file); + return FileVisitResult.CONTINUE; + } + } +} diff --git a/test/langtools/tools/javac/lint/TextBlockSuppress.java b/test/langtools/tools/javac/lint/TextBlockSuppress.java new file mode 100644 index 0000000000000..05019be6bc212 --- /dev/null +++ b/test/langtools/tools/javac/lint/TextBlockSuppress.java @@ -0,0 +1,61 @@ +/* + * @test /nodynamiccopyright/ + * @bug 8224228 + * @summary Verify SuppressWarnings works for LintCategore.TEXT_BLOCKS + * @compile/fail/ref=TextBlockSuppress.out -XDrawDiagnostics -Xlint:text-blocks -Werror TextBlockSuppress.java + */ + +public class TextBlockSuppress { + + public static class Example1 { + public void method() { + String s = """ + trailing space here:\u0020 + """; // SHOULD get a warning here + } + } + + @SuppressWarnings("text-blocks") + public static class Example2 { + public void method() { + String s = """ + trailing space here:\u0020 + """; // SHOULD NOT get a warning here + } + } + + public static class Example3 { + @SuppressWarnings("text-blocks") + public void method() { + String s = """ + trailing space here:\u0020 + """; // SHOULD NOT get a warning here + } + } + + public static class Example4 { + { + String s = """ + trailing space here:\u0020 + """; // SHOULD get a warning here + } + } + + @SuppressWarnings("text-blocks") + public static class Example5 { + { + String s = """ + trailing space here:\u0020 + """; // SHOULD NOT get a warning here + } + } + + public static class Example6 { + public void method() { + @SuppressWarnings("text-blocks") + String s = """ + trailing space here:\u0020 + """; // SHOULD NOT get a warning here + } + } +} diff --git a/test/langtools/tools/javac/lint/TextBlockSuppress.out b/test/langtools/tools/javac/lint/TextBlockSuppress.out new file mode 100644 index 0000000000000..d16f080a13384 --- /dev/null +++ b/test/langtools/tools/javac/lint/TextBlockSuppress.out @@ -0,0 +1,5 @@ +TextBlockSuppress.java:12:24: compiler.warn.trailing.white.space.will.be.removed +TextBlockSuppress.java:38:24: compiler.warn.trailing.white.space.will.be.removed +- compiler.err.warnings.and.werror +1 error +2 warnings diff --git a/test/langtools/tools/javac/mandatoryWarnings/deprecated/Test5.out b/test/langtools/tools/javac/mandatoryWarnings/deprecated/Test5.out index 6fe57b8979d08..1656d8eeff5d7 100644 --- a/test/langtools/tools/javac/mandatoryWarnings/deprecated/Test5.out +++ b/test/langtools/tools/javac/mandatoryWarnings/deprecated/Test5.out @@ -1,4 +1,4 @@ -Q.java:7:10: compiler.warn.has.been.deprecated: bar(), Q2 P.java:10:18: compiler.warn.has.been.deprecated: foo(), Q +Q.java:7:10: compiler.warn.has.been.deprecated: bar(), Q2 Q.java:17:25: compiler.warn.has.been.deprecated: foo(), Q 3 warnings diff --git a/test/langtools/tools/javac/mandatoryWarnings/deprecated/Test5b.out b/test/langtools/tools/javac/mandatoryWarnings/deprecated/Test5b.out index fe4a91e258432..5dee9c1414c29 100644 --- a/test/langtools/tools/javac/mandatoryWarnings/deprecated/Test5b.out +++ b/test/langtools/tools/javac/mandatoryWarnings/deprecated/Test5b.out @@ -1,4 +1,4 @@ -Q.java:7:10: compiler.warn.has.been.deprecated: bar(), Q2 P.java:10:18: compiler.warn.has.been.deprecated: foo(), Q +Q.java:7:10: compiler.warn.has.been.deprecated: bar(), Q2 - compiler.note.deprecated.filename.additional: Q.java 2 warnings diff --git a/test/langtools/tools/javac/modules/AnnotationsOnModules.java b/test/langtools/tools/javac/modules/AnnotationsOnModules.java index 6d9bfaad40697..f9858a105ebea 100644 --- a/test/langtools/tools/javac/modules/AnnotationsOnModules.java +++ b/test/langtools/tools/javac/modules/AnnotationsOnModules.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 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 @@ -599,8 +599,8 @@ public void testModuleDeprecation(Path base) throws Exception { "1 warning"); } else if (suppress.equals(DEPRECATED_JAVADOC)) { expected = Arrays.asList( - "module-info.java:1:19: compiler.warn.missing.deprecated.annotation", "module-info.java:2:14: compiler.warn.has.been.deprecated.module: m1x", + "module-info.java:1:19: compiler.warn.missing.deprecated.annotation", "2 warnings"); } else { expected = Arrays.asList(""); diff --git a/test/langtools/tools/javac/preview/PreviewErrors.java b/test/langtools/tools/javac/preview/PreviewErrors.java index eab5b2af9bfae..db17aabbd425f 100644 --- a/test/langtools/tools/javac/preview/PreviewErrors.java +++ b/test/langtools/tools/javac/preview/PreviewErrors.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 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 @@ -324,7 +324,9 @@ public void test(Object o) { ok = true; switch (elementType) { case LANGUAGE -> { - if (lint == Lint.ENABLE_PREVIEW) { + if (suppress == Suppress.YES) { + expected = Set.of(); + } else if (lint == Lint.ENABLE_PREVIEW) { expected = Set.of("5:41:compiler.warn.preview.feature.use"); } else { expected = Set.of("-1:-1:compiler.note.preview.filename", diff --git a/test/langtools/tools/javac/preview/PreviewTest.java b/test/langtools/tools/javac/preview/PreviewTest.java index 36f1e70acd07d..e681f3f837e54 100644 --- a/test/langtools/tools/javac/preview/PreviewTest.java +++ b/test/langtools/tools/javac/preview/PreviewTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 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 @@ -587,12 +587,12 @@ interface Produce { "Test.java:19:11: compiler.err.is.preview: test()", "Test.java:20:11: compiler.err.is.preview: test()", "Test.java:21:11: compiler.err.is.preview: test()", - "Test.java:24:11: compiler.warn.is.preview.reflective: test()", "Test.java:29:16: compiler.err.is.preview: preview.api.Preview", "Test.java:32:21: compiler.err.is.preview: test()", "Test.java:36:21: compiler.err.is.preview: test()", "Test.java:40:13: compiler.err.is.preview: test()", "Test.java:41:21: compiler.err.is.preview: FIELD", + "Test.java:24:11: compiler.warn.is.preview.reflective: test()", "17 errors", "1 warning"); @@ -792,6 +792,99 @@ public void test(NonPreview np, Preview p) { throw new Exception("expected output not found" + log); } + @Test //JDK-8224228: + public void testSuppressWarnings(Path base) throws Exception { + Path apiSrc = base.resolve("api-src"); + tb.writeJavaFiles(apiSrc, + """ + package preview.api; + @jdk.internal.javac.PreviewFeature(feature=jdk.internal.javac.PreviewFeature.Feature.TEST) + public class Preview { + public static int test() { + return 0; + } + } + """); + Path apiClasses = base.resolve("api-classes"); + + new JavacTask(tb, Task.Mode.CMDLINE) + .outdir(apiClasses) + .options("--patch-module", "java.base=" + apiSrc.toString(), + "-Werror") + .files(tb.findJavaFiles(apiSrc)) + .run() + .writeAll() + .getOutputLines(Task.OutputKind.DIRECT); + + Path testSrc = base.resolve("test-src"); + tb.writeJavaFiles(testSrc, + """ + package test; + import preview.api.Preview; + public class Test { + + public static class Example1 { + public void method() { + Preview.test(); // SHOULD get a warning here + } + } + + @SuppressWarnings("preview") + public static class Example2 { + public void method() { + Preview.test(); // SHOULD NOT get a warning here + } + } + + public static class Example3 { + @SuppressWarnings("preview") + public void method() { + Preview.test(); // SHOULD NOT get a warning here + } + } + + public static class Example4 { + { + Preview.test(); // SHOULD get a warning here + } + } + + @SuppressWarnings("preview") + public static class Example5 { + { + Preview.test(); // SHOULD NOT get a warning here + } + } + + public static class Example6 { + @SuppressWarnings("preview") + int x = Preview.test(); // SHOULD NOT get a warning here + } + } + """); + Path testClasses = base.resolve("test-classes"); + List log = new JavacTask(tb, Task.Mode.CMDLINE) + .outdir(testClasses) + .options("--patch-module", "java.base=" + apiClasses.toString(), + "--add-exports", "java.base/preview.api=ALL-UNNAMED", + "--enable-preview", + "-Xlint:preview", + "-source", String.valueOf(Runtime.version().feature()), + "-XDrawDiagnostics") + .files(tb.findJavaFiles(testSrc)) + .run(Task.Expect.SUCCESS) + .writeAll() + .getOutputLines(Task.OutputKind.DIRECT); + + List expected = + List.of("Test.java:7:11: compiler.warn.is.preview: preview.api.Preview", + "Test.java:27:11: compiler.warn.is.preview: preview.api.Preview", + "2 warnings"); + + if (!log.equals(expected)) + throw new Exception("expected output not found: " + log); + } + @Test //JDK-8343540: public void nonPreviewImplementsPreview5(Path base) throws Exception { Path apiSrc = base.resolve("api-src"); diff --git a/test/langtools/tools/javac/varargs/7097436/T7097436.out b/test/langtools/tools/javac/varargs/7097436/T7097436.out index 5e35910d2faca..392869f3b9f38 100644 --- a/test/langtools/tools/javac/varargs/7097436/T7097436.out +++ b/test/langtools/tools/javac/varargs/7097436/T7097436.out @@ -1,6 +1,6 @@ -T7097436.java:13:20: compiler.warn.varargs.unsafe.use.varargs.param: ls -T7097436.java:14:25: compiler.warn.varargs.unsafe.use.varargs.param: ls T7097436.java:15:20: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: java.util.List[], java.lang.String) T7097436.java:16:26: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: java.util.List[], java.lang.Integer[]) +T7097436.java:13:20: compiler.warn.varargs.unsafe.use.varargs.param: ls +T7097436.java:14:25: compiler.warn.varargs.unsafe.use.varargs.param: ls 2 errors 2 warnings diff --git a/test/langtools/tools/javac/warnings/6594914/T6594914a.out b/test/langtools/tools/javac/warnings/6594914/T6594914a.out index 62f99072a7ad5..d3d759ca044b9 100644 --- a/test/langtools/tools/javac/warnings/6594914/T6594914a.out +++ b/test/langtools/tools/javac/warnings/6594914/T6594914a.out @@ -1,7 +1,7 @@ T6594914a.java:11:5: compiler.warn.has.been.deprecated: DeprecatedClass, compiler.misc.unnamed.package T6594914a.java:16:16: compiler.warn.has.been.deprecated: DeprecatedClass, compiler.misc.unnamed.package +T6594914a.java:16:52: compiler.warn.has.been.deprecated: DeprecatedClass, compiler.misc.unnamed.package T6594914a.java:16:33: compiler.warn.has.been.deprecated: DeprecatedClass, compiler.misc.unnamed.package T6594914a.java:17:20: compiler.warn.has.been.deprecated: DeprecatedClass, compiler.misc.unnamed.package -T6594914a.java:16:52: compiler.warn.has.been.deprecated: DeprecatedClass, compiler.misc.unnamed.package T6594914a.java:24:9: compiler.warn.has.been.deprecated: DeprecatedClass, compiler.misc.unnamed.package 6 warnings diff --git a/test/langtools/tools/javac/warnings/7090499/T7090499.out b/test/langtools/tools/javac/warnings/7090499/T7090499.out index 1ff9e164e482e..2241c0a04bb1d 100644 --- a/test/langtools/tools/javac/warnings/7090499/T7090499.out +++ b/test/langtools/tools/javac/warnings/7090499/T7090499.out @@ -1,14 +1,14 @@ +T7090499.java:26:10: compiler.err.improperly.formed.type.inner.raw.param +T7090499.java:27:10: compiler.err.improperly.formed.type.inner.raw.param +T7090499.java:28:17: compiler.err.improperly.formed.type.inner.raw.param +T7090499.java:28:10: compiler.err.improperly.formed.type.inner.raw.param T7090499.java:18:5: compiler.warn.raw.class.use: T7090499, T7090499 T7090499.java:18:22: compiler.warn.raw.class.use: T7090499, T7090499 T7090499.java:20:10: compiler.warn.raw.class.use: T7090499.A.X, T7090499.A.X T7090499.java:21:10: compiler.warn.raw.class.use: T7090499.A.Z, T7090499.A.Z T7090499.java:24:17: compiler.warn.raw.class.use: T7090499.B, T7090499.B -T7090499.java:26:10: compiler.err.improperly.formed.type.inner.raw.param -T7090499.java:27:10: compiler.err.improperly.formed.type.inner.raw.param T7090499.java:28:18: compiler.warn.raw.class.use: T7090499.B, T7090499.B -T7090499.java:28:17: compiler.err.improperly.formed.type.inner.raw.param T7090499.java:28:11: compiler.warn.raw.class.use: T7090499.B, T7090499.B -T7090499.java:28:10: compiler.err.improperly.formed.type.inner.raw.param T7090499.java:30:32: compiler.warn.raw.class.use: T7090499.B, T7090499.B T7090499.java:33:13: compiler.warn.raw.class.use: T7090499.A, T7090499.A T7090499.java:33:24: compiler.warn.raw.class.use: T7090499.A, T7090499.A diff --git a/test/langtools/tools/javac/warnings/DepAnn.java b/test/langtools/tools/javac/warnings/DepAnn.java index d725258cf3ab7..4209089971140 100644 --- a/test/langtools/tools/javac/warnings/DepAnn.java +++ b/test/langtools/tools/javac/warnings/DepAnn.java @@ -1,7 +1,7 @@ /* * @test /nodynamiccopyright/ * @bug 4986256 - * @compile/ref=DepAnn.out -XDrawDiagnostics -Xlint:all,-dangling-doc-comments DepAnn.java + * @compile/ref=DepAnn.out -XDrawDiagnostics -Xlint:all,-dangling-doc-comments,-suppression DepAnn.java */ // control: this class should generate warnings diff --git a/test/langtools/tools/javac/warnings/UnneededStrictfpWarningToolBox.java b/test/langtools/tools/javac/warnings/UnneededStrictfpWarningToolBox.java index b5062a9b63fcd..a9101b47e42b3 100644 --- a/test/langtools/tools/javac/warnings/UnneededStrictfpWarningToolBox.java +++ b/test/langtools/tools/javac/warnings/UnneededStrictfpWarningToolBox.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 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 @@ -116,8 +116,8 @@ public void testWithAndWithOutLint(Path base) throws IOException { var expected = List.of("UnneededStrictfpWarning1.java:1:17: compiler.warn.strictfp", "UnneededStrictfpWarning1.java:10:10: compiler.warn.strictfp", "UnneededStrictfpWarning1.java:12:29: compiler.warn.strictfp", - "UnneededStrictfpWarning1.java:16:28: compiler.warn.strictfp", "UnneededStrictfpWarning1.java:18:21: compiler.warn.strictfp", + "UnneededStrictfpWarning1.java:16:28: compiler.warn.strictfp", "5 warnings"); checkLog(log, expected); } diff --git a/test/langtools/tools/javac/warnings/suppress/T6480588.out b/test/langtools/tools/javac/warnings/suppress/T6480588.out index 630ba55523dac..267ef32c96408 100644 --- a/test/langtools/tools/javac/warnings/suppress/T6480588.out +++ b/test/langtools/tools/javac/warnings/suppress/T6480588.out @@ -1,19 +1,19 @@ T6480588.java:16:24: compiler.warn.has.been.deprecated: DeprecatedClass, compiler.misc.unnamed.package T6480588.java:16:51: compiler.warn.has.been.deprecated: DeprecatedInterface, compiler.misc.unnamed.package T6480588.java:15:2: compiler.warn.has.been.deprecated: DeprecatedAnnotation, compiler.misc.unnamed.package +T6480588.java:18:35: compiler.warn.has.been.deprecated: DeprecatedClass, compiler.misc.unnamed.package T6480588.java:18:12: compiler.warn.has.been.deprecated: DeprecatedClass, compiler.misc.unnamed.package T6480588.java:18:65: compiler.warn.has.been.deprecated: DeprecatedClass, compiler.misc.unnamed.package +T6480588.java:30:5: compiler.warn.has.been.deprecated: DeprecatedClass, compiler.misc.unnamed.package +T6480588.java:33:25: compiler.warn.has.been.deprecated: DeprecatedClass, compiler.misc.unnamed.package +T6480588.java:33:52: compiler.warn.has.been.deprecated: DeprecatedInterface, compiler.misc.unnamed.package +T6480588.java:32:6: compiler.warn.has.been.deprecated: DeprecatedAnnotation, compiler.misc.unnamed.package T6480588.java:17:6: compiler.warn.has.been.deprecated: DeprecatedAnnotation, compiler.misc.unnamed.package -T6480588.java:18:35: compiler.warn.has.been.deprecated: DeprecatedClass, compiler.misc.unnamed.package +T6480588.java:29:6: compiler.warn.has.been.deprecated: DeprecatedAnnotation, compiler.misc.unnamed.package T6480588.java:19:9: compiler.warn.has.been.deprecated: DeprecatedClass, compiler.misc.unnamed.package T6480588.java:19:34: compiler.warn.has.been.deprecated: DeprecatedClass, compiler.misc.unnamed.package T6480588.java:21:9: compiler.warn.has.been.deprecated: DeprecatedClass, compiler.misc.unnamed.package T6480588.java:21:25: compiler.warn.deprecated.annotation.has.no.effect: kindname.variable T6480588.java:21:35: compiler.warn.has.been.deprecated: DeprecatedClass, compiler.misc.unnamed.package -T6480588.java:30:5: compiler.warn.has.been.deprecated: DeprecatedClass, compiler.misc.unnamed.package -T6480588.java:29:6: compiler.warn.has.been.deprecated: DeprecatedAnnotation, compiler.misc.unnamed.package T6480588.java:30:33: compiler.warn.has.been.deprecated: DeprecatedClass, compiler.misc.unnamed.package -T6480588.java:33:25: compiler.warn.has.been.deprecated: DeprecatedClass, compiler.misc.unnamed.package -T6480588.java:33:52: compiler.warn.has.been.deprecated: DeprecatedInterface, compiler.misc.unnamed.package -T6480588.java:32:6: compiler.warn.has.been.deprecated: DeprecatedAnnotation, compiler.misc.unnamed.package 18 warnings diff --git a/test/langtools/tools/lib/toolbox/TestRunner.java b/test/langtools/tools/lib/toolbox/TestRunner.java index e93370a6e33c7..2bdbe5ddaebfc 100644 --- a/test/langtools/tools/lib/toolbox/TestRunner.java +++ b/test/langtools/tools/lib/toolbox/TestRunner.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 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 @@ -29,7 +29,12 @@ import java.lang.annotation.RetentionPolicy; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; +import java.util.Iterator; +import java.util.Optional; +import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Function; +import java.util.stream.Collectors; +import java.util.stream.Stream; /** * Utility class to manage and execute sub-tests within a test. @@ -74,28 +79,59 @@ protected void runTests() throws Exception { } /** - * Invoke all methods annotated with @Test. - * @param f a lambda expression to specify arguments for the test method + * Invoke each @Test method once using the parameters returned by the function. + * + *

+ * If the function returns null for some method, that method is skipped. + * + *

+ * If system property {@code test.query} is set, only that method is included. + * + * @param f function mapping method name to an array of method parameters * @throws java.lang.Exception if any errors occur */ protected void runTests(Function f) throws Exception { + runTestsMulti(f.andThen(Stream::of)); + } + + /** + * Invoke each @Test method once for each array of parameters returned by the iteration. + * + *

+ * If the function returns null for some method, that method is skipped. + * + * @param f function mapping method name to an iteration of arrays of method parameters + * @throws java.lang.Exception if any errors occur + */ + protected void runTestsMulti(Function> f) throws Exception { String testQuery = System.getProperty("test.query"); for (Method m : getClass().getDeclaredMethods()) { Annotation a = m.getAnnotation(Test.class); if (a != null) { testName = m.getName(); if (testQuery == null || testQuery.equals(testName)) { - try { - testCount++; - out.println("test: " + testName); - m.invoke(this, f.apply(m)); - } catch (InvocationTargetException e) { - errorCount++; - Throwable cause = e.getCause(); - out.println("Exception running test " + testName + ": " + e.getCause()); - cause.printStackTrace(out); + Iterator iterator = Optional.of(m).map(f).map(Stream::iterator).orElse(null); + if (iterator == null) + return; + for (int testNum = 1; iterator.hasNext(); testNum++) { + Object[] params = iterator.next(); + try { + testCount++; + out.println(String.format("test: %s#%d", testName, testNum)); + m.invoke(this, params); + } catch (InvocationTargetException e) { + errorCount++; + Throwable cause = e.getCause(); + String paramsDesc = Stream.of(params) + .map(String::valueOf) + .collect(Collectors.joining(", ")); + out.println(String.format( + "Exception running test %s#%d(%s): %s", + testName, testNum, paramsDesc, e.getCause())); + cause.printStackTrace(out); + } + out.println(); } - out.println(); } } }