diff options
| author | Taylor Cramer <cramertj@google.com> | 2017-11-27 18:14:24 -0800 |
|---|---|---|
| committer | Taylor Cramer <cramertj@google.com> | 2017-12-19 14:58:51 -0800 |
| commit | 07f51fb8684e1ea0a3859234d360814093d891bd (patch) | |
| tree | 2ba4570ab672999453ebbf0d15610392a086d044 /src/libsyntax | |
| parent | b39c4bc12358078f77ddd01288b24252f757f37d (diff) | |
| download | rust-07f51fb8684e1ea0a3859234d360814093d891bd.tar.gz rust-07f51fb8684e1ea0a3859234d360814093d891bd.zip | |
Implement non-mod.rs mod statements
Diffstat (limited to 'src/libsyntax')
| -rw-r--r-- | src/libsyntax/ext/base.rs | 2 | ||||
| -rw-r--r-- | src/libsyntax/ext/expand.rs | 10 | ||||
| -rw-r--r-- | src/libsyntax/ext/source_util.rs | 2 | ||||
| -rw-r--r-- | src/libsyntax/feature_gate.rs | 34 | ||||
| -rw-r--r-- | src/libsyntax/parse/lexer/mod.rs | 1 | ||||
| -rw-r--r-- | src/libsyntax/parse/mod.rs | 9 | ||||
| -rw-r--r-- | src/libsyntax/parse/parser.rs | 132 |
7 files changed, 140 insertions, 50 deletions
diff --git a/src/libsyntax/ext/base.rs b/src/libsyntax/ext/base.rs index 0eb4ec23d08..d53335f5be7 100644 --- a/src/libsyntax/ext/base.rs +++ b/src/libsyntax/ext/base.rs @@ -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(), } diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs index f2346ed19de..07ea6a09086 100644 --- a/src/libsyntax/ext/expand.rs +++ b/src/libsyntax/ext/expand.rs @@ -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()); @@ -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; diff --git a/src/libsyntax/ext/source_util.rs b/src/libsyntax/ext/source_util.rs index 2a80686aa0f..4da485fc9a4 100644 --- a/src/libsyntax/ext/source_util.rs +++ b/src/libsyntax/ext/source_util.rs @@ -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> { diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs index ba534676324..a6946d18d67 100644 --- a/src/libsyntax/feature_gate.rs +++ b/src/libsyntax/feature_gate.rs @@ -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) => {{ @@ -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! ( @@ -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() { @@ -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)] diff --git a/src/libsyntax/parse/lexer/mod.rs b/src/libsyntax/parse/lexer/mod.rs index 1e84fb98a66..9828995362a 100644 --- a/src/libsyntax/parse/lexer/mod.rs +++ b/src/libsyntax/parse/lexer/mod.rs @@ -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![]), } } diff --git a/src/libsyntax/parse/mod.rs b/src/libsyntax/parse/mod.rs index 4d435665d3c..89a54989f96 100644 --- a/src/libsyntax/parse/mod.rs +++ b/src/libsyntax/parse/mod.rs @@ -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>, @@ -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![]), } } @@ -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? */), } diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index c3dd17e8775..d6f09cd85d7 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -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 { @@ -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()); } @@ -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); @@ -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 { @@ -5790,7 +5815,10 @@ 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, @@ -5798,49 +5826,69 @@ impl<'a> Parser<'a> { }); } - 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)) } } |
