mirror of
https://github.com/colbymchenry/codegraph.git
synced 2026-11-11 09:55:39 +08:00
e6527a8d34a002d347e7027f69f3d4db4ce799c9
244
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
e6527a8d34 |
fix(php): an untyped receiver keeps a unique method only when named for it (#2152)
PHP joins the untyped-receiver languages for the unique-name method guess: `$x->m()` takes the one project method named `m` only when the receiver shares a word with its owner, or names a class (the whole name, or its last camel word: `$newRole` → Role) whose ancestry — extends + trait `use`, namespace-aware — has the method. A/B (edges removed / added): guzzle -359, BookStack -302, console -124, Slim -124, monica -53. Removals are PSR-7 / framework / exception calls bound to a test double or unrelated helper; the deliberate loss is calls through receivers named after neither (`$loaner->loansAsLoaner()`). Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
6eb08d68cf |
fix(csharp): a bare name reaches only members in scope (#2151)
A bare C# name (a `calls` or `references` ref with no dot, where the line shows no receiver) can mean a member only of the types around it (outer classes included), their base types (read from `class X : Base, IFoo` heads, every partial declaration), or a static using: the file's `using static`, any `global using static`, or `<Using Include="…" Static="true"/>` in the .csproj / Directory.Build.props files above it. Names outside any recovered type are not judged. A/B (edges removed / added): Newtonsoft.Json -1394/+910 (enum-valued properties now reach the enum type), AutoMapper -135/+58, eShop -117/+5, serilog -20/+17, CleanArchitecture 0. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
b238e240e2 |
fix(objc): a message reached by name alone must be in reach (#2150)
* fix(objc): a message reached by name alone must be in reach - `[self m]` / `[super m]` / `[[self class] m]` (receiver dropped by the extractor) reach only methods of the sender's hierarchy, read from `@interface Name : Super` plus a small UIKit/AppKit/MapKit superclass table for categories on system classes. When tree-sitter-objc loses an @implementation and its methods become functions, a same-file function, one in the file named after a hierarchy class, or any when the sender's class itself was lost, still counts; `[super init]` never lands on another file's function. - C call syntax (`completionBlock()`) never means a method or property. - Local receivers are typed from ObjC declarations (`FMResultSet *rs`, `(nullable FMResultSet *)rs`, block params), and objc joins the untyped- receiver languages for the unique-name guess (`weakSelf` / `strongSelf` count as self). Shared receiver words must be 3+ letters so class prefixes (`SD`, `NS`) don't match; Ruby/CFML unchanged but one wrong lobsters edge. A/B (edges removed / added): SDWebImage -321/+47, fmdb -28/+85, CocoaLumberjack -19/+10, AFNetworking -13/+1, MBProgressHUD 0. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(objc): a class or typed-property receiver keeps its inherited method `[AllTypesObject objectsInRealm:…]` (a class method inherited from RLMObject) and `[managed.anyDataObj containsObject:]` (a property declared `RLMSet<…> *anyDataObj`) now keep the unique-name match when the receiver's class — or one of the property's declared types — has the method's owner in its hierarchy. realm-swift goes from -2562/+588 to -504/+529 vs main. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
889b6a3526 |
fix(vbnet): a member access resolves through what the line names (#2149)
VB.NET's extractor keeps a call's last name only, so `Me.Panel.Controls.Add(x)` and `New System.Drawing.Size(1, 2)` arrived as bare `Add` / `Size` and exact / fuzzy matching took any class's member (even an enum case). The receiver is now read from the line: a member candidate needs `Me` / `MyBase` / `MyClass`, its own type or module as the last receiver segment, or a `(Of T)` type argument (`GetService(Of Notifier).Notify()`). Genuinely bare names are unchanged. A/B (edges removed / added): SCrawler -3452/+6, remoteapptool -143/+0, CompactGUI -31/+5, ViVeTool-GUI -1, TaskbarX 0. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
bd9741432f |
fix(cfml): a component method reached by name alone must be in reach (#2148)
- A bare call in a .cfc reaches only the component's own methods and those of the components it extends (extends="a.b.C" read from source, matched to indexed .cfc files by longest path suffix). Calls in .cfm templates and multi-line chain links (name not on the ref's line) are not judged. - The untyped-receiver rule from the Ruby fix now covers CFML too: the one project method named `m` needs a receiver named after its owner. The word match also accepts the receiver's whole name (`cbsecurity` → CBSecurity) and ignores trailing digits (`executor1` → Executor); Ruby is byte-identical to main. A/B (edges removed / added): coldbox -2429/+295, TestBox -255/+31, fw1 -103/+4, cbsecurity -8/+5, cfdocs 0. Deliberate losses: framework-repo calls through conventionally named, untyped receivers (ColdBox's `event.*` → RequestContext, TestBox's `variables.assert.*` → Assertion). Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
df761e4596 |
fix(ruby): a method reached by name alone must be in reach (#2147)
Ruby types nothing, so two guesses produced most of a Ruby repo's wrong
call edges:
- `receiver.m()` through an untyped receiver took the project's one method
named `m`. It now also needs a word shared between the receiver and the
method's owner (`web_push_request.legacy_encrypt` → WebPushRequest);
`self` / `self.class` are exempt.
- a bare `m()` inside a class took any class's `m`. It now reaches only the
class's ancestry — superclasses and include/extend/prepend modules, read
from source and resolved against the lexical nesting. Calls in module
bodies and top-level blocks (specs, DSLs) are not judged.
A/B (edges removed / added): rubocop -4406/+262, mastodon -2642/+540,
jekyll -343/+10, lobsters -243/+4, sinatra -207/+2, devise -112/+3.
Deliberate losses: instance_eval'd DSL blocks inside a class method
(sinatra's own tests' `mock_app { get '/' }`), and helpers on a framework
class the repo reopens past an external superclass (devise setup_mailer).
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
|
||
|
|
75976f9929 |
fix(kotlin): top-level functions resolve only where they're visible (#2146)
A Kotlin call's top-level candidate (a function, an extension function, a property) must be in the caller's package, imported by name, or in a star-imported package. Class members aren't judged: a lambda's receiver can put any type's members in scope. A function type with a qualified receiver (`(A.Builder.() -> Unit)?`) broke the tree-sitter-kotlin parse badly enough to drop the enclosing class; an offset-preserving preParse blanks the qualifier (hoisted, so the kernel parses the same bytes — parity 0 diffs on Exposed, okhttp, nowinandroid). A/B (edges removed / added): Exposed -5370/+4256 (1,318 R2DBC assertEquals misroutes; DatabaseTestsBase's members and 250 extends/imports edges back), koin -965/+960, okhttp -690/+675 (567 MockResponse to the imported mockwebserver3 class), okio -98/+44, nowinandroid -14. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
0ff11b36e7 |
fix(dart): a bare call stays in the class hierarchy (#2145)
A receiver-less Dart call now reaches only a method of the enclosing class, something it extends / mixes in / implements, or an extension on one of those types, and the nearest one wins. The hierarchy is read from each declaration's head (comments and type arguments dropped), since supertype edges don't exist on the first pass. Body-less abstract members (extracted as owned functions) are scoped too. Mixin-application classes (`class A = B with C;`) were extracted as `<anonymous>` with no supertypes; both the wasm extractor and the Rust kernel now name them and emit their extends/implements refs (kernel parity verified on bloc, riverpod, shelf, dio, getx). A/B (edges removed / added): riverpod -1945/+517, shelf -785, getx -855/+18, bloc -178/+95, dio -78. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
422db1866e |
fix(r): a test file's top-level variables stay in that file (#2144)
testthat runs each test file in an environment of its own, so a variable a test assigns is invisible to the package and to other tests. ggplot2's `c <- ggplot(…)` in test-facet-grid-.R was the one node named `c`, and took 2,455 of the package's `c(…)` calls (the sweep's top-depended symbol). isVisibleAcrossFiles rejects an R variable/constant under `tests/` from another file — except `helper-*.R` / `setup-*.R`, which testthat sources for every test. (A first cut that limited every R call to function nodes lost ggplot2's factory-defined `geom_point <- make_constructor(…)`, dplyr's `summarize <- summarise` and shiny's aliases.) Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
7ac8eefb85 |
fix(lua): a local is invisible outside its chunk (#2143)
A Lua `local` (variable or function) belongs to the file that declares it, but cross-file name matching treated it like a global. kong's spec helpers re-bind busted's globals (`local it = it`, `local assert = require "luassert"`), and every other spec file's `it(…)` / `assert(…)` / `describe(…)` linked to them — 15,776 edges; koreader's `local ipairs = ipairs` in one module took the project's `ipairs` / `pairs` / `type` / `tonumber` (2,329). isVisibleAcrossFiles now reads a Lua/Luau variable or function's declaration line and rejects a `local` one from another file (the reference stays unresolved rather than taking a runner-up). Globals stay visible. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
dcbbd1687d |
fix(php): $this / self / static / parent calls stay in the class hierarchy (#2142)
Extraction records `$this->m()` under the bare name, so exact and fuzzy matching took ANY same-named method: Drupal core's tests' `$this->assertEquals()` (PHPUnit's) went to a comparator class 8,832 times, `$this->assertSession()` to WebDriverTestBase 9,556 times, `$this->t()` to `Views::t` 6,811 times; Laravel's `$this->assertCount()` to FilesystemAdapter 1,722 times. A call written through `$this->` / `self::` / `static::` (or `parent::`) now takes only a METHOD of the enclosing class's ancestry — `extends` and trait `use`, read from source and resolved through the file's `namespace` / `use` imports (aliases included) to fully qualified classes — or a subclass's (a base calling what a subclass defines). Past an ancestor outside the repository (Orchestra's TestCase) a repository trait's method still counts, an unrelated class's never does. Inside a trait, `$this` is the using class, so trait bodies are not scoped. Multi-line `use A,\n B;` trait lists parse. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
ca3bb444f7 |
fix(resolution): case-insensitive names pass the prefilter; PHP => fn() is a bare call (#2141)
- The name prefilter checks a case-SENSITIVE set of known names, so in PHP,
Pascal, CFML, COBOL and VB.NET — whose identifiers ignore case — a call
written in another case (`formatprice()` for `FormatPrice`, TestBox's
`ToMatchWithCase()`) was dropped before any strategy ran. Those languages
now also check a lowercase set (built lazily, only when asked).
- `isBarePhpCall` took any `>` before a name for `->`: every call after `=>`
in a PHP array (`'total' => count($items)`, `'by' => user()->id`,
`'label' => trans('…')`) was a member call, and found a same-named method
— 166 wrong edges on BookStack (`trans` → LocaleDefinition::trans,
`new Response` → ImageStorageDisk::response, `url()`, `config()`, `count()`).
It now requires `->` / `?->` / `::`.
- A fuzzy `instantiates` never names a method.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
|
||
|
|
a3d675ef4d |
fix(vue): Options API methods, computed, watchers and hooks are symbols (#2140)
A `<script>` block goes through the TS/JS extractor, which extracts no
symbol for a method in an object literal — so every Vue 2 component, and
every Vue 3 one not on `<script setup>`, had no `handleLogin`, no
`mounted`: their calls, their `this.$router.push`, and the template's
`@click="handleLogin"` all belonged to the file.
`vue-options-api.ts` reads the options object the default export is
(directly, or via defineComponent / Vue.extend / Vue.component) and names
each function in it — `methods`, `computed` (incl. get/set objects),
`watch` (incl. `{ handler }`), `data`, `setup`, lifecycle hooks, Nuxt 2's
asyncData/fetch — as `method` nodes owned by the component; the Vue
extractor re-attributes the references and edges written inside each.
A component's own method is only reached as `this.m()` in that component:
the member-call and exact-name strategies no longer hand it
`e.preventDefault()` (ImageCropper's `preventDefault` method took every
event's call), `this.editor.setValue()`, or `this.$refs['input'].click()`.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
|
||
|
|
66d5dc3641 |
fix(resolution): fuzzy matching is exact-case outside case-insensitive languages (#2139)
The last-resort fuzzy match looks names up in a lowercase index — right for PHP, Pascal/Delphi, CFML, COBOL and VB.NET, whose identifiers ignore case, and wrong for every other language. jsoup's 1,844 `@Test` annotations decorated a `CharPredicate.test` method; commons-lang's `new BitSet()` instantiated a `bitSet` field accessor 64 times; mall's `new Info()` an `info()` endpoint; gson's `Method` type a test's `method()`; fmt's `#include <optional>` gtest's `Optional`. This generalizes the per-language exact-case rules (Rust, Python, JS) into one: outside CASE_INSENSITIVE_LANGUAGES a fuzzy candidate must match the reference's exact name. The cross-language fuzzy-gate test now builds its winner from an exact-case name. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
6a02dcaa76 |
fix(react-native): event names held in constants join the native↔JS channel (#2138)
The rn-event-channel pairs a native emit with a JS listener on the event's LITERAL name. Most libraries name events through constants — NetInfo listens with `addListener(PrivateTypes.DEVICE_CONNECTIVITY_EVENT, …)`, many emit with `.emit(EVENT_NAME, …)` or `sendEventWithName:kEvent` — so their channels were empty. Identifier / member-chain event names are now read to the literal their declaration holds (JS/TS const & enum, Java `static final`, Kotlin `const val`, ObjC `NSString *const` / `#define`, Swift `let` / enum case), scoped as the language scopes them: a bare name in the same file or behind an import, `Owner.NAME` inside Owner or a namespace import's file. The handler of a constant-named listener is a function in the same file, else the enclosing one. A first cut that read constants by name alone turned a `.emit(eventName, …)` parameter into some test's `eventName = "pong"`. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
6f798dce6b |
fix(react-native): Paper requireNativeComponent views reach native; JSX via default imports (#2137)
- `requireNativeComponent('X')` (Paper) now emits the same JS `component`
node a Codegen spec does, so the Fabric pass links it to the native view /
manager classes; the extractor also runs on `.js` / `.jsx` spec modules
(segmented-control's spec is Flow `.js`). react-native-maps' `AIRMap`
gains its iOS implementation.
- A JSX tag that names no node is the file's DEFAULT import of a module's
one component: `<RNCSegmentedControlNativeComponent>`, and element-plus's
test `<Autocomplete>` for `autocomplete.vue` (whose component node is
`autocomplete`). A named import never takes a barrel's one component.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
|
||
|
|
a4f9820233 |
fix(resolution): a super call in an override is not a self-call (#2136)
Extraction keeps `super.didMoveToWindow()` under the bare method name, so every strategy resolved it to the enclosing override itself. Across the bridge sweep that was 139 self-edges on Charts, 142 on commons-lang, 101 on BookStack, 92 on realm-swift, 82 on react-native-svg — lifecycle overrides and delegates drawn as recursion. `gateSuperSelfCall` declines a `calls` result that targets the calling node when the call site is written through `super` / `base` (C#) / `[super …]` (Objective-C) / `parent::` (PHP) / `super().` / `super(C, self).` (Python). Plain recursion keeps its edge. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
72a9346b12 |
fix(expo-router): +api files are endpoints; the nearest router manifest decides (#2135)
- `app/blog/og-image/[post]+api.ts` was a screen named `/blog/og-image/[post]+api` (evanbacon.dev). An Expo API route is now one endpoint per exported HTTP method (`GET /blog/og-image/:post`), bound to its handler, the way Next.js `route.ts` files are. - The per-app framework gate (#2129) took ANY enclosing manifest's declaration: react-native-true-sheet's root declares expo-router for its example app, so its `docs/` Next.js app got 16 Expo screens (`/layout`, `/page`, `/api/search/route`, …). Walking up from a file, the first manifest that names any gated framework now decides. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
67744468d2 |
fix(resolution): Expo module bindings, imported receivers, TS type-only imports (#2134)
- Expo Modules: a JS call on a `requireNativeModule('N')` binding — typed
(`<CameraNativeModule>`) or not, local or imported (expo-camera's
`import CameraManager from './ExpoCameraManager'`) — resolves to module
N's declared function, Swift first and Kotlin beside it, else to the
member on the binding's declared type. It went to the same-named static
method making the call.
- A JS/TS member call on an import binding never picks a method declared in
the calling file, and ruling those out never manufactures a unique guess.
- `import type { X }` was parsed as a default import named `type` (every
type-only import bound `type`), and `{ a, type B }` bound `type B`.
- A name the file binds from an out-of-repo package names nothing in the
project, for every reference kind, and JS/TS fuzzy matching is exact-case:
trpc's 402 `Record<…>` references went to a `record` property, element-plus's
121 `mount` imports from @vue/test-utils to a test helper.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
|
||
|
|
cec041d241 |
fix(angular): Nx workspaces, barrels, class-constant paths and root-relative navigate (#2133)
From the router sweep:
- angular-spotify had 12 routes all named `/` and no navigation. Its lazy
routes are `async () => (await import('@ws/home')).HomeModule` through an
Nx library's `src/index.ts` barrel, which the mount pass never looked
through; and every lib is its own `src/`, so each routes file got its own
route table and no template's `routerLink` found the screen it names.
- jira-clone had 3 routes and no navigation: its issue route is
`issue/:${ProjectConst.IssueId}` (a class `static readonly`), its root
redirect lives in `app.routes.ts`, which only mounts, and
`navigate(['project', 'issue', id])` has no leading `/`.
Now: lazy mounts follow `export … from` barrels (and an NgModule's routing
imports); `(await import(x)).M` is read; route constants may be class
statics (strings or objects), enum members, or live behind a barrel, and a
template-literal path takes constant holes; a routes file that only mounts
contributes its redirects at its mount prefix; the route table is keyed by
the `angular.json` / `nx.json` workspace; and `router.navigate` /
`createUrlTree` without `relativeTo` resolve a bare first command from the
root, as Angular does (template `routerLink` stays relative).
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
|
||
|
|
4423891e76 |
fix(react-router): v5 <Redirect to> and styled(Link) wrappers navigate (#2132)
react-boilerplate's header links are `export default styled(Link)`…``, used as `<HeaderLink to="/features">`; takenote's guards render v5's `<Redirect to="/" />`. Neither tag was read, so both apps had routes and no links between their screens. The link synthesizer now reads `<Redirect to>` (navMethod `redirect`) and any styled-components / emotion wrapper of `Link` / `NavLink` — declared in the file, or imported from a file that declares or default-exports one. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
b62da73550 |
fix(sveltekit): route names drop (group) folders and parameter matchers (#2131)
A `(group)` directory shares a layout and is never part of the URL, and a
parameter's `=matcher` checks its value. Kept in the name, shadcn-svelte's
`src/routes/(app)/(layout)/blocks/+page.svelte` was `/(app)/(layout)/blocks`,
so no `goto('/blocks')` or `<a href="/blocks">` ever reached it and its
Screens showed no navigation at all.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
|
||
|
|
54a1698caa |
fix(vue-router): admin-template route tables, children and layouts; Nuxt routes only in Nuxt (#2130)
vue-element-admin, vue-admin-template and vben had no routes: their tables
are named arrays (`export const constantRoutes = [...]`, `const routes:
RouteRecordRaw[] = [...]`) or per-module objects (`const tableRouter = {…}`)
handed to `new Router(...)`, and most of their screens are `children`.
The Vue Router reader now reads those tables in any file that builds a
router, imports vue-router or lives in a router/ directory; joins children
onto their parent's path (a parent is a screen only when no child claims
its address and it doesn't redirect); binds a lazy view by the FILE it
imports (all of vue-element-admin's are `…/index.vue`), through an alias the
resolver can't follow by the one file in the app with that path; links each
screen to its parents' components as layouts; and takes `this.$router.push`.
Nuxt's file routes move into their own `nuxt` resolver, detected only in a
Nuxt app: halo's plain-Vue console got 30 made-up screens from its `pages/`
folders. A Nuxt app at the repository root now gets its routes, and a
top-level `pages/index.vue` is `/`, not `/index`.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
|
||
|
|
92e14eda40 |
fix(extraction): a file-based router reads only its own app in a monorepo (#2129)
create-t3-turbo, tamagui's starter and react-native-reusables keep an Expo app beside a Next.js app. Expo Router read the Next app's `app/` folder as its own, so `layout.tsx` became a `/layout` screen, `page.tsx` a `/page` one, `_components/posts.tsx` and `api/auth/[...all]/route.ts` screens too, and a shared `packages/app/` library's files became screens as well. A resolver can now name `appDependencies`; its extractor runs on a file only when that file's package.json or an enclosing one declares one of them. When no manifest in the project declares any, detection found the framework by other evidence and it runs everywhere, as before. Expo Router names `expo-router`, Next.js `next`. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
1ef1eb886c |
fix(resolution): Vue, Svelte and Astro calls follow the JS/TS rules (#2128)
Calls in `.vue`, `.svelte` and `.astro` files carry their own language, so the JS/TS rules from #2120 never applied to them: halo's `t('…')` from `useI18n()` went to an interface property `t` (562 callers) and `ref(…)` from `vue` to a local `ref` (523). "Outside the repository" is now a package the importing file's package.json chain (or a Deno import map) declares, a Node built-in, `@std/…`, or a framework's virtual module. An alias the resolver can't follow — SvelteKit's `$lib/…`, a nested Nuxt app's `~/…`, a nested app's own `@/…` — stays the project's, and a `workspace:`/`file:`/`link:` dependency is in the repo. SFC files also get the JS built-ins (`fetch`, `Error`, `Map`), and a file's own import of a built-in's name (`import Map from './Map.svelte'`) is the import. Untyped three-segment chains (`api.groupReports.getAll()`) keep resolving in SFCs, where they reach their API client class. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
8987be4254 |
fix(resolution): a bare Java call reaches its class, supertypes or static imports (#2126)
Found by the README-wide sweep: halo's most-depended-on symbols were an `EmailVerificationService.verify` (1,038 — Mockito's `verify(mock)`) and a builder's `eq` (845 — ArgumentMatchers `eq`); retrofit's a test helper's `assertThat` (1,357 — Truth); mall's a DTO's `hashCode` (Object's). A bare Java call (`verify(x)`, `helper()`, `this.x()`, `super.x()`) now has only method candidates declared on a class around it, on one of that class's supertypes (read transitively from the declarations' `extends` / `implements` — the resolved edges don't exist yet on the first pass), or imported statically (`import static a.B.m;` / `a.B.*`). Memoized per type and per file. A/B vs main (edge diffs, every change classified): halo −2,103/+145, retrofit −1,380/+18, commons-lang −202/+87, jsoup −92/+45, mall −76, gson −7/+4. Gains are the right target where one exists: halo's `and` / `equal` / `isNull` through `import static …Queries.*`, jsoup's `attr(…)` through `LeafNode`, commons-lang's `addExact` on its own class instead of a nested MathBridge, retrofit's `getRawType` through `CallAdapter.Factory`. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
99209b6f3d |
fix(resolution): a Python call reaches a method through its receiver, never by name alone (#2125)
Found by the README-wide sweep — Django/Flask/FastAPI apps' most-depended-on lists were test and serializer methods: netbox `UserConfig.all` (3,610 from `X.objects.all()`), `PortSerializer.create` (2,093 from `.objects.create`), a test-local `FakeQuerySet.count` (1,910); healthchecks `AuthTestCase.get` (880 from `Channel.objects.get()`), `ProfileAdmin.login` (702 from `self.client.login()`); mealie `_FakeHTTPResponse.json`. Python keeps a call through a receiver it cannot name (`User.objects.get`, `self.client.login`, `request.POST.get`) as the bare `get` / `login`, and exact-name matching then took any class's method by proximity. The call's shape is now read at its column (the call's start): - `bare` (`get(1)`): not a method — Python has no implicit self — and not a project symbol when the file takes that name `from` a module the project doesn't contain (`from django.shortcuts import render`); - `chained`: only a member of what the chain names last — a method of a class of that name (case/underscores ignored: `self.store` → `Store`, `self.user_service` → `UserService`) or a function/class in a module of that name (`app.helpers.slugify()` → helpers.py); - `self.x()` / `cls.x()`, and chains split across lines: unchanged. Fuzzy matching is case-exact for Python (`dir(…)` ≠ class `Dir`). Lexical reachability now walks every enclosing scope, so a method of a class declared inside a function is only reachable in there (memoized per candidate). A/B vs main (edge diffs, every change classified): netbox −18,091/+73 (indexes 8s → 5s), healthchecks −3,354/+361, pytest −2,950/+105, mealie −1,802/+6, allauth −1,086/+57, DRF −943/+50, flask −167/+17, httpx −147/+24. Gains are precise member links (`self.channel.notify` → Channel.notify, `item.stash.get` → Stash.get, `config.cache.get` → Cache.get, `self.provider.verify_token`); the one deliberate loss class is calls through an instance whose type needs inference (`flask.g.setdefault` in flask's own repo). excalidraw, typeorm, gson, okio, AutoMapper, gin and hono byte-identical. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
ddc03f7c79 |
fix(resolution): a bare Rust name reaches only what is in scope (#2124)
Found by the README-wide sweep: ripgrep bound 469 `Some(x)` calls to its own `EncodingMode::Some` variant and 536 `Ok(x)` to `ParseResult::Ok`; serde bound its `Ok(…)`/`Result<…>` to the `struct Ok` / `struct Result` its macro-hygiene test declares (845 on one); tokio 924 `Ok` onto `TransitionToIdle::Ok`, 290 `drop(x)` onto a `drop` method. For a name that is bare IN THE SOURCE (a `::` path is kept by its last segment and is not a prelude lookup — checked at the reference's column): - an enum variant is in scope only through a `use` of it (and its enum) or of its enum's `*`, and never names a type; - a prelude name (Ok/Err/Some/None/Result/Option/Box/Vec/String/drop/…) reaches a project item in another file only through a project `use` of that name or a glob of that item's own module (`use crate::glob::*`, `use super::*` for the parent) — `use std::…` trees don't count, and the `use` scan runs on comment-stripped text (a doc comment's "…use the Option…" once read as an import); - fuzzy matching is case-exact for Rust (`Bytes` ≠ method `bytes`, `Ok` ≠ fn `ok`). Applied as a candidate filter (so the in-scope item can win) and at resolveOne's seam — a Rust framework resolver's `Ok(x)` → `struct Ok` construction bypassed name matching. A/B vs main (edge diffs, every change classified): ripgrep −1,462/+0, serde −2,072/+40, tokio −1,820/+2, clap −752/+7, axum −397/+4, bat −45/+0 — losses are Ok/Some/Err/Result/Option/Vec/drop and same-named variants pinned on project items; gains are pre-existing same-name ambiguity (serde's two `Content` enums). Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
c5ba68076a |
fix(resolution): type parameters, def-local types and Scala scoping (#2123)
Found by the README-wide sweep: cats' most-depended-on symbols were an `implicit def A` (4,916) and a `case class B()` declared inside a test method (3,573) — every type parameter `A`/`B`/`F` in the library bound to them; getx's `T` to a constant `T`; serde's `E`/`T`/`D`/`S`/`F` to test structs. New seam gate (type-parameters.ts, applied in resolveOne so every strategy obeys it): - A resolved TYPE reference whose name an enclosing declaration declares as a type parameter is that parameter: read from the heads (the index records none) — after the declared name (`class Foo<T>`, `fn f<T>`, `func F[T any]`, `def f[F[_]: Monad, A]`), before it detached from a type (`<T> T max(`, `fun <T> f`), a C++ `template <…>` above; annotations (`@sp(Int) A`), variance and keywords skipped; a `[…]` list counts only [] and () so Scala's `B >: A` bound does not close it; lists up to 40 lines. - Scala: a bare name a `def` takes as a value parameter (`(f: A => B)`, `(implicit G: …)`) is that parameter — cats bound 846 `f(a)` calls to a case class's `f` field. Class parameters are fields and are left alone. - Scala: a BARE (in the source) type reference reaches a member of another type only from inside it, a subtype, or a file importing its members; otherwise the in-scope type of that name is taken when one fits (cats' `trait FlatMap`, not `Eval`'s nested `FlatMap`; sttp's `trait Backend`). Name matcher: - A type (class/struct/enum/interface/trait/alias) declared inside a function is only reachable from inside it, like nested functions (#1230). - A capitalized Scala type position never names a method. A/B vs main (edge diffs, every change classified; each arm's init exit code checked — an earlier cut crashed resolution on a Scala method named `*`, now pinned by the test): cats −25,281/+179 (the gains retargets to the in-scope FlatMap/State; the real types Shown and IsEq kept 5/5 and 315/315), sttp −1,237/+23, getx −430, serde −446/+9, typeorm −632/+152 (`EntityTarget<Entity>`'s type parameter; codemod fixtures' function-local classes), gson −9; okio/fmt ±2, Newtonsoft.Json and gin byte-identical. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
e453e820a8 |
fix(resolution): a C#/VB.NET type position names a type, never a same-named member (#2121)
Found by the README-wide sweep: AutoMapper's most-depended-on list held a
test class's `List` property (519) and an attribute's `Type` property (259);
eShop's held MSTest's `TestContext` and MAUI's `Easing` as properties.
A `references`/`type_of`/`returns` ref whose SITE is a type position — read
from the source at its column: `Type name` (a declaration names its type
first), `List<`, `Type?` (not `?.`), `Type[]`, a generic argument, `new X`,
VB's `As` / `New` / `Of` — and every `instantiates` ref (its column is the
`new`) now has only type candidates in exact-name and fuzzy matching: a
property, method (a constructor is one), field or enum case sharing the name
is not one. With the member out of the way the real type wins: `TypeMap
typeMap` now links the `TypeMap` class instead of a `TypeMap` property beside
it. Anything else — `Builder.Services`, `MapGet("/x", GetItems)`, a route's
reference to its action — keeps every candidate.
A/B vs main (edge diffs, every change classified): AutoMapper −1,871/+777,
Newtonsoft.Json −3,612/+599, serilog −447/+187, eShop −78/+15,
CleanArchitecture −9. Losses are BCL/framework types that had been pinned on
project members (List, Type, DateTime, CancellationToken, Exception,
TimeSpan, Easing, TestContext…); gains are the project's own types
(TypeMap, MemberMap, LogEvent, LogEventProperty, TypeNameHandling,
JTokenReader, OrderStatus). An earlier cut that dropped every member
candidate also dropped method groups, `Builder.Services` reads and ASP.NET
route→action references — the position reading keeps all of those.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
|
||
|
|
7a51969fb0 |
fix(resolution): JS/TS calls never pick a class member by name alone when the code rules it out (#2120)
Found by the README-wide sweep: the "most depended on" lists of popular
repos were topped by members that nothing actually calls.
- A member call on a NAMESPACE import (`import * as z from "zod/v4"`) calls
one of the module's exports, never some class's method; a receiver
imported from a package outside the repository names nothing in it.
matchMethodCall's name/word-overlap strategies no longer run for either.
zod: `z.string()` had bound 2,640 calls to a test helper's `string`
getter (Mocker::string topped the hub list with 3,209), `z.toJSONSchema`
371 to ZodType::toJSONSchema; trpc `z.record` to a generated SDK class.
- A receiver-less JS/TS or Go call cannot reach a PROPERTY, FIELD or enum
case either (it already excluded methods, #1714/#1857): typeorm's mocha
`it(…)` bound 2,879 calls to an interface's `it` property and `describe(…)`
1,368 to a command class's `describe` string field.
- A bare call to a name the file imports from outside the repository
(`import { useQuery } from '@tanstack/react-query'`, CommonJS
`require('supertest')`, vitest's `test`) gets no name-matched candidate.
- The React resolver leaves an imported component/hook/context to import
resolution (framework resolution runs first and picked any same-named
project hook — trpc tests' `useQuery` landed on a hook nested inside one of
trpc's factories), and never targets a nested hook; same-file hook first.
- "Outside the repository" is the coordinator's `isOutOfRepoImport`:
isExternalImport (aliases, workspace members) AND not the repo's own package
name AND unresolvable by resolveImportPath — so axios's own tests'
`import { getAdapter } from 'axios'` now resolve to lib/adapters.js instead
of the .d.ts interface member.
A/B vs main (edge diffs, every change classified): zod −5,228, typeorm
−4,318, express −700 (calls onto the `var request = require('supertest')`
binding), trpc −350/+17, excalidraw −233/+3 (props callbacks onto Props
interface members), hono −150, axios −52/+23 (the self-import upgrade),
bulletproof-react: 156 sites that pointed at ANOTHER app's component/hook in
the three-app monorepo now point at the importing app's own, 16 wrong
cross-app links dropped, 8 imports the React heuristic had guessed right lost
(per-app tsconfig paths don't resolve — separate follow-up); gin/cobra
byte-identical, react-redux-realworld byte-identical.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
|
||
|
|
b50c7c0a70 |
fix(swift): a call through a type path lands on the type it names (#2118)
`API.PackageController.GetRoute.query(on:)` kept only `query` — the
receiver was a navigation_expression, not an identifier — and the bare
name exact-matched whichever type's `query` came first. On
SwiftPackageIndex-Server every route type has one, so health checks and
package-controller tests "called" the dependency controller (or a test's
own `query`).
Extraction (TS and the kernel's swift walker, byte-parity): a receiver that
is a path of capitalized type names, two segments or more, not led by
`Self.`, is kept: `API.PackageController.GetRoute.query`. Instance chains
(`self.store.load()`) and mixed paths (`URLSession.shared.data`) are
unchanged.
Resolution (swift-type-visibility.ts, called decisively from
matchReferenceInner so nothing falls through to name guessing): the member
on the type the path names — a method, an enum case with associated values,
or a nested type's initializer. A member's owner path is its qualified name
with the outermost extension's written path read back from its line
(`extension API.PackageController { enum ShowRoute … }` →
`API::PackageController::ShowRoute`), so twin `Feature::State`s declared in
`extension BasicsView.Feature` and `extension ObservableBasicsView.Feature`
are told apart. A path shortened inside its namespace or behind a module
qualifier fits; two owners, or none, leave the call unresolved. TCA's
`@Reducer enum Path { case detail }` generates `Path.State`/`Path.Action`,
so `Path.State.detail(…)` lands on the written case.
A/B vs main (edge diffs, every change classified): SPI 76 retargeted, all to
the type the line names, 7 dropped (Fluent `App.Version.query`, `Plot.Node.*`
— SDK members main pinned on project methods); TCA 61 retargeted, 1 dropped
(`State.StateReducer.scope` → an unrelated `Store::scope`); mastodon-ios 177
retargeted (`Mastodon.API.V2.Instance.instance`, `L10n.Plural.Count.vote`),
11 SDK drops; swift-nio 6 calls into a deprecated alias became instantiates
of `ChannelOptions.Types.*`; IceCubes 1/1, Alamofire 2 SDK drops.
Kernel/wasm parity 0 diffs over 2,752 files on six repos.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
|
||
|
|
e845b1cd10 |
fix(nestjs): routes carry the app's global prefix and URI version (#2117)
`app.setGlobalPrefix('api')` and `app.enableVersioning({ type:
VersioningType.URI, defaultVersion: '1' })` put every controller under
`/api/v1`, but routes were named by the controller path alone (`GET /user`).
A front end's `this.http.get('/api/v1/user')` then linked to nothing:
Ghostfolio's Angular client had no cross-tier edge into its own NestJS API.
postExtract now reads each `NestFactory.create` bootstrap and renames the
routes of controllers in that app, in NestJS's order:
`/{prefix unless excluded}/{v}{version}/{RouterModule prefix}/{path}`.
- `exclude` paths (plain, `{ path, method }`, `{/*wildcard}`, `(.*)`, `*`)
skip the prefix and keep their version, as NestJS does.
- A route's version: its own `@Version` (found past decorators with object
arguments), else the class's `@Version` or `@Controller({ version })`,
else `defaultVersion`. `VERSION_NEUTRAL` has none; header/media-type
versioning adds no segment.
- A computed prefix (`config.get(...)`) is not guessed.
- Each app uses its own bootstrap (the longest root above it). A seed script
that creates the same app without serving it doesn't displace the one that
configures it, and an app with no config doesn't borrow another app's.
- Only routes whose decorator sits in a `@Controller` are touched, so an
Express route in the same repo keeps its name.
Ghostfolio: 0 → 103 cross-tier edges (82 of 85 client call sites), no
mismatches. koel, proshop, IceCubes and nest-samples are byte-identical to main.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
|
||
|
|
c3efbbf4e7 |
feat(angular): a template's event bindings call the component's methods (#2114)
An Angular handler's only caller is its template: `(click)="toggleFavorite()"`,
`(ngSubmit)="submitForm()"`. With no edge, the method showed no callers,
impact stopped at it, and the Steps picture of a screen reached its
handlers only through class containment, with nothing to say what fires
them.
The template pass now reads each `(event)="…"` binding — not
`[(ngModel)]`'s two-way half — and links the component to every method of
its own the statement calls (`save()`, `this.toggle(x)`; not
`closed.emit()` or `form.reset()`), as a `calls` edge
(`synthesizedBy: 'angular-event'`) carrying the binding as
`metadata.trigger`: `{ kind: 'prop', name: '(click)', of: 'button' }`.
The binding is in the template, so it cannot be read back from the source
at the edge's line; Steps now takes a trigger an edge carries before
reading one at the site.
angular-realworld: 21 binding sites → 17 edges (3 repeats of a method and
event, 1 `delete.emit(true)`); Steps for /article/:slug draws
deleteArticle on (click) · <button> → DELETE and back to /, addComment on
(ngSubmit) · <form>, the favorite button's toggle → POST/DELETE or
/register. Ghostfolio 225 edges of 302 sites, ngx-admin 75 of 100 — the
rest are `$event.stopPropagation()`, output emits, assignments and
repeats. Controls (koel, proshop) byte-identical.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
|
||
|
|
761611c18e |
feat(angular): routes, navigation and the component tree for Angular apps (#2113)
An Angular app had no route nodes, no navigates edges and no link from a template to what it renders — the viewer's Steps tab said "No screens or endpoints" and there was no Screens tab. Routes (frameworks/angular-router.ts): every `Routes` array — typed `Routes` / `Route[]`, `RouterModule.forRoot/forChild`, `provideRouter`, a routes file's `export default [...]` / `as|satisfies Routes` — walked as objects. `component` binds by `references` (route-roots takes a class as a named handler); a lazy `loadComponent` binds to the file's `@Component` class. `children` join their parent's path; a lazily loaded file's routes are put under the path that loads them in postExtract (following an NgModule `loadChildren` to the routing module it imports), idempotently from the in-file path kept in the qualified name. A route with children is a layout: a screen only where no child claims its address, and linked from each screen inside it (`references`, `layout: true`). Paths written as route constants (`internalRoutes.account.path`, through destructured locals and `const x + '/:id'`) and `$localize` strings are read from the constant object. `redirectTo` aliases an address to the screen it sends the user to; a root `**` redirect answers `/` only. A `matcher` route and a `**` catch-all are not screens. Navigation: `router.navigate([...])`, `navigateByUrl`, a guard's `createUrlTree` / `parseUrl` — a command array (non-literal elements are holes), a static string, a route constant, a component property holding one, a function constant called with arguments, `.concat(id)`. A `relativeTo` navigation, a query-only `navigate([])` and a destination made only of holes (it would match any route of its length) are left unresolved. Templates (angular-template-synthesizer.ts): a component's `templateUrl` file or inline `template:` is read at synthesis time. `<app-foo>` by element selector (same app first; two candidates = none) is a `calls` edge from parent to child (`angular-template`), the edge a navigation in a child component rides to its screen. `routerLink` / `[routerLink]` and a `routerLink:` field written in the class (a tab bar's config) are navigates edges. Gated on an `@angular/core` dependency. Measured: - angular-realworld: 10 routes all bound; 11/11 navigation sites accounted for (10 resolved, 1 query-only); 31/31 routerLink sites resolved; 18 renders. - Ghostfolio: 84 routes all bound, 0 unresolved constant paths; 27 navigate resolved, 23 correctly not (8 relative, 9 query-only, 2 computed, 4 aimed at a matcher route); 162 routerLink edges, every one checked against its site; 170 renders. - ngx-admin (NgModule): 55 routes, 47 bound (8 are @nebular/auth's); no navigation calls in the source. - Controls byte-identical vs main (nodes and edges incl. metadata): koel, IceCubesApp, spring-petclinic, proshop. Ghostfolio index time at parity. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
7639c787e4 |
fix(swift): what a SwiftUI app's viewer showed that was not so (#2109)
Three things IceCubesApp showed in `codegraph ui`:
Steps labelled calls that leave the index by other stacks' names:
`rawValue.data(using: .utf8)` network (25 boxes), Foundation's `Timer`
telemetry, `Calendar` and a SiriKit `intent` device, the app's own
`Notifications` endpoint enum device (expo-notifications' row), and
`viewModel.votes.firstIndex(of:)` a database read (the `\w*Model` row).
Now: a Swift `.data/.upload/.download/.bytes` is network only on a
`*session` receiver; Timer/Calendar/intent are no effect in Swift; a
`viewModel` is not a table (any language); a keychain receiver is
storage ahead of Swift's generic `x.delete` database row. A call whose
receiver is a type the Swift project declares — not an `extension X {}`,
which declares nothing (read through the site reader) — no longer
matches the library-name rows; the model and reply rows, written for
project types, still do.
"Files that run something" listed every view with a `#Preview { … }`:
the macro sits at a file's top level, so its constructions were
module-level edges. Entry points now subtract calls inside a Swift
file's top-level previews (trailing closures included — WidgetKit's
`} timeline: {`), drop a file left with none, and re-rank.
Every `struct X: View` had a one-line `component` twin, and `@main`
apps, view controllers and UIView subclasses a `class` twin, from the
SwiftUI/UIKit resolvers' regex extract(): 188 nodes on IceCubesApp, 184
on mastodon-ios, zero edges into any of them — dead ends in search and
the symbol view. The two extract()s are removed.
IceCubesApp's effect sweep is now URLSession-only network, no
telemetry, no database, keychain storage; its entry files drop from
eleven preview files to its one real script. SwiftPackageIndex's Fluent
database and reply steps are unchanged.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
|
||
|
|
e9be68b61d |
fix(swift): a composition continued on & lines no longer swallows its type (#2108)
typealias EditorClient = AutocompleteService.Client
& MediaUploadService.Client
is valid Swift, but in a type's body tree-sitter-swift ends the member at
the newline and the `&` line is an ERROR that swallows the enclosing
type. IceCubesApp's 980-line `EditorStore` came out as loose variables
and functions — no class, no init, no methods — so a call in its init
counted as file-level code ("a file that runs something"), and calls
into its methods from the views landed on same-named methods of other
services.
A Swift preParse moves a line-leading `&` onto the line before, for an
indented `typealias`/`let`/`var` statement's continuation lines only.
Same length and line count; the continuation line's tokens sit one
column left (the Swift type gate reads the site's qualifier at that one
column over). Measured shapes: the failure is type bodies only — a
top-level declaration, a function body and a parenthesized type parse as
written, and are left alone; `&&`, `&+` and inout `&x` are never
touched. The preParse is hoisted before the kernel call like the C/C#
ones, so both arms parse the same bytes, and the file no longer defers
to wasm.
Across IceCubesApp, SwiftPackageIndex-Server, swift-nio, Alamofire, TCA
and mastodon-ios (3,000 Swift files) it rewrites exactly the one failing
file, which now parses clean; none newly erroring. EditorStore is a class
again with 38 methods; views now reference it and call its
insertStatusText / runAssistant (62 edges into it), and
`urlSession.upload` no longer lands on MediaUploadService.upload.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
|
||
|
|
5753de20f5 |
fix(swift): an extension is not the type it extends (#2107)
`extension View { … }` parses as a class_declaration, so the index held a
`class View` node for every file extending SwiftUI's View, Text, Color,
String, URL. With no declaration of the type in the project, every
`struct X: View`, `Text("…")` and `Color.red` bound to whichever
extension came first: on IceCubesApp 6 of the top 12 "most depended on"
symbols were extension files (View 632 edges, Text 402, Label 209). And
a bare `@State` bound to a view model's nested `enum State` (253 edges),
`@Test` to a test's nested struct (821 on SwiftPackageIndex), `: Error`
to a nested `IOError.Error` (233 on swift-nio).
A gate at resolveOne's seam (every strategy, framework patterns and
name matching alike) checks a Swift reference's type target against what
the site wrote:
- an extension moves to the declaration it extends, or the ref stays
unresolved when the SDK declares the type;
- a bare name of a nested type needs the site inside its parent, the type
itself, or a subtype that declares inheriting from the parent;
- a qualified name must fit its path. The index keeps `Build.Id` as a ref
named `Id`, so the qualifier is read back from the source line
(generic arguments stripped: `EventLoopFuture<T>.Isolated`); a member
indexed top-level because an `#if` ended its type's body early still
fits a parent declared in its file.
Redirects pick the unique declaration in scope, then the same file, then
the nearest directory; a tie is left unresolved.
Two things rode on the wrong edges and are kept:
- `getSupertypes` named `View` for `struct X: View` only because that
edge resolved to an extension. Swift conformances to a type the
project only extends are now read from the declaration head (once per
node, memoized; a per-node unresolved_refs query was +75% index time on
swift-nio), so `X().withEnvironments()` still resolves to the
`extension View` method.
- interface-impl dispatch read a protocol's methods off the node the
conformance landed on. Swift protocol requirements are not extracted as
methods; the default implementations live on `extension P` nodes, which
are now included — every extension, not one arbitrary one (+359
dispatch edges on swift-nio, +44 on Alamofire, none lost).
Measured edge diffs vs main, every removal classified:
IceCubesApp 2473 removed (172 redirects to the declaration, 1989
SDK-extension edges, 280 wrong bare nested-type bindings, ~30 from the
class the typealias parse error swallows); SwiftPackageIndex 3810 (1461
redirects); swift-nio 10991 (5085 redirects); Alamofire 1368 (198). Index
time at parity (swift-nio 4.1s vs 4.0s, Alamofire 1.33 vs 1.37,
IceCubes 1.31 vs 1.31).
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
|
||
|
|
48b1a19b19 |
fix(vapor): bind a route to the handler on the type it names (#2106)
`use: SearchController.show` kept only `show`, and name matching bound every controller's `show` route to whichever `show` came first — on SwiftPackageIndex-Server 7 of 13 routes pointed at another endpoint's code (Search, HealthCheck, ValidateSPIManifest and ReadyForSwift6's `show` all at AuthorController.show). The route's handler ref now keeps the type: `Type@method` (dotted for nested types, `API.PackageController@get`), `@method` for `self.x` and a bare `x`, and a `let todos = TodoController()` receiver's type. The Vapor resolver claims these and resolves on the owner's qualified name: nested types, handlers declared in `extension Type` in another file, and `extension API.PackageController` (whose node carries only the last segment, so its declaration line decides). `self.x` / bare `x` take the method of the type the route is written in. Of one type's overloads, the one declared to take a `Request` wins. Two types, or none, leave the ref unresolved rather than guess. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
179ac85bd9 |
fix(steps): draw how a server-rendered endpoint answers; bare PHP calls are functions (#2103)
Found testing `codegraph ui` on Spring and Laravel: a server-rendered
endpoint's Steps picture never said how it answers. petclinic's POST
/owners/new showed its database write and no reply; BookStack's book page
showed a 404 and not the page it renders.
Spring MVC answers by what a handler RETURNS, which is no call:
- A new tree-sitter reader, returnsInTree / returnsForFile (graph/
branch-guards.ts, exposed as SiteReader.returns), lists the returns a
definition makes itself — never one in a lambda, a local function or a
nested / anonymous class — and the string a same-class constant holds.
- steps.ts: for a mapped method of a class that renders views (not
@RestController, not @ResponseBody), a returned view name / constant is a
200 render, `"redirect:…"` a 302, `"forward:…"` a forward, each under the
condition it is returned in (effectLink takes a preset effect for a site
that is no call). Non-literal returns are not guessed at.
- effects.ts: `new ModelAndView(…)` (200, or 302 for "redirect:…") and
`new RedirectView(…)` (302) are replies.
Laravel's `view(…)` and `redirect(…)` were already reply rules, but never
reached them:
- name-matcher.ts: a PHP call written without a receiver is a function
call, so only a `function` is a candidate — BookStack's every
`return redirect(…)` bound to ApiDocsController::redirect, every
`view(…)` to a `$view` field (and, through the fuzzy fallback, to the
class `View`). PHP refs record the call expression's column, so the call
text at that column decides. koel alone loses 60 such wrong edges
(`basename()`, `auth()`, `value()`, …).
- effects.ts: the PHP reply rule never matched a chain — rules see the text
with argument lists stripped (`response->json`), the rule wanted `()->` —
so `response()->json(…)`, `redirect()->route(…)`, `redirect()->back()->
withErrors(…)` were never replies; `to_route` added; redirect chains are
302s. PHP's SPL programming-error exceptions (InvalidArgumentException,
LogicException, …) are no longer replies, and a namespaced exception is
read by its class name.
petclinic POST /owners/new: 200 `view owners/createOrUpdateOwnerForm` WHEN
result.hasErrors(), 302 `redirect:/owners/${…}` WHEN !result.hasErrors().
BookStack GET /books/{slug}: 404, 200 `view`, 302 `redirect` (the
InvalidArgumentException from a helper is gone). realworld's REST login is
unchanged.
Not in scope: PHP has no site reader (no branch-guard rules), so Laravel
replies carry no WHEN and no arguments — `abort(Response::HTTP_…)` and
`response()->json()` show without a status.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
|
||
|
|
0419a51636 |
fix(synthesis): link a Spring or Laravel test's request to the route it reaches (#2100)
Tests reach a controller by URL, never by a call — MockMvc's
`perform(post("/owners/new"))`, Laravel's `$this->postJson('api/me')` — so
every controller those suites exercise read as untested: the viewer's
"No test reaches this" and explore's `tests:` line both walk callers.
petclinic's processCreationForm, hit three times by OwnerControllerTests,
said no test reached it.
A new synthesis pass (tier-synthesizer section 4, registered after the
cross-tier pass, gated on java / kotlin / php) reads test files only:
- Spring: MockMvc builders (with MockMvcRequestBuilders imported),
`request(HttpMethod.X, …)`, the Kotlin `mockMvc.get(…)` DSL,
WebTestClient `.get().uri(…)`, TestRestTemplate, RestAssured.
- Laravel: `$this->get/post/…Json(…)`, `->json('POST', …)`, `->call(…)`
on a chain rooted at `$this` that only calls methods (so
`$this->app->get('config')` is not a request); Pest's helpers where the
file imports Pest\Laravel; and a project's own verb-named helpers
(koel's getAs → jsonAs → `$this->json($method, $uri)`), recognised by
their body, never by name alone.
Paths are read as built — Spring `{var}` templates, PHP / Kotlin
interpolation and concatenation become holes — and matched with the
cross-tier pass's own route matcher (a hole fills a parameter, never a
literal). One `calls` edge per (test, route), `synthesizedBy:
'test-request'`. These edges are excluded from countIncomingEdges, so a
well-tested endpoint never becomes a hub the Steps walk refuses to enter.
Routes reached by a test: petclinic 16/17, realworld 18/19, BookStack
194/340, koel 105/134 (from 0 each). A random sample of edges checked
against the test source was all correct. The pass costs 86 ms on
BookStack and 68 ms on koel.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
|
||
|
|
ffced05e3c |
fix(laravel): name routes by the path a request takes — leading slash, prefix groups, the /api mount (#2099)
Routes were named by the bare literal in the routes file, so BookStack's
api.php showed `GET pages` for `/api/pages` and koel's `Route::prefix('api')`
group showed `GET ping` for `/api/ping`. Beyond the label, a front-end
`fetch('/api/pages')` could never pair with a route recorded as `pages`.
- extract(): `Route::prefix()` chains and `Route::group(['prefix' => …])`
scope their routes, nested ones in order; a computed prefix is kept as a
`{prefix}` segment. Paths are joined the way Laravel joins them (leading
slash, no doubled or trailing slashes). qualifiedName now holds that
in-file path.
- postExtract(): finds where each routes file is mounted — a group whose
argument is the file, a require / include / loadRoutesFrom inside a
group, Laravel 11's `withRouting(api:, web:, apiPrefix:)` — composes the
prefixes down the include tree, and names every Laravel route from its
qualifiedName plus its file's prefix (idempotent; a removed mount is
undone). routes/api.php defaults to `/api` unless something mounts it or
a `withRouting(using:)` loader might. A file mounted at two prefixes stays
at its in-file path. Only files in routes/ or naming `routes/` /
`withRouting(` are read.
On BookStack all 80 api.php routes become `/api/…` (from its
RouteServiceProvider group); on koel all 118 become `/api/…`; no route is
left without a leading slash.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
|
||
|
|
15a5d1d0cd |
fix(c): keep an #undef under an undecidable #if unknown; a macro call never binds to a type (#2069, #2070) (#2081)
Two follow-ups to the unreleased macro-visibility work (#1838/#1839): - #2069: `#undef FLAG` under an `#if` nothing in the source decides made a never-seen FLAG certainly undefined, so `#ifdef FLAG` was read as false and the functions it guards lost their callers (CPython's `Py_GIL_DISABLED`). A name no directive has touched is now unknown; only an #undef on a certain line clears it. - #2070: dropping the macro constant from a call's candidates let the next same-named node win, so expat's `PREFIX(scanRef)(...)` instantiated an unrelated `struct PREFIX`. When the name is a function-like macro in the project, only a function or method is an eligible callee. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
fef37765c1 |
perf(resolution): remove the indexing regressions since v1.6.0 (vscode stall, per-ref rescans) — graph byte-identical (#2072)
* perf(resolution): read JS/TS parameter lists without exponential backtracking isLocallyBoundJsName (#1759, |
||
|
|
49607201d6 |
fix(resolution): bind implements to the interface of a value+interface pair (#2055)
The inheritance target-kind gate (#1796) rejected any extends/implements resolution whose target cannot be a supertype. VS Code declares every service twice under one name, `export const IFoo = createDecorator<IFoo>(…)` beside `export interface IFoo`, and the import resolver takes the first export of that name, which is the value. The gate then dropped the edge instead of taking the interface the same file declares. Found by the pre-release v1.6.0 vs main comparison: 864 `implements` edges lost on vscode (AccessibilityService -> IAccessibilityService and ~860 like it), plus the ~5.8k interface-dispatch call edges synthesized from them. The gate now moves a TypeScript constant/variable target to the one same-named supertype in its file; with no such sibling it still drops the edge. vscode: implements 4,139 -> 5,003 (v1.6.0: 5,436, 254 of them on non-type targets; now 0), node count unchanged. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
9be3f1a8a2 |
fix(resolution): keep unknown Python import members unresolved (#2040)
Bisect identifies
|
||
|
|
fa25883ab0 |
fix(cpp): resolve local constructors and macro visibility (#1839) (#2035)
* fix(cpp): macro invocations are not calls; local objects call their constructor A function-like macro invocation (`TRACE_POINT(1)`) parses as a call, and the resolver bound it to a same-named free function in another translation unit — a caller and a callee that never existed (#1838). And local object initialization (`Widget w;` / `Widget w(1);` / `Widget w{1};`) reached the class node instead of a constructor, or nothing at all for the default form (#1839). Extraction (wasm walker and the Rust kernel, in parity): - every `preproc_function_def` becomes a `constant` node whose signature is the directive, so the index knows which names are macros; - a C++ declaration emits one `calls` ref per constructed object, shaped `ns::T::T/<arity>`; pointer, reference and function declarators, `extern`, and array element braces construct nothing; the #1035 `instantiates` ref is unchanged; - constructors carry their parameter list as the signature. Resolution: - `cpp-macro-visibility.ts` walks the translation unit (root file plus in-repo includes, each file once) over per-file summaries of function-like defines, undefs and includes, evaluating what each file can decide itself (literals, its own defines, the include-guard idiom). A call whose name is definitely a macro at that point, or a name that only macros bear, resolves to nothing. A macro that exists in one build configuration only (CMSIS's `__DSB()` under the ARM compilers, a target's HAL compat header) keeps the call to the function the other configuration compiles; a wrapper macro that calls its own name is how that function gets called and hides nothing. - `cpp-constructor.ts` resolves the constructor refs: the type in the lexical namespaces of the call site, then the single overload whose parameter count admits the arguments; two admitting overloads, an initializer_list overload, or an aggregate yield no edge. - a `#define` constant is never a `calls` target and does not count toward the same-name ceiling. Measured on a betaflight tree (4,017 files, wasm arm): 153 `calls` edges lost — all fabricated (`MIN`/`MAX` bound to a CLI enum member, macros misparsed as functions, a cross-vendor `SWAPBYTE`) — and 34 constructor edges gained; indexing 34s vs 30s. Fixes #1838 Fixes #1839 Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * fix(cpp): resolve local constructors and macro visibility (#1839) Local construction lacked constructor calls, while macro invocations could bind unrelated functions. Resolve constructors using lexical types and overload signatures, preserving separately declared defaults and array element construction. Evaluate macro directives in translation-unit order, respecting reinclusion, guards and pragma once. Mirror extraction in Rust and WASM, preserve type dependencies, and distinguish them in CLI output. Co-authored-by: danusha2345 <danusha2345@users.noreply.github.com> Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: danusha2345 <danusha2345@users.noreply.github.com> Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com> |
||
|
|
5eaa6fedd5 |
fix(extraction): preserve receivers for method values (#1820) (#2034)
* fix(extraction): record callers for methods passed as values (#1820) pool.submit(obj.method), thread_pool_exec(self.store.fetch), and Go Submit(c.store.Fetch) produced no callers/impact edge because only obj.method(...) call expressions were captured. Member values are now emitted as *.method function-refs and resolve unique-or-drop (same-file first). Two methods of the same name still produce no edge. Go go c.store.Fetch(ids) remains a calls edge (it is a call expression). EXTRACTION_VERSION 27: MCP daemon catch-up rebuilds a stale index on start so the watcher does not keep serving the hole after upgrade. * fix(resolution): do not let test mocks veto #1820 method-value callers A unique production method plus a unit-test double of the same name was treated as ambiguous, so retrieve_vision_vectors still looked callback-invisible on a real repo. Prefer the non-test file when exactly one production target remains. * fix(resolution): prefer a real override over a NotImplementedError base #1820 unique-or-drop treated DocStoreConnection.delete_payload_fields (the ABC stub) and QdrantConnection.delete_payload_fields as ambiguous, so gc_stale_member_stamps stayed invisible. Skip short not-implemented stubs when exactly one real implementation remains. * fix(extraction): preserve receivers for method values (#1820) Python attributes and Go selectors used as callback values were dropped or reduced to bare method names. Preserve receiver paths in native and WASM extraction, resolve through type/import scope, and reject ambiguous or noncallable targets. Cover callback, assignment, collection, and query behavior, with extraction version 27 signaling the required reindex. Co-authored-by: Josh66 <info@aska-systems.de> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Josh66 <info@aska-systems.de> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
2a2f71ec41 |
fix(sync): refresh synthesized edges after incremental changes (#1988) (#2033)
* fix(sync): refresh synthesized edges after incremental changes (#1988) Scoped sync skipped synthesis and retained obsolete dispatch edges. Refresh all synthesis passes after base resolution when dependencies or source patterns change. Stage and atomically replace owned edges, preserve ordinary edges, and retry interrupted refreshes. Stabilize traversal order and migrate existing indexes for rebuild convergence. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix: preserve structural Go containment during synthesis refresh --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
9649ce1bd6 |
fix(resolution): reject unrelated cross-language matches (#1986) (#2032)
* fix(resolution): stop expression-receiver calls from binding to unrelated methods A method called on an expression receiver reached the resolver as the bare method name, and exact-match bound it to any project method of that name: `(await list()).map(...)` in TypeScript onto a UI adapter class's `map` (0.9), and `v.iter().map(...)` in Rust onto the same TypeScript method across languages (0.5). TS/JS extraction (mirrored in the kernel): look through receiver wrappers that keep the same object — parentheses, `!`, `as`/`satisfies`, `<T>x`, `await` (including the grammar's `(a && b)!` parse of `a && b!`) — so `x!.m()` is `x.m` and `(await f()).m()` is `f().m`; any other receiver with no static type (`(a ?? b).m()`, `f().list.m()`, `arr[0].m()`) emits nothing instead of the bare name. `this`/`super` (and chains rooted at them), `window.*` chains and `new C().m()` keep their existing bare form. Resolution: a bare-named call may cross a language family only through the C ABI (a C/C++ symbol, or a free function a C/Objective-C/Swift caller reaches, e.g. a cgo `//export`). A class member of another family, or a function/class/variable outside that pair, is rejected as the candidate a strategy commits to (exact-match single/best and fuzzy), never filtered out of the pool, so no lone survivor is manufactured. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(resolution): reject unrelated cross-language matches (#1986) Name-based calls, constructors, and inheritance could select unrelated foreign-language symbols. Apply a shared interoperability policy to selected results without manufacturing replacement matches, including JSX/Vue synthesis. Preserve framework exports and ABI-backed calls, building on PR #1944's TS/Rust expression-receiver fixes. Validate with both repro scripts, 529 targeted tests, kernel/wasm parity, and clean TypeScript checks. Co-authored-by: danusha2345 <ewidusoc498@gmail.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: danusha2345 <ewidusoc498@gmail.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> |