diff --git a/__tests__/extraction.test.ts b/__tests__/extraction.test.ts index d7947073a..d081a29db 100644 --- a/__tests__/extraction.test.ts +++ b/__tests__/extraction.test.ts @@ -4074,6 +4074,35 @@ class Both : public Base, public Plain {}; }); }); + describe('C++ out-of-line template method qualifier (#1286)', () => { + // An out-of-line template method definition + // (`template T Box::get()`) recorded its class qualifier as the + // full instantiation `Box`, so the method's qualifiedName was `Box::get`. + // That never name-matched the class node indexed as `Box`, and long/multi-line + // template params could push the qualifiedName past the 255-byte filename + // limit. The `<…>` args are stripped so the qualifier is the bare `Box`, + // exactly like #1043 did for templated base classes. Inline method bodies were + // already unaffected. + it('strips template args from an out-of-line template method qualifier', () => { + const code = ` +template +class Box { public: T get() const; void set(T v); private: T value; }; + +template T Box::get() const { return value; } +template void Box::set(T v) { value = v; } +`; + const nodes = extractFromSource('box.cpp', code).nodes; + const get = nodes.find((n) => n.name === 'get'); + const set = nodes.find((n) => n.name === 'set'); + + // Qualifier is the bare class, not the `Box` instantiation. + expect(get?.qualifiedName).toBe('Box::get'); + expect(set?.qualifiedName).toBe('Box::set'); + // No node still carries angle brackets in its qualified name. + expect(nodes.find((n) => n.qualifiedName?.includes('<'))).toBeUndefined(); + }); + }); + describe('C++ stack-allocation construction (#1035)', () => { // `Calculator calc(0)` (direct-init) and `Widget w{1, 2}` (brace-init) carry // the constructor args directly on the declarator — no call/new node — so diff --git a/src/extraction/languages/c-cpp.ts b/src/extraction/languages/c-cpp.ts index 76a093965..7bfb69add 100644 --- a/src/extraction/languages/c-cpp.ts +++ b/src/extraction/languages/c-cpp.ts @@ -88,7 +88,14 @@ function extractCppReceiverType(node: SyntaxNode, source: string): string | unde if (!declarator) return undefined; const qid = findDeclaratorQualifiedId(declarator); if (!qid) return undefined; - const parts = getNodeText(qid, source).trim().split('::').filter(Boolean); + // Strip template arguments before splitting on `::` so an out-of-line template + // method definition (`template T Box::get()`) yields the bare + // class `Box`, not `Box` — matching the class node indexed as `Box`, the + // same reason #1043 stripped them from templated base-class references. Doing it + // before the split also tolerates `::` inside the args (`Box::get`). + const parts = stripCppTemplateArgs(getNodeText(qid, source).trim()) + .split('::') + .filter(Boolean); return parts.length > 1 ? parts.slice(0, -1).join('::') : undefined; }