]> git.lizzy.rs Git - rust.git/blob - src/misc.rs
Add wiki docs, in line with #492
[rust.git] / src / misc.rs
1 use rustc::lint::*;
2 use syntax::ptr::P;
3 use rustc_front::hir::*;
4 use reexport::*;
5 use rustc_front::util::{is_comparison_binop, binop_to_string};
6 use syntax::codemap::{Span, Spanned};
7 use rustc_front::intravisit::FnKind;
8 use rustc::middle::ty;
9 use rustc::middle::const_eval::ConstVal::Float;
10 use rustc::middle::const_eval::eval_const_expr_partial;
11 use rustc::middle::const_eval::EvalHint::ExprTypeChecked;
12 use rustc::middle::def::Def;
13
14 use utils::{get_item_name, match_path, snippet, span_lint, walk_ptrs_ty, is_integer_literal};
15 use utils::span_help_and_lint;
16
17 /// **What it does:** This lint checks for function arguments and let bindings denoted as `ref`. It is `Warn` by default.
18 ///
19 /// **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.
20 ///
21 /// For let bindings, `let x = &foo;` is preferred over `let ref x = foo`. The type of `x` is more obvious with the former.
22 ///
23 /// **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.
24 ///
25 /// **Example:** `fn foo(ref x: u8) -> bool { .. }`
26 declare_lint!(pub TOPLEVEL_REF_ARG, Warn,
27               "An entire binding was declared as `ref`, in a function argument (`fn foo(ref x: Bar)`), \
28                or a `let` statement (`let ref x = foo()`). In such cases, it is preferred to take \
29                references with `&`.");
30
31 #[allow(missing_copy_implementations)]
32 pub struct TopLevelRefPass;
33
34 impl LintPass for TopLevelRefPass {
35     fn get_lints(&self) -> LintArray {
36         lint_array!(TOPLEVEL_REF_ARG)
37     }
38 }
39
40 impl LateLintPass for TopLevelRefPass {
41     fn check_fn(&mut self, cx: &LateContext, k: FnKind, decl: &FnDecl, _: &Block, _: Span, _: NodeId) {
42         if let FnKind::Closure = k {
43             // Does not apply to closures
44             return
45         }
46         for ref arg in &decl.inputs {
47             if let PatIdent(BindByRef(_), _, _) = arg.pat.node {
48                 span_lint(cx,
49                     TOPLEVEL_REF_ARG,
50                     arg.pat.span,
51                     "`ref` directly on a function argument is ignored. Consider using a reference type instead."
52                 );
53             }
54         }
55     }
56     fn check_stmt(&mut self, cx: &LateContext, s: &Stmt) {
57         if_let_chain! {
58             [
59             let StmtDecl(ref d, _) = s.node,
60             let DeclLocal(ref l) = d.node,
61             let PatIdent(BindByRef(_), i, None) = l.pat.node,
62             let Some(ref init) = l.init
63             ], {
64                 let tyopt = if let Some(ref ty) = l.ty {
65                     format!(": {:?} ", ty)
66                 } else {
67                     "".to_owned()
68                 };
69                 span_help_and_lint(cx,
70                     TOPLEVEL_REF_ARG,
71                     l.pat.span,
72                     "`ref` on an entire `let` pattern is discouraged, take a reference with & instead",
73                     &format!("try `let {} {}= &{};`", snippet(cx, i.span, "_"),
74                              tyopt, snippet(cx, init.span, "_"))
75                 );
76             }
77         };
78     }
79 }
80
81 /// **What it does:** This lint checks for comparisons to NAN. It is `Deny` by default.
82 ///
83 /// **Why is this bad?** NAN does not compare meaningfully to anything – not even itself – so those comparisons are simply wrong.
84 ///
85 /// **Known problems:** None
86 ///
87 /// **Example:** `x == NAN`
88 declare_lint!(pub CMP_NAN, Deny,
89               "comparisons to NAN (which will always return false, which is probably not intended)");
90
91 #[derive(Copy,Clone)]
92 pub struct CmpNan;
93
94 impl LintPass for CmpNan {
95     fn get_lints(&self) -> LintArray {
96         lint_array!(CMP_NAN)
97     }
98 }
99
100 impl LateLintPass for CmpNan {
101     fn check_expr(&mut self, cx: &LateContext, expr: &Expr) {
102         if let ExprBinary(ref cmp, ref left, ref right) = expr.node {
103             if is_comparison_binop(cmp.node) {
104                 if let ExprPath(_, ref path) = left.node {
105                     check_nan(cx, path, expr.span);
106                 }
107                 if let ExprPath(_, ref path) = right.node {
108                     check_nan(cx, path, expr.span);
109                 }
110             }
111         }
112     }
113 }
114
115 fn check_nan(cx: &LateContext, path: &Path, span: Span) {
116     path.segments.last().map(|seg| if seg.identifier.name.as_str() == "NAN" {
117         span_lint(cx, CMP_NAN, span,
118             "doomed comparison with NAN, use `std::{f32,f64}::is_nan()` instead");
119     });
120 }
121
122 /// **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). It is `Warn` by default.
123 ///
124 /// **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).
125 ///
126 /// **Known problems:** None
127 ///
128 /// **Example:** `y == 1.23f64`
129 declare_lint!(pub FLOAT_CMP, Warn,
130               "using `==` or `!=` on float values (as floating-point operations \
131                usually involve rounding errors, it is always better to check for approximate \
132                equality within small bounds)");
133
134 #[derive(Copy,Clone)]
135 pub struct FloatCmp;
136
137 impl LintPass for FloatCmp {
138     fn get_lints(&self) -> LintArray {
139         lint_array!(FLOAT_CMP)
140     }
141 }
142
143 impl LateLintPass for FloatCmp {
144     fn check_expr(&mut self, cx: &LateContext, expr: &Expr) {
145         if let ExprBinary(ref cmp, ref left, ref right) = expr.node {
146             let op = cmp.node;
147             if (op == BiEq || op == BiNe) && (is_float(cx, left) || is_float(cx, right)) {
148                 if is_allowed(cx, left) || is_allowed(cx, right) { return; }
149                 if let Some(name) = get_item_name(cx, expr) {
150                     let name = name.as_str();
151                     if name == "eq" || name == "ne" || name == "is_nan" ||
152                             name.starts_with("eq_") ||
153                             name.ends_with("_eq") {
154                         return;
155                     }
156                 }
157                 span_lint(cx, FLOAT_CMP, expr.span, &format!(
158                     "{}-comparison of f32 or f64 detected. Consider changing this to \
159                      `abs({} - {}) < epsilon` for some suitable value of epsilon",
160                     binop_to_string(op), snippet(cx, left.span, ".."),
161                     snippet(cx, right.span, "..")));
162             }
163         }
164     }
165 }
166
167 fn is_allowed(cx: &LateContext, expr: &Expr) -> bool {
168     let res = eval_const_expr_partial(cx.tcx, expr, ExprTypeChecked, None);
169     if let Ok(Float(val)) = res {
170         val == 0.0 || val == ::std::f64::INFINITY || val == ::std::f64::NEG_INFINITY
171     } else { false }
172 }
173
174 fn is_float(cx: &LateContext, expr: &Expr) -> bool {
175     if let ty::TyFloat(_) = walk_ptrs_ty(cx.tcx.expr_ty(expr)).sty {
176         true
177     } else {
178         false
179     }
180 }
181
182 /// **What it does:** This lint checks for conversions to owned values just for the sake of a comparison. It is `Warn` by default.
183 ///
184 /// **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.
185 ///
186 /// **Known problems:** None
187 ///
188 /// **Example:** `x.to_owned() == y`
189 declare_lint!(pub CMP_OWNED, Warn,
190               "creating owned instances for comparing with others, e.g. `x == \"foo\".to_string()`");
191
192 #[derive(Copy,Clone)]
193 pub struct CmpOwned;
194
195 impl LintPass for CmpOwned {
196     fn get_lints(&self) -> LintArray {
197         lint_array!(CMP_OWNED)
198     }
199 }
200
201 impl LateLintPass for CmpOwned {
202     fn check_expr(&mut self, cx: &LateContext, expr: &Expr) {
203         if let ExprBinary(ref cmp, ref left, ref right) = expr.node {
204             if is_comparison_binop(cmp.node) {
205                 check_to_owned(cx, left, right.span, true, cmp.span);
206                 check_to_owned(cx, right, left.span, false, cmp.span)
207             }
208         }
209     }
210 }
211
212 fn check_to_owned(cx: &LateContext, expr: &Expr, other_span: Span, left: bool, op: Span) {
213     let snip = match expr.node {
214         ExprMethodCall(Spanned{node: ref name, ..}, _, ref args) if args.len() == 1 => {
215             if name.as_str() == "to_string" ||
216                 name.as_str() == "to_owned" && is_str_arg(cx, args) {
217                     snippet(cx, args[0].span, "..")
218                 } else {
219                     return
220                 }
221         }
222         ExprCall(ref path, ref v) if v.len() == 1 => {
223             if let ExprPath(None, ref path) = path.node {
224                 if match_path(path, &["String", "from_str"]) ||
225                     match_path(path, &["String", "from"]) {
226                             snippet(cx, v[0].span, "..")
227                     } else {
228                         return
229                     }
230             } else {
231                 return
232             }
233         }
234         _ => return
235     };
236     if left {
237         span_lint(cx, CMP_OWNED, expr.span, &format!(
238         "this creates an owned instance just for comparison. Consider using \
239         `{} {} {}` to compare without allocation", snip,
240         snippet(cx, op, "=="), snippet(cx, other_span, "..")));
241     } else {
242         span_lint(cx, CMP_OWNED, expr.span, &format!(
243         "this creates an owned instance just for comparison. Consider using \
244         `{} {} {}` to compare without allocation",
245         snippet(cx, other_span, ".."), snippet(cx, op, "=="),  snip));
246     }
247
248 }
249
250 fn is_str_arg(cx: &LateContext, args: &[P<Expr>]) -> bool {
251     args.len() == 1 && if let ty::TyStr =
252         walk_ptrs_ty(cx.tcx.expr_ty(&args[0])).sty { true } else { false }
253 }
254
255 /// **What it does:** This lint checks for getting the remainder of a division by one. It is `Warn` by default.
256 ///
257 /// **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.
258 ///
259 /// **Known problems:** None
260 ///
261 /// **Example:** `x % 1`
262 declare_lint!(pub MODULO_ONE, Warn, "taking a number modulo 1, which always returns 0");
263
264 #[derive(Copy,Clone)]
265 pub struct ModuloOne;
266
267 impl LintPass for ModuloOne {
268     fn get_lints(&self) -> LintArray {
269         lint_array!(MODULO_ONE)
270     }
271 }
272
273 impl LateLintPass for ModuloOne {
274     fn check_expr(&mut self, cx: &LateContext, expr: &Expr) {
275         if let ExprBinary(ref cmp, _, ref right) = expr.node {
276             if let Spanned {node: BinOp_::BiRem, ..} = *cmp {
277                 if is_integer_literal(right, 1) {
278                     cx.span_lint(MODULO_ONE, expr.span, "any number modulo 1 will be 0");
279                 }
280             }
281         }
282     }
283 }
284
285 /// **What it does:** This lint checks for patterns in the form `name @ _`.
286 ///
287 /// **Why is this bad?** It's almost always more readable to just use direct bindings.
288 ///
289 /// **Known problems:** None
290 ///
291 /// **Example**:
292 /// ```
293 /// match v {
294 ///     Some(x) => (),
295 ///     y @ _   => (), // easier written as `y`,
296 /// }
297 /// ```
298 declare_lint!(pub REDUNDANT_PATTERN, Warn, "using `name @ _` in a pattern");
299
300 #[derive(Copy,Clone)]
301 pub struct PatternPass;
302
303 impl LintPass for PatternPass {
304     fn get_lints(&self) -> LintArray {
305         lint_array!(REDUNDANT_PATTERN)
306     }
307 }
308
309 impl LateLintPass for PatternPass {
310     fn check_pat(&mut self, cx: &LateContext, pat: &Pat) {
311         if let PatIdent(_, ref ident, Some(ref right)) = pat.node {
312             if right.node == PatWild {
313                 cx.span_lint(REDUNDANT_PATTERN, pat.span, &format!(
314                     "the `{} @ _` pattern can be written as just `{}`",
315                     ident.node.name, ident.node.name));
316             }
317         }
318     }
319 }
320
321
322 /// **What it does:** This lint checks for the use of bindings with a single leading underscore
323 ///
324 /// **Why is this bad?** A single leading underscore is usually used to indicate that a binding
325 /// will not be used. Using such a binding breaks this expectation.
326 ///
327 /// **Known problems:** This lint's idea of a "used" variable is not quite the same as in the
328 /// built-in `unused_variables` lint. For example, in the following code
329 /// ```
330 /// fn foo(_y: u32) -> u32) {
331 ///     let _x = 1;
332 ///     _x +=1;
333 ///     y
334 /// }
335 /// ```
336 /// _x will trigger both the `unused_variables` lint and the `used_underscore_binding` lint.
337 ///
338 /// **Example**:
339 /// ```
340 /// let _x = 0;
341 /// let y = _x + 1; // Here we are using `_x`, even though it has a leading underscore.
342 ///                 // We should rename `_x` to `x`
343 /// ```
344 declare_lint!(pub USED_UNDERSCORE_BINDING, Warn,
345               "using a binding which is prefixed with an underscore");
346
347 #[derive(Copy, Clone)]
348 pub struct UsedUnderscoreBinding;
349
350 impl LintPass for UsedUnderscoreBinding {
351     fn get_lints(&self) -> LintArray {
352         lint_array!(USED_UNDERSCORE_BINDING)
353     }
354 }
355
356 impl LateLintPass for UsedUnderscoreBinding {
357     fn check_expr(&mut self, cx: &LateContext, expr: &Expr) {
358         let needs_lint = match expr.node {
359             ExprPath(_, ref path) => {
360                 let ident = path.segments.last()
361                                 .expect("path should always have at least one segment")
362                                 .identifier;
363                 ident.name.as_str().chars().next() == Some('_') //starts with '_'
364                 && ident.name.as_str().chars().skip(1).next() != Some('_') //doesn't start with "__"
365                 && ident.name != ident.unhygienic_name //not in macro
366                 && cx.tcx.def_map.borrow().values().any(|res| match res.base_def {
367                                                   Def::DefLocal(_, _) => true,
368                                                   _ => false
369                                             }) //local variable
370             },
371             ExprField(_, spanned) => spanned.node.as_str().chars().next() == Some('_'),
372             _ => false
373         };
374         if needs_lint {
375             cx.span_lint(USED_UNDERSCORE_BINDING, expr.span,
376                          "used binding which is prefixed with an underscore. A leading underscore\
377                           signals that a binding will not be used.");
378         }
379     }
380 }