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::translation::Translate;
7 use rustc_errors::{ColorConfig, Diagnostic, Handler, Level as DiagnosticLevel, TerminalUrl};
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::config::options::Color;
16 use crate::ignore_path::IgnorePathSet;
17 use crate::parse::parser::{ModError, ModulePathSuccess};
18 use crate::source_map::LineRangeUtils;
19 use crate::utils::starts_with_newline;
20 use crate::visitor::SnippetProvider;
21 use crate::{Config, ErrorKind, FileName};
22
23 /// ParseSess holds structs necessary for constructing a parser.
24 pub(crate) struct ParseSess {
25 parse_sess: RawParseSess,
26 ignore_path_set: Lrc<IgnorePathSet>,
27 can_reset_errors: Lrc<AtomicBool>,
28 }
29
30 /// Emitter which discards every error.
31 struct SilentEmitter;
32
33 impl Translate for SilentEmitter {
fluent_bundle(&self) -> Option<&Lrc<rustc_errors::FluentBundle>>34 fn fluent_bundle(&self) -> Option<&Lrc<rustc_errors::FluentBundle>> {
35 None
36 }
37
fallback_fluent_bundle(&self) -> &rustc_errors::FluentBundle38 fn fallback_fluent_bundle(&self) -> &rustc_errors::FluentBundle {
39 panic!("silent emitter attempted to translate a diagnostic");
40 }
41 }
42
43 impl Emitter for SilentEmitter {
source_map(&self) -> Option<&Lrc<SourceMap>>44 fn source_map(&self) -> Option<&Lrc<SourceMap>> {
45 None
46 }
47
emit_diagnostic(&mut self, _db: &Diagnostic)48 fn emit_diagnostic(&mut self, _db: &Diagnostic) {}
49 }
50
silent_emitter() -> Box<dyn Emitter + Send>51 fn silent_emitter() -> Box<dyn Emitter + Send> {
52 Box::new(SilentEmitter {})
53 }
54
55 /// Emit errors against every files expect ones specified in the `ignore_path_set`.
56 struct SilentOnIgnoredFilesEmitter {
57 ignore_path_set: Lrc<IgnorePathSet>,
58 source_map: Lrc<SourceMap>,
59 emitter: Box<dyn Emitter + Send>,
60 has_non_ignorable_parser_errors: bool,
61 can_reset: Lrc<AtomicBool>,
62 }
63
64 impl SilentOnIgnoredFilesEmitter {
handle_non_ignoreable_error(&mut self, db: &Diagnostic)65 fn handle_non_ignoreable_error(&mut self, db: &Diagnostic) {
66 self.has_non_ignorable_parser_errors = true;
67 self.can_reset.store(false, Ordering::Release);
68 self.emitter.emit_diagnostic(db);
69 }
70 }
71
72 impl Translate for SilentOnIgnoredFilesEmitter {
fluent_bundle(&self) -> Option<&Lrc<rustc_errors::FluentBundle>>73 fn fluent_bundle(&self) -> Option<&Lrc<rustc_errors::FluentBundle>> {
74 self.emitter.fluent_bundle()
75 }
76
fallback_fluent_bundle(&self) -> &rustc_errors::FluentBundle77 fn fallback_fluent_bundle(&self) -> &rustc_errors::FluentBundle {
78 self.emitter.fallback_fluent_bundle()
79 }
80 }
81
82 impl Emitter for SilentOnIgnoredFilesEmitter {
source_map(&self) -> Option<&Lrc<SourceMap>>83 fn source_map(&self) -> Option<&Lrc<SourceMap>> {
84 None
85 }
86
emit_diagnostic(&mut self, db: &Diagnostic)87 fn emit_diagnostic(&mut self, db: &Diagnostic) {
88 if db.level() == DiagnosticLevel::Fatal {
89 return self.handle_non_ignoreable_error(db);
90 }
91 if let Some(primary_span) = &db.span.primary_span() {
92 let file_name = self.source_map.span_to_filename(*primary_span);
93 if let rustc_span::FileName::Real(rustc_span::RealFileName::LocalPath(ref path)) =
94 file_name
95 {
96 if self
97 .ignore_path_set
98 .is_match(&FileName::Real(path.to_path_buf()))
99 {
100 if !self.has_non_ignorable_parser_errors {
101 self.can_reset.store(true, Ordering::Release);
102 }
103 return;
104 }
105 };
106 }
107 self.handle_non_ignoreable_error(db);
108 }
109 }
110
111 impl From<Color> for ColorConfig {
from(color: Color) -> Self112 fn from(color: Color) -> Self {
113 match color {
114 Color::Auto => ColorConfig::Auto,
115 Color::Always => ColorConfig::Always,
116 Color::Never => ColorConfig::Never,
117 }
118 }
119 }
120
default_handler( source_map: Lrc<SourceMap>, ignore_path_set: Lrc<IgnorePathSet>, can_reset: Lrc<AtomicBool>, hide_parse_errors: bool, color: Color, ) -> Handler121 fn default_handler(
122 source_map: Lrc<SourceMap>,
123 ignore_path_set: Lrc<IgnorePathSet>,
124 can_reset: Lrc<AtomicBool>,
125 hide_parse_errors: bool,
126 color: Color,
127 ) -> Handler {
128 let supports_color = term::stderr().map_or(false, |term| term.supports_color());
129 let emit_color = if supports_color {
130 ColorConfig::from(color)
131 } else {
132 ColorConfig::Never
133 };
134
135 let emitter = if hide_parse_errors {
136 silent_emitter()
137 } else {
138 let fallback_bundle = rustc_errors::fallback_fluent_bundle(
139 rustc_driver::DEFAULT_LOCALE_RESOURCES.to_vec(),
140 false,
141 );
142 Box::new(EmitterWriter::stderr(
143 emit_color,
144 Some(source_map.clone()),
145 None,
146 fallback_bundle,
147 false,
148 false,
149 None,
150 false,
151 false,
152 TerminalUrl::No,
153 ))
154 };
155 Handler::with_emitter(
156 true,
157 None,
158 Box::new(SilentOnIgnoredFilesEmitter {
159 has_non_ignorable_parser_errors: false,
160 source_map,
161 emitter,
162 ignore_path_set,
163 can_reset,
164 }),
165 )
166 }
167
168 impl ParseSess {
new(config: &Config) -> Result<ParseSess, ErrorKind>169 pub(crate) fn new(config: &Config) -> Result<ParseSess, ErrorKind> {
170 let ignore_path_set = match IgnorePathSet::from_ignore_list(&config.ignore()) {
171 Ok(ignore_path_set) => Lrc::new(ignore_path_set),
172 Err(e) => return Err(ErrorKind::InvalidGlobPattern(e)),
173 };
174 let source_map = Lrc::new(SourceMap::new(FilePathMapping::empty()));
175 let can_reset_errors = Lrc::new(AtomicBool::new(false));
176
177 let handler = default_handler(
178 Lrc::clone(&source_map),
179 Lrc::clone(&ignore_path_set),
180 Lrc::clone(&can_reset_errors),
181 config.hide_parse_errors(),
182 config.color(),
183 );
184 let parse_sess = RawParseSess::with_span_handler(handler, source_map);
185
186 Ok(ParseSess {
187 parse_sess,
188 ignore_path_set,
189 can_reset_errors,
190 })
191 }
192
193 /// Determine the submodule path for the given module identifier.
194 ///
195 /// * `id` - The name of the module
196 /// * `relative` - If Some(symbol), the symbol name is a directory relative to the dir_path.
197 /// If relative is Some, resolve the submodle at {dir_path}/{symbol}/{id}.rs
198 /// or {dir_path}/{symbol}/{id}/mod.rs. if None, resolve the module at {dir_path}/{id}.rs.
199 /// * `dir_path` - Module resolution will occur relative to this directory.
default_submod_path( &self, id: symbol::Ident, relative: Option<symbol::Ident>, dir_path: &Path, ) -> Result<ModulePathSuccess, ModError<'_>>200 pub(crate) fn default_submod_path(
201 &self,
202 id: symbol::Ident,
203 relative: Option<symbol::Ident>,
204 dir_path: &Path,
205 ) -> Result<ModulePathSuccess, ModError<'_>> {
206 rustc_expand::module::default_submod_path(&self.parse_sess, id, relative, dir_path).or_else(
207 |e| {
208 // If resloving a module relative to {dir_path}/{symbol} fails because a file
209 // could not be found, then try to resolve the module relative to {dir_path}.
210 // If we still can't find the module after searching for it in {dir_path},
211 // surface the original error.
212 if matches!(e, ModError::FileNotFound(..)) && relative.is_some() {
213 rustc_expand::module::default_submod_path(&self.parse_sess, id, None, dir_path)
214 .map_err(|_| e)
215 } else {
216 Err(e)
217 }
218 },
219 )
220 }
221
is_file_parsed(&self, path: &Path) -> bool222 pub(crate) fn is_file_parsed(&self, path: &Path) -> bool {
223 self.parse_sess
224 .source_map()
225 .get_source_file(&rustc_span::FileName::Real(
226 rustc_span::RealFileName::LocalPath(path.to_path_buf()),
227 ))
228 .is_some()
229 }
230
ignore_file(&self, path: &FileName) -> bool231 pub(crate) fn ignore_file(&self, path: &FileName) -> bool {
232 self.ignore_path_set.as_ref().is_match(path)
233 }
234
set_silent_emitter(&mut self)235 pub(crate) fn set_silent_emitter(&mut self) {
236 self.parse_sess.span_diagnostic = Handler::with_emitter(true, None, silent_emitter());
237 }
238
span_to_filename(&self, span: Span) -> FileName239 pub(crate) fn span_to_filename(&self, span: Span) -> FileName {
240 self.parse_sess.source_map().span_to_filename(span).into()
241 }
242
span_to_file_contents(&self, span: Span) -> Lrc<rustc_span::SourceFile>243 pub(crate) fn span_to_file_contents(&self, span: Span) -> Lrc<rustc_span::SourceFile> {
244 self.parse_sess
245 .source_map()
246 .lookup_source_file(span.data().lo)
247 }
248
span_to_first_line_string(&self, span: Span) -> String249 pub(crate) fn span_to_first_line_string(&self, span: Span) -> String {
250 let file_lines = self.parse_sess.source_map().span_to_lines(span).ok();
251
252 match file_lines {
253 Some(fl) => fl
254 .file
255 .get_line(fl.lines[0].line_index)
256 .map_or_else(String::new, |s| s.to_string()),
257 None => String::new(),
258 }
259 }
260
line_of_byte_pos(&self, pos: BytePos) -> usize261 pub(crate) fn line_of_byte_pos(&self, pos: BytePos) -> usize {
262 self.parse_sess.source_map().lookup_char_pos(pos).line
263 }
264
265 // TODO(calebcartwright): Preemptive, currently unused addition
266 // that will be used to support formatting scenarios that take original
267 // positions into account
268 /// Determines whether two byte positions are in the same source line.
269 #[allow(dead_code)]
byte_pos_same_line(&self, a: BytePos, b: BytePos) -> bool270 pub(crate) fn byte_pos_same_line(&self, a: BytePos, b: BytePos) -> bool {
271 self.line_of_byte_pos(a) == self.line_of_byte_pos(b)
272 }
273
span_to_debug_info(&self, span: Span) -> String274 pub(crate) fn span_to_debug_info(&self, span: Span) -> String {
275 self.parse_sess.source_map().span_to_diagnostic_string(span)
276 }
277
inner(&self) -> &RawParseSess278 pub(crate) fn inner(&self) -> &RawParseSess {
279 &self.parse_sess
280 }
281
snippet_provider(&self, span: Span) -> SnippetProvider282 pub(crate) fn snippet_provider(&self, span: Span) -> SnippetProvider {
283 let source_file = self.parse_sess.source_map().lookup_char_pos(span.lo()).file;
284 SnippetProvider::new(
285 source_file.start_pos,
286 source_file.end_pos,
287 Lrc::clone(source_file.src.as_ref().unwrap()),
288 )
289 }
290
get_original_snippet(&self, file_name: &FileName) -> Option<Lrc<String>>291 pub(crate) fn get_original_snippet(&self, file_name: &FileName) -> Option<Lrc<String>> {
292 self.parse_sess
293 .source_map()
294 .get_source_file(&file_name.into())
295 .and_then(|source_file| source_file.src.clone())
296 }
297 }
298
299 // Methods that should be restricted within the parse module.
300 impl ParseSess {
emit_diagnostics(&self, diagnostics: Vec<Diagnostic>)301 pub(super) fn emit_diagnostics(&self, diagnostics: Vec<Diagnostic>) {
302 for mut diagnostic in diagnostics {
303 self.parse_sess
304 .span_diagnostic
305 .emit_diagnostic(&mut diagnostic);
306 }
307 }
308
can_reset_errors(&self) -> bool309 pub(super) fn can_reset_errors(&self) -> bool {
310 self.can_reset_errors.load(Ordering::Acquire)
311 }
312
has_errors(&self) -> bool313 pub(super) fn has_errors(&self) -> bool {
314 self.parse_sess.span_diagnostic.has_errors().is_some()
315 }
316
reset_errors(&self)317 pub(super) fn reset_errors(&self) {
318 self.parse_sess.span_diagnostic.reset_err_count();
319 }
320 }
321
322 impl LineRangeUtils for ParseSess {
lookup_line_range(&self, span: Span) -> LineRange323 fn lookup_line_range(&self, span: Span) -> LineRange {
324 let snippet = self
325 .parse_sess
326 .source_map()
327 .span_to_snippet(span)
328 .unwrap_or_default();
329 let lo = self.parse_sess.source_map().lookup_line(span.lo()).unwrap();
330 let hi = self.parse_sess.source_map().lookup_line(span.hi()).unwrap();
331
332 debug_assert_eq!(
333 lo.sf.name, hi.sf.name,
334 "span crossed file boundary: lo: {:?}, hi: {:?}",
335 lo, hi
336 );
337
338 // in case the span starts with a newline, the line range is off by 1 without the
339 // adjustment below
340 let offset = 1 + if starts_with_newline(&snippet) { 1 } else { 0 };
341 // Line numbers start at 1
342 LineRange {
343 file: lo.sf.clone(),
344 lo: lo.line + offset,
345 hi: hi.line + offset,
346 }
347 }
348 }
349
350 #[cfg(test)]
351 mod tests {
352 use super::*;
353
354 use rustfmt_config_proc_macro::nightly_only_test;
355
356 mod emitter {
357 use super::*;
358 use crate::config::IgnoreList;
359 use crate::utils::mk_sp;
360 use rustc_errors::MultiSpan;
361 use rustc_span::{FileName as SourceMapFileName, RealFileName};
362 use std::path::PathBuf;
363 use std::sync::atomic::AtomicU32;
364
365 struct TestEmitter {
366 num_emitted_errors: Lrc<AtomicU32>,
367 }
368
369 impl Translate for TestEmitter {
fluent_bundle(&self) -> Option<&Lrc<rustc_errors::FluentBundle>>370 fn fluent_bundle(&self) -> Option<&Lrc<rustc_errors::FluentBundle>> {
371 None
372 }
373
fallback_fluent_bundle(&self) -> &rustc_errors::FluentBundle374 fn fallback_fluent_bundle(&self) -> &rustc_errors::FluentBundle {
375 panic!("test emitter attempted to translate a diagnostic");
376 }
377 }
378
379 impl Emitter for TestEmitter {
source_map(&self) -> Option<&Lrc<SourceMap>>380 fn source_map(&self) -> Option<&Lrc<SourceMap>> {
381 None
382 }
383
emit_diagnostic(&mut self, _db: &Diagnostic)384 fn emit_diagnostic(&mut self, _db: &Diagnostic) {
385 self.num_emitted_errors.fetch_add(1, Ordering::Release);
386 }
387 }
388
build_diagnostic(level: DiagnosticLevel, span: Option<MultiSpan>) -> Diagnostic389 fn build_diagnostic(level: DiagnosticLevel, span: Option<MultiSpan>) -> Diagnostic {
390 let mut diag = Diagnostic::new(level, "");
391 diag.message.clear();
392 if let Some(span) = span {
393 diag.span = span;
394 }
395 diag
396 }
397
build_emitter( num_emitted_errors: Lrc<AtomicU32>, can_reset: Lrc<AtomicBool>, source_map: Option<Lrc<SourceMap>>, ignore_list: Option<IgnoreList>, ) -> SilentOnIgnoredFilesEmitter398 fn build_emitter(
399 num_emitted_errors: Lrc<AtomicU32>,
400 can_reset: Lrc<AtomicBool>,
401 source_map: Option<Lrc<SourceMap>>,
402 ignore_list: Option<IgnoreList>,
403 ) -> SilentOnIgnoredFilesEmitter {
404 let emitter_writer = TestEmitter { num_emitted_errors };
405 let source_map =
406 source_map.unwrap_or_else(|| Lrc::new(SourceMap::new(FilePathMapping::empty())));
407 let ignore_path_set = Lrc::new(
408 IgnorePathSet::from_ignore_list(&ignore_list.unwrap_or_default()).unwrap(),
409 );
410 SilentOnIgnoredFilesEmitter {
411 has_non_ignorable_parser_errors: false,
412 source_map,
413 emitter: Box::new(emitter_writer),
414 ignore_path_set,
415 can_reset,
416 }
417 }
418
get_ignore_list(config: &str) -> IgnoreList419 fn get_ignore_list(config: &str) -> IgnoreList {
420 Config::from_toml(config, Path::new("")).unwrap().ignore()
421 }
422
423 #[test]
handles_fatal_parse_error_in_ignored_file()424 fn handles_fatal_parse_error_in_ignored_file() {
425 let num_emitted_errors = Lrc::new(AtomicU32::new(0));
426 let can_reset_errors = Lrc::new(AtomicBool::new(false));
427 let ignore_list = get_ignore_list(r#"ignore = ["foo.rs"]"#);
428 let source_map = Lrc::new(SourceMap::new(FilePathMapping::empty()));
429 let source =
430 String::from(r#"extern "system" fn jni_symbol!( funcName ) ( ... ) -> {} "#);
431 source_map.new_source_file(
432 SourceMapFileName::Real(RealFileName::LocalPath(PathBuf::from("foo.rs"))),
433 source,
434 );
435 let mut emitter = build_emitter(
436 Lrc::clone(&num_emitted_errors),
437 Lrc::clone(&can_reset_errors),
438 Some(Lrc::clone(&source_map)),
439 Some(ignore_list),
440 );
441 let span = MultiSpan::from_span(mk_sp(BytePos(0), BytePos(1)));
442 let fatal_diagnostic = build_diagnostic(DiagnosticLevel::Fatal, Some(span));
443 emitter.emit_diagnostic(&fatal_diagnostic);
444 assert_eq!(num_emitted_errors.load(Ordering::Acquire), 1);
445 assert_eq!(can_reset_errors.load(Ordering::Acquire), false);
446 }
447
448 #[nightly_only_test]
449 #[test]
handles_recoverable_parse_error_in_ignored_file()450 fn handles_recoverable_parse_error_in_ignored_file() {
451 let num_emitted_errors = Lrc::new(AtomicU32::new(0));
452 let can_reset_errors = Lrc::new(AtomicBool::new(false));
453 let ignore_list = get_ignore_list(r#"ignore = ["foo.rs"]"#);
454 let source_map = Lrc::new(SourceMap::new(FilePathMapping::empty()));
455 let source = String::from(r#"pub fn bar() { 1x; }"#);
456 source_map.new_source_file(
457 SourceMapFileName::Real(RealFileName::LocalPath(PathBuf::from("foo.rs"))),
458 source,
459 );
460 let mut emitter = build_emitter(
461 Lrc::clone(&num_emitted_errors),
462 Lrc::clone(&can_reset_errors),
463 Some(Lrc::clone(&source_map)),
464 Some(ignore_list),
465 );
466 let span = MultiSpan::from_span(mk_sp(BytePos(0), BytePos(1)));
467 let non_fatal_diagnostic = build_diagnostic(DiagnosticLevel::Warning(None), Some(span));
468 emitter.emit_diagnostic(&non_fatal_diagnostic);
469 assert_eq!(num_emitted_errors.load(Ordering::Acquire), 0);
470 assert_eq!(can_reset_errors.load(Ordering::Acquire), true);
471 }
472
473 #[nightly_only_test]
474 #[test]
handles_recoverable_parse_error_in_non_ignored_file()475 fn handles_recoverable_parse_error_in_non_ignored_file() {
476 let num_emitted_errors = Lrc::new(AtomicU32::new(0));
477 let can_reset_errors = Lrc::new(AtomicBool::new(false));
478 let source_map = Lrc::new(SourceMap::new(FilePathMapping::empty()));
479 let source = String::from(r#"pub fn bar() { 1x; }"#);
480 source_map.new_source_file(
481 SourceMapFileName::Real(RealFileName::LocalPath(PathBuf::from("foo.rs"))),
482 source,
483 );
484 let mut emitter = build_emitter(
485 Lrc::clone(&num_emitted_errors),
486 Lrc::clone(&can_reset_errors),
487 Some(Lrc::clone(&source_map)),
488 None,
489 );
490 let span = MultiSpan::from_span(mk_sp(BytePos(0), BytePos(1)));
491 let non_fatal_diagnostic = build_diagnostic(DiagnosticLevel::Warning(None), Some(span));
492 emitter.emit_diagnostic(&non_fatal_diagnostic);
493 assert_eq!(num_emitted_errors.load(Ordering::Acquire), 1);
494 assert_eq!(can_reset_errors.load(Ordering::Acquire), false);
495 }
496
497 #[nightly_only_test]
498 #[test]
handles_mix_of_recoverable_parse_error()499 fn handles_mix_of_recoverable_parse_error() {
500 let num_emitted_errors = Lrc::new(AtomicU32::new(0));
501 let can_reset_errors = Lrc::new(AtomicBool::new(false));
502 let source_map = Lrc::new(SourceMap::new(FilePathMapping::empty()));
503 let ignore_list = get_ignore_list(r#"ignore = ["foo.rs"]"#);
504 let bar_source = String::from(r#"pub fn bar() { 1x; }"#);
505 let foo_source = String::from(r#"pub fn foo() { 1x; }"#);
506 let fatal_source =
507 String::from(r#"extern "system" fn jni_symbol!( funcName ) ( ... ) -> {} "#);
508 source_map.new_source_file(
509 SourceMapFileName::Real(RealFileName::LocalPath(PathBuf::from("bar.rs"))),
510 bar_source,
511 );
512 source_map.new_source_file(
513 SourceMapFileName::Real(RealFileName::LocalPath(PathBuf::from("foo.rs"))),
514 foo_source,
515 );
516 source_map.new_source_file(
517 SourceMapFileName::Real(RealFileName::LocalPath(PathBuf::from("fatal.rs"))),
518 fatal_source,
519 );
520 let mut emitter = build_emitter(
521 Lrc::clone(&num_emitted_errors),
522 Lrc::clone(&can_reset_errors),
523 Some(Lrc::clone(&source_map)),
524 Some(ignore_list),
525 );
526 let bar_span = MultiSpan::from_span(mk_sp(BytePos(0), BytePos(1)));
527 let foo_span = MultiSpan::from_span(mk_sp(BytePos(21), BytePos(22)));
528 let bar_diagnostic = build_diagnostic(DiagnosticLevel::Warning(None), Some(bar_span));
529 let foo_diagnostic = build_diagnostic(DiagnosticLevel::Warning(None), Some(foo_span));
530 let fatal_diagnostic = build_diagnostic(DiagnosticLevel::Fatal, None);
531 emitter.emit_diagnostic(&bar_diagnostic);
532 emitter.emit_diagnostic(&foo_diagnostic);
533 emitter.emit_diagnostic(&fatal_diagnostic);
534 assert_eq!(num_emitted_errors.load(Ordering::Acquire), 2);
535 assert_eq!(can_reset_errors.load(Ordering::Acquire), false);
536 }
537 }
538 }
539