Skip to content

fix(scanner): Resolve Rust use trees - #124

Merged
JordanCoin merged 5 commits into
JordanCoin:mainfrom
reneleonhardt:fix/rust-use-trees
Aug 21, 2026
Merged

fix(scanner): Resolve Rust use trees#124
JordanCoin merged 5 commits into
JordanCoin:mainfrom
reneleonhardt:fix/rust-use-trees

Conversation

@reneleonhardt

@reneleonhardt reneleonhardt commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

What does this PR do?

  • Resolve local crate, self, and super use trees into per-path dependency edges: nested groups, aliases, self, glob module imports, and pub re-exports.
  • Treat unexpandable brace trees conservatively (comments in a group, empty groups, external groups): resolve to nothing instead of emitting a false crate-root (or dependency-root) edge.
  • Preserve the existing scanner path for non-grouped external imports.

Type of change

  • Bug fix
  • New feature
  • New language support
  • Documentation
  • Other (describe below)

Checklist

  • I've tested this locally with go build && ./codemap .
  • I've read CONTRIBUTING.md; this does not add a new language.
  • Documentation is unchanged because this corrects existing Rust scanning.

Additional notes

Focused parser/extraction/resolution tests, go vet ./..., and the full race/coverage suite are GREEN on macOS Go 1.26.5. The exact publication branch is also GREEN on Linux Go 1.24.13.

On GitButler's 978-file Rust corpus, the change adds 588 specific local-module edges and removes 75 false crate/test-root fallback edges — measured before the review follow-ups (pub re-exports and conservative brace handling), which shift both numbers. Non-grouped external imports are unchanged; pub re-export hubs now gain edges.

Developed with carefully directed, manually reviewed AI assistance.

@JordanCoin

Copy link
Copy Markdown
Owner

Reviewed and approved on the merits — but #126 just landed and this is now DIRTY, so it needs a rebase before I can merge it. That was unavoidable: all four Rust PRs edit the same m.RuleID disjunction in scanner/astgrep.go and the same two switches, so whichever lands first makes the other three conflict. Not a criticism of how they were split; just the shape of the collision.

What I verified, by dumping the full resolved edge set from BuildFileGraph on both main and this branch against a real 2-crate workspace (cargo check --workspace clean first):

main:  caller.rs -> [caller/helper.rs, exports.rs, lib.rs]        <- lib.rs is a false crate-root fallback
PR:    caller.rs -> [alpha.rs, beta.rs, caller/helper.rs,
                     exports.rs, nested/leaf.rs, nested/mod.rs]   <- all correct

Net on the fixture: +6 true edges, −2 false edges, 0 new false edges. std::collections::{BTreeMap, HashMap} correctly yields nothing, and use crate::* yields nothing rather than a wrong target. The expansion is a pure syntactic prefix-join, so it can't invent a path that isn't a genuine prefix of the source tree — that's the right property.

One thing worth folding in while you rebase (~3 lines): the claim that malformed trees "remain conservative" isn't quite true today. The gate at astgrep.go:351 only upgrades kind on success; on failure the reference falls through as rust-path carrying raw brace text, which resolveRustPath splits on :: into garbage and then hits rootFallback, emitting an edge to the crate root. Both of these are valid Rust and both produce a false edge:

use crate::{/* note */ alpha::Thing, beta};   // -> lib.rs   (should be alpha.rs + beta.rs)
use crate::{};                                 // -> lib.rs   (a no-op import inventing an edge)

Setting a kind that resolves to nothing when expansion fails on a brace-bearing path would make the PR's own claim true and delete two false edges.

Not blocking, for later: use crate::* now yields no edge where main produced one (under-reporting, so it doesn't violate the contract, but a correct edge is lost); and grouped pub use never reaches the new code at all, because the sg-rule patterns don't match a use_declaration with a visibility modifier — re-export hubs stay invisible. Neither is claimed in the PR body, just flagging so they're not assumed fixed.

Suggested order for the remaining three: this one next (only the rebase), then #127 (needs the println! rule scoped — it currently matches every println! in every Rust file), then #125 (needs the files[0] != target guard, or #130 fixed first, which makes it moot). Also: the resolver for the astgrep.go conflict must union all three rule IDs — taking either side silently disables a feature with fully green CI.

reneleonhardt and others added 2 commits August 18, 2026 11:09
Resolve grouped Rust use trees (use crate::{a, b}) into per-path dependency references, including aliased imports.

Co-Authored-By: GPT-5.6 Sol <codex@openai.com>
@reneleonhardt
reneleonhardt marked this pull request as ready for review August 18, 2026 09:28
@reneleonhardt

Copy link
Copy Markdown
Contributor Author

Rebased

The astgrep conflict was resolved as a union of all three rule IDs, so nothing was silently disabled.

Compliance

  • use crate::{/* note */ alpha::Thing, beta} and use crate::{} now resolve to nothing instead of a false crate-root edge.
  • Brace-bearing paths that fail to expand stay on the expand-per-item kind, which emits no edge.
  • pub use groups now reach the expansion, so re-export hubs get edges.
  • PR body updated to state both behaviors.

Where I didn't fully comply

  • use crate::* still yields no edge (the under-reporting you flagged), left as-is deliberately:
    • The "correct" edge would be the crate-root fallback you called false in the malformed case.
    • Glob-ness is stripped during expansion, so emitting it needs resolver-level changes rather than a gate tweak.
    • It doesn't violate the contract; happy to tackle it as a follow-up if you want it.
  • Only plain pub visibility is matched:
    • ast-grep rejected a generic visibility metavar — the whole rule silently stopped matching.
    • Explicit pub use patterns are used instead; pub(crate) / pub(super) re-exports remain invisible.

Other changes the review forced (not in your suggestions)

  • Grouped external imports (use dependency::{A, B}) fell through to the same raw-brace splitting and now resolve to nothing too, deleting a false edge to the dependency root for indexed dependencies.
  • Non-grouped external imports keep the existing scanner path unchanged.
  • The extraction test now classifies malformed, external, plain, and pub groups as use-tree references.
  • Added an empty-group expander case and a regression test asserting no crate-root edge.
  • ast-grep gotcha: the invalid metavar version of the rule produced zero matches with fully green CI otherwise — worth knowing for the other Rust PRs touching these rules.

The termination budget ran concurrently with the scan deadline (both
started at marker-read), leaving only the fake ast-grep spawn latency
of slack after the deadline fired — loaded macOS CI runners missed it.
Budget now spans scan deadline plus termination, matching the elapsed
check's own 10s allowance.
@JordanCoin
JordanCoin merged commit 37d12d3 into JordanCoin:main Aug 21, 2026
12 checks passed
@reneleonhardt
reneleonhardt deleted the fix/rust-use-trees branch August 21, 2026 19:58
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