Receiver inference reads a C++ declaration as a type followed by the
name. What may stand between them was read as the type: in rocksdb's
`Iterator* const iter = iter_guard.get();` the type was `const`, which
names nothing, so the scan went on to an earlier function's `iter`, and
in protobuf's `io::EpsCopyOutputStream* PROTOBUF_NONNULL stream` the
type was a class named `PROTOBUF_NONNULL`. A name an annotation follows
(leveldb's `MemTable* imm_ GUARDED_BY(mutex_);`) was not read at all.
- The declarator regex (and the range-for type tail) reads past a cv-
qualifier or `__restrict` before or after a `*` or `&` (`T* const`,
`T const&`), the nullability annotations after one (abseil's and
clang's, and the ALL-CAPS macros c-cpp.ts already blanks for the
parser), and an ALL-CAPS annotation macro's call after the name.
- matchCppDeclarator hands the readers the type without those
annotations (cppDeclaratorType); cv-qualifiers stay, since what reads
a type reads past them. An ALL-CAPS word counts as an annotation only
after a `*` or `&`, so a type's own name stays (`OS_HANDLE* const h`),
and template arguments are kept as written.
- The call's own file, its same-named header, a class's members and a
range-for's variable are read that way; the local-name check for bare
calls reads past the qualifiers too.
- #2533's test that a method's parameter is no member used `T* const`
locals, which the scan couldn't read; it now declares no local, since
one the scan reads decides first.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Receiver inference read back through the call's file and the header of the
same name only, so a member of a class declared elsewhere — rocksdb's
DBImpl is declared in db_impl.h, its methods in db_impl_write.cc,
db_impl_compaction_flush.cc, db_impl_open.cc and more — was never read, and
calls on it (`versions_->GetColumnFamilySet()`, `mutex_.Lock()`) were
guessed by the receiver's name.
- Where the scan leaves the calling function, the name is looked up as a
data member of the function's class before anything outside it
(cppCallersMember): the class's own declaration lines wherever it is
declared (the declaration in the caller's own translation unit, of
classes sharing a name), then the classes it derives from through their
base edges, skipping a base that depends on the template's parameters.
A gtest `TEST_F` / `TEST_P` body is a method of a class derived from its
fixture, so `db_` there is the fixture's.
- The member goes into the declaration record as the caller's own, with
its class as the lookup site, so #2493's lookup, the library-type gate and
#2440's held-type path apply. A member of a template parameter's type,
or of an alias of one, and a name the scan saw an unreadable declaration
of, stay as before. (isCppTemplateDependentType now also counts a std
trait of a parameter: protobuf's `using MapFieldBase =
std::conditional_t<kIsMutable, …>`.)
- cppDataMemberIn reads only declarations outside parentheses and not
followed by an argument list (method declarations have no nodes, so their
parameters are in the class's lines), skips nested functions and classes,
follows block comments across lines, and blanks string literals.
- A member declared below an inline method that uses it, and a member of
the class itself rather than of a class nested in it, are found the same
way; cppExpressionType's own member fallback became redundant.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>