]> git.lizzy.rs Git - rust.git/blobdiff - clippy_lints/src/no_effect.rs
Merge branch 'macro-use' into HEAD
[rust.git] / clippy_lints / src / no_effect.rs
index ae3bac0045518dca45409346edf1f7b962ae5442..dc83b29854c72581821430c51bde88305768cfc3 100644 (file)
 use rustc::lint::{LateContext, LateLintPass, LintArray, LintPass};
-use rustc::hir::def::{Def, PathResolution};
-use rustc::hir::{Expr, Expr_, Stmt, StmtSemi, BlockCheckMode, UnsafeSource};
-use utils::{in_macro, span_lint, snippet_opt, span_lint_and_then};
+use rustc::{declare_lint, lint_array};
+use rustc::hir::def::Def;
+use rustc::hir::{BinOpKind, BlockCheckMode, Expr, ExprKind, Stmt, StmtKind, UnsafeSource};
+use crate::utils::{has_drop, in_macro, snippet_opt, span_lint, span_lint_and_sugg};
 use std::ops::Deref;
 
-/// **What it does:** This lint checks for statements which have no effect.
+/// **What it does:** Checks for statements which have no effect.
 ///
-/// **Why is this bad?** Similar to dead code, these statements are actually executed. However, as they have no effect, all they do is make the code less readable.
+/// **Why is this bad?** Similar to dead code, these statements are actually
+/// executed. However, as they have no effect, all they do is make the code less
+/// readable.
 ///
 /// **Known problems:** None.
 ///
