Repository navigation
Conversation
[skip ci] Auto-generated by Release workflow.
The 1.6.1 bump changed package.json only, as AGENTS.md said was enough, so
ui-package.test.ts ("is versioned with the engine") has failed on main since.
Bump ui/package.json to match, and say in AGENTS.md that a version bump
touches both files.
Co-authored-by: Claude Opus 5.5 <[email protected]>
…, 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 <[email protected]>
…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 <[email protected]>
…ns on its packages (#2102) A Maven / Gradle project keeps every line of Java under src/main/java/org/<company>/<app>/, and those folders hold nothing but the next one. The Map cut one folder per level, so petclinic drew the whole program as one `src/main/java/org` box, and the deepest grouping option (4) stopped at `src/main/java/org/springframework` — no setting reached the packages seven levels down. A folder with exactly one subfolder and no file of its own (read from every indexed file, tests included, so ids don't move when tests are toggled) now joins the level below it. moduleIdFor takes the set as an optional fourth argument — without it nothing changes — and pickDefaultDepth counts the same levels. Collapsing never splits a group at the same depth (a pass-through folder has one child), so repos without such chains keep their grouping; a lone chain like koel's `app/Console` → `app/Console/Commands` is renamed to the folder that holds the files. The box label (the wire `label`, which nothing read) now elides a chain of three or more folders — `src/main/java/…/petclinic/owner` — and ModuleNode and the width calculation use it; the tooltip and side panel keep the full path. Everywhere else the label equals the id, so other maps look the same. petclinic's default map: 8 boxes (owner, vet, model, system, the package's root files, resources) instead of one; realworld opens on io/spring's api, application, core, graphql, infrastructure. Co-authored-by: Claude Opus 5.5 <[email protected]>
…ls 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 <[email protected]>
…t edit (#2104) The symlink retarget/restart test flaked inside the full suite with "waitFor timed out". A traced failing run showed the edit made right after unwatch()+watch() was never reported: fs.watch() returns before macOS registers the stream (libuv serves a process's watches from one FSEvents stream, rebuilt on its own thread after each watch/close), and an edit made in that gap is dropped, not delayed — so no timeout would have helped. Poke a probe file under the link until the watcher reports it, then make the edit under test once. Applied after the restart (the observed failure) and after the retarget (the same race with a 100-300ms gap). Under a simulated 300ms registration delay the old test fails 3/3 and the new one passes 3/3; a dead post-restart stream still fails it. Co-authored-by: Claude Opus 5.5 <[email protected]>
…ding (#2105) `MCP staleness banner > lists pending files under "Pending sync"` flaked in the full suite (2 of 4 runs): status showed "0 modified" and no Pending sync section. The tests used `debounceMs: 4000` to keep an edit pending while they queried, but since #1397 a lone edit syncs after a 300ms quiet window whatever the configured debounce. codegraph_status awaits a freshly spawned worker to count changes before reading the pending set, and under load that outlasted the window, so the sync had already cleared the entry. A 500ms pause before the query fails the status, banner and #1968 path tests deterministically. Hold the watcher's sync open with a never-settling spy in those four setups instead; no assertion changes, and the test that checks the banner drops after a sync still runs a real one. Co-authored-by: Claude Opus 5.5 <[email protected]>
`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 <[email protected]>
`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 <[email protected]>
…ts 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 <[email protected]>
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 <[email protected]>
…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 <[email protected]>
…ds (#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 <[email protected]>
) `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 <[email protected]>
) `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 <[email protected]>
…lled (#2119) `codegraph ui` re-execs itself with `--liftoff-only`; the shim the user started blocks in spawnSync and cannot forward a signal. Killing it by pid — a process manager, an IDE task, `kill` — left the re-exec'd server serving its port forever (found during a sweep: servers from hours earlier were still answering on ports 4801–4808 against deleted indexes, holding their stdout pipes so the harness that started them never exited). Ctrl+C was unaffected (the signal reaches the whole process group). `index`/`init` already guard this with the #277 PPID watchdog. Factor it out of installCommandSupervision as `watchParent(onLost)` and use it in `ui` with the same shutdown as SIGINT/SIGTERM (close the index, then the socket). The liveness watchdog is deliberately not installed for `ui`: a long Steps computation can legitimately hold the event loop. Test: POSIX end-to-end through the real relaunch (CODEGRAPH_WASM_RELAUNCHED unset, 200ms poll) — SIGKILL the shim, the port stops answering; confirmed failing without the fix. Co-authored-by: Claude Opus 5.5 <[email protected]>
…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 <[email protected]>
…-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 <[email protected]>
…re not hubs (#2122) Found by the README-wide sweep: vendored bundles topped "most depended on" across many repos — delphimvcframework's bundled `Buffer` (6,162) and four `n`s (2,386 each), retrofit's docs `main.js` `t`/`e`/`i`, TestBox's `__webpack_require__` — because only `*.min.js` was recognized as generated. - detectGeneratedFile (index time, persisted as files.generated) now also flags a .js/.mjs/.cjs file whose text is mostly lines of 1,000+ chars AND whose long lines are code (≥3% `;{}(),`) — not an ordinary file carrying one long base64 string — and any file that defines webpack's `__webpack_require__` loader, however readable its lines. The path rule also matches `-min.js` (underscore-min.js). - Entry points' hubs skip nodes in generated files, as they skip tests. Scanned every .js file of express, axios, lodash, excalidraw, typeorm, zod, trpc, hono, bulletproof-react: zero new flags on hand-written code; newly caught: lodash vendor/underscore/underscore-min.js, retrofit website main.js (754 KB) + prism.js, TestBox syntaxhighlighter.js (webpack). Co-authored-by: Claude Opus 5.5 <[email protected]>
…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 <[email protected]>
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 <[email protected]>
… 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 <[email protected]>
…atic 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 <[email protected]>
…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 <[email protected]>
…repo (#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 <[email protected]>
…uxt 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 <[email protected]>
… a Shopify theme (#2393) isShopifyLiquidJson matched every templates/**/*.json and sections/**/*.json by path alone, so a .NET project template's template.json (jasontaylordev/ CleanArchitecture), schematics and CMS content were indexed as empty Liquid files, and JSON listing `sections` could link to an unrelated Liquid file. The rule now needs the directory holding templates/ or sections/ to also hold layout/theme.liquid or config/settings_schema.json, looked up under the project root. isSourceFile, detectLanguage and preloadLanguagesForFiles take an optional rootDir, and every caller that decides what to index or how to parse passes it: the scans, git change detection, scoped sync, the watcher, indexAll, indexFileWithContent and CodeGraph.extractFromSource. Without a root there is no theme to find. A sync removes JSON an older index stored this way. Shopify/dawn, expressjs/express and gofynd/Astra index byte-identically; CleanArchitecture loses its 2 bogus Liquid files. Co-authored-by: Claude Opus 5.5 <[email protected]>
…2394) extractInheritance carried Go's struct-embedding check (a field_declaration with no direct field_identifier names an embedded type) into every grammar with a field_declaration_list. tree-sitter-c/cpp, and Objective-C's C structs, nest a member's field_identifier inside its pointer/array/ function/reference declarator, so `SharedState* shared;`, `jv elements[];` and `virtual Status Put(...) = 0;` all became `extends` edges to the member's type: false supertypes in the type hierarchy, blast radius and codegraph_explore, and false cpp-override call edges built on them (google/leveldb: 246 of 302 extends edges). Gate the branch to Go in the TS extractor, drop the mirrored arm from the C/C++ kernel port (and the "kept for parity" quirk note), and drop the inert copy from the Rust-language port. C++ base_class_clause, Objective-C superclasses and Go embedding are unchanged. Validated on leveldb, jq, llm.c and redis: every removed extends edge is a member site, no base-clause edge is lost; gin, SDWebImage and ripgrep dumps are byte-identical; kernel/wasm parity sweeps show 0 diffs. Co-authored-by: Claude Opus 5.5 <[email protected]>
…levels down (#2395) declaredDependencies read the root manifest and the ones one or two directories down. An app whose package.json sits deeper was never read: an ASP.NET solution's src/Web/ClientApp/, or prometheus's web/ui/mantine-ui/ under a workspace root that declares only tooling. Every resolver gated on dependsOn() then stayed off for it: Angular Router and the Angular template pass, React Router's navigation, Vue Router, SvelteKit and TanStack Router. The existing pass is unchanged. When it leaves slots open, a second pass probes the directories three or more levels down that hold indexed JS/TS code, shallowest first and sorted within a level, at most 192 of them, never inside node_modules. Co-authored-by: Claude Opus 5.5 <[email protected]>
…ide that theme (#2396) A `{% render %}`, `{% section %}` or JSON template section `type` written in a file inside a Shopify theme now resolves to that theme's own `sections/` or `snippets/` file, or to nothing. It no longer reaches another theme's copy by path tail (panoply/syncify: using-tailwind's templates linked straps/dusk's sections), nor the root theme's copy over a folder theme's own. The theme detector is shared with isShopifyLiquidJson (#2393): shopifyThemeRoot in grammars.ts. Co-authored-by: Claude Opus 5.5 <[email protected]>
extractInheritance's Go interface-embedding branch matched a `constraint_elem`, a node type neither the vendored tree-sitter-go wasm nor the kernel's tree-sitter-go 0.23 crate has: both parse an embedded interface as a `type_elem` holding one type. So no Go interface ever extended anything, in either engine; gin's IRouter never reached IRoutes. Read an embedded type the same way in an interface and in a struct: `T`, `*T`, `pkg.T` and `T[X]` all embed `T`, recorded at the name so resolution reads the package back from the source (#2322). A type_elem holding a union, a `~T` term or a lone predeclared type is a type-set constraint and records nothing, and predeclared names (`error`, `any`) are skipped in structs too. Qualified and generic struct embeddings, skipped until now, link the same way. Resolution keeps an embedded type in its package. The Go framework resolver's naming heuristics no longer take extends/implements refs: its PascalCase-model pattern saw structs only, so an embedded interface went to another package's same-named struct (promql/parser's Node, etcd's Lease), as #1827 did for Spring. And a Go supertype written through a package that is none of the file's imports as indexed (`yaml` under an unaliased go.yaml.in/yaml/v3, registered as `v3`) stays unresolved instead of binding by its bare name. Validated on gin, prometheus and etcd (before/after edge diffs, every added supertype edge checked against its package; kernel/wasm parity sweeps unchanged). Co-Authored-By: Claude Opus 5.5 <[email protected]>
…e member it calls (#2398) The Angular template pass read own-member calls only in event bindings, so a member a template calls from `[name]="icon()"`, `{{ label() }}`, `*ngIf="isOpen()"` or `@if (loading())` had no incoming edge. With signals that is the usual way a template reads state, and the dead-code derivation listed such members as unused (CleanArchitecture's ThemeToggleComponent icon and label). Every render-time expression is now read: property bindings, interpolations, structural-directive microsyntax, control-flow blocks and @let. A call of one of the component's own members becomes an `angular-binding` edge with the binding as `via` and no trigger. It runs at render, and a trigger drew computed signals as user actions in Steps. A read without a call counts only for a method-kind member: a getter is called, a function handed to a child is a fnRef reference, and a field a call filled is a plain reference. Property reads link nothing, as in TypeScript. Pipes, object and microsyntax keys, members of other objects, template locals, strings and comments link nothing either. Validated on CleanArchitecture, angular-realworld, ngx-admin and Ghostfolio: 272 edges added, none removed, and every registeredAt line checked. The proshop_mern and next-saas-starter dumps are byte-identical. Co-authored-by: Claude Opus 5.5 <[email protected]>
…the type it names (#2399) google/leveldb's MemTable::Add calls `table_.Insert(buf)` on `Table table_;`, declared next to `typedef SkipList<const char*, KeyComparator> Table;` in the class. Receiver inference returned `Table`, no class `Table` has an `Insert` (the one class `Table` is the unrelated leveldb::Table, and C++ type_alias nodes record no aliased type), so the call fell through to the receiver-word guess HandleTable::Insert. It now reaches SkipList::Insert. cpp-type-aliases.ts reads the alias from source and looks it up the way C++ does: an alias declared earlier in the calling function, the caller's class and its bases (read from the class's own base clause), then the namespaces around it. A qualified name resolves a segment at a time (Table::Iterator is SkipList::Iterator). Aliases of a template parameter, a dependent typename or a decltype name no particular class. The constructor path uses the same lookup, so `Table::Iterator iter(&table_);` constructs SkipList::Iterator and an alias hides an outer class of its name. When the aliased type lacks the method, a `.` call or a call through a pointer on a receiver the caller itself declares gets no guessed edge. A `->` through an iterator or smart pointer, a project class template (whose specializations can add members) and declarations read from outside the caller keep the previous behavior. Co-authored-by: Claude Opus 5.5 <[email protected]>
… not dead (#2405) The viewer's Dead code screen listed two kinds of framework-called methods. A method whose decorator resolves outside the project (`@HostListener`, `@Cron`, `@OnEvent`, `@Process`, `@BeforeInsert`, `@InitBinder`) has no `decorates` edge and no `node.decorators`, only the resolver's unresolved `decorates` row. That row now counts as a decorator, the same evidence the opaque-ancestor rule reads for an `extends`/`implements` row, under the existing `decorated` count. A lifecycle hook on a class that skips the `implements` clause (`ngOnInit` on a `@Component` without `implements OnInit`) had no evidence at all. A new `hooks` rule drops a TypeScript method when its class carries a decorator and an interface outside the index carries the name. What the interface carries is read off the classes that write it out: every one has the method, declared or inherited, at least two and more than half declare it, and more than half of them carry a decorator. TypeScript only, because `implements` is erased there and a framework can only be finding the hook by name. The ancestor walk moves out of overrideCandidates into ancestorsOf, shared by both rules. Declarations are read from the candidate list rather than searched by name: a hook is never called from inside the index, and a name some declaration of which is called is the ambiguous-name rule's anyway. Co-authored-by: Claude Opus 5.5 <[email protected]>
…struct elsewhere (#2408) * fix(go): a type written bare links its own package's, not a namesake struct elsewhere The Go framework resolver's name heuristics (Patterns 1-4) picked a same-named declaration by folder convention and fell back to the first candidate anywhere, outranking name matching on a tie. Go reads a bare name only from the reference's own package, so every cross-package pick was wrong: promql/parser's `Node` parameters went to discovery/kubernetes's struct, `apiv1.Node` to the same file's struct, and method calls through an expression (`err[i].Error()`, a chained `.String()`) became instantiations of structs named like the method. - The patterns guess only for a name written bare, and only in its own package; Pattern 4 takes any type there, not only structs. - isGoBareName is the one check for "Go reads this name from its own package"; a Go call through an expression never lands on a type. - Go's if/switch/for/range join the bare-call keywords, so `if T(x)` conversions stay bare. - goRefQualifier reads a variadic `...pkg.T` as written through `pkg`. Co-Authored-By: Claude Opus 5.5 <[email protected]> * docs(changelog): the Go type fix covers exported types; name the before-state precisely Co-Authored-By: Claude Opus 5.5 <[email protected]> --------- Co-authored-by: Claude Opus 5.5 <[email protected]>
…2400) A route table kept in a file of its own produced no routes: the ASP.NET Core React template's AppRoutes array, which App.js maps into <Route {...rest}>, a routes array imported into useRoutes(routes) or createBrowserRouter(routes), and codedthemes' one-object-per-file [MainRoutes, LoginRoutes]. Route objects were read only from a file that itself names a data router, so these apps had no routes and their navigate()/<Link to> navigation reached nothing. The table's own file cannot tell it from a menu's { path, element } list, so the evidence is the file that hands it over: a useRoutes / create*Router argument, or a .map whose callback renders a <Route> from the item's own fields ({...route}, {...rest}, path={route.path}). A new FrameworkResolver.crossFileNodes hook keeps those routes, reconciled in runPostExtract on every index and sync (removal-only syncs included): new ones are inserted with pending references, ones no longer wanted are removed with their incoming resolution edges resurrected, unchanged ones keep their edges. The React pass starts from the files that import React Router, follows the import to the table and the tables it names (...ApiAuthorizationRoutes, children: x), each under its mount, and owns its routes by id (route:<file>:<line>:table:<path>). Route objects (tables and data routers alike): an index: true route is the page at its parent's address; a route object around others is their layout (a references edge marked layout: true, as Angular's), and is not a page of its own where a child claims its address. A route renders past a *Wrapper or *Route guard, and through a same-file lazy(() => import(...)) to the module it loads, never a same-named component in another app. useRoutes joins the file-level gate. Co-authored-by: Claude Opus 5.5 <[email protected]>
…in (#2402) * fix(go): satisfying an interface counts the methods embedding brings in goImplementsEdges (#584) built each interface's method set and each struct's from their own `contains` edges only. Embedded interfaces are `extends` edges since #2397, and embedded structs `extends` (or `implements` for an embedded interface), so both sides now follow those declared edges, recursively and around cycles: an interface asks for the methods of the interfaces it embeds, and a struct has the methods promoted from the structs, interfaces and defined types it embeds. Synthesized edges are never followed. A struct that embeds the interface keeps its declared edge rather than gaining a synthesized duplicate, and under the per-interface cap, structs that declare every method themselves are linked before those that need promoted ones, so the cap keeps the implementations interface-impl dispatch reaches. The empty-interface skip and the cap are unchanged. Co-Authored-By: Claude Opus 5.5 <[email protected]> * test(go): the embedding test's supertype list reads declared edges only Store in that fixture now satisfies other.Closer through the Close that *storage.Base promotes, a go-implements edge synthesized from method names. The assertion is about how declared embeddings resolve, as the rest of the test already treats synthesized edges. Co-Authored-By: Claude Opus 5.5 <[email protected]> --------- Co-authored-by: Claude Opus 5.5 <[email protected]>
…a namesake (#2407) A C++ base class (`class DynamicMessage final : public Message`) was resolved by its name alone, so a base named like a class of another namespace, or of another language, bound to whichever namesake ranked first. In protocolbuffers/protobuf, `DynamicMessage`, `MapEntry`, `ZeroFieldsBase` and two arena_unittest classes derived from `json_internal::ResolverPool::Message`; before #2397 they derived from the PHP extension's C struct `Message`. The Go framework resolver claimed those: protobuf ships Go examples, so the resolver is detected, its `resolve()` runs for every language's refs (`languages` only gates extraction), its PascalCase-struct rule found the C struct, and the framework language gate lets C++ reach C. google/leveldb's and rocksdb's iterators derived from an unrelated nested `Iterator` (`SkipList::Iterator`, `WriteThread::WriteGroup::Iterator`), and leveldb's test comparators from a `Comparator` struct that skiplist_test.cc declares for itself. C and C++ inheritance refs now resolve in their own matcher (cpp-supertypes.ts), ahead of every other strategy and with no fallthrough, so no framework resolver, import strategy or name matcher sees them, and only C and C++ declarations are candidates. The base is looked up the way C++ looks it up: - from the scope the deriving class is declared in, outwards, using the lookup cpp-type-aliases.ts (#2399) already has: a class scope with its bases, a qualified name a segment at a time, an alias followed to the class it names; `::Base` from the global scope only; - then through what the file writes before the class: namespace aliases (`namespace _pbi = ::google::protobuf::internal;`), using-declarations and using-directives, and fmt's macro-opened namespaces; - a class a .cc/.c file defines belongs to that translation unit; - a template parameter, or a member of one, names no class: no edge. When none of that finds the class, the only visible C/C++ class with that qualified tail is taken, since the index loses some classes' namespaces (`class PROTOBUF_EXPORT FieldDescriptor`, classes after a partial specialization); several are a guess, so no edge. The base is read as written, template arguments included, so an alias template (`bool_constant<…>`) is found. An alias that leads to no project class (one of `std::integral_constant`, one each `#if` arm declares differently) is linked itself, as before. Validated with site-keyed edge diffs on leveldb, fmt, protobuf and rocksdb, and redis, SDWebImage and gson as controls (dumps byte-identical): first against main 02d22ae, then again on main dd70646 after #2405, #2408 and #2400 landed, with identical site diffs (#2402, merged after, changes only Go's implements synthesis). Every removed edge was wrong: 9 protobuf `suite_.ReportFailure` guesses that reached `ForkPipeRunner`, cpp-override edges from the wrong bases, and two rocksdb bases that are template parameters. Co-Authored-By: Claude Opus 5.5 <[email protected]>
…on ref (#2401) * fix(kernel): a Go value like `wm.new` or `raft.None` keeps its function ref The kernel's Go fn-ref capture checked a selector value's field name against the name stoplist, so `raft.None`, `compression.None`, cgo's `C.NULL` and a method value like `sync.Pool{New: wm.new}` were dropped. The wasm extractor, the reference, applies NAME_STOPLIST to the normalized name (`raft.None`), which the stoplist never matches. It also builds that name from the operand and the field, so a line break or comment beside the dot keeps the candidate; the kernel validated the selector's whole source text and dropped it. Build the name from operand and field as normalizeSpecial does, and drop the field-name check. No other walker has the pattern: each applies the stoplist to the same name as its TS spec. kernel-parity.mjs --lang go: prometheus 202 -> 0 function_ref diffs, etcd 6 -> 0, golang/go 35 -> 0, kapacitor 2 -> 0, plus 0 on gin, grpc-go, hugo, caddy and go-ethereum. Indexed with both kernels, edges are unchanged on prometheus, etcd, gin and hugo; kapacitor gains the one missing edge, NewExecutor -> workerMaker::new. Co-Authored-By: Claude Opus 5.5 <[email protected]> * fix(kernel): note that a comment before the dot keeps a Go selector value too Co-Authored-By: Claude Opus 5.5 <[email protected]> --------- Co-authored-by: Claude Opus 5.5 <[email protected]>
…the file appears later (#2403) * fix(sync): a Liquid section or snippet reference links its file when the file appears later A sync retries a parked failed reference by its name tail, matched against the names of the nodes the changed files carry; a file's node is named for the file. Liquid records `{% render 'price' %}`, `{% include %}`, `{% section %}` and a JSON template's section `type` as references named `snippets/price.liquid` / `sections/404.liquid`, and a failed one was parked under the text after its last dot: `liquid`, the extension, which no node is named. So a section or snippet added after the files naming it (or restored after a delete) stayed unlinked until a referencing file changed or the project was re-indexed, even in a repository with a single theme. A `references` ref whose name is a path ending in a file name with an extension is now parked under that file name (`price.liquid`), the key path matching looks the file up by. The existing symbol-name retry then finds it with no new query, since the changed files' node names already include each file's own. Calls are left alone: their slashes sit in string arguments, comments or divisions. Schema v13 rewrites the tails an older version parked. Co-Authored-By: Claude Opus 5.5 <[email protected]> * test(liquid): compare the synced links with a fresh index of the same files Indexing again over an existing index skips files whose content is unchanged, so it kept the synced links of the files that name the new ones instead of resolving them again. Co-Authored-By: Claude Opus 5.5 <[email protected]> --------- Co-authored-by: Claude Opus 5.5 <[email protected]>
…2411) Three sync-convergence checks used `indexAll()` on the synced graph as the "full re-index" side. Over an existing index that run skips every file whose content is unchanged, so the edges the sync wrote survive it and each check compared the synced graph with itself. They now rebuild the index from an empty database, as `codegraph index` does (`CodeGraph.recreate` + `indexAll`, the same oracle the CG-33 convergence suite uses): - sync-import-retry: the imports of the unchanged importer, all 20 languages. - sync.test.ts "the synced graph matches a full re-index" and "rebinds the unchanged caller when the symbol moves to another file": every node and edge by natural key instead of node and edge counts, which a bidirectional difference can net out of. Both now index twice in the test body, so they get the 60s bound #1773 gave such tests. Co-Authored-By: Claude Opus 5.5 <[email protected]>
…mes for its package (#2410) An unaliased Go import was named by its path's last element, so the qualifier in yaml.Node (go.yaml.in/yaml/v3), klog.V (k8s.io/klog/v2) or sqlite3.Error (github.com/mattn/go-sqlite3) matched none of the file's imports. The reference then resolved by its bare name to whatever project symbol shared it. An unaliased import now also takes the name goimports' ImportPathToAssumedName gives its path (the last element that isn't a major version, without a go- prefix, cut at the first non-identifier character), unless another import of the file is bound to that name or a second import assumes it too. The last element stays registered: k8s.io/api/core/v1 really is package v1. The import scan now reads only the import section, without comments: the last word of a comment had become the next import's alias, and imports spelled in doc comments or string literals were read as real ones. Co-authored-by: Claude Opus 5.5 <[email protected]>
…utward is not dead (#2418) The viewer's Dead code screen listed TypeScript interfaces that augment a type declared outside the project: chart.js's `PluginOptionsByType` in `declare module 'chart.js' { … }` (ghostfolio), `interface Window` in `declare global { … }` (angular-realworld). The library or the runtime reads them through the type they merge into, so nothing in the repository names them, and the index recorded them as unexported file-level interfaces. TypeScript exports what such a body declares without the keyword. The binder makes an ambient module body an export context unless it holds an export declaration (`export {}`, `export =`, `export default x`), and a namespace nested in one is ambient too. Both extractors now set isExported that way: isAmbientExport in languages/typescript.ts, shared by TSX and ArkTS, and is_ambient_export in the kernel's tsjs walker. The exported rule then drops them, counted under `exported`, and that count's label now names them. `declare namespace X` is unchanged: in a module file X belongs to that file. The resolver sees them as exported too. A file holding `declare global` was already treated as exporting, so nothing moves there. A `declare module` member becomes reachable across files by name. Co-Authored-By: Claude Opus 5.5 <[email protected]>
…udes it (#2421) isVisibleAcrossFiles hides every symbol in a test-suite file from non-test code, decided by the file's name. A C or C++ header is part of every translation unit that includes it, whatever its name: protobuf's conformance framework lives in conformance/conformance_test.h/.cc and test_runner.h, and the 9 `suite_.ReportFailure(...)` calls in the suites found ConformanceTestSuite::ReportFailure through the supertype walk only to have it dropped by the test-suite check. The check now lets a C/C++ candidate through when the reference's file includes it, directly or through other headers. A definition in a source file counts through the same-named test-suite header it includes (conformance_test.cc -> conformance_test.h), so a test that includes the production header it tests (jemalloc's test/unit/hash.c and hash.h) does not become its implementation. The include graph (new cpp-includers.ts) is read from the import nodes extraction mints for every #include, each resolved the way the graph's include edge is, walked from the included file up, from a narrow query of six columns. protobuf +675 edges, none removed or retargeted: 112 in conformance/ and 563 from *_unittest.cc files (which isTestPath does not recognize as tests) into the test utilities they include. redis +1 (expr.c includes fastjson_test.c). leveldb, fmt, llm.c, jq, rocksdb, typeorm, tokio, okhttp and excalidraw are byte-identical. The same exception for Strategy 3's receiver-name guess was measured and left out: it moved rocksdb guesses onto test::StringFS, a test double production code includes. Co-Authored-By: Claude Opus 5.5 <[email protected]>
…ess, inside the layout around it (#2423) Route objects already read `{ index: true, element }` as the page at the parent's address and a route object around others as their layout. JSX routes did neither: `<Route path="/" element={<Layout/>}><Route index element={<Home/>}/>…</Route>` bound `/` to Layout, the index child was never read, and Layout's links counted only from `/`. The JSX branch of scanRoutes now: - reads `<Route index>` / `index={true}` (and a `path=""` leaf with an element) as the page at its parent's address, and lets it claim that address from the layout or the path-only `<Route path>` around it; - treats a `<Route element>` with `<Route>`s inside as their layout (`layout:` references), a pathless or `path=""` one included, and threads those layouts into a table mapped inside it when the table is written in the same file; - reads an index route only where its parent's address is written down: an enclosing route with a readable path, or the router itself (`createRoutesFromElements(…)`, `<BrowserRouter>`). At the top of a component's own `<Routes>` the address is wherever another file mounts the component, and under `path={paths.x}` it is not spelled out; - reads `path={"agents"}` as a path. Reading what an element renders: tags inside an attribute are no longer the page unless the attribute hands one over (`component`, `element`, `page`), so `<Suspense fallback={<Loader/>}>` no longer binds the spinner; the line break Prettier writes after `element={` no longer hides the element; a guard that wraps only `<Outlet/>` is the guard; and `element={<Outlet/>}` renders nothing of its own. Validated against the merged #2400 build: 16 control repos byte-identical, 578 of 581 new layout edges name a component of an enclosing <Route> (3 inherit a pre-existing default-import resolution bug), 27 of 27 new navigations precise, no navigation lost. Co-Authored-By: Claude Opus 5.5 <[email protected]>
…g on Node 22 (#2424) go-ethereum's `codegraph init` sat in "Resolving refs" for good on Node 22 (killed after 1 h 55 min and 9,203 CPU-seconds). It was not Go: two resolver pool workers each sat on one 500-ref chunk of graphql/internal/graphiql/graphiql.min.js, a 980 KB bundle on one line (18,388 refs, 2,234 functions). Whether a JS function binds a name itself (jsFunctionLocalScope, #2226) is read off its comment-stripped code with a parameter-list regex that starts with a `(?<!\b(?:if|while|for|switch|with)\s*)` lookbehind. Node 22's V8 compiles new regexes without optimization once a process has generated about a megabyte of regex code and holds 16 MB of executable memory, which a pool worker reaches after some 30,000 refs. Unoptimized, the lookbehind runs at every position and its `\s*` reads back through the whole run of blanks before it, so a run of n blanks costs n^2/2 steps. The comment stripper, which doesn't know regex literals, reads the `//` that closes `/Trident\//` as a comment and blanks the remaining 961,968 characters of the line. In a worker's state the regex took 36 ms over 16 K characters of that line, 5.1 s over 32 K and 54 s over 64 K, against 0.23 ms over 128 K in a fresh process. Compiling 20,000 throwaway regexes first reproduces it in a bare Node 22 process, and --no-regexp-optimization reproduces it anywhere. Node 24's V8, the runtime release bundles ship, kept optimizing after 60,000, so released installs finish: 1.6.2 indexes go-ethereum on its bundled Node 24 and stalls the same way on Node 22. The stall is as old as #2226. - A leading `(?=\()` keeps the lookbehind to where a parameter list opens. Every match starts with `(` anyway, so the regex matches exactly what it did. - The jsx-render pass split the file, sliced a function's lines and scanned them for tags once per function: 2,234 times over the bundle's line, 25 of the 28 s its linking passes took on Node 22 (9 of 13 on Node 24), for no edges. Functions that span the same lines now share one scan, and the file is split once. Graph: go-ethereum dumps (nodes, edges, unresolved refs, files) are byte-identical between main on Node 24 (main never finishes on Node 22) and this change on Node 22 and on Node 24. Main and this change also match byte for byte on bootstrap4, legend-state, takenote, mantis, excalidraw (482 jsx-render edges), qwik, gin, prometheus, etcd and hugo. Time, go-ethereum: on Node 22 main never finishes and this change takes 36 s; on Node 24 (three interleaved rounds, medians) 42.1 s -> 38.3 s wall, with resolution 17.7 s -> 12.7 s and the linking passes 6.5 s -> 1.5 s. Tests: js-local-binding-backtracking forces V8's unoptimized mode and indexes a function with a 60,000-character comment before a call (69 s on main, 1.4 s now); jsx-render-work counts the tag scans of 60 components on one line (60 on main, 1 now). Co-Authored-By: Claude Opus 5.5 <[email protected]>
… by embedding (#2414) Since goImplementsEdges counts promoted methods, a struct can satisfy an interface with methods its embedded types declare. The interface-dispatch bridge linked each interface method only to a method the struct declares itself, so a call through such a method reached nothing when the embedded type does not implement the interface on its own: prometheus's scraper.Report never reached Target.Report through targetScraper. For a Go struct's synthesized implements edge, each interface method the struct does not declare now links to the method Go's selector picks: the shallowest embedded type that has it, nothing when two occurrences tie at that depth (one type reached along two paths included), and nothing when an embedded interface provides it, which is a dynamic call again. These links come after the per-implementer loop, from what the struct's cap has left, so every existing edge is unchanged. They keep interface-impl metadata plus promotedInto, and registeredAt names the struct's embedding; codegraph_explore labels the hop as a promoted method. The embedding test is shared with goImplementsEdges. Co-Authored-By: Claude Opus 5.5 <[email protected]>
…ppears later (#2422) A router names the page or layout it loads lazily by the module's path: React Router's `lazy-import:./pages/Team`, Vue Router's and Angular's `import:./home/home.component#HomeComponent`, each also behind `layout:`. Sync retries a parked failed ref by name_tail (#1240), and these were parked under a fragment of the path ('/pages/Team', 'component#HomeComponent') that no file's keys match. So a route whose module was added after the router was indexed, or whose module gained its component in a later edit, stayed unlinked until the router file changed or the project was indexed again. #2392 fixed the same gap for imports, #2403 for Liquid's path references. - referenceNameTail parks a module reference under the stem of its path behind 'module:' ('module:Team'), a key no symbol's name can be, so name lookups never retry it by a namesake. - Sync adds the changed files' moduleReferenceKeys (importPathKeys behind 'module:') to the symbol retry: an added file can be the module, and an edit can give the module the component the route renders. - Schema v14 rewrites the tails parked before. Its select keeps SQLite off idx_unresolved_status with `+status`, reading idx_unresolved_name ranges instead of every failed row (vscode's index: about 1 s down to ~12 ms). - The retry resolves in row order, as a full index does, so when a route's module reference and its layout reference make the same edge, sync keeps the one a fresh index keeps (ghostfolio's markets page). Co-Authored-By: Claude Opus 5.5 <[email protected]>
…2412) bulletproof-react's router imports its app shell as `import { default as AppRoot, ErrorBoundary as AppRootErrorBoundary } from './routes/app/root'`. The import mapping recorded AppRoot as a named import of an export called `default`, which no module declares, so resolveViaImport found nothing for it and every reference fell through to name matching. The route /app bound to AppRoot by exact-match, and in a project where another file declares an AppRoot (or a default export is imported under a name another file uses) the call, route or JSX attribute bound to that file's symbol instead. The mapping now records `{ default as X }` as the default import, the same as `import X from`, so every reader of isDefault agrees with it: the import resolver finds the module's default export, the binding's own `imports` reference links the module file as a default import's does, and the JSX-child, Expo native-module, HTTP-client and styled Link lookups read it as the default import. Re-exports (`export { default as X } from`) were already chased as the default. `require('./x').default` is unchanged: `.default` there is a property of module.exports, which a CommonJS module can set by name. alan2207/bulletproof-react: 4 edges re-resolved at the same sites (the route /app reaches AppRoot by import; three binding imports link their module file). bitwarden/clients: 2 failed Storybook imports now link their module. bradtraversy/proshop_mern, leerob/next-saas-starter and t3-oss/create-t3-turbo: byte-identical. Dead-code reports unchanged. Co-Authored-By: Claude Opus 5.5 <[email protected]>
…own languages (#2415) Strategy 1 of resolveOneInner ran every detected framework's resolve() on every reference, whatever its language: `languages` gated only extraction, and gateFrameworkLanguage lets every `calls` result through. Express, detected in etcd by its file-content scan, linked Go `*zap.Logger` result types to a method named `Logger`, and in harbor and bitwarden it linked Python and Rust `validate()` / `authenticate()` calls to an unrelated method or to the calling function itself. getResolvingFrameworks() narrows the loop to the frameworks whose `resolveLanguages` (else `languages`) list the reference's language; one declaring neither stays universal. `resolveLanguages` lets a resolver read a language it extracts nothing from without running extract() there: Svelte reads TS/JS ($lib imports, runes in .svelte.ts), ASP.NET reads Razor (a page's `@model` is the PageModel beside it). claimsReference() stays universal: protobuf's leading-`::` C++ calls get past the name pre-filter only on the Swift/ObjC bridge's claim. Co-Authored-By: Claude Opus 5.5 <[email protected]>
…the parser misreads (#2426) tree-sitter recovers from a construct it can't parse by inserting the token it expected, and inside a class body that is often a `}`: an unknown macro in front of a member (`PROTOBUF_FUTURE_ADD_EARLY_NODISCARD absl::string_view name() const`) or in a class head (`struct ALIGN_AS(64U) HandleImpl`) closed the class there, and every `}` after it closed the scope one level out. Recovery also ran the other way, keeping a scope open past its own `}` once an ERROR swallowed it. The walker scoped declarations by tree nesting, so qualified names lost their namespace and class segments, or gained ones they don't belong to: protobuf's `FieldDescriptor`, `MapFieldBase` and `MutableRepeatedFieldProxyImpl`, fmt's `detail::` buffers (and `fmt::format_int` put in `detail`), rocksdb's clock_cache classes and the `Opts` structs nested in them, gtest's `PrettyUnitTestResultPrinter`. Every such file parses with errors, and the kernel defers those to wasm, so the wasm walker produced all of them; the kernel's walker has the same tree-nested stack and reaches erroring trees only under the CODEGRAPH_KERNEL_CCPP_ERROR_EXTRACT sweep hatch. For a C++ file whose tree has errors, the walker now takes each declaration's namespaces, and at declaration level its enclosing classes, from the source's braces (languages/cpp-brace-scopes.ts: comments, literals, raw strings, digit separators and preprocessor lines skipped, each #if branch read from the braces open at the #if). A class-like node ends at its body's `}`, and a class the tree glued into a namespace-level declaration's type is walked as a class. A file whose braces don't balance, and every file that parses cleanly, keeps the tree's scopes. Ported to the kernel (ccpp/brace_scopes.rs) so the hatch stays parity-true: the same erroring files diverge before and after (UTF-8/UTF-16 recovery), every other one is byte-identical. Co-Authored-By: Claude Opus 5.5 <[email protected]>
…er a method of its name (#2416) A Go type position -- a parameter or result type, a composite literal's type -- names a type of the package Go reads it from. Name matching (and the instance-method guess, and the Express resolver reading Go refs) took whatever declaration shared the name: etcd's `func (ti *treeIndex) KeyIndex(keyi *keyIndex) *keyIndex` linked both types to the method `treeIndex.keyIndex`, prometheus's `samples{...}` literals to `sampleRing.samples`, and `config.URL{...}` from an outside package to `scrape.Target.URL`. gateTargetKind now hands every Go type position to goTypePositionTarget: a method or function target moves to the type of that name in the reference's own package (bare) or the imported project package (`pkg.T`), or is dropped when there is none; a bare name that found another package's type moves to its own package's type when it declares one (prometheus's `prompb` builds its own `Histogram_CountInt`). A generic method's receiver (`func (p *Pool[T]) Get() T`) now declares type parameters for gateTypeParameter. Co-Authored-By: Claude Opus 5.5 <[email protected]>
…ater (#2425) A router's navigation call (`history.push('/login')`, `navigate('/login')`, `router.push('/x')`, `goto('/x')`) names its route by path and its reference by the router's method, so sync never noticed a route that changed in another file. A synced index then kept answers a fresh index of the same files does not have (CG-33): - A call parked as failed while its route was missing never matched the #1240 retry, which keys on the names the synced files define. - A call bound to a catch-all, a parameter route or the other arm of a conditional kept that binding. - A route renamed in place by runPostExtract kept the calls bound to it. - Template links from a routes file that matches no synthesis trigger were never redrawn. When a navigation router is detected, sync now reads the route nodes at its first file change and again after runPostExtract. For each route on one side only, it puts back the calls the routers' new `navigation` hook (method tails and app scope) says that route can answer: the failed ones, and the `navigates` edges a resolver made. The orchestrator's orphan sweep resolves them, under the same 500-per-name ceiling, and the sync refreshes synthesis. This covers re-extracted and removed files, postExtract renames and cross-file table routes. Co-Authored-By: Claude Opus 5.5 <[email protected]>
…#2419) goImplementsEdges (#584) offered only structs as implementers. A Go defined type over a slice, map, function or basic type declares methods the same way (gin's `formSource map[string][]string` has TrySet, prometheus's `staticDiscoverer []*targetgroup.Group` has Run, an adapter `HandlerFunc func(...)` has ServeHTTP) and is extracted as a `type_alias` that owns them through go-method-contains edges. So it never satisfied an interface, and interfaceOverrideEdges, which walks only class, struct and union, could not link a call through the interface to its methods. Structs and defined types are now candidates together, in one canonical (file, line) order. A defined type declares every method it has, so under the per-interface cap it ranks with the structs that declare theirs, ahead of promoted-only ones. interfaceOverrideEdges also walks Go `type_alias` nodes. Every Go type's embeddings come from the batched prefetch (the per-type lazy lookup for defined types is gone), and a type without a method is dropped before the per-interface loop. A true alias (`type A = B`) is not extracted, so it never implements anything itself. iterateNodesByKindIn takes several kinds, interleaved in its one order. Co-Authored-By: Claude Opus 5.5 <[email protected]>
…owercase name calls that type's own member (#2413) A C++ call on a variable, parameter or member the calling function or its class declares as a namespace-qualified type the project does not declare, with a lowercase name (std::string, std::vector, absl::flat_hash_set, ...), no longer falls through to Strategy 3's guess by the method's name. leveldb's `std::string saved_key_; saved_key_.clear()` reached Slice::clear, protobuf's `std::string proto; proto.append(...)` LeftoverBuffer::append. The gate applies to `.` on a value and `->` through a raw pointer; `->` on a smart pointer, iterator or optional still reaches the element type. It skips declarations it cannot trust: read from outside the caller's function and class (including a class nested in the caller's), contradicted by the call (`.` on a pointer), shadowed by an unreadable re-declaration (`auto`, range-for, structured binding), unqualified, with unbalanced `<>` (the tail of a multi-line declaration), or a `_t` name (scalars, tags, traits like std::conditional_t). A `std::` name counts as the project's only when the project declares it in `std` (vendored googletest's testing::internal::string). Receiver inference now blanks comments before matching declarations, so `// ... non-null imm_` no longer types leveldb's imm_ as `null`. Co-Authored-By: Claude Opus 5.5 <[email protected]>
…t names (#2417) tree-sitter-go parses `type Event = mvccpb.Event` as a `type_alias`, beside the `type_spec` of a defined type, and both extractors read `type_spec` only. etcd's client/v3 `Event` had no node; a use through its package (`clientv3.Event`, prometheus's `storage.AOptions{…}`) linked to nothing, and a use inside it linked by name to any type so named, another package's included. - Extraction (TS and kernel): an alias is a `type_alias` node referencing each type its right-hand side names, at the name so resolution reads the package back; an alias of a struct or interface literal is a struct or interface. The alias's own type parameters and Go's predeclared types are skipped. A generic alias, which tree-sitter-go 0.23 parses as a `type_spec` around an error, reads as an alias on wasm; the kernel defers its file. - A method called on an alias-typed value is the aliased type's: an alias of a project type is followed in its own package, through pointers and further aliases; one of an outside type resolves by name as before. - An alias's target written through a package that is none of the file's imports as indexed stays unresolved, as an embedded type's does, so the alias never links to itself or a namesake. - With Go defined types as implementers (#2419), an alias that owns methods written with it as the receiver implements what they satisfy, in place of the type it names. Co-Authored-By: Claude Opus 5.5 <[email protected]>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to subscribe to this conversation on GitHub.
Already have an account?
Sign in.
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
See Commits and Changes for more details.
Created by
pull[bot] (v2.0.0-alpha.4)
Can you help keep this open source service alive? 💖 Please sponsor : )