Repository navigation
fix(resolve): keep Rust method calls that lost their receiver off functions and std names - #307
Merged
Merged
Conversation
added 2 commits
October 3, 2026 14:03
…ctions and std names The extractor keeps a Rust receiver only for an identifier, `self`, one `self.<field>` hop and a `path::call()` receiver. Any other receiver (a plain call, a deeper field chain, `vec![…]`, `?`, `.await`, a chain broken across lines) leaves the bare method name, so `root().join(name)` resolved like a bare call and bound the project's only `join` method at 0.9. In this repository, 964 calls from 169 files landed on `LineIndex::join`. Resolution now reads such a call's shape from its source line, where the ref's column is the call expression's start. These KEEP-RUST rules are stricter than upstream: - R1: a call with neither receiver nor path never binds a method; - R2: a call whose receiver was dropped binds only a method, never through a `use` binding and never by a fuzzy guess; - R3: such a call binds nothing when its name is one of 307 std methods; - R4: a bare `Ok`/`Err`/`Some`/`None` binds a project variant only when a `use` naming it, or a glob, is in its own block and module. `rust_code_mask` moves from the Tauri resolver into a shared `rust_lexical` module, which also records where each `use` item is visible. Extraction version 21. On this repository the four rules remove 3,284 edges (R3 2,743, R1 296, R4 193, R2 52). Every sampled one is false, and no golden moves.
`RustUseScopes` recorded every identifier of a `use` item, so `use crate::outcome::Outcome::Err as Failure;` counted as importing `Err`, and a later bare `Err(..)`, which is the prelude's, kept its false binding to the project variant. A use tree now contributes: - each leaf's alias (`as _` contributes nothing); - otherwise the leaf's last path segment; - for a `self` leaf, the segment before its group. The tier-2 fixture gains the aliased case, and a unit test covers aliases, braced groups, `self` leaves and `as _`.
Codecov Report❌ Patch coverage is
@@ Coverage Diff @@
## main #307 +/- ##
==========================================
+ Coverage 95.06% 95.07% +0.01%
==========================================
Files 197 198 +1
Lines 108505 108998 +493
==========================================
+ Hits 103147 103634 +487
- Misses 5358 5364 +6
... and 3 files with indirect coverage changes 🚀 New features to boost your workflow:
|
Merged
sunerpy
added a commit
that referenced
this pull request
Oct 3, 2026
The release record for #307: the release PR merge and its tag SHA, the workflow run, the published digest, and the black-box acceptance against v0.53.0. Current alignment now lists v0.53.1 in place of the unreleased note. Co-authored-by: CodeGraph Test <codegraph@example.invalid>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
join,iter,as_strandis_emptytopped this repository's most-called lists because of false edges. The extractor keeps a Rust receiver only for:self;self.<field>hop;path::call()receiver.Every other receiver leaves the bare method name. That covers a plain call
root(), a deeper field chain,vec![…],?,.await, and a chain broken across lines; upstream'stree-sitter.tsdoes the same.Resolution then treated
root().join(name)as a bare call, and the project's onlyjoinmethod won at exact-match 0.9. That method isLineIndex::join, which 169 files "called" 964 times.Upstream has no Rust counterpart to its JS/TS gates (#1987, #1714/#1857, #1683), so these are KEEP-RUST rules, stricter than upstream. The design passed the kirocodex plan gate in round 4 (6 → 1 → 1 → 0); the plan is
/config/.claude/plans/p15-rust-receiver-gates.md.The rules
Shape. It is read from the source line. A ref's column is the call expression's start, so:
.name(follows within 4 KiB;callsrefhelper())root().join(x))use(decided before import resolution), never by a fuzzy guessRUST_STD_METHODS(307 std names, sorted, pinned)Ok/Err/Some/Noneusenaming it, or a glob, is in its own block and moduleR4's names are the ones a
usebrings in. A leaf introduces its alias, else its last path segment, and aselfleaf the segment before its group. Souse …::Err as Failurebrings inFailure, and a later bareErr(..)is still the prelude's;as _brings in nothing. This came from review round 1 (second commit,ec1bf0f).R4's scope is Rust's own. A
useis visible in its block and that block's nested blocks. An inlinemod NAME { … }is a boundary, crossed only by the child's ownuse super::…. The scope is computed once per file from masked source (SourceFacts::rust_use_in_scope). If the braces do not balance, everyusecounts file-wide, which can only keep today's binding.rust_code_maskmoves unchanged fromframeworks/tauri.rsinto a sharedrust_lexicalmodule; Tauri's 59 tests pass as they were. Extraction version 20 → 21.Tests
Red first. On
main, the three negative integration tests fail for the stated reason (/tmp/evidence-p15/red-tier2.log):helper()bindsStore::helper;dropped_joinbindsLineIndex::join;Err(2)bindsOutcome::Err.The shape precondition and the positives pass on
maintoo.Integration fixture.
tests/fixtures/tier2/rust_receiver/holds five tests intier2_false_edges.rs, run through the full extract → resolve pipeline:It first asserts the bare
join/takeshape the extractor givesroot().join(x), a broken chain, andmake_opt().take().None of those binds
LineIndex::join/Cache::take, nor the imported free fnjoin.These still bind:
self.join(..);LineIndex::join(..);store().nodes_by_ids(..).A bare
helper()and the importedjoin(..)bind their free fns, andonly_method()binds nothing.Err/OkbindOutcome::*at:use super::*;But not:
use …::Err as Failure.Unit tests.
rust_call_shape: bare, turbofish, dropped receiver, broken chain,joinedvsjoin, a range end, off-line columns, non-Rust and non-callsrefs.match_by_exact_name, including "no source keeps today's binding".RUST_STD_METHODSsorted.RustUseScopes: blocks, modules, a comment,use<>, a braced tree past a comment, unbalanced braces, and the names a use tree introduces (aliases,as _, braced groups,selfleaves).Verification
make pre-cipassed on the headec1bf0f: 4360 Rust tests, 0 failed; 569 frontend tests; bundle byte check; archive smoke (/tmp/evidence-p15/pre-ci-r2.log). The goldens, the rule diff, thesynccheck and the 0.53.1 acceptance dry run below were re-run with a release binary built from that head (sha256132a1621…; the*-r2.logfiles). The numbers did not change: this repository imports no prelude-named variant under an alias.docs/equivalence.md's recipe; all 19 are identical (golden-drift.log). No corpus has a Rust call resolved by exact-match or fuzzy.6cc9507was indexed with the0.53.0and new binaries (diff_rules.py,removed-samples.txt):drop(x)→ aDropimpl,.context()→ an import,.iter()→OrderedMap::iter,Err(…)→Dispatch::Err.node(…)reaches its free fn instead of a method. In 13,.extract(…)reachesFrameworkResolver::extract, now under the ambiguity ceiling. One new false edge:.id()→ a fixture'sid.index_root.join("diagnostics").join(&filename)the outer, dropped-receiver call is gone; the innerindex_root.joinstill binds through the member path's word overlap ("index"), which this change leaves alone.sync == index --force. After movingLineIndex::join, nodes, edges and unresolved refs are identical (sync-equiv.log).mainvs this change, three runs each: 7.80 / 7.71 / 7.64 s vs 7.66 / 7.63 / 7.51 s (timing.log).Known limits (from the plan)
self.store().get(id)); this is #1987's trade-off.is_match,body,json) and std types outside the list (SocketAddr::port) are not gated: 451 Method-shaped edges survive, and a sample of 20 had about six such cases.index_root.join) still bind std names through word overlap. A #1987-style gate there would be a follow-up.🤖 Generated with Claude Code