about summary refs log tree commit diff
path: root/src/parse
diff options
context:
space:
mode:
authorCaleb Cartwright <caleb.cartwright@outlook.com>2021-12-29 20:49:39 -0600
committerCaleb Cartwright <caleb.cartwright@outlook.com>2021-12-29 20:49:39 -0600
commit6db6bafc61b6f4f782c5f10e8dbe3f006c41fc5d (patch)
tree590f289ac45203fc8330dfb513aa0d148aeada9f /src/parse
parent122e1c3802afcaa803287b06127c499df63e2dab (diff)
Merge commit '4a053f206fd6799a25823c307f7d7f9d897be118' into sync-rustfmt-subtree
Diffstat (limited to 'src/parse')
-rw-r--r--src/parse/macros/asm.rs11
-rw-r--r--src/parse/macros/cfg_if.rs89
-rw-r--r--src/parse/macros/lazy_static.rs50
-rw-r--r--src/parse/macros/mod.rs231
-rw-r--r--src/parse/mod.rs3
-rw-r--r--src/parse/parser.rs175
-rw-r--r--src/parse/session.rs465
7 files changed, 1024 insertions, 0 deletions
diff --git a/src/parse/macros/asm.rs b/src/parse/macros/asm.rs
new file mode 100644
index 00000000000..cc9fb5072ce
--- /dev/null
+++ b/src/parse/macros/asm.rs
@@ -0,0 +1,11 @@
+use rustc_ast::ast;
+use rustc_builtin_macros::asm::{parse_asm_args, AsmArgs};
+
+use crate::rewrite::RewriteContext;
+
+#[allow(dead_code)]
+pub(crate) fn parse_asm(context: &RewriteContext<'_>, mac: &ast::MacCall) -> Option<AsmArgs> {
+    let ts = mac.args.inner_tokens();
+    let mut parser = super::build_parser(context, ts);
+    parse_asm_args(&mut parser, context.parse_sess.inner(), mac.span(), false).ok()
+}
diff --git a/src/parse/macros/cfg_if.rs b/src/parse/macros/cfg_if.rs
new file mode 100644
index 00000000000..e10fbe64bcd
--- /dev/null
+++ b/src/parse/macros/cfg_if.rs
@@ -0,0 +1,89 @@
+use std::panic::{catch_unwind, AssertUnwindSafe};
+
+use rustc_ast::ast;
+use rustc_ast::token::{DelimToken, TokenKind};
+use rustc_parse::parser::ForceCollect;
+use rustc_span::symbol::kw;
+
+use crate::parse::macros::build_stream_parser;
+use crate::parse::session::ParseSess;
+
+pub(crate) fn parse_cfg_if<'a>(
+    sess: &'a ParseSess,
+    mac: &'a ast::MacCall,
+) -> Result<Vec<ast::Item>, &'static str> {
+    match catch_unwind(AssertUnwindSafe(|| parse_cfg_if_inner(sess, mac))) {
+        Ok(Ok(items)) => Ok(items),
+        Ok(err @ Err(_)) => err,
+        Err(..) => Err("failed to parse cfg_if!"),
+    }
+}
+
+fn parse_cfg_if_inner<'a>(
+    sess: &'a ParseSess,
+    mac: &'a ast::MacCall,
+) -> Result<Vec<ast::Item>, &'static str> {
+    let ts = mac.args.inner_tokens();
+    let mut parser = build_stream_parser(sess.inner(), ts);
+
+    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`");
+            }
+            // Inner attributes are not actually syntactically permitted here, but we don't
+            // care about inner vs outer attributes in this position. Our purpose with this
+            // special case parsing of cfg_if macros is to ensure we can correctly resolve
+            // imported modules that may have a custom `path` defined.
+            //
+            // As such, we just need to advance the parser past the attribute and up to
+            // to the opening brace.
+            // See also https://github.com/rust-lang/rust/pull/79433
+            parser
+                .parse_attribute(rustc_parse::parser::attr::InnerAttrPolicy::Permitted)
+                .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(ForceCollect::No) {
+                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/parse/macros/lazy_static.rs b/src/parse/macros/lazy_static.rs
new file mode 100644
index 00000000000..9c8651aa3fa
--- /dev/null
+++ b/src/parse/macros/lazy_static.rs
@@ -0,0 +1,50 @@
+use rustc_ast::ast;
+use rustc_ast::ptr::P;
+use rustc_ast::token::TokenKind;
+use rustc_ast::tokenstream::TokenStream;
+use rustc_span::symbol::{self, kw};
+
+use crate::rewrite::RewriteContext;
+
+pub(crate) fn parse_lazy_static(
+    context: &RewriteContext<'_>,
+    ts: TokenStream,
+) -> Option<Vec<(ast::Visibility, symbol::Ident, P<ast::Ty>, P<ast::Expr>)>> {
+    let mut result = vec![];
+    let mut parser = super::build_parser(context, ts);
+    macro_rules! parse_or {
+        ($method:ident $(,)* $($arg:expr),* $(,)*) => {
+            match parser.$method($($arg,)*) {
+                Ok(val) => {
+                    if parser.sess.span_diagnostic.has_errors() {
+                        parser.sess.span_diagnostic.reset_err_count();
+                        return None;
+                    } else {
+                        val
+                    }
+                }
+                Err(mut err) => {
+                    err.cancel();
+                    parser.sess.span_diagnostic.reset_err_count();
+                    return None;
+                }
+            }
+        }
+    }
+
+    while parser.token.kind != TokenKind::Eof {
+        // Parse a `lazy_static!` item.
+        let vis = parse_or!(parse_visibility, rustc_parse::parser::FollowedByType::No);
+        parser.eat_keyword(kw::Static);
+        parser.eat_keyword(kw::Ref);
+        let id = parse_or!(parse_ident);
+        parser.eat(&TokenKind::Colon);
+        let ty = parse_or!(parse_ty);
+        parser.eat(&TokenKind::Eq);
+        let expr = parse_or!(parse_expr);
+        parser.eat(&TokenKind::Semi);
+        result.push((vis, id, ty, expr));
+    }
+
+    Some(result)
+}
diff --git a/src/parse/macros/mod.rs b/src/parse/macros/mod.rs
new file mode 100644
index 00000000000..2e9ce1d35f4
--- /dev/null
+++ b/src/parse/macros/mod.rs
@@ -0,0 +1,231 @@
+use rustc_ast::token::{DelimToken, TokenKind};
+use rustc_ast::tokenstream::TokenStream;
+use rustc_ast::{ast, ptr};
+use rustc_parse::parser::{ForceCollect, Parser};
+use rustc_parse::{stream_to_parser, MACRO_ARGUMENTS};
+use rustc_session::parse::ParseSess;
+use rustc_span::symbol::{self, kw};
+use rustc_span::Symbol;
+
+use crate::macros::MacroArg;
+use crate::rewrite::RewriteContext;
+
+pub(crate) mod asm;
+pub(crate) mod cfg_if;
+pub(crate) mod lazy_static;
+
+fn build_stream_parser<'a>(sess: &'a ParseSess, tokens: TokenStream) -> Parser<'a> {
+    stream_to_parser(sess, tokens, MACRO_ARGUMENTS)
+}
+
+fn build_parser<'a>(context: &RewriteContext<'a>, tokens: TokenStream) -> Parser<'a> {
+    build_stream_parser(context.parse_sess.inner(), tokens)
+}
+
+fn parse_macro_arg<'a, 'b: 'a>(parser: &'a mut Parser<'b>) -> Option<MacroArg> {
+    macro_rules! parse_macro_arg {
+        ($macro_arg:ident, $parser:expr, $f:expr) => {
+            let mut cloned_parser = (*parser).clone();
+            match $parser(&mut cloned_parser) {
+                Ok(x) => {
+                    if parser.sess.span_diagnostic.has_errors() {
+                        parser.sess.span_diagnostic.reset_err_count();
+                    } else {
+                        // Parsing succeeded.
+                        *parser = cloned_parser;
+                        return Some(MacroArg::$macro_arg($f(x)?));
+                    }
+                }
+                Err(mut e) => {
+                    e.cancel();
+                    parser.sess.span_diagnostic.reset_err_count();
+                }
+            }
+        };
+    }
+
+    parse_macro_arg!(
+        Expr,
+        |parser: &mut rustc_parse::parser::Parser<'b>| parser.parse_expr(),
+        |x: ptr::P<ast::Expr>| Some(x)
+    );
+    parse_macro_arg!(
+        Ty,
+        |parser: &mut rustc_parse::parser::Parser<'b>| parser.parse_ty(),
+        |x: ptr::P<ast::Ty>| Some(x)
+    );
+    parse_macro_arg!(
+        Pat,
+        |parser: &mut rustc_parse::parser::Parser<'b>| parser.parse_pat_no_top_alt(None),
+        |x: ptr::P<ast::Pat>| Some(x)
+    );
+    // `parse_item` returns `Option<ptr::P<ast::Item>>`.
+    parse_macro_arg!(
+        Item,
+        |parser: &mut rustc_parse::parser::Parser<'b>| parser.parse_item(ForceCollect::No),
+        |x: Option<ptr::P<ast::Item>>| x
+    );
+
+    None
+}
+
+pub(crate) struct ParsedMacroArgs {
+    pub(crate) vec_with_semi: bool,
+    pub(crate) trailing_comma: bool,
+    pub(crate) args: Vec<MacroArg>,
+}
+
+fn check_keyword<'a, 'b: 'a>(parser: &'a mut Parser<'b>) -> Option<MacroArg> {
+    for &keyword in RUST_KW.iter() {
+        if parser.token.is_keyword(keyword)
+            && parser.look_ahead(1, |t| {
+                t.kind == TokenKind::Eof
+                    || t.kind == TokenKind::Comma
+                    || t.kind == TokenKind::CloseDelim(DelimToken::NoDelim)
+            })
+        {
+            parser.bump();
+            return Some(MacroArg::Keyword(
+                symbol::Ident::with_dummy_span(keyword),
+                parser.prev_token.span,
+            ));
+        }
+    }
+    None
+}
+
+pub(crate) fn parse_macro_args(
+    context: &RewriteContext<'_>,
+    tokens: TokenStream,
+    style: DelimToken,
+    forced_bracket: bool,
+) -> Option<ParsedMacroArgs> {
+    let mut parser = build_parser(context, tokens);
+    let mut args = Vec::new();
+    let mut vec_with_semi = false;
+    let mut trailing_comma = false;
+
+    if DelimToken::Brace != style {
+        loop {
+            if let Some(arg) = check_keyword(&mut parser) {
+                args.push(arg);
+            } else if let Some(arg) = parse_macro_arg(&mut parser) {
+                args.push(arg);
+            } else {
+                return None;
+            }
+
+            match parser.token.kind {
+                TokenKind::Eof => break,
+                TokenKind::Comma => (),
+                TokenKind::Semi => {
+                    // Try to parse `vec![expr; expr]`
+                    if forced_bracket {
+                        parser.bump();
+                        if parser.token.kind != TokenKind::Eof {
+                            match parse_macro_arg(&mut parser) {
+                                Some(arg) => {
+                                    args.push(arg);
+                                    parser.bump();
+                                    if parser.token.kind == TokenKind::Eof && args.len() == 2 {
+                                        vec_with_semi = true;
+                                        break;
+                                    }
+                                }
+                                None => {
+                                    return None;
+                                }
+                            }
+                        }
+                    }
+                    return None;
+                }
+                _ if args.last().map_or(false, MacroArg::is_item) => continue,
+                _ => return None,
+            }
+
+            parser.bump();
+
+            if parser.token.kind == TokenKind::Eof {
+                trailing_comma = true;
+                break;
+            }
+        }
+    }
+
+    Some(ParsedMacroArgs {
+        vec_with_semi,
+        trailing_comma,
+        args,
+    })
+}
+
+pub(crate) fn parse_expr(
+    context: &RewriteContext<'_>,
+    tokens: TokenStream,
+) -> Option<ptr::P<ast::Expr>> {
+    let mut parser = build_parser(context, tokens);
+    parser.parse_expr().ok()
+}
+
+const RUST_KW: [Symbol; 59] = [
+    kw::PathRoot,
+    kw::DollarCrate,
+    kw::Underscore,
+    kw::As,
+    kw::Box,
+    kw::Break,
+    kw::Const,
+    kw::Continue,
+    kw::Crate,
+    kw::Else,
+    kw::Enum,
+    kw::Extern,
+    kw::False,
+    kw::Fn,
+    kw::For,
+    kw::If,
+    kw::Impl,
+    kw::In,
+    kw::Let,
+    kw::Loop,
+    kw::Match,
+    kw::Mod,
+    kw::Move,
+    kw::Mut,
+    kw::Pub,
+    kw::Ref,
+    kw::Return,
+    kw::SelfLower,
+    kw::SelfUpper,
+    kw::Static,
+    kw::Struct,
+    kw::Super,
+    kw::Trait,
+    kw::True,
+    kw::Type,
+    kw::Unsafe,
+    kw::Use,
+    kw::Where,
+    kw::While,
+    kw::Abstract,
+    kw::Become,
+    kw::Do,
+    kw::Final,
+    kw::Macro,
+    kw::Override,
+    kw::Priv,
+    kw::Typeof,
+    kw::Unsized,
+    kw::Virtual,
+    kw::Yield,
+    kw::Dyn,
+    kw::Async,
+    kw::Try,
+    kw::UnderscoreLifetime,
+    kw::StaticLifetime,
+    kw::Auto,
+    kw::Catch,
+    kw::Default,
+    kw::Union,
+];
diff --git a/src/parse/mod.rs b/src/parse/mod.rs
new file mode 100644
index 00000000000..5e88826ea8c
--- /dev/null
+++ b/src/parse/mod.rs
@@ -0,0 +1,3 @@
+pub(crate) mod macros;
+pub(crate) mod parser;
+pub(crate) mod session;
diff --git a/src/parse/parser.rs b/src/parse/parser.rs
new file mode 100644
index 00000000000..657217633f4
--- /dev/null
+++ b/src/parse/parser.rs
@@ -0,0 +1,175 @@
+use std::panic::{catch_unwind, AssertUnwindSafe};
+use std::path::{Path, PathBuf};
+
+use rustc_ast::token::TokenKind;
+use rustc_ast::{ast, ptr};
+use rustc_errors::Diagnostic;
+use rustc_parse::{new_parser_from_file, parser::Parser as RawParser};
+use rustc_span::{sym, Span};
+
+use crate::attr::first_attr_value_str_by_name;
+use crate::parse::session::ParseSess;
+use crate::Input;
+
+pub(crate) type DirectoryOwnership = rustc_expand::module::DirOwnership;
+pub(crate) type ModulePathSuccess = rustc_expand::module::ModulePathSuccess;
+pub(crate) type ModError<'a> = rustc_expand::module::ModError<'a>;
+
+#[derive(Clone)]
+pub(crate) struct Directory {
+    pub(crate) path: PathBuf,
+    pub(crate) ownership: DirectoryOwnership,
+}
+
+/// A parser for Rust source code.
+pub(crate) struct Parser<'a> {
+    parser: RawParser<'a>,
+}
+
+/// A builder for the `Parser`.
+#[derive(Default)]
+pub(crate) struct ParserBuilder<'a> {
+    sess: Option<&'a ParseSess>,
+    input: Option<Input>,
+}
+
+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 build(self) -> Result<Parser<'a>, ParserError> {
+        let sess = self.sess.ok_or(ParserError::NoParseSess)?;
+        let input = self.input.ok_or(ParserError::NoInput)?;
+
+        let parser = match Self::parser(sess.inner(), input) {
+            Ok(p) => p,
+            Err(db) => {
+                if let Some(diagnostics) = db {
+                    sess.emit_diagnostics(diagnostics);
+                    return Err(ParserError::ParserCreationError);
+                }
+                return Err(ParserError::ParsePanicError);
+            }
+        };
+
+        Ok(Parser { parser })
+    }
+
+    fn parser(
+        sess: &'a rustc_session::parse::ParseSess,
+        input: Input,
+    ) -> Result<rustc_parse::parser::Parser<'a>, Option<Vec<Diagnostic>>> {
+        match input {
+            Input::File(ref file) => catch_unwind(AssertUnwindSafe(move || {
+                new_parser_from_file(sess, file, None)
+            }))
+            .map_err(|_| None),
+            Input::Text(text) => rustc_parse::maybe_new_parser_from_source_str(
+                sess,
+                rustc_span::FileName::Custom("stdin".to_owned()),
+                text,
+            )
+            .map_err(Some),
+        }
+    }
+}
+
+#[derive(Debug, PartialEq)]
+pub(crate) enum ParserError {
+    NoParseSess,
+    NoInput,
+    ParserCreationError,
+    ParseError,
+    ParsePanicError,
+}
+
+impl<'a> Parser<'a> {
+    pub(crate) fn submod_path_from_attr(attrs: &[ast::Attribute], path: &Path) -> Option<PathBuf> {
+        let path_sym = first_attr_value_str_by_name(attrs, sym::path)?;
+        let path_str = path_sym.as_str();
+
+        // On windows, the base path might have the form
+        // `\\?\foo\bar` in which case it does not tolerate
+        // mixed `/` and `\` separators, so canonicalize
+        // `/` to `\`.
+        #[cfg(windows)]
+        let path_str = path_str.replace("/", "\\");
+
+        Some(path.join(path_str))
+    }
+
+    pub(crate) fn parse_file_as_module(
+        sess: &'a ParseSess,
+        path: &Path,
+        span: Span,
+    ) -> Result<(Vec<ast::Attribute>, Vec<ptr::P<ast::Item>>, Span), ParserError> {
+        let result = catch_unwind(AssertUnwindSafe(|| {
+            let mut parser = new_parser_from_file(sess.inner(), path, Some(span));
+            match parser.parse_mod(&TokenKind::Eof) {
+                Ok(result) => Some(result),
+                Err(mut e) => {
+                    sess.emit_or_cancel_diagnostic(&mut e);
+                    if sess.can_reset_errors() {
+                        sess.reset_errors();
+                    }
+                    None
+                }
+            }
+        }));
+        match result {
+            Ok(Some(m)) if !sess.has_errors() => Ok(m),
+            Ok(Some(m)) if sess.can_reset_errors() => {
+                sess.reset_errors();
+                Ok(m)
+            }
+            Ok(_) => Err(ParserError::ParseError),
+            Err(..) if path.exists() => Err(ParserError::ParseError),
+            Err(_) => Err(ParserError::ParsePanicError),
+        }
+    }
+
+    pub(crate) fn parse_crate(
+        input: Input,
+        sess: &'a ParseSess,
+    ) -> Result<ast::Crate, ParserError> {
+        let krate = Parser::parse_crate_inner(input, sess)?;
+        if !sess.has_errors() {
+            return Ok(krate);
+        }
+
+        if sess.can_reset_errors() {
+            sess.reset_errors();
+            return Ok(krate);
+        }
+
+        Err(ParserError::ParseError)
+    }
+
+    fn parse_crate_inner(input: Input, sess: &'a ParseSess) -> Result<ast::Crate, ParserError> {
+        ParserBuilder::default()
+            .input(input)
+            .sess(sess)
+            .build()?
+            .parse_crate_mod()
+    }
+
+    fn parse_crate_mod(&mut self) -> Result<ast::Crate, ParserError> {
+        let mut parser = AssertUnwindSafe(&mut self.parser);
+
+        match catch_unwind(move || parser.parse_crate_mod()) {
+            Ok(Ok(k)) => Ok(k),
+            Ok(Err(mut db)) => {
+                db.emit();
+                Err(ParserError::ParseError)
+            }
+            Err(_) => Err(ParserError::ParsePanicError),
+        }
+    }
+}
diff --git a/src/parse/session.rs b/src/parse/session.rs
new file mode 100644
index 00000000000..624fed0d2de
--- /dev/null
+++ b/src/parse/session.rs
@@ -0,0 +1,465 @@
+use std::path::Path;
+use std::sync::atomic::{AtomicBool, Ordering};
+
+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},
+    symbol, BytePos, Span,
+};
+
+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: Lrc<IgnorePathSet>,
+    can_reset_errors: Lrc<AtomicBool>,
+}
+
+/// 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: Lrc<IgnorePathSet>,
+    source_map: Lrc<SourceMap>,
+    emitter: Box<dyn Emitter + Send>,
+    has_non_ignorable_parser_errors: bool,
+    can_reset: Lrc<AtomicBool>,
+}
+
+impl SilentOnIgnoredFilesEmitter {
+    fn handle_non_ignoreable_error(&mut self, db: &Diagnostic) {
+        self.has_non_ignorable_parser_errors = true;
+        self.can_reset.store(false, Ordering::Release);
+        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(rustc_span::RealFileName::LocalPath(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.store(true, Ordering::Release);
+                    }
+                    return;
+                }
+            };
+        }
+        self.handle_non_ignoreable_error(db);
+    }
+}
+
+fn default_handler(
+    source_map: Lrc<SourceMap>,
+    ignore_path_set: Lrc<IgnorePathSet>,
+    can_reset: Lrc<AtomicBool>,
+    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) => Lrc::new(ignore_path_set),
+            Err(e) => return Err(ErrorKind::InvalidGlobPattern(e)),
+        };
+        let source_map = Lrc::new(SourceMap::new(FilePathMapping::empty()));
+        let can_reset_errors = Lrc::new(AtomicBool::new(false));
+
+        let handler = default_handler(
+            Lrc::clone(&source_map),
+            Lrc::clone(&ignore_path_set),
+            Lrc::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: symbol::Ident,
+        relative: Option<symbol::Ident>,
+        dir_path: &Path,
+    ) -> Result<rustc_expand::module::ModulePathSuccess, rustc_expand::module::ModError<'_>> {
+        rustc_expand::module::default_submod_path(&self.parse_sess, id, relative, dir_path)
+    }
+
+    pub(crate) fn is_file_parsed(&self, path: &Path) -> bool {
+        self.parse_sess
+            .source_map()
+            .get_source_file(&rustc_span::FileName::Real(
+                rustc_span::RealFileName::LocalPath(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_file_contents(&self, span: Span) -> Lrc<rustc_span::SourceFile> {
+        self.parse_sess
+            .source_map()
+            .lookup_source_file(span.data().lo)
+    }
+
+    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_diagnostic_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,
+            Lrc::clone(source_file.src.as_ref().unwrap()),
+        )
+    }
+
+    pub(crate) fn get_original_snippet(&self, file_name: &FileName) -> Option<Lrc<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 parse 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(crate) fn emit_or_cancel_diagnostic(&self, diagnostic: &mut Diagnostic) {
+        self.parse_sess.span_diagnostic.emit_diagnostic(diagnostic);
+        // The Handler will check whether the diagnostic should be emitted
+        // based on the user's rustfmt configuration and the originating file
+        // that caused the parser error. If the Handler determined it should skip
+        // emission then we need to ensure the diagnostic is cancelled.
+        if !diagnostic.cancelled() {
+            diagnostic.cancel();
+        }
+    }
+
+    pub(super) fn can_reset_errors(&self) -> bool {
+        self.can_reset_errors.load(Ordering::Acquire)
+    }
+
+    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::*;
+
+    use rustfmt_config_proc_macro::nightly_only_test;
+
+    mod emitter {
+        use super::*;
+        use crate::config::IgnoreList;
+        use crate::utils::mk_sp;
+        use rustc_span::{FileName as SourceMapFileName, MultiSpan, RealFileName, DUMMY_SP};
+        use std::path::PathBuf;
+        use std::sync::atomic::AtomicU32;
+
+        struct TestEmitter {
+            num_emitted_errors: Lrc<AtomicU32>,
+        }
+
+        impl Emitter for TestEmitter {
+            fn source_map(&self) -> Option<&Lrc<SourceMap>> {
+                None
+            }
+            fn emit_diagnostic(&mut self, _db: &Diagnostic) {
+                self.num_emitted_errors.fetch_add(1, Ordering::Release);
+            }
+        }
+
+        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,
+                is_lint: false,
+            }
+        }
+
+        fn build_emitter(
+            num_emitted_errors: Lrc<AtomicU32>,
+            can_reset: Lrc<AtomicBool>,
+            source_map: Option<Lrc<SourceMap>>,
+            ignore_list: Option<IgnoreList>,
+        ) -> SilentOnIgnoredFilesEmitter {
+            let emitter_writer = TestEmitter { num_emitted_errors };
+            let source_map =
+                source_map.unwrap_or_else(|| Lrc::new(SourceMap::new(FilePathMapping::empty())));
+            let ignore_path_set = Lrc::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 = Lrc::new(AtomicU32::new(0));
+            let can_reset_errors = Lrc::new(AtomicBool::new(false));
+            let ignore_list = get_ignore_list(r#"ignore = ["foo.rs"]"#);
+            let source_map = Lrc::new(SourceMap::new(FilePathMapping::empty()));
+            let source =
+                String::from(r#"extern "system" fn jni_symbol!( funcName ) ( ... ) -> {} "#);
+            source_map.new_source_file(
+                SourceMapFileName::Real(RealFileName::LocalPath(PathBuf::from("foo.rs"))),
+                source,
+            );
+            let mut emitter = build_emitter(
+                Lrc::clone(&num_emitted_errors),
+                Lrc::clone(&can_reset_errors),
+                Some(Lrc::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.load(Ordering::Acquire), 1);
+            assert_eq!(can_reset_errors.load(Ordering::Acquire), false);
+        }
+
+        #[nightly_only_test]
+        #[test]
+        fn handles_recoverable_parse_error_in_ignored_file() {
+            let num_emitted_errors = Lrc::new(AtomicU32::new(0));
+            let can_reset_errors = Lrc::new(AtomicBool::new(false));
+            let ignore_list = get_ignore_list(r#"ignore = ["foo.rs"]"#);
+            let source_map = Lrc::new(SourceMap::new(FilePathMapping::empty()));
+            let source = String::from(r#"pub fn bar() { 1x; }"#);
+            source_map.new_source_file(
+                SourceMapFileName::Real(RealFileName::LocalPath(PathBuf::from("foo.rs"))),
+                source,
+            );
+            let mut emitter = build_emitter(
+                Lrc::clone(&num_emitted_errors),
+                Lrc::clone(&can_reset_errors),
+                Some(Lrc::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.load(Ordering::Acquire), 0);
+            assert_eq!(can_reset_errors.load(Ordering::Acquire), true);
+        }
+
+        #[nightly_only_test]
+        #[test]
+        fn handles_recoverable_parse_error_in_non_ignored_file() {
+            let num_emitted_errors = Lrc::new(AtomicU32::new(0));
+            let can_reset_errors = Lrc::new(AtomicBool::new(false));
+            let source_map = Lrc::new(SourceMap::new(FilePathMapping::empty()));
+            let source = String::from(r#"pub fn bar() { 1x; }"#);
+            source_map.new_source_file(
+                SourceMapFileName::Real(RealFileName::LocalPath(PathBuf::from("foo.rs"))),
+                source,
+            );
+            let mut emitter = build_emitter(
+                Lrc::clone(&num_emitted_errors),
+                Lrc::clone(&can_reset_errors),
+                Some(Lrc::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.load(Ordering::Acquire), 1);
+            assert_eq!(can_reset_errors.load(Ordering::Acquire), false);
+        }
+
+        #[nightly_only_test]
+        #[test]
+        fn handles_mix_of_recoverable_parse_error() {
+            let num_emitted_errors = Lrc::new(AtomicU32::new(0));
+            let can_reset_errors = Lrc::new(AtomicBool::new(false));
+            let source_map = Lrc::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(RealFileName::LocalPath(PathBuf::from("bar.rs"))),
+                bar_source,
+            );
+            source_map.new_source_file(
+                SourceMapFileName::Real(RealFileName::LocalPath(PathBuf::from("foo.rs"))),
+                foo_source,
+            );
+            source_map.new_source_file(
+                SourceMapFileName::Real(RealFileName::LocalPath(PathBuf::from("fatal.rs"))),
+                fatal_source,
+            );
+            let mut emitter = build_emitter(
+                Lrc::clone(&num_emitted_errors),
+                Lrc::clone(&can_reset_errors),
+                Some(Lrc::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.load(Ordering::Acquire), 2);
+            assert_eq!(can_reset_errors.load(Ordering::Acquire), false);
+        }
+    }
+}