Skip to content

0.1.9: impact and test-selection fixes across Python, JavaScript, TypeScript, Java and C# - #1882

Merged
swapnilpaliwal-sd merged 33 commits into
apps/integration-0.1.9from
release/0.1.9-impact-loops
Oct 10, 2026
Merged

swapnilpaliwal-sd merged 33 commits into
apps/integration-0.1.9from
release/0.1.9-impact-loops

Conversation

@swapnilpaliwal-sd

Copy link
Copy Markdown
Contributor

One PR for the 0.1.9 impact / test-selection work, so it can be reviewed and merged once. It merges six branches onto the current integration tip, in this order, with no conflicts:

Branch Supersedes
1 Python parser: an import of a module with a .pyi beside it resolves to the .py #1878
2 JavaScript: imports past a hand-written .d.ts, callbacks through test-hot functions, wrapper(impl) results #1877
3 TypeScript: JSX hand-offs, bound-method exports, platform built-ins not by-name callers, list element types through aliases #1876
4 Java: type initializers run by users of the type, field initializer after a comment (parser), equals/hashCode/library overrides through constructors #1880
5 C#: namespace-relative names, .cs endpoints for path, using resources, implicit base(), unstaged receiver types, static members through the type name, generic receivers #1881
6 Python: fixture-typed test parameters, update_wrapper/cast decorators, main-guard calls, on-demand dunders, property setters/deleters, Optional/union locals, src-layout and conftest import facts #1873 (rebuilt on the tip, without its engine-side .pyi workaround, which #1878 replaces)

Each fix has a case that fails on the base and passes here, with a control.

Test-selection recall (impact --tests; truth = test files that newly fail when one function is broken; repositories held out from tuning are scored separately):

Language Tuning repos Held-out repos
Java 0.930 → 0.982 0.946 → 0.992
TypeScript 0.74 → 0.84 0.74 → 0.74
C# 0.462 → 0.624 0.573 → 0.604
JavaScript 0.441 → 0.553 0.734 → 0.748
Python (16 repos) 0.540 → 0.592 (included)

Precision moves little (TypeScript 0.21 → 0.22, Java 0.181 → 0.183, Python 0.463 → 0.499, C# 0.400 → 0.374, JavaScript 0.388 → 0.336, falling only where recall rose). Known held-out costs are listed in the superseded PRs.

Checks on this branch: query cases python 330/330, typescript 297/297, javascript 319/319, java 343/343, csharp 241/241; Python engine suite 43/43 (torture agree 588, missing 46); parser Python suite 23/23. IMPACT_VERSION 63 → 66, so cached facts from before are rebuilt.

swapnilpaliwal-sd and others added 30 commits October 9, 2026 08:35
…class component tag hands over render and its lifecycle

A function written inside a JSX `{…}` — `onPointerMove={this.handleMove}`, `onClick={onSave}`,
`renderItem={drawRow}` — was reached by nothing. The parser writes each `{…}` as an expression root
of its own, linked to no element (an intrinsic tag has no call site at all), so no hand-over rule saw
it, and a handler read off a field or named by reference was cut off from every test that renders the
component and fires the event. The `{…}` is now the site that hands the function over, from the
function it is written in (callback_registered, as for `xs.map(cb)`); what the value is read through is
followed exactly as for an argument. A call there hands over what it returns and stays out.

A tag that builds a class component resolved only to its constructor, so render and the lifecycle
methods the renderer calls (componentDidMount, componentWillUnmount, …) had no caller, and nor did
anything below them. The tag now hands them over too, for a class that has a render method.

Co-authored-by: axiomcode-bot[bot] <334110751+axiomcode-bot[bot]@users.noreply.github.com>
A package's public functions are often its default instance's methods exported as consts:
`export const createDraft = immer.createDraft.bind(immer)`, `export const plain = engine.plain`.
The holder rules already knew what such a const holds (a method read as a value, through `bind`),
and a call to it in the defining module ran that method. A call from any other module names the const
through an import, and only a local variable, a parameter or a field was followed — so every caller
outside the defining module, its whole test suite included, ran nothing.

A call whose callee is an imported const, or a namespace import's member (`api.applyPatches(…)`),
now runs what that module's own variable holds. The import is followed to its module, never matched
by name: the same exported name bound in another module runs that module's method.

Co-authored-by: axiomcode-bot[bot] <334110751+axiomcode-bot[bot]@users.noreply.github.com>
… of a project method

With no standard library staged, `JSON.parse(s)`, `Promise.resolve(v).then(f)`,
`Object.keys(o).forEach(g)`, `Reflect.getMetadata(k, t)`, `document.createElement('a')` and
`console.error(e)` resolve to nothing, so every one was an untyped `x.parse()` to impact: a project
method that happens to be named parse, resolve, all, keys, error or getMetadata got every such line in
the repository as a by-name caller, and every test around those lines entered its closure.

The engine now traces the receiver to the platform's global object (an AMBIENT_GLOBAL reference to
JSON, Promise, Object, Reflect, Array, document, console, …), and what calling it returns, the way it
already traces a package's value: the site stays unresolved, is listed apart as a call on a package's
or the platform's value, and seeds no closure. A program that declares one of those names itself binds
it as its own variable and is unaffected; window, globalThis and self are left out, since a program may
hang its own functions on them. An untyped receiver stays a by-name caller.

