1 ///////////////////////////////////////////////////////////////////////////////////////////////
2 // checkstyle: Checks Java source code and other text files for adherence to a set of rules.
3 // Copyright (C) 2001-2026 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 /**
60 * Creates a new {@code MultipleVariableDeclarationsCheck} instance.
61 */
62 public MultipleVariableDeclarationsCheck() {
63 // no code by default
64 }
65
66 @Override
67 public int[] getAcceptableTokens() {
68 return getRequiredTokens();
69 }
70
71 @Override
72 public int[] getDefaultTokens() {
73 return getRequiredTokens();
74 }
75
76 @Override
77 public int[] getRequiredTokens() {
78 return new int[] {TokenTypes.VARIABLE_DEF};
79 }
80
81 @Override
82 public void visitToken(DetailAST ast) {
83 DetailAST nextNode = ast.getNextSibling();
84
85 if (nextNode != null) {
86 final boolean isCommaSeparated = nextNode.getType() == TokenTypes.COMMA;
87
88 if (isCommaSeparated
89 || nextNode.getType() == TokenTypes.SEMI) {
90 nextNode = nextNode.getNextSibling();
91 }
92
93 if (nextNode != null
94 && nextNode.getType() == TokenTypes.VARIABLE_DEF) {
95 final DetailAST firstNode = CheckUtil.getFirstNode(ast);
96 if (isCommaSeparated) {
97 // Check if the multiple variable declarations are in a
98 // for loop initializer. If they are, then no warning
99 // should be displayed. Declaring multiple variables in
100 // a for loop initializer is a good way to minimize
101 // variable scope. Refer Feature Request Id - 2895985
102 // for more details
103 if (ast.getParent().getType() != TokenTypes.FOR_INIT) {
104 log(firstNode, MSG_MULTIPLE_COMMA);
105 }
106 }
107 else {
108 final DetailAST lastNode = getLastNode(ast);
109 final DetailAST firstNextNode = CheckUtil.getFirstNode(nextNode);
110
111 if (TokenUtil.areOnSameLine(firstNextNode, lastNode)) {
112 log(firstNode, MSG_MULTIPLE);
113 }
114 }
115 }
116 }
117 }
118
119 /**
120 * Finds sub-node for given node maximum (line, column) pair.
121 *
122 * @param node the root of tree for search.
123 * @return sub-node with maximum (line, column) pair.
124 */
125 private static DetailAST getLastNode(final DetailAST node) {
126 DetailAST currentNode = node;
127 final DetailAST child = node.getLastChild();
128 if (child != null) {
129 currentNode = getLastNode(child);
130 }
131
132 return currentNode;
133 }
134
135 }