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>
#2493 resolved calls on declared pointers, values and references by C++
lookup. Calls whose receiver type came from an initializer or a return
type still went by the type's last name:
- An `auto` local now has the type its initializer has, as written where
that is declared: a cast or smart pointer written out
(`std::unique_ptr<Iterator>(…)`, `static_cast<InstrumentedMutex*>(arg)`),
`new` / `std::make_unique` / a construction of a class, or what a
function or method is declared to return (`txn->GetIterator(…)`), with
the initializer read to its `;` across lines. Its calls then go through
matchCppDeclaredTypeCall from that site. A type that depends on a
template parameter (`T*`, `std::shared_ptr<T>`) is left to the old path.
- A chained call `a->b()->c()` looks `c` up on the class C++ finds for the
written return type of `b`, through a smart pointer too
(matchCppChainOnWrittenType), before the last-name path.
- A type from outside the project gets no project method: a std/abseil
library type (#2413's gate, now applied before the last-name match, so a
namesake class like fmt's own `ostream` no longer wins), and a
capitalized type under a namespace the project declares no type in
(`absl::Status`, which reached `upb::Status::ok`).
- A bare call in a method types an `auto` local from the class's own or
inherited method; a `template <…>` header followed by macros or
specifiers still names its parameters; a trailing annotation macro
(`T* PROTOBUF_NONNULL`) is no part of a return type; and memoized
`auto` types are worked out with a fresh depth budget, so a deep chain
that reads a local first no longer leaves it untyped for its other calls.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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>