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