Rollup merge of #121619 - RossSmyth:pfix_match, r=petrochenkov
Experimental feature postfix match This has a basic experimental implementation for the RFC postfix match (rust-lang/rfcs#3295, #121618). [Liaison is](https://rust-lang.zulipchat.com/#narrow/stream/213817-t-lang/topic/Postfix.20Match.20Liaison/near/423301844) ```@scottmcm``` with the lang team's [experimental feature gate process](https://github.com/rust-lang/lang-team/blob/master/src/how_to/experiment.md). This feature has had an RFC for a while, and there has been discussion on it for a while. It would probably be valuable to see it out in the field rather than continue discussing it. This feature also allows to see how popular postfix expressions like this are for the postfix macros RFC, as those will take more time to implement. It is entirely implemented in the parser, so it should be relatively easy to remove if needed. This PR is split in to 5 commits to ease review. 1. The implementation of the feature & gating. 2. Add a MatchKind field, fix uses, fix pretty. 3. Basic rustfmt impl, as rustfmt crashes upon seeing this syntax without a fix. 4. Add new MatchSource to HIR for Clippy & other HIR consumers
This commit is contained in:
commit
783778c631
34 changed files with 367 additions and 41 deletions
|
@ -1441,7 +1441,7 @@ pub enum ExprKind {
|
||||||
/// `'label: loop { block }`
|
/// `'label: loop { block }`
|
||||||
Loop(P<Block>, Option<Label>, Span),
|
Loop(P<Block>, Option<Label>, Span),
|
||||||
/// A `match` block.
|
/// A `match` block.
|
||||||
Match(P<Expr>, ThinVec<Arm>),
|
Match(P<Expr>, ThinVec<Arm>, MatchKind),
|
||||||
/// A closure (e.g., `move |a, b, c| a + b + c`).
|
/// A closure (e.g., `move |a, b, c| a + b + c`).
|
||||||
Closure(Box<Closure>),
|
Closure(Box<Closure>),
|
||||||
/// A block (`'label: { ... }`).
|
/// A block (`'label: { ... }`).
|
||||||
|
@ -1766,6 +1766,15 @@ pub enum StrStyle {
|
||||||
Raw(u8),
|
Raw(u8),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The kind of match expression
|
||||||
|
#[derive(Clone, Copy, Encodable, Decodable, Debug, PartialEq)]
|
||||||
|
pub enum MatchKind {
|
||||||
|
/// match expr { ... }
|
||||||
|
Prefix,
|
||||||
|
/// expr.match { ... }
|
||||||
|
Postfix,
|
||||||
|
}
|
||||||
|
|
||||||
/// A literal in a meta item.
|
/// A literal in a meta item.
|
||||||
#[derive(Clone, Encodable, Decodable, Debug, HashStable_Generic)]
|
#[derive(Clone, Encodable, Decodable, Debug, HashStable_Generic)]
|
||||||
pub struct MetaItemLit {
|
pub struct MetaItemLit {
|
||||||
|
|
|
@ -1425,7 +1425,7 @@ pub fn noop_visit_expr<T: MutVisitor>(
|
||||||
visit_opt(label, |label| vis.visit_label(label));
|
visit_opt(label, |label| vis.visit_label(label));
|
||||||
vis.visit_span(span);
|
vis.visit_span(span);
|
||||||
}
|
}
|
||||||
ExprKind::Match(expr, arms) => {
|
ExprKind::Match(expr, arms, _kind) => {
|
||||||
vis.visit_expr(expr);
|
vis.visit_expr(expr);
|
||||||
arms.flat_map_in_place(|arm| vis.flat_map_arm(arm));
|
arms.flat_map_in_place(|arm| vis.flat_map_arm(arm));
|
||||||
}
|
}
|
||||||
|
|
|
@ -923,7 +923,7 @@ pub fn walk_expr<'a, V: Visitor<'a>>(visitor: &mut V, expression: &'a Expr) -> V
|
||||||
visit_opt!(visitor, visit_label, opt_label);
|
visit_opt!(visitor, visit_label, opt_label);
|
||||||
try_visit!(visitor.visit_block(block));
|
try_visit!(visitor.visit_block(block));
|
||||||
}
|
}
|
||||||
ExprKind::Match(subexpression, arms) => {
|
ExprKind::Match(subexpression, arms, _kind) => {
|
||||||
try_visit!(visitor.visit_expr(subexpression));
|
try_visit!(visitor.visit_expr(subexpression));
|
||||||
walk_list!(visitor, visit_arm, arms);
|
walk_list!(visitor, visit_arm, arms);
|
||||||
}
|
}
|
||||||
|
|
|
@ -181,10 +181,13 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
||||||
)
|
)
|
||||||
}),
|
}),
|
||||||
ExprKind::TryBlock(body) => self.lower_expr_try_block(body),
|
ExprKind::TryBlock(body) => self.lower_expr_try_block(body),
|
||||||
ExprKind::Match(expr, arms) => hir::ExprKind::Match(
|
ExprKind::Match(expr, arms, kind) => hir::ExprKind::Match(
|
||||||
self.lower_expr(expr),
|
self.lower_expr(expr),
|
||||||
self.arena.alloc_from_iter(arms.iter().map(|x| self.lower_arm(x))),
|
self.arena.alloc_from_iter(arms.iter().map(|x| self.lower_arm(x))),
|
||||||
hir::MatchSource::Normal,
|
match kind {
|
||||||
|
MatchKind::Prefix => hir::MatchSource::Normal,
|
||||||
|
MatchKind::Postfix => hir::MatchSource::Postfix,
|
||||||
|
},
|
||||||
),
|
),
|
||||||
ExprKind::Await(expr, await_kw_span) => self.lower_expr_await(*await_kw_span, expr),
|
ExprKind::Await(expr, await_kw_span) => self.lower_expr_await(*await_kw_span, expr),
|
||||||
ExprKind::Closure(box Closure {
|
ExprKind::Closure(box Closure {
|
||||||
|
|
|
@ -564,6 +564,7 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session, features: &Features) {
|
||||||
gate_all!(generic_const_items, "generic const items are experimental");
|
gate_all!(generic_const_items, "generic const items are experimental");
|
||||||
gate_all!(unnamed_fields, "unnamed fields are not yet fully implemented");
|
gate_all!(unnamed_fields, "unnamed fields are not yet fully implemented");
|
||||||
gate_all!(fn_delegation, "functions delegation is not yet fully implemented");
|
gate_all!(fn_delegation, "functions delegation is not yet fully implemented");
|
||||||
|
gate_all!(postfix_match, "postfix match is experimental");
|
||||||
|
|
||||||
if !visitor.features.never_patterns {
|
if !visitor.features.never_patterns {
|
||||||
if let Some(spans) = spans.get(&sym::never_patterns) {
|
if let Some(spans) = spans.get(&sym::never_patterns) {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use crate::pp::Breaks::Inconsistent;
|
use crate::pp::Breaks::Inconsistent;
|
||||||
use crate::pprust::state::{AnnNode, PrintState, State, INDENT_UNIT};
|
use crate::pprust::state::{AnnNode, PrintState, State, INDENT_UNIT};
|
||||||
use ast::ForLoopKind;
|
use ast::{ForLoopKind, MatchKind};
|
||||||
use itertools::{Itertools, Position};
|
use itertools::{Itertools, Position};
|
||||||
use rustc_ast::ptr::P;
|
use rustc_ast::ptr::P;
|
||||||
use rustc_ast::token;
|
use rustc_ast::token;
|
||||||
|
@ -589,12 +589,22 @@ impl<'a> State<'a> {
|
||||||
self.word_nbsp("loop");
|
self.word_nbsp("loop");
|
||||||
self.print_block_with_attrs(blk, attrs);
|
self.print_block_with_attrs(blk, attrs);
|
||||||
}
|
}
|
||||||
ast::ExprKind::Match(expr, arms) => {
|
ast::ExprKind::Match(expr, arms, match_kind) => {
|
||||||
self.cbox(0);
|
self.cbox(0);
|
||||||
self.ibox(0);
|
self.ibox(0);
|
||||||
|
|
||||||
|
match match_kind {
|
||||||
|
MatchKind::Prefix => {
|
||||||
self.word_nbsp("match");
|
self.word_nbsp("match");
|
||||||
self.print_expr_as_cond(expr);
|
self.print_expr_as_cond(expr);
|
||||||
self.space();
|
self.space();
|
||||||
|
}
|
||||||
|
MatchKind::Postfix => {
|
||||||
|
self.print_expr_as_cond(expr);
|
||||||
|
self.word_nbsp(".match");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
self.bopen();
|
self.bopen();
|
||||||
self.print_inner_attributes_no_trailing_hardbreak(attrs);
|
self.print_inner_attributes_no_trailing_hardbreak(attrs);
|
||||||
for arm in arms {
|
for arm in arms {
|
||||||
|
|
|
@ -245,7 +245,7 @@ impl<'cx, 'a> Context<'cx, 'a> {
|
||||||
ExprKind::Let(_, local_expr, _, _) => {
|
ExprKind::Let(_, local_expr, _, _) => {
|
||||||
self.manage_cond_expr(local_expr);
|
self.manage_cond_expr(local_expr);
|
||||||
}
|
}
|
||||||
ExprKind::Match(local_expr, _) => {
|
ExprKind::Match(local_expr, ..) => {
|
||||||
self.manage_cond_expr(local_expr);
|
self.manage_cond_expr(local_expr);
|
||||||
}
|
}
|
||||||
ExprKind::MethodCall(call) => {
|
ExprKind::MethodCall(call) => {
|
||||||
|
|
|
@ -132,7 +132,7 @@ fn cs_partial_cmp(
|
||||||
// Reference: https://github.com/rust-lang/rust/pull/103659#issuecomment-1328126354
|
// Reference: https://github.com/rust-lang/rust/pull/103659#issuecomment-1328126354
|
||||||
|
|
||||||
if !tag_then_data
|
if !tag_then_data
|
||||||
&& let ExprKind::Match(_, arms) = &mut expr1.kind
|
&& let ExprKind::Match(_, arms, _) = &mut expr1.kind
|
||||||
&& let Some(last) = arms.last_mut()
|
&& let Some(last) = arms.last_mut()
|
||||||
&& let PatKind::Wild = last.pat.kind
|
&& let PatKind::Wild = last.pat.kind
|
||||||
{
|
{
|
||||||
|
|
|
@ -2,8 +2,7 @@ use crate::deriving::generic::ty::*;
|
||||||
use crate::deriving::generic::*;
|
use crate::deriving::generic::*;
|
||||||
use crate::deriving::path_std;
|
use crate::deriving::path_std;
|
||||||
|
|
||||||
use ast::EnumDef;
|
use rustc_ast::{self as ast, EnumDef, MetaItem};
|
||||||
use rustc_ast::{self as ast, MetaItem};
|
|
||||||
use rustc_expand::base::{Annotatable, ExtCtxt};
|
use rustc_expand::base::{Annotatable, ExtCtxt};
|
||||||
use rustc_span::symbol::{sym, Ident, Symbol};
|
use rustc_span::symbol::{sym, Ident, Symbol};
|
||||||
use rustc_span::Span;
|
use rustc_span::Span;
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use crate::base::ExtCtxt;
|
use crate::base::ExtCtxt;
|
||||||
use rustc_ast::ptr::P;
|
use rustc_ast::ptr::P;
|
||||||
use rustc_ast::{self as ast, AttrVec, BlockCheckMode, Expr, LocalKind, PatKind, UnOp};
|
use rustc_ast::{self as ast, AttrVec, BlockCheckMode, Expr, LocalKind, MatchKind, PatKind, UnOp};
|
||||||
use rustc_ast::{attr, token, util::literal};
|
use rustc_ast::{attr, token, util::literal};
|
||||||
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};
|
||||||
|
@ -524,7 +524,7 @@ impl<'a> ExtCtxt<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn expr_match(&self, span: Span, arg: P<ast::Expr>, arms: ThinVec<ast::Arm>) -> P<Expr> {
|
pub fn expr_match(&self, span: Span, arg: P<ast::Expr>, arms: ThinVec<ast::Arm>) -> P<Expr> {
|
||||||
self.expr(span, ast::ExprKind::Match(arg, arms))
|
self.expr(span, ast::ExprKind::Match(arg, arms, MatchKind::Prefix))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn expr_if(
|
pub fn expr_if(
|
||||||
|
|
|
@ -559,6 +559,8 @@ declare_features! (
|
||||||
(unstable, offset_of_nested, "1.77.0", Some(120140)),
|
(unstable, offset_of_nested, "1.77.0", Some(120140)),
|
||||||
/// Allows using `#[optimize(X)]`.
|
/// Allows using `#[optimize(X)]`.
|
||||||
(unstable, optimize_attribute, "1.34.0", Some(54882)),
|
(unstable, optimize_attribute, "1.34.0", Some(54882)),
|
||||||
|
/// Allows postfix match `expr.match { ... }`
|
||||||
|
(unstable, postfix_match, "CURRENT_RUSTC_VERSION", Some(121618)),
|
||||||
/// Allows macro attributes on expressions, statements and non-inline modules.
|
/// Allows macro attributes on expressions, statements and non-inline modules.
|
||||||
(unstable, proc_macro_hygiene, "1.30.0", Some(54727)),
|
(unstable, proc_macro_hygiene, "1.30.0", Some(54727)),
|
||||||
/// Allows `&raw const $place_expr` and `&raw mut $place_expr` expressions.
|
/// Allows `&raw const $place_expr` and `&raw mut $place_expr` expressions.
|
||||||
|
|
|
@ -2019,6 +2019,8 @@ pub enum LocalSource {
|
||||||
pub enum MatchSource {
|
pub enum MatchSource {
|
||||||
/// A `match _ { .. }`.
|
/// A `match _ { .. }`.
|
||||||
Normal,
|
Normal,
|
||||||
|
/// A `expr.match { .. }`.
|
||||||
|
Postfix,
|
||||||
/// A desugared `for _ in _ { .. }` loop.
|
/// A desugared `for _ in _ { .. }` loop.
|
||||||
ForLoopDesugar,
|
ForLoopDesugar,
|
||||||
/// A desugared `?` operator.
|
/// A desugared `?` operator.
|
||||||
|
@ -2035,6 +2037,7 @@ impl MatchSource {
|
||||||
use MatchSource::*;
|
use MatchSource::*;
|
||||||
match self {
|
match self {
|
||||||
Normal => "match",
|
Normal => "match",
|
||||||
|
Postfix => ".match",
|
||||||
ForLoopDesugar => "for",
|
ForLoopDesugar => "for",
|
||||||
TryDesugar(_) => "?",
|
TryDesugar(_) => "?",
|
||||||
AwaitDesugar => ".await",
|
AwaitDesugar => ".await",
|
||||||
|
|
|
@ -865,7 +865,7 @@ trait UnusedDelimLint {
|
||||||
(iter, UnusedDelimsCtx::ForIterExpr, true, None, Some(body.span.lo()), true)
|
(iter, UnusedDelimsCtx::ForIterExpr, true, None, Some(body.span.lo()), true)
|
||||||
}
|
}
|
||||||
|
|
||||||
Match(ref head, _) if Self::LINT_EXPR_IN_PATTERN_MATCHING_CTX => {
|
Match(ref head, ..) if Self::LINT_EXPR_IN_PATTERN_MATCHING_CTX => {
|
||||||
let left = e.span.lo() + rustc_span::BytePos(5);
|
let left = e.span.lo() + rustc_span::BytePos(5);
|
||||||
(head, UnusedDelimsCtx::MatchScrutineeExpr, true, Some(left), None, true)
|
(head, UnusedDelimsCtx::MatchScrutineeExpr, true, Some(left), None, true)
|
||||||
}
|
}
|
||||||
|
@ -1133,7 +1133,7 @@ impl EarlyLintPass for UnusedParens {
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
ExprKind::Match(ref _expr, ref arm) => {
|
ExprKind::Match(ref _expr, ref arm, _) => {
|
||||||
for a in arm {
|
for a in arm {
|
||||||
if let Some(body) = &a.body {
|
if let Some(body) = &a.body {
|
||||||
self.check_unused_delims_expr(
|
self.check_unused_delims_expr(
|
||||||
|
|
|
@ -481,6 +481,7 @@ impl<'p, 'tcx> MatchVisitor<'p, 'tcx> {
|
||||||
// when the iterator is an uninhabited type. unreachable_code will trigger instead.
|
// when the iterator is an uninhabited type. unreachable_code will trigger instead.
|
||||||
hir::MatchSource::ForLoopDesugar if arms.len() == 1 => {}
|
hir::MatchSource::ForLoopDesugar if arms.len() == 1 => {}
|
||||||
hir::MatchSource::ForLoopDesugar
|
hir::MatchSource::ForLoopDesugar
|
||||||
|
| hir::MatchSource::Postfix
|
||||||
| hir::MatchSource::Normal
|
| hir::MatchSource::Normal
|
||||||
| hir::MatchSource::FormatArgs => report_arm_reachability(&cx, &report),
|
| hir::MatchSource::FormatArgs => report_arm_reachability(&cx, &report),
|
||||||
// Unreachable patterns in try and await expressions occur when one of
|
// Unreachable patterns in try and await expressions occur when one of
|
||||||
|
|
|
@ -11,7 +11,7 @@ use crate::errors;
|
||||||
use crate::maybe_recover_from_interpolated_ty_qpath;
|
use crate::maybe_recover_from_interpolated_ty_qpath;
|
||||||
use ast::mut_visit::{noop_visit_expr, MutVisitor};
|
use ast::mut_visit::{noop_visit_expr, MutVisitor};
|
||||||
use ast::token::IdentIsRaw;
|
use ast::token::IdentIsRaw;
|
||||||
use ast::{CoroutineKind, ForLoopKind, GenBlockKind, Pat, Path, PathSegment};
|
use ast::{CoroutineKind, ForLoopKind, GenBlockKind, MatchKind, Pat, Path, PathSegment};
|
||||||
use core::mem;
|
use core::mem;
|
||||||
use core::ops::ControlFlow;
|
use core::ops::ControlFlow;
|
||||||
use rustc_ast::ptr::P;
|
use rustc_ast::ptr::P;
|
||||||
|
@ -1379,6 +1379,13 @@ impl<'a> Parser<'a> {
|
||||||
return Ok(self.mk_await_expr(self_arg, lo));
|
return Ok(self.mk_await_expr(self_arg, lo));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Post-fix match
|
||||||
|
if self.eat_keyword(kw::Match) {
|
||||||
|
let match_span = self.prev_token.span;
|
||||||
|
self.psess.gated_spans.gate(sym::postfix_match, match_span);
|
||||||
|
return self.parse_match_block(lo, match_span, self_arg, MatchKind::Postfix);
|
||||||
|
}
|
||||||
|
|
||||||
let fn_span_lo = self.token.span;
|
let fn_span_lo = self.token.span;
|
||||||
let mut seg = self.parse_path_segment(PathStyle::Expr, None)?;
|
let mut seg = self.parse_path_segment(PathStyle::Expr, None)?;
|
||||||
self.check_trailing_angle_brackets(&seg, &[&token::OpenDelim(Delimiter::Parenthesis)]);
|
self.check_trailing_angle_brackets(&seg, &[&token::OpenDelim(Delimiter::Parenthesis)]);
|
||||||
|
@ -2894,8 +2901,20 @@ impl<'a> Parser<'a> {
|
||||||
/// Parses a `match ... { ... }` expression (`match` token already eaten).
|
/// Parses a `match ... { ... }` expression (`match` token already eaten).
|
||||||
fn parse_expr_match(&mut self) -> PResult<'a, P<Expr>> {
|
fn parse_expr_match(&mut self) -> PResult<'a, P<Expr>> {
|
||||||
let match_span = self.prev_token.span;
|
let match_span = self.prev_token.span;
|
||||||
let lo = self.prev_token.span;
|
|
||||||
let scrutinee = self.parse_expr_res(Restrictions::NO_STRUCT_LITERAL, None)?;
|
let scrutinee = self.parse_expr_res(Restrictions::NO_STRUCT_LITERAL, None)?;
|
||||||
|
|
||||||
|
self.parse_match_block(match_span, match_span, scrutinee, MatchKind::Prefix)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Parses the block of a `match expr { ... }` or a `expr.match { ... }`
|
||||||
|
/// expression. This is after the match token and scrutinee are eaten
|
||||||
|
fn parse_match_block(
|
||||||
|
&mut self,
|
||||||
|
lo: Span,
|
||||||
|
match_span: Span,
|
||||||
|
scrutinee: P<Expr>,
|
||||||
|
match_kind: MatchKind,
|
||||||
|
) -> PResult<'a, P<Expr>> {
|
||||||
if let Err(mut e) = self.expect(&token::OpenDelim(Delimiter::Brace)) {
|
if let Err(mut e) = self.expect(&token::OpenDelim(Delimiter::Brace)) {
|
||||||
if self.token == token::Semi {
|
if self.token == token::Semi {
|
||||||
e.span_suggestion_short(
|
e.span_suggestion_short(
|
||||||
|
@ -2938,7 +2957,7 @@ impl<'a> Parser<'a> {
|
||||||
});
|
});
|
||||||
return Ok(self.mk_expr_with_attrs(
|
return Ok(self.mk_expr_with_attrs(
|
||||||
span,
|
span,
|
||||||
ExprKind::Match(scrutinee, arms),
|
ExprKind::Match(scrutinee, arms, match_kind),
|
||||||
attrs,
|
attrs,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
@ -2946,7 +2965,7 @@ impl<'a> Parser<'a> {
|
||||||
}
|
}
|
||||||
let hi = self.token.span;
|
let hi = self.token.span;
|
||||||
self.bump();
|
self.bump();
|
||||||
Ok(self.mk_expr_with_attrs(lo.to(hi), ExprKind::Match(scrutinee, arms), attrs))
|
Ok(self.mk_expr_with_attrs(lo.to(hi), ExprKind::Match(scrutinee, arms, match_kind), attrs))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Attempt to recover from match arm body with statements and no surrounding braces.
|
/// Attempt to recover from match arm body with statements and no surrounding braces.
|
||||||
|
@ -3955,7 +3974,7 @@ impl MutVisitor for CondChecker<'_> {
|
||||||
| ExprKind::While(_, _, _)
|
| ExprKind::While(_, _, _)
|
||||||
| ExprKind::ForLoop { .. }
|
| ExprKind::ForLoop { .. }
|
||||||
| ExprKind::Loop(_, _, _)
|
| ExprKind::Loop(_, _, _)
|
||||||
| ExprKind::Match(_, _)
|
| ExprKind::Match(_, _, _)
|
||||||
| ExprKind::Closure(_)
|
| ExprKind::Closure(_)
|
||||||
| ExprKind::Block(_, _)
|
| ExprKind::Block(_, _)
|
||||||
| ExprKind::Gen(_, _, _)
|
| ExprKind::Gen(_, _, _)
|
||||||
|
|
|
@ -48,7 +48,7 @@ impl NonConstExpr {
|
||||||
Self::Match(TryDesugar(_)) => &[sym::const_try],
|
Self::Match(TryDesugar(_)) => &[sym::const_try],
|
||||||
|
|
||||||
// All other expressions are allowed.
|
// All other expressions are allowed.
|
||||||
Self::Loop(Loop | While) | Self::Match(Normal | FormatArgs) => &[],
|
Self::Loop(Loop | While) | Self::Match(Normal | Postfix | FormatArgs) => &[],
|
||||||
};
|
};
|
||||||
|
|
||||||
Some(gates)
|
Some(gates)
|
||||||
|
|
|
@ -1335,6 +1335,7 @@ symbols! {
|
||||||
poll,
|
poll,
|
||||||
poll_next,
|
poll_next,
|
||||||
post_dash_lto: "post-lto",
|
post_dash_lto: "post-lto",
|
||||||
|
postfix_match,
|
||||||
powerpc_target_feature,
|
powerpc_target_feature,
|
||||||
powf128,
|
powf128,
|
||||||
powf16,
|
powf16,
|
||||||
|
|
22
src/doc/unstable-book/src/language-features/postfix-match.md
Normal file
22
src/doc/unstable-book/src/language-features/postfix-match.md
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
# `postfix-match`
|
||||||
|
|
||||||
|
`postfix-match` adds the feature for matching upon values postfix
|
||||||
|
the expressions that generate the values.
|
||||||
|
|
||||||
|
```rust,edition2021
|
||||||
|
#![feature(postfix_match)]
|
||||||
|
|
||||||
|
enum Foo {
|
||||||
|
Bar,
|
||||||
|
Baz
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_foo() -> Foo {
|
||||||
|
Foo::Bar
|
||||||
|
}
|
||||||
|
|
||||||
|
get_foo().match {
|
||||||
|
Foo::Bar => {},
|
||||||
|
Foo::Baz => panic!(),
|
||||||
|
}
|
||||||
|
```
|
|
@ -105,7 +105,7 @@ impl<'ast> Visitor<'ast> for BreakVisitor {
|
||||||
fn visit_expr(&mut self, expr: &'ast Expr) {
|
fn visit_expr(&mut self, expr: &'ast Expr) {
|
||||||
self.is_break = match expr.kind {
|
self.is_break = match expr.kind {
|
||||||
ExprKind::Break(..) | ExprKind::Continue(..) | ExprKind::Ret(..) => true,
|
ExprKind::Break(..) | ExprKind::Continue(..) | ExprKind::Ret(..) => true,
|
||||||
ExprKind::Match(_, ref arms) => arms.iter().all(|arm|
|
ExprKind::Match(_, ref arms, _) => arms.iter().all(|arm|
|
||||||
arm.body.is_none() || arm.body.as_deref().is_some_and(|body| self.check_expr(body))
|
arm.body.is_none() || arm.body.as_deref().is_some_and(|body| self.check_expr(body))
|
||||||
),
|
),
|
||||||
ExprKind::If(_, ref then, Some(ref els)) => self.check_block(then) && self.check_expr(els),
|
ExprKind::If(_, ref then, Some(ref els)) => self.check_block(then) && self.check_expr(els),
|
||||||
|
|
|
@ -552,7 +552,7 @@ fn ident_difference_expr_with_base_location(
|
||||||
| (Gen(_, _, _), Gen(_, _, _))
|
| (Gen(_, _, _), Gen(_, _, _))
|
||||||
| (Block(_, _), Block(_, _))
|
| (Block(_, _), Block(_, _))
|
||||||
| (Closure(_), Closure(_))
|
| (Closure(_), Closure(_))
|
||||||
| (Match(_, _), Match(_, _))
|
| (Match(_, _, _), Match(_, _, _))
|
||||||
| (Loop(_, _, _), Loop(_, _, _))
|
| (Loop(_, _, _), Loop(_, _, _))
|
||||||
| (ForLoop { .. }, ForLoop { .. })
|
| (ForLoop { .. }, ForLoop { .. })
|
||||||
| (While(_, _, _), While(_, _, _))
|
| (While(_, _, _), While(_, _, _))
|
||||||
|
|
|
@ -198,7 +198,7 @@ pub fn eq_expr(l: &Expr, r: &Expr) -> bool {
|
||||||
},
|
},
|
||||||
(AssignOp(lo, lp, lv), AssignOp(ro, rp, rv)) => lo.node == ro.node && eq_expr(lp, rp) && eq_expr(lv, rv),
|
(AssignOp(lo, lp, lv), AssignOp(ro, rp, rv)) => lo.node == ro.node && eq_expr(lp, rp) && eq_expr(lv, rv),
|
||||||
(Field(lp, lf), Field(rp, rf)) => eq_id(*lf, *rf) && eq_expr(lp, rp),
|
(Field(lp, lf), Field(rp, rf)) => eq_id(*lf, *rf) && eq_expr(lp, rp),
|
||||||
(Match(ls, la), Match(rs, ra)) => eq_expr(ls, rs) && over(la, ra, eq_arm),
|
(Match(ls, la, lkind), Match(rs, ra, rkind)) => (lkind == rkind) && eq_expr(ls, rs) && over(la, ra, eq_arm),
|
||||||
(
|
(
|
||||||
Closure(box ast::Closure {
|
Closure(box ast::Closure {
|
||||||
binder: lb,
|
binder: lb,
|
||||||
|
|
|
@ -3,7 +3,7 @@ use std::cmp::min;
|
||||||
|
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use rustc_ast::token::{Delimiter, Lit, LitKind};
|
use rustc_ast::token::{Delimiter, Lit, LitKind};
|
||||||
use rustc_ast::{ast, ptr, token, ForLoopKind};
|
use rustc_ast::{ast, ptr, token, ForLoopKind, MatchKind};
|
||||||
use rustc_span::{BytePos, Span};
|
use rustc_span::{BytePos, Span};
|
||||||
|
|
||||||
use crate::chains::rewrite_chain;
|
use crate::chains::rewrite_chain;
|
||||||
|
@ -170,8 +170,8 @@ pub(crate) fn format_expr(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ast::ExprKind::Match(ref cond, ref arms) => {
|
ast::ExprKind::Match(ref cond, ref arms, kind) => {
|
||||||
rewrite_match(context, cond, arms, shape, expr.span, &expr.attrs)
|
rewrite_match(context, cond, arms, shape, expr.span, &expr.attrs, kind)
|
||||||
}
|
}
|
||||||
ast::ExprKind::Path(ref qself, ref path) => {
|
ast::ExprKind::Path(ref qself, ref path) => {
|
||||||
rewrite_path(context, PathContext::Expr, qself, path, shape)
|
rewrite_path(context, PathContext::Expr, qself, path, shape)
|
||||||
|
@ -625,7 +625,7 @@ pub(crate) fn rewrite_cond(
|
||||||
shape: Shape,
|
shape: Shape,
|
||||||
) -> Option<String> {
|
) -> Option<String> {
|
||||||
match expr.kind {
|
match expr.kind {
|
||||||
ast::ExprKind::Match(ref cond, _) => {
|
ast::ExprKind::Match(ref cond, _, MatchKind::Prefix) => {
|
||||||
// `match `cond` {`
|
// `match `cond` {`
|
||||||
let cond_shape = match context.config.indent_style() {
|
let cond_shape = match context.config.indent_style() {
|
||||||
IndentStyle::Visual => shape.shrink_left(6).and_then(|s| s.sub_width(2))?,
|
IndentStyle::Visual => shape.shrink_left(6).and_then(|s| s.sub_width(2))?,
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
use std::iter::repeat;
|
use std::iter::repeat;
|
||||||
|
|
||||||
use rustc_ast::{ast, ptr};
|
use rustc_ast::{ast, ptr, MatchKind};
|
||||||
use rustc_span::{BytePos, Span};
|
use rustc_span::{BytePos, Span};
|
||||||
|
|
||||||
use crate::comment::{combine_strs_with_missing_comments, rewrite_comment};
|
use crate::comment::{combine_strs_with_missing_comments, rewrite_comment};
|
||||||
|
@ -72,6 +72,7 @@ pub(crate) fn rewrite_match(
|
||||||
shape: Shape,
|
shape: Shape,
|
||||||
span: Span,
|
span: Span,
|
||||||
attrs: &[ast::Attribute],
|
attrs: &[ast::Attribute],
|
||||||
|
match_kind: MatchKind,
|
||||||
) -> Option<String> {
|
) -> Option<String> {
|
||||||
// Do not take the rhs overhead from the upper expressions into account
|
// Do not take the rhs overhead from the upper expressions into account
|
||||||
// when rewriting match condition.
|
// when rewriting match condition.
|
||||||
|
@ -131,7 +132,9 @@ pub(crate) fn rewrite_match(
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
let span_after_cond = mk_sp(cond.span.hi(), span.hi());
|
let span_after_cond = mk_sp(cond.span.hi(), span.hi());
|
||||||
Some(format!(
|
|
||||||
|
match match_kind {
|
||||||
|
MatchKind::Prefix => Some(format!(
|
||||||
"match {}{}{{\n{}{}{}\n{}}}",
|
"match {}{}{{\n{}{}{}\n{}}}",
|
||||||
cond_str,
|
cond_str,
|
||||||
block_sep,
|
block_sep,
|
||||||
|
@ -139,7 +142,17 @@ pub(crate) fn rewrite_match(
|
||||||
nested_indent_str,
|
nested_indent_str,
|
||||||
rewrite_match_arms(context, arms, shape, span_after_cond, open_brace_pos)?,
|
rewrite_match_arms(context, arms, shape, span_after_cond, open_brace_pos)?,
|
||||||
shape.indent.to_string(context.config),
|
shape.indent.to_string(context.config),
|
||||||
))
|
)),
|
||||||
|
MatchKind::Postfix => Some(format!(
|
||||||
|
"{}.match{}{{\n{}{}{}\n{}}}",
|
||||||
|
cond_str,
|
||||||
|
block_sep,
|
||||||
|
inner_attrs_str,
|
||||||
|
nested_indent_str,
|
||||||
|
rewrite_match_arms(context, arms, shape, span_after_cond, open_brace_pos)?,
|
||||||
|
shape.indent.to_string(context.config),
|
||||||
|
)),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
20
src/tools/rustfmt/tests/source/postfix-match/pf-match.rs
Normal file
20
src/tools/rustfmt/tests/source/postfix-match/pf-match.rs
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
#![feature(postfix_match)]
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let val = Some(42);
|
||||||
|
|
||||||
|
val.match {
|
||||||
|
Some(_) => 2,
|
||||||
|
_ => 1
|
||||||
|
};
|
||||||
|
|
||||||
|
Some(2).match {
|
||||||
|
Some(_) => true,
|
||||||
|
None => false
|
||||||
|
}.match {
|
||||||
|
false => "ferris is cute",
|
||||||
|
true => "I turn cats in to petted cats",
|
||||||
|
}.match {
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
}
|
20
src/tools/rustfmt/tests/target/postfix-match/pf-match.rs
Normal file
20
src/tools/rustfmt/tests/target/postfix-match/pf-match.rs
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
#![feature(postfix_match)]
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let val = Some(42);
|
||||||
|
|
||||||
|
val.match {
|
||||||
|
Some(_) => 2,
|
||||||
|
_ => 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
Some(2).match {
|
||||||
|
Some(_) => true,
|
||||||
|
None => false,
|
||||||
|
}.match {
|
||||||
|
false => "ferris is cute",
|
||||||
|
true => "I turn cats in to petted cats",
|
||||||
|
}.match {
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
}
|
21
tests/pretty/postfix-match.rs
Normal file
21
tests/pretty/postfix-match.rs
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
#![feature(postfix_match)]
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let val = Some(42);
|
||||||
|
|
||||||
|
val.match {
|
||||||
|
Some(_) => 2,
|
||||||
|
_ => 1
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
Some(2).match {
|
||||||
|
Some(_) => true,
|
||||||
|
None => false
|
||||||
|
}.match {
|
||||||
|
false => "ferris is cute",
|
||||||
|
true => "I turn cats in to petted cats",
|
||||||
|
}.match {
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
}
|
17
tests/ui/feature-gates/feature-gate-postfix_match.rs
Normal file
17
tests/ui/feature-gates/feature-gate-postfix_match.rs
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
// Testing that postfix match doesn't work without enabling the feature
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let val = Some(42);
|
||||||
|
|
||||||
|
val.match { //~ ERROR postfix match is experimental
|
||||||
|
Some(42) => "the answer to life, the universe, and everything",
|
||||||
|
_ => "might be the answer to something"
|
||||||
|
};
|
||||||
|
|
||||||
|
// Test that the gate works behind a cfg
|
||||||
|
#[cfg(FALSE)]
|
||||||
|
val.match { //~ ERROR postfix match is experimental
|
||||||
|
Some(42) => "the answer to life, the universe, and everything",
|
||||||
|
_ => "might be the answer to something"
|
||||||
|
};
|
||||||
|
}
|
23
tests/ui/feature-gates/feature-gate-postfix_match.stderr
Normal file
23
tests/ui/feature-gates/feature-gate-postfix_match.stderr
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
error[E0658]: postfix match is experimental
|
||||||
|
--> $DIR/feature-gate-postfix_match.rs:6:9
|
||||||
|
|
|
||||||
|
LL | val.match {
|
||||||
|
| ^^^^^
|
||||||
|
|
|
||||||
|
= note: see issue #121618 <https://github.com/rust-lang/rust/issues/121618> for more information
|
||||||
|
= help: add `#![feature(postfix_match)]` to the crate attributes to enable
|
||||||
|
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
|
||||||
|
|
||||||
|
error[E0658]: postfix match is experimental
|
||||||
|
--> $DIR/feature-gate-postfix_match.rs:13:9
|
||||||
|
|
|
||||||
|
LL | val.match {
|
||||||
|
| ^^^^^
|
||||||
|
|
|
||||||
|
= note: see issue #121618 <https://github.com/rust-lang/rust/issues/121618> for more information
|
||||||
|
= help: add `#![feature(postfix_match)]` to the crate attributes to enable
|
||||||
|
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
|
||||||
|
|
||||||
|
error: aborting due to 2 previous errors
|
||||||
|
|
||||||
|
For more information about this error, try `rustc --explain E0658`.
|
16
tests/ui/match/postfix-match/pf-match-chain.rs
Normal file
16
tests/ui/match/postfix-match/pf-match-chain.rs
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
//@ run-pass
|
||||||
|
|
||||||
|
#![feature(postfix_match)]
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
1.match {
|
||||||
|
2 => Some(0),
|
||||||
|
_ => None,
|
||||||
|
}.match {
|
||||||
|
None => Ok(true),
|
||||||
|
Some(_) => Err("nope")
|
||||||
|
}.match {
|
||||||
|
Ok(_) => (),
|
||||||
|
Err(_) => panic!()
|
||||||
|
}
|
||||||
|
}
|
7
tests/ui/match/postfix-match/pf-match-exhaustiveness.rs
Normal file
7
tests/ui/match/postfix-match/pf-match-exhaustiveness.rs
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
#![feature(postfix_match)]
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
Some(1).match { //~ non-exhaustive patterns
|
||||||
|
None => {},
|
||||||
|
}
|
||||||
|
}
|
21
tests/ui/match/postfix-match/pf-match-exhaustiveness.stderr
Normal file
21
tests/ui/match/postfix-match/pf-match-exhaustiveness.stderr
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
error[E0004]: non-exhaustive patterns: `Some(_)` not covered
|
||||||
|
--> $DIR/pf-match-exhaustiveness.rs:4:5
|
||||||
|
|
|
||||||
|
LL | Some(1).match {
|
||||||
|
| ^^^^^^^ pattern `Some(_)` not covered
|
||||||
|
|
|
||||||
|
note: `Option<i32>` defined here
|
||||||
|
--> $SRC_DIR/core/src/option.rs:LL:COL
|
||||||
|
::: $SRC_DIR/core/src/option.rs:LL:COL
|
||||||
|
|
|
||||||
|
= note: not covered
|
||||||
|
= note: the matched value is of type `Option<i32>`
|
||||||
|
help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown
|
||||||
|
|
|
||||||
|
LL ~ None => {},
|
||||||
|
LL ~ Some(_) => todo!(),
|
||||||
|
|
|
||||||
|
|
||||||
|
error: aborting due to 1 previous error
|
||||||
|
|
||||||
|
For more information about this error, try `rustc --explain E0004`.
|
15
tests/ui/match/postfix-match/pf-match-types.rs
Normal file
15
tests/ui/match/postfix-match/pf-match-types.rs
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
#![feature(postfix_match)]
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
Some(10).match {
|
||||||
|
//~^ NOTE `match` arms have incompatible types
|
||||||
|
Some(5) => false,
|
||||||
|
//~^ NOTE this is found to be of type `bool`
|
||||||
|
Some(2) => true,
|
||||||
|
//~^ NOTE this is found to be of type `bool`
|
||||||
|
None => (),
|
||||||
|
//~^ ERROR `match` arms have incompatible types
|
||||||
|
//~| NOTE expected `bool`, found `()`
|
||||||
|
_ => true
|
||||||
|
}
|
||||||
|
}
|
21
tests/ui/match/postfix-match/pf-match-types.stderr
Normal file
21
tests/ui/match/postfix-match/pf-match-types.stderr
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
error[E0308]: `match` arms have incompatible types
|
||||||
|
--> $DIR/pf-match-types.rs:10:20
|
||||||
|
|
|
||||||
|
LL | / Some(10).match {
|
||||||
|
LL | |
|
||||||
|
LL | | Some(5) => false,
|
||||||
|
| | ----- this is found to be of type `bool`
|
||||||
|
LL | |
|
||||||
|
LL | | Some(2) => true,
|
||||||
|
| | ---- this is found to be of type `bool`
|
||||||
|
LL | |
|
||||||
|
LL | | None => (),
|
||||||
|
| | ^^ expected `bool`, found `()`
|
||||||
|
... |
|
||||||
|
LL | | _ => true
|
||||||
|
LL | | }
|
||||||
|
| |_____- `match` arms have incompatible types
|
||||||
|
|
||||||
|
error: aborting due to 1 previous error
|
||||||
|
|
||||||
|
For more information about this error, try `rustc --explain E0308`.
|
62
tests/ui/match/postfix-match/postfix-match.rs
Normal file
62
tests/ui/match/postfix-match/postfix-match.rs
Normal file
|
@ -0,0 +1,62 @@
|
||||||
|
//@ run-pass
|
||||||
|
|
||||||
|
#![feature(postfix_match)]
|
||||||
|
|
||||||
|
struct Bar {
|
||||||
|
foo: u8,
|
||||||
|
baz: u8,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn main() {
|
||||||
|
let thing = Some("thing");
|
||||||
|
|
||||||
|
thing.match {
|
||||||
|
Some("nothing") => {},
|
||||||
|
Some(text) if text.eq_ignore_ascii_case("tapir") => {},
|
||||||
|
Some("true") | Some("false") => {},
|
||||||
|
Some("thing") => {},
|
||||||
|
Some(_) => {},
|
||||||
|
None => {}
|
||||||
|
};
|
||||||
|
|
||||||
|
let num = 2u8;
|
||||||
|
|
||||||
|
num.match {
|
||||||
|
0 => {},
|
||||||
|
1..=5 => {},
|
||||||
|
_ => {},
|
||||||
|
};
|
||||||
|
|
||||||
|
let slic = &[1, 2, 3, 4][..];
|
||||||
|
|
||||||
|
slic.match {
|
||||||
|
[1] => {},
|
||||||
|
[2, _tail @ ..] => {},
|
||||||
|
[1, _] => {},
|
||||||
|
_ => {},
|
||||||
|
};
|
||||||
|
|
||||||
|
slic[0].match {
|
||||||
|
1 => 0,
|
||||||
|
i => i,
|
||||||
|
};
|
||||||
|
|
||||||
|
let out = (1, 2).match {
|
||||||
|
(1, 3) => 0,
|
||||||
|
(_, 1) => 0,
|
||||||
|
(1, i) => i,
|
||||||
|
_ => 3,
|
||||||
|
};
|
||||||
|
assert!(out == 2);
|
||||||
|
|
||||||
|
let strct = Bar {
|
||||||
|
foo: 3,
|
||||||
|
baz: 4
|
||||||
|
};
|
||||||
|
|
||||||
|
strct.match {
|
||||||
|
Bar { foo: 1, .. } => {},
|
||||||
|
Bar { baz: 2, .. } => {},
|
||||||
|
_ => (),
|
||||||
|
};
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue