Repository navigation
fix(c,cpp): a name in a C or C++ test links to what the test's own includes declare - #2675
Merged
Merged
Conversation
…cludes declare When several files declare a name a C or C++ test uses, the test now links to one its translation unit declares, when there is one: a definition in its own file, in a file it includes (directly or through other includes), or in the source file that implements such a header (the same-stem file that includes it), unless that source keeps the definition to itself (a `static`, an unnamed namespace, a class it declares). Before, the candidates were ranked by directory and index order, so a test took whichever namesake came first: - protobuf's C# generator tests called test_util2.h's `TestUtil::TestSourceDir()`, a test suite they never include, instead of the `TestSourceDir()` googletest.h declares; - protobuf's upb and hpb tests left `upb_Arena_New()` and the rest of upb's C API unlinked: the copies bundled for PHP and Ruby won the ranking and were then rejected; - rocksdb's c_test.c left `rocksdb_open()` and the rest of the C API in db/c.cc unlinked, and its histogram test's `clock->SleepForMicroseconds()` went to emulated_clock.h's `EmulatedSystemClock`, which it doesn't include. The narrowing applies to test files only, to the exact-name strategy, typed receivers (resolveMethodOnType), and the class-name and receiver-word strategies of matchMethodCall. When the translation unit declares none of the candidates, as when a test's includes don't resolve (jemalloc's tests include a generated header), the candidates stay as they were. A name a test writes alone (no receiver, no qualifier) is narrowed only to what C++ finds for it outside a class: a function, a namespace-scope type or a constructor, never another class's method or nested type, and a call the extractor recorded without the receiver it is written on is left as it was. The qualified-name strategy is unchanged. Validated on main 3695818 (site-keyed edge diffs, every group triaged): rocksdb +1,442 / 47 retargeted / -12, protobuf +2,316 / 55 / -67, fmt +33 / 40, breakpad +13 / 14, glog +22, leveldb +10, llm.c 6 retargeted; jq, tokio, excalidraw, okhttp and typeorm byte-identical. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
colbymchenry
added a commit
that referenced
this pull request
Oct 19, 2026
…ile's own includes declare (#2684) PR #2675 narrowed a C or C++ test's same-named candidates to the ones its translation unit declares. Production code now gets the same rule: protobuf's Python extension called the copies of upb's C API that the PHP extension bundles in php-upb.c, and its Java generator's calls of the free HasHasbit() went to ImmutableFieldGenerator::HasHasbit, a method beside the caller. Refinements the wider scope needed: - a source implements the same-stem header beside it, when it includes one: pyext/message.cc is no implementation of google/protobuf/message.h; - a header's own overloads reach the definitions its source holds: conformance/binary_wireformat.h's template Varint calls the .cc's; - narrowing never leaves a call with only functions its arguments can't fill, past one outside the unit that they can: glog's three-argument GetStackTrace(...) is not utilities.h's GetStackTrace(). This also mends 9 breakpad unittest calls #2675 had narrowed the same way; - nor, for a C call, to definitions only a flag nothing sets compiles, past one the build without it does: that stays #2615's choice. A file-level "call" (a macro, or a declaration read as a call) is left as it was. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
When several files declare a name a C or C++ test uses, the test now links to one its translation unit declares, when there is one:
static, an unnamed namespace, or a class only it declares (fix(cpp): a call or type name never links to what only another source file can see #2538'sisCppTranslationUnitLocal).Before, those candidates were ranked by directory and index order, so a test took whichever namesake came first.
TestUtil::TestSourceDir(), a test suite none of them includes. TheTestSourceDir()they call is the onegoogletest.hdeclares. This is the issue's own example.upb_Arena_New()and the rest of upb's C API unlinked (about 2,300 calls). The copies bundled for PHP and Ruby (php-upb.h, ruby-upb.h) won the ranking and were then rejected.db/c_test.cleftrocksdb_open()and the rest of the C API indb/c.ccunlinked (about 1,400 calls).clock->SleepForMicroseconds()to emulated_clock.h'sEmulatedSystemClock, a header it doesn't include.test_gpt2_fp32.cuincludestrain_gpt2_fp32.cudirectly, yet itsgpt2_update(…)andcublasCheck(…)went to the other training programs.Scope, and what it deliberately doesn't do
resolveMethodOnType), andmatchMethodCall's class-name and receiver-word strategies. The qualified-name strategy is unchanged (see the qualified-name note below).test/jemalloc_test.h, keeps the one helper there is. I measured a "drop out-of-TU test suites first" step and a veto variant. Both removed right edges or added wrong ones where main had correctly linked nothing:gmock-gtest-all.ccdefines whatgtest.hdeclares under another name;EXPECT_CALL(memory, GetBase())reached a productionGetBase;table_factory->GetOptions<…>()reachedDBImpl::GetOptions.Value(result, …)matcher is noRepeatedFieldAccessor::Value, and breakpad'sEXPECT_CALL(handler, Entry(…))is noCallFrameInfo::Entrystruct. A construction narrows to types and constructors only (std::string buffer(n, c)is no enum'sstring). A call the extractor recorded without the receiver it is written on (result.columns()[0].value()) is left as it was, read with fix(cpp): a call a method makes with no receiver links to its class's own or inherited method #2479'scppImplicitReceiver.Re-verified on current main first
Most of the issue's original symptom was already fixed on main 3695818 by #2538 (TU-local classes), #2537 (TEST_F bodies reach the fixture) and #2479. Measured with the issue's own script,
test-noinclude.mjs <db> '_test\.(cc|cpp|h)$'. It counts edges from test files into test files the source's translation unit doesn't include:_unittest\.(cc|cpp|h)$sourcesrocksdb's 4 are synthesized base→override links from
point_lock_manager_test.hinto the tests that derive from it, which is by design.The 14 protobuf unittest edges left are of two kinds:
TestAllTypes,TestMessageSetExtension1) whose only in-repo namesakes sit inwire_format_unittest.h, a header these tests don't include (9 of them in an unnamed namespace);TestUtil::ExpectClearcalls taken by the qualified-name boundary bug (see the notes).What this PR fixes is the rest:
TestSourceDircase;A/B (main 3695818 vs this branch), site-keyed edge diffs
Full indexes of 13 repos with the same kernel. A site is (kind, source node, line, col, refName).
Triage. Every (before → after target) group was read. The added sites, by group:
rocksdb_*→ db/c.cc; protobufupb_*→ upb's own .h/.c, not the PHP/Ruby bundles.SetLogDestination(),FlushLogFiles()andLogToStderr()→ logging.cc.crc32c_test.ccValue(…)→crc32c::Value.format(…)→fmt::format,test_string(…)→ util.h's alias,open_buffered_file()→ test/util.cc.WriteFile()/ReadFile()→ common/tests/file_utils.cc.NewWritableFile(fs, …)→ read_write_util.cc, anddb_->Get(…)/wp_db->ReleaseSnapshot(…)→DB::.Wrong among the 3,836 added: 11.
arena_message1->GetArena()on a generated message →FieldWithArena::GetArena(receiver-word guess).get<0>(GetParam())→ vendored gtest'sstd::tr1::get.std::string_viewconstruction → fmt'sstring_viewalias.Retargeted, 162 sites. About 145 are right or equivalent:
TestSourceDir×6;clock->SleepForMicroseconds→SystemClock×12;CreateFile/Info/Log/ValidateOptions/Roundup/FindFile→ their free functions;VisitFields,Prefetch<…>,ValidateEnum, thejson::stream functions; llm.c's included-source functions ×6; breakpad's per-platformCrashGenerationServer/ExceptionHandlerandWriteMinidump→ the free function;range_map->StoreRange→RangeMap;DB::Put,Matcher::Matcher).The rest of the retargets:
swap(…)×1, the 4trie_iter->Seek/Nextonauto x = make_iter(…), protobufFileDescriptorProto::descriptor()×2,FieldOptions::default_instance()×2;info.Clear()on a generic lambda'sauto×2;Varint(127)×1 in upb's conformance harness, whoseupb/wire/test_util/wire_message.hinclude doesn't resolve.Removed, 79 sites. All were wrong receiver-word guesses: rocksdb
result.size()→ gtest'sTestPartResultArray::size×8 and 4std_iter->Seek/Nextguesses; protobufFileDescriptorProto::descriptor()-style calls intojava_features.pb.h/cpp_file_options.pb.h×67.Confidence only: the same target, now the only candidate the test's unit declares, so
exact-match0.4/0.7 → 0.9. Also 2 protobufinstance-method0.8 → 0.65 with the same target.Cost
Full indexes, interleaved main/branch, 3 rounds, same kernel:
The ranges overlap heavily (protobuf branch 80–115s, main 93–104s). The check runs only for a test's reference with two or more candidates, and reuses the memoized include graph (
cppIncluders), the same-stem header list and the memoized test-path checks.Tests
__tests__/cpp-test-translation-unit.test.ts, 5 cases. On main, 3 fail:TestSourceDircase;Two guards pass on both:
Full suite on 08b8361 (this change on main 3695818), with the kernel built,
CODEGRAPH_KERNEL_EXPECT=1and--maxWorkers=4 --minWorkers=1: 7,481 passed, 1 failed, 80 skipped.proxy-shutdown.test.ts's stopDaemonAt reports a gracefully stopping daemon as still-running, and the proxy fallback exits before its engine stops #2311 shutdown-timing test, under load. It passes alone (2/2).cpp-included-test-suiteand the 5 new test files from main pass (26/26).Notes
matchByQualifiedName's partial match isn't anchored at a::boundary, soDB::Open(…)also matchesBlobDB::OpenandTransactionDB::Open, andStatus::OK()matchesIOStatus::OK. Narrowing those candidates to the unit retargeted 16 rocksdb calls right and 3 wrong, which is an artifact of the boundary bug. So this PR leaves that strategy alone. Branchclaude/strange-greider-bd9f76holds a boundary fix that never became a PR.🤖 Generated with Claude Code