diff --git a/src/eclipseAgent/lombok/eclipse/agent/EclipsePatcher.java b/src/eclipseAgent/lombok/eclipse/agent/EclipsePatcher.java index c5b0ea07..c176f515 100644 --- a/src/eclipseAgent/lombok/eclipse/agent/EclipsePatcher.java +++ b/src/eclipseAgent/lombok/eclipse/agent/EclipsePatcher.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009-2023 The Project Lombok Authors. + * Copyright (C) 2009-2024 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 @@ -100,6 +100,7 @@ public class EclipsePatcher implements AgentLauncher.AgentLaunchable { patchJavadoc(sm); patchASTConverterLiterals(sm); patchASTNodeSearchUtil(sm); + patchFieldInitializer(sm); patchPostCompileHookEcj(sm); @@ -1029,6 +1030,42 @@ public class EclipsePatcher implements AgentLauncher.AgentLaunchable { .build()); } + private static void patchFieldInitializer(ScriptManager sm) { + sm.addScriptIfWitness(OSGI_TYPES, ScriptBuilder.addField() + .targetClass("org.eclipse.jdt.internal.core.CompilationUnitStructureRequestor") + .fieldName("$fieldInfo") + .fieldType("Ljava/lang/Object;") + .build()); + + sm.addScriptIfWitness(OSGI_TYPES, ScriptBuilder.addField() + .targetClass("org.eclipse.jdt.internal.core.CompilationUnitStructureRequestor") + .fieldName("$sourceFieldElementInfo") + .fieldType("Lorg/eclipse/jdt/internal/core/SourceFieldElementInfo;") + .build()); + + sm.addScriptIfWitness(OSGI_TYPES, ScriptBuilder.exitEarly() + .target(new MethodTarget("org.eclipse.jdt.internal.core.CompilationUnitStructureRequestor", "exitField", "void", "int", "int", "int")) + .decisionMethod(new Hook("lombok.launch.PatchFixesHider$FieldInitializer", "storeFieldInfo", "boolean", "org.eclipse.jdt.internal.core.CompilationUnitStructureRequestor")) + .request(StackRequest.THIS) + .transplant() + .build()); + + sm.addScriptIfWitness(OSGI_TYPES, ScriptBuilder.wrapMethodCall() + .target(new MethodTarget("org.eclipse.jdt.internal.core.CompilationUnitStructureRequestor", "exitField", "void", "int", "int", "int")) + .methodToWrap(new Hook("org.eclipse.jdt.internal.core.SourceFieldElementInfo", "", "void")) + .wrapMethod(new Hook("lombok.launch.PatchFixesHider$FieldInitializer", "storeSourceFieldElementInfo", "void", "org.eclipse.jdt.internal.core.SourceFieldElementInfo", "org.eclipse.jdt.internal.core.CompilationUnitStructureRequestor")) + .requestExtra(StackRequest.THIS) + .transplant() + .build()); + + sm.addScriptIfWitness(OSGI_TYPES, ScriptBuilder.wrapReturnValue() + .target(new MethodTarget("org.eclipse.jdt.internal.core.CompilationUnitStructureRequestor", "exitField", "void", "int", "int", "int")) + .wrapMethod(new Hook("lombok.launch.PatchFixesHider$FieldInitializer", "overwriteInitializer", "void", "org.eclipse.jdt.internal.core.CompilationUnitStructureRequestor")) + .request(StackRequest.THIS) + .transplant() + .build()); + } + private static void patchCrossModuleClassLoading(ScriptManager sm) { sm.addScriptIfWitness(OSGI_TYPES, ScriptBuilder.wrapReturnValue() .target(new MethodTarget("org.eclipse.jdt.internal.compiler.parser.Parser", "")) diff --git a/src/eclipseAgent/lombok/launch/PatchFixesHider.java b/src/eclipseAgent/lombok/launch/PatchFixesHider.java index c88f3171..3bdf7974 100755 --- a/src/eclipseAgent/lombok/launch/PatchFixesHider.java +++ b/src/eclipseAgent/lombok/launch/PatchFixesHider.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010-2023 The Project Lombok Authors. + * Copyright (C) 2010-2024 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 @@ -52,7 +52,9 @@ import org.eclipse.jdt.core.dom.SimpleName; import org.eclipse.jdt.core.dom.SingleVariableDeclaration; import org.eclipse.jdt.core.dom.Type; import org.eclipse.jdt.core.search.SearchMatch; +import org.eclipse.jdt.internal.compiler.ISourceElementRequestor.FieldInfo; import org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration; +import org.eclipse.jdt.internal.compiler.ast.AbstractVariableDeclaration; import org.eclipse.jdt.internal.compiler.ast.Annotation; import org.eclipse.jdt.internal.compiler.ast.Expression; import org.eclipse.jdt.internal.compiler.ast.FieldDeclaration; @@ -61,7 +63,9 @@ import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration; import org.eclipse.jdt.internal.compiler.lookup.BlockScope; import org.eclipse.jdt.internal.compiler.lookup.TypeBinding; import org.eclipse.jdt.internal.compiler.parser.Parser; +import org.eclipse.jdt.internal.core.CompilationUnitStructureRequestor; import org.eclipse.jdt.internal.core.SourceField; +import org.eclipse.jdt.internal.core.SourceFieldElementInfo; import org.eclipse.jdt.internal.core.dom.rewrite.NodeRewriteEvent; import org.eclipse.jdt.internal.core.dom.rewrite.RewriteEvent; import org.eclipse.jdt.internal.core.dom.rewrite.TokenScanner; @@ -935,6 +939,68 @@ final class PatchFixesHider { } } + public static class FieldInitializer { + public static final Field INFO_STACK; + public static final Field FIELD_INFO; + public static final Field SOURCE_FIELD_ELEMENT_INFO; + public static final Field INITIALIZATION_SOURCE; + public static final Field NODE; + public static final boolean INITIALIZED; + + static { + INFO_STACK = Permit.permissiveGetField(CompilationUnitStructureRequestor.class, "infoStack"); + FIELD_INFO = Permit.permissiveGetField(CompilationUnitStructureRequestor.class, "$fieldInfo"); + SOURCE_FIELD_ELEMENT_INFO = Permit.permissiveGetField(CompilationUnitStructureRequestor.class, "$sourceFieldElementInfo"); + INITIALIZATION_SOURCE = Permit.permissiveGetField(SourceFieldElementInfo.class, "initializationSource"); + NODE = Permit.permissiveGetField(FieldInfo.class, "node"); + INITIALIZED = INFO_STACK != null && FIELD_INFO != null && SOURCE_FIELD_ELEMENT_INFO != null && INITIALIZATION_SOURCE != null && NODE != null; + } + + public static boolean storeFieldInfo(CompilationUnitStructureRequestor compilationUnitStructureRequestor) { + try { + if (INITIALIZED) { + Stack infoStack = Permit.get(INFO_STACK, compilationUnitStructureRequestor); + Object fieldInfo = infoStack.peek(); + Permit.set(FIELD_INFO, compilationUnitStructureRequestor, fieldInfo); + } + } catch (Exception e) { + // do not break eclipse + } + return false; + } + public static void storeSourceFieldElementInfo(SourceFieldElementInfo fieldInfo, CompilationUnitStructureRequestor compilationUnitStructureRequestor) { + try { + if (INITIALIZED) { + Permit.set(SOURCE_FIELD_ELEMENT_INFO, compilationUnitStructureRequestor, fieldInfo); + } + } catch (Exception e) { + // do not break eclipse + } + } + + public static void overwriteInitializer(CompilationUnitStructureRequestor compilationUnitStructureRequestor) { + try { + if (INITIALIZED) { + FieldInfo fieldInfo = Permit.get(FIELD_INFO, compilationUnitStructureRequestor); + Permit.set(FIELD_INFO, compilationUnitStructureRequestor, null); + + SourceFieldElementInfo sourceFieldElementInfo = Permit.get(SOURCE_FIELD_ELEMENT_INFO,compilationUnitStructureRequestor); + Permit.set(SOURCE_FIELD_ELEMENT_INFO, compilationUnitStructureRequestor, null); + + if (sourceFieldElementInfo.getInitializationSource() != null) { + AbstractVariableDeclaration node = Permit.get(NODE, fieldInfo); + + if (PatchFixes.isGenerated(node)) { + Permit.set(INITIALIZATION_SOURCE, sourceFieldElementInfo, node.initialization.toString().toCharArray()); + } + } + } + } catch (Exception e) { + // do not break eclipse + } + } + } + public static class Tests { public static StringBuffer printMethod(AbstractMethodDeclaration methodDeclaration, int tab, StringBuffer output, TypeDeclaration type) { return (StringBuffer) printMethod(methodDeclaration, tab, (Object) output, type); diff --git a/test/eclipse/resource/noerrors/fieldNameConstantsInAnnotation/Annotation.java b/test/eclipse/resource/noerrors/fieldNameConstantsInAnnotation/Annotation.java new file mode 100644 index 00000000..e3ecb40f --- /dev/null +++ b/test/eclipse/resource/noerrors/fieldNameConstantsInAnnotation/Annotation.java @@ -0,0 +1,5 @@ +package pkg; + +public @interface Annotation { + String value(); +} \ No newline at end of file diff --git a/test/eclipse/resource/noerrors/fieldNameConstantsInAnnotation/Constants.java b/test/eclipse/resource/noerrors/fieldNameConstantsInAnnotation/Constants.java new file mode 100644 index 00000000..fad3811f --- /dev/null +++ b/test/eclipse/resource/noerrors/fieldNameConstantsInAnnotation/Constants.java @@ -0,0 +1,8 @@ +package pkg; + +import lombok.experimental.FieldNameConstants; + +@FieldNameConstants +public class Constants { + String test; +} \ No newline at end of file diff --git a/test/eclipse/resource/noerrors/fieldNameConstantsInAnnotation/Usage.java b/test/eclipse/resource/noerrors/fieldNameConstantsInAnnotation/Usage.java new file mode 100644 index 00000000..5f8880ef --- /dev/null +++ b/test/eclipse/resource/noerrors/fieldNameConstantsInAnnotation/Usage.java @@ -0,0 +1,5 @@ +package pkg; + +@Annotation(Constants.Fields.test) +public class Usage { +} \ 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 5fba9903..2e219995 100644 --- a/test/eclipse/src/lombok/eclipse/EclipseTests.java +++ b/test/eclipse/src/lombok/eclipse/EclipseTests.java @@ -5,6 +5,7 @@ import org.junit.runners.Suite; import org.junit.runners.Suite.SuiteClasses; import lombok.eclipse.cleanup.CleanupTest; +import lombok.eclipse.compile.NoErrorsTest; import lombok.eclipse.edit.SelectTest; import lombok.eclipse.refactoring.ExtractInterfaceTest; import lombok.eclipse.refactoring.InlineTest; @@ -12,7 +13,7 @@ import lombok.eclipse.refactoring.RenameTest; import lombok.eclipse.references.FindReferencesTest; @RunWith(Suite.class) -@SuiteClasses({ExtractInterfaceTest.class, RenameTest.class, SelectTest.class, CleanupTest.class, FindReferencesTest.class, InlineTest.class}) +@SuiteClasses({ExtractInterfaceTest.class, RenameTest.class, SelectTest.class, CleanupTest.class, FindReferencesTest.class, InlineTest.class, NoErrorsTest.class}) public class EclipseTests { } diff --git a/test/eclipse/src/lombok/eclipse/compile/NoErrorsTest.java b/test/eclipse/src/lombok/eclipse/compile/NoErrorsTest.java new file mode 100644 index 00000000..9fd02d9f --- /dev/null +++ b/test/eclipse/src/lombok/eclipse/compile/NoErrorsTest.java @@ -0,0 +1,68 @@ +package lombok.eclipse.compile; + +import static org.junit.Assert.assertTrue; + +import java.util.ArrayList; +import java.util.List; + +import org.eclipse.jdt.core.ICompilationUnit; +import org.eclipse.jdt.core.IProblemRequestor; +import org.eclipse.jdt.core.WorkingCopyOwner; +import org.eclipse.jdt.core.compiler.IProblem; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +import lombok.eclipse.EclipseRunner; +import lombok.eclipse.SetupSingleFileTest; + +@RunWith(EclipseRunner.class) +public class NoErrorsTest { + + @Rule + public SetupSingleFileTest setup = new SetupSingleFileTest(); + + @Test + public void fieldNameConstantsInAnnotation() throws Exception { + ICompilationUnit cu = setup.getPackageFragment().getCompilationUnit("Usage.java"); + + final List problems = new ArrayList(); + final IProblemRequestor requestor = new IProblemRequestor() { + @Override + public void acceptProblem(IProblem problem) { + problems.add(problem); + } + + @Override + public void beginReporting() { + problems.clear(); + } + + @Override + public void endReporting() { + } + + @Override + public boolean isActive() { + return true; + } + }; + + WorkingCopyOwner workingCopyOwner = new WorkingCopyOwner() { + @Override + public IProblemRequestor getProblemRequestor(ICompilationUnit workingCopy) { + return requestor; + } + }; + + ICompilationUnit workingCopy = cu.getWorkingCopy(workingCopyOwner, null); + try { + workingCopy.reconcile(ICompilationUnit.NO_AST, true, true, workingCopy.getOwner(), null); + } finally { + workingCopy.discardWorkingCopy(); + } + + System.out.println(problems); + assertTrue(problems.isEmpty()); + } +}