Skip to content

refactor(completion): rewrite lambda scope-context onto the CST ancestor-walk#202

Merged
Hessesian merged 1 commit into
refactor/unified-resolutionfrom
refactor/cst-resolution-s7
Jul 4, 2026
Merged

refactor(completion): rewrite lambda scope-context onto the CST ancestor-walk#202
Hessesian merged 1 commit into
refactor/unified-resolutionfrom
refactor/cst-resolution-s7

Conversation

@Hessesian

Copy link
Copy Markdown
Owner

Summary

Task 7 of the CST lambda-triad collapse — the hardest step: the completion scope-context builder (build_lambda_scope) was the last architectural text scan, assembling LambdaScope for ALL enclosing lambdas via a 50-line backward line-walk. It is now a CST ancestor-walk.

  • New cst_lambda_scopes walk in the catalogue, exposed as CstQuery::lambda_scope (generic over D: InferDeps — TestDeps keeps driving the engine); tree via Indexer::live_doc_or_parse (universal CST path).
  • completion_context.rs consumes the walk; the direct lambda_receiver_type_from_context call and import are gone from this module (the function itself stays until Tasks 6/9 remove its remaining callers).
  • Deleted: the old collect_lambda_scopes/build_lambda_scope text bodies, parse_named_params, should_collect_named_param, lambda_label (relocated into the catalogue), line_before_cursor, SCOPE_SCAN_BACK_LINES.
  • Extracted cst_lambda_param_type_at, shared between the named-param resolver and the walk.

Behavior

Verified subsumption, not just claimed: the scope-name consumers (resolve_receiver, named_param_type) iterate innermost-first, so every previously-answered input keeps its answer. The walk additionally covers what the 50-line cap and single-line lambda-header parsing missed (decoy test: lambda opening 61 lines up — old path None, new path resolves). Known pre-existing quirk now more visible: resolve_labeled_receiver iterates outermost-first; duplicate this@label collisions spanning >50 lines can differ (follow-up noted in the ledger).

Verification

  • cargo test --bin kmp-lsp1441 passed, 0 failed (baseline 1438 + 2 named-arg decoys from the s5 base + 1 scope-walk decoy).
  • cargo clippy --bin kmp-lsp — clean. Decoy test RED→GREEN evidence in the task report.
  • Task-scoped review (spec + quality): Approved, no Critical/Important findings.

Stacked on #200 (refactor/cst-resolution-s5). Remaining plan steps: Task 6 (it text-body deletion), Task 8 (receiver.rs deletion), Task 9 (dead helpers).

🤖 Generated with Claude Code

https://claude.ai/code/session_01EJsyQ1UgF8uJYR4HgpxEBB

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Refactors completion’s lambda scope-context construction from a bounded backward text scan to a CST ancestor-walk, making scope resolution more structurally accurate and removing the 50-line scan cap.

Changes:

  • Add a CST catalogue walk for enclosing lambda scopes (CstQuery::lambda_scope / cst_lambda_scopes) and expose LambdaScopeInfo for consumption by completion.
  • Update completion scope-context building to use live_doc_or_parse + cursor-node lookup + CST walk, removing the old scan-based helpers.
  • Add a regression test proving scope discovery beyond the former backward-scan window.

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
src/indexer/infer/mod.rs Exposes CstQuery::lambda_scope as the new completion-facing CST lambda-scope query.
src/indexer/infer/cst_lambda.rs Implements the enclosing-lambda CST ancestor-walk and shared lambda-param typing helper.
src/indexer.rs Re-exports cursor_node_at and LambdaScopeInfo for feature-layer use.
src/features/completion.rs Updates CompletionContext::analyse call signature after removing line-based scope scanning needs.
src/features/completion_context.rs Replaces the old backward scan (build_lambda_scope et al.) with the CST-based scope walk via live_doc_or_parse.
src/features/completion_context_tests.rs Adds a test covering lambdas beyond the previous 50-line scan window and updates analyse callers.

Comment thread src/indexer/infer/cst_lambda.rs
…tor-walk

Replace completion_context's ~50-line backward brace-scan
(collect_lambda_scopes / build_lambda_scope) with a CST ancestor-walk:
CstQuery::lambda_scope walks every enclosing lambda_literal from the
cursor node and builds the full LambdaScope stack (it type, named
params, label), outermost first. The tree comes from
Indexer::live_doc_or_parse, so open files use the live tree and closed
files a transient parse.

The walk lives in the infer catalogue (cst_lambda::cst_lambda_scopes,
LambdaScopeInfo) and reuses the existing per-lambda typing engine
(locate_and_extract / cst_lambda_param_type_via_call) via the new
cst_lambda_param_type_at shared by cst_named_lambda_param_type.

Closes Gap 4 (completion_context.rs direct
lambda_receiver_type_from_context text call); the function itself stays
for its remaining it_this/cst_lambda callers. The ancestor walk also
lifts the 50-line SCOPE_SCAN_BACK_LINES ceiling — decoy test covers a
lambda opening 61 lines above the cursor.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EJsyQ1UgF8uJYR4HgpxEBB
@Hessesian Hessesian force-pushed the refactor/cst-resolution-s7 branch from 60cbeb1 to bf3673a Compare July 4, 2026 21:04
@Hessesian Hessesian merged commit 3823c40 into refactor/unified-resolution Jul 4, 2026
4 checks passed
Hessesian added a commit that referenced this pull request Jul 4, 2026
…tion path

