diff --git a/src/SUMMARY.md b/src/SUMMARY.md index f1efcf59..87c638ff 100644 --- a/src/SUMMARY.md +++ b/src/SUMMARY.md @@ -50,6 +50,7 @@ - [The Rustc Driver and Interface](./rustc-driver.md) - [Rustdoc](./rustdoc.md) - [Ex: Type checking through `rustc_interface`](./rustc-driver-interacting-with-the-ast.md) + - [Ex: Getting diagnostics through `rustc_interface`](./rustc-driver-getting-diagnostics.md) - [Syntax and the AST](./syntax-intro.md) - [Lexing and Parsing](./the-parser.md) - [`#[test]` Implementation](./test-implementation.md) diff --git a/src/rustc-driver-getting-diagnostics.md b/src/rustc-driver-getting-diagnostics.md new file mode 100644 index 00000000..d4b39555 --- /dev/null +++ b/src/rustc-driver-getting-diagnostics.md @@ -0,0 +1,101 @@ +# Example: Getting diagnostic through `rustc_interface` + +`rustc_interface` allows you to intercept diagnostics that would otherwise be printed to stderr. + +## Getting diagnostics + +NOTE: For the example to compile, you will need to first run the following: + + rustup component add rustc-dev + +To get diagnostics from the compiler, configure `rustc_interface::Config` to output diagnostic to a buffer, and run `TyCtxt.analysis`: + +```rust +#![feature(rustc_private)] + +extern crate rustc; +extern crate rustc_error_codes; +extern crate rustc_errors; +extern crate rustc_hash; +extern crate rustc_hir; +extern crate rustc_interface; +extern crate rustc_session; +extern crate rustc_span; + +use rustc_errors::registry; +use rustc_session::config; +use rustc_span::source_map; +use std::io; +use std::path; +use std::process; +use std::str; +use std::sync; + +// Buffer diagnostics in a Vec. +#[derive(Clone)] +pub struct DiagnosticSink(sync::Arc>>); + +impl io::Write for DiagnosticSink { + fn write(&mut self, buf: &[u8]) -> io::Result { + self.0.lock().unwrap().write(buf) + } + fn flush(&mut self) -> io::Result<()> { + self.0.lock().unwrap().flush() + } +} + +fn main() { + let out = process::Command::new("rustc") + .arg("--print=sysroot") + .current_dir(".") + .output() + .unwrap(); + let sysroot = str::from_utf8(&out.stdout).unwrap().trim(); + let buffer = sync::Arc::new(sync::Mutex::new(Vec::new())); + let config = rustc_interface::Config { + opts: config::Options { + maybe_sysroot: Some(path::PathBuf::from(sysroot)), + // Configure the compiler to emit diagnostics in compact JSON format. + error_format: config::ErrorOutputType::Json { + pretty: false, + json_rendered: rustc_errors::emitter::HumanReadableErrorType::Default( + rustc_errors::emitter::ColorConfig::Never, + ), + }, + ..config::Options::default() + }, + // This program contains a type error. + input: config::Input::Str { + name: source_map::FileName::Custom("main.rs".to_string()), + input: "fn main() { let x: &str = 1; }".to_string(), + }, + // Redirect the diagnostic output of the compiler to a buffer. + diagnostic_output: rustc_session::DiagnosticOutput::Raw(Box::from(DiagnosticSink( + buffer.clone(), + ))), + crate_cfg: rustc_hash::FxHashSet::default(), + input_path: None, + output_dir: None, + output_file: None, + file_loader: None, + stderr: None, + crate_name: None, + lint_caps: rustc_hash::FxHashMap::default(), + register_lints: None, + override_queries: None, + registry: registry::Registry::new(&rustc_error_codes::DIAGNOSTICS), + }; + rustc_interface::run_compiler(config, |compiler| { + compiler.enter(|queries| { + queries.global_ctxt().unwrap().take().enter(|tcx| { + // Run the analysis phase on the local crate to trigger the type error. + tcx.analysis(rustc_hir::def_id::LOCAL_CRATE); + }); + }); + }); + // Read and print buffered diagnostics. + let diagnostics = String::from_utf8(buffer.lock().unwrap().clone()).unwrap(); + println!("{}", diagnostics); +} + +```