Skip to content

Commit 4068b3a

Browse files
committed
Auto merge of #46531 - cramertj:no-mo-modrs, r=nikomatsakis
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?
2 parents 02b4d3d + 75176de commit 4068b3a

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

57 files changed

+606
-63
lines changed

src/libsyntax/ext/base.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -688,7 +688,7 @@ impl<'a> ExtCtxt<'a> {
688688
mark: Mark::root(),
689689
depth: 0,
690690
module: Rc::new(ModuleData { mod_path: Vec::new(), directory: PathBuf::new() }),
691-
directory_ownership: DirectoryOwnership::Owned,
691+
directory_ownership: DirectoryOwnership::Owned { relative: None },
692692
},
693693
expansions: HashMap::new(),
694694
}

src/libsyntax/ext/expand.rs

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -974,16 +974,20 @@ impl<'a, 'b> Folder for InvocationCollector<'a, 'b> {
974974

975975
if inline_module {
976976
if let Some(path) = attr::first_attr_value_str_by_name(&item.attrs, "path") {
977-
self.cx.current_expansion.directory_ownership = DirectoryOwnership::Owned;
977+
self.cx.current_expansion.directory_ownership =
978+
DirectoryOwnership::Owned { relative: None };
978979
module.directory.push(&*path.as_str());
979980
} else {
980981
module.directory.push(&*item.ident.name.as_str());
981982
}
982983
} else {
983984
let mut path = self.cx.parse_sess.codemap().span_to_unmapped_path(inner);
984985
let directory_ownership = match path.file_name().unwrap().to_str() {
985-
Some("mod.rs") => DirectoryOwnership::Owned,
986-
_ => DirectoryOwnership::UnownedViaMod(false),
986+
Some("mod.rs") => DirectoryOwnership::Owned { relative: None },
987+
Some(_) => DirectoryOwnership::Owned {
988+
relative: Some(item.ident),
989+
},
990+
None => DirectoryOwnership::UnownedViaMod(false),
987991
};
988992
path.pop();
989993
module.directory = path;

src/libsyntax/ext/source_util.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ pub fn expand_include<'cx>(cx: &'cx mut ExtCtxt, sp: Span, tts: &[tokenstream::T
100100
};
101101
// The file will be added to the code map by the parser
102102
let path = res_rel_file(cx, sp, Path::new(&file));
103-
let directory_ownership = DirectoryOwnership::Owned;
103+
let directory_ownership = DirectoryOwnership::Owned { relative: None };
104104
let p = parse::new_sub_parser_from_file(cx.parse_sess(), &path, directory_ownership, None, sp);
105105

106106
struct ExpandResult<'a> {

src/libsyntax/feature_gate.rs

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ use visit::{self, FnKind, Visitor};
3535
use parse::ParseSess;
3636
use symbol::{keywords, Symbol};
3737

38-
use std::env;
38+
use std::{env, path};
3939

4040
macro_rules! set {
4141
(proc_macro) => {{
@@ -430,6 +430,9 @@ declare_features! (
430430

431431
// generic associated types (RFC 1598)
432432
(active, generic_associated_types, "1.23.0", Some(44265)),
433+
434+
// `foo.rs` as an alternative to `foo/mod.rs`
435+
(active, non_modrs_mods, "1.24.0", Some(44660)),
433436
);
434437

435438
declare_features! (
@@ -1297,6 +1300,31 @@ fn contains_novel_literal(item: &ast::MetaItem) -> bool {
12971300
}
12981301
}
12991302

1303+
impl<'a> PostExpansionVisitor<'a> {
1304+
fn whole_crate_feature_gates(&mut self) {
1305+
for &(ident, span) in &*self.context.parse_sess.non_modrs_mods.borrow() {
1306+
if !span.allows_unstable() {
1307+
let cx = &self.context;
1308+
let level = GateStrength::Hard;
1309+
let has_feature = cx.features.non_modrs_mods;
1310+
let name = "non_modrs_mods";
1311+
debug!("gate_feature(feature = {:?}, span = {:?}); has? {}",
1312+
name, span, has_feature);
1313+
1314+
if !has_feature && !span.allows_unstable() {
1315+
leveled_feature_err(
1316+
cx.parse_sess, name, span, GateIssue::Language,
1317+
"mod statements in non-mod.rs files are unstable", level
1318+
)
1319+
.help(&format!("on stable builds, rename this file to {}{}mod.rs",
1320+
ident, path::MAIN_SEPARATOR))
1321+
.emit();
1322+
}
1323+
}
1324+
}
1325+
}
1326+
}
1327+
13001328
impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {
13011329
fn visit_attribute(&mut self, attr: &ast::Attribute) {
13021330
if !attr.span.allows_unstable() {
@@ -1843,7 +1871,9 @@ pub fn check_crate(krate: &ast::Crate,
18431871
parse_sess: sess,
18441872
plugin_attributes,
18451873
};
1846-
visit::walk_crate(&mut PostExpansionVisitor { context: &ctx }, krate);
1874+
let visitor = &mut PostExpansionVisitor { context: &ctx };
1875+
visitor.whole_crate_feature_gates();
1876+
visit::walk_crate(visitor, krate);
18471877
}
18481878

18491879
#[derive(Clone, Copy, PartialEq, Eq, Hash)]

src/libsyntax/parse/lexer/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1727,6 +1727,7 @@ mod tests {
17271727
included_mod_stack: RefCell::new(Vec::new()),
17281728
code_map: cm,
17291729
missing_fragment_specifiers: RefCell::new(HashSet::new()),
1730+
non_modrs_mods: RefCell::new(vec![]),
17301731
}
17311732
}
17321733

src/libsyntax/parse/mod.rs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,9 @@ pub struct ParseSess {
4747
pub unstable_features: UnstableFeatures,
4848
pub config: CrateConfig,
4949
pub missing_fragment_specifiers: RefCell<HashSet<Span>>,
50+
// Spans where a `mod foo;` statement was included in a non-mod.rs file.
51+
// These are used to issue errors if the non_modrs_mods feature is not enabled.
52+
pub non_modrs_mods: RefCell<Vec<(ast::Ident, Span)>>,
5053
/// Used to determine and report recursive mod inclusions
5154
included_mod_stack: RefCell<Vec<PathBuf>>,
5255
code_map: Rc<CodeMap>,
@@ -70,6 +73,7 @@ impl ParseSess {
7073
missing_fragment_specifiers: RefCell::new(HashSet::new()),
7174
included_mod_stack: RefCell::new(vec![]),
7275
code_map,
76+
non_modrs_mods: RefCell::new(vec![]),
7377
}
7478
}
7579

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

8791
#[derive(Copy, Clone)]
8892
pub enum DirectoryOwnership {
89-
Owned,
93+
Owned {
94+
// None if `mod.rs`, `Some("foo")` if we're in `foo.rs`
95+
relative: Option<ast::Ident>,
96+
},
9097
UnownedViaBlock,
9198
UnownedViaMod(bool /* legacy warnings? */),
9299
}

src/libsyntax/parse/parser.rs

Lines changed: 91 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -507,7 +507,10 @@ impl<'a> Parser<'a> {
507507
restrictions: Restrictions::empty(),
508508
obsolete_set: HashSet::new(),
509509
recurse_into_file_modules,
510-
directory: Directory { path: PathBuf::new(), ownership: DirectoryOwnership::Owned },
510+
directory: Directory {
511+
path: PathBuf::new(),
512+
ownership: DirectoryOwnership::Owned { relative: None }
513+
},
511514
root_module_name: None,
512515
expected_tokens: Vec::new(),
513516
token_cursor: TokenCursor {
@@ -5676,7 +5679,7 @@ impl<'a> Parser<'a> {
56765679
fn push_directory(&mut self, id: Ident, attrs: &[Attribute]) {
56775680
if let Some(path) = attr::first_attr_value_str_by_name(attrs, "path") {
56785681
self.directory.path.push(&path.as_str());
5679-
self.directory.ownership = DirectoryOwnership::Owned;
5682+
self.directory.ownership = DirectoryOwnership::Owned { relative: None };
56805683
} else {
56815684
self.directory.path.push(&id.name.as_str());
56825685
}
@@ -5687,10 +5690,28 @@ impl<'a> Parser<'a> {
56875690
}
56885691

56895692
/// Returns either a path to a module, or .
5690-
pub fn default_submod_path(id: ast::Ident, dir_path: &Path, codemap: &CodeMap) -> ModulePath {
5693+
pub fn default_submod_path(
5694+
id: ast::Ident,
5695+
relative: Option<ast::Ident>,
5696+
dir_path: &Path,
5697+
codemap: &CodeMap) -> ModulePath
5698+
{
5699+
// If we're in a foo.rs file instead of a mod.rs file,
5700+
// we need to look for submodules in
5701+
// `./foo/<id>.rs` and `./foo/<id>/mod.rs` rather than
5702+
// `./<id>.rs` and `./<id>/mod.rs`.
5703+
let relative_prefix_string;
5704+
let relative_prefix = if let Some(ident) = relative {
5705+
relative_prefix_string = format!("{}{}", ident.name.as_str(), path::MAIN_SEPARATOR);
5706+
&relative_prefix_string
5707+
} else {
5708+
""
5709+
};
5710+
56915711
let mod_name = id.to_string();
5692-
let default_path_str = format!("{}.rs", mod_name);
5693-
let secondary_path_str = format!("{}{}mod.rs", mod_name, path::MAIN_SEPARATOR);
5712+
let default_path_str = format!("{}{}.rs", relative_prefix, mod_name);
5713+
let secondary_path_str = format!("{}{}{}mod.rs",
5714+
relative_prefix, mod_name, path::MAIN_SEPARATOR);
56945715
let default_path = dir_path.join(&default_path_str);
56955716
let secondary_path = dir_path.join(&secondary_path_str);
56965717
let default_exists = codemap.file_exists(&default_path);
@@ -5699,12 +5720,16 @@ impl<'a> Parser<'a> {
56995720
let result = match (default_exists, secondary_exists) {
57005721
(true, false) => Ok(ModulePathSuccess {
57015722
path: default_path,
5702-
directory_ownership: DirectoryOwnership::UnownedViaMod(false),
5723+
directory_ownership: DirectoryOwnership::Owned {
5724+
relative: Some(id),
5725+
},
57035726
warn: false,
57045727
}),
57055728
(false, true) => Ok(ModulePathSuccess {
57065729
path: secondary_path,
5707-
directory_ownership: DirectoryOwnership::Owned,
5730+
directory_ownership: DirectoryOwnership::Owned {
5731+
relative: None,
5732+
},
57085733
warn: false,
57095734
}),
57105735
(false, false) => Err(Error::FileNotFoundForModule {
@@ -5735,55 +5760,78 @@ impl<'a> Parser<'a> {
57355760
if let Some(path) = Parser::submod_path_from_attr(outer_attrs, &self.directory.path) {
57365761
return Ok(ModulePathSuccess {
57375762
directory_ownership: match path.file_name().and_then(|s| s.to_str()) {
5738-
Some("mod.rs") => DirectoryOwnership::Owned,
5763+
Some("mod.rs") => DirectoryOwnership::Owned { relative: None },
5764+
Some(_) => {
5765+
DirectoryOwnership::Owned { relative: Some(id) }
5766+
}
57395767
_ => DirectoryOwnership::UnownedViaMod(true),
57405768
},
57415769
path,
57425770
warn: false,
57435771
});
57445772
}
57455773

5746-
let paths = Parser::default_submod_path(id, &self.directory.path, self.sess.codemap());
5774+
let relative = match self.directory.ownership {
5775+
DirectoryOwnership::Owned { relative } => {
5776+
// Push the usage onto the list of non-mod.rs mod uses.
5777+
// This is used later for feature-gate error reporting.
5778+
if let Some(cur_file_ident) = relative {
5779+
self.sess
5780+
.non_modrs_mods.borrow_mut()
5781+
.push((cur_file_ident, id_sp));
5782+
}
5783+
relative
5784+
},
5785+
DirectoryOwnership::UnownedViaBlock |
5786+
DirectoryOwnership::UnownedViaMod(_) => None,
5787+
};
5788+
let paths = Parser::default_submod_path(
5789+
id, relative, &self.directory.path, self.sess.codemap());
57475790

5748-
if let DirectoryOwnership::UnownedViaBlock = self.directory.ownership {
5749-
let msg =
5750-
"Cannot declare a non-inline module inside a block unless it has a path attribute";
5751-
let mut err = self.diagnostic().struct_span_err(id_sp, msg);
5752-
if paths.path_exists {
5753-
let msg = format!("Maybe `use` the module `{}` instead of redeclaring it",
5754-
paths.name);
5755-
err.span_note(id_sp, &msg);
5756-
}
5757-
Err(err)
5758-
} else if let DirectoryOwnership::UnownedViaMod(warn) = self.directory.ownership {
5759-
if warn {
5760-
if let Ok(result) = paths.result {
5761-
return Ok(ModulePathSuccess { warn: true, ..result });
5791+
match self.directory.ownership {
5792+
DirectoryOwnership::Owned { .. } => {
5793+
paths.result.map_err(|err| self.span_fatal_err(id_sp, err))
5794+
},
5795+
DirectoryOwnership::UnownedViaBlock => {
5796+
let msg =
5797+
"Cannot declare a non-inline module inside a block \
5798+
unless it has a path attribute";
5799+
let mut err = self.diagnostic().struct_span_err(id_sp, msg);
5800+
if paths.path_exists {
5801+
let msg = format!("Maybe `use` the module `{}` instead of redeclaring it",
5802+
paths.name);
5803+
err.span_note(id_sp, &msg);
57625804
}
5805+
Err(err)
57635806
}
5764-
let mut err = self.diagnostic().struct_span_err(id_sp,
5765-
"cannot declare a new module at this location");
5766-
if id_sp != syntax_pos::DUMMY_SP {
5767-
let src_path = PathBuf::from(self.sess.codemap().span_to_filename(id_sp));
5768-
if let Some(stem) = src_path.file_stem() {
5769-
let mut dest_path = src_path.clone();
5770-
dest_path.set_file_name(stem);
5771-
dest_path.push("mod.rs");
5807+
DirectoryOwnership::UnownedViaMod(warn) => {
5808+
if warn {
5809+
if let Ok(result) = paths.result {
5810+
return Ok(ModulePathSuccess { warn: true, ..result });
5811+
}
5812+
}
5813+
let mut err = self.diagnostic().struct_span_err(id_sp,
5814+
"cannot declare a new module at this location");
5815+
if id_sp != syntax_pos::DUMMY_SP {
5816+
let src_path = PathBuf::from(self.sess.codemap().span_to_filename(id_sp));
5817+
if let Some(stem) = src_path.file_stem() {
5818+
let mut dest_path = src_path.clone();
5819+
dest_path.set_file_name(stem);
5820+
dest_path.push("mod.rs");
5821+
err.span_note(id_sp,
5822+
&format!("maybe move this module `{}` to its own \
5823+
directory via `{}`", src_path.to_string_lossy(),
5824+
dest_path.to_string_lossy()));
5825+
}
5826+
}
5827+
if paths.path_exists {
57725828
err.span_note(id_sp,
5773-
&format!("maybe move this module `{}` to its own \
5774-
directory via `{}`", src_path.to_string_lossy(),
5775-
dest_path.to_string_lossy()));
5829+
&format!("... or maybe `use` the module `{}` instead \
5830+
of possibly redeclaring it",
5831+
paths.name));
57765832
}
5833+
Err(err)
57775834
}
5778-
if paths.path_exists {
5779-
err.span_note(id_sp,
5780-
&format!("... or maybe `use` the module `{}` instead \
5781-
of possibly redeclaring it",
5782-
paths.name));
5783-
}
5784-
Err(err)
5785-
} else {
5786-
paths.result.map_err(|err| self.span_fatal_err(id_sp, err))
57875835
}
57885836
}
57895837

src/test/compile-fail/directory_ownership/backcompat-warnings.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,7 @@
88
// option. This file may not be copied, modified, or distributed
99
// except according to those terms.
1010

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

1413
#[path="mod_file_not_owning_aux3.rs"]
1514
mod foo;

src/test/compile-fail/directory_ownership/foo/compiletest-ignore-dir

Whitespace-only changes.
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.

src/test/compile-fail/directory_ownership/mod_file_not_owning.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
// option. This file may not be copied, modified, or distributed
99
// except according to those terms.
1010

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

1313
mod mod_file_not_owning_aux1;
1414

src/test/compile-fail/directory_ownership/mod_file_not_owning_aux1/compiletest-ignore-dir

Whitespace-only changes.
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.

src/test/compile-fail/directory_ownership/unowned_mod_with_path.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
// option. This file may not be copied, modified, or distributed
99
// except according to those terms.
1010

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

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

0 commit comments

Comments
 (0)