]> git.proxmox.com Git - rustc.git/blame - src/tools/rustfmt/src/syntux/session.rs
Update upstream source from tag 'upstream/1.52.1+dfsg1'
[rustc.git] / src / tools / rustfmt / src / syntux / session.rs
CommitLineData
f20569fa
XL
1use std::cell::RefCell;
2use std::path::Path;
3use std::rc::Rc;
4
5use rustc_data_structures::sync::{Lrc, Send};
6use rustc_errors::emitter::{Emitter, EmitterWriter};
7use rustc_errors::{ColorConfig, Diagnostic, Handler, Level as DiagnosticLevel};
8use rustc_session::parse::ParseSess as RawParseSess;
9use rustc_span::{
10 source_map::{FilePathMapping, SourceMap},
11 symbol, BytePos, Span,
12};
13
14use crate::config::file_lines::LineRange;
15use crate::ignore_path::IgnorePathSet;
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,
24 ignore_path_set: Rc<IgnorePathSet>,
25 can_reset_errors: Rc<RefCell<bool>>,
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) {}
36}
37
38fn silent_emitter() -> Box<dyn Emitter + Send> {
39 Box::new(SilentEmitter {})
40}
41
42/// Emit errors against every files expect ones specified in the `ignore_path_set`.
43struct SilentOnIgnoredFilesEmitter {
44 ignore_path_set: Rc<IgnorePathSet>,
45 source_map: Rc<SourceMap>,
46 emitter: Box<dyn Emitter + Send>,
47 has_non_ignorable_parser_errors: bool,
48 can_reset: Rc<RefCell<bool>>,
49}
50
51impl SilentOnIgnoredFilesEmitter {
52 fn handle_non_ignoreable_error(&mut self, db: &Diagnostic) {
53 self.has_non_ignorable_parser_errors = true;
54 *self.can_reset.borrow_mut() = false;
55 self.emitter.emit_diagnostic(db);
56 }
57}
58
59impl Emitter for SilentOnIgnoredFilesEmitter {
60 fn source_map(&self) -> Option<&Lrc<SourceMap>> {
61 None
62 }
63 fn emit_diagnostic(&mut self, db: &Diagnostic) {
64 if db.level == DiagnosticLevel::Fatal {
65 return self.handle_non_ignoreable_error(db);
66 }
67 if let Some(primary_span) = &db.span.primary_span() {
68 let file_name = self.source_map.span_to_filename(*primary_span);
69 if let rustc_span::FileName::Real(rustc_span::RealFileName::Named(ref path)) = 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.borrow_mut() = true;
77 }
78 return;
79 }
80 };
81 }
82 self.handle_non_ignoreable_error(db);
83 }
84}
85
86fn default_handler(
87 source_map: Rc<SourceMap>,
88 ignore_path_set: Rc<IgnorePathSet>,
89 can_reset: Rc<RefCell<bool>>,
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()) {
127 Ok(ignore_path_set) => Rc::new(ignore_path_set),
128 Err(e) => return Err(ErrorKind::InvalidGlobPattern(e)),
129 };
130 let source_map = Rc::new(SourceMap::new(FilePathMapping::empty()));
131 let can_reset_errors = Rc::new(RefCell::new(false));
132
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(),
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,
153 ) -> rustc_expand::module::ModulePath<'_> {
154 rustc_expand::module::default_submod_path(
155 &self.parse_sess,
156 id,
157 rustc_span::DUMMY_SP,
158 relative,
159 dir_path,
160 )
161 }
162
163 pub(crate) fn is_file_parsed(&self, path: &Path) -> bool {
164 self.parse_sess
165 .source_map()
166 .get_source_file(&rustc_span::FileName::Real(
167 rustc_span::RealFileName::Named(path.to_path_buf()),
168 ))
169 .is_some()
170 }
171
172 pub(crate) fn ignore_file(&self, path: &FileName) -> bool {
173 self.ignore_path_set.as_ref().is_match(&path)
174 }
175
176 pub(crate) fn set_silent_emitter(&mut self) {
177 self.parse_sess.span_diagnostic = Handler::with_emitter(true, None, silent_emitter());
178 }
179
180 pub(crate) fn span_to_filename(&self, span: Span) -> FileName {
181 self.parse_sess.source_map().span_to_filename(span).into()
182 }
183
184 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
196 pub(crate) fn line_of_byte_pos(&self, pos: BytePos) -> usize {
197 self.parse_sess.source_map().lookup_char_pos(pos).line
198 }
199
200 pub(crate) fn span_to_debug_info(&self, span: Span) -> String {
201 self.parse_sess.source_map().span_to_string(span)
202 }
203
204 pub(crate) fn inner(&self) -> &RawParseSess {
205 &self.parse_sess
206 }
207
208 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 Rc::clone(source_file.src.as_ref().unwrap()),
214 )
215 }
216
217 pub(crate) fn get_original_snippet(&self, file_name: &FileName) -> Option<Rc<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.
226impl ParseSess {
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
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
244 pub(super) fn can_reset_errors(&self) -> bool {
245 *self.can_reset_errors.borrow()
246 }
247
248 pub(super) fn has_errors(&self) -> bool {
249 self.parse_sess.span_diagnostic.has_errors()
250 }
251
252 pub(super) fn reset_errors(&self) {
253 self.parse_sess.span_diagnostic.reset_err_count();
254 }
255}
256
257impl LineRangeUtils for ParseSess {
258 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)]
286mod 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
297 struct TestEmitter {
298 num_emitted_errors: Rc<RefCell<u32>>,
299 }
300
301 impl Emitter for TestEmitter {
302 fn source_map(&self) -> Option<&Lrc<SourceMap>> {
303 None
304 }
305 fn emit_diagnostic(&mut self, _db: &Diagnostic) {
306 *self.num_emitted_errors.borrow_mut() += 1;
307 }
308 }
309
310 fn build_diagnostic(level: DiagnosticLevel, span: Option<MultiSpan>) -> Diagnostic {
311 Diagnostic {
312 level,
313 code: None,
314 message: vec![],
315 children: vec![],
316 suggestions: vec![],
317 span: span.unwrap_or_else(MultiSpan::new),
318 sort_span: DUMMY_SP,
319 }
320 }
321
322 fn build_emitter(
323 num_emitted_errors: Rc<RefCell<u32>>,
324 can_reset: Rc<RefCell<bool>>,
325 source_map: Option<Rc<SourceMap>>,
326 ignore_list: Option<IgnoreList>,
327 ) -> SilentOnIgnoredFilesEmitter {
328 let emitter_writer = TestEmitter { num_emitted_errors };
329 let source_map =
330 source_map.unwrap_or_else(|| Rc::new(SourceMap::new(FilePathMapping::empty())));
331 let ignore_path_set =
332 Rc::new(IgnorePathSet::from_ignore_list(&ignore_list.unwrap_or_default()).unwrap());
333 SilentOnIgnoredFilesEmitter {
334 has_non_ignorable_parser_errors: false,
335 source_map,
336 emitter: Box::new(emitter_writer),
337 ignore_path_set,
338 can_reset,
339 }
340 }
341
342 fn get_ignore_list(config: &str) -> IgnoreList {
343 Config::from_toml(config, Path::new("")).unwrap().ignore()
344 }
345
346 #[test]
347 fn handles_fatal_parse_error_in_ignored_file() {
348 let num_emitted_errors = Rc::new(RefCell::new(0));
349 let can_reset_errors = Rc::new(RefCell::new(false));
350 let ignore_list = get_ignore_list(r#"ignore = ["foo.rs"]"#);
351 let source_map = Rc::new(SourceMap::new(FilePathMapping::empty()));
352 let source =
353 String::from(r#"extern "system" fn jni_symbol!( funcName ) ( ... ) -> {} "#);
354 source_map.new_source_file(
355 SourceMapFileName::Real(RealFileName::Named(PathBuf::from("foo.rs"))),
356 source,
357 );
358 let mut emitter = build_emitter(
359 Rc::clone(&num_emitted_errors),
360 Rc::clone(&can_reset_errors),
361 Some(Rc::clone(&source_map)),
362 Some(ignore_list),
363 );
364 let span = MultiSpan::from_span(mk_sp(BytePos(0), BytePos(1)));
365 let fatal_diagnostic = build_diagnostic(DiagnosticLevel::Fatal, Some(span));
366 emitter.emit_diagnostic(&fatal_diagnostic);
367 assert_eq!(*num_emitted_errors.borrow(), 1);
368 assert_eq!(*can_reset_errors.borrow(), false);
369 }
370
371 #[test]
372 fn handles_recoverable_parse_error_in_ignored_file() {
373 if !is_nightly_channel!() {
374 return;
375 }
376 let num_emitted_errors = Rc::new(RefCell::new(0));
377 let can_reset_errors = Rc::new(RefCell::new(false));
378 let ignore_list = get_ignore_list(r#"ignore = ["foo.rs"]"#);
379 let source_map = Rc::new(SourceMap::new(FilePathMapping::empty()));
380 let source = String::from(r#"pub fn bar() { 1x; }"#);
381 source_map.new_source_file(
382 SourceMapFileName::Real(RealFileName::Named(PathBuf::from("foo.rs"))),
383 source,
384 );
385 let mut emitter = build_emitter(
386 Rc::clone(&num_emitted_errors),
387 Rc::clone(&can_reset_errors),
388 Some(Rc::clone(&source_map)),
389 Some(ignore_list),
390 );
391 let span = MultiSpan::from_span(mk_sp(BytePos(0), BytePos(1)));
392 let non_fatal_diagnostic = build_diagnostic(DiagnosticLevel::Warning, Some(span));
393 emitter.emit_diagnostic(&non_fatal_diagnostic);
394 assert_eq!(*num_emitted_errors.borrow(), 0);
395 assert_eq!(*can_reset_errors.borrow(), true);
396 }
397
398 #[test]
399 fn handles_recoverable_parse_error_in_non_ignored_file() {
400 if !is_nightly_channel!() {
401 return;
402 }
403 let num_emitted_errors = Rc::new(RefCell::new(0));
404 let can_reset_errors = Rc::new(RefCell::new(false));
405 let source_map = Rc::new(SourceMap::new(FilePathMapping::empty()));
406 let source = String::from(r#"pub fn bar() { 1x; }"#);
407 source_map.new_source_file(
408 SourceMapFileName::Real(RealFileName::Named(PathBuf::from("foo.rs"))),
409 source,
410 );
411 let mut emitter = build_emitter(
412 Rc::clone(&num_emitted_errors),
413 Rc::clone(&can_reset_errors),
414 Some(Rc::clone(&source_map)),
415 None,
416 );
417 let span = MultiSpan::from_span(mk_sp(BytePos(0), BytePos(1)));
418 let non_fatal_diagnostic = build_diagnostic(DiagnosticLevel::Warning, Some(span));
419 emitter.emit_diagnostic(&non_fatal_diagnostic);
420 assert_eq!(*num_emitted_errors.borrow(), 1);
421 assert_eq!(*can_reset_errors.borrow(), false);
422 }
423
424 #[test]
425 fn handles_mix_of_recoverable_parse_error() {
426 if !is_nightly_channel!() {
427 return;
428 }
429 let num_emitted_errors = Rc::new(RefCell::new(0));
430 let can_reset_errors = Rc::new(RefCell::new(false));
431 let source_map = Rc::new(SourceMap::new(FilePathMapping::empty()));
432 let ignore_list = get_ignore_list(r#"ignore = ["foo.rs"]"#);
433 let bar_source = String::from(r#"pub fn bar() { 1x; }"#);
434 let foo_source = String::from(r#"pub fn foo() { 1x; }"#);
435 let fatal_source =
436 String::from(r#"extern "system" fn jni_symbol!( funcName ) ( ... ) -> {} "#);
437 source_map.new_source_file(
438 SourceMapFileName::Real(RealFileName::Named(PathBuf::from("bar.rs"))),
439 bar_source,
440 );
441 source_map.new_source_file(
442 SourceMapFileName::Real(RealFileName::Named(PathBuf::from("foo.rs"))),
443 foo_source,
444 );
445 source_map.new_source_file(
446 SourceMapFileName::Real(RealFileName::Named(PathBuf::from("fatal.rs"))),
447 fatal_source,
448 );
449 let mut emitter = build_emitter(
450 Rc::clone(&num_emitted_errors),
451 Rc::clone(&can_reset_errors),
452 Some(Rc::clone(&source_map)),
453 Some(ignore_list),
454 );
455 let bar_span = MultiSpan::from_span(mk_sp(BytePos(0), BytePos(1)));
456 let foo_span = MultiSpan::from_span(mk_sp(BytePos(21), BytePos(22)));
457 let bar_diagnostic = build_diagnostic(DiagnosticLevel::Warning, Some(bar_span));
458 let foo_diagnostic = build_diagnostic(DiagnosticLevel::Warning, Some(foo_span));
459 let fatal_diagnostic = build_diagnostic(DiagnosticLevel::Fatal, None);
460 emitter.emit_diagnostic(&bar_diagnostic);
461 emitter.emit_diagnostic(&foo_diagnostic);
462 emitter.emit_diagnostic(&fatal_diagnostic);
463 assert_eq!(*num_emitted_errors.borrow(), 2);
464 assert_eq!(*can_reset_errors.borrow(), false);
465 }
466 }
467}