mirror of
https://github.com/abhigyanpatwari/GitNexus.git
synced 2026-11-05 07:37:30 +00:00
271 commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
9772bcd24f
|
feat(api_impact): list the callers of the requesting function as indirect consumers (#3720)
* feat(api_impact): list the callers of the requesting function as indirect consumers The FETCHES edge is file-level, so api_impact saw only the file that sends the request. The files that call its request function never mention the URL and were missing from the report (#3644). The edge's reason now names the callables whose spans contain the request sites, one `|via:<id>` field each, under the containment rule the R3-6 sink set already uses; that rule moves into EnclosingCallableIndex so both stay one. Several sites of one file for one route now share the edge instead of colliding on its id. api_impact follows the CALLS edges into those callables one level up and reports the callers as `indirectConsumers`, apart from the direct consumers and outside the risk level. Direct consumer files and the handler file are left out. An index built before the attribution carries no field. Expo navigation sites carry their real line so they attribute to the component that contains them. Closes #3644 * fix(api_impact): attribute a request site by its AST enclosing callable A helper defined on the line of the request has the smaller span, so the line-only lookup named it and the callers of the real requesting function vanished from indirectConsumers. The parser now records the enclosing callable of each fetch site and of each wrapper call (through the call's position), and attribution prefers that identity; the span lookup remains for sites found by text scan. The same preference applies to the R3-6 sink set. A real analyze of the fixture followed by api_impact pins the helper case and the wrapper case. * test(bench): rebaseline receiver resolution for the indirect-consumers fixture The api-impact-indirect-consumers fixture adds three receiver-unresolved sites to the corpus: two `.then(...)` calls on the untyped return of `fetch(...)` and of the local wrapper `apiFetch(...)`, and one `req.query.path` read inside an inline Express handler with an untyped `req`. Call drops 138 -> 140, reads 30 -> 31, all kinds 168 -> 171. No shape-arm state and no existing site changed. * fix(api_impact): preserve module-scope request attribution Keep AST-confirmed module scope distinct from missing attribution for direct fetches, HTTP clients, and wrapper calls. Prevent overlapping callable spans from inventing indirect consumers or process sinks. Preserve span fallback for callable forms the generic identity walk cannot name. Add eight regression cases, including real analyze/api_impact coverage. Rebaseline receiver-resolution counts for the two then calls and one property read added by this PR's existing fixtures; shape expectations are unchanged. --------- Co-authored-by: Gergo Magyar <gergomagyar0@gmail.com> Co-authored-by: Gergő Magyar <gergomagyar@icloud.com> |
||
|
|
f3fa67b144
|
fix(routes): link consumers to wrapper-composed URLs (#3697)
* fix(routes): resolve wrapper URL prefixes Carry proven URL composition through lexical bindings and wrapper calls so consumers match the runtime mount path. Refs #2858 * fix(review): apply review findings * fix(routes): address PR review feedback (#3697) * fix(routes): avoid native parent walks in URL facts --------- Co-authored-by: Gergo Magyar <gergomagyar0@gmail.com> |
||
|
|
09b7a5b174
|
fix(ingestion): read more receiver-chain spellings (#3498) (#3667)
* fix(ingestion): name chain steps without their explicit type arguments C#, C++ and Rust put explicit type arguments inside the callee node, not beside it as TypeScript, Java and Kotlin do. The receiver-chain walk read the callee's text, so `svc.GetTyped<User>().Save()` built the step `GetTyped<User>`, which matches no member, and C++ built `getTyped<User>`. Rust's `svc.get_typed::<User>()` wraps the whole field access in a `generic_function`, so the walk found neither a name nor the inner receiver and built no chain at all. The walk now peels these callee nodes (C# `generic_name`, C++ `template_method`, `template_function` and `dependent_name`, Rust `generic_function`) before it reads the step name and the receiver. It is a node-type table next to the existing ones; nothing branches on a language. Type arguments choose an instantiation, never a member, so the name loses nothing. Shape arm: csharp and rust `explicitTypeArgs` go from VISIBLE-GAP to RESOLVES, to `User.Save`/`User.save`. cpp `explicitTypeArgs` keeps its drop: the step is now `getTyped`, but `template <typename T> T* getTyped()` has no return type until `T` is substituted, which is separate work. baseline.json and BASELINE.md record the move; the count arm and a corpus A/B against main are unchanged. The chain wire format is unchanged, so the receiver-chain codec version stays at 2. Parse caches need no manual bump: the parse-cache identity hashes every file under `core/ingestion`. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(ingestion): read receiver chains through C# `!` and Swift `(x)` The chain walk peels wrappers that denote exactly their operand, so the chain's base is `svc` rather than `svc!` or `(svc)`. Two spellings of that were missing. C#'s null-forgiving `svc!` is a `postfix_unary_expression`, so `svc!.GetUser().Save()` built `2|svc!|cGetUser`, whose base binds nothing. It now joins the `!`-gated entry Swift and Kotlin already use. The same node type carries `x++` and `x--`, and the `!` gate leaves those alone. Swift has no parenthesized-expression node: `(await svc.getUserAsync())` and `(holder)` parse as a one-element `tuple_expression`, and the walk stopped there. `(await svc.getUserAsync()).save()` built no chain, and `(holder).repo.save()` built one whose base `(holder)` binds nothing. A new gate peels a tuple only when it is exactly `(`, one element, `)`. That excludes Rust's real one-element tuple `(svc,)`, Swift's labelled `(x: svc)`, and every tuple of two or more. Shape arm: csharp `nonNullAssert` goes from VISIBLE-GAP to RESOLVES (`User.Save`). Swift `awaitParen` now builds `2|svc|cgetUserAsync|a`, but it was already a VISIBLE-GAP and stays one: Swift return types live on the Module scope, and the fold looks there only after the Swift/Dart hoist change lands. Swift `(holder).repo.save()` now resolves to `User.save`. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(ingestion): walk C# null-conditional access in receiver chains C# parses `svc?.GetUser()` as a `conditional_access_expression` whose receiver sits in a `condition` field and whose member sits in a `member_binding_expression` child. The chain walk read it like any other callee: it named the step `.GetUser` and found no inner receiver, so `svc?.GetUser().Save()` built no chain and could never resolve. As a receiver (`user?.Address.Save()`, `repos?[0].Save()`) the node stopped the walk, and its whole text became the base. A node-type table now says where a conditional access keeps its receiver. As a callee, the walk names the step from the member binding; an element binding (`handlers?[0]()`) names no member. As a receiver, a member binding becomes a field step and an element binding becomes an index step. Shape arm: csharp `optionalChain` goes from VISIBLE-GAP to RESOLVES (`User.Save`). `svc?.GetTyped<User>().Save()` also resolves, through the type-argument peel from the earlier commit. `repos?[0].Save()` now records its miss, which was silent before: `User[] repos` has no type binding yet. `user?.Address.Save()` already resolved through the receiver-text cascade; a resolver row pins it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(ingestion): read Ruby and Swift subscripts as index steps Two grammars spell a subscript as something the chain walk did not recognise. Ruby's `repos[0]` is an `element_reference`, which was missing from the subscript table, so the walk stopped there. The receiver has no `.` or `(` either, so the call vanished: no edge and no drop. Swift has no subscript node at all. `repos[0]` parses as a `call_expression` whose `call_suffix` holds `value_arguments`, the same as a call, except that the list is delimited by `[`/`]` instead of `(`/`)`. Walked as a call, it named no member, and `holder.users[0]` named `users` as if it were a method. A call node whose suffix argument list opens with `[` is now read as an index step. The opening token is the only thing that tells the two apart, and `svc.getUser(0)` stays a call. Shape arm: ruby `indexElement` goes from INVISIBLE-GAP to VISIBLE-GAP. `repos` is untyped, so there is nothing to fold, but the miss is now counted. swift `indexElement` builds `2|repos|i` and records its miss too, going from INVISIBLE-GAP to VISIBLE-GAP; resolving it needs a Swift `elementTypeOf` hook from a separate change. So does `holder.users[0].save()`, which now builds `2|holder|fusers|i` instead of a call step named `users`. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * docs: clarify receiver walker review limits * test: pin receiver-chain walker control spellings (#3667) - Kotlin calls share Swift's `call_suffix → value_arguments`; pin that a parenthesized and a generic Kotlin call stay call steps, never index steps. - Pin that a Swift trailing-closure call stays a call step. - Pin that C# `GetTyped<T>()` and C++ `ns::getTyped<T>()` without a member receiver are named for the member and mint no chain. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test: skip Swift chain rows when its grammar is unavailable (#3667) The walker rows loaded the optional vendored Swift grammar at module scope, so a host without its prebuild failed to collect every other language's test in the file. Gate Swift on isLanguageAvailable and report its rows as skipped; every other row still runs. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test: expect Swift awaitParen through hoisted returns (#3667) Rebased onto #3666, whose hoisted Swift return types let the walker's `(x)` chain resolve awaitParen to User.save. This carries the content the replaced merge commits held: the walker row now expects that edge and no drop, the receiver baseline marks swift awaitParen RESOLVES, and BASELINE.md keeps the measured main and stack integration notes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * docs(bench): label parent-only awaitParen note and drop issue claim (#3667) The #3666 section said Swift awaitParen still needed a fix. That holds for #3666 alone; combined with this walker it resolves to User.save with no drop, while dart nonNullAssert stays unsupported. Also stop calling the generic-arity limitation tracked by #3609, a general triage tracker. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Gergo Magyar <gergomagyar0@gmail.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
cc0b5d54f7
|
fix(swift,dart): type chained receivers through hoisted return types (#3498) (#3666)
* fix(swift,dart): type chained receivers through hoisted return types (#3498) Swift and Dart hoist every method's return-type binding out of the class body onto the Module scope, as C# does, but neither set hoistTypeBindingsToModule. The receiver walk therefore never looked there, and svc.getUser().save() had no receiver type. Both now set it. The flag alone was not safe. The hoisted bindings are keyed by bare method name, so with class A { getUser() -> User } and class B { getUser() -> Admin } in one file the Module scope keeps only B's entry, and a.getUser().save() resolved to Admin.save. C# and TypeScript already do this on main. So there is a new ScopeResolver toggle, hoistedTypeBindingsRequireOwner, which Swift and Dart also set. With it, the walk accepts a hoisted binding only when its declaration anchor encloses the declaration of a member of that name owned by the receiver's class. Otherwise it keeps climbing, and declines if nothing passes. The four walk-ups in compound-receiver.ts now share one helper. The toggle is opt-in. With the guard on everywhere, TypeScript loses correct edges where two same-named methods happen to return the same type (11 rows in inferred-field-receiver-matrix), because the Module map cannot hold both. Swift and Dart never had those edges. Bench: swift plainChain, plainDeepChain, optionalChain and nonNullAssert (INVISIBLE-GAP) and dart plainChain, plainDeepChain and optionalChain (VISIBLE-GAP) now RESOLVE with correct targets. No other cell and no count-arm figure moves. The inferred-field matrix gains correct Inner.compute edges for Swift (3 rows) and Dart (7 Alien rows). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(scope): guard hoisted return ownership * fix(scope): gate hoisted return replay checks * fix(scope): preserve parameterized return replay Keep exact callee return facts when ancestor candidates have matching known parameter types, while refusing same-owner overload ties. Refs #3666 * fix(scope): preserve known variadic return replay Compare complete captured parameter vectors across distinct ancestor owners, including variadic methods with unknown numeric arity. Keep unknown Swift parameter slots instead of compressing them away. Refs #3666 * test(swift): retain reviewed receiver fixtures * fix(scope): retain unknown overload candidates Keep unknown parameter slots possible without hiding conversion overloads. Check return-owner precision without requiring valid calls to stay unresolved. Refs #3498 * fix(scope): preserve conversion suppression Keep explicit empty conversion results final when parameter metadata is unknown. Add exact positive controls for receiver-fixture liveness. Refs #3498 --------- Co-authored-by: Gergo Magyar <gergomagyar0@gmail.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
673dc6968e
|
fix(rust): see through Box, Rc and Arc on struct field types (#3498) (#3665)
* fix(rust): see through Box, Rc and Arc on struct field types (#3498) The struct-field normalizer in rust/range-binding.ts cut a type at its first `<`. A `repo: Box<User>` field was typed `Box`, so `self.repo.save()` bound nothing. It also stripped a leading `&` but not a lifetime, so a borrowed field, which always carries one (`&'a User`), came out as `'a User`. Box, Rc and Arc auto-deref on a method call, so the field now takes their first type argument: bare or path-qualified (`std::rc::Rc<User>`), and nested (`Rc<Box<User>>`). Borrows with a lifetime or `mut` are stripped, and `Box<dyn Trait>` reduces to the trait through the stripDynBound helper that parameters already use (#2604). RefCell, Cell, Mutex, RwLock, Option and Vec are deliberately left alone. A method on the inner type needs `.borrow()`, `.get()`, `.lock()`, `.read()`, a match or an index first, so the receiver really is the wrapper, and a direct call through one must not bind. Shape arm: rust `decoratedFieldType` moves INVISIBLE-GAP -> RESOLVES, with the edge on `User::save`. The corpus has no smart-pointer or borrowed struct field, so the count arm and every other cell are unchanged, and the corpus edge (2838) and drop (192) sets match main exactly. Scope-capture fingerprints are unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(rust): keep opaque pointer fields wrapped * fix(rust): refuse opaque macro expansions * test(rust): pin pointer wrapper targets and opacity limits (#3665) - Assert a same-file Box keeps its own save target while User.save stays unbound (review thread on custom same-named wrappers). - Pin the documented workspace-wide recall limit: an unrelated unresolved import or macro_rules! keeps an otherwise valid pointer field wrapped. - Drop per-case logging and group the opaque/expansion cases in describe blocks. - Collapse the stacked pointer-field receipts in BASELINE.md into one current section listing every workspace-wide refusal. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * chore: preserve Rust integration notes * test(rust): assert complete pointer targets --------- Co-authored-by: Gergo Magyar <gergomagyar0@gmail.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
af5fd9665c
|
fix(csharp): bind nullable field types for receiver resolution (#3498) (#3664)
* fix(csharp): bind nullable field types for receiver resolution (#3498) A C# field written `User? repo;` got no type binding. Its type is a `nullable_type` node, and the field_declaration rules only matched identifier, generic_name and qualified_name types. Parameters, properties, method returns and foreach variables already have a nullable_type rule, so fields were the odd one out: the class scope had no binding for `repo`, and `this.repo.Save()` was dropped as receiver-unresolved. Add the missing field rule. interpretCsharpTypeBinding already strips the `?`, so the field binds `User`, as the property spelling does. Shape arm: csharp `decoratedFieldType` moves VISIBLE-GAP -> RESOLVES, with the edge on `User.Save`. No corpus fixture declares a nullable field, so the count arm and every other cell are unchanged, and the corpus edge (2838) and drop (192) sets match main exactly. Scope-capture fingerprints are unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(csharp): preserve nullable field ownership * fix(csharp): refuse unowned nullable types * test(csharp): guard nullable-member fixture cleanup (#3664) Address nexus-check review on resolvers/csharp.test.ts:1471: skip the temp-root removal when mkdtempSync never ran, so a setup failure is not masked by a secondary rmSync(undefined) TypeError. Matches the guard in csharp-nullable-field-review.test.ts. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * docs(bench): scope nullable wrapper claim to what the code does (#3664) The ownership-controls note said the nullable-field marker preserves custom generic wrappers. Its only control, Custom.List<User>?, passes through the qualified-type refusal instead. State that unqualified wrappers keep the wrapper type (no native control yet) and that qualified spellings, custom wrappers included, stay unbound. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * chore: preserve C# integration notes * fix(csharp): preserve generic argument qualifiers Ignore comments when checking outer nullable-field qualification. Refs #3664 --------- Co-authored-by: Gergo Magyar <gergomagyar0@gmail.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
36e16434ea
|
fix(php): type locals from namespaced-function factory calls (#3498) (#3662)
* fix(php): type locals from namespaced-function factory calls
`namespace App; use App\Mod as rp; $x = rp\make(); $x->m()` left `$x`
untyped although `make` is declared `: C` in `namespace App\Mod`, and the
inline `rp\make()->m()` recorded a drop. The query matched only an
unqualified callee (`function: (name)`), so a `qualified_name` callee minted
neither a call site nor a result binding; `\App\Mod\make()`, `Mod\make()`
and `namespace\make()` failed the same way, and nothing could have expanded
the path anyway: functions were never indexed by fully qualified name.
- A `qualified_name` / `relative_name` callee is captured as a free call
(tail name, written path as `@reference.qualified-name`) and as a
call-result binding that keeps the whole path, leading `\` included.
- New `php/qualified-function.ts` expands that path by PHP's name rules:
`\` is absolute, `namespace\` is the file's namespace, and a qualified
head goes through the file's class/namespace `use` table, else the file's
namespace. It indexes every `Function` def by lower-cased FQN per pass and
answers only when exactly one def declares the name.
- PHP implements `resolveQualifiedCallee` and `resolveQualifiedFreeCall`
with that one resolution, so a call's type and its CALLS edge agree.
`Qff\Decoy\make` in the fixture is never picked.
- A receiver bound to an in-program namespaced function with no return type
(`$x = rp\untyped()`) now records a `receiver-unresolved` drop.
A full lang-resolution A/B against the parent removes no CALLS/ACCESSES edge
and no drop. It adds 20 edges, all inside the new
php-qualified-function-factory fixture, and 2 drops (its two untyped sites).
The PHP capture golden gains the new fixture only. Existing fixtures' digests
are byte-identical.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(php): bind use-function imports outside PSR-4 by fully qualified name
`use function App\Mod\make; $x = make(); $x->m()` typed nothing, and the
call itself had no CALLS edge, when `make` is declared outside any PSR-4
path: loaded through composer `autoload.files`, or in a project with no
composer.json. Import resolution finds a function only through a PSR-4
directory or a file named after it, so the import bound nothing (or, with a
PSR-4 directory that holds other files, resolved to one that declares no
`make`). In the new fixture, `namedCall` even picked up another `make`'s
return type, mirrored by name from a sibling file, and emitted a wrong
`QffGadget.ping` edge.
After namespaces are known, a `use function A\f` whose module scope binds
nothing for its local name is bound to the one workspace function named
`A\f`, through the same per-pass FQN index as qualified callees. The fully
qualified name is the function's identity, so this guesses nothing; a
resolved import or a local declaration is left alone, and two declarations
of one name bind nothing. The IMPORTS edge is unchanged: finalize has no
namespaces yet.
Lang-resolution A/B against the parent: one wrong edge removed
(`namedCall -> QffGadget.ping`), two correct fixture edges added
(`namedCall -> make`, `namedCall -> QffWidget.ping`), and four correct
edges the combined corpus lacked because its merged composer configs map
`App\` to many fixtures: php-calls `create_user -> write_audit`,
php-overload-dispatch `run -> format_text` and `run -> format_text_padded`,
php-use-function-const `Calculator.process -> formatName`. Each FQN is
declared once in the corpus. Drops are unchanged.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* test(bench): rebaseline php captures and receiver counts
- receiver-resolution: the new php-qualified-function-factory fixture's two
untyped sites (`$x = rp\untyped(); $x->ping()` and inline
`rp\untyped()->ping()`) are each one call drop: call drops 154 -> 156,
all kinds 224 -> 226. No other drop moves in a same-corpus A/B, and
shape-arm cells are unchanged.
- scope-capture (php): corpus growth only. The query now captures
qualified function callees, but no existing fixture has one: parking the
new fixture reproduces the prior fingerprint byte-for-byte. It adds 96
capture groups and 9 fixture entries.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(php): fail closed on qualified calls the path cannot resolve
A qualified call site was named by its tail (`count` for `\count($x)`),
so when `resolveQualifiedFreeCall` refused the path, the free-call pass
fell through to its simple-name tiers and could bind a same-named
function in the caller's namespace: `\count($x)` -> a user `Qff\Ext\count`,
`\Vendor\Pkg\make()` -> `Qff\Ext\make`, and a twice-declared
`\Qff\Dup\make()` the same way. Before this branch such calls minted no
site at all, so these were new wrong edges.
The site is now named by its whole written path, the same text it carries
as `@reference.qualified-name`. The provider hook still resolves it by
PHP's name rules. Every simple-name tier (scope chain, implicit `this`,
global unique-name fallback) looks up a name with a `\` in it and finds
nothing, so a refused path (builtin, uninstalled package, ambiguous name,
multi-namespace file) leaves the call unresolved. This change is PHP-only.
Lang-resolution A/B against the parent: no edge or drop removed. Added
edges: the three new fixture functions' `new QffWidget()` calls. No edge
from builtinCall, vendorCall or ambiguousCall. Versus the pre-branch base:
29 edges added (25 in the fixture, plus the 4 `use function` edges from
|
||
|
|
e7ef82dfcb
|
fix(kotlin): type locals from package-qualified factory calls (#3660)
* fix(scope): follow namespace factory return types and record untyped drops `import mod as rp; x = rp.make(); x.m()` lost the receiver type even when `make` is declared `-> C`: `x` was bound to the raw dotted name `rp.make`, and nothing followed a dotted name to a function's return type. The inline `rp.make().m()` failed the same way in the compound resolver, which read `ns.f()` only as a construction. Resolve `ns.f` to `f`'s return annotation when `ns` is a namespace import, the fact `propagateImportedReturnTypes` already mirrors for a named import. The post-finalize chain follow uses it for bindings and the compound resolver for inline calls. Only sites that emitted nothing change: the full lang-resolution corpus emits the identical 2,838 CALLS/ACCESSES edges. Record the drops that left `impact` at "exact": - a plain receiver bound to a namespace function with no usable return type (`x = ns.untyped()`); - an outer call whose `file:line:col` key a resolved inner site already claimed (`ns.untyped().m()`, `s.repo.Delete(id)`). Only a duplicate capture of the same member hides a drop now. Receiver baseline: call drops 128 -> 151, reads 44 -> 48, ten shape-arm cells INVISIBLE-GAP -> VISIBLE-GAP; every new drop has no CALLS edge to its member (BASELINE.md lists them). The python.factory-qualified accuracy gap is removed; the python-scope fingerprint and capture golden gain the new fixture only. Fixes #3498 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(dart): type locals from prefixed-import factory calls `import 'mod.dart' as rp; final x = rp.make(); x.m()` gave `x` no type: local call inference accepted only a bare callee (`Callee()`), and the prefix-stripping type normalizer would have reduced `rp.make` to `make`. Bind a local initialized by exactly `prefix.member(...)` (direct or awaited) to the dotted callee and keep its prefix, so the shared chain follow types it from the namespace function's return annotation. Only call-derived bindings keep the prefix; annotated `prefix.Type` spellings are unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(zig): type locals from namespace-module factory calls `const rp = @import("mod.zig"); const x = rp.make(); x.m()` bound `x` to the namespace handle `rp` itself: the call-return rule reads a non-value head as the type being constructed (`Counter.init()`), and import bindings are not values. The member name was gone before the shared chain follow could use the new namespace return-type lookup. For a module-level handle on a lowercase file (a namespace module, by Zig convention), bind the dotted callee `rp.make` so the shared chain follow types it from `make`'s return annotation. File-struct handles (`@import("Page.zig")`, any spelling of the handle) keep naming the struct, so `Page.init()` is unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(bench): rebaseline captures and receiver counts for Dart and Zig - receiver-resolution: the Dart fixture's untyped factory adds one call drop (151 -> 152); every typed form in the Dart and Zig fixtures resolves. - scope-capture (dart): two existing dart-method-enrichment locals initialized by `obj.method()` / `Type.method()` gain a call-derived binding, plus the new fixture's captures. A full lang-resolution A/B emits the identical edges outside the new fixtures. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(scope): bind same-namespace inheritance bases before sibling visibility exists Cause: same-package / same-namespace visibility (`populateNamespaceSiblings`) is populated after the inheritance pre-pass and `buildMro`. A base written without an import (`class C extends B`, B in another file of the package) could bind only through the workspace-unique simple-name fallback, which any other package declaring a `B` defeats: no EXTENDS edge, an empty MRO, and lost calls through inherited members. In the combined lang-resolution corpus `java-grandparent-resolution` `c.greet().save()` dropped because `java-unrelated-method-collision` declares default-package A and B. Moving the sibling hook ahead of the pre-pass was measured and rejected. It retargeted correct edges and minted wrong ones, because the sibling channels carry candidates the pre-pass must not see: C# global-namespace types are consulted at every scope (same-file `Cat : Animal` bound another fixture's global Animal), namespace buckets merge across source roots with no proximity order (PHP, C#), and a qualified `extends app.base.Box` bound through a same-package `app.Box`. Fix: an optional `ScopeResolver.resolveImplicitInheritanceBase`, consulted by the pre-pass only when the shared chain bound nothing, the base was written bare (no qualifier, name capture at the start of the base expression), and no import claims the name. It adds edges and never retargets one. `resolveSameNamespaceInheritanceBase` picks the candidate in the deriving class's namespace, nearest by shared path prefix, refusing a tie. Java and Kotlin answer from their package facts, C# from the exact `namespacePrefix`. Corpus A/B (main vs this, same corpus): EXTENDS/IMPLEMENTS +42, all within their own fixture, 0 removed. CALLS: six `super.save()` edges keep their target but are now MRO-dispatched (`global`, as in isolation), one moves off another fixture's BaseModel.Save onto its own, and 7 are new (both grandparent `greet` calls, a default-method `validate`, the new fixtures). Receiver bench: call drops 128 -> 127, all kinds 192 -> 191, shape arm unchanged. Scope-capture fingerprints re-baselined for the added fixtures only (parked, --check passes on the old ones). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(java): resolve a duplicated package+type import to the importer's own root Cause: `buildJavaPackageIndex` kept the first file declaring each package+type. One repository can hold several build modules or sample projects that each declare `models.User`, so every `import models.User;` bound whichever copy was parsed first, a wrong IMPORTS edge at full confidence that carries the receiver types behind it. In the combined lang-resolution corpus 24 fixtures declare `models.User`, and e.g. `java-heritage` UserService.processUser called `java-assignment-chain/models/User.save`. Fix: the index keeps every declaring file in a sparse `duplicateTypes` map (only colliding pairs, so a normal workspace pays nothing), and a type import resolves to the declaring file sharing the longest leading path with the importer (its own module or source root). A tie, or an unknown importer, refuses rather than guessing. Wildcard imports are unchanged. Corpus A/B (previous commit vs this, same corpus): 40 CALLS edges that crossed into another fixture's User/Repo/Address/UserService are gone, 55 CALLS/ACCESSES edges now land in their own fixture (0 cross-fixture), and `java-parent-resolution` User IMPLEMENTS Serializable moves off `java-heritage` onto its own interface. Receiver-unresolved drops unchanged; import-target and receiver benches pass. Scope-capture java fingerprint re-baselined for the added fixture only (parked, --check passes on the old one). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * refactor(scope): expose the position a receiver-chain fold reaches `foldReceiverChain` answers "which class", so a chain whose last step is declared as a type the program does not contain (`Dictionary<string, User>`, `String`) reads only as a miss. That miss is informative to the receiver-origin classifier: every hop was typed by a program declaration and the last one names a library type. Extract the fold loop into `foldReceiverChainState` and export `foldReceiverChainPosition`, which returns the final position including one whose declared type named no class, and reports nothing to `recordReceiverType`. `foldReceiverChain` keeps its behaviour: it now wraps the same loop and makes the receiver-type report from the final state, as before. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(scope): classify library-rooted receiver drops from provider evidence `classifyReceiverOrigin` had one source of positive external evidence, `LanguageProvider.isBuiltInName`. That set also filters callee names out of the call graph, so it could not grow to cover host globals, stdlib imports or library-typed values without dropping real edges, and every such drop hedged `impact()` to `lower-bound`. It also read a base name as evidence even where the program bound that name itself: a parameter named `console` with an unresolved type, or an unresolved import of a built-in spelling from the program's own module, came back `external`. Add two provider data hooks read only by the classifier: `externalReceiverNames` (globals, platform roots, library types) and `isExternalModuleSpecifier` (import specifiers reserved for the runtime or stdlib). The classifier now answers `external` when: - the base is unbound at the site and is a built-in or listed name; - the base is bound only by imports that resolved to no program file and all name a library module; - the receiver chain folds, through declarations each owner makes, to a position declared as a library type (`this.data.Keys` with `Dictionary<string, User> data`), and the program declares nothing by the dropped member's name (an extension method or trait impl could be the real target). The fold skips ancestor-hoisted bindings, which can lend another class's member type to the step. A base the program binds (a parameter, a local, a resolved import, an import from a non-library module) never takes a name-based verdict. The in-program arms are unchanged and still win first. No drop is added or removed and no edge changes. Java supplies its reserved `java` package root for both hooks: the JVM refuses to define classes under `java.*`, so `java.util.Optional.of(...)` and `import java.util.Map` are rooted in the platform. Without the specifier hook, the stricter import rule would have demoted the existing `import java.util.List; List.of(...)` verdict to unknown. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(typescript): name JavaScript host globals as the program boundary `document.body.appendChild(...)`, `window.history.pushState(...)` and `Buffer.from(raw).toString()` were recorded `unknown`: none of `document`, `window`, `Buffer`, `process` or `globalThis` is in `BUILT_INS`, and they cannot be added there because that set also filters callee names, so `process` would drop every call to a program function named `process`. Each drop hedged `impact()` to `lower-bound` although no program node could have been the target. List the host objects in `EXTERNAL_RECEIVER_NAMES` and supply it to the TypeScript, JavaScript and Vue providers as `externalReceiverNames`. A name counts only where the program binds nothing by it and declares nothing by it, so a `document: Doc` parameter, an untyped `window` parameter, a local `navigator` and a program `class Buffer` keep their verdicts. `location`, `history`, `self` and `crypto` stay out: they are ordinary local names. Corpus: 5 call drops move from unknown to external. `process.env.*` (3 reads) stays in-program there because three fixtures declare a top-level `function process()`, one of them in a global script. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(python): name standard-library imports as the program boundary `os.environ.setdefault(...)` after `import os`, and `Path(__file__).resolve()` after `from pathlib import Path`, were recorded `unknown`. The imports resolve to no file in the program, but an unresolved import alone cannot be told apart from an in-program module the resolver missed, and nothing said that `os` and `pathlib` belong to the interpreter. Supply `isExternalModuleSpecifier` from a curated set of standard-library modules, matched on the specifier's first segment (`os.path` -> `os`). A relative specifier never matches. The classifier still requires the import to have resolved to no program file, so a local `os.py` that the resolver finds wins, and a parameter named `os` is the program's own. Generic nouns an application plausibly uses for its own modules (`types`, `queue`, `signal`, `secrets`, `string`, `email`, `http`, ...) are left out. Third-party packages are not listed: an unresolved `fastapi` import stays `unknown`. Corpus: 4 call drops move from unknown to external (`os.environ.setdefault` x3, `Path(__file__).resolve()`). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(zig): name @import("std") and @import("builtin") as the program boundary `std.sort.pdq(...)` and `const Allocator = std.mem.Allocator;` were recorded `unknown`: `std` is bound by `const std = @import("std")`, an import the resolver deliberately leaves external, and nothing told the classifier that the module is the standard library rather than an in-program file it failed to find. Supply `isExternalModuleSpecifier` for the two module names Zig reserves: `std` and the compiler-generated `builtin`. Exact names only, so `@import("std.zig")` (a file path) stays `unknown`, as does `root` (the program's own root module) and any module `build.zig` names. Corpus: 9 drops move from unknown to external: `std.sort.pdq` (2 calls, 2 reads), `std.mem.Allocator` (1 read), and the `zig-qualified-literal` literals `std.Thread.Mutex{}` and `std.mem.Allocator{...}` (2 calls, 2 reads). The last four are that fixture's edge negative control (`useExternal` must emit no edge, which still holds); their receiver is std, so `external` is the right origin. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(csharp): name the System namespace and Dictionary as the program boundary `System.Console.WriteLine(...)` was recorded `unknown`: `Console` is in `BUILT_INS` but the chain base is `System`, which is not, and `this.data.Keys` with `Dictionary<string, User> data` was recorded `in-program` because the base `this` resolves to a program class even though the member lives on a BCL type. Supply `externalReceiverNames` with `System` (the BCL root namespace), `Dictionary` (the one collection capture does not reduce to its element, because it takes two type arguments) and `string` (a keyword alias that no program can declare). The base check covers `System.Console`; the chain fold reaches `Dictionary<string, User>` through the program's own field declaration. A program `class System`, a field of a program type, and an extension method the program declares on `Dictionary` all keep their verdicts. Corpus: 3 call drops move from unknown to external (`System.Console.WriteLine`). The two `this.data.Keys/Values` reads in `csharp-nested-member-foreach` stay in-program there: the fixture's global-namespace `App` is one of several in the merged corpus, so `this` binds another fixture's `App` and the fold declines. Analysed alone, both read `external`. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(kotlin): name java.lang.System and the platform roots as the program boundary `System.out.println(...)` in Kotlin was recorded `unknown`: Kotlin's `BUILT_INS` lists stdlib function names, not `System`, which Kotlin/JVM imports by default from `java.lang`. Supply `externalReceiverNames` with `System` and the package roots no program can declare into (`java`, refused by the JVM; `kotlin`, reserved by the compiler for the standard library), and `isExternalModuleSpecifier` for imports under those roots. A program `object System` keeps the receiver `in-program`. Corpus: 1 call drop moves from unknown to external (`kotlin-object-literal-scope` `System.out.println`, whose decoy `println` on the enclosing object still gets no edge). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(bench): rebaseline receiver-origin buckets for library receivers The preceding commits label library- and runtime-rooted drops `external` instead of `unknown`. Call drops by origin move from 55 in-program, 44 external and 29 unknown to 55, 62 and 11. Total drops (192), call drops (128), every per-extension and per-shape count, and every shape-arm state are unchanged; a same-corpus A/B against main keeps all 2,838 CALLS/ACCESSES edges byte-identical. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(rust): type locals from module-qualified factory calls `mod modx; use crate::modx as rp; let x = rp::make(); x.m()` left `x` untyped although `make` is declared `-> C`. The call-return rule kept only the text before the first `::` (the `Foo::new()` idiom), so `x` was bound to the module `rp` and the function name was lost. `modx::make()` and `crate::modx::make()` failed the same way, and the inline `rp::make().m()` failed in the compound resolver, which cannot split a `::` path. With a `let modx = Other;` in scope, `modx::make()` even typed `x` as `Other` and emitted a wrong `Other.m` edge. - Rust keeps the whole callee path when every segment is lower-case: a module function, by rustc's lint-enforced casing. `Foo::new()` and `Self::new()` still read as the type. - New `ScopeResolver.resolveQualifiedCallee` hook. The shared chain follow, the compound call branch and the drop recorder hand a callee text no scope binds to the provider, which returns the callable it names; the shared side reads that callable's return annotation by definition identity and never splits the path. Rust answers with the module-tree walk behind `resolveQualifiedFreeCall`, so a call's type and its CALLS edge come from the same resolution. - A receiver bound to an in-program module function with no return annotation (`x = rp::untyped()`) records a `receiver-unresolved` drop, as #3498 does for `ns.untyped()`. - `rp::make()` did not resolve as a call either: the module-name filter rejected the alias `rp`, and under Cargo the import binds the module's `mod modx;` declaration rather than `modx.rs`. The `use` candidate now admits an alias that names a module and reads a module from its top-level declaration. A full lang-resolution A/B against the parent emits identical CALLS and ACCESSES edges outside the new fixture, except one new correct edge: `rust-ambiguous` `main -> models::Handler.handle`, typed from `let h = services::create_handler()`. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(bench): rebaseline rust captures and receiver counts - receiver-resolution: the new rust-qualified-factory-return fixture's two untyped sites (`x = rp::untyped(); x.m()` and inline `modx::untyped().m()`) are each a call and a callee-position read drop: call drops 152 -> 154, reads 48 -> 50, all kinds 220 -> 224. No other drop moves in a same-corpus A/B, and shape-arm cells are unchanged. - scope-capture (rust): corpus growth only. emitRustScopeCaptures is untouched, and parking the new fixture reproduces the prior fingerprint byte-for-byte; it adds 131 capture groups and 3 fixture entries. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(scope): ask resolveQualifiedCallee about dotted callee paths A dotted call-return binding (`x -> com.acme.mod.make`) whose head no namespace import binds came back from followChainPostFinalize untouched. The dotted branch tried only namespaceCallReturnType, which needs a two-segment path headed by a namespace import. The inline `com.acme.mod.make().m()` failed the same way in the compound resolver: once the object part resolved to no class and named no namespace import, the call branch returned nothing. A language whose package paths are dotted and never imported (Kotlin) could type neither form. Both sites now pass the dotted text to the provider's resolveQualifiedCallee after their own lookups miss, as they already did for undotted callees, and read the returned callable's return annotation by definition identity. The contract documents the two new consult points, and says that a provider must refuse a path whose head the call site can see as a value or type. No provider answers dotted text yet: Rust's hook reads only `::` paths. No edge moves, and the Rust, Python, Dart and Zig resolver suites pass unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(kotlin): type locals from package-qualified factory calls `val x = kqfmod.make(); x.m()` left `x` untyped although `make` is declared `: C`. `com.acme.mod.make()` failed the same way, and the inline `kqfmod.make().m()` dropped. Three gaps lined up: - the constructor-inferred query matches only a bare callee; - the navigation-call inference handles only typed locals and same-file classes; - nothing resolved a package name, because Kotlin imports declarations, not packages, so no import edge names `kqfmod`. The named-import form (`import kqfmod.make; make()`) already worked. - The local-assignment synthesis binds `x` to the whole callee path when the callee is a dotted path of plain names. The head must be lower-case and must not be declared or imported in the file as a value or type. A `@type.qualified-callee` marker stops interpretKotlinTypeBinding from reducing the path to the function name. Typed-local and same-file-class receivers (`repo.find()`, `Logger.create()`) are tried first, so they keep their meaning. - New Kotlin resolveQualifiedCallee. The package is everything before the last segment. A package spans files, and across them it must declare exactly one top-level function with that name. The index of package facts and module bindings is built once per pass. The hook refuses overloads. It also refuses a head the call site can see: anything the lexical claim walk finds, plus top-level names in same-package sibling files and star-imported packages, which that walk misses. - An untyped package function (`val x = kqfmod.untyped()`) now records a receiver-unresolved drop instead of an exact miss. New fixture kotlin-package-qualified-factory covers: - single- and multi-segment packages, and inline calls; - the untyped drop and the named-import control; - local, parameter, property, import, star-import and same-package values named like the package; - overloads, and companion/object member calls. A full lang-resolution A/B against the parent emits identical CALLS/ACCESSES edges and drops outside the new fixture. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(bench): rebaseline kotlin captures and receiver counts - receiver-resolution: the new kotlin-package-qualified-factory fixture records six drops, all in app/Callers.kt. Two are call drops on untyped receivers: `x.m()` on `val x = kqfmod.untyped()` and the inline `kqfmod.untyped().m()`. The other four come from `com.kqfacme.deep.make()` in deepCall and inlineDeep: each is a call drop plus a read drop on the `deep` receiver path. Call drops 154 -> 158, reads 50 -> 52, all kinds 224 -> 230. A same-corpus A/B shows no other drop change. Shape-arm cells are unchanged. - scope-capture (kotlin): moving the new fixture out of the corpus reproduces the prior fingerprint byte-for-byte. Base and new emitters differ by exactly six matches over the same corpus, all of them in the new fixture. Synthetic counts are unchanged, and scaling is 1.039 < 1.5. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * perf(scope): reuse qualified call file sets * docs(scope): clarify finalized type refinement * test(kotlin): sync measured capture counts * fix(rust): scope generic alias parameters * fix(scope): respect base candidate visibility * test(scope): preserve library member collision hedge * fix(scope): cover more library receiver evidence * fix(vue): classify imported runtime receivers * fix(scope): match written JVM base arity * fix(kotlin): refuse unproven package receivers * fix(kotlin): prove companion receiver headers * test(kotlin): complete JVM scope fixtures * fix(kotlin): unify package receiver shadow checks --------- Co-authored-by: Gergo Magyar <gergomagyar0@gmail.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
c30449394b
|
fix(scope): bind same-package inheritance bases when the simple name is reused elsewhere (#3659)
* fix(scope): bind same-namespace inheritance bases before sibling visibility exists Cause: same-package / same-namespace visibility (`populateNamespaceSiblings`) is populated after the inheritance pre-pass and `buildMro`. A base written without an import (`class C extends B`, B in another file of the package) could bind only through the workspace-unique simple-name fallback, which any other package declaring a `B` defeats: no EXTENDS edge, an empty MRO, and lost calls through inherited members. In the combined lang-resolution corpus `java-grandparent-resolution` `c.greet().save()` dropped because `java-unrelated-method-collision` declares default-package A and B. Moving the sibling hook ahead of the pre-pass was measured and rejected. It retargeted correct edges and minted wrong ones, because the sibling channels carry candidates the pre-pass must not see: C# global-namespace types are consulted at every scope (same-file `Cat : Animal` bound another fixture's global Animal), namespace buckets merge across source roots with no proximity order (PHP, C#), and a qualified `extends app.base.Box` bound through a same-package `app.Box`. Fix: an optional `ScopeResolver.resolveImplicitInheritanceBase`, consulted by the pre-pass only when the shared chain bound nothing, the base was written bare (no qualifier, name capture at the start of the base expression), and no import claims the name. It adds edges and never retargets one. `resolveSameNamespaceInheritanceBase` picks the candidate in the deriving class's namespace, nearest by shared path prefix, refusing a tie. Java and Kotlin answer from their package facts, C# from the exact `namespacePrefix`. Corpus A/B (main vs this, same corpus): EXTENDS/IMPLEMENTS +42, all within their own fixture, 0 removed. CALLS: six `super.save()` edges keep their target but are now MRO-dispatched (`global`, as in isolation), one moves off another fixture's BaseModel.Save onto its own, and 7 are new (both grandparent `greet` calls, a default-method `validate`, the new fixtures). Receiver bench: call drops 128 -> 127, all kinds 192 -> 191, shape arm unchanged. Scope-capture fingerprints re-baselined for the added fixtures only (parked, --check passes on the old ones). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(java): resolve a duplicated package+type import to the importer's own root Cause: `buildJavaPackageIndex` kept the first file declaring each package+type. One repository can hold several build modules or sample projects that each declare `models.User`, so every `import models.User;` bound whichever copy was parsed first, a wrong IMPORTS edge at full confidence that carries the receiver types behind it. In the combined lang-resolution corpus 24 fixtures declare `models.User`, and e.g. `java-heritage` UserService.processUser called `java-assignment-chain/models/User.save`. Fix: the index keeps every declaring file in a sparse `duplicateTypes` map (only colliding pairs, so a normal workspace pays nothing), and a type import resolves to the declaring file sharing the longest leading path with the importer (its own module or source root). A tie, or an unknown importer, refuses rather than guessing. Wildcard imports are unchanged. Corpus A/B (previous commit vs this, same corpus): 40 CALLS edges that crossed into another fixture's User/Repo/Address/UserService are gone, 55 CALLS/ACCESSES edges now land in their own fixture (0 cross-fixture), and `java-parent-resolution` User IMPLEMENTS Serializable moves off `java-heritage` onto its own interface. Receiver-unresolved drops unchanged; import-target and receiver benches pass. Scope-capture java fingerprint re-baselined for the added fixture only (parked, --check passes on the old one). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(scope): respect base candidate visibility * fix(scope): match written JVM base arity --------- Co-authored-by: Gergo Magyar <gergomagyar0@gmail.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
169aa5d960
|
fix(scope): label library-rooted receiver drops as external from provider evidence (#3658)
* refactor(scope): expose the position a receiver-chain fold reaches
`foldReceiverChain` answers "which class", so a chain whose last step is
declared as a type the program does not contain (`Dictionary<string,
User>`, `String`) reads only as a miss. That miss is informative to the
receiver-origin classifier: every hop was typed by a program declaration
and the last one names a library type.
Extract the fold loop into `foldReceiverChainState` and export
`foldReceiverChainPosition`, which returns the final position including
one whose declared type named no class, and reports nothing to
`recordReceiverType`. `foldReceiverChain` keeps its behaviour: it now
wraps the same loop and makes the receiver-type report from the final
state, as before.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(scope): classify library-rooted receiver drops from provider evidence
`classifyReceiverOrigin` had one source of positive external evidence,
`LanguageProvider.isBuiltInName`. That set also filters callee names out
of the call graph, so it could not grow to cover host globals, stdlib
imports or library-typed values without dropping real edges, and every
such drop hedged `impact()` to `lower-bound`. It also read a base name as
evidence even where the program bound that name itself: a parameter
named `console` with an unresolved type, or an unresolved import of a
built-in spelling from the program's own module, came back `external`.
Add two provider data hooks read only by the classifier:
`externalReceiverNames` (globals, platform roots, library types) and
`isExternalModuleSpecifier` (import specifiers reserved for the runtime
or stdlib). The classifier now answers `external` when:
- the base is unbound at the site and is a built-in or listed name;
- the base is bound only by imports that resolved to no program file and
all name a library module;
- the receiver chain folds, through declarations each owner makes, to a
position declared as a library type (`this.data.Keys` with
`Dictionary<string, User> data`), and the program declares nothing by
the dropped member's name (an extension method or trait impl could be
the real target). The fold skips ancestor-hoisted bindings, which can
lend another class's member type to the step.
A base the program binds (a parameter, a local, a resolved import, an
import from a non-library module) never takes a name-based verdict. The
in-program arms are unchanged and still win first. No drop is added or
removed and no edge changes.
Java supplies its reserved `java` package root for both hooks: the JVM
refuses to define classes under `java.*`, so `java.util.Optional.of(...)`
and `import java.util.Map` are rooted in the platform. Without the
specifier hook, the stricter import rule would have demoted the existing
`import java.util.List; List.of(...)` verdict to unknown.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(typescript): name JavaScript host globals as the program boundary
`document.body.appendChild(...)`, `window.history.pushState(...)` and
`Buffer.from(raw).toString()` were recorded `unknown`: none of
`document`, `window`, `Buffer`, `process` or `globalThis` is in
`BUILT_INS`, and they cannot be added there because that set also
filters callee names, so `process` would drop every call to a program
function named `process`. Each drop hedged `impact()` to `lower-bound`
although no program node could have been the target.
List the host objects in `EXTERNAL_RECEIVER_NAMES` and supply it to the
TypeScript, JavaScript and Vue providers as `externalReceiverNames`. A
name counts only where the program binds nothing by it and declares
nothing by it, so a `document: Doc` parameter, an untyped `window`
parameter, a local `navigator` and a program `class Buffer` keep their
verdicts. `location`, `history`, `self` and `crypto` stay out: they are
ordinary local names.
Corpus: 5 call drops move from unknown to external. `process.env.*`
(3 reads) stays in-program there because three fixtures declare a
top-level `function process()`, one of them in a global script.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(python): name standard-library imports as the program boundary
`os.environ.setdefault(...)` after `import os`, and
`Path(__file__).resolve()` after `from pathlib import Path`, were
recorded `unknown`. The imports resolve to no file in the program, but an
unresolved import alone cannot be told apart from an in-program module
the resolver missed, and nothing said that `os` and `pathlib` belong to
the interpreter.
Supply `isExternalModuleSpecifier` from a curated set of standard-library
modules, matched on the specifier's first segment (`os.path` -> `os`).
A relative specifier never matches. The classifier still requires the
import to have resolved to no program file, so a local `os.py` that the
resolver finds wins, and a parameter named `os` is the program's own.
Generic nouns an application plausibly uses for its own modules
(`types`, `queue`, `signal`, `secrets`, `string`, `email`, `http`, ...)
are left out. Third-party packages are not listed: an unresolved
`fastapi` import stays `unknown`.
Corpus: 4 call drops move from unknown to external
(`os.environ.setdefault` x3, `Path(__file__).resolve()`).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(zig): name @import("std") and @import("builtin") as the program boundary
`std.sort.pdq(...)` and `const Allocator = std.mem.Allocator;` were
recorded `unknown`: `std` is bound by `const std = @import("std")`, an
import the resolver deliberately leaves external, and nothing told the
classifier that the module is the standard library rather than an
in-program file it failed to find.
Supply `isExternalModuleSpecifier` for the two module names Zig
reserves: `std` and the compiler-generated `builtin`. Exact names only,
so `@import("std.zig")` (a file path) stays `unknown`, as does `root`
(the program's own root module) and any module `build.zig` names.
Corpus: 9 drops move from unknown to external: `std.sort.pdq` (2 calls,
2 reads), `std.mem.Allocator` (1 read), and the
`zig-qualified-literal` literals `std.Thread.Mutex{}` and
`std.mem.Allocator{...}` (2 calls, 2 reads). The last four are that
fixture's edge negative control (`useExternal` must emit no edge, which
still holds); their receiver is std, so `external` is the right origin.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(csharp): name the System namespace and Dictionary as the program boundary
`System.Console.WriteLine(...)` was recorded `unknown`: `Console` is in
`BUILT_INS` but the chain base is `System`, which is not, and `this.data.Keys`
with `Dictionary<string, User> data` was recorded `in-program` because
the base `this` resolves to a program class even though the member lives
on a BCL type.
Supply `externalReceiverNames` with `System` (the BCL root namespace),
`Dictionary` (the one collection capture does not reduce to its element,
because it takes two type arguments) and `string` (a keyword alias that
no program can declare). The base check covers `System.Console`; the
chain fold reaches `Dictionary<string, User>` through the program's own
field declaration. A program `class System`, a field of a program type,
and an extension method the program declares on `Dictionary` all keep
their verdicts.
Corpus: 3 call drops move from unknown to external
(`System.Console.WriteLine`). The two `this.data.Keys/Values` reads in
`csharp-nested-member-foreach` stay in-program there: the fixture's
global-namespace `App` is one of several in the merged corpus, so `this`
binds another fixture's `App` and the fold declines. Analysed alone,
both read `external`.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(kotlin): name java.lang.System and the platform roots as the program boundary
`System.out.println(...)` in Kotlin was recorded `unknown`: Kotlin's
`BUILT_INS` lists stdlib function names, not `System`, which Kotlin/JVM
imports by default from `java.lang`.
Supply `externalReceiverNames` with `System` and the package roots no
program can declare into (`java`, refused by the JVM; `kotlin`, reserved
by the compiler for the standard library), and `isExternalModuleSpecifier`
for imports under those roots. A program `object System` keeps the
receiver `in-program`.
Corpus: 1 call drop moves from unknown to external
(`kotlin-object-literal-scope` `System.out.println`, whose decoy
`println` on the enclosing object still gets no edge).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* test(bench): rebaseline receiver-origin buckets for library receivers
The preceding commits label library- and runtime-rooted drops `external`
instead of `unknown`. Call drops by origin move from 55 in-program, 44
external and 29 unknown to 55, 62 and 11. Total drops (192), call drops
(128), every per-extension and per-shape count, and every shape-arm
state are unchanged; a same-corpus A/B against main keeps all 2,838
CALLS/ACCESSES edges byte-identical.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* test(scope): preserve library member collision hedge
* fix(scope): cover more library receiver evidence
* fix(vue): classify imported runtime receivers
---------
Co-authored-by: Gergo Magyar <gergomagyar0@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
|
||
|
|
65b101407e
|
fix(rust): type locals from module-qualified factory calls (#3657)
* fix(scope): follow namespace factory return types and record untyped drops `import mod as rp; x = rp.make(); x.m()` lost the receiver type even when `make` is declared `-> C`: `x` was bound to the raw dotted name `rp.make`, and nothing followed a dotted name to a function's return type. The inline `rp.make().m()` failed the same way in the compound resolver, which read `ns.f()` only as a construction. Resolve `ns.f` to `f`'s return annotation when `ns` is a namespace import, the fact `propagateImportedReturnTypes` already mirrors for a named import. The post-finalize chain follow uses it for bindings and the compound resolver for inline calls. Only sites that emitted nothing change: the full lang-resolution corpus emits the identical 2,838 CALLS/ACCESSES edges. Record the drops that left `impact` at "exact": - a plain receiver bound to a namespace function with no usable return type (`x = ns.untyped()`); - an outer call whose `file:line:col` key a resolved inner site already claimed (`ns.untyped().m()`, `s.repo.Delete(id)`). Only a duplicate capture of the same member hides a drop now. Receiver baseline: call drops 128 -> 151, reads 44 -> 48, ten shape-arm cells INVISIBLE-GAP -> VISIBLE-GAP; every new drop has no CALLS edge to its member (BASELINE.md lists them). The python.factory-qualified accuracy gap is removed; the python-scope fingerprint and capture golden gain the new fixture only. Fixes #3498 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(dart): type locals from prefixed-import factory calls `import 'mod.dart' as rp; final x = rp.make(); x.m()` gave `x` no type: local call inference accepted only a bare callee (`Callee()`), and the prefix-stripping type normalizer would have reduced `rp.make` to `make`. Bind a local initialized by exactly `prefix.member(...)` (direct or awaited) to the dotted callee and keep its prefix, so the shared chain follow types it from the namespace function's return annotation. Only call-derived bindings keep the prefix; annotated `prefix.Type` spellings are unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(zig): type locals from namespace-module factory calls `const rp = @import("mod.zig"); const x = rp.make(); x.m()` bound `x` to the namespace handle `rp` itself: the call-return rule reads a non-value head as the type being constructed (`Counter.init()`), and import bindings are not values. The member name was gone before the shared chain follow could use the new namespace return-type lookup. For a module-level handle on a lowercase file (a namespace module, by Zig convention), bind the dotted callee `rp.make` so the shared chain follow types it from `make`'s return annotation. File-struct handles (`@import("Page.zig")`, any spelling of the handle) keep naming the struct, so `Page.init()` is unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(bench): rebaseline captures and receiver counts for Dart and Zig - receiver-resolution: the Dart fixture's untyped factory adds one call drop (151 -> 152); every typed form in the Dart and Zig fixtures resolves. - scope-capture (dart): two existing dart-method-enrichment locals initialized by `obj.method()` / `Type.method()` gain a call-derived binding, plus the new fixture's captures. A full lang-resolution A/B emits the identical edges outside the new fixtures. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(rust): type locals from module-qualified factory calls `mod modx; use crate::modx as rp; let x = rp::make(); x.m()` left `x` untyped although `make` is declared `-> C`. The call-return rule kept only the text before the first `::` (the `Foo::new()` idiom), so `x` was bound to the module `rp` and the function name was lost. `modx::make()` and `crate::modx::make()` failed the same way, and the inline `rp::make().m()` failed in the compound resolver, which cannot split a `::` path. With a `let modx = Other;` in scope, `modx::make()` even typed `x` as `Other` and emitted a wrong `Other.m` edge. - Rust keeps the whole callee path when every segment is lower-case: a module function, by rustc's lint-enforced casing. `Foo::new()` and `Self::new()` still read as the type. - New `ScopeResolver.resolveQualifiedCallee` hook. The shared chain follow, the compound call branch and the drop recorder hand a callee text no scope binds to the provider, which returns the callable it names; the shared side reads that callable's return annotation by definition identity and never splits the path. Rust answers with the module-tree walk behind `resolveQualifiedFreeCall`, so a call's type and its CALLS edge come from the same resolution. - A receiver bound to an in-program module function with no return annotation (`x = rp::untyped()`) records a `receiver-unresolved` drop, as #3498 does for `ns.untyped()`. - `rp::make()` did not resolve as a call either: the module-name filter rejected the alias `rp`, and under Cargo the import binds the module's `mod modx;` declaration rather than `modx.rs`. The `use` candidate now admits an alias that names a module and reads a module from its top-level declaration. A full lang-resolution A/B against the parent emits identical CALLS and ACCESSES edges outside the new fixture, except one new correct edge: `rust-ambiguous` `main -> models::Handler.handle`, typed from `let h = services::create_handler()`. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(bench): rebaseline rust captures and receiver counts - receiver-resolution: the new rust-qualified-factory-return fixture's two untyped sites (`x = rp::untyped(); x.m()` and inline `modx::untyped().m()`) are each a call and a callee-position read drop: call drops 152 -> 154, reads 48 -> 50, all kinds 220 -> 224. No other drop moves in a same-corpus A/B, and shape-arm cells are unchanged. - scope-capture (rust): corpus growth only. emitRustScopeCaptures is untouched, and parking the new fixture reproduces the prior fingerprint byte-for-byte; it adds 131 capture groups and 3 fixture entries. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * perf(scope): reuse qualified call file sets * docs(scope): clarify finalized type refinement * fix(rust): scope generic alias parameters --------- Co-authored-by: Gergo Magyar <gergomagyar0@gmail.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
a09f76be39
|
fix(cpp): type receivers declared or produced through qualified names (#3656)
* fix(cpp): resolve qualified receiver types by namespace, not by tail
Cause: a C++ receiver declared with a qualified type (`rp::C x = ...`,
`void f(a::Config c)`, `rp::Box<int> b`) typed nothing. The capture keeps
the qualifier, but the shared receiver lookups read a qualifier only as a
`.` namespace-import handle, and C++ has neither: `#include` binds no
handle and the index keys `rp::C` under `C`. The two places that did type
qualified spellings — the `auto x = ns::T{}` brace-init rule and qualified
member fields — reduced them to their tail, which binds the NEAREST
same-named class: `auto x = rp::C{}` reached a global `C` through another
include, and `a::Config` inside a `namespace b` declaring its own Config
reached `b::Config` (both confident wrong CALLS edges).
Fix: a ScopeResolver opt-in, `resolveQualifiedTypeSpellings`, that C++
sets. When a qualified declared or return type binds nothing,
`resolveClassBindingForName` and the compound resolver's return-type lookup
fall back to `resolveQualifiedInheritanceBase` — the route qualified base
classes and qualified type references already take: the qualifier is
matched against the candidate's namespace and a tie resolves nothing. It
runs after every existing lookup and after the template-argument match, so
it adds edges and never retargets one. The brace-init rule now captures the
whole qualified_identifier, and non-generic qualified member fields keep
their qualifier, so both resolve through the same route; generic members
stay tail-reduced because specialization matching keys on the bare base
name.
Corpus A/B (lang-resolution, all languages): no edge outside the new
fixture moved; drops unchanged. bench/scope-capture cpp re-baselined (one
brace-init capture text, plus the new fixture).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(cpp): bind qualified declarations that have no initializer
Cause: `rp::C x;` and `rp::C* x = nullptr;` emitted no type binding at
all. The plain-declaration annotation rules accept only a bare
`type_identifier` or `template_type`, and tree-sitter-cpp parses a
qualified type as a `qualified_identifier` wrapping one of those, so the
receiver of `x.m()` was untyped (no edge, and no drop either). The
initialized form `rp::C x = ...` was already captured by the `type: (_)`
assignment rule.
Fix: two @type-binding.annotation rules on the OUTER qualified_identifier
(plain declarator, and pointer inside an init_declarator) — the qualified
twins of the bare rules, one pattern per declarator shape like the
qualified member-field rules, with no node-type alternation. The spelling
is kept as written and resolved by the qualifier-aware route from the
previous commit.
Capture diff over the cpp corpus: 29 added @type-binding.annotation
matches on qualified locals, nothing removed or changed. Corpus A/B: no
edge or drop outside the new fixture moved. bench/scope-capture cpp
re-baselined.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(cpp): record return types of header function prototypes
Cause: only function DEFINITIONS (and in-class method prototypes)
recorded a return type. A free-function prototype — `D makeD();`,
`namespace rp { C make(); }` — recorded none, and the usual layout is
"header declares, .cpp defines": the .cpp is never included, so an
includer saw no return fact at all and `auto x = makeD(); x.m()` /
`makeD().m()` typed nothing (the inline form recorded a drop, the bound
form not even that).
Fix: @type-binding.return rules for a prototype with a plain or pointer
return. In a function body the same syntax declares a block-scope
function (the most vexing parse, `D x();`), whose binding would be
hoisted to the module and type every undeclared `x` in the file, so
captures.ts keeps a prototype match only at namespace scope — the
translation unit or a namespace / extern "C" body, through preprocessor
conditionals (include guards), single-declaration extern "C" and
template prefixes. A prototype declared in a namespace keeps its binding
in that namespace instead of the module: the binding's scope is also
where its type is looked up, and from the module a global `Widget` in
the same header won over `rp::Widget`. Callers read namespace members by
definition identity, which joins in any scope, and namespace members are
never mirrored to includers by bare name anyway.
A reference return (`D& f();`) gets no rule: such a function is not
declared as a Function at all (no declaration pattern), so a return fact
would have nothing to attach to.
Capture diff over the cpp corpus: one added match outside the new
fixture (cpp-return-type's header prototype, same type as its
definition); the gate drops only the fixture's deliberate `E y();`.
Corpus A/B: no edge or drop outside the fixture moved.
bench/scope-capture cpp re-baselined.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(scope): type an inline call receiver from its callee's own return type
Cause: the compound resolver's free-call branch (`make().m()`) read the
callee's return type BY NAME from the caller's scope chain, while the
chain follow that types the bound form (`auto x = make(); x.m()`) reads
it BY DEFINITION IDENTITY (`producerTypeRef`). A callee that arrives by a
channel nothing mirrors a return binding through — a C++ using-directive
— therefore typed the bound form and dropped the inline one.
Fix: when the name read finds nothing, fall back to the return type the
callee's definition declares, via `producerTypeRef` (now exported from
imported-return-types.ts; the only change there). Only for a callee the
name actually BINDS at the call: `findExportedDefByName` falls back to the
first workspace callable of that name, and reading a return type off that
guess is not evidence. Measured: without the binding check the corpus
gained 15 non-C++ edges (Go range/chain factories, Swift call results),
all reached through that workspace-name guess and one of them provably
wrong (swift-return-type's `user.save()` bound to another fixture's
User); with it, no edge outside the new fixture moves.
Corpus A/B: edges and drops outside the fixture identical to main.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(cpp): type receivers produced by a qualified factory callee
Cause: `auto x = rp::make(); x.m()` bound `x` to the literal type `auto`
— the bare `auto x = f()` constructor rule matches only an identifier
callee, so the generic assignment rule captured the placeholder — and
`rp::make().m()` reached the compound resolver as the free call
`rp::make`, whose name binds nothing. Neither had any route from the
qualified callee to its definition, so both typed nothing (`auto x =
rp::C()` likewise).
Fix:
- captures.ts promotes `auto x = ns::f()` / `auto x = ns::T()` (a
qualified_identifier whose innermost name is a plain identifier) to
@type-binding.constructor with the whole callee as the type, the
qualified twin of the bare rule; template factories keep their own
rule. interpret.ts drops any leftover `auto` / `decltype(auto)` binding,
which named no type.
- New ScopeResolver hook `splitQualifiedCallee` maps a callee spelling onto
the receiver/member pair the language's qualified CALL captures use (C++:
innermost scope segment, `a:🅱️:make` -> `b` / `make`), so shared code
never splits on `::`. The compound resolver's free-call branch, when the
callee's name binds nothing, resolves the definition through the existing
`resolveQualifiedReceiverMember` hook — the answer the call itself got —
and reads that definition's own return type by identity
(`producerTypeRef`). `auto x = rp::C()` resolves through the
qualified-type route instead.
Capture diff over the cpp corpus: one changed match outside the new
fixture (cpp-method-enrichment `auto category = Animal::classify(...)`
now binds the callee instead of `auto`; a class static method, which the
namespace lookup does not answer, so it stays untyped as before). Corpus
A/B: no edge or drop outside the fixture moved. bench/scope-capture cpp
re-baselined. The new cpp-captures rows also pin the namespace-scope gate
on prototype return types from the earlier commit.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(cpp): bind qualified receiver owners
Preserve complete namespace qualifiers and declaration-owned return facts. Resolve inline namespace and chained alias types without borrowing unrelated functions or classes; cover review collision and header-order cases.
* fix(cpp): honor qualified type visibility
Carry header alias activation positions through include chains. Refuse unresolved type-owned qualifiers without rejecting value names, and align the lexical-ownership fixture with the scope-index contract.
* style(cpp): format inline namespace lookup
* fix(cpp): preserve factory lookup position
Keep later local type declarations from hiding an earlier namespace
factory. Name overload and class-factory recall limits explicitly.
Refs #3656
* fix(cpp): expand factory return aliases
Apply the provider qualified-type hook in producer-return lookup.
Keep authoritative refusals and the original declaration context.
Refs #3656
---------
Co-authored-by: Gergo Magyar <gergomagyar0@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
|
||
|
|
caacc3bac2
|
fix(csharp): rank global-namespace types below the file's own namespaces (#3655)
* fix(csharp): rank global-namespace types below the file's own namespaces C# global-namespace types were stored in the scope-independent workspaceFqnBindings channel. lookupNameClaim returns that channel at every scope, while namespace siblings are consulted only at the module scope. A type name looked up inside a method therefore stopped at the first global type with that name. The walk never reached the file's own or same-namespace declaration. On the merged lang-resolution corpus this caused four field-chain drops in csharp-field-types and csharp-deep-field-chain. It also bound calls to other fixtures' global types. For example, csharp-hello's Demo.Greeter.Main called csharp-optional-params' Greeter. The C# hook now stores global types in namespaceFqnBindings under the '' key. Each file's accessibility list follows C# name lookup: the declared namespace, the using targets of that declaration, the enclosing namespaces, then the global namespace. The compilation unit's using targets come after it. A using after a file-scoped namespace belongs to that namespace. Shared walker logic is unchanged (comments only); PHP's FQN keys still use workspaceFqnBindings. Corpus A/B against main: the four drops resolve and no drop is added. Wrong cross-fixture edges are retargeted or removed. One global-namespace fixture now types `this` as its own class and reaches a duplicated global User. Receiver bench call drops 128 -> 125, all-kind 192 -> 188 (baseline and BASELINE.md updated; the old note that these sites declined on ambiguity was wrong). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(csharp): initialize voucher fixture payee * test(csharp): refresh initialized payee captures --------- Co-authored-by: Gergo Magyar <gergomagyar0@gmail.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
2b44565534
|
fix(resolution): correct receiver drops and property ownership (#3653)
Some checks are pending
Gitleaks / gitleaks (push) Waiting to run
CodeQL / Analyze (javascript-typescript) (push) Waiting to run
CodeQL / Analyze (python) (push) Waiting to run
Publish / Classify release event (push) Waiting to run
Publish / RC guard (marker + release-PR skip) (push) Blocked by required conditions
Publish / ci (push) Blocked by required conditions
Publish / Publish to npm (push) Blocked by required conditions
Publish / Build & Push RC Docker images (push) Blocked by required conditions
Scorecard / Scorecard analysis (push) Waiting to run
Trivy Image Scan / Trivy (gitnexus-web) (push) Waiting to run
Trivy Image Scan / Trivy (gitnexus-cli) (push) Waiting to run
* fix(js): withdraw receiver drops for object-literal key writes a later pass answers
The JS capture makes the enclosing object literal the receiver of each key
it writes (`return { wickRatio: row.a }`). Case 0 of the receiver-bound pass
treats any receiver text holding `.` or `(` as a compound expression, and a
literal holding `row.a`, `0.5` or `...seed` does, so the fold failed and the
site was recorded as a `receiver-unresolved` drop. The return-shape and
unique-name passes run later and answered 17 of these 19 writes with an
ACCESSES edge, but nothing withdrew the earlier drop.
Both passes now report the sites they emitted an edge for, keyed by file,
position, site kind and member name (position alone is shared by `a.b` and
`a.b.c()`), and run.ts withdraws the matching `receiver-unresolved` drops
once they have run. A site they leave unanswered keeps its drop.
The other two writes, in `SignalService.make()`, had no edge at all: the
own-return-shape tier read the caller's owner qualifier as
`SignalService.make#0`, so it never matched the method's keys
(`SignalService.make.secretFlag`) and three same-named candidates were
refused. The qualifier is now the qualified name after `<Label>:<file>:`
without the `#<arity>` suffix. It also stops cutting a positioned id
(`outer.parse@25:2`) down to its column.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(js): type a static field from its initializer for class-level receivers
`Config.DEFAULT.validate()` with `static DEFAULT = new Config()` produced no
CALLS edge. JS and TS keep a static field's type binding out of the class
scope on purpose: the scope has one binding map for static and instance
members, so the static binding would retype an instance field of the same
name. The class-level receiver route (`declaredTypeOfClassLevelField`) reads
the static Property node instead, but only its annotation-derived
`declaredType`, and an unannotated field has none. The type had nowhere to
live.
The field extractor now records `initializerType` for a field that declares
no type and is initialized by `new X()` / `new ns.X()` (the same two shapes
the scope queries bind), the parse worker stamps it on the Property node, and
the class-level route falls back to it after `declaredType`. The binding still
never enters the shared class-scope map, so the instance-twin hazard does not
return.
This resolves one hop: `Host.q.m()` now reaches `Alien.m`. A longer chain off
the field still goes through the compound fold, which types a field step only
from the class-scope map, so it stays unresolved. The matrix known-gap row
`static-read-without-a-twin-loses-its-type` flips to a positive row for TS,
with a JS twin added.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(js): keep CommonJS module.exports declarations out of receiver drops
`module.exports.arrowRequest = (url) => fetch(url)` declares this module's
`arrowRequest` export; the CJS capture rules already bind it like
`exports.memberRequest = …`. The same line also yields a write reference
whose receiver is `module.exports`, and because that text has a dot the
receiver-bound pass folded it as a compound value, failed, and recorded the
declaration as a `receiver-unresolved` write. The bare `exports.x = …`
spelling never reached that path.
A new ScopeResolver hook, `isModuleExportAssignment`, lets a language say a
write site is a module-export declaration. The receiver-bound pass consults
it only at the drop guard, after every case has failed, so it records no
drop and changes nothing else: the site is not marked handled, no edge is
added, and the existing module-level write edge stays. The JS and TS
resolvers implement it for a receiver of exactly `module.exports` (from the
minted chain, or the text when no chain was minted) when `module` resolves
to nothing in scope. A `module` parameter, as in a UMD wrapper, or a write
into a value hanging off the export object still records its drop.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(ts): record an owned-`this` chain receiver as owned-but-unbound
`this.route.go()` inside an object-literal method was filed as a generic
`receiver-unresolved` drop (or, depending on site order, as nothing). The
method scope owns `this` and gives it no type, which the pass detects with
`isReceiverOwnedButUnbound`, but that check was handed the whole receiver
text `this.route`. No scope owns a chain, so the call fell through to Case 0,
whose fold cannot type an owned `this` either.
The check now receives the receiver's base (from the minted chain, else the
text before the first `.`/`(`), the same base the `suppressReceiverLookup`
check already used. The call is recorded as `receiver-owned-but-unbound`,
its real reason, and still emits no edge: the dynamic-this negative control
holds. On the fixture corpus this moves only that site; no edge changes.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(resolution): preserve receiver ownership
Bind a return key's declaration write by exact file and position so
unrelated input accesses keep their ordinary candidate ranking. Stop at
untyped static overrides and resolve constructor initializers as values.
Preserve the combined drop guards after rebasing onto main, cover the
binding regressions, and refresh the receiver baseline after #3654.
Key answered sites by their full range to retain repeated-name outer
reads. Keep owned-but-unbound calls in the persisted completeness signal.
* fix(resolution): address feedback (#3653)
Classify module-export declarations using captured module kind before
suppressing receiver drops. Match same-named field metadata by source
position, including declarations on one line.
Require reference kinds in suppression assertions and guard temporary
fixture cleanup. Cover the reported failures with regression tests.
* perf(parser): index repeated field declarations
Sort private field buckets once and use binary search to avoid
quadratic scans while preserving declaration disambiguation.
* fix(ingestion): preserve capture metadata
Record module kind from the parsed tree on worker and main-thread paths.
Match Ruby field metadata at the accessor call instead of its argument.
* fix(ingestion): harden capture metadata
Keep Ruby reader/writer properties writable. Walk module-kind ASTs iteratively so deep expressions cannot abort extraction.
* test(ingestion): clarify worker metadata scope
* docs(ingestion): state capture metadata effects
---------
Co-authored-by: Gergo Magyar <gergomagyar0@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
|
||
|
|
d57f570dff
|
refactor(ingestion): colocate import resolution rules (#3670)
* refactor(ingestion): remove dormant import resolvers Remove the unused LanguageProvider importResolver contract, factory and strategy chain. Keep active scope resolver helpers and their semantic tests. Verified 5,309 corpus edges unchanged, 7,411 focused tests, typechecks, build and resolver benchmarks. Full local suite is still running; graph analysis remains partial on deleted and comment-only regions. * refactor: colocate language import helpers Keep import policy beside language resolvers and share only generic indexes. Pass the existing ordered candidates explicitly so lookup behavior and cache identities stay unchanged. * fix: defer MCP imports and narrow Python paths * fix(python): prefer packages in relative imports * test(python): pin package-first import lookup * test(python): use a valid parent-relative import --------- Co-authored-by: Gergely Magyar <magyargergo@users.noreply.github.com> Co-authored-by: Gergo Magyar <gergomagyar0@gmail.com> |
||
|
|
f6e42092b3
|
fix(zig): resolve import-alias, forwarding-constructor, anonymous-container and type-application receivers (#3652)
* fix(zig): bind the module of a one-member @import alias as a receiver
`const X = @import("f.zig").X;` reads `X` off an `@import(...)` receiver.
The named-import rule claims that builtin, so the inline-import branch
skipped it and never emitted the receiver-keyed namespace binding the deep
form (`@import("lib.zig").B.work`) and member-call receivers already get.
Every such read was recorded as a receiver-unresolved drop; the ones whose
alias shares the member's name only produced an edge through the lexical
read fallback, and a renaming alias (`const Gauge = ...DEFAULT_NS;`)
produced nothing.
`isZigInlineImportReceiver` now also accepts the value read of a keyword
one-member import declaration (never a type annotation), and a claimed
builtin that is a receiver gets the namespace binding instead of being
dropped. The named import itself is unchanged, so `Gauge` stays an import
binding. Clears the 9 F-A rows of the #3498 drop triage; the reads now
resolve through the written module (`import-resolved`).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(zig): type a forwarding type constructor as the type it forwards to
`fn Bridge(comptime T: type) type { return Builder(T); }` (Lightpanda's
`js.Bridge(T)`) names no container of its own. `zigReturnTypeIsNominal`
drops `type` returns and `zigTypeConstructorOf` only names a container
returned directly, so a receiver typed `Bridge(X)` reached a Function that
owns no members and every call on it was lost.
A type constructor whose body is a single `return <TitleCase call>` (and
whose callee is not one of its own parameters) now binds `Bridge ↦ Builder`
through its return-type binding. The binding is declared in Bridge's own
scope, so a chain followed from an importer still looks `Builder` up where
it is visible. The new aliases.zig `b12` case (a named import of the
forwarding constructor, bound and inline spellings) resolves into
generic.zig's Builder. The namespace-qualified spelling of the #3498 F-B
rows (`generic.Bridge(Local)`) additionally needs the `ns.f` return-type
fold, which this binding feeds.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(zig): resolve a member read off an anonymous container in place
`struct { fn lessThan ... }.lessThan` (the comparator handed to
`std.sort.pdq`) and `const byteSize = struct { fn it ... }.it;` read a fn
value off an anonymous container. The receiver capture was the container's
whole body, which names nothing, so each read was a receiver-unresolved
drop and the qualified-argument value reference declined. The only edge
came from the lexical read fallback at the site's anchor, which starts at
`struct` and therefore inside the container's own Class scope: the
container "accessed" its own fn.
A reference group whose receiver is a container node now carries the
identity that container is minted under (`Sorter.sortBoth$1`, via
`zigContainerName`), and its site anchor moves to the member, which is
evaluated in the enclosing scope. The reads resolve as ACCESSES from the
fn that evaluates them (and, for the pdq argument, a value-ref USES); no
CALLS edge. Clears the 3 F-C rows of the #3498 drop triage.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(zig): type a type-constructor call used as a receiver
`Stack(u8).init()` calls `init` on the TYPE `Stack(u8)`. The free-call
branch of `resolveCompoundReceiverClass` found `fn Stack`, found no return
binding (it returns `type`), and fell into the construction path, which
declines for a language without `constructionSyntax`. The call and its
callee-position read were dropped, while the bound form
`var t = Stack(u8).init(); t.top()` resolved.
Add a language-neutral `ScopeResolver.isTypeApplicationCall(callee)` hook.
When it answers true, the construction path types the receiver as the
class the callee names (direct or namespace-qualified), exactly as a bare
construction does; the callee's return-type binding still wins. Zig answers
with `isZigTypeConstructorCallee`, the text twin of
`isZigTypeConstructorCall` (a TitleCase last identifier). Clears the
2 F-D rows of the #3498 drop triage (main.zig:42 call + read).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* test(bench): re-baseline receiver resolution for the Zig receiver fixes
The four Zig fixes clear 14 receiver-unresolved sites in the corpus:
all-kind drops 192 -> 178, reads 44 -> 31, calls 128 -> 127 (Zig 11 -> 10,
no-chain 27 -> 26, in-program 55 -> 54). Nine one-member `@import(...).X`
reads, three anonymous-container reads and the `Stack(u8).init()` call and
read now resolve. Writes and every shape-arm state are unchanged; no new
drop was introduced. BASELINE.md records the per-site breakdown.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(zig): keep type applications lexical
* fix(zig): bind typed inline imports
---------
Co-authored-by: Gergo Magyar <gergomagyar0@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
|
||
|
|
2b6542737a
|
fix(go): resolve imports within selected modules (#3654)
* fix(go): resolve imports against every go.mod in the repository The Go resolver read only the repository-root go.mod, and its import target ignored the importing file. With no root go.mod, every import took the GOPATH suffix match down to a two-segment tail. Two modules' `internal/repository` then both answered `.../internal/repository`, and a module whose directory differs from its path (`/vN`, `services/orders` for `github.com/acme/orders`) resolved to nothing. With a root go.mod, imports of a nested module were not the root's and dropped, while the root module's own packages matched any directory ending in the package path, including nested modules' trees. Capture every go.mod and go.work from the scan's path list, as Dart does for pubspec.yaml; standalone and rename callers rescan with the same walker. For each import, the module whose path is the longest `/`-prefix owns it. The package is the exact directory under that module's root, unless a nested go.mod removes the directory from the module. Several go.mod files declaring one path are disambiguated by the importer's own module, then by its go.work `use` list, else left unresolved. Unowned imports from inside a module stay unresolved; the GOPATH suffix match remains only for importers outside every module. In the combined lang-resolution corpus, no CALLS or ACCESSES edge is removed or retargeted against the parent commit, and 70 are added outside the two new fixtures. Drops are unchanged at 179: these sites were silent. The import-target bench's go arm now passes no module config, and its answers and fingerprint are unchanged. The scope-capture go fingerprint moves for the new fixtures only. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * perf(go): look up an importer's module only when a path is contested `locateGoImport` walked to the importer's nearest `go.mod` and `go.work` for every import, though most imports (standard library, dependencies) are owned by no in-repo module and need neither, and every candidate package directory repeated the same ownership walk. The walks are now lazy (only a module path several `go.mod` files declare consults them) and memoized per directory on the config's index, and a path with no backslash skips the normalizing replace. The Go package index is fetched once per import instead of per candidate directory. Behavior-preserving: the lang-resolution corpus emits the identical 3,002 CALLS/ACCESSES edges before and after. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(go): surface go.mod read failures instead of dropping the module Go's workspace capture skipped any manifest it could not read and turned a failed standalone scan into "no modules". Either one silently erases a module boundary: its imports then resolve against the enclosing module or fall back to GOPATH suffix matching, the misresolution this capture exists to remove, and a rename refresh compares a different config against its preview's. `captureResolutionConfig` requires capture failures to throw. - A manifest that fails to read now throws; the standalone scan propagates its error. - Manifests skipped on purpose stay skipped, named in one place: symlinks, which can point outside the repository (Dart's manifest capture excludes them too), non-files, files over the source-file size cap, and files gone since the scan listed them. - Manifests are read 32 at a time; capture blocks the scan, and k8s-style trees have hundreds of go.mod files. Tests: data-driven rows for each skipped shape, a go.mod that fails to read, and a standalone scan of a non-directory root. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(bench): re-measure Go counts and fingerprint on top of #3651 Rebased onto main after #3651. Import resolution across every go.mod clears the 13 Go call drops left in the combined corpus (go-chain-call, go-grouped-type-decl, go-interface-field-dispatch, go-pointer-receiver-field-chain): call drops 148 -> 135, all kinds 208 -> 195, .go call drops 14 -> 1. No drop added; shape-arm states unchanged. The scope-capture go fingerprint moves for the two new fixtures only: parking them restores main's acee4449f691d9138e68e7b2b19da863fc2c89b0e9846acdf10e93d76b2eec78 byte-for-byte. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(go): read go.mod through one handle so it cannot change mid-check CodeQL flagged the manifest read as a file-system race: `lstat` checked the path and `readFile` then opened it again, so a different file could be read than the one checked. The manifest is now opened once (O_NOFOLLOW refuses a leaf symlink, O_NONBLOCK keeps a FIFO from hanging) and its type, size and contents all come from that handle, the way Dart's manifest reader works. A platform without O_NOFOLLOW still refuses a link after the open. The read-permission test is POSIX-only: permission bits do not block reads on Windows. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(go): skip the unreadable-go.mod test as root; correct a fixture comment Review follow-up for #3654. - The permission test asserted EACCES on a mode-000 go.mod, which root can still open. It now skips for root as well as on Windows. - The orders-service ledger fixture said it shares its method names with billing's; only Save is shared. The comment says so, in the same two lines. Comment text is part of the Go captures, so the golden gains the new text and the scope-capture go fingerprint moves: 3ba3b6e1551311ddb0e9330c939ac995856b487303b6f64bc89da3689a6d9d2c -> b1dfa8e1a8d92fd9d5779a02bc4bd99f54ec87adae4a326be08b74cb48fd5ef3, with fixture and group counts unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(go): enforce module visibility (#3654) Limit local providers to the importing module and peers selected by its nearest go.work. Treat failed package-qualified receiver lookup as final for direct calls, field chains, and method values so bare-name fallback cannot recreate calls into independent modules. Add import/call boundary regressions and remeasure receiver drops. The same-corpus comparison removes exactly two false calls and adds no edges. Validation: 7,263 resolver tests, both typechecks, lint, and import-target, scope-capture, and receiver baseline checks pass. Two parser-identity checks affected by local snapshot/build drift pass after freezing inputs and rebuilding. The full local run continues; final CI remains required. * fix(go): reject ambiguous package providers (#3654) Check all selected module prefixes for package presence. Preserve a shorter-only provider and refuse imports supplied by multiple modules. Cover all four presence cases and correct the receiver suppression comment. Note: the full local suite hit a host GLIBCXX_3.4.31 failure in parse-cache-identity-package.test.ts; focused Go tests and both TypeScript checks pass. --------- Co-authored-by: Gergo Magyar <gergomagyar0@gmail.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
ab0b03846d
|
fix(go): type receivers through package-qualified declared types (#3651)
`normalizeGoTypeName` cuts the package qualifier from field, parameter and local types, so `*models.User` is stored as `User` and the bare lookup either refuses the name or binds the wrong package once two packages declare a `User`. A new optional `ScopeResolver.qualifiedTypeLookupSpelling` hook recovers `models.User` from the written spelling; receiver typing (compound receivers, simple bindings and callable-value-flow method values) tries it first, grounded on the qualifier's import with the unique-name fallback off, and falls back to the bare lookup on any miss. The qualifier is read from the module scope so `vault *vault.Vault` cannot shadow its import, and a chain step keeps a binding's own written spelling so a binding whose type the step replaced never borrows another binding's package. Corpus A/B against main: no edge removed or retargeted outside the new fixture; 19 Go edges added and 13 Go drops cleared. Receiver bench: call drops 153 -> 148, all kinds 221 -> 208. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
278863068f
|
fix(scope): follow namespace factory return types and record untyped drops (#3648)
`ns.make()` now takes `make`'s return annotation from the namespace's module, in both the bound (`x = rp.make()`) and inline (`rp.make().m()`) forms, for every language whose provider records return annotations; Dart binds its prefixed-call locals so the same lookup applies. The lookup fails closed whenever it cannot prove the fact: the return must be anchored on a module-level Function of that name (TS/Dart hoist method returns, Go binds package-scope methods), the namespace must bind the module it targets (`import pkg.sub`), and a dotted return is not followed further. The namespace-name set is built from finalized import edges, never a `scopeTree.byId` walk, so it is safe under GITNEXUS_DISK_SCOPE_INDEX=1. Calls the analyzer cannot type are recorded as receiver-unresolved drops instead of exact misses: a receiver bound to an untyped namespace call, and an outer call that shares its start position with an inner one (site identity is now `receiver.member`). Corpus A/B against main: identical CALLS/ACCESSES edges outside the new fixtures. Receiver bench: call drops 128 -> 153, reads 44 -> 48. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
9df1e98350
|
fix(routes): scope inline Express handlers and report api_impact lower bound (#3643)
* fix(routes): scope inline Express handlers and report api_impact lower bound Inline Express handlers are anonymous Function nodes named by position, but the route facts only recorded a handler name for bare identifiers. Routes with inline handlers therefore had no handlerSymbolId: every route in a file got the union of its handlers' response keys and every flow starting there. Record the positional name for inline handlers, read response keys from the resolved handler when a file registers several routes, and let processes link exactly by handlerSymbolId as they already do for named handlers. Count, per route, the files that pass its URL to a callee not proven to be a fetch wrapper (excluding proven consumers and the route's own file). The count is stored as Route.unverifiedConsumers; api_impact reports epistemic: 'lower-bound' with that count when it is non-zero. Older indexes without the column fall back to the previous query. Closes #3488 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * refactor(routes): match unverified consumers once per URL Memoize route matching per distinct fetch URL, skip the proven-consumer scan when no unproven call matches a route, and build the Route query's optional columns from a list instead of two booleans. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(group): keep inline handlers attributed to their registrar Inline Express handlers now carry their positional Function name in the route facts so ingestion can resolve a handlerSymbolId. Group HTTP contracts read the same facts, but that name is a graph identity rather than a symbol name the contract lookup can match, so the provider lost its enclosing-registrar attribution. Ignore the name for inline handlers there. Rebaseline the two bench guards the branch moved: - emit-persistence: route.csv gains the header-only unverifiedConsumers field; stripping it restores the prior fingerprint exactly. - receiver-resolution: the new fixture's req.query.path read on an untyped inline-handler parameter adds one receiver-unresolved read (191 -> 192). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Gergo Magyar <gergomagyar0@gmail.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: Gergő Magyar <gergomagyar@icloud.com> |
||
|
|
01af833875
|
fix(scope): preserve lexical bindings in HTTP wrapper detection (#3632)
* docs(scope): clarify lexical binding ownership and wrapper provenance Checkpoint for #3630. Publishing the implemented source, regression tests, and benchmark harness as a draft PR; full-suite and final scaling validation remain in progress. * fix(scope): publish #3630 docs/plans * fix(scope): publish #3630 gitnexus/bench/lexical-binding-provenance * fix(scope): publish #3630 gitnexus/bench/lexical-binding-provenance/fixtures * fix(scope): publish #3630 gitnexus/bench/lexical-binding-provenance/results * fix(scope): publish #3630 gitnexus/src/core/ingestion * fix(scope): publish #3630 gitnexus/src/core/ingestion/languages/typescript * fix(scope): publish #3630 gitnexus/src/core/ingestion/languages * fix(scope): publish #3630 gitnexus/src/core/ingestion/pipeline-phases * fix(scope): publish #3630 gitnexus/src/core/ingestion/route-extractors * fix(scope): publish #3630 gitnexus/src/core/ingestion/workers * fix(scope): publish #3630 gitnexus/test/fixtures/lang-resolution/fetch-wrapper-target-identity/lib * fix(scope): publish #3630 gitnexus/test/fixtures/lang-resolution/fetch-wrapper-target-identity * fix(scope): publish #3630 gitnexus/test/fixtures/lang-resolution/fetch-wrapper-target-identity/packages/http * fix(scope): publish #3630 gitnexus/test/fixtures/lang-resolution/fetch-wrapper-target-identity/packages/http/src * fix(scope): publish #3630 gitnexus/test/fixtures/lang-resolution/fetch-wrapper-target-identity/server * fix(scope): publish #3630 gitnexus/test/fixtures/lang-resolution/fetch-wrapper-target-identity/web * fix(scope): publish #3630 gitnexus/test/integration/resolvers * fix(scope): publish #3630 gitnexus/test/unit * fix(scope): publish #3630 gitnexus/test/unit/group * fix(scope): publish #3630 gitnexus/test/unit/scope-resolution * fix(scope): publish #3630 gitnexus/test/unit/scope-resolution/typescript * fix(scope): publish #3630 gitnexus-shared/src/scope-resolution * fix(scope): treat wrapped assignment targets as writes `(app) = new Map()`, `(app as T) = x` and `app! = x` assign the same binding as `app = x`, but route-fact invalidation and the shared pattern walker stopped at the wrapper, so Express provenance survived the write and produced a false route. Recurse through parenthesized, as/satisfies and non-null targets. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(routes): resolve wrappers through overloads, declarator lists and barrels Three wrapper shapes lost their identity: - Bodyless overload signatures were indexed as separate definitions sharing the implementation's binding, so the binding looked ambiguous and both local and imported calls were rejected. Signatures now declare no callable. - Facts for `const base = 0, request = (url) => fetch(url)` followed the first declarator. The captured name now selects its own declarator. - An import resolved to a workspace barrel stopped at index.ts. Workers now record named, star and imported-then-exported re-exports, and resolution follows them to the declaring module: local and named exports shadow `export *`, star exports never carry `default`, and conflicting star exports block the name. No repository-wide name fallback is restored. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(scope): assert closure ownership against the declaration The closure-write assertion compared one lookup with itself, which also passes when both are undefined. Compare it with the outer declaration and the shadowing parameter, and keep the memoization check explicit. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * refactor(routes): share lexical helpers and release per-file HTTP contexts - Share binding keys, parameter patterns and transparent expression wrappers from lexical-bindings instead of four local copies; drop the unused moduleStem export; name the inferred-source predicates in lookup. - Key the per-file HTTP context by its tree rather than its ParsedFile. Workers retain ParsedFiles for the whole chunk, which kept every file's AST and traversal maps alive until the chunk flushed. - Index re-exports by name, memoize barrel walks per starting export, and keep facts without import targets as-is instead of copying them. - Move TS-only wrapper cases into the TS describe.each instead of branching inside the per-language loop. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(routes): keep wrapper identity for HOCs, CommonJS, object keys and fetch packages Four definition shapes that main linked by name lost their wrapper identity: - `const load = useCallback((url) => fetch(url))` and curried HOCs produced no facts because definition lookup stopped at the call; it now follows the callable argument, as the structural definition capture does. - `module.exports = { request }`, `exports.x = …` and `module.exports.x = …` were not exports, so destructured `require` callers missed the wrapper. CommonJS export assignments are recorded when `module`/`exports` are the unshadowed globals, including functions defined inline in the export object. - An object-literal key or method name was resolved as a lexical binding and collided with a same-named module wrapper. Only binding identifiers now name a definition's binding. - `import fetch from 'node-fetch'` no longer counted as fetch. Imports of known fetch implementations (node-fetch, cross-fetch, isomorphic-fetch, the fetch member of undici and whatwg-fetch) are fetch evidence again; parameters and other local names still are not. Reference identifiers are also resolved only when their name is bound to a callable or class region; other identifiers cannot add a reachability edge. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(routes): drive the forwarding fallback case from data and cover re-export merges Replace the boolean-switched forwarding fallback case with table rows instead of branching inside the test, and assert that barrel re-export facts survive worker sub-batch merges. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * chore(bench): reject stale or dirty call-graph exports The committed comparison named a candidate revision that is not on this branch, and nothing stopped it. Comparison now requires both exports to come from clean checkouts and the candidate export to come from the checkout running the comparison. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * chore(bench): regenerate the call-graph comparison on the reviewed head Clean baseline |
||
|
|
47d63a0601
|
chore(vendor): update tree-sitter-dart to 1.0.0-g0038280 (#3633)
* chore(vendor): update tree-sitter-dart to 1.0.0-g0038280 * chore(vendor): rebuild native prebuilds (tree-sitter-dart) Built by https://github.com/abhigyanpatwari/GitNexus/actions/runs/39212887661 * fix(dart): use native extension type parsing The upgraded grammar parses extension types directly. The compatibility rewrite loses generic extension bodies and implements clauses, so capture the native nodes and register their member ownership instead. Refresh the Dart capture fingerprint after comparing the old grammar and emitter over the same fixture corpus. Keep the scaling budget unchanged. Refs #3633 * chore(vendor): rebuild native prebuilds (tree-sitter-dart) Built by https://github.com/abhigyanpatwari/GitNexus/actions/runs/39239767139 * fix: repair Dart parity and checkpoint regressions Remove Dart from the preprocessing fixture inventory after retiring its hook. Use the parsed body boundary when reading extension-type heritage so named-record representations retain their implements clauses. Seed the WAL recovery fixture with the normal checkpoint threshold, then force one-byte checkpoints only for the deliberately blocked write. Require an actual error exit instead of accepting a subprocess timeout. Refs #3633 * chore(vendor): rebuild native prebuilds (tree-sitter-dart) Built by https://github.com/abhigyanpatwari/GitNexus/actions/runs/39248441688 --------- Co-authored-by: gitnexus-release-bot[bot] <gitnexus-release-bot[bot]@users.noreply.github.com> Co-authored-by: Gergo Magyar <gergomagyar0@gmail.com> |
||
|
|
565e50276d
|
fix(routes): resolve Express registrations made through helper parameters (#3629)
* fix(routes): resolve Express registrations made through helper parameters (#3487) #3603 made Express receivers provenance-proven, which removed the receiver- name false positives but also every registration on a function parameter: a parameter has no binding value, so `app.get(...)` inside a helper never resolved. On GitNexus's own server that dropped `* /api/mcp` (`mountMCPEndpoints(app, ...)`) and `GET /` (`registerWebUI(app, ...)`), and the two SSE progress routes registered by `mountSSEProgress(app, '/api/.../progress', jobs)` were never found at all (#3487, item a). Named function values in the route facts now carry an identity and the binding id of each parameter (a TypeScript `this` parameter takes no position, and a parameter written anywhere in the body, nested closures included, takes none either). Calls to plain functions and to namespace or default import members are recorded as invocations, keeping only the arguments that can carry an allocation or a literal path; arguments after a spread have no position. The resolver resolves a registration whose receiver, literal path or mounted child is a parameter through each call site of its function, once per site and in the helper's file. This is one hop only: a site argument that is itself a parameter does not resolve. When no call site is recorded, the call is evaluated as written, so an unknown mount path still suppresses its child router instead of exposing its paths at the root. Only calls whose callee is named like a function that registers on its parameters are evaluated, which keeps resolution at its previous cost on repositories with many helper calls. A function value's invalidation key now includes its identity, so a property write on one function (`Button.displayName = ...`) no longer disables every other. The group layer shares the resolver, so it gets the same routes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(routes): forward mounted-child parameters and aliased helper imports Review follow-ups for the helper-parameter forwarding: - A helper that forwards only its mounted router (`function mountChild(child) { app.use('/api', child) }`) was never resolved, because the forwarded function was chosen from the receiver and path alone. Any parameter among the receiver and arguments now selects it, as long as all come from one function. - A helper called through an aliased import (`{ mountSSEProgress as mountProgress }`) was filtered out before its call sites were recorded, since the filter compared only the local name. It now also accepts the imported name. - A write to a same-named local declared in a nested block (`{ let app; app = other; }`) discarded the outer parameter. A write to a binding that a nested block, function or catch clause declares no longer counts. - The fixture's `registerWebUI` uses its static directory. The release tool-accuracy gate now passes `routes.helper-registration`, so its known-gap allowance is removed. Its expected file moves from the caller (`api.ts`) to the helper (`progress.ts`): an Express route belongs to the file of its `app.get` call, which is also where its handler and response keys are, as for every directly registered route. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(routes): treat destructuring reassignment as a parameter write `[app] = [other]` reassigns `app`, but the write check compared the whole target text (`[app]`) with the parameter name, so the parameter stayed forwardable and later registrations used the call-site argument. A destructuring target now writes each name it binds; a member target still writes only a property. Also drops an unused local flagged by CodeQL. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(routes): follow helpers exported under another name `export { mountRoutes as setup }` records the export as a binding, so the call-site filter knew the helper only as `mountRoutes` and skipped callers importing `setup`. An export of a binding to a named function now adds its exported name too. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Gergo Magyar <gergomagyar0@gmail.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: Gergő Magyar <gergomagyar@icloud.com> |
||
|
|
aa33a6e94c
|
fix(routes): link base-URL fetch calls made through wrapper chains (#3628)
* fix(routes): link base-URL fetch calls made through wrapper chains (#3488) GitNexus's own web client calls `fetchWithTimeout(`${_backendUrl}/api/repos`)`, which reaches fetch through `resilientFetch` and a local `fetchImpl` alias. `api_impact` found none of those consumers, for three independent reasons: - normalizeFetchURL rejected any URL that did not start with `/`, so a leading base-URL binding dropped the call. It now strips a leading `${...}` run followed by `/`, with the same rules as the group layer's consumer normalizer (#3111); the helper moves to route-path.ts so both layers share it. A path whose every segment is a param (`${base}/${id}`, `${base}/${id}.json`) is rejected, since it would match every route of that depth. Interpolations are collapsed brace-balanced before the query split, so `${repo ? `?repo=${repo}` : ''}` no longer ends the path, and one glued to the last static segment is treated as a query or suffix. - The wrapper call collector required a `/`-leading literal, so the same template never became a wrapper candidate. - Wrapper detection only looked five AST levels deep for a call to an identifier named `fetch`. Arrow-const wrappers sit deeper than that, and `resilientFetch` calls an alias of `globalThis.fetch`. Detection now walks the whole definition and accepts `globalThis`/`window`/`self.fetch` and aliases whose value names fetch (through `??`, `||`, `?:`), read from the AST rather than the text. It records whether the first parameter reaches fetch's URL argument and which callees receive that parameter. The routes phase closes the wrapper set over that forwarding: `fetchWithTimeout` is a wrapper because it forwards its URL to `resilientFetch`. A callee declared in the caller's file or imported from a relative module must carry a URL in exactly that module; a package-imported name is followed only when every recorded function of that name carries one. A fetch caller whose first parameter is not the URL (`track(event)`) or a same-named function elsewhere (an IPC `request(channel)`) lends nothing. When `[param]` matches several sibling routes (`/api/analyze/upload` and `/api/analyze/:jobId`), the fetch now links to the route whose segments match by kind instead of the first one registered. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(routes): match wrapper calls by declaring module and decline ambiguous fetch matches Review follow-ups for #3488: - A propagated wrapper's name went into a repo-wide set, so a literal call to an unrelated same-named function in another module was taken as a fetch. Wrapper call candidates now carry the module declaring a bare callee when the file declares or imports it relatively, and must reach a wrapper defined in exactly that module. Package and member callees keep the name match. - An alias declared in a nested helper (`const fetchImpl = globalThis.fetch`) marked an outer call to a same-named parameter as a fetch. An alias now counts only where its declaring function encloses the call. - A first parameter redeclared inside the function (`{ const url = ...; fetch(url) }`) was taken as the URL. A shadowed parameter is no longer followed. - A fetch whose `[param]` ties between sibling static routes (`/api/kinds/foo`, `/api/kinds/bar`) linked to the first registered one. A tie now links none. - The fixture's `resilientFetch` returns the last response instead of rejecting with `undefined`, and its client encodes the repo query value. The release tool-accuracy gate now passes `api.template-wrapper`, so its known-gap allowance is removed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(routes): keep call-site wrapper names and ignore helpers that never run - A wrapper call through a renamed relative import (`import { request as send }`) stored the exported name, so a configured `fetchWrappers` entry naming the call-site `send` no longer matched. The call keeps its call-site name and carries the declared name separately for the module lookup. - A nested helper bound to a local that is never referenced again (`const later = () => resilientFetch(url)`) made its enclosing function forward its URL. Calls inside such a helper are ignored; a helper that is called or passed on, such as a retry callback, still counts. - The fixture's `resilientFetch` no longer retries after a timeout or abort. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(routes): let a nested parameter shadow a fetch alias A function parameter between a fetch alias and a call shadows the alias there (`function send(fetchImpl) { return fetchImpl(event) }` beside an outer `const fetchImpl = globalThis.fetch`), so that call no longer marks the enclosing function as a fetch wrapper. The fixture's retry helper also stops returning a stale 5xx response after a later attempt throws. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(routes): drop fetch aliases whose name is redeclared with another value A block that redeclares an alias name with a non-fetch value (`{ const fetchImpl = other; fetchImpl('/x') }`) let the inner call read as a fetch. A name declared anywhere in the function with a value that does not name fetch is no longer an alias, which covers every block-scoped shadow without lexical scope tracking. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(routes): ignore uncalled nested declarations and shadowed global objects - An uncalled nested `function helper() { fetch('/x') }` made its enclosing function a fetch wrapper. Nested function declarations that are never referenced are now ignored, as unused function-valued locals already were. - `window.fetch(url)` through a parameter or local named `window` (or `self`, `globalThis`) was read as the platform fetch. Such a root is now that value's method. A bare `fetch` parameter is still taken as fetch, since injecting a fetch implementation is the common reason for it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(bench): refresh the receiver-resolution baseline for the fetch fixture The new `fetch-base-url-wrapper` fixture in `test/fixtures/lang-resolution` adds one unresolved TypeScript chain-field call to the committed check corpus (127 to 128 call drops). Regenerated with `measure.mjs --update-baseline` after a fresh build and cleared parse caches; no other counter moved. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Gergo Magyar <gergomagyar0@gmail.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: Gergő Magyar <gergomagyar@icloud.com> |
||
|
|
702c917377
|
fix(python): retain nullable factory receiver types (#3595)
* fix(python): retain nullable factory receiver types * test(accuracy): remove repaired Python gap allowances * test(bench): refresh Python capture fingerprint * fix(python): type `None or call()` assignments like `call() or None` `None or make_user()` always evaluates to the call, but only the `call() or None` order matched, so the variable lost the factory's return type. Add the mirrored boolean pattern, matching the two conditional-expression orders the PR already supports. Fixture gains `none_or` (resolves to User.save) and `none_and` (stays unresolved: `None and x` is always None). The capture golden changes only for the edited fixture, and the python-scope baseline is re-recorded (`measure.mjs --check` passes, scaling 1.021). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Gergő Magyar <gergomagyar@icloud.com> Co-authored-by: Gergo Magyar <gergomagyar0@gmail.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
22a7608b7c
|
feat(cache): derive parser and PDG cache identities automatically (#3606) | ||
|
|
f8209df2e2
|
fix(routes): resolve Express receivers and router mount paths (#3603)
* fix(routes): resolve Express receiver provenance and mounted paths Require statically proven framework receivers, compose Router mount paths, and share the same provider resolution between ingestion and group routes. Preserve worker and parse-cache parity with per-file facts. * fix(review): apply review findings Invalidate exported Routers through namespace property mutations and retain uncertain incoming mounts so invalid parents cannot revive local paths. * refactor(routes): reuse export checks and fixture writer * fix(routes): evaluate fluent call receivers once * fix(review): preserve provider paths and uncertain mounts * fix(test): distinguish parameterized router mutation cases * docs: remove Express route implementation plan |
||
|
|
94f167a90f
|
fix(routes): reject middleware and route metadata false positives (#3561)
* fix(routes): reject middleware and route metadata false positives * fix(routes): invalidate cached phantom routes on upgrade * test(routes): cover cache upgrades across merged schema versions Verify that schemas 137 through 140 invalidate both parse stores after merging main into PR #3561 and advancing the schema to 141. --------- Co-authored-by: Gergő Magyar <gergomagyar@icloud.com> |
||
|
|
b51f25606c
|
fix(routes): match configured fetch wrapper calls that pass type arguments (#3567)
Some checks failed
Devcontainer Smoke / Build devcontainer image (push) Has been cancelled
Devcontainer Smoke / Config-transform unit tests (push) Has been cancelled
Skill copy sync / shipped skills drift guard (push) Has been cancelled
CodeQL / Analyze (javascript-typescript) (push) Waiting to run
Gitleaks / gitleaks (push) Waiting to run
Trivy Image Scan / Trivy (gitnexus-cli) (push) Waiting to run
CodeQL / Analyze (python) (push) Waiting to run
Publish / Classify release event (push) Waiting to run
Publish / RC guard (marker + release-PR skip) (push) Blocked by required conditions
Publish / ci (push) Blocked by required conditions
Publish / Publish to npm (push) Blocked by required conditions
Publish / Build & Push RC Docker images (push) Blocked by required conditions
Scorecard / Scorecard analysis (push) Waiting to run
Trivy Image Scan / Trivy (gitnexus-web) (push) Waiting to run
|
||
|
|
8fa8e64388
|
fix(rename): extend safe refactoring and flow coverage across languages (#3557) | ||
|
|
191a954bf6
|
fix(python): resolve calls through module-returning accessors (#3542)
Some checks failed
Trivy Image Scan / Trivy (gitnexus-cli) (push) Has been cancelled
Trivy Image Scan / Trivy (gitnexus-web) (push) Has been cancelled
CodeQL / Analyze (javascript-typescript) (push) Has been cancelled
CodeQL / Analyze (python) (push) Has been cancelled
Gitleaks / gitleaks (push) Has been cancelled
Publish / Classify release event (push) Has been cancelled
Scorecard / Scorecard analysis (push) Has been cancelled
Publish / RC guard (marker + release-PR skip) (push) Has been cancelled
Publish / ci (push) Has been cancelled
Publish / Publish to npm (push) Has been cancelled
Publish / Build & Push RC Docker images (push) Has been cancelled
* test(accuracy): retire repaired Python accessor gaps * fix(python): connect module accessor resolution * fix(python): resolve calls through module-returning accessors Bind `_pc().target()` and `m = _pc(); m.target()` to the module's function when a local accessor returns an imported module, and count capped-out dropped names in the impact lower bound. Closes #3497 Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * refactor(python): share module-accessor position key (#3542) - Reuse the capture-side positionKey in the receiver namespace resolver instead of a second line:col helper plus an inline template. - Note why the synthetic { moduleScope, scopes: [] } input is sufficient for createNamespaceTargetCache (sealed disk-backed ParsedFile shape). Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * refactor(python): reuse subtype-dispatch positionKey (#3542) - Export the existing line:col positionKey from subtype-dispatch instead of defining an identical copy in module-accessors, so both Python capture side channels key positions through one helper. - Apply prettier to the Function-scope indexing line. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * fix(ci): unique test titles and rebaseline Python benches (#3542) - impact-epistemic-lower-bound: `%j` rendered null, NaN and Infinity all as `null`, so the test-completeness audit rejected the duplicate identities. Use `%s` so every parameterized case has a distinct title. - python-scope: capture fingerprint changes with the new module-accessor capture facts (golden captures were already updated). - receiver-resolution: +4 Python call drops (125 -> 129). The two-step `x = f(); x.m()` form is now counted as an unresolved call instead of vanishing: fastapi-depends `router.get/post` (unknown, x3) and python-method-chain-binding `user.get_city()` (in-program; `User` has no `get_city`, so the site is genuinely unresolvable). Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Gergo Magyar <gergomagyar0@gmail.com> Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com> |
||
|
|
999cc9f913
|
fix(deps): upgrade native tree-sitter to 0.25.1 (#3531)
* fix(deps): upgrade native tree-sitter to 0.25.1 * fix(tree-sitter): preserve sync loading and update verified capture baselines Route C# scope queries through the synchronous native grammar loader so the compiled analyze module remains require-compatible on Node 22.18. Refresh only the audited empty-Python-module and renamed-PHP-fixture captures, and pin the runtime assertion to 0.25.1. Validated 479 targeted tests, all 16 scope-capture benchmark cases, Python capture/import guards, compiled C# worker parsing, build, typecheck and formatting. Performance budgets are unchanged. * fix(tree-sitter): refresh upstream vendors and preserve parser semantics * fix(ci): refresh prebuilds and support Windows ARM Refresh macOS, Linux ARM, and Windows x64 grammar binaries from the successful native CI builds. Pin node-gyp 12.4.0 so the Windows ARM jobs recognize Visual Studio 2026. Use local Git filtering for E2E to avoid PR-files API timeouts on generated parser diffs. Validation: 143 focused tests, Actionlint, and all 16 scope-capture benchmark checks passed. Full npm test run started; native database suites still running. * fix(ci): refresh Windows ARM grammar prebuilds Import all seven Windows ARM grammar artifacts from successful native build run 37965761760. Artifact filenames and SHA-256 hashes verified; all 42 native build jobs passed. Validation: 31 prebuild coverage tests passed. * fix(bench): stabilize scope capture timing guard Warm each input size seven times and use 21 median samples while retaining isolated timing blocks. Yield between languages so native finalizers release allocations outside the measured sections. Keep all fixture sizes, fingerprints, scaling formulas, and budgets unchanged. Validation: all 16 language guards passed with a 2 GB heap limit (1.38 deep-chain ratio; 1518 MB peak RSS). Linear and quadratic controls passed their expected guard outcomes. Formatting and graph change analysis passed. Full npm test is running without failures so far. * fix(parser): reduce TypeScript capture traversal cost * fix(test): await parser worker readiness before teardown * fix(parser): load C# synchronously in workers * fix(grpc): resolve native proto detections against their own file With the native proto grammar active, sibling .proto files that declare the same short service name under different packages tied in the directory-based resolveProtoConflict heuristic and both providers were dropped. A proto-origin detection already knows its defining file, so use that file's entry before falling back to the heuristic. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * ci(packaging): assert native grammars load from the installed tarball The packaged-install smoke only ran `gitnexus --version`, so a grammar that failed to load from the published package (bundled runtime, patched peer manifests, vendored prebuilds, C# sync loader, Protobuf wrapper) would ship silently; the dev-tree ABI smoke also accepts vendored grammars that report unavailable. Parse a sample for every registered grammar plus the native Protobuf plugin from the installed package, on the main thread and in a worker thread. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * ci(tree-sitter): track compatibility workarounds instead of 0.25 readiness The daily readiness report still targeted tree-sitter@0.25.0, so after the upgrade it would keep reporting "Blocked" and never close its tracking issue. Repurpose it to track what we carry for the bundled runtime: derive the target from the package.json pin, list each pack-time peer patch from prepare-tree-sitter-bundle.cjs with its drop condition (bump, wait for an upstream release, transitive, or intentionally pinned), and reword the buckets. The workflow adopts the issue opened under the old title. Also update the grammar-update-monitor header and auto-PR text, which still described the 0.21.1 runtime and a tree-sitter-c hold. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * test(dart): assert both nested labels target the loop; fix Zig call-shape doc The nested-label CFG test only jumped to `outer` and checked reachability, which still passes when a jump is retargeted to the inner loop (its exit reaches the outer header). Jump to both labels and assert the exact `continue` edges; dropping the inner label now fails the test. Update the Zig callable-flow comment: call_expression now wraps actuals in a required `arguments:` field. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Codex <codex@openai.com> Co-authored-by: Gergo Magyar <gergomagyar0@gmail.com> Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com> |
||
|
|
82347f0f07
|
fix(scope): resolve local imports by lexical ownership (#3532) | ||
|
|
50aa4be3b2
|
fix(routes): prefer production handlers and preserve app.all (#3505)
Some checks failed
Trivy Image Scan / Trivy (gitnexus-cli) (push) Has been cancelled
Trivy Image Scan / Trivy (gitnexus-web) (push) Has been cancelled
Gitleaks / gitleaks (push) Has been cancelled
Publish / Classify release event (push) Has been cancelled
Scorecard / Scorecard analysis (push) Has been cancelled
CodeQL / Analyze (javascript-typescript) (push) Has been cancelled
CodeQL / Analyze (python) (push) Has been cancelled
Publish / Build & Push RC Docker images (push) Has been cancelled
Publish / RC guard (marker + release-PR skip) (push) Has been cancelled
Publish / ci (push) Has been cancelled
Publish / Publish to npm (push) Has been cancelled
|
||
|
|
ff922c0a3b
|
fix(python): resolve calls through aliased package re-exports (#3504) | ||
|
|
4ccba12004
|
fix(scope): keep nested declarations out of module bindings (#3502) | ||
|
|
672fc72f56
|
fix(dart): capture package metadata portably during repository scan (#3465)
Some checks failed
CodeQL / Analyze (javascript-typescript) (push) Waiting to run
CodeQL / Analyze (python) (push) Waiting to run
Gitleaks / gitleaks (push) Waiting to run
Publish / Classify release event (push) Waiting to run
Publish / RC guard (marker + release-PR skip) (push) Blocked by required conditions
Publish / ci (push) Blocked by required conditions
Publish / Publish to npm (push) Blocked by required conditions
Publish / Build & Push RC Docker images (push) Blocked by required conditions
Scorecard / Scorecard analysis (push) Waiting to run
Trivy Image Scan / Trivy (gitnexus-cli) (push) Waiting to run
Trivy Image Scan / Trivy (gitnexus-web) (push) Waiting to run
Skill copy sync / shipped skills drift guard (push) Has been cancelled
|
||
|
|
c474a811ba
|
fix(test): align fixtures and mocks with current types (#3471) | ||
|
|
f99dde8aa3
|
fix(mcp): discover positional SDK tool registrations (#3450)
Some checks are pending
CodeQL / Analyze (javascript-typescript) (push) Waiting to run
CodeQL / Analyze (python) (push) Waiting to run
Gitleaks / gitleaks (push) Waiting to run
Publish / Classify release event (push) Waiting to run
Publish / RC guard (marker + release-PR skip) (push) Blocked by required conditions
Publish / ci (push) Blocked by required conditions
Publish / Publish to npm (push) Blocked by required conditions
Publish / Build & Push RC Docker images (push) Blocked by required conditions
Scorecard / Scorecard analysis (push) Waiting to run
Trivy Image Scan / Trivy (gitnexus-cli) (push) Waiting to run
Trivy Image Scan / Trivy (gitnexus-web) (push) Waiting to run
|
||
|
|
a8f18f00b9
|
fix(swift): avoid false calls for injected closure properties (#3434) | ||
|
|
e42122a0f6
|
feat(go): index gin/echo routes and report them in impact (#3402) (#3417) | ||
|
|
821ffb2fcb
|
fix(python): resolve decorator identity like CPython (#3411)
* fix(python): resolve decorator identity like CPython Decorator identity was decided by two different predicates. One read the raw decorator text, so a trailing comment such as `@staticmethod # type: ignore` hid the builtin and turned an explicit first parameter into a fabricated receiver. The other trusted any `staticmethod` spelling, including a module-level rebinding and a `staticmethod(classmethod(f))` stack, which CPython cannot call. Read the decorator expression node only, and recognize a bare builtin descriptor only when the file does not rebind that name. Publish subtype capacity only for a plain function or a single builtin staticmethod or classmethod wrapper. Drop a no-op coverage guard, move the implicit classmethod comment next to the code it describes, and bump the parse cache to v121. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * fix(python): shadow builtin descriptors only by visible bindings The whole-file identifier scan counted plain reads (`staticmethod(f)`), `from builtins import staticmethod`, and bindings that run after the decorator as rebindings. CPython evaluates a class-body decorator with LOAD_NAME when the `def` runs, so none of those change which object the decorator names. Methods decorated with the real builtin lost their subtype call shape, and static methods lost their first parameter in arity metadata. Move decorator identity into builtin-descriptors.ts and count only binding occurrences (assignment and loop targets, walrus, def/class, parameters, import aliases, except/with/match captures, del, type parameters, and wildcard imports) that are visible where the decorator runs: the class body or module before the definition, a repeating enclosing loop, any binding in an enclosing function, and any global/nonlocal rebind. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * fix(python): model global and del like CPython's symbol table `global x` and `nonlocal x` bind nothing; they redirect the declaring function's own bindings of `x` to an outer scope. A bare declaration was treated as an unconditional rebinding, so `@staticmethod` anywhere in the file lost builtin recognition. A module- or class-level `del` restores the outer lookup rather than binding the name. Treat an unconditional `del` that runs after a binding and before the decorator as undoing that binding. A `del` inside control flow may not run, and a `del` inside a function still makes the name local there. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * fix(python): order global rebinds and honor builtins re-exports A function that declares `global staticmethod` and assigns it rebinds the module name only when called, and it cannot be called before the top-level statement that defines it runs. Treat such a rebind as visible only when that statement precedes the decorator, or when the decorator sits in a deferred class body. `nonlocal` rebinds stay visible anywhere in the enclosing function. `from builtins import staticmethod as staticmethod` binds the builtin to its own name, so it no longer counts as shadowing. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * test(python): scope the descriptor-identity CPython claim The wildcard-import case expects the fail-closed resolver verdict, not a CPython outcome, because the imported module's exports are unknown. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * fix(python): resolve descriptor names as LOAD_NAME does Model each binding by its effect on the namespace (Language Reference 4.2.1): an import that binds the `builtins` object itself (plain, aliased to the same name, or `from builtins import *`) restores the builtin, a module- or class-level `del` unbinds so lookup falls through, and any other binding shadows. Resolve the decorator like LOAD_NAME (4.2.2): the class namespace, then module globals, then builtins, each as it stands when the `def` runs. A restoring effect counts only as an unconditional simple statement that runs before the decorator, so an import or `del` under `if`/`try` or a loop stays fail-closed. A helper's `global` delete depends on whether the helper is called, which the resolver does not model, so it keeps the override. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * refactor(python): simplify decorator descriptor lookup Derive the descriptor type and name set from one `as const` list, replace indexed non-null assertions with destructuring, build the scope chain without an assertion, and skip the enclosing-function owner lookup when the decorator has no enclosing function. Key the stacked-decorator verdicts by case name so a failure names its case. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * fix(python): resolve enclosing-function and deferred descriptor lookups A class body reads a free name from the innermost enclosing function that binds it (Language Reference 4.2.2). Evaluate that function's namespace the same way as the class and module ones, so an unconditional `from builtins import staticmethod` there resolves to the builtin. Any other binding still shadows, including a local assigned only after the class, which raises NameError rather than falling back to the builtin. A class body inside a function runs whenever that function is called, which can be any time after its top-level statement starts. Read module state at that statement instead of after the whole module, so an earlier `del` restores the builtin, and treat any later module override as possibly visible. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Gergo Magyar <gergomagyar0@gmail.com> Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com> |
||
|
|
0bcddec8e6
|
fix(python): keep uncertain decorated receivers unresolved (#3405)
Some checks are pending
CodeQL / Analyze (javascript-typescript) (push) Waiting to run
CodeQL / Analyze (python) (push) Waiting to run
Gitleaks / gitleaks (push) Waiting to run
Publish / Classify release event (push) Waiting to run
Publish / RC guard (marker + release-PR skip) (push) Blocked by required conditions
Publish / ci (push) Blocked by required conditions
Publish / Publish to npm (push) Blocked by required conditions
Publish / Build & Push RC Docker images (push) Blocked by required conditions
Scorecard / Scorecard analysis (push) Waiting to run
Trivy Image Scan / Trivy (gitnexus-cli) (push) Waiting to run
Trivy Image Scan / Trivy (gitnexus-web) (push) Waiting to run
* fix(python): suppress uncertain decorated receivers * fix(ci): keep uncertain Python receivers out of method arity Unrecognized decorators now leave the receiver kind unproven, but the first parameter is still the implicit receiver slot for ordinary bound calls. Method extraction stopped stripping it, so decorated methods reported one extra parameter and shifted capture arity metadata. Share the uncertain-receiver classification between type-binding synthesis and parameter extraction, then refresh the Python capture golden and benchmark fingerprint for the intended capture change. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Gergo Magyar <gergomagyar0@gmail.com> Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com> |
||
|
|
0ef3f28d0e
|
fix(python): avoid receiverless subtype targets
Fixes #3396 |
||
|
|
b20c8b6ef2
|
fix(python): guard decorated subtype targets
Fixes #3398 |
||
|
|
3d09c85ee3
|
fix(python): record partial subtype dispatch coverage
Fixes #3395 |
||
|
|
c744ce1dfd
|
fix(python): resolve mixin calls with CPython C3 order (#3393)
* fix(python): resolve mixin calls with CPython C3 order Breadth-first MRO bound a diamond mixin call to the wrong base, and dropping the site left the real method out of the graph. Use C3 and take the first compatible method in that order. Co-authored-by: Cursor <cursoragent@cursor.com> * chore(autofix): apply prettier + eslint fixes via /autofix command * fix(python): correct mixin receiver baseline counts * fix(python): guard incomplete mixin inheritance * fix(python): record unresolved MRO tail coverage (#3393) Track a missing subtype target when the last indexed MRO owner has an unindexed parent, and cover the case with an integration test. Correct the C3 fixture description. Note: local full npm test timed out amid parse-worker startup failures; focused tests and benchmark baseline passed. --------- Co-authored-by: Gergo Magyar <gergomagyar0@gmail.com> Co-authored-by: Cursor <cursoragent@cursor.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> |
||
|
|
f6e70016d6
|
fix(python): resolve mixin self calls to subtype implementations (#3390)
* fix(python): resolve missing mixin self members through subtypes * fix: honor Python effective MRO and static mixin targets * fix: bind Python subtype dispatch to receiver provenance * fix(python): limit mixin fanout to instance receivers * fix(python): capture call arity and invalidate stale parsed facts * fix(python): count bound receivers by method context Preserve static, free, nested and typed variadic parameters; test renamed target receivers without weakening incompatible-arity rejection. Regenerate capture goldens for receiver metadata and eight mixin fixtures. Record the deliberate missing_target coverage outcome: CI measured Python call drops 5->6 and total call drops 113->114; no shape or scaling threshold relaxed. * test(python): require a call capture before checking unknown arity * fix(python): bind subtype dispatch to receiver definition * test(python): include conditional renamed-receiver target * fix(python): prove positional mixin targets and report partial coverage Preserve upstream notebook coordinate mapping and maintainer changes. Reject incompatible/implicit-class targets, retain proven targets across ambiguous alternatives, and report capped or unresolved coverage without confusing edge deduplication. * fix(python): isolate subtype call proof and preserve lookup boundaries --------- Co-authored-by: Eva <eva@100yen.org> Co-authored-by: Gergő Magyar <gergomagyar@icloud.com> |
||
|
|
274ec3df6e
|
feat: index Jupyter notebooks as Python (#3381) | ||
|
|
51fb64c976
|
fix(swift): model Swift modules like the compiler (nested packages, Xcode targets, linear visibility) (#3387)
* fix(swift): discover nested Package.swift manifests for module grouping A Swift monorepo laid out as Core/<pkg>/Package.swift has no root manifest and no root Sources/, so every Swift file fell into one __default__ module. The Swift resolver now walks the repo (bounded, skipping dot, ignored, and Xcode bundle directories) and adds each nested package's targets, keyed by repo-relative directory and ordered deepest-first so first-match grouping picks the most specific target. Import resolution keeps the root view. Refs #3355 Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * fix(swift): bound implicit IMPORTS edges by a total budget Implicit same-module IMPORTS are n*(n-1) edges per module, and the graph keeps every relationship in one Map capped by V8 at 2^24 entries. Emission now fills a 4M-edge budget smallest module first and skips, with a warning, any module that does not fit, so no module layout can crash analyze. Refs #3355 Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * fix(swift): skip pairwise sibling passes for oversized modules The target-siblings and sibling-type-bindings passes copy every file's declarations into every other file of a module, so heap grows as n^2: about 3.8 GB at 1,000 files with 15 defs each, about 15 GB at 2,000. Modules over 1,000 files now skip both passes with a warning and resolve through the global name fallback. GITNEXUS_SWIFT_MAX_MODULE_FILES changes the ceiling. Refs #3355 Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * test(swift): cover nested SwiftPM packages end to end A fixture with three nested Package.swift manifests (two declaring a target named Net) and no root manifest. Each target gets its own implicit IMPORTS, none cross packages, and Config() resolves to the caller's own package. The Swift capture golden and scope-capture fingerprint grow with the new fixture corpus only. Refs #3355 Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * fix(review): apply review findings - Rebase nested target paths with the existing Zig path helpers, which also reject Windows drive paths and paths that resolve to the repo root (whose empty prefix would group every file into one target). - Document GITNEXUS_SWIFT_MAX_MODULE_FILES in the README env table. - State that skipped modules resolve through lower-confidence fallback edges, why nested-type fragments still run for oversized modules, and fix a stale loader name in the target-grouping header. Refs #3355 Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * test(swift): cover every skipped Xcode bundle suffix in the package walk Refs #3355 Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * fix(swift): model modules the way the compiler does Replace the caps from the first round with representations that stay linear, and derive module identity from the same sources the compiler uses. - Same-module visibility is one File -> Module IMPORTS edge per file (reason `module-membership`) instead of an edge per ordered file pair. Incremental importer expansion treats files sharing a Module hub as importers of each other. The 4M edge budget is gone. - Sibling declarations and type bindings live in one shared table per module in the namespace channel C# uses since #1871, instead of being copied into every file. The 1,000-file ceiling and GITNEXUS_SWIFT_MAX_MODULE_FILES are gone. - Modules come from root and nested SwiftPM manifests (Sources, Source, src, srcs; plugins under Plugins; the newest Package@swift-X.Y.swift) and from Xcode native targets in project.pbxproj, including Xcode 16 synchronized folders. Target paths are matched from the repo root, so a vendored copy of the same layout is no longer grouped into a root target (this reverses #2931's floating match). - `import X` resolves to the modules named X instead of any folder named X. A name no module carries is external when every manifest and project was read completely. - The global-name-fallback veto uses the same membership, so test targets, custom-path targets and Xcode targets have module identity. Refs #3355 Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * fix(bench): move the Swift package bench to module grouping The bench still imported the removed groupSwiftFilesBySpmTarget. Its first-wins probe keeps its meaning under root-anchored grouping: the clash file sits under Sources/Mod0, and the Sources/Mod1 further down its path is a vendored copy. Plugins are now non-importable modules under Plugins/, so parse_targets counts importable source targets (still 3) and parse_binary_skipped also checks that the plugin is recorded that way. Baseline values unchanged; the notes say why the definitions moved. Refs #3355 Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * fix(swift): match compiler module names, manifests, and target filters - Module names follow the compiler's c99 mangling (`my-lib` imports as `my_lib`); Xcode targets use a literal PRODUCT_MODULE_NAME or PRODUCT_NAME when the project sets one. - Package manifests are one-file modules, as SwiftPM compiles them. Files outside every target are one-file modules once every manifest and project was read; otherwise they keep the shared __default__ module. - Xcode 16 synchronized-folder exceptions add a file to a target that does not list the folder, and remove it from one that does. - SwiftPM `sources:` / `exclude:` narrow a target; a computed list marks the manifest unreadable. - The default target folder is chosen once per package, as SwiftPM does, instead of per target. Refs #3355 Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * Address PR review feedback (#3387) - Inferred Swift folders keep SwiftPM's first predefined parent when a target name repeats (Sources before srcs). - The pbxproj parser rejects a \U escape without four hex digits instead of decoding garbage, so the project reads as incomplete. - Extension owners are stamped in every Xcode membership of a shared file. - A plugin name never reaches a plugin: neither the fallback veto nor the folder-index fallback treats a known non-importable module as imported. - A file an Xcode target compiles keeps that membership even when it also lies under a SwiftPM target directory. - The workspace scan bounds the queue, not only the directories read. - Integration tests require each module hub to exist before comparing. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Gergo Magyar <gergomagyar0@gmail.com> Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com> |