1 /***
2 * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
3 */
4 package net.sourceforge.pmd.rules;
5
6 import net.sourceforge.pmd.RuleContext;
7 import net.sourceforge.pmd.ast.ASTAssignmentOperator;
8 import net.sourceforge.pmd.ast.ASTClassDeclaration;
9 import net.sourceforge.pmd.ast.ASTIfStatement;
10 import net.sourceforge.pmd.ast.ASTInterfaceDeclaration;
11 import net.sourceforge.pmd.ast.ASTLiteral;
12 import net.sourceforge.pmd.ast.ASTMethodDeclaration;
13 import net.sourceforge.pmd.ast.ASTName;
14 import net.sourceforge.pmd.ast.ASTNestedClassDeclaration;
15 import net.sourceforge.pmd.ast.ASTNestedInterfaceDeclaration;
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.ASTResultType;
20 import net.sourceforge.pmd.ast.ASTReturnStatement;
21 import net.sourceforge.pmd.ast.ASTStatementExpression;
22 import net.sourceforge.pmd.ast.ASTSynchronizedStatement;
23 import net.sourceforge.pmd.ast.ASTType;
24 import net.sourceforge.pmd.ast.Node;
25
26 import java.util.ArrayList;
27 import java.util.List;
28
29 /***
30 * void method() {
31 * if(x == null) {
32 * synchronized(this){
33 * if(x == null) {
34 * x = new | method();
35 * }
36 * }
37 * }
38 * 1. The error is when one uses the value assigned within a synchronized
39 * section, outside of a synchronized section.
40 * if(x == null) is outside of synchronized section
41 * x = new | method();
42 *
43 *
44 * Very very specific check for double checked locking.
45 *
46 * @author CL Gilbert (dnoyeb@users.sourceforge.net)
47 */
48 public class DoubleCheckedLockingRule extends net.sourceforge.pmd.AbstractRule {
49
50 private boolean interfaceSkipper;
51
52 public Object visit(ASTMethodDeclaration node, Object data) {
53 if (interfaceSkipper == true) {
54 return super.visit(node, data);
55 }
56 ASTResultType rt = (ASTResultType) node.jjtGetChild(0);
57 if (rt.isVoid() == true) {
58 return super.visit(node, data);
59 }
60 ASTType t = (ASTType) rt.jjtGetChild(0);
61 if (t.jjtGetNumChildren() == 0 || !(t.jjtGetChild(0) instanceof ASTName)) {
62 return super.visit(node, data);
63 }
64 String returnVariableName = null;
65 List finder = new ArrayList();
66 GET_RETURN_VARIABLE_NAME:{
67 node.findChildrenOfType(ASTReturnStatement.class, finder, true);
68 if (finder.size() != 1) {
69 return super.visit(node, data);
70 }
71 ASTReturnStatement rs = (ASTReturnStatement) finder.get(0);
72
73 finder.clear();
74 rs.findChildrenOfType(ASTPrimaryExpression.class, finder, true);
75 ASTPrimaryExpression ape = (ASTPrimaryExpression) finder.get(0);
76 Node lastChild = ape.jjtGetChild(ape.jjtGetNumChildren() - 1);
77 if (lastChild instanceof ASTPrimaryPrefix) {
78 returnVariableName = getNameFromPrimaryPrefix((ASTPrimaryPrefix) lastChild);
79 }
80 if (returnVariableName == null) {
81 return super.visit(node, data);
82 }
83 }
84 CHECK_OUTER_IF:{
85 finder.clear();
86 node.findChildrenOfType(ASTIfStatement.class, finder, true);
87 if (finder.size() == 2) {
88 ASTIfStatement is = (ASTIfStatement) finder.get(0);
89 if (ifVerify(is, returnVariableName)) {
90
91 finder.clear();
92 is.findChildrenOfType(ASTSynchronizedStatement.class, finder, true);
93 if (finder.size() == 1) {
94 ASTSynchronizedStatement ss = (ASTSynchronizedStatement) finder.get(0);
95 finder.clear();
96 ss.findChildrenOfType(ASTIfStatement.class, finder, true);
97 if (finder.size() == 1) {
98 ASTIfStatement is2 = (ASTIfStatement) finder.get(0);
99 if (ifVerify(is2, returnVariableName)) {
100 finder.clear();
101 is2.findChildrenOfType(ASTStatementExpression.class, finder, true);
102 if (finder.size() == 1) {
103 ASTStatementExpression se = (ASTStatementExpression) finder.get(0);
104 if (se.jjtGetNumChildren() == 3) {
105 if (se.jjtGetChild(0) instanceof ASTPrimaryExpression) {
106 ASTPrimaryExpression pe = (ASTPrimaryExpression) se.jjtGetChild(0);
107 if (matchName(pe, returnVariableName)) {
108 if (se.jjtGetChild(1) instanceof ASTAssignmentOperator) {
109 RuleContext ctx = (RuleContext) data;
110 ctx.getReport().addRuleViolation(createRuleViolation(ctx, node.getBeginLine()));
111 }
112 }
113 }
114 }
115 }
116 }
117 }
118 }
119 }
120 }
121 }
122 return super.visit(node, data);
123 }
124
125 private boolean ifVerify(ASTIfStatement is, String varname) {
126 List finder = new ArrayList();
127 is.findChildrenOfType(ASTPrimaryExpression.class, finder, true);
128 if (finder.size() > 1) {
129 ASTPrimaryExpression apeLeft = (ASTPrimaryExpression) finder.get(0);
130 if (matchName(apeLeft, varname)) {
131 ASTPrimaryExpression apeRight = (ASTPrimaryExpression) finder.get(1);
132 if ((apeRight.jjtGetNumChildren() == 1) && (apeRight.jjtGetChild(0) instanceof ASTPrimaryPrefix)) {
133 ASTPrimaryPrefix pp2 = (ASTPrimaryPrefix) apeRight.jjtGetChild(0);
134 if ((pp2.jjtGetNumChildren() == 1) && (pp2.jjtGetChild(0) instanceof ASTLiteral)) {
135 ASTLiteral lit = (ASTLiteral) pp2.jjtGetChild(0);
136 if ((lit.jjtGetNumChildren() == 1) && (lit.jjtGetChild(0) instanceof ASTNullLiteral)) {
137 return true;
138 }
139 }
140 }
141 }
142 }
143 return false;
144 }
145
146 public Object visit(ASTClassDeclaration node, Object data) {
147 boolean temp = interfaceSkipper;
148 interfaceSkipper = false;
149
150
151 Object o = super.visit(node, data);
152 interfaceSkipper = temp;
153 return o;
154 }
155
156 public Object visit(ASTNestedClassDeclaration node, Object data) {
157 boolean temp = interfaceSkipper;
158 interfaceSkipper = false;
159
160
161 Object o = super.visit(node, data);
162 interfaceSkipper = temp;
163 return o;
164 }
165
166 public Object visit(ASTInterfaceDeclaration node, Object data) {
167 boolean temp = interfaceSkipper;
168 interfaceSkipper = true;
169 Object o = super.visit(node, data);
170 interfaceSkipper = temp;
171 return o;
172 }
173
174 public Object visit(ASTNestedInterfaceDeclaration node, Object data) {
175 boolean temp = interfaceSkipper;
176 interfaceSkipper = true;
177 Object o = super.visit(node, data);
178 interfaceSkipper = temp;
179 return o;
180 }
181
182 public boolean matchName(ASTPrimaryExpression ape, String name) {
183 if ((ape.jjtGetNumChildren() == 1) && (ape.jjtGetChild(0) instanceof ASTPrimaryPrefix)) {
184 ASTPrimaryPrefix pp = (ASTPrimaryPrefix) ape.jjtGetChild(0);
185 String name2 = getNameFromPrimaryPrefix(pp);
186 if (name2 != null && name2.equals(name)) {
187 return true;
188 }
189 }
190 return false;
191 }
192
193 public String getNameFromPrimaryPrefix(ASTPrimaryPrefix pp) {
194 if ((pp.jjtGetNumChildren() == 1) && (pp.jjtGetChild(0) instanceof ASTName)) {
195 String name2 = ((ASTName) pp.jjtGetChild(0)).getImage();
196 return name2;
197 }
198 return null;
199 }
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221 }