about summary refs log tree commit diff
diff options
context:
space:
mode:
authorAlex Crichton <alex@alexcrichton.com>2014-05-16 14:23:04 -0700
committerAlex Crichton <alex@alexcrichton.com>2014-05-17 01:01:47 -0700
commit4e9e091e91ea2ad8a6f45a9b20ff331d4bca7a23 (patch)
treec01cb3c61732225abd28d5eb8991537ca7ad30b5
parent25c54226c3e7dd6f59cf2e92238a4d79d8b0128d (diff)
downloadrust-4e9e091e91ea2ad8a6f45a9b20ff331d4bca7a23.tar.gz
rust-4e9e091e91ea2ad8a6f45a9b20ff331d4bca7a23.zip
syntax: Tighten search paths for inner modules
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]
-rw-r--r--src/libregex/parse/mod.rs (renamed from src/libregex/parse.rs)0
-rw-r--r--src/libregex/parse/unicode.rs (renamed from src/libregex/unicode.rs)0
-rw-r--r--src/libsyntax/ext/deriving/generic/mod.rs (renamed from src/libsyntax/ext/deriving/generic.rs)0
-rw-r--r--src/libsyntax/ext/deriving/generic/ty.rs (renamed from src/libsyntax/ext/deriving/ty.rs)0
-rw-r--r--src/libsyntax/ext/source_util.rs2
-rw-r--r--src/libsyntax/parse/mod.rs7
-rw-r--r--src/libsyntax/parse/parser.rs50
-rw-r--r--src/test/compile-fail/circular_modules_main.rs2
-rw-r--r--src/test/compile-fail/mod_file_not_owning.rs15
-rw-r--r--src/test/compile-fail/mod_file_not_owning_aux1.rs13
-rw-r--r--src/test/compile-fail/mod_file_not_owning_aux2.rs11
-rw-r--r--src/test/run-pass/mod_dir_simple/load_another_mod.rs1
12 files changed, 93 insertions, 8 deletions
diff --git a/src/libregex/parse.rs b/src/libregex/parse/mod.rs
index a695da9fa16..a695da9fa16 100644
--- a/src/libregex/parse.rs
+++ b/src/libregex/parse/mod.rs
diff --git a/src/libregex/unicode.rs b/src/libregex/parse/unicode.rs
index c263827dab8..c263827dab8 100644
--- a/src/libregex/unicode.rs
+++ b/src/libregex/parse/unicode.rs
diff --git a/src/libsyntax/ext/deriving/generic.rs b/src/libsyntax/ext/deriving/generic/mod.rs
index 6df4da89402..6df4da89402 100644
--- a/src/libsyntax/ext/deriving/generic.rs
+++ b/src/libsyntax/ext/deriving/generic/mod.rs
diff --git a/src/libsyntax/ext/deriving/ty.rs b/src/libsyntax/ext/deriving/generic/ty.rs
index 602245b4c47..602245b4c47 100644
--- a/src/libsyntax/ext/deriving/ty.rs
+++ b/src/libsyntax/ext/deriving/generic/ty.rs
diff --git a/src/libsyntax/ext/source_util.rs b/src/libsyntax/ext/source_util.rs
index 6e7e72bd2e8..6bc08741c07 100644
--- a/src/libsyntax/ext/source_util.rs
+++ b/src/libsyntax/ext/source_util.rs
@@ -95,6 +95,8 @@ pub fn expand_include(cx: &mut ExtCtxt, sp: Span, tts: &[ast::TokenTree])
                                         &res_rel_file(cx,
                                                       sp,
                                                       &Path::new(file)),
+                                        true,
+                                        None,
                                         sp);
     base::MacExpr::new(p.parse_expr())
 }
