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