@@ -3,52 +3,53 @@ use clippy_utils::diagnostics::span_lint_and_then;
33use clippy_utils:: msrvs:: Msrv ;
44use clippy_utils:: res:: { MaybeDef as _, MaybeTypeckRes as _} ;
55use clippy_utils:: source:: snippet_opt;
6- use clippy_utils:: ty:: implements_trait;
6+ use clippy_utils:: ty:: { deref_chain , implements_trait} ;
77use clippy_utils:: visitors:: is_const_evaluatable;
88use clippy_utils:: { contains_name, msrvs, sym} ;
99use rustc_ast:: LitKind ;
1010use rustc_data_structures:: packed:: Pu128 ;
1111use rustc_errors:: Applicability ;
1212use rustc_hir:: def_id:: DefId ;
13- use rustc_hir:: { Expr , ExprKind , ImplItemKind , Item , ItemKind , Node , QPath , TraitFn , TraitItemKind } ;
13+ use rustc_hir:: { Expr , ExprKind , ImplItemKind , Item , ItemKind , Node , QPath , TraitFn , TraitItemKind , UnOp } ;
1414use rustc_lint:: LateContext ;
15- use rustc_middle:: ty;
16- use rustc_middle:: ty:: adjustment:: { Adjust , AutoBorrow , AutoBorrowMutability } ;
1715use rustc_middle:: ty:: { AssocTag , EarlyBinder , Ty , Unnormalized } ;
1816use rustc_span:: { Span , Symbol } ;
19- use std:: fmt:: Display ;
2017use std:: ops:: Not as _;
2118
2219const ARRAY_WINDOWS : Symbol = sym:: array_windows;
2320const WINDOWS : Symbol = sym:: windows;
2421const SLICE : Symbol = sym:: slice;
2522
26- enum SpanLocation {
27- MethodCall ,
28- EntireExpression ,
29- }
30-
23+ #[ derive( Debug , Clone , PartialEq , Eq , Hash ) ]
3124struct Suggestion {
3225 span_location : SpanLocation ,
3326 code : String ,
3427 destructuring_example : Option < String > ,
3528}
3629
37- enum DestructuringExtent {
38- Full ,
39- Partial ,
30+ #[ derive( Debug , Clone , Copy , PartialEq , Eq , Hash ) ]
31+ enum SpanLocation {
32+ MethodCall ,
33+ EntireExpression ,
4034}
4135
36+ #[ derive( Debug , Clone , PartialEq , Eq , Hash ) ]
4237struct DestructuredArray {
4338 array_snippet : String ,
4439 extent : DestructuringExtent ,
4540}
4641
42+ #[ derive( Debug , Clone , Copy , PartialEq , Eq , Hash ) ]
43+ enum DestructuringExtent {
44+ Full ,
45+ Partial ,
46+ }
47+
4748pub ( super ) fn check < ' tcx > (
4849 cx : & LateContext < ' tcx > ,
49- expr : & Expr < ' _ > ,
50- recv : & Expr < ' _ > ,
51- size_arg : & ' tcx Expr < ' _ > ,
50+ expr : & ' tcx Expr < ' tcx > ,
51+ recv : & ' tcx Expr < ' tcx > ,
52+ size_arg : & ' tcx Expr < ' tcx > ,
5253 call_span : Span ,
5354 msrv : Msrv ,
5455) {
@@ -104,7 +105,11 @@ fn is_const_size_window<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>, size_arg:
104105 is_slice_method_call && is_const_evaluatable ( cx. tcx , cx. typeck_results ( ) , size_arg)
105106}
106107
107- fn compute_suggestion ( cx : & LateContext < ' _ > , recv : & Expr < ' _ > , size_arg : & Expr < ' _ > ) -> Option < Suggestion > {
108+ fn compute_suggestion < ' tcx > (
109+ cx : & LateContext < ' tcx > ,
110+ recv : & ' tcx Expr < ' tcx > ,
111+ size_arg : & ' tcx Expr < ' tcx > ,
112+ ) -> Option < Suggestion > {
108113 let ( recv_snippet, size_arg_snippet) = Option :: zip (
109114 snippet_opt ( cx, recv. span . source_callsite ( ) ) ,
110115 snippet_opt ( cx, size_arg. span . source_callsite ( ) ) ,
@@ -116,14 +121,14 @@ fn compute_suggestion(cx: &LateContext<'_>, recv: &Expr<'_>, size_arg: &Expr<'_>
116121 size_arg_snippet
117122 } ;
118123
119- let is_ucfs_required = is_array_windows_shadowed ( cx, cx . typeck_results ( ) . expr_ty ( recv) . peel_refs ( ) ) ;
124+ let is_ucfs_required = is_array_windows_method_call_ambiguous ( cx, recv) ;
120125
121126 let ( span_location, code) = if is_ucfs_required {
122127 (
123128 SpanLocation :: EntireExpression ,
124129 format ! (
125130 "<[_]>::{ARRAY_WINDOWS}::<{size_generic_const_arg}>({})" ,
126- add_ref_prefix_for_ucfs_if_required ( cx, recv, & recv_snippet )
131+ normalize_expr_to_single_ref ( cx, recv) ?
127132 ) ,
128133 )
129134 } else {
@@ -134,21 +139,20 @@ fn compute_suggestion(cx: &LateContext<'_>, recv: &Expr<'_>, size_arg: &Expr<'_>
134139 } ;
135140
136141 let destructuring_example =
137- build_destructured_array ( cx, size_arg) . map ( |DestructuredArray { array_snippet, extent } | {
142+ build_destructured_array ( cx, size_arg) . and_then ( |DestructuredArray { array_snippet, extent } | {
138143 let iterator_expr_snippet = match extent {
139- DestructuringExtent :: Full if is_ucfs_required => format ! (
140- "<[_]>::{ARRAY_WINDOWS}({})" ,
141- add_ref_prefix_for_ucfs_if_required( cx, recv, & recv_snippet)
142- ) ,
144+ DestructuringExtent :: Full if is_ucfs_required => {
145+ format ! ( "<[_]>::{ARRAY_WINDOWS}({})" , normalize_expr_to_single_ref( cx, recv) ?)
146+ } ,
143147 DestructuringExtent :: Full => format ! ( "{recv_snippet}.{ARRAY_WINDOWS}()" ) ,
144148 DestructuringExtent :: Partial if is_ucfs_required => format ! (
145149 "<[_]>::{ARRAY_WINDOWS}::<{size_generic_const_arg}>({})" ,
146- add_ref_prefix_for_ucfs_if_required ( cx, recv, & recv_snippet )
150+ normalize_expr_to_single_ref ( cx, recv) ?
147151 ) ,
148152 DestructuringExtent :: Partial => format ! ( "{recv_snippet}.{ARRAY_WINDOWS}::<{size_generic_const_arg}>()" ) ,
149153 } ;
150154
151- format ! ( "for {array_snippet} in {iterator_expr_snippet}" )
155+ Some ( format ! ( "for {array_snippet} in {iterator_expr_snippet}" ) )
152156 } ) ;
153157
154158 Some ( Suggestion {
@@ -212,11 +216,26 @@ fn expr_needs_braces_in_const_arg(expr: &Expr<'_>) -> bool {
212216 }
213217}
214218
215- fn is_array_windows_shadowed < ' tcx > ( cx : & LateContext < ' tcx > , ty : Ty < ' tcx > ) -> bool {
219+ /// We are overly careful with this check: we allow FP because our goal is never allowing FN.
220+ /// The worst-case scenario of being overly careful is that UCFS is suggested when it isn't strictly
221+ /// necessary.
222+ fn is_array_windows_method_call_ambiguous < ' tcx > ( cx : & LateContext < ' tcx > , recv : & Expr < ' _ > ) -> bool {
223+ let typeck_results = cx. typeck_results ( ) ;
224+ let candidate_recv_types: Vec < _ > = [ typeck_results. expr_ty ( recv) , typeck_results. expr_ty_adjusted ( recv) ]
225+ . into_iter ( )
226+ . flat_map ( |ty| deref_chain ( cx, ty) )
227+ . collect ( ) ;
228+
229+ let recv_may_implements_trait = |trait_def_id : DefId | {
230+ candidate_recv_types
231+ . iter ( )
232+ . any ( |& ty| implements_trait ( cx, ty, trait_def_id, & [ ] ) )
233+ } ;
234+
216235 cx. tcx
217- . all_traits_including_private ( )
236+ . visible_traits ( )
218237 . filter ( |& trait_def_id| trait_declares_array_windows ( cx, trait_def_id) )
219- . any ( |trait_def_id| implements_trait ( cx , ty , trait_def_id , & [ ] ) )
238+ . any ( recv_may_implements_trait )
220239}
221240
222241fn trait_declares_array_windows ( cx : & LateContext < ' _ > , trait_def_id : DefId ) -> bool {
@@ -226,23 +245,6 @@ fn trait_declares_array_windows(cx: &LateContext<'_>, trait_def_id: DefId) -> bo
226245 . any ( |item| item. tag ( ) == AssocTag :: Fn )
227246}
228247
229- fn is_ref_prefix_required_for_ucfs ( cx : & LateContext < ' _ > , recv : & Expr < ' _ > ) -> bool {
230- !matches ! ( cx. typeck_results( ) . expr_ty( recv) . kind( ) , ty:: Ref ( ..) )
231- && cx
232- . typeck_results ( )
233- . expr_adjustments ( recv)
234- . iter ( )
235- . any ( |adj| matches ! ( adj. kind, Adjust :: Borrow ( AutoBorrow :: Ref ( AutoBorrowMutability :: Not ) ) ) )
236- }
237-
238- fn add_ref_prefix_for_ucfs_if_required ( cx : & LateContext < ' _ > , recv : & Expr < ' _ > , recv_snippet : impl Display ) -> String {
239- if is_ref_prefix_required_for_ucfs ( cx, recv) {
240- format ! ( "&{recv_snippet}" )
241- } else {
242- recv_snippet. to_string ( )
243- }
244- }
245-
246248fn get_containing_fn < ' tcx > ( cx : & LateContext < ' tcx > , expr : & Expr < ' _ > ) -> Option < & ' tcx Expr < ' tcx > > {
247249 let parent_item = cx. tcx . hir_get_parent_item ( expr. hir_id ) ;
248250
@@ -264,3 +266,30 @@ fn get_containing_fn<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>) -> Option<&'
264266
265267 Some ( cx. tcx . hir_body ( body_id?) . value )
266268}
269+
270+ /// Attempt to produce a snippet of type &T by removing/adding `&` and `*` operators.
271+ /// If this is not possible, return `None`.
272+ /// Stops at macro expansion.
273+ ///
274+ /// Examples:
275+ /// if `expr` is of type `T` -> `&expr`
276+ /// if `expr` is of type `&T` -> `expr`
277+ /// if `expr` is of type `&&T` -> `*expr`
278+ fn normalize_expr_to_single_ref ( cx : & LateContext < ' _ > , recv : & Expr < ' _ > ) -> Option < String > {
279+ // Peel & and * until we reach macro expansion or core referent
280+ let mut referent = recv;
281+ while !referent. span . from_expansion ( )
282+ && let ExprKind :: AddrOf ( _, _, inner) | ExprKind :: Unary ( UnOp :: Deref , inner) = referent. kind
283+ {
284+ referent = inner;
285+ }
286+
287+ let referent_snippet = snippet_opt ( cx, referent. span . source_callsite ( ) ) ?;
288+ let snippet = if cx. typeck_results ( ) . expr_ty ( referent) . is_ref ( ) {
289+ format ! ( "{referent_snippet}" )
290+ } else {
291+ format ! ( "&{referent_snippet}" )
292+ } ;
293+
294+ Some ( snippet)
295+ }
0 commit comments