diff --git a/src/libsyntax/parse/mod.rs b/src/libsyntax/parse/mod.rs
index d8a9f69e293..8e139b049c5 100644
--- a/src/libsyntax/parse/mod.rs
+++ b/src/libsyntax/parse/mod.rs
@@ -184,8 +184,13 @@ pub fn new_parser_from_file<'a>(sess: &'a ParseSess,
 pub fn new_sub_parser_from_file<'a>(sess: &'a ParseSess,
                                     cfg: ast::CrateConfig,
                                     path: &Path,
+                                    owns_directory: bool,
+                                    module_name: Option<StrBuf>,
                                     sp: Span) -> Parser<'a> {
-    filemap_to_parser(sess, file_to_filemap(sess, path, Some(sp)), cfg)
+    let mut p = filemap_to_parser(sess, file_to_filemap(sess, path, Some(sp)), cfg);
+    p.owns_directory = owns_directory;
+    p.root_module_name = module_name;
+    p
 }
 
 /// Given a filemap and config, return a parser
diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs
index 92e5f8da6aa..c42febcd607 100644
--- a/src/libsyntax/parse/parser.rs
+++ b/src/libsyntax/parse/parser.rs
@@ -313,6 +313,8 @@ pub fn Parser<'a>(
         obsolete_set: HashSet::new(),
         mod_path_stack: Vec::new(),
         open_braces: Vec::new(),
+        owns_directory: true,
+        root_module_name: None,
     }
 }
 
@@ -342,6 +344,13 @@ pub struct Parser<'a> {
     pub mod_path_stack: Vec<InternedString>,
     /// Stack of spans of open delimiters. Used for error message.
     pub open_braces: Vec<Span>,
