summaryrefslogtreecommitdiffstats
path: root/compiler/rustc_mir_build/src/thir/pattern/check_match.rs
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/rustc_mir_build/src/thir/pattern/check_match.rs')
-rw-r--r--compiler/rustc_mir_build/src/thir/pattern/check_match.rs445
1 files changed, 149 insertions, 296 deletions
diff --git a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs
index e369dba55..34e637f59 100644
--- a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs
+++ b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs
@@ -4,18 +4,22 @@ use super::usefulness::{
};
use super::{PatCtxt, PatternError};
+use crate::errors::*;
+
+use hir::{ExprKind, PatKind};
use rustc_arena::TypedArena;
-use rustc_ast::Mutability;
+use rustc_ast::{LitKind, Mutability};
use rustc_errors::{
- error_code, pluralize, struct_span_err, Applicability, DelayDm, Diagnostic, DiagnosticBuilder,
- ErrorGuaranteed, MultiSpan,
+ struct_span_err, Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed, MultiSpan,
};
use rustc_hir as hir;
use rustc_hir::def::*;
use rustc_hir::def_id::DefId;
use rustc_hir::intravisit::{self, Visitor};
use rustc_hir::{HirId, Pat};
+use rustc_middle::ty::print::with_no_trimmed_paths;
use rustc_middle::ty::{self, AdtDef, Ty, TyCtxt};
+
use rustc_session::lint::builtin::{
BINDINGS_WITH_VARIANT_NAME, IRREFUTABLE_LET_PATTERNS, UNREACHABLE_PATTERNS,
};
@@ -107,28 +111,20 @@ impl PatCtxt<'_, '_> {
for error in &self.errors {
match *error {
PatternError::StaticInPattern(span) => {
- self.span_e0158(span, "statics cannot be referenced in patterns")
+ self.tcx.sess.emit_err(StaticInPattern { span });
}
PatternError::AssocConstInPattern(span) => {
- self.span_e0158(span, "associated consts cannot be referenced in patterns")
+ self.tcx.sess.emit_err(AssocConstInPattern { span });
}
PatternError::ConstParamInPattern(span) => {
- self.span_e0158(span, "const parameters cannot be referenced in patterns")
+ self.tcx.sess.emit_err(ConstParamInPattern { span });
}
PatternError::NonConstPath(span) => {
- rustc_middle::mir::interpret::struct_error(
- self.tcx.at(span),
- "runtime values cannot be referenced in patterns",
- )
- .emit();
+ self.tcx.sess.emit_err(NonConstPath { span });
}
}
}
}
-
- fn span_e0158(&self, span: Span, text: &str) {
- struct_span_err!(self.tcx.sess, span, E0158, "{}", text).emit();
- }
}
impl<'p, 'tcx> MatchVisitor<'_, 'p, 'tcx> {
@@ -251,14 +247,14 @@ impl<'p, 'tcx> MatchVisitor<'_, 'p, 'tcx> {
fn check_let_chain(&mut self, cx: &mut MatchCheckCtxt<'p, 'tcx>, pat_id: HirId) -> bool {
let hir = self.tcx.hir();
- let parent = hir.get_parent_node(pat_id);
+ let parent = hir.parent_id(pat_id);
// First, figure out if the given pattern is part of a let chain,
// and if so, obtain the top node of the chain.
let mut top = parent;
let mut part_of_chain = false;
loop {
- let new_top = hir.get_parent_node(top);
+ let new_top = hir.parent_id(top);
if let hir::Node::Expr(
hir::Expr {
kind: hir::ExprKind::Binary(Spanned { node: hir::BinOpKind::And, .. }, lhs, rhs),
@@ -345,29 +341,6 @@ impl<'p, 'tcx> MatchVisitor<'_, 'p, 'tcx> {
);
return true;
}
- let lint_affix = |affix: &[Option<(Span, bool)>], kind, suggestion| {
- let span_start = affix[0].unwrap().0;
- let span_end = affix.last().unwrap().unwrap().0;
- let span = span_start.to(span_end);
- let cnt = affix.len();
- let s = pluralize!(cnt);
- cx.tcx.struct_span_lint_hir(
- IRREFUTABLE_LET_PATTERNS,
- top,
- span,
- format!("{kind} irrefutable pattern{s} in let chain"),
- |lint| {
- lint.note(format!(
- "{these} pattern{s} will always match",
- these = pluralize!("this", cnt),
- ))
- .help(format!(
- "consider moving {} {suggestion}",
- if cnt > 1 { "them" } else { "it" }
- ))
- },
- );
- };
if let Some(until) = chain_refutabilities.iter().position(|r| !matches!(*r, Some((_, false)))) && until > 0 {
// The chain has a non-zero prefix of irrefutable `let` statements.
@@ -381,13 +354,21 @@ impl<'p, 'tcx> MatchVisitor<'_, 'p, 'tcx> {
if !matches!(let_source, LetSource::WhileLet | LetSource::IfLetGuard) {
// Emit the lint
let prefix = &chain_refutabilities[..until];
- lint_affix(prefix, "leading", "outside of the construct");
+ let span_start = prefix[0].unwrap().0;
+ let span_end = prefix.last().unwrap().unwrap().0;
+ let span = span_start.to(span_end);
+ let count = prefix.len();
+ cx.tcx.emit_spanned_lint(IRREFUTABLE_LET_PATTERNS, top, span, LeadingIrrefutableLetPatterns { count });
}
}
if let Some(from) = chain_refutabilities.iter().rposition(|r| !matches!(*r, Some((_, false)))) && from != (chain_refutabilities.len() - 1) {
// The chain has a non-empty suffix of irrefutable `let` statements
let suffix = &chain_refutabilities[from + 1..];
- lint_affix(suffix, "trailing", "into the body");
+ let span_start = suffix[0].unwrap().0;
+ let span_end = suffix.last().unwrap().unwrap().0;
+ let span = span_start.to(span_end);
+ let count = suffix.len();
+ cx.tcx.emit_spanned_lint(IRREFUTABLE_LET_PATTERNS, top, span, TrailingIrrefutableLetPatterns { count });
}
true
}
@@ -397,8 +378,8 @@ impl<'p, 'tcx> MatchVisitor<'_, 'p, 'tcx> {
let pattern = self.lower_pattern(&mut cx, pat, &mut false);
let pattern_ty = pattern.ty();
- let arms = vec![MatchArm { pat: pattern, hir_id: pat.hir_id, has_guard: false }];
- let report = compute_match_usefulness(&cx, &arms, pat.hir_id, pattern_ty);
+ let arm = MatchArm { pat: pattern, hir_id: pat.hir_id, has_guard: false };
+ let report = compute_match_usefulness(&cx, &[arm], pat.hir_id, pattern_ty);
// Note: we ignore whether the pattern is unreachable (i.e. whether the type is empty). We
// only care about exhaustiveness here.
@@ -409,145 +390,84 @@ impl<'p, 'tcx> MatchVisitor<'_, 'p, 'tcx> {
return;
}
- let joined_patterns = joined_uncovered_patterns(&cx, &witnesses);
-
- let mut bindings = vec![];
-
- let mut err = struct_span_err!(
- self.tcx.sess,
- pat.span,
- E0005,
- "refutable pattern in {}: {} not covered",
- origin,
- joined_patterns
- );
- let suggest_if_let = match &pat.kind {
- hir::PatKind::Path(hir::QPath::Resolved(None, path))
- if path.segments.len() == 1 && path.segments[0].args.is_none() =>
+ let (inform, interpreted_as_const, res_defined_here,let_suggestion, misc_suggestion) =
+ if let hir::PatKind::Path(hir::QPath::Resolved(
+ None,
+ hir::Path {
+ segments: &[hir::PathSegment { args: None, res, ident, .. }],
+ ..
+ },
+ )) = &pat.kind
{
- const_not_var(&mut err, cx.tcx, pat, path);
- false
- }
- _ => {
- pat.walk(&mut |pat: &hir::Pat<'_>| {
- match pat.kind {
- hir::PatKind::Binding(_, _, ident, _) => {
- bindings.push(ident);
+ (
+ None,
+ Some(InterpretedAsConst {
+ span: pat.span,
+ article: res.article(),
+ variable: ident.to_string().to_lowercase(),
+ res,
+ }),
+ try {
+ ResDefinedHere {
+ def_span: cx.tcx.hir().res_span(res)?,
+ res,
}
- _ => {}
+ },
+ None,
+ None,
+ )
+ } else if let Some(span) = sp && self.tcx.sess.source_map().is_span_accessible(span) {
+ let mut bindings = vec![];
+ pat.walk_always(&mut |pat: &hir::Pat<'_>| {
+ if let hir::PatKind::Binding(_, _, ident, _) = pat.kind {
+ bindings.push(ident);
}
- true
});
-
- err.span_label(pat.span, pattern_not_covered_label(&witnesses, &joined_patterns));
- true
- }
- };
-
- if let (Some(span), true) = (sp, suggest_if_let) {
- err.note(
- "`let` bindings require an \"irrefutable pattern\", like a `struct` or \
- an `enum` with only one variant",
- );
- if self.tcx.sess.source_map().is_span_accessible(span) {
let semi_span = span.shrink_to_hi().with_lo(span.hi() - BytePos(1));
let start_span = span.shrink_to_lo();
let end_span = semi_span.shrink_to_lo();
- err.multipart_suggestion(
- &format!(
- "you might want to use `if let` to ignore the variant{} that {} matched",
- pluralize!(witnesses.len()),
- match witnesses.len() {
- 1 => "isn't",
- _ => "aren't",
- },
- ),
- vec![
- match &bindings[..] {
- [] => (start_span, "if ".to_string()),
- [binding] => (start_span, format!("let {} = if ", binding)),
- bindings => (
- start_span,
- format!(
- "let ({}) = if ",
- bindings
- .iter()
- .map(|ident| ident.to_string())
- .collect::<Vec<_>>()
- .join(", ")
- ),
- ),
- },
- match &bindings[..] {
- [] => (semi_span, " { todo!() }".to_string()),
- [binding] => {
- (end_span, format!(" {{ {} }} else {{ todo!() }}", binding))
- }
- bindings => (
- end_span,
- format!(
- " {{ ({}) }} else {{ todo!() }}",
- bindings
- .iter()
- .map(|ident| ident.to_string())
- .collect::<Vec<_>>()
- .join(", ")
- ),
- ),
- },
- ],
- Applicability::HasPlaceholders,
- );
- if !bindings.is_empty() {
- err.span_suggestion_verbose(
- semi_span.shrink_to_lo(),
- &format!(
- "alternatively, you might want to use \
- let else to handle the variant{} that {} matched",
- pluralize!(witnesses.len()),
- match witnesses.len() {
- 1 => "isn't",
- _ => "aren't",
- },
- ),
- " else { todo!() }",
- Applicability::HasPlaceholders,
- );
- }
- }
- err.note(
- "for more information, visit \
- https://doc.rust-lang.org/book/ch18-02-refutability.html",
- );
- }
-
- adt_defined_here(&cx, &mut err, pattern_ty, &witnesses);
- err.note(&format!("the matched value is of type `{}`", pattern_ty));
- err.emit();
- }
-}
+ let count = witnesses.len();
+
+ // If the pattern to match is an integer literal:
+ let int_suggestion = if
+ let PatKind::Lit(expr) = &pat.kind
+ && bindings.is_empty()
+ && let ExprKind::Lit(Spanned { node: LitKind::Int(_, _), span }) = expr.kind {
+ // Then give a suggestion, the user might've meant to create a binding instead.
+ Some(MiscPatternSuggestion::AttemptedIntegerLiteral { start_span: span.shrink_to_lo() })
+ } else { None };
+
+ let let_suggestion = if bindings.is_empty() {SuggestLet::If{start_span, semi_span, count}} else{ SuggestLet::Else{end_span, count }};
+ (sp.map(|_|Inform), None, None, Some(let_suggestion), int_suggestion)
+ } else{
+ (sp.map(|_|Inform), None, None, None, None)
+ };
-/// A path pattern was interpreted as a constant, not a new variable.
-/// This caused an irrefutable match failure in e.g. `let`.
-fn const_not_var(err: &mut Diagnostic, tcx: TyCtxt<'_>, pat: &Pat<'_>, path: &hir::Path<'_>) {
- let descr = path.res.descr();
- err.span_label(
- pat.span,
- format!("interpreted as {} {} pattern, not a new variable", path.res.article(), descr,),
- );
+ let adt_defined_here = try {
+ let ty = pattern_ty.peel_refs();
+ let ty::Adt(def, _) = ty.kind() else { None? };
+ let adt_def_span = cx.tcx.hir().get_if_local(def.did())?.ident()?.span;
+ let mut variants = vec![];
- err.span_suggestion(
- pat.span,
- "introduce a variable instead",
- format!("{}_var", path.segments[0].ident).to_lowercase(),
- // Cannot use `MachineApplicable` as it's not really *always* correct
- // because there may be such an identifier in scope or the user maybe
- // really wanted to match against the constant. This is quite unlikely however.
- Applicability::MaybeIncorrect,
- );
+ for span in maybe_point_at_variant(&cx, *def, witnesses.iter().take(5)) {
+ variants.push(Variant { span });
+ }
+ AdtDefinedHere { adt_def_span, ty, variants }
+ };
- if let Some(span) = tcx.hir().res_span(path.res) {
- err.span_label(span, format!("{} defined here", descr));
+ self.tcx.sess.emit_err(PatternNotCovered {
+ span: pat.span,
+ origin,
+ uncovered: Uncovered::new(pat.span, &cx, witnesses),
+ inform,
+ interpreted_as_const,
+ _p: (),
+ pattern_ty,
+ let_suggestion,
+ misc_suggestion,
+ res_defined_here,
+ adt_defined_here,
+ });
}
}
@@ -568,32 +488,22 @@ fn check_for_bindings_named_same_as_variants(
})
{
let variant_count = edef.variants().len();
- cx.tcx.struct_span_lint_hir(
+ let ty_path = with_no_trimmed_paths!({
+ cx.tcx.def_path_str(edef.did())
+ });
+ cx.tcx.emit_spanned_lint(
BINDINGS_WITH_VARIANT_NAME,
p.hir_id,
p.span,
- DelayDm(|| format!(
- "pattern binding `{}` is named the same as one \
- of the variants of the type `{}`",
- ident, cx.tcx.def_path_str(edef.did())
- )),
- |lint| {
- let ty_path = cx.tcx.def_path_str(edef.did());
- lint.code(error_code!(E0170));
-
+ BindingsWithVariantName {
// If this is an irrefutable pattern, and there's > 1 variant,
// then we can't actually match on this. Applying the below
// suggestion would produce code that breaks on `check_irrefutable`.
- if rf == Refutable || variant_count == 1 {
- lint.span_suggestion(
- p.span,
- "to match on the variant, qualify the path",
- format!("{}::{}", ty_path, ident),
- Applicability::MachineApplicable,
- );
- }
-
- lint
+ suggestion: if rf == Refutable || variant_count == 1 {
+ Some(p.span)
+ } else { None },
+ ty_path,
+ ident,
},
)
}
@@ -611,14 +521,12 @@ fn pat_is_catchall(pat: &DeconstructedPat<'_, '_>) -> bool {
}
fn unreachable_pattern(tcx: TyCtxt<'_>, span: Span, id: HirId, catchall: Option<Span>) {
- tcx.struct_span_lint_hir(UNREACHABLE_PATTERNS, id, span, "unreachable pattern", |lint| {
- if let Some(catchall) = catchall {
- // We had a catchall pattern, hint at that.
- lint.span_label(span, "unreachable pattern");
- lint.span_label(catchall, "matches any value");
- }
- lint
- });
+ tcx.emit_spanned_lint(
+ UNREACHABLE_PATTERNS,
+ id,
+ span,
+ UnreachablePattern { span: if catchall.is_some() { Some(span) } else { None }, catchall },
+ );
}
fn irrefutable_let_pattern(tcx: TyCtxt<'_>, id: HirId, span: Span) {
@@ -634,67 +542,18 @@ fn irrefutable_let_patterns(
span: Span,
) {
macro_rules! emit_diag {
- (
- $lint:expr,
- $source_name:expr,
- $note_sufix:expr,
- $help_sufix:expr
- ) => {{
- let s = pluralize!(count);
- let these = pluralize!("this", count);
- tcx.struct_span_lint_hir(
- IRREFUTABLE_LET_PATTERNS,
- id,
- span,
- format!("irrefutable {} pattern{s}", $source_name),
- |lint| {
- lint.note(&format!(
- "{these} pattern{s} will always match, so the {}",
- $note_sufix
- ))
- .help(concat!("consider ", $help_sufix))
- },
- )
+ ($lint:tt) => {{
+ tcx.emit_spanned_lint(IRREFUTABLE_LET_PATTERNS, id, span, $lint { count });
}};
}
match source {
- LetSource::GenericLet => {
- emit_diag!(lint, "`let`", "`let` is useless", "removing `let`");
- }
- LetSource::IfLet => {
- emit_diag!(
- lint,
- "`if let`",
- "`if let` is useless",
- "replacing the `if let` with a `let`"
- );
- }
- LetSource::IfLetGuard => {
- emit_diag!(
- lint,
- "`if let` guard",
- "guard is useless",
- "removing the guard and adding a `let` inside the match arm"
- );
- }
- LetSource::LetElse => {
- emit_diag!(
- lint,
- "`let...else`",
- "`else` clause is useless",
- "removing the `else` clause"
- );
- }
- LetSource::WhileLet => {
- emit_diag!(
- lint,
- "`while let`",
- "loop will never exit",
- "instead using a `loop { ... }` with a `let` inside it"
- );
- }
- };
+ LetSource::GenericLet => emit_diag!(IrrefutableLetPatternsGenericLet),
+ LetSource::IfLet => emit_diag!(IrrefutableLetPatternsIfLet),
+ LetSource::IfLetGuard => emit_diag!(IrrefutableLetPatternsIfLetGuard),
+ LetSource::LetElse => emit_diag!(IrrefutableLetPatternsLetElse),
+ LetSource::WhileLet => emit_diag!(IrrefutableLetPatternsWhileLet),
+ }
}
fn is_let_irrefutable<'p, 'tcx>(
@@ -760,15 +619,17 @@ fn non_exhaustive_match<'p, 'tcx>(
// informative.
let mut err;
let pattern;
- let mut patterns_len = 0;
+ let patterns_len;
if is_empty_match && !non_empty_enum {
- err = create_e0004(
- cx.tcx.sess,
- sp,
- format!("non-exhaustive patterns: type `{}` is non-empty", scrut_ty),
- );
- pattern = "_".to_string();
+ cx.tcx.sess.emit_err(NonExhaustivePatternsTypeNotEmpty {
+ cx,
+ expr_span,
+ span: sp,
+ ty: scrut_ty,
+ });
+ return;
} else {
+ // FIXME: migration of this diagnostic will require list support
let joined_patterns = joined_uncovered_patterns(cx, &witnesses);
err = create_e0004(
cx.tcx.sess,
@@ -1039,24 +900,17 @@ fn check_borrow_conflicts_in_at_patterns(cx: &MatchVisitor<'_, '_, '_>, pat: &Pa
}
});
if !conflicts_ref.is_empty() {
- let occurs_because = format!(
- "move occurs because `{}` has type `{}` which does not implement the `Copy` trait",
+ sess.emit_err(BorrowOfMovedValue {
+ span: pat.span,
+ binding_span,
+ conflicts_ref,
name,
- typeck_results.node_type(pat.hir_id),
- );
- let mut err = sess.struct_span_err(pat.span, "borrow of moved value");
- err.span_label(binding_span, format!("value moved into `{}` here", name))
- .span_label(binding_span, occurs_because)
- .span_labels(conflicts_ref, "value borrowed here after move");
- if pat.span.contains(binding_span) {
- err.span_suggestion_verbose(
- binding_span.shrink_to_lo(),
- "borrow this binding in the pattern to avoid moving the value",
- "ref ".to_string(),
- Applicability::MachineApplicable,
- );
- }
- err.emit();
+ ty: typeck_results.node_type(pat.hir_id),
+ suggest_borrowing: pat
+ .span
+ .contains(binding_span)
+ .then(|| binding_span.shrink_to_lo()),
+ });
}
return;
}
@@ -1086,19 +940,18 @@ fn check_borrow_conflicts_in_at_patterns(cx: &MatchVisitor<'_, '_, '_>, pat: &Pa
// Report errors if any.
if !conflicts_mut_mut.is_empty() {
// Report mutability conflicts for e.g. `ref mut x @ Some(ref mut y)`.
- let mut err = sess
- .struct_span_err(pat.span, "cannot borrow value as mutable more than once at a time");
- err.span_label(binding_span, format!("first mutable borrow, by `{}`, occurs here", name));
- for (span, name) in conflicts_mut_mut {
- err.span_label(span, format!("another mutable borrow, by `{}`, occurs here", name));
+ let mut occurences = vec![];
+
+ for (span, name_mut) in conflicts_mut_mut {
+ occurences.push(MultipleMutBorrowOccurence::Mutable { span, name_mut });
}
- for (span, name) in conflicts_mut_ref {
- err.span_label(span, format!("also borrowed as immutable, by `{}`, here", name));
+ for (span, name_immut) in conflicts_mut_ref {
+ occurences.push(MultipleMutBorrowOccurence::Immutable { span, name_immut });
}
- for (span, name) in conflicts_move {
- err.span_label(span, format!("also moved into `{}` here", name));
+ for (span, name_moved) in conflicts_move {
+ occurences.push(MultipleMutBorrowOccurence::Moved { span, name_moved });
}
- err.emit();
+ sess.emit_err(MultipleMutBorrows { span: pat.span, binding_span, occurences, name });
} else if !conflicts_mut_ref.is_empty() {
// Report mutability conflicts for e.g. `ref x @ Some(ref mut y)` or the converse.
let (primary, also) = match mut_outer {
@@ -1140,7 +993,7 @@ pub enum LetSource {
fn let_source(tcx: TyCtxt<'_>, pat_id: HirId) -> LetSource {
let hir = tcx.hir();
- let parent = hir.get_parent_node(pat_id);
+ let parent = hir.parent_id(pat_id);
let_source_parent(tcx, parent, Some(pat_id))
}
@@ -1159,7 +1012,7 @@ fn let_source_parent(tcx: TyCtxt<'_>, parent: HirId, pat_id: Option<HirId>) -> L
_ => {}
}
- let parent_parent = hir.get_parent_node(parent);
+ let parent_parent = hir.parent_id(parent);
let parent_parent_node = hir.get(parent_parent);
match parent_parent_node {
hir::Node::Stmt(hir::Stmt { kind: hir::StmtKind::Local(_), .. }) => {
@@ -1171,8 +1024,8 @@ fn let_source_parent(tcx: TyCtxt<'_>, parent: HirId, pat_id: Option<HirId>) -> L
_ => {}
}
- 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 = hir.parent_id(parent_parent);
+ let parent_parent_parent_parent = hir.parent_id(parent_parent_parent);
let parent_parent_parent_parent_node = hir.get(parent_parent_parent_parent);
if let hir::Node::Expr(hir::Expr {