View Javadoc
1   /**
2    * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
3    */
4   package net.sourceforge.pmd.lang.java.rule.sunsecure;
5   
6   import java.util.List;
7   
8   import org.jaxen.JaxenException;
9   
10  import net.sourceforge.pmd.lang.ast.Node;
11  import net.sourceforge.pmd.lang.java.ast.ASTAllocationExpression;
12  import net.sourceforge.pmd.lang.java.ast.ASTArrayInitializer;
13  import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceDeclaration;
14  import net.sourceforge.pmd.lang.java.ast.ASTExpression;
15  import net.sourceforge.pmd.lang.java.ast.ASTFieldDeclaration;
16  import net.sourceforge.pmd.lang.java.ast.ASTMethodDeclaration;
17  import net.sourceforge.pmd.lang.java.ast.ASTName;
18  import net.sourceforge.pmd.lang.java.ast.ASTPrimaryExpression;
19  import net.sourceforge.pmd.lang.java.ast.ASTPrimaryPrefix;
20  import net.sourceforge.pmd.lang.java.ast.ASTPrimarySuffix;
21  import net.sourceforge.pmd.lang.java.ast.ASTReturnStatement;
22  import net.sourceforge.pmd.lang.java.ast.ASTTypeDeclaration;
23  import net.sourceforge.pmd.lang.java.ast.ASTVariableDeclaratorId;
24  import net.sourceforge.pmd.lang.java.ast.ASTVariableInitializer;
25  
26  /**
27   * Implementation note: this rule currently ignores return types of y.x.z,
28   * currently it handles only local type fields.
29   * Created on Jan 17, 2005
30   *
31   * @author mgriffa
32   */
33  public class MethodReturnsInternalArrayRule extends AbstractSunSecureRule {
34  
35      @Override
36      public Object visit(ASTClassOrInterfaceDeclaration node, Object data) {
37          if (node.isInterface()) {
38              return data;
39          }
40          return super.visit(node, data);
41      }
42  
43      @Override
44      public Object visit(ASTMethodDeclaration method, Object data) {
45          if (!method.getResultType().returnsArray() || method.isPrivate()) {
46              return data;
47          }
48          List<ASTReturnStatement> returns = method.findDescendantsOfType(ASTReturnStatement.class);
49          ASTTypeDeclaration td = method.getFirstParentOfType(ASTTypeDeclaration.class);
50          for (ASTReturnStatement ret: returns) {
51              final String vn = getReturnedVariableName(ret);
52              if (!isField(vn, td)) {
53                  continue;
54              }
55              if (ret.findDescendantsOfType(ASTPrimarySuffix.class).size() > 2) {
56                  continue;
57              }
58              if (ret.hasDescendantOfType(ASTAllocationExpression.class)) {
59                  continue;
60              }
61              if (hasArraysCopyOf(ret)) {
62                  continue;
63              }
64              if (hasClone(ret, vn)) {
65                  continue;
66              }
67              if (isEmptyArray(vn, td)) {
68                  continue;
69              }
70              if (!isLocalVariable(vn, method)) {
71                  addViolation(data, ret, vn);
72              } else {
73                  // This is to handle field hiding
74                  final ASTPrimaryPrefix pp = ret.getFirstDescendantOfType(ASTPrimaryPrefix.class);
75                  if (pp != null && pp.usesThisModifier()) {
76                      final ASTPrimarySuffix ps = ret.getFirstDescendantOfType(ASTPrimarySuffix.class);
77                      if (ps.hasImageEqualTo(vn)) {
78                          addViolation(data, ret, vn);
79                      }
80                  }
81              }
82          }
83          return data;
84      }
85  
86      private boolean hasClone(ASTReturnStatement ret, String varName) {
87          List<ASTPrimaryExpression> expressions = ret.findDescendantsOfType(ASTPrimaryExpression.class);
88          for (ASTPrimaryExpression e : expressions) {
89              if (e.jjtGetChild(0) instanceof ASTPrimaryPrefix
90                      && e.jjtGetNumChildren() == 2
91                      && e.jjtGetChild(1) instanceof ASTPrimarySuffix
92                      && ((ASTPrimarySuffix) e.jjtGetChild(1)).isArguments()
93                      && ((ASTPrimarySuffix) e.jjtGetChild(1)).getArgumentCount() == 0) {
94                  ASTName name = e.getFirstDescendantOfType(ASTName.class);
95                  if (name != null && name.hasImageEqualTo(varName + ".clone")) {
96                      return true;
97                  }
98              }
99          }
100         return false;
101     }
102 
103     private boolean hasArraysCopyOf(ASTReturnStatement ret) {
104         List<ASTPrimaryExpression> expressions = ret.findDescendantsOfType(ASTPrimaryExpression.class);
105         for (ASTPrimaryExpression e : expressions) {
106             if (e.jjtGetNumChildren() == 2 && e.jjtGetChild(0) instanceof ASTPrimaryPrefix
107                     && e.jjtGetChild(0).jjtGetNumChildren() == 1 && e.jjtGetChild(0).jjtGetChild(0) instanceof ASTName
108                     && e.jjtGetChild(0).jjtGetChild(0).getImage().endsWith("Arrays.copyOf")) {
109                 return true;
110             }
111         }
112         return false;
113     }
114 
115     private boolean isEmptyArray(String varName, ASTTypeDeclaration typeDeclaration) {
116         final List<ASTFieldDeclaration> fds = typeDeclaration.findDescendantsOfType(ASTFieldDeclaration.class);
117         if (fds != null) {
118             for (ASTFieldDeclaration fd : fds) {
119                 final ASTVariableDeclaratorId vid = fd.getFirstDescendantOfType(ASTVariableDeclaratorId.class);
120                 if (vid != null && vid.hasImageEqualTo(varName)) {
121                     ASTVariableInitializer initializer = fd.getFirstDescendantOfType(ASTVariableInitializer.class);
122                     if (initializer != null && initializer.jjtGetNumChildren() == 1) {
123                         Node child = initializer.jjtGetChild(0);
124                         if (child instanceof ASTArrayInitializer && child.jjtGetNumChildren() == 0) {
125                             return true;
126                         } else if (child instanceof ASTExpression) {
127                             try {
128                                 List<? extends Node> arrayAllocation = child.findChildNodesWithXPath("./PrimaryExpression/PrimaryPrefix/AllocationExpression/ArrayDimsAndInits/Expression/PrimaryExpression/PrimaryPrefix/Literal[@IntLiteral=\"true\"][@Image=\"0\"]");
129                                 if (arrayAllocation != null && arrayAllocation.size() == 1) {
130                                     return true;
131                                 }
132                             } catch (JaxenException e) {
133                                 return false;
134                             }
135                         }
136                     }
137                 }
138             }
139         }
140         return false;
141     }
142 }