Coverage Report - com.puppycrawl.tools.checkstyle.checks.coding.ExplicitInitializationCheck
 
Classes in this File Line Coverage Branch Coverage Complexity
ExplicitInitializationCheck
100%
54/54
100%
56/56
3.364
 
 1  
 ////////////////////////////////////////////////////////////////////////////////
 2  
 // checkstyle: Checks Java source code for adherence to a set of rules.
 3  
 // Copyright (C) 2001-2017 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.CheckUtils;
 27  
 import com.puppycrawl.tools.checkstyle.utils.ScopeUtils;
 28  
 
 29  
 /**
 30  
  * <p>
 31  
  * Checks if any class or object member explicitly initialized
 32  
  * to default for its type value ({@code null} for object
 33  
  * references, zero for numeric types and {@code char}
 34  
  * and {@code false} for {@code boolean}.
 35  
  * </p>
 36  
  * <p>
 37  
  * Rationale: each instance variable gets
 38  
  * initialized twice, to the same value.  Java
 39  
  * initializes each instance variable to its default
 40  
  * value (0 or null) before performing any
 41  
  * initialization specified in the code.  So in this case,
 42  
  * x gets initialized to 0 twice, and bar gets initialized
 43  
  * to null twice.  So there is a minor inefficiency.  This style of
 44  
  * coding is a hold-over from C/C++ style coding,
 45  
  * and it shows that the developer isn't really confident that
 46  
  * Java really initializes instance variables to default
 47  
  * values.
 48  
  * </p>
 49  
  *
 50  
  * @author o_sukhodolsky
 51  
  */
 52  
 @StatelessCheck
 53  11
 public class ExplicitInitializationCheck extends AbstractCheck {
 54  
 
 55  
     /**
 56  
      * A key is pointing to the warning message text in "messages.properties"
 57  
      * file.
 58  
      */
 59  
     public static final String MSG_KEY = "explicit.init";
 60  
 
 61  
     /** Whether only explicit initialization made to null should be checked.**/
 62  
     private boolean onlyObjectReferences;
 63  
 
 64  
     @Override
 65  
     public final int[] getDefaultTokens() {
 66  15
         return getRequiredTokens();
 67  
     }
 68  
 
 69  
     @Override
 70  
     public final int[] getRequiredTokens() {
 71  35
         return new int[] {TokenTypes.VARIABLE_DEF};
 72  
     }
 73  
 
 74  
     @Override
 75  
     public final int[] getAcceptableTokens() {
 76  5
         return getRequiredTokens();
 77  
     }
 78  
 
 79  
     /**
 80  
      * Sets whether only explicit initialization made to null should be checked.
 81  
      * @param onlyObjectReferences whether only explicit initialization made to null
 82  
      *                             should be checked
 83  
      */
 84  
     public void setOnlyObjectReferences(boolean onlyObjectReferences) {
 85  2
         this.onlyObjectReferences = onlyObjectReferences;
 86  2
     }
 87  
 
 88  
     @Override
 89  
     public void visitToken(DetailAST ast) {
 90  90
         if (!isSkipCase(ast)) {
 91  60
             final DetailAST assign = ast.findFirstToken(TokenTypes.ASSIGN);
 92  60
             final DetailAST exprStart =
 93  60
                 assign.getFirstChild().getFirstChild();
 94  60
             final DetailAST type = ast.findFirstToken(TokenTypes.TYPE);
 95  60
             if (isObjectType(type)
 96  24
                 && exprStart.getType() == TokenTypes.LITERAL_NULL) {
 97  20
                 final DetailAST ident = ast.findFirstToken(TokenTypes.IDENT);
 98  20
                 log(ident, MSG_KEY, ident.getText(), "null");
 99  
             }
 100  60
             if (!onlyObjectReferences) {
 101  30
                 validateNonObjects(ast);
 102  
             }
 103  
         }
 104  90
     }
 105  
 
 106  
     /**
 107  
      * Checks for explicit initializations made to 'false', '0' and '\0'.
 108  
      * @param ast token being checked for explicit initializations
 109  
      */
 110  
     private void validateNonObjects(DetailAST ast) {
 111  30
         final DetailAST ident = ast.findFirstToken(TokenTypes.IDENT);
 112  30
         final DetailAST assign = ast.findFirstToken(TokenTypes.ASSIGN);
 113  30
         final DetailAST exprStart =
 114  30
                 assign.getFirstChild().getFirstChild();
 115  30
         final DetailAST type = ast.findFirstToken(TokenTypes.TYPE);
 116  30
         final int primitiveType = type.getFirstChild().getType();
 117  30
         if (primitiveType == TokenTypes.LITERAL_BOOLEAN
 118  2
                 && exprStart.getType() == TokenTypes.LITERAL_FALSE) {
 119  1
             log(ident, MSG_KEY, ident.getText(), "false");
 120  
         }
 121  30
         if (isNumericType(primitiveType) && isZero(exprStart)) {
 122  6
             log(ident, MSG_KEY, ident.getText(), "0");
 123  
         }
 124  30
         if (primitiveType == TokenTypes.LITERAL_CHAR
 125  4
                 && isZeroChar(exprStart)) {
 126  2
             log(ident, MSG_KEY, ident.getText(), "\\0");
 127  
         }
 128  30
     }
 129  
 
 130  
     /**
 131  
      * Examine char literal for initializing to default value.
 132  
      * @param exprStart expression
 133  
      * @return true is literal is initialized by zero symbol
 134  
      */
 135  
     private static boolean isZeroChar(DetailAST exprStart) {
 136  8
         return isZero(exprStart)
 137  3
             || exprStart.getType() == TokenTypes.CHAR_LITERAL
 138  2
             && "'\\0'".equals(exprStart.getText());
 139  
     }
 140  
 
 141  
     /**
 142  
      * Checks for cases that should be skipped: no assignment, local variable, final variables.
 143  
      * @param ast Variable def AST
 144  
      * @return true is that is a case that need to be skipped.
 145  
      */
 146  
     private static boolean isSkipCase(DetailAST ast) {
 147  90
         boolean skipCase = true;
 148  
 
 149  
         // do not check local variables and
 150  
         // fields declared in interface/annotations
 151  90
         if (!ScopeUtils.isLocalVariableDef(ast)
 152  84
                 && !ScopeUtils.isInInterfaceOrAnnotationBlock(ast)) {
 153  72
             final DetailAST assign = ast.findFirstToken(TokenTypes.ASSIGN);
 154  
 
 155  72
             if (assign != null) {
 156  62
                 final DetailAST modifiers = ast.findFirstToken(TokenTypes.MODIFIERS);
 157  62
                 skipCase = modifiers.findFirstToken(TokenTypes.FINAL) != null;
 158  
             }
 159  
         }
 160  90
         return skipCase;
 161  
     }
 162  
 
 163  
     /**
 164  
      * Determines if a given type is an object type.
 165  
      * @param type type to check.
 166  
      * @return true if it is an object type.
 167  
      */
 168  
     private static boolean isObjectType(DetailAST type) {
 169  60
         final int objectType = type.getFirstChild().getType();
 170  60
         return objectType == TokenTypes.IDENT || objectType == TokenTypes.DOT
 171  
                 || objectType == TokenTypes.ARRAY_DECLARATOR;
 172  
     }
 173  
 
 174  
     /**
 175  
      * Determine if a given type is a numeric type.
 176  
      * @param type code of the type for check.
 177  
      * @return true if it's a numeric type.
 178  
      * @see TokenTypes
 179  
      */
 180  
     private static boolean isNumericType(int type) {
 181  30
         return type == TokenTypes.LITERAL_BYTE
 182  
                 || type == TokenTypes.LITERAL_SHORT
 183  
                 || type == TokenTypes.LITERAL_INT
 184  
                 || type == TokenTypes.LITERAL_FLOAT
 185  
                 || type == TokenTypes.LITERAL_LONG
 186  
                 || type == TokenTypes.LITERAL_DOUBLE;
 187  
     }
 188  
 
 189  
     /**
 190  
      * Checks if given node contains numeric constant for zero.
 191  
      *
 192  
      * @param expr node to check.
 193  
      * @return true if given node contains numeric constant for zero.
 194  
      */
 195  
     private static boolean isZero(DetailAST expr) {
 196  16
         final int type = expr.getType();
 197  
         final boolean isZero;
 198  16
         switch (type) {
 199  
             case TokenTypes.NUM_FLOAT:
 200  
             case TokenTypes.NUM_DOUBLE:
 201  
             case TokenTypes.NUM_INT:
 202  
             case TokenTypes.NUM_LONG:
 203  12
                 final String text = expr.getText();
 204  12
                 isZero = Double.compare(CheckUtils.parseDouble(text, type), 0.0) == 0;
 205  12
                 break;
 206  
             default:
 207  4
                 isZero = false;
 208  
         }
 209  16
         return isZero;
 210  
     }
 211  
 }