]>
Commit | Line | Data |
---|---|---|
cdc7bbd5 XL |
1 | use clippy_utils::diagnostics::span_lint_and_then; |
2 | use clippy_utils::ty::is_type_diagnostic_item; | |
2b03887a | 3 | use clippy_utils::visitors::for_each_expr; |
cdc7bbd5 | 4 | use clippy_utils::{method_chain_args, return_ty}; |
2b03887a | 5 | use core::ops::ControlFlow; |
f20569fa XL |
6 | use if_chain::if_chain; |
7 | use rustc_hir as hir; | |
2b03887a | 8 | use rustc_hir::ImplItemKind; |
f20569fa | 9 | use rustc_lint::{LateContext, LateLintPass}; |
f20569fa XL |
10 | use rustc_session::{declare_lint_pass, declare_tool_lint}; |
11 | use rustc_span::{sym, Span}; | |
12 | ||
13 | declare_clippy_lint! { | |
94222f64 | 14 | /// ### What it does |
3c0e092e | 15 | /// Checks for functions of type `Result` that contain `expect()` or `unwrap()` |
f20569fa | 16 | /// |
94222f64 XL |
17 | /// ### Why is this bad? |
18 | /// These functions promote recoverable errors to non-recoverable errors which may be undesirable in code bases which wish to avoid panics. | |
f20569fa | 19 | /// |
94222f64 XL |
20 | /// ### Known problems |
21 | /// This can cause false positives in functions that handle both recoverable and non recoverable errors. | |
f20569fa | 22 | /// |
94222f64 | 23 | /// ### Example |
f20569fa XL |
24 | /// Before: |
25 | /// ```rust | |
26 | /// fn divisible_by_3(i_str: String) -> Result<(), String> { | |
27 | /// let i = i_str | |
28 | /// .parse::<i32>() | |
29 | /// .expect("cannot divide the input by three"); | |
30 | /// | |
31 | /// if i % 3 != 0 { | |
32 | /// Err("Number is not divisible by 3")? | |
33 | /// } | |
34 | /// | |
35 | /// Ok(()) | |
36 | /// } | |
37 | /// ``` | |
38 | /// | |
39 | /// After: | |
40 | /// ```rust | |
41 | /// fn divisible_by_3(i_str: String) -> Result<(), String> { | |
42 | /// let i = i_str | |
43 | /// .parse::<i32>() | |
44 | /// .map_err(|e| format!("cannot divide the input by three: {}", e))?; | |
45 | /// | |
46 | /// if i % 3 != 0 { | |
47 | /// Err("Number is not divisible by 3")? | |
48 | /// } | |
49 | /// | |
50 | /// Ok(()) | |
51 | /// } | |
52 | /// ``` | |
a2a8927a | 53 | #[clippy::version = "1.48.0"] |
f20569fa XL |
54 | pub UNWRAP_IN_RESULT, |
55 | restriction, | |
56 | "functions of type `Result<..>` or `Option`<...> that contain `expect()` or `unwrap()`" | |
57 | } | |
58 | ||
59 | declare_lint_pass!(UnwrapInResult=> [UNWRAP_IN_RESULT]); | |
60 | ||
61 | impl<'tcx> LateLintPass<'tcx> for UnwrapInResult { | |
62 | fn check_impl_item(&mut self, cx: &LateContext<'tcx>, impl_item: &'tcx hir::ImplItem<'_>) { | |
63 | if_chain! { | |
64 | // first check if it's a method or function | |
65 | if let hir::ImplItemKind::Fn(ref _signature, _) = impl_item.kind; | |
66 | // checking if its return type is `result` or `option` | |
c295e0f8 XL |
67 | if is_type_diagnostic_item(cx, return_ty(cx, impl_item.hir_id()), sym::Result) |
68 | || is_type_diagnostic_item(cx, return_ty(cx, impl_item.hir_id()), sym::Option); | |
f20569fa XL |
69 | then { |
70 | lint_impl_body(cx, impl_item.span, impl_item); | |
71 | } | |
72 | } | |
73 | } | |
74 | } | |
75 | ||
2b03887a FG |
76 | fn lint_impl_body<'tcx>(cx: &LateContext<'tcx>, impl_span: Span, impl_item: &'tcx hir::ImplItem<'_>) { |
77 | if let ImplItemKind::Fn(_, body_id) = impl_item.kind { | |
78 | let body = cx.tcx.hir().body(body_id); | |
79 | let typeck = cx.tcx.typeck(impl_item.owner_id.def_id); | |
80 | let mut result = Vec::new(); | |
81 | let _: Option<!> = for_each_expr(body.value, |e| { | |
82 | // check for `expect` | |
83 | if let Some(arglists) = method_chain_args(e, &["expect"]) { | |
84 | let receiver_ty = typeck.expr_ty(arglists[0].0).peel_refs(); | |
85 | if is_type_diagnostic_item(cx, receiver_ty, sym::Option) | |
86 | || is_type_diagnostic_item(cx, receiver_ty, sym::Result) | |
87 | { | |
88 | result.push(e.span); | |
89 | } | |
f20569fa | 90 | } |
f20569fa | 91 | |
2b03887a FG |
92 | // check for `unwrap` |
93 | if let Some(arglists) = method_chain_args(e, &["unwrap"]) { | |
94 | let receiver_ty = typeck.expr_ty(arglists[0].0).peel_refs(); | |
95 | if is_type_diagnostic_item(cx, receiver_ty, sym::Option) | |
96 | || is_type_diagnostic_item(cx, receiver_ty, sym::Result) | |
97 | { | |
98 | result.push(e.span); | |
99 | } | |
f20569fa | 100 | } |
f20569fa | 101 | |
2b03887a FG |
102 | ControlFlow::Continue(()) |
103 | }); | |
f20569fa | 104 | |
cdc7bbd5 | 105 | // if we've found one, lint |
2b03887a | 106 | if !result.is_empty() { |
cdc7bbd5 XL |
107 | span_lint_and_then( |
108 | cx, | |
109 | UNWRAP_IN_RESULT, | |
110 | impl_span, | |
111 | "used unwrap or expect in a function that returns result or option", | |
112 | move |diag| { | |
113 | diag.help("unwrap and expect should not be used in a function that returns result or option"); | |
2b03887a | 114 | diag.span_note(result, "potential non-recoverable error(s)"); |
cdc7bbd5 XL |
115 | }, |
116 | ); | |
f20569fa XL |
117 | } |
118 | } | |
119 | } |