Skip to content

Commit ed15677

Browse files
committed
auto merge of #14251 : alexcrichton/rust/hierarchy, r=huonw
This is an implementation of RFC 16. A module can now only be loaded if the module declaring `mod name;` "owns" the current directory. A module is considered as owning its directory if it meets one of the following criteria: * It is the top-level crate file * It is a `mod.rs` file * It was loaded via `#[path]` * It was loaded via `include!` * The module was declared via an inline `mod foo { ... }` statement For example, this directory structure is now invalid // lib.rs mod foo; // foo.rs mod bar; // bar.rs; fn bar() {} With this change `foo.rs` must be renamed to `foo/mod.rs`, and `bar.rs` must be renamed to `foo/bar.rs`. This makes it clear that `bar` is a submodule of `foo`, and can only be accessed through `foo`. RFC: 0016-module-file-system-hierarchy Closes #14180 [breaking-change]
2 parents 5d2eddd + 4e9e091 commit ed15677

File tree

12 files changed

+93
-8
lines changed

12 files changed

+93
-8
lines changed
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.

Diff for: src/libsyntax/ext/source_util.rs

+2
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,8 @@ pub fn expand_include(cx: &mut ExtCtxt, sp: Span, tts: &[ast::TokenTree])
9595
&res_rel_file(cx,
9696
sp,
9797
&Path::new(file)),
98+
true,
99+
None,
98100
sp);
99101
base::MacExpr::new(p.parse_expr())
100102
}

Diff for: src/libsyntax/parse/mod.rs

+6-1
Original file line numberDiff line numberDiff line change
@@ -184,8 +184,13 @@ pub fn new_parser_from_file<'a>(sess: &'a ParseSess,
184184
pub fn new_sub_parser_from_file<'a>(sess: &'a ParseSess,
185185
cfg: ast::CrateConfig,
186186
path: &Path,
187+
owns_directory: bool,
188+
module_name: Option<StrBuf>,
187189
sp: Span) -> Parser<'a> {
188-
filemap_to_parser(sess, file_to_filemap(sess, path, Some(sp)), cfg)
190+
let mut p = filemap_to_parser(sess, file_to_filemap(sess, path, Some(sp)), cfg);
191+
p.owns_directory = owns_directory;
192+
p.root_module_name = module_name;
193+
p
189194
}
190195

191196
/// Given a filemap and config, return a parser

Diff for: src/libsyntax/parse/parser.rs

+44-6
Original file line numberDiff line numberDiff line change
@@ -313,6 +313,8 @@ pub fn Parser<'a>(
313313
obsolete_set: HashSet::new(),
314314
mod_path_stack: Vec::new(),
315315
open_braces: Vec::new(),
316+
owns_directory: true,
317+
root_module_name: None,
316318
}
317319
}
318320

@@ -342,6 +344,13 @@ pub struct Parser<'a> {
342344
pub mod_path_stack: Vec<InternedString>,
343345
/// Stack of spans of open delimiters. Used for error message.
344346
pub open_braces: Vec<Span>,
347+
/// Flag if this parser "owns" the directory that it is currently parsing
348+
/// in. This will affect how nested files are looked up.
349+
pub owns_directory: bool,
350+
/// Name of the root module this parser originated from. If `None`, then the
351+
/// name is not known. This does not change while the parser is descending
352+
/// into modules, and sub-parsers have new values for this name.
353+
pub root_module_name: Option<StrBuf>,
345354
}
346355