Move-don't-rewrite relocation of the three survivors to their sole callers:
`uppercase_dotted_type_prefix` → chain.rs; `fun_trailing_lambda_this_type` and
`resolve_call_params` → cst_lambda.rs. receiver.rs and its `mod` declaration are
gone; every deleted function verified at zero references.

Also folds in the PR #202 review finding: `cursor_node_at` looked the cursor
line up via `str::lines()`, which yields no final empty line for `\n`-terminated
content — a cursor on the trailing empty line (end-of-file, e.g. right after
typing `items.forEach {` + Enter) returned `None`, failing `it` resolution and
the brace-repair gate before they could run, and emptying the completion
lambda-scope stack. The trailing-gap cursor now resolves on the last character
of the last content line (node ends are exclusive), whose node carries the
enclosing context. Pinned by `it_resolves_on_trailing_empty_line_at_eof`
(RED without the fix, GREEN with it).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EJsyQ1UgF8uJYR4HgpxEBB
Hessesian added a commit that referenced this pull request Jul 4, 2026
…tion path (#205)

* refactor(infer): resolve walk scopes and inner-call callees on CST nodes; retire the text heuristics

Problem A: the completion ancestor-walk (`lambda_scope_info`) resolved each
enclosing lambda's `it` type by slicing before-brace text and calling
`lambda_receiver_type_from_context`. It now resolves the `lambda_literal` node
directly through `cst_lambda_param_type_at`, the same path the innermost lambda
and named params already use.

Problem B: `f(args) { it }` nests as `outer_call(inner_call, call_suffix{lambda})`,
so the callee lived on the inner call_expression that the CST lookups did not
unwrap. `call_fn_and_qualifier` and `resolve_callee_chain` now unwrap that inner
call, so `loadProduct(a, b) { it }` and `items.mapNotNull(::t) { it }` resolve
through the node path. Decoys `trailing_lambda_it_infer_at_cursor` and
`it_element_type_with_call_args` are RED without the text call, GREEN via nodes.

With both call sites CST-native the text-heuristic family is production-dead, and
the pre-commit clippy gate (-D dead-code) forces its removal here rather than in
the follow-up: `lambda_receiver_type_from_context` + its ~15 internal helpers,
`lambda_receiver_type_named_arg_ml`, `fun_trailing_lambda_it_type`,
`inline_lambda_param_type`, `receiver_aware_params_from_text`, and the orphaned
`extract_named_arg_name`, `lambda_type_first_input`, `first_concrete_type_arg_str`.
`it_type_at_lambda` collapses onto `cst_lambda_param_type_at`; the never-constructed
`LambdaParamKind::This` walk branch and the enum go with it (`cst_it_or_this_type`
→ `cst_it_element_type`); the duplicate inner-call unwrap in `lambda_this_ctx` is
removed. receiver.rs keeps only the three signature utilities the CST resolvers
still call — the follow-up commit relocates them and deletes the file.

Tests: the 25 it_this_tests sites that unit-tested the text heuristic now drive
the production CST resolver via `cst_param_type` (same TestDeps, real lambda
nodes); indexer_tests conversions use full-document + cursor positions. Deleted
as implementation-detail or exact CST twins: trailing_lambda_it_from_fun_def,
trailing_lambda_it_with_method_call_arg, this_in_run_resolves_to_receiver_type,
extract_named_arg_name_test, extract_named_arg_* (args_tests, 5),
lambda_type_first_input_parses_correctly.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EJsyQ1UgF8uJYR4HgpxEBB

* refactor(infer): delete receiver.rs — the CST path is the only resolution path

Move-don't-rewrite relocation of the three survivors to their sole callers:
`uppercase_dotted_type_prefix` → chain.rs; `fun_trailing_lambda_this_type` and
`resolve_call_params` → cst_lambda.rs. receiver.rs and its `mod` declaration are
gone; every deleted function verified at zero references.

Also folds in the PR #202 review finding: `cursor_node_at` looked the cursor
line up via `str::lines()`, which yields no final empty line for `\n`-terminated
content — a cursor on the trailing empty line (end-of-file, e.g. right after
typing `items.forEach {` + Enter) returned `None`, failing `it` resolution and
the brace-repair gate before they could run, and emptying the completion
lambda-scope stack. The trailing-gap cursor now resolves on the last character
of the last content line (node ends are exclusive), whose node carries the
enclosing context. Pinned by `it_resolves_on_trailing_empty_line_at_eof`
(RED without the fix, GREEN with it).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EJsyQ1UgF8uJYR4HgpxEBB

* fix(infer): EOF remap targets the last character's start; repair gate goes tree-wide

The remap used `len() - 1` as a byte column, splitting a multi-byte final
codepoint. Fixing that surfaced a second hole the new pinning test caught:
a trailing comment is a tree-sitter extra attached outside the ERROR node,
so the chain-only gate saw no error and skipped repair for an unclosed
lambda ending in `// note`. The gate now treats any parse error in the tree
as license to repair when no enclosing lambda_literal exists — repair
self-verifies, so over-permission still converges to authoritative None.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EJsyQ1UgF8uJYR4HgpxEBB

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
@Hessesian Hessesian deleted the refactor/cst-resolution-s7 branch July 4, 2026 21:09
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.

2 participants