Skip to content

refactor(parser): separate binding patterns from assignment targets 🧩 - #225

Merged
timfennis merged 2 commits into
masterfrom
refactor/ast-binding-targets
Sep 9, 2026
Merged

timfennis merged 2 commits into
masterfrom
refactor/ast-binding-targets

Conversation

@timfennis

Copy link
Copy Markdown
Owner

Lvalue represented both declarations and writes, forcing the analyser and compiler to handle impossible index/member declarations. Split it into BindingPattern for declarations, parameters, and loop binders, and AssignmentTarget for writes. Update parser, analyser, compiler, and LSP consumers around those roles.

Both representations have location wrappers with a NodeId and complete source span, including nested destructuring elements. Consuming expression conversion preserves node identities; receivers and indices remain expressions. Retain identifier token spans separately so parenthesized targets still give precise definition locations. Existing resolution and inferred-type fields remain in the AST for the current analyser.

Preserve the current accepted syntax, overload resolution, assignment evaluation order, and closure behavior. Function parameters still require identifiers, and member targets remain forbidden inside destructuring.

Validation:

  • cargo test --workspace --locked: 659 tests passed, including six new parser tests and two LSP definition tests.
  • cargo build --no-default-features --locked: passed.
  • cargo fmt --all --check and git diff --check: passed.

AI disclosure: Codex implemented this refactor, added the tests, and wrote this PR description from a design discussed with the maintainer.

Comment thread ndc_parser/src/expression.rs Outdated
Comment thread ndc_parser/src/expression.rs Outdated
@timfennis
timfennis merged commit 3274693 into master Sep 9, 2026
1 check passed
@timfennis
timfennis deleted the refactor/ast-binding-targets branch September 9, 2026 15:13
timfennis added a commit that referenced this pull request Oct 13, 2026
The name resolver being built on `feat/new-analyser` will resolve type
names through lexical scopes. Struct names are scoped like values, but
they live in a separate type namespace. The resolver records its results
in side tables keyed by `NodeId`, but type annotations had no node ids.
Each name needs its own key: in `Map<P, Q>`, `P` and `Q` resolve
separately. Parameter annotations can't borrow an owner's id either,
because `FunctionParameter` has no node of its own. This follows the
same approach as #225, which gave binding patterns and assignment
targets their own ids.

- `TypeExpr::Name` gets an `id: NodeId`. Both places that build one
assign a fresh id: ordinary annotations, and the cast fast path for `x
as Int < 5`.
- The current analyser ignores the new field.
- New parser tests check that every name in a nested annotation, and in
the cast fast path, gets its own id.

`TypeExpr::Tuple` gets no id, because tuple types have no name to
resolve. Nothing in the workspace compares annotations for equality, so
including the id in the derived `PartialEq` changes no behaviour.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
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