001//////////////////////////////////////////////////////////////////////////////// 002// checkstyle: Checks Java source code for adherence to a set of rules. 003// Copyright (C) 2001-2019 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; 027 028/** 029 * <p> 030 * Checks that each variable declaration is in its own statement 031 * and on its own line. 032 * </p> 033 * <p> 034 * Rationale: <a 035 * href="https://www.oracle.com/technetwork/java/javase/documentation/codeconventions-141270.html"> 036 * the SUN Code conventions chapter 6.1</a> recommends that 037 * declarations should be one per line. 038 * </p> 039 * <p> 040 * An example of how to configure the check is: 041 * </p> 042 * <pre> 043 * <module name="MultipleVariableDeclarations"/> 044 * </pre> 045 */ 046@StatelessCheck 047public class MultipleVariableDeclarationsCheck extends AbstractCheck { 048 049 /** 050 * A key is pointing to the warning message text in "messages.properties" 051 * file. 052 */ 053 public static final String MSG_MULTIPLE = "multiple.variable.declarations"; 054 055 /** 056 * A key is pointing to the warning message text in "messages.properties" 057 * file. 058 */ 059 public static final String MSG_MULTIPLE_COMMA = "multiple.variable.declarations.comma"; 060 061 @Override 062 public int[] getAcceptableTokens() { 063 return getRequiredTokens(); 064 } 065 066 @Override 067 public int[] getDefaultTokens() { 068 return getRequiredTokens(); 069 } 070 071 @Override 072 public int[] getRequiredTokens() { 073 return new int[] {TokenTypes.VARIABLE_DEF}; 074 } 075 076 @Override 077 public void visitToken(DetailAST ast) { 078 DetailAST nextNode = ast.getNextSibling(); 079 080 if (nextNode != null) { 081 final boolean isCommaSeparated = nextNode.getType() == TokenTypes.COMMA; 082 083 if (isCommaSeparated 084 || nextNode.getType() == TokenTypes.SEMI) { 085 nextNode = nextNode.getNextSibling(); 086 } 087 088 if (nextNode != null 089 && nextNode.getType() == TokenTypes.VARIABLE_DEF) { 090 final DetailAST firstNode = CheckUtil.getFirstNode(ast); 091 if (isCommaSeparated) { 092 // Check if the multiple variable declarations are in a 093 // for loop initializer. If they are, then no warning 094 // should be displayed. Declaring multiple variables in 095 // a for loop initializer is a good way to minimize 096 // variable scope. Refer Feature Request Id - 2895985 097 // for more details 098 if (ast.getParent().getType() != TokenTypes.FOR_INIT) { 099 log(firstNode, MSG_MULTIPLE_COMMA); 100 } 101 } 102 else { 103 final DetailAST lastNode = getLastNode(ast); 104 final DetailAST firstNextNode = CheckUtil.getFirstNode(nextNode); 105 106 if (firstNextNode.getLineNo() == lastNode.getLineNo()) { 107 log(firstNode, MSG_MULTIPLE); 108 } 109 } 110 } 111 } 112 } 113 114 /** 115 * Finds sub-node for given node maximum (line, column) pair. 116 * @param node the root of tree for search. 117 * @return sub-node with maximum (line, column) pair. 118 */ 119 private static DetailAST getLastNode(final DetailAST node) { 120 DetailAST currentNode = node; 121 DetailAST child = node.getFirstChild(); 122 while (child != null) { 123 final DetailAST newNode = getLastNode(child); 124 if (newNode.getLineNo() > currentNode.getLineNo() 125 || newNode.getLineNo() == currentNode.getLineNo() 126 && newNode.getColumnNo() > currentNode.getColumnNo()) { 127 currentNode = newNode; 128 } 129 child = child.getNextSibling(); 130 } 131 132 return currentNode; 133 } 134 135}