Skip to content

fix(cpp): a call on what a container's front()/back()/at() or a holder's get()/value() returns, like rocksdb's edits.back()->MarkAtomicGroup(…), calls the element's method - #2616

Merged
colbymchenry merged 1 commit into
mainfrom
claude/cpp-container-accessor-calls
Oct 16, 2026
Merged

colbymchenry merged 1 commit into
mainfrom
claude/cpp-container-accessor-calls

Conversation

@colbymchenry

Copy link
Copy Markdown
Owner

Summary

This is the follow-up both #2601 and #2602 left open: calls on what a library container or holder hands out.

The bug. A call made through one of these accessors linked to nothing:

  • bufs.front().Flush() over a std::vector<Buffer>;
  • wals.at(n).HasSyncedSize() over a std::map;
  • pending.top()->Valid() on a std::stack;
  • mock_clock.get()->GetCurrentTime() on a std::shared_ptr;
  • scan_opts_.value().GetScanRanges() on a std::optional.

The library's member returns nothing the index can read, and the library-receiver gate (#2413) rightly keeps the name strategies off a std::vector.

The fix. cppLibraryElementOf reads what such a member returns, where the container's type is written:

Member On Gives
front(), back(), at(i) a sequence: std/absl, plus the project sequences cppRangeElementOf already treats as such (RepeatedPtrField, rocksdb's autovector) the element
at(key) a library map the mapped value
top() std::stack, std::priority_queue the element
front(), back() std::queue the element
get() std::unique_ptr, std::shared_ptr, absl::NoDestructor a pointer to the held type
value(), value_or() an optional, absl::StatusOr the held type

It is asked in two places:

  • cppChainInnerType, for a chain like recv.front().m();
  • cppExpressionType, for an auto local, a range or an argument, next to cppLibraryIteratorOf.

A typed argument also feeds overload choice, so a call given such a value now takes the overload for its type.

A/B on the C/C++ kit (main 98127e54 vs this branch)

repo added retargeted removed confidence only
leveldb, fmt, redis 0 0 0 0
rocksdb 299 89 10 56
protobuf 7 16 0 2

Triage method. An instrumented copy of the fix arm logged every element type cppLibraryElementOf derived. Each changed site was joined with those logs, by file and nearby line, and checked against the target's class or one of its ancestors (extends edges).

  • rocksdb: 356 sites match directly.
  • The other 98 are all explained:
    • element types through aliases: typedef std::shared_ptr<PersistentCacheTier> Tier, using InternalIterator = InternalIteratorBase<Slice>, using UserDefinedIndexIterator = IndexFactoryIterator;
    • second hops on a newly typed element, like range.limit.value() → OptSlice::value;
    • a .get() through a member chain, like tbo.moptions.compression_manager.get();
    • free-function overloads picked by a now-typed .get() argument. test::PerThreadDBPath(mock_env_.get(), …) moves from (std::string dir, std::string name) to (Env* env, std::string name), and CreateNewBackup(db_.get()), WriteStringToFile, ReadFileToString, CopyFile, CreateFile and LogFlush likewise.
  • The 10 removals were all wrong guesses on what turned out to be library containers:
    • tracked_files.size() on the std::unordered_map from get()->GetTrackedFiles(), which went to CompactionInputFiles::size;
    • scan_ranges.size() on a std::vector, which went to MultiScanArgs::size;
    • str.c_str(), which went to gtest's FilePath::c_str.
  • Retargets on a typed element move from a guessed namesake (0.65) to the declared type (0.9), for example index->SetPrimaryColumnFamily over a std::vector<std::shared_ptr<SecondaryIndex>>.
  • protobuf additions: children_.back()->InsertImpl, message_generators_.back()->AddGenerators, ordered_fields_.back()->number(), subs_.at(i).value(), and others.
  • protobuf retargets:
    • hpb::Parse(model.get(), bytes.value()) reaches the (PtrOrRaw<T>, string_view) overload;
    • field.Add("") leaves a = deleted overload;
    • the DynamicCastMessage/DownCastMessage calls on ptr.get() leave their std::shared_ptr overloads for the pointer ones.

Known limit. About 6 of those cast calls pass a non-const pointer and land on the const MessageLite* overload, where C++ picks the MessageLite* one. Overload fit ties const and non-const pointers and breaks the tie by declaration order. That is pre-existing overload logic, filed as a follow-up.

Tests

  • New file __tests__/cpp-container-accessor-calls.test.ts (5 tests). The accessor members each have a case, plus an auto local and a project sequence. On 98127e54 the four behavior tests fail (checked on a detached checkout). The guard test passes on both: a project class named like a container, a std::string element and a template's own T.
  • Mutations: 13, of which 11 are killed. The two survivors are covered elsewhere: cppRangeElementOf's project-namesake guard covers the library && check on sequences, and the pointer guard is the other.
  • Full suite: 7205 passed. One mcp-daemon.test.ts daemon-reaper test timed out at 20 s under load; the file passes 21/21 when rerun on its own.

🤖 Generated with Claude Code

… holder's `get()`/`value()` returns, like rocksdb's `edits.back()->MarkAtomicGroup(…)`, calls the element's method

A call on what a library container hands out linked to nothing: the
standard library's `front()` returns nothing the index can read, and the
library-receiver gate keeps the name strategies off a `std::vector`.

cppLibraryElementOf reads what such a member returns, where the
container's type is written:
- `front()`, `back()` and `at(i)` of a sequence (std/absl, and the project
  sequences cppRangeElementOf already treats as such) → its element;
- a library map's `at(key)` → the mapped value;
- `std::stack`/`std::priority_queue` `top()`, `std::queue` `front()`/`back()`
  → the element;
- `get()` of `std::unique_ptr`/`std::shared_ptr`/`absl::NoDestructor` → a
  pointer to the held type, `value()`/`value_or()` of an optional or an
  `absl::StatusOr` → the held type.
It is asked in cppChainInnerType (a chain `recv.front().m()`) and in
cppExpressionType (an `auto` local, a range, an argument), next to
cppLibraryIteratorOf.

A/B on the cgstd kit against main 98127e5: leveldb, fmt and redis
identical; rocksdb 299 calls added, 89 retargeted (overloads picked by a
typed `.get()` argument, like `test::PerThreadDBPath(env_.get(), …)`, and
inherited methods on a typed element), 10 removed (guesses on what turned
out to be library containers) and 56 confidence-only; protobuf 7 added and
16 retargeted (cast helpers off their `std::shared_ptr` overloads, `Add`
off a deleted one). Every site was joined with the element type an
instrumented arm logged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant