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 |