Skip to content

graph: add import resolvers for C/C++, PHP, C#, Dart, Vue/Svelte/Astro, Lua, GDScript and Swift (graph reads 8 language families today) #980

Open

Description

@manojmallick

Problem / Goal

The import graph resolves 8 language families (JS/TS, Python, Go, Rust, JVM, Ruby, Elixir, R) of the 38 SigMap extracts, so file-level impact, plan's blast radius and the graph boost are blind for the rest (measured in the guard issue: 0 edges on PHP, C#, Dart, Swift, Lua, GDScript and C++ clones). codebase-memory-mcp and code-review-graph both resolve these (Tree-sitter, per their READMEs). This is the largest single contributor to the "code graph" gap that does not need Tree-sitter: most of these import forms are explicit, regular and cheap to resolve deterministically.

Proposed solution

One resolver per language family inside src/graph/builder.js's existing pattern (extractFileDeps plus a per-build lookup table), zero dependencies, in value order:

  1. C / C++ / Objective-C: #include "x.h" / #import "x.h", resolved relative to the file, then the include roots. Angle-bracket system headers ignored.
  2. PHP: use Ns\Class; plus require / include with literal paths, resolved through composer.json autoload.psr-4.
  3. C#: using Ns; resolved through an index of namespace X declarations built in the same pass.
  4. Dart: import 'package:pkg/x.dart' (local package) and relative imports.
  5. Vue / Svelte / Astro single-file components: parse the <script> / frontmatter block and reuse the JS/TS resolver. These file types are not in JS_EXTS today, so a .vue file contributes no edges even though its script imports are ordinary ES imports.
  6. Lua require("a.b") to a/b.lua; GDScript preload("res://x.gd") / load(...).
  7. Swift: module-level only (a file depends on the target that declares an imported module); no file-level edges are invented.

For each: precision first. An unresolvable import produces no edge (as the JS resolver does for bare package names), never a guessed one.

