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