/** * BSD-style license; for more info see http://pmd.sourceforge.net/license.html */ package net.sourceforge.pmd.lang.java.rule.unusedcode; import java.util.ArrayList; import java.util.List; import java.util.Map; import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceBody; import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceBodyDeclaration; import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceDeclaration; import net.sourceforge.pmd.lang.java.ast.ASTEnumBody; import net.sourceforge.pmd.lang.java.ast.ASTEnumConstant; import net.sourceforge.pmd.lang.java.ast.ASTEnumDeclaration; import net.sourceforge.pmd.lang.java.ast.ASTName; import net.sourceforge.pmd.lang.java.ast.ASTPrimaryPrefix; import net.sourceforge.pmd.lang.java.ast.ASTPrimarySuffix; import net.sourceforge.pmd.lang.java.ast.AccessNode; import net.sourceforge.pmd.lang.java.ast.JavaNode; import net.sourceforge.pmd.lang.java.rule.AbstractLombokAwareRule; import net.sourceforge.pmd.lang.java.symboltable.JavaNameOccurrence; import net.sourceforge.pmd.lang.java.symboltable.VariableNameDeclaration; import net.sourceforge.pmd.lang.symboltable.NameDeclaration; import net.sourceforge.pmd.lang.symboltable.NameOccurrence; public class UnusedPrivateFieldRule extends AbstractLombokAwareRule { @Override public Object visit(ASTClassOrInterfaceDeclaration node, Object data) { boolean classHasLombok = hasLombokAnnotation(node); Map<VariableNameDeclaration, List<NameOccurrence>> vars = node.getScope() .getDeclarations(VariableNameDeclaration.class); for (Map.Entry<VariableNameDeclaration, List<NameOccurrence>> entry : vars.entrySet()) { VariableNameDeclaration decl = entry.getKey(); AccessNode accessNodeParent = decl.getAccessNodeParent(); if (!accessNodeParent.isPrivate() || isOK(decl.getImage()) || classHasLombok || hasLombokAnnotation(accessNodeParent)) { continue; } if (!actuallyUsed(entry.getValue())) { if (!usedInOuterClass(node, decl) && !usedInOuterEnum(node, decl)) { addViolation(data, decl.getNode(), decl.getImage()); } } } return super.visit(node, data); } private boolean usedInOuterEnum(ASTClassOrInterfaceDeclaration node, NameDeclaration decl) { List<ASTEnumDeclaration> outerEnums = node.getParentsOfType(ASTEnumDeclaration.class); for (ASTEnumDeclaration outerEnum : outerEnums) { ASTEnumBody enumBody = outerEnum.getFirstChildOfType(ASTEnumBody.class); if (usedInOuter(decl, enumBody)) { return true; } } return false; } /** * Find out whether the variable is used in an outer class */ private boolean usedInOuterClass(ASTClassOrInterfaceDeclaration node, NameDeclaration decl) { List<ASTClassOrInterfaceDeclaration> outerClasses = node.getParentsOfType(ASTClassOrInterfaceDeclaration.class); for (ASTClassOrInterfaceDeclaration outerClass : outerClasses) { ASTClassOrInterfaceBody classOrInterfaceBody = outerClass .getFirstChildOfType(ASTClassOrInterfaceBody.class); if (usedInOuter(decl, classOrInterfaceBody)) { return true; } } return false; } private boolean usedInOuter(NameDeclaration decl, JavaNode body) { List<ASTClassOrInterfaceBodyDeclaration> classOrInterfaceBodyDeclarations = body .findChildrenOfType(ASTClassOrInterfaceBodyDeclaration.class); List<ASTEnumConstant> enumConstants = body.findChildrenOfType(ASTEnumConstant.class); List<JavaNode> nodes = new ArrayList<>(); nodes.addAll(classOrInterfaceBodyDeclarations); nodes.addAll(enumConstants); for (JavaNode node : nodes) { List<ASTPrimarySuffix> primarySuffixes = node.findDescendantsOfType(ASTPrimarySuffix.class); for (ASTPrimarySuffix primarySuffix : primarySuffixes) { if (decl.getImage().equals(primarySuffix.getImage())) { return true; // No violation } } List<ASTPrimaryPrefix> primaryPrefixes = node.findDescendantsOfType(ASTPrimaryPrefix.class); for (ASTPrimaryPrefix primaryPrefix : primaryPrefixes) { ASTName name = primaryPrefix.getFirstDescendantOfType(ASTName.class); if (name != null) { for (String id : name.getImage().split("\\.")) { if (id.equals(decl.getImage())) { return true; // No violation } } } } } return false; } private boolean actuallyUsed(List<NameOccurrence> usages) { for (NameOccurrence nameOccurrence : usages) { JavaNameOccurrence jNameOccurrence = (JavaNameOccurrence) nameOccurrence; if (!jNameOccurrence.isOnLeftHandSide()) { return true; } } return false; } private boolean isOK(String image) { return "serialVersionUID".equals(image) || "serialPersistentFields".equals(image) || "IDENT".equals(image); } }