about summary refs log tree commit diff
path: root/clippy_lints
diff options
context:
space:
mode:
authorJason Newcomb <jsnewcomb@pm.me>2021-08-04 13:48:45 -0400
committerJason Newcomb <jsnewcomb@pm.me>2021-08-14 19:49:54 -0400
commit251dd30d77c98cbebd1c68840fce029affe9b6a8 (patch)
treee1efdac07311d834631939ce5643ed725d88d6ea /clippy_lints
parent4838c78ba4ef784379ae6ec5617479de2a32d3f6 (diff)
downloadrust-251dd30d77c98cbebd1c68840fce029affe9b6a8.tar.gz
rust-251dd30d77c98cbebd1c68840fce029affe9b6a8.zip
Improve `manual_map`
In some cases check if a borrow made in the scrutinee expression would prevent creating the closure used by `map`
Diffstat (limited to 'clippy_lints')
-rw-r--r--clippy_lints/src/manual_map.rs36
1 files changed, 30 insertions, 6 deletions
diff --git a/clippy_lints/src/manual_map.rs b/clippy_lints/src/manual_map.rs
index 7dec1595e0d..3ea88f52a1c 100644
--- a/clippy_lints/src/manual_map.rs
+++ b/clippy_lints/src/manual_map.rs
@@ -4,12 +4,14 @@ use clippy_utils::source::{snippet_with_applicability, snippet_with_context};
 use clippy_utils::ty::{is_type_diagnostic_item, peel_mid_ty_refs_is_mutable};
 use clippy_utils::{
     can_move_expr_to_closure, in_constant, is_else_clause, is_lang_ctor, is_lint_allowed, path_to_local_id,
-    peel_hir_expr_refs,
+    peel_hir_expr_refs, peel_hir_expr_while, CaptureKind,
 };
 use rustc_ast::util::parser::PREC_POSTFIX;
 use rustc_errors::Applicability;
 use rustc_hir::LangItem::{OptionNone, OptionSome};
-use rustc_hir::{Arm, BindingAnnotation, Block, Expr, ExprKind, HirId, MatchSource, Mutability, Pat, PatKind};
+use rustc_hir::{
+    def::Res, Arm, BindingAnnotation, Block, Expr, ExprKind, HirId, MatchSource, Mutability, Pat, PatKind, Path, QPath,
+};
 use rustc_lint::{LateContext, LateLintPass, LintContext};
 use rustc_middle::lint::in_external_macro;
 use rustc_session::{declare_lint_pass, declare_tool_lint};
@@ -111,10 +113,6 @@ impl LateLintPass<'_> for ManualMap {
                 return;
             }
 
-            if !can_move_expr_to_closure(cx, some_expr) {
-                return;
-            }
-
             // Determine which binding mode to use.
             let explicit_ref = some_pat.contains_explicit_ref_binding();
             let binding_ref = explicit_ref.or_else(|| (ty_ref_count != pat_ref_count).then(|| ty_mutability));
@@ -125,6 +123,32 @@ impl LateLintPass<'_> for ManualMap {
                 None => "",
             };
 
+            match can_move_expr_to_closure(cx, some_expr) {
+                Some(captures) => {
+                    // Check if captures the closure will need conflict with borrows made in the scrutinee.
+                    // TODO: check all the references made in the scrutinee expression. This will require interacting
+                    // with the borrow checker. Currently only `<local>[.<field>]*` is checked for.
+                    if let Some(binding_ref_mutability) = binding_ref {
+                        let e = peel_hir_expr_while(scrutinee, |e| match e.kind {
+                            ExprKind::Field(e, _) | ExprKind::AddrOf(_, _, e) => Some(e),
+                            _ => None,
+                        });
+                        if let ExprKind::Path(QPath::Resolved(None, Path { res: Res::Local(l), .. })) = e.kind {
+                            match captures.get(l) {
+                                Some(CaptureKind::Value | CaptureKind::Ref(Mutability::Mut)) => return,
+                                Some(CaptureKind::Ref(Mutability::Not))
+                                    if binding_ref_mutability == Mutability::Mut =>
+                                {
+                                    return;
+                                }
+                                Some(CaptureKind::Ref(Mutability::Not)) | None => (),
+                            }
+                        }
+                    }
+                },
+                None => return,
+            };
+
             let mut app = Applicability::MachineApplicable;
 
             // Remove address-of expressions from the scrutinee. Either `as_ref` will be called, or