11use clippy_config:: Conf ;
2- use clippy_utils:: diagnostics:: span_lint_and_then ;
2+ use clippy_utils:: diagnostics:: span_lint_and_sugg ;
33use clippy_utils:: is_from_proc_macro;
44use clippy_utils:: msrvs:: Msrv ;
55use rustc_attr_data_structures:: { StabilityLevel , StableSince } ;
66use rustc_errors:: Applicability ;
77use rustc_hir:: def:: Res ;
88use rustc_hir:: def_id:: DefId ;
9- use rustc_hir:: { HirId , Path , PathSegment } ;
10- use rustc_lint:: { LateContext , LateLintPass , LintContext } ;
9+ use rustc_hir:: { Block , Body , HirId , Path , PathSegment } ;
10+ use rustc_lint:: { LateContext , LateLintPass , Lint , LintContext } ;
1111use rustc_session:: impl_lint_pass;
1212use rustc_span:: symbol:: kw;
1313use rustc_span:: { Span , sym} ;
@@ -88,24 +88,35 @@ declare_clippy_lint! {
8888}
8989
9090pub struct StdReexports {
91- // Paths which can be either a module or a macro (e.g. `std::env`) will cause this check to happen
92- // twice. First for the mod, second for the macro. This is used to avoid the lint reporting for the macro
93- // when the path could be also be used to access the module.
94- prev_span : Span ,
91+ lint_point : ( Span , Option < LintPoint > ) ,
9592 msrv : Msrv ,
9693}
9794
9895impl StdReexports {
9996 pub fn new ( conf : & ' static Conf ) -> Self {
10097 Self {
101- prev_span : Span :: default ( ) ,
98+ lint_point : Default :: default ( ) ,
10299 msrv : conf. msrv ,
103100 }
104101 }
102+
103+ fn lint_if_finish ( & mut self , cx : & LateContext < ' _ > , ( span, item) : ( Span , Option < LintPoint > ) ) {
104+ if span. source_equal ( self . lint_point . 0 ) {
105+ return ;
106+ }
107+
108+ if !self . lint_point . 0 . is_dummy ( ) {
109+ emit_lints ( cx, & self . lint_point ) ;
110+ }
111+
112+ self . lint_point = ( span, item) ;
113+ }
105114}
106115
107116impl_lint_pass ! ( StdReexports => [ STD_INSTEAD_OF_CORE , STD_INSTEAD_OF_ALLOC , ALLOC_INSTEAD_OF_CORE ] ) ;
108117
118+ type LintPoint = ( & ' static Lint , & ' static str , & ' static str ) ;
119+
109120impl < ' tcx > LateLintPass < ' tcx > for StdReexports {
110121 fn check_path ( & mut self , cx : & LateContext < ' tcx > , path : & Path < ' tcx > , _: HirId ) {
111122 if let Res :: Def ( _, def_id) = path. res
@@ -119,40 +130,52 @@ impl<'tcx> LateLintPass<'tcx> for StdReexports {
119130 sym:: core => ( STD_INSTEAD_OF_CORE , "std" , "core" ) ,
120131 sym:: alloc => ( STD_INSTEAD_OF_ALLOC , "std" , "alloc" ) ,
121132 _ => {
122- self . prev_span = first_segment. ident . span ;
133+ self . lint_if_finish ( cx , ( first_segment. ident . span , None ) ) ;
123134 return ;
124135 } ,
125136 } ,
126137 sym:: alloc => {
127138 if cx. tcx . crate_name ( def_id. krate ) == sym:: core {
128139 ( ALLOC_INSTEAD_OF_CORE , "alloc" , "core" )
129140 } else {
130- self . prev_span = first_segment. ident . span ;
141+ self . lint_if_finish ( cx , ( first_segment. ident . span , None ) ) ;
131142 return ;
132143 }
133144 } ,
134145 _ => return ,
135146 } ;
136- if first_segment. ident . span != self . prev_span {
137- #[ expect( clippy:: collapsible_span_lint_calls, reason = "rust-clippy#7797" ) ]
138- span_lint_and_then (
139- cx,
140- lint,
141- first_segment. ident . span ,
142- format ! ( "used import from `{used_mod}` instead of `{replace_with}`" ) ,
143- |diag| {
144- diag. span_suggestion (
145- first_segment. ident . span ,
146- format ! ( "consider importing the item from `{replace_with}`" ) ,
147- replace_with. to_string ( ) ,
148- Applicability :: MachineApplicable ,
149- ) ;
150- } ,
151- ) ;
152- self . prev_span = first_segment. ident . span ;
153- }
147+
148+ self . lint_if_finish ( cx, ( first_segment. ident . span , Some ( ( lint, used_mod, replace_with) ) ) ) ;
154149 }
155150 }
151+
152+ fn check_block_post ( & mut self , cx : & LateContext < ' tcx > , _: & Block < ' tcx > ) {
153+ self . lint_if_finish ( cx, Default :: default ( ) ) ;
154+ }
155+
156+ fn check_body_post ( & mut self , cx : & LateContext < ' tcx > , _: & Body < ' tcx > ) {
157+ self . lint_if_finish ( cx, Default :: default ( ) ) ;
158+ }
159+
160+ fn check_crate_post ( & mut self , cx : & LateContext < ' tcx > ) {
161+ self . lint_if_finish ( cx, Default :: default ( ) ) ;
162+ }
163+ }
164+
165+ fn emit_lints ( cx : & LateContext < ' _ > , ( span, item) : & ( Span , Option < LintPoint > ) ) {
166+ let Some ( ( lint, used_mod, replace_with) ) = item else {
167+ return ;
168+ } ;
169+
170+ span_lint_and_sugg (
171+ cx,
172+ lint,
173+ * span,
174+ format ! ( "used import from `{used_mod}` instead of `{replace_with}`" ) ,
175+ format ! ( "consider importing the item from `{replace_with}`" ) ,
176+ ( * replace_with) . to_string ( ) ,
177+ Applicability :: MachineApplicable ,
178+ ) ;
156179}
157180
158181/// Returns the first named segment of a [`Path`].
0 commit comments