@@ -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 ;
5+ use rustc_errors:: { Applicability , MultiSpan } ;
66use rustc_hir:: def:: { DefKind , Res } ;
77use rustc_hir:: def_id:: DefId ;
88use rustc_hir:: { Block , Body , HirId , Path , PathSegment , StabilityLevel , StableSince } ;
99use rustc_lint:: { LateContext , LateLintPass , Lint , LintContext } ;
1010use rustc_session:: impl_lint_pass;
1111use rustc_span:: symbol:: kw;
12- use rustc_span:: { Span , sym} ;
12+ use rustc_span:: { Span , Symbol , sym} ;
13+ use std:: collections:: BTreeMap ;
1314
1415declare_clippy_lint ! {
1516 /// ### What it does
@@ -93,7 +94,7 @@ impl_lint_pass!(StdReexports => [
9394] ) ;
9495
9596pub struct StdReexports {
96- lint_points : Option < ( Span , Vec < LintPoint > ) > ,
97+ lint_points : Option < ( Span , LintPoints ) > ,
9798 msrv : Msrv ,
9899}
99100
@@ -105,55 +106,105 @@ impl StdReexports {
105106 }
106107 }
107108
108- fn lint_if_finish ( & mut self , cx : & LateContext < ' _ > , krate : Span , lint_point : LintPoint ) {
109- match & mut self . lint_points {
109+ fn lint_if_finish ( & mut self , cx : & LateContext < ' _ > , krate : Span , lint_point : LintPoint , path : & Path < ' _ > ) {
110+ let mut lint_points = match & mut self . lint_points {
110111 Some ( ( prev_krate, prev_lints) ) if prev_krate. overlaps ( krate) => {
111- prev_lints. push ( lint_point) ;
112+ prev_lints. lint_point . update ( lint_point) ;
113+ prev_lints
112114 } ,
113- _ => emit_lints ( cx, self . lint_points . replace ( ( krate, vec ! [ lint_point] ) ) ) ,
115+ _ => {
116+ emit_lints ( cx, self . lint_points . replace ( ( krate, LintPoints :: new ( lint_point) ) ) ) ;
117+ & mut self . lint_points . as_mut ( ) . unwrap ( ) . 1
118+ } ,
119+ } ;
120+
121+ for segment in path
122+ . segments
123+ . iter ( )
124+ . skip_while ( |segment| segment. ident . name == kw:: PathRoot )
125+ {
126+ lint_points = lint_points
127+ . children
128+ . entry ( segment. ident . span )
129+ . or_insert ( LintPoints :: new ( lint_point) ) ;
130+ lint_points. lint_point . update ( lint_point) ;
114131 }
115132 }
116133}
117134
118- #[ derive( Debug ) ]
135+ #[ derive( Debug , PartialEq , Eq , PartialOrd , Ord , Clone , Copy ) ]
119136enum LintPoint {
120- Available ( Span , & ' static Lint , & ' static str , & ' static str ) ,
137+ Available ( Symbol , Symbol ) ,
121138 Conflict ,
122139}
123140
141+ impl LintPoint {
142+ fn into_lint ( self ) -> Option < ( & ' static Lint , & ' static Symbol , & ' static Symbol ) > {
143+ match self {
144+ Self :: Available ( sym:: alloc, sym:: core) => Some ( ( ALLOC_INSTEAD_OF_CORE , & sym:: alloc, & sym:: core) ) ,
145+ Self :: Available ( sym:: std, sym:: alloc) => Some ( ( STD_INSTEAD_OF_ALLOC , & sym:: std, & sym:: alloc) ) ,
146+ Self :: Available ( sym:: std, sym:: core) => Some ( ( STD_INSTEAD_OF_CORE , & sym:: std, & sym:: core) ) ,
147+ _ => None ,
148+ }
149+ }
150+
151+ fn from_symbols ( used : Symbol , defined_in : Symbol ) -> Self {
152+ match ( used, defined_in) {
153+ ( sym:: std, sym:: core) => Self :: Available ( sym:: std, sym:: core) ,
154+ ( sym:: std, sym:: alloc) => Self :: Available ( sym:: std, sym:: alloc) ,
155+ ( sym:: alloc, sym:: core) => Self :: Available ( sym:: alloc, sym:: core) ,
156+ _ => Self :: Conflict ,
157+ }
158+ }
159+
160+ fn update ( & mut self , other : Self ) {
161+ * self = match ( * self , other) {
162+ ( Self :: Conflict , _) | ( _, Self :: Conflict ) => Self :: Conflict ,
163+ ( Self :: Available ( a_used, a_replace) , Self :: Available ( b_used, b_replace) ) => {
164+ if a_used != b_used {
165+ Self :: Conflict
166+ } else if a_replace == b_replace {
167+ other
168+ } else {
169+ Self :: Available ( a_used, sym:: alloc)
170+ }
171+ } ,
172+ } ;
173+ }
174+ }
175+
176+ #[ derive( Debug ) ]
177+ struct LintPoints {
178+ lint_point : LintPoint ,
179+ children : BTreeMap < Span , LintPoints > ,
180+ }
181+
182+ impl LintPoints {
183+ fn new ( lint_point : LintPoint ) -> Self {
184+ Self {
185+ lint_point,
186+ children : BTreeMap :: new ( ) ,
187+ }
188+ }
189+ }
190+
124191impl < ' tcx > LateLintPass < ' tcx > for StdReexports {
125192 fn check_path ( & mut self , cx : & LateContext < ' tcx > , path : & Path < ' tcx > , _: HirId ) {
126193 if let Res :: Def ( def_kind, def_id) = path. res
127194 && let Some ( first_segment) = get_first_segment ( path)
128- && is_stable ( cx, def_id, self . msrv )
129195 && !path. span . in_external_macro ( cx. sess ( ) . source_map ( ) )
130196 && !is_from_proc_macro ( cx, & first_segment. ident )
131197 && !matches ! ( def_kind, DefKind :: Macro ( _) )
132- && let Some ( last_segment) = path. segments . last ( )
133198 && let Res :: Def ( DefKind :: Mod , crate_def_id) = first_segment. res
134199 && crate_def_id. is_crate_root ( )
135200 {
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- } ,
201+ let lint_point = if is_stable ( cx, def_id, self . msrv ) {
202+ LintPoint :: from_symbols ( first_segment. ident . name , cx. tcx . crate_name ( def_id. krate ) )
203+ } else {
204+ LintPoint :: Conflict
150205 } ;
151206
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- ) ;
207+ self . lint_if_finish ( cx, first_segment. ident . span , lint_point, path) ;
157208 }
158209 }
159210
@@ -170,28 +221,31 @@ impl<'tcx> LateLintPass<'tcx> for StdReexports {
170221 }
171222}
172223
173- fn emit_lints ( cx : & LateContext < ' _ > , lint_points : Option < ( Span , Vec < LintPoint > ) > ) {
224+ fn emit_lints ( cx : & LateContext < ' _ > , lint_points : Option < ( Span , LintPoints ) > ) {
174225 let Some ( ( krate_span, lint_points) ) = lint_points else {
175226 return ;
176227 } ;
177228
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- } ,
229+ let mut combined = BTreeMap :: new ( ) ;
230+ let mut stack = vec ! [ ( krate_span, lint_points) ] ;
231+ while let Some ( ( span, lint_points) ) = stack. pop ( ) {
232+ if let LintPoint :: Conflict = lint_points. lint_point {
233+ stack. extend ( lint_points. children ) ;
191234 }
235+
236+ combined
237+ . entry ( lint_points. lint_point )
238+ . or_insert_with ( MultiSpan :: new)
239+ . push_primary_span ( span) ;
192240 }
241+ let mut combined = combined. into_iter ( ) . collect :: < Vec < _ > > ( ) ;
242+ combined. sort_by_key ( |( _, span) | span. primary_span ( ) ) ;
193243
194- if !has_conflict && let Some ( ( lint, used_mod, replace_with) ) = lint {
244+ if let Some ( ( lint, used_mod, replace_with) ) = combined
245+ . first ( )
246+ . filter ( |_| combined. len ( ) == 1 )
247+ . and_then ( |first| first. 0 . into_lint ( ) )
248+ {
195249 span_lint_and_sugg (
196250 cx,
197251 lint,
@@ -204,8 +258,8 @@ fn emit_lints(cx: &LateContext<'_>, lint_points: Option<(Span, Vec<LintPoint>)>)
204258 return ;
205259 }
206260
207- for lint_point in lint_points {
208- let LintPoint :: Available ( span , lint, used_mod, replace_with) = lint_point else {
261+ for ( lint_point, span ) in combined {
262+ let Some ( ( lint, used_mod, replace_with) ) = lint_point. into_lint ( ) else {
209263 continue ;
210264 } ;
211265 span_lint_and_help (
0 commit comments