From 011be63f616796837be9ee07dc95dc6342edcbe2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20Lars=C3=A9n?= Date: Tue, 1 Jun 2021 07:44:17 +0200 Subject: [PATCH] fix(noclasspath): Handle type resolution of anonymous class with unresolved constructor (#3971) --- .../compiler/jdt/ReferenceBuilder.java | 2 +- .../constructorcallnewclass/NewClassTest.java | 28 +++++++++++++++++++ .../BadAnonymousClassOfNestedType.java | 12 ++++++++ 3 files changed, 41 insertions(+), 1 deletion(-) create mode 100644 src/test/resources/noclasspath/BadAnonymousClassOfNestedType.java diff --git a/src/main/java/spoon/support/compiler/jdt/ReferenceBuilder.java b/src/main/java/spoon/support/compiler/jdt/ReferenceBuilder.java index 648137e6fce..88f702b1b5f 100644 --- a/src/main/java/spoon/support/compiler/jdt/ReferenceBuilder.java +++ b/src/main/java/spoon/support/compiler/jdt/ReferenceBuilder.java @@ -659,7 +659,7 @@ CtTypeReference getTypeReference(TypeReference ref) { CtPackageReference packageReference = index >= 0 ? packageFactory.getOrCreate(concatSubArray(namesParameterized, index)).getReference() : packageFactory.topLevel(); inner.setPackage(packageReference); } - if (!res.toStringDebug().replace(", ?", ",?").endsWith(nameParameterized)) { + if (!res.toStringDebug().replace(", ", ",").endsWith(nameParameterized)) { // verify that we did not match a class that have the same name in a different package return this.jdtTreeBuilder.getFactory().Type().createReference(typeName); } diff --git a/src/test/java/spoon/test/constructorcallnewclass/NewClassTest.java b/src/test/java/spoon/test/constructorcallnewclass/NewClassTest.java index 37bb472fec5..3b433d3db67 100644 --- a/src/test/java/spoon/test/constructorcallnewclass/NewClassTest.java +++ b/src/test/java/spoon/test/constructorcallnewclass/NewClassTest.java @@ -18,10 +18,13 @@ import org.junit.Before; import org.junit.Test; +import spoon.FluentLauncher; import spoon.Launcher; +import spoon.reflect.CtModel; import spoon.reflect.code.CtExpression; import spoon.reflect.code.CtNewClass; import spoon.reflect.declaration.CtClass; +import spoon.reflect.declaration.CtType; import spoon.reflect.factory.Factory; import spoon.reflect.reference.CtExecutableReference; import spoon.reflect.reference.CtTypeReference; @@ -33,6 +36,10 @@ import java.util.List; +import static org.hamcrest.CoreMatchers.equalTo; +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.CoreMatchers.startsWith; +import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; @@ -201,4 +208,25 @@ public void testCtNewClassInNoClasspath() { canBeBuilt("./target/new-class", 8, true); } + + @Test + public void testBadConstructorCallToAnonymousGenericType() { + // contract: Spoon should be able to resolve the type of a constructor call to an anonymous + // subclass of a generic type, when the constructor does not exist. + // See https://github.com/INRIA/spoon/issues/3913 for details. + + CtModel model = new FluentLauncher() + .inputResource("./src/test/resources/noclasspath/BadAnonymousClassOfNestedType.java") + .buildModel(); + CtNewClass newClass = model.filterChildren(CtNewClass.class::isInstance).first(); + CtType anonymousClass = newClass.getAnonymousClass(); + + CtType expectedSuperclass = model + .getUnnamedModule() + .getFactory() + .Type() + .get("BadAnonymousClassOfNestedType$GenericType"); + assertThat(anonymousClass.getQualifiedName(), startsWith("BadAnonymousClassOfNestedType")); + assertThat(anonymousClass.getSuperclass().getTypeDeclaration(), equalTo(expectedSuperclass)); + } } diff --git a/src/test/resources/noclasspath/BadAnonymousClassOfNestedType.java b/src/test/resources/noclasspath/BadAnonymousClassOfNestedType.java new file mode 100644 index 00000000000..9f4a08c588a --- /dev/null +++ b/src/test/resources/noclasspath/BadAnonymousClassOfNestedType.java @@ -0,0 +1,12 @@ +/* +Test source file to reproduce the behavior reported in https://github.com/INRIA/spoon/issues/3913 + */ +public final class BadAnonymousClassOfNestedType { + public void test() { + new BadAnonymousClassOfNestedType.GenericType(this) { + }; + } + + static class GenericType { + } +}