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