Regression in 2.12.0: @Builder.Constructor with newBuilder + stagedBuilder generates uncompilable code
Summary
Immutables 2.12.0 introduces a regression where using @Builder.Constructor with a style configuration containing both newBuilder = "builder" and stagedBuilder = true generates separate *BuilderStages.java files that cannot access the private constructors of the generated builder classes, resulting in compilation failures.
Environment
- Immutables Version: 2.12.0 (regression from 2.11.0)
- Java Version: 11, 17
- Build Tool: Gradle, Maven
Issue Description
When using @Builder.Constructor on a class with a style that combines newBuilder = "builder" and stagedBuilder = true, Immutables 2.12.0 generates separate *BuilderStages.java files that attempt to instantiate builder classes with private constructors, causing compilation failures.
Minimal Reproduction Case
Style Configuration
@Target({ElementType.PACKAGE, ElementType.TYPE})
@Retention(RetentionPolicy.CLASS)
@Value.Style(
builderVisibility = BuilderVisibility.PUBLIC,
newBuilder = "builder", // ← Key factor
stagedBuilder = true, // ← Key factor
strictBuilder = true,
defaults = @Value.Immutable(copy = false),
depluralize = true,
typeAbstract = "_*",
typeImmutable = "*",
visibility = ImplementationVisibility.PUBLIC
)
public @interface MyStyle {
}
Source Class
@MyStyle
public class FooBarKey {
protected final List<String> fields;
@Builder.Constructor
protected FooBarKey(String service,
String dataType,
String keyVersion,
List<String> identifiers) {
this.fields = List.of(service, dataType, keyVersion);
}
public List<String> getFields() {
return fields;
}
}
Expected Behavior (2.11.0)
- Generates single file:
FooBarKeyBuilder.java
- Everything embedded in one class
- Private constructors accessible within the same class
- ✅ Compiles successfully
Actual Behavior (2.12.0)
- Generates separate files:
FooBarKeyBuilder.java (contains private FooBarKeyBuilder() constructor)
FooBarKeyBuilderStages.java (contains return new FooBarKeyBuilder();)
- ❌ Compilation fails with:
error: FooBarKeyBuilder() has private access in FooBarKeyBuilder
return new FooBarKeyBuilder();
^
Generated Code Analysis
FooBarKeyBuilderStages.java (Problematic)
public final class FooBarKeyBuilderStages {
private FooBarKeyBuilderStages() {}
public static BuildStart start() {
return new FooBarKeyBuilder(); // ← Cannot access private constructor
}
// ...
}
FooBarKeyBuilder.java (Private Constructor)
public final class FooBarKeyBuilder {
// ...
private FooBarKeyBuilder() { // ← Private constructor
}
// ...
}
Root Cause
The issue occurs when:
@Builder.Constructor is used with newBuilder + stagedBuilder style
- Immutables 2.12.0 generates separate
*BuilderStages.java files (new behavior)
- These separate files cannot access the private constructors of builder classes
- In 2.11.0, everything was generated in one file where private constructors were accessible
Affected Patterns
This regression affects both:
@Builder.Constructor with the problematic style ❌
@Builder.Factory with the problematic style ❌
Both patterns fail with the same root cause.
Workarounds
✅ Workaround 1: Remove newBuilder
@Value.Style(
// newBuilder = "builder", // ← Remove this line
stagedBuilder = true, // ← Keep staged builders
// ... other settings
)
✅ Workaround 2: Remove stagedBuilder
@Value.Style(
newBuilder = "builder", // ← Keep custom naming
// stagedBuilder = true, // ← Remove staged builders
// ... other settings
)
Test Case
The issue can be reproduced by:
- Creating the style and class shown above
- Compiling with Immutables 2.12.0
- Observing the compilation failure in the generated
*BuilderStages.java file
Regression in 2.12.0:
@Builder.ConstructorwithnewBuilder + stagedBuildergenerates uncompilable codeSummary
Immutables 2.12.0 introduces a regression where using
@Builder.Constructorwith a style configuration containing bothnewBuilder = "builder"andstagedBuilder = truegenerates separate*BuilderStages.javafiles that cannot access the private constructors of the generated builder classes, resulting in compilation failures.Environment
Issue Description
When using
@Builder.Constructoron a class with a style that combinesnewBuilder = "builder"andstagedBuilder = true, Immutables 2.12.0 generates separate*BuilderStages.javafiles that attempt to instantiate builder classes with private constructors, causing compilation failures.Minimal Reproduction Case
Style Configuration
Source Class
Expected Behavior (2.11.0)
FooBarKeyBuilder.javaActual Behavior (2.12.0)
FooBarKeyBuilder.java(containsprivate FooBarKeyBuilder()constructor)FooBarKeyBuilderStages.java(containsreturn new FooBarKeyBuilder();)Generated Code Analysis
FooBarKeyBuilderStages.java (Problematic)
FooBarKeyBuilder.java (Private Constructor)
Root Cause
The issue occurs when:
@Builder.Constructoris used withnewBuilder + stagedBuilderstyle*BuilderStages.javafiles (new behavior)Affected Patterns
This regression affects both:
@Builder.Constructorwith the problematic style ❌@Builder.Factorywith the problematic style ❌Both patterns fail with the same root cause.
Workarounds
✅ Workaround 1: Remove
newBuilder✅ Workaround 2: Remove
stagedBuilderTest Case
The issue can be reproduced by:
*BuilderStages.javafile