forked from phoedos/pmd
Use JavaQualifiedName to resolve anonymous classes types in ClassTypeResolver
Success! We still need to figure out a straightforward bridge between JavaTypeDefinition and qualified names
This commit is contained in:
@ -238,6 +238,22 @@ public abstract class AbstractNode implements Node {
|
||||
}
|
||||
|
||||
|
||||
@SafeVarargs
|
||||
@Override
|
||||
public final <T> T getFirstParentOfAnyType(Class<? extends T>... parentTypes) {
|
||||
Node parentNode = jjtGetParent();
|
||||
while (parentNode != null) {
|
||||
for (Class<? extends T> c : parentTypes) {
|
||||
if (c.isInstance(parentNode)) {
|
||||
return c.cast(parentNode);
|
||||
}
|
||||
}
|
||||
parentNode = parentNode.jjtGetParent();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public <T> List<T> findDescendantsOfType(Class<T> targetType) {
|
||||
List<T> list = new ArrayList<>();
|
||||
|
@ -147,6 +147,18 @@ public interface Node {
|
||||
*/
|
||||
<T> List<T> getParentsOfType(Class<T> parentType);
|
||||
|
||||
|
||||
/**
|
||||
* Gets the first parent that's an instance of any of the given types.
|
||||
*
|
||||
* @param parentTypes Types to look for
|
||||
* @param <T> Most specific common type of the parameters
|
||||
*
|
||||
* @return The first parent with a matching type. Returns null if there
|
||||
* is no such parent
|
||||
*/
|
||||
<T> T getFirstParentOfAnyType(Class<? extends T>... parentTypes);
|
||||
|
||||
/**
|
||||
* Traverses the children to find all the instances of type childType or
|
||||
* one of its subclasses.
|
||||
|
@ -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
|
||||
|
@ -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<String, String> importedClasses;
|
||||
private List<String> importedOnDemand;
|
||||
private Map<Node, AnonymousClassMetadata> 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();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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);
|
||||
|
Reference in New Issue
Block a user