]> git.lizzy.rs Git - rust.git/blobdiff - src/misc.rs
Merge pull request #127 from birkenfeld/better-helptext-if-let
[rust.git] / src / misc.rs
index 24f0d8afeb6526abe0bd7e54dac028e4ebdae254..444062a5919fc9f35eb5459e5de42c9b2be199a3 100644 (file)
@@ -41,7 +41,7 @@ fn check_expr(&mut self, cx: &Context, expr: &Expr) {
                     }
                     // In some cases, an exhaustive match is preferred to catch situations when
                     // an enum is extended. So we only consider cases where a `_` wildcard is used
-                    if arms[1].pats[0].node == PatWild(PatWildSingle) && 
+                    if arms[1].pats[0].node == PatWild(PatWildSingle) &&
                             arms[0].pats.len() == 1 {
                         let body_code = snippet(cx, arms[0].body.span, "..");
                         let suggestion = if let ExprBlock(_) = arms[0].body.node {
@@ -87,7 +87,7 @@ fn check_expr(&mut self, cx: &Context, expr: &ast::Expr) {
         }
 
         fn is_str(cx: &Context, expr: &ast::Expr) -> bool {
-            match walk_ty(cx.tcx.expr_ty(expr)).sty { 
+            match walk_ty(cx.tcx.expr_ty(expr)).sty {
                 ty::TyStr => true,
                 _ => false
             }
@@ -109,7 +109,7 @@ fn get_lints(&self) -> LintArray {
     fn check_fn(&mut self, cx: &Context, _: FnKind, decl: &FnDecl, _: &Block, _: Span, _: NodeId) {
         for ref arg in decl.inputs.iter() {
             if let PatIdent(BindByRef(_), _, _) = arg.pat.node {
-                span_lint(cx, 
+                span_lint(cx,
                     TOPLEVEL_REF_ARG,
                     arg.pat.span,
                     "`ref` directly on a function argument is ignored. Have you considered using a reference type instead?"
@@ -128,7 +128,7 @@ impl LintPass for CmpNan {
     fn get_lints(&self) -> LintArray {
         lint_array!(CMP_NAN)
     }
-    
+
     fn check_expr(&mut self, cx: &Context, expr: &Expr) {
         if let ExprBinary(ref cmp, ref left, ref right) = expr.node {
             if is_comparison_binop(cmp.node) {
@@ -144,15 +144,15 @@ fn check_expr(&mut self, cx: &Context, expr: &Expr) {
 }
 
 fn check_nan(cx: &Context, path: &Path, span: Span) {
-       path.segments.last().map(|seg| if seg.identifier.name == "NAN" {
-               span_lint(cx, CMP_NAN, span, 
-                       "Doomed comparison with NAN, use std::{f32,f64}::is_nan instead");
-       });
+    path.segments.last().map(|seg| if seg.identifier.name == "NAN" {
+        span_lint(cx, CMP_NAN, span,
+                  "Doomed comparison with NAN, use std::{f32,f64}::is_nan instead");
+    });
 }
 
 declare_lint!(pub FLOAT_CMP, Warn,
               "Warn on ==/!= comparison of floaty values");
-              
+
 #[derive(Copy,Clone)]
 pub struct FloatCmp;
 
@@ -160,14 +160,14 @@ impl LintPass for FloatCmp {
     fn get_lints(&self) -> LintArray {
         lint_array!(FLOAT_CMP)
     }
-    
+
     fn check_expr(&mut self, cx: &Context, expr: &Expr) {
         if let ExprBinary(ref cmp, ref left, ref right) = expr.node {
             let op = cmp.node;
             if (op == BiEq || op == BiNe) && (is_float(cx, left) || is_float(cx, right)) {
                 span_lint(cx, FLOAT_CMP, expr.span, &format!(
                     "{}-Comparison of f32 or f64 detected. You may want to change this to 'abs({} - {}) < epsilon' for some suitable value of epsilon",
-                    binop_to_string(op), snippet(cx, left.span, ".."), 
+                    binop_to_string(op), snippet(cx, left.span, ".."),
                     snippet(cx, right.span, "..")));
             }
         }
@@ -175,16 +175,16 @@ fn check_expr(&mut self, cx: &Context, expr: &Expr) {
 }
 
 fn is_float(cx: &Context, expr: &Expr) -> bool {
-    if let ty::TyFloat(_) = walk_ty(cx.tcx.expr_ty(expr)).sty { 
+    if let ty::TyFloat(_) = walk_ty(cx.tcx.expr_ty(expr)).sty {
         true
-    } else { 
-        false 
+    } else {
+        false
     }
 }
 
 declare_lint!(pub PRECEDENCE, Warn,
               "Warn on mixing bit ops with integer arithmetic without parenthesis");
-              
+
 #[derive(Copy,Clone)]
 pub struct Precedence;
 
@@ -192,11 +192,11 @@ impl LintPass for Precedence {
     fn get_lints(&self) -> LintArray {
         lint_array!(PRECEDENCE)
     }
-    
+
     fn check_expr(&mut self, cx: &Context, expr: &Expr) {
         if let ExprBinary(Spanned { node: op, ..}, ref left, ref right) = expr.node {
             if is_bit_op(op) && (is_arith_expr(left) || is_arith_expr(right)) {
-                span_lint(cx, PRECEDENCE, expr.span, 
+                span_lint(cx, PRECEDENCE, expr.span,
                     "Operator precedence can trip the unwary. Consider adding parenthesis to the subexpression.");
             }
         }
@@ -226,7 +226,7 @@ fn is_arith_op(op : BinOp_) -> bool {
 
 declare_lint!(pub CMP_OWNED, Warn,
               "Warn on creating an owned string just for comparison");
-              
+
 #[derive(Copy,Clone)]
 pub struct CmpOwned;
 
@@ -234,7 +234,7 @@ impl LintPass for CmpOwned {
     fn get_lints(&self) -> LintArray {
         lint_array!(CMP_OWNED)
     }
-    
+
     fn check_expr(&mut self, cx: &Context, expr: &Expr) {
         if let ExprBinary(ref cmp, ref left, ref right) = expr.node {
             if is_comparison_binop(cmp.node) {
@@ -246,33 +246,132 @@ fn check_expr(&mut self, cx: &Context, expr: &Expr) {
 }
 
 fn check_to_owned(cx: &Context, expr: &Expr, other_span: Span) {
-       match &expr.node {
-               &ExprMethodCall(Spanned{node: ref ident, ..}, _, ref args) => {
-                       let name = ident.name;
-                       if name == "to_string" || 
-                          name == "to_owned" && is_str_arg(cx, args) {
-                               span_lint(cx, CMP_OWNED, expr.span, &format!(
-                                       "this creates an owned instance just for comparison. \
-                                       Consider using {}.as_slice() to compare without allocation",
-                                       snippet(cx, other_span, "..")))
-                       }
-               },
-               &ExprCall(ref path, _) => {
-                       if let &ExprPath(None, ref path) = &path.node {
-                               if match_path(path, &["String", "from_str"]) ||
-                                               match_path(path, &["String", "from"]) {
-                                       span_lint(cx, CMP_OWNED, expr.span, &format!(
-                                       "this creates an owned instance just for comparison. \
-                                       Consider using {}.as_slice() to compare without allocation",
-                                       snippet(cx, other_span, "..")))
-                               }
-                       }
-               },
-               _ => ()
-       }
+    match &expr.node {
+        &ExprMethodCall(Spanned{node: ref ident, ..}, _, ref args) => {
+            let name = ident.name;
+            if name == "to_string" ||
+                name == "to_owned" && is_str_arg(cx, args) {
+                    span_lint(cx, CMP_OWNED, expr.span, &format!(
+                        "this creates an owned instance just for comparison. \
+                         Consider using {}.as_slice() to compare without allocation",
+                        snippet(cx, other_span, "..")))
+                }
+        },
+        &ExprCall(ref path, _) => {
+            if let &ExprPath(None, ref path) = &path.node {
+                if match_path(path, &["String", "from_str"]) ||
+                    match_path(path, &["String", "from"]) {
+                        span_lint(cx, CMP_OWNED, expr.span, &format!(
+                            "this creates an owned instance just for comparison. \
+                             Consider using {}.as_slice() to compare without allocation",
+                            snippet(cx, other_span, "..")))
+                    }
+            }
+        },
+        _ => ()
+    }
 }
 
 fn is_str_arg(cx: &Context, args: &[P<Expr>]) -> bool {
-    args.len() == 1 && if let ty::TyStr = 
+    args.len() == 1 && if let ty::TyStr =
         walk_ty(cx.tcx.expr_ty(&*args[0])).sty { true } else { false }
 }
+
+declare_lint!(pub NEEDLESS_RETURN, Warn,
+              "Warn on using a return statement where an expression would be enough");
+
+#[derive(Copy,Clone)]
+pub struct NeedlessReturn;
+
+impl NeedlessReturn {
+    // Check the final stmt or expr in a block for unnecessary return.
+    fn check_block_return(&mut self, cx: &Context, block: &Block) {
+        if let Some(ref expr) = block.expr {
+            self.check_final_expr(cx, expr);
+        } else if let Some(stmt) = block.stmts.last() {
+            if let StmtSemi(ref expr, _) = stmt.node {
+                if let ExprRet(Some(ref inner)) = expr.node {
+                    self.emit_lint(cx, (expr.span, inner.span));
+                }
+            }
+        }
+    }
+
+    // Check a the final expression in a block if it's a return.
+    fn check_final_expr(&mut self, cx: &Context, expr: &Expr) {
+        match expr.node {
+            // simple return is always "bad"
+            ExprRet(Some(ref inner)) => {
+                self.emit_lint(cx, (expr.span, inner.span));
+            }
+            // a whole block? check it!
+            ExprBlock(ref block) => {
+                self.check_block_return(cx, block);
+            }
+            // an if/if let expr, check both exprs
+            // note, if without else is going to be a type checking error anyways
+            // (except for unit type functions) so we don't match it
+            ExprIf(_, ref ifblock, Some(ref elsexpr)) |
+            ExprIfLet(_, _, ref ifblock, Some(ref elsexpr)) => {
+                self.check_block_return(cx, ifblock);
+                self.check_final_expr(cx, elsexpr);
+            }
+            // a match expr, check all arms
+            ExprMatch(_, ref arms, _) => {
+                for arm in arms {
+                    self.check_final_expr(cx, &*arm.body);
+                }
+            }
+            _ => { }
+        }
+    }
+
+    fn emit_lint(&mut self, cx: &Context, spans: (Span, Span)) {
+        span_lint(cx, NEEDLESS_RETURN, spans.0, &format!(
+            "unneeded return statement. Consider using {} \
+             without the trailing semicolon",
+            snippet(cx, spans.1, "..")))
+    }
+}
+
+impl LintPass for NeedlessReturn {
+    fn get_lints(&self) -> LintArray {
+        lint_array!(NEEDLESS_RETURN)
+    }
+
+    fn check_fn(&mut self, cx: &Context, _: FnKind, _: &FnDecl,
+                block: &Block, _: Span, _: ast::NodeId) {
+        self.check_block_return(cx, block);
+    }
+}
+
+
+declare_lint!(pub MODULO_ONE, Warn, "Warn on expressions that include % 1, which is always 0");
+
+#[derive(Copy,Clone)]
+pub struct ModuloOne;
+
+impl LintPass for ModuloOne {
+    fn get_lints(&self) -> LintArray {
+        lint_array!(MODULO_ONE)
+    }
+
+    fn check_expr(&mut self, cx: &Context, expr: &Expr) {
+        if let ExprBinary(ref cmp, _, ref right) = expr.node {
+            if let &Spanned {node: BinOp_::BiRem, ..} = cmp {
+                if is_lit_one(right) {
+                    cx.span_lint(MODULO_ONE, expr.span, "Any number modulo 1 will be 0");
+                }
+            }
+        }
+    }
+}
+
+fn is_lit_one(expr: &Expr) -> bool {
+    if let ExprLit(ref spanned) = expr.node {
+        if let LitInt(1, _) = spanned.node {
+            return true;
+        }
+    }
+    false
+}