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:
Clément Fournier
2018-03-10 17:54:28 +01:00
parent eb8908796c
commit ccc1443373
5 changed files with 45 additions and 95 deletions

View File

@ -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<>();

View File

@ -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.

View File

@ -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

View File

@ -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();
}
/**

View File

@ -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);