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