+    /// Flag if this parser "owns" the directory that it is currently parsing
+    /// in. This will affect how nested files are looked up.
+    pub owns_directory: 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.
+    pub root_module_name: Option<StrBuf>,
 }
 
 fn is_plain_ident_or_underscore(t: &token::Token) -> bool {
@@ -4179,9 +4188,12 @@ impl<'a> Parser<'a> {
             self.push_mod_path(id, outer_attrs);
             self.expect(&token::LBRACE);
             let mod_inner_lo = self.span.lo;
+            let old_owns_directory = self.owns_directory;
+            self.owns_directory = true;
             let (inner, next) = self.parse_inner_attrs_and_next();
             let m = self.parse_mod_items(token::RBRACE, next, mod_inner_lo);
             self.expect(&token::RBRACE);
+            self.owns_directory = old_owns_directory;
             self.pop_mod_path();
             (id, ItemMod(m), Some(inner))
         }
@@ -4211,11 +4223,11 @@ impl<'a> Parser<'a> {
         prefix.pop();
         let mod_path = Path::new(".").join_many(self.mod_path_stack.as_slice());
         let dir_path = prefix.join(&mod_path);
-        let file_path = match ::attr::first_attr_value_str_by_name(
+        let mod_string = token::get_ident(id);
+        let (file_path, owns_directory) = match ::attr::first_attr_value_str_by_name(
                 outer_attrs, "path") {
-            Some(d) => dir_path.join(d),
+            Some(d) => (dir_path.join(d), true),
             None => {
-                let mod_string = token::get_ident(id);
                 let mod_name = mod_string.get().to_owned();
                 let default_path_str = mod_name + ".rs";
                 let secondary_path_str = mod_name + "/mod.rs";
@@ -4223,9 +4235,30 @@ impl<'a> Parser<'a> {
                 let secondary_path = dir_path.join(secondary_path_str.as_slice());
                 let default_exists = default_path.exists();
                 let secondary_exists = secondary_path.exists();
+
+                if !self.owns_directory {
+                    self.span_err(id_sp,
+                                  "cannot declare a new module at this location");
+                    let this_module = match self.mod_path_stack.last() {
+                        Some(name) => name.get().to_strbuf(),
+                        None => self.root_module_name.get_ref().clone(),
+                    };
+                    self.span_note(id_sp,
+                                   format!("maybe move this module `{0}` \
+                                            to its own directory via \
+                                            `{0}/mod.rs`", this_module));
+                    if default_exists || secondary_exists {
+                        self.span_note(id_sp,
+                                       format!("... or maybe `use` the module \
+                                                `{}` instead of possibly \
+                                                redeclaring it", mod_name));
+                    }
+                    self.abort_if_errors();
+                }
+
                 match (default_exists, secondary_exists) {
-                    (true, false) => default_path,
-                    (false, true) => secondary_path,
+                    (true, false) => (default_path, false),
+                    (false, true) => (secondary_path, true),
                     (false, false) => {
                         self.span_fatal(id_sp, format!("file not found for module `{}`", mod_name));
                     }
@@ -4238,11 +4271,14 @@ impl<'a> Parser<'a> {
             }
         };
 
-        self.eval_src_mod_from_path(file_path, id_sp)
+        self.eval_src_mod_from_path(file_path, owns_directory,
+                                    mod_string.get().to_strbuf(), id_sp)
     }
 
     fn eval_src_mod_from_path(&mut self,
                               path: Path,
+                              owns_directory: bool,
+                              name: StrBuf,
                               id_sp: Span) -> (ast::Item_, Vec<ast::Attribute> ) {
         let mut included_mod_stack = self.sess.included_mod_stack.borrow_mut();
         match included_mod_stack.iter().position(|p| *p == path) {
@@ -4265,6 +4301,8 @@ impl<'a> Parser<'a> {
             new_sub_parser_from_file(self.sess,
                                      self.cfg.clone(),
                                      &path,
+                                     owns_directory,
+                                     Some(name),
                                      id_sp);
         let mod_inner_lo = p0.span.lo;
         let (mod_attrs, next) = p0.parse_inner_attrs_and_next();
diff --git a/src/test/compile-fail/circular_modules_main.rs b/src/test/compile-fail/circular_modules_main.rs
index bc9f3247db7..7296ea655c2 100644
--- a/src/test/compile-fail/circular_modules_main.rs
+++ b/src/test/compile-fail/circular_modules_main.rs
@@ -8,7 +8,7 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-
+#[path = "circular_modules_hello.rs"]
 mod circular_modules_hello; //~ERROR: circular modules
 
 pub fn hi_str() -> StrBuf {
diff --git a/src/test/compile-fail/mod_file_not_owning.rs b/src/test/compile-fail/mod_file_not_owning.rs
new file mode 100644
index 00000000000..adbcedd91f2
--- /dev/null
+++ b/src/test/compile-fail/mod_file_not_owning.rs
@@ -0,0 +1,15 @@
+// Copyright 2014 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.
+
+// error-pattern: cannot declare a new module at this location
+
+mod mod_file_not_owning_aux1;
+
+fn main() {}
diff --git a/src/test/compile-fail/mod_file_not_owning_aux1.rs b/src/test/compile-fail/mod_file_not_owning_aux1.rs
new file mode 100644
index 00000000000..2d522be6dc5
--- /dev/null
+++ b/src/test/compile-fail/mod_file_not_owning_aux1.rs
@@ -0,0 +1,13 @@
+// Copyright 2014 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.
+
+// ignore-test this is not a test
+
+mod mod_file_not_owning_aux2;
diff --git a/src/test/compile-fail/mod_file_not_owning_aux2.rs b/src/test/compile-fail/mod_file_not_owning_aux2.rs
new file mode 100644
index 00000000000..41401d640f6
--- /dev/null
+++ b/src/test/compile-fail/mod_file_not_owning_aux2.rs
@@ -0,0 +1,11 @@
+// Copyright 2014 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.
+
+// ignore-test this is not a test
diff --git a/src/test/run-pass/mod_dir_simple/load_another_mod.rs b/src/test/run-pass/mod_dir_simple/load_another_mod.rs
index c11b1e8c074..ca45864a5a1 100644
--- a/src/test/run-pass/mod_dir_simple/load_another_mod.rs
+++ b/src/test/run-pass/mod_dir_simple/load_another_mod.rs
@@ -8,4 +8,5 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
+#[path = "test.rs"]
 pub mod test;