diff --git a/compiler/rustc_ast_lowering/src/block.rs b/compiler/rustc_ast_lowering/src/block.rs index 9444fffc331c7..0ec596e705374 100644 --- a/compiler/rustc_ast_lowering/src/block.rs +++ b/compiler/rustc_ast_lowering/src/block.rs @@ -1,8 +1,8 @@ use crate::{ImplTraitContext, ImplTraitPosition, LoweringContext}; -use rustc_ast::{AttrVec, Block, BlockCheckMode, Expr, Local, LocalKind, Stmt, StmtKind}; +use rustc_ast::{Block, BlockCheckMode, Local, LocalKind, Stmt, StmtKind}; use rustc_hir as hir; use rustc_session::parse::feature_err; -use rustc_span::{sym, DesugaringKind}; +use rustc_span::sym; use smallvec::SmallVec; @@ -36,21 +36,25 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { match s.kind { StmtKind::Local(ref local) => { let hir_id = self.lower_node_id(s.id); - match &local.kind { - LocalKind::InitElse(init, els) => { - let e = self.lower_let_else(hir_id, local, init, els, tail); - expr = Some(e); - // remaining statements are in let-else expression - break; + let els = if let LocalKind::InitElse(_, els) = &local.kind { + if !self.tcx.features().let_else { + feature_err( + &self.tcx.sess.parse_sess, + sym::let_else, + s.span, + "`let...else` statements are unstable", + ) + .emit(); } - _ => { - let local = self.lower_local(local); - self.alias_attrs(hir_id, local.hir_id); - let kind = hir::StmtKind::Local(local); - let span = self.lower_span(s.span); - stmts.push(hir::Stmt { hir_id, kind, span }); - } - } + Some(self.lower_block(els, false)) + } else { + None + }; + let local = self.lower_local(local); + self.alias_attrs(hir_id, local.hir_id); + let kind = hir::StmtKind::Local(local, els); + let span = self.lower_span(s.span); + stmts.push(hir::Stmt { hir_id, kind, span }); } StmtKind::Item(ref it) => { stmts.extend(self.lower_item_ref(it).into_iter().enumerate().map( @@ -115,59 +119,4 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { } } } - - fn lower_let_else( - &mut self, - stmt_hir_id: hir::HirId, - local: &Local, - init: &Expr, - els: &Block, - tail: &[Stmt], - ) -> &'hir hir::Expr<'hir> { - let ty = local - .ty - .as_ref() - .map(|t| self.lower_ty(t, ImplTraitContext::Disallowed(ImplTraitPosition::Variable))); - let span = self.lower_span(local.span); - let span = self.mark_span_with_reason(DesugaringKind::LetElse, span, None); - let init = self.lower_expr(init); - let local_hir_id = self.lower_node_id(local.id); - self.lower_attrs(local_hir_id, &local.attrs); - let let_expr = { - let lex = self.arena.alloc(hir::Let { - hir_id: local_hir_id, - pat: self.lower_pat(&local.pat), - ty, - init, - span, - }); - self.arena.alloc(self.expr(span, hir::ExprKind::Let(lex), AttrVec::new())) - }; - let then_expr = { - let (stmts, expr) = self.lower_stmts(tail); - let block = self.block_all(span, stmts, expr); - self.arena.alloc(self.expr_block(block, AttrVec::new())) - }; - let else_expr = { - let block = self.lower_block(els, false); - self.arena.alloc(self.expr_block(block, AttrVec::new())) - }; - self.alias_attrs(let_expr.hir_id, local_hir_id); - self.alias_attrs(else_expr.hir_id, local_hir_id); - let if_expr = self.arena.alloc(hir::Expr { - hir_id: stmt_hir_id, - span, - kind: hir::ExprKind::If(let_expr, then_expr, Some(else_expr)), - }); - if !self.tcx.features().let_else { - feature_err( - &self.tcx.sess.parse_sess, - sym::let_else, - local.span, - "`let...else` statements are unstable", - ) - .emit(); - } - if_expr - } } diff --git a/compiler/rustc_ast_lowering/src/index.rs b/compiler/rustc_ast_lowering/src/index.rs index ddd54f7c2089d..05210e9b89eac 100644 --- a/compiler/rustc_ast_lowering/src/index.rs +++ b/compiler/rustc_ast_lowering/src/index.rs @@ -284,10 +284,10 @@ impl<'a, 'hir> Visitor<'hir> for NodeCollector<'a, 'hir> { }); } - fn visit_local(&mut self, l: &'hir Local<'hir>) { + fn visit_local(&mut self, l: &'hir Local<'hir>, e: Option<&'hir Block<'hir>>) { self.insert(l.span, l.hir_id, Node::Local(l)); self.with_parent(l.hir_id, |this| { - intravisit::walk_local(this, l); + intravisit::walk_local(this, l, e); }) } diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index fdf60e60914a4..6b420c42d6152 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -2147,7 +2147,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { self.attrs.insert(hir_id.local_id, a); } let local = hir::Local { hir_id, init, pat, source, span: self.lower_span(span), ty: None }; - self.stmt(span, hir::StmtKind::Local(self.arena.alloc(local))) + self.stmt(span, hir::StmtKind::Local(self.arena.alloc(local), None)) } fn block_expr(&mut self, expr: &'hir hir::Expr<'hir>) -> &'hir hir::Block<'hir> { diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index 04f585df34cc2..65cb5f77e0629 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -1296,7 +1296,8 @@ pub struct Stmt<'hir> { #[derive(Debug, HashStable_Generic)] pub enum StmtKind<'hir> { /// A local (`let`) binding. - Local(&'hir Local<'hir>), + /// FIXME: bundle the last two components into another `struct` + Local(&'hir Local<'hir>, Option<&'hir Block<'hir>>), /// An item binding. Item(ItemId), diff --git a/compiler/rustc_hir/src/intravisit.rs b/compiler/rustc_hir/src/intravisit.rs index 531d9f1404021..3585aba5f3db0 100644 --- a/compiler/rustc_hir/src/intravisit.rs +++ b/compiler/rustc_hir/src/intravisit.rs @@ -310,8 +310,8 @@ pub trait Visitor<'v>: Sized { fn visit_foreign_item(&mut self, i: &'v ForeignItem<'v>) { walk_foreign_item(self, i) } - fn visit_local(&mut self, l: &'v Local<'v>) { - walk_local(self, l) + fn visit_local(&mut self, l: &'v Local<'v>, els: Option<&'v Block<'v>>) { + walk_local(self, l, els) } fn visit_block(&mut self, b: &'v Block<'v>) { walk_block(self, b) @@ -466,12 +466,19 @@ pub fn walk_body<'v, V: Visitor<'v>>(visitor: &mut V, body: &'v Body<'v>) { visitor.visit_expr(&body.value); } -pub fn walk_local<'v, V: Visitor<'v>>(visitor: &mut V, local: &'v Local<'v>) { +pub fn walk_local<'v, V: Visitor<'v>>( + visitor: &mut V, + local: &'v Local<'v>, + els: Option<&'v Block<'v>>, +) { // Intentionally visiting the expr first - the initialization expr // dominates the local's definition. walk_list!(visitor, visit_expr, &local.init); visitor.visit_id(local.hir_id); visitor.visit_pat(&local.pat); + if let Some(els) = els { + visitor.visit_block(els); + } walk_list!(visitor, visit_ty, &local.ty); } @@ -1055,9 +1062,9 @@ pub fn walk_block<'v, V: Visitor<'v>>(visitor: &mut V, block: &'v Block<'v>) { pub fn walk_stmt<'v, V: Visitor<'v>>(visitor: &mut V, statement: &'v Stmt<'v>) { visitor.visit_id(statement.hir_id); - match statement.kind { - StmtKind::Local(ref local) => visitor.visit_local(local), - StmtKind::Item(item) => visitor.visit_nested_item(item), + match &statement.kind { + StmtKind::Local(ref local, els) => visitor.visit_local(local, *els), + StmtKind::Item(item) => visitor.visit_nested_item(*item), StmtKind::Expr(ref expression) | StmtKind::Semi(ref expression) => { visitor.visit_expr(expression) } diff --git a/compiler/rustc_hir_pretty/src/lib.rs b/compiler/rustc_hir_pretty/src/lib.rs index 50acb0270b0be..7f8601308734f 100644 --- a/compiler/rustc_hir_pretty/src/lib.rs +++ b/compiler/rustc_hir_pretty/src/lib.rs @@ -883,7 +883,12 @@ impl<'a> State<'a> { self.ann.post(self, AnnNode::SubItem(ii.hir_id())) } - pub fn print_local(&mut self, init: Option<&hir::Expr<'_>>, decl: impl Fn(&mut Self)) { + pub fn print_local( + &mut self, + init: Option<&hir::Expr<'_>>, + els: Option<&hir::Block<'_>>, + decl: impl Fn(&mut Self), + ) { self.space_if_not_bol(); self.ibox(INDENT_UNIT); self.word_nbsp("let"); @@ -897,14 +902,21 @@ impl<'a> State<'a> { self.word_space("="); self.print_expr(init); } + + if let Some(els) = els { + self.nbsp(); + self.word_space("else"); + self.print_block(els); + } + self.end() } pub fn print_stmt(&mut self, st: &hir::Stmt<'_>) { self.maybe_print_comment(st.span.lo()); match st.kind { - hir::StmtKind::Local(loc) => { - self.print_local(loc.init, |this| this.print_local_decl(loc)); + hir::StmtKind::Local(loc, els) => { + self.print_local(loc.init, els, |this| this.print_local_decl(loc)); } hir::StmtKind::Item(item) => self.ann.nested(self, Nested::Item(item)), hir::StmtKind::Expr(expr) => { @@ -1404,7 +1416,7 @@ impl<'a> State<'a> { // Print `let _t = $init;`: let temp = Ident::from_str("_t"); - self.print_local(Some(init), |this| this.print_ident(temp)); + self.print_local(Some(init), None, |this| this.print_ident(temp)); self.word(";"); // Print `_t`: @@ -2293,7 +2305,7 @@ fn expr_requires_semi_to_be_stmt(e: &hir::Expr<'_>) -> bool { /// seen the semicolon, and thus don't need another. fn stmt_ends_with_semi(stmt: &hir::StmtKind<'_>) -> bool { match *stmt { - hir::StmtKind::Local(_) => true, + hir::StmtKind::Local(_, _) => true, hir::StmtKind::Item(_) => false, hir::StmtKind::Expr(e) => expr_requires_semi_to_be_stmt(e), hir::StmtKind::Semi(..) => false, diff --git a/compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs b/compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs index 4d29fc469462c..ace30e61769f8 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs @@ -1,12 +1,13 @@ use crate::infer::type_variable::TypeVariableOriginKind; use crate::infer::InferCtxt; +use hir::{Block, LocalSource}; use rustc_errors::{pluralize, struct_span_err, Applicability, DiagnosticBuilder, ErrorGuaranteed}; use rustc_hir as hir; use rustc_hir::def::Res; use rustc_hir::def::{CtorOf, DefKind, Namespace}; use rustc_hir::def_id::DefId; use rustc_hir::intravisit::{self, Visitor}; -use rustc_hir::{Body, Expr, ExprKind, FnRetTy, HirId, Local, LocalSource}; +use rustc_hir::{Body, Expr, ExprKind, FnRetTy, HirId, Local}; use rustc_middle::hir::nested_filter; use rustc_middle::infer::unify_key::ConstVariableOriginKind; use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow, AutoBorrowMutability}; @@ -952,8 +953,8 @@ impl<'a, 'tcx> Visitor<'tcx> for FindInferSourceVisitor<'a, 'tcx> { self.infcx.tcx.hir() } - fn visit_local(&mut self, local: &'tcx Local<'tcx>) { - intravisit::walk_local(self, local); + fn visit_local(&mut self, local: &'tcx Local<'tcx>, els: Option<&'tcx Block<'tcx>>) { + intravisit::walk_local(self, local, els); if let Some(ty) = self.opt_node_type(local.hir_id) { if self.generic_arg_contains_target(ty.into()) { diff --git a/compiler/rustc_lint/src/late.rs b/compiler/rustc_lint/src/late.rs index 27f67207209dd..695d89e791202 100644 --- a/compiler/rustc_lint/src/late.rs +++ b/compiler/rustc_lint/src/late.rs @@ -251,10 +251,10 @@ impl<'tcx, T: LateLintPass<'tcx>> hir_visit::Visitor<'tcx> for LateContextAndPas } } - fn visit_local(&mut self, l: &'tcx hir::Local<'tcx>) { + fn visit_local(&mut self, l: &'tcx hir::Local<'tcx>, e: Option<&'tcx hir::Block<'tcx>>) { self.with_lint_attrs(l.hir_id, |cx| { - lint_callback!(cx, check_local, l); - hir_visit::walk_local(cx, l); + lint_callback!(cx, check_local, l, e); + hir_visit::walk_local(cx, l, e); }) } diff --git a/compiler/rustc_lint/src/levels.rs b/compiler/rustc_lint/src/levels.rs index 00e96f20d1aaa..43c7ff3c4bb20 100644 --- a/compiler/rustc_lint/src/levels.rs +++ b/compiler/rustc_lint/src/levels.rs @@ -783,9 +783,9 @@ impl<'tcx> intravisit::Visitor<'tcx> for LintLevelMapBuilder<'tcx> { }) } - fn visit_local(&mut self, l: &'tcx hir::Local<'tcx>) { + fn visit_local(&mut self, l: &'tcx hir::Local<'tcx>, e: Option<&'tcx hir::Block<'tcx>>) { self.with_lint_attrs(l.hir_id, |builder| { - intravisit::walk_local(builder, l); + intravisit::walk_local(builder, l, e); }) } diff --git a/compiler/rustc_lint/src/passes.rs b/compiler/rustc_lint/src/passes.rs index b1b4229b1f738..41d8e5cf859dd 100644 --- a/compiler/rustc_lint/src/passes.rs +++ b/compiler/rustc_lint/src/passes.rs @@ -24,7 +24,7 @@ macro_rules! late_lint_methods { fn check_foreign_item_post(a: &$hir hir::ForeignItem<$hir>); fn check_item(a: &$hir hir::Item<$hir>); fn check_item_post(a: &$hir hir::Item<$hir>); - fn check_local(a: &$hir hir::Local<$hir>); + fn check_local(a: &$hir hir::Local<$hir>, b: Option<&$hir hir::Block<$hir>>); fn check_block(a: &$hir hir::Block<$hir>); fn check_block_post(a: &$hir hir::Block<$hir>); fn check_stmt(a: &$hir hir::Stmt<$hir>); diff --git a/compiler/rustc_middle/src/hir/map/mod.rs b/compiler/rustc_middle/src/hir/map/mod.rs index 3e99ba5742a4b..85fc897c7ff03 100644 --- a/compiler/rustc_middle/src/hir/map/mod.rs +++ b/compiler/rustc_middle/src/hir/map/mod.rs @@ -789,7 +789,7 @@ impl<'hir> Map<'hir> { | Node::ForeignItem(_) | Node::TraitItem(_) | Node::ImplItem(_) - | Node::Stmt(Stmt { kind: StmtKind::Local(_), .. }) => break, + | Node::Stmt(Stmt { kind: StmtKind::Local(_, _), .. }) => break, Node::Expr(expr @ Expr { kind: ExprKind::If(..) | ExprKind::Match(..), .. }) => { return Some(expr); } diff --git a/compiler/rustc_middle/src/thir.rs b/compiler/rustc_middle/src/thir.rs index 3fe6394ad7e9c..3e5f6bb8f0bcf 100644 --- a/compiler/rustc_middle/src/thir.rs +++ b/compiler/rustc_middle/src/thir.rs @@ -182,6 +182,9 @@ pub enum StmtKind<'tcx> { /// `let pat: ty = ` initializer: Option, + /// `let pat: ty = else { } + else_block: Option, + /// The lint level for this `let` statement. lint_level: LintLevel, }, diff --git a/compiler/rustc_middle/src/thir/visit.rs b/compiler/rustc_middle/src/thir/visit.rs index c8d09875c2896..97249fdd17563 100644 --- a/compiler/rustc_middle/src/thir/visit.rs +++ b/compiler/rustc_middle/src/thir/visit.rs @@ -167,11 +167,15 @@ pub fn walk_stmt<'a, 'tcx: 'a, V: Visitor<'a, 'tcx>>(visitor: &mut V, stmt: &Stm init_scope: _, ref pattern, lint_level: _, + else_block, } => { if let Some(init) = initializer { visitor.visit_expr(&visitor.thir()[*init]); } visitor.visit_pat(pattern); + if let Some(block) = else_block { + visitor.visit_block(block) + } } } } diff --git a/compiler/rustc_mir_build/src/build/block.rs b/compiler/rustc_mir_build/src/build/block.rs index a83328c0cabc6..cb8be51a08562 100644 --- a/compiler/rustc_mir_build/src/build/block.rs +++ b/compiler/rustc_mir_build/src/build/block.rs @@ -99,6 +99,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { ref pattern, initializer, lint_level, + else_block, } => { let ignores_expr_result = matches!(*pattern.kind, PatKind::Wild); this.block_context.push(BlockFrame::Statement { ignores_expr_result }); @@ -124,18 +125,30 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { |this| { let scope = (*init_scope, source_info); this.in_scope(scope, *lint_level, |this| { - this.declare_bindings( - visibility_scope, - remainder_span, - pattern, - ArmHasGuard(false), - Some((None, initializer_span)), - ); - this.expr_into_pattern(block, pattern.clone(), init) + if let Some(else_block) = else_block { + this.ast_let_else( + block, + init, + initializer_span, + else_block, + visibility_scope, + remainder_span, + pattern, + ) + } else { + this.declare_bindings( + visibility_scope, + remainder_span, + pattern, + ArmHasGuard(false), + Some((None, initializer_span)), + ); + this.expr_into_pattern(block, pattern.clone(), init) // irrefutable pattern + } }) - } + }, ) - ); + ) } else { let scope = (*init_scope, source_info); unpack!(this.in_scope(scope, *lint_level, |this| { diff --git a/compiler/rustc_mir_build/src/build/matches/mod.rs b/compiler/rustc_mir_build/src/build/matches/mod.rs index 1628f1a4b850b..7067a48b783ec 100644 --- a/compiler/rustc_mir_build/src/build/matches/mod.rs +++ b/compiler/rustc_mir_build/src/build/matches/mod.rs @@ -1615,7 +1615,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { // those N possible outcomes, create a (initially empty) // vector of candidates. Those are the candidates that still // apply if the test has that particular outcome. - debug!("match_candidates: test={:?} match_pair={:?}", test, match_pair); + debug!("test_candidates: test={:?} match_pair={:?}", test, match_pair); let mut target_candidates: Vec>> = vec![]; target_candidates.resize_with(test.targets(), Default::default); @@ -1635,8 +1635,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { } // at least the first candidate ought to be tested assert!(total_candidate_count > candidates.len()); - debug!("tested_candidates: {}", total_candidate_count - candidates.len()); - debug!("untested_candidates: {}", candidates.len()); + debug!("test_candidates: tested_candidates: {}", total_candidate_count - candidates.len()); + debug!("test_candidates: untested_candidates: {}", candidates.len()); // HACK(matthewjasper) This is a closure so that we can let the test // create its blocks before the rest of the match. This currently @@ -2274,4 +2274,75 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { debug!("declare_binding: vars={:?}", locals); self.var_indices.insert(var_id, locals); } + + pub(crate) fn ast_let_else( + &mut self, + mut block: BasicBlock, + init: &Expr<'tcx>, + initializer_span: Span, + else_block: &Block, + visibility_scope: Option, + remainder_span: Span, + pattern: &Pat<'tcx>, + ) -> BlockAnd<()> { + let scrutinee = unpack!(block = self.lower_scrutinee(block, init, initializer_span)); + let pat = Pat { ty: init.ty, span: else_block.span, kind: Box::new(PatKind::Wild) }; + let mut wildcard = Candidate::new(scrutinee.clone(), &pat, false); + self.declare_bindings( + visibility_scope, + remainder_span, + pattern, + ArmHasGuard(false), + Some((None, initializer_span)), + ); + let mut candidate = Candidate::new(scrutinee.clone(), pattern, false); + let fake_borrow_temps = self.lower_match_tree( + block, + initializer_span, + pattern.span, + false, + &mut [&mut candidate, &mut wildcard], + ); + // This block is for the matching case + let matching = self.bind_pattern( + self.source_info(pattern.span), + candidate, + None, + &fake_borrow_temps, + initializer_span, + None, + None, + None, + ); + // This block is for the failure case + let failure = self.bind_pattern( + self.source_info(else_block.span), + wildcard, + None, + &fake_borrow_temps, + initializer_span, + None, + None, + None, + ); + // This place is not really used because this destination place + // should never be used to take values at the end of the failure + // block. + let dummy_place = Place { local: RETURN_PLACE, projection: ty::List::empty() }; + let failure_block; + unpack!( + failure_block = self.ast_block( + dummy_place, + failure, + else_block, + self.source_info(else_block.span), + ) + ); + self.cfg.terminate( + failure_block, + self.source_info(else_block.span), + TerminatorKind::Unreachable, + ); + matching.unit() + } } diff --git a/compiler/rustc_mir_build/src/thir/cx/block.rs b/compiler/rustc_mir_build/src/thir/cx/block.rs index 59750d5d0b88e..d77d74e25f6e5 100644 --- a/compiler/rustc_mir_build/src/thir/cx/block.rs +++ b/compiler/rustc_mir_build/src/thir/cx/block.rs @@ -48,7 +48,7 @@ impl<'tcx> Cx<'tcx> { .filter_map(|(index, stmt)| { let hir_id = stmt.hir_id; let opt_dxn_ext = self.region_scope_tree.opt_destruction_scope(hir_id.local_id); - match stmt.kind { + match &stmt.kind { hir::StmtKind::Expr(ref expr) | hir::StmtKind::Semi(ref expr) => { let stmt = Stmt { kind: StmtKind::Expr { @@ -66,7 +66,7 @@ impl<'tcx> Cx<'tcx> { // ignore for purposes of the MIR None } - hir::StmtKind::Local(ref local) => { + hir::StmtKind::Local(local, els) => { let remainder_scope = region::Scope { id: block_id, data: region::ScopeData::Remainder(region::FirstStatementIndex::new( @@ -74,6 +74,8 @@ impl<'tcx> Cx<'tcx> { )), }; + let else_block = els.map(|els| self.mirror_block(els)); + let mut pattern = self.pattern_from_hir(local.pat); debug!(?pattern); @@ -110,6 +112,7 @@ impl<'tcx> Cx<'tcx> { }, pattern, initializer: local.init.map(|init| self.mirror_expr(init)), + else_block, lint_level: LintLevel::Explicit(local.hir_id), }, opt_destruction_scope: opt_dxn_ext, diff --git a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs index 75fd156ebfdf3..daaabd855c87e 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs @@ -21,7 +21,7 @@ use rustc_session::lint::builtin::{ }; use rustc_session::Session; use rustc_span::source_map::Spanned; -use rustc_span::{BytePos, DesugaringKind, ExpnKind, Span}; +use rustc_span::{BytePos, Span}; pub(crate) fn check_match(tcx: TyCtxt<'_>, def_id: DefId) { let body_id = match def_id.as_local() { @@ -75,8 +75,11 @@ impl<'tcx> Visitor<'tcx> for MatchVisitor<'_, '_, 'tcx> { } } - fn visit_local(&mut self, loc: &'tcx hir::Local<'tcx>) { - intravisit::walk_local(self, loc); + fn visit_local(&mut self, loc: &'tcx hir::Local<'tcx>, els: Option<&'tcx hir::Block<'tcx>>) { + intravisit::walk_local(self, loc, els); + if let Some(init) = &loc.init && els.is_some() { + self.check_let(&loc.pat, &init, loc.span); + } let (msg, sp) = match loc.source { hir::LocalSource::Normal => ("local binding", Some(loc.span)), @@ -84,7 +87,9 @@ impl<'tcx> Visitor<'tcx> for MatchVisitor<'_, '_, 'tcx> { hir::LocalSource::AwaitDesugar => ("`await` future binding", None), hir::LocalSource::AssignDesugar(_) => ("destructuring assignment binding", None), }; - self.check_irrefutable(&loc.pat, msg, sp); + if els.is_none() { + self.check_irrefutable(&loc.pat, msg, sp); + } } fn visit_param(&mut self, param: &'tcx hir::Param<'tcx>) { @@ -1125,17 +1130,16 @@ fn let_source_parent(tcx: TyCtxt<'_>, parent: HirId, pat_id: Option) -> L }) if Some(*hir_id) == pat_id => { return LetSource::IfLetGuard; } - hir::Node::Expr(hir::Expr { kind: hir::ExprKind::Let(..), span, .. }) => { - let expn_data = span.ctxt().outer_expn_data(); - if let ExpnKind::Desugaring(DesugaringKind::LetElse) = expn_data.kind { - return LetSource::LetElse(expn_data.call_site); - } - } _ => {} } let parent_parent = hir.get_parent_node(parent); let parent_parent_node = hir.get(parent_parent); + if let hir::Node::Stmt(hir::Stmt { kind: hir::StmtKind::Local(_, Some(_)), span, .. }) = + parent_parent_node + { + return LetSource::LetElse(*span); + } let parent_parent_parent = hir.get_parent_node(parent_parent); let parent_parent_parent_parent = hir.get_parent_node(parent_parent_parent); diff --git a/compiler/rustc_mir_transform/src/remove_uninit_drops.rs b/compiler/rustc_mir_transform/src/remove_uninit_drops.rs index efa45883eab4b..c48aa9a90efbe 100644 --- a/compiler/rustc_mir_transform/src/remove_uninit_drops.rs +++ b/compiler/rustc_mir_transform/src/remove_uninit_drops.rs @@ -102,7 +102,7 @@ fn is_needs_drop_and_init<'tcx>( let field_needs_drop_and_init = |(f, f_ty, mpi)| { let child = move_path_children_matching(move_data, mpi, |x| x.is_field_to(f)); let Some(mpi) = child else { - return f_ty.needs_drop(tcx, param_env); + return Ty::needs_drop(f_ty, tcx, param_env); }; is_needs_drop_and_init(tcx, param_env, maybe_inits, move_data, f_ty, mpi) diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index d0723c68a77e8..f78c0f4f545ff 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -2311,7 +2311,7 @@ impl<'tcx> Visitor<'tcx> for CheckAttrVisitor<'tcx> { fn visit_stmt(&mut self, stmt: &'tcx hir::Stmt<'tcx>) { // When checking statements ignore expressions, they will be checked later. - if let hir::StmtKind::Local(ref l) = stmt.kind { + if let hir::StmtKind::Local(ref l, _) = stmt.kind { self.check_attributes(l.hir_id, stmt.span, Target::Statement, None); } intravisit::walk_stmt(self, stmt) diff --git a/compiler/rustc_passes/src/hir_stats.rs b/compiler/rustc_passes/src/hir_stats.rs index a3be827a7ccec..d8e97e5adef2f 100644 --- a/compiler/rustc_passes/src/hir_stats.rs +++ b/compiler/rustc_passes/src/hir_stats.rs @@ -131,9 +131,9 @@ impl<'v> hir_visit::Visitor<'v> for StatCollector<'v> { hir_visit::walk_foreign_item(self, i) } - fn visit_local(&mut self, l: &'v hir::Local<'v>) { + fn visit_local(&mut self, l: &'v hir::Local<'v>, e: Option<&'v hir::Block<'v>>) { self.record("Local", Id::Node(l.hir_id), l); - hir_visit::walk_local(self, l) + hir_visit::walk_local(self, l, e) } fn visit_block(&mut self, b: &'v hir::Block<'v>) { diff --git a/compiler/rustc_passes/src/liveness.rs b/compiler/rustc_passes/src/liveness.rs index 0070c0699a4aa..f125f107fe2a5 100644 --- a/compiler/rustc_passes/src/liveness.rs +++ b/compiler/rustc_passes/src/liveness.rs @@ -278,7 +278,7 @@ impl<'tcx> IrMaps<'tcx> { pats.extend(inner_pat.iter()); } Struct(_, fields, _) => { - let (short, not_short): (Vec<&_>, Vec<&_>) = + let (short, not_short): (Vec<_>, _) = fields.iter().partition(|f| f.is_shorthand); shorthand_field_ids.extend(short.iter().map(|f| f.pat.hir_id)); pats.extend(not_short.iter().map(|f| f.pat)); @@ -298,7 +298,7 @@ impl<'tcx> IrMaps<'tcx> { } } - return shorthand_field_ids; + shorthand_field_ids } fn add_from_pat(&mut self, pat: &hir::Pat<'tcx>) { @@ -366,9 +366,12 @@ impl<'tcx> Visitor<'tcx> for IrMaps<'tcx> { lsets.warn_about_unused_args(body, entry_ln); } - fn visit_local(&mut self, local: &'tcx hir::Local<'tcx>) { + fn visit_local(&mut self, local: &'tcx hir::Local<'tcx>, els: Option<&'tcx hir::Block<'tcx>>) { self.add_from_pat(&local.pat); - intravisit::walk_local(self, local); + if els.is_some() { + self.add_live_node_for_node(local.hir_id, ExprNode(local.span, local.hir_id)); + } + intravisit::walk_local(self, local, els); } fn visit_arm(&mut self, arm: &'tcx hir::Arm<'tcx>) { @@ -785,7 +788,7 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> { fn propagate_through_stmt(&mut self, stmt: &hir::Stmt<'_>, succ: LiveNode) -> LiveNode { match stmt.kind { - hir::StmtKind::Local(ref local) => { + hir::StmtKind::Local(ref local, els) => { // Note: we mark the variable as defined regardless of whether // there is an initializer. Initially I had thought to only mark // the live variable as defined if it was initialized, and then we @@ -800,8 +803,40 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> { // initialization, which is mildly more complex than checking // once at the func header but otherwise equivalent. - let succ = self.propagate_through_opt_expr(local.init, succ); - self.define_bindings_in_pat(&local.pat, succ) + if let Some(els) = els { + // Eventually, `let pat: ty = init else { els };` is mostly equivalent to + // `let (bindings, ...) = match init { pat => (bindings, ...), _ => els };` + // except that extended lifetime applies at the `init` location. + // + // (e) + // | + // v + // (expr) + // / \ + // | | + // v v + // bindings els + // | + // v + // ( succ ) + // + if let Some(init) = local.init { + let else_ln = self.propagate_through_block(els, succ); + let ln = self.live_node(local.hir_id, local.span); + self.init_from_succ(ln, succ); + self.merge_from_succ(ln, else_ln); + let succ = self.propagate_through_expr(init, ln); + self.define_bindings_in_pat(&local.pat, succ) + } else { + span_bug!( + stmt.span, + "variable is uninitialized but an unexpected else branch is found" + ) + } + } else { + let succ = self.propagate_through_opt_expr(local.init, succ); + self.define_bindings_in_pat(&local.pat, succ) + } } hir::StmtKind::Item(..) => succ, hir::StmtKind::Expr(ref expr) | hir::StmtKind::Semi(ref expr) => { @@ -1121,7 +1156,7 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> { // (rvalue) || (rvalue) // | || | // v || v - // (write of place) || (place components) + // (write of place) || (place components) // | || | // v || v // (succ) || (succ) @@ -1306,14 +1341,14 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> { // Checking for error conditions impl<'a, 'tcx> Visitor<'tcx> for Liveness<'a, 'tcx> { - fn visit_local(&mut self, local: &'tcx hir::Local<'tcx>) { + fn visit_local(&mut self, local: &'tcx hir::Local<'tcx>, els: Option<&'tcx hir::Block<'tcx>>) { self.check_unused_vars_in_pat(&local.pat, None, |spans, hir_id, ln, var| { if local.init.is_some() { self.warn_about_dead_assign(spans, hir_id, ln, var); } }); - intravisit::walk_local(self, local); + intravisit::walk_local(self, local, els); } fn visit_expr(&mut self, ex: &'tcx Expr<'tcx>) { diff --git a/compiler/rustc_privacy/src/lib.rs b/compiler/rustc_privacy/src/lib.rs index 5560d44aa0d52..05345f73ef65f 100644 --- a/compiler/rustc_privacy/src/lib.rs +++ b/compiler/rustc_privacy/src/lib.rs @@ -1275,7 +1275,7 @@ impl<'tcx> Visitor<'tcx> for TypePrivacyVisitor<'tcx> { intravisit::walk_pat(self, pattern); } - fn visit_local(&mut self, local: &'tcx hir::Local<'tcx>) { + fn visit_local(&mut self, local: &'tcx hir::Local<'tcx>, els: Option<&'tcx hir::Block<'tcx>>) { if let Some(init) = local.init { if self.check_expr_pat_type(init.hir_id, init.span) { // Do not report duplicate errors for `let x = y`. @@ -1283,7 +1283,7 @@ impl<'tcx> Visitor<'tcx> for TypePrivacyVisitor<'tcx> { } } - intravisit::walk_local(self, local); + intravisit::walk_local(self, local, els); } // Check types in item interfaces. diff --git a/compiler/rustc_save_analysis/src/dump_visitor.rs b/compiler/rustc_save_analysis/src/dump_visitor.rs index 6eb2f2d929d57..2aaacad4ba4e2 100644 --- a/compiler/rustc_save_analysis/src/dump_visitor.rs +++ b/compiler/rustc_save_analysis/src/dump_visitor.rs @@ -82,14 +82,7 @@ impl<'tcx> DumpVisitor<'tcx> { pub fn new(save_ctxt: SaveContext<'tcx>) -> DumpVisitor<'tcx> { let span_utils = SpanUtils::new(&save_ctxt.tcx.sess); let dumper = Dumper::new(save_ctxt.config.clone()); - DumpVisitor { - tcx: save_ctxt.tcx, - save_ctxt, - dumper, - span: span_utils, - // mac_defs: FxHashSet::default(), - // macro_calls: FxHashSet::default(), - } + DumpVisitor { tcx: save_ctxt.tcx, save_ctxt, dumper, span: span_utils } } pub fn analysis(&self) -> &rls_data::Analysis { @@ -1421,13 +1414,14 @@ impl<'tcx> Visitor<'tcx> for DumpVisitor<'tcx> { intravisit::walk_stmt(self, s) } - fn visit_local(&mut self, l: &'tcx hir::Local<'tcx>) { + fn visit_local(&mut self, l: &'tcx hir::Local<'tcx>, e: Option<&'tcx hir::Block<'tcx>>) { self.process_macro_use(l.span); self.process_var_decl(&l.pat); - // Just walk the initializer and type (don't want to walk the pattern again). + // Just walk the initializer, the else branch and type (don't want to walk the pattern again). walk_list!(self, visit_ty, &l.ty); walk_list!(self, visit_expr, &l.init); + walk_list!(self, visit_block, e); } fn visit_foreign_item(&mut self, item: &'tcx hir::ForeignItem<'tcx>) { diff --git a/compiler/rustc_span/src/hygiene.rs b/compiler/rustc_span/src/hygiene.rs index 3df4dfb74b319..29879c48b04ae 100644 --- a/compiler/rustc_span/src/hygiene.rs +++ b/compiler/rustc_span/src/hygiene.rs @@ -1141,7 +1141,6 @@ pub enum DesugaringKind { Async, Await, ForLoop, - LetElse, WhileLoop, } @@ -1157,7 +1156,6 @@ impl DesugaringKind { DesugaringKind::YeetExpr => "`do yeet` expression", DesugaringKind::OpaqueTy => "`impl Trait`", DesugaringKind::ForLoop => "`for` loop", - DesugaringKind::LetElse => "`let...else`", DesugaringKind::WhileLoop => "`while` loop", } } diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs index 33585de600130..adbe71b83ed92 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs @@ -734,7 +734,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { let hir_id = hir.local_def_id_to_hir_id(def_id.as_local()?); let parent_node = hir.get_parent_node(hir_id); match hir.find(parent_node) { - Some(hir::Node::Stmt(hir::Stmt { kind: hir::StmtKind::Local(local), .. })) => { + Some(hir::Node::Stmt(hir::Stmt { kind: hir::StmtKind::Local(local, _), .. })) => { get_name(err, &local.pat.kind) } // Different to previous arm because one is `&hir::Local` and the other @@ -1311,7 +1311,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { visitor.visit_body(&body); let typeck_results = self.in_progress_typeck_results.map(|t| t.borrow()).unwrap(); - let Some(liberated_sig) = typeck_results.liberated_fn_sigs().get(fn_hir_id) else { return false; }; + let Some(liberated_sig) = typeck_results.liberated_fn_sigs().get(fn_hir_id).copied() else { return false; }; let ret_types = visitor .returns diff --git a/compiler/rustc_typeck/src/check/expr.rs b/compiler/rustc_typeck/src/check/expr.rs index 58c01a34cad51..5ae6ed4f72f3f 100644 --- a/compiler/rustc_typeck/src/check/expr.rs +++ b/compiler/rustc_typeck/src/check/expr.rs @@ -997,26 +997,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { coerce.coerce(self, &self.misc(sp), then_expr, then_ty); if let Some(else_expr) = opt_else_expr { - let else_ty = if sp.desugaring_kind() == Some(DesugaringKind::LetElse) { - // todo introduce `check_expr_with_expectation(.., Expectation::LetElse)` - // for errors that point to the offending expression rather than the entire block. - // We could use `check_expr_eq_type(.., tcx.types.never)`, but then there is no - // way to detect that the expected type originated from let-else and provide - // a customized error. - let else_ty = self.check_expr(else_expr); - let cause = self.cause(else_expr.span, ObligationCauseCode::LetElse); - - if let Some(mut err) = - self.demand_eqtype_with_origin(&cause, self.tcx.types.never, else_ty) - { - err.emit(); - self.tcx.ty_error() - } else { - else_ty - } - } else { - self.check_expr_with_expectation(else_expr, expected) - }; + let else_ty = self.check_expr_with_expectation(else_expr, expected); let else_diverges = self.diverges.get(); let opt_suggest_box_span = self.opt_suggest_box_span(else_ty, orig_expected); diff --git a/compiler/rustc_typeck/src/check/fn_ctxt/checks.rs b/compiler/rustc_typeck/src/check/fn_ctxt/checks.rs index a7c7089234aa9..fcd610e677a75 100644 --- a/compiler/rustc_typeck/src/check/fn_ctxt/checks.rs +++ b/compiler/rustc_typeck/src/check/fn_ctxt/checks.rs @@ -1218,8 +1218,23 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } /// Type check a `let` statement. - pub fn check_decl_local(&self, local: &'tcx hir::Local<'tcx>) { + pub fn check_decl_local( + &self, + local: &'tcx hir::Local<'tcx>, + els: Option<&'tcx hir::Block<'tcx>>, + ) { self.check_decl(local.into()); + if let Some(blk) = els { + let previous_diverges = self.diverges.get(); + let else_ty = self.check_block_with_expected(blk, NoExpectation); + let cause = self.cause(blk.span, ObligationCauseCode::LetElse); + if let Some(mut err) = + self.demand_eqtype_with_origin(&cause, self.tcx.types.never, else_ty) + { + err.emit(); + } + self.diverges.set(previous_diverges); + } } pub fn check_stmt(&self, stmt: &'tcx hir::Stmt<'tcx>, is_last: bool) { @@ -1236,8 +1251,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let old_has_errors = self.has_errors.replace(false); match stmt.kind { - hir::StmtKind::Local(ref l) => { - self.check_decl_local(&l); + hir::StmtKind::Local(l, e) => { + self.check_decl_local(l, e); } // Ignore for now. hir::StmtKind::Item(_) => {} @@ -1396,7 +1411,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { source: hir::LocalSource::AssignDesugar(_), .. - }), + }, _), .. }, hir::Stmt { diff --git a/compiler/rustc_typeck/src/check/gather_locals.rs b/compiler/rustc_typeck/src/check/gather_locals.rs index 576dc6f127cbd..956f315f68426 100644 --- a/compiler/rustc_typeck/src/check/gather_locals.rs +++ b/compiler/rustc_typeck/src/check/gather_locals.rs @@ -99,9 +99,9 @@ impl<'a, 'tcx> GatherLocalsVisitor<'a, 'tcx> { impl<'a, 'tcx> Visitor<'tcx> for GatherLocalsVisitor<'a, 'tcx> { // Add explicitly-declared locals. - fn visit_local(&mut self, local: &'tcx hir::Local<'tcx>) { + fn visit_local(&mut self, local: &'tcx hir::Local<'tcx>, els: Option<&'tcx hir::Block<'tcx>>) { self.declare(local.into()); - intravisit::walk_local(self, local); + intravisit::walk_local(self, local, els) } fn visit_let_expr(&mut self, let_expr: &'tcx hir::Let<'tcx>) { diff --git a/compiler/rustc_typeck/src/check/region.rs b/compiler/rustc_typeck/src/check/region.rs index 9f1368a3e0777..54fb17734f4de 100644 --- a/compiler/rustc_typeck/src/check/region.rs +++ b/compiler/rustc_typeck/src/check/region.rs @@ -460,6 +460,7 @@ fn resolve_local<'tcx>( visitor: &mut RegionResolutionVisitor<'tcx>, pat: Option<&'tcx hir::Pat<'tcx>>, init: Option<&'tcx hir::Expr<'tcx>>, + els: Option<&'tcx hir::Block<'tcx>>, ) { debug!("resolve_local(pat={:?}, init={:?})", pat, init); @@ -537,13 +538,18 @@ fn resolve_local<'tcx>( } } - // Make sure we visit the initializer first, so expr_and_pat_count remains correct + // Make sure we visit the initializer first, so expr_and_pat_count remains correct. + // The correct order, as shared between generator_interior, drop_ranges and intravisitor, + // is to walk initializer, followed by pattern bindings, finally followed by the `else` block. if let Some(expr) = init { visitor.visit_expr(expr); } if let Some(pat) = pat { visitor.visit_pat(pat); } + if let Some(els) = els { + visitor.visit_block(els); + } /// Returns `true` if `pat` match the `P&` non-terminal. /// @@ -764,7 +770,7 @@ impl<'tcx> Visitor<'tcx> for RegionResolutionVisitor<'tcx> { // (i.e., `'static`), which means that after `g` returns, it drops, // and all the associated destruction scope rules apply. self.cx.var_parent = None; - resolve_local(self, None, Some(&body.value)); + resolve_local(self, None, Some(&body.value), None); } if body.generator_kind.is_some() { @@ -790,8 +796,8 @@ impl<'tcx> Visitor<'tcx> for RegionResolutionVisitor<'tcx> { fn visit_expr(&mut self, ex: &'tcx Expr<'tcx>) { resolve_expr(self, ex); } - fn visit_local(&mut self, l: &'tcx Local<'tcx>) { - resolve_local(self, Some(&l.pat), l.init); + fn visit_local(&mut self, l: &'tcx Local<'tcx>, e: Option<&'tcx Block<'tcx>>) { + resolve_local(self, Some(&l.pat), l.init, e) } } diff --git a/compiler/rustc_typeck/src/check/writeback.rs b/compiler/rustc_typeck/src/check/writeback.rs index 41d241f84acea..b61ec13e388ac 100644 --- a/compiler/rustc_typeck/src/check/writeback.rs +++ b/compiler/rustc_typeck/src/check/writeback.rs @@ -321,8 +321,8 @@ impl<'cx, 'tcx> Visitor<'tcx> for WritebackCx<'cx, 'tcx> { intravisit::walk_pat(self, p); } - fn visit_local(&mut self, l: &'tcx hir::Local<'tcx>) { - intravisit::walk_local(self, l); + fn visit_local(&mut self, l: &'tcx hir::Local<'tcx>, e: Option<&'tcx hir::Block<'tcx>>) { + intravisit::walk_local(self, l, e); let var_ty = self.fcx.local_ty(l.span, l.hir_id).decl_ty; let var_ty = self.resolve(var_ty, &l.span); self.write_ty_to_typeck_results(l.hir_id, var_ty); diff --git a/compiler/rustc_typeck/src/expr_use_visitor.rs b/compiler/rustc_typeck/src/expr_use_visitor.rs index 920e3d578c812..12581286941f3 100644 --- a/compiler/rustc_typeck/src/expr_use_visitor.rs +++ b/compiler/rustc_typeck/src/expr_use_visitor.rs @@ -252,7 +252,7 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> { } hir::ExprKind::Let(hir::Let { pat, init, .. }) => { - self.walk_local(init, pat, |t| t.borrow_expr(init, ty::ImmBorrow)); + self.walk_local(init, pat, None, |t| t.borrow_expr(init, ty::ImmBorrow)) } hir::ExprKind::Match(ref discr, arms, _) => { @@ -453,11 +453,11 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> { fn walk_stmt(&mut self, stmt: &hir::Stmt<'_>) { match stmt.kind { - hir::StmtKind::Local(hir::Local { pat, init: Some(expr), .. }) => { - self.walk_local(expr, pat, |_| {}); + hir::StmtKind::Local(hir::Local { pat, init: Some(expr), .. }, els) => { + self.walk_local(expr, pat, els, |_| {}) } - hir::StmtKind::Local(_) => {} + hir::StmtKind::Local(_, _) => {} hir::StmtKind::Item(_) => { // We don't visit nested items in this visitor, @@ -470,13 +470,23 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> { } } - fn walk_local(&mut self, expr: &hir::Expr<'_>, pat: &hir::Pat<'_>, mut f: F) - where + fn walk_local( + &mut self, + expr: &hir::Expr<'_>, + pat: &hir::Pat<'_>, + els: Option<&hir::Block<'_>>, + mut f: F, + ) where F: FnMut(&mut Self), { self.walk_expr(expr); let expr_place = return_if_err!(self.mc.cat_expr(expr)); f(self); + if let Some(els) = els { + // borrowing because we need to test the descriminant + self.borrow_expr(expr, ImmBorrow); + self.walk_block(els) + } self.walk_irrefutable_pat(&expr_place, &pat); } @@ -667,7 +677,7 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> { let ExprUseVisitor { ref mc, body_owner: _, ref mut delegate } = *self; return_if_err!(mc.cat_pattern(discr_place.clone(), pat, |place, pat| { if let PatKind::Binding(_, canonical_id, ..) = pat.kind { - debug!("walk_pat: binding place={:?} pat={:?}", place, pat,); + debug!("walk_pat: binding place={:?} pat={:?}", place, pat); if let Some(bm) = mc.typeck_results.extract_binding_mode(tcx.sess, pat.hir_id, pat.span) { diff --git a/src/test/ui/let-else/let-else-binding-explicit-mut-annotated.stderr b/src/test/ui/let-else/let-else-binding-explicit-mut-annotated.stderr index fdec7e7f6a753..065787cab08ff 100644 --- a/src/test/ui/let-else/let-else-binding-explicit-mut-annotated.stderr +++ b/src/test/ui/let-else/let-else-binding-explicit-mut-annotated.stderr @@ -2,7 +2,9 @@ error[E0308]: mismatched types --> $DIR/let-else-binding-explicit-mut-annotated.rs:9:37 | LL | let Some(n): &mut Option = &&Some(5i32) else { return }; - | ^^^^^^^^^^^^ types differ in mutability + | ---------------- ^^^^^^^^^^^^ types differ in mutability + | | + | expected due to this | = note: expected mutable reference `&mut Option` found reference `&&Option` @@ -11,7 +13,9 @@ error[E0308]: mismatched types --> $DIR/let-else-binding-explicit-mut-annotated.rs:13:37 | LL | let Some(n): &mut Option = &&mut Some(5i32) else { return }; - | ^^^^^^^^^^^^^^^^ types differ in mutability + | ---------------- ^^^^^^^^^^^^^^^^ types differ in mutability + | | + | expected due to this | = note: expected mutable reference `&mut Option` found reference `&&mut Option` diff --git a/src/test/ui/let-else/let-else-check.stderr b/src/test/ui/let-else/let-else-check.stderr index b3da412ec280e..3d647a4c05d86 100644 --- a/src/test/ui/let-else/let-else-check.stderr +++ b/src/test/ui/let-else/let-else-check.stderr @@ -1,8 +1,8 @@ error: unused variable: `x` - --> $DIR/let-else-check.rs:18:9 + --> $DIR/let-else-check.rs:14:13 | -LL | let x = 1; - | ^ help: if this is intentional, prefix it with an underscore: `_x` +LL | let x = 1; + | ^ help: if this is intentional, prefix it with an underscore: `_x` | note: the lint level is defined here --> $DIR/let-else-check.rs:3:9 @@ -11,10 +11,10 @@ LL | #![deny(unused_variables)] | ^^^^^^^^^^^^^^^^ error: unused variable: `x` - --> $DIR/let-else-check.rs:14:13 + --> $DIR/let-else-check.rs:18:9 | -LL | let x = 1; - | ^ help: if this is intentional, prefix it with an underscore: `_x` +LL | let x = 1; + | ^ help: if this is intentional, prefix it with an underscore: `_x` error: aborting due to 2 previous errors diff --git a/src/test/ui/let-else/let-else-non-diverging.stderr b/src/test/ui/let-else/let-else-non-diverging.stderr index b961b16b6f6ef..05e45f689890d 100644 --- a/src/test/ui/let-else/let-else-non-diverging.stderr +++ b/src/test/ui/let-else/let-else-non-diverging.stderr @@ -1,8 +1,11 @@ error[E0308]: `else` clause of `let...else` does not diverge - --> $DIR/let-else-non-diverging.rs:12:32 + --> $DIR/let-else-non-diverging.rs:4:32 | -LL | let Some(x) = Some(1) else { Some(2) }; - | ^^^^^^^^^^^ expected `!`, found enum `Option` +LL | let Some(x) = Some(1) else { + | ________________________________^ +LL | | Some(2) +LL | | }; + | |_____^ expected `!`, found enum `Option` | = note: expected type `!` found enum `Option<{integer}>` @@ -26,13 +29,10 @@ LL | | }; = help: ...or use `match` instead of `let...else` error[E0308]: `else` clause of `let...else` does not diverge - --> $DIR/let-else-non-diverging.rs:4:32 + --> $DIR/let-else-non-diverging.rs:12:32 | -LL | let Some(x) = Some(1) else { - | ________________________________^ -LL | | Some(2) -LL | | }; - | |_____^ expected `!`, found enum `Option` +LL | let Some(x) = Some(1) else { Some(2) }; + | ^^^^^^^^^^^ expected `!`, found enum `Option` | = note: expected type `!` found enum `Option<{integer}>` diff --git a/src/test/ui/let-else/let-else-ref-bindings.stderr b/src/test/ui/let-else/let-else-ref-bindings.stderr index 650f4ec5e779f..56b9e073330a6 100644 --- a/src/test/ui/let-else/let-else-ref-bindings.stderr +++ b/src/test/ui/let-else/let-else-ref-bindings.stderr @@ -20,7 +20,9 @@ error[E0308]: mismatched types --> $DIR/let-else-ref-bindings.rs:24:34 | LL | let Some(a): Option<&[u8]> = some else { return }; - | ^^^^ expected `&[u8]`, found struct `Vec` + | ------------- ^^^^ expected `&[u8]`, found struct `Vec` + | | + | expected due to this | = note: expected enum `Option<&[u8]>` found enum `Option>` @@ -29,7 +31,9 @@ error[E0308]: mismatched types --> $DIR/let-else-ref-bindings.rs:27:34 | LL | let Some(a): Option<&[u8]> = &some else { return }; - | ^^^^^ expected enum `Option`, found `&Option>` + | ------------- ^^^^^ expected enum `Option`, found `&Option>` + | | + | expected due to this | = note: expected enum `Option<&[u8]>` found reference `&Option>` @@ -56,7 +60,9 @@ error[E0308]: mismatched types --> $DIR/let-else-ref-bindings.rs:52:38 | LL | let Some(a): Option<&mut [u8]> = some else { return }; - | ^^^^ expected `&mut [u8]`, found struct `Vec` + | ----------------- ^^^^ expected `&mut [u8]`, found struct `Vec` + | | + | expected due to this | = note: expected enum `Option<&mut [u8]>` found enum `Option>` @@ -65,7 +71,9 @@ error[E0308]: mismatched types --> $DIR/let-else-ref-bindings.rs:55:38 | LL | let Some(a): Option<&mut [u8]> = &mut some else { return }; - | ^^^^^^^^^ expected enum `Option`, found mutable reference + | ----------------- ^^^^^^^^^ expected enum `Option`, found mutable reference + | | + | expected due to this | = note: expected enum `Option<&mut [u8]>` found mutable reference `&mut Option>` diff --git a/src/test/ui/let-else/let-else-temporary-lifetime.rs b/src/test/ui/let-else/let-else-temporary-lifetime.rs new file mode 100644 index 0000000000000..624c2ea37a70b --- /dev/null +++ b/src/test/ui/let-else/let-else-temporary-lifetime.rs @@ -0,0 +1,25 @@ +// run-pass +#![feature(let_else)] + +use std::sync::atomic::{AtomicU8, Ordering}; + +static TRACKER: AtomicU8 = AtomicU8::new(0); + +#[derive(Default)] +struct Droppy { + inner: u32, +} + +impl Drop for Droppy { + fn drop(&mut self) { + TRACKER.store(1, Ordering::Release); + println!("I've been dropped"); + } +} + +fn main() { + assert_eq!(TRACKER.load(Ordering::Acquire), 0); + let 0 = Droppy::default().inner else { return }; + assert_eq!(TRACKER.load(Ordering::Acquire), 1); + println!("Should have dropped 👆"); +} diff --git a/src/tools/clippy/clippy_lints/src/attrs.rs b/src/tools/clippy/clippy_lints/src/attrs.rs index 4bcbeacf9feb5..93ce3b30fb1d3 100644 --- a/src/tools/clippy/clippy_lints/src/attrs.rs +++ b/src/tools/clippy/clippy_lints/src/attrs.rs @@ -505,7 +505,7 @@ fn is_relevant_block(cx: &LateContext<'_>, typeck_results: &ty::TypeckResults<'_ .as_ref() .map_or(false, |e| is_relevant_expr(cx, typeck_results, e)), |stmt| match &stmt.kind { - StmtKind::Local(_) => true, + StmtKind::Local(_, _) => true, StmtKind::Expr(expr) | StmtKind::Semi(expr) => is_relevant_expr(cx, typeck_results, expr), StmtKind::Item(_) => false, }, diff --git a/src/tools/clippy/clippy_lints/src/copies.rs b/src/tools/clippy/clippy_lints/src/copies.rs index 1deff9684a140..0b9fdb891b15c 100644 --- a/src/tools/clippy/clippy_lints/src/copies.rs +++ b/src/tools/clippy/clippy_lints/src/copies.rs @@ -324,7 +324,7 @@ impl BlockEq { /// If the statement is a local, checks if the bound names match the expected list of names. fn eq_binding_names(s: &Stmt<'_>, names: &[(HirId, Symbol)]) -> bool { - if let StmtKind::Local(l) = s.kind { + if let StmtKind::Local(l, _) = s.kind { let mut i = 0usize; let mut res = true; l.pat.each_binding_or_first(&mut |_, _, _, name| { @@ -349,7 +349,7 @@ fn eq_stmts( eq: &mut HirEqInterExpr<'_, '_, '_>, moved_bindings: &mut Vec<(HirId, Symbol)>, ) -> bool { - (if let StmtKind::Local(l) = stmt.kind { + (if let StmtKind::Local(l, _) = stmt.kind { let old_count = moved_bindings.len(); l.pat.each_binding_or_first(&mut |_, id, _, name| { moved_bindings.push((id, name.name)); @@ -435,7 +435,7 @@ fn scan_block_for_eq(cx: &LateContext<'_>, _conds: &[&Expr<'_>], block: &Block<' // Clear out all locals seen at the end so far. None of them can be moved. let stmts = &blocks[0].stmts; for stmt in &stmts[stmts.len() - init..=stmts.len() - offset] { - if let StmtKind::Local(l) = stmt.kind { + if let StmtKind::Local(l, _) = stmt.kind { l.pat.each_binding_or_first(&mut |_, id, _, _| { eq.locals.remove(&id); }); diff --git a/src/tools/clippy/clippy_lints/src/default.rs b/src/tools/clippy/clippy_lints/src/default.rs index d99a1aa296946..7fe3443858a01 100644 --- a/src/tools/clippy/clippy_lints/src/default.rs +++ b/src/tools/clippy/clippy_lints/src/default.rs @@ -126,7 +126,7 @@ impl<'tcx> LateLintPass<'tcx> for Default { // checked and the name of the bound variable let (local, variant, binding_name, binding_type, span) = if_chain! { // only take `let ...` statements - if let StmtKind::Local(local) = stmt.kind; + if let StmtKind::Local(local, _) = stmt.kind; if let Some(expr) = local.init; if !any_parent_is_automatically_derived(cx.tcx, expr.hir_id); if !expr.span.from_expansion(); diff --git a/src/tools/clippy/clippy_lints/src/default_numeric_fallback.rs b/src/tools/clippy/clippy_lints/src/default_numeric_fallback.rs index fb418a3251f58..0f374d12a84f6 100644 --- a/src/tools/clippy/clippy_lints/src/default_numeric_fallback.rs +++ b/src/tools/clippy/clippy_lints/src/default_numeric_fallback.rs @@ -192,7 +192,7 @@ impl<'a, 'tcx> Visitor<'tcx> for NumericFallbackVisitor<'a, 'tcx> { fn visit_stmt(&mut self, stmt: &'tcx Stmt<'_>) { match stmt.kind { - StmtKind::Local(local) => { + StmtKind::Local(local, _) => { if local.ty.is_some() { self.ty_bounds.push(TyBound::Any); } else { diff --git a/src/tools/clippy/clippy_lints/src/entry.rs b/src/tools/clippy/clippy_lints/src/entry.rs index 27743a0ebec7e..e0986b710c508 100644 --- a/src/tools/clippy/clippy_lints/src/entry.rs +++ b/src/tools/clippy/clippy_lints/src/entry.rs @@ -386,7 +386,7 @@ impl<'tcx> Visitor<'tcx> for InsertSearcher<'_, 'tcx> { } }, StmtKind::Expr(e) => self.visit_expr(e), - StmtKind::Local(l) => { + StmtKind::Local(l, _) => { self.visit_pat(l.pat); if let Some(e) = l.init { self.allow_insert_closure &= !self.in_tail_pos; diff --git a/src/tools/clippy/clippy_lints/src/explicit_write.rs b/src/tools/clippy/clippy_lints/src/explicit_write.rs index 5bf4313b41a49..bd1ac3371b063 100644 --- a/src/tools/clippy/clippy_lints/src/explicit_write.rs +++ b/src/tools/clippy/clippy_lints/src/explicit_write.rs @@ -116,7 +116,7 @@ fn look_in_block<'tcx, 'hir>(cx: &LateContext<'tcx>, kind: &'tcx ExprKind<'hir>) if_chain! { if let ExprKind::Block(block, _label @ None) = kind; if let Block { - stmts: [Stmt { kind: StmtKind::Local(local), .. }], + stmts: [Stmt { kind: StmtKind::Local(local, _), .. }], expr: Some(expr_end_of_block), rules: BlockCheckMode::DefaultBlock, .. diff --git a/src/tools/clippy/clippy_lints/src/let_if_seq.rs b/src/tools/clippy/clippy_lints/src/let_if_seq.rs index 56bbbbbc819e5..5dcb86feb7622 100644 --- a/src/tools/clippy/clippy_lints/src/let_if_seq.rs +++ b/src/tools/clippy/clippy_lints/src/let_if_seq.rs @@ -62,7 +62,7 @@ impl<'tcx> LateLintPass<'tcx> for LetIfSeq { while let Some(stmt) = it.next() { if_chain! { if let Some(expr) = it.peek(); - if let hir::StmtKind::Local(local) = stmt.kind; + if let hir::StmtKind::Local(local, _) = stmt.kind; if let hir::PatKind::Binding(mode, canonical_id, ident, None) = local.pat.kind; if let hir::StmtKind::Expr(if_) = expr.kind; if let hir::ExprKind::If(hir::Expr { kind: hir::ExprKind::DropTemps(cond), ..}, then, else_) = if_.kind; diff --git a/src/tools/clippy/clippy_lints/src/let_underscore.rs b/src/tools/clippy/clippy_lints/src/let_underscore.rs index 176787497ebf2..a37dfb7b7151d 100644 --- a/src/tools/clippy/clippy_lints/src/let_underscore.rs +++ b/src/tools/clippy/clippy_lints/src/let_underscore.rs @@ -2,7 +2,7 @@ use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::ty::{is_must_use_ty, match_type}; use clippy_utils::{is_must_use_func_call, paths}; use if_chain::if_chain; -use rustc_hir::{Local, PatKind}; +use rustc_hir::{Block, Local, PatKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::lint::in_external_macro; use rustc_middle::ty::subst::GenericArgKind; @@ -109,7 +109,7 @@ const SYNC_GUARD_PATHS: [&[&str]; 6] = [ ]; impl<'tcx> LateLintPass<'tcx> for LetUnderscore { - fn check_local(&mut self, cx: &LateContext<'_>, local: &Local<'_>) { + fn check_local(&mut self, cx: &LateContext<'_>, local: &Local<'_>, _: Option<&Block<'_>>) { if in_external_macro(cx.tcx.sess, local.span) { return; } diff --git a/src/tools/clippy/clippy_lints/src/loops/needless_collect.rs b/src/tools/clippy/clippy_lints/src/loops/needless_collect.rs index ddaffc751880d..ba0f01d9ed25b 100644 --- a/src/tools/clippy/clippy_lints/src/loops/needless_collect.rs +++ b/src/tools/clippy/clippy_lints/src/loops/needless_collect.rs @@ -76,7 +76,7 @@ fn check_needless_collect_indirect_usage<'tcx>(expr: &'tcx Expr<'_>, cx: &LateCo if let ExprKind::Block(block, _) = expr.kind { for stmt in block.stmts { if_chain! { - if let StmtKind::Local(local) = stmt.kind; + if let StmtKind::Local(local, _) = stmt.kind; if let PatKind::Binding(_, id, ..) = local.pat.kind; if let Some(init_expr) = local.init; if let ExprKind::MethodCall(method_name, &[ref iter_source], ..) = init_expr.kind; @@ -276,7 +276,7 @@ fn get_expr_and_hir_id_from_stmt<'v>(stmt: &'v Stmt<'v>) -> Option<(&'v Expr<'v> match stmt.kind { StmtKind::Expr(expr) | StmtKind::Semi(expr) => Some((expr, None)), StmtKind::Item(..) => None, - StmtKind::Local(Local { init, pat, .. }) => { + StmtKind::Local(Local { init, pat, .. }, _) => { if let PatKind::Binding(_, hir_id, ..) = pat.kind { init.map(|init_expr| (init_expr, Some(hir_id))) } else { diff --git a/src/tools/clippy/clippy_lints/src/loops/never_loop.rs b/src/tools/clippy/clippy_lints/src/loops/never_loop.rs index 32de20f6531fe..c60d551806067 100644 --- a/src/tools/clippy/clippy_lints/src/loops/never_loop.rs +++ b/src/tools/clippy/clippy_lints/src/loops/never_loop.rs @@ -104,7 +104,7 @@ fn never_loop_expr_seq<'a, T: Iterator>>(es: &mut T, main_lo fn stmt_to_expr<'tcx>(stmt: &Stmt<'tcx>) -> Option<&'tcx Expr<'tcx>> { match stmt.kind { StmtKind::Semi(e, ..) | StmtKind::Expr(e, ..) => Some(e), - StmtKind::Local(local) => local.init, + StmtKind::Local(local, _) => local.init, StmtKind::Item(..) => None, } } diff --git a/src/tools/clippy/clippy_lints/src/loops/utils.rs b/src/tools/clippy/clippy_lints/src/loops/utils.rs index 4801a84eb92ce..661af8fe642f9 100644 --- a/src/tools/clippy/clippy_lints/src/loops/utils.rs +++ b/src/tools/clippy/clippy_lints/src/loops/utils.rs @@ -4,7 +4,7 @@ use if_chain::if_chain; use rustc_ast::ast::{LitIntType, LitKind}; use rustc_errors::Applicability; use rustc_hir::intravisit::{walk_expr, walk_local, walk_pat, walk_stmt, Visitor}; -use rustc_hir::{BinOpKind, BorrowKind, Expr, ExprKind, HirId, HirIdMap, Local, Mutability, Pat, PatKind, Stmt}; +use rustc_hir::{BinOpKind, Block, BorrowKind, Expr, ExprKind, HirId, HirIdMap, Local, Mutability, Pat, PatKind, Stmt}; use rustc_lint::LateContext; use rustc_middle::hir::nested_filter; use rustc_middle::ty::{self, Ty}; @@ -148,7 +148,7 @@ impl<'a, 'tcx> InitializeVisitor<'a, 'tcx> { impl<'a, 'tcx> Visitor<'tcx> for InitializeVisitor<'a, 'tcx> { type NestedFilter = nested_filter::OnlyBodies; - fn visit_local(&mut self, l: &'tcx Local<'_>) { + fn visit_local(&mut self, l: &'tcx Local<'_>, e: Option<&'tcx Block<'_>>) { // Look for declarations of the variable if_chain! { if l.pat.hir_id == self.var_id; @@ -166,7 +166,7 @@ impl<'a, 'tcx> Visitor<'tcx> for InitializeVisitor<'a, 'tcx> { } } - walk_local(self, l); + walk_local(self, l, e); } fn visit_expr(&mut self, expr: &'tcx Expr<'_>) { diff --git a/src/tools/clippy/clippy_lints/src/loops/while_let_loop.rs b/src/tools/clippy/clippy_lints/src/loops/while_let_loop.rs index 45af6be2653f3..8c3524942520b 100644 --- a/src/tools/clippy/clippy_lints/src/loops/while_let_loop.rs +++ b/src/tools/clippy/clippy_lints/src/loops/while_let_loop.rs @@ -11,7 +11,7 @@ use rustc_lint::LateContext; pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, loop_block: &'tcx Block<'_>) { let (init, has_trailing_exprs) = match (loop_block.stmts, loop_block.expr) { ([stmt, stmts @ ..], expr) => { - if let StmtKind::Local(&Local { init: Some(e), .. }) | StmtKind::Semi(e) | StmtKind::Expr(e) = stmt.kind { + if let StmtKind::Local(&Local { init: Some(e), .. }, None) | StmtKind::Semi(e) | StmtKind::Expr(e) = stmt.kind { (e, !stmts.is_empty() || expr.is_some()) } else { return; diff --git a/src/tools/clippy/clippy_lints/src/loops/while_let_on_iterator.rs b/src/tools/clippy/clippy_lints/src/loops/while_let_on_iterator.rs index a57159750664f..1abdfaac7ec64 100644 --- a/src/tools/clippy/clippy_lints/src/loops/while_let_on_iterator.rs +++ b/src/tools/clippy/clippy_lints/src/loops/while_let_on_iterator.rs @@ -8,7 +8,7 @@ use clippy_utils::{ use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::intravisit::{walk_expr, Visitor}; -use rustc_hir::{def::Res, Expr, ExprKind, HirId, Local, Mutability, PatKind, QPath, UnOp}; +use rustc_hir::{def::Res, Block, Expr, ExprKind, HirId, Local, Mutability, PatKind, QPath, UnOp}; use rustc_lint::LateContext; use rustc_middle::ty::adjustment::Adjust; use rustc_span::{symbol::sym, Symbol}; @@ -283,7 +283,7 @@ fn needs_mutable_borrow(cx: &LateContext<'_>, iter_expr: &IterExpr, loop_expr: & used_after: bool, } impl<'a, 'b, 'tcx> Visitor<'tcx> for NestedLoopVisitor<'a, 'b, 'tcx> { - fn visit_local(&mut self, l: &'tcx Local<'_>) { + fn visit_local(&mut self, l: &'tcx Local<'_>, _: Option<&'tcx Block<'_>>) { if !self.after_loop { l.pat.each_binding_or_first(&mut |_, id, _, _| { if id == self.local_id { diff --git a/src/tools/clippy/clippy_lints/src/map_unit_fn.rs b/src/tools/clippy/clippy_lints/src/map_unit_fn.rs index 663246b4c8622..3bfe5428133fc 100644 --- a/src/tools/clippy/clippy_lints/src/map_unit_fn.rs +++ b/src/tools/clippy/clippy_lints/src/map_unit_fn.rs @@ -144,7 +144,7 @@ fn reduce_unit_expression<'a>(cx: &LateContext<'_>, expr: &'a hir::Expr<'_>) -> // If block only contains statements, // reduce `{ X; }` to `X` or `X;` match inner_stmt.kind { - hir::StmtKind::Local(local) => Some(local.span), + hir::StmtKind::Local(local, _) => Some(local.span), hir::StmtKind::Expr(e) => Some(e.span), hir::StmtKind::Semi(..) => Some(inner_stmt.span), hir::StmtKind::Item(..) => None, diff --git a/src/tools/clippy/clippy_lints/src/matches/mod.rs b/src/tools/clippy/clippy_lints/src/matches/mod.rs index b2a873ef5823c..cc8674a200652 100644 --- a/src/tools/clippy/clippy_lints/src/matches/mod.rs +++ b/src/tools/clippy/clippy_lints/src/matches/mod.rs @@ -1,6 +1,6 @@ use clippy_utils::source::{snippet_opt, span_starts_with, walk_span_to_context}; use clippy_utils::{higher, in_constant, meets_msrv, msrvs}; -use rustc_hir::{Arm, Expr, ExprKind, Local, MatchSource, Pat}; +use rustc_hir::{Arm, Block, Expr, ExprKind, Local, MatchSource, Pat}; use rustc_lexer::{tokenize, TokenKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; @@ -1040,8 +1040,14 @@ impl<'tcx> LateLintPass<'tcx> for Matches { } } - fn check_local(&mut self, cx: &LateContext<'tcx>, local: &'tcx Local<'_>) { - self.infallible_destructuring_match_linted |= infallible_destructuring_match::check(cx, local); + fn check_local( + &mut self, + cx: &LateContext<'tcx>, + local: &'tcx Local<'_>, + els: Option<&'tcx Block<'_>>, + ) { + self.infallible_destructuring_match_linted |= + els.is_none() && infallible_destructuring_match::check(cx, local); } fn check_pat(&mut self, cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>) { diff --git a/src/tools/clippy/clippy_lints/src/methods/str_splitn.rs b/src/tools/clippy/clippy_lints/src/methods/str_splitn.rs index 4ac738272d085..80dbd14b2c56a 100644 --- a/src/tools/clippy/clippy_lints/src/methods/str_splitn.rs +++ b/src/tools/clippy/clippy_lints/src/methods/str_splitn.rs @@ -220,7 +220,7 @@ fn indirect_usage<'tcx>( init: Some(init_expr), hir_id: local_hir_id, .. - }) = stmt.kind + }, _) = stmt.kind { let mut path_to_binding = None; expr_visitor(cx, |expr| { diff --git a/src/tools/clippy/clippy_lints/src/misc.rs b/src/tools/clippy/clippy_lints/src/misc.rs index be7df08d89f05..2ad7ac60b9250 100644 --- a/src/tools/clippy/clippy_lints/src/misc.rs +++ b/src/tools/clippy/clippy_lints/src/misc.rs @@ -161,7 +161,7 @@ impl<'tcx> LateLintPass<'tcx> for MiscLints { fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) { if_chain! { if !in_external_macro(cx.tcx.sess, stmt.span); - if let StmtKind::Local(local) = stmt.kind; + if let StmtKind::Local(local, _) = stmt.kind; if let PatKind::Binding(an, .., name, None) = local.pat.kind; if let Some(init) = local.init; if an == BindingAnnotation::Ref || an == BindingAnnotation::RefMut; diff --git a/src/tools/clippy/clippy_lints/src/mixed_read_write_in_expression.rs b/src/tools/clippy/clippy_lints/src/mixed_read_write_in_expression.rs index a2419c277e9c2..de993c3c0a47a 100644 --- a/src/tools/clippy/clippy_lints/src/mixed_read_write_in_expression.rs +++ b/src/tools/clippy/clippy_lints/src/mixed_read_write_in_expression.rs @@ -96,7 +96,7 @@ impl<'tcx> LateLintPass<'tcx> for EvalOrderDependence { } fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) { match stmt.kind { - StmtKind::Local(local) => { + StmtKind::Local(local, _) => { if let Local { init: Some(e), .. } = local { DivergenceVisitor { cx }.visit_expr(e); } @@ -273,7 +273,7 @@ fn check_stmt<'a, 'tcx>(vis: &mut ReadVisitor<'a, 'tcx>, stmt: &'tcx Stmt<'_>) - StmtKind::Expr(expr) | StmtKind::Semi(expr) => check_expr(vis, expr), // If the declaration is of a local variable, check its initializer // expression if it has one. Otherwise, keep going. - StmtKind::Local(local) => local + StmtKind::Local(local, _) => local .init .as_ref() .map_or(StopEarly::KeepGoing, |expr| check_expr(vis, expr)), diff --git a/src/tools/clippy/clippy_lints/src/mut_key.rs b/src/tools/clippy/clippy_lints/src/mut_key.rs index 4db103bbc1305..251181165b022 100644 --- a/src/tools/clippy/clippy_lints/src/mut_key.rs +++ b/src/tools/clippy/clippy_lints/src/mut_key.rs @@ -101,7 +101,12 @@ impl<'tcx> LateLintPass<'tcx> for MutableKeyType { } } - fn check_local(&mut self, cx: &LateContext<'_>, local: &hir::Local<'_>) { + fn check_local( + &mut self, + cx: &LateContext<'_>, + local: &hir::Local<'_>, + _: Option<&hir::Block<'_>>, + ) { if let hir::PatKind::Wild = local.pat.kind { return; } diff --git a/src/tools/clippy/clippy_lints/src/needless_late_init.rs b/src/tools/clippy/clippy_lints/src/needless_late_init.rs index ff2999b1f4a51..fa1c09d8f9032 100644 --- a/src/tools/clippy/clippy_lints/src/needless_late_init.rs +++ b/src/tools/clippy/clippy_lints/src/needless_late_init.rs @@ -92,7 +92,7 @@ fn contains_let(cond: &Expr<'_>) -> bool { } fn stmt_needs_ordered_drop(cx: &LateContext<'_>, stmt: &Stmt<'_>) -> bool { - let StmtKind::Local(local) = stmt.kind else { return false }; + let StmtKind::Local(local, _) = stmt.kind else { return false }; !local.pat.walk_short(|pat| { if let PatKind::Binding(.., None) = pat.kind { !needs_ordered_drop(cx, cx.typeck_results().pat_ty(pat)) @@ -367,7 +367,7 @@ fn check<'tcx>( } impl<'tcx> LateLintPass<'tcx> for NeedlessLateInit { - fn check_local(&mut self, cx: &LateContext<'tcx>, local: &'tcx Local<'tcx>) { + fn check_local(&mut self, cx: &LateContext<'tcx>, local: &'tcx Local<'tcx>, _: Option<&'tcx Block<'tcx>>) { let mut parents = cx.tcx.hir().parent_iter(local.hir_id); if_chain! { if let Local { diff --git a/src/tools/clippy/clippy_lints/src/no_effect.rs b/src/tools/clippy/clippy_lints/src/no_effect.rs index 6598413c77ecc..105e145ac3069 100644 --- a/src/tools/clippy/clippy_lints/src/no_effect.rs +++ b/src/tools/clippy/clippy_lints/src/no_effect.rs @@ -88,10 +88,11 @@ fn check_no_effect(cx: &LateContext<'_>, stmt: &Stmt<'_>) -> bool { span_lint_hir(cx, NO_EFFECT, expr.hir_id, stmt.span, "statement with no effect"); return true; } - } else if let StmtKind::Local(local) = stmt.kind { + } else if let StmtKind::Local(local, els) = stmt.kind { if_chain! { if !is_lint_allowed(cx, NO_EFFECT_UNDERSCORE_BINDING, local.hir_id); if let Some(init) = local.init; + if els.is_none(); if !local.pat.span.from_expansion(); if has_no_effect(cx, init); if let PatKind::Binding(_, _, ident, _) = local.pat.kind; diff --git a/src/tools/clippy/clippy_lints/src/only_used_in_recursion.rs b/src/tools/clippy/clippy_lints/src/only_used_in_recursion.rs index 677ac998b5682..c7f8f2f8d7045 100644 --- a/src/tools/clippy/clippy_lints/src/only_used_in_recursion.rs +++ b/src/tools/clippy/clippy_lints/src/only_used_in_recursion.rs @@ -261,13 +261,13 @@ impl<'tcx> Visitor<'tcx> for SideEffectVisit<'tcx> { match s.kind { StmtKind::Local(Local { pat, init: Some(init), .. - }) => { + }, _) => { self.visit_pat_expr(pat, init, false); }, StmtKind::Item(_) | StmtKind::Expr(_) | StmtKind::Semi(_) => { walk_stmt(self, s); }, - StmtKind::Local(_) => {}, + StmtKind::Local(_, _) => {}, } self.ret_vars.clear(); } diff --git a/src/tools/clippy/clippy_lints/src/pattern_type_mismatch.rs b/src/tools/clippy/clippy_lints/src/pattern_type_mismatch.rs index a4d265111f9ae..83e18e207117a 100644 --- a/src/tools/clippy/clippy_lints/src/pattern_type_mismatch.rs +++ b/src/tools/clippy/clippy_lints/src/pattern_type_mismatch.rs @@ -83,7 +83,7 @@ declare_lint_pass!(PatternTypeMismatch => [PATTERN_TYPE_MISMATCH]); impl<'tcx> LateLintPass<'tcx> for PatternTypeMismatch { fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) { - if let StmtKind::Local(local) = stmt.kind { + if let StmtKind::Local(local, _) = stmt.kind { if in_external_macro(cx.sess(), local.pat.span) { return; } diff --git a/src/tools/clippy/clippy_lints/src/read_zero_byte_vec.rs b/src/tools/clippy/clippy_lints/src/read_zero_byte_vec.rs index 9538a8104739e..8316efad1ffed 100644 --- a/src/tools/clippy/clippy_lints/src/read_zero_byte_vec.rs +++ b/src/tools/clippy/clippy_lints/src/read_zero_byte_vec.rs @@ -53,7 +53,7 @@ impl<'tcx> LateLintPass<'tcx> for ReadZeroByteVec { for (idx, stmt) in block.stmts.iter().enumerate() { if !stmt.span.from_expansion() // matches `let v = Vec::new();` - && let StmtKind::Local(local) = stmt.kind + && let StmtKind::Local(local, _) = stmt.kind && let Local { pat, init: Some(init), .. } = local && let PatKind::Binding(_, _, ident, _) = pat.kind && let Some(vec_init_kind) = get_vec_init_kind(cx, init) diff --git a/src/tools/clippy/clippy_lints/src/redundant_closure_call.rs b/src/tools/clippy/clippy_lints/src/redundant_closure_call.rs index 65ed798867d19..48bf14d511c71 100644 --- a/src/tools/clippy/clippy_lints/src/redundant_closure_call.rs +++ b/src/tools/clippy/clippy_lints/src/redundant_closure_call.rs @@ -133,7 +133,7 @@ impl<'tcx> LateLintPass<'tcx> for RedundantClosureCall { for w in block.stmts.windows(2) { if_chain! { - if let hir::StmtKind::Local(local) = w[0].kind; + if let hir::StmtKind::Local(local, _) = w[0].kind; if let Option::Some(t) = local.init; if let hir::ExprKind::Closure { .. } = t.kind; if let hir::PatKind::Binding(_, _, ident, _) = local.pat.kind; diff --git a/src/tools/clippy/clippy_lints/src/returns.rs b/src/tools/clippy/clippy_lints/src/returns.rs index 5ae04947b82d0..b2ec32abb442a 100644 --- a/src/tools/clippy/clippy_lints/src/returns.rs +++ b/src/tools/clippy/clippy_lints/src/returns.rs @@ -10,7 +10,6 @@ use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; use rustc_middle::ty::subst::GenericArgKind; use rustc_session::{declare_lint_pass, declare_tool_lint}; -use rustc_span::hygiene::DesugaringKind; use rustc_span::source_map::Span; use rustc_span::sym; @@ -83,7 +82,7 @@ impl<'tcx> LateLintPass<'tcx> for Return { if_chain! { if let Some(retexpr) = block.expr; if let Some(stmt) = block.stmts.iter().last(); - if let StmtKind::Local(local) = &stmt.kind; + if let StmtKind::Local(local, _) = &stmt.kind; if local.ty.is_none(); if cx.tcx.hir().attrs(local.hir_id).is_empty(); if let Some(initexpr) = &local.init; @@ -203,9 +202,7 @@ fn check_final_expr<'tcx>( check_block_return(cx, ifblock); } if let Some(else_clause) = else_clause_opt { - if expr.span.desugaring_kind() != Some(DesugaringKind::LetElse) { - check_final_expr(cx, else_clause, None, RetReplacement::Empty); - } + check_final_expr(cx, else_clause, None, RetReplacement::Empty); } }, // a match expr, check all arms diff --git a/src/tools/clippy/clippy_lints/src/slow_vector_initialization.rs b/src/tools/clippy/clippy_lints/src/slow_vector_initialization.rs index 2c8aa17e80dbd..3d7ef747a86c8 100644 --- a/src/tools/clippy/clippy_lints/src/slow_vector_initialization.rs +++ b/src/tools/clippy/clippy_lints/src/slow_vector_initialization.rs @@ -98,7 +98,7 @@ impl<'tcx> LateLintPass<'tcx> for SlowVectorInit { fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) { // Matches statements which initializes vectors. For example: `let mut vec = Vec::with_capacity(10)` if_chain! { - if let StmtKind::Local(local) = stmt.kind; + if let StmtKind::Local(local, _) = stmt.kind; if let PatKind::Binding(BindingAnnotation::Mutable, local_id, _, None) = local.pat.kind; if let Some(init) = local.init; if let Some(len_arg) = Self::is_vec_with_capacity(cx, init); diff --git a/src/tools/clippy/clippy_lints/src/swap.rs b/src/tools/clippy/clippy_lints/src/swap.rs index 1885f3ca414df..a8c96543c7c6e 100644 --- a/src/tools/clippy/clippy_lints/src/swap.rs +++ b/src/tools/clippy/clippy_lints/src/swap.rs @@ -141,7 +141,7 @@ fn check_manual_swap(cx: &LateContext<'_>, block: &Block<'_>) { for w in block.stmts.windows(3) { if_chain! { // let t = foo(); - if let StmtKind::Local(tmp) = w[0].kind; + if let StmtKind::Local(tmp, _) = w[0].kind; if let Some(tmp_init) = tmp.init; if let PatKind::Binding(.., ident, None) = tmp.pat.kind; diff --git a/src/tools/clippy/clippy_lints/src/types/mod.rs b/src/tools/clippy/clippy_lints/src/types/mod.rs index 353a6f6b899ea..2a7d5f2623e2c 100644 --- a/src/tools/clippy/clippy_lints/src/types/mod.rs +++ b/src/tools/clippy/clippy_lints/src/types/mod.rs @@ -12,7 +12,7 @@ mod vec_box; use rustc_hir as hir; use rustc_hir::intravisit::FnKind; use rustc_hir::{ - Body, FnDecl, FnRetTy, GenericArg, HirId, ImplItem, ImplItemKind, Item, ItemKind, Local, MutTy, QPath, TraitItem, + Block, Body, FnDecl, FnRetTy, GenericArg, HirId, ImplItem, ImplItemKind, Item, ItemKind, Local, MutTy, QPath, TraitItem, TraitItemKind, TyKind, }; use rustc_lint::{LateContext, LateLintPass}; @@ -406,7 +406,7 @@ impl<'tcx> LateLintPass<'tcx> for Types { } } - fn check_local(&mut self, cx: &LateContext<'_>, local: &Local<'_>) { + fn check_local(&mut self, cx: &LateContext<'_>, local: &Local<'_>, _: Option<&Block<'_>>) { if let Some(ty) = local.ty { self.check_ty( cx, diff --git a/src/tools/clippy/clippy_lints/src/uninit_vec.rs b/src/tools/clippy/clippy_lints/src/uninit_vec.rs index 9f4c5555f11b7..eab3b9b7b01c2 100644 --- a/src/tools/clippy/clippy_lints/src/uninit_vec.rs +++ b/src/tools/clippy/clippy_lints/src/uninit_vec.rs @@ -155,7 +155,7 @@ impl<'tcx> VecLocation<'tcx> { /// or `self` expression for `Vec::reserve()`. fn extract_init_or_reserve_target<'tcx>(cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'tcx>) -> Option> { match stmt.kind { - StmtKind::Local(local) => { + StmtKind::Local(local, _) => { if_chain! { if let Some(init_expr) = local.init; if let PatKind::Binding(_, hir_id, _, None) = local.pat.kind; diff --git a/src/tools/clippy/clippy_lints/src/unit_types/let_unit_value.rs b/src/tools/clippy/clippy_lints/src/unit_types/let_unit_value.rs index cf509455aad0a..80e7b8de392c7 100644 --- a/src/tools/clippy/clippy_lints/src/unit_types/let_unit_value.rs +++ b/src/tools/clippy/clippy_lints/src/unit_types/let_unit_value.rs @@ -12,7 +12,7 @@ use rustc_middle::ty::{self, Ty, TypeVisitable, TypeSuperVisitable, TypeVisitor} use super::LET_UNIT_VALUE; pub(super) fn check(cx: &LateContext<'_>, stmt: &Stmt<'_>) { - if let StmtKind::Local(local) = stmt.kind + if let StmtKind::Local(local, _) = stmt.kind && let Some(init) = local.init && !local.pat.span.from_expansion() && !in_external_macro(cx.sess(), stmt.span) diff --git a/src/tools/clippy/clippy_lints/src/utils/author.rs b/src/tools/clippy/clippy_lints/src/utils/author.rs index 2c8820eb7e1a1..99ac84fbaabae 100644 --- a/src/tools/clippy/clippy_lints/src/utils/author.rs +++ b/src/tools/clippy/clippy_lints/src/utils/author.rs @@ -685,7 +685,7 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { } match stmt.value.kind { - StmtKind::Local(local) => { + StmtKind::Local(local, _) => { bind!(self, local); kind!("Local({local})"); self.option(field!(local.init), "init", |init| { diff --git a/src/tools/clippy/clippy_lints/src/vec_init_then_push.rs b/src/tools/clippy/clippy_lints/src/vec_init_then_push.rs index 35db45e2b0c99..c71bacfa29a70 100644 --- a/src/tools/clippy/clippy_lints/src/vec_init_then_push.rs +++ b/src/tools/clippy/clippy_lints/src/vec_init_then_push.rs @@ -155,7 +155,7 @@ impl<'tcx> LateLintPass<'tcx> for VecInitThenPush { self.searcher = None; } - fn check_local(&mut self, cx: &LateContext<'tcx>, local: &'tcx Local<'tcx>) { + fn check_local(&mut self, cx: &LateContext<'tcx>, local: &'tcx Local<'tcx>, _: Option<&'tcx Block<'tcx>>) { if let Some(init_expr) = local.init && let PatKind::Binding(BindingAnnotation::Mutable, id, name, None) = local.pat.kind && !in_external_macro(cx.sess(), local.span) diff --git a/src/tools/clippy/clippy_utils/src/hir_utils.rs b/src/tools/clippy/clippy_utils/src/hir_utils.rs index 793e3cc58c21d..0b5325adfed20 100644 --- a/src/tools/clippy/clippy_utils/src/hir_utils.rs +++ b/src/tools/clippy/clippy_utils/src/hir_utils.rs @@ -102,7 +102,7 @@ pub struct HirEqInterExpr<'a, 'b, 'tcx> { impl HirEqInterExpr<'_, '_, '_> { pub fn eq_stmt(&mut self, left: &Stmt<'_>, right: &Stmt<'_>) -> bool { match (&left.kind, &right.kind) { - (&StmtKind::Local(l), &StmtKind::Local(r)) => { + (&StmtKind::Local(l, le), &StmtKind::Local(r, re)) => { // This additional check ensures that the type of the locals are equivalent even if the init // expression or type have some inferred parts. if let Some((typeck_lhs, typeck_rhs)) = self.inner.maybe_typeck_results { @@ -117,6 +117,7 @@ impl HirEqInterExpr<'_, '_, '_> { // these only get added if the init and type is equal. both(&l.init, &r.init, |l, r| self.eq_expr(l, r)) && both(&l.ty, &r.ty, |l, r| self.eq_ty(l, r)) + && both(&le, &re, |l, r| self.eq_block(l, r)) && self.eq_pat(l.pat, r.pat) }, (&StmtKind::Expr(l), &StmtKind::Expr(r)) | (&StmtKind::Semi(l), &StmtKind::Semi(r)) => self.eq_expr(l, r), @@ -921,11 +922,14 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { std::mem::discriminant(&b.kind).hash(&mut self.s); match &b.kind { - StmtKind::Local(local) => { + StmtKind::Local(local, els) => { self.hash_pat(local.pat); if let Some(init) = local.init { self.hash_expr(init); } + if let Some(els) = els { + self.hash_block(els); + } }, StmtKind::Item(..) => {}, StmtKind::Expr(expr) | StmtKind::Semi(expr) => { diff --git a/src/tools/clippy/clippy_utils/src/lib.rs b/src/tools/clippy/clippy_utils/src/lib.rs index 1b32f0aaeb8df..ac6490cfd2c72 100644 --- a/src/tools/clippy/clippy_utils/src/lib.rs +++ b/src/tools/clippy/clippy_utils/src/lib.rs @@ -1826,7 +1826,7 @@ pub fn is_expr_used_or_unified(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool { .. }, .. - }), + }, _), .. }), _