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>
This commit is contained in:
co-authored by
Claude Fable 5
parent
e1f339f732
commit
4dd29ea5c1
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user