* fix(cpp): resolve explicit operator calls (a.operator+(b)) to the operator method (#1247) tree-sitter-cpp can't parse an operator_name in field position: the call_expression carries `function: <receiver>` plus an ERROR child wrapping the operator_name instead of a field_expression callee, so the extractor emitted a calls ref named just the receiver (`a`) and the edge never resolved — while the operator method itself indexed fine. Two-part fix, scoped to the explicit call form (infix `a + b` / `a[i]` need receiver type inference and are tracked in #1258): - extraction: recover the operator_name from the ERROR child and emit `<receiver>.operator+` (`->` receivers normalized, `this->` emits the bare name), like any other member call - resolution: matchMethodCall's dot pattern now admits an operator method part (cpp-gated; symbol chars failed the \w match), so receiver-type inference + resolveMethodOnType validate the target — a same-named operator on an unrelated class can't capture the edge Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(cpp): harden explicit operator-call recovery against real-world shapes (#1247) Validated on nlohmann/json (dozens of explicit operator[] / operator* / operator< call sites). Two refinements the synthetic fixtures missed: - normalize spaced call-site operator names (`it.operator * ()`, `other.operator < (*this)`) to the compact form definitions index as - drop the ref for a complex receiver (`obj()->operator+`, member chains ending in a call) instead of emitting a bare operator name: exact-name fallback GUESSED among unrelated same-named operators (linked a std::map operator[] call to an in-repo operator[]) — silent miss, never a wrong edge Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
44 lines
1.5 KiB
TypeScript
44 lines
1.5 KiB
TypeScript
import { describe, it, beforeAll } from 'vitest';
|
|
import { extractFromSource } from '../src/extraction';
|
|
import { initGrammars, loadAllGrammars, getParser } from '../src/extraction/grammars';
|
|
|
|
beforeAll(async () => {
|
|
await initGrammars();
|
|
await loadAllGrammars();
|
|
});
|
|
|
|
const CODE = `struct V {
|
|
int x;
|
|
V operator+(const V& o) const { return V{x + o.x}; }
|
|
V operator[](int i) const { return V{x + i}; }
|
|
int get() const { return x; }
|
|
};
|
|
|
|
int plainCaller(const V& a) { return a.get(); }
|
|
V explicitCaller(const V& a, const V& b) { return a.operator+(b); }
|
|
`;
|
|
|
|
describe('scratch', () => {
|
|
it('dumps', async () => {
|
|
const parser: any = getParser('cpp' as any);
|
|
const tree = parser.parse(CODE);
|
|
const dump = (n: any, d = 0) => {
|
|
let out = `${' '.repeat(d)}${n.type} [${JSON.stringify(n.text.slice(0, 40))}]\n`;
|
|
for (let i = 0; i < n.childCount; i++) {
|
|
const c = n.child(i);
|
|
const f = n.fieldNameForChild ? n.fieldNameForChild(i) : null;
|
|
out += `${' '.repeat(d + 1)}${f ? f + ': ' : ''}`.trimEnd() ? '' : '';
|
|
out += dump(c, d + 1);
|
|
}
|
|
return out;
|
|
};
|
|
// just dump the explicitCaller subtree
|
|
console.log(dump(tree.rootNode));
|
|
|
|
const result = extractFromSource('optest.cpp', CODE);
|
|
console.log('NODES', result.nodes.map((n: any) => `${n.kind}:${n.name}`));
|
|
console.log('KEYS', Object.keys(result));
|
|
console.log('UREFS', JSON.stringify((result as any).unresolvedReferences, null, 1));
|
|
});
|
|
});
|