`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>
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>
* fix(extraction): an MPEG-TS video named .ts is not TypeScript (#1910)
`.ts` mapped to TypeScript by extension alone, so MPEG transport-stream
fixtures (`testdata/*.ts`) were parsed by tree-sitter: ~28 s of CPU for a
900 KB clip, minutes for a directory of them, for zero symbols.
Recognise the stream from the head of the file — the 0x47 sync byte at
offsets 0, 188, 376 and 564 plus a NUL byte, which every stream carries in
its first packets and UTF-8 source never does — and drop it at discovery:
not indexed, not parsed, not counted, not tallied as an unsupported
language. The check reads under 1 KB and only for `.ts` files; the batch
reader sniffs the bytes it already read, and the single-file path (sync,
watcher) does the same head read, so a clip handed in by name is skipped
too. The skip happens before parse dispatch, so no kernel mirror is needed.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
* fix(extraction): a video .ts is never pending on the git path or a scoped sync
Review found two places that decide "is this a source file" without the
MPEG-TS check the scan applies: the git fast path's candidate loop in
getChangedFiles and the scoped-sync path filter (watcher events). An
untracked clip in a git repo was reported as `added`, skipped by sync
without a record, and reported as `added` again on every status; a tracked
.ts that became a clip stayed `modified` with its stale nodes. Both now
treat the clip as non-source: removed when tracked, ignored otherwise.
Regression tests fail without this change and pass with it, on both arms.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(extraction): require a binary head before calling a .ts file video (#1910)
Review on #1915: the sniff took four aligned 0x47 sync bytes plus any NUL
as proof of a transport stream, so TypeScript with a `G` at four 188-byte
strides and one raw NUL in a comment was dropped unindexed. The sniff now
needs the sync byte on sixteen consecutive packets (3 KB of head) and at
least 1/64 of the head to be control bytes. Compressed payload carries
8-20% of them (checked on ffmpeg-made H.264/AAC, MPEG-2 and MP2 streams);
source text carries none. A shorter clip is cheap to parse anyway.
Also drops the benchmark figures from the changelog entry.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(extraction): a file over the size limit is never read (#1910)
The size gate ran after `readFile`, so a committed video or blob fixture was
decoded in full — 3.4 GB of RSS for a 400 MB file, `Invalid string length`
past ~512 MB — only to be stored as skipped. It was then read again by every
pass that scans files by name: the framework detectors' `readFile` and the
resolver's cached reader, three full passes on the reporter's fixtures.
Stat first, everywhere a source file is read by path: the batch reader and
`extractFile` store an oversize file with a size stamp in place of its
content, change detection hashes the same stamp (so a same-size rewrite of a
file nothing is indexed from is not a change, while crossing the limit in
either direction is), and both by-name readers return null above the limit.
The limit moves to `src/file-limits.ts` so the three sites share one number.
strace on a sparse 400 MB `.ts`: 153 603 reads before, 0 after; indexing it
next to a real file costs no measurable RSS. Stacked on #1914, which shares
the batch-reader hunk.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
* refactor(extraction): one-purpose size-stamp reader, comments in operation order
`readSourceOrStamp` took a `stats` argument no caller passed and returned
stats no caller read; it now returns the text or stamp change detection
hashes. The batch reader's two comments are back in the order its code runs:
the size gate, then the MPEG-TS head check. No behaviour change.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(extraction): bound the read itself, not only the stat before it (#1910)
A file can grow between the stat that gates the size limit and the read
that follows (a log, a download, a build output being written). Every
source read now goes through readBoundedSource/readBoundedSourceSync: the
open descriptor is re-checked, and the read stops one byte past the limit,
so an oversize file is still stamped rather than decoded. The extraction
batch reader, single-file extraction, framework detectors and the resolver's
file cache all use it. Ported from the maintainer's hardening of this fix
in #1919.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(extraction): bound the parse-retry reads too (#1910)
The retry passes for files whose worker crashed or timed out re-read the
source with an unbounded fsp.readFile. The file can have grown since the
first, bounded read; an oversize file is now skipped by the retry instead
of being decoded.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(extraction): check an oversize file against its size stamp everywhere (#1910)
Review on #1915:
- The viewer (readFileShape, hasDriftedOnDisk, the source endpoint) and
MCP's drift gate hashed a file's real bytes, but the index stores the
size stamp for a file over the limit, so every unchanged 1-8 MB file read
as "changed on disk". oversizeStamp moves to file-limits.ts with an
indexedHashInput helper, and all four checks hash what the index stored.
An oversize file is now compared without being read.
- git-index-currency's "keeps a committed path pending when sync cannot
read it" injected its failure only into readFileSync, which the bounded
reader no longer calls, so it failed. The failure now reaches openSync
too.
- The changelog entry drops its benchmark figures.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(mcp): check an oversize answer file against its size stamp (#1910)
#2043's answer-freshness check hashes each contributing file whole and
compares it with the stored contentHash. For a file over the index's size
limit the stored hash is the size stamp, so an unchanged oversize file
would read as stale. Compare it by its stamp, without reading it, like
the viewer and the MCP drift gate do.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* perf(extraction): judge a .ts clip from the bytes it is read for, not at discovery (#1910)
Discovery sniffed the head of every `.ts` file on every full scan — an
open + 3 KB read + close per file, before anything asked for its content.
On a 10k-file TypeScript tree that took a warm-cache scan from ~105 ms to
~243 ms; on a slow disk or a network share it is a random read per file
per scan, the access pattern #1231 and the SMB reports (#1014, #448) are
about.
The bytes are now judged where they are read anyway, so the check costs no
I/O and runs only for a file that is new or changed: the batch reader (as
before), `indexFile`, the sync reconcile (an untracked clip is skipped, a
tracked file that became one is removed through the same removal path as
a deletion), and both `getChangedFiles` paths. The scan lists `.ts` files
by name again; a test pins that discovery opens none of them on the walk
or the git path. Scan time is back to main's (~98 ms), and a full index is
unchanged within noise.
Also collapses the four #1910 changelog bullets (#1914 and #1915 each added
both) to two, without the benchmark figures.
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>
Co-authored-by: danusha2345 <ewidusoc498@gmail.com>
The in-order commit window waits for the commit cursor to advance before
dispatching more parses. After a store fails, flushOrdered returns at once and
the cursor never moves, so the backpressure loop spun on microtasks forever:
indexAll never settled, a core stayed pinned, and no timer in the process
could fire. It reproduces whenever a store error (a closed database, disk
full, an I/O error) arrives with more files left than the window holds.
In the test suite, which runs from source with a window of one, a test that
timed out mid-index and closed its graph in afterEach wedged the whole worker,
so a loaded Windows run never produced a report.
Stop waiting once a store has failed or the run was aborted, and destroy the
parse pool on that error path so its worker threads do not outlive the index.
The regression test runs the scenario in a child process under a hard timeout.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* 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#1838Fixes#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>
* 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>
* 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>
* 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>
Same-kind, same-name declarations on one line shared an ID and silently overwrote each other in SQLite.
Use per-extraction collision tracking to append a UTF-16 column only to later distinct declarations.
Apply the same rule across wasm, Rust language walkers, Liquid, and CFML while preserving file IDs and edge endpoints.
Add persistence, call-edge, identity-vector, and Unicode parity regressions.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* fix(scala): resolve extends/with to the trait, not its companion object
The Scala trait + companion object idiom puts two same-named symbols in
one file. Both were indexed as kind 'class', so an 'extends X' reference
tied between the trait and its companion and the winner was arbitrary.
When the companion won, every subtype was detached from the trait's
inheritance chain, and impact analysis on a widely-used trait stopped at
depth 1.
- classify Scala object_definition as 'module' (a singleton value, not a
type - 'extends' can never target it)
- add 'module' to the classifyClassNode contract and handle it in the
extractor dispatch
- in name matching, bias extends/implements references toward actual
type definitions and penalize 'module' candidates, so the trait wins
the tie deterministically
Regression test: trait + companion in one file, subclass in another -
the extends edge must land on the trait and impact must reach the
subtype through it.
* fix(scala): resolve companion inheritance to type definitions (#1824)
Scala objects and their companion types could tie during inheritance resolution, disconnecting subtype impact paths.
Classify objects as modules in both extraction engines and reject Scala singleton inheritance targets across resolution strategies.
Preserve object method lookup and inherited receiver methods, with declaration-order, impact, and native/WASM parity regressions.
Co-authored-by: Hubert Tarnacki <hubert.tarnacki@comarch.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Hubert Tarnacki <hubert.tarnacki@comarch.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* fix(resolution): resolve this.#field.method() on the field's type (#1987)
A call through an ES private field fell outside the `this.<field>` shape
(#1496): the extractor accepted only a property_identifier, so
`this.#items.add(x)` was emitted as the bare `add` and exact-matched
whichever project method shared the name, often the calling method itself.
The extractor and its kernel mirror now keep `this.#items.add`, and the
resolver matches that shape and reads the field's type off the class
declaration. The field regexes open with a lookbehind instead of `\b`,
which never matches before `#`. A builtin or external field type
(`new Set()`, a `Map`, an array) now yields no edge.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(resolution): require receiver evidence for built-in method calls (#1987)
Unknown JavaScript receivers could match unrelated methods by name or class-name similarity.
Require validated receiver evidence for built-in method names while retaining typed, imported, object-literal and class resolution.
Preserve private-field receivers in TypeScript and Rust extraction and keep public/private field declarations distinct.
Build on PR #1990 with regression coverage across all four JS/TS variants and kernel/WASM parity.
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>
The orphan sweep discarded resolver statistics, so unchanged files caused the CLI to report a no-op after recovery.
Carry processed, resolved, and unresolved sweep counts into SyncResult and report recovery without changing quiet mode.
Cover successful recovery, unresolved attempts, and genuine no-ops with real SQLite regression tests.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
MSVC COM interface declarations were parsed as functions, losing their owners and methods.
Normalize declaration-position interface keywords to padded struct keywords when alias or declaration evidence is present.
Preserve source offsets and protected text through the shared native/WASM preprocessing hook.
Cover real-file indexing, inheritance, source retrieval, and parser parity.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Rust command attributes were not retained as decorators, so runtime entry points could appear as dead code.
Preserve tauri::command metadata in both native and fallback extraction and reuse the existing decorated exclusion.
Add SQLite regression coverage and native/fallback parity checks, including negative cases.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Single-argument function macros were indexed under parentheses in C or the macro name in C++, breaking caller resolution.
Recover both parser shapes only when a preceding local macro definition establishes the function name, and mirror the recovery in the Rust kernel.
Preserve existing multi-argument recovery and ordinary declarations; add extraction, resolution, explore-flow, and parity regressions.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
DFM/FMX component ranges stayed pinned to their declaration lines because closing blocks only popped IDs.
Store nodes in the component stack and update their ending positions at the matching end.
Cover persisted ranges and MCP source retrieval while preserving containment and event references.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Workspace imports could follow excluded dependency archives and decode them without a size guard.
Reuse extraction's 1 MiB limit, reject non-files before reading, and cache null results.
Add sparse-HAR and real ohpm indexing coverage while preserving legitimate import edges.
Co-authored-by: danusha2345 <ewidusoc498@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* feat(extraction): index Python docstrings, not just preceding comments (#1905)
getPrecedingDocstring only walks preceding comment siblings, so a Python
docstring — a bare string literal first in the body — never reached the
docstring column, never entered nodes_fts, and was never shown by
`codegraph node`. The identical sentence written as a leading `#` comment was
both. For a Python codebase that is most of the prose there is. (#1905)
Adds an optional getBodyDocstring() to LanguageExtractor, in the same shape as
getSignature(), and routes every docstring call site through one docstringFor()
helper that consults both sources. A node carrying a comment AND a docstring
keeps both, joined: they are two things the author wrote about the same symbol
and the column is free text.
The Python implementation reads the grammar`s string_content rather than
slicing quotes off the raw text, so r/u/b prefixes and both triple-quote forms
work without a regex per case; f-strings are skipped because an interpolated
string is code, not prose. Dedent follows PEP 257 (first line exempt).
Scoped to Python deliberately. Julia (a string sibling before the def) and
Elixir (@doc) fit the same hook and are left as follow-ups.
* fix(extraction): index Python body docstrings across backends (#1905)
Python extraction only consulted preceding comments, leaving body docstrings absent from search and rendered prose.
Extend the contributor's hook to module and definition docstrings, and mirror it in the native kernel.
Handle comments, concatenated literals, and indentation consistently while rejecting bytes, f-strings, and tuples.
Verify persistence, exact-name ranking, MCP/CLI rendering, and native/WASM parity.
Co-authored-by: Max Hsu <maxmilian@gmail.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Max Hsu <maxmilian@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
The vendored grammar (master@0aca5d0a6f, in both the wasm and the kernel's C
sources) parsed `class A(x: X)(implicit y: Y) extends B(x)(y) with C with D`
with an ERROR and cut the extends clause after `B(x)(y)`, so `C` and `D` never
became inheritance edges. Both copies now come from the v0.26.2 release: the
wasm is the release asset, parser.c/scanner.c are the tag's generated sources.
The grammar change is additive over the old pin: no node type or field was
removed.
On two real repos the share of Scala files that fail to parse drops to zero
(lila 85/1565, playframework 83/846), with no file newly failing. Edges from
those files were built from error-recovered trees; playframework, for
example, nested top-level classes inside their neighbours and drew an
`extends` self-loop.
The kernel defer pins used inputs that the new grammar reads cleanly; they
now use inputs that still fail, and the old inputs pin that both paths
extract them in parity.
(cherry picked from commit 199bc8d8a0)
Co-authored-by: danusha2345 <ewidusoc498@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* fix(extraction): name a function bound through a curried wrapper (#1747)
reactHookBoundName already names an anonymous function after the
variable_declarator that binds it. The shape is general, but the method
is bounded to three React hooks, so `const run =
Effect.fn("Session.run")(function* () {…})` produced no function node at
all -- and its body's calls were attributed to the enclosing container,
so a file gained an outgoing edge belonging to a function and the
callee's caller list named the file instead.
Bound the new path by a different, equally decidable test: the callee is
itself a call, i.e. a factory returning the wrapper. That admits
Effect.fn(...)(fn), connect(mapState)(fn) and a project's own
wrap("n")(fn), and excludes the single-call forms whose argument is a
computation -- useMemo(() => …, []), arr.map(…) -- which stay anonymous
exactly as before. Widening to "any callee" would have given useMemo a
function node and reddened react-hook-handlers.test.ts.
Generators are admitted here and not in reactHookBoundName: a React
handler is never a generator, while function* is the common form in the
ecosystem this shape comes from, and it is what the report opens with --
broadening the callee test alone would not have fixed that repro.
Mirrored in the Rust kernel; kernel-parity.mjs reports byte-parity on
the new shape.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WgtWMywGTyu6twnk1Bu9nP
(cherry picked from commit 4f2b40080d)
* fix(extraction): name a curried-wrapper function that is an object member (#1747)
The curried-wrapper bound stopped at a variable_declarator parent, so
`return { getMode: Effect.fn("ACP.Session.getMode")(function* () {…}) }`
still produced no function node. An Effect service is usually an object a
factory returns, and on sst/opencode (df23b7f) these member positions were
the bulk of what the branch missed.
The member already has a name in the source: its property key. This repo
names `key: () => {}` pairs by that key (extractObjectLiteralFunctions /
objectKeyName), so a `pair` parent is now named the same way. The
callee-is-a-call rule is unchanged, so `key: useMemo(() => …, [])` and
`key: arr.map(…)` stay anonymous.
extractObjectLiteralFunctions also reaches such a member, because an
exported object with an inline function member is extracted member by member
and its initializer is never walked.
A wrapper passed as an argument to another call has no binding at all and is
left as it was.
Mirrored in the Rust kernel; kernel-parity.mjs reports byte-parity on the
new shapes.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Dfb3aazPY1ccmp5vam4k3L
(cherry picked from commit bbdef20542)
* chore: drop CHANGELOG entry (collected separately)
---------
Co-authored-by: Max Hsu <maxmilian@gmail.com>
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
`extension_type_declaration` appears in none of the three places Dart's other
type-like declarations do. `extension_declaration` — the older `extension` — is
in all three, and the two names are near neighbours.
That is why #1780's `isInsideClassLikeNode()` gate dropped these members and no
others: the gate asks whether a class-like node is on the stack, and an
`extension type` never put one there, while `extension`, `mixin` and `class`
all did. The gate surfaced the omission rather than causing it. Before it, the
members were still reached — but as top-level `function:km` rather than
`method:MetersT::km`, indexed and attributed to nothing.
The kernel omits the same node type, so it walks the members through its own
fallback and mints them as top-level functions with no `MetersT` to belong to.
`kernel-dart-parity.test.ts` fails four cases on `main` because of it; fixing
only the wasm side would leave those red.
Listing it alongside the others is the whole fix — `extraClassNodeTypes` and
`dartEnclosingTypeName` on the wasm side, the two `matches!` arms on the
kernel's. Both paths now give:
class:MetersT · method:MetersT::km · method:MetersT::report
with `report`'s span running to its closing brace rather than stopping at the
signature line, and an ordinary class untouched.
Two bugs in the MyBatis mapper extractor:
1. A close tag with trailing whitespace (`</select >`, legal XML) was not
matched by the statement regex, causing the non-greedy body to overshoot
to the next close tag — silently swallowing the statement in between.
Fix: add `\s*` before `>` in the close-tag pattern.
2. A fully-qualified `<include refid="com.example.M.base">` had every dot
replaced with `::`, producing `com::example::M::base` — which never
matched the fragment node's `qualifiedName` (`com.example.M::base`).
Fix: split on the last dot only, matching `qualifyStatement`'s logic.
Also fixes the include-offset calculation to derive from the opening tag
length rather than subtracting the close tag, so it stays correct when the
close tag carries trailing whitespace.
Closes#1209
* fix: index Java packages named build (#1642)
* fix(extraction): preserve Java packages named build (#1642)
The default build-directory exclusion hid legal Java package paths before extraction.
Retain the contributor's main/test Java source-root exceptions, restricted to directories so other default exclusions survive.
Cover filesystem and git enumeration, explicit ignores, indexed retrieval, and incremental sync.
Co-authored-by: danusha2345 <ewidusoc498@gmail.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(extraction): preserve Java packages named build (#1642)
The default build-directory exclusion hid legal Java package paths before extraction.
Retain the contributor's main/test Java source-root exceptions, restricted to directories so other default exclusions survive.
Cover filesystem and git enumeration, explicit ignores, indexed retrieval, and incremental sync.
Co-authored-by: danusha2345 <ewidusoc498@gmail.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>
Fixes#1861. Reuses L4XB's receiver retention and owner resolution from #1866, with duplicate-owner and edit/sync guards.
Co-authored-by: L4XB <lukas.buck@e-mail.de>
Preserve the indexed-commit approach and regression cases from #1848, with dirty-path retention, commit-race guards, NUL Git paths, and failed-I/O recovery.
Fixes#1829.
Co-authored-by: Eric Minish <eric.minish@gmail.com>
Land upstream PR #1687 by danusha2345 (fix commit 6e9bbb26), using the
PR tip implementation with only a Rust doc-comment placement cleanup.
Read parameter lists and return types positionally in the wasm extractor
and native kernel in lockstep, preserving verbatim signature text.
Verified on Linux x64 with Node 22.19.0: reproduced three undefined
signatures in both backends before the fix, then confirmed all three
expected signatures and exact wasm/kernel parity after rebuilding
TypeScript and the linux-x64 kernel. All 31 focused tests pass: 15 Kotlin
extraction, 6 Kotlin parity, and 10 kernel scaffold checks, with
CODEGRAPH_KERNEL_EXPECT=1 for the native suites.
Add the upstream #1495 changelog bullet while preserving all other
Unreleased entries. Keep EXTRACTION_VERSION unchanged for this bug fix.
Co-authored-by: Colby McHenry <colbymchenry@users.noreply.github.com>
A project CodeGraph has no grammar for was indistinguishable from an empty one: unsupported extensions are filtered out at discovery, so filesDiscovered was 0, the reconciliation in index.ts found no shortfall and recorded index_state as complete, and the CLI printed the same 'No files found to index' it prints for an empty repo.
That silence is what makes it costly over MCP: an empty result reads identically to 'no match', and the agent has been told to trust the graph rather than grep.
The scan already visits every file, so the tally of what it declined to index costs no extra I/O and no second pass. index_state itself is left alone: changing its values would change the status --json contract, which is a call for the maintainer to make.
(cherry picked from commit 2c20892789)
Co-authored-by: Max Hsu <maxmilian@gmail.com>
Co-authored-by: netbrah <netbrah@users.noreply.github.com>
Squash danusha2345's PR #1511 at d282f9e8 onto main 8c9c4761,
preserving its nine non-merge commits and main's existing Unreleased notes.
Calls in Kotlin, Java, TS/JS, Scala, Rust and Python declaration initializers
now retain the owner established by the upstream regression expectations.
Include the upstream CFML, dynamic-dispatch summary and viewer follow-ups.
Linux fail-to-pass validation (Node 22.19.0, rebuilt dist and native kernel):
- Before: TS load belonged to file:app.ts; Python/Kotlin/Scala/Rust calls
vanished; Java lost the field-lambda, anonymous override and eager calls.
- After: all six languages PASS; 12 native/WASM LF/CRLF parity checks PASS.
- Focused initializer regressions: 10 passed with CODEGRAPH_KERNEL=0 and
10 passed with the kernel enabled; Kotlin's grammar fallback is recorded.
- Related regression suites: 879 passed, 1 skipped across 15 test files.
- Evidence: /workspace/cg-1510-repro/before and /workspace/cg-1510-repro/after
(combined test output: after/vitest.log).
Fixes#1510
Supersedes #1511
Co-authored-by: Colby McHenry <colbymchenry@users.noreply.github.com>
Co-authored-by: danusha2345 <ewidusoc498@gmail.com>
Land upstream PR #1514 for issue #1513 by cherry-picking
ctype_lab's a94c9dc94e.
Keep allowBodilessStruct as a Rust-only opt-in, with matching behavior in
the wasm/TypeScript walker and native kernel. Create the node before
checking for a body and walk members only when present. Resolve against
main's shared struct/union walker while retaining its stack guard, kinds,
fields, and existing impl-receiver fixes.
Verified FAIL to PASS on Linux x64 with Node 22.19.0 after rebuilding via
tsc, copy-assets, and build:kernel. The identical fixture on main 7b339373
had two structs and two implements edges; fresh wasm (CODEGRAPH_KERNEL=0)
and native indexes now have three of each. UnitStruct and its Greet
implements edge are recovered; tuple and brace structs remain intact.
The native run loaded the rebuilt kernel and completed without fallback.
Focused extraction.test.ts and kernel-rustlang-parity.test.ts runs:
645 tests passed with the kernel disabled, and 645 with it enabled;
all three parity tests ran in each configuration, with no skips.
(cherry picked from commit a94c9dc94e)
Co-authored-by: ctype_lab <cksgud1226@gmail.com>
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Land upstream PR #1604 by @maxmilian from commit
27c149524d on current main cece0720.
Fixes#1549.
Use the upstream lsFilesStaged helper at both collection and embedded-repo
discovery call sites. Retry without --recurse-submodules when staged
recursive listing fails, retaining the mode bits needed for gitlinks.
Keep the upstream regression test byte-for-byte and preserve main's
existing extraction/watcher changes, including #1728. Resolve the
changelog conflict with a concise Unreleased entry, and keep the existing
collectGitFiles documentation attached to its function.
Verified on Linux x86_64 with Node 22.19.0 and Git 2.47.3:
- The unchanged upstream test fails against main: expected [ 'a.ts' ]
to include 'dir_b/b.ts'; it passes with the fix.
- The same persistent fixture under a PATH shim rejecting -s with
--recurse-submodules (exit 128) changes scanDirectory from [a.ts]
to [a.ts, dir_b/b.ts], and gitlink watcher roots from [] to [lib/].
- Real-Git controls return both files and the watcher root in both arms.
- 69 related scope/config tests and 23 extraction scanning tests pass.
- npx tsc --noEmit passes.
Co-authored-by: Colby McHenry <colbymchenry@users.noreply.github.com>
Co-authored-by: Max Hsu <maxmilian@gmail.com>
Co-authored-by: newshowardz777 <newshowardz777@users.noreply.github.com>
Land the six-file fix from upstream PR #1691 by danusha2345
(pr-1691 at 6d0e80d52a), preserving
wasm/native extraction parity and exclusive field-type resolution.
Preserve coexistence with the #1566 Map/collection fix merged in #1790,
including nested holder.values.get coverage and the unchanged #1566
Unreleased changelog bullet. EXTRACTION_VERSION remains unchanged.
Align the existing chained-receiver regression with the fix: a declared
service field calls its method, while an anonymous field type does not
bind to unrelated same-named project functions.
Verified on Linux with Node 22.19.0:
- Rebuilt the native kernel and TypeScript/browser distribution.
- Both backends change Outbox::send -> Outbox::send into
Outbox::send -> Mailer::send, keep Relay::forward -> Mailer::send,
and store no self-edges in the issue repro.
- Wasm: 224 tests passed; native kernel: 255 tests passed, no skips.
- All 10 #1566 resolution cases pass on each backend, plus all four
nested-receiver extraction parity cases.
Co-authored-by: Colby McHenry <colbymchenry@users.noreply.github.com>
Land upstream PR #1634 by @maxmilian, commit
8d398a92e3.
Path-only detection classifies .h files as c, so preloading previously
covered c and cpp but missed objc selected by content-aware detection.
Preload objc alongside cpp whenever c is present. Full indexing and
changed-file reindexing now share preloadLanguagesForFiles().
Retain all four upstream unit tests and place the existing #1628 changelog
entry under Unreleased / Fixes / Symbols, tests and the viewer.
Verified fail -> pass on Linux with the reporter's repro.h as the only
source file, without a .m or .mm grammar seed. Before: Objective-C parser
initialization failure, 0 nodes, index state failed. After: Indexed 1 files,
2 nodes, 1 edge, index state complete; node CGRepro finds the class and
errors.log is absent.
Validation: npx tsc && npm run copy-assets passed; rebuilt CLI is executable.
npx vitest run __tests__/preload-languages.test.ts: 4 tests passed.
Co-authored-by: Colby McHenry <colbymchenry@users.noreply.github.com>
Co-authored-by: Max Hsu <maxmilian@gmail.com>
Land upstream #1686 (maxmilian + bompus kernel/CG-28 follow-ups)
onto current main. tree-sitter-typescript interface members
(method_signature / property_signature) were never listed in the
TS extractor, so platform .d.ts APIs had no declaration nodes for
call edges. Mirrors on the Rust kernel path; keeps CG-28 damping
for pure-interface declaration files; filters damped files from
the explore RWR seed set.
Co-authored-by: Colby McHenry <colbymchenry@users.noreply.github.com>
Apply upstream PR #1623 by danusha2345 (fix commit aa777063),
which also addresses #1616, to the current main base. Preserve the
upstream WASM and Rust implementations and regression coverage.
Index assigned locals, table members, static string keys and nested
callback tables as callable symbols, with calls owned by those symbols.
Keep dynamic keys unguessed. Add #1650 to the Unreleased changelog and
retain the existing re-index guidance without an extraction-version bump.
Verified on Linux x64 with Node 22.19.0:
- native kernel build, tsc, asset copy, executable CLI
- issue repro: 3 nodes / 2 edges -> 4 nodes / 4 edges
- EPR.PowerController::SyncHydroPower is indexed; its caller is client.lua
- syncHydroPower depth-2 impact reaches client.lua
- extraction/resolution/Lua parity: 844 tests passed (kernel expected)
- forced-WASM Lua/Luau extraction/resolution: 20 tests passed
Co-authored-by: Colby McHenry <colbymchenry@users.noreply.github.com>
`const handleClear = () => {…}` inside a component — every React handler
that skips useCallback — was never a symbol: the body walker only named
nested function declarations and hook-bound arrows, so the handler was
absent from callers/impact ("Symbol not found", indistinguishable from
"no callers") and its calls attributed to the component. Bind the arrow
or function expression to its declarator the way module scope already
does, in both the wasm walker and the kernel.
A navigation such a handler makes is now the handler's own edge and a hop
in the Screens `via` chain — the shape a useCallback handler already has —
so the react-router and expo-router expectations follow that convention.
Co-authored-by: danusha2345 <ewidusoc498@gmail.com>
Co-authored-by: Colby McHenry <colbymchenry@users.noreply.github.com>
`exports.getItems = async (req, res) => {…}` and `module.exports.x =
function () {…}` — the Express controller style — produced no symbol: the
arrow's parent is an assignment, not a declarator, so it stayed anonymous,
its calls attributed to the file, and `node`/`callers` answered "Symbol
not found" for a route-wired handler. Resolve the name from the export
property, mark it exported, in both the wasm walker and the kernel.
Co-authored-by: danusha2345 <ewidusoc498@gmail.com>
Co-authored-by: Colby McHenry <colbymchenry@users.noreply.github.com>
`chrome.storage.local.get(key)` and `document.body.querySelector(s)` end in
a platform API, but the extractor emitted the bare method name for them. That
name then exact-matched whatever project symbol shared it: in a Chrome
extension every `chrome.storage.local.get/set` inside a storage wrapper bound
to the wrapper's own `get`/`set`, giving self-edges that are not in the
source (#1707).
A member chain whose root identifier is a host object the project never
declares now emits nothing — a silent miss instead of a wrong edge, the same
trade the literal-receiver gate makes (#1230). `window` is deliberately not a
host root: `window.MyNs.doThing()` reaches a project symbol. A chain rooted at
a project value keeps the bare name, so `store.getState().act()`, `ref.value
.m()` and `this.<field>.m()` are untouched.
The Rust kernel mirrors the same gate. Verified on Linux: fail→pass on both
kernel and wasm arms for `__tests__/ts-chained-receiver.test.ts` (2 fail / 1
pass on main → 3/3 with the fix).
Lands / rebases https://github.com/colbymchenry/codegraph/pull/1710 onto
current main.
Co-authored-by: Colby McHenry <colbymchenry@users.noreply.github.com>
Co-authored-by: Aaron Queen <bompus@users.noreply.github.com>
Pure-virtual declarations (`virtual int read(int key) = 0;`) parse as
field_declaration, not function_definition, so they minted no method node —
calls through an abstract base and cpp-override synthesis had nothing to
attach to. Mirror Java interface methods: mint the node (TS + kernel), mark
isAbstract, and cover with extraction/e2e/parity fixtures.
Co-authored-by: Colby McHenry <colbymchenry@users.noreply.github.com>
tree-sitter-c has no rule for `.field = value` as a call argument. A
statement-level `MACRO(a, b, .x = …, .y = { … },);` recovers by extending
the enclosing function_definition to EOF — later functions vanish or nest
as outer::inner (#1729). blankCDesignatedMacroArgs empties such argument
lists to spaces (newlines kept) at the head of preParseCSource, before the
kernel route point, so both wasm and kernel C arms see the same bytes.
Tests cover the issue fixture (trailing-comma designated args) and a
120-field scale guard. Refs #1729.
Co-authored-by: Colby McHenry <colbymchenry@users.noreply.github.com>
buildDefaultIgnore now reads .git/info/exclude and core.excludesFile; buildScopeIgnore also seeds directories git ls-files reports as ignored-untracked so nested .gitignore effects prune the live watcher. Defect B (full-project sync per event) was already fixed via scoped pendingFiles sync.
Co-authored-by: Colby McHenry <colbymchenry@users.noreply.github.com>
sync() armed the WAL valve but never called backpressure(), so daemon
catch-up could grow the WAL without bound while query-pool readers pinned
frames. Wire the writer pause into sync store + batched resolution, and
abort with WalValveAbortError after parked backfills fail past the
documented hard/file caps instead of disabling parking for 60s.
Co-authored-by: Colby McHenry <colbymchenry@users.noreply.github.com>
Could not reproduce the reported nested-gitignore node_modules blowup on
Linux against main (or with the real Boba-Base ignore files): both the git
ls-files path and scanDirectoryWalk already exclude via DEFAULT_IGNORE and
per-directory .gitignore. Add CODEGRAPH_DEBUG logging when the git listing
falls back, plus regression tests for the reporter's layout on both scan
paths so a future regression fails loudly.
Co-authored-by: Colby McHenry <colbymchenry@users.noreply.github.com>
A member call whose receiver is itself a call — `d.setdefault(k, []).append(v)`,
`make().run()` — used to drop the receiver at extraction time, degrade to the
bare method name, and exact-match any top-level project symbol of that name
(Python and JavaScript/TypeScript). Keep the inner callee encoded as
`<inner>().<method>` in the TS extractor and native kernel; the name-matcher
refuses to guess for that shape (store-accessor exception only). Based on
#1692, rebased onto main after #1746. Fixes#1683.
Co-authored-by: Colby McHenry <colbymchenry@users.noreply.github.com>
Tree-sitter kinds generator_function_declaration / generator_function were
missing from both the wasm and native kernel function-type lists, so
function* / async function* (and const g = function* () {}) produced no
nodes. Add the kinds on both paths and cover TS+JS declaration/expression
forms in extraction tests.
Co-authored-by: Colby McHenry <colbymchenry@users.noreply.github.com>