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.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) {//skip methods in interfaces
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);//EXPLODES IF THE CLASS IS AN INTERFACE SINCE NO RETURN
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                      //find synchronized
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) { //primaryExpression, AssignmentOperator, Expression
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         //		String className = ((ASTUnmodifiedClassDeclaration)node.jjtGetChild(0)).getImage();
150         //		System.out.println("classname = " + className);
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         //		String className = ((ASTUnmodifiedNestedClassDeclaration)node.jjtGetChild(0)).getImage();
160         //		System.out.println("classname = " + className);
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     //Testing Section
201     //    public Object visit(ASTCompilationUnit node, Object data) {
202     //		interfaceSkipper = false; //safety
203     //		try {
204     //			return super.visit(node,data);
205     //		}
206     //		catch(Exception e){
207     //			e.printStackTrace();
208     //			throw new RuntimeException(e.getMessage());
209     //		}
210     //    }
211     //	public Object visit(ASTMethodDeclarator node, Object data) {
212     //		System.out.println("method = " + node.getImage());
213     //		return super.visit(node,data);
214     //	}
215     //
216     //	public Object visit(ASTPackageDeclaration node, Object data){
217     //		String name = ((ASTName)node.jjtGetChild(0)).getImage();
218     //		System.out.println("package = " + name);
219     //		return super.visit(node, data);
220     //	}
221 }