diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index e1ea464dedb..0632d937c4c 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -1005,13 +1005,42 @@ pub struct Local { pub id: NodeId, pub pat: P, pub ty: Option>, - /// Initializer expression to set the value, if any. - pub init: Option>, + pub kind: LocalKind, pub span: Span, pub attrs: AttrVec, pub tokens: Option, } +#[derive(Clone, Encodable, Decodable, Debug)] +pub enum LocalKind { + /// Local declaration. + /// Example: `let x;` + Decl, + /// Local declaration with an initializer. + /// Example: `let x = y;` + Init(P), + /// Local declaration with an initializer and an `else` clause. + /// Example: `let Some(x) = y else { return };` + InitElse(P, P), +} + +impl LocalKind { + pub fn init(&self) -> Option<&Expr> { + match self { + Self::Decl => None, + Self::Init(i) | Self::InitElse(i, _) => Some(i), + } + } + + pub fn init_else_opt(&self) -> Option<(&Expr, Option<&Block>)> { + match self { + Self::Decl => None, + Self::Init(init) => Some((init, None)), + Self::InitElse(init, els) => Some((init, Some(els))), + } + } +} + /// An arm of a 'match'. /// /// E.g., `0..=10 => { println!("match!") }` as in diff --git a/compiler/rustc_ast/src/mut_visit.rs b/compiler/rustc_ast/src/mut_visit.rs index fb4db6005ac..368a23e3429 100644 --- a/compiler/rustc_ast/src/mut_visit.rs +++ b/compiler/rustc_ast/src/mut_visit.rs @@ -571,11 +571,20 @@ pub fn noop_visit_parenthesized_parameter_data( } pub fn noop_visit_local(local: &mut P, vis: &mut T) { - let Local { id, pat, ty, init, span, attrs, tokens } = local.deref_mut(); + let Local { id, pat, ty, kind, span, attrs, tokens } = local.deref_mut(); vis.visit_id(id); vis.visit_pat(pat); visit_opt(ty, |ty| vis.visit_ty(ty)); - visit_opt(init, |init| vis.visit_expr(init)); + match kind { + LocalKind::Decl => {} + LocalKind::Init(init) => { + vis.visit_expr(init); + } + LocalKind::InitElse(init, els) => { + vis.visit_expr(init); + vis.visit_block(els); + } + } vis.visit_span(span); visit_thin_attrs(attrs, vis); visit_lazy_tts(tokens, vis); diff --git a/compiler/rustc_ast/src/visit.rs b/compiler/rustc_ast/src/visit.rs index dd8927496e0..c30f711b397 100644 --- a/compiler/rustc_ast/src/visit.rs +++ b/compiler/rustc_ast/src/visit.rs @@ -242,7 +242,10 @@ pub fn walk_local<'a, V: Visitor<'a>>(visitor: &mut V, local: &'a Local) { } visitor.visit_pat(&local.pat); walk_list!(visitor, visit_ty, &local.ty); - walk_list!(visitor, visit_expr, &local.init); + if let Some((init, els)) = local.kind.init_else_opt() { + visitor.visit_expr(init); + walk_list!(visitor, visit_block, els); + } } pub fn walk_label<'a, V: Visitor<'a>>(visitor: &mut V, label: &'a Label) { diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index b71fcb7a349..8efb1680d85 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -1798,7 +1798,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { .ty .as_ref() .map(|t| self.lower_ty(t, ImplTraitContext::Disallowed(ImplTraitPosition::Binding))); - let init = l.init.as_ref().map(|e| self.lower_expr(e)); + let init = l.kind.init().map(|init| self.lower_expr(init)); let hir_id = self.lower_node_id(l.id); self.lower_attrs(hir_id, &l.attrs); hir::Local { diff --git a/compiler/rustc_ast_pretty/src/pprust/state.rs b/compiler/rustc_ast_pretty/src/pprust/state.rs index 949e7a1fbb8..3cf04be160c 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state.rs @@ -1518,13 +1518,19 @@ impl<'a> State<'a> { self.ibox(INDENT_UNIT); self.print_local_decl(loc); self.end(); - if let Some(ref init) = loc.init { + if let Some((init, els)) = loc.kind.init_else_opt() { self.nbsp(); self.word_space("="); self.print_expr(init); + if let Some(els) = els { + self.cbox(INDENT_UNIT); + self.ibox(INDENT_UNIT); + self.s.word(" else "); + self.print_block(els); + } } self.s.word(";"); - self.end(); + self.end(); // `let` ibox } ast::StmtKind::Item(ref item) => self.print_item(item), ast::StmtKind::Expr(ref expr) => { diff --git a/compiler/rustc_builtin_macros/src/deriving/debug.rs b/compiler/rustc_builtin_macros/src/deriving/debug.rs index cc6dac52d76..14506f296bf 100644 --- a/compiler/rustc_builtin_macros/src/deriving/debug.rs +++ b/compiler/rustc_builtin_macros/src/deriving/debug.rs @@ -3,7 +3,7 @@ use crate::deriving::generic::*; use crate::deriving::path_std; use rustc_ast::ptr::P; -use rustc_ast::{self as ast, Expr, MetaItem}; +use rustc_ast::{self as ast, Expr, LocalKind, MetaItem}; use rustc_expand::base::{Annotatable, ExtCtxt}; use rustc_span::symbol::{sym, Ident}; use rustc_span::{Span, DUMMY_SP}; @@ -135,8 +135,8 @@ fn stmt_let_underscore(cx: &mut ExtCtxt<'_>, sp: Span, expr: P) -> as let local = P(ast::Local { pat: cx.pat_wild(sp), ty: None, - init: Some(expr), id: ast::DUMMY_NODE_ID, + kind: LocalKind::Init(expr), span: sp, attrs: ast::AttrVec::new(), tokens: None, diff --git a/compiler/rustc_expand/src/build.rs b/compiler/rustc_expand/src/build.rs index 824df2757ea..1d83ecbfd40 100644 --- a/compiler/rustc_expand/src/build.rs +++ b/compiler/rustc_expand/src/build.rs @@ -2,7 +2,7 @@ use crate::base::ExtCtxt; use rustc_ast::attr; use rustc_ast::ptr::P; -use rustc_ast::{self as ast, AttrVec, BlockCheckMode, Expr, PatKind, UnOp}; +use rustc_ast::{self as ast, AttrVec, BlockCheckMode, Expr, LocalKind, PatKind, UnOp}; use rustc_span::source_map::Spanned; use rustc_span::symbol::{kw, sym, Ident, Symbol}; @@ -153,8 +153,8 @@ impl<'a> ExtCtxt<'a> { let local = P(ast::Local { pat, ty: None, - init: Some(ex), id: ast::DUMMY_NODE_ID, + kind: LocalKind::Init(ex), span: sp, attrs: AttrVec::new(), tokens: None, @@ -167,8 +167,8 @@ impl<'a> ExtCtxt<'a> { let local = P(ast::Local { pat: self.pat_wild(span), ty: Some(ty), - init: None, id: ast::DUMMY_NODE_ID, + kind: LocalKind::Decl, span, attrs: AttrVec::new(), tokens: None, diff --git a/compiler/rustc_lint/src/unused.rs b/compiler/rustc_lint/src/unused.rs index be137884b4b..a289b379fc8 100644 --- a/compiler/rustc_lint/src/unused.rs +++ b/compiler/rustc_lint/src/unused.rs @@ -618,7 +618,7 @@ trait UnusedDelimLint { fn check_stmt(&mut self, cx: &EarlyContext<'_>, s: &ast::Stmt) { match s.kind { StmtKind::Local(ref local) if Self::LINT_EXPR_IN_PATTERN_MATCHING_CTX => { - if let Some(ref value) = local.init { + if let Some(value) = local.kind.init() { self.check_unused_delims_expr( cx, &value, diff --git a/compiler/rustc_parse/src/parser/stmt.rs b/compiler/rustc_parse/src/parser/stmt.rs index 85515bd2a63..d083c379f77 100644 --- a/compiler/rustc_parse/src/parser/stmt.rs +++ b/compiler/rustc_parse/src/parser/stmt.rs @@ -11,8 +11,9 @@ use rustc_ast as ast; use rustc_ast::ptr::P; use rustc_ast::token::{self, TokenKind}; use rustc_ast::util::classify; -use rustc_ast::AstLike; -use rustc_ast::{AttrStyle, AttrVec, Attribute, MacCall, MacCallStmt, MacStmtStyle}; +use rustc_ast::{ + AstLike, AttrStyle, AttrVec, Attribute, LocalKind, MacCall, MacCallStmt, MacStmtStyle, +}; use rustc_ast::{Block, BlockCheckMode, Expr, ExprKind, Local, Stmt}; use rustc_ast::{StmtKind, DUMMY_NODE_ID}; use rustc_errors::{Applicability, PResult}; @@ -292,8 +293,19 @@ impl<'a> Parser<'a> { return Err(err); } }; + let kind = match init { + None => LocalKind::Decl, + Some(init) => { + if self.eat_keyword(kw::Else) { + let els = self.parse_block()?; + LocalKind::InitElse(init, els) + } else { + LocalKind::Init(init) + } + } + }; let hi = if self.token == token::Semi { self.token.span } else { self.prev_token.span }; - Ok(P(ast::Local { ty, pat, init, id: DUMMY_NODE_ID, span: lo.to(hi), attrs, tokens: None })) + Ok(P(ast::Local { ty, pat, kind, id: DUMMY_NODE_ID, span: lo.to(hi), attrs, tokens: None })) } /// Parses the RHS of a local variable declaration (e.g., `= 14;`). @@ -495,13 +507,13 @@ impl<'a> Parser<'a> { StmtKind::Expr(_) | StmtKind::MacCall(_) => {} StmtKind::Local(ref mut local) if let Err(e) = self.expect_semi() => { // We might be at the `,` in `let x = foo;`. Try to recover. - match &mut local.init { - Some(ref mut expr) => { - self.check_mistyped_turbofish_with_multiple_type_params(e, expr)?; - // We found `foo`, have we fully recovered? - self.expect_semi()?; - } - None => return Err(e), + match &mut local.kind { + LocalKind::Init(expr) | LocalKind::InitElse(expr, _) => { + self.check_mistyped_turbofish_with_multiple_type_params(e, expr)?; + // We found `foo`, have we fully recovered? + self.expect_semi()?; + } + LocalKind::Decl => return Err(e), } eat_semi = false; } diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index 0b552aa07f5..5c7b4b02822 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -454,7 +454,7 @@ impl<'a: 'ast, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> { _ => Some(( local.pat.span, local.ty.as_ref().map(|ty| ty.span), - local.init.as_ref().map(|init| init.span), + local.kind.init().map(|init| init.span), )), }; let original = replace(&mut self.diagnostic_metadata.current_let_binding, local_spans); @@ -1426,7 +1426,14 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { walk_list!(self, visit_ty, &local.ty); // Resolve the initializer. - walk_list!(self, visit_expr, &local.init); + if let Some((init, els)) = local.kind.init_else_opt() { + self.visit_expr(init); + + // Resolve the `else` block + if let Some(els) = els { + self.visit_block(els); + } + } // Resolve the pattern. self.resolve_pattern_top(&local.pat, PatternSource::Let);