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::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) {
44 self.has_non_ignorable_parser_errors = true;
45 self.can_reset.store(false, Ordering::Release);
46 self.emitter.emit_diagnostic(diag);
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) {
56 if diag.level() == DiagnosticLevel::Fatal {
57 return self.handle_non_ignoreable_error(diag);
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(real) = file_name {
62 if let Some(path) = real.local_path() {
63 if self
64 .ignore_path_set
65 .is_match(&FileName::Real(path.to_path_buf()))
66 {
67 if !self.has_non_ignorable_parser_errors {
68 self.can_reset.store(true, Ordering::Release);
69 }
70 return;
71 }
72 }
73 }
74 }
75 self.handle_non_ignoreable_error(diag);
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 AnnotateSnippetEmitter::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 self.raw_psess
185 .source_map()
186 .path_mapping()
187 .to_real_filename(self.raw_psess.source_map().working_dir(), path),
188 ))
189 .is_some()
190 }
191
192 pub(crate) fn ignore_file(&self, path: &FileName) -> bool {
193 self.ignore_path_set.as_ref().is_match(path)
194 }
195
196 pub(crate) fn set_silent_emitter(&mut self) {
197 self.raw_psess.dcx().make_silent();
198 }
199
200 pub(crate) fn span_to_filename(&self, span: Span) -> FileName {
201 self.raw_psess.source_map().span_to_filename(span).into()
202 }
203
204 pub(crate) fn span_to_file_contents(&self, span: Span) -> Arc<rustc_span::SourceFile> {
205 self.raw_psess
206 .source_map()
207 .lookup_source_file(span.data().lo)
208 }
209
210 pub(crate) fn span_to_first_line_string(&self, span: Span) -> String {
211 let file_lines = self.raw_psess.source_map().span_to_lines(span).ok();
212
213 match file_lines {
214 Some(fl) => fl
215 .file
216 .get_line(fl.lines[0].line_index)
217 .map_or_else(String::new, |s| s.to_string()),
218 None => String::new(),
219 }
220 }
221
222 pub(crate) fn line_of_byte_pos(&self, pos: BytePos) -> usize {
223 self.raw_psess.source_map().lookup_char_pos(pos).line
224 }
225
226 #[allow(dead_code)]
231 pub(crate) fn byte_pos_same_line(&self, a: BytePos, b: BytePos) -> bool {
232 self.line_of_byte_pos(a) == self.line_of_byte_pos(b)
233 }
234
235 pub(crate) fn span_to_debug_info(&self, span: Span) -> String {
236 self.raw_psess.source_map().span_to_diagnostic_string(span)
237 }
238
239 pub(crate) fn inner(&self) -> &RawParseSess {
240 &self.raw_psess
241 }
242
243 pub(crate) fn snippet_provider(&self, span: Span) -> SnippetProvider {
244 let source_file = self.raw_psess.source_map().lookup_char_pos(span.lo()).file;
245 SnippetProvider::new(
246 source_file.start_pos,
247 source_file.end_position(),
248 Arc::clone(source_file.src.as_ref().unwrap()),
249 )
250 }
251
252 pub(crate) fn get_original_snippet(&self, filename: &FileName) -> Option<Arc<String>> {
253 let rustc_filename = match filename {
254 FileName::Real(path) => rustc_span::FileName::Real(
255 self.raw_psess
256 .source_map()
257 .path_mapping()
258 .to_real_filename(self.raw_psess.source_map().working_dir(), path),
259 ),
260 FileName::Stdin => rustc_span::FileName::Custom("stdin".to_owned()),
261 };
262
263 self.raw_psess
264 .source_map()
265 .get_source_file(&rustc_filename)
266 .and_then(|source_file| source_file.src.clone())
267 }
268}
269
270impl ParseSess {
272 pub(super) fn emit_diagnostics(&self, diagnostics: Vec<Diag<'_>>) {
273 for diagnostic in diagnostics {
274 diagnostic.emit();
275 }
276 }
277
278 pub(super) fn can_reset_errors(&self) -> bool {
279 self.can_reset_errors.load(Ordering::Acquire)
280 }
281
282 pub(super) fn has_errors(&self) -> bool {
283 self.raw_psess.dcx().has_errors().is_some()
284 }
285
286 pub(super) fn reset_errors(&self) {
287 self.raw_psess.dcx().reset_err_count();
288 }
289}
290
291impl LineRangeUtils for ParseSess {
292 fn lookup_line_range(&self, span: Span) -> LineRange {
293 let snippet = self
294 .raw_psess
295 .source_map()
296 .span_to_snippet(span)
297 .unwrap_or_default();
298 let lo = self.raw_psess.source_map().lookup_line(span.lo()).unwrap();
299 let hi = self.raw_psess.source_map().lookup_line(span.hi()).unwrap();
300
301 debug_assert_eq!(
302 lo.sf.name, hi.sf.name,
303 "span crossed file boundary: lo: {lo:?}, hi: {hi:?}"
304 );
305
306 let offset = 1 + if starts_with_newline(&snippet) { 1 } else { 0 };
309 LineRange {
311 file: lo.sf.clone(),
312 lo: lo.line + offset,
313 hi: hi.line + offset,
314 }
315 }
316}
317
318#[cfg(test)]
319mod tests {
320 use super::*;
321
322 use rustfmt_config_proc_macro::nightly_only_test;
323
324 mod emitter {
325 use super::*;
326 use crate::config::IgnoreList;
327 use crate::utils::mk_sp;
328 use rustc_errors::MultiSpan;
329 use rustc_span::FileName as SourceMapFileName;
330 use std::path::PathBuf;
331 use std::sync::atomic::AtomicU32;
332
333 struct TestEmitter {
334 num_emitted_errors: Arc<AtomicU32>,
335 }
336
337 impl Emitter for TestEmitter {
338 fn source_map(&self) -> Option<&SourceMap> {
339 None
340 }
341
342 fn emit_diagnostic(&mut self, _diag: DiagInner) {
343 self.num_emitted_errors.fetch_add(1, Ordering::Release);
344 }
345
346 fn translator(&self) -> &Translator {
347 panic!("test emitter attempted to translate a diagnostic");
348 }
349 }
350
351 fn build_diagnostic(level: DiagnosticLevel, span: Option<MultiSpan>) -> DiagInner {
352 let mut diag = DiagInner::new(level, "");
353 diag.messages.clear();
354 if let Some(span) = span {
355 diag.span = span;
356 }
357 diag
358 }
359
360 fn build_emitter(
361 num_emitted_errors: Arc<AtomicU32>,
362 can_reset: Arc<AtomicBool>,
363 source_map: Option<Arc<SourceMap>>,
364 ignore_list: Option<IgnoreList>,
365 ) -> SilentOnIgnoredFilesEmitter {
366 let emitter_writer = TestEmitter { num_emitted_errors };
367 let source_map =
368 source_map.unwrap_or_else(|| Arc::new(SourceMap::new(FilePathMapping::empty())));
369 let ignore_path_set = Arc::new(
370 IgnorePathSet::from_ignore_list(&ignore_list.unwrap_or_default()).unwrap(),
371 );
372 SilentOnIgnoredFilesEmitter {
373 has_non_ignorable_parser_errors: false,
374 source_map,
375 emitter: Box::new(emitter_writer),
376 ignore_path_set: IntoDynSyncSend(ignore_path_set),
377 can_reset,
378 }
379 }
380
381 fn get_ignore_list(config: &str) -> IgnoreList {
382 Config::from_toml(config, Path::new("./rustfmt.toml"))
383 .unwrap()
384 .ignore()
385 }
386
387 fn filename(sm: &SourceMap, path: &str) -> SourceMapFileName {
388 SourceMapFileName::Real(
389 sm.path_mapping()
390 .to_real_filename(sm.working_dir(), PathBuf::from(path)),
391 )
392 }
393
394 #[test]
395 fn handles_fatal_parse_error_in_ignored_file() {
396 let num_emitted_errors = Arc::new(AtomicU32::new(0));
397 let can_reset_errors = Arc::new(AtomicBool::new(false));
398 let ignore_list = get_ignore_list(r#"ignore = ["foo.rs"]"#);
399 let source_map = Arc::new(SourceMap::new(FilePathMapping::empty()));
400 let source =
401 String::from(r#"extern "system" fn jni_symbol!( funcName ) ( ... ) -> {} "#);
402 source_map.new_source_file(filename(&source_map, "foo.rs"), source);
403 let mut emitter = build_emitter(
404 Arc::clone(&num_emitted_errors),
405 Arc::clone(&can_reset_errors),
406 Some(Arc::clone(&source_map)),
407 Some(ignore_list),
408 );
409 let span = MultiSpan::from_span(mk_sp(BytePos(0), BytePos(1)));
410 let fatal_diagnostic = build_diagnostic(DiagnosticLevel::Fatal, Some(span));
411 emitter.emit_diagnostic(fatal_diagnostic);
412 assert_eq!(num_emitted_errors.load(Ordering::Acquire), 1);
413 assert_eq!(can_reset_errors.load(Ordering::Acquire), false);
414 }
415
416 #[nightly_only_test]
417 #[test]
418 fn handles_recoverable_parse_error_in_ignored_file() {
419 let num_emitted_errors = Arc::new(AtomicU32::new(0));
420 let can_reset_errors = Arc::new(AtomicBool::new(false));
421 let ignore_list = get_ignore_list(r#"ignore = ["foo.rs"]"#);
422 let source_map = Arc::new(SourceMap::new(FilePathMapping::empty()));
423 let source = String::from(r#"pub fn bar() { 1x; }"#);
424 source_map.new_source_file(filename(&source_map, "foo.rs"), source);
425 let mut emitter = build_emitter(
426 Arc::clone(&num_emitted_errors),
427 Arc::clone(&can_reset_errors),
428 Some(Arc::clone(&source_map)),
429 Some(ignore_list),
430 );
431 let span = MultiSpan::from_span(mk_sp(BytePos(0), BytePos(1)));
432 let non_fatal_diagnostic = build_diagnostic(DiagnosticLevel::Warning, Some(span));
433 emitter.emit_diagnostic(non_fatal_diagnostic);
434 assert_eq!(num_emitted_errors.load(Ordering::Acquire), 0);
435 assert_eq!(can_reset_errors.load(Ordering::Acquire), true);
436 }
437
438 #[nightly_only_test]
439 #[test]
440 fn handles_recoverable_parse_error_in_non_ignored_file() {
441 let num_emitted_errors = Arc::new(AtomicU32::new(0));
442 let can_reset_errors = Arc::new(AtomicBool::new(false));
443 let source_map = Arc::new(SourceMap::new(FilePathMapping::empty()));
444 let source = String::from(r#"pub fn bar() { 1x; }"#);
445 source_map.new_source_file(filename(&source_map, "foo.rs"), source);
446 let mut emitter = build_emitter(
447 Arc::clone(&num_emitted_errors),
448 Arc::clone(&can_reset_errors),
449 Some(Arc::clone(&source_map)),
450 None,
451 );
452 let span = MultiSpan::from_span(mk_sp(BytePos(0), BytePos(1)));
453 let non_fatal_diagnostic = build_diagnostic(DiagnosticLevel::Warning, Some(span));
454 emitter.emit_diagnostic(non_fatal_diagnostic);
455 assert_eq!(num_emitted_errors.load(Ordering::Acquire), 1);
456 assert_eq!(can_reset_errors.load(Ordering::Acquire), false);
457 }
458
459 #[nightly_only_test]
460 #[test]
461 fn handles_mix_of_recoverable_parse_error() {
462 let num_emitted_errors = Arc::new(AtomicU32::new(0));
463 let can_reset_errors = Arc::new(AtomicBool::new(false));
464 let source_map = Arc::new(SourceMap::new(FilePathMapping::empty()));
465 let ignore_list = get_ignore_list(r#"ignore = ["foo.rs"]"#);
466 let bar_source = String::from(r#"pub fn bar() { 1x; }"#);
467 let foo_source = String::from(r#"pub fn foo() { 1x; }"#);
468 let fatal_source =
469 String::from(r#"extern "system" fn jni_symbol!( funcName ) ( ... ) -> {} "#);
470 source_map.new_source_file(filename(&source_map, "bar.rs"), bar_source);
471 source_map.new_source_file(filename(&source_map, "foo.rs"), foo_source);
472 source_map.new_source_file(filename(&source_map, "fatal.rs"), fatal_source);
473 let mut emitter = build_emitter(
474 Arc::clone(&num_emitted_errors),
475 Arc::clone(&can_reset_errors),
476 Some(Arc::clone(&source_map)),
477 Some(ignore_list),
478 );
479 let bar_span = MultiSpan::from_span(mk_sp(BytePos(0), BytePos(1)));
480 let foo_span = MultiSpan::from_span(mk_sp(BytePos(21), BytePos(22)));
481 let bar_diagnostic = build_diagnostic(DiagnosticLevel::Warning, Some(bar_span));
482 let foo_diagnostic = build_diagnostic(DiagnosticLevel::Warning, Some(foo_span));
483 let fatal_diagnostic = build_diagnostic(DiagnosticLevel::Fatal, None);
484 emitter.emit_diagnostic(bar_diagnostic);
485 emitter.emit_diagnostic(foo_diagnostic);
486 emitter.emit_diagnostic(fatal_diagnostic);
487 assert_eq!(num_emitted_errors.load(Ordering::Acquire), 2);
488 assert_eq!(can_reset_errors.load(Ordering::Acquire), false);
489 }
490 }
491}