]> git.lizzy.rs Git - rust.git/blob - clippy_lints/src/identity_conversion.rs
Fill in Applicability from review comments by @flip1995
[rust.git] / clippy_lints / src / identity_conversion.rs
1 use crate::rustc::lint::{LateContext, LateLintPass, LintArray, LintPass};
2 use crate::rustc::{declare_tool_lint, lint_array};
3 use crate::rustc::hir::*;
4 use crate::syntax::ast::NodeId;
5 use crate::utils::{in_macro, match_def_path, match_trait_method, same_tys, snippet, span_lint_and_then};
6 use crate::utils::{opt_def_id, paths, resolve_node};
7 use crate::rustc_errors::Applicability;
8
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 declare_clippy_lint! {
21     pub IDENTITY_CONVERSION,
22     complexity,
23     "using always-identical `Into`/`From`/`IntoIter` conversions"
24 }
25
26 #[derive(Default)]
27 pub struct IdentityConversion {
28     try_desugar_arm: Vec<NodeId>,
29 }
30
31 impl LintPass for IdentityConversion {
32     fn get_lints(&self) -> LintArray {
33         lint_array!(IDENTITY_CONVERSION)
34     }
35 }
36
37 impl<'a, 'tcx> LateLintPass<'a, 'tcx> for IdentityConversion {
38     fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, e: &'tcx Expr) {
39         if in_macro(e.span) {
40             return;
41         }
42
43         if Some(&e.id) == self.try_desugar_arm.last() {
44             return;
45         }
46
47         match e.node {
48             ExprKind::Match(_, ref arms, MatchSource::TryDesugar) => {
49                 let e = match arms[0].body.node {
50                     ExprKind::Ret(Some(ref e)) | ExprKind::Break(_, Some(ref e)) => e,
51                     _ => return,
52                 };
53                 if let ExprKind::Call(_, ref args) = e.node {
54                     self.try_desugar_arm.push(args[0].id);
55                 } else {
56                     return;
57                 }
58             },
59
60             ExprKind::MethodCall(ref name, .., ref args) => {
61                 if match_trait_method(cx, e, &paths::INTO[..]) && &*name.ident.as_str() == "into" {
62                     let a = cx.tables.expr_ty(e);
63                     let b = cx.tables.expr_ty(&args[0]);
64                     if same_tys(cx, a, b) {
65                         let sugg = snippet(cx, args[0].span, "<expr>").into_owned();
66                         span_lint_and_then(cx, IDENTITY_CONVERSION, e.span, "identical conversion", |db| {
67                             db.span_suggestion_with_applicability(
68                                 e.span,
69                                 "consider removing `.into()`",
70                                 sugg,
71                                 Applicability::MachineApplicable, // snippet
72                             );
73                         });
74                     }
75                 }
76                 if match_trait_method(cx, e, &paths::INTO_ITERATOR) && &*name.ident.as_str() == "into_iter" {
77                     let a = cx.tables.expr_ty(e);
78                     let b = cx.tables.expr_ty(&args[0]);
79                     if same_tys(cx, a, b) {
80                         let sugg = snippet(cx, args[0].span, "<expr>").into_owned();
81                         span_lint_and_then(cx, IDENTITY_CONVERSION, e.span, "identical conversion", |db| {
82                             db.span_suggestion_with_applicability(
83                                 e.span,
84                                 "consider removing `.into_iter()`",
85                                 sugg,
86                                 Applicability::MachineApplicable, // snippet
87                             );
88                         });
89                     }
90                 }
91             },
92
93             ExprKind::Call(ref path, ref args) => if let ExprKind::Path(ref qpath) = path.node {
94                 if let Some(def_id) = opt_def_id(resolve_node(cx, qpath, path.hir_id)) {
95                     if match_def_path(cx.tcx, def_id, &paths::FROM_FROM[..]) {
96                         let a = cx.tables.expr_ty(e);
97                         let b = cx.tables.expr_ty(&args[0]);
98                         if same_tys(cx, a, b) {
99                             let sugg = snippet(cx, args[0].span.source_callsite(), "<expr>").into_owned();
100                             let sugg_msg = format!("consider removing `{}()`", snippet(cx, path.span, "From::from"));
101                             span_lint_and_then(cx, IDENTITY_CONVERSION, e.span, "identical conversion", |db| {
102                                 db.span_suggestion_with_applicability(
103                                     e.span,
104                                     &sugg_msg,
105                                     sugg,
106                                     Applicability::MachineApplicable, // snippet
107                                 );
108                             });
109                         }
110                     }
111                 }
112             },
113
114             _ => {},
115         }
116     }
117
118     fn check_expr_post(&mut self, _: &LateContext<'a, 'tcx>, e: &'tcx Expr) {
119         if Some(&e.id) == self.try_desugar_arm.last() {
120             self.try_desugar_arm.pop();
121         }
122     }
123 }