Namespace-aware C# type resolution with lexical using-scope and qualified refs#1562
Namespace-aware C# type resolution with lexical using-scope and qualified refs#1562TheFedaikin wants to merge 2 commits into
Conversation
…scope and qualified refs
|
Merged into One note: your branch predated the C++/ObjC cross-file member-call resolvers that landed on |
Extends the C# type-reference resolver (#1466) to be namespace-aware, advancing the #1318 shadow-node umbrella for C#: - The namespace is folded into the C# node id (_make_id(stem, namespace, name)), so two same-named types in different namespaces in one file no longer collapse — replacing #1466's detect-and-skip workaround for multi-namespace files. - Lexical per-block using-scope: a `using` applies only where it is in scope (file-level, or the enclosing namespace block via a scope chain), so sibling namespace blocks no longer share each other's usings. - Qualified references (`Namespace.Type`) resolve via in-scope aliases (`using Q = X.Y`) then exact known namespaces; generics are stripped. Preserves (and tightens) the refuse-rather-than-guess discipline: a bare reference resolves only when exactly one in-scope namespace provides the type; an ambiguous reference (e.g. `using A; using B;` both defining `Widget`) resolves to nothing rather than fanning out. Verified: `using A` -> A.Widget only; ambiguous -> no edge; qualified `B.Widget` -> B.Widget regardless of usings; sibling-block using-scope isolated; no dangling edges or fan-out. Reconciled onto current v8 (the PR predated the C++/ObjC member-call resolvers); full suite green, the C++/ObjC resolution coexists. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Cross-file member-call resolution for C++/ObjC (#1547/#1556) and namespace-aware C# type resolution (#1562), the work-memory overlay (#1441), test-mock call-graph fix (#1553), hyperedge member-key aliases (#1561), plus the TS/JS/ObjC resolution fixes (#1316/#1544/#1552/#1475). See CHANGELOG. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Summary
The C# cross-file resolver was sound but conservatively incomplete: it resolved
the common case and skipped three reference classes it couldn't faithfully
represent. This turns those three skips into real resolution while keeping the
same hard guarantee — never emit a wrong edge: resolve only on exact, unique
facts, otherwise leave the reference dangling.
What changed
1. Namespace in the C# type-node id. A C# type node's id was built from the
file stem + simple name, so two same-named types in different namespaces of one
file (
namespace A { class T } namespace B { class T }) collapsed to one nodeand the second was dropped. The dotted namespace is now folded into the id at
the three sites that construct or probe it (class declaration,
ensure_named_node, base-list).make_iddrops the empty namespace segment, soevery non-C# id and every C# global-namespace id stays byte-identical — the
change is opt-in for C#. The previous
ns_collisiondetect-and-skip workaroundis removed.
2. Lexical
using-scope. C#usingscope is lexical (per declarationblock), not per namespace name —
namespace A { using N; class Good : T {} } namespace A { class Bad : T {} }rejectsBad : Teven though both blocks arenamed
A. The previous file-wide clamp, keyed on unique namespace strings, wasunsound for that case. Each declaration node now carries a per-file lexical
scope-id chain, and each
usingedge carries itsscope_kind(file vsnamespace) and
scope_id. Ausing— namespace or alias — applies to areference iff it is file-scoped or its scope id is in the reference's scope
chain, matched within the same file. This is sound for sibling same-name blocks,
nested blocks (a using flows inward), file-level usings, and aliases, and it
recovers usings in multi-namespace files that were previously dropped. The
scenarios were verified against
dotnet build.3. Qualified references. Qualified references (
B.T) were skipped andqualified generics (
A.B<C>) emitted junk labels. The qualifier is nowpreserved on the edge (
metadata.ref_qualifier;ref_tokenstays the simplelookup key), and a qualified
Q.Tresolves via an in-scope namespace alias(
using Q = X.Y) or an exact known namespace, otherwise dangles. Generic argsare stripped from qualified tails so
N.Box<int>resolves to the realN.Box.An in-scope alias shadows a like-named namespace.
Soundness
Every emitted edge is exact and uniquely scoped; uncertainty dangles rather than
guessing. The
ns_collisionremoval and def-index completion (which expose thede-collapsed types) land in the same change as the lexical resolver (which makes
13 new C# resolution tests cover id-distinctness, same-name-in-one-file
resolution, the four lexical
usingcases, and qualifier resolution (generics,namespace aliases, and out-of-scope / shadowing alias edge cases). Three
superseded tests were rewritten around the new behavior. The full graphify suite
passes.
Residual gaps (sound, deferred)
Core.TmeaningGame.Core.Tviausing Game;) —exact-match only; prefix composition is deferred.
modeled).