1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package com.puppycrawl.tools.checkstyle.checks.coding;
21
22 import java.util.Optional;
23
24 import com.puppycrawl.tools.checkstyle.StatelessCheck;
25 import com.puppycrawl.tools.checkstyle.api.AbstractCheck;
26 import com.puppycrawl.tools.checkstyle.api.DetailAST;
27 import com.puppycrawl.tools.checkstyle.api.TokenTypes;
28
29
30
31
32
33
34
35
36
37
38
39
40
41 @StatelessCheck
42 public class UnnecessaryNullCheckWithInstanceOfCheck extends AbstractCheck {
43
44
45
46
47 public static final String MSG_UNNECESSARY_NULLCHECK = "unnecessary.nullcheck.with.instanceof";
48
49
50
51
52 public UnnecessaryNullCheckWithInstanceOfCheck() {
53
54 }
55
56 @Override
57 public int[] getDefaultTokens() {
58 return getRequiredTokens();
59 }
60
61 @Override
62 public int[] getAcceptableTokens() {
63 return getRequiredTokens();
64 }
65
66 @Override
67 public int[] getRequiredTokens() {
68 return new int[] {TokenTypes.LITERAL_INSTANCEOF};
69 }
70
71 @Override
72 public void visitToken(DetailAST instanceofNode) {
73 findUnnecessaryNullCheck(instanceofNode)
74 .ifPresent(violationNode -> log(violationNode, MSG_UNNECESSARY_NULLCHECK));
75 }
76
77
78
79
80
81
82
83 private static Optional<DetailAST> findUnnecessaryNullCheck(DetailAST instanceOfNode) {
84 final DetailAST topLevelExpr = findTopLevelLogicalExpression(instanceOfNode);
85
86 Optional<DetailAST> result = Optional.empty();
87 if (topLevelExpr.getType() == TokenTypes.LAND) {
88 result = findRedundantNullCheck(topLevelExpr, instanceOfNode)
89 .map(DetailAST::getFirstChild);
90 }
91 return result;
92 }
93
94
95
96
97
98
99
100 private static DetailAST findTopLevelLogicalExpression(DetailAST node) {
101 DetailAST currentParent = node;
102 while (currentParent.getParent().getType() == TokenTypes.LAND
103 || currentParent.getParent().getType() == TokenTypes.LOR) {
104 currentParent = currentParent.getParent();
105 }
106 return currentParent;
107 }
108
109
110
111
112
113
114
115
116 private static Optional<DetailAST> findRedundantNullCheck(DetailAST logicalAndNode,
117 DetailAST instanceOfNode) {
118
119 Optional<DetailAST> nullCheckNode = Optional.empty();
120 final DetailAST instanceOfIdent = instanceOfNode.findFirstToken(TokenTypes.IDENT);
121
122 if (instanceOfIdent != null
123 && !containsVariableDereference(logicalAndNode, instanceOfIdent.getText())) {
124
125 nullCheckNode = searchForNullCheck(logicalAndNode, instanceOfNode, instanceOfIdent);
126 }
127 return nullCheckNode;
128 }
129
130
131
132
133
134
135
136
137
138 private static Optional<DetailAST> searchForNullCheck(DetailAST logicalAndNode,
139 DetailAST instanceOfNode, DetailAST instanceOfIdent) {
140
141 Optional<DetailAST> nullCheckNode = Optional.empty();
142
143 final Optional<DetailAST> instanceOfSubtree =
144 findDirectChildContaining(logicalAndNode, instanceOfNode);
145
146 final DetailAST instanceOfSubtreeNode =
147 instanceOfSubtree.orElse(null);
148
149 final boolean instanceOfInLor =
150 instanceOfSubtreeNode != null
151 && instanceOfSubtreeNode.getType() == TokenTypes.LOR;
152
153 DetailAST currentChild = logicalAndNode.getFirstChild();
154 while (currentChild != null) {
155 if (instanceOfInLor && currentChild.equals(instanceOfSubtreeNode)) {
156 break;
157 }
158
159 if (nullCheckNode.isEmpty()) {
160 nullCheckNode = checkChildForNullCheck(
161 currentChild, instanceOfNode, instanceOfIdent);
162 }
163
164 currentChild = currentChild.getNextSibling();
165 }
166
167 return nullCheckNode;
168 }
169
170
171
172
173
174
175
176
177
178 private static Optional<DetailAST> checkChildForNullCheck(DetailAST currentChild,
179 DetailAST instanceOfNode, DetailAST instanceOfIdent) {
180
181 Optional<DetailAST> result = Optional.empty();
182
183 if (isNotEqual(currentChild)
184 && isNullCheckRedundant(instanceOfIdent, currentChild)) {
185 result = Optional.of(currentChild);
186 }
187 else if (currentChild.getType() == TokenTypes.LAND) {
188 result = findRedundantNullCheck(currentChild, instanceOfNode);
189 }
190
191 return result;
192 }
193
194
195
196
197
198
199
200
201 private static Optional<DetailAST> findDirectChildContaining(DetailAST parent,
202 DetailAST target) {
203
204 DetailAST result = null;
205 DetailAST child = parent.getFirstChild();
206 while (child != null) {
207 if (isAncestorOf(child, target)) {
208 result = child;
209 break;
210 }
211 child = child.getNextSibling();
212 }
213 return Optional.ofNullable(result);
214 }
215
216
217
218
219
220
221
222
223 private static boolean isAncestorOf(DetailAST node, DetailAST target) {
224 boolean found = false;
225 DetailAST current = target;
226 while (current != null) {
227 if (current.equals(node)) {
228 found = true;
229 break;
230 }
231 current = current.getParent();
232 }
233 return found;
234 }
235
236
237
238
239
240
241
242
243
244 private static boolean containsVariableDereference(DetailAST node, String variableName) {
245
246 boolean found = false;
247
248 if (node.getType() == TokenTypes.DOT
249 || node.getType() == TokenTypes.METHOD_CALL
250 || node.getType() == TokenTypes.LAND
251 || node.getType() == TokenTypes.LOR) {
252
253 DetailAST firstChild = node.getFirstChild();
254
255 while (firstChild != null) {
256 if (variableName.equals(firstChild.getText())
257 && firstChild.getNextSibling().getType() != TokenTypes.ELIST
258 || containsVariableDereference(firstChild, variableName)) {
259 found = true;
260 break;
261 }
262 firstChild = firstChild.getNextSibling();
263 }
264 }
265 return found;
266 }
267
268
269
270
271
272
273
274 private static boolean isNotEqual(DetailAST node) {
275 return node.getType() == TokenTypes.NOT_EQUAL;
276 }
277
278
279
280
281
282
283
284 private static boolean isNullLiteral(DetailAST node) {
285 return node.getType() == TokenTypes.LITERAL_NULL;
286 }
287
288
289
290
291
292
293
294
295 private static boolean isNullCheckRedundant(DetailAST instanceOfIdent,
296 final DetailAST nullCheckNode) {
297
298 final DetailAST nullCheckIdent = nullCheckNode.findFirstToken(TokenTypes.IDENT);
299 return nullCheckIdent != null
300 && (isNullLiteral(nullCheckNode.getFirstChild().getNextSibling())
301 || isNullLiteral(nullCheckNode.getFirstChild()))
302 && instanceOfIdent.getText().equals(nullCheckIdent.getText());
303 }
304
305 }