Skip to content

Commit 3a668a8

Browse files
committed
panic_in_result_fn: Extend to also check usages of [debug_]assert* macros
Also, the macro-finding logic has been moved to the util module, for use by future lints.
1 parent 249b6fe commit 3a668a8

7 files changed

+285
-50
lines changed

clippy_lints/src/panic_in_result_fn.rs

Lines changed: 31 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,16 @@
1-
use crate::utils::{is_expn_of, is_type_diagnostic_item, return_ty, span_lint_and_then};
1+
use crate::utils::{find_macro_calls, is_type_diagnostic_item, return_ty, span_lint_and_then};
22
use rustc_hir as hir;
3-
use rustc_hir::intravisit::{self, FnKind, NestedVisitorMap, Visitor};
4-
use rustc_hir::Expr;
3+
use rustc_hir::intravisit::FnKind;
54
use rustc_lint::{LateContext, LateLintPass};
6-
use rustc_middle::hir::map::Map;
75
use rustc_session::{declare_lint_pass, declare_tool_lint};
86
use rustc_span::{sym, Span};
97

108
declare_clippy_lint! {
11-
/// **What it does:** Checks for usage of `panic!`, `unimplemented!`, `todo!` or `unreachable!` in a function of type result.
9+
/// **What it does:** Checks for usage of `panic!`, `unimplemented!`, `todo!`, `unreachable!` or assertions in a function of type result.
1210
///
13-
/// **Why is this bad?** For some codebases, it is desirable for functions of type result to return an error instead of crashing. Hence unimplemented, panic and unreachable should be avoided.
11+
/// **Why is this bad?** For some codebases, it is desirable for functions of type result to return an error instead of crashing. Hence panicking macros should be avoided.
1412
///
15-
/// **Known problems:** None.
13+
/// **Known problems:** Functions called from a function returning a `Result` may invoke a panicking macro. This is not checked.
1614
///
1715
/// **Example:**
1816
///
@@ -22,9 +20,15 @@ declare_clippy_lint! {
2220
/// panic!("error");
2321
/// }
2422
/// ```
23+
/// Use instead:
24+
/// ```rust
25+
/// fn result_without_panic() -> Result<bool, String> {
26+
/// Err(String::from("error"))
27+
/// }
28+
/// ```
2529
pub PANIC_IN_RESULT_FN,
2630
restriction,
27-
"functions of type `Result<..>` that contain `panic!()`, `todo!()` or `unreachable()` or `unimplemented()` "
31+
"functions of type `Result<..>` that contain `panic!()`, `todo!()`, `unreachable()`, `unimplemented()` or assertion"
2832
}
2933

3034
declare_lint_pass!(PanicInResultFn => [PANIC_IN_RESULT_FN]);
@@ -47,43 +51,33 @@ impl<'tcx> LateLintPass<'tcx> for PanicInResultFn {
4751
}
4852
}
4953

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 ["unimplemented", "unreachable", "panic", "todo"]
59-
.iter()
60-
.any(|fun| is_expn_of(expr.span, fun).is_some())
61-
{
62-
self.result.push(expr.span);
63-
}
64-
// and check sub-expressions
65-
intravisit::walk_expr(self, expr);
66-
}
67-
68-
fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
69-
NestedVisitorMap::None
70-
}
71-
}
72-
7354
fn lint_impl_body<'tcx>(cx: &LateContext<'tcx>, impl_span: Span, body: &'tcx hir::Body<'tcx>) {
74-
let mut panics = FindPanicUnimplementedUnreachable { result: Vec::new() };
75-
panics.visit_expr(&body.value);
76-
if !panics.result.is_empty() {
55+
let panics = find_macro_calls(
56+
vec![
57+
"unimplemented",
58+
"unreachable",
59+
"panic",
60+
"todo",
61+
"assert",
62+
"assert_eq",
63+
"assert_ne",
64+
"debug_assert",
65+
"debug_assert_eq",
66+
"debug_assert_ne",
67+
],
68+
body,
69+
);
70+
if !panics.is_empty() {
7771
span_lint_and_then(
7872
cx,
7973
PANIC_IN_RESULT_FN,
8074
impl_span,
81-
"used `unimplemented!()`, `unreachable!()`, `todo!()` or `panic!()` in a function that returns `Result`",
75+
"used `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertion in a function that returns `Result`",
8276
move |diag| {
8377
diag.help(
84-
"`unimplemented!()`, `unreachable!()`, `todo!()` or `panic!()` should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing",
78+
"`unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertions should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing",
8579
);
86-
diag.span_note(panics.result, "return Err() instead of panicking");
80+
diag.span_note(panics, "return Err() instead of panicking");
8781
},
8882
);
8983
}

clippy_lints/src/utils/mod.rs

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ use rustc_errors::Applicability;
4141
use rustc_hir as hir;
4242
use rustc_hir::def::{DefKind, Res};
4343
use rustc_hir::def_id::{DefId, CRATE_DEF_INDEX, LOCAL_CRATE};
44-
use rustc_hir::intravisit::{NestedVisitorMap, Visitor};
44+
use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor};
4545
use rustc_hir::Node;
4646
use rustc_hir::{
4747
def, Arm, Block, Body, Constness, Crate, Expr, ExprKind, FnDecl, HirId, ImplItem, ImplItemKind, Item, ItemKind,
@@ -603,6 +603,37 @@ pub fn contains_return(expr: &hir::Expr<'_>) -> bool {
603603
visitor.found
604604
}
605605

606+
struct FindMacroCalls<'a> {
607+
names: Vec<&'a str>,
608+
result: Vec<Span>,
609+
}
610+
611+
impl<'a, 'tcx> Visitor<'tcx> for FindMacroCalls<'a> {
612+
type Map = Map<'tcx>;
613+
614+
fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
615+
if self.names.iter().any(|fun| is_expn_of(expr.span, fun).is_some()) {
616+
self.result.push(expr.span);
617+
}
618+
// and check sub-expressions
619+
intravisit::walk_expr(self, expr);
620+
}
621+
622+
fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
623+
NestedVisitorMap::None
624+
}
625+
}
626+
627+
/// Finds calls of the specified macros in a function body.
628+
pub fn find_macro_calls(names: Vec<&str>, body: &'tcx Body<'tcx>) -> Vec<Span> {
629+
let mut fmc = FindMacroCalls {
630+
names,
631+
result: Vec::new(),
632+
};
633+
fmc.visit_expr(&body.value);
634+
fmc.result
635+
}
636+
606637
/// Converts a span to a code snippet if available, otherwise use default.
607638
///
608639
/// This is useful if you want to provide suggestions for your lint or more generally, if you want

