8000 check_match: refactor + improve non-exhaustive diagnostics for default binding modes by Centril · Pull Request #64271 · rust-lang/rust · GitHub
[go: up one dir, main page]

Skip to content

check_match: refactor + improve non-exhaustive diagnostics for default binding modes #64271

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Sep 11, 2019
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
check_match: unify check_irrefutable & check_exhaustive more.
  • Loading branch information
Centril committed Sep 9, 2019
commit 7d4665b0973892c76416d9113debb677342f6ff8
4 changes: 2 additions & 2 deletions src/librustc_mir/hair/pattern/_match.rs
Original file line number Diff line number Diff line change
Expand Up @@ -517,9 +517,9 @@ struct PatternContext<'tcx> {
pub struct Witness<'tcx>(Vec<Pattern<'tcx>>);

impl<'tcx> Witness<'tcx> {
pub fn single_pattern(&self) -> &Pattern<'tcx> {
pub fn single_pattern(self) -> Pattern<'tcx> {
assert_eq!(self.0.len(), 1);
&self.0[0]
self.0.into_iter().next().unwrap()
}

fn push_wild_constructor<'a>(
Expand Down
94 changes: 51 additions & 43 deletions src/librustc_mir/hair/pattern/check_match.rs
10000
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use super::_match::{MatchCheckCtxt, Matrix, Witness, expand_pattern, is_useful};
use super::_match::{MatchCheckCtxt, Matrix, expand_pattern, is_useful};
use super::_match::Usefulness::*;
use super::_match::WitnessPreference::*;

Expand Down Expand Up @@ -276,26 +276,26 @@ impl<'tcx> MatchVisitor<'_, 'tcx> {
expand_pattern(cx, pattern)
]].into_iter().collect();

