From fedee8d52405ae1d9526c6ee87a97b3d14b6d4fd Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Sun, 29 Oct 2023 04:11:45 +0100 Subject: [PATCH 01/12] Reorder --- .../src/thir/pattern/check_match.rs | 299 +++++++++--------- 1 file changed, 151 insertions(+), 148 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 933653e708e94..136f4210fe4c8 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs @@ -572,6 +572,111 @@ impl<'p, 'tcx> MatchVisitor<'_, 'p, 'tcx> { } } +/// Check if a by-value binding is by-value. That is, check if the binding's type is not `Copy`. +/// Check that there are no borrow or move conflicts in `binding @ subpat` patterns. +/// +/// For example, this would reject: +/// - `ref x @ Some(ref mut y)`, +/// - `ref mut x @ Some(ref y)`, +/// - `ref mut x @ Some(ref mut y)`, +/// - `ref mut? x @ Some(y)`, and +/// - `x @ Some(ref mut? y)`. +/// +/// This analysis is *not* subsumed by NLL. +fn check_borrow_conflicts_in_at_patterns<'tcx>(cx: &MatchVisitor<'_, '_, 'tcx>, pat: &Pat<'tcx>) { + // Extract `sub` in `binding @ sub`. + let PatKind::Binding { name, mode, ty, subpattern: Some(box ref sub), .. } = pat.kind else { + return; + }; + + let is_binding_by_move = |ty: Ty<'tcx>| !ty.is_copy_modulo_regions(cx.tcx, cx.param_env); + + let sess = cx.tcx.sess; + + // Get the binding move, extract the mutability if by-ref. + let mut_outer = match mode { + BindingMode::ByValue if is_binding_by_move(ty) => { + // We have `x @ pat` where `x` is by-move. Reject all borrows in `pat`. + let mut conflicts_ref = Vec::new(); + sub.each_binding(|_, mode, _, span| match mode { + BindingMode::ByValue => {} + BindingMode::ByRef(_) => conflicts_ref.push(span), + }); + if !conflicts_ref.is_empty() { + sess.emit_err(BorrowOfMovedValue { + binding_span: pat.span, + conflicts_ref, + name, + ty, + suggest_borrowing: Some(pat.span.shrink_to_lo()), + }); + } + return; + } + BindingMode::ByValue => return, + BindingMode::ByRef(m) => m.mutability(), + }; + + // We now have `ref $mut_outer binding @ sub` (semantically). + // Recurse into each binding in `sub` and find mutability or move conflicts. + let mut conflicts_move = Vec::new(); + let mut conflicts_mut_mut = Vec::new(); + let mut conflicts_mut_ref = Vec::new(); + sub.each_binding(|name, mode, ty, span| { + match mode { + BindingMode::ByRef(mut_inner) => match (mut_outer, mut_inner.mutability()) { + // Both sides are `ref`. + (Mutability::Not, Mutability::Not) => {} + // 2x `ref mut`. + (Mutability::Mut, Mutability::Mut) => { + conflicts_mut_mut.push(Conflict::Mut { span, name }) + } + (Mutability::Not, Mutability::Mut) => { + conflicts_mut_ref.push(Conflict::Mut { span, name }) + } + (Mutability::Mut, Mutability::Not) => { + conflicts_mut_ref.push(Conflict::Ref { span, name }) + } + }, + BindingMode::ByValue if is_binding_by_move(ty) => { + conflicts_move.push(Conflict::Moved { span, name }) // `ref mut?` + by-move conflict. + } + BindingMode::ByValue => {} // `ref mut?` + by-copy is fine. + } + }); + + let report_mut_mut = !conflicts_mut_mut.is_empty(); + let report_mut_ref = !conflicts_mut_ref.is_empty(); + let report_move_conflict = !conflicts_move.is_empty(); + + let mut occurrences = match mut_outer { + Mutability::Mut => vec![Conflict::Mut { span: pat.span, name }], + Mutability::Not => vec![Conflict::Ref { span: pat.span, name }], + }; + occurrences.extend(conflicts_mut_mut); + occurrences.extend(conflicts_mut_ref); + occurrences.extend(conflicts_move); + + // Report errors if any. + if report_mut_mut { + // Report mutability conflicts for e.g. `ref mut x @ Some(ref mut y)`. + sess.emit_err(MultipleMutBorrows { span: pat.span, occurrences }); + } else if report_mut_ref { + // Report mutability conflicts for e.g. `ref x @ Some(ref mut y)` or the converse. + match mut_outer { + Mutability::Mut => { + sess.emit_err(AlreadyMutBorrowed { span: pat.span, occurrences }); + } + Mutability::Not => { + sess.emit_err(AlreadyBorrowed { span: pat.span, occurrences }); + } + }; + } else if report_move_conflict { + // Report by-ref and by-move conflicts, e.g. `ref x @ y`. + sess.emit_err(MovedWhileBorrowed { span: pat.span, occurrences }); + } +} + fn check_for_bindings_named_same_as_variants( cx: &MatchVisitor<'_, '_, '_>, pat: &Pat<'_>, @@ -616,25 +721,6 @@ fn check_for_bindings_named_same_as_variants( }); } -/// Checks for common cases of "catchall" patterns that may not be intended as such. -fn pat_is_catchall(pat: &DeconstructedPat<'_, '_>) -> bool { - use Constructor::*; - match pat.ctor() { - Wildcard => true, - Single => pat.iter_fields().all(|pat| pat_is_catchall(pat)), - _ => false, - } -} - -fn unreachable_pattern(tcx: TyCtxt<'_>, span: Span, id: HirId, catchall: Option) { - tcx.emit_spanned_lint( - UNREACHABLE_PATTERNS, - id, - span, - UnreachablePattern { span: if catchall.is_some() { Some(span) } else { None }, catchall }, - ); -} - fn irrefutable_let_patterns( tcx: TyCtxt<'_>, id: HirId, @@ -680,11 +766,23 @@ fn report_arm_reachability<'p, 'tcx>( cx: &MatchCheckCtxt<'p, 'tcx>, report: &UsefulnessReport<'p, 'tcx>, ) { + let report_unreachable_pattern = |span, hir_id, catchall: Option| { + cx.tcx.emit_spanned_lint( + UNREACHABLE_PATTERNS, + hir_id, + span, + UnreachablePattern { + span: if catchall.is_some() { Some(span) } else { None }, + catchall, + }, + ); + }; + use Reachability::*; let mut catchall = None; for (arm, is_useful) in report.arm_usefulness.iter() { match is_useful { - Unreachable => unreachable_pattern(cx.tcx, arm.pat.span(), arm.hir_id, catchall), + Unreachable => report_unreachable_pattern(arm.pat.span(), arm.hir_id, catchall), Reachable(unreachables) if unreachables.is_empty() => {} // The arm is reachable, but contains unreachable subpatterns (from or-patterns). Reachable(unreachables) => { @@ -692,7 +790,7 @@ fn report_arm_reachability<'p, 'tcx>( // Emit lints in the order in which they occur in the file. unreachables.sort_unstable(); for span in unreachables { - unreachable_pattern(cx.tcx, span, arm.hir_id, None); + report_unreachable_pattern(span, arm.hir_id, None); } } } @@ -702,22 +800,14 @@ fn report_arm_reachability<'p, 'tcx>( } } -fn collect_non_exhaustive_tys<'tcx>( - tcx: TyCtxt<'tcx>, - pat: &WitnessPat<'tcx>, - non_exhaustive_tys: &mut FxHashSet>, -) { - if matches!(pat.ctor(), Constructor::NonExhaustive) { - non_exhaustive_tys.insert(pat.ty()); - } - if let Constructor::IntRange(range) = pat.ctor() { - if range.is_beyond_boundaries(pat.ty(), tcx) { - // The range denotes the values before `isize::MIN` or the values after `usize::MAX`/`isize::MAX`. - non_exhaustive_tys.insert(pat.ty()); - } +/// Checks for common cases of "catchall" patterns that may not be intended as such. +fn pat_is_catchall(pat: &DeconstructedPat<'_, '_>) -> bool { + use Constructor::*; + match pat.ctor() { + Wildcard => true, + Single => pat.iter_fields().all(|pat| pat_is_catchall(pat)), + _ => false, } - pat.iter_fields() - .for_each(|field_pat| collect_non_exhaustive_tys(tcx, field_pat, non_exhaustive_tys)) } /// Report that a match is not exhaustive. @@ -755,7 +845,14 @@ fn non_exhaustive_match<'p, 'tcx>( sp, format!("non-exhaustive patterns: {joined_patterns} not covered"), ); - err.span_label(sp, pattern_not_covered_label(&witnesses, &joined_patterns)); + err.span_label( + sp, + format!( + "pattern{} {} not covered", + rustc_errors::pluralize!(witnesses.len()), + joined_patterns + ), + ); patterns_len = witnesses.len(); pattern = if witnesses.len() < 4 { witnesses @@ -910,7 +1007,7 @@ fn non_exhaustive_match<'p, 'tcx>( err.emit() } -pub(crate) fn joined_uncovered_patterns<'p, 'tcx>( +fn joined_uncovered_patterns<'p, 'tcx>( cx: &MatchCheckCtxt<'p, 'tcx>, witnesses: &[WitnessPat<'tcx>], ) -> String { @@ -931,11 +1028,22 @@ pub(crate) fn joined_uncovered_patterns<'p, 'tcx>( } } -pub(crate) fn pattern_not_covered_label( - witnesses: &[WitnessPat<'_>], - joined_patterns: &str, -) -> String { - format!("pattern{} {} not covered", rustc_errors::pluralize!(witnesses.len()), joined_patterns) +fn collect_non_exhaustive_tys<'tcx>( + tcx: TyCtxt<'tcx>, + pat: &WitnessPat<'tcx>, + non_exhaustive_tys: &mut FxHashSet>, +) { + if matches!(pat.ctor(), Constructor::NonExhaustive) { + non_exhaustive_tys.insert(pat.ty()); + } + if let Constructor::IntRange(range) = pat.ctor() { + if range.is_beyond_boundaries(pat.ty(), tcx) { + // The range denotes the values before `isize::MIN` or the values after `usize::MAX`/`isize::MAX`. + non_exhaustive_tys.insert(pat.ty()); + } + } + pat.iter_fields() + .for_each(|field_pat| collect_non_exhaustive_tys(tcx, field_pat, non_exhaustive_tys)) } /// Point at the definition of non-covered `enum` variants. @@ -997,108 +1105,3 @@ fn maybe_point_at_variant<'a, 'p: 'a, 'tcx: 'a>( } covered } - -/// Check if a by-value binding is by-value. That is, check if the binding's type is not `Copy`. -/// Check that there are no borrow or move conflicts in `binding @ subpat` patterns. -/// -/// For example, this would reject: -/// - `ref x @ Some(ref mut y)`, -/// - `ref mut x @ Some(ref y)`, -/// - `ref mut x @ Some(ref mut y)`, -/// - `ref mut? x @ Some(y)`, and -/// - `x @ Some(ref mut? y)`. -/// -/// This analysis is *not* subsumed by NLL. -fn check_borrow_conflicts_in_at_patterns<'tcx>(cx: &MatchVisitor<'_, '_, 'tcx>, pat: &Pat<'tcx>) { - // Extract `sub` in `binding @ sub`. - let PatKind::Binding { name, mode, ty, subpattern: Some(box ref sub), .. } = pat.kind else { - return; - }; - - let is_binding_by_move = |ty: Ty<'tcx>| !ty.is_copy_modulo_regions(cx.tcx, cx.param_env); - - let sess = cx.tcx.sess; - - // Get the binding move, extract the mutability if by-ref. - let mut_outer = match mode { - BindingMode::ByValue if is_binding_by_move(ty) => { - // We have `x @ pat` where `x` is by-move. Reject all borrows in `pat`. - let mut conflicts_ref = Vec::new(); - sub.each_binding(|_, mode, _, span| match mode { - BindingMode::ByValue => {} - BindingMode::ByRef(_) => conflicts_ref.push(span), - }); - if !conflicts_ref.is_empty() { - sess.emit_err(BorrowOfMovedValue { - binding_span: pat.span, - conflicts_ref, - name, - ty, - suggest_borrowing: Some(pat.span.shrink_to_lo()), - }); - } - return; - } - BindingMode::ByValue => return, - BindingMode::ByRef(m) => m.mutability(), - }; - - // We now have `ref $mut_outer binding @ sub` (semantically). - // Recurse into each binding in `sub` and find mutability or move conflicts. - let mut conflicts_move = Vec::new(); - let mut conflicts_mut_mut = Vec::new(); - let mut conflicts_mut_ref = Vec::new(); - sub.each_binding(|name, mode, ty, span| { - match mode { - BindingMode::ByRef(mut_inner) => match (mut_outer, mut_inner.mutability()) { - // Both sides are `ref`. - (Mutability::Not, Mutability::Not) => {} - // 2x `ref mut`. - (Mutability::Mut, Mutability::Mut) => { - conflicts_mut_mut.push(Conflict::Mut { span, name }) - } - (Mutability::Not, Mutability::Mut) => { - conflicts_mut_ref.push(Conflict::Mut { span, name }) - } - (Mutability::Mut, Mutability::Not) => { - conflicts_mut_ref.push(Conflict::Ref { span, name }) - } - }, - BindingMode::ByValue if is_binding_by_move(ty) => { - conflicts_move.push(Conflict::Moved { span, name }) // `ref mut?` + by-move conflict. - } - BindingMode::ByValue => {} // `ref mut?` + by-copy is fine. - } - }); - - let report_mut_mut = !conflicts_mut_mut.is_empty(); - let report_mut_ref = !conflicts_mut_ref.is_empty(); - let report_move_conflict = !conflicts_move.is_empty(); - - let mut occurrences = match mut_outer { - Mutability::Mut => vec![Conflict::Mut { span: pat.span, name }], - Mutability::Not => vec![Conflict::Ref { span: pat.span, name }], - }; - occurrences.extend(conflicts_mut_mut); - occurrences.extend(conflicts_mut_ref); - occurrences.extend(conflicts_move); - - // Report errors if any. - if report_mut_mut { - // Report mutability conflicts for e.g. `ref mut x @ Some(ref mut y)`. - sess.emit_err(MultipleMutBorrows { span: pat.span, occurrences }); - } else if report_mut_ref { - // Report mutability conflicts for e.g. `ref x @ Some(ref mut y)` or the converse. - match mut_outer { - Mutability::Mut => { - sess.emit_err(AlreadyMutBorrowed { span: pat.span, occurrences }); - } - Mutability::Not => { - sess.emit_err(AlreadyBorrowed { span: pat.span, occurrences }); - } - }; - } else if report_move_conflict { - // Report by-ref and by-move conflicts, e.g. `ref x @ y`. - sess.emit_err(MovedWhileBorrowed { span: pat.span, occurrences }); - } -} From 1f9a2f73e12b0bbfff21f78b220cf742e44e35b7 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Sun, 29 Oct 2023 04:36:06 +0100 Subject: [PATCH 02/12] Uncomplicate check_let_chain --- .../src/thir/pattern/check_match.rs | 80 ++++++++----------- 1 file changed, 35 insertions(+), 45 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 136f4210fe4c8..9cae42c435903 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs @@ -150,8 +150,8 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for MatchVisitor<'a, '_, 'tcx> { ExprKind::Let { box ref pat, expr } => { self.check_let(pat, expr, self.let_source, ex.span); } - ExprKind::LogicalOp { op: LogicalOp::And, lhs, rhs } => { - self.check_let_chain(self.let_source, ex.span, lhs, rhs); + ExprKind::LogicalOp { op: LogicalOp::And, .. } => { + self.check_let_chain(self.let_source, ex); } _ => {} }; @@ -326,71 +326,61 @@ impl<'p, 'tcx> MatchVisitor<'_, 'p, 'tcx> { } #[instrument(level = "trace", skip(self))] - fn check_let_chain( - &mut self, - let_source: LetSource, - top_expr_span: Span, - mut lhs: ExprId, - rhs: ExprId, - ) { + fn check_let_chain(&mut self, let_source: LetSource, expr: &Expr<'tcx>) { if let LetSource::None = let_source { return; } - // Lint level enclosing the next `lhs`. - let mut cur_lint_level = self.lint_level; + let top_expr_span = expr.span; + + // Lint level enclosing `next_expr`. + let mut next_expr_lint_level = self.lint_level; // Obtain the refutabilities of all exprs in the chain, // and record chain members that aren't let exprs. let mut chain_refutabilities = Vec::new(); let mut error = Ok(()); - let mut add = |expr: ExprId, mut local_lint_level| { - // `local_lint_level` is the lint level enclosing the pattern inside `expr`. - let mut expr = &self.thir[expr]; - debug!(?expr, ?local_lint_level, "add"); + let mut next_expr = Some(expr); + while let Some(mut expr) = next_expr { + while let ExprKind::Scope { value, lint_level, .. } = expr.kind { + if let LintLevel::Explicit(hir_id) = lint_level { + next_expr_lint_level = hir_id + } + expr = &self.thir[value]; + } + if let ExprKind::LogicalOp { op: LogicalOp::And, lhs, rhs } = expr.kind { + expr = &self.thir[rhs]; + // Let chains recurse on the left, so we recurse into the lhs. + next_expr = Some(&self.thir[lhs]); + } else { + next_expr = None; + } + + // Lint level enclosing `expr`. + let mut expr_lint_level = next_expr_lint_level; // Fast-forward through scopes. while let ExprKind::Scope { value, lint_level, .. } = expr.kind { if let LintLevel::Explicit(hir_id) = lint_level { - local_lint_level = hir_id + expr_lint_level = hir_id } expr = &self.thir[value]; } - debug!(?expr, ?local_lint_level, "after scopes"); - match expr.kind { + let value = match expr.kind { ExprKind::Let { box ref pat, expr: _ } => { if let Err(err) = pat.pat_error_reported() { error = Err(err); - return None; + None + } else { + let mut ncx = self.new_cx(expr_lint_level, true); + let tpat = self.lower_pattern(&mut ncx, pat); + let refutable = !is_let_irrefutable(&mut ncx, expr_lint_level, tpat); + Some((expr.span, refutable)) } - let mut ncx = self.new_cx(local_lint_level, true); - let tpat = self.lower_pattern(&mut ncx, pat); - let refutable = !is_let_irrefutable(&mut ncx, local_lint_level, tpat); - Some((expr.span, refutable)) } _ => None, - } - }; - - // Let chains recurse on the left, so we start by adding the rightmost. - chain_refutabilities.push(add(rhs, cur_lint_level)); - - loop { - while let ExprKind::Scope { value, lint_level, .. } = self.thir[lhs].kind { - if let LintLevel::Explicit(hir_id) = lint_level { - cur_lint_level = hir_id - } - lhs = value; - } - if let ExprKind::LogicalOp { op: LogicalOp::And, lhs: new_lhs, rhs: expr } = - self.thir[lhs].kind - { - chain_refutabilities.push(add(expr, cur_lint_level)); - lhs = new_lhs; - } else { - chain_refutabilities.push(add(lhs, cur_lint_level)); - break; - } + }; + chain_refutabilities.push(value); } debug!(?chain_refutabilities); chain_refutabilities.reverse(); From 380c56c6b3260bf96bd7cfd98c2095455a621d2a Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Sun, 29 Oct 2023 04:40:03 +0100 Subject: [PATCH 03/12] Check pattern error while lowering --- .../src/thir/pattern/check_match.rs | 71 ++++++++----------- 1 file changed, 29 insertions(+), 42 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 9cae42c435903..31e1b779bfd35 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs @@ -81,6 +81,9 @@ struct MatchVisitor<'a, 'p, 'tcx> { lint_level: HirId, let_source: LetSource, pattern_arena: &'p TypedArena>, + /// Tracks if we encountered an error while checking this body. That the first function to + /// report it stores it here. Some functions return `Result` to allow callers to short-circuit + /// on error, but callers don't need to store it here again. error: Result<(), ErrorGuaranteed>, } @@ -211,11 +214,16 @@ impl<'p, 'tcx> MatchVisitor<'_, 'p, 'tcx> { } fn lower_pattern( - &self, - cx: &mut MatchCheckCtxt<'p, 'tcx>, + &mut self, + cx: &MatchCheckCtxt<'p, 'tcx>, pattern: &Pat<'tcx>, - ) -> &'p DeconstructedPat<'p, 'tcx> { - cx.pattern_arena.alloc(DeconstructedPat::from_pat(cx, &pattern)) + ) -> Result<&'p DeconstructedPat<'p, 'tcx>, ErrorGuaranteed> { + if let Err(err) = pattern.pat_error_reported() { + self.error = Err(err); + Err(err) + } else { + Ok(cx.pattern_arena.alloc(DeconstructedPat::from_pat(cx, pattern))) + } } fn new_cx(&self, hir_id: HirId, refutable: bool) -> MatchCheckCtxt<'p, 'tcx> { @@ -233,13 +241,9 @@ impl<'p, 'tcx> MatchVisitor<'_, 'p, 'tcx> { if let LetSource::None = source { return; } - if let Err(err) = pat.pat_error_reported() { - self.error = Err(err); - return; - } self.check_patterns(pat, Refutable); let mut cx = self.new_cx(self.lint_level, true); - let tpat = self.lower_pattern(&mut cx, pat); + let Ok(tpat) = self.lower_pattern(&cx, pat) else { return }; self.check_let_reachability(&mut cx, self.lint_level, source, tpat, span); } @@ -252,31 +256,22 @@ impl<'p, 'tcx> MatchVisitor<'_, 'p, 'tcx> { ) { let mut cx = self.new_cx(self.lint_level, true); + let mut tarms = Vec::with_capacity(arms.len()); for &arm in arms { // Check the arm for some things unrelated to exhaustiveness. let arm = &self.thir.arms[arm]; self.with_lint_level(arm.lint_level, |this| { this.check_patterns(&arm.pattern, Refutable); }); - if let Err(err) = arm.pattern.pat_error_reported() { - self.error = Err(err); - return; - } + let hir_id = match arm.lint_level { + LintLevel::Explicit(hir_id) => hir_id, + LintLevel::Inherited => self.lint_level, + }; + let Ok(pat) = self.lower_pattern(&mut cx, &arm.pattern) else { return }; + let arm = MatchArm { pat, hir_id, has_guard: arm.guard.is_some() }; + tarms.push(arm); } - let tarms: Vec<_> = arms - .iter() - .map(|&arm| { - let arm = &self.thir.arms[arm]; - let hir_id = match arm.lint_level { - LintLevel::Explicit(hir_id) => hir_id, - LintLevel::Inherited => self.lint_level, - }; - let pat = self.lower_pattern(&mut cx, &arm.pattern); - MatchArm { pat, hir_id, has_guard: arm.guard.is_some() } - }) - .collect(); - let scrut = &self.thir[scrut]; let scrut_ty = scrut.ty; let report = compute_match_usefulness(&cx, &tarms, self.lint_level, scrut_ty, scrut.span); @@ -340,7 +335,7 @@ impl<'p, 'tcx> MatchVisitor<'_, 'p, 'tcx> { // and record chain members that aren't let exprs. let mut chain_refutabilities = Vec::new(); - let mut error = Ok(()); + let mut got_lowering_error = false; let mut next_expr = Some(expr); while let Some(mut expr) = next_expr { while let ExprKind::Scope { value, lint_level, .. } = expr.kind { @@ -368,14 +363,13 @@ impl<'p, 'tcx> MatchVisitor<'_, 'p, 'tcx> { } let value = match expr.kind { ExprKind::Let { box ref pat, expr: _ } => { - if let Err(err) = pat.pat_error_reported() { - error = Err(err); - None - } else { - let mut ncx = self.new_cx(expr_lint_level, true); - let tpat = self.lower_pattern(&mut ncx, pat); + let mut ncx = self.new_cx(expr_lint_level, true); + if let Ok(tpat) = self.lower_pattern(&mut ncx, pat) { let refutable = !is_let_irrefutable(&mut ncx, expr_lint_level, tpat); Some((expr.span, refutable)) + } else { + got_lowering_error = true; + None } } _ => None, @@ -385,8 +379,7 @@ impl<'p, 'tcx> MatchVisitor<'_, 'p, 'tcx> { debug!(?chain_refutabilities); chain_refutabilities.reverse(); - if error.is_err() { - self.error = error; + if got_lowering_error { return; } @@ -452,15 +445,9 @@ impl<'p, 'tcx> MatchVisitor<'_, 'p, 'tcx> { #[instrument(level = "trace", skip(self))] fn check_irrefutable(&mut self, pat: &Pat<'tcx>, origin: &str, sp: Option) { - // If we got errors while lowering, don't emit anything more. - if let Err(err) = pat.pat_error_reported() { - self.error = Err(err); - return; - } - let mut cx = self.new_cx(self.lint_level, false); - let pattern = self.lower_pattern(&mut cx, pat); + let Ok(pattern) = self.lower_pattern(&mut cx, pat) else { return }; let pattern_ty = pattern.ty(); let arm = MatchArm { pat: pattern, hir_id: self.lint_level, has_guard: false }; let report = From d95f6a9532708e13d779e431384daa64b638703e Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Sun, 29 Oct 2023 04:43:25 +0100 Subject: [PATCH 04/12] Tweak diagnostic for consistency --- .../src/thir/pattern/check_match.rs | 6 +- .../empty-match.exhaustive_patterns.stderr | 68 +++++++++++-------- .../usefulness/empty-match.normal.stderr | 68 +++++++++++-------- tests/ui/pattern/usefulness/empty-match.rs | 12 +++- .../ui/pattern/usefulness/issue-35609.stderr | 3 + .../non-exhaustive-pattern-witness.stderr | 15 +++- 6 files changed, 111 insertions(+), 61 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 31e1b779bfd35..0e218cd8788e8 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs @@ -1033,10 +1033,8 @@ fn adt_defined_here<'p, 'tcx>( let ty = ty.peel_refs(); if let ty::Adt(def, _) = ty.kind() { let mut spans = vec![]; - if witnesses.len() < 5 { - for sp in maybe_point_at_variant(cx, *def, witnesses.iter()) { - spans.push(sp); - } + for sp in maybe_point_at_variant(cx, *def, witnesses.iter().take(5)) { + spans.push(sp); } let def_span = cx .tcx diff --git a/tests/ui/pattern/usefulness/empty-match.exhaustive_patterns.stderr b/tests/ui/pattern/usefulness/empty-match.exhaustive_patterns.stderr index 5b81a8c3d3c02..fce1d8d19280d 100644 --- a/tests/ui/pattern/usefulness/empty-match.exhaustive_patterns.stderr +++ b/tests/ui/pattern/usefulness/empty-match.exhaustive_patterns.stderr @@ -1,5 +1,5 @@ error: unreachable pattern - --> $DIR/empty-match.rs:58:9 + --> $DIR/empty-match.rs:68:9 | LL | _ => {}, | ^ @@ -11,25 +11,25 @@ LL | #![deny(unreachable_patterns)] | ^^^^^^^^^^^^^^^^^^^^ error: unreachable pattern - --> $DIR/empty-match.rs:61:9 + --> $DIR/empty-match.rs:71:9 | LL | _ if false => {}, | ^ error: unreachable pattern - --> $DIR/empty-match.rs:68:9 + --> $DIR/empty-match.rs:78:9 | LL | _ => {}, | ^ error: unreachable pattern - --> $DIR/empty-match.rs:71:9 + --> $DIR/empty-match.rs:81:9 | LL | _ if false => {}, | ^ error[E0005]: refutable pattern in local binding - --> $DIR/empty-match.rs:76:9 + --> $DIR/empty-match.rs:86:9 | LL | let None = x; | ^^^^ pattern `Some(_)` not covered @@ -44,19 +44,19 @@ LL | if let None = x { todo!() }; | ++ +++++++++++ error: unreachable pattern - --> $DIR/empty-match.rs:88:9 + --> $DIR/empty-match.rs:98:9 | LL | _ => {}, | ^ error: unreachable pattern - --> $DIR/empty-match.rs:91:9 + --> $DIR/empty-match.rs:101:9 | LL | _ if false => {}, | ^ error[E0004]: non-exhaustive patterns: type `u8` is non-empty - --> $DIR/empty-match.rs:109:20 + --> $DIR/empty-match.rs:119:20 | LL | match_no_arms!(0u8); | ^^^ @@ -65,7 +65,7 @@ LL | match_no_arms!(0u8); = help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern error[E0004]: non-exhaustive patterns: type `NonEmptyStruct1` is non-empty - --> $DIR/empty-match.rs:111:20 + --> $DIR/empty-match.rs:121:20 | LL | match_no_arms!(NonEmptyStruct1); | ^^^^^^^^^^^^^^^ @@ -79,7 +79,7 @@ LL | struct NonEmptyStruct1; = help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern error[E0004]: non-exhaustive patterns: type `NonEmptyStruct2` is non-empty - --> $DIR/empty-match.rs:113:20 + --> $DIR/empty-match.rs:123:20 | LL | match_no_arms!(NonEmptyStruct2(true)); | ^^^^^^^^^^^^^^^^^^^^^ @@ -93,7 +93,7 @@ LL | struct NonEmptyStruct2(bool); = help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern error[E0004]: non-exhaustive patterns: type `NonEmptyUnion1` is non-empty - --> $DIR/empty-match.rs:115:20 + --> $DIR/empty-match.rs:125:20 | LL | match_no_arms!((NonEmptyUnion1 { foo: () })); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -107,7 +107,7 @@ LL | union NonEmptyUnion1 { = help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern error[E0004]: non-exhaustive patterns: type `NonEmptyUnion2` is non-empty - --> $DIR/empty-match.rs:117:20 + --> $DIR/empty-match.rs:127:20 | LL | match_no_arms!((NonEmptyUnion2 { foo: () })); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -121,7 +121,7 @@ LL | union NonEmptyUnion2 { = help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern error[E0004]: non-exhaustive patterns: `NonEmptyEnum1::Foo(_)` not covered - --> $DIR/empty-match.rs:119:20 + --> $DIR/empty-match.rs:129:20 | LL | match_no_arms!(NonEmptyEnum1::Foo(true)); | ^^^^^^^^^^^^^^^^^^^^^^^^ pattern `NonEmptyEnum1::Foo(_)` not covered @@ -137,7 +137,7 @@ LL | Foo(bool), = help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern error[E0004]: non-exhaustive patterns: `NonEmptyEnum2::Foo(_)` and `NonEmptyEnum2::Bar` not covered - --> $DIR/empty-match.rs:122:20 + --> $DIR/empty-match.rs:132:20 | LL | match_no_arms!(NonEmptyEnum2::Foo(true)); | ^^^^^^^^^^^^^^^^^^^^^^^^ patterns `NonEmptyEnum2::Foo(_)` and `NonEmptyEnum2::Bar` not covered @@ -156,21 +156,28 @@ LL | Bar, = help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or multiple match arms error[E0004]: non-exhaustive patterns: `NonEmptyEnum5::V1`, `NonEmptyEnum5::V2`, `NonEmptyEnum5::V3` and 2 more not covered - --> $DIR/empty-match.rs:125:20 + --> $DIR/empty-match.rs:135:20 | LL | match_no_arms!(NonEmptyEnum5::V1); | ^^^^^^^^^^^^^^^^^ patterns `NonEmptyEnum5::V1`, `NonEmptyEnum5::V2`, `NonEmptyEnum5::V3` and 2 more not covered | note: `NonEmptyEnum5` defined here - --> $DIR/empty-match.rs:49:6 + --> $DIR/empty-match.rs:50:5 | LL | enum NonEmptyEnum5 { - | ^^^^^^^^^^^^^ + | ------------- +LL | V1, V2, V3, V4, V5, + | ^^ ^^ ^^ ^^ ^^ not covered + | | | | | + | | | | not covered + | | | not covered + | | not covered + | not covered = note: the matched value is of type `NonEmptyEnum5` = help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or multiple match arms error[E0004]: non-exhaustive patterns: `_` not covered - --> $DIR/empty-match.rs:129:24 + --> $DIR/empty-match.rs:139:24 | LL | match_guarded_arm!(0u8); | ^^^ pattern `_` not covered @@ -184,7 +191,7 @@ LL + _ => todo!() | error[E0004]: non-exhaustive patterns: `NonEmptyStruct1` not covered - --> $DIR/empty-match.rs:134:24 + --> $DIR/empty-match.rs:144:24 | LL | match_guarded_arm!(NonEmptyStruct1); | ^^^^^^^^^^^^^^^ pattern `NonEmptyStruct1` not covered @@ -203,7 +210,7 @@ LL + NonEmptyStruct1 => todo!() | error[E0004]: non-exhaustive patterns: `NonEmptyStruct2(_)` not covered - --> $DIR/empty-match.rs:139:24 + --> $DIR/empty-match.rs:149:24 | LL | match_guarded_arm!(NonEmptyStruct2(true)); | ^^^^^^^^^^^^^^^^^^^^^ pattern `NonEmptyStruct2(_)` not covered @@ -222,7 +229,7 @@ LL + NonEmptyStruct2(_) => todo!() | error[E0004]: non-exhaustive patterns: `NonEmptyUnion1 { .. }` not covered - --> $DIR/empty-match.rs:144:24 + --> $DIR/empty-match.rs:154:24 | LL | match_guarded_arm!((NonEmptyUnion1 { foo: () })); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ pattern `NonEmptyUnion1 { .. }` not covered @@ -241,7 +248,7 @@ LL + NonEmptyUnion1 { .. } => todo!() | error[E0004]: non-exhaustive patterns: `NonEmptyUnion2 { .. }` not covered - --> $DIR/empty-match.rs:149:24 + --> $DIR/empty-match.rs:159:24 | LL | match_guarded_arm!((NonEmptyUnion2 { foo: () })); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ pattern `NonEmptyUnion2 { .. }` not covered @@ -260,7 +267,7 @@ LL + NonEmptyUnion2 { .. } => todo!() | error[E0004]: non-exhaustive patterns: `NonEmptyEnum1::Foo(_)` not covered - --> $DIR/empty-match.rs:154:24 + --> $DIR/empty-match.rs:164:24 | LL | match_guarded_arm!(NonEmptyEnum1::Foo(true)); | ^^^^^^^^^^^^^^^^^^^^^^^^ pattern `NonEmptyEnum1::Foo(_)` not covered @@ -281,7 +288,7 @@ LL + NonEmptyEnum1::Foo(_) => todo!() | error[E0004]: non-exhaustive patterns: `NonEmptyEnum2::Foo(_)` and `NonEmptyEnum2::Bar` not covered - --> $DIR/empty-match.rs:159:24 + --> $DIR/empty-match.rs:169:24 | LL | match_guarded_arm!(NonEmptyEnum2::Foo(true)); | ^^^^^^^^^^^^^^^^^^^^^^^^ patterns `NonEmptyEnum2::Foo(_)` and `NonEmptyEnum2::Bar` not covered @@ -305,16 +312,23 @@ LL + NonEmptyEnum2::Foo(_) | NonEmptyEnum2::Bar => todo!() | error[E0004]: non-exhaustive patterns: `NonEmptyEnum5::V1`, `NonEmptyEnum5::V2`, `NonEmptyEnum5::V3` and 2 more not covered - --> $DIR/empty-match.rs:164:24 + --> $DIR/empty-match.rs:174:24 | LL | match_guarded_arm!(NonEmptyEnum5::V1); | ^^^^^^^^^^^^^^^^^ patterns `NonEmptyEnum5::V1`, `NonEmptyEnum5::V2`, `NonEmptyEnum5::V3` and 2 more not covered | note: `NonEmptyEnum5` defined here - --> $DIR/empty-match.rs:49:6 + --> $DIR/empty-match.rs:50:5 | LL | enum NonEmptyEnum5 { - | ^^^^^^^^^^^^^ + | ------------- +LL | V1, V2, V3, V4, V5, + | ^^ ^^ ^^ ^^ ^^ not covered + | | | | | + | | | | not covered + | | | not covered + | | not covered + | not covered = note: the matched value is of type `NonEmptyEnum5` = note: match arms with guards don't count towards exhaustivity help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern as shown, or multiple match arms diff --git a/tests/ui/pattern/usefulness/empty-match.normal.stderr b/tests/ui/pattern/usefulness/empty-match.normal.stderr index 6d17455086beb..97aac68c8d93b 100644 --- a/tests/ui/pattern/usefulness/empty-match.normal.stderr +++ b/tests/ui/pattern/usefulness/empty-match.normal.stderr @@ -1,5 +1,5 @@ error: unreachable pattern - --> $DIR/empty-match.rs:58:9 + --> $DIR/empty-match.rs:68:9 | LL | _ => {}, | ^ @@ -11,25 +11,25 @@ LL | #![deny(unreachable_patterns)] | ^^^^^^^^^^^^^^^^^^^^ error: unreachable pattern - --> $DIR/empty-match.rs:61:9 + --> $DIR/empty-match.rs:71:9 | LL | _ if false => {}, | ^ error: unreachable pattern - --> $DIR/empty-match.rs:68:9 + --> $DIR/empty-match.rs:78:9 | LL | _ => {}, | ^ error: unreachable pattern - --> $DIR/empty-match.rs:71:9 + --> $DIR/empty-match.rs:81:9 | LL | _ if false => {}, | ^ error[E0005]: refutable pattern in local binding - --> $DIR/empty-match.rs:76:9 + --> $DIR/empty-match.rs:86:9 | LL | let None = x; | ^^^^ pattern `Some(_)` not covered @@ -43,19 +43,19 @@ LL | if let None = x { todo!() }; | ++ +++++++++++ error: unreachable pattern - --> $DIR/empty-match.rs:88:9 + --> $DIR/empty-match.rs:98:9 | LL | _ => {}, | ^ error: unreachable pattern - --> $DIR/empty-match.rs:91:9 + --> $DIR/empty-match.rs:101:9 | LL | _ if false => {}, | ^ error[E0004]: non-exhaustive patterns: type `u8` is non-empty - --> $DIR/empty-match.rs:109:20 + --> $DIR/empty-match.rs:119:20 | LL | match_no_arms!(0u8); | ^^^ @@ -64,7 +64,7 @@ LL | match_no_arms!(0u8); = help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern error[E0004]: non-exhaustive patterns: type `NonEmptyStruct1` is non-empty - --> $DIR/empty-match.rs:111:20 + --> $DIR/empty-match.rs:121:20 | LL | match_no_arms!(NonEmptyStruct1); | ^^^^^^^^^^^^^^^ @@ -78,7 +78,7 @@ LL | struct NonEmptyStruct1; = help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern error[E0004]: non-exhaustive patterns: type `NonEmptyStruct2` is non-empty - --> $DIR/empty-match.rs:113:20 + --> $DIR/empty-match.rs:123:20 | LL | match_no_arms!(NonEmptyStruct2(true)); | ^^^^^^^^^^^^^^^^^^^^^ @@ -92,7 +92,7 @@ LL | struct NonEmptyStruct2(bool); = help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern error[E0004]: non-exhaustive patterns: type `NonEmptyUnion1` is non-empty - --> $DIR/empty-match.rs:115:20 + --> $DIR/empty-match.rs:125:20 | LL | match_no_arms!((NonEmptyUnion1 { foo: () })); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -106,7 +106,7 @@ LL | union NonEmptyUnion1 { = help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern error[E0004]: non-exhaustive patterns: type `NonEmptyUnion2` is non-empty - --> $DIR/empty-match.rs:117:20 + --> $DIR/empty-match.rs:127:20 | LL | match_no_arms!((NonEmptyUnion2 { foo: () })); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -120,7 +120,7 @@ LL | union NonEmptyUnion2 { = help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern error[E0004]: non-exhaustive patterns: `NonEmptyEnum1::Foo(_)` not covered - --> $DIR/empty-match.rs:119:20 + --> $DIR/empty-match.rs:129:20 | LL | match_no_arms!(NonEmptyEnum1::Foo(true)); | ^^^^^^^^^^^^^^^^^^^^^^^^ pattern `NonEmptyEnum1::Foo(_)` not covered @@ -136,7 +136,7 @@ LL | Foo(bool), = help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern error[E0004]: non-exhaustive patterns: `NonEmptyEnum2::Foo(_)` and `NonEmptyEnum2::Bar` not covered - --> $DIR/empty-match.rs:122:20 + --> $DIR/empty-match.rs:132:20 | LL | match_no_arms!(NonEmptyEnum2::Foo(true)); | ^^^^^^^^^^^^^^^^^^^^^^^^ patterns `NonEmptyEnum2::Foo(_)` and `NonEmptyEnum2::Bar` not covered @@ -155,21 +155,28 @@ LL | Bar, = help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or multiple match arms error[E0004]: non-exhaustive patterns: `NonEmptyEnum5::V1`, `NonEmptyEnum5::V2`, `NonEmptyEnum5::V3` and 2 more not covered - --> $DIR/empty-match.rs:125:20 + --> $DIR/empty-match.rs:135:20 | LL | match_no_arms!(NonEmptyEnum5::V1); | ^^^^^^^^^^^^^^^^^ patterns `NonEmptyEnum5::V1`, `NonEmptyEnum5::V2`, `NonEmptyEnum5::V3` and 2 more not covered | note: `NonEmptyEnum5` defined here - --> $DIR/empty-match.rs:49:6 + --> $DIR/empty-match.rs:50:5 | LL | enum NonEmptyEnum5 { - | ^^^^^^^^^^^^^ + | ------------- +LL | V1, V2, V3, V4, V5, + | ^^ ^^ ^^ ^^ ^^ not covered + | | | | | + | | | | not covered + | | | not covered + | | not covered + | not covered = note: the matched value is of type `NonEmptyEnum5` = help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or multiple match arms error[E0004]: non-exhaustive patterns: `_` not covered - --> $DIR/empty-match.rs:129:24 + --> $DIR/empty-match.rs:139:24 | LL | match_guarded_arm!(0u8); | ^^^ pattern `_` not covered @@ -183,7 +190,7 @@ LL + _ => todo!() | error[E0004]: non-exhaustive patterns: `NonEmptyStruct1` not covered - --> $DIR/empty-match.rs:134:24 + --> $DIR/empty-match.rs:144:24 | LL | match_guarded_arm!(NonEmptyStruct1); | ^^^^^^^^^^^^^^^ pattern `NonEmptyStruct1` not covered @@ -202,7 +209,7 @@ LL + NonEmptyStruct1 => todo!() | error[E0004]: non-exhaustive patterns: `NonEmptyStruct2(_)` not covered - --> $DIR/empty-match.rs:139:24 + --> $DIR/empty-match.rs:149:24 | LL | match_guarded_arm!(NonEmptyStruct2(true)); | ^^^^^^^^^^^^^^^^^^^^^ pattern `NonEmptyStruct2(_)` not covered @@ -221,7 +228,7 @@ LL + NonEmptyStruct2(_) => todo!() | error[E0004]: non-exhaustive patterns: `NonEmptyUnion1 { .. }` not covered - --> $DIR/empty-match.rs:144:24 + --> $DIR/empty-match.rs:154:24 | LL | match_guarded_arm!((NonEmptyUnion1 { foo: () })); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ pattern `NonEmptyUnion1 { .. }` not covered @@ -240,7 +247,7 @@ LL + NonEmptyUnion1 { .. } => todo!() | error[E0004]: non-exhaustive patterns: `NonEmptyUnion2 { .. }` not covered - --> $DIR/empty-match.rs:149:24 + --> $DIR/empty-match.rs:159:24 | LL | match_guarded_arm!((NonEmptyUnion2 { foo: () })); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ pattern `NonEmptyUnion2 { .. }` not covered @@ -259,7 +266,7 @@ LL + NonEmptyUnion2 { .. } => todo!() | error[E0004]: non-exhaustive patterns: `NonEmptyEnum1::Foo(_)` not covered - --> $DIR/empty-match.rs:154:24 + --> $DIR/empty-match.rs:164:24 | LL | match_guarded_arm!(NonEmptyEnum1::Foo(true)); | ^^^^^^^^^^^^^^^^^^^^^^^^ pattern `NonEmptyEnum1::Foo(_)` not covered @@ -280,7 +287,7 @@ LL + NonEmptyEnum1::Foo(_) => todo!() | error[E0004]: non-exhaustive patterns: `NonEmptyEnum2::Foo(_)` and `NonEmptyEnum2::Bar` not covered - --> $DIR/empty-match.rs:159:24 + --> $DIR/empty-match.rs:169:24 | LL | match_guarded_arm!(NonEmptyEnum2::Foo(true)); | ^^^^^^^^^^^^^^^^^^^^^^^^ patterns `NonEmptyEnum2::Foo(_)` and `NonEmptyEnum2::Bar` not covered @@ -304,16 +311,23 @@ LL + NonEmptyEnum2::Foo(_) | NonEmptyEnum2::Bar => todo!() | error[E0004]: non-exhaustive patterns: `NonEmptyEnum5::V1`, `NonEmptyEnum5::V2`, `NonEmptyEnum5::V3` and 2 more not covered - --> $DIR/empty-match.rs:164:24 + --> $DIR/empty-match.rs:174:24 | LL | match_guarded_arm!(NonEmptyEnum5::V1); | ^^^^^^^^^^^^^^^^^ patterns `NonEmptyEnum5::V1`, `NonEmptyEnum5::V2`, `NonEmptyEnum5::V3` and 2 more not covered | note: `NonEmptyEnum5` defined here - --> $DIR/empty-match.rs:49:6 + --> $DIR/empty-match.rs:50:5 | LL | enum NonEmptyEnum5 { - | ^^^^^^^^^^^^^ + | ------------- +LL | V1, V2, V3, V4, V5, + | ^^ ^^ ^^ ^^ ^^ not covered + | | | | | + | | | | not covered + | | | not covered + | | not covered + | not covered = note: the matched value is of type `NonEmptyEnum5` = note: match arms with guards don't count towards exhaustivity help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern as shown, or multiple match arms diff --git a/tests/ui/pattern/usefulness/empty-match.rs b/tests/ui/pattern/usefulness/empty-match.rs index d56d2e3c817ce..6551900bbc6c2 100644 --- a/tests/ui/pattern/usefulness/empty-match.rs +++ b/tests/ui/pattern/usefulness/empty-match.rs @@ -47,9 +47,19 @@ enum NonEmptyEnum2 { //~| NOTE not covered } enum NonEmptyEnum5 { + V1, V2, V3, V4, V5, //~^ NOTE `NonEmptyEnum5` defined here //~| NOTE `NonEmptyEnum5` defined here - V1, V2, V3, V4, V5, + //~| NOTE not covered + //~| NOTE not covered + //~| NOTE not covered + //~| NOTE not covered + //~| NOTE not covered + //~| NOTE not covered + //~| NOTE not covered + //~| NOTE not covered + //~| NOTE not covered + //~| NOTE not covered } fn empty_enum(x: EmptyEnum) { diff --git a/tests/ui/pattern/usefulness/issue-35609.stderr b/tests/ui/pattern/usefulness/issue-35609.stderr index 6d5e2f410bc97..9feedfde4697a 100644 --- a/tests/ui/pattern/usefulness/issue-35609.stderr +++ b/tests/ui/pattern/usefulness/issue-35609.stderr @@ -107,6 +107,9 @@ LL | match Some(A) { | note: `Option` 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` help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern as shown, or multiple match arms | diff --git a/tests/ui/pattern/usefulness/non-exhaustive-pattern-witness.stderr b/tests/ui/pattern/usefulness/non-exhaustive-pattern-witness.stderr index f914b98d9231f..c75c4ccdc8752 100644 --- a/tests/ui/pattern/usefulness/non-exhaustive-pattern-witness.stderr +++ b/tests/ui/pattern/usefulness/non-exhaustive-pattern-witness.stderr @@ -68,10 +68,21 @@ LL | match ExcessiveEnum::First { | ^^^^^^^^^^^^^^^^^^^^ patterns `ExcessiveEnum::Second`, `ExcessiveEnum::Third`, `ExcessiveEnum::Fourth` and 8 more not covered | note: `ExcessiveEnum` defined here - --> $DIR/non-exhaustive-pattern-witness.rs:44:6 + --> $DIR/non-exhaustive-pattern-witness.rs:46:5 | LL | enum ExcessiveEnum { - | ^^^^^^^^^^^^^ + | ------------- +LL | First, +LL | Second, + | ^^^^^^ not covered +LL | Third, + | ^^^^^ not covered +LL | Fourth, + | ^^^^^^ not covered +LL | Fifth, + | ^^^^^ not covered +LL | Sixth, + | ^^^^^ not covered = note: the matched value is of type `ExcessiveEnum` help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern as shown, or multiple match arms | From b60f08a66d5a545d414a70a7047161cb191054b9 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Sun, 29 Oct 2023 05:11:52 +0100 Subject: [PATCH 05/12] Add regression test for pattern checks --- .../usefulness/conflicting_bindings.rs | 22 ++++++++ .../usefulness/conflicting_bindings.stderr | 50 +++++++++++++++++++ 2 files changed, 72 insertions(+) create mode 100644 tests/ui/pattern/usefulness/conflicting_bindings.rs create mode 100644 tests/ui/pattern/usefulness/conflicting_bindings.stderr diff --git a/tests/ui/pattern/usefulness/conflicting_bindings.rs b/tests/ui/pattern/usefulness/conflicting_bindings.rs new file mode 100644 index 0000000000000..1df4425c48bf3 --- /dev/null +++ b/tests/ui/pattern/usefulness/conflicting_bindings.rs @@ -0,0 +1,22 @@ +#![feature(if_let_guard, let_chains)] + +fn main() { + let mut x = Some(String::new()); + let ref mut y @ ref mut z = x; + //~^ ERROR: mutable more than once + let Some(ref mut y @ ref mut z) = x else { return }; + //~^ ERROR: mutable more than once + if let Some(ref mut y @ ref mut z) = x {} + //~^ ERROR: mutable more than once + if let Some(ref mut y @ ref mut z) = x && true {} + while let Some(ref mut y @ ref mut z) = x {} + //~^ ERROR: mutable more than once + while let Some(ref mut y @ ref mut z) = x && true {} + match x { + ref mut y @ ref mut z => {} //~ ERROR: mutable more than once + } + match () { + () if let Some(ref mut y @ ref mut z) = x => {} //~ ERROR: mutable more than once + _ => {} + } +} diff --git a/tests/ui/pattern/usefulness/conflicting_bindings.stderr b/tests/ui/pattern/usefulness/conflicting_bindings.stderr new file mode 100644 index 0000000000000..af20eece20cfa --- /dev/null +++ b/tests/ui/pattern/usefulness/conflicting_bindings.stderr @@ -0,0 +1,50 @@ +error: cannot borrow value as mutable more than once at a time + --> $DIR/conflicting_bindings.rs:5:9 + | +LL | let ref mut y @ ref mut z = x; + | ^^^^^^^^^ --------- value is mutably borrowed by `z` here + | | + | value is mutably borrowed by `y` here + +error: cannot borrow value as mutable more than once at a time + --> $DIR/conflicting_bindings.rs:7:14 + | +LL | let Some(ref mut y @ ref mut z) = x else { return }; + | ^^^^^^^^^ --------- value is mutably borrowed by `z` here + | | + | value is mutably borrowed by `y` here + +error: cannot borrow value as mutable more than once at a time + --> $DIR/conflicting_bindings.rs:9:17 + | +LL | if let Some(ref mut y @ ref mut z) = x {} + | ^^^^^^^^^ --------- value is mutably borrowed by `z` here + | | + | value is mutably borrowed by `y` here + +error: cannot borrow value as mutable more than once at a time + --> $DIR/conflicting_bindings.rs:12:20 + | +LL | while let Some(ref mut y @ ref mut z) = x {} + | ^^^^^^^^^ --------- value is mutably borrowed by `z` here + | | + | value is mutably borrowed by `y` here + +error: cannot borrow value as mutable more than once at a time + --> $DIR/conflicting_bindings.rs:16:9 + | +LL | ref mut y @ ref mut z => {} + | ^^^^^^^^^ --------- value is mutably borrowed by `z` here + | | + | value is mutably borrowed by `y` here + +error: cannot borrow value as mutable more than once at a time + --> $DIR/conflicting_bindings.rs:19:24 + | +LL | () if let Some(ref mut y @ ref mut z) = x => {} + | ^^^^^^^^^ --------- value is mutably borrowed by `z` here + | | + | value is mutably borrowed by `y` here + +error: aborting due to 6 previous errors + From c19856929dbbcc4b0b6103e8a566b7763b7ab117 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Sun, 29 Oct 2023 05:15:36 +0100 Subject: [PATCH 06/12] Always do all the pattern checks --- .../src/thir/pattern/check_match.rs | 120 +++++++++--------- .../usefulness/conflicting_bindings.rs | 2 + .../usefulness/conflicting_bindings.stderr | 24 +++- 3 files changed, 82 insertions(+), 64 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 0e218cd8788e8..c66a26123bc86 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs @@ -58,7 +58,7 @@ fn create_e0004( struct_span_err!(sess, sp, E0004, "{}", &error_message) } -#[derive(PartialEq)] +#[derive(Copy, Clone, PartialEq)] enum RefutableFlag { Irrefutable, Refutable, @@ -197,32 +197,36 @@ impl<'p, 'tcx> MatchVisitor<'_, 'p, 'tcx> { self.let_source = old_let_source; } - fn with_lint_level(&mut self, new_lint_level: LintLevel, f: impl FnOnce(&mut Self)) { + fn with_lint_level( + &mut self, + new_lint_level: LintLevel, + f: impl FnOnce(&mut Self) -> T, + ) -> T { if let LintLevel::Explicit(hir_id) = new_lint_level { let old_lint_level = self.lint_level; self.lint_level = hir_id; - f(self); + let ret = f(self); self.lint_level = old_lint_level; + ret } else { - f(self); + f(self) } } - fn check_patterns(&self, pat: &Pat<'tcx>, rf: RefutableFlag) { - pat.walk_always(|pat| check_borrow_conflicts_in_at_patterns(self, pat)); - check_for_bindings_named_same_as_variants(self, pat, rf); - } - fn lower_pattern( &mut self, cx: &MatchCheckCtxt<'p, 'tcx>, - pattern: &Pat<'tcx>, + pat: &Pat<'tcx>, ) -> Result<&'p DeconstructedPat<'p, 'tcx>, ErrorGuaranteed> { - if let Err(err) = pattern.pat_error_reported() { + if let Err(err) = pat.pat_error_reported() { self.error = Err(err); Err(err) } else { - Ok(cx.pattern_arena.alloc(DeconstructedPat::from_pat(cx, pattern))) + // Check the pattern for some things unrelated to exhaustiveness. + let refutable = if cx.refutable { Refutable } else { Irrefutable }; + pat.walk_always(|pat| check_borrow_conflicts_in_at_patterns(self, pat)); + pat.walk_always(|pat| check_for_bindings_named_same_as_variants(self, pat, refutable)); + Ok(cx.pattern_arena.alloc(DeconstructedPat::from_pat(cx, pat))) } } @@ -241,7 +245,6 @@ impl<'p, 'tcx> MatchVisitor<'_, 'p, 'tcx> { if let LetSource::None = source { return; } - self.check_patterns(pat, Refutable); let mut cx = self.new_cx(self.lint_level, true); let Ok(tpat) = self.lower_pattern(&cx, pat) else { return }; self.check_let_reachability(&mut cx, self.lint_level, source, tpat, span); @@ -258,18 +261,18 @@ impl<'p, 'tcx> MatchVisitor<'_, 'p, 'tcx> { let mut tarms = Vec::with_capacity(arms.len()); for &arm in arms { - // Check the arm for some things unrelated to exhaustiveness. let arm = &self.thir.arms[arm]; - self.with_lint_level(arm.lint_level, |this| { - this.check_patterns(&arm.pattern, Refutable); + let got_error = self.with_lint_level(arm.lint_level, |this| { + let Ok(pat) = this.lower_pattern(&mut cx, &arm.pattern) else { + return true; + }; + let arm = MatchArm { pat, hir_id: this.lint_level, has_guard: arm.guard.is_some() }; + tarms.push(arm); + false }); - let hir_id = match arm.lint_level { - LintLevel::Explicit(hir_id) => hir_id, - LintLevel::Inherited => self.lint_level, - }; - let Ok(pat) = self.lower_pattern(&mut cx, &arm.pattern) else { return }; - let arm = MatchArm { pat, hir_id, has_guard: arm.guard.is_some() }; - tarms.push(arm); + if got_error { + return; + } } let scrut = &self.thir[scrut]; @@ -458,7 +461,6 @@ impl<'p, 'tcx> MatchVisitor<'_, 'p, 'tcx> { let witnesses = report.non_exhaustiveness_witnesses; if witnesses.is_empty() { // The pattern is irrefutable. - self.check_patterns(pat, Irrefutable); return; } @@ -659,43 +661,41 @@ fn check_for_bindings_named_same_as_variants( pat: &Pat<'_>, rf: RefutableFlag, ) { - pat.walk_always(|p| { - if let PatKind::Binding { - name, - mode: BindingMode::ByValue, - mutability: Mutability::Not, - subpattern: None, - ty, - .. - } = p.kind - && let ty::Adt(edef, _) = ty.peel_refs().kind() - && edef.is_enum() - && edef - .variants() - .iter() - .any(|variant| variant.name == name && variant.ctor_kind() == Some(CtorKind::Const)) - { - let variant_count = edef.variants().len(); - let ty_path = with_no_trimmed_paths!(cx.tcx.def_path_str(edef.did())); - cx.tcx.emit_spanned_lint( - BINDINGS_WITH_VARIANT_NAME, - cx.lint_level, - p.span, - 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`. - suggestion: if rf == Refutable || variant_count == 1 { - Some(p.span) - } else { - None - }, - ty_path, - name, + if let PatKind::Binding { + name, + mode: BindingMode::ByValue, + mutability: Mutability::Not, + subpattern: None, + ty, + .. + } = pat.kind + && let ty::Adt(edef, _) = ty.peel_refs().kind() + && edef.is_enum() + && edef + .variants() + .iter() + .any(|variant| variant.name == name && variant.ctor_kind() == Some(CtorKind::Const)) + { + let variant_count = edef.variants().len(); + let ty_path = with_no_trimmed_paths!(cx.tcx.def_path_str(edef.did())); + cx.tcx.emit_spanned_lint( + BINDINGS_WITH_VARIANT_NAME, + cx.lint_level, + pat.span, + 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`. + suggestion: if rf == Refutable || variant_count == 1 { + Some(pat.span) + } else { + None }, - ) - } - }); + ty_path, + name, + }, + ) + } } fn irrefutable_let_patterns( diff --git a/tests/ui/pattern/usefulness/conflicting_bindings.rs b/tests/ui/pattern/usefulness/conflicting_bindings.rs index 1df4425c48bf3..0b3e7ce9e9a0f 100644 --- a/tests/ui/pattern/usefulness/conflicting_bindings.rs +++ b/tests/ui/pattern/usefulness/conflicting_bindings.rs @@ -9,9 +9,11 @@ fn main() { if let Some(ref mut y @ ref mut z) = x {} //~^ ERROR: mutable more than once if let Some(ref mut y @ ref mut z) = x && true {} + //~^ ERROR: mutable more than once while let Some(ref mut y @ ref mut z) = x {} //~^ ERROR: mutable more than once while let Some(ref mut y @ ref mut z) = x && true {} + //~^ ERROR: mutable more than once match x { ref mut y @ ref mut z => {} //~ ERROR: mutable more than once } diff --git a/tests/ui/pattern/usefulness/conflicting_bindings.stderr b/tests/ui/pattern/usefulness/conflicting_bindings.stderr index af20eece20cfa..679fc83e7f563 100644 --- a/tests/ui/pattern/usefulness/conflicting_bindings.stderr +++ b/tests/ui/pattern/usefulness/conflicting_bindings.stderr @@ -23,7 +23,15 @@ LL | if let Some(ref mut y @ ref mut z) = x {} | value is mutably borrowed by `y` here error: cannot borrow value as mutable more than once at a time - --> $DIR/conflicting_bindings.rs:12:20 + --> $DIR/conflicting_bindings.rs:11:17 + | +LL | if let Some(ref mut y @ ref mut z) = x && true {} + | ^^^^^^^^^ --------- value is mutably borrowed by `z` here + | | + | value is mutably borrowed by `y` here + +error: cannot borrow value as mutable more than once at a time + --> $DIR/conflicting_bindings.rs:13:20 | LL | while let Some(ref mut y @ ref mut z) = x {} | ^^^^^^^^^ --------- value is mutably borrowed by `z` here @@ -31,7 +39,15 @@ LL | while let Some(ref mut y @ ref mut z) = x {} | value is mutably borrowed by `y` here error: cannot borrow value as mutable more than once at a time - --> $DIR/conflicting_bindings.rs:16:9 + --> $DIR/conflicting_bindings.rs:15:20 + | +LL | while let Some(ref mut y @ ref mut z) = x && true {} + | ^^^^^^^^^ --------- value is mutably borrowed by `z` here + | | + | value is mutably borrowed by `y` here + +error: cannot borrow value as mutable more than once at a time + --> $DIR/conflicting_bindings.rs:18:9 | LL | ref mut y @ ref mut z => {} | ^^^^^^^^^ --------- value is mutably borrowed by `z` here @@ -39,12 +55,12 @@ LL | ref mut y @ ref mut z => {} | value is mutably borrowed by `y` here error: cannot borrow value as mutable more than once at a time - --> $DIR/conflicting_bindings.rs:19:24 + --> $DIR/conflicting_bindings.rs:21:24 | LL | () if let Some(ref mut y @ ref mut z) = x => {} | ^^^^^^^^^ --------- value is mutably borrowed by `z` here | | | value is mutably borrowed by `y` here -error: aborting due to 6 previous errors +error: aborting due to 8 previous errors From fcd24fbd3c73e90ddb5416f644bf8e7337b17f87 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Sun, 29 Oct 2023 05:35:03 +0100 Subject: [PATCH 07/12] Factor out pointing at ADT definition --- .../src/thir/pattern/check_match.rs | 85 +++++++++---------- 1 file changed, 41 insertions(+), 44 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 c66a26123bc86..694e4c07bd5d4 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs @@ -9,9 +9,7 @@ use rustc_arena::TypedArena; use rustc_ast::Mutability; use rustc_data_structures::fx::FxHashSet; use rustc_data_structures::stack::ensure_sufficient_stack; -use rustc_errors::{ - struct_span_err, Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed, MultiSpan, -}; +use rustc_errors::{struct_span_err, Applicability, DiagnosticBuilder, ErrorGuaranteed, MultiSpan}; use rustc_hir as hir; use rustc_hir::def::*; use rustc_hir::def_id::LocalDefId; @@ -507,17 +505,7 @@ impl<'p, 'tcx> MatchVisitor<'_, 'p, 'tcx> { }); }; - 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![]; - - for span in maybe_point_at_variant(&cx, *def, witnesses.iter().take(5)) { - variants.push(Variant { span }); - } - AdtDefinedHere { adt_def_span, ty, variants } - }; + let adt_defined_here = report_adt_defined_here(self.tcx, pattern_ty, &witnesses, false); // Emit an extra note if the first uncovered witness would be uninhabited // if we disregard visibility. @@ -842,7 +830,22 @@ fn non_exhaustive_match<'p, 'tcx>( }; }; - adt_defined_here(cx, &mut err, scrut_ty, &witnesses); + // Point at the definition of non-covered `enum` variants. + if let Some(AdtDefinedHere { adt_def_span, ty, variants }) = + report_adt_defined_here(cx.tcx, scrut_ty, &witnesses, true) + { + let mut multi_span = if variants.is_empty() { + MultiSpan::from_span(adt_def_span) + } else { + MultiSpan::from_spans(variants.iter().map(|Variant { span }| *span).collect()) + }; + + multi_span.push_span_label(adt_def_span, ""); + for Variant { span } in variants { + multi_span.push_span_label(span, "not covered"); + } + err.span_note(multi_span, format!("`{ty}` defined here")); + } err.note(format!("the matched value is of type `{}`", scrut_ty)); if !is_empty_match { @@ -1023,39 +1026,33 @@ fn collect_non_exhaustive_tys<'tcx>( .for_each(|field_pat| collect_non_exhaustive_tys(tcx, field_pat, non_exhaustive_tys)) } -/// Point at the definition of non-covered `enum` variants. -fn adt_defined_here<'p, 'tcx>( - cx: &MatchCheckCtxt<'p, 'tcx>, - err: &mut Diagnostic, +fn report_adt_defined_here<'tcx>( + tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, witnesses: &[WitnessPat<'tcx>], -) { + point_at_non_local_ty: bool, +) -> Option> { let ty = ty.peel_refs(); - if let ty::Adt(def, _) = ty.kind() { - let mut spans = vec![]; - for sp in maybe_point_at_variant(cx, *def, witnesses.iter().take(5)) { - spans.push(sp); - } - let def_span = cx - .tcx - .hir() - .get_if_local(def.did()) - .and_then(|node| node.ident()) - .map(|ident| ident.span) - .unwrap_or_else(|| cx.tcx.def_span(def.did())); - let mut span: MultiSpan = - if spans.is_empty() { def_span.into() } else { spans.clone().into() }; - - span.push_span_label(def_span, ""); - for pat in spans { - span.push_span_label(pat, "not covered"); - } - err.span_note(span, format!("`{ty}` defined here")); + let ty::Adt(def, _) = ty.kind() else { + return None; + }; + let adt_def_span = + tcx.hir().get_if_local(def.did()).and_then(|node| node.ident()).map(|ident| ident.span); + let adt_def_span = if point_at_non_local_ty { + adt_def_span.unwrap_or_else(|| tcx.def_span(def.did())) + } else { + adt_def_span? + }; + + let mut variants = vec![]; + for span in maybe_point_at_variant(tcx, *def, witnesses.iter().take(5)) { + variants.push(Variant { span }); } + Some(AdtDefinedHere { adt_def_span, ty, variants }) } -fn maybe_point_at_variant<'a, 'p: 'a, 'tcx: 'a>( - cx: &MatchCheckCtxt<'p, 'tcx>, +fn maybe_point_at_variant<'a, 'tcx: 'a>( + tcx: TyCtxt<'tcx>, def: AdtDef<'tcx>, patterns: impl Iterator>, ) -> Vec { @@ -1068,7 +1065,7 @@ fn maybe_point_at_variant<'a, 'p: 'a, 'tcx: 'a>( { continue; } - let sp = def.variant(*variant_index).ident(cx.tcx).span; + let sp = def.variant(*variant_index).ident(tcx).span; if covered.contains(&sp) { // Don't point at variants that have already been covered due to other patterns to avoid // visual clutter. @@ -1076,7 +1073,7 @@ fn maybe_point_at_variant<'a, 'p: 'a, 'tcx: 'a>( } covered.push(sp); } - covered.extend(maybe_point_at_variant(cx, def, pattern.iter_fields())); + covered.extend(maybe_point_at_variant(tcx, def, pattern.iter_fields())); } covered } From 3760d919d86b1f4ad464ee1370ba6389e161a7a2 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Sun, 29 Oct 2023 05:37:59 +0100 Subject: [PATCH 08/12] Cleanup check_match code paths --- .../src/thir/pattern/check_match.rs | 214 +++++++++--------- 1 file changed, 104 insertions(+), 110 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 694e4c07bd5d4..eadd7a310b718 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs @@ -42,7 +42,7 @@ pub(crate) fn check_match(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Result<(), Err for param in thir.params.iter() { if let Some(box ref pattern) = param.pat { - visitor.check_irrefutable(pattern, "function argument", None); + visitor.check_binding_is_irrefutable(pattern, "function argument", None); } } visitor.error @@ -66,16 +66,17 @@ use RefutableFlag::*; #[derive(Clone, Copy, Debug, PartialEq, Eq)] enum LetSource { None, + PlainLet, IfLet, IfLetGuard, LetElse, WhileLet, } -struct MatchVisitor<'a, 'p, 'tcx> { +struct MatchVisitor<'thir, 'p, 'tcx> { tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>, - thir: &'a Thir<'tcx>, + thir: &'thir Thir<'tcx>, lint_level: HirId, let_source: LetSource, pattern_arena: &'p TypedArena>, @@ -85,8 +86,10 @@ struct MatchVisitor<'a, 'p, 'tcx> { error: Result<(), ErrorGuaranteed>, } -impl<'a, 'tcx> Visitor<'a, 'tcx> for MatchVisitor<'a, '_, 'tcx> { - fn thir(&self) -> &'a Thir<'tcx> { +// Visitor for a thir body. This calls `check_match`, `check_let` and `check_let_chain` as +// appropriate. +impl<'thir, 'tcx> Visitor<'thir, 'tcx> for MatchVisitor<'thir, '_, 'tcx> { + fn thir(&self) -> &'thir Thir<'tcx> { self.thir } @@ -101,7 +104,7 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for MatchVisitor<'a, '_, 'tcx> { } Some(Guard::IfLet(ref pat, expr)) => { this.with_let_source(LetSource::IfLetGuard, |this| { - this.check_let(pat, expr, LetSource::IfLetGuard, pat.span); + this.check_let(pat, Some(expr), pat.span); this.visit_pat(pat); this.visit_expr(&this.thir[expr]); }); @@ -148,45 +151,43 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for MatchVisitor<'a, '_, 'tcx> { }; self.check_match(scrutinee, arms, source, ex.span); } - ExprKind::Let { box ref pat, expr } => { - self.check_let(pat, expr, self.let_source, ex.span); + ExprKind::Let { box ref pat, expr } if !matches!(self.let_source, LetSource::None) => { + self.check_let(pat, Some(expr), ex.span); } - ExprKind::LogicalOp { op: LogicalOp::And, .. } => { - self.check_let_chain(self.let_source, ex); + ExprKind::LogicalOp { op: LogicalOp::And, .. } + if !matches!(self.let_source, LetSource::None) => + { + self.check_let_chain(ex); } _ => {} }; + // If we got e.g. `let pat1 = x1 && let pat2 = x2` above, we will now traverse the two + // `let`s. In order not to check them twice we set `LetSource::None`. self.with_let_source(LetSource::None, |this| visit::walk_expr(this, ex)); } fn visit_stmt(&mut self, stmt: &Stmt<'tcx>) { - let old_lint_level = self.lint_level; match stmt.kind { StmtKind::Let { box ref pattern, initializer, else_block, lint_level, span, .. } => { - if let LintLevel::Explicit(lint_level) = lint_level { - self.lint_level = lint_level; - } - - if let Some(initializer) = initializer - && else_block.is_some() - { - self.check_let(pattern, initializer, LetSource::LetElse, span); - } - - if else_block.is_none() { - self.check_irrefutable(pattern, "local binding", Some(span)); - } + self.with_lint_level(lint_level, |this| { + let let_source = + if else_block.is_some() { LetSource::LetElse } else { LetSource::PlainLet }; + this.with_let_source(let_source, |this| { + this.check_let(pattern, initializer, span) + }); + visit::walk_stmt(this, stmt); + }); + } + StmtKind::Expr { .. } => { + visit::walk_stmt(self, stmt); } - _ => {} } - visit::walk_stmt(self, stmt); - self.lint_level = old_lint_level; } } -impl<'p, 'tcx> MatchVisitor<'_, 'p, 'tcx> { +impl<'thir, 'p, 'tcx> MatchVisitor<'thir, 'p, 'tcx> { #[instrument(level = "trace", skip(self, f))] fn with_let_source(&mut self, let_source: LetSource, f: impl FnOnce(&mut Self)) { let old_let_source = self.let_source; @@ -228,24 +229,37 @@ impl<'p, 'tcx> MatchVisitor<'_, 'p, 'tcx> { } } - fn new_cx(&self, hir_id: HirId, refutable: bool) -> MatchCheckCtxt<'p, 'tcx> { + fn new_cx(&self, refutability: RefutableFlag) -> MatchCheckCtxt<'p, 'tcx> { + let refutable = match refutability { + Irrefutable => false, + Refutable => true, + }; MatchCheckCtxt { tcx: self.tcx, param_env: self.param_env, - module: self.tcx.parent_module(hir_id).to_def_id(), + module: self.tcx.parent_module(self.lint_level).to_def_id(), pattern_arena: &self.pattern_arena, refutable, } } #[instrument(level = "trace", skip(self))] - fn check_let(&mut self, pat: &Pat<'tcx>, scrutinee: ExprId, source: LetSource, span: Span) { - if let LetSource::None = source { - return; + fn check_let(&mut self, pat: &Pat<'tcx>, scrutinee: Option, span: Span) { + assert!(self.let_source != LetSource::None); + if let LetSource::PlainLet = self.let_source { + self.check_binding_is_irrefutable(pat, "local binding", Some(span)) + } else { + let Ok(irrefutable) = self.is_let_irrefutable(pat) else { return }; + if irrefutable { + report_irrefutable_let_patterns( + self.tcx, + self.lint_level, + self.let_source, + 1, + span, + ); + } } - let mut cx = self.new_cx(self.lint_level, true); - let Ok(tpat) = self.lower_pattern(&cx, pat) else { return }; - self.check_let_reachability(&mut cx, self.lint_level, source, tpat, span); } fn check_match( @@ -255,15 +269,13 @@ impl<'p, 'tcx> MatchVisitor<'_, 'p, 'tcx> { source: hir::MatchSource, expr_span: Span, ) { - let mut cx = self.new_cx(self.lint_level, true); + let cx = self.new_cx(Refutable); let mut tarms = Vec::with_capacity(arms.len()); for &arm in arms { let arm = &self.thir.arms[arm]; let got_error = self.with_lint_level(arm.lint_level, |this| { - let Ok(pat) = this.lower_pattern(&mut cx, &arm.pattern) else { - return true; - }; + let Ok(pat) = this.lower_pattern(&cx, &arm.pattern) else { return true }; let arm = MatchArm { pat, hir_id: this.lint_level, has_guard: arm.guard.is_some() }; tarms.push(arm); false @@ -299,34 +311,18 @@ impl<'p, 'tcx> MatchVisitor<'_, 'p, 'tcx> { debug_assert_eq!(pat.span.desugaring_kind(), Some(DesugaringKind::ForLoop)); let PatKind::Variant { ref subpatterns, .. } = pat.kind else { bug!() }; let [pat_field] = &subpatterns[..] else { bug!() }; - self.check_irrefutable(&pat_field.pattern, "`for` loop binding", None); + self.check_binding_is_irrefutable(&pat_field.pattern, "`for` loop binding", None); } else { - self.error = Err(non_exhaustive_match( + self.error = Err(report_non_exhaustive_match( &cx, self.thir, scrut_ty, scrut.span, witnesses, arms, expr_span, )); } } } - fn check_let_reachability( - &mut self, - cx: &mut MatchCheckCtxt<'p, 'tcx>, - pat_id: HirId, - source: LetSource, - pat: &'p DeconstructedPat<'p, 'tcx>, - span: Span, - ) { - if is_let_irrefutable(cx, pat_id, pat) { - irrefutable_let_patterns(cx.tcx, pat_id, source, 1, span); - } - } - #[instrument(level = "trace", skip(self))] - fn check_let_chain(&mut self, let_source: LetSource, expr: &Expr<'tcx>) { - if let LetSource::None = let_source { - return; - } - + fn check_let_chain(&mut self, expr: &Expr<'tcx>) { + assert!(self.let_source != LetSource::None); let top_expr_span = expr.span; // Lint level enclosing `next_expr`. @@ -336,7 +332,7 @@ impl<'p, 'tcx> MatchVisitor<'_, 'p, 'tcx> { // and record chain members that aren't let exprs. let mut chain_refutabilities = Vec::new(); - let mut got_lowering_error = false; + let mut got_error = false; let mut next_expr = Some(expr); while let Some(mut expr) = next_expr { while let ExprKind::Scope { value, lint_level, .. } = expr.kind { @@ -364,14 +360,15 @@ impl<'p, 'tcx> MatchVisitor<'_, 'p, 'tcx> { } let value = match expr.kind { ExprKind::Let { box ref pat, expr: _ } => { - let mut ncx = self.new_cx(expr_lint_level, true); - if let Ok(tpat) = self.lower_pattern(&mut ncx, pat) { - let refutable = !is_let_irrefutable(&mut ncx, expr_lint_level, tpat); - Some((expr.span, refutable)) - } else { - got_lowering_error = true; - None - } + self.with_lint_level(LintLevel::Explicit(expr_lint_level), |this| { + match this.is_let_irrefutable(pat) { + Ok(irrefutable) => Some((expr.span, !irrefutable)), + Err(_) => { + got_error = true; + None + } + } + }) } _ => None, }; @@ -380,17 +377,17 @@ impl<'p, 'tcx> MatchVisitor<'_, 'p, 'tcx> { debug!(?chain_refutabilities); chain_refutabilities.reverse(); - if got_lowering_error { + if got_error { return; } - // Third, emit the actual warnings. + // Emit the actual warnings. if chain_refutabilities.iter().all(|r| matches!(*r, Some((_, false)))) { // The entire chain is made up of irrefutable `let` statements - irrefutable_let_patterns( + report_irrefutable_let_patterns( self.tcx, self.lint_level, - let_source, + self.let_source, chain_refutabilities.len(), top_expr_span, ); @@ -409,7 +406,7 @@ impl<'p, 'tcx> MatchVisitor<'_, 'p, 'tcx> { // so can't always be moved out. // FIXME: Add checking whether the bindings are actually used in the prefix, // and lint if they are not. - if !matches!(let_source, LetSource::WhileLet | LetSource::IfLetGuard) { + if !matches!(self.let_source, LetSource::WhileLet | LetSource::IfLetGuard) { // Emit the lint let prefix = &chain_refutabilities[..until]; let span_start = prefix[0].unwrap().0; @@ -444,18 +441,33 @@ impl<'p, 'tcx> MatchVisitor<'_, 'p, 'tcx> { } } - #[instrument(level = "trace", skip(self))] - fn check_irrefutable(&mut self, pat: &Pat<'tcx>, origin: &str, sp: Option) { - let mut cx = self.new_cx(self.lint_level, false); + fn analyze_binding( + &mut self, + pat: &Pat<'tcx>, + refutability: RefutableFlag, + ) -> Result<(MatchCheckCtxt<'p, 'tcx>, UsefulnessReport<'p, 'tcx>), ErrorGuaranteed> { + let cx = self.new_cx(refutability); + let pat = self.lower_pattern(&cx, pat)?; + let arms = [MatchArm { pat, hir_id: self.lint_level, has_guard: false }]; + let report = compute_match_usefulness(&cx, &arms, self.lint_level, pat.ty(), pat.span()); + Ok((cx, report)) + } - let Ok(pattern) = self.lower_pattern(&mut cx, pat) else { return }; - let pattern_ty = pattern.ty(); - let arm = MatchArm { pat: pattern, hir_id: self.lint_level, has_guard: false }; - let report = - compute_match_usefulness(&cx, &[arm], self.lint_level, pattern_ty, pattern.span()); + fn is_let_irrefutable(&mut self, pat: &Pat<'tcx>) -> Result { + let (cx, report) = self.analyze_binding(pat, Refutable)?; + // Report if the pattern is unreachable, which can only occur when the type is + // uninhabited. This also reports unreachable sub-patterns. + report_arm_reachability(&cx, &report); + // If the list of witnesses is empty, the match is exhaustive, + // i.e. the `if let` pattern is irrefutable. + Ok(report.non_exhaustiveness_witnesses.is_empty()) + } - // Note: we ignore whether the pattern is unreachable (i.e. whether the type is empty). We - // only care about exhaustiveness here. + #[instrument(level = "trace", skip(self))] + fn check_binding_is_irrefutable(&mut self, pat: &Pat<'tcx>, origin: &str, sp: Option) { + let pattern_ty = pat.ty; + + let Ok((cx, report)) = self.analyze_binding(pat, Irrefutable) else { return }; let witnesses = report.non_exhaustiveness_witnesses; if witnesses.is_empty() { // The pattern is irrefutable. @@ -509,16 +521,16 @@ impl<'p, 'tcx> MatchVisitor<'_, 'p, 'tcx> { // Emit an extra note if the first uncovered witness would be uninhabited // if we disregard visibility. - let witness_1_is_privately_uninhabited = if cx.tcx.features().exhaustive_patterns + let witness_1_is_privately_uninhabited = if self.tcx.features().exhaustive_patterns && let Some(witness_1) = witnesses.get(0) && let ty::Adt(adt, args) = witness_1.ty().kind() && adt.is_enum() && let Constructor::Variant(variant_index) = witness_1.ctor() { let variant = adt.variant(*variant_index); - let inhabited = variant.inhabited_predicate(cx.tcx, *adt).instantiate(cx.tcx, args); - assert!(inhabited.apply(cx.tcx, cx.param_env, cx.module)); - !inhabited.apply_ignore_module(cx.tcx, cx.param_env) + let inhabited = variant.inhabited_predicate(self.tcx, *adt).instantiate(self.tcx, args); + assert!(inhabited.apply(self.tcx, cx.param_env, cx.module)); + !inhabited.apply_ignore_module(self.tcx, cx.param_env) } else { false }; @@ -673,7 +685,7 @@ fn check_for_bindings_named_same_as_variants( 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`. + // suggestion would produce code that breaks on `check_binding_is_irrefutable`. suggestion: if rf == Refutable || variant_count == 1 { Some(pat.span) } else { @@ -686,7 +698,7 @@ fn check_for_bindings_named_same_as_variants( } } -fn irrefutable_let_patterns( +fn report_irrefutable_let_patterns( tcx: TyCtxt<'_>, id: HirId, source: LetSource, @@ -700,7 +712,7 @@ fn irrefutable_let_patterns( } match source { - LetSource::None => bug!(), + LetSource::None | LetSource::PlainLet => bug!(), LetSource::IfLet => emit_diag!(IrrefutableLetPatternsIfLet), LetSource::IfLetGuard => emit_diag!(IrrefutableLetPatternsIfLetGuard), LetSource::LetElse => emit_diag!(IrrefutableLetPatternsLetElse), @@ -708,24 +720,6 @@ fn irrefutable_let_patterns( } } -fn is_let_irrefutable<'p, 'tcx>( - cx: &mut MatchCheckCtxt<'p, 'tcx>, - pat_id: HirId, - pat: &'p DeconstructedPat<'p, 'tcx>, -) -> bool { - let arms = [MatchArm { pat, hir_id: pat_id, has_guard: false }]; - let report = compute_match_usefulness(&cx, &arms, pat_id, pat.ty(), pat.span()); - - // Report if the pattern is unreachable, which can only occur when the type is uninhabited. - // This also reports unreachable sub-patterns though, so we can't just replace it with an - // `is_uninhabited` check. - report_arm_reachability(&cx, &report); - - // If the list of witnesses is empty, the match is exhaustive, - // i.e. the `if let` pattern is irrefutable. - report.non_exhaustiveness_witnesses.is_empty() -} - /// Report unreachable arms, if any. fn report_arm_reachability<'p, 'tcx>( cx: &MatchCheckCtxt<'p, 'tcx>, @@ -776,7 +770,7 @@ fn pat_is_catchall(pat: &DeconstructedPat<'_, '_>) -> bool { } /// Report that a match is not exhaustive. -fn non_exhaustive_match<'p, 'tcx>( +fn report_non_exhaustive_match<'p, 'tcx>( cx: &MatchCheckCtxt<'p, 'tcx>, thir: &Thir<'tcx>, scrut_ty: Ty<'tcx>, From 3984914affa48bb6a65a68844a0ddff498c30512 Mon Sep 17 00:00:00 2001 From: Josh Stone Date: Fri, 3 Nov 2023 09:17:16 -0700 Subject: [PATCH 09/12] Use `filter_map` in `try_par_for_each_in` This simplifies the expression, especially for the rayon part, and also lets us drop the `E: Copy` constraint. --- compiler/rustc_data_structures/src/sync/parallel.rs | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/compiler/rustc_data_structures/src/sync/parallel.rs b/compiler/rustc_data_structures/src/sync/parallel.rs index 39dddb5956911..5695d839d3eb1 100644 --- a/compiler/rustc_data_structures/src/sync/parallel.rs +++ b/compiler/rustc_data_structures/src/sync/parallel.rs @@ -77,12 +77,12 @@ mod disabled { }) } - pub fn try_par_for_each_in( + pub fn try_par_for_each_in( t: T, mut for_each: impl FnMut(T::Item) -> Result<(), E>, ) -> Result<(), E> { parallel_guard(|guard| { - t.into_iter().fold(Ok(()), |ret, i| guard.run(|| for_each(i)).unwrap_or(ret).and(ret)) + t.into_iter().filter_map(|i| guard.run(|| for_each(i))).fold(Ok(()), Result::and) }) } @@ -178,7 +178,7 @@ mod enabled { pub fn try_par_for_each_in< T: IntoIterator + IntoParallelIterator::Item>, - E: Copy + Send, + E: Send, >( t: T, for_each: impl Fn(::Item) -> Result<(), E> + DynSync + DynSend, @@ -187,11 +187,10 @@ mod enabled { if mode::is_dyn_thread_safe() { let for_each = FromDyn::from(for_each); t.into_par_iter() - .fold_with(Ok(()), |ret, i| guard.run(|| for_each(i)).unwrap_or(ret).and(ret)) - .reduce(|| Ok(()), |a, b| a.and(b)) + .filter_map(|i| guard.run(|| for_each(i))) + .reduce(|| Ok(()), Result::and) } else { - t.into_iter() - .fold(Ok(()), |ret, i| guard.run(|| for_each(i)).unwrap_or(ret).and(ret)) + t.into_iter().filter_map(|i| guard.run(|| for_each(i))).fold(Ok(()), Result::and) } }) } From 335156ca732ec32fc3fb82b44516f8fa25d19576 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Sun, 29 Oct 2023 09:20:15 +0100 Subject: [PATCH 10/12] Accumulate let chains alongside the visit --- .../src/thir/pattern/check_match.rs | 154 +++++++++--------- 1 file changed, 78 insertions(+), 76 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 eadd7a310b718..d94272bcc796b 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs @@ -56,7 +56,7 @@ fn create_e0004( struct_span_err!(sess, sp, E0004, "{}", &error_message) } -#[derive(Copy, Clone, PartialEq)] +#[derive(Debug, Copy, Clone, PartialEq)] enum RefutableFlag { Irrefutable, Refutable, @@ -151,18 +151,22 @@ impl<'thir, 'tcx> Visitor<'thir, 'tcx> for MatchVisitor<'thir, '_, 'tcx> { }; self.check_match(scrutinee, arms, source, ex.span); } - ExprKind::Let { box ref pat, expr } if !matches!(self.let_source, LetSource::None) => { + ExprKind::Let { box ref pat, expr } => { self.check_let(pat, Some(expr), ex.span); } ExprKind::LogicalOp { op: LogicalOp::And, .. } if !matches!(self.let_source, LetSource::None) => { - self.check_let_chain(ex); + let mut chain_refutabilities = Vec::new(); + let Ok(()) = self.visit_land(ex, &mut chain_refutabilities) else { return }; + // If at least one of the operands is a `let ... = ...`. + if chain_refutabilities.iter().any(|x| x.is_some()) { + self.check_let_chain(chain_refutabilities, ex.span); + } + return; } _ => {} }; - // If we got e.g. `let pat1 = x1 && let pat2 = x2` above, we will now traverse the two - // `let`s. In order not to check them twice we set `LetSource::None`. self.with_let_source(LetSource::None, |this| visit::walk_expr(this, ex)); } @@ -212,6 +216,58 @@ impl<'thir, 'p, 'tcx> MatchVisitor<'thir, 'p, 'tcx> { } } + /// Visit a nested chain of `&&`. Used for if-let chains. This must call `visit_expr` on the + /// subexpressions we are not handling ourselves. + fn visit_land( + &mut self, + ex: &Expr<'tcx>, + accumulator: &mut Vec>, + ) -> Result<(), ErrorGuaranteed> { + match ex.kind { + ExprKind::Scope { value, lint_level, .. } => self.with_lint_level(lint_level, |this| { + this.visit_land(&this.thir[value], accumulator) + }), + ExprKind::LogicalOp { op: LogicalOp::And, lhs, rhs } => { + // We recurse into the lhs only, because `&&` chains associate to the left. + let res_lhs = self.visit_land(&self.thir[lhs], accumulator); + let res_rhs = self.visit_land_rhs(&self.thir[rhs])?; + accumulator.push(res_rhs); + res_lhs + } + _ => { + let res = self.visit_land_rhs(ex)?; + accumulator.push(res); + Ok(()) + } + } + } + + /// Visit the right-hand-side of a `&&`. Used for if-let chains. Returns `Some` if the + /// expression was ultimately a `let ... = ...`, and `None` if it was a normal boolean + /// expression. This must call `visit_expr` on the subexpressions we are not handling ourselves. + fn visit_land_rhs( + &mut self, + ex: &Expr<'tcx>, + ) -> Result, ErrorGuaranteed> { + match ex.kind { + ExprKind::Scope { value, lint_level, .. } => { + self.with_lint_level(lint_level, |this| this.visit_land_rhs(&this.thir[value])) + } + ExprKind::Let { box ref pat, expr } => { + self.with_let_source(LetSource::None, |this| { + this.visit_expr(&this.thir()[expr]); + }); + Ok(Some((ex.span, self.is_let_irrefutable(pat)?))) + } + _ => { + self.with_let_source(LetSource::None, |this| { + this.visit_expr(ex); + }); + Ok(None) + } + } + } + fn lower_pattern( &mut self, cx: &MatchCheckCtxt<'p, 'tcx>, @@ -249,8 +305,8 @@ impl<'thir, 'p, 'tcx> MatchVisitor<'thir, 'p, 'tcx> { if let LetSource::PlainLet = self.let_source { self.check_binding_is_irrefutable(pat, "local binding", Some(span)) } else { - let Ok(irrefutable) = self.is_let_irrefutable(pat) else { return }; - if irrefutable { + let Ok(refutability) = self.is_let_irrefutable(pat) else { return }; + if matches!(refutability, Irrefutable) { report_irrefutable_let_patterns( self.tcx, self.lint_level, @@ -321,81 +377,27 @@ impl<'thir, 'p, 'tcx> MatchVisitor<'thir, 'p, 'tcx> { } #[instrument(level = "trace", skip(self))] - fn check_let_chain(&mut self, expr: &Expr<'tcx>) { + fn check_let_chain( + &mut self, + chain_refutabilities: Vec>, + whole_chain_span: Span, + ) { assert!(self.let_source != LetSource::None); - let top_expr_span = expr.span; - - // Lint level enclosing `next_expr`. - let mut next_expr_lint_level = self.lint_level; - - // Obtain the refutabilities of all exprs in the chain, - // and record chain members that aren't let exprs. - let mut chain_refutabilities = Vec::new(); - - let mut got_error = false; - let mut next_expr = Some(expr); - while let Some(mut expr) = next_expr { - while let ExprKind::Scope { value, lint_level, .. } = expr.kind { - if let LintLevel::Explicit(hir_id) = lint_level { - next_expr_lint_level = hir_id - } - expr = &self.thir[value]; - } - if let ExprKind::LogicalOp { op: LogicalOp::And, lhs, rhs } = expr.kind { - expr = &self.thir[rhs]; - // Let chains recurse on the left, so we recurse into the lhs. - next_expr = Some(&self.thir[lhs]); - } else { - next_expr = None; - } - - // Lint level enclosing `expr`. - let mut expr_lint_level = next_expr_lint_level; - // Fast-forward through scopes. - while let ExprKind::Scope { value, lint_level, .. } = expr.kind { - if let LintLevel::Explicit(hir_id) = lint_level { - expr_lint_level = hir_id - } - expr = &self.thir[value]; - } - let value = match expr.kind { - ExprKind::Let { box ref pat, expr: _ } => { - self.with_lint_level(LintLevel::Explicit(expr_lint_level), |this| { - match this.is_let_irrefutable(pat) { - Ok(irrefutable) => Some((expr.span, !irrefutable)), - Err(_) => { - got_error = true; - None - } - } - }) - } - _ => None, - }; - chain_refutabilities.push(value); - } - debug!(?chain_refutabilities); - chain_refutabilities.reverse(); - - if got_error { - return; - } - // Emit the actual warnings. - if chain_refutabilities.iter().all(|r| matches!(*r, Some((_, false)))) { + if chain_refutabilities.iter().all(|r| matches!(*r, Some((_, Irrefutable)))) { // The entire chain is made up of irrefutable `let` statements report_irrefutable_let_patterns( self.tcx, self.lint_level, self.let_source, chain_refutabilities.len(), - top_expr_span, + whole_chain_span, ); return; } if let Some(until) = - chain_refutabilities.iter().position(|r| !matches!(*r, Some((_, false)))) + chain_refutabilities.iter().position(|r| !matches!(*r, Some((_, Irrefutable)))) && until > 0 { // The chain has a non-zero prefix of irrefutable `let` statements. @@ -423,7 +425,7 @@ impl<'thir, 'p, 'tcx> MatchVisitor<'thir, 'p, 'tcx> { } if let Some(from) = - chain_refutabilities.iter().rposition(|r| !matches!(*r, Some((_, false)))) + chain_refutabilities.iter().rposition(|r| !matches!(*r, Some((_, Irrefutable)))) && from != (chain_refutabilities.len() - 1) { // The chain has a non-empty suffix of irrefutable `let` statements @@ -453,14 +455,14 @@ impl<'thir, 'p, 'tcx> MatchVisitor<'thir, 'p, 'tcx> { Ok((cx, report)) } - fn is_let_irrefutable(&mut self, pat: &Pat<'tcx>) -> Result { + fn is_let_irrefutable(&mut self, pat: &Pat<'tcx>) -> Result { let (cx, report) = self.analyze_binding(pat, Refutable)?; - // Report if the pattern is unreachable, which can only occur when the type is - // uninhabited. This also reports unreachable sub-patterns. + // Report if the pattern is unreachable, which can only occur when the type is uninhabited. + // This also reports unreachable sub-patterns. report_arm_reachability(&cx, &report); - // If the list of witnesses is empty, the match is exhaustive, - // i.e. the `if let` pattern is irrefutable. - Ok(report.non_exhaustiveness_witnesses.is_empty()) + // If the list of witnesses is empty, the match is exhaustive, i.e. the `if let` pattern is + // irrefutable. + Ok(if report.non_exhaustiveness_witnesses.is_empty() { Irrefutable } else { Refutable }) } #[instrument(level = "trace", skip(self))] From 746197c08a737828353a5ace272b0fa92dcd578a Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Sun, 29 Oct 2023 06:50:59 +0100 Subject: [PATCH 11/12] Tweak spans for "adt defined here" note --- .../src/thir/pattern/check_match.rs | 7 +-- .../match/non-exhaustive-match.stderr | 4 +- tests/ui/error-codes/E0004.stderr | 6 +-- ...on_exhaustive_omitted_patterns_lint.stderr | 6 +-- tests/ui/match/match_non_exhaustive.stderr | 4 +- tests/ui/pattern/issue-94866.stderr | 4 +- .../doc-hidden-non-exhaustive.stderr | 18 +++---- .../empty-match.exhaustive_patterns.stderr | 46 +++++++++------- .../usefulness/empty-match.normal.stderr | 46 +++++++++------- tests/ui/pattern/usefulness/empty-match.rs | 12 ++--- .../ui/pattern/usefulness/issue-39362.stderr | 6 +-- .../ui/pattern/usefulness/issue-40221.stderr | 6 +-- .../ui/pattern/usefulness/issue-56379.stderr | 10 ++-- .../usefulness/non-exhaustive-defined-here.rs | 18 ++++--- .../non-exhaustive-defined-here.stderr | 54 +++++++++---------- .../non-exhaustive-match-nested.stderr | 4 +- .../usefulness/non-exhaustive-match.stderr | 8 +-- .../non-exhaustive-pattern-witness.stderr | 36 ++++++------- .../usefulness/stable-gated-patterns.stderr | 6 +-- .../struct-like-enum-nonexhaustive.stderr | 6 +-- .../usefulness/unstable-gated-patterns.stderr | 6 +-- .../enum_same_crate_empty_match.stderr | 20 +++---- .../uninhabited/match.stderr | 8 +-- .../uninhabited/match_same_crate.stderr | 8 +-- .../match_with_exhaustive_patterns.stderr | 8 +-- 25 files changed, 183 insertions(+), 174 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 d94272bcc796b..165d3f37111e9 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs @@ -830,12 +830,7 @@ fn report_non_exhaustive_match<'p, 'tcx>( if let Some(AdtDefinedHere { adt_def_span, ty, variants }) = report_adt_defined_here(cx.tcx, scrut_ty, &witnesses, true) { - let mut multi_span = if variants.is_empty() { - MultiSpan::from_span(adt_def_span) - } else { - MultiSpan::from_spans(variants.iter().map(|Variant { span }| *span).collect()) - }; - + let mut multi_span = MultiSpan::from_span(adt_def_span); multi_span.push_span_label(adt_def_span, ""); for Variant { span } in variants { multi_span.push_span_label(span, "not covered"); diff --git a/tests/ui/closures/2229_closure_analysis/match/non-exhaustive-match.stderr b/tests/ui/closures/2229_closure_analysis/match/non-exhaustive-match.stderr index 0807f4590298f..85426dd9a5ea6 100644 --- a/tests/ui/closures/2229_closure_analysis/match/non-exhaustive-match.stderr +++ b/tests/ui/closures/2229_closure_analysis/match/non-exhaustive-match.stderr @@ -5,10 +5,10 @@ LL | let _b = || { match l1 { L1::A => () } }; | ^^ pattern `L1::B` not covered | note: `L1` defined here - --> $DIR/non-exhaustive-match.rs:12:14 + --> $DIR/non-exhaustive-match.rs:12:6 | LL | enum L1 { A, B } - | -- ^ not covered + | ^^ - not covered = note: the matched value is of type `L1` help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown | diff --git a/tests/ui/error-codes/E0004.stderr b/tests/ui/error-codes/E0004.stderr index 603bc5237ea20..ced478d65eaec 100644 --- a/tests/ui/error-codes/E0004.stderr +++ b/tests/ui/error-codes/E0004.stderr @@ -5,12 +5,12 @@ LL | match x { | ^ pattern `Terminator::HastaLaVistaBaby` not covered | note: `Terminator` defined here - --> $DIR/E0004.rs:2:5 + --> $DIR/E0004.rs:1:6 | LL | enum Terminator { - | ---------- + | ^^^^^^^^^^ LL | HastaLaVistaBaby, - | ^^^^^^^^^^^^^^^^ not covered + | ---------------- not covered = note: the matched value is of type `Terminator` help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown | diff --git a/tests/ui/feature-gates/feature-gate-non_exhaustive_omitted_patterns_lint.stderr b/tests/ui/feature-gates/feature-gate-non_exhaustive_omitted_patterns_lint.stderr index eb61c4cf159ab..8af0eedc82b1a 100644 --- a/tests/ui/feature-gates/feature-gate-non_exhaustive_omitted_patterns_lint.stderr +++ b/tests/ui/feature-gates/feature-gate-non_exhaustive_omitted_patterns_lint.stderr @@ -134,13 +134,13 @@ LL | match Foo::A { | ^^^^^^ pattern `Foo::C` not covered | note: `Foo` defined here - --> $DIR/feature-gate-non_exhaustive_omitted_patterns_lint.rs:16:9 + --> $DIR/feature-gate-non_exhaustive_omitted_patterns_lint.rs:13:10 | LL | enum Foo { - | --- + | ^^^ ... LL | C, - | ^ not covered + | - not covered = note: the matched value is of type `Foo` help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown | diff --git a/tests/ui/match/match_non_exhaustive.stderr b/tests/ui/match/match_non_exhaustive.stderr index 7b8bdfe0053ae..40be39ec07747 100644 --- a/tests/ui/match/match_non_exhaustive.stderr +++ b/tests/ui/match/match_non_exhaustive.stderr @@ -5,10 +5,10 @@ LL | match l { L::A => () }; | ^ pattern `L::B` not covered | note: `L` defined here - --> $DIR/match_non_exhaustive.rs:10:13 + --> $DIR/match_non_exhaustive.rs:10:6 | LL | enum L { A, B } - | - ^ not covered + | ^ - not covered = note: the matched value is of type `L` help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown | diff --git a/tests/ui/pattern/issue-94866.stderr b/tests/ui/pattern/issue-94866.stderr index b3c17ce8974df..dee4b3f557c7d 100644 --- a/tests/ui/pattern/issue-94866.stderr +++ b/tests/ui/pattern/issue-94866.stderr @@ -5,10 +5,10 @@ LL | match Enum::A { | ^^^^^^^ pattern `Enum::B` not covered | note: `Enum` defined here - --> $DIR/issue-94866.rs:7:16 + --> $DIR/issue-94866.rs:7:6 | LL | enum Enum { A, B } - | ---- ^ not covered + | ^^^^ - not covered = note: the matched value is of type `Enum` help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown | diff --git a/tests/ui/pattern/usefulness/doc-hidden-non-exhaustive.stderr b/tests/ui/pattern/usefulness/doc-hidden-non-exhaustive.stderr index ff29de03d6b36..24f3eaa5230d7 100644 --- a/tests/ui/pattern/usefulness/doc-hidden-non-exhaustive.stderr +++ b/tests/ui/pattern/usefulness/doc-hidden-non-exhaustive.stderr @@ -23,13 +23,13 @@ LL | match HiddenEnum::A { | ^^^^^^^^^^^^^ pattern `HiddenEnum::B` not covered | note: `HiddenEnum` defined here - --> $DIR/auxiliary/hidden.rs:3:5 + --> $DIR/auxiliary/hidden.rs:1:1 | LL | pub enum HiddenEnum { - | ------------------- + | ^^^^^^^^^^^^^^^^^^^ LL | A, LL | B, - | ^ not covered + | - not covered = note: the matched value is of type `HiddenEnum` help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown | @@ -44,13 +44,13 @@ LL | match HiddenEnum::A { | ^^^^^^^^^^^^^ patterns `HiddenEnum::B` and `_` not covered | note: `HiddenEnum` defined here - --> $DIR/auxiliary/hidden.rs:3:5 + --> $DIR/auxiliary/hidden.rs:1:1 | LL | pub enum HiddenEnum { - | ------------------- + | ^^^^^^^^^^^^^^^^^^^ LL | A, LL | B, - | ^ not covered + | - not covered = note: the matched value is of type `HiddenEnum` help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern, a match arm with multiple or-patterns as shown, or multiple match arms | @@ -83,13 +83,13 @@ LL | match InCrate::A { | ^^^^^^^^^^ pattern `InCrate::C` not covered | note: `InCrate` defined here - --> $DIR/doc-hidden-non-exhaustive.rs:11:5 + --> $DIR/doc-hidden-non-exhaustive.rs:7:6 | LL | enum InCrate { - | ------- + | ^^^^^^^ ... LL | C, - | ^ not covered + | - not covered = note: the matched value is of type `InCrate` help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown | diff --git a/tests/ui/pattern/usefulness/empty-match.exhaustive_patterns.stderr b/tests/ui/pattern/usefulness/empty-match.exhaustive_patterns.stderr index fce1d8d19280d..8f9bd5bde89aa 100644 --- a/tests/ui/pattern/usefulness/empty-match.exhaustive_patterns.stderr +++ b/tests/ui/pattern/usefulness/empty-match.exhaustive_patterns.stderr @@ -127,12 +127,13 @@ LL | match_no_arms!(NonEmptyEnum1::Foo(true)); | ^^^^^^^^^^^^^^^^^^^^^^^^ pattern `NonEmptyEnum1::Foo(_)` not covered | note: `NonEmptyEnum1` defined here - --> $DIR/empty-match.rs:33:5 + --> $DIR/empty-match.rs:32:6 | LL | enum NonEmptyEnum1 { - | ------------- + | ^^^^^^^^^^^^^ +... LL | Foo(bool), - | ^^^ not covered + | --- not covered = note: the matched value is of type `NonEmptyEnum1` = help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern @@ -143,15 +144,16 @@ LL | match_no_arms!(NonEmptyEnum2::Foo(true)); | ^^^^^^^^^^^^^^^^^^^^^^^^ patterns `NonEmptyEnum2::Foo(_)` and `NonEmptyEnum2::Bar` not covered | note: `NonEmptyEnum2` defined here - --> $DIR/empty-match.rs:40:5 + --> $DIR/empty-match.rs:39:6 | LL | enum NonEmptyEnum2 { - | ------------- + | ^^^^^^^^^^^^^ +... LL | Foo(bool), - | ^^^ not covered + | --- not covered ... LL | Bar, - | ^^^ not covered + | --- not covered = note: the matched value is of type `NonEmptyEnum2` = help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or multiple match arms @@ -162,12 +164,13 @@ LL | match_no_arms!(NonEmptyEnum5::V1); | ^^^^^^^^^^^^^^^^^ patterns `NonEmptyEnum5::V1`, `NonEmptyEnum5::V2`, `NonEmptyEnum5::V3` and 2 more not covered | note: `NonEmptyEnum5` defined here - --> $DIR/empty-match.rs:50:5 + --> $DIR/empty-match.rs:49:6 | LL | enum NonEmptyEnum5 { - | ------------- + | ^^^^^^^^^^^^^ +... LL | V1, V2, V3, V4, V5, - | ^^ ^^ ^^ ^^ ^^ not covered + | -- -- -- -- -- not covered | | | | | | | | | not covered | | | not covered @@ -273,12 +276,13 @@ LL | match_guarded_arm!(NonEmptyEnum1::Foo(true)); | ^^^^^^^^^^^^^^^^^^^^^^^^ pattern `NonEmptyEnum1::Foo(_)` not covered | note: `NonEmptyEnum1` defined here - --> $DIR/empty-match.rs:33:5 + --> $DIR/empty-match.rs:32:6 | LL | enum NonEmptyEnum1 { - | ------------- + | ^^^^^^^^^^^^^ +... LL | Foo(bool), - | ^^^ not covered + | --- not covered = note: the matched value is of type `NonEmptyEnum1` = note: match arms with guards don't count towards exhaustivity help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown @@ -294,15 +298,16 @@ LL | match_guarded_arm!(NonEmptyEnum2::Foo(true)); | ^^^^^^^^^^^^^^^^^^^^^^^^ patterns `NonEmptyEnum2::Foo(_)` and `NonEmptyEnum2::Bar` not covered | note: `NonEmptyEnum2` defined here - --> $DIR/empty-match.rs:40:5 + --> $DIR/empty-match.rs:39:6 | LL | enum NonEmptyEnum2 { - | ------------- + | ^^^^^^^^^^^^^ +... LL | Foo(bool), - | ^^^ not covered + | --- not covered ... LL | Bar, - | ^^^ not covered + | --- not covered = note: the matched value is of type `NonEmptyEnum2` = note: match arms with guards don't count towards exhaustivity help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern, a match arm with multiple or-patterns as shown, or multiple match arms @@ -318,12 +323,13 @@ LL | match_guarded_arm!(NonEmptyEnum5::V1); | ^^^^^^^^^^^^^^^^^ patterns `NonEmptyEnum5::V1`, `NonEmptyEnum5::V2`, `NonEmptyEnum5::V3` and 2 more not covered | note: `NonEmptyEnum5` defined here - --> $DIR/empty-match.rs:50:5 + --> $DIR/empty-match.rs:49:6 | LL | enum NonEmptyEnum5 { - | ------------- + | ^^^^^^^^^^^^^ +... LL | V1, V2, V3, V4, V5, - | ^^ ^^ ^^ ^^ ^^ not covered + | -- -- -- -- -- not covered | | | | | | | | | not covered | | | not covered diff --git a/tests/ui/pattern/usefulness/empty-match.normal.stderr b/tests/ui/pattern/usefulness/empty-match.normal.stderr index 97aac68c8d93b..7f0389f40e233 100644 --- a/tests/ui/pattern/usefulness/empty-match.normal.stderr +++ b/tests/ui/pattern/usefulness/empty-match.normal.stderr @@ -126,12 +126,13 @@ LL | match_no_arms!(NonEmptyEnum1::Foo(true)); | ^^^^^^^^^^^^^^^^^^^^^^^^ pattern `NonEmptyEnum1::Foo(_)` not covered | note: `NonEmptyEnum1` defined here - --> $DIR/empty-match.rs:33:5 + --> $DIR/empty-match.rs:32:6 | LL | enum NonEmptyEnum1 { - | ------------- + | ^^^^^^^^^^^^^ +... LL | Foo(bool), - | ^^^ not covered + | --- not covered = note: the matched value is of type `NonEmptyEnum1` = help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern @@ -142,15 +143,16 @@ LL | match_no_arms!(NonEmptyEnum2::Foo(true)); | ^^^^^^^^^^^^^^^^^^^^^^^^ patterns `NonEmptyEnum2::Foo(_)` and `NonEmptyEnum2::Bar` not covered | note: `NonEmptyEnum2` defined here - --> $DIR/empty-match.rs:40:5 + --> $DIR/empty-match.rs:39:6 | LL | enum NonEmptyEnum2 { - | ------------- + | ^^^^^^^^^^^^^ +... LL | Foo(bool), - | ^^^ not covered + | --- not covered ... LL | Bar, - | ^^^ not covered + | --- not covered = note: the matched value is of type `NonEmptyEnum2` = help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or multiple match arms @@ -161,12 +163,13 @@ LL | match_no_arms!(NonEmptyEnum5::V1); | ^^^^^^^^^^^^^^^^^ patterns `NonEmptyEnum5::V1`, `NonEmptyEnum5::V2`, `NonEmptyEnum5::V3` and 2 more not covered | note: `NonEmptyEnum5` defined here - --> $DIR/empty-match.rs:50:5 + --> $DIR/empty-match.rs:49:6 | LL | enum NonEmptyEnum5 { - | ------------- + | ^^^^^^^^^^^^^ +... LL | V1, V2, V3, V4, V5, - | ^^ ^^ ^^ ^^ ^^ not covered + | -- -- -- -- -- not covered | | | | | | | | | not covered | | | not covered @@ -272,12 +275,13 @@ LL | match_guarded_arm!(NonEmptyEnum1::Foo(true)); | ^^^^^^^^^^^^^^^^^^^^^^^^ pattern `NonEmptyEnum1::Foo(_)` not covered | note: `NonEmptyEnum1` defined here - --> $DIR/empty-match.rs:33:5 + --> $DIR/empty-match.rs:32:6 | LL | enum NonEmptyEnum1 { - | ------------- + | ^^^^^^^^^^^^^ +... LL | Foo(bool), - | ^^^ not covered + | --- not covered = note: the matched value is of type `NonEmptyEnum1` = note: match arms with guards don't count towards exhaustivity help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown @@ -293,15 +297,16 @@ LL | match_guarded_arm!(NonEmptyEnum2::Foo(true)); | ^^^^^^^^^^^^^^^^^^^^^^^^ patterns `NonEmptyEnum2::Foo(_)` and `NonEmptyEnum2::Bar` not covered | note: `NonEmptyEnum2` defined here - --> $DIR/empty-match.rs:40:5 + --> $DIR/empty-match.rs:39:6 | LL | enum NonEmptyEnum2 { - | ------------- + | ^^^^^^^^^^^^^ +... LL | Foo(bool), - | ^^^ not covered + | --- not covered ... LL | Bar, - | ^^^ not covered + | --- not covered = note: the matched value is of type `NonEmptyEnum2` = note: match arms with guards don't count towards exhaustivity help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern, a match arm with multiple or-patterns as shown, or multiple match arms @@ -317,12 +322,13 @@ LL | match_guarded_arm!(NonEmptyEnum5::V1); | ^^^^^^^^^^^^^^^^^ patterns `NonEmptyEnum5::V1`, `NonEmptyEnum5::V2`, `NonEmptyEnum5::V3` and 2 more not covered | note: `NonEmptyEnum5` defined here - --> $DIR/empty-match.rs:50:5 + --> $DIR/empty-match.rs:49:6 | LL | enum NonEmptyEnum5 { - | ------------- + | ^^^^^^^^^^^^^ +... LL | V1, V2, V3, V4, V5, - | ^^ ^^ ^^ ^^ ^^ not covered + | -- -- -- -- -- not covered | | | | | | | | | not covered | | | not covered diff --git a/tests/ui/pattern/usefulness/empty-match.rs b/tests/ui/pattern/usefulness/empty-match.rs index 6551900bbc6c2..fe5d0bce14fe3 100644 --- a/tests/ui/pattern/usefulness/empty-match.rs +++ b/tests/ui/pattern/usefulness/empty-match.rs @@ -30,27 +30,27 @@ union NonEmptyUnion2 { bar: (), } enum NonEmptyEnum1 { - Foo(bool), //~^ NOTE `NonEmptyEnum1` defined here //~| NOTE `NonEmptyEnum1` defined here - //~| NOTE not covered + Foo(bool), + //~^ NOTE not covered //~| NOTE not covered } enum NonEmptyEnum2 { - Foo(bool), //~^ NOTE `NonEmptyEnum2` defined here //~| NOTE `NonEmptyEnum2` defined here - //~| NOTE not covered + Foo(bool), + //~^ NOTE not covered //~| NOTE not covered Bar, //~^ NOTE not covered //~| NOTE not covered } enum NonEmptyEnum5 { - V1, V2, V3, V4, V5, //~^ NOTE `NonEmptyEnum5` defined here //~| NOTE `NonEmptyEnum5` defined here - //~| NOTE not covered + V1, V2, V3, V4, V5, + //~^ NOTE not covered //~| NOTE not covered //~| NOTE not covered //~| NOTE not covered diff --git a/tests/ui/pattern/usefulness/issue-39362.stderr b/tests/ui/pattern/usefulness/issue-39362.stderr index b8b17918aef8c..8dc534916061b 100644 --- a/tests/ui/pattern/usefulness/issue-39362.stderr +++ b/tests/ui/pattern/usefulness/issue-39362.stderr @@ -5,12 +5,12 @@ LL | match f { | ^ patterns `Foo::Bar { bar: Bar::C, .. }`, `Foo::Bar { bar: Bar::D, .. }`, `Foo::Bar { bar: Bar::E, .. }` and 1 more not covered | note: `Foo` defined here - --> $DIR/issue-39362.rs:2:5 + --> $DIR/issue-39362.rs:1:6 | LL | enum Foo { - | --- + | ^^^ LL | Bar { bar: Bar, id: usize } - | ^^^ not covered + | --- not covered = note: the matched value is of type `Foo` help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern as shown, or multiple match arms | diff --git a/tests/ui/pattern/usefulness/issue-40221.stderr b/tests/ui/pattern/usefulness/issue-40221.stderr index 4973e42b05447..40b42af26b347 100644 --- a/tests/ui/pattern/usefulness/issue-40221.stderr +++ b/tests/ui/pattern/usefulness/issue-40221.stderr @@ -5,12 +5,12 @@ LL | match proto { | ^^^^^ pattern `P::C(PC::QA)` not covered | note: `P` defined here - --> $DIR/issue-40221.rs:2:5 + --> $DIR/issue-40221.rs:1:6 | LL | enum P { - | - + | ^ LL | C(PC), - | ^ not covered + | - not covered = note: the matched value is of type `P` help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown | diff --git a/tests/ui/pattern/usefulness/issue-56379.stderr b/tests/ui/pattern/usefulness/issue-56379.stderr index b3e40b992399a..50e13bdfdfede 100644 --- a/tests/ui/pattern/usefulness/issue-56379.stderr +++ b/tests/ui/pattern/usefulness/issue-56379.stderr @@ -5,16 +5,16 @@ LL | match Foo::A(true) { | ^^^^^^^^^^^^ patterns `Foo::A(false)`, `Foo::B(false)` and `Foo::C(false)` not covered | note: `Foo` defined here - --> $DIR/issue-56379.rs:2:5 + --> $DIR/issue-56379.rs:1:6 | LL | enum Foo { - | --- + | ^^^ LL | A(bool), - | ^ not covered + | - not covered LL | B(bool), - | ^ not covered + | - not covered LL | C(bool), - | ^ not covered + | - not covered = note: the matched value is of type `Foo` help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern, a match arm with multiple or-patterns as shown, or multiple match arms | diff --git a/tests/ui/pattern/usefulness/non-exhaustive-defined-here.rs b/tests/ui/pattern/usefulness/non-exhaustive-defined-here.rs index 5145f769075d9..1d1ea8e496466 100644 --- a/tests/ui/pattern/usefulness/non-exhaustive-defined-here.rs +++ b/tests/ui/pattern/usefulness/non-exhaustive-defined-here.rs @@ -1,10 +1,15 @@ +#![feature(custom_inner_attributes)] +#![rustfmt::skip] // Test the "defined here" and "not covered" diagnostic hints. // We also make sure that references are peeled off from the scrutinee type // so that the diagnostics work better with default binding modes. #[derive(Clone)] enum E { - //~^ NOTE + //~^ NOTE `E` defined here + //~| NOTE `E` defined here + //~| NOTE `E` defined here + //~| NOTE //~| NOTE //~| NOTE //~| NOTE @@ -12,10 +17,7 @@ enum E { //~| NOTE A, B, - //~^ NOTE `E` defined here - //~| NOTE `E` defined here - //~| NOTE `E` defined here - //~| NOTE not covered + //~^ NOTE not covered //~| NOTE not covered //~| NOTE not covered //~| NOTE not covered @@ -79,12 +81,12 @@ fn by_ref_thrice(e: & &mut &E) { } enum Opt { - //~^ NOTE + //~^ NOTE `Opt` defined here + //~| NOTE //~| NOTE Some(u8), None, - //~^ NOTE `Opt` defined here - //~| NOTE not covered + //~^ NOTE not covered //~| NOTE not covered } diff --git a/tests/ui/pattern/usefulness/non-exhaustive-defined-here.stderr b/tests/ui/pattern/usefulness/non-exhaustive-defined-here.stderr index 8489e2f14b871..a9e55fa53a68e 100644 --- a/tests/ui/pattern/usefulness/non-exhaustive-defined-here.stderr +++ b/tests/ui/pattern/usefulness/non-exhaustive-defined-here.stderr @@ -1,20 +1,20 @@ error[E0004]: non-exhaustive patterns: `E::B` and `E::C` not covered - --> $DIR/non-exhaustive-defined-here.rs:35:11 + --> $DIR/non-exhaustive-defined-here.rs:37:11 | LL | match e1 { | ^^ patterns `E::B` and `E::C` not covered | note: `E` defined here - --> $DIR/non-exhaustive-defined-here.rs:14:5 + --> $DIR/non-exhaustive-defined-here.rs:8:6 | LL | enum E { - | - + | ^ ... LL | B, - | ^ not covered + | - not covered ... LL | C - | ^ not covered + | - not covered = note: the matched value is of type `E` help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern, a match arm with multiple or-patterns as shown, or multiple match arms | @@ -23,7 +23,7 @@ LL + E::B | E::C => todo!() | error[E0005]: refutable pattern in local binding - --> $DIR/non-exhaustive-defined-here.rs:41:9 + --> $DIR/non-exhaustive-defined-here.rs:43:9 | LL | let E::A = e; | ^^^^ patterns `E::B` and `E::C` not covered @@ -31,7 +31,7 @@ LL | let E::A = e; = note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant = note: for more information, visit https://doc.rust-lang.org/book/ch18-02-refutability.html note: `E` defined here - --> $DIR/non-exhaustive-defined-here.rs:6:6 + --> $DIR/non-exhaustive-defined-here.rs:8:6 | LL | enum E { | ^ @@ -48,22 +48,22 @@ LL | if let E::A = e { todo!() }; | ++ +++++++++++ error[E0004]: non-exhaustive patterns: `&E::B` and `&E::C` not covered - --> $DIR/non-exhaustive-defined-here.rs:50:11 + --> $DIR/non-exhaustive-defined-here.rs:52:11 | LL | match e { | ^ patterns `&E::B` and `&E::C` not covered | note: `E` defined here - --> $DIR/non-exhaustive-defined-here.rs:14:5 + --> $DIR/non-exhaustive-defined-here.rs:8:6 | LL | enum E { - | - + | ^ ... LL | B, - | ^ not covered + | - not covered ... LL | C - | ^ not covered + | - not covered = note: the matched value is of type `&E` help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern, a match arm with multiple or-patterns as shown, or multiple match arms | @@ -72,7 +72,7 @@ LL + &E::B | &E::C => todo!() | error[E0005]: refutable pattern in local binding - --> $DIR/non-exhaustive-defined-here.rs:57:9 + --> $DIR/non-exhaustive-defined-here.rs:59:9 | LL | let E::A = e; | ^^^^ patterns `&E::B` and `&E::C` not covered @@ -80,7 +80,7 @@ LL | let E::A = e; = note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant = note: for more information, visit https://doc.rust-lang.org/book/ch18-02-refutability.html note: `E` defined here - --> $DIR/non-exhaustive-defined-here.rs:6:6 + --> $DIR/non-exhaustive-defined-here.rs:8:6 | LL | enum E { | ^ @@ -97,22 +97,22 @@ LL | if let E::A = e { todo!() }; | ++ +++++++++++ error[E0004]: non-exhaustive patterns: `&&mut &E::B` and `&&mut &E::C` not covered - --> $DIR/non-exhaustive-defined-here.rs:66:11 + --> $DIR/non-exhaustive-defined-here.rs:68:11 | LL | match e { | ^ patterns `&&mut &E::B` and `&&mut &E::C` not covered | note: `E` defined here - --> $DIR/non-exhaustive-defined-here.rs:14:5 + --> $DIR/non-exhaustive-defined-here.rs:8:6 | LL | enum E { - | - + | ^ ... LL | B, - | ^ not covered + | - not covered ... LL | C - | ^ not covered + | - not covered = note: the matched value is of type `&&mut &E` help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern, a match arm with multiple or-patterns as shown, or multiple match arms | @@ -121,7 +121,7 @@ LL + &&mut &E::B | &&mut &E::C => todo!() | error[E0005]: refutable pattern in local binding - --> $DIR/non-exhaustive-defined-here.rs:73:9 + --> $DIR/non-exhaustive-defined-here.rs:75:9 | LL | let E::A = e; | ^^^^ patterns `&&mut &E::B` and `&&mut &E::C` not covered @@ -129,7 +129,7 @@ LL | let E::A = e; = note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant = note: for more information, visit https://doc.rust-lang.org/book/ch18-02-refutability.html note: `E` defined here - --> $DIR/non-exhaustive-defined-here.rs:6:6 + --> $DIR/non-exhaustive-defined-here.rs:8:6 | LL | enum E { | ^ @@ -146,19 +146,19 @@ LL | if let E::A = e { todo!() }; | ++ +++++++++++ error[E0004]: non-exhaustive patterns: `Opt::None` not covered - --> $DIR/non-exhaustive-defined-here.rs:92:11 + --> $DIR/non-exhaustive-defined-here.rs:94:11 | LL | match e { | ^ pattern `Opt::None` not covered | note: `Opt` defined here - --> $DIR/non-exhaustive-defined-here.rs:85:5 + --> $DIR/non-exhaustive-defined-here.rs:83:6 | LL | enum Opt { - | --- + | ^^^ ... LL | None, - | ^^^^ not covered + | ---- not covered = note: the matched value is of type `Opt` help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown | @@ -167,7 +167,7 @@ LL + Opt::None => todo!() | error[E0005]: refutable pattern in local binding - --> $DIR/non-exhaustive-defined-here.rs:99:9 + --> $DIR/non-exhaustive-defined-here.rs:101:9 | LL | let Opt::Some(ref _x) = e; | ^^^^^^^^^^^^^^^^^ pattern `Opt::None` not covered @@ -175,7 +175,7 @@ LL | let Opt::Some(ref _x) = e; = note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant = note: for more information, visit https://doc.rust-lang.org/book/ch18-02-refutability.html note: `Opt` defined here - --> $DIR/non-exhaustive-defined-here.rs:81:6 + --> $DIR/non-exhaustive-defined-here.rs:83:6 | LL | enum Opt { | ^^^ diff --git a/tests/ui/pattern/usefulness/non-exhaustive-match-nested.stderr b/tests/ui/pattern/usefulness/non-exhaustive-match-nested.stderr index 98e417a17f860..310049fe13ec1 100644 --- a/tests/ui/pattern/usefulness/non-exhaustive-match-nested.stderr +++ b/tests/ui/pattern/usefulness/non-exhaustive-match-nested.stderr @@ -18,10 +18,10 @@ LL | match x { | ^ pattern `T::A(U::C)` not covered | note: `T` defined here - --> $DIR/non-exhaustive-match-nested.rs:1:10 + --> $DIR/non-exhaustive-match-nested.rs:1:6 | LL | enum T { A(U), B } - | - ^ not covered + | ^ - not covered = note: the matched value is of type `T` help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown | diff --git a/tests/ui/pattern/usefulness/non-exhaustive-match.stderr b/tests/ui/pattern/usefulness/non-exhaustive-match.stderr index e59e8885e1a48..4bebd3cbbefd0 100644 --- a/tests/ui/pattern/usefulness/non-exhaustive-match.stderr +++ b/tests/ui/pattern/usefulness/non-exhaustive-match.stderr @@ -5,10 +5,10 @@ LL | match x { T::B => { } } | ^ pattern `T::A` not covered | note: `T` defined here - --> $DIR/non-exhaustive-match.rs:3:10 + --> $DIR/non-exhaustive-match.rs:3:6 | LL | enum T { A, B } - | - ^ not covered + | ^ - not covered = note: the matched value is of type `T` help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown | @@ -79,10 +79,10 @@ LL | match T::A { | ^^^^ pattern `T::B` not covered | note: `T` defined here - --> $DIR/non-exhaustive-match.rs:3:13 + --> $DIR/non-exhaustive-match.rs:3:6 | LL | enum T { A, B } - | - ^ not covered + | ^ - not covered = note: the matched value is of type `T` help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown | diff --git a/tests/ui/pattern/usefulness/non-exhaustive-pattern-witness.stderr b/tests/ui/pattern/usefulness/non-exhaustive-pattern-witness.stderr index c75c4ccdc8752..cceb1d8f65ddb 100644 --- a/tests/ui/pattern/usefulness/non-exhaustive-pattern-witness.stderr +++ b/tests/ui/pattern/usefulness/non-exhaustive-pattern-witness.stderr @@ -23,12 +23,12 @@ LL | match Color::Red { | ^^^^^^^^^^ pattern `Color::Red` not covered | note: `Color` defined here - --> $DIR/non-exhaustive-pattern-witness.rs:17:5 + --> $DIR/non-exhaustive-pattern-witness.rs:16:6 | LL | enum Color { - | ----- + | ^^^^^ LL | Red, - | ^^^ not covered + | --- not covered = note: the matched value is of type `Color` help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown | @@ -43,17 +43,17 @@ LL | match Direction::North { | ^^^^^^^^^^^^^^^^ patterns `Direction::East`, `Direction::South` and `Direction::West` not covered | note: `Direction` defined here - --> $DIR/non-exhaustive-pattern-witness.rs:32:5 + --> $DIR/non-exhaustive-pattern-witness.rs:30:6 | LL | enum Direction { - | --------- + | ^^^^^^^^^ LL | North, LL | East, - | ^^^^ not covered + | ---- not covered LL | South, - | ^^^^^ not covered + | ----- not covered LL | West, - | ^^^^ not covered + | ---- not covered = note: the matched value is of type `Direction` help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern, a match arm with multiple or-patterns as shown, or multiple match arms | @@ -68,21 +68,21 @@ LL | match ExcessiveEnum::First { | ^^^^^^^^^^^^^^^^^^^^ patterns `ExcessiveEnum::Second`, `ExcessiveEnum::Third`, `ExcessiveEnum::Fourth` and 8 more not covered | note: `ExcessiveEnum` defined here - --> $DIR/non-exhaustive-pattern-witness.rs:46:5 + --> $DIR/non-exhaustive-pattern-witness.rs:44:6 | LL | enum ExcessiveEnum { - | ------------- + | ^^^^^^^^^^^^^ LL | First, LL | Second, - | ^^^^^^ not covered + | ------ not covered LL | Third, - | ^^^^^ not covered + | ----- not covered LL | Fourth, - | ^^^^^^ not covered + | ------ not covered LL | Fifth, - | ^^^^^ not covered + | ----- not covered LL | Sixth, - | ^^^^^ not covered + | ----- not covered = note: the matched value is of type `ExcessiveEnum` help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern as shown, or multiple match arms | @@ -97,13 +97,13 @@ LL | match Color::Red { | ^^^^^^^^^^ pattern `Color::CustomRGBA { a: true, .. }` not covered | note: `Color` defined here - --> $DIR/non-exhaustive-pattern-witness.rs:19:5 + --> $DIR/non-exhaustive-pattern-witness.rs:16:6 | LL | enum Color { - | ----- + | ^^^^^ ... LL | CustomRGBA { a: bool, r: u8, g: u8, b: u8 }, - | ^^^^^^^^^^ not covered + | ---------- not covered = note: the matched value is of type `Color` help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown | diff --git a/tests/ui/pattern/usefulness/stable-gated-patterns.stderr b/tests/ui/pattern/usefulness/stable-gated-patterns.stderr index f944c25a905c4..f75517fb7910d 100644 --- a/tests/ui/pattern/usefulness/stable-gated-patterns.stderr +++ b/tests/ui/pattern/usefulness/stable-gated-patterns.stderr @@ -5,13 +5,13 @@ LL | match UnstableEnum::Stable { | ^^^^^^^^^^^^^^^^^^^^ patterns `UnstableEnum::Stable2` and `_` not covered | note: `UnstableEnum` defined here - --> $DIR/auxiliary/unstable.rs:9:5 + --> $DIR/auxiliary/unstable.rs:5:1 | LL | pub enum UnstableEnum { - | --------------------- + | ^^^^^^^^^^^^^^^^^^^^^ ... LL | Stable2, - | ^^^^^^^ not covered + | ------- not covered = note: the matched value is of type `UnstableEnum` help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern, a match arm with multiple or-patterns as shown, or multiple match arms | diff --git a/tests/ui/pattern/usefulness/struct-like-enum-nonexhaustive.stderr b/tests/ui/pattern/usefulness/struct-like-enum-nonexhaustive.stderr index 22425aa0dd4dd..3d2b540a9f57a 100644 --- a/tests/ui/pattern/usefulness/struct-like-enum-nonexhaustive.stderr +++ b/tests/ui/pattern/usefulness/struct-like-enum-nonexhaustive.stderr @@ -5,12 +5,12 @@ LL | match x { | ^ pattern `A::B { x: Some(_) }` not covered | note: `A` defined here - --> $DIR/struct-like-enum-nonexhaustive.rs:2:5 + --> $DIR/struct-like-enum-nonexhaustive.rs:1:6 | LL | enum A { - | - + | ^ LL | B { x: Option }, - | ^ not covered + | - not covered = note: the matched value is of type `A` help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown | diff --git a/tests/ui/pattern/usefulness/unstable-gated-patterns.stderr b/tests/ui/pattern/usefulness/unstable-gated-patterns.stderr index d776249b23181..4a4945156f73c 100644 --- a/tests/ui/pattern/usefulness/unstable-gated-patterns.stderr +++ b/tests/ui/pattern/usefulness/unstable-gated-patterns.stderr @@ -5,13 +5,13 @@ LL | match UnstableEnum::Stable { | ^^^^^^^^^^^^^^^^^^^^ pattern `UnstableEnum::Unstable` not covered | note: `UnstableEnum` defined here - --> $DIR/auxiliary/unstable.rs:11:5 + --> $DIR/auxiliary/unstable.rs:5:1 | LL | pub enum UnstableEnum { - | --------------------- + | ^^^^^^^^^^^^^^^^^^^^^ ... LL | Unstable, - | ^^^^^^^^ not covered + | -------- not covered = note: the matched value is of type `UnstableEnum` help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown | diff --git a/tests/ui/rfcs/rfc-2008-non-exhaustive/enum_same_crate_empty_match.stderr b/tests/ui/rfcs/rfc-2008-non-exhaustive/enum_same_crate_empty_match.stderr index de1bf8be8854e..7386f10a6fb2c 100644 --- a/tests/ui/rfcs/rfc-2008-non-exhaustive/enum_same_crate_empty_match.stderr +++ b/tests/ui/rfcs/rfc-2008-non-exhaustive/enum_same_crate_empty_match.stderr @@ -17,18 +17,18 @@ LL | match NonExhaustiveEnum::Unit {} | ^^^^^^^^^^^^^^^^^^^^^^^ patterns `NonExhaustiveEnum::Unit`, `NonExhaustiveEnum::Tuple(_)` and `NonExhaustiveEnum::Struct { .. }` not covered | note: `NonExhaustiveEnum` defined here - --> $DIR/enum_same_crate_empty_match.rs:5:5 + --> $DIR/enum_same_crate_empty_match.rs:4:10 | LL | pub enum NonExhaustiveEnum { - | ----------------- + | ^^^^^^^^^^^^^^^^^ LL | Unit, - | ^^^^ not covered + | ---- not covered LL | LL | Tuple(u32), - | ^^^^^ not covered + | ----- not covered LL | LL | Struct { field: u32 } - | ^^^^^^ not covered + | ------ not covered = note: the matched value is of type `NonExhaustiveEnum` help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern, a match arm with multiple or-patterns as shown, or multiple match arms | @@ -44,18 +44,18 @@ LL | match NormalEnum::Unit {} | ^^^^^^^^^^^^^^^^ patterns `NormalEnum::Unit`, `NormalEnum::Tuple(_)` and `NormalEnum::Struct { .. }` not covered | note: `NormalEnum` defined here - --> $DIR/enum_same_crate_empty_match.rs:14:5 + --> $DIR/enum_same_crate_empty_match.rs:13:10 | LL | pub enum NormalEnum { - | ---------- + | ^^^^^^^^^^ LL | Unit, - | ^^^^ not covered + | ---- not covered LL | LL | Tuple(u32), - | ^^^^^ not covered + | ----- not covered LL | LL | Struct { field: u32 } - | ^^^^^^ not covered + | ------ not covered = note: the matched value is of type `NormalEnum` help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern, a match arm with multiple or-patterns as shown, or multiple match arms | diff --git a/tests/ui/rfcs/rfc-2008-non-exhaustive/uninhabited/match.stderr b/tests/ui/rfcs/rfc-2008-non-exhaustive/uninhabited/match.stderr index a9c54af0418a5..c125756a646d5 100644 --- a/tests/ui/rfcs/rfc-2008-non-exhaustive/uninhabited/match.stderr +++ b/tests/ui/rfcs/rfc-2008-non-exhaustive/uninhabited/match.stderr @@ -62,14 +62,14 @@ LL | match x {} | ^ patterns `UninhabitedVariants::Tuple(_)` and `UninhabitedVariants::Struct { .. }` not covered | note: `UninhabitedVariants` defined here - --> $DIR/auxiliary/uninhabited.rs:17:23 + --> $DIR/auxiliary/uninhabited.rs:16:1 | LL | pub enum UninhabitedVariants { - | ---------------------------- + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ LL | #[non_exhaustive] Tuple(!), - | ^^^^^ not covered + | ----- not covered LL | #[non_exhaustive] Struct { x: ! } - | ^^^^^^ not covered + | ------ not covered = note: the matched value is of type `UninhabitedVariants` help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern, a match arm with multiple or-patterns as shown, or multiple match arms | diff --git a/tests/ui/rfcs/rfc-2008-non-exhaustive/uninhabited/match_same_crate.stderr b/tests/ui/rfcs/rfc-2008-non-exhaustive/uninhabited/match_same_crate.stderr index ec2a2f6f05531..7a12aca8520d3 100644 --- a/tests/ui/rfcs/rfc-2008-non-exhaustive/uninhabited/match_same_crate.stderr +++ b/tests/ui/rfcs/rfc-2008-non-exhaustive/uninhabited/match_same_crate.stderr @@ -43,14 +43,14 @@ LL | match x {} | ^ patterns `UninhabitedVariants::Tuple(_)` and `UninhabitedVariants::Struct { .. }` not covered | note: `UninhabitedVariants` defined here - --> $DIR/match_same_crate.rs:16:23 + --> $DIR/match_same_crate.rs:15:10 | LL | pub enum UninhabitedVariants { - | ------------------- + | ^^^^^^^^^^^^^^^^^^^ LL | #[non_exhaustive] Tuple(!), - | ^^^^^ not covered + | ----- not covered LL | #[non_exhaustive] Struct { x: ! } - | ^^^^^^ not covered + | ------ not covered = note: the matched value is of type `UninhabitedVariants` help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern, a match arm with multiple or-patterns as shown, or multiple match arms | diff --git a/tests/ui/rfcs/rfc-2008-non-exhaustive/uninhabited/match_with_exhaustive_patterns.stderr b/tests/ui/rfcs/rfc-2008-non-exhaustive/uninhabited/match_with_exhaustive_patterns.stderr index b6b777ec56c43..19e2546b0da88 100644 --- a/tests/ui/rfcs/rfc-2008-non-exhaustive/uninhabited/match_with_exhaustive_patterns.stderr +++ b/tests/ui/rfcs/rfc-2008-non-exhaustive/uninhabited/match_with_exhaustive_patterns.stderr @@ -62,14 +62,14 @@ LL | match x {} | ^ patterns `UninhabitedVariants::Tuple(_)` and `UninhabitedVariants::Struct { .. }` not covered | note: `UninhabitedVariants` defined here - --> $DIR/auxiliary/uninhabited.rs:17:23 + --> $DIR/auxiliary/uninhabited.rs:16:1 | LL | pub enum UninhabitedVariants { - | ---------------------------- + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ LL | #[non_exhaustive] Tuple(!), - | ^^^^^ not covered + | ----- not covered LL | #[non_exhaustive] Struct { x: ! } - | ^^^^^^ not covered + | ------ not covered = note: the matched value is of type `UninhabitedVariants` help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern, a match arm with multiple or-patterns as shown, or multiple match arms | From f8daa7d4f6c13ca12f3074ee2ba4f9866a6f8bee Mon Sep 17 00:00:00 2001 From: Augie Fackler Date: Fri, 3 Nov 2023 14:24:23 -0400 Subject: [PATCH 12/12] consts: remove dead code around `i1` constant values `LLVMConstZext` recently got deleted, and it turns out (thanks to @nikic for knowing!) that this is dead code. Tests all pass for me without this logic, and per nikic: > We always generate constants in "relocatable bag of bytes" > representation, so you're never going to get a plain bool. So this should be a safe thing to do. r? @nikic @rustbot label: +llvm-main --- compiler/rustc_codegen_llvm/src/consts.rs | 10 +--------- compiler/rustc_codegen_llvm/src/llvm/ffi.rs | 1 - 2 files changed, 1 insertion(+), 10 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/consts.rs b/compiler/rustc_codegen_llvm/src/consts.rs index 73821b1685df4..307c1264dc1be 100644 --- a/compiler/rustc_codegen_llvm/src/consts.rs +++ b/compiler/rustc_codegen_llvm/src/consts.rs @@ -374,15 +374,7 @@ impl<'ll> StaticMethods for CodegenCx<'ll, '_> { let g = self.get_static(def_id); - // boolean SSA values are i1, but they have to be stored in i8 slots, - // otherwise some LLVM optimization passes don't work as expected - let mut val_llty = self.val_ty(v); - let v = if val_llty == self.type_i1() { - val_llty = self.type_i8(); - llvm::LLVMConstZExt(v, val_llty) - } else { - v - }; + let val_llty = self.val_ty(v); let instance = Instance::mono(self.tcx, def_id); let ty = instance.ty(self.tcx, ty::ParamEnv::reveal_all()); diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index a038b3af03dd6..7fc02a95be0a9 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -969,7 +969,6 @@ extern "C" { ConstantIndices: *const &'a Value, NumIndices: c_uint, ) -> &'a Value; - pub fn LLVMConstZExt<'a>(ConstantVal: &'a Value, ToType: &'a Type) -> &'a Value; pub fn LLVMConstPtrToInt<'a>(ConstantVal: &'a Value, ToType: &'a Type) -> &'a Value; pub fn LLVMConstIntToPtr<'a>(ConstantVal: &'a Value, ToType: &'a Type) -> &'a Value; pub fn LLVMConstBitCast<'a>(ConstantVal: &'a Value, ToType: &'a Type) -> &'a Value;