Skip to content

Commit 84e7974

Browse files
committed
Add autofix for functions in unnecessary_fallible_conversions
1 parent e1dbafd commit 84e7974

File tree

1 file changed

+68
-33
lines changed

1 file changed

+68
-33
lines changed

clippy_lints/src/methods/unnecessary_fallible_conversions.rs

Lines changed: 68 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use clippy_utils::diagnostics::span_lint_and_then;
22
use clippy_utils::get_parent_expr;
33
use clippy_utils::ty::implements_trait;
44
use rustc_errors::Applicability;
5-
use rustc_hir::{Expr, ExprKind};
5+
use rustc_hir::{Expr, ExprKind, QPath};
66
use rustc_lint::LateContext;
77
use rustc_middle::ty;
88
use rustc_middle::ty::print::with_forced_trimmed_paths;
@@ -29,6 +29,7 @@ fn check<'tcx>(
2929
node_args: ty::GenericArgsRef<'tcx>,
3030
kind: FunctionKind,
3131
primary_span: Span,
32+
qpath: Option<&QPath<'_>>,
3233
) {
3334
if let &[self_ty, other_ty] = node_args.as_slice()
3435
// useless_conversion already warns `T::try_from(T)`, so ignore it here
@@ -45,47 +46,79 @@ fn check<'tcx>(
4546
&& implements_trait(cx, self_ty, from_into_trait, &[other_ty])
4647
&& let Some(other_ty) = other_ty.as_type()
4748
{
49+
// Extend the span to include the unwrap/expect call:
50+
// `foo.try_into().expect("..")`
51+
// ^^^^^^^^^^^^^^^^^^^^^^^
52+
//
53+
// `try_into().unwrap()` specifically can be trivially replaced with just `into()`,
54+
// so that can be machine-applicable
4855
let parent_unwrap_call = get_parent_expr(cx, expr).and_then(|parent| {
4956
if let ExprKind::MethodCall(path, .., span) = parent.kind
5057
&& let sym::unwrap | sym::expect = path.ident.name
5158
{
52-
Some(span)
59+
// include `.` before `unwrap`/`expect`
60+
Some(span.with_lo(expr.span.hi()))
5361
} else {
5462
None
5563
}
5664
});
57-
let (source_ty, target_ty, sugg, span, applicability) = match kind {
58-
FunctionKind::TryIntoMethod if let Some(unwrap_span) = parent_unwrap_call => {
59-
// Extend the span to include the unwrap/expect call:
60-
// `foo.try_into().expect("..")`
61-
// ^^^^^^^^^^^^^^^^^^^^^^^
62-
//
63-
// `try_into().unwrap()` specifically can be trivially replaced with just `into()`,
64-
// so that can be machine-applicable
6565

66-
(
67-
self_ty,
68-
other_ty,
69-
"into()",
70-
primary_span.with_hi(unwrap_span.hi()),
71-
Applicability::MachineApplicable,
72-
)
66+
let span = if let Some(unwrap_call) = parent_unwrap_call {
67+
primary_span.with_hi(unwrap_call.hi())
68+
} else {
69+
primary_span
70+
};
71+
72+
let qpath_spans = qpath.and_then(|qpath| match qpath {
73+
QPath::Resolved(_, path) => {
74+
let segments = path.segments.iter().map(|seg| seg.ident).collect::<Vec<_>>();
75+
(segments.len() == 2).then(|| vec![segments[0].span, segments[1].span])
76+
},
77+
QPath::TypeRelative(_, seg) => Some(vec![seg.ident.span]),
78+
QPath::LangItem(_, _) => unreachable!("`TryFrom` and `TryInto` are not lang items"),
79+
});
80+
81+
let (source_ty, target_ty, sugg, applicability) = match (kind, &qpath_spans, parent_unwrap_call) {
82+
(FunctionKind::TryIntoMethod, _, Some(unwrap_span)) => {
83+
let sugg = vec![(primary_span, String::from("into")), (unwrap_span, String::new())];
84+
(self_ty, other_ty, sugg, Applicability::MachineApplicable)
85+
},
86+
(FunctionKind::TryFromFunction, Some(spans), Some(unwrap_span)) => {
87+
let sugg = match spans.len() {
88+
1 => vec![(spans[0], String::from("from")), (unwrap_span, String::new())],
89+
2 => vec![
90+
(spans[0], String::from("From")),
91+
(spans[1], String::from("from")),
92+
(unwrap_span, String::new()),
93+
],
94+
_ => unreachable!(),
95+
};
96+
(other_ty, self_ty, sugg, Applicability::MachineApplicable)
97+
},
98+
(FunctionKind::TryIntoFunction, Some(spans), Some(unwrap_span)) => {
99+
let sugg = match spans.len() {
100+
1 => vec![(spans[0], String::from("into")), (unwrap_span, String::new())],
101+
2 => vec![
102+
(spans[0], String::from("Into")),
103+
(spans[1], String::from("into")),
104+
(unwrap_span, String::new()),
105+
],
106+
_ => unreachable!(),
107+
};
108+
(self_ty, other_ty, sugg, Applicability::MachineApplicable)
109+
},
110+
(FunctionKind::TryFromFunction, _, _) => {
111+
let sugg = vec![(primary_span, String::from("From::from"))];
112+
(other_ty, self_ty, sugg, Applicability::Unspecified)
113+
},
114+
(FunctionKind::TryIntoFunction, _, _) => {
115+
let sugg = vec![(primary_span, String::from("Into::into"))];
116+
(self_ty, other_ty, sugg, Applicability::Unspecified)
117+
},
118+
(FunctionKind::TryIntoMethod, _, _) => {
119+
let sugg = vec![(primary_span, String::from("into"))];
120+
(self_ty, other_ty, sugg, Applicability::Unspecified)
73121
},
74-
FunctionKind::TryFromFunction => (
75-
other_ty,
76-
self_ty,
77-
"From::from",
78-
primary_span,
79-
Applicability::Unspecified,
80-
),
81-
FunctionKind::TryIntoFunction => (
82-
self_ty,
83-
other_ty,
84-
"Into::into",
85-
primary_span,
86-
Applicability::Unspecified,
87-
),
88-
FunctionKind::TryIntoMethod => (self_ty, other_ty, "into", primary_span, Applicability::Unspecified),
89122
};
90123

91124
span_lint_and_then(
@@ -97,7 +130,7 @@ fn check<'tcx>(
97130
with_forced_trimmed_paths!({
98131
diag.note(format!("converting `{source_ty}` to `{target_ty}` cannot fail"));
99132
});
100-
diag.span_suggestion(span, "use", sugg, applicability);
133+
diag.multipart_suggestion("use", sugg, applicability);
101134
},
102135
);
103136
}
@@ -113,6 +146,7 @@ pub(super) fn check_method(cx: &LateContext<'_>, expr: &Expr<'_>) {
113146
cx.typeck_results().node_args(expr.hir_id),
114147
FunctionKind::TryIntoMethod,
115148
path.ident.span,
149+
None,
116150
);
117151
}
118152
}
@@ -135,6 +169,7 @@ pub(super) fn check_function(cx: &LateContext<'_>, expr: &Expr<'_>, callee: &Exp
135169
_ => return,
136170
},
137171
callee.span,
172+
Some(qpath),
138173
);
139174
}
140175
}

0 commit comments

Comments
 (0)