Skip to content

Fix shortenFullyQualifiedTypes corrupting method references on fields (#3133) - #3134

Open
maxandersen wants to merge 1 commit into
diffplug:mainfrom
maxandersen:fix-3133-methodref-scope
Open

maxandersen wants to merge 1 commit into
diffplug:mainfrom
maxandersen:fix-3133-methodref-scope

Conversation

@maxandersen

@maxandersen maxandersen commented Oct 6, 2026 •

Copy link
Copy Markdown
Contributor

Fixes #3133.

Problem

shortenFullyQualifiedTypes() corrupts a method reference on a field/variable. JavaParser parses the scope of localVar.field::method as a ClassOrInterfaceType (the LHS of :: is type-ambiguous), so the formatter treated catalog.aliases::get as a fully-qualified type, rewrote it to aliases::get, and injected a bogus import catalog.aliases; — which does not compile. It only manifests on spotlessApply (not the read-only spotlessCheck), so a developer formats, commits, and then the build breaks.

Before / After (the bug)

// input
return find(catalog.aliases::get);   // catalog = local var, aliases = Map field

// shortenFullyQualifiedTypes() produced:
import catalog.aliases;              // bogus
return find(aliases::get);          // qualifier dropped -> won't compile

Fix

The expression-context path (FieldAccessExpr / MethodCallExpr scopes) already guards against variable.field shapes via a known-package / minimum-depth heuristic. A method-reference scope, however, reaches the ClassOrInterfaceType visitor instead, which trusts any package-like (lowercase first segment) dotted name — so catalog.aliases slips through.

This routes method-reference scopes through the same heuristic: the existing inline check in processExpressionScope is extracted into one shared isTrustedFullyQualifiedName(dottedName, knownPackages) used by both paths. A dotted name is trusted as a fully-qualified type only when the package (segments before the first upper-case segment) is already known from imports / the file's own package / java.lang, or has at least two segments. This:

  • leaves catalog.aliases::get alone (no upper-case type segment);
  • also leaves single-segment, non-package scopes alone, e.g. a static-imported or field config.Database::connect (upper-case simple name but config is not a known package) — which the old type path mis-shortened;
  • still shortens genuine FQN method references like java.lang.Integer::parseInt → Integer::parseInt.

No symbol resolution / classpath is used (consistent with the step's "when in doubt, leave it alone" design).

Tests

  • issue3133_methodReferenceOnFieldNotShortened — catalog.aliases::get left untouched, no bogus import.
  • methodReferenceWithSingleSegmentScopeLeftAlone — config.Database::connect left untouched.
  • methodReferenceOnRealTypeStillShortened — java.lang.Integer::parseInt → Integer::parseInt.

All 39 ShortenFullyQualifiedTypesStepTest cases pass.

Found while enabling the step on a real codebase (jbangdev/jbang), where catalog.aliases::get broke the build.

@maxandersen
maxandersen force-pushed the fix-3133-methodref-scope branch from ef52431 to e02c017 Compare October 6, 2026 23:38
…diffplug#3133)

`localVar.field::method` is parsed by JavaParser as a ClassOrInterfaceType
scope, so the formatter shortened `catalog.aliases::get` to `aliases::get`
and injected a bogus `import catalog.aliases;`, breaking compilation.

Method-reference scopes now go through the same known-package / minimum-depth
heuristic already used for other expression scopes (FieldAccessExpr /
MethodCallExpr) before being treated as a fully-qualified type. This also
leaves single-segment, non-package scopes alone (e.g. a static-imported or
field `config.Database::connect`), which the type path previously mis-shortened.

Adds regression tests: field method-ref left untouched; single-segment
non-package scope left untouched; genuine FQN type method-ref
(java.lang.Integer::parseInt) still shortened.
@maxandersen
maxandersen force-pushed the fix-3133-methodref-scope branch from e02c017 to cb79023 Compare October 6, 2026 23:42

This branch has not been deployed

No deployments
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.

shortenFullyQualifiedTypes corrupts method references on fields (x.field::m -> bogus import x.field)

1 participant