Skip to content

fix(cpp): a call on a dereferenced iterator or pointer, (*it)->Visit(), calls the method of what it points at - #2598

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

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

Conversation

@colbymchenry

Copy link
Copy Markdown
Owner

Summary

This is the follow-up left after #2579: calls through a dereference.

The extractor records (*it)->Visit() and (*node).Weight() by their bare names, because it can't spell a (*name) receiver. The name strategies then gave those calls to whichever class had a method of that name, at confidence 0.4–0.7. Examples:

  • protobuf's (*iter)->number() over const FieldDescriptor* went to the C# generator's FieldGeneratorBase::number;
  • rocksdb's (*range_tombstone_iter_)->SeekToFirst() went to the caller's own LevelIterator.

Separately, a local set by a dereference, auto first = *nodes.begin(), had no type.

What changed:

  • cppDereferenced: *x and x-> in C++ expression typing now dereference a container's iterator to its element, as well as pointers and holders. It's used by cppExpressionType, cppMemberObject and cppChainInnerType. This types:

    • auto first = *nodes.begin();
    • auto& wal = *it;
    • structured bindings over *it;
    • -> through iterators in chains and initializers (it->Child()->Visit()).

    The iterator part is fix(cpp): a -> call on a declared container iterator calls the element's method #2576's element reading, factored out as cppIteratorTarget.

  • matchCppDereferencedCall, which runs before the name strategies: a bare-name call written on (*name) is resolved on the dereferenced value's class by C++ lookup.

    • The receiver is read at the call's column, where it starts. cppUtf8Column is factored out of cppImplicitReceiver for that.
    • The result is the method, or no project method for a library type, a template parameter's, or a class that surely lacks the method.
    • Anything else goes on as before.

Validation

  • New test __tests__/cpp-dereferenced-iterator-calls.test.ts (4 tests): all 4 fail on main and pass here.
  • Mutations: switching off each part fails its test:
    • *x on iterators: 3 tests;
    • x-> in expressions: 1;
    • x-> in chains: 1;
    • the dereferenced-call matcher: 3;
    • "a library type calls nothing": 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, and all changes are correct.
    • leveldb (2), fmt (1): metadata-only. (*table)->ReadMeta, (*ptr)->key() and (*captured_stream)->… through double pointers keep their target, typed at 0.9.
    • rocksdb:
      • 9 added: (*it)->Name() reaches ReadOnlyMemTable; (*pointer)->GetOptions<…> reaches Configurable; (*txn)->Put reaches Transaction; (*builder)->Finish reaches BlockBuilder; 5 × bfile->PathName() on auto bfile = *iter reach BlobFile.
      • 28 retargeted from 0.4/0.7 guesses to the typed class. Examples: (*log_writer)->AddRecord now reaches log::Writer rather than BlobLogWriter; (*r)->use_direct_io() reaches FSSequentialFile, FSWritableFile and FSRandomAccessFile; (*txn)->Commit reaches Transaction rather than PendingExpectedValue.
      • One retarget is an overload: range_del_agg->ShouldDelete(test_case.lookup_key, …), where test_case = *it, now picks the ParsedInternalKey overload, as main already does for the identical range-for loop above it.
      • 8 removed, all wrong guesses on library or template-parameter types: (*handle).reset() on a std::unique_ptr*; (*id).data() on a std::array alias; (*lk).lock()/unlock(); two (*result)->IsInstanceOf on U** in a template.
      • 38 metadata-only: same targets, confidence up to 0.9. This includes the census's auto first_cfd = *compaction_queue_.begin().
    • protobuf:
      • 6 retargeted: (*lhs_sub)->GetReflection()/New/CopyFrom on Message** now reach Message rather than Any; (*target)->GetPrototype() on a const TypeInfo** reaches TypeInfo rather than the caller's own class.
      • 5 removed: 4 × (*iter)->number() over const FieldDescriptor*, whose accessors aren't indexed, had reached the C# generator; child_ptr.get() on a std::unique_ptr bound from *it had reached python's ScopedPythonPtr::get.
      • 7 metadata-only.
    • redis: byte-identical.
  • Full suite, including every C/C++ test file: 7,150 passed, 0 failed.

Remaining, out of scope: map iterators' it->second.m(), which needs std::pair members; and other receivers the extractor can't spell (arr_[0].Foo(), this->p_->Foo()).

🤖 Generated with Claude Code

…()`, calls the method of what it points at

The extractor records `(*it)->Visit()` and `(*node).Weight()` by their
bare names, which the name strategies then gave to whichever class had a
method of that name (0.4-0.7 guesses), and a local a dereference sets,
`auto first = *nodes.begin()`, had no type.

`*x` and `x->` in C++ expression typing now dereference a container's
iterator to its element as well as pointers and holders (cppDereferenced,
used by cppExpressionType, cppMemberObject and cppChainInnerType), and a
bare-name call written on `(*name)` (read at the call's column, where the
receiver starts) is resolved on the dereferenced value's class by C++
lookup before the name strategies run: the method, or no project method
for a library type, a template parameter's, or a class that lacks it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@colbymchenry
colbymchenry merged commit 18efc8c into main Oct 15, 2026
@colbymchenry
colbymchenry deleted the claude/cpp-deref-iterators branch October 15, 2026 21:58
colbymchenry added a commit that referenced this pull request Oct 15, 2026
…, `it->second.Flush()`, calls the value's method (#2601)

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