|
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