]> git.lizzy.rs Git - rust.git/blob - src/tools/rustfmt/src/parse/session.rs
Auto merge of #100210 - mystor:proc_macro_diag_struct, r=eddyb
[rust.git] / src / tools / rustfmt / src / parse / session.rs
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};
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::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};
21
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>,
27 }
28
29 /// Emitter which discards every error.
30 struct SilentEmitter;
31
32 impl Translate for SilentEmitter {
33     fn fluent_bundle(&self) -> Option<&Lrc<rustc_errors::FluentBundle>> {
34         None
35     }
36
37     fn fallback_fluent_bundle(&self) -> &rustc_errors::FluentBundle {
38         panic!("silent emitter attempted to translate a diagnostic");
39     }
40 }
41
42 impl Emitter for SilentEmitter {
43     fn source_map(&self) -> Option<&Lrc<SourceMap>> {
44         None
45     }
46
47     fn emit_diagnostic(&mut self, _db: &Diagnostic) {}
48 }
49
50 fn silent_emitter() -> Box<dyn Emitter + Send> {
51     Box::new(SilentEmitter {})
52 }
53
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>,
61 }
62
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);
68     }
69 }
70
71 impl Translate for SilentOnIgnoredFilesEmitter {
72     fn fluent_bundle(&self) -> Option<&Lrc<rustc_errors::FluentBundle>> {
73         self.emitter.fluent_bundle()
74     }
75
76     fn fallback_fluent_bundle(&self) -> &rustc_errors::FluentBundle {
77         self.emitter.fallback_fluent_bundle()
78     }
79 }
80
81 impl Emitter for SilentOnIgnoredFilesEmitter {
82     fn source_map(&self) -> Option<&Lrc<SourceMap>> {
83         None
84     }
85
86     fn emit_diagnostic(&mut self, db: &Diagnostic) {
87         if db.level() == DiagnosticLevel::Fatal {
88             return self.handle_non_ignoreable_error(db);
89         }
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)) =
93                 file_name
94             {
95                 if self
96                     .ignore_path_set
97                     .is_match(&FileName::Real(path.to_path_buf()))
98                 {
99                     if !self.has_non_ignorable_parser_errors {
100                         self.can_reset.store(true, Ordering::Release);
101                     }
102                     return;
103                 }
104             };
105         }
106         self.handle_non_ignoreable_error(db);
107     }
108 }
109
110 fn default_handler(
111     source_map: Lrc<SourceMap>,
112     ignore_path_set: Lrc<IgnorePathSet>,
113     can_reset: Lrc<AtomicBool>,
114     hide_parse_errors: bool,
115 ) -> Handler {
116     let supports_color = term::stderr().map_or(false, |term| term.supports_color());
117     let color_cfg = if supports_color {
118         ColorConfig::Auto
119     } else {
120         ColorConfig::Never
121     };
122
123     let emitter = if hide_parse_errors {
124         silent_emitter()
125     } else {
126         let fallback_bundle =
127             rustc_errors::fallback_fluent_bundle(rustc_errors::DEFAULT_LOCALE_RESOURCES, false);
128         Box::new(EmitterWriter::stderr(
129             color_cfg,
130             Some(source_map.clone()),
131             None,
132             fallback_bundle,
133             false,
134             false,
135             None,
136             false,
137         ))
138     };
139     Handler::with_emitter(
140         true,
141         None,
142         Box::new(SilentOnIgnoredFilesEmitter {
143             has_non_ignorable_parser_errors: false,
144             source_map,
145             emitter,
146             ignore_path_set,
147             can_reset,
148         }),
149     )
150 }
151
152 impl ParseSess {
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)),
157         };
158         let source_map = Lrc::new(SourceMap::new(FilePathMapping::empty()));
159         let can_reset_errors = Lrc::new(AtomicBool::new(false));
160
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(),
166         );
167         let parse_sess = RawParseSess::with_span_handler(handler, source_map);
168
169         Ok(ParseSess {
170             parse_sess,
171             ignore_path_set,
172             can_reset_errors,
173         })
174     }
175
176     /// Determine the submodule path for the given module identifier.
177     ///
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(
184         &self,
185         id: symbol::Ident,
186         relative: Option<symbol::Ident>,
187         dir_path: &Path,
188     ) -> Result<ModulePathSuccess, ModError<'_>> {
189         rustc_expand::module::default_submod_path(&self.parse_sess, id, relative, dir_path).or_else(
190             |e| {
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)
197                         .map_err(|_| e)
198                 } else {
199                     Err(e)
200                 }
201             },
202         )
203     }
204
205     pub(crate) fn is_file_parsed(&self, path: &Path) -> bool {
206         self.parse_sess
207             .source_map()
208             .get_source_file(&rustc_span::FileName::Real(
209                 rustc_span::RealFileName::LocalPath(path.to_path_buf()),
210             ))
211             .is_some()
212     }
213
214     pub(crate) fn ignore_file(&self, path: &FileName) -> bool {
215         self.ignore_path_set.as_ref().is_match(path)
216     }
217
218     pub(crate) fn set_silent_emitter(&mut self) {
219         self.parse_sess.span_diagnostic = Handler::with_emitter(true, None, silent_emitter());
220     }
221
222     pub(crate) fn span_to_filename(&self, span: Span) -> FileName {
223         self.parse_sess.source_map().span_to_filename(span).into()
224     }
225
226     pub(crate) fn span_to_file_contents(&self, span: Span) -> Lrc<rustc_span::SourceFile> {
227         self.parse_sess
228             .source_map()
229             .lookup_source_file(span.data().lo)
230     }
231
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();
234
235         match file_lines {
236             Some(fl) => fl
237                 .file
238                 .get_line(fl.lines[0].line_index)
239                 .map_or_else(String::new, |s| s.to_string()),
240             None => String::new(),
241         }
242     }
243
244     pub(crate) fn line_of_byte_pos(&self, pos: BytePos) -> usize {
245         self.parse_sess.source_map().lookup_char_pos(pos).line
246     }
247
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.
252     #[allow(dead_code)]
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)
255     }
256
257     pub(crate) fn span_to_debug_info(&self, span: Span) -> String {
258         self.parse_sess.source_map().span_to_diagnostic_string(span)
259     }
260
261     pub(crate) fn inner(&self) -> &RawParseSess {
262         &self.parse_sess
263     }
264
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,
269             source_file.end_pos,
270             Lrc::clone(source_file.src.as_ref().unwrap()),
271         )
272     }
273
274     pub(crate) fn get_original_snippet(&self, file_name: &FileName) -> Option<Lrc<String>> {
275         self.parse_sess
276             .source_map()
277             .get_source_file(&file_name.into())
278             .and_then(|source_file| source_file.src.clone())
279     }
280 }
281
282 // Methods that should be restricted within the parse module.
283 impl ParseSess {
284     pub(super) fn emit_diagnostics(&self, diagnostics: Vec<Diagnostic>) {
285         for mut diagnostic in diagnostics {
286             self.parse_sess
287                 .span_diagnostic
288                 .emit_diagnostic(&mut diagnostic);
289         }
290     }
291
292     pub(super) fn can_reset_errors(&self) -> bool {
293         self.can_reset_errors.load(Ordering::Acquire)
294     }
295
296     pub(super) fn has_errors(&self) -> bool {
297         self.parse_sess.span_diagnostic.has_errors().is_some()
298     }
299
300     pub(super) fn reset_errors(&self) {
301         self.parse_sess.span_diagnostic.reset_err_count();
302     }
303 }
304
305 impl LineRangeUtils for ParseSess {
306     fn lookup_line_range(&self, span: Span) -> LineRange {
307         let snippet = self
308             .parse_sess
309             .source_map()
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();
314
315         debug_assert_eq!(
316             lo.sf.name, hi.sf.name,
317             "span crossed file boundary: lo: {:?}, hi: {:?}",
318             lo, hi
319         );
320
321         // in case the span starts with a newline, the line range is off by 1 without the
322         // adjustment below
323         let offset = 1 + if starts_with_newline(&snippet) { 1 } else { 0 };
324         // Line numbers start at 1
325         LineRange {
326             file: lo.sf.clone(),
327             lo: lo.line + offset,
328             hi: hi.line + offset,
329         }
330     }
331 }
332
333 #[cfg(test)]
334 mod tests {
335     use super::*;
336
337     use rustfmt_config_proc_macro::nightly_only_test;
338
339     mod emitter {
340         use super::*;
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;
347
348         struct TestEmitter {
349             num_emitted_errors: Lrc<AtomicU32>,
350         }
351
352         impl Translate for TestEmitter {
353             fn fluent_bundle(&self) -> Option<&Lrc<rustc_errors::FluentBundle>> {
354                 None
355             }
356
357             fn fallback_fluent_bundle(&self) -> &rustc_errors::FluentBundle {
358                 panic!("test emitter attempted to translate a diagnostic");
359             }
360         }
361
362         impl Emitter for TestEmitter {
363             fn source_map(&self) -> Option<&Lrc<SourceMap>> {
364                 None
365             }
366
367             fn emit_diagnostic(&mut self, _db: &Diagnostic) {
368                 self.num_emitted_errors.fetch_add(1, Ordering::Release);
369             }
370         }
371
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 {
376                 diag.span = span;
377             }
378             diag
379         }
380
381         fn build_emitter(
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 };
388             let source_map =
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(),
392             );
393             SilentOnIgnoredFilesEmitter {
394                 has_non_ignorable_parser_errors: false,
395                 source_map,
396                 emitter: Box::new(emitter_writer),
397                 ignore_path_set,
398                 can_reset,
399             }
400         }
401
402         fn get_ignore_list(config: &str) -> IgnoreList {
403             Config::from_toml(config, Path::new("")).unwrap().ignore()
404         }
405
406         #[test]
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()));
412             let source =
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"))),
416                 source,
417             );
418             let mut emitter = build_emitter(
419                 Lrc::clone(&num_emitted_errors),
420                 Lrc::clone(&can_reset_errors),
421                 Some(Lrc::clone(&source_map)),
422                 Some(ignore_list),
423             );
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);
429         }
430
431         #[nightly_only_test]
432         #[test]
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"))),
441                 source,
442             );
443             let mut emitter = build_emitter(
444                 Lrc::clone(&num_emitted_errors),
445                 Lrc::clone(&can_reset_errors),
446                 Some(Lrc::clone(&source_map)),
447                 Some(ignore_list),
448             );
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);
454         }
455
456         #[nightly_only_test]
457         #[test]
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"))),
465                 source,
466             );
467             let mut emitter = build_emitter(
468                 Lrc::clone(&num_emitted_errors),
469                 Lrc::clone(&can_reset_errors),
470                 Some(Lrc::clone(&source_map)),
471                 None,
472             );
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);
478         }
479
480         #[nightly_only_test]
481         #[test]
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; }"#);
489             let fatal_source =
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"))),
493                 bar_source,
494             );
495             source_map.new_source_file(
496                 SourceMapFileName::Real(RealFileName::LocalPath(PathBuf::from("foo.rs"))),
497                 foo_source,
498             );
499             source_map.new_source_file(
500                 SourceMapFileName::Real(RealFileName::LocalPath(PathBuf::from("fatal.rs"))),
501                 fatal_source,
502             );
503             let mut emitter = build_emitter(
504                 Lrc::clone(&num_emitted_errors),
505                 Lrc::clone(&can_reset_errors),
506                 Some(Lrc::clone(&source_map)),
507                 Some(ignore_list),
508             );
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);
519         }
520     }
521 }