|
1 |
| -use clippy_utils::diagnostics::{span_lint_and_then, span_lint_hir_and_then}; |
| 1 | +use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then, span_lint_hir_and_then}; |
2 | 2 | use clippy_utils::source::{snippet_opt, snippet_with_context};
|
3 | 3 | use clippy_utils::visitors::{for_each_expr_with_closures, Descend};
|
4 |
| -use clippy_utils::{fn_def_id, path_to_local_id, span_find_starting_semi}; |
| 4 | +use clippy_utils::{fn_def_id, is_from_proc_macro, path_to_local_id, span_find_starting_semi}; |
5 | 5 | use core::ops::ControlFlow;
|
6 | 6 | use if_chain::if_chain;
|
7 | 7 | use rustc_errors::Applicability;
|
8 | 8 | use rustc_hir::intravisit::FnKind;
|
9 |
| -use rustc_hir::{Block, Body, Expr, ExprKind, FnDecl, LangItem, MatchSource, PatKind, QPath, StmtKind}; |
| 9 | +use rustc_hir::{ |
| 10 | + Block, Body, Expr, ExprKind, FnDecl, ItemKind, LangItem, MatchSource, OwnerNode, PatKind, QPath, Stmt, StmtKind, |
| 11 | +}; |
10 | 12 | use rustc_lint::{LateContext, LateLintPass, LintContext};
|
11 | 13 | use rustc_middle::lint::in_external_macro;
|
12 | 14 | use rustc_middle::ty::subst::GenericArgKind;
|
@@ -77,6 +79,46 @@ declare_clippy_lint! {
|
77 | 79 | "using a return statement like `return expr;` where an expression would suffice"
|
78 | 80 | }
|
79 | 81 |
|
| 82 | +declare_clippy_lint! { |
| 83 | + /// ### What it does |
| 84 | + /// Checks for return statements on `Err` paired with the `?` operator. |
| 85 | + /// |
| 86 | + /// ### Why is this bad? |
| 87 | + /// The `return` is unnecessary. |
| 88 | + /// |
| 89 | + /// ### Example |
| 90 | + /// ```rust,ignore |
| 91 | + /// fn foo(x: usize) -> Result<(), Box<dyn Error>> { |
| 92 | + /// if x == 0 { |
| 93 | + /// return Err(...)?; |
| 94 | + /// } |
| 95 | + /// Ok(()) |
| 96 | + /// } |
| 97 | + /// ``` |
| 98 | + /// simplify to |
| 99 | + /// ```rust,ignore |
| 100 | + /// fn foo(x: usize) -> Result<(), Box<dyn Error>> { |
| 101 | + /// if x == 0 { |
| 102 | + /// Err(...)?; |
| 103 | + /// } |
| 104 | + /// Ok(()) |
| 105 | + /// } |
| 106 | + /// ``` |
| 107 | + /// if paired with `try_err`, use instead: |
| 108 | + /// ```rust,ignore |
| 109 | + /// fn foo(x: usize) -> Result<(), Box<dyn Error>> { |
| 110 | + /// if x == 0 { |
| 111 | + /// return Err(...); |
| 112 | + /// } |
| 113 | + /// Ok(()) |
| 114 | + /// } |
| 115 | + /// ``` |
| 116 | + #[clippy::version = "1.73.0"] |
| 117 | + pub NEEDLESS_RETURN_WITH_QUESTION_MARK, |
| 118 | + style, |
| 119 | + "using a return statement like `return Err(expr)?;` where removing it would suffice" |
| 120 | +} |
| 121 | + |
80 | 122 | #[derive(PartialEq, Eq)]
|
81 | 123 | enum RetReplacement<'tcx> {
|
82 | 124 | Empty,
|
@@ -116,9 +158,35 @@ impl<'tcx> ToString for RetReplacement<'tcx> {
|
116 | 158 | }
|
117 | 159 | }
|
118 | 160 |
|
119 |
| -declare_lint_pass!(Return => [LET_AND_RETURN, NEEDLESS_RETURN]); |
| 161 | +declare_lint_pass!(Return => [LET_AND_RETURN, NEEDLESS_RETURN, NEEDLESS_RETURN_WITH_QUESTION_MARK]); |
120 | 162 |
|
121 | 163 | impl<'tcx> LateLintPass<'tcx> for Return {
|
| 164 | + fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) { |
| 165 | + if !in_external_macro(cx.sess(), stmt.span) |
| 166 | + && let StmtKind::Semi(expr) = stmt.kind |
| 167 | + && let ExprKind::Ret(Some(ret)) = expr.kind |
| 168 | + && let ExprKind::Match(.., MatchSource::TryDesugar) = ret.kind |
| 169 | + // Ensure this is not the final stmt, otherwise removing it would cause a compile error |
| 170 | + && let OwnerNode::Item(item) = cx.tcx.hir().owner(cx.tcx.hir().get_parent_item(expr.hir_id)) |
| 171 | + && let ItemKind::Fn(_, _, body) = item.kind |
| 172 | + && let block = cx.tcx.hir().body(body).value |
| 173 | + && let ExprKind::Block(block, _) = block.kind |
| 174 | + && let [.., final_stmt] = block.stmts |
| 175 | + && final_stmt.hir_id != stmt.hir_id |
| 176 | + && !is_from_proc_macro(cx, expr) |
| 177 | + { |
| 178 | + span_lint_and_sugg( |
| 179 | + cx, |
| 180 | + NEEDLESS_RETURN_WITH_QUESTION_MARK, |
| 181 | + expr.span.until(ret.span), |
| 182 | + "unneeded `return` statement with `?` operator", |
| 183 | + "remove it", |
| 184 | + String::new(), |
| 185 | + Applicability::MachineApplicable, |
| 186 | + ); |
| 187 | + } |
| 188 | + } |
| 189 | + |
122 | 190 | fn check_block(&mut self, cx: &LateContext<'tcx>, block: &'tcx Block<'_>) {
|
123 | 191 | // we need both a let-binding stmt and an expr
|
124 | 192 | if_chain! {
|
|
0 commit comments