Rollup merge of #123394 - compiler-errors:postfix-match-fixes, r=estebank
Postfix match fixes 1. Don't ice on `expr as Ty.match {}` 2. Fix the suggestion span for non-exhaustive matches to add `_ => todo!(),` Fixes #123383
This commit is contained in:
commit
deb48aa0f5
7 changed files with 89 additions and 33 deletions
|
@ -12,7 +12,7 @@ use rustc_ast::{InlineAsmOptions, InlineAsmTemplatePiece};
|
||||||
use rustc_errors::{DiagArgValue, IntoDiagArg};
|
use rustc_errors::{DiagArgValue, IntoDiagArg};
|
||||||
use rustc_hir as hir;
|
use rustc_hir as hir;
|
||||||
use rustc_hir::def_id::DefId;
|
use rustc_hir::def_id::DefId;
|
||||||
use rustc_hir::{BindingAnnotation, ByRef, RangeEnd};
|
use rustc_hir::{BindingAnnotation, ByRef, MatchSource, RangeEnd};
|
||||||
use rustc_index::newtype_index;
|
use rustc_index::newtype_index;
|
||||||
use rustc_index::IndexVec;
|
use rustc_index::IndexVec;
|
||||||
use rustc_middle::middle::region;
|
use rustc_middle::middle::region;
|
||||||
|
@ -358,6 +358,7 @@ pub enum ExprKind<'tcx> {
|
||||||
scrutinee: ExprId,
|
scrutinee: ExprId,
|
||||||
scrutinee_hir_id: hir::HirId,
|
scrutinee_hir_id: hir::HirId,
|
||||||
arms: Box<[ArmId]>,
|
arms: Box<[ArmId]>,
|
||||||
|
match_source: MatchSource,
|
||||||
},
|
},
|
||||||
/// A block.
|
/// A block.
|
||||||
Block {
|
Block {
|
||||||
|
|
|
@ -456,8 +456,8 @@ pub enum UnusedUnsafeEnclosing {
|
||||||
|
|
||||||
pub(crate) struct NonExhaustivePatternsTypeNotEmpty<'p, 'tcx, 'm> {
|
pub(crate) struct NonExhaustivePatternsTypeNotEmpty<'p, 'tcx, 'm> {
|
||||||
pub cx: &'m RustcPatCtxt<'p, 'tcx>,
|
pub cx: &'m RustcPatCtxt<'p, 'tcx>,
|
||||||
pub expr_span: Span,
|
pub scrut_span: Span,
|
||||||
pub span: Span,
|
pub braces_span: Option<Span>,
|
||||||
pub ty: Ty<'tcx>,
|
pub ty: Ty<'tcx>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -465,7 +465,7 @@ impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for NonExhaustivePatternsTypeNo
|
||||||
fn into_diag(self, dcx: &'a DiagCtxt, level: Level) -> Diag<'_, G> {
|
fn into_diag(self, dcx: &'a DiagCtxt, level: Level) -> Diag<'_, G> {
|
||||||
let mut diag =
|
let mut diag =
|
||||||
Diag::new(dcx, level, fluent::mir_build_non_exhaustive_patterns_type_not_empty);
|
Diag::new(dcx, level, fluent::mir_build_non_exhaustive_patterns_type_not_empty);
|
||||||
diag.span(self.span);
|
diag.span(self.scrut_span);
|
||||||
diag.code(E0004);
|
diag.code(E0004);
|
||||||
let peeled_ty = self.ty.peel_refs();
|
let peeled_ty = self.ty.peel_refs();
|
||||||
diag.arg("ty", self.ty);
|
diag.arg("ty", self.ty);
|
||||||
|
@ -502,26 +502,19 @@ impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for NonExhaustivePatternsTypeNo
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut suggestion = None;
|
|
||||||
let sm = self.cx.tcx.sess.source_map();
|
let sm = self.cx.tcx.sess.source_map();
|
||||||
if self.span.eq_ctxt(self.expr_span) {
|
if let Some(braces_span) = self.braces_span {
|
||||||
// Get the span for the empty match body `{}`.
|
// Get the span for the empty match body `{}`.
|
||||||
let (indentation, more) = if let Some(snippet) = sm.indentation_before(self.span) {
|
let (indentation, more) = if let Some(snippet) = sm.indentation_before(self.scrut_span)
|
||||||
|
{
|
||||||
(format!("\n{snippet}"), " ")
|
(format!("\n{snippet}"), " ")
|
||||||
} else {
|
} else {
|
||||||
(" ".to_string(), "")
|
(" ".to_string(), "")
|
||||||
};
|
};
|
||||||
suggestion = Some((
|
|
||||||
self.span.shrink_to_hi().with_hi(self.expr_span.hi()),
|
|
||||||
format!(" {{{indentation}{more}_ => todo!(),{indentation}}}",),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some((span, sugg)) = suggestion {
|
|
||||||
diag.span_suggestion_verbose(
|
diag.span_suggestion_verbose(
|
||||||
span,
|
braces_span,
|
||||||
fluent::mir_build_suggestion,
|
fluent::mir_build_suggestion,
|
||||||
sugg,
|
format!(" {{{indentation}{more}_ => todo!(),{indentation}}}"),
|
||||||
Applicability::HasPlaceholders,
|
Applicability::HasPlaceholders,
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -716,10 +716,11 @@ impl<'tcx> Cx<'tcx> {
|
||||||
then: self.mirror_expr(then),
|
then: self.mirror_expr(then),
|
||||||
else_opt: else_opt.map(|el| self.mirror_expr(el)),
|
else_opt: else_opt.map(|el| self.mirror_expr(el)),
|
||||||
},
|
},
|
||||||
hir::ExprKind::Match(discr, arms, _) => ExprKind::Match {
|
hir::ExprKind::Match(discr, arms, match_source) => ExprKind::Match {
|
||||||
scrutinee: self.mirror_expr(discr),
|
scrutinee: self.mirror_expr(discr),
|
||||||
scrutinee_hir_id: discr.hir_id,
|
scrutinee_hir_id: discr.hir_id,
|
||||||
arms: arms.iter().map(|a| self.convert_arm(a)).collect(),
|
arms: arms.iter().map(|a| self.convert_arm(a)).collect(),
|
||||||
|
match_source,
|
||||||
},
|
},
|
||||||
hir::ExprKind::Loop(body, ..) => {
|
hir::ExprKind::Loop(body, ..) => {
|
||||||
let block_ty = self.typeck_results().node_type(body.hir_id);
|
let block_ty = self.typeck_results().node_type(body.hir_id);
|
||||||
|
|
|
@ -144,16 +144,8 @@ impl<'p, 'tcx> Visitor<'p, 'tcx> for MatchVisitor<'p, 'tcx> {
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
ExprKind::Match { scrutinee, scrutinee_hir_id, box ref arms } => {
|
ExprKind::Match { scrutinee, scrutinee_hir_id: _, box ref arms, match_source } => {
|
||||||
let source = match ex.span.desugaring_kind() {
|
self.check_match(scrutinee, arms, match_source, ex.span);
|
||||||
Some(DesugaringKind::ForLoop) => hir::MatchSource::ForLoopDesugar,
|
|
||||||
Some(DesugaringKind::QuestionMark) => {
|
|
||||||
hir::MatchSource::TryDesugar(scrutinee_hir_id)
|
|
||||||
}
|
|
||||||
Some(DesugaringKind::Await) => hir::MatchSource::AwaitDesugar,
|
|
||||||
_ => hir::MatchSource::Normal,
|
|
||||||
};
|
|
||||||
self.check_match(scrutinee, arms, source, ex.span);
|
|
||||||
}
|
}
|
||||||
ExprKind::Let { box ref pat, expr } => {
|
ExprKind::Let { box ref pat, expr } => {
|
||||||
self.check_let(pat, Some(expr), ex.span);
|
self.check_let(pat, Some(expr), ex.span);
|
||||||
|
@ -505,8 +497,41 @@ impl<'p, 'tcx> MatchVisitor<'p, 'tcx> {
|
||||||
None,
|
None,
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
|
// span after scrutinee, or after `.match`. That is, the braces, arms,
|
||||||
|
// and any whitespace preceding the braces.
|
||||||
|
let braces_span = match source {
|
||||||
|
hir::MatchSource::Normal => scrut
|
||||||
|
.span
|
||||||
|
.find_ancestor_in_same_ctxt(expr_span)
|
||||||
|
.map(|scrut_span| scrut_span.shrink_to_hi().with_hi(expr_span.hi())),
|
||||||
|
hir::MatchSource::Postfix => {
|
||||||
|
// This is horrendous, and we should deal with it by just
|
||||||
|
// stashing the span of the braces somewhere (like in the match source).
|
||||||
|
scrut.span.find_ancestor_in_same_ctxt(expr_span).and_then(|scrut_span| {
|
||||||
|
let sm = self.tcx.sess.source_map();
|
||||||
|
let brace_span = sm.span_extend_to_next_char(scrut_span, '{', true);
|
||||||
|
if sm.span_to_snippet(sm.next_point(brace_span)).as_deref() == Ok("{") {
|
||||||
|
let sp = brace_span.shrink_to_hi().with_hi(expr_span.hi());
|
||||||
|
// We also need to extend backwards for whitespace
|
||||||
|
sm.span_extend_prev_while(sp, |c| c.is_whitespace()).ok()
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
hir::MatchSource::ForLoopDesugar
|
||||||
|
| hir::MatchSource::TryDesugar(_)
|
||||||
|
| hir::MatchSource::AwaitDesugar
|
||||||
|
| hir::MatchSource::FormatArgs => None,
|
||||||
|
};
|
||||||
self.error = Err(report_non_exhaustive_match(
|
self.error = Err(report_non_exhaustive_match(
|
||||||
&cx, self.thir, scrut.ty, scrut.span, witnesses, arms, expr_span,
|
&cx,
|
||||||
|
self.thir,
|
||||||
|
scrut.ty,
|
||||||
|
scrut.span,
|
||||||
|
witnesses,
|
||||||
|
arms,
|
||||||
|
braces_span,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -929,7 +954,7 @@ fn report_non_exhaustive_match<'p, 'tcx>(
|
||||||
sp: Span,
|
sp: Span,
|
||||||
witnesses: Vec<WitnessPat<'p, 'tcx>>,
|
witnesses: Vec<WitnessPat<'p, 'tcx>>,
|
||||||
arms: &[ArmId],
|
arms: &[ArmId],
|
||||||
expr_span: Span,
|
braces_span: Option<Span>,
|
||||||
) -> ErrorGuaranteed {
|
) -> ErrorGuaranteed {
|
||||||
let is_empty_match = arms.is_empty();
|
let is_empty_match = arms.is_empty();
|
||||||
let non_empty_enum = match scrut_ty.kind() {
|
let non_empty_enum = match scrut_ty.kind() {
|
||||||
|
@ -941,8 +966,8 @@ fn report_non_exhaustive_match<'p, 'tcx>(
|
||||||
if is_empty_match && !non_empty_enum {
|
if is_empty_match && !non_empty_enum {
|
||||||
return cx.tcx.dcx().emit_err(NonExhaustivePatternsTypeNotEmpty {
|
return cx.tcx.dcx().emit_err(NonExhaustivePatternsTypeNotEmpty {
|
||||||
cx,
|
cx,
|
||||||
expr_span,
|
scrut_span: sp,
|
||||||
span: sp,
|
braces_span,
|
||||||
ty: scrut_ty,
|
ty: scrut_ty,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -1028,7 +1053,7 @@ fn report_non_exhaustive_match<'p, 'tcx>(
|
||||||
let mut suggestion = None;
|
let mut suggestion = None;
|
||||||
let sm = cx.tcx.sess.source_map();
|
let sm = cx.tcx.sess.source_map();
|
||||||
match arms {
|
match arms {
|
||||||
[] if sp.eq_ctxt(expr_span) => {
|
[] if let Some(braces_span) = braces_span => {
|
||||||
// Get the span for the empty match body `{}`.
|
// Get the span for the empty match body `{}`.
|
||||||
let (indentation, more) = if let Some(snippet) = sm.indentation_before(sp) {
|
let (indentation, more) = if let Some(snippet) = sm.indentation_before(sp) {
|
||||||
(format!("\n{snippet}"), " ")
|
(format!("\n{snippet}"), " ")
|
||||||
|
@ -1036,7 +1061,7 @@ fn report_non_exhaustive_match<'p, 'tcx>(
|
||||||
(" ".to_string(), "")
|
(" ".to_string(), "")
|
||||||
};
|
};
|
||||||
suggestion = Some((
|
suggestion = Some((
|
||||||
sp.shrink_to_hi().with_hi(expr_span.hi()),
|
braces_span,
|
||||||
format!(" {{{indentation}{more}{suggested_arm},{indentation}}}",),
|
format!(" {{{indentation}{more}{suggested_arm},{indentation}}}",),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
|
@ -860,6 +860,7 @@ impl<'a> Parser<'a> {
|
||||||
ExprKind::MethodCall(_) => "a method call",
|
ExprKind::MethodCall(_) => "a method call",
|
||||||
ExprKind::Call(_, _) => "a function call",
|
ExprKind::Call(_, _) => "a function call",
|
||||||
ExprKind::Await(_, _) => "`.await`",
|
ExprKind::Await(_, _) => "`.await`",
|
||||||
|
ExprKind::Match(_, _, MatchKind::Postfix) => "a postfix match",
|
||||||
ExprKind::Err(_) => return Ok(with_postfix),
|
ExprKind::Err(_) => return Ok(with_postfix),
|
||||||
_ => unreachable!("parse_dot_or_call_expr_with_ shouldn't produce this"),
|
_ => unreachable!("parse_dot_or_call_expr_with_ shouldn't produce this"),
|
||||||
}
|
}
|
||||||
|
|
7
tests/ui/match/postfix-match/match-after-as.rs
Normal file
7
tests/ui/match/postfix-match/match-after-as.rs
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
#![feature(postfix_match)]
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
1 as i32.match {};
|
||||||
|
//~^ ERROR cast cannot be followed by a postfix match
|
||||||
|
//~| ERROR non-exhaustive patterns
|
||||||
|
}
|
28
tests/ui/match/postfix-match/match-after-as.stderr
Normal file
28
tests/ui/match/postfix-match/match-after-as.stderr
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
error: cast cannot be followed by a postfix match
|
||||||
|
--> $DIR/match-after-as.rs:4:5
|
||||||
|
|
|
||||||
|
LL | 1 as i32.match {};
|
||||||
|
| ^^^^^^^^
|
||||||
|
|
|
||||||
|
help: try surrounding the expression in parentheses
|
||||||
|
|
|
||||||
|
LL | (1 as i32).match {};
|
||||||
|
| + +
|
||||||
|
|
||||||
|
error[E0004]: non-exhaustive patterns: type `i32` is non-empty
|
||||||
|
--> $DIR/match-after-as.rs:4:5
|
||||||
|
|
|
||||||
|
LL | 1 as i32.match {};
|
||||||
|
| ^^^^^^^^
|
||||||
|
|
|
||||||
|
= note: the matched value is of type `i32`
|
||||||
|
help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern as shown
|
||||||
|
|
|
||||||
|
LL ~ 1 as i32.match {
|
||||||
|
LL + _ => todo!(),
|
||||||
|
LL ~ };
|
||||||
|
|
|
||||||
|
|
||||||
|
error: aborting due to 2 previous errors
|
||||||
|
|
||||||
|
For more information about this error, try `rustc --explain E0004`.
|
Loading…
Add table
Add a link
Reference in a new issue