about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorCaleb Cartwright <caleb.cartwright@outlook.com>2020-03-26 21:25:34 -0500
committerCaleb Cartwright <caleb.cartwright@outlook.com>2020-03-26 21:25:34 -0500
commitbd5dff4012a73ad4c5eb5db1dbb111bd92ed31dd (patch)
treeb781aa6ed959466d2b6605ab844a2ba7fe56b6a6 /src
parent9699c96cf1e6a13e0cc12e17c55ca11d2f9a6bf2 (diff)
refactor: backport syntux mod
Diffstat (limited to 'src')
-rw-r--r--src/closures.rs6
-rw-r--r--src/comment.rs4
-rw-r--r--src/expr.rs27
-rw-r--r--src/formatting.rs458
-rw-r--r--src/items.rs6
-rw-r--r--src/lib.rs11
-rw-r--r--src/lists.rs4
-rw-r--r--src/macros.rs10
-rw-r--r--src/matches.rs7
-rw-r--r--src/missed_spans.rs25
-rw-r--r--src/modules.rs170
-rw-r--r--src/modules/visitor.rs67
-rw-r--r--src/reorder.rs4
-rw-r--r--src/rewrite.rs9
-rw-r--r--src/source_file.rs17
-rw-r--r--src/source_map.rs29
-rw-r--r--src/syntux.rs4
-rw-r--r--src/syntux/parser.rs348
-rw-r--r--src/syntux/session.rs440
-rw-r--r--src/utils.rs2
-rw-r--r--src/visitor.rs73
21 files changed, 958 insertions, 763 deletions
diff --git a/src/closures.rs b/src/closures.rs
index 396463083a8..625ef4a81f9 100644
--- a/src/closures.rs
+++ b/src/closures.rs
@@ -43,7 +43,7 @@ pub(crate) fn rewrite_closure(
 
     if let ast::ExprKind::Block(ref block, _) = body.kind {
         // The body of the closure is an empty block.
-        if block.stmts.is_empty() && !block_contains_comment(block, context.source_map) {
+        if block.stmts.is_empty() && !block_contains_comment(context, block) {
             return body
                 .rewrite(context, shape)
                 .map(|s| format!("{} {}", prefix, s));
@@ -116,7 +116,7 @@ fn needs_block(block: &ast::Block, prefix: &str, context: &RewriteContext<'_>) -
     is_unsafe_block(block)
         || block.stmts.len() > 1
         || has_attributes
-        || block_contains_comment(block, context.source_map)
+        || block_contains_comment(context, block)
         || prefix.contains('\n')
 }
 
@@ -309,7 +309,7 @@ pub(crate) fn rewrite_last_closure(
             ast::ExprKind::Block(ref block, _)
                 if !is_unsafe_block(block)
                     && !context.inside_macro()
-                    && is_simple_block(block, Some(&body.attrs), context.source_map) =>
+                    && is_simple_block(context, block, Some(&body.attrs)) =>
             {
                 stmt_expr(&block.stmts[0]).unwrap_or(body)
             }
diff --git a/src/comment.rs b/src/comment.rs
index 5b46001ff7d..518f2bcda0c 100644
--- a/src/comment.rs
+++ b/src/comment.rs
@@ -1556,10 +1556,10 @@ pub(crate) fn recover_comment_removed(
         // We missed some comments. Warn and keep the original text.
         if context.config.error_on_unformatted() {
             context.report.append(
-                context.source_map.span_to_filename(span).into(),
+                context.parse_sess.span_to_filename(span),
                 vec![FormattingError::from_span(
                     span,
-                    &context.source_map,
+                    &context.parse_sess,
                     ErrorKind::LostComment,
                 )],
             );
diff --git a/src/expr.rs b/src/expr.rs
index 84f01f2a1a9..608425b34bd 100644
--- a/src/expr.rs
+++ b/src/expr.rs
@@ -2,7 +2,7 @@ use std::borrow::Cow;
 use std::cmp::min;
 
 use itertools::Itertools;
-use rustc_span::{source_map::SourceMap, BytePos, Span};
+use rustc_span::{BytePos, Span};
 use syntax::token::{DelimToken, LitKind};
 use syntax::{ast, ptr};
 
@@ -430,7 +430,7 @@ fn rewrite_empty_block(
         return None;
     }
 
-    if !block_contains_comment(block, context.source_map) && shape.width >= 2 {
+    if !block_contains_comment(context, block) && shape.width >= 2 {
         return Some(format!("{}{}{{}}", prefix, label_str));
     }
 
@@ -487,7 +487,7 @@ fn rewrite_single_line_block(
     label: Option<ast::Label>,
     shape: Shape,
 ) -> Option<String> {
-    if is_simple_block(block, attrs, context.source_map) {
+    if is_simple_block(context, block, attrs) {
         let expr_shape = shape.offset_left(last_line_width(prefix))?;
         let expr_str = block.stmts[0].rewrite(context, expr_shape)?;
         let label_str = rewrite_label(label);
@@ -750,8 +750,8 @@ impl<'a> ControlFlow<'a> {
         let fixed_cost = self.keyword.len() + "  {  } else {  }".len();
 
         if let ast::ExprKind::Block(ref else_node, _) = else_block.kind {
-            if !is_simple_block(self.block, None, context.source_map)
-                || !is_simple_block(else_node, None, context.source_map)
+            if !is_simple_block(context, self.block, None)
+                || !is_simple_block(context, else_node, None)
                 || pat_expr_str.contains('\n')
             {
                 return None;
@@ -1134,9 +1134,8 @@ fn extract_comment(span: Span, context: &RewriteContext<'_>, shape: Shape) -> Op
     }
 }
 
-pub(crate) fn block_contains_comment(block: &ast::Block, source_map: &SourceMap) -> bool {
-    let snippet = source_map.span_to_snippet(block.span).unwrap();
-    contains_comment(&snippet)
+pub(crate) fn block_contains_comment(context: &RewriteContext<'_>, block: &ast::Block) -> bool {
+    contains_comment(context.snippet(block.span))
 }
 
 // Checks that a block contains no statements, an expression and no comments or
@@ -1144,37 +1143,37 @@ pub(crate) fn block_contains_comment(block: &ast::Block, source_map: &SourceMap)
 // FIXME: incorrectly returns false when comment is contained completely within
 // the expression.
 pub(crate) fn is_simple_block(
+    context: &RewriteContext<'_>,
     block: &ast::Block,
     attrs: Option<&[ast::Attribute]>,
-    source_map: &SourceMap,
 ) -> bool {
     block.stmts.len() == 1
         && stmt_is_expr(&block.stmts[0])
-        && !block_contains_comment(block, source_map)
+        && !block_contains_comment(context, block)
         && attrs.map_or(true, |a| a.is_empty())
 }
 
 /// Checks whether a block contains at most one statement or expression, and no
 /// comments or attributes.
 pub(crate) fn is_simple_block_stmt(
+    context: &RewriteContext<'_>,
     block: &ast::Block,
     attrs: Option<&[ast::Attribute]>,
-    source_map: &SourceMap,
 ) -> bool {
     block.stmts.len() <= 1
-        && !block_contains_comment(block, source_map)
+        && !block_contains_comment(context, block)
         && attrs.map_or(true, |a| a.is_empty())
 }
 
 /// Checks whether a block contains no statements, expressions, comments, or
 /// inner attributes.
 pub(crate) fn is_empty_block(
+    context: &RewriteContext<'_>,
     block: &ast::Block,
     attrs: Option<&[ast::Attribute]>,
-    source_map: &SourceMap,
 ) -> bool {
     block.stmts.is_empty()
-        && !block_contains_comment(block, source_map)
+        && !block_contains_comment(context, block)
         && attrs.map_or(true, |a| inner_attributes(a).is_empty())
 }
 
diff --git a/src/formatting.rs b/src/formatting.rs
index bed87748253..104e631f441 100644
--- a/src/formatting.rs
+++ b/src/formatting.rs
@@ -1,29 +1,20 @@
 // High level formatting functions.
 
-use std::cell::RefCell;
 use std::collections::HashMap;
 use std::io::{self, Write};
-use std::panic::{catch_unwind, AssertUnwindSafe};
-use std::rc::Rc;
 use std::time::{Duration, Instant};
 
-use rustc_data_structures::sync::{Lrc, Send};
-use rustc_errors::emitter::{Emitter, EmitterWriter};
-use rustc_errors::{ColorConfig, Diagnostic, DiagnosticBuilder, Handler, Level as DiagnosticLevel};
-use rustc_session::parse::ParseSess;
-use rustc_span::{
-    source_map::{FilePathMapping, SourceMap},
-    Span, DUMMY_SP,
-};
+use rustc_span::Span;
 use syntax::ast;
 
 use self::newline_style::apply_newline_style;
 use crate::comment::{CharClasses, FullCodeCharKind};
 use crate::config::{Config, FileName, Verbosity};
-use crate::ignore_path::IgnorePathSet;
 use crate::issues::BadIssueSeeker;
+use crate::syntux::parser::{DirectoryOwnership, Parser, ParserError};
+use crate::syntux::session::ParseSess;
 use crate::utils::count_newlines;
-use crate::visitor::{FmtVisitor, SnippetProvider};
+use crate::visitor::FmtVisitor;
 use crate::{modules, source_file, ErrorKind, FormatReport, Input, Session};
 
 mod newline_style;
@@ -71,54 +62,41 @@ fn format_project<T: FormatHandler>(
     let main_file = input.file_name();
     let input_is_stdin = main_file == FileName::Stdin;
 
-    let ignore_path_set = match IgnorePathSet::from_ignore_list(&config.ignore()) {
-        Ok(set) => Rc::new(set),
-        Err(e) => return Err(ErrorKind::InvalidGlobPattern(e)),
-    };
-    if config.skip_children() && ignore_path_set.is_match(&main_file) {
+    let mut parse_session = ParseSess::new(config)?;
+    if config.skip_children() && parse_session.ignore_file(&main_file) {
         return Ok(FormatReport::new());
     }
 
     // Parse the crate.
-    let can_reset_parser_errors = Rc::new(RefCell::new(false));
-    let source_map = Rc::new(SourceMap::new(FilePathMapping::empty()));
-    let mut parse_session = make_parse_sess(
-        source_map.clone(),
-        config,
-        Rc::clone(&ignore_path_set),
-        can_reset_parser_errors.clone(),
-    );
     let mut report = FormatReport::new();
     let directory_ownership = input.to_directory_ownership();
-    let krate = match parse_crate(
-        input,
-        &parse_session,
-        config,
-        &mut report,
-        directory_ownership,
-        can_reset_parser_errors.clone(),
-    ) {
+    let krate = match Parser::parse_crate(config, input, directory_ownership, &parse_session) {
         Ok(krate) => krate,
         // Surface parse error via Session (errors are merged there from report)
-        Err(ErrorKind::ParseError) => return Ok(report),
-        Err(e) => return Err(e),
+        Err(e) => {
+            let forbid_verbose = input_is_stdin || e != ParserError::ParsePanicError;
+            should_emit_verbose(forbid_verbose, config, || {
+                eprintln!("The Rust parser panicked");
+            });
+            report.add_parsing_error();
+            return Ok(report);
+        }
     };
     timer = timer.done_parsing();
 
     // Suppress error output if we have to do any further parsing.
-    let silent_emitter = silent_emitter();
-    parse_session.span_diagnostic = Handler::with_emitter(true, None, silent_emitter);
+    parse_session.set_silent_emitter();
 
     let mut context = FormatContext::new(&krate, report, parse_session, config, handler);
     let files = modules::ModResolver::new(
         &context.parse_session,
-        directory_ownership.unwrap_or(rustc_parse::DirectoryOwnership::UnownedViaMod),
+        directory_ownership.unwrap_or(DirectoryOwnership::UnownedViaMod),
         !(input_is_stdin || config.skip_children()),
     )
     .visit_crate(&krate)
     .map_err(|e| io::Error::new(io::ErrorKind::Other, e))?;
     for (path, module) in files {
-        let should_ignore = !input_is_stdin && ignore_path_set.is_match(&path);
+        let should_ignore = !input_is_stdin && context.ignore_file(&path);
         if (config.skip_children() && path != main_file) || should_ignore {
             continue;
         }
@@ -150,6 +128,10 @@ struct FormatContext<'a, T: FormatHandler> {
 }
 
 impl<'a, T: FormatHandler + 'a> FormatContext<'a, T> {
+    fn ignore_file(&self, path: &FileName) -> bool {
+        self.parse_session.ignore_file(path)
+    }
+
     // Formats a single file/module.
     fn format_file(
         &mut self,
@@ -157,15 +139,8 @@ impl<'a, T: FormatHandler + 'a> FormatContext<'a, T> {
         module: &ast::Mod,
         is_root: bool,
     ) -> Result<(), ErrorKind> {
-        let source_file = self
-            .parse_session
-            .source_map()
-            .lookup_char_pos(module.inner.lo())
-            .file;
-        let big_snippet = source_file.src.as_ref().unwrap();
-        let snippet_provider =
-            SnippetProvider::new(source_file.start_pos, source_file.end_pos, big_snippet);
-        let mut visitor = FmtVisitor::from_source_map(
+        let snippet_provider = self.parse_session.snippet_provider(module.inner);
+        let mut visitor = FmtVisitor::from_parse_sess(
             &self.parse_session,
             &self.config,
             &snippet_provider,
@@ -175,16 +150,16 @@ impl<'a, T: FormatHandler + 'a> FormatContext<'a, T> {
 
         // Format inner attributes if available.
         if !self.krate.attrs.is_empty() && is_root {
-            visitor.skip_empty_lines(source_file.end_pos);
+            visitor.skip_empty_lines(snippet_provider.end_pos());
             if visitor.visit_attrs(&self.krate.attrs, ast::AttrStyle::Inner) {
                 visitor.push_rewrite(module.inner, None);
             } else {
-                visitor.format_separate_mod(module, &*source_file);
+                visitor.format_separate_mod(module, snippet_provider.end_pos());
             }
         } else {
-            visitor.last_pos = source_file.start_pos;
-            visitor.skip_empty_lines(source_file.end_pos);
-            visitor.format_separate_mod(module, &*source_file);
+            visitor.last_pos = snippet_provider.start_pos();
+            visitor.skip_empty_lines(snippet_provider.end_pos());
+            visitor.format_separate_mod(module, snippet_provider.end_pos());
         };
 
         debug_assert_eq!(
@@ -209,7 +184,7 @@ impl<'a, T: FormatHandler + 'a> FormatContext<'a, T> {
         apply_newline_style(
             self.config.newline_style(),
             &mut visitor.buffer,
-            &big_snippet,
+            snippet_provider.entire_snippet(),
         );
 
         if visitor.macro_rewrite_failure {
@@ -219,7 +194,7 @@ impl<'a, T: FormatHandler + 'a> FormatContext<'a, T> {
             .add_non_formatted_ranges(visitor.skipped_range.borrow().clone());
 
         self.handler.handle_formatted_file(
-            self.parse_session.source_map(),
+            &self.parse_session,
             path,
             visitor.buffer.to_owned(),
             &mut self.report,
@@ -231,7 +206,7 @@ impl<'a, T: FormatHandler + 'a> FormatContext<'a, T> {
 trait FormatHandler {
     fn handle_formatted_file(
         &mut self,
-        source_map: &SourceMap,
+        parse_session: &ParseSess,
         path: FileName,
         result: String,
         report: &mut FormatReport,
@@ -242,14 +217,14 @@ impl<'b, T: Write + 'b> FormatHandler for Session<'b, T> {
     // Called for each formatted file.
     fn handle_formatted_file(
         &mut self,
-        source_map: &SourceMap,
+        parse_session: &ParseSess,
         path: FileName,
         result: String,
         report: &mut FormatReport,
     ) -> Result<(), ErrorKind> {
         if let Some(ref mut out) = self.out {
             match source_file::write_file(
-                Some(source_map),
+                Some(parse_session),
                 &path,
                 &result,
                 out,
@@ -282,23 +257,15 @@ pub(crate) struct FormattingError {
 impl FormattingError {
     pub(crate) fn from_span(
         span: Span,
-        source_map: &SourceMap,
+        parse_sess: &ParseSess,
         kind: ErrorKind,
     ) -> FormattingError {
         FormattingError {
-            line: source_map.lookup_char_pos(span.lo()).line,
+            line: parse_sess.line_of_byte_pos(span.lo()),
             is_comment: kind.is_comment(),
             kind,
             is_string: false,
-            line_buffer: source_map
-                .span_to_lines(span)
-                .ok()
-                .and_then(|fl| {
-                    fl.file
-                        .get_line(fl.lines[0].line_index)
-                        .map(std::borrow::Cow::into_owned)
-                })
-                .unwrap_or_else(String::new),
+            line_buffer: parse_sess.span_to_first_line_string(span),
         }
     }
 
@@ -633,358 +600,11 @@ impl<'a> FormatLines<'a> {
     }
 }
 
-fn parse_crate(
-    input: Input,
-    parse_session: &ParseSess,
-    config: &Config,
-    report: &mut FormatReport,
-    directory_ownership: Option<rustc_parse::DirectoryOwnership>,
-    can_reset_parser_errors: Rc<RefCell<bool>>,
-) -> Result<ast::Crate, ErrorKind> {
-    let input_is_stdin = input.is_text();
-
-    let parser = match input {
-        Input::File(ref file) => {
-            // Use `new_sub_parser_from_file` when we the input is a submodule.
-            Ok(if let Some(dir_own) = directory_ownership {
-                rustc_parse::new_sub_parser_from_file(parse_session, file, dir_own, None, DUMMY_SP)
-            } else {
-                rustc_parse::new_parser_from_file(parse_session, file)
-            })
-        }
-        Input::Text(text) => rustc_parse::maybe_new_parser_from_source_str(
-            parse_session,
-            rustc_span::FileName::Custom("stdin".to_owned()),
-            text,
-        )
-        .map(|mut parser| {
-            parser.recurse_into_file_modules = false;
-            parser
-        }),
-    };
-
-    let result = match parser {
-        Ok(mut parser) => {
-            parser.cfg_mods = false;
-            if config.skip_children() {
-                parser.recurse_into_file_modules = false;
-            }
-
-            let mut parser = AssertUnwindSafe(parser);
-            catch_unwind(move || parser.0.parse_crate_mod().map_err(|d| vec![d]))
-        }
-        Err(diagnostics) => {
-            for diagnostic in diagnostics {
-                parse_session.span_diagnostic.emit_diagnostic(&diagnostic);
-            }
-            report.add_parsing_error();
-            return Err(ErrorKind::ParseError);
-        }
-    };
-
-    match result {
-        Ok(Ok(c)) => {
-            if !parse_session.span_diagnostic.has_errors() {
-                return Ok(c);
-            }
-            // This scenario occurs when the parser encountered errors
-            // but was still able to recover. If all of the parser errors
-            // occurred in files that are ignored, then reset
-            // the error count and continue.
-            // https://github.com/rust-lang/rustfmt/issues/3779
-            if *can_reset_parser_errors.borrow() {
-                parse_session.span_diagnostic.reset_err_count();
-                return Ok(c);
-            }
-        }
-        Ok(Err(mut diagnostics)) => diagnostics.iter_mut().for_each(DiagnosticBuilder::emit),
-        Err(_) => {
-            // Note that if you see this message and want more information,
-            // then run the `parse_crate_mod` function above without
-            // `catch_unwind` so rustfmt panics and you can get a backtrace.
-            should_emit_verbose(input_is_stdin, config, || {
-                println!("The Rust parser panicked")
-            });
-        }
-    }
-
-    report.add_parsing_error();
-    Err(ErrorKind::ParseError)
-}
-
-struct SilentOnIgnoredFilesEmitter {
-    ignore_path_set: Rc<IgnorePathSet>,
-    source_map: Rc<SourceMap>,
-    emitter: Box<dyn Emitter + Send>,
-    has_non_ignorable_parser_errors: bool,
-    can_reset: Rc<RefCell<bool>>,
-}
-
-impl SilentOnIgnoredFilesEmitter {
-    fn handle_non_ignoreable_error(&mut self, db: &Diagnostic) {
-        self.has_non_ignorable_parser_errors = true;
-        *self.can_reset.borrow_mut() = false;
-        self.emitter.emit_diagnostic(db);
-    }
-}
-
-impl Emitter for SilentOnIgnoredFilesEmitter {
-    fn source_map(&self) -> Option<&Lrc<SourceMap>> {
-        None
-    }
-
-    fn emit_diagnostic(&mut self, db: &Diagnostic) {
-        if db.level == DiagnosticLevel::Fatal {
-            return self.handle_non_ignoreable_error(db);
-        }
-        if let Some(primary_span) = &db.span.primary_span() {
-            let file_name = self.source_map.span_to_filename(*primary_span);
-            if let rustc_span::FileName::Real(ref path) = file_name {
-                if self
-                    .ignore_path_set
-                    .is_match(&FileName::Real(path.to_path_buf()))
-                {
-                    if !self.has_non_ignorable_parser_errors {
-                        *self.can_reset.borrow_mut() = true;
-                    }
-                    return;
-                }
-            };
-        }
-        self.handle_non_ignoreable_error(db);
-    }
-}
-
-/// Emitter which discards every error.
-struct SilentEmitter;
-
-impl Emitter for SilentEmitter {
-    fn source_map(&self) -> Option<&Lrc<SourceMap>> {
-        None
-    }
-    fn emit_diagnostic(&mut self, _db: &Diagnostic) {}
-}
-
-fn silent_emitter() -> Box<dyn Emitter + Send> {
-    Box::new(SilentEmitter {})
-}
-
-fn make_parse_sess(
-    source_map: Rc<SourceMap>,
-    config: &Config,
-    ignore_path_set: Rc<IgnorePathSet>,
-    can_reset: Rc<RefCell<bool>>,
-) -> ParseSess {
-    let supports_color = term::stderr().map_or(false, |term| term.supports_color());
-    let color_cfg = if supports_color {
-        ColorConfig::Auto
-    } else {
-        ColorConfig::Never
-    };
-
-    let emitter = if config.hide_parse_errors() {
-        silent_emitter()
-    } else {
-        Box::new(EmitterWriter::stderr(
-            color_cfg,
-            Some(source_map.clone()),
-            false,
-            false,
-            None,
-            false,
-        ))
-    };
-    let handler = Handler::with_emitter(
-        true,
-        None,
-        Box::new(SilentOnIgnoredFilesEmitter {
-            has_non_ignorable_parser_errors: false,
-            source_map: source_map.clone(),
-            emitter,
-            ignore_path_set,
-            can_reset,
-        }),
-    );
-
-    ParseSess::with_span_handler(handler, source_map)
-}
-
-fn should_emit_verbose<F>(is_stdin: bool, config: &Config, f: F)
+fn should_emit_verbose<F>(forbid_verbose_output: bool, config: &Config, f: F)
 where
     F: Fn(),
 {
-    if config.verbose() == Verbosity::Verbose && !is_stdin {
+    if config.verbose() == Verbosity::Verbose && !forbid_verbose_output {
         f();
     }
 }
-
-#[cfg(test)]
-mod tests {
-    use super::*;
-
-    mod emitter {
-        use super::*;
-        use crate::config::IgnoreList;
-        use crate::is_nightly_channel;
-        use crate::utils::mk_sp;
-        use rustc_span::{BytePos, FileName as SourceMapFileName, MultiSpan, DUMMY_SP};
-        use std::path::{Path, PathBuf};
-
-        struct TestEmitter {
-            num_emitted_errors: Rc<RefCell<u32>>,
-        }
-
-        impl Emitter for TestEmitter {
-            fn source_map(&self) -> Option<&Lrc<SourceMap>> {
-                None
-            }
-            fn emit_diagnostic(&mut self, _db: &Diagnostic) {
-                *self.num_emitted_errors.borrow_mut() += 1;
-            }
-        }
-
-        fn build_diagnostic(level: DiagnosticLevel, span: Option<MultiSpan>) -> Diagnostic {
-            Diagnostic {
-                level,
-                code: None,
-                message: vec![],
-                children: vec![],
-                suggestions: vec![],
-                span: span.unwrap_or_else(MultiSpan::new),
-                sort_span: DUMMY_SP,
-            }
-        }
-
-        fn build_emitter(
-            num_emitted_errors: Rc<RefCell<u32>>,
-            can_reset: Rc<RefCell<bool>>,
-            source_map: Option<Rc<SourceMap>>,
-            ignore_list: Option<IgnoreList>,
-        ) -> SilentOnIgnoredFilesEmitter {
-            let emitter_writer = TestEmitter { num_emitted_errors };
-            let source_map =
-                source_map.unwrap_or_else(|| Rc::new(SourceMap::new(FilePathMapping::empty())));
-            let ignore_path_set =
-                Rc::new(IgnorePathSet::from_ignore_list(&ignore_list.unwrap_or_default()).unwrap());
-            SilentOnIgnoredFilesEmitter {
-                has_non_ignorable_parser_errors: false,
-                source_map,
-                emitter: Box::new(emitter_writer),
-                ignore_path_set,
-                can_reset,
-            }
-        }
-
-        fn get_ignore_list(config: &str) -> IgnoreList {
-            Config::from_toml(config, Path::new("")).unwrap().ignore()
-        }
-
-        #[test]
-        fn handles_fatal_parse_error_in_ignored_file() {
-            let num_emitted_errors = Rc::new(RefCell::new(0));
-            let can_reset_errors = Rc::new(RefCell::new(false));
-            let ignore_list = get_ignore_list(r#"ignore = ["foo.rs"]"#);
-            let source_map = Rc::new(SourceMap::new(FilePathMapping::empty()));
-            let source =
-                String::from(r#"extern "system" fn jni_symbol!( funcName ) ( ... ) -> {} "#);
-            source_map.new_source_file(SourceMapFileName::Real(PathBuf::from("foo.rs")), source);
-            let mut emitter = build_emitter(
-                Rc::clone(&num_emitted_errors),
-                Rc::clone(&can_reset_errors),
-                Some(Rc::clone(&source_map)),
-                Some(ignore_list),
-            );
-            let span = MultiSpan::from_span(mk_sp(BytePos(0), BytePos(1)));
-            let fatal_diagnostic = build_diagnostic(DiagnosticLevel::Fatal, Some(span));
-            emitter.emit_diagnostic(&fatal_diagnostic);
-            assert_eq!(*num_emitted_errors.borrow(), 1);
-            assert_eq!(*can_reset_errors.borrow(), false);
-        }
-
-        #[test]
-        fn handles_recoverable_parse_error_in_ignored_file() {
-            if !is_nightly_channel!() {
-                return;
-            }
-            let num_emitted_errors = Rc::new(RefCell::new(0));
-            let can_reset_errors = Rc::new(RefCell::new(false));
-            let ignore_list = get_ignore_list(r#"ignore = ["foo.rs"]"#);
-            let source_map = Rc::new(SourceMap::new(FilePathMapping::empty()));
-            let source = String::from(r#"pub fn bar() { 1x; }"#);
-            source_map.new_source_file(SourceMapFileName::Real(PathBuf::from("foo.rs")), source);
-            let mut emitter = build_emitter(
-                Rc::clone(&num_emitted_errors),
-                Rc::clone(&can_reset_errors),
-                Some(Rc::clone(&source_map)),
-                Some(ignore_list),
-            );
-            let span = MultiSpan::from_span(mk_sp(BytePos(0), BytePos(1)));
-            let non_fatal_diagnostic = build_diagnostic(DiagnosticLevel::Warning, Some(span));
-            emitter.emit_diagnostic(&non_fatal_diagnostic);
-            assert_eq!(*num_emitted_errors.borrow(), 0);
-            assert_eq!(*can_reset_errors.borrow(), true);
-        }
-
-        #[test]
-        fn handles_recoverable_parse_error_in_non_ignored_file() {
-            if !is_nightly_channel!() {
-                return;
-            }
-            let num_emitted_errors = Rc::new(RefCell::new(0));
-            let can_reset_errors = Rc::new(RefCell::new(false));
-            let source_map = Rc::new(SourceMap::new(FilePathMapping::empty()));
-            let source = String::from(r#"pub fn bar() { 1x; }"#);
-            source_map.new_source_file(SourceMapFileName::Real(PathBuf::from("foo.rs")), source);
-            let mut emitter = build_emitter(
-                Rc::clone(&num_emitted_errors),
-                Rc::clone(&can_reset_errors),
-                Some(Rc::clone(&source_map)),
-                None,
-            );
-            let span = MultiSpan::from_span(mk_sp(BytePos(0), BytePos(1)));
-            let non_fatal_diagnostic = build_diagnostic(DiagnosticLevel::Warning, Some(span));
-            emitter.emit_diagnostic(&non_fatal_diagnostic);
-            assert_eq!(*num_emitted_errors.borrow(), 1);
-            assert_eq!(*can_reset_errors.borrow(), false);
-        }
-
-        #[test]
-        fn handles_mix_of_recoverable_parse_error() {
-            if !is_nightly_channel!() {
-                return;
-            }
-            let num_emitted_errors = Rc::new(RefCell::new(0));
-            let can_reset_errors = Rc::new(RefCell::new(false));
-            let source_map = Rc::new(SourceMap::new(FilePathMapping::empty()));
-            let ignore_list = get_ignore_list(r#"ignore = ["foo.rs"]"#);
-            let bar_source = String::from(r#"pub fn bar() { 1x; }"#);
-            let foo_source = String::from(r#"pub fn foo() { 1x; }"#);
-            let fatal_source =
-                String::from(r#"extern "system" fn jni_symbol!( funcName ) ( ... ) -> {} "#);
-            source_map
-                .new_source_file(SourceMapFileName::Real(PathBuf::from("bar.rs")), bar_source);
-            source_map
-                .new_source_file(SourceMapFileName::Real(PathBuf::from("foo.rs")), foo_source);
-            source_map.new_source_file(
-                SourceMapFileName::Real(PathBuf::from("fatal.rs")),
-                fatal_source,
-            );
-            let mut emitter = build_emitter(
-                Rc::clone(&num_emitted_errors),
-                Rc::clone(&can_reset_errors),
-                Some(Rc::clone(&source_map)),
-                Some(ignore_list),
-            );
-            let bar_span = MultiSpan::from_span(mk_sp(BytePos(0), BytePos(1)));
-            let foo_span = MultiSpan::from_span(mk_sp(BytePos(21), BytePos(22)));
-            let bar_diagnostic = build_diagnostic(DiagnosticLevel::Warning, Some(bar_span));
-            let foo_diagnostic = build_diagnostic(DiagnosticLevel::Warning, Some(foo_span));
-            let fatal_diagnostic = build_diagnostic(DiagnosticLevel::Fatal, None);
-            emitter.emit_diagnostic(&bar_diagnostic);
-            emitter.emit_diagnostic(&foo_diagnostic);
-            emitter.emit_diagnostic(&fatal_diagnostic);
-            assert_eq!(*num_emitted_errors.borrow(), 2);
-            assert_eq!(*can_reset_errors.borrow(), false);
-        }
-    }
-}
diff --git a/src/items.rs b/src/items.rs
index 8c7be672cac..9bd254250cc 100644
--- a/src/items.rs
+++ b/src/items.rs
@@ -363,17 +363,17 @@ impl<'a> FmtVisitor<'a> {
             return None;
         }
 
-        let source_map = self.get_context().source_map;
+        let context = self.get_context();
 
         if self.config.empty_item_single_line()
-            && is_empty_block(block, None, source_map)
+            && is_empty_block(&context, block, None)
             && self.block_indent.width() + fn_str.len() + 3 <= self.config.max_width()
             && !last_line_contains_single_line_comment(fn_str)
         {
             return Some(format!("{} {{}}", fn_str));
         }
 
-        if !self.config.fn_single_line() || !is_simple_block_stmt(block, None, source_map) {
+        if !self.config.fn_single_line() || !is_simple_block_stmt(&context, block, None) {
             return None;
         }
 
diff --git a/src/lib.rs b/src/lib.rs
index f0783b2ca36..dbd9469908e 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -3,7 +3,6 @@
 
 #[macro_use]
 extern crate derive_new;
-#[cfg(test)]
 #[macro_use]
 extern crate lazy_static;
 #[macro_use]
@@ -20,7 +19,6 @@ use std::rc::Rc;
 
 use failure::Fail;
 use ignore;
-use rustc_parse::DirectoryOwnership;
 use syntax::ast;
 
 use crate::comment::LineClasses;
@@ -28,6 +26,7 @@ use crate::emitter::Emitter;
 use crate::formatting::{FormatErrorMap, FormattingError, ReportedErrors, SourceFile};
 use crate::issues::Issue;
 use crate::shape::Indent;
+use crate::syntux::parser::DirectoryOwnership;
 use crate::utils::indent_next_line;
 
 pub use crate::config::{
@@ -75,6 +74,7 @@ pub(crate) mod source_map;
 mod spanned;
 mod stmt;
 mod string;
+mod syntux;
 #[cfg(test)]
 mod test;
 mod types;
@@ -509,13 +509,6 @@ pub enum Input {
 }
 
 impl Input {
-    fn is_text(&self) -> bool {
-        match *self {
-            Input::File(_) => false,
-            Input::Text(_) => true,
-        }
-    }
-
     fn file_name(&self) -> FileName {
         match *self {
             Input::File(ref file) => FileName::Real(file.clone()),
diff --git a/src/lists.rs b/src/lists.rs
index 96a10924e25..bc55ef6686a 100644
--- a/src/lists.rs
+++ b/src/lists.rs
@@ -560,7 +560,7 @@ pub(crate) struct ListItems<'a, I, F1, F2, F3>
 where
     I: Iterator,
 {
-    snippet_provider: &'a SnippetProvider<'a>,
+    snippet_provider: &'a SnippetProvider,
     inner: Peekable<I>,
     get_lo: F1,
     get_hi: F2,
@@ -777,7 +777,7 @@ where
 #[allow(clippy::too_many_arguments)]
 // Creates an iterator over a list's items with associated comments.
 pub(crate) fn itemize_list<'a, T, I, F1, F2, F3>(
-    snippet_provider: &'a SnippetProvider<'_>,
+    snippet_provider: &'a SnippetProvider,
     inner: I,
     terminator: &'a str,
     separator: &'a str,
diff --git a/src/macros.rs b/src/macros.rs
index 858c2917aa9..482b680310c 100644
--- a/src/macros.rs
+++ b/src/macros.rs
@@ -178,8 +178,8 @@ fn return_macro_parse_failure_fallback(
     }
 
     context.skipped_range.borrow_mut().push((
-        context.source_map.lookup_line(span.lo()).unwrap().line,
-        context.source_map.lookup_line(span.hi()).unwrap().line,
+        context.parse_sess.line_of_byte_pos(span.lo()),
+        context.parse_sess.line_of_byte_pos(span.hi()),
     ));
 
     // Return the snippet unmodified if the macro is not block-like
@@ -286,7 +286,7 @@ fn rewrite_macro_inner(
         }
     }
 
-    let mut parser = new_parser_from_tts(context.parse_session, ts.trees().collect());
+    let mut parser = new_parser_from_tts(context.parse_sess.inner(), ts.trees().collect());
     let mut arg_vec = Vec::new();
     let mut vec_with_semi = false;
     let mut trailing_comma = false;
@@ -1190,7 +1190,7 @@ pub(crate) fn convert_try_mac(mac: &ast::Mac, context: &RewriteContext<'_>) -> O
     let path = &pprust::path_to_string(&mac.path);
     if path == "try" || path == "r#try" {
         let ts = mac.args.inner_tokens();
-        let mut parser = new_parser_from_tts(context.parse_session, ts.trees().collect());
+        let mut parser = new_parser_from_tts(context.parse_sess.inner(), ts.trees().collect());
 
         Some(ast::Expr {
             id: ast::NodeId::root(), // dummy value
@@ -1422,7 +1422,7 @@ fn format_lazy_static(
     ts: &TokenStream,
 ) -> Option<String> {
     let mut result = String::with_capacity(1024);
-    let mut parser = new_parser_from_tts(context.parse_session, ts.trees().collect());
+    let mut parser = new_parser_from_tts(context.parse_sess.inner(), ts.trees().collect());
     let nested_shape = shape
         .block_indent(context.config.tab_spaces())
         .with_max_width(context.config);
diff --git a/src/matches.rs b/src/matches.rs
index 954db40ae52..792b3bfd466 100644
--- a/src/matches.rs
+++ b/src/matches.rs
@@ -276,7 +276,7 @@ fn block_can_be_flattened<'a>(
         ast::ExprKind::Block(ref block, _)
             if !is_unsafe_block(block)
                 && !context.inside_macro()
-                && is_simple_block(block, Some(&expr.attrs), context.source_map) =>
+                && is_simple_block(context, block, Some(&expr.attrs)) =>
         {
             Some(&*block)
         }
@@ -332,10 +332,7 @@ fn rewrite_match_body(
         shape.offset_left(extra_offset(pats_str, shape) + 4),
     );
     let (is_block, is_empty_block) = if let ast::ExprKind::Block(ref block, _) = body.kind {
-        (
-            true,
-            is_empty_block(block, Some(&body.attrs), context.source_map),
-        )
+        (true, is_empty_block(context, block, Some(&body.attrs)))
     } else {
         (false, false)
     };
diff --git a/src/missed_spans.rs b/src/missed_spans.rs
index 99595436f89..e6889ae9786 100644
--- a/src/missed_spans.rs
+++ b/src/missed_spans.rs
@@ -86,9 +86,8 @@ impl<'a> FmtVisitor<'a> {
 
         assert!(
             start < end,
-            "Request to format inverted span: {:?} to {:?}",
-            self.source_map.lookup_char_pos(start),
-            self.source_map.lookup_char_pos(end)
+            "Request to format inverted span: {}",
+            self.parse_sess.span_to_debug_info(mk_sp(start, end)),
         );
 
         self.last_pos = end;
@@ -139,26 +138,22 @@ impl<'a> FmtVisitor<'a> {
         // Get a snippet from the file start to the span's hi without allocating.
         // We need it to determine what precedes the current comment. If the comment
         // follows code on the same line, we won't touch it.
-        let big_span_lo = self.source_map.lookup_char_pos(span.lo()).file.start_pos;
-        let local_begin = self.source_map.lookup_byte_offset(big_span_lo);
-        let local_end = self.source_map.lookup_byte_offset(span.hi());
-        let start_index = local_begin.pos.to_usize();
-        let end_index = local_end.pos.to_usize();
-        let big_snippet = &local_begin.sf.src.as_ref().unwrap()[start_index..end_index];
-
+        let big_span_lo = self.snippet_provider.start_pos();
+        let big_snippet = self.snippet_provider.entire_snippet();
         let big_diff = (span.lo() - big_span_lo).to_usize();
+
         let snippet = self.snippet(span);
 
         debug!("write_snippet `{}`", snippet);
 
-        self.write_snippet_inner(big_snippet, big_diff, snippet, span, process_last_snippet);
+        self.write_snippet_inner(big_snippet, snippet, big_diff, span, process_last_snippet);
     }
 
     fn write_snippet_inner<F>(
         &mut self,
         big_snippet: &str,
-        big_diff: usize,
         old_snippet: &str,
+        big_diff: usize,
         span: Span,
         process_last_snippet: F,
     ) where
@@ -167,9 +162,9 @@ impl<'a> FmtVisitor<'a> {
         // Trim whitespace from the right hand side of each line.
         // Annoyingly, the library functions for splitting by lines etc. are not
         // quite right, so we must do it ourselves.
-        let char_pos = self.source_map.lookup_char_pos(span.lo());
-        let file_name = &char_pos.file.name.clone().into();
-        let mut status = SnippetStatus::new(char_pos.line);
+        let line = self.parse_sess.line_of_byte_pos(span.lo());
+        let file_name = &self.parse_sess.span_to_filename(span);
+        let mut status = SnippetStatus::new(line);
 
         let snippet = &*transform_missing_snippet(self.config, old_snippet);
 
diff --git a/src/modules.rs b/src/modules.rs
index 99bd832997a..bb0effedfa8 100644
--- a/src/modules.rs
+++ b/src/modules.rs
@@ -2,24 +2,25 @@ use std::borrow::Cow;
 use std::collections::BTreeMap;
 use std::path::{Path, PathBuf};
 
-use rustc_errors::PResult;
-use rustc_parse::{new_sub_parser_from_file, parser, DirectoryOwnership};
-use rustc_session::parse::ParseSess;
 use rustc_span::symbol::{sym, Symbol};
-use rustc_span::{source_map, Span, DUMMY_SP};
 use syntax::ast;
-use syntax::token::TokenKind;
 use syntax::visit::Visitor;
 
 use crate::attr::MetaVisitor;
 use crate::config::FileName;
 use crate::items::is_mod_decl;
+use crate::syntux::parser::{Directory, DirectoryOwnership, ModulePathSuccess, Parser};
+use crate::syntux::session::ParseSess;
 use crate::utils::contains_skip;
 
 mod visitor;
 
 type FileModMap<'ast> = BTreeMap<FileName, Cow<'ast, ast::Mod>>;
 
+lazy_static! {
+    static ref CFG_IF: Symbol = Symbol::intern("cfg_if");
+}
+
 /// Maps each module to the corresponding file.
 pub(crate) struct ModResolver<'ast, 'sess> {
     parse_sess: &'sess ParseSess,
@@ -29,21 +30,6 @@ pub(crate) struct ModResolver<'ast, 'sess> {
 }
 
 #[derive(Clone)]
-struct Directory {
-    path: PathBuf,
-    ownership: DirectoryOwnership,
-}
-
-impl<'a> Directory {
-    fn to_syntax_directory(&'a self) -> rustc_parse::Directory {
-        rustc_parse::Directory {
-            path: self.path.clone(),
-            ownership: self.ownership.clone(),
-        }
-    }
-}
-
-#[derive(Clone)]
 enum SubModKind<'a, 'ast> {
     /// `mod foo;`
     External(PathBuf, DirectoryOwnership),
@@ -78,12 +64,9 @@ impl<'ast, 'sess, 'c> ModResolver<'ast, 'sess> {
         mut self,
         krate: &'ast ast::Crate,
     ) -> Result<FileModMap<'ast>, String> {
-        let root_filename = self.parse_sess.source_map().span_to_filename(krate.span);
+        let root_filename = self.parse_sess.span_to_filename(krate.span);
         self.directory.path = match root_filename {
-            source_map::FileName::Real(ref path) => path
-                .parent()
-                .expect("Parent directory should exists")
-                .to_path_buf(),
+            FileName::Real(ref p) => p.parent().unwrap_or(Path::new("")).to_path_buf(),
             _ => PathBuf::new(),
         };
 
@@ -93,14 +76,13 @@ impl<'ast, 'sess, 'c> ModResolver<'ast, 'sess> {
         }
 
         self.file_map
-            .insert(root_filename.into(), Cow::Borrowed(&krate.module));
+            .insert(root_filename, Cow::Borrowed(&krate.module));
         Ok(self.file_map)
     }
 
     /// Visit `cfg_if` macro and look for module declarations.
     fn visit_cfg_if(&mut self, item: Cow<'ast, ast::Item>) -> Result<(), String> {
-        let mut visitor =
-            visitor::CfgIfVisitor::new(self.parse_sess, self.directory.to_syntax_directory());
+        let mut visitor = visitor::CfgIfVisitor::new(self.parse_sess, &self.directory);
         visitor.visit_item(&item);
         for module_item in visitor.mods() {
             if let ast::ItemKind::Mod(ref sub_mod) = module_item.item.kind {
@@ -258,7 +240,7 @@ impl<'ast, 'sess, 'c> ModResolver<'ast, 'sess> {
         attrs: &[ast::Attribute],
         sub_mod: &Cow<'ast, ast::Mod>,
     ) -> Result<SubModKind<'c, 'ast>, String> {
-        if let Some(path) = parser::Parser::submod_path_from_attr(attrs, &self.directory.path) {
+        if let Some(path) = Parser::submod_path_from_attr(attrs, &self.directory.path) {
             return Ok(SubModKind::External(
                 path,
                 DirectoryOwnership::Owned { relative: None },
@@ -266,23 +248,18 @@ impl<'ast, 'sess, 'c> ModResolver<'ast, 'sess> {
         }
 
         // Look for nested path, like `#[cfg_attr(feature = "foo", path = "bar.rs")]`.
-        let mut mods_outside_ast = self
-            .find_mods_ouside_of_ast(attrs, sub_mod)
-            .unwrap_or(vec![]);
+        let mut mods_outside_ast = self.find_mods_outside_of_ast(attrs, sub_mod);
 
         let relative = match self.directory.ownership {
             DirectoryOwnership::Owned { relative } => relative,
             DirectoryOwnership::UnownedViaBlock | DirectoryOwnership::UnownedViaMod => None,
         };
-        match parser::Parser::default_submod_path(
-            mod_name,
-            relative,
-            &self.directory.path,
-            self.parse_sess.source_map(),
-        )
-        .result
+        match self
+            .parse_sess
+            .default_submod_path(mod_name, relative, &self.directory.path)
+            .result
         {
-            Ok(parser::ModulePathSuccess {
+            Ok(ModulePathSuccess {
                 path,
                 directory_ownership,
                 ..
@@ -323,21 +300,7 @@ impl<'ast, 'sess, 'c> ModResolver<'ast, 'sess> {
         }
     }
 
-    fn find_mods_ouside_of_ast(
-        &self,
-        attrs: &[ast::Attribute],
-        sub_mod: &Cow<'ast, ast::Mod>,
-    ) -> Option<Vec<(PathBuf, DirectoryOwnership, Cow<'ast, ast::Mod>)>> {
-        use std::panic::{catch_unwind, AssertUnwindSafe};
-        Some(
-            catch_unwind(AssertUnwindSafe(|| {
-                self.find_mods_ouside_of_ast_inner(attrs, sub_mod)
-            }))
-            .ok()?,
-        )
-    }
-
-    fn find_mods_ouside_of_ast_inner(
+    fn find_mods_outside_of_ast(
         &self,
         attrs: &[ast::Attribute],
         sub_mod: &Cow<'ast, ast::Mod>,
@@ -356,14 +319,8 @@ impl<'ast, 'sess, 'c> ModResolver<'ast, 'sess> {
             if !actual_path.exists() {
                 continue;
             }
-            let file_name = rustc_span::FileName::Real(actual_path.clone());
-            if self
-                .parse_sess
-                .source_map()
-                .get_source_file(&file_name)
-                .is_some()
-            {
-                // If the specfied file is already parsed, then we just use that.
+            if self.parse_sess.is_file_parsed(&actual_path) {
+                // If the specified file is already parsed, then we just use that.
                 result.push((
                     actual_path,
                     DirectoryOwnership::Owned { relative: None },
@@ -371,32 +328,15 @@ impl<'ast, 'sess, 'c> ModResolver<'ast, 'sess> {
                 ));
                 continue;
             }
-            let mut parser = new_sub_parser_from_file(
+            let m = match Parser::parse_file_as_module(
+                self.directory.ownership,
                 self.parse_sess,
                 &actual_path,
-                self.directory.ownership,
-                None,
-                DUMMY_SP,
-            );
-            parser.cfg_mods = false;
-            let lo = parser.token.span;
-            // FIXME(topecongiro) Format inner attributes (#3606).
-            let _mod_attrs = match parse_inner_attributes(&mut parser) {
-                Ok(attrs) => attrs,
-                Err(mut e) => {
-                    e.cancel();
-                    parser.sess.span_diagnostic.reset_err_count();
-                    continue;
-                }
-            };
-            let m = match parse_mod_items(&mut parser, lo) {
-                Ok(m) => m,
-                Err(mut e) => {
-                    e.cancel();
-                    parser.sess.span_diagnostic.reset_err_count();
-                    continue;
-                }
+            ) {
+                Some(m) => m,
+                None => continue,
             };
+
             result.push((
                 actual_path,
                 DirectoryOwnership::Owned { relative: None },
@@ -422,67 +362,11 @@ fn find_path_value(attrs: &[ast::Attribute]) -> Option<Symbol> {
     attrs.iter().flat_map(path_value).next()
 }
 
-// FIXME(topecongiro) Use the method from libsyntax[1] once it become public.
-//
-// [1] https://github.com/rust-lang/rust/blob/master/src/libsyntax/parse/attr.rs
-fn parse_inner_attributes<'a>(parser: &mut parser::Parser<'a>) -> PResult<'a, Vec<ast::Attribute>> {
-    let mut attrs: Vec<ast::Attribute> = vec![];
-    loop {
-        match parser.token.kind {
-            TokenKind::Pound => {
-                // Don't even try to parse if it's not an inner attribute.
-                if !parser.look_ahead(1, |t| t == &TokenKind::Not) {
-                    break;
-                }
-
-                let attr = parser.parse_attribute(true)?;
-                assert_eq!(attr.style, ast::AttrStyle::Inner);
-                attrs.push(attr);
-            }
-            TokenKind::DocComment(s) => {
-                // we need to get the position of this token before we bump.
-                let attr = syntax::attr::mk_doc_comment(
-                    syntax::util::comments::doc_comment_style(&s.as_str()),
-                    s,
-                    parser.token.span,
-                );
-                if attr.style == ast::AttrStyle::Inner {
-                    attrs.push(attr);
-                    parser.bump();
-                } else {
-                    break;
-                }
-            }
-            _ => break,
-        }
-    }
-    Ok(attrs)
-}
-
-fn parse_mod_items<'a>(parser: &mut parser::Parser<'a>, inner_lo: Span) -> PResult<'a, ast::Mod> {
-    let mut items = vec![];
-    while let Some(item) = parser.parse_item()? {
-        items.push(item);
-    }
-
-    let hi = if parser.token.span.is_dummy() {
-        inner_lo
-    } else {
-        parser.prev_token.span
-    };
-
-    Ok(ast::Mod {
-        inner: inner_lo.to(hi),
-        items,
-        inline: false,
-    })
-}
-
 fn is_cfg_if(item: &ast::Item) -> bool {
     match item.kind {
         ast::ItemKind::Mac(ref mac) => {
             if let Some(first_segment) = mac.path.segments.first() {
-                if first_segment.ident.name == Symbol::intern("cfg_if") {
+                if first_segment.ident.name == *CFG_IF {
                     return true;
                 }
             }
diff --git a/src/modules/visitor.rs b/src/modules/visitor.rs
index a2055b6f4ea..ad9111f7a9f 100644
--- a/src/modules/visitor.rs
+++ b/src/modules/visitor.rs
@@ -1,11 +1,10 @@
-use rustc_parse::{stream_to_parser_with_base_dir, Directory};
-use rustc_session::parse::ParseSess;
-use rustc_span::{symbol::kw, Symbol};
+use rustc_span::Symbol;
 use syntax::ast;
-use syntax::token::{DelimToken, TokenKind};
 use syntax::visit::Visitor;
 
 use crate::attr::MetaVisitor;
+use crate::syntux::parser::{Directory, Parser};
+use crate::syntux::session::ParseSess;
 
 pub(crate) struct ModItem {
     pub(crate) item: ast::Item,
@@ -15,11 +14,11 @@ pub(crate) struct ModItem {
 pub(crate) struct CfgIfVisitor<'a> {
     parse_sess: &'a ParseSess,
     mods: Vec<ModItem>,
-    base_dir: Directory,
+    base_dir: &'a Directory,
 }
 
 impl<'a> CfgIfVisitor<'a> {
-    pub(crate) fn new(parse_sess: &'a ParseSess, base_dir: Directory) -> CfgIfVisitor<'a> {
+    pub(crate) fn new(parse_sess: &'a ParseSess, base_dir: &'a Directory) -> CfgIfVisitor<'a> {
         CfgIfVisitor {
             mods: vec![],
             parse_sess,
@@ -65,59 +64,9 @@ impl<'a, 'ast: 'a> CfgIfVisitor<'a> {
             }
         };
 
-        let ts = mac.args.inner_tokens();
-        let mut parser =
-            stream_to_parser_with_base_dir(self.parse_sess, ts.clone(), self.base_dir.clone());
-        parser.cfg_mods = false;
-        let mut process_if_cfg = true;
-
-        while parser.token.kind != TokenKind::Eof {
-            if process_if_cfg {
-                if !parser.eat_keyword(kw::If) {
-                    return Err("Expected `if`");
-                }
-                parser
-                    .parse_attribute(false)
-                    .map_err(|_| "Failed to parse attributes")?;
-            }
-
-            if !parser.eat(&TokenKind::OpenDelim(DelimToken::Brace)) {
-                return Err("Expected an opening brace");
-            }
-
-            while parser.token != TokenKind::CloseDelim(DelimToken::Brace)
-                && parser.token.kind != TokenKind::Eof
-            {
-                let item = match parser.parse_item() {
-                    Ok(Some(item_ptr)) => item_ptr.into_inner(),
-                    Ok(None) => continue,
-                    Err(mut err) => {
-                        err.cancel();
-                        parser.sess.span_diagnostic.reset_err_count();
-                        return Err(
-                            "Expected item inside cfg_if block, but failed to parse it as an item",
-                        );
-                    }
-                };
-                if let ast::ItemKind::Mod(..) = item.kind {
-                    self.mods.push(ModItem { item });
-                }
-            }
-
-            if !parser.eat(&TokenKind::CloseDelim(DelimToken::Brace)) {
-                return Err("Expected a closing brace");
-            }
-
-            if parser.eat(&TokenKind::Eof) {
-                break;
-            }
-
-            if !parser.eat_keyword(kw::Else) {
-                return Err("Expected `else`");
-            }
-
-            process_if_cfg = parser.token.is_keyword(kw::If);
-        }
+        let items = Parser::parse_cfg_if(self.parse_sess, mac, &self.base_dir)?;
+        self.mods
+            .append(&mut items.into_iter().map(|item| ModItem { item }).collect());
 
         Ok(())
     }
diff --git a/src/reorder.rs b/src/reorder.rs
index 4b607812c00..0235e552be7 100644
--- a/src/reorder.rs
+++ b/src/reorder.rs
@@ -207,13 +207,13 @@ impl<'b, 'a: 'b> FmtVisitor<'a> {
         item_kind: ReorderableItemKind,
         in_group: bool,
     ) -> usize {
-        let mut last = self.source_map.lookup_line_range(items[0].span());
+        let mut last = self.parse_sess.lookup_line_range(items[0].span());
         let item_length = items
             .iter()
             .take_while(|ppi| {
                 item_kind.is_same_item_kind(&***ppi)
                     && (!in_group || {
-                        let current = self.source_map.lookup_line_range(ppi.span());
+                        let current = self.parse_sess.lookup_line_range(ppi.span());
                         let in_same_group = current.lo < last.hi + 2;
                         last = current;
                         in_same_group
diff --git a/src/rewrite.rs b/src/rewrite.rs
index fbae4bc7122..f73602f09a2 100644
--- a/src/rewrite.rs
+++ b/src/rewrite.rs
@@ -3,13 +3,13 @@
 use std::cell::{Cell, RefCell};
 use std::rc::Rc;
 
-use rustc_session::parse::ParseSess;
-use rustc_span::{source_map::SourceMap, Span};
+use rustc_span::Span;
 use syntax::ptr;
 
 use crate::config::{Config, IndentStyle};
 use crate::shape::Shape;
 use crate::skip::SkipContext;
+use crate::syntux::session::ParseSess;
 use crate::visitor::SnippetProvider;
 use crate::FormatReport;
 
@@ -26,8 +26,7 @@ impl<T: Rewrite> Rewrite for ptr::P<T> {
 
 #[derive(Clone)]
 pub(crate) struct RewriteContext<'a> {
-    pub(crate) parse_session: &'a ParseSess,
-    pub(crate) source_map: &'a SourceMap,
+    pub(crate) parse_sess: &'a ParseSess,
     pub(crate) config: &'a Config,
     pub(crate) inside_macro: Rc<Cell<bool>>,
     // Force block indent style even if we are using visual indent style.
@@ -37,7 +36,7 @@ pub(crate) struct RewriteContext<'a> {
     pub(crate) is_if_else_block: Cell<bool>,
     // When rewriting chain, veto going multi line except the last element
     pub(crate) force_one_line_chain: Cell<bool>,
-    pub(crate) snippet_provider: &'a SnippetProvider<'a>,
+    pub(crate) snippet_provider: &'a SnippetProvider,
     // Used for `format_snippet`
     pub(crate) macro_rewrite_failure: Cell<bool>,
     pub(crate) report: FormatReport,
diff --git a/src/source_file.rs b/src/source_file.rs
index 4cb894e55b8..4f02d8224e2 100644
--- a/src/source_file.rs
+++ b/src/source_file.rs
@@ -2,10 +2,9 @@ use std::fs;
 use std::io::{self, Write};
 use std::path::Path;
 
-use rustc_span::source_map::SourceMap;
-
 use crate::config::FileName;
 use crate::emitter::{self, Emitter};
+use crate::syntux::session::ParseSess;
 use crate::NewlineStyle;
 
 #[cfg(test)]
@@ -14,6 +13,7 @@ use crate::config::Config;
 use crate::create_emitter;
 #[cfg(test)]
 use crate::formatting::FileRecord;
+use std::rc::Rc;
 
 // Append a newline to the end of each file.
 pub(crate) fn append_newline(s: &mut String) {
@@ -48,7 +48,7 @@ where
 }
 
 pub(crate) fn write_file<T>(
-    source_map: Option<&SourceMap>,
+    parse_sess: Option<&ParseSess>,
     filename: &FileName,
     formatted_text: &str,
     out: &mut T,
@@ -84,20 +84,17 @@ where
     // source map instead of hitting the file system. This also supports getting
     // original text for `FileName::Stdin`.
     let original_text = if newline_style != NewlineStyle::Auto && *filename != FileName::Stdin {
-        fs::read_to_string(ensure_real_path(filename))?
+        Rc::new(fs::read_to_string(ensure_real_path(filename))?)
     } else {
-        match source_map
-            .and_then(|x| x.get_source_file(&filename.into()))
-            .and_then(|x| x.src.as_ref().map(ToString::to_string))
-        {
+        match parse_sess.and_then(|sess| sess.get_original_snippet(filename)) {
             Some(ori) => ori,
-            None => fs::read_to_string(ensure_real_path(filename))?,
+            None => Rc::new(fs::read_to_string(ensure_real_path(filename))?),
         }
     };
 
     let formatted_file = emitter::FormattedFile {
         filename,
-        original_text: &original_text,
+        original_text: original_text.as_str(),
         formatted_text,
     };
 
diff --git a/src/source_map.rs b/src/source_map.rs
index c3a549d3b84..76e0d24cf1e 100644
--- a/src/source_map.rs
+++ b/src/source_map.rs
@@ -1,11 +1,10 @@
 //! This module contains utilities that work with the `SourceMap` from `libsyntax`/`syntex_syntax`.
 //! This includes extension traits and methods for looking up spans and line ranges for AST nodes.
 
-use rustc_span::{source_map::SourceMap, BytePos, Span};
+use rustc_span::{BytePos, Span};
 
 use crate::comment::FindUncommented;
 use crate::config::file_lines::LineRange;
-use crate::utils::starts_with_newline;
 use crate::visitor::SnippetProvider;
 
 pub(crate) trait SpanUtils {
@@ -26,7 +25,7 @@ pub(crate) trait LineRangeUtils {
     fn lookup_line_range(&self, span: Span) -> LineRange;
 }
 
-impl<'a> SpanUtils for SnippetProvider<'a> {
+impl SpanUtils for SnippetProvider {
     fn span_after(&self, original: Span, needle: &str) -> BytePos {
         self.opt_span_after(original, needle).unwrap_or_else(|| {
             panic!(
@@ -81,27 +80,3 @@ impl<'a> SpanUtils for SnippetProvider<'a> {
         Some(original.lo() + BytePos(offset as u32))
     }
 }
-
-impl LineRangeUtils for SourceMap {
-    fn lookup_line_range(&self, span: Span) -> LineRange {
-        let snippet = self.span_to_snippet(span).unwrap_or_default();
-        let lo = self.lookup_line(span.lo()).unwrap();
-        let hi = self.lookup_line(span.hi()).unwrap();
-
-        debug_assert_eq!(
-            lo.sf.name, hi.sf.name,
-            "span crossed file boundary: lo: {:?}, hi: {:?}",
-            lo, hi
-        );
-
-        // in case the span starts with a newline, the line range is off by 1 without the
-        // adjustment below
-        let offset = 1 + if starts_with_newline(&snippet) { 1 } else { 0 };
-        // Line numbers start at 1
-        LineRange {
-            file: lo.sf.clone(),
-            lo: lo.line + offset,
-            hi: hi.line + offset,
-        }
-    }
-}
diff --git a/src/syntux.rs b/src/syntux.rs
new file mode 100644
index 00000000000..845576bd8d8
--- /dev/null
+++ b/src/syntux.rs
@@ -0,0 +1,4 @@
+//! This module defines a thin abstract layer on top of the rustc's parser and syntax libraries.
+
+pub(crate) mod parser;
+pub(crate) mod session;
diff --git a/src/syntux/parser.rs b/src/syntux/parser.rs
new file mode 100644
index 00000000000..6e3cd5e2375
--- /dev/null
+++ b/src/syntux/parser.rs
@@ -0,0 +1,348 @@
+use std::panic::{catch_unwind, AssertUnwindSafe};
+use std::path::{Path, PathBuf};
+
+use rustc_errors::{Diagnostic, PResult};
+use rustc_parse::{new_sub_parser_from_file, parser::Parser as RawParser};
+use rustc_span::{symbol::kw, Span, DUMMY_SP};
+use syntax::ast;
+use syntax::token::{DelimToken, TokenKind};
+
+use crate::syntux::session::ParseSess;
+use crate::{Config, Input};
+
+pub(crate) type DirectoryOwnership = rustc_parse::DirectoryOwnership;
+pub(crate) type ModulePathSuccess = rustc_parse::parser::ModulePathSuccess;
+
+#[derive(Clone)]
+pub(crate) struct Directory {
+    pub(crate) path: PathBuf,
+    pub(crate) ownership: DirectoryOwnership,
+}
+
+impl<'a> Directory {
+    fn to_syntax_directory(&'a self) -> rustc_parse::Directory {
+        rustc_parse::Directory {
+            path: self.path.clone(),
+            ownership: self.ownership,
+        }
+    }
+}
+
+/// A parser for Rust source code.
+pub(crate) struct Parser<'a> {
+    parser: RawParser<'a>,
+    sess: &'a ParseSess,
+}
+
+/// A builder for the `Parser`.
+#[derive(Default)]
+pub(crate) struct ParserBuilder<'a> {
+    config: Option<&'a Config>,
+    sess: Option<&'a ParseSess>,
+    input: Option<Input>,
+    directory_ownership: Option<DirectoryOwnership>,
+}
+
+impl<'a> ParserBuilder<'a> {
+    pub(crate) fn input(mut self, input: Input) -> ParserBuilder<'a> {
+        self.input = Some(input);
+        self
+    }
+
+    pub(crate) fn sess(mut self, sess: &'a ParseSess) -> ParserBuilder<'a> {
+        self.sess = Some(sess);
+        self
+    }
+
+    pub(crate) fn config(mut self, config: &'a Config) -> ParserBuilder<'a> {
+        self.config = Some(config);
+        self
+    }
+
+    pub(crate) fn directory_ownership(
+        mut self,
+        directory_ownership: Option<DirectoryOwnership>,
+    ) -> ParserBuilder<'a> {
+        self.directory_ownership = directory_ownership;
+        self
+    }
+
+    pub(crate) fn build(self) -> Result<Parser<'a>, ParserError> {
+        let config = self.config.ok_or(ParserError::NoConfig)?;
+        let sess = self.sess.ok_or(ParserError::NoParseSess)?;
+        let input = self.input.ok_or(ParserError::NoInput)?;
+
+        let mut parser = match Self::parser(sess.inner(), input, self.directory_ownership) {
+            Ok(p) => p,
+            Err(db) => {
+                sess.emit_diagnostics(db);
+                return Err(ParserError::ParserCreationError);
+            }
+        };
+
+        parser.cfg_mods = false;
+        if config.skip_children() {
+            parser.recurse_into_file_modules = false;
+        }
+
+        Ok(Parser { parser, sess })
+    }
+
+    fn parser(
+        sess: &'a rustc_session::parse::ParseSess,
+        input: Input,
+        directory_ownership: Option<DirectoryOwnership>,
+    ) -> Result<rustc_parse::parser::Parser<'a>, Vec<Diagnostic>> {
+        match input {
+            Input::File(ref file) => Ok(if let Some(directory_ownership) = directory_ownership {
+                rustc_parse::new_sub_parser_from_file(
+                    sess,
+                    file,
+                    directory_ownership,
+                    None,
+                    DUMMY_SP,
+                )
+            } else {
+                rustc_parse::new_parser_from_file(sess, file)
+            }),
+            Input::Text(text) => rustc_parse::maybe_new_parser_from_source_str(
+                sess,
+                rustc_span::FileName::Custom("stdin".to_owned()),
+                text,
+            )
+            .map(|mut parser| {
+                parser.recurse_into_file_modules = false;
+                parser
+            }),
+        }
+    }
+}
+
+#[derive(Debug, PartialEq)]
+pub(crate) enum ParserError {
+    NoConfig,
+    NoParseSess,
+    NoInput,
+    ParserCreationError,
+    ParseError,
+    ParsePanicError,
+}
+
+impl<'a> Parser<'a> {
+    pub(crate) fn submod_path_from_attr(attrs: &[ast::Attribute], path: &Path) -> Option<PathBuf> {
+        rustc_parse::parser::Parser::submod_path_from_attr(attrs, path)
+    }
+
+    // FIXME(topecongiro) Use the method from libsyntax[1] once it become public.
+    //
+    // [1] https://github.com/rust-lang/rust/blob/master/src/libsyntax/parse/attr.rs
+    fn parse_inner_attrs(parser: &mut RawParser<'a>) -> PResult<'a, Vec<ast::Attribute>> {
+        let mut attrs: Vec<ast::Attribute> = vec![];
+        loop {
+            match parser.token.kind {
+                TokenKind::Pound => {
+                    // Don't even try to parse if it's not an inner attribute.
+                    if !parser.look_ahead(1, |t| t == &TokenKind::Not) {
+                        break;
+                    }
+
+                    let attr = parser.parse_attribute(true)?;
+                    assert_eq!(attr.style, ast::AttrStyle::Inner);
+                    attrs.push(attr);
+                }
+                TokenKind::DocComment(s) => {
+                    // we need to get the position of this token before we bump.
+                    let attr = syntax::attr::mk_doc_comment(
+                        syntax::util::comments::doc_comment_style(&s.as_str()),
+                        s,
+                        parser.token.span,
+                    );
+                    if attr.style == ast::AttrStyle::Inner {
+                        attrs.push(attr);
+                        parser.bump();
+                    } else {
+                        break;
+                    }
+                }
+                _ => break,
+            }
+        }
+        Ok(attrs)
+    }
+
+    fn parse_mod_items(parser: &mut RawParser<'a>, span: Span) -> PResult<'a, ast::Mod> {
+        let mut items = vec![];
+        while let Some(item) = parser.parse_item()? {
+            items.push(item);
+        }
+
+        let hi = if parser.token.span.is_dummy() {
+            span
+        } else {
+            parser.prev_token.span
+        };
+
+        Ok(ast::Mod {
+            inner: span.to(hi),
+            items,
+            inline: false,
+        })
+    }
+
+    pub(crate) fn parse_file_as_module(
+        directory_ownership: DirectoryOwnership,
+        sess: &'a ParseSess,
+        path: &Path,
+    ) -> Option<ast::Mod> {
+        let result = catch_unwind(AssertUnwindSafe(|| {
+            let mut parser =
+                new_sub_parser_from_file(sess.inner(), &path, directory_ownership, None, DUMMY_SP);
+
+            parser.cfg_mods = false;
+            let lo = parser.token.span;
+            // FIXME(topecongiro) Format inner attributes (#3606).
+            match Parser::parse_inner_attrs(&mut parser) {
+                Ok(_attrs) => (),
+                Err(mut e) => {
+                    e.cancel();
+                    sess.reset_errors();
+                    return None;
+                }
+            }
+
+            match Parser::parse_mod_items(&mut parser, lo) {
+                Ok(m) => Some(m),
+                Err(mut db) => {
+                    db.cancel();
+                    sess.reset_errors();
+                    None
+                }
+            }
+        }));
+        match result {
+            Ok(Some(m)) => Some(m),
+            _ => None,
+        }
+    }
+
+    pub(crate) fn parse_crate(
+        config: &'a Config,
+        input: Input,
+        directory_ownership: Option<DirectoryOwnership>,
+        sess: &'a ParseSess,
+    ) -> Result<ast::Crate, ParserError> {
+        let mut parser = ParserBuilder::default()
+            .config(config)
+            .input(input)
+            .directory_ownership(directory_ownership)
+            .sess(sess)
+            .build()?;
+
+        parser.parse_crate_inner()
+    }
+
+    fn parse_crate_inner(&mut self) -> Result<ast::Crate, ParserError> {
+        let mut parser = AssertUnwindSafe(&mut self.parser);
+
+        match catch_unwind(move || parser.parse_crate_mod()) {
+            Ok(Ok(krate)) => {
+                if !self.sess.has_errors() {
+                    return Ok(krate);
+                }
+
+                if self.sess.can_reset_errors() {
+                    self.sess.reset_errors();
+                    return Ok(krate);
+                }
+
+                Err(ParserError::ParseError)
+            }
+            Ok(Err(mut db)) => {
+                db.emit();
+                Err(ParserError::ParseError)
+            }
+            Err(_) => Err(ParserError::ParsePanicError),
+        }
+    }
+
+    pub(crate) fn parse_cfg_if(
+        sess: &'a ParseSess,
+        mac: &'a ast::Mac,
+        base_dir: &Directory,
+    ) -> Result<Vec<ast::Item>, &'static str> {
+        match catch_unwind(AssertUnwindSafe(|| {
+            Parser::parse_cfg_if_inner(sess, mac, base_dir)
+        })) {
+            Ok(Ok(items)) => Ok(items),
+            Ok(err @ Err(_)) => err,
+            Err(..) => Err("failed to parse cfg_if!"),
+        }
+    }
+
+    fn parse_cfg_if_inner(
+        sess: &'a ParseSess,
+        mac: &'a ast::Mac,
+        base_dir: &Directory,
+    ) -> Result<Vec<ast::Item>, &'static str> {
+        let token_stream = mac.args.inner_tokens();
+        let mut parser = rustc_parse::stream_to_parser_with_base_dir(
+            sess.inner(),
+            token_stream.clone(),
+            base_dir.to_syntax_directory(),
+        );
+
+        parser.cfg_mods = false;
+        let mut items = vec![];
+        let mut process_if_cfg = true;
+
+        while parser.token.kind != TokenKind::Eof {
+            if process_if_cfg {
+                if !parser.eat_keyword(kw::If) {
+                    return Err("Expected `if`");
+                }
+                parser
+                    .parse_attribute(false)
+                    .map_err(|_| "Failed to parse attributes")?;
+            }
+
+            if !parser.eat(&TokenKind::OpenDelim(DelimToken::Brace)) {
+                return Err("Expected an opening brace");
+            }
+
+            while parser.token != TokenKind::CloseDelim(DelimToken::Brace)
+                && parser.token.kind != TokenKind::Eof
+            {
+                let item = match parser.parse_item() {
+                    Ok(Some(item_ptr)) => item_ptr.into_inner(),
+                    Ok(None) => continue,
+                    Err(mut err) => {
+                        err.cancel();
+                        parser.sess.span_diagnostic.reset_err_count();
+                        return Err(
+                            "Expected item inside cfg_if block, but failed to parse it as an item",
+                        );
+                    }
+                };
+                if let ast::ItemKind::Mod(..) = item.kind {
+                    items.push(item);
+                }
+            }
+
+            if !parser.eat(&TokenKind::CloseDelim(DelimToken::Brace)) {
+                return Err("Expected a closing brace");
+            }
+
+            if parser.eat(&TokenKind::Eof) {
+                break;
+            }
+
+            if !parser.eat_keyword(kw::Else) {
+                return Err("Expected `else`");
+            }
+
+            process_if_cfg = parser.token.is_keyword(kw::If);
+        }
+
+        Ok(items)
+    }
+}
diff --git a/src/syntux/session.rs b/src/syntux/session.rs
new file mode 100644
index 00000000000..9ec85efb109
--- /dev/null
+++ b/src/syntux/session.rs
@@ -0,0 +1,440 @@
+use std::cell::RefCell;
+use std::path::Path;
+use std::rc::Rc;
+
+use rustc_data_structures::sync::{Lrc, Send};
+use rustc_errors::emitter::{Emitter, EmitterWriter};
+use rustc_errors::{ColorConfig, Diagnostic, Handler, Level as DiagnosticLevel};
+use rustc_session::parse::ParseSess as RawParseSess;
+use rustc_span::{
+    source_map::{FilePathMapping, SourceMap},
+    BytePos, Span,
+};
+use syntax::ast;
+
+use crate::config::file_lines::LineRange;
+use crate::ignore_path::IgnorePathSet;
+use crate::source_map::LineRangeUtils;
+use crate::utils::starts_with_newline;
+use crate::visitor::SnippetProvider;
+use crate::{Config, ErrorKind, FileName};
+
+/// ParseSess holds structs necessary for constructing a parser.
+pub(crate) struct ParseSess {
+    parse_sess: RawParseSess,
+    ignore_path_set: Rc<IgnorePathSet>,
+    can_reset_errors: Rc<RefCell<bool>>,
+}
+
+/// Emitter which discards every error.
+struct SilentEmitter;
+
+impl Emitter for SilentEmitter {
+    fn source_map(&self) -> Option<&Lrc<SourceMap>> {
+        None
+    }
+    fn emit_diagnostic(&mut self, _db: &Diagnostic) {}
+}
+
+fn silent_emitter() -> Box<dyn Emitter + Send> {
+    Box::new(SilentEmitter {})
+}
+
+/// Emit errors against every files expect ones specified in the `ignore_path_set`.
+struct SilentOnIgnoredFilesEmitter {
+    ignore_path_set: Rc<IgnorePathSet>,
+    source_map: Rc<SourceMap>,
+    emitter: Box<dyn Emitter + Send>,
+    has_non_ignorable_parser_errors: bool,
+    can_reset: Rc<RefCell<bool>>,
+}
+
+impl SilentOnIgnoredFilesEmitter {
+    fn handle_non_ignoreable_error(&mut self, db: &Diagnostic) {
+        self.has_non_ignorable_parser_errors = true;
+        *self.can_reset.borrow_mut() = false;
+        self.emitter.emit_diagnostic(db);
+    }
+}
+
+impl Emitter for SilentOnIgnoredFilesEmitter {
+    fn source_map(&self) -> Option<&Lrc<SourceMap>> {
+        None
+    }
+    fn emit_diagnostic(&mut self, db: &Diagnostic) {
+        if db.level == DiagnosticLevel::Fatal {
+            return self.handle_non_ignoreable_error(db);
+        }
+        if let Some(primary_span) = &db.span.primary_span() {
+            let file_name = self.source_map.span_to_filename(*primary_span);
+            if let rustc_span::FileName::Real(ref path) = file_name {
+                if self
+                    .ignore_path_set
+                    .is_match(&FileName::Real(path.to_path_buf()))
+                {
+                    if !self.has_non_ignorable_parser_errors {
+                        *self.can_reset.borrow_mut() = true;
+                    }
+                    return;
+                }
+            };
+        }
+        self.handle_non_ignoreable_error(db);
+    }
+}
+
+fn default_handler(
+    source_map: Rc<SourceMap>,
+    ignore_path_set: Rc<IgnorePathSet>,
+    can_reset: Rc<RefCell<bool>>,
+    hide_parse_errors: bool,
+) -> Handler {
+    let supports_color = term::stderr().map_or(false, |term| term.supports_color());
+    let color_cfg = if supports_color {
+        ColorConfig::Auto
+    } else {
+        ColorConfig::Never
+    };
+
+    let emitter = if hide_parse_errors {
+        silent_emitter()
+    } else {
+        Box::new(EmitterWriter::stderr(
+            color_cfg,
+            Some(source_map.clone()),
+            false,
+            false,
+            None,
+            false,
+        ))
+    };
+    Handler::with_emitter(
+        true,
+        None,
+        Box::new(SilentOnIgnoredFilesEmitter {
+            has_non_ignorable_parser_errors: false,
+            source_map,
+            emitter,
+            ignore_path_set,
+            can_reset,
+        }),
+    )
+}
+
+impl ParseSess {
+    pub(crate) fn new(config: &Config) -> Result<ParseSess, ErrorKind> {
+        let ignore_path_set = match IgnorePathSet::from_ignore_list(&config.ignore()) {
+            Ok(ignore_path_set) => Rc::new(ignore_path_set),
+            Err(e) => return Err(ErrorKind::InvalidGlobPattern(e)),
+        };
+        let source_map = Rc::new(SourceMap::new(FilePathMapping::empty()));
+        let can_reset_errors = Rc::new(RefCell::new(false));
+
+        let handler = default_handler(
+            Rc::clone(&source_map),
+            Rc::clone(&ignore_path_set),
+            Rc::clone(&can_reset_errors),
+            config.hide_parse_errors(),
+        );
+        let parse_sess = RawParseSess::with_span_handler(handler, source_map);
+
+        Ok(ParseSess {
+            parse_sess,
+            ignore_path_set,
+            can_reset_errors,
+        })
+    }
+
+    pub(crate) fn default_submod_path(
+        &self,
+        id: ast::Ident,
+        relative: Option<ast::Ident>,
+        dir_path: &Path,
+    ) -> rustc_parse::parser::ModulePath {
+        rustc_parse::parser::Parser::default_submod_path(
+            id,
+            relative,
+            dir_path,
+            self.parse_sess.source_map(),
+        )
+    }
+
+    pub(crate) fn is_file_parsed(&self, path: &Path) -> bool {
+        self.parse_sess
+            .source_map()
+            .get_source_file(&rustc_span::FileName::Real(path.to_path_buf()))
+            .is_some()
+    }
+
+    pub(crate) fn ignore_file(&self, path: &FileName) -> bool {
+        self.ignore_path_set.as_ref().is_match(&path)
+    }
+
+    pub(crate) fn set_silent_emitter(&mut self) {
+        self.parse_sess.span_diagnostic = Handler::with_emitter(true, None, silent_emitter());
+    }
+
+    pub(crate) fn span_to_filename(&self, span: Span) -> FileName {
+        self.parse_sess.source_map().span_to_filename(span).into()
+    }
+
+    pub(crate) fn span_to_first_line_string(&self, span: Span) -> String {
+        let file_lines = self.parse_sess.source_map().span_to_lines(span).ok();
+
+        match file_lines {
+            Some(fl) => fl
+                .file
+                .get_line(fl.lines[0].line_index)
+                .map_or_else(String::new, |s| s.to_string()),
+            None => String::new(),
+        }
+    }
+
+    pub(crate) fn line_of_byte_pos(&self, pos: BytePos) -> usize {
+        self.parse_sess.source_map().lookup_char_pos(pos).line
+    }
+
+    pub(crate) fn span_to_debug_info(&self, span: Span) -> String {
+        self.parse_sess.source_map().span_to_string(span)
+    }
+
+    pub(crate) fn inner(&self) -> &RawParseSess {
+        &self.parse_sess
+    }
+
+    pub(crate) fn snippet_provider(&self, span: Span) -> SnippetProvider {
+        let source_file = self.parse_sess.source_map().lookup_char_pos(span.lo()).file;
+        SnippetProvider::new(
+            source_file.start_pos,
+            source_file.end_pos,
+            Rc::clone(source_file.src.as_ref().unwrap()),
+        )
+    }
+
+    pub(crate) fn get_original_snippet(&self, file_name: &FileName) -> Option<Rc<String>> {
+        self.parse_sess
+            .source_map()
+            .get_source_file(&file_name.into())
+            .and_then(|source_file| source_file.src.clone())
+    }
+}
+
+// Methods that should be restricted within the syntux module.
+impl ParseSess {
+    pub(super) fn emit_diagnostics(&self, diagnostics: Vec<Diagnostic>) {
+        for diagnostic in diagnostics {
+            self.parse_sess.span_diagnostic.emit_diagnostic(&diagnostic);
+        }
+    }
+
+    pub(super) fn can_reset_errors(&self) -> bool {
+        *self.can_reset_errors.borrow()
+    }
+
+    pub(super) fn has_errors(&self) -> bool {
+        self.parse_sess.span_diagnostic.has_errors()
+    }
+
+    pub(super) fn reset_errors(&self) {
+        self.parse_sess.span_diagnostic.reset_err_count();
+    }
+}
+
+impl LineRangeUtils for ParseSess {
+    fn lookup_line_range(&self, span: Span) -> LineRange {
+        let snippet = self
+            .parse_sess
+            .source_map()
+            .span_to_snippet(span)
+            .unwrap_or_default();
+        let lo = self.parse_sess.source_map().lookup_line(span.lo()).unwrap();
+        let hi = self.parse_sess.source_map().lookup_line(span.hi()).unwrap();
+
+        debug_assert_eq!(
+            lo.sf.name, hi.sf.name,
+            "span crossed file boundary: lo: {:?}, hi: {:?}",
+            lo, hi
+        );
+
+        // in case the span starts with a newline, the line range is off by 1 without the
+        // adjustment below
+        let offset = 1 + if starts_with_newline(&snippet) { 1 } else { 0 };
+        // Line numbers start at 1
+        LineRange {
+            file: lo.sf.clone(),
+            lo: lo.line + offset,
+            hi: hi.line + offset,
+        }
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+
+    mod emitter {
+        use super::*;
+        use crate::config::IgnoreList;
+        use crate::is_nightly_channel;
+        use crate::utils::mk_sp;
+        use rustc_span::{FileName as SourceMapFileName, MultiSpan, DUMMY_SP};
+        use std::path::PathBuf;
+
+        struct TestEmitter {
+            num_emitted_errors: Rc<RefCell<u32>>,
+        }
+
+        impl Emitter for TestEmitter {
+            fn source_map(&self) -> Option<&Lrc<SourceMap>> {
+                None
+            }
+            fn emit_diagnostic(&mut self, _db: &Diagnostic) {
+                *self.num_emitted_errors.borrow_mut() += 1;
+            }
+        }
+
+        fn build_diagnostic(level: DiagnosticLevel, span: Option<MultiSpan>) -> Diagnostic {
+            Diagnostic {
+                level,
+                code: None,
+                message: vec![],
+                children: vec![],
+                suggestions: vec![],
+                span: span.unwrap_or_else(MultiSpan::new),
+                sort_span: DUMMY_SP,
+            }
+        }
+
+        fn build_emitter(
+            num_emitted_errors: Rc<RefCell<u32>>,
+            can_reset: Rc<RefCell<bool>>,
+            source_map: Option<Rc<SourceMap>>,
+            ignore_list: Option<IgnoreList>,
+        ) -> SilentOnIgnoredFilesEmitter {
+            let emitter_writer = TestEmitter { num_emitted_errors };
+            let source_map =
+                source_map.unwrap_or_else(|| Rc::new(SourceMap::new(FilePathMapping::empty())));
+            let ignore_path_set =
+                Rc::new(IgnorePathSet::from_ignore_list(&ignore_list.unwrap_or_default()).unwrap());
+            SilentOnIgnoredFilesEmitter {
+                has_non_ignorable_parser_errors: false,
+                source_map,
+                emitter: Box::new(emitter_writer),
+                ignore_path_set,
+                can_reset,
+            }
+        }
+
+        fn get_ignore_list(config: &str) -> IgnoreList {
+            Config::from_toml(config, Path::new("")).unwrap().ignore()
+        }
+
+        #[test]
+        fn handles_fatal_parse_error_in_ignored_file() {
+            let num_emitted_errors = Rc::new(RefCell::new(0));
+            let can_reset_errors = Rc::new(RefCell::new(false));
+            let ignore_list = get_ignore_list(r#"ignore = ["foo.rs"]"#);
+            let source_map = Rc::new(SourceMap::new(FilePathMapping::empty()));
+            let source =
+                String::from(r#"extern "system" fn jni_symbol!( funcName ) ( ... ) -> {} "#);
+            source_map.new_source_file(SourceMapFileName::Real(PathBuf::from("foo.rs")), source);
+            let mut emitter = build_emitter(
+                Rc::clone(&num_emitted_errors),
+                Rc::clone(&can_reset_errors),
+                Some(Rc::clone(&source_map)),
+                Some(ignore_list),
+            );
+            let span = MultiSpan::from_span(mk_sp(BytePos(0), BytePos(1)));
+            let fatal_diagnostic = build_diagnostic(DiagnosticLevel::Fatal, Some(span));
+            emitter.emit_diagnostic(&fatal_diagnostic);
+            assert_eq!(*num_emitted_errors.borrow(), 1);
+            assert_eq!(*can_reset_errors.borrow(), false);
+        }
+
+        #[test]
+        fn handles_recoverable_parse_error_in_ignored_file() {
+            if !is_nightly_channel!() {
+                return;
+            }
+            let num_emitted_errors = Rc::new(RefCell::new(0));
+            let can_reset_errors = Rc::new(RefCell::new(false));
+            let ignore_list = get_ignore_list(r#"ignore = ["foo.rs"]"#);
+            let source_map = Rc::new(SourceMap::new(FilePathMapping::empty()));
+            let source = String::from(r#"pub fn bar() { 1x; }"#);
+            source_map.new_source_file(SourceMapFileName::Real(PathBuf::from("foo.rs")), source);
+            let mut emitter = build_emitter(
+                Rc::clone(&num_emitted_errors),
+                Rc::clone(&can_reset_errors),
+                Some(Rc::clone(&source_map)),
+                Some(ignore_list),
+            );
+            let span = MultiSpan::from_span(mk_sp(BytePos(0), BytePos(1)));
+            let non_fatal_diagnostic = build_diagnostic(DiagnosticLevel::Warning, Some(span));
+            emitter.emit_diagnostic(&non_fatal_diagnostic);
+            assert_eq!(*num_emitted_errors.borrow(), 0);
+            assert_eq!(*can_reset_errors.borrow(), true);
+        }
+
+        #[test]
+        fn handles_recoverable_parse_error_in_non_ignored_file() {
+            if !is_nightly_channel!() {
+                return;
+            }
+            let num_emitted_errors = Rc::new(RefCell::new(0));
+            let can_reset_errors = Rc::new(RefCell::new(false));
+            let source_map = Rc::new(SourceMap::new(FilePathMapping::empty()));
+            let source = String::from(r#"pub fn bar() { 1x; }"#);
+            source_map.new_source_file(SourceMapFileName::Real(PathBuf::from("foo.rs")), source);
+            let mut emitter = build_emitter(
+                Rc::clone(&num_emitted_errors),
+                Rc::clone(&can_reset_errors),
+                Some(Rc::clone(&source_map)),
+                None,
+            );
+            let span = MultiSpan::from_span(mk_sp(BytePos(0), BytePos(1)));
+            let non_fatal_diagnostic = build_diagnostic(DiagnosticLevel::Warning, Some(span));
+            emitter.emit_diagnostic(&non_fatal_diagnostic);
+            assert_eq!(*num_emitted_errors.borrow(), 1);
+            assert_eq!(*can_reset_errors.borrow(), false);
+        }
+
+        #[test]
+        fn handles_mix_of_recoverable_parse_error() {
+            if !is_nightly_channel!() {
+                return;
+            }
+            let num_emitted_errors = Rc::new(RefCell::new(0));
+            let can_reset_errors = Rc::new(RefCell::new(false));
+            let source_map = Rc::new(SourceMap::new(FilePathMapping::empty()));
+            let ignore_list = get_ignore_list(r#"ignore = ["foo.rs"]"#);
+            let bar_source = String::from(r#"pub fn bar() { 1x; }"#);
+            let foo_source = String::from(r#"pub fn foo() { 1x; }"#);
+            let fatal_source =
+                String::from(r#"extern "system" fn jni_symbol!( funcName ) ( ... ) -> {} "#);
+            source_map
+                .new_source_file(SourceMapFileName::Real(PathBuf::from("bar.rs")), bar_source);
+            source_map
+                .new_source_file(SourceMapFileName::Real(PathBuf::from("foo.rs")), foo_source);
+            source_map.new_source_file(
+                SourceMapFileName::Real(PathBuf::from("fatal.rs")),
+                fatal_source,
+            );
+            let mut emitter = build_emitter(
+                Rc::clone(&num_emitted_errors),
+                Rc::clone(&can_reset_errors),
+                Some(Rc::clone(&source_map)),
+                Some(ignore_list),
+            );
+            let bar_span = MultiSpan::from_span(mk_sp(BytePos(0), BytePos(1)));
+            let foo_span = MultiSpan::from_span(mk_sp(BytePos(21), BytePos(22)));
+            let bar_diagnostic = build_diagnostic(DiagnosticLevel::Warning, Some(bar_span));
+            let foo_diagnostic = build_diagnostic(DiagnosticLevel::Warning, Some(foo_span));
+            let fatal_diagnostic = build_diagnostic(DiagnosticLevel::Fatal, None);
+            emitter.emit_diagnostic(&bar_diagnostic);
+            emitter.emit_diagnostic(&foo_diagnostic);
+            emitter.emit_diagnostic(&fatal_diagnostic);
+            assert_eq!(*num_emitted_errors.borrow(), 2);
+            assert_eq!(*can_reset_errors.borrow(), false);
+        }
+    }
+}
diff --git a/src/utils.rs b/src/utils.rs
index de18b8e6d5f..7e287a4b80a 100644
--- a/src/utils.rs
+++ b/src/utils.rs
@@ -355,7 +355,7 @@ macro_rules! out_of_file_lines_range {
             && !$self
                 .config
                 .file_lines()
-                .intersects(&$self.source_map.lookup_line_range($span))
+                .intersects(&$self.parse_sess.lookup_line_range($span))
     };
 }
 
diff --git a/src/visitor.rs b/src/visitor.rs
index 2073c8a1ef3..fe489f32a16 100644
--- a/src/visitor.rs
+++ b/src/visitor.rs
@@ -1,11 +1,7 @@
 use std::cell::{Cell, RefCell};
 use std::rc::Rc;
 
-use rustc_session::parse::ParseSess;
-use rustc_span::{
-    source_map::{self, SourceMap},
-    BytePos, Pos, Span,
-};
+use rustc_span::{BytePos, Pos, Span};
 use syntax::token::DelimToken;
 use syntax::{ast, visit};
 
@@ -27,6 +23,7 @@ use crate::skip::{is_skip_attr, SkipContext};
 use crate::source_map::{LineRangeUtils, SpanUtils};
 use crate::spanned::Spanned;
 use crate::stmt::Stmt;
+use crate::syntux::session::ParseSess;
 use crate::utils::{
     self, contains_skip, count_newlines, depr_skip_annotation, inner_attributes, last_line_width,
     mk_sp, ptr_vec_to_ref_vec, rewrite_ident, stmt_expr,
@@ -34,23 +31,23 @@ use crate::utils::{
 use crate::{ErrorKind, FormatReport, FormattingError};
 
 /// Creates a string slice corresponding to the specified span.
-pub(crate) struct SnippetProvider<'a> {
+pub(crate) struct SnippetProvider {
     /// A pointer to the content of the file we are formatting.
-    big_snippet: &'a str,
+    big_snippet: Rc<String>,
     /// A position of the start of `big_snippet`, used as an offset.
     start_pos: usize,
     /// A end position of the file that this snippet lives.
     end_pos: usize,
 }
 
-impl<'a> SnippetProvider<'a> {
+impl SnippetProvider {
     pub(crate) fn span_to_snippet(&self, span: Span) -> Option<&str> {
         let start_index = span.lo().to_usize().checked_sub(self.start_pos)?;
         let end_index = span.hi().to_usize().checked_sub(self.start_pos)?;
         Some(&self.big_snippet[start_index..end_index])
     }
 
-    pub(crate) fn new(start_pos: BytePos, end_pos: BytePos, big_snippet: &'a str) -> Self {
+    pub(crate) fn new(start_pos: BytePos, end_pos: BytePos, big_snippet: Rc<String>) -> Self {
         let start_pos = start_pos.to_usize();
         let end_pos = end_pos.to_usize();
         SnippetProvider {
@@ -60,6 +57,14 @@ impl<'a> SnippetProvider<'a> {
         }
     }
 
+    pub(crate) fn entire_snippet(&self) -> &str {
+        self.big_snippet.as_str()
+    }
+
+    pub(crate) fn start_pos(&self) -> BytePos {
+        BytePos::from_usize(self.start_pos)
+    }
+
     pub(crate) fn end_pos(&self) -> BytePos {
         BytePos::from_usize(self.end_pos)
     }
@@ -67,15 +72,14 @@ impl<'a> SnippetProvider<'a> {
 
 pub(crate) struct FmtVisitor<'a> {
     parent_context: Option<&'a RewriteContext<'a>>,
-    pub(crate) parse_session: &'a ParseSess,
-    pub(crate) source_map: &'a SourceMap,
+    pub(crate) parse_sess: &'a ParseSess,
     pub(crate) buffer: String,
     pub(crate) last_pos: BytePos,
     // FIXME: use an RAII util or closure for indenting
     pub(crate) block_indent: Indent,
     pub(crate) config: &'a Config,
     pub(crate) is_if_else_block: bool,
-    pub(crate) snippet_provider: &'a SnippetProvider<'a>,
+    pub(crate) snippet_provider: &'a SnippetProvider,
     pub(crate) line_number: usize,
     /// List of 1-based line ranges which were annotated with skip
     /// Both bounds are inclusifs.
@@ -110,10 +114,8 @@ impl<'b, 'a: 'b> FmtVisitor<'a> {
 
     fn visit_stmt(&mut self, stmt: &Stmt<'_>) {
         debug!(
-            "visit_stmt: {:?} {:?} `{}`",
-            self.source_map.lookup_char_pos(stmt.span().lo()),
-            self.source_map.lookup_char_pos(stmt.span().hi()),
-            self.snippet(stmt.span()),
+            "visit_stmt: {}",
+            self.parse_sess.span_to_debug_info(stmt.span())
         );
 
         // https://github.com/rust-lang/rust/issues/63679.
@@ -201,9 +203,8 @@ impl<'b, 'a: 'b> FmtVisitor<'a> {
         has_braces: bool,
     ) {
         debug!(
-            "visit_block: {:?} {:?}",
-            self.source_map.lookup_char_pos(b.span.lo()),
-            self.source_map.lookup_char_pos(b.span.hi())
+            "visit_block: {}",
+            self.parse_sess.span_to_debug_info(b.span),
         );
 
         // Check if this block has braces.
@@ -744,10 +745,10 @@ impl<'b, 'a: 'b> FmtVisitor<'a> {
         // do not take into account the lines with attributes as part of the skipped range
         let attrs_end = attrs
             .iter()
-            .map(|attr| self.source_map.lookup_char_pos(attr.span.hi()).line)
+            .map(|attr| self.parse_sess.line_of_byte_pos(attr.span.hi()))
             .max()
             .unwrap_or(1);
-        let first_line = self.source_map.lookup_char_pos(main_span.lo()).line;
+        let first_line = self.parse_sess.line_of_byte_pos(main_span.lo());
         // Statement can start after some newlines and/or spaces
         // or it can be on the same line as the last attribute.
         // So here we need to take a minimum between the two.
@@ -758,8 +759,8 @@ impl<'b, 'a: 'b> FmtVisitor<'a> {
     }
 
     pub(crate) fn from_context(ctx: &'a RewriteContext<'_>) -> FmtVisitor<'a> {
-        let mut visitor = FmtVisitor::from_source_map(
-            ctx.parse_session,
+        let mut visitor = FmtVisitor::from_parse_sess(
+            ctx.parse_sess,
             ctx.config,
             ctx.snippet_provider,
             ctx.report.clone(),
@@ -769,16 +770,15 @@ impl<'b, 'a: 'b> FmtVisitor<'a> {
         visitor
     }
 
-    pub(crate) fn from_source_map(
+    pub(crate) fn from_parse_sess(
         parse_session: &'a ParseSess,
         config: &'a Config,
-        snippet_provider: &'a SnippetProvider<'_>,
+        snippet_provider: &'a SnippetProvider,
         report: FormatReport,
     ) -> FmtVisitor<'a> {
         FmtVisitor {
             parent_context: None,
-            parse_session,
-            source_map: parse_session.source_map(),
+            parse_sess: parse_session,
             buffer: String::with_capacity(snippet_provider.big_snippet.len() * 2),
             last_pos: BytePos(0),
             block_indent: Indent::empty(),
@@ -805,12 +805,12 @@ impl<'b, 'a: 'b> FmtVisitor<'a> {
     pub(crate) fn visit_attrs(&mut self, attrs: &[ast::Attribute], style: ast::AttrStyle) -> bool {
         for attr in attrs {
             if attr.check_name(depr_skip_annotation()) {
-                let file_name = self.source_map.span_to_filename(attr.span).into();
+                let file_name = self.parse_sess.span_to_filename(attr.span);
                 self.report.append(
                     file_name,
                     vec![FormattingError::from_span(
                         attr.span,
-                        &self.source_map,
+                        self.parse_sess,
                         ErrorKind::DeprecatedAttr,
                     )],
                 );
@@ -819,12 +819,12 @@ impl<'b, 'a: 'b> FmtVisitor<'a> {
                     ast::AttrKind::Normal(ref attribute_item)
                         if self.is_unknown_rustfmt_attr(&attribute_item.path.segments) =>
                     {
-                        let file_name = self.source_map.span_to_filename(attr.span).into();
+                        let file_name = self.parse_sess.span_to_filename(attr.span);
                         self.report.append(
                             file_name,
                             vec![FormattingError::from_span(
                                 attr.span,
-                                self.source_map,
+                                self.parse_sess,
                                 ErrorKind::BadAttr,
                             )],
                         );
@@ -932,14 +932,10 @@ impl<'b, 'a: 'b> FmtVisitor<'a> {
         }
     }
 
-    pub(crate) fn format_separate_mod(
-        &mut self,
-        m: &ast::Mod,
-        source_file: &source_map::SourceFile,
-    ) {
+    pub(crate) fn format_separate_mod(&mut self, m: &ast::Mod, end_pos: BytePos) {
         self.block_indent = Indent::empty();
         self.walk_mod_items(m);
-        self.format_missing_with_indent(source_file.end_pos);
+        self.format_missing_with_indent(end_pos);
     }
 
     pub(crate) fn skip_empty_lines(&mut self, end_pos: BytePos) {
@@ -970,8 +966,7 @@ impl<'b, 'a: 'b> FmtVisitor<'a> {
 
     pub(crate) fn get_context(&self) -> RewriteContext<'_> {
         RewriteContext {
-            parse_session: self.parse_session,
-            source_map: self.source_map,
+            parse_sess: self.parse_sess,
             config: self.config,
             inside_macro: Rc::new(Cell::new(false)),
             use_block: Cell::new(false),