1
2
3
4 package net.sourceforge.pmd.lang.java.rule.basic;
5
6 import java.util.ArrayList;
7 import java.util.List;
8
9 import net.sourceforge.pmd.lang.ast.Node;
10 import net.sourceforge.pmd.lang.java.ast.ASTAssignmentOperator;
11 import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceType;
12 import net.sourceforge.pmd.lang.java.ast.ASTConditionalAndExpression;
13 import net.sourceforge.pmd.lang.java.ast.ASTConditionalOrExpression;
14 import net.sourceforge.pmd.lang.java.ast.ASTEqualityExpression;
15 import net.sourceforge.pmd.lang.java.ast.ASTExpression;
16 import net.sourceforge.pmd.lang.java.ast.ASTIfStatement;
17 import net.sourceforge.pmd.lang.java.ast.ASTLiteral;
18 import net.sourceforge.pmd.lang.java.ast.ASTName;
19 import net.sourceforge.pmd.lang.java.ast.ASTNullLiteral;
20 import net.sourceforge.pmd.lang.java.ast.ASTPrimaryExpression;
21 import net.sourceforge.pmd.lang.java.ast.ASTPrimaryPrefix;
22 import net.sourceforge.pmd.lang.java.ast.ASTPrimarySuffix;
23 import net.sourceforge.pmd.lang.java.rule.AbstractJavaRule;
24 import net.sourceforge.pmd.util.StringUtil;
25
26 public class BrokenNullCheckRule extends AbstractJavaRule {
27
28 @Override
29 public Object visit(ASTIfStatement node, Object data) {
30 ASTExpression expression = (ASTExpression)node.jjtGetChild(0);
31
32 ASTConditionalAndExpression conditionalAndExpression = expression.getFirstDescendantOfType(ASTConditionalAndExpression.class);
33 if (conditionalAndExpression != null) {
34 checkForViolations(node, data, conditionalAndExpression);
35 }
36
37 ASTConditionalOrExpression conditionalOrExpression = expression.getFirstDescendantOfType(ASTConditionalOrExpression.class);
38 if (conditionalOrExpression != null) {
39 checkForViolations(node, data, conditionalOrExpression);
40 }
41
42 return super.visit(node, data);
43 }
44
45
46 private void checkForViolations(ASTIfStatement node, Object data, Node conditionalExpression) {
47 ASTEqualityExpression equalityExpression = conditionalExpression.getFirstChildOfType(ASTEqualityExpression.class);
48 if (equalityExpression == null) {
49 return;
50 }
51 if (conditionalExpression instanceof ASTConditionalAndExpression &&
52 !"==".equals(equalityExpression.getImage())) {
53 return;
54 }
55 if (conditionalExpression instanceof ASTConditionalOrExpression &&
56 !"!=".equals(equalityExpression.getImage())) {
57 return;
58 }
59 ASTNullLiteral nullLiteral = equalityExpression.getFirstDescendantOfType(ASTNullLiteral.class);
60 if (nullLiteral == null) {
61 return;
62 }
63
64 if (conditionalExpression.hasDescendantOfType(ASTAssignmentOperator.class)) {
65 return;
66 }
67
68
69 ASTPrimaryExpression nullCompareExpression = findNullCompareExpression(equalityExpression);
70 if (nullCompareExpression == null) {
71 return;
72 }
73
74
75 for (int i = 0; i < conditionalExpression.jjtGetNumChildren(); i++) {
76 Node conditionalSubnode = conditionalExpression.jjtGetChild(i);
77
78
79 ASTEqualityExpression nullEqualityExpression = nullLiteral.getFirstParentOfType(ASTEqualityExpression.class);
80 if (conditionalSubnode.equals(nullEqualityExpression)) {
81 continue;
82 }
83 ASTPrimaryExpression conditionalPrimaryExpression;
84 if (conditionalSubnode instanceof ASTPrimaryExpression) {
85 conditionalPrimaryExpression = (ASTPrimaryExpression)conditionalSubnode;
86 } else {
87
88 conditionalPrimaryExpression = conditionalSubnode
89 .getFirstDescendantOfType(ASTPrimaryExpression.class);
90 }
91
92 if (primaryExpressionsAreEqual(nullCompareExpression, conditionalPrimaryExpression)) {
93 addViolation(data, node);
94 }
95
96 }
97 }
98
99 private boolean primaryExpressionsAreEqual(ASTPrimaryExpression nullCompareVariable, ASTPrimaryExpression expressionUsage) {
100 List<String> nullCompareNames = new ArrayList<String>();
101 findExpressionNames(nullCompareVariable, nullCompareNames);
102
103 List<String> expressionUsageNames = new ArrayList<String>();
104 findExpressionNames(expressionUsage, expressionUsageNames);
105
106 for (int i = 0; i < nullCompareNames.size(); i++) {
107 if (expressionUsageNames.size() == i) {
108 return false;
109 }
110
111 String nullCompareExpressionName = nullCompareNames.get(i);
112 String expressionUsageName = expressionUsageNames.get(i);
113
114
115 if (!nullCompareExpressionName.equals(expressionUsageName) &&
116 !expressionUsageName.startsWith(nullCompareExpressionName + ".")) {
117 return false;
118 }
119 }
120
121 return true;
122 }
123
124
125
126
127
128 private void findExpressionNames(Node nullCompareVariable, List<String> results) {
129 for (int i = 0; i < nullCompareVariable.jjtGetNumChildren(); i++) {
130 Node child = nullCompareVariable.jjtGetChild(i);
131
132 if (child instanceof ASTName) {
133 results.add( ((ASTName)child).getImage() );
134 } else if (child instanceof ASTLiteral) {
135 String literalImage = ((ASTLiteral)child).getImage();
136
137 if (literalImage != null) {
138 results.add( literalImage );
139 }
140 } else if (child instanceof ASTPrimarySuffix) {
141 String name = ((ASTPrimarySuffix)child).getImage();
142 if (StringUtil.isNotEmpty(name)) {
143 results.add(name);
144 }
145 } else if (child instanceof ASTClassOrInterfaceType) {
146 String name = ((ASTClassOrInterfaceType)child).getImage();
147 results.add(name);
148 }
149
150 if (child.jjtGetNumChildren() > 0) {
151 findExpressionNames(child, results);
152 }
153 }
154 }
155
156 private ASTPrimaryExpression findNullCompareExpression(ASTEqualityExpression equalityExpression) {
157 List<ASTPrimaryExpression> primaryExpressions = equalityExpression.findDescendantsOfType(ASTPrimaryExpression.class);
158 for (ASTPrimaryExpression primaryExpression: primaryExpressions) {
159 List<ASTPrimaryPrefix> primaryPrefixes = primaryExpression.findDescendantsOfType(ASTPrimaryPrefix.class);
160 for (ASTPrimaryPrefix primaryPrefix: primaryPrefixes) {
161 if (primaryPrefix.hasDescendantOfType(ASTName.class)) {
162
163 return primaryExpression;
164 }
165 }
166 }
167 return null;
168 }
169
170 }