]> git.lizzy.rs Git - rust.git/blob - clippy_lints/src/panic_in_result_fn.rs
Merge remote-tracking branch 'upstream/beta' into backport_remerge
[rust.git] / clippy_lints / src / panic_in_result_fn.rs
1 use crate::utils::{is_expn_of, is_type_diagnostic_item, return_ty, span_lint_and_then};
2 use rustc_hir as hir;
3 use rustc_hir::intravisit::{self, FnKind, NestedVisitorMap, Visitor};
4 use rustc_hir::Expr;
5 use rustc_lint::{LateContext, LateLintPass};
6 use rustc_middle::hir::map::Map;
7 use rustc_session::{declare_lint_pass, declare_tool_lint};
8 use rustc_span::Span;
9
10 declare_clippy_lint! {
11     /// **What it does:** Checks for usage of `panic!`, `unimplemented!`, `todo!` or `unreachable!` in a function of type result.
12     ///
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.
14     ///
15     /// **Known problems:** None.
16     ///
17     /// **Example:**
18     ///
19     /// ```rust
20     /// fn result_with_panic() -> Result<bool, String>
21     /// {
22     ///     panic!("error");
23     /// }
24     /// ```
25     pub PANIC_IN_RESULT_FN,
26     restriction,
27     "functions of type `Result<..>` that contain `panic!()`, `todo!()` or `unreachable()` or `unimplemented()` "
28 }
29
30 declare_lint_pass!(PanicInResultFn  => [PANIC_IN_RESULT_FN]);
31
32 impl<'tcx> LateLintPass<'tcx> for PanicInResultFn {
33     fn check_fn(
34         &mut self,
35         cx: &LateContext<'tcx>,
36         fn_kind: FnKind<'tcx>,
37         _: &'tcx hir::FnDecl<'tcx>,
38         body: &'tcx hir::Body<'tcx>,
39         span: Span,
40         hir_id: hir::HirId,
41     ) {
42         if !matches!(fn_kind, FnKind::Closure(_))
43             && is_type_diagnostic_item(cx, return_ty(cx, hir_id), sym!(result_type))
44         {
45             lint_impl_body(cx, span, body);
46         }
47     }
48 }
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 ["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
73 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() {
77         span_lint_and_then(
78             cx,
79             PANIC_IN_RESULT_FN,
80             impl_span,
81             "used `unimplemented!()`, `unreachable!()`, `todo!()` or `panic!()` in a function that returns `Result`",
82             move |diag| {
83                 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",
85                 );
86                 diag.span_note(panics.result, "return Err() instead of panicking");
87             },
88         );
89     }
90 }