001//////////////////////////////////////////////////////////////////////////////// 002// checkstyle: Checks Java source code for adherence to a set of rules. 003// Copyright (C) 2001-2014 Oliver Burn 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//////////////////////////////////////////////////////////////////////////////// 019package com.puppycrawl.tools.checkstyle.checks.coding; 020 021import com.puppycrawl.tools.checkstyle.api.Check; 022import com.puppycrawl.tools.checkstyle.api.DetailAST; 023import com.puppycrawl.tools.checkstyle.api.ScopeUtils; 024import com.puppycrawl.tools.checkstyle.api.TokenTypes; 025 026import com.puppycrawl.tools.checkstyle.checks.CheckUtils; 027 028/** 029 * <p> 030 * Checks if any class or object member explicitly initialized 031 * to default for its type value (<code>null</code> for object 032 * references, zero for numeric types and <code>char</code> 033 * and <code>false</code> for <code>boolean</code>. 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 Check 052{ 053 @Override 054 public final int[] getDefaultTokens() 055 { 056 return new int[] {TokenTypes.VARIABLE_DEF}; 057 } 058 059 @Override 060 public final int[] getRequiredTokens() 061 { 062 return getDefaultTokens(); 063 } 064 065 @Override 066 public void visitToken(DetailAST ast) 067 { 068 // do not check local variables and 069 // fields declared in interface/annotations 070 if (ScopeUtils.isLocalVariableDef(ast) 071 || ScopeUtils.inInterfaceOrAnnotationBlock(ast)) 072 { 073 return; 074 } 075 076 final DetailAST assign = ast.findFirstToken(TokenTypes.ASSIGN); 077 if (assign == null) { 078 // no assign - no check 079 return; 080 } 081 082 final DetailAST modifiers = ast.findFirstToken(TokenTypes.MODIFIERS); 083 if ((modifiers != null) 084 && modifiers.branchContains(TokenTypes.FINAL)) 085 { 086 // do not check final variables 087 return; 088 } 089 090 final DetailAST type = ast.findFirstToken(TokenTypes.TYPE); 091 final DetailAST ident = ast.findFirstToken(TokenTypes.IDENT); 092 final DetailAST exprStart = 093 assign.getFirstChild().getFirstChild(); 094 if (isObjectType(type) 095 && (exprStart.getType() == TokenTypes.LITERAL_NULL)) 096 { 097 log(ident, "explicit.init", ident.getText(), "null"); 098 } 099 100 final int primitiveType = type.getFirstChild().getType(); 101 if ((primitiveType == TokenTypes.LITERAL_BOOLEAN) 102 && (exprStart.getType() == TokenTypes.LITERAL_FALSE)) 103 { 104 log(ident, "explicit.init", ident.getText(), "false"); 105 } 106 if (isNumericType(primitiveType) && isZero(exprStart)) { 107 log(ident, "explicit.init", ident.getText(), "0"); 108 } 109 if ((primitiveType == TokenTypes.LITERAL_CHAR) 110 && (isZero(exprStart) 111 || ((exprStart.getType() == TokenTypes.CHAR_LITERAL) 112 && "'\\0'".equals(exprStart.getText())))) 113 { 114 log(ident, "explicit.init", ident.getText(), "\\0"); 115 } 116 } 117 118 /** 119 * Determines if a giiven type is an object type. 120 * @param type type to check. 121 * @return true if it is an object type. 122 */ 123 private boolean isObjectType(DetailAST type) 124 { 125 final int objectType = type.getFirstChild().getType(); 126 return ((objectType == TokenTypes.IDENT) || (objectType == TokenTypes.DOT) 127 || (objectType == TokenTypes.ARRAY_DECLARATOR)); 128 } 129 130 /** 131 * Determine if a given type is a numeric type. 132 * @param type code of the type for check. 133 * @return true if it's a numeric type. 134 * @see TokenTypes 135 */ 136 private boolean isNumericType(int type) 137 { 138 return ((type == TokenTypes.LITERAL_BYTE) 139 || (type == TokenTypes.LITERAL_SHORT) 140 || (type == TokenTypes.LITERAL_INT) 141 || (type == TokenTypes.LITERAL_FLOAT) 142 || (type == TokenTypes.LITERAL_LONG) 143 || (type == TokenTypes.LITERAL_DOUBLE)); 144 } 145 146 /** 147 * @param expr node to check. 148 * @return true if given node contains numeric constant for zero. 149 */ 150 private boolean isZero(DetailAST expr) 151 { 152 final int type = expr.getType(); 153 switch (type) { 154 case TokenTypes.NUM_FLOAT: 155 case TokenTypes.NUM_DOUBLE: 156 case TokenTypes.NUM_INT: 157 case TokenTypes.NUM_LONG: 158 final String text = expr.getText(); 159 return (0 == CheckUtils.parseFloat(text, type)); 160 default: 161 return false; 162 } 163 } 164}