001/////////////////////////////////////////////////////////////////////////////////////////////// 002// checkstyle: Checks Java source code and other text files for adherence to a set of rules. 003// Copyright (C) 2001-2023 the original author or authors. 004// 005// This library is free software; you can redistribute it and/or 006// modify it under the terms of the GNU Lesser General Public 007// License as published by the Free Software Foundation; either 008// version 2.1 of the License, or (at your option) any later version. 009// 010// This library is distributed in the hope that it will be useful, 011// but WITHOUT ANY WARRANTY; without even the implied warranty of 012// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 013// Lesser General Public License for more details. 014// 015// You should have received a copy of the GNU Lesser General Public 016// License along with this library; if not, write to the Free Software 017// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 018/////////////////////////////////////////////////////////////////////////////////////////////// 019 020package com.puppycrawl.tools.checkstyle.checks.javadoc; 021 022import java.util.ArrayList; 023import java.util.Collection; 024import java.util.List; 025import java.util.Set; 026import java.util.regex.Matcher; 027import java.util.regex.Pattern; 028 029import com.puppycrawl.tools.checkstyle.StatelessCheck; 030import com.puppycrawl.tools.checkstyle.api.AbstractCheck; 031import com.puppycrawl.tools.checkstyle.api.DetailAST; 032import com.puppycrawl.tools.checkstyle.api.FileContents; 033import com.puppycrawl.tools.checkstyle.api.Scope; 034import com.puppycrawl.tools.checkstyle.api.TextBlock; 035import com.puppycrawl.tools.checkstyle.api.TokenTypes; 036import com.puppycrawl.tools.checkstyle.utils.AnnotationUtil; 037import com.puppycrawl.tools.checkstyle.utils.CheckUtil; 038import com.puppycrawl.tools.checkstyle.utils.CommonUtil; 039import com.puppycrawl.tools.checkstyle.utils.JavadocUtil; 040import com.puppycrawl.tools.checkstyle.utils.ScopeUtil; 041import com.puppycrawl.tools.checkstyle.utils.TokenUtil; 042 043/** 044 * <p> 045 * Checks the Javadoc comments for type definitions. By default, does 046 * not check for author or version tags. The scope to verify is specified using the {@code Scope} 047 * class and defaults to {@code Scope.PRIVATE}. To verify another scope, set property 048 * scope to one of the {@code Scope} constants. To define the format for an author 049 * tag or a version tag, set property authorFormat or versionFormat respectively to a 050 * <a href="https://docs.oracle.com/javase/7/docs/api/java/util/regex/Pattern.html"> 051 * pattern</a>. 052 * </p> 053 * <p> 054 * Does not perform checks for author and version tags for inner classes, 055 * as they should be redundant because of outer class. 056 * </p> 057 * <p> 058 * Does not perform checks for type definitions that do not have any Javadoc comments. 059 * </p> 060 * <p> 061 * Error messages about type parameters and record components for which no param tags are present 062 * can be suppressed by defining property {@code allowMissingParamTags}. 063 * </p> 064 * <ul> 065 * <li> 066 * Property {@code scope} - Specify the visibility scope where Javadoc comments are checked. 067 * Type is {@code com.puppycrawl.tools.checkstyle.api.Scope}. 068 * Default value is {@code private}. 069 * </li> 070 * <li> 071 * Property {@code excludeScope} - Specify the visibility scope where Javadoc 072 * comments are not checked. 073 * Type is {@code com.puppycrawl.tools.checkstyle.api.Scope}. 074 * Default value is {@code null}. 075 * </li> 076 * <li> 077 * Property {@code authorFormat} - Specify the pattern for {@code @author} tag. 078 * Type is {@code java.util.regex.Pattern}. 079 * Default value is {@code null}. 080 * </li> 081 * <li> 082 * Property {@code versionFormat} - Specify the pattern for {@code @version} tag. 083 * Type is {@code java.util.regex.Pattern}. 084 * Default value is {@code null}. 085 * </li> 086 * <li> 087 * Property {@code allowMissingParamTags} - Control whether to ignore violations 088 * when a class has type parameters but does not have matching param tags in the Javadoc. 089 * Type is {@code boolean}. 090 * Default value is {@code false}. 091 * </li> 092 * <li> 093 * Property {@code allowUnknownTags} - Control whether to ignore violations when 094 * a Javadoc tag is not recognised. 095 * Type is {@code boolean}. 096 * Default value is {@code false}. 097 * </li> 098 * <li> 099 * Property {@code allowedAnnotations} - Specify annotations that allow 100 * skipping validation at all. Only short names are allowed, e.g. {@code Generated}. 101 * Type is {@code java.lang.String[]}. 102 * Default value is {@code Generated}. 103 * </li> 104 * <li> 105 * Property {@code tokens} - tokens to check 106 * Type is {@code java.lang.String[]}. 107 * Validation type is {@code tokenSet}. 108 * Default value is: 109 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#INTERFACE_DEF"> 110 * INTERFACE_DEF</a>, 111 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#CLASS_DEF"> 112 * CLASS_DEF</a>, 113 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#ENUM_DEF"> 114 * ENUM_DEF</a>, 115 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#ANNOTATION_DEF"> 116 * ANNOTATION_DEF</a>, 117 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#RECORD_DEF"> 118 * RECORD_DEF</a>. 119 * </li> 120 * </ul> 121 * <p> 122 * To configure the default check: 123 * </p> 124 * <pre> 125 * <module name="JavadocType"/> 126 * </pre> 127 * <p> 128 * Example: 129 * </p> 130 * <pre> 131 * /** 132 * * @author a 133 * * @version $Revision1$ 134 * */ 135 * public class ClassA { // OK 136 * /** */ 137 * private class ClassB {} // OK 138 * } 139 * 140 * /** 141 * * @author 142 * * @version abc 143 * * @unknownTag value // violation 144 * */ 145 * public class ClassC {} // OK 146 * 147 * /** */ 148 * public class ClassD<T> {} // violation, as param tag for <T> is missing 149 * 150 * /** */ 151 * private class ClassE<T> {} // violation, as param tag for <T> is missing 152 * 153 * /** */ 154 * @Generated 155 * public class ClassF<T> {} // OK 156 * </pre> 157 * <p> 158 * To configure the check for {@code public} scope: 159 * </p> 160 * <pre> 161 * <module name="JavadocType"> 162 * <property name="scope" value="public"/> 163 * </module> 164 * </pre> 165 * <p> 166 * Example: 167 * </p> 168 * <pre> 169 * /** 170 * * @author a 171 * * @version $Revision1$ 172 * */ 173 * public class ClassA { // OK 174 * /** */ 175 * private class ClassB {} // OK 176 * } 177 * 178 * /** 179 * * @author 180 * * @version abc 181 * * @unknownTag value // violation 182 * */ 183 * public class ClassC {} // OK 184 * 185 * /** */ 186 * public class ClassD<T> {} // violation, as param tag for <T> is missing 187 * 188 * /** */ 189 * private class ClassE<T> {} // OK 190 * 191 * /** */ 192 * @Generated 193 * public class ClassF<T> {} // OK 194 * </pre> 195 * <p> 196 * To configure the check for an {@code @author} tag: 197 * </p> 198 * <pre> 199 * <module name="JavadocType"> 200 * <property name="authorFormat" value="\S"/> 201 * </module> 202 * </pre> 203 * <p> 204 * Example: 205 * </p> 206 * <pre> 207 * /** 208 * * @author a 209 * * @version $Revision1$ 210 * */ 211 * public class ClassA { // OK 212 * /** */ 213 * private class ClassB {} // OK, as author tag check is ignored for inner class 214 * } 215 * 216 * /** 217 * * @author 218 * * @version abc 219 * * @unknownTag value // violation 220 * */ 221 * public class ClassC {} // violation, as author format with only whitespace or new line is invalid 222 * 223 * /** */ 224 * public class ClassD {} // violation, as author tag is missing 225 * 226 * /** */ 227 * private class ClassE {} // violation, as author tag is missing 228 * 229 * /** */ 230 * @Generated 231 * public class ClassF<T> {} // OK 232 * </pre> 233 * <p> 234 * To configure the check for a CVS revision version tag: 235 * </p> 236 * <pre> 237 * <module name="JavadocType"> 238 * <property name="versionFormat" value="\$Revision.*\$"/> 239 * </module> 240 * </pre> 241 * <p> 242 * Example: 243 * </p> 244 * <pre> 245 * /** 246 * * @author a 247 * * @version $Revision1$ 248 * */ 249 * public class ClassA { // OK 250 * /** */ 251 * private class ClassB {} // OK, as version tag check is ignored for inner class 252 * } 253 * 254 * /** 255 * * @author 256 * * @version abc 257 * * @unknownTag value // violation 258 * */ 259 * public class ClassC {} // violation, as version format is invalid 260 * 261 * /** */ 262 * public class ClassD {} // violation, as version tag is missing 263 * 264 * /** */ 265 * private class ClassE {} // violation, as version tag is missing 266 * 267 * /** */ 268 * @Generated 269 * public class ClassF<T> {} // OK 270 * </pre> 271 * <p> 272 * To configure the check for {@code private} classes only: 273 * </p> 274 * <pre> 275 * <module name="JavadocType"> 276 * <property name="scope" value="private"/> 277 * <property name="excludeScope" value="package"/> 278 * </module> 279 * </pre> 280 * <p> 281 * Example: 282 * </p> 283 * <pre> 284 * /** 285 * * @author a 286 * * @version $Revision1$ 287 * */ 288 * public class ClassA { // OK 289 * /** */ 290 * private class ClassB {} // OK 291 * } 292 * 293 * /** 294 * * @author 295 * * @version abc 296 * * @unknownTag value // OK 297 * */ 298 * public class ClassC {} // OK 299 * 300 * /** */ 301 * public class ClassD<T> {} // OK 302 * 303 * /** */ 304 * private class ClassE<T> {} // violation, as param tag for <T> is missing 305 * 306 * /** */ 307 * @Generated 308 * public class ClassF<T> {} // OK 309 * </pre> 310 * <p> 311 * To configure the check that allows missing {@code @param} tags: 312 * </p> 313 * <pre> 314 * <module name="JavadocType"> 315 * <property name="allowMissingParamTags" value="true"/> 316 * </module> 317 * </pre> 318 * <p> 319 * Example: 320 * </p> 321 * <pre> 322 * /** 323 * * @author a 324 * * @version $Revision1$ 325 * */ 326 * public class ClassA { // OK 327 * /** */ 328 * private class ClassB {} // OK 329 * } 330 * 331 * /** 332 * * @author 333 * * @version abc 334 * * @unknownTag value // violation 335 * */ 336 * public class ClassC {} // OK 337 * 338 * /** */ 339 * public class ClassD<T> {} // OK, as missing param tag is allowed 340 * 341 * /** */ 342 * private class ClassE<T> {} // OK, as missing param tag is allowed 343 * 344 * /** */ 345 * @Generated 346 * public class ClassF<T> {} // OK 347 * </pre> 348 * <p> 349 * To configure the check that allows unknown tags: 350 * </p> 351 * <pre> 352 * <module name="JavadocType"> 353 * <property name="allowUnknownTags" value="true"/> 354 * </module> 355 * </pre> 356 * <p> 357 * Example: 358 * </p> 359 * <pre> 360 * /** 361 * * @author a 362 * * @version $Revision1$ 363 * */ 364 * public class ClassA { // OK 365 * /** */ 366 * private class ClassB {} // OK 367 * } 368 * 369 * /** 370 * * @author 371 * * @version abc 372 * * @unknownTag value // OK, as unknown tag is allowed 373 * */ 374 * public class ClassC {} // OK 375 * 376 * /** */ 377 * public class ClassD {} // OK 378 * 379 * /** */ 380 * private class ClassE {} // OK 381 * 382 * /** */ 383 * @Generated 384 * public class ClassF<T> {} // OK 385 * </pre> 386 * <p> 387 * To configure a check that allows skipping validation at all for classes annotated 388 * with {@code @SpringBootApplication} and {@code @Configuration}: 389 * </p> 390 * <pre> 391 * <module name="JavadocType"> 392 * <property name="allowedAnnotations" value="SpringBootApplication,Configuration"/> 393 * </module> 394 * </pre> 395 * <p> 396 * Example: 397 * </p> 398 * <pre> 399 * /** */ 400 * @SpringBootApplication // no violations about missing param tag on class 401 * public class Application<T> {} 402 * 403 * /** */ 404 * @Configuration // no violations about missing param tag on class 405 * class DatabaseConfiguration<T> {} 406 * </pre> 407 * <p> 408 * Parent is {@code com.puppycrawl.tools.checkstyle.TreeWalker} 409 * </p> 410 * <p> 411 * Violation Message Keys: 412 * </p> 413 * <ul> 414 * <li> 415 * {@code javadoc.unknownTag} 416 * </li> 417 * <li> 418 * {@code javadoc.unusedTag} 419 * </li> 420 * <li> 421 * {@code javadoc.unusedTagGeneral} 422 * </li> 423 * <li> 424 * {@code type.missingTag} 425 * </li> 426 * <li> 427 * {@code type.tagFormat} 428 * </li> 429 * </ul> 430 * 431 * @since 3.0 432 * 433 */ 434@StatelessCheck 435public class JavadocTypeCheck 436 extends AbstractCheck { 437 438 /** 439 * A key is pointing to the warning message text in "messages.properties" 440 * file. 441 */ 442 public static final String MSG_UNKNOWN_TAG = "javadoc.unknownTag"; 443 444 /** 445 * A key is pointing to the warning message text in "messages.properties" 446 * file. 447 */ 448 public static final String MSG_TAG_FORMAT = "type.tagFormat"; 449 450 /** 451 * A key is pointing to the warning message text in "messages.properties" 452 * file. 453 */ 454 public static final String MSG_MISSING_TAG = "type.missingTag"; 455 456 /** 457 * A key is pointing to the warning message text in "messages.properties" 458 * file. 459 */ 460 public static final String MSG_UNUSED_TAG = "javadoc.unusedTag"; 461 462 /** 463 * A key is pointing to the warning message text in "messages.properties" 464 * file. 465 */ 466 public static final String MSG_UNUSED_TAG_GENERAL = "javadoc.unusedTagGeneral"; 467 468 /** Open angle bracket literal. */ 469 private static final String OPEN_ANGLE_BRACKET = "<"; 470 471 /** Close angle bracket literal. */ 472 private static final String CLOSE_ANGLE_BRACKET = ">"; 473 474 /** Space literal. */ 475 private static final String SPACE = " "; 476 477 /** Pattern to match type name within angle brackets in javadoc param tag. */ 478 private static final Pattern TYPE_NAME_IN_JAVADOC_TAG = 479 Pattern.compile("\\s*<([^>]+)>.*"); 480 481 /** Pattern to split type name field in javadoc param tag. */ 482 private static final Pattern TYPE_NAME_IN_JAVADOC_TAG_SPLITTER = 483 Pattern.compile("\\s+"); 484 485 /** Specify the visibility scope where Javadoc comments are checked. */ 486 private Scope scope = Scope.PRIVATE; 487 /** Specify the visibility scope where Javadoc comments are not checked. */ 488 private Scope excludeScope; 489 /** Specify the pattern for {@code @author} tag. */ 490 private Pattern authorFormat; 491 /** Specify the pattern for {@code @version} tag. */ 492 private Pattern versionFormat; 493 /** 494 * Control whether to ignore violations when a class has type parameters but 495 * does not have matching param tags in the Javadoc. 496 */ 497 private boolean allowMissingParamTags; 498 /** Control whether to ignore violations when a Javadoc tag is not recognised. */ 499 private boolean allowUnknownTags; 500 501 /** 502 * Specify annotations that allow skipping validation at all. 503 * Only short names are allowed, e.g. {@code Generated}. 504 */ 505 private Set<String> allowedAnnotations = Set.of("Generated"); 506 507 /** 508 * Setter to specify the visibility scope where Javadoc comments are checked. 509 * 510 * @param scope a scope. 511 */ 512 public void setScope(Scope scope) { 513 this.scope = scope; 514 } 515 516 /** 517 * Setter to specify the visibility scope where Javadoc comments are not checked. 518 * 519 * @param excludeScope a scope. 520 */ 521 public void setExcludeScope(Scope excludeScope) { 522 this.excludeScope = excludeScope; 523 } 524 525 /** 526 * Setter to specify the pattern for {@code @author} tag. 527 * 528 * @param pattern a pattern. 529 */ 530 public void setAuthorFormat(Pattern pattern) { 531 authorFormat = pattern; 532 } 533 534 /** 535 * Setter to specify the pattern for {@code @version} tag. 536 * 537 * @param pattern a pattern. 538 */ 539 public void setVersionFormat(Pattern pattern) { 540 versionFormat = pattern; 541 } 542 543 /** 544 * Setter to control whether to ignore violations when a class has type parameters but 545 * does not have matching param tags in the Javadoc. 546 * 547 * @param flag a {@code Boolean} value 548 */ 549 public void setAllowMissingParamTags(boolean flag) { 550 allowMissingParamTags = flag; 551 } 552 553 /** 554 * Setter to control whether to ignore violations when a Javadoc tag is not recognised. 555 * 556 * @param flag a {@code Boolean} value 557 */ 558 public void setAllowUnknownTags(boolean flag) { 559 allowUnknownTags = flag; 560 } 561 562 /** 563 * Setter to specify annotations that allow skipping validation at all. 564 * Only short names are allowed, e.g. {@code Generated}. 565 * 566 * @param userAnnotations user's value. 567 */ 568 public void setAllowedAnnotations(String... userAnnotations) { 569 allowedAnnotations = Set.of(userAnnotations); 570 } 571 572 @Override 573 public int[] getDefaultTokens() { 574 return getAcceptableTokens(); 575 } 576 577 @Override 578 public int[] getAcceptableTokens() { 579 return new int[] { 580 TokenTypes.INTERFACE_DEF, 581 TokenTypes.CLASS_DEF, 582 TokenTypes.ENUM_DEF, 583 TokenTypes.ANNOTATION_DEF, 584 TokenTypes.RECORD_DEF, 585 }; 586 } 587 588 @Override 589 public int[] getRequiredTokens() { 590 return CommonUtil.EMPTY_INT_ARRAY; 591 } 592 593 // suppress deprecation until https://github.com/checkstyle/checkstyle/issues/11166 594 @SuppressWarnings("deprecation") 595 @Override 596 public void visitToken(DetailAST ast) { 597 if (shouldCheck(ast)) { 598 final FileContents contents = getFileContents(); 599 final int lineNo = ast.getLineNo(); 600 final TextBlock textBlock = contents.getJavadocBefore(lineNo); 601 if (textBlock != null) { 602 final List<JavadocTag> tags = getJavadocTags(textBlock); 603 if (ScopeUtil.isOuterMostType(ast)) { 604 // don't check author/version for inner classes 605 checkTag(ast, tags, JavadocTagInfo.AUTHOR.getName(), 606 authorFormat); 607 checkTag(ast, tags, JavadocTagInfo.VERSION.getName(), 608 versionFormat); 609 } 610 611 final List<String> typeParamNames = 612 CheckUtil.getTypeParameterNames(ast); 613 final List<String> recordComponentNames = 614 getRecordComponentNames(ast); 615 616 if (!allowMissingParamTags) { 617 618 typeParamNames.forEach(typeParamName -> { 619 checkTypeParamTag(ast, tags, typeParamName); 620 }); 621 622 recordComponentNames.forEach(componentName -> { 623 checkComponentParamTag(ast, tags, componentName); 624 }); 625 } 626 627 checkUnusedParamTags(tags, typeParamNames, recordComponentNames); 628 } 629 } 630 } 631 632 /** 633 * Whether we should check this node. 634 * 635 * @param ast a given node. 636 * @return whether we should check a given node. 637 */ 638 private boolean shouldCheck(DetailAST ast) { 639 final Scope customScope = ScopeUtil.getScope(ast); 640 final Scope surroundingScope = ScopeUtil.getSurroundingScope(ast); 641 642 return customScope.isIn(scope) 643 && (surroundingScope == null || surroundingScope.isIn(scope)) 644 && (excludeScope == null 645 || !customScope.isIn(excludeScope) 646 || surroundingScope != null 647 && !surroundingScope.isIn(excludeScope)) 648 && !AnnotationUtil.containsAnnotation(ast, allowedAnnotations); 649 } 650 651 /** 652 * Gets all standalone tags from a given javadoc. 653 * 654 * @param textBlock the Javadoc comment to process. 655 * @return all standalone tags from the given javadoc. 656 */ 657 private List<JavadocTag> getJavadocTags(TextBlock textBlock) { 658 final JavadocTags tags = JavadocUtil.getJavadocTags(textBlock, 659 JavadocUtil.JavadocTagType.BLOCK); 660 if (!allowUnknownTags) { 661 for (final InvalidJavadocTag tag : tags.getInvalidTags()) { 662 log(tag.getLine(), tag.getCol(), MSG_UNKNOWN_TAG, 663 tag.getName()); 664 } 665 } 666 return tags.getValidTags(); 667 } 668 669 /** 670 * Verifies that a type definition has a required tag. 671 * 672 * @param ast the AST node for the type definition. 673 * @param tags tags from the Javadoc comment for the type definition. 674 * @param tagName the required tag name. 675 * @param formatPattern regexp for the tag value. 676 */ 677 private void checkTag(DetailAST ast, Iterable<JavadocTag> tags, String tagName, 678 Pattern formatPattern) { 679 if (formatPattern != null) { 680 boolean hasTag = false; 681 final String tagPrefix = "@"; 682 683 for (final JavadocTag tag :tags) { 684 if (tag.getTagName().equals(tagName)) { 685 hasTag = true; 686 if (!formatPattern.matcher(tag.getFirstArg()).find()) { 687 log(ast, MSG_TAG_FORMAT, tagPrefix + tagName, formatPattern.pattern()); 688 } 689 } 690 } 691 if (!hasTag) { 692 log(ast, MSG_MISSING_TAG, tagPrefix + tagName); 693 } 694 } 695 } 696 697 /** 698 * Verifies that a record definition has the specified param tag for 699 * the specified record component name. 700 * 701 * @param ast the AST node for the record definition. 702 * @param tags tags from the Javadoc comment for the record definition. 703 * @param recordComponentName the name of the type parameter 704 */ 705 private void checkComponentParamTag(DetailAST ast, 706 Collection<JavadocTag> tags, 707 String recordComponentName) { 708 709 final boolean found = tags 710 .stream() 711 .filter(JavadocTag::isParamTag) 712 .anyMatch(tag -> tag.getFirstArg().indexOf(recordComponentName) == 0); 713 714 if (!found) { 715 log(ast, MSG_MISSING_TAG, JavadocTagInfo.PARAM.getText() 716 + SPACE + recordComponentName); 717 } 718 } 719 720 /** 721 * Verifies that a type definition has the specified param tag for 722 * the specified type parameter name. 723 * 724 * @param ast the AST node for the type definition. 725 * @param tags tags from the Javadoc comment for the type definition. 726 * @param typeParamName the name of the type parameter 727 */ 728 private void checkTypeParamTag(DetailAST ast, 729 Collection<JavadocTag> tags, String typeParamName) { 730 final String typeParamNameWithBrackets = 731 OPEN_ANGLE_BRACKET + typeParamName + CLOSE_ANGLE_BRACKET; 732 733 final boolean found = tags 734 .stream() 735 .filter(JavadocTag::isParamTag) 736 .anyMatch(tag -> tag.getFirstArg().indexOf(typeParamNameWithBrackets) == 0); 737 738 if (!found) { 739 log(ast, MSG_MISSING_TAG, JavadocTagInfo.PARAM.getText() 740 + SPACE + typeParamNameWithBrackets); 741 } 742 } 743 744 /** 745 * Checks for unused param tags for type parameters and record components. 746 * 747 * @param tags tags from the Javadoc comment for the type definition 748 * @param typeParamNames names of type parameters 749 * @param recordComponentNames record component names in this definition 750 */ 751 private void checkUnusedParamTags( 752 List<JavadocTag> tags, 753 List<String> typeParamNames, 754 List<String> recordComponentNames) { 755 756 for (final JavadocTag tag: tags) { 757 if (tag.isParamTag()) { 758 final String paramName = extractParamNameFromTag(tag); 759 final boolean found = typeParamNames.contains(paramName) 760 || recordComponentNames.contains(paramName); 761 762 if (!found) { 763 final String actualParamName = 764 TYPE_NAME_IN_JAVADOC_TAG_SPLITTER.split(tag.getFirstArg())[0]; 765 log(tag.getLineNo(), tag.getColumnNo(), 766 MSG_UNUSED_TAG, 767 JavadocTagInfo.PARAM.getText(), actualParamName); 768 } 769 } 770 } 771 772 } 773 774 /** 775 * Extracts parameter name from tag. 776 * 777 * @param tag javadoc tag to extract parameter name 778 * @return extracts type parameter name from tag 779 */ 780 private static String extractParamNameFromTag(JavadocTag tag) { 781 final String typeParamName; 782 final Matcher matchInAngleBrackets = 783 TYPE_NAME_IN_JAVADOC_TAG.matcher(tag.getFirstArg()); 784 if (matchInAngleBrackets.find()) { 785 typeParamName = matchInAngleBrackets.group(1).trim(); 786 } 787 else { 788 typeParamName = TYPE_NAME_IN_JAVADOC_TAG_SPLITTER.split(tag.getFirstArg())[0]; 789 } 790 return typeParamName; 791 } 792 793 /** 794 * Collects the record components in a record definition. 795 * 796 * @param node the possible record definition ast. 797 * @return the record components in this record definition. 798 */ 799 private static List<String> getRecordComponentNames(DetailAST node) { 800 final DetailAST components = node.findFirstToken(TokenTypes.RECORD_COMPONENTS); 801 final List<String> componentList = new ArrayList<>(); 802 803 if (components != null) { 804 TokenUtil.forEachChild(components, 805 TokenTypes.RECORD_COMPONENT_DEF, component -> { 806 final DetailAST ident = component.findFirstToken(TokenTypes.IDENT); 807 componentList.add(ident.getText()); 808 }); 809 } 810 811 return componentList; 812 } 813}