Skip to content

Commit 459969f

Browse files
author
Vali Schneider
committed
added restriction lint that prohibits the usage of unimplemented, unreachable or panic in a function of type result or option
1 parent dead45f commit 459969f

File tree

6 files changed

+280
-0
lines changed

6 files changed

+280
-0
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1651,6 +1651,7 @@ Released 2018-09-13
16511651
[`out_of_bounds_indexing`]: https://rust-lang.github.io/rust-clippy/master/index.html#out_of_bounds_indexing
16521652
[`overflow_check_conditional`]: https://rust-lang.github.io/rust-clippy/master/index.html#overflow_check_conditional
16531653
[`panic`]: https://rust-lang.github.io/rust-clippy/master/index.html#panic
1654+
[`panic_in_result`]: https://rust-lang.github.io/rust-clippy/master/index.html#panic_in_result
16541655
[`panic_params`]: https://rust-lang.github.io/rust-clippy/master/index.html#panic_params
16551656
[`panicking_unwrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#panicking_unwrap
16561657
[`partialeq_ne_impl`]: https://rust-lang.github.io/rust-clippy/master/index.html#partialeq_ne_impl

clippy_lints/src/lib.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -267,6 +267,7 @@ mod open_options;
267267
mod option_env_unwrap;
268268
mod option_if_let_else;
269269
mod overflow_check_conditional;
270+
mod panic_in_result;
270271
mod panic_unimplemented;
271272
mod partialeq_ne_impl;
272273
mod path_buf_push_overwrite;
@@ -747,6 +748,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
747748
&option_env_unwrap::OPTION_ENV_UNWRAP,
748749
&option_if_let_else::OPTION_IF_LET_ELSE,
749750
&overflow_check_conditional::OVERFLOW_CHECK_CONDITIONAL,
751+
&panic_in_result::PANIC_IN_RESULT,
750752
&panic_unimplemented::PANIC,
751753
&panic_unimplemented::PANIC_PARAMS,
752754
&panic_unimplemented::TODO,
@@ -1086,6 +1088,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
10861088
store.register_late_pass(|| box manual_async_fn::ManualAsyncFn);
10871089
store.register_early_pass(|| box redundant_field_names::RedundantFieldNames);
10881090
store.register_late_pass(|| box vec_resize_to_zero::VecResizeToZero);
1091+
store.register_late_pass(|| box panic_in_result::PanicInResult);
1092+
10891093
let single_char_binding_names_threshold = conf.single_char_binding_names_threshold;
10901094
store.register_early_pass(move || box non_expressive_names::NonExpressiveNames {
10911095
single_char_binding_names_threshold,
@@ -1128,6 +1132,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
11281132
LintId::of(&missing_doc::MISSING_DOCS_IN_PRIVATE_ITEMS),
11291133
LintId::of(&missing_inline::MISSING_INLINE_IN_PUBLIC_ITEMS),
11301134
LintId::of(&modulo_arithmetic::MODULO_ARITHMETIC),
1135+
LintId::of(&panic_in_result::PANIC_IN_RESULT),
11311136
LintId::of(&panic_unimplemented::PANIC),
11321137
LintId::of(&panic_unimplemented::TODO),
11331138
LintId::of(&panic_unimplemented::UNIMPLEMENTED),

clippy_lints/src/panic_in_result.rs

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
use crate::utils::{is_expn_of, is_type_diagnostic_item, return_ty, span_lint_and_then};
2+
use if_chain::if_chain;
3+
use rustc_hir as hir;
4+
use rustc_lint::{LateContext, LateLintPass};
5+
use rustc_middle::hir::map::Map;
6+
use rustc_session::{declare_lint_pass, declare_tool_lint};
7+
use rustc_span::Span;
8+
9+
declare_clippy_lint! {
10+
/// **What it does:** Checks for usage of `panic!`, `unimplemented!` or `unreachable!` in a function of type result/option.
11+
///
12+
/// **Why is this bad?** For some codebases,
13+
///
14+
/// **Known problems:** None.
15+
///
16+
/// **Example:**
17+
///
18+
/// ```rust
19+
/// fn option_with_panic() -> Option<bool> // should emit lint
20+
/// {
21+
/// panic!("error");
22+
/// }
23+
/// ```
24+
25+
pub PANIC_IN_RESULT,
26+
restriction,
27+
"functions of type `Result<..>` / `Option`<...> that contain `panic!()` or `unreachable()` or `unimplemented()` "
28+
}
29+
30+
declare_lint_pass!(PanicInResult => [PANIC_IN_RESULT]);
31+
32+
impl<'tcx> LateLintPass<'tcx> for PanicInResult {
33+
fn check_impl_item(&mut self, cx: &LateContext<'tcx>, impl_item: &'tcx hir::ImplItem<'_>) {
34+
if_chain! {
35+
// first check if it's a method or function
36+
if let hir::ImplItemKind::Fn(ref _signature, _) = impl_item.kind;
37+
// checking if its return type is `result` or `option`
38+
if is_type_diagnostic_item(cx, return_ty(cx, impl_item.hir_id), sym!(result_type))
39+
|| is_type_diagnostic_item(cx, return_ty(cx, impl_item.hir_id), sym!(option_type));
40+
then {
41+
lint_impl_body(cx, impl_item.span, impl_item);
42+
}
43+
}
44+
}
45+
}
46+
47+
use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor};
48+
use rustc_hir::{Expr, ImplItemKind};
49+
50+
struct FindPanicUnimplementedUnreachable {
51+
result: Vec<Span>,
52+
}
53+
54+
impl<'tcx> Visitor<'tcx> for FindPanicUnimplementedUnreachable {
55+
type Map = Map<'tcx>;
56+
57+
fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
58+
if is_expn_of(expr.span, "unimplemented").is_some() {
59+
self.result.push(expr.span);
60+
} else if is_expn_of(expr.span, "unreachable").is_some() {
61+
self.result.push(expr.span);
62+
} else if is_expn_of(expr.span, "panic").is_some() {
63+
self.result.push(expr.span);
64+
}
65+
66+
// and check sub-expressions
67+
intravisit::walk_expr(self, expr);
68+
}
69+
70+
fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
71+
NestedVisitorMap::None
72+
}
73+
}
74+
75+
fn lint_impl_body<'tcx>(cx: &LateContext<'tcx>, impl_span: Span, impl_item: &'tcx hir::ImplItem<'_>) {
76+
if_chain! {
77+
if let ImplItemKind::Fn(_, body_id) = impl_item.kind;
78+
then {
79+
let body = cx.tcx.hir().body(body_id);
80+
let mut fpu = FindPanicUnimplementedUnreachable {
81+
result: Vec::new(),
82+
};
83+
fpu.visit_expr(&body.value);
84+
85+
// if we've found one, lint
86+
if !fpu.result.is_empty() {
87+
span_lint_and_then(
88+
cx,
89+
PANIC_IN_RESULT,
90+
impl_span,
91+
"used unimplemented, unreachable or panic in a function that returns result or option",
92+
move |diag| {
93+
diag.help(
94+
"unimplemented, unreachable or panic should not be used in a function that returns result or option" );
95+
diag.span_note(fpu.result, "will cause the application to crash.");
96+
});
97+
}
98+
}
99+
}
100+
}

