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.unusedcode;
5   
6   import java.io.InvalidObjectException;
7   import java.io.ObjectInputStream;
8   import java.util.List;
9   import java.util.Map;
10  
11  import net.sourceforge.pmd.lang.ast.Node;
12  import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceDeclaration;
13  import net.sourceforge.pmd.lang.java.ast.ASTConstructorDeclaration;
14  import net.sourceforge.pmd.lang.java.ast.ASTFormalParameter;
15  import net.sourceforge.pmd.lang.java.ast.ASTMarkerAnnotation;
16  import net.sourceforge.pmd.lang.java.ast.ASTMethodDeclaration;
17  import net.sourceforge.pmd.lang.java.ast.ASTMethodDeclarator;
18  import net.sourceforge.pmd.lang.java.ast.ASTName;
19  import net.sourceforge.pmd.lang.java.ast.ASTNameList;
20  import net.sourceforge.pmd.lang.java.ast.ASTType;
21  import net.sourceforge.pmd.lang.java.ast.JavaNode;
22  import net.sourceforge.pmd.lang.java.rule.AbstractJavaRule;
23  import net.sourceforge.pmd.lang.java.symboltable.JavaNameOccurrence;
24  import net.sourceforge.pmd.lang.java.symboltable.VariableNameDeclaration;
25  import net.sourceforge.pmd.lang.rule.properties.BooleanProperty;
26  import net.sourceforge.pmd.lang.symboltable.NameOccurrence;
27  
28  public class UnusedFormalParameterRule extends AbstractJavaRule {
29  
30      private static final BooleanProperty CHECKALL_DESCRIPTOR = new BooleanProperty("checkAll",
31              "Check all methods, including non-private ones", false, 1.0f);
32  
33      public UnusedFormalParameterRule() {
34          definePropertyDescriptor(CHECKALL_DESCRIPTOR);
35      }
36  
37      public Object visit(ASTConstructorDeclaration node, Object data) {
38          check(node, data);
39          return data;
40      }
41  
42      public Object visit(ASTMethodDeclaration node, Object data) {
43          if (!node.isPrivate() && !getProperty(CHECKALL_DESCRIPTOR)) {
44              return data;
45          }
46          if (!node.isNative() && !node.isAbstract() && !isSerializationMethod(node) && !hasOverrideAnnotation(node)) {
47              check(node, data);
48          }
49          return data;
50      }
51  
52      private boolean isSerializationMethod(ASTMethodDeclaration node) {
53          ASTMethodDeclarator declarator = node.getFirstDescendantOfType(ASTMethodDeclarator.class);
54          List<ASTFormalParameter> parameters = declarator.findDescendantsOfType(ASTFormalParameter.class);
55          if (node.isPrivate()
56              && "readObject".equals(node.getMethodName())
57              && parameters.size() == 1
58              && throwsOneException(node, InvalidObjectException.class)) {
59              ASTType type = parameters.get(0).getTypeNode();
60              if (type.getType() == ObjectInputStream.class
61                      || ObjectInputStream.class.getSimpleName().equals(type.getTypeImage())
62                      || ObjectInputStream.class.getName().equals(type.getTypeImage())) {
63                  return true;
64              }
65          }
66          return false;
67      }
68  
69      private boolean throwsOneException(ASTMethodDeclaration node, Class<? extends Throwable> exception) {
70          ASTNameList throwsList = node.getThrows();
71          if (throwsList != null && throwsList.jjtGetNumChildren() == 1) {
72              ASTName n = (ASTName)throwsList.jjtGetChild(0);
73              if (n.getType() == exception
74                  || exception.getSimpleName().equals(n.getImage())
75                  || exception.getName().equals(n.getImage())) {
76                  return true;
77              }
78          }
79          return false;
80      }
81  
82      private void check(Node node, Object data) {
83          Node parent = node.jjtGetParent().jjtGetParent().jjtGetParent();
84          if (parent instanceof ASTClassOrInterfaceDeclaration
85                  && !((ASTClassOrInterfaceDeclaration) parent).isInterface()) {
86              Map<VariableNameDeclaration, List<NameOccurrence>> vars = ((JavaNode) node).getScope().getDeclarations(
87                      VariableNameDeclaration.class);
88              for (Map.Entry<VariableNameDeclaration, List<NameOccurrence>> entry : vars.entrySet()) {
89                  VariableNameDeclaration nameDecl = entry.getKey();
90                  if (actuallyUsed(nameDecl, entry.getValue())) {
91                      continue;
92                  }
93                  addViolation(data, nameDecl.getNode(), new Object[] {
94                          node instanceof ASTMethodDeclaration ? "method" : "constructor", nameDecl.getImage() });
95              }
96          }
97      }
98  
99      private boolean actuallyUsed(VariableNameDeclaration nameDecl, List<NameOccurrence> usages) {
100         for (NameOccurrence occ : usages) {
101             JavaNameOccurrence jocc = (JavaNameOccurrence) occ;
102             if (jocc.isOnLeftHandSide()) {
103                 if (nameDecl.isArray() && jocc.getLocation().jjtGetParent().jjtGetParent().jjtGetNumChildren() > 1) {
104                     // array element access
105                     return true;
106                 }
107                 continue;
108             } else {
109                 return true;
110             }
111         }
112         return false;
113     }
114 
115     private boolean hasOverrideAnnotation(ASTMethodDeclaration node) {
116         int childIndex = node.jjtGetChildIndex();
117         for (int i = 0; i < childIndex; i++) {
118             Node previousSibling = node.jjtGetParent().jjtGetChild(i);
119             List<ASTMarkerAnnotation> annotations = previousSibling.findDescendantsOfType(ASTMarkerAnnotation.class);
120             for (ASTMarkerAnnotation annotation : annotations) {
121                 ASTName name = annotation.getFirstChildOfType( ASTName.class );
122                 if (name != null && (name.hasImageEqualTo( "Override" ) || name.hasImageEqualTo( "java.lang.Override" ))) {
123                     return true;
124                 }
125             }
126         }
127         return false;
128     }
129 }