tests/ui/panic_in_result_fn.stderr

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
error: used `unimplemented!()`, `unreachable!()`, `todo!()` or `panic!()` in a function that returns `Result`
1+
error: used `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertion in a function that returns `Result`
22
--> $DIR/panic_in_result_fn.rs:7:5
33
|
44
LL | / fn result_with_panic() -> Result<bool, String> // should emit lint
@@ -8,15 +8,15 @@ LL | | }
88
| |_____^
99
|
1010
= note: `-D clippy::panic-in-result-fn` implied by `-D warnings`
11-
= help: `unimplemented!()`, `unreachable!()`, `todo!()` or `panic!()` should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing
11+
= help: `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertions should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing
1212
note: return Err() instead of panicking
1313
--> $DIR/panic_in_result_fn.rs:9:9
1414
|
1515
LL | panic!("error");
1616
| ^^^^^^^^^^^^^^^^
1717
= note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
1818

19-
error: used `unimplemented!()`, `unreachable!()`, `todo!()` or `panic!()` in a function that returns `Result`
19+
error: used `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertion in a function that returns `Result`
2020
--> $DIR/panic_in_result_fn.rs:12:5
2121
|
2222
LL | / fn result_with_unimplemented() -> Result<bool, String> // should emit lint
@@ -25,15 +25,15 @@ LL | | unimplemented!();
2525
LL | | }
2626
| |_____^
2727
|
28-
= help: `unimplemented!()`, `unreachable!()`, `todo!()` or `panic!()` should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing
28+
= help: `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertions should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing
2929
note: return Err() instead of panicking
3030
--> $DIR/panic_in_result_fn.rs:14:9
3131
|
3232
LL | unimplemented!();
3333
| ^^^^^^^^^^^^^^^^^
3434
= note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
3535

36-
error: used `unimplemented!()`, `unreachable!()`, `todo!()` or `panic!()` in a function that returns `Result`
36+
error: used `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertion in a function that returns `Result`
3737
--> $DIR/panic_in_result_fn.rs:17:5
3838
|
3939
LL | / fn result_with_unreachable() -> Result<bool, String> // should emit lint
@@ -42,15 +42,15 @@ LL | | unreachable!();
4242
LL | | }
4343
| |_____^
4444
|
45-
= help: `unimplemented!()`, `unreachable!()`, `todo!()` or `panic!()` should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing
45+
= help: `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertions should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing
4646
note: return Err() instead of panicking
4747
--> $DIR/panic_in_result_fn.rs:19:9
4848
|
4949
LL | unreachable!();
5050
| ^^^^^^^^^^^^^^^
5151
= note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
5252

53-
error: used `unimplemented!()`, `unreachable!()`, `todo!()` or `panic!()` in a function that returns `Result`
53+
error: used `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertion in a function that returns `Result`
5454
--> $DIR/panic_in_result_fn.rs:22:5
5555
|
5656
LL | / fn result_with_todo() -> Result<bool, String> // should emit lint
@@ -59,15 +59,15 @@ LL | | todo!("Finish this");
5959
LL | | }
6060
| |_____^
6161
|
62-
= help: `unimplemented!()`, `unreachable!()`, `todo!()` or `panic!()` should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing
62+
= help: `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertions should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing
6363
note: return Err() instead of panicking
6464
--> $DIR/panic_in_result_fn.rs:24:9
6565
|
6666
LL | todo!("Finish this");
6767
| ^^^^^^^^^^^^^^^^^^^^^
6868
= note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
6969

