use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::macros::root_macro_call_first_node;
+use clippy_utils::return_ty;
use clippy_utils::ty::is_type_diagnostic_item;
-use clippy_utils::{find_macro_calls, return_ty};
+use clippy_utils::visitors::{for_each_expr, Descend};
+use core::ops::ControlFlow;
use rustc_hir as hir;
use rustc_hir::intravisit::FnKind;
use rustc_lint::{LateContext, LateLintPass};
use rustc_span::{sym, Span};
declare_clippy_lint! {
- /// **What it does:** Checks for usage of `panic!`, `unimplemented!`, `todo!`, `unreachable!` or assertions in a function of type result.
+ /// ### What it does
+ /// Checks for usage of `panic!`, `unimplemented!`, `todo!`, `unreachable!` or assertions in a function of type result.
///
- /// **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.
+ /// ### 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.
///
- /// **Known problems:** Functions called from a function returning a `Result` may invoke a panicking macro. This is not checked.
- ///
- /// **Example:**
+ /// ### Known problems
+ /// Functions called from a function returning a `Result` may invoke a panicking macro. This is not checked.
///
+ /// ### Example
/// ```rust
/// fn result_with_panic() -> Result<bool, String>
/// {
/// Err(String::from("error"))
/// }
/// ```
+ #[clippy::version = "1.48.0"]
pub PANIC_IN_RESULT_FN,
restriction,
"functions of type `Result<..>` that contain `panic!()`, `todo!()`, `unreachable()`, `unimplemented()` or assertion"
span: Span,
hir_id: hir::HirId,
) {
- if !matches!(fn_kind, FnKind::Closure) && is_type_diagnostic_item(cx, return_ty(cx, hir_id), sym::result_type) {
+ if !matches!(fn_kind, FnKind::Closure) && is_type_diagnostic_item(cx, return_ty(cx, hir_id), sym::Result) {
lint_impl_body(cx, span, body);
}
}
}
fn lint_impl_body<'tcx>(cx: &LateContext<'tcx>, impl_span: Span, body: &'tcx hir::Body<'tcx>) {
- let panics = find_macro_calls(
- &[
- "unimplemented",
- "unreachable",
- "panic",
- "todo",
- "assert",
- "assert_eq",
- "assert_ne",
- "debug_assert",
- "debug_assert_eq",
- "debug_assert_ne",
- ],
- body,
- );
+ let mut panics = Vec::new();
+ let _: Option<!> = for_each_expr(body.value, |e| {
+ let Some(macro_call) = root_macro_call_first_node(cx, e) else {
+ return ControlFlow::Continue(Descend::Yes);
+ };
+ if matches!(
+ cx.tcx.item_name(macro_call.def_id).as_str(),
+ "unimplemented" | "unreachable" | "panic" | "todo" | "assert" | "assert_eq" | "assert_ne"
+ ) {
+ panics.push(macro_call.span);
+ ControlFlow::Continue(Descend::No)
+ } else {
+ ControlFlow::Continue(Descend::Yes)
+ }
+ });
if !panics.is_empty() {
span_lint_and_then(
cx,