Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
48 commits
Select commit Hold shift + click to select a range
6453aa5
typescript: a JSX attribute hands its function to the element, and a …
swapnilpaliwal-sd Oct 9, 2026
b2e7cda
typescript: a call through an imported const runs the function it holds
swapnilpaliwal-sd Oct 9, 2026
5c2130a
typescript: a call on the platform's own objects is no by-name caller…
swapnilpaliwal-sd Oct 9, 2026
92d7cee
fix(parser/js): an import resolves to the code it runs, not a declara…
swapnilpaliwal-sd Oct 9, 2026
21c0f9f
typescript: a list's element is typed through a type variable's const…
swapnilpaliwal-sd Oct 9, 2026
3388587
fix(js): a test file's calls do not decide whether the program's argu…
swapnilpaliwal-sd Oct 9, 2026
408d01f
fix(js): a call of what a project wrapper returned runs what that sit…
swapnilpaliwal-sd Oct 9, 2026
9d52b70
fix(parser/python): an import of a module shipped with a .pyi beside …
swapnilpaliwal-sd Oct 9, 2026
221e3b6
fix(python): type a test parameter by the fixture the runner hands it
swapnilpaliwal-sd Oct 9, 2026
6bc6ce6
fix(python): a decorator returning update_wrapper(wrapper, f) or cast…
swapnilpaliwal-sd Oct 9, 2026
6aae527
fix(impact): a call under `if __name__ == "__main__":` is not taken b…
swapnilpaliwal-sd Oct 9, 2026
bfb7089
fix(impact): a conjunctive main guard (`cond and __name__ == "__main_…
swapnilpaliwal-sd Oct 9, 2026
cafd6d2
fix(impact): an on-demand protocol member is reached from test code t…
swapnilpaliwal-sd Oct 9, 2026
b0dca3c
fix(python): `obj.x = v` and `del obj.x` are calls to the property's …
swapnilpaliwal-sd Oct 9, 2026
ba6d9ba
fix(python): a local's Optional/union annotation types it, and every …
swapnilpaliwal-sd Oct 9, 2026
78eaedb
fix(impact): a src-layout module is imported by its package name, and…
swapnilpaliwal-sd Oct 9, 2026
9e4379e
csharp: a name written inside a namespace is read from that namespace
swapnilpaliwal-sd Oct 9, 2026
d867f4f
path: a C# source file is an endpoint
swapnilpaliwal-sd Oct 9, 2026
ea42c61
csharp: a using resource is typed by its initializer
swapnilpaliwal-sd Oct 9, 2026
59a1ce9
csharp: a constructor runs its base class's parameterless constructor
swapnilpaliwal-sd Oct 9, 2026
0966ce7
csharp: an unstaged receiver keeps its type name for extension matching
swapnilpaliwal-sd Oct 9, 2026
21917af
csharp: a static member read through its type name types the chain
swapnilpaliwal-sd Oct 9, 2026
c0dab0b
csharp: a constructed generic type is a static receiver
swapnilpaliwal-sd Oct 9, 2026
8061569
impact: a Java type's initializer is run by whoever uses the type
swapnilpaliwal-sd Oct 9, 2026
34b9ebf
parser: a field initializer written after a comment is read
swapnilpaliwal-sd Oct 9, 2026
7422e84
impact: the at-load hop sees a test that reads a static field
swapnilpaliwal-sd Oct 9, 2026
b73381c
impact: the members a library runs on a Java object reach whoever bui…
swapnilpaliwal-sd Oct 9, 2026
5d153ad
fix(parser/python): an import of a module shipped with a .pyi beside …
swapnilpaliwal-sd Oct 9, 2026
cba62f5
Merge origin/fix/parser-py-module-prefers-source into the 0.1.9 impac…
swapnilpaliwal-sd Oct 9, 2026
d08b4a5
Merge origin/fix/js-impact-loop into the 0.1.9 impact-loop batch
swapnilpaliwal-sd Oct 9, 2026
1ec53ab
Merge origin/fix/ts-impact-loop-2 into the 0.1.9 impact-loop batch
swapnilpaliwal-sd Oct 9, 2026
76b4292
Merge origin/fix/java-impact-loop into the 0.1.9 impact-loop batch
swapnilpaliwal-sd Oct 9, 2026
8deef3e
Merge origin/fix/cs-impact-loop into the 0.1.9 impact-loop batch
swapnilpaliwal-sd Oct 9, 2026
6495d8f
Merge local/py-1873 into the 0.1.9 impact-loop batch
swapnilpaliwal-sd Oct 9, 2026
ab1e09b
fix(impact): a test that spawns `python -m pkg` runs pkg/__main__.py
swapnilpaliwal-sd Oct 9, 2026
d202458
fix(python): getattr(self, f"visit_{...}") dispatches to the visit_* …
swapnilpaliwal-sd Oct 9, 2026
ee7d906
fix(python): `with X() as y` types y as X where X.__enter__ returns s…
swapnilpaliwal-sd Oct 9, 2026
b836d07
fix(python): a loop over a mapping's .items()/.values()/.keys() types…
swapnilpaliwal-sd Oct 9, 2026
e4eeb91
fix(python): a parametrize row or a fixture's params= value types the…
swapnilpaliwal-sd Oct 9, 2026
8b6c3ec
fix(python): repr(x), len(x), str(x) and the other one-dunder builtin…
swapnilpaliwal-sd Oct 9, 2026
0646c84
fix(python): a parameter's default value reaches it, as a call-site a…
swapnilpaliwal-sd Oct 9, 2026
857e8f1
fix(parser/python): an import more qualified than a module resolves t…
swapnilpaliwal-sd Oct 9, 2026
32f0b35
feat(python): library callback summaries -- what an installed library…
swapnilpaliwal-sd Oct 9, 2026
9938c30
feat(impact): walk a library calling back into the client, as its own…
swapnilpaliwal-sd Oct 9, 2026
cf5b8bd
feat(path, impact): a library's hand-back is a hop path walks and a d…
swapnilpaliwal-sd Oct 9, 2026
92bf8ed
Merge Python recall fixes (#1883) onto the 0.1.9 batch
swapnilpaliwal-sd Oct 10, 2026
026edf7
Merge Python library callback summaries (#1884) onto the 0.1.9 batch
swapnilpaliwal-sd Oct 10, 2026
48841c5
fix(python): the dispatch cap is off by default for Python; re-freeze…
swapnilpaliwal-sd Oct 10, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
7 changes: 7 additions & 0 deletions bin/axiomcode
Original file line number Diff line number Diff line change
Expand Up @@ -295,6 +295,13 @@ case "$cmd" in
echo "▶ parsing $src → IR"
"$0" parser "$src" "$ir" ${popts[@]+"${popts[@]}"} > "$int/parse.log" 2>&1 || { echo "❌ parser failed — see $int/parse.log" >&2; tail -5 "$int/parse.log" >&2; exit 1; }
head -1 "$int/parse.log"
# WHAT AN INSTALLED PYTHON LIBRARY HANDS BACK to the objects the client gives it (graph/python/libsum): read from
# the project's environment, cached by package and version, staged beside the client IR. Never fatal -- without
# it the engine sees no library hand-backs, exactly as before. AXIOMCODE_LIB_CALLBACKS=0 turns it off.
if [ -d "$ir/python" ] && [ "${AXIOMCODE_LIB_CALLBACKS:-1}" != 0 ]; then
python3 "$ROOT/graph/python/libsum/libsum.py" --src "$src" --out "$ir/python" > "$int/libsum.log" 2>&1 \
&& tail -1 "$int/libsum.log" || echo "⚠️ library callback summaries failed (see $int/libsum.log); continuing without them" >&2
fi
# which languages the IR holds — one folder each — and which of them have an engine
found=(); for d in "$ir"/*/; do l="$(basename "$d")"; [ -d "$ROOT/graph/$l/engine" ] && found+=("$l"); done
[ ${#found[@]} -gt 0 ] || { echo "❌ no language with an engine in the IR (folders: $(ls "$ir" | tr '\n' ' '))" >&2; exit 1; }
Expand Down
5 changes: 4 additions & 1 deletion graph/bundle/SCHEMA.md
Original file line number Diff line number Diff line change
Expand Up @@ -442,7 +442,7 @@ One row per place a call is written (or, for a synthesised edge, the construct t
- **typescript** — end_line / end_column come from the expression row; the call-site row itself records only the start.
- **javascript** — caller_id is the parser's enclosing method, or the module initializer for top-level code. end_line / end_column come from the expression row. `require()` is a module edge, not a call site.
- **typescript** — PROPERTY_READ and PROPERTY_WRITE rows are accessor invocations with no written call: the site is the property-access expression that runs the getter or setter, positioned from the expressions table, and callee_name is NULL because nothing was written; the accessor's name is on the callee's methods row. Filter them out with kind NOT IN (…) when counting calls.
- **python** — PROPERTY_READ, CONTEXT_MANAGER, ITERATION_PROTOCOL, METACLASS_CREATION and DYNAMIC_CALL rows are protocol or indirect edges with no written call: their site is the expression that triggers them, and callee_name is always NULL because nothing was written. SUBSCRIPT_CALL is NULL only when the subscript is not a written name (measured 206 of 337 rows on a Python subject). Filter them out with kind NOT IN (…) when counting calls.
- **python** — PROPERTY_READ, PROPERTY_WRITE, CONTEXT_MANAGER, ITERATION_PROTOCOL, BUILTIN_PROTOCOL, METACLASS_CREATION and DYNAMIC_CALL rows are protocol or indirect edges with no written call: their site is the expression that triggers them, and callee_name is always NULL because nothing was written. SUBSCRIPT_CALL is NULL only when the subscript is not a written name (measured 206 of 337 rows on a Python subject). Filter them out with kind NOT IN (…) when counting calls.
- **python** — The id is an EXPRESSION hash for a written call; a DECORATOR hash (PY_DECORATOR_…) for DECORATOR_APPLICATION and DECORATOR_* sites, positioned at the decorator line; and the class's TYPE hash for METACLASS_CREATION, positioned at the class declaration.

### `call_edges`
Expand All @@ -467,6 +467,7 @@ THE GRAPH. One row per (site, resolved target). A site with N possible targets h
| `new` | csharp | An object creation. The target is the constructed type's constructor, and it is never dispatched. |
| `ctor_delegate` | csharp | `: this(...)` or `: base(...)`. No name is written, so the target is structural. |
| `primary_ctor_base` | csharp | SYNTHESISED. A primary constructor's base invocation, written in the heritage clause: `class D(int a) : B(a)`. There is no call syntax anywhere in the body. FromExpr is the heritage type reference. |
| `implicit_base_ctor` | csharp | SYNTHESISED. The base class's parameterless constructor that a constructor with no `: base(...)` / `: this(...)` runs before its body, or that the implicit constructor of a class declaring none runs at its `new`. FromExpr is the heritage type reference, or the `new` site. |
| `delegate` | csharp | A call through a delegate value: `handler(x)` or `handler.Invoke(x)`. |
| `operator` | csharp | A user-defined operator invoked by operator syntax. |
| `conversion` | csharp | A user-defined conversion. An implicit one has no syntax at the call site. |
Expand Down Expand Up @@ -531,9 +532,11 @@ THE GRAPH. One row per (site, resolved target). A site with N possible targets h
| `DECORATOR_*` | python | Applying an unparenthesised decorator; the suffix is the parser's decorator kind: BARE, ATTRIBUTE, SUBSCRIPT, EXPRESSION (and CALL/ATTRIBUTE_CALL when the factory expression is not itself a call site). The site is the decorator hash. |
| `METACLASS_CREATION` | python | A class statement invokes its metaclass's `__new__` / `__init__` at import time, whether the metaclass is written on the statement (`class X(metaclass=M)`) or inherited from a base, and the nearest base's `__init_subclass__`. No written call; the site is the class's type hash. |
| `PROPERTY_READ` | python | Reading `obj.attr` where `attr` is a `@property` runs the getter; reading `Cls.attr` where the METACLASS defines `attr` as a property runs that getter. No written call; the site is the attribute-access expression. |
| `PROPERTY_WRITE` | python | Assigning `obj.attr = v` where `attr` is a `@property` with a setter runs the setter; `del obj.attr` runs its deleter. No written call; the site is the attribute-access expression. |
| `CONTEXT_MANAGER` | python | `with expr:` runs `__enter__` / `__exit__` (or the async pair). No written call; the site is the context-manager expression. |
| `ITERATION_PROTOCOL` | python | `for x in expr:` (and comprehensions) runs `__iter__` / `__next__` (or the async pair). No written call; the site is the iterated expression. |
| `SUBSCRIPT_PROTOCOL` | python | `x[k]` runs `__getitem__` (and `x[k] = v` / `del x[k]` the setter and deleter) of the receiver's class. No written call; the site is the subscript expression. Its own kind so it is never counted as a written call. |
| `BUILTIN_PROTOCOL` | python | `repr(x)`, `str(x)`, `len(x)`, `hash(x)`, `bool(x)`, `iter(x)`, `next(x)`, `abs(x)`, `format(x)` and `reversed(x)` run the matching dunder of the argument's class (str falls back to `__repr__`, bool to `__len__`). The written call is to the builtin; this edge is the dunder it runs. The site is the argument expression. Its own kind so it is never counted as a written call. |

**`call_edges.tier` values**

Expand Down
5 changes: 4 additions & 1 deletion graph/bundle/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -358,6 +358,7 @@ export const VOCAB: readonly VocabSpec[] = [
{ table: 'call_edges', column: 'kind', value: 'new', languages: C, meaning: 'An object creation. The target is the constructed type\'s constructor, and it is never dispatched.' },
{ table: 'call_edges', column: 'kind', value: 'ctor_delegate', languages: C, meaning: '`: this(...)` or `: base(...)`. No name is written, so the target is structural.' },
{ table: 'call_edges', column: 'kind', value: 'primary_ctor_base', languages: C, meaning: 'SYNTHESISED. A primary constructor\'s base invocation, written in the heritage clause: `class D(int a) : B(a)`. There is no call syntax anywhere in the body. FromExpr is the heritage type reference.' },
{ table: 'call_edges', column: 'kind', value: 'implicit_base_ctor', languages: C, meaning: 'SYNTHESISED. The base class\'s parameterless constructor that a constructor with no `: base(...)` / `: this(...)` runs before its body, or that the implicit constructor of a class declaring none runs at its `new`. FromExpr is the heritage type reference, or the `new` site.' },
{ table: 'call_edges', column: 'kind', value: 'delegate', languages: C, meaning: 'A call through a delegate value: `handler(x)` or `handler.Invoke(x)`.' },
{ table: 'call_edges', column: 'kind', value: 'operator', languages: C, meaning: 'A user-defined operator invoked by operator syntax.' },
{ table: 'call_edges', column: 'kind', value: 'conversion', languages: C, meaning: 'A user-defined conversion. An implicit one has no syntax at the call site.' },
Expand Down Expand Up @@ -667,9 +668,11 @@ export const VOCAB: readonly VocabSpec[] = [
{ table: 'call_edges', column: 'kind', value: 'DECORATOR_*', languages: P, meaning: 'Applying an unparenthesised decorator; the suffix is the parser\'s decorator kind: BARE, ATTRIBUTE, SUBSCRIPT, EXPRESSION (and CALL/ATTRIBUTE_CALL when the factory expression is not itself a call site). The site is the decorator hash.' },
{ table: 'call_edges', column: 'kind', value: 'METACLASS_CREATION', languages: P, meaning: 'A class statement invokes its metaclass\'s `__new__` / `__init__` at import time, whether the metaclass is written on the statement (`class X(metaclass=M)`) or inherited from a base, and the nearest base\'s `__init_subclass__`. No written call; the site is the class\'s type hash.' },
{ table: 'call_edges', column: 'kind', value: 'PROPERTY_READ', languages: P, meaning: 'Reading `obj.attr` where `attr` is a `@property` runs the getter; reading `Cls.attr` where the METACLASS defines `attr` as a property runs that getter. No written call; the site is the attribute-access expression.' },
{ table: 'call_edges', column: 'kind', value: 'PROPERTY_WRITE', languages: P, meaning: 'Assigning `obj.attr = v` where `attr` is a `@property` with a setter runs the setter; `del obj.attr` runs its deleter. No written call; the site is the attribute-access expression.' },
{ table: 'call_edges', column: 'kind', value: 'CONTEXT_MANAGER', languages: P, meaning: '`with expr:` runs `__enter__` / `__exit__` (or the async pair). No written call; the site is the context-manager expression.' },
{ table: 'call_edges', column: 'kind', value: 'ITERATION_PROTOCOL', languages: P, meaning: '`for x in expr:` (and comprehensions) runs `__iter__` / `__next__` (or the async pair). No written call; the site is the iterated expression.' },
{ table: 'call_edges', column: 'kind', value: 'SUBSCRIPT_PROTOCOL', languages: P, meaning: '`x[k]` runs `__getitem__` (and `x[k] = v` / `del x[k]` the setter and deleter) of the receiver\'s class. No written call; the site is the subscript expression. Its own kind so it is never counted as a written call.' },
{ table: 'call_edges', column: 'kind', value: 'BUILTIN_PROTOCOL', languages: P, meaning: '`repr(x)`, `str(x)`, `len(x)`, `hash(x)`, `bool(x)`, `iter(x)`, `next(x)`, `abs(x)`, `format(x)` and `reversed(x)` run the matching dunder of the argument\'s class (str falls back to `__repr__`, bool to `__len__`). The written call is to the builtin; this edge is the dunder it runs. The site is the argument expression. Its own kind so it is never counted as a written call.' },

// entry_points.reason
{ table: 'entry_points', column: 'reason', value: 'main', languages: ['java', 'csharp'], meaning: 'A static `main`. C#: a static `Main`, or the method top-level statements compile to.' },
Expand Down Expand Up @@ -748,7 +751,7 @@ export const NOTES: readonly NoteSpec[] = [
{ language: 'typescript', table: 'overrides', note: 'EMPTY — this table is Java-shaped. The TypeScript dispatch envelope is in dispatch_candidates, with basis `nominal` or `structural`.' },
{ language: 'typescript', table: 'type_instantiated', note: 'Every row has how = `new`. Not restricted to client provenance: a type the library constructs is still a type that exists at run time, and dropping it would narrow the envelope unsoundly.' },
{ language: 'typescript', table: 'call_sites', note: 'PROPERTY_READ and PROPERTY_WRITE rows are accessor invocations with no written call: the site is the property-access expression that runs the getter or setter, positioned from the expressions table, and callee_name is NULL because nothing was written; the accessor\'s name is on the callee\'s methods row. Filter them out with kind NOT IN (…) when counting calls.' },
{ language: 'python', table: 'call_sites', note: 'PROPERTY_READ, CONTEXT_MANAGER, ITERATION_PROTOCOL, METACLASS_CREATION and DYNAMIC_CALL rows are protocol or indirect edges with no written call: their site is the expression that triggers them, and callee_name is always NULL because nothing was written. SUBSCRIPT_CALL is NULL only when the subscript is not a written name (measured 206 of 337 rows on a Python subject). Filter them out with kind NOT IN (…) when counting calls.' },
{ language: 'python', table: 'call_sites', note: 'PROPERTY_READ, PROPERTY_WRITE, CONTEXT_MANAGER, ITERATION_PROTOCOL, BUILTIN_PROTOCOL, METACLASS_CREATION and DYNAMIC_CALL rows are protocol or indirect edges with no written call: their site is the expression that triggers them, and callee_name is always NULL because nothing was written. SUBSCRIPT_CALL is NULL only when the subscript is not a written name (measured 206 of 337 rows on a Python subject). Filter them out with kind NOT IN (…) when counting calls.' },
{ language: 'python', table: 'call_sites', note: 'The id is an EXPRESSION hash for a written call; a DECORATOR hash (PY_DECORATOR_…) for DECORATOR_APPLICATION and DECORATOR_* sites, positioned at the decorator line; and the class\'s TYPE hash for METACLASS_CREATION, positioned at the class declaration.' },
{ language: 'python', table: 'call_edges', note: 'A `boundary_lib` edge may point at a builtin (callee_provenance builtin, callee_label `builtin:NAME`) or at an unstaged import path (callee_provenance external) — neither has a methods row.' },
{ language: 'java', table: 'call_edges', note: 'A `boundary_lib` edge with callee_provenance external names a method of an ancestor type no staged IR declares (callee_label `external:<type>.<name>`, no methods row). A site whose receiver is declared as such a type is multi_inferred even with one client override: the platform method itself, and the platform\'s own subclasses, are the other possible targets. Stage the library to replace the label with the real method.' },
Expand Down
46 changes: 46 additions & 0 deletions graph/csharp/engine/call-edge-generation/call_chain.dl
Original file line number Diff line number Diff line change
Expand Up @@ -286,6 +286,52 @@ call_chain_edge(tr, caller, "-", ctor, "lib", "boundary_lib", "primary_ctor_base
method_decl("lib", _, _, _, ctor),
type_ctor("client", gk, caller).

// ── SYNTHESISED EDGES: THE IMPLICIT `base()` ────────────────────────────────
// A constructor that writes neither `: base(..)` nor `: this(..)` runs its base
// class's parameterless constructor before its own body, and a class that declares
// no constructor at all gets one that does the same. Neither is written anywhere, so
// without these edges a base constructor that every derived class runs has no caller.
//
// implicit_base_ctor(TypeGroup, Ctor): the constructor `base()` runs for a type --
// its base class's constructor that takes no argument, or, where the base declares
// no constructor either, the one the base's own implicit constructor runs.
implicit_base_ctor(gk, ctor) :-
type_extends("client", gk, bgk),
type_ctor("client", bgk, ctor), ctor_accepts_argc("client", ctor, "0").
implicit_base_ctor(gk, ctor) :-
type_extends("client", gk, bgk),
!type_ctor("client", bgk, _), type_group("client", _, bgk),
implicit_base_ctor(bgk, ctor).

// A constructor that hands off with `: this(..)` or `: base(..)`, or a primary
// constructor whose heritage passes arguments: the base constructor it runs is the
// one written there, already an edge.
ctor_has_initializer(m) :- call_is_base_ctor("client", e), expr_ultimate_method("client", e, m).
ctor_has_initializer(m) :- call_is_this_ctor("client", e), expr_ultimate_method("client", e, m).
ctor_has_initializer(m) :-
heritage_has_ctor_args("client", t, _), type_group("client", t, gk), type_ctor("client", gk, m).

// The FromExpr is the base class's entry in the heritage clause, as for a primary
// constructor's base invocation: the closest thing to where the call is written.
implicit_base_site(gk, tr) :-
type_extends("client", gk, bgk),
heritage_resolves("client", gk, pos, bgk),
heritage_of_entity("client", gk, pos, _, _, tr), tr != "".

// (1) A declared constructor with no initializer.
call_chain_edge(tr, m, "-", ctor, "client", "known_edge", "implicit_base_ctor") :-
type_ctor("client", gk, m), method_decl("client", _, _, _, m),
!ctor_has_initializer(m),
implicit_base_ctor(gk, ctor),
implicit_base_site(gk, tr).

// (2) `new T()` where T declares no constructor: the implicit one runs T's base's.
// The edge sits on the `new` site itself, beside its known_implicit_ctor row.
call_chain_edge(e, caller, "-", ctor, "client", "known_edge", "implicit_base_ctor") :-
call_ctor_implicit("client", e, gk),
implicit_base_ctor(gk, ctor),
call_from_expr("client", e, caller).

// ── ACCESSOR EDGES: A PROPERTY READ IS A CALL ───────────────────────────────
// `x.Name` invokes get_Name, `a[i]` an indexer accessor, `e += h` an add accessor.
// These have no cs_call_site row -- the parser cannot know whether `x.Name` is a
Expand Down
24 changes: 24 additions & 0 deletions graph/csharp/engine/containment/type-nesting.dl
Original file line number Diff line number Diff line change
Expand Up @@ -149,3 +149,27 @@ module_in_scope(prov, mod, outer) :-

// The GLOBAL namespace ("") is always in scope.
module_in_scope(prov, mod, "") :- module_decl(prov, _, _, _, mod).

// module_ns_chain(Prov, ModuleHash, Namespace) -- the namespaces a name written in this
// file is read from: the file's own namespaces and every dotted prefix of them, as
// text. A prefix that holds no type of its own (`Acme` over `Acme.Shop`) still
// qualifies a name written below it, so this is not ns_encloses, which needs one.
module_ns_chain(prov, mod, ns) :- module_namespace_own(prov, mod, ns), ns != "".
module_ns_chain(prov, mod, p) :- module_namespace_own(prov, mod, inner), ns_prefix(prov, inner, p).

// ── A USING WRITTEN INSIDE A NAMESPACE ─────────────────────────────────────
// `namespace Shop.Tests; using Results;` imports Shop.Results. C# reads the name in
// a using directive from the namespace the directive sits in, innermost outward,
// exactly as it reads any other name there. Taken as written it names a namespace
// that does not exist, and every type the file names through it reads as a library
// type.
//
// The IR does not record whether a using sits inside the namespace or above it.
// The language decides it instead: a using whose name is no namespace at all only
// compiles inside a namespace that has it as a child. So the relative reading is
// taken only when the name as written names nothing, and only for a child that
// exists -- a file whose namespaces have no such child gains nothing.
module_in_scope(prov, mod, full) :-
using_namespace(prov, mod, n), !ns_exists(prov, n),
module_ns_chain(prov, mod, e), full = cat(e, cat(".", n)),
ns_exists(prov, full).
Loading
Loading