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
! {
16 /// Checks for functions of type Result that contain `expect()` or `unwrap()`
18 /// ### Why is this bad?
19 /// These functions promote recoverable errors to non-recoverable errors which may be undesirable in code bases which wish to avoid panics.
21 /// ### Known problems
22 /// This can cause false positives in functions that handle both recoverable and non recoverable errors.
27 /// fn divisible_by_3(i_str: String) -> Result<(), String> {
30 /// .expect("cannot divide the input by three");
33 /// Err("Number is not divisible by 3")?
42 /// fn divisible_by_3(i_str: String) -> Result<(), String> {
45 /// .map_err(|e| format!("cannot divide the input by three: {}", e))?;
48 /// Err("Number is not divisible by 3")?
56 "functions of type `Result<..>` or `Option`<...> that contain `expect()` or `unwrap()`"
59 declare_lint_pass
!(UnwrapInResult
=> [UNWRAP_IN_RESULT
]);
61 impl<'tcx
> LateLintPass
<'tcx
> for UnwrapInResult
{
62 fn check_impl_item(&mut self, cx
: &LateContext
<'tcx
>, impl_item
: &'tcx hir
::ImplItem
<'_
>) {
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`
67 if is_type_diagnostic_item(cx
, return_ty(cx
, impl_item
.hir_id()), sym
::result_type
)
68 || is_type_diagnostic_item(cx
, return_ty(cx
, impl_item
.hir_id()), sym
::option_type
);
70 lint_impl_body(cx
, impl_item
.span
, impl_item
);
76 struct FindExpectUnwrap
<'a
, 'tcx
> {
77 lcx
: &'a LateContext
<'tcx
>,
78 typeck_results
: &'tcx ty
::TypeckResults
<'tcx
>,
82 impl<'a
, 'tcx
> Visitor
<'tcx
> for FindExpectUnwrap
<'a
, 'tcx
> {
85 fn visit_expr(&mut self, expr
: &'tcx Expr
<'_
>) {
87 if let Some(arglists
) = method_chain_args(expr
, &["expect"]) {
88 let reciever_ty
= self.typeck_results
.expr_ty(&arglists
[0][0]).peel_refs();
89 if is_type_diagnostic_item(self.lcx
, reciever_ty
, sym
::option_type
)
90 || is_type_diagnostic_item(self.lcx
, reciever_ty
, sym
::result_type
)
92 self.result
.push(expr
.span
);
97 if let Some(arglists
) = method_chain_args(expr
, &["unwrap"]) {
98 let reciever_ty
= self.typeck_results
.expr_ty(&arglists
[0][0]).peel_refs();
99 if is_type_diagnostic_item(self.lcx
, reciever_ty
, sym
::option_type
)
100 || is_type_diagnostic_item(self.lcx
, reciever_ty
, sym
::result_type
)
102 self.result
.push(expr
.span
);
106 // and check sub-expressions
107 intravisit
::walk_expr(self, expr
);
110 fn nested_visit_map(&mut self) -> NestedVisitorMap
<Self::Map
> {
111 NestedVisitorMap
::None
115 fn lint_impl_body
<'tcx
>(cx
: &LateContext
<'tcx
>, impl_span
: Span
, impl_item
: &'tcx hir
::ImplItem
<'_
>) {
116 if let ImplItemKind
::Fn(_
, body_id
) = impl_item
.kind
{
117 let body
= cx
.tcx
.hir().body(body_id
);
118 let mut fpu
= FindExpectUnwrap
{
120 typeck_results
: cx
.tcx
.typeck(impl_item
.def_id
),
123 fpu
.visit_expr(&body
.value
);
125 // if we've found one, lint
126 if !fpu
.result
.is_empty() {
131 "used unwrap or expect in a function that returns result or option",
133 diag
.help("unwrap and expect should not be used in a function that returns result or option");
134 diag
.span_note(fpu
.result
, "potential non-recoverable error(s)");