]> git.lizzy.rs Git - rust.git/blob - src/syntux/session.rs
Merge pull request #4100 from calebcartwright/rustfmt1x-rustc-v650
[rust.git] / src / syntux / session.rs
1 use std::cell::RefCell;
2 use std::path::Path;
3 use std::rc::Rc;
4
5 use rustc_ast::ast;
6 use rustc_data_structures::sync::{Lrc, Send};
7 use rustc_errors::emitter::{Emitter, EmitterWriter};
8 use rustc_errors::{ColorConfig, Diagnostic, Handler, Level as DiagnosticLevel};
9 use rustc_session::parse::ParseSess as RawParseSess;
10 use rustc_span::{
11     source_map::{FilePathMapping, SourceMap},
12     BytePos, Span,
13 };
14
15 use crate::config::file_lines::LineRange;
16 use crate::ignore_path::IgnorePathSet;
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: Rc<IgnorePathSet>,
26     can_reset_errors: Rc<RefCell<bool>>,
27 }
28
29 /// Emitter which discards every error.
30 struct SilentEmitter;
31
32 impl Emitter for SilentEmitter {
33     fn source_map(&self) -> Option<&Lrc<SourceMap>> {
34         None
35     }
36     fn emit_diagnostic(&mut self, _db: &Diagnostic) {}
37 }
38
39 fn silent_emitter() -> Box<dyn Emitter + Send> {
40     Box::new(SilentEmitter {})
41 }
42
43 /// Emit errors against every files expect ones specified in the `ignore_path_set`.
44 struct SilentOnIgnoredFilesEmitter {
45     ignore_path_set: Rc<IgnorePathSet>,
46     source_map: Rc<SourceMap>,
47     emitter: Box<dyn Emitter + Send>,
48     has_non_ignorable_parser_errors: bool,
49     can_reset: Rc<RefCell<bool>>,
50 }
51
52 impl SilentOnIgnoredFilesEmitter {
53     fn handle_non_ignoreable_error(&mut self, db: &Diagnostic) {
54         self.has_non_ignorable_parser_errors = true;
55         *self.can_reset.borrow_mut() = false;
56         self.emitter.emit_diagnostic(db);
57     }
58 }
59
60 impl Emitter for SilentOnIgnoredFilesEmitter {
61     fn source_map(&self) -> Option<&Lrc<SourceMap>> {
62         None
63     }
64     fn emit_diagnostic(&mut self, db: &Diagnostic) {
65         if db.level == DiagnosticLevel::Fatal {
66             return self.handle_non_ignoreable_error(db);
67         }
68         if let Some(primary_span) = &db.span.primary_span() {
69             let file_name = self.source_map.span_to_filename(*primary_span);
70             if let rustc_span::FileName::Real(ref path) = file_name {
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.borrow_mut() = true;
77                     }
78                     return;
79                 }
80             };
81         }
82         self.handle_non_ignoreable_error(db);
83     }
84 }
85
86 fn default_handler(
87     source_map: Rc<SourceMap>,
88     ignore_path_set: Rc<IgnorePathSet>,
89     can_reset: Rc<RefCell<bool>>,
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) => Rc::new(ignore_path_set),
128             Err(e) => return Err(ErrorKind::InvalidGlobPattern(e)),
129         };
130         let source_map = Rc::new(SourceMap::new(FilePathMapping::empty()));
131         let can_reset_errors = Rc::new(RefCell::new(false));
132
133         let handler = default_handler(
134             Rc::clone(&source_map),
135             Rc::clone(&ignore_path_set),
136             Rc::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: ast::Ident,
151         relative: Option<ast::Ident>,
152         dir_path: &Path,
153     ) -> rustc_expand::module::ModulePath<'_> {
154         rustc_expand::module::default_submod_path(
155             &self.parse_sess,
156             id,
157             rustc_span::DUMMY_SP,
158             relative,
159             dir_path,
160         )
161     }
162
163     pub(crate) fn is_file_parsed(&self, path: &Path) -> bool {
164         self.parse_sess
165             .source_map()
166             .get_source_file(&rustc_span::FileName::Real(path.to_path_buf()))
167             .is_some()
168     }
169
170     pub(crate) fn ignore_file(&self, path: &FileName) -> bool {
171         self.ignore_path_set.as_ref().is_match(&path)
172     }
173
174     pub(crate) fn set_silent_emitter(&mut self) {
175         self.parse_sess.span_diagnostic = Handler::with_emitter(true, None, silent_emitter());
176     }
177
178     pub(crate) fn span_to_filename(&self, span: Span) -> FileName {
179         self.parse_sess.source_map().span_to_filename(span).into()
180     }
181
182     pub(crate) fn span_to_first_line_string(&self, span: Span) -> String {
183         let file_lines = self.parse_sess.source_map().span_to_lines(span).ok();
184
185         match file_lines {
186             Some(fl) => fl
187                 .file
188                 .get_line(fl.lines[0].line_index)
189                 .map_or_else(String::new, |s| s.to_string()),
190             None => String::new(),
191         }
192     }
193
194     pub(crate) fn line_of_byte_pos(&self, pos: BytePos) -> usize {
195         self.parse_sess.source_map().lookup_char_pos(pos).line
196     }
197
198     pub(crate) fn span_to_debug_info(&self, span: Span) -> String {
199         self.parse_sess.source_map().span_to_string(span)
200     }
201
202     pub(crate) fn inner(&self) -> &RawParseSess {
203         &self.parse_sess
204     }
205
206     pub(crate) fn snippet_provider(&self, span: Span) -> SnippetProvider {
207         let source_file = self.parse_sess.source_map().lookup_char_pos(span.lo()).file;
208         SnippetProvider::new(
209             source_file.start_pos,
210             source_file.end_pos,
211             Rc::clone(source_file.src.as_ref().unwrap()),
212         )
213     }
214
215     pub(crate) fn get_original_snippet(&self, file_name: &FileName) -> Option<Rc<String>> {
216         self.parse_sess
217             .source_map()
218             .get_source_file(&file_name.into())
219             .and_then(|source_file| source_file.src.clone())
220     }
221 }
222
223 // Methods that should be restricted within the syntux module.
224 impl ParseSess {
225     pub(super) fn emit_diagnostics(&self, diagnostics: Vec<Diagnostic>) {
226         for diagnostic in diagnostics {
227             self.parse_sess.span_diagnostic.emit_diagnostic(&diagnostic);
228         }
229     }
230
231     pub(super) fn can_reset_errors(&self) -> bool {
232         *self.can_reset_errors.borrow()
233     }
234
235     pub(super) fn has_errors(&self) -> bool {
236         self.parse_sess.span_diagnostic.has_errors()
237     }
238
239     pub(super) fn reset_errors(&self) {
240         self.parse_sess.span_diagnostic.reset_err_count();
241     }
242 }
243
244 impl LineRangeUtils for ParseSess {
245     fn lookup_line_range(&self, span: Span) -> LineRange {
246         let snippet = self
247             .parse_sess
248             .source_map()
249             .span_to_snippet(span)
250             .unwrap_or_default();
251         let lo = self.parse_sess.source_map().lookup_line(span.lo()).unwrap();
252         let hi = self.parse_sess.source_map().lookup_line(span.hi()).unwrap();
253
254         debug_assert_eq!(
255             lo.sf.name, hi.sf.name,
256             "span crossed file boundary: lo: {:?}, hi: {:?}",
257             lo, hi
258         );
259
260         // in case the span starts with a newline, the line range is off by 1 without the
261         // adjustment below
262         let offset = 1 + if starts_with_newline(&snippet) { 1 } else { 0 };
263         // Line numbers start at 1
264         LineRange {
265             file: lo.sf.clone(),
266             lo: lo.line + offset,
267             hi: hi.line + offset,
268         }
269     }
270 }
271
272 #[cfg(test)]
273 mod tests {
274     use super::*;
275
276     mod emitter {
277         use super::*;
278         use crate::config::IgnoreList;
279         use crate::is_nightly_channel;
280         use crate::utils::mk_sp;
281         use rustc_span::{FileName as SourceMapFileName, MultiSpan, DUMMY_SP};
282         use std::path::PathBuf;
283
284         struct TestEmitter {
285             num_emitted_errors: Rc<RefCell<u32>>,
286         }
287
288         impl Emitter for TestEmitter {
289             fn source_map(&self) -> Option<&Lrc<SourceMap>> {
290                 None
291             }
292             fn emit_diagnostic(&mut self, _db: &Diagnostic) {
293                 *self.num_emitted_errors.borrow_mut() += 1;
294             }
295         }
296
297         fn build_diagnostic(level: DiagnosticLevel, span: Option<MultiSpan>) -> Diagnostic {
298             Diagnostic {
299                 level,
300                 code: None,
301                 message: vec![],
302                 children: vec![],
303                 suggestions: vec![],
304                 span: span.unwrap_or_else(MultiSpan::new),
305                 sort_span: DUMMY_SP,
306             }
307         }
308
309         fn build_emitter(
310             num_emitted_errors: Rc<RefCell<u32>>,
311             can_reset: Rc<RefCell<bool>>,
312             source_map: Option<Rc<SourceMap>>,
313             ignore_list: Option<IgnoreList>,
314         ) -> SilentOnIgnoredFilesEmitter {
315             let emitter_writer = TestEmitter { num_emitted_errors };
316             let source_map =
317                 source_map.unwrap_or_else(|| Rc::new(SourceMap::new(FilePathMapping::empty())));
318             let ignore_path_set =
319                 Rc::new(IgnorePathSet::from_ignore_list(&ignore_list.unwrap_or_default()).unwrap());
320             SilentOnIgnoredFilesEmitter {
321                 has_non_ignorable_parser_errors: false,
322                 source_map,
323                 emitter: Box::new(emitter_writer),
324                 ignore_path_set,
325                 can_reset,
326             }
327         }
328
329         fn get_ignore_list(config: &str) -> IgnoreList {
330             Config::from_toml(config, Path::new("")).unwrap().ignore()
331         }
332
333         #[test]
334         fn handles_fatal_parse_error_in_ignored_file() {
335             let num_emitted_errors = Rc::new(RefCell::new(0));
336             let can_reset_errors = Rc::new(RefCell::new(false));
337             let ignore_list = get_ignore_list(r#"ignore = ["foo.rs"]"#);
338             let source_map = Rc::new(SourceMap::new(FilePathMapping::empty()));
339             let source =
340                 String::from(r#"extern "system" fn jni_symbol!( funcName ) ( ... ) -> {} "#);
341             source_map.new_source_file(SourceMapFileName::Real(PathBuf::from("foo.rs")), source);
342             let mut emitter = build_emitter(
343                 Rc::clone(&num_emitted_errors),
344                 Rc::clone(&can_reset_errors),
345                 Some(Rc::clone(&source_map)),
346                 Some(ignore_list),
347             );
348             let span = MultiSpan::from_span(mk_sp(BytePos(0), BytePos(1)));
349             let fatal_diagnostic = build_diagnostic(DiagnosticLevel::Fatal, Some(span));
350             emitter.emit_diagnostic(&fatal_diagnostic);
351             assert_eq!(*num_emitted_errors.borrow(), 1);
352             assert_eq!(*can_reset_errors.borrow(), false);
353         }
354
355         #[test]
356         fn handles_recoverable_parse_error_in_ignored_file() {
357             if !is_nightly_channel!() {
358                 return;
359             }
360             let num_emitted_errors = Rc::new(RefCell::new(0));
361             let can_reset_errors = Rc::new(RefCell::new(false));
362             let ignore_list = get_ignore_list(r#"ignore = ["foo.rs"]"#);
363             let source_map = Rc::new(SourceMap::new(FilePathMapping::empty()));
364             let source = String::from(r#"pub fn bar() { 1x; }"#);
365             source_map.new_source_file(SourceMapFileName::Real(PathBuf::from("foo.rs")), source);
366             let mut emitter = build_emitter(
367                 Rc::clone(&num_emitted_errors),
368                 Rc::clone(&can_reset_errors),
369                 Some(Rc::clone(&source_map)),
370                 Some(ignore_list),
371             );
372             let span = MultiSpan::from_span(mk_sp(BytePos(0), BytePos(1)));
373             let non_fatal_diagnostic = build_diagnostic(DiagnosticLevel::Warning, Some(span));
374             emitter.emit_diagnostic(&non_fatal_diagnostic);
375             assert_eq!(*num_emitted_errors.borrow(), 0);
376             assert_eq!(*can_reset_errors.borrow(), true);
377         }
378
379         #[test]
380         fn handles_recoverable_parse_error_in_non_ignored_file() {
381             if !is_nightly_channel!() {
382                 return;
383             }
384             let num_emitted_errors = Rc::new(RefCell::new(0));
385             let can_reset_errors = Rc::new(RefCell::new(false));
386             let source_map = Rc::new(SourceMap::new(FilePathMapping::empty()));
387             let source = String::from(r#"pub fn bar() { 1x; }"#);
388             source_map.new_source_file(SourceMapFileName::Real(PathBuf::from("foo.rs")), source);
389             let mut emitter = build_emitter(
390                 Rc::clone(&num_emitted_errors),
391                 Rc::clone(&can_reset_errors),
392                 Some(Rc::clone(&source_map)),
393                 None,
394             );
395             let span = MultiSpan::from_span(mk_sp(BytePos(0), BytePos(1)));
396             let non_fatal_diagnostic = build_diagnostic(DiagnosticLevel::Warning, Some(span));
397             emitter.emit_diagnostic(&non_fatal_diagnostic);
398             assert_eq!(*num_emitted_errors.borrow(), 1);
399             assert_eq!(*can_reset_errors.borrow(), false);
400         }
401
402         #[test]
403         fn handles_mix_of_recoverable_parse_error() {
404             if !is_nightly_channel!() {
405                 return;
406             }
407             let num_emitted_errors = Rc::new(RefCell::new(0));
408             let can_reset_errors = Rc::new(RefCell::new(false));
409             let source_map = Rc::new(SourceMap::new(FilePathMapping::empty()));
410             let ignore_list = get_ignore_list(r#"ignore = ["foo.rs"]"#);
411             let bar_source = String::from(r#"pub fn bar() { 1x; }"#);
412             let foo_source = String::from(r#"pub fn foo() { 1x; }"#);
413             let fatal_source =
414                 String::from(r#"extern "system" fn jni_symbol!( funcName ) ( ... ) -> {} "#);
415             source_map
416                 .new_source_file(SourceMapFileName::Real(PathBuf::from("bar.rs")), bar_source);
417             source_map
418                 .new_source_file(SourceMapFileName::Real(PathBuf::from("foo.rs")), foo_source);
419             source_map.new_source_file(
420                 SourceMapFileName::Real(PathBuf::from("fatal.rs")),
421                 fatal_source,
422             );
423             let mut emitter = build_emitter(
424                 Rc::clone(&num_emitted_errors),
425                 Rc::clone(&can_reset_errors),
426                 Some(Rc::clone(&source_map)),
427                 Some(ignore_list),
428             );
429             let bar_span = MultiSpan::from_span(mk_sp(BytePos(0), BytePos(1)));
430             let foo_span = MultiSpan::from_span(mk_sp(BytePos(21), BytePos(22)));
431             let bar_diagnostic = build_diagnostic(DiagnosticLevel::Warning, Some(bar_span));
432             let foo_diagnostic = build_diagnostic(DiagnosticLevel::Warning, Some(foo_span));
433             let fatal_diagnostic = build_diagnostic(DiagnosticLevel::Fatal, None);
434             emitter.emit_diagnostic(&bar_diagnostic);
435             emitter.emit_diagnostic(&foo_diagnostic);
436             emitter.emit_diagnostic(&fatal_diagnostic);
437             assert_eq!(*num_emitted_errors.borrow(), 2);
438             assert_eq!(*can_reset_errors.borrow(), false);
439         }
440     }
441 }