Skip to content

Commit 6e2ab0c

Browse files
missing_error_impl
1 parent 162b0e8 commit 6e2ab0c

File tree

6 files changed

+107
-0
lines changed

6 files changed

+107
-0
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5873,6 +5873,7 @@ Released 2018-09-13
58735873
[`missing_const_for_thread_local`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_const_for_thread_local
58745874
[`missing_docs_in_private_items`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_docs_in_private_items
58755875
[`missing_enforced_import_renames`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_enforced_import_renames
5876+
[`missing_error_implementations`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_error_implementations
58765877
[`missing_errors_doc`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_errors_doc
58775878
[`missing_fields_in_debug`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_fields_in_debug
58785879
[`missing_inline_in_public_items`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_inline_in_public_items

clippy_lints/src/declared_lints.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -535,6 +535,7 @@ pub static LINTS: &[&crate::LintInfo] = &[
535535
crate::missing_const_for_thread_local::MISSING_CONST_FOR_THREAD_LOCAL_INFO,
536536
crate::missing_doc::MISSING_DOCS_IN_PRIVATE_ITEMS_INFO,
537537
crate::missing_enforced_import_rename::MISSING_ENFORCED_IMPORT_RENAMES_INFO,
538+
crate::missing_error_implementations::MISSING_ERROR_IMPLEMENTATIONS_INFO,
538539
crate::missing_fields_in_debug::MISSING_FIELDS_IN_DEBUG_INFO,
539540
crate::missing_inline::MISSING_INLINE_IN_PUBLIC_ITEMS_INFO,
540541
crate::missing_trait_methods::MISSING_TRAIT_METHODS_INFO,

clippy_lints/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -243,6 +243,7 @@ mod missing_const_for_fn;
243243
mod missing_const_for_thread_local;
244244
mod missing_doc;
245245
mod missing_enforced_import_rename;
246+
mod missing_error_implementations;
246247
mod missing_fields_in_debug;
247248
mod missing_inline;
248249
mod missing_trait_methods;
@@ -982,5 +983,6 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) {
982983
store.register_late_pass(move |_| Box::new(non_std_lazy_statics::NonStdLazyStatic::new(conf)));
983984
store.register_late_pass(|_| Box::new(manual_option_as_slice::ManualOptionAsSlice::new(conf)));
984985
store.register_late_pass(|_| Box::new(single_option_map::SingleOptionMap));
986+
store.register_late_pass(|_| Box::new(missing_error_implementations::MissingErrorImplementations));
985987
// add lints here, do not remove this comment, it's used in `new_lint`
986988
}
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
use clippy_utils::diagnostics::span_lint;
2+
use clippy_utils::ty::implements_trait;
3+
use rustc_hir::{Item, ItemKind};
4+
use rustc_lint::{LateContext, LateLintPass};
5+
use rustc_middle::ty::Visibility;
6+
use rustc_session::declare_lint_pass;
7+
use rustc_span::sym;
8+
9+
declare_clippy_lint! {
10+
/// ### What it does
11+
/// Checks for potentially forgotten implementations of `Error` for public types.
12+
///
13+
/// ### Why is this bad?
14+
/// `Error` provides a common interface for errors.
15+
/// Errors not implementing `Error` can not be used with functions that expect it.
16+
///
17+
/// ### Example
18+
/// ```no_run
19+
/// #[derive(Debug)]
20+
/// pub struct ParseError;
21+
///
22+
/// impl core::fmt::Display for ParseError { ... }
23+
/// ```
24+
/// Use instead:
25+
/// ```no_run
26+
/// #[derive(Debug)]
27+
/// pub struct ParseError;
28+
///
29+
/// impl core::fmt::Display for ParseError { ... }
30+
///
31+
/// impl core::error::Error for ParseError { ... }
32+
/// ```
33+
#[clippy::version = "1.87.0"]
34+
pub MISSING_ERROR_IMPL,
35+
suspicious,
36+
"exported types with potentially forgotten `Error` implementation"
37+
}
38+
39+
declare_lint_pass!(MissingErrorImpl => [MISSING_ERROR_IMPL]);
40+
41+
impl<'tcx> LateLintPass<'tcx> for MissingErrorImpl {
42+
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) {
43+
match item.kind {
44+
ItemKind::Enum(_, generics) | ItemKind::Struct(_, generics) => {
45+
let is_error_candidate = {
46+
let name: &str = item.ident.name.as_str();
47+
name.ends_with("Error") && name != "Error"
48+
};
49+
if is_error_candidate
50+
// Only check public items, as missing impls for private items are easy to fix.
51+
&& (cx.tcx.visibility(item.owner_id.def_id) == Visibility::Public)
52+
// Check whether Error is implemented,
53+
// skipping generic types as we'd have to ask whether there is an error impl
54+
// for any instantiation of it.
55+
&& generics.params.is_empty()
56+
&& let ty = cx.tcx.type_of(item.owner_id).instantiate_identity()
57+
&& let Some(error_def_id) = cx.tcx.get_diagnostic_item(sym::Error)
58+
&& !implements_trait(cx, ty, error_def_id, &[])
59+
{
60+
span_lint(
61+
cx,
62+
MISSING_ERROR_IMPL,
63+
item.ident.span,
64+
"error type doesn't implement `Error`",
65+
);
66+
}
67+
},
68+
_ => {},
69+
}
70+
}
71+
}

tests/ui/missing_error_impl.rs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
#![warn(clippy::missing_error_impl)]
2+
3+
struct InternalError;
4+
5+
pub(crate) struct CrateInternalError;
6+
7+
pub struct PublicError;
8+
//~^ missing_error_impl
9+
10+
pub struct NotAnErrorType;
11+
12+
#[derive(Debug)]
13+
pub struct GenericError<T>(T);
14+
15+
impl<T> core::fmt::Display for GenericError<T> {
16+
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
17+
unimplemented!()
18+
}
19+
}
20+
21+
impl<T> core::error::Error for GenericError<T> where T: core::fmt::Display + core::fmt::Debug {}

tests/ui/missing_error_impl.stderr

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
error: error type doesn't implement `Error`
2+
--> tests/ui/missing_error_impl.rs:7:12
3+
|
4+
LL | pub struct PublicError;
5+
| ^^^^^^^^^^^
6+
|
7+
= note: `-D clippy::missing-error-impl` implied by `-D warnings`
8+
= help: to override `-D warnings` add `#[allow(clippy::missing_error_impl)]`
9+
10+
error: aborting due to 1 previous error
11+

0 commit comments

Comments
 (0)