Repository navigation
fix(rust): a call through a type a path names reaches that type's own function, not a namesake's - #3203
fix(rust): a call through a type a path names reaches that type's own function, not a namesake's#3203bob-codedaptive wants to merge 1 commit into
Conversation
… function, not a namesake's
A call written through a type's path, `crate::a::Cache::open()`, took the
function of any type of that name in the crate: rustTypeMember matched by
qualified name and crate alone. A namesake type in another inline module of
the same file, one a function declares in its own body, and an `impl` for a
reference to the type (`impl Open for &Cache`, the reference's, not the
type's) could each be linked instead of the type the path walks to. The
walk itself also took a head wrongly: a `use` in a function's body that
binds it (`use crate::b as a;`) was ignored, and so was a `mod a { … }` the
function declares, which rustSubmodule also took for the module's own `a`
when it was written before the module's `pub mod a`.
- rustTypeMember takes a function, `const` or variant of a struct, enum or
union only from an `impl` whose header names that type (rustImplSelfItems,
walked from the module the `impl` is in); a trait's own declaration is
still the trait body's.
- rustImplSelfItems reads the `impl` header with the new rustImplSelfType,
which rustImplSelfArgs shares; a header naming a reference to the type,
or a bare name in an `impl` inside a function's body, names no type here.
- rustSubmodule skips a `mod` that sits in a function's body or another
block (RustModBlock.scope, read with rustUseScopes), which is no item of
its module.
- rustPathTarget reads a head that a `use` in the body binds as the path
that `use` names, and declines one a `mod` in the body declares.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: b57434f41d
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| segs = [...via, ...segs.slice(1)]; | ||
| head = segs[0]!; | ||
| bare = head !== 'crate' && head !== 'self' && head !== 'super'; |
There was a problem hiding this comment.
Follow local import aliases before walking the rewritten path
When a block-scoped import names another local import, this single substitution can produce a wrong call edge. For example, with sibling modules a and b both exposing make, use crate::a as b; { use b as a; a::make() } calls crate::a::make. The substitution instead produces b::make, and the subsequent module walk ignores the outer local binding of b and selects the sibling module's function. The parent revision resolves this example correctly, and the later Rust scope gate accepts the incorrect target. Resolve the imported head recursively in the import site's lexical scope before walking modules.
Useful? React with 👍 / 👎.
| // module of the file, or one a function declares for itself) does not. | ||
| const within = (n: Node): boolean => type.kind === 'trait' | ||
| ? n.filePath === type.filePath && n.startLine >= type.startLine && n.startLine <= (type.endLine ?? type.startLine) | ||
| : rustImplSelfItems(n, context)?.has(type.id) === true; |
There was a problem hiding this comment.
Preserve methods on impls with callback bounds
For a valid inherent impl such as impl<F: Fn() -> usize> Cache<F>, this newly mandatory ownership check rejects all its methods. The generic-stripping regex in rustImplSelfType treats the > in -> as the closing generic bracket, leaving usize> Cache<F> for rustPathHead, which returns null. Consequently, a previously resolved call such as crate::a::Cache::open(|| 1) loses its call edge even though the indexed method unambiguously belongs to Cache; the name-matching fallbacks do not resolve that full path. Parse the impl's generic parameters with an arrow-aware balanced-bracket reader before using the header to reject candidates.
Useful? React with 👍 / 👎.
#3025 made
Type::membercalls go through the module walk, but the member lookup (rustTypeMember) still matched a type's function by its qualified name and crate alone, and a few heads the walk read wrongly. The result was a wrong link, which is worse than none, in six shapes:crate::a::Cache::open()tookb::Cache::openwhena::Cachehas only a trait's providedopen.fn f() { struct Cache; impl Cache { fn open() … } }) took the call to the module-levelCache::open.implfor a reference to the type (impl Open for &Cache { fn open() … }) is the reference's, notCache's, and was linked asCache::open.usein the function's body that binds the path's head (use crate::b as a; a::Cache::open()) was ignored, so the call went to the moduleabeside it.mod a { … }the function declares in its body was ignored the same way.mod aof its own before the module'spub mod amaderustSubmoduletake the function'safor the module's, because it took the firstmod ain source order.What changes:
rustTypeMembertakes a struct, enum or union's function,constor variant only from animplwhose header names that type, walked from the module theimplis written in (rustImplSelfItems). Animplin another file than its type, by path or through ause, still counts. A trait's own declaration is still the trait body's.rustImplSelfTypeis the header reader thatrustImplSelfArgsnow shares.rustSubmoduleignores amodwritten in a function's body or another block (RustModBlock.scope, found with the existingrustUseScopes); it is no item of the module around it.rustPathTargetreads a head that ausein the body binds as the path thatusenames, and returns nothing for a head amodin the body declares. This applies to module paths (a::make()) as well as type paths.Tests:
npx vitest run __tests__/rust-path-call-edge-cases.test.ts(6 cases). On main 6 of 6 fail; with the change all pass. The 36 otherrust-*suites (247 tests with the new one) andresolution,qualified-name-boundary,qualified-lookup-boundary,self-call-delegation,reference-target-kind,cross-file-visibility,function-ref,value-reference-edgesandtauri-ipcpass;npx tsc --noEmit -p .is clean.Verified against upstream main 47a9c7d.
Not measured: the cost of
rustImplSelfItemsper candidate on a large Rust repository. It runs once per (type, member) pair, after the name and crate filters, and reads the scopes of the candidate's file (cached).Testing
🤖 Generated with Claude Code