ソースを参照

fix(cpp): strip template args from out-of-line method receiver qualifiers (#1309)

template<typename T> T Box<T>::get() stored qualified_name Box<T>::get —
the <T> qualifier never matched the class node indexed as Box, so the
method didn't link to its class, while the inline form of the same
method produced Box::get. ICU-shaped multi-line template parameter
lists leaked whole <…> blocks (newlines included) into qualified_name,
exceeding NAME_MAX for downstream consumers.

extractCppReceiverType now applies stripCppTemplateArgs (the #1043
normalization for base-class refs) to the receiver qualifier.

fmt re-index: template-arg-in-qualifier names 25 -> 4 (remaining are a
FMT_BEGIN_EXPORT misparse artifact and gmock conversion-operator names,
both distinct pre-existing shapes), node count byte-stable at 7,536.

Fixes #1286

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Colby Mchenry 1 ヶ月 前
コミット
4dd29ea5c1
3 ファイル変更70 行追加1 行削除
  1. 1 0
      CHANGELOG.md
  2. 60 0
      __tests__/extraction.test.ts
  3. 9 1
      src/extraction/languages/c-cpp.ts

+ 1 - 0
CHANGELOG.md

@@ -18,6 +18,7 @@ and adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
 
 ### Fixes
 
+- C++ methods defined out-of-line on a template class (`template <typename T> T Box<T>::get() { ... }`) no longer keep the template parameter list in their qualified name. They now index as `Box::get` — identical to an inline definition of the same method — so they link to their class and resolve from call sites again, and pathological multi-line template parameter lists can no longer blow the qualified name past filesystem name limits. (#1286)
 - Go route detection no longer misidentifies ordinary method calls that share HTTP verb names — `cache.Put("key", value)`, `store.Get("config", out)`, `bus.Handle("user.created", handler)` and the like were being indexed as HTTP routes, polluting route listings in cache-heavy codebases. A registration now has to look like one: its first argument must be a `/`-prefixed path (all routers) or a Go 1.22 `"METHOD /path"` pattern on `Handle`/`HandleFunc`, which now also extracts the method instead of listing the route as `ANY`. (#1259)
 - Progress output on Windows no longer mixes ASCII `|` rails with the Unicode `│ ◆ ●` frame around them. In terminals that render Unicode (Windows Terminal, VS Code, ConEmu/Cmder, JetBrains, Alacritty), the whole `codegraph init` / `index` / `sync` block now draws with matching box-drawing characters; unrecognized legacy consoles keep the safe all-ASCII output that avoids garbled characters. `CODEGRAPH_ASCII=1` / `CODEGRAPH_UNICODE=1` still override in either direction. (#398)
 - CLI output now honors the `NO_COLOR` convention and new `--color` / `--no-color` flags, and goes plain automatically when piped: commands like `codegraph status`, `query`, `callers`, and `files` no longer embed ANSI color codes when stdout isn't a terminal, and a piped `codegraph init` / `index` / `sync` prints simple per-phase lines instead of progress-animation control characters. `FORCE_COLOR` or `--color` forces color back on for pipes that render it. (#1281)

+ 60 - 0
__tests__/extraction.test.ts

@@ -4074,6 +4074,66 @@ class Both : public Base<char>, public Plain {};
     });
   });
 
+  describe('C++ out-of-line template method receivers (#1286)', () => {
+    // `template<typename T> T Box<T>::get()` used to store qualified_name
+    // `Box<T>::get` — the `<T>` qualifier never matched the class node indexed
+    // as `Box`, and long multi-line parameter lists could push qualified_name
+    // past NAME_MAX. Inline definitions of the same method produce `Box::get`,
+    // so the out-of-line form must normalize to the identical name.
+    it('strips the template parameter list from the receiver qualifier', () => {
+      const code = `template <typename T>
+class Box {
+public:
+    T get() const;
+    void set(T v);
+private:
+    T value;
+};
+
+template <typename T> T Box<T>::get() const { return value; }
+template <typename T> void Box<T>::set(T v) { value = v; }
+`;
+      const result = extractFromSource('box.cpp', code);
+      expect(result.errors).toHaveLength(0);
+      const methods = result.nodes.filter((n) => n.kind === 'method');
+      const qns = methods.map((n) => n.qualifiedName).sort();
+      // Out-of-line definitions carry the SAME qualifier as the class node.
+      expect(qns).toContain('Box::get');
+      expect(qns).toContain('Box::set');
+      expect(qns.find((q) => q?.includes('<'))).toBeUndefined();
+      // Names themselves stay clean.
+      expect(methods.map((n) => n.name).sort()).toEqual(expect.arrayContaining(['get', 'set']));
+    });
+
+    it('multi-line template parameter lists cannot leak into qualified_name (NAME_MAX overflow shape)', () => {
+      // The ICU capi_helper.h shape: enormous multi-line parameter names made
+      // qualified_name 272 bytes (> NAME_MAX 255) including embedded newlines.
+      const code = `template <typename CType,
+          typename CPPType,
+          int32_t kMagicValidationSentinelConstantForTheHelperTemplateClassInstanceGuardLong>
+class ApiHelper {
+public:
+    CPPType* validate();
+};
+
+template <typename CType,
+          typename CPPType,
+          int32_t kMagicValidationSentinelConstantForTheHelperTemplateClassInstanceGuardLong>
+CPPType* ApiHelper<CType,
+                   CPPType,
+                   kMagicValidationSentinelConstantForTheHelperTemplateClassInstanceGuardLong>::validate() {
+    return nullptr;
+}
+`;
+      const result = extractFromSource('capi_helper.h', code);
+      const validate = result.nodes.find((n) => n.kind === 'method' && n.name === 'validate' && n.qualifiedName?.includes('::'));
+      expect(validate).toBeDefined();
+      expect(validate!.qualifiedName).toBe('ApiHelper::validate');
+      expect(validate!.qualifiedName!.length).toBeLessThan(255);
+      expect(validate!.qualifiedName).not.toMatch(/[<>\n]/);
+    });
+  });
+
   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

+ 9 - 1
src/extraction/languages/c-cpp.ts

@@ -89,7 +89,15 @@ function extractCppReceiverType(node: SyntaxNode, source: string): string | unde
   const qid = findDeclaratorQualifiedId(declarator);
   if (!qid) return undefined;
   const parts = getNodeText(qid, source).trim().split('::').filter(Boolean);
-  return parts.length > 1 ? parts.slice(0, -1).join('::') : undefined;
+  if (parts.length <= 1) return undefined;
+  // An out-of-line template method definition carries the class's template
+  // parameter list in the qualifier (`template<typename T> T Box<T>::get()`),
+  // but the class node is indexed as bare `Box` — strip `<…>` so the receiver
+  // matches it, the same normalization #1043 applies to base-class refs.
+  // Multi-line parameter lists otherwise leak whole `<…>` blocks (newlines
+  // included) into qualified_name, which can exceed NAME_MAX (#1286).
+  const receiver = stripCppTemplateArgs(parts.slice(0, -1).join('::'));
+  return receiver || undefined;
 }
 
 /**