From e620e692915979b40b49acc225ffa51d9db799ac Mon Sep 17 00:00:00 2001 From: Simon Urli Date: Tue, 17 Oct 2017 18:09:43 +0200 Subject: [PATCH] fix: fix bug related to generics in CtTypeReference in noclasspath (#1609) Close #1607 --- .../support/compiler/jdt/JDTTreeBuilder.java | 4 +++- .../compiler/jdt/ReferenceBuilder.java | 16 +++++++++----- .../java/spoon/test/imports/ImportTest.java | 22 +++++++++++++++++++ .../test/reference/TypeReferenceTest.java | 16 ++++++++++++++ .../TestWithGenerics.java | 7 ++++++ 5 files changed, 59 insertions(+), 6 deletions(-) create mode 100644 src/test/resources/import-with-generics/TestWithGenerics.java diff --git a/src/main/java/spoon/support/compiler/jdt/JDTTreeBuilder.java b/src/main/java/spoon/support/compiler/jdt/JDTTreeBuilder.java index 64885b43d83..8fd81c86adf 100644 --- a/src/main/java/spoon/support/compiler/jdt/JDTTreeBuilder.java +++ b/src/main/java/spoon/support/compiler/jdt/JDTTreeBuilder.java @@ -1244,7 +1244,9 @@ private boolean createParameterizedType(TypeReference parameterizedTypeReference if (skipTypeInAnnotation) { return true; } - context.enter(factory.Code().createTypeAccessWithoutCloningReference(references.buildTypeReference(parameterizedTypeReference, null)), parameterizedTypeReference); + CtTypeReference typeReference = references.buildTypeReference(parameterizedTypeReference, null); + CtTypeAccess typeAccess = factory.Code().createTypeAccessWithoutCloningReference(typeReference); + context.enter(typeAccess, parameterizedTypeReference); return true; } diff --git a/src/main/java/spoon/support/compiler/jdt/ReferenceBuilder.java b/src/main/java/spoon/support/compiler/jdt/ReferenceBuilder.java index fffe89db9ff..983dc6a36b7 100644 --- a/src/main/java/spoon/support/compiler/jdt/ReferenceBuilder.java +++ b/src/main/java/spoon/support/compiler/jdt/ReferenceBuilder.java @@ -130,7 +130,8 @@ CtTypeReference buildTypeReference(TypeReference type, Scope scope) { if (type == null) { return null; } - return buildTypeReferenceInternal(this.getTypeReference(type.resolvedType, type), type, scope); + CtTypeReference typeReference = this.getTypeReference(type.resolvedType, type); + return buildTypeReferenceInternal(typeReference, type, scope); } /** @@ -457,7 +458,8 @@ CtTypeReference getTypeReference(TypeBinding binding, TypeReference ref) insertGenericTypesInNoClasspathFromJDTInSpoon(ref, ctRef); return ctRef; } - return getTypeReference(ref); + CtTypeReference result = getTypeReference(ref); + return result; } CtTypeReference getTypeParameterReference(TypeBinding binding, TypeReference ref) { @@ -518,6 +520,9 @@ CtTypeReference getTypeReference(TypeReference ref) { CtTypeReference res = null; CtTypeReference inner = null; final String[] namesParameterized = CharOperation.charArrayToStringArray(ref.getParameterizedTypeName()); + String nameParameterized = CharOperation.toString(ref.getParameterizedTypeName()); + String typeName = CharOperation.toString(ref.getTypeName()); + int index = namesParameterized.length - 1; for (; index >= 0; index--) { // Start at the end to get the class name first. @@ -533,16 +538,17 @@ CtTypeReference getTypeReference(TypeReference ref) { inner = main; } if (res == null) { - return this.jdtTreeBuilder.getFactory().Type().createReference(CharOperation.toString(ref.getParameterizedTypeName())); + return this.jdtTreeBuilder.getFactory().Type().createReference(nameParameterized); } + if (inner.getPackage() == null) { PackageFactory packageFactory = this.jdtTreeBuilder.getFactory().Package(); CtPackageReference packageReference = index >= 0 ? packageFactory.getOrCreate(concatSubArray(namesParameterized, index)).getReference() : packageFactory.topLevel(); inner.setPackage(packageReference); } - if (!res.toString().replace(", ?", ",?").endsWith(CharOperation.toString(ref.getParameterizedTypeName()))) { + if (!res.toString().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(CharOperation.toString(ref.getParameterizedTypeName())); + return this.jdtTreeBuilder.getFactory().Type().createReference(typeName); } return res; } diff --git a/src/test/java/spoon/test/imports/ImportTest.java b/src/test/java/spoon/test/imports/ImportTest.java index 5d7dec6cf79..14bae826c9c 100644 --- a/src/test/java/spoon/test/imports/ImportTest.java +++ b/src/test/java/spoon/test/imports/ImportTest.java @@ -1230,4 +1230,26 @@ public void testImportStarredPackageWithNonVisibleClass() throws IOException { assertEquals(3, cu.getImports().size()); } + + @Test + public void testImportWithGenerics() throws IOException { + // contract: in noclasspath autoimport, we should be able to use generic type + final Launcher launcher = new Launcher(); + launcher.addInputResource("./src/test/resources/import-with-generics/TestWithGenerics.java"); + launcher.getEnvironment().setAutoImports(true); + launcher.getEnvironment().setShouldCompile(true); + launcher.getEnvironment().setNoClasspath(true); + launcher.setSourceOutputDirectory("./target/import-with-generics"); + launcher.run(); + + PrettyPrinter prettyPrinter = launcher.createPrettyPrinter(); + CtType element = launcher.getFactory().Class().get("spoon.test.imports.testclasses.TestWithGenerics"); + List> toPrint = new ArrayList<>(); + toPrint.add(element); + + prettyPrinter.calculate(element.getPosition().getCompilationUnit(), toPrint); + String output = prettyPrinter.getResult(); + + assertTrue(output.contains("import spoon.test.imports.testclasses.withgenerics.Target;")); + } } diff --git a/src/test/java/spoon/test/reference/TypeReferenceTest.java b/src/test/java/spoon/test/reference/TypeReferenceTest.java index 35b686b061f..b69af0904ac 100644 --- a/src/test/java/spoon/test/reference/TypeReferenceTest.java +++ b/src/test/java/spoon/test/reference/TypeReferenceTest.java @@ -623,4 +623,20 @@ public void testEqualityTypeReference() throws Exception { assertEquals(parameterRef1, parameterRef2); } + + @Test + public void testTypeReferenceWithGenerics() throws Exception { + // contract: in noclasspath, a generic type name should not contain generic information + final Launcher launcher = new Launcher(); + launcher.addInputResource("./src/test/resources/import-with-generics/TestWithGenerics.java"); + launcher.getEnvironment().setAutoImports(true); + launcher.getEnvironment().setNoClasspath(true); + launcher.buildModel(); + + CtField field = launcher.getModel().getElements(new TypeFilter(CtField.class)).get(0); + CtTypeReference fieldTypeRef = field.getType(); + + assertEquals("spoon.test.imports.testclasses.withgenerics.Target", fieldTypeRef.getQualifiedName()); + assertEquals(2, fieldTypeRef.getActualTypeArguments().size()); + } } diff --git a/src/test/resources/import-with-generics/TestWithGenerics.java b/src/test/resources/import-with-generics/TestWithGenerics.java new file mode 100644 index 00000000000..42164104c81 --- /dev/null +++ b/src/test/resources/import-with-generics/TestWithGenerics.java @@ -0,0 +1,7 @@ +package spoon.test.imports.testclasses; +/** + * Created by urli on 16/10/2017. + */ +public class TestWithGenerics { + public spoon.test.imports.testclasses.withgenerics.Target myfields; +}