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