Skip to content

Commit d7b27ec

Browse files
authored
add lint infallible_try_from (#14813)
Looks for `TryFrom` implementations with uninhabited error types and suggests to implement `From` instead. Fixes #2144 --- changelog: Add [`infallible_try_from`] lint
2 parents 88bb8d1 + 8e581e3 commit d7b27ec

File tree

7 files changed

+137
-0
lines changed

7 files changed

+137
-0
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5862,6 +5862,7 @@ Released 2018-09-13
58625862
[`ineffective_open_options`]: https://rust-lang.github.io/rust-clippy/master/index.html#ineffective_open_options
58635863
[`inefficient_to_string`]: https://rust-lang.github.io/rust-clippy/master/index.html#inefficient_to_string
58645864
[`infallible_destructuring_match`]: https://rust-lang.github.io/rust-clippy/master/index.html#infallible_destructuring_match
5865+
[`infallible_try_from`]: https://rust-lang.github.io/rust-clippy/master/index.html#infallible_try_from
58655866
[`infinite_iter`]: https://rust-lang.github.io/rust-clippy/master/index.html#infinite_iter
58665867
[`infinite_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#infinite_loop
58675868
[`inherent_to_string`]: https://rust-lang.github.io/rust-clippy/master/index.html#inherent_to_string

clippy_lints/src/declared_lints.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,7 @@ pub static LINTS: &[&crate::LintInfo] = &[
212212
crate::indexing_slicing::INDEXING_SLICING_INFO,
213213
crate::indexing_slicing::OUT_OF_BOUNDS_INDEXING_INFO,
214214
crate::ineffective_open_options::INEFFECTIVE_OPEN_OPTIONS_INFO,
215+
crate::infallible_try_from::INFALLIBLE_TRY_FROM_INFO,
215216
crate::infinite_iter::INFINITE_ITER_INFO,
216217
crate::infinite_iter::MAYBE_INFINITE_ITER_INFO,
217218
crate::inherent_impl::MULTIPLE_INHERENT_IMPL_INFO,
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
use clippy_utils::diagnostics::span_lint;
2+
use clippy_utils::sym;
3+
use rustc_errors::MultiSpan;
4+
use rustc_hir::{AssocItemKind, Item, ItemKind};
5+
use rustc_lint::{LateContext, LateLintPass};
6+
use rustc_session::declare_lint_pass;
7+
8+
declare_clippy_lint! {
9+
/// ### What it does
10+
///
11+
/// Finds manual impls of `TryFrom` with infallible error types.
12+
///
13+
/// ### Why is this bad?
14+
///
15+
/// Infalliable conversions should be implemented via `From` with the blanket conversion.
16+
///
17+
/// ### Example
18+
/// ```no_run
19+
/// use std::convert::Infallible;
20+
/// struct MyStruct(i16);
21+
/// impl TryFrom<i16> for MyStruct {
22+
/// type Error = Infallible;
23+
/// fn try_from(other: i16) -> Result<Self, Infallible> {
24+
/// Ok(Self(other.into()))
25+
/// }
26+
/// }
27+
/// ```
28+
/// Use instead:
29+
/// ```no_run
30+
/// struct MyStruct(i16);
31+
/// impl From<i16> for MyStruct {
32+
/// fn from(other: i16) -> Self {
33+
/// Self(other)
34+
/// }
35+
/// }
36+
/// ```
37+
#[clippy::version = "1.88.0"]
38+
pub INFALLIBLE_TRY_FROM,
39+
suspicious,
40+
"TryFrom with infallible Error type"
41+
}
42+
declare_lint_pass!(InfallibleTryFrom => [INFALLIBLE_TRY_FROM]);
43+
44+
impl<'tcx> LateLintPass<'tcx> for InfallibleTryFrom {
45+
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) {
46+
let ItemKind::Impl(imp) = item.kind else { return };
47+
let Some(r#trait) = imp.of_trait else { return };
48+
let Some(trait_def_id) = r#trait.trait_def_id() else {
49+
return;
50+
};
51+
if !cx.tcx.is_diagnostic_item(sym::TryFrom, trait_def_id) {
52+
return;
53+
}
54+
for ii in imp.items {
55+
if ii.kind == AssocItemKind::Type {
56+
let ii = cx.tcx.hir_impl_item(ii.id);
57+
if ii.ident.name != sym::Error {
58+
continue;
59+
}
60+
let ii_ty = ii.expect_type();
61+
let ii_ty_span = ii_ty.span;
62+
let ii_ty = clippy_utils::ty::ty_from_hir_ty(cx, ii_ty);
63+
if !ii_ty.is_inhabited_from(cx.tcx, ii.owner_id.to_def_id(), cx.typing_env()) {
64+
let mut span = MultiSpan::from_span(cx.tcx.def_span(item.owner_id.to_def_id()));
65+
span.push_span_label(ii_ty_span, "infallible error type");
66+
span_lint(
67+
cx,
68+
INFALLIBLE_TRY_FROM,
69+
span,
70+
"infallible TryFrom impl; consider implementing From, instead",
71+
);
72+
}
73+
}
74+
}
75+
}
76+
}

