1use std::path::Path;
2use std::sync::Arc;
3use std::sync::atomic::{AtomicBool, Ordering};
4
5use rustc_data_structures::sync::IntoDynSyncSend;
6use rustc_errors::annotate_snippet_emitter_writer::AnnotateSnippetEmitter;
7use rustc_errors::emitter::{DynEmitter, Emitter, SilentEmitter, stderr_destination};
8use rustc_errors::registry::Registry;
9use rustc_errors::translation::Translator;
10use rustc_errors::{ColorConfig, Diag, DiagCtxt, DiagInner, Level as DiagnosticLevel};
11use rustc_session::parse::ParseSess as RawParseSess;
12use rustc_span::{
13 BytePos, Span,
14 source_map::{FilePathMapping, SourceMap},
15 symbol,
16};
17
18use crate::config::file_lines::LineRange;
19use crate::config::options::Color;
20use crate::ignore_path::IgnorePathSet;
21use crate::parse::parser::{ModError, ModulePathSuccess};
22use crate::source_map::LineRangeUtils;
23use crate::utils::starts_with_newline;
24use crate::visitor::SnippetProvider;
25use crate::{Config, ErrorKind, FileName};
26
27pub(crate) struct ParseSess {
29 raw_psess: RawParseSess,
30 ignore_path_set: Arc<IgnorePathSet>,
31 can_reset_errors: Arc<AtomicBool>,
32}
33
34struct SilentOnIgnoredFilesEmitter {
36 ignore_path_set: IntoDynSyncSend<Arc<IgnorePathSet>>,
37 source_map: Arc<SourceMap>,
38 emitter: Box<DynEmitter>,
39 has_non_ignorable_parser_errors: bool,
40 can_reset: Arc<AtomicBool>,
41}
42
43impl SilentOnIgnoredFilesEmitter {
44 fn handle_non_ignoreable_error(&mut self, diag: DiagInner, registry: &Registry) {
45 self.has_non_ignorable_parser_errors = true;
46 self.can_reset.store(false, Ordering::Release);
47 self.emitter.emit_diagnostic(diag, registry);
48 }
49}
50
51impl Emitter for SilentOnIgnoredFilesEmitter {
52 fn source_map(&self) -> Option<&SourceMap> {
53 None
54 }
55
56 fn emit_diagnostic(&mut self, diag: DiagInner, registry: &Registry) {
57 if diag.level() == DiagnosticLevel::Fatal {
58 return self.handle_non_ignoreable_error(diag, registry);
59 }
60 if let Some(primary_span) = &diag.span.primary_span() {
61 let file_name = self.source_map.span_to_filename(*primary_span);
62 if let rustc_span::FileName::Real(real) = file_name {
63 if let Some(path) = real.local_path() {
64 if self
65 .ignore_path_set
66 .is_match(&FileName::Real(path.to_path_buf()))
67 {
68 if !self.has_non_ignorable_parser_errors {
69 self.can_reset.store(true, Ordering::Release);
70 }
71 return;
72 }
73 }
74 }
75 }
76 self.handle_non_ignoreable_error(diag, registry);
77 }
78
79 fn translator(&self) -> &Translator {
80 self.emitter.translator()
81 }
82}
83
84impl From<Color> for ColorConfig {
85 fn from(color: Color) -> Self {
86 match color {
87 Color::Auto => ColorConfig::Auto,
88 Color::Always => ColorConfig::Always,
89 Color::Never => ColorConfig::Never,
90 }
91 }
92}
93
94fn default_dcx(
95 source_map: Arc<SourceMap>,
96 ignore_path_set: Arc<IgnorePathSet>,
97 can_reset: Arc<AtomicBool>,
98 show_parse_errors: bool,
99 color: Color,
100) -> DiagCtxt {
101 let supports_color = term::stderr().map_or(false, |term| term.supports_color());
102 let emit_color = if supports_color {
103 ColorConfig::from(color)
104 } else {
105 ColorConfig::Never
106 };
107
108 let translator = rustc_driver::default_translator();
109
110 let emitter: Box<DynEmitter> = if show_parse_errors {
111 Box::new(
112 AnnotateSnippetEmitter::new(stderr_destination(emit_color), translator)
113 .sm(Some(source_map.clone())),
114 )
115 } else {
116 Box::new(SilentEmitter { translator })
117 };
118 DiagCtxt::new(Box::new(SilentOnIgnoredFilesEmitter {
119 has_non_ignorable_parser_errors: false,
120 source_map,
121 emitter,
122 ignore_path_set: IntoDynSyncSend(ignore_path_set),
123 can_reset,
124 }))
125}
126
127impl ParseSess {
128 pub(crate) fn new(config: &Config) -> Result<ParseSess, ErrorKind> {
129 let ignore_path_set = match IgnorePathSet::from_ignore_list(&config.ignore()) {
130 Ok(ignore_path_set) => Arc::new(ignore_path_set),
131 Err(e) => return Err(ErrorKind::InvalidGlobPattern(e)),
132 };
133 let source_map = Arc::new(SourceMap::new(FilePathMapping::empty()));
134 let can_reset_errors = Arc::new(AtomicBool::new(false));
135
136 let dcx = default_dcx(
137 Arc::clone(&source_map),
138 Arc::clone(&ignore_path_set),
139 Arc::clone(&can_reset_errors),
140 config.show_parse_errors(),
141 config.color(),
142 );
143 let raw_psess = RawParseSess::with_dcx(dcx, source_map);
144
145 Ok(ParseSess {
146 raw_psess,
147 ignore_path_set,
148 can_reset_errors,
149 })
150 }
151
152 pub(crate) fn default_submod_path(
160 &self,
161 id: symbol::Ident,
162 relative: Option<symbol::Ident>,
163 dir_path: &Path,
164 ) -> Result<ModulePathSuccess, ModError<'_>> {
165 rustc_expand::module::default_submod_path(&self.raw_psess, id, relative, dir_path).or_else(
166 |e| {
167 if matches!(e, ModError::FileNotFound(..)) && relative.is_some() {
172 rustc_expand::module::default_submod_path(&self.raw_psess, id, None, dir_path)
173 .map_err(|_| e)
174 } else {
175 Err(e)
176 }
177 },
178 )
179 }
180
181 pub(crate) fn is_file_parsed(&self, path: &Path) -> bool {
182 self.raw_psess
183 .source_map()
184 .get_source_file(&rustc_span::FileName::Real(
185 self.raw_psess
186 .source_map()
187 .path_mapping()
188 .to_real_filename(self.raw_psess.source_map().working_dir(), path),
189 ))
190 .is_some()
191 }
192
193 pub(crate) fn ignore_file(&self, path: &FileName) -> bool {
194 self.ignore_path_set.as_ref().is_match(path)
195 }
196
197 pub(crate) fn set_silent_emitter(&mut self) {
198 self.raw_psess.dcx().make_silent();
199 }
200
201 pub(crate) fn span_to_filename(&self, span: Span) -> FileName {
202 self.raw_psess.source_map().span_to_filename(span).into()
203 }
204
205 pub(crate) fn span_to_file_contents(&self, span: Span) -> Arc<rustc_span::SourceFile> {
206 self.raw_psess
207 .source_map()
208 .lookup_source_file(span.data().lo)
209 }
210
211 pub(crate) fn span_to_first_line_string(&self, span: Span) -> String {
212 let file_lines = self.raw_psess.source_map().span_to_lines(span).ok();
213
214 match file_lines {
215 Some(fl) => fl
216 .file
217 .get_line(fl.lines[0].line_index)
218 .map_or_else(String::new, |s| s.to_string()),
219 None => String::new(),
220 }
221 }
222
223 pub(crate) fn line_of_byte_pos(&self, pos: BytePos) -> usize {
224 self.raw_psess.source_map().lookup_char_pos(pos).line
225 }
226
227 #[allow(dead_code)]
232 pub(crate) fn byte_pos_same_line(&self, a: BytePos, b: BytePos) -> bool {
233 self.line_of_byte_pos(a) == self.line_of_byte_pos(b)
234 }
235
236 pub(crate) fn span_to_debug_info(&self, span: Span) -> String {
237 self.raw_psess.source_map().span_to_diagnostic_string(span)
238 }
239
240 pub(crate) fn inner(&self) -> &RawParseSess {
241 &self.raw_psess
242 }
243
244 pub(crate) fn snippet_provider(&self, span: Span) -> SnippetProvider {
245 let source_file = self.raw_psess.source_map().lookup_char_pos(span.lo()).file;
246 SnippetProvider::new(
247 source_file.start_pos,
248 source_file.end_position(),
249 Arc::clone(source_file.src.as_ref().unwrap()),
250 )
251 }
252
253 pub(crate) fn get_original_snippet(&self, filename: &FileName) -> Option<Arc<String>> {
254 let rustc_filename = match filename {
255 FileName::Real(path) => rustc_span::FileName::Real(
256 self.raw_psess
257 .source_map()
258 .path_mapping()
259 .to_real_filename(self.raw_psess.source_map().working_dir(), path),
260 ),
261 FileName::Stdin => rustc_span::FileName::Custom("stdin".to_owned()),
262 };
263
264 self.raw_psess
265 .source_map()
266 .get_source_file(&rustc_filename)
267 .and_then(|source_file| source_file.src.clone())
268 }
269}
270
271impl ParseSess {
273 pub(super) fn emit_diagnostics(&self, diagnostics: Vec<Diag<'_>>) {
274 for diagnostic in diagnostics {
275 diagnostic.emit();
276 }
277 }
278
279 pub(super) fn can_reset_errors(&self) -> bool {
280 self.can_reset_errors.load(Ordering::Acquire)
281 }
282
283 pub(super) fn has_errors(&self) -> bool {
284 self.raw_psess.dcx().has_errors().is_some()
285 }
286
287 pub(super) fn reset_errors(&self) {
288 self.raw_psess.dcx().reset_err_count();
289 }
290}
291
292impl LineRangeUtils for ParseSess {
293 fn lookup_line_range(&self, span: Span) -> LineRange {
294 let snippet = self
295 .raw_psess
296 .source_map()
297 .span_to_snippet(span)
298 .unwrap_or_default();
299 let lo = self.raw_psess.source_map().lookup_line(span.lo()).unwrap();
300 let hi = self.raw_psess.source_map().lookup_line(span.hi()).unwrap();
301
302 debug_assert_eq!(
303 lo.sf.name, hi.sf.name,
304 "span crossed file boundary: lo: {lo:?}, hi: {hi:?}"
305 );
306
307 let offset = 1 + if starts_with_newline(&snippet) { 1 } else { 0 };
310 LineRange {
312 file: lo.sf.clone(),
313 lo: lo.line + offset,
314 hi: hi.line + offset,
315 }
316 }
317}
318
319#[cfg(test)]
320mod tests {
321 use super::*;
322
323 use rustfmt_config_proc_macro::nightly_only_test;
324
325 mod emitter {
326 use super::*;
327 use crate::config::IgnoreList;
328 use crate::utils::mk_sp;
329 use rustc_errors::MultiSpan;
330 use rustc_span::FileName as SourceMapFileName;
331 use std::path::PathBuf;
332 use std::sync::atomic::AtomicU32;
333
334 struct TestEmitter {
335 num_emitted_errors: Arc<AtomicU32>,
336 }
337
338 impl Emitter for TestEmitter {
339 fn source_map(&self) -> Option<&SourceMap> {
340 None
341 }
342
343 fn emit_diagnostic(&mut self, _diag: DiagInner, _registry: &Registry) {
344 self.num_emitted_errors.fetch_add(1, Ordering::Release);
345 }
346
347 fn translator(&self) -> &Translator {
348 panic!("test emitter attempted to translate a diagnostic");
349 }
350 }
351
352 fn build_diagnostic(level: DiagnosticLevel, span: Option<MultiSpan>) -> DiagInner {
353 let mut diag = DiagInner::new(level, "");
354 diag.messages.clear();
355 if let Some(span) = span {
356 diag.span = span;
357 }
358 diag
359 }
360
361 fn build_emitter(
362 num_emitted_errors: Arc<AtomicU32>,
363 can_reset: Arc<AtomicBool>,
364 source_map: Option<Arc<SourceMap>>,
365 ignore_list: Option<IgnoreList>,
366 ) -> SilentOnIgnoredFilesEmitter {
367 let emitter_writer = TestEmitter { num_emitted_errors };
368 let source_map =
369 source_map.unwrap_or_else(|| Arc::new(SourceMap::new(FilePathMapping::empty())));
370 let ignore_path_set = Arc::new(
371 IgnorePathSet::from_ignore_list(&ignore_list.unwrap_or_default()).unwrap(),
372 );
373 SilentOnIgnoredFilesEmitter {
374 has_non_ignorable_parser_errors: false,
375 source_map,
376 emitter: Box::new(emitter_writer),
377 ignore_path_set: IntoDynSyncSend(ignore_path_set),
378 can_reset,
379 }
380 }
381
382 fn get_ignore_list(config: &str) -> IgnoreList {
383 Config::from_toml(config, Path::new("./rustfmt.toml"))
384 .unwrap()
385 .ignore()
386 }
387
388 fn filename(sm: &SourceMap, path: &str) -> SourceMapFileName {
389 SourceMapFileName::Real(
390 sm.path_mapping()
391 .to_real_filename(sm.working_dir(), PathBuf::from(path)),
392 )
393 }
394
395 #[test]
396 fn handles_fatal_parse_error_in_ignored_file() {
397 let num_emitted_errors = Arc::new(AtomicU32::new(0));
398 let can_reset_errors = Arc::new(AtomicBool::new(false));
399 let ignore_list = get_ignore_list(r#"ignore = ["foo.rs"]"#);
400 let source_map = Arc::new(SourceMap::new(FilePathMapping::empty()));
401 let source =
402 String::from(r#"extern "system" fn jni_symbol!( funcName ) ( ... ) -> {} "#);
403 source_map.new_source_file(filename(&source_map, "foo.rs"), source);
404 let registry = Registry::new(&[]);
405 let mut emitter = build_emitter(
406 Arc::clone(&num_emitted_errors),
407 Arc::clone(&can_reset_errors),
408 Some(Arc::clone(&source_map)),
409 Some(ignore_list),
410 );
411 let span = MultiSpan::from_span(mk_sp(BytePos(0), BytePos(1)));
412 let fatal_diagnostic = build_diagnostic(DiagnosticLevel::Fatal, Some(span));
413 emitter.emit_diagnostic(fatal_diagnostic, ®istry);
414 assert_eq!(num_emitted_errors.load(Ordering::Acquire), 1);
415 assert_eq!(can_reset_errors.load(Ordering::Acquire), false);
416 }
417
418 #[nightly_only_test]
419 #[test]
420 fn handles_recoverable_parse_error_in_ignored_file() {
421 let num_emitted_errors = Arc::new(AtomicU32::new(0));
422 let can_reset_errors = Arc::new(AtomicBool::new(false));
423 let ignore_list = get_ignore_list(r#"ignore = ["foo.rs"]"#);
424 let source_map = Arc::new(SourceMap::new(FilePathMapping::empty()));
425 let source = String::from(r#"pub fn bar() { 1x; }"#);
426 source_map.new_source_file(filename(&source_map, "foo.rs"), source);
427 let registry = Registry::new(&[]);
428 let mut emitter = build_emitter(
429 Arc::clone(&num_emitted_errors),
430 Arc::clone(&can_reset_errors),
431 Some(Arc::clone(&source_map)),
432 Some(ignore_list),
433 );
434 let span = MultiSpan::from_span(mk_sp(BytePos(0), BytePos(1)));
435 let non_fatal_diagnostic = build_diagnostic(DiagnosticLevel::Warning, Some(span));
436 emitter.emit_diagnostic(non_fatal_diagnostic, ®istry);
437 assert_eq!(num_emitted_errors.load(Ordering::Acquire), 0);
438 assert_eq!(can_reset_errors.load(Ordering::Acquire), true);
439 }
440
441 #[nightly_only_test]
442 #[test]
443 fn handles_recoverable_parse_error_in_non_ignored_file() {
444 let num_emitted_errors = Arc::new(AtomicU32::new(0));
445 let can_reset_errors = Arc::new(AtomicBool::new(false));
446 let source_map = Arc::new(SourceMap::new(FilePathMapping::empty()));
447 let source = String::from(r#"pub fn bar() { 1x; }"#);
448 source_map.new_source_file(filename(&source_map, "foo.rs"), source);
449 let registry = Registry::new(&[]);
450 let mut emitter = build_emitter(
451 Arc::clone(&num_emitted_errors),
452 Arc::clone(&can_reset_errors),
453 Some(Arc::clone(&source_map)),
454 None,
455 );
456 let span = MultiSpan::from_span(mk_sp(BytePos(0), BytePos(1)));
457 let non_fatal_diagnostic = build_diagnostic(DiagnosticLevel::Warning, Some(span));
458 emitter.emit_diagnostic(non_fatal_diagnostic, ®istry);
459 assert_eq!(num_emitted_errors.load(Ordering::Acquire), 1);
460 assert_eq!(can_reset_errors.load(Ordering::Acquire), false);
461 }
462
463 #[nightly_only_test]
464 #[test]
465 fn handles_mix_of_recoverable_parse_error() {
466 let num_emitted_errors = Arc::new(AtomicU32::new(0));
467 let can_reset_errors = Arc::new(AtomicBool::new(false));
468 let source_map = Arc::new(SourceMap::new(FilePathMapping::empty()));
469 let ignore_list = get_ignore_list(r#"ignore = ["foo.rs"]"#);
470 let bar_source = String::from(r#"pub fn bar() { 1x; }"#);
471 let foo_source = String::from(r#"pub fn foo() { 1x; }"#);
472 let fatal_source =
473 String::from(r#"extern "system" fn jni_symbol!( funcName ) ( ... ) -> {} "#);
474 source_map.new_source_file(filename(&source_map, "bar.rs"), bar_source);
475 source_map.new_source_file(filename(&source_map, "foo.rs"), foo_source);
476 source_map.new_source_file(filename(&source_map, "fatal.rs"), fatal_source);
477 let registry = Registry::new(&[]);
478 let mut emitter = build_emitter(
479 Arc::clone(&num_emitted_errors),
480 Arc::clone(&can_reset_errors),
481 Some(Arc::clone(&source_map)),
482 Some(ignore_list),
483 );
484 let bar_span = MultiSpan::from_span(mk_sp(BytePos(0), BytePos(1)));
485 let foo_span = MultiSpan::from_span(mk_sp(BytePos(21), BytePos(22)));
486 let bar_diagnostic = build_diagnostic(DiagnosticLevel::Warning, Some(bar_span));
487 let foo_diagnostic = build_diagnostic(DiagnosticLevel::Warning, Some(foo_span));
488 let fatal_diagnostic = build_diagnostic(DiagnosticLevel::Fatal, None);
489 emitter.emit_diagnostic(bar_diagnostic, ®istry);
490 emitter.emit_diagnostic(foo_diagnostic, ®istry);
491 emitter.emit_diagnostic(fatal_diagnostic, ®istry);
492 assert_eq!(num_emitted_errors.load(Ordering::Acquire), 2);
493 assert_eq!(can_reset_errors.load(Ordering::Acquire), false);
494 }
495 }
496}