fix(cpp-extractor): capture default-value params, templated declarations, and nested-namespace method names#438
Conversation
…ons, and nested-namespace method names Three edge-case bugs in the C++ tree-sitter extractor: 1. extractParams only iterated parameter_declaration nodes, so any parameter with a default value (parsed as optional_parameter_declaration) was silently dropped. Now both node types are handled in source order. 2. walkTopLevel had no template_declaration case, so all templated free functions and templated classes/structs were dropped. Added a case that dispatches the inner declaration through the existing handlers. 3. extractFuncDeclName mis-parsed nested qualified_identifiers: for void net::Server::start() it produced name 'Server::start' and qualifier 'net'. Now walks down the name field to the leaf identifier, tracking the immediately-enclosing scope, yielding name 'start' / qualifier 'Server'. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
A few concerns before this lands. (Surfacing because the same LanguageExtractor pattern is in flight for Dart in #435 — the optional-param / generics-in-qualifier shape repeats there, so worth aligning.)
1. Call-graph caller name is a silent behavior change. extractCallGraph → extractFunctionName → extractFuncDeclName. For void net::Server::start() { foo(); } the caller string was "Server::start" and is now "start". Anything keying call-graph edges on the prior form (dashboard nodes, the linker, snapshot tests in other packages) will see edges rename. Please grep before merge, or call this out as intentional.
2. The "templated declarations" fix is partial. walkTopLevel's new template_declaration arm only handles one nesting level + three inner shapes. These still drop silently:
- Nested
template_declaration(out-of-class member templates:template<class T> template<class U> void Box<T>::set(U)). - In-class member templates —
extractClassOrStructhas notemplate_declarationarm at all, soclass Foo { template<class T> void bar(T); };losesbar.
At minimum, a TODO comment so the next reader doesn't think these are covered.
3. Templated qualifier mismatch. For void Box<int>::get() the scope text is "Box<int>", so methodsByClass.get("Box<int>") misses the class registered as "Box". Pre-existing, but the nested-walk loop makes it more reachable. A qualifier.replace(/<.*>$/, "") would fix the common case; otherwise file a follow-up.
4. Test gaps.
- The
net::Server::starttest is effectively two-level — a regression that only descends one level would still pass it. A three-level case (void a::b::Server::foo()) would lock the loop. - The fixture puts
class Serverat file scope and then definesnet::Server::start— syntactically legal but doesn't exercise namespace recursion + qualifier resolution together. Anamespace net { class Server {...}; } void net::Server::start(){}fixture would. - No direct assertion on
qualifier; the current "no function namedServer::start" is a negative existence check.
5. Variadic params. variadic_parameter_declaration (e.g. void f(Ts... xs)) isn't in the new accept-list. Probably fine if unwrapDeclaratorName reaches the identifier through the pack-expansion declarator, but worth a one-line test to confirm — and same question applies to my Dart PR for [positional] / {named: default} shapes once cpp lands.
Nit: stale JSDoc on extractFuncDeclName (lines 28–38) — still describes the simple case only.
…plated qualifiers Addresses review on PR Egonex-AI#438: - Variadic parameter packs (`Ts... xs`) are now captured via variadic_parameter_declaration in extractParams. - Templated qualifiers (`Box<T>::get`) strip the template_type arguments so the method resolves against the bare class registered as "Box". - In-class member templates (`class Foo { template<class T> void bar(T); };`) are captured (both declaration-only and inline) via a template_declaration arm in the class member loop. - Out-of-class member templates with nested template_declaration (`template<class T> template<class U> void Box<T>::set(U)`) are captured by recursing through nested template_declaration nodes. - Documented the intentional leaf-name choice for qualified call-graph caller names and refreshed the extractFuncDeclName JSDoc. Adds tests locking each behavior plus a combined namespace+qualifier fixture, a three-segment qualifier walk, and a positive class-method association assertion. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
All five points plus the nit are addressed in 1 — Call-graph caller name. Confirmed this only changes names with >=3 segments: for single-level 2 — Member templates. Implemented, not deferred. In-class member templates ( 3 — Templated qualifier. Fixed. The 4 — Test gaps. Added a 5 — Variadic params. Confirmed Nit — JSDoc. Updated to describe the deep-nesting leaf/immediate-scope behavior and the templated-qualifier stripping. Full core suite green (703 passed); |
Problem
Three edge-case bugs caused the C++ tree-sitter extractor to silently drop real code constructs:
Default-valued parameters dropped.
extractParamsonly iteratedparameter_declarationnodes. A parameter with a default value (e.g.int b = 10) is parsed by tree-sitter-cpp as anoptional_parameter_declaration, so it was never captured.int add(int a, int b = 10)yielded params["a"], andvoid f(int a = 1, int b = 2)yielded[].Templated functions and classes dropped.
walkTopLevelhad notemplate_declarationcase. Any templated free function or templated class/struct is wrapped in atemplate_declarationnode, so all of them vanished fromfunctions/classes/exports.template <typename T> T identity(T x) {}produced no function;template <typename T> class Box { ... };produced no class.Wrong method name/qualifier for nested-namespace definitions.
extractFuncDeclNameread only the topnamefield and the firstnamespace_identifierfor aqualified_identifier. For a deeply scoped definition likevoid net::Server::start()the qualified_identifiers nest, so the name becameServer::startand the qualifier becamenet— polluting the functions list and registering the method under the wrong class.Fix
packages/core/src/plugins/extractors/cpp-extractor.ts:extractParamsnow iterates all params in source order, handling bothparameter_declarationandoptional_parameter_declaration.template_declarationcase towalkTopLevelthat dispatches the innerfunction_definition/class_specifier/struct_specifierthrough the existing handlers.extractFuncDeclNamenow walks down thenamefield of nestedqualified_identifiers to the leaf identifier, tracking the immediately-enclosingscopeas the qualifier — yielding namestart/ qualifierServerforvoid net::Server::start().(Also dropped the now-unused
findChildrenimport.)Testing
Added five edge-case tests in
cpp-extractor.test.ts(default-value param, all-default params, templated free function, templated class, nested-namespace out-of-class method). All five fail before the fix and pass after. The full core suite (697 tests) is green, andtsc --noEmit+eslinton the changed files both pass clean.🤖 Generated with Claude Code