]> git.lizzy.rs Git - rust.git/blob - clippy_lints/src/map_err_ignore.rs
Removed map_err suggestion in lint, and updated lint documentation example
[rust.git] / clippy_lints / src / map_err_ignore.rs
1 use crate::utils::span_lint_and_help;
2
3 use rustc_hir::{CaptureBy, Expr, ExprKind, PatKind};
4 use rustc_lint::{LateContext, LateLintPass};
5 use rustc_session::{declare_lint_pass, declare_tool_lint};
6
7 declare_clippy_lint! {
8     /// **What it does:** Checks for instances of `map_err(|_| Some::Enum)`
9     ///
10     /// **Why is this bad?** This map_err throws away the original error rather than allowing the enum to bubble the original error
11     ///
12     /// **Known problems:** None.
13     ///
14     /// **Example:**
15     /// Before:
16     /// ```rust
17     /// use std::convert::TryFrom;
18     ///
19     /// #[derive(Debug)]
20     /// enum Errors {
21     ///     Ignored
22     /// }
23     ///
24     /// fn divisible_by_3(inp: i32) -> Result<u32, Errors> {
25     ///     let i = u32::try_from(inp).map_err(|_| Errors::Ignored)?;
26     ///
27     ///     Ok(i)
28     /// }
29     ///  ```
30     ///
31     ///  After:
32     ///  ```rust
33     /// use std::convert::TryFrom;
34     /// use std::num::TryFromIntError;
35     /// use std::fmt;
36     /// use std::error::Error;
37     ///
38     /// #[derive(Debug)]
39     /// enum ParseError {
40     ///     Indivisible {
41     ///         source: TryFromIntError,
42     ///         input: String,
43     ///     }
44     /// }
45     ///
46     /// impl fmt::Display for ParseError {
47     ///     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
48     ///         match &self {
49     ///             ParseError::Indivisible{source: _, input} => write!(f, "Error: {}", input)
50     ///         }
51     ///     }
52     /// }
53     ///
54     /// impl Error for ParseError {}
55     ///
56     /// impl ParseError {
57     ///     fn new(source: TryFromIntError, input: String) -> ParseError {
58     ///         ParseError::Indivisible{source, input}
59     ///     }
60     /// }
61     ///
62     /// fn divisible_by_3(inp: i32) -> Result<u32, ParseError> {
63     ///     let i = u32::try_from(inp).map_err(|e| ParseError::new(e, e.to_string()))?;
64     ///
65     ///     Ok(i)
66     /// }
67     /// ```
68     pub MAP_ERR_IGNORE,
69     style,
70     "`map_err` should not ignore the original error"
71 }
72
73 declare_lint_pass!(MapErrIgnore => [MAP_ERR_IGNORE]);
74
75 impl<'tcx> LateLintPass<'tcx> for MapErrIgnore {
76     // do not try to lint if this is from a macro or desugaring
77     fn check_expr(&mut self, cx: &LateContext<'_>, e: &Expr<'_>) {
78         if e.span.from_expansion() {
79             return;
80         }
81
82         // check if this is a method call (e.g. x.foo())
83         if let ExprKind::MethodCall(ref method, _t_span, ref args, _) = e.kind {
84             // only work if the method name is `map_err` and there are only 2 arguments (e.g. x.map_err(|_|[1]
85             // Enum::Variant[2]))
86             if method.ident.as_str() == "map_err" && args.len() == 2 {
87                 // make sure the first argument is a closure, and grab the CaptureRef, body_id, and body_span fields
88                 if let ExprKind::Closure(capture, _, body_id, body_span, _) = args[1].kind {
89                     // check if this is by Reference (meaning there's no move statement)
90                     if capture == CaptureBy::Ref {
91                         // Get the closure body to check the parameters and values
92                         let closure_body = cx.tcx.hir().body(body_id);
93                         // make sure there's only one parameter (`|_|`)
94                         if closure_body.params.len() == 1 {
95                             // make sure that parameter is the wild token (`_`)
96                             if let PatKind::Wild = closure_body.params[0].pat.kind {
97                                 // span the area of the closure capture and warn that the
98                                 // original error will be thrown away
99                                 span_lint_and_help(
100                                     cx,
101                                     MAP_ERR_IGNORE,
102                                     body_span,
103                                     "`map_else(|_|...` ignores the original error",
104                                     None,
105                                     "Consider wrapping the error in an enum variant",
106                                 );
107                             }
108                         }
109                     }
110                 }
111             }
112         }
113     }
114 }