@@ -3,10 +3,10 @@ use if_chain::if_chain;
33use rustc_errors:: Applicability ;
44use rustc_hir:: {
55 def:: { DefKind , Res } ,
6- Item , ItemKind , UseKind ,
6+ Item , ItemKind , PathSegment , UseKind ,
77} ;
88use rustc_lint:: { LateContext , LateLintPass } ;
9- use rustc_session:: { declare_lint_pass , declare_tool_lint } ;
9+ use rustc_session:: { declare_tool_lint , impl_lint_pass } ;
1010use rustc_span:: BytePos ;
1111
1212declare_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
7898impl 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}
0 commit comments