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