From f57bf23715b0a6438414d3254269f5cca48b16dc Mon Sep 17 00:00:00 2001 From: I-Al-Istannen Date: Wed, 11 Dec 2024 16:46:22 +0100 Subject: [PATCH] fix: Crashes when records contain static fields (#6095) Spoon tried to remove the field to prevent record field duplication. This fails for static fields, which do not exist (yet). For these, spoon passed null to the remove method, which failed with an exception. We now check for the modifier and null explicitly. --- .../spoon/support/compiler/jdt/ParentExiter.java | 7 +++++-- .../java/spoon/test/record/CtRecordTest.java | 16 ++++++++++++++++ 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/src/main/java/spoon/support/compiler/jdt/ParentExiter.java b/src/main/java/spoon/support/compiler/jdt/ParentExiter.java index 776075f4f78..4bea9488b3c 100644 --- a/src/main/java/spoon/support/compiler/jdt/ParentExiter.java +++ b/src/main/java/spoon/support/compiler/jdt/ParentExiter.java @@ -254,8 +254,11 @@ public void scanCtType(CtType type) { } else if (child instanceof CtField field) { // We add the field in addRecordComponent. Afterward, however, JDT visits the Field itself -> Duplication. // To combat this, we delete the existing field and trust JDTs version. - if (type instanceof CtRecord record) { - record.removeField(record.getField(field.getSimpleName())); + if (type instanceof CtRecord record && !field.isStatic()) { + CtField existing = record.getField(field.getSimpleName()); + if (existing != null) { + record.removeField(existing); + } } type.addField(field); return; diff --git a/src/test/java/spoon/test/record/CtRecordTest.java b/src/test/java/spoon/test/record/CtRecordTest.java index 7a1ac7c4af7..e42b91a14a6 100644 --- a/src/test/java/spoon/test/record/CtRecordTest.java +++ b/src/test/java/spoon/test/record/CtRecordTest.java @@ -24,6 +24,7 @@ import spoon.reflect.code.CtReturn; import spoon.reflect.code.CtStatement; import spoon.reflect.declaration.CtAnonymousExecutable; +import spoon.reflect.declaration.CtClass; import spoon.reflect.declaration.CtConstructor; import spoon.reflect.declaration.CtElement; import spoon.reflect.declaration.CtField; @@ -315,6 +316,21 @@ void testProperReturnInRecordAccessor(Factory factory) { } } + @Test + void testRecordWithStaticField() { + // contract: Static fields in records do not cause crashes + CtClass parsed = Launcher.parseClass(""" + public record User(int id, String name) { + private static String ADMIN_NAME = "admin"; + } + """); + assertThat(parsed).isInstanceOf(CtRecord.class); + assertThat(parsed).getFields().hasSize(3); + assertThat(parsed.getFields()).anySatisfy(it -> assertThat(it.getSimpleName()).isEqualTo("id")); + assertThat(parsed.getFields()).anySatisfy(it -> assertThat(it.getSimpleName()).isEqualTo("name")); + assertThat(parsed.getFields()).anySatisfy(it -> assertThat(it.getSimpleName()).isEqualTo("ADMIN_NAME")); + } + private T head(Collection collection) { return collection.iterator().next(); }