|  | 
| 1 |  | -use std::iter; | 
| 2 |  | - | 
| 3 | 1 | use either::Either; | 
|  | 2 | +use hir::PatField; | 
| 4 | 3 | use rustc_data_structures::captures::Captures; | 
| 5 | 4 | use rustc_data_structures::fx::FxIndexSet; | 
| 6 | 5 | use rustc_errors::{ | 
| @@ -28,6 +27,7 @@ use rustc_span::symbol::{kw, sym, Ident}; | 
| 28 | 27 | use rustc_span::{BytePos, Span, Symbol}; | 
| 29 | 28 | use rustc_trait_selection::infer::InferCtxtExt; | 
| 30 | 29 | use rustc_trait_selection::traits::ObligationCtxt; | 
|  | 30 | +use std::iter; | 
| 31 | 31 | 
 | 
| 32 | 32 | use crate::borrow_set::TwoPhaseActivation; | 
| 33 | 33 | use crate::borrowck_errors; | 
| @@ -992,6 +992,11 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { | 
| 992 | 992 |                     issued_borrow.borrowed_place, | 
| 993 | 993 |                     &issued_spans, | 
| 994 | 994 |                 ); | 
|  | 995 | +                self.explain_iterator_advancement_in_for_loop_if_applicable( | 
|  | 996 | +                    &mut err, | 
|  | 997 | +                    span, | 
|  | 998 | +                    &issued_spans, | 
|  | 999 | +                ); | 
| 995 | 1000 |                 err | 
| 996 | 1001 |             } | 
| 997 | 1002 | 
 | 
| @@ -1279,6 +1284,73 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { | 
| 1279 | 1284 |         } | 
| 1280 | 1285 |     } | 
| 1281 | 1286 | 
 | 
|  | 1287 | +    /// Suggest using `while let` for call `next` on an iterator in a for loop. | 
|  | 1288 | +    /// | 
|  | 1289 | +    /// For example: | 
|  | 1290 | +    /// ```ignore (illustrative) | 
|  | 1291 | +    /// | 
|  | 1292 | +    /// for x in iter { | 
|  | 1293 | +    ///     ... | 
|  | 1294 | +    ///     iter.next() | 
|  | 1295 | +    /// } | 
|  | 1296 | +    /// ``` | 
|  | 1297 | +    pub(crate) fn explain_iterator_advancement_in_for_loop_if_applicable( | 
|  | 1298 | +        &self, | 
|  | 1299 | +        err: &mut Diagnostic, | 
|  | 1300 | +        span: Span, | 
|  | 1301 | +        issued_spans: &UseSpans<'tcx>, | 
|  | 1302 | +    ) { | 
|  | 1303 | +        let issue_span = issued_spans.args_or_use(); | 
|  | 1304 | +        let tcx = self.infcx.tcx; | 
|  | 1305 | +        let hir = tcx.hir(); | 
|  | 1306 | + | 
|  | 1307 | +        let Some(body_id) = hir.get(self.mir_hir_id()).body_id() else { return }; | 
|  | 1308 | +        let typeck_results = tcx.typeck(self.mir_def_id()); | 
|  | 1309 | + | 
|  | 1310 | +        struct ExprFinder<'hir> { | 
|  | 1311 | +            issue_span: Span, | 
|  | 1312 | +            expr_span: Span, | 
|  | 1313 | +            body_expr: Option<&'hir hir::Expr<'hir>>, | 
|  | 1314 | +            loop_bind: Option<Symbol>, | 
|  | 1315 | +        } | 
|  | 1316 | +        impl<'hir> Visitor<'hir> for ExprFinder<'hir> { | 
|  | 1317 | +            fn visit_expr(&mut self, ex: &'hir hir::Expr<'hir>) { | 
|  | 1318 | +                if let hir::ExprKind::Loop(hir::Block{ stmts: [stmt, ..], ..}, _, hir::LoopSource::ForLoop, _) = ex.kind && | 
|  | 1319 | +                    let hir::StmtKind::Expr(hir::Expr{ kind: hir::ExprKind::Match(call, [_, bind, ..], _), ..}) = stmt.kind && | 
|  | 1320 | +                    let hir::ExprKind::Call(path, _args) = call.kind && | 
|  | 1321 | +                    let hir::ExprKind::Path(hir::QPath::LangItem(LangItem::IteratorNext, _, _, )) = path.kind && | 
|  | 1322 | +                    let hir::PatKind::Struct(path, [field, ..], _) = bind.pat.kind && | 
|  | 1323 | +                    let hir::QPath::LangItem(LangItem::OptionSome, _, _) = path && | 
|  | 1324 | +                    let PatField { pat: hir::Pat{ kind: hir::PatKind::Binding(_, _, ident, ..), .. }, ..} = field && | 
|  | 1325 | +                    self.issue_span.source_equal(call.span) { | 
|  | 1326 | +                        self.loop_bind = Some(ident.name); | 
|  | 1327 | +                    } | 
|  | 1328 | + | 
|  | 1329 | +                if let hir::ExprKind::MethodCall(body_call, _recv, ..) = ex.kind && | 
|  | 1330 | +                    body_call.ident.name == sym::next && ex.span.source_equal(self.expr_span) { | 
|  | 1331 | +                        self.body_expr = Some(ex); | 
|  | 1332 | +                } | 
|  | 1333 | + | 
|  | 1334 | +                hir::intravisit::walk_expr(self, ex); | 
|  | 1335 | +            } | 
|  | 1336 | +        } | 
|  | 1337 | +        let mut finder = | 
|  | 1338 | +            ExprFinder { expr_span: span, issue_span, loop_bind: None, body_expr: None }; | 
|  | 1339 | +        finder.visit_expr(hir.body(body_id).value); | 
|  | 1340 | + | 
|  | 1341 | +        if let Some(loop_bind) = finder.loop_bind && | 
|  | 1342 | +            let Some(body_expr) = finder.body_expr && | 
|  | 1343 | +                let Some(def_id) = typeck_results.type_dependent_def_id(body_expr.hir_id) && | 
|  | 1344 | +                let Some(trait_did) = tcx.trait_of_item(def_id) && | 
|  | 1345 | +                tcx.is_diagnostic_item(sym::Iterator, trait_did) { | 
|  | 1346 | +                    err.note(format!( | 
|  | 1347 | +                        "a for loop advances the iterator for you, the result is stored in `{}`.", | 
|  | 1348 | +                        loop_bind | 
|  | 1349 | +                    )); | 
|  | 1350 | +                    err.help("if you want to call `next` on a iterator within the loop, consider using `while let`."); | 
|  | 1351 | +        } | 
|  | 1352 | +    } | 
|  | 1353 | + | 
| 1282 | 1354 |     /// Suggest using closure argument instead of capture. | 
| 1283 | 1355 |     /// | 
| 1284 | 1356 |     /// For example: | 
|  | 
0 commit comments