let witness = match check_not_useful(cx, pattern_ty, &pats) {
let witnesses = match check_not_useful(cx, pattern_ty, &pats) {
Ok(_) => return,
Err((witness, _)) => witness,
Err(err) => err,
};

let pattern_string = witness[0].single_pattern().to_string();
let joined_patterns = joined_uncovered_patterns(&witnesses);
let mut err = struct_span_err!(
self.tcx.sess, pat.span, E0005,
"refutable pattern in {}: `{}` not covered",
origin, pattern_string
"refutable pattern in {}: {} not covered",
origin, joined_patterns
);
err.span_label(pat.span, match pat.node {
PatKind::Path(hir::QPath::Resolved(None, ref path))
if path.segments.len() == 1 && path.segments[0].args.is_none() => {
err.span_label(pat.span, match &pat.node {
PatKind::Path(hir::QPath::Resolved(None, path))
if path.segments.len() == 1 && path.segments[0].args.is_none() => {
format!("interpreted as {} {} pattern, not new variable",
path.res.article(), path.res.descr())
}
_ => format!("pattern `{}` not covered", pattern_string),
_ => pattern_not_convered_label(&witnesses, &joined_patterns),
});
adt_defined_here(cx, pattern_ty.peel_refs(), &mut err);
adt_defined_here(cx, &mut err, pattern_ty, &witnesses);
err.emit();
});
}
Expand Down Expand Up @@ -437,11 +437,15 @@ fn check_not_useful(
cx: &mut MatchCheckCtxt<'_, 'tcx>,
ty: Ty<'tcx>,
matrix: &Matrix<'_, 'tcx>,
) -> Result<(), (Vec<Witness<'tcx>>, Pattern<'tcx>)> {
) -> Result<(), Vec<Pattern<'tcx>>> {
let wild_pattern = Pattern { ty, span: DUMMY_SP, kind: box PatternKind::Wild };
match is_useful(cx, matrix, &[&wild_pattern], ConstructWitness) {
NotUseful => Ok(()), // This is good, wildcard pattern isn't reachable.
UsefulWithWitness(pats) => Err((pats, wild_pattern)),
UsefulWithWitness(pats) => Err(if pats.is_empty() {
vec![wild_pattern]
} else {
pats.into_iter().map(|w| w.single_pattern()).collect()
}),
Useful => bug!(),
}
}
Expand All @@ -452,42 +456,26 @@ fn check_exhaustive<'tcx>(
sp: Span,
matrix: &Matrix<'_, 'tcx>,
) {
let (pats, wild_pattern) = match check_not_useful(cx, scrut_ty, matrix) {
let witnesses = match check_not_useful(cx, scrut_ty, matrix) {
Ok(_) => return,
Err(err) => err,
};

let witnesses = if pats.is_empty() {
vec![&wild_pattern]
} else {
pats.iter().map(|w| w.single_pattern()).collect()
};

let joined_patterns = joined_uncovered_patterns(&witnesses);

let mut err = create_e0004(cx.tcx.sess, sp, format!(
"non-exhaustive patterns: {} not covered",
joined_patterns,
));
err.span_label(sp, match witnesses.len() {
1 => format!("pattern {} not covered", joined_patterns),
_ => format!("patterns {} not covered", joined_patterns),
});
// point at the definition of non-covered enum variants
let scrut_ty = scrut_ty.peel_refs();
adt_defined_here(cx, scrut_ty, &mut err);
let patterns = witnesses.iter().map(|p| (**p).clone()).collect::<Vec<Pattern<'_>>>();
if patterns.len() < 4 {
for sp in maybe_point_at_variant(scrut_ty, &patterns) {
err.span_label(sp, "not covered");
}
}
err.help("ensure that all possible cases are being handled, \
possibly by adding wildcards or more match arms");
err.emit();
let mut err = create_e0004(
cx.tcx.sess, sp,
format!("non-exhaustive patterns: {} not covered", joined_patterns),
);
err.span_label(sp, pattern_not_convered_label(&witnesses, &joined_patterns));
adt_defined_here(cx, &mut err, scrut_ty, &witnesses);
err.help(
"ensure that all possible cases are being handled, \
possibly by adding wildcards or more match arms"
)
.emit();
}

fn joined_uncovered_patterns(witnesses: &[&Pattern<'_>]) -> String {
fn joined_uncovered_patterns(witnesses: &[Pattern<'_>]) -> String {
const LIMIT: usize = 3;
match witnesses {
[] => bug!(),
Expand All @@ -504,11 +492,31 @@ fn joined_uncovered_patterns(witnesses: &[&Pattern<'_>]) -> String {
}
}

fn adt_defined_here(cx: &mut MatchCheckCtxt<'_, '_>, ty: Ty<'_>, err: &mut DiagnosticBuilder<'_>) {
fn pattern_not_convered_label(witnesses: &[Pattern<'_>], joined_patterns: &str) -> String {
match witnesses.len() {
1 => format!("pattern {} not covered", joined_patterns),
_ => format!("patterns {} not covered", joined_patterns),
}
}

/// Point at the definition of non-covered `enum` variants.
fn adt_defined_here(
cx: &MatchCheckCtxt<'_, '_>,
err: &mut DiagnosticBuilder<'_>,
ty: Ty<'_>,
witnesses: &[Pattern<'_>],
) {
let ty = ty.peel_refs();
if let ty::Adt(def, _) = ty.sty {
if let Some(sp) = cx.tcx.hir().span_if_local(def.did) {
err.span_label(sp, format!("`{}` defined here", ty));
}

if witnesses.len() < 4 {
for sp in maybe_point_at_variant(ty, &witnesses) {
err.span_label(sp, "not covered");
}
}
}
}

Expand Down
4 changes: 2 additions & 2 deletions src/test/ui/consts/const-match-check.eval1.stderr
2364
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
error[E0005]: refutable pattern in local binding: `std::i32::MIN..=-1i32` not covered
error[E0005]: refutable pattern in local binding: `std::i32::MIN..=-1i32` and `1i32..=std::i32::MAX` not covered
--> $DIR/const-match-check.rs:25:15
|
LL | A = { let 0 = 0; 0 },
| ^ pattern `std::i32::MIN..=-1i32` not covered
| ^ patterns `std::i32::MIN..=-1i32` and `1i32..=std::i32::MAX` not covered

error: aborting due to previous error

Expand Down
4 changes: 2 additions & 2 deletions src/test/ui/consts/const-match-check.eval2.stderr
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
error[E0005]: refutable pattern in local binding: `std::i32::MIN..=-1i32` not covered
error[E0005]: refutable pattern in local binding: `std::i32::MIN..=-1i32` and `1i32..=std::i32::MAX` not covered
--> $DIR/const-match-check.rs:31:24
|
LL | let x: [i32; { let 0 = 0; 0 }] = [];
| ^ pattern `std::i32::MIN..=-1i32` not covered
| ^ patterns `std::i32::MIN..=-1i32` and `1i32..=std::i32::MAX` not covered

error: aborting due to previous error

Expand Down
16 changes: 8 additions & 8 deletions src/test/ui/consts/const-match-check.matchck.stderr
Original file line number Diff line number Diff line change
@@ -1,26 +1,26 @@
error[E0005]: refutable pattern in local binding: `std::i32::MIN..=-1i32` not covered
error[E0005]: refutable pattern in local binding: `std::i32::MIN..=-1i32` and `1i32..=std::i32::MAX` not covered
--> $DIR/const-match-check.rs:4:22
|
LL | const X: i32 = { let 0 = 0; 0 };
| ^ pattern `std::i32::MIN..=-1i32` not covered
| ^ patterns `std::i32::MIN..=-1i32` and `1i32..=std::i32::MAX` not covered

error[E0005]: refutable pattern in local binding: `std::i32::MIN..=-1i32` not covered
error[E0005]: refutable pattern in local binding: `std::i32::MIN..=-1i32` and `1i32..=std::i32::MAX` not covered
--> $DIR/const-match-check.rs:8:23
|
LL | static Y: i32 = { let 0 = 0; 0 };
| ^ pattern `std::i32::MIN..=-1i32` not covered
| ^ patterns `std::i32::MIN..=-1i32` and `1i32..=std::i32::MAX` not covered

error[E0005]: refutable pattern in local binding: `std::i32::MIN..=-1i32` not covered
error[E0005]: refutable pattern in local binding: `std::i32::MIN..=-1i32` and `1i32..=std::i32::MAX` not covered
--> $DIR/const-match-check.rs:13:26
|
LL | const X: i32 = { let 0 = 0; 0 };
| ^ pattern `std::i32::MIN..=-1i32` not covered
| ^ patterns `std::i32::MIN..=-1i32` and `1i32..=std::i32::MAX` not covered

error[E0005]: refutable pattern in local binding: `std::i32::MIN..=-1i32` not covered
error[E0005]: refutable pattern in local binding: `std::i32::MIN..=-1i32` and `1i32..=std::i32::MAX` not covered
--> $DIR/const-match-check.rs:19:26
|
LL | const X: i32 = { let 0 = 0; 0 };
| ^ pattern `std::i32::MIN..=-1i32` not covered
| ^ patterns `std::i32::MIN..=-1i32` and `1i32..=std::i32::MAX` not covered

error: aborting due to 4 previous errors

Expand Down
6 changes: 3 additions & 3 deletions src/test/ui/consts/const-pattern-irrefutable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ use foo::d;
const a: u8 = 2;

fn main() {
let a = 4; //~ ERROR refutable pattern in local binding: `0u8..=1u8` not covered
let c = 4; //~ ERROR refutable pattern in local binding: `0u8..=1u8` not covered
let d = 4; //~ ERROR refutable pattern in local binding: `0u8..=1u8` not covered
let a = 4; //~ ERROR refutable pattern in local binding: `0u8..=1u8` and `3u8..=std::u8::MAX
let c = 4; //~ ERROR refutable pattern in local binding: `0u8..=1u8` and `3u8..=std::u8::MAX
let d = 4; //~ ERROR refutable pattern in local binding: `0u8..=1u8` and `3u8..=std::u8::MAX
fn f() {} // Check that the `NOTE`s still work with an item here (cf. issue #35115).
}
6 changes: 3 additions & 3 deletions src/test/ui/consts/const-pattern-irrefutable.stderr
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
error[E0005]: refutable pattern in local binding: `0u8..=1u8` not covered
error[E0005]: refutable pattern in local binding: `0u8..=1u8` and `3u8..=std::u8::MAX` not covered
--> $DIR/const-pattern-irrefutable.rs:12:9
|
LL | let a = 4;
| ^ interpreted as a constant pattern, not new variable

error[E0005]: refutable pattern in local binding: `0u8..=1u8` not covered
error[E0005]: refutable pattern in local binding: `0u8..=1u8` and `3u8..=std::u8::MAX` not covered
--> $DIR/const-pattern-irrefutable.rs:13:9
|
LL | let c = 4;
| ^ interpreted as a constant pattern, not new variable

error[E0005]: refutable pattern in local binding: `0u8..=1u8` not covered
error[E0005]: refutable pattern in local binding: `0u8..=1u8` and `3u8..=std::u8::MAX` not covered
--> $DIR/const-pattern-irrefutable.rs:14:9
|
LL | let d = 4;
Expand Down
4 changes: 2 additions & 2 deletions src/test/ui/consts/const_let_refutable.stderr
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
error[E0005]: refutable pattern in function argument: `&[]` not covered
error[E0005]: refutable pattern in function argument: `&[]`, `&[_]` and `&[_, _, _]` not covered
--> $DIR/const_let_refutable.rs:3:16
|
LL | const fn slice([a, b]: &[i32]) -> i32 {
| ^^^^^^ pattern `&[]` not covered
| ^^^^^^ patterns `&[]`, `&[_]` and `&[_, _, _]` not covered

error[E0723]: can only call other `const fn` within a `const fn`, but `const <&i32 as std::ops::Add>::add` is not stable as `const fn`
--> $DIR/const_let_refutable.rs:4:5
Expand Down
1 change: 1 addition & 0 deletions src/test/ui/empty/empty-never-array.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ error[E0005]: refutable pattern in local binding: `T(_, _)` not covered
|
LL | / enum Helper<T, U> {
LL | | T(T, [!; 0]),
| | - not covered
LL | | #[allow(dead_code)]
LL | | U(U),
LL | | }
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
error[E0005]: refutable pattern in `for` loop binding: `&std::i32::MIN..=0i32` not covered
error[E0005]: refutable pattern in `for` loop binding: `&std::i32::MIN..=0i32` and `&2i32..=std::i32::MAX` not covered
--> $DIR/for-loop-refutable-pattern-error-message.rs:2:9
|
LL | for &1 in [1].iter() {}
| ^^ pattern `&std::i32::MIN..=0i32` not covered
| ^^ patterns `&std::i32::MIN..=0i32` and `&2i32..=std::i32::MAX` not covered

error: aborting due to previous error

Expand Down
2 changes: 1 addition & 1 deletion src/test/ui/issues/issue-15381.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ fn main() {
let values: Vec<u8> = vec![1,2,3,4,5,6,7,8];

for &[x,y,z] in values.chunks(3).filter(|&xs| xs.len() == 3) {
//~^ ERROR refutable pattern in `for` loop binding: `&[]` not covered
//~^ ERROR refutable pattern in `for` loop binding: `&[]`, `&[_]`, `&[_, _]` and 1 more not
println!("y={}", y);
//~^ ERROR borrow of possibly-uninitialized variable: `y`
}
Expand Down
4 changes: 2 additions & 2 deletions src/test/ui/issues/issue-15381.stderr
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
error[E0005]: refutable pattern in `for` loop binding: `&[]` not covered
error[E0005]: refutable pattern in `for` loop binding: `&[]`, `&[_]`, `&[_, _]` and 1 more not covered
--> $DIR/issue-15381.rs:4:9
|
LL | for &[x,y,z] in values.chunks(3).filter(|&xs| xs.len() == 3) {
| ^^^^^^^^ pattern `&[]` not cove F438 red
| ^^^^^^^^ patterns `&[]`, `&[_]`, `&[_, _]` and 1 more not covered

error[E0381]: borrow of possibly-uninitialized variable: `y`
--> $DIR/issue-15381.rs:6:26
Expand Down
2 changes: 1 addition & 1 deletion src/test/ui/issues/issue-31561.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,5 @@ enum Thing {

fn main() {
let Thing::Foo(y) = Thing::Foo(1);
//~^ ERROR refutable pattern in local binding: `Bar` not covered
//~^ ERROR refutable pattern in local binding: `Bar` and `Baz` not covered
}
6 changes: 4 additions & 2 deletions src/test/ui/issues/issue-31561.stderr
Original file line number Diff line number Diff line change
@@ -1,15 +1,17 @@
error[E0005]: refutable pattern in local binding: `Bar` not covered
error[E0005]: refutable pattern in local binding: `Bar` and `Baz` not covered
--> $DIR/issue-31561.rs:8:9
|
LL | / enum Thing {
LL | | Foo(u8),
LL | | Bar,
| | --- not covered
LL | | Baz
| | --- not covered
LL | | }
| |_- `Thing` defined here
...
LL | let Thing::Foo(y) = Thing::Foo(1);
| ^^^^^^^^^^^^^ pattern `Bar` not covered
| ^^^^^^^^^^^^^ patterns `Bar` and `Baz` not covered

error: aborting due to previous error

Expand Down
13 changes: 10 additions & 3 deletions src/test/ui/match/non-exhaustive-defined-here.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,16 @@ enum E {
//~^ not covered
//~| not covered
//~| not covered
//~| not covered
//~| not covered
//~| not covered
C
//~^ not covered
//~| not covered
//~| not covered
//~| not covered
//~| not covered
//~| not covered
}

fn by_val(e: E) {
Expand All @@ -27,23 +33,24 @@ fn by_val(e: E) {
E::A => {}
}

let E::A = e; //~ ERROR refutable pattern in local binding: `B` not covered
let E::A = e; //~ ERROR refutable pattern in local binding: `B` and `C` not covered
}

fn by_ref_once(e: &E) {
match e { //~ ERROR non-exhaustive patterns: `&B` and `&C` not covered
E::A => {}
}

let E::A = e; //~ ERROR refutable pattern in local binding: `&B` not covered
let E::A = e; //~ ERROR refutable pattern in local binding: `&B` and `&C` not covered
}

fn by_ref_thrice(e: & &mut &E) {
match e { //~ ERROR non-exhaustive patterns: `&&mut &B` and `&&mut &C` not covered
E::A => {}
}

let E::A = e; //~ ERROR refutable pattern in local binding: `&&mut &B` not covered
let E::A = e;
//~^ ERROR refutable pattern in local binding: `&&mut &B` and `&&mut &C` not covered
}

enum Opt {
Expand Down
Loading
0