/** * BSD-style license; for more info see http://pmd.sourceforge.net/license.html */ package net.sourceforge.pmd.lang.java.rule.unusedcode; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import net.sourceforge.pmd.lang.ast.Node; import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceDeclaration; import net.sourceforge.pmd.lang.java.ast.ASTConstructorDeclaration; import net.sourceforge.pmd.lang.java.ast.ASTInitializer; import net.sourceforge.pmd.lang.java.ast.ASTMethodDeclaration; import net.sourceforge.pmd.lang.java.ast.ASTMethodDeclarator; import net.sourceforge.pmd.lang.java.ast.AccessNode; import net.sourceforge.pmd.lang.java.rule.AbstractJavaRule; import net.sourceforge.pmd.lang.java.symboltable.ClassScope; import net.sourceforge.pmd.lang.java.symboltable.MethodNameDeclaration; import net.sourceforge.pmd.lang.symboltable.NameDeclaration; import net.sourceforge.pmd.lang.symboltable.NameOccurrence; /** * This rule detects private methods, that are not used and can therefore be * deleted. */ public class UnusedPrivateMethodRule extends AbstractJavaRule { /** * Visit each method declaration. * * @param node * the method declaration * @param data * data - rule context * @return data */ public Object visit(ASTClassOrInterfaceDeclaration node, Object data) { if (node.isInterface()) { return data; } Map<MethodNameDeclaration, List<NameOccurrence>> methods = node.getScope().getEnclosingScope(ClassScope.class) .getMethodDeclarations(); for (MethodNameDeclaration mnd : findUnique(methods)) { List<NameOccurrence> occs = methods.get(mnd); if (!privateAndNotExcluded(mnd)) { continue; } if (occs.isEmpty()) { addViolation(data, mnd.getNode(), mnd.getImage() + mnd.getParameterDisplaySignature()); } else { if (calledFromOutsideItself(occs, mnd)) { addViolation(data, mnd.getNode(), mnd.getImage() + mnd.getParameterDisplaySignature()); } } } return data; } private Set<MethodNameDeclaration> findUnique(Map<MethodNameDeclaration, List<NameOccurrence>> methods) { // some rather hideous hackery here // to work around the fact that PMD does not yet do full type analysis // when it does, delete this Set<MethodNameDeclaration> unique = new HashSet<>(); Set<String> sigs = new HashSet<>(); for (MethodNameDeclaration mnd : methods.keySet()) { String sig = mnd.getImage() + mnd.getParameterCount() + mnd.isVarargs(); if (!sigs.contains(sig)) { unique.add(mnd); } sigs.add(sig); } return unique; } private boolean calledFromOutsideItself(List<NameOccurrence> occs, NameDeclaration mnd) { int callsFromOutsideMethod = 0; for (NameOccurrence occ : occs) { Node occNode = occ.getLocation(); ASTConstructorDeclaration enclosingConstructor = occNode .getFirstParentOfType(ASTConstructorDeclaration.class); if (enclosingConstructor != null) { callsFromOutsideMethod++; break; // Do we miss unused private constructors here? } ASTInitializer enclosingInitializer = occNode.getFirstParentOfType(ASTInitializer.class); if (enclosingInitializer != null) { callsFromOutsideMethod++; break; } ASTMethodDeclaration enclosingMethod = occNode.getFirstParentOfType(ASTMethodDeclaration.class); if (enclosingMethod == null || !mnd.getNode().jjtGetParent().equals(enclosingMethod)) { callsFromOutsideMethod++; } } return callsFromOutsideMethod == 0; } private boolean privateAndNotExcluded(NameDeclaration mnd) { ASTMethodDeclarator node = (ASTMethodDeclarator) mnd.getNode(); return ((AccessNode) node.jjtGetParent()).isPrivate() && !node.hasImageEqualTo("readObject") && !node.hasImageEqualTo("writeObject") && !node.hasImageEqualTo("readResolve") && !node.hasImageEqualTo("writeReplace"); } }