Skip to content

Sync to upstream 10e316d, past v1.6.2, and release as 1.6.2-fmagent.1 - #19

Open
Dshuishui wants to merge 364 commits into
mainfrom
sync-upstream-10e316d
Open

Dshuishui wants to merge 364 commits into
mainfrom
sync-upstream-10e316d

Conversation

@Dshuishui

Copy link
Copy Markdown
Collaborator

Moves the base from e63fe2e to upstream 10e316d (main, 2026-10-19), 363
commits on, none of them merges, and releases as 1.6.2-fmagent.1.

Why main, not v1.6.2

Upstream tagged v1.6.1 (09-29) and v1.6.2 (10-03) since our base, but what this
sync is for landed after v1.6.2:

What happens to each patch

All four merge back without a conflict, and every line they add or remove is the
same as in .6:

  1. fm_agent work-directory exclusion — unchanged.
  2. Wrapper naming where nothing binds the result — unchanged.
  3. Effect-TS service member resolution — unchanged. Upstream grew
    name-matcher.ts by about 15k lines in this span, none of it overlapping.
  4. Generator class fields — unchanged. Upstream's two class-field helpers still
    do not list generator_function, on either extraction path.

Conflicts were only outside them:

  • package.json, ui/package.json, package-lock.json: the version marker.
  • release.yml: the fork keeps its workflow without the npm steps.
  • install.ps1: upstream added Install-CodeGraphFiles, a Windows swap that
    renames files aside and puts them back on failure. Its "reinstall" hint names
    upstream's installer; it now names the fork's, in install.ps1 and in its copy
    WINDOWS_SWAP_FUNCTION in src/upgrade/index.ts (a test keeps the two identical).
    install.sh's hint for Windows shells still names upstream's install.ps1, as in
    .6: installer-targets.test.ts pins that string.

FORK.md and the fork's CHANGELOG entries are updated for the new base.

Verification

Tests (winbox, Ubuntu 24.04, Node 22, locally built kernel,
GIT_CONFIG_GLOBAL=/dev/null):

files tests
upstream 10e316d 600 passed, 1 failed 7,317 passed, 2 failed, 37 skipped
this 603 passed, 2 failed 7,336 passed, 5 failed, 37 skipped

codegraph-benchmark (make check, with 11 new cases in
fmagent-project/codegraph-benchmark — one per extraction bug FM-Agent has hit:
upstream colbymchenry#1211, colbymchenry#1373, colbymchenry#1585, colbymchenry#1588, colbymchenry#1727, colbymchenry#1741, colbymchenry#1747, colbymchenry#1861, and the four
patches here):

build passed
this 15 / 15
1.6.0-fmagent.6 15 / 15
upstream 10e316d, upstream v1.6.2 11 / 15 (the four patch cases fail)
upstream v1.6.0 6 / 15
upstream v1.5.0 4 / 15

What FM-Agent reads, on 12 projects, through FM-Agent's own
CodeGraphExtractor:

project language functions .6 → this call edges .6 / upstream / this
requests Python 268 → 268 249 / 218 / 218
FM-Agent Python 824 → 824 1,166 / 1,152 / 1,152
etcd server/etcdserver Go 1,859 → 1,859 2,920 / 2,885 / 2,885
jsoup Java 2,200 → 2,311 4,200 / 4,913 / 4,913
zstd lib C 2,012 → 2,012 3,563 / 3,564 / 3,564
lua C 1,295 → 1,295 3,097 / 3,097 / 3,097
re2 C++ 1,307 → 1,307 2,789 / 2,879 / 2,879
leveldb C++ 1,750 → 1,806 4,318 / 4,574 / 4,574
ripgrep crates Rust 2,939 → 2,939 5,485 / 4,877 / 4,877
ESLint lib JS 1,970 → 2,062 2,097 / 2,243 / 2,243
excalidraw TS 2,192 → 2,242 3,263 / 3,273 / 3,273
opencode src TS 3,039 → 3,095 3,870 / 3,293 / 3,700

Eleven projects come out the same as upstream; on opencode the patches bring back
407 edges. Reading samples of what changed against .6, by the call site's source
line: the edges that go are mostly calls on library values landing on a
same-named project method (.clone(), .map(), .get(), zap.Error(),
super.x() on itself); the ones that come are mostly real — jsoup's enum
constants now have their own method bodies (+111 functions, the parser's state
machine), and etcd's and leveldb's field and base-class calls resolve.

Not fixed here (upstream, to report)

  • Rust: a method call whose receiver comes from an index, an Option or a
    Box field (errs[0].io_error(), prev.choose(seq)) is no longer resolved.
    About 5 in 25 of the edges ripgrep loses are real ones like these.
  • JavaScript, since v1.6.2: in a class whose file does
    const { parse, matches } = require("./esquery"), this.matches(...) resolves
    to the required function instead of the class's own matches method
    (ESLint lib/linter/source-code-traverser.js). .6 got this right.

Both are recorded for a separate check before reporting.

Release

1.6.2-fmagent.1, as a prerelease first so the build can be checked against
codegraph-benchmark before FM-Agent's pin moves (a separate PR there). Projects
FM-Agent analysed with .6 need one full run before --incremental: the
extraction version goes from 27 to 28, and C++ function names change.

colbymchenry and others added 30 commits September 30, 2026 21:09
…y#2204)

A vendored minified script (`*.min.js`, or a script of a few enormous lines
- the same detection colbymchenry#2122 uses to rank them last) keeps mangled one-letter
names, so a name match from any other file is coincidence: healthchecks'
369 `$(...)` jQuery calls went to a helper inside bootstrap-native.min.js,
TestBox's popper.min.js to jquery.min.js internals, flaskbb's docs script
to its vendors bundle. Such files' symbols are no longer visible across
files by name; calls inside a bundle still resolve.

healthchecks -460, flaskbb -48, TestBox -36; bat, lodash, minimal-mistakes,
netbox, express, excalidraw unchanged.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…mchenry#2205)

The declared-member receiver inference colbymchenry#2171 gave C#, Java and Kotlin now
reads Swift too: `var cache: ImageCache!`, `let storage: DiskStorage.Backend<Data>`,
`self.x` as well as `x`, a function's own `let` shadowing the property. The
dotted-call path types the receiver from it, and a chained bare call keeps
only members of that type or its supertypes / protocols.

Kingfisher: `cache.imageCachedType(forKey:)` -> ImageCache's (was a test
subclass's override), `storage.store/value/isCached` -> DiskStorage.Backend,
`manager.retrieveImage` -> KingfisherManager (was a test double), DispatchQueue
`ioQueue.async` no longer a project method. realm-swift: `List::append`,
`RealmCollection::sum/index` (were Objective-C RLMArray selectors); vapor:
`self.storage.write` -> NIOHTTPBodyWriterStorage.

Kingfisher -100 +179, realm-swift -108 +203, Alamofire -16 +54, vapor -15
+10, IceCubesApp -6 +5; Newtonsoft.Json, okhttp unchanged.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
colbymchenry#2172 made the members of every type a project function type takes as a
receiver reachable from any bare call, since it could not tell which
lambda a call is in. With several such types in reach, proximity chose:
koin's `single { C51(get(), get()) }` and `Scope.new(...)`'s `get()` went to
`Koin.get` (koin also has `Koin.() -> ...` lambdas), 2,190 times.

- A lambda's receiver is read from the function it is passed to: the type
  before `.(` in its last parameter's function type, directly or through a
  typealias (`definition: Definition<T>`, `typealias Definition<T> =
  Scope.(ParametersHolder) -> T`), when every project function of that name
  agrees. The brace frames put it in the lambda's hierarchy.
- Of the members a bare call can reach, those its lexical hierarchy reaches -
  its class, an extension's receiver, the lambda it is in - come before those
  only the project-wide receiver set reaches.

koin: `get()` -> Scope (2,184), `viewModel` in `scope { }` -> ScopeDSL,
`modules(...)` in `startKoin { }` -> KoinApplication. Exposed: `varchar(...)`,
`integer`, `reference`, `datetime` in a table object -> Table's (were a
test table's same-named property). kotlinx.coroutines: `launch { }` ->
CoroutineScope.launch (was a test DSL's). Edge counts ~unchanged (targets
move): koin, Exposed, okhttp, coroutines, okio; nowinandroid, moshi
unchanged.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…colbymchenry#2207)

Dart joins Rust/Go/Kotlin/C# with a standard-method list (String, List,
Iterable, Map, Set: endsWith, contains, forEach, putIfAbsent, where,
sublist...): a guess onto a project method of such a name needs a receiver
named after its owner. And a Dart extension's owner, for that naming, is the
type it is `on` - getx's `ext.endsWith(".avi")` on a String shared the word
`Ext` with `extension RxStringExt on Rx<String>`, none with `Rx<String>`.

