1
1
use clippy_utils:: diagnostics:: { span_lint_and_help, span_lint_and_sugg} ;
2
+ use rustc_ast:: node_id:: { NodeId , NodeMap } ;
2
3
use rustc_ast:: { ptr:: P , Crate , Item , ItemKind , MacroDef , ModKind , UseTreeKind } ;
3
4
use rustc_errors:: Applicability ;
4
5
use 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 } ;
6
7
use rustc_span:: { edition:: Edition , symbol:: kw, Span , Symbol } ;
7
8
8
9
declare_clippy_lint ! {
@@ -33,51 +34,32 @@ declare_clippy_lint! {
33
34
"imports with single component path are redundant"
34
35
}
35
36
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 ] ) ;
37
51
38
52
impl EarlyLintPass for SingleComponentPathImports {
39
53
fn check_crate ( & mut self , cx : & EarlyContext < ' _ > , krate : & Crate ) {
40
54
if cx. sess ( ) . opts . edition < Edition :: Edition2018 {
41
55
return ;
42
56
}
43
- check_mod ( cx, & krate. items ) ;
44
- }
45
- }
46
57
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 ) ;
77
59
}
78
60
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 ( ) {
81
63
if can_suggest {
82
64
span_lint_and_sugg (
83
65
cx,
@@ -102,74 +84,127 @@ fn check_mod(cx: &EarlyContext<'_>, items: &[P<Item>]) {
102
84
}
103
85
}
104
86
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 ( ) ;
115
97
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) ;
135
124
}
125
+ }
126
+ }
136
127
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
+ } ) ;
149
161
}
150
162
}
163
+ return ;
151
164
}
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
- }
160
165
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
162
168
if let UseTreeKind :: Nested ( trees) = & use_tree. kind {
163
169
for tree in trees {
164
170
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
+ }
167
202
}
168
203
}
169
204
}
170
205
}
171
- }
172
- } ,
173
- _ => { } ,
206
+ } ,
207
+ _ => { } ,
208
+ }
174
209
}
175
210
}
0 commit comments