]> git.lizzy.rs Git - rust.git/blob - src/tools/rustfmt/src/syntux/session.rs
Rollup merge of #88353 - jhpratt:stabilize-array-as-ref, r=joshtriplett
[rust.git] / src / tools / rustfmt / src / syntux / 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::{ColorConfig, Diagnostic, Handler, Level as DiagnosticLevel};
7 use rustc_session::parse::ParseSess as RawParseSess;
8 use rustc_span::{
9     source_map::{FilePathMapping, SourceMap},
10     symbol, BytePos, Span,
11 };
12
13 use crate::config::file_lines::LineRange;
14 use crate::ignore_path::IgnorePathSet;
15 use crate::source_map::LineRangeUtils;
16 use crate::utils::starts_with_newline;
17 use crate::visitor::SnippetProvider;
18 use crate::{Config, ErrorKind, FileName};
19
20 /// ParseSess holds structs necessary for constructing a parser.
21 pub(crate) struct ParseSess {
22     parse_sess: RawParseSess,
23     ignore_path_set: Lrc<IgnorePathSet>,
24     can_reset_errors: Lrc<AtomicBool>,
25 }
26
27 /// Emitter which discards every error.
28 struct SilentEmitter;
29
30 impl Emitter for SilentEmitter {
31     fn source_map(&self) -> Option<&Lrc<SourceMap>> {
32         None
33     }
34     fn emit_diagnostic(&mut self, _db: &Diagnostic) {}
35 }
36
37 fn silent_emitter() -> Box<dyn Emitter + Send> {
38     Box::new(SilentEmitter {})
39 }
40
41 /// Emit errors against every files expect ones specified in the `ignore_path_set`.
42 struct SilentOnIgnoredFilesEmitter {
43     ignore_path_set: Lrc<IgnorePathSet>,
44     source_map: Lrc<SourceMap>,
45     emitter: Box<dyn Emitter + Send>,
46     has_non_ignorable_parser_errors: bool,
47     can_reset: Lrc<AtomicBool>,
48 }
49
50 impl SilentOnIgnoredFilesEmitter {
51     fn handle_non_ignoreable_error(&mut self, db: &Diagnostic) {
52         self.has_non_ignorable_parser_errors = true;
53         self.can_reset.store(false, Ordering::Release);
54         self.emitter.emit_diagnostic(db);
55     }
56 }
57
58 impl Emitter for SilentOnIgnoredFilesEmitter {
59     fn source_map(&self) -> Option<&Lrc<SourceMap>> {
60         None
61     }
62     fn emit_diagnostic(&mut self, db: &Diagnostic) {
63         if db.level == DiagnosticLevel::Fatal {
64             return self.handle_non_ignoreable_error(db);
65         }
66         if let Some(primary_span) = &db.span.primary_span() {
67             let file_name = self.source_map.span_to_filename(*primary_span);
68             if let rustc_span::FileName::Real(rustc_span::RealFileName::LocalPath(ref path)) =
69                 file_name
70             {
71                 if self
72                     .ignore_path_set
73                     .is_match(&FileName::Real(path.to_path_buf()))
74                 {
75                     if !self.has_non_ignorable_parser_errors {
76                         self.can_reset.store(true, Ordering::Release);
77                     }
78                     return;
79                 }
80             };
81         }
82         self.handle_non_ignoreable_error(db);
83     }
84 }
85
86 fn default_handler(
87     source_map: Lrc<SourceMap>,
88     ignore_path_set: Lrc<IgnorePathSet>,
89     can_reset: Lrc<AtomicBool>,
90     hide_parse_errors: bool,
91 ) -> Handler {
92     let supports_color = term::stderr().map_or(false, |term| term.supports_color());
93     let color_cfg = if supports_color {
94         ColorConfig::Auto
95     } else {
96         ColorConfig::Never
97     };
98
99     let emitter = if hide_parse_errors {
100         silent_emitter()
101     } else {
102         Box::new(EmitterWriter::stderr(
103             color_cfg,
104             Some(source_map.clone()),
105             false,
106             false,
107             None,
108             false,
109         ))
110     };
111     Handler::with_emitter(
112         true,
113         None,
114         Box::new(SilentOnIgnoredFilesEmitter {
115             has_non_ignorable_parser_errors: false,
116             source_map,
117             emitter,
118             ignore_path_set,
119             can_reset,
120         }),
121     )
122 }
123
124 impl ParseSess {
125     pub(crate) fn new(config: &Config) -> Result<ParseSess, ErrorKind> {
126         let ignore_path_set = match IgnorePathSet::from_ignore_list(&config.ignore()) {
127             Ok(ignore_path_set) => Lrc::new(ignore_path_set),
128             Err(e) => return Err(ErrorKind::InvalidGlobPattern(e)),
129         };
130         let source_map = Lrc::new(SourceMap::new(FilePathMapping::empty()));
131         let can_reset_errors = Lrc::new(AtomicBool::new(false));
132
133         let handler = default_handler(
134             Lrc::clone(&source_map),
135             Lrc::clone(&ignore_path_set),
136             Lrc::clone(&can_reset_errors),
137             config.hide_parse_errors(),
138         );
139         let parse_sess = RawParseSess::with_span_handler(handler, source_map);
140
141         Ok(ParseSess {
142             parse_sess,
143             ignore_path_set,
144             can_reset_errors,
145         })
146     }
147
148     pub(crate) fn default_submod_path(
149         &self,
150         id: symbol::Ident,
151         relative: Option<symbol::Ident>,
152         dir_path: &Path,
153     ) -> Result<rustc_expand::module::ModulePathSuccess, rustc_expand::module::ModError<'_>> {
154         rustc_expand::module::default_submod_path(&self.parse_sess, id, relative, dir_path)
155     }
156
157     pub(crate) fn is_file_parsed(&self, path: &Path) -> bool {
158         self.parse_sess
159             .source_map()
160             .get_source_file(&rustc_span::FileName::Real(
161                 rustc_span::RealFileName::LocalPath(path.to_path_buf()),
162             ))
163             .is_some()
164     }
165
166     pub(crate) fn ignore_file(&self, path: &FileName) -> bool {
167         self.ignore_path_set.as_ref().is_match(&path)
168     }
169
170     pub(crate) fn set_silent_emitter(&mut self) {
171         self.parse_sess.span_diagnostic = Handler::with_emitter(true, None, silent_emitter());
172     }
173
174     pub(crate) fn span_to_filename(&self, span: Span) -> FileName {
175         self.parse_sess.source_map().span_to_filename(span).into()
176     }
177
178     pub(crate) fn span_to_first_line_string(&self, span: Span) -> String {
179         let file_lines = self.parse_sess.source_map().span_to_lines(span).ok();
180
181         match file_lines {
182             Some(fl) => fl
183                 .file
184                 .get_line(fl.lines[0].line_index)
185                 .map_or_else(String::new, |s| s.to_string()),
186             None => String::new(),
187         }
188     }
189
190     pub(crate) fn line_of_byte_pos(&self, pos: BytePos) -> usize {
191         self.parse_sess.source_map().lookup_char_pos(pos).line
192     }
193
194     pub(crate) fn span_to_debug_info(&self, span: Span) -> String {
195         self.parse_sess.source_map().span_to_diagnostic_string(span)
196     }
197
198     pub(crate) fn inner(&self) -> &RawParseSess {
199         &self.parse_sess
200     }
201
202     pub(crate) fn snippet_provider(&self, span: Span) -> SnippetProvider {
203         let source_file = self.parse_sess.source_map().lookup_char_pos(span.lo()).file;
204         SnippetProvider::new(
205             source_file.start_pos,
206             source_file.end_pos,
207             Lrc::clone(source_file.src.as_ref().unwrap()),
208         )
209     }
210
211     pub(crate) fn get_original_snippet(&self, file_name: &FileName) -> Option<Lrc<String>> {
212         self.parse_sess
213             .source_map()
214             .get_source_file(&file_name.into())
215             .and_then(|source_file| source_file.src.clone())
216     }
217 }
218
219 // Methods that should be restricted within the syntux module.
220 impl ParseSess {
221     pub(super) fn emit_diagnostics(&self, diagnostics: Vec<Diagnostic>) {
222         for diagnostic in diagnostics {
223             self.parse_sess.span_diagnostic.emit_diagnostic(&diagnostic);
224         }
225     }
226
227     pub(crate) fn emit_or_cancel_diagnostic(&self, diagnostic: &mut Diagnostic) {
228         self.parse_sess.span_diagnostic.emit_diagnostic(diagnostic);
229         // The Handler will check whether the diagnostic should be emitted
230         // based on the user's rustfmt configuration and the originating file
231         // that caused the parser error. If the Handler determined it should skip
232         // emission then we need to ensure the diagnostic is cancelled.
233         if !diagnostic.cancelled() {
234             diagnostic.cancel();
235         }
236     }
237
238     pub(super) fn can_reset_errors(&self) -> bool {
239         self.can_reset_errors.load(Ordering::Acquire)
240     }
241
242     pub(super) fn has_errors(&self) -> bool {
243         self.parse_sess.span_diagnostic.has_errors()
244     }
245
246     pub(super) fn reset_errors(&self) {
247         self.parse_sess.span_diagnostic.reset_err_count();
248     }
249 }
250
251 impl LineRangeUtils for ParseSess {
252     fn lookup_line_range(&self, span: Span) -> LineRange {
253         let snippet = self
254             .parse_sess
255             .source_map()
256             .span_to_snippet(span)
257             .unwrap_or_default();
258         let lo = self.parse_sess.source_map().lookup_line(span.lo()).unwrap();
259         let hi = self.parse_sess.source_map().lookup_line(span.hi()).unwrap();
260
261         debug_assert_eq!(
262             lo.sf.name, hi.sf.name,
263             "span crossed file boundary: lo: {:?}, hi: {:?}",
264             lo, hi
265         );
266
267         // in case the span starts with a newline, the line range is off by 1 without the
268         // adjustment below
269         let offset = 1 + if starts_with_newline(&snippet) { 1 } else { 0 };
270         // Line numbers start at 1
271         LineRange {
272             file: lo.sf.clone(),
273             lo: lo.line + offset,
274             hi: hi.line + offset,
275         }
276     }
277 }
278
279 #[cfg(test)]
280 mod tests {
281     use super::*;
282
283     mod emitter {
284         use super::*;
285         use crate::config::IgnoreList;
286         use crate::is_nightly_channel;
287         use crate::utils::mk_sp;
288         use rustc_span::{FileName as SourceMapFileName, MultiSpan, RealFileName, DUMMY_SP};
289         use std::path::PathBuf;
290         use std::sync::atomic::AtomicU32;
291
292         struct TestEmitter {
293             num_emitted_errors: Lrc<AtomicU32>,
294         }
295
296         impl Emitter for TestEmitter {
297             fn source_map(&self) -> Option<&Lrc<SourceMap>> {
298                 None
299             }
300             fn emit_diagnostic(&mut self, _db: &Diagnostic) {
301                 self.num_emitted_errors.fetch_add(1, Ordering::Release);
302             }
303         }
304
305         fn build_diagnostic(level: DiagnosticLevel, span: Option<MultiSpan>) -> Diagnostic {
306             Diagnostic {
307                 level,
308                 code: None,
309                 message: vec![],
310                 children: vec![],
311                 suggestions: vec![],
312                 span: span.unwrap_or_else(MultiSpan::new),
313                 sort_span: DUMMY_SP,
314                 is_lint: false,
315             }
316         }
317
318         fn build_emitter(
319             num_emitted_errors: Lrc<AtomicU32>,
320             can_reset: Lrc<AtomicBool>,
321             source_map: Option<Lrc<SourceMap>>,
322             ignore_list: Option<IgnoreList>,
323         ) -> SilentOnIgnoredFilesEmitter {
324             let emitter_writer = TestEmitter { num_emitted_errors };
325             let source_map =
326                 source_map.unwrap_or_else(|| Lrc::new(SourceMap::new(FilePathMapping::empty())));
327             let ignore_path_set = Lrc::new(
328                 IgnorePathSet::from_ignore_list(&ignore_list.unwrap_or_default()).unwrap(),
329             );
330             SilentOnIgnoredFilesEmitter {
331                 has_non_ignorable_parser_errors: false,
332                 source_map,
333                 emitter: Box::new(emitter_writer),
334                 ignore_path_set,
335                 can_reset,
336             }
337         }
338
339         fn get_ignore_list(config: &str) -> IgnoreList {
340             Config::from_toml(config, Path::new("")).unwrap().ignore()
341         }
342
343         #[test]
344         fn handles_fatal_parse_error_in_ignored_file() {
345             let num_emitted_errors = Lrc::new(AtomicU32::new(0));
346             let can_reset_errors = Lrc::new(AtomicBool::new(false));
347             let ignore_list = get_ignore_list(r#"ignore = ["foo.rs"]"#);
348             let source_map = Lrc::new(SourceMap::new(FilePathMapping::empty()));
349             let source =
350                 String::from(r#"extern "system" fn jni_symbol!( funcName ) ( ... ) -> {} "#);
351             source_map.new_source_file(
352                 SourceMapFileName::Real(RealFileName::LocalPath(PathBuf::from("foo.rs"))),
353                 source,
354             );
355             let mut emitter = build_emitter(
356                 Lrc::clone(&num_emitted_errors),
357                 Lrc::clone(&can_reset_errors),
358                 Some(Lrc::clone(&source_map)),
359                 Some(ignore_list),
360             );
361             let span = MultiSpan::from_span(mk_sp(BytePos(0), BytePos(1)));
362             let fatal_diagnostic = build_diagnostic(DiagnosticLevel::Fatal, Some(span));
363             emitter.emit_diagnostic(&fatal_diagnostic);
364             assert_eq!(num_emitted_errors.load(Ordering::Acquire), 1);
365             assert_eq!(can_reset_errors.load(Ordering::Acquire), false);
366         }
367
368         #[test]
369         fn handles_recoverable_parse_error_in_ignored_file() {
370             if !is_nightly_channel!() {
371                 return;
372             }
373             let num_emitted_errors = Lrc::new(AtomicU32::new(0));
374             let can_reset_errors = Lrc::new(AtomicBool::new(false));
375             let ignore_list = get_ignore_list(r#"ignore = ["foo.rs"]"#);
376             let source_map = Lrc::new(SourceMap::new(FilePathMapping::empty()));
377             let source = String::from(r#"pub fn bar() { 1x; }"#);
378             source_map.new_source_file(
379                 SourceMapFileName::Real(RealFileName::LocalPath(PathBuf::from("foo.rs"))),
380                 source,
381             );
382             let mut emitter = build_emitter(
383                 Lrc::clone(&num_emitted_errors),
384                 Lrc::clone(&can_reset_errors),
385                 Some(Lrc::clone(&source_map)),
386                 Some(ignore_list),
387             );
388             let span = MultiSpan::from_span(mk_sp(BytePos(0), BytePos(1)));
389             let non_fatal_diagnostic = build_diagnostic(DiagnosticLevel::Warning, Some(span));
390             emitter.emit_diagnostic(&non_fatal_diagnostic);
391             assert_eq!(num_emitted_errors.load(Ordering::Acquire), 0);
392             assert_eq!(can_reset_errors.load(Ordering::Acquire), true);
393         }
394
395         #[test]
396         fn handles_recoverable_parse_error_in_non_ignored_file() {
397             if !is_nightly_channel!() {
398                 return;
399             }
400             let num_emitted_errors = Lrc::new(AtomicU32::new(0));
401             let can_reset_errors = Lrc::new(AtomicBool::new(false));
402             let source_map = Lrc::new(SourceMap::new(FilePathMapping::empty()));
403             let source = String::from(r#"pub fn bar() { 1x; }"#);
404             source_map.new_source_file(
405                 SourceMapFileName::Real(RealFileName::LocalPath(PathBuf::from("foo.rs"))),
406                 source,
407             );
408             let mut emitter = build_emitter(
409                 Lrc::clone(&num_emitted_errors),
410                 Lrc::clone(&can_reset_errors),
411                 Some(Lrc::clone(&source_map)),
412                 None,
413             );
414             let span = MultiSpan::from_span(mk_sp(BytePos(0), BytePos(1)));
415             let non_fatal_diagnostic = build_diagnostic(DiagnosticLevel::Warning, Some(span));
416             emitter.emit_diagnostic(&non_fatal_diagnostic);
417             assert_eq!(num_emitted_errors.load(Ordering::Acquire), 1);
418             assert_eq!(can_reset_errors.load(Ordering::Acquire), false);
419         }
420
421         #[test]
422         fn handles_mix_of_recoverable_parse_error() {
423             if !is_nightly_channel!() {
424                 return;
425             }
426             let num_emitted_errors = Lrc::new(AtomicU32::new(0));
427             let can_reset_errors = Lrc::new(AtomicBool::new(false));
428             let source_map = Lrc::new(SourceMap::new(FilePathMapping::empty()));
429             let ignore_list = get_ignore_list(r#"ignore = ["foo.rs"]"#);
430             let bar_source = String::from(r#"pub fn bar() { 1x; }"#);
431             let foo_source = String::from(r#"pub fn foo() { 1x; }"#);
432             let fatal_source =
433                 String::from(r#"extern "system" fn jni_symbol!( funcName ) ( ... ) -> {} "#);
434             source_map.new_source_file(
435                 SourceMapFileName::Real(RealFileName::LocalPath(PathBuf::from("bar.rs"))),
436                 bar_source,
437             );
438             source_map.new_source_file(
439                 SourceMapFileName::Real(RealFileName::LocalPath(PathBuf::from("foo.rs"))),
440                 foo_source,
441             );
442             source_map.new_source_file(
443                 SourceMapFileName::Real(RealFileName::LocalPath(PathBuf::from("fatal.rs"))),
444                 fatal_source,
445             );
446             let mut emitter = build_emitter(
447                 Lrc::clone(&num_emitted_errors),
448                 Lrc::clone(&can_reset_errors),
449                 Some(Lrc::clone(&source_map)),
450                 Some(ignore_list),
451             );
452             let bar_span = MultiSpan::from_span(mk_sp(BytePos(0), BytePos(1)));
453             let foo_span = MultiSpan::from_span(mk_sp(BytePos(21), BytePos(22)));
454             let bar_diagnostic = build_diagnostic(DiagnosticLevel::Warning, Some(bar_span));
455             let foo_diagnostic = build_diagnostic(DiagnosticLevel::Warning, Some(foo_span));
456             let fatal_diagnostic = build_diagnostic(DiagnosticLevel::Fatal, None);
457             emitter.emit_diagnostic(&bar_diagnostic);
458             emitter.emit_diagnostic(&foo_diagnostic);
459             emitter.emit_diagnostic(&fatal_diagnostic);
460             assert_eq!(num_emitted_errors.load(Ordering::Acquire), 2);
461             assert_eq!(can_reset_errors.load(Ordering::Acquire), false);
462         }
463     }
464 }