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
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 { 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
155pub 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}