Co-authored-by: axiomcode-bot[bot] <334110751+axiomcode-bot[bot]@users.noreply.github.com>
…tion beside it

The compiler's resolver prefers a declaration file: require('../') in a package whose
package.json names `types` (or an exports "types" condition), and import './x.js' beside
a hand-written x.d.ts, both resolved to the .d.ts. JavaScript never loads a declaration,
so every value imported that way was external and every call through it an untyped
receiver: a test of the package's own API reached nothing in it.

When the first answer is a declaration outside node_modules, the import is resolved again
with declarations hidden from the resolver (types/typings and the .d.ts substitution fall
through to main, the exports code conditions and the .js itself). A module that exists only
as a declaration keeps its first answer.

Co-authored-by: axiomcode-bot[bot] <334110751+axiomcode-bot[bot]@users.noreply.github.com>
…raint and an object-type alias

`plugins.map((p) => p.setup(app))` left `p` with nothing to dispatch on in two common shapes:

- the element is an object type written as an alias (`type Plugin = { setup(): … }`): the no-lib
  callback binding gave the parameter the element's TYPE only, and an alias of an object type has no
  type declaration, only members — so every call on it was unresolved, while an annotated parameter of
  the same type resolved through its shape. The callback parameter now takes the element's shape too.
- the list is typed by a type variable constrained to an array or tuple
  (`<Ms extends [Module, ...Module[]]>(...modules: Ms)`): its element had no type at all. The
  constraint is what every element is at least, as it already is for a `<T extends HasId>` receiver;
  the element is now read through it, for callbacks and for `for…of`, by the exact link to the type
  parameter or by its name in scope.

A plugin/module system built this way — a builder that maps over its modules and calls `init` — was
reached by none of the tests that drive it.

Co-authored-by: axiomcode-bot[bot] <334110751+axiomcode-bot[bot]@users.noreply.github.com>
…ments flow

The parameter fan cap refuses argument flow into a function called from more than
--dispatch-cap places, and it counted every site, tests included. A suite calls the API it
tests over and over, so exactly the functions under test went "hot": a constructor called
from 21 test files beside 13 library sites lost its parameters, a callback the library
handed it and stored on the instance (this.decode = options['decode']) was never followed,
and the library's own call through the field resolved only to the fallback beside it.

Two verdicts now. Whether the PROGRAM's arguments flow is decided by the program's own
sites; whether a TEST's arguments flow is decided by every site, as before, so nothing that
flowed before stops flowing: a plugin a test hands the program, which the program calls back
with its own class, still types what the test then calls (a version that dropped every test
argument lost exactly that; the case guards it). For the name count, the overcount used for
module-level functions, a test's bare call or `new` of the name leaves it, while a test's
member call that only shares the name (`lib.add(1, 2)` on an export that is a different
function, such as a curried closure) stays in it: dropping those too made a curried library's
graph three times larger and its index five times slower, for no change in any answer.

Co-authored-by: axiomcode-bot[bot] <334110751+axiomcode-bot[bot]@users.noreply.github.com>
…e was handed

