@@ -11,7 +11,7 @@ use rustc_hir::{Block, Expr, ExprKind, HirId, LetStmt, Node, UnOp};
1111use rustc_lint:: { LateContext , LateLintPass } ;
1212use rustc_middle:: ty:: adjustment:: { Adjust , AutoBorrow , AutoBorrowMutability } ;
1313use rustc_session:: declare_lint_pass;
14- use rustc_span:: sym;
14+ use rustc_span:: { Span , sym} ;
1515
1616declare_clippy_lint ! {
1717 /// ### What it does
@@ -36,7 +36,7 @@ declare_clippy_lint! {
3636 ///
3737 /// }
3838 /// ```
39- #[ clippy:: version = "1.78 .0" ]
39+ #[ clippy:: version = "1.86 .0" ]
4040 pub UNNECESSARY_INDEXING ,
4141 complexity,
4242 "unnecessary use of `seq.is_empty()` in a conditional when if..let is more appropriate"
@@ -47,6 +47,7 @@ declare_lint_pass!(UnnecessaryIndexing => [UNNECESSARY_INDEXING]);
4747impl < ' tcx > LateLintPass < ' tcx > for UnnecessaryIndexing {
4848 fn check_expr ( & mut self , cx : & LateContext < ' tcx > , expr : & ' _ Expr < ' tcx > ) {
4949 if let Some ( if_expr) = clippy_utils:: higher:: If :: hir ( expr)
50+ && !if_expr. cond . span . from_expansion ( )
5051 // check for negation
5152 && let ExprKind :: Unary ( UnOp :: Not , unary_inner) = if_expr. cond . kind
5253 // check for call of is_empty
@@ -60,96 +61,65 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryIndexing {
6061 && expr_ty. ref_mutability ( ) != Some ( Mutability :: Mut )
6162 && let Some ( con_path) = path_to_local ( conditional_receiver)
6263 && let Some ( r) = process_indexing ( cx, block, con_path)
63- && let Some ( receiver ) = r. index_receiver
64+ && let Some ( receiver_span ) = r. index_receiver_span
6465 {
66+ let receiver = snippet ( cx, receiver_span, ".." ) ;
67+ let mut suggestions: Vec < ( Span , String ) > = vec ! [ ] ;
68+ let mut message = "consider using `if..let` syntax instead of indexing" . to_string ( ) ;
6569 span_lint_and_then (
6670 cx,
6771 UNNECESSARY_INDEXING ,
6872 expr. span ,
69- "condition can be simplified with if..let syntax" ,
73+ "condition can be simplified with ` if..let` syntax" ,
7074 |diag| {
7175 if let Some ( first_local) = r. first_local
72- && first_local. pat . simple_ident ( ) . is_some ( )
76+ && let Some ( name ) = first_local. pat . simple_ident ( ) . map ( |ident| ident . name )
7377 {
74- diag . span_suggestion (
78+ suggestions . push ( (
7579 if_expr. cond . span ,
76- "consider using if..let syntax (variable may need to be dereferenced)" ,
7780 format ! (
78- "let Some({}{}) = {}.first()" ,
81+ "let Some({}{name }) = {receiver }.first()" ,
7982 // if we arent borrowing anything then we can pass a reference here for correctness
8083 if r. extra_exprs_borrow. is_empty( ) { "&" } else { "" } ,
81- snippet( cx, first_local. pat. span, ".." ) ,
82- snippet( cx, receiver. span, ".." )
8384 ) ,
84- Applicability :: Unspecified ,
85- ) ;
86- diag. span_suggestion ( first_local. span , "remove this line" , "" , Applicability :: Unspecified ) ;
87- if !r. extra_exprs_borrow . is_empty ( ) {
88- let mut index_accesses = r
89- . extra_exprs_borrow
90- . iter ( )
91- . map ( |x| ( x. span , snippet ( cx, first_local. pat . span , ".." ) . to_string ( ) ) )
92- . collect :: < Vec < _ > > ( ) ;
85+ ) ) ;
86+ suggestions. push ( ( first_local. span , String :: new ( ) ) ) ;
9387
94- index_accesses. extend (
95- r. extra_exprs_copy
88+ if !r. extra_exprs_borrow . is_empty ( ) {
89+ suggestions. extend (
90+ r. extra_exprs_borrow
9691 . iter ( )
97- . map ( |x| ( x. span , snippet ( cx, first_local. pat . span , ".." ) . to_string ( ) ) ) ,
92+ . chain ( r. extra_exprs_copy . iter ( ) )
93+ . map ( |x| ( x. span , name. to_string ( ) ) ) ,
9894 ) ;
9995
100- diag. multipart_suggestion (
101- "set this indexing to be the `Some` variant (may need dereferencing)" ,
102- index_accesses,
103- Applicability :: Unspecified ,
104- ) ;
96+ message. push_str ( ", and replacing indexing expression(s) with the value in `Some` variant" ) ;
10597 } else if !r. extra_exprs_copy . is_empty ( ) {
106- let index_accesses = r
107- . extra_exprs_copy
108- . iter ( )
109- . map ( |x| ( x. span , snippet ( cx, first_local. pat . span , ".." ) . to_string ( ) ) )
110- . collect :: < Vec < _ > > ( ) ;
111-
112- diag. multipart_suggestion (
113- "set this indexing to be the `Some` variant" ,
114- index_accesses,
115- Applicability :: Unspecified ,
116- ) ;
98+ suggestions. extend ( r. extra_exprs_copy . iter ( ) . map ( |x| ( x. span , name. to_string ( ) ) ) ) ;
11799 }
118100 } else if r. extra_exprs_borrow . is_empty ( ) {
119- let mut index_accesses = vec ! [ (
120- if_expr. cond. span,
121- format!( "let Some(&element) = {}.first()" , snippet( cx, receiver. span, ".." ) ) ,
122- ) ] ;
123- index_accesses. extend ( r. extra_exprs_copy . iter ( ) . map ( |x| ( x. span , "element" . to_owned ( ) ) ) ) ;
124-
125- diag. multipart_suggestion (
126- "consider using if..let syntax" ,
127- index_accesses,
128- Applicability :: Unspecified ,
129- ) ;
101+ suggestions. push ( ( if_expr. cond . span , format ! ( "let Some(&element) = {receiver}.first()" ) ) ) ;
102+ suggestions. extend ( r. extra_exprs_copy . iter ( ) . map ( |x| ( x. span , "element" . to_owned ( ) ) ) ) ;
130103 } else {
131- let mut index_accesses = vec ! [ (
132- if_expr. cond. span,
133- format!( "let Some(element) = {}.first()" , snippet( cx, receiver. span, ".." ) ) ,
134- ) ] ;
135- index_accesses. extend ( r. extra_exprs_borrow . iter ( ) . map ( |x| ( x. span , "element" . to_owned ( ) ) ) ) ;
136- index_accesses. extend ( r. extra_exprs_copy . iter ( ) . map ( |x| ( x. span , "element" . to_owned ( ) ) ) ) ;
137-
138- diag. multipart_suggestion (
139- "consider using if..let syntax (variable may need to be dereferenced)" ,
140- index_accesses,
141- Applicability :: Unspecified ,
104+ suggestions. push ( ( if_expr. cond . span , format ! ( "let Some(element) = {receiver}.first()" ) ) ) ;
105+ suggestions. extend (
106+ r. extra_exprs_borrow
107+ . iter ( )
108+ . chain ( r. extra_exprs_copy . iter ( ) )
109+ . map ( |x| ( x. span , "element" . to_owned ( ) ) ) ,
142110 ) ;
143111 }
112+
113+ diag. multipart_suggestion ( message, suggestions, Applicability :: MaybeIncorrect ) ;
144114 } ,
145115 ) ;
146116 }
147117 }
148118}
149119
150120struct IndexCheckResult < ' a > {
151- // the receiver for the index operation, only Some in the event the indexing is via a direct primitive
152- index_receiver : Option < & ' a Expr < ' a > > ,
121+ // span of the receiver for the index operation, only Some in the event the indexing is via a direct primitive
122+ index_receiver_span : Option < Span > ,
153123 // first local in the block - used as pattern for `Some(pat)`
154124 first_local : Option < & ' a LetStmt < ' a > > ,
155125 // any other index expressions to replace with `pat` (or "element" if no local exists)
@@ -166,27 +136,27 @@ fn process_indexing<'a>(
166136 block : & ' a Block < ' a > ,
167137 conditional_receiver_hid : HirId ,
168138) -> Option < IndexCheckResult < ' a > > {
169- let mut index_receiver : Option < & Expr < ' _ > > = None ;
139+ let mut index_receiver_span : Option < Span > = None ;
170140 let mut first_local: Option < & LetStmt < ' _ > > = None ;
171141 let mut extra_exprs_borrow: Vec < & Expr < ' _ > > = vec ! [ ] ;
172142 let mut extra_exprs_copy: Vec < & Expr < ' _ > > = vec ! [ ] ;
173143
174144 // if res == Some(()), then mutation occurred
175145 // & therefore we should not lint on this
176- let res = for_each_expr ( cx, block. stmts , |x| {
146+ let res = for_each_expr ( cx, block, |x| {
177147 let adjustments = cx. typeck_results ( ) . expr_adjustments ( x) ;
178148 if let ExprKind :: Index ( receiver, index, _) = x. kind
179149 && let ExprKind :: Lit ( lit) = index. kind
180150 && let LitKind :: Int ( val, _) = lit. node
181151 && path_to_local_id ( receiver, conditional_receiver_hid)
182152 && val. 0 == 0
183153 {
184- index_receiver = Some ( receiver) ;
154+ index_receiver_span = Some ( receiver. span ) ;
185155 if let Node :: LetStmt ( local) = cx. tcx . parent_hir_node ( x. hir_id ) {
186156 if first_local. is_none ( ) {
187157 first_local = Some ( local) ;
188158 return ControlFlow :: Continue :: < ( ) > ( ( ) ) ;
189- } ;
159+ }
190160 }
191161
192162 if let Node :: Expr ( x) = cx. tcx . parent_hir_node ( x. hir_id )
@@ -195,24 +165,24 @@ fn process_indexing<'a>(
195165 extra_exprs_borrow. push ( x) ;
196166 } else {
197167 extra_exprs_copy. push ( x) ;
198- } ;
168+ }
199169 } else if adjustments. iter ( ) . any ( |adjustment| {
200170 matches ! (
201171 adjustment. kind,
202- Adjust :: Borrow ( AutoBorrow :: Ref ( _ , AutoBorrowMutability :: Mut { .. } ) )
172+ Adjust :: Borrow ( AutoBorrow :: Ref ( AutoBorrowMutability :: Mut { .. } ) )
203173 )
204174 } ) {
205175 // do not lint on mutable auto borrows (https://github.com/rust-lang/rust-clippy/pull/12464#discussion_r1600352696)
206176 return ControlFlow :: Break ( ( ) ) ;
207177 } else if let ExprKind :: AddrOf ( _, Mutability :: Mut , _) = x. kind {
208178 return ControlFlow :: Break ( ( ) ) ;
209- } ;
179+ }
210180
211181 ControlFlow :: Continue :: < ( ) > ( ( ) )
212182 } ) ;
213183
214184 res. is_none ( ) . then_some ( IndexCheckResult {
215- index_receiver ,
185+ index_receiver_span ,
216186 first_local,
217187 extra_exprs_borrow,
218188 extra_exprs_copy,
0 commit comments