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