src/lintlist/mod.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1704,6 +1704,13 @@ pub static ref ALL_LINTS: Vec<Lint> = vec![
17041704
deprecation: None,
17051705
module: "panic_unimplemented",
17061706
},
1707+
Lint {
1708+
name: "panic_in_result",
1709+
group: "restriction",
1710+
desc: "default lint description",
1711+
deprecation: None,
1712+
module: "panic_in_result",
1713+
},
17071714
Lint {
17081715
name: "panic_params",
17091716
group: "style",

tests/ui/panic_in_result.rs

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
#![warn(clippy::panic_in_result)]
2+
3+
struct A;
4+
5+
impl A {
6+
fn result_with_panic() -> Result<bool, String> // should emit lint
7+
{
8+
panic!("error");
9+
}
10+
11+
fn result_with_unimplemented() -> Result<bool, String> // should emit lint
12+
{
13+
unimplemented!();
14+
}
15+
16+
fn result_with_unreachable() -> Result<bool, String> // should emit lint
17+
{
18+
unreachable!();
19+
}
20+
21+
fn option_with_unreachable() -> Option<bool> // should emit lint
22+
{
23+
unreachable!();
24+
}
25+
26+
fn option_with_unimplemented() -> Option<bool> // should emit lint
27+
{
28+
unimplemented!();
29+
}
30+
31+
fn option_with_panic() -> Option<bool> // should emit lint
32+
{
33+
panic!("error");
34+
}
35+
36+
fn other_with_panic() // should not emit lint
37+
{
38+
panic!("");
39+
}
40+
41+
fn other_with_unreachable() // should not emit lint
42+
{
43+
unreachable!();
44+
}
45+
46+
fn other_with_unimplemented() // should not emit lint
47+
{
48+
unimplemented!();
49+
}
50+
51+
fn result_without_banned_functions() -> Result<bool, String> // should not emit lint
52+
{
53+
Ok(true)
54+
}
55+
56+
fn option_without_banned_functions() -> Option<bool> // should not emit lint
57+
{
58+
Some(true)
59+
}
60+
}
61+
62+
fn main() {}

tests/ui/panic_in_result.stderr

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
error: used unimplemented, unreachable or panic in a function that returns result or option
2+
--> $DIR/panic_in_result.rs:6:5
3+
|
4+
LL | / fn result_with_panic() -> Result<bool, String> // should emit lint
5+
LL | | {
6+
LL | | panic!("error");
7+
LL | | }
8+
| |_____^
9+
|
10+
= note: `-D clippy::panic-in-result` implied by `-D warnings`
11+
= help: unimplemented, unreachable or panic should not be used in a function that returns result or option
12+
note: will cause the application to crash.
13+
--> $DIR/panic_in_result.rs:8:9
14+
|
15+
LL | panic!("error");
16+
| ^^^^^^^^^^^^^^^^
17+
= note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
18+
19+
error: used unimplemented, unreachable or panic in a function that returns result or option
20+
--> $DIR/panic_in_result.rs:11:5
21+
|
22+
LL | / fn result_with_unimplemented() -> Result<bool, String> // should emit lint
23+
LL | | {
24+
LL | | unimplemented!();
25+
LL | | }
26+
| |_____^
27+
|
28+
= help: unimplemented, unreachable or panic should not be used in a function that returns result or option
29+
note: will cause the application to crash.
30+
--> $DIR/panic_in_result.rs:13:9
31+
|
32+
LL | unimplemented!();
33+
| ^^^^^^^^^^^^^^^^^
34+
= note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
35+
36+
error: used unimplemented, unreachable or panic in a function that returns result or option
37+
--> $DIR/panic_in_result.rs:16:5
38+
|
39+
LL | / fn result_with_unreachable() -> Result<bool, String> // should emit lint
40+
LL | | {
41+
LL | | unreachable!();
42+
LL | | }
43+
| |_____^
44+
|
45+
= help: unimplemented, unreachable or panic should not be used in a function that returns result or option
46+
note: will cause the application to crash.
47+
--> $DIR/panic_in_result.rs:18:9
48+
|
49+
LL | unreachable!();
50+
| ^^^^^^^^^^^^^^^
51+
= note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
52+
53+
error: used unimplemented, unreachable or panic in a function that returns result or option
54+
--> $DIR/panic_in_result.rs:21:5
55+
|
56+
LL | / fn option_with_unreachable() -> Option<bool> // should emit lint
57+
LL | | {
58+
LL | | unreachable!();
59+
LL | | }
60+
| |_____^
61+
|
62+
= help: unimplemented, unreachable or panic should not be used in a function that returns result or option
63+
note: will cause the application to crash.
64+
--> $DIR/panic_in_result.rs:23:9
65+
|
66+
LL | unreachable!();
67+
| ^^^^^^^^^^^^^^^
68+
= note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
69+
70+
error: used unimplemented, unreachable or panic in a function that returns result or option
71+
--> $DIR/panic_in_result.rs:26:5
72+
|
73+
LL | / fn option_with_unimplemented() -> Option<bool> // should emit lint
74+
LL | | {
75+
LL | | unimplemented!();
76+
LL | | }
77+
| |_____^
78+
|
79+
= help: unimplemented, unreachable or panic should not be used in a function that returns result or option
80+
note: will cause the application to crash.
81+
--> $DIR/panic_in_result.rs:28:9
82+
|
83+
LL | unimplemented!();
84+
| ^^^^^^^^^^^^^^^^^
85+
= note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
86+
87+
error: used unimplemented, unreachable or panic in a function that returns result or option
88+
--> $DIR/panic_in_result.rs:31:5
89+
|
90+
LL | / fn option_with_panic() -> Option<bool> // should emit lint
91+
LL | | {
92+
LL | | panic!("error");
93+
LL | | }
94+
| |_____^
95+
|
96+
= help: unimplemented, unreachable or panic should not be used in a function that returns result or option
97+
note: will cause the application to crash.
98+
--> $DIR/panic_in_result.rs:33:9
99+
|
100+
LL | panic!("error");
101+
| ^^^^^^^^^^^^^^^^
102+
= note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
103+
104+
error: aborting due to 6 previous errors
105+

0 commit comments

Comments
 (0)