@@ -2,14 +2,15 @@ use clippy_config::Conf;
22use clippy_utils:: diagnostics:: { span_lint_and_help, span_lint_and_sugg} ;
33use clippy_utils:: is_from_proc_macro;
44use clippy_utils:: msrvs:: Msrv ;
5- use rustc_errors:: Applicability ;
6- use rustc_hir:: def:: { DefKind , Res } ;
5+ use clippy_utils:: paths:: { PathNS , lookup_path} ;
6+ use rustc_errors:: { Applicability , MultiSpan } ;
7+ use rustc_hir:: def:: { DefKind , Namespace , Res } ;
78use rustc_hir:: def_id:: DefId ;
89use rustc_hir:: { Block , Body , HirId , Path , PathSegment , StabilityLevel , StableSince } ;
9- use rustc_lint:: { LateContext , LateLintPass , Lint , LintContext } ;
10+ use rustc_lint:: { LateContext , LateLintPass , LintContext } ;
1011use rustc_session:: impl_lint_pass;
1112use rustc_span:: symbol:: kw;
12- use rustc_span:: { Span , sym} ;
13+ use rustc_span:: { Span , Symbol , sym} ;
1314
1415declare_clippy_lint ! {
1516 /// ### What it does
@@ -116,43 +117,55 @@ impl StdReexports {
116117}
117118
118119#[ derive( Debug ) ]
119- enum LintPoint {
120- Available ( Span , & ' static Lint , & ' static str , & ' static str ) ,
121- Conflict ,
120+ struct LintPoint {
121+ span : Span ,
122+ used_from : Symbol ,
123+ is_stable : bool ,
124+ available_from_core : bool ,
125+ available_from_alloc : bool ,
122126}
123127
124128impl < ' tcx > LateLintPass < ' tcx > for StdReexports {
125129 fn check_path ( & mut self , cx : & LateContext < ' tcx > , path : & Path < ' tcx > , _: HirId ) {
126130 if let Res :: Def ( def_kind, def_id) = path. res
127131 && let Some ( first_segment) = get_first_segment ( path)
128- && is_stable ( cx, def_id, self . msrv )
129132 && !path. span . in_external_macro ( cx. sess ( ) . source_map ( ) )
130133 && !is_from_proc_macro ( cx, & first_segment. ident )
131134 && !matches ! ( def_kind, DefKind :: Macro ( _) )
132135 && let Some ( last_segment) = path. segments . last ( )
133136 && let Res :: Def ( DefKind :: Mod , crate_def_id) = first_segment. res
134137 && crate_def_id. is_crate_root ( )
135138 {
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- } ,
139+ let namespace = match def_kind. ns ( ) {
140+ Some ( Namespace :: TypeNS ) => PathNS :: Type ,
141+ Some ( Namespace :: ValueNS ) => PathNS :: Value ,
142+ Some ( Namespace :: MacroNS ) => PathNS :: Macro ,
143+ None => PathNS :: Arbitrary ,
150144 } ;
151145
146+ let mut path_new = path
147+ . segments
148+ . iter ( )
149+ . map ( |segment| segment. ident . name )
150+ . skip_while ( |& segment| segment == kw:: PathRoot )
151+ . collect :: < Vec < _ > > ( ) ;
152+
153+ path_new[ 0 ] = sym:: core;
154+ let available_from_core = lookup_path ( cx. tcx , namespace, & path_new) . contains ( & def_id) ;
155+
156+ path_new[ 0 ] = sym:: alloc;
157+ let available_from_alloc = lookup_path ( cx. tcx , namespace, & path_new) . contains ( & def_id) ;
158+
152159 self . lint_if_finish (
153160 cx,
154161 first_segment. ident . span ,
155- LintPoint :: Available ( last_segment. ident . span , lint, used_mod, replace_with) ,
162+ LintPoint {
163+ span : last_segment. ident . span ,
164+ used_from : first_segment. ident . name ,
165+ is_stable : is_stable ( cx, def_id, self . msrv ) ,
166+ available_from_core,
167+ available_from_alloc,
168+ } ,
156169 ) ;
157170 }
158171 }
@@ -171,27 +184,78 @@ impl<'tcx> LateLintPass<'tcx> for StdReexports {
171184}
172185
173186fn emit_lints ( cx : & LateContext < ' _ > , lint_points : Option < ( Span , Vec < LintPoint > ) > ) {
174- let Some ( ( krate_span, lint_points) ) = lint_points else {
187+ let Some ( ( krate_span, mut lint_points) ) = lint_points else {
175188 return ;
176189 } ;
177190
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- } ,
191+ // It's possible for multiple items to come from the same path.
192+ // For example, `std::vec` refers to a macro and a module.
193+ // In these cases, it's possible for them to have different availabilities.
194+ // `std::vec` as a macro and a module are both defined in `alloc` and unavailable in `core`.
195+ // Whereas, `std::env` is not available in `alloc`, and only the macro is available in `core`.
196+ // Since we aren't checking which of the shadowed items the user needs, we take the intersection
197+ // of these availabilities to ensure we don't provide the user a false positive.
198+ lint_points. sort_by_key ( |lint_point| lint_point. span ) ;
199+ lint_points. dedup_by ( |a, b| {
200+ if a. span == b. span {
201+ b. is_stable &= a. is_stable ;
202+ b. available_from_alloc &= a. available_from_alloc ;
203+ b. available_from_core &= a. available_from_core ;
204+ true
205+ } else {
206+ false
207+ }
208+ } ) ;
209+
210+ let mut core_span = MultiSpan :: new ( ) ;
211+ let mut alloc_span = MultiSpan :: new ( ) ;
212+ let mut all_from_std = true ;
213+ let mut all_from_alloc = true ;
214+ let mut all_core = true ;
215+ let mut all_alloc = true ;
216+
217+ for lint_point in lint_points {
218+ all_from_std &= lint_point. used_from == sym:: std;
219+ all_from_alloc &= lint_point. used_from == sym:: alloc;
220+
221+ if lint_point. is_stable && lint_point. available_from_core {
222+ core_span. push_primary_span ( lint_point. span ) ;
223+ } else {
224+ all_core = false ;
225+ }
226+
227+ if lint_point. is_stable && lint_point. available_from_alloc {
228+ if !lint_point. available_from_core {
229+ alloc_span. push_primary_span ( lint_point. span ) ;
230+ }
231+ } else {
232+ all_alloc = false ;
191233 }
192234 }
193235
194- if !has_conflict && let Some ( ( lint, used_mod, replace_with) ) = lint {
236+ let mut helps = Vec :: new ( ) ;
237+ let mut suggestions = Vec :: new ( ) ;
238+
239+ if all_from_std {
240+ if all_core {
241+ suggestions. push ( ( STD_INSTEAD_OF_CORE , & sym:: std, & sym:: core) ) ;
242+ helps. push ( ( STD_INSTEAD_OF_ALLOC , & sym:: std, & sym:: alloc, alloc_span) ) ;
243+ } else if all_alloc {
244+ suggestions. push ( ( STD_INSTEAD_OF_ALLOC , & sym:: std, & sym:: alloc) ) ;
245+ helps. push ( ( STD_INSTEAD_OF_CORE , & sym:: std, & sym:: core, core_span) ) ;
246+ } else {
247+ helps. push ( ( STD_INSTEAD_OF_CORE , & sym:: std, & sym:: core, core_span) ) ;
248+ helps. push ( ( STD_INSTEAD_OF_ALLOC , & sym:: std, & sym:: alloc, alloc_span) ) ;
249+ }
250+ } else if all_from_alloc {
251+ if all_core {
252+ suggestions. push ( ( ALLOC_INSTEAD_OF_CORE , & sym:: alloc, & sym:: core) ) ;
253+ } else {
254+ helps. push ( ( ALLOC_INSTEAD_OF_CORE , & sym:: alloc, & sym:: core, core_span) ) ;
255+ }
256+ }
257+
258+ for ( lint, used_mod, replace_with) in suggestions {
195259 span_lint_and_sugg (
196260 cx,
197261 lint,
@@ -201,21 +265,19 @@ fn emit_lints(cx: &LateContext<'_>, lint_points: Option<(Span, Vec<LintPoint>)>)
201265 ( * replace_with) . to_string ( ) ,
202266 Applicability :: MachineApplicable ,
203267 ) ;
204- return ;
205268 }
206269
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- ) ;
270+ for ( lint, used_mod, replace_with, span) in helps {
271+ for & span in span. primary_spans ( ) {
272+ span_lint_and_help (
273+ cx,
274+ lint,
275+ span,
276+ format ! ( "used import from `{used_mod}` instead of `{replace_with}`" ) ,
277+ None ,
278+ format ! ( "consider importing the item from `{replace_with}`" ) ,
279+ ) ;
280+ }
219281 }
220282}
221283
0 commit comments