Skip to content

fix(cpp): an auto local holding a library container's iterator calls the element's method through -> - #2579

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

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

Conversation

@colbymchenry

Copy link
Copy Markdown
Owner

Summary

This is the follow-up left after #2576.

An auto local initialized with a library container's iterator had no type. rocksdb's auto it = logs_.begin(); it->IsSyncing() is an example: the auto-local reader (cppExpressionType) had nothing for a call on std::deque<LogWriterNumber>. So -> calls on such locals were matched by the method's name alone.

What changed:

  • Container members: a standard-library or abseil container's begin/end/cbegin/rbegin/crbegin/find/lower_bound/upper_bound/… now types as <container>::iterator, with the right iterator kind.

  • Searches: a standard search (std::find, find_if, lower_bound, upper_bound, min_element, max_element, partition_point, adjacent_find, next, prev) types as the iterator it is given first.

  • From there, fix(cpp): a -> call on a declared container iterator calls the element's method #2576's cppArrowTarget reaches the container's element.

  • Where the element is looked up: where the container's type is written — the auto local's declared site, or a data member's class — not at the call. So an element nested in another class (vs->obsolete_files_.begin()) finds that class's type, not a namesake visible at the call.

  • Smart pointers are unchanged: their held type is still looked up at the call. A first version moved holders to the declaration site too, and the A/B caught two problems with it:

    • a pimpl's Rep, which the header only declares, wasn't found from the header, so about 80 rocksdb calls dropped from 0.9 to 0.65–0.8;
    • protobuf's index_->AddFile(…) went to SimpleDescriptorDatabase::DescriptorIndex, a namesake visible from the header.

    A new test pins this.

  • Project containers keep what their own begin() returns. A project class named like a library container (util::span) isn't taken for it.

Validation

  • New test __tests__/cpp-auto-iterator-arrow.test.ts (6 tests): 4 fail on main and pass here. 2 guards pass on both: the project span, and the pimpl smart pointer.
  • Mutations: switching off each part fails its test:
    • the container members: 4 tests;
    • the searches: 1;
    • the std/abseil head check: 1;
    • looking elements up at the declared site: 1;
    • holders kept at the call site: 1.
  • A/B (full index, main vs fix) on google/leveldb, fmtlib/fmt, redis (C control), facebook/rocksdb and protocolbuffers/protobuf. Every site was triaged:
    • rocksdb:
      • 6 added: it->LargestUserKey() on sub_compact_states.rbegin() reaches SubcompactionState; to_it->Merge(…) on a copy of pairs_.begin() reaches SeqnoTimePair; it->start() over intervals_ reaches Interval, the same target main gives . calls on that type; it->data()/it->size() over operand_list reach Slice.
      • 2 metadata-only: it->IsSyncing() and ReleaseWriter() on logs_.begin() keep their target, with confidence up from 0.7 to 0.9.
    • protobuf:
      • 3 added: it->get() over an absl::flat_hash_set<Arena::UniquePtr<int>> and over one of Arena::Ptr<int> reach Arena::UniquePtr::get and Arena::Ptr::get; it->name(*this) from std::lower_bound(by_name_flat_.begin(), …) reaches FileEntry::name.
      • 2 metadata-only: it->extendee(*this) keeps its target, with confidence up from 0.7 to 0.9.
    • leveldb, fmt, redis: byte-identical.
    • No edges removed or retargeted.
  • Full suite, including every C/C++ test file: 7,146 passed, 0 failed.

Remaining, out of scope:

  • (*it)->m() / (*it).m() dereferences, and auto x = *c.begin(), which would need cppPointee to dereference iterators;
  • map iterators' it->second.m() chains, which need std::pair members.

🤖 Generated with Claude Code

…s the element's method through `->`

`auto it = logs_.begin(); it->IsSyncing()` had no type for `it`: a call
on a library container (`std::deque<LogWriterNumber>`) returned nothing
the auto-local reader could use, so `->` calls on it were matched by the
method's name. A standard or abseil container's `begin`/`end`/`find`/
`lower_bound`/`rbegin`/… now types as `<container>::iterator`, and a
standard search (`std::lower_bound`, `std::find_if`, `std::next`, …) as
the iterator it is given first; #2576's cppArrowTarget then reaches the
container's element.

The element is looked up where the container's type is written (the
`auto` local's declared site, a data member's class), not where the call
is: an element nested in another class (`vs->obsolete_files_.begin()`)
finds that class's type, not a namesake visible at the call. A holder's
type stays looked up at the call: from a pimpl member's header the lookup
finds a namesake (protobuf's other `DescriptorIndex`) or nothing.

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