Skip to content

Commit 96fef40

Browse files
committedOct 16, 2021
8189591: No way to locally suppress doclint warnings
Reviewed-by: hannesw, prappo
1 parent 7fc3a8d commit 96fef40

File tree

4 files changed

+257
-10
lines changed

4 files changed

+257
-10
lines changed
 

‎src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclint/Env.java

+107-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2012, 2020, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2012, 2021, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -26,21 +26,31 @@
2626
package jdk.javadoc.internal.doclint;
2727

2828

29+
import java.util.ArrayList;
2930
import java.util.Arrays;
31+
import java.util.Collections;
32+
import java.util.EnumSet;
33+
import java.util.HashMap;
3034
import java.util.HashSet;
3135
import java.util.LinkedHashSet;
3236
import java.util.List;
37+
import java.util.Map;
3338
import java.util.Set;
39+
import java.util.function.Function;
3440
import java.util.regex.Pattern;
41+
import java.util.stream.Collectors;
3542

43+
import javax.lang.model.element.AnnotationMirror;
44+
import javax.lang.model.element.AnnotationValue;
3645
import javax.lang.model.element.Element;
3746
import javax.lang.model.element.ElementKind;
3847
import javax.lang.model.element.ExecutableElement;
3948
import javax.lang.model.element.Modifier;
49+
import javax.lang.model.type.DeclaredType;
50+
import javax.lang.model.type.TypeKind;
4051
import javax.lang.model.type.TypeMirror;
4152
import javax.lang.model.util.Elements;
4253
import javax.lang.model.util.Types;
43-
import javax.tools.Diagnostic.Kind;
4454

4555
import com.sun.source.doctree.DocCommentTree;
4656
import com.sun.source.tree.CompilationUnitTree;
@@ -106,6 +116,7 @@ else if (mods.contains(Modifier.PRIVATE))
106116
// Types used when analysing doc comments.
107117
TypeMirror java_lang_Error;
108118
TypeMirror java_lang_RuntimeException;
119+
TypeMirror java_lang_SuppressWarnings;
109120
TypeMirror java_lang_Throwable;
110121
TypeMirror java_lang_Void;
111122

@@ -125,6 +136,9 @@ else if (mods.contains(Modifier.PRIVATE))
125136
/** The set of methods, if any, that the current declaration overrides. */
126137
Set<? extends ExecutableElement> currOverriddenMethods;
127138

139+
/** A map containing the info derived from {@code @SuppressWarnings} for an element. */
140+
Map<Element, Set<Messages.Group>> suppressWarnings = new HashMap<>();
141+
128142
Env() {
129143
messages = new Messages(this);
130144
}
@@ -145,6 +159,7 @@ void initTypes() {
145159

146160
java_lang_Error = elements.getTypeElement("java.lang.Error").asType();
147161
java_lang_RuntimeException = elements.getTypeElement("java.lang.RuntimeException").asType();
162+
java_lang_SuppressWarnings = elements.getTypeElement("java.lang.SuppressWarnings").asType();
148163
java_lang_Throwable = elements.getTypeElement("java.lang.Throwable").asType();
149164
java_lang_Void = elements.getTypeElement("java.lang.Void").asType();
150165
}
@@ -247,6 +262,96 @@ boolean shouldCheck(CompilationUnitTree unit) {
247262
return true;
248263
}
249264

