Skip to main content

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, 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 { 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            }
122        }
123        config::ErrorOutputType::Json { pretty, json_rendered, color_config } => {
124            Box::new(
125                JsonEmitter::new(
126                    Box::new(io::BufWriter::new(io::stderr())),
127                    source_map,
128                    translator,
129                    pretty,
130                    json_rendered,
131                    color_config,
132                )
133                .ui_testing(sopts.unstable_opts.ui_testing)
134                .ignored_directories_in_source_blocks(
135                    sopts
136                        .unstable_opts
137                        .ignore_directory_in_diagnostics_source_blocks
138                        .clone(),
139                )
140                .diagnostic_width(sopts.diagnostic_width)
141                .macro_backtrace(macro_backtrace)
142                .track_diagnostics(track_diagnostics)
143                .terminal_url(terminal_url),
144            )
145        }
146    }
147}
148
149impl ErrorEmitter for FluxSession {
150    fn emit<'a>(&'a self, err: impl Diagnostic<'a>) -> ErrorGuaranteed {
151        self.emit_err(err)
152    }
153}
154
155/// Convenience struct implementing [`ErrorEmitter`] and [`ErrorCollector`]
156pub struct Errors<'sess> {
157    sess: &'sess FluxSession,
158    err: Cell<Option<ErrorGuaranteed>>,
159}
160
161impl<'sess> Errors<'sess> {
162    pub fn new(sess: &'sess FluxSession) -> Self {
163        Self { sess, err: Cell::new(None) }
164    }
165
166    pub fn has_errors(&self) -> bool {
167        self.err.get().is_some()
168    }
169
170    #[track_caller]
171    pub fn emit<'a>(&'a self, err: impl Diagnostic<'a>) -> ErrorGuaranteed {
172        let err = self.sess.emit_err(err);
173        self.err.set(Some(err));
174        err
175    }
176
177    pub fn to_result(&self) -> Result<(), ErrorGuaranteed> {
178        if let Some(err) = self.err.get() { Err(err) } else { Ok(()) }
179    }
180}
181
182impl ErrorEmitter for Errors<'_> {
183    #[track_caller]
184    fn emit<'a>(&'a self, err: impl Diagnostic<'a>) -> ErrorGuaranteed {
185        Errors::emit(self, err)
186    }
187}
188
189impl ErrorCollector<ErrorGuaranteed> for Errors<'_> {
190    type Result = Result<(), ErrorGuaranteed>;
191
192    fn collect(&mut self, err: ErrorGuaranteed) {
193        *self.err.get_mut() = Some(err);
194    }
195
196    fn into_result(self) -> Self::Result {
197        Errors::to_result(&self)
198    }
199}