A C++ base class (`class DynamicMessage final : public Message`) was
resolved by its name alone, so a base named like a class of another
namespace, or of another language, bound to whichever namesake ranked
first. In protocolbuffers/protobuf, `DynamicMessage`, `MapEntry`,
`ZeroFieldsBase` and two arena_unittest classes derived from
`json_internal::ResolverPool::Message`; before #2397 they derived from the
PHP extension's C struct `Message`. The Go framework resolver claimed
those: protobuf ships Go examples, so the resolver is detected, its
`resolve()` runs for every language's refs (`languages` only gates
extraction), its PascalCase-struct rule found the C struct, and the
framework language gate lets C++ reach C. google/leveldb's and rocksdb's
iterators derived from an unrelated nested `Iterator` (`SkipList::Iterator`,
`WriteThread::WriteGroup::Iterator`), and leveldb's test comparators from a
`Comparator` struct that skiplist_test.cc declares for itself.
C and C++ inheritance refs now resolve in their own matcher
(cpp-supertypes.ts), ahead of every other strategy and with no
fallthrough, so no framework resolver, import strategy or name matcher sees
them, and only C and C++ declarations are candidates. The base is looked
up the way C++ looks it up:
- from the scope the deriving class is declared in, outwards, using the
lookup cpp-type-aliases.ts (#2399) already has: a class scope with its
bases, a qualified name a segment at a time, an alias followed to the
class it names; `::Base` from the global scope only;
- then through what the file writes before the class: namespace aliases
(`namespace _pbi = ::google::protobuf::internal;`), using-declarations
and using-directives, and fmt's macro-opened namespaces;
- a class a .cc/.c file defines belongs to that translation unit;
- a template parameter, or a member of one, names no class: no edge.
When none of that finds the class, the only visible C/C++ class with that
qualified tail is taken, since the index loses some classes' namespaces
(`class PROTOBUF_EXPORT FieldDescriptor`, classes after a partial
specialization); several are a guess, so no edge.
The base is read as written, template arguments included, so an alias
template (`bool_constant<…>`) is found. An alias that leads to no project
class (one of `std::integral_constant`, one each `#if` arm declares
differently) is linked itself, as before.
Validated with site-keyed edge diffs on leveldb, fmt, protobuf and
rocksdb, and redis, SDWebImage and gson as controls (dumps
byte-identical): first against main 02d22aeb, then again on main
dd706469 after #2405, #2408 and #2400 landed, with identical site
diffs (#2402, merged after, changes only Go's implements synthesis).
Every removed edge was wrong: 9 protobuf
`suite_.ReportFailure` guesses that reached `ForkPipeRunner`, cpp-override
edges from the wrong bases, and two rocksdb bases that are template
parameters.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>