Skip to content

fix: resolve Rust super:: and self:: calls through the written path - #1110

Merged
vitali87 merged 2 commits into
mainfrom
fix/rust-super-self-prefix-resolution
Aug 8, 2026
Merged

vitali87 merged 2 commits into
mainfrom
fix/rust-super-self-prefix-resolution

Conversation

@vitali87

@vitali87 vitali87 commented Aug 7, 2026 •

Copy link
Copy Markdown
Owner

Closes #1093.

Root cause, confirmed by reproduction

The issue's corpus reproduces exactly: _try_resolve_via_imports answers super::helper() with the caller's OWN helper, never honouring the prefix, and it runs before _try_resolve_rust_module_qualified — so _resolve_rust_prefixed_path, which knows how to climb, is never reached.

recorded: rs.src.engine.thing.S.build -> rs.src.engine.thing.helper
rustc:    rs.src.engine.thing.S.build -> rs.src.engine.helper

Changes

1. The prefixed-path probe gets first refusal. A call whose first segment is crate::/self::/super:: says outright which module holds the item, so it must not reach probes that answer by name. Undecided still falls through to every existing fallback, so nothing else changes.

2. The enclosing-scope bail now distinguishes an impl block from an inline mod. _resolve_rust_prefixed_path declined any caller nested below the file module because the two are "indistinguishable in qn space". They are distinguishable via the type registry: new _rust_enclosing_mod_scopes filters out scopes registered as Class/Interface/Enum/Type/Union (new RS_TYPE_SCOPE_LABELS). An impl block adds no module level, so a method's super:: counts from the file module's parent exactly as a free function's does.

Change 1 alone fixes the free-function case; the issue's headline repro is inside an impl, which needs change 2 as well.

This does not encroach on #1086. A genuine inline mod chain still triggers the bail and keeps today's behaviour. Only impl targets are filtered out.

Tests

codebase_rag/tests/test_rust_super_self_prefix_calls.py:

  • the issue's exact corpus, super:: from inside an impl
  • the same from a free function (the impl is incidental, as the issue notes)
  • self::helper() — the adjacent case the issue flags, pinned so the outer same-named item cannot win
  • super:: with no local shadow, which used to drop rather than bind wrongly
  • use super::item;, out of scope here, pinned so the reordering does not disturb it

Two fail without the fix; all five pass with it.

Regression coverage

Full unit suite: 6502 passed, 79 skipped, no failures. ruff and ty clean.

Summary by CodeRabbit

  • Bug Fixes

    • Improved resolution of Rust module-qualified calls using crate::, self::, and super::.
    • Corrected super:: behavior from implementation blocks and free functions.
    • Preserved resolution for use super::... imports and calls without local name shadowing.
  • Tests

    • Added regression coverage for Rust module, implementation-block, and import-based call resolution.

@coderabbitai

coderabbitai Bot commented Aug 7, 2026 •

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@vitali87, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 1 minute

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 0cebb9b3-2a4f-4bb3-8178-66c41dd58bf4

📥 Commits

Reviewing files that changed from the base of the PR and between 45877f7 and 880ea5d.

📒 Files selected for processing (5)
  • codebase_rag/constants/ast_rust.py
  • codebase_rag/parsers/call_processor.py
  • codebase_rag/parsers/call_resolver.py
  • codebase_rag/parsers/factory.py
  • codebase_rag/tests/test_rust_super_self_prefix_calls.py
📝 Walkthrough

Walkthrough

Rust prefixed calls now resolve against declared inline-module scopes before fallback probes. Implementation blocks no longer add false module nesting. Regression tests cover self::, super::, and use super:: behavior.

Changes

Rust module-qualified call resolution

Layer / File(s) Summary
Scope-aware prefixed call resolution
codebase_rag/constants/ast_rust.py, codebase_rag/parsers/call_resolver.py, codebase_rag/parsers/call_processor.py, codebase_rag/parsers/factory.py
Adds Rust type-scope labels, passes declared inline-module names through the processor stack, prioritizes prefixed path resolution, caches decided results, and excludes implementation blocks from module nesting.
Module-resolution regression coverage
codebase_rag/tests/test_rust_super_self_prefix_calls.py
Adds temporary-crate tests for super::, self::, and use super:: resolution from functions and implementation blocks, including primitive-type implementations.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Sequence Diagram(s)

