Skip to content

Commit 43a1777

Browse files
committed
Auto merge of rust-lang#5564 - MrAwesome:master, r=flip1995
Allow `use super::*;` glob imports changelog: Allow super::* glob imports fixes rust-lang#5554 fixes rust-lang#5569 A first pass at rust-lang#5554 - this allows all `use super::*` to pass, which may or may not be desirable. The original issue was around allowing test modules to import their entire parent modules - I'm happy to modify this to do that instead, may just need some guidance on how to implement that (I played around a bit with #[cfg(test)] but from what I can gather, clippy itself isn't in test mode when running, even if the code in question is being checked for the test target).
2 parents 43d9cdc + b69200b commit 43a1777

File tree

7 files changed

+245
-14
lines changed

7 files changed

+245
-14
lines changed

clippy_lints/src/lib.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -1058,7 +1058,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
10581058
let max_struct_bools = conf.max_struct_bools;
10591059
store.register_early_pass(move || box excessive_bools::ExcessiveBools::new(max_struct_bools, max_fn_params_bools));
10601060
store.register_early_pass(|| box option_env_unwrap::OptionEnvUnwrap);
1061-
store.register_late_pass(|| box wildcard_imports::WildcardImports);
1061+
let warn_on_all_wildcard_imports = conf.warn_on_all_wildcard_imports;
1062+
store.register_late_pass(move || box wildcard_imports::WildcardImports::new(warn_on_all_wildcard_imports));
10621063
store.register_early_pass(|| box macro_use::MacroUseImports);
10631064
store.register_late_pass(|| box verbose_file_reads::VerboseFileReads);
10641065
store.register_late_pass(|| box redundant_pub_crate::RedundantPubCrate::default());

clippy_lints/src/utils/conf.rs

+2
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,8 @@ define_Conf! {
158158
(max_struct_bools, "max_struct_bools": u64, 3),
159159
/// Lint: FN_PARAMS_EXCESSIVE_BOOLS. The maximum number of bools function parameters can have
160160
(max_fn_params_bools, "max_fn_params_bools": u64, 3),
161+
/// Lint: WILDCARD_IMPORTS. Whether to allow certain wildcard imports (prelude, super in tests).
162+
(warn_on_all_wildcard_imports, "warn_on_all_wildcard_imports": bool, false),
161163
}
162164

