]>
Commit | Line | Data |
---|---|---|
f20569fa XL |
1 | use crate::utils::sugg::Sugg; |
2 | use crate::utils::{ | |
3 | get_parent_expr, is_type_diagnostic_item, match_def_path, match_trait_method, paths, snippet, | |
4 | snippet_with_macro_callsite, span_lint_and_help, span_lint_and_sugg, | |
5 | }; | |
6 | use if_chain::if_chain; | |
7 | use rustc_errors::Applicability; | |
8 | use rustc_hir::{Expr, ExprKind, HirId, MatchSource}; | |
9 | use rustc_lint::{LateContext, LateLintPass}; | |
10 | use rustc_middle::ty::{self, TyS}; | |
11 | use rustc_session::{declare_tool_lint, impl_lint_pass}; | |
12 | use rustc_span::sym; | |
13 | ||
14 | declare_clippy_lint! { | |
15 | /// **What it does:** Checks for `Into`, `TryInto`, `From`, `TryFrom`, or `IntoIter` calls | |
16 | /// which uselessly convert to the same type. | |
17 | /// | |
18 | /// **Why is this bad?** Redundant code. | |
19 | /// | |
20 | /// **Known problems:** None. | |
21 | /// | |
22 | /// **Example:** | |
23 | /// | |
24 | /// ```rust | |
25 | /// // Bad | |
26 | /// // format!() returns a `String` | |
27 | /// let s: String = format!("hello").into(); | |
28 | /// | |
29 | /// // Good | |
30 | /// let s: String = format!("hello"); | |
31 | /// ``` | |
32 | pub USELESS_CONVERSION, | |
33 | complexity, | |
34 | "calls to `Into`, `TryInto`, `From`, `TryFrom`, or `IntoIter` which perform useless conversions to the same type" | |
35 | } | |
36 | ||
37 | #[derive(Default)] | |
38 | pub struct UselessConversion { | |
39 | try_desugar_arm: Vec<HirId>, | |
40 | } | |
41 | ||
42 | impl_lint_pass!(UselessConversion => [USELESS_CONVERSION]); | |
43 | ||
44 | #[allow(clippy::too_many_lines)] | |
45 | impl<'tcx> LateLintPass<'tcx> for UselessConversion { | |
46 | fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) { | |
47 | if e.span.from_expansion() { | |
48 | return; | |
49 | } | |
50 | ||
51 | if Some(&e.hir_id) == self.try_desugar_arm.last() { | |
52 | return; | |
53 | } | |
54 | ||
55 | match e.kind { | |
56 | ExprKind::Match(_, ref arms, MatchSource::TryDesugar) => { | |
57 | let e = match arms[0].body.kind { | |
58 | ExprKind::Ret(Some(ref e)) | ExprKind::Break(_, Some(ref e)) => e, | |
59 | _ => return, | |
60 | }; | |
61 | if let ExprKind::Call(_, ref args) = e.kind { | |
62 | self.try_desugar_arm.push(args[0].hir_id); | |
63 | } | |
64 | }, | |
65 | ||
66 | ExprKind::MethodCall(ref name, .., ref args, _) => { | |
67 | if match_trait_method(cx, e, &paths::INTO) && &*name.ident.as_str() == "into" { | |
68 | let a = cx.typeck_results().expr_ty(e); | |
69 | let b = cx.typeck_results().expr_ty(&args[0]); | |
70 | if TyS::same_type(a, b) { | |
71 | let sugg = snippet_with_macro_callsite(cx, args[0].span, "<expr>").to_string(); | |
72 | span_lint_and_sugg( | |
73 | cx, | |
74 | USELESS_CONVERSION, | |
75 | e.span, | |
76 | &format!("useless conversion to the same type: `{}`", b), | |
77 | "consider removing `.into()`", | |
78 | sugg, | |
79 | Applicability::MachineApplicable, // snippet | |
80 | ); | |
81 | } | |
82 | } | |
83 | if match_trait_method(cx, e, &paths::INTO_ITERATOR) && name.ident.name == sym::into_iter { | |
84 | if let Some(parent_expr) = get_parent_expr(cx, e) { | |
85 | if let ExprKind::MethodCall(ref parent_name, ..) = parent_expr.kind { | |
86 | if parent_name.ident.name != sym::into_iter { | |
87 | return; | |
88 | } | |
89 | } | |
90 | } | |
91 | let a = cx.typeck_results().expr_ty(e); | |
92 | let b = cx.typeck_results().expr_ty(&args[0]); | |
93 | if TyS::same_type(a, b) { | |
94 | let sugg = snippet(cx, args[0].span, "<expr>").into_owned(); | |
95 | span_lint_and_sugg( | |
96 | cx, | |
97 | USELESS_CONVERSION, | |
98 | e.span, | |
99 | &format!("useless conversion to the same type: `{}`", b), | |
100 | "consider removing `.into_iter()`", | |
101 | sugg, | |
102 | Applicability::MachineApplicable, // snippet | |
103 | ); | |
104 | } | |
105 | } | |
106 | if match_trait_method(cx, e, &paths::TRY_INTO_TRAIT) && &*name.ident.as_str() == "try_into" { | |
107 | if_chain! { | |
108 | let a = cx.typeck_results().expr_ty(e); | |
109 | let b = cx.typeck_results().expr_ty(&args[0]); | |
110 | if is_type_diagnostic_item(cx, a, sym::result_type); | |
111 | if let ty::Adt(_, substs) = a.kind(); | |
112 | if let Some(a_type) = substs.types().next(); | |
113 | if TyS::same_type(a_type, b); | |
114 | ||
115 | then { | |
116 | span_lint_and_help( | |
117 | cx, | |
118 | USELESS_CONVERSION, | |
119 | e.span, | |
120 | &format!("useless conversion to the same type: `{}`", b), | |
121 | None, | |
122 | "consider removing `.try_into()`", | |
123 | ); | |
124 | } | |
125 | } | |
126 | } | |
127 | }, | |
128 | ||
129 | ExprKind::Call(ref path, ref args) => { | |
130 | if_chain! { | |
131 | if args.len() == 1; | |
132 | if let ExprKind::Path(ref qpath) = path.kind; | |
133 | if let Some(def_id) = cx.qpath_res(qpath, path.hir_id).opt_def_id(); | |
134 | let a = cx.typeck_results().expr_ty(e); | |
135 | let b = cx.typeck_results().expr_ty(&args[0]); | |
136 | ||
137 | then { | |
138 | if_chain! { | |
139 | if match_def_path(cx, def_id, &paths::TRY_FROM); | |
140 | if is_type_diagnostic_item(cx, a, sym::result_type); | |
141 | if let ty::Adt(_, substs) = a.kind(); | |
142 | if let Some(a_type) = substs.types().next(); | |
143 | if TyS::same_type(a_type, b); | |
144 | ||
145 | then { | |
146 | let hint = format!("consider removing `{}()`", snippet(cx, path.span, "TryFrom::try_from")); | |
147 | span_lint_and_help( | |
148 | cx, | |
149 | USELESS_CONVERSION, | |
150 | e.span, | |
151 | &format!("useless conversion to the same type: `{}`", b), | |
152 | None, | |
153 | &hint, | |
154 | ); | |
155 | } | |
156 | } | |
157 | ||
158 | if_chain! { | |
159 | if match_def_path(cx, def_id, &paths::FROM_FROM); | |
160 | if TyS::same_type(a, b); | |
161 | ||
162 | then { | |
163 | let sugg = Sugg::hir_with_macro_callsite(cx, &args[0], "<expr>").maybe_par(); | |
164 | let sugg_msg = | |
165 | format!("consider removing `{}()`", snippet(cx, path.span, "From::from")); | |
166 | span_lint_and_sugg( | |
167 | cx, | |
168 | USELESS_CONVERSION, | |
169 | e.span, | |
170 | &format!("useless conversion to the same type: `{}`", b), | |
171 | &sugg_msg, | |
172 | sugg.to_string(), | |
173 | Applicability::MachineApplicable, // snippet | |
174 | ); | |
175 | } | |
176 | } | |
177 | } | |
178 | } | |
179 | }, | |
180 | ||
181 | _ => {}, | |
182 | } | |
183 | } | |
184 | ||
185 | fn check_expr_post(&mut self, _: &LateContext<'tcx>, e: &'tcx Expr<'_>) { | |
186 | if Some(&e.hir_id) == self.try_desugar_arm.last() { | |
187 | self.try_desugar_arm.pop(); | |
188 | } | |
189 | } | |
190 | } |