Repository navigation
fix(rust): a name written through a path is what the path names, not a same-file namesake - #2711
Merged
Merged
Conversation
…a same-file namesake
A Rust name written through a path is an item of what the path names: a
module (`ast::Pattern`, `io::Error`), a type (`Source::Record { … }`) or a
crate (`mio::Interest`, `std::ffi::OsStr`). isRustNameInScope accepted any
candidate in the file the path is written in, whatever the path said, and
a std path only when the candidate was in another file:
- clap's `std::ffi::OsStr` linked to its own `struct OsStr`, and
`io::Error`, `fmt::Error` and `fmt::Result` in `error/mod.rs` to its own
`Error` and `Result`;
- tokio's `mio::Interest` linked to its own `Interest`, and
`std::time::Instant` to its own `Instant`;
- serde's tests' `Token::Struct { … }` literals linked to their own
`Struct`s, and serde_derive's `syn::Path { … }` to a `Default::Path`
variant;
- gleam's `ast::Pattern` linked to the `enum Pattern` beside it, and
`BinaryBuilderPosition::RecordAttribute { … }` to another enum's
variant of that name.
Fix, in the path branch of isRustNameInScope:
- A path from std, core, alloc or a crate the manifests depend on names
nothing of the project's, in any file, unless the project is that crate
or a `use` in the file binds the root.
- The same-file shortcut is gone. A candidate is the path's when its
file is the path's module, its qualified name holds the segment (a
type's variant or method), or it sits in an inline `mod` of that name
in the file (isInRustInlineModule; rustfmt closes the block at the
indentation it opens at).
- Through a module a `use` binds, a candidate must be in a crate that
`use`'s path can reach (rustUseCanReach, as for a bare name): tokio's
`io::Handle` under `use crate::runtime::io` is tokio's own, not
tokio-test's `io.rs` `Handle` (a dev-dependency).
Tests: rust-path-same-file.test.ts, 6 tests, on both engines; 5 fail
on main (the sixth is the inline-module guard), and the crate-reach one
also fails with only that rule switched off.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
colbymchenry
added a commit
that referenced
this pull request
Oct 20, 2026
…not its indentation (#2713) PR #2711 and PR #2702 landed a way each to tell which inline `mod` a place in a Rust file is in. #2711's isInRustInlineModule scanned lines upward for `mod <name> {` and trusted rustfmt's indentation to find where the block closes. #2702's rustModBlocks scans the braces, skipping strings, raw strings, char literals and comments, and keeps the result in rustUsesOf. isInRustInlineModule now asks rustBlockAt for the innermost block around the candidate, as rustDeclaredItems does, so there is one answer to that question. What changes: - An item written without indentation inside `mod flat { … }` is found through `flat::Flat`. The indentation rule rejected it. - `outer::Shell` is only `outer`'s own `Shell`, not one declared in a `mod deep` inside it. The upward scan accepted any item below the `mod outer {` line. - A file whose braces don't all close says nothing, as it does for the `use` walk of PR #2702. The A/B (main 818e780 vs this change; axum, tokio, clap, serde, ripgrep, gleam, dioxus, and the gin, serilog, flask and express controls) is byte-identical. rust-path-same-file.test.ts gains the two cases. The `flat::Flat` one fails on main. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
danusha2345
pushed a commit
to danusha2345/codegraph
that referenced
this pull request
Oct 21, 2026
…olbymchenry#2308 follow-up for colbymchenry#2711's test) The same change went into the rebased colbymchenry#2308 branch. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
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.
Problem
Found while triaging the A/B for #2707. A Rust name written through a path is an item of what the path names: a module (
ast::Pattern,io::Error), a type (Source::Record { … }) or a crate (mio::Interest,std::ffi::OsStr).isRustNameInScopeaccepted any candidate in the file the path is written in, whatever the path said. It refused a std path's candidate only when the candidate was in another file. So a path linked to a same-named item that merely shared its file:std::ffi::OsStrlinked to its ownstruct OsStr. Inerror/mod.rs,io::Error,fmt::Error,error::Errorandfmt::Resultlinked to its ownErrorandResult.mio::Interestlinked to its ownInterest,std::time::Instantto its ownInstant, andstd::fs::ReadDirto its ownReadDir.Token::Struct { … },Token::Seq,Token::Mapand the like, all variants of serde_test'sToken, linked to the test files' ownStructtypes and variants: 317 links. serde_derive'ssyn::Path { … }linked to aDefault::Pathvariant.ast::Patternlinked to theenum Patternbeside it.BinaryBuilderPosition::RecordAttribute { … }linked toErlangSourceBuilderPosition::RecordAttribute, another enum's variant of that name, and the same happened to 90 more variant literals.Fix
All three changes are in the path branch of
isRustNameInScope.usebinds the root.modof that name in the file (isInRustInlineModule). An inline module adds nothing to its items' qualified names, and rustfmt closes the block at the indentation it opens at.usebinds, a candidate must be in a crate thatuse's path can reach. This isrustUseCanReach, as a bare name already gets. Without it, removing the same-file shortcut sent tokio'sio::Handle(underuse crate::runtime::io) to tokio-test'sio.rsHandle, a dev-dependency's. It now reachesruntime/io/driver.rs's.Verification
__tests__/rust-path-same-file.test.ts, 6 tests, on both engines. It covers:ast::Patternbeside an ownenum Pattern, which still links from a barePattern;io::Error,fmt::Resultanderror::Errorinerror/mod.rs;mio::Interestandstd::ffi::OsStr;Source::Record { … }beside another enum'sRecord;inner::Cellfrom an inlinemod inner;io::Handleunderuse crate::runtime::iobeside a dev-dependency'sio.rsHandle.name-matcher.ts; the sixth is the inline-module guard. With only the crate-reach rule switched off, the workspace test fails.tscis clean. The Rust suites pass (17 files, 334 tests): this one, fix(rust): an impl's associated type or const no longer takes a bare name or a module path #2707's, parity, fix(rust): a call through a Pin, Rc or Box self reaches the method Rust picks, inherent first #2701's, the other Rust resolution suites andresolution.test.ts.CODEGRAPH_KERNEL_EXPECT=1 --maxWorkers=4 --minWorkers=1and the kernel built: 7,796 tests, 7,714 passed, 0 failed, 82 skipped.as_mut()method-call receivers are a separate path from this one. The kernel was rebuilt for fix(resolution): honour TypeScripttypemodifiers on imports and re-exports #1955's tsjs change.tsc, the Rust suites with fix(rust): a call on what as_mut() hands back reaches the receiver's declared type #2710's, and the incoming C, C++, TS and Express tests (tsjs parity,frameworks.test.ts) all pass; this suite also passes on wasm.A/B on real repos
Arms: main b5553e2 (#2707), and this change on it. The later main commits change C, C++, TS and Express only. Both use the same kernel, since this change is in the resolver only. Every repo was indexed fresh with
codegraph init -y, dumped withscripts/dump-graph.mjs, and edges were paired by kind, source, line and column. Every changed site was triaged: the written path was read from the source, and the old and new targets were checked against it. No edge was added.std::,io::,fmt::,fs::,error::,task::), a dependency (mio::,syn::,tungstenite::,walkdir::,lsp_server::,pubgrub::,backtrace::,anyhow::,proc_macro2::, axum-extra'scookie::) or an alias of one (ts::Message,TMessage::Close), or through serde_test'sToken::. In each case it reached a same-named project item that the path doesn't lead to.syn::paths for 15.sys::Blockingiscrate::blocking::JoinHandlere-exported under that name fromio/mod.rs's inlinemod sys, notio/blocking.rs's ownstruct Blocking.ast::Patternreachast.rs's enum;BinaryBuilderPosition::RecordAttribute { … }orBitArrayOption::Utf16, reach the variant of the enum they name;driver::Handle,io::Handle,io::Driver,list::Txandsuper::task::trace::Tracereach the module the path walks.🤖 Generated with Claude Code