Skip to content

Commit b4188ed

Browse files
committed
Rework the driver docs
The removal of driver queries has changed the way rustc_interface can be used. I've also changed things a bit to fix things that were wrong in the docs even before the driver query removal. And finally this is now recommends using rustc_driver over rustc_interface like most tools are already doing. The rustc_driver api is a bit more stable and much easier to use when you don't need the flexibility of rustc_interface as is the case almost all the time. Rustdoc is one of the few tools that needs the flexibility of rustc_interface.
1 parent e7dd865 commit b4188ed

7 files changed

+249
-184
lines changed

examples/rustc-driver-example.rs

Lines changed: 67 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
#![feature(rustc_private)]
22

3+
extern crate rustc_ast_pretty;
34
extern crate rustc_driver;
45
extern crate rustc_error_codes;
56
extern crate rustc_errors;
@@ -9,84 +10,78 @@ extern crate rustc_interface;
910
extern crate rustc_session;
1011
extern crate rustc_span;
1112

12-
use std::{path, process, str, sync::Arc};
13+
use std::io;
14+
use std::path::Path;
15+
use std::str;
16+
use std::sync::Arc;
1317

18+
use rustc_ast_pretty::pprust::item_to_string;
19+
use rustc_data_structures::sync::Lrc;
20+
use rustc_driver::Compilation;
1421
use rustc_errors::registry;
15-
use rustc_hash::FxHashMap;
1622
use rustc_session::config;
1723