265+
/**
266+
* {@return whether or not warnings in a group are suppressed for the current element}
267+
* @param g the group
268+
*/
269+
boolean suppressWarnings(Messages.Group g) {
270+
return suppressWarnings(currElement, g);
271+
}
272+
273+
/**
274+
* {@return whether or not warnings in a group are suppressed for a given element}
275+
* @param e the element
276+
* @param g the group
277+
*/
278+
boolean suppressWarnings(Element e, Messages.Group g) {
279+
// check if warnings are suppressed in any enclosing classes
280+
Element encl = e.getEnclosingElement();
281+
if (encl != null && encl.asType().getKind() == TypeKind.DECLARED) {
282+
if (suppressWarnings(encl, g)) {
283+
return true;
284+
}
285+
}
286+
287+
// check the local @SuppressWarnings annotation, caching the results
288+
return suppressWarnings.computeIfAbsent(e, this::getSuppressedGroups).contains(g);
289+
}
290+
291+
/**
292+
* Returns the set of groups for an element for which messages should be suppressed.
293+
* The set is determined by examining the arguments for any {@code @SuppressWarnings}
294+
* annotation that may be present on the element.
295+
* The supported strings are: "doclint" and "doclint:GROUP,..." for each GROUP
296+
*
297+
* @param e the element
298+
* @return the set
299+
*/
300+
private Set<Messages.Group> getSuppressedGroups(Element e) {
301+
var gMap = Arrays.stream(Messages.Group.values())
302+
.collect(Collectors.toMap(Messages.Group::optName, Function.identity()));
303+
var set = EnumSet.noneOf(Messages.Group.class);
304+
for (String arg : getSuppressWarningsValue(e)) {
305+
if (arg.equals("doclint")) {
306+
set = EnumSet.allOf(Messages.Group.class);
307+
break;
308+
} else if (arg.startsWith("doclint:")) {
309+
final int len = "doclint:".length();
310+
for (String a : arg.substring(len).split(",")) {
311+
Messages.Group argGroup = gMap.get(a);
312+
if (argGroup != null) {
313+
set.add(argGroup);
314+
}
315+
}
316+
}
317+
}
318+
return set;
319+
}
320+
321+
/**
322+
* Returns the list of values given to an instance of {@code @SuppressWarnings} for an element,
323+
* or an empty list if there is no annotation.
324+
*
325+
* @param e the element
326+
* @return the list
327+
*/
328+
private List<String> getSuppressWarningsValue(Element e) {
329+
for (AnnotationMirror am : e.getAnnotationMirrors()) {
330+
DeclaredType dt = am.getAnnotationType();
331+
if (types.isSameType(dt, java_lang_SuppressWarnings)) {
332+
var values = am.getElementValues();
333+
for (var entry : values.entrySet()) {
334+
if (entry.getKey().getSimpleName().contentEquals("value")) {
335+
AnnotationValue av = entry.getValue();
336+
if (av.getValue() instanceof List<?> list) {
337+
List<String> result = new ArrayList<>();
338+
for (var item : list) {
339+
if (item instanceof AnnotationValue avItem
340+
&& avItem.getValue() instanceof String s) {
341+
result.add(s);
342+
}
343+
}
344+
return result;
345+
}
346+
}
347+
}
348+
349+
}
350+
}
351+
return Collections.emptyList();
352+
}
353+
354+
250355
private <T extends Comparable<T>> T min(T item1, T item2) {
251356
return (item1 == null) ? item2
252357
: (item2 == null) ? item1

‎src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclint/Messages.java

+9-7
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2012, 2021, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -115,6 +115,9 @@ void reportStats(PrintWriter out) {
115115

116116
protected void report(Group group, Diagnostic.Kind dkind, DocTree tree, String code, Object... args) {
117117
if (options.isEnabled(group, env.currAccess)) {
118+
if (dkind == Diagnostic.Kind.WARNING && env.suppressWarnings(group)) {
119+
return;
120+
}
118121
String msg = (code == null) ? (String) args[0] : localize(code, args);
119122
env.trees.printMessage(dkind, msg, tree,
120123
env.currDocComment, env.currPath.getCompilationUnit());
@@ -125,6 +128,9 @@ protected void report(Group group, Diagnostic.Kind dkind, DocTree tree, String c
125128

126129
protected void report(Group group, Diagnostic.Kind dkind, Tree tree, String code, Object... args) {
127130
if (options.isEnabled(group, env.currAccess)) {
131+
if (dkind == Diagnostic.Kind.WARNING && env.suppressWarnings(group)) {
132+
return;
133+
}
128134
String msg = localize(code, args);
129135
env.trees.printMessage(dkind, msg, tree, env.currPath.getCompilationUnit());
130136

@@ -315,18 +321,14 @@ void report(PrintWriter out) {
315321
*/
316322
private static class Table {
317323

318-
private static final Comparator<Integer> DECREASING = (o1, o2) -> o2.compareTo(o1);
324+
private static final Comparator<Integer> DECREASING = Comparator.reverseOrder();
319325
private final TreeMap<Integer, Set<String>> map = new TreeMap<>(DECREASING);
320326

321327
void put(String label, int n) {
322328
if (n == 0) {
323329
return;
324330
}
325-
Set<String> labels = map.get(n);
326-
if (labels == null) {
327-
map.put(n, labels = new TreeSet<>());
328-
}
329-
labels.add(label);
331+
map.computeIfAbsent(n, k -> new TreeSet<>()).add(label);
330332
}
331333

332334
void print(PrintWriter out) {

‎test/langtools/jdk/javadoc/doclet/testJavaFX/TestJavaFX.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -456,7 +456,7 @@ public class BooleanProperty { }
456456
checkExit(Exit.OK);
457457

458458
checkOutput(Output.OUT, false,
459-
"warning",
459+
": warning:",
460460
"no comment");
461461
}
462462
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
/*
2+
* Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* This code is free software; you can redistribute it and/or modify it
6+
* under the terms of the GNU General Public License version 2 only, as
7+
* published by the Free Software Foundation.
8+
*
9+
* This code is distributed in the hope that it will be useful, but WITHOUT
10+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12+
* version 2 for more details (a copy is included in the LICENSE file that
13+
* accompanied this code).
14+
*
15+
* You should have received a copy of the GNU General Public License version
16+
* 2 along with this work; if not, write to the Free Software Foundation,
17+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18+
*
19+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20+
* or visit www.oracle.com if you need additional information or have any
21+
* questions.
22+
*/
23+
24+
/*
25+
* @test
26+
* @bug 8189591
27+
* @summary No way to locally suppress doclint warnings
28+
* @library /tools/lib
29+
* @modules jdk.javadoc/jdk.javadoc.internal.doclint
30+
* @build toolbox.ToolBox SuppressWarningsTest
31+
* @run main SuppressWarningsTest
32+
*/
33+
34+
import java.io.PrintStream;
35+
import java.io.PrintWriter;
36+
import java.io.StringWriter;
37+
import java.nio.file.Files;
38+
import java.nio.file.Path;
39+
import java.util.List;
40+
41+
import jdk.javadoc.internal.doclint.DocLint;
42+
import toolbox.ToolBox;
43+
44+
public class SuppressWarningsTest {
45+
public static void main(String... args) throws Exception {
46+
new SuppressWarningsTest().run();
47+
}
48+
49+
enum Kind {
50+
NONE("// no annotation"),
51+
MISSING("@SuppressWarnings(\"doclint:missing\")"),
52+
OTHER("@SuppressWarnings(\"doclint:html\")"),
53+
ALL("@SuppressWarnings(\"doclint\")");
54+
final String anno;
55+
Kind(String anno) {
56+
this.anno = anno;
57+
}
58+
}
59+
60+
ToolBox tb = new ToolBox();
61+
PrintStream log = System.err;
62+
63+
void run() throws Exception {
64+
for (Kind ok : Kind.values()) {
65+
for (Kind ik : Kind.values()) {
66+
for (Kind mk : Kind.values()) {
67+
test(ok, ik, mk);
68+
}
69+
}
70+
}
71+
72+
if (errorCount == 0) {
73+
log.println("No errors");
74+
} else {
75+
log.println(errorCount + " errors");
76+
throw new Exception(errorCount + " errors");
77+
}
78+
}
79+
80+
void test(Kind outerKind, Kind innerKind, Kind memberKind) throws Exception {
81+
log.println("Test: outer:" + outerKind + " inner: " + innerKind + " member:" + memberKind);
82+
Path base = Path.of(outerKind + "-" + innerKind + "-" + memberKind);
83+
Files.createDirectories(base);
84+
Path src = base.resolve("src");
85+
tb.writeJavaFiles(src, """
86+
/** . */
87+
##OUTER##
88+
public class Outer {
89+
/** . */
90+
private Outer() { }
91+
/** . */
92+
##INNER##
93+
public class Inner {
94+
/** . */
95+
private Inner() { }
96+
##MEMBER##
97+
public void m() { }
98+
}
99+
}
100+
"""
101+
.replace("##OUTER##", outerKind.anno)
102+
.replace("##INNER##", innerKind.anno)
103+
.replace("##MEMBER##", memberKind.anno));
104+
105+
DocLint dl = new DocLint();
106+
StringWriter sw = new StringWriter();
107+
try (PrintWriter pw = new PrintWriter(sw)) {
108+
dl.run(pw, src.resolve("Outer.java").toString());
109+
}
110+
String out = sw.toString();
111+
out.lines().forEach(System.err::println);
112+
113+
boolean expectSuppressed = false;
114+
for (Kind k : List.of(outerKind, innerKind, memberKind)) {
115+
expectSuppressed |= k == Kind.ALL || k == Kind.MISSING;
116+
}
117+
boolean foundWarning = out.contains("warning: no comment");
118+
if (expectSuppressed) {
119+
if (foundWarning) {
120+
error("found unexpected warning");
121+
} else {
122+
log.println("Passed: no warning, as expected");
123+
}
124+
} else {
125+
if (foundWarning) {
126+
log.println("Passed: found warning, as expected");
127+
} else {
128+
error("no warning");
129+
}
130+
}
131+
log.println();
132+
}
133+
134+
int errorCount;
135+
136+
void error(String message) {
137+
log.println("Error: " + message);
138+
errorCount++;
139+
}
140+
}

0 commit comments

Comments
 (0)
Please sign in to comment.