Skip to main content

flux_errors/
lib.rs

1#![feature(rustc_private)]
2
3extern crate rustc_data_structures;
4extern crate rustc_errors;
5extern crate rustc_session;
6extern crate rustc_span;
7
8use std::{cell::Cell, io, sync::Arc};
9
10use flux_common::result::{ErrorCollector, ErrorEmitter};
11use rustc_data_structures::sync;
12pub use rustc_errors::ErrorGuaranteed;
13use rustc_errors::{
14    Diagnostic, ErrCode, FatalError, TerminalUrl,
15    annotate_snippet_emitter_writer::AnnotateSnippetEmitter,
16    emitter::{Emitter, HumanReadableErrorType, OutputTheme, stderr_destination},
17    json::JsonEmitter,
18};
19use rustc_session::{config, parse::ParseSess};
20use rustc_span::source_map::SourceMap;
21
22pub struct FluxSession {
23    pub parse_sess: ParseSess,
24}
25
26// FIXME(nilehmann) We probably need to move out of this error reporting
27pub const E0999: ErrCode = ErrCode::from_u32(999);
28
29impl FluxSession {
30    pub fn new(opts: &config::Options, source_map: Arc<SourceMap>) -> Self {
31        let emitter = emitter(opts, source_map.clone());
32        let dcx = rustc_errors::DiagCtxt::new(emitter);
33        Self { parse_sess: ParseSess::with_dcx(dcx, source_map) }
34    }
35
36    pub fn err_count(&self) -> usize {
37        self.parse_sess.dcx().err_count()
38    }
39
40    #[track_caller]
41    pub fn emit_err<'a>(&'a self, err: impl Diagnostic<'a>) -> ErrorGuaranteed {
42        self.parse_sess.dcx().emit_err(err)
43    }
44
45    #[track_caller]
46    pub fn emit_fatal<'a>(&'a self, fatal: impl Diagnostic<'a>) -> ! {
47        self.parse_sess.dcx().emit_fatal(fatal)
48    }
49
50    pub fn abort(&self, _: ErrorGuaranteed) -> ! {
51        self.parse_sess.dcx().abort_if_errors();
52        FatalError.raise()
53    }
54
55    pub fn abort_if_errors(&self) {
56        self.parse_sess.dcx().abort_if_errors();
57    }
58
59    pub fn finish_diagnostics(&self) {
60        self.parse_sess.dcx().print_error_count();
61        self.abort_if_errors();
62    }
63
64    pub fn dcx(&self) -> rustc_errors::DiagCtxtHandle<'_> {
65        self.parse_sess.dcx()
66    }
67}
68
69fn emitter(
70    sopts: &config::Options,
71    source_map: Arc<SourceMap>,
72) -> Box<dyn Emitter + sync::DynSend> {
73    // All the code below is copied from rustc_session::session::default_emitter
74    let macro_backtrace = sopts.unstable_opts.macro_backtrace;
75    let track_diagnostics = sopts.unstable_opts.track_diagnostics;
76    let terminal_url = match sopts.unstable_opts.terminal_urls {
77        TerminalUrl::Auto => {
78            match (std::env::var("COLORTERM").as_deref(), std::env::var("TERM").as_deref()) {
79                (Ok("truecolor"), Ok("xterm-256color"))
80                    if sopts.unstable_features.is_nightly_build() =>
81                {
82                    TerminalUrl::Yes
83                }
84                _ => TerminalUrl::No,
85            }
86        }
87        t => t,
88    };
89
90    let source_map = if sopts.unstable_opts.link_only { None } else { Some(source_map) };
91
92    match sopts.error_format {
93        config::ErrorOutputType::HumanReadable { kind, color_config } => {
94            match kind {
95                HumanReadableErrorType { short, unicode } => {
96                    let emitter = AnnotateSnippetEmitter::new(stderr_destination(color_config))
97                        .sm(source_map)
98                        .short_message(short)
99                        .diagnostic_width(sopts.diagnostic_width)
100                        .macro_backtrace(macro_backtrace)
101                        .track_diagnostics(track_diagnostics)
102                        .terminal_url(terminal_url)
103                        .theme(if unicode { OutputTheme::Unicode } else { OutputTheme::Ascii })
104                        .ignored_directories_in_source_blocks(
105                            sopts
106                                .unstable_opts
107                                .ignore_directory_in_diagnostics_source_blocks
108                                .clone(),
109                        );
110                    Box::new(emitter.ui_testing(sopts.unstable_opts.ui_testing))
111                }
112            }
113        }
114        config::ErrorOutputType::Json { pretty, json_rendered, color_config } => {
115            Box::new(
116                JsonEmitter::new(
117                    Box::new(io::BufWriter::new(io::stderr())),
118                    source_map,
119                    pretty,
120                    json_rendered,
121                    color_config,
122                )
123                .ui_testing(sopts.unstable_opts.ui_testing)
124                .ignored_directories_in_source_blocks(
125                    sopts
126                        .unstable_opts
127                        .ignore_directory_in_diagnostics_source_blocks
128                        .clone(),
129                )
130                .diagnostic_width(sopts.diagnostic_width)
131                .macro_backtrace(macro_backtrace)
132                .track_diagnostics(track_diagnostics)
133                .terminal_url(terminal_url),
134            )
135        }
136    }
137}
138
139impl ErrorEmitter for FluxSession {
140    fn emit<'a>(&'a self, err: impl Diagnostic<'a>) -> ErrorGuaranteed {
141        self.emit_err(err)
142    }
143}
144
145/// Convenience struct implementing [`ErrorEmitter`] and [`ErrorCollector`]
146pub struct Errors<'sess> {
147    sess: &'sess FluxSession,
148    err: Cell<Option<ErrorGuaranteed>>,
149}
150
151impl<'sess> Errors<'sess> {
152    pub fn new(sess: &'sess FluxSession) -> Self {
153        Self { sess, err: Cell::new(None) }
154    }
155
156    pub fn has_errors(&self) -> bool {
157        self.err.get().is_some()
158    }
159
160    #[track_caller]
161    pub fn emit<'a>(&'a self, err: impl Diagnostic<'a>) -> ErrorGuaranteed {
162        let err = self.sess.emit_err(err);
163        self.err.set(Some(err));
164        err
165    }
166
167    pub fn to_result(&self) -> Result<(), ErrorGuaranteed> {
168        if let Some(err) = self.err.get() { Err(err) } else { Ok(()) }
169    }
170}
171
172impl ErrorEmitter for Errors<'_> {
173    #[track_caller]
174    fn emit<'a>(&'a self, err: impl Diagnostic<'a>) -> ErrorGuaranteed {
175        Errors::emit(self, err)
176    }
177}
178
179impl ErrorCollector<ErrorGuaranteed> for Errors<'_> {
180    type Result = Result<(), ErrorGuaranteed>;
181
182    fn collect(&mut self, err: ErrorGuaranteed) {
183        *self.err.get_mut() = Some(err);
184    }
185
186    fn into_result(self) -> Self::Result {
187        Errors::to_result(&self)
188    }
189}