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.util.HashSet;
7   import java.util.List;
8   import java.util.Map;
9   import java.util.Set;
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.ASTInitializer;
15  import net.sourceforge.pmd.lang.java.ast.ASTMethodDeclaration;
16  import net.sourceforge.pmd.lang.java.ast.ASTMethodDeclarator;
17  import net.sourceforge.pmd.lang.java.ast.AccessNode;
18  import net.sourceforge.pmd.lang.java.rule.AbstractJavaRule;
19  import net.sourceforge.pmd.lang.java.symboltable.ClassScope;
20  import net.sourceforge.pmd.lang.java.symboltable.MethodNameDeclaration;
21  import net.sourceforge.pmd.lang.symboltable.NameDeclaration;
22  import net.sourceforge.pmd.lang.symboltable.NameOccurrence;
23  
24  /**
25   * This rule detects private methods, that are not used and can therefore be deleted.
26   */
27  public class UnusedPrivateMethodRule extends AbstractJavaRule {
28  
29  
30      /**
31       * Visit each method declaration.
32       * @param node the method declaration
33       * @param data data - rule context
34       * @return data
35       */
36      public Object visit(ASTClassOrInterfaceDeclaration node, Object data) {
37          if (node.isInterface()) {
38              return data;
39          }
40  
41          Map<MethodNameDeclaration, List<NameOccurrence>> methods = node.getScope().getEnclosingScope(ClassScope.class).getMethodDeclarations();
42          for (MethodNameDeclaration mnd: findUnique(methods)) {
43              List<NameOccurrence> occs = methods.get(mnd);
44              if (!privateAndNotExcluded(mnd)) {
45                  continue;
46              }
47              if (occs.isEmpty()) {
48                  addViolation(data, mnd.getNode(), mnd.getImage() + mnd.getParameterDisplaySignature());
49              } else {
50                  if (calledFromOutsideItself(occs, mnd)) {
51                      addViolation(data, mnd.getNode(), mnd.getImage() + mnd.getParameterDisplaySignature());
52                  }
53  
54              }
55          }
56          return data;
57      }
58  
59      private Set<MethodNameDeclaration> findUnique(Map<MethodNameDeclaration, List<NameOccurrence>> methods) {
60          // some rather hideous hackery here
61          // to work around the fact that PMD does not yet do full type analysis
62          // when it does, delete this
63          Set<MethodNameDeclaration> unique = new HashSet<>();
64          Set<String> sigs = new HashSet<>();
65          for (MethodNameDeclaration mnd: methods.keySet()) {
66              String sig = mnd.getImage() + mnd.getParameterCount() + mnd.isVarargs();
67              if (!sigs.contains(sig)) {
68                  unique.add(mnd);
69              }
70              sigs.add(sig);
71          }
72          return unique;
73      }
74  
75      private boolean calledFromOutsideItself(List<NameOccurrence> occs, NameDeclaration mnd) {
76          int callsFromOutsideMethod = 0;
77          for (NameOccurrence occ: occs) {
78              Node occNode = occ.getLocation();
79              ASTConstructorDeclaration enclosingConstructor = occNode.getFirstParentOfType(ASTConstructorDeclaration.class);
80              if (enclosingConstructor != null) {
81                  callsFromOutsideMethod++;
82                  break; // Do we miss unused private constructors here?
83              }
84              ASTInitializer enclosingInitializer = occNode.getFirstParentOfType(ASTInitializer.class);
85              if (enclosingInitializer != null) {
86                  callsFromOutsideMethod++;
87                  break;
88              }
89  
90              ASTMethodDeclaration enclosingMethod = occNode.getFirstParentOfType(ASTMethodDeclaration.class);
91              if (enclosingMethod == null || !mnd.getNode().jjtGetParent().equals(enclosingMethod)) {
92                  callsFromOutsideMethod++;
93              }
94          }
95          return callsFromOutsideMethod == 0;
96      }
97  
98      private boolean privateAndNotExcluded(NameDeclaration mnd) {
99          ASTMethodDeclarator node = (ASTMethodDeclarator) mnd.getNode();
100         return ((AccessNode) node.jjtGetParent()).isPrivate() && !node.hasImageEqualTo("readObject") && !node.hasImageEqualTo("writeObject") && !node.hasImageEqualTo("readResolve") && !node.hasImageEqualTo("writeReplace");
101     }
102 }