1 use clippy_utils
::diagnostics
::span_lint_and_help
;
2 use rustc_hir
::{CaptureBy, Expr, ExprKind, PatKind}
;
3 use rustc_lint
::{LateContext, LateLintPass}
;
4 use rustc_session
::{declare_lint_pass, declare_tool_lint}
;
8 /// Checks for instances of `map_err(|_| Some::Enum)`
10 /// ### Why is this bad?
11 /// This `map_err` throws away the original error rather than allowing the enum to contain and report the cause of the error
24 /// impl fmt::Display for Error {
25 /// fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
27 /// Error::Indivisible => write!(f, "could not divide input by three"),
28 /// Error::Remainder(remainder) => write!(
30 /// "input is not divisible by three, remainder = {}",
37 /// impl std::error::Error for Error {}
39 /// fn divisible_by_3(input: &str) -> Result<(), Error> {
42 /// .map_err(|_| Error::Indivisible)
44 /// .and_then(|remainder| {
45 /// if remainder == 0 {
48 /// Err(Error::Remainder(remainder as u8))
56 /// use std::{fmt, num::ParseIntError};
60 /// Indivisible(ParseIntError),
64 /// impl fmt::Display for Error {
65 /// fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
67 /// Error::Indivisible(_) => write!(f, "could not divide input by three"),
68 /// Error::Remainder(remainder) => write!(
70 /// "input is not divisible by three, remainder = {}",
77 /// impl std::error::Error for Error {
78 /// fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
80 /// Error::Indivisible(source) => Some(source),
86 /// fn divisible_by_3(input: &str) -> Result<(), Error> {
89 /// .map_err(Error::Indivisible)
91 /// .and_then(|remainder| {
92 /// if remainder == 0 {
95 /// Err(Error::Remainder(remainder as u8))
100 #[clippy::version = "1.48.0"]
103 "`map_err` should not ignore the original error"
106 declare_lint_pass
!(MapErrIgnore
=> [MAP_ERR_IGNORE
]);
108 impl<'tcx
> LateLintPass
<'tcx
> for MapErrIgnore
{
109 // do not try to lint if this is from a macro or desugaring
110 fn check_expr(&mut self, cx
: &LateContext
<'_
>, e
: &Expr
<'_
>) {
111 if e
.span
.from_expansion() {
115 // check if this is a method call (e.g. x.foo())
116 if let ExprKind
::MethodCall(method
, args
, _
) = e
.kind
{
117 // only work if the method name is `map_err` and there are only 2 arguments (e.g. x.map_err(|_|[1]
118 // Enum::Variant[2]))
119 if method
.ident
.as_str() == "map_err" && args
.len() == 2 {
120 // make sure the first argument is a closure, and grab the CaptureRef, BodyId, and fn_decl_span
122 if let ExprKind
::Closure
{
129 // check if this is by Reference (meaning there's no move statement)
130 if capture_clause
== CaptureBy
::Ref
{
131 // Get the closure body to check the parameters and values
132 let closure_body
= cx
.tcx
.hir().body(body
);
133 // make sure there's only one parameter (`|_|`)
134 if closure_body
.params
.len() == 1 {
135 // make sure that parameter is the wild token (`_`)
136 if let PatKind
::Wild
= closure_body
.params
[0].pat
.kind
{
137 // span the area of the closure capture and warn that the
138 // original error will be thrown away
143 "`map_err(|_|...` wildcard pattern discards the original error",
145 "consider storing the original error as a source in the new error, or silence this warning using an ignored identifier (`.map_err(|_foo| ...`)",