Skip to content

feat(callgraph): C call graph builder#674

Open
shivasurya wants to merge 1 commit intoshiva/cpp-type-inferencefrom
shiva/cpp-c-call-graph
Open

feat(callgraph): C call graph builder#674
shivasurya wants to merge 1 commit intoshiva/cpp-type-inferencefrom
shiva/cpp-c-call-graph

Conversation

@shivasurya
Copy link
Copy Markdown
Owner

Summary

Adds BuildCCallGraph — a four-pass algorithm that produces a *core.CallGraph for C projects, ready to merge into the unified graph alongside Python/Go.

Pass Purpose
1 Index every C function_definition under \"<relpath>::<name>\" and ensure the FQN is also in registry.FunctionIndex
2 Register explicit return types (skipping void) and emit ParameterSymbol entries for every named parameter
3 Walk parser-emitted edges (function_definition → call_expression) to extract one CallSiteInternal per call — no second AST traversal
4 Resolve targets in a definition-preferring order, then emit edges and CallSite records

Resolution order (Pass 4)

  1. Same-file definition — common case (helper in same .c); deterministic and independent of include state.
  2. Global definition — scan registry.FunctionIndex[name] for an FQN whose call-graph entry is a definition. Handles cross-.c calls.
  3. Same-file declaration — accept a forward declaration when no definition exists project-wide.
  4. Declaration reachable through #include — last resort so externs handed off to another translation unit still surface as edges.

Calls that don't match any source produce a CallSite{Resolved: false, FailureReason: \"external_or_unresolved\"} — stdlib calls (printf, malloc) and unknown function pointers remain visible to rule writers without polluting the edge set.

Design notes

  • Edges from the parser: every parseCCallExpression adds an edge from the enclosing function to the call node. The builder walks OutgoingEdges of each indexed function instead of doing byte-range containment, keeping Pass 3 deterministic and trivially testable.
  • Definition vs declaration: the parser sets Metadata[\"is_declaration\"]=true on prototype/extern decls. isDeclaration() reads that key with a typed assertion so non-declaration nodes (no metadata) fall through correctly.
  • Recursion: self-edges (process → process) are emitted as-is; the call graph already deduplicates via AddEdge.
  • Static functions: same FQN-by-file mechanism — file-scope statics in different .c files map to disjoint FQNs.
  • Unique FunctionIndex entries: Pass 1 dedupes against the registry's existing FunctionIndex so calling BuildCCallGraph after BuildCModuleRegistry is idempotent.

Test plan

  • go build ./...
  • go test ./... — full suite green
  • go vet ./...
  • golangci-lint run ./graph/callgraph/builder/ — 0 issues
  • Coverage on c_builder.go lines: ~89.6%
  • Spec scenarios covered:
    • Single-file main()add() edge
    • Cross-file .c definition preferred over .h declaration
    • Header declaration via #include fallback
    • printf (stdlib) recorded as Resolved:false with failure reason
    • Recursive self-call
    • Same name in two .c files (file-scope statics)
    • Type engine populated; void returns dropped
    • Parameters indexed (anonymous params skipped)
    • Declarations skipped from Pass 2 type extraction
    • Merges cleanly into an empty unified graph
    • Non-C nodes ignored (mixed-language safety)
    • Anonymous / missing-file functions filtered
    • Empty-target call_expression produces no edge and no recorded site
    • Cross-.c global lookup works without an #include
    • Same-file forward declaration accepted when no definition exists

Stacked on

shiva/cpp-type-inference (#673)

Add BuildCCallGraph — a four-pass algorithm that produces a
*core.CallGraph for C projects:

  Pass 1  Index every C function_definition under "<relpath>::<name>"
          and ensure the FQN appears in the module registry's
          FunctionIndex for cross-file resolution.
  Pass 2  Register explicit return types with the type engine
          (skipping void) and emit ParameterSymbol entries for every
          named parameter.
  Pass 3  Walk the parser-emitted edges (function_definition →
          call_expression) to extract one CallSiteInternal per call,
          deterministically and without a second AST traversal.
  Pass 4  Resolve targets in a definition-preferring order:
          same-file definition → global definition → same-file
          declaration → declaration reachable through #include "...".
          Resolved sites add an edge; unresolved sites are recorded
          as CallSite{Resolved:false} so external/stdlib calls remain
          visible to rule writers.

The result merges cleanly into a unified graph via the existing
MergeCallGraphs since C FQNs ("src/main.c::main") share no namespace
with Python, Go, or Java.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
@shivasurya shivasurya added enhancement New feature or request go Pull requests that update go code labels May 2, 2026
@shivasurya shivasurya self-assigned this May 2, 2026
@safedep
Copy link
Copy Markdown

safedep Bot commented May 2, 2026

SafeDep Report Summary

Green Malicious Packages Badge Green Vulnerable Packages Badge Green Risky License Badge

No dependency changes detected. Nothing to scan.

View complete scan results →

This report is generated by SafeDep Github App

@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 2, 2026

Code Pathfinder Security Scan

Pass Critical High Medium Low Info

No security issues detected.

Metric Value
Files Scanned 2
Rules 205

Powered by Code Pathfinder

@codecov
Copy link
Copy Markdown

codecov Bot commented May 2, 2026

Codecov Report

❌ Patch coverage is 88.35616% with 17 lines in your changes missing coverage. Please review.
✅ Project coverage is 85.30%. Comparing base (47d63d3) to head (3055d72).

Files with missing lines Patch % Lines
sast-engine/graph/callgraph/builder/c_builder.go 88.35% 11 Missing and 6 partials ⚠️
Additional details and impacted files
@@                     Coverage Diff                      @@
##           shiva/cpp-type-inference     #674      +/-   ##
============================================================
+ Coverage                     85.28%   85.30%   +0.01%     
============================================================
  Files                           182      183       +1     
  Lines                         26399    26545     +146     
============================================================
+ Hits                          22515    22644     +129     
- Misses                         3023     3034      +11     
- Partials                        861      867       +6     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request go Pull requests that update go code

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant