View Javadoc

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);//EXPLODES IF THE CLASS IS AN INTERFACE SINCE NO RETURN
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                      //find synchronized
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) { //primaryExpression, AssignmentOperator, Expression
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 }