1
+ use std:: hash:: { Hash , Hasher } ;
1
2
use clippy_utils:: diagnostics:: span_lint_and_sugg;
2
3
use clippy_utils:: is_from_proc_macro;
4
+ use rustc_data_structures:: fx:: FxIndexSet ;
3
5
use rustc_errors:: Applicability ;
4
6
use rustc_hir:: def:: Res ;
5
7
use rustc_hir:: def_id:: DefId ;
6
- use rustc_hir:: { HirId , Path , PathSegment } ;
8
+ use rustc_hir:: { HirId , Item , ItemKind , Path , PathSegment , UseKind } ;
7
9
use rustc_lint:: { LateContext , LateLintPass , LintContext } ;
8
10
use rustc_middle:: lint:: in_external_macro;
9
11
use rustc_session:: impl_lint_pass;
@@ -93,9 +95,47 @@ pub struct StdReexports {
93
95
// twice. First for the mod, second for the macro. This is used to avoid the lint reporting for the macro
94
96
// when the path could be also be used to access the module.
95
97
prev_span : Span ,
98
+ open_use : Option < OpenUseSpan >
96
99
}
100
+
97
101
impl_lint_pass ! ( StdReexports => [ STD_INSTEAD_OF_CORE , STD_INSTEAD_OF_ALLOC , ALLOC_INSTEAD_OF_CORE ] ) ;
98
102
103
+ #[ derive( Debug ) ]
104
+ struct OpenUseSpan {
105
+ container : Span ,
106
+ members : FxIndexSet < UseSpanMember >
107
+ }
108
+
109
+ #[ derive( Debug , Copy , Clone ) ]
110
+ struct UseSpanMember {
111
+ inner : Span ,
112
+ lint_data : LintData ,
113
+ }
114
+
115
+ impl PartialEq for UseSpanMember {
116
+ fn eq ( & self , other : & Self ) -> bool {
117
+ self . inner . eq ( & other. inner )
118
+ }
119
+ }
120
+
121
+ impl Eq for UseSpanMember { }
122
+
123
+ impl Hash for UseSpanMember {
124
+ fn hash < H : Hasher > ( & self , state : & mut H ) {
125
+ self . inner . hash ( state) ;
126
+ }
127
+ }
128
+
129
+ #[ derive( Debug , Copy , Clone ) ]
130
+ enum LintData {
131
+ CanReplace {
132
+ lint : & ' static crate :: Lint ,
133
+ used_mod : & ' static str ,
134
+ replace_with : & ' static str ,
135
+ } ,
136
+ NoReplace ,
137
+ }
138
+
99
139
impl < ' tcx > LateLintPass < ' tcx > for StdReexports {
100
140
fn check_path ( & mut self , cx : & LateContext < ' tcx > , path : & Path < ' tcx > , _: HirId ) {
101
141
if let Res :: Def ( _, def_id) = path. res
@@ -104,37 +144,74 @@ impl<'tcx> LateLintPass<'tcx> for StdReexports {
104
144
&& !in_external_macro ( cx. sess ( ) , path. span )
105
145
&& !is_from_proc_macro ( cx, & first_segment. ident )
106
146
{
107
- let ( lint , used_mod , replace_with ) = match first_segment. ident . name {
147
+ let lint_data = match first_segment. ident . name {
108
148
sym:: std => match cx. tcx . crate_name ( def_id. krate ) {
109
- sym:: core => ( STD_INSTEAD_OF_CORE , "std" , "core" ) ,
110
- sym:: alloc => ( STD_INSTEAD_OF_ALLOC , "std" , "alloc" ) ,
149
+ sym:: core => LintData :: CanReplace {
150
+ lint : STD_INSTEAD_OF_CORE ,
151
+ used_mod : "std" ,
152
+ replace_with : "core" ,
153
+ } ,
154
+ sym:: alloc => LintData :: CanReplace {
155
+ lint : STD_INSTEAD_OF_ALLOC ,
156
+ used_mod : "std" ,
157
+ replace_with : "alloc" ,
158
+ } ,
111
159
_ => {
112
160
self . prev_span = first_segment. ident . span ;
113
- return ;
161
+ LintData :: NoReplace
114
162
} ,
115
163
} ,
116
164
sym:: alloc => {
117
165
if cx. tcx . crate_name ( def_id. krate ) == sym:: core {
118
- ( ALLOC_INSTEAD_OF_CORE , "alloc" , "core" )
166
+ LintData :: CanReplace {
167
+ lint : ALLOC_INSTEAD_OF_CORE ,
168
+ used_mod : "alloc" ,
169
+ replace_with : "core" ,
170
+ }
119
171
} else {
120
172
self . prev_span = first_segment. ident . span ;
121
- return ;
173
+ LintData :: NoReplace
122
174
}
123
175
} ,
124
176
_ => return ,
125
177
} ;
126
- if first_segment. ident . span != self . prev_span {
127
- span_lint_and_sugg (
128
- cx,
129
- lint,
130
- first_segment. ident . span ,
131
- format ! ( "used import from `{used_mod}` instead of `{replace_with}`" ) ,
132
- format ! ( "consider importing the item from `{replace_with}`" ) ,
133
- replace_with. to_string ( ) ,
134
- Applicability :: MachineApplicable ,
135
- ) ;
136
- self . prev_span = first_segment. ident . span ;
178
+ if let Some ( in_use) = self . open_use . as_mut ( ) {
179
+ in_use. members . insert ( UseSpanMember {
180
+ inner : path. span ,
181
+ lint_data,
182
+ } ) ;
183
+ return ;
137
184
}
185
+ if let LintData :: CanReplace { lint, used_mod, replace_with } = lint_data {
186
+ if first_segment. ident . span != self . prev_span {
187
+ span_lint_and_sugg (
188
+ cx,
189
+ lint,
190
+ first_segment. ident . span ,
191
+ & format ! ( "used import from `{used_mod}` instead of `{replace_with}`" ) ,
192
+ & format ! ( "consider importing the item from `{replace_with}`" ) ,
193
+ replace_with. to_string ( ) ,
194
+ Applicability :: MachineApplicable ,
195
+ ) ;
196
+ self . prev_span = first_segment. ident . span ;
197
+ }
198
+ }
199
+ }
200
+ }
201
+
202
+ fn check_item ( & mut self , cx : & LateContext < ' tcx > , item : & ' tcx Item < ' tcx > ) {
203
+ if matches ! ( item. kind, ItemKind :: Use ( _, UseKind :: ListStem ) ) {
204
+ self . open_use = Some ( OpenUseSpan {
205
+ container : item. span ,
206
+ members : FxIndexSet :: default ( ) ,
207
+ } )
208
+ }
209
+
210
+ }
211
+
212
+ fn check_item_post ( & mut self , cx : & LateContext < ' tcx > , item : & ' tcx Item < ' tcx > ) {
213
+ if let Some ( collected_use) = self . open_use . take ( ) {
214
+
138
215
}
139
216
}
140
217
}
0 commit comments