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 java.util.Collections; | |
23 | import java.util.HashSet; | |
24 | import java.util.Set; | |
25 | ||
26 | import com.puppycrawl.tools.checkstyle.FileStatefulCheck; | |
27 | import com.puppycrawl.tools.checkstyle.api.AbstractCheck; | |
28 | import com.puppycrawl.tools.checkstyle.api.DetailAST; | |
29 | import com.puppycrawl.tools.checkstyle.api.TokenTypes; | |
30 | ||
31 | /** | |
32 | * Checks that any combination of String literals | |
33 | * is on the left side of an equals() comparison. | |
34 | * Also checks for String literals assigned to some field | |
35 | * (such as {@code someString.equals(anotherString = "text")}). | |
36 | * | |
37 | * <p>Rationale: Calling the equals() method on String literals | |
38 | * will avoid a potential NullPointerException. Also, it is | |
39 | * pretty common to see null check right before equals comparisons | |
40 | * which is not necessary in the below example. | |
41 | * | |
42 | * <p>For example: | |
43 | * | |
44 | * <pre> | |
45 | * {@code | |
46 | * String nullString = null; | |
47 | * nullString.equals("My_Sweet_String"); | |
48 | * } | |
49 | * </pre> | |
50 | * should be refactored to | |
51 | * | |
52 | * <pre> | |
53 | * {@code | |
54 | * String nullString = null; | |
55 | * "My_Sweet_String".equals(nullString); | |
56 | * } | |
57 | * </pre> | |
58 | * | |
59 | * @author Travis Schneeberger | |
60 | * @author Vladislav Lisetskiy | |
61 | */ | |
62 | @FileStatefulCheck | |
63 | public class EqualsAvoidNullCheck extends AbstractCheck { | |
64 | ||
65 | /** | |
66 | * A key is pointing to the warning message text in "messages.properties" | |
67 | * file. | |
68 | */ | |
69 | public static final String MSG_EQUALS_AVOID_NULL = "equals.avoid.null"; | |
70 | ||
71 | /** | |
72 | * A key is pointing to the warning message text in "messages.properties" | |
73 | * file. | |
74 | */ | |
75 | public static final String MSG_EQUALS_IGNORE_CASE_AVOID_NULL = "equalsIgnoreCase.avoid.null"; | |
76 | ||
77 | /** Method name for comparison. */ | |
78 | private static final String EQUALS = "equals"; | |
79 | ||
80 | /** Type name for comparison. */ | |
81 | private static final String STRING = "String"; | |
82 | ||
83 | /** Whether to process equalsIgnoreCase() invocations. */ | |
84 | private boolean ignoreEqualsIgnoreCase; | |
85 | ||
86 | /** Stack of sets of field names, one for each class of a set of nested classes. */ | |
87 | private FieldFrame currentFrame; | |
88 | ||
89 | @Override | |
90 | public int[] getDefaultTokens() { | |
91 |
1
1. getDefaultTokens : mutated return of Object value for com/puppycrawl/tools/checkstyle/checks/coding/EqualsAvoidNullCheck::getDefaultTokens to ( if (x != null) null else throw new RuntimeException ) → KILLED |
return getRequiredTokens(); |
92 | } | |
93 | ||
94 | @Override | |
95 | public int[] getAcceptableTokens() { | |
96 |
1
1. getAcceptableTokens : mutated return of Object value for com/puppycrawl/tools/checkstyle/checks/coding/EqualsAvoidNullCheck::getAcceptableTokens to ( if (x != null) null else throw new RuntimeException ) → KILLED |
return getRequiredTokens(); |
97 | } | |
98 | ||
99 | @Override | |
100 | public int[] getRequiredTokens() { | |
101 |
1
1. getRequiredTokens : mutated return of Object value for com/puppycrawl/tools/checkstyle/checks/coding/EqualsAvoidNullCheck::getRequiredTokens to ( if (x != null) null else throw new RuntimeException ) → KILLED |
return new int[] { |
102 | TokenTypes.METHOD_CALL, | |
103 | TokenTypes.CLASS_DEF, | |
104 | TokenTypes.METHOD_DEF, | |
105 | TokenTypes.LITERAL_IF, | |
106 | TokenTypes.LITERAL_FOR, | |
107 | TokenTypes.LITERAL_WHILE, | |
108 | TokenTypes.LITERAL_DO, | |
109 | TokenTypes.LITERAL_CATCH, | |
110 | TokenTypes.LITERAL_TRY, | |
111 | TokenTypes.VARIABLE_DEF, | |
112 | TokenTypes.PARAMETER_DEF, | |
113 | TokenTypes.CTOR_DEF, | |
114 | TokenTypes.SLIST, | |
115 | TokenTypes.ENUM_DEF, | |
116 | TokenTypes.ENUM_CONSTANT_DEF, | |
117 | TokenTypes.LITERAL_NEW, | |
118 | }; | |
119 | } | |
120 | ||
121 | /** | |
122 | * Whether to ignore checking {@code String.equalsIgnoreCase(String)}. | |
123 | * @param newValue whether to ignore checking | |
124 | * {@code String.equalsIgnoreCase(String)}. | |
125 | */ | |
126 | public void setIgnoreEqualsIgnoreCase(boolean newValue) { | |
127 | ignoreEqualsIgnoreCase = newValue; | |
128 | } | |
129 | ||
130 | @Override | |
131 | public void beginTree(DetailAST rootAST) { | |
132 | currentFrame = new FieldFrame(null); | |
133 | } | |
134 | ||
135 | @Override | |
136 | public void visitToken(final DetailAST ast) { | |
137 | switch (ast.getType()) { | |
138 | case TokenTypes.VARIABLE_DEF: | |
139 | case TokenTypes.PARAMETER_DEF: | |
140 |
1
1. visitToken : removed call to com/puppycrawl/tools/checkstyle/checks/coding/EqualsAvoidNullCheck$FieldFrame::addField → KILLED |
currentFrame.addField(ast); |
141 | break; | |
142 | case TokenTypes.METHOD_CALL: | |
143 |
1
1. visitToken : removed call to com/puppycrawl/tools/checkstyle/checks/coding/EqualsAvoidNullCheck::processMethodCall → KILLED |
processMethodCall(ast); |
144 | break; | |
145 | case TokenTypes.SLIST: | |
146 |
1
1. visitToken : removed call to com/puppycrawl/tools/checkstyle/checks/coding/EqualsAvoidNullCheck::processSlist → KILLED |
processSlist(ast); |
147 | break; | |
148 | case TokenTypes.LITERAL_NEW: | |
149 |
1
1. visitToken : removed call to com/puppycrawl/tools/checkstyle/checks/coding/EqualsAvoidNullCheck::processLiteralNew → KILLED |
processLiteralNew(ast); |
150 | break; | |
151 | default: | |
152 |
1
1. visitToken : removed call to com/puppycrawl/tools/checkstyle/checks/coding/EqualsAvoidNullCheck::processFrame → KILLED |
processFrame(ast); |
153 | } | |
154 | } | |
155 | ||
156 | @Override | |
157 | public void leaveToken(DetailAST ast) { | |
158 | final int astType = ast.getType(); | |
159 |
6
1. leaveToken : negated conditional → KILLED 2. leaveToken : negated conditional → KILLED 3. leaveToken : negated conditional → KILLED 4. leaveToken : negated conditional → KILLED 5. leaveToken : negated conditional → KILLED 6. leaveToken : negated conditional → KILLED |
if (astType != TokenTypes.VARIABLE_DEF |
160 | && astType != TokenTypes.PARAMETER_DEF | |
161 | && astType != TokenTypes.METHOD_CALL | |
162 | && astType != TokenTypes.SLIST | |
163 | && astType != TokenTypes.LITERAL_NEW | |
164 | || astType == TokenTypes.LITERAL_NEW | |
165 |
1
1. leaveToken : negated conditional → KILLED |
&& ast.findFirstToken(TokenTypes.OBJBLOCK) != null) { |
166 | currentFrame = currentFrame.getParent(); | |
167 | } | |
168 |
1
1. leaveToken : negated conditional → KILLED |
else if (astType == TokenTypes.SLIST) { |
169 |
1
1. leaveToken : removed call to com/puppycrawl/tools/checkstyle/checks/coding/EqualsAvoidNullCheck::leaveSlist → KILLED |
leaveSlist(ast); |
170 | } | |
171 | } | |
172 | ||
173 | @Override | |
174 | public void finishTree(DetailAST ast) { | |
175 |
1
1. finishTree : removed call to com/puppycrawl/tools/checkstyle/checks/coding/EqualsAvoidNullCheck::traverseFieldFrameTree → KILLED |
traverseFieldFrameTree(currentFrame); |
176 | } | |
177 | ||
178 | /** | |
179 | * Determine whether SLIST begins static or non-static block and add it as | |
180 | * a frame in this case. | |
181 | * @param ast SLIST ast. | |
182 | */ | |
183 | private void processSlist(DetailAST ast) { | |
184 | final int parentType = ast.getParent().getType(); | |
185 |
3
1. processSlist : negated conditional → KILLED 2. processSlist : negated conditional → KILLED 3. processSlist : negated conditional → KILLED |
if (parentType == TokenTypes.SLIST |
186 | || parentType == TokenTypes.STATIC_INIT | |
187 | || parentType == TokenTypes.INSTANCE_INIT) { | |
188 | final FieldFrame frame = new FieldFrame(currentFrame); | |
189 |
1
1. processSlist : removed call to com/puppycrawl/tools/checkstyle/checks/coding/EqualsAvoidNullCheck$FieldFrame::addChild → KILLED |
currentFrame.addChild(frame); |
190 | currentFrame = frame; | |
191 | } | |
192 | } | |
193 | ||
194 | /** | |
195 | * Determine whether SLIST begins static or non-static block. | |
196 | * @param ast SLIST ast. | |
197 | */ | |
198 | private void leaveSlist(DetailAST ast) { | |
199 | final int parentType = ast.getParent().getType(); | |
200 |
3
1. leaveSlist : negated conditional → KILLED 2. leaveSlist : negated conditional → KILLED 3. leaveSlist : negated conditional → KILLED |
if (parentType == TokenTypes.SLIST |
201 | || parentType == TokenTypes.STATIC_INIT | |
202 | || parentType == TokenTypes.INSTANCE_INIT) { | |
203 | currentFrame = currentFrame.getParent(); | |
204 | } | |
205 | } | |
206 | ||
207 | /** | |
208 | * Process CLASS_DEF, METHOD_DEF, LITERAL_IF, LITERAL_FOR, LITERAL_WHILE, LITERAL_DO, | |
209 | * LITERAL_CATCH, LITERAL_TRY, CTOR_DEF, ENUM_DEF, ENUM_CONSTANT_DEF. | |
210 | * @param ast processed ast. | |
211 | */ | |
212 | private void processFrame(DetailAST ast) { | |
213 | final FieldFrame frame = new FieldFrame(currentFrame); | |
214 | final int astType = ast.getType(); | |
215 |
3
1. processFrame : negated conditional → KILLED 2. processFrame : negated conditional → KILLED 3. processFrame : negated conditional → KILLED |
if (astType == TokenTypes.CLASS_DEF |
216 | || astType == TokenTypes.ENUM_DEF | |
217 | || astType == TokenTypes.ENUM_CONSTANT_DEF) { | |
218 |
1
1. processFrame : removed call to com/puppycrawl/tools/checkstyle/checks/coding/EqualsAvoidNullCheck$FieldFrame::setClassOrEnumOrEnumConstDef → KILLED |
frame.setClassOrEnumOrEnumConstDef(true); |
219 |
1
1. processFrame : removed call to com/puppycrawl/tools/checkstyle/checks/coding/EqualsAvoidNullCheck$FieldFrame::setFrameName → KILLED |
frame.setFrameName(ast.findFirstToken(TokenTypes.IDENT).getText()); |
220 | } | |
221 |
1
1. processFrame : removed call to com/puppycrawl/tools/checkstyle/checks/coding/EqualsAvoidNullCheck$FieldFrame::addChild → KILLED |
currentFrame.addChild(frame); |
222 | currentFrame = frame; | |
223 | } | |
224 | ||
225 | /** | |
226 | * Add the method call to the current frame if it should be processed. | |
227 | * @param methodCall METHOD_CALL ast. | |
228 | */ | |
229 | private void processMethodCall(DetailAST methodCall) { | |
230 | final DetailAST dot = methodCall.getFirstChild(); | |
231 |
1
1. processMethodCall : negated conditional → KILLED |
if (dot.getType() == TokenTypes.DOT) { |
232 | final String methodName = dot.getLastChild().getText(); | |
233 |
2
1. processMethodCall : negated conditional → KILLED 2. processMethodCall : negated conditional → KILLED |
if (EQUALS.equals(methodName) |
234 |
1
1. processMethodCall : negated conditional → KILLED |
|| !ignoreEqualsIgnoreCase && "equalsIgnoreCase".equals(methodName)) { |
235 |
1
1. processMethodCall : removed call to com/puppycrawl/tools/checkstyle/checks/coding/EqualsAvoidNullCheck$FieldFrame::addMethodCall → KILLED |
currentFrame.addMethodCall(methodCall); |
236 | } | |
237 | } | |
238 | } | |
239 | ||
240 | /** | |
241 | * Determine whether LITERAL_NEW is an anonymous class definition and add it as | |
242 | * a frame in this case. | |
243 | * @param ast LITERAL_NEW ast. | |
244 | */ | |
245 | private void processLiteralNew(DetailAST ast) { | |
246 |
1
1. processLiteralNew : negated conditional → KILLED |
if (ast.findFirstToken(TokenTypes.OBJBLOCK) != null) { |
247 | final FieldFrame frame = new FieldFrame(currentFrame); | |
248 |
1
1. processLiteralNew : removed call to com/puppycrawl/tools/checkstyle/checks/coding/EqualsAvoidNullCheck$FieldFrame::addChild → KILLED |
currentFrame.addChild(frame); |
249 | currentFrame = frame; | |
250 | } | |
251 | } | |
252 | ||
253 | /** | |
254 | * Traverse the tree of the field frames to check all equals method calls. | |
255 | * @param frame to check method calls in. | |
256 | */ | |
257 | private void traverseFieldFrameTree(FieldFrame frame) { | |
258 | for (FieldFrame child: frame.getChildren()) { | |
259 |
1
1. traverseFieldFrameTree : negated conditional → KILLED |
if (!child.getChildren().isEmpty()) { |
260 |
1
1. traverseFieldFrameTree : removed call to com/puppycrawl/tools/checkstyle/checks/coding/EqualsAvoidNullCheck::traverseFieldFrameTree → KILLED |
traverseFieldFrameTree(child); |
261 | } | |
262 | currentFrame = child; | |
263 |
1
1. traverseFieldFrameTree : removed call to java/util/Set::forEach → KILLED |
child.getMethodCalls().forEach(this::checkMethodCall); |
264 | } | |
265 | } | |
266 | ||
267 | /** | |
268 | * Check whether the method call should be violated. | |
269 | * @param methodCall method call to check. | |
270 | */ | |
271 | private void checkMethodCall(DetailAST methodCall) { | |
272 | DetailAST objCalledOn = methodCall.getFirstChild().getFirstChild(); | |
273 |
1
1. checkMethodCall : negated conditional → KILLED |
if (objCalledOn.getType() == TokenTypes.DOT) { |
274 | objCalledOn = objCalledOn.getLastChild(); | |
275 | } | |
276 | final DetailAST expr = methodCall.findFirstToken(TokenTypes.ELIST).getFirstChild(); | |
277 |
1
1. checkMethodCall : negated conditional → KILLED |
if (isObjectValid(objCalledOn) |
278 |
1
1. checkMethodCall : negated conditional → KILLED |
&& containsOneArgument(methodCall) |
279 |
1
1. checkMethodCall : negated conditional → KILLED |
&& containsAllSafeTokens(expr) |
280 |
1
1. checkMethodCall : negated conditional → KILLED |
&& isCalledOnStringFieldOrVariable(objCalledOn)) { |
281 | final String methodName = methodCall.getFirstChild().getLastChild().getText(); | |
282 |
1
1. checkMethodCall : negated conditional → KILLED |
if (EQUALS.equals(methodName)) { |
283 |
1
1. checkMethodCall : removed call to com/puppycrawl/tools/checkstyle/checks/coding/EqualsAvoidNullCheck::log → KILLED |
log(methodCall.getLineNo(), methodCall.getColumnNo(), |
284 | MSG_EQUALS_AVOID_NULL); | |
285 | } | |
286 | else { | |
287 |
1
1. checkMethodCall : removed call to com/puppycrawl/tools/checkstyle/checks/coding/EqualsAvoidNullCheck::log → KILLED |
log(methodCall.getLineNo(), methodCall.getColumnNo(), |
288 | MSG_EQUALS_IGNORE_CASE_AVOID_NULL); | |
289 | } | |
290 | } | |
291 | } | |
292 | ||
293 | /** | |
294 | * Check whether the object equals method is called on is not a String literal | |
295 | * and not too complex. | |
296 | * @param objCalledOn the object equals method is called on ast. | |
297 | * @return true if the object is valid. | |
298 | */ | |
299 | private static boolean isObjectValid(DetailAST objCalledOn) { | |
300 | boolean result = true; | |
301 | final DetailAST previousSibling = objCalledOn.getPreviousSibling(); | |
302 |
1
1. isObjectValid : negated conditional → KILLED |
if (previousSibling != null |
303 |
1
1. isObjectValid : negated conditional → KILLED |
&& previousSibling.getType() == TokenTypes.DOT) { |
304 | result = false; | |
305 | } | |
306 |
1
1. isObjectValid : negated conditional → KILLED |
if (isStringLiteral(objCalledOn)) { |
307 | result = false; | |
308 | } | |
309 |
1
1. isObjectValid : replaced return of integer sized value with (x == 0 ? 1 : 0) → KILLED |
return result; |
310 | } | |
311 | ||
312 | /** | |
313 | * Checks for calling equals on String literal and | |
314 | * anon object which cannot be null. | |
315 | * @param objCalledOn object AST | |
316 | * @return if it is string literal | |
317 | */ | |
318 | private static boolean isStringLiteral(DetailAST objCalledOn) { | |
319 |
2
1. isStringLiteral : negated conditional → KILLED 2. isStringLiteral : replaced return of integer sized value with (x == 0 ? 1 : 0) → KILLED |
return objCalledOn.getType() == TokenTypes.STRING_LITERAL |
320 |
1
1. isStringLiteral : negated conditional → KILLED |
|| objCalledOn.getType() == TokenTypes.LITERAL_NEW; |
321 | } | |
322 | ||
323 | /** | |
324 | * Verify that method call has one argument. | |
325 | * | |
326 | * @param methodCall METHOD_CALL DetailAST | |
327 | * @return true if method call has one argument. | |
328 | */ | |
329 | private static boolean containsOneArgument(DetailAST methodCall) { | |
330 | final DetailAST elist = methodCall.findFirstToken(TokenTypes.ELIST); | |
331 |
2
1. containsOneArgument : negated conditional → KILLED 2. containsOneArgument : replaced return of integer sized value with (x == 0 ? 1 : 0) → KILLED |
return elist.getChildCount() == 1; |
332 | } | |
333 | ||
334 | /** | |
335 | * Looks for all "safe" Token combinations in the argument | |
336 | * expression branch. | |
337 | * @param expr the argument expression | |
338 | * @return - true if any child matches the set of tokens, false if not | |
339 | */ | |
340 | private static boolean containsAllSafeTokens(final DetailAST expr) { | |
341 | DetailAST arg = expr.getFirstChild(); | |
342 | arg = skipVariableAssign(arg); | |
343 | ||
344 | boolean argIsNotNull = false; | |
345 |
1
1. containsAllSafeTokens : negated conditional → KILLED |
if (arg.getType() == TokenTypes.PLUS) { |
346 | DetailAST child = arg.getFirstChild(); | |
347 |
2
1. containsAllSafeTokens : negated conditional → KILLED 2. containsAllSafeTokens : negated conditional → KILLED |
while (child != null |
348 | && !argIsNotNull) { | |
349 |
1
1. containsAllSafeTokens : negated conditional → KILLED |
argIsNotNull = child.getType() == TokenTypes.STRING_LITERAL |
350 |
1
1. containsAllSafeTokens : negated conditional → KILLED |
|| child.getType() == TokenTypes.IDENT; |
351 | child = child.getNextSibling(); | |
352 | } | |
353 | } | |
354 | ||
355 |
2
1. containsAllSafeTokens : negated conditional → KILLED 2. containsAllSafeTokens : replaced return of integer sized value with (x == 0 ? 1 : 0) → KILLED |
return argIsNotNull |
356 |
1
1. containsAllSafeTokens : negated conditional → KILLED |
|| !arg.branchContains(TokenTypes.IDENT) |
357 |
1
1. containsAllSafeTokens : negated conditional → KILLED |
&& !arg.branchContains(TokenTypes.LITERAL_NULL); |
358 | } | |
359 | ||
360 | /** | |
361 | * Skips over an inner assign portion of an argument expression. | |
362 | * @param currentAST current token in the argument expression | |
363 | * @return the next relevant token | |
364 | */ | |
365 | private static DetailAST skipVariableAssign(final DetailAST currentAST) { | |
366 | DetailAST result = currentAST; | |
367 |
1
1. skipVariableAssign : negated conditional → KILLED |
if (currentAST.getType() == TokenTypes.ASSIGN |
368 |
1
1. skipVariableAssign : negated conditional → KILLED |
&& currentAST.getFirstChild().getType() == TokenTypes.IDENT) { |
369 | result = currentAST.getFirstChild().getNextSibling(); | |
370 | } | |
371 |
1
1. skipVariableAssign : mutated return of Object value for com/puppycrawl/tools/checkstyle/checks/coding/EqualsAvoidNullCheck::skipVariableAssign to ( if (x != null) null else throw new RuntimeException ) → KILLED |
return result; |
372 | } | |
373 | ||
374 | /** | |
375 | * Determine, whether equals method is called on a field of String type. | |
376 | * @param objCalledOn object ast. | |
377 | * @return true if the object is of String type. | |
378 | */ | |
379 | private boolean isCalledOnStringFieldOrVariable(DetailAST objCalledOn) { | |
380 | final boolean result; | |
381 | final DetailAST previousSiblingAst = objCalledOn.getPreviousSibling(); | |
382 |
1
1. isCalledOnStringFieldOrVariable : negated conditional → KILLED |
if (previousSiblingAst == null) { |
383 | result = isStringFieldOrVariable(objCalledOn); | |
384 | } | |
385 | else { | |
386 |
1
1. isCalledOnStringFieldOrVariable : negated conditional → KILLED |
if (previousSiblingAst.getType() == TokenTypes.LITERAL_THIS) { |
387 | result = isStringFieldOrVariableFromThisInstance(objCalledOn); | |
388 | } | |
389 | else { | |
390 | final String className = previousSiblingAst.getText(); | |
391 | result = isStringFieldOrVariableFromClass(objCalledOn, className); | |
392 | } | |
393 | } | |
394 |
1
1. isCalledOnStringFieldOrVariable : replaced return of integer sized value with (x == 0 ? 1 : 0) → KILLED |
return result; |
395 | } | |
396 | ||
397 | /** | |
398 | * Whether the field or the variable is of String type. | |
399 | * @param objCalledOn the field or the variable to check. | |
400 | * @return true if the field or the variable is of String type. | |
401 | */ | |
402 | private boolean isStringFieldOrVariable(DetailAST objCalledOn) { | |
403 | boolean result = false; | |
404 | final String name = objCalledOn.getText(); | |
405 | FieldFrame frame = currentFrame; | |
406 |
1
1. isStringFieldOrVariable : negated conditional → KILLED |
while (frame != null) { |
407 | final DetailAST field = frame.findField(name); | |
408 |
1
1. isStringFieldOrVariable : negated conditional → KILLED |
if (field != null |
409 |
1
1. isStringFieldOrVariable : negated conditional → KILLED |
&& (frame.isClassOrEnumOrEnumConstDef() |
410 |
1
1. isStringFieldOrVariable : negated conditional → KILLED |
|| checkLineNo(field, objCalledOn))) { |
411 | result = STRING.equals(getFieldType(field)); | |
412 | break; | |
413 | } | |
414 | frame = frame.getParent(); | |
415 | } | |
416 |
1
1. isStringFieldOrVariable : replaced return of integer sized value with (x == 0 ? 1 : 0) → KILLED |
return result; |
417 | } | |
418 | ||
419 | /** | |
420 | * Whether the field or the variable from THIS instance is of String type. | |
421 | * @param objCalledOn the field or the variable from THIS instance to check. | |
422 | * @return true if the field or the variable from THIS instance is of String type. | |
423 | */ | |
424 | private boolean isStringFieldOrVariableFromThisInstance(DetailAST objCalledOn) { | |
425 | boolean result = false; | |
426 | final String name = objCalledOn.getText(); | |
427 | final DetailAST field = getObjectFrame(currentFrame).findField(name); | |
428 |
1
1. isStringFieldOrVariableFromThisInstance : negated conditional → KILLED |
if (field != null) { |
429 | result = STRING.equals(getFieldType(field)); | |
430 | } | |
431 |
1
1. isStringFieldOrVariableFromThisInstance : replaced return of integer sized value with (x == 0 ? 1 : 0) → KILLED |
return result; |
432 | } | |
433 | ||
434 | /** | |
435 | * Whether the field or the variable from the specified class is of String type. | |
436 | * @param objCalledOn the field or the variable from the specified class to check. | |
437 | * @param className the name of the class to check in. | |
438 | * @return true if the field or the variable from the specified class is of String type. | |
439 | */ | |
440 | private boolean isStringFieldOrVariableFromClass(DetailAST objCalledOn, | |
441 | final String className) { | |
442 | boolean result = false; | |
443 | final String name = objCalledOn.getText(); | |
444 | FieldFrame frame = getObjectFrame(currentFrame); | |
445 |
1
1. isStringFieldOrVariableFromClass : negated conditional → KILLED |
while (frame != null) { |
446 |
1
1. isStringFieldOrVariableFromClass : negated conditional → KILLED |
if (className.equals(frame.getFrameName())) { |
447 | final DetailAST field = frame.findField(name); | |
448 |
1
1. isStringFieldOrVariableFromClass : negated conditional → KILLED |
if (field != null) { |
449 | result = STRING.equals(getFieldType(field)); | |
450 | } | |
451 | break; | |
452 | } | |
453 | frame = getObjectFrame(frame.getParent()); | |
454 | } | |
455 |
1
1. isStringFieldOrVariableFromClass : replaced return of integer sized value with (x == 0 ? 1 : 0) → KILLED |
return result; |
456 | } | |
457 | ||
458 | /** | |
459 | * Get the nearest parent frame which is CLASS_DEF, ENUM_DEF or ENUM_CONST_DEF. | |
460 | * @param frame to start the search from. | |
461 | * @return the nearest parent frame which is CLASS_DEF, ENUM_DEF or ENUM_CONST_DEF. | |
462 | */ | |
463 | private static FieldFrame getObjectFrame(FieldFrame frame) { | |
464 | FieldFrame objectFrame = frame; | |
465 |
2
1. getObjectFrame : negated conditional → KILLED 2. getObjectFrame : negated conditional → KILLED |
while (objectFrame != null && !objectFrame.isClassOrEnumOrEnumConstDef()) { |
466 | objectFrame = objectFrame.getParent(); | |
467 | } | |
468 |
1
1. getObjectFrame : mutated return of Object value for com/puppycrawl/tools/checkstyle/checks/coding/EqualsAvoidNullCheck::getObjectFrame to ( if (x != null) null else throw new RuntimeException ) → KILLED |
return objectFrame; |
469 | } | |
470 | ||
471 | /** | |
472 | * Check whether the field is declared before the method call in case of | |
473 | * methods and initialization blocks. | |
474 | * @param field field to check. | |
475 | * @param objCalledOn object equals method called on. | |
476 | * @return true if the field is declared before the method call. | |
477 | */ | |
478 | private static boolean checkLineNo(DetailAST field, DetailAST objCalledOn) { | |
479 | boolean result = false; | |
480 | // Required for pitest coverage. We should specify columnNo passing condition | |
481 | // in such a way, so that the minimal possible distance between field and | |
482 | // objCalledOn will be the maximal condition to pass this check. | |
483 | // The minimal distance between objCalledOn and field (of type String) initialization | |
484 | // is calculated as follows: | |
485 | // String(6) + space(1) + variableName(1) + assign(1) + | |
486 | // anotherStringVariableName(1) + semicolon(1) = 11 | |
487 | // Example: length of "String s=d;" is 11 symbols. | |
488 | final int minimumSymbolsBetween = 11; | |
489 |
2
1. checkLineNo : changed conditional boundary → KILLED 2. checkLineNo : negated conditional → KILLED |
if (field.getLineNo() < objCalledOn.getLineNo() |
490 |
1
1. checkLineNo : negated conditional → KILLED |
|| field.getLineNo() == objCalledOn.getLineNo() |
491 |
3
1. checkLineNo : Replaced integer addition with subtraction → SURVIVED 2. checkLineNo : changed conditional boundary → KILLED 3. checkLineNo : negated conditional → KILLED |
&& field.getColumnNo() + minimumSymbolsBetween <= objCalledOn.getColumnNo()) { |
492 | result = true; | |
493 | } | |
494 |
1
1. checkLineNo : replaced return of integer sized value with (x == 0 ? 1 : 0) → KILLED |
return result; |
495 | } | |
496 | ||
497 | /** | |
498 | * Get field type. | |
499 | * @param field to get the type from. | |
500 | * @return type of the field. | |
501 | */ | |
502 | private static String getFieldType(DetailAST field) { | |
503 | String fieldType = null; | |
504 | final DetailAST identAst = field.findFirstToken(TokenTypes.TYPE) | |
505 | .findFirstToken(TokenTypes.IDENT); | |
506 |
1
1. getFieldType : negated conditional → KILLED |
if (identAst != null) { |
507 | fieldType = identAst.getText(); | |
508 | } | |
509 |
1
1. getFieldType : mutated return of Object value for com/puppycrawl/tools/checkstyle/checks/coding/EqualsAvoidNullCheck::getFieldType to ( if (x != null) null else throw new RuntimeException ) → KILLED |
return fieldType; |
510 | } | |
511 | ||
512 | /** | |
513 | * Holds the names of fields of a type. | |
514 | */ | |
515 | private static class FieldFrame { | |
516 | ||
517 | /** Parent frame. */ | |
518 | private final FieldFrame parent; | |
519 | ||
520 | /** Set of frame's children. */ | |
521 | private final Set<FieldFrame> children = new HashSet<>(); | |
522 | ||
523 | /** Set of fields. */ | |
524 | private final Set<DetailAST> fields = new HashSet<>(); | |
525 | ||
526 | /** Set of equals calls. */ | |
527 | private final Set<DetailAST> methodCalls = new HashSet<>(); | |
528 | ||
529 | /** Name of the class, enum or enum constant declaration. */ | |
530 | private String frameName; | |
531 | ||
532 | /** Whether the frame is CLASS_DEF, ENUM_DEF or ENUM_CONST_DEF. */ | |
533 | private boolean classOrEnumOrEnumConstDef; | |
534 | ||
535 | /** | |
536 | * Creates new frame. | |
537 | * @param parent parent frame. | |
538 | */ | |
539 | FieldFrame(FieldFrame parent) { | |
540 | this.parent = parent; | |
541 | } | |
542 | ||
543 | /** | |
544 | * Set the frame name. | |
545 | * @param frameName value to set. | |
546 | */ | |
547 | public void setFrameName(String frameName) { | |
548 | this.frameName = frameName; | |
549 | } | |
550 | ||
551 | /** | |
552 | * Getter for the frame name. | |
553 | * @return frame name. | |
554 | */ | |
555 | public String getFrameName() { | |
556 |
1
1. getFrameName : mutated return of Object value for com/puppycrawl/tools/checkstyle/checks/coding/EqualsAvoidNullCheck$FieldFrame::getFrameName to ( if (x != null) null else throw new RuntimeException ) → KILLED |
return frameName; |
557 | } | |
558 | ||
559 | /** | |
560 | * Getter for the parent frame. | |
561 | * @return parent frame. | |
562 | */ | |
563 | public FieldFrame getParent() { | |
564 |
1
1. getParent : mutated return of Object value for com/puppycrawl/tools/checkstyle/checks/coding/EqualsAvoidNullCheck$FieldFrame::getParent to ( if (x != null) null else throw new RuntimeException ) → KILLED |
return parent; |
565 | } | |
566 | ||
567 | /** | |
568 | * Getter for frame's children. | |
569 | * @return children of this frame. | |
570 | */ | |
571 | public Set<FieldFrame> getChildren() { | |
572 |
1
1. getChildren : mutated return of Object value for com/puppycrawl/tools/checkstyle/checks/coding/EqualsAvoidNullCheck$FieldFrame::getChildren to ( if (x != null) null else throw new RuntimeException ) → KILLED |
return Collections.unmodifiableSet(children); |
573 | } | |
574 | ||
575 | /** | |
576 | * Add child frame to this frame. | |
577 | * @param child frame to add. | |
578 | */ | |
579 | public void addChild(FieldFrame child) { | |
580 | children.add(child); | |
581 | } | |
582 | ||
583 | /** | |
584 | * Add field to this FieldFrame. | |
585 | * @param field the ast of the field. | |
586 | */ | |
587 | public void addField(DetailAST field) { | |
588 | fields.add(field); | |
589 | } | |
590 | ||
591 | /** | |
592 | * Sets isClassOrEnum. | |
593 | * @param value value to set. | |
594 | */ | |
595 | public void setClassOrEnumOrEnumConstDef(boolean value) { | |
596 | classOrEnumOrEnumConstDef = value; | |
597 | } | |
598 | ||
599 | /** | |
600 | * Getter for classOrEnumOrEnumConstDef. | |
601 | * @return classOrEnumOrEnumConstDef. | |
602 | */ | |
603 | public boolean isClassOrEnumOrEnumConstDef() { | |
604 |
1
1. isClassOrEnumOrEnumConstDef : replaced return of integer sized value with (x == 0 ? 1 : 0) → KILLED |
return classOrEnumOrEnumConstDef; |
605 | } | |
606 | ||
607 | /** | |
608 | * Add method call to this frame. | |
609 | * @param methodCall METHOD_CALL ast. | |
610 | */ | |
611 | public void addMethodCall(DetailAST methodCall) { | |
612 | methodCalls.add(methodCall); | |
613 | } | |
614 | ||
615 | /** | |
616 | * Determines whether this FieldFrame contains the field. | |
617 | * @param name name of the field to check. | |
618 | * @return true if this FieldFrame contains instance field field. | |
619 | */ | |
620 | public DetailAST findField(String name) { | |
621 | DetailAST resultField = null; | |
622 | for (DetailAST field: fields) { | |
623 |
1
1. findField : negated conditional → KILLED |
if (getFieldName(field).equals(name)) { |
624 | resultField = field; | |
625 | break; | |
626 | } | |
627 | } | |
628 |
1
1. findField : mutated return of Object value for com/puppycrawl/tools/checkstyle/checks/coding/EqualsAvoidNullCheck$FieldFrame::findField to ( if (x != null) null else throw new RuntimeException ) → KILLED |
return resultField; |
629 | } | |
630 | ||
631 | /** | |
632 | * Getter for frame's method calls. | |
633 | * @return method calls of this frame. | |
634 | */ | |
635 | public Set<DetailAST> getMethodCalls() { | |
636 |
1
1. getMethodCalls : mutated return of Object value for com/puppycrawl/tools/checkstyle/checks/coding/EqualsAvoidNullCheck$FieldFrame::getMethodCalls to ( if (x != null) null else throw new RuntimeException ) → KILLED |
return Collections.unmodifiableSet(methodCalls); |
637 | } | |
638 | ||
639 | /** | |
640 | * Get the name of the field. | |
641 | * @param field to get the name from. | |
642 | * @return name of the field. | |
643 | */ | |
644 | private static String getFieldName(DetailAST field) { | |
645 |
1
1. getFieldName : mutated return of Object value for com/puppycrawl/tools/checkstyle/checks/coding/EqualsAvoidNullCheck$FieldFrame::getFieldName to ( if (x != null) null else throw new RuntimeException ) → KILLED |
return field.findFirstToken(TokenTypes.IDENT).getText(); |
646 | } | |
647 | ||
648 | } | |
649 | ||
650 | } | |
Mutations | ||
91 |
1.1 |
|
96 |
1.1 |
|
101 |
1.1 |
|
140 |
1.1 |
|
143 |
1.1 |
|
146 |
1.1 |
|
149 |
1.1 |
|
152 |
1.1 |
|
159 |
1.1 2.2 3.3 4.4 5.5 6.6 |
|
165 |
1.1 |
|
168 |
1.1 |
|
169 |
1.1 |
|
175 |
1.1 |
|
185 |
1.1 2.2 3.3 |
|
189 |
1.1 |
|
200 |
1.1 2.2 3.3 |
|
215 |
1.1 2.2 3.3 |
|
218 |
1.1 |
|
219 |
1.1 |
|
221 |
1.1 |
|
231 |
1.1 |
|
233 |
1.1 2.2 |
|
234 |
1.1 |
|
235 |
1.1 |
|
246 |
1.1 |
|
248 |
1.1 |
|
259 |
1.1 |
|
260 |
1.1 |
|
263 |
1.1 |
|
273 |
1.1 |
|
277 |
1.1 |
|
278 |
1.1 |
|
279 |
1.1 |
|
280 |
1.1 |
|
282 |
1.1 |
|
283 |
1.1 |
|
287 |
1.1 |
|
302 |
1.1 |
|
303 |
1.1 |
|
306 |
1.1 |
|
309 |
1.1 |
|
319 |
1.1 2.2 |
|
320 |
1.1 |
|
331 |
1.1 2.2 |
|
345 |
1.1 |
|
347 |
1.1 2.2 |
|
349 |
1.1 |
|
350 |
1.1 |
|
355 |
1.1 2.2 |
|
356 |
1.1 |
|
357 |
1.1 |
|
367 |
1.1 |
|
368 |
1.1 |
|
371 |
1.1 |
|
382 |
1.1 |
|
386 |
1.1 |
|
394 |
1.1 |
|
406 |
1.1 |
|
408 |
1.1 |
|
409 |
1.1 |
|
410 |
1.1 |
|
416 |
1.1 |
|
428 |
1.1 |
|
431 |
1.1 |
|
445 |
1.1 |
|
446 |
1.1 |
|
448 |
1.1 |
|
455 |
1.1 |
|
465 |
1.1 2.2 |
|
468 |
1.1 |
|
489 |
1.1 2.2 |
|
490 |
1.1 |
|
491 |
1.1 2.2 3.3 |
|
494 |
1.1 |
|
506 |
1.1 |
|
509 |
1.1 |
|
556 |
1.1 |
|
564 |
1.1 |
|
572 |
1.1 |
|
604 |
1.1 |
|
623 |
1.1 |
|
628 |
1.1 |
|
636 |
1.1 |
|
645 |
1.1 |