Polyglot code analysis in Rust — parses Python, TypeScript, Go & Rust into a shared graph IR. The type checkers (ty, typescript-go) are linked in, so there are no language servers to install. Call/dependency graphs, cross-language HTTP & gRPC boundaries.
翻译准备中
取自仓库 README
正在加载完整 README。
查看完整 README ↗Documentation · Repository · Issues
Repository → Language Adapter → Graph (IR) → your code
Nodes and directed relations, typed and deterministic: functions, classes, modules, the calls between them, and the HTTP or gRPC boundaries where one service meets another.
Code analysis usually asks you to assemble it — install a language server, put
it on PATH, wire up the transport, keep the versions in step. callix links
the analysis engines into the module instead:
| Language | Symbol resolution | Extra setup |
|---|---|---|
| Python | ty, linked as a library | none |
| TypeScript | typescript-go, linked as a Go c-archive | none |
| Go | go/packages + go/types, same c-archive | a Go toolchain |
| Rust | rust-analyzer scip index, decoded natively | rust-analyzer and Cargo |
| PHP | a symbol table built from the sources | none |
| C / C++ | a symbol table over the parsed sources | none |
| YAML | none — it declares no symbols | none |
Python and TypeScript need nothing beyond the wheel — the checkers and their
standard-library stubs are inside it. Go and Rust use the toolchain the project
already has, because a standard library shipped as sources cannot be frozen
into a wheel. PHP and the C family have no checker worth linking and no widely
installed indexer worth shelling out to — every precise C/C++ option requires a
compile_commands.json, which almost no repository ships — so callix builds a
symbol table from the sources and reports degraded, honest about being a symbol
table and not a type checker.
pip install callix
Python ≥ 3.10. Wheels for Linux glibc and musl (x86_64, aarch64) and macOS (arm64, x86_64); Windows is not supported.
from callix import NodeKind, PythonAdapter
graph = PythonAdapter().analyze("path/to/project")
print(len(graph.nodes), len(graph.relations))
for node in graph.nodes_by_kind(NodeKind.FUNCTION):
print(node.qualified_name, graph.callers(node.id))
graph.to_json(indent=2) # serialization
old.diff(new) # structural diff
The same call works for every other language — TypeScriptAdapter, GoAdapter,
RustAdapter, PhpAdapter, CAdapter, CppAdapter, YamlAdapter — and each
discovers its own project roots, so a monorepo works without configuration.
Symbol resolution is on by default. resolve=False gives a structural graph.
graph.metadata["resolver_status"] says whether the backend came up (ok), is a
table of names rather than a type checker (degraded), or did not run
(unavailable) — not how many calls it found. Each edge records how it was
found in metadata["resolution"] (definition, value, flow, external,
unlocated, name), callix report -t status counts them, and
how accurate resolution is
says what each language follows and what it does not, with measured numbers.
A Python server declaring @app.get("/users/{id}") and a TypeScript client
calling fetch("/users/1") produce the same boundary node, so merging
their graphs links the two services. See
Cross-language analysis.
The same analysis without writing Python — and the formats other tools read:
docker run --rm -v "$PWD:/workspace" ghcr.io/callix-tools/callix:full \
export . -f lsif -o dump.lsif # GitLab code navigation, Sourcegraph-style tools
callix export also writes SCIP, CycloneDX, CSV, Graphviz and Mermaid; callix report
prints summaries, resolver status, dependencies, boundaries and call lists. The
full image carries the Go and Rust toolchains, so every language that has a
real resolver gets it. See
Command line,
Export formats and
Docker.
https://callix-tools.github.io/callix/
Last run: 2026-07-30 12:01 UTC · callix main · runner Linux x86_64 · single cold run, indicative only.
| Project | Lang | Commit | LOC | Files | Nodes | Relations | Time | Peak RSS | KLOC/s | Resolver | Answered |
|---|---|---|---|---|---|---|---|---|---|---|---|
| apache/superset | python | c83fb2b | 399 519 | 1 886 | 181 320 | 370 764 | 19.4s | 1,026 MB | 20.6 | ok | 85% of 281 667 (11s) |
| colinhacks/zod | typescript | 1fb56a5 | 74 194 | 404 | 15 332 | 27 334 | 2.5s | 419 MB | 30.2 | ok | 89% of 17 067 (1s) |
| gin-gonic/gin | go | 73726dc | 23 374 | 97 | 13 109 | 19 424 | 6.1s | 948 MB | 3.8 | ok | 100% of 13 916 (1s) |
| casdoor/casdoor | go | 696bcf0 | 86 898 | 458 | 33 324 | 58 107 | 35.7s | 4,240 MB | 2.4 | ok | 100% of 36 633 (10s) |
Peak RSS measured via getrusage.max (largest single process — ours or a resolver subprocess; no cgroup counter was readable, so this is not the tree total). KLOC/s = analysed thousands-of-lines per second. Answered = the share of use-sites the backend returned a place for - not a measure of correctness; see how accurate resolution is in the docs. Generated by benchmarks/run_benchmarks.py.
Speed-ups against graphlens, the Python implementation this replaces: 5.2× on apache/superset, 3.3× on colinhacks/zod, 2.6× on gin-gonic/gin. Details and method in Benchmarks.
Semver covers the graph contract — the node and relation kinds, the two id
formulas, 1-based spans, node ordering, the serialization format — and the
Python API as generated into the committed .pyi. It does not cover what the
resolvers answer: the Python one is a pinned 0.0.x release of ty, the Rust
one is whatever rust-analyzer the machine has. A patch release may change
which CALLS edges appear. What is watched instead is what the resolvers
get right on the accuracy suites (tests/accuracy, a floor CI enforces) and the
aggregate share of queries the backend answered, recorded per project in the
baselines.
Full statement, including which changes need a major release: Versioning.
See CONTRIBUTING.md for the development loop, the build constraints, and how releases are cut.
MIT
| gohugoio/hugo | go | 4d22555 | 224 821 | 897 | 75 036 | 130 508 | 22.0s | 4,559 MB | 10.2 | ok | 98% of 87 043 (6s) |
| BurntSushi/ripgrep | rust | 4649aa9 | 50 275 | 98 | 7 787 | 22 852 | 12.8s | 1,077 MB | 3.9 | ok | 99% of 16 156 (0s) |
| tokio-rs/axum | rust | c59208c | 43 653 | 296 | 10 627 | 21 118 | 69.0s | 3,123 MB | 0.6 | ok | 87% of 12 625 (0s) |
| astral-sh/ruff | rust | 6686f63 | 687 409 | 1 870 | 102 595 | 330 597 | 176.0s | 6,007 MB | 3.9 | ok | 100% of 216 936 (2s) |
| laravel/framework | php | 92a7072 | 540 800 | 3 016 | 83 164 | 291 129 | 17.8s | 376 MB | 30.4 | degraded | 70% of 283 108 (1s) |
| redis/redis | c | 5279a8d | 374 211 | 786 | 156 896 | 200 372 | 7.0s | 294 MB | 53.8 | degraded | 100% of 161 477 (0s) |
| google/leveldb | cpp | 99b3c03 | 28 280 | 131 | 12 235 | 20 600 | 0.9s | 77 MB | 32.5 | degraded | 100% of 14 322 (0s) |
| facebook/rocksdb | cpp | 3b44608 | 774 485 | 1 342 | 300 996 | 513 211 | 26.9s | 718 MB | 28.8 | degraded | 100% of 433 814 (0s) |
| APIs-guru/openapi-directory | yaml | 959d727 | 1 268 530 | 971 | 11 812 | 16 795 | 5.8s | 115 MB | 219.8 | ok | — |
| argoproj/argo-cd | yaml | 564b949 | 322 262 | 2 201 | 2 220 | 2 246 | 1.4s | 91 MB | 222.5 | ok | — |
| Total | 4 898 711 | 1 006 453 | 403.3s | 12.1 | 91% of 1 574 764 |