Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion java/java.hints/nbproject/project.properties
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@

spec.version.base=1.117.0

javac.release=17
javac.release=21

nbroot=../..
jbrowse.external=${nbroot}/retouche
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,6 @@
package org.netbeans.modules.java.hints.errors;

import com.sun.source.util.TreePath;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
Expand All @@ -31,65 +29,180 @@
import org.netbeans.spi.editor.hints.Fix;
import org.openide.util.NbBundle;

import static java.util.Map.entry;

/**
*
* SupressWarnings hint for standard javac warnings.
*
* @author Jan Lahoda
*/
public class SuppressWarningsFixer implements ErrorRule<Void> {

/** Creates a new instance of SuppressWarningsFixer */
public SuppressWarningsFixer() {
}

private static final Map<String, String> KEY2SUPRESS_KEY;
static final Map<String, String> DIAG2LINT;

static {
Map<String, String> map = new HashMap<String, String>();

String uncheckedKey = "unchecked";

map.put("compiler.warn.prob.found.req", uncheckedKey); // NOI18N
map.put("compiler.warn.unchecked.cast.to.type", uncheckedKey); // NOI18N
map.put("compiler.warn.unchecked.assign", uncheckedKey); // NOI18N
map.put("compiler.warn.unchecked.assign.to.var", uncheckedKey); // NOI18N
map.put("compiler.warn.unchecked.call.mbr.of.raw.type", uncheckedKey); // NOI18N
map.put("compiler.warn.unchecked.meth.invocation.applied", uncheckedKey); // NOI18N
map.put("compiler.warn.unchecked.generic.array.creation", uncheckedKey); // NOI18N

String fallThroughKey = "fallthrough"; // NOI18N

map.put("compiler.warn.possible.fall-through.into.case", fallThroughKey); // NOI18N

String deprecationKey = "deprecation"; // NOI18N

map.put("compiler.warn.has.been.deprecated", deprecationKey); // NOI18N

KEY2SUPRESS_KEY = Collections.unmodifiableMap(map);

// checked and generated via unit test

DIAG2LINT = Map.ofEntries(
entry("compiler.warn.auxiliary.class.accessed.from.outside.of.its.source.file", "auxiliaryclass"), // NOI18N
entry("compiler.warn.redundant.cast", "cast"), // NOI18N
entry("compiler.warn.annotation.method.not.found", "classfile"), // NOI18N
entry("compiler.warn.annotation.method.not.found.reason", "classfile"), // NOI18N
entry("compiler.warn.future.attr", "classfile"), // NOI18N
entry("compiler.warn.inconsistent.inner.classes", "classfile"), // NOI18N
entry("compiler.warn.runtime.invisible.parameter.annotations", "classfile"), // NOI18N
entry("compiler.warn.runtime.visible.invisible.param.annotations.mismatch", "classfile"), // NOI18N
entry("compiler.warn.unknown.enum.constant", "classfile"), // NOI18N
entry("compiler.warn.unknown.enum.constant.reason", "classfile"), // NOI18N
entry("compiler.warn.dangling.doc.comment", "dangling-doc-comments"), // NOI18N
entry("compiler.warn.missing.deprecated.annotation", "dep-ann"), // NOI18N
entry("compiler.warn.deprecated.annotation.has.no.effect", "deprecation"), // NOI18N
entry("compiler.warn.has.been.deprecated", "deprecation"), // NOI18N
entry("compiler.warn.has.been.deprecated.module", "deprecation"), // NOI18N
entry("compiler.warn.div.zero", "divzero"), // NOI18N
entry("compiler.warn.empty.if", "empty"), // NOI18N
entry("compiler.warn.leaks.not.accessible", "exports"), // NOI18N
entry("compiler.warn.leaks.not.accessible.not.required.transitive", "exports"), // NOI18N
entry("compiler.warn.leaks.not.accessible.unexported", "exports"), // NOI18N
entry("compiler.warn.leaks.not.accessible.unexported.qualified", "exports"), // NOI18N
entry("compiler.warn.possible.fall-through.into.case", "fallthrough"), // NOI18N
entry("compiler.warn.finally.cannot.complete", "finally"), // NOI18N
entry("compiler.warn.attempt.to.synchronize.on.instance.of.value.based.class", "identity"), // NOI18N
entry("compiler.warn.attempt.to.use.value.based.where.identity.expected", "identity"), // NOI18N
entry("compiler.warn.incubating.modules", "incubating"), // NOI18N
entry("compiler.warn.bit.shift.out.of.range", "lossy-conversions"), // NOI18N
entry("compiler.warn.possible.loss.of.precision", "lossy-conversions"), // NOI18N
entry("compiler.warn.missing-explicit-ctor", "missing-explicit-ctor"), // NOI18N
entry("compiler.warn.module.not.found", "module"), // NOI18N
entry("compiler.warn.poor.choice.for.module.name", "module"), // NOI18N
entry("compiler.warn.package.empty.or.not.found", "opens"), // NOI18N
entry("compiler.warn.addopens.ignored", "options"), // NOI18N
entry("compiler.warn.module.for.option.not.found", "options"), // NOI18N
entry("compiler.warn.option.obsolete.source", "options"), // NOI18N
entry("compiler.warn.option.obsolete.suppression", "options"), // NOI18N
entry("compiler.warn.option.obsolete.target", "options"), // NOI18N
entry("compiler.warn.source.no.bootclasspath", "options"), // NOI18N
entry("compiler.warn.source.no.system.modules.path", "options"), // NOI18N
entry("compiler.warn.output.file.clash", "output-file-clash"), // NOI18N
entry("compiler.warn.potentially.ambiguous.overload", "overloads"), // NOI18N
entry("compiler.warn.override.equals.but.not.hashcode", "overrides"), // NOI18N
entry("compiler.warn.override.varargs.extra", "overrides"), // NOI18N
entry("compiler.warn.override.varargs.missing", "overrides"), // NOI18N
entry("compiler.warn.dir.path.element.not.directory", "path"), // NOI18N
entry("compiler.warn.dir.path.element.not.found", "path"), // NOI18N
entry("compiler.warn.invalid.archive.file", "path"), // NOI18N
entry("compiler.warn.invalid.path", "path"), // NOI18N
entry("compiler.warn.locn.unknown.file.on.module.path", "path"), // NOI18N
entry("compiler.warn.outdir.is.in.exploded.module", "path"), // NOI18N
entry("compiler.warn.path.element.not.found", "path"), // NOI18N
entry("compiler.warn.unexpected.archive.file", "path"), // NOI18N
entry("compiler.warn.declared.using.preview", "preview"), // NOI18N
entry("compiler.warn.is.preview", "preview"), // NOI18N
entry("compiler.warn.is.preview.reflective", "preview"), // NOI18N
entry("compiler.warn.preview.feature.use", "preview"), // NOI18N
entry("compiler.warn.preview.feature.use.classfile", "preview"), // NOI18N
entry("compiler.warn.preview.feature.use.plural", "preview"), // NOI18N
entry("compiler.warn.proc.annotations.without.processors", "processing"), // NOI18N
entry("compiler.warn.proc.duplicate.option.name", "processing"), // NOI18N
entry("compiler.warn.proc.duplicate.supported.annotation", "processing"), // NOI18N
entry("compiler.warn.proc.file.reopening", "processing"), // NOI18N
entry("compiler.warn.proc.illegal.file.name", "processing"), // NOI18N
entry("compiler.warn.proc.malformed.supported.string", "processing"), // NOI18N
entry("compiler.warn.proc.redundant.types.with.wildcard", "processing"), // NOI18N
entry("compiler.warn.proc.suspicious.class.name", "processing"), // NOI18N
entry("compiler.warn.proc.type.already.exists", "processing"), // NOI18N
entry("compiler.warn.proc.type.recreate", "processing"), // NOI18N
entry("compiler.warn.raw.class.use", "rawtypes"), // NOI18N
entry("compiler.warn.has.been.deprecated.for.removal", "removal"), // NOI18N
entry("compiler.warn.has.been.deprecated.for.removal.module", "removal"), // NOI18N
entry("compiler.warn.requires.automatic", "requires-automatic"), // NOI18N
entry("compiler.warn.requires.transitive.automatic", "requires-transitive-automatic"), // NOI18N
entry("compiler.warn.restricted.method", "restricted"), // NOI18N
entry("compiler.warn.OSF.array.SPF", "serial"), // NOI18N
entry("compiler.warn.SPF.null.init", "serial"), // NOI18N
entry("compiler.warn.access.to.member.from.serializable.element", "serial"), // NOI18N
entry("compiler.warn.access.to.member.from.serializable.lambda", "serial"), // NOI18N
entry("compiler.warn.constant.SVUID", "serial"), // NOI18N
entry("compiler.warn.default.ineffective", "serial"), // NOI18N
entry("compiler.warn.externalizable.missing.public.no.arg.ctor", "serial"), // NOI18N
entry("compiler.warn.improper.SPF", "serial"), // NOI18N
entry("compiler.warn.improper.SVUID", "serial"), // NOI18N
entry("compiler.warn.ineffectual.extern.method.enum", "serial"), // NOI18N
entry("compiler.warn.ineffectual.externalizable.method.record", "serial"), // NOI18N
entry("compiler.warn.ineffectual.serial.field.enum", "serial"), // NOI18N
entry("compiler.warn.ineffectual.serial.field.externalizable", "serial"), // NOI18N
entry("compiler.warn.ineffectual.serial.field.interface", "serial"), // NOI18N
entry("compiler.warn.ineffectual.serial.field.record", "serial"), // NOI18N
entry("compiler.warn.ineffectual.serial.method.enum", "serial"), // NOI18N
entry("compiler.warn.ineffectual.serial.method.externalizable", "serial"), // NOI18N
entry("compiler.warn.ineffectual.serial.method.record", "serial"), // NOI18N
entry("compiler.warn.long.SVUID", "serial"), // NOI18N
entry("compiler.warn.missing.SVUID", "serial"), // NOI18N
entry("compiler.warn.non.private.method.weaker.access", "serial"), // NOI18N
entry("compiler.warn.non.serializable.instance.field", "serial"), // NOI18N
entry("compiler.warn.non.serializable.instance.field.array", "serial"), // NOI18N
entry("compiler.warn.serial.concrete.instance.method", "serial"), // NOI18N
entry("compiler.warn.serial.method.no.args", "serial"), // NOI18N
entry("compiler.warn.serial.method.not.private", "serial"), // NOI18N
entry("compiler.warn.serial.method.one.arg", "serial"), // NOI18N
entry("compiler.warn.serial.method.parameter.type", "serial"), // NOI18N
entry("compiler.warn.serial.method.static", "serial"), // NOI18N
entry("compiler.warn.serial.method.unexpected.exception", "serial"), // NOI18N
entry("compiler.warn.serial.method.unexpected.return.type", "serial"), // NOI18N
entry("compiler.warn.serializable.missing.access.no.arg.ctor", "serial"), // NOI18N
entry("compiler.warn.static.not.qualified.by.type", "static"), // NOI18N
entry("compiler.warn.static.not.qualified.by.type2", "static"), // NOI18N
entry("compiler.warn.strictfp", "strictfp"), // NOI18N
entry("compiler.warn.inconsistent.white.space.indentation", "text-blocks"), // NOI18N
entry("compiler.warn.trailing.white.space.will.be.removed", "text-blocks"), // NOI18N
entry("compiler.warn.possible.this.escape", "this-escape"), // NOI18N
entry("compiler.warn.possible.this.escape.location", "this-escape"), // NOI18N
entry("compiler.warn.try.explicit.close.call", "try"), // NOI18N
entry("compiler.warn.try.resource.can.throw.interrupted.exc", "try"), // NOI18N
entry("compiler.warn.try.resource.not.referenced", "try"), // NOI18N
entry("compiler.warn.try.resource.throws.interrupted.exc", "try"), // NOI18N
entry("compiler.warn.override.unchecked.ret", "unchecked"), // NOI18N
entry("compiler.warn.override.unchecked.thrown", "unchecked"), // NOI18N
entry("compiler.warn.prob.found.req", "unchecked"), // NOI18N
entry("compiler.warn.unchecked.assign", "unchecked"), // NOI18N
entry("compiler.warn.unchecked.assign.to.var", "unchecked"), // NOI18N
entry("compiler.warn.unchecked.call.mbr.of.raw.type", "unchecked"), // NOI18N
entry("compiler.warn.unchecked.cast.to.type", "unchecked"), // NOI18N
entry("compiler.warn.unchecked.generic.array.creation", "unchecked"), // NOI18N
entry("compiler.warn.unchecked.meth.invocation.applied", "unchecked"), // NOI18N
entry("compiler.warn.unchecked.varargs.non.reifiable.type", "unchecked"), // NOI18N
entry("compiler.warn.varargs.redundant.trustme.anno", "varargs"), // NOI18N
entry("compiler.warn.varargs.unsafe.use.varargs.param", "varargs") // NOI18N
);
}


public SuppressWarningsFixer() {
}

@Override
public Set<String> getCodes() {
return KEY2SUPRESS_KEY.keySet();
return DIAG2LINT.keySet();
}

public List<Fix> run(CompilationInfo compilationInfo, String diagnosticKey,
int offset, TreePath treePath,
Data<Void> data) {
String suppressKey = KEY2SUPRESS_KEY.get(diagnosticKey);

if (suppressKey != null) {
return FixFactory.createSuppressWarnings(compilationInfo, treePath, suppressKey);
}

return Collections.<Fix>emptyList();
@Override
public List<Fix> run(CompilationInfo compilationInfo, String diagnosticKey, int offset, TreePath treePath, Data<Void> data) {
String suppressKey = DIAG2LINT.get(diagnosticKey);
return suppressKey != null
? FixFactory.createSuppressWarnings(compilationInfo, treePath, suppressKey)
: List.of();
}

@Override
public void cancel() {
}

@Override
public String getId() {
return "SuppressWarningsFixer"; // NOI18N
return getClass().getSimpleName();
}

@Override
public String getDisplayName() {
return NbBundle.getMessage(SuppressWarningsFixer.class, "LBL_Suppress_Waning"); // NOI18N
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,16 @@
*/
package org.netbeans.modules.java.hints.errors;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.TreeSet;
import java.util.stream.Collectors;
import org.netbeans.modules.java.hints.infrastructure.HintsTestBase;
import org.netbeans.modules.java.hints.spiimpl.TestCompilerSettings;

Expand Down Expand Up @@ -92,7 +102,78 @@ public void testSuppressWarnings11() throws Exception {
}

public void testSuppressWarnings106794() throws Exception {
performTestDoNotPerform("Test3", 3, 10);
performTestDoNotPerform("Test3", 3, 10);
}

public void testDiag2LintMap() throws Exception {
// We could get the suppression keys via Lint.LintCategory but not the diag keys.
// The diag keys are in a properties file and the suppression key in comments, so we have to get them the hacky way to create a mapping.
// https://github.com/openjdk/jdk/blob/master/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler.properties
try (BufferedReader reader = new BufferedReader(
new InputStreamReader(SuppressWarningsFixer.class.getResourceAsStream("/com/sun/tools/javac/resources/compiler.properties")))) {

List<String> lines = readAllLines(reader);

Map<String, Set<String>> mapping = new TreeMap<>();
// Gatherer with windowSliding(3)
for (int i = 0; i < lines.size(); i++) {
// # 0: symbol
// # lint: removal
// # flags: aggregate, mandatory, default-enabled
// compiler.warn.has.been.deprecated.for.removal.module=\
// module {0} has been deprecated and marked for removal
String comment = lines.get(i);
if (comment.startsWith("# lint: ")) {
String lintCategory = comment.substring(8).strip();
String propertyLine = lines.get(i + 1).startsWith("#") // skip line if still a comment
? lines.get(i + 2)
: lines.get(i + 1);
String diagKey = propertyLine.substring(0, propertyLine.indexOf("=")).strip();
mapping.computeIfAbsent(lintCategory, k -> new TreeSet<>())
.add(diagKey);
}
}

Set<String> diagKeys = mapping.values().stream().flatMap(Set::stream).collect(Collectors.toSet());
for (String key : diagKeys) {
if (!SuppressWarningsFixer.DIAG2LINT.containsKey(key)) {
dumpCodeSnippet(mapping);
fail(key + " not found");
}
}
if (diagKeys.size() != SuppressWarningsFixer.DIAG2LINT.size()) {
dumpCodeSnippet(mapping);
fail("map size does not match");
}
}
}

private void dumpCodeSnippet(Map<String, Set<String>> diag2lint) {
StringBuilder snippet = new StringBuilder();
snippet.append("Please update the keys in ").append(SuppressWarningsFixer.class.getName()).append("\n");
snippet.append("- - -\n");
for (Map.Entry<String, Set<String>> entry : diag2lint.entrySet()) {
for (String diag : entry.getValue()) {
snippet.append(
"entry(\"%s\", \"%s\"), // NOI18N\n"
.formatted(diag, entry.getKey())
.indent(12)
);
}
}
snippet.append("- - -\n");
System.out.println(snippet);
}

// TODO remove after JDK 25
private static List<String> readAllLines(BufferedReader reader) throws IOException {
List<String> lines = new ArrayList<>();
String line = reader.readLine();
while (line != null) {
lines.add(line);
line = reader.readLine();
}
return lines;
}

}