]> git.lizzy.rs Git - rust.git/commitdiff
mem_replace: match on path.
authorJay Kickliter <jay@kickliter.com>
Tue, 18 Sep 2018 23:54:01 +0000 (16:54 -0700)
committerJay Kickliter <jay@kickliter.com>
Wed, 19 Sep 2018 21:41:22 +0000 (14:41 -0700)
clippy_lints/src/mem_replace.rs
tests/ui/mem_replace.rs
tests/ui/mem_replace.stderr

index f516fc5085c94e7df1ffb6d6557fca0ec515986f..22460ccb5ea5669ff7900199616c21ba99c2fe97 100644 (file)
@@ -1,7 +1,7 @@
 use crate::rustc::hir::{Expr, ExprKind, MutMutable, QPath};
 use crate::rustc::lint::{LateContext, LateLintPass, LintArray, LintPass};
 use crate::rustc::{declare_tool_lint, lint_array};
-use crate::utils::{match_def_path, match_qpath, match_type, opt_def_id, paths, snippet, span_lint_and_sugg};
+use crate::utils::{match_def_path, match_qpath, opt_def_id, paths, snippet, span_lint_and_sugg};
 use if_chain::if_chain;
 
 /// **What it does:** Checks for `mem::replace()` on an `Option` with
@@ -40,25 +40,42 @@ fn get_lints(&self) -> LintArray {
 impl<'a, 'tcx> LateLintPass<'a, 'tcx> for MemReplace {
     fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr) {
         if_chain! {
+            // Check that `expr` is a call to `mem::replace()`
             if let ExprKind::Call(ref func, ref func_args) = expr.node;
             if func_args.len() == 2;
             if let ExprKind::Path(ref func_qpath) = func.node;
             if let Some(def_id) = opt_def_id(cx.tables.qpath_def(func_qpath, func.hir_id));
             if match_def_path(cx.tcx, def_id, &paths::MEM_REPLACE);
-            if let ExprKind::AddrOf(MutMutable, ref replaced) = func_args[0].node;
-            if match_type(cx, cx.tables.expr_ty(replaced), &paths::OPTION);
+
+            // Check that second argument is `Option::None`
             if let ExprKind::Path(ref replacement_qpath) = func_args[1].node;
             if match_qpath(replacement_qpath, &paths::OPTION_NONE);
-            if let ExprKind::Path(QPath::Resolved(None, ref replaced_path)) = replaced.node;
+
             then {
-                let sugg = format!("{}.take()", snippet(cx, replaced_path.span, ""));
+                // Since this is a late pass (already type-checked),
+                // and we already know that the second argument is an
+                // `Option`, we do not need to check if the first
+                // argument's type. All that's left is to get
+                // replacee's path.
+                let replaced_path = match func_args[0].node {
+                    ExprKind::AddrOf(MutMutable, ref replaced) => {
+                        if let ExprKind::Path(QPath::Resolved(None, ref replaced_path)) = replaced.node {
+                            replaced_path
+                        } else {
+                            return
+                        }
+                    },
+                    ExprKind::Path(QPath::Resolved(None, ref replaced_path)) => replaced_path,
+                    _ => return,
+                };
+
                 span_lint_and_sugg(
                     cx,
                     MEM_REPLACE_OPTION_WITH_NONE,
                     expr.span,
                     "replacing an `Option` with `None`",
                     "consider `Option::take()` instead",
-                    sugg
+                    format!("{}.take()", snippet(cx, replaced_path.span, ""))
                 );
             }
         }
index 14f586e71bfda971467ecd1e25595ae603f73e7c..62df42ef2d290786d45a905cc64e49c3f20cdeb5 100644 (file)
@@ -6,4 +6,6 @@
 fn main() {
     let mut an_option = Some(1);
     let _ = mem::replace(&mut an_option, None);
+    let an_option = &mut Some(1);
+    let _ = mem::replace(an_option, None);
 }
index 1ce9fc38093e92ba4f18cad74ff4b11a59235875..8385fa3cb3c804438e3322cd29b09d762f3db21a 100644 (file)
@@ -6,5 +6,11 @@ error: replacing an `Option` with `None`
   |
   = note: `-D clippy::mem-replace-option-with-none` implied by `-D warnings`
 
-error: aborting due to previous error
+error: replacing an `Option` with `None`
+  --> $DIR/mem_replace.rs:10:13
+   |
+10 |     let _ = mem::replace(an_option, None);
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider `Option::take()` instead: `an_option.take()`
+
+error: aborting due to 2 previous errors