about summary refs log tree commit diff
diff options
context:
space:
mode:
authorMazdak Farrokhzad <twingoow@gmail.com>2020-03-08 13:36:20 +0100
committerMazdak Farrokhzad <twingoow@gmail.com>2020-03-18 15:08:25 +0100
commit83a757a9ca0d0f61bd2325e9a95d591d53e01129 (patch)
tree22d1a2dd0556c0a303290cd02f5212412b0aae58
parent59bf8a07f93d194abcc880a366bda37563a9402b (diff)
downloadrust-83a757a9ca0d0f61bd2325e9a95d591d53e01129.tar.gz
rust-83a757a9ca0d0f61bd2325e9a95d591d53e01129.zip
outline modules: parse -> expand.
-rw-r--r--src/librustc_builtin_macros/source_util.rs5
-rw-r--r--src/librustc_expand/expand.rs77
-rw-r--r--src/librustc_expand/mbe/macro_rules.rs37
-rw-r--r--src/librustc_parse/config.rs9
-rw-r--r--src/librustc_parse/lib.rs20
-rw-r--r--src/librustc_parse/parser/mod.rs33
-rw-r--r--src/librustc_parse/parser/module.rs105
-rw-r--r--src/librustc_parse/parser/stmt.rs10
8 files changed, 116 insertions, 180 deletions
diff --git a/src/librustc_builtin_macros/source_util.rs b/src/librustc_builtin_macros/source_util.rs
index 5ad72a7443d..662bbe6a287 100644
--- a/src/librustc_builtin_macros/source_util.rs
+++ b/src/librustc_builtin_macros/source_util.rs
@@ -5,7 +5,7 @@ use rustc_ast::tokenstream::TokenStream;
 use rustc_ast_pretty::pprust;
 use rustc_expand::base::{self, *};
 use rustc_expand::panictry;
-use rustc_parse::{self, new_sub_parser_from_file, parser::Parser, DirectoryOwnership};
+use rustc_parse::{self, new_sub_parser_from_file, parser::Parser};
 use rustc_session::lint::builtin::INCOMPLETE_INCLUDE;
 use rustc_span::symbol::Symbol;
 use rustc_span::{self, Pos, Span};
@@ -108,8 +108,7 @@ pub fn expand_include<'cx>(
             return DummyResult::any(sp);
         }
     };
-    let directory_ownership = DirectoryOwnership::Owned { relative: None };
-    let p = new_sub_parser_from_file(cx.parse_sess(), &file, directory_ownership, None, sp);
+    let p = new_sub_parser_from_file(cx.parse_sess(), &file, None, sp);
 
     struct ExpandResult<'a> {
         p: Parser<'a>,
diff --git a/src/librustc_expand/expand.rs b/src/librustc_expand/expand.rs
index 38b8ab62212..61a34a39d58 100644
--- a/src/librustc_expand/expand.rs
+++ b/src/librustc_expand/expand.rs
@@ -18,10 +18,10 @@ use rustc_attr::{self as attr, is_builtin_attr, HasAttrs};
 use rustc_errors::{Applicability, FatalError, PResult};
 use rustc_feature::Features;
 use rustc_parse::configure;
-use rustc_parse::parser::module;
+use rustc_parse::parser::module::{parse_external_mod, push_directory};
 use rustc_parse::parser::Parser;
 use rustc_parse::validate_attr;
-use rustc_parse::DirectoryOwnership;
+use rustc_parse::{Directory, DirectoryOwnership};
 use rustc_session::lint::builtin::UNUSED_DOC_COMMENTS;
 use rustc_session::lint::BuiltinLintDiagnostics;
 use rustc_session::parse::{feature_err, ParseSess};
@@ -1428,8 +1428,12 @@ impl<'a, 'b> MutVisitor for InvocationCollector<'a, 'b> {
                 .make_items();
         }
 
