diff options
| author | bors <bors@rust-lang.org> | 2021-12-06 18:34:27 +0000 |
|---|---|---|
| committer | bors <bors@rust-lang.org> | 2021-12-06 18:34:27 +0000 |
| commit | 48d939f5166a19529a9e4723ed3bf5bab2314563 (patch) | |
| tree | 4e403bd04c84f0774174771283186d86d8c4b7aa | |
| parent | 9e0852705eb74ab5b840715d47226c707808735c (diff) | |
| parent | 16bbd243f9e5c71ff0481c7168a32f4f442c582f (diff) | |
| download | rust-48d939f5166a19529a9e4723ed3bf5bab2314563.tar.gz rust-48d939f5166a19529a9e4723ed3bf5bab2314563.zip | |
Auto merge of #8042 - camsteffen:peel-blocks, r=xFrednet
Peel blocks and statements utils changelog: none * Rename `remove_blocks` to `peel_blocks` * Add `peel_blocks_and_stmts` * Various refactors to use the above utils * The utils also now check `block.rules`
24 files changed, 197 insertions, 319 deletions
diff --git a/clippy_lints/src/assertions_on_constants.rs b/clippy_lints/src/assertions_on_constants.rs index 521fc84ee9c..b7f414742f1 100644 --- a/clippy_lints/src/assertions_on_constants.rs +++ b/clippy_lints/src/assertions_on_constants.rs @@ -2,7 +2,7 @@ use clippy_utils::consts::{constant, Constant}; use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::higher; use clippy_utils::source::snippet_opt; -use clippy_utils::{is_direct_expn_of, is_expn_of, match_panic_call}; +use clippy_utils::{is_direct_expn_of, is_expn_of, match_panic_call, peel_blocks}; use if_chain::if_chain; use rustc_hir::{Expr, ExprKind, UnOp}; use rustc_lint::{LateContext, LateLintPass}; @@ -122,15 +122,7 @@ fn match_assert_with_message<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) if let ExprKind::Unary(UnOp::Not, expr) = cond.kind; // bind the first argument of the `assert!` macro if let Some((Constant::Bool(is_true), _)) = constant(cx, cx.typeck_results(), expr); - // block - if let ExprKind::Block(block, _) = then.kind; - if block.stmts.is_empty(); - if let Some(block_expr) = &block.expr; - // inner block is optional. unwrap it if it exists, or use the expression as is otherwise. - if let Some(begin_panic_call) = match block_expr.kind { - ExprKind::Block(inner_block, _) => &inner_block.expr, - _ => &block.expr, - }; + let begin_panic_call = peel_blocks(then); // function call if let Some(arg) = match_panic_call(cx, begin_panic_call); // bind the second argument of the `assert!` macro if it exists diff --git a/clippy_lints/src/bytecount.rs b/clippy_lints/src/bytecount.rs index afb317421d0..657c1f88c7d 100644 --- a/clippy_lints/src/bytecount.rs +++ b/clippy_lints/src/bytecount.rs @@ -2,7 +2,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::match_type; use clippy_utils::visitors::is_local_used; -use clippy_utils::{path_to_local_id, paths, peel_ref_operators, remove_blocks, strip_pat_refs}; +use clippy_utils::{path_to_local_id, paths, peel_blocks, peel_ref_operators, strip_pat_refs}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{BinOpKind, Expr, ExprKind, PatKind}; @@ -55,7 +55,7 @@ impl<'tcx> LateLintPass<'tcx> for ByteCount { cx.typeck_results().expr_ty(filter_recv).peel_refs(), &paths::SLICE_ITER); let operand_is_arg = |expr| { - let expr = peel_ref_operators(cx, remove_blocks(expr)); + let expr = peel_ref_operators(cx, peel_blocks(expr)); path_to_local_id(expr, arg_id) }; let needle = if operand_is_arg(l) { diff --git a/clippy_lints/src/collapsible_match.rs b/clippy_lints/src/collapsible_match.rs index 626f9971f01..c71e9f10f79 100644 --- a/clippy_lints/src/collapsible_match.rs +++ b/clippy_lints/src/collapsible_match.rs @@ -1,10 +1,10 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::higher::IfLetOrMatch; use clippy_utils::visitors::is_local_used; -use clippy_utils::{is_lang_ctor, is_unit_expr, path_to_local, peel_ref_operators, SpanlessEq}; +use clippy_utils::{is_lang_ctor, is_unit_expr, path_to_local, peel_blocks_with_stmt, peel_ref_operators, SpanlessEq}; use if_chain::if_chain; use rustc_hir::LangItem::OptionNone; -use rustc_hir::{Arm, Expr, ExprKind, Guard, HirId, Pat, PatKind, StmtKind}; +use rustc_hir::{Arm, Expr, Guard, HirId, Pat, PatKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::{MultiSpan, Span}; @@ -75,7 +75,7 @@ fn check_arm<'tcx>( outer_guard: Option<&'tcx Guard<'tcx>>, outer_else_body: Option<&'tcx Expr<'tcx>>, ) { - let inner_expr = strip_singleton_blocks(outer_then_body); + let inner_expr = peel_blocks_with_stmt(outer_then_body); if_chain! { if let Some(inner) = IfLetOrMatch::parse(cx, inner_expr); if let Some((inner_scrutinee, inner_then_pat, inner_else_body)) = match inner { @@ -138,20 +138,6 @@ fn check_arm<'tcx>( } } -fn strip_singleton_blocks<'hir>(mut expr: &'hir Expr<'hir>) -> &'hir Expr<'hir> { - while let ExprKind::Block(block, _) = expr.kind { - match (block.stmts, block.expr) { - ([stmt], None) => match stmt.kind { - StmtKind::Expr(e) | StmtKind::Semi(e) => expr = e, - _ => break, - }, - ([], Some(e)) => expr = e, - _ => break, - } - } - expr -} - /// A "wild-like" arm has a wild (`_`) or `None` pattern and no guard. Such arms can be "collapsed" /// into a single wild arm without any significant loss in semantics or readability. fn arm_is_wild_like(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool { diff --git a/clippy_lints/src/derivable_impls.rs b/clippy_lints/src/derivable_impls.rs index d0fab2b48fb..eccb18982f3 100644 --- a/clippy_lints/src/derivable_impls.rs +++ b/clippy_lints/src/derivable_impls.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_help; -use clippy_utils::{is_automatically_derived, is_default_equivalent, remove_blocks}; +use clippy_utils::{is_automatically_derived, is_default_equivalent, peel_blocks}; use rustc_hir::{ def::{DefKind, Res}, Body, Expr, ExprKind, GenericArg, Impl, ImplItemKind, Item, ItemKind, Node, PathSegment, QPath, TyKind, @@ -95,7 +95,7 @@ impl<'tcx> LateLintPass<'tcx> for DerivableImpls { } } } - let should_emit = match remove_blocks(func_expr).kind { + let should_emit = match peel_blocks(func_expr).kind { ExprKind::Tup(fields) => fields.iter().all(|e| is_default_equivalent(cx, e)), ExprKind::Call(callee, args) if is_path_self(callee) => args.iter().all(|e| is_default_equivalent(cx, e)), diff --git a/clippy_lints/src/floating_point_arithmetic.rs b/clippy_lints/src/floating_point_arithmetic.rs index 3df511ea8e7..2de2bfc040b 100644 --- a/clippy_lints/src/floating_point_arithmetic.rs +++ b/clippy_lints/src/floating_point_arithmetic.rs @@ -4,7 +4,7 @@ use clippy_utils::consts::{ }; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::higher; -use clippy_utils::{eq_expr_value, get_parent_expr, in_constant, numeric_literal, sugg}; +use clippy_utils::{eq_expr_value, get_parent_expr, in_constant, numeric_literal, peel_blocks, sugg}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{BinOpKind, Expr, ExprKind, PathSegment, UnOp}; @@ -546,13 +546,9 @@ fn are_negated<'a>(cx: &LateContext<'_>, expr1: &'a Expr<'a>, expr2: &'a Expr<'a fn check_custom_abs(cx: &LateContext<'_>, expr: &Expr<'_>) { if_chain! { - if let Some(higher::If { cond, then, r#else }) = higher::If::hir(expr); - if let ExprKind::Block(block, _) = then.kind; - if block.stmts.is_empty(); - if let Some(if_body_expr) = block.expr; - if let Some(ExprKind::Block(else_block, _)) = r#else.map(|el| &el.kind); - if else_block.stmts.is_empty(); - if let Some(else_body_expr) = else_block.expr; + if let Some(higher::If { cond, then, r#else: Some(r#else) }) = higher::If::hir(expr); + let if_body_expr = peel_blocks(then); + let else_body_expr = peel_blocks(r#else); if let Some((if_expr_positive, body)) = are_negated(cx, if_body_expr, else_body_expr); then { let positive_abs_sugg = ( diff --git a/clippy_lints/src/if_then_some_else_none.rs b/clippy_lints/src/if_then_some_else_none.rs index 30d222bd7d2..16e5c5ca603 100644 --- a/clippy_lints/src/if_then_some_else_none.rs +++ b/clippy_lints/src/if_then_some_else_none.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::source::snippet_with_macro_callsite; -use clippy_utils::{contains_return, higher, is_else_clause, is_lang_ctor, meets_msrv, msrvs}; +use clippy_utils::{contains_return, higher, is_else_clause, is_lang_ctor, meets_msrv, msrvs, peel_blocks}; use if_chain::if_chain; use rustc_hir::LangItem::{OptionNone, OptionSome}; use rustc_hir::{Expr, ExprKind, Stmt, StmtKind}; @@ -77,10 +77,7 @@ impl LateLintPass<'_> for IfThenSomeElseNone { if let ExprKind::Call(then_call, [then_arg]) = then_expr.kind; if let ExprKind::Path(ref then_call_qpath) = then_call.kind; if is_lang_ctor(cx, then_call_qpath, OptionSome); - if let ExprKind::Block(els_block, _) = els.kind; - if els_block.stmts.is_empty(); - if let Some(els_expr) = els_block.expr; - if let ExprKind::Path(ref qpath) = els_expr.kind; + if let ExprKind::Path(ref qpath) = peel_blocks(els).kind; if is_lang_ctor(cx, qpath, OptionNone); if !stmts_contains_early_return(then_block.stmts); then { diff --git a/clippy_lints/src/implicit_saturating_sub.rs b/clippy_lints/src/implicit_saturating_sub.rs index 4088c54623d..26a196aab59 100644 --- a/clippy_lints/src/implicit_saturating_sub.rs +++ b/clippy_lints/src/implicit_saturating_sub.rs @@ -1,10 +1,9 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::higher; -use clippy_utils::SpanlessEq; +use clippy_utils::{higher, peel_blocks_with_stmt, SpanlessEq}; use if_chain::if_chain; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; -use rustc_hir::{lang_items::LangItem, BinOpKind, Expr, ExprKind, QPath, StmtKind}; +use rustc_hir::{lang_items::LangItem, BinOpKind, Expr, ExprKind, QPath}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -52,13 +51,8 @@ impl<'tcx> LateLintPass<'tcx> for ImplicitSaturatingSub { // Ensure that the binary operator is >, != and < if BinOpKind::Ne == cond_op.node || BinOpKind::Gt == cond_op.node || BinOpKind::Lt == cond_op.node; - // Check if the true condition block has only one statement - if let ExprKind::Block(block, _) = then.kind; - if block.stmts.len() == 1 && block.expr.is_none(); - // Check if assign operation is done - if let StmtKind::Semi(e) = block.stmts[0].kind; - if let Some(target) = subtracts_one(cx, e); + if let Some(target) = subtracts_one(cx, then); // Extracting out the variable name if let ExprKind::Path(QPath::Resolved(_, ares_path)) = target.kind; @@ -138,8 +132,8 @@ impl<'tcx> LateLintPass<'tcx> for ImplicitSaturatingSub { } } -fn subtracts_one<'a>(cx: &LateContext<'_>, expr: &Expr<'a>) -> Option<&'a Expr<'a>> { - match expr.kind { +fn subtracts_one<'a>(cx: &LateContext<'_>, expr: &'a Expr<'a>) -> Option<&'a Expr<'a>> { + match peel_blocks_with_stmt(expr).kind { ExprKind::AssignOp(ref op1, target, value) => { if_chain! { if BinOpKind::Sub == op1.node; diff --git a/clippy_lints/src/loops/manual_flatten.rs b/clippy_lints/src/loops/manual_flatten.rs index 5b6e27085d5..d276c901059 100644 --- a/clippy_lints/src/loops/manual_flatten.rs +++ b/clippy_lints/src/loops/manual_flatten.rs @@ -3,11 +3,11 @@ use super::MANUAL_FLATTEN; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::higher; use clippy_utils::visitors::is_local_used; -use clippy_utils::{is_lang_ctor, path_to_local_id}; +use clippy_utils::{is_lang_ctor, path_to_local_id, peel_blocks_with_stmt}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::LangItem::{OptionSome, ResultOk}; -use rustc_hir::{Expr, ExprKind, Pat, PatKind, StmtKind}; +use rustc_hir::{Expr, Pat, PatKind}; use rustc_lint::LateContext; use rustc_middle::ty; use rustc_span::source_map::Span; @@ -21,71 +21,55 @@ pub(super) fn check<'tcx>( body: &'tcx Expr<'_>, span: Span, ) { - if let ExprKind::Block(block, _) = body.kind { - // Ensure the `if let` statement is the only expression or statement in the for-loop - let inner_expr = if block.stmts.len() == 1 && block.expr.is_none() { - let match_stmt = &block.stmts[0]; - if let StmtKind::Semi(inner_expr) = match_stmt.kind { - Some(inner_expr) - } else { - None - } - } else if block.stmts.is_empty() { - block.expr - } else { - None - }; + let inner_expr = peel_blocks_with_stmt(body); + if_chain! { + if let Some(higher::IfLet { let_pat, let_expr, if_then, if_else: None }) + = higher::IfLet::hir(cx, inner_expr); + // Ensure match_expr in `if let` statement is the same as the pat from the for-loop + if let PatKind::Binding(_, pat_hir_id, _, _) = pat.kind; + if path_to_local_id(let_expr, pat_hir_id); + // Ensure the `if let` statement is for the `Some` variant of `Option` or the `Ok` variant of `Result` + if let PatKind::TupleStruct(ref qpath, _, _) = let_pat.kind; + let some_ctor = is_lang_ctor(cx, qpath, OptionSome); + let ok_ctor = is_lang_ctor(cx, qpath, ResultOk); + if some_ctor || ok_ctor; + // Ensure expr in `if let` is not used afterwards + if !is_local_used(cx, if_then, pat_hir_id); + then { + let if_let_type = if some_ctor { "Some" } else { "Ok" }; + // Prepare the error message + let msg = format!("unnecessary `if let` since only the `{}` variant of the iterator element is used", if_let_type); - if_chain! { - if let Some(inner_expr) = inner_expr; - if let Some(higher::IfLet { let_pat, let_expr, if_then, if_else: None }) - = higher::IfLet::hir(cx, inner_expr); - // Ensure match_expr in `if let` statement is the same as the pat from the for-loop - if let PatKind::Binding(_, pat_hir_id, _, _) = pat.kind; - if path_to_local_id(let_expr, pat_hir_id); - // Ensure the `if let` statement is for the `Some` variant of `Option` or the `Ok` variant of `Result` - if let PatKind::TupleStruct(ref qpath, _, _) = let_pat.kind; - let some_ctor = is_lang_ctor(cx, qpath, OptionSome); - let ok_ctor = is_lang_ctor(cx, qpath, ResultOk); - if some_ctor || ok_ctor; - // Ensure epxr in `if let` is not used afterwards - if !is_local_used(cx, if_then, pat_hir_id); - then { - let if_let_type = if some_ctor { "Some" } else { "Ok" }; - // Prepare the error message - let msg = format!("unnecessary `if let` since only the `{}` variant of the iterator element is used", if_let_type); - - // Prepare the help message - let mut applicability = Applicability::MaybeIncorrect; - let arg_snippet = make_iterator_snippet(cx, arg, &mut applicability); - let copied = match cx.typeck_results().expr_ty(let_expr).kind() { - ty::Ref(_, inner, _) => match inner.kind() { - ty::Ref(..) => ".copied()", - _ => "" - } + // Prepare the help message + let mut applicability = Applicability::MaybeIncorrect; + let arg_snippet = make_iterator_snippet(cx, arg, &mut applicability); + let copied = match cx.typeck_results().expr_ty(let_expr).kind() { + ty::Ref(_, inner, _) => match inner.kind() { + ty::Ref(..) => ".copied()", _ => "" - }; + } + _ => "" + }; - span_lint_and_then( - cx, - MANUAL_FLATTEN, - span, - &msg, - |diag| { - let sugg = format!("{}{}.flatten()", arg_snippet, copied); - diag.span_suggestion( - arg.span, - "try", - sugg, - Applicability::MaybeIncorrect, - ); - diag.span_help( - inner_expr.span, - "...and remove the `if let` statement in the for loop", - ); - } - ); - } + span_lint_and_then( + cx, + MANUAL_FLATTEN, + span, + &msg, + |diag| { + let sugg = format!("{}{}.flatten()", arg_snippet, copied); + diag.span_suggestion( + arg.span, + "try", + sugg, + Applicability::MaybeIncorrect, + ); + diag.span_help( + inner_expr.span, + "...and remove the `if let` statement in the for loop", + ); + } + ); } } } diff --git a/clippy_lints/src/loops/never_loop.rs b/clippy_lints/src/loops/never_loop.rs index 86b7d6d989a..68ffcd1abfb 100644 --- a/clippy_lints/src/loops/never_loop.rs +++ b/clippy_lints/src/loops/never_loop.rs @@ -92,9 +92,7 @@ fn combine_branches(b1: NeverLoopResult, b2: NeverLoopResult) -> NeverLoopResult } fn never_loop_block(block: &Block<'_>, main_loop_id: HirId) -> NeverLoopResult { - let stmts = block.stmts.iter().map(stmt_to_expr); - let expr = once(block.expr); - let mut iter = stmts.chain(expr).flatten(); + let mut iter = block.stmts.iter().filter_map(stmt_to_expr).chain(block.expr); never_loop_expr_seq(&mut iter, main_loop_id) } diff --git a/clippy_lints/src/manual_map.rs b/clippy_lints/src/manual_map.rs index 4d8ad566e6b..34a70ca76c6 100644 --- a/clippy_lints/src/manual_map.rs +++ b/clippy_lints/src/manual_map.rs @@ -5,7 +5,7 @@ use clippy_utils::source::{snippet_with_applicability, snippet_with_context}; use clippy_utils::ty::{is_type_diagnostic_item, peel_mid_ty_refs_is_mutable, type_is_unsafe_function}; use clippy_utils::{ can_move_expr_to_closure, in_constant, is_else_clause, is_lang_ctor, is_lint_allowed, path_to_local_id, - peel_hir_expr_refs, peel_hir_expr_while, CaptureKind, + peel_blocks, peel_hir_expr_refs, peel_hir_expr_while, CaptureKind, }; use rustc_ast::util::parser::PREC_POSTFIX; use rustc_errors::Applicability; @@ -307,16 +307,5 @@ fn get_some_expr( // Checks for the `None` value. fn is_none_expr(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> bool { - match expr.kind { - ExprKind::Path(ref qpath) => is_lang_ctor(cx, qpath, OptionNone), - ExprKind::Block( - Block { - stmts: [], - expr: Some(expr), - .. - }, - _, - ) => is_none_expr(cx, expr), - _ => false, - } + matches!(peel_blocks(expr).kind, ExprKind::Path(ref qpath) if is_lang_ctor(cx, qpath, OptionNone)) } diff --git a/clippy_lints/src/map_clone.rs b/clippy_lints/src/map_clone.rs index c2b78e21861..174c7da28d3 100644 --- a/clippy_lints/src/map_clone.rs +++ b/clippy_lints/src/map_clone.rs @@ -1,8 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::is_trait_method; -use clippy_utils::remove_blocks; use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::{is_copy, is_type_diagnostic_item}; +use clippy_utils::{is_trait_method, peel_blocks}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir as hir; @@ -60,7 +59,7 @@ impl<'tcx> LateLintPass<'tcx> for MapClone { if let hir::ExprKind::Closure(_, _, body_id, _, _) = args[1].kind; then { let closure_body = cx.tcx.hir().body(body_id); - let closure_expr = remove_blocks(&closure_body.value); + let closure_expr = peel_blocks(&closure_body.value); match closure_body.params[0].pat.kind { hir::PatKind::Ref(inner, hir::Mutability::Not) => if let hir::PatKind::Binding( hir::BindingAnnotation::Unannotated, .., name, None diff --git a/clippy_lints/src/matches.rs b/clippy_lints/src/matches.rs index 08c23ca6d32..385232f4d71 100644 --- a/clippy_lints/src/matches.rs +++ b/clippy_lints/src/matches.rs @@ -9,7 +9,7 @@ use clippy_utils::ty::{implements_trait, is_type_diagnostic_item, match_type, pe use clippy_utils::visitors::is_local_used; use clippy_utils::{ get_parent_expr, is_expn_of, is_lang_ctor, is_lint_allowed, is_refutable, is_unit_expr, is_wild, meets_msrv, msrvs, - path_to_local, path_to_local_id, peel_hir_pat_refs, peel_n_hir_expr_refs, recurse_or_patterns, remove_blocks, + path_to_local, path_to_local_id, peel_blocks, peel_hir_pat_refs, peel_n_hir_expr_refs, recurse_or_patterns, strip_pat_refs, }; use clippy_utils::{paths, search_same, SpanlessEq, SpanlessHash}; @@ -659,7 +659,7 @@ impl<'tcx> LateLintPass<'tcx> for Matches { QPath::Resolved(None, variant_name), args, _) = arms[0].pat.kind; if args.len() == 1; if let PatKind::Binding(_, arg, ..) = strip_pat_refs(&args[0]).kind; - let body = remove_blocks(arms[0].body); + let body = peel_blocks(arms[0].body); if path_to_local_id(body, arg); then { @@ -724,7 +724,7 @@ fn check_single_match(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], exp return; } let els = arms[1].body; - let els = if is_unit_expr(remove_blocks(els)) { + let els = if is_unit_expr(peel_blocks(els)) { None } else if let ExprKind::Block(Block { stmts, expr: block_expr, .. }, _) = els.kind { if stmts.len() == 1 && block_expr.is_none() || stmts.is_empty() && block_expr.is_some() { @@ -1482,7 +1482,7 @@ fn check_match_single_binding<'a>(cx: &LateContext<'a>, ex: &Expr<'a>, arms: &[A let matched_vars = ex.span; let bind_names = arms[0].pat.span; - let match_body = remove_blocks(arms[0].body); + let match_body = peel_blocks(arms[0].body); let mut snippet_body = if match_body.span.from_expansion() { Sugg::hir_with_macro_callsite(cx, match_body, "..").to_string() } else { @@ -1679,7 +1679,7 @@ fn is_ref_some_arm(cx: &LateContext<'_>, arm: &Arm<'_>) -> Option<BindingAnnotat if is_lang_ctor(cx, qpath, OptionSome); if let PatKind::Binding(rb, .., ident, _) = first_pat.kind; if rb == BindingAnnotation::Ref || rb == BindingAnnotation::RefMut; - if let ExprKind::Call(e, args) = remove_blocks(arm.body).kind; + if let ExprKind::Call(e, args) = peel_blocks(arm.body).kind; if let ExprKind::Path(ref some_path) = e.kind; if is_lang_ctor(cx, some_path, OptionSome) && args.len() == 1; if let ExprKind::Path(QPath::Resolved(_, path2)) = args[0].kind; diff --git a/clippy_lints/src/methods/bind_instead_of_map.rs b/clippy_lints/src/methods/bind_instead_of_map.rs index b7690cf9222..150bafc0f5d 100644 --- a/clippy_lints/src/methods/bind_instead_of_map.rs +++ b/clippy_lints/src/methods/bind_instead_of_map.rs @@ -1,7 +1,7 @@ use super::{contains_return, BIND_INSTEAD_OF_MAP}; use clippy_utils::diagnostics::{multispan_sugg_with_applicability, span_lint_and_sugg, span_lint_and_then}; use clippy_utils::source::{snippet, snippet_with_macro_callsite}; -use clippy_utils::{remove_blocks, visitors::find_all_ret_expressions}; +use clippy_utils::{peel_blocks, visitors::find_all_ret_expressions}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir as hir; @@ -152,7 +152,7 @@ pub(crate) trait BindInsteadOfMap { match arg.kind { hir::ExprKind::Closure(_, _, body_id, closure_args_span, _) => { let closure_body = cx.tcx.hir().body(body_id); - let closure_expr = remove_blocks(&closure_body.value); + let closure_expr = peel_blocks(&closure_body.value); if Self::lint_closure_autofixable(cx, expr, recv, closure_expr, closure_args_span) { true diff --git a/clippy_lints/src/methods/filter_map.rs b/clippy_lints/src/methods/filter_map.rs index c96c817bb8b..6d8733c08b4 100644 --- a/clippy_lints/src/methods/filter_map.rs +++ b/clippy_lints/src/methods/filter_map.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::{indent_of, reindent_multiline, snippet}; use clippy_utils::ty::is_type_diagnostic_item; -use clippy_utils::{is_trait_method, path_to_local_id, remove_blocks, SpanlessEq}; +use clippy_utils::{is_trait_method, path_to_local_id, peel_blocks, SpanlessEq}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir as hir; @@ -25,7 +25,7 @@ fn is_method<'tcx>(cx: &LateContext<'tcx>, expr: &hir::Expr<'_>, method_name: Sy }, hir::ExprKind::Closure(_, _, c, _, _) => { let body = cx.tcx.hir().body(*c); - let closure_expr = remove_blocks(&body.value); + let closure_expr = peel_blocks(&body.value); let arg_id = body.params[0].pat.hir_id; match closure_expr.kind { hir::ExprKind::MethodCall(hir::PathSegment { ident, .. }, _, args, _) => { diff --git a/clippy_lints/src/methods/option_as_ref_deref.rs b/clippy_lints/src/methods/option_as_ref_deref.rs index d3f40d26208..fa74a8f3dc3 100644 --- a/clippy_lints/src/methods/option_as_ref_deref.rs +++ b/clippy_lints/src/methods/option_as_ref_deref.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet; use clippy_utils::ty::is_type_diagnostic_item; -use clippy_utils::{match_def_path, meets_msrv, msrvs, path_to_local_id, paths, remove_blocks}; +use clippy_utils::{match_def_path, meets_msrv, msrvs, path_to_local_id, paths, peel_blocks}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir as hir; @@ -53,7 +53,7 @@ pub(super) fn check<'tcx>( }), hir::ExprKind::Closure(_, _, body_id, _, _) => { let closure_body = cx.tcx.hir().body(body_id); - let closure_expr = remove_blocks(&closure_body.value); + let closure_expr = peel_blocks(&closure_body.value); match &closure_expr.kind { hir::ExprKind::MethodCall(_, _, args, _) => { diff --git a/clippy_lints/src/methods/unnecessary_fold.rs b/clippy_lints/src/methods/unnecessary_fold.rs index 4c4034437da..47a81199608 100644 --- a/clippy_lints/src/methods/unnecessary_fold.rs +++ b/clippy_lints/src/methods/unnecessary_fold.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::{is_trait_method, path_to_local_id, remove_blocks, strip_pat_refs}; +use clippy_utils::{is_trait_method, path_to_local_id, peel_blocks, strip_pat_refs}; use if_chain::if_chain; use rustc_ast::ast; use rustc_errors::Applicability; @@ -31,7 +31,7 @@ pub(super) fn check( // Extract the body of the closure passed to fold if let hir::ExprKind::Closure(_, _, body_id, _, _) = acc.kind; let closure_body = cx.tcx.hir().body(body_id); - let closure_expr = remove_blocks(&closure_body.value); + let closure_expr = peel_blocks(&closure_body.value); // Check if the closure body is of the form `acc <op> some_expr(x)` if let hir::ExprKind::Binary(ref bin_op, left_expr, right_expr) = closure_expr.kind; diff --git a/clippy_lints/src/needless_bool.rs b/clippy_lints/src/needless_bool.rs index e7809436252..d391fbecf82 100644 --- a/clippy_lints/src/needless_bool.rs +++ b/clippy_lints/src/needless_bool.rs @@ -6,10 +6,10 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg}; use clippy_utils::higher; use clippy_utils::source::snippet_with_applicability; use clippy_utils::sugg::Sugg; -use clippy_utils::{get_parent_node, is_else_clause, is_expn_of}; +use clippy_utils::{get_parent_node, is_else_clause, is_expn_of, peel_blocks, peel_blocks_with_stmt}; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; -use rustc_hir::{BinOpKind, Block, Expr, ExprKind, HirId, Node, StmtKind, UnOp}; +use rustc_hir::{BinOpKind, Block, Expr, ExprKind, HirId, Node, UnOp}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::Spanned; @@ -143,8 +143,8 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessBool { applicability, ); }; - if let ExprKind::Block(then, _) = then.kind { - match (fetch_bool_block(then), fetch_bool_expr(r#else)) { + if let Some((a, b)) = fetch_bool_block(then).and_then(|a| Some((a, fetch_bool_block(r#else)?))) { + match (a, b) { (RetBool(true), RetBool(true)) | (Bool(true), Bool(true)) => { span_lint( cx, @@ -167,8 +167,6 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessBool { (Bool(false), Bool(true)) => reduce(false, true), _ => (), } - } else { - panic!("IfExpr `then` node is not an `ExprKind::Block`"); } } } @@ -271,8 +269,6 @@ fn check_comparison<'a, 'tcx>( right_false: Option<(impl FnOnce(Sugg<'a>) -> Sugg<'a>, &str)>, no_literal: Option<(impl FnOnce(Sugg<'a>, Sugg<'a>) -> Sugg<'a>, &str)>, ) { - use self::Expression::{Bool, Other}; - if let ExprKind::Binary(op, left_side, right_side) = e.kind { let (l_ty, r_ty) = ( cx.typeck_results().expr_ty(left_side), @@ -304,19 +300,19 @@ fn check_comparison<'a, 'tcx>( } match (fetch_bool_expr(left_side), fetch_bool_expr(right_side)) { - (Bool(true), Other) => left_true.map_or((), |(h, m)| { + (Some(true), None) => left_true.map_or((), |(h, m)| { suggest_bool_comparison(cx, e, right_side, applicability, m, h); }), - (Other, Bool(true)) => right_true.map_or((), |(h, m)| { + (None, Some(true)) => right_true.map_or((), |(h, m)| { suggest_bool_comparison(cx, e, left_side, applicability, m, h); }), - (Bool(false), Other) => left_false.map_or((), |(h, m)| { + (Some(false), None) => left_false.map_or((), |(h, m)| { suggest_bool_comparison(cx, e, right_side, applicability, m, h); }), - (Other, Bool(false)) => right_false.map_or((), |(h, m)| { + (None, Some(false)) => right_false.map_or((), |(h, m)| { suggest_bool_comparison(cx, e, left_side, applicability, m, h); }), - (Other, Other) => no_literal.map_or((), |(h, m)| { + (None, None) => no_literal.map_or((), |(h, m)| { let left_side = Sugg::hir_with_applicability(cx, left_side, "..", &mut applicability); let right_side = Sugg::hir_with_applicability(cx, right_side, "..", &mut applicability); span_lint_and_sugg( @@ -365,41 +361,20 @@ fn suggest_bool_comparison<'a, 'tcx>( enum Expression { Bool(bool), RetBool(bool), - Other, } -fn fetch_bool_block(block: &Block<'_>) -> Expression { - match (&*block.stmts, block.expr.as_ref()) { - (&[], Some(e)) => fetch_bool_expr(&**e), - (&[ref e], None) => { - if let StmtKind::Semi(e) = e.kind { - if let ExprKind::Ret(_) = e.kind { - fetch_bool_expr(e) - } else { - Expression::Other - } - } else { - Expression::Other - } - }, - _ => Expression::Other, +fn fetch_bool_block(expr: &Expr<'_>) -> Option<Expression> { + match peel_blocks_with_stmt(expr).kind { + ExprKind::Ret(Some(ret)) => Some(Expression::RetBool(fetch_bool_expr(ret)?)), + _ => Some(Expression::Bool(fetch_bool_expr(expr)?)), } } -fn fetch_bool_expr(expr: &Expr<'_>) -> Expression { - match expr.kind { - ExprKind::Block(block, _) => fetch_bool_block(block), - ExprKind::Lit(ref lit_ptr) => { - if let LitKind::Bool(value) = lit_ptr.node { - Expression::Bool(value) - } else { - Expression::Other - } - }, - ExprKind::Ret(Some(expr)) => match fetch_bool_expr(expr) { - Expression::Bool(value) => Expression::RetBool(value), - _ => Expression::Other, - }, - _ => Expression::Other, +fn fetch_bool_expr(expr: &Expr<'_>) -> Option<bool> { + if let ExprKind::Lit(ref lit_ptr) = peel_blocks(expr).kind { + if let LitKind::Bool(value) = lit_ptr.node { + return Some(value); + } } + None } diff --git a/clippy_lints/src/no_effect.rs b/clippy_lints/src/no_effect.rs index 6fcc9ca29b9..9d5babc5de8 100644 --- a/clippy_lints/src/no_effect.rs +++ b/clippy_lints/src/no_effect.rs @@ -1,5 +1,6 @@ use clippy_utils::diagnostics::{span_lint_hir, span_lint_hir_and_then}; use clippy_utils::is_lint_allowed; +use clippy_utils::peel_blocks; use clippy_utils::source::snippet_opt; use clippy_utils::ty::has_drop; use rustc_errors::Applicability; @@ -114,7 +115,7 @@ fn has_no_effect(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { if expr.span.from_expansion() { return false; } - match expr.kind { + match peel_blocks(expr).kind { ExprKind::Lit(..) | ExprKind::Closure(..) => true, ExprKind::Path(..) => !has_drop(cx, cx.typeck_results().expr_ty(expr)), ExprKind::Index(a, b) | ExprKind::Binary(_, a, b) => has_no_effect(cx, a) && has_no_effect(cx, b), @@ -150,9 +151,6 @@ fn has_no_effect(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { false } }, - ExprKind::Block(block, _) => { - block.stmts.is_empty() && block.expr.as_ref().map_or(false, |expr| has_no_effect(cx, expr)) - }, _ => false, } } diff --git a/clippy_lints/src/option_if_let_else.rs b/clippy_lints/src/option_if_let_else.rs index 262be17f617..897207ebf50 100644 --- a/clippy_lints/src/option_if_let_else.rs +++ b/clippy_lints/src/option_if_let_else.rs @@ -1,15 +1,14 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::higher; use clippy_utils::sugg::Sugg; use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::{ - can_move_expr_to_closure, eager_or_lazy, in_constant, is_else_clause, is_lang_ctor, peel_hir_expr_while, - CaptureKind, + can_move_expr_to_closure, eager_or_lazy, higher, in_constant, is_else_clause, is_lang_ctor, peel_blocks, + peel_hir_expr_while, CaptureKind, }; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::LangItem::OptionSome; -use rustc_hir::{def::Res, BindingAnnotation, Block, Expr, ExprKind, Mutability, PatKind, Path, QPath, UnOp}; +use rustc_hir::{def::Res, BindingAnnotation, Expr, ExprKind, Mutability, PatKind, Path, QPath, UnOp}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::sym; @@ -86,28 +85,6 @@ struct OptionIfLetElseOccurence { none_expr: String, } -/// Extracts the body of a given arm. If the arm contains only an expression, -/// then it returns the expression. Otherwise, it returns the entire block -fn extract_body_from_expr<'a>(expr: &'a Expr<'a>) -> Option<&'a Expr<'a>> { - if let ExprKind::Block( - Block { - stmts: block_stmts, - expr: Some(block_expr), - .. - }, - _, - ) = expr.kind - { - if let [] = block_stmts { - Some(block_expr) - } else { - Some(expr) - } - } else { - None - } -} - fn format_option_in_sugg(cx: &LateContext<'_>, cond_expr: &Expr<'_>, as_ref: bool, as_mut: bool) -> String { format!( "{}{}", @@ -145,8 +122,8 @@ fn detect_option_if_let_else<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) -> then { let capture_mut = if bind_annotation == &BindingAnnotation::Mutable { "mut " } else { "" }; - let some_body = extract_body_from_expr(if_then)?; - let none_body = extract_body_from_expr(if_else)?; + let some_body = peel_blocks(if_then); + let none_body = peel_blocks(if_else); let method_sugg = if eager_or_lazy::switch_to_eager_eval(cx, none_body) { "map_or" } else { "map_or_else" }; let capture_name = id.name.to_ident_string(); let (as_ref, as_mut) = match &let_expr.kind { diff --git a/clippy_lints/src/question_mark.rs b/clippy_lints/src/question_mark.rs index a5531993ee6..65a962dcd21 100644 --- a/clippy_lints/src/question_mark.rs +++ b/clippy_lints/src/question_mark.rs @@ -1,14 +1,13 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::higher; -use clippy_utils::is_lang_ctor; use clippy_utils::source::snippet_with_applicability; use clippy_utils::sugg::Sugg; use clippy_utils::ty::is_type_diagnostic_item; -use clippy_utils::{eq_expr_value, path_to_local, path_to_local_id}; +use clippy_utils::{eq_expr_value, is_lang_ctor, path_to_local, path_to_local_id, peel_blocks, peel_blocks_with_stmt}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::LangItem::{OptionNone, OptionSome, ResultOk}; -use rustc_hir::{BindingAnnotation, Block, Expr, ExprKind, PatKind, StmtKind}; +use rustc_hir::{BindingAnnotation, Expr, ExprKind, PatKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::sym; @@ -68,14 +67,8 @@ impl QuestionMark { let receiver_str = &Sugg::hir_with_applicability(cx, subject, "..", &mut applicability); let mut replacement: Option<String> = None; if let Some(else_inner) = r#else { - if_chain! { - if let ExprKind::Block(block, None) = &else_inner.kind; - if block.stmts.is_empty(); - if let Some(block_expr) = &block.expr; - if eq_expr_value(cx, subject, block_expr); - then { - replacement = Some(format!("Some({}?)", receiver_str)); - } + if eq_expr_value(cx, subject, peel_blocks(else_inner)) { + replacement = Some(format!("Some({}?)", receiver_str)); } } else if Self::moves_by_default(cx, subject) && !matches!(subject.kind, ExprKind::Call(..) | ExprKind::MethodCall(..)) @@ -110,10 +103,7 @@ impl QuestionMark { if let PatKind::Binding(annot, bind_id, _, _) = fields[0].kind; let by_ref = matches!(annot, BindingAnnotation::Ref | BindingAnnotation::RefMut); - if let ExprKind::Block(block, None) = if_then.kind; - if block.stmts.is_empty(); - if let Some(trailing_expr) = &block.expr; - if path_to_local_id(trailing_expr, bind_id); + if path_to_local_id(peel_blocks(if_then), bind_id); then { let mut applicability = Applicability::MachineApplicable; let receiver_str = snippet_with_applicability(cx, let_expr.span, "..", &mut applicability); @@ -159,14 +149,7 @@ impl QuestionMark { } fn expression_returns_none(cx: &LateContext<'_>, expression: &Expr<'_>) -> bool { - match expression.kind { - ExprKind::Block(block, _) => { - if let Some(return_expression) = Self::return_expression(block) { - return Self::expression_returns_none(cx, return_expression); - } - - false - }, + match peel_blocks_with_stmt(expression).kind { ExprKind::Ret(Some(expr)) => Self::expression_returns_none(cx, expr), ExprKind::Path(ref qpath) => is_lang_ctor(cx, qpath, OptionNone), _ => false, @@ -174,44 +157,12 @@ impl QuestionMark { } fn expression_returns_unmodified_err(cx: &LateContext<'_>, expr: &Expr<'_>, cond_expr: &Expr<'_>) -> bool { - match expr.kind { - ExprKind::Block(block, _) => { - if let Some(return_expression) = Self::return_expression(block) { - return Self::expression_returns_unmodified_err(cx, return_expression, cond_expr); - } - - false - }, + match peel_blocks_with_stmt(expr).kind { ExprKind::Ret(Some(ret_expr)) => Self::expression_returns_unmodified_err(cx, ret_expr, cond_expr), ExprKind::Path(_) => path_to_local(expr) == path_to_local(cond_expr), _ => false, } } - - fn return_expression<'tcx>(block: &Block<'tcx>) -> Option<&'tcx Expr<'tcx>> { - // Check if last expression is a return statement. Then, return the expression - if_chain! { - if block.stmts.len() == 1; - if let Some(expr) = block.stmts.iter().last(); - if let StmtKind::Semi(expr) = expr.kind; - if let ExprKind::Ret(Some(ret_expr)) = expr.kind; - - then { - return Some(ret_expr); - } - } - - // Check for `return` without a semicolon. - if_chain! { - if block.stmts.is_empty(); - if let Some(ExprKind::Ret(Some(ret_expr))) = block.expr.as_ref().map(|e| &e.kind); - then { - return Some(ret_expr); - } - } - - None - } } impl<'tcx> LateLintPass<'tcx> for QuestionMark { diff --git a/clippy_lints/src/strings.rs b/clippy_lints/src/strings.rs index 368274440d5..b03f4583365 100644 --- a/clippy_lints/src/strings.rs +++ b/clippy_lints/src/strings.rs @@ -1,8 +1,8 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_help, span_lint_and_sugg}; use clippy_utils::source::{snippet, snippet_with_applicability}; use clippy_utils::ty::is_type_diagnostic_item; -use clippy_utils::SpanlessEq; use clippy_utils::{get_parent_expr, is_lint_allowed, match_function_call, method_calls, paths}; +use clippy_utils::{peel_blocks, SpanlessEq}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{BinOpKind, BorrowKind, Expr, ExprKind, LangItem, QPath}; @@ -201,7 +201,7 @@ fn is_string(cx: &LateContext<'_>, e: &Expr<'_>) -> bool { } fn is_add(cx: &LateContext<'_>, src: &Expr<'_>, target: &Expr<'_>) -> bool { - match src.kind { + match peel_blocks(src).kind { ExprKind::Binary( Spanned { node: BinOpKind::Add, .. @@ -209,9 +209,6 @@ fn is_add(cx: &LateContext<'_>, src: &Expr<'_>, target: &Expr<'_>) -> bool { left, _, ) => SpanlessEq::new(cx).eq_expr(target, left), - ExprKind::Block(block, _) => { - block.stmts.is_empty() && block.expr.as_ref().map_or(false, |expr| is_add(cx, expr, target)) - }, _ => false, } } diff --git a/clippy_lints/src/utils/internal_lints.rs b/clippy_lints/src/utils/internal_lints.rs index 1f97c8ba7e6..81e9c2e15c9 100644 --- a/clippy_lints/src/utils/internal_lints.rs +++ b/clippy_lints/src/utils/internal_lints.rs @@ -1,11 +1,10 @@ use clippy_utils::consts::{constant_simple, Constant}; use clippy_utils::diagnostics::{span_lint, span_lint_and_help, span_lint_and_sugg, span_lint_and_then}; -use clippy_utils::higher; use clippy_utils::source::snippet; use clippy_utils::ty::match_type; use clippy_utils::{ - is_else_clause, is_expn_of, is_expr_path_def_path, is_lint_allowed, match_def_path, method_calls, path_to_res, - paths, SpanlessEq, + higher, is_else_clause, is_expn_of, is_expr_path_def_path, is_lint_allowed, match_def_path, method_calls, + path_to_res, paths, peel_blocks_with_stmt, SpanlessEq, }; use if_chain::if_chain; use rustc_ast as ast; @@ -662,10 +661,7 @@ impl<'tcx> LateLintPass<'tcx> for CollapsibleCalls { if and_then_args.len() == 5; if let ExprKind::Closure(_, _, body_id, _, _) = &and_then_args[4].kind; let body = cx.tcx.hir().body(*body_id); - if let ExprKind::Block(block, _) = &body.value.kind; - let stmts = &block.stmts; - if stmts.len() == 1 && block.expr.is_none(); - if let StmtKind::Semi(only_expr) = &stmts[0].kind; + let only_expr = peel_blocks_with_stmt(&body.value); if let ExprKind::MethodCall(ps, _, span_call_args, _) = &only_expr.kind; then { let and_then_snippets = get_and_then_snippets(cx, and_then_args); diff --git a/clippy_lints/src/utils/internal_lints/metadata_collector.rs b/clippy_lints/src/utils/internal_lints/metadata_collector.rs index 8051c58bad7..7707eebd622 100644 --- a/clippy_lints/src/utils/internal_lints/metadata_collector.rs +++ b/clippy_lints/src/utils/internal_lints/metadata_collector.rs @@ -8,6 +8,11 @@ //! during any comparison or mapping. (Please take care of this, it's not fun to spend time on such //! a simple mistake) +use crate::utils::internal_lints::{extract_clippy_version_value, is_lint_ref_type}; + +use clippy_utils::diagnostics::span_lint; +use clippy_utils::ty::{match_type, walk_ptrs_ty_depth}; +use clippy_utils::{last_path_segment, match_def_path, match_function_call, match_path, paths}; use if_chain::if_chain; use rustc_ast as ast; use rustc_data_structures::fx::FxHashMap; @@ -25,12 +30,6 @@ use std::fs::{self, OpenOptions}; use std::io::prelude::*; use std::path::Path; -use crate::utils::internal_lints::{extract_clippy_version_value, is_lint_ref_type}; -use clippy_utils::{ - diagnostics::span_lint, last_path_segment, match_def_path, match_function_call, match_path, paths, ty::match_type, - ty::walk_ptrs_ty_depth, -}; - /// This is the output file of the lint collector. const OUTPUT_FILE: &str = "../util/gh-pages/lints.json"; /// These lints are excluded from the export. diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index f011380c127..4e214361435 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -73,10 +73,10 @@ use rustc_hir::intravisit::{walk_expr, ErasedMap, FnKind, NestedVisitorMap, Visi use rustc_hir::itemlikevisit::ItemLikeVisitor; use rustc_hir::LangItem::{OptionNone, ResultErr, ResultOk}; use rustc_hir::{ - def, Arm, BindingAnnotation, Block, Body, Constness, Destination, Expr, ExprKind, FnDecl, ForeignItem, GenericArgs, - HirId, Impl, ImplItem, ImplItemKind, IsAsync, Item, ItemKind, LangItem, Local, MatchSource, Mutability, Node, - Param, Pat, PatKind, Path, PathSegment, PrimTy, QPath, Stmt, StmtKind, TraitItem, TraitItemKind, TraitRef, TyKind, - UnOp, + def, Arm, BindingAnnotation, Block, BlockCheckMode, Body, Constness, Destination, Expr, ExprKind, FnDecl, + ForeignItem, GenericArgs, HirId, Impl, ImplItem, ImplItemKind, IsAsync, Item, ItemKind, LangItem, Local, + MatchSource, Mutability, Node, Param, Pat, PatKind, Path, PathSegment, PrimTy, QPath, Stmt, StmtKind, TraitItem, + TraitItemKind, TraitRef, TyKind, UnOp, }; use rustc_lint::{LateContext, Level, Lint, LintContext}; use rustc_middle::hir::exports::Export; @@ -1224,6 +1224,70 @@ pub fn get_parent_as_impl(tcx: TyCtxt<'_>, id: HirId) -> Option<&Impl<'_>> { } } +/// Removes blocks around an expression, only if the block contains just one expression +/// and no statements. Unsafe blocks are not removed. +/// +/// Examples: +/// * `{}` -> `{}` +/// * `{ x }` -> `x` +/// * `{{ x }}` -> `x` +/// * `{ x; }` -> `{ x; }` +/// * `{ x; y }` -> `{ x; y }` +/// * `{ unsafe { x } }` -> `unsafe { x }` +pub fn peel_blocks<'a>(mut expr: &'a Expr<'a>) -> &'a Expr<'a> { + while let ExprKind::Block( + Block { + stmts: [], + expr: Some(inner), + rules: BlockCheckMode::DefaultBlock, + .. + }, + _, + ) = expr.kind + { + expr = inner; + } + expr +} + +/// Removes blocks around an expression, only if the block contains just one expression +/// or just one expression statement with a semicolon. Unsafe blocks are not removed. +/// +/// Examples: +/// * `{}` -> `{}` +/// * `{ x }` -> `x` +/// * `{ x; }` -> `x` +/// * `{{ x; }}` -> `x` +/// * `{ x; y }` -> `{ x; y }` +/// * `{ unsafe { x } }` -> `unsafe { x }` +pub fn peel_blocks_with_stmt<'a>(mut expr: &'a Expr<'a>) -> &'a Expr<'a> { + while let ExprKind::Block( + Block { + stmts: [], + expr: Some(inner), + rules: BlockCheckMode::DefaultBlock, + .. + } + | Block { + stmts: + [ + Stmt { + kind: StmtKind::Expr(inner) | StmtKind::Semi(inner), + .. + }, + ], + expr: None, + rules: BlockCheckMode::DefaultBlock, + .. + }, + _, + ) = expr.kind + { + expr = inner; + } + expr +} + /// Checks if the given expression is the else clause of either an `if` or `if let` expression. pub fn is_else_clause(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool { let mut iter = tcx.hir().parent_iter(expr.hir_id); @@ -1405,20 +1469,6 @@ pub fn is_automatically_derived(attrs: &[ast::Attribute]) -> bool { has_attr(attrs, sym::automatically_derived) } -/// Remove blocks around an expression. -/// -/// Ie. `x`, `{ x }` and `{{{{ x }}}}` all give `x`. `{ x; y }` and `{}` return -/// themselves. -pub fn remove_blocks<'tcx>(mut expr: &'tcx Expr<'tcx>) -> &'tcx Expr<'tcx> { - while let ExprKind::Block(block, ..) = expr.kind { - match (block.stmts.is_empty(), block.expr.as_ref()) { - (true, Some(e)) => expr = e, - _ => break, - } - } - expr -} - pub fn is_self(slf: &Param<'_>) -> bool { if let PatKind::Binding(.., name, _) = slf.pat.kind { name.name == kw::SelfLower |
