• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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, declare_tool_lint};
9 use rustc_span::sym;
10 
11 declare_clippy_lint! {
12     /// ### What it does
13     /// Detects `if`-then-`panic!` that can be replaced with `assert!`.
14     ///
15     /// ### Why is this bad?
16     /// `assert!` is simpler than `if`-then-`panic!`.
17     ///
18     /// ### Example
19     /// ```rust
20     /// let sad_people: Vec<&str> = vec![];
21     /// if !sad_people.is_empty() {
22     ///     panic!("there are sad people: {:?}", sad_people);
23     /// }
24     /// ```
25     /// Use instead:
26     /// ```rust
27     /// let sad_people: Vec<&str> = vec![];
28     /// assert!(sad_people.is_empty(), "there are sad people: {:?}", sad_people);
29     /// ```
30     #[clippy::version = "1.57.0"]
31     pub MANUAL_ASSERT,
32     pedantic,
33     "`panic!` and only a `panic!` in `if`-then statement"
34 }
35 
36 declare_lint_pass!(ManualAssert => [MANUAL_ASSERT]);
37 
38 impl<'tcx> LateLintPass<'tcx> for ManualAssert {
check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'tcx>)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)
54         {
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() {
59                 comments += "\n";
60             }
61             let (cond, not) = match cond.kind {
62                 ExprKind::Unary(UnOp::Not, e) => (e, ""),
63                 _ => (cond, "!"),
64             };
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 comments in the block
68             span_lint_and_then(
69                 cx,
70                 MANUAL_ASSERT,
71                 expr.span,
72                 "only a `panic!` in `if`-then statement",
73                 |diag| {
74                     // comments can be noisy, do not show them to the user
75                     if !comments.is_empty() {
76                         diag.tool_only_span_suggestion(
77                             expr.span.shrink_to_lo(),
78                             "add comments back",
79                             comments,
80                             applicability
81                         );
82                     }
83                     diag.span_suggestion(
84                         expr.span,
85                         "try instead",
86                         sugg,
87                         applicability
88                     );
89                 }
90             );
91         }
92     }
93 }
94