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 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
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -23,3 +23,30 @@ pub fn expr_requires_semi_to_be_stmt(e: &ast::Expr) -> bool {
|
||||||
| ast::ExprKind::TryBlock(..)
|
| 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);
|
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) {
|
||||||
|
|
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_span::{Span, DUMMY_SP};
|
||||||
use rustc_target::spec::abi::Abi;
|
use rustc_target::spec::abi::Abi;
|
||||||
|
|
||||||
use smallvec::{smallvec, SmallVec};
|
use smallvec::SmallVec;
|
||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
use std::mem;
|
use std::mem;
|
||||||
use tracing::{debug, trace};
|
use tracing::{debug, trace};
|
||||||
|
@ -77,6 +77,7 @@ macro_rules! arena_vec {
|
||||||
}
|
}
|
||||||
|
|
||||||
mod asm;
|
mod asm;
|
||||||
|
mod block;
|
||||||
mod expr;
|
mod expr;
|
||||||
mod item;
|
mod item;
|
||||||
mod pat;
|
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] {
|
fn lower_fn_params_to_names(&mut self, decl: &FnDecl) -> &'hir [Ident] {
|
||||||
// Skip the `...` (`CVarArgs`) trailing arguments from the AST,
|
// Skip the `...` (`CVarArgs`) trailing arguments from the AST,
|
||||||
// as they are not explicit in HIR/Ty function signatures.
|
// 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()))
|
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
|
/// Lowers a block directly to an expression, presuming that it
|
||||||
/// has no attributes and is not targeted by a `break`.
|
/// has no attributes and is not targeted by a `break`.
|
||||||
fn lower_block_expr(&mut self, b: &Block) -> hir::Expr<'hir> {
|
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 {
|
fn lower_unsafe_source(&mut self, u: UnsafeSource) -> hir::UnsafeSource {
|
||||||
match u {
|
match u {
|
||||||
CompilerGenerated => hir::UnsafeSource::CompilerGenerated,
|
CompilerGenerated => hir::UnsafeSource::CompilerGenerated,
|
||||||
|
|
|
@ -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) => {
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -676,6 +676,9 @@ declare_features! (
|
||||||
/// Allows additional const parameter types, such as `&'static str` or user defined types
|
/// Allows additional const parameter types, such as `&'static str` or user defined types
|
||||||
(incomplete, adt_const_params, "1.56.0", Some(44580), None),
|
(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
|
// 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"),
|
IfExpression { .. } => Error0308("`if` and `else` have incompatible types"),
|
||||||
IfExpressionWithNoElse => Error0317("`if` may be missing an `else` clause"),
|
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"),
|
MainFunctionType => Error0580("`main` function has wrong type"),
|
||||||
StartFunctionType => Error0308("`#[start]` function has wrong type"),
|
StartFunctionType => Error0308("`#[start]` function has wrong type"),
|
||||||
IntrinsicType => Error0308("intrinsic has wrong type"),
|
IntrinsicType => Error0308("intrinsic has wrong type"),
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use crate::Lint;
|
use crate::Lint;
|
||||||
use crate::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintContext};
|
use crate::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintContext};
|
||||||
use rustc_ast as ast;
|
use rustc_ast as ast;
|
||||||
use rustc_ast::util::parser;
|
use rustc_ast::util::{classify, parser};
|
||||||
use rustc_ast::{ExprKind, StmtKind};
|
use rustc_ast::{ExprKind, StmtKind};
|
||||||
use rustc_ast_pretty::pprust;
|
use rustc_ast_pretty::pprust;
|
||||||
use rustc_errors::{pluralize, Applicability};
|
use rustc_errors::{pluralize, Applicability};
|
||||||
|
@ -382,6 +382,7 @@ enum UnusedDelimsCtx {
|
||||||
FunctionArg,
|
FunctionArg,
|
||||||
MethodArg,
|
MethodArg,
|
||||||
AssignedValue,
|
AssignedValue,
|
||||||
|
AssignedValueLetElse,
|
||||||
IfCond,
|
IfCond,
|
||||||
WhileCond,
|
WhileCond,
|
||||||
ForIterExpr,
|
ForIterExpr,
|
||||||
|
@ -398,7 +399,9 @@ impl From<UnusedDelimsCtx> for &'static str {
|
||||||
match ctx {
|
match ctx {
|
||||||
UnusedDelimsCtx::FunctionArg => "function argument",
|
UnusedDelimsCtx::FunctionArg => "function argument",
|
||||||
UnusedDelimsCtx::MethodArg => "method argument",
|
UnusedDelimsCtx::MethodArg => "method argument",
|
||||||
UnusedDelimsCtx::AssignedValue => "assigned value",
|
UnusedDelimsCtx::AssignedValue | UnusedDelimsCtx::AssignedValueLetElse => {
|
||||||
|
"assigned value"
|
||||||
|
}
|
||||||
UnusedDelimsCtx::IfCond => "`if` condition",
|
UnusedDelimsCtx::IfCond => "`if` condition",
|
||||||
UnusedDelimsCtx::WhileCond => "`while` condition",
|
UnusedDelimsCtx::WhileCond => "`while` condition",
|
||||||
UnusedDelimsCtx::ForIterExpr => "`for` iterator expression",
|
UnusedDelimsCtx::ForIterExpr => "`for` iterator expression",
|
||||||
|
@ -441,14 +444,26 @@ trait UnusedDelimLint {
|
||||||
right_pos: Option<BytePos>,
|
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) }`
|
// Prevent false-positives in cases like `fn x() -> u8 { ({ 0 } + 1) }`
|
||||||
let lhs_needs_parens = {
|
let lhs_needs_parens = {
|
||||||
let mut innermost = inner;
|
let mut innermost = inner;
|
||||||
loop {
|
loop {
|
||||||
if let ExprKind::Binary(_, lhs, _rhs) = &innermost.kind {
|
if let ExprKind::Binary(_, lhs, _rhs) = &innermost.kind {
|
||||||
innermost = lhs;
|
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;
|
break true;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -618,15 +633,12 @@ 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((init, els)) = local.kind.init_else_opt() {
|
||||||
self.check_unused_delims_expr(
|
let ctx = match els {
|
||||||
cx,
|
None => UnusedDelimsCtx::AssignedValue,
|
||||||
&value,
|
Some(_) => UnusedDelimsCtx::AssignedValueLetElse,
|
||||||
UnusedDelimsCtx::AssignedValue,
|
};
|
||||||
false,
|
self.check_unused_delims_expr(cx, init, ctx, false, None, None);
|
||||||
None,
|
|
||||||
None,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
StmtKind::Expr(ref expr) => {
|
StmtKind::Expr(ref expr) => {
|
||||||
|
@ -702,7 +714,8 @@ impl UnusedDelimLint for UnusedParens {
|
||||||
) {
|
) {
|
||||||
match value.kind {
|
match value.kind {
|
||||||
ast::ExprKind::Paren(ref inner) => {
|
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.attrs.is_empty()
|
||||||
&& !value.span.from_expansion()
|
&& !value.span.from_expansion()
|
||||||
&& (ctx != UnusedDelimsCtx::LetScrutineeExpr
|
&& (ctx != UnusedDelimsCtx::LetScrutineeExpr
|
||||||
|
@ -941,7 +954,7 @@ impl UnusedDelimLint for UnusedBraces {
|
||||||
// FIXME(const_generics): handle paths when #67075 is fixed.
|
// FIXME(const_generics): handle paths when #67075 is fixed.
|
||||||
if let [stmt] = inner.stmts.as_slice() {
|
if let [stmt] = inner.stmts.as_slice() {
|
||||||
if let ast::StmtKind::Expr(ref expr) = stmt.kind {
|
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
|
&& (ctx != UnusedDelimsCtx::AnonConst
|
||||||
|| matches!(expr.kind, ast::ExprKind::Lit(_)))
|
|| matches!(expr.kind, ast::ExprKind::Lit(_)))
|
||||||
&& !cx.sess().source_map().is_multiline(value.span)
|
&& !cx.sess().source_map().is_multiline(value.span)
|
||||||
|
|
|
@ -305,6 +305,9 @@ pub enum ObligationCauseCode<'tcx> {
|
||||||
/// Intrinsic has wrong type
|
/// Intrinsic has wrong type
|
||||||
IntrinsicType,
|
IntrinsicType,
|
||||||
|
|
||||||
|
/// A let else block does not diverge
|
||||||
|
LetElse,
|
||||||
|
|
||||||
/// Method receiver
|
/// Method receiver
|
||||||
MethodReceiver,
|
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::BINDINGS_WITH_VARIANT_NAME;
|
||||||
use rustc_session::lint::builtin::{IRREFUTABLE_LET_PATTERNS, UNREACHABLE_PATTERNS};
|
use rustc_session::lint::builtin::{IRREFUTABLE_LET_PATTERNS, UNREACHABLE_PATTERNS};
|
||||||
use rustc_session::Session;
|
use rustc_session::Session;
|
||||||
use rustc_span::Span;
|
use rustc_span::{DesugaringKind, ExpnKind, Span};
|
||||||
use std::slice;
|
use std::slice;
|
||||||
|
|
||||||
crate fn check_match(tcx: TyCtxt<'_>, def_id: DefId) {
|
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);
|
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>(
|
fn lower_pattern<'p>(
|
||||||
&self,
|
&self,
|
||||||
cx: &mut MatchCheckCtxt<'p, 'tcx>,
|
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) {
|
fn check_let(&mut self, pat: &'tcx hir::Pat<'tcx>, expr: &hir::Expr<'_>, span: Span) {
|
||||||
self.check_patterns(pat);
|
self.check_patterns(pat);
|
||||||
let ls = self.let_source(pat, expr);
|
|
||||||
let mut cx = self.new_cx(expr.hir_id);
|
let mut cx = self.new_cx(expr.hir_id);
|
||||||
let tpat = self.lower_pattern(&mut cx, pat, &mut false).0;
|
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(
|
fn check_match(
|
||||||
|
@ -192,13 +166,7 @@ impl<'tcx> MatchVisitor<'_, 'tcx> {
|
||||||
if let Some(hir::Guard::IfLet(ref pat, _)) = arm.guard {
|
if let Some(hir::Guard::IfLet(ref pat, _)) = arm.guard {
|
||||||
self.check_patterns(pat);
|
self.check_patterns(pat);
|
||||||
let tpat = self.lower_pattern(&mut cx, pat, &mut false).0;
|
let tpat = self.lower_pattern(&mut cx, pat, &mut false).0;
|
||||||
check_let_reachability(
|
check_let_reachability(&mut cx, pat.hir_id, &tpat, tpat.span);
|
||||||
&mut cx,
|
|
||||||
LetSource::IfLetGuard,
|
|
||||||
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 {
|
macro_rules! emit_diag {
|
||||||
(
|
(
|
||||||
$lint:expr,
|
$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 => {
|
LetSource::GenericLet => {
|
||||||
emit_diag!(lint, "`let`", "`let` is useless", "removing `let`");
|
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"
|
"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 => {
|
LetSource::WhileLet => {
|
||||||
emit_diag!(
|
emit_diag!(
|
||||||
lint,
|
lint,
|
||||||
|
@ -445,7 +426,6 @@ fn irrefutable_let_pattern(id: HirId, ls: LetSource, span: Span, tcx: TyCtxt<'_>
|
||||||
|
|
||||||
fn check_let_reachability<'p, 'tcx>(
|
fn check_let_reachability<'p, 'tcx>(
|
||||||
cx: &mut MatchCheckCtxt<'p, 'tcx>,
|
cx: &mut MatchCheckCtxt<'p, 'tcx>,
|
||||||
ls: LetSource,
|
|
||||||
pat_id: HirId,
|
pat_id: HirId,
|
||||||
pat: &'p super::Pat<'tcx>,
|
pat: &'p super::Pat<'tcx>,
|
||||||
span: Span,
|
span: Span,
|
||||||
|
@ -454,13 +434,13 @@ fn check_let_reachability<'p, 'tcx>(
|
||||||
let report = compute_match_usefulness(&cx, &arms, pat_id, pat.ty);
|
let report = compute_match_usefulness(&cx, &arms, pat_id, pat.ty);
|
||||||
|
|
||||||
report_arm_reachability(&cx, &report, |arm_index, arm_span, arm_hir_id, _| {
|
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 => {
|
LetSource::IfLet | LetSource::WhileLet => {
|
||||||
match arm_index {
|
match arm_index {
|
||||||
// The arm with the user-specified pattern.
|
// The arm with the user-specified pattern.
|
||||||
0 => unreachable_pattern(cx.tcx, arm_span, arm_hir_id, None),
|
0 => unreachable_pattern(cx.tcx, arm_span, arm_hir_id, None),
|
||||||
// The arm with the wildcard pattern.
|
// 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!(),
|
_ => bug!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -473,7 +453,7 @@ fn check_let_reachability<'p, 'tcx>(
|
||||||
|
|
||||||
if report.non_exhaustiveness_witnesses.is_empty() {
|
if report.non_exhaustiveness_witnesses.is_empty() {
|
||||||
// The match is exhaustive, i.e. the `if let` pattern is irrefutable.
|
// 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,
|
GenericLet,
|
||||||
IfLet,
|
IfLet,
|
||||||
IfLetGuard,
|
IfLetGuard,
|
||||||
|
LetElse(Span),
|
||||||
WhileLet,
|
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::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,65 @@ 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()?;
|
||||||
|
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 };
|
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;`).
|
/// Parses the RHS of a local variable declaration (e.g., `= 14;`).
|
||||||
|
@ -495,13 +553,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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -1097,6 +1097,7 @@ pub enum DesugaringKind {
|
||||||
Async,
|
Async,
|
||||||
Await,
|
Await,
|
||||||
ForLoop(ForLoopLoc),
|
ForLoop(ForLoopLoc),
|
||||||
|
LetElse,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A location in the desugaring of a `for` loop
|
/// A location in the desugaring of a `for` loop
|
||||||
|
@ -1117,6 +1118,7 @@ impl DesugaringKind {
|
||||||
DesugaringKind::TryBlock => "`try` block",
|
DesugaringKind::TryBlock => "`try` block",
|
||||||
DesugaringKind::OpaqueTy => "`impl Trait`",
|
DesugaringKind::OpaqueTy => "`impl Trait`",
|
||||||
DesugaringKind::ForLoop(_) => "`for` loop",
|
DesugaringKind::ForLoop(_) => "`for` loop",
|
||||||
|
DesugaringKind::LetElse => "`let...else`",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -744,6 +744,7 @@ symbols! {
|
||||||
le,
|
le,
|
||||||
len,
|
len,
|
||||||
let_chains,
|
let_chains,
|
||||||
|
let_else,
|
||||||
lhs,
|
lhs,
|
||||||
lib,
|
lib,
|
||||||
libc,
|
libc,
|
||||||
|
|
|
@ -1928,7 +1928,11 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
|
||||||
| ObligationCauseCode::OpaqueType
|
| ObligationCauseCode::OpaqueType
|
||||||
| ObligationCauseCode::MiscObligation
|
| ObligationCauseCode::MiscObligation
|
||||||
| ObligationCauseCode::WellFormed(..)
|
| ObligationCauseCode::WellFormed(..)
|
||||||
| ObligationCauseCode::MatchImpl(..) => {}
|
| ObligationCauseCode::MatchImpl(..)
|
||||||
|
| ObligationCauseCode::ReturnType
|
||||||
|
| ObligationCauseCode::ReturnValue(_)
|
||||||
|
| ObligationCauseCode::BlockTailExpression(_)
|
||||||
|
| ObligationCauseCode::LetElse => {}
|
||||||
ObligationCauseCode::SliceOrArrayElem => {
|
ObligationCauseCode::SliceOrArrayElem => {
|
||||||
err.note("slice and array elements must have `Sized` type");
|
err.note("slice and array elements must have `Sized` type");
|
||||||
}
|
}
|
||||||
|
@ -2338,9 +2342,6 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
|
||||||
predicate
|
predicate
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
ObligationCauseCode::ReturnType
|
|
||||||
| ObligationCauseCode::ReturnValue(_)
|
|
||||||
| ObligationCauseCode::BlockTailExpression(_) => (),
|
|
||||||
ObligationCauseCode::TrivialBound => {
|
ObligationCauseCode::TrivialBound => {
|
||||||
err.help("see issue #48214");
|
err.help("see issue #48214");
|
||||||
if tcx.sess.opts.unstable_features.is_nightly_build() {
|
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);
|
coerce.coerce(self, &self.misc(sp), then_expr, then_ty);
|
||||||
|
|
||||||
if let Some(else_expr) = opt_else_expr {
|
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 else_diverges = self.diverges.get();
|
||||||
|
|
||||||
let opt_suggest_box_span =
|
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>();
|
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
|
--> $DIR/issue-40396.rs:11:43
|
||||||
|
|
|
|
||||||
LL | let x = std::collections::HashMap<i128, i128>::new();
|
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
|
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]]; }
|
LL | #[cfg(FALSE)] fn e() { let _ = [#[attr]]; }
|
||||||
| ^ expected expression
|
| ^ 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
|
--> $DIR/attr-stmt-expr-attr-bad.rs:9:35
|
||||||
|
|
|
|
||||||
LL | #[cfg(FALSE)] fn e() { let _ = foo#[attr](); }
|
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
|
error: an inner attribute is not permitted in this context
|
||||||
--> $DIR/attr-stmt-expr-attr-bad.rs:11:36
|
--> $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.
|
= 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
|
--> $DIR/attr-stmt-expr-attr-bad.rs:23:34
|
||||||
|
|
|
|
||||||
LL | #[cfg(FALSE)] fn e() { let _ = x #![attr] as Y; }
|
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
|
error: an inner attribute is not permitted in this context
|
||||||
--> $DIR/attr-stmt-expr-attr-bad.rs:25:35
|
--> $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(); }
|
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
|
--> $DIR/attr-stmt-expr-attr-bad.rs:100:34
|
||||||
|
|
|
|
||||||
LL | #[cfg(FALSE)] fn e() { let _ = x.#![attr]foo(); }
|
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: `#`
|
error: unexpected token: `#`
|
||||||
--> $DIR/attr-stmt-expr-attr-bad.rs:103:34
|
--> $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(); }
|
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
|
--> $DIR/attr-stmt-expr-attr-bad.rs:103:34
|
||||||
|
|
|
|
||||||
LL | #[cfg(FALSE)] fn e() { let _ = x.#[attr]foo(); }
|
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
|
error: expected statement after outer attribute
|
||||||
--> $DIR/attr-stmt-expr-attr-bad.rs:108:37
|
--> $DIR/attr-stmt-expr-attr-bad.rs:108:37
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
fn main() {
|
fn main() {
|
||||||
let a = std::process::Command::new("echo")
|
let a = std::process::Command::new("echo")
|
||||||
.arg("1")
|
.arg("1")
|
||||||
,arg("2") //~ ERROR expected one of `.`, `;`, `?`, or an operator, found `,`
|
,arg("2") //~ ERROR expected one of `.`, `;`, `?`, `else`, or an operator, found `,`
|
||||||
.output();
|
.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
|
--> $DIR/issue-72253.rs:4:9
|
||||||
|
|
|
|
||||||
LL | .arg("1")
|
LL | .arg("1")
|
||||||
| - expected one of `.`, `;`, `?`, or an operator
|
| - expected one of `.`, `;`, `?`, `else`, or an operator
|
||||||
LL | ,arg("2")
|
LL | ,arg("2")
|
||||||
| ^ unexpected token
|
| ^ unexpected token
|
||||||
|
|
||||||
|
|
|
@ -2,8 +2,8 @@ fn main() {
|
||||||
let outer_local:e_outer<&str, { let inner_local:e_inner<&str, }
|
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
|
||||||
//~| ERROR expected one of `>`, a const expression, lifetime, or type, found `}`
|
//~| ERROR expected one of `>`, a const expression, lifetime, or type, found `}`
|
||||||
//~| ERROR expected one of `!`, `.`, `::`, `;`, `?`, `{`, or an operator, found `,`
|
//~| ERROR expected one of `!`, `.`, `::`, `;`, `?`, `else`, `{`, 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 `!`, `.`, `::`, `;`, `?`, `{`, or an operator, found `,`
|
//~| ERROR expected one of `!`, `.`, `::`, `;`, `?`, `else`, `{`, or an operator, found `,`
|
||||||
}
|
}
|
||||||
//~^ ERROR expected one of `,`, `:`, `=`, or `>`, 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
|
| | help: use `=` if you meant to assign
|
||||||
| while parsing the type for `inner_local`
|
| 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
|
--> $DIR/issue-84117.rs:2:65
|
||||||
|
|
|
|
||||||
LL | let outer_local:e_outer<&str, { let inner_local:e_inner<&str, }
|
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 `}`
|
error: expected one of `,`, `:`, `=`, or `>`, found `}`
|
||||||
--> $DIR/issue-84117.rs:8:1
|
--> $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
|
| | help: use `=` if you meant to assign
|
||||||
| while parsing the type for `inner_local`
|
| 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
|
--> $DIR/issue-84117.rs:2:65
|
||||||
|
|
|
|
||||||
LL | let outer_local:e_outer<&str, { let inner_local:e_inner<&str, }
|
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
|
--> $DIR/issue-84117.rs:2:33
|
||||||
|
|
|
|
||||||
LL | let outer_local:e_outer<&str, { let inner_local:e_inner<&str, }
|
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
|
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
|
--> $DIR/issue-37234.rs:3:19
|
||||||
|
|
|
|
||||||
LL | let x = 5 "";
|
LL | let x = 5 "";
|
||||||
| ^^ expected one of `.`, `;`, `?`, or an operator
|
| ^^ expected one of `.`, `;`, `?`, `else`, or an operator
|
||||||
...
|
...
|
||||||
LL | failed!();
|
LL | failed!();
|
||||||
| ---------- in this macro invocation
|
| ---------- in this macro invocation
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
macro_rules! m {
|
macro_rules! m {
|
||||||
($($e1:expr),*; $($e2:expr),*) => {
|
($($e1:expr),*; $($e2:expr),*) => {
|
||||||
$( let x = $e1 )*; //~ ERROR expected one of `.`, `;`, `?`, or
|
$( let x = $e1 )*; //~ ERROR expected one of `.`, `;`, `?`, `else`, or
|
||||||
$( println!("{}", $e2) )*;
|
$( 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
|
--> $DIR/missing-semicolon.rs:3:12
|
||||||
|
|
|
|
||||||
LL | $( let x = $e1 )*;
|
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); }
|
LL | fn main() { m!(0, 0; 0, 0); }
|
||||||
| --------------- in this macro invocation
|
| --------------- in this macro invocation
|
||||||
|
|
|
@ -2,5 +2,5 @@
|
||||||
|
|
||||||
pub fn main() {
|
pub fn main() {
|
||||||
let r = 1..2..3;
|
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
|
--> $DIR/range-3.rs:4:17
|
||||||
|
|
|
|
||||||
LL | let r = 1..2..3;
|
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
|
error: aborting due to previous error
|
||||||
|
|
||||||
|
|
|
@ -2,5 +2,5 @@
|
||||||
|
|
||||||
pub fn main() {
|
pub fn main() {
|
||||||
let r = ..1..2;
|
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
|
--> $DIR/range-4.rs:4:16
|
||||||
|
|
|
|
||||||
LL | let r = ..1..2;
|
LL | let r = ..1..2;
|
||||||
| ^^ expected one of `.`, `;`, `?`, or an operator
|
| ^^ expected one of `.`, `;`, `?`, `else`, or an operator
|
||||||
|
|
||||||
error: aborting due to previous error
|
error: aborting due to previous error
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
#![feature(let_else)]
|
||||||
|
|
||||||
#![deny(unreachable_patterns)]
|
#![deny(unreachable_patterns)]
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
|
@ -53,4 +55,5 @@ fn main() {
|
||||||
1..=2 => {}, //~ ERROR unreachable pattern
|
1..=2 => {}, //~ ERROR unreachable pattern
|
||||||
_ => {},
|
_ => {},
|
||||||
}
|
}
|
||||||
|
let (0 | 0) = 0 else { return }; //~ ERROR unreachable pattern
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,68 +1,74 @@
|
||||||
error: unreachable pattern
|
error: unreachable pattern
|
||||||
--> $DIR/top-level-alternation.rs:4:23
|
--> $DIR/top-level-alternation.rs:6:23
|
||||||
|
|
|
|
||||||
LL | while let 0..=2 | 1 = 0 {}
|
LL | while let 0..=2 | 1 = 0 {}
|
||||||
| ^
|
| ^
|
||||||
|
|
|
|
||||||
note: the lint level is defined here
|
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)]
|
LL | #![deny(unreachable_patterns)]
|
||||||
| ^^^^^^^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
error: unreachable pattern
|
error: unreachable pattern
|
||||||
--> $DIR/top-level-alternation.rs:5:20
|
--> $DIR/top-level-alternation.rs:7:20
|
||||||
|
|
|
|
||||||
LL | if let 0..=2 | 1 = 0 {}
|
LL | if let 0..=2 | 1 = 0 {}
|
||||||
| ^
|
| ^
|
||||||
|
|
||||||
error: unreachable pattern
|
error: unreachable pattern
|
||||||
--> $DIR/top-level-alternation.rs:9:15
|
--> $DIR/top-level-alternation.rs:11:15
|
||||||
|
|
|
|
||||||
LL | | 0 => {}
|
LL | | 0 => {}
|
||||||
| ^
|
| ^
|
||||||
|
|
||||||
error: unreachable pattern
|
error: unreachable pattern
|
||||||
--> $DIR/top-level-alternation.rs:14:15
|
--> $DIR/top-level-alternation.rs:16:15
|
||||||
|
|
|
|
||||||
LL | | Some(0) => {}
|
LL | | Some(0) => {}
|
||||||
| ^^^^^^^
|
| ^^^^^^^
|
||||||
|
|
||||||
error: unreachable pattern
|
error: unreachable pattern
|
||||||
--> $DIR/top-level-alternation.rs:19:9
|
--> $DIR/top-level-alternation.rs:21:9
|
||||||
|
|
|
|
||||||
LL | (0, 0) => {}
|
LL | (0, 0) => {}
|
||||||
| ^^^^^^
|
| ^^^^^^
|
||||||
|
|
||||||
error: unreachable pattern
|
error: unreachable pattern
|
||||||
--> $DIR/top-level-alternation.rs:39:9
|
--> $DIR/top-level-alternation.rs:41:9
|
||||||
|
|
|
|
||||||
LL | _ => {}
|
LL | _ => {}
|
||||||
| ^
|
| ^
|
||||||
|
|
||||||
error: unreachable pattern
|
error: unreachable pattern
|
||||||
--> $DIR/top-level-alternation.rs:43:9
|
--> $DIR/top-level-alternation.rs:45:9
|
||||||
|
|
|
|
||||||
LL | Some(_) => {}
|
LL | Some(_) => {}
|
||||||
| ^^^^^^^
|
| ^^^^^^^
|
||||||
|
|
||||||
error: unreachable pattern
|
error: unreachable pattern
|
||||||
--> $DIR/top-level-alternation.rs:44:9
|
--> $DIR/top-level-alternation.rs:46:9
|
||||||
|
|
|
|
||||||
LL | None => {}
|
LL | None => {}
|
||||||
| ^^^^
|
| ^^^^
|
||||||
|
|
||||||
error: unreachable pattern
|
error: unreachable pattern
|
||||||
--> $DIR/top-level-alternation.rs:49:9
|
--> $DIR/top-level-alternation.rs:51:9
|
||||||
|
|
|
|
||||||
LL | None | Some(_) => {}
|
LL | None | Some(_) => {}
|
||||||
| ^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^
|
||||||
|
|
||||||
error: unreachable pattern
|
error: unreachable pattern
|
||||||
--> $DIR/top-level-alternation.rs:53:9
|
--> $DIR/top-level-alternation.rs:55:9
|
||||||
|
|
|
|
||||||
LL | 1..=2 => {},
|
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> {
|
impl<'a, 'tcx> Visitor<'tcx> for SimilarNamesLocalVisitor<'a, 'tcx> {
|
||||||
fn visit_local(&mut self, local: &'tcx Local) {
|
fn visit_local(&mut self, local: &'tcx Local) {
|
||||||
if let Some(ref init) = local.init {
|
if let Some((init, els)) = &local.kind.init_else_opt() {
|
||||||
self.apply(|this| walk_expr(this, &**init));
|
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
|
// add the pattern after the expression because the bindings aren't available
|
||||||
// yet in the init
|
// yet in the init
|
||||||
|
|
|
@ -221,7 +221,7 @@ pub fn eq_stmt(l: &Stmt, r: &Stmt) -> bool {
|
||||||
(Local(l), Local(r)) => {
|
(Local(l), Local(r)) => {
|
||||||
eq_pat(&l.pat, &r.pat)
|
eq_pat(&l.pat, &r.pat)
|
||||||
&& both(&l.ty, &r.ty, |l, r| eq_ty(l, r))
|
&& 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))
|
&& over(&l.attrs, &r.attrs, |l, r| eq_attr(l, r))
|
||||||
},
|
},
|
||||||
(Item(l), Item(r)) => eq_item(l, r, eq_item_kind),
|
(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 {
|
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)
|
eq_id(l.ident, r.ident)
|
||||||
&& over(&l.attrs, &r.attrs, |l, r| eq_attr(l, r))
|
&& 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);
|
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;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -97,7 +97,7 @@ impl Rewrite for ast::Local {
|
||||||
infix.push_str(&rewrite);
|
infix.push_str(&rewrite);
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.init.is_some() {
|
if self.kind.init().is_some() {
|
||||||
infix.push_str(" =");
|
infix.push_str(" =");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -106,11 +106,12 @@ impl Rewrite for ast::Local {
|
||||||
|
|
||||||
result.push_str(&infix);
|
result.push_str(&infix);
|
||||||
|
|
||||||
if let Some(ref ex) = self.init {
|
if let Some((init, _els)) = self.kind.init_else_opt() {
|
||||||
// 1 = trailing semicolon;
|
// 1 = trailing semicolon;
|
||||||
let nested_shape = shape.sub_width(1)?;
|
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(';');
|
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