70-
error: used `unimplemented!()`, `unreachable!()`, `todo!()` or `panic!()` in a function that returns `Result`
70+
error: used `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertion in a function that returns `Result`
7171
--> $DIR/panic_in_result_fn.rs:53:1
7272
|
7373
LL | / fn function_result_with_panic() -> Result<bool, String> // should emit lint
@@ -76,15 +76,15 @@ LL | | panic!("error");
7676
LL | | }
7777
| |_^
7878
|
79-
= help: `unimplemented!()`, `unreachable!()`, `todo!()` or `panic!()` should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing
79+
= help: `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertions should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing
8080
note: return Err() instead of panicking
8181
--> $DIR/panic_in_result_fn.rs:55:5
8282
|
8383
LL | panic!("error");
8484
| ^^^^^^^^^^^^^^^^
8585
= note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
8686

87-
error: used `unimplemented!()`, `unreachable!()`, `todo!()` or `panic!()` in a function that returns `Result`
87+
error: used `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertion in a function that returns `Result`
8888
--> $DIR/panic_in_result_fn.rs:68:1
8989
|
9090
LL | / fn main() -> Result<(), String> {
@@ -93,7 +93,7 @@ LL | | Ok(())
9393
LL | | }
9494
| |_^
9595
|
96-
= help: `unimplemented!()`, `unreachable!()`, `todo!()` or `panic!()` should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing
96+
= help: `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertions should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing
9797
note: return Err() instead of panicking
9898
--> $DIR/panic_in_result_fn.rs:69:5
9999
|
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
#![warn(clippy::panic_in_result_fn)]
2+
#![allow(clippy::unnecessary_wraps)]
3+
4+
struct A;
5+
6+
impl A {
7+
fn result_with_assert_with_message(x: i32) -> Result<bool, String> // should emit lint
8+
{
9+
assert!(x == 5, "wrong argument");
10+
Ok(true)
11+
}
12+
13+
fn result_with_assert_eq(x: i32) -> Result<bool, String> // should emit lint
14+
{
15+
assert_eq!(x, 5);
16+
Ok(true)
17+
}
18+
19+
fn result_with_assert_ne(x: i32) -> Result<bool, String> // should emit lint
20+
{
21+
assert_ne!(x, 1);
22+
Ok(true)
23+
}
24+
25+
fn other_with_assert_with_message(x: i32) // should not emit lint
26+
{
27+
assert!(x == 5, "wrong argument");
28+
}
29+
30+
fn other_with_assert_eq(x: i32) // should not emit lint
31+
{
32+
assert_eq!(x, 5);
33+
}
34+
35+
fn other_with_assert_ne(x: i32) // should not emit lint
36+
{
37+
assert_ne!(x, 1);
38+
}
39+
40+
fn result_without_banned_functions() -> Result<bool, String> // should not emit lint
41+
{
42+
let assert = "assert!";
43+
println!("No {}", assert);
44+
Ok(true)
45+
}
46+
}
47+
48+
fn main() {}
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
error: used `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertion in a function that returns `Result`
2+
--> $DIR/panic_in_result_fn_assertions.rs:7:5
3+
|
4+
LL | / fn result_with_assert_with_message(x: i32) -> Result<bool, String> // should emit lint
5+
LL | | {
6+
LL | | assert!(x == 5, "wrong argument");
7+
LL | | Ok(true)
8+
LL | | }
9+
| |_____^
10+
|
11+
= note: `-D clippy::panic-in-result-fn` implied by `-D warnings`
12+
= help: `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertions should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing
13+
note: return Err() instead of panicking
14+
--> $DIR/panic_in_result_fn_assertions.rs:9:9
15+
|
16+
LL | assert!(x == 5, "wrong argument");
17+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
18+
= note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
19+
20+
error: used `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertion in a function that returns `Result`
21+
--> $DIR/panic_in_result_fn_assertions.rs:13:5
22+
|
23+
LL | / fn result_with_assert_eq(x: i32) -> Result<bool, String> // should emit lint
24+
LL | | {
25+
LL | | assert_eq!(x, 5);
26+
LL | | Ok(true)
27+
LL | | }
28+
| |_____^
29+
|
30+
= help: `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertions should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing
31+
note: return Err() instead of panicking
32+
--> $DIR/panic_in_result_fn_assertions.rs:15:9
33+
|
34+
LL | assert_eq!(x, 5);
35+
| ^^^^^^^^^^^^^^^^^
36+
= note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
37+
38+
error: used `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertion in a function that returns `Result`
39+
--> $DIR/panic_in_result_fn_assertions.rs:19:5
40+
|
41+
LL | / fn result_with_assert_ne(x: i32) -> Result<bool, String> // should emit lint
42+
LL | | {
43+
LL | | assert_ne!(x, 1);
44+
LL | | Ok(true)
45+
LL | | }
46+
| |_____^
47+
|
48+
= help: `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertions should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing
49+
note: return Err() instead of panicking
50+
--> $DIR/panic_in_result_fn_assertions.rs:21:9
51+
|
52+
LL | assert_ne!(x, 1);
53+
| ^^^^^^^^^^^^^^^^^
54+
= note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
55+
56+
error: aborting due to 3 previous errors
57+

0 commit comments

Comments
 (0)