001//////////////////////////////////////////////////////////////////////////////// 002// checkstyle: Checks Java source code for adherence to a set of rules. 003// Copyright (C) 2001-2016 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.api.AbstractCheck; 023import com.puppycrawl.tools.checkstyle.api.DetailAST; 024import com.puppycrawl.tools.checkstyle.api.TokenTypes; 025import com.puppycrawl.tools.checkstyle.utils.CheckUtils; 026import com.puppycrawl.tools.checkstyle.utils.ScopeUtils; 027 028/** 029 * <p> 030 * Checks if any class or object member explicitly initialized 031 * to default for its type value ({@code null} for object 032 * references, zero for numeric types and {@code char} 033 * and {@code false} for {@code boolean}. 034 * </p> 035 * <p> 036 * Rationale: each instance variable gets 037 * initialized twice, to the same value. Java 038 * initializes each instance variable to its default 039 * value (0 or null) before performing any 040 * initialization specified in the code. So in this case, 041 * x gets initialized to 0 twice, and bar gets initialized 042 * to null twice. So there is a minor inefficiency. This style of 043 * coding is a hold-over from C/C++ style coding, 044 * and it shows that the developer isn't really confident that 045 * Java really initializes instance variables to default 046 * values. 047 * </p> 048 * 049 * @author o_sukhodolsky 050 */ 051public class ExplicitInitializationCheck extends AbstractCheck { 052 053 /** 054 * A key is pointing to the warning message text in "messages.properties" 055 * file. 056 */ 057 public static final String MSG_KEY = "explicit.init"; 058 059 @Override 060 public final int[] getDefaultTokens() { 061 return new int[] {TokenTypes.VARIABLE_DEF}; 062 } 063 064 @Override 065 public final int[] getRequiredTokens() { 066 return getDefaultTokens(); 067 } 068 069 @Override 070 public final int[] getAcceptableTokens() { 071 return new int[] {TokenTypes.VARIABLE_DEF}; 072 } 073 074 @Override 075 public void visitToken(DetailAST ast) { 076 if (!isSkipCase(ast)) { 077 final DetailAST ident = ast.findFirstToken(TokenTypes.IDENT); 078 final DetailAST assign = ast.findFirstToken(TokenTypes.ASSIGN); 079 final DetailAST exprStart = 080 assign.getFirstChild().getFirstChild(); 081 final DetailAST type = ast.findFirstToken(TokenTypes.TYPE); 082 if (isObjectType(type) 083 && exprStart.getType() == TokenTypes.LITERAL_NULL) { 084 log(ident, MSG_KEY, ident.getText(), "null"); 085 } 086 087 final int primitiveType = type.getFirstChild().getType(); 088 if (primitiveType == TokenTypes.LITERAL_BOOLEAN 089 && exprStart.getType() == TokenTypes.LITERAL_FALSE) { 090 log(ident, MSG_KEY, ident.getText(), "false"); 091 } 092 if (isNumericType(primitiveType) && isZero(exprStart)) { 093 log(ident, MSG_KEY, ident.getText(), "0"); 094 } 095 if (primitiveType == TokenTypes.LITERAL_CHAR 096 && isZeroChar(exprStart)) { 097 log(ident, MSG_KEY, ident.getText(), "\\0"); 098 } 099 } 100 } 101 102 /** 103 * Examine char literal for initializing to default value. 104 * @param exprStart expression 105 * @return true is literal is initialized by zero symbol 106 */ 107 private static boolean isZeroChar(DetailAST exprStart) { 108 return isZero(exprStart) 109 || exprStart.getType() == TokenTypes.CHAR_LITERAL 110 && "'\\0'".equals(exprStart.getText()); 111 } 112 113 /** 114 * Checks for cases that should be skipped: no assignment, local variable, final variables 115 * @param ast Variable def AST 116 * @return true is that is a case that need to be skipped. 117 */ 118 private static boolean isSkipCase(DetailAST ast) { 119 boolean skipCase = true; 120 121 // do not check local variables and 122 // fields declared in interface/annotations 123 if (!ScopeUtils.isLocalVariableDef(ast) 124 && !ScopeUtils.isInInterfaceOrAnnotationBlock(ast)) { 125 final DetailAST assign = ast.findFirstToken(TokenTypes.ASSIGN); 126 127 if (assign != null) { 128 final DetailAST modifiers = ast.findFirstToken(TokenTypes.MODIFIERS); 129 skipCase = modifiers.branchContains(TokenTypes.FINAL); 130 } 131 } 132 return skipCase; 133 } 134 135 /** 136 * Determines if a given type is an object type. 137 * @param type type to check. 138 * @return true if it is an object type. 139 */ 140 private static boolean isObjectType(DetailAST type) { 141 final int objectType = type.getFirstChild().getType(); 142 return objectType == TokenTypes.IDENT || objectType == TokenTypes.DOT 143 || objectType == TokenTypes.ARRAY_DECLARATOR; 144 } 145 146 /** 147 * Determine if a given type is a numeric type. 148 * @param type code of the type for check. 149 * @return true if it's a numeric type. 150 * @see TokenTypes 151 */ 152 private static boolean isNumericType(int type) { 153 return type == TokenTypes.LITERAL_BYTE 154 || type == TokenTypes.LITERAL_SHORT 155 || type == TokenTypes.LITERAL_INT 156 || type == TokenTypes.LITERAL_FLOAT 157 || type == TokenTypes.LITERAL_LONG 158 || type == TokenTypes.LITERAL_DOUBLE; 159 } 160 161 /** 162 * @param expr node to check. 163 * @return true if given node contains numeric constant for zero. 164 */ 165 private static boolean isZero(DetailAST expr) { 166 final int type = expr.getType(); 167 switch (type) { 168 case TokenTypes.NUM_FLOAT: 169 case TokenTypes.NUM_DOUBLE: 170 case TokenTypes.NUM_INT: 171 case TokenTypes.NUM_LONG: 172 final String text = expr.getText(); 173 return Double.compare( 174 CheckUtils.parseDouble(text, type), 0.0) == 0; 175 default: 176 return false; 177 } 178 } 179}