Skip to content

processor: decouple codegen layer from builder internals (phase 0) - #299

Merged
AndreasIgel merged 6 commits into
java-helpers:mainfrom
igel-devin-ai:devin/1790369750-codegen-decoupling
Oct 22, 2026
Merged

AndreasIgel merged 6 commits into
java-helpers:mainfrom
igel-devin-ai:devin/1790369750-codegen-decoupling

Conversation

@igel-devin-ai

@igel-devin-ai igel-devin-ai commented Sep 25, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Closes #347. Part of #233.

Pure refactor, zero behavior change — decouples the code-generation layer inside processor so it no longer depends on builder-specific internals. This is Phase 0 of extracting a reusable simple-codegen library (~40 files) into its own module/artifact: do the decoupling before any git mv so the move commits stay rename-safe.

  • ClassCodeGenerator SPI (new): BuilderProcessor now talks to the generator through the interface. RoasterCodeGenerator implements it and receives a GenerationEnvironment (processing environment, logger, performance tracker, GenerationIssueReporter) instead of the whole ProcessingContext; the caller passes a generic SourceFormatter per generateClass call (resolved and cached per formatting mode by SourceFormatterResolver, which owns the formatterProfile compiler argument). Strict-mode reporting keeps its semantics via context::reportBasedOnStrictMode wired as the issue reporter.
  • CodeGenerationException (new): the code generation layer's own checked failure type — ClassCodeGenerator.generateClass and RoasterCodeGenerator no longer declare or throw the builder-specific BuilderException. The "builder class already exists" pre-check moved out of the generator into BuilderProcessor (assertTargetClassDoesNotExist), where BuilderException naturally lives; callers wrap CodeGenerationException at the boundary. classgen now imports zero builder-side exception types.
  • TypeMappingContext interface (new): exposes exactly the functions the type mapper needs (javax.lang.model lookups like getTypeElement, asElement, erasure, isAssignable, isSameType, directSupertypes, getPackageName, getAllMembers, plus isAccessibleFromGeneratedPackage, debug, shouldCopyTypeAnnotations and resolveGeneratedType). ProcessingContext implements it, so mapper signatures collapse back to (element, context) — the builder scope resolver and the builder configuration are no longer visible to the mapper. The interface is what moves into simple-codegen in phase 1.
  • findConstructorForBuilder takes the constructor marker annotation as a parameter instead of hardcoding SimpleBuilderConstructor (the builder caller passes it in).
  • ProcessingLogger no longer reads compiler options itself: the constructor is private behind the create(env) / createVerbose(env) named factories over a private Verbosity enum — callers pick the variant by intent; the -Averbose option is read once in BuilderProcessor.
  • CompilerOption interface (new) decouples CompilerArgumentsReader from the builder option enum; readBuilderConfiguration moved to BuilderConfigurationReader next to the other configuration sources.
  • Phase constants moved out of PerformanceTracker (now a pure protocol) into ProcessingPhases (pipeline phases + hierarchy) and GenerationPhases (codegen sub-phases); ActivePerformanceTracker takes them as constructor arguments.

Out of scope (next PRs)

  • Splitting ProcessingContext into a generic AnnotationProcessingContext + builder-specific context (separate PR — it touches many files on its own).
  • History-preserving git mv of the decoupled classes into simple-codegen / simple-codegen-roaster modules (Phase 1).

Test plan

  • mvn test -pl processor — all 528 tests pass
  • mvn -pl example clean compile — regenerated generated-example-builder/ tree is byte-identical (confirms zero behavior change)
  • license:check — all new files carry the license header

@devin-ai-integration
devin-ai-integration Bot force-pushed the devin/1790369750-codegen-decoupling branch from 353c73d to 4dde491 Compare September 25, 2026 21:23
@devin-ai-integration
devin-ai-integration Bot force-pushed the devin/1790369750-codegen-decoupling branch from 4dde491 to df5d937 Compare October 12, 2026 00:34
@devin-ai-integration
devin-ai-integration Bot force-pushed the devin/1790369750-codegen-decoupling branch 12 times, most recently from 932eee2 to 574a6a1 Compare October 22, 2026 20:24
Keep the assertThrows lambda wrapping only generateClass, so the expected
exception is clearly attributed to the generation call.
The five constructor tests only need a compilable input to trigger the lazy
profile initialization; a named constant states that intent.
@codecov

codecov Bot commented Oct 22, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ All tests successful. No failed tests found.

📢 Thoughts on this report? Let us know!

Types.directSupertypes/Elements.getAllMembers return List<? extends X> by
API design; same suppression the codebase already uses for such signatures.
The 'Factory for creating a source formatter' block lost its field when
RoasterSourceFormatterFactory was removed.
The wildcard return types are enforced by Types.directSupertypes and
Elements.getAllMembers, not a design choice.
@sonarqubecloud

Copy link
Copy Markdown

@AndreasIgel
AndreasIgel merged commit 96a4576 into java-helpers:main Oct 22, 2026
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Decouple codegen layer from builder internals in processor (phase 0)

2 participants