fix(cpp): a call on an inherited member reached through a chain, like options.statistics->getTickerCount(…) on a RocksDB Options, links to the member's class (#2628)

Step 3 of member chains, after #2626 and #2627. The type of `x.member` was
read from x's class alone, so a member that class inherits was never found
and the call fell back to its bare name.

A temporary diagnostic on main measured why chain calls still fell through:
2,801 of 4,884 were a member declared in a base class. Nearly all were
rocksdb's `Options`, which declares no members of its own and inherits
`statistics`, `table_factory`, `listeners`, `prefix_extractor` and the rest
from `DBOptions` and `ColumnFamilyOptions`. The other causes are left for
later:
- a chain whose base can't be typed: 875;
- a class the index lacks, e.g. protobuf's generated `_impl_`: 626;
- a member found nowhere: 337.

cppExpressionType's member branch now uses cppInheritedDataMember, the
lookup the calling class's own members already use (#2533):
- the class's own declaration first;
- then the classes it derives from, through their base edges, to depth 4;
- a base that depends on the class's template parameters is skipped
  unless a using-declaration brings the name in.

Kit A/B against main e8ec7fd8:

| repo | removed | added | retargeted | same target, now typed |
|---|---|---|---|---|
| rocksdb | 1,410 | 634 | 392 | 184 |
| protobuf | 0 | 2 | 149 | 55 |
| leveldb, fmt, redis | identical | | | |

- Removed: the bare-name guesses on inherited std::shared_ptr and
  std::vector members, such as `options.table_factory.reset(…)` →
  FSWritableFilePtr::reset (654) and `options_.db_paths.emplace_back(…)` →
  autovector::emplace_back (256).
- Added: `options.statistics->getTickerCount(…)` → Statistics (432),
  `rate_limiter` → RateLimiter (51), `comparator` → Comparator (26).
- Retargeted: DummyOldStats -> Statistics, DB -> Env, ClockCacheShard -> Cache.
- protobuf's generated `this_._internal_metadata_.Clear<…>()`,
  `MergeFrom<…>` and `unknown_fields<…>` now reach InternalMetadata, a
  member every message inherits from MessageLite. Before, they reached the
  message's own `Clear` or `Any::MergeFrom`.

All 50 random sites read by hand (36 rocksdb, 14 protobuf) were correct.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Colby Mchenry
2026-10-17 02:05:39 +00:00
committed by GitHub
parent e8ec7fd847
commit e2b0cf5eea
3 changed files with 138 additions and 4 deletions
+6 -3
View File
@@ -10016,14 +10016,17 @@ function cppExpressionType(expr: string, at: UnresolvedRef, context: ResolutionC
return followed && cppSubscriptElement(followed, at, context);
}
if (!text.endsWith(')')) {
// `compact_->sub_compact_states`: a data member of the object's class;
// `it->second` through a map's iterator: the member of the `std::pair`.
// `compact_->sub_compact_states`: a data member of the object's class, or
// of a class it derives from — rocksdb's `Options` declares none of its
// own, and inherits `table_factory`, `statistics` and the rest from
// `DBOptions` and `ColumnFamilyOptions`; `it->second` through a map's
// iterator: the member of the `std::pair`.
const field = /^(.+?)\s*(\.|->)\s*([A-Za-z_]\w*)$/.exec(text);
const object = field && cppMemberObject(field[1]!, field[2]!, at, context, depth);
const element = object && cppPairMember(object, field![3]!, at, context);
if (element) return element;
const cls = object && cppWrittenClass(object, at, context);
return cls ? cppDataMemberIn(cls, field![3]!, context) : null;
return cls ? cppInheritedDataMember([cls], field![3]!, context, 0, new Set([cls.qualifiedName])) : null;
}
const open = cppOpeningParen(text, text.length - 1);
const callee = open > 0 ? text.slice(0, open).trim() : '';