Skip to content

fix(cpp): a receiver type written from the global scope is looked up there, and a far lowercase outside type reaches no namesake - #2569

Merged
colbymchenry merged 1 commit into
mainfrom
claude/cpp-receiver-global-far-library
Oct 15, 2026
Merged

colbymchenry merged 1 commit into
mainfrom
claude/cpp-receiver-global-far-library

Conversation

@colbymchenry

Copy link
Copy Markdown
Owner

Summary

The two C++ receiver follow-ups that #2567 and #2568 left open.

1. A receiver declared with a leading :: lost it. The declarator regex started a type at a letter, so ::upb::Arena& a was read as upb::Arena& and looked up from the caller's namespace. Inside namespace hpb, that found a namespace-local hpb::upb::Arena first.

  • The declared type now keeps a leading ::, but only where one can stand: not after a name, a template's arguments or a ). A single-character lookbehind does this, so SkipList<Key, Comparator>::Iterator iter is not read as ::Iterator.
  • Two-line declarations and range-based for loops read it the same way.
  • resolveCppAliasedType no longer takes a function-local using Foo = Bar; for ::Foo. fix(cpp): an alias of a type written from the global scope names that global type #2568 had dropped that guard because no receiver could reach it. Now one can.

2. A lowercase outside type declared outside the calling function still used the last-name typed match. Examples are another function's variable of the same name, or a file's global. #2413's library rule skips such declarations, and #2567's namesake gate covered capitalized types only. So:

  • a far absl::flat_hash_set<int> seen linked seen.insert(v) to the project's util::flat_hash_set::insert;
  • a far std::string text linked text.clear() to a global project string class.

The gate now covers lowercase types too. Their typed match is dropped, and the call is decided as it would be if no class shared the type's name: by the library rule, or by the name strategies for a far declaration. cppFindsOnlyNamesakes now reads a std:: name exactly, so a global string class is no std::string.

Validation

  • New test __tests__/cpp-receiver-global-far-library.test.ts (7 tests): the 5 reproductions fail on main and pass here. The 2 guards pass on both: the project's own lowercase type stays its own, and Outer<T>::Iterator it stays Outer's.
  • Mutations: switching off each change fails its test:
    • the declarator's ::: 3 tests fail;
    • the :: lookbehind: 1;
    • the local-alias global guard: 1;
    • the lowercase gate: 2;
    • std exactness: 1.
  • A/B (full index, main vs fix) on google/leveldb, fmtlib/fmt, redis (C control), facebook/rocksdb and protocolbuffers/protobuf: graphs are byte-identical. None of these repos has these shapes next to a namesake.
    • A first version had no lookbehind. It retargeted 109 calls in leveldb's and rocksdb's skip-list tests, because SkipList<Key, Comparator>::Iterator iter was read as ::Iterator. It also lowered 3 rocksdb confidences. The lookbehind fixed this, and the new guard test pins it.
    • Pre-existing and out of scope: main reads that declaration as plain Iterator, so those 109 calls link to leveldb::Iterator and IteratorBase instead of the skip list's iterator. This is filed as a follow-up task.
  • All 47 C/C++ test files pass (448 tests). Full suite: 7,130 passed, 0 failed.

🤖 Generated with Claude Code

…there, and a far lowercase outside type reaches no namesake

The two follow-ups #2567 and #2568 left open.

A receiver declared with a leading `::` lost it: the declarator regex
started a type at a letter, so `::upb::Arena& a` inside `namespace hpb`
was looked up from `hpb` and found a namespace-local `hpb::upb::Arena`.
The declared type now keeps a leading `::` where one can stand (not after
a name, a template's arguments or a `)`, so `SkipList<Key,
Comparator>::Iterator iter` is not read as `::Iterator`), and
resolveCppAliasedType no longer takes a function-local `using Foo = Bar;`
for `::Foo`.

A lowercase outside type declared outside the calling function still used
the last-name typed match: #2413's library rule skips such declarations,
and #2567's namesake gate took capitalized types only. So a far
`absl::flat_hash_set<int> seen` linked `seen.insert(v)` to the project's
`util::flat_hash_set::insert`, and a far `std::string text` linked
`text.clear()` to a global project `string` class. The gate now covers
lowercase types, which are decided as when no class shares their name,
and cppFindsOnlyNamesakes reads a `std::` name exactly.

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