feat(callgraph): C++ call graph builder#675
Merged
shivasurya merged 1 commit intomainfrom May 3, 2026
Merged
Conversation
SafeDep Report SummaryNo dependency changes detected. Nothing to scan. This report is generated by SafeDep Github App |
Code Pathfinder Security ScanNo security issues detected.
Powered by Code Pathfinder |
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #675 +/- ##
==========================================
+ Coverage 85.30% 85.33% +0.02%
==========================================
Files 183 184 +1
Lines 26545 26751 +206
==========================================
+ Hits 22644 22827 +183
- Misses 3034 3045 +11
- Partials 867 879 +12 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
This was referenced May 3, 2026
Owner
Author
This was referenced May 3, 2026
Owner
Author
Merge activity
|
Add BuildCppCallGraph — same four-pass structure as the C builder
plus three C++-specific resolution paths:
1. Namespace-/scope-qualified calls (`ns::func`,
`Class::staticMethod`) resolve directly through
registry.NamespaceIndex.
2. Method calls on typed receivers (`obj.method()` /
`obj->method()`) look up the receiver's declared type via the
type engine and then locate the method on that class. The
receiver type is normalised: pointer/reference qualifiers,
`const`, `volatile`, and trailing whitespace are stripped before
class lookup so `Dog*`, `const Dog&`, and `Dog **` all reduce to
`Dog`.
3. `this->method()` derives the receiver type from the caller's
enclosing class via byte-range containment, so calls inside
method bodies resolve without a separate `this` symbol in the
scope.
Plain free-function calls fall through to resolveCCallTarget so the
C++ builder is a strict superset of the C one. Pass 2 also registers
class method return types and class field types on the type engine
to support future receiver-typed resolution chains.
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
08fdce6 to
2aef962
Compare
shivasurya
added a commit
that referenced
this pull request
May 3, 2026
## Summary
Adds `ExtractCStatements` and `ExtractCppStatements` — they walk a parsed function body and emit one `*core.Statement` per recognised construct (declaration, assignment, call, return, if/for/while/do/switch, plus throw / try / range-for in C++). The result feeds the CFG builder (PR-10) and the future variable-dependency graph.
### Statement shapes captured
| Construct | Statement |
|---|---|
| `int x = a + b;` | `{Type: assignment, Def: x, Uses: [a, b]}` |
| `x = func(y);` | `{Type: assignment, Def: x, Uses: [y], CallTarget: func}` |
| `func(a, b);` | `{Type: call, CallTarget: func, Uses: [a, b]}` |
| `return x;` | `{Type: return, Uses: [x]}` |
| `if (x>0) { ... } else { ... }` | `{Type: if, Uses: [x], NestedStatements, ElseBranch}` |
| `for (int i=0; i<n; i++)` | `{Type: for, Def: i, Uses: [n]}` |
| `while (cond) { ... }` | `{Type: while, Uses: [cond]}` |
| `p->name = val;` | `{Type: assignment, Def: p, Uses: [val]}` |
| `buf[i] = input[j];` | `{Type: assignment, Def: buf, Uses: [input, i, j]}` |
| `obj.method(x);` | `{Type: call, CallTarget: method, CallChain: obj.method, Uses: [obj, x]}` |
| `ns::sort(begin, end);` | `{Type: call, CallTarget: ns::sort, Uses: [begin, end]}` |
| `auto x = obj.get();` | `{Type: assignment, Def: x, Uses: [obj], CallTarget: get}` |
| `throw std::runtime_error(msg);` | `{Type: raise, CallTarget: std::runtime_error}` |
| `for (auto x : items)` | `{Type: for, Def: x, Uses: [items]}` |
### Design notes
- **One shared dispatcher**: `clikeExtractor` in `statements_clike.go` owns every node-type handler. C and C++ are thin wrappers that bind a keyword predicate (`clike.IsCKeyword` / `clike.IsCppKeyword`) and an `extraNodeHandler` hook for C++-only nodes. Adding the next clike construct touches one file.
- **LHS collapsing**: `buf[i] = ...`, `p->name = ...`, `(*p) = ...` all collapse to the base variable for `Def`; index/field expressions surface as additional `Uses`. Matches the def-use convention used by the Go and Python extractors.
- **Loop-variable hygiene**: the C-style and range-based `for` handlers add the defined loop variable to `Def` and remove it from `Uses` last, so the variable appears once even though it shows up in init / cond / update.
- **Identifier walker** filters `field_identifier` (the method name in `obj.method`), `type_identifier`, `qualified_identifier` (treated atomically as a call target rather than as variable uses), and every literal type — keeping `Uses` to true variable references.
- **Catch clauses** are flattened: the caught variable becomes the `Def` of an empty assignment statement so def-use sees the binding site, then the body's statements follow in `ElseBranch`.
## Test plan
- [x] `go build ./...`
- [x] `go test ./...` — full suite green
- [x] `go vet ./...`
- [x] `golangci-lint run ./graph/callgraph/extraction/` — 0 issues
- [x] Coverage on new lines: 88.2%
- [x] Spec C scenarios covered: assignment with binary op, assignment from call, bare call, if/else, for loop (with loop-var dropped from Uses), while, do-while, switch, pointer-arrow assignment, subscript assignment, keyword filter (`sizeof`, `(int)`, `NULL`), bare declaration, forward declaration (nil function).
- [x] Spec C++ scenarios covered: method call on object, qualified call, auto-from-method-call, throw constructor, try/catch (with bound exception name as Def), range-based for, C++ keyword filter (`nullptr`, `static_cast`, `delete`, `this`, `auto`), C-style fallthrough, namespace assignment.
## Stacked on
`shiva/cpp-cpp-call-graph` (#675)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.




Summary
Adds
BuildCppCallGraph— the four-pass C builder plus three C++-specific resolution paths exercised in Pass 4. Plain free-function calls fall through toresolveCCallTarget, making the C++ builder a strict superset of the C one.ns::func,Class::staticMethod) — directNamespaceIndexlookupthis->method()— caller's enclosing class derived via byte-range containmentReceiver type normalisation
obj.method()calls drive resolution through the receiver's declared type. Before the lookup, the type string is normalised:const,volatile, and pointer/reference suffixes (*,**,&,&&) are stripped soDog*,const Dog&, andDog **all reduce toDog.Class member tracking (Pass 2)
field_declarationnodes for future field-chain resolution (obj.field.method()).Method-to-class association
Same byte-range containment used in PR-05 (
BuildCppModuleRegistry): for each method, find the smallest class declaration whose[StartByte, EndByte)range contains the method's start byte. Nested classes (class Outer { class Inner { ... }; };) resolve to the innermost match. The builder doesn't depend on parser-internal context tracking, so it stays composable across future parser refactors.Test plan
go build ./...go test ./...— full suite greengo vet ./...golangci-lint run ./graph/callgraph/builder/— 0 issuescpp_builder.golines: ~93%mylib::process)Socket::create)dog.speak()wheredog: Dog*)this->method()inside a method bodyprintf(stdlib) → unresolved with failure reasonFoo→ns::Foo::bar)this->method()on a non-method caller falls through cleanlyStacked on
shiva/cpp-c-call-graph(#674)