]> git.lizzy.rs Git - rust.git/blob - clippy_lints/src/misc.rs
Merge pull request #1101 from Manishearth/fix-1100
[rust.git] / clippy_lints / src / misc.rs
1 use reexport::*;
2 use rustc::hir::*;
3 use rustc::hir::intravisit::FnKind;
4 use rustc::lint::*;
5 use rustc::middle::const_val::ConstVal;
6 use rustc::ty;
7 use rustc_const_eval::EvalHint::ExprTypeChecked;
8 use rustc_const_eval::eval_const_expr_partial;
9 use rustc_const_math::ConstFloat;
10 use syntax::codemap::{Span, Spanned, ExpnFormat};
11 use syntax::ptr::P;
12 use utils::{
13     get_item_name, get_parent_expr, implements_trait, in_macro, is_integer_literal, match_path,
14     snippet, span_lint, span_lint_and_then, walk_ptrs_ty
15 };
16 use utils::sugg::Sugg;
17
18 /// **What it does:** This lint checks for function arguments and let bindings denoted as `ref`.
19 ///
20 /// **Why is this bad?** The `ref` declaration makes the function take an owned value, but turns the argument into a reference (which means that the value is destroyed when exiting the function). This adds not much value: either take a reference type, or take an owned value and create references in the body.
21 ///
22 /// For let bindings, `let x = &foo;` is preferred over `let ref x = foo`. The type of `x` is more obvious with the former.
23 ///
24 /// **Known problems:** If the argument is dereferenced within the function, removing the `ref` will lead to errors. This can be fixed by removing the dereferences, e.g. changing `*x` to `x` within the function.
25 ///
26 /// **Example:** `fn foo(ref x: u8) -> bool { .. }`
27 declare_lint! {
28     pub TOPLEVEL_REF_ARG, Warn,
29     "An entire binding was declared as `ref`, in a function argument (`fn foo(ref x: Bar)`), \
30      or a `let` statement (`let ref x = foo()`). In such cases, it is preferred to take \
31      references with `&`."
32 }
33
34 #[allow(missing_copy_implementations)]
35 pub struct TopLevelRefPass;
36
37 impl LintPass for TopLevelRefPass {
38     fn get_lints(&self) -> LintArray {
39         lint_array!(TOPLEVEL_REF_ARG)
40     }
41 }
42
43 impl LateLintPass for TopLevelRefPass {
44     fn check_fn(&mut self, cx: &LateContext, k: FnKind, decl: &FnDecl, _: &Block, _: Span, _: NodeId) {
45         if let FnKind::Closure(_) = k {
46             // Does not apply to closures
47             return;
48         }
49         for ref arg in &decl.inputs {
50             if let PatKind::Binding(BindByRef(_), _, _) = arg.pat.node {
51                 span_lint(cx,
52                           TOPLEVEL_REF_ARG,
53                           arg.pat.span,
54                           "`ref` directly on a function argument is ignored. Consider using a reference type instead.");
55             }
56         }
57     }
58     fn check_stmt(&mut self, cx: &LateContext, s: &Stmt) {
59         if_let_chain! {[
60             let StmtDecl(ref d, _) = s.node,
61             let DeclLocal(ref l) = d.node,
62             let PatKind::Binding(BindByRef(mt), i, None) = l.pat.node,
63             let Some(ref init) = l.init
64         ], {
65             let tyopt = if let Some(ref ty) = l.ty {
66                 format!(": &{}", snippet(cx, ty.span, "_"))
67             } else {
68                 "".to_owned()
69             };
70             let mutopt = if mt == Mutability::MutMutable {
71                 "mut "
72             } else {
73                 ""
74             };
75             span_lint_and_then(cx,
76                 TOPLEVEL_REF_ARG,
77                 l.pat.span,
78                 "`ref` on an entire `let` pattern is discouraged, take a reference with `&` instead",
79                 |db| {
80                     let init = Sugg::hir(cx, init, "..");
81                     db.span_suggestion(s.span,
82                                        "try",
83                                        format!("let {}{}{} = {};",
84                                                mutopt,
85                                                snippet(cx, i.span, "_"),
86                                                tyopt,
87                                                init.addr()));
88                 }
89             );
90         }}
91     }
92 }
93
94 /// **What it does:** This lint checks for comparisons to NAN.
95 ///
96 /// **Why is this bad?** NAN does not compare meaningfully to anything – not even itself – so those comparisons are simply wrong.
97 ///
98 /// **Known problems:** None
99 ///
100 /// **Example:** `x == NAN`
101 declare_lint!(pub CMP_NAN, Deny,
102               "comparisons to NAN (which will always return false, which is probably not intended)");
103
104 #[derive(Copy,Clone)]
105 pub struct CmpNan;
106
107 impl LintPass for CmpNan {
108     fn get_lints(&self) -> LintArray {
109         lint_array!(CMP_NAN)
110     }
111 }
112
113 impl LateLintPass for CmpNan {
114     fn check_expr(&mut self, cx: &LateContext, expr: &Expr) {
115         if let ExprBinary(ref cmp, ref left, ref right) = expr.node {
116             if cmp.node.is_comparison() {
117                 if let ExprPath(_, ref path) = left.node {
118                     check_nan(cx, path, expr.span);
119                 }
120                 if let ExprPath(_, ref path) = right.node {
121                     check_nan(cx, path, expr.span);
122                 }
123             }
124         }
125     }
126 }
127
128 fn check_nan(cx: &LateContext, path: &Path, span: Span) {
129     path.segments.last().map(|seg| {
130         if seg.name.as_str() == "NAN" {
131             span_lint(cx,
132                       CMP_NAN,
133                       span,
134                       "doomed comparison with NAN, use `std::{f32,f64}::is_nan()` instead");
135         }
136     });
137 }
138
139 /// **What it does:** This lint checks for (in-)equality comparisons on floating-point values (apart from zero), except in functions called `*eq*` (which probably implement equality for a type involving floats).
140 ///
141 /// **Why is this bad?** Floating point calculations are usually imprecise, so asking if two values are *exactly* equal is asking for trouble. For a good guide on what to do, see [the floating point guide](http://www.floating-point-gui.de/errors/comparison).
142 ///
143 /// **Known problems:** None
144 ///
145 /// **Example:** `y == 1.23f64`
146 declare_lint!(pub FLOAT_CMP, Warn,
147               "using `==` or `!=` on float values (as floating-point operations \
148                usually involve rounding errors, it is always better to check for approximate \
149                equality within small bounds)");
150
151 #[derive(Copy,Clone)]
152 pub struct FloatCmp;
153
154 impl LintPass for FloatCmp {
155     fn get_lints(&self) -> LintArray {
156         lint_array!(FLOAT_CMP)
157     }
158 }
159
160 impl LateLintPass for FloatCmp {
161     fn check_expr(&mut self, cx: &LateContext, expr: &Expr) {
162         if let ExprBinary(ref cmp, ref left, ref right) = expr.node {
163             let op = cmp.node;
164             if (op == BiEq || op == BiNe) && (is_float(cx, left) || is_float(cx, right)) {
165                 if is_allowed(cx, left) || is_allowed(cx, right) {
166                     return;
167                 }
168                 if let Some(name) = get_item_name(cx, expr) {
169                     let name = name.as_str();
170                     if name == "eq" || name == "ne" || name == "is_nan" || name.starts_with("eq_") ||
171                        name.ends_with("_eq") {
172                         return;
173                     }
174                 }
175                 span_lint_and_then(cx,
176                                    FLOAT_CMP,
177                                    expr.span,
178                                    "strict comparison of f32 or f64",
179                                    |db| {
180                     let lhs = Sugg::hir(cx, left, "..");
181                     let rhs = Sugg::hir(cx, right, "..");
182
183                     db.span_suggestion(expr.span,
184                                        "consider comparing them within some error",
185                                        format!("({}).abs() < error", lhs - rhs));
186                     db.span_note(expr.span, "std::f32::EPSILON and std::f64::EPSILON are available.");
187                 });
188             }
189         }
190     }
191 }
192
193 fn is_allowed(cx: &LateContext, expr: &Expr) -> bool {
194     let res = eval_const_expr_partial(cx.tcx, expr, ExprTypeChecked, None);
195     if let Ok(ConstVal::Float(val)) = res {
196         use std::cmp::Ordering;
197
198         let zero = ConstFloat::FInfer {
199             f32: 0.0,
200             f64: 0.0,
201         };
202
203         let infinity = ConstFloat::FInfer {
204             f32: ::std::f32::INFINITY,
205             f64: ::std::f64::INFINITY,
206         };
207
208         let neg_infinity = ConstFloat::FInfer {
209             f32: ::std::f32::NEG_INFINITY,
210             f64: ::std::f64::NEG_INFINITY,
211         };
212
213         val.try_cmp(zero) == Ok(Ordering::Equal)
214             || val.try_cmp(infinity) == Ok(Ordering::Equal)
215             || val.try_cmp(neg_infinity) == Ok(Ordering::Equal)
216     } else {
217         false
218     }
219 }
220
221 fn is_float(cx: &LateContext, expr: &Expr) -> bool {
222     matches!(walk_ptrs_ty(cx.tcx.expr_ty(expr)).sty, ty::TyFloat(_))
223 }
224
225 /// **What it does:** This lint checks for conversions to owned values just for the sake of a comparison.
226 ///
227 /// **Why is this bad?** The comparison can operate on a reference, so creating an owned value effectively throws it away directly afterwards, which is needlessly consuming code and heap space.
228 ///
229 /// **Known problems:** None
230 ///
231 /// **Example:** `x.to_owned() == y`
232 declare_lint!(pub CMP_OWNED, Warn,
233               "creating owned instances for comparing with others, e.g. `x == \"foo\".to_string()`");
234
235 #[derive(Copy,Clone)]
236 pub struct CmpOwned;
237
238 impl LintPass for CmpOwned {
239     fn get_lints(&self) -> LintArray {
240         lint_array!(CMP_OWNED)
241     }
242 }
243
244 impl LateLintPass for CmpOwned {
245     fn check_expr(&mut self, cx: &LateContext, expr: &Expr) {
246         if let ExprBinary(ref cmp, ref left, ref right) = expr.node {
247             if cmp.node.is_comparison() {
248                 check_to_owned(cx, left, right, true, cmp.span);
249                 check_to_owned(cx, right, left, false, cmp.span)
250             }
251         }
252     }
253 }
254
255 fn check_to_owned(cx: &LateContext, expr: &Expr, other: &Expr, left: bool, op: Span) {
256     let (arg_ty, snip) = match expr.node {
257         ExprMethodCall(Spanned { node: ref name, .. }, _, ref args) if args.len() == 1 => {
258             if name.as_str() == "to_string" || name.as_str() == "to_owned" && is_str_arg(cx, args) {
259                 (cx.tcx.expr_ty(&args[0]), snippet(cx, args[0].span, ".."))
260             } else {
261                 return;
262             }
263         }
264         ExprCall(ref path, ref v) if v.len() == 1 => {
265             if let ExprPath(None, ref path) = path.node {
266                 if match_path(path, &["String", "from_str"]) || match_path(path, &["String", "from"]) {
267                     (cx.tcx.expr_ty(&v[0]), snippet(cx, v[0].span, ".."))
268                 } else {
269                     return;
270                 }
271             } else {
272                 return;
273             }
274         }
275         _ => return,
276     };
277
278     let other_ty = cx.tcx.expr_ty(other);
279     let partial_eq_trait_id = match cx.tcx.lang_items.eq_trait() {
280         Some(id) => id,
281         None => return,
282     };
283
284     if !implements_trait(cx, arg_ty, partial_eq_trait_id, vec![other_ty]) {
285         return;
286     }
287
288     if left {
289         span_lint(cx,
290                   CMP_OWNED,
291                   expr.span,
292                   &format!("this creates an owned instance just for comparison. Consider using `{} {} {}` to \
293                             compare without allocation",
294                            snip,
295                            snippet(cx, op, "=="),
296                            snippet(cx, other.span, "..")));
297     } else {
298         span_lint(cx,
299                   CMP_OWNED,
300                   expr.span,
301                   &format!("this creates an owned instance just for comparison. Consider using `{} {} {}` to \
302                             compare without allocation",
303                            snippet(cx, other.span, ".."),
304                            snippet(cx, op, "=="),
305                            snip));
306     }
307
308 }
309
310 fn is_str_arg(cx: &LateContext, args: &[P<Expr>]) -> bool {
311     args.len() == 1 &&
312         matches!(walk_ptrs_ty(cx.tcx.expr_ty(&args[0])).sty, ty::TyStr)
313 }
314
315 /// **What it does:** This lint checks for getting the remainder of a division by one.
316 ///
317 /// **Why is this bad?** The result can only ever be zero. No one will write such code deliberately, unless trying to win an Underhanded Rust Contest. Even for that contest, it's probably a bad idea. Use something more underhanded.
318 ///
319 /// **Known problems:** None
320 ///
321 /// **Example:** `x % 1`
322 declare_lint!(pub MODULO_ONE, Warn, "taking a number modulo 1, which always returns 0");
323
324 #[derive(Copy,Clone)]
325 pub struct ModuloOne;
326
327 impl LintPass for ModuloOne {
328     fn get_lints(&self) -> LintArray {
329         lint_array!(MODULO_ONE)
330     }
331 }
332
333 impl LateLintPass for ModuloOne {
334     fn check_expr(&mut self, cx: &LateContext, expr: &Expr) {
335         if let ExprBinary(ref cmp, _, ref right) = expr.node {
336             if let Spanned { node: BinOp_::BiRem, .. } = *cmp {
337                 if is_integer_literal(right, 1) {
338                     span_lint(cx, MODULO_ONE, expr.span, "any number modulo 1 will be 0");
339                 }
340             }
341         }
342     }
343 }
344
345 /// **What it does:** This lint checks for patterns in the form `name @ _`.
346 ///
347 /// **Why is this bad?** It's almost always more readable to just use direct bindings.
348 ///
349 /// **Known problems:** None
350 ///
351 /// **Example**:
352 /// ```
353 /// match v {
354 ///     Some(x) => (),
355 ///     y @ _   => (), // easier written as `y`,
356 /// }
357 /// ```
358 declare_lint!(pub REDUNDANT_PATTERN, Warn, "using `name @ _` in a pattern");
359
360 #[derive(Copy,Clone)]
361 pub struct PatternPass;
362
363 impl LintPass for PatternPass {
364     fn get_lints(&self) -> LintArray {
365         lint_array!(REDUNDANT_PATTERN)
366     }
367 }
368
369 impl LateLintPass for PatternPass {
370     fn check_pat(&mut self, cx: &LateContext, pat: &Pat) {
371         if let PatKind::Binding(_, ref ident, Some(ref right)) = pat.node {
372             if right.node == PatKind::Wild {
373                 span_lint(cx,
374                           REDUNDANT_PATTERN,
375                           pat.span,
376                           &format!("the `{} @ _` pattern can be written as just `{}`",
377                                    ident.node,
378                                    ident.node));
379             }
380         }
381     }
382 }
383
384
385 /// **What it does:** This lint checks for the use of bindings with a single leading underscore
386 ///
387 /// **Why is this bad?** A single leading underscore is usually used to indicate that a binding
388 /// will not be used. Using such a binding breaks this expectation.
389 ///
390 /// **Known problems:** The lint does not work properly with desugaring and macro, it has been
391 /// allowed in the mean time.
392 ///
393 /// **Example**:
394 /// ```
395 /// let _x = 0;
396 /// let y = _x + 1; // Here we are using `_x`, even though it has a leading underscore.
397 ///                 // We should rename `_x` to `x`
398 /// ```
399 declare_lint!(pub USED_UNDERSCORE_BINDING, Allow,
400               "using a binding which is prefixed with an underscore");
401
402 #[derive(Copy, Clone)]
403 pub struct UsedUnderscoreBinding;
404
405 impl LintPass for UsedUnderscoreBinding {
406     fn get_lints(&self) -> LintArray {
407         lint_array!(USED_UNDERSCORE_BINDING)
408     }
409 }
410
411 impl LateLintPass for UsedUnderscoreBinding {
412     #[cfg_attr(rustfmt, rustfmt_skip)]
413     fn check_expr(&mut self, cx: &LateContext, expr: &Expr) {
414         if in_attributes_expansion(cx, expr) {
415             // Don't lint things expanded by #[derive(...)], etc
416             return;
417         }
418         let binding = match expr.node {
419             ExprPath(_, ref path) => {
420                 let binding = path.segments
421                                 .last()
422                                 .expect("path should always have at least one segment")
423                                 .name
424                                 .as_str();
425                 if binding.starts_with('_') &&
426                    !binding.starts_with("__") &&
427                    binding != "_result" && // FIXME: #944
428                    is_used(cx, expr) &&
429                    // don't lint if the declaration is in a macro
430                    non_macro_local(cx, &cx.tcx.expect_def(expr.id)) {
431                     Some(binding)
432                 } else {
433                     None
434                 }
435             }
436             ExprField(_, spanned) => {
437                 let name = spanned.node.as_str();
438                 if name.starts_with('_') && !name.starts_with("__") {
439                     Some(name)
440                 } else {
441                     None
442                 }
443             }
444             _ => None,
445         };
446         if let Some(binding) = binding {
447             span_lint(cx,
448                       USED_UNDERSCORE_BINDING,
449                       expr.span,
450                       &format!("used binding `{}` which is prefixed with an underscore. A leading \
451                                 underscore signals that a binding will not be used.", binding));
452         }
453     }
454 }
455
456 /// Heuristic to see if an expression is used. Should be compatible with `unused_variables`'s idea
457 /// of what it means for an expression to be "used".
458 fn is_used(cx: &LateContext, expr: &Expr) -> bool {
459     if let Some(ref parent) = get_parent_expr(cx, expr) {
460         match parent.node {
461             ExprAssign(_, ref rhs) |
462             ExprAssignOp(_, _, ref rhs) => **rhs == *expr,
463             _ => is_used(cx, parent),
464         }
465     } else {
466         true
467     }
468 }
469
470 /// Test whether an expression is in a macro expansion (e.g. something generated by
471 /// `#[derive(...)`] or the like).
472 fn in_attributes_expansion(cx: &LateContext, expr: &Expr) -> bool {
473     cx.sess().codemap().with_expn_info(expr.span.expn_id, |info_opt| {
474         info_opt.map_or(false, |info| {
475             matches!(info.callee.format, ExpnFormat::MacroAttribute(_))
476         })
477     })
478 }
479
480 /// Test whether `def` is a variable defined outside a macro.
481 fn non_macro_local(cx: &LateContext, def: &def::Def) -> bool {
482     match *def {
483         def::Def::Local(_, id) | def::Def::Upvar(_, id, _, _) => {
484             if let Some(span) = cx.tcx.map.opt_span(id) {
485                 !in_macro(cx, span)
486             } else {
487                 true
488             }
489         }
490         _ => false,
491     }
492 }