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 {
source_map(&self) -> Option<&Lrc<SourceMap>>31 fn source_map(&self) -> Option<&Lrc<SourceMap>> {
32 None
33 }
emit_diagnostic(&mut self, _db: &Diagnostic)34 fn emit_diagnostic(&mut self, _db: &Diagnostic) {}
35 }
36
silent_emitter() -> Box<dyn Emitter + Send>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 {
handle_non_ignoreable_error(&mut self, db: &Diagnostic)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 {
source_map(&self) -> Option<&Lrc<SourceMap>>59 fn source_map(&self) -> Option<&Lrc<SourceMap>> {
60 None
61 }
emit_diagnostic(&mut self, db: &Diagnostic)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
default_handler( source_map: Lrc<SourceMap>, ignore_path_set: Lrc<IgnorePathSet>, can_reset: Lrc<AtomicBool>, hide_parse_errors: bool, ) -> Handler86 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 {
new(config: &Config) -> Result<ParseSess, ErrorKind>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
default_submod_path( &self, id: symbol::Ident, relative: Option<symbol::Ident>, dir_path: &Path, ) -> Result<rustc_expand::module::ModulePathSuccess, rustc_expand::module::ModError<'_>>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
is_file_parsed(&self, path: &Path) -> bool157 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
ignore_file(&self, path: &FileName) -> bool166 pub(crate) fn ignore_file(&self, path: &FileName) -> bool {
167 self.ignore_path_set.as_ref().is_match(path)
168 }
169
set_silent_emitter(&mut self)170 pub(crate) fn set_silent_emitter(&mut self) {
171 self.parse_sess.span_diagnostic = Handler::with_emitter(true, None, silent_emitter());
172 }
173
span_to_filename(&self, span: Span) -> FileName174 pub(crate) fn span_to_filename(&self, span: Span) -> FileName {
175 self.parse_sess.source_map().span_to_filename(span).into()
176 }
177
span_to_file_contents(&self, span: Span) -> Lrc<rustc_span::SourceFile>178 pub(crate) fn span_to_file_contents(&self, span: Span) -> Lrc<rustc_span::SourceFile> {
179 self.parse_sess
180 .source_map()
181 .lookup_source_file(span.data().lo)
182 }
183
span_to_first_line_string(&self, span: Span) -> String184 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
line_of_byte_pos(&self, pos: BytePos) -> usize196 pub(crate) fn line_of_byte_pos(&self, pos: BytePos) -> usize {
197 self.parse_sess.source_map().lookup_char_pos(pos).line
198 }
199
span_to_debug_info(&self, span: Span) -> String200 pub(crate) fn span_to_debug_info(&self, span: Span) -> String {
201 self.parse_sess.source_map().span_to_diagnostic_string(span)
202 }
203
inner(&self) -> &RawParseSess204 pub(crate) fn inner(&self) -> &RawParseSess {
205 &self.parse_sess
206 }
207
snippet_provider(&self, span: Span) -> SnippetProvider208 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 Lrc::clone(source_file.src.as_ref().unwrap()),
214 )
215 }
216
get_original_snippet(&self, file_name: &FileName) -> Option<Lrc<String>>217 pub(crate) fn get_original_snippet(&self, file_name: &FileName) -> Option<Lrc<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 {
emit_diagnostics(&self, diagnostics: Vec<Diagnostic>)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
emit_or_cancel_diagnostic(&self, diagnostic: &mut Diagnostic)233 pub(crate) fn emit_or_cancel_diagnostic(&self, diagnostic: &mut Diagnostic) {
234 self.parse_sess.span_diagnostic.emit_diagnostic(diagnostic);
235 // The Handler will check whether the diagnostic should be emitted
236 // based on the user's rustfmt configuration and the originating file
237 // that caused the parser error. If the Handler determined it should skip
238 // emission then we need to ensure the diagnostic is cancelled.
239 if !diagnostic.cancelled() {
240 diagnostic.cancel();
241 }
242 }
243
can_reset_errors(&self) -> bool244 pub(super) fn can_reset_errors(&self) -> bool {
245 self.can_reset_errors.load(Ordering::Acquire)
246 }
247
has_errors(&self) -> bool248 pub(super) fn has_errors(&self) -> bool {
249 self.parse_sess.span_diagnostic.has_errors()
250 }
251
reset_errors(&self)252 pub(super) fn reset_errors(&self) {
253 self.parse_sess.span_diagnostic.reset_err_count();
254 }
255 }
256
257 impl LineRangeUtils for ParseSess {
lookup_line_range(&self, span: Span) -> LineRange258 fn lookup_line_range(&self, span: Span) -> LineRange {
259 let snippet = self
260 .parse_sess
261 .source_map()
262 .span_to_snippet(span)
263 .unwrap_or_default();
264 let lo = self.parse_sess.source_map().lookup_line(span.lo()).unwrap();
265 let hi = self.parse_sess.source_map().lookup_line(span.hi()).unwrap();
266
267 debug_assert_eq!(
268 lo.sf.name, hi.sf.name,
269 "span crossed file boundary: lo: {:?}, hi: {:?}",
270 lo, hi
271 );
272
273 // in case the span starts with a newline, the line range is off by 1 without the
274 // adjustment below
275 let offset = 1 + if starts_with_newline(&snippet) { 1 } else { 0 };
276 // Line numbers start at 1
277 LineRange {
278 file: lo.sf.clone(),
279 lo: lo.line + offset,
280 hi: hi.line + offset,
281 }
282 }
283 }
284
285 #[cfg(test)]
286 mod tests {
287 use super::*;
288
289 mod emitter {
290 use super::*;
291 use crate::config::IgnoreList;
292 use crate::is_nightly_channel;
293 use crate::utils::mk_sp;
294 use rustc_span::{FileName as SourceMapFileName, MultiSpan, RealFileName, DUMMY_SP};
295 use std::path::PathBuf;
296 use std::sync::atomic::AtomicU32;
297
298 struct TestEmitter {
299 num_emitted_errors: Lrc<AtomicU32>,
300 }
301
302 impl Emitter for TestEmitter {
source_map(&self) -> Option<&Lrc<SourceMap>>303 fn source_map(&self) -> Option<&Lrc<SourceMap>> {
304 None
305 }
emit_diagnostic(&mut self, _db: &Diagnostic)306 fn emit_diagnostic(&mut self, _db: &Diagnostic) {
307 self.num_emitted_errors.fetch_add(1, Ordering::Release);
308 }
309 }
310
build_diagnostic(level: DiagnosticLevel, span: Option<MultiSpan>) -> Diagnostic311 fn build_diagnostic(level: DiagnosticLevel, span: Option<MultiSpan>) -> Diagnostic {
312 Diagnostic {
313 level,
314 code: None,
315 message: vec![],
316 children: vec![],
317 suggestions: vec![],
318 span: span.unwrap_or_else(MultiSpan::new),
319 sort_span: DUMMY_SP,
320 is_lint: false,
321 }
322 }
323
build_emitter( num_emitted_errors: Lrc<AtomicU32>, can_reset: Lrc<AtomicBool>, source_map: Option<Lrc<SourceMap>>, ignore_list: Option<IgnoreList>, ) -> SilentOnIgnoredFilesEmitter324 fn build_emitter(
325 num_emitted_errors: Lrc<AtomicU32>,
326 can_reset: Lrc<AtomicBool>,
327 source_map: Option<Lrc<SourceMap>>,
328 ignore_list: Option<IgnoreList>,
329 ) -> SilentOnIgnoredFilesEmitter {
330 let emitter_writer = TestEmitter { num_emitted_errors };
331 let source_map =
332 source_map.unwrap_or_else(|| Lrc::new(SourceMap::new(FilePathMapping::empty())));
333 let ignore_path_set = Lrc::new(
334 IgnorePathSet::from_ignore_list(&ignore_list.unwrap_or_default()).unwrap(),
335 );
336 SilentOnIgnoredFilesEmitter {
337 has_non_ignorable_parser_errors: false,
338 source_map,
339 emitter: Box::new(emitter_writer),
340 ignore_path_set,
341 can_reset,
342 }
343 }
344
get_ignore_list(config: &str) -> IgnoreList345 fn get_ignore_list(config: &str) -> IgnoreList {
346 Config::from_toml(config, Path::new("")).unwrap().ignore()
347 }
348
349 #[test]
handles_fatal_parse_error_in_ignored_file()350 fn handles_fatal_parse_error_in_ignored_file() {
351 let num_emitted_errors = Lrc::new(AtomicU32::new(0));
352 let can_reset_errors = Lrc::new(AtomicBool::new(false));
353 let ignore_list = get_ignore_list(r#"ignore = ["foo.rs"]"#);
354 let source_map = Lrc::new(SourceMap::new(FilePathMapping::empty()));
355 let source =
356 String::from(r#"extern "system" fn jni_symbol!( funcName ) ( ... ) -> {} "#);
357 source_map.new_source_file(
358 SourceMapFileName::Real(RealFileName::LocalPath(PathBuf::from("foo.rs"))),
359 source,
360 );
361 let mut emitter = build_emitter(
362 Lrc::clone(&num_emitted_errors),
363 Lrc::clone(&can_reset_errors),
364 Some(Lrc::clone(&source_map)),
365 Some(ignore_list),
366 );
367 let span = MultiSpan::from_span(mk_sp(BytePos(0), BytePos(1)));
368 let fatal_diagnostic = build_diagnostic(DiagnosticLevel::Fatal, Some(span));
369 emitter.emit_diagnostic(&fatal_diagnostic);
370 assert_eq!(num_emitted_errors.load(Ordering::Acquire), 1);
371 assert_eq!(can_reset_errors.load(Ordering::Acquire), false);
372 }
373
374 #[test]
handles_recoverable_parse_error_in_ignored_file()375 fn handles_recoverable_parse_error_in_ignored_file() {
376 if !is_nightly_channel!() {
377 return;
378 }
379 let num_emitted_errors = Lrc::new(AtomicU32::new(0));
380 let can_reset_errors = Lrc::new(AtomicBool::new(false));
381 let ignore_list = get_ignore_list(r#"ignore = ["foo.rs"]"#);
382 let source_map = Lrc::new(SourceMap::new(FilePathMapping::empty()));
383 let source = String::from(r#"pub fn bar() { 1x; }"#);
384 source_map.new_source_file(
385 SourceMapFileName::Real(RealFileName::LocalPath(PathBuf::from("foo.rs"))),
386 source,
387 );
388 let mut emitter = build_emitter(
389 Lrc::clone(&num_emitted_errors),
390 Lrc::clone(&can_reset_errors),
391 Some(Lrc::clone(&source_map)),
392 Some(ignore_list),
393 );
394 let span = MultiSpan::from_span(mk_sp(BytePos(0), BytePos(1)));
395 let non_fatal_diagnostic = build_diagnostic(DiagnosticLevel::Warning, Some(span));
396 emitter.emit_diagnostic(&non_fatal_diagnostic);
397 assert_eq!(num_emitted_errors.load(Ordering::Acquire), 0);
398 assert_eq!(can_reset_errors.load(Ordering::Acquire), true);
399 }
400
401 #[test]
handles_recoverable_parse_error_in_non_ignored_file()402 fn handles_recoverable_parse_error_in_non_ignored_file() {
403 if !is_nightly_channel!() {
404 return;
405 }
406 let num_emitted_errors = Lrc::new(AtomicU32::new(0));
407 let can_reset_errors = Lrc::new(AtomicBool::new(false));
408 let source_map = Lrc::new(SourceMap::new(FilePathMapping::empty()));
409 let source = String::from(r#"pub fn bar() { 1x; }"#);
410 source_map.new_source_file(
411 SourceMapFileName::Real(RealFileName::LocalPath(PathBuf::from("foo.rs"))),
412 source,
413 );
414 let mut emitter = build_emitter(
415 Lrc::clone(&num_emitted_errors),
416 Lrc::clone(&can_reset_errors),
417 Some(Lrc::clone(&source_map)),
418 None,
419 );
420 let span = MultiSpan::from_span(mk_sp(BytePos(0), BytePos(1)));
421 let non_fatal_diagnostic = build_diagnostic(DiagnosticLevel::Warning, Some(span));
422 emitter.emit_diagnostic(&non_fatal_diagnostic);
423 assert_eq!(num_emitted_errors.load(Ordering::Acquire), 1);
424 assert_eq!(can_reset_errors.load(Ordering::Acquire), false);
425 }
426
427 #[test]
handles_mix_of_recoverable_parse_error()428 fn handles_mix_of_recoverable_parse_error() {
429 if !is_nightly_channel!() {
430 return;
431 }
432 let num_emitted_errors = Lrc::new(AtomicU32::new(0));
433 let can_reset_errors = Lrc::new(AtomicBool::new(false));
434 let source_map = Lrc::new(SourceMap::new(FilePathMapping::empty()));
435 let ignore_list = get_ignore_list(r#"ignore = ["foo.rs"]"#);
436 let bar_source = String::from(r#"pub fn bar() { 1x; }"#);
437 let foo_source = String::from(r#"pub fn foo() { 1x; }"#);
438 let fatal_source =
439 String::from(r#"extern "system" fn jni_symbol!( funcName ) ( ... ) -> {} "#);
440 source_map.new_source_file(
441 SourceMapFileName::Real(RealFileName::LocalPath(PathBuf::from("bar.rs"))),
442 bar_source,
443 );
444 source_map.new_source_file(
445 SourceMapFileName::Real(RealFileName::LocalPath(PathBuf::from("foo.rs"))),
446 foo_source,
447 );
448 source_map.new_source_file(
449 SourceMapFileName::Real(RealFileName::LocalPath(PathBuf::from("fatal.rs"))),
450 fatal_source,
451 );
452 let mut emitter = build_emitter(
453 Lrc::clone(&num_emitted_errors),
454 Lrc::clone(&can_reset_errors),
455 Some(Lrc::clone(&source_map)),
456 Some(ignore_list),
457 );
458 let bar_span = MultiSpan::from_span(mk_sp(BytePos(0), BytePos(1)));
459 let foo_span = MultiSpan::from_span(mk_sp(BytePos(21), BytePos(22)));
460 let bar_diagnostic = build_diagnostic(DiagnosticLevel::Warning, Some(bar_span));
461 let foo_diagnostic = build_diagnostic(DiagnosticLevel::Warning, Some(foo_span));
462 let fatal_diagnostic = build_diagnostic(DiagnosticLevel::Fatal, None);
463 emitter.emit_diagnostic(&bar_diagnostic);
464 emitter.emit_diagnostic(&foo_diagnostic);
465 emitter.emit_diagnostic(&fatal_diagnostic);
466 assert_eq!(num_emitted_errors.load(Ordering::Acquire), 2);
467 assert_eq!(can_reset_errors.load(Ordering::Acquire), false);
468 }
469 }
470 }
471