rustfmt_nightly/parse/
session.rs

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