fix(cpp): a smart pointer's held class lacking a method gets no edge only when its bases are known (#2530)

matchCppHeldTypeCall (#2440) returned null (no edge) for a `->` call on a
`std::unique_ptr`/`std::shared_ptr`/`std::optional` whose held class
lacks the method, unless that class is a class template. #2493 found the
gap in the same rule for raw pointers and values: a base the index can't
link may declare the method. protobuf's `class FlatAllocator : public
decltype(ApplyTypeList<FlatAllocatorImpl>(…))` has no extends edge to
`FlatAllocatorImpl`, and rocksdb's `WritePreparedTxnDB` derives from an
alias each `#if` arm declares differently.

Both paths now share one verdict, cppLacksMethod: no class template, and
cppBasesKnown (every base on the way is linked to a class, or is a library
type). Otherwise the call goes on as before.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Colby Mchenry
2026-10-12 22:16:09 +00:00
committed by GitHub
parent 80e4b459ff
commit 85201e95b3
3 changed files with 53 additions and 12 deletions
+34 -3
View File
@@ -17,9 +17,9 @@
* `Iterator` the call means inherits `Valid` from `IteratorBase`. A template
* parameter, a library type and an alias of a type outside the project hold
* no project class, so those calls get no edge; neither does a call the held
* class has no method for (unless a class template's specialization may
* declare it). A held type the project doesn't declare — a generated message
* — leaves the call as it was.
* class has no method for (unless a class template's specialization, or a
* base the index doesn't link, may declare it). A held type the project
* doesn't declare — a generated message — leaves the call as it was.
*/
import { describe, it, expect, afterEach } from 'vitest';
import * as fs from 'fs';
@@ -447,4 +447,35 @@ describe('what keeps the call as it was', () => {
cg.close();
}
});
it('a held class with a base the index does not link, which may declare the method', async () => {
// protobuf's FlatAllocator derives from a `decltype(…)` that names FlatAllocatorImpl.
const cg = await indexed({
'src/google/protobuf/descriptor.cc': lines(
'#include <memory>',
'namespace google {',
'namespace protobuf {',
'namespace internal {',
'template <typename... T>',
'class FlatAllocatorImpl {',
' public:',
' void PlanArray(int array_size) {}',
'};',
'template <typename... T>',
'FlatAllocatorImpl<T...> ApplyTypeList(int);',
'class FlatAllocator : public decltype(ApplyTypeList<char, int>(0)) {};',
'} // namespace internal',
'void PlanAllocationSize(std::unique_ptr<internal::FlatAllocator>& alloc) {',
' alloc->PlanArray(1);',
'}',
'} // namespace protobuf',
'} // namespace google',
),
});
try {
expect(calls(cg, 'google::protobuf::PlanAllocationSize')).toEqual(['google::protobuf::internal::FlatAllocatorImpl::PlanArray']);
} finally {
cg.close();
}
});
});