clippy_lints/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,7 @@ mod inconsistent_struct_constructor;
169169
mod index_refutable_slice;
170170
mod indexing_slicing;
171171
mod ineffective_open_options;
172+
mod infallible_try_from;
172173
mod infinite_iter;
173174
mod inherent_impl;
174175
mod inherent_to_string;
@@ -946,5 +947,6 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) {
946947
store.register_late_pass(|_| Box::new(single_option_map::SingleOptionMap));
947948
store.register_late_pass(move |_| Box::new(redundant_test_prefix::RedundantTestPrefix));
948949
store.register_late_pass(|_| Box::new(cloned_ref_to_slice_refs::ClonedRefToSliceRefs::new(conf)));
950+
store.register_late_pass(|_| Box::new(infallible_try_from::InfallibleTryFrom));
949951
// add lints here, do not remove this comment, it's used in `new_lint`
950952
}

tests/ui/crashes/ice-rust-107877.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
struct Foo;
66

7+
#[allow(clippy::infallible_try_from)]
78
impl<'a> std::convert::TryFrom<&'a String> for Foo {
89
type Error = std::convert::Infallible;
910

tests/ui/infallible_try_from.rs

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
#![feature(never_type)]
2+
#![warn(clippy::infallible_try_from)]
3+
4+
use std::convert::Infallible;
5+
6+
struct MyStruct(i32);
7+
8+
impl TryFrom<i8> for MyStruct {
9+
//~^ infallible_try_from
10+
type Error = !;
11+
fn try_from(other: i8) -> Result<Self, !> {
12+
Ok(Self(other.into()))
13+
}
14+
}
15+
16+
impl TryFrom<i16> for MyStruct {
17+
//~^ infallible_try_from
18+
type Error = Infallible;
19+
fn try_from(other: i16) -> Result<Self, Infallible> {
20+
Ok(Self(other.into()))
21+
}
22+
}
23+
24+
impl TryFrom<i64> for MyStruct {
25+
type Error = i64;
26+
fn try_from(other: i64) -> Result<Self, i64> {
27+
Ok(Self(i32::try_from(other).map_err(|_| other)?))
28+
}
29+
}
30+
31+
fn main() {
32+
// test code goes here
33+
}

tests/ui/infallible_try_from.stderr

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
error: infallible TryFrom impl; consider implementing From, instead
2+
--> tests/ui/infallible_try_from.rs:8:1
3+
|
4+
LL | impl TryFrom<i8> for MyStruct {
5+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
6+
LL |
7+
LL | type Error = !;
8+
| - infallible error type
9+
|
10+
= note: `-D clippy::infallible-try-from` implied by `-D warnings`
11+
= help: to override `-D warnings` add `#[allow(clippy::infallible_try_from)]`
12+
13+
error: infallible TryFrom impl; consider implementing From, instead
14+
--> tests/ui/infallible_try_from.rs:16:1
15+
|
16+
LL | impl TryFrom<i16> for MyStruct {
17+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
18+
LL |
19+
LL | type Error = Infallible;
20+
| ---------- infallible error type
21+
22+
error: aborting due to 2 previous errors
23+

0 commit comments

Comments
 (0)