diff --git a/src/librustc/lint/builtin.rs b/src/librustc/lint/builtin.rs index 82a46f76401d5..c369bc10e9482 100644 --- a/src/librustc/lint/builtin.rs +++ b/src/librustc/lint/builtin.rs @@ -204,6 +204,13 @@ declare_lint! { "detects extra requirements in impls that were erroneously allowed" } +declare_lint! { + pub LEGACY_DIRECTORY_OWNERSHIP, + Warn, + "non-inline, non-`#[path]` modules (e.g. `mod foo;`) were erroneously allowed in some files \ + not named `mod.rs`" +} + /// Does nothing as a lint pass, but registers some `Lint`s /// which are used by other parts of the compiler. #[derive(Copy, Clone)] @@ -242,7 +249,8 @@ impl LintPass for HardwiredLints { LIFETIME_UNDERSCORE, SAFE_EXTERN_STATICS, PATTERNS_IN_FNS_WITHOUT_BODY, - EXTRA_REQUIREMENT_IN_IMPL + EXTRA_REQUIREMENT_IN_IMPL, + LEGACY_DIRECTORY_OWNERSHIP ) } } diff --git a/src/librustc_lint/lib.rs b/src/librustc_lint/lib.rs index 114c0ea556ef5..1a3ea5db871eb 100644 --- a/src/librustc_lint/lib.rs +++ b/src/librustc_lint/lib.rs @@ -232,6 +232,10 @@ pub fn register_builtins(store: &mut lint::LintStore, sess: Option<&Session>) { id: LintId::of(EXTRA_REQUIREMENT_IN_IMPL), reference: "issue #37166 ", }, + FutureIncompatibleInfo { + id: LintId::of(LEGACY_DIRECTORY_OWNERSHIP), + reference: "issue #37872 ", + }, ]); // Register renamed and removed lints diff --git a/src/librustc_passes/ast_validation.rs b/src/librustc_passes/ast_validation.rs index 89c3efaafcdcc..105155d75aba6 100644 --- a/src/librustc_passes/ast_validation.rs +++ b/src/librustc_passes/ast_validation.rs @@ -207,6 +207,13 @@ impl<'a> Visitor for AstValidator<'a> { ItemKind::Mod(_) => { // Ensure that `path` attributes on modules are recorded as used (c.f. #35584). attr::first_attr_value_str_by_name(&item.attrs, "path"); + if let Some(attr) = + item.attrs.iter().find(|attr| attr.name() == "warn_directory_ownership") { + let lint = lint::builtin::LEGACY_DIRECTORY_OWNERSHIP; + let msg = "cannot declare a new module at this location"; + self.session.add_lint(lint, item.id, item.span, msg.to_string()); + attr::mark_used(attr); + } } ItemKind::Union(ref vdata, _) => { if !vdata.is_struct() { diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs index 3e8f118ce62de..fd6cae1e1b668 100644 --- a/src/libsyntax/ext/expand.rs +++ b/src/libsyntax/ext/expand.rs @@ -790,7 +790,7 @@ impl<'a, 'b> Folder for InvocationCollector<'a, 'b> { PathBuf::from(self.cx.parse_sess.codemap().span_to_filename(inner)); let directory_ownership = match path.file_name().unwrap().to_str() { Some("mod.rs") => DirectoryOwnership::Owned, - _ => DirectoryOwnership::UnownedViaMod, + _ => DirectoryOwnership::UnownedViaMod(false), }; path.pop(); module.directory = path; diff --git a/src/libsyntax/parse/mod.rs b/src/libsyntax/parse/mod.rs index f969e45b83a8e..bfaf00a3d3f08 100644 --- a/src/libsyntax/parse/mod.rs +++ b/src/libsyntax/parse/mod.rs @@ -86,7 +86,7 @@ pub struct Directory { pub enum DirectoryOwnership { Owned, UnownedViaBlock, - UnownedViaMod, + UnownedViaMod(bool /* legacy warnings? */), } // a bunch of utility functions of the form parse__from_ diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index ee69125ffae83..b00e6b5d58f04 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -38,7 +38,7 @@ use ast::{Ty, TyKind, TypeBinding, TyParam, TyParamBounds}; use ast::{ViewPath, ViewPathGlob, ViewPathList, ViewPathSimple}; use ast::{Visibility, WhereClause}; use ast::{BinOpKind, UnOp}; -use ast; +use {ast, attr}; use codemap::{self, CodeMap, Spanned, spanned, respan}; use syntax_pos::{self, Span, BytePos, mk_sp}; use errors::{self, DiagnosticBuilder}; @@ -243,6 +243,7 @@ pub struct ModulePath { pub struct ModulePathSuccess { pub path: PathBuf, pub directory_ownership: DirectoryOwnership, + warn: bool, } pub struct ModulePathError { @@ -5268,10 +5269,25 @@ impl<'a> Parser<'a> { self.bump(); if in_cfg { // This mod is in an external file. Let's go get it! - let ModulePathSuccess { path, directory_ownership } = + let ModulePathSuccess { path, directory_ownership, warn } = self.submod_path(id, &outer_attrs, id_span)?; - let (module, attrs) = + let (module, mut attrs) = self.eval_src_mod(path, directory_ownership, id.to_string(), id_span)?; + if warn { + let attr = ast::Attribute { + id: attr::mk_attr_id(), + style: ast::AttrStyle::Outer, + value: ast::MetaItem { + name: Symbol::intern("warn_directory_ownership"), + node: ast::MetaItemKind::Word, + span: syntax_pos::DUMMY_SP, + }, + is_sugared_doc: false, + span: syntax_pos::DUMMY_SP, + }; + attr::mark_known(&attr); + attrs.push(attr); + } Ok((id, module, Some(attrs))) } else { let placeholder = ast::Mod { inner: syntax_pos::DUMMY_SP, items: Vec::new() }; @@ -5290,7 +5306,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") { + 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; } else { @@ -5299,7 +5315,7 @@ impl<'a> Parser<'a> { } pub fn submod_path_from_attr(attrs: &[ast::Attribute], dir_path: &Path) -> Option { - ::attr::first_attr_value_str_by_name(attrs, "path").map(|d| dir_path.join(&*d.as_str())) + attr::first_attr_value_str_by_name(attrs, "path").map(|d| dir_path.join(&*d.as_str())) } /// Returns either a path to a module, or . @@ -5316,11 +5332,13 @@ impl<'a> Parser<'a> { let result = match (default_exists, secondary_exists) { (true, false) => Ok(ModulePathSuccess { path: default_path, - directory_ownership: DirectoryOwnership::UnownedViaMod, + directory_ownership: DirectoryOwnership::UnownedViaMod(false), + warn: false, }), (false, true) => Ok(ModulePathSuccess { path: secondary_path, directory_ownership: DirectoryOwnership::Owned, + warn: false, }), (false, false) => Err(ModulePathError { err_msg: format!("file not found for module `{}`", mod_name), @@ -5353,9 +5371,10 @@ impl<'a> Parser<'a> { return Ok(ModulePathSuccess { directory_ownership: match path.file_name().and_then(|s| s.to_str()) { Some("mod.rs") => DirectoryOwnership::Owned, - _ => DirectoryOwnership::UnownedViaMod, + _ => DirectoryOwnership::UnownedViaMod(true), }, path: path, + warn: false, }); } @@ -5371,7 +5390,12 @@ impl<'a> Parser<'a> { err.span_note(id_sp, &msg); } return Err(err); - } else if let DirectoryOwnership::UnownedViaMod = self.directory.ownership { + } else if let DirectoryOwnership::UnownedViaMod(warn) = self.directory.ownership { + 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"); let this_module = match self.directory.path.file_name() { @@ -5387,8 +5411,10 @@ impl<'a> Parser<'a> { &format!("... or maybe `use` the module `{}` instead \ of possibly redeclaring it", paths.name)); - } - return Err(err); + return Err(err); + } else { + return Err(err); + }; } match paths.result { diff --git a/src/test/compile-fail/directory_ownership/backcompat-warnings.rs b/src/test/compile-fail/directory_ownership/backcompat-warnings.rs new file mode 100644 index 0000000000000..75e3426a39935 --- /dev/null +++ b/src/test/compile-fail/directory_ownership/backcompat-warnings.rs @@ -0,0 +1,21 @@ +// Copyright 2016 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 or the MIT license +// , 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 +// error-pattern: will become a hard error +// error-pattern: compilation successful + +#![feature(rustc_attrs)] + +#[path="mod_file_not_owning_aux3.rs"] +mod foo; + +#[rustc_error] +fn main() {} diff --git a/src/test/compile-fail/directory_ownership/mod_file_not_owning_aux3.rs b/src/test/compile-fail/directory_ownership/mod_file_not_owning_aux3.rs new file mode 100644 index 0000000000000..3a164fd55d927 --- /dev/null +++ b/src/test/compile-fail/directory_ownership/mod_file_not_owning_aux3.rs @@ -0,0 +1,13 @@ +// Copyright 2016 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 or the MIT license +// , 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;