-/// **Example:** `0;`
-declare_lint! {
+/// **Example:**
+/// ```rust
+/// 0;
+/// ```
+declare_clippy_lint! {
     pub NO_EFFECT,
-    Warn,
+    complexity,
     "statements with no effect"
 }
 
-/// **What it does:** This lint checks for expression statements that can be reduced to a sub-expression
+/// **What it does:** Checks for expression statements that can be reduced to a
+/// sub-expression.
 ///
-/// **Why is this bad?** Expressions by themselves often have no side-effects. Having such expressions reduces redability.
+/// **Why is this bad?** Expressions by themselves often have no side-effects.
+/// Having such expressions reduces readability.
 ///
 /// **Known problems:** None.
 ///
-/// **Example:** `compute_array()[0];`
-declare_lint! {
+/// **Example:**
+/// ```rust
+/// compute_array()[0];
+/// ```
+declare_clippy_lint! {
     pub UNNECESSARY_OPERATION,
-    Warn,
+    complexity,
     "outer expressions with no effect"
 }
 
 fn has_no_effect(cx: &LateContext, expr: &Expr) -> bool {
-    if in_macro(cx, expr.span) {
+    if in_macro(expr.span) {
         return false;
     }
     match expr.node {
-        Expr_::ExprLit(..) |
-        Expr_::ExprClosure(..) |
-        Expr_::ExprPath(..) => true,
-        Expr_::ExprIndex(ref a, ref b) |
-        Expr_::ExprBinary(_, ref a, ref b) => has_no_effect(cx, a) && has_no_effect(cx, b),
-        Expr_::ExprVec(ref v) |
-        Expr_::ExprTup(ref v) => v.iter().all(|val| has_no_effect(cx, val)),
-        Expr_::ExprRepeat(ref inner, _) |
-        Expr_::ExprCast(ref inner, _) |
-        Expr_::ExprType(ref inner, _) |
-        Expr_::ExprUnary(_, ref inner) |
-        Expr_::ExprField(ref inner, _) |
-        Expr_::ExprTupField(ref inner, _) |
-        Expr_::ExprAddrOf(_, ref inner) |
-        Expr_::ExprBox(ref inner) => has_no_effect(cx, inner),
-        Expr_::ExprStruct(_, ref fields, ref base) => {
-            fields.iter().all(|field| has_no_effect(cx, &field.expr)) &&
-            match *base {
+        ExprKind::Lit(..) | ExprKind::Closure(.., _) => true,
+        ExprKind::Path(..) => !has_drop(cx, expr),
+        ExprKind::Index(ref a, ref b) | ExprKind::Binary(_, ref a, ref b) => {
+            has_no_effect(cx, a) && has_no_effect(cx, b)
+        },
+        ExprKind::Array(ref v) | ExprKind::Tup(ref v) => v.iter().all(|val| has_no_effect(cx, val)),
+        ExprKind::Repeat(ref inner, _) |
+        ExprKind::Cast(ref inner, _) |
+        ExprKind::Type(ref inner, _) |
+        ExprKind::Unary(_, ref inner) |
+        ExprKind::Field(ref inner, _) |
+        ExprKind::AddrOf(_, ref inner) |
+        ExprKind::Box(ref inner) => has_no_effect(cx, inner),
+        ExprKind::Struct(_, ref fields, ref base) => {
+            !has_drop(cx, expr) && fields.iter().all(|field| has_no_effect(cx, &field.expr)) && match *base {
                 Some(ref base) => has_no_effect(cx, base),
                 None => true,
             }
-        }
-        Expr_::ExprCall(ref callee, ref args) => {
-            let def = cx.tcx.def_map.borrow().get(&callee.id).map(|d| d.full_def());
+        },
+        ExprKind::Call(ref callee, ref args) => if let ExprKind::Path(ref qpath) = callee.node {
+            let def = cx.tables.qpath_def(qpath, callee.hir_id);
             match def {
-                Some(Def::Struct(..)) |
-                Some(Def::Variant(..)) => args.iter().all(|arg| has_no_effect(cx, arg)),
+                Def::Struct(..) | Def::Variant(..) | Def::StructCtor(..) | Def::VariantCtor(..) => {
+                    !has_drop(cx, expr) && args.iter().all(|arg| has_no_effect(cx, arg))
+                },
                 _ => false,
             }
-        }
-        Expr_::ExprBlock(ref block) => {
-            block.stmts.is_empty() &&
-            if let Some(ref expr) = block.expr {
+        } else {
+            false
+        },
+        ExprKind::Block(ref block, _) => {
+            block.stmts.is_empty() && if let Some(ref expr) = block.expr {
                 has_no_effect(cx, expr)
             } else {
                 false
             }
-        }
+        },
         _ => false,
     }
 }
 
 #[derive(Copy, Clone)]
-pub struct NoEffectPass;
+pub struct Pass;
 
-impl LintPass for NoEffectPass {
+impl LintPass for Pass {
     fn get_lints(&self) -> LintArray {
         lint_array!(NO_EFFECT, UNNECESSARY_OPERATION)
     }
 }
 
-impl LateLintPass for NoEffectPass {
-    fn check_stmt(&mut self, cx: &LateContext, stmt: &Stmt) {
-        if let StmtSemi(ref expr, _) = stmt.node {
+impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Pass {
+    fn check_stmt(&mut self, cx: &LateContext<'a, 'tcx>, stmt: &'tcx Stmt) {
+        if let StmtKind::Semi(ref expr, _) = stmt.node {
             if has_no_effect(cx, expr) {
                 span_lint(cx, NO_EFFECT, stmt.span, "statement with no effect");
             } else if let Some(reduced) = reduce_expression(cx, expr) {
                 let mut snippet = String::new();
                 for e in reduced {
-                    if in_macro(cx, e.span) {
+                    if in_macro(e.span) {
                         return;
                     }
                     if let Some(snip) = snippet_opt(cx, e.span) {
@@ -104,9 +114,14 @@ fn check_stmt(&mut self, cx: &LateContext, stmt: &Stmt) {
                         return;
                     }
                 }
-                span_lint_and_then(cx, UNNECESSARY_OPERATION, stmt.span, "statement can be reduced", |db| {
-                    db.span_suggestion(stmt.span, "replace it with", snippet);
-                });
+                span_lint_and_sugg(
+                    cx,
+                    UNNECESSARY_OPERATION,
+                    stmt.span,
+                    "statement can be reduced",
+                    "replace it with",
+                    snippet,
+                );
             }
         }
     }
@@ -114,42 +129,61 @@ fn check_stmt(&mut self, cx: &LateContext, stmt: &Stmt) {
 
 
 fn reduce_expression<'a>(cx: &LateContext, expr: &'a Expr) -> Option<Vec<&'a Expr>> {
-    if in_macro(cx, expr.span) {
+    if in_macro(expr.span) {
         return None;
     }
     match expr.node {
-        Expr_::ExprIndex(ref a, ref b) |
-        Expr_::ExprBinary(_, ref a, ref b) => Some(vec![&**a, &**b]),
-        Expr_::ExprVec(ref v) |
-        Expr_::ExprTup(ref v) => Some(v.iter().map(Deref::deref).collect()),
-        Expr_::ExprRepeat(ref inner, _) |
-        Expr_::ExprCast(ref inner, _) |
-        Expr_::ExprType(ref inner, _) |
-        Expr_::ExprUnary(_, ref inner) |
-        Expr_::ExprField(ref inner, _) |
-        Expr_::ExprTupField(ref inner, _) |
-        Expr_::ExprAddrOf(_, ref inner) |
-        Expr_::ExprBox(ref inner) => reduce_expression(cx, inner).or_else(|| Some(vec![inner])),
-        Expr_::ExprStruct(_, ref fields, ref base) => Some(fields.iter().map(|f| &f.expr).chain(base).map(Deref::deref).collect()),
-        Expr_::ExprCall(ref callee, ref args) => {
-            match cx.tcx.def_map.borrow().get(&callee.id).map(PathResolution::full_def) {
-                Some(Def::Struct(..)) |
-                Some(Def::Variant(..)) => Some(args.iter().map(Deref::deref).collect()),
+        ExprKind::Index(ref a, ref b) => Some(vec![&**a, &**b]),
+        ExprKind::Binary(ref binop, ref a, ref b) if binop.node != BinOpKind::And && binop.node != BinOpKind::Or => {
+            Some(vec![&**a, &**b])
+        },
+        ExprKind::Array(ref v) | ExprKind::Tup(ref v) => Some(v.iter().collect()),
+        ExprKind::Repeat(ref inner, _) |
+        ExprKind::Cast(ref inner, _) |
+        ExprKind::Type(ref inner, _) |
+        ExprKind::Unary(_, ref inner) |
+        ExprKind::Field(ref inner, _) |
+        ExprKind::AddrOf(_, ref inner) |
+        ExprKind::Box(ref inner) => reduce_expression(cx, inner).or_else(|| Some(vec![inner])),
+        ExprKind::Struct(_, ref fields, ref base) => if has_drop(cx, expr) {
+            None
+        } else {
+            Some(
+                fields
+                    .iter()
+                    .map(|f| &f.expr)
+                    .chain(base)
+                    .map(Deref::deref)
+                    .collect(),
+            )
+        },
+        ExprKind::Call(ref callee, ref args) => if let ExprKind::Path(ref qpath) = callee.node {
+            let def = cx.tables.qpath_def(qpath, callee.hir_id);
+            match def {
+                Def::Struct(..) | Def::Variant(..) | Def::StructCtor(..) | Def::VariantCtor(..)
+                    if !has_drop(cx, expr) =>
+                {
+                    Some(args.iter().collect())
+                },
                 _ => None,
             }
-        }
-        Expr_::ExprBlock(ref block) => {
+        } else {
+            None
+        },
+        ExprKind::Block(ref block, _) => {
             if block.stmts.is_empty() {
-                block.expr.as_ref().and_then(|e| match block.rules {
-                    BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided) => None,
-                    BlockCheckMode::DefaultBlock => Some(vec![&**e]),
-                    // in case of compiler-inserted signaling blocks
-                    _ => reduce_expression(cx, e),
+                block.expr.as_ref().and_then(|e| {
+                    match block.rules {
+                        BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided) => None,
+                        BlockCheckMode::DefaultBlock => Some(vec![&**e]),
+                        // in case of compiler-inserted signaling blocks
+                        _ => reduce_expression(cx, e),
+                    }
                 })
             } else {
                 None
             }
-        }
+        },
         _ => None,
     }
 }