Skip to content

Commit

Permalink
Auto merge of #46531 - cramertj:no-mo-modrs, r=nikomatsakis
Browse files Browse the repository at this point in the history
Implement non-mod.rs mod statements

Fixes #45385, cc #44660

This will fail tidy right now because it doesn't recognize my UI tests as feature-gate tests. However, I'm not sure if compile-fail will work out either because compile-fail usually requires there to be error patterns in the top-level file, which isn't possible with this feature. What's the recommended way to handle this?
  • Loading branch information
bors committed Dec 10, 2017
2 parents 02b4d3d + 75176de commit 4068b3a
Show file tree
Hide file tree
Showing 57 changed files with 606 additions and 63 deletions.
2 changes: 1 addition & 1 deletion src/libsyntax/ext/base.rs
Original file line number Diff line number Diff line change
Expand Up @@ -688,7 +688,7 @@ impl<'a> ExtCtxt<'a> {
mark: Mark::root(),
depth: 0,
module: Rc::new(ModuleData { mod_path: Vec::new(), directory: PathBuf::new() }),
directory_ownership: DirectoryOwnership::Owned,
directory_ownership: DirectoryOwnership::Owned { relative: None },
},
expansions: HashMap::new(),
}
Expand Down
10 changes: 7 additions & 3 deletions src/libsyntax/ext/expand.rs
Original file line number Diff line number Diff line change
Expand Up @@ -974,16 +974,20 @@ impl<'a, 'b> Folder for InvocationCollector<'a, 'b> {

if inline_module {
if let Some(path) = attr::first_attr_value_str_by_name(&item.attrs, "path") {
self.cx.current_expansion.directory_ownership = DirectoryOwnership::Owned;
self.cx.current_expansion.directory_ownership =
DirectoryOwnership::Owned { relative: None };
module.directory.push(&*path.as_str());
} else {
module.directory.push(&*item.ident.name.as_str());
}
} else {
let mut path = self.cx.parse_sess.codemap().span_to_unmapped_path(inner);
let directory_ownership = match path.file_name().unwrap().to_str() {
Some("mod.rs") => DirectoryOwnership::Owned,
_ => DirectoryOwnership::UnownedViaMod(false),
Some("mod.rs") => DirectoryOwnership::Owned { relative: None },
Some(_) => DirectoryOwnership::Owned {
relative: Some(item.ident),
},
None => DirectoryOwnership::UnownedViaMod(false),
};
path.pop();
module.directory = path;
Expand Down
2 changes: 1 addition & 1 deletion src/libsyntax/ext/source_util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ pub fn expand_include<'cx>(cx: &'cx mut ExtCtxt, sp: Span, tts: &[tokenstream::T
};
// The file will be added to the code map by the parser
let path = res_rel_file(cx, sp, Path::new(&file));
let directory_ownership = DirectoryOwnership::Owned;
let directory_ownership = DirectoryOwnership::Owned { relative: None };
let p = parse::new_sub_parser_from_file(cx.parse_sess(), &path, directory_ownership, None, sp);

struct ExpandResult<'a> {
Expand Down
34 changes: 32 additions & 2 deletions src/libsyntax/feature_gate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ use visit::{self, FnKind, Visitor};
use parse::ParseSess;
use symbol::{keywords, Symbol};

use std::env;
use std::{env, path};

macro_rules! set {
(proc_macro) => {{
Expand Down Expand Up @@ -430,6 +430,9 @@ declare_features! (

// generic associated types (RFC 1598)
(active, generic_associated_types, "1.23.0", Some(44265)),

// `foo.rs` as an alternative to `foo/mod.rs`
(active, non_modrs_mods, "1.24.0", Some(44660)),
);

declare_features! (
Expand Down Expand Up @@ -1297,6 +1300,31 @@ fn contains_novel_literal(item: &ast::MetaItem) -> bool {
}
}

impl<'a> PostExpansionVisitor<'a> {
fn whole_crate_feature_gates(&mut self) {
for &(ident, span) in &*self.context.parse_sess.non_modrs_mods.borrow() {
if !span.allows_unstable() {
let cx = &self.context;
let level = GateStrength::Hard;
let has_feature = cx.features.non_modrs_mods;
let name = "non_modrs_mods";
debug!("gate_feature(feature = {:?}, span = {:?}); has? {}",
name, span, has_feature);

if !has_feature && !span.allows_unstable() {
leveled_feature_err(
cx.parse_sess, name, span, GateIssue::Language,
"mod statements in non-mod.rs files are unstable", level
)
.help(&format!("on stable builds, rename this file to {}{}mod.rs",
ident, path::MAIN_SEPARATOR))
.emit();
}
}
}
}
}

impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {
fn visit_attribute(&mut self, attr: &ast::Attribute) {
if !attr.span.allows_unstable() {
Expand Down Expand Up @@ -1843,7 +1871,9 @@ pub fn check_crate(krate: &ast::Crate,
parse_sess: sess,
plugin_attributes,
};
visit::walk_crate(&mut PostExpansionVisitor { context: &ctx }, krate);
let visitor = &mut PostExpansionVisitor { context: &ctx };
visitor.whole_crate_feature_gates();
visit::walk_crate(visitor, krate);
}

#[derive(Clone, Copy, PartialEq, Eq, Hash)]
Expand Down
1 change: 1 addition & 0 deletions src/libsyntax/parse/lexer/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1727,6 +1727,7 @@ mod tests {
included_mod_stack: RefCell::new(Vec::new()),
code_map: cm,
missing_fragment_specifiers: RefCell::new(HashSet::new()),
non_modrs_mods: RefCell::new(vec![]),
}
}

Expand Down
9 changes: 8 additions & 1 deletion src/libsyntax/parse/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,9 @@ pub struct ParseSess {
pub unstable_features: UnstableFeatures,
pub config: CrateConfig,
pub missing_fragment_specifiers: RefCell<HashSet<Span>>,
// Spans where a `mod foo;` statement was included in a non-mod.rs file.
// These are used to issue errors if the non_modrs_mods feature is not enabled.
pub non_modrs_mods: RefCell<Vec<(ast::Ident, Span)>>,
/// Used to determine and report recursive mod inclusions
included_mod_stack: RefCell<Vec<PathBuf>>,
code_map: Rc<CodeMap>,
Expand All @@ -70,6 +73,7 @@ impl ParseSess {
missing_fragment_specifiers: RefCell::new(HashSet::new()),
included_mod_stack: RefCell::new(vec![]),
code_map,
non_modrs_mods: RefCell::new(vec![]),
}
}

Expand All @@ -86,7 +90,10 @@ pub struct Directory {

#[derive(Copy, Clone)]
pub enum DirectoryOwnership {
Owned,
Owned {
// None if `mod.rs`, `Some("foo")` if we're in `foo.rs`
relative: Option<ast::Ident>,
},
UnownedViaBlock,
UnownedViaMod(bool /* legacy warnings? */),
}
Expand Down
134 changes: 91 additions & 43 deletions src/libsyntax/parse/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -507,7 +507,10 @@ impl<'a> Parser<'a> {
restrictions: Restrictions::empty(),
obsolete_set: HashSet::new(),
recurse_into_file_modules,
directory: Directory { path: PathBuf::new(), ownership: DirectoryOwnership::Owned },
directory: Directory {
path: PathBuf::new(),
ownership: DirectoryOwnership::Owned { relative: None }
},
root_module_name: None,
expected_tokens: Vec::new(),
token_cursor: TokenCursor {
Expand Down Expand Up @@ -5676,7 +5679,7 @@ impl<'a> Parser<'a> {
fn push_directory(&mut self, id: Ident, attrs: &[Attribute]) {
if let Some(path) = attr::first_attr_value_str_by_name(attrs, "path") {
self.directory.path.push(&path.as_str());
self.directory.ownership = DirectoryOwnership::Owned;
self.directory.ownership = DirectoryOwnership::Owned { relative: None };
} else {
self.directory.path.push(&id.name.as_str());
}
Expand All @@ -5687,10 +5690,28 @@ impl<'a> Parser<'a> {
}

/// Returns either a path to a module, or .
pub fn default_submod_path(id: ast::Ident, dir_path: &Path, codemap: &CodeMap) -> ModulePath {
pub fn default_submod_path(
id: ast::Ident,
relative: Option<ast::Ident>,
dir_path: &Path,
codemap: &CodeMap) -> 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.as_str(), path::MAIN_SEPARATOR);
&relative_prefix_string
} else {
""
};

let mod_name = id.to_string();
let default_path_str = format!("{}.rs", mod_name);
let secondary_path_str = format!("{}{}mod.rs", mod_name, path::MAIN_SEPARATOR);
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 = codemap.file_exists(&default_path);
Expand All @@ -5699,12 +5720,16 @@ impl<'a> Parser<'a> {
let result = match (default_exists, secondary_exists) {
(true, false) => Ok(ModulePathSuccess {
path: default_path,
directory_ownership: DirectoryOwnership::UnownedViaMod(false),
directory_ownership: DirectoryOwnership::Owned {
relative: Some(id),
},
warn: false,
}),
(false, true) => Ok(ModulePathSuccess {
path: secondary_path,
directory_ownership: DirectoryOwnership::Owned,
directory_ownership: DirectoryOwnership::Owned {
relative: None,
},
warn: false,
}),
(false, false) => Err(Error::FileNotFoundForModule {
Expand Down Expand Up @@ -5735,55 +5760,78 @@ impl<'a> Parser<'a> {
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()) {
Some("mod.rs") => DirectoryOwnership::Owned,
Some("mod.rs") => DirectoryOwnership::Owned { relative: None },
Some(_) => {
DirectoryOwnership::Owned { relative: Some(id) }
}
_ => DirectoryOwnership::UnownedViaMod(true),
},
path,
warn: false,
});
}

let paths = Parser::default_submod_path(id, &self.directory.path, self.sess.codemap());
let relative = match self.directory.ownership {
DirectoryOwnership::Owned { relative } => {
// Push the usage onto the list of non-mod.rs mod uses.
// This is used later for feature-gate error reporting.
if let Some(cur_file_ident) = relative {
self.sess
.non_modrs_mods.borrow_mut()
.push((cur_file_ident, id_sp));
}
relative
},
DirectoryOwnership::UnownedViaBlock |
DirectoryOwnership::UnownedViaMod(_) => None,
};
let paths = Parser::default_submod_path(
id, relative, &self.directory.path, self.sess.codemap());

if let DirectoryOwnership::UnownedViaBlock = self.directory.ownership {
let msg =
"Cannot declare a non-inline module inside a block unless it has a path attribute";
let mut err = self.diagnostic().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)
} else if let DirectoryOwnership::UnownedViaMod(warn) = self.directory.ownership {
if warn {
if let Ok(result) = paths.result {
return Ok(ModulePathSuccess { warn: true, ..result });
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.diagnostic().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)
}
let mut err = self.diagnostic().struct_span_err(id_sp,
"cannot declare a new module at this location");
if id_sp != syntax_pos::DUMMY_SP {
let src_path = PathBuf::from(self.sess.codemap().span_to_filename(id_sp));
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");
DirectoryOwnership::UnownedViaMod(warn) => {
if warn {
if let Ok(result) = paths.result {
return Ok(ModulePathSuccess { warn: true, ..result });
}
}
let mut err = self.diagnostic().struct_span_err(id_sp,
"cannot declare a new module at this location");
if id_sp != syntax_pos::DUMMY_SP {
let src_path = PathBuf::from(self.sess.codemap().span_to_filename(id_sp));
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.to_string_lossy(),
dest_path.to_string_lossy()));
}
}
if paths.path_exists {
err.span_note(id_sp,
&format!("maybe move this module `{}` to its own \
directory via `{}`", src_path.to_string_lossy(),
dest_path.to_string_lossy()));
&format!("... or maybe `use` the module `{}` instead \
of possibly redeclaring it",
paths.name));
}
Err(err)
}
if paths.path_exists {
err.span_note(id_sp,
&format!("... or maybe `use` the module `{}` instead \
of possibly redeclaring it",
paths.name));
}
Err(err)
} else {
paths.result.map_err(|err| self.span_fatal_err(id_sp, err))
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,7 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.

// error-pattern: cannot declare a new module at this location
// error-pattern: will become a hard error
// error-pattern: mod statements in non-mod.rs files are unstable

#[path="mod_file_not_owning_aux3.rs"]
mod foo;
Expand Down
Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.

// error-pattern: cannot declare a new module at this location
// error-pattern: mod statements in non-mod.rs files are unstable

mod mod_file_not_owning_aux1;

Expand Down
Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.

// error-pattern: cannot declare a new module at this location
// error-pattern: mod statements in non-mod.rs files are unstable

// This is not a directory owner since the file name is not "mod.rs".
#[path = "mod_file_not_owning_aux1.rs"]
Expand Down
Loading

0 comments on commit 4068b3a

Please sign in to comment.