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