11use clippy_utils:: diagnostics:: { span_lint_and_help, span_lint_and_sugg} ;
2+ use rustc_ast:: node_id:: { NodeId , NodeMap } ;
23use rustc_ast:: { ptr:: P , Crate , Item , ItemKind , MacroDef , ModKind , UseTreeKind } ;
34use rustc_errors:: Applicability ;
45use rustc_lint:: { EarlyContext , EarlyLintPass , LintContext } ;
5- use rustc_session:: { declare_lint_pass , declare_tool_lint } ;
6+ use rustc_session:: { declare_tool_lint , impl_lint_pass } ;
67use rustc_span:: { edition:: Edition , symbol:: kw, Span , Symbol } ;
78
89declare_clippy_lint ! {
@@ -33,51 +34,32 @@ declare_clippy_lint! {
3334 "imports with single component path are redundant"
3435}
3536
36- declare_lint_pass ! ( SingleComponentPathImports => [ SINGLE_COMPONENT_PATH_IMPORTS ] ) ;
37+ #[ derive( Default ) ]
38+ pub struct SingleComponentPathImports {
39+ /// Buffer found usages to emit when visiting that item so that `#[allow]` works as expected
40+ found : NodeMap < Vec < SingleUse > > ,
41+ }
42+
43+ struct SingleUse {
44+ name : Symbol ,
45+ span : Span ,
46+ item_id : NodeId ,
47+ can_suggest : bool ,
48+ }
49+
50+ impl_lint_pass ! ( SingleComponentPathImports => [ SINGLE_COMPONENT_PATH_IMPORTS ] ) ;
3751
3852impl EarlyLintPass for SingleComponentPathImports {
3953 fn check_crate ( & mut self , cx : & EarlyContext < ' _ > , krate : & Crate ) {
4054 if cx. sess ( ) . opts . edition < Edition :: Edition2018 {
4155 return ;
4256 }
43- check_mod ( cx, & krate. items ) ;
44- }
45- }
4657
47- fn check_mod ( cx : & EarlyContext < ' _ > , items : & [ P < Item > ] ) {
48- // keep track of imports reused with `self` keyword,
49- // such as `self::crypto_hash` in the example below
50- // ```rust,ignore
51- // use self::crypto_hash::{Algorithm, Hasher};
52- // ```
53- let mut imports_reused_with_self = Vec :: new ( ) ;
54-
55- // keep track of single use statements
56- // such as `crypto_hash` in the example below
57- // ```rust,ignore
58- // use crypto_hash;
59- // ```
60- let mut single_use_usages = Vec :: new ( ) ;
61-
62- // keep track of macros defined in the module as we don't want it to trigger on this (#7106)
63- // ```rust,ignore
64- // macro_rules! foo { () => {} };
65- // pub(crate) use foo;
66- // ```
67- let mut macros = Vec :: new ( ) ;
68-
69- for item in items {
70- track_uses (
71- cx,
72- item,
73- & mut imports_reused_with_self,
74- & mut single_use_usages,
75- & mut macros,
76- ) ;
58+ self . check_mod ( cx, & krate. items ) ;
7759 }
7860
79- for ( name , span , can_suggest ) in single_use_usages {
80- if !imports_reused_with_self . contains ( & name ) {
61+ fn check_item ( & mut self , cx : & EarlyContext < ' _ > , item : & Item ) {
62+ for SingleUse { span , can_suggest , .. } in self . found . remove ( & item . id ) . into_iter ( ) . flatten ( ) {
8163 if can_suggest {
8264 span_lint_and_sugg (
8365 cx,
@@ -102,74 +84,127 @@ fn check_mod(cx: &EarlyContext<'_>, items: &[P<Item>]) {
10284 }
10385}
10486
105- fn track_uses (
106- cx : & EarlyContext < ' _ > ,
107- item : & Item ,
108- imports_reused_with_self : & mut Vec < Symbol > ,
109- single_use_usages : & mut Vec < ( Symbol , Span , bool ) > ,
110- macros : & mut Vec < Symbol > ,
111- ) {
112- if item . span . from_expansion ( ) || item . vis . kind . is_pub ( ) {
113- return ;
114- }
87+ impl SingleComponentPathImports {
88+ fn check_mod ( & mut self , cx : & EarlyContext < ' _ > , items : & [ P < Item > ] ) {
89+ // keep track of imports reused with `self` keyword, such as `self::crypto_hash` in the example
90+ // below. Removing the `use crypto_hash;` would make this a compile error
91+ // ```
92+ // use crypto_hash;
93+ //
94+ // use self::crypto_hash::{Algorithm, Hasher};
95+ // ```
96+ let mut imports_reused_with_self = Vec :: new ( ) ;
11597
116- match & item. kind {
117- ItemKind :: Mod ( _, ModKind :: Loaded ( ref items, ..) ) => {
118- check_mod ( cx, items) ;
119- } ,
120- ItemKind :: MacroDef ( MacroDef { macro_rules : true , .. } ) => {
121- macros. push ( item. ident . name ) ;
122- } ,
123- ItemKind :: Use ( use_tree) => {
124- let segments = & use_tree. prefix . segments ;
125-
126- // keep track of `use some_module;` usages
127- if segments. len ( ) == 1 {
128- if let UseTreeKind :: Simple ( None , _, _) = use_tree. kind {
129- let name = segments[ 0 ] . ident . name ;
130- if !macros. contains ( & name) {
131- single_use_usages. push ( ( name, item. span , true ) ) ;
132- }
133- }
134- return ;
98+ // keep track of single use statements such as `crypto_hash` in the example below
99+ // ```
100+ // use crypto_hash;
101+ // ```
102+ let mut single_use_usages = Vec :: new ( ) ;
103+
104+ // keep track of macros defined in the module as we don't want it to trigger on this (#7106)
105+ // ```
106+ // macro_rules! foo { () => {} };
107+ // pub(crate) use foo;
108+ // ```
109+ let mut macros = Vec :: new ( ) ;
110+
111+ for item in items {
112+ self . track_uses (
113+ cx,
114+ item,
115+ & mut imports_reused_with_self,
116+ & mut single_use_usages,
117+ & mut macros,
118+ ) ;
119+ }
120+
121+ for usage in single_use_usages {
122+ if !imports_reused_with_self. contains ( & usage. name ) {
123+ self . found . entry ( usage. item_id ) . or_default ( ) . push ( usage) ;
135124 }
125+ }
126+ }
136127
137- if segments. is_empty ( ) {
138- // keep track of `use {some_module, some_other_module};` usages
139- if let UseTreeKind :: Nested ( trees) = & use_tree. kind {
140- for tree in trees {
141- let segments = & tree. 0 . prefix . segments ;
142- if segments. len ( ) == 1 {
143- if let UseTreeKind :: Simple ( None , _, _) = tree. 0 . kind {
144- let name = segments[ 0 ] . ident . name ;
145- if !macros. contains ( & name) {
146- single_use_usages. push ( ( name, tree. 0 . span , false ) ) ;
147- }
148- }
128+ fn track_uses (
129+ & mut self ,
130+ cx : & EarlyContext < ' _ > ,
131+ item : & Item ,
132+ imports_reused_with_self : & mut Vec < Symbol > ,
133+ single_use_usages : & mut Vec < SingleUse > ,
134+ macros : & mut Vec < Symbol > ,
135+ ) {
136+ if item. span . from_expansion ( ) || item. vis . kind . is_pub ( ) {
137+ return ;
138+ }
139+
140+ match & item. kind {
141+ ItemKind :: Mod ( _, ModKind :: Loaded ( ref items, ..) ) => {
142+ self . check_mod ( cx, items) ;
143+ } ,
144+ ItemKind :: MacroDef ( MacroDef { macro_rules : true , .. } ) => {
145+ macros. push ( item. ident . name ) ;
146+ } ,
147+ ItemKind :: Use ( use_tree) => {
148+ let segments = & use_tree. prefix . segments ;
149+
150+ // keep track of `use some_module;` usages
151+ if segments. len ( ) == 1 {
152+ if let UseTreeKind :: Simple ( None , _, _) = use_tree. kind {
153+ let name = segments[ 0 ] . ident . name ;
154+ if !macros. contains ( & name) {
155+ single_use_usages. push ( SingleUse {
156+ name,
157+ span : item. span ,
158+ item_id : item. id ,
159+ can_suggest : true ,
160+ } ) ;
149161 }
150162 }
163+ return ;
151164 }
152- } else {
153- // keep track of `use self::some_module` usages
154- if segments[ 0 ] . ident . name == kw:: SelfLower {
155- // simple case such as `use self::module::SomeStruct`
156- if segments. len ( ) > 1 {
157- imports_reused_with_self. push ( segments[ 1 ] . ident . name ) ;
158- return ;
159- }
160165
161- // nested case such as `use self::{module1::Struct1, module2::Struct2}`
166+ if segments. is_empty ( ) {
167+ // keep track of `use {some_module, some_other_module};` usages
162168 if let UseTreeKind :: Nested ( trees) = & use_tree. kind {
163169 for tree in trees {
164170 let segments = & tree. 0 . prefix . segments ;
165- if !segments. is_empty ( ) {
166- imports_reused_with_self. push ( segments[ 0 ] . ident . name ) ;
171+ if segments. len ( ) == 1 {
172+ if let UseTreeKind :: Simple ( None , _, _) = tree. 0 . kind {
173+ let name = segments[ 0 ] . ident . name ;
174+ if !macros. contains ( & name) {
175+ single_use_usages. push ( SingleUse {
176+ name,
177+ span : tree. 0 . span ,
178+ item_id : item. id ,
179+ can_suggest : false ,
180+ } ) ;
181+ }
182+ }
183+ }
184+ }
185+ }
186+ } else {
187+ // keep track of `use self::some_module` usages
188+ if segments[ 0 ] . ident . name == kw:: SelfLower {
189+ // simple case such as `use self::module::SomeStruct`
190+ if segments. len ( ) > 1 {
191+ imports_reused_with_self. push ( segments[ 1 ] . ident . name ) ;
192+ return ;
193+ }
194+
195+ // nested case such as `use self::{module1::Struct1, module2::Struct2}`
196+ if let UseTreeKind :: Nested ( trees) = & use_tree. kind {
197+ for tree in trees {
198+ let segments = & tree. 0 . prefix . segments ;
199+ if !segments. is_empty ( ) {
200+ imports_reused_with_self. push ( segments[ 0 ] . ident . name ) ;
201+ }
167202 }
168203 }
169204 }
170205 }
171- }
172- } ,
173- _ => { } ,
206+ } ,
207+ _ => { } ,
208+ }
174209 }
175210}
0 commit comments