}
// 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 {
}
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
}
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?"
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) {
}
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;
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, "..")));
}
}
}
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;
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.");
}
}
declare_lint!(pub CMP_OWNED, Warn,
"Warn on creating an owned string just for comparison");
-
+
#[derive(Copy,Clone)]
pub struct 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) {
}
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
+}