001/////////////////////////////////////////////////////////////////////////////////////////////// 002// checkstyle: Checks Java source code and other text files for adherence to a set of rules. 003// Copyright (C) 2001-2023 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.coding; 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.CheckUtil; 027import com.puppycrawl.tools.checkstyle.utils.TokenUtil; 028 029/** 030 * <p> 031 * Checks that each variable declaration is in its own statement 032 * and on its own line. 033 * </p> 034 * <p> 035 * Rationale: <a 036 * href="https://checkstyle.org/styleguides/sun-code-conventions-19990420/CodeConventions.doc5.html#a2992"> 037 * the Java code conventions chapter 6.1</a> recommends that 038 * declarations should be one per line/statement. 039 * </p> 040 * <p> 041 * To configure the check: 042 * </p> 043 * <pre> 044 * <module name="MultipleVariableDeclarations"/> 045 * </pre> 046 * <p> 047 * Example: 048 * </p> 049 * <pre> 050 * public class Test { 051 * public void myTest() { 052 * int mid; 053 * int high; 054 * // ... 055 * 056 * int lower, higher; // violation 057 * // ... 058 * 059 * int value, 060 * index; // violation 061 * // ... 062 * 063 * int place = mid, number = high; // violation 064 * } 065 * } 066 * </pre> 067 * <p> 068 * Parent is {@code com.puppycrawl.tools.checkstyle.TreeWalker} 069 * </p> 070 * <p> 071 * Violation Message Keys: 072 * </p> 073 * <ul> 074 * <li> 075 * {@code multiple.variable.declarations} 076 * </li> 077 * <li> 078 * {@code multiple.variable.declarations.comma} 079 * </li> 080 * </ul> 081 * 082 * @since 3.4 083 */ 084@StatelessCheck 085public class MultipleVariableDeclarationsCheck extends AbstractCheck { 086 087 /** 088 * A key is pointing to the warning message text in "messages.properties" 089 * file. 090 */ 091 public static final String MSG_MULTIPLE = "multiple.variable.declarations"; 092 093 /** 094 * A key is pointing to the warning message text in "messages.properties" 095 * file. 096 */ 097 public static final String MSG_MULTIPLE_COMMA = "multiple.variable.declarations.comma"; 098 099 @Override 100 public int[] getAcceptableTokens() { 101 return getRequiredTokens(); 102 } 103 104 @Override 105 public int[] getDefaultTokens() { 106 return getRequiredTokens(); 107 } 108 109 @Override 110 public int[] getRequiredTokens() { 111 return new int[] {TokenTypes.VARIABLE_DEF}; 112 } 113 114 @Override 115 public void visitToken(DetailAST ast) { 116 DetailAST nextNode = ast.getNextSibling(); 117 118 if (nextNode != null) { 119 final boolean isCommaSeparated = nextNode.getType() == TokenTypes.COMMA; 120 121 if (isCommaSeparated 122 || nextNode.getType() == TokenTypes.SEMI) { 123 nextNode = nextNode.getNextSibling(); 124 } 125 126 if (nextNode != null 127 && nextNode.getType() == TokenTypes.VARIABLE_DEF) { 128 final DetailAST firstNode = CheckUtil.getFirstNode(ast); 129 if (isCommaSeparated) { 130 // Check if the multiple variable declarations are in a 131 // for loop initializer. If they are, then no warning 132 // should be displayed. Declaring multiple variables in 133 // a for loop initializer is a good way to minimize 134 // variable scope. Refer Feature Request Id - 2895985 135 // for more details 136 if (ast.getParent().getType() != TokenTypes.FOR_INIT) { 137 log(firstNode, MSG_MULTIPLE_COMMA); 138 } 139 } 140 else { 141 final DetailAST lastNode = getLastNode(ast); 142 final DetailAST firstNextNode = CheckUtil.getFirstNode(nextNode); 143 144 if (TokenUtil.areOnSameLine(firstNextNode, lastNode)) { 145 log(firstNode, MSG_MULTIPLE); 146 } 147 } 148 } 149 } 150 } 151 152 /** 153 * Finds sub-node for given node maximum (line, column) pair. 154 * 155 * @param node the root of tree for search. 156 * @return sub-node with maximum (line, column) pair. 157 */ 158 private static DetailAST getLastNode(final DetailAST node) { 159 DetailAST currentNode = node; 160 final DetailAST child = node.getLastChild(); 161 if (child != null) { 162 currentNode = getLastNode(child); 163 } 164 165 return currentNode; 166 } 167 168}