From 4ff9bedbedb6dedf46e03d7ce2648e24514f9ba8 Mon Sep 17 00:00:00 2001 From: iDawer Date: Mon, 20 Jun 2022 15:48:09 +0500 Subject: [PATCH] Display witnesses of non-exhaustive match Reporting format follows rustc and shows at most three witnesses. --- crates/hir-ty/src/diagnostics/expr.rs | 51 ++++++- crates/hir-ty/src/diagnostics/match_check.rs | 140 +++++++++++++++++- .../match_check/deconstruct_pat.rs | 107 ++++++++++--- crates/hir/src/diagnostics.rs | 1 + crates/hir/src/lib.rs | 3 +- .../src/handlers/missing_match_arms.rs | 98 ++++++------ 6 files changed, 320 insertions(+), 80 deletions(-) diff --git a/crates/hir-ty/src/diagnostics/expr.rs b/crates/hir-ty/src/diagnostics/expr.rs index 335d95c0cf..e9743b4dfe 100644 --- a/crates/hir-ty/src/diagnostics/expr.rs +++ b/crates/hir-ty/src/diagnostics/expr.rs @@ -4,9 +4,10 @@ use std::sync::Arc; -use hir_def::{path::path, resolver::HasResolver, AssocItemId, DefWithBodyId, HasModule}; +use hir_def::{path::path, resolver::HasResolver, AdtId, AssocItemId, DefWithBodyId, HasModule}; use hir_expand::name; use itertools::Either; +use itertools::Itertools; use rustc_hash::FxHashSet; use typed_arena::Arena; @@ -17,7 +18,8 @@ use crate::{ deconstruct_pat::DeconstructedPat, usefulness::{compute_match_usefulness, MatchCheckCtx}, }, - InferenceResult, TyExt, + display::HirDisplay, + InferenceResult, Ty, TyExt, }; pub(crate) use hir_def::{ @@ -37,6 +39,7 @@ pub enum BodyValidationDiagnostic { }, MissingMatchArms { match_expr: ExprId, + uncovered_patterns: String, }, } @@ -211,10 +214,11 @@ impl ExprValidator { // https://github.com/rust-lang/rust/blob/f31622a50/compiler/rustc_mir_build/src/thir/pattern/check_match.rs#L200 let witnesses = report.non_exhaustiveness_witnesses; - // FIXME Report witnesses - // eprintln!("compute_match_usefulness(..) -> {:?}", &witnesses); if !witnesses.is_empty() { - self.diagnostics.push(BodyValidationDiagnostic::MissingMatchArms { match_expr: id }); + self.diagnostics.push(BodyValidationDiagnostic::MissingMatchArms { + match_expr: id, + uncovered_patterns: missing_match_arms(&cx, match_expr_ty, witnesses, arms), + }); } } @@ -367,3 +371,40 @@ fn types_of_subpatterns_do_match(pat: PatId, body: &Body, infer: &InferenceResul walk(pat, body, infer, &mut has_type_mismatches); !has_type_mismatches } + +fn missing_match_arms<'p>( + cx: &MatchCheckCtx<'_, 'p>, + scrut_ty: &Ty, + witnesses: Vec>, + arms: &[MatchArm], +) -> String { + let non_empty_enum = match scrut_ty.as_adt() { + Some((AdtId::EnumId(e), _)) => !cx.db.enum_data(e).variants.is_empty(), + _ => false, + }; + if arms.is_empty() && !non_empty_enum { + format!("type `{}` is non-empty", scrut_ty.display(cx.db)) + } else { + const LIMIT: usize = 3; + match &*witnesses { + [witness] => format!("`{}` not covered", witness.to_pat(&cx).display(cx.db)), + [head @ .., tail] if head.len() < LIMIT => { + let head: Vec<_> = head.iter().map(|w| w.to_pat(cx)).collect(); + format!( + "`{}` and `{}` not covered", + head.iter().map(|p| p.display(cx.db)).join("`, `"), + tail.to_pat(&cx).display(cx.db) + ) + } + _ => { + let (head, tail) = witnesses.split_at(LIMIT); + let head: Vec<_> = head.iter().map(|w| w.to_pat(cx)).collect(); + format!( + "`{}` and {} more not covered", + head.iter().map(|p| p.display(cx.db)).join("`, `"), + tail.len() + ) + } + } + } +} diff --git a/crates/hir-ty/src/diagnostics/match_check.rs b/crates/hir-ty/src/diagnostics/match_check.rs index e00dfdf2fa..276246f266 100644 --- a/crates/hir-ty/src/diagnostics/match_check.rs +++ b/crates/hir-ty/src/diagnostics/match_check.rs @@ -10,11 +10,19 @@ mod pat_util; pub(crate) mod deconstruct_pat; pub(crate) mod usefulness; -use hir_def::{body::Body, expr::PatId, EnumVariantId, LocalFieldId, VariantId}; +use chalk_ir::Mutability; +use hir_def::{ + adt::VariantData, body::Body, expr::PatId, AdtId, EnumVariantId, HasModule, LocalFieldId, + VariantId, +}; +use hir_expand::name::{name, Name}; use stdx::{always, never}; use crate::{ - db::HirDatabase, infer::BindingMode, InferenceResult, Interner, Substitution, Ty, TyKind, + db::HirDatabase, + display::{HirDisplay, HirDisplayError, HirFormatter}, + infer::BindingMode, + InferenceResult, Interner, Substitution, Ty, TyExt, TyKind, }; use self::pat_util::EnumerateAndAdjustIterator; @@ -49,6 +57,7 @@ pub(crate) enum PatKind { /// `x`, `ref x`, `x @ P`, etc. Binding { + name: Name, subpattern: Option, }, @@ -148,7 +157,7 @@ impl<'a> PatCtxt<'a> { } _ => (), } - PatKind::Binding { subpattern: self.lower_opt_pattern(subpat) } + PatKind::Binding { name: name.clone(), subpattern: self.lower_opt_pattern(subpat) } } hir_def::expr::Pat::TupleStruct { ref args, ellipsis, .. } if variant.is_some() => { @@ -282,6 +291,127 @@ impl<'a> PatCtxt<'a> { } } +impl HirDisplay for Pat { + fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError> { + match &*self.kind { + PatKind::Wild => write!(f, "_"), + PatKind::Binding { name, subpattern } => { + write!(f, "{name}")?; + if let Some(subpattern) = subpattern { + write!(f, " @ ")?; + subpattern.hir_fmt(f)?; + } + Ok(()) + } + PatKind::Variant { subpatterns, .. } | PatKind::Leaf { subpatterns } => { + let variant = match *self.kind { + PatKind::Variant { enum_variant, .. } => Some(VariantId::from(enum_variant)), + _ => self.ty.as_adt().and_then(|(adt, _)| match adt { + AdtId::StructId(s) => Some(s.into()), + AdtId::UnionId(u) => Some(u.into()), + AdtId::EnumId(_) => None, + }), + }; + + if let Some(variant) = variant { + match variant { + VariantId::EnumVariantId(v) => { + let data = f.db.enum_data(v.parent); + write!(f, "{}", data.variants[v.local_id].name)?; + } + VariantId::StructId(s) => write!(f, "{}", f.db.struct_data(s).name)?, + VariantId::UnionId(u) => write!(f, "{}", f.db.union_data(u).name)?, + }; + + let variant_data = variant.variant_data(f.db.upcast()); + if let VariantData::Record(rec_fields) = &*variant_data { + write!(f, " {{ ")?; + + let mut printed = 0; + let subpats = subpatterns + .iter() + .filter(|p| !matches!(*p.pattern.kind, PatKind::Wild)) + .map(|p| { + printed += 1; + WriteWith(move |f| { + write!(f, "{}: ", rec_fields[p.field].name)?; + p.pattern.hir_fmt(f) + }) + }); + f.write_joined(subpats, ", ")?; + + if printed < rec_fields.len() { + write!(f, "{}..", if printed > 0 { ", " } else { "" })?; + } + + return write!(f, " }}"); + } + } + + let num_fields = variant + .map_or(subpatterns.len(), |v| v.variant_data(f.db.upcast()).fields().len()); + if num_fields != 0 || variant.is_none() { + write!(f, "(")?; + let subpats = (0..num_fields).map(|i| { + WriteWith(move |f| { + let fid = LocalFieldId::from_raw((i as u32).into()); + if let Some(p) = subpatterns.get(i) { + if p.field == fid { + return p.pattern.hir_fmt(f); + } + } + if let Some(p) = subpatterns.iter().find(|p| p.field == fid) { + p.pattern.hir_fmt(f) + } else { + write!(f, "_") + } + }) + }); + f.write_joined(subpats, ", ")?; + if let (TyKind::Tuple(..), 1) = (self.ty.kind(Interner), num_fields) { + write!(f, ",")?; + } + write!(f, ")")?; + } + + Ok(()) + } + PatKind::Deref { subpattern } => { + match self.ty.kind(Interner) { + TyKind::Adt(adt, _) if is_box(adt.0, f.db) => write!(f, "box ")?, + &TyKind::Ref(mutbl, ..) => { + write!(f, "&{}", if mutbl == Mutability::Mut { "mut " } else { "" })? + } + _ => never!("{:?} is a bad Deref pattern type", self.ty), + } + subpattern.hir_fmt(f) + } + PatKind::LiteralBool { value } => write!(f, "{}", value), + PatKind::Or { pats } => f.write_joined(pats.iter(), " | "), + } + } +} + +struct WriteWith(F) +where + F: Fn(&mut HirFormatter) -> Result<(), HirDisplayError>; + +impl HirDisplay for WriteWith +where + F: Fn(&mut HirFormatter) -> Result<(), HirDisplayError>, +{ + fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError> { + (self.0)(f) + } +} + +fn is_box(adt: AdtId, db: &dyn HirDatabase) -> bool { + let owned_box = name![owned_box].to_smol_str(); + let krate = adt.module(db.upcast()).krate(); + let box_adt = db.lang_item(krate, owned_box).and_then(|it| it.as_struct()).map(AdtId::from); + Some(adt) == box_adt +} + pub(crate) trait PatternFoldable: Sized { fn fold_with(&self, folder: &mut F) -> Self { self.super_fold_with(folder) @@ -357,8 +487,8 @@ impl PatternFoldable for PatKind { fn super_fold_with(&self, folder: &mut F) -> Self { match self { PatKind::Wild => PatKind::Wild, - PatKind::Binding { subpattern } => { - PatKind::Binding { subpattern: subpattern.fold_with(folder) } + PatKind::Binding { name, subpattern } => { + PatKind::Binding { name: name.clone(), subpattern: subpattern.fold_with(folder) } } PatKind::Variant { substs, enum_variant, subpatterns } => PatKind::Variant { substs: substs.fold_with(folder), diff --git a/crates/hir-ty/src/diagnostics/match_check/deconstruct_pat.rs b/crates/hir-ty/src/diagnostics/match_check/deconstruct_pat.rs index 84ded517ba..7011ed9f10 100644 --- a/crates/hir-ty/src/diagnostics/match_check/deconstruct_pat.rs +++ b/crates/hir-ty/src/diagnostics/match_check/deconstruct_pat.rs @@ -51,13 +51,13 @@ use std::{ use hir_def::{EnumVariantId, HasModule, LocalFieldId, VariantId}; use smallvec::{smallvec, SmallVec}; use stdx::never; -use syntax::SmolStr; use crate::{infer::normalize, AdtId, Interner, Scalar, Ty, TyExt, TyKind}; use super::{ + is_box, usefulness::{helper::Captures, MatchCheckCtx, PatCtxt}, - Pat, PatKind, + FieldPat, Pat, PatKind, }; use self::Constructor::*; @@ -144,6 +144,24 @@ impl IntRange { } } + fn to_pat(&self, _cx: &MatchCheckCtx, ty: Ty) -> Pat { + match ty.kind(Interner) { + TyKind::Scalar(Scalar::Bool) => { + let kind = match self.boundaries() { + (0, 0) => PatKind::LiteralBool { value: false }, + (1, 1) => PatKind::LiteralBool { value: true }, + (0, 1) => PatKind::Wild, + (lo, hi) => { + never!("bad range for bool pattern: {}..={}", lo, hi); + PatKind::Wild + } + }; + Pat { ty, kind: kind.into() } + } + _ => unimplemented!(), + } + } + /// See `Constructor::is_covered_by` fn is_covered_by(&self, other: &Self) -> bool { if self.intersection(other).is_some() { @@ -363,7 +381,7 @@ impl Constructor { TyKind::Tuple(arity, ..) => arity, TyKind::Ref(..) => 1, TyKind::Adt(adt, ..) => { - if adt_is_box(adt.0, pcx.cx) { + if is_box(adt.0, pcx.cx.db) { // The only legal patterns of type `Box` (outside `std`) are `_` and box // patterns. If we're here we can assume this is a box pattern. 1 @@ -782,7 +800,7 @@ impl<'p> Fields<'p> { } TyKind::Ref(.., rty) => Fields::wildcards_from_tys(cx, once(rty.clone())), &TyKind::Adt(AdtId(adt), ref substs) => { - if adt_is_box(adt, cx) { + if is_box(adt, cx.db) { // The only legal patterns of type `Box` (outside `std`) are `_` and box // patterns. If we're here we can assume this is a box pattern. let subst_ty = substs.at(Interner, 0).assert_ty_ref(Interner).clone(); @@ -865,8 +883,8 @@ impl<'p> DeconstructedPat<'p> { let ctor; let fields; match pat.kind.as_ref() { - PatKind::Binding { subpattern: Some(subpat) } => return mkpat(subpat), - PatKind::Binding { subpattern: None } | PatKind::Wild => { + PatKind::Binding { subpattern: Some(subpat), .. } => return mkpat(subpat), + PatKind::Binding { subpattern: None, .. } | PatKind::Wild => { ctor = Wildcard; fields = Fields::empty(); } @@ -889,7 +907,7 @@ impl<'p> DeconstructedPat<'p> { } fields = Fields::from_iter(cx, wilds) } - TyKind::Adt(adt, substs) if adt_is_box(adt.0, cx) => { + TyKind::Adt(adt, substs) if is_box(adt.0, cx.db) => { // The only legal patterns of type `Box` (outside `std`) are `_` and box // patterns. If we're here we can assume this is a box pattern. // FIXME(Nadrieril): A `Box` can in theory be matched either with `Box(_, @@ -963,10 +981,67 @@ impl<'p> DeconstructedPat<'p> { DeconstructedPat::new(ctor, fields, pat.ty.clone()) } - // // FIXME(iDawer): implement reporting of noncovered patterns - // pub(crate) fn to_pat(&self, _cx: &MatchCheckCtx<'_, 'p>) -> Pat { - // Pat { ty: self.ty.clone(), kind: PatKind::Wild.into() } - // } + pub(crate) fn to_pat(&self, cx: &MatchCheckCtx<'_, 'p>) -> Pat { + let mut subpatterns = self.iter_fields().map(|p| p.to_pat(cx)); + let pat = match &self.ctor { + Single | Variant(_) => match self.ty.kind(Interner) { + TyKind::Tuple(..) => PatKind::Leaf { + subpatterns: subpatterns + .zip(0u32..) + .map(|(p, i)| FieldPat { + field: LocalFieldId::from_raw(i.into()), + pattern: p, + }) + .collect(), + }, + TyKind::Adt(adt, _) if is_box(adt.0, cx.db) => { + // Without `box_patterns`, the only legal pattern of type `Box` is `_` (outside + // of `std`). So this branch is only reachable when the feature is enabled and + // the pattern is a box pattern. + PatKind::Deref { subpattern: subpatterns.next().unwrap() } + } + TyKind::Adt(adt, substs) => { + let variant = self.ctor.variant_id_for_adt(adt.0); + let subpatterns = Fields::list_variant_nonhidden_fields(cx, self.ty(), variant) + .zip(subpatterns) + .map(|((field, _ty), pattern)| FieldPat { field, pattern }) + .collect(); + + if let VariantId::EnumVariantId(enum_variant) = variant { + PatKind::Variant { substs: substs.clone(), enum_variant, subpatterns } + } else { + PatKind::Leaf { subpatterns } + } + } + // Note: given the expansion of `&str` patterns done in `expand_pattern`, we should + // be careful to reconstruct the correct constant pattern here. However a string + // literal pattern will never be reported as a non-exhaustiveness witness, so we + // ignore this issue. + TyKind::Ref(..) => PatKind::Deref { subpattern: subpatterns.next().unwrap() }, + _ => { + never!("unexpected ctor for type {:?} {:?}", self.ctor, self.ty); + PatKind::Wild + } + }, + &Slice(Slice { _unimplemented: _void }) => unimplemented!(), + &Str(_void) => unimplemented!(), + &FloatRange(_void) => unimplemented!(), + IntRange(range) => return range.to_pat(cx, self.ty.clone()), + Wildcard | NonExhaustive => PatKind::Wild, + Missing { .. } => { + never!( + "trying to convert a `Missing` constructor into a `Pat`; this is probably a bug, + `Missing` should have been processed in `apply_constructors`" + ); + PatKind::Wild + } + Opaque | Or => { + never!("can't convert to pattern: {:?}", self.ctor); + PatKind::Wild + } + }; + Pat { ty: self.ty.clone(), kind: Box::new(pat) } + } pub(super) fn is_or_pat(&self) -> bool { matches!(self.ctor, Or) @@ -980,7 +1055,7 @@ impl<'p> DeconstructedPat<'p> { &self.ty } - pub(super) fn iter_fields<'a>(&'a self) -> impl Iterator> + 'a { + pub(super) fn iter_fields<'a>(&'a self) -> impl Iterator> + 'a { self.fields.iter_patterns() } @@ -1023,11 +1098,3 @@ fn is_field_list_non_exhaustive(variant_id: VariantId, cx: &MatchCheckCtx<'_, '_ }; cx.db.attrs(attr_def_id).by_key("non_exhaustive").exists() } - -fn adt_is_box(adt: hir_def::AdtId, cx: &MatchCheckCtx<'_, '_>) -> bool { - use hir_def::lang_item::LangItemTarget; - match cx.db.lang_item(cx.module.krate(), SmolStr::new_inline("owned_box")) { - Some(LangItemTarget::StructId(box_id)) => adt == box_id.into(), - _ => false, - } -} diff --git a/crates/hir/src/diagnostics.rs b/crates/hir/src/diagnostics.rs index 08e239804c..00a6d4584f 100644 --- a/crates/hir/src/diagnostics.rs +++ b/crates/hir/src/diagnostics.rs @@ -150,6 +150,7 @@ pub struct MismatchedArgCount { pub struct MissingMatchArms { pub file: HirFileId, pub match_expr: AstPtr, + pub uncovered_patterns: String, } #[derive(Debug)] diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs index 3f62a2cd33..1d2912b9dd 100644 --- a/crates/hir/src/lib.rs +++ b/crates/hir/src/lib.rs @@ -1295,7 +1295,7 @@ impl DefWithBody { ); } } - BodyValidationDiagnostic::MissingMatchArms { match_expr } => { + BodyValidationDiagnostic::MissingMatchArms { match_expr, uncovered_patterns } => { match source_map.expr_syntax(match_expr) { Ok(source_ptr) => { let root = source_ptr.file_syntax(db.upcast()); @@ -1307,6 +1307,7 @@ impl DefWithBody { MissingMatchArms { file: source_ptr.file_id, match_expr: AstPtr::new(&match_expr), + uncovered_patterns, } .into(), ); diff --git a/crates/ide-diagnostics/src/handlers/missing_match_arms.rs b/crates/ide-diagnostics/src/handlers/missing_match_arms.rs index 3eb4cf60a9..9e66fbfb75 100644 --- a/crates/ide-diagnostics/src/handlers/missing_match_arms.rs +++ b/crates/ide-diagnostics/src/handlers/missing_match_arms.rs @@ -11,7 +11,7 @@ pub(crate) fn missing_match_arms( ) -> Diagnostic { Diagnostic::new( "missing-match-arm", - "missing match arm", + format!("missing match arm: {}", d.uncovered_patterns), ctx.sema.diagnostics_display_range(InFile::new(d.file, d.match_expr.clone().into())).range, ) } @@ -31,9 +31,9 @@ mod tests { r#" fn main() { match () { } - //^^ error: missing match arm + //^^ error: missing match arm: type `()` is non-empty match (()) { } - //^^^^ error: missing match arm + //^^^^ error: missing match arm: type `()` is non-empty match () { _ => (), } match () { () => (), } @@ -49,7 +49,7 @@ fn main() { r#" fn main() { match ((), ()) { } - //^^^^^^^^ error: missing match arm + //^^^^^^^^ error: missing match arm: type `((), ())` is non-empty match ((), ()) { ((), ()) => (), } } @@ -63,21 +63,21 @@ fn main() { r#" fn test_main() { match false { } - //^^^^^ error: missing match arm + //^^^^^ error: missing match arm: type `bool` is non-empty match false { true => (), } - //^^^^^ error: missing match arm + //^^^^^ error: missing match arm: `false` not covered match (false, true) {} - //^^^^^^^^^^^^^ error: missing match arm + //^^^^^^^^^^^^^ error: missing match arm: type `(bool, bool)` is non-empty match (false, true) { (true, true) => (), } - //^^^^^^^^^^^^^ error: missing match arm + //^^^^^^^^^^^^^ error: missing match arm: `(false, _)` not covered match (false, true) { - //^^^^^^^^^^^^^ error: missing match arm + //^^^^^^^^^^^^^ error: missing match arm: `(true, true)` not covered (false, true) => (), (false, false) => (), (true, false) => (), } match (false, true) { (true, _x) => (), } - //^^^^^^^^^^^^^ error: missing match arm + //^^^^^^^^^^^^^ error: missing match arm: `(false, _)` not covered match false { true => (), false => (), } match (false, true) { @@ -116,11 +116,11 @@ fn test_main() { r#" fn main() { match (false, ((), false)) {} - //^^^^^^^^^^^^^^^^^^^^ error: missing match arm + //^^^^^^^^^^^^^^^^^^^^ error: missing match arm: type `(bool, ((), bool))` is non-empty match (false, ((), false)) { (true, ((), true)) => (), } - //^^^^^^^^^^^^^^^^^^^^ error: missing match arm + //^^^^^^^^^^^^^^^^^^^^ error: missing match arm: `(false, _)` not covered match (false, ((), false)) { (true, _) => (), } - //^^^^^^^^^^^^^^^^^^^^ error: missing match arm + //^^^^^^^^^^^^^^^^^^^^ error: missing match arm: `(false, _)` not covered match (false, ((), false)) { (true, ((), true)) => (), @@ -146,12 +146,12 @@ enum Either { A, B, } fn main() { match Either::A { } - //^^^^^^^^^ error: missing match arm + //^^^^^^^^^ error: missing match arm: `A` and `B` not covered match Either::B { Either::A => (), } - //^^^^^^^^^ error: missing match arm + //^^^^^^^^^ error: missing match arm: `B` not covered match &Either::B { - //^^^^^^^^^^ error: missing match arm + //^^^^^^^^^^ error: missing match arm: `&B` not covered Either::A => (), } @@ -174,9 +174,9 @@ enum Either { A(bool), B } fn main() { match Either::B { } - //^^^^^^^^^ error: missing match arm + //^^^^^^^^^ error: missing match arm: `A(_)` and `B` not covered match Either::B { - //^^^^^^^^^ error: missing match arm + //^^^^^^^^^ error: missing match arm: `A(false)` not covered Either::A(true) => (), Either::B => () } @@ -207,7 +207,7 @@ enum Either { A(bool), B(bool, bool) } fn main() { match Either::A(false) { - //^^^^^^^^^^^^^^^^ error: missing match arm + //^^^^^^^^^^^^^^^^ error: missing match arm: `B(true, _)` not covered Either::A(_) => (), Either::B(false, _) => (), } @@ -353,7 +353,7 @@ fn main() { Either::A => (), } match loop { break Foo::A } { - //^^^^^^^^^^^^^^^^^^^^^ error: missing match arm + //^^^^^^^^^^^^^^^^^^^^^ error: missing match arm: `B` not covered Either::A => (), } match loop { break Foo::A } { @@ -391,9 +391,9 @@ enum Either { A { foo: bool }, B } fn main() { let a = Either::A { foo: true }; match a { } - //^ error: missing match arm + //^ error: missing match arm: `A { .. }` and `B` not covered match a { Either::A { foo: true } => () } - //^ error: missing match arm + //^ error: missing match arm: `B` not covered match a { Either::A { } => (), //^^^^^^^^^ 💡 error: missing structure fields: @@ -401,7 +401,7 @@ fn main() { Either::B => (), } match a { - //^ error: missing match arm + //^ error: missing match arm: `B` not covered Either::A { } => (), } //^^^^^^^^^ 💡 error: missing structure fields: // | - foo @@ -432,7 +432,7 @@ enum Either { fn main() { let a = Either::A { foo: true, bar: () }; match a { - //^ error: missing match arm + //^ error: missing match arm: `B` not covered Either::A { bar: (), foo: false } => (), Either::A { foo: true, bar: () } => (), } @@ -459,12 +459,12 @@ enum Either { fn main() { let a = Either::B; match a { - //^ error: missing match arm + //^ error: missing match arm: `A { foo: false, .. }` not covered Either::A { foo: true, .. } => (), Either::B => (), } match a { - //^ error: missing match arm + //^ error: missing match arm: `B` not covered Either::A { .. } => (), } @@ -494,14 +494,14 @@ enum Either { fn main() { match Either::B { - //^^^^^^^^^ error: missing match arm + //^^^^^^^^^ error: missing match arm: `A(false, _, _, true)` not covered Either::A(true, .., true) => (), Either::A(true, .., false) => (), Either::A(false, .., false) => (), Either::B => (), } match Either::B { - //^^^^^^^^^ error: missing match arm + //^^^^^^^^^ error: missing match arm: `A(false, _, _, false)` not covered Either::A(true, .., true) => (), Either::A(true, .., false) => (), Either::A(.., true) => (), @@ -538,7 +538,7 @@ fn enum_(never: Never) { } fn enum_ref(never: &Never) { match never {} - //^^^^^ error: missing match arm + //^^^^^ error: missing match arm: type `&Never` is non-empty } fn bang(never: !) { match never {} @@ -562,7 +562,7 @@ fn main() { Some(never) => match never {}, } match Option::::None { - //^^^^^^^^^^^^^^^^^^^^^ error: missing match arm + //^^^^^^^^^^^^^^^^^^^^^ error: missing match arm: `None` not covered Option::Some(_never) => {}, } } @@ -576,7 +576,7 @@ fn main() { r#" fn main() { match (false, true, false) { - //^^^^^^^^^^^^^^^^^^^^ error: missing match arm + //^^^^^^^^^^^^^^^^^^^^ error: missing match arm: `(true, _, _)` not covered (false, ..) => (), } }"#, @@ -589,7 +589,7 @@ fn main() { r#" fn main() { match (false, true, false) { - //^^^^^^^^^^^^^^^^^^^^ error: missing match arm + //^^^^^^^^^^^^^^^^^^^^ error: missing match arm: `(_, _, true)` not covered (.., false) => (), } }"#, @@ -602,7 +602,7 @@ fn main() { r#" fn main() { match (false, true, false) { - //^^^^^^^^^^^^^^^^^^^^ error: missing match arm + //^^^^^^^^^^^^^^^^^^^^ error: missing match arm: `(false, _, _)` not covered (true, .., false) => (), } }"#, @@ -615,11 +615,11 @@ fn main() { r#"struct Foo { a: bool } fn main(f: Foo) { match f {} - //^ error: missing match arm + //^ error: missing match arm: type `Foo` is non-empty match f { Foo { a: true } => () } - //^ error: missing match arm + //^ error: missing match arm: `Foo { a: false }` not covered match &f { Foo { a: true } => () } - //^^ error: missing match arm + //^^ error: missing match arm: `&Foo { a: false }` not covered match f { Foo { a: _ } => () } match f { Foo { a: true } => (), @@ -640,9 +640,9 @@ fn main(f: Foo) { r#"struct Foo(bool); fn main(f: Foo) { match f {} - //^ error: missing match arm + //^ error: missing match arm: type `Foo` is non-empty match f { Foo(true) => () } - //^ error: missing match arm + //^ error: missing match arm: `Foo(false)` not covered match f { Foo(true) => (), Foo(false) => (), @@ -658,7 +658,7 @@ fn main(f: Foo) { r#"struct Foo; fn main(f: Foo) { match f {} - //^ error: missing match arm + //^ error: missing match arm: type `Foo` is non-empty match f { Foo => () } } "#, @@ -671,9 +671,9 @@ fn main(f: Foo) { r#"struct Foo { foo: bool, bar: bool } fn main(f: Foo) { match f { Foo { foo: true, .. } => () } - //^ error: missing match arm + //^ error: missing match arm: `Foo { foo: false, .. }` not covered match f { - //^ error: missing match arm + //^ error: missing match arm: `Foo { foo: false, bar: true }` not covered Foo { foo: true, .. } => (), Foo { bar: false, .. } => () } @@ -694,7 +694,7 @@ fn main(f: Foo) { fn main() { enum Either { A(bool), B } match Either::B { - //^^^^^^^^^ error: missing match arm + //^^^^^^^^^ error: missing match arm: `B` not covered Either::A(true | false) => (), } } @@ -716,7 +716,7 @@ fn main(v: S) { match v { S{..} => {} } match v { _ => {} } match v { } - //^ error: missing match arm + //^ error: missing match arm: type `S` is non-empty } "#, ); @@ -732,7 +732,7 @@ fn main() { false => {} } match true { _x @ true => {} } - //^^^^ error: missing match arm + //^^^^ error: missing match arm: `false` not covered } "#, ); @@ -787,12 +787,12 @@ use lib::E; fn main() { match E::A { _ => {} } match E::A { - //^^^^ error: missing match arm + //^^^^ error: missing match arm: `_` not covered E::A => {} E::B => {} } match E::A { - //^^^^ error: missing match arm + //^^^^ error: missing match arm: `_` not covered E::A | E::B => {} } } @@ -811,7 +811,7 @@ fn main() { false => {} } match true { - //^^^^ error: missing match arm + //^^^^ error: missing match arm: `true` not covered true if false => {} false => {} } @@ -876,7 +876,7 @@ struct Next(T::Projection); static __: () = { let n: Next = Next(E::Foo); match n { Next(E::Foo) => {} } - // ^ error: missing match arm + // ^ error: missing match arm: `Next(Bar)` not covered match n { Next(E::Foo | E::Bar) => {} } match n { Next(E::Foo | _ ) => {} } match n { Next(_ | E::Bar) => {} } @@ -919,7 +919,7 @@ enum Enum { fn f(ty: Enum) { match ty { - //^^ error: missing match arm + //^^ error: missing match arm: `Type3` not covered m!() => (), }