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..e08b4e7b446fc 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 @@ -147,50 +147,31 @@ protected Lint(Lint other) { // Process command line options on demand to allow use of root Lint early during startup private void initializeRootIfNeeded() { - - // Already initialized? - if (values != null) - return; - - // Initialize enabled categories based on "-Xlint" flags - if (options.isSet(Option.XLINT) || options.isSet(Option.XLINT_CUSTOM, Option.LINT_CUSTOM_ALL)) { - // If -Xlint or -Xlint:all is given, enable all categories by default - values = EnumSet.allOf(LintCategory.class); - } else if (options.isSet(Option.XLINT_CUSTOM, Option.LINT_CUSTOM_NONE)) { - // if -Xlint:none is given, disable all categories by default - values = LintCategory.newEmptySet(); - } else { - // otherwise, enable on-by-default categories - values = LintCategory.newEmptySet(); - - Source source = Source.instance(context); - if (source.compareTo(Source.JDK9) >= 0) { - values.add(LintCategory.DEP_ANN); - } - if (Source.Feature.REDUNDANT_STRICTFP.allowedInSource(source)) { - values.add(LintCategory.STRICTFP); - } - values.add(LintCategory.REQUIRES_TRANSITIVE_AUTOMATIC); - values.add(LintCategory.OPENS); - values.add(LintCategory.MODULE); - values.add(LintCategory.REMOVAL); - if (!options.isSet(Option.PREVIEW)) { - values.add(LintCategory.PREVIEW); - } - values.add(LintCategory.IDENTITY); - values.add(LintCategory.INCUBATING); + if (values == null) { + values = options.getLintCategories(Option.XLINT, this::getDefaults); + suppressedValues = LintCategory.newEmptySet(); } + } - // Look for specific overrides - for (LintCategory lc : LintCategory.values()) { - if (options.isExplicitlyEnabled(Option.XLINT, lc)) { - values.add(lc); - } else if (options.isExplicitlyDisabled(Option.XLINT, lc)) { - values.remove(lc); - } + private EnumSet getDefaults() { + Source source = Source.instance(context); + EnumSet values = LintCategory.newEmptySet(); + if (source.compareTo(Source.JDK9) >= 0) { + values.add(LintCategory.DEP_ANN); } - - suppressedValues = LintCategory.newEmptySet(); + if (Source.Feature.REDUNDANT_STRICTFP.allowedInSource(source)) { + values.add(LintCategory.STRICTFP); + } + values.add(LintCategory.REQUIRES_TRANSITIVE_AUTOMATIC); + values.add(LintCategory.OPENS); + values.add(LintCategory.MODULE); + values.add(LintCategory.REMOVAL); + if (!options.isSet(Option.PREVIEW)) { + values.add(LintCategory.PREVIEW); + } + values.add(LintCategory.IDENTITY); + values.add(LintCategory.INCUBATING); + return values; } @Override 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..1ff1779760eca 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 @@ -31,6 +31,7 @@ import java.nio.file.ReadOnlyFileSystemException; import java.util.Collection; import java.util.Comparator; +import java.util.EnumSet; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashMap; @@ -444,7 +445,8 @@ public JavaCompiler(Context context) { context.get(DiagnosticListener.class) != null; devVerbose = options.isSet("dev"); processPcks = options.isSet("process.packages"); - werror = options.isSet(WERROR); + werrorAny = options.isSet(WERROR) || options.isSet(WERROR_CUSTOM, Option.LINT_CUSTOM_ALL); + werrorLint = options.getLintCategories(WERROR, LintCategory::newEmptySet); verboseCompilePolicy = options.isSet("verboseCompilePolicy"); @@ -517,9 +519,13 @@ public boolean exists() { */ protected boolean processPcks; - /** Switch: treat warnings as errors + /** Switch: treat any kind of warning (including non-lint warnings) as an error. */ - protected boolean werror; + protected boolean werrorAny; + + /** Switch: treat lint warnings in the specified {@link LintCategory}s as errors. + */ + protected EnumSet werrorLint; /** Switch: is annotation processing requested explicitly via * CompilationTask.setProcessors? @@ -584,7 +590,8 @@ protected boolean shouldStop(CompileState cs) { /** The number of errors reported so far. */ public int errorCount() { - if (werror && log.nerrors == 0 && log.nwarnings > 0) { + if (log.nerrors == 0 && log.nwarnings > 0 && + (werrorAny || werrorLint.clone().removeAll(log.lintWarnings))) { log.error(Errors.WarningsAndWerror); } return log.nerrors; diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/main/Option.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/main/Option.java index f4122cebb64af..54d3d717d4333 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/main/Option.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/main/Option.java @@ -531,6 +531,8 @@ public void process(OptionHelper helper, String option) throws InvalidValueExcep // treat warnings as errors WERROR("-Werror", "opt.Werror", STANDARD, BASIC), + WERROR_CUSTOM("-Werror:", "opt.arg.Werror", "opt.Werror.custom", STANDARD, BASIC, ANYOF, getXLintChoices()), + // prompt after each error // new Option("-prompt", "opt.prompt"), PROMPT("-prompt", null, HIDDEN, BASIC), 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..a7b5d3a2f8321 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 @@ -213,7 +213,7 @@ protected JavacProcessingEnvironment(Context context) { } fatalErrors = options.isSet("fatalEnterError"); showResolveErrors = options.isSet("showResolveErrors"); - werror = options.isSet(Option.WERROR); + werror = options.isSet(Option.WERROR, PROCESSING, false); fileManager = context.get(JavaFileManager.class); platformAnnotations = initPlatformAnnotations(); 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..3a3904c302d71 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 @@ -95,7 +95,13 @@ javac.opt.source=\ Provide source compatibility with the specified Java SE release.\n\ Supported releases: \n {0} javac.opt.Werror=\ - Terminate compilation if warnings occur + Terminate compilation if any warnings occur +javac.opt.arg.Werror=\ + (,)* +javac.opt.Werror.custom=\ + Specify warnings that should terminate compilation, separated by comma.\n\ + Precede a key by ''-'' to exclude the specified warning.\n\ + Use --help-lint to see the supported keys. javac.opt.A=\ Options to pass to annotation processors javac.opt.implicit=\ @@ -330,9 +336,9 @@ javac.opt.X=\ javac.opt.help=\ Print this help message javac.opt.help.lint=\ - Print the supported keys for -Xlint + Print the supported keys for -Xlint and -Werror javac.opt.help.lint.header=\ - The supported keys for -Xlint are: + The supported keys for -Xlint and -Werror are: javac.opt.print=\ Print out a textual representation of specified types javac.opt.printRounds=\ 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..9f986d0f8f772 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,6 +30,7 @@ import java.util.Arrays; import java.util.Comparator; import java.util.EnumMap; +import java.util.EnumSet; import java.util.HashSet; import java.util.List; import java.util.Map; @@ -41,6 +42,7 @@ import javax.tools.JavaFileObject; import com.sun.tools.javac.api.DiagnosticFormatter; +import com.sun.tools.javac.code.Lint.LintCategory; import com.sun.tools.javac.main.Main; import com.sun.tools.javac.main.Option; import com.sun.tools.javac.tree.EndPosTable; @@ -403,10 +405,14 @@ protected int getDefaultMaxWarnings() { */ public int nerrors = 0; - /** The number of warnings encountered so far. + /** The total number of warnings encountered so far. */ public int nwarnings = 0; + /** Tracks whether any warnings have been encountered in each {@link LintCategory}. + */ + public final EnumSet lintWarnings = LintCategory.newEmptySet(); + /** The number of errors encountered after MaxErrors was reached. */ public int nsuppressederrors = 0; @@ -671,7 +677,6 @@ protected void directError(String key, Object... args) { */ public void strictWarning(DiagnosticPosition pos, String key, Object ... args) { writeDiagnostic(diags.warning(null, source, pos, key, args)); - nwarnings++; } /** @@ -689,6 +694,7 @@ public void report(JCDiagnostic diagnostic) { public void clear() { recorded.clear(); sourceMap.clear(); + lintWarnings.clear(); nerrors = 0; nwarnings = 0; nsuppressederrors = 0; @@ -730,7 +736,6 @@ public void report(JCDiagnostic diagnostic) { if (emitWarnings || diagnostic.isMandatory()) { if (nwarnings < MaxWarnings) { writeDiagnostic(diagnostic); - nwarnings++; } else { nsuppressedwarns++; } @@ -742,7 +747,6 @@ public void report(JCDiagnostic diagnostic) { shouldReport(diagnostic)) { if (nerrors < MaxErrors) { writeDiagnostic(diagnostic); - nerrors++; } else { nsuppressederrors++; } @@ -756,9 +760,25 @@ public void report(JCDiagnostic diagnostic) { } /** - * Write out a diagnostic. + * Write out a diagnostic and bump the warning and error counters as needed. */ protected void writeDiagnostic(JCDiagnostic diag) { + + // Increment counter(s) + switch (diag.getType()) { + case WARNING: + nwarnings++; + Optional.of(diag) + .map(JCDiagnostic::getLintCategory) + .ifPresent(lintWarnings::add); + break; + case ERROR: + nerrors++; + break; + default: + break; + } + if (diagListener != null) { diagListener.report(diag); return; 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..11f16c9a74025 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 @@ -223,6 +223,54 @@ public boolean isExplicitlyDisabled(Option option, LintCategory lc) { return !isSet(option, lc, true); } + /** + * Collect the set of {@link LintCategory}s specified by option flag(s) of the form + * {@code -Flag} and/or {@code -Flag:[-]key,[-]key,...}. + * + *

+ * The set of categories is calculated as folllows. First, an initial set is created: + *

    + *
  • If {@code -Flag} or {@code -Flag:all} appears, the initial set contains all categories; otherwise, + *
  • If {@code -Flag:none} appears, the initial set is empty; otherwise, + *
  • The {@code defaults} parameter is invoked to construct an initial set. + *
+ * Next, for each lint category key {@code key}: + *
    + *
  • If {@code -Flag:key} flag appears, the corresponding category is added to the set; otherwise + *
  • If {@code -Flag:-key} flag appears, the corresponding category is removed to the set + *
+ * Unrecognized {@code key}s are ignored. + * + * @param option the plain option + * @param defaults populates the default set, or null for an empty default set + * @return the specified set of categories + */ + public EnumSet getLintCategories(Option option, Supplier> defaults) { + + // Create the initial set + EnumSet categories; + Option customOption = option.getCustom(); + if (isSet(option) || isSet(customOption, Option.LINT_CUSTOM_ALL)) { + categories = EnumSet.allOf(LintCategory.class); + } else if (isSet(customOption, Option.LINT_CUSTOM_NONE)) { + categories = EnumSet.noneOf(LintCategory.class); + } else { + categories = defaults.get(); + } + + // Apply specific overrides + for (LintCategory category : LintCategory.values()) { + if (isExplicitlyEnabled(option, category)) { + categories.add(category); + } else if (isExplicitlyDisabled(option, category)) { + categories.remove(category); + } + } + + // Done + return categories; + } + public void put(String name, String value) { values.put(name, value); initialized = true; diff --git a/src/jdk.compiler/share/man/javac.md b/src/jdk.compiler/share/man/javac.md index 997023487b0ae..71fa5d96d94e6 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 @@ -448,7 +448,12 @@ file system locations may be directories, JAR files or JMOD files. : Prints version information. `-Werror` -: Terminates compilation when warnings occur. +: Terminates compilation when any warnings occur; this includes warnings in all lint + categories, as well as non-lint warnings. + +`-Werror:`\[`-`\]*key*(`,`\[`-`\]*key*)\* +: Enables and/or disables specific categories of lint warnings that should terminate compilation. + See [`-Xlint`](#option-Xlint-custom) below for the list of lint category keys. ### Extra Options diff --git a/test/langtools/tools/javac/warnings/WerrorLint.e1.out b/test/langtools/tools/javac/warnings/WerrorLint.e1.out new file mode 100644 index 0000000000000..ae99cfa5056e2 --- /dev/null +++ b/test/langtools/tools/javac/warnings/WerrorLint.e1.out @@ -0,0 +1,4 @@ +WerrorLint.java:20:19: compiler.warn.strictfp +- compiler.err.warnings.and.werror +1 error +1 warning diff --git a/test/langtools/tools/javac/warnings/WerrorLint.e2.out b/test/langtools/tools/javac/warnings/WerrorLint.e2.out new file mode 100644 index 0000000000000..1c9bd4d54f8e1 --- /dev/null +++ b/test/langtools/tools/javac/warnings/WerrorLint.e2.out @@ -0,0 +1,5 @@ +WerrorLint.java:20:19: compiler.warn.strictfp +WerrorLint.java:21:30: compiler.warn.empty.if +- compiler.err.warnings.and.werror +1 error +2 warnings diff --git a/test/langtools/tools/javac/warnings/WerrorLint.java b/test/langtools/tools/javac/warnings/WerrorLint.java new file mode 100644 index 0000000000000..3331a664d5598 --- /dev/null +++ b/test/langtools/tools/javac/warnings/WerrorLint.java @@ -0,0 +1,23 @@ +/* + * @test /nodynamiccopyright/ + * @bug 8349847 + * + * @compile -XDrawDiagnostics -Xlint:none WerrorLint.java + * @compile -XDrawDiagnostics -Xlint:none -Werror WerrorLint.java + * @compile -XDrawDiagnostics -Xlint:none -Werror:empty WerrorLint.java + * @compile -XDrawDiagnostics -Xlint:none -Werror:strictfp WerrorLint.java + * @compile/ref=WerrorLint.w2.out -XDrawDiagnostics -Xlint:all WerrorLint.java + * @compile/fail/ref=WerrorLint.e2.out -XDrawDiagnostics -Xlint:all -Werror WerrorLint.java + * @compile/fail/ref=WerrorLint.e2.out -XDrawDiagnostics -Xlint:all -Werror:empty WerrorLint.java + * @compile/fail/ref=WerrorLint.e2.out -XDrawDiagnostics -Xlint:all -Werror:strictfp WerrorLint.java + * @compile/ref=WerrorLint.w1.out -XDrawDiagnostics WerrorLint.java + * @compile/fail/ref=WerrorLint.e1.out -XDrawDiagnostics -Werror WerrorLint.java + * @compile/ref=WerrorLint.w1.out -XDrawDiagnostics -Werror:empty WerrorLint.java + * @compile/fail/ref=WerrorLint.e1.out -XDrawDiagnostics -Werror:strictfp WerrorLint.java + */ + +class WerrorLint { + strictfp void m() { // [strictfp] - this category is enabled by default + if (hashCode() == 1) ; // [empty] - this category is disabled by default + } +} diff --git a/test/langtools/tools/javac/warnings/WerrorLint.w1.out b/test/langtools/tools/javac/warnings/WerrorLint.w1.out new file mode 100644 index 0000000000000..3e19de5103373 --- /dev/null +++ b/test/langtools/tools/javac/warnings/WerrorLint.w1.out @@ -0,0 +1,2 @@ +WerrorLint.java:20:19: compiler.warn.strictfp +1 warning diff --git a/test/langtools/tools/javac/warnings/WerrorLint.w2.out b/test/langtools/tools/javac/warnings/WerrorLint.w2.out new file mode 100644 index 0000000000000..bac258706a6f3 --- /dev/null +++ b/test/langtools/tools/javac/warnings/WerrorLint.w2.out @@ -0,0 +1,3 @@ +WerrorLint.java:20:19: compiler.warn.strictfp +WerrorLint.java:21:30: compiler.warn.empty.if +2 warnings