`export default promisable(eachItem, 3)`, `const add = curry2(function add(a, b) {...})`:
the wrapper returns a closure that calls its parameter, and the module exports that
closure. The closure's call of the parameter is read context-insensitively, so it held
every function any of the wrapper's sites passed (past the fan cap: nothing), and a test
calling the export reached the closure and stopped there.

The wrapping site is already a value of its own (("wrap", site), value-flow.dl), used for
what a call through it returns. A call through it now also runs the function that site was
handed at the forwarded position, what the wrapper writes into that parameter, or what a
wrapped argument itself runs: one of a set, beside the edge to the closure the call keeps,
and under the dispatch cap (a parameter every wrapped export is passed to runs one of them,
and naming all of them says nothing). A wrapper whose closure never calls its parameter is
not a wrapper and runs nothing it was handed. Two engine goldens gain these edges (memoize
/ once / lazy results and the param-reassigned shapes), reviewed and blessed with --oracle.

Co-authored-by: axiomcode-bot[bot] <334110751+axiomcode-bot[bot]@users.noreply.github.com>
…it resolves to the .py

The project linker keys modules by qualified name, and a package that ships `impl.pyi`
beside `impl.py` declares that name twice. The later file won the map, and `.pyi` sorts
after `.py`, so `from .impl import *` and `import pkg` resolved to the stub. The engine
refuses a stub declaration as an edge target, so every call into such a package ended at
the library boundary and its tests reached nothing in it.

A stub no longer replaces a non-stub module of the same name. A stub with no `.py` beside
it is still the import's target.

Co-authored-by: axiomcode-bot[bot] <334110751+axiomcode-bot[bot]@users.noreply.github.com>
pytest fills a test's parameter with whatever the fixture of that name returned or
yielded, and no call site spells that call, so the parameter stayed untyped: every
method called on it was ambiguous_unknown, and everything derived from it (a local
assigned from one of those calls, a with block) went with it. Across ten public
suites 1,529 such calls were unresolved; a suite whose fixtures are annotated
already resolved its own.

value-flow.dl now treats the runner's call as one more argument reaching a
parameter: param_arg_type from the fixture's value type (its return, its yield, or
its declared return). The fixture serving a parameter comes from a syntax-only copy
of the runner's lookup (class, own module, nearest conftest): the existing
py_fixture_injection reads module_member_method for star-imported fixtures, which
is resolution, and its nearest-conftest MAX would then be a cyclic aggregate.
Star-imported and pytest_plugins fixtures still reach their tests through the
injection edge; they only stay untyped.

Measured with a behavioural oracle (break each of 600 sampled functions, record
which test files fail) on ten held-in repositories: impact --tests recall
0.668 -> 0.698, every-failing-file-selected 46.7% -> 50.5%, precision unchanged;
the three repositories carrying the pattern move, the seven without it are
byte-identical. Engine suite 43/43 with identical case results; query cases
310/310. New case fixture-value-types-the-parameter fails on the base engine on its
derived-local check.

Co-authored-by: axiomcode-bot[bot] <334110751+axiomcode-bot[bot]@users.noreply.github.com>
…(T, wrapper) returns wrapper

functools.update_wrapper(wrapper, wrapped) hands back `wrapper`, and typing.cast(T, x)
hands back `x`; both are documented, neither module is staged in a client-only run. A
decorator written `return update_wrapper(wrapper, f)`, or typed as
`return t.cast(F, update_wrapper(wrapper, f))`, was therefore opaque: every method it
decorates was decorator_replaced_target and no call through the attribute reached
anything. That shape occurs in five of sixteen public repositories measured.

A catalogue, py_returns_arg(DottedName, Position) in builtins.dl, lists the two
functions and the argument each returns; call_chain.dl follows such calls (to any depth)
to the returned name, matched through the import that binds the callee: a module
import, its alias (`import typing as t` is MODULE_IMPORT_ALIAS, which the copy.copy
rule beside it also misses), or a from-import.

On ten held-in repositories, path's found rate on runtime-proven chains goes
0.720 -> 0.744 (the repository with the shape: 0.52 -> 0.76); test selection and the
other verbs unchanged. Engine suite 43/43 with identical case results; query cases
313/313; case decorator-returning-update-wrapper fails on the base engine on both
decorator checks.

