Skip to content

feat(jdk-codemodel): resolve Symbol.EnhancedForVariable, Symbol.CatchParameter, and Symbol.PatternBinding#118

Merged
deer merged 1 commit into
mainfrom
more_symbol
Jul 10, 2026
Merged

feat(jdk-codemodel): resolve Symbol.EnhancedForVariable, Symbol.CatchParameter, and Symbol.PatternBinding#118
deer merged 1 commit into
mainfrom
more_symbol

Conversation

@deer

@deer deer commented Jul 10, 2026

Copy link
Copy Markdown
Collaborator

Symbol.LocalVariable already resolved back to its declaring LocalVariableDeclaration, but three other locally-scoped variable-declaring constructs had no equivalent path: enhanced-for loop variables, catch-clause exception parameters, and instanceof/switch pattern bindings. Identifiers referencing them either fell back to a type-only Symbol.LocalVariable with a permanently-empty declaration, or in the catch/pattern-binding cases got no Symbol trait at all.

Added three new sealed Symbol variants — Symbol.EnhancedForVariable, Symbol.CatchParameter, and Symbol.PatternBinding — each carrying a direct Optional reference to its declaring construct (EnhancedFor, CatchClause, InstanceOf), matching the existing Symbol.LocalVariable shape. JdkExpressionConverter.resolveSymbol now handles the EXCEPTION_PARAMETER and BINDING_VARIABLE element kinds, and distinguishes enhanced-for locals from plain locals since javac reports both under LOCAL_VARIABLE. A direct object reference (rather than Symbol.Field's live/CodeModel-resolved style) is correct here because none of these constructs can outlive a rescan() independently of the identifiers referencing them — rescan() evicts and rebuilds a whole TypeDescriptor, and hence a whole method body, atomically.

Wiring this up surfaced a real construction-order bug: unlike LocalVariableDeclaration, whose scope is sibling statements converted after it returns, EnhancedFor and CatchClause each contain their own body as a child, which is the only place their variable can ever be referenced. The body was being converted as a constructor argument before the wrapping node existed, so there was nothing yet to register when resolving identifiers inside it. Fixed by giving both classes a package-visible two-phase construction path — ofPending(...) followed by a call-once completeBody(...) — so the declaring construct's element can be registered before its body is converted. The node stays fully immutable from any external caller's perspective. InstanceOf needed no such change, since a pattern binding's scope is the enclosing if/switch-case body, a sibling node converted afterward.

@deer deer merged commit b893e32 into main Jul 10, 2026
1 check passed
@deer deer deleted the more_symbol branch July 10, 2026 08:49
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.

1 participant