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