Co-authored-by: axiomcode-bot[bot] <334110751+axiomcode-bot[bot]@users.noreply.github.com>
…y the import walk

impact's `at import` rung names every test file importing a module whose BODY reaches
the change through calls that run, because a module that raises while being imported
fails every file importing it. A module body's calls inside `if __name__ == "__main__":`
are real edges, but they run only when the file is executed as a script, never on import.
Walking them named every importer of a module that ends in a demo block.

The fact export now records guard_only(module, callee) where every call site the module
body has to that callee lies inside a top-level main guard, and up_running does not take
those edges. Ordinary reachability is unchanged; only the import hop is.

Measured with the mutation oracle on ten held-in repositories, the at-import rung was
the second-noisiest (precision 0.16 over 720 selected test files), and 637 of those 720
were for targets that never run on import. On the repository carrying the shape its
at-import selections drop 713 -> 305 and test-selection precision 0.335 -> 0.373 with
recall unchanged; the control repository whose at-import selections were all correct is
unchanged. Query cases 315/315 python, 264/264 typescript, 333/333 java; case
main-guard-does-not-run-on-import fails on the base engine on the guarded check.

Co-authored-by: axiomcode-bot[bot] <334110751+axiomcode-bot[bot]@users.noreply.github.com>
…_"`) is a guard too

`if sys.platform != "win32" and __name__ == "__main__":` is false on import whatever the
other conjunct is, so the block cannot run then; an `or` could, and is not read as a guard.
The first guard rule missed the conjunction, which on the repository carrying the shape
was where almost all of the remaining false at-import routes came from.

There: at-import selections 305 -> 55, of which 53 fail under the mutation oracle; test-
selection precision 0.373 -> 0.446. Recall 0.810 -> 0.794: a few failing files had been
selected only through an at-import route that cannot run, right by accident; their real
route is one the graph does not see. Control repository unchanged. IMPACT_VERSION 65, so
fact caches written under 64 are not reused. Case extended with the conjunctive spelling;
query cases green.

Co-authored-by: axiomcode-bot[bot] <334110751+axiomcode-bot[bot]@users.noreply.github.com>
…hat builds the object, not from every constructor

impact takes a hop from a protocol member (a dunder no call site names) to whoever
constructs its type: you do not build a context manager without entering it. That holds
for __enter__/__exit__, __bool__, __hash__, __call__ (precision 0.61-0.80 under the
mutation oracle), and not for showing a value (__repr__, __str__, __format__), pickling or
copying it (__getstate__, __setstate__, __reduce__, ...) or deleting from it (__delitem__,
__delattr__, __del__): those run only when some code asks, and a constructor deep inside
other code says nothing about that. Measured on ten held-in repositories, __repr__ alone
was 124 selected test files at precision 0.05, across eight of them.

For those members the hop is now taken only from test code that constructs the type: a
test that builds the object is the one that asks for its repr. Dropping the hop outright
removed 403 false files but left 13 targets with a failing test and an empty answer; this
form keeps every answer non-empty.

Ten held-in repositories (this commit with the two main-guard ones): test-selection
precision 0.408 -> 0.454, recall 0.698 -> 0.694, empty answers unchanged at 84/535. Case
on-demand-dunder-hop-from-tests fails on the base engine on the __repr__ check; its control
(__eq__ keeps every constructor) passes on both. Query cases 318/318 python, 264/264
typescript, 333/333 java.

Co-authored-by: axiomcode-bot[bot] <334110751+axiomcode-bot[bot]@users.noreply.github.com>
…setter and deleter

The engine emitted a PROPERTY_READ edge for reading a @Property and nothing for
writing or deleting one, though `obj.x = v` runs `@x.setter` and `del obj.x` runs
`@x.deleter` exactly as a read runs the getter. A setter therefore had no caller: an
impact on it named no test, and everything the setter calls was cut off from whoever
assigns. Setters occur in five of sixteen public repositories measured; the torture
fixture documented the gap as a known miss.

type_property_accessor finds the setter / deleter on the class that wins the name in
the receiver's MRO (they share the getter's binding, so mro_lookup's single answer is
not enough); property_write_edge keys on the attribute access's STORE / DEL context;
the edge is exported as call kind PROPERTY_WRITE (schema vocabulary for Python, the
kind TypeScript already uses for its accessors), mapped to the `property` rung.