163165
impl Default for Conf {

clippy_lints/src/wildcard_imports.rs

+63-11
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,10 @@ use if_chain::if_chain;
33
use rustc_errors::Applicability;
44
use rustc_hir::{
55
def::{DefKind, Res},
6-
Item, ItemKind, UseKind,
6+
Item, ItemKind, PathSegment, UseKind,
77
};
88
use rustc_lint::{LateContext, LateLintPass};
9-
use rustc_session::{declare_lint_pass, declare_tool_lint};
9+
use rustc_session::{declare_tool_lint, impl_lint_pass};
1010
use rustc_span::BytePos;
1111

1212
declare_clippy_lint! {
@@ -43,9 +43,14 @@ declare_clippy_lint! {
4343
///
4444
/// This can lead to confusing error messages at best and to unexpected behavior at worst.
4545
///
46-
/// Note that this will not warn about wildcard imports from modules named `prelude`; many
47-
/// crates (including the standard library) provide modules named "prelude" specifically
48-
/// designed for wildcard import.
46+
/// **Exceptions:**
47+
///
48+
/// Wildcard imports are allowed from modules named `prelude`. Many crates (including the standard library)
49+
/// provide modules named "prelude" specifically designed for wildcard import.
50+
///
51+
/// `use super::*` is allowed in test modules. This is defined as any module with "test" in the name.
52+
///
53+
/// These exceptions can be disabled using the `warn-on-all-wildcard-imports` configuration flag.
4954
///
5055
/// **Known problems:** If macros are imported through the wildcard, this macro is not included
5156
/// by the suggestion and has to be added by hand.
@@ -73,18 +78,34 @@ declare_clippy_lint! {
7378
"lint `use _::*` statements"
7479
}
7580

76-
declare_lint_pass!(WildcardImports => [ENUM_GLOB_USE, WILDCARD_IMPORTS]);
81+
#[derive(Default)]
82+
pub struct WildcardImports {
83+
warn_on_all: bool,
84+
test_modules_deep: u32,
85+
}
86+
87+
impl WildcardImports {
88+
pub fn new(warn_on_all: bool) -> Self {
89+
Self {
90+
warn_on_all,
91+
test_modules_deep: 0,
92+
}
93+
}
94+
}
95+
96+
impl_lint_pass!(WildcardImports => [ENUM_GLOB_USE, WILDCARD_IMPORTS]);
7797

7898
impl LateLintPass<'_, '_> for WildcardImports {
7999
fn check_item(&mut self, cx: &LateContext<'_, '_>, item: &Item<'_>) {
100+
if is_test_module_or_function(item) {
101+
self.test_modules_deep = self.test_modules_deep.saturating_add(1);
102+
}
80103
if item.vis.node.is_pub() || item.vis.node.is_pub_restricted() {
81104
return;
82105
}
83106
if_chain! {
84-
if !in_macro(item.span);
85107
if let ItemKind::Use(use_path, UseKind::Glob) = &item.kind;
86-
// don't lint prelude glob imports
87-
if !use_path.segments.iter().last().map_or(false, |ps| ps.ident.as_str() == "prelude");
108+
if self.warn_on_all || !self.check_exceptions(item, use_path.segments);
88109
let used_imports = cx.tcx.names_imported_by_glob_use(item.hir_id.owner);
89110
if !used_imports.is_empty(); // Already handled by `unused_imports`
90111
then {
@@ -109,8 +130,7 @@ impl LateLintPass<'_, '_> for WildcardImports {
109130
span = use_path.span.with_hi(item.span.hi() - BytePos(1));
110131
}
111132
(
112-
span,
113-
false,
133+
span, false,
114134
)
115135
};
116136

@@ -153,4 +173,36 @@ impl LateLintPass<'_, '_> for WildcardImports {
153173
}
154174
}
155175
}
176+
177+
fn check_item_post(&mut self, _: &LateContext<'_, '_>, item: &Item<'_>) {
178+
if is_test_module_or_function(item) {
179+
self.test_modules_deep = self.test_modules_deep.saturating_sub(1);
180+
}
181+
}
182+
}
183+
184+
impl WildcardImports {
185+
fn check_exceptions(&self, item: &Item<'_>, segments: &[PathSegment<'_>]) -> bool {
186+
in_macro(item.span)
187+
|| is_prelude_import(segments)
188+
|| (is_super_only_import(segments) && self.test_modules_deep > 0)
189+
}
190+
}
191+
192+
// Allow "...prelude::*" imports.
193+
// Many crates have a prelude, and it is imported as a glob by design.
194+
fn is_prelude_import(segments: &[PathSegment<'_>]) -> bool {
195+
segments
196+
.iter()
197+
.last()
198+
.map_or(false, |ps| ps.ident.as_str() == "prelude")
199+
}
200+
201+
// Allow "super::*" imports in tests.
202+
fn is_super_only_import(segments: &[PathSegment<'_>]) -> bool {
203+
segments.len() == 1 && segments[0].ident.as_str() == "super"
204+
}
205+
206+
fn is_test_module_or_function(item: &Item<'_>) -> bool {
207+
matches!(item.kind, ItemKind::Mod(..)) && item.ident.name.as_str().contains("test")
156208
}
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
error: error reading Clippy's configuration file `$DIR/clippy.toml`: unknown field `foobar`, expected one of `blacklisted-names`, `cognitive-complexity-threshold`, `cyclomatic-complexity-threshold`, `doc-valid-idents`, `too-many-arguments-threshold`, `type-complexity-threshold`, `single-char-binding-names-threshold`, `too-large-for-stack`, `enum-variant-name-threshold`, `enum-variant-size-threshold`, `verbose-bit-mask-threshold`, `literal-representation-threshold`, `trivial-copy-size-limit`, `too-many-lines-threshold`, `array-size-threshold`, `vec-box-size-threshold`, `max-struct-bools`, `max-fn-params-bools`, `third-party` at line 5 column 1
1+
error: error reading Clippy's configuration file `$DIR/clippy.toml`: unknown field `foobar`, expected one of `blacklisted-names`, `cognitive-complexity-threshold`, `cyclomatic-complexity-threshold`, `doc-valid-idents`, `too-many-arguments-threshold`, `type-complexity-threshold`, `single-char-binding-names-threshold`, `too-large-for-stack`, `enum-variant-name-threshold`, `enum-variant-size-threshold`, `verbose-bit-mask-threshold`, `literal-representation-threshold`, `trivial-copy-size-limit`, `too-many-lines-threshold`, `array-size-threshold`, `vec-box-size-threshold`, `max-struct-bools`, `max-fn-params-bools`, `warn-on-all-wildcard-imports`, `third-party` at line 5 column 1
22

33
error: aborting due to previous error
44

tests/ui/wildcard_imports.fixed

+73
Original file line numberDiff line numberDiff line change
@@ -155,3 +155,76 @@ fn test_weird_formatting() {
155155
exported();
156156
foo();
157157
}
158+
159+
mod super_imports {
160+
fn foofoo() {}
161+
162+
mod should_be_replaced {
163+
use super::foofoo;
164+
165+
fn with_super() {
166+
let _ = foofoo();
167+
}
168+
}
169+
170+
mod test_should_pass {
171+
use super::*;
172+
173+
fn with_super() {
174+
let _ = foofoo();
175+
}
176+
}
177+
178+
mod test_should_pass_inside_function {
179+
fn with_super_inside_function() {
180+
use super::*;
181+
let _ = foofoo();
182+
}
183+
}
184+
185+
mod test_should_pass_further_inside {
186+
fn insidefoo() {}
187+
mod inner {
188+
use super::*;
189+
fn with_super() {
190+
let _ = insidefoo();
191+
}
192+
}
193+
}
194+
195+
mod should_be_replaced_futher_inside {
196+
fn insidefoo() {}
197+
mod inner {
198+
use super::insidefoo;
199+
fn with_super() {
200+
let _ = insidefoo();
201+
}
202+
}
203+
}
204+
205+
mod use_explicit_should_be_replaced {
206+
use super_imports::foofoo;
207+
208+
fn with_explicit() {
209+
let _ = foofoo();
210+
}
211+
}
212+
213+
mod use_double_super_should_be_replaced {
214+
mod inner {
215+
use super::super::foofoo;
216+
217+
fn with_double_super() {
218+
let _ = foofoo();
219+
}
220+
}
221+
}
222+
223+
mod use_super_explicit_should_be_replaced {
224+
use super::super::super_imports::foofoo;
225+
226+
fn with_super_explicit() {
227+
let _ = foofoo();
228+
}
229+
}
230+
}

tests/ui/wildcard_imports.rs

+73
Original file line numberDiff line numberDiff line change
@@ -156,3 +156,76 @@ fn test_weird_formatting() {
156156
exported();
157157
foo();
158158
}
159+
160+
mod super_imports {
161+
fn foofoo() {}
162+
163+
mod should_be_replaced {
164+
use super::*;
165+
166+
fn with_super() {
167+
let _ = foofoo();
168+
}
169+
}
170+
171+
mod test_should_pass {
172+
use super::*;
173+
174+
fn with_super() {
175+
let _ = foofoo();
176+
}
177+
}
178+
179+
mod test_should_pass_inside_function {
180+
fn with_super_inside_function() {
181+
use super::*;
182+
let _ = foofoo();
183+
}
184+
}
185+
186+
mod test_should_pass_further_inside {
187+
fn insidefoo() {}
188+
mod inner {
189+
use super::*;
190+
fn with_super() {
191+
let _ = insidefoo();
192+
}
193+
}
194+
}
195+
196+
mod should_be_replaced_futher_inside {
197+
fn insidefoo() {}
198+
mod inner {
199+
use super::*;
200+
fn with_super() {
201+
let _ = insidefoo();
202+
}
203+
}
204+
}
205+
206+
mod use_explicit_should_be_replaced {
207+
use super_imports::*;
208+
209+
fn with_explicit() {
210+
let _ = foofoo();
211+
}
212+
}
213+
214+
mod use_double_super_should_be_replaced {
215+
mod inner {
216+
use super::super::*;
217+
218+
fn with_double_super() {
219+
let _ = foofoo();
220+
}
221+
}
222+
}
223+
224+
mod use_super_explicit_should_be_replaced {
225+
use super::super::super_imports::*;
226+
227+
fn with_super_explicit() {
228+
let _ = foofoo();
229+
}
230+
}
231+
}

tests/ui/wildcard_imports.stderr

+31-1
Original file line numberDiff line numberDiff line change
@@ -92,5 +92,35 @@ LL | use crate:: fn_mod::
9292
LL | | *;
9393
| |_________^ help: try: `crate:: fn_mod::foo`
9494

95-
error: aborting due to 15 previous errors
95+
error: usage of wildcard import
96+
--> $DIR/wildcard_imports.rs:164:13
97+
|
98+
LL | use super::*;
99+
| ^^^^^^^^ help: try: `super::foofoo`
100+
101+
error: usage of wildcard import
102+
--> $DIR/wildcard_imports.rs:199:17
103+
|
104+
LL | use super::*;
105+
| ^^^^^^^^ help: try: `super::insidefoo`
106+
107+
error: usage of wildcard import
108+
--> $DIR/wildcard_imports.rs:207:13
109+
|
110+
LL | use super_imports::*;
111+
| ^^^^^^^^^^^^^^^^ help: try: `super_imports::foofoo`
112+
113+
error: usage of wildcard import
114+
--> $DIR/wildcard_imports.rs:216:17
115+
|
116+
LL | use super::super::*;
117+
| ^^^^^^^^^^^^^^^ help: try: `super::super::foofoo`
118+
119+
error: usage of wildcard import
120+
--> $DIR/wildcard_imports.rs:225:13
121+
|
122+
LL | use super::super::super_imports::*;
123+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `super::super::super_imports::foofoo`
124+
125+
error: aborting due to 20 previous errors
96126

0 commit comments

Comments
 (0)