fix(cpp): a qualified receiver type never reaches a class that only shares its last name, and rocksdb::DB is RocksDB's own (#2567)

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>
This commit is contained in:
Colby Mchenry
2026-10-15 03:29:12 +00:00
committed by GitHub
co-authored by Claude Opus 5.5
parent eec9288a56
commit 00d7430ebb
4 changed files with 526 additions and 8 deletions
+9
View File
@@ -81,6 +81,15 @@ export function cppMacroNamespaceHeads(context: ResolutionContext): string[] {
return [...cppNamespaceMacros(context).openers.values()].map((path) => path[0]!);
}
/**
* The project's macros that stand for a single name, by the macro's name:
* `#define ROCKSDB_NAMESPACE rocksdb`, so the namespace rocksdb declares as
* `namespace ROCKSDB_NAMESPACE` is `rocksdb` to code that names it.
*/
export function cppNameMacros(context: ResolutionContext): ReadonlyMap<string, string> {
return cppNamespaceMacros(context).aliases;
}
/** The line ranges of a C / C++ file each namespace macro opens, with the namespace path it opens. */
export function cppMacroNamespaceFrames(file: string, context: ResolutionContext): Array<{ start: number; end: number; path: string[] }> {
let memo = CPP_NS_FRAMES.get(context);