View Javadoc
1   ///////////////////////////////////////////////////////////////////////////////////////////////
2   // checkstyle: Checks Java source code and other text files for adherence to a set of rules.
3   // Copyright (C) 2001-2025 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/17/docs/specs/javadoc/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 {&#64;code } and {&#64;literal }.
40   * </div>
41   *
42   * <p>
43   * Rationale: according to
44   * <a href="https://docs.oracle.com/en/java/javase/17/docs/specs/javadoc/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   * Notes:
54   * To place a tag explicitly as text, escape the {@code @} symbol with HTML entity
55   * &amp;#64; or place it inside {@code {@code }}, for example:
56   * </p>
57   * <div class="wrapper"><pre class="prettyprint"><code class="language-java">
58   * &#47;**
59   *  * &amp;#64;serial literal in {&#64;code &#64;serial} Javadoc tag.
60   *  *&#47;
61   * </code></pre></div>
62   * <ul>
63   * <li>
64   * Property {@code tags} - Specify the javadoc tags to process.
65   * Type is {@code java.lang.String[]}.
66   * Default value is {@code author, deprecated, exception, hidden, param, provides,
67   * return, see, serial, serialData, serialField, since, throws, uses, version}.
68   * </li>
69   * <li>
70   * Property {@code violateExecutionOnNonTightHtml} - Control when to print violations
71   * if the Javadoc being examined by this check violates the tight html rules defined at
72   * <a href="https://checkstyle.org/writingjavadocchecks.html#Tight-HTML_rules">Tight-HTML Rules</a>.
73   * Type is {@code boolean}.
74   * Default value is {@code false}.
75   * </li>
76   * </ul>
77   *
78   * <p>
79   * Parent is {@code com.puppycrawl.tools.checkstyle.TreeWalker}
80   * </p>
81   *
82   * <p>
83   * Violation Message Keys:
84   * </p>
85   * <ul>
86   * <li>
87   * {@code javadoc.blockTagLocation}
88   * </li>
89   * <li>
90   * {@code javadoc.missed.html.close}
91   * </li>
92   * <li>
93   * {@code javadoc.parse.rule.error}
94   * </li>
95   * <li>
96   * {@code javadoc.unclosedHtml}
97   * </li>
98   * <li>
99   * {@code javadoc.wrong.singleton.html.tag}
100  * </li>
101  * </ul>
102  *
103  * @since 8.24
104  */
105 @StatelessCheck
106 public class JavadocBlockTagLocationCheck extends AbstractJavadocCheck {
107 
108     /**
109      * A key is pointing to the warning message text in "messages.properties" file.
110      */
111     public static final String MSG_BLOCK_TAG_LOCATION = "javadoc.blockTagLocation";
112 
113     /**
114      * This regexp is used to extract the javadoc tags.
115      */
116     private static final Pattern JAVADOC_BLOCK_TAG_PATTERN = Pattern.compile("\\s@(\\w+)");
117 
118     /**
119      * Block tags from Java 11
120      * <a href="https://docs.oracle.com/en/java/javase/17/docs/specs/javadoc/doc-comment-spec.html">
121      * Documentation Comment Specification</a>.
122      */
123     private static final String[] DEFAULT_TAGS = {
124         "author",
125         "deprecated",
126         "exception",
127         "hidden",
128         "param",
129         "provides",
130         "return",
131         "see",
132         "serial",
133         "serialData",
134         "serialField",
135         "since",
136         "throws",
137         "uses",
138         "version",
139     };
140 
141     /**
142      * Specify the javadoc tags to process.
143      */
144     private Set<String> tags;
145 
146     /**
147      * Creates a new {@code JavadocBlockTagLocationCheck} instance with default settings.
148      */
149     public JavadocBlockTagLocationCheck() {
150         setTags(DEFAULT_TAGS);
151     }
152 
153     /**
154      * Setter to specify the javadoc tags to process.
155      *
156      * @param values user's values.
157      * @since 8.24
158      */
159     public final void setTags(String... values) {
160         tags = Arrays.stream(values).collect(Collectors.toUnmodifiableSet());
161     }
162 
163     /**
164      * The javadoc tokens that this check must be registered for. According to
165      * <a href="https://docs.oracle.com/en/java/javase/17/docs/specs/javadoc/doc-comment-spec.html#block-tags">
166      * the specs</a> each block tag must appear at the beginning of a line, otherwise
167      * it will be interpreted as a plain text. This check looks for a block tag
168      * in the javadoc text, thus it needs the {@code TEXT} tokens.
169      *
170      * @return the javadoc token set this must be registered for.
171      * @see JavadocTokenTypes
172      */
173     @Override
174     public int[] getRequiredJavadocTokens() {
175         return new int[] {
176             JavadocTokenTypes.TEXT,
177         };
178     }
179 
180     @Override
181     public int[] getAcceptableJavadocTokens() {
182         return getRequiredJavadocTokens();
183     }
184 
185     @Override
186     public int[] getDefaultJavadocTokens() {
187         return getRequiredJavadocTokens();
188     }
189 
190     @Override
191     public void visitJavadocToken(DetailNode ast) {
192         if (!isCommentOrInlineTag(ast.getParent())) {
193             final Matcher tagMatcher = JAVADOC_BLOCK_TAG_PATTERN.matcher(ast.getText());
194             while (tagMatcher.find()) {
195                 final String tagName = tagMatcher.group(1);
196                 if (tags.contains(tagName)) {
197                     log(ast.getLineNumber(), MSG_BLOCK_TAG_LOCATION, tagName);
198                 }
199             }
200         }
201     }
202 
203     /**
204      * Checks if the node can contain an unescaped block tag without violation.
205      *
206      * @param node to check
207      * @return {@code true} if node is {@code @code}, {@code @literal} or HTML comment.
208      */
209     private static boolean isCommentOrInlineTag(DetailNode node) {
210         return node.getType() == JavadocTokenTypes.JAVADOC_INLINE_TAG
211                 || node.getType() == JavadocTokenTypes.HTML_COMMENT;
212     }
213 
214 }