2 use std::sync::atomic::{AtomicBool, Ordering};
4 use rustc_data_structures::sync::{Lrc, Send};
5 use rustc_errors::emitter::{Emitter, EmitterWriter};
6 use rustc_errors::translation::Translate;
7 use rustc_errors::{ColorConfig, Diagnostic, Handler, Level as DiagnosticLevel};
8 use rustc_session::parse::ParseSess as RawParseSess;
10 source_map::{FilePathMapping, SourceMap},
11 symbol, BytePos, Span,
14 use crate::config::file_lines::LineRange;
15 use crate::ignore_path::IgnorePathSet;
16 use crate::parse::parser::{ModError, ModulePathSuccess};
17 use crate::source_map::LineRangeUtils;
18 use crate::utils::starts_with_newline;
19 use crate::visitor::SnippetProvider;
20 use crate::{Config, ErrorKind, FileName};
22 /// ParseSess holds structs necessary for constructing a parser.
23 pub(crate) struct ParseSess {
24 parse_sess: RawParseSess,
25 ignore_path_set: Lrc<IgnorePathSet>,
26 can_reset_errors: Lrc<AtomicBool>,
29 /// Emitter which discards every error.
32 impl Translate for SilentEmitter {
33 fn fluent_bundle(&self) -> Option<&Lrc<rustc_errors::FluentBundle>> {
37 fn fallback_fluent_bundle(&self) -> &rustc_errors::FluentBundle {
38 panic!("silent emitter attempted to translate a diagnostic");
42 impl Emitter for SilentEmitter {
43 fn source_map(&self) -> Option<&Lrc<SourceMap>> {
47 fn emit_diagnostic(&mut self, _db: &Diagnostic) {}
50 fn silent_emitter() -> Box<dyn Emitter + Send> {
51 Box::new(SilentEmitter {})
54 /// Emit errors against every files expect ones specified in the `ignore_path_set`.
55 struct SilentOnIgnoredFilesEmitter {
56 ignore_path_set: Lrc<IgnorePathSet>,
57 source_map: Lrc<SourceMap>,
58 emitter: Box<dyn Emitter + Send>,
59 has_non_ignorable_parser_errors: bool,
60 can_reset: Lrc<AtomicBool>,
63 impl SilentOnIgnoredFilesEmitter {
64 fn handle_non_ignoreable_error(&mut self, db: &Diagnostic) {
65 self.has_non_ignorable_parser_errors = true;
66 self.can_reset.store(false, Ordering::Release);
67 self.emitter.emit_diagnostic(db);
71 impl Translate for SilentOnIgnoredFilesEmitter {
72 fn fluent_bundle(&self) -> Option<&Lrc<rustc_errors::FluentBundle>> {
73 self.emitter.fluent_bundle()
76 fn fallback_fluent_bundle(&self) -> &rustc_errors::FluentBundle {
77 self.emitter.fallback_fluent_bundle()
81 impl Emitter for SilentOnIgnoredFilesEmitter {
82 fn source_map(&self) -> Option<&Lrc<SourceMap>> {
86 fn emit_diagnostic(&mut self, db: &Diagnostic) {
87 if db.level() == DiagnosticLevel::Fatal {
88 return self.handle_non_ignoreable_error(db);
90 if let Some(primary_span) = &db.span.primary_span() {
91 let file_name = self.source_map.span_to_filename(*primary_span);
92 if let rustc_span::FileName::Real(rustc_span::RealFileName::LocalPath(ref path)) =
97 .is_match(&FileName::Real(path.to_path_buf()))
99 if !self.has_non_ignorable_parser_errors {
100 self.can_reset.store(true, Ordering::Release);
106 self.handle_non_ignoreable_error(db);
111 source_map: Lrc<SourceMap>,
112 ignore_path_set: Lrc<IgnorePathSet>,
113 can_reset: Lrc<AtomicBool>,
114 hide_parse_errors: bool,
116 let supports_color = term::stderr().map_or(false, |term| term.supports_color());
117 let color_cfg = if supports_color {
123 let emitter = if hide_parse_errors {
126 let fallback_bundle =
127 rustc_errors::fallback_fluent_bundle(rustc_errors::DEFAULT_LOCALE_RESOURCES, false);
128 Box::new(EmitterWriter::stderr(
130 Some(source_map.clone()),
139 Handler::with_emitter(
142 Box::new(SilentOnIgnoredFilesEmitter {
143 has_non_ignorable_parser_errors: false,
153 pub(crate) fn new(config: &Config) -> Result<ParseSess, ErrorKind> {
154 let ignore_path_set = match IgnorePathSet::from_ignore_list(&config.ignore()) {
155 Ok(ignore_path_set) => Lrc::new(ignore_path_set),
156 Err(e) => return Err(ErrorKind::InvalidGlobPattern(e)),
158 let source_map = Lrc::new(SourceMap::new(FilePathMapping::empty()));
159 let can_reset_errors = Lrc::new(AtomicBool::new(false));
161 let handler = default_handler(
162 Lrc::clone(&source_map),
163 Lrc::clone(&ignore_path_set),
164 Lrc::clone(&can_reset_errors),
165 config.hide_parse_errors(),
167 let parse_sess = RawParseSess::with_span_handler(handler, source_map);
176 /// Determine the submodule path for the given module identifier.
178 /// * `id` - The name of the module
179 /// * `relative` - If Some(symbol), the symbol name is a directory relative to the dir_path.
180 /// If relative is Some, resolve the submodle at {dir_path}/{symbol}/{id}.rs
181 /// or {dir_path}/{symbol}/{id}/mod.rs. if None, resolve the module at {dir_path}/{id}.rs.
182 /// * `dir_path` - Module resolution will occur relative to this directory.
183 pub(crate) fn default_submod_path(
186 relative: Option<symbol::Ident>,
188 ) -> Result<ModulePathSuccess, ModError<'_>> {
189 rustc_expand::module::default_submod_path(&self.parse_sess, id, relative, dir_path).or_else(
191 // If resloving a module relative to {dir_path}/{symbol} fails because a file
192 // could not be found, then try to resolve the module relative to {dir_path}.
193 // If we still can't find the module after searching for it in {dir_path},
194 // surface the original error.
195 if matches!(e, ModError::FileNotFound(..)) && relative.is_some() {
196 rustc_expand::module::default_submod_path(&self.parse_sess, id, None, dir_path)
205 pub(crate) fn is_file_parsed(&self, path: &Path) -> bool {
208 .get_source_file(&rustc_span::FileName::Real(
209 rustc_span::RealFileName::LocalPath(path.to_path_buf()),
214 pub(crate) fn ignore_file(&self, path: &FileName) -> bool {
215 self.ignore_path_set.as_ref().is_match(path)
218 pub(crate) fn set_silent_emitter(&mut self) {
219 self.parse_sess.span_diagnostic = Handler::with_emitter(true, None, silent_emitter());
222 pub(crate) fn span_to_filename(&self, span: Span) -> FileName {
223 self.parse_sess.source_map().span_to_filename(span).into()
226 pub(crate) fn span_to_file_contents(&self, span: Span) -> Lrc<rustc_span::SourceFile> {
229 .lookup_source_file(span.data().lo)
232 pub(crate) fn span_to_first_line_string(&self, span: Span) -> String {
233 let file_lines = self.parse_sess.source_map().span_to_lines(span).ok();
238 .get_line(fl.lines[0].line_index)
239 .map_or_else(String::new, |s| s.to_string()),
240 None => String::new(),
244 pub(crate) fn line_of_byte_pos(&self, pos: BytePos) -> usize {
245 self.parse_sess.source_map().lookup_char_pos(pos).line
248 // TODO(calebcartwright): Preemptive, currently unused addition
249 // that will be used to support formatting scenarios that take original
250 // positions into account
251 /// Determines whether two byte positions are in the same source line.
253 pub(crate) fn byte_pos_same_line(&self, a: BytePos, b: BytePos) -> bool {
254 self.line_of_byte_pos(a) == self.line_of_byte_pos(b)
257 pub(crate) fn span_to_debug_info(&self, span: Span) -> String {
258 self.parse_sess.source_map().span_to_diagnostic_string(span)
261 pub(crate) fn inner(&self) -> &RawParseSess {
265 pub(crate) fn snippet_provider(&self, span: Span) -> SnippetProvider {
266 let source_file = self.parse_sess.source_map().lookup_char_pos(span.lo()).file;
267 SnippetProvider::new(
268 source_file.start_pos,
270 Lrc::clone(source_file.src.as_ref().unwrap()),
274 pub(crate) fn get_original_snippet(&self, file_name: &FileName) -> Option<Lrc<String>> {
277 .get_source_file(&file_name.into())
278 .and_then(|source_file| source_file.src.clone())
282 // Methods that should be restricted within the parse module.
284 pub(super) fn emit_diagnostics(&self, diagnostics: Vec<Diagnostic>) {
285 for mut diagnostic in diagnostics {
288 .emit_diagnostic(&mut diagnostic);
292 pub(super) fn can_reset_errors(&self) -> bool {
293 self.can_reset_errors.load(Ordering::Acquire)
296 pub(super) fn has_errors(&self) -> bool {
297 self.parse_sess.span_diagnostic.has_errors().is_some()
300 pub(super) fn reset_errors(&self) {
301 self.parse_sess.span_diagnostic.reset_err_count();
305 impl LineRangeUtils for ParseSess {
306 fn lookup_line_range(&self, span: Span) -> LineRange {
310 .span_to_snippet(span)
311 .unwrap_or_default();
312 let lo = self.parse_sess.source_map().lookup_line(span.lo()).unwrap();
313 let hi = self.parse_sess.source_map().lookup_line(span.hi()).unwrap();
316 lo.sf.name, hi.sf.name,
317 "span crossed file boundary: lo: {:?}, hi: {:?}",
321 // in case the span starts with a newline, the line range is off by 1 without the
323 let offset = 1 + if starts_with_newline(&snippet) { 1 } else { 0 };
324 // Line numbers start at 1
327 lo: lo.line + offset,
328 hi: hi.line + offset,
337 use rustfmt_config_proc_macro::nightly_only_test;
341 use crate::config::IgnoreList;
342 use crate::utils::mk_sp;
343 use rustc_errors::MultiSpan;
344 use rustc_span::{FileName as SourceMapFileName, RealFileName};
345 use std::path::PathBuf;
346 use std::sync::atomic::AtomicU32;
349 num_emitted_errors: Lrc<AtomicU32>,
352 impl Translate for TestEmitter {
353 fn fluent_bundle(&self) -> Option<&Lrc<rustc_errors::FluentBundle>> {
357 fn fallback_fluent_bundle(&self) -> &rustc_errors::FluentBundle {
358 panic!("test emitter attempted to translate a diagnostic");
362 impl Emitter for TestEmitter {
363 fn source_map(&self) -> Option<&Lrc<SourceMap>> {
367 fn emit_diagnostic(&mut self, _db: &Diagnostic) {
368 self.num_emitted_errors.fetch_add(1, Ordering::Release);
372 fn build_diagnostic(level: DiagnosticLevel, span: Option<MultiSpan>) -> Diagnostic {
373 let mut diag = Diagnostic::new(level, "");
374 diag.message.clear();
375 if let Some(span) = span {
382 num_emitted_errors: Lrc<AtomicU32>,
383 can_reset: Lrc<AtomicBool>,
384 source_map: Option<Lrc<SourceMap>>,
385 ignore_list: Option<IgnoreList>,
386 ) -> SilentOnIgnoredFilesEmitter {
387 let emitter_writer = TestEmitter { num_emitted_errors };
389 source_map.unwrap_or_else(|| Lrc::new(SourceMap::new(FilePathMapping::empty())));
390 let ignore_path_set = Lrc::new(
391 IgnorePathSet::from_ignore_list(&ignore_list.unwrap_or_default()).unwrap(),
393 SilentOnIgnoredFilesEmitter {
394 has_non_ignorable_parser_errors: false,
396 emitter: Box::new(emitter_writer),
402 fn get_ignore_list(config: &str) -> IgnoreList {
403 Config::from_toml(config, Path::new("")).unwrap().ignore()
407 fn handles_fatal_parse_error_in_ignored_file() {
408 let num_emitted_errors = Lrc::new(AtomicU32::new(0));
409 let can_reset_errors = Lrc::new(AtomicBool::new(false));
410 let ignore_list = get_ignore_list(r#"ignore = ["foo.rs"]"#);
411 let source_map = Lrc::new(SourceMap::new(FilePathMapping::empty()));
413 String::from(r#"extern "system" fn jni_symbol!( funcName ) ( ... ) -> {} "#);
414 source_map.new_source_file(
415 SourceMapFileName::Real(RealFileName::LocalPath(PathBuf::from("foo.rs"))),
418 let mut emitter = build_emitter(
419 Lrc::clone(&num_emitted_errors),
420 Lrc::clone(&can_reset_errors),
421 Some(Lrc::clone(&source_map)),
424 let span = MultiSpan::from_span(mk_sp(BytePos(0), BytePos(1)));
425 let fatal_diagnostic = build_diagnostic(DiagnosticLevel::Fatal, Some(span));
426 emitter.emit_diagnostic(&fatal_diagnostic);
427 assert_eq!(num_emitted_errors.load(Ordering::Acquire), 1);
428 assert_eq!(can_reset_errors.load(Ordering::Acquire), false);
433 fn handles_recoverable_parse_error_in_ignored_file() {
434 let num_emitted_errors = Lrc::new(AtomicU32::new(0));
435 let can_reset_errors = Lrc::new(AtomicBool::new(false));
436 let ignore_list = get_ignore_list(r#"ignore = ["foo.rs"]"#);
437 let source_map = Lrc::new(SourceMap::new(FilePathMapping::empty()));
438 let source = String::from(r#"pub fn bar() { 1x; }"#);
439 source_map.new_source_file(
440 SourceMapFileName::Real(RealFileName::LocalPath(PathBuf::from("foo.rs"))),
443 let mut emitter = build_emitter(
444 Lrc::clone(&num_emitted_errors),
445 Lrc::clone(&can_reset_errors),
446 Some(Lrc::clone(&source_map)),
449 let span = MultiSpan::from_span(mk_sp(BytePos(0), BytePos(1)));
450 let non_fatal_diagnostic = build_diagnostic(DiagnosticLevel::Warning(None), Some(span));
451 emitter.emit_diagnostic(&non_fatal_diagnostic);
452 assert_eq!(num_emitted_errors.load(Ordering::Acquire), 0);
453 assert_eq!(can_reset_errors.load(Ordering::Acquire), true);
458 fn handles_recoverable_parse_error_in_non_ignored_file() {
459 let num_emitted_errors = Lrc::new(AtomicU32::new(0));
460 let can_reset_errors = Lrc::new(AtomicBool::new(false));
461 let source_map = Lrc::new(SourceMap::new(FilePathMapping::empty()));
462 let source = String::from(r#"pub fn bar() { 1x; }"#);
463 source_map.new_source_file(
464 SourceMapFileName::Real(RealFileName::LocalPath(PathBuf::from("foo.rs"))),
467 let mut emitter = build_emitter(
468 Lrc::clone(&num_emitted_errors),
469 Lrc::clone(&can_reset_errors),
470 Some(Lrc::clone(&source_map)),
473 let span = MultiSpan::from_span(mk_sp(BytePos(0), BytePos(1)));
474 let non_fatal_diagnostic = build_diagnostic(DiagnosticLevel::Warning(None), Some(span));
475 emitter.emit_diagnostic(&non_fatal_diagnostic);
476 assert_eq!(num_emitted_errors.load(Ordering::Acquire), 1);
477 assert_eq!(can_reset_errors.load(Ordering::Acquire), false);
482 fn handles_mix_of_recoverable_parse_error() {
483 let num_emitted_errors = Lrc::new(AtomicU32::new(0));
484 let can_reset_errors = Lrc::new(AtomicBool::new(false));
485 let source_map = Lrc::new(SourceMap::new(FilePathMapping::empty()));
486 let ignore_list = get_ignore_list(r#"ignore = ["foo.rs"]"#);
487 let bar_source = String::from(r#"pub fn bar() { 1x; }"#);
488 let foo_source = String::from(r#"pub fn foo() { 1x; }"#);
490 String::from(r#"extern "system" fn jni_symbol!( funcName ) ( ... ) -> {} "#);
491 source_map.new_source_file(
492 SourceMapFileName::Real(RealFileName::LocalPath(PathBuf::from("bar.rs"))),
495 source_map.new_source_file(
496 SourceMapFileName::Real(RealFileName::LocalPath(PathBuf::from("foo.rs"))),
499 source_map.new_source_file(
500 SourceMapFileName::Real(RealFileName::LocalPath(PathBuf::from("fatal.rs"))),
503 let mut emitter = build_emitter(
504 Lrc::clone(&num_emitted_errors),
505 Lrc::clone(&can_reset_errors),
506 Some(Lrc::clone(&source_map)),
509 let bar_span = MultiSpan::from_span(mk_sp(BytePos(0), BytePos(1)));
510 let foo_span = MultiSpan::from_span(mk_sp(BytePos(21), BytePos(22)));
511 let bar_diagnostic = build_diagnostic(DiagnosticLevel::Warning(None), Some(bar_span));
512 let foo_diagnostic = build_diagnostic(DiagnosticLevel::Warning(None), Some(foo_span));
513 let fatal_diagnostic = build_diagnostic(DiagnosticLevel::Fatal, None);
514 emitter.emit_diagnostic(&bar_diagnostic);
515 emitter.emit_diagnostic(&foo_diagnostic);
516 emitter.emit_diagnostic(&fatal_diagnostic);
517 assert_eq!(num_emitted_errors.load(Ordering::Acquire), 2);
518 assert_eq!(can_reset_errors.load(Ordering::Acquire), false);