1 /////////////////////////////////////////////////////////////////////////////////////////////// 2 // checkstyle: Checks Java source code and other text files for adherence to a set of rules. 3 // Copyright (C) 2001-2024 the original author or authors. 4 // 5 // This library is free software; you can redistribute it and/or 6 // modify it under the terms of the GNU Lesser General Public 7 // License as published by the Free Software Foundation; either 8 // version 2.1 of the License, or (at your option) any later version. 9 // 10 // This library is distributed in the hope that it will be useful, 11 // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 // Lesser General Public License for more details. 14 // 15 // You should have received a copy of the GNU Lesser General Public 16 // License along with this library; if not, write to the Free Software 17 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 18 /////////////////////////////////////////////////////////////////////////////////////////////// 19 20 package com.puppycrawl.tools.checkstyle.checks.javadoc; 21 22 import java.util.Arrays; 23 import java.util.Set; 24 import java.util.regex.Matcher; 25 import java.util.regex.Pattern; 26 import java.util.stream.Collectors; 27 28 import com.puppycrawl.tools.checkstyle.StatelessCheck; 29 import com.puppycrawl.tools.checkstyle.api.DetailNode; 30 import com.puppycrawl.tools.checkstyle.api.JavadocTokenTypes; 31 32 /** 33 * <div> 34 * Checks that a 35 * <a href="https://docs.oracle.com/en/java/javase/11/docs/specs/doc-comment-spec.html#block-tags"> 36 * javadoc block tag</a> appears only at the beginning of a line, ignoring 37 * leading asterisks and white space. A block tag is a token that starts with 38 * {@code @} symbol and is preceded by a whitespace. This check ignores block 39 * tags in comments and inside inline tags {@code } and {@literal }. 40 * </div> 41 * 42 * <p> 43 * Rationale: according to 44 * <a href="https://docs.oracle.com/en/java/javase/11/docs/specs/doc-comment-spec.html#block-tags"> 45 * the specification</a> all javadoc block tags should be placed at the beginning 46 * of a line. Tags that are not placed at the beginning are treated as plain text. 47 * To recognize intentional tag placement to text area it is better to escape the 48 * {@code @} symbol, and all non-escaped tags should be located at the beginning 49 * of the line. See NOTE section for details on how to escape. 50 * </p> 51 * 52 * <p> 53 * To place a tag explicitly as text, escape the {@code @} symbol with HTML entity 54 * &#64; or place it inside {@code {@code }}, for example: 55 * </p> 56 * <pre> 57 * /** 58 * * &#64;serial literal in {@code @serial} Javadoc tag. 59 * */ 60 * </pre> 61 * <ul> 62 * <li> 63 * Property {@code tags} - Specify the javadoc tags to process. 64 * Type is {@code java.lang.String[]}. 65 * Default value is {@code author, deprecated, exception, hidden, param, provides, 66 * return, see, serial, serialData, serialField, since, throws, uses, version}. 67 * </li> 68 * <li> 69 * Property {@code violateExecutionOnNonTightHtml} - Control when to print violations 70 * if the Javadoc being examined by this check violates the tight html rules defined at 71 * <a href="https://checkstyle.org/writingjavadocchecks.html#Tight-HTML_rules">Tight-HTML Rules</a>. 72 * Type is {@code boolean}. 73 * Default value is {@code false}. 74 * </li> 75 * </ul> 76 * 77 * <p> 78 * Parent is {@code com.puppycrawl.tools.checkstyle.TreeWalker} 79 * </p> 80 * 81 * <p> 82 * Violation Message Keys: 83 * </p> 84 * <ul> 85 * <li> 86 * {@code javadoc.blockTagLocation} 87 * </li> 88 * <li> 89 * {@code javadoc.missed.html.close} 90 * </li> 91 * <li> 92 * {@code javadoc.parse.rule.error} 93 * </li> 94 * <li> 95 * {@code javadoc.unclosedHtml} 96 * </li> 97 * <li> 98 * {@code javadoc.wrong.singleton.html.tag} 99 * </li> 100 * </ul> 101 * 102 * @since 8.24 103 */ 104 @StatelessCheck 105 public class JavadocBlockTagLocationCheck extends AbstractJavadocCheck { 106 107 /** 108 * A key is pointing to the warning message text in "messages.properties" file. 109 */ 110 public static final String MSG_BLOCK_TAG_LOCATION = "javadoc.blockTagLocation"; 111 112 /** 113 * This regexp is used to extract the javadoc tags. 114 */ 115 private static final Pattern JAVADOC_BLOCK_TAG_PATTERN = Pattern.compile("\\s@(\\w+)"); 116 117 /** 118 * Block tags from Java 11 119 * <a href="https://docs.oracle.com/en/java/javase/11/docs/specs/doc-comment-spec.html"> 120 * Documentation Comment Specification</a>. 121 */ 122 private static final String[] DEFAULT_TAGS = { 123 "author", 124 "deprecated", 125 "exception", 126 "hidden", 127 "param", 128 "provides", 129 "return", 130 "see", 131 "serial", 132 "serialData", 133 "serialField", 134 "since", 135 "throws", 136 "uses", 137 "version", 138 }; 139 140 /** 141 * Specify the javadoc tags to process. 142 */ 143 private Set<String> tags; 144 145 /** 146 * Creates a new {@code JavadocBlockTagLocationCheck} instance with default settings. 147 */ 148 public JavadocBlockTagLocationCheck() { 149 setTags(DEFAULT_TAGS); 150 } 151 152 /** 153 * Setter to specify the javadoc tags to process. 154 * 155 * @param values user's values. 156 * @since 8.24 157 */ 158 public final void setTags(String... values) { 159 tags = Arrays.stream(values).collect(Collectors.toUnmodifiableSet()); 160 } 161 162 /** 163 * The javadoc tokens that this check must be registered for. According to 164 * <a href="https://docs.oracle.com/en/java/javase/11/docs/specs/doc-comment-spec.html#block-tags"> 165 * the specs</a> each block tag must appear at the beginning of a line, otherwise 166 * it will be interpreted as a plain text. This check looks for a block tag 167 * in the javadoc text, thus it needs the {@code TEXT} tokens. 168 * 169 * @return the javadoc token set this must be registered for. 170 * @see JavadocTokenTypes 171 */ 172 @Override 173 public int[] getRequiredJavadocTokens() { 174 return new int[] { 175 JavadocTokenTypes.TEXT, 176 }; 177 } 178 179 @Override 180 public int[] getAcceptableJavadocTokens() { 181 return getRequiredJavadocTokens(); 182 } 183 184 @Override 185 public int[] getDefaultJavadocTokens() { 186 return getRequiredJavadocTokens(); 187 } 188 189 @Override 190 public void visitJavadocToken(DetailNode ast) { 191 if (!isCommentOrInlineTag(ast.getParent())) { 192 final Matcher tagMatcher = JAVADOC_BLOCK_TAG_PATTERN.matcher(ast.getText()); 193 while (tagMatcher.find()) { 194 final String tagName = tagMatcher.group(1); 195 if (tags.contains(tagName)) { 196 log(ast.getLineNumber(), MSG_BLOCK_TAG_LOCATION, tagName); 197 } 198 } 199 } 200 } 201 202 /** 203 * Checks if the node can contain an unescaped block tag without violation. 204 * 205 * @param node to check 206 * @return {@code true} if node is {@code @code}, {@code @literal} or HTML comment. 207 */ 208 private static boolean isCommentOrInlineTag(DetailNode node) { 209 return node.getType() == JavadocTokenTypes.JAVADOC_INLINE_TAG 210 || node.getType() == JavadocTokenTypes.HTML_COMMENT; 211 } 212 213 }