Repository navigation
fix(cpp): a qualified receiver type never reaches a class that only shares its last name, and rocksdb::DB is RocksDB's own - #2567
Merged
Conversation
…hares its last name, and rocksdb::DB is RocksDB's own 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>
colbymchenry
added a commit
that referenced
this pull request
Oct 15, 2026
…there, and a far lowercase outside type reaches no namesake (#2569) 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>
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.
What
A C++ receiver declared with a namespace-qualified type should call a member of a class that qualified name can be, never one of a class that only shares its last name. Most of this landed on main while this branch waited:
autovariable or a chained call reaches the class C++ finds, not a namesake #2532:matchCppDeclaredTypeCallruns before the last-name match;isCppOutsideQualifiedTypenullsabsl::Status s; s.ok().->on an absl::StatusOr reaches the class it holds; a using-declared member of a dependent base is found #2539:->onabsl::StatusOrreaches the held type.I re-checked on main eec9288, and the original reproduction passes there. This PR fixes the three gaps left over:
autovariable or a chained call reaches the class C++ finds, not a namesake #2532). RocksDB declares its classes innamespace ROCKSDB_NAMESPACEunder#define ROCKSDB_NAMESPACE rocksdb, so the index names themROCKSDB_NAMESPACE::DB.cppProjectScopeNamesonly held segments of indexed type names, sorocksdb::DB* db; db->Put(…)was judged a call on an outside type and got no edge. The same happened toROCKSDB_NAMESPACE::Cachein a tree that declaresnamespace rocksdbitself. The set now adds the name a single-name macro stands for, in both directions, throughcppNameMacros, the#define X ymapcppNamespaceMacrosalready builds.isCppLibraryType(fix(cpp): a receiver declared as a std or other outside type with a lowercase name calls that type's own member #2413) took a non-stdlowercase type as the library's only when no project type had its name at all, soabsl::flat_hash_set<int> seen; seen.insert(…)reached a project's ownutil::flat_hash_set::insert. It is now also the library's when every project type of that name is a namesake (cppFindsOnlyNamesakes)._status.ok()in itsRETURN_IF_ERRORmacro body reachedupb::Status::ok.auto bytes = ::hpb::Serialize(…); bytes.value(), typed fromhpb.h'sabsl::StatusOr<absl::string_view>, reachedhpb::StatusOr::value. The other is a namespace the project uses. Its generatedDescriptorProto::ExtensionRange, which the index doesn't hold (descriptor.pb.his over the size cap), reachedDescriptor::ExtensionRange::options. Such a call now gets no edge, asisUndeclaredTypeNamerules where no class shares the name. A->on a value still goes to the name strategies, asisCppArrowThroughOutsideTypedoes, and a template parameter's member is left alone.cppFindsOnlyNamesakesasks whether every C/C++ type of the last name, and every owner of a method of the called name, has a qualified name that ends unlike the written one, neither being the other's tail. Method owners count because the index can hold a class's methods without the class, as with generated protobuf messages. C++ lookup only adds scopes in front of a written name, and the index drops some (a namespace a macro opens, one lost to a parse error), hence the tail rule both ways. Namespace aliases are expanded, and macro-named namespaces are read both ways. A name a project type qualifies (ns::Derived::Node), an unqualified name and an unbalanced<>are never judged, and neither is a name nothing in the project shares, which main's own rules already handle.Validation
Tests:
__tests__/cpp-outside-type-receiver.test.ts, 9 tests.rocksdb::DB::PutandROCKSDB_NAMESPACE::Cachegetting no edge,absl::flat_hash_setreachingutil::flat_hash_set::insert, andDescriptorProto::ExtensionRangereachingDescriptor::ExtensionRange::options. The 5 guard tests pass on main too.Mutations: each guard switched off fails exactly its own test.
absl::flat_hash_setreproductionDescriptorProto::ExtensionRangereproductionowners.length > 0grpc::ClientReader)DescriptorProto::Clearbeside anupb::DescriptorProto)typename PolicyT::Payloadfmt::detail::buffer/u::flat_hash_setthroughnamespace u = util;ns::Derived::Nodeinherited fromBase->on a value → name strategiesfolly::Optional<Widget>holderThe one guard-less mutation, the gate extended to lowercase names, changes nothing: those stay with
isCppLibraryType.Site-keyed A/B (main eec9288 vs this branch, kit
%TEMP%\cgstd,diff-sites.mjs):All 7 protobuf removals were wrong edges:
hpb_generator/tests/test_generated.cc:488,:495:bytes.value()onauto bytes = ::hpb::Serialize(…), anabsl::StatusOr<absl::string_view>, reachedhpb::StatusOr::value.src/google/protobuf/compiler/importer_unittest.cc:567,569,579,581:range.options()onconst DescriptorProto::ExtensionRange& range, the generated message, reached the descriptor class'sDescriptor::ExtensionRange::options.src/google/protobuf/stubs/status_macros.h:33:_status.ok()onconst absl::Status _statusinRETURN_IF_ERRORreachedupb::Status::ok.A logging probe on main listed every qualified-type site main still sends down the last-name path. Besides these 7, the only owner-mismatch sites left are right ones:
DefaultFieldComparator→SimpleFieldComparator::set_treat_nan_as_equal;typename PolicyT::Payloadtemplate members, kept on purpose;hpb/arena.h'sbackend::Arena→hpb::Arena(3 sites), an alias question for the found-class path rather than an outside type.The rocksdb and lowercase gaps don't occur in these five repos: rocksdb itself writes
ROCKSDB_NAMESPACE::(8,602 uses against a handful ofrocksdb::calls). The fixtures pin them.Overlap / convergence
autovariable or a chained call reaches the class C++ finds, not a namesake #2532'sisCppOutsideQualifiedTypekeeps its namespace-head test and gains the macro mapping throughcppProjectScopeNames, which the held-type and chain paths also read. fix(cpp): a receiver declared as a std or other outside type with a lowercase name calls that type's own member #2413'sisCppLibraryTypeand the new gate sharecppFindsOnlyNamesakes.autovariable or a chained call reaches the class C++ finds, not a namesake #2532 / fix(cpp):->on an absl::StatusOr reaches the class it holds; a using-declared member of a dependent base is found #2539 landed, had its own outside verdict before the typed match. It was dropped for the above.CHANGELOG: #2413's and #2532's
[Unreleased]bullets were extended rather than given new ones, since neither is released yet (latest release 1.6.2), as #2530 did.🤖 Generated with Claude Code