Rollup merge of #69838 - Centril:expand-module, r=petrochenkov

Expansion-driven outline module parsing

After this PR, the parser will not do any conditional compilation or loading of external module files when `mod foo;` is encountered. Instead, the parser only leaves `mod foo;` in place in the AST, with no items filled in. Expansion later kicks in and will load the actual files and do the parsing. This entails that the following is now valid:

```rust
#[cfg(FALSE)]
mod foo {
    mod bar {
        mod baz; // `foo/bar/baz.rs` doesn't exist, but no error!
    }
}
```

Fixes https://github.com/rust-lang/rust/issues/64197.

r? @petrochenkov
This commit is contained in:
Mazdak Farrokhzad 2020-03-18 18:03:38 +01:00 committed by GitHub
commit 23b79d83f2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
47 changed files with 601 additions and 593 deletions

View File

@ -6,8 +6,6 @@
//! Emscripten's runtime always implements those APIs and does not
//! implement libunwind.
#![allow(private_no_mangle_fns)]
use alloc::boxed::Box;
use core::any::Any;
use core::mem;

View File

@ -36,8 +36,6 @@
//! Once stack has been unwound down to the handler frame level, unwinding stops
//! and the last personality routine transfers control to the catch block.
#![allow(private_no_mangle_fns)]
use alloc::boxed::Box;
use core::any::Any;

View File

@ -45,7 +45,6 @@
//! [llvm]: http://llvm.org/docs/ExceptionHandling.html#background-on-windows-exceptions
#![allow(nonstandard_style)]
#![allow(private_no_mangle_fns)]
use alloc::boxed::Box;
use core::any::Any;

View File

