mirror of
https://github.com/l0ng-ai/openidea.git
synced 2026-11-08 08:52:12 +00:00
fix(java): support Lombok builders on compact record constructors (#950)
@Builder on a compact record constructor (record R(...) { @Builder R {} }) failed in the
editor: ECJ has not filled in a compact constructor's implicit parameters when Lombok
runs, so the builder got no fields and builder() was undefined. A patch to Lombok's
Eclipse HandleBuilder takes the parameters from the record components in that case.
The bundled agent is now lombok-1.18.48-openidea.3, built from v1.18.48 with #4059,
#4089 and java/lombok/patches/builder-compact-constructor.patch through the documented
Ant build; only HandleBuilder differs from openidea.2. The new agent path gives JDT LS a
fresh workspace. java/lombok/tests/verify.py compiles and runs the regression cases with
javac and with ECJ plus the agent.
Co-authored-by: egasa21 <[email protected]>
This commit is contained in:
1 parent
37f0897eb7
commit
ba8d224a46
11 files changed
+261
-24
No files matched your search
@@ -0,0 +1,139 @@
|
||||
import java.math.BigDecimal;
|
||||
import java.util.List;
|
||||
import lombok.Builder;
|
||||
import lombok.NonNull;
|
||||
import lombok.Singular;
|
||||
import lombok.SneakyThrows;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
public class BuilderCompactConstructor {
|
||||
@Builder(toBuilder = true)
|
||||
record Payment(String number, String phone, BigDecimal amount,
|
||||
BigDecimal serviceCharge, BigDecimal total, Status status) {
|
||||
@Builder
|
||||
Payment {
|
||||
if (serviceCharge == null) serviceCharge = BigDecimal.ZERO;
|
||||
}
|
||||
}
|
||||
|
||||
enum Status { PENDING, SUCCESS, EXPIRED }
|
||||
|
||||
record ConstructorOnly(String name, int count) {
|
||||
@Builder(toBuilder = true, setterPrefix = "with")
|
||||
ConstructorOnly {
|
||||
if (count < 0) throw new IllegalArgumentException("count");
|
||||
}
|
||||
}
|
||||
|
||||
@Builder(toBuilder = true)
|
||||
record TypeOnly(int count) {
|
||||
TypeOnly { count++; }
|
||||
}
|
||||
|
||||
record Generic<T>(@NonNull T value, @Singular List<String> tags) {
|
||||
static final String IGNORED = "not a component";
|
||||
|
||||
@Builder(toBuilder = true)
|
||||
Generic {}
|
||||
}
|
||||
|
||||
record Empty() {
|
||||
@Builder
|
||||
Empty {}
|
||||
}
|
||||
|
||||
record Canonical(String name, int count) {
|
||||
@Builder
|
||||
Canonical(String name, int count) {
|
||||
this.name = name.trim();
|
||||
this.count = count;
|
||||
}
|
||||
}
|
||||
|
||||
record Explicit(String name, int count) {
|
||||
@Builder
|
||||
Explicit(String name) { this(name, 7); }
|
||||
}
|
||||
|
||||
static class Ordinary {
|
||||
final String name;
|
||||
|
||||
@Builder
|
||||
@SneakyThrows
|
||||
Ordinary(String name) { this.name = name; }
|
||||
}
|
||||
|
||||
static class Parent {
|
||||
Parent(String name) {}
|
||||
}
|
||||
|
||||
static class ConstructorCalls extends Parent {
|
||||
final String name;
|
||||
|
||||
@SneakyThrows
|
||||
ConstructorCalls() {
|
||||
this("default");
|
||||
assert name.equals("default");
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
ConstructorCalls(@NonNull String name) {
|
||||
super(name);
|
||||
this.name = name;
|
||||
}
|
||||
}
|
||||
|
||||
@RequiredArgsConstructor
|
||||
static class GeneratedConstructor {
|
||||
final int initialized = 7;
|
||||
@NonNull final String name;
|
||||
}
|
||||
|
||||
@Builder(builderMethodName = "methodBuilder", builderClassName = "MethodBuilder")
|
||||
static String named(String name) { return name; }
|
||||
|
||||
public static void main(String[] args) {
|
||||
var payment = Payment.builder().number("inquiry").phone("123")
|
||||
.amount(BigDecimal.TEN).total(BigDecimal.TEN).status(Status.PENDING).build();
|
||||
assert payment.number().equals("inquiry");
|
||||
assert payment.phone().equals("123");
|
||||
assert payment.amount().equals(BigDecimal.TEN);
|
||||
assert payment.serviceCharge().equals(BigDecimal.ZERO);
|
||||
assert payment.total().equals(BigDecimal.TEN);
|
||||
assert payment.status() == Status.PENDING;
|
||||
assert payment.toBuilder().phone("456").build().phone().equals("456");
|
||||
assert Payment.builder().serviceCharge(BigDecimal.ONE).build().serviceCharge().equals(BigDecimal.ONE);
|
||||
|
||||
var constructor = ConstructorOnly.builder().withName("test").withCount(2).build();
|
||||
assert constructor.toBuilder().withCount(3).build().count() == 3;
|
||||
assert constructor.name().equals("test");
|
||||
try {
|
||||
ConstructorOnly.builder().withCount(-1).build();
|
||||
throw new AssertionError("compact constructor validation was bypassed");
|
||||
} catch (IllegalArgumentException expected) {}
|
||||
|
||||
assert TypeOnly.builder().count(2).build().count() == 3;
|
||||
assert Empty.builder().build().equals(new Empty());
|
||||
var generic = Generic.<String>builder().value("value").tag("one").build();
|
||||
assert generic.value().equals("value");
|
||||
assert generic.tags().equals(List.of("one"));
|
||||
assert generic.toBuilder().tag("two").build().tags().equals(List.of("one", "two"));
|
||||
try {
|
||||
Generic.<String>builder().build();
|
||||
throw new AssertionError("non-null check was bypassed");
|
||||
} catch (NullPointerException expected) {}
|
||||
|
||||
assert Canonical.builder().name(" test ").count(2).build().equals(new Canonical("test", 2));
|
||||
assert Explicit.builder().name("test").build().equals(new Explicit("test", 7));
|
||||
assert Ordinary.builder().name("test").build().name.equals("test");
|
||||
assert methodBuilder().name("test").build().equals("test");
|
||||
assert new ConstructorCalls().name.equals("default");
|
||||
assert new ConstructorCalls("explicit").name.equals("explicit");
|
||||
assert new GeneratedConstructor("generated").initialized == 7;
|
||||
assert new GeneratedConstructor("generated").name.equals("generated");
|
||||
try {
|
||||
new ConstructorCalls(null);
|
||||
throw new AssertionError("constructor non-null check was bypassed");
|
||||
} catch (NullPointerException expected) {}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user