347356
fn is_plain_ident_or_underscore(t: &token::Token) -> bool {
@@ -4179,9 +4188,12 @@ impl<'a> Parser<'a> {
41794188
self.push_mod_path(id, outer_attrs);
41804189
self.expect(&token::LBRACE);
41814190
let mod_inner_lo = self.span.lo;
4191+
let old_owns_directory = self.owns_directory;
4192+
self.owns_directory = true;
41824193
let (inner, next) = self.parse_inner_attrs_and_next();
41834194
let m = self.parse_mod_items(token::RBRACE, next, mod_inner_lo);
41844195
self.expect(&token::RBRACE);
4196+
self.owns_directory = old_owns_directory;
41854197
self.pop_mod_path();
41864198
(id, ItemMod(m), Some(inner))
41874199
}
@@ -4211,21 +4223,42 @@ impl<'a> Parser<'a> {
42114223
prefix.pop();
42124224
let mod_path = Path::new(".").join_many(self.mod_path_stack.as_slice());
42134225
let dir_path = prefix.join(&mod_path);
4214-
let file_path = match ::attr::first_attr_value_str_by_name(
4226+
let mod_string = token::get_ident(id);
4227+
let (file_path, owns_directory) = match ::attr::first_attr_value_str_by_name(
42154228
outer_attrs, "path") {
4216-
Some(d) => dir_path.join(d),
4229+
Some(d) => (dir_path.join(d), true),
42174230
None => {
4218-
let mod_string = token::get_ident(id);
42194231
let mod_name = mod_string.get().to_owned();
42204232
let default_path_str = mod_name + ".rs";
42214233
let secondary_path_str = mod_name + "/mod.rs";
42224234
let default_path = dir_path.join(default_path_str.as_slice());
42234235
let secondary_path = dir_path.join(secondary_path_str.as_slice());
42244236
let default_exists = default_path.exists();
42254237
let secondary_exists = secondary_path.exists();
4238+
4239+
if !self.owns_directory {
4240+
self.span_err(id_sp,
4241+
"cannot declare a new module at this location");
4242+
let this_module = match self.mod_path_stack.last() {
4243+
Some(name) => name.get().to_strbuf(),
4244+
None => self.root_module_name.get_ref().clone(),
4245+
};
4246+
self.span_note(id_sp,
4247+
format!("maybe move this module `{0}` \
4248+
to its own directory via \
4249+
`{0}/mod.rs`", this_module));
4250+
if default_exists || secondary_exists {
4251+
self.span_note(id_sp,
4252+
format!("... or maybe `use` the module \
4253+
`{}` instead of possibly \
4254+
redeclaring it", mod_name));
4255+
}
4256+
self.abort_if_errors();
4257+
}
4258+
42264259
match (default_exists, secondary_exists) {
4227-
(true, false) => default_path,
4228-
(false, true) => secondary_path,
4260+
(true, false) => (default_path, false),
4261+
(false, true) => (secondary_path, true),
42294262
(false, false) => {
42304263
self.span_fatal(id_sp, format!("file not found for module `{}`", mod_name));
42314264
}
@@ -4238,11 +4271,14 @@ impl<'a> Parser<'a> {
42384271
}
42394272
};
42404273

4241-
self.eval_src_mod_from_path(file_path, id_sp)
4274+
self.eval_src_mod_from_path(file_path, owns_directory,
4275+
mod_string.get().to_strbuf(), id_sp)
42424276
}
42434277

42444278
fn eval_src_mod_from_path(&mut self,
42454279
path: Path,
4280+
owns_directory: bool,
4281+
name: StrBuf,
42464282
id_sp: Span) -> (ast::Item_, Vec<ast::Attribute> ) {
42474283
let mut included_mod_stack = self.sess.included_mod_stack.borrow_mut();
42484284
match included_mod_stack.iter().position(|p| *p == path) {
@@ -4265,6 +4301,8 @@ impl<'a> Parser<'a> {
42654301
new_sub_parser_from_file(self.sess,
42664302
self.cfg.clone(),
42674303
&path,
4304+
owns_directory,
4305+
Some(name),
42684306
id_sp);
42694307
let mod_inner_lo = p0.span.lo;
42704308
let (mod_attrs, next) = p0.parse_inner_attrs_and_next();

Diff for: src/test/compile-fail/circular_modules_main.rs

+1-1
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-
11+
#[path = "circular_modules_hello.rs"]
1212
mod circular_modules_hello; //~ERROR: circular modules
1313

1414
pub fn hi_str() -> StrBuf {

Diff for: src/test/compile-fail/mod_file_not_owning.rs

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// Copyright 2014 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.
10+
11+
// error-pattern: cannot declare a new module at this location
12+
13+
mod mod_file_not_owning_aux1;
14+
15+
fn main() {}

Diff for: src/test/compile-fail/mod_file_not_owning_aux1.rs

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
// Copyright 2014 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.
10+
11+
// ignore-test this is not a test
12+
13+
mod mod_file_not_owning_aux2;

Diff for: src/test/compile-fail/mod_file_not_owning_aux2.rs

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
// Copyright 2014 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.
10+
11+
// ignore-test this is not a test

Diff for: src/test/run-pass/mod_dir_simple/load_another_mod.rs

+1
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,5 @@
88
// option. This file may not be copied, modified, or distributed
99
// except according to those terms.
1010

11+
#[path = "test.rs"]
1112
pub mod test;

0 commit comments

Comments
 (0)