Thanks for your interest in contributing to the Turbo compiler. This guide covers everything you need to get started.
- Rust (stable toolchain) -- install via rustup
- C compiler (
cc) -- required for linking the C runtime into AOT binaries - Git
git clone https://github.com/ZVN-DEV/Turbo-Language.git
cd Turbo-Language
# Build (debug)
cargo build --manifest-path turbo/Cargo.toml
# Run all unit tests
cargo test --workspace --manifest-path turbo/Cargo.toml
# Run a source file to sanity-check
cargo run --manifest-path turbo/Cargo.toml -- run turbo/tests/phase1/hello.tbThe compiler is a Cargo workspace under turbo/ with seven crates:
| Crate | Purpose |
|---|---|
turbo-lexer |
Logos-based tokenizer. Produces Spanned<Token> stream. |
turbo-ast |
Shared AST types: Module, Item, Expr, Stmt, TypeExpr, Pattern. |
turbo-parser |
Recursive descent parser with multi-error recovery; also runs a post-parse COW rewrite pass (cow_rewrite.rs). |
turbo-sema |
Semantic analysis: type checking, scope resolution, exhaustiveness. |
turbo-codegen-cranelift |
Cranelift JIT + AOT backend. Also contains runtime/turbo_rt.c. |
turbo-cli |
CLI frontend: run, build, test, fmt, init, lsp, repl, bench, doc. |
turbo-lsp |
Language Server Protocol server (diagnostics, hover, go-to-def). |
Compiler pipeline: Lexer -> Parser (+ COW rewrite pass) -> Sema -> Codegen
See CLAUDE.md for detailed architecture, key types, and common task walkthroughs.
- Integration tests:
turbo/tests/phase1/--.tbsource files with matching.expectedoutput files. - Unit tests:
#[cfg(test)]modules inside each crate. - Test runner:
turbo/tests/run_tests.shruns all integration tests against a release build.
Before opening a PR, make sure all checks pass locally:
# Format -- must produce no changes
cargo fmt --all --manifest-path turbo/Cargo.toml
# Lint -- must produce zero warnings
cargo clippy --all --manifest-path turbo/Cargo.toml -- -D warnings
# Unit tests
cargo test --workspace --manifest-path turbo/Cargo.toml
# Integration tests (requires release build)
cargo build --release -p turbo-cli --manifest-path turbo/Cargo.toml
cd turbo && ./tests/run_tests.shCI enforces all four of these checks. A PR that fails any of them will not be merged.
The repo ships a pre-commit hook at turbo/.git-hooks/pre-commit that runs
cargo fmt --check and cargo clippy -D warnings before every commit. Install
it once with:
git config core.hooksPath turbo/.git-hooksThe hook is fast (fmt first; clippy only runs if fmt passes) and prints the command to re-run if any check fails.
- Sema (
turbo-sema/src/lib.rs) -- add the function signature to the built-in type environment. - Codegen (
turbo-codegen-cranelift/src/builtins.rs) -- add a compile function, and add the dispatch branch incompile_call()insrc/expr.rs. - JIT setup (
turbo-codegen-cranelift/src/jit.rs) -- register the function pointer in the JIT symbol table. - C runtime (
turbo-codegen-cranelift/runtime/turbo_rt.c) -- implement the C function for AOT builds. - Test -- add a
.tb/.expectedpair inturbo/tests/phase1/. - COW registration -- if the builtin returns a new value instead of mutating its first argument in place (like
push,map,trim), add its name to theCOW_BUILTINSlist inturbo-parser/src/cow_rewrite.rsso that statement-position calls get rewritten into self-assigns (arr.push(4)becomesarr = push(arr, 4)).
- Lexer (
turbo-lexer/src/lib.rs) -- add any new tokens or keywords. - AST (
turbo-ast/src/lib.rs) -- add the new variant toExpr,Stmt, orItem. - Parser (
turbo-parser/src/lib.rs) -- parse the new syntax into the AST node. - Sema (
turbo-sema/src/lib.rs) -- add type-checking logic. - Codegen (
turbo-codegen-cranelift/src/lib.rs) -- add code generation. - Formatter (
turbo-formatter/src/lib.rs) -- handle pretty-printing if applicable. - Test -- add a
.tb/.expectedpair inturbo/tests/phase1/.
- Create
turbo/tests/phase1/my_feature.tbwith afn main()that prints output. - Create
turbo/tests/phase1/my_feature.expectedwith the exact expected stdout. - For error tests, put
ERROR:<pattern>as the first line of.expected.
- Fork the repository and create a feature branch from
master. - Implement your change, following the patterns above.
- Test locally -- all four checks (fmt, clippy, tests, integration) must pass.
- Open a PR against
masterwith a clear description of what and why. - Keep PRs focused -- one feature or one fix per PR. Split large changes into smaller PRs.
CI runs automatically on every PR. All checks must pass before merge.
Every diagnostic in Turbo carries a unique ErrorCode (e.g. E0100).
The long-form explanation lives in one place — never duplicate it.
- Source of truth:
turbo/crates/turbo-cli/src/errors/E0NNN.md. The CLI embeds these viainclude_str!soturbolang explain E0NNNkeeps working in a release binary with no filesystem dependency. - Public docs path:
docs/errors/E0NNN.mdat the repo root. These are symlinks pointing back at the source-of-truth files. Both paths must resolve to the same content;turbo-cli/build.rsfails the build if any variant ofErrorCodeis missing adocs/errors/entry. - URL convention: every rendered error ends with a
more info:footer pointing at the GitHub blob URLhttps://github.com/ZVN-DEV/Turbo-Language/blob/master/docs/errors/E0NNN.md. Onceturbolang.dev/errors/E0NNNhas a real redirect to the same content the CLI will be flipped back to the short form (see theTODO(P3)inerror_code_url()inturbo-cli/src/main.rs).
- Add the variant to
turbo-ast::ErrorCode(turbo/crates/turbo-ast/src/errors.rs). - Update the
as_str,description, andallimpls so the new variant is recognized everywhere. - Create the long-form explanation at
turbo/crates/turbo-cli/src/errors/E0NNN.md. - Create the public symlink:
cd docs/errors && ln -s ../../turbo/crates/turbo-cli/src/errors/E0NNN.md E0NNN.md
- Add the
include_str!line indetailed_explanation()insideturbo-cli/src/main.rs. - The build will fail until steps 3, 4, and 5 are all in place — that is the intended drift-prevention behavior, not a bug.
Release artifacts ship with a signed checksums.txt. To verify a download
before installing:
# 1. Import the public release-signing key (one-time setup).
curl -sSL https://turbolang.dev/keys/release.asc | gpg --import
# 2. Verify the signature on the manifest.
gpg --verify checksums.txt.sig checksums.txt
# 3. Verify the tarball matches the signed checksum.
sha256sum --check --ignore-missing checksums.txtIf gpg --verify reports Good signature, the manifest is trusted; the
sha256sum --check step then proves the tarball you downloaded matches
the manifest entry.
- Naming:
snake_casefor functions and variables,CamelCasefor types and enums. - Error types:
SemaError,ParseError, andCodegenErrorall carry aSpanfor diagnostic rendering. Useariadnefor pretty error output. - Comments: Explain why, not what. The code should be clear enough to explain itself.
- No
unwrap()in production code. Use?ormatchin the CLI, LSP, and codegen crates.unwrap()is acceptable only in unit tests. - Error propagation: The parser collects errors into
Vec<ParseError>and keeps going. Sema usesTy::Erroras a poison type to prevent cascading errors. - Built-in functions are recognized by name in
compile_call(), not in the AST. Follow the existing pattern when adding new ones.
If you discover a security vulnerability, do not open a public issue.
Use one of the private channels described in SECURITY.md:
- GitHub: Private vulnerability reporting
For details on the security model, threat boundaries, and what is in scope,
see SECURITY.md.
Open an issue or start a discussion on GitHub. We are happy to help you find the right place to make your change.