Skip to content

fix(cpp): a call on the member of the pair a map's iterator points at, it->second.Flush(), calls the value's method - #2601

Merged
colbymchenry merged 1 commit into
mainfrom
claude/cpp-map-iterators
Oct 15, 2026
Merged

colbymchenry merged 1 commit into
mainfrom
claude/cpp-map-iterators

Conversation

@colbymchenry

Copy link
Copy Markdown
Owner

Summary

This is the follow-up left after #2598: calls through the key or value of a map's iterator, and through the members of a std::pair generally.

The extractor records it->second.Flush(), a range-for's kv.second.Flush() and (*it).first.Print() by their bare names. The name strategies then gave those calls to whichever class had a method of that name. Examples:

  • rocksdb's 45 pair.first.c_str() calls on std::strings all went to googletest's FilePath::c_str;
  • protobuf's kv.second->GenerateMembers(…) went to ImmutableEnumFieldGenerator instead of OneofGenerator.

Separately, a local set from such a member (auto& v = it->second) had no type.

What changed:

  • cppPairMember: x.first / x.second (and ->) on a std::pair now type as the pair's elements in C++ expression typing. So a map's iterator, a range-for element, a declared pair, or a pair a method returns all reach their key and value. This covers the receiver forms below, auto& v = it->second, and range expressions like stats_history_.begin()->second.
  • More receivers read from the source: fix(cpp): a call on a dereferenced iterator or pointer, (*it)->Visit(), calls the method of what it points at #2598's bare-name receiver reader (now cppUnspelledReceiver / matchCppUnspelledReceiverCall) also takes name->second, name.first and (*name).second.
    • A bare (*this) is still left to cppImplicitReceiver.
    • The pattern requires a dereference or a first/second member, so plain this->Foo() calls are untouched.
  • Template parameters: only the calling function's own template parameter names "no particular class". A member typed by another class template's parameter goes on as before. An example is CoreLocalArray<T>::AccessElementAndIndex(), which returns a std::pair<T*, size_t>. The first A/B caught exactly that: rocksdb's p.first->GetUncompressData(idx) lost its correct link, and the guard restores it.

Validation

  • New test __tests__/cpp-map-iterator-calls.test.ts (5 tests): 3 fail on main and pass here. The 2 guards pass on both: another template's parameter, and this->Flush().
  • Mutations: switching off each part fails its test:
    • pair members in expression typing: 3 tests;
    • name->second receivers: 3;
    • (*name).second receivers: 1;
    • the foreign-template guard: 1.
  • A/B (full index, main vs fix) on google/leveldb, fmtlib/fmt, redis (C control), facebook/rocksdb and protocolbuffers/protobuf. Every changed site was triaged, by category and line by line for every project-named method.
    • rocksdb, 130 removed: all library calls on a pair member's std::string, container, smart pointer, std::optional or weak_ptr that had been guessed onto project methods (c_str, size, data, find, get, lock, has_value, …). For example, main's filters.size() reached Slice::size at 0.9, but filters is it->second of a string-to-string map.
    • rocksdb, 55 added: Slice keys and values (entry.first.ToString()), HistogramStat, RangeDelAggregator::StripeRep, Snapshot, TrackedKeyInfo, InternalKey, and range-fors over a pair's member (ObjectLibrary::Entry).
    • rocksdb, 38 retargeted from 0.4/0.7 guesses:
      • batch_and_status.first → WriteBatch;
      • wal_and_status->first → log::Writer;
      • pair.second->Timestamp() → cassandra::ColumnBase;
      • toku range_buffer;
      • Timer::FunctionInfo;
      • an OptionTypeInfo::AreEqualByName overload, now the std::string one, matching iter->second of a string map.
    • rocksdb, 179 metadata-only: same targets, confidence up to 0.9.
    • protobuf:
      • 29 removed: library calls, plus descriptor accessors (p.second->file(), e.second->options()), which aren't indexed;
      • 1 added: ExtensionSet::Extension;
      • 14 retargeted: OneofGenerator, UnknownField;
      • 8 metadata-only.
    • leveldb: 1 metadata-only.
    • fmt, redis: byte-identical.
  • Full suite, including every C/C++ test file: 7,155 passed, 0 failed.

Remaining, out of scope: element access the extractor drops (m[k].Foo(), arr_[0].Foo()), and m.at(k).Foo() chains, which would need library containers' element-returning members.

🤖 Generated with Claude Code

…, `it->second.Flush()`, calls the value's method

The extractor records `it->second.Flush()`, `kv.second.Flush()` and
`(*it).first.Print()` by their bare names, which the name strategies gave
to whichever class had a method of that name — 45 `std::string::c_str()`
calls went to googletest's `FilePath::c_str` — and a local such a member
initializes (`auto& v = it->second`) had no type.

`x.first` / `x.second` (and `->`) on a `std::pair` now type as the pair's
elements in C++ expression typing (cppPairMember), so a map's iterator,
range-for element or declared pair reaches its key and value, and #2598's
bare-name receiver reader also takes `name->second`, `name.first` and
`(*name).second`. A member typed by another class template's parameter
(`CoreLocalArray<T>::AccessElementAndIndex()`'s `std::pair<T*, size_t>`)
goes on as before: only the calling function's own template parameter
names no particular class.

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