Repository navigation
fix(cpp): a -> call on a smart pointer or optional reaches the type it holds - #2440
Merged
Merged
Conversation
…owercase name calls that type's own member A C++ call on a variable, parameter or member the calling function or its class declares as a namespace-qualified type the project does not declare, with a lowercase name (std::string, std::vector, absl::flat_hash_set, ...), no longer falls through to Strategy 3's guess by the method's name. leveldb's `std::string saved_key_; saved_key_.clear()` reached Slice::clear, protobuf's `std::string proto; proto.append(...)` LeftoverBuffer::append. The gate applies to `.` on a value and `->` through a raw pointer; `->` on a smart pointer, iterator or optional still reaches the element type. It skips declarations it cannot trust: read from outside the caller's function and class (including a class nested in the caller's), contradicted by the call (`.` on a pointer), shadowed by an unreadable re-declaration (`auto`, range-for, structured binding), unqualified, with unbalanced `<>` (the tail of a multi-line declaration), or a `_t` name (scalars, tags, traits like std::conditional_t). A `std::` name counts as the project's only when the project declares it in `std` (vendored googletest's testing::internal::string). Receiver inference now blanks comments before matching declarations, so `// ... non-null imm_` no longer types leveldb's imm_ as `null`. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…to claude/eloquent-wozniak-cc7d7d
… it holds A C++ call made with `->` on a receiver the calling function or its class declares as a `std::unique_ptr<T>`, `std::shared_ptr<T>` or `std::optional<T>` now resolves on `T`: on the class C++ name lookup finds for it where the call is written, or on a class that class derives from. Before, receiver inference read the type as `unique_ptr`, found no method, and the call fell through to the guess by method name: rocksdb's `std::unique_ptr<Iterator> iter; iter->Valid()` reached `ArenaWrappedDBIter::Valid`. The held type goes through #2407's lookup (`cppClassWritten`, extracted from `matchCppSupertype`), not `resolveMethodOnType` on its last name: rocksdb has 22 classes named `Iterator`, and the one the calls mean inherits its methods from `IteratorBase`, so a last-name lookup reaches `MemTableRep::Iterator`. The method is taken from that class or its bases through the class's own extends edges (`getSupertypeNodes`). A template parameter, a library type or an alias of one gets no edge; a type the project doesn't declare, or an alias the lookup can't follow, leaves the call as it was. The namespace-macro helpers move from name-matcher.ts to cpp-namespaces.ts unchanged, so name-matcher can import cpp-supertypes without a cycle. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ak-cc7d7d # Conflicts: # CHANGELOG.md # src/resolution/name-matcher.ts
…ak-cc7d7d # Conflicts: # CHANGELOG.md
…ak-cc7d7d # Conflicts: # src/resolution/name-matcher.ts
…ak-cc7d7d # Conflicts: # CHANGELOG.md
bompus
added a commit
to bompus/codegraph
that referenced
this pull request
Oct 10, 2026
…eceivers (#430) * fix(context): call paths label synthesized hops like explore, not as events (colbymchenry#2428) Two surfaces label a synthesized (dynamic-dispatch) `calls` hop: codegraph_explore's Flow and dynamic-dispatch links plus the codegraph_node trail (`dynamic: interface → impl @file:line`), and ContextBuilder's "## Call paths", which `codegraph context` prints (`→[…]`). Each kept its own list. The call paths' list stopped at callback, react-render, jsx-render, vue-handler, http-client, queue-job and socket messages, so every other synthesizer fell through to `event ${m.event ?? ''}`: an interface-impl hop read `→[event @src/pdf-exporter.ts:5]`, and so did a C++ override, a redux thunk, a gin middleware chain or a closure collection. The wording now lives once, in src/graph/synthesized-hop.ts (`describeSynthesizedHop`: a summary, the detail only the call paths print, and the wiring site), as AGENTS.md asks of a derivation two surfaces render. ToolHandler renders `dynamic: <summary> @site`, the call paths `<summary><detail> @site`. Explore and the node trail print exactly what they printed before; the call paths keep their wording for the kinds they already labelled, including the queue name, a socket's direction and the HTTP clause, and now name the rest the way explore does. A bus event reads `bus event` there too, and the section's footnote no longer says hops are marked `[callback/event …]`. The helper also holds the wording colbymchenry#2414 gives a Go method a struct gets by embedding (`metadata.promotedInto`), so that arm moves here too. The unused long `label` form of ToolHandler.synthEdgeNote (last read by the removed codegraph_trace) is dropped. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(sync): a route rendering a same-file lazy value follows its module, and a new component gets its JSX edges (colbymchenry#2452) A route that renders `const Docs = lazy(() => import('./pages/Docs'))` binds to the component that module exports, and to the declaration while the module is missing or exports none (colbymchenry#2400's declaredComponent). The reference had resolved, so no failed-ref retry saw it, and CG-33's rebind reached it only when the module's component shared the declaration's name: a sync that added the module, gave it its default export or moved that export left the route on its old answer. A data router's `lazy` route kept a moved default export the same way. FrameworkResolver.lazyModules names the modules a route's answer reads, including the files a barrel forwards it through (colbymchenry#2436); React implements it for lazy-import references and the same-file lazy values Pattern 1 reads. After the CG-33 rebind, sync puts back the route references whose modules it added or changed (reopenRouteModuleReaders), in the order their edges were written, for the orphan sweep. The jsx-render refresh read only each changed file's own content, so a new component without JSX of its own never got the edge from `<Team />` in an unchanged file. A sync whose definition delta holds a JSX-renderable node now refreshes synthesis. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(cpp): a range-based for loop's variable has the type the loop declares (colbymchenry#2439) C++ receiver inference read a declaration only when the declared name was followed by `;`, `=`, `,`, `)`, `[`, `{` or `(`, so a range-based for's `Type name :` was never read. In protocolbuffers/protobuf's conformance runner, `suite->SetVerbose(…)`, `suite->RunSuite(…)` and `suite->GetFailureListFlagName()` inside `for (ConformanceTestSuite *suite : suites)` fell through to Strategy 3's guess by the receiver's name, and in RocksDB `log->file()` on a `log::Writer` loop variable went to `BlobLogWriter::file`. A line the declarator regex doesn't match is now read as a range-based for when its `for (` header declares the receiver before a single `:` (not `::`), and the loop's variable is taken only when the call is inside the loop's body: past the header, up to the `}` closing a braced body or the end of a single-statement body, with comments and literals skipped. After the loop, or in a later function, the same name is another variable, and the scan goes on as before. A loop over `auto` elements has nothing to read and keeps the existing fallback. Bit-fields and the scope operator are never read as a loop's declaration. A loop's declaration is recorded like any other (colbymchenry#2413's noteCppDeclaration), so the std-type gate applies to `for (const std::string& s : …)` too, and a loop that doesn't enclose the call still marks the receiver as shadowed. With colbymchenry#2421, the 13 conformance runner calls reach ConformanceTestSuite's methods at 0.9. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(js,ts): a default import is what `export default` declares, not the first exported function (colbymchenry#2432) A default import resolved to the module's first exported function or component whenever the `export default` statement wrote a declaration (`export default function Vans()`, `export default class Store`): only a separate `export default NAME` statement was read. A React Router 6.4+ data-router page exports its `loader` or `action` above the page component, so `import Vans, { loader as vansLoader } from './Vans'` bound the route `/vans` (and every call through `Vans`) to `loader`. The resolver now reads the file's own `export default` statement from comment-stripped code. A declaration it writes is found by position, a binding it names by name, and an anonymous function, class or arrow function is no node at all (the guess took an exported function beside it, or one nested inside it). The statement also beats an exported styled/memo component above it. Every line-initial statement is read and the first one that names a node of the file wins, so template text that only reads like a statement names nothing. Strings are not masked: a masker loses step on a template whose `${…}` holds another template or a regex with a backtick, and on outline it blanked the real statement below one. Only an expression default (`memo(Card)`, `new Service()`), a binding the file doesn't declare, or no statement keeps the old guess. `require('./x').default` (colbymchenry#2433) reads the same statement first, so it reaches the declared default too. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(cpp): a `->` call on a smart pointer or optional reaches the type it holds (colbymchenry#2440) A C++ call made with `->` on a receiver the calling function or its class declares as a `std::unique_ptr<T>`, `std::shared_ptr<T>` or `std::optional<T>` now resolves on `T`: on the class C++ name lookup finds for it where the call is written, or on a class that class derives from. Before, receiver inference read the type as `unique_ptr`, found no method, and the call fell through to the guess by method name: rocksdb's `std::unique_ptr<Iterator> iter; iter->Valid()` reached `ArenaWrappedDBIter::Valid`. The held type goes through colbymchenry#2407's lookup (`cppClassWritten`, extracted from `matchCppSupertype`), not `resolveMethodOnType` on its last name: rocksdb has 22 classes named `Iterator`, and the one the calls mean inherits its methods from `IteratorBase`, so a last-name lookup reaches `MemTableRep::Iterator`. The method is taken from that class or its bases through the class's own extends edges (`getSupertypeNodes`). A template parameter, a library type or an alias of one gets no edge; a type the project doesn't declare, or an alias the lookup can't follow, leaves the call as it was. The namespace-macro helpers move from name-matcher.ts to cpp-namespaces.ts unchanged, so name-matcher can import cpp-supertypes without a cycle. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(sync): a barrel edited to forward another page moves the routes that lazily load it --------- Co-authored-by: Colby Mchenry <me@colbymchenry.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
colbymchenry
added a commit
that referenced
this pull request
Oct 10, 2026
… own or inherited method (#2479) A C++ call written with no receiver, or through `this->`, inside a method was matched by its name alone, so another class's method of that name won by file proximity: protobuf's generated `Api::operator=` calling `InternalSwap(&from)` reached `Any::InternalSwap`, `Any::InternalSwap`'s inherited `GetArena()` reached `Arena::InternalHelper::GetArena`, and googletest's `~linked_ptr() { depart(); }` reached `linked_ptr_internal::depart`. matchMethodCall now resolves C++'s implicit `this` the way it resolves a typed receiver: the calling method's class, then the classes it derives from (its own base edges, through #2440's cppMethodOf), then the classes it is nested in, with the overload the arguments fit. The call's shape is read at its column (the extractor drops `this->` and some receivers it can't spell). A free function, a parameter or local of the name, a dependent base reached by a bare call, a namesake class in another translation unit and a member a macro may declare all leave the call to the name strategies. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
colbymchenry
added a commit
that referenced
this pull request
Oct 11, 2026
… type of its range (#2492) #2439 typed a range-for variable when the loop names its type. A loop over `auto` elements names none, so receiver inference kept scanning as for an `auto` local with an unreadable initializer: it reached an earlier declaration of the same name, often another variable's, or fell through to the guess by the receiver's name. facebook/rocksdb's db_impl_write.cc loops `for (auto cfd : cfds)` over a `const autovector<ColumnFamilyData*>&`. The loop's range is now read from its header and typed as written, template arguments and all, since the index keeps only a return type's last name: - a name the calling function declares, or a data member of its class read from the class's own lines (`versions_` from db_impl_write.cc); - an `auto` local's initializer; - `*x`, `x.m`, `x->m`, `this`, and calls whose declaration writes their return type, free or on the object's class (own or inherited); - aliases followed with their template arguments (cppAliasedTypeWritten); - the element: a standard or abseil sequence container's first template argument, protobuf's RepeatedPtrField and rocksdb's autovector's, or what a project class's begin() returns, dereferenced (ColumnFamilySet's iterator's operator* gives ColumnFamilyData*). Nullability annotations are dropped. The element goes through the same declaration record as any declared receiver, so the typed path, #2413's gate for a standard-library element and #2440's held-type lookup for `->` on a smart-pointer element apply unchanged. Classes are looked up with #2440's cppClassWritten, methods through its cppMethodOf, and matchCppHeldTypeCall now reads its held type through the shared cppStdHeldType. A/B on main 8a5ba3d, site-keyed: leveldb identical; fmt +1 / 3 retargeted; rocksdb -68 / +173 / 104 retargeted / 231 confidence only; protobuf -13 / +25 / 7 / 59. Every removal and retarget was a wrong guess; 4 of rocksdb's additions reach MemTableRep::Iterator through the typed path's last-name lookup, as a declared `Iterator* it` does on main. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
colbymchenry
added a commit
that referenced
this pull request
Oct 11, 2026
…e class its type names, not a namesake (#2493) Receiver typing resolved a C++ call on a variable, parameter or member by the declared type's last name: `Iterator* iter; iter->Valid()` became resolveMethodOnType('Iterator', 'Valid'), which takes any class named `Iterator` that declares `Valid`. facebook/rocksdb has 22 such classes, and the `Iterator` the code means inherits `Valid`, `Next` and `key` from `IteratorBase`, so nested namesakes like `MemTableRep::Iterator` took the calls (google/leveldb's went to `SkipList::Iterator`). A declaration the calling function or its class writes, called with the operator it takes (`->` on a pointer, `.` on a value or reference), now goes through the same C++ lookup #2440 uses for smart pointers' held types (cppClassWritten from the caller's scopes, then the file's `using`s, aliases followed) and cppMethodOf (the class's own method, else one through its own base edges): - An alias #2399 followed from the caller (function-local typedefs included) is the class it names, by qualified name. An `auto` loop's element type (#2492) is looked up where its range's type is written. - A class found only as the one class of that name the file can see, a template parameter's type, a library type, or a type nothing visible declares: the call goes on as before. - A class with no such method gets no edge, unless it is a class template or a base on the way is one the index doesn't link (a `decltype(…)` base, an alias each `#if` arm declares differently), which may declare it: then as before. - Declarations read from elsewhere, or past one the scan couldn't read, are unchanged. cppClassWritten now tries the file's `using`s before the global scope: protobuf's upb generator writes `using google::protobuf::Descriptor;` inside its namespace, and the PHP extension declares a global C struct `Descriptor` in a header. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
A C++ call made with
->on a receiver declared as astd::unique_ptr<T>,std::shared_ptr<T>orstd::optional<T>now links toT's method, or to a method of a classTderives from.Before, receiver inference read the declared type as
unique_ptr, found no method on it, and the call fell through to Strategy 3's guess by method name and receiver words:std::unique_ptr<Iterator> iter; iter->Valid()reachedArenaWrappedDBIter::Valid.std::shared_ptr<FileSystem> fs; fs->GetFileSize(…)reached the test doubletest::StringFS::GetFileSize.std::unique_ptr<Transaction> writer; writer->Put(…)reachedSstFileWriter::Put.std::unique_ptr<FieldGeneratorBase> generator; generator->GenerateMembers(…)reachedMapFieldGenerator::GenerateMembers. The Kotlin generator'sstd::unique_ptr<FileGenerator>reachedjava::FileGenerator.This is the follow-up #2413 left open. It uses that PR's
CppReceiverDeclaration(raw,callers,shadowed) andcppMemberOperator. It applies only when all of these hold:->;std::-qualified, asisCppLibraryTypereads it.Why the held type is looked up as C++ does, not by its last name
The plan was
resolveMethodOnTypeonT's last segment. Measured on rocksdb, that sends about 3,800 calls to the wrong class with a 0.9 typed edge (2,998 retargets, 611 additions, 154 confidence bumps):Iteratornames 22 classes in rocksdb:MemTableRep::Iterator,SkipList::Iterator,InlineSkipList::Iteratorand gtest's generatorIterators among them.Iteratorthese calls mean declares none ofValid/Next/Seek/key; it inherits them fromIteratorBase.getMethodMatches('Iterator', 'Valid')findsMemTableRep::Iterator::Valid, and the supertype walk never runs.So
Tgoes through #2407's C++ lookup, now shared ascppClassWritten, from where the call is written:usingdeclarations, directives and namespace aliases;The method is then taken from that class, or from its bases through the class's own
extendsedges. A new node-keyedgetSupertypeNodesreads those edges; the name-keyedgetSupertypesunions every same-named class's bases.Each shape, decided by measurement
std::unique_ptr<IterType>)std::deque<…>,std::vector<…>), or an alias of one (using VectorWalPtr = std::vector<…>)Types the project doesn't declare keep their guesses. In the probe, those guesses were about half right:
unittest::TestAllTypes→Message::SpaceUsedLong;std::unique_ptr<Rep> rep_→ methods of an out-of-linestruct BlockBasedTableBuilder::Rep, which the index names<anonymous>;ZeroCopyInputStream→ a test stream, whilePROTOBUF_EXPORThid the class. fix(cpp): a class keeps its members past attribute macros the parser can't read #2449 now indexes it, so those 24 calls resolve through this PR toZeroCopyInputStream/ZeroCopyOutputStream.An alias the lookup can't follow keeps its guess. rocksdb's
port::Threadisstd::threadinport_posix.handWindowsThreadinport_win.h. Only an alias of a library type or of a template parameter is silenced.Iterators: unchanged. No receiver in the five repos is declared as
std::…<T>::iteratorand called with->. All 31->calls on an iterator go through aliases:ExpectationSet::const_iterator;ColumnFamilySet::iterator, which is a project class.Reading the element type through an alias needs the alias's template arguments, which
cpp-type-aliases.tsdoesn't keep. Left as a follow-up.->on a raw pointer to a smart pointer: unchanged.std::unique_ptr<T>* p; p->reset()calls the holder's own member, and fix(cpp): a receiver declared as a std or other outside type with a lowercase name calls that type's own member #2413's gate already silences it. fix(cpp): a receiver declared as a std or other outside type with a lowercase name calls that type's own member #2413's gate log has 549 such calls in rocksdb and 1 in protobuf. All arereset/get/release/swap/has_value/value/emplace. A test pins it.Validation
Setup.
codegraph init -ywith the native kernel, on this Windows box.b8b3eb97, which includes fix(cpp): a receiver declared as a std or other outside type with a lowercase name calls that type's own member #2413, fix(c,cpp): a file named like a test is visible to the code that includes it #2421, fix(cpp): a class keeps its namespaces and enclosing class past code the parser misreads #2426, fix(c,cpp): a file named like a Google unittest, foo_unittest.cc, is a test #2434, fix(cpp): a range-based for loop's variable has the type the loop declares #2439, fix(cpp): a receiver declared with nested template arguments calls its own class's method #2443, fix(cpp): a call written from the global scope links the declaration of exactly that name #2445, fix(c,cpp): a struct, class, union or enum defined in a variable declaration is indexed #2446 and fix(cpp): a class keeps its members past attribute macros the parser can't read #2449; the other arm is this branch merged with it. Both use one kernel built from that source.ed199e60,9a1d4bd9(fix(cpp): a class keeps its members past attribute macros the parser can't read #2449),16344af8(fix(c,cpp): a file named like a Google unittest, foo_unittest.cc, is a test #2434, protobuf only),e47cb258(fix(cpp): a receiver declared with nested template arguments calls its own class's method #2443, fix(cpp): a call written from the global scope links the declaration of exactly that name #2445, fix(c,cpp): a struct, class, union or enum defined in a variable declaration is indexed #2446) andb8b3eb97(fix(cpp): a range-based for loop's variable has the type the loop declares #2439).ZeroCopy*Streamcalls above,msg->GetReflection()turning from confidence bumps into additions where main stopped guessing, andDynamicMessageFactory's override (below). It also dropped one rocksdb removal: main no longer guessesWindowsThread::joinfor astd::optional<std::thread>.forloop variables, so 8 rocksdb calls on smart-pointer loop variables now take this path. Seven arefor (const std::shared_ptr<Cache>& cache : …)in db_block_cache_test.cc, which go from 0.8 to 0.9 on the sameCache::*targets. The eighth isfor (const std::shared_ptr<TableFactory>& tf : …) tf->Name(), newly linked toCustomizable::Name.d8a7f868+ fix(cpp): a receiver declared as a std or other outside type with a lowercase name calls that type's own member #2413) gave identical changed sites once qualified names are compared without the namespaces fix(cpp): a class keeps its namespaces and enclosing class past code the parser misreads #2426 restores. The one exception is protobuf'sstd::unique_ptr<TestMapField>. On the old base its alias led to aMapFieldwhose namespace the index had lost, so the call kept its guess. With fix(cpp): a class keeps its namespaces and enclosing class past code the parser misreads #2426 the alias is followed, and the sameMapFieldBase::GetRepeatedFieldtarget goes from 0.65 to 0.9.What didn't change.
matchCppSupertyperefactor changes nothing on real data.->call on a caller-ownedstd::holder.Removed (rocksdb 34; protobuf and fmt none), every one read:
StressTest::TestIterateImpl'sstd::unique_ptr<IterType>→ArenaWrappedDBIter::*;BlockBasedTable::PutDataBlockToCache'sstd::unique_ptr<TBlocklike>→Block::own_bytesandBlockBasedTable::ApproximateMemoryUsage.std::unique_ptr<std::deque<BlockHandleInfo>>→Block::size/BlockBuilder::empty/autovector::emplace_back;std::optional<std::vector<WriteBatch>>→autovector::emplace_back.std::unique_ptr<VectorWalPtr>calls →CompactionInputFiles::size.Retargeted and added.
An independent check re-derived each target from the declaration's spelling, the caller's scopes and the
extendsedges. It agrees for rocksdb's 6,913 + 2,450 class sites and for every class site in fmt and protobuf; the 29 protobuf sites fix(cpp): a class keeps its members past attribute macros the parser can't read #2449 added and the 8 rocksdb range-for sites fix(cpp): a range-based for loop's variable has the type the loop declares #2439 added were read by hand.The ~1,000 it can't place go through:
InternalIterator→InternalIteratorBase,LogFile→WalFile,MergerMaxIterHeap→BinaryHeap,UserDefinedIndexBuilder→IndexFactoryBuilder);using namespace ROCKSDB_NAMESPACE;(std::unique_ptr<DB>in tools and examples);TestItem,CountingFileReadListener).I grouped them by declaration and target and read each group.
Read by hand:
All were right. The old targets were implementations, test doubles or namesakes:
ModelDB::CompactRangefor astd::unique_ptr<DB>;CuckooTableReader::NewIteratorfor astd::unique_ptr<TableReader>;ChargedCache::Lookupfor astd::shared_ptr<SecondaryCache>;BlobFileBuilder::Finish;KeyGenerator::Nextfor astd::unique_ptr<LockTracker::KeyIterator>.DynamicMessageFactory: on mained199e60, protobuf'sstd::unique_ptr<DynamicMessageFactory>reached the base virtualMessageFactory::GetPrototype, because the index stored the class's own override under a namePROTOBUF_NONNULLmangled. fix(cpp): a class keeps its members past attribute macros the parser can't read #2449 fixed the name, and the call now reachesDynamicMessageFactory::GetPrototypeitself.Cost. Process CPU time (all threads) for a full index, interleaved runs on the earlier base:
No measurable cost; fewer calls reach the by-name search.
Code
src/resolution/name-matcher.ts:matchCppHeldTypeCallandcppMethodOf, called frommatchMethodCallright after fix(cpp): a receiver declared as a std or other outside type with a lowercase name calls that type's own member #2413's gate.src/resolution/cpp-supertypes.ts:matchCppSupertype's lookup is nowcppClassWritten(written, site), parameterized by where the name is written: scope, file and line, template parameters, and the class it can't be.matchCppSupertypeis a thin wrapper over it, with unchanged behavior.src/resolution/cpp-namespaces.ts(new):cppNamespaceAliasesandcppMacroNamespaceFrames, moved byte for byte out of name-matcher.ts. That way cpp-supertypes.ts no longer imports name-matcher, and name-matcher imports cpp-supertypes without a cycle.src/resolution/types.tsandindex.ts:getSupertypeNodes(id), memoized per supertype generation likegetSupertypes.src/resolution/cpp-type-aliases.ts:cppTemplateArguments.Tests
New file:
__tests__/cpp-smart-pointer-receiver.test.ts, 8 tests. It covers:MemTableRep::IteratorandArenaWrappedDBIteras decoys;shared_ptr,optional, a parameter, and a member the class declares in its header, with decoys named after each receiver;using namespace, and a class local to the function;->on astd::unique_ptr<T>*.Against main, 6 of the 8 fail. The other 2 pin unchanged behavior.
All 19 C/C++ test files pass on the merged branch (216 tests), among them fix(cpp): a base class links to the class C++ name lookup finds, not a namesake #2407's
cpp-base-class-lookupagainst the refactored lookup, and fix(cpp): a class keeps its namespaces and enclosing class past code the parser misreads #2426's and fix(c,cpp): a file named like a test is visible to the code that includes it #2421's new suites.tsc --noEmitis clean.Full suite on the merged branch. This box ran at 100% CPU, with other sessions' suites and indexing alongside.
EBUSYtemp-dir removals (2), in 17 load-sensitive files such assync,wal-deferral,worktree-detection,mcp-*,git-index-currencyandfunction-ref.--testTimeout=120000, those 17 files pass: 938 tests, 0 failed, 1 skipped.Overlap
TestMapFieldcall now gets its typed edge, as described above. fix(cpp): a class keeps its members past attribute macros the parser can't read #2449 (merged) made theZeroCopy*StreamandDynamicMessageFactorycalls resolvable. fix(cpp): a receiver declared with nested template arguments calls its own class's method #2443 (nested template arguments), fix(cpp): a call written from the global scope links the declaration of exactly that name #2445 (::-qualified names) and fix(cpp): a range-based for loop's variable has the type the loop declares #2439 (range-forloop variables) touch the same files. Each sync conflicted only on name-matcher's import line or CHANGELOG, and the A/B on top of them is unchanged apart from fix(cpp): a range-based for loop's variable has the type the loop declares #2439's eight range-forsites.Follow-ups (not in this PR)
Iterator* it; it->Valid()resolves by the last segment, so in rocksdb 1,364 of the 44,915 calls that path types reachMemTableRep::Iteratorrather than the class C++ lookup finds. Routing that path throughcppClassWrittenandcppMethodOfis the fix.->calls: 60 throughScopedArenaPtr<InternalIterator>, an alias template ofstd::unique_ptr, and 28 through block_test.cc'stypedef std::unique_ptr<DataBlockIter> IterPtr;. Following it needs the alias's template arguments.absl::StatusOr<T>andabsl::optional<T>also hand->toT. protobuf has about 75 such calls, most unlinked today.f(std::shared_ptr<A> a, std::unique_ptr<B> b),b's type reads asstd::shared_ptr<A> a, std::unique_ptr<B>. That's 31->calls in the probes, 1 of them on a smart pointer.🤖 Generated with Claude Code