flux_errors/
lib.rs

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