fix(cpp): resolve calls through singletons/factories/chained getters (#645) (#742)

A C++ method call whose receiver is another call's result — `Foo::instance().bar()`,
`WidgetFactory::create().draw()`, `openSession()->run()`, or the same stored in an
`auto` local first — lost the receiver's type during extraction. The callee degraded
to a bare method name, so when two classes shared a method name the call silently
resolved to whichever was indexed first (or not at all), corrupting callers / impact /
trace with a plausible-but-wrong edge.

Three parts:
- Capture C++ return types (new nodes.return_type column, schema v5): the
  function_definition's `type` field, normalized — smart-pointer pointee unwrapped,
  void/primitives dropped.
- Preserve the inner-call receiver in extraction: a C/C++ field_expression whose
  receiver is itself a call is encoded `inner().method` instead of dropping to the
  bare name. Other languages keep the existing behavior.
- New resolution strategy (matchCppCallChain): infer the receiver's class from the
  inner call's return type, then resolve AND validate the method on it. Handles
  singletons/accessors, factories returning a different type, free-function
  factories, make_unique/make_shared/new/direct construction, single-level member
  chains, and namespace-qualified inner calls. A wrong inference yields no edge,
  never a wrong one.

EXTRACTION_VERSION 2->3 (re-index to populate return types).

Validated on the issue repro + spdlog: node count stable (no explosion),
deterministic, and ~100 pre-existing wrong `.size()`-style edges removed.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Colby Mchenry
2026-06-08 20:18:17 -04:00
committed by GitHub
co-authored by Claude Opus 4.8
parent a56d9e6941
commit fd03f31b2c
14 changed files with 421 additions and 8 deletions
+9
View File
@@ -163,6 +163,15 @@ export interface Node {
/** Generic type parameters */
typeParameters?: string[];
/**
* Normalized return/result type name for a function/method (the bare class
* name, smart-pointer pointee unwrapped). Captured for C/C++ so resolution
* can infer a chained receiver's type from what the inner call returns —
* `Foo::instance().bar()` resolves `bar` on `Foo` (issue #645). Undefined for
* languages/symbols where it isn't captured.
*/
returnType?: string;
/** When the node was last updated */
updatedAt: number;
}