about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--book/src/lint_configuration.md10
-rw-r--r--clippy_lints/src/enum_variants.rs31
-rw-r--r--clippy_lints/src/lib.rs2
-rw-r--r--clippy_lints/src/utils/conf.rs4
-rw-r--r--tests/ui-toml/module_inception/clippy.toml1
-rw-r--r--tests/ui-toml/module_inception/module_inception.rs34
-rw-r--r--tests/ui-toml/module_inception/module_inception.stderr20
-rw-r--r--tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr2
-rw-r--r--tests/ui/module_inception.rs12
-rw-r--r--tests/ui/module_inception.stderr22
10 files changed, 121 insertions, 17 deletions
diff --git a/book/src/lint_configuration.md b/book/src/lint_configuration.md
index 3c955c9e574..16dc5dcef42 100644
--- a/book/src/lint_configuration.md
+++ b/book/src/lint_configuration.md
@@ -653,3 +653,13 @@ The byte size a `T` in `Box<T>` can have, below which it triggers the `clippy::u
 * [`unnecessary_box_returns`](https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_box_returns)
 
 
+## `allow-private-module-inception`
+Whether to allow module inception if it's not public.
+
+**Default Value:** `false` (`bool`)
+
+---
+**Affected lints:**
+* [`module_inception`](https://rust-lang.github.io/rust-clippy/master/index.html#module_inception)
+
+
diff --git a/clippy_lints/src/enum_variants.rs b/clippy_lints/src/enum_variants.rs
index faac6340419..d4df6f7aa2d 100644
--- a/clippy_lints/src/enum_variants.rs
+++ b/clippy_lints/src/enum_variants.rs
@@ -3,7 +3,7 @@
 use clippy_utils::diagnostics::{span_lint, span_lint_and_help, span_lint_hir};
 use clippy_utils::source::is_present_in_source;
 use clippy_utils::str_utils::{camel_case_split, count_match_end, count_match_start};
-use rustc_hir::{EnumDef, Item, ItemKind, Variant};
+use rustc_hir::{EnumDef, Item, ItemKind, OwnerId, Variant};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_session::{declare_tool_lint, impl_lint_pass};
 use rustc_span::source_map::Span;
@@ -105,18 +105,20 @@ declare_clippy_lint! {
 }
 
 pub struct EnumVariantNames {
-    modules: Vec<(Symbol, String)>,
+    modules: Vec<(Symbol, String, OwnerId)>,
     threshold: u64,
     avoid_breaking_exported_api: bool,
+    allow_private_module_inception: bool,
 }
 
 impl EnumVariantNames {
     #[must_use]
-    pub fn new(threshold: u64, avoid_breaking_exported_api: bool) -> Self {
+    pub fn new(threshold: u64, avoid_breaking_exported_api: bool, allow_private_module_inception: bool) -> Self {
         Self {
             modules: Vec::new(),
             threshold,
             avoid_breaking_exported_api,
+            allow_private_module_inception,
         }
     }
 }
@@ -252,18 +254,19 @@ impl LateLintPass<'_> for EnumVariantNames {
         let item_name = item.ident.name.as_str();
         let item_camel = to_camel_case(item_name);
         if !item.span.from_expansion() && is_present_in_source(cx, item.span) {
-            if let Some((mod_name, mod_camel)) = self.modules.last() {
+            if let [.., (mod_name, mod_camel, owner_id)] = &*self.modules {
                 // constants don't have surrounding modules
                 if !mod_camel.is_empty() {
-                    if mod_name == &item.ident.name {
-                        if let ItemKind::Mod(..) = item.kind {
-                            span_lint(
-                                cx,
-                                MODULE_INCEPTION,
-                                item.span,
-                                "module has the same name as its containing module",
-                            );
-                        }
+                    if mod_name == &item.ident.name
+                        && let ItemKind::Mod(..) = item.kind
+                        && (!self.allow_private_module_inception || cx.tcx.visibility(owner_id.def_id).is_public())
+                    {
+                        span_lint(
+                            cx,
+                            MODULE_INCEPTION,
+                            item.span,
+                            "module has the same name as its containing module",
+                        );
                     }
                     // The `module_name_repetitions` lint should only trigger if the item has the module in its
                     // name. Having the same name is accepted.
@@ -302,6 +305,6 @@ impl LateLintPass<'_> for EnumVariantNames {
                 check_variant(cx, self.threshold, def, item_name, item.span);
             }
         }
-        self.modules.push((item.ident.name, item_camel));
+        self.modules.push((item.ident.name, item_camel, item.owner_id));
     }
 }
diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs
index 22ff5ef5859..8e42bfe7add 100644
--- a/clippy_lints/src/lib.rs
+++ b/clippy_lints/src/lib.rs
@@ -822,10 +822,12 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         ))
     });
     let enum_variant_name_threshold = conf.enum_variant_name_threshold;
+    let allow_private_module_inception = conf.allow_private_module_inception;
     store.register_late_pass(move |_| {
         Box::new(enum_variants::EnumVariantNames::new(
             enum_variant_name_threshold,
             avoid_breaking_exported_api,
+            allow_private_module_inception,
         ))
     });
     store.register_early_pass(|| Box::new(tabs_in_doc_comments::TabsInDocComments));
diff --git a/clippy_lints/src/utils/conf.rs b/clippy_lints/src/utils/conf.rs
index 69143c3c726..6962299e468 100644
--- a/clippy_lints/src/utils/conf.rs
+++ b/clippy_lints/src/utils/conf.rs
@@ -518,6 +518,10 @@ define_Conf! {
     ///
     /// The byte size a `T` in `Box<T>` can have, below which it triggers the `clippy::unnecessary_box` lint
     (unnecessary_box_size: u64 = 128),
+    /// Lint: MODULE_INCEPTION.
+    ///
+    /// Whether to allow module inception if it's not public.
+    (allow_private_module_inception: bool = false),
 }
 
 /// Search for the configuration file.
diff --git a/tests/ui-toml/module_inception/clippy.toml b/tests/ui-toml/module_inception/clippy.toml
new file mode 100644
index 00000000000..787620d865c
--- /dev/null
+++ b/tests/ui-toml/module_inception/clippy.toml
@@ -0,0 +1 @@
+allow-private-module-inception = true
diff --git a/tests/ui-toml/module_inception/module_inception.rs b/tests/ui-toml/module_inception/module_inception.rs
new file mode 100644
index 00000000000..cd495c884a4
--- /dev/null
+++ b/tests/ui-toml/module_inception/module_inception.rs
@@ -0,0 +1,34 @@
+#![warn(clippy::module_inception)]
+
+// Lint
+pub mod foo2 {
+    pub mod bar2 {
+        pub mod bar2 {
+            pub mod foo2 {}
+        }
+        pub mod foo2 {}
+    }
+    pub mod foo2 {
+        pub mod bar2 {}
+    }
+}
+
+// Don't lint
+mod foo {
+    pub mod bar {
+        pub mod foo {
+            pub mod bar {}
+        }
+    }
+    pub mod foo {
+        pub mod bar {}
+    }
+}
+
+// No warning. See <https://github.com/rust-lang/rust-clippy/issues/1220>.
+pub mod bar {
+    #[allow(clippy::module_inception)]
+    pub mod bar {}
+}
+
+fn main() {}
diff --git a/tests/ui-toml/module_inception/module_inception.stderr b/tests/ui-toml/module_inception/module_inception.stderr
new file mode 100644
index 00000000000..a5a09c322e1
--- /dev/null
+++ b/tests/ui-toml/module_inception/module_inception.stderr
@@ -0,0 +1,20 @@
+error: module has the same name as its containing module
+  --> $DIR/module_inception.rs:6:9
+   |
+LL | /         pub mod bar2 {
+LL | |             pub mod foo2 {}
+LL | |         }
+   | |_________^
+   |
+   = note: `-D clippy::module-inception` implied by `-D warnings`
+
+error: module has the same name as its containing module
+  --> $DIR/module_inception.rs:11:5
+   |
+LL | /     pub mod foo2 {
+LL | |         pub mod bar2 {}
+LL | |     }
+   | |_____^
+
+error: aborting due to 2 previous errors
+
diff --git a/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr b/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr
index 5ec6f3decc3..c11835f5696 100644
--- a/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr
+++ b/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr
@@ -3,6 +3,7 @@ error: error reading Clippy's configuration file: unknown field `foobar`, expect
            allow-expect-in-tests
            allow-mixed-uninlined-format-args
            allow-print-in-tests
+           allow-private-module-inception
            allow-unwrap-in-tests
            allowed-scripts
            arithmetic-side-effects-allowed
@@ -65,6 +66,7 @@ error: error reading Clippy's configuration file: unknown field `barfoo`, expect
            allow-expect-in-tests
            allow-mixed-uninlined-format-args
            allow-print-in-tests
+           allow-private-module-inception
            allow-unwrap-in-tests
            allowed-scripts
            arithmetic-side-effects-allowed
diff --git a/tests/ui/module_inception.rs b/tests/ui/module_inception.rs
index a23aba9164a..802c3ec39b6 100644
--- a/tests/ui/module_inception.rs
+++ b/tests/ui/module_inception.rs
@@ -1,5 +1,17 @@
 #![warn(clippy::module_inception)]
 
+pub mod foo2 {
+    pub mod bar2 {
+        pub mod bar2 {
+            pub mod foo2 {}
+        }
+        pub mod foo2 {}
+    }
+    pub mod foo2 {
+        pub mod bar2 {}
+    }
+}
+
 mod foo {
     mod bar {
         mod bar {
diff --git a/tests/ui/module_inception.stderr b/tests/ui/module_inception.stderr
index 77564dce9eb..ebb8e296f46 100644
--- a/tests/ui/module_inception.stderr
+++ b/tests/ui/module_inception.stderr
@@ -1,8 +1,8 @@
 error: module has the same name as its containing module
   --> $DIR/module_inception.rs:5:9
    |
-LL | /         mod bar {
-LL | |             mod foo {}
+LL | /         pub mod bar2 {
+LL | |             pub mod foo2 {}
 LL | |         }
    | |_________^
    |
@@ -11,10 +11,26 @@ LL | |         }
 error: module has the same name as its containing module
   --> $DIR/module_inception.rs:10:5
    |
+LL | /     pub mod foo2 {
+LL | |         pub mod bar2 {}
+LL | |     }
+   | |_____^
+
+error: module has the same name as its containing module
+  --> $DIR/module_inception.rs:17:9
+   |
+LL | /         mod bar {
+LL | |             mod foo {}
+LL | |         }
+   | |_________^
+
+error: module has the same name as its containing module
+  --> $DIR/module_inception.rs:22:5
+   |
 LL | /     mod foo {
 LL | |         mod bar {}
 LL | |     }
    | |_____^
 
-error: aborting due to 2 previous errors
+error: aborting due to 4 previous errors