From 747c1a5efaba3886b7aa1197236e442f1aebea57 Mon Sep 17 00:00:00 2001 From: Hannes Greule Date: Mon, 8 Jan 2024 15:36:27 +0100 Subject: [PATCH] chore: update jdt to 3.36.0 (#5586) Co-authored-by: I-Al-Istannen Co-authored-by: Martin Wittlinger --- flake.nix | 2 +- pom.xml | 2 +- .../spoon/smpl/ZippedCodeBaseTestContext.java | 1 + .../support/compiler/jdt/ParentExiter.java | 42 +++++++++++- src/test/java/spoon/FluentLauncherTest.java | 2 +- .../java/spoon/test/record/CtRecordTest.java | 67 +++++++++++++++++-- .../records/MultipleConstructors.java | 8 +++ .../NonCompactCanonicalConstructor.java | 9 +++ 8 files changed, 124 insertions(+), 9 deletions(-) create mode 100644 src/test/resources/records/MultipleConstructors.java create mode 100644 src/test/resources/records/NonCompactCanonicalConstructor.java diff --git a/flake.nix b/flake.nix index 3ff578b3215..bc5e2ca0659 100644 --- a/flake.nix +++ b/flake.nix @@ -147,7 +147,7 @@ mvn -q versions:use-latest-versions -DallowSnapshots=true -Dincludes=fr.inria.gforge.spoon mvn -q versions:update-parent -DallowSnapshots=true git diff - mvn -q -Djava.src.version=11 test + mvn -q -Djava.src.version=17 test mvn -q checkstyle:checkstyle license:check mvn -q depclean:depclean popd || exit 1 diff --git a/pom.xml b/pom.xml index 1cc05994390..56d6536cab7 100644 --- a/pom.xml +++ b/pom.xml @@ -34,7 +34,7 @@ org.eclipse.jdt org.eclipse.jdt.core - 3.33.0 + 3.36.0 org.eclipse.platform diff --git a/spoon-smpl/src/test/java/spoon/smpl/ZippedCodeBaseTestContext.java b/spoon-smpl/src/test/java/spoon/smpl/ZippedCodeBaseTestContext.java index 98496517a87..3122fdbf9a5 100644 --- a/spoon-smpl/src/test/java/spoon/smpl/ZippedCodeBaseTestContext.java +++ b/spoon-smpl/src/test/java/spoon/smpl/ZippedCodeBaseTestContext.java @@ -47,6 +47,7 @@ public ZippedCodeBaseTestContext(String smpl, String pathToSourcesZipFile, boole public static CtModel buildModel(String pathToZipFile, boolean useAutoImports) { Launcher launcher = new Launcher(); launcher.getEnvironment().setAutoImports(useAutoImports); + launcher.getEnvironment().setIgnoreSyntaxErrors(true); try { launcher.addInputResource(new ZipFolder(new File(pathToZipFile))); diff --git a/src/main/java/spoon/support/compiler/jdt/ParentExiter.java b/src/main/java/spoon/support/compiler/jdt/ParentExiter.java index 989abef9761..2c700541aea 100644 --- a/src/main/java/spoon/support/compiler/jdt/ParentExiter.java +++ b/src/main/java/spoon/support/compiler/jdt/ParentExiter.java @@ -1121,8 +1121,8 @@ public void visitCtTypePattern(CtTypePattern pattern) { @Override public void visitCtRecord(CtRecord recordType) { - if (child instanceof CtConstructor) { - recordType.addConstructor((CtConstructor) child); + if (child instanceof CtConstructor newConstructor) { + adjustConstructors(recordType, newConstructor); } if (child instanceof CtAnonymousExecutable) { recordType.addAnonymousExecutable((CtAnonymousExecutable) child); @@ -1132,6 +1132,44 @@ public void visitCtRecord(CtRecord recordType) { } super.visitCtRecord(recordType); } + /** + * Modifies the set of constructors of a {@link CtRecord} instance based on the properties of a new constructor. + *

+ * This method performs the following operations: + *

+ * - If the new constructor is implicit, the method checks against all constructors in the record. If a constructor + * with matching parameters is found, the function returns without adding the new constructor. + *

+ * - If the new constructor is not implicit, the method traverses the existing constructors of the record. If any + * implicit constructor with matching parameters is found, it's removed from the record. + *

+ * - If constructor to be added passes the conditions above, or no matching parameters are found, it gets added to the record. + * + * @param recordType The {@link CtRecord} instance to which the new constructor might be added. + * @param newConstructor The new constructor that should be added to the record, contingent on certain conditions. + */ + private static void adjustConstructors(CtRecord recordType, CtConstructor newConstructor) { + if (newConstructor.isImplicit()) { + for (CtConstructor constructor : recordType.getConstructors()) { + if (hasSameParameters(newConstructor, constructor)) { + return; + } + } + } else { + for (CtConstructor constructor : recordType.getConstructors()) { + if (constructor.isImplicit() && hasSameParameters(newConstructor, constructor)) { + recordType.removeConstructor(constructor); + } + } + } + recordType.addConstructor(newConstructor); + } + + private static boolean hasSameParameters(CtConstructor newConstructor, CtConstructor constructor) { + // use endsWith because constructor already has a declaring type set while newConstructor doesn't + // but we are only interested in the parameters, so we compare e.g. "R(int)" with "(int)" + return constructor.getSignature().endsWith(newConstructor.getSignature()); + } @Override public void visitCtRecordComponent(CtRecordComponent recordComponent) { diff --git a/src/test/java/spoon/FluentLauncherTest.java b/src/test/java/spoon/FluentLauncherTest.java index cd16d62ad93..0a96edb29da 100644 --- a/src/test/java/spoon/FluentLauncherTest.java +++ b/src/test/java/spoon/FluentLauncherTest.java @@ -96,7 +96,7 @@ public void testSettings(@TempDir Path tempDir) throws IOException { @Test public void testMavenLauncher(@TempDir Path tempDir) throws IOException { CtModel model = new FluentLauncher(new MavenLauncher("./pom.xml", MavenLauncher.SOURCE_TYPE.ALL_SOURCE)) - .complianceLevel(11) + .complianceLevel(17) .outputDirectory(tempDir.toString()) .buildModel(); assertNotNull(model); diff --git a/src/test/java/spoon/test/record/CtRecordTest.java b/src/test/java/spoon/test/record/CtRecordTest.java index 712d25f8e52..67bc31d50dc 100644 --- a/src/test/java/spoon/test/record/CtRecordTest.java +++ b/src/test/java/spoon/test/record/CtRecordTest.java @@ -9,7 +9,9 @@ import static org.junit.jupiter.api.Assertions.assertTrue; import java.util.Arrays; import java.util.Collection; +import java.util.Comparator; import java.util.List; +import java.util.Set; import java.util.stream.Collectors; import javax.validation.constraints.NotNull; @@ -22,7 +24,9 @@ import spoon.reflect.declaration.CtMethod; import spoon.reflect.declaration.CtRecord; import spoon.reflect.declaration.CtType; +import spoon.reflect.factory.Factory; import spoon.reflect.visitor.filter.TypeFilter; +import spoon.testing.utils.ModelTest; public class CtRecordTest { @@ -130,14 +134,18 @@ void testCompactConstructor() { CtModel model = createModelFromPath(code); assertEquals(1, model.getAllTypes().size()); Collection records = model.getElements(new TypeFilter<>(CtRecord.class)); - assertTrue(records.iterator().next().getConstructors().iterator().next().isCompactConstructor()); + Set> constructors = head(records).getConstructors(); + assertEquals(1, constructors.size()); + CtConstructor constructor = head(constructors); + assertTrue(constructor.isCompactConstructor()); + assertFalse(constructor.isImplicit()); String correctConstructor = "public Rational {" + lineSeparator() + " int gcd = records.Rational.gcd(num, denom);" + lineSeparator() + " num /= gcd;" + lineSeparator() + " denom /= gcd;" + lineSeparator() + "}"; - assertEquals(correctConstructor, head(head(records).getConstructors()).toString()); + assertEquals(correctConstructor, constructor.toString()); } @Test @@ -147,14 +155,18 @@ void testCompactConstructor2() { CtModel model = createModelFromPath(code); assertEquals(1, model.getAllTypes().size()); Collection records = model.getElements(new TypeFilter<>(CtRecord.class)); - assertTrue(records.iterator().next().getConstructors().iterator().next().isCompactConstructor()); + Set> constructors = head(records).getConstructors(); + assertEquals(1, constructors.size()); + CtConstructor constructor = head(constructors); + assertTrue(constructor.isCompactConstructor()); + assertFalse(constructor.isImplicit()); String correctConstructor = "public CompactConstructor2 {" + lineSeparator() + " int gcd = records.CompactConstructor2.gcd(num, denom);" + lineSeparator() + " num /= gcd;" + lineSeparator() + " denom /= gcd;" + lineSeparator() + "}"; - assertEquals(correctConstructor, head(head(records).getConstructors()).toString()); + assertEquals(correctConstructor, constructor.toString()); } @Test @@ -216,6 +228,53 @@ void testBuildRecordModelWithStaticInitializer() { assertThat(execs.size(), equalTo(2)); } + @ModelTest(value = "./src/test/resources/records/MultipleConstructors.java", complianceLevel = 16) + void testMultipleConstructors(Factory factory) { + // contract: we can have an explicit constructor delegating to the implicit canonical constructor + // Arrange + CtModel model = factory.getModel(); + int totalTypes = model.getAllTypes().size(); + assertEquals(1, totalTypes); + CtRecord firstRecord = model.getElements(new TypeFilter<>(CtRecord.class)).get(0); + + // Act + Set> recordConstructors = firstRecord.getConstructors(); + assertEquals(2, recordConstructors.size()); + CtConstructor[] sortedConstructors = recordConstructors.toArray(CtConstructor[]::new); + + // Sorting the constructors with implicit ones last + Arrays.sort(sortedConstructors, Comparator.comparing(CtConstructor::isImplicit)); + + // Assert + assertFalse(sortedConstructors[0].isImplicit()); + assertEquals(sortedConstructors[0].getParameters().get(0).getSimpleName(), "s"); + assertFalse(sortedConstructors[0].isCompactConstructor()); + + assertTrue(sortedConstructors[1].isImplicit()); + assertEquals(sortedConstructors[1].getParameters().get(0).getSimpleName(), "i"); + assertFalse(sortedConstructors[1].isCompactConstructor()); + } + + @ModelTest(value = "./src/test/resources/records/NonCompactCanonicalConstructor.java", complianceLevel = 16) + void testNonCompactCanonicalConstructor(Factory factory) { + // contract: we can have an explicit canonical constructor replacing the implicit canonical constructor + // Arrange + CtModel model = factory.getModel(); + int totalTypes = model.getAllTypes().size(); + assertEquals(1, totalTypes); + CtRecord firstRecord = model.getElements(new TypeFilter<>(CtRecord.class)).get(0); + + // Act + List> recordConstructors = firstRecord.getElements(new TypeFilter<>(CtConstructor.class)); + assertEquals(1, recordConstructors.size()); + CtConstructor constructor = head(recordConstructors); + + // Assert + assertFalse(constructor.isImplicit()); + assertFalse(constructor.isCompactConstructor()); + assertEquals(constructor.getParameters().get(0).getSimpleName(), "x"); + } + private T head(Collection collection) { return collection.iterator().next(); } diff --git a/src/test/resources/records/MultipleConstructors.java b/src/test/resources/records/MultipleConstructors.java new file mode 100644 index 00000000000..16aaea956ca --- /dev/null +++ b/src/test/resources/records/MultipleConstructors.java @@ -0,0 +1,8 @@ +package records; + +public record MultipleConstructors(int i) { + + public MultipleConstructors(String s) { + this(Integer.parseInt(s)); + } +} diff --git a/src/test/resources/records/NonCompactCanonicalConstructor.java b/src/test/resources/records/NonCompactCanonicalConstructor.java new file mode 100644 index 00000000000..73b5e50f179 --- /dev/null +++ b/src/test/resources/records/NonCompactCanonicalConstructor.java @@ -0,0 +1,9 @@ +package records; + +public record NonCompactCanonicalConstructor(int i) { + + public NonCompactCanonicalConstructor(int x) { + this.i = x; + } + +}