diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/AbstractNode.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/AbstractNode.java index aef48f0d30..b326986a90 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/AbstractNode.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/AbstractNode.java @@ -238,6 +238,22 @@ public abstract class AbstractNode implements Node { } + @SafeVarargs + @Override + public final T getFirstParentOfAnyType(Class... parentTypes) { + Node parentNode = jjtGetParent(); + while (parentNode != null) { + for (Class c : parentTypes) { + if (c.isInstance(parentNode)) { + return c.cast(parentNode); + } + } + parentNode = parentNode.jjtGetParent(); + } + return null; + } + + @Override public List findDescendantsOfType(Class targetType) { List list = new ArrayList<>(); diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/Node.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/Node.java index 256d80a92e..25b4e90a59 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/Node.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/Node.java @@ -147,6 +147,18 @@ public interface Node { */ List getParentsOfType(Class parentType); + + /** + * Gets the first parent that's an instance of any of the given types. + * + * @param parentTypes Types to look for + * @param Most specific common type of the parameters + * + * @return The first parent with a matching type. Returns null if there + * is no such parent + */ + T getFirstParentOfAnyType(Class... parentTypes); + /** * Traverses the children to find all the instances of type childType or * one of its subclasses. diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/AbstractJavaTypeNode.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/AbstractJavaTypeNode.java index 3bb3eb148c..8ade1689e3 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/AbstractJavaTypeNode.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/AbstractJavaTypeNode.java @@ -25,11 +25,7 @@ public abstract class AbstractJavaTypeNode extends AbstractJavaNode implements T @Override public Class getType() { - if (typeDefinition != null) { - return typeDefinition.getType(); - } - - return null; + return typeDefinition == null ? null : typeDefinition.getType(); } @Override diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/typeresolution/ClassTypeResolver.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/typeresolution/ClassTypeResolver.java index 71a19b19c6..f06d39b23d 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/typeresolution/ClassTypeResolver.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/typeresolution/ClassTypeResolver.java @@ -23,18 +23,17 @@ import java.util.logging.Level; import java.util.logging.Logger; import net.sourceforge.pmd.lang.ast.Node; +import net.sourceforge.pmd.lang.ast.QualifiableNode; import net.sourceforge.pmd.lang.java.ast.ASTAdditiveExpression; import net.sourceforge.pmd.lang.java.ast.ASTAllocationExpression; import net.sourceforge.pmd.lang.java.ast.ASTAndExpression; import net.sourceforge.pmd.lang.java.ast.ASTAnnotation; -import net.sourceforge.pmd.lang.java.ast.ASTAnnotationTypeDeclaration; import net.sourceforge.pmd.lang.java.ast.ASTAnyTypeDeclaration; import net.sourceforge.pmd.lang.java.ast.ASTArgumentList; import net.sourceforge.pmd.lang.java.ast.ASTArguments; import net.sourceforge.pmd.lang.java.ast.ASTArrayDimsAndInits; import net.sourceforge.pmd.lang.java.ast.ASTBooleanLiteral; import net.sourceforge.pmd.lang.java.ast.ASTCastExpression; -import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceBody; import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceDeclaration; import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceType; import net.sourceforge.pmd.lang.java.ast.ASTCompilationUnit; @@ -42,8 +41,7 @@ import net.sourceforge.pmd.lang.java.ast.ASTConditionalAndExpression; import net.sourceforge.pmd.lang.java.ast.ASTConditionalExpression; import net.sourceforge.pmd.lang.java.ast.ASTConditionalOrExpression; import net.sourceforge.pmd.lang.java.ast.ASTConstructorDeclaration; -import net.sourceforge.pmd.lang.java.ast.ASTEnumBody; -import net.sourceforge.pmd.lang.java.ast.ASTEnumDeclaration; +import net.sourceforge.pmd.lang.java.ast.ASTEnumConstant; import net.sourceforge.pmd.lang.java.ast.ASTEqualityExpression; import net.sourceforge.pmd.lang.java.ast.ASTExclusiveOrExpression; import net.sourceforge.pmd.lang.java.ast.ASTExpression; @@ -169,16 +167,7 @@ public class ClassTypeResolver extends JavaParserVisitorAdapter { private final PMDASMClassLoader pmdClassLoader; private Map importedClasses; private List importedOnDemand; - private Map anonymousClassMetadata = new HashMap<>(); - private static class AnonymousClassMetadata { - public final String name; - public int anonymousClassCounter; - - AnonymousClassMetadata(final String className) { - this.name = className; - } - } public ClassTypeResolver() { this(ClassTypeResolver.class.getClassLoader()); @@ -259,11 +248,10 @@ public class ClassTypeResolver extends JavaParserVisitorAdapter { String typeName = node.getImage(); if (node.isAnonymousClass()) { - final AnonymousClassMetadata parentAnonymousClassMetadata = getParentAnonymousClassMetadata(node); - if (parentAnonymousClassMetadata != null) { - typeName = parentAnonymousClassMetadata.name + "$" + ++parentAnonymousClassMetadata - .anonymousClassCounter; - anonymousClassMetadata.put(node, new AnonymousClassMetadata(typeName)); + QualifiableNode parent = node.getFirstParentOfAnyType(ASTAllocationExpression.class, ASTEnumConstant.class); + + if (parent != null) { + typeName = parent.getQualifiedName().toString(); } } @@ -283,73 +271,6 @@ public class ClassTypeResolver extends JavaParserVisitorAdapter { return data; } - private AnonymousClassMetadata getParentAnonymousClassMetadata(final ASTClassOrInterfaceType node) { - Node parent = node; - do { - parent = parent.jjtGetParent(); - } while (parent != null && !(parent instanceof ASTClassOrInterfaceBody) && !(parent instanceof ASTEnumBody)); - - // TODO : Should never happen, but add this for safety until we are sure to cover all possible scenarios in - // unit testing - if (parent == null) { - return null; - } - - parent = parent.jjtGetParent(); - - TypeNode typedParent; - // The parent may now be an ASTEnumConstant, an ASTAllocationExpression, an ASTEnumDeclaration or an - // ASTClassOrInterfaceDeclaration - if (parent instanceof ASTAllocationExpression) { - typedParent = parent.getFirstChildOfType(ASTClassOrInterfaceType.class); - } else if (parent instanceof ASTClassOrInterfaceDeclaration || parent instanceof ASTEnumDeclaration) { - typedParent = (TypeNode) parent; - } else { - typedParent = parent.getFirstParentOfType(ASTEnumDeclaration.class); - } - - final AnonymousClassMetadata metadata = anonymousClassMetadata.get(typedParent); - if (metadata != null) { - return metadata; - } - - final AnonymousClassMetadata newMetadata; - if (typedParent instanceof ASTClassOrInterfaceType) { - ASTClassOrInterfaceType parentTypeNode = (ASTClassOrInterfaceType) typedParent; - if (parentTypeNode.isAnonymousClass()) { - final AnonymousClassMetadata parentMetadata = getParentAnonymousClassMetadata(parentTypeNode); - newMetadata = new AnonymousClassMetadata(parentMetadata.name + "$" + ++parentMetadata - .anonymousClassCounter); - } else { - newMetadata = new AnonymousClassMetadata(parentTypeNode.getImage()); - } - } else { - newMetadata = new AnonymousClassMetadata(typedParent.getImage()); - } - - anonymousClassMetadata.put(typedParent, newMetadata); - - return newMetadata; - } - - @Override - public Object visit(ASTClassOrInterfaceDeclaration node, Object data) { - populateType(node, node.getImage()); - return super.visit(node, data); - } - - @Override - public Object visit(ASTEnumDeclaration node, Object data) { - populateType(node, node.getImage()); - return super.visit(node, data); - } - - @Override - public Object visit(ASTAnnotationTypeDeclaration node, Object data) { - populateType(node, node.getImage()); - return super.visit(node, data); - } - /** * Set's the node's type to the found Class in the node's name (if there is a class to be found). * @@ -1407,9 +1328,8 @@ public class ClassTypeResolver extends JavaParserVisitorAdapter { if (node.declarationsAreInDefaultPackage()) { return classDecl.getImage(); } - ASTPackageDeclaration pkgDecl = node.getPackageDeclaration(); - importedOnDemand.add(pkgDecl.getPackageNameImage()); - return pkgDecl.getPackageNameImage() + "." + classDecl.getImage(); + importedOnDemand.add(node.getPackageDeclaration().getPackageNameImage()); + return classDecl.getQualifiedName().toString(); } /** diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/typeresolution/ClassTypeResolverTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/typeresolution/ClassTypeResolverTest.java index 4b0689950d..7efdb6b25e 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/typeresolution/ClassTypeResolverTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/typeresolution/ClassTypeResolverTest.java @@ -41,6 +41,7 @@ import net.sourceforge.pmd.lang.java.ast.ASTBooleanLiteral; import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceDeclaration; import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceType; import net.sourceforge.pmd.lang.java.ast.ASTCompilationUnit; +import net.sourceforge.pmd.lang.java.ast.ASTEnumConstant; import net.sourceforge.pmd.lang.java.ast.ASTExpression; import net.sourceforge.pmd.lang.java.ast.ASTFieldDeclaration; import net.sourceforge.pmd.lang.java.ast.ASTFormalParameter; @@ -167,9 +168,14 @@ public class ClassTypeResolverTest { @Test public void testEnumAnonymousInnerClass() { ASTCompilationUnit acu = parseAndTypeResolveForClass15(EnumWithAnonymousInnerClass.class); + // try it in jshell, an enum constant with a body is compiled to an anonymous class, + // the counter is shared with other anonymous classes of the enum + Class enumAnon = acu.getFirstDescendantOfType(ASTEnumConstant.class).getQualifiedName().getType(); + assertEquals("net.sourceforge.pmd.typeresolution.testdata.EnumWithAnonymousInnerClass$1", enumAnon.getName()); + Class inner = acu.getFirstDescendantOfType(ASTAllocationExpression.class) .getFirstDescendantOfType(ASTClassOrInterfaceType.class).getType(); - assertEquals("net.sourceforge.pmd.typeresolution.testdata.EnumWithAnonymousInnerClass$1", inner.getName()); + assertEquals("net.sourceforge.pmd.typeresolution.testdata.EnumWithAnonymousInnerClass$2", inner.getName()); } @Test @@ -245,7 +251,7 @@ public class ClassTypeResolverTest { } @Test - public void testAnoymousExtendingObject() throws Exception { + public void testAnonymousExtendingObject() throws Exception { Node acu = parseAndTypeResolveForClass(AnoymousExtendingObject.class, "1.8"); ASTAllocationExpression allocationExpression = acu.getFirstDescendantOfType(ASTAllocationExpression.class); TypeNode child = (TypeNode) allocationExpression.jjtGetChild(0);