Skip to content

Commit

Permalink
[fixes projectlombok#3529] Set real field initializer source
Browse files Browse the repository at this point in the history
Eclipse copies the field initializer source code if it is a constant.
For generated nodes we have to read it from the ast node and not from
the source file.
  • Loading branch information
Rawi01 committed Jan 15, 2024
1 parent fd55c7a commit f277791
Show file tree
Hide file tree
Showing 7 changed files with 193 additions and 3 deletions.
39 changes: 38 additions & 1 deletion src/eclipseAgent/lombok/eclipse/agent/EclipsePatcher.java
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -100,6 +100,7 @@ public String mapResourceName(int classFileFormatVersion, String resourceName) {
patchJavadoc(sm);
patchASTConverterLiterals(sm);
patchASTNodeSearchUtil(sm);
patchFieldInitializer(sm);

patchPostCompileHookEcj(sm);

Expand Down Expand Up @@ -1029,6 +1030,42 @@ private static void patchASTNodeSearchUtil(ScriptManager sm) {
.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", "<init>", "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", "<clinit>"))
Expand Down
68 changes: 67 additions & 1 deletion src/eclipseAgent/lombok/launch/PatchFixesHider.java
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -52,7 +52,9 @@
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;
Expand All @@ -61,7 +63,9 @@
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;
Expand Down Expand Up @@ -935,6 +939,68 @@ public static String[] getRealCodeBlocks(String[] blocks, SourceProvider sourceP
}
}

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);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package pkg;

public @interface Annotation {
String value();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package pkg;

import lombok.experimental.FieldNameConstants;

@FieldNameConstants
public class Constants {
String test;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package pkg;

@Annotation(Constants.Fields.test)
public class Usage {
}
3 changes: 2 additions & 1 deletion test/eclipse/src/lombok/eclipse/EclipseTests.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,15 @@
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;
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 {

}
68 changes: 68 additions & 0 deletions test/eclipse/src/lombok/eclipse/compile/NoErrorsTest.java
Original file line number Diff line number Diff line change
@@ -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<IProblem> problems = new ArrayList<IProblem>();
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());
}
}

0 comments on commit f277791

Please sign in to comment.