Torture: the CPython oracle confirms the new edge (agree 587 -> 588, missing 47 -> 46,
extra unchanged); f43's known-miss marker is removed with its docstring rewritten at the
same line count; goldens updated. Engine suite 43/43. Query cases 322/322 python,
264/264 typescript, 333/333 java; case property-setter-is-a-call fails on the base
engine on all three write/delete checks.

All six commits together, ten held-in repositories: test-selection recall
0.668 -> 0.700, precision 0.403 -> 0.452, impact source-caller recall 0.673 -> 0.686,
path found 0.720 -> 0.752, context recall@5 0.789 -> 0.799; no repository regresses
beyond 0.003 on any of them.

Co-authored-by: axiomcode-bot[bot] <334110751+axiomcode-bot[bot]@users.noreply.github.com>
…operand of a 3+ way union counts

Two gaps in "Optional[X] IS X", the rule that types a value annotated Optional[X] or
X | None:

- It existed for parameters, returns and fields but not for a LOCAL variable's
  annotation, so `ctx: Optional[Context] = ...` and `x: Foo | None = ...` left every
  call on the local unresolved while the same annotation on a parameter resolved.
- `|` is left-associative: `A | B | None` is `(A | B) | None`, so A and B sit two
  levels below the annotation, under a nested union, and the depth-1 element rule saw
  only that nested union (which names no type) and None. Every union of three or more
  operands lost its members, on parameters, returns, fields and locals alike.

binding_declared_nominal gains the Optional/union clause; union_operand walks nested
PEP 604 unions and feeds their operands to type_ref_element, resolved and by name.
Containers are untouched (union_operand is rooted at an Optional/union kind only).

Optional/union locals occur in 15 of 16 public repositories measured (96 written with
`|`, 50 with Optional[]/Union[]). Ten held-in repositories, against the previous
commit: path found 0.752 -> 0.756, impact source-caller recall 0.686 -> 0.687 (one
repository 0.795 -> 0.807); the new callers are a union's dispatch set and are labelled
`one of a set`, resolved-caller precision unchanged at 0.979. Engine suite 43/43,
torture unchanged; query cases 325/325; case optional-and-union-annotations fails on
the base engine on both the local and the three-operand checks.

Co-authored-by: axiomcode-bot[bot] <334110751+axiomcode-bot[bot]@users.noreply.github.com>
… a test file imports the conftests above it

The import facts named a Python module only by its path from the repository root, so in a src layout
`src/app/core.py` was `src.app.core` and no `import app.core` matched it: a src-layout repository had almost
no test-to-source imports, which the import walk (a function that runs while a module is imported breaks
every file that imports it) and the loads-the-change filter both read. A module is now also named from its
package root, the first directory above it that is not a package.

pytest imports every conftest.py from the rootdir down to a test file's directory before the file, so what a
conftest imports is imported for each test file beneath it; a test file now imports those conftests.

Co-authored-by: axiomcode-bot[bot] <334110751+axiomcode-bot[bot]@users.noreply.github.com>
`namespace Shop.Tests; using Results;` imports Shop.Results, and
`Reflection.Info.Create()` written inside namespace Shop names
Shop.Reflection.Info: C# reads the first segment of a using's name, and of a
qualified name, from the enclosing namespaces outward. The engine took both as
written, so the using imported nothing and every client type named through it
read as a library type (`new Failure(..)` was boundary_lib), and the qualified
receiver stayed untyped.

- module_in_scope: a using whose name is no namespace at all takes the child of
  the file's namespace chain that does exist (the IR does not say whether the
  using sits inside the namespace; a name that is no namespace only compiles
  there).
- type_name_cand: a type's qualified name is split at each dot into namespace
  prefix and dotted tail; the tail is a rank-1 candidate in every file whose
  namespace chain holds the prefix. Keyed from the declaration side.

Case a-namespace-name-is-read-from-its-namespace fails 4/6 on the base (the two
controls, a file in an unrelated namespace, pass on both).

