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