mirror of
https://github.com/colbymchenry/codegraph.git
synced 2026-10-25 03:49:59 +08:00
fix(cpp): a method a macro of its file defines, like protobuf's PROTOBUF_DEFINE_ACCESSOR(FieldDescriptor, number, int), is indexed where the macro is invoked (#2605)
protobuf declares its descriptor accessors in their classes and defines
them below with macros, like `PROTOBUF_DEFINE_ACCESSOR(FieldDescriptor,
number, int)`; its reflection pastes `Reflection::Get##TYPENAME` in
another. The parser never sees those definitions (the preParse blanks the
invocations) and a declaration mints no node, so the methods had none and
every call to them linked to nothing.
- After either extractor (the kernel or the wasm walker), a step over the
original source expands the file's own function-like macros whose body
defines `Class::method(...) ... {`, at each invocation at namespace
scope, and mints a method node per definition: named and qualified like
an out-of-line definition, contained by the file and the same-file
class, with the expanded head as its signature. Constructors,
destructors and variadic macros are left out.
- The parse worker leaves the raw kernel fast path only for a file that
may define one (a cheap text test), so the step sees it.
- The C++ resolver reads such a method's return type and parameters from
its signature, and never takes it for a deleted overload.
A/B vs main on the kernel path, every changed site triaged: leveldb and
redis identical; fmt +13 nodes and rocksdb +11 (gtest's TestEventRepeater
overrides); protobuf +257 nodes, 6,203 calls and 2 references added, 252
calls retargeted, 11 metadata-only, none removed. Kernel and wasm dumps
are byte-identical, protobuf's one pre-existing Swift line aside.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
4727a068e4
commit
f7951a13db
@@ -9438,17 +9438,31 @@ function cppSubscriptElement(type: CppWrittenType, at: UnresolvedRef, context: R
|
||||
return element ? { ...element, text: element.text.replace(/&+\s*$/, '').trim() } : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* The expanded definition a C++ method that a macro defines carries as its
|
||||
* signature (cpp-macro-methods.ts), `inline const FileDescriptor*
|
||||
* Descriptor::file() const` for protobuf's `PROTOBUF_DEFINE_ACCESSOR(Descriptor,
|
||||
* file, const FileDescriptor*)`: its source is the invocation, which spells
|
||||
* neither its return type nor its parameters. Null for any other node.
|
||||
*/
|
||||
function cppMacroMethodHead(fn: Node): string | null {
|
||||
if (fn.kind !== 'method' || !fn.signature) return null;
|
||||
const name = fn.name.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
||||
return new RegExp(`::\\s*${name}\\s*\\(`).test(fn.signature) ? fn.signature : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* The return type of a C or C++ function, as its declaration writes it:
|
||||
* `std::vector<const FieldDescriptor*>` for `std::vector<const
|
||||
* FieldDescriptor*> FieldNumberOrder(…)`. (The index keeps only `vector`.)
|
||||
* Null for none that can be read: a constructor, `void`, `auto` or
|
||||
* `decltype(…)`.
|
||||
* `decltype(…)`. A method a macro defines is read from its expansion.
|
||||
*/
|
||||
function cppWrittenReturnType(fn: Node, context: ResolutionContext): string | null {
|
||||
const lines = context.getFileLines?.(fn.filePath) ?? context.readFile(fn.filePath)?.split(/\r?\n/);
|
||||
if (!lines) return null;
|
||||
const head = lines.slice(fn.startLine - 1, fn.startLine + 2).join(' ').slice(fn.startColumn);
|
||||
const macro = cppMacroMethodHead(fn);
|
||||
const lines = macro === null ? context.getFileLines?.(fn.filePath) ?? context.readFile(fn.filePath)?.split(/\r?\n/) : null;
|
||||
if (macro === null && !lines) return null;
|
||||
const head = macro ?? lines!.slice(fn.startLine - 1, fn.startLine + 2).join(' ').slice(fn.startColumn);
|
||||
const name = fn.name.replace(/[.*+?^${}()|[\]\\]/g, '\\$&').replace(/^operator/, 'operator\\s*');
|
||||
const declared = new RegExp(String.raw`^(.*?)(?:\b[A-Za-z_]\w*\s*(?:<[^<>;{}]*>)?\s*::\s*)*\b${name}\s*\(`).exec(head);
|
||||
let written = declared?.[1];
|
||||
@@ -11169,9 +11183,11 @@ const CPP_DELETED_FUNCTIONS = new WeakMap<ResolutionContext, Map<string, boolean
|
||||
/**
|
||||
* Whether the C++ function or method `n`, named `name`, is declared deleted:
|
||||
* from its name to the declaration's `;` or body, it says `= delete`
|
||||
* (protobuf's `void reset(T* PROTOBUF_NULLABLE) = delete;`).
|
||||
* (protobuf's `void reset(T* PROTOBUF_NULLABLE) = delete;`). A method a macro
|
||||
* defines has a body.
|
||||
*/
|
||||
function isCppDeletedFunction(n: Node, name: string, context: ResolutionContext): boolean {
|
||||
if (cppMacroMethodHead(n) !== null) return false;
|
||||
let memo = CPP_DELETED_FUNCTIONS.get(context);
|
||||
if (!memo) CPP_DELETED_FUNCTIONS.set(context, (memo = new Map()));
|
||||
const hit = memo.get(n.id);
|
||||
@@ -16859,7 +16875,11 @@ function cppTemplateArgumentsEnd(text: string, at: number): number {
|
||||
/** The balanced `( … )` after `name` from `line`/`column` of `file` (up to a dozen lines), or null. */
|
||||
function cppParenListAfter(file: string, line: number, column: number, name: string, context: ResolutionContext): string | null {
|
||||
const lines = context.getFileLines?.(file) ?? context.readFile(file)?.split(/\r?\n/) ?? [];
|
||||
const text = lines.slice(line - 1, line + 11).join('\n');
|
||||
return cppParenListIn(lines.slice(line - 1, line + 11).join('\n'), column, name);
|
||||
}
|
||||
|
||||
/** The `( … )` that follows `name` in `text`, from `column` on, without its parentheses. */
|
||||
function cppParenListIn(text: string, column: number, name: string): string | null {
|
||||
const at = new RegExp(`\\b${name.replace(/[~]/g, '\\$&')}\\s*(?:<[^<>()]*>)?\\s*\\(`).exec(text.slice(column));
|
||||
if (!at) return null;
|
||||
const open = column + at.index + at[0].length - 1;
|
||||
@@ -16913,10 +16933,11 @@ const CPP_OVERLOAD_PARAMETERS = new WeakMap<ResolutionContext, Map<string, strin
|
||||
* absl::optional<Semantic> semantic)` below the class, whose declaration
|
||||
* gave `semantic` its `= absl::nullopt`. Its own list when the class
|
||||
* declares the method with the same parameter types and no default, or
|
||||
* not at all.
|
||||
* not at all. A method a macro defines writes its list in its expansion.
|
||||
*/
|
||||
function cppOverloadParameters(n: Node, name: string, context: ResolutionContext): string | null {
|
||||
const own = cppParenListAfter(n.filePath, n.startLine, 0, name, context);
|
||||
const macro = cppMacroMethodHead(n);
|
||||
const own = macro !== null ? cppParenListIn(macro, 0, name) : cppParenListAfter(n.filePath, n.startLine, 0, name, context);
|
||||
if (own === null || n.kind !== 'method') return own;
|
||||
let memo = CPP_OVERLOAD_PARAMETERS.get(context);
|
||||
if (!memo) CPP_OVERLOAD_PARAMETERS.set(context, (memo = new Map()));
|
||||
|
||||
Reference in New Issue
Block a user