]> git.lizzy.rs Git - rust.git/blob - clippy_lints/src/misc.rs
Merge remote-tracking branch 'origin/rustup' into sugg
[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(_), 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             span_lint_and_then(cx,
71                 TOPLEVEL_REF_ARG,
72                 l.pat.span,
73                 "`ref` on an entire `let` pattern is discouraged, take a reference with `&` instead",
74                 |db| {
75                     let init = &Sugg::hir(cx, init, "..");
76                     db.span_suggestion(s.span,
77                                        "try",
78                                        format!("let {}{} = {};",
79                                                snippet(cx, i.span, "_"),
80                                                tyopt,
81                                                init.addr()));
82                 }
83             );
84         }}
85     }
86 }
87
88 /// **What it does:** This lint checks for comparisons to NAN.
89 ///
90 /// **Why is this bad?** NAN does not compare meaningfully to anything – not even itself – so those comparisons are simply wrong.
91 ///
92 /// **Known problems:** None
93 ///
94 /// **Example:** `x == NAN`
95 declare_lint!(pub CMP_NAN, Deny,
96               "comparisons to NAN (which will always return false, which is probably not intended)");
97
98 #[derive(Copy,Clone)]
99 pub struct CmpNan;
100
101 impl LintPass for CmpNan {
102     fn get_lints(&self) -> LintArray {
103         lint_array!(CMP_NAN)
104     }
105 }
106
107 impl LateLintPass for CmpNan {
108     fn check_expr(&mut self, cx: &LateContext, expr: &Expr) {
109         if let ExprBinary(ref cmp, ref left, ref right) = expr.node {
110             if cmp.node.is_comparison() {
111                 if let ExprPath(_, ref path) = left.node {
112                     check_nan(cx, path, expr.span);
113                 }
114                 if let ExprPath(_, ref path) = right.node {
115                     check_nan(cx, path, expr.span);
116                 }
117             }
118         }
119     }
120 }
121
122 fn check_nan(cx: &LateContext, path: &Path, span: Span) {
123     path.segments.last().map(|seg| {
124         if seg.name.as_str() == "NAN" {
125             span_lint(cx,
126                       CMP_NAN,
127                       span,
128                       "doomed comparison with NAN, use `std::{f32,f64}::is_nan()` instead");
129         }
130     });
131 }
132
133 /// **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).
134 ///
135 /// **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).
136 ///
137 /// **Known problems:** None
138 ///
139 /// **Example:** `y == 1.23f64`
140 declare_lint!(pub FLOAT_CMP, Warn,
141               "using `==` or `!=` on float values (as floating-point operations \
142                usually involve rounding errors, it is always better to check for approximate \
143                equality within small bounds)");
144
145 #[derive(Copy,Clone)]
146 pub struct FloatCmp;
147
148 impl LintPass for FloatCmp {
149     fn get_lints(&self) -> LintArray {
150         lint_array!(FLOAT_CMP)
151     }
152 }
153
154 impl LateLintPass for FloatCmp {
155     fn check_expr(&mut self, cx: &LateContext, expr: &Expr) {
156         if let ExprBinary(ref cmp, ref left, ref right) = expr.node {
157             let op = cmp.node;
158             if (op == BiEq || op == BiNe) && (is_float(cx, left) || is_float(cx, right)) {
159                 if is_allowed(cx, left) || is_allowed(cx, right) {
160                     return;
161                 }
162                 if let Some(name) = get_item_name(cx, expr) {
163                     let name = name.as_str();
164                     if name == "eq" || name == "ne" || name == "is_nan" || name.starts_with("eq_") ||
165                        name.ends_with("_eq") {
166                         return;
167                     }
168                 }
169                 span_lint_and_then(cx,
170                                    FLOAT_CMP,
171                                    expr.span,
172                                    "strict comparison of f32 or f64",
173                                    |db| {
174                     let lhs = &Sugg::hir(cx, left, "..");
175                     let rhs = &Sugg::hir(cx, right, "..");
176
177                     db.span_suggestion(expr.span,
178                                        "consider comparing them within some error",
179                                        format!("({}).abs() < error", lhs - rhs));
180                     db.span_note(expr.span, "std::f32::EPSILON and std::f64::EPSILON are available.");
181                 });
182             }
183         }
184     }
185 }
186
187 fn is_allowed(cx: &LateContext, expr: &Expr) -> bool {
188     let res = eval_const_expr_partial(cx.tcx, expr, ExprTypeChecked, None);
189     if let Ok(ConstVal::Float(val)) = res {
190         use std::cmp::Ordering;
191
192         let zero = ConstFloat::FInfer {
193             f32: 0.0,
194             f64: 0.0,
195         };
196
197         let infinity = ConstFloat::FInfer {
198             f32: ::std::f32::INFINITY,
199             f64: ::std::f64::INFINITY,
200         };
201
202         let neg_infinity = ConstFloat::FInfer {
203             f32: ::std::f32::NEG_INFINITY,
204             f64: ::std::f64::NEG_INFINITY,
205         };
206
207         val.try_cmp(zero) == Ok(Ordering::Equal)
208             || val.try_cmp(infinity) == Ok(Ordering::Equal)
209             || val.try_cmp(neg_infinity) == Ok(Ordering::Equal)
210     } else {
211         false
212     }
213 }
214
215 fn is_float(cx: &LateContext, expr: &Expr) -> bool {
216     matches!(walk_ptrs_ty(cx.tcx.expr_ty(expr)).sty, ty::TyFloat(_))
217 }
218
219 /// **What it does:** This lint checks for conversions to owned values just for the sake of a comparison.
220 ///
221 /// **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.
222 ///
223 /// **Known problems:** None
224 ///
225 /// **Example:** `x.to_owned() == y`
226 declare_lint!(pub CMP_OWNED, Warn,
227               "creating owned instances for comparing with others, e.g. `x == \"foo\".to_string()`");
228
229 #[derive(Copy,Clone)]
230 pub struct CmpOwned;
231
232 impl LintPass for CmpOwned {
233     fn get_lints(&self) -> LintArray {
234         lint_array!(CMP_OWNED)
235     }
236 }
237
238 impl LateLintPass for CmpOwned {
239     fn check_expr(&mut self, cx: &LateContext, expr: &Expr) {
240         if let ExprBinary(ref cmp, ref left, ref right) = expr.node {
241             if cmp.node.is_comparison() {
242                 check_to_owned(cx, left, right, true, cmp.span);
243                 check_to_owned(cx, right, left, false, cmp.span)
244             }
245         }
246     }
247 }
248
249 fn check_to_owned(cx: &LateContext, expr: &Expr, other: &Expr, left: bool, op: Span) {
250     let (arg_ty, snip) = match expr.node {
251         ExprMethodCall(Spanned { node: ref name, .. }, _, ref args) if args.len() == 1 => {
252             if name.as_str() == "to_string" || name.as_str() == "to_owned" && is_str_arg(cx, args) {
253                 (cx.tcx.expr_ty(&args[0]), snippet(cx, args[0].span, ".."))
254             } else {
255                 return;
256             }
257         }
258         ExprCall(ref path, ref v) if v.len() == 1 => {
259             if let ExprPath(None, ref path) = path.node {
260                 if match_path(path, &["String", "from_str"]) || match_path(path, &["String", "from"]) {
261                     (cx.tcx.expr_ty(&v[0]), snippet(cx, v[0].span, ".."))
262                 } else {
263                     return;
264                 }
265             } else {
266                 return;
267             }
268         }
269         _ => return,
270     };
271
272     let other_ty = cx.tcx.expr_ty(other);
273     let partial_eq_trait_id = match cx.tcx.lang_items.eq_trait() {
274         Some(id) => id,
275         None => return,
276     };
277
278     if !implements_trait(cx, arg_ty, partial_eq_trait_id, vec![other_ty]) {
279         return;
280     }
281
282     if left {
283         span_lint(cx,
284                   CMP_OWNED,
285                   expr.span,
286                   &format!("this creates an owned instance just for comparison. Consider using `{} {} {}` to \
287                             compare without allocation",
288                            snip,
289                            snippet(cx, op, "=="),
290                            snippet(cx, other.span, "..")));
291     } else {
292         span_lint(cx,
293                   CMP_OWNED,
294                   expr.span,
295                   &format!("this creates an owned instance just for comparison. Consider using `{} {} {}` to \
296                             compare without allocation",
297                            snippet(cx, other.span, ".."),
298                            snippet(cx, op, "=="),
299                            snip));
300     }
301
302 }
303
304 fn is_str_arg(cx: &LateContext, args: &[P<Expr>]) -> bool {
305     args.len() == 1 &&
306         matches!(walk_ptrs_ty(cx.tcx.expr_ty(&args[0])).sty, ty::TyStr)
307 }
308
309 /// **What it does:** This lint checks for getting the remainder of a division by one.
310 ///
311 /// **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.
312 ///
313 /// **Known problems:** None
314 ///
315 /// **Example:** `x % 1`
316 declare_lint!(pub MODULO_ONE, Warn, "taking a number modulo 1, which always returns 0");
317
318 #[derive(Copy,Clone)]
319 pub struct ModuloOne;
320
321 impl LintPass for ModuloOne {
322     fn get_lints(&self) -> LintArray {
323         lint_array!(MODULO_ONE)
324     }
325 }
326
327 impl LateLintPass for ModuloOne {
328     fn check_expr(&mut self, cx: &LateContext, expr: &Expr) {
329         if let ExprBinary(ref cmp, _, ref right) = expr.node {
330             if let Spanned { node: BinOp_::BiRem, .. } = *cmp {
331                 if is_integer_literal(right, 1) {
332                     span_lint(cx, MODULO_ONE, expr.span, "any number modulo 1 will be 0");
333                 }
334             }
335         }
336     }
337 }
338
339 /// **What it does:** This lint checks for patterns in the form `name @ _`.
340 ///
341 /// **Why is this bad?** It's almost always more readable to just use direct bindings.
342 ///
343 /// **Known problems:** None
344 ///
345 /// **Example**:
346 /// ```
347 /// match v {
348 ///     Some(x) => (),
349 ///     y @ _   => (), // easier written as `y`,
350 /// }
351 /// ```
352 declare_lint!(pub REDUNDANT_PATTERN, Warn, "using `name @ _` in a pattern");
353
354 #[derive(Copy,Clone)]
355 pub struct PatternPass;
356
357 impl LintPass for PatternPass {
358     fn get_lints(&self) -> LintArray {
359         lint_array!(REDUNDANT_PATTERN)
360     }
361 }
362
363 impl LateLintPass for PatternPass {
364     fn check_pat(&mut self, cx: &LateContext, pat: &Pat) {
365         if let PatKind::Binding(_, ref ident, Some(ref right)) = pat.node {
366             if right.node == PatKind::Wild {
367                 span_lint(cx,
368                           REDUNDANT_PATTERN,
369                           pat.span,
370                           &format!("the `{} @ _` pattern can be written as just `{}`",
371                                    ident.node,
372                                    ident.node));
373             }
374         }
375     }
376 }
377
378
379 /// **What it does:** This lint checks for the use of bindings with a single leading underscore
380 ///
381 /// **Why is this bad?** A single leading underscore is usually used to indicate that a binding
382 /// will not be used. Using such a binding breaks this expectation.
383 ///
384 /// **Known problems:** The lint does not work properly with desugaring and macro, it has been
385 /// allowed in the mean time.
386 ///
387 /// **Example**:
388 /// ```
389 /// let _x = 0;
390 /// let y = _x + 1; // Here we are using `_x`, even though it has a leading underscore.
391 ///                 // We should rename `_x` to `x`
392 /// ```
393 declare_lint!(pub USED_UNDERSCORE_BINDING, Allow,
394               "using a binding which is prefixed with an underscore");
395
396 #[derive(Copy, Clone)]
397 pub struct UsedUnderscoreBinding;
398
399 impl LintPass for UsedUnderscoreBinding {
400     fn get_lints(&self) -> LintArray {
401         lint_array!(USED_UNDERSCORE_BINDING)
402     }
403 }
404
405 impl LateLintPass for UsedUnderscoreBinding {
406     #[cfg_attr(rustfmt, rustfmt_skip)]
407     fn check_expr(&mut self, cx: &LateContext, expr: &Expr) {
408         if in_attributes_expansion(cx, expr) {
409             // Don't lint things expanded by #[derive(...)], etc
410             return;
411         }
412         let binding = match expr.node {
413             ExprPath(_, ref path) => {
414                 let binding = path.segments
415                                 .last()
416                                 .expect("path should always have at least one segment")
417                                 .name
418                                 .as_str();
419                 if binding.starts_with('_') &&
420                    !binding.starts_with("__") &&
421                    binding != "_result" && // FIXME: #944
422                    is_used(cx, expr) &&
423                    // don't lint if the declaration is in a macro
424                    non_macro_local(cx, &cx.tcx.expect_def(expr.id)) {
425                     Some(binding)
426                 } else {
427                     None
428                 }
429             }
430             ExprField(_, spanned) => {
431                 let name = spanned.node.as_str();
432                 if name.starts_with('_') && !name.starts_with("__") {
433                     Some(name)
434                 } else {
435                     None
436                 }
437             }
438             _ => None,
439         };
440         if let Some(binding) = binding {
441             span_lint(cx,
442                       USED_UNDERSCORE_BINDING,
443                       expr.span,
444                       &format!("used binding `{}` which is prefixed with an underscore. A leading \
445                                 underscore signals that a binding will not be used.", binding));
446         }
447     }
448 }
449
450 /// Heuristic to see if an expression is used. Should be compatible with `unused_variables`'s idea
451 /// of what it means for an expression to be "used".
452 fn is_used(cx: &LateContext, expr: &Expr) -> bool {
453     if let Some(ref parent) = get_parent_expr(cx, expr) {
454         match parent.node {
455             ExprAssign(_, ref rhs) |
456             ExprAssignOp(_, _, ref rhs) => **rhs == *expr,
457             _ => is_used(cx, parent),
458         }
459     } else {
460         true
461     }
462 }
463
464 /// Test whether an expression is in a macro expansion (e.g. something generated by
465 /// `#[derive(...)`] or the like).
466 fn in_attributes_expansion(cx: &LateContext, expr: &Expr) -> bool {
467     cx.sess().codemap().with_expn_info(expr.span.expn_id, |info_opt| {
468         info_opt.map_or(false, |info| {
469             matches!(info.callee.format, ExpnFormat::MacroAttribute(_))
470         })
471     })
472 }
473
474 /// Test whether `def` is a variable defined outside a macro.
475 fn non_macro_local(cx: &LateContext, def: &def::Def) -> bool {
476     match *def {
477         def::Def::Local(_, id) | def::Def::Upvar(_, id, _, _) => {
478             if let Some(span) = cx.tcx.map.opt_span(id) {
479                 !in_macro(cx, span)
480             } else {
481                 true
482             }
483         }
484         _ => false,
485     }
486 }