Co-authored-by: axiomcode-bot[bot] <334110751+axiomcode-bot[bot]@users.noreply.github.com>
`axiomcode path Tests/OrderTests.cs Order.Place` was refused with "nothing
named 'Tests/OrderTests.cs' is declared" on every C# graph, although the file is
indexed: the file-endpoint branch matched .ts/.tsx/.js/.mjs/.cjs/.py/.java only.

Every extension a front end reads (ax_langs.BY_EXT) is now a file endpoint. An
extension outside the old list falls through to the name lookup when no indexed
file has that name, so `Owner.cs` still resolves a method `cs` of Owner (the
case's control).

Co-authored-by: axiomcode-bot[bot] <334110751+axiomcode-bot[bot]@users.noreply.github.com>
`using (var w = new Writer(..)) { w.Write(..); }` and
`using var w = new Writer();` left the receiver untyped: var_value_initializer
read LOCAL and CONST declarations only, and the IR gives these the
declarationKind USING. The resource is the disposable itself, so its
initializer is a value of its type exactly as a local's is.

Case a-using-resource-is-typed-by-its-initializer: both shapes fail on the base;
the control (a foreach variable is typed by its element, never by the
collection) passes on both.

Co-authored-by: axiomcode-bot[bot] <334110751+axiomcode-bot[bot]@users.noreply.github.com>
A constructor with no `: base(..)` / `: this(..)` runs `base()` before its body,
and a class that declares no constructor gets an implicit one that does the
same. Neither is written, so the base constructor had no caller: an abstract
base whose constructor every derived class runs read as uncalled.

New synthesised call_edges kind implicit_base_ctor (known_edge):
- from a declared constructor without an initializer (and not a primary
  constructor whose heritage passes arguments), FromExpr = the base's heritage
  type reference, as primary_ctor_base does;
- from a `new T()` site where T declares no constructor, beside its
  known_implicit_ctor row.
The target is the base's constructor that takes no argument, walking up through
bases that declare none. Registered in the bundle schema and ax_edges' kind
vocabulary (`ctor`).

Case a-constructor-runs-its-base-constructor: 4 positives fail on the base; the
controls (`: base(name)` and `: this(..)` constructors gain no implicit edge)
pass on both.

Co-authored-by: axiomcode-bot[bot] <334110751+axiomcode-bot[bot]@users.noreply.github.com>
`var services = new ServiceCollection(); services.AddWidgets();` and
`Configure(s => s.AddWidgets())` with `Configure(Action<IServiceCollection> c)`
never reached the client's `AddWidgets(this IServiceCollection s)`: both
receivers' types are unstaged, so they have no group, and call_recv_type_name --
the NAME the by-name rung (3) and the loose DI rung (7, #1472) compare -- had no
clause for either.

call_recv_type_name now also gives
- an implicitly typed local initialised with `new T(..)`: the T written there;
- an implicitly typed lambda parameter: the name of the delegate argument that
  types it (lambda_param_arg_ref, the same reference param_type reads).
So the lambda's call matches by name (resolved) and the local's is a rung-7
candidate (one of a set, keeping its external label).

Case an-unstaged-receiver-keeps-its-type-name: both positives fail on the base;
controls (an extension on an in-source `this` type, a lambda typed as another
unstaged type) pass on both; extension-on-unstaged-receiver unchanged.

Co-authored-by: axiomcode-bot[bot] <334110751+axiomcode-bot[bot]@users.noreply.github.com>
`var f = Context.Current.Factory; f.Create()` -- the getter of the static
property `Current` was an edge, but its result had no type: expr_type for a
MEMBER_ACCESS needed an expr_type on the qualifier, and a qualifier that names
a TYPE has none. So `.Factory` and every call after it were unresolved.

Two clauses mirror the instance ones with ref_names_type on the qualifier
(property and field). ref_names_type already refuses a name that also binds a
value, the language's member-over-type rule.

Case a-static-member-read-types-the-chain: four shapes (local, one chain, a var
of the static read, a static field) fail on the base; the "Color Color" control
-- a property named like the type is the value read -- passes on both.

Co-authored-by: axiomcode-bot[bot] <334110751+axiomcode-bot[bot]@users.noreply.github.com>
`Cache<int>.Get("k")`, `Registry<A, B>.Create()` and
`Pair<int, string>.Make().Use()` were unresolved: the parser emitted a
generic_name in expression position with typeArgumentCount set and NO
potentialQualifiedName, so the receiver had no name to resolve.

- parser: a generic_name row carries its identifier as the written name (its
  arity stays in typeArgumentCount).
- engine: expr_type_argc projects the count; ref_names_type and the call
  receiver's type_ref_demand resolve the name at that arity, so `Cache<int>`
  names Cache`1 and never a non-generic Cache. Every receiver written without
  type arguments has arity "0", which is what both clauses used before.

Case a-generic-type-name-is-a-static-receiver: three positives fail on the
base; the non-generic `Cache.Get` control resolves to its own type on both.
Parser C# suite 62/62.

Co-authored-by: axiomcode-bot[bot] <334110751+axiomcode-bot[bot]@users.noreply.github.com>
A call written in a field initializer has no method around it, so the engine names the
type as its caller, and a static or instance block is a <clinit> / <init_block> callable.
Nothing calls either, so the upward walk stopped there: a factory whose
`INSTANCE = new Factory()` throws breaks every test that touches the factory, and none of
them was selected.

impact.dl now hops from such an initializer (a type reached as a caller, or a <clinit> /
<init_block>) to whoever uses the type in a way that runs it: a call to one of its
members, a construction of a subtype, a read or write of one of its fields. The hop is
taken only where the walk arrives at the type as a caller, never from a type or field
target's own seed. Test routes through it carry a new rung, `at load`, ranked beside
`at import`. The SQL fast path declines a closure that reaches such an initializer.

Co-authored-by: axiomcode-bot[bot] <334110751+axiomcode-bot[bot]@users.noreply.github.com>
`static final F X = // why
     new F() { ... };` — the Java field extractor took the node right after `=` as the
initializer, and a comment is a named node in tree-sitter, so the whole initializer was
dropped: its calls, and an anonymous class it creates together with that class's methods.
A try-with-resources declaration had the same read. Both now skip comments, as a local
variable's initializer already did.

The comment-invariance twins gain a field initializer, a field holding an anonymous class
and a resource declaration; the case checks that a test reaching the anonymous class's
method through its interface is selected, beside the same shape with no comment.

Co-authored-by: axiomcode-bot[bot] <334110751+axiomcode-bot[bot]@users.noreply.github.com>
A method query is handed the field and field-access facts empty (KIND_SKIPS: only a field or
type target was thought to reach them), so the `at load` hop's field clause derived nothing
and a test that touches the initialized type only by reading one of its static fields
(`Factory.INSTANCE_NULL.lookup(...)`) was dropped. Both relations are now loaded for the
kinds that walk the closure; every other rule that joins them also needs a relation that
stays skipped for those kinds, so nothing else changes. The case gains that shape.

Co-authored-by: axiomcode-bot[bot] <334110751+axiomcode-bot[bot]@users.noreply.github.com>
…lds it

`equals`, `hashCode` and `toString` are run by a HashMap key, a StringBuilder, String.format,
an assertion; an override of a library method (`iterator` of an Iterable, `size` of a
Collection, `close`) by the for-each loop, the collection, try-with-resources. The call is
written on a library receiver or not at all, and Object's members are deliberately not
fanned to client overrides by the engine, so a change to one had no caller and selected no
test.

The protocol hop Python already has (whoever constructs the type runs its protocol
members) now has Java clauses: a member named equals / hashCode / toString, or one that
overrides a library method, is reached by the callers of the type's constructors and of its
subtypes' constructors. Still taken only from the change itself, under the `protocol` rung.
The SQL fast path declines such a target.

Co-authored-by: axiomcode-bot[bot] <334110751+axiomcode-bot[bot]@users.noreply.github.com>
@swapnilpaliwal-sd
swapnilpaliwal-sd merged commit 4b50e8e into apps/integration-0.1.9 Oct 10, 2026
12 checks passed
@swapnilpaliwal-sd
swapnilpaliwal-sd deleted the release/0.1.9-impact-loops branch October 10, 2026 03:48
swapnilpaliwal-sd added a commit that referenced this pull request Oct 10, 2026
TypeScript: multi-line test registrars, runner set-up files, Object.assign function objects, overload implementations (stacked on #1882)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant