@@ -2,7 +2,7 @@ use super::NEEDLESS_RANGE_LOOP;
22use clippy_utils:: diagnostics:: span_lint_and_then;
33use clippy_utils:: source:: snippet;
44use clippy_utils:: ty:: has_iter_method;
5- use clippy_utils:: visitors:: is_local_used;
5+ use clippy_utils:: visitors:: { is_const_evaluatable , is_local_used} ;
66use clippy_utils:: { SpanlessEq , contains_name, higher, is_integer_literal, peel_hir_expr_while, sugg} ;
77use rustc_ast:: ast;
88use rustc_data_structures:: fx:: { FxHashMap , FxHashSet , FxIndexMap } ;
@@ -43,6 +43,7 @@ pub(super) fn check<'tcx>(
4343 unnamed_indexed_indirectly : false ,
4444 indexed_directly : FxIndexMap :: default ( ) ,
4545 unnamed_indexed_directly : false ,
46+ has_dynamic_indexed_container : false ,
4647 referenced : FxHashSet :: default ( ) ,
4748 nonindex : false ,
4849 prefer_mutable : false ,
@@ -53,6 +54,7 @@ pub(super) fn check<'tcx>(
5354 if visitor. indexed_indirectly . is_empty ( )
5455 && !visitor. unnamed_indexed_indirectly
5556 && !visitor. unnamed_indexed_directly
57+ && !visitor. has_dynamic_indexed_container
5658 && visitor. indexed_directly . len ( ) == 1
5759 {
5860 let ( indexed, ( indexed_extent, indexed_ty) ) = visitor
@@ -239,6 +241,9 @@ struct VarVisitor<'a, 'tcx> {
239241 indexed_directly : FxIndexMap < Symbol , ( Option < region:: Scope > , Ty < ' tcx > ) > ,
240242 /// directly indexed literals, like `[1, 2, 3][i]`
241243 unnamed_indexed_directly : bool ,
244+ /// Whether the loop variable indexes a container selected by a non-constant expression.
245+ /// For example, in `a[sum % 5][i]`, iterating over `a` would change the program's semantics.
246+ has_dynamic_indexed_container : bool ,
242247 /// Any names that are used outside an index operation.
243248 /// Used to detect things like `&mut vec` used together with `vec[i]`
244249 referenced : FxHashSet < Symbol > ,
@@ -256,24 +261,39 @@ impl<'tcx> VarVisitor<'_, 'tcx> {
256261 // It is `true` if all indices are direct
257262 let mut index_used_directly = true ;
258263
264+ // Track whether the loop variable has been found while walking from the outermost index
265+ // towards the base expression. Any later non-constant index selects the container indexed
266+ // by the loop variable, so it cannot be replaced by an iterator over the base expression.
267+ let mut found_loop_index = false ;
268+ let mut indexed_container_is_stable = true ;
269+
259270 // Handle initial index
260271 if is_local_used ( self . cx , idx, self . var ) {
261272 used_cnt += 1 ;
262273 index_used_directly &= matches ! ( idx. kind, ExprKind :: Path ( _) ) ;
274+ found_loop_index = true ;
263275 }
264276 // Handle nested indices
265277 let seqexpr = peel_hir_expr_while ( seqexpr, |e| {
266278 if let ExprKind :: Index ( e, idx, _) = e. kind {
267279 if is_local_used ( self . cx , idx, self . var ) {
268280 used_cnt += 1 ;
269281 index_used_directly &= matches ! ( idx. kind, ExprKind :: Path ( _) ) ;
282+ found_loop_index = true ;
283+ } else if found_loop_index && !is_const_evaluatable ( self . cx . tcx , self . cx . typeck_results ( ) , idx) {
284+ indexed_container_is_stable = false ;
270285 }
271286 Some ( e)
272287 } else {
273288 None
274289 }
275290 } ) ;
276291
292+ if !indexed_container_is_stable {
293+ self . has_dynamic_indexed_container = true ;
294+ return false ;
295+ }
296+
277297 match used_cnt {
278298 0 => return true ,
279299 n if n > 1 => self . nonindex = true , // Optimize code like `a[i][i]`
0 commit comments