1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package com.puppycrawl.tools.checkstyle.checks.annotation;
21
22 import java.util.ArrayDeque;
23 import java.util.Deque;
24 import java.util.Objects;
25 import java.util.regex.Matcher;
26 import java.util.regex.Pattern;
27
28 import com.puppycrawl.tools.checkstyle.StatelessCheck;
29 import com.puppycrawl.tools.checkstyle.api.AbstractCheck;
30 import com.puppycrawl.tools.checkstyle.api.DetailAST;
31 import com.puppycrawl.tools.checkstyle.api.TokenTypes;
32 import com.puppycrawl.tools.checkstyle.utils.AnnotationUtil;
33 import com.puppycrawl.tools.checkstyle.utils.CommonUtil;
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80 @StatelessCheck
81 public class SuppressWarningsCheck extends AbstractCheck {
82
83
84
85
86
87 public static final String MSG_KEY_SUPPRESSED_WARNING_NOT_ALLOWED =
88 "suppressed.warning.not.allowed";
89
90
91 private static final String SUPPRESS_WARNINGS = "SuppressWarnings";
92
93
94
95
96
97 private static final String FQ_SUPPRESS_WARNINGS =
98 "java.lang." + SUPPRESS_WARNINGS;
99
100
101
102
103
104 private Pattern format = Pattern.compile("^\\s*+$");
105
106
107
108
109
110
111
112
113 public final void setFormat(Pattern pattern) {
114 format = pattern;
115 }
116
117 @Override
118 public final int[] getDefaultTokens() {
119 return getAcceptableTokens();
120 }
121
122 @Override
123 public final int[] getAcceptableTokens() {
124 return new int[] {
125 TokenTypes.CLASS_DEF,
126 TokenTypes.INTERFACE_DEF,
127 TokenTypes.ENUM_DEF,
128 TokenTypes.ANNOTATION_DEF,
129 TokenTypes.ANNOTATION_FIELD_DEF,
130 TokenTypes.ENUM_CONSTANT_DEF,
131 TokenTypes.PARAMETER_DEF,
132 TokenTypes.VARIABLE_DEF,
133 TokenTypes.METHOD_DEF,
134 TokenTypes.CTOR_DEF,
135 TokenTypes.COMPACT_CTOR_DEF,
136 TokenTypes.RECORD_DEF,
137 TokenTypes.PATTERN_VARIABLE_DEF,
138 };
139 }
140
141 @Override
142 public int[] getRequiredTokens() {
143 return CommonUtil.EMPTY_INT_ARRAY;
144 }
145
146 @Override
147 public void visitToken(final DetailAST ast) {
148 final DetailAST annotation = getSuppressWarnings(ast);
149
150 if (annotation != null) {
151 final DetailAST warningHolder =
152 findWarningsHolder(annotation);
153 final DetailAST token =
154 warningHolder.findFirstToken(TokenTypes.ANNOTATION_MEMBER_VALUE_PAIR);
155
156
157 final DetailAST parent = Objects.requireNonNullElse(token, warningHolder);
158 final DetailAST warning = parent.findFirstToken(TokenTypes.EXPR);
159
160 if (warning == null) {
161
162 logMatch(warningHolder, "");
163 }
164 else {
165 processWarnings(warning);
166 }
167 }
168 }
169
170
171
172
173
174
175 private void processWarnings(final DetailAST warning) {
176 for (DetailAST current = warning; current != null; current = current.getNextSibling()) {
177 if (current.getType() == TokenTypes.EXPR) {
178 processWarningExpr(current.getFirstChild(), current);
179 }
180 }
181 }
182
183
184
185
186
187
188
189 private void processWarningExpr(final DetailAST fChild, final DetailAST warning) {
190 switch (fChild.getType()) {
191 case TokenTypes.STRING_LITERAL -> logMatch(warning,
192 removeQuotes(warning.getFirstChild().getText()));
193
194 case TokenTypes.QUESTION ->
195
196 walkConditional(fChild);
197
198 default -> {
199
200
201
202
203
204
205
206
207 }
208 }
209 }
210
211
212
213
214
215
216
217
218
219 private static DetailAST getSuppressWarnings(DetailAST ast) {
220 DetailAST annotation = AnnotationUtil.getAnnotation(ast, SUPPRESS_WARNINGS);
221
222 if (annotation == null) {
223 annotation = AnnotationUtil.getAnnotation(ast, FQ_SUPPRESS_WARNINGS);
224 }
225 return annotation;
226 }
227
228
229
230
231
232
233
234
235 private void logMatch(DetailAST ast, final String warningText) {
236 final Matcher matcher = format.matcher(warningText);
237 if (matcher.matches()) {
238 log(ast,
239 MSG_KEY_SUPPRESSED_WARNING_NOT_ALLOWED, warningText);
240 }
241 }
242
243
244
245
246
247
248
249 private static DetailAST findWarningsHolder(final DetailAST annotation) {
250 final DetailAST annValuePair =
251 annotation.findFirstToken(TokenTypes.ANNOTATION_MEMBER_VALUE_PAIR);
252
253 final DetailAST annArrayInitParent = Objects.requireNonNullElse(annValuePair, annotation);
254 final DetailAST annArrayInit = annArrayInitParent
255 .findFirstToken(TokenTypes.ANNOTATION_ARRAY_INIT);
256 return Objects.requireNonNullElse(annArrayInit, annotation);
257 }
258
259
260
261
262
263
264
265
266
267
268
269
270
271 private static String removeQuotes(final String warning) {
272 return warning.substring(1, warning.length() - 1);
273 }
274
275
276
277
278
279
280
281
282
283 private void walkConditional(final DetailAST cond) {
284 final Deque<DetailAST> condStack = new ArrayDeque<>();
285 condStack.push(cond);
286
287 while (!condStack.isEmpty()) {
288 final DetailAST currentCond = condStack.pop();
289 if (currentCond.getType() == TokenTypes.QUESTION) {
290 condStack.push(getCondRight(currentCond));
291 condStack.push(getCondLeft(currentCond));
292 }
293 else {
294 final String warningText = removeQuotes(currentCond.getText());
295 logMatch(currentCond, warningText);
296 }
297 }
298 }
299
300
301
302
303
304
305
306
307
308 private static DetailAST getCondLeft(final DetailAST cond) {
309 final DetailAST colon = cond.findFirstToken(TokenTypes.COLON);
310 return colon.getPreviousSibling();
311 }
312
313
314
315
316
317
318
319
320
321 private static DetailAST getCondRight(final DetailAST cond) {
322 final DetailAST colon = cond.findFirstToken(TokenTypes.COLON);
323 return colon.getNextSibling();
324 }
325
326 }