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