getx -36 (`RxStringExt::endsWith` x23, IOHttpResponse::join/toList),
riverpod -37 (`UnmodifiableMap::putIfAbsent/addAll` on plain maps), bloc -18
(`ignore.contains(...)` -> GithubCache, a Map's `forEach` -> _Emitter; bloc's
untyped `emit.forEach(...)` handler lambdas lose their lucky guess too).

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…colbymchenry#2208)

`answers in under 100 ms` took one wall-clock sample while the rest of the
suite ran in parallel, and failed under that load at 250-430 ms (three
times in one day of full-suite runs) while passing alone. It now takes the
fastest of five warm requests: contention is gone from at least one, and a
real slowdown is slow on every one.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…olbymchenry#2209)

A test suite is not linked into the program, so a name match from
production code into one is coincidence - but only Strategy 3's receiver
guess (colbymchenry#2171) ruled it out. Now no strategy's name match reaches a symbol
in a test suite from a non-test file: a test source set (`src/test/`,
`jvmTest/`), a `tests/` / `__tests__/` / `spec/` / `e2e/` directory, a
`FooTest.kt` / `test_foo.py` / `foo.test.ts` / `foo_spec.rb` file.
Test-support code a project ships - `testing/`, `fakes/`, `mocks/`, a
`*-test` module - stays in reach, and `…Spec.java` (halo's `IndexSpecs`,
okhttp's `ConnectionSpec`), `post-spec.ts` models and `useTests.ts` hooks
are not test suites.

typeorm -64 (`Record<K, V>` -> a test entity), okhttp -164 (117 sample
`@Override` -> a test's nested `Override`), kotlinx.coroutines -318
(`import kotlinx.coroutines.*` onto one test file's package node), sttp -62,
Exposed -92, halo 0, element-plus -36 (`isClient` -> a test's vueuse mock),
excalidraw -10 (`actionManager.executeAction` -> the test `API` helper),
tokio -30, trpc -36, AutoMapper -37, netbox -22, cats -21, mealie -10,
pytest -8, Alamofire -7; zod, eShop, react-native-reusables unchanged.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…henry#2210)

Framework resolvers see every reference of every language, and the Vue
resolver had no language guard: its PascalCase-component rule took
mealie's Python `QueryFilterBuilder(...)` (an unresolved import) to the
`QueryFilterBuilder.vue` component at 0.8, and its macro / auto-import rules
matched any language's `ref(...)` or `computed(...)`. It now answers only
refs from vue / javascript / typescript / tsx / jsx files.

mealie: 6 Python calls reroute to the Python class; halo, elk, element-plus
unchanged.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…olbymchenry#2211)

The React resolver's `languages` gates its route extraction, not its
resolve(): framework resolvers see every language's references. Its
component rule was already JSX-only, but its hook (`use...`) and context
(`...Context` / `...Provider`) rules were not: in halo - a Spring app whose
UI package makes React detected - 209 Java import edges (`SecurityContext`,
`ApplicationContext`, `ObjectProvider`, `DirtiesContext`...) were resolved by
it onto import nodes. It now answers only JavaScript / TypeScript / TSX /
JSX references.

halo -209 +3 (a Java test's `responseContext()` helper now resolves to
itself); trpc, excalidraw, react-redux-realworld, bulletproof-react,
react-native-reusables unchanged.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…it (colbymchenry#2212)

A method that delegates to another overload of itself -
`toInstant(instant)` returning `toInstant(instant, Instant.EPOCH)`,
`DeserializeXNode(value)` returning `DeserializeXNode(value, null)` - bound to
itself, since every overload shares the name: the fuller overload never
listed its convenience overloads among its callers, and its impact missed
them.

When a name match lands on the calling method itself, in a language that
overloads by arity (C#, Java, Kotlin, Swift, C++, Scala, Dart, VB.NET), the
call's argument count is read and compared with the method's parameters
(defaults and varargs / `params` / `...` counted); if they cannot take it,
the one same-owner overload they fit is the target. Real recursion
(`depth(n - 1)`) keeps its self-edge.

Self-loops -> overload edges: commons-lang 637 -> 457, Newtonsoft 303 -> 196,
halo 173 -> 116, okhttp 248 -> 208, Alamofire 147 -> 120, gson 74 -> 55,
AutoMapper 71 -> 57 (edge counts unchanged; overloads share a qualified
name, so only the target moves).

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…bymchenry#2213)

colbymchenry#2212 moved a method's call to its own name onto the sibling overload the
arguments fit. The same holds for every caller: a match landing on an
overload whose parameters cannot take the call's arguments moves to the one
same-owner overload they fit - commons-lang's
`HashCodeBuilder.reflectionHashCode(this)` from the three-parameter overload
indexed first to the `(Object, String...)` one, Newtonsoft's
`SetToken(token, value)` from `SetToken(token)`, halo's four-argument
`menuItem(...)` from the one-argument `menuItem`.

Overload sets and arities are read once per target node, and a target with
no same-owner sibling costs one lookup, so indexing time is unchanged
(commons-lang 4.67s vs 4.70s, Newtonsoft 4.04s vs 4.05s, wasm both arms).
C++ keeps only the self-call case: its overload sets (templates, SFINAE
priority tags, `data()` on any container) move targets unreliably.

Targets moved between same-named overloads: commons-lang 2,334, Newtonsoft
697, halo 286, gson 232, okhttp 188, Alamofire 50; nlohmann json and fmt
unchanged. Edge counts unchanged.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…olbymchenry#2214)

The Rust scope rule (colbymchenry#2124) only checked prelude names; every other bare
name could mean any same-named project item, and a name after any path
(`io::Result`) could mean any at all. The Rust framework resolver's
PascalCase / handler / service patterns bound names with no scope check at
0.7-0.8. tokio's 1,221 `io::Result<...>` went to a private
`runtime::task::Result` alias, 642 `Context<'_>` (from `use
std::task::{Context, Poll}`) to `runtime::context::Context`.

- A type or function from another file is in scope only through a `use`
  that binds it (its leaf, `as` alias, or `{self}`) or a glob over its
  module; methods and fields (reached through a value), import refs and
  module / file targets are unaffected.
- A name a file imports from outside the project - `std` / `core` /
  `alloc`, or a crate its Cargo manifests declare as a dependency - is that
  outside item. (An inline `mod support { pub mod panic; }` is the
  project's.)
- A path names its module: `seg::Name` reaches an item of module `seg`;
  a path rooted at std (`std::io::Error`) or at an outside import (`io::`
  under `use std::io`) reaches none; `crate::` / `self::` / `super::` and a
  project crate's name (`clap::Command`, re-exported) look it up as before.
  The path is read anywhere on the line, not only at the reference's column.
- The framework resolver's patterns apply the same check.

tokio -3008 +610, serde -792 +650, ripgrep -602 +247, axum -453 +186, clap
-342 +32, bat -38 +9. Sampled losses: std / core / futures / syn / http /
anyhow imports and paths, associated types (`S::Ok`, `S::Error`, `V::Value`,
`Fut::Output`); gains are same-module items the old first match hid.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…enry#2216)

A bare C# type name could mean any same-named project type. Newtonsoft's
`async Task` tests (`using System.Threading.Tasks;`) bound `Task` to a test
class `Task` in `Newtonsoft.Json.Tests.Schema` 433 times; AutoMapper's DTOs
in `OmmitedDTOModel3` extended `OmmitedDatabaseModel3`'s `BaseEntity`;
Newtonsoft's `BindingFlags` went to its portable-build polyfill.

A type in namespace N is now in reach of a file whose code runs in N or a
namespace inside it, or that imports N: its `using N;`, any file's
`global using N;`, the project files' `<Using Include="N" />`, the SDK's
implicit usings when `<ImplicitUsings>` is on (serilog's own
`System.TimeProvider` polyfill is seen through them), or an alias naming
the type (`using License = AutoMapper.Licensing.License;`). A type in the
global namespace is seen everywhere; qualified names are not judged.

Newtonsoft -529, AutoMapper -72, eShop -4; serilog, CleanArchitecture
unchanged.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…olbymchenry#2217)

A kind-projector placeholder (`Align[Either[A, *]]`, `Functor[Map[K, ?]]`)
reached the resolver as a type reference named `*`, and exact or fuzzy
matching bound it to the one `*` in the project - algebra's
`Signed.Sign.*` operator - 567 times in cats. A symbolic name in a Scala
type now resolves only to a type or type alias of that name (cats'
`F ~> G`), and otherwise to nothing, whatever strategy runs.

cats -567; sttp unchanged.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…nly its owner names (colbymchenry#2218)

A lone project method now stands for an untyped receiver's member value only
when the receiver is named after the method's class: netbox's
`device=self.parent.device` model-field reads no longer reach a GraphQL
filter's `device` method.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…t is written (colbymchenry#2219)

A function nested in another function is reachable only from inside it; a
Python name the enclosing function binds is that local's value; a pytest
fixture is a test's parameter only in its own module or under its conftest.
Unreachable same-named functions still count against a lone cross-file guess.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…s scopes (colbymchenry#2220)

A block namespace qualifies only its own body (a nested one is Outer.Inner),
mirrored in the kernel; a using names the project's namespace, never another
file's using; a global using is its own project's; a nested type is reached
by bare name only from inside its owner or a type deriving from it, through
any partial part; visible C# types are filtered before ranking.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…t calling itself (colbymchenry#2221)

A TS/JS call written through this.<field>… or window.<x>… reaches the resolver
by its bare name; it resolves to the calling method only when the field is
declared as the caller's own class. A name-scored receiver guess never lands
on the calling method.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…package (colbymchenry#2222)

The Spring resolver's entity/service/controller name heuristics took the first
same-named class under a conventional folder; they now prefer the ref's own
file, then its package, and never reach another file's nested class by its
bare name.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…s not the caller (colbymchenry#2223)

Generalizes the collapsed-chain self veto beyond TS/JS this./window. roots: a
call the extractor recorded by its bare name but the source writes as a member
call on anything other than this/self/super is never the calling method; and a
value's initializer never calls the value itself.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
colbymchenry#2224)

The Rust resolver's handler/service/struct heuristics took the first
same-named item and then checked scope; they now drop items declared inside
a function body and out-of-scope names before choosing, and take the file's
own item first.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…, own scope first (colbymchenry#2225)

One shared pick behind the Django/FastAPI/Flask, ASP.NET, Gin, SwiftUI/Vapor,
Spring and Rust name heuristics: never a function-local declaration, a type
nested in another file's type, or a node the language keeps out of the
reference's file; the reference's own file first, then its directory (not
Rust), then a conventional folder.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…#2226)

A JS/TS parameter or plain var/let/const above the reference shadows a
same-named function declared elsewhere in the file, whichever strategy found
it; destructured call results, member calls and followed aliases are left
alone.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…wn (colbymchenry#2227)

isVisibleAcrossFiles rejects a declaration in a .svelte instance <script> or a
Vue <script setup> from another file; a Svelte <script module>, a plain Vue
<script>, and a type a <script setup> exports stay reachable.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…ns (colbymchenry#2228)

A receiver that is the test's own parameter takes the type its in-reach
fixture returns (`return Cls(…)` or a local assigned `Cls(…)`), so calls on
it resolve on that class, or on nothing when it is a library's; fixtures a
conftest.py star-imports or lists in pytest_plugins are in reach.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
An extends/implements ref that settled on the declaring type is re-chosen
among the other types of its name, the one the written qualifier leads to
(owner path plus file package), or none.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
… or by import (colbymchenry#2230)

isScalaPackageObjectMemberVisible: a member of `package object X` resolves
from its package and those under it, or a file that imports through it;
applied before ranking (so the visible namesake wins) and as a veto.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…bymchenry#2231)

In isRustNameInScope, a type reference written `Self::Name` is in scope only
as a type alias declared in the enclosing impl/trait block above it, and one
written through a generic type parameter (`V::Value`) names no project item.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…ember (colbymchenry#2232)

In a .kts file, a bare call reaches a method/field/property only when its
owner is no class the project declares (build logic's extensions on Gradle's
types); the old rule let every member through.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
… symbol (colbymchenry#2233)

isVisibleAcrossFiles (before the same-file shortcut) rejects every Go
candidate for a reference its line writes as `pkg.Name` through an import
from outside the module (stdlib or third-party).

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
matchReference refuses a method target for a calls ref from a C file: C has
no methods and cannot call a C++ one.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
colbymchenry and others added 28 commits October 15, 2026 04:21
… global type (colbymchenry#2568)

protobuf's hpb declares `class Arena { … backend::Arena arena_; }`, where
`namespace backend = hpb::internal::backend::upb;` and that namespace says
`using Arena = ::upb::Arena;`. cppTypeName dropped the leading `::`, so the
alias's target was looked up as `upb::Arena` from the alias's own scope,
whose nearest `upb` is that very namespace: the alias named itself, the
lookup ended on no class, and `arena_.Fuse(other.arena_)` fell back to the
type's last name, linking `hpb::Arena::Fuse` (and `IsFused`, `RefArena`) to
itself instead of `upb::Arena::Fuse`.

TypeName now records a leading `::` (`global`), and a global name is looked
up from the global scope alone: when an alias's target is followed (follow,
and the scope expand looks the target up from and hands on), in a class's
base clause (baseClassesOf), and in cppAliasedTypeWritten, which colbymchenry#2532's
`auto` locals and chains walk alias by alias.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…there, and a far lowercase outside type reaches no namesake (colbymchenry#2569)

The two follow-ups colbymchenry#2567 and colbymchenry#2568 left open.

A receiver declared with a leading `::` lost it: the declarator regex
started a type at a letter, so `::upb::Arena& a` inside `namespace hpb`
was looked up from `hpb` and found a namespace-local `hpb::upb::Arena`.
The declared type now keeps a leading `::` where one can stand (not after
a name, a template's arguments or a `)`, so `SkipList<Key,
Comparator>::Iterator iter` is not read as `::Iterator`), and
resolveCppAliasedType no longer takes a function-local `using Foo = Bar;`
for `::Foo`.

A lowercase outside type declared outside the calling function still used
the last-name typed match: colbymchenry#2413's library rule skips such declarations,
and colbymchenry#2567's namesake gate took capitalized types only. So a far
`absl::flat_hash_set<int> seen` linked `seen.insert(v)` to the project's
`util::flat_hash_set::insert`, and a far `std::string text` linked
`text.clear()` to a global project `string` class. The gate now covers
lowercase types, which are decided as when no class shares their name,
and cppFindsOnlyNamesakes reads a `std::` name exactly.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… SkipList<Key, Comparator>::Iterator, is that template's member (colbymchenry#2573)

The declarator regex read a type as a name plus one template argument
list, so `SkipList<Key, Comparator>::Iterator iter(&list);` was read from
its last name alone and looked up from the calling function: leveldb's
skip-list tests linked `iter.Valid()` to `leveldb::Iterator::Valid`,
rocksdb's to `IteratorBase::Valid`, and rocksdb's SkipListRep iterators
linked their `iter_` member's calls (`InlineSkipList<…>::Iterator iter_;`)
to their own methods. The type is now read whole, `Outer<Args>::Member`,
and C++ lookup finds the member in the class template.

A library template's member read this way is a library type only as an
iterator (`std::vector<int>::iterator`, never a project class named
`iterator`). Others name another type that may be the project's
(`std::vector<Slice>::value_type`, gmock's `typename
std::tuple_element<N - 1, MatcherTuple>::type matcher`), so their calls go
on as before.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ent's method (colbymchenry#2576)

`std::deque<Slice>::const_iterator it; it->data()` was matched by the
method's name alone, so it could reach any class with a `data`, and
`itr->c_str()` on a `std::vector<std::string>::const_iterator` reached
gtest's `FilePath::c_str`. A `->` on a value of an iterator type now
reaches the container's element, as `->` on a smart pointer reaches what it
holds: the element is read from the container's template arguments
(cppRangeElementOf), before the container's own `iterator` alias would
lose them (protobuf's `RepeatedPtrField<T>::iterator`), and looked up as
C++ does. A library element (`std::string`) or the caller's template
parameter calls no project method. An element only a class template's own
`begin()`/`operator*` writes with its parameter (googletest's
`ParamGenerator<T>::iterator`) goes on as before.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…s the element's method through `->` (colbymchenry#2579)

`auto it = logs_.begin(); it->IsSyncing()` had no type for `it`: a call
on a library container (`std::deque<LogWriterNumber>`) returned nothing
the auto-local reader could use, so `->` calls on it were matched by the
method's name. A standard or abseil container's `begin`/`end`/`find`/
`lower_bound`/`rbegin`/… now types as `<container>::iterator`, and a
standard search (`std::lower_bound`, `std::find_if`, `std::next`, …) as
the iterator it is given first; colbymchenry#2576's cppArrowTarget then reaches the
container's element.

The element is looked up where the container's type is written (the
`auto` local's declared site, a data member's class), not where the call
is: an element nested in another class (`vs->obsolete_files_.begin()`)
finds that class's type, not a namesake visible at the call. A holder's
type stays looked up at the call: from a pimpl member's header the lookup
finds a namesake (protobuf's other `DescriptorIndex`) or nothing.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…()`, calls the method of what it points at (colbymchenry#2598)

The extractor records `(*it)->Visit()` and `(*node).Weight()` by their
bare names, which the name strategies then gave to whichever class had a
method of that name (0.4-0.7 guesses), and a local a dereference sets,
`auto first = *nodes.begin()`, had no type.

`*x` and `x->` in C++ expression typing now dereference a container's
iterator to its element as well as pointers and holders (cppDereferenced,
used by cppExpressionType, cppMemberObject and cppChainInnerType), and a
bare-name call written on `(*name)` (read at the call's column, where the
receiver starts) is resolved on the dereferenced value's class by C++
lookup before the name strategies run: the method, or no project method
for a library type, a template parameter's, or a class that lacks it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…, `it->second.Flush()`, calls the value's method (colbymchenry#2601)

The extractor records `it->second.Flush()`, `kv.second.Flush()` and
`(*it).first.Print()` by their bare names, which the name strategies gave
to whichever class had a method of that name — 45 `std::string::c_str()`
calls went to googletest's `FilePath::c_str` — and a local such a member
initializes (`auto& v = it->second`) had no type.

`x.first` / `x.second` (and `->`) on a `std::pair` now type as the pair's
elements in C++ expression typing (cppPairMember), so a map's iterator,
range-for element or declared pair reaches its key and value, and colbymchenry#2598's
bare-name receiver reader also takes `name->second`, `name.first` and
`(*name).second`. A member typed by another class template's parameter
(`CoreLocalArray<T>::AccessElementAndIndex()`'s `std::pair<T*, size_t>`)
goes on as before: only the calling function's own template parameter
names no particular class.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ound()`, calls the element's method (colbymchenry#2602)

The extractor records a call on a subscripted receiver, like
`statuses[i].IsNotFound()`, `buffers[key].Flush()` or `iters[i]->Valid()`,
by its bare name, which went to whichever class had a method of that name,
and a local an element initializes (`auto& s = statuses[i]`) had no type.

- C++ expression typing reads a subscript: an array's element, what a
  pointer points at, a library map's mapped value, a sequence's element, or
  what a class's `operator[]` returns.
- Declarations record their array dimensions (locals, parameters and data
  members, also through an alias), so a subscript, `*arr`, `arr->` and a
  range-for over an array read the type the array is declared with.
- The bare-name receiver reader takes subscripts after a name or `(*name)`,
  reading the line with its literals blanked.
- Overload arguments use the same subscript typing: a map's `values[key]`
  is its value type, no longer a `std::pair`.

A/B vs main, every changed site triaged: leveldb 12 removed, 14 added,
1 retargeted, 11 metadata-only; fmt 6/4/10/2; redis identical; rocksdb
513/292/843/807; protobuf 58/5/34/66.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…OBUF_DEFINE_ACCESSOR(FieldDescriptor, number, int)`, is indexed where the macro is invoked (colbymchenry#2605)

protobuf declares its descriptor accessors in their classes and defines
them below with macros, like `PROTOBUF_DEFINE_ACCESSOR(FieldDescriptor,
number, int)`; its reflection pastes `Reflection::Get##TYPENAME` in
another. The parser never sees those definitions (the preParse blanks the
invocations) and a declaration mints no node, so the methods had none and
every call to them linked to nothing.

- After either extractor (the kernel or the wasm walker), a step over the
  original source expands the file's own function-like macros whose body
  defines `Class::method(...) ... {`, at each invocation at namespace
  scope, and mints a method node per definition: named and qualified like
  an out-of-line definition, contained by the file and the same-file
  class, with the expanded head as its signature. Constructors,
  destructors and variadic macros are left out.
- The parse worker leaves the raw kernel fast path only for a file that
  may define one (a cheap text test), so the step sees it.
- The C++ resolver reads such a method's return type and parameters from
  its signature, and never takes it for a deleted overload.

A/B vs main on the kernel path, every changed site triaged: leveldb and
redis identical; fmt +13 nodes and rocksdb +11 (gtest's TestEventRepeater
overrides); protobuf +257 nodes, 6,203 calls and 2 references added, 252
calls retargeted, 11 metadata-only, none removed. Kernel and wasm dumps
are byte-identical, protobuf's one pre-existing Swift line aside.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…::ReadPrimitive<int32_t, WireFormatLite::TYPE_INT32>`, is named after its template (colbymchenry#2609)

An explicit specialization of a function or member function template kept
its template arguments in its name: protobuf's `template <> bool
WireFormatLite::ReadPrimitive<int32_t, WireFormatLite::TYPE_INT32>(…)` was
named `TYPE_INT32>` and qualified `…::ReadPrimitive::TYPE_INT32>`, the
qualified id split at the `::` inside its arguments; rocksdb's `template <>
int64_t Deserialize<int64_t>(…)` was `Deserialize<int64_t>`. A call writes
the bare name, so nothing linked to them.

Both extractors (wasm and the native kernel) now name a specialization
after its template, from the template-id's own name, and take its receiver
from the text before the declarator's name. That text must end in a real
`::`: tree-sitter recovers Metal's `vertex VertexOut vertexShader(…)` with
a missing, zero-width one.

A specialization now meets its template among a name's candidates, so the
resolver chooses between them wherever it picks among C++ candidates (a
class's methods, the implicit-this walk and its bases, exact, qualified,
global, fuzzy and macro-namespace matching):
 - a call that writes template arguments reaches the specialization
   written with the same ones (spacing aside, or qualified further on one
   side), else the template itself or an ordinary overload, else nothing:
   fmt's `num_bits<unsigned short>()` stays on `num_bits<T>()` instead of
   the nearer `num_bits<uint128>()`; protobuf's `Arena::Create<T>` keeps
   calling `DefaultConstruct<T>`, not the tests' `DefaultConstruct<
   DispatcherTestProto>` hook; a dependent `ReadPrimitive<CType,
   DeclaredType>(…)` links nowhere rather than to the first specialization.
 - a call that writes none, or `<>`, reaches a specialization only when
   nothing else has the name.
 - a class's own member template, specialized but none for these
   arguments, ends the lookup there instead of falling to a base's
   namesake.
The `cpp-override` pass leaves specializations out on both sides: a member
template is never virtual.

Validated on the cgstd kit against main (f7951a1): leveldb and redis
byte-identical; fmt: 8 specializations renamed, every call target as on
main; rocksdb: the cassandra `Serialize<T>`/`Deserialize<T>` calls (25 in
format.cc, 75 in its tests, which main sent to `ColumnBase` methods) each
reach the specialization their argument names (100 of 100 agree);
protobuf: `MergeFrom<MessageLite>` (3 calls), `ReadPrimitive<int,
TYPE_ENUM>` (2) and a test's `ReadPackedPrimitive<uint32_t, TYPE_FIXED32>`
reach their specializations; 5 dependent calls like
`GetRepeatedFieldInternal<T>`, and one `MutableRepeatedFieldInternal<int>`
(the macro writes `<int32_t>`), no longer link to the first specialization
a macro defines. Kernel and wasm dumps identical (but for protobuf's known
Swift line). Full suite: 7179 passed, 0 failed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…seWithLimit(limit, data, result)`, reaches the overload its arguments fit (colbymchenry#2612)

A C++ call to a function outside a class took whichever same-named
function the name strategies ranked first, by file and line proximity,
never by its arguments. Methods already picked their overload by argument
count, string literals and written argument types (cppOverloadFor);
functions never did. protobuf's `ParseWithLimit(limit, data, result)` on an
`int32_t result` reached the `int64_t&` overload beside the call, and
rocksdb's `NewLRUCache(cache_opts)` the overload taking a `size_t
capacity`.

Exact, qualified (global, exact, partial) and macro-namespace matching now
keep their choice of function, then let the call's arguments pick among its
overloads, the functions of its qualified name the call's file can see
(cppFunctionOverload, with cppOverloadFor's count-then-type fit; the
macro-namespace scorer breaks its ties by type). The choice stays on a tie.
- An overload only another source file sees (another `.cc` file's
  `static`, an unnamed namespace's) never competes. When the name strategy
  had picked such a function, which was then rejected, the call now links
  to the overload its file sees: leveldb's `GetLengthPrefixedSlice(&input,
  &key)` reached memtable.cc's static and linked nothing.
- A function hides a class of its name, so a call to a name both have
  reaches the function.
- Only C++ calls: protobuf's Rust FFI calls lost their `extern` targets to
  the C definitions when every language went through it.

Argument types read better too: a parameter named like the function it
belongs to, protobuf's `std::string u64(uint64_t u64)`, was read as the
function's own declarator, so `u64` looked like a `std::string`.

Validated on the cgstd kit against main (627018e), every changed site
classified by argument count against both targets, ~100 type-decided sites
read by hand:
- leveldb: 5 calls linked that linked nothing.
- fmt: 283 retargeted (139 to an overload that takes that many arguments,
  110 by type, 34 multi-line or defaulted); fmt's `fill_n(it, …)` on an
  iterator of unknown type now takes the `basic_appender` overload, right
  for fmt's usual iterator.
- rocksdb: 101 linked, 1118 retargeted (720 by count, 339 by type).
- protobuf: 29 linked, 941 retargeted (372 by count, 558 by type), e.g. the
  compiler helpers' `QualifiedClassName(enum_, …)` reaching the
  `EnumDescriptor*` overload and `Namespace(d->file())` no longer calling
  itself.
- redis (C): byte-identical.
Full suite: 7188 passed, 0 failed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…wn file can see, past another file's `static` copy (colbymchenry#2613)

A C call now links to the function of its name the call's own file can see:
its own, or one a header it includes defines, else one with external
linkage, the best placed of them (cppFunctionOverload's C branch). Before,
the name strategies took the function nearest the call; when that was
another source file's `static`, isVisibleAcrossFiles dropped it and the
call was left unlinked, so no call reached redis's `server.c` `mstime()`
or `ustime()`.

- A source file's `static` function is visible to a file that `#include`s
  that source file: hiredis's async.c includes dict.c, redis's ae.c its
  event-loop backend.
- The coordinator's `resolveImport` hook falls back to matchByFilePath for
  a C/C++ `#include` the import resolver can't place, as resolveOneInner
  already does, so cppIncluders sees includes only the end of their path
  names: protobuf's `google/protobuf/descriptor.upb.h`, which two generated
  copies end in, and jemalloc's `jemalloc/internal/*.h`. Standard headers
  are left out: otherwise `#include <math.h>` makes jemalloc's
  test/math.h visible to its own include through isIncludedCppTestSuite.

A/B on the cgstd kit against main 4a591ae: leveldb, fmt and rocksdb
identical; redis +194 calls linked and 115 retargeted (114 to hiredis
alloc.h's POSIX `static inline`s, 1 known miss: lua-cjson's fpconv_init to
an inline only `#ifdef USE_INTERNAL_FPCONV` builds use); protobuf 412 upb
calls retargeted from the unincluded php-upb.h amalgamation to the headers
the calling file includes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…'s `#ifdef USE_INTERNAL_FPCONV` `fpconv_init`, gives way to the source file the build compiles (colbymchenry#2615)

lua-cjson's fpconv.h defines `static inline void fpconv_init()` only under
`#ifdef USE_INTERNAL_FPCONV` and otherwise declares it `extern` for fpconv.c
to define. Nothing in redis's tree sets the flag, so its build calls
fpconv.c's, but colbymchenry#2613's rule (the file's own or an included header's
definition first) took the header's.

c-feature-variants.ts decides whether a line is compiled only by a build
that sets a flag nothing sets. It reads the conditionals around the line
(cpp-macro-visibility's directiveLines, now exported): `#ifdef`/`#ifndef`,
and `#if` with defined, !, &&, ||, parentheses, integers and bare flags,
then `#elif` and `#else`. A flag is set nowhere when the line's own file
doesn't define it, no build file from the project root down to the
definition or the call names it, and no C/C++ file of their components
(the tree under the nearest directory with a build file) defines it.
Reserved and platform names stay unknown.

cppFunctionOverload's C branch then takes a non-static, non-variant
definition outside the file's tier when one exists, when every definition
the file sees is such a variant, and when the call itself is compiled by
the default build. A branch around both the definition and the call decides
nothing: redis's quicklist.c tests call their own `static mstime` inside
`#ifdef REDIS_TEST`.

A/B against main dc4ccf6: leveldb, fmt, rocksdb and protobuf identical;
redis 1 call retargeted, lua_cjson.c's fpconv_init() from fpconv.h to
fpconv.c. Indexing time within noise (redis 10.5-11.9 s on main, 10.2-12.3 s
here).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… holder's `get()`/`value()` returns, like rocksdb's `edits.back()->MarkAtomicGroup(…)`, calls the element's method (colbymchenry#2616)

A call on what a library container hands out linked to nothing: the
standard library's `front()` returns nothing the index can read, and the
library-receiver gate keeps the name strategies off a `std::vector`.

cppLibraryElementOf reads what such a member returns, where the
container's type is written:
- `front()`, `back()` and `at(i)` of a sequence (std/absl, and the project
  sequences cppRangeElementOf already treats as such) → its element;
- a library map's `at(key)` → the mapped value;
- `std::stack`/`std::priority_queue` `top()`, `std::queue` `front()`/`back()`
  → the element;
- `get()` of `std::unique_ptr`/`std::shared_ptr`/`absl::NoDestructor` → a
  pointer to the held type, `value()`/`value_or()` of an optional or an
  `absl::StatusOr` → the held type.
It is asked in cppChainInnerType (a chain `recv.front().m()`) and in
cppExpressionType (an `auto` local, a range, an argument), next to
cppLibraryIteratorOf.

A/B on the cgstd kit against main 98127e5: leveldb, fmt and redis
identical; rocksdb 299 calls added, 89 retargeted (overloads picked by a
typed `.get()` argument, like `test::PerThreadDBPath(env_.get(), …)`, and
inherited methods on a typed element), 10 removed (guesses on what turned
out to be library containers) and 56 confidence-only; protobuf 7 added and
16 retargeted (cast helpers off their `std::shared_ptr` overloads, `Add`
off a deleted one). Every site was joined with the element type an
instrumented arm logged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…DynamicCastMessage(const MessageLite*)` and `DynamicCastMessage(MessageLite*)`, a call reaches the one C++ picks (colbymchenry#2618)

Overload fit by argument type (cppArgumentTypeChoice) scored a `const T*`
parameter and its `T*` twin alike, and the one declared first won: protobuf's
`DynamicCastMessage<T>(ptr.get())` on a `std::shared_ptr<Message>` took the
`const MessageLite*` overload.

- cppConstTwinChoice: of the best-fitting overload's twins, alike but for
  the `const` of a pointer or reference parameter, the one that adds `const`
  to the fewest arguments.
- cppWrittenTypeFit: a pointer or reference to a non-const type never takes
  a const object, nor a reference a value a call returns (cppNamesObject
  tells a named object from a call's result); an object is no pointer, nor a
  pointer to characters a pointer to another type (or the reverse). None of
  that for an argument of a template parameter's type, and the pointer and
  `const` rules not for a parameter the function's own template parameter
  types (it deduces the `const`).
- The argument's type keeps a `const` its declaration writes in front
  (`const Buffer* b`, `constexpr char key[]`), which the declarator regex
  leaves out; an array is a pointer to its element; `&ref` is a pointer to
  the referee; cppLibraryElementOf keeps a `const` in a holder's template
  argument (`std::shared_ptr<const Message>`).
- Template headers are read 8 lines up, not 3 (fmt's `join`).

A/B on the cgstd kit against main 0893606: leveldb and redis identical;
fmt 14, rocksdb 25 and protobuf 194 calls retargeted, each triaged against
C++'s choice (const twins, objects and arrays off the wrong pointer
overloads, temporaries to `T&&`).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Every full test run left about 40 directories in the temp dir, even when it
passed. On the Windows dev machine about 16,000 had piled up by mid-October.
A full run with TEMP pointed at an empty folder showed where each one came
from:

- function-ref (20) and ts-field-classification (3): each case's `finally`
  cleared `tmpDir` right after `cg.destroy()`, so the `afterEach` that removes
  the dir found nothing to remove.
- npm-sdk (3) and liveness-watchdog (3) never removed the dirs they made.
- extraction's colbymchenry#1502 suite (3) made a dir in `beforeEach` and had no
  `afterEach`; pr19-improvements' schema-version case (1) made one inline.
- setup-home-sandbox (6 on Windows): Vitest runs no hooks for a file whose
  every test is skipped, so each platform-gated file left its home sandbox.
  Each sandbox now records itself in a per-run registry, and a global
  teardown removes whatever is still listed and names any dir still in use.

With the fix, the same full run leaves nothing behind but Node's own reused
compile cache. test-home-sandbox gains two tests: a file's home is listed in
the run's registry, and the teardown removes every listed home, then the
registry.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…mchenry#2624)

Two MCP test files sometimes failed under full-suite load on Windows with
EBUSY in their afterEach, and then left their temp dir behind:

- mcp-status-freshness: every case leaves an index-freshness measurement
  worker still ending, and the deadline case's worker is terminated in the
  middle of its measurement. Until it exits, the worker can hold the index
  or the project dir, and the removal raced that exit (EBUSY on
  codegraph.db or on the dir, ENOTEMPTY on .codegraph). The afterEach now
  awaits endFreshnessMeasurements(), the server's own shutdown wait, before
  it closes the index and removes the dir.
- mcp-initialize: after the killed server's 'close', the dir itself can
  stay busy a moment longer (EBUSY rmdir). It freed within about 80 ms,
  with no process left under the server. A bounded `maxRetries` doesn't
  help: on Node 22, fs.rmSync never retries EBUSY on a directory and throws
  at once. So removeWhenReleased() retries the removal itself, for up to
  5 s, and a Windows-only test holds a dir as a child's cwd to prove the
  helper waits.

Under CPU load (14 of 16 cores busy, two runs at once), the status
teardown failed 20 times in 240 without the wait and 0 times in 240 with
it. Alternating real runs of both files failed in 5 of 18 on main and 0 of
10 with the fix.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ions.cf_paths.emplace_back(…)` on a `std::vector`, no longer links to a project method of that name (colbymchenry#2626)

The extractor records a call on a member chain (`a.b.m()`, `a->b->m()`,
`this->p_.m()`) by its bare method name, so it went to whichever project
method shared the name. A census of the cgstd kit found most such calls
linked by name alone, and most of those links wrong. Above all, they were
on members of standard-library types, whose methods (`size`, `reset`,
`push_back`, `get`, `empty`) a large codebase has namesakes of.

matchCppOutsideMemberChainCall reads the chain where the call is written
(cppMemberChainReceiver) and types it with the existing cppMemberObject:
- a data member is typed by its declared type in its class
  (cppDataMemberType);
- `->` goes through a pointer, smart pointer, optional or iterator.

It gives no edge when the object the method is called on is a library type
(isCppLibraryType) or a type under a namespace the project declares nothing
in (isCppOutsideQualifiedType). These are the call-chain resolver's rules.
Unlike a call chain, the operator is read as written, so `.` on a
`std::vector<T>` or a `std::unique_ptr<T>` is that type's own member,
whatever `T` is.

Anything else, a member of a project class included, goes to the name
strategies as before.

Kit A/B against main a257069 (only removals; nothing added or retargeted):

| repo | calls removed |
|---|---|
| rocksdb | 2,001 |
| protobuf | 234 |
| leveldb | 15 |
| fmt | 12 |
| redis | 0 (identical) |

Every removed link was a bare-name guess. Of the 2,235 removed sites in
rocksdb and protobuf, a judge reading the members' declarations across each
repo rated:
- 2,044 as wrong links (a library type's namesake, or an unrelated class);
- 165 unknown;
- 10 not counted as chains by its pattern (after a cast, like
  `(int)data->entries.size()`);
- 16 right. These turned out to be name collisions on the judge's side,
  checked in the source (`std::deque logs_`, `std::shared_ptr decompressor`,
  `std::vector ids`).

24 random removals were also read by hand, and all were correct.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ead->rand.Next()` on a `Random64`, links to the method of the member's class (colbymchenry#2627)

Step 2 of member chains; colbymchenry#2626 was step 1. matchCppMemberChainCall (formerly
matchCppOutsideMemberChainCall) now also resolves a chain whose member is
of a project class, in the same steps matchCppUnspelledReceiverCall takes
for `it->second.Flush()`:
- the class C++ lookup finds for the member's type, where that type is
  written;
- then its method, or one it inherits (cppMethodOf, with overloads picked
  by argument types);
- or no edge when the class surely lacks it (cppLacksMethod).

Before, such a call went by its bare name to whichever class had a method
of that name. Inside a method it could also be taken for an implicit
`this` call on the method's own class.

A member is now read from the class node C++ lookup found (cppDataMemberIn),
not from the first class of that qualified name that declares it
(cppDataMemberType, removed). rocksdb's db_bench_tool.cc and
db_stress_shared_state.h each declare a ROCKSDB_NAMESPACE::ThreadState,
whose `rand` is a Random64 in one and a Random in the other.

Kit A/B against main 6e884d7:

| repo | added | retargeted | same target, now typed | removed |
|---|---|---|---|---|
| rocksdb | 571 | 2,008 | 1,738 | 2 |
| protobuf | 42 | 95 | 142 | 0 |
| leveldb | 42 | 30 | 141 | 0 |
| fmt | 0 | 56 | 20 | 0 |
| redis | 0 | 0 | 0 | 0 (identical) |

The most common rocksdb retargets are DBImpl -> DB (calls on a `DB*`),
BlobContents -> Slice, BlobFileReader -> FileDescriptor and
EnvWrapper -> Env. Hand-reading random samples found one wrong
retarget (db_bench's `thread->rand.Next()`, the ThreadState namesake),
which led to the member-lookup fix above. After that fix, all 76 sites
sampled across the four repos were correct:
- 30 retargets and additions, plus both removals, in rocksdb;
- 20 in protobuf, 14 in leveldb and 10 in fmt.

The two removed calls are `task_reader->rep_->GetCoroutine(…)` on a
`SstFileReader::Rep`, which has no such method. They had linked to
`CoroDB`'s.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… `options.statistics->getTickerCount(…)` on a RocksDB `Options`, links to the member's class (colbymchenry#2628)

Step 3 of member chains, after colbymchenry#2626 and colbymchenry#2627. The type of `x.member` was
read from x's class alone, so a member that class inherits was never found
and the call fell back to its bare name.

A temporary diagnostic on main measured why chain calls still fell through:
2,801 of 4,884 were a member declared in a base class. Nearly all were
rocksdb's `Options`, which declares no members of its own and inherits
`statistics`, `table_factory`, `listeners`, `prefix_extractor` and the rest
from `DBOptions` and `ColumnFamilyOptions`. The other causes are left for
later:
- a chain whose base can't be typed: 875;
- a class the index lacks, e.g. protobuf's generated `_impl_`: 626;
- a member found nowhere: 337.

cppExpressionType's member branch now uses cppInheritedDataMember, the
lookup the calling class's own members already use (colbymchenry#2533):
- the class's own declaration first;
- then the classes it derives from, through their base edges, to depth 4;
- a base that depends on the class's template parameters is skipped
  unless a using-declaration brings the name in.

Kit A/B against main e8ec7fd:

| repo | removed | added | retargeted | same target, now typed |
|---|---|---|---|---|
| rocksdb | 1,410 | 634 | 392 | 184 |
| protobuf | 0 | 2 | 149 | 55 |
| leveldb, fmt, redis | identical | | | |

- Removed: the bare-name guesses on inherited std::shared_ptr and
  std::vector members, such as `options.table_factory.reset(…)` →
  FSWritableFilePtr::reset (654) and `options_.db_paths.emplace_back(…)` →
  autovector::emplace_back (256).
- Added: `options.statistics->getTickerCount(…)` → Statistics (432),
  `rate_limiter` → RateLimiter (51), `comparator` → Comparator (26).
- Retargeted: DummyOldStats -> Statistics, DB -> Env, ClockCacheShard -> Cache.
- protobuf's generated `this_._internal_metadata_.Clear<…>()`,
  `MergeFrom<…>` and `unknown_fields<…>` now reach InternalMetadata, a
  member every message inherits from MessageLite. Before, they reached the
  message's own `Clear` or `Any::MergeFrom`.

All 50 random sites read by hand (36 rocksdb, 14 protobuf) were correct.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…colbymchenry#2629)

A full suite run on Windows left a `serve --mcp --path %TEMP%\cg-rebuild-…`
process behind. Its parent was gone, and its one thread was suspended with
0 CPU used. It would never exit, and it held the checkout's dist.

libuv creates every detached child suspended on Windows and resumes it only
at the end of `uv_spawn`. A launcher killed inside that call never resumes
its daemon. The daemon never runs, so it writes no daemon.pid, and nothing
can find or stop it.

The rebuild test's teardown killed its launcher at once. The test stops the
daemon on purpose, so by then the launcher is serving calls in-process with
a retry timer running. When the retry comes due during the last call, the
launcher spawns its daemon as soon as that call answers, just as the
teardown begins. Under load the two coincide.

The teardown now:
- closes each launcher's stdin and waits for it to exit. A graceful exit
  can't land inside the synchronous spawn.
- records the daemons each launcher spawns (colbymchenry#1773's recorder) and stops them
  until all have exited. One spawned as its launcher exited may still be
  starting, and stopDaemonAt leaves a daemon running until it answers.

The recorder's comment now says that a launcher killed mid-spawn records
nothing.

CODEGRAPH_DAEMON_RETRY_MS=2500 puts the retry inside the last call on a
quiet box. Under it, main's test left a daemon in 6 of 12 runs: 2 suspended
ones, plus 4 live ones whose fixture then failed to delete. This test left
nothing in 12 runs. Of the mutants, killing launchers again, a single
stopDaemonAt and no spawn recorder each leaked. The other daemon test files
left nothing in 2 runs each.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ule when the module changes (colbymchenry#2630)

`NODE_PATH_EXCLUDELIST.includes(p)` after `import { NODE_PATH_EXCLUDELIST }
from './constants'`, `store.get(k)` through a default import or
`api.get()` through a namespace import is linked by a fresh index through
the import. When the module was missing or lacked the export, the failed
reference was parked under its member's name (`includes`, `get`), which the
module seldom declares and which the retry skips past its 500 ceiling. A
sync that gave the module the export never relinked it, nor one that added
the module where no import edge reached its file.

A direct member reference through an import binding of a project module is
now parked under the module's key, as whole names through a binding are
since colbymchenry#2450, and keeps two more triggers. Its member's name, through a new
expression index on the tail its name gives (schema v16), under the same
ceiling as the references still parked under that name: a namespace
import's barrel forwards the member from another file, and a guess by name
links a receiver whose module never resolves. Its receiver's name, through
a prefix range on the module-name index of colbymchenry#2450: a barrel's import of the
declaring file that no walk finds. The router navigation retry and the
dead-code check read module-parked calls by their member through the new
index. A chain or a longer path keeps its member's tail: on vscode,
parking them too tripled the refs an edit of a hot module retries, and
none of them resolved.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…odule's file (colbymchenry#2633)

In a project with a `vue` dependency or a `.vue` file, a script's module
import through `@/` or `~/` went through the Vue resolver's own mapping to
`src/`, which linked the first node of the file it found: a constant the
module declares, or a component. A tsconfig alias that starts like a Nuxt
virtual module (`#app/where`) linked the importing file itself. Without Vue
the import resolver links the module's file node, following tsconfig
paths. So the walks a sync makes from a module or barrel that appears or
goes away to the files importing it (colbymchenry#2512, colbymchenry#2560) never found these
importers. Which node the edge hit also depended on the order a file's
nodes were stored, so a sync and a fresh index could disagree.

The Vue resolver now leaves a module import to the import resolver whenever
that finds the file, and only adds what a Vue app's bundler finds from a
plain script: a component named without `.vue`, or a folder's `index.vue`
(`import Layout from '@/layout'`), linked to the file.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…when the class's module changes (colbymchenry#2637)

`cache.get(k)` after `const cache = new LRUCache(10)` (or an annotated
local, a typed parameter, a default or namespace import of the class) is
linked by a fresh index through the local's declaration. When the class's
module was missing or empty, the failed call was parked under its member's
name, `get`, which more failed refs share than the retry's ceiling, so a
sync that added, restored or filled in the module never retried it.

Such a call now waits under the key of the module the type's binding
imports, as a member of the binding itself does since the previous change
(2630), and keeps that change's member and receiver triggers. Only calls
are parked this way: in TypeScript a typed local's member is only ever a
call, and a method of `this` passed as a value is looked up in the
enclosing class.

A local declared as an array of the class (`results: URI[]`) keeps its
member's tail. Resolution reads the element type there too, but
`results.push` is the array's method, which no edit of the class's module
links. On vscode those calls were 60% of what the parking added and nearly
doubled what an edit of `uri.ts` retried. Inference now reports the line
it read the declaration from.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…glob leaves members out (colbymchenry#2638)

pnpm's `packages/**/*` (folo) and npm's `libs/**/*` (bitwarden) list
member packages at any depth, `packages/plugin-*` and `apps/*/web` name
them by part of a folder name or by a folder below the wildcard, and a
negated glob (`!**/example`, `!examples/with-nx`) leaves some out. The
workspace loader listed only the folders directly under the text before
the first `*` and took a negation for a folder name, so folo's
`@follow/utils`, two levels under `packages/`, was no member and every
import of it counted as an outside package's.

Globs now match folder by folder: `*`, `?` and `{a,b}` within a name,
`**` across folders, breadth-first so a member nearer the root keeps a
name a deeper copy also declares. A wildcard never enters a dot-folder,
`node_modules` or `bower_components`, and a `**` skips `test` and `tests`
folders as pnpm's default ignores do; a folder the glob names literally
is always entered. What a negated glob matches is no member.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…links to nothing, not the class's method (colbymchenry#2639)

`items: Item[]` was read as an `Item`: the annotation's capture stopped at
`[`, so `items.find(…)` linked to `Item.find` whenever the class declares
a method of that name. On vscode that was 16 wrong edges, like
`distinctElements.find(…)` on an `ExplorerItem[]` reaching
`ExplorerItem.find` and `ranges.map(…)` on an `OffsetRange[]` reaching
`OffsetRange.map`.

The annotation pattern now captures a bracket suffix with the type. On an
array of a type, a method arrays have links to nothing and isn't matched
by its name either. Any other method can't be called on the array, so it
is called on an element that shares its name, as in `d.forEach(d =>
d.dispose())`, and keeps the element's type as before. An indexed type
(`Element["groupIds"]`) is still read as the type it indexes.

The sync parking that came with the previous change now reads arrays from
the type inference gives instead of re-reading the declaration line: an
array method on an array keeps its member's tail, and an element's or an
indexed type's call waits for that type's module.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ts (colbymchenry#2641)

When several classes share a name, a TypeScript or JavaScript call on a
typed value broke the tie only by the call site's own file, then took the
first declaration indexed, and a `this.<field>` call by folder nearness.
On vscode, about 25,000 calls on classes vscode imports from its own
`vs/base` and `vs/platform` reached the copies the Copilot extension
vendors, 907 `Emitter` calls a copilot test fixture's copy, and 952 calls
whose first match was a test fixture's class linked to nothing.

The file declaring the class the call's file imports under the written
name, read through re-exports, `import type`, namespace imports, default
imports and `require`, is now passed to resolveMethodOnType, which picks
that file's method among several, through the supertype walk too.
`this.<field>` calls check the import of the call's own file before
folder nearness. Java, Kotlin and every other language are unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Moves the base from e63fe2e to upstream 10e316d (main, 2026-10-19), 363
commits on, past v1.6.2. The four fork patches merge back without a conflict,
line for line as they were. Conflicts were only the version marker, the
lockfile, release.yml (the fork keeps its npm-free workflow) and install.ps1,
where upstream's new Install-CodeGraphFiles swap now names the fork in its
reinstall hint, as does its copy in src/upgrade/index.ts.
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 19, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-19T07:44:00.843585Z 4dc197c PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 4dc197c813

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +5250 to +5251
const std = (n.language === 'go' ? GO_STD_METHODS : RUST_STD_METHODS).has(n.name);
return !std || /^(?:self|Self)$/.test(shape.receiver) || (shape.receiver !== '' && sharesReceiverWord(shape.receiver, n));

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve indexed Rust receiver calls

When a project Rust type has a standard-library-named method and it is called through an indexed receiver, e.g. items[0].map(), this filter drops the real Item::map target: the new shape parser reduces the receiver to items, which neither equals nor shares a word with Item. That leaves the call unresolved even though the indexed expression has a concrete project element type; retain candidate methods for indexed/wrapped receivers unless their type is known to be a standard-library type.

Useful? React with 👍 / 👎.

Comment on lines +1174 to +1176
// Require statements — each declarator of a list too (`var express = require('../'),
// request = require('supertest')`), and a member of the module (`require('./utils').methods`).
const requireRegex = /(?:\b(?:const|let|var)\s+|,\s*)(?:([A-Za-z_$][\w$]*)|{([^}]+)})\s*=\s*require\(\s*['"]([^'"]+)['"]\s*\)(?:\s*\.\s*([A-Za-z_$][\w$]*))?/g;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Keep CommonJS bindings from claiming this calls

With valid whitespace inside a destructured CommonJS require, such as const { matches } = require( "./esquery" ), this broadened regex now records matches as an import binding. Because TS/JS extraction represents this.matches() by the bare name and import resolution runs before method matching, the call is resolved to the required function instead of the enclosing class's own matches method, creating a false cross-module edge. Exclude calls written through this from the import-binding path.

Useful? React with 👍 / 👎.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants