c2go: translating C to Go through the clang AST
⚖️ A tool for transpiling C to Go.
At a glance
- What is it?
- A transpiler that preprocesses C with clang, reads the resulting abstract syntax tree, and emits Go you can actually run, plus an integration test suite that diffs both binaries' output.
- Who is it for?
- c2go is a serious transpiler rather than a regex over C, because it takes the type information from clang seriously and emits Go that compiles without hand cleanup. The pipeline is three steps, preprocess, parse to AST, emit Go, and the part that makes it practical is the integration suite that compiles the original C, builds the generated Go, runs both and compares stdout.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 51 days ago.
- What is it written in?
- Mainly Go, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 11, 2026, and from our analysis. They are not legal advice.
Editorial analysis
Installing the binary and running it on one file
Installation is a single `go install`, which means you get a working transpiler if you have a Go toolchain, and no build step beyond that.
go install github.com/elliotchance/c2go@latestThe README says Go 1.9 or newer is required, while `go.mod` declares `go 1.19` and the module depends on `golang.org/x/tools` at v0.24.0 and `cupaloy/v2` at v2.8.0 for snapshot testing. In practice, use a current Go release.
The tool handles one C file at a time:
c2go transpile myfile.cThat single file limit is a design decision with consequences. Real C projects are made of many files, so c2go is a per translation unit tool rather than a whole program converter. If you are porting a library one function at a time, or experimenting with what the AST approach yields, that is a good shape. If you want to drop a command line tool into Go, the assembly is on you.
What a translated prime.c actually looks like
The README walks through `examples/prime.c`, a small C program that reads a number and prints whether it is prime. The point of showing it is the shape of the output, so here is the interesting part of the generated `prime.go`:
package main
import "unsafe"
import "github.com/elliotchance/c2go/noarch"
var stdin *noarch.File
var stdout *noarch.File
var stderr *noarch.FileTwo things stand out. The first is `unsafe.Pointer` in a `Scanf` call, passed as a slice expression to emulate C's pointer-to-argument convention. That is the honest cost of keeping C semantics: variadic pointer arguments do not translate cleanly, and the transpiler uses unsafe code rather than allocating temporary variables.
The second is the loop increment, which becomes an immediately invoked function:
for c = 2; c <= n-1; func() int {
c += 1
return c
}() {C for loops have a third clause that can be any statement. Go's for loop has only a post statement. When the increment is simple, the transpiler inlines it; here it wraps it in a closure so an arbitrary expression still works. You can read the generated code and understand why each construct is shaped that way, which is a good sign for a transpiler that aims at output you are meant to keep.
Strings also carry explicit null terminators, visible as `\x00` at the end of each literal, because C strings are NUL terminated and Go's are not.
Preprocess, parse to AST, then emit Go
The three step pipeline is described plainly in the README, and each step is a deliberate choice.
First, the C is preprocessed with clang, producing a larger intermediate file while removing platform specific directives and macros. Second, that preprocessed file is parsed and the clang AST is dumped in a colourful text format, which the transpiler then reads. Third, the tree is traversed to produce Go.
Step two is the load bearing one. The README points out that the AST contains all the resolved information a compiler needs, including data types, which means the code must compile successfully under clang for the AST to be useful. That is a real constraint: c2go can only transpile code clang accepts, so constructs clang rejects are out of scope by construction.
The README also names the reason the tool works across clang versions, and it is not a portability abstraction: the clang AST API is not stable, so the project aims to work against as many clang versions as possible rather than depending on the API. Dumping to text and parsing that is a pragmatic way to avoid binding to a C API from Go.
The repository structure follows the pipeline. `preprocessor/` handles step one, `ast/` the parsing and dumping, `transpiler/` the traversal and emission, and `types/`, `cc/`, `program/` and `util/` support them. `noarch/` holds the runtime library the generated code imports.
The noarch runtime is what makes generated code portable
Generated Go imports `github.com/elliotchance/c2go/noarch`, and the tree shows three platform directories, `linux/`, `darwin/` and `noarch/`. The split is the interesting part. Platform specific code lives in the platform directories, while everything a C program reaches for regardless of operating system lives in `noarch`.
`noarch` supplies the C standard library surface as Go: `Printf`, `Scanf`, the `File` type, and the `Stdin`, `Stdout` and `Stderr` values that the generated `__init` function assigns. That last detail appears in the README's output, where `__init` sets all three streams from the runtime, mirroring how a C program's globals point at the real streams.
This is the design decision with the widest blast radius. Transpiled output is not standalone Go that runs anywhere; it carries a dependency on this project's runtime library, at an import path pinned to this repository. Forking or vendoring c2go means taking the runtime with you, and a project that stops being maintained leaves generated code pointing at a module version that still has to resolve. That is the honest tradeoff for avoiding a from scratch reimplementation of the C library in every generated file.
Integration tests that compile and run both languages
The test strategy is more informative than the roadmap. By default `go test` runs unit tests only, and integration tests are behind a build tag:
go test -tags=integration ./...The integration tests are complete C programs in the `tests/` directory, and the procedure has four steps: clang compiles the C to a binary, c2go converts the file to Go, the Go is built into another binary, and then both binaries are executed with their output compared. The README notes that every C file contains some output specifically so the comparison has something to check.
That is a much stronger guarantee than checking that the transpiler emits code. Output equivalence catches semantic drift, which is where transpilers usually fail quietly. Combined with the `.snapshots/` directory and the `cupaloy` dependency for snapshot testing, the project is testing both that output is correct and that generated code has not changed shape unexpectedly.
There is also `main_test.go` and `cli_test.go` at the root, which suggests the command line interface itself is covered rather than only the translation core.
An ambitious goal, an old release, active commits
The stated goals are worth quoting because goal four and five set the expectations. The project wants to be cross platform across Linux and mac, repeatable and predictable rather than mostly right with cleanup needed, and it wants quick small version increments. The ultimate milestone, explicitly stated, is being able to compile the SQLite3 source code and have it work without modification, and that is what has been named as the 1.0.0 release.
That is a well chosen target. SQLite3 is a large, portable, C89 C codebase with its own test suite, so if a transpiler can run it unmodified, it can run ordinary C. No such milestone has been reached: the releases sit at v0.26.11, v0.26.10 and v0.26.9.
The dates tell a specific story. v0.26.9 published in June 2021, v0.26.10 in August of the same year, then a long gap to v0.26.11 on 2024-06-03, and the repository was last pushed on 2026-08-21. So commits continue while tagged releases have been infrequent, which is normal for a project with a small maintainer base.
There are 183 open issues against 2185 stars and 165 forks. For a tool in its zero something series with a stated ambition of completeness, that backlog is expected rather than alarming, and the contributing guide asks for help in exactly that form: find a simple C program that fails to translate and submit it, or file it as an issue. The MIT licence and the low bar to contributing make it a reasonable project to test against your own C code.
Editorial conclusion
c2go is a serious transpiler rather than a regex over C, because it takes the type information from clang seriously and emits Go that compiles without hand cleanup. The pipeline is three steps, preprocess, parse to AST, emit Go, and the part that makes it practical is the integration suite that compiles the original C, builds the generated Go, runs both and compares stdout. That design tells you what the project is optimising for: predictable output you can check, rather than best effort. The stated 1.0.0 milestone is compiling the SQLite3 source unmodified, which is a good yardstick for any C to Go transpiler. It is MIT licensed, needs Go 1.19 or newer for the module and clang on the path, and the latest tagged release is v0.26.11 from 2024-06-03. Start by running `c2go transpile examples/prime.c` and reading the emitted `prime.go`.
Frequently asked questions
What is c2go used for?
c2go converts C source into Go source. It preprocesses the file with clang, reads the resulting AST for its type information, and emits Go you can compile and run, with calls into a bundled runtime library for the C standard library surface.
How do I install and run c2go?
Run go install github.com/elliotchance/c2go@latest, which needs a Go toolchain and clang available on your path. Then transpile one file at a time with c2go transpile myfile.c, and build the resulting Go as you would any other program.
How does c2go verify that a translation is correct?
Its integration tests compile the original C to a binary, transpile it, build the generated Go into a second binary, run both and compare their output. Those tests are behind a build tag and run with go test -tags=integration ./..., alongside snapshot testing of generated code.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/elliotchance-c2go)