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