|
1 | 1 | use clippy_utils::diagnostics::span_lint_and_then; |
2 | 2 | use clippy_utils::higher::IfLetOrMatch; |
3 | 3 | use clippy_utils::msrvs::{self, Msrv}; |
4 | | -use clippy_utils::peel_blocks; |
5 | 4 | use clippy_utils::source::snippet_with_context; |
6 | 5 | use clippy_utils::ty::is_type_diagnostic_item; |
7 | 6 | use clippy_utils::visitors::{Descend, Visitable}; |
| 7 | +use clippy_utils::{is_refutable, is_res_lang_ctor, peel_blocks}; |
8 | 8 | use if_chain::if_chain; |
9 | 9 | use rustc_data_structures::fx::{FxHashMap, FxHashSet}; |
10 | 10 | use rustc_errors::Applicability; |
11 | 11 | use rustc_hir::intravisit::{walk_expr, Visitor}; |
| 12 | +use rustc_hir::LangItem::{OptionNone, OptionSome}; |
12 | 13 | use rustc_hir::{Expr, ExprKind, HirId, ItemId, Local, MatchSource, Pat, PatKind, QPath, Stmt, StmtKind, Ty}; |
13 | 14 | use rustc_lint::{LateContext, LateLintPass, LintContext}; |
14 | 15 | use rustc_middle::lint::in_external_macro; |
@@ -85,6 +86,7 @@ impl<'tcx> LateLintPass<'tcx> for ManualLetElse { |
85 | 86 | if let Some(ident_map) = expr_simple_identity_map(local.pat, let_pat, if_then); |
86 | 87 | if let Some(if_else) = if_else; |
87 | 88 | if expr_diverges(cx, if_else); |
| 89 | + if pat_and_expr_can_be_question_mark(cx, let_pat, if_else).is_none(); |
88 | 90 | then { |
89 | 91 | emit_manual_let_else(cx, stmt.span, if_let_expr, &ident_map, let_pat, if_else); |
90 | 92 | } |
@@ -167,6 +169,50 @@ fn emit_manual_let_else( |
167 | 169 | ); |
168 | 170 | } |
169 | 171 |
|
| 172 | +/// Returns whether the given let pattern and else body can be turned into a question mark |
| 173 | +/// |
| 174 | +/// For this example: |
| 175 | +/// ```ignore |
| 176 | +/// let FooBar { a, b } = if let Some(a) = ex { a } else { return None }; |
| 177 | +/// ``` |
| 178 | +/// We get as parameters: |
| 179 | +/// ```ignore |
| 180 | +/// pat: Some(a) |
| 181 | +/// else_body: return None |
| 182 | +/// ``` |
| 183 | +
|
| 184 | +/// And for this example: |
| 185 | +/// ```ignore |
| 186 | +/// let Some(FooBar { a, b }) = ex else { return None }; |
| 187 | +/// ``` |
| 188 | +/// We get as parameters: |
| 189 | +/// ```ignore |
| 190 | +/// pat: Some(FooBar { a, b }) |
| 191 | +/// else_body: return None |
| 192 | +/// ``` |
| 193 | +
|
| 194 | +/// We output `Some(a)` in the first instance, and `Some(FooBar { a, b })` in the second, because |
| 195 | +/// the question mark operator is applicable here. Callers have to check whether we are in a |
| 196 | +/// constant or not. |
| 197 | +pub fn pat_and_expr_can_be_question_mark<'a, 'hir>( |
| 198 | + cx: &LateContext<'_>, |
| 199 | + pat: &'a Pat<'hir>, |
| 200 | + else_body: &Expr<'_>, |
| 201 | +) -> Option<&'a Pat<'hir>> { |
| 202 | + if let PatKind::TupleStruct(pat_path, [inner_pat], _) = pat.kind && |
| 203 | + is_res_lang_ctor(cx, cx.qpath_res(&pat_path, pat.hir_id), OptionSome) && |
| 204 | + !is_refutable(cx, inner_pat) && |
| 205 | + let else_body = peel_blocks(else_body) && |
| 206 | + let ExprKind::Ret(Some(ret_val)) = else_body.kind && |
| 207 | + let ExprKind::Path(ret_path) = ret_val.kind && |
| 208 | + is_res_lang_ctor(cx, cx.qpath_res(&ret_path, ret_val.hir_id), OptionNone) |
| 209 | + { |
| 210 | + Some(inner_pat) |
| 211 | + } else { |
| 212 | + None |
| 213 | + } |
| 214 | +} |
| 215 | + |
170 | 216 | /// Replaces the locals in the pattern |
171 | 217 | /// |
172 | 218 | /// For this example: |
|
0 commit comments