Skip to content

Detect build method by signature in builder contract check - #320

Merged
AndreasIgel merged 7 commits into
java-helpers:mainfrom
igel-devin-ai:devin/1791039226-signature-build-contract
Oct 3, 2026
Merged

AndreasIgel merged 7 commits into
java-helpers:mainfrom
igel-devin-ai:devin/1791039226-signature-build-contract

Conversation

@igel-devin-ai

@igel-devin-ai igel-devin-ai commented Oct 3, 2026 •

Copy link
Copy Markdown
Contributor

Closes #319

Sub-task of #301.

Summary

The builder-contract check in BuilderScopeResolver required the build method to be literally named build. This drops the name check so any accessible, non-static, parameterless method returning the referenced type satisfies the contract — matching SimpleBuildersBuilderProvider.findBuildMethods, which already detects build methods by signature.

  • BuilderScopeResolver.findBuildMethod selects the terminal method by signature: parameterless + return-type match + accessible from the generated builder's package (the last via the generic findMethods), excluding static methods and java.lang.Object signatures. When several candidates exist, build is preferred, then the alphabetically first name — the same convention create()/of() already use for static factories.
  • JavaLangAnalyser.isNoMethodOfObjectClass is now signature-based (was declaration-site), so overrides like StringBuilder.toString are also excluded — otherwise StringBuilder.toString() + StringBuilder(String) would falsely satisfy the contract for String. The shared check is used by setter detection and the contract alike.
  • ResolvedBuilder carries the selected buildMethodName and renders the terminal call itself (buildCall), consistent with BuilderInstantiation.instantiationCode/methodReference; generated code emits builder.<name>() instead of a hard-coded build().
  • Contract javadoc updated to describe the signature-based rule.

Tests

  • Resolution: non-build name (toTarget()) resolves and the picked name is recorded; inherited build methods count; build is preferred over other candidates; alphabetical fallback without build.
  • Rejection: void/parameterised/static/non-public returning methods, package-private methods inaccessible from the generated package, and StringBuilder for String (Object-signature guard).
  • mvn -pl processor test: 496 tests, 0 failures. Generated example builders unchanged.

@codecov

codecov Bot commented Oct 3, 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!

devin-ai-integration Bot and others added 6 commits October 3, 2026 15:05
Generated code emits a literal call to the builder's terminal method,
so the contract resolution must remember which method matched. When
several parameterless methods return the referenced type, 'build' is
preferred and the alphabetically first name is chosen otherwise -
matching the create()/of() preference used for static factories.

Co-Authored-By: Andreas Igel <andreas.igel@computacenter.com>
The contract-specific selection (build-name preference, non-static and
Object-method filtering) belongs to the resolver's contract check;
JavaLangAnalyser keeps only the generic findMethods lookup, which
already restricts candidates to members accessible from the generated
builder's package.

Co-Authored-By: Andreas Igel <andreas.igel@computacenter.com>
Like the instantiationCode/methodReference renderers on
BuilderInstantiation, the record now renders its own terminal call via
buildCall(builderExpression); MethodGeneratorUtil's private
calculateBuildExpression helper is no longer needed.

Co-Authored-By: Andreas Igel <andreas.igel@computacenter.com>
Renamed to isMethodOfObjectClass alongside isNoMethodOfObjectClass:
the existing check filters by declaring type and misses overrides like
StringBuilder.toString, while the signature comparison catches them.

Co-Authored-By: Andreas Igel <andreas.igel@computacenter.com>
…overage

The existing check filtered by declaring type and missed overrides like
StringBuilder.toString; now signature-based and shared between setter
detection and the contract's build-method selection.

New tests: StringBuilder rejected for String (Object-signature guard),
inherited build method selected, package-private build method rejected
when inaccessible.

Co-Authored-By: Andreas Igel <andreas.igel@computacenter.com>
Fold the new rejection cases into rejectedContractVariants and point the
resolve-side tests at the equivalent usageWithoutAnnotation probe instead
of a duplicated contractResult field (Sonar java:S5976).

Co-Authored-By: Andreas Igel <andreas.igel@computacenter.com>
@sonarqubecloud

sonarqubecloud Bot commented Oct 3, 2026

Copy link
Copy Markdown

@AndreasIgel
AndreasIgel merged commit c220be3 into java-helpers:main Oct 3, 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.

Detect build function by signature, not by name, in referenced-builder contract

2 participants