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