Inside a class body a `virtual double area() = 0;` (pure virtual), a
`virtual void draw();` and a plain prototype `int run();` parse as a
`field_declaration` whose declarator is a `function_declarator`. The field
branch recognised that shape only to skip emitting a type reference — it still
emitted a node for every declarator as a DATA MEMBER (a bare `area` label with
a `defines`/field edge).
So a pure-virtual interface class had no method nodes, only phantom fields; a
header-declared method and its out-of-line/overriding definition did not share
one node (`area` field vs `.area()` method); and a call to such a method
resolved to a field-shaped node.
Fix: in the field-declaration branch, emit a function_declarator with the same
shape a defined method uses — a `.name()` label, a `method` edge, and callable
— so the declaration and its definition collapse to one method node (parity
with the Java/TypeScript/Scala abstract-method contract). A genuine data member
still gets its `defines` field edge.
This corrects the label of every header-declared method, so a few existing
cross-file tests that asserted the old bare `bar`/`run` field label now assert
`.bar()`/`.run()`; the resolved call edges (source, target, confidence) are
unchanged, and the decl/def merge still produces exactly one node carrying the
definition site.
Added tests/test_cpp_method_declarations.py.
(cherry picked from commit 0038af9394)
Two different classes that both fail header parsing and happen to
share a class name and a method name must not let a call resolve to
either one.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
(cherry picked from commit 9e26952d8b)
The issue's own repro, an Unreal style UCLASS whose method
declaration never survives header parsing, must still get a calls
edge from a site that invokes it through the qualified name.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
(cherry picked from commit 3d4dec6b9a)
Connects paired classes across files: Main.cpp's `Foo f; f.bar()` now resolves
to Foo::bar, and ObjC `Foo *f = [[Foo alloc] init]; [f doThing]` to Foo's
doThing — the "connect with other classes" goal of #1547/#1556.
Design grounded in prior-art research (ctags qualified-name matching, Doxygen's
name-keyed false-edge failure modes, PAIGE's receiver-type approach, Clang USR):
resolve by RECEIVER TYPE, never bare name, and skip when the type can't be
inferred rather than guess (a false call edge / god-node is worse than a missing
one). Mirrors the existing Swift/Python/Ruby/TS member-call resolvers.
- C++ extractor now captures the member-call receiver (field_expression /
qualified_identifier / pointer access) and builds a per-file type table from
local declarations (`Foo f;`, `Foo* f;`, `Foo *f = ...;`); emits raw_calls.
- ObjC extractor emits raw_calls for message sends with the receiver + selector
and a type table from `Foo *f = ...;` locals (existing in-file selector /
alloc-init / dot-syntax / @selector matching preserved).
- New _resolve_cpp_member_calls / _resolve_objc_member_calls, registered for
their suffixes. Receiver tiers: `Foo::bar()` / capitalized ObjC receiver and
this/self/super (enclosing class) -> EXTRACTED; local-var-typed -> INFERRED.
Single-definition god-node guard (skip unless exactly one type def matches);
the just-shipped decl/def class merge makes a paired class one def so the
guard resolves it. Verified: a.run() -> A::run only (not a same-named B::run);
an uninferable receiver with run() in two classes emits zero edges (no
fan-out); ObjC [f doThing] -> Foo only.
- build.py: the cross-language INFERRED-call prune treated .h/.cpp/.m as
different families and dropped header/impl interop calls; unified the C family
(.c .h .cc .cpp .hpp .cxx .hh .hxx .cu .cuh .metal .m .mm) so a .cpp/.m call to
a .h-declared method survives.
Still open (tracked on #1547/#1556): the file-level `#include` edge can stay
uncanonicalized when the project root isn't symlink-resolved (the extract()
id-remap `continue`s on a /var-vs-/private/var mismatch) — the class connection
above is robust to it; include-reachability candidate narrowing and ObjC
dynamic-dispatch/id-typed receivers also deferred (expected low ObjC recall, per
the research).
Reported by @c0dezer019 (#1547) and @JabberYQ (#1556).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>