`private readonly _onDidChange = this._register(new Emitter<void>())` and
`this._store.add(new Emitter())` gave the field no type, so its `fire()`
calls linked nowhere. The field reader now reads `new X(` handed to
`this.<method>(…)` or `this.<holder>.<method>(…)` when that method's own
declaration returns its argument (`<T>(o: T): T`, T its own type
parameter) and the call ends the statement.
The end-of-statement check trims what follows the wrapper's `)`: a CRLF
checkout's lines keep their `\r`, which a `$` anchor never matched.
The field walk and the wrapper lookup now go ten classes up a class's
`extends` chain instead of four: vscode's editors inherit `_register`
and `_store` from `Disposable` up to eight classes above them.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A field the calling class doesn't declare was read off any same-named
class elsewhere, never off the classes it extends. It is now looked up on
the calling class's base classes, each the class its subclass's file
imports or declares, and a same-named class elsewhere is read last, only
for a type the call's file imports or declares.
A type a file imports from a package outside the project now links to
nothing in the project, unless a project file declares the package with
`declare module`, Flow libdefs included: microsoft-authentication's
`TelemetryReporter` from `@vscode/extension-telemetry` reached another
extension's interface of that name.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A call on a value another file exports, like `store.add(…)` after
`import { store } from './state'` with `export const store = new Store()`
there, read the type off the value's declaration but broke a tie between
same-named classes by the calling file, which declares neither, and then
by index order: on vscode, `MenuRegistry`, `FileAccess`, `extUri` and
`errorHandler` calls reached copilot's vendored copies or a test fixture.
The value's file now says which class it is, the one it imports or
declares, in Vue, Svelte and Astro components too.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A local or parameter annotated as one of a type's properties, like
`editor: IChatWidget['inputEditor']`, was read as the type it indexes, so
its calls reached that type's method of the same name or a guess by the
method's name: on vscode, `visibility.isVisible(…)` through
`ISessionChatPillVisibilityService['pullRequests']` reached the service's
own `isVisible`, and `chatModel.onDidChange(…)` a folding model's.
The property's declared type now decides alone, read off the indexed type
the call's file imports, with its class the one the declaring file
imports or declares, and an array property follows the array rules. A
property whose type isn't written there (a getter's, a `typeof`, a type
parameter of the class) or a key that isn't a name still reads as the
type it indexes.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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>
`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>
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>
`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>
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 (#2512, #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>
`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 #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 #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>
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 (#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>
Step 3 of member chains, after #2626 and #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 (#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 e8ec7fd8:
| 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>
Step 2 of member chains; #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 6e884d7b:
| 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>
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 a2570695 (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>
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>
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 #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>
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 0893606e: 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>
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 98127e54: 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>
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 #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 dc4ccf6b: 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>
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 4a591aee: 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>
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 (627018eb), 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>
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 (f7951a13): 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>
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>
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>
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 #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>
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>
`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; #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>
`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>
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>
The two follow-ups #2567 and #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: #2413's library rule skips such declarations,
and #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>
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 #2532's
`auto` locals and chains walk alias by alias.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A C++ receiver declared with a namespace-qualified type is from outside
the project only when no class of the project can be what that name names.
Three cases read that wrong on main:
- A namespace a macro stands for. RocksDB declares its classes in
`namespace ROCKSDB_NAMESPACE` under `#define ROCKSDB_NAMESPACE rocksdb`,
so the index names them `ROCKSDB_NAMESPACE::DB`, and #2532's outside rule
(cppProjectScopeNames) never saw `rocksdb`: `rocksdb::DB* db; db->Put(…)`
got no edge. cppProjectScopeNames now adds the name a single-name macro
stands for, in both directions (cppNameMacros, the map
cppNamespaceMacros already builds).
- A lowercase outside type beside a project namesake. isCppLibraryType
(#2413) took a non-std lowercase type as the library's only when no
project type had its name at all, so `absl::flat_hash_set<int> seen;
seen.insert(…)` reached a project's own `util::flat_hash_set::insert`.
It is now also the library's when every project type of that name is a
namesake (cppFindsOnlyNamesakes).
- A capitalized type whose last name finds only namesakes, on the paths
#2493's call-site rules leave to the last-name match: a declaration read
outside the calling function (protobuf's `absl::Status _status` in its
RETURN_IF_ERROR macro body reached `upb::Status::ok`; `auto bytes =
::hpb::Serialize(…); bytes.value()` reached `hpb::StatusOr::value`), and
a namespace the project uses (its generated
`DescriptorProto::ExtensionRange`, which the index doesn't hold, reached
`Descriptor::ExtensionRange::options`). That call now gets no edge, as
isUndeclaredTypeName rules where no class shares the name; `->` on a
value still goes to the name strategies, and a template parameter's
member is left alone.
cppFindsOnlyNamesakes: every type of the last name, and every owner of a
method of the called name (the index can hold a class's methods without
the class), has a qualified name that ends unlike the written one, neither
the other's tail; namespace aliases are expanded and macro-named
namespaces read both ways. A name a project type qualifies, an unqualified
name and an unbalanced `<>` are never judged.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The jsx-render pass read tag names off each parent's raw lines, so text
that only mentions a tag rendered it: react-router's hooks rendered the
`Router`, `Routes` and `Route` their JSDoc, comments and invariant
messages name ("You cannot render a <Router> inside another <Router>"),
shadcn's `useCarousel` the `Carousel` its error names, and layouts the
markup commented out of them (`{/* <Progress /> */}`).
The pass now scans a code-only view of each file (`jsxCode`, new in
src/resolution/jsx-code.ts): comments and the contents of string,
template and regex literals and of JSX attribute strings are blanked,
offsets and line breaks kept. The lexer follows JSX elements, so a quote,
slash or backtick in JSX text (`Don't`, `https://…`) stays text, and it
re-reads a `<` that opens a type parameter list (`<T,>(x: T) =>`) as an
operator. A file it can't read as one program (an unbalanced brace, a
string open at a line's end, a mismatched closing tag) is scanned as it
is, as before. TypeScript `.ts` files are skipped outright: they can't
hold JSX. The lexer's state lives in a class whose methods every file
shares: closures made per file sent V8 back to recompile it for file
after file.
Checked against TypeScript's parser on 36 React repos (7,649 files,
62,377 `<Name` matches): no real tag blanked, no file given up on, 614
non-JSX matches removed. Graph A/B on 12 repos: -36 jsx-render edges,
+0, nothing else changed; the TypeScript oracle grades all 36 as tags in
comments or strings.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Value-reference edges (a reader symbol -> the file-scope const/var it
reads, which impact follows) ran for typescript, tsx and javascript but
not jsx. A .jsx file is its own language, parsed with the JavaScript
grammar, so the same component had no edge to the lazy page it renders
(`<LoginPage />` under `const LoginPage = Loadable(lazy(...))`), the route
table or the config it maps over as .jsx, and all of them as .js or .tsx.
Every other JS-family gate in the extractor lists jsx, and nothing in the
history says this one was left out on purpose: the kernel mirrored the gap
with an explicit "NOT jsx" check for parity.
Add jsx to VALUE_REF_LANGS and drop the kernel's jsx exclusion.
On mantis, matx, berry, mastodon and bigbluebutton-html5 the index only
gains edges (35, 56, 15, 28 and 266): no node, ref or file changes and no
edge removed. Every added edge is a same-file read of a file-scope value
that is the only binding of its name in the file. A repo with no .jsx
files indexes byte-identically, and kernel-parity.mjs shows 0 diffs over
those repos with the same deferrals as before.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The JSX render pass matched a tag by name and used the file's imports only
to break ties, so an index that forwards another module's component
declared no candidate and the tag took a namesake elsewhere: outline's
Sidebar/index.ts is `import Sidebar from "./App"; export default Sidebar`,
and <Sidebar> rendered Aside.tsx's styled Sidebar, not the AppSidebar that
App.tsx exports as observer(AppSidebar).
A tag imported from a module of the project now renders what that module
exports under the imported name, read by exportedComponent, the reader
lazy routes use: through an index or barrel, past a wrapper call. Two rules
join that reader, for routes too: a const holding another binding is that
binding (`const Avatar = AvatarWithHoverCard; export { Avatar }`; a let may
be reassigned, so it stays), and a memo wrapper's bodyless component node is
the component it wraps (`const MemoizedBlurhash = memo(Blurhash)`). A module
whose export is no component node (`export default () => ...`) links nothing
rather than a namesake. Sync records every file a tag's import is read
through as a synthesis input, so rewiring only the index redraws the tag.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A PascalCase name that a .tsx/.jsx file declares as a module-level value is
that declaration wherever the file uses it: a JSX tag, a route's element, a
layout or a TSX type. Its initializer decides what it renders, in order:
- the component of the module its lazy loader loads, or the export the
loader picks, in either branch of a conditional;
- the function it wraps (`observer(function X() {…})`,
`forwardRef(Inner) as {…}`);
- else the declaration itself.
Before, the lookup by kind skipped the value and took a lone same-named
component anywhere, often in another app of the repository.
- Pattern 1 asks declaredComponent about every PascalCase reference, not
only route references. layoutComponent and the jsx-render synthesizer's
jsxChild ask it too.
- valueLoader reads a value's loader with loaderValue (#2480).
- defaultExportedName reads `export default observer(Login)`.
- lazyValueModules records the modules a file's lazy values load as
synthesis inputs, so a sync that changes one of them redraws the tag.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A lazy React Router route whose module is a barrel, and whose page sits in a
folder below the barrel's, stayed unlinked after `codegraph sync` added the
page or gave it its export, though a fresh index linked it. In
DigiShop.Frontend, `@/pages` is `src/pages/index.ts`, which imports `./Home`,
`src/pages/Home/index.tsx`, and exports it again. While the page is missing,
the route's reference fails and waits under the key of the module it names,
`module:pages` (#2422). The retry looks failed references up by the keys of
the changed files, and a file in `pages/Home/` gives only `module:Home` and
its own stem. The limit, which the `export *` barrels of #2436 share, was
recorded with #2558.
After the failed-ref retry, which makes the barrel's import edge into an
added file, sync now walks up the import edges from each file it added, or
gave a definition, to the barrels that pass it on: `export … from`, or an
import exported again (`import { X } from './x'; export { X }`, `export
default X`), as deep as the resolver chases a name. It re-opens, for the
sweep, the failed references waiting under those barrels' keys.
Only keys a failed reference waits under are looked up, in one DISTINCT
over the failed rows parked under module keys, so a sync with none reads
nothing, and a body-only edit seeds nothing. The ImportReader that the
added-files reopen (#2512) uses is shared, and gains exportedBindings,
behind a regex pre-check, since most importers export nothing.
defaultExportBinding moves to alias-binding.ts for this walk and for
react.ts's exportsBinding.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A lazily loaded folder whose index passes on a page it imports linked
nothing: DigiShop.Frontend's `src/layout/index.ts` does `import { UserLayout }
from './UserLayout'; export { UserLayout }`, and its routes load
`await import('@/layout')`. exportedComponent, the barrel walk behind lazy
routes, React.lazy values and lazy layouts (#2436, #2452, #2480), read the
export clause as naming a declaration in the barrel, which has none: the
name is an import binding. `import Login from './Login'; export default
Login` and `export { Page as Component }` of an imported Page failed the
same way, so all 27 lazy route refs in DigiShop's App.tsx stayed failed.
A binding the module exports but does not declare is now followed through
the module's import of it, a default import as `default` and a named one by
the name it imports, with each file read added to `reads` so lazyModules
still redraws the route. A namespace import or a package's binding links
nothing. `export default withAuth(Page)` hands over a call, whose first
identifier ownExport reads; that was harmless while only same-file
declarations counted, so a default is followed only when the statement is
the binding itself (exportsBinding), or the route would link the HOC.
#2451 (open) adds the same step to the general import walk; #2420 (open)
carried it in a lazyRouteComponent rewrite that exportedComponent superseded.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
codegraph_explore's low-value check (the hard filter, the rank penalty and
the comparator key) and the dominant-file, route-file and routing-manifest
filter in db/queries.ts each kept a regex list of their own. They missed
every test convention isTestFile learned since: C++ `foo_test.cc` and
`FooTest.cpp`, Google's `foo_unittest.cc`, LLVM's `unittests/`, `testing/`,
`e2e/`, `__mocks__/`, Gradle `foo-test/` modules, Kotlin source sets, and
the examples, samples, fixtures and benchmarks that explore's gather already
dampens through isTestFile.
Both now build on isTestFile, keeping icons/i18n (explore) and generated
files (queries), plus the singular `__test__/` the old lists matched.
Dropping their lowercase suffix patterns also ends false positives such as
`Latest.java` and `contest.cs`.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A member call was read only when its callee ended in the method's name, so
`reflection->GetRaw<RepeatedPtrField<T>>(…)` and
`factory->GetOptions<BlockBasedTableOptions>()` gave the `auto` locals they
initialize no type. Such a call now reaches the class's template overloads,
and when each is declared to return the same one of its own template
parameters (`template <typename Type> const Type& GetRaw(…)`), it returns the
call's argument for it. A macro body's `\`-spliced lines are read as one
statement.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Receiver inference read a declaration a line at a time, so a receiver whose
type began on a line above (protobuf's `const
google::protobuf::internal::ExtensionIdentifier<` over `FeatureSet,
TypeTraitsT, field_type, is_packed>& extension`) or sat alone on the line above
its name (rocksdb's `autovector<…>` over `cf_sv_pairs;`) declared nothing it
could read, and the call fell to name matching. Such a line is now read with
the lines it began on: joined until the template brackets pair, or with the
type line above, as class members already were.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A `nullptr` argument fitted any pointer parameter better than a
`std::nullptr_t` one, which counted as a class, so protobuf's
`ptr.reset(nullptr)` on an `Arena::UniquePtr` took `reset(T*)` over
`reset(std::nullptr_t)`. That `reset(T*)` is declared `= delete`, as is
`RepeatedPtrField::Add(const Element&)`, which `field.Add("foo")` reached.
`nullptr` now fits `std::nullptr_t` exactly and a pointer by conversion, and
an overload declared deleted is taken only when every overload is.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Splitting a call's arguments counted every `<` as a template's, so protobuf's
`p->Emit({{"declare", has_bit_word_index < 0 ? … : ""}, …}, "…")` and
`WriteVarint(num << 3, s)` read as one argument and took the wrong overloads.
A `<` now opens template arguments only right after a name and when a `>`
closes it before the bracket around it does, and the splitter closes the list
at exactly that `>`. A declared type's template arguments now pair their
brackets and may be empty, so `const std::vector<A*>& a, std::vector<B>& b`
and fmt's `parse_context<>& ctx` are read. A line that ends a declaration
begun above, like `std::unique_ptr<Blob>>>& reqs`, still declares the
receiver there.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The line-at-a-time C++ declaration scans took any line starting with `*` for
a comment continuation, skipping rocksdb's `const auto& el =` /
`*reinterpret_cast<std::shared_ptr<EventListener>*>(jhandle);`, and read code
commented out in `/* … */` and the text of string literals (protobuf's
`[[deprecated("Please use SpaceUsedLong() instead")]]`) as declarations.
Block comments are now found in the per-file pass that finds raw strings, and
every scan blanks them, with string contents, before reading a line.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A TEST_F / TEST_P body is a member function of a class derived from its
fixture, so its unqualified names find the fixture's nested types (its bases'
included) before the enclosing namespaces'. And since the index names a
function's local class after the function, every TEST_F body's local `FakeWF`
or `MyManager` shared one qualified name: a class local to a function is now
visible only from inside that function, and a namesake class's methods are
its own body's.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
protobuf's `using ConformanceRequestSetting =
BinaryAndJsonConformanceSuite::ConformanceRequestSetting;` names a class the
suite inherits from `ConformanceTestSuite`. An alias was followed only to a
class declared under exactly the name it spells, so constructions through it
stopped at the alias. Its target is now looked up a segment at a time through
each class's bases, as C++ does.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The class-name and capitalized-receiver strategies took any method of the
call's name whose qualified name contained the class's name, so protobuf's
`descriptor->file()` reached `Descriptor::ExtensionRange::file` and rocksdb's
`CompositeEnv::IsInstanceOf(name)` reached `CompositeEnvWrapper::IsInstanceOf`,
while an explicit base call like `PessimisticTransaction::PopSavePoint()`
never found the method the class inherits. In C and C++ a method is now the
class's only when its qualified name is the class's plus the method's, and a
call written through a class asks C++ lookup, bases included.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A class, struct or union whose declaration writes its name in a scope —
leveldb's `struct SkipList<Key, Comparator>::Node { … }`, rocksdb's
`struct BlockBasedTable::Rep { … }` — was indexed as `<anonymous>`, so its
members belonged to no class anyone names and a call on a `Node*` or `Rep*`
was guessed by the method's name. Such a class now takes its own name,
qualified under the written scope the way an out-of-line method is, and its
members are qualified under it, in the TS extractor and the kernel alike. A
specialization (`struct fmt::formatter<Answer>`) stays unnamed: named after
its template, it stood in for the template and every other specialization.
Constructions are recorded by the type's last name, so with `DBImpl::Writer`
named, leveldb's `new log::Writer(…)` in `DBImpl` methods went to it. A C++
construction now makes the class C++ lookup finds for the type as written at
the construction (matchCppConstruction): read with its template arguments
(`new Outer<int>::Inner(…)` makes an `Outer::Inner`), at 0.9, or 0.7 when it
is only the class of that name the file can see; an enum lookup finds; through
an alias the calling function declares, the class it names; and nothing for a
template parameter, a member of a type from outside the project or a local
alias of one. Of several classes or aliases the file declares under one
qualified name (the local classes of separate gtest TEST_F bodies, the members
of unnamed specializations), the one declared around the construction. A `<`
or `>` in parentheses inside template arguments is now read as a comparison.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Overloads of a C++ class's method were told apart by argument count and
string literals only, for a call without a receiver; a call on a typed
receiver always took the first one declared. rocksdb's `db->Get(ro, key,
&value)` reached the five-parameter `Get(…, std::string* timestamp)`, and
`Insert(ch)` on a `char ch` reached `Insert(const std::string& key, …)`.
- cppOverloadFor takes the overloads the count fits best, then the one the
arguments' types fit (cppArgumentTypeFit): a char, number, bool, null or
zero literal; the written type of a name the calling function declares,
of `&name` and of `name[i]`; or an expression's type (cppExpressionType).
The same type scores, as does the same kind of type; a character,
number or pointer for a string, a string or pointer for a number, and a
character or number for a pointer count against.
- cppMethodOf picks among a typed receiver's overloads the same way, of
the class C++ found when namesake classes share its name (leveldb's two
`RandomGenerator`s); a chained call `inner(…).name(…)` keeps the first,
since the arguments at its position are the inner call's.
- cppOverloadParameters reads an out-of-line method's defaults from its
declaration in the class (protobuf's `Printer::Annotate`, whose
`semantic` defaults only there).
- cppOverloadFit: a string literal, raw ones too, counts against a pointer
to anything but characters, a number and a character; comments are
dropped from argument and parameter lists (cppWithoutComments).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Name matching linked C and C++ names to declarations no other
translation unit can name: a class or alias a `.cc` file declares for
itself, its members, and what a source file's unnamed namespace
declares. In rocksdb, other test files' bare `Put(…)`, `Flush()` and
`dbfull()` reached column_family_test.cc's `ColumnFamilyTestBase`
thousands of times, and inlineskiplist.h's `Node*` receivers reached the
`Node` hash_linklist_rep.cc declares in `namespace { … }`.
- isVisibleAcrossFiles rejects such a candidate for a C or C++ ref
(isCppTranslationUnitLocal), as it does a `static` C function: a type
only source files declare; a member written inside such a class; an
out-of-line member of a type the member's own file sees only source
files declare (a scope its file sees no type of may be a namespace:
fmt's posix-mock-test.cc defines posix-mock.h's `test::close` beside
ostream-test.cc's own `struct test`); and, for the rest, what an
unnamed namespace of a source file holds. An alias of the scope keeps
the member, since the class may be declared anywhere.
- A source file's declarations are still seen by the file itself, by a
file that includes it, and by a header it includes below them
(rocksdb's version_set.cc includes version_set_sync_and_async.h after
its `FilePicker` classes).
- The unnamed-namespace spans come from a scanner that skips comments,
preprocessor lines (continued past a CRLF too) and string, character
and raw string literals.
- The typed lookups (cppMethodOf, resolveMethodOnType) take the
candidates the call's file can see first (cppVisibleFirst): rocksdb's
db_stress_shared_state.h and two benchmark `.cc` files each declare a
`SharedState`, and a call in db_stress_common.cc is the header's.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>