use super::WHILE_LET_ON_ITERATOR;
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::source::snippet_with_applicability;
-use clippy_utils::{get_enclosing_loop, is_refutable, is_trait_method, match_def_path, paths, visitors::is_res_used};
+use clippy_utils::{
+ get_enclosing_loop_or_closure, is_refutable, is_trait_method, match_def_path, paths, visitors::is_res_used,
+};
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir::intravisit::{walk_expr, ErasedMap, NestedVisitorMap, Visitor};
-use rustc_hir::{def::Res, Expr, ExprKind, HirId, Local, MatchSource, Node, PatKind, QPath, UnOp};
+use rustc_hir::{def::Res, Expr, ExprKind, HirId, Local, MatchSource, Mutability, Node, PatKind, QPath, UnOp};
use rustc_lint::LateContext;
use rustc_span::{symbol::sym, Span, Symbol};
// borrowed mutably. TODO: If the struct can be partially moved from and the struct isn't used
// afterwards a mutable borrow of a field isn't necessary.
let ref_mut = if !iter_expr.fields.is_empty() || needs_mutable_borrow(cx, &iter_expr, loop_expr) {
- "&mut "
+ if cx.typeck_results().node_type(iter_expr.hir_id).ref_mutability() == Some(Mutability::Mut) {
+ // Reborrow for mutable references. It may not be possible to get a mutable reference here.
+ "&mut *"
+ } else {
+ "&mut "
+ }
} else {
""
};
struct IterExpr {
/// The span of the whole expression, not just the path and fields stored here.
span: Span,
+ /// The HIR id of the whole expression, not just the path and fields stored here.
+ hir_id: HirId,
/// The fields used, in order of child to parent.
fields: Vec<Symbol>,
/// The path being used.
/// the expression might have side effects.
fn try_parse_iter_expr(cx: &LateContext<'_>, mut e: &Expr<'_>) -> Option<IterExpr> {
let span = e.span;
+ let hir_id = e.hir_id;
let mut fields = Vec::new();
loop {
match e.kind {
ExprKind::Path(ref path) => {
break Some(IterExpr {
span,
+ hir_id,
fields,
path: cx.qpath_res(path, e.hir_id),
});
}
}
- if let Some(e) = get_enclosing_loop(cx.tcx, loop_expr) {
- // The iterator expression will be used on the next iteration unless it is declared within the outer
- // loop.
+ if let Some(e) = get_enclosing_loop_or_closure(cx.tcx, loop_expr) {
+ // The iterator expression will be used on the next iteration (for loops), or on the next call (for
+ // closures) unless it is declared within the enclosing expression. TODO: Check for closures
+ // used where an `FnOnce` type is expected.
let local_id = match iter_expr.path {
Res::Local(id) => id,
_ => return true,