Contributing to SAF
March 16, 2026 · View on GitHub
Thanks for your interest in contributing to SAF! This guide covers the development workflow, coding conventions, and how to submit changes.
Development Environment
All builds and tests run inside Docker — you don't need LLVM or Rust installed locally.
make shell # Open interactive dev shell
make test # Run all tests (Rust + Python)
make fmt # Auto-format Rust code
make lint # Run clippy + format check
make build # Build minimal runtime image
make clean # Remove Docker volumes and local target/
make help # Show all available commands
Always run make fmt before make lint — lint includes a formatting check that will fail on unformatted code.
Project Structure
crates/
saf-core/ # AIR, config, IDs (no LLVM dependency)
saf-frontends/ # LLVM bitcode + AIR-JSON frontends
saf-analysis/ # CFG, call graph, PTA, value-flow, checkers
saf-cli/ # CLI binary
saf-python/ # Python SDK (PyO3 bindings)
saf-wasm/ # Browser/WASM build
python/ # Python SDK source
tests/ # Fixtures and benchmarks
docs/ # Documentation book
tutorials/ # Step-by-step tutorial content
Coding Conventions
Rust
- Error handling — use
thiserrorin libraries,anyhowonly in binaries. No.unwrap()in library code; useResultorexpect()with a message. - Determinism — use
BTreeMap/BTreeSetinstead ofHashMap/HashSetfor deterministic iteration order. - Doc comments — all public items need doc comments. Wrap type names and identifiers in backticks (e.g.,
`ValueId`) to satisfy thedoc_markdownclippy lint. - Clippy — pedantic lints are enabled. Prefer function-level
#[allow]with a comment over crate-level allows.
Python
- Type annotations on all public functions
- Docstrings on all public functions and classes
- Minimum Python 3.12
- Use
uvfor package management (not pip/poetry)
Testing
- TDD workflow — write failing test, implement, refactor
- Prefer specific assertions —
assert!(items.iter().any(|x| x == expected))overassert_eq!(items.len(), 2) - Smoke tests go in
crates/<name>/tests/smoke.rs - Python tests go in
python/tests/ - Integration fixtures in
tests/fixtures/
Compiling C test programs to LLVM IR (inside Docker):
make shell
clang -S -emit-llvm -g -O0 tests/programs/c/example.c -o tests/fixtures/llvm/e2e/example.ll
Submitting Changes
- Fork the repo and create a feature branch
- Make your changes with tests
- Run
make fmt && make lint && make test - Open a pull request with a clear description of what and why
Questions?
Open an issue if you have questions or need guidance on where to start.