sequenceDiagram
  participant CallProcessor
  participant CallResolver
  participant ModuleScopes
  participant ResolutionCache
  CallProcessor->>CallResolver: pass declared inline-module names
  CallResolver->>ModuleScopes: inspect enclosing inline modules
  ModuleScopes-->>CallResolver: return valid module scopes
  CallResolver->>ResolutionCache: cache resolved or known-unresolved result
Loading

Possibly related issues

Possibly related PRs

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely describes the primary fix for Rust super:: and self:: call resolution.
Description check ✅ Passed The description clearly explains the root cause, implementation changes, related issue, regression tests, and reported validation results.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/rust-super-self-prefix-resolution

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@greptile-apps

greptile-apps Bot commented Aug 7, 2026 •

Copy link
Copy Markdown
Contributor

Greptile Summary

The Rust resolver now uses the set of declared inline modules to distinguish module scopes from implementation scopes. This keeps super:: resolution correct for implementations on primitive types while preserving inline-module behavior. The checked Rust fixture compiles and the resolver selects only the declared inline module as a module scope. Ready to merge.

Confidence Score: 5/5

No blocking failure remains.

No accepted blocking findings remain after confirming the resolver excludes primitive implementation scopes and retains declared inline-module scopes.

T-Rex T-Rex Logs

What T-Rex did

  • The resolver was executed to identify the enclosing module for the caller rs_timing.src.lib.inner.u8.run, and it found only the declared inline module.
  • A check with an empty declaration set showed that no enclosing module was identified for that caller.
  • The Rust fixture compiled successfully.
  • An attempt to perform full ingestion could not be completed because the configured Python 3.12 launcher is missing, so no full-parser pass or failure was asserted.

T-Rex Ran code and verified through T-Rex

Reviews (2): Last reviewed commit: "fix: decide Rust inline-mod scopes by in..." | Re-trigger Greptile

Comment thread codebase_rag/parsers/call_resolver.py Resolved
@vitali87

vitali87 commented Aug 7, 2026

Copy link
Copy Markdown
Owner Author

Confirmed and fixed in 880ea5d — thanks, this was a real defect and the reproduction was exactly right.

I reproduced it before changing anything. impl LocalTrait for u8 with super::helper(), against a registered-struct control:

impl LocalTrait for S  (S is a Class)   -> src.lib.helper    correct
impl LocalTrait for u8 (primitive)      -> src.area.helper   WRONG — one level short

The root cause is that my discriminator was an absence test. Probing the registry directly showed why it cannot work:

REGISTRY ...src.area.u8       -> None     (impl target)
REGISTRY ...src.area.inner    -> None     (inline mod)

Both are None, so "not a registered type" carries no information about which one a scope is — it only ever proved the scope wasn't a Class, which an impl on a primitive or an unindexed foreign type also isn't.

The fix replaces it with a positive membership test against declared_module_qns, the set the class pass already fills with every inline mod qn it ingests. A scope is an inline mod iff it is in that set; impl targets never are, whatever their registry label. That set is threaded DefinitionProcessor -> CallProcessor -> CallResolver alongside the existing rehydrated_definition_paths, and the registry check is now gone rather than merely supplemented.

Regression test added to test_rust_super_self_prefix_calls.py covering the primitive-impl case, asserting both the correct edge and the absence of the wrong one. Rust suite: 876 passed; ty clean.

@sonarqubecloud

sonarqubecloud Bot commented Aug 7, 2026

Copy link
Copy Markdown

@vitali87

vitali87 commented Aug 8, 2026

Copy link
Copy Markdown
Owner Author

@coderabbitai full review

@greptile-apps review

All review threads on this PR have been answered inline: findings implemented, or explicitly rebutted with the reasoning. Please re-assess against the current head and confirm whether this is ready to merge.

@coderabbitai

coderabbitai Bot commented Aug 8, 2026

Copy link
Copy Markdown

Rate Limit Exceeded

@vitali87 have exceeded the limit for the number of chat messages per hour. Please wait 35 minutes and 6 seconds before sending another message.

@vitali87
vitali87 merged commit a111a9f into main Aug 8, 2026
24 checks passed
@vitali87

vitali87 commented Aug 8, 2026

Copy link
Copy Markdown
Owner Author

@coderabbitai full review

@coderabbitai

coderabbitai Bot commented Aug 8, 2026 •

Copy link
Copy Markdown
⚠️ Action not completed

Review rate limited.


Your included review limit is currently reached under our Fair Usage Limits Policy. This review may still proceed through usage-based billing if eligible. Your next included review will be available in 1 minute.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

fix: a super:: call resolves in the caller's own module because the import probe decides first

1 participant