]> git.lizzy.rs Git - rust.git/blob - clippy_lints/src/identity_conversion.rs
Auto merge of #3597 - xfix:match-ergonomics, r=phansch
[rust.git] / clippy_lints / src / identity_conversion.rs
1 // Copyright 2014-2018 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution.
3 //
4 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
5 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
6 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
7 // option. This file may not be copied, modified, or distributed
8 // except according to those terms.
9
10 use crate::utils::{
11     in_macro, match_def_path, match_trait_method, same_tys, snippet, snippet_with_macro_callsite, span_lint_and_then,
12 };
13 use crate::utils::{opt_def_id, paths, resolve_node};
14 use rustc::hir::*;
15 use rustc::lint::{LateContext, LateLintPass, LintArray, LintPass};
16 use rustc::{declare_tool_lint, lint_array};
17 use rustc_errors::Applicability;
18 use syntax::ast::NodeId;
19
20 /// **What it does:** Checks for always-identical `Into`/`From`/`IntoIter` conversions.
21 ///
22 /// **Why is this bad?** Redundant code.
23 ///
24 /// **Known problems:** None.
25 ///
26 /// **Example:**
27 /// ```rust
28 /// // format!() returns a `String`
29 /// let s: String = format!("hello").into();
30 /// ```
31 declare_clippy_lint! {
32     pub IDENTITY_CONVERSION,
33     complexity,
34     "using always-identical `Into`/`From`/`IntoIter` conversions"
35 }
36
37 #[derive(Default)]
38 pub struct IdentityConversion {
39     try_desugar_arm: Vec<NodeId>,
40 }
41
42 impl LintPass for IdentityConversion {
43     fn get_lints(&self) -> LintArray {
44         lint_array!(IDENTITY_CONVERSION)
45     }
46 }
47
48 impl<'a, 'tcx> LateLintPass<'a, 'tcx> for IdentityConversion {
49     fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, e: &'tcx Expr) {
50         if in_macro(e.span) {
51             return;
52         }
53
54         if Some(&e.id) == self.try_desugar_arm.last() {
55             return;
56         }
57
58         match e.node {
59             ExprKind::Match(_, ref arms, MatchSource::TryDesugar) => {
60                 let e = match arms[0].body.node {
61                     ExprKind::Ret(Some(ref e)) | ExprKind::Break(_, Some(ref e)) => e,
62                     _ => return,
63                 };
64                 if let ExprKind::Call(_, ref args) = e.node {
65                     self.try_desugar_arm.push(args[0].id);
66                 } else {
67                     return;
68                 }
69             },
70
71             ExprKind::MethodCall(ref name, .., ref args) => {
72                 if match_trait_method(cx, e, &paths::INTO[..]) && &*name.ident.as_str() == "into" {
73                     let a = cx.tables.expr_ty(e);
74                     let b = cx.tables.expr_ty(&args[0]);
75                     if same_tys(cx, a, b) {
76                         let sugg = snippet_with_macro_callsite(cx, args[0].span, "<expr>").to_string();
77
78                         span_lint_and_then(cx, IDENTITY_CONVERSION, e.span, "identical conversion", |db| {
79                             db.span_suggestion_with_applicability(
80                                 e.span,
81                                 "consider removing `.into()`",
82                                 sugg,
83                                 Applicability::MachineApplicable, // snippet
84                             );
85                         });
86                     }
87                 }
88                 if match_trait_method(cx, e, &paths::INTO_ITERATOR) && &*name.ident.as_str() == "into_iter" {
89                     let a = cx.tables.expr_ty(e);
90                     let b = cx.tables.expr_ty(&args[0]);
91                     if same_tys(cx, a, b) {
92                         let sugg = snippet(cx, args[0].span, "<expr>").into_owned();
93                         span_lint_and_then(cx, IDENTITY_CONVERSION, e.span, "identical conversion", |db| {
94                             db.span_suggestion_with_applicability(
95                                 e.span,
96                                 "consider removing `.into_iter()`",
97                                 sugg,
98                                 Applicability::MachineApplicable, // snippet
99                             );
100                         });
101                     }
102                 }
103             },
104
105             ExprKind::Call(ref path, ref args) => {
106                 if let ExprKind::Path(ref qpath) = path.node {
107                     if let Some(def_id) = opt_def_id(resolve_node(cx, qpath, path.hir_id)) {
108                         if match_def_path(cx.tcx, def_id, &paths::FROM_FROM[..]) {
109                             let a = cx.tables.expr_ty(e);
110                             let b = cx.tables.expr_ty(&args[0]);
111                             if same_tys(cx, a, b) {
112                                 let sugg = snippet(cx, args[0].span.source_callsite(), "<expr>").into_owned();
113                                 let sugg_msg =
114                                     format!("consider removing `{}()`", snippet(cx, path.span, "From::from"));
115                                 span_lint_and_then(cx, IDENTITY_CONVERSION, e.span, "identical conversion", |db| {
116                                     db.span_suggestion_with_applicability(
117                                         e.span,
118                                         &sugg_msg,
119                                         sugg,
120                                         Applicability::MachineApplicable, // snippet
121                                     );
122                                 });
123                             }
124                         }
125                     }
126                 }
127             },
128
129             _ => {},
130         }
131     }
132
133     fn check_expr_post(&mut self, _: &LateContext<'a, 'tcx>, e: &'tcx Expr) {
134         if Some(&e.id) == self.try_desugar_arm.last() {
135             self.try_desugar_arm.pop();
136         }
137     }
138 }