Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement non-mod.rs mod statements #46531

Merged
merged 4 commits into from
Dec 21, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/libsyntax/ext/base.rs
Original file line number Diff line number Diff line change
Expand Up @@ -687,7 +687,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 @@ -976,7 +976,8 @@ 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());
Expand All @@ -988,8 +989,11 @@ impl<'a, 'b> Folder for InvocationCollector<'a, 'b> {
other => PathBuf::from(other.to_string()),
};
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, 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 @@ -435,6 +435,9 @@ declare_features! (

// Resolve absolute paths as paths from other crates
(active, extern_absolute_paths, "1.24.0", Some(44660)),

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

declare_features! (
Expand Down Expand Up @@ -1302,6 +1305,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 @@ -1854,7 +1882,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 @@ -1754,6 +1754,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
132 changes: 90 additions & 42 deletions src/libsyntax/parse/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -514,7 +514,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 @@ -5731,7 +5734,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 @@ -5742,10 +5745,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 @@ -5754,12 +5775,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 @@ -5790,57 +5815,80 @@ 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 = self.sess.codemap().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,
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 = self.sess.codemap().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)
}
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
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