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;