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