From b4eddcae588611341b4fca879e344bbdbcc632eb Mon Sep 17 00:00:00 2001 From: I-Al-Istannen Date: Thu, 17 Nov 2022 20:45:57 +0100 Subject: [PATCH] fix: getTypeDeclaration() on nested record type Co-authored-by: Hannes Greule Co-authored-by: Martin Wittlinger --- .../reflect/declaration/CtTypeImpl.java | 75 +++++++++++-------- .../java/spoon/test/ctType/CtTypeTest.java | 23 ++++++ 2 files changed, 65 insertions(+), 33 deletions(-) diff --git a/src/main/java/spoon/support/reflect/declaration/CtTypeImpl.java b/src/main/java/spoon/support/reflect/declaration/CtTypeImpl.java index 8e4a5804c7f..af383b290f0 100644 --- a/src/main/java/spoon/support/reflect/declaration/CtTypeImpl.java +++ b/src/main/java/spoon/support/reflect/declaration/CtTypeImpl.java @@ -14,15 +14,20 @@ import spoon.reflect.code.CtBlock; import spoon.reflect.declaration.CtAnnotation; import spoon.reflect.declaration.CtAnnotationType; +import spoon.reflect.declaration.CtClass; import spoon.reflect.declaration.CtCompilationUnit; import spoon.reflect.declaration.CtConstructor; +import spoon.reflect.declaration.CtElement; +import spoon.reflect.declaration.CtEnum; import spoon.reflect.declaration.CtExecutable; import spoon.reflect.declaration.CtField; import spoon.reflect.declaration.CtFormalTypeDeclarer; +import spoon.reflect.declaration.CtInterface; import spoon.reflect.declaration.CtMethod; import spoon.reflect.declaration.CtModifiable; import spoon.reflect.declaration.CtNamedElement; import spoon.reflect.declaration.CtPackage; +import spoon.reflect.declaration.CtRecord; import spoon.reflect.declaration.CtShadowable; import spoon.reflect.declaration.CtType; import spoon.reflect.declaration.CtTypeMember; @@ -347,7 +352,7 @@ public CtType getTopLevelType() { public > N getNestedType(final String name) { class NestedTypeScanner extends EarlyTerminatingScanner> { - private boolean checkType(CtType type) { + private boolean typeIsTarget(CtType type) { if (type.getSimpleName().equals(name) && CtTypeImpl.this.equals(type.getDeclaringType())) { setResult(type); terminate(); @@ -357,49 +362,53 @@ private boolean checkType(CtType type) { } @Override - public void visitCtClass(spoon.reflect.declaration.CtClass ctClass) { - if (!checkType(ctClass)) { - final List typeMembers = new ArrayList<>(); - for (CtTypeMember typeMember : ctClass.getTypeMembers()) { - if (typeMember instanceof CtType || typeMember instanceof CtConstructor || typeMember instanceof CtMethod) { - typeMembers.add(typeMember); - } - } - scan(typeMembers); - } + public void visitCtClass(CtClass ctClass) { + visitType(ctClass); } @Override - public void visitCtInterface(spoon.reflect.declaration.CtInterface intrface) { - if (!checkType(intrface)) { - final List typeMembers = new ArrayList<>(); - for (CtTypeMember typeMember : intrface.getTypeMembers()) { - if (typeMember instanceof CtType || typeMember instanceof CtMethod) { - typeMembers.add(typeMember); - } - } - scan(typeMembers); - } + public void visitCtInterface(CtInterface intrface) { + visitType(intrface); } @Override - public > void visitCtEnum(spoon.reflect.declaration.CtEnum ctEnum) { - if (!checkType(ctEnum)) { - final List typeMembers = new ArrayList<>(); - for (CtTypeMember typeMember : ctEnum.getTypeMembers()) { - if (typeMember instanceof CtType || typeMember instanceof CtConstructor || typeMember instanceof CtMethod) { - typeMembers.add(typeMember); - } - } - scan(typeMembers); - } + public > void visitCtEnum(CtEnum ctEnum) { + visitType(ctEnum); } @Override public void visitCtAnnotationType(CtAnnotationType annotationType) { - if (!checkType(annotationType)) { - scan(annotationType.getNestedTypes()); + visitType(annotationType); + } + + @Override + public void visitCtRecord(CtRecord recordType) { + visitType(recordType); + } + + @Override + public void visitCtTypeParameter(CtTypeParameter typeParameter) { + // ignored, can not harbour any nested types + } + + @Override + protected void enter(CtElement e) { + if (e instanceof CtType) { + throw new SpoonException("Unknown type, extend scanner: " + e.getClass()); + } + } + + private void visitType(CtType type) { + if (typeIsTarget(type)) { + return; + } + final List typeMembers = new ArrayList<>(); + for (CtTypeMember typeMember : type.getTypeMembers()) { + if (typeMember instanceof CtType || typeMember instanceof CtConstructor || typeMember instanceof CtMethod) { + typeMembers.add(typeMember); + } } + scan(typeMembers); } } NestedTypeScanner scanner = new NestedTypeScanner(); diff --git a/src/test/java/spoon/test/ctType/CtTypeTest.java b/src/test/java/spoon/test/ctType/CtTypeTest.java index 585b71c834b..27fb8ae52f0 100644 --- a/src/test/java/spoon/test/ctType/CtTypeTest.java +++ b/src/test/java/spoon/test/ctType/CtTypeTest.java @@ -35,6 +35,7 @@ import spoon.reflect.reference.CtTypeReference; import spoon.reflect.visitor.filter.NamedElementFilter; import spoon.reflect.visitor.filter.TypeFilter; +import spoon.support.compiler.VirtualFile; import spoon.test.ctType.testclasses.X; import spoon.testing.utils.ModelTest; @@ -305,4 +306,26 @@ public void testEnumPackage() { assertThat(types.stream().findFirst().get(), notNullValue()); assertThat(types.stream().findFirst().get().getQualifiedName(), is("keywordCompliance.enum.Foo")); } + + @Test + void testRecordInnerClassesHaveDefinition() { + // contract: Record inner classes should have a definition + Launcher launcher = new Launcher(); + launcher.addInputResource(new VirtualFile("class Foo {\n" + + " class Inner {\n" + + " record Inner2(String name) {\n" + + " }\n" + + " }\n" + + "}")); + launcher.getEnvironment().setComplianceLevel(17); + CtType foo = launcher.buildModel().getAllTypes().iterator().next(); + assertEquals(foo, foo.getReference().getTypeDeclaration()); + + for (CtType nestedType : foo.getNestedTypes()) { + assertEquals(nestedType, nestedType.getReference().getTypeDeclaration()); + for (CtType type : nestedType.getNestedTypes()) { + assertEquals(type, type.getReference().getTypeDeclaration()); + } + } + } }