Measure with the references already planned, not by eye: SCIP-derived edges (#976) where an indexer exists, plus a git co-change reference for file-level impact (files changed together in a commit, independent of the graph), on the clones listed above.

Impact

Moves the graph from 8 to roughly 15 language families, makes --impact, plan and get_impact meaningful on PHP, C#, C/C++, Dart and the frontend frameworks, and gives the ranker's graph boost something to work with on exactly the repos where retrieval is weakest (laravel 40%, serilog 40%, riverpod 60%). Whether the boost helps retrieval there is an open question: #935 found no damping rule worth shipping on the current graphs, so retrieval is a measured side effect, not a promise.

Acceptance criteria

  • A resolver and fixtures per language above (fixtures include re-exports, aliases, conditional includes, and unresolvable imports that must yield no edge).
  • Edge precision and recall against the chosen references published per language (shares the harness of benchmark: call-graph accuracy against a compiler-derived reference (SCIP) - no one publishes a non-circular number #976).
  • The Laravel Str.php case reports its importers (about 164).
  • xrepo per-repo no-regress holds; any movement on laravel / serilog / riverpod is reported unedited, win or lose.
  • KNOWN_LIMITATIONS.md graph language list updated.

Related: #976, #935, #934, the graph guard issue (do that first).


Pickup (epic #978)

Activity

  1. manojmallick commented on Oct 26, 2026

    @manojmallick
    OwnerAuthor

    Evidence for #980, from PR #1030 (not merged, not released).

    Command

    node scripts/run-import-accuracy.mjs --setup-references <dir>
    node scripts/run-import-accuracy.mjs --references <dir> --save      # benchmarks/reports/import-accuracy.json
    node scripts/run-import-accuracy.mjs --write-doc                    # docs-vp/guide/import-accuracy.md
    

    Nine pinned repositories (the commits the xrepo manifest pins), each exported with git archive into a scratch directory; the shared clones are only read. Protocol and eight predictions committed in 975d281, before the scorer ran on any evaluation repository; --save refuses a partial run, an edited protocol and a dirty scored tree.

    Before

    The import graph read eight families. For a PHP, C/C++, C#, Dart, Vue, Svelte, Astro, Lua or GDScript file it drew no edge (--impact said graph: unsupported-language, #979), and for Swift likewise. No edge, so nothing to measure, and no accuracy figure for any language.

    After (registered run, scorer at 7287d5a, protocol sha256 e259922692bf…)

    Group (reference) Precision Recall Graph / reference edges
    C / C++ / Objective-C (clang -E -H, 3 configurations): libuv, abseil-cpp 99.7% 99.7% 4,609 / 4,611
    PHP (tokenizer + Composer's ClassLoader): laravel, composer 100.0% 100.0% 4,976 / 4,975
    Swift, module level (swiftc + SwiftPM): alamofire, vapor 100.0% 99.3% 150 / 151
    Vue / Svelte / Astro (framework parser + ts.resolveModuleName): vue-core, svelte, astro 100.0% 79.1% 1,535 / 1,941

    Registered predictions: P1, P2, P3, P5, P6, P7, P8 held; P4 (component recall ≥ 90%) missed at 79.1%. 390 of the 406 component misses are in svelte (389: files inside the svelte package importing "svelte", resolved by TypeScript through the package's own exports); the rest are ./x.js written for x.ts (12), JSON modules (3), a #name import (1) and one pair of files whose names differ only in case (the run was on a case-insensitive filesystem). All live in the resolver every .js/.ts file shares; not changed here (it also builds the ranker's graph, #934).

    C#, Dart, Lua, GDScript: resolvers and fixtures with known answers, no figure (no reference could be run; reasons in the protocol).

    Other acceptance criteria:

    • Laravel Str.php: 167 direct importers, every PHP source file that names Illuminate\Support\Str; the four other grep hits are Blade templates.
    • xrepo no-regress: 116/208 (55.8%), won 0 / lost 0, identical to the baseline. Report only, ranker handed the extended graph: 114/208, lost laravel x049 and newtonsoft-json x109 (p = 0.5); serilog and riverpod do not move.
    • Retrieval gate: hard 63/90 = 70.0% (the floor, 0 tasks of headroom), mined 68.3%, easy 90.0%; no hard task changes between hit and miss against develop.
    • KNOWN_LIMITATIONS.md: graph language list (fifteen families), what each resolver counts, the measured figures, the gaps.
    • Not done: the git co-change reference.

    Deviation log (in the protocol): three runs. The second reproduced the first exactly and added a why-was-it-missed breakdown; reading the misses showed two defects in the new component reader (a blank line before an Astro --- fence; a recursive component importing itself), fixed in 91aec34; the third run is the saved report. C, PHP, Swift are bit-for-bit the same across all three.

    Environment: macOS (Darwin 25.5.0) arm64, load average about 4 on a shared machine; Node v25.6.0; Apple clang 21.0.0; Apple Swift 6.3.2; PHP 8.4.23 and Composer 2.10.3 (sha256-checked); typescript 6.0.3, @vue/compiler-sfc 3.5.44, svelte 5.57.2, @astrojs/compiler 4.0.0.

    Report: benchmarks/reports/import-accuracy.json; method benchmarks/IMPORT_RESOLVER_PROTOCOL.md; page docs-vp/guide/import-accuracy.md (tables generated from the report and pinned to it by a test).

    Regression tests: test/integration/import-resolvers.test.js (29; 50/50 mutants killed), test/integration/import-accuracy.test.js (22 with the reference tools; 32/32 mutants killed), test/integration/graph-unsupported-languages.test.js (35). Full integration suite 261 passed, 0 failed.

    Found, not fixed (needs your call): the JS/TS import matcher is line-based, so an import list that spans several lines (import {\n a,\n b\n} from './x') gives no edge in a plain .ts/.js file. Components are read; plain JS/TS is not, because the ranker's graph is built from the same matcher.

  2. added a commit that references this issue on Oct 26, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    P2Planned, not urgentenhancementNew feature or request

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions