Auto merge of #87688 - camsteffen:let-else, r=cjgillot
Introduce `let...else` Tracking issue: #87335 The trickiest part for me was enforcing the diverging else block with clear diagnostics. Perhaps the obvious solution is to expand to `let _: ! = ..`, but I decided against this because, when a "mismatched type" error is found in typeck, there is no way to trace where in the HIR the expected type originated, AFAICT. In order to pass down this information, I believe we should introduce `Expectation::LetElseNever(HirId)` or maybe add `HirId` to `Expectation::HasType`, but I left that as a future enhancement. For now, I simply assert that the block is `!` with a custom `ObligationCauseCode`, and I think this is clear enough, at least to start. The downside here is that the error points at the entire block rather than the specific expression with the wrong type. I left a todo to this effect. Overall, I believe this PR is feature-complete with regard to the RFC.
This commit is contained in:
commit
c2a408840a
59 changed files with 901 additions and 232 deletions
|
@ -1005,13 +1005,42 @@ pub struct Local {
|
|||
pub id: NodeId,
|
||||
pub pat: P<Pat>,
|
||||
pub ty: Option<P<Ty>>,
|
||||
/// Initializer expression to set the value, if any.
|
||||
pub init: Option<P<Expr>>,
|
||||
pub kind: LocalKind,
|
||||
pub span: Span,
|
||||
pub attrs: AttrVec,
|
||||
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'.
|
||||
///
|
||||
/// E.g., `0..=10 => { println!("match!") }` as in
|
||||
|
|
|
@ -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) {
|
||||
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);
|
||||
|
|
|
@ -23,3 +23,30 @@ pub fn expr_requires_semi_to_be_stmt(e: &ast::Expr) -> bool {
|
|||
| ast::ExprKind::TryBlock(..)
|
||||
)
|
||||
}
|
||||
|
||||
/// If an expression ends with `}`, returns the innermost expression ending in the `}`
|
||||
pub fn expr_trailing_brace(mut expr: &ast::Expr) -> Option<&ast::Expr> {
|
||||
use ast::ExprKind::*;
|
||||
|
||||
loop {
|
||||
match &expr.kind {
|
||||
AddrOf(_, _, e)
|
||||
| Assign(_, e, _)
|
||||
| AssignOp(_, _, e)
|
||||
| Binary(_, _, e)
|
||||
| Box(e)
|
||||
| Break(_, Some(e))
|
||||
| Closure(.., e, _)
|
||||
| Let(_, e, _)
|
||||
| Range(_, Some(e), _)
|
||||
| Ret(Some(e))
|
||||
| Unary(_, e)
|
||||
| Yield(Some(e)) => {
|
||||
expr = e;
|
||||
}
|
||||
Async(..) | Block(..) | ForLoop(..) | If(..) | Loop(..) | Match(..) | Struct(..)
|
||||
| TryBlock(..) | While(..) => break Some(expr),
|
||||
_ => break None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
185
compiler/rustc_ast_lowering/src/block.rs
Normal file
185
compiler/rustc_ast_lowering/src/block.rs
Normal file
|
@ -0,0 +1,185 @@
|
|||
use crate::{ImplTraitContext, ImplTraitPosition, LoweringContext};
|
||||
use rustc_ast::{AttrVec, Block, BlockCheckMode, Expr, Local, LocalKind, Stmt, StmtKind};
|
||||
use rustc_hir as hir;
|
||||
use rustc_session::parse::feature_err;
|
||||
use rustc_span::symbol::Ident;
|
||||
use rustc_span::{sym, DesugaringKind};
|
||||
|
||||
use smallvec::SmallVec;
|
||||
|
||||
impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
||||
pub(super) fn lower_block(
|
||||
&mut self,
|
||||
b: &Block,
|
||||
targeted_by_break: bool,
|
||||
) -> &'hir hir::Block<'hir> {
|
||||
self.arena.alloc(self.lower_block_noalloc(b, targeted_by_break))
|
||||
}
|
||||
|
||||
pub(super) fn lower_block_noalloc(
|
||||
&mut self,
|
||||
b: &Block,
|
||||
targeted_by_break: bool,
|
||||
) -> hir::Block<'hir> {
|
||||
let (stmts, expr) = self.lower_stmts(&b.stmts);
|
||||
let rules = self.lower_block_check_mode(&b.rules);
|
||||
let hir_id = self.lower_node_id(b.id);
|
||||
hir::Block { hir_id, stmts, expr, rules, span: self.lower_span(b.span), targeted_by_break }
|
||||
}
|
||||
|
||||
fn lower_stmts(
|
||||
&mut self,
|
||||
mut ast_stmts: &[Stmt],
|
||||
) -> (&'hir [hir::Stmt<'hir>], Option<&'hir hir::Expr<'hir>>) {
|
||||
let mut stmts = SmallVec::<[hir::Stmt<'hir>; 8]>::new();
|
||||
let mut expr = None;
|
||||
while let [s, tail @ ..] = ast_stmts {
|
||||
match s.kind {
|
||||
StmtKind::Local(ref local) => {
|
||||
let hir_id = self.lower_node_id(s.id);
|
||||
match &local.kind {
|
||||
LocalKind::InitElse(init, els) => {
|
||||
let (s, e) = self.lower_let_else(hir_id, local, init, els, tail);
|
||||
stmts.push(s);
|
||||
expr = Some(e);
|
||||
// remaining statements are in let-else expression
|
||||
break;
|
||||
}
|
||||
_ => {
|
||||
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 });
|
||||
}
|
||||
}
|
||||
}
|
||||
StmtKind::Item(ref it) => {
|
||||
stmts.extend(self.lower_item_id(it).into_iter().enumerate().map(
|
||||
|(i, item_id)| {
|
||||
let hir_id = match i {
|
||||
0 => self.lower_node_id(s.id),
|
||||
_ => self.next_id(),
|
||||
};
|
||||
let kind = hir::StmtKind::Item(item_id);
|
||||
let span = self.lower_span(s.span);
|
||||
hir::Stmt { hir_id, kind, span }
|
||||
},
|
||||
));
|
||||
}
|
||||
StmtKind::Expr(ref e) => {
|
||||
let e = self.lower_expr(e);
|
||||
if tail.is_empty() {
|
||||
expr = Some(e);
|
||||
} else {
|
||||
let hir_id = self.lower_node_id(s.id);
|
||||
self.alias_attrs(hir_id, e.hir_id);
|
||||
let kind = hir::StmtKind::Expr(e);
|
||||
let span = self.lower_span(s.span);
|
||||
stmts.push(hir::Stmt { hir_id, kind, span });
|
||||
}
|
||||
}
|
||||
StmtKind::Semi(ref e) => {
|
||||
let e = self.lower_expr(e);
|
||||
let hir_id = self.lower_node_id(s.id);
|
||||
self.alias_attrs(hir_id, e.hir_id);
|
||||
let kind = hir::StmtKind::Semi(e);
|
||||
let span = self.lower_span(s.span);
|
||||
stmts.push(hir::Stmt { hir_id, kind, span });
|
||||
}
|
||||
StmtKind::Empty => {}
|
||||
StmtKind::MacCall(..) => panic!("shouldn't exist here"),
|
||||
}
|
||||
ast_stmts = &ast_stmts[1..];
|
||||
}
|
||||
(self.arena.alloc_from_iter(stmts), expr)
|
||||
}
|
||||
|
||||
fn lower_local(&mut self, l: &Local) -> &'hir hir::Local<'hir> {
|
||||
let ty = l
|
||||
.ty
|
||||
.as_ref()
|
||||
.map(|t| self.lower_ty(t, ImplTraitContext::Disallowed(ImplTraitPosition::Binding)));
|
||||
let init = l.kind.init().map(|init| self.lower_expr(init));
|
||||
let hir_id = self.lower_node_id(l.id);
|
||||
let pat = self.lower_pat(&l.pat);
|
||||
let span = self.lower_span(l.span);
|
||||
let source = hir::LocalSource::Normal;
|
||||
self.lower_attrs(hir_id, &l.attrs);
|
||||
self.arena.alloc(hir::Local { hir_id, ty, pat, init, span, source })
|
||||
}
|
||||
|
||||
fn lower_block_check_mode(&mut self, b: &BlockCheckMode) -> hir::BlockCheckMode {
|
||||
match *b {
|
||||
BlockCheckMode::Default => hir::BlockCheckMode::DefaultBlock,
|
||||
BlockCheckMode::Unsafe(u) => {
|
||||
hir::BlockCheckMode::UnsafeBlock(self.lower_unsafe_source(u))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn lower_let_else(
|
||||
&mut self,
|
||||
stmt_hir_id: hir::HirId,
|
||||
local: &Local,
|
||||
init: &Expr,
|
||||
els: &Block,
|
||||
tail: &[Stmt],
|
||||
) -> (hir::Stmt<'hir>, &'hir hir::Expr<'hir>) {
|
||||
let ty = local
|
||||
.ty
|
||||
.as_ref()
|
||||
.map(|t| self.lower_ty(t, ImplTraitContext::Disallowed(ImplTraitPosition::Binding)));
|
||||
let span = self.lower_span(local.span);
|
||||
let span = self.mark_span_with_reason(DesugaringKind::LetElse, span, None);
|
||||
let init = Some(self.lower_expr(init));
|
||||
let val = Ident::with_dummy_span(sym::val);
|
||||
let (pat, val_id) =
|
||||
self.pat_ident_binding_mode(span, val, hir::BindingAnnotation::Unannotated);
|
||||
let local_hir_id = self.lower_node_id(local.id);
|
||||
self.lower_attrs(local_hir_id, &local.attrs);
|
||||
// first statement which basically exists for the type annotation
|
||||
let stmt = {
|
||||
let local = self.arena.alloc(hir::Local {
|
||||
hir_id: local_hir_id,
|
||||
ty,
|
||||
pat,
|
||||
init,
|
||||
span,
|
||||
source: hir::LocalSource::Normal,
|
||||
});
|
||||
let kind = hir::StmtKind::Local(local);
|
||||
hir::Stmt { hir_id: stmt_hir_id, kind, span }
|
||||
};
|
||||
let let_expr = {
|
||||
let scrutinee = self.expr_ident(span, val, val_id);
|
||||
let let_kind = hir::ExprKind::Let(self.lower_pat(&local.pat), scrutinee, span);
|
||||
self.arena.alloc(self.expr(span, let_kind, 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(else_expr.hir_id, local_hir_id);
|
||||
let if_expr = self.arena.alloc(hir::Expr {
|
||||
hir_id: self.next_id(),
|
||||
span,
|
||||
kind: hir::ExprKind::If(let_expr, then_expr, Some(else_expr)),
|
||||
});
|
||||
if !self.sess.features_untracked().let_else {
|
||||
feature_err(
|
||||
&self.sess.parse_sess,
|
||||
sym::let_else,
|
||||
local.span,
|
||||
"`let...else` statements are unstable",
|
||||
)
|
||||
.emit();
|
||||
}
|
||||
(stmt, if_expr)
|
||||
}
|
||||
}
|
|
@ -64,7 +64,7 @@ use rustc_span::symbol::{kw, sym, Ident, Symbol};
|
|||
use rustc_span::{Span, DUMMY_SP};
|
||||
use rustc_target::spec::abi::Abi;
|
||||
|
||||
use smallvec::{smallvec, SmallVec};
|
||||
use smallvec::SmallVec;
|
||||
use std::collections::BTreeMap;
|
||||
use std::mem;
|
||||
use tracing::{debug, trace};
|
||||
|
@ -77,6 +77,7 @@ macro_rules! arena_vec {
|
|||
}
|
||||
|
||||
mod asm;
|
||||
mod block;
|
||||
mod expr;
|
||||
mod item;
|
||||
mod pat;
|
||||
|
@ -1793,24 +1794,6 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
|||
)
|
||||
}
|
||||
|
||||
fn lower_local(&mut self, l: &Local) -> hir::Local<'hir> {
|
||||
let ty = l
|
||||
.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 hir_id = self.lower_node_id(l.id);
|
||||
self.lower_attrs(hir_id, &l.attrs);
|
||||
hir::Local {
|
||||
hir_id,
|
||||
ty,
|
||||
pat: self.lower_pat(&l.pat),
|
||||
init,
|
||||
span: self.lower_span(l.span),
|
||||
source: hir::LocalSource::Normal,
|
||||
}
|
||||
}
|
||||
|
||||
fn lower_fn_params_to_names(&mut self, decl: &FnDecl) -> &'hir [Ident] {
|
||||
// Skip the `...` (`CVarArgs`) trailing arguments from the AST,
|
||||
// as they are not explicit in HIR/Ty function signatures.
|
||||
|
@ -2396,23 +2379,6 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
|||
bounds.iter().map(move |bound| self.lower_param_bound(bound, itctx.reborrow()))
|
||||
}
|
||||
|
||||
fn lower_block(&mut self, b: &Block, targeted_by_break: bool) -> &'hir hir::Block<'hir> {
|
||||
self.arena.alloc(self.lower_block_noalloc(b, targeted_by_break))
|
||||
}
|
||||
|
||||
fn lower_block_noalloc(&mut self, b: &Block, targeted_by_break: bool) -> hir::Block<'hir> {
|
||||
let (stmts, expr) = match &*b.stmts {
|
||||
[stmts @ .., Stmt { kind: StmtKind::Expr(e), .. }] => (stmts, Some(&*e)),
|
||||
stmts => (stmts, None),
|
||||
};
|
||||
let stmts = self.arena.alloc_from_iter(stmts.iter().flat_map(|stmt| self.lower_stmt(stmt)));
|
||||
let expr = expr.map(|e| self.lower_expr(e));
|
||||
let rules = self.lower_block_check_mode(&b.rules);
|
||||
let hir_id = self.lower_node_id(b.id);
|
||||
|
||||
hir::Block { hir_id, stmts, expr, rules, span: self.lower_span(b.span), targeted_by_break }
|
||||
}
|
||||
|
||||
/// Lowers a block directly to an expression, presuming that it
|
||||
/// has no attributes and is not targeted by a `break`.
|
||||
fn lower_block_expr(&mut self, b: &Block) -> hir::Expr<'hir> {
|
||||
|
@ -2427,65 +2393,6 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
|||
})
|
||||
}
|
||||
|
||||
fn lower_stmt(&mut self, s: &Stmt) -> SmallVec<[hir::Stmt<'hir>; 1]> {
|
||||
let (hir_id, kind) = match s.kind {
|
||||
StmtKind::Local(ref l) => {
|
||||
let l = self.lower_local(l);
|
||||
let hir_id = self.lower_node_id(s.id);
|
||||
self.alias_attrs(hir_id, l.hir_id);
|
||||
return smallvec![hir::Stmt {
|
||||
hir_id,
|
||||
kind: hir::StmtKind::Local(self.arena.alloc(l)),
|
||||
span: self.lower_span(s.span),
|
||||
}];
|
||||
}
|
||||
StmtKind::Item(ref it) => {
|
||||
// Can only use the ID once.
|
||||
let mut id = Some(s.id);
|
||||
return self
|
||||
.lower_item_id(it)
|
||||
.into_iter()
|
||||
.map(|item_id| {
|
||||
let hir_id = id
|
||||
.take()
|
||||
.map(|id| self.lower_node_id(id))
|
||||
.unwrap_or_else(|| self.next_id());
|
||||
|
||||
hir::Stmt {
|
||||
hir_id,
|
||||
kind: hir::StmtKind::Item(item_id),
|
||||
span: self.lower_span(s.span),
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
}
|
||||
StmtKind::Expr(ref e) => {
|
||||
let e = self.lower_expr(e);
|
||||
let hir_id = self.lower_node_id(s.id);
|
||||
self.alias_attrs(hir_id, e.hir_id);
|
||||
(hir_id, hir::StmtKind::Expr(e))
|
||||
}
|
||||
StmtKind::Semi(ref e) => {
|
||||
let e = self.lower_expr(e);
|
||||
let hir_id = self.lower_node_id(s.id);
|
||||
self.alias_attrs(hir_id, e.hir_id);
|
||||
(hir_id, hir::StmtKind::Semi(e))
|
||||
}
|
||||
StmtKind::Empty => return smallvec![],
|
||||
StmtKind::MacCall(..) => panic!("shouldn't exist here"),
|
||||
};
|
||||
smallvec![hir::Stmt { hir_id, kind, span: self.lower_span(s.span) }]
|
||||
}
|
||||
|
||||
fn lower_block_check_mode(&mut self, b: &BlockCheckMode) -> hir::BlockCheckMode {
|
||||
match *b {
|
||||
BlockCheckMode::Default => hir::BlockCheckMode::DefaultBlock,
|
||||
BlockCheckMode::Unsafe(u) => {
|
||||
hir::BlockCheckMode::UnsafeBlock(self.lower_unsafe_source(u))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn lower_unsafe_source(&mut self, u: UnsafeSource) -> hir::UnsafeSource {
|
||||
match u {
|
||||
CompilerGenerated => hir::UnsafeSource::CompilerGenerated,
|
||||
|
|
|
@ -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) => {
|
||||
|
|
|
@ -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<ast::Expr>) -> 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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -676,6 +676,9 @@ declare_features! (
|
|||
/// Allows additional const parameter types, such as `&'static str` or user defined types
|
||||
(incomplete, adt_const_params, "1.56.0", Some(44580), None),
|
||||
|
||||
/// Allows `let...else` statements.
|
||||
(active, let_else, "1.56.0", Some(87335), None),
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// feature-group-end: actual feature gates
|
||||
// -------------------------------------------------------------------------
|
||||
|
|
|
@ -781,6 +781,10 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
|
|||
);
|
||||
}
|
||||
}
|
||||
ObligationCauseCode::LetElse => {
|
||||
err.help("try adding a diverging expression, such as `return` or `panic!(..)`");
|
||||
err.help("...or use `match` instead of `let...else`");
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
@ -2592,6 +2596,7 @@ impl<'tcx> ObligationCauseExt<'tcx> for ObligationCause<'tcx> {
|
|||
}
|
||||
IfExpression { .. } => Error0308("`if` and `else` have incompatible types"),
|
||||
IfExpressionWithNoElse => Error0317("`if` may be missing an `else` clause"),
|
||||
LetElse => Error0308("`else` clause of `let...else` does not diverge"),
|
||||
MainFunctionType => Error0580("`main` function has wrong type"),
|
||||
StartFunctionType => Error0308("`#[start]` function has wrong type"),
|
||||
IntrinsicType => Error0308("intrinsic has wrong type"),
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use crate::Lint;
|
||||
use crate::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintContext};
|
||||
use rustc_ast as ast;
|
||||
use rustc_ast::util::parser;
|
||||
use rustc_ast::util::{classify, parser};
|
||||
use rustc_ast::{ExprKind, StmtKind};
|
||||
use rustc_ast_pretty::pprust;
|
||||
use rustc_errors::{pluralize, Applicability};
|
||||
|
@ -382,6 +382,7 @@ enum UnusedDelimsCtx {
|
|||
FunctionArg,
|
||||
MethodArg,
|
||||
AssignedValue,
|
||||
AssignedValueLetElse,
|
||||
IfCond,
|
||||
WhileCond,
|
||||
ForIterExpr,
|
||||
|
@ -398,7 +399,9 @@ impl From<UnusedDelimsCtx> for &'static str {
|
|||
match ctx {
|
||||
UnusedDelimsCtx::FunctionArg => "function argument",
|
||||
UnusedDelimsCtx::MethodArg => "method argument",
|
||||
UnusedDelimsCtx::AssignedValue => "assigned value",
|
||||
UnusedDelimsCtx::AssignedValue | UnusedDelimsCtx::AssignedValueLetElse => {
|
||||
"assigned value"
|
||||
}
|
||||
UnusedDelimsCtx::IfCond => "`if` condition",
|
||||
UnusedDelimsCtx::WhileCond => "`while` condition",
|
||||
UnusedDelimsCtx::ForIterExpr => "`for` iterator expression",
|
||||
|
@ -441,14 +444,26 @@ trait UnusedDelimLint {
|
|||
right_pos: Option<BytePos>,
|
||||
);
|
||||
|
||||
fn is_expr_delims_necessary(inner: &ast::Expr, followed_by_block: bool) -> bool {
|
||||
fn is_expr_delims_necessary(
|
||||
inner: &ast::Expr,
|
||||
followed_by_block: bool,
|
||||
followed_by_else: bool,
|
||||
) -> bool {
|
||||
if followed_by_else {
|
||||
match inner.kind {
|
||||
ast::ExprKind::Binary(op, ..) if op.node.lazy() => return true,
|
||||
_ if classify::expr_trailing_brace(inner).is_some() => return true,
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
// Prevent false-positives in cases like `fn x() -> u8 { ({ 0 } + 1) }`
|
||||
let lhs_needs_parens = {
|
||||
let mut innermost = inner;
|
||||
loop {
|
||||
if let ExprKind::Binary(_, lhs, _rhs) = &innermost.kind {
|
||||
innermost = lhs;
|
||||
if !rustc_ast::util::classify::expr_requires_semi_to_be_stmt(innermost) {
|
||||
if !classify::expr_requires_semi_to_be_stmt(innermost) {
|
||||
break true;
|
||||
}
|
||||
} else {
|
||||
|
@ -618,15 +633,12 @@ 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 {
|
||||
self.check_unused_delims_expr(
|
||||
cx,
|
||||
&value,
|
||||
UnusedDelimsCtx::AssignedValue,
|
||||
false,
|
||||
None,
|
||||
None,
|
||||
);
|
||||
if let Some((init, els)) = local.kind.init_else_opt() {
|
||||
let ctx = match els {
|
||||
None => UnusedDelimsCtx::AssignedValue,
|
||||
Some(_) => UnusedDelimsCtx::AssignedValueLetElse,
|
||||
};
|
||||
self.check_unused_delims_expr(cx, init, ctx, false, None, None);
|
||||
}
|
||||
}
|
||||
StmtKind::Expr(ref expr) => {
|
||||
|
@ -702,7 +714,8 @@ impl UnusedDelimLint for UnusedParens {
|
|||
) {
|
||||
match value.kind {
|
||||
ast::ExprKind::Paren(ref inner) => {
|
||||
if !Self::is_expr_delims_necessary(inner, followed_by_block)
|
||||
let followed_by_else = ctx == UnusedDelimsCtx::AssignedValueLetElse;
|
||||
if !Self::is_expr_delims_necessary(inner, followed_by_block, followed_by_else)
|
||||
&& value.attrs.is_empty()
|
||||
&& !value.span.from_expansion()
|
||||
&& (ctx != UnusedDelimsCtx::LetScrutineeExpr
|
||||
|
@ -941,7 +954,7 @@ impl UnusedDelimLint for UnusedBraces {
|
|||
// FIXME(const_generics): handle paths when #67075 is fixed.
|
||||
if let [stmt] = inner.stmts.as_slice() {
|
||||
if let ast::StmtKind::Expr(ref expr) = stmt.kind {
|
||||
if !Self::is_expr_delims_necessary(expr, followed_by_block)
|
||||
if !Self::is_expr_delims_necessary(expr, followed_by_block, false)
|
||||
&& (ctx != UnusedDelimsCtx::AnonConst
|
||||
|| matches!(expr.kind, ast::ExprKind::Lit(_)))
|
||||
&& !cx.sess().source_map().is_multiline(value.span)
|
||||
|
|
|
@ -305,6 +305,9 @@ pub enum ObligationCauseCode<'tcx> {
|
|||
/// Intrinsic has wrong type
|
||||
IntrinsicType,
|
||||
|
||||
/// A let else block does not diverge
|
||||
LetElse,
|
||||
|
||||
/// Method receiver
|
||||
MethodReceiver,
|
||||
|
||||
|
|
|
@ -17,7 +17,7 @@ use rustc_middle::ty::{self, Ty, TyCtxt};
|
|||
use rustc_session::lint::builtin::BINDINGS_WITH_VARIANT_NAME;
|
||||
use rustc_session::lint::builtin::{IRREFUTABLE_LET_PATTERNS, UNREACHABLE_PATTERNS};
|
||||
use rustc_session::Session;
|
||||
use rustc_span::Span;
|
||||
use rustc_span::{DesugaringKind, ExpnKind, Span};
|
||||
use std::slice;
|
||||
|
||||
crate fn check_match(tcx: TyCtxt<'_>, def_id: DefId) {
|
||||
|
@ -118,31 +118,6 @@ impl<'tcx> MatchVisitor<'_, 'tcx> {
|
|||
check_for_bindings_named_same_as_variants(self, pat);
|
||||
}
|
||||
|
||||
fn let_source(&mut self, pat: &'tcx hir::Pat<'tcx>, _expr: &hir::Expr<'_>) -> LetSource {
|
||||
let hir = self.tcx.hir();
|
||||
let parent = hir.get_parent_node(pat.hir_id);
|
||||
let parent_parent = hir.get_parent_node(parent);
|
||||
let parent_parent_node = hir.get(parent_parent);
|
||||
|
||||
let parent_parent_parent = hir.get_parent_node(parent_parent);
|
||||
let parent_parent_parent_parent = hir.get_parent_node(parent_parent_parent);
|
||||
let parent_parent_parent_parent_node = hir.get(parent_parent_parent_parent);
|
||||
|
||||
if let hir::Node::Expr(hir::Expr {
|
||||
kind: hir::ExprKind::Loop(_, _, hir::LoopSource::While, _),
|
||||
..
|
||||
}) = parent_parent_parent_parent_node
|
||||
{
|
||||
LetSource::WhileLet
|
||||
} else if let hir::Node::Expr(hir::Expr { kind: hir::ExprKind::If { .. }, .. }) =
|
||||
parent_parent_node
|
||||
{
|
||||
LetSource::IfLet
|
||||
} else {
|
||||
LetSource::GenericLet
|
||||
}
|
||||
}
|
||||
|
||||
fn lower_pattern<'p>(
|
||||
&self,
|
||||
cx: &mut MatchCheckCtxt<'p, 'tcx>,
|
||||
|
@ -172,10 +147,9 @@ impl<'tcx> MatchVisitor<'_, 'tcx> {
|
|||
|
||||
fn check_let(&mut self, pat: &'tcx hir::Pat<'tcx>, expr: &hir::Expr<'_>, span: Span) {
|
||||
self.check_patterns(pat);
|
||||
let ls = self.let_source(pat, expr);
|
||||
let mut cx = self.new_cx(expr.hir_id);
|
||||
let tpat = self.lower_pattern(&mut cx, pat, &mut false).0;
|
||||
check_let_reachability(&mut cx, ls, pat.hir_id, &tpat, span);
|
||||
check_let_reachability(&mut cx, pat.hir_id, &tpat, span);
|
||||
}
|
||||
|
||||
fn check_match(
|
||||
|
@ -192,13 +166,7 @@ impl<'tcx> MatchVisitor<'_, 'tcx> {
|
|||
if let Some(hir::Guard::IfLet(ref pat, _)) = arm.guard {
|
||||
self.check_patterns(pat);
|
||||
let tpat = self.lower_pattern(&mut cx, pat, &mut false).0;
|
||||
check_let_reachability(
|
||||
&mut cx,
|
||||
LetSource::IfLetGuard,
|
||||
pat.hir_id,
|
||||
&tpat,
|
||||
tpat.span,
|
||||
);
|
||||
check_let_reachability(&mut cx, pat.hir_id, &tpat, tpat.span);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -397,7 +365,7 @@ fn unreachable_pattern(tcx: TyCtxt<'_>, span: Span, id: HirId, catchall: Option<
|
|||
});
|
||||
}
|
||||
|
||||
fn irrefutable_let_pattern(id: HirId, ls: LetSource, span: Span, tcx: TyCtxt<'_>) {
|
||||
fn irrefutable_let_pattern(tcx: TyCtxt<'_>, id: HirId, span: Span) {
|
||||
macro_rules! emit_diag {
|
||||
(
|
||||
$lint:expr,
|
||||
|
@ -412,7 +380,12 @@ fn irrefutable_let_pattern(id: HirId, ls: LetSource, span: Span, tcx: TyCtxt<'_>
|
|||
}};
|
||||
}
|
||||
|
||||
tcx.struct_span_lint_hir(IRREFUTABLE_LET_PATTERNS, id, span, |lint| match ls {
|
||||
let source = let_source(tcx, id);
|
||||
let span = match source {
|
||||
LetSource::LetElse(span) => span,
|
||||
_ => span,
|
||||
};
|
||||
tcx.struct_span_lint_hir(IRREFUTABLE_LET_PATTERNS, id, span, |lint| match source {
|
||||
LetSource::GenericLet => {
|
||||
emit_diag!(lint, "`let`", "`let` is useless", "removing `let`");
|
||||
}
|
||||
|
@ -432,6 +405,14 @@ fn irrefutable_let_pattern(id: HirId, ls: LetSource, span: Span, tcx: TyCtxt<'_>
|
|||
"removing the guard and adding a `let` inside the match arm"
|
||||
);
|
||||
}
|
||||
LetSource::LetElse(..) => {
|
||||
emit_diag!(
|
||||
lint,
|
||||
"`let...else`",
|
||||
"`else` clause is useless",
|
||||
"removing the `else` clause"
|
||||
);
|
||||
}
|
||||
LetSource::WhileLet => {
|
||||
emit_diag!(
|
||||
lint,
|
||||
|
@ -445,7 +426,6 @@ fn irrefutable_let_pattern(id: HirId, ls: LetSource, span: Span, tcx: TyCtxt<'_>
|
|||
|
||||
fn check_let_reachability<'p, 'tcx>(
|
||||
cx: &mut MatchCheckCtxt<'p, 'tcx>,
|
||||
ls: LetSource,
|
||||
pat_id: HirId,
|
||||
pat: &'p super::Pat<'tcx>,
|
||||
span: Span,
|
||||
|
@ -454,13 +434,13 @@ fn check_let_reachability<'p, 'tcx>(
|
|||
let report = compute_match_usefulness(&cx, &arms, pat_id, pat.ty);
|
||||
|
||||
report_arm_reachability(&cx, &report, |arm_index, arm_span, arm_hir_id, _| {
|
||||
match ls {
|
||||
match let_source(cx.tcx, pat_id) {
|
||||
LetSource::IfLet | LetSource::WhileLet => {
|
||||
match arm_index {
|
||||
// The arm with the user-specified pattern.
|
||||
0 => unreachable_pattern(cx.tcx, arm_span, arm_hir_id, None),
|
||||
// The arm with the wildcard pattern.
|
||||
1 => irrefutable_let_pattern(pat_id, ls, arm_span, cx.tcx),
|
||||
1 => irrefutable_let_pattern(cx.tcx, pat_id, arm_span),
|
||||
_ => bug!(),
|
||||
}
|
||||
}
|
||||
|
@ -473,7 +453,7 @@ fn check_let_reachability<'p, 'tcx>(
|
|||
|
||||
if report.non_exhaustiveness_witnesses.is_empty() {
|
||||
// The match is exhaustive, i.e. the `if let` pattern is irrefutable.
|
||||
irrefutable_let_pattern(pat_id, ls, span, cx.tcx);
|
||||
irrefutable_let_pattern(cx.tcx, pat_id, span);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -787,5 +767,46 @@ pub enum LetSource {
|
|||
GenericLet,
|
||||
IfLet,
|
||||
IfLetGuard,
|
||||
LetElse(Span),
|
||||
WhileLet,
|
||||
}
|
||||
|
||||
fn let_source(tcx: TyCtxt<'_>, pat_id: HirId) -> LetSource {
|
||||
let hir = tcx.hir();
|
||||
let parent = hir.get_parent_node(pat_id);
|
||||
match hir.get(parent) {
|
||||
hir::Node::Arm(hir::Arm {
|
||||
guard: Some(hir::Guard::IfLet(&hir::Pat { hir_id, .. }, _)),
|
||||
..
|
||||
}) if 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);
|
||||
|
||||
let parent_parent_parent = hir.get_parent_node(parent_parent);
|
||||
let parent_parent_parent_parent = hir.get_parent_node(parent_parent_parent);
|
||||
let parent_parent_parent_parent_node = hir.get(parent_parent_parent_parent);
|
||||
|
||||
if let hir::Node::Expr(hir::Expr {
|
||||
kind: hir::ExprKind::Loop(_, _, hir::LoopSource::While, _),
|
||||
..
|
||||
}) = parent_parent_parent_parent_node
|
||||
{
|
||||
LetSource::WhileLet
|
||||
} else if let hir::Node::Expr(hir::Expr { kind: hir::ExprKind::If { .. }, .. }) =
|
||||
parent_parent_node
|
||||
{
|
||||
LetSource::IfLet
|
||||
} else {
|
||||
LetSource::GenericLet
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,65 @@ 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()?;
|
||||
self.check_let_else_init_bool_expr(&init);
|
||||
self.check_let_else_init_trailing_brace(&init);
|
||||
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 }))
|
||||
}
|
||||
|
||||
fn check_let_else_init_bool_expr(&self, init: &ast::Expr) {
|
||||
if let ast::ExprKind::Binary(op, ..) = init.kind {
|
||||
if op.node.lazy() {
|
||||
let suggs = vec![
|
||||
(init.span.shrink_to_lo(), "(".to_string()),
|
||||
(init.span.shrink_to_hi(), ")".to_string()),
|
||||
];
|
||||
self.struct_span_err(
|
||||
init.span,
|
||||
&format!(
|
||||
"a `{}` expression cannot be directly assigned in `let...else`",
|
||||
op.node.to_string()
|
||||
),
|
||||
)
|
||||
.multipart_suggestion(
|
||||
"wrap the expression in parenthesis",
|
||||
suggs,
|
||||
Applicability::MachineApplicable,
|
||||
)
|
||||
.emit();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn check_let_else_init_trailing_brace(&self, init: &ast::Expr) {
|
||||
if let Some(trailing) = classify::expr_trailing_brace(init) {
|
||||
let err_span = trailing.span.with_lo(trailing.span.hi() - BytePos(1));
|
||||
let suggs = vec![
|
||||
(trailing.span.shrink_to_lo(), "(".to_string()),
|
||||
(trailing.span.shrink_to_hi(), ")".to_string()),
|
||||
];
|
||||
self.struct_span_err(
|
||||
err_span,
|
||||
"right curly brace `}` before `else` in a `let...else` statement not allowed",
|
||||
)
|
||||
.multipart_suggestion(
|
||||
"try wrapping the expression in parenthesis",
|
||||
suggs,
|
||||
Applicability::MachineApplicable,
|
||||
)
|
||||
.emit();
|
||||
}
|
||||
}
|
||||
|
||||
/// Parses the RHS of a local variable declaration (e.g., `= 14;`).
|
||||
|
@ -495,13 +553,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<bar, baz>;`. Try to recover.
|
||||
match &mut local.init {
|
||||
Some(ref mut expr) => {
|
||||
self.check_mistyped_turbofish_with_multiple_type_params(e, expr)?;
|
||||
// We found `foo<bar, baz>`, 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<bar, baz>`, have we fully recovered?
|
||||
self.expect_semi()?;
|
||||
}
|
||||
LocalKind::Decl => return Err(e),
|
||||
}
|
||||
eat_semi = false;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -1097,6 +1097,7 @@ pub enum DesugaringKind {
|
|||
Async,
|
||||
Await,
|
||||
ForLoop(ForLoopLoc),
|
||||
LetElse,
|
||||
}
|
||||
|
||||
/// A location in the desugaring of a `for` loop
|
||||
|
@ -1117,6 +1118,7 @@ impl DesugaringKind {
|
|||
DesugaringKind::TryBlock => "`try` block",
|
||||
DesugaringKind::OpaqueTy => "`impl Trait`",
|
||||
DesugaringKind::ForLoop(_) => "`for` loop",
|
||||
DesugaringKind::LetElse => "`let...else`",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -744,6 +744,7 @@ symbols! {
|
|||
le,
|
||||
len,
|
||||
let_chains,
|
||||
let_else,
|
||||
lhs,
|
||||
lib,
|
||||
libc,
|
||||
|
|
|
@ -1928,7 +1928,11 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
|
|||
| ObligationCauseCode::OpaqueType
|
||||
| ObligationCauseCode::MiscObligation
|
||||
| ObligationCauseCode::WellFormed(..)
|
||||
| ObligationCauseCode::MatchImpl(..) => {}
|
||||
| ObligationCauseCode::MatchImpl(..)
|
||||
| ObligationCauseCode::ReturnType
|
||||
| ObligationCauseCode::ReturnValue(_)
|
||||
| ObligationCauseCode::BlockTailExpression(_)
|
||||
| ObligationCauseCode::LetElse => {}
|
||||
ObligationCauseCode::SliceOrArrayElem => {
|
||||
err.note("slice and array elements must have `Sized` type");
|
||||
}
|
||||
|
@ -2338,9 +2342,6 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
|
|||
predicate
|
||||
));
|
||||
}
|
||||
ObligationCauseCode::ReturnType
|
||||
| ObligationCauseCode::ReturnValue(_)
|
||||
| ObligationCauseCode::BlockTailExpression(_) => (),
|
||||
ObligationCauseCode::TrivialBound => {
|
||||
err.help("see issue #48214");
|
||||
if tcx.sess.opts.unstable_features.is_nightly_build() {
|
||||
|
|
|
@ -849,7 +849,26 @@ 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 = self.check_expr_with_expectation(else_expr, expected);
|
||||
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_diverges = self.diverges.get();
|
||||
|
||||
let opt_suggest_box_span =
|
||||
|
|
|
@ -31,11 +31,11 @@ help: use `::<...>` instead of `<...>` to specify type or const arguments
|
|||
LL | (0..13).collect::<Vec<i32>();
|
||||
| ++
|
||||
|
||||
error: expected one of `!`, `.`, `::`, `;`, `?`, `{`, or an operator, found `,`
|
||||
error: expected one of `!`, `.`, `::`, `;`, `?`, `else`, `{`, or an operator, found `,`
|
||||
--> $DIR/issue-40396.rs:11:43
|
||||
|
|
||||
LL | let x = std::collections::HashMap<i128, i128>::new();
|
||||
| ^ expected one of 7 possible tokens
|
||||
| ^ expected one of 8 possible tokens
|
||||
|
|
||||
help: use `::<...>` instead of `<...>` to specify type or const arguments
|
||||
|
|
||||
|
|
5
src/test/ui/feature-gates/feature-gate-let_else.rs
Normal file
5
src/test/ui/feature-gates/feature-gate-let_else.rs
Normal file
|
@ -0,0 +1,5 @@
|
|||
fn main() {
|
||||
let Some(x) = Some(1) else { //~ ERROR `let...else` statements are unstable
|
||||
return;
|
||||
};
|
||||
}
|
14
src/test/ui/feature-gates/feature-gate-let_else.stderr
Normal file
14
src/test/ui/feature-gates/feature-gate-let_else.stderr
Normal file
|
@ -0,0 +1,14 @@
|
|||
error[E0658]: `let...else` statements are unstable
|
||||
--> $DIR/feature-gate-let_else.rs:2:5
|
||||
|
|
||||
LL | / let Some(x) = Some(1) else {
|
||||
LL | | return;
|
||||
LL | | };
|
||||
| |______^
|
||||
|
|
||||
= note: see issue #87335 <https://github.com/rust-lang/rust/issues/87335> for more information
|
||||
= help: add `#![feature(let_else)]` to the crate attributes to enable
|
||||
|
||||
error: aborting due to previous error
|
||||
|
||||
For more information about this error, try `rustc --explain E0658`.
|
8
src/test/ui/let-else/let-else-bool-binop-init.fixed
Normal file
8
src/test/ui/let-else/let-else-bool-binop-init.fixed
Normal file
|
@ -0,0 +1,8 @@
|
|||
// run-rustfix
|
||||
|
||||
#![feature(let_else)]
|
||||
|
||||
fn main() {
|
||||
let true = (true && false) else { return }; //~ ERROR a `&&` expression cannot be directly assigned in `let...else`
|
||||
let true = (true || false) else { return }; //~ ERROR a `||` expression cannot be directly assigned in `let...else`
|
||||
}
|
8
src/test/ui/let-else/let-else-bool-binop-init.rs
Normal file
8
src/test/ui/let-else/let-else-bool-binop-init.rs
Normal file
|
@ -0,0 +1,8 @@
|
|||
// run-rustfix
|
||||
|
||||
#![feature(let_else)]
|
||||
|
||||
fn main() {
|
||||
let true = true && false else { return }; //~ ERROR a `&&` expression cannot be directly assigned in `let...else`
|
||||
let true = true || false else { return }; //~ ERROR a `||` expression cannot be directly assigned in `let...else`
|
||||
}
|
24
src/test/ui/let-else/let-else-bool-binop-init.stderr
Normal file
24
src/test/ui/let-else/let-else-bool-binop-init.stderr
Normal file
|
@ -0,0 +1,24 @@
|
|||
error: a `&&` expression cannot be directly assigned in `let...else`
|
||||
--> $DIR/let-else-bool-binop-init.rs:6:16
|
||||
|
|
||||
LL | let true = true && false else { return };
|
||||
| ^^^^^^^^^^^^^
|
||||
|
|
||||
help: wrap the expression in parenthesis
|
||||
|
|
||||
LL | let true = (true && false) else { return };
|
||||
| + +
|
||||
|
||||
error: a `||` expression cannot be directly assigned in `let...else`
|
||||
--> $DIR/let-else-bool-binop-init.rs:7:16
|
||||
|
|
||||
LL | let true = true || false else { return };
|
||||
| ^^^^^^^^^^^^^
|
||||
|
|
||||
help: wrap the expression in parenthesis
|
||||
|
|
||||
LL | let true = (true || false) else { return };
|
||||
| + +
|
||||
|
||||
error: aborting due to 2 previous errors
|
||||
|
26
src/test/ui/let-else/let-else-brace-before-else.fixed
Normal file
26
src/test/ui/let-else/let-else-brace-before-else.fixed
Normal file
|
@ -0,0 +1,26 @@
|
|||
// run-rustfix
|
||||
|
||||
#![feature(let_else)]
|
||||
|
||||
fn main() {
|
||||
let Some(1) = ({ Some(1) }) else {
|
||||
//~^ ERROR right curly brace `}` before `else` in a `let...else` statement not allowed
|
||||
return;
|
||||
};
|
||||
let Some(1) = (loop { break Some(1) }) else {
|
||||
//~^ ERROR right curly brace `}` before `else` in a `let...else` statement not allowed
|
||||
return;
|
||||
};
|
||||
let 2 = 1 + (match 1 { n => n }) else {
|
||||
//~^ ERROR right curly brace `}` before `else` in a `let...else` statement not allowed
|
||||
return;
|
||||
};
|
||||
let Some(1) = (unsafe { unsafe_fn() }) else {
|
||||
//~^ ERROR right curly brace `}` before `else` in a `let...else` statement not allowed
|
||||
return;
|
||||
};
|
||||
}
|
||||
|
||||
unsafe fn unsafe_fn<T>() -> T {
|
||||
unimplemented!();
|
||||
}
|
26
src/test/ui/let-else/let-else-brace-before-else.rs
Normal file
26
src/test/ui/let-else/let-else-brace-before-else.rs
Normal file
|
@ -0,0 +1,26 @@
|
|||
// run-rustfix
|
||||
|
||||
#![feature(let_else)]
|
||||
|
||||
fn main() {
|
||||
let Some(1) = { Some(1) } else {
|
||||
//~^ ERROR right curly brace `}` before `else` in a `let...else` statement not allowed
|
||||
return;
|
||||
};
|
||||
let Some(1) = loop { break Some(1) } else {
|
||||
//~^ ERROR right curly brace `}` before `else` in a `let...else` statement not allowed
|
||||
return;
|
||||
};
|
||||
let 2 = 1 + match 1 { n => n } else {
|
||||
//~^ ERROR right curly brace `}` before `else` in a `let...else` statement not allowed
|
||||
return;
|
||||
};
|
||||
let Some(1) = unsafe { unsafe_fn() } else {
|
||||
//~^ ERROR right curly brace `}` before `else` in a `let...else` statement not allowed
|
||||
return;
|
||||
};
|
||||
}
|
||||
|
||||
unsafe fn unsafe_fn<T>() -> T {
|
||||
unimplemented!();
|
||||
}
|
46
src/test/ui/let-else/let-else-brace-before-else.stderr
Normal file
46
src/test/ui/let-else/let-else-brace-before-else.stderr
Normal file
|
@ -0,0 +1,46 @@
|
|||
error: right curly brace `}` before `else` in a `let...else` statement not allowed
|
||||
--> $DIR/let-else-brace-before-else.rs:6:29
|
||||
|
|
||||
LL | let Some(1) = { Some(1) } else {
|
||||
| ^
|
||||
|
|
||||
help: try wrapping the expression in parenthesis
|
||||
|
|
||||
LL | let Some(1) = ({ Some(1) }) else {
|
||||
| + +
|
||||
|
||||
error: right curly brace `}` before `else` in a `let...else` statement not allowed
|
||||
--> $DIR/let-else-brace-before-else.rs:10:40
|
||||
|
|
||||
LL | let Some(1) = loop { break Some(1) } else {
|
||||
| ^
|
||||
|
|
||||
help: try wrapping the expression in parenthesis
|
||||
|
|
||||
LL | let Some(1) = (loop { break Some(1) }) else {
|
||||
| + +
|
||||
|
||||
error: right curly brace `}` before `else` in a `let...else` statement not allowed
|
||||
--> $DIR/let-else-brace-before-else.rs:14:34
|
||||
|
|
||||
LL | let 2 = 1 + match 1 { n => n } else {
|
||||
| ^
|
||||
|
|
||||
help: try wrapping the expression in parenthesis
|
||||
|
|
||||
LL | let 2 = 1 + (match 1 { n => n }) else {
|
||||
| + +
|
||||
|
||||
error: right curly brace `}` before `else` in a `let...else` statement not allowed
|
||||
--> $DIR/let-else-brace-before-else.rs:18:40
|
||||
|
|
||||
LL | let Some(1) = unsafe { unsafe_fn() } else {
|
||||
| ^
|
||||
|
|
||||
help: try wrapping the expression in parenthesis
|
||||
|
|
||||
LL | let Some(1) = (unsafe { unsafe_fn() }) else {
|
||||
| + +
|
||||
|
||||
error: aborting due to 4 previous errors
|
||||
|
14
src/test/ui/let-else/let-else-check.rs
Normal file
14
src/test/ui/let-else/let-else-check.rs
Normal file
|
@ -0,0 +1,14 @@
|
|||
#![feature(let_else)]
|
||||
|
||||
#![deny(unused_variables)]
|
||||
|
||||
fn main() {
|
||||
// type annotation, attributes
|
||||
#[allow(unused_variables)]
|
||||
let Some(_): Option<u32> = Some(Default::default()) else {
|
||||
let x = 1; // OK
|
||||
return;
|
||||
};
|
||||
|
||||
let x = 1; //~ ERROR unused variable: `x`
|
||||
}
|
14
src/test/ui/let-else/let-else-check.stderr
Normal file
14
src/test/ui/let-else/let-else-check.stderr
Normal file
|
@ -0,0 +1,14 @@
|
|||
error: unused variable: `x`
|
||||
--> $DIR/let-else-check.rs:13:9
|
||||
|
|
||||
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
|
||||
|
|
||||
LL | #![deny(unused_variables)]
|
||||
| ^^^^^^^^^^^^^^^^
|
||||
|
||||
error: aborting due to previous error
|
||||
|
7
src/test/ui/let-else/let-else-irrefutable.rs
Normal file
7
src/test/ui/let-else/let-else-irrefutable.rs
Normal file
|
@ -0,0 +1,7 @@
|
|||
// check-pass
|
||||
|
||||
#![feature(let_else)]
|
||||
|
||||
fn main() {
|
||||
let x = 1 else { return }; //~ WARN irrefutable `let...else` pattern
|
||||
}
|
12
src/test/ui/let-else/let-else-irrefutable.stderr
Normal file
12
src/test/ui/let-else/let-else-irrefutable.stderr
Normal file
|
@ -0,0 +1,12 @@
|
|||
warning: irrefutable `let...else` pattern
|
||||
--> $DIR/let-else-irrefutable.rs:6:5
|
||||
|
|
||||
LL | let x = 1 else { return };
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: `#[warn(irrefutable_let_patterns)]` on by default
|
||||
= note: this pattern will always match, so the `else` clause is useless
|
||||
= help: consider removing the `else` clause
|
||||
|
||||
warning: 1 warning emitted
|
||||
|
11
src/test/ui/let-else/let-else-missing-semicolon.rs
Normal file
11
src/test/ui/let-else/let-else-missing-semicolon.rs
Normal file
|
@ -0,0 +1,11 @@
|
|||
#![feature(let_else)]
|
||||
|
||||
fn main() {
|
||||
let Some(x) = Some(1) else {
|
||||
return;
|
||||
} //~ ERROR expected `;`, found keyword `let`
|
||||
let _ = "";
|
||||
let Some(x) = Some(1) else {
|
||||
panic!();
|
||||
} //~ ERROR expected `;`, found `}`
|
||||
}
|
18
src/test/ui/let-else/let-else-missing-semicolon.stderr
Normal file
18
src/test/ui/let-else/let-else-missing-semicolon.stderr
Normal file
|
@ -0,0 +1,18 @@
|
|||
error: expected `;`, found keyword `let`
|
||||
--> $DIR/let-else-missing-semicolon.rs:6:6
|
||||
|
|
||||
LL | }
|
||||
| ^ help: add `;` here
|
||||
LL | let _ = "";
|
||||
| --- unexpected token
|
||||
|
||||
error: expected `;`, found `}`
|
||||
--> $DIR/let-else-missing-semicolon.rs:10:6
|
||||
|
|
||||
LL | }
|
||||
| ^ help: add `;` here
|
||||
LL | }
|
||||
| - unexpected token
|
||||
|
||||
error: aborting due to 2 previous errors
|
||||
|
13
src/test/ui/let-else/let-else-non-diverging.rs
Normal file
13
src/test/ui/let-else/let-else-non-diverging.rs
Normal file
|
@ -0,0 +1,13 @@
|
|||
#![feature(let_else)]
|
||||
|
||||
fn main() {
|
||||
let Some(x) = Some(1) else { //~ ERROR does not diverge
|
||||
Some(2)
|
||||
};
|
||||
let Some(x) = Some(1) else { //~ ERROR does not diverge
|
||||
if 1 == 1 {
|
||||
panic!();
|
||||
}
|
||||
};
|
||||
let Some(x) = Some(1) else { Some(2) }; //~ ERROR does not diverge
|
||||
}
|
44
src/test/ui/let-else/let-else-non-diverging.stderr
Normal file
44
src/test/ui/let-else/let-else-non-diverging.stderr
Normal file
|
@ -0,0 +1,44 @@
|
|||
error[E0308]: `else` clause of `let...else` does not diverge
|
||||
--> $DIR/let-else-non-diverging.rs:12:32
|
||||
|
|
||||
LL | let Some(x) = Some(1) else { Some(2) };
|
||||
| ^^^^^^^^^^^ expected `!`, found enum `Option`
|
||||
|
|
||||
= note: expected type `!`
|
||||
found type `Option<{integer}>`
|
||||
= help: try adding a diverging expression, such as `return` or `panic!(..)`
|
||||
= help: ...or use `match` instead of `let...else`
|
||||
|
||||
error[E0308]: `else` clause of `let...else` does not diverge
|
||||
--> $DIR/let-else-non-diverging.rs:7:32
|
||||
|
|
||||
LL | let Some(x) = Some(1) else {
|
||||
| ________________________________^
|
||||
LL | | if 1 == 1 {
|
||||
LL | | panic!();
|
||||
LL | | }
|
||||
LL | | };
|
||||
| |_____^ expected `!`, found `()`
|
||||
|
|
||||
= note: expected type `!`
|
||||
found type `()`
|
||||
= help: try adding a diverging expression, such as `return` or `panic!(..)`
|
||||
= 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
|
||||
|
|
||||
LL | let Some(x) = Some(1) else {
|
||||
| ________________________________^
|
||||
LL | | Some(2)
|
||||
LL | | };
|
||||
| |_____^ expected `!`, found enum `Option`
|
||||
|
|
||||
= note: expected type `!`
|
||||
found type `Option<{integer}>`
|
||||
= help: try adding a diverging expression, such as `return` or `panic!(..)`
|
||||
= help: ...or use `match` instead of `let...else`
|
||||
|
||||
error: aborting due to 3 previous errors
|
||||
|
||||
For more information about this error, try `rustc --explain E0308`.
|
35
src/test/ui/let-else/let-else-run-pass.rs
Normal file
35
src/test/ui/let-else/let-else-run-pass.rs
Normal file
|
@ -0,0 +1,35 @@
|
|||
// run-pass
|
||||
|
||||
#![feature(let_else)]
|
||||
|
||||
fn main() {
|
||||
#[allow(dead_code)]
|
||||
enum MyEnum {
|
||||
A(String),
|
||||
B { f: String },
|
||||
C,
|
||||
}
|
||||
// ref binding to non-copy value and or-pattern
|
||||
let (MyEnum::A(ref x) | MyEnum::B { f: ref x }) = (MyEnum::B { f: String::new() }) else {
|
||||
panic!();
|
||||
};
|
||||
assert_eq!(x, "");
|
||||
|
||||
// nested let-else
|
||||
let mut x = 1;
|
||||
loop {
|
||||
let 4 = x else {
|
||||
let 3 = x else {
|
||||
x += 1;
|
||||
continue;
|
||||
};
|
||||
break;
|
||||
};
|
||||
panic!();
|
||||
}
|
||||
assert_eq!(x, 3);
|
||||
|
||||
// else return
|
||||
let Some(1) = Some(2) else { return };
|
||||
panic!();
|
||||
}
|
7
src/test/ui/let-else/let-else-scope.rs
Normal file
7
src/test/ui/let-else/let-else-scope.rs
Normal file
|
@ -0,0 +1,7 @@
|
|||
#![feature(let_else)]
|
||||
|
||||
fn main() {
|
||||
let Some(x) = Some(2) else {
|
||||
panic!("{}", x); //~ ERROR cannot find value `x` in this scope
|
||||
};
|
||||
}
|
9
src/test/ui/let-else/let-else-scope.stderr
Normal file
9
src/test/ui/let-else/let-else-scope.stderr
Normal file
|
@ -0,0 +1,9 @@
|
|||
error[E0425]: cannot find value `x` in this scope
|
||||
--> $DIR/let-else-scope.rs:5:22
|
||||
|
|
||||
LL | panic!("{}", x);
|
||||
| ^ not found in this scope
|
||||
|
||||
error: aborting due to previous error
|
||||
|
||||
For more information about this error, try `rustc --explain E0425`.
|
|
@ -12,11 +12,11 @@ error: expected expression, found `]`
|
|||
LL | #[cfg(FALSE)] fn e() { let _ = [#[attr]]; }
|
||||
| ^ expected expression
|
||||
|
||||
error: expected one of `!`, `.`, `::`, `;`, `?`, `{`, or an operator, found `#`
|
||||
error: expected one of `!`, `.`, `::`, `;`, `?`, `else`, `{`, or an operator, found `#`
|
||||
--> $DIR/attr-stmt-expr-attr-bad.rs:9:35
|
||||
|
|
||||
LL | #[cfg(FALSE)] fn e() { let _ = foo#[attr](); }
|
||||
| ^ expected one of 7 possible tokens
|
||||
| ^ expected one of 8 possible tokens
|
||||
|
||||
error: an inner attribute is not permitted in this context
|
||||
--> $DIR/attr-stmt-expr-attr-bad.rs:11:36
|
||||
|
@ -70,11 +70,11 @@ LL | #[cfg(FALSE)] fn e() { let _ = -#![attr] 0; }
|
|||
|
|
||||
= note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files. Outer attributes, like `#[test]`, annotate the item following them.
|
||||
|
||||
error: expected one of `!`, `.`, `::`, `;`, `?`, `{`, or an operator, found `#`
|
||||
error: expected one of `!`, `.`, `::`, `;`, `?`, `else`, `{`, or an operator, found `#`
|
||||
--> $DIR/attr-stmt-expr-attr-bad.rs:23:34
|
||||
|
|
||||
LL | #[cfg(FALSE)] fn e() { let _ = x #![attr] as Y; }
|
||||
| ^ expected one of 7 possible tokens
|
||||
| ^ expected one of 8 possible tokens
|
||||
|
||||
error: an inner attribute is not permitted in this context
|
||||
--> $DIR/attr-stmt-expr-attr-bad.rs:25:35
|
||||
|
@ -372,11 +372,11 @@ error: unexpected token: `#`
|
|||
LL | #[cfg(FALSE)] fn e() { let _ = x.#![attr]foo(); }
|
||||
| ^
|
||||
|
||||
error: expected one of `.`, `;`, `?`, or an operator, found `#`
|
||||
error: expected one of `.`, `;`, `?`, `else`, or an operator, found `#`
|
||||
--> $DIR/attr-stmt-expr-attr-bad.rs:100:34
|
||||
|
|
||||
LL | #[cfg(FALSE)] fn e() { let _ = x.#![attr]foo(); }
|
||||
| ^ expected one of `.`, `;`, `?`, or an operator
|
||||
| ^ expected one of `.`, `;`, `?`, `else`, or an operator
|
||||
|
||||
error: unexpected token: `#`
|
||||
--> $DIR/attr-stmt-expr-attr-bad.rs:103:34
|
||||
|
@ -384,11 +384,11 @@ error: unexpected token: `#`
|
|||
LL | #[cfg(FALSE)] fn e() { let _ = x.#[attr]foo(); }
|
||||
| ^
|
||||
|
||||
error: expected one of `.`, `;`, `?`, or an operator, found `#`
|
||||
error: expected one of `.`, `;`, `?`, `else`, or an operator, found `#`
|
||||
--> $DIR/attr-stmt-expr-attr-bad.rs:103:34
|
||||
|
|
||||
LL | #[cfg(FALSE)] fn e() { let _ = x.#[attr]foo(); }
|
||||
| ^ expected one of `.`, `;`, `?`, or an operator
|
||||
| ^ expected one of `.`, `;`, `?`, `else`, or an operator
|
||||
|
||||
error: expected statement after outer attribute
|
||||
--> $DIR/attr-stmt-expr-attr-bad.rs:108:37
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
fn main() {
|
||||
let a = std::process::Command::new("echo")
|
||||
.arg("1")
|
||||
,arg("2") //~ ERROR expected one of `.`, `;`, `?`, or an operator, found `,`
|
||||
,arg("2") //~ ERROR expected one of `.`, `;`, `?`, `else`, or an operator, found `,`
|
||||
.output();
|
||||
}
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
error: expected one of `.`, `;`, `?`, or an operator, found `,`
|
||||
error: expected one of `.`, `;`, `?`, `else`, or an operator, found `,`
|
||||
--> $DIR/issue-72253.rs:4:9
|
||||
|
|
||||
LL | .arg("1")
|
||||
| - expected one of `.`, `;`, `?`, or an operator
|
||||
| - expected one of `.`, `;`, `?`, `else`, or an operator
|
||||
LL | ,arg("2")
|
||||
| ^ unexpected token
|
||||
|
||||
|
|
|
@ -2,8 +2,8 @@ fn main() {
|
|||
let outer_local:e_outer<&str, { let inner_local:e_inner<&str, }
|
||||
//~^ ERROR expected one of `>`, a const expression
|
||||
//~| ERROR expected one of `>`, a const expression, lifetime, or type, found `}`
|
||||
//~| ERROR expected one of `!`, `.`, `::`, `;`, `?`, `{`, or an operator, found `,`
|
||||
//~| ERROR expected one of `!`, `.`, `::`, `;`, `?`, `{`, or an operator, found `,`
|
||||
//~| ERROR expected one of `!`, `.`, `::`, `;`, `?`, `{`, or an operator, found `,`
|
||||
//~| ERROR expected one of `!`, `.`, `::`, `;`, `?`, `else`, `{`, or an operator, found `,`
|
||||
//~| ERROR expected one of `!`, `.`, `::`, `;`, `?`, `else`, `{`, or an operator, found `,`
|
||||
//~| ERROR expected one of `!`, `.`, `::`, `;`, `?`, `else`, `{`, or an operator, found `,`
|
||||
}
|
||||
//~^ ERROR expected one of `,`, `:`, `=`, or `>`, found `}`
|
||||
|
|
|
@ -7,11 +7,11 @@ LL | let outer_local:e_outer<&str, { let inner_local:e_inner<&str, }
|
|||
| | help: use `=` if you meant to assign
|
||||
| while parsing the type for `inner_local`
|
||||
|
||||
error: expected one of `!`, `.`, `::`, `;`, `?`, `{`, or an operator, found `,`
|
||||
error: expected one of `!`, `.`, `::`, `;`, `?`, `else`, `{`, or an operator, found `,`
|
||||
--> $DIR/issue-84117.rs:2:65
|
||||
|
|
||||
LL | let outer_local:e_outer<&str, { let inner_local:e_inner<&str, }
|
||||
| ^ expected one of 7 possible tokens
|
||||
| ^ expected one of 8 possible tokens
|
||||
|
||||
error: expected one of `,`, `:`, `=`, or `>`, found `}`
|
||||
--> $DIR/issue-84117.rs:8:1
|
||||
|
@ -33,17 +33,17 @@ LL | let outer_local:e_outer<&str, { let inner_local:e_inner<&str, }
|
|||
| | help: use `=` if you meant to assign
|
||||
| while parsing the type for `inner_local`
|
||||
|
||||
error: expected one of `!`, `.`, `::`, `;`, `?`, `{`, or an operator, found `,`
|
||||
error: expected one of `!`, `.`, `::`, `;`, `?`, `else`, `{`, or an operator, found `,`
|
||||
--> $DIR/issue-84117.rs:2:65
|
||||
|
|
||||
LL | let outer_local:e_outer<&str, { let inner_local:e_inner<&str, }
|
||||
| ^ expected one of 7 possible tokens
|
||||
| ^ expected one of 8 possible tokens
|
||||
|
||||
error: expected one of `!`, `.`, `::`, `;`, `?`, `{`, or an operator, found `,`
|
||||
error: expected one of `!`, `.`, `::`, `;`, `?`, `else`, `{`, or an operator, found `,`
|
||||
--> $DIR/issue-84117.rs:2:33
|
||||
|
|
||||
LL | let outer_local:e_outer<&str, { let inner_local:e_inner<&str, }
|
||||
| ^ expected one of 7 possible tokens
|
||||
| ^ expected one of 8 possible tokens
|
||||
|
||||
error: aborting due to 6 previous errors
|
||||
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
error: expected one of `.`, `;`, `?`, or an operator, found `""`
|
||||
error: expected one of `.`, `;`, `?`, `else`, or an operator, found `""`
|
||||
--> $DIR/issue-37234.rs:3:19
|
||||
|
|
||||
LL | let x = 5 "";
|
||||
| ^^ expected one of `.`, `;`, `?`, or an operator
|
||||
| ^^ expected one of `.`, `;`, `?`, `else`, or an operator
|
||||
...
|
||||
LL | failed!();
|
||||
| ---------- in this macro invocation
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
macro_rules! m {
|
||||
($($e1:expr),*; $($e2:expr),*) => {
|
||||
$( let x = $e1 )*; //~ ERROR expected one of `.`, `;`, `?`, or
|
||||
$( let x = $e1 )*; //~ ERROR expected one of `.`, `;`, `?`, `else`, or
|
||||
$( println!("{}", $e2) )*;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
error: expected one of `.`, `;`, `?`, or an operator, found keyword `let`
|
||||
error: expected one of `.`, `;`, `?`, `else`, or an operator, found keyword `let`
|
||||
--> $DIR/missing-semicolon.rs:3:12
|
||||
|
|
||||
LL | $( let x = $e1 )*;
|
||||
| ^^^ expected one of `.`, `;`, `?`, or an operator
|
||||
| ^^^ expected one of `.`, `;`, `?`, `else`, or an operator
|
||||
...
|
||||
LL | fn main() { m!(0, 0; 0, 0); }
|
||||
| --------------- in this macro invocation
|
||||
|
|
|
@ -2,5 +2,5 @@
|
|||
|
||||
pub fn main() {
|
||||
let r = 1..2..3;
|
||||
//~^ ERROR expected one of `.`, `;`, `?`, or an operator, found `..`
|
||||
//~^ ERROR expected one of `.`, `;`, `?`, `else`, or an operator, found `..`
|
||||
}
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
error: expected one of `.`, `;`, `?`, or an operator, found `..`
|
||||
error: expected one of `.`, `;`, `?`, `else`, or an operator, found `..`
|
||||
--> $DIR/range-3.rs:4:17
|
||||
|
|
||||
LL | let r = 1..2..3;
|
||||
| ^^ expected one of `.`, `;`, `?`, or an operator
|
||||
| ^^ expected one of `.`, `;`, `?`, `else`, or an operator
|
||||
|
||||
error: aborting due to previous error
|
||||
|
||||
|
|
|
@ -2,5 +2,5 @@
|
|||
|
||||
pub fn main() {
|
||||
let r = ..1..2;
|
||||
//~^ ERROR expected one of `.`, `;`, `?`, or an operator, found `..`
|
||||
//~^ ERROR expected one of `.`, `;`, `?`, `else`, or an operator, found `..`
|
||||
}
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
error: expected one of `.`, `;`, `?`, or an operator, found `..`
|
||||
error: expected one of `.`, `;`, `?`, `else`, or an operator, found `..`
|
||||
--> $DIR/range-4.rs:4:16
|
||||
|
|
||||
LL | let r = ..1..2;
|
||||
| ^^ expected one of `.`, `;`, `?`, or an operator
|
||||
| ^^ expected one of `.`, `;`, `?`, `else`, or an operator
|
||||
|
||||
error: aborting due to previous error
|
||||
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
#![feature(let_else)]
|
||||
|
||||
#![deny(unreachable_patterns)]
|
||||
|
||||
fn main() {
|
||||
|
@ -53,4 +55,5 @@ fn main() {
|
|||
1..=2 => {}, //~ ERROR unreachable pattern
|
||||
_ => {},
|
||||
}
|
||||
let (0 | 0) = 0 else { return }; //~ ERROR unreachable pattern
|
||||
}
|
||||
|
|
|
@ -1,68 +1,74 @@
|
|||
error: unreachable pattern
|
||||
--> $DIR/top-level-alternation.rs:4:23
|
||||
--> $DIR/top-level-alternation.rs:6:23
|
||||
|
|
||||
LL | while let 0..=2 | 1 = 0 {}
|
||||
| ^
|
||||
|
|
||||
note: the lint level is defined here
|
||||
--> $DIR/top-level-alternation.rs:1:9
|
||||
--> $DIR/top-level-alternation.rs:3:9
|
||||
|
|
||||
LL | #![deny(unreachable_patterns)]
|
||||
| ^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: unreachable pattern
|
||||
--> $DIR/top-level-alternation.rs:5:20
|
||||
--> $DIR/top-level-alternation.rs:7:20
|
||||
|
|
||||
LL | if let 0..=2 | 1 = 0 {}
|
||||
| ^
|
||||
|
||||
error: unreachable pattern
|
||||
--> $DIR/top-level-alternation.rs:9:15
|
||||
--> $DIR/top-level-alternation.rs:11:15
|
||||
|
|
||||
LL | | 0 => {}
|
||||
| ^
|
||||
|
||||
error: unreachable pattern
|
||||
--> $DIR/top-level-alternation.rs:14:15
|
||||
--> $DIR/top-level-alternation.rs:16:15
|
||||
|
|
||||
LL | | Some(0) => {}
|
||||
| ^^^^^^^
|
||||
|
||||
error: unreachable pattern
|
||||
--> $DIR/top-level-alternation.rs:19:9
|
||||
--> $DIR/top-level-alternation.rs:21:9
|
||||
|
|
||||
LL | (0, 0) => {}
|
||||
| ^^^^^^
|
||||
|
||||
error: unreachable pattern
|
||||
--> $DIR/top-level-alternation.rs:39:9
|
||||
--> $DIR/top-level-alternation.rs:41:9
|
||||
|
|
||||
LL | _ => {}
|
||||
| ^
|
||||
|
||||
error: unreachable pattern
|
||||
--> $DIR/top-level-alternation.rs:43:9
|
||||
--> $DIR/top-level-alternation.rs:45:9
|
||||
|
|
||||
LL | Some(_) => {}
|
||||
| ^^^^^^^
|
||||
|
||||
error: unreachable pattern
|
||||
--> $DIR/top-level-alternation.rs:44:9
|
||||
--> $DIR/top-level-alternation.rs:46:9
|
||||
|
|
||||
LL | None => {}
|
||||
| ^^^^
|
||||
|
||||
error: unreachable pattern
|
||||
--> $DIR/top-level-alternation.rs:49:9
|
||||
--> $DIR/top-level-alternation.rs:51:9
|
||||
|
|
||||
LL | None | Some(_) => {}
|
||||
| ^^^^^^^^^^^^^^
|
||||
|
||||
error: unreachable pattern
|
||||
--> $DIR/top-level-alternation.rs:53:9
|
||||
--> $DIR/top-level-alternation.rs:55:9
|
||||
|
|
||||
LL | 1..=2 => {},
|
||||
| ^^^^^
|
||||
|
||||
error: aborting due to 10 previous errors
|
||||
error: unreachable pattern
|
||||
--> $DIR/top-level-alternation.rs:58:14
|
||||
|
|
||||
LL | let (0 | 0) = 0 else { return };
|
||||
| ^
|
||||
|
||||
error: aborting due to 11 previous errors
|
||||
|
||||
|
|
|
@ -316,8 +316,11 @@ impl<'a, 'b> SimilarNamesLocalVisitor<'a, 'b> {
|
|||
|
||||
impl<'a, 'tcx> Visitor<'tcx> for SimilarNamesLocalVisitor<'a, 'tcx> {
|
||||
fn visit_local(&mut self, local: &'tcx Local) {
|
||||
if let Some(ref init) = local.init {
|
||||
self.apply(|this| walk_expr(this, &**init));
|
||||
if let Some((init, els)) = &local.kind.init_else_opt() {
|
||||
self.apply(|this| walk_expr(this, init));
|
||||
if let Some(els) = els {
|
||||
self.apply(|this| walk_block(this, els));
|
||||
}
|
||||
}
|
||||
// add the pattern after the expression because the bindings aren't available
|
||||
// yet in the init
|
||||
|
|
|
@ -221,7 +221,7 @@ pub fn eq_stmt(l: &Stmt, r: &Stmt) -> bool {
|
|||
(Local(l), Local(r)) => {
|
||||
eq_pat(&l.pat, &r.pat)
|
||||
&& both(&l.ty, &r.ty, |l, r| eq_ty(l, r))
|
||||
&& eq_expr_opt(&l.init, &r.init)
|
||||
&& eq_local_kind(&l.kind, &r.kind)
|
||||
&& over(&l.attrs, &r.attrs, |l, r| eq_attr(l, r))
|
||||
},
|
||||
(Item(l), Item(r)) => eq_item(l, r, eq_item_kind),
|
||||
|
@ -234,6 +234,16 @@ pub fn eq_stmt(l: &Stmt, r: &Stmt) -> bool {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn eq_local_kind(l: &LocalKind, r: &LocalKind) -> bool {
|
||||
use LocalKind::*;
|
||||
match (l, r) {
|
||||
(Decl, Decl) => true,
|
||||
(Init(l), Init(r)) => eq_expr(l, r),
|
||||
(InitElse(li, le), InitElse(ri, re)) => eq_expr(li, ri) && eq_block(le, re),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn eq_item<K>(l: &Item<K>, r: &Item<K>, mut eq_kind: impl FnMut(&K, &K) -> bool) -> bool {
|
||||
eq_id(l.ident, r.ident)
|
||||
&& over(&l.attrs, &r.attrs, |l, r| eq_attr(l, r))
|
||||
|
|
|
@ -48,7 +48,7 @@ impl Rewrite for ast::Local {
|
|||
|
||||
skip_out_of_file_lines_range!(context, self.span);
|
||||
|
||||
if contains_skip(&self.attrs) {
|
||||
if contains_skip(&self.attrs) || matches!(self.kind, ast::LocalKind::InitElse(..)) {
|
||||
return None;
|
||||
}
|
||||
|
||||
|
@ -97,7 +97,7 @@ impl Rewrite for ast::Local {
|
|||
infix.push_str(&rewrite);
|
||||
}
|
||||
|
||||
if self.init.is_some() {
|
||||
if self.kind.init().is_some() {
|
||||
infix.push_str(" =");
|
||||
}
|
||||
|
||||
|
@ -106,11 +106,12 @@ impl Rewrite for ast::Local {
|
|||
|
||||
result.push_str(&infix);
|
||||
|
||||
if let Some(ref ex) = self.init {
|
||||
if let Some((init, _els)) = self.kind.init_else_opt() {
|
||||
// 1 = trailing semicolon;
|
||||
let nested_shape = shape.sub_width(1)?;
|
||||
|
||||
result = rewrite_assign_rhs(context, result, &**ex, nested_shape)?;
|
||||
result = rewrite_assign_rhs(context, result, init, nested_shape)?;
|
||||
// todo else
|
||||
}
|
||||
|
||||
result.push(';');
|
||||
|
|
3
src/tools/rustfmt/tests/source/let_else.rs
Normal file
3
src/tools/rustfmt/tests/source/let_else.rs
Normal file
|
@ -0,0 +1,3 @@
|
|||
fn main() {
|
||||
let Some(1) = Some(1) else { return };
|
||||
}
|
3
src/tools/rustfmt/tests/target/let_else.rs
Normal file
3
src/tools/rustfmt/tests/target/let_else.rs
Normal file
|
@ -0,0 +1,3 @@
|
|||
fn main() {
|
||||
let Some(1) = Some(1) else { return };
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue