mirror of
https://github.com/colbymchenry/codegraph.git
synced 2026-10-16 20:53:57 +08:00
fix(cpp): a call on what a container's front()/back()/at() or a holder's get()/value() returns, like rocksdb's edits.back()->MarkAtomicGroup(…), calls the element's method (#2616)
A call on what a library container hands out linked to nothing: the
standard library's `front()` returns nothing the index can read, and the
library-receiver gate keeps the name strategies off a `std::vector`.
cppLibraryElementOf reads what such a member returns, where the
container's type is written:
- `front()`, `back()` and `at(i)` of a sequence (std/absl, and the project
sequences cppRangeElementOf already treats as such) → its element;
- a library map's `at(key)` → the mapped value;
- `std::stack`/`std::priority_queue` `top()`, `std::queue` `front()`/`back()`
→ the element;
- `get()` of `std::unique_ptr`/`std::shared_ptr`/`absl::NoDestructor` → a
pointer to the held type, `value()`/`value_or()` of an optional or an
`absl::StatusOr` → the held type.
It is asked in cppChainInnerType (a chain `recv.front().m()`) and in
cppExpressionType (an `auto` local, a range, an argument), next to
cppLibraryIteratorOf.
A/B on the cgstd kit against main 98127e54: leveldb, fmt and redis
identical; rocksdb 299 calls added, 89 retargeted (overloads picked by a
typed `.get()` argument, like `test::PerThreadDBPath(env_.get(), …)`, and
inherited methods on a typed element), 10 removed (guesses on what turned
out to be library containers) and 56 confidence-only; protobuf 7 added and
16 retargeted (cast helpers off their `std::shared_ptr` overloads, `Add`
off a deleted one). Every site was joined with the element type an
instrumented arm logged.
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 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 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 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. 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`. That includes an iterator an `auto` variable gets from a standard-library container's `begin()`, `find()` and the like, or from a standard search like `std::lower_bound`, as in facebook/rocksdb's `auto it = logs_.begin(); it->IsSyncing()`. A call on a dereferenced iterator or pointer, like `(*it)->Visit()` over a `std::vector<Node*>` or `(*it).Weight()` over a `std::vector<Node>`, and on a variable a dereference sets, like `auto first = *nodes.begin()`, links the same way, where before such a call went to whichever class had a method of that name. So does a call on the key or value a map's iterator points at, like `it->second.Flush()`, `it->second->Flush()` or a range-based `for` loop's `kv.second.Flush()`, and on the `first` or `second` of a `std::pair` the code declares. So does a call on an element read by subscript, like `statuses[i].IsNotFound()` over a `std::vector<Status>`, `buffers[key].Flush()` over a map, `iters[i]->Valid()` over owning pointers, or an element of an array or of a class's `operator[]`. 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`. That includes an iterator an `auto` variable gets from a standard-library container's `begin()`, `find()` and the like, or from a standard search like `std::lower_bound`, as in facebook/rocksdb's `auto it = logs_.begin(); it->IsSyncing()`. A call on a dereferenced iterator or pointer, like `(*it)->Visit()` over a `std::vector<Node*>` or `(*it).Weight()` over a `std::vector<Node>`, and on a variable a dereference sets, like `auto first = *nodes.begin()`, links the same way, where before such a call went to whichever class had a method of that name. So does a call on the key or value a map's iterator points at, like `it->second.Flush()`, `it->second->Flush()` or a range-based `for` loop's `kv.second.Flush()`, and on the `first` or `second` of a `std::pair` the code declares. So does a call on an element read by subscript, like `statuses[i].IsNotFound()` over a `std::vector<Status>`, `buffers[key].Flush()` over a map, `iters[i]->Valid()` over owning pointers, or an element of an array or of a class's `operator[]`. So does a call on what a container's `front()`, `back()` or `at()` hands out, like facebook/rocksdb's `edits.back()->MarkAtomicGroup(…)` or `wals.at(log_number).HasSyncedSize()` over a map, on a `std::stack`'s `top()` or a `std::queue`'s `front()`, and on what a smart pointer's `get()` or an optional's or `absl::StatusOr`'s `value()` returns, like `mock_clock.get()->GetCurrentTime(…)`, where before such calls linked to nothing; an overloaded function given such a value, like `test::PerThreadDBPath(env_.get(), …)`, now takes the overload for its type. 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 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 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.
|
- 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,137 @@
|
|||||||
|
/**
|
||||||
|
* A C++ call on what a library container or holder hands out, like
|
||||||
|
* `bufs.front().Flush()` over a `std::vector<Buffer>`, `by_id.at(1)->Flush()`
|
||||||
|
* over a map, `pending.top()->Valid()` over a `std::stack`, `iter.get()->Valid()`
|
||||||
|
* on a `std::unique_ptr` or `table.value().Size()` on a `std::optional`, calls
|
||||||
|
* a member of the element's class. The library's member returned nothing the
|
||||||
|
* index could read, so such calls linked to nothing.
|
||||||
|
*/
|
||||||
|
import { describe, it, expect, beforeAll, afterAll } 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 lines = (...l: string[]): string => [...l, ''].join('\n');
|
||||||
|
|
||||||
|
describe('a C++ call on what a library container or holder hands out', () => {
|
||||||
|
let root: string;
|
||||||
|
let cg: CodeGraph;
|
||||||
|
|
||||||
|
beforeAll(async () => {
|
||||||
|
root = fs.mkdtempSync(path.join(os.tmpdir(), 'codegraph-cpp-container-accessors-'));
|
||||||
|
const write = (file: string, text: string): void => {
|
||||||
|
fs.mkdirSync(path.dirname(path.join(root, file)), { recursive: true });
|
||||||
|
fs.writeFileSync(path.join(root, file), text);
|
||||||
|
};
|
||||||
|
// `Other` comes first: a call matched by its bare name lands on it.
|
||||||
|
write('src/types.h', lines(
|
||||||
|
'class Other {',
|
||||||
|
' public:',
|
||||||
|
' void Flush() {}',
|
||||||
|
' bool Valid() const { return false; }',
|
||||||
|
' int Size() const { return 0; }',
|
||||||
|
'};',
|
||||||
|
'class Buffer {',
|
||||||
|
' public:',
|
||||||
|
' void Flush() {}',
|
||||||
|
'};',
|
||||||
|
'class Iterator {',
|
||||||
|
' public:',
|
||||||
|
' bool Valid() const { return true; }',
|
||||||
|
'};',
|
||||||
|
'class Table {',
|
||||||
|
' public:',
|
||||||
|
' int Size() const { return 1; }',
|
||||||
|
'};',
|
||||||
|
// A project class named like a standard container is that class.
|
||||||
|
'class list {',
|
||||||
|
' public:',
|
||||||
|
' Other front() { return Other(); }',
|
||||||
|
'};',
|
||||||
|
));
|
||||||
|
write('src/autovector.h', lines(
|
||||||
|
'namespace rocksdb {',
|
||||||
|
'template <class T, int kSize = 8>',
|
||||||
|
'class autovector {',
|
||||||
|
' public:',
|
||||||
|
' T& front() { return values_[0]; }',
|
||||||
|
' T& back() { return values_[kSize - 1]; }',
|
||||||
|
' private:',
|
||||||
|
' T values_[kSize];',
|
||||||
|
'};',
|
||||||
|
'} // namespace rocksdb',
|
||||||
|
));
|
||||||
|
write('src/use.cc', lines(
|
||||||
|
'#include <map>',
|
||||||
|
'#include <memory>',
|
||||||
|
'#include <optional>',
|
||||||
|
'#include <queue>',
|
||||||
|
'#include <stack>',
|
||||||
|
'#include <string>',
|
||||||
|
'#include <vector>',
|
||||||
|
'#include "src/autovector.h"',
|
||||||
|
'#include "src/types.h"',
|
||||||
|
'void Front(std::vector<Buffer>& bufs) { bufs.front().Flush(); }',
|
||||||
|
'void Back(std::vector<Iterator*>* iters) { iters->back()->Valid(); }',
|
||||||
|
'void At(const std::vector<Table>& tables) { tables.at(0).Size(); }',
|
||||||
|
'void MapAt(std::map<int, Buffer*>& by_id) { by_id.at(1)->Flush(); }',
|
||||||
|
'void Top(std::stack<Iterator*>& pending) { pending.top()->Valid(); }',
|
||||||
|
'void QueueFront(std::queue<Buffer>& queue) { queue.front().Flush(); }',
|
||||||
|
'void Get(const std::unique_ptr<Iterator>& iter) { iter.get()->Valid(); }',
|
||||||
|
'void Value(const std::optional<Table>& table) { table.value().Size(); }',
|
||||||
|
'void StatusOrValue(absl::StatusOr<Buffer>& buf) { buf.value().Flush(); }',
|
||||||
|
'void Owned(std::vector<std::unique_ptr<Iterator>>& iters) { iters.back()->Valid(); }',
|
||||||
|
'void Local(std::vector<Buffer>& bufs) { auto& last = bufs.back(); last.Flush(); }',
|
||||||
|
'void ProjectSequence(rocksdb::autovector<Buffer*>& bufs) { bufs.front()->Flush(); }',
|
||||||
|
'void ProjectList(list& l) { l.front().Flush(); }',
|
||||||
|
'void Strings(std::vector<std::string>& names) { names.front().size(); }',
|
||||||
|
'template <typename T>',
|
||||||
|
'void Generic(std::vector<T>& items) { items.front().Flush(); }',
|
||||||
|
));
|
||||||
|
cg = await CodeGraph.init(root, { index: true });
|
||||||
|
}, 120_000);
|
||||||
|
|
||||||
|
afterAll(() => {
|
||||||
|
cg?.close();
|
||||||
|
if (root) fs.rmSync(root, { recursive: true, force: true });
|
||||||
|
});
|
||||||
|
|
||||||
|
/** `calls` callees of the function `name`, as `qualifiedName`. */
|
||||||
|
const calls = (name: string): string[] => {
|
||||||
|
const caller = cg.getNodesByKind('function').find((n: Node) => n.name === name);
|
||||||
|
expect(caller, name).toBeDefined();
|
||||||
|
return cg.getCallees(caller!.id).filter((r) => r.edge.kind === 'calls').map((r) => r.node.qualifiedName).sort();
|
||||||
|
};
|
||||||
|
|
||||||
|
it("a sequence's front(), back() and at(i)", () => {
|
||||||
|
expect(calls('Front')).toEqual(['Buffer::Flush']);
|
||||||
|
expect(calls('Back')).toEqual(['Iterator::Valid']);
|
||||||
|
expect(calls('At')).toEqual(['Table::Size']);
|
||||||
|
expect(calls('Owned')).toEqual(['Iterator::Valid']);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("a map's at(key), a stack's top() and a queue's front()", () => {
|
||||||
|
expect(calls('MapAt')).toEqual(['Buffer::Flush']);
|
||||||
|
expect(calls('Top')).toEqual(['Iterator::Valid']);
|
||||||
|
expect(calls('QueueFront')).toEqual(['Buffer::Flush']);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("a smart pointer's get() and an optional's or a StatusOr's value()", () => {
|
||||||
|
expect(calls('Get')).toEqual(['Iterator::Valid']);
|
||||||
|
expect(calls('Value')).toEqual(['Table::Size']);
|
||||||
|
expect(calls('StatusOrValue')).toEqual(['Buffer::Flush']);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('a local the accessor initializes, and a project sequence', () => {
|
||||||
|
expect(calls('Local')).toEqual(['Buffer::Flush']);
|
||||||
|
expect(calls('ProjectSequence')).toEqual(['Buffer::Flush', 'rocksdb::autovector::front']);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("a project class of a container's name, a library element and a template's own", () => {
|
||||||
|
expect(calls('ProjectList')).toEqual(['Other::Flush', 'list::front']);
|
||||||
|
expect(calls('Strings')).toEqual([]);
|
||||||
|
expect(calls('Generic')).toEqual([]);
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -9904,6 +9904,46 @@ function cppLibraryIteratorOf(object: CppWrittenType, member: string): CppWritte
|
|||||||
return CPP_LIBRARY_SEQUENCES.has(last) || CPP_LIBRARY_MAPS.has(last) ? { ...object, text: `${container}::${kind}` } : null;
|
return CPP_LIBRARY_SEQUENCES.has(last) || CPP_LIBRARY_MAPS.has(last) ? { ...object, text: `${container}::${kind}` } : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** The standard library's container adaptors, whose element is their first template argument. */
|
||||||
|
const CPP_LIBRARY_ADAPTORS: ReadonlySet<string> = new Set(['stack', 'queue', 'priority_queue']);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* What the member `member` of a library container or holder of the type
|
||||||
|
* `object` hands out, where the container's type is written: an element of
|
||||||
|
* a sequence for `front()`, `back()` and `at(i)` — of one of
|
||||||
|
* CPP_PROJECT_SEQUENCES' too — the value a map's `at(key)` maps to, the
|
||||||
|
* element of a `std::stack`'s `top()` or a `std::queue`'s `front()` and
|
||||||
|
* `back()`, the pointer a smart pointer's `get()` returns and the value an
|
||||||
|
* optional's or an `absl::StatusOr`'s `value()` returns (cppHeldType).
|
||||||
|
* rocksdb's `cfds.front()->GetName()` over an `autovector<ColumnFamilyData*>`
|
||||||
|
* calls ColumnFamilyData's. Null for any other member or type.
|
||||||
|
*/
|
||||||
|
function cppLibraryElementOf(object: CppWrittenType, member: string, at: UnresolvedRef, context: ResolutionContext): CppWrittenType | null {
|
||||||
|
if (isCppPointerType(object.text)) return null;
|
||||||
|
const text = cppWithoutTypeKeywords(object.text);
|
||||||
|
const segments = cppTypeSegments(text);
|
||||||
|
const last = segments?.[segments.length - 1];
|
||||||
|
if (!segments || !last) return null;
|
||||||
|
const library = segments[0] === 'std' || segments[0] === 'absl';
|
||||||
|
if (member === 'front' || member === 'back' || member === 'at') {
|
||||||
|
if ((library && CPP_LIBRARY_SEQUENCES.has(last)) || CPP_PROJECT_SEQUENCES.has(last)) return cppRangeElementOf(object, at, context);
|
||||||
|
if (member === 'at' && library && CPP_LIBRARY_MAPS.has(last)) {
|
||||||
|
const value = cppTemplateArguments(text)?.[1];
|
||||||
|
return value ? { ...object, text: cppWithoutNullability(value) } : null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (segments[0] === 'std' && CPP_LIBRARY_ADAPTORS.has(last) && (member === 'top' || ((member === 'front' || member === 'back') && last === 'queue'))) {
|
||||||
|
const element = cppTemplateArguments(text)?.[0];
|
||||||
|
return element ? { ...object, text: cppWithoutNullability(element) } : null;
|
||||||
|
}
|
||||||
|
const held = cppHeldType(text);
|
||||||
|
if (!held || held.includes('[')) return null;
|
||||||
|
const holder = segments.join('::');
|
||||||
|
if (member === 'get' && (holder === 'std::unique_ptr' || holder === 'std::shared_ptr' || holder === 'absl::NoDestructor')) return { ...object, text: `${held}*` };
|
||||||
|
if ((member === 'value' || member === 'value_or') && (holder === 'std::optional' || holder === 'absl::optional' || holder === 'absl::StatusOr')) return { ...object, text: held };
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
/** What dereferencing an iterator of the type `type` gives, as written. */
|
/** What dereferencing an iterator of the type `type` gives, as written. */
|
||||||
function cppIteratorElement(type: CppWrittenType, at: UnresolvedRef, context: ResolutionContext, hops: number): CppWrittenType | null {
|
function cppIteratorElement(type: CppWrittenType, at: UnresolvedRef, context: ResolutionContext, hops: number): CppWrittenType | null {
|
||||||
const iterator = cppFollowWrittenAliases(type, at, context);
|
const iterator = cppFollowWrittenAliases(type, at, context);
|
||||||
@@ -10020,6 +10060,9 @@ function cppExpressionType(expr: string, at: UnresolvedRef, context: ResolutionC
|
|||||||
// `logs_.begin()`, `map.find(key)`: the iterator a library container hands out.
|
// `logs_.begin()`, `map.find(key)`: the iterator a library container hands out.
|
||||||
const iterator = object && !member[4] ? cppLibraryIteratorOf(object, member[3]!) : null;
|
const iterator = object && !member[4] ? cppLibraryIteratorOf(object, member[3]!) : null;
|
||||||
if (iterator) return iterator;
|
if (iterator) return iterator;
|
||||||
|
// `ws.front()`, `m.at(key)`, `p.get()`, `s.value()`: what it holds.
|
||||||
|
const element = object && !member[4] ? cppLibraryElementOf(object, member[3]!, at, context) : null;
|
||||||
|
if (element) return element;
|
||||||
const cls = object && cppWrittenClass(object, at, context);
|
const cls = object && cppWrittenClass(object, at, context);
|
||||||
if (!cls) return null;
|
if (!cls) return null;
|
||||||
const methods = cppMethodsOf(cls, member[3]!, at, context);
|
const methods = cppMethodsOf(cls, member[3]!, at, context);
|
||||||
@@ -12330,6 +12373,8 @@ function cppChainInnerType(inner: string, ref: UnresolvedRef, context: Resolutio
|
|||||||
const followed = recvType && cppFollowWrittenAliases(recvType, ref, context);
|
const followed = recvType && cppFollowWrittenAliases(recvType, ref, context);
|
||||||
if (!followed) return null;
|
if (!followed) return null;
|
||||||
const object = op === '->' ? cppDereferenced(followed, ref, context) : isCppPointerType(followed.text) ? null : followed;
|
const object = op === '->' ? cppDereferenced(followed, ref, context) : isCppPointerType(followed.text) ? null : followed;
|
||||||
|
const element = object && cppLibraryElementOf(object, inner.slice(dot + 1), ref, context);
|
||||||
|
if (element) return element;
|
||||||
const cls = object && cppWrittenClass(object, ref, context);
|
const cls = object && cppWrittenClass(object, ref, context);
|
||||||
return cls ? cppReturnTypeOf(cppMethodsOf(cls, inner.slice(dot + 1), ref, context), context) : null;
|
return cls ? cppReturnTypeOf(cppMethodsOf(cls, inner.slice(dot + 1), ref, context), context) : null;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user