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