| 1 | //////////////////////////////////////////////////////////////////////////////// | |
| 2 | // checkstyle: Checks Java source code for adherence to a set of rules. | |
| 3 | // Copyright (C) 2001-2018 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 | 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 |
1
1. getDefaultTokens : mutated return of Object value for com/puppycrawl/tools/checkstyle/checks/coding/ExplicitInitializationCheck::getDefaultTokens to ( if (x != null) null else throw new RuntimeException ) → KILLED |
return getRequiredTokens(); |
| 67 | } | |
| 68 | ||
| 69 | @Override | |
| 70 | public final int[] getRequiredTokens() { | |
| 71 |
1
1. getRequiredTokens : mutated return of Object value for com/puppycrawl/tools/checkstyle/checks/coding/ExplicitInitializationCheck::getRequiredTokens to ( if (x != null) null else throw new RuntimeException ) → KILLED |
return new int[] {TokenTypes.VARIABLE_DEF}; |
| 72 | } | |
| 73 | ||
| 74 | @Override | |
| 75 | public final int[] getAcceptableTokens() { | |
| 76 |
1
1. getAcceptableTokens : mutated return of Object value for com/puppycrawl/tools/checkstyle/checks/coding/ExplicitInitializationCheck::getAcceptableTokens to ( if (x != null) null else throw new RuntimeException ) → KILLED |
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 | this.onlyObjectReferences = onlyObjectReferences; | |
| 86 | } | |
| 87 | ||
| 88 | @Override | |
| 89 | public void visitToken(DetailAST ast) { | |
| 90 |
1
1. visitToken : negated conditional → KILLED |
if (!isSkipCase(ast)) { |
| 91 | final DetailAST assign = ast.findFirstToken(TokenTypes.ASSIGN); | |
| 92 | final DetailAST exprStart = | |
| 93 | assign.getFirstChild().getFirstChild(); | |
| 94 | final DetailAST type = ast.findFirstToken(TokenTypes.TYPE); | |
| 95 |
1
1. visitToken : negated conditional → KILLED |
if (isObjectType(type) |
| 96 |
1
1. visitToken : negated conditional → KILLED |
&& exprStart.getType() == TokenTypes.LITERAL_NULL) { |
| 97 | final DetailAST ident = ast.findFirstToken(TokenTypes.IDENT); | |
| 98 |
1
1. visitToken : removed call to com/puppycrawl/tools/checkstyle/checks/coding/ExplicitInitializationCheck::log → KILLED |
log(ident, MSG_KEY, ident.getText(), "null"); |
| 99 | } | |
| 100 |
1
1. visitToken : negated conditional → KILLED |
if (!onlyObjectReferences) { |
| 101 |
1
1. visitToken : removed call to com/puppycrawl/tools/checkstyle/checks/coding/ExplicitInitializationCheck::validateNonObjects → KILLED |
validateNonObjects(ast); |
| 102 | } | |
| 103 | } | |
| 104 | } | |
| 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 | final DetailAST ident = ast.findFirstToken(TokenTypes.IDENT); | |
| 112 | final DetailAST assign = ast.findFirstToken(TokenTypes.ASSIGN); | |
| 113 | final DetailAST exprStart = | |
| 114 | assign.getFirstChild().getFirstChild(); | |
| 115 | final DetailAST type = ast.findFirstToken(TokenTypes.TYPE); | |
| 116 | final int primitiveType = type.getFirstChild().getType(); | |
| 117 |
1
1. validateNonObjects : negated conditional → KILLED |
if (primitiveType == TokenTypes.LITERAL_BOOLEAN |
| 118 |
1
1. validateNonObjects : negated conditional → KILLED |
&& exprStart.getType() == TokenTypes.LITERAL_FALSE) { |
| 119 |
1
1. validateNonObjects : removed call to com/puppycrawl/tools/checkstyle/checks/coding/ExplicitInitializationCheck::log → KILLED |
log(ident, MSG_KEY, ident.getText(), "false"); |
| 120 | } | |
| 121 |
2
1. validateNonObjects : negated conditional → KILLED 2. validateNonObjects : negated conditional → KILLED |
if (isNumericType(primitiveType) && isZero(exprStart)) { |
| 122 |
1
1. validateNonObjects : removed call to com/puppycrawl/tools/checkstyle/checks/coding/ExplicitInitializationCheck::log → KILLED |
log(ident, MSG_KEY, ident.getText(), "0"); |
| 123 | } | |
| 124 |
1
1. validateNonObjects : negated conditional → KILLED |
if (primitiveType == TokenTypes.LITERAL_CHAR |
| 125 |
1
1. validateNonObjects : negated conditional → KILLED |
&& isZeroChar(exprStart)) { |
| 126 |
1
1. validateNonObjects : removed call to com/puppycrawl/tools/checkstyle/checks/coding/ExplicitInitializationCheck::log → KILLED |
log(ident, MSG_KEY, ident.getText(), "\\0"); |
| 127 | } | |
| 128 | } | |
| 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 |
2
1. isZeroChar : negated conditional → KILLED 2. isZeroChar : replaced return of integer sized value with (x == 0 ? 1 : 0) → KILLED |
return isZero(exprStart) |
| 137 |
1
1. isZeroChar : negated conditional → KILLED |
|| exprStart.getType() == TokenTypes.CHAR_LITERAL |
| 138 |
1
1. isZeroChar : negated conditional → KILLED |
&& "'\\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 | boolean skipCase = true; | |
| 148 | ||
| 149 | // do not check local variables and | |
| 150 | // fields declared in interface/annotations | |
| 151 |
1
1. isSkipCase : negated conditional → KILLED |
if (!ScopeUtils.isLocalVariableDef(ast) |
| 152 |
1
1. isSkipCase : negated conditional → KILLED |
&& !ScopeUtils.isInInterfaceOrAnnotationBlock(ast)) { |
| 153 | final DetailAST assign = ast.findFirstToken(TokenTypes.ASSIGN); | |
| 154 | ||
| 155 |
1
1. isSkipCase : negated conditional → KILLED |
if (assign != null) { |
| 156 | final DetailAST modifiers = ast.findFirstToken(TokenTypes.MODIFIERS); | |
| 157 |
1
1. isSkipCase : negated conditional → KILLED |
skipCase = modifiers.findFirstToken(TokenTypes.FINAL) != null; |
| 158 | } | |
| 159 | } | |
| 160 |
1
1. isSkipCase : replaced return of integer sized value with (x == 0 ? 1 : 0) → KILLED |
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 | final int objectType = type.getFirstChild().getType(); | |
| 170 |
4
1. isObjectType : negated conditional → KILLED 2. isObjectType : negated conditional → KILLED 3. isObjectType : negated conditional → KILLED 4. isObjectType : replaced return of integer sized value with (x == 0 ? 1 : 0) → KILLED |
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 |
7
1. isNumericType : negated conditional → KILLED 2. isNumericType : negated conditional → KILLED 3. isNumericType : negated conditional → KILLED 4. isNumericType : negated conditional → KILLED 5. isNumericType : negated conditional → KILLED 6. isNumericType : negated conditional → KILLED 7. isNumericType : replaced return of integer sized value with (x == 0 ? 1 : 0) → KILLED |
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 | final int type = expr.getType(); | |
| 197 | final boolean isZero; | |
| 198 | switch (type) { | |
| 199 | case TokenTypes.NUM_FLOAT: | |
| 200 | case TokenTypes.NUM_DOUBLE: | |
| 201 | case TokenTypes.NUM_INT: | |
| 202 | case TokenTypes.NUM_LONG: | |
| 203 | final String text = expr.getText(); | |
| 204 |
1
1. isZero : negated conditional → KILLED |
isZero = Double.compare(CheckUtils.parseDouble(text, type), 0.0) == 0; |
| 205 | break; | |
| 206 | default: | |
| 207 | isZero = false; | |
| 208 | } | |
| 209 |
1
1. isZero : replaced return of integer sized value with (x == 0 ? 1 : 0) → KILLED |
return isZero; |
| 210 | } | |
| 211 | ||
| 212 | } | |
Mutations | ||
| 66 |
1.1 |
|
| 71 |
1.1 |
|
| 76 |
1.1 |
|
| 90 |
1.1 |
|
| 95 |
1.1 |
|
| 96 |
1.1 |
|
| 98 |
1.1 |
|
| 100 |
1.1 |
|
| 101 |
1.1 |
|
| 117 |
1.1 |
|
| 118 |
1.1 |
|
| 119 |
1.1 |
|
| 121 |
1.1 2.2 |
|
| 122 |
1.1 |
|
| 124 |
1.1 |
|
| 125 |
1.1 |
|
| 126 |
1.1 |
|
| 136 |
1.1 2.2 |
|
| 137 |
1.1 |
|
| 138 |
1.1 |
|
| 151 |
1.1 |
|
| 152 |
1.1 |
|
| 155 |
1.1 |
|
| 157 |
1.1 |
|
| 160 |
1.1 |
|
| 170 |
1.1 2.2 3.3 4.4 |
|
| 181 |
1.1 2.2 3.3 4.4 5.5 6.6 7.7 |
|
| 204 |
1.1 |
|
| 209 |
1.1 |