1
Fork 0

Add let-else to AST

This commit is contained in:
Cameron Steffen 2021-06-22 13:00:58 -05:00
parent 6f388bb369
commit 89d2600d01
10 changed files with 92 additions and 26 deletions

View file

@ -1005,13 +1005,42 @@ pub struct Local {
pub id: NodeId, pub id: NodeId,
pub pat: P<Pat>, pub pat: P<Pat>,
pub ty: Option<P<Ty>>, pub ty: Option<P<Ty>>,
/// Initializer expression to set the value, if any. pub kind: LocalKind,
pub init: Option<P<Expr>>,
pub span: Span, pub span: Span,
pub attrs: AttrVec, pub attrs: AttrVec,
pub tokens: Option<LazyTokenStream>, pub tokens: Option<LazyTokenStream>,
} }
#[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<Expr>),
/// Local declaration with an initializer and an `else` clause.
/// Example: `let Some(x) = y else { return };`
InitElse(P<Expr>, P<Block>),
}
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'. /// An arm of a 'match'.
/// ///
/// E.g., `0..=10 => { println!("match!") }` as in /// E.g., `0..=10 => { println!("match!") }` as in

View file

@ -571,11 +571,20 @@ pub fn noop_visit_parenthesized_parameter_data<T: MutVisitor>(
} }
pub fn noop_visit_local<T: MutVisitor>(local: &mut P<Local>, vis: &mut T) { pub fn noop_visit_local<T: MutVisitor>(local: &mut P<Local>, 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_id(id);
vis.visit_pat(pat); vis.visit_pat(pat);
visit_opt(ty, |ty| vis.visit_ty(ty)); 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); vis.visit_span(span);
visit_thin_attrs(attrs, vis); visit_thin_attrs(attrs, vis);
visit_lazy_tts(tokens, vis); visit_lazy_tts(tokens, vis);

View file

@ -242,7 +242,10 @@ pub fn walk_local<'a, V: Visitor<'a>>(visitor: &mut V, local: &'a Local) {
} }
visitor.visit_pat(&local.pat); visitor.visit_pat(&local.pat);
walk_list!(visitor, visit_ty, &local.ty); 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) { pub fn walk_label<'a, V: Visitor<'a>>(visitor: &mut V, label: &'a Label) {

View file

@ -1798,7 +1798,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
.ty .ty
.as_ref() .as_ref()
.map(|t| self.lower_ty(t, ImplTraitContext::Disallowed(ImplTraitPosition::Binding))); .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); let hir_id = self.lower_node_id(l.id);
self.lower_attrs(hir_id, &l.attrs); self.lower_attrs(hir_id, &l.attrs);
hir::Local { hir::Local {

View file

@ -1518,13 +1518,19 @@ impl<'a> State<'a> {
self.ibox(INDENT_UNIT); self.ibox(INDENT_UNIT);
self.print_local_decl(loc); self.print_local_decl(loc);
self.end(); self.end();
if let Some(ref init) = loc.init { if let Some((init, els)) = loc.kind.init_else_opt() {
self.nbsp(); self.nbsp();
self.word_space("="); self.word_space("=");
self.print_expr(init); 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.s.word(";");
self.end(); self.end(); // `let` ibox
} }
ast::StmtKind::Item(ref item) => self.print_item(item), ast::StmtKind::Item(ref item) => self.print_item(item),
ast::StmtKind::Expr(ref expr) => { ast::StmtKind::Expr(ref expr) => {

View file

@ -3,7 +3,7 @@ use crate::deriving::generic::*;
use crate::deriving::path_std; use crate::deriving::path_std;
use rustc_ast::ptr::P; 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_expand::base::{Annotatable, ExtCtxt};
use rustc_span::symbol::{sym, Ident}; use rustc_span::symbol::{sym, Ident};
use rustc_span::{Span, DUMMY_SP}; use rustc_span::{Span, DUMMY_SP};
@ -135,8 +135,8 @@ fn stmt_let_underscore(cx: &mut ExtCtxt<'_>, sp: Span, expr: P<ast::Expr>) -> as
let local = P(ast::Local { let local = P(ast::Local {
pat: cx.pat_wild(sp), pat: cx.pat_wild(sp),
ty: None, ty: None,
init: Some(expr),
id: ast::DUMMY_NODE_ID, id: ast::DUMMY_NODE_ID,
kind: LocalKind::Init(expr),
span: sp, span: sp,
attrs: ast::AttrVec::new(), attrs: ast::AttrVec::new(),
tokens: None, tokens: None,

View file

@ -2,7 +2,7 @@ use crate::base::ExtCtxt;
use rustc_ast::attr; use rustc_ast::attr;
use rustc_ast::ptr::P; 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::source_map::Spanned;
use rustc_span::symbol::{kw, sym, Ident, Symbol}; use rustc_span::symbol::{kw, sym, Ident, Symbol};
@ -153,8 +153,8 @@ impl<'a> ExtCtxt<'a> {
let local = P(ast::Local { let local = P(ast::Local {
pat, pat,
ty: None, ty: None,
init: Some(ex),
id: ast::DUMMY_NODE_ID, id: ast::DUMMY_NODE_ID,
kind: LocalKind::Init(ex),
span: sp, span: sp,
attrs: AttrVec::new(), attrs: AttrVec::new(),
tokens: None, tokens: None,
@ -167,8 +167,8 @@ impl<'a> ExtCtxt<'a> {
let local = P(ast::Local { let local = P(ast::Local {
pat: self.pat_wild(span), pat: self.pat_wild(span),
ty: Some(ty), ty: Some(ty),
init: None,
id: ast::DUMMY_NODE_ID, id: ast::DUMMY_NODE_ID,
kind: LocalKind::Decl,
span, span,
attrs: AttrVec::new(), attrs: AttrVec::new(),
tokens: None, tokens: None,

View file

@ -618,7 +618,7 @@ trait UnusedDelimLint {
fn check_stmt(&mut self, cx: &EarlyContext<'_>, s: &ast::Stmt) { fn check_stmt(&mut self, cx: &EarlyContext<'_>, s: &ast::Stmt) {
match s.kind { match s.kind {
StmtKind::Local(ref local) if Self::LINT_EXPR_IN_PATTERN_MATCHING_CTX => { 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( self.check_unused_delims_expr(
cx, cx,
&value, &value,

View file

@ -11,8 +11,9 @@ use rustc_ast as ast;
use rustc_ast::ptr::P; use rustc_ast::ptr::P;
use rustc_ast::token::{self, TokenKind}; use rustc_ast::token::{self, TokenKind};
use rustc_ast::util::classify; use rustc_ast::util::classify;
use rustc_ast::AstLike; use rustc_ast::{
use rustc_ast::{AttrStyle, AttrVec, Attribute, MacCall, MacCallStmt, MacStmtStyle}; AstLike, AttrStyle, AttrVec, Attribute, LocalKind, MacCall, MacCallStmt, MacStmtStyle,
};
use rustc_ast::{Block, BlockCheckMode, Expr, ExprKind, Local, Stmt}; use rustc_ast::{Block, BlockCheckMode, Expr, ExprKind, Local, Stmt};
use rustc_ast::{StmtKind, DUMMY_NODE_ID}; use rustc_ast::{StmtKind, DUMMY_NODE_ID};
use rustc_errors::{Applicability, PResult}; use rustc_errors::{Applicability, PResult};
@ -292,8 +293,19 @@ impl<'a> Parser<'a> {
return Err(err); 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 }; 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;`). /// Parses the RHS of a local variable declaration (e.g., `= 14;`).
@ -495,13 +507,13 @@ impl<'a> Parser<'a> {
StmtKind::Expr(_) | StmtKind::MacCall(_) => {} StmtKind::Expr(_) | StmtKind::MacCall(_) => {}
StmtKind::Local(ref mut local) if let Err(e) = self.expect_semi() => { StmtKind::Local(ref mut local) if let Err(e) = self.expect_semi() => {
// We might be at the `,` in `let x = foo<bar, baz>;`. Try to recover. // We might be at the `,` in `let x = foo<bar, baz>;`. Try to recover.
match &mut local.init { match &mut local.kind {
Some(ref mut expr) => { LocalKind::Init(expr) | LocalKind::InitElse(expr, _) => {
self.check_mistyped_turbofish_with_multiple_type_params(e, expr)?; self.check_mistyped_turbofish_with_multiple_type_params(e, expr)?;
// We found `foo<bar, baz>`, have we fully recovered? // We found `foo<bar, baz>`, have we fully recovered?
self.expect_semi()?; self.expect_semi()?;
} }
None => return Err(e), LocalKind::Decl => return Err(e),
} }
eat_semi = false; eat_semi = false;
} }

View file

@ -454,7 +454,7 @@ impl<'a: 'ast, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> {
_ => Some(( _ => Some((
local.pat.span, local.pat.span,
local.ty.as_ref().map(|ty| ty.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); 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); walk_list!(self, visit_ty, &local.ty);
// Resolve the initializer. // 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. // Resolve the pattern.
self.resolve_pattern_top(&local.pat, PatternSource::Let); self.resolve_pattern_top(&local.pat, PatternSource::Let);