#1276 False positive in UnusedPrivateMethod with inner enum

This commit is contained in:
Andreas Dangel
2014-11-15 12:55:15 +01:00
parent a5bc2a079f
commit a8d7b594ab
6 changed files with 95 additions and 6 deletions

View File

@ -5,6 +5,7 @@ package net.sourceforge.pmd.lang.java.symboltable;
import net.sourceforge.pmd.lang.ast.Node;
import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceDeclaration;
import net.sourceforge.pmd.lang.java.ast.ASTEnumDeclaration;
import net.sourceforge.pmd.lang.symboltable.AbstractNameDeclaration;
public class ClassNameDeclaration extends AbstractNameDeclaration implements TypedNameDeclaration {
@ -13,8 +14,20 @@ public class ClassNameDeclaration extends AbstractNameDeclaration implements Typ
super(node);
}
public ClassNameDeclaration(ASTEnumDeclaration node) {
super(node);
}
public String toString() {
return "Class " + node.getImage();
if (node instanceof ASTClassOrInterfaceDeclaration) {
if (((ASTClassOrInterfaceDeclaration) node).isInterface()) {
return "Interface " + node.getImage();
} else {
return "Class " + node.getImage();
}
} else {
return "Enum " + node.getImage();
}
}
public Node getAccessNodeParent() {

View File

@ -221,15 +221,33 @@ public class ClassScope extends AbstractJavaScope {
List<TypedNameDeclaration> parameterTypes = new ArrayList<TypedNameDeclaration>();
List<ASTFormalParameter> parameters = mnd.getMethodNameDeclaratorNode().findDescendantsOfType(ASTFormalParameter.class);
for (ASTFormalParameter p : parameters) {
Class<?> resolvedType = this.getEnclosingScope(SourceFileScope.class).resolveType(p.getTypeNode().getTypeImage());
String typeImage = p.getTypeNode().getTypeImage();
// typeImage might be qualified/unqualified. If it refers to a type, defined in the same toplevel class,
// we should normalize the name here.
typeImage = qualifyTypeName(typeImage);
Class<?> resolvedType = this.getEnclosingScope(SourceFileScope.class).resolveType(typeImage);
if (resolvedType == null) {
resolvedType = resolveGenericType(p, p.getTypeNode().getTypeImage());
resolvedType = resolveGenericType(p, typeImage);
}
parameterTypes.add(new SimpleTypedNameDeclaration(p.getTypeNode().getTypeImage(), resolvedType));
parameterTypes.add(new SimpleTypedNameDeclaration(typeImage, resolvedType));
}
return parameterTypes;
}
private String qualifyTypeName(String typeImage) {
String result = typeImage;
for (String qualified : this.getEnclosingScope(SourceFileScope.class).getQualifiedTypeNames()) {
int fullLength = qualified.length();
int nameLength = typeImage.length();
if (qualified.endsWith(typeImage)
&& (fullLength == nameLength || qualified.substring(0, fullLength - nameLength).endsWith("."))) {
result = qualified;
break;
}
}
return result;
}
/**
* Provide a list of types of the arguments of the given method call.
* The types are simple type images. If the argument type cannot be determined (e.g. because it is itself
@ -274,8 +292,10 @@ public class ClassScope extends AbstractJavaScope {
Map<VariableNameDeclaration, List<NameOccurrence>> vars = s.getDeclarations(VariableNameDeclaration.class);
for (VariableNameDeclaration d : vars.keySet()) {
if (d.getImage().equals(name.getImage())) {
type = new SimpleTypedNameDeclaration(d.getTypeImage(),
this.getEnclosingScope(SourceFileScope.class).resolveType(d.getTypeImage()));
String typeName = d.getTypeImage();
typeName = qualifyTypeName(typeName);
type = new SimpleTypedNameDeclaration(typeName,
this.getEnclosingScope(SourceFileScope.class).resolveType(typeName));
break;
}
}
@ -298,6 +318,7 @@ public class ClassScope extends AbstractJavaScope {
} else if (child instanceof ASTAllocationExpression && child.jjtGetChild(0) instanceof ASTClassOrInterfaceType) {
ASTClassOrInterfaceType classInterface = (ASTClassOrInterfaceType)child.jjtGetChild(0);
String typeImage = classInterface.getImage();
typeImage = qualifyTypeName(typeImage);
type = new SimpleTypedNameDeclaration(typeImage,
this.getEnclosingScope(SourceFileScope.class).resolveType(typeImage));
}

View File

@ -165,6 +165,8 @@ public class ScopeAndDeclarationFinder extends JavaParserVisitorAdapter {
@Override
public Object visit(ASTEnumDeclaration node, Object data) {
createClassScope(node);
Scope s = ((JavaNode)node.jjtGetParent()).getScope();
s.addDeclaration(new ClassNameDeclaration(node));
cont(node);
return data;
}

View File

@ -4,12 +4,15 @@
package net.sourceforge.pmd.lang.java.symboltable;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import net.sourceforge.pmd.lang.java.ast.ASTImportDeclaration;
import net.sourceforge.pmd.lang.symboltable.NameDeclaration;
import net.sourceforge.pmd.lang.symboltable.NameOccurrence;
import net.sourceforge.pmd.lang.symboltable.Scope;
/**
* This scope is the outer most scope of a Java file.
@ -103,4 +106,26 @@ public class SourceFileScope extends AbstractJavaScope {
Applier.apply(finder, getDeclarations().keySet().iterator());
return finder.getDecl();
}
/**
* Returns a set of all types defined within this source file.
* This includes all top-level types and nested types.
* @return set of all types in this source file.
*/
public Set<String> getQualifiedTypeNames() {
return getSubTypes(null, this);
}
private Set<String> getSubTypes(String qualifyingName, Scope subType) {
Set<String> types = new HashSet<String>();
for (ClassNameDeclaration c : subType.getDeclarations(ClassNameDeclaration.class).keySet()) {
String typeName = c.getName();
if (qualifyingName != null) {
typeName = qualifyingName + "." + typeName;
}
types.add(typeName);
types.addAll(getSubTypes(typeName, c.getScope()));
}
return types;
}
}

View File

@ -1242,6 +1242,33 @@ public class TestPrivate {
private <X extends Map> void setTotals(X ledgerable) throws ServiceException {
// do stuff
}
}
]]></code>
</test-code>
<test-code>
<description>#1276 False positive in UnusedPrivateMethod when method arg is Object and not called with plain Object</description>
<expected-problems>0</expected-problems>
<code><![CDATA[
public class Parent {
enum A {
someEnum;
}
public void doSomethingUnqualified(A a) {
doSomethingPrivateWithQualified(a);
}
private void doSomethingPrivateWithQualified(Parent.A a) {
// PMD error because it doesn't equate Parent.A as the same type as A.
}
public void doSomethingQualified(Parent.A a) {
doSomethingPrivateUnqualified(a);
}
private void doSomethingPrivateUnqualified(A a) {
// PMD error because it doesn't equate Parent.A as the same type as A.
}
}
]]></code>
</test-code>