Add cross-file call linker for C/C++/Go directory parsing#34
Merged
tob-scott-a merged 1 commit intotrailofbits:mainfrom Apr 29, 2026
Merged
Conversation
…parse Per-file parsers resolve bare calls like `foo()` as `current_module:foo`. After merging files, if `foo` is defined in another module that edge target doesn't exist and is silently dropped by the graph index. This adds a post-merge linker pass in `parse_directory` that rewrites dangling CALLS edges to point at the actual definition site by matching on function name. Tested on libavif (24K lines of C): resolved edges increased from 1,099 to 2,780 and decode-path reachability expanded from 125 functions in 1 module to 240 functions across 15 modules. Closes trailofbits#10 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
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
_link_cross_file_calls()pass inparse_directory()that rewrites danglingCALLSedges to point at definitions in other modulesfoo()ascurrent_module:foo— after merging, iffoois defined in another module, that edge target doesn't exist and gets silently dropped byGraphStore._build_indexHow it works
CALLSedges whosetarget_iddoesn't exist in the node set:separator)::,->,.in the bare name) — these are method calls that shouldn't be rewrittenCERTAINconfidenceUNCERTAINconfidenceImpact
Tested on libavif v1.4.1 (24K lines of C, 818 nodes, 6,339 call edges):
Previously,
paths_between("avifDecoderRead", "avifImageRGBToYUV")returned nothing because the call crosses fromread.ctoreformat.c. After this fix, the full decode→reformat→stream call chain resolves.Test plan
test_common_parser.pycovering unique resolution, same-module preservation, and unresolvable callsCloses #10
🤖 Generated with Claude Code