@ -2153,7 +2153,7 @@ impl FnRetTy {
/// Module declaration.
///
/// E.g., `mod foo;` or `mod foo { .. }`.
#[derive(Clone, RustcEncodable, RustcDecodable, Debug)]
#[derive(Clone, RustcEncodable, RustcDecodable, Debug, Default)]
pub struct Mod {
/// A span from the first token past `{` to the last token until `}`.
/// For `mod foo;`, the inner span ranges from the first token

View File

@ -59,7 +59,7 @@ pub fn inject(
handler: &rustc_errors::Handler,
) -> ast::Crate {
let ecfg = ExpansionConfig::default("proc_macro".to_string());
let mut cx = ExtCtxt::new(sess, ecfg, resolver);
let mut cx = ExtCtxt::new(sess, ecfg, resolver, None);
let mut collect = CollectProcMacros {
macros: Vec::new(),

View File

@ -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>,

View File

@ -39,7 +39,7 @@ pub fn inject(
let call_site = DUMMY_SP.with_call_site_ctxt(expn_id);
let ecfg = ExpansionConfig::default("std_lib_injection".to_string());
let cx = ExtCtxt::new(sess, ecfg, resolver);
let cx = ExtCtxt::new(sess, ecfg, resolver, None);
// .rev() to preserve ordering above in combination with insert(0, ...)
for &name in names.iter().rev() {

View File

@ -202,7 +202,7 @@ fn generate_test_harness(
let mut econfig = ExpansionConfig::default("test".to_string());
econfig.features = Some(features);
let ext_cx = ExtCtxt::new(sess, econfig, resolver);
let ext_cx = ExtCtxt::new(sess, econfig, resolver, None);
let expn_id = ext_cx.resolver.expansion_for_ast_pass(
DUMMY_SP,

View File

@ -1,4 +1,5 @@
use crate::expand::{self, AstFragment, Invocation};
use crate::module::DirectoryOwnership;
use rustc_ast::ast::{self, Attribute, Name, NodeId, PatKind};
use rustc_ast::mut_visit::{self, MutVisitor};
@ -10,7 +11,7 @@ use rustc_attr::{self as attr, Deprecation, HasAttrs, Stability};
use rustc_data_structures::fx::FxHashMap;
use rustc_data_structures::sync::{self, Lrc};
use rustc_errors::{DiagnosticBuilder, DiagnosticId};
use rustc_parse::{self, parser, DirectoryOwnership, MACRO_ARGUMENTS};
use rustc_parse::{self, parser, MACRO_ARGUMENTS};
use rustc_session::parse::ParseSess;
use rustc_span::edition::Edition;
use rustc_span::hygiene::{AstPass, ExpnData, ExpnId, ExpnKind};
@ -925,6 +926,8 @@ pub struct ExtCtxt<'a> {
pub resolver: &'a mut dyn Resolver,
pub current_expansion: ExpansionData,
pub expansions: FxHashMap<Span, Vec<String>>,
/// Called directly after having parsed an external `mod foo;` in expansion.
pub(super) extern_mod_loaded: Option<&'a dyn Fn(&ast::Crate)>,
}
impl<'a> ExtCtxt<'a> {
@ -932,12 +935,14 @@ impl<'a> ExtCtxt<'a> {
parse_sess: &'a ParseSess,
ecfg: expand::ExpansionConfig<'a>,
resolver: &'a mut dyn Resolver,
extern_mod_loaded: Option<&'a dyn Fn(&ast::Crate)>,
) -> ExtCtxt<'a> {
ExtCtxt {
parse_sess,
ecfg,
root_path: PathBuf::new(),
resolver,
extern_mod_loaded,
root_path: PathBuf::new(),
current_expansion: ExpansionData {
id: ExpnId::root(),
depth: 0,

View File

@ -1,14 +1,5 @@
//! Process the potential `cfg` attributes on a module.
//! Also determine if the module should be included in this configuration.
//!
//! This module properly belongs in rustc_expand, but for now it's tied into
//! parsing, so we leave it here to avoid complicated out-of-line dependencies.
//!
//! A principled solution to this wrong location would be to implement [#64197].
//!
//! [#64197]: https://github.com/rust-lang/rust/issues/64197
//! Conditional compilation stripping.
use crate::{parse_in, validate_attr};
use rustc_ast::ast::{self, AttrItem, Attribute, MetaItem};
use rustc_ast::attr::HasAttrs;
use rustc_ast::mut_visit::*;
@ -21,6 +12,7 @@ use rustc_feature::{Feature, Features, State as FeatureState};
use rustc_feature::{
ACCEPTED_FEATURES, ACTIVE_FEATURES, REMOVED_FEATURES, STABLE_REMOVED_FEATURES,
};
use rustc_parse::{parse_in, validate_attr};
use rustc_session::parse::{feature_err, ParseSess};
use rustc_span::edition::{Edition, ALL_EDITIONS};
use rustc_span::symbol::{sym, Symbol};
@ -538,12 +530,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)
}

View File

@ -1,7 +1,9 @@
use crate::base::*;
use crate::config::StripUnconfigured;
use crate::configure;
use crate::hygiene::{ExpnData, ExpnId, ExpnKind, SyntaxContext};
use crate::mbe::macro_rules::annotate_err_with_kind;
use crate::module::{parse_external_mod, push_directory, Directory, DirectoryOwnership};
use crate::placeholders::{placeholder, PlaceholderExpander};
use crate::proc_macro::collect_derives;
@ -17,10 +19,8 @@ use rustc_ast_pretty::pprust;
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::Parser;
use rustc_parse::validate_attr;
use rustc_parse::DirectoryOwnership;
use rustc_session::lint::builtin::UNUSED_DOC_COMMENTS;
use rustc_session::lint::BuiltinLintDiagnostics;
use rustc_session::parse::{feature_err, ParseSess};
@ -1427,59 +1427,83 @@ 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;
let span = item.span;
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
.collect(
AstFragmentKind::Items,
InvocationKind::Bang { mac, span: item.span },
)
.collect(AstFragmentKind::Items, InvocationKind::Bang { mac, span })
.make_items(),
_ => 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 {
if let Some(path) = attr::first_attr_value_str_by_name(&item.attrs, sym::path) {
self.cx.current_expansion.directory_ownership =
DirectoryOwnership::Owned { relative: None };
module.directory.push(&*path.as_str());
} else {
module.directory.push(&*item.ident.as_str());
}
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,
};
path.pop();
module.directory = path;
self.cx.current_expansion.directory_ownership = directory_ownership;
}
// We have an outline `mod foo;` so we need to parse the file.
let (new_mod, dir) =
parse_external_mod(sess, ident, span, dir, &mut attrs, pushed);
let krate = ast::Crate {
span: new_mod.inner,
module: new_mod,
attrs,
proc_macros: vec![],
};
if let Some(extern_mod_loaded) = self.cx.extern_mod_loaded {
extern_mod_loaded(&krate);
}
*old_mod = krate.module;
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
};
// 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)
}
}
}

View File

@ -1,9 +1,11 @@
#![feature(bool_to_option)]
#![feature(cow_is_borrowed)]
#![feature(crate_visibility_modifier)]
#![feature(decl_macro)]
#![feature(proc_macro_diagnostic)]
#![feature(proc_macro_internals)]
#![feature(proc_macro_span)]
#![feature(try_blocks)]
extern crate proc_macro as pm;
@ -33,8 +35,10 @@ pub use mbe::macro_rules::compile_declarative_macro;
crate use rustc_span::hygiene;
pub mod base;
pub mod build;
#[macro_use]
pub mod config;
pub mod expand;
pub use rustc_parse::config;
pub mod module;
pub mod proc_macro;
crate mod mbe;

View File

@ -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(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(sess: &ParseSess, tts: TokenStream) -> Parser<'_> {
Parser::new(sess, tts, true, rustc_parse::MACRO_ARGUMENTS)
}
/// Generates an appropriate parsing failure message. For EOF, this is "unexpected end...". For

View File

@ -0,0 +1,306 @@
use rustc_ast::ast::{self, Attribute, Ident, Mod};
use rustc_ast::{attr, token};
use rustc_errors::{struct_span_err, PResult};
use rustc_parse::new_sub_parser_from_file;
use rustc_session::parse::ParseSess;
use rustc_span::source_map::{FileName, Span};
use rustc_span::symbol::sym;
use std::path::{self, Path, PathBuf};
#[derive(Clone)]
pub struct Directory {
pub path: PathBuf,
pub ownership: DirectoryOwnership,
}
#[derive(Copy, Clone)]
pub enum DirectoryOwnership {
Owned {
// None if `mod.rs`, `Some("foo")` if we're in `foo.rs`.
relative: Option<ast::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,
}
crate fn parse_external_mod(
sess: &ParseSess,
id: ast::Ident,
span: Span, // The span to blame on errors.
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, span, &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, 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()), 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();
(module, Directory { ownership, path })
}
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(
id: Ident,
attrs: &[Attribute],
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
// `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());
}
Directory { ownership, path }
}
fn submod_path<'a>(
sess: &'a ParseSess,
id: ast::Ident,
span: Span,
attrs: &[Attribute],
ownership: DirectoryOwnership,
dir_path: &Path,
) -> PResult<'a, ModulePathSuccess> {
if let Some(path) = submod_path_from_attr(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 });
}
let relative = match ownership {
DirectoryOwnership::Owned { relative } => relative,
DirectoryOwnership::UnownedViaBlock | DirectoryOwnership::UnownedViaMod => None,
};
let ModulePath { path_exists, name, result } =
default_submod_path(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, span, path_exists, &name)
}
DirectoryOwnership::UnownedViaMod => {
let _ = result.map_err(|mut err| err.cancel());
error_cannot_declare_mod_here(sess, span, path_exists, &name)
}
}
}
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_path) = sess.source_map().span_to_filename(span) {
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`.
// Public for rustfmt usage.
pub fn submod_path_from_attr(attrs: &[Attribute], dir_path: &Path) -> Option<PathBuf> {
// Extract path string from first `#[path = "path_string"]` attribute.
let path_string = attr::first_attr_value_str_by_name(attrs, sym::path)?;
let path_string = path_string.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_string = path_string.replace("/", "\\");
Some(dir_path.join(&*path_string))
}
/// Returns a path to a module.
// Public for rustfmt usage.
pub fn default_submod_path<'a>(
sess: &'a ParseSess,
id: ast::Ident,
span: Span,
relative: Option<ast::Ident>,
dir_path: &Path,
) -> ModulePath<'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`.
let relative_prefix_string;
let relative_prefix = if let Some(ident) = relative {
relative_prefix_string = format!("{}{}", ident.name, path::MAIN_SEPARATOR);
&relative_prefix_string
} else {
""
};
let mod_name = id.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);
let default_path = dir_path.join(&default_path_str);
let secondary_path = dir_path.join(&secondary_path_str);
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) {
(true, false) => Ok(ModulePathSuccess {
path: default_path,
ownership: DirectoryOwnership::Owned { relative: Some(id) },
}),
(false, true) => Ok(ModulePathSuccess {
path: secondary_path,
ownership: DirectoryOwnership::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)
}
(true, true) => {
let mut err = struct_span_err!(
sess.span_diagnostic,
span,
E0584,
"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)
}
};
ModulePath { name: mod_name, path_exists: default_exists || secondary_exists, result }
}

