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.coding;
21
22 import com.puppycrawl.tools.checkstyle.StatelessCheck;
23 import com.puppycrawl.tools.checkstyle.api.AbstractCheck;
24 import com.puppycrawl.tools.checkstyle.api.DetailAST;
25 import com.puppycrawl.tools.checkstyle.api.TokenTypes;
26 import com.puppycrawl.tools.checkstyle.utils.CheckUtil;
27 import com.puppycrawl.tools.checkstyle.utils.TokenUtil;
28
29 /**
30 * <div>
31 * Checks that each variable declaration is in its own statement
32 * and on its own line.
33 * </div>
34 *
35 * <p>
36 * Rationale: <a
37 * href="https://checkstyle.org/styleguides/sun-code-conventions-19990420/CodeConventions.doc5.html#a2992">
38 * the Java code conventions chapter 6.1</a> recommends that
39 * declarations should be one per line/statement.
40 * </p>
41 *
42 * @since 3.4
43 */
44 @StatelessCheck
45 public class MultipleVariableDeclarationsCheck extends AbstractCheck {
46
47 /**
48 * A key is pointing to the warning message text in "messages.properties"
49 * file.
50 */
51 public static final String MSG_MULTIPLE = "multiple.variable.declarations";
52
53 /**
54 * A key is pointing to the warning message text in "messages.properties"
55 * file.
56 */
57 public static final String MSG_MULTIPLE_COMMA = "multiple.variable.declarations.comma";
58
59 @Override
60 public int[] getAcceptableTokens() {
61 return getRequiredTokens();
62 }
63
64 @Override
65 public int[] getDefaultTokens() {
66 return getRequiredTokens();
67 }
68
69 @Override
70 public int[] getRequiredTokens() {
71 return new int[] {TokenTypes.VARIABLE_DEF};
72 }
73
74 @Override
75 public void visitToken(DetailAST ast) {
76 DetailAST nextNode = ast.getNextSibling();
77
78 if (nextNode != null) {
79 final boolean isCommaSeparated = nextNode.getType() == TokenTypes.COMMA;
80
81 if (isCommaSeparated
82 || nextNode.getType() == TokenTypes.SEMI) {
83 nextNode = nextNode.getNextSibling();
84 }
85
86 if (nextNode != null
87 && nextNode.getType() == TokenTypes.VARIABLE_DEF) {
88 final DetailAST firstNode = CheckUtil.getFirstNode(ast);
89 if (isCommaSeparated) {
90 // Check if the multiple variable declarations are in a
91 // for loop initializer. If they are, then no warning
92 // should be displayed. Declaring multiple variables in
93 // a for loop initializer is a good way to minimize
94 // variable scope. Refer Feature Request Id - 2895985
95 // for more details
96 if (ast.getParent().getType() != TokenTypes.FOR_INIT) {
97 log(firstNode, MSG_MULTIPLE_COMMA);
98 }
99 }
100 else {
101 final DetailAST lastNode = getLastNode(ast);
102 final DetailAST firstNextNode = CheckUtil.getFirstNode(nextNode);
103
104 if (TokenUtil.areOnSameLine(firstNextNode, lastNode)) {
105 log(firstNode, MSG_MULTIPLE);
106 }
107 }
108 }
109 }
110 }
111
112 /**
113 * Finds sub-node for given node maximum (line, column) pair.
114 *
115 * @param node the root of tree for search.
116 * @return sub-node with maximum (line, column) pair.
117 */
118 private static DetailAST getLastNode(final DetailAST node) {
119 DetailAST currentNode = node;
120 final DetailAST child = node.getLastChild();
121 if (child != null) {
122 currentNode = getLastNode(child);
123 }
124
125 return currentNode;
126 }
127
128 }