4 use syntax::ast_util::{is_comparison_binop, binop_to_string};
5 use syntax::visit::{FnKind};
6 use rustc::lint::{Context, LintPass, LintArray, Lint, Level};
8 use syntax::codemap::{Span, Spanned};
10 use utils::{match_path, snippet, span_lint, span_help_and_lint, walk_ptrs_ty};
12 /// Handles uncategorized lints
13 /// Currently handles linting of if-let-able matches
14 #[allow(missing_copy_implementations)]
18 declare_lint!(pub SINGLE_MATCH, Warn,
19 "Warn on usage of matches with a single nontrivial arm");
21 impl LintPass for MiscPass {
22 fn get_lints(&self) -> LintArray {
23 lint_array!(SINGLE_MATCH)
26 fn check_expr(&mut self, cx: &Context, expr: &Expr) {
27 if let ExprMatch(ref ex, ref arms, ast::MatchSource::Normal) = expr.node {
29 if arms[0].guard.is_none() && arms[1].pats.len() == 1 {
30 match arms[1].body.node {
31 ExprTup(ref v) if v.is_empty() && arms[1].guard.is_none() => (),
32 ExprBlock(ref b) if b.stmts.is_empty() && arms[1].guard.is_none() => (),
35 // In some cases, an exhaustive match is preferred to catch situations when
36 // an enum is extended. So we only consider cases where a `_` wildcard is used
37 if arms[1].pats[0].node == PatWild(PatWildSingle) &&
38 arms[0].pats.len() == 1 {
39 let body_code = snippet(cx, arms[0].body.span, "..");
40 let suggestion = if let ExprBlock(_) = arms[0].body.node {
41 body_code.into_owned()
43 format!("{{ {} }}", body_code)
45 span_help_and_lint(cx, SINGLE_MATCH, expr.span,
46 "you seem to be trying to use match for \
47 destructuring a single pattern. Did you mean to \
49 &*format!("try\nif let {} = {} {}",
50 snippet(cx, arms[0].pats[0].span, ".."),
51 snippet(cx, ex.span, ".."),
62 declare_lint!(pub TOPLEVEL_REF_ARG, Warn, "Warn about pattern matches with top-level `ref` bindings");
64 #[allow(missing_copy_implementations)]
65 pub struct TopLevelRefPass;
67 impl LintPass for TopLevelRefPass {
68 fn get_lints(&self) -> LintArray {
69 lint_array!(TOPLEVEL_REF_ARG)
72 fn check_fn(&mut self, cx: &Context, _: FnKind, decl: &FnDecl, _: &Block, _: Span, _: NodeId) {
73 for ref arg in decl.inputs.iter() {
74 if let PatIdent(BindByRef(_), _, _) = arg.pat.node {
78 "`ref` directly on a function argument is ignored. Consider using a reference type instead."
85 declare_lint!(pub CMP_NAN, Deny, "Deny comparisons to std::f32::NAN or std::f64::NAN");
90 impl LintPass for CmpNan {
91 fn get_lints(&self) -> LintArray {
95 fn check_expr(&mut self, cx: &Context, expr: &Expr) {
96 if let ExprBinary(ref cmp, ref left, ref right) = expr.node {
97 if is_comparison_binop(cmp.node) {
98 if let &ExprPath(_, ref path) = &left.node {
99 check_nan(cx, path, expr.span);
101 if let &ExprPath(_, ref path) = &right.node {
102 check_nan(cx, path, expr.span);
109 fn check_nan(cx: &Context, path: &Path, span: Span) {
110 path.segments.last().map(|seg| if seg.identifier.name == "NAN" {
111 span_lint(cx, CMP_NAN, span,
112 "doomed comparison with NAN, use `std::{f32,f64}::is_nan()` instead");
116 declare_lint!(pub FLOAT_CMP, Warn,
117 "Warn on ==/!= comparison of floaty values");
119 #[derive(Copy,Clone)]
122 impl LintPass for FloatCmp {
123 fn get_lints(&self) -> LintArray {
124 lint_array!(FLOAT_CMP)
127 fn check_expr(&mut self, cx: &Context, expr: &Expr) {
128 if let ExprBinary(ref cmp, ref left, ref right) = expr.node {
130 if (op == BiEq || op == BiNe) && (is_float(cx, left) || is_float(cx, right)) {
131 span_lint(cx, FLOAT_CMP, expr.span, &format!(
132 "{}-comparison of f32 or f64 detected. Consider changing this to `abs({} - {}) < epsilon` for some suitable value of epsilon.",
133 binop_to_string(op), snippet(cx, left.span, ".."),
134 snippet(cx, right.span, "..")));
140 fn is_float(cx: &Context, expr: &Expr) -> bool {
141 if let ty::TyFloat(_) = walk_ptrs_ty(cx.tcx.expr_ty(expr)).sty {
148 declare_lint!(pub PRECEDENCE, Warn,
149 "Warn on mixing bit ops with integer arithmetic without parenthesis");
151 #[derive(Copy,Clone)]
152 pub struct Precedence;
154 impl LintPass for Precedence {
155 fn get_lints(&self) -> LintArray {
156 lint_array!(PRECEDENCE)
159 fn check_expr(&mut self, cx: &Context, expr: &Expr) {
160 if let ExprBinary(Spanned { node: op, ..}, ref left, ref right) = expr.node {
161 if is_bit_op(op) && (is_arith_expr(left) || is_arith_expr(right)) {
162 span_lint(cx, PRECEDENCE, expr.span,
163 "operator precedence can trip the unwary. Consider adding parenthesis to the subexpression.");
169 fn is_arith_expr(expr : &Expr) -> bool {
171 ExprBinary(Spanned { node: op, ..}, _, _) => is_arith_op(op),
176 fn is_bit_op(op : BinOp_) -> bool {
178 BiBitXor | BiBitAnd | BiBitOr | BiShl | BiShr => true,
183 fn is_arith_op(op : BinOp_) -> bool {
185 BiAdd | BiSub | BiMul | BiDiv | BiRem => true,
190 declare_lint!(pub CMP_OWNED, Warn,
191 "Warn on creating an owned string just for comparison");
193 #[derive(Copy,Clone)]
196 impl LintPass for CmpOwned {
197 fn get_lints(&self) -> LintArray {
198 lint_array!(CMP_OWNED)
201 fn check_expr(&mut self, cx: &Context, expr: &Expr) {
202 if let ExprBinary(ref cmp, ref left, ref right) = expr.node {
203 if is_comparison_binop(cmp.node) {
204 check_to_owned(cx, left, right.span);
205 check_to_owned(cx, right, left.span)
211 fn check_to_owned(cx: &Context, expr: &Expr, other_span: Span) {
213 &ExprMethodCall(Spanned{node: ref ident, ..}, _, ref args) => {
214 let name = ident.name;
215 if name == "to_string" ||
216 name == "to_owned" && is_str_arg(cx, args) {
217 span_lint(cx, CMP_OWNED, expr.span, &format!(
218 "this creates an owned instance just for comparison. \
219 Consider using `{}.as_slice()` to compare without allocation.",
220 snippet(cx, other_span, "..")))
223 &ExprCall(ref path, _) => {
224 if let &ExprPath(None, ref path) = &path.node {
225 if match_path(path, &["String", "from_str"]) ||
226 match_path(path, &["String", "from"]) {
227 span_lint(cx, CMP_OWNED, expr.span, &format!(
228 "this creates an owned instance just for comparison. \
229 Consider using `{}.as_slice()` to compare without allocation.",
230 snippet(cx, other_span, "..")))
238 fn is_str_arg(cx: &Context, args: &[P<Expr>]) -> bool {
239 args.len() == 1 && if let ty::TyStr =
240 walk_ptrs_ty(cx.tcx.expr_ty(&*args[0])).sty { true } else { false }
243 declare_lint!(pub MODULO_ONE, Warn, "Warn on expressions that include % 1, which is always 0");
245 #[derive(Copy,Clone)]
246 pub struct ModuloOne;
248 impl LintPass for ModuloOne {
249 fn get_lints(&self) -> LintArray {
250 lint_array!(MODULO_ONE)
253 fn check_expr(&mut self, cx: &Context, expr: &Expr) {
254 if let ExprBinary(ref cmp, _, ref right) = expr.node {
255 if let &Spanned {node: BinOp_::BiRem, ..} = cmp {
256 if is_lit_one(right) {
257 cx.span_lint(MODULO_ONE, expr.span, "any number modulo 1 will be 0");
264 fn is_lit_one(expr: &Expr) -> bool {
265 if let ExprLit(ref spanned) = expr.node {
266 if let LitInt(1, _) = spanned.node {