1 use crate::rustc_lint
::LintContext
;
2 use clippy_utils
::diagnostics
::span_lint_and_then
;
3 use clippy_utils
::macros
::root_macro_call
;
4 use clippy_utils
::{is_else_clause, peel_blocks_with_stmt, span_extract_comment, sugg}
;
5 use rustc_errors
::Applicability
;
6 use rustc_hir
::{Expr, ExprKind, UnOp}
;
7 use rustc_lint
::{LateContext, LateLintPass}
;
8 use rustc_session
::declare_lint_pass
;
11 declare_clippy_lint
! {
13 /// Detects `if`-then-`panic!` that can be replaced with `assert!`.
15 /// ### Why is this bad?
16 /// `assert!` is simpler than `if`-then-`panic!`.
20 /// let sad_people: Vec<&str> = vec![];
21 /// if !sad_people.is_empty() {
22 /// panic!("there are sad people: {:?}", sad_people);
27 /// let sad_people: Vec<&str> = vec![];
28 /// assert!(sad_people.is_empty(), "there are sad people: {:?}", sad_people);
30 #[clippy::version = "1.57.0"]
33 "`panic!` and only a `panic!` in `if`-then statement"
36 declare_lint_pass
!(ManualAssert
=> [MANUAL_ASSERT
]);
38 impl<'tcx
> LateLintPass
<'tcx
> for ManualAssert
{
39 fn check_expr(&mut self, cx
: &LateContext
<'tcx
>, expr
: &Expr
<'tcx
>) {
40 if let ExprKind
::If(cond
, then
, None
) = expr
.kind
41 && !matches
!(cond
.kind
, ExprKind
::Let(_
))
42 && !expr
.span
.from_expansion()
43 && let then
= peel_blocks_with_stmt(then
)
44 && let Some(macro_call
) = root_macro_call(then
.span
)
45 && cx
.tcx
.item_name(macro_call
.def_id
) == sym
::panic
46 && !cx
.tcx
.sess
.source_map().is_multiline(cond
.span
)
47 && let Ok(panic_snippet
) = cx
.sess().source_map().span_to_snippet(macro_call
.span
)
48 && let Some(panic_snippet
) = panic_snippet
.strip_suffix('
)'
)
49 && let Some((_
, format_args_snip
)) = panic_snippet
.split_once('
('
)
50 // Don't change `else if foo { panic!(..) }` to `else { assert!(foo, ..) }` as it just
51 // shuffles the condition around.
52 // Should this have a config value?
53 && !is_else_clause(cx
.tcx
, expr
)
55 let mut applicability
= Applicability
::MachineApplicable
;
56 let cond
= cond
.peel_drop_temps();
57 let mut comments
= span_extract_comment(cx
.sess().source_map(), expr
.span
);
58 if !comments
.is_empty() {
61 let (cond
, not
) = match cond
.kind
{
62 ExprKind
::Unary(UnOp
::Not
, e
) => (e
, ""),
65 let cond_sugg
= sugg
::Sugg
::hir_with_applicability(cx
, cond
, "..", &mut applicability
).maybe_par();
66 let sugg
= format
!("assert!({not}{cond_sugg}, {format_args_snip});");
67 // we show to the user the suggestion without the comments, but when applying the fix, include the
68 // comments in the block
73 "only a `panic!` in `if`-then statement",
75 // comments can be noisy, do not show them to the user
76 if !comments
.is_empty() {
77 diag
.tool_only_span_suggestion(
78 expr
.span
.shrink_to_lo(),
84 diag
.span_suggestion(expr
.span
, "try instead", sugg
, applicability
);