View File

@ -210,14 +210,7 @@ pub fn register_plugins<'a>(
Ok((krate, Lrc::new(lint_store)))
}
fn configure_and_expand_inner<'a>(
sess: &'a Session,
lint_store: &'a LintStore,
mut krate: ast::Crate,
crate_name: &str,
resolver_arenas: &'a ResolverArenas<'a>,
metadata_loader: &'a MetadataLoaderDyn,
) -> Result<(ast::Crate, Resolver<'a>)> {
fn pre_expansion_lint(sess: &Session, lint_store: &LintStore, krate: &ast::Crate) {
sess.time("pre_AST_expansion_lint_checks", || {
rustc_lint::check_ast_crate(
sess,
@ -228,6 +221,17 @@ fn configure_and_expand_inner<'a>(
rustc_lint::BuiltinCombinedPreExpansionLintPass::new(),
);
});
}
fn configure_and_expand_inner<'a>(
sess: &'a Session,
lint_store: &'a LintStore,
mut krate: ast::Crate,
crate_name: &str,
resolver_arenas: &'a ResolverArenas<'a>,
metadata_loader: &'a MetadataLoaderDyn,
) -> Result<(ast::Crate, Resolver<'a>)> {
pre_expansion_lint(sess, lint_store, &krate);
let mut resolver = Resolver::new(sess, &krate, crate_name, metadata_loader, &resolver_arenas);
rustc_builtin_macros::register_builtin_macros(&mut resolver, sess.edition());
@ -291,7 +295,8 @@ fn configure_and_expand_inner<'a>(
..rustc_expand::expand::ExpansionConfig::default(crate_name.to_string())
};
let mut ecx = ExtCtxt::new(&sess.parse_sess, cfg, &mut resolver);
let extern_mod_loaded = |k: &ast::Crate| pre_expansion_lint(sess, lint_store, k);
let mut ecx = ExtCtxt::new(&sess.parse_sess, cfg, &mut resolver, Some(&extern_mod_loaded));
// Expand macros now!
let krate = sess.time("expand_crate", || ecx.monotonic_expander().expand_crate(krate));

View File

