@@ -3,10 +3,10 @@ use if_chain::if_chain;
3
3
use rustc_errors:: Applicability ;
4
4
use rustc_hir:: {
5
5
def:: { DefKind , Res } ,
6
- Item , ItemKind , UseKind ,
6
+ Item , ItemKind , PathSegment , UseKind ,
7
7
} ;
8
8
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 } ;
10
10
use rustc_span:: BytePos ;
11
11
12
12
declare_clippy_lint ! {
@@ -43,9 +43,14 @@ declare_clippy_lint! {
43
43
///
44
44
/// This can lead to confusing error messages at best and to unexpected behavior at worst.
45
45
///
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.
49
54
///
50
55
/// **Known problems:** If macros are imported through the wildcard, this macro is not included
51
56
/// by the suggestion and has to be added by hand.
@@ -73,18 +78,34 @@ declare_clippy_lint! {
73
78
"lint `use _::*` statements"
74
79
}
75
80
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 ] ) ;
77
97
78
98
impl LateLintPass < ' _ , ' _ > for WildcardImports {
79
99
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
+ }
80
103
if item. vis . node . is_pub ( ) || item. vis . node . is_pub_restricted ( ) {
81
104
return ;
82
105
}
83
106
if_chain ! {
84
- if !in_macro( item. span) ;
85
107
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) ;
88
109
let used_imports = cx. tcx. names_imported_by_glob_use( item. hir_id. owner) ;
89
110
if !used_imports. is_empty( ) ; // Already handled by `unused_imports`
90
111
then {
@@ -109,8 +130,7 @@ impl LateLintPass<'_, '_> for WildcardImports {
109
130
span = use_path. span. with_hi( item. span. hi( ) - BytePos ( 1 ) ) ;
110
131
}
111
132
(
112
- span,
113
- false ,
133
+ span, false ,
114
134
)
115
135
} ;
116
136
@@ -153,4 +173,36 @@ impl LateLintPass<'_, '_> for WildcardImports {
153
173
}
154
174
}
155
175
}
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" )
156
208
}
0 commit comments