1 use clippy_utils
::diagnostics
::{span_lint_and_help, span_lint_and_sugg}
;
2 use clippy_utils
::source
::{snippet, snippet_with_macro_callsite}
;
3 use clippy_utils
::sugg
::Sugg
;
4 use clippy_utils
::ty
::{is_type_diagnostic_item, same_type_and_consts}
;
5 use clippy_utils
::{get_parent_expr, match_def_path, match_trait_method, paths}
;
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}
;
11 use rustc_session
::{declare_tool_lint, impl_lint_pass}
;
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.
18 /// **Why is this bad?** Redundant code.
20 /// **Known problems:** None.
26 /// // format!() returns a `String`
27 /// let s: String = format!("hello").into();
30 /// let s: String = format!("hello");
32 pub USELESS_CONVERSION
,
34 "calls to `Into`, `TryInto`, `From`, `TryFrom`, or `IntoIter` which perform useless conversions to the same type"
38 pub struct UselessConversion
{
39 try_desugar_arm
: Vec
<HirId
>,
42 impl_lint_pass
!(UselessConversion
=> [USELESS_CONVERSION
]);
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() {
51 if Some(&e
.hir_id
) == self.try_desugar_arm
.last() {
56 ExprKind
::Match(_
, arms
, MatchSource
::TryDesugar
) => {
57 let e
= match arms
[0].body
.kind
{
58 ExprKind
::Ret(Some(e
)) | ExprKind
::Break(_
, Some(e
)) => e
,
61 if let ExprKind
::Call(_
, args
) = e
.kind
{
62 self.try_desugar_arm
.push(args
[0].hir_id
);
66 ExprKind
::MethodCall(name
, .., 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 same_type_and_consts(a
, b
) {
71 let sugg
= snippet_with_macro_callsite(cx
, args
[0].span
, "<expr>").to_string();
76 &format
!("useless conversion to the same type: `{}`", b
),
77 "consider removing `.into()`",
79 Applicability
::MachineApplicable
, // snippet
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(parent_name
, ..) = parent_expr
.kind
{
86 if parent_name
.ident
.name
!= sym
::into_iter
{
91 let a
= cx
.typeck_results().expr_ty(e
);
92 let b
= cx
.typeck_results().expr_ty(&args
[0]);
93 if same_type_and_consts(a
, b
) {
94 let sugg
= snippet(cx
, args
[0].span
, "<expr>").into_owned();
99 &format
!("useless conversion to the same type: `{}`", b
),
100 "consider removing `.into_iter()`",
102 Applicability
::MachineApplicable
, // snippet
107 if match_trait_method(cx
, e
, &paths
::TRY_INTO_TRAIT
) && name
.ident
.name
== sym
::try_into
;
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 same_type_and_consts(a_type
, b
);
120 &format
!("useless conversion to the same type: `{}`", b
),
122 "consider removing `.try_into()`",
128 ExprKind
::Call(path
, args
) => {
131 if let ExprKind
::Path(ref qpath
) = path
.kind
;
132 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]);
137 if match_def_path(cx
, def_id
, &paths
::TRY_FROM
);
138 if is_type_diagnostic_item(cx
, a
, sym
::result_type
);
139 if let ty
::Adt(_
, substs
) = a
.kind();
140 if let Some(a_type
) = substs
.types().next();
141 if same_type_and_consts(a_type
, b
);
144 let hint
= format
!("consider removing `{}()`", snippet(cx
, path
.span
, "TryFrom::try_from"));
149 &format
!("useless conversion to the same type: `{}`", b
),
157 if match_def_path(cx
, def_id
, &paths
::FROM_FROM
);
158 if same_type_and_consts(a
, b
);
161 let sugg
= Sugg
::hir_with_macro_callsite(cx
, &args
[0], "<expr>").maybe_par();
163 format
!("consider removing `{}()`", snippet(cx
, path
.span
, "From::from"));
168 &format
!("useless conversion to the same type: `{}`", b
),
171 Applicability
::MachineApplicable
, // snippet
183 fn check_expr_post(&mut self, _
: &LateContext
<'tcx
>, e
: &'tcx Expr
<'_
>) {
184 if Some(&e
.hir_id
) == self.try_desugar_arm
.last() {
185 self.try_desugar_arm
.pop();