mirror of
https://github.com/colbymchenry/codegraph.git
synced 2026-10-17 05:07:37 +08:00
fix(cpp): a -> call on a declared container iterator calls the element's method (#2576)
`std::deque<Slice>::const_iterator it; it->data()` was matched by the method's name alone, so it could reach any class with a `data`, and `itr->c_str()` on a `std::vector<std::string>::const_iterator` reached gtest's `FilePath::c_str`. A `->` on a value of an iterator type now reaches the container's element, as `->` on a smart pointer reaches what it holds: the element is read from the container's template arguments (cppRangeElementOf), before the container's own `iterator` alias would lose them (protobuf's `RepeatedPtrField<T>::iterator`), and looked up as C++ does. A library element (`std::string`) or the caller's template parameter calls no project method. An element only a class template's own `begin()`/`operator*` writes with its parameter (googletest's `ParamGenerator<T>::iterator`) goes on as before. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
+1
-1
@@ -95,7 +95,7 @@ and adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
- In C++, a call on a variable, parameter or member declared as a standard-library or other outside type with a lowercase name, like `std::string`, `std::vector`, `std::set` or `absl::flat_hash_set`, no longer links to a project method that only shares the method's name, even one of a project class in another namespace with the type's name, like your own `util::flat_hash_set`. A variable declared outside the calling function, like another function's or a file's `std::string text`, no longer links its calls to such a class either. In google/leveldb, `saved_key_.clear()` on a `std::string saved_key_;` reached `Slice::clear`, and in protocolbuffers/protobuf, `proto.append(…)` on a `std::string` reached an unrelated `LeftoverBuffer::append`. The same goes for `->` through a raw pointer like `std::string* out`, while `->` through a smart pointer, iterator or optional still links to the type it holds. Comments also no longer pass for a variable's declaration, so more calls now link to the method of the type the variable is really declared with. Re-index C++ projects after upgrading.
|
||||
- In C++, a class written with attribute macros the parser can't read now keeps its members. That covers several macros between `class` and the class name, like protocolbuffers/protobuf's generated `class PROTOBUF_EXPORT PROTOBUF_FUTURE_ADD_EARLY_WARN_UNUSED Any final`, a macro with arguments there, like RocksDB's `struct ALIGN_AS(64U) HandleImpl`, or one before a template specialization, and macros on a member: in front of it (`PROTOBUF_FUTURE_ADD_EARLY_NODISCARD`), between a pointer and its name (`PROTOBUF_NONNULL`), after its parameters or its name (`ABSL_ATTRIBUTE_LIFETIME_BOUND`, google/leveldb's `LOCKS_EXCLUDED(mu_)` and `GUARDED_BY(mu_)`), or alone on a line above a comment ({fmt}'s `FMT_BEGIN_EXPORT`). Before, protobuf's generated message classes were missing and their members, like `GetDescriptor`, `GetReflection` and `unknown_fields`, were indexed as functions outside any class, and leveldb's annotated methods were indexed under the annotation's name, so calls to them linked to nothing. Re-index C++ projects after upgrading.
|
||||
- In C++, a call on a range-based `for` loop's variable, like `suite->SetVerbose(verbose)` inside `for (ConformanceTestSuite *suite : suites)`, now links to the method of the type the loop declares. Before, the loop's declaration was never read, so the call was matched by the variable's name alone: in RocksDB, `log->file()` on a `log::Writer` went to `BlobLogWriter::file`, and a loop variable named like a member of its class was taken for that member. The variable counts only inside its loop. Re-index C++ projects after upgrading.
|
||||
- In C++, a call made with `->` on a variable, parameter or member declared as a `std::unique_ptr`, `std::shared_ptr` or `std::optional`, like `iter->Valid()` on a `std::unique_ptr<Iterator> iter`, now links to that method of the type it holds, or of a class that type inherits from, with the type looked up the way C++ does it, so a class of the same name nested elsewhere is not mistaken for it. Before, the call went to whichever project method shared its name: in facebook/rocksdb, `iter->Valid()` reached `ArenaWrappedDBIter::Valid` and `fs->GetFileSize(…)` on a `std::shared_ptr<FileSystem>` reached a test's stand-in file system, and calls like `it->Next()` were often not linked at all. When what it holds is a template parameter or a standard-library type, like `std::unique_ptr<std::deque<…>>`, or a class that has no method of that name and neither do the classes it derives from, the call no longer links to a project method; a class derived from something that can't be followed, like a `decltype(…)`, may still have it, so that call is matched as before. Re-index C++ projects after upgrading.
|
||||
- In C++, a call made with `->` on a variable, parameter or member declared as a `std::unique_ptr`, `std::shared_ptr` or `std::optional`, like `iter->Valid()` on a `std::unique_ptr<Iterator> iter`, now links to that method of the type it holds, or of a class that type inherits from, with the type looked up the way C++ does it, so a class of the same name nested elsewhere is not mistaken for it. Before, the call went to whichever project method shared its name: in facebook/rocksdb, `iter->Valid()` reached `ArenaWrappedDBIter::Valid` and `fs->GetFileSize(…)` on a `std::shared_ptr<FileSystem>` reached a test's stand-in file system, and calls like `it->Next()` were often not linked at all. When what it holds is a template parameter or a standard-library type, like `std::unique_ptr<std::deque<…>>`, or a class that has no method of that name and neither do the classes it derives from, the call no longer links to a project method; a class derived from something that can't be followed, like a `decltype(…)`, may still have it, so that call is matched as before. The same goes for `->` on a container's iterator, like `it->data()` on a `std::deque<Slice>::const_iterator it` or `loc->path_size()` on protocolbuffers/protobuf's `RepeatedPtrField<SourceCodeInfo_Location>::iterator loc`: the call links to that method of the container's element type, and to no project method when the element is a standard-library type like `std::string`. Re-index C++ projects after upgrading.
|
||||
- In C++, a call a method makes with no receiver, or through `this->`, now links to the method C++ itself would call: one of the method's own class, else one the class inherits from a base class, else one of a class it is nested in. The same goes for a call in a lambda inside the method, and of a class's overloads the one the call's arguments fit is taken. Before, the call was matched by its name alone, so another class's method of that name could win because it was declared nearer or found first: in protocolbuffers/protobuf, every generated message's `Swap` and move assignment calling `InternalSwap(…)` reached `Any::InternalSwap`, and the `GetArena()` messages inherit from `MessageLite` reached an arena helper's `GetArena`, while googletest's `~linked_ptr() { depart(); }`, vendored in facebook/rocksdb, reached `linked_ptr_internal::depart`. Calls C++ doesn't look up in the class are matched as before: a free function's, a call to a parameter or local of that name, and a template's bare call into a base class that depends on its template parameters. Re-index C++ projects after upgrading. Thanks @danusha2345 for reporting the C++ case in #1952.
|
||||
- In C++, a call on the variable of a range-based `for` loop over `auto` elements, like `cfd->Ref()` inside facebook/rocksdb's `for (auto cfd : cfds)`, now links to the method of the type the loop goes over. That type is read from what the loop iterates: a `std::vector`, `std::set`, `absl::Span` or similar container, protocolbuffers/protobuf's `RepeatedPtrField` or RocksDB's `autovector`, whether a variable, parameter or member holds it, an `auto` variable is set to it or a function or method returns it, and a project class you loop over through its `begin()`, like RocksDB's `ColumnFamilySet`. Before, the call was matched by the variable's name, or by an earlier variable of that name: in RocksDB, `blob_file.GetBlobFileNumber()` over a list of `BlobFileGarbage` reached `BlobFileAddition::GetBlobFileNumber`, and in protobuf's Objective-C generator, calls over a list of `std::unique_ptr<MessageGenerator>` reached other generators' methods. A call on an element of a standard-library type, like `file.find(…)` over a `std::vector<std::string>`, no longer links to a project method, and `->` on an element held in a `std::unique_ptr` or `std::shared_ptr` reaches the type it holds. Re-index C++ projects after upgrading.
|
||||
- In C++, a call on a variable, parameter or member declared as one of your classes, like `iter->Valid()` on an `Iterator* iter` or `it.Valid()` on a `const Iterator& it`, now links to that method of the class C++ itself would find for the type where the call is written, or of a class it inherits from. Before, the method was picked by the type's last name, so a class of the same name nested elsewhere could win: in facebook/rocksdb, calls on an `Iterator` reached `MemTableRep::Iterator`, which declares `Valid` and `Next` itself while the `Iterator` the code means inherits them, google/leveldb's reached `SkipList::Iterator`, and in protocolbuffers/protobuf the Java generator's `context->options()` reached the hpb generator's `Context` and `text_parser.Parse(…)` on a `TextFormat::Parser` reached the `.proto` parser. When that class and the classes it derives from have no method of that name, the call no longer links to an unrelated class's method that shares it, as protobuf's `field->full_name()` on a `FieldDescriptor*` did. A type is also found through a `using` declaration the file writes, like protobuf's `using google::protobuf::Descriptor;`, before a class of the same name declared at global scope elsewhere in the repository; this also applies to base classes and to the type a smart pointer holds. A type written from the global scope, like `::upb::Arena& arena`, is looked up there, so a namespace's own `upb::Arena` is not taken for it, nor is an alias of that name the calling function declares. A type written as a member of a class template, like `SkipList<Key, Comparator>::Iterator iter` in leveldb's and RocksDB's skip-list tests, is now read whole, so its calls reach the skip list's own iterator rather than the general `Iterator`, and RocksDB's skip-list memtable iterator no longer links its calls on the skip list's iterator to its own methods; a standard container's `iterator` written this way is the library's, never a class of yours named `iterator`. Re-index C++ projects after upgrading.
|
||||
|
||||
@@ -0,0 +1,224 @@
|
||||
/**
|
||||
* A `->` call on a C++ receiver declared as a container's iterator, like
|
||||
* `std::deque<Slice>::const_iterator it; it->data()`, calls a member of the
|
||||
* container's element, as `->` on a smart pointer calls one of what it
|
||||
* holds. The call was matched by the method's name alone, so it could reach
|
||||
* any class with a method of that name.
|
||||
*/
|
||||
import { describe, it, expect, afterEach } from 'vitest';
|
||||
import * as fs from 'fs';
|
||||
import * as os from 'os';
|
||||
import * as path from 'path';
|
||||
import { CodeGraph } from '../src';
|
||||
import type { Node } from '../src/types';
|
||||
|
||||
const roots: string[] = [];
|
||||
afterEach(() => {
|
||||
for (const root of roots.splice(0)) fs.rmSync(root, { recursive: true, force: true });
|
||||
});
|
||||
|
||||
async function indexed(files: Record<string, string>): Promise<CodeGraph> {
|
||||
const root = fs.mkdtempSync(path.join(os.tmpdir(), 'codegraph-cpp-iterator-arrow-'));
|
||||
roots.push(root);
|
||||
for (const [rel, content] of Object.entries(files)) {
|
||||
fs.mkdirSync(path.dirname(path.join(root, rel)), { recursive: true });
|
||||
fs.writeFileSync(path.join(root, rel), content);
|
||||
}
|
||||
return CodeGraph.init(root, { index: true });
|
||||
}
|
||||
|
||||
function callable(cg: CodeGraph, qualifiedName: string): Node {
|
||||
const node = [...cg.getNodesByKind('method'), ...cg.getNodesByKind('function')].find((n) => n.qualifiedName === qualifiedName);
|
||||
if (!node) throw new Error(`no function or method ${qualifiedName}`);
|
||||
return node;
|
||||
}
|
||||
|
||||
/** `calls` callees of a function or method, as `qualifiedName`. */
|
||||
function calls(cg: CodeGraph, caller: string): string[] {
|
||||
return cg
|
||||
.getCallees(callable(cg, caller).id)
|
||||
.filter((r) => r.edge.kind === 'calls')
|
||||
.map((r) => r.node.qualifiedName)
|
||||
.sort();
|
||||
}
|
||||
|
||||
const lines = (...l: string[]): string => [...l, ''].join('\n');
|
||||
|
||||
describe('a `->` call on a C++ receiver declared as a container\'s iterator', () => {
|
||||
it('reproduction: `std::deque<Slice>::const_iterator it; it->data()` calls the element\'s method', async () => {
|
||||
const cg = await indexed({
|
||||
'util/buffer.h': lines(
|
||||
'class Buffer {',
|
||||
' public:',
|
||||
' const char* data() const { return nullptr; }',
|
||||
'};',
|
||||
),
|
||||
'include/slice.h': lines(
|
||||
'class Slice {',
|
||||
' public:',
|
||||
' const char* data() const { return nullptr; }',
|
||||
'};',
|
||||
),
|
||||
'util/append.cc': lines(
|
||||
'#include <deque>',
|
||||
'#include "util/buffer.h"',
|
||||
'#include "include/slice.h"',
|
||||
'void Append(const std::deque<Slice>& operands) {',
|
||||
' for (std::deque<Slice>::const_iterator it = operands.begin(); it != operands.end(); ++it) {',
|
||||
' it->data();',
|
||||
' }',
|
||||
'}',
|
||||
),
|
||||
});
|
||||
try {
|
||||
expect(calls(cg, 'Append')).toEqual(['Slice::data']);
|
||||
} finally {
|
||||
cg.close();
|
||||
}
|
||||
});
|
||||
|
||||
it('an element of a library type has no project method to call', async () => {
|
||||
const cg = await indexed({
|
||||
'util/name.h': lines(
|
||||
'class Name {',
|
||||
' public:',
|
||||
' const char* c_str() const { return nullptr; }',
|
||||
'};',
|
||||
),
|
||||
'tools/print.cc': lines(
|
||||
'#include <string>',
|
||||
'#include <vector>',
|
||||
'#include "util/name.h"',
|
||||
'void Print(const std::vector<std::string>& names) {',
|
||||
' for (std::vector<std::string>::const_iterator itr = names.begin(); itr != names.end(); ++itr) {',
|
||||
' itr->c_str();',
|
||||
' }',
|
||||
'}',
|
||||
),
|
||||
});
|
||||
try {
|
||||
expect(calls(cg, 'Print')).toEqual([]);
|
||||
} finally {
|
||||
cg.close();
|
||||
}
|
||||
});
|
||||
|
||||
it('the iterator a project container\'s own alias names: protobuf\'s `RepeatedPtrField<T>::iterator`', async () => {
|
||||
const cg = await indexed({
|
||||
'src/repeated_ptr_field.h': lines(
|
||||
'namespace google {',
|
||||
'namespace protobuf {',
|
||||
'namespace internal {',
|
||||
'template <typename Element>',
|
||||
'class RepeatedPtrIterator {',
|
||||
' public:',
|
||||
' Element& operator*() const { return *it_; }',
|
||||
' Element* operator->() const { return it_; }',
|
||||
' private:',
|
||||
' Element* it_;',
|
||||
'};',
|
||||
'} // namespace internal',
|
||||
'template <typename Element>',
|
||||
'class RepeatedPtrField {',
|
||||
' public:',
|
||||
' typedef internal::RepeatedPtrIterator<Element> iterator;',
|
||||
' iterator begin() { return iterator(); }',
|
||||
'};',
|
||||
'} // namespace protobuf',
|
||||
'} // namespace google',
|
||||
),
|
||||
'src/descriptor.pb.h': lines(
|
||||
'namespace google {',
|
||||
'namespace protobuf {',
|
||||
'class FileDescriptorProto {',
|
||||
' public:',
|
||||
' int path_size() const { return 1; }',
|
||||
'};',
|
||||
'class SourceCodeInfo_Location {',
|
||||
' public:',
|
||||
' int path_size() const { return 0; }',
|
||||
'};',
|
||||
'} // namespace protobuf',
|
||||
'} // namespace google',
|
||||
),
|
||||
'src/option_interpreter.cc': lines(
|
||||
'#include "src/repeated_ptr_field.h"',
|
||||
'#include "src/descriptor.pb.h"',
|
||||
'namespace google {',
|
||||
'namespace protobuf {',
|
||||
'int Count(RepeatedPtrField<SourceCodeInfo_Location>* locs) {',
|
||||
' RepeatedPtrField<SourceCodeInfo_Location>::iterator loc = locs->begin();',
|
||||
' return loc->path_size();',
|
||||
'}',
|
||||
'} // namespace protobuf',
|
||||
'} // namespace google',
|
||||
),
|
||||
});
|
||||
try {
|
||||
expect(calls(cg, 'google::protobuf::Count')).toEqual([
|
||||
'google::protobuf::RepeatedPtrField::begin',
|
||||
'google::protobuf::SourceCodeInfo_Location::path_size',
|
||||
]);
|
||||
} finally {
|
||||
cg.close();
|
||||
}
|
||||
});
|
||||
|
||||
it('an element of the calling template\'s own parameter names no particular class', async () => {
|
||||
const cg = await indexed({
|
||||
'src/visit.cc': lines(
|
||||
'#include <vector>',
|
||||
'class Node {',
|
||||
' public:',
|
||||
' void Visit() {}',
|
||||
'};',
|
||||
'template <typename T>',
|
||||
'void VisitAll(typename std::vector<T>::iterator it) {',
|
||||
' it->Visit();',
|
||||
'}',
|
||||
),
|
||||
});
|
||||
try {
|
||||
expect(calls(cg, 'VisitAll')).toEqual([]);
|
||||
} finally {
|
||||
cg.close();
|
||||
}
|
||||
});
|
||||
|
||||
it('an element only a class template\'s own `operator*` names goes on as before', async () => {
|
||||
const cg = await indexed({
|
||||
'gtest/param.h': lines(
|
||||
'template <typename T>',
|
||||
'class ParamIterator {',
|
||||
' public:',
|
||||
' const T& operator*() const { return *value_; }',
|
||||
' const T* operator->() const { return value_; }',
|
||||
' private:',
|
||||
' const T* value_;',
|
||||
'};',
|
||||
'template <typename T>',
|
||||
'class ParamGenerator {',
|
||||
' public:',
|
||||
' typedef ParamIterator<T> iterator;',
|
||||
' iterator begin() const { return iterator(); }',
|
||||
'};',
|
||||
),
|
||||
'gtest/run.cc': lines(
|
||||
'#include "gtest/param.h"',
|
||||
'class Case {',
|
||||
' public:',
|
||||
' void Run() const {}',
|
||||
'};',
|
||||
'void RunAll(const ParamGenerator<Case>& gen) {',
|
||||
' ParamGenerator<Case>::iterator it = gen.begin();',
|
||||
' it->Run();',
|
||||
'}',
|
||||
),
|
||||
});
|
||||
try {
|
||||
expect(calls(cg, 'RunAll')).toEqual(['Case::Run', 'ParamGenerator::begin']);
|
||||
} finally {
|
||||
cg.close();
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -9756,13 +9756,21 @@ function cppRangeElementOf(type: CppWrittenType, at: UnresolvedRef, context: Res
|
||||
return begin ? cppIteratorElement(begin, at, context, hops) : null;
|
||||
}
|
||||
|
||||
/** A container's iterator type, `std::vector<Foo*>::const_iterator`, with the container. */
|
||||
const CPP_ITERATOR_OF = /^(.*>)\s*::\s*(?:const_)?(?:reverse_)?iterator\s*$/;
|
||||
|
||||
/** A written type without `typename`, `const` and `&`, which name no part of it. */
|
||||
function cppWithoutTypeKeywords(text: string): string {
|
||||
return text.replace(/\b(?:typename|const)\b|&/g, ' ').trim();
|
||||
}
|
||||
|
||||
/** What dereferencing an iterator of the type `type` gives, as written. */
|
||||
function cppIteratorElement(type: CppWrittenType, at: UnresolvedRef, context: ResolutionContext, hops: number): CppWrittenType | null {
|
||||
const iterator = cppFollowWrittenAliases(type, at, context);
|
||||
if (!iterator) return null;
|
||||
if (isCppPointerType(iterator.text)) return cppPointee(iterator);
|
||||
// `std::vector<Foo*>::const_iterator`: the container's element.
|
||||
const nested = /^(.*>)\s*::\s*(?:const_)?(?:reverse_)?iterator\s*$/.exec(iterator.text.replace(/\b(?:typename|const)\b|&/g, ' ').trim());
|
||||
const nested = CPP_ITERATOR_OF.exec(cppWithoutTypeKeywords(iterator.text));
|
||||
if (nested) return cppRangeElementOf({ ...iterator, text: nested[1]! }, at, context, hops + 1);
|
||||
const cls = cppWrittenClass(iterator, at, context);
|
||||
const star = cls ? cppReturnTypeOf(cppMethodsOf(cls, 'operator*', at, context), context) : null;
|
||||
@@ -10248,6 +10256,26 @@ function cppHeldType(raw: string): string | null {
|
||||
return cppTemplateArguments(raw)?.[0] ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* What `->` on a C++ value of the type `raw`, written at `site`, reaches, as
|
||||
* written: the type a library holder holds (cppHeldType), or the element of
|
||||
* the container whose iterator it is — `Slice` for
|
||||
* `std::deque<Slice>::const_iterator`, protobuf's `SourceCodeInfo_Location`
|
||||
* for `RepeatedPtrField<SourceCodeInfo_Location>::iterator`, read from the
|
||||
* container's template arguments before the `iterator` alias would lose them
|
||||
* (cppRangeElementOf). Null for any other type, and for an element a class
|
||||
* template's own `begin()` or `operator*` writes with its template parameter
|
||||
* (googletest's `ParamGenerator<T>::iterator`), which the specialization
|
||||
* decides.
|
||||
*/
|
||||
function cppArrowTarget(raw: string, site: CppNameSite, ref: UnresolvedRef, context: ResolutionContext): CppWrittenType | null {
|
||||
const held = cppHeldType(raw);
|
||||
if (held) return isCppLibraryHolder(raw, context) ? { ...site, text: held } : null;
|
||||
const container = CPP_ITERATOR_OF.exec(cppWithoutTypeKeywords(raw))?.[1];
|
||||
const element = container ? cppRangeElementOf({ ...site, text: container }, ref, context, 1) : null;
|
||||
return element && (element.scope === site.scope || !isCppTemplateParameterType(element)) ? element : null;
|
||||
}
|
||||
|
||||
/** Whether the holder `raw` names (cppHeldType) is the library's: no type of the project's has its name. */
|
||||
function isCppLibraryHolder(raw: string, context: ResolutionContext): boolean {
|
||||
const spelled = cppTypeSegments(raw)!.join('::');
|
||||
@@ -10258,10 +10286,11 @@ function isCppLibraryHolder(raw: string, context: ResolutionContext): boolean {
|
||||
* A `->` call on a C++ receiver declared as a standard smart pointer, an
|
||||
* optional or an `absl::StatusOr` (`std::unique_ptr<Iterator> iter;
|
||||
* iter->Valid()`, protobuf's `absl::StatusOr<FeatureResolver> resolver;
|
||||
* resolver->MergeFeatures(…)`) calls a member of
|
||||
* the type it holds. That type is looked up as C++ looks it up where the call
|
||||
* is written (cpp-supertypes.ts): the scopes around the call, the file's
|
||||
* `using`s, and aliases followed. Looking it up by its last name instead
|
||||
* resolver->MergeFeatures(…)`), or as a container's iterator
|
||||
* (`std::deque<Slice>::const_iterator it; it->data()`), calls a member of
|
||||
* the type it holds or points at (cppArrowTarget). That type is looked up
|
||||
* as C++ looks it up where the call is written (cpp-supertypes.ts): the
|
||||
* scopes around the call, the file's `using`s, and aliases followed. Looking it up by its last name instead
|
||||
* finds namesakes: 21 other classes in rocksdb are named `Iterator`, nested
|
||||
* ones like `MemTableRep::Iterator`, while the `Iterator` the call means
|
||||
* inherits `Valid` from `IteratorBase`.
|
||||
@@ -10271,18 +10300,19 @@ function isCppLibraryHolder(raw: string, context: ResolutionContext): boolean {
|
||||
* alias of one, or a class that surely lacks the method (cppLacksMethod:
|
||||
* not when a class template's specialization or a base the index doesn't
|
||||
* link may declare it).
|
||||
* - undefined when `raw` declares no such holder, or the type it holds is
|
||||
* none the project declares (a generated message, a class the index
|
||||
* doesn't name): the call goes on as before.
|
||||
* - undefined when `raw` declares no such holder or iterator, or the type
|
||||
* it holds is none the project declares (a generated message, a class the
|
||||
* index doesn't name): the call goes on as before.
|
||||
*/
|
||||
function matchCppHeldTypeCall(raw: string, methodName: string, ref: UnresolvedRef, context: ResolutionContext): ResolvedRef | null | undefined {
|
||||
const held = cppHeldType(raw);
|
||||
if (!held || !isCppLibraryHolder(raw, context)) return undefined;
|
||||
const caller = context.getNodeById?.(ref.fromNodeId);
|
||||
if (!caller || isCppPointerType(held)) return undefined;
|
||||
if (!caller) return undefined;
|
||||
const target = cppArrowTarget(raw, cppCallSite(caller, ref, context), ref, context);
|
||||
if (!target || isCppPointerType(target.text)) return undefined;
|
||||
const held = target.text;
|
||||
if (isCppLibraryType(held, context)) return null;
|
||||
// A template parameter's type (`std::unique_ptr<T>`, `typename T::Iter`) names no particular class: null.
|
||||
const found = cppClassWritten(held, cppCallSite(caller, ref, context), ref, context);
|
||||
const found = cppClassWritten(held, target, ref, context);
|
||||
if (found === undefined) return undefined;
|
||||
if (found === null) return null;
|
||||
if (!CPP_CLASS_KINDS.has(found.node.kind)) {
|
||||
@@ -13523,11 +13553,13 @@ export function matchMethodCall(
|
||||
if (isUndeclaredTypeName(cppDecl.written!, ref, context)) return null;
|
||||
}
|
||||
// A `->` call on a smart pointer, an optional or an `absl::StatusOr`
|
||||
// (cppHeldType) the calling function or its class declares is a call on
|
||||
// the type it holds:
|
||||
// (cppHeldType), or on a container's iterator, the calling function or
|
||||
// its class declares is a call on the type it holds or points at:
|
||||
// rocksdb's `std::unique_ptr<Iterator> iter; iter->Valid()` reached
|
||||
// `ArenaWrappedDBIter::Valid` by the method's name.
|
||||
if (ref.language === 'cpp' && cppDecl.callers && !cppDecl.aliased && !cppDecl.shadowed && !cppDecl.pointer &&
|
||||
// `ArenaWrappedDBIter::Valid` by the method's name. (The iterator a
|
||||
// container's own alias names counts too: protobuf's
|
||||
// `RepeatedPtrField<SourceCodeInfo_Location>::iterator`.)
|
||||
if (ref.language === 'cpp' && cppDecl.callers && !cppDecl.shadowed && !cppDecl.pointer &&
|
||||
cppMemberOperator(objectOrClass!, ref, context) === '->') {
|
||||
const held = nmTimedT('mc-cppheld', ref, () => matchCppHeldTypeCall(cppDecl.raw!, methodName!, ref, context));
|
||||
if (held !== undefined) return held;
|
||||
|
||||
Reference in New Issue
Block a user