24+
struct MyFileLoader;
25+
26+
impl rustc_span::source_map::FileLoader for MyFileLoader {
27+
fn file_exists(&self, path: &Path) -> bool {
28+
path == "main.rs"
29+
}
30+
31+
fn read_file(&self, path: &Path) -> io::Result<String> {
32+
if path == "main.rs" {
33+
Ok(r#"
1834
fn main() {
19-
let out = process::Command::new("rustc")
20-
.arg("--print=sysroot")
21-
.current_dir(".")
22-
.output()
23-
.unwrap();
24-
let sysroot = str::from_utf8(&out.stdout).unwrap().trim();
25-
let config = rustc_interface::Config {
26-
// Command line options
27-
opts: config::Options {
28-
maybe_sysroot: Some(path::PathBuf::from(sysroot)),
29-
..config::Options::default()
30-
},
31-
// cfg! configuration in addition to the default ones
32-
crate_cfg: Vec::new(), // FxHashSet<(String, Option<String>)>
33-
crate_check_cfg: Vec::new(), // CheckCfg
34-
input: config::Input::Str {
35-
name: rustc_span::FileName::Custom("main.rs".into()),
36-
input: r#"
37-
static HELLO: &str = "Hello, world!";
38-
fn main() {
39-
println!("{HELLO}");
35+
let message = "Hello, World!";
36+
println!("{message}");
4037
}
4138
"#
42-
.into(),
43-
},
44-
output_dir: None, // Option<PathBuf>
45-
output_file: None, // Option<PathBuf>
46-
file_loader: None, // Option<Box<dyn FileLoader + Send + Sync>>
47-
locale_resources: rustc_driver::DEFAULT_LOCALE_RESOURCES,
48-
lint_caps: FxHashMap::default(), // FxHashMap<lint::LintId, lint::Level>
49-
// This is a callback from the driver that is called when [`ParseSess`] is created.
50-
psess_created: None, //Option<Box<dyn FnOnce(&mut ParseSess) + Send>>
51-
// This is a callback from the driver that is called when we're registering lints;
52-
// it is called during plugin registration when we have the LintStore in a non-shared state.
53-
//
54-
// Note that if you find a Some here you probably want to call that function in the new
55-
// function being registered.
56-
register_lints: None, // Option<Box<dyn Fn(&Session, &mut LintStore) + Send + Sync>>
57-
// This is a callback from the driver that is called just after we have populated
58-
// the list of queries.
59-
//
60-
// The second parameter is local providers and the third parameter is external providers.
61-
override_queries: None, // Option<fn(&Session, &mut ty::query::Providers<'_>, &mut ty::query::Providers<'_>)>
62-
// Registry of diagnostics codes.
63-
registry: registry::Registry::new(rustc_errors::codes::DIAGNOSTICS),
64-
make_codegen_backend: None,
65-
expanded_args: Vec::new(),
66-
ice_file: None,
67-
hash_untracked_state: None,
68-
using_internal_features: Arc::default(),
69-
};
70-
rustc_interface::run_compiler(config, |compiler| {
71-
compiler.enter(|queries| {
72-
// Parse the program and print the syntax tree.
73-
let parse = queries.parse().unwrap().get_mut().clone();
74-
println!("{parse:?}");
75-
// Analyze the program and inspect the types of definitions.
76-
queries.global_ctxt().unwrap().enter(|tcx| {
77-
for id in tcx.hir().items() {
78-
let hir = tcx.hir();
79-
let item = hir.item(id);
80-
match item.kind {
81-
rustc_hir::ItemKind::Static(_, _, _) | rustc_hir::ItemKind::Fn(_, _, _) => {
82-
let name = item.ident;
83-
let ty = tcx.type_of(item.hir_id().owner.def_id);
84-
println!("{name:?}:\t{ty:?}")
85-
}
86-
_ => (),
87-
}
39+
.to_string())
40+
} else {
41+
Err(io::Error::other("oops"))
42+
}
43+
}
44+
45+
fn read_binary_file(&self, path: &Path) -> io::Result<Lrc<[u8]>> {
46+
Err(io::Error::other("oops"))
47+
}
48+
}
49+
50+
struct MyCallbacks;
51+
52+
impl rustc_driver::Callbacks for MyCallbacks {
53+
fn after_crate_root_parsing(
54+
_compiler: &rustc_interface::Compiler,
55+
krate: &rustc_ast::Crate,
56+
) -> Compilation {
57+
for item in krate.items {
58+
println!("{}", item_to_string(&item));
59+
}
60+
61+
Compilation::Continue
62+
}
63+
64+
fn after_analysis(_compiler: &rustc_interface::Compiler, tcx: TyCtxt<'_>) -> Compilation {
65+
// Analyze the program and inspect the types of definitions.
66+
for id in tcx.hir().items() {
67+
let hir = tcx.hir();
68+
let item = hir.item(id);
69+
match item.kind {
70+
rustc_hir::ItemKind::Static(_, _, _) | rustc_hir::ItemKind::Fn(_, _, _) => {
71+
let name = item.ident;
72+
let ty = tcx.type_of(item.hir_id().owner.def_id);
73+
println!("{name:?}:\t{ty:?}")
8874
}
89-
})
90-
});
91-
});
75+
_ => (),
76+
}
77+
}
78+
79+
Compilation::Stop
80+
}
81+
}
82+
83+
fn main() {
84+
RunCompiler::new(&["main.rs".to_string()], MyCallbacks)
85+
.set_file_loader(Some(Box::new(MyFileLoader)))
86+
.run();
9287
}

examples/rustc-driver-interacting-with-the-ast.rs

Lines changed: 68 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -10,81 +10,85 @@ extern crate rustc_interface;
1010
extern crate rustc_session;
1111
extern crate rustc_span;
1212

13-
use std::{path, process, str, sync::Arc};
13+
use std::io;
14+
use std::path::Path;
15+
use std::str;
16+
use std::sync::Arc;
1417

1518
use rustc_ast_pretty::pprust::item_to_string;
19+
use rustc_data_structures::sync::Lrc;
20+
use rustc_driver::Compilation;
1621
use rustc_errors::registry;
1722
use rustc_session::config;
1823

19-
fn main() {
20-
let out = process::Command::new("rustc")
21-
.arg("--print=sysroot")
22-
.current_dir(".")
23-
.output()
24-
.unwrap();
25-
let sysroot = str::from_utf8(&out.stdout).unwrap().trim();
26-
let config = rustc_interface::Config {
27-
opts: config::Options {
28-
maybe_sysroot: Some(path::PathBuf::from(sysroot)),
29-
..config::Options::default()
30-
},
31-
input: config::Input::Str {
32-
name: rustc_span::FileName::Custom("main.rs".to_string()),
33-
input: r#"
24+
struct MyFileLoader;
25+
26+
impl rustc_span::source_map::FileLoader for MyFileLoader {
27+
fn file_exists(&self, path: &Path) -> bool {
28+
path == "main.rs"
29+
}
30+
31+
fn read_file(&self, path: &Path) -> io::Result<String> {
32+
if path == "main.rs" {
33+
Ok(r#"
3434
fn main() {
3535
let message = "Hello, World!";
3636
println!("{message}");
3737
}
3838
"#
39-
.to_string(),
40-
},
41-
crate_cfg: Vec::new(),
42-
crate_check_cfg: Vec::new(),
43-
output_dir: None,
44-
output_file: None,
45-
file_loader: None,
46-
locale_resources: rustc_driver::DEFAULT_LOCALE_RESOURCES,
47-
lint_caps: rustc_hash::FxHashMap::default(),
48-
psess_created: None,
49-
register_lints: None,
50-
override_queries: None,
51-
make_codegen_backend: None,
52-
registry: registry::Registry::new(rustc_errors::codes::DIAGNOSTICS),
53-
expanded_args: Vec::new(),
54-
ice_file: None,
55-
hash_untracked_state: None,
56-
using_internal_features: Arc::default(),
57-
};
58-
rustc_interface::run_compiler(config, |compiler| {
59-
compiler.enter(|queries| {
60-
// TODO: add this to -Z unpretty
61-
let ast_krate = queries.parse().unwrap().get_mut().clone();
62-
for item in ast_krate.items {
63-
println!("{}", item_to_string(&item));
64-
}
65-
// Analyze the crate and inspect the types under the cursor.
66-
queries.global_ctxt().unwrap().enter(|tcx| {
67-
// Every compilation contains a single crate.
68-
let hir_krate = tcx.hir();
69-
// Iterate over the top-level items in the crate, looking for the main function.
70-
for id in hir_krate.items() {
71-
let item = hir_krate.item(id);
72-
// Use pattern-matching to find a specific node inside the main function.
73-
if let rustc_hir::ItemKind::Fn(_, _, body_id) = item.kind {
74-
let expr = &tcx.hir().body(body_id).value;
75-
if let rustc_hir::ExprKind::Block(block, _) = expr.kind {
76-
if let rustc_hir::StmtKind::Let(let_stmt) = block.stmts[0].kind {
77-
if let Some(expr) = let_stmt.init {
78-
let hir_id = expr.hir_id; // hir_id identifies the string "Hello, world!"
79-
let def_id = item.hir_id().owner.def_id; // def_id identifies the main function
80-
let ty = tcx.typeck(def_id).node_type(hir_id);
81-
println!("{expr:#?}: {ty:?}");
82-
}
83-
}
39+
.to_string())
40+
} else {
41+
Err(io::Error::other("oops"))
42+
}
43+
}
44+
45+
fn read_binary_file(&self, path: &Path) -> io::Result<Lrc<[u8]>> {
46+
Err(io::Error::other("oops"))
47+
}
48+
}
49+
50+
struct MyCallbacks;
51+
52+
impl rustc_driver::Callbacks for MyCallbacks {
53+
fn after_crate_root_parsing(
54+
_compiler: &rustc_interface::Compiler,
55+
krate: &rustc_ast::Crate,
56+
) -> Compilation {
57+
for item in krate.items {
58+
println!("{}", item_to_string(&item));
59+
}
60+
61+
Compilation::Continue
62+
}
63+
64+
fn after_analysis(_compiler: &rustc_interface::Compiler, tcx: TyCtxt<'_>) -> Compilation {
65+
// Every compilation contains a single crate.
66+
let hir_krate = tcx.hir();
67+
// Iterate over the top-level items in the crate, looking for the main function.
68+
for id in hir_krate.items() {
69+
let item = hir_krate.item(id);
70+
// Use pattern-matching to find a specific node inside the main function.
71+
if let rustc_hir::ItemKind::Fn(_, _, body_id) = item.kind {
72+
let expr = &tcx.hir().body(body_id).value;
73+
if let rustc_hir::ExprKind::Block(block, _) = expr.kind {
74+
if let rustc_hir::StmtKind::Let(let_stmt) = block.stmts[0].kind {
75+
if let Some(expr) = let_stmt.init {
76+
let hir_id = expr.hir_id; // hir_id identifies the string "Hello, world!"
77+
let def_id = item.hir_id().owner.def_id; // def_id identifies the main function
78+
let ty = tcx.typeck(def_id).node_type(hir_id);
79+
println!("{expr:#?}: {ty:?}");
8480
}
8581
}
8682
}
87-
})
88-
});
89-
});
83+
}
84+
}
85+
86+
Compilation::Stop
87+
}
88+
}
89+
90+
fn main() {
91+
RunCompiler::new(&["main.rs".to_string()], MyCallbacks)
92+
.set_file_loader(Some(Box::new(MyFileLoader)))
93+
.run();
9094
}

examples/rustc-interface-example.rs

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
#![feature(rustc_private)]
2+
3+
extern crate rustc_driver;
4+
extern crate rustc_error_codes;
5+
extern crate rustc_errors;
6+
extern crate rustc_hash;
7+
extern crate rustc_hir;
8+
extern crate rustc_interface;
9+
extern crate rustc_session;
10+
extern crate rustc_span;
11+
12+
use std::{path, str, sync::Arc};
13+
14+
use rustc_errors::registry;
15+
use rustc_hash::FxHashMap;
16+
use rustc_session::config;
17+
18+
fn main() {
19+
let config = rustc_interface::Config {
20+
// Command line options
21+
opts: config::Options::default(),
22+
// cfg! configuration in addition to the default ones
23+
crate_cfg: Vec::new(), // FxHashSet<(String, Option<String>)>
24+
crate_check_cfg: Vec::new(), // CheckCfg
25+
input: config::Input::Str {
26+
name: rustc_span::FileName::Custom("main.rs".into()),
27+
input: r#"
28+
static HELLO: &str = "Hello, world!";
29+
fn main() {
30+
println!("{HELLO}");
31+
}
32+
"#
33+
.into(),
34+
},
35+
output_dir: None, // Option<PathBuf>
36+
output_file: None, // Option<PathBuf>
37+
file_loader: None, // Option<Box<dyn FileLoader + Send + Sync>>
38+
locale_resources: rustc_driver::DEFAULT_LOCALE_RESOURCES,
39+
lint_caps: FxHashMap::default(), // FxHashMap<lint::LintId, lint::Level>
40+
// This is a callback from the driver that is called when [`ParseSess`] is created.
41+
psess_created: None, //Option<Box<dyn FnOnce(&mut ParseSess) + Send>>
42+
// This is a callback from the driver that is called when we're registering lints;
43+
// it is called during plugin registration when we have the LintStore in a non-shared state.
44+
//
45+
// Note that if you find a Some here you probably want to call that function in the new
46+
// function being registered.
47+
register_lints: None, // Option<Box<dyn Fn(&Session, &mut LintStore) + Send + Sync>>
48+
// This is a callback from the driver that is called just after we have populated
49+
// the list of queries.
50+
//
51+
// The second parameter is local providers and the third parameter is external providers.
52+
override_queries: None, // Option<fn(&Session, &mut ty::query::Providers<'_>, &mut ty::query::Providers<'_>)>
53+
// Registry of diagnostics codes.
54+
registry: registry::Registry::new(rustc_errors::codes::DIAGNOSTICS),
55+
make_codegen_backend: None,
56+
expanded_args: Vec::new(),
57+
ice_file: None,
58+
hash_untracked_state: None,
59+
using_internal_features: Arc::default(),
60+
};
61+
rustc_interface::run_compiler(config, |compiler| {
62+
// Parse the program and print the syntax tree.
63+
let parse = rustc_interface::passes::parse(&compiler.sess);
64+
println!("{parse:?}");
65+
// Analyze the program and inspect the types of definitions.
66+
rustc_interface::create_and_enter_global_ctxt(&compiler, krate, |tcx| {
67+
for id in tcx.hir().items() {
68+
let hir = tcx.hir();
69+
let item = hir.item(id);
70+
match item.kind {
71+
rustc_hir::ItemKind::Static(_, _, _) | rustc_hir::ItemKind::Fn(_, _, _) => {
72+
let name = item.ident;
73+
let ty = tcx.type_of(item.hir_id().owner.def_id);
74+
println!("{name:?}:\t{ty:?}")
75+
}
76+
_ => (),
77+
}
78+
}
79+
});
80+
});
81+
}

0 commit comments

Comments
 (0)