1use std::path::Path;
2use std::sync::Arc;
3use std::sync::atomic::{AtomicBool, Ordering};
4
5use rustc_data_structures::sync::IntoDynSyncSend;
6use rustc_errors::emitter::{DynEmitter, Emitter, HumanEmitter, SilentEmitter, stderr_destination};
7use rustc_errors::registry::Registry;
8use rustc_errors::translation::Translate;
9use rustc_errors::{ColorConfig, Diag, DiagCtxt, DiagInner, Level as DiagnosticLevel};
10use rustc_session::parse::ParseSess as RawParseSess;
11use rustc_span::{
12 BytePos, Span,
13 source_map::{FilePathMapping, SourceMap},
14 symbol,
15};
16
17use crate::config::file_lines::LineRange;
18use crate::config::options::Color;
19use crate::ignore_path::IgnorePathSet;
20use crate::parse::parser::{ModError, ModulePathSuccess};
21use crate::source_map::LineRangeUtils;
22use crate::utils::starts_with_newline;
23use crate::visitor::SnippetProvider;
24use crate::{Config, ErrorKind, FileName};
25
26pub(crate) struct ParseSess {
28 raw_psess: RawParseSess,
29 ignore_path_set: Arc<IgnorePathSet>,
30 can_reset_errors: Arc<AtomicBool>,
31}
32
33struct SilentOnIgnoredFilesEmitter {
35 ignore_path_set: IntoDynSyncSend<Arc<IgnorePathSet>>,
36 source_map: Arc<SourceMap>,
37 emitter: Box<DynEmitter>,
38 has_non_ignorable_parser_errors: bool,
39 can_reset: Arc<AtomicBool>,
40}
41
42impl SilentOnIgnoredFilesEmitter {
43 fn handle_non_ignoreable_error(&mut self, diag: DiagInner, registry: &Registry) {
44 self.has_non_ignorable_parser_errors = true;
45 self.can_reset.store(false, Ordering::Release);
46 self.emitter.emit_diagnostic(diag, registry);
47 }
48}
49
50impl Translate for SilentOnIgnoredFilesEmitter {
51 fn fluent_bundle(&self) -> Option<&rustc_errors::FluentBundle> {
52 self.emitter.fluent_bundle()
53 }
54
55 fn fallback_fluent_bundle(&self) -> &rustc_errors::FluentBundle {
56 self.emitter.fallback_fluent_bundle()
57 }
58}
59
60impl Emitter for SilentOnIgnoredFilesEmitter {
61 fn source_map(&self) -> Option<&SourceMap> {
62 None
63 }
64
65 fn emit_diagnostic(&mut self, diag: DiagInner, registry: &Registry) {
66 if diag.level() == DiagnosticLevel::Fatal {
67 return self.handle_non_ignoreable_error(diag, registry);
68 }
69 if let Some(primary_span) = &diag.span.primary_span() {
70 let file_name = self.source_map.span_to_filename(*primary_span);
71 if let rustc_span::FileName::Real(rustc_span::RealFileName::LocalPath(ref path)) =
72 file_name
73 {
74 if self
75 .ignore_path_set
76 .is_match(&FileName::Real(path.to_path_buf()))
77 {
78 if !self.has_non_ignorable_parser_errors {
79 self.can_reset.store(true, Ordering::Release);
80 }
81 return;
82 }
83 };
84 }
85 self.handle_non_ignoreable_error(diag, registry);
86 }
87}
88
89impl From<Color> for ColorConfig {
90 fn from(color: Color) -> Self {
91 match color {
92 Color::Auto => ColorConfig::Auto,
93 Color::Always => ColorConfig::Always,
94 Color::Never => ColorConfig::Never,
95 }
96 }
97}
98
99fn default_dcx(
100 source_map: Arc<SourceMap>,
101 ignore_path_set: Arc<IgnorePathSet>,
102 can_reset: Arc<AtomicBool>,
103 show_parse_errors: bool,
104 color: Color,
105) -> DiagCtxt {
106 let supports_color = term::stderr().map_or(false, |term| term.supports_color());
107 let emit_color = if supports_color {
108 ColorConfig::from(color)
109 } else {
110 ColorConfig::Never
111 };
112
113 let fallback_bundle = rustc_errors::fallback_fluent_bundle(
114 rustc_driver::DEFAULT_LOCALE_RESOURCES.to_vec(),
115 false,
116 );
117 let emitter = Box::new(
118 HumanEmitter::new(stderr_destination(emit_color), fallback_bundle)
119 .sm(Some(source_map.clone())),
120 );
121
122 let emitter: Box<DynEmitter> = if !show_parse_errors {
123 Box::new(SilentEmitter {
124 fatal_emitter: emitter,
125 fatal_note: None,
126 emit_fatal_diagnostic: false,
127 })
128 } else {
129 emitter
130 };
131 DiagCtxt::new(Box::new(SilentOnIgnoredFilesEmitter {
132 has_non_ignorable_parser_errors: false,
133 source_map,
134 emitter,
135 ignore_path_set: IntoDynSyncSend(ignore_path_set),
136 can_reset,
137 }))
138}
139
140impl ParseSess {
141 pub(crate) fn new(config: &Config) -> Result<ParseSess, ErrorKind> {
142 let ignore_path_set = match IgnorePathSet::from_ignore_list(&config.ignore()) {
143 Ok(ignore_path_set) => Arc::new(ignore_path_set),
144 Err(e) => return Err(ErrorKind::InvalidGlobPattern(e)),
145 };
146 let source_map = Arc::new(SourceMap::new(FilePathMapping::empty()));
147 let can_reset_errors = Arc::new(AtomicBool::new(false));
148
149 let dcx = default_dcx(
150 Arc::clone(&source_map),
151 Arc::clone(&ignore_path_set),
152 Arc::clone(&can_reset_errors),
153 config.show_parse_errors(),
154 config.color(),
155 );
156 let raw_psess = RawParseSess::with_dcx(dcx, source_map);
157
158 Ok(ParseSess {
159 raw_psess,
160 ignore_path_set,
161 can_reset_errors,
162 })
163 }
164
165 pub(crate) fn default_submod_path(
173 &self,
174 id: symbol::Ident,
175 relative: Option<symbol::Ident>,
176 dir_path: &Path,
177 ) -> Result<ModulePathSuccess, ModError<'_>> {
178 rustc_expand::module::default_submod_path(&self.raw_psess, id, relative, dir_path).or_else(
179 |e| {
180 if matches!(e, ModError::FileNotFound(..)) && relative.is_some() {
185 rustc_expand::module::default_submod_path(&self.raw_psess, id, None, dir_path)
186 .map_err(|_| e)
187 } else {
188 Err(e)
189 }
190 },
191 )
192 }
193
194 pub(crate) fn is_file_parsed(&self, path: &Path) -> bool {
195 self.raw_psess
196 .source_map()
197 .get_source_file(&rustc_span::FileName::Real(
198 rustc_span::RealFileName::LocalPath(path.to_path_buf()),
199 ))
200 .is_some()
201 }
202
203 pub(crate) fn ignore_file(&self, path: &FileName) -> bool {
204 self.ignore_path_set.as_ref().is_match(path)
205 }
206
207 pub(crate) fn set_silent_emitter(&mut self) {
208 self.raw_psess.dcx().make_silent(None, false);
209 }
210
211 pub(crate) fn span_to_filename(&self, span: Span) -> FileName {
212 self.raw_psess.source_map().span_to_filename(span).into()
213 }
214
215 pub(crate) fn span_to_file_contents(&self, span: Span) -> Arc<rustc_span::SourceFile> {
216 self.raw_psess
217 .source_map()
218 .lookup_source_file(span.data().lo)
219 }
220
221 pub(crate) fn span_to_first_line_string(&self, span: Span) -> String {
222 let file_lines = self.raw_psess.source_map().span_to_lines(span).ok();
223
224 match file_lines {
225 Some(fl) => fl
226 .file
227 .get_line(fl.lines[0].line_index)
228 .map_or_else(String::new, |s| s.to_string()),
229 None => String::new(),
230 }
231 }
232
233 pub(crate) fn line_of_byte_pos(&self, pos: BytePos) -> usize {
234 self.raw_psess.source_map().lookup_char_pos(pos).line
235 }
236
237 #[allow(dead_code)]
242 pub(crate) fn byte_pos_same_line(&self, a: BytePos, b: BytePos) -> bool {
243 self.line_of_byte_pos(a) == self.line_of_byte_pos(b)
244 }
245
246 pub(crate) fn span_to_debug_info(&self, span: Span) -> String {
247 self.raw_psess.source_map().span_to_diagnostic_string(span)
248 }
249
250 pub(crate) fn inner(&self) -> &RawParseSess {
251 &self.raw_psess
252 }
253
254 pub(crate) fn snippet_provider(&self, span: Span) -> SnippetProvider {
255 let source_file = self.raw_psess.source_map().lookup_char_pos(span.lo()).file;
256 SnippetProvider::new(
257 source_file.start_pos,
258 source_file.end_position(),
259 Arc::clone(source_file.src.as_ref().unwrap()),
260 )
261 }
262
263 pub(crate) fn get_original_snippet(&self, file_name: &FileName) -> Option<Arc<String>> {
264 self.raw_psess
265 .source_map()
266 .get_source_file(&file_name.into())
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, RealFileName};
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 Translate for TestEmitter {
339 fn fluent_bundle(&self) -> Option<&rustc_errors::FluentBundle> {
340 None
341 }
342
343 fn fallback_fluent_bundle(&self) -> &rustc_errors::FluentBundle {
344 panic!("test emitter attempted to translate a diagnostic");
345 }
346 }
347
348 impl Emitter for TestEmitter {
349 fn source_map(&self) -> Option<&SourceMap> {
350 None
351 }
352
353 fn emit_diagnostic(&mut self, _diag: DiagInner, _registry: &Registry) {
354 self.num_emitted_errors.fetch_add(1, Ordering::Release);
355 }
356 }
357
358 fn build_diagnostic(level: DiagnosticLevel, span: Option<MultiSpan>) -> DiagInner {
359 #[allow(rustc::untranslatable_diagnostic)] let mut diag = DiagInner::new(level, "");
361 diag.messages.clear();
362 if let Some(span) = span {
363 diag.span = span;
364 }
365 diag
366 }
367
368 fn build_emitter(
369 num_emitted_errors: Arc<AtomicU32>,
370 can_reset: Arc<AtomicBool>,
371 source_map: Option<Arc<SourceMap>>,
372 ignore_list: Option<IgnoreList>,
373 ) -> SilentOnIgnoredFilesEmitter {
374 let emitter_writer = TestEmitter { num_emitted_errors };
375 let source_map =
376 source_map.unwrap_or_else(|| Arc::new(SourceMap::new(FilePathMapping::empty())));
377 let ignore_path_set = Arc::new(
378 IgnorePathSet::from_ignore_list(&ignore_list.unwrap_or_default()).unwrap(),
379 );
380 SilentOnIgnoredFilesEmitter {
381 has_non_ignorable_parser_errors: false,
382 source_map,
383 emitter: Box::new(emitter_writer),
384 ignore_path_set: IntoDynSyncSend(ignore_path_set),
385 can_reset,
386 }
387 }
388
389 fn get_ignore_list(config: &str) -> IgnoreList {
390 Config::from_toml(config, Path::new("./rustfmt.toml"))
391 .unwrap()
392 .ignore()
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(
404 SourceMapFileName::Real(RealFileName::LocalPath(PathBuf::from("foo.rs"))),
405 source,
406 );
407 let registry = Registry::new(&[]);
408 let mut emitter = build_emitter(
409 Arc::clone(&num_emitted_errors),
410 Arc::clone(&can_reset_errors),
411 Some(Arc::clone(&source_map)),
412 Some(ignore_list),
413 );
414 let span = MultiSpan::from_span(mk_sp(BytePos(0), BytePos(1)));
415 let fatal_diagnostic = build_diagnostic(DiagnosticLevel::Fatal, Some(span));
416 emitter.emit_diagnostic(fatal_diagnostic, ®istry);
417 assert_eq!(num_emitted_errors.load(Ordering::Acquire), 1);
418 assert_eq!(can_reset_errors.load(Ordering::Acquire), false);
419 }
420
421 #[nightly_only_test]
422 #[test]
423 fn handles_recoverable_parse_error_in_ignored_file() {
424 let num_emitted_errors = Arc::new(AtomicU32::new(0));
425 let can_reset_errors = Arc::new(AtomicBool::new(false));
426 let ignore_list = get_ignore_list(r#"ignore = ["foo.rs"]"#);
427 let source_map = Arc::new(SourceMap::new(FilePathMapping::empty()));
428 let source = String::from(r#"pub fn bar() { 1x; }"#);
429 source_map.new_source_file(
430 SourceMapFileName::Real(RealFileName::LocalPath(PathBuf::from("foo.rs"))),
431 source,
432 );
433 let registry = Registry::new(&[]);
434 let mut emitter = build_emitter(
435 Arc::clone(&num_emitted_errors),
436 Arc::clone(&can_reset_errors),
437 Some(Arc::clone(&source_map)),
438 Some(ignore_list),
439 );
440 let span = MultiSpan::from_span(mk_sp(BytePos(0), BytePos(1)));
441 let non_fatal_diagnostic = build_diagnostic(DiagnosticLevel::Warning, Some(span));
442 emitter.emit_diagnostic(non_fatal_diagnostic, ®istry);
443 assert_eq!(num_emitted_errors.load(Ordering::Acquire), 0);
444 assert_eq!(can_reset_errors.load(Ordering::Acquire), true);
445 }
446
447 #[nightly_only_test]
448 #[test]
449 fn handles_recoverable_parse_error_in_non_ignored_file() {
450 let num_emitted_errors = Arc::new(AtomicU32::new(0));
451 let can_reset_errors = Arc::new(AtomicBool::new(false));
452 let source_map = Arc::new(SourceMap::new(FilePathMapping::empty()));
453 let source = String::from(r#"pub fn bar() { 1x; }"#);
454 source_map.new_source_file(
455 SourceMapFileName::Real(RealFileName::LocalPath(PathBuf::from("foo.rs"))),
456 source,
457 );
458 let registry = Registry::new(&[]);
459 let mut emitter = build_emitter(
460 Arc::clone(&num_emitted_errors),
461 Arc::clone(&can_reset_errors),
462 Some(Arc::clone(&source_map)),
463 None,
464 );
465 let span = MultiSpan::from_span(mk_sp(BytePos(0), BytePos(1)));
466 let non_fatal_diagnostic = build_diagnostic(DiagnosticLevel::Warning, Some(span));
467 emitter.emit_diagnostic(non_fatal_diagnostic, ®istry);
468 assert_eq!(num_emitted_errors.load(Ordering::Acquire), 1);
469 assert_eq!(can_reset_errors.load(Ordering::Acquire), false);
470 }
471
472 #[nightly_only_test]
473 #[test]
474 fn handles_mix_of_recoverable_parse_error() {
475 let num_emitted_errors = Arc::new(AtomicU32::new(0));
476 let can_reset_errors = Arc::new(AtomicBool::new(false));
477 let source_map = Arc::new(SourceMap::new(FilePathMapping::empty()));
478 let ignore_list = get_ignore_list(r#"ignore = ["foo.rs"]"#);
479 let bar_source = String::from(r#"pub fn bar() { 1x; }"#);
480 let foo_source = String::from(r#"pub fn foo() { 1x; }"#);
481 let fatal_source =
482 String::from(r#"extern "system" fn jni_symbol!( funcName ) ( ... ) -> {} "#);
483 source_map.new_source_file(
484 SourceMapFileName::Real(RealFileName::LocalPath(PathBuf::from("bar.rs"))),
485 bar_source,
486 );
487 source_map.new_source_file(
488 SourceMapFileName::Real(RealFileName::LocalPath(PathBuf::from("foo.rs"))),
489 foo_source,
490 );
491 source_map.new_source_file(
492 SourceMapFileName::Real(RealFileName::LocalPath(PathBuf::from("fatal.rs"))),
493 fatal_source,
494 );
495 let registry = Registry::new(&[]);
496 let mut emitter = build_emitter(
497 Arc::clone(&num_emitted_errors),
498 Arc::clone(&can_reset_errors),
499 Some(Arc::clone(&source_map)),
500 Some(ignore_list),
501 );
502 let bar_span = MultiSpan::from_span(mk_sp(BytePos(0), BytePos(1)));
503 let foo_span = MultiSpan::from_span(mk_sp(BytePos(21), BytePos(22)));
504 let bar_diagnostic = build_diagnostic(DiagnosticLevel::Warning, Some(bar_span));
505 let foo_diagnostic = build_diagnostic(DiagnosticLevel::Warning, Some(foo_span));
506 let fatal_diagnostic = build_diagnostic(DiagnosticLevel::Fatal, None);
507 emitter.emit_diagnostic(bar_diagnostic, ®istry);
508 emitter.emit_diagnostic(foo_diagnostic, ®istry);
509 emitter.emit_diagnostic(fatal_diagnostic, ®istry);
510 assert_eq!(num_emitted_errors.load(Ordering::Acquire), 2);
511 assert_eq!(can_reset_errors.load(Ordering::Acquire), false);
512 }
513 }
514}