@ -18,7 +18,7 @@ use crate::context::{EarlyContext, LintContext, LintStore};
use crate::passes::{EarlyLintPass, EarlyLintPassObject};
use rustc_ast::ast;
use rustc_ast::visit as ast_visit;
use rustc_session::lint::{LintBuffer, LintPass};
use rustc_session::lint::{BufferedEarlyLint, LintBuffer, LintPass};
use rustc_session::Session;
use rustc_span::Span;
@ -37,13 +37,7 @@ struct EarlyContextAndPass<'a, T: EarlyLintPass> {
impl<'a, T: EarlyLintPass> EarlyContextAndPass<'a, T> {
fn check_id(&mut self, id: ast::NodeId) {
for early_lint in self.context.buffered.take(id) {
let rustc_session::lint::BufferedEarlyLint {
span,
msg,
node_id: _,
lint_id,
diagnostic,
} = early_lint;
let BufferedEarlyLint { span, msg, node_id: _, lint_id, diagnostic } = early_lint;
self.context.lookup_with_diagnostics(
lint_id.lint,
Some(span),
@ -326,11 +320,9 @@ pub fn check_ast_crate<T: EarlyLintPass>(
lint_buffer: Option<LintBuffer>,
builtin_lints: T,
) {
let mut passes: Vec<_> = if pre_expansion {
lint_store.pre_expansion_passes.iter().map(|p| (p)()).collect()
} else {
lint_store.early_passes.iter().map(|p| (p)()).collect()
};
let passes =
if pre_expansion { &lint_store.pre_expansion_passes } else { &lint_store.early_passes };
let mut passes: Vec<_> = passes.iter().map(|p| (p)()).collect();
let mut buffered = lint_buffer.unwrap_or_default();
if !sess.opts.debugging_opts.no_interleave_lints {

View File

@ -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};
@ -13,7 +14,7 @@ use rustc_errors::{Diagnostic, FatalError, Level, PResult};
use rustc_session::parse::ParseSess;
use rustc_span::{FileName, SourceFile, Span};
use std::path::{Path, PathBuf};
use std::path::Path;
use std::str;
use log::info;
@ -25,24 +26,6 @@ pub mod parser;
use parser::{emit_unclosed_delims, make_unclosed_delims_error, Parser};
pub mod lexer;
pub mod validate_attr;
#[macro_use]
pub mod config;
#[derive(Clone)]
pub struct Directory {
pub path: PathBuf,
pub ownership: DirectoryOwnership,
}
#[derive(Copy, Clone)]
pub enum DirectoryOwnership {
Owned {
// None if `mod.rs`, `Some("foo")` if we're in `foo.rs`.
relative: Option<ast::Ident>,
},
UnownedViaBlock,
UnownedViaMod,
}
// A bunch of utility functions of the form `parse_<thing>_from_<source>`
// where <thing> includes crate, expr, item, stmt, tts, and one that
@ -119,10 +102,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 +126,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,26 +235,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)
}
/// Given a stream, the `ParseSess` and the base directory, produces a parser.
///
/// Use this function when you are creating a parser from the token stream
/// and also care about the current working directory of the parser (e.g.,
/// you are trying to resolve modules defined inside a macro invocation).
///
/// # Note
///
/// 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)
Parser::new(sess, stream, false, subparser_name)
}
/// Runs the given subparser `f` on the tokens of the given `attr`'s item.
@ -286,7 +245,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()?;

View File

@ -18,7 +18,6 @@ use rustc_span::{MultiSpan, Span, SpanSnippetError, DUMMY_SP};
use log::{debug, trace};
use std::mem;
use std::path::PathBuf;
const TURBOFISH: &str = "use `::<...>` instead of `<...>` to specify type arguments";
@ -41,42 +40,12 @@ pub(super) fn dummy_arg(ident: Ident) -> Param {
}
pub enum Error {
FileNotFoundForModule { mod_name: String, default_path: PathBuf },
DuplicatePaths { mod_name: String, default_path: String, secondary_path: String },
UselessDocComment,
}
impl Error {
fn span_err(self, sp: impl Into<MultiSpan>, handler: &Handler) -> DiagnosticBuilder<'_> {
match self {
Error::FileNotFoundForModule { ref mod_name, ref default_path } => {
let mut err = struct_span_err!(
handler,
sp,
E0583,
"file not found for module `{}`",
mod_name,
);
err.help(&format!(
"to create the module `{}`, create file \"{}\"",
mod_name,
default_path.display(),
));
err
}
Error::DuplicatePaths { ref mod_name, ref default_path, ref secondary_path } => {
let mut err = struct_span_err!(
handler,
sp,
E0584,
"file for module `{}` found at both {} and {}",
mod_name,
default_path,
secondary_path,
);
err.help("delete or rename one of them to remove the ambiguity");
err
}
Error::UselessDocComment => {
let mut err = struct_span_err!(
handler,

View File

@ -4,14 +4,15 @@ use super::{FollowedByType, Parser, PathStyle};
use crate::maybe_whole;
use rustc_ast::ast::{self, Async, AttrStyle, AttrVec, Attribute, Ident, DUMMY_NODE_ID};
use rustc_ast::ast::{AssocItem, AssocItemKind, ForeignItemKind, Item, ItemKind};
use rustc_ast::ast::{BindingMode, Block, FnDecl, FnSig, MacArgs, MacCall, MacDelimiter, Param};
use rustc_ast::ast::{Const, Defaultness, IsAuto, PathSegment, Unsafe, UseTree, UseTreeKind};
use rustc_ast::ast::{self, AttrStyle, AttrVec, Attribute, Ident, DUMMY_NODE_ID};
use rustc_ast::ast::{AssocItem, AssocItemKind, ForeignItemKind, Item, ItemKind, Mod};
use rustc_ast::ast::{Async, Const, Defaultness, IsAuto, Mutability, Unsafe, UseTree, UseTreeKind};
use rustc_ast::ast::{BindingMode, Block, FnDecl, FnSig, Param, SelfKind};
use rustc_ast::ast::{EnumDef, Generics, StructField, TraitRef, Ty, TyKind, Variant, VariantData};
use rustc_ast::ast::{FnHeader, ForeignItem, Mutability, SelfKind, Visibility, VisibilityKind};
use rustc_ast::ast::{FnHeader, ForeignItem, PathSegment, Visibility, VisibilityKind};
use rustc_ast::ast::{MacArgs, MacCall, MacDelimiter};
use rustc_ast::ptr::P;
use rustc_ast::token;
use rustc_ast::token::{self, TokenKind};
use rustc_ast::tokenstream::{DelimSpan, TokenStream, TokenTree};
use rustc_ast_pretty::pprust;
use rustc_errors::{struct_span_err, Applicability, PResult, StashKey};
@ -23,6 +24,61 @@ use log::debug;
use std::convert::TryFrom;
use std::mem;
impl<'a> Parser<'a> {
/// Parses a source module as a crate. This is the main entry point for the parser.
pub fn parse_crate_mod(&mut self) -> PResult<'a, ast::Crate> {
let lo = self.token.span;
let (module, attrs) = self.parse_mod(&token::Eof)?;
let span = lo.to(self.token.span);
let proc_macros = Vec::new(); // Filled in by `proc_macro_harness::inject()`.
Ok(ast::Crate { attrs, module, span, proc_macros })
}
/// Parses a `mod <foo> { ... }` or `mod <foo>;` item.
fn parse_item_mod(&mut self, attrs: &mut Vec<Attribute>) -> PResult<'a, ItemInfo> {
let id = self.parse_ident()?;
let (module, mut inner_attrs) = if self.eat(&token::Semi) {
Default::default()
} else {
self.expect(&token::OpenDelim(token::Brace))?;
self.parse_mod(&token::CloseDelim(token::Brace))?
};
attrs.append(&mut inner_attrs);
Ok((id, ItemKind::Mod(module)))
}
/// Parses the contents of a module (inner attributes followed by module items).
pub fn parse_mod(&mut self, term: &TokenKind) -> PResult<'a, (Mod, Vec<Attribute>)> {
let lo = self.token.span;
let attrs = self.parse_inner_attributes()?;
let module = self.parse_mod_items(term, lo)?;
Ok((module, attrs))
}
/// Given a termination token, parses all of the items in a module.
fn parse_mod_items(&mut self, term: &TokenKind, inner_lo: Span) -> PResult<'a, Mod> {
let mut items = vec![];
while let Some(item) = self.parse_item()? {
items.push(item);
self.maybe_consume_incorrect_semicolon(&items);
}
if !self.eat(term) {
let token_str = super::token_descr(&self.token);
if !self.maybe_consume_incorrect_semicolon(&items) {
let msg = &format!("expected item, found {}", token_str);
let mut err = self.struct_span_err(self.token.span, msg);
err.span_label(self.token.span, "expected item");
return Err(err);
}
}
let hi = if self.token.span.is_dummy() { inner_lo } else { self.prev_token.span };
Ok(Mod { inner: inner_lo.to(hi), items, inline: true })
}
}
pub(super) type ItemInfo = (Ident, ItemKind);
impl<'a> Parser<'a> {

View File

@ -1,8 +1,6 @@
pub mod attr;
mod expr;
mod item;
mod module;
pub use module::{ModulePath, ModulePathSuccess};
mod pat;
mod path;
mod ty;
@ -13,7 +11,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 +25,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 +88,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 +95,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 +342,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 +350,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 +357,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 +368,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
}

View File

@ -1,303 +0,0 @@
use super::diagnostics::Error;
use super::item::ItemInfo;
use super::Parser;
use crate::{new_sub_parser_from_file, DirectoryOwnership};
use rustc_ast::ast::{self, Attribute, Crate, Ident, ItemKind, Mod};
use rustc_ast::attr;
use rustc_ast::token::{self, TokenKind};
use rustc_errors::PResult;
use rustc_span::source_map::{FileName, SourceMap, Span, DUMMY_SP};
use rustc_span::symbol::sym;
use std::path::{self, Path, PathBuf};
/// Information about the path to a module.
// Public for rustfmt usage.
pub struct ModulePath {
name: String,
path_exists: bool,
pub result: Result<ModulePathSuccess, Error>,
}
// Public for rustfmt usage.
pub struct ModulePathSuccess {
pub path: PathBuf,
pub directory_ownership: DirectoryOwnership,
}
impl<'a> Parser<'a> {
/// Parses a source module as a crate. This is the main entry point for the parser.
pub fn parse_crate_mod(&mut self) -> PResult<'a, Crate> {
let lo = self.token.span;
let krate = Ok(ast::Crate {
attrs: self.parse_inner_attributes()?,
module: self.parse_mod_items(&token::Eof, lo)?,
span: lo.to(self.token.span),
// Filled in by proc_macro_harness::inject()
proc_macros: Vec::new(),
});
krate
}
/// 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_span = self.token.span;
let id = self.parse_ident()?;
let (module, mut inner_attrs) = if self.eat(&token::Semi) {
if in_cfg && self.recurse_into_file_modules {
// This mod is in an external file. Let's go get it!
let ModulePathSuccess { path, directory_ownership } =
self.submod_path(id, &attrs, id_span)?;
self.eval_src_mod(path, directory_ownership, id.to_string(), id_span)?
} else {
(ast::Mod { inner: DUMMY_SP, items: Vec::new(), inline: false }, Vec::new())
}
} else {
let old_directory = self.directory.clone();
self.push_directory(id, &attrs);
self.expect(&token::OpenDelim(token::Brace))?;
let mod_inner_lo = self.token.span;
let inner_attrs = self.parse_inner_attributes()?;
let module = self.parse_mod_items(&token::CloseDelim(token::Brace), mod_inner_lo)?;
self.directory = old_directory;
(module, inner_attrs)
};
attrs.append(&mut inner_attrs);
Ok((id, ItemKind::Mod(module)))
}
/// Given a termination token, parses all of the items in a module.
fn parse_mod_items(&mut self, term: &TokenKind, inner_lo: Span) -> PResult<'a, Mod> {
let mut items = vec![];
while let Some(item) = self.parse_item()? {
items.push(item);
self.maybe_consume_incorrect_semicolon(&items);
}
if !self.eat(term) {
let token_str = super::token_descr(&self.token);
if !self.maybe_consume_incorrect_semicolon(&items) {
let msg = &format!("expected item, found {}", token_str);
let mut err = self.struct_span_err(self.token.span, msg);
err.span_label(self.token.span, "expected item");
return Err(err);
}
}
let hi = if self.token.span.is_dummy() { inner_lo } else { self.prev_token.span };
Ok(Mod { inner: inner_lo.to(hi), items, inline: true })
}
fn submod_path(
&mut self,
id: ast::Ident,
outer_attrs: &[Attribute],
id_sp: Span,
) -> PResult<'a, ModulePathSuccess> {
if let Some(path) = Parser::submod_path_from_attr(outer_attrs, &self.directory.path) {
return Ok(ModulePathSuccess {
directory_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,
},
path,
});
}
let relative = match self.directory.ownership {
DirectoryOwnership::Owned { relative } => relative,
DirectoryOwnership::UnownedViaBlock | DirectoryOwnership::UnownedViaMod => None,
};
let paths =
Parser::default_submod_path(id, relative, &self.directory.path, self.sess.source_map());
match self.directory.ownership {
DirectoryOwnership::Owned { .. } => {
paths.result.map_err(|err| self.span_fatal_err(id_sp, err))
}
DirectoryOwnership::UnownedViaBlock => {
let msg = "Cannot declare a non-inline module inside a block \
unless it has a path attribute";
let mut err = self.struct_span_err(id_sp, msg);
if paths.path_exists {
let msg = format!(
"Maybe `use` the module `{}` instead of redeclaring it",
paths.name
);
err.span_note(id_sp, &msg);
}
Err(err)
}
DirectoryOwnership::UnownedViaMod => {
let mut err =
self.struct_span_err(id_sp, "cannot declare a new module at this location");
if !id_sp.is_dummy() {
let src_path = self.sess.source_map().span_to_filename(id_sp);
if let FileName::Real(src_path) = src_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(
id_sp,
&format!(
"maybe move this module `{}` to its own \
directory via `{}`",
src_path.display(),
dest_path.display()
),
);
}
}
}
if paths.path_exists {
err.span_note(
id_sp,
&format!(
"... or maybe `use` the module `{}` instead \
of possibly redeclaring it",
paths.name
),
);
}
Err(err)
}
}
}
// Public for rustfmt usage.
pub fn submod_path_from_attr(attrs: &[Attribute], dir_path: &Path) -> Option<PathBuf> {
if let Some(s) = attr::first_attr_value_str_by_name(attrs, sym::path) {
let s = s.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 s = s.replace("/", "\\");
Some(dir_path.join(&*s))
} else {
None
}
}
/// Returns a path to a module.
// Public for rustfmt usage.
pub fn default_submod_path(
id: ast::Ident,
relative: Option<ast::Ident>,
dir_path: &Path,
source_map: &SourceMap,
) -> ModulePath {
// 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`.
let relative_prefix_string;
let relative_prefix = if let Some(ident) = relative {
relative_prefix_string = format!("{}{}", ident.name, path::MAIN_SEPARATOR);
&relative_prefix_string
} else {
""
};
let mod_name = id.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);
let default_path = dir_path.join(&default_path_str);
let secondary_path = dir_path.join(&secondary_path_str);
let default_exists = source_map.file_exists(&default_path);
let secondary_exists = source_map.file_exists(&secondary_path);
let result = match (default_exists, secondary_exists) {
(true, false) => Ok(ModulePathSuccess {
path: default_path,
directory_ownership: DirectoryOwnership::Owned { relative: Some(id) },
}),
(false, true) => Ok(ModulePathSuccess {
path: secondary_path,
directory_ownership: DirectoryOwnership::Owned { relative: None },
}),
(false, false) => {
Err(Error::FileNotFoundForModule { mod_name: mod_name.clone(), default_path })
}
(true, true) => Err(Error::DuplicatePaths {
mod_name: mod_name.clone(),
default_path: default_path_str,
secondary_path: secondary_path_str,
}),
};
ModulePath { name: mod_name, path_exists: default_exists || secondary_exists, result }
}
/// Reads a module from a source file.
fn eval_src_mod(
&mut self,
path: PathBuf,
directory_ownership: DirectoryOwnership,
name: String,
id_sp: Span,
) -> PResult<'a, (Mod, Vec<Attribute>)> {
let mut included_mod_stack = self.sess.included_mod_stack.borrow_mut();
if let Some(i) = included_mod_stack.iter().position(|p| *p == path) {
let mut err = String::from("circular modules: ");
let len = included_mod_stack.len();
for p in &included_mod_stack[i..len] {
err.push_str(&p.to_string_lossy());
err.push_str(" -> ");
}
err.push_str(&path.to_string_lossy());
return Err(self.struct_span_err(id_sp, &err[..]));
}
included_mod_stack.push(path.clone());
drop(included_mod_stack);
let mut p0 =
new_sub_parser_from_file(self.sess, &path, directory_ownership, Some(name), id_sp);
p0.cfg_mods = self.cfg_mods;
let mod_inner_lo = p0.token.span;
let mod_attrs = p0.parse_inner_attributes()?;
let mut m0 = p0.parse_mod_items(&token::Eof, mod_inner_lo)?;
m0.inline = false;
self.sess.included_mod_stack.borrow_mut().pop();
Ok((m0, mod_attrs))
}
fn push_directory(&mut self, id: Ident, attrs: &[Attribute]) {
if let Some(path) = attr::first_attr_value_str_by_name(attrs, sym::path) {
self.directory.path.push(&*path.as_str());
self.directory.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 self.directory.ownership {
if let Some(ident) = relative.take() {
// remove the relative offset
self.directory.path.push(&*ident.as_str());
}
}
self.directory.path.push(&*id.as_str());
}
}
}