+        let mut attrs = mem::take(&mut item.attrs); // We do this to please borrowck.
+        let ident = item.ident;
+
         match item.kind {
             ast::ItemKind::MacCall(..) => {
+                item.attrs = attrs;
                 self.check_attributes(&item.attrs);
                 item.and_then(|item| match item.kind {
                     ItemKind::MacCall(mac) => self
@@ -1441,45 +1445,56 @@ impl<'a, 'b> MutVisitor for InvocationCollector<'a, 'b> {
                     _ => unreachable!(),
                 })
             }
-            ast::ItemKind::Mod(ast::Mod { inner, inline, .. })
-                if item.ident != Ident::invalid() =>
-            {
-                let orig_directory_ownership = self.cx.current_expansion.directory_ownership;
+            ast::ItemKind::Mod(ref mut old_mod @ ast::Mod { .. }) if ident != Ident::invalid() => {
+                let sess = self.cx.parse_sess;
+                let orig_ownership = self.cx.current_expansion.directory_ownership;
                 let mut module = (*self.cx.current_expansion.module).clone();
-                module.mod_path.push(item.ident);
-
-                if inline {
-                    module::push_directory(
-                        item.ident,
-                        &item.attrs,
-                        &mut self.cx.current_expansion.directory_ownership,
-                        &mut module.directory,
-                    );
+
+                let pushed = &mut false; // Record `parse_external_mod` pushing so we can pop.
+                let dir = Directory { ownership: orig_ownership, path: module.directory };
+                let Directory { ownership, path } = if old_mod.inline {
+                    // Inline `mod foo { ... }`, but we still need to push directories.
+                    item.attrs = attrs;
+                    push_directory(ident, &item.attrs, dir)
                 } else {
-                    let path = self.cx.parse_sess.source_map().span_to_unmapped_path(inner);
-                    let mut path = match path {
-                        FileName::Real(path) => path,
-                        other => PathBuf::from(other.to_string()),
-                    };
-                    let directory_ownership = match path.file_name().unwrap().to_str() {
-                        Some("mod.rs") => DirectoryOwnership::Owned { relative: None },
-                        Some(_) => DirectoryOwnership::Owned { relative: Some(item.ident) },
-                        None => DirectoryOwnership::UnownedViaMod,
+                    // We have an outline `mod foo;` so we need to parse the file.
+                    let (new_mod, dir) = parse_external_mod(sess, ident, dir, &mut attrs, pushed);
+                    *old_mod = new_mod;
+                    item.attrs = attrs;
+                    // File can have inline attributes, e.g., `#![cfg(...)]` & co. => Reconfigure.
+                    item = match self.configure(item) {
+                        Some(node) => node,
+                        None => {
+                            if *pushed {
+                                sess.included_mod_stack.borrow_mut().pop();
+                            }
+                            return Default::default();
+                        }
                     };
-                    path.pop();
-                    module.directory = path;
-                    self.cx.current_expansion.directory_ownership = directory_ownership;
-                }
+                    dir
+                };
 
+                // Set the module info before we flat map.
+                self.cx.current_expansion.directory_ownership = ownership;
+                module.directory = path;
+                module.mod_path.push(ident);
                 let orig_module =
                     mem::replace(&mut self.cx.current_expansion.module, Rc::new(module));
+
                 let result = noop_flat_map_item(item, self);
+
+                // Restore the module info.
                 self.cx.current_expansion.module = orig_module;
-                self.cx.current_expansion.directory_ownership = orig_directory_ownership;
+                self.cx.current_expansion.directory_ownership = orig_ownership;
+                if *pushed {
+                    sess.included_mod_stack.borrow_mut().pop();
+                }
                 result
             }
-
-            _ => noop_flat_map_item(item, self),
+            _ => {
+                item.attrs = attrs;
+                noop_flat_map_item(item, self)
+            }
         }
     }
 
diff --git a/src/librustc_expand/mbe/macro_rules.rs b/src/librustc_expand/mbe/macro_rules.rs
index 2268c9b3854..1628d8bfdbc 100644
--- a/src/librustc_expand/mbe/macro_rules.rs
+++ b/src/librustc_expand/mbe/macro_rules.rs
@@ -1,4 +1,4 @@
-use crate::base::{DummyResult, ExpansionData, ExtCtxt, MacResult, TTMacroExpander};
+use crate::base::{DummyResult, ExtCtxt, MacResult, TTMacroExpander};
 use crate::base::{SyntaxExtension, SyntaxExtensionKind};
 use crate::expand::{ensure_complete_parse, parse_ast_fragment, AstFragment, AstFragmentKind};
 use crate::mbe;
@@ -18,7 +18,6 @@ use rustc_data_structures::sync::Lrc;
 use rustc_errors::{Applicability, DiagnosticBuilder, FatalError};
 use rustc_feature::Features;
 use rustc_parse::parser::Parser;
-use rustc_parse::Directory;
 use rustc_session::parse::ParseSess;
 use rustc_span::edition::Edition;
 use rustc_span::hygiene::Transparency;
@@ -182,6 +181,8 @@ fn generic_extension<'cx>(
     lhses: &[mbe::TokenTree],
     rhses: &[mbe::TokenTree],
 ) -> Box<dyn MacResult + 'cx> {
+    let sess = cx.parse_sess;
+
     if cx.trace_macros() {
         let msg = format!("expanding `{}! {{ {} }}`", name, pprust::tts_to_string(arg.clone()));
         trace_macros_note(&mut cx.expansions, sp, msg);
@@ -209,7 +210,7 @@ fn generic_extension<'cx>(
     // hacky, but speeds up the `html5ever` benchmark significantly. (Issue
     // 68836 suggests a more comprehensive but more complex change to deal with
     // this situation.)
-    let parser = parser_from_cx(&cx.current_expansion, &cx.parse_sess, arg.clone());
+    let parser = parser_from_cx(sess, arg.clone());
 
     for (i, lhs) in lhses.iter().enumerate() {
         // try each arm's matchers
@@ -222,14 +223,13 @@ fn generic_extension<'cx>(
         // This is used so that if a matcher is not `Success(..)`ful,
         // then the spans which became gated when parsing the unsuccessful matcher
         // are not recorded. On the first `Success(..)`ful matcher, the spans are merged.
-        let mut gated_spans_snapshot =
-            mem::take(&mut *cx.parse_sess.gated_spans.spans.borrow_mut());
+        let mut gated_spans_snapshot = mem::take(&mut *sess.gated_spans.spans.borrow_mut());
 
         match parse_tt(&mut Cow::Borrowed(&parser), lhs_tt) {
             Success(named_matches) => {
                 // The matcher was `Success(..)`ful.
                 // Merge the gated spans from parsing the matcher with the pre-existing ones.
-                cx.parse_sess.gated_spans.merge(gated_spans_snapshot);
+                sess.gated_spans.merge(gated_spans_snapshot);
 
                 let rhs = match rhses[i] {
                     // ignore delimiters
@@ -258,11 +258,7 @@ fn generic_extension<'cx>(
                     trace_macros_note(&mut cx.expansions, sp, msg);
                 }
 
-                let directory = Directory {
-                    path: cx.current_expansion.module.directory.clone(),
-                    ownership: cx.current_expansion.directory_ownership,
-                };
-                let mut p = Parser::new(cx.parse_sess(), tts, Some(directory), true, false, None);
+                let mut p = Parser::new(cx.parse_sess(), tts, false, None);
                 p.root_module_name =
                     cx.current_expansion.module.mod_path.last().map(|id| id.to_string());
                 p.last_type_ascription = cx.current_expansion.prior_type_ascription;
@@ -289,7 +285,7 @@ fn generic_extension<'cx>(
 
         // The matcher was not `Success(..)`ful.
         // Restore to the state before snapshotting and maybe try again.
-        mem::swap(&mut gated_spans_snapshot, &mut cx.parse_sess.gated_spans.spans.borrow_mut());
+        mem::swap(&mut gated_spans_snapshot, &mut sess.gated_spans.spans.borrow_mut());
     }
     drop(parser);
 
@@ -309,8 +305,7 @@ fn generic_extension<'cx>(
                 mbe::TokenTree::Delimited(_, ref delim) => &delim.tts[..],
                 _ => continue,
             };
-            let parser = parser_from_cx(&cx.current_expansion, &cx.parse_sess, arg.clone());
-            match parse_tt(&mut Cow::Borrowed(&parser), lhs_tt) {
+            match parse_tt(&mut Cow::Borrowed(&parser_from_cx(sess, arg.clone())), lhs_tt) {
                 Success(_) => {
                     if comma_span.is_dummy() {
                         err.note("you might be missing a comma");
@@ -392,7 +387,7 @@ pub fn compile_declarative_macro(
         ),
     ];
 
-    let parser = Parser::new(sess, body, None, true, true, rustc_parse::MACRO_ARGUMENTS);
+    let parser = Parser::new(sess, body, true, rustc_parse::MACRO_ARGUMENTS);
     let argument_map = match parse_tt(&mut Cow::Borrowed(&parser), &argument_gram) {
         Success(m) => m,
         Failure(token, msg) => {
@@ -1209,16 +1204,8 @@ fn quoted_tt_to_string(tt: &mbe::TokenTree) -> String {
     }
 }
 
-fn parser_from_cx<'cx>(
-    current_expansion: &'cx ExpansionData,
-    sess: &'cx ParseSess,
-    tts: TokenStream,
-) -> Parser<'cx> {
-    let directory = Directory {
-        path: current_expansion.module.directory.clone(),
-        ownership: current_expansion.directory_ownership,
-    };
-    Parser::new(sess, tts, Some(directory), true, true, rustc_parse::MACRO_ARGUMENTS)
+fn parser_from_cx<'cx>(sess: &'cx ParseSess, tts: TokenStream) -> Parser<'cx> {
+    Parser::new(sess, tts, true, rustc_parse::MACRO_ARGUMENTS)
 }
 
 /// Generates an appropriate parsing failure message. For EOF, this is "unexpected end...". For
diff --git a/src/librustc_parse/config.rs b/src/librustc_parse/config.rs
index d209da866e1..c611f249420 100644
--- a/src/librustc_parse/config.rs
+++ b/src/librustc_parse/config.rs
@@ -538,12 +538,3 @@ impl<'a> MutVisitor for StripUnconfigured<'a> {
 fn is_cfg(attr: &Attribute) -> bool {
     attr.check_name(sym::cfg)
 }
-
-/// Process the potential `cfg` attributes on a module.
-/// Also determine if the module should be included in this configuration.
-pub fn process_configure_mod(sess: &ParseSess, cfg_mods: bool, attrs: &mut Vec<Attribute>) -> bool {
-    // Don't perform gated feature checking.
-    let mut strip_unconfigured = StripUnconfigured { sess, features: None };
-    strip_unconfigured.process_cfg_attrs(attrs);
-    !cfg_mods || strip_unconfigured.in_cfg(&attrs)
-}
diff --git a/src/librustc_parse/lib.rs b/src/librustc_parse/lib.rs
index 884499ff2dd..bcaae02942e 100644
--- a/src/librustc_parse/lib.rs
+++ b/src/librustc_parse/lib.rs
@@ -3,6 +3,7 @@
 #![feature(bool_to_option)]
 #![feature(crate_visibility_modifier)]
 #![feature(bindings_after_at)]
+#![feature(try_blocks)]
 
 use rustc_ast::ast;
 use rustc_ast::token::{self, Nonterminal};
@@ -119,10 +120,7 @@ pub fn maybe_new_parser_from_source_str(
     name: FileName,
     source: String,
 ) -> Result<Parser<'_>, Vec<Diagnostic>> {
-    let mut parser =
-        maybe_source_file_to_parser(sess, sess.source_map().new_source_file(name, source))?;
-    parser.recurse_into_file_modules = false;
-    Ok(parser)
+    maybe_source_file_to_parser(sess, sess.source_map().new_source_file(name, source))
 }
 
 /// Creates a new parser, handling errors as appropriate if the file doesn't exist.
@@ -146,12 +144,10 @@ pub fn maybe_new_parser_from_file<'a>(
 pub fn new_sub_parser_from_file<'a>(
     sess: &'a ParseSess,
     path: &Path,
-    directory_ownership: DirectoryOwnership,
     module_name: Option<String>,
     sp: Span,
 ) -> Parser<'a> {
     let mut p = source_file_to_parser(sess, file_to_source_file(sess, path, Some(sp)));
-    p.directory.ownership = directory_ownership;
     p.root_module_name = module_name;
     p
 }
@@ -257,7 +253,7 @@ pub fn stream_to_parser<'a>(
     stream: TokenStream,
     subparser_name: Option<&'static str>,
 ) -> Parser<'a> {
-    Parser::new(sess, stream, None, true, false, subparser_name)
+    Parser::new(sess, stream, false, subparser_name)
 }
 
 /// Given a stream, the `ParseSess` and the base directory, produces a parser.
@@ -271,12 +267,8 @@ pub fn stream_to_parser<'a>(
 /// The main usage of this function is outside of rustc, for those who uses
 /// librustc_ast as a library. Please do not remove this function while refactoring
 /// just because it is not used in rustc codebase!
-pub fn stream_to_parser_with_base_dir(
-    sess: &ParseSess,
-    stream: TokenStream,
-    base_dir: Directory,
-) -> Parser<'_> {
-    Parser::new(sess, stream, Some(base_dir), true, false, None)
+pub fn stream_to_parser_with_base_dir(sess: &ParseSess, stream: TokenStream) -> Parser<'_> {
+    Parser::new(sess, stream, false, None)
 }
 
 /// Runs the given subparser `f` on the tokens of the given `attr`'s item.
@@ -286,7 +278,7 @@ pub fn parse_in<'a, T>(
     name: &'static str,
     mut f: impl FnMut(&mut Parser<'a>) -> PResult<'a, T>,
 ) -> PResult<'a, T> {
-    let mut parser = Parser::new(sess, tts, None, false, false, Some(name));
+    let mut parser = Parser::new(sess, tts, false, Some(name));
     let result = f(&mut parser)?;
     if parser.token != token::Eof {
         parser.unexpected()?;
diff --git a/src/librustc_parse/parser/mod.rs b/src/librustc_parse/parser/mod.rs
index 40dc9275b32..f4862a6c87b 100644
--- a/src/librustc_parse/parser/mod.rs
+++ b/src/librustc_parse/parser/mod.rs
@@ -13,7 +13,6 @@ mod stmt;
 use diagnostics::Error;
 
 use crate::lexer::UnmatchedBrace;
-use crate::{Directory, DirectoryOwnership};
 
 use log::debug;
 use rustc_ast::ast::DUMMY_NODE_ID;
@@ -28,11 +27,9 @@ use rustc_ast::util::comments::{doc_comment_style, strip_doc_comment_decoration}
 use rustc_ast_pretty::pprust;
 use rustc_errors::{struct_span_err, Applicability, DiagnosticBuilder, FatalError, PResult};
 use rustc_session::parse::ParseSess;
-use rustc_span::source_map::respan;
+use rustc_span::source_map::{respan, Span, DUMMY_SP};
 use rustc_span::symbol::{kw, sym, Symbol};
-use rustc_span::{FileName, Span, DUMMY_SP};
 
-use std::path::PathBuf;
 use std::{cmp, mem, slice};
 
 bitflags::bitflags! {
@@ -93,11 +90,6 @@ pub struct Parser<'a> {
     /// The previous token.
     pub prev_token: Token,
     restrictions: Restrictions,
-    /// Used to determine the path to externally loaded source files.
-    pub(super) directory: Directory,
-    /// `true` to parse sub-modules in other files.
-    // Public for rustfmt usage.
-    pub recurse_into_file_modules: bool,
     /// Name of the root module this parser originated from. If `None`, then the
     /// name is not known. This does not change while the parser is descending
     /// into modules, and sub-parsers have new values for this name.
@@ -105,9 +97,6 @@ pub struct Parser<'a> {
     expected_tokens: Vec<TokenType>,
     token_cursor: TokenCursor,
     desugar_doc_comments: bool,
-    /// `true` we should configure out of line modules as we parse.
-    // Public for rustfmt usage.
-    pub cfg_mods: bool,
     /// This field is used to keep track of how many left angle brackets we have seen. This is
     /// required in order to detect extra leading left angle brackets (`<` characters) and error
     /// appropriately.
@@ -355,8 +344,6 @@ impl<'a> Parser<'a> {
     pub fn new(
         sess: &'a ParseSess,
         tokens: TokenStream,
-        directory: Option<Directory>,
-        recurse_into_file_modules: bool,
         desugar_doc_comments: bool,
         subparser_name: Option<&'static str>,
     ) -> Self {
@@ -365,11 +352,6 @@ impl<'a> Parser<'a> {
             token: Token::dummy(),
             prev_token: Token::dummy(),
             restrictions: Restrictions::empty(),
-            recurse_into_file_modules,
-            directory: Directory {
-                path: PathBuf::new(),
-                ownership: DirectoryOwnership::Owned { relative: None },
-            },
             root_module_name: None,
             expected_tokens: Vec::new(),
             token_cursor: TokenCursor {
@@ -377,7 +359,6 @@ impl<'a> Parser<'a> {
                 stack: Vec::new(),
             },
             desugar_doc_comments,
-            cfg_mods: true,
             unmatched_angle_bracket_count: 0,
             max_angle_bracket_count: 0,
             unclosed_delims: Vec::new(),
@@ -389,18 +370,6 @@ impl<'a> Parser<'a> {
         // Make parser point to the first token.
         parser.bump();
 
-        if let Some(directory) = directory {
-            parser.directory = directory;
-        } else if !parser.token.span.is_dummy() {
-            if let Some(FileName::Real(path)) =
-                &sess.source_map().lookup_char_pos(parser.token.span.lo()).file.unmapped_path
-            {
-                if let Some(directory_path) = path.parent() {
-                    parser.directory.path = directory_path.to_path_buf();
-                }
-            }
-        }
-
         parser
     }
 
diff --git a/src/librustc_parse/parser/module.rs b/src/librustc_parse/parser/module.rs
index 66faf295b72..695afafdd82 100644
--- a/src/librustc_parse/parser/module.rs
+++ b/src/librustc_parse/parser/module.rs
@@ -1,7 +1,7 @@
 use super::item::ItemInfo;
 use super::Parser;
 
-use crate::{new_sub_parser_from_file, DirectoryOwnership};
+use crate::{new_sub_parser_from_file, Directory, DirectoryOwnership};
 
 use rustc_ast::ast::{self, Attribute, Crate, Ident, ItemKind, Mod};
 use rustc_ast::attr;
@@ -39,25 +39,12 @@ impl<'a> Parser<'a> {
 
     /// Parses a `mod <foo> { ... }` or `mod <foo>;` item.
     pub(super) fn parse_item_mod(&mut self, attrs: &mut Vec<Attribute>) -> PResult<'a, ItemInfo> {
-        let in_cfg = crate::config::process_configure_mod(self.sess, self.cfg_mods, attrs);
-
         let id = self.parse_ident()?;
         let (module, mut inner_attrs) = if self.eat(&token::Semi) {
-            if in_cfg && self.recurse_into_file_modules {
-                let dir = &self.directory;
-                parse_external_module(self.sess, self.cfg_mods, id, dir.ownership, &dir.path, attrs)
-            } else {
-                Default::default()
-            }
+            Default::default()
         } else {
-            let old_directory = self.directory.clone();
-            push_directory(id, &attrs, &mut self.directory.ownership, &mut self.directory.path);
-
             self.expect(&token::OpenDelim(token::Brace))?;
-            let module = self.parse_mod(&token::CloseDelim(token::Brace))?;
-
-            self.directory = old_directory;
-            module
+            self.parse_mod(&token::CloseDelim(token::Brace))?
         };
         attrs.append(&mut inner_attrs);
         Ok((id, ItemKind::Mod(module)))
@@ -95,41 +82,45 @@ impl<'a> Parser<'a> {
     }
 }
 
-fn parse_external_module(
+pub fn parse_external_mod(
     sess: &ParseSess,
-    cfg_mods: bool,
-    id: ast::Ident,
-    ownership: DirectoryOwnership,
-    dir_path: &Path,
-    attrs: &[Attribute],
-) -> (Mod, Vec<Attribute>) {
-    submod_path(sess, id, &attrs, ownership, dir_path)
-        .and_then(|r| eval_src_mod(sess, cfg_mods, r.path, r.ownership, id))
-        .map_err(|mut err| err.emit())
-        .unwrap_or_default()
-}
-
-/// Reads a module from a source file.
-fn eval_src_mod<'a>(
-    sess: &'a ParseSess,
-    cfg_mods: bool,
-    path: PathBuf,
-    dir_ownership: DirectoryOwnership,
     id: ast::Ident,
-) -> PResult<'a, (Mod, Vec<Attribute>)> {
-    let mut included_mod_stack = sess.included_mod_stack.borrow_mut();
-    error_on_circular_module(sess, id.span, &path, &included_mod_stack)?;
-    included_mod_stack.push(path.clone());
-    drop(included_mod_stack);
-
-    let mut p0 =
-        new_sub_parser_from_file(sess, &path, dir_ownership, Some(id.to_string()), id.span);
-    p0.cfg_mods = cfg_mods;
-    let mut module = p0.parse_mod(&token::Eof)?;
-    module.0.inline = false;
+    Directory { mut ownership, path }: Directory,
+    attrs: &mut Vec<Attribute>,
+    pop_mod_stack: &mut bool,
+) -> (Mod, Directory) {
+    // We bail on the first error, but that error does not cause a fatal error... (1)
+    let result: PResult<'_, _> = try {
+        // Extract the file path and the new ownership.
+        let mp = submod_path(sess, id, &attrs, ownership, &path)?;
+        ownership = mp.ownership;
+
+        // Ensure file paths are acyclic.
+        let mut included_mod_stack = sess.included_mod_stack.borrow_mut();
+        error_on_circular_module(sess, id.span, &mp.path, &included_mod_stack)?;
+        included_mod_stack.push(mp.path.clone());
+        *pop_mod_stack = true; // We have pushed, so notify caller.
+        drop(included_mod_stack);
+
+        // Actually parse the external file as amodule.
+        let mut p0 = new_sub_parser_from_file(sess, &mp.path, Some(id.to_string()), id.span);
+        let mut module = p0.parse_mod(&token::Eof)?;
+        module.0.inline = false;
+        module
+    };
+    // (1) ...instead, we return a dummy module.
+    let (module, mut new_attrs) = result.map_err(|mut err| err.emit()).unwrap_or_default();
+    attrs.append(&mut new_attrs);
+
+    // Extract the directory path for submodules of `module`.
+    let path = sess.source_map().span_to_unmapped_path(module.inner);
+    let mut path = match path {
+        FileName::Real(path) => path,
+        other => PathBuf::from(other.to_string()),
+    };
+    path.pop();
 
-    sess.included_mod_stack.borrow_mut().pop();
-    Ok(module)
+    (module, Directory { ownership, path })
 }
 
 fn error_on_circular_module<'a>(
@@ -153,12 +144,11 @@ fn error_on_circular_module<'a>(
 pub fn push_directory(
     id: Ident,
     attrs: &[Attribute],
-    dir_ownership: &mut DirectoryOwnership,
-    dir_path: &mut PathBuf,
-) {
-    if let Some(path) = attr::first_attr_value_str_by_name(attrs, sym::path) {
-        dir_path.push(&*path.as_str());
-        *dir_ownership = DirectoryOwnership::Owned { relative: None };
+    Directory { mut ownership, mut path }: Directory,
+) -> Directory {
+    if let Some(filename) = attr::first_attr_value_str_by_name(attrs, sym::path) {
+        path.push(&*filename.as_str());
+        ownership = DirectoryOwnership::Owned { relative: None };
     } else {
         // We have to push on the current module name in the case of relative
         // paths in order to ensure that any additional module paths from inline
@@ -166,14 +156,15 @@ pub fn push_directory(
         //
         // For example, a `mod z { ... }` inside `x/y.rs` should set the current
         // directory path to `/x/y/z`, not `/x/z` with a relative offset of `y`.
-        if let DirectoryOwnership::Owned { relative } = dir_ownership {
+        if let DirectoryOwnership::Owned { relative } = &mut ownership {
             if let Some(ident) = relative.take() {
                 // Remove the relative offset.
-                dir_path.push(&*ident.as_str());
+                path.push(&*ident.as_str());
             }
         }
-        dir_path.push(&*id.as_str());
+        path.push(&*id.as_str());
     }
+    Directory { ownership, path }
 }
 
 fn submod_path<'a>(
diff --git a/src/librustc_parse/parser/stmt.rs b/src/librustc_parse/parser/stmt.rs
index 4359823be08..d40597d8fcb 100644
--- a/src/librustc_parse/parser/stmt.rs
+++ b/src/librustc_parse/parser/stmt.rs
@@ -5,7 +5,6 @@ use super::pat::GateOr;
 use super::path::PathStyle;
 use super::{BlockMode, Parser, Restrictions, SemiColonMode};
 use crate::maybe_whole;
-use crate::DirectoryOwnership;
 
 use rustc_ast::ast;
 use rustc_ast::ast::{AttrStyle, AttrVec, Attribute, MacCall, MacStmtStyle};
@@ -54,7 +53,7 @@ impl<'a> Parser<'a> {
             // that starts like a path (1 token), but it fact not a path.
             // Also, we avoid stealing syntax from `parse_item_`.
             self.parse_stmt_path_start(lo, attrs)?
-        } else if let Some(item) = self.parse_stmt_item(attrs.clone())? {
+        } else if let Some(item) = self.parse_item_common(attrs.clone(), false, true, |_| true)? {
             // FIXME: Bad copy of attrs
             self.mk_stmt(lo.to(item.span), StmtKind::Item(P(item)))
         } else if self.eat(&token::Semi) {
@@ -72,13 +71,6 @@ impl<'a> Parser<'a> {
         Ok(Some(stmt))
     }
 
-    fn parse_stmt_item(&mut self, attrs: Vec<Attribute>) -> PResult<'a, Option<ast::Item>> {
-        let old = mem::replace(&mut self.directory.ownership, DirectoryOwnership::UnownedViaBlock);
-        let item = self.parse_item_common(attrs, false, true, |_| true)?;
-        self.directory.ownership = old;
-        Ok(item)
-    }
-
     fn parse_stmt_path_start(&mut self, lo: Span, attrs: Vec<Attribute>) -> PResult<'a, Stmt> {
         let path = self.parse_path(PathStyle::Expr)?;