diff --git a/.github/workflows/ant.yml b/.github/workflows/ant.yml index 0c5cc94d31..b3fe6f8e10 100644 --- a/.github/workflows/ant.yml +++ b/.github/workflows/ant.yml @@ -96,10 +96,12 @@ jobs: - eclipse-202006-jdk8 - eclipse-202403 - eclipse-202503 + - eclipse-202509 - eclipse-I-build - eclipse-oxygen-full - eclipse-202403-full - eclipse-202503-full + - eclipse-202509-full - eclipse-I-build-full - ecj11 - ecj14 diff --git a/buildScripts/ivy.xml b/buildScripts/ivy.xml index 61d30e3a99..ba34f2e8fc 100644 --- a/buildScripts/ivy.xml +++ b/buildScripts/ivy.xml @@ -32,6 +32,7 @@ + @@ -135,6 +136,20 @@ + + + + + + + + + + + + + + diff --git a/buildScripts/setup.ant.xml b/buildScripts/setup.ant.xml index 9ddf877aeb..9353ee2a69 100644 --- a/buildScripts/setup.ant.xml +++ b/buildScripts/setup.ant.xml @@ -172,6 +172,10 @@ This buildfile is part of projectlombok.org. It sets up the build itself. + + + + diff --git a/buildScripts/tests.ant.xml b/buildScripts/tests.ant.xml index 1936ab8829..3256ac2870 100644 --- a/buildScripts/tests.ant.xml +++ b/buildScripts/tests.ant.xml @@ -231,6 +231,11 @@ This buildfile is part of projectlombok.org. It takes care of compiling and runn + + + + + @@ -300,6 +305,10 @@ This buildfile is part of projectlombok.org. It takes care of compiling and runn + + + + diff --git a/src/core/lombok/eclipse/handlers/HandleLockedUtil.java b/src/core/lombok/eclipse/handlers/HandleLockedUtil.java index a0199913ad..fdfe40a6f4 100644 --- a/src/core/lombok/eclipse/handlers/HandleLockedUtil.java +++ b/src/core/lombok/eclipse/handlers/HandleLockedUtil.java @@ -220,6 +220,7 @@ public static void handle(String annotationValue, Annotation source, EclipseNode tryStatement.tryBlock = block; tryStatement.finallyBlock = new Block(0); tryStatement.finallyBlock.statements = new Statement[] { unLock }; + setGeneratedBy(tryStatement, source); method.statements = new Statement[] { acquireLock, tryStatement }; diff --git a/src/eclipseAgent/lombok/eclipse/agent/EclipsePatcher.java b/src/eclipseAgent/lombok/eclipse/agent/EclipsePatcher.java index 0c2de06ddd..87bee957ff 100644 --- a/src/eclipseAgent/lombok/eclipse/agent/EclipsePatcher.java +++ b/src/eclipseAgent/lombok/eclipse/agent/EclipsePatcher.java @@ -103,6 +103,7 @@ public String mapResourceName(int classFileFormatVersion, String resourceName) { patchASTConverterLiterals(sm); patchASTNodeSearchUtil(sm); patchFieldInitializer(sm); + patchFoldingStructure(sm); patchPostCompileHookEcj(sm); @@ -1073,6 +1074,30 @@ private static void patchASTNodeSearchUtil(ScriptManager sm) { .build()); } + private static void patchFoldingStructure(ScriptManager sm) { + sm.addScriptIfWitness(OSGI_TYPES, ScriptBuilder.exitEarly() + .target(new MethodTarget("org.eclipse.jdt.ui.text.folding.DefaultJavaFoldingStructureProvider$FoldingVisitor", "createFoldingRegionForTryBlock", "void", "org.eclipse.jdt.core.dom.TryStatement")) + .target(new MethodTarget("org.eclipse.jdt.ui.text.folding.DefaultJavaFoldingStructureProvider$FoldingVisitor", "createFoldingRegionForFinallyBlock", "void", "org.eclipse.jdt.core.dom.TryStatement")) + .target(new MethodTarget("org.eclipse.jdt.ui.text.folding.DefaultJavaFoldingStructureProvider$FoldingVisitor", "createFoldingRegionForStatement", "void", "org.eclipse.jdt.core.dom.ASTNode")) + .target(new MethodTarget("org.eclipse.jdt.ui.text.folding.DefaultJavaFoldingStructureProvider$FoldingVisitor", "createFoldingRegion", "void", "org.eclipse.jdt.core.dom.ASTNode", "boolean")) + .decisionMethod(new Hook("lombok.launch.PatchFixesHider$PatchFixes", "isGenerated", "boolean", "org.eclipse.jdt.core.dom.ASTNode")) + .request(StackRequest.PARAM1) + .transplant() + .build()); + + sm.addScriptIfWitness(OSGI_TYPES, ScriptBuilder.exitEarly() + .target(new MethodTarget("org.eclipse.jdt.ui.text.folding.DefaultJavaFoldingStructureProvider$FoldingVisitor", "visit", "boolean", "org.eclipse.jdt.core.dom.TypeDeclaration")) + .target(new MethodTarget("org.eclipse.jdt.ui.text.folding.DefaultJavaFoldingStructureProvider$FoldingVisitor", "visit", "boolean", "org.eclipse.jdt.core.dom.MethodDeclaration")) + .target(new MethodTarget("org.eclipse.jdt.ui.text.folding.DefaultJavaFoldingStructureProvider$FoldingVisitor", "visit", "boolean", "org.eclipse.jdt.core.dom.IfStatement")) + .target(new MethodTarget("org.eclipse.jdt.ui.text.folding.DefaultJavaFoldingStructureProvider$FoldingVisitor", "visit", "boolean", "org.eclipse.jdt.core.dom.IfStatement")) + .decisionMethod(new Hook("lombok.launch.PatchFixesHider$PatchFixes", "isGenerated", "boolean", "org.eclipse.jdt.core.dom.ASTNode")) + .request(StackRequest.PARAM1) + .valueMethod(new Hook("lombok.launch.PatchFixesHider$PatchFixes", "returnFalse", "boolean", "java.lang.Object")) + .transplant() + .build()); + + } + private static void patchFieldInitializer(ScriptManager sm) { sm.addScriptIfWitness(OSGI_TYPES, ScriptBuilder.addField() .targetClass("org.eclipse.jdt.internal.core.CompilationUnitStructureRequestor") diff --git a/test/eclipse/resource/folding/cleanup/CleanupFolding.java b/test/eclipse/resource/folding/cleanup/CleanupFolding.java new file mode 100644 index 0000000000..3c21b01efa --- /dev/null +++ b/test/eclipse/resource/folding/cleanup/CleanupFolding.java @@ -0,0 +1,25 @@ +package pkg; + +import lombok.Cleanup; + +import java.util.List; + +public class CleanupFolding { + public void method() { + @Cleanup FileInputStream inStream = new FileInputStream("test"); + + List a = null; + + if (a.isBlank()) { + return; + } + + try { + + } catch (Exception e) { + + } finally { + + } + } +} \ No newline at end of file diff --git a/test/eclipse/resource/folding/locked/LockedFolding.java b/test/eclipse/resource/folding/locked/LockedFolding.java new file mode 100644 index 0000000000..860c048ca0 --- /dev/null +++ b/test/eclipse/resource/folding/locked/LockedFolding.java @@ -0,0 +1,24 @@ +package pkg; + +import lombok.Locked; + +import java.util.List; + +public class LockedFolding { + @Locked + public void method() { + List a = null; + + if (a.isBlank()) { + return; + } + + try { + + } catch (Exception e) { + + } finally { + + } + } +} \ No newline at end of file diff --git a/test/eclipse/resource/folding/nonNull/NonNullFolding.java b/test/eclipse/resource/folding/nonNull/NonNullFolding.java new file mode 100644 index 0000000000..654cdf482f --- /dev/null +++ b/test/eclipse/resource/folding/nonNull/NonNullFolding.java @@ -0,0 +1,21 @@ +package pkg; + +import java.util.List; + +import lombok.NonNull; + +public class NonNullFolding { + public void method(@NonNull List a) { + if (a.isEmpty()) { + return; + } + + try { + + } catch (Exception e) { + + } finally { + + } + } +} \ No newline at end of file diff --git a/test/eclipse/resource/folding/synch/SynchronizedFolding.java b/test/eclipse/resource/folding/synch/SynchronizedFolding.java new file mode 100644 index 0000000000..9fda039577 --- /dev/null +++ b/test/eclipse/resource/folding/synch/SynchronizedFolding.java @@ -0,0 +1,24 @@ +package pkg; + +import lombok.Synchronized; + +import java.util.List; + +public class LockedFolding { + @Synchronized + public void method() { + List a = null; + + if (a.isBlank()) { + return; + } + + try { + + } catch (Exception e) { + + } finally { + + } + } +} \ No newline at end of file diff --git a/test/eclipse/src/lombok/eclipse/EclipseTests.java b/test/eclipse/src/lombok/eclipse/EclipseTests.java index c0306aa6a5..83199be788 100644 --- a/test/eclipse/src/lombok/eclipse/EclipseTests.java +++ b/test/eclipse/src/lombok/eclipse/EclipseTests.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2022-2024 The Project Lombok Authors. + * Copyright (C) 2022-2025 The Project Lombok Authors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -27,6 +27,7 @@ import lombok.eclipse.cleanup.CleanupTest; import lombok.eclipse.compile.NoErrorsTest; +import lombok.eclipse.edit.FoldingTest; import lombok.eclipse.edit.SelectTest; import lombok.eclipse.misc.DelegateTest; import lombok.eclipse.misc.JavadocTest; @@ -37,7 +38,7 @@ import lombok.eclipse.references.FindReferencesTest; @RunWith(Suite.class) -@SuiteClasses({ExtractInterfaceTest.class, RenameTest.class, SelectTest.class, CleanupTest.class, FindReferencesTest.class, InlineTest.class, NoErrorsTest.class, JavadocTest.class, DelegateTest.class, ExtractVariableTest.class}) +@SuiteClasses({ExtractInterfaceTest.class, RenameTest.class, SelectTest.class, CleanupTest.class, FindReferencesTest.class, InlineTest.class, NoErrorsTest.class, JavadocTest.class, DelegateTest.class, ExtractVariableTest.class, FoldingTest.class}) public class EclipseTests { } diff --git a/test/eclipse/src/lombok/eclipse/TestUtils.java b/test/eclipse/src/lombok/eclipse/TestUtils.java new file mode 100644 index 0000000000..01b6f5488b --- /dev/null +++ b/test/eclipse/src/lombok/eclipse/TestUtils.java @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2025 The Project Lombok Authors. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package lombok.eclipse; + +import static org.junit.Assume.assumeTrue; + +import org.eclipse.core.runtime.adaptor.EclipseStarter; +import org.osgi.framework.Bundle; + +public class TestUtils { + public static void assumeMinJdtVersion(int version) { + Bundle coreBundle = getBundle("org.eclipse.jdt.core"); + + assumeTrue(coreBundle.getVersion().getMinor() >= version); + } + + public static Bundle getBundle(String name) { + for (Bundle bundle : EclipseStarter.getSystemBundleContext().getBundles()) { + if (bundle.getSymbolicName().equals(name)) { + return bundle; + } + } + return null; + } +} diff --git a/test/eclipse/src/lombok/eclipse/edit/FoldingTest.java b/test/eclipse/src/lombok/eclipse/edit/FoldingTest.java new file mode 100644 index 0000000000..d7ef01e5e6 --- /dev/null +++ b/test/eclipse/src/lombok/eclipse/edit/FoldingTest.java @@ -0,0 +1,108 @@ +/* + * Copyright (C) 2022-2025 The Project Lombok Authors. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package lombok.eclipse.edit; + +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertThat; + +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.util.Map; + +import org.eclipse.jdt.core.ICompilationUnit; +import org.eclipse.jdt.core.compiler.IScanner; +import org.eclipse.jdt.ui.text.folding.DefaultJavaFoldingStructureProvider; +import org.eclipse.jface.text.Document; +import org.eclipse.jface.text.IDocument; +import org.eclipse.jface.text.source.projection.ProjectionAnnotationModel; +import org.junit.BeforeClass; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +import lombok.eclipse.EclipseRunner; +import lombok.eclipse.SetupSingleFileTest; +import lombok.eclipse.TestUtils; +import lombok.permit.Permit; + +@RunWith(EclipseRunner.class) +public class FoldingTest { + + @Rule + public SetupSingleFileTest setup = new SetupSingleFileTest(); + + @BeforeClass + public static void versionCheck() { + TestUtils.assumeMinJdtVersion(43); + } + + @Test + public void locked() throws Exception { + ICompilationUnit cu = setup.getPackageFragment().getCompilationUnit("LockedFolding.java"); + + Map foldingMap = getFoldingMap(cu); + + assertThat(foldingMap.size(), is(6)); + } + + @Test + public void nonNull() throws Exception { + ICompilationUnit cu = setup.getPackageFragment().getCompilationUnit("NonNullFolding.java"); + + Map foldingMap = getFoldingMap(cu); + + assertThat(foldingMap.size(), is(6)); + } + + @Test + public void cleanup() throws Exception { + ICompilationUnit cu = setup.getPackageFragment().getCompilationUnit("CleanupFolding.java"); + + Map foldingMap = getFoldingMap(cu); + + assertThat(foldingMap.size(), is(6)); + } + + @Test + public void synch() throws Exception { + ICompilationUnit cu = setup.getPackageFragment().getCompilationUnit("SynchronizedFolding.java"); + + Map foldingMap = getFoldingMap(cu); + + assertThat(foldingMap.size(), is(6)); + } + + private Map getFoldingMap(ICompilationUnit cu) throws Exception { + Class contextClass = Class.forName("org.eclipse.jdt.ui.text.folding.DefaultJavaFoldingStructureProvider$FoldingStructureComputationContext"); + Constructor contextConstructor = Permit.getConstructor(contextClass, DefaultJavaFoldingStructureProvider.class, IDocument.class, ProjectionAnnotationModel.class, boolean.class, IScanner.class); + Method processMethod = Permit.getMethod(DefaultJavaFoldingStructureProvider.class, "processCompilationUnit", ICompilationUnit.class, contextClass); + Field foldingMapField = Permit.getField(contextClass, "fMap"); + + DefaultJavaFoldingStructureProvider defaultJavaFoldingStructureProvider = new DefaultJavaFoldingStructureProvider(); + Object context = Permit.newInstance(contextConstructor, defaultJavaFoldingStructureProvider, new Document(cu.getSource()), new ProjectionAnnotationModel(), true, null); + Permit.invoke(processMethod, defaultJavaFoldingStructureProvider, cu, context); + + return (Map) Permit.get(foldingMapField, context); + } + +}