Aller au contenu
login
arrow_backRetour aux issues
repowise-dev/repowise #1886

[Bug] A call site can resolve to a module, and a module is not callable

ecoDébutant bug help wanted

descriptionDescription

**Summary** A call site can resolve to a symbol whose kind is `module`, and a module is not callable in any language we index. The edge is minted at 0.85 or 0.50 confidence and points at a `mod` declaration. Seen on bevy: `error!(` at `crates/bevy_pbr/src/render/mesh.rs:2907` resolves to `crates/bevy_ecs/src/lib.rs::error`, which is line 36 of that file: ```rust pub mod error; ``` There is no `macro_rules! error` anywhere in `bevy_ecs/src/lib.rs`. The real target is tracing's logging macro, brought in by `mesh.rs:85`: ```rust use tracing::{error, warn}; ``` `mesh.rs` also imports from `bevy_ecs` (`mesh.rs:24-25`), so `bevy_ecs`'s symbols are in the merged lookup, and the module named `error` is the one that answers. **Mechanism** Two things combine, and each is independently a defect. 1. `_NON_CALLABLE_KINDS` in `packages/core/src/repowise/core/ingestion/call_resolver.py:129` is `frozenset({"property"})`. `module` is not in it, so a module symbol is treated as a legitimate call target everywhere. 2. That set is consulted on the global-unique tier only (`call_resolver.py:1137`). The import tiers above it, `import_scoped` at `:1109` and `import_merged` at `:1113-1115`, apply no kind filter at all, so they can answer with a non-callable symbol even once the set is corrected. The bevy site above goes through `import_merged`. **Repro** Three files, no external crates: `src/lib.rs` ```rust pub mod warn; ``` `src/warn/mod.rs` ```rust pub fn helper() {} ``` `src/user.rs` ```rust use crate::warn; fn run() { warn!("something happened"); } ``` Result on `main`: ``` calls src/user.rs::run -> src/lib.rs::warn | callee kind: module | origin: global_unique ``` `warn!` is the standard logging macro. The edge instead lands on a module declaration. Note this repro fires on the global-unique tier, which does have the filter, so it isolates half 1 above; the bevy site isolates half 2. **Impact** Unsized. It was found as the single wrong target in 30 rust call sites read from source during a precision audit, so 1 in 30 on that sample and nothing more is claimed. The first thing to measure is how many call sites across the corpus resolve to a symbol whose kind is `module`, per language, since the merged-import tier is language-agnostic and rust is unlikely to be the only one affected. Worth knowing: this is the same class as the fix in `dc797b36`, which taught the global-unique tier that a struct field is not callable. That commit is the model to copy, including its rule. **Done looks like** `module` is refused as a call target, and the refusal is applied on the import tiers as well as the global-unique one. Two constraints carried over from `dc797b36`, both load bearing: * **Filter the answer, never the pool.** The refusal must sit after the candidate has been chosen and must return rather than fall through, so the tier can lose an edge and can never gain one. Filtering the candidate pool before the uniqueness test re-uniquifies a name that two symbols share and fires the tier where it used to refuse. * **Grade removals per language.** A tier registered for one language family does not share its premise elsewhere. Report what is removed per language rather than in total. Gate: removed edges read wrong on a sample from the population diff, controls byte-identical on repositories with no module-kind symbols, and dead-code findings unmoved. "Removes N, gains 0" is not sufficient on its own. Tests: the repro above pinned as a parser or resolver test, plus one covering the import-tier path, plus a control showing an ordinary free-function call in the same file still resolves.
codeOuvre sur GitHub