Skip to main content

devscout_rs/
resolve.rs

1// The resolution ladder, including ambiguous marking: `build_def_index`,
2// `resolve_ref`, `collect_global_usings_by_unit`, `capped_candidates`,
3// `resolve_graph`.
4// Pure: no file I/O, no tree-sitter -- the only I/O this module performs is the
5// single `git rev-parse HEAD` shell-out inside `resolve_graph`, delegated to
6// `manifest::git_head`, and the emission record `provenance` writes when
7// `SCOUT_EDGE_PROVENANCE` names a path, which no resolution ever reads.
8// Artifact load/save and the fragments cache live in `graph.rs`.
9//
10// Ladder rules (see `resolve_ref`'s doc comment for the exact order):
11//   0. Type ALIASES (`using Foo = Some.Ns.Bar;` and `global` counterpart)
12//      short-circuit before the ladder for bare (non-dotted) references --
13//      never ambiguous, never falls through.
14//   1. Exact qualified name, tried at every ENCLOSING namespace prefix,
15//      innermost first, only for dotted references. A dotted reference the
16//      exact step misses gets two further chances and never reaches steps
17//      2-4: steps 1a/1b (an alias at its head rewritten to the alias target,
18//      then only a def whose full path ends with the written text, or a
19//      nested def inside the inheritance closure of the type the qualifier
20//      names), and then step 1.5.
21//   1.5 A dotted qualifier walked through NESTED types: the shortest head
22//      that names a type, then one exact `{id}+{segment}` lookup per
23//      remaining segment. Skipped when the extractor vouches that the
24//      qualifier is an instance. A dotted reference this step cannot answer
25//      either finishes External rather than falling into steps 2-4.
26//   2. File's usings (local ∪ every `global using`) + simple name, each
27//      using name itself tried at every enclosing-namespace prefix.
28//   3. The reference site's namespace and every ancestor of it, innermost
29//      first (the ancestor-namespace rule -- a walk, like step 1).
30//   4. Globally unique simple name.
31//   A step that finds exactly one candidate resolves; two or more STOPS
32//   there as ambiguous, never falling through looking for a tiebreaker.
33//
34// The enum-member asymmetry (the single most load-bearing invariant here):
35// enum members ARE keyed in `qualified_name_to_def` (reachable by exact id,
36// e.g. for uses-member resolution) but are EXCLUDED from `simple_name_to_defs`
37// (the pool step 2/4's bare-name lookups draw from) -- see `build_def_index`.
38// Losing this exclusion doesn't change any TYPE resolution that was already
39// unambiguous via using/namespace/alias, but it does turn every type whose
40// simple name collides with some unrelated enum's member name into a false
41// ambiguous (or worse, a step-4 resolution picking the wrong one) purely
42// because that enum happens to exist somewhere in the same build. The test
43// `enum_member_does_not_collide_with_a_same_named_class_via_global_uniqueness`
44// in `tests/ladder.rs` is a regression trap for exactly this: it fails loudly (ambiguous where
45// it should resolve cleanly) if the exclusion is ever dropped.
46
47mod arity;
48mod assembly;
49mod dispatch;
50mod edges;
51mod index;
52mod ladder;
53mod lambda_arity;
54mod members;
55mod provenance;
56mod receiver;
57mod scope;
58
59pub use assembly::{resolve_graph, resolve_graph_with_model, resolve_graph_with_ts};
60pub use index::{DefIndex, ExtCandidate, MemberLists, MethodOverloadParams};
61
62#[cfg(test)]
63mod tests;