Skip to content

fix(cpp): name reference-returning free functions + resolve qualified calls ns::a::Func(...)#790

Open
songwei163 wants to merge 2 commits into
colbymchenry:mainfrom
songwei163:fix/cpp-qualified-call-resolution
Open

fix(cpp): name reference-returning free functions + resolve qualified calls ns::a::Func(...)#790
songwei163 wants to merge 2 commits into
colbymchenry:mainfrom
songwei163:fix/cpp-qualified-call-resolution

Conversation

@songwei163

@songwei163 songwei163 commented Jun 11, 2026

Copy link
Copy Markdown

Two related C++ extraction bugs, both rooted in how the function NAME is taken from a function_definition's declarator. tree-sitter parses the code cleanly — these are extractor/resolver issues.

Fix 1 — reference-returning free functions are mis-named (unsearchable)

const std::string& GetRef(a::Ctx& c) wraps the function_declarator in a reference_declarator. extractCppQualifiedMethodName only resolved a name when the declarator contained a qualified_identifier; a plain-identifier free function fell through to the generic declarator-text fallback, which doesn't unwrap reference_declarator and indexed it as & GetRef(a::Ctx& c) — unsearchable, and its callers never resolved.

Now we walk the declarator chain to the NAME node only (unwrapping pointer/reference/parenthesized wrappers, never descending into the parameter list) and return plain identifiers directly. operator_name / destructor_name / template_function are still left to the generic fallback, which names them correctly.

10-pattern battery, before → after on current main:

pattern before after
free fn + qualified param GetThing(a::Ctx&) GetThing GetThing
pointer return int* GetPtr(...) GetPtr GetPtr
trailing return auto T(...)->a::Ret Trailing Trailing
qualified-type value return std::string FreeStrRet(...) FreeStrRet FreeStrRet
reference return const std::string& GetRef(a::Ctx&) & GetRef(a::Ctx& c) GetRef
operator operator==(const a::A&,...) operator== operator==
out-of-line member void Klass::Method(a::Ctx&) Method Method (recv=Klass)
template template<class T> a::R TplFn(a::Ctx&) TplFn TplFn
in-class method void Process(a::Ctx&) Process Process

(The "named after the first namespaced parameter type" variant — GetThingCtx — was already fixed on main; this PR closes the remaining reference-return case.)

Fix 2 — qualified call ns::a::Func(...) produces no calls edge

A C++ qualified call's function field is a qualified_identifier. In extractCall (src/extraction/tree-sitter.ts) it fell through to the generic else and stored the full ns::a::Func text as the callee name. Function nodes are stored under their simple name, so the name-based resolver never linked the calls edge — the callee reported "No callers".

namespace mmpayinspolicymgrao { namespace insured {
std::string GetInsured(const std::string& id) { return id; }
} }
// `callers GetInsured` was empty before this fix:
std::string CallIt() { return mmpayinspolicymgrao::insured::GetInsured("x"); }

Added a qualified_identifier branch (right after the existing scoped_identifier branch) that references the last :: segment, consistent with how the C++ extractor names nodes and how methods resolve.

Why safe

  • Other languages unaffected: qualified_identifier call nodes are C++-specific (Rust uses scoped_identifier, which keeps its existing full-text branch); the name-node walk is in the C++ extractor only.
  • Fix 1 is a pure rename/reclassify: no nodes added or dropped.
  • Fix 2's last-segment matching follows the resolver's existing best-effort, name-based strategy (same as methods) and is strictly better than no edge.

Tests

New cases under __tests__/extraction.test.ts → "C++ free-function name extraction":

  • reference-returning free function is named GetRef (not & GetRef(...)) and resolves cross-file callers;
  • fully-qualified call mmpayinspolicymgrao::insured::GetInsured("x") resolves to the defining file.

npm run build passes; the C++ extraction tests pass.

…allee

A C++ qualified call's `function` field is a `qualified_identifier`, which
fell through to the generic `else` in `extractCall` and stored the full
`ns::a::Func` text as the callee name. Function nodes are stored under their
SIMPLE name (the C++ extractor records the last `::` segment), so the
name-based resolver could never link the `calls` edge and these functions
reported "No callers".

Add a `qualified_identifier` branch that references the last `::` segment,
consistent with how the C++ extractor names nodes and how methods resolve.

Adds a regression test under "C++ free-function name extraction".
`const std::string& GetRef(a::Ctx& c)` wraps the function_declarator in a
reference_declarator. `extractCppQualifiedMethodName` only resolved a name
when the declarator contained a `qualified_identifier`, so a plain-identifier
free function fell through to the generic declarator-text fallback, which
doesn't unwrap reference_declarator and indexed it as `& GetRef(a::Ctx& c)` —
unsearchable, and its callers never resolved.

Walk the declarator chain (unwrapping pointer/reference/parenthesized
wrappers, never descending into the parameter list) to the NAME node and
return plain identifiers directly. operator/destructor/template names are
still left to the generic fallback, which handles them. Verified across a
10-pattern battery (qualified/plain params, pointer/reference/trailing
return, operator, template, out-of-line member, file-scope free fn).
@songwei163 songwei163 changed the title fix(cpp): resolve fully-qualified calls ns::a::Func(...) to their callee fix(cpp): name reference-returning free functions + resolve qualified calls ns::a::Func(...) Jun 11, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants