1 use std::cell::RefCell;
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;
11 source_map::{FilePathMapping, SourceMap},
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};
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>>,
29 /// Emitter which discards every error.
32 impl Emitter for SilentEmitter {
33 fn source_map(&self) -> Option<&Lrc<SourceMap>> {
36 fn emit_diagnostic(&mut self, _db: &Diagnostic) {}
39 fn silent_emitter() -> Box<dyn Emitter + Send> {
40 Box::new(SilentEmitter {})
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>>,
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);
60 impl Emitter for SilentOnIgnoredFilesEmitter {
61 fn source_map(&self) -> Option<&Lrc<SourceMap>> {
64 fn emit_diagnostic(&mut self, db: &Diagnostic) {
65 if db.level == DiagnosticLevel::Fatal {
66 return self.handle_non_ignoreable_error(db);
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 {
73 .is_match(&FileName::Real(path.to_path_buf()))
75 if !self.has_non_ignorable_parser_errors {
76 *self.can_reset.borrow_mut() = true;
82 self.handle_non_ignoreable_error(db);
87 source_map: Rc<SourceMap>,
88 ignore_path_set: Rc<IgnorePathSet>,
89 can_reset: Rc<RefCell<bool>>,
90 hide_parse_errors: bool,
92 let supports_color = term::stderr().map_or(false, |term| term.supports_color());
93 let color_cfg = if supports_color {
99 let emitter = if hide_parse_errors {
102 Box::new(EmitterWriter::stderr(
104 Some(source_map.clone()),
111 Handler::with_emitter(
114 Box::new(SilentOnIgnoredFilesEmitter {
115 has_non_ignorable_parser_errors: false,
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)),
130 let source_map = Rc::new(SourceMap::new(FilePathMapping::empty()));
131 let can_reset_errors = Rc::new(RefCell::new(false));
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(),
139 let parse_sess = RawParseSess::with_span_handler(handler, source_map);
148 pub(crate) fn default_submod_path(
151 relative: Option<ast::Ident>,
153 ) -> rustc_expand::module::ModulePath<'_> {
154 rustc_expand::module::default_submod_path(
157 rustc_span::DUMMY_SP,
163 pub(crate) fn is_file_parsed(&self, path: &Path) -> bool {
166 .get_source_file(&rustc_span::FileName::Real(path.to_path_buf()))
170 pub(crate) fn ignore_file(&self, path: &FileName) -> bool {
171 self.ignore_path_set.as_ref().is_match(&path)
174 pub(crate) fn set_silent_emitter(&mut self) {
175 self.parse_sess.span_diagnostic = Handler::with_emitter(true, None, silent_emitter());
178 pub(crate) fn span_to_filename(&self, span: Span) -> FileName {
179 self.parse_sess.source_map().span_to_filename(span).into()
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();
188 .get_line(fl.lines[0].line_index)
189 .map_or_else(String::new, |s| s.to_string()),
190 None => String::new(),
194 pub(crate) fn line_of_byte_pos(&self, pos: BytePos) -> usize {
195 self.parse_sess.source_map().lookup_char_pos(pos).line
198 pub(crate) fn span_to_debug_info(&self, span: Span) -> String {
199 self.parse_sess.source_map().span_to_string(span)
202 pub(crate) fn inner(&self) -> &RawParseSess {
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,
211 Rc::clone(source_file.src.as_ref().unwrap()),
215 pub(crate) fn get_original_snippet(&self, file_name: &FileName) -> Option<Rc<String>> {
218 .get_source_file(&file_name.into())
219 .and_then(|source_file| source_file.src.clone())
223 // Methods that should be restricted within the syntux module.
225 pub(super) fn emit_diagnostics(&self, diagnostics: Vec<Diagnostic>) {
226 for diagnostic in diagnostics {
227 self.parse_sess.span_diagnostic.emit_diagnostic(&diagnostic);
231 pub(super) fn can_reset_errors(&self) -> bool {
232 *self.can_reset_errors.borrow()
235 pub(super) fn has_errors(&self) -> bool {
236 self.parse_sess.span_diagnostic.has_errors()
239 pub(super) fn reset_errors(&self) {
240 self.parse_sess.span_diagnostic.reset_err_count();
244 impl LineRangeUtils for ParseSess {
245 fn lookup_line_range(&self, span: Span) -> LineRange {
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();
255 lo.sf.name, hi.sf.name,
256 "span crossed file boundary: lo: {:?}, hi: {:?}",
260 // in case the span starts with a newline, the line range is off by 1 without the
262 let offset = 1 + if starts_with_newline(&snippet) { 1 } else { 0 };
263 // Line numbers start at 1
266 lo: lo.line + offset,
267 hi: hi.line + offset,
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;
285 num_emitted_errors: Rc<RefCell<u32>>,
288 impl Emitter for TestEmitter {
289 fn source_map(&self) -> Option<&Lrc<SourceMap>> {
292 fn emit_diagnostic(&mut self, _db: &Diagnostic) {
293 *self.num_emitted_errors.borrow_mut() += 1;
297 fn build_diagnostic(level: DiagnosticLevel, span: Option<MultiSpan>) -> Diagnostic {
304 span: span.unwrap_or_else(MultiSpan::new),
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 };
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,
323 emitter: Box::new(emitter_writer),
329 fn get_ignore_list(config: &str) -> IgnoreList {
330 Config::from_toml(config, Path::new("")).unwrap().ignore()
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()));
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)),
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);
356 fn handles_recoverable_parse_error_in_ignored_file() {
357 if !is_nightly_channel!() {
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)),
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);
380 fn handles_recoverable_parse_error_in_non_ignored_file() {
381 if !is_nightly_channel!() {
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)),
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);
403 fn handles_mix_of_recoverable_parse_error() {
404 if !is_nightly_channel!() {
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; }"#);
414 String::from(r#"extern "system" fn jni_symbol!( funcName ) ( ... ) -> {} "#);
416 .new_source_file(SourceMapFileName::Real(PathBuf::from("bar.rs")), bar_source);
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")),
423 let mut emitter = build_emitter(
424 Rc::clone(&num_emitted_errors),
425 Rc::clone(&can_reset_errors),
426 Some(Rc::clone(&source_map)),
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);