001//////////////////////////////////////////////////////////////////////////////// 002// checkstyle: Checks Java source code for adherence to a set of rules. 003// Copyright (C) 2001-2022 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.annotation; 021 022import com.puppycrawl.tools.checkstyle.StatelessCheck; 023import com.puppycrawl.tools.checkstyle.api.AbstractCheck; 024import com.puppycrawl.tools.checkstyle.api.DetailAST; 025import com.puppycrawl.tools.checkstyle.api.TokenTypes; 026import com.puppycrawl.tools.checkstyle.utils.CommonUtil; 027import com.puppycrawl.tools.checkstyle.utils.TokenUtil; 028 029/** 030 * <p> 031 * Checks that annotations are located on the same line with their targets. 032 * Verifying with this check is not good practice, but it is using by some style guides. 033 * </p> 034 * <ul> 035 * <li> 036 * Property {@code tokens} - tokens to check 037 * Type is {@code java.lang.String[]}. 038 * Validation type is {@code tokenSet}. 039 * Default value is: 040 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#CLASS_DEF"> 041 * CLASS_DEF</a>, 042 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#INTERFACE_DEF"> 043 * INTERFACE_DEF</a>, 044 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#ENUM_DEF"> 045 * ENUM_DEF</a>, 046 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#METHOD_DEF"> 047 * METHOD_DEF</a>, 048 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#CTOR_DEF"> 049 * CTOR_DEF</a>, 050 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#VARIABLE_DEF"> 051 * VARIABLE_DEF</a>, 052 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#RECORD_DEF"> 053 * RECORD_DEF</a>, 054 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#COMPACT_CTOR_DEF"> 055 * COMPACT_CTOR_DEF</a>. 056 * </li> 057 * </ul> 058 * <p> 059 * To configure the check: 060 * </p> 061 * <pre> 062 * <module name="AnnotationOnSameLine"/> 063 * </pre> 064 * <p> 065 * Example: 066 * </p> 067 * <pre> 068 * class Foo { 069 * 070 * @SuppressWarnings("deprecation") // violation, annotation should be on the same line 071 * public Foo() { 072 * } 073 * 074 * @SuppressWarnings("unchecked") public void fun2() { // OK 075 * } 076 * 077 * } 078 * 079 * @SuppressWarnings("unchecked") class Bar extends Foo { // OK 080 * 081 * @Deprecated public Bar() { // OK 082 * } 083 * 084 * @Override // violation, annotation should be on the same line 085 * public void fun1() { 086 * } 087 * 088 * @Before @Override public void fun2() { // OK 089 * } 090 * 091 * @SuppressWarnings("deprecation") // violation, annotation should be on the same line 092 * @Before public void fun3() { 093 * } 094 * 095 * } 096 * </pre> 097 * <p> 098 * To configure the check to check for annotations applied on 099 * interfaces, variables and constructors: 100 * </p> 101 * <pre> 102 * <module name="AnnotationOnSameLine"> 103 * <property name="tokens" 104 * value="INTERFACE_DEF, VARIABLE_DEF, CTOR_DEF"/> 105 * </module> 106 * </pre> 107 * <p> 108 * Example: 109 * </p> 110 * <pre> 111 * @Deprecated interface Foo { // OK 112 * 113 * void doSomething(); 114 * 115 * } 116 * 117 * class Bar implements Foo { 118 * 119 * @SuppressWarnings("deprecation") // violation, annotation should be on the same line 120 * public Bar() { 121 * } 122 * 123 * @Override // OK 124 * public void doSomething() { 125 * } 126 * 127 * @Nullable // violation, annotation should be on the same line 128 * String s; 129 * 130 * } 131 * </pre> 132 * <p> 133 * Parent is {@code com.puppycrawl.tools.checkstyle.TreeWalker} 134 * </p> 135 * <p> 136 * Violation Message Keys: 137 * </p> 138 * <ul> 139 * <li> 140 * {@code annotation.same.line} 141 * </li> 142 * </ul> 143 * 144 * @since 8.2 145 */ 146@StatelessCheck 147public class AnnotationOnSameLineCheck extends AbstractCheck { 148 149 /** A key is pointing to the warning message text in "messages.properties" file. */ 150 public static final String MSG_KEY_ANNOTATION_ON_SAME_LINE = "annotation.same.line"; 151 152 @Override 153 public int[] getDefaultTokens() { 154 return new int[] { 155 TokenTypes.CLASS_DEF, 156 TokenTypes.INTERFACE_DEF, 157 TokenTypes.ENUM_DEF, 158 TokenTypes.METHOD_DEF, 159 TokenTypes.CTOR_DEF, 160 TokenTypes.VARIABLE_DEF, 161 TokenTypes.RECORD_DEF, 162 TokenTypes.COMPACT_CTOR_DEF, 163 }; 164 } 165 166 @Override 167 public int[] getAcceptableTokens() { 168 return new int[] { 169 TokenTypes.CLASS_DEF, 170 TokenTypes.INTERFACE_DEF, 171 TokenTypes.ENUM_DEF, 172 TokenTypes.METHOD_DEF, 173 TokenTypes.CTOR_DEF, 174 TokenTypes.VARIABLE_DEF, 175 TokenTypes.PARAMETER_DEF, 176 TokenTypes.ANNOTATION_DEF, 177 TokenTypes.TYPECAST, 178 TokenTypes.LITERAL_THROWS, 179 TokenTypes.IMPLEMENTS_CLAUSE, 180 TokenTypes.TYPE_ARGUMENT, 181 TokenTypes.LITERAL_NEW, 182 TokenTypes.DOT, 183 TokenTypes.ANNOTATION_FIELD_DEF, 184 TokenTypes.RECORD_DEF, 185 TokenTypes.COMPACT_CTOR_DEF, 186 }; 187 } 188 189 @Override 190 public int[] getRequiredTokens() { 191 return CommonUtil.EMPTY_INT_ARRAY; 192 } 193 194 @Override 195 public void visitToken(DetailAST ast) { 196 DetailAST nodeWithAnnotations = ast; 197 if (ast.getType() == TokenTypes.TYPECAST) { 198 nodeWithAnnotations = ast.findFirstToken(TokenTypes.TYPE); 199 } 200 DetailAST modifiersNode = nodeWithAnnotations.findFirstToken(TokenTypes.MODIFIERS); 201 if (modifiersNode == null) { 202 modifiersNode = nodeWithAnnotations.findFirstToken(TokenTypes.ANNOTATIONS); 203 } 204 if (modifiersNode != null) { 205 for (DetailAST annotationNode = modifiersNode.getFirstChild(); 206 annotationNode != null; 207 annotationNode = annotationNode.getNextSibling()) { 208 if (annotationNode.getType() == TokenTypes.ANNOTATION 209 && !TokenUtil.areOnSameLine(annotationNode, getNextNode(annotationNode))) { 210 log(annotationNode, MSG_KEY_ANNOTATION_ON_SAME_LINE, 211 getAnnotationName(annotationNode)); 212 } 213 } 214 } 215 } 216 217 /** 218 * Finds next node of ast tree. 219 * 220 * @param node current node 221 * @return node that is next to given 222 */ 223 private static DetailAST getNextNode(DetailAST node) { 224 DetailAST nextNode = node.getNextSibling(); 225 if (nextNode == null) { 226 nextNode = node.getParent().getNextSibling(); 227 } 228 return nextNode; 229 } 230 231 /** 232 * Returns the name of the given annotation. 233 * 234 * @param annotation annotation node. 235 * @return annotation name. 236 */ 237 private static String getAnnotationName(DetailAST annotation) { 238 DetailAST identNode = annotation.findFirstToken(TokenTypes.IDENT); 239 if (identNode == null) { 240 identNode = annotation.findFirstToken(TokenTypes.DOT).getLastChild(); 241 } 242 return identNode.getText(); 243 } 244 245}