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