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.ASTClassOrInterfaceDeclaration;
9 import net.sourceforge.pmd.ast.ASTIfStatement;
10 import net.sourceforge.pmd.ast.ASTLiteral;
11 import net.sourceforge.pmd.ast.ASTMethodDeclaration;
12 import net.sourceforge.pmd.ast.ASTName;
13 import net.sourceforge.pmd.ast.ASTNullLiteral;
14 import net.sourceforge.pmd.ast.ASTPrimaryExpression;
15 import net.sourceforge.pmd.ast.ASTPrimaryPrefix;
16 import net.sourceforge.pmd.ast.ASTReferenceType;
17 import net.sourceforge.pmd.ast.ASTResultType;
18 import net.sourceforge.pmd.ast.ASTReturnStatement;
19 import net.sourceforge.pmd.ast.ASTStatementExpression;
20 import net.sourceforge.pmd.ast.ASTSynchronizedStatement;
21 import net.sourceforge.pmd.ast.ASTType;
22 import net.sourceforge.pmd.ast.Node;
23
24 import java.util.ArrayList;
25 import java.util.List;
26
27 /***
28 * void method() {
29 * if(x == null) {
30 * synchronized(this){
31 * if(x == null) {
32 * x = new | method();
33 * }
34 * }
35 * }
36 * 1. The error is when one uses the value assigned within a synchronized
37 * section, outside of a synchronized section.
38 * if(x == null) is outside of synchronized section
39 * x = new | method();
40 * <p/>
41 * <p/>
42 * Very very specific check for double checked locking.
43 *
44 * @author CL Gilbert (dnoyeb@users.sourceforge.net)
45 */
46 public class DoubleCheckedLocking extends net.sourceforge.pmd.AbstractRule {
47
48 private boolean insideInterface;
49
50 public Object visit(ASTMethodDeclaration node, Object data) {
51 if (insideInterface) {
52 return super.visit(node, data);
53 }
54
55 ASTResultType rt = (ASTResultType)node.jjtGetChild(0);
56 if (rt.isVoid()) {
57 return super.visit(node, data);
58 }
59
60 ASTType typeNode = (ASTType) rt.jjtGetChild(0);
61 if (typeNode.jjtGetNumChildren() == 0 || !(typeNode.jjtGetChild(0) instanceof ASTReferenceType)) {
62 return super.visit(node, data);
63 }
64
65 String returnVariableName = null;
66 List finder = new ArrayList();
67 GET_RETURN_VARIABLE_NAME:{
68 node.findChildrenOfType(ASTReturnStatement.class, finder, true);
69 if (finder.size() != 1) {
70 return super.visit(node, data);
71 }
72 ASTReturnStatement rs = (ASTReturnStatement) finder.get(0);
73
74 finder.clear();
75 rs.findChildrenOfType(ASTPrimaryExpression.class, finder, true);
76 ASTPrimaryExpression ape = (ASTPrimaryExpression) finder.get(0);
77 Node lastChild = ape.jjtGetChild(ape.jjtGetNumChildren() - 1);
78 if (lastChild instanceof ASTPrimaryPrefix) {
79 returnVariableName = getNameFromPrimaryPrefix((ASTPrimaryPrefix) lastChild);
80 }
81 if (returnVariableName == null) {
82 return super.visit(node, data);
83 }
84 }
85 CHECK_OUTER_IF:{
86 finder.clear();
87 node.findChildrenOfType(ASTIfStatement.class, finder, true);
88 if (finder.size() == 2) {
89 ASTIfStatement is = (ASTIfStatement) finder.get(0);
90 if (ifVerify(is, returnVariableName)) {
91
92 finder.clear();
93 is.findChildrenOfType(ASTSynchronizedStatement.class, finder, true);
94 if (finder.size() == 1) {
95 ASTSynchronizedStatement ss = (ASTSynchronizedStatement) finder.get(0);
96 finder.clear();
97 ss.findChildrenOfType(ASTIfStatement.class, finder, true);
98 if (finder.size() == 1) {
99 ASTIfStatement is2 = (ASTIfStatement) finder.get(0);
100 if (ifVerify(is2, returnVariableName)) {
101 finder.clear();
102 is2.findChildrenOfType(ASTStatementExpression.class, finder, true);
103 if (finder.size() == 1) {
104 ASTStatementExpression se = (ASTStatementExpression) finder.get(0);
105 if (se.jjtGetNumChildren() == 3) {
106 if (se.jjtGetChild(0) instanceof ASTPrimaryExpression) {
107 ASTPrimaryExpression pe = (ASTPrimaryExpression) se.jjtGetChild(0);
108 if (matchName(pe, returnVariableName)) {
109 if (se.jjtGetChild(1) instanceof ASTAssignmentOperator) {
110 RuleContext ctx = (RuleContext) data;
111 ctx.getReport().addRuleViolation(createRuleViolation(ctx, node));
112 }
113 }
114 }
115 }
116 }
117 }
118 }
119 }
120 }
121 }
122 }
123 return super.visit(node, data);
124 }
125
126 private boolean ifVerify(ASTIfStatement is, String varname) {
127 List finder = new ArrayList();
128 is.findChildrenOfType(ASTPrimaryExpression.class, finder, true);
129 if (finder.size() > 1) {
130 ASTPrimaryExpression apeLeft = (ASTPrimaryExpression) finder.get(0);
131 if (matchName(apeLeft, varname)) {
132 ASTPrimaryExpression apeRight = (ASTPrimaryExpression) finder.get(1);
133 if ((apeRight.jjtGetNumChildren() == 1) && (apeRight.jjtGetChild(0) instanceof ASTPrimaryPrefix)) {
134 ASTPrimaryPrefix pp2 = (ASTPrimaryPrefix) apeRight.jjtGetChild(0);
135 if ((pp2.jjtGetNumChildren() == 1) && (pp2.jjtGetChild(0) instanceof ASTLiteral)) {
136 ASTLiteral lit = (ASTLiteral) pp2.jjtGetChild(0);
137 if ((lit.jjtGetNumChildren() == 1) && (lit.jjtGetChild(0) instanceof ASTNullLiteral)) {
138 return true;
139 }
140 }
141 }
142 }
143 }
144 return false;
145 }
146
147 public Object visit(ASTClassOrInterfaceDeclaration node, Object data) {
148 boolean temp = insideInterface;
149 insideInterface = node.isInterface();
150 Object o = super.visit(node, data);
151 insideInterface = temp;
152 return o;
153 }
154
155 public boolean matchName(ASTPrimaryExpression ape, String name) {
156 if ((ape.jjtGetNumChildren() == 1) && (ape.jjtGetChild(0) instanceof ASTPrimaryPrefix)) {
157 ASTPrimaryPrefix pp = (ASTPrimaryPrefix) ape.jjtGetChild(0);
158 String name2 = getNameFromPrimaryPrefix(pp);
159 if (name2 != null && name2.equals(name)) {
160 return true;
161 }
162 }
163 return false;
164 }
165
166 public String getNameFromPrimaryPrefix(ASTPrimaryPrefix pp) {
167 if ((pp.jjtGetNumChildren() == 1) && (pp.jjtGetChild(0) instanceof ASTName)) {
168 return ((ASTName) pp.jjtGetChild(0)).getImage();
169 }
170 return null;
171 }
172 }