1 use clippy_utils
::diagnostics
::span_lint_and_then
;
2 use clippy_utils
::ty
::is_type_diagnostic_item
;
3 use clippy_utils
::{method_chain_args, return_ty}
;
4 use if_chain
::if_chain
;
6 use rustc_hir
::intravisit
::{self, NestedVisitorMap, Visitor}
;
7 use rustc_hir
::{Expr, ImplItemKind}
;
8 use rustc_lint
::{LateContext, LateLintPass}
;
9 use rustc_middle
::hir
::map
::Map
;
11 use rustc_session
::{declare_lint_pass, declare_tool_lint}
;
12 use rustc_span
::{sym, Span}
;
14 declare_clippy_lint
! {
15 /// **What it does:** Checks for functions of type Result that contain `expect()` or `unwrap()`
17 /// **Why is this bad?** These functions promote recoverable errors to non-recoverable errors which may be undesirable in code bases which wish to avoid panics.
19 /// **Known problems:** This can cause false positives in functions that handle both recoverable and non recoverable errors.
24 /// fn divisible_by_3(i_str: String) -> Result<(), String> {
27 /// .expect("cannot divide the input by three");
30 /// Err("Number is not divisible by 3")?
39 /// fn divisible_by_3(i_str: String) -> Result<(), String> {
42 /// .map_err(|e| format!("cannot divide the input by three: {}", e))?;
45 /// Err("Number is not divisible by 3")?
53 "functions of type `Result<..>` or `Option`<...> that contain `expect()` or `unwrap()`"
56 declare_lint_pass
!(UnwrapInResult
=> [UNWRAP_IN_RESULT
]);
58 impl<'tcx
> LateLintPass
<'tcx
> for UnwrapInResult
{
59 fn check_impl_item(&mut self, cx
: &LateContext
<'tcx
>, impl_item
: &'tcx hir
::ImplItem
<'_
>) {
61 // first check if it's a method or function
62 if let hir
::ImplItemKind
::Fn(ref _signature
, _
) = impl_item
.kind
;
63 // checking if its return type is `result` or `option`
64 if is_type_diagnostic_item(cx
, return_ty(cx
, impl_item
.hir_id()), sym
::result_type
)
65 || is_type_diagnostic_item(cx
, return_ty(cx
, impl_item
.hir_id()), sym
::option_type
);
67 lint_impl_body(cx
, impl_item
.span
, impl_item
);
73 struct FindExpectUnwrap
<'a
, 'tcx
> {
74 lcx
: &'a LateContext
<'tcx
>,
75 typeck_results
: &'tcx ty
::TypeckResults
<'tcx
>,
79 impl<'a
, 'tcx
> Visitor
<'tcx
> for FindExpectUnwrap
<'a
, 'tcx
> {
82 fn visit_expr(&mut self, expr
: &'tcx Expr
<'_
>) {
84 if let Some(arglists
) = method_chain_args(expr
, &["expect"]) {
85 let reciever_ty
= self.typeck_results
.expr_ty(&arglists
[0][0]).peel_refs();
86 if is_type_diagnostic_item(self.lcx
, reciever_ty
, sym
::option_type
)
87 || is_type_diagnostic_item(self.lcx
, reciever_ty
, sym
::result_type
)
89 self.result
.push(expr
.span
);
94 if let Some(arglists
) = method_chain_args(expr
, &["unwrap"]) {
95 let reciever_ty
= self.typeck_results
.expr_ty(&arglists
[0][0]).peel_refs();
96 if is_type_diagnostic_item(self.lcx
, reciever_ty
, sym
::option_type
)
97 || is_type_diagnostic_item(self.lcx
, reciever_ty
, sym
::result_type
)
99 self.result
.push(expr
.span
);
103 // and check sub-expressions
104 intravisit
::walk_expr(self, expr
);
107 fn nested_visit_map(&mut self) -> NestedVisitorMap
<Self::Map
> {
108 NestedVisitorMap
::None
112 fn lint_impl_body
<'tcx
>(cx
: &LateContext
<'tcx
>, impl_span
: Span
, impl_item
: &'tcx hir
::ImplItem
<'_
>) {
113 if let ImplItemKind
::Fn(_
, body_id
) = impl_item
.kind
{
114 let body
= cx
.tcx
.hir().body(body_id
);
115 let mut fpu
= FindExpectUnwrap
{
117 typeck_results
: cx
.tcx
.typeck(impl_item
.def_id
),
120 fpu
.visit_expr(&body
.value
);
122 // if we've found one, lint
123 if !fpu
.result
.is_empty() {
128 "used unwrap or expect in a function that returns result or option",
130 diag
.help("unwrap and expect should not be used in a function that returns result or option");
131 diag
.span_note(fpu
.result
, "potential non-recoverable error(s)");