for fun and profit
I plan to mercilessly show you his private notes, at least when they're endearing.
Hopefully you are not here to play Rust the game…
You're here because you
"oh boy, let me tell you about my PhD thesis which is just about that topic"
Write a rustc that runs a custom lint to detect comparisons like x == x
.
Then we can give a nice friendly error message!
All examples work with
rustc 1.53.0-nightly (f82664191 2021-03-21)
You can follow these examples via the hackmd of this presentation.
Also you can learn tons about rustc in the rustc-dev-guide:
rustup component add rustc-dev llvm-tools-preview
#![feature(rustc_private)]
#![deny(rustc::internal)]
extern crate rustc_driver;
extern crate rustc_interface;
extern crate rustc_errors;
extern crate rustc_lint;
At present, the API is forever unstable, use at your own risk
struct MyCallbacks;
impl rustc_driver::Callbacks for MyCallbacks {}
fn main() -> Result<(), rustc_errors::ErrorReported> {
}
fn main() -> Result<(), rustc_errors::ErrorReported> {
let args: Vec<_> = std::env::args().collect();
}
fn main() -> Result<(), rustc_errors::ErrorReported> {
let args: Vec<_> = std::env::args().collect();
let mut my_cb = MyCallbacks;
}
fn main() -> Result<(), rustc_errors::ErrorReported> {
let args: Vec<_> = std::env::args().collect();
let mut my_cb = MyCallbacks;
rustc_driver::RunCompiler::new(&args, &mut my_cb).run()
}
You have now reproduced rustc. You rock!
Too bad people could already run rustc.
struct MyCallbacks;
impl rustc_driver::Callbacks for MyCallbacks {}
Callbacks is a trait which you can use to customize your compilation.
impl rustc_driver::Callbacks for MyCallbacks {
fn config(&mut self, config: &mut Config) {
}
}
impl rustc_driver::Callbacks for MyCallbacks {
fn config(&mut self, config: &mut Config) {
config.register_lints = Some(Box::new(|_, ls| {
}));
}
}
impl rustc_driver::Callbacks for MyCallbacks {
fn config(&mut self, config: &mut Config) {
config.register_lints = Some(Box::new(|_, ls| {
lint_store.register_late_pass(|| {
})
}));
}
}
impl rustc_driver::Callbacks for MyCallbacks {
fn config(&mut self, config: &mut Config) {
config.register_lints = Some(Box::new(|_, ls| {
lint_store.register_late_pass(|| {
Box::new(MyLint)
})
}));
}
}
struct MyLint;
impl rustc_lint::LintPass for MyLint {
fn name(&self) -> &'static str {
"The best lint"
}
}
impl<'tcx> rustc_lint::LateLintPass<'tcx> for MyLint {}
graph LR
.rs --> AST;
AST --Macro expansion--> AST;
AST --> HIR;
HIR --Type checking--> HIR;
HIR --> MIR;
MIR --Optimization--> MIR;
MIR --> LLVM;
LLVM --Dear god who knows--> LLVM;
LLVM --> .exe;
lint type | trait name | datastructures |
---|---|---|
pre-expansion | EarlyLintPass |
AST |
post-expansion | EarlyLintPass |
AST |
type-checked | LateLintPass |
HIR |
impl<'tcx> rustc_lint::LateLintPass<'tcx> for MyLint {
fn check_expr(
&mut self,
cx: &rustc_lint::LateContext<'tcx>,
expr: &rustc_hir::Expr<'tcx>,
) {
// Static analysis goes here
}
}
if let rustc_hir::ExprKind::Binary(op, l, r) = expr {
if l.kind == r.kind {
// Complain loudly
}
}
Not the code you really want, but gives you the idea:
==
compares too strictlyCheck the clippy version for something more realistic.
file_loader
register_lints
override_queries
make_codegen_backend
file_loader
digraph {
mir_built -> compute_hir
layout_of -> typeck
optimized_mir -> analyzed_mir -> mir_built
const_eval -> mir_for_ctfe -> analyzed_mir
codegen -> optimized_mir -> layout_of
codegen -> const_eval -> layout_of
late_lints -> compute_hir
typeck -> compute_hir
}
override_queries
layout_of
optimized_mir
mir_built
__start
symbolrustc_mir::interpret