From 64d98f8ee037282c35007b64c2649055c56af1db Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Wed, 17 Apr 2024 14:19:03 +0200 Subject: Merging upstream version 1.68.2+dfsg1. Signed-off-by: Daniel Baumann --- .../src/thir/pattern/check_match.rs | 445 +++++++-------------- 1 file changed, 149 insertions(+), 296 deletions(-) (limited to 'compiler/rustc_mir_build/src/thir/pattern/check_match.rs') 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::>() - .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::>() - .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) { - 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) -> 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) -> 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 { -- cgit v1.2.3