11use clippy_config:: Conf ;
2- use clippy_utils:: diagnostics:: { span_lint_and_help , span_lint_and_sugg } ;
2+ use clippy_utils:: diagnostics:: span_lint_hir_and_then ;
33use clippy_utils:: is_from_proc_macro;
44use clippy_utils:: msrvs:: Msrv ;
55use rustc_errors:: Applicability ;
6- use rustc_hir:: def:: { DefKind , Res } ;
76use rustc_hir:: def_id:: DefId ;
8- use rustc_hir:: { Block , Body , HirId , Path , PathSegment , StabilityLevel , StableSince } ;
9- use rustc_lint:: { LateContext , LateLintPass , Lint , LintContext as _} ;
7+ use rustc_hir:: { Block , Body , HirId , Item , ItemKind , Path , PathSegment , StabilityLevel , StableSince } ;
8+ use rustc_lint:: { LateContext , LateLintPass , LintContext as _} ;
109use rustc_session:: impl_lint_pass;
10+ use rustc_span:: sym;
1111use rustc_span:: symbol:: kw;
12- use rustc_span:: { Span , sym} ;
1312
1413declare_clippy_lint ! {
1514 /// ### What it does
@@ -86,144 +85,106 @@ declare_clippy_lint! {
8685 "type is imported from std when available in core"
8786}
8887
89- impl_lint_pass ! ( StdReexports => [
88+ impl_lint_pass ! ( StdReexports < ' _> => [
9089 ALLOC_INSTEAD_OF_CORE ,
9190 STD_INSTEAD_OF_ALLOC ,
9291 STD_INSTEAD_OF_CORE ,
9392] ) ;
9493
95- pub struct StdReexports {
96- lint_points : Option < ( Span , Vec < LintPoint > ) > ,
94+ pub struct StdReexports < ' a > {
95+ lint_points : Vec < LintPoint < ' a > > ,
9796 msrv : Msrv ,
97+ use_item_depth : usize ,
9898}
9999
100- impl StdReexports {
100+ impl < ' a > StdReexports < ' a > {
101101 pub fn new ( conf : & ' static Conf ) -> Self {
102102 Self {
103- lint_points : Option :: default ( ) ,
103+ lint_points : Vec :: new ( ) ,
104104 msrv : conf. msrv ,
105- }
106- }
107-
108- fn lint_if_finish ( & mut self , cx : & LateContext < ' _ > , krate : Span , lint_point : LintPoint ) {
109- match & mut self . lint_points {
110- Some ( ( prev_krate, prev_lints) ) if prev_krate. overlaps ( krate) => {
111- prev_lints. push ( lint_point) ;
112- } ,
113- _ => emit_lints ( cx, self . lint_points . replace ( ( krate, vec ! [ lint_point] ) ) ) ,
105+ use_item_depth : 0 ,
114106 }
115107 }
116108}
117109
118- #[ derive( Debug ) ]
119- enum LintPoint {
120- Available ( Span , & ' static Lint , & ' static str , & ' static str ) ,
121- Conflict ,
110+ #[ derive( Debug , Clone , Copy ) ]
111+ struct LintPoint < ' a > {
112+ root : PathSegment < ' a > ,
113+ tail : PathSegment < ' a > ,
122114}
123115
124- impl < ' tcx > LateLintPass < ' tcx > for StdReexports {
125- fn check_path ( & mut self , cx : & LateContext < ' tcx > , path : & Path < ' tcx > , _: HirId ) {
126- if let Res :: Def ( def_kind, def_id) = path. res
127- && let Some ( first_segment) = get_first_segment ( path)
128- && is_stable ( cx, def_id, self . msrv )
129- && !path. span . in_external_macro ( cx. sess ( ) . source_map ( ) )
130- && !is_from_proc_macro ( cx, & first_segment. ident )
131- && !matches ! ( def_kind, DefKind :: Macro ( _) )
132- && let Some ( last_segment) = path. segments . last ( )
133- && let Res :: Def ( DefKind :: Mod , crate_def_id) = first_segment. res
134- && crate_def_id. is_crate_root ( )
116+ impl < ' tcx > LateLintPass < ' tcx > for StdReexports < ' tcx > {
117+ fn check_path ( & mut self , _: & LateContext < ' tcx > , path : & Path < ' tcx > , _: HirId ) {
118+ if self . use_item_depth == 0
119+ && path. segments . len ( ) > 1
120+ && let Some ( & root) = get_first_segment ( path)
121+ && let Some ( & tail) = path. segments . last ( )
135122 {
136- let ( lint, used_mod, replace_with) = match first_segment. ident . name {
137- sym:: std => match cx. tcx . crate_name ( def_id. krate ) {
138- sym:: core => ( STD_INSTEAD_OF_CORE , "std" , "core" ) ,
139- sym:: alloc => ( STD_INSTEAD_OF_ALLOC , "std" , "alloc" ) ,
140- _ => {
141- self . lint_if_finish ( cx, first_segment. ident . span , LintPoint :: Conflict ) ;
142- return ;
143- } ,
144- } ,
145- sym:: alloc if cx. tcx . crate_name ( def_id. krate ) == sym:: core => ( ALLOC_INSTEAD_OF_CORE , "alloc" , "core" ) ,
146- _ => {
147- self . lint_if_finish ( cx, first_segment. ident . span , LintPoint :: Conflict ) ;
148- return ;
149- } ,
150- } ;
123+ self . lint_points . push ( LintPoint {
124+ root,
125+ tail : PathSegment { res : path. res , ..tail } ,
126+ } ) ;
127+ }
128+ }
151129
152- self . lint_if_finish (
153- cx,
154- first_segment. ident . span ,
155- LintPoint :: Available ( last_segment. ident . span , lint, used_mod, replace_with) ,
156- ) ;
130+ fn check_item ( & mut self , _: & LateContext < ' tcx > , item : & ' tcx Item < ' tcx > ) {
131+ if let ItemKind :: Use ( path, _use_kind) = item. kind {
132+ if path. segments . len ( ) > 1
133+ && let Some ( & root) = get_first_segment ( path)
134+ && let Some ( & tail) = path. segments . last ( )
135+ {
136+ for res in path. res . into_iter ( ) . flatten ( ) {
137+ self . lint_points . push ( LintPoint {
138+ root,
139+ tail : PathSegment { res, ..tail } ,
140+ } ) ;
141+ }
142+ }
143+ self . use_item_depth += 1 ;
144+ }
145+ }
146+
147+ fn check_item_post ( & mut self , _: & LateContext < ' tcx > , item : & ' tcx Item < ' tcx > ) {
148+ if matches ! ( item. kind, ItemKind :: Use ( _path, _use_kind) ) {
149+ self . use_item_depth -= 1 ;
157150 }
158151 }
159152
160153 fn check_block_post ( & mut self , cx : & LateContext < ' tcx > , _: & Block < ' tcx > ) {
161- emit_lints ( cx, self . lint_points . take ( ) ) ;
154+ emit_lints ( cx, & mut self . lint_points , self . msrv ) ;
162155 }
163156
164157 fn check_body_post ( & mut self , cx : & LateContext < ' tcx > , _: & Body < ' tcx > ) {
165- emit_lints ( cx, self . lint_points . take ( ) ) ;
158+ emit_lints ( cx, & mut self . lint_points , self . msrv ) ;
166159 }
167160
168161 fn check_crate_post ( & mut self , cx : & LateContext < ' tcx > ) {
169- emit_lints ( cx, self . lint_points . take ( ) ) ;
162+ emit_lints ( cx, & mut self . lint_points , self . msrv ) ;
170163 }
171164}
172165
173- fn emit_lints ( cx : & LateContext < ' _ > , lint_points : Option < ( Span , Vec < LintPoint > ) > ) {
174- let Some ( ( krate_span, lint_points) ) = lint_points else {
175- return ;
176- } ;
166+ fn emit_lints ( cx : & LateContext < ' _ > , lint_points : & mut Vec < LintPoint < ' _ > > , msrv : Msrv ) {
167+ // This should already be the case, but we'll assert that it is
168+ lint_points. sort_by_key ( |x| ( x. root . ident . span , x. tail . ident . span ) ) ;
177169
178- let mut lint: Option < ( & ' static Lint , & ' static str , & ' static str ) > = None ;
179- let mut has_conflict = false ;
180- for lint_point in & lint_points {
181- match lint_point {
182- LintPoint :: Available ( _, l, used_mod, replace_with)
183- if lint. is_none_or ( |( prev_l, ..) | l. name == prev_l. name ) =>
184- {
185- lint = Some ( ( l, used_mod, replace_with) ) ;
186- } ,
187- _ => {
188- has_conflict = true ;
189- break ;
190- } ,
170+ for shared_root in lint_points. chunk_by ( |a, b| a. root . ident . span == b. root . ident . span ) {
171+ if let Some ( root) = shared_root. iter ( ) . find_map ( |a| a. root . res . opt_def_id ( ) )
172+ && !( root. is_crate_root ( ) && LintPoint :: try_emit ( shared_root, cx, msrv, true , root) )
173+ {
174+ for shared_tail in shared_root. chunk_by ( |a, b| a. tail . ident . span == b. tail . ident . span ) {
175+ LintPoint :: try_emit ( shared_tail, cx, msrv, false , root) ;
176+ }
191177 }
192178 }
193179
194- if !has_conflict && let Some ( ( lint, used_mod, replace_with) ) = lint {
195- span_lint_and_sugg (
196- cx,
197- lint,
198- krate_span,
199- format ! ( "used import from `{used_mod}` instead of `{replace_with}`" ) ,
200- format ! ( "consider importing the item from `{replace_with}`" ) ,
201- ( * replace_with) . to_string ( ) ,
202- Applicability :: MachineApplicable ,
203- ) ;
204- return ;
205- }
206-
207- for lint_point in lint_points {
208- let LintPoint :: Available ( span, lint, used_mod, replace_with) = lint_point else {
209- continue ;
210- } ;
211- span_lint_and_help (
212- cx,
213- lint,
214- span,
215- format ! ( "used import from `{used_mod}` instead of `{replace_with}`" ) ,
216- None ,
217- format ! ( "consider importing the item from `{replace_with}`" ) ,
218- ) ;
219- }
180+ lint_points. clear ( ) ;
220181}
221182
222183/// Returns the first named segment of a [`Path`].
223184///
224185/// If this is a global path (such as `::std::fmt::Debug`), then the segment after [`kw::PathRoot`]
225186/// is returned.
226- fn get_first_segment < ' tcx > ( path : & Path < ' tcx > ) -> Option < & ' tcx PathSegment < ' tcx > > {
187+ fn get_first_segment < ' tcx , T > ( path : & Path < ' tcx , T > ) -> Option < & ' tcx PathSegment < ' tcx > > {
227188 match path. segments {
228189 // A global path will have PathRoot as the first segment. In this case, return the segment after.
229190 [ x, y, ..] if x. ident . name == kw:: PathRoot => Some ( y) ,
@@ -232,11 +193,12 @@ fn get_first_segment<'tcx>(path: &Path<'tcx>) -> Option<&'tcx PathSegment<'tcx>>
232193 }
233194}
234195
235- /// Checks if all ancestors of `def_id` meet `msrv` to avoid linting [unstable moves](https://github.com/rust-lang/rust/pull/95956)
236- /// or now stable moves that were once unstable.
196+ /// Checks if all ancestors of `def_id` meet `msrv` at `node` to avoid linting
197+ /// [unstable moves](https://github.com/rust-lang/rust/pull/95956) or now stable
198+ /// moves that were once unstable.
237199///
238200/// Does not catch individually moved items
239- fn is_stable ( cx : & LateContext < ' _ > , mut def_id : DefId , msrv : Msrv ) -> bool {
201+ fn is_stable_at ( cx : & LateContext < ' _ > , mut def_id : DefId , msrv : Msrv , node : HirId ) -> bool {
240202 loop {
241203 if let Some ( stability) = cx. tcx . lookup_stability ( def_id) {
242204 match stability. level {
@@ -247,8 +209,8 @@ fn is_stable(cx: &LateContext<'_>, mut def_id: DefId, msrv: Msrv) -> bool {
247209 ..
248210 } => return true ,
249211 StabilityLevel :: Stable { since, .. } => match since {
250- StableSince :: Version ( v) if !msrv. meets ( cx, v) => return false ,
251- StableSince :: Current if msrv. current ( cx) . is_none ( ) => return false ,
212+ StableSince :: Version ( v) if !msrv. meets_at ( cx. tcx , node , v) => return false ,
213+ StableSince :: Current if msrv. at ( cx. tcx , node ) . is_none ( ) => return false ,
252214 StableSince :: Err ( _) => return false ,
253215 StableSince :: Version ( _) | StableSince :: Current => { } ,
254216 } ,
@@ -262,3 +224,53 @@ fn is_stable(cx: &LateContext<'_>, mut def_id: DefId, msrv: Msrv) -> bool {
262224 }
263225 }
264226}
227+
228+ impl < ' a > LintPoint < ' a > {
229+ fn try_emit ( lint_points : & [ Self ] , cx : & LateContext < ' _ > , msrv : Msrv , is_suggestion : bool , root : DefId ) -> bool {
230+ if let Some ( first) = lint_points. first ( )
231+ && let Some ( tail) = first. tail . res . opt_def_id ( )
232+ && !first. root . ident . span . in_external_macro ( cx. sess ( ) . source_map ( ) )
233+ && !is_from_proc_macro ( cx, & first. root . ident )
234+ && lint_points
235+ . iter ( )
236+ . map ( |lint_point| lint_point. tail . res . opt_def_id ( ) )
237+ . all ( |id| id. is_some_and ( |id| id. krate == tail. krate && is_stable_at ( cx, id, msrv, first. tail . hir_id ) ) )
238+ {
239+ let root = cx. tcx . crate_name ( root. krate ) ;
240+ let tail = cx. tcx . crate_name ( tail. krate ) ;
241+
242+ let ( lint, message, help) = match ( root, tail) {
243+ ( sym:: alloc, sym:: core) => (
244+ ALLOC_INSTEAD_OF_CORE ,
245+ "used import from `alloc` instead of `core`" ,
246+ "consider importing the item from `core`" ,
247+ ) ,
248+ ( sym:: std, sym:: alloc) => (
249+ STD_INSTEAD_OF_ALLOC ,
250+ "used import from `std` instead of `alloc`" ,
251+ "consider importing the item from `alloc`" ,
252+ ) ,
253+ ( sym:: std, sym:: core) => (
254+ STD_INSTEAD_OF_CORE ,
255+ "used import from `std` instead of `core`" ,
256+ "consider importing the item from `core`" ,
257+ ) ,
258+ _ => return false ,
259+ } ;
260+
261+ if is_suggestion {
262+ span_lint_hir_and_then ( cx, lint, first. root . hir_id , first. root . ident . span , message, |diag| {
263+ diag. span_suggestion ( first. root . ident . span , help, tail, Applicability :: MachineApplicable ) ;
264+ } ) ;
265+ } else {
266+ span_lint_hir_and_then ( cx, lint, first. tail . hir_id , first. tail . ident . span , message, |diag| {
267+ diag. help ( help) ;
268+ } ) ;
269+ }
270+
271+ true
272+ } else {
273+ false
274+ }
275+ }
276+ }
0 commit comments