|
1 | | -use clippy_utils::diagnostics::span_lint_and_sugg; |
2 | | -use clippy_utils::{is_from_proc_macro, is_inside_let_else, is_res_lang_ctor, path_res}; |
3 | | -use rustc_errors::Applicability; |
4 | | -use rustc_hir::LangItem::ResultErr; |
5 | 1 | use rustc_hir::intravisit::FnKind; |
6 | | -use rustc_hir::{Block, Body, ExprKind, FnDecl, HirId, ItemKind, MatchSource, Node, OwnerNode, Stmt, StmtKind}; |
7 | | -use rustc_lint::{LateContext, LateLintPass, LintContext}; |
8 | | -use rustc_middle::ty::adjustment::Adjust; |
| 2 | +use rustc_hir::{Block, Body, FnDecl, Stmt}; |
| 3 | +use rustc_lint::{LateContext, LateLintPass}; |
9 | 4 | use rustc_session::declare_lint_pass; |
10 | 5 | use rustc_span::Span; |
11 | 6 | use rustc_span::def_id::LocalDefId; |
12 | 7 |
|
13 | 8 | mod let_and_return; |
14 | 9 | mod needless_return; |
| 10 | +mod needless_return_with_question_mark; |
15 | 11 |
|
16 | 12 | declare_clippy_lint! { |
17 | 13 | /// ### What it does |
@@ -121,56 +117,9 @@ declare_clippy_lint! { |
121 | 117 |
|
122 | 118 | declare_lint_pass!(Return => [LET_AND_RETURN, NEEDLESS_RETURN, NEEDLESS_RETURN_WITH_QUESTION_MARK]); |
123 | 119 |
|
124 | | -/// Checks if a return statement is "needed" in the middle of a block, or if it can be removed. This |
125 | | -/// is the case when the enclosing block expression is coerced to some other type, which only works |
126 | | -/// because of the never-ness of `return` expressions |
127 | | -fn stmt_needs_never_type(cx: &LateContext<'_>, stmt_hir_id: HirId) -> bool { |
128 | | - cx.tcx |
129 | | - .hir_parent_iter(stmt_hir_id) |
130 | | - .find_map(|(_, node)| if let Node::Expr(expr) = node { Some(expr) } else { None }) |
131 | | - .is_some_and(|e| { |
132 | | - cx.typeck_results() |
133 | | - .expr_adjustments(e) |
134 | | - .iter() |
135 | | - .any(|adjust| adjust.target != cx.tcx.types.unit && matches!(adjust.kind, Adjust::NeverToAny)) |
136 | | - }) |
137 | | -} |
138 | | - |
139 | 120 | impl<'tcx> LateLintPass<'tcx> for Return { |
140 | 121 | fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) { |
141 | | - if !stmt.span.in_external_macro(cx.sess().source_map()) |
142 | | - && let StmtKind::Semi(expr) = stmt.kind |
143 | | - && let ExprKind::Ret(Some(ret)) = expr.kind |
144 | | - // return Err(...)? desugars to a match |
145 | | - // over a Err(...).branch() |
146 | | - // which breaks down to a branch call, with the callee being |
147 | | - // the constructor of the Err variant |
148 | | - && let ExprKind::Match(maybe_cons, _, MatchSource::TryDesugar(_)) = ret.kind |
149 | | - && let ExprKind::Call(_, [maybe_result_err]) = maybe_cons.kind |
150 | | - && let ExprKind::Call(maybe_constr, _) = maybe_result_err.kind |
151 | | - && is_res_lang_ctor(cx, path_res(cx, maybe_constr), ResultErr) |
152 | | - |
153 | | - // Ensure this is not the final stmt, otherwise removing it would cause a compile error |
154 | | - && let OwnerNode::Item(item) = cx.tcx.hir_owner_node(cx.tcx.hir_get_parent_item(expr.hir_id)) |
155 | | - && let ItemKind::Fn { body, .. } = item.kind |
156 | | - && let block = cx.tcx.hir_body(body).value |
157 | | - && let ExprKind::Block(block, _) = block.kind |
158 | | - && !is_inside_let_else(cx.tcx, expr) |
159 | | - && let [.., final_stmt] = block.stmts |
160 | | - && final_stmt.hir_id != stmt.hir_id |
161 | | - && !is_from_proc_macro(cx, expr) |
162 | | - && !stmt_needs_never_type(cx, stmt.hir_id) |
163 | | - { |
164 | | - span_lint_and_sugg( |
165 | | - cx, |
166 | | - NEEDLESS_RETURN_WITH_QUESTION_MARK, |
167 | | - expr.span.until(ret.span), |
168 | | - "unneeded `return` statement with `?` operator", |
169 | | - "remove it", |
170 | | - String::new(), |
171 | | - Applicability::MachineApplicable, |
172 | | - ); |
173 | | - } |
| 122 | + needless_return_with_question_mark::check_stmt(cx, stmt); |
174 | 123 | } |
175 | 124 |
|
176 | 125 | fn check_block(&mut self, cx: &LateContext<'tcx>, block: &'tcx Block<'_>) { |
|
0 commit comments