• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 use std::path::Path;
2 use std::sync::atomic::{AtomicBool, Ordering};
3 
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, TerminalUrl};
8 use rustc_session::parse::ParseSess as RawParseSess;
9 use rustc_span::{
10     source_map::{FilePathMapping, SourceMap},
11     symbol, BytePos, Span,
12 };
13 
14 use crate::config::file_lines::LineRange;
15 use crate::config::options::Color;
16 use crate::ignore_path::IgnorePathSet;
17 use crate::parse::parser::{ModError, ModulePathSuccess};
18 use crate::source_map::LineRangeUtils;
19 use crate::utils::starts_with_newline;
20 use crate::visitor::SnippetProvider;
21 use crate::{Config, ErrorKind, FileName};
22 
23 /// ParseSess holds structs necessary for constructing a parser.
24 pub(crate) struct ParseSess {
25     parse_sess: RawParseSess,
26     ignore_path_set: Lrc<IgnorePathSet>,
27     can_reset_errors: Lrc<AtomicBool>,
28 }
29 
30 /// Emitter which discards every error.
31 struct SilentEmitter;
32 
33 impl Translate for SilentEmitter {
fluent_bundle(&self) -> Option<&Lrc<rustc_errors::FluentBundle>>34     fn fluent_bundle(&self) -> Option<&Lrc<rustc_errors::FluentBundle>> {
35         None
36     }
37 
fallback_fluent_bundle(&self) -> &rustc_errors::FluentBundle38     fn fallback_fluent_bundle(&self) -> &rustc_errors::FluentBundle {
39         panic!("silent emitter attempted to translate a diagnostic");
40     }
41 }
42 
43 impl Emitter for SilentEmitter {
source_map(&self) -> Option<&Lrc<SourceMap>>44     fn source_map(&self) -> Option<&Lrc<SourceMap>> {
45         None
46     }
47 
emit_diagnostic(&mut self, _db: &Diagnostic)48     fn emit_diagnostic(&mut self, _db: &Diagnostic) {}
49 }
50 
silent_emitter() -> Box<dyn Emitter + Send>51 fn silent_emitter() -> Box<dyn Emitter + Send> {
52     Box::new(SilentEmitter {})
53 }
54 
55 /// Emit errors against every files expect ones specified in the `ignore_path_set`.
56 struct SilentOnIgnoredFilesEmitter {
57     ignore_path_set: Lrc<IgnorePathSet>,
58     source_map: Lrc<SourceMap>,
59     emitter: Box<dyn Emitter + Send>,
60     has_non_ignorable_parser_errors: bool,
61     can_reset: Lrc<AtomicBool>,
62 }
63 
64 impl SilentOnIgnoredFilesEmitter {
handle_non_ignoreable_error(&mut self, db: &Diagnostic)65     fn handle_non_ignoreable_error(&mut self, db: &Diagnostic) {
66         self.has_non_ignorable_parser_errors = true;
67         self.can_reset.store(false, Ordering::Release);
68         self.emitter.emit_diagnostic(db);
69     }
70 }
71 
72 impl Translate for SilentOnIgnoredFilesEmitter {
fluent_bundle(&self) -> Option<&Lrc<rustc_errors::FluentBundle>>73     fn fluent_bundle(&self) -> Option<&Lrc<rustc_errors::FluentBundle>> {
74         self.emitter.fluent_bundle()
75     }
76 
fallback_fluent_bundle(&self) -> &rustc_errors::FluentBundle77     fn fallback_fluent_bundle(&self) -> &rustc_errors::FluentBundle {
78         self.emitter.fallback_fluent_bundle()
79     }
80 }
81 
82 impl Emitter for SilentOnIgnoredFilesEmitter {
source_map(&self) -> Option<&Lrc<SourceMap>>83     fn source_map(&self) -> Option<&Lrc<SourceMap>> {
84         None
85     }
86 
emit_diagnostic(&mut self, db: &Diagnostic)87     fn emit_diagnostic(&mut self, db: &Diagnostic) {
88         if db.level() == DiagnosticLevel::Fatal {
89             return self.handle_non_ignoreable_error(db);
90         }
91         if let Some(primary_span) = &db.span.primary_span() {
92             let file_name = self.source_map.span_to_filename(*primary_span);
93             if let rustc_span::FileName::Real(rustc_span::RealFileName::LocalPath(ref path)) =
94                 file_name
95             {
96                 if self
97                     .ignore_path_set
98                     .is_match(&FileName::Real(path.to_path_buf()))
99                 {
100                     if !self.has_non_ignorable_parser_errors {
101                         self.can_reset.store(true, Ordering::Release);
102                     }
103                     return;
104                 }
105             };
106         }
107         self.handle_non_ignoreable_error(db);
108     }
109 }
110 
111 impl From<Color> for ColorConfig {
from(color: Color) -> Self112     fn from(color: Color) -> Self {
113         match color {
114             Color::Auto => ColorConfig::Auto,
115             Color::Always => ColorConfig::Always,
116             Color::Never => ColorConfig::Never,
117         }
118     }
119 }
120 
default_handler( source_map: Lrc<SourceMap>, ignore_path_set: Lrc<IgnorePathSet>, can_reset: Lrc<AtomicBool>, hide_parse_errors: bool, color: Color, ) -> Handler121 fn default_handler(
122     source_map: Lrc<SourceMap>,
123     ignore_path_set: Lrc<IgnorePathSet>,
124     can_reset: Lrc<AtomicBool>,
125     hide_parse_errors: bool,
126     color: Color,
127 ) -> Handler {
128     let supports_color = term::stderr().map_or(false, |term| term.supports_color());
129     let emit_color = if supports_color {
130         ColorConfig::from(color)
131     } else {
132         ColorConfig::Never
133     };
134 
135     let emitter = if hide_parse_errors {
136         silent_emitter()
137     } else {
138         let fallback_bundle = rustc_errors::fallback_fluent_bundle(
139             rustc_driver::DEFAULT_LOCALE_RESOURCES.to_vec(),
140             false,
141         );
142         Box::new(EmitterWriter::stderr(
143             emit_color,
144             Some(source_map.clone()),
145             None,
146             fallback_bundle,
147             false,
148             false,
149             None,
150             false,
151             false,
152             TerminalUrl::No,
153         ))
154     };
155     Handler::with_emitter(
156         true,
157         None,
158         Box::new(SilentOnIgnoredFilesEmitter {
159             has_non_ignorable_parser_errors: false,
160             source_map,
161             emitter,
162             ignore_path_set,
163             can_reset,
164         }),
165     )
166 }
167 
168 impl ParseSess {
new(config: &Config) -> Result<ParseSess, ErrorKind>169     pub(crate) fn new(config: &Config) -> Result<ParseSess, ErrorKind> {
170         let ignore_path_set = match IgnorePathSet::from_ignore_list(&config.ignore()) {
171             Ok(ignore_path_set) => Lrc::new(ignore_path_set),
172             Err(e) => return Err(ErrorKind::InvalidGlobPattern(e)),
173         };
174         let source_map = Lrc::new(SourceMap::new(FilePathMapping::empty()));
175         let can_reset_errors = Lrc::new(AtomicBool::new(false));
176 
177         let handler = default_handler(
178             Lrc::clone(&source_map),
179             Lrc::clone(&ignore_path_set),
180             Lrc::clone(&can_reset_errors),
181             config.hide_parse_errors(),
182             config.color(),
183         );
184         let parse_sess = RawParseSess::with_span_handler(handler, source_map);
185 
186         Ok(ParseSess {
187             parse_sess,
188             ignore_path_set,
189             can_reset_errors,
190         })
191     }
192 
193     /// Determine the submodule path for the given module identifier.
194     ///
195     /// * `id` - The name of the module
196     /// * `relative` - If Some(symbol), the symbol name is a directory relative to the dir_path.
197     ///   If relative is Some, resolve the submodle at {dir_path}/{symbol}/{id}.rs
198     ///   or {dir_path}/{symbol}/{id}/mod.rs. if None, resolve the module at {dir_path}/{id}.rs.
199     /// *  `dir_path` - Module resolution will occur relative to this directory.
default_submod_path( &self, id: symbol::Ident, relative: Option<symbol::Ident>, dir_path: &Path, ) -> Result<ModulePathSuccess, ModError<'_>>200     pub(crate) fn default_submod_path(
201         &self,
202         id: symbol::Ident,
203         relative: Option<symbol::Ident>,
204         dir_path: &Path,
205     ) -> Result<ModulePathSuccess, ModError<'_>> {
206         rustc_expand::module::default_submod_path(&self.parse_sess, id, relative, dir_path).or_else(
207             |e| {
208                 // If resloving a module relative to {dir_path}/{symbol} fails because a file
209                 // could not be found, then try to resolve the module relative to {dir_path}.
210                 // If we still can't find the module after searching for it in {dir_path},
211                 // surface the original error.
212                 if matches!(e, ModError::FileNotFound(..)) && relative.is_some() {
213                     rustc_expand::module::default_submod_path(&self.parse_sess, id, None, dir_path)
214                         .map_err(|_| e)
215                 } else {
216                     Err(e)
217                 }
218             },
219         )
220     }
221 
is_file_parsed(&self, path: &Path) -> bool222     pub(crate) fn is_file_parsed(&self, path: &Path) -> bool {
223         self.parse_sess
224             .source_map()
225             .get_source_file(&rustc_span::FileName::Real(
226                 rustc_span::RealFileName::LocalPath(path.to_path_buf()),
227             ))
228             .is_some()
229     }
230 
ignore_file(&self, path: &FileName) -> bool231     pub(crate) fn ignore_file(&self, path: &FileName) -> bool {
232         self.ignore_path_set.as_ref().is_match(path)
233     }
234 
set_silent_emitter(&mut self)235     pub(crate) fn set_silent_emitter(&mut self) {
236         self.parse_sess.span_diagnostic = Handler::with_emitter(true, None, silent_emitter());
237     }
238 
span_to_filename(&self, span: Span) -> FileName239     pub(crate) fn span_to_filename(&self, span: Span) -> FileName {
240         self.parse_sess.source_map().span_to_filename(span).into()
241     }
242 
span_to_file_contents(&self, span: Span) -> Lrc<rustc_span::SourceFile>243     pub(crate) fn span_to_file_contents(&self, span: Span) -> Lrc<rustc_span::SourceFile> {
244         self.parse_sess
245             .source_map()
246             .lookup_source_file(span.data().lo)
247     }
248 
span_to_first_line_string(&self, span: Span) -> String249     pub(crate) fn span_to_first_line_string(&self, span: Span) -> String {
250         let file_lines = self.parse_sess.source_map().span_to_lines(span).ok();
251 
252         match file_lines {
253             Some(fl) => fl
254                 .file
255                 .get_line(fl.lines[0].line_index)
256                 .map_or_else(String::new, |s| s.to_string()),
257             None => String::new(),
258         }
259     }
260 
line_of_byte_pos(&self, pos: BytePos) -> usize261     pub(crate) fn line_of_byte_pos(&self, pos: BytePos) -> usize {
262         self.parse_sess.source_map().lookup_char_pos(pos).line
263     }
264 
265     // TODO(calebcartwright): Preemptive, currently unused addition
266     // that will be used to support formatting scenarios that take original
267     // positions into account
268     /// Determines whether two byte positions are in the same source line.
269     #[allow(dead_code)]
byte_pos_same_line(&self, a: BytePos, b: BytePos) -> bool270     pub(crate) fn byte_pos_same_line(&self, a: BytePos, b: BytePos) -> bool {
271         self.line_of_byte_pos(a) == self.line_of_byte_pos(b)
272     }
273 
span_to_debug_info(&self, span: Span) -> String274     pub(crate) fn span_to_debug_info(&self, span: Span) -> String {
275         self.parse_sess.source_map().span_to_diagnostic_string(span)
276     }
277 
inner(&self) -> &RawParseSess278     pub(crate) fn inner(&self) -> &RawParseSess {
279         &self.parse_sess
280     }
281 
snippet_provider(&self, span: Span) -> SnippetProvider282     pub(crate) fn snippet_provider(&self, span: Span) -> SnippetProvider {
283         let source_file = self.parse_sess.source_map().lookup_char_pos(span.lo()).file;
284         SnippetProvider::new(
285             source_file.start_pos,
286             source_file.end_pos,
287             Lrc::clone(source_file.src.as_ref().unwrap()),
288         )
289     }
290 
get_original_snippet(&self, file_name: &FileName) -> Option<Lrc<String>>291     pub(crate) fn get_original_snippet(&self, file_name: &FileName) -> Option<Lrc<String>> {
292         self.parse_sess
293             .source_map()
294             .get_source_file(&file_name.into())
295             .and_then(|source_file| source_file.src.clone())
296     }
297 }
298 
299 // Methods that should be restricted within the parse module.
300 impl ParseSess {
emit_diagnostics(&self, diagnostics: Vec<Diagnostic>)301     pub(super) fn emit_diagnostics(&self, diagnostics: Vec<Diagnostic>) {
302         for mut diagnostic in diagnostics {
303             self.parse_sess
304                 .span_diagnostic
305                 .emit_diagnostic(&mut diagnostic);
306         }
307     }
308 
can_reset_errors(&self) -> bool309     pub(super) fn can_reset_errors(&self) -> bool {
310         self.can_reset_errors.load(Ordering::Acquire)
311     }
312 
has_errors(&self) -> bool313     pub(super) fn has_errors(&self) -> bool {
314         self.parse_sess.span_diagnostic.has_errors().is_some()
315     }
316 
reset_errors(&self)317     pub(super) fn reset_errors(&self) {
318         self.parse_sess.span_diagnostic.reset_err_count();
319     }
320 }
321 
322 impl LineRangeUtils for ParseSess {
lookup_line_range(&self, span: Span) -> LineRange323     fn lookup_line_range(&self, span: Span) -> LineRange {
324         let snippet = self
325             .parse_sess
326             .source_map()
327             .span_to_snippet(span)
328             .unwrap_or_default();
329         let lo = self.parse_sess.source_map().lookup_line(span.lo()).unwrap();
330         let hi = self.parse_sess.source_map().lookup_line(span.hi()).unwrap();
331 
332         debug_assert_eq!(
333             lo.sf.name, hi.sf.name,
334             "span crossed file boundary: lo: {:?}, hi: {:?}",
335             lo, hi
336         );
337 
338         // in case the span starts with a newline, the line range is off by 1 without the
339         // adjustment below
340         let offset = 1 + if starts_with_newline(&snippet) { 1 } else { 0 };
341         // Line numbers start at 1
342         LineRange {
343             file: lo.sf.clone(),
344             lo: lo.line + offset,
345             hi: hi.line + offset,
346         }
347     }
348 }
349 
350 #[cfg(test)]
351 mod tests {
352     use super::*;
353 
354     use rustfmt_config_proc_macro::nightly_only_test;
355 
356     mod emitter {
357         use super::*;
358         use crate::config::IgnoreList;
359         use crate::utils::mk_sp;
360         use rustc_errors::MultiSpan;
361         use rustc_span::{FileName as SourceMapFileName, RealFileName};
362         use std::path::PathBuf;
363         use std::sync::atomic::AtomicU32;
364 
365         struct TestEmitter {
366             num_emitted_errors: Lrc<AtomicU32>,
367         }
368 
369         impl Translate for TestEmitter {
fluent_bundle(&self) -> Option<&Lrc<rustc_errors::FluentBundle>>370             fn fluent_bundle(&self) -> Option<&Lrc<rustc_errors::FluentBundle>> {
371                 None
372             }
373 
fallback_fluent_bundle(&self) -> &rustc_errors::FluentBundle374             fn fallback_fluent_bundle(&self) -> &rustc_errors::FluentBundle {
375                 panic!("test emitter attempted to translate a diagnostic");
376             }
377         }
378 
379         impl Emitter for TestEmitter {
source_map(&self) -> Option<&Lrc<SourceMap>>380             fn source_map(&self) -> Option<&Lrc<SourceMap>> {
381                 None
382             }
383 
emit_diagnostic(&mut self, _db: &Diagnostic)384             fn emit_diagnostic(&mut self, _db: &Diagnostic) {
385                 self.num_emitted_errors.fetch_add(1, Ordering::Release);
386             }
387         }
388 
build_diagnostic(level: DiagnosticLevel, span: Option<MultiSpan>) -> Diagnostic389         fn build_diagnostic(level: DiagnosticLevel, span: Option<MultiSpan>) -> Diagnostic {
390             let mut diag = Diagnostic::new(level, "");
391             diag.message.clear();
392             if let Some(span) = span {
393                 diag.span = span;
394             }
395             diag
396         }
397 
build_emitter( num_emitted_errors: Lrc<AtomicU32>, can_reset: Lrc<AtomicBool>, source_map: Option<Lrc<SourceMap>>, ignore_list: Option<IgnoreList>, ) -> SilentOnIgnoredFilesEmitter398         fn build_emitter(
399             num_emitted_errors: Lrc<AtomicU32>,
400             can_reset: Lrc<AtomicBool>,
401             source_map: Option<Lrc<SourceMap>>,
402             ignore_list: Option<IgnoreList>,
403         ) -> SilentOnIgnoredFilesEmitter {
404             let emitter_writer = TestEmitter { num_emitted_errors };
405             let source_map =
406                 source_map.unwrap_or_else(|| Lrc::new(SourceMap::new(FilePathMapping::empty())));
407             let ignore_path_set = Lrc::new(
408                 IgnorePathSet::from_ignore_list(&ignore_list.unwrap_or_default()).unwrap(),
409             );
410             SilentOnIgnoredFilesEmitter {
411                 has_non_ignorable_parser_errors: false,
412                 source_map,
413                 emitter: Box::new(emitter_writer),
414                 ignore_path_set,
415                 can_reset,
416             }
417         }
418 
get_ignore_list(config: &str) -> IgnoreList419         fn get_ignore_list(config: &str) -> IgnoreList {
420             Config::from_toml(config, Path::new("")).unwrap().ignore()
421         }
422 
423         #[test]
handles_fatal_parse_error_in_ignored_file()424         fn handles_fatal_parse_error_in_ignored_file() {
425             let num_emitted_errors = Lrc::new(AtomicU32::new(0));
426             let can_reset_errors = Lrc::new(AtomicBool::new(false));
427             let ignore_list = get_ignore_list(r#"ignore = ["foo.rs"]"#);
428             let source_map = Lrc::new(SourceMap::new(FilePathMapping::empty()));
429             let source =
430                 String::from(r#"extern "system" fn jni_symbol!( funcName ) ( ... ) -> {} "#);
431             source_map.new_source_file(
432                 SourceMapFileName::Real(RealFileName::LocalPath(PathBuf::from("foo.rs"))),
433                 source,
434             );
435             let mut emitter = build_emitter(
436                 Lrc::clone(&num_emitted_errors),
437                 Lrc::clone(&can_reset_errors),
438                 Some(Lrc::clone(&source_map)),
439                 Some(ignore_list),
440             );
441             let span = MultiSpan::from_span(mk_sp(BytePos(0), BytePos(1)));
442             let fatal_diagnostic = build_diagnostic(DiagnosticLevel::Fatal, Some(span));
443             emitter.emit_diagnostic(&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]
handles_recoverable_parse_error_in_ignored_file()450         fn handles_recoverable_parse_error_in_ignored_file() {
451             let num_emitted_errors = Lrc::new(AtomicU32::new(0));
452             let can_reset_errors = Lrc::new(AtomicBool::new(false));
453             let ignore_list = get_ignore_list(r#"ignore = ["foo.rs"]"#);
454             let source_map = Lrc::new(SourceMap::new(FilePathMapping::empty()));
455             let source = String::from(r#"pub fn bar() { 1x; }"#);
456             source_map.new_source_file(
457                 SourceMapFileName::Real(RealFileName::LocalPath(PathBuf::from("foo.rs"))),
458                 source,
459             );
460             let mut emitter = build_emitter(
461                 Lrc::clone(&num_emitted_errors),
462                 Lrc::clone(&can_reset_errors),
463                 Some(Lrc::clone(&source_map)),
464                 Some(ignore_list),
465             );
466             let span = MultiSpan::from_span(mk_sp(BytePos(0), BytePos(1)));
467             let non_fatal_diagnostic = build_diagnostic(DiagnosticLevel::Warning(None), Some(span));
468             emitter.emit_diagnostic(&non_fatal_diagnostic);
469             assert_eq!(num_emitted_errors.load(Ordering::Acquire), 0);
470             assert_eq!(can_reset_errors.load(Ordering::Acquire), true);
471         }
472 
473         #[nightly_only_test]
474         #[test]
handles_recoverable_parse_error_in_non_ignored_file()475         fn handles_recoverable_parse_error_in_non_ignored_file() {
476             let num_emitted_errors = Lrc::new(AtomicU32::new(0));
477             let can_reset_errors = Lrc::new(AtomicBool::new(false));
478             let source_map = Lrc::new(SourceMap::new(FilePathMapping::empty()));
479             let source = String::from(r#"pub fn bar() { 1x; }"#);
480             source_map.new_source_file(
481                 SourceMapFileName::Real(RealFileName::LocalPath(PathBuf::from("foo.rs"))),
482                 source,
483             );
484             let mut emitter = build_emitter(
485                 Lrc::clone(&num_emitted_errors),
486                 Lrc::clone(&can_reset_errors),
487                 Some(Lrc::clone(&source_map)),
488                 None,
489             );
490             let span = MultiSpan::from_span(mk_sp(BytePos(0), BytePos(1)));
491             let non_fatal_diagnostic = build_diagnostic(DiagnosticLevel::Warning(None), Some(span));
492             emitter.emit_diagnostic(&non_fatal_diagnostic);
493             assert_eq!(num_emitted_errors.load(Ordering::Acquire), 1);
494             assert_eq!(can_reset_errors.load(Ordering::Acquire), false);
495         }
496 
497         #[nightly_only_test]
498         #[test]
handles_mix_of_recoverable_parse_error()499         fn handles_mix_of_recoverable_parse_error() {
500             let num_emitted_errors = Lrc::new(AtomicU32::new(0));
501             let can_reset_errors = Lrc::new(AtomicBool::new(false));
502             let source_map = Lrc::new(SourceMap::new(FilePathMapping::empty()));
503             let ignore_list = get_ignore_list(r#"ignore = ["foo.rs"]"#);
504             let bar_source = String::from(r#"pub fn bar() { 1x; }"#);
505             let foo_source = String::from(r#"pub fn foo() { 1x; }"#);
506             let fatal_source =
507                 String::from(r#"extern "system" fn jni_symbol!( funcName ) ( ... ) -> {} "#);
508             source_map.new_source_file(
509                 SourceMapFileName::Real(RealFileName::LocalPath(PathBuf::from("bar.rs"))),
510                 bar_source,
511             );
512             source_map.new_source_file(
513                 SourceMapFileName::Real(RealFileName::LocalPath(PathBuf::from("foo.rs"))),
514                 foo_source,
515             );
516             source_map.new_source_file(
517                 SourceMapFileName::Real(RealFileName::LocalPath(PathBuf::from("fatal.rs"))),
518                 fatal_source,
519             );
520             let mut emitter = build_emitter(
521                 Lrc::clone(&num_emitted_errors),
522                 Lrc::clone(&can_reset_errors),
523                 Some(Lrc::clone(&source_map)),
524                 Some(ignore_list),
525             );
526             let bar_span = MultiSpan::from_span(mk_sp(BytePos(0), BytePos(1)));
527             let foo_span = MultiSpan::from_span(mk_sp(BytePos(21), BytePos(22)));
528             let bar_diagnostic = build_diagnostic(DiagnosticLevel::Warning(None), Some(bar_span));
529             let foo_diagnostic = build_diagnostic(DiagnosticLevel::Warning(None), Some(foo_span));
530             let fatal_diagnostic = build_diagnostic(DiagnosticLevel::Fatal, None);
531             emitter.emit_diagnostic(&bar_diagnostic);
532             emitter.emit_diagnostic(&foo_diagnostic);
533             emitter.emit_diagnostic(&fatal_diagnostic);
534             assert_eq!(num_emitted_errors.load(Ordering::Acquire), 2);
535             assert_eq!(can_reset_errors.load(Ordering::Acquire), false);
536         }
537     }
538 }
539