1 | |
|
2 | |
|
3 | |
|
4 | |
|
5 | |
|
6 | |
|
7 | |
|
8 | |
|
9 | |
|
10 | |
|
11 | |
|
12 | |
|
13 | |
|
14 | |
|
15 | |
|
16 | |
|
17 | |
|
18 | |
|
19 | |
package com.puppycrawl.tools.checkstyle.checks.javadoc; |
20 | |
|
21 | |
import antlr.collections.AST; |
22 | |
import com.google.common.collect.Lists; |
23 | |
import com.google.common.collect.Sets; |
24 | |
import com.puppycrawl.tools.checkstyle.api.DetailAST; |
25 | |
import com.puppycrawl.tools.checkstyle.api.FileContents; |
26 | |
import com.puppycrawl.tools.checkstyle.api.FullIdent; |
27 | |
import com.puppycrawl.tools.checkstyle.api.JavadocTagInfo; |
28 | |
import com.puppycrawl.tools.checkstyle.api.Scope; |
29 | |
import com.puppycrawl.tools.checkstyle.api.ScopeUtils; |
30 | |
import com.puppycrawl.tools.checkstyle.api.TextBlock; |
31 | |
import com.puppycrawl.tools.checkstyle.api.TokenTypes; |
32 | |
import com.puppycrawl.tools.checkstyle.api.Utils; |
33 | |
import com.puppycrawl.tools.checkstyle.checks.AbstractTypeAwareCheck; |
34 | |
import com.puppycrawl.tools.checkstyle.checks.CheckUtils; |
35 | |
import java.util.Iterator; |
36 | |
import java.util.List; |
37 | |
import java.util.ListIterator; |
38 | |
import java.util.Set; |
39 | |
import java.util.regex.Matcher; |
40 | |
import java.util.regex.Pattern; |
41 | |
|
42 | |
|
43 | |
|
44 | |
|
45 | |
|
46 | |
|
47 | |
|
48 | |
|
49 | 104 | public class JavadocMethodCheck extends AbstractTypeAwareCheck |
50 | |
{ |
51 | |
|
52 | 1 | private static final Pattern MATCH_JAVADOC_ARG = |
53 | |
Utils.createPattern("@(throws|exception|param)\\s+(\\S+)\\s+\\S*"); |
54 | |
|
55 | |
|
56 | 1 | private static final Pattern MATCH_JAVADOC_ARG_MULTILINE_START = |
57 | |
Utils.createPattern("@(throws|exception|param)\\s+(\\S+)\\s*$"); |
58 | |
|
59 | |
|
60 | 1 | private static final Pattern MATCH_JAVADOC_MULTILINE_CONT = |
61 | |
Utils.createPattern("(\\*/|@|[^\\s\\*])"); |
62 | |
|
63 | |
|
64 | |
private static final String END_JAVADOC = "*/"; |
65 | |
|
66 | |
private static final String NEXT_TAG = "@"; |
67 | |
|
68 | |
|
69 | 1 | private static final Pattern MATCH_JAVADOC_NOARG = |
70 | |
Utils.createPattern("@(return|see)\\s+\\S"); |
71 | |
|
72 | 1 | private static final Pattern MATCH_JAVADOC_NOARG_MULTILINE_START = |
73 | |
Utils.createPattern("@(return|see)\\s*$"); |
74 | |
|
75 | 1 | private static final Pattern MATCH_JAVADOC_NOARG_CURLY = |
76 | |
Utils.createPattern("\\{\\s*@(inheritDoc)\\s*\\}"); |
77 | |
|
78 | |
|
79 | |
private static final int MAX_CHILDREN = 7; |
80 | |
|
81 | |
|
82 | |
private static final int BODY_SIZE = 3; |
83 | |
|
84 | |
|
85 | 25 | private Scope mScope = Scope.PRIVATE; |
86 | |
|
87 | |
|
88 | |
private Scope mExcludeScope; |
89 | |
|
90 | |
|
91 | |
|
92 | |
|
93 | |
|
94 | |
private boolean mAllowUndeclaredRTE; |
95 | |
|
96 | |
|
97 | |
|
98 | |
|
99 | |
|
100 | |
private boolean mAllowThrowsTagsForSubclasses; |
101 | |
|
102 | |
|
103 | |
|
104 | |
|
105 | |
|
106 | |
private boolean mAllowMissingParamTags; |
107 | |
|
108 | |
|
109 | |
|
110 | |
|
111 | |
|
112 | |
|
113 | |
private boolean mAllowMissingThrowsTags; |
114 | |
|
115 | |
|
116 | |
|
117 | |
|
118 | |
|
119 | |
private boolean mAllowMissingReturnTag; |
120 | |
|
121 | |
|
122 | |
|
123 | |
|
124 | |
|
125 | |
private boolean mAllowMissingJavadoc; |
126 | |
|
127 | |
|
128 | |
|
129 | |
|
130 | |
|
131 | |
private boolean mAllowMissingPropertyJavadoc; |
132 | |
|
133 | |
|
134 | |
|
135 | |
|
136 | |
|
137 | |
|
138 | |
public void setScope(String aFrom) |
139 | |
{ |
140 | 7 | mScope = Scope.getInstance(aFrom); |
141 | 7 | } |
142 | |
|
143 | |
|
144 | |
|
145 | |
|
146 | |
|
147 | |
|
148 | |
public void setExcludeScope(String aScope) |
149 | |
{ |
150 | 1 | mExcludeScope = Scope.getInstance(aScope); |
151 | 1 | } |
152 | |
|
153 | |
|
154 | |
|
155 | |
|
156 | |
|
157 | |
|
158 | |
|
159 | |
public void setAllowUndeclaredRTE(boolean aFlag) |
160 | |
{ |
161 | 5 | mAllowUndeclaredRTE = aFlag; |
162 | 5 | } |
163 | |
|
164 | |
|
165 | |
|
166 | |
|
167 | |
|
168 | |
|
169 | |
|
170 | |
public void setAllowThrowsTagsForSubclasses(boolean aFlag) |
171 | |
{ |
172 | 5 | mAllowThrowsTagsForSubclasses = aFlag; |
173 | 5 | } |
174 | |
|
175 | |
|
176 | |
|
177 | |
|
178 | |
|
179 | |
|
180 | |
|
181 | |
public void setAllowMissingParamTags(boolean aFlag) |
182 | |
{ |
183 | 0 | mAllowMissingParamTags = aFlag; |
184 | 0 | } |
185 | |
|
186 | |
|
187 | |
|
188 | |
|
189 | |
|
190 | |
|
191 | |
|
192 | |
|
193 | |
public void setAllowMissingThrowsTags(boolean aFlag) |
194 | |
{ |
195 | 0 | mAllowMissingThrowsTags = aFlag; |
196 | 0 | } |
197 | |
|
198 | |
|
199 | |
|
200 | |
|
201 | |
|
202 | |
|
203 | |
|
204 | |
public void setAllowMissingReturnTag(boolean aFlag) |
205 | |
{ |
206 | 0 | mAllowMissingReturnTag = aFlag; |
207 | 0 | } |
208 | |
|
209 | |
|
210 | |
|
211 | |
|
212 | |
|
213 | |
|
214 | |
|
215 | |
public void setAllowMissingJavadoc(boolean aFlag) |
216 | |
{ |
217 | 1 | mAllowMissingJavadoc = aFlag; |
218 | 1 | } |
219 | |
|
220 | |
|
221 | |
|
222 | |
|
223 | |
|
224 | |
|
225 | |
|
226 | |
public void setAllowMissingPropertyJavadoc(final boolean aFlag) |
227 | |
{ |
228 | 1 | mAllowMissingPropertyJavadoc = aFlag; |
229 | 1 | } |
230 | |
|
231 | |
@Override |
232 | |
public int[] getDefaultTokens() |
233 | |
{ |
234 | 25 | return new int[] {TokenTypes.PACKAGE_DEF, TokenTypes.IMPORT, |
235 | |
TokenTypes.CLASS_DEF, TokenTypes.ENUM_DEF, |
236 | |
TokenTypes.METHOD_DEF, TokenTypes.CTOR_DEF, |
237 | |
TokenTypes.ANNOTATION_FIELD_DEF, |
238 | |
}; |
239 | |
} |
240 | |
|
241 | |
@Override |
242 | |
public int[] getAcceptableTokens() |
243 | |
{ |
244 | 0 | return new int[] {TokenTypes.METHOD_DEF, TokenTypes.CTOR_DEF, |
245 | |
TokenTypes.ANNOTATION_FIELD_DEF, |
246 | |
}; |
247 | |
} |
248 | |
|
249 | |
@Override |
250 | |
protected final void processAST(DetailAST aAST) |
251 | |
{ |
252 | 390 | final Scope theScope = calculateScope(aAST); |
253 | 390 | if (shouldCheck(aAST, theScope)) { |
254 | 312 | final FileContents contents = getFileContents(); |
255 | 312 | final TextBlock cmt = contents.getJavadocBefore(aAST.getLineNo()); |
256 | |
|
257 | 312 | if (cmt == null) { |
258 | 158 | if (!isMissingJavadocAllowed(aAST)) { |
259 | 117 | log(aAST, "javadoc.missing"); |
260 | |
} |
261 | |
} |
262 | |
else { |
263 | 154 | checkComment(aAST, cmt); |
264 | |
} |
265 | |
} |
266 | 390 | } |
267 | |
|
268 | |
@Override |
269 | |
protected final void logLoadError(Token aIdent) |
270 | |
{ |
271 | 0 | logLoadErrorImpl(aIdent.getLineNo(), aIdent.getColumnNo(), |
272 | |
"javadoc.classInfo", |
273 | |
JavadocTagInfo.THROWS.getText(), aIdent.getText()); |
274 | 0 | } |
275 | |
|
276 | |
|
277 | |
|
278 | |
|
279 | |
|
280 | |
|
281 | |
|
282 | |
|
283 | |
|
284 | |
|
285 | |
|
286 | |
|
287 | |
protected boolean isMissingJavadocAllowed(final DetailAST aAST) |
288 | |
{ |
289 | 158 | return mAllowMissingJavadoc || isOverrideMethod(aAST) |
290 | |
|| (mAllowMissingPropertyJavadoc |
291 | |
&& (isSetterMethod(aAST) || isGetterMethod(aAST))); |
292 | |
} |
293 | |
|
294 | |
|
295 | |
|
296 | |
|
297 | |
|
298 | |
|
299 | |
|
300 | |
|
301 | |
private boolean shouldCheck(final DetailAST aAST, final Scope aScope) |
302 | |
{ |
303 | 390 | final Scope surroundingScope = ScopeUtils.getSurroundingScope(aAST); |
304 | |
|
305 | 390 | return aScope.isIn(mScope) |
306 | |
&& surroundingScope.isIn(mScope) |
307 | |
&& ((mExcludeScope == null) || !aScope.isIn(mExcludeScope) |
308 | |
|| !surroundingScope.isIn(mExcludeScope)); |
309 | |
} |
310 | |
|
311 | |
|
312 | |
|
313 | |
|
314 | |
|
315 | |
|
316 | |
|
317 | |
private void checkComment(DetailAST aAST, TextBlock aComment) |
318 | |
{ |
319 | 154 | final List<JavadocTag> tags = getMethodTags(aComment); |
320 | |
|
321 | 154 | if (hasShortCircuitTag(aAST, tags)) { |
322 | 17 | return; |
323 | |
} |
324 | |
|
325 | 137 | Iterator<JavadocTag> it = tags.iterator(); |
326 | 137 | if (aAST.getType() != TokenTypes.ANNOTATION_FIELD_DEF) { |
327 | |
|
328 | 134 | boolean hasInheritDocTag = false; |
329 | 301 | while (it.hasNext() && !hasInheritDocTag) { |
330 | 167 | hasInheritDocTag |= (it.next()).isInheritDocTag(); |
331 | |
} |
332 | |
|
333 | 134 | checkParamTags(tags, aAST, !hasInheritDocTag); |
334 | 134 | checkThrowsTags(tags, getThrows(aAST), !hasInheritDocTag); |
335 | 134 | if (isFunction(aAST)) { |
336 | 25 | checkReturnTag(tags, aAST.getLineNo(), !hasInheritDocTag); |
337 | |
} |
338 | |
} |
339 | |
|
340 | |
|
341 | 137 | it = tags.iterator(); |
342 | 164 | while (it.hasNext()) { |
343 | 27 | final JavadocTag jt = it.next(); |
344 | 27 | if (!jt.isSeeOrInheritDocTag()) { |
345 | 6 | log(jt.getLineNo(), "javadoc.unusedTagGeneral"); |
346 | |
} |
347 | 27 | } |
348 | 137 | } |
349 | |
|
350 | |
|
351 | |
|
352 | |
|
353 | |
|
354 | |
|
355 | |
|
356 | |
|
357 | |
|
358 | |
private boolean hasShortCircuitTag(final DetailAST aAST, |
359 | |
final List<JavadocTag> aTags) |
360 | |
{ |
361 | |
|
362 | 154 | if ((aTags.size() != 1) |
363 | |
|| !(aTags.get(0)).isInheritDocTag()) |
364 | |
{ |
365 | 137 | return false; |
366 | |
} |
367 | |
|
368 | |
|
369 | 17 | if (!JavadocTagInfo.INHERIT_DOC.isValidOn(aAST)) { |
370 | 6 | log(aAST, "javadoc.invalidInheritDoc"); |
371 | |
} |
372 | |
|
373 | 17 | return true; |
374 | |
} |
375 | |
|
376 | |
|
377 | |
|
378 | |
|
379 | |
|
380 | |
|
381 | |
|
382 | |
|
383 | |
|
384 | |
private Scope calculateScope(final DetailAST aAST) |
385 | |
{ |
386 | 390 | final DetailAST mods = aAST.findFirstToken(TokenTypes.MODIFIERS); |
387 | 390 | final Scope declaredScope = ScopeUtils.getScopeFromMods(mods); |
388 | 390 | return ScopeUtils.inInterfaceOrAnnotationBlock(aAST) ? Scope.PUBLIC |
389 | |
: declaredScope; |
390 | |
} |
391 | |
|
392 | |
|
393 | |
|
394 | |
|
395 | |
|
396 | |
|
397 | |
|
398 | |
|
399 | |
private List<JavadocTag> getMethodTags(TextBlock aComment) |
400 | |
{ |
401 | 154 | final String[] lines = aComment.getText(); |
402 | 154 | final List<JavadocTag> tags = Lists.newArrayList(); |
403 | 154 | int currentLine = aComment.getStartLineNo() - 1; |
404 | |
|
405 | 710 | for (int i = 0; i < lines.length; i++) { |
406 | 556 | currentLine++; |
407 | 556 | final Matcher javadocArgMatcher = |
408 | |
MATCH_JAVADOC_ARG.matcher(lines[i]); |
409 | 556 | final Matcher javadocNoargMatcher = |
410 | |
MATCH_JAVADOC_NOARG.matcher(lines[i]); |
411 | 556 | final Matcher noargCurlyMatcher = |
412 | |
MATCH_JAVADOC_NOARG_CURLY.matcher(lines[i]); |
413 | 556 | final Matcher argMultilineStart = |
414 | |
MATCH_JAVADOC_ARG_MULTILINE_START.matcher(lines[i]); |
415 | 556 | final Matcher noargMultilineStart = |
416 | |
MATCH_JAVADOC_NOARG_MULTILINE_START.matcher(lines[i]); |
417 | |
|
418 | 556 | if (javadocArgMatcher.find()) { |
419 | 118 | int col = javadocArgMatcher.start(1) - 1; |
420 | 118 | if (i == 0) { |
421 | 12 | col += aComment.getStartColNo(); |
422 | |
} |
423 | 118 | tags.add(new JavadocTag(currentLine, col, javadocArgMatcher |
424 | |
.group(1), javadocArgMatcher.group(2))); |
425 | 118 | } |
426 | 438 | else if (javadocNoargMatcher.find()) { |
427 | 37 | int col = javadocNoargMatcher.start(1) - 1; |
428 | 37 | if (i == 0) { |
429 | 3 | col += aComment.getStartColNo(); |
430 | |
} |
431 | 37 | tags.add(new JavadocTag(currentLine, col, javadocNoargMatcher |
432 | |
.group(1))); |
433 | 37 | } |
434 | 401 | else if (noargCurlyMatcher.find()) { |
435 | 26 | int col = noargCurlyMatcher.start(1) - 1; |
436 | 26 | if (i == 0) { |
437 | 17 | col += aComment.getStartColNo(); |
438 | |
} |
439 | 26 | tags.add(new JavadocTag(currentLine, col, noargCurlyMatcher |
440 | |
.group(1))); |
441 | 26 | } |
442 | 375 | else if (argMultilineStart.find()) { |
443 | 18 | final String p1 = argMultilineStart.group(1); |
444 | 18 | final String p2 = argMultilineStart.group(2); |
445 | 18 | int col = argMultilineStart.start(1) - 1; |
446 | 18 | if (i == 0) { |
447 | 3 | col += aComment.getStartColNo(); |
448 | |
} |
449 | |
|
450 | |
|
451 | |
|
452 | |
|
453 | |
|
454 | 18 | int remIndex = i + 1; |
455 | 51 | while (remIndex < lines.length) { |
456 | 33 | final Matcher multilineCont = MATCH_JAVADOC_MULTILINE_CONT |
457 | |
.matcher(lines[remIndex]); |
458 | 33 | if (multilineCont.find()) { |
459 | 18 | remIndex = lines.length; |
460 | 18 | final String lFin = multilineCont.group(1); |
461 | 18 | if (!lFin.equals(NEXT_TAG) |
462 | |
&& !lFin.equals(END_JAVADOC)) |
463 | |
{ |
464 | 9 | tags.add(new JavadocTag(currentLine, col, p1, p2)); |
465 | |
} |
466 | |
} |
467 | 33 | remIndex++; |
468 | 33 | } |
469 | 18 | } |
470 | 357 | else if (noargMultilineStart.find()) { |
471 | 3 | final String p1 = noargMultilineStart.group(1); |
472 | 3 | int col = noargMultilineStart.start(1) - 1; |
473 | 3 | if (i == 0) { |
474 | 0 | col += aComment.getStartColNo(); |
475 | |
} |
476 | |
|
477 | |
|
478 | |
|
479 | |
|
480 | |
|
481 | 3 | int remIndex = i + 1; |
482 | 6 | while (remIndex < lines.length) { |
483 | 3 | final Matcher multilineCont = MATCH_JAVADOC_MULTILINE_CONT |
484 | |
.matcher(lines[remIndex]); |
485 | 3 | if (multilineCont.find()) { |
486 | 3 | remIndex = lines.length; |
487 | 3 | final String lFin = multilineCont.group(1); |
488 | 3 | if (!lFin.equals(NEXT_TAG) |
489 | |
&& !lFin.equals(END_JAVADOC)) |
490 | |
{ |
491 | 3 | tags.add(new JavadocTag(currentLine, col, p1)); |
492 | |
} |
493 | |
} |
494 | 3 | remIndex++; |
495 | 3 | } |
496 | |
} |
497 | |
} |
498 | 154 | return tags; |
499 | |
} |
500 | |
|
501 | |
|
502 | |
|
503 | |
|
504 | |
|
505 | |
|
506 | |
|
507 | |
private List<DetailAST> getParameters(DetailAST aAST) |
508 | |
{ |
509 | 134 | final DetailAST params = aAST.findFirstToken(TokenTypes.PARAMETERS); |
510 | 134 | final List<DetailAST> retVal = Lists.newArrayList(); |
511 | |
|
512 | 134 | DetailAST child = params.getFirstChild(); |
513 | 212 | while (child != null) { |
514 | 78 | if (child.getType() == TokenTypes.PARAMETER_DEF) { |
515 | 60 | final DetailAST ident = child.findFirstToken(TokenTypes.IDENT); |
516 | 60 | retVal.add(ident); |
517 | |
} |
518 | 78 | child = child.getNextSibling(); |
519 | |
} |
520 | 134 | return retVal; |
521 | |
} |
522 | |
|
523 | |
|
524 | |
|
525 | |
|
526 | |
|
527 | |
|
528 | |
|
529 | |
private List<ExceptionInfo> getThrows(DetailAST aAST) |
530 | |
{ |
531 | 134 | final List<ExceptionInfo> retVal = Lists.newArrayList(); |
532 | 134 | final DetailAST throwsAST = aAST |
533 | |
.findFirstToken(TokenTypes.LITERAL_THROWS); |
534 | 134 | if (throwsAST != null) { |
535 | 63 | DetailAST child = throwsAST.getFirstChild(); |
536 | 158 | while (child != null) { |
537 | 95 | if ((child.getType() == TokenTypes.IDENT) |
538 | |
|| (child.getType() == TokenTypes.DOT)) |
539 | |
{ |
540 | 79 | final FullIdent fi = FullIdent.createFullIdent(child); |
541 | 79 | final ExceptionInfo ei = new ExceptionInfo(new Token(fi), |
542 | |
getCurrentClassName()); |
543 | 79 | retVal.add(ei); |
544 | |
} |
545 | 95 | child = child.getNextSibling(); |
546 | |
} |
547 | |
} |
548 | 134 | return retVal; |
549 | |
} |
550 | |
|
551 | |
|
552 | |
|
553 | |
|
554 | |
|
555 | |
|
556 | |
|
557 | |
|
558 | |
|
559 | |
private void checkParamTags(final List<JavadocTag> aTags, |
560 | |
final DetailAST aParent, boolean aReportExpectedTags) |
561 | |
{ |
562 | 134 | final List<DetailAST> params = getParameters(aParent); |
563 | 134 | final List<DetailAST> typeParams = CheckUtils |
564 | |
.getTypeParameters(aParent); |
565 | |
|
566 | |
|
567 | 134 | final ListIterator<JavadocTag> tagIt = aTags.listIterator(); |
568 | 307 | while (tagIt.hasNext()) { |
569 | 173 | final JavadocTag tag = tagIt.next(); |
570 | |
|
571 | 173 | if (!tag.isParamTag()) { |
572 | 128 | continue; |
573 | |
} |
574 | |
|
575 | 45 | tagIt.remove(); |
576 | |
|
577 | 45 | boolean found = false; |
578 | |
|
579 | |
|
580 | 45 | final Iterator<DetailAST> paramIt = params.iterator(); |
581 | 57 | while (paramIt.hasNext()) { |
582 | 41 | final DetailAST param = paramIt.next(); |
583 | 41 | if (param.getText().equals(tag.getArg1())) { |
584 | 29 | found = true; |
585 | 29 | paramIt.remove(); |
586 | 29 | break; |
587 | |
} |
588 | 12 | } |
589 | |
|
590 | 45 | if (tag.getArg1().startsWith("<") && tag.getArg1().endsWith(">")) { |
591 | |
|
592 | 4 | final Iterator<DetailAST> typeParamsIt = typeParams.iterator(); |
593 | 5 | while (typeParamsIt.hasNext()) { |
594 | 4 | final DetailAST typeParam = typeParamsIt.next(); |
595 | 4 | if (typeParam.findFirstToken(TokenTypes.IDENT).getText() |
596 | |
.equals( |
597 | |
tag.getArg1().substring(1, |
598 | |
tag.getArg1().length() - 1))) |
599 | |
{ |
600 | 3 | found = true; |
601 | 3 | typeParamsIt.remove(); |
602 | 3 | break; |
603 | |
} |
604 | 1 | } |
605 | |
|
606 | |
} |
607 | |
|
608 | |
|
609 | 45 | if (!found) { |
610 | 13 | log(tag.getLineNo(), tag.getColumnNo(), "javadoc.unusedTag", |
611 | |
"@param", tag.getArg1()); |
612 | |
} |
613 | 45 | } |
614 | |
|
615 | |
|
616 | |
|
617 | 134 | if (!mAllowMissingParamTags && aReportExpectedTags) { |
618 | 125 | for (DetailAST param : params) { |
619 | 22 | log(param, "javadoc.expectedTag", |
620 | |
JavadocTagInfo.PARAM.getText(), param.getText()); |
621 | |
} |
622 | |
|
623 | 125 | for (DetailAST typeParam : typeParams) { |
624 | 4 | log(typeParam, "javadoc.expectedTag", |
625 | |
JavadocTagInfo.PARAM.getText(), |
626 | |
"<" + typeParam.findFirstToken(TokenTypes.IDENT).getText() |
627 | |
+ ">"); |
628 | |
} |
629 | |
} |
630 | 134 | } |
631 | |
|
632 | |
|
633 | |
|
634 | |
|
635 | |
|
636 | |
|
637 | |
|
638 | |
private boolean isFunction(DetailAST aAST) |
639 | |
{ |
640 | 134 | boolean retVal = false; |
641 | 134 | if (aAST.getType() == TokenTypes.METHOD_DEF) { |
642 | 121 | final DetailAST typeAST = aAST.findFirstToken(TokenTypes.TYPE); |
643 | 121 | if ((typeAST != null) |
644 | |
&& (typeAST.findFirstToken(TokenTypes.LITERAL_VOID) == null)) |
645 | |
{ |
646 | 25 | retVal = true; |
647 | |
} |
648 | |
} |
649 | 134 | return retVal; |
650 | |
} |
651 | |
|
652 | |
|
653 | |
|
654 | |
|
655 | |
|
656 | |
|
657 | |
|
658 | |
|
659 | |
|
660 | |
|
661 | |
private void checkReturnTag(List<JavadocTag> aTags, int aLineNo, |
662 | |
boolean aReportExpectedTags) |
663 | |
{ |
664 | |
|
665 | |
|
666 | 25 | boolean found = false; |
667 | 25 | final ListIterator<JavadocTag> it = aTags.listIterator(); |
668 | 56 | while (it.hasNext()) { |
669 | 31 | final JavadocTag jt = it.next(); |
670 | 31 | if (jt.isReturnTag()) { |
671 | 22 | if (found) { |
672 | 3 | log(jt.getLineNo(), jt.getColumnNo(), |
673 | |
"javadoc.duplicateTag", |
674 | |
JavadocTagInfo.RETURN.getText()); |
675 | |
} |
676 | 22 | found = true; |
677 | 22 | it.remove(); |
678 | |
} |
679 | 31 | } |
680 | |
|
681 | |
|
682 | |
|
683 | 25 | if (!found && !mAllowMissingReturnTag && aReportExpectedTags) { |
684 | 6 | log(aLineNo, "javadoc.return.expected"); |
685 | |
} |
686 | 25 | } |
687 | |
|
688 | |
|
689 | |
|
690 | |
|
691 | |
|
692 | |
|
693 | |
|
694 | |
|
695 | |
|
696 | |
private void checkThrowsTags(List<JavadocTag> aTags, |
697 | |
List<ExceptionInfo> aThrows, boolean aReportExpectedTags) |
698 | |
{ |
699 | |
|
700 | |
|
701 | 134 | final Set<String> foundThrows = Sets.newHashSet(); |
702 | 134 | final ListIterator<JavadocTag> tagIt = aTags.listIterator(); |
703 | 262 | while (tagIt.hasNext()) { |
704 | 128 | final JavadocTag tag = tagIt.next(); |
705 | |
|
706 | 128 | if (!tag.isThrowsTag()) { |
707 | 46 | continue; |
708 | |
} |
709 | |
|
710 | 82 | tagIt.remove(); |
711 | |
|
712 | |
|
713 | 82 | final String documentedEx = tag.getArg1(); |
714 | 82 | final Token token = new Token(tag.getArg1(), tag.getLineNo(), tag |
715 | |
.getColumnNo()); |
716 | 82 | final ClassInfo documentedCI = createClassInfo(token, |
717 | |
getCurrentClassName()); |
718 | 82 | boolean found = foundThrows.contains(documentedEx); |
719 | |
|
720 | |
|
721 | 82 | ListIterator<ExceptionInfo> throwIt = aThrows.listIterator(); |
722 | 180 | while (!found && throwIt.hasNext()) { |
723 | 98 | final ExceptionInfo ei = throwIt.next(); |
724 | |
|
725 | 98 | if (ei.getName().getText().equals( |
726 | |
documentedCI.getName().getText())) |
727 | |
{ |
728 | 44 | found = true; |
729 | 44 | ei.setFound(); |
730 | 44 | foundThrows.add(documentedEx); |
731 | |
} |
732 | 98 | } |
733 | |
|
734 | |
|
735 | 82 | throwIt = aThrows.listIterator(); |
736 | 125 | while (!found && throwIt.hasNext()) { |
737 | 43 | final ExceptionInfo ei = throwIt.next(); |
738 | |
|
739 | 43 | if (documentedCI.getClazz() == ei.getClazz()) { |
740 | 14 | found = true; |
741 | 14 | ei.setFound(); |
742 | 14 | foundThrows.add(documentedEx); |
743 | |
} |
744 | 29 | else if (mAllowThrowsTagsForSubclasses) { |
745 | 13 | found = isSubclass(documentedCI.getClazz(), ei.getClazz()); |
746 | |
} |
747 | 43 | } |
748 | |
|
749 | |
|
750 | 82 | if (!found) { |
751 | 16 | boolean reqd = true; |
752 | 16 | if (mAllowUndeclaredRTE) { |
753 | 6 | reqd = !isUnchecked(documentedCI.getClazz()); |
754 | |
} |
755 | |
|
756 | 16 | if (reqd) { |
757 | 13 | log(tag.getLineNo(), tag.getColumnNo(), |
758 | |
"javadoc.unusedTag", |
759 | |
JavadocTagInfo.THROWS.getText(), tag.getArg1()); |
760 | |
|
761 | |
} |
762 | |
} |
763 | 82 | } |
764 | |
|
765 | |
|
766 | |
|
767 | 134 | if (!mAllowMissingThrowsTags && aReportExpectedTags) { |
768 | 125 | for (ExceptionInfo ei : aThrows) { |
769 | 79 | if (!ei.isFound()) { |
770 | 24 | final Token fi = ei.getName(); |
771 | 24 | log(fi.getLineNo(), fi.getColumnNo(), |
772 | |
"javadoc.expectedTag", |
773 | |
JavadocTagInfo.THROWS.getText(), fi.getText()); |
774 | 79 | } |
775 | |
} |
776 | |
} |
777 | 134 | } |
778 | |
|
779 | |
|
780 | |
|
781 | |
|
782 | |
|
783 | |
|
784 | |
private boolean isSetterMethod(final DetailAST aAST) |
785 | |
{ |
786 | |
|
787 | |
|
788 | |
|
789 | 9 | if ((aAST.getType() != TokenTypes.METHOD_DEF) |
790 | |
|| (aAST.getChildCount() != MAX_CHILDREN)) |
791 | |
{ |
792 | 1 | return false; |
793 | |
} |
794 | |
|
795 | |
|
796 | |
|
797 | |
|
798 | 8 | final DetailAST type = aAST.findFirstToken(TokenTypes.TYPE); |
799 | 8 | final String name = type.getNextSibling().getText(); |
800 | 8 | if (!name.matches("^set[A-Z].*")) { |
801 | 5 | return false; |
802 | |
} |
803 | |
|
804 | |
|
805 | 3 | if (type.getChildCount(TokenTypes.LITERAL_VOID) == 0) { |
806 | 0 | return false; |
807 | |
} |
808 | |
|
809 | |
|
810 | 3 | final DetailAST params = aAST.findFirstToken(TokenTypes.PARAMETERS); |
811 | 3 | if ((params == null) |
812 | |
|| (params.getChildCount(TokenTypes.PARAMETER_DEF) != 1)) |
813 | |
{ |
814 | 1 | return false; |
815 | |
} |
816 | |
|
817 | |
|
818 | |
|
819 | |
|
820 | |
|
821 | 2 | final DetailAST slist = aAST.findFirstToken(TokenTypes.SLIST); |
822 | 2 | if ((slist == null) || (slist.getChildCount() != BODY_SIZE)) { |
823 | 1 | return false; |
824 | |
} |
825 | |
|
826 | 1 | final AST expr = slist.getFirstChild(); |
827 | 1 | if ((expr.getType() != TokenTypes.EXPR) |
828 | |
|| (expr.getFirstChild().getType() != TokenTypes.ASSIGN)) |
829 | |
{ |
830 | 0 | return false; |
831 | |
} |
832 | |
|
833 | 1 | return true; |
834 | |
} |
835 | |
|
836 | |
|
837 | |
|
838 | |
|
839 | |
|
840 | |
|
841 | |
private boolean isGetterMethod(final DetailAST aAST) |
842 | |
{ |
843 | |
|
844 | |
|
845 | |
|
846 | 8 | if ((aAST.getType() != TokenTypes.METHOD_DEF) |
847 | |
|| (aAST.getChildCount() != MAX_CHILDREN)) |
848 | |
{ |
849 | 1 | return false; |
850 | |
} |
851 | |
|
852 | |
|
853 | |
|
854 | 7 | final DetailAST type = aAST.findFirstToken(TokenTypes.TYPE); |
855 | 7 | final String name = type.getNextSibling().getText(); |
856 | 7 | if (!name.matches("^(is|get)[A-Z].*")) { |
857 | 2 | return false; |
858 | |
} |
859 | |
|
860 | |
|
861 | 5 | if (type.getChildCount(TokenTypes.LITERAL_VOID) > 0) { |
862 | 1 | return false; |
863 | |
} |
864 | |
|
865 | |
|
866 | 4 | final DetailAST params = aAST.findFirstToken(TokenTypes.PARAMETERS); |
867 | 4 | if ((params == null) |
868 | |
|| (params.getChildCount(TokenTypes.PARAMETER_DEF) > 0)) |
869 | |
{ |
870 | 1 | return false; |
871 | |
} |
872 | |
|
873 | |
|
874 | |
|
875 | |
|
876 | 3 | final DetailAST slist = aAST.findFirstToken(TokenTypes.SLIST); |
877 | 3 | if ((slist == null) || (slist.getChildCount() != 2)) { |
878 | 1 | return false; |
879 | |
} |
880 | |
|
881 | 2 | final AST expr = slist.getFirstChild(); |
882 | 2 | if ((expr.getType() != TokenTypes.LITERAL_RETURN) |
883 | |
|| (expr.getFirstChild().getType() != TokenTypes.EXPR)) |
884 | |
{ |
885 | 0 | return false; |
886 | |
} |
887 | |
|
888 | 2 | return true; |
889 | |
} |
890 | |
|
891 | |
|
892 | |
|
893 | |
|
894 | |
|
895 | |
|
896 | |
private boolean isOverrideMethod(DetailAST aAST) |
897 | |
{ |
898 | |
|
899 | |
|
900 | 122 | if ((TokenTypes.METHOD_DEF != aAST.getType()) |
901 | |
|| (TokenTypes.MODIFIERS != aAST.getFirstChild().getType())) |
902 | |
{ |
903 | 10 | return false; |
904 | |
} |
905 | |
|
906 | |
|
907 | |
|
908 | 112 | DetailAST node = aAST.getFirstChild().getFirstChild(); |
909 | 114 | while ((null != node) && (TokenTypes.ANNOTATION == node.getType())) { |
910 | 4 | if ((node.getFirstChild().getType() == TokenTypes.AT) |
911 | |
&& (node.getFirstChild().getNextSibling().getType() |
912 | |
== TokenTypes.IDENT) |
913 | |
&& ("Override".equals( |
914 | |
node.getFirstChild().getNextSibling().getText()))) |
915 | |
{ |
916 | 2 | return true; |
917 | |
} |
918 | 2 | node = node.getNextSibling(); |
919 | |
} |
920 | 110 | return false; |
921 | |
} |
922 | |
|
923 | |
|
924 | 25 | private class ExceptionInfo |
925 | |
{ |
926 | |
|
927 | |
private boolean mFound; |
928 | |
|
929 | |
private final ClassInfo mClassInfo; |
930 | |
|
931 | |
|
932 | |
|
933 | |
|
934 | |
|
935 | |
|
936 | |
|
937 | |
ExceptionInfo(Token aIdent, String aCurrentClass) |
938 | 79 | { |
939 | 79 | mClassInfo = createClassInfo(aIdent, aCurrentClass); |
940 | 79 | } |
941 | |
|
942 | |
|
943 | |
final void setFound() |
944 | |
{ |
945 | 58 | mFound = true; |
946 | 58 | } |
947 | |
|
948 | |
|
949 | |
final boolean isFound() |
950 | |
{ |
951 | 79 | return mFound; |
952 | |
} |
953 | |
|
954 | |
|
955 | |
final Token getName() |
956 | |
{ |
957 | 122 | return mClassInfo.getName(); |
958 | |
} |
959 | |
|
960 | |
|
961 | |
final Class<?> getClazz() |
962 | |
{ |
963 | 56 | return mClassInfo.getClazz(); |
964 | |
} |
965 | |
} |
966 | |
} |