]> git.lizzy.rs Git - rust.git/blob - clippy_lints/src/methods.rs
Run rustfmt
[rust.git] / clippy_lints / src / methods.rs
1 use rustc::hir;
2 use rustc::lint::*;
3 use rustc::middle::const_val::ConstVal;
4 use rustc::middle::const_qualif::ConstQualif;
5 use rustc::ty::subst::{Subst, TypeSpace};
6 use rustc::ty;
7 use rustc_const_eval::EvalHint::ExprTypeChecked;
8 use rustc_const_eval::eval_const_expr_partial;
9 use std::borrow::Cow;
10 use std::fmt;
11 use syntax::codemap::Span;
12 use syntax::ptr::P;
13 use utils::{get_trait_def_id, implements_trait, in_external_macro, in_macro, match_path, match_trait_method,
14             match_type, method_chain_args, return_ty, same_tys, snippet, snippet_opt, span_lint,
15             span_lint_and_then, span_note_and_lint, walk_ptrs_ty, walk_ptrs_ty_depth};
16 use utils::MethodArgs;
17 use utils::paths;
18
19 #[derive(Clone)]
20 pub struct MethodsPass;
21
22 /// **What it does:** This lint checks for `.unwrap()` calls on `Option`s.
23 ///
24 /// **Why is this bad?** Usually it is better to handle the `None` case, or to at least call `.expect(_)` with a more helpful message. Still, for a lot of quick-and-dirty code, `unwrap` is a good choice, which is why this lint is `Allow` by default.
25 ///
26 /// **Known problems:** None
27 ///
28 /// **Example:** `x.unwrap()`
29 declare_lint! {
30     pub OPTION_UNWRAP_USED, Allow,
31     "using `Option.unwrap()`, which should at least get a better message using `expect()`"
32 }
33
34 /// **What it does:** This lint checks for `.unwrap()` calls on `Result`s.
35 ///
36 /// **Why is this bad?** `result.unwrap()` will let the thread panic on `Err` values. Normally, you want to implement more sophisticated error handling, and propagate errors upwards with `try!`.
37 ///
38 /// Even if you want to panic on errors, not all `Error`s implement good messages on display. Therefore it may be beneficial to look at the places where they may get displayed. Activate this lint to do just that.
39 ///
40 /// **Known problems:** None
41 ///
42 /// **Example:** `x.unwrap()`
43 declare_lint! {
44     pub RESULT_UNWRAP_USED, Allow,
45     "using `Result.unwrap()`, which might be better handled"
46 }
47
48 /// **What it does:** This lint checks for methods that should live in a trait implementation of a `std` trait (see [llogiq's blog post](http://llogiq.github.io/2015/07/30/traits.html) for further information) instead of an inherent implementation.
49 ///
50 /// **Why is this bad?** Implementing the traits improve ergonomics for users of the code, often with very little cost. Also people seeing a `mul(..)` method may expect `*` to work equally, so you should have good reason to disappoint them.
51 ///
52 /// **Known problems:** None
53 ///
54 /// **Example:**
55 /// ```
56 /// struct X;
57 /// impl X {
58 ///    fn add(&self, other: &X) -> X { .. }
59 /// }
60 /// ```
61 declare_lint! {
62     pub SHOULD_IMPLEMENT_TRAIT, Warn,
63     "defining a method that should be implementing a std trait"
64 }
65
66 /// **What it does:** This lint checks for methods with certain name prefixes and which doesn't match how self is taken. The actual rules are:
67 ///
68 /// |Prefix |`self` taken        |
69 /// |-------|--------------------|
70 /// |`as_`  |`&self` or &mut self|
71 /// |`from_`| none               |
72 /// |`into_`|`self`              |
73 /// |`is_`  |`&self` or none     |
74 /// |`to_`  |`&self`             |
75 ///
76 /// **Why is this bad?** Consistency breeds readability. If you follow the conventions, your users won't be surprised that they e.g. need to supply a mutable reference to a `as_..` function.
77 ///
78 /// **Known problems:** None
79 ///
80 /// **Example**
81 ///
82 /// ```
83 /// impl X {
84 ///     fn as_str(self) -> &str { .. }
85 /// }
86 /// ```
87 declare_lint! {
88     pub WRONG_SELF_CONVENTION, Warn,
89     "defining a method named with an established prefix (like \"into_\") that takes \
90      `self` with the wrong convention"
91 }
92
93 /// **What it does:** This is the same as [`wrong_self_convention`](#wrong_self_convention), but for public items.
94 ///
95 /// **Why is this bad?** See [`wrong_self_convention`](#wrong_self_convention).
96 ///
97 /// **Known problems:** Actually *renaming* the function may break clients if the function is part of the public interface. In that case, be mindful of the stability guarantees you've given your users.
98 ///
99 /// **Example:**
100 /// ```
101 /// impl X {
102 ///     pub fn as_str(self) -> &str { .. }
103 /// }
104 /// ```
105 declare_lint! {
106     pub WRONG_PUB_SELF_CONVENTION, Allow,
107     "defining a public method named with an established prefix (like \"into_\") that takes \
108      `self` with the wrong convention"
109 }
110
111 /// **What it does:** This lint checks for usage of `ok().expect(..)`.
112 ///
113 /// **Why is this bad?** Because you usually call `expect()` on the `Result` directly to get a good error message.
114 ///
115 /// **Known problems:** None.
116 ///
117 /// **Example:** `x.ok().expect("why did I do this again?")`
118 declare_lint! {
119     pub OK_EXPECT, Warn,
120     "using `ok().expect()`, which gives worse error messages than \
121      calling `expect` directly on the Result"
122 }
123
124 /// **What it does:** This lint checks for usage of `_.map(_).unwrap_or(_)`.
125 ///
126 /// **Why is this bad?** Readability, this can be written more concisely as `_.map_or(_, _)`.
127 ///
128 /// **Known problems:** None.
129 ///
130 /// **Example:** `x.map(|a| a + 1).unwrap_or(0)`
131 declare_lint! {
132     pub OPTION_MAP_UNWRAP_OR, Warn,
133     "using `Option.map(f).unwrap_or(a)`, which is more succinctly expressed as \
134      `map_or(a, f)`"
135 }
136
137 /// **What it does:** This lint `Warn`s on `_.map(_).unwrap_or_else(_)`.
138 ///
139 /// **Why is this bad?** Readability, this can be written more concisely as `_.map_or_else(_, _)`.
140 ///
141 /// **Known problems:** None.
142 ///
143 /// **Example:** `x.map(|a| a + 1).unwrap_or_else(some_function)`
144 declare_lint! {
145     pub OPTION_MAP_UNWRAP_OR_ELSE, Warn,
146     "using `Option.map(f).unwrap_or_else(g)`, which is more succinctly expressed as \
147      `map_or_else(g, f)`"
148 }
149
150 /// **What it does:** This lint `Warn`s on `_.filter(_).next()`.
151 ///
152 /// **Why is this bad?** Readability, this can be written more concisely as `_.find(_)`.
153 ///
154 /// **Known problems:** None.
155 ///
156 /// **Example:** `iter.filter(|x| x == 0).next()`
157 declare_lint! {
158     pub FILTER_NEXT, Warn,
159     "using `filter(p).next()`, which is more succinctly expressed as `.find(p)`"
160 }
161
162 /// **What it does:** This lint `Warn`s on an iterator search (such as `find()`, `position()`, or
163 /// `rposition()`) followed by a call to `is_some()`.
164 ///
165 /// **Why is this bad?** Readability, this can be written more concisely as `_.any(_)`.
166 ///
167 /// **Known problems:** None.
168 ///
169 /// **Example:** `iter.find(|x| x == 0).is_some()`
170 declare_lint! {
171     pub SEARCH_IS_SOME, Warn,
172     "using an iterator search followed by `is_some()`, which is more succinctly \
173      expressed as a call to `any()`"
174 }
175
176 /// **What it does:** This lint `Warn`s on using `.chars().next()` on a `str` to check if it
177 /// starts with a given char.
178 ///
179 /// **Why is this bad?** Readability, this can be written more concisely as `_.starts_with(_)`.
180 ///
181 /// **Known problems:** None.
182 ///
183 /// **Example:** `name.chars().next() == Some('_')`
184 declare_lint! {
185     pub CHARS_NEXT_CMP, Warn,
186     "using `.chars().next()` to check if a string starts with a char"
187 }
188
189 /// **What it does:** This lint checks for calls to `.or(foo(..))`, `.unwrap_or(foo(..))`, etc., and
190 /// suggests to use `or_else`, `unwrap_or_else`, etc., or `unwrap_or_default` instead.
191 ///
192 /// **Why is this bad?** The function will always be called and potentially allocate an object
193 /// in expressions such as:
194 /// ```rust
195 /// foo.unwrap_or(String::new())
196 /// ```
197 /// this can instead be written:
198 /// ```rust
199 /// foo.unwrap_or_else(String::new)
200 /// ```
201 /// or
202 /// ```rust
203 /// foo.unwrap_or_default()
204 /// ```
205 ///
206 /// **Known problems:** If the function as side-effects, not calling it will change the semantic of
207 /// the program, but you shouldn't rely on that anyway.
208 declare_lint! {
209     pub OR_FUN_CALL, Warn,
210     "using any `*or` method when the `*or_else` would do"
211 }
212
213 /// **What it does:** This lint checks for usage of `.extend(s)` on a `Vec` to extend the vector by a slice.
214 ///
215 /// **Why is this bad?** Since Rust 1.6, the `extend_from_slice(_)` method is stable and at least for now faster.
216 ///
217 /// **Known problems:** None.
218 ///
219 /// **Example:** `my_vec.extend(&xs)`
220 declare_lint! {
221     pub EXTEND_FROM_SLICE, Warn,
222     "`.extend_from_slice(_)` is a faster way to extend a Vec by a slice"
223 }
224
225 /// **What it does:** This lint warns on using `.clone()` on a `Copy` type.
226 ///
227 /// **Why is this bad?** The only reason `Copy` types implement `Clone` is for generics, not for
228 /// using the `clone` method on a concrete type.
229 ///
230 /// **Known problems:** None.
231 ///
232 /// **Example:** `42u64.clone()`
233 declare_lint! {
234     pub CLONE_ON_COPY, Warn, "using `clone` on a `Copy` type"
235 }
236
237 /// **What it does:** This lint warns on using `.clone()` on an `&&T`
238 ///
239 /// **Why is this bad?** Cloning an `&&T` copies the inner `&T`, instead of cloning the underlying
240 /// `T`
241 ///
242 /// **Known problems:** None.
243 ///
244 /// **Example:**
245 /// ```rust
246 /// fn main() {
247 ///    let x = vec![1];
248 ///    let y = &&x;
249 ///    let z = y.clone();
250 ///    println!("{:p} {:p}",*y, z); // prints out the same pointer
251 /// }
252 /// ```
253 declare_lint! {
254     pub CLONE_DOUBLE_REF, Warn, "using `clone` on `&&T`"
255 }
256
257 /// **What it does:** This lint warns about `new` not returning `Self`.
258 ///
259 /// **Why is this bad?** As a convention, `new` methods are used to make a new instance of a type.
260 ///
261 /// **Known problems:** None.
262 ///
263 /// **Example:**
264 /// ```rust
265 /// impl Foo {
266 ///     fn new(..) -> NotAFoo {
267 ///     }
268 /// }
269 /// ```
270 declare_lint! {
271     pub NEW_RET_NO_SELF, Warn, "not returning `Self` in a `new` method"
272 }
273
274 /// **What it does:** This lint checks for string methods that receive a single-character `str` as an argument, e.g. `_.split("x")`.
275 ///
276 /// **Why is this bad?** Performing these methods using a `char` is faster than using a `str`.
277 ///
278 /// **Known problems:** Does not catch multi-byte unicode characters.
279 ///
280 /// **Example:** `_.split("x")` could be `_.split('x')`
281 declare_lint! {
282     pub SINGLE_CHAR_PATTERN,
283     Warn,
284     "using a single-character str where a char could be used, e.g. \
285      `_.split(\"x\")`"
286 }
287
288 /// **What it does:** This lint checks for getting the inner pointer of a temporary `CString`.
289 ///
290 /// **Why is this bad?** The inner pointer of a `CString` is only valid as long as the `CString` is
291 /// alive.
292 ///
293 /// **Known problems:** None.
294 ///
295 /// **Example:**
296 /// ```rust,ignore
297 /// let c_str = CString::new("foo").unwrap().as_ptr();
298 /// unsafe {
299 /// call_some_ffi_func(c_str);
300 /// }
301 /// ```
302 /// Here `c_str` point to a freed address. The correct use would be:
303 /// ```rust,ignore
304 /// let c_str = CString::new("foo").unwrap();
305 /// unsafe {
306 /// call_some_ffi_func(c_str.as_ptr());
307 /// }
308 /// ```
309 declare_lint! {
310     pub TEMPORARY_CSTRING_AS_PTR,
311     Warn,
312     "getting the inner pointer of a temporary `CString`"
313 }
314
315 impl LintPass for MethodsPass {
316     fn get_lints(&self) -> LintArray {
317         lint_array!(EXTEND_FROM_SLICE,
318                     OPTION_UNWRAP_USED,
319                     RESULT_UNWRAP_USED,
320                     SHOULD_IMPLEMENT_TRAIT,
321                     WRONG_SELF_CONVENTION,
322                     WRONG_PUB_SELF_CONVENTION,
323                     OK_EXPECT,
324                     OPTION_MAP_UNWRAP_OR,
325                     OPTION_MAP_UNWRAP_OR_ELSE,
326                     OR_FUN_CALL,
327                     CHARS_NEXT_CMP,
328                     CLONE_ON_COPY,
329                     CLONE_DOUBLE_REF,
330                     NEW_RET_NO_SELF,
331                     SINGLE_CHAR_PATTERN,
332                     SEARCH_IS_SOME,
333                     TEMPORARY_CSTRING_AS_PTR)
334     }
335 }
336
337 impl LateLintPass for MethodsPass {
338     fn check_expr(&mut self, cx: &LateContext, expr: &hir::Expr) {
339         if in_macro(cx, expr.span) {
340             return;
341         }
342
343         match expr.node {
344             hir::ExprMethodCall(name, _, ref args) => {
345                 // Chain calls
346                 if let Some(arglists) = method_chain_args(expr, &["unwrap"]) {
347                     lint_unwrap(cx, expr, arglists[0]);
348                 } else if let Some(arglists) = method_chain_args(expr, &["ok", "expect"]) {
349                     lint_ok_expect(cx, expr, arglists[0]);
350                 } else if let Some(arglists) = method_chain_args(expr, &["map", "unwrap_or"]) {
351                     lint_map_unwrap_or(cx, expr, arglists[0], arglists[1]);
352                 } else if let Some(arglists) = method_chain_args(expr, &["map", "unwrap_or_else"]) {
353                     lint_map_unwrap_or_else(cx, expr, arglists[0], arglists[1]);
354                 } else if let Some(arglists) = method_chain_args(expr, &["filter", "next"]) {
355                     lint_filter_next(cx, expr, arglists[0]);
356                 } else if let Some(arglists) = method_chain_args(expr, &["find", "is_some"]) {
357                     lint_search_is_some(cx, expr, "find", arglists[0], arglists[1]);
358                 } else if let Some(arglists) = method_chain_args(expr, &["position", "is_some"]) {
359                     lint_search_is_some(cx, expr, "position", arglists[0], arglists[1]);
360                 } else if let Some(arglists) = method_chain_args(expr, &["rposition", "is_some"]) {
361                     lint_search_is_some(cx, expr, "rposition", arglists[0], arglists[1]);
362                 } else if let Some(arglists) = method_chain_args(expr, &["extend"]) {
363                     lint_extend(cx, expr, arglists[0]);
364                 } else if let Some(arglists) = method_chain_args(expr, &["unwrap", "as_ptr"]) {
365                     lint_cstring_as_ptr(cx, expr, &arglists[0][0], &arglists[1][0]);
366                 }
367
368                 lint_or_fun_call(cx, expr, &name.node.as_str(), args);
369
370                 let self_ty = cx.tcx.expr_ty_adjusted(&args[0]);
371                 if args.len() == 1 && name.node.as_str() == "clone" {
372                     lint_clone_on_copy(cx, expr);
373                     lint_clone_double_ref(cx, expr, &args[0], self_ty);
374                 }
375
376                 match self_ty.sty {
377                     ty::TyRef(_, ty) if ty.ty.sty == ty::TyStr => {
378                         for &(method, pos) in &PATTERN_METHODS {
379                             if name.node.as_str() == method && args.len() > pos {
380                                 lint_single_char_pattern(cx, expr, &args[pos]);
381                             }
382                         }
383                     }
384                     _ => (),
385                 }
386             }
387             hir::ExprBinary(op, ref lhs, ref rhs) if op.node == hir::BiEq || op.node == hir::BiNe => {
388                 if !lint_chars_next(cx, expr, lhs, rhs, op.node == hir::BiEq) {
389                     lint_chars_next(cx, expr, rhs, lhs, op.node == hir::BiEq);
390                 }
391             }
392             _ => (),
393         }
394     }
395
396     fn check_item(&mut self, cx: &LateContext, item: &hir::Item) {
397         if in_external_macro(cx, item.span) {
398             return;
399         }
400
401         if let hir::ItemImpl(_, _, _, None, _, ref items) = item.node {
402             for implitem in items {
403                 let name = implitem.name;
404                 if_let_chain! {[
405                     let hir::ImplItemKind::Method(ref sig, _) = implitem.node,
406                     let Some(explicit_self) = sig.decl.inputs.get(0).and_then(hir::Arg::to_self),
407                 ], {
408                     // check missing trait implementations
409                     for &(method_name, n_args, self_kind, out_type, trait_name) in &TRAIT_METHODS {
410                         if name.as_str() == method_name &&
411                            sig.decl.inputs.len() == n_args &&
412                            out_type.matches(&sig.decl.output) &&
413                            self_kind.matches(&explicit_self, false) {
414                             span_lint(cx, SHOULD_IMPLEMENT_TRAIT, implitem.span, &format!(
415                                 "defining a method called `{}` on this type; consider implementing \
416                                  the `{}` trait or choosing a less ambiguous name", name, trait_name));
417                         }
418                     }
419
420                     // check conventions w.r.t. conversion method names and predicates
421                     let ty = cx.tcx.lookup_item_type(cx.tcx.map.local_def_id(item.id)).ty;
422                     let is_copy = is_copy(cx, ty, item);
423                     for &(ref conv, self_kinds) in &CONVENTIONS {
424                         if_let_chain! {[
425                             conv.check(&name.as_str()),
426                             let Some(explicit_self) = sig.decl.inputs.get(0).and_then(hir::Arg::to_self),
427                             !self_kinds.iter().any(|k| k.matches(&explicit_self, is_copy)),
428                         ], {
429                             let lint = if item.vis == hir::Visibility::Public {
430                                 WRONG_PUB_SELF_CONVENTION
431                             } else {
432                                 WRONG_SELF_CONVENTION
433                             };
434                             span_lint(cx,
435                                       lint,
436                                       explicit_self.span,
437                                       &format!("methods called `{}` usually take {}; consider choosing a less \
438                                                 ambiguous name",
439                                                conv,
440                                                &self_kinds.iter()
441                                                           .map(|k| k.description())
442                                                           .collect::<Vec<_>>()
443                                                           .join(" or ")));
444                         }}
445                     }
446
447                     let ret_ty = return_ty(cx, implitem.id);
448                     if &name.as_str() == &"new" &&
449                        !ret_ty.map_or(false, |ret_ty| ret_ty.walk().any(|t| same_tys(cx, t, ty, implitem.id))) {
450                         span_lint(cx,
451                                   NEW_RET_NO_SELF,
452                                   explicit_self.span,
453                                   "methods called `new` usually return `Self`");
454                     }
455                 }}
456             }
457         }
458     }
459 }
460
461 /// Checks for the `OR_FUN_CALL` lint.
462 fn lint_or_fun_call(cx: &LateContext, expr: &hir::Expr, name: &str, args: &[P<hir::Expr>]) {
463     /// Check for `unwrap_or(T::new())` or `unwrap_or(T::default())`.
464     fn check_unwrap_or_default(cx: &LateContext, name: &str, fun: &hir::Expr, self_expr: &hir::Expr, arg: &hir::Expr,
465                                or_has_args: bool, span: Span)
466                                -> bool {
467         if or_has_args {
468             return false;
469         }
470
471         if name == "unwrap_or" {
472             if let hir::ExprPath(_, ref path) = fun.node {
473                 let path: &str = &path.segments
474                                       .last()
475                                       .expect("A path must have at least one segment")
476                                       .name
477                                       .as_str();
478
479                 if ["default", "new"].contains(&path) {
480                     let arg_ty = cx.tcx.expr_ty(arg);
481                     let default_trait_id = if let Some(default_trait_id) = get_trait_def_id(cx, &paths::DEFAULT_TRAIT) {
482                         default_trait_id
483                     } else {
484                         return false;
485                     };
486
487                     if implements_trait(cx, arg_ty, default_trait_id, Vec::new()) {
488                         span_lint(cx,
489                                   OR_FUN_CALL,
490                                   span,
491                                   &format!("use of `{}` followed by a call to `{}`", name, path))
492                             .span_suggestion(span,
493                                              "try this",
494                                              format!("{}.unwrap_or_default()", snippet(cx, self_expr.span, "_")));
495                         return true;
496                     }
497                 }
498             }
499         }
500
501         false
502     }
503
504     /// Check for `*or(foo())`.
505     fn check_general_case(cx: &LateContext, name: &str, fun: &hir::Expr, self_expr: &hir::Expr, arg: &hir::Expr, or_has_args: bool,
506                           span: Span) {
507         // don't lint for constant values
508         // FIXME: can we `expect` here instead of match?
509         if let Some(qualif) = cx.tcx.const_qualif_map.borrow().get(&arg.id) {
510             if !qualif.contains(ConstQualif::NOT_CONST) {
511                 return;
512             }
513         }
514         // (path, fn_has_argument, methods, suffix)
515         let know_types: &[(&[_], _, &[_], _)] = &[(&paths::BTREEMAP_ENTRY, false, &["or_insert"], "with"),
516                                                   (&paths::HASHMAP_ENTRY, false, &["or_insert"], "with"),
517                                                   (&paths::OPTION,
518                                                    false,
519                                                    &["map_or", "ok_or", "or", "unwrap_or"],
520                                                    "else"),
521                                                   (&paths::RESULT, true, &["or", "unwrap_or"], "else")];
522
523         let self_ty = cx.tcx.expr_ty(self_expr);
524
525         let (fn_has_arguments, poss, suffix) = if let Some(&(_, fn_has_arguments, poss, suffix)) =
526                                                       know_types.iter().find(|&&i| match_type(cx, self_ty, i.0)) {
527             (fn_has_arguments, poss, suffix)
528         } else {
529             return;
530         };
531
532         if !poss.contains(&name) {
533             return;
534         }
535
536         let sugg: Cow<_> = match (fn_has_arguments, !or_has_args) {
537             (true, _) => format!("|_| {}", snippet(cx, arg.span, "..")).into(),
538             (false, false) => format!("|| {}", snippet(cx, arg.span, "..")).into(),
539             (false, true) => snippet(cx, fun.span, ".."),
540         };
541
542         span_lint(cx, OR_FUN_CALL, span, &format!("use of `{}` followed by a function call", name))
543             .span_suggestion(span,
544                              "try this",
545                              format!("{}.{}_{}({})", snippet(cx, self_expr.span, "_"), name, suffix, sugg));
546     }
547
548     if args.len() == 2 {
549         if let hir::ExprCall(ref fun, ref or_args) = args[1].node {
550             let or_has_args = !or_args.is_empty();
551             if !check_unwrap_or_default(cx, name, fun, &args[0], &args[1], or_has_args, expr.span) {
552                 check_general_case(cx, name, fun, &args[0], &args[1], or_has_args, expr.span);
553             }
554         }
555     }
556 }
557
558 /// Checks for the `CLONE_ON_COPY` lint.
559 fn lint_clone_on_copy(cx: &LateContext, expr: &hir::Expr) {
560     let ty = cx.tcx.expr_ty(expr);
561     let parent = cx.tcx.map.get_parent(expr.id);
562     let parameter_environment = ty::ParameterEnvironment::for_item(cx.tcx, parent);
563
564     if !ty.moves_by_default(cx.tcx.global_tcx(), &parameter_environment, expr.span) {
565         span_lint(cx, CLONE_ON_COPY, expr.span, "using `clone` on a `Copy` type");
566     }
567 }
568
569 /// Checks for the `CLONE_DOUBLE_REF` lint.
570 fn lint_clone_double_ref(cx: &LateContext, expr: &hir::Expr, arg: &hir::Expr, ty: ty::Ty) {
571     if let ty::TyRef(_, ty::TypeAndMut { ty: ref inner, .. }) = ty.sty {
572         if let ty::TyRef(..) = inner.sty {
573             let mut db = span_lint(cx,
574                                    CLONE_DOUBLE_REF,
575                                    expr.span,
576                                    "using `clone` on a double-reference; \
577                                     this will copy the reference instead of cloning \
578                                     the inner type");
579             if let Some(snip) = snippet_opt(cx, arg.span) {
580                 db.span_suggestion(expr.span, "try dereferencing it", format!("(*{}).clone()", snip));
581             }
582         }
583     }
584 }
585
586 fn lint_extend(cx: &LateContext, expr: &hir::Expr, args: &MethodArgs) {
587     let (obj_ty, _) = walk_ptrs_ty_depth(cx.tcx.expr_ty(&args[0]));
588     if !match_type(cx, obj_ty, &paths::VEC) {
589         return;
590     }
591     let arg_ty = cx.tcx.expr_ty(&args[1]);
592     if let Some((span, r)) = derefs_to_slice(cx, &args[1], &arg_ty) {
593         span_lint(cx, EXTEND_FROM_SLICE, expr.span, "use of `extend` to extend a Vec by a slice")
594             .span_suggestion(expr.span,
595                              "try this",
596                              format!("{}.extend_from_slice({}{})",
597                                      snippet(cx, args[0].span, "_"),
598                                      r,
599                                      snippet(cx, span, "_")));
600     }
601 }
602
603 fn lint_cstring_as_ptr(cx: &LateContext, expr: &hir::Expr, new: &hir::Expr, unwrap: &hir::Expr) {
604     if_let_chain!{[
605         let hir::ExprCall(ref fun, ref args) = new.node,
606         args.len() == 1,
607         let hir::ExprPath(None, ref path) = fun.node,
608         match_path(path, &paths::CSTRING_NEW),
609     ], {
610         span_lint_and_then(cx, TEMPORARY_CSTRING_AS_PTR, expr.span,
611                            "you are getting the inner pointer of a temporary `CString`",
612                            |db| {
613                                db.note("that pointer will be invalid outside this expression");
614                                db.span_help(unwrap.span, "assign the `CString` to a variable to extend its lifetime");
615                            });
616     }}
617 }
618
619 fn derefs_to_slice(cx: &LateContext, expr: &hir::Expr, ty: &ty::Ty) -> Option<(Span, &'static str)> {
620     fn may_slice(cx: &LateContext, ty: &ty::Ty) -> bool {
621         match ty.sty {
622             ty::TySlice(_) => true,
623             ty::TyStruct(..) => match_type(cx, ty, &paths::VEC),
624             ty::TyArray(_, size) => size < 32,
625             ty::TyRef(_, ty::TypeAndMut { ty: ref inner, .. }) |
626             ty::TyBox(ref inner) => may_slice(cx, inner),
627             _ => false,
628         }
629     }
630     if let hir::ExprMethodCall(name, _, ref args) = expr.node {
631         if &name.node.as_str() == &"iter" && may_slice(cx, &cx.tcx.expr_ty(&args[0])) {
632             Some((args[0].span, "&"))
633         } else {
634             None
635         }
636     } else {
637         match ty.sty {
638             ty::TySlice(_) => Some((expr.span, "")),
639             ty::TyRef(_, ty::TypeAndMut { ty: ref inner, .. }) |
640             ty::TyBox(ref inner) => {
641                 if may_slice(cx, inner) {
642                     Some((expr.span, ""))
643                 } else {
644                     None
645                 }
646             }
647             _ => None,
648         }
649     }
650 }
651
652 #[allow(ptr_arg)]
653 // Type of MethodArgs is potentially a Vec
654 /// lint use of `unwrap()` for `Option`s and `Result`s
655 fn lint_unwrap(cx: &LateContext, expr: &hir::Expr, unwrap_args: &MethodArgs) {
656     let (obj_ty, _) = walk_ptrs_ty_depth(cx.tcx.expr_ty(&unwrap_args[0]));
657
658     let mess = if match_type(cx, obj_ty, &paths::OPTION) {
659         Some((OPTION_UNWRAP_USED, "an Option", "None"))
660     } else if match_type(cx, obj_ty, &paths::RESULT) {
661         Some((RESULT_UNWRAP_USED, "a Result", "Err"))
662     } else {
663         None
664     };
665
666     if let Some((lint, kind, none_value)) = mess {
667         span_lint(cx,
668                   lint,
669                   expr.span,
670                   &format!("used unwrap() on {} value. If you don't want to handle the {} case gracefully, consider \
671                             using expect() to provide a better panic
672                             message",
673                            kind,
674                            none_value));
675     }
676 }
677
678 #[allow(ptr_arg)]
679 // Type of MethodArgs is potentially a Vec
680 /// lint use of `ok().expect()` for `Result`s
681 fn lint_ok_expect(cx: &LateContext, expr: &hir::Expr, ok_args: &MethodArgs) {
682     // lint if the caller of `ok()` is a `Result`
683     if match_type(cx, cx.tcx.expr_ty(&ok_args[0]), &paths::RESULT) {
684         let result_type = cx.tcx.expr_ty(&ok_args[0]);
685         if let Some(error_type) = get_error_type(cx, result_type) {
686             if has_debug_impl(error_type, cx) {
687                 span_lint(cx,
688                           OK_EXPECT,
689                           expr.span,
690                           "called `ok().expect()` on a Result value. You can call `expect` directly on the `Result`");
691             }
692         }
693     }
694 }
695
696 #[allow(ptr_arg)]
697 // Type of MethodArgs is potentially a Vec
698 /// lint use of `map().unwrap_or()` for `Option`s
699 fn lint_map_unwrap_or(cx: &LateContext, expr: &hir::Expr, map_args: &MethodArgs, unwrap_args: &MethodArgs) {
700     // lint if the caller of `map()` is an `Option`
701     if match_type(cx, cx.tcx.expr_ty(&map_args[0]), &paths::OPTION) {
702         // lint message
703         let msg = "called `map(f).unwrap_or(a)` on an Option value. This can be done more directly by calling \
704                    `map_or(a, f)` instead";
705         // get snippets for args to map() and unwrap_or()
706         let map_snippet = snippet(cx, map_args[1].span, "..");
707         let unwrap_snippet = snippet(cx, unwrap_args[1].span, "..");
708         // lint, with note if neither arg is > 1 line and both map() and
709         // unwrap_or() have the same span
710         let multiline = map_snippet.lines().count() > 1 || unwrap_snippet.lines().count() > 1;
711         let same_span = map_args[1].span.expn_id == unwrap_args[1].span.expn_id;
712         if same_span && !multiline {
713             span_note_and_lint(cx,
714                                OPTION_MAP_UNWRAP_OR,
715                                expr.span,
716                                msg,
717                                expr.span,
718                                &format!("replace `map({0}).unwrap_or({1})` with `map_or({1}, {0})`",
719                                         map_snippet,
720                                         unwrap_snippet));
721         } else if same_span && multiline {
722             span_lint(cx, OPTION_MAP_UNWRAP_OR, expr.span, msg);
723         };
724     }
725 }
726
727 #[allow(ptr_arg)]
728 // Type of MethodArgs is potentially a Vec
729 /// lint use of `map().unwrap_or_else()` for `Option`s
730 fn lint_map_unwrap_or_else(cx: &LateContext, expr: &hir::Expr, map_args: &MethodArgs, unwrap_args: &MethodArgs) {
731     // lint if the caller of `map()` is an `Option`
732     if match_type(cx, cx.tcx.expr_ty(&map_args[0]), &paths::OPTION) {
733         // lint message
734         let msg = "called `map(f).unwrap_or_else(g)` on an Option value. This can be done more directly by calling \
735                    `map_or_else(g, f)` instead";
736         // get snippets for args to map() and unwrap_or_else()
737         let map_snippet = snippet(cx, map_args[1].span, "..");
738         let unwrap_snippet = snippet(cx, unwrap_args[1].span, "..");
739         // lint, with note if neither arg is > 1 line and both map() and
740         // unwrap_or_else() have the same span
741         let multiline = map_snippet.lines().count() > 1 || unwrap_snippet.lines().count() > 1;
742         let same_span = map_args[1].span.expn_id == unwrap_args[1].span.expn_id;
743         if same_span && !multiline {
744             span_note_and_lint(cx,
745                                OPTION_MAP_UNWRAP_OR_ELSE,
746                                expr.span,
747                                msg,
748                                expr.span,
749                                &format!("replace `map({0}).unwrap_or_else({1})` with `with map_or_else({1}, {0})`",
750                                         map_snippet,
751                                         unwrap_snippet));
752         } else if same_span && multiline {
753             span_lint(cx, OPTION_MAP_UNWRAP_OR_ELSE, expr.span, msg);
754         };
755     }
756 }
757
758 #[allow(ptr_arg)]
759 // Type of MethodArgs is potentially a Vec
760 /// lint use of `filter().next() for Iterators`
761 fn lint_filter_next(cx: &LateContext, expr: &hir::Expr, filter_args: &MethodArgs) {
762     // lint if caller of `.filter().next()` is an Iterator
763     if match_trait_method(cx, expr, &paths::ITERATOR) {
764         let msg = "called `filter(p).next()` on an Iterator. This is more succinctly expressed by calling `.find(p)` \
765                    instead.";
766         let filter_snippet = snippet(cx, filter_args[1].span, "..");
767         if filter_snippet.lines().count() <= 1 {
768             // add note if not multi-line
769             span_note_and_lint(cx,
770                                FILTER_NEXT,
771                                expr.span,
772                                msg,
773                                expr.span,
774                                &format!("replace `filter({0}).next()` with `find({0})`", filter_snippet));
775         } else {
776             span_lint(cx, FILTER_NEXT, expr.span, msg);
777         }
778     }
779 }
780
781 #[allow(ptr_arg)]
782 // Type of MethodArgs is potentially a Vec
783 /// lint searching an Iterator followed by `is_some()`
784 fn lint_search_is_some(cx: &LateContext, expr: &hir::Expr, search_method: &str, search_args: &MethodArgs,
785                        is_some_args: &MethodArgs) {
786     // lint if caller of search is an Iterator
787     if match_trait_method(cx, &*is_some_args[0], &paths::ITERATOR) {
788         let msg = format!("called `is_some()` after searching an iterator with {}. This is more succinctly expressed \
789                            by calling `any()`.",
790                           search_method);
791         let search_snippet = snippet(cx, search_args[1].span, "..");
792         if search_snippet.lines().count() <= 1 {
793             // add note if not multi-line
794             span_note_and_lint(cx,
795                                SEARCH_IS_SOME,
796                                expr.span,
797                                &msg,
798                                expr.span,
799                                &format!("replace `{0}({1}).is_some()` with `any({1})`", search_method, search_snippet));
800         } else {
801             span_lint(cx, SEARCH_IS_SOME, expr.span, &msg);
802         }
803     }
804 }
805
806 /// Checks for the `CHARS_NEXT_CMP` lint.
807 fn lint_chars_next(cx: &LateContext, expr: &hir::Expr, chain: &hir::Expr, other: &hir::Expr, eq: bool) -> bool {
808     if_let_chain! {[
809         let Some(args) = method_chain_args(chain, &["chars", "next"]),
810         let hir::ExprCall(ref fun, ref arg_char) = other.node,
811         arg_char.len() == 1,
812         let hir::ExprPath(None, ref path) = fun.node,
813         path.segments.len() == 1 && path.segments[0].name.as_str() == "Some"
814     ], {
815         let self_ty = walk_ptrs_ty(cx.tcx.expr_ty_adjusted(&args[0][0]));
816
817         if self_ty.sty != ty::TyStr {
818             return false;
819         }
820
821         span_lint_and_then(cx,
822                            CHARS_NEXT_CMP,
823                            expr.span,
824                            "you should use the `starts_with` method",
825                            |db| {
826                                let sugg = format!("{}{}.starts_with({})",
827                                                   if eq { "" } else { "!" },
828                                                   snippet(cx, args[0][0].span, "_"),
829                                                   snippet(cx, arg_char[0].span, "_")
830                                                   );
831
832                                db.span_suggestion(expr.span, "like this", sugg);
833                            });
834
835         return true;
836     }}
837
838     false
839 }
840
841 /// lint for length-1 `str`s for methods in `PATTERN_METHODS`
842 fn lint_single_char_pattern(cx: &LateContext, expr: &hir::Expr, arg: &hir::Expr) {
843     if let Ok(ConstVal::Str(r)) = eval_const_expr_partial(cx.tcx, arg, ExprTypeChecked, None) {
844         if r.len() == 1 {
845             let hint = snippet(cx, expr.span, "..").replace(&format!("\"{}\"", r), &format!("'{}'", r));
846             span_lint_and_then(cx,
847                                SINGLE_CHAR_PATTERN,
848                                arg.span,
849                                "single-character string constant used as pattern",
850                                |db| {
851                                    db.span_suggestion(expr.span, "try using a char instead:", hint);
852                                });
853         }
854     }
855 }
856
857 /// Given a `Result<T, E>` type, return its error type (`E`).
858 fn get_error_type<'a>(cx: &LateContext, ty: ty::Ty<'a>) -> Option<ty::Ty<'a>> {
859     if !match_type(cx, ty, &paths::RESULT) {
860         return None;
861     }
862     if let ty::TyEnum(_, substs) = ty.sty {
863         if let Some(err_ty) = substs.types.opt_get(TypeSpace, 1) {
864             return Some(err_ty);
865         }
866     }
867     None
868 }
869
870 /// This checks whether a given type is known to implement Debug.
871 fn has_debug_impl<'a, 'b>(ty: ty::Ty<'a>, cx: &LateContext<'b, 'a>) -> bool {
872     match cx.tcx.lang_items.debug_trait() {
873         Some(debug) => implements_trait(cx, ty, debug, Vec::new()),
874         None => false,
875     }
876 }
877
878 enum Convention {
879     Eq(&'static str),
880     StartsWith(&'static str),
881 }
882
883 #[cfg_attr(rustfmt, rustfmt_skip)]
884 const CONVENTIONS: [(Convention, &'static [SelfKind]); 6] = [
885     (Convention::Eq("new"), &[SelfKind::No]),
886     (Convention::StartsWith("as_"), &[SelfKind::Ref, SelfKind::RefMut]),
887     (Convention::StartsWith("from_"), &[SelfKind::No]),
888     (Convention::StartsWith("into_"), &[SelfKind::Value]),
889     (Convention::StartsWith("is_"), &[SelfKind::Ref, SelfKind::No]),
890     (Convention::StartsWith("to_"), &[SelfKind::Ref]),
891 ];
892
893 #[cfg_attr(rustfmt, rustfmt_skip)]
894 const TRAIT_METHODS: [(&'static str, usize, SelfKind, OutType, &'static str); 30] = [
895     ("add", 2, SelfKind::Value, OutType::Any, "std::ops::Add"),
896     ("as_mut", 1, SelfKind::RefMut, OutType::Ref, "std::convert::AsMut"),
897     ("as_ref", 1, SelfKind::Ref, OutType::Ref, "std::convert::AsRef"),
898     ("bitand", 2, SelfKind::Value, OutType::Any, "std::ops::BitAnd"),
899     ("bitor", 2, SelfKind::Value, OutType::Any, "std::ops::BitOr"),
900     ("bitxor", 2, SelfKind::Value, OutType::Any, "std::ops::BitXor"),
901     ("borrow", 1, SelfKind::Ref, OutType::Ref, "std::borrow::Borrow"),
902     ("borrow_mut", 1, SelfKind::RefMut, OutType::Ref, "std::borrow::BorrowMut"),
903     ("clone", 1, SelfKind::Ref, OutType::Any, "std::clone::Clone"),
904     ("cmp", 2, SelfKind::Ref, OutType::Any, "std::cmp::Ord"),
905     ("default", 0, SelfKind::No, OutType::Any, "std::default::Default"),
906     ("deref", 1, SelfKind::Ref, OutType::Ref, "std::ops::Deref"),
907     ("deref_mut", 1, SelfKind::RefMut, OutType::Ref, "std::ops::DerefMut"),
908     ("div", 2, SelfKind::Value, OutType::Any, "std::ops::Div"),
909     ("drop", 1, SelfKind::RefMut, OutType::Unit, "std::ops::Drop"),
910     ("eq", 2, SelfKind::Ref, OutType::Bool, "std::cmp::PartialEq"),
911     ("from_iter", 1, SelfKind::No, OutType::Any, "std::iter::FromIterator"),
912     ("from_str", 1, SelfKind::No, OutType::Any, "std::str::FromStr"),
913     ("hash", 2, SelfKind::Ref, OutType::Unit, "std::hash::Hash"),
914     ("index", 2, SelfKind::Ref, OutType::Ref, "std::ops::Index"),
915     ("index_mut", 2, SelfKind::RefMut, OutType::Ref, "std::ops::IndexMut"),
916     ("into_iter", 1, SelfKind::Value, OutType::Any, "std::iter::IntoIterator"),
917     ("mul", 2, SelfKind::Value, OutType::Any, "std::ops::Mul"),
918     ("neg", 1, SelfKind::Value, OutType::Any, "std::ops::Neg"),
919     ("next", 1, SelfKind::RefMut, OutType::Any, "std::iter::Iterator"),
920     ("not", 1, SelfKind::Value, OutType::Any, "std::ops::Not"),
921     ("rem", 2, SelfKind::Value, OutType::Any, "std::ops::Rem"),
922     ("shl", 2, SelfKind::Value, OutType::Any, "std::ops::Shl"),
923     ("shr", 2, SelfKind::Value, OutType::Any, "std::ops::Shr"),
924     ("sub", 2, SelfKind::Value, OutType::Any, "std::ops::Sub"),
925 ];
926
927 #[cfg_attr(rustfmt, rustfmt_skip)]
928 const PATTERN_METHODS: [(&'static str, usize); 17] = [
929     ("contains", 1),
930     ("starts_with", 1),
931     ("ends_with", 1),
932     ("find", 1),
933     ("rfind", 1),
934     ("split", 1),
935     ("rsplit", 1),
936     ("split_terminator", 1),
937     ("rsplit_terminator", 1),
938     ("splitn", 2),
939     ("rsplitn", 2),
940     ("matches", 1),
941     ("rmatches", 1),
942     ("match_indices", 1),
943     ("rmatch_indices", 1),
944     ("trim_left_matches", 1),
945     ("trim_right_matches", 1),
946 ];
947
948
949 #[derive(Clone, Copy)]
950 enum SelfKind {
951     Value,
952     Ref,
953     RefMut,
954     No,
955 }
956
957 impl SelfKind {
958     fn matches(self, slf: &hir::ExplicitSelf, allow_value_for_ref: bool) -> bool {
959         match (self, &slf.node) {
960             (SelfKind::Value, &hir::SelfKind::Value(_)) |
961             (SelfKind::Ref, &hir::SelfKind::Region(_, hir::Mutability::MutImmutable)) |
962             (SelfKind::RefMut, &hir::SelfKind::Region(_, hir::Mutability::MutMutable)) => true,
963             (SelfKind::Ref, &hir::SelfKind::Value(_)) |
964             (SelfKind::RefMut, &hir::SelfKind::Value(_)) => allow_value_for_ref,
965             (_, &hir::SelfKind::Explicit(ref ty, _)) => self.matches_explicit_type(ty, allow_value_for_ref),
966
967             _ => false,
968         }
969     }
970
971     fn matches_explicit_type(self, ty: &hir::Ty, allow_value_for_ref: bool) -> bool {
972         match (self, &ty.node) {
973             (SelfKind::Value, &hir::TyPath(..)) |
974             (SelfKind::Ref, &hir::TyRptr(_, hir::MutTy { mutbl: hir::Mutability::MutImmutable, .. })) |
975             (SelfKind::RefMut, &hir::TyRptr(_, hir::MutTy { mutbl: hir::Mutability::MutMutable, .. })) => true,
976             (SelfKind::Ref, &hir::TyPath(..)) |
977             (SelfKind::RefMut, &hir::TyPath(..)) => allow_value_for_ref,
978             _ => false,
979         }
980     }
981
982     fn description(&self) -> &'static str {
983         match *self {
984             SelfKind::Value => "self by value",
985             SelfKind::Ref => "self by reference",
986             SelfKind::RefMut => "self by mutable reference",
987             SelfKind::No => "no self",
988         }
989     }
990 }
991
992 impl Convention {
993     fn check(&self, other: &str) -> bool {
994         match *self {
995             Convention::Eq(this) => this == other,
996             Convention::StartsWith(this) => other.starts_with(this),
997         }
998     }
999 }
1000
1001 impl fmt::Display for Convention {
1002     fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
1003         match *self {
1004             Convention::Eq(this) => this.fmt(f),
1005             Convention::StartsWith(this) => this.fmt(f).and_then(|_| '*'.fmt(f)),
1006         }
1007     }
1008 }
1009
1010 #[derive(Clone, Copy)]
1011 enum OutType {
1012     Unit,
1013     Bool,
1014     Any,
1015     Ref,
1016 }
1017
1018 impl OutType {
1019     fn matches(&self, ty: &hir::FunctionRetTy) -> bool {
1020         match (self, ty) {
1021             (&OutType::Unit, &hir::DefaultReturn(_)) => true,
1022             (&OutType::Unit, &hir::Return(ref ty)) if ty.node == hir::TyTup(vec![].into()) => true,
1023             (&OutType::Bool, &hir::Return(ref ty)) if is_bool(ty) => true,
1024             (&OutType::Any, &hir::Return(ref ty)) if ty.node != hir::TyTup(vec![].into()) => true,
1025             (&OutType::Ref, &hir::Return(ref ty)) => matches!(ty.node, hir::TyRptr(_, _)),
1026             _ => false,
1027         }
1028     }
1029 }
1030
1031 fn is_bool(ty: &hir::Ty) -> bool {
1032     if let hir::TyPath(None, ref p) = ty.node {
1033         match_path(p, &["bool"])
1034     } else {
1035         false
1036     }
1037 }
1038
1039 fn is_copy<'a, 'ctx>(cx: &LateContext<'a, 'ctx>, ty: ty::Ty<'ctx>, item: &hir::Item) -> bool {
1040     let env = ty::ParameterEnvironment::for_item(cx.tcx, item.id);
1041     !ty.subst(cx.tcx, env.free_substs).moves_by_default(cx.tcx.global_tcx(), &env, item.span)
1042 }