View File

@ -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)?;

View File

@ -57,7 +57,7 @@ pub fn parse_meta<'a>(sess: &'a ParseSess, attr: &Attribute) -> PResult<'a, Meta
})
}
crate fn check_meta_bad_delim(sess: &ParseSess, span: DelimSpan, delim: MacDelimiter, msg: &str) {
pub fn check_meta_bad_delim(sess: &ParseSess, span: DelimSpan, delim: MacDelimiter, msg: &str) {
if let ast::MacDelimiter::Parenthesis = delim {
return;
}

View File

@ -2,10 +2,8 @@
// compile-flags: -Zquery-dep-graph
#![feature(rustc_attrs)]
#![allow(private_no_mangle_fns)]
#![rustc_partition_codegened(module="change_symbol_export_status-mod1", cfg="rpass2")]
#![rustc_partition_reused(module="change_symbol_export_status-mod2", cfg="rpass2")]
#![rustc_partition_codegened(module = "change_symbol_export_status-mod1", cfg = "rpass2")]
#![rustc_partition_reused(module = "change_symbol_export_status-mod2", cfg = "rpass2")]
// This test case makes sure that a change in symbol visibility is detected by
// our dependency tracking. We do this by changing a module's visibility to

View File

@ -1,4 +1,5 @@
// pp-exact
// pretty-compare-only
// The next line should not be expanded

View File

@ -2,16 +2,14 @@
#![feature(rustc_private)]
// We're testing linkage visibility; the compiler warns us, but we want to
// do the runtime check that these functions aren't exported.
#![allow(private_no_mangle_fns)]
extern crate rustc_metadata;
use rustc_metadata::dynamic_lib::DynamicLibrary;
#[no_mangle]
pub fn foo() { bar(); }
pub fn foo() {
bar();
}
pub fn foo2<T>() {
fn bar2() {
@ -21,11 +19,11 @@ pub fn foo2<T>() {
}
#[no_mangle]
fn bar() { }
fn bar() {}
#[allow(dead_code)]
#[no_mangle]
fn baz() { }
fn baz() {}
pub fn test() {
let lib = DynamicLibrary::open(None).unwrap();

View File

@ -1,7 +1,9 @@
// Test that macro-expanded non-inline modules behave correctly
macro_rules! mod_decl {
($i:ident) => { mod $i; } //~ ERROR Cannot declare a non-inline module inside a block
($i:ident) => {
mod $i; //~ ERROR Cannot declare a non-inline module inside a block
};
}
mod macro_expanded_mod_helper {

View File

@ -1,8 +1,8 @@
error: Cannot declare a non-inline module inside a block unless it has a path attribute
--> $DIR/macro-expanded-mod.rs:4:25
--> $DIR/macro-expanded-mod.rs:5:9
|
LL | ($i:ident) => { mod $i; }
| ^^
LL | mod $i;
| ^^^^^^^
...
LL | mod_decl!(foo);
| --------------- in this macro invocation

View File

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

View File

@ -1,8 +1,8 @@
error[E0583]: file not found for module `module_that_doesnt_exist`
--> $DIR/E0583.rs:1:5
--> $DIR/E0583.rs:1:1
|
LL | mod module_that_doesnt_exist;
| ^^^^^^^^^^^^^^^^^^^^^^^^
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= help: to create the module `module_that_doesnt_exist`, create file "$DIR/module_that_doesnt_exist.rs"

View File

@ -1,8 +1,8 @@
error[E0583]: file not found for module `baz`
--> $DIR/auxiliary/foo/bar.rs:1:9
--> $DIR/auxiliary/foo/bar.rs:1:1
|
LL | pub mod baz;
| ^^^
| ^^^^^^^^^^^^
|
= help: to create the module `baz`, create file "$DIR/auxiliary/foo/bar/baz.rs"

View File

@ -0,0 +1,7 @@
// check-pass
// compile-flags: -W rust-2018-compatibility
// error-pattern: `try` is a keyword in the 2018 edition
fn main() {}
mod lint_pre_expansion_extern_module_aux;

View File

@ -0,0 +1,10 @@
warning: `try` is a keyword in the 2018 edition
--> $DIR/lint_pre_expansion_extern_module_aux.rs:3:8
|
LL | pub fn try() {}
| ^^^ help: you can use a raw identifier to stay compatible: `r#try`
|
= note: `-W keyword-idents` implied by `-W rust-2018-compatibility`
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in the 2018 edition!
= note: for more information, see issue #49716 <https://github.com/rust-lang/rust/issues/49716>

View File

@ -0,0 +1,3 @@
// ignore-test: not a test
pub fn try() {}

View File

@ -1,8 +1,8 @@
error[E0583]: file not found for module `missing`
--> $DIR/foo.rs:4:5
--> $DIR/foo.rs:4:1
|
LL | mod missing;
| ^^^^^^^
| ^^^^^^^^^^^^
|
= help: to create the module `missing`, create file "$DIR/foo/missing.rs"

View File

@ -1,8 +1,8 @@
error[E0583]: file not found for module `missing`
--> $DIR/foo_inline.rs:4:9
--> $DIR/foo_inline.rs:4:5
|
LL | mod missing;
| ^^^^^^^
| ^^^^^^^^^^^^
|
= help: to create the module `missing`, create file "$DIR/foo_inline/inline/missing.rs"

View File

@ -2,4 +2,5 @@ mod mod_file_disambig_aux; //~ ERROR file for module `mod_file_disambig_aux` fou
fn main() {
assert_eq!(mod_file_aux::bar(), 10);
//~^ ERROR failed to resolve: use of undeclared type or module `mod_file_aux`
}

View File

@ -1,11 +1,18 @@
error[E0584]: file for module `mod_file_disambig_aux` found at both mod_file_disambig_aux.rs and mod_file_disambig_aux/mod.rs
--> $DIR/mod_file_disambig.rs:1:5
--> $DIR/mod_file_disambig.rs:1:1
|
LL | mod mod_file_disambig_aux;
| ^^^^^^^^^^^^^^^^^^^^^
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= help: delete or rename one of them to remove the ambiguity
error: aborting due to previous error
error[E0433]: failed to resolve: use of undeclared type or module `mod_file_aux`
--> $DIR/mod_file_disambig.rs:4:16
|
LL | assert_eq!(mod_file_aux::bar(), 10);
| ^^^^^^^^^^^^ use of undeclared type or module `mod_file_aux`
For more information about this error, try `rustc --explain E0584`.
error: aborting due to 2 previous errors
Some errors have detailed explanations: E0433, E0584.
For more information about an error, try `rustc --explain E0433`.

View File

@ -6,5 +6,5 @@ pub fn hi_str() -> String {
}
fn main() {
circular_modules_hello::say_hello();
circular_modules_hello::say_hello(); //~ ERROR cannot find function `say_hello` in module
}

View File

@ -1,8 +1,20 @@
error: circular modules: $DIR/circular_modules_hello.rs -> $DIR/circular_modules_main.rs -> $DIR/circular_modules_hello.rs
--> $DIR/circular_modules_main.rs:2:5
--> $DIR/circular_modules_main.rs:2:1
|
LL | mod circular_modules_hello;
| ^^^^^^^^^^^^^^^^^^^^^^
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: aborting due to previous error
error[E0425]: cannot find function `say_hello` in module `circular_modules_hello`
--> $DIR/circular_modules_main.rs:9:29
|
LL | circular_modules_hello::say_hello();
| ^^^^^^^^^ not found in `circular_modules_hello`
|
help: possible candidate is found in another module, you can import it into scope
|
LL | use circular_modules_hello::say_hello;
|
error: aborting due to 2 previous errors
For more information about this error, try `rustc --explain E0425`.

View File

@ -1,8 +1,8 @@
error: couldn't read $DIR/../parser: $ACCESS_DENIED_MSG (os error $ACCESS_DENIED_CODE)
--> $DIR/issue-5806.rs:5:5
--> $DIR/issue-5806.rs:5:1
|
LL | mod foo;
| ^^^
| ^^^^^^^^
error: aborting due to previous error

View File

@ -1,8 +1,9 @@
// ignore-windows
mod not_a_real_file; //~ ERROR file not found for module `not_a_real_file`
//~^ HELP to create the module `not_a_real_file`, create file "
//~^ HELP to create the module `not_a_real_file`, create file
fn main() {
assert_eq!(mod_file_aux::bar(), 10);
//~^ ERROR failed to resolve: use of undeclared type or module `mod_file_aux`
}

View File

@ -1,11 +1,18 @@
error[E0583]: file not found for module `not_a_real_file`
--> $DIR/mod_file_not_exist.rs:3:5
--> $DIR/mod_file_not_exist.rs:3:1
|
LL | mod not_a_real_file;
| ^^^^^^^^^^^^^^^
| ^^^^^^^^^^^^^^^^^^^^
|
= help: to create the module `not_a_real_file`, create file "$DIR/not_a_real_file.rs"
error: aborting due to previous error
error[E0433]: failed to resolve: use of undeclared type or module `mod_file_aux`
--> $DIR/mod_file_not_exist.rs:7:16
|
LL | assert_eq!(mod_file_aux::bar(), 10);
| ^^^^^^^^^^^^ use of undeclared type or module `mod_file_aux`
For more information about this error, try `rustc --explain E0583`.
error: aborting due to 2 previous errors
Some errors have detailed explanations: E0433, E0583.
For more information about an error, try `rustc --explain E0433`.

View File

@ -5,4 +5,5 @@ mod not_a_real_file; //~ ERROR file not found for module `not_a_real_file`
fn main() {
assert_eq!(mod_file_aux::bar(), 10);
//~^ ERROR failed to resolve: use of undeclared type or module `mod_file_aux`
}

View File

@ -1,11 +1,18 @@
error[E0583]: file not found for module `not_a_real_file`
--> $DIR/mod_file_not_exist_windows.rs:3:5
--> $DIR/mod_file_not_exist_windows.rs:3:1
|
LL | mod not_a_real_file;
| ^^^^^^^^^^^^^^^
| ^^^^^^^^^^^^^^^^^^^^
|
= help: to create the module `not_a_real_file`, create file "$DIR/not_a_real_file.rs"
error: aborting due to previous error
error[E0433]: failed to resolve: use of undeclared type or module `mod_file_aux`
--> $DIR/mod_file_not_exist_windows.rs:7:16
|
LL | assert_eq!(mod_file_aux::bar(), 10);
| ^^^^^^^^^^^^ use of undeclared type or module `mod_file_aux`
For more information about this error, try `rustc --explain E0583`.
error: aborting due to 2 previous errors
Some errors have detailed explanations: E0433, E0583.
For more information about an error, try `rustc --explain E0433`.

View File

@ -1,8 +1,8 @@
error: couldn't read $DIR/not_a_real_file.rs: $FILE_NOT_FOUND_MSG (os error 2)
--> $DIR/mod_file_with_path_attr.rs:4:5
--> $DIR/mod_file_with_path_attr.rs:4:1
|
LL | mod m;
| ^
| ^^^^^^
error: aborting due to previous error

View File

@ -0,0 +1,13 @@
// Expansion drives parsing, so conditional compilation will strip
// out outline modules and we will never attempt parsing them.
// check-pass
fn main() {}
#[cfg(FALSE)]
mod foo {
mod bar {
mod baz; // This was an error before.
}
}