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
27pub 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 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
172pub 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}