Rollup merge of #82415 - petrochenkov:modin3, r=davidtwco

expand: Refactor module loading

This is an accompanying PR to https://github.com/rust-lang/rust/pull/82399, but they can be landed independently.
See individual commits for more details.

Anyone should be able to review this equally well because all people actually familiar with this code left the project.
This commit is contained in:
Dylan DPC 2021-03-08 13:13:19 +01:00 committed by GitHub
commit 4a4e3e667d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 298 additions and 295 deletions

View File

@ -4,7 +4,7 @@ use rustc_ast::token;
use rustc_ast::tokenstream::TokenStream;
use rustc_ast_pretty::pprust;
use rustc_expand::base::{self, *};
use rustc_expand::module::DirectoryOwnership;
use rustc_expand::module::DirOwnership;
use rustc_parse::parser::{ForceCollect, Parser};
use rustc_parse::{self, new_parser_from_file};
use rustc_session::lint::builtin::INCOMPLETE_INCLUDE;
@ -101,7 +101,7 @@ pub fn expand_include<'cx>(
None => return DummyResult::any(sp),
};
// The file will be added to the code map by the parser
let mut file = match cx.resolve_path(file, sp) {
let file = match cx.resolve_path(file, sp) {
Ok(f) => f,
Err(mut err) => {
err.emit();
@ -114,10 +114,9 @@ pub fn expand_include<'cx>(
// then the path of `bar.rs` should be relative to the directory of `file`.
// See https://github.com/rust-lang/rust/pull/69838/files#r395217057 for a discussion.
// `MacroExpander::fully_expand_fragment` later restores, so "stack discipline" is maintained.
file.pop();
cx.current_expansion.directory_ownership = DirectoryOwnership::Owned { relative: None };
let mod_path = cx.current_expansion.module.mod_path.clone();
cx.current_expansion.module = Rc::new(ModuleData { mod_path, directory: file });
let dir_path = file.parent().unwrap_or(&file).to_owned();
cx.current_expansion.module = Rc::new(cx.current_expansion.module.with_dir_path(dir_path));
cx.current_expansion.dir_ownership = DirOwnership::Owned { relative: None };
struct ExpandResult<'a> {
p: Parser<'a>,

View File

@ -1,11 +1,11 @@
use crate::expand::{self, AstFragment, Invocation};
use crate::module::DirectoryOwnership;
use crate::module::DirOwnership;
use rustc_ast::ptr::P;
use rustc_ast::token::{self, Nonterminal};
use rustc_ast::tokenstream::{CanSynthesizeMissingTokens, LazyTokenStream, TokenStream};
use rustc_ast::visit::{AssocCtxt, Visitor};
use rustc_ast::{self as ast, AstLike, Attribute, NodeId, PatKind};
use rustc_ast::{self as ast, AstLike, Attribute, Item, NodeId, PatKind};
use rustc_attr::{self as attr, Deprecation, Stability};
use rustc_data_structures::fx::FxHashMap;
use rustc_data_structures::sync::{self, Lrc};
@ -900,10 +900,26 @@ pub trait ResolverExpand {
fn cfg_accessible(&mut self, expn_id: ExpnId, path: &ast::Path) -> Result<bool, Indeterminate>;
}
#[derive(Clone)]
#[derive(Clone, Default)]
pub struct ModuleData {
/// Path to the module starting from the crate name, like `my_crate::foo::bar`.
pub mod_path: Vec<Ident>,
pub directory: PathBuf,
/// Stack of paths to files loaded by out-of-line module items,
/// used to detect and report recursive module inclusions.
pub file_path_stack: Vec<PathBuf>,
/// Directory to search child module files in,
/// often (but not necessarily) the parent of the top file path on the `file_path_stack`.
pub dir_path: PathBuf,
}
impl ModuleData {
pub fn with_dir_path(&self, dir_path: PathBuf) -> ModuleData {
ModuleData {
mod_path: self.mod_path.clone(),
file_path_stack: self.file_path_stack.clone(),
dir_path,
}
}
}
#[derive(Clone)]
@ -911,10 +927,13 @@ pub struct ExpansionData {
pub id: ExpnId,
pub depth: usize,
pub module: Rc<ModuleData>,
pub directory_ownership: DirectoryOwnership,
pub dir_ownership: DirOwnership,
pub prior_type_ascription: Option<(Span, bool)>,
}
type OnExternModLoaded<'a> =
Option<&'a dyn Fn(Ident, Vec<Attribute>, Vec<P<Item>>, Span) -> (Vec<Attribute>, Vec<P<Item>>)>;
/// One of these is made during expansion and incrementally updated as we go;
/// when a macro expansion occurs, the resulting nodes have the `backtrace()
/// -> expn_data` of their expansion context stored into their span.
@ -932,7 +951,7 @@ pub struct ExtCtxt<'a> {
/// Called directly after having parsed an external `mod foo;` in expansion.
///
/// `Ident` is the module name.
pub(super) extern_mod_loaded: Option<&'a dyn Fn(&ast::Crate, Ident)>,
pub(super) extern_mod_loaded: OnExternModLoaded<'a>,
}
impl<'a> ExtCtxt<'a> {
@ -940,7 +959,7 @@ impl<'a> ExtCtxt<'a> {
sess: &'a Session,
ecfg: expand::ExpansionConfig<'a>,
resolver: &'a mut dyn ResolverExpand,
extern_mod_loaded: Option<&'a dyn Fn(&ast::Crate, Ident)>,
extern_mod_loaded: OnExternModLoaded<'a>,
) -> ExtCtxt<'a> {
ExtCtxt {
sess,
@ -952,8 +971,8 @@ impl<'a> ExtCtxt<'a> {
current_expansion: ExpansionData {
id: ExpnId::root(),
depth: 0,
module: Rc::new(ModuleData { mod_path: Vec::new(), directory: PathBuf::new() }),
directory_ownership: DirectoryOwnership::Owned { relative: None },
module: Default::default(),
dir_ownership: DirOwnership::Owned { relative: None },
prior_type_ascription: None,
},
force_mode: false,

View File

@ -3,7 +3,7 @@ use crate::config::StripUnconfigured;
use crate::configure;
use crate::hygiene::SyntaxContext;
use crate::mbe::macro_rules::annotate_err_with_kind;
use crate::module::{parse_external_mod, push_directory, Directory, DirectoryOwnership};
use crate::module::{mod_dir_path, parse_external_mod, DirOwnership, ParsedExternalMod};
use crate::placeholders::{placeholder, PlaceholderExpander};
use rustc_ast as ast;
@ -355,16 +355,17 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
// FIXME: Avoid visiting the crate as a `Mod` item,
// make crate a first class expansion target instead.
pub fn expand_crate(&mut self, mut krate: ast::Crate) -> ast::Crate {
let mut module = ModuleData {
mod_path: vec![Ident::from_str(&self.cx.ecfg.crate_name)],
directory: match self.cx.source_map().span_to_unmapped_path(krate.span) {
FileName::Real(name) => name.into_local_path(),
other => PathBuf::from(other.to_string()),
},
let file_path = match self.cx.source_map().span_to_unmapped_path(krate.span) {
FileName::Real(name) => name.into_local_path(),
other => PathBuf::from(other.to_string()),
};
module.directory.pop();
self.cx.root_path = module.directory.clone();
self.cx.current_expansion.module = Rc::new(module);
let dir_path = file_path.parent().unwrap_or(&file_path).to_owned();
self.cx.root_path = dir_path.clone();
self.cx.current_expansion.module = Rc::new(ModuleData {
mod_path: vec![Ident::from_str(&self.cx.ecfg.crate_name)],
file_path_stack: vec![file_path],
dir_path,
});
let krate_item = AstFragment::Items(smallvec![P(ast::Item {
attrs: krate.attrs,
@ -1245,10 +1246,12 @@ impl<'a, 'b> MutVisitor for InvocationCollector<'a, 'b> {
}
fn visit_block(&mut self, block: &mut P<Block>) {
let old_directory_ownership = self.cx.current_expansion.directory_ownership;
self.cx.current_expansion.directory_ownership = DirectoryOwnership::UnownedViaBlock;
let orig_dir_ownership = mem::replace(
&mut self.cx.current_expansion.dir_ownership,
DirOwnership::UnownedViaBlock,
);
noop_visit_block(block, self);
self.cx.current_expansion.directory_ownership = old_directory_ownership;
self.cx.current_expansion.dir_ownership = orig_dir_ownership;
}
fn flat_map_item(&mut self, item: P<ast::Item>) -> SmallVec<[P<ast::Item>; 1]> {
@ -1276,63 +1279,76 @@ impl<'a, 'b> MutVisitor for InvocationCollector<'a, 'b> {
})
}
ast::ItemKind::Mod(_, ref mut mod_kind) if ident != Ident::invalid() => {
let sess = &self.cx.sess.parse_sess;
let orig_ownership = self.cx.current_expansion.directory_ownership;
let mut module = (*self.cx.current_expansion.module).clone();
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 } = match mod_kind {
ModKind::Loaded(_, Inline::Yes, _) => {
let (file_path, dir_path, dir_ownership) = match mod_kind {
ModKind::Loaded(_, inline, _) => {
// Inline `mod foo { ... }`, but we still need to push directories.
assert!(
*inline == Inline::Yes,
"`mod` item is loaded from a file for the second time"
);
let (dir_path, dir_ownership) = mod_dir_path(
&self.cx.sess,
ident,
&attrs,
&self.cx.current_expansion.module,
self.cx.current_expansion.dir_ownership,
);
item.attrs = attrs;
push_directory(&self.cx.sess, ident, &item.attrs, dir)
}
ModKind::Loaded(_, Inline::No, _) => {
panic!("`mod` item is loaded from a file for the second time")
(None, dir_path, dir_ownership)
}
ModKind::Unloaded => {
// We have an outline `mod foo;` so we need to parse the file.
let (items, inner_span, dir) =
parse_external_mod(&self.cx.sess, ident, span, dir, &mut attrs, pushed);
let old_attrs_len = attrs.len();
let ParsedExternalMod {
mut items,
inner_span,
file_path,
dir_path,
dir_ownership,
} = parse_external_mod(
&self.cx.sess,
ident,
span,
&self.cx.current_expansion.module,
self.cx.current_expansion.dir_ownership,
&mut attrs,
);
let krate =
ast::Crate { attrs, items, span: inner_span, proc_macros: vec![] };
if let Some(extern_mod_loaded) = self.cx.extern_mod_loaded {
extern_mod_loaded(&krate, ident);
(attrs, items) = extern_mod_loaded(ident, attrs, items, inner_span);
}
*mod_kind = ModKind::Loaded(krate.items, Inline::No, inner_span);
item.attrs = krate.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();
}
};
dir
*mod_kind = ModKind::Loaded(items, Inline::No, inner_span);
item.attrs = attrs;
if item.attrs.len() > old_attrs_len {
// If we loaded an out-of-line module and added some inner attributes,
// then we need to re-configure it.
// FIXME: Attributes also need to be recollected
// for resolution and expansion.
item = configure!(self, item);
}
(Some(file_path), dir_path, dir_ownership)
}
};
// Set the module info before we flat map.
self.cx.current_expansion.directory_ownership = ownership;
module.directory = path;
let mut module = self.cx.current_expansion.module.with_dir_path(dir_path);
module.mod_path.push(ident);
if let Some(file_path) = file_path {
module.file_path_stack.push(file_path);
}
let orig_module =
mem::replace(&mut self.cx.current_expansion.module, Rc::new(module));
let orig_dir_ownership =
mem::replace(&mut self.cx.current_expansion.dir_ownership, dir_ownership);
let result = noop_flat_map_item(item, self);
// Restore the module info.
self.cx.current_expansion.dir_ownership = orig_dir_ownership;
self.cx.current_expansion.module = orig_module;
self.cx.current_expansion.directory_ownership = orig_ownership;
if *pushed {
sess.included_mod_stack.borrow_mut().pop();
}
result
}
_ => {

View File

@ -1,5 +1,7 @@
#![feature(bool_to_option)]
#![feature(crate_visibility_modifier)]
#![feature(decl_macro)]
#![feature(destructuring_assignment)]
#![feature(or_patterns)]
#![feature(proc_macro_diagnostic)]
#![feature(proc_macro_internals)]

View File

@ -1,232 +1,155 @@
use crate::base::ModuleData;
use rustc_ast::ptr::P;
use rustc_ast::{token, Attribute, Item};
use rustc_errors::{struct_span_err, PResult};
use rustc_errors::{struct_span_err, DiagnosticBuilder};
use rustc_parse::new_parser_from_file;
use rustc_session::parse::ParseSess;
use rustc_session::Session;
use rustc_span::source_map::{FileName, Span};
use rustc_span::symbol::{sym, Ident};
use rustc_span::Span;
use std::path::{self, Path, PathBuf};
#[derive(Clone)]
pub struct Directory {
pub path: PathBuf,
pub ownership: DirectoryOwnership,
}
#[derive(Copy, Clone)]
pub enum DirectoryOwnership {
pub enum DirOwnership {
Owned {
// None if `mod.rs`, `Some("foo")` if we're in `foo.rs`.
relative: Option<Ident>,
},
UnownedViaBlock,
UnownedViaMod,
}
/// Information about the path to a module.
// Public for rustfmt usage.
pub struct ModulePath<'a> {
name: String,
path_exists: bool,
pub result: PResult<'a, ModulePathSuccess>,
}
// Public for rustfmt usage.
pub struct ModulePathSuccess {
pub path: PathBuf,
pub ownership: DirectoryOwnership,
pub file_path: PathBuf,
pub dir_ownership: DirOwnership,
}
crate struct ParsedExternalMod {
pub items: Vec<P<Item>>,
pub inner_span: Span,
pub file_path: PathBuf,
pub dir_path: PathBuf,
pub dir_ownership: DirOwnership,
}
pub enum ModError<'a> {
CircularInclusion(Vec<PathBuf>),
ModInBlock(Option<Ident>),
FileNotFound(Ident, PathBuf),
MultipleCandidates(Ident, String, String),
ParserError(DiagnosticBuilder<'a>),
}
crate fn parse_external_mod(
sess: &Session,
id: Ident,
ident: Ident,
span: Span, // The span to blame on errors.
Directory { mut ownership, path }: Directory,
module: &ModuleData,
mut dir_ownership: DirOwnership,
attrs: &mut Vec<Attribute>,
pop_mod_stack: &mut bool,
) -> (Vec<P<Item>>, Span, Directory) {
) -> ParsedExternalMod {
// We bail on the first error, but that error does not cause a fatal error... (1)
let result: PResult<'_, _> = try {
let result: Result<_, ModError<'_>> = try {
// Extract the file path and the new ownership.
let mp = submod_path(sess, id, span, &attrs, ownership, &path)?;
ownership = mp.ownership;
let mp = mod_file_path(sess, ident, &attrs, &module.dir_path, dir_ownership)?;
dir_ownership = mp.dir_ownership;
// Ensure file paths are acyclic.
let mut included_mod_stack = sess.parse_sess.included_mod_stack.borrow_mut();
error_on_circular_module(&sess.parse_sess, 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);
if let Some(pos) = module.file_path_stack.iter().position(|p| p == &mp.file_path) {
Err(ModError::CircularInclusion(module.file_path_stack[pos..].to_vec()))?;
}
// Actually parse the external file as a module.
let mut parser = new_parser_from_file(&sess.parse_sess, &mp.path, Some(span));
let (mut inner_attrs, items, inner_span) = parser.parse_mod(&token::Eof)?;
let mut parser = new_parser_from_file(&sess.parse_sess, &mp.file_path, Some(span));
let (mut inner_attrs, items, inner_span) =
parser.parse_mod(&token::Eof).map_err(|err| ModError::ParserError(err))?;
attrs.append(&mut inner_attrs);
(items, inner_span)
(items, inner_span, mp.file_path)
};
// (1) ...instead, we return a dummy module.
let (items, inner_span) = result.map_err(|mut err| err.emit()).unwrap_or_default();
let (items, inner_span, file_path) =
result.map_err(|err| err.report(sess, span)).unwrap_or_default();
// Extract the directory path for submodules of the module.
let path = sess.source_map().span_to_unmapped_path(inner_span);
let mut path = match path {
FileName::Real(name) => name.into_local_path(),
other => PathBuf::from(other.to_string()),
};
path.pop();
// Extract the directory path for submodules of the module.
let dir_path = file_path.parent().unwrap_or(&file_path).to_owned();
(items, inner_span, Directory { ownership, path })
ParsedExternalMod { items, inner_span, file_path, dir_path, dir_ownership }
}
fn error_on_circular_module<'a>(
sess: &'a ParseSess,
span: Span,
path: &Path,
included_mod_stack: &[PathBuf],
) -> PResult<'a, ()> {
if let Some(i) = included_mod_stack.iter().position(|p| *p == path) {
let mut err = String::from("circular modules: ");
for p in &included_mod_stack[i..] {
err.push_str(&p.to_string_lossy());
err.push_str(" -> ");
}
err.push_str(&path.to_string_lossy());
return Err(sess.span_diagnostic.struct_span_err(span, &err[..]));
}
Ok(())
}
crate fn push_directory(
crate fn mod_dir_path(
sess: &Session,
id: Ident,
ident: Ident,
attrs: &[Attribute],
Directory { mut ownership, mut path }: Directory,
) -> Directory {
if let Some(filename) = sess.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
// `mod x { ... }` come after the relative extension.
//
// 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 } = &mut ownership {
if let Some(ident) = relative.take() {
// Remove the relative offset.
path.push(&*ident.as_str());
}
}
path.push(&*id.as_str());
module: &ModuleData,
mut dir_ownership: DirOwnership,
) -> (PathBuf, DirOwnership) {
if let Some(file_path) = mod_file_path_from_attr(sess, attrs, &module.dir_path) {
// For inline modules file path from `#[path]` is actually the directory path
// for historical reasons, so we don't pop the last segment here.
return (file_path, DirOwnership::Owned { relative: None });
}
Directory { ownership, path }
// 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
// `mod x { ... }` come after the relative extension.
//
// 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`.
let mut dir_path = module.dir_path.clone();
if let DirOwnership::Owned { relative } = &mut dir_ownership {
if let Some(ident) = relative.take() {
// Remove the relative offset.
dir_path.push(&*ident.as_str());
}
}
dir_path.push(&*ident.as_str());
(dir_path, dir_ownership)
}
fn submod_path<'a>(
fn mod_file_path<'a>(
sess: &'a Session,
id: Ident,
span: Span,
ident: Ident,
attrs: &[Attribute],
ownership: DirectoryOwnership,
dir_path: &Path,
) -> PResult<'a, ModulePathSuccess> {
if let Some(path) = submod_path_from_attr(sess, attrs, dir_path) {
let ownership = match path.file_name().and_then(|s| s.to_str()) {
// All `#[path]` files are treated as though they are a `mod.rs` file.
// This means that `mod foo;` declarations inside `#[path]`-included
// files are siblings,
//
// Note that this will produce weirdness when a file named `foo.rs` is
// `#[path]` included and contains a `mod foo;` declaration.
// If you encounter this, it's your own darn fault :P
Some(_) => DirectoryOwnership::Owned { relative: None },
_ => DirectoryOwnership::UnownedViaMod,
};
return Ok(ModulePathSuccess { ownership, path });
dir_ownership: DirOwnership,
) -> Result<ModulePathSuccess, ModError<'a>> {
if let Some(file_path) = mod_file_path_from_attr(sess, attrs, dir_path) {
// All `#[path]` files are treated as though they are a `mod.rs` file.
// This means that `mod foo;` declarations inside `#[path]`-included
// files are siblings,
//
// Note that this will produce weirdness when a file named `foo.rs` is
// `#[path]` included and contains a `mod foo;` declaration.
// If you encounter this, it's your own darn fault :P
let dir_ownership = DirOwnership::Owned { relative: None };
return Ok(ModulePathSuccess { file_path, dir_ownership });
}
let relative = match ownership {
DirectoryOwnership::Owned { relative } => relative,
DirectoryOwnership::UnownedViaBlock | DirectoryOwnership::UnownedViaMod => None,
let relative = match dir_ownership {
DirOwnership::Owned { relative } => relative,
DirOwnership::UnownedViaBlock => None,
};
let ModulePath { path_exists, name, result } =
default_submod_path(&sess.parse_sess, id, span, relative, dir_path);
match ownership {
DirectoryOwnership::Owned { .. } => Ok(result?),
DirectoryOwnership::UnownedViaBlock => {
let _ = result.map_err(|mut err| err.cancel());
error_decl_mod_in_block(&sess.parse_sess, span, path_exists, &name)
}
DirectoryOwnership::UnownedViaMod => {
let _ = result.map_err(|mut err| err.cancel());
error_cannot_declare_mod_here(&sess.parse_sess, span, path_exists, &name)
}
let result = default_submod_path(&sess.parse_sess, ident, relative, dir_path);
match dir_ownership {
DirOwnership::Owned { .. } => result,
DirOwnership::UnownedViaBlock => Err(ModError::ModInBlock(match result {
Ok(_) | Err(ModError::MultipleCandidates(..)) => Some(ident),
_ => None,
})),
}
}
fn error_decl_mod_in_block<'a, T>(
sess: &'a ParseSess,
span: Span,
path_exists: bool,
name: &str,
) -> PResult<'a, T> {
let msg = "Cannot declare a non-inline module inside a block unless it has a path attribute";
let mut err = sess.span_diagnostic.struct_span_err(span, msg);
if path_exists {
let msg = format!("Maybe `use` the module `{}` instead of redeclaring it", name);
err.span_note(span, &msg);
}
Err(err)
}
fn error_cannot_declare_mod_here<'a, T>(
sess: &'a ParseSess,
span: Span,
path_exists: bool,
name: &str,
) -> PResult<'a, T> {
let mut err =
sess.span_diagnostic.struct_span_err(span, "cannot declare a new module at this location");
if !span.is_dummy() {
if let FileName::Real(src_name) = sess.source_map().span_to_filename(span) {
let src_path = src_name.into_local_path();
if let Some(stem) = src_path.file_stem() {
let mut dest_path = src_path.clone();
dest_path.set_file_name(stem);
dest_path.push("mod.rs");
err.span_note(
span,
&format!(
"maybe move this module `{}` to its own directory via `{}`",
src_path.display(),
dest_path.display()
),
);
}
}
}
if path_exists {
err.span_note(
span,
&format!("... or maybe `use` the module `{}` instead of possibly redeclaring it", name),
);
}
Err(err)
}
/// Derive a submodule path from the first found `#[path = "path_string"]`.
/// The provided `dir_path` is joined with the `path_string`.
pub(super) fn submod_path_from_attr(
fn mod_file_path_from_attr(
sess: &Session,
attrs: &[Attribute],
dir_path: &Path,
) -> Option<PathBuf> {
// Extract path string from first `#[path = "path_string"]` attribute.
let path_string = sess.first_attr_value_str_by_name(attrs, sym::path)?;
let path_string = path_string.as_str();
let path_string = sess.first_attr_value_str_by_name(attrs, sym::path)?.as_str();
// On windows, the base path might have the form
// `\\?\foo\bar` in which case it does not tolerate
@ -242,15 +165,14 @@ pub(super) fn submod_path_from_attr(
// Public for rustfmt usage.
pub fn default_submod_path<'a>(
sess: &'a ParseSess,
id: Ident,
span: Span,
ident: Ident,
relative: Option<Ident>,
dir_path: &Path,
) -> ModulePath<'a> {
) -> Result<ModulePathSuccess, ModError<'a>> {
// If we're in a foo.rs file instead of a mod.rs file,
// we need to look for submodules in
// `./foo/<id>.rs` and `./foo/<id>/mod.rs` rather than
// `./<id>.rs` and `./<id>/mod.rs`.
// `./foo/<ident>.rs` and `./foo/<ident>/mod.rs` rather than
// `./<ident>.rs` and `./<ident>/mod.rs`.
let relative_prefix_string;
let relative_prefix = if let Some(ident) = relative {
relative_prefix_string = format!("{}{}", ident.name, path::MAIN_SEPARATOR);
@ -259,7 +181,7 @@ pub fn default_submod_path<'a>(
""
};
let mod_name = id.name.to_string();
let mod_name = ident.name.to_string();
let default_path_str = format!("{}{}.rs", relative_prefix, mod_name);
let secondary_path_str =
format!("{}{}{}mod.rs", relative_prefix, mod_name, path::MAIN_SEPARATOR);
@ -268,44 +190,74 @@ pub fn default_submod_path<'a>(
let default_exists = sess.source_map().file_exists(&default_path);
let secondary_exists = sess.source_map().file_exists(&secondary_path);
let result = match (default_exists, secondary_exists) {
match (default_exists, secondary_exists) {
(true, false) => Ok(ModulePathSuccess {
path: default_path,
ownership: DirectoryOwnership::Owned { relative: Some(id) },
file_path: default_path,
dir_ownership: DirOwnership::Owned { relative: Some(ident) },
}),
(false, true) => Ok(ModulePathSuccess {
path: secondary_path,
ownership: DirectoryOwnership::Owned { relative: None },
file_path: secondary_path,
dir_ownership: DirOwnership::Owned { relative: None },
}),
(false, false) => {
let mut err = struct_span_err!(
sess.span_diagnostic,
span,
E0583,
"file not found for module `{}`",
mod_name,
);
err.help(&format!(
"to create the module `{}`, create file \"{}\"",
mod_name,
default_path.display(),
));
Err(err)
}
(false, false) => Err(ModError::FileNotFound(ident, default_path)),
(true, true) => {
let mut err = struct_span_err!(
sess.span_diagnostic,
span,
E0761,
"file for module `{}` found at both {} and {}",
mod_name,
default_path_str,
secondary_path_str,
);
err.help("delete or rename one of them to remove the ambiguity");
Err(err)
Err(ModError::MultipleCandidates(ident, default_path_str, secondary_path_str))
}
};
ModulePath { name: mod_name, path_exists: default_exists || secondary_exists, result }
}
}
impl ModError<'_> {
fn report(self, sess: &Session, span: Span) {
let diag = &sess.parse_sess.span_diagnostic;
match self {
ModError::CircularInclusion(file_paths) => {
let mut msg = String::from("circular modules: ");
for file_path in &file_paths {
msg.push_str(&file_path.display().to_string());
msg.push_str(" -> ");
}
msg.push_str(&file_paths[0].display().to_string());
diag.struct_span_err(span, &msg)
}
ModError::ModInBlock(ident) => {
let msg = "cannot declare a non-inline module inside a block unless it has a path attribute";
let mut err = diag.struct_span_err(span, msg);
if let Some(ident) = ident {
let note =
format!("maybe `use` the module `{}` instead of redeclaring it", ident);
err.span_note(span, &note);
}
err
}
ModError::FileNotFound(ident, default_path) => {
let mut err = struct_span_err!(
diag,
span,
E0583,
"file not found for module `{}`",
ident,
);
err.help(&format!(
"to create the module `{}`, create file \"{}\"",
ident,
default_path.display(),
));
err
}
ModError::MultipleCandidates(ident, default_path_short, secondary_path_short) => {
let mut err = struct_span_err!(
diag,
span,
E0761,
"file for module `{}` found at both {} and {}",
ident,
default_path_short,
secondary_path_short,
);
err.help("delete or rename one of them to remove the ambiguity");
err
}
ModError::ParserError(err) => err,
}.emit()
}
}

View File

@ -302,8 +302,10 @@ fn configure_and_expand_inner<'a>(
..rustc_expand::expand::ExpansionConfig::default(crate_name.to_string())
};
let extern_mod_loaded = |k: &ast::Crate, ident: Ident| {
pre_expansion_lint(sess, lint_store, k, &*ident.name.as_str())
let extern_mod_loaded = |ident: Ident, attrs, items, span| {
let krate = ast::Crate { attrs, items, span, proc_macros: vec![] };
pre_expansion_lint(sess, lint_store, &krate, &ident.name.as_str());
(krate.attrs, krate.items)
};
let mut ecx = ExtCtxt::new(&sess, cfg, &mut resolver, Some(&extern_mod_loaded));

View File

@ -13,7 +13,6 @@ use rustc_span::hygiene::ExpnId;
use rustc_span::source_map::{FilePathMapping, SourceMap};
use rustc_span::{MultiSpan, Span, Symbol};
use std::path::PathBuf;
use std::str;
/// The set of keys (and, optionally, values) that define the compilation
@ -122,8 +121,6 @@ pub struct ParseSess {
pub missing_fragment_specifiers: Lock<FxHashMap<Span, NodeId>>,
/// Places where raw identifiers were used. This is used for feature-gating raw identifiers.
pub raw_identifier_spans: Lock<Vec<Span>>,
/// Used to determine and report recursive module inclusions.
pub included_mod_stack: Lock<Vec<PathBuf>>,
source_map: Lrc<SourceMap>,
pub buffered_lints: Lock<Vec<BufferedEarlyLint>>,
/// Contains the spans of block expressions that could have been incomplete based on the
@ -157,7 +154,6 @@ impl ParseSess {
edition: ExpnId::root().expn_data().edition,
missing_fragment_specifiers: Default::default(),
raw_identifier_spans: Lock::new(Vec::new()),
included_mod_stack: Lock::new(vec![]),
source_map,
buffered_lints: Lock::new(vec![]),
ambiguous_block_expr_parse: Lock::new(FxHashMap::default()),

View File

@ -2,7 +2,7 @@
macro_rules! mod_decl {
($i:ident) => {
mod $i; //~ ERROR Cannot declare a non-inline module inside a block
mod $i; //~ ERROR cannot declare a non-inline module inside a block
};
}

View File

@ -1,4 +1,4 @@
error: Cannot declare a non-inline module inside a block unless it has a path attribute
error: cannot declare a non-inline module inside a block unless it has a path attribute
--> $DIR/macro-expanded-mod.rs:5:9
|
LL | mod $i;

View File

@ -1,5 +1,5 @@
// Test that non-inline modules are not allowed inside blocks.
fn main() {
mod foo; //~ ERROR Cannot declare a non-inline module inside a block
mod foo; //~ ERROR cannot declare a non-inline module inside a block
}

View File

@ -1,4 +1,4 @@
error: Cannot declare a non-inline module inside a block unless it has a path attribute
error: cannot declare a non-inline module inside a block unless it has a path attribute
--> $DIR/non-inline-mod-restriction.rs:4:5
|
LL | mod foo;

View File

@ -0,0 +1,7 @@
// normalize-stderr-test: "\.:.*\(" -> ".: $$ACCESS_DENIED_MSG ("
// normalize-stderr-test: "os error \d+" -> "os error $$ACCESS_DENIED_CODE"
#[path = "."]
mod m; //~ ERROR couldn't read
fn main() {}

View File

@ -0,0 +1,8 @@
error: couldn't read $DIR/.: $ACCESS_DENIED_MSG (os error $ACCESS_DENIED_CODE)
--> $DIR/path-no-file-name.rs:5:1
|
LL | mod m;
| ^^^^^^
error: aborting due to previous error

View File

@ -1,10 +1,12 @@
// error-pattern: circular modules
#[path = "circular_modules_hello.rs"]
mod circular_modules_hello; //~ ERROR: circular modules
mod circular_modules_hello;
pub fn hi_str() -> String {
"Hi!".to_string()
}
fn main() {
circular_modules_hello::say_hello(); //~ ERROR cannot find function `say_hello` in module
circular_modules_hello::say_hello();
}

View File

@ -1,18 +1,18 @@
error: circular modules: $DIR/circular_modules_hello.rs -> $DIR/circular_modules_main.rs -> $DIR/circular_modules_hello.rs
--> $DIR/circular_modules_main.rs:2:1
error: circular modules: $DIR/circular_modules_main.rs -> $DIR/circular_modules_hello.rs -> $DIR/circular_modules_main.rs
--> $DIR/circular_modules_hello.rs:4:1
|
LL | mod circular_modules_hello;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
LL | mod circular_modules_main;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
error[E0425]: cannot find function `say_hello` in module `circular_modules_hello`
--> $DIR/circular_modules_main.rs:9:29
error[E0425]: cannot find function `hi_str` in module `circular_modules_main`
--> $DIR/circular_modules_hello.rs:7:43
|
LL | circular_modules_hello::say_hello();
| ^^^^^^^^^ not found in `circular_modules_hello`
LL | println!("{}", circular_modules_main::hi_str());
| ^^^^^^ not found in `circular_modules_main`
|
help: consider importing this function
|
LL | use circular_modules_hello::say_hello;
LL | use hi_str;
|
error: aborting due to 2 previous errors