-Ewarning: The Uno Reverse card for compiler errors
I taught clang to fix my code instead of yelling at me
This one’s a bit more technical than usual. But here’s the concept in thirty seconds:
You know how spell check doesn’t just underline the word in red, it fixes it for you? I built that, but for code. Your program has an error, and instead of the compiler stopping and telling you what’s wrong, it calls an AI to fix it and keeps going.
Except spell check is free, runs locally, and doesn’t send your novel to a cloud API every time you misspell “receive.”
And spell check is deterministic. It always corrects “recieve” to “receive.” This thing runs on vibes. You write “I di dnot want to delete the database”, and depending on the mood of the AI that day, you get back either “I did not want to delete the database” or “I want to delete the database.” Both are valid sentences. One of them ruins your week.
That’s the spirit of the project. Now for the details.
Years ago, deep in a C codebase, I had one of those thoughts that lodges itself in your brain and refuses to leave.
You know -Werror, right? The compiler flag that says “treat warnings as errors.” The flag that senior devs enable in CI and junior devs curse at 11 PM on a Friday.
I was staring at yet another -Werror-induced build failure, and a stupid question formed:
What if you could do the opposite?
Not treat warnings as errors. Treat errors as warnings. -Ewarning.
The compiler hits an error, shrugs, and just... fixes it. Keeps going. Like a coworker who sees you typed retrun 0 and just quietly corrects it without making a Slack thread about it.
Obviously absurd. Obviously impossible. You’d need the compiler to somehow understand your code, understand what you meant, and rewrite it correctly.
I filed it under “shower thoughts” and moved on with my life.
That was years ago. Then the world got weird.
The phase we’re in
We’re living through a genuinely strange moment in computing history.
A few weeks ago, Anthropic published a blog post about 16 Claude agents building a C compiler from scratch. 100,000 lines of Rust. Compiles the Linux kernel. Cost about $20,000. Two billion input tokens. Two weeks. A compiler that passes 99% of standard test suites.
Around the same time, I sat in my living room, forked LLVM, added ~460 lines of C++, and made clang call Claude when your code doesn’t compile. So it can fix your semicolons.
One team used AI to build a compiler. I used AI to make a compiler forgive you.
Both of these things are happening simultaneously, and neither feels entirely real. The old rules of what’s hard and what’s easy in computer science have been thrown into a blender.
Building a whole compiler from scratch? Apparently a two-week side project now. Making an existing compiler nice to you? Also a side project. A smaller one.
The weirdness isn’t that either of these exists. It’s that they both exist at the same time, in the same universe, using the same underlying technology. One of them is deeply impressive engineering. The other one adds curl as a dependency to LLVM.
What it actually does
Say you write this:
#include <stdio.h>
int main() {
printf("Hello, World!\\n")
retrun 0;
}
Two errors. Missing semicolon, typo in return. Classic. The kind of thing you’ve fixed ten thousand times. Normally, the compiler tells you about it and stops:
$ clang broken.c -o broken
broken.c:4:30: error: expected ';' after expression
4 | printf("Hello, World!\\n")
| ^
| ;
broken.c:5:5: error: use of undeclared identifier 'retrun'
5 | retrun 0
| ^~~~~~
2 errors generated.
You read the errors, fix the code, recompile. The cycle of life.
With -Ewarning, the compiler does that part for you:
$ ANTHROPIC_API_KEY=sk-ant-... clang -Ewarning broken.c -o broken
broken.c:4:30: error: expected ';' after expression
4 | printf("Hello, World!\\n")
| ^
| ;
broken.c:5:5: error: use of undeclared identifier 'retrun'
5 | retrun 0
| ^~~~~~
2 errors generated.
-Ewarning: Hmm, that didn't compile. Let me take a look...
-Ewarning: compilation succeeded after 1 fix(es)!
$ cat broken.c
#include <stdio.h>
int main() {
printf("Hello, World!\\n");
return 0;
}
$ ./broken
Hello, World!
Your broken.c now contains correct code. The binary works. Life goes on.
-Werror says: “I don’t care that this is just a warning. Fail.”
-Ewarning says: “I don’t care that this is an error. Fix it.”
The perfect inverse.
And yes, of course you can pass both at the same time. -Werror -Ewarning promotes your warnings to errors and then fixes them.
The compiler argues with itself so you don’t have to.
How far can we take this?
Fixing typos in hello world is the boring case. What happens when the input isn’t even code?
$ cat broken.c
I want to print hello world
$ clang -Ewarning broken.c -o broken
broken.c:1:1: error: unknown type name 'I'
1 | I want to print hello world
| ^
...
-Ewarning: Hmm, that didn't compile. Let me take a look...
-Ewarning: compilation succeeded after 1 fix(es)!
$ ./broken
hello world
$ cat broken.c
#include <stdio.h>
int main(void) {
printf("hello world\\n");
return 0;
}
English in, working C out. The file that said “I want to print hello world” is now a valid C program that prints hello world. The compiler didn’t just fix your code. It wrote your code.
It scales, too. I put in a paragraph asking for a DVD screensaver: an ASCII “DVD” logo bouncing around the terminal, changing color when it hits a wall. The compiler choked on the English, sent it to Claude, and got back 80 lines of C with ioctl terminal size detection, ANSI escape codes, and proper edge collision. Compiled first try. The DVD logo bounces.
At this point you’re not using a compiler anymore. You’re using a compiler-shaped chatbot that happens to produce binaries.
The guts
The implementation is surprisingly straightforward, which is part of what makes this era so strange. The hard part isn’t the code. It’s the fact that the idea works at all.
The flag itself lives in Clang’s option definitions, right next to -Werror. Its evil twin:
def Ewarning : Flag\\<\\["-"\\], "Ewarning">,
Visibility<\\[ClangOption\\]>,
HelpText<"Treat errors as warnings: use an LLM to fix
compilation errors in-place.">;
The loop is where it gets interesting. The driver normally compiles once, reports errors, and exits. With -Ewarning, it wraps compilation in a retry loop: compile, fail, grab the diagnostics, send source + errors to Claude (or GPT-4o), write the fixed code back to the file, rebuild, try again. Up to 5 retries by default. Configurable via EWARNING_MAX_RETRIES, for the truly reckless.
The core, LLMFixit.cpp, does exactly what you’d hope it wouldn’t. It reads your source file, reads the compiler’s error output, builds a JSON payload for the Anthropic or OpenAI API, shells out to curl, parses the response, strips any markdown fences the LLM might have hallucinated, and writes the fixed code back to disk.
It uses curl. From inside Clang. Let that sink in.
It makes HTTP requests during compilation.
It sends your source code to a cloud API.
It modifies your source files in-place while compiling them.
It costs money per compilation error.
It’s amazing.
The provider auto-detection checks your ANTHROPIC_API_KEY or OPENAI_API_KEY. You can override with EWARNING_API_URL to point it at Ollama or OpenRouter if you want to be responsible about your irresponsible compiler flag usage.
The retry messages escalate, naturally:
"Hmm, that didn't compile. Let me take a look..."
"OK I see the issue, trying a different approach..."
"Third time's the charm, right?"
"I've seen worse code... actually, no I haven't. Fixing..."
"Last attempt, I promise this will work (probably)..."
In color. With ANSI escape codes. Inside the Clang compiler.
The old and the new
Here’s what gets me about this project.
Clang is old software. Not “a few years old.” It’s the product of decades of compiler research. LLVM started in 2000. Generations of compiler engineers have poured their expertise into making it produce the most precise, most helpful error messages possible.
Those error messages are works of art:
broken.c:4:30: error: expected ';' after expression
4 | printf("Hello, World!\\n")
| ^
| ;
It tells you exactly what’s wrong. Points to the exact column. Suggests the fix with a little caret and the missing character. Decades of UX work went into that output.
And now I’m feeding it to an LLM and asking “hey, can you just... fix this?”
Clang’s error messages were designed to help humans understand and fix their code. -Ewarning uses those same carefully crafted messages to help an AI fix the code instead. The error messages work just as well for both audiences. The compiler engineers accidentally built perfect LLM prompts, twenty years before LLMs existed.
Meanwhile, Carlini’s team at Anthropic went the other direction entirely. Instead of bolting AI onto an existing compiler, they had AI write the compiler itself. Two approaches, same era, same technology. One is a genuine feat of autonomous AI engineering. The other exists because now it can.
Somehow both feel equally representative of the moment we’re in.
The fine print
Every engineering decision has tradeoffs. Here are some of the tradeoffs.
It costs money per error. Every typo is an API call. And it sends your entire source file as context, so the cost scales with your codebase. A missing semicolon in a 10-line file is cheap. A missing semicolon in a 10,000-line file is a conversation with your finance team.
It also sends that source code to an external API, over the wire, to fix said semicolon. It modifies your source files while compiling, in-place, without asking.
LLMs are non-deterministic, so the same error might produce different fixes on different runs. Good luck debugging that.
Each retry takes seconds, so compilation that used to fail in milliseconds now takes 10+ seconds to succeed.
Progress?
And it might “fix” things you didn’t want fixed. The LLM sees errors and fixes them. It doesn’t know your intent. It knows your mistakes.
If you put -Ewarning in a CI pipeline, you deserve whatever happens to you.
This is a toy. A beautiful, cursed, gloriously stupid toy. It exists because the world we live in makes it possible, not because it’s a good idea.
The weirdness of it all
Twenty years ago, you’d pitch -Ewarning at a conference talk to get a laugh. “What if the compiler just fixed your code?”
Ten years ago, it would’ve been a research paper. “We trained a neural network on Stack Overflow to suggest fixes for common C errors.” Results: 12% accuracy. Conclusion: more research needed.
Five years ago, GPT-3 existed but you wouldn’t trust it to fix a semicolon.
Today it’s 460 lines of C++ and it works. Not perfectly. Not even reliably. But it works often enough to be genuinely impressive, and unreliable enough to teach you how you should deal with compiler errors in no-time.
The line between “side project” and “genuinely useful tool” has gotten blurry. Carlini’s 16-agent compiler started as an experiment and ended up compiling the Linux kernel. My -Ewarning flag started as a decades-old intrusive thought and ended up as a real Clang feature (in my personal fork).
The old world of compilers; precise, deterministic, painstakingly engineered, is colliding with the new world of LLMs: probabilistic, surprising, occasionally brilliant, occasionally unhinged. -Ewarning lives exactly at that collision point. It takes the most rigorous piece of software on your machine and injects pure chaos into it.
We’re in the era where a thought from the -Werror days can become a real compiler flag. Where fixing your own code is optional. Where an AI can build the compiler and forgive the programmer.
Do try it out at [Driver] Add -Ewarning flag: use an LLM to fix compilation errors in-place by Afstkla · Pull Request #1 · Afstkla/llvm-project
Building at neople.io.



