Skip to content

Commit d4eee17

Browse files
committed
add lint infallible_try_from
Looks for `TryFrom` implementations with uninhabited error types and suggests to implement `From` instead. Fixes #2144
1 parent 40bead0 commit d4eee17

File tree

6 files changed

+134
-0
lines changed

6 files changed

+134
-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
@@ -211,6 +211,7 @@ pub static LINTS: &[&crate::LintInfo] = &[
211211
crate::indexing_slicing::INDEXING_SLICING_INFO,
212212
crate::indexing_slicing::OUT_OF_BOUNDS_INDEXING_INFO,
213213
crate::ineffective_open_options::INEFFECTIVE_OPEN_OPTIONS_INFO,
214+
crate::infallible_try_from::INFALLIBLE_TRY_FROM_INFO,
214215
crate::infinite_iter::INFINITE_ITER_INFO,
215216
crate::infinite_iter::MAYBE_INFINITE_ITER_INFO,
216217
crate::inherent_impl::MULTIPLE_INHERENT_IMPL_INFO,
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
use clippy_utils::diagnostics::span_lint;
2+
use clippy_utils::sym;
3+
use rustc_errors::MultiSpan;
4+
use rustc_hir::*;
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+
/// impl TryFrom<i16> for MyStruct {
20+
/// type Error = Infallible;
21+
/// fn try_from(other: i16) -> Result<Self, Infallible> {
22+
/// Ok(Self(other.into()))
23+
/// }
24+
/// }
25+
/// ```
26+
/// Use instead:
27+
/// ```no_run
28+
/// impl From<i16> for MyStruct {
29+
/// fn from(other: i16) -> Self {
30+
/// Self(other)
31+
/// }
32+
/// }
33+
/// ```
34+
#[clippy::version = "1.88.0"]
35+
pub INFALLIBLE_TRY_FROM,
36+
nursery,
37+
"default lint description"
38+
}
39+
declare_lint_pass!(InfallibleTryFrom => [INFALLIBLE_TRY_FROM]);
40+
41+
impl<'tcx> LateLintPass<'tcx> for InfallibleTryFrom {
42+
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) {
43+
let ItemKind::Impl(imp) = item.kind else { return };
44+
let Some(r#trait) = imp.of_trait else { return };
45+
let Some(trait_def_id) = r#trait.trait_def_id() else {
46+
return;
47+
};
48+
if !cx.tcx.is_diagnostic_item(sym::TryFrom, trait_def_id) {
49+
return;
50+
}
51+
for ii in imp.items {
52+
if ii.kind == AssocItemKind::Type {
53+
let ii_id = ii.id;
54+
let ii = cx.tcx.hir_impl_item(ii.id);
55+
if ii.ident.name.as_str() != "Error" {
56+
continue;
57+
}
58+
let ii_ty = ii.expect_type();
59+
let ii_ty_span = ii_ty.span;
60+
let ii_ty = clippy_utils::ty::ty_from_hir_ty(cx, ii_ty);
61+
if !ii_ty.is_inhabited_from(cx.tcx, ii_id.owner_id.to_def_id(), cx.typing_env()) {
62+
let mut span = MultiSpan::from_span(cx.tcx.def_span(item.owner_id.to_def_id()));
63+
span.push_span_label(ii_ty_span, "infallible error type");
64+
span_lint(
65+
cx,
66+
INFALLIBLE_TRY_FROM,
67+
span,
68+
"infallible TryFrom impl; consider implementing From, instead",
69+
);
70+
}
71+
}
72+
}
73+
}
74+
}

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;
@@ -944,5 +945,6 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) {
944945
store.register_late_pass(|_| Box::new(single_option_map::SingleOptionMap));
945946
store.register_late_pass(move |_| Box::new(redundant_test_prefix::RedundantTestPrefix));
946947
store.register_late_pass(|_| Box::new(cloned_ref_to_slice_refs::ClonedRefToSliceRefs::new(conf)));
948+
store.register_late_pass(|_| Box::new(infallible_try_from::InfallibleTryFrom));
947949
// add lints here, do not remove this comment, it's used in `new_lint`
948950
}

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)