Make is_useful handle empty types properly

This commit is contained in:
Andrew Cann 2016-11-29 15:10:26 +08:00
parent 9ad20442e8
commit bcdbe942e1
24 changed files with 246 additions and 85 deletions

View File

@ -70,6 +70,12 @@ declare_lint! {
"detects unreachable code paths"
}
declare_lint! {
pub UNREACHABLE_PATTERNS,
Warn,
"detects unreachable patterns"
}
declare_lint! {
pub WARNINGS,
Warn,
@ -239,6 +245,7 @@ impl LintPass for HardwiredLints {
UNUSED_ASSIGNMENTS,
DEAD_CODE,
UNREACHABLE_CODE,
UNREACHABLE_PATTERNS,
WARNINGS,
UNUSED_FEATURES,
STABLE_FEATURES,

View File

@ -17,7 +17,7 @@ use eval::{compare_const_vals};
use rustc_const_math::ConstInt;
use rustc_data_structures::fx::FxHashMap;
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_data_structures::indexed_vec::Idx;
use pattern::{FieldPattern, Pattern, PatternKind};
@ -29,6 +29,7 @@ use rustc::ty::{self, Ty, TyCtxt, TypeFoldable};
use rustc::mir::Field;
use rustc::util::common::ErrorReported;
use syntax::ast::NodeId;
use syntax_pos::{Span, DUMMY_SP};
use arena::TypedArena;
@ -144,6 +145,14 @@ impl<'a, 'tcx> FromIterator<Vec<&'a Pattern<'tcx>>> for Matrix<'a, 'tcx> {
//NOTE: appears to be the only place other then InferCtxt to contain a ParamEnv
pub struct MatchCheckCtxt<'a, 'tcx: 'a> {
pub tcx: TyCtxt<'a, 'tcx, 'tcx>,
/// (roughly) where in the code the match occurs. This is necessary for
/// checking inhabited-ness of types because whether a type is (visibly)
/// inhabited can depend on whether it was defined in the current module or
/// not. eg.
/// struct Foo { _private: ! }
/// can not be seen to be empty outside it's module and should not
/// be matchable with an empty match statement.
pub node: NodeId,
/// A wild pattern with an error type - it exists to avoid having to normalize
/// associated types to get field types.
pub wild_pattern: &'a Pattern<'tcx>,
@ -154,6 +163,7 @@ pub struct MatchCheckCtxt<'a, 'tcx: 'a> {
impl<'a, 'tcx> MatchCheckCtxt<'a, 'tcx> {
pub fn create_and_enter<F, R>(
tcx: TyCtxt<'a, 'tcx, 'tcx>,
node: NodeId,
f: F) -> R
where F: for<'b> FnOnce(MatchCheckCtxt<'b, 'tcx>) -> R
{
@ -167,6 +177,7 @@ impl<'a, 'tcx> MatchCheckCtxt<'a, 'tcx> {
f(MatchCheckCtxt {
tcx: tcx,
node: node,
wild_pattern: &wild_pattern,
pattern_arena: &pattern_arena,
byte_array_map: FxHashMap(),
@ -362,9 +373,9 @@ impl<'tcx> Witness<'tcx> {
/// Therefore, if there is some pattern that is unmatched by `matrix`, it will
/// still be unmatched if the first constructor is replaced by any of the constructors
/// in the return value.
fn missing_constructors(cx: &mut MatchCheckCtxt,
matrix: &Matrix,
pcx: PatternContext) -> Vec<Constructor> {
fn missing_constructors<'a, 'tcx: 'a>(cx: &mut MatchCheckCtxt<'a, 'tcx>,
matrix: &Matrix,
pcx: PatternContext<'tcx>) -> Vec<Constructor> {
let used_constructors: Vec<Constructor> =
matrix.0.iter()
.flat_map(|row| pat_constructors(cx, row[0], pcx).unwrap_or(vec![]))
@ -384,16 +395,46 @@ fn missing_constructors(cx: &mut MatchCheckCtxt,
///
/// but is instead bounded by the maximum fixed length of slice patterns in
/// the column of patterns being analyzed.
fn all_constructors(_cx: &mut MatchCheckCtxt, pcx: PatternContext) -> Vec<Constructor> {
///
/// We make sure to omit constructors that are statically impossible. eg for
/// Option<!> we do not include Some(_) in the returned list of constructors.
fn all_constructors<'a, 'tcx: 'a>(cx: &mut MatchCheckCtxt<'a, 'tcx>,
pcx: PatternContext<'tcx>) -> Vec<Constructor>
{
match pcx.ty.sty {
ty::TyBool =>
[true, false].iter().map(|b| ConstantValue(ConstVal::Bool(*b))).collect(),
ty::TySlice(_) =>
(0..pcx.max_slice_length+1).map(|length| Slice(length)).collect(),
ty::TyArray(_, length) => vec![Slice(length)],
ty::TyAdt(def, _) if def.is_enum() && def.variants.len() > 1 =>
def.variants.iter().map(|v| Variant(v.did)).collect(),
_ => vec![Single]
ty::TySlice(ref sub_ty) => {
if sub_ty.is_uninhabited(Some(cx.node), cx.tcx) {
vec![Slice(0)]
} else {
(0..pcx.max_slice_length+1).map(|length| Slice(length)).collect()
}
}
ty::TyArray(ref sub_ty, length) => {
if length == 0 || !sub_ty.is_uninhabited(Some(cx.node), cx.tcx) {
vec![Slice(length)]
} else {
vec![]
}
}
ty::TyAdt(def, substs) if def.is_enum() && def.variants.len() != 1 => {
def.variants.iter().filter_map(|v| {
let mut visited = FxHashSet::default();
if v.is_uninhabited_recurse(&mut visited, Some(cx.node), cx.tcx, substs, false) {
None
} else {
Some(Variant(v.did))
}
}).collect()
}
_ => {
if pcx.ty.is_uninhabited(Some(cx.node), cx.tcx) {
vec![]
} else {
vec![Single]
}
}
}
}

View File

@ -25,6 +25,7 @@ use rustc::middle::mem_categorization::{cmt};
use rustc::session::Session;
use rustc::traits::Reveal;
use rustc::ty::{self, TyCtxt};
use rustc::lint;
use rustc_errors::DiagnosticBuilder;
use rustc::hir::def::*;
@ -150,7 +151,7 @@ impl<'a, 'tcx> MatchVisitor<'a, 'tcx> {
}
}
MatchCheckCtxt::create_and_enter(self.tcx, |ref mut cx| {
MatchCheckCtxt::create_and_enter(self.tcx, scrut.id, |ref mut cx| {
let mut have_errors = false;
let inlined_arms : Vec<(Vec<_>, _)> = arms.iter().map(|arm| (
@ -210,7 +211,7 @@ impl<'a, 'tcx> MatchVisitor<'a, 'tcx> {
"local binding"
};
MatchCheckCtxt::create_and_enter(self.tcx, |ref mut cx| {
MatchCheckCtxt::create_and_enter(self.tcx, pat.id, |ref mut cx| {
let mut patcx = PatternContext::new(self.tcx);
let pats : Matrix = vec![vec![
expand_pattern(cx, patcx.lower_pattern(pat))
@ -324,14 +325,19 @@ fn check_arms<'a, 'tcx>(cx: &mut MatchCheckCtxt<'a, 'tcx>,
},
hir::MatchSource::Normal => {
let mut err = struct_span_err!(cx.tcx.sess, pat.span, E0001,
"unreachable pattern");
err.span_label(pat.span, &"this is an unreachable pattern");
// if we had a catchall pattern, hint at that
// if we had a catchall pattern, raise an error.
// Otherwise an unreachable pattern raises a warning.
if let Some(catchall) = catchall {
let mut err = struct_span_err!(cx.tcx.sess, pat.span, E0001,
"unreachable pattern");
err.span_label(pat.span, &"this is an unreachable pattern");
err.span_note(catchall, "this pattern matches any value");
err.emit();
} else {
cx.tcx.sess.add_lint(lint::builtin::UNREACHABLE_PATTERNS,
hir_pat.id, pat.span,
String::from("unreachable pattern"));
}
err.emit();
},
hir::MatchSource::TryDesugar => {

View File

@ -28,7 +28,7 @@ For example, the following `match` block has too many arms:
```compile_fail,E0001
match Some(0) {
Some(bar) => {/* ... */}
None => {/* ... */}
x => {/* ... */} // This handles the `None` case
_ => {/* ... */} // All possible cases have already been handled
}
```

View File

@ -165,6 +165,7 @@ pub fn register_builtins(store: &mut lint::LintStore, sess: Option<&Session>) {
DEAD_CODE,
UNUSED_MUT,
UNREACHABLE_CODE,
UNREACHABLE_PATTERNS,
UNUSED_MUST_USE,
UNUSED_UNSAFE,
PATH_STATEMENTS,

View File

@ -399,7 +399,13 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
self.check_pat(&p, discrim_ty);
all_pats_diverge &= self.diverges.get();
}
all_pats_diverge
// As discussed with @eddyb, this is for disabling unreachable_code
// warnings on patterns (they're now subsumed by unreachable_patterns
// warnings).
match all_pats_diverge {
Diverges::Maybe => Diverges::Maybe,
Diverges::Always | Diverges::WarnedAlways => Diverges::WarnedAlways,
}
}).collect();
// Now typecheck the blocks.

View File

@ -8,11 +8,13 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#![deny(unreachable_patterns)]
fn main() {
let foo = Some(1);
match foo {
Some(bar) => {/* ... */}
Some(_) => {/* ... */}
None => {/* ... */}
_ => {/* ... */} //~ ERROR E0001
_ => {/* ... */} //~ ERROR unreachable pattern
}
}

View File

@ -20,7 +20,7 @@ fn tail(source_list: &IntList) -> IntList {
match source_list {
&IntList::Cons(val, box ref next_list) => tail(next_list),
&IntList::Cons(val, box Nil) => IntList::Cons(val, box Nil),
//~^ ERROR unreachable pattern
//~^ ERROR cannot move out of borrowed content
//~^^ WARN pattern binding `Nil` is named the same as one of the variants of the type `IntList`
_ => panic!()
}

View File

@ -9,6 +9,8 @@
// except according to those terms.
#![feature(slice_patterns)]
#![allow(unused_variables)]
#![deny(unreachable_patterns)]
fn main() {
let sl = vec![1,2,3];

View File

@ -8,6 +8,9 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#![allow(overflowing_literals)]
#![deny(unreachable_patterns)]
fn test(val: u8) {
match val {
256 => print!("0b1110\n"),

View File

@ -0,0 +1,26 @@
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#![deny(unreachable_patterns)]
fn main() {
match "world" {
"hello" => {}
_ => {},
}
match "world" {
ref _x if false => {}
"hello" => {}
"hello" => {} //~ ERROR unreachable pattern
_ => {},
}
}

View File

@ -16,6 +16,5 @@ fn main() {
match "world" { //~ ERROR non-exhaustive patterns: `&_`
ref _x if false => {}
"hello" => {}
"hello" => {} //~ ERROR unreachable pattern
}
}

View File

@ -8,6 +8,8 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#![deny(unreachable_patterns)]
enum Enum {
Var1,
Var2,
@ -41,13 +43,4 @@ fn main() {
//~^ ERROR unreachable pattern
//~^^ NOTE this is an unreachable pattern
};
// `_` need not emit a note, it is pretty obvious already.
let t = (Var1, Var1);
match t {
(Var1, b) => (),
_ => (),
anything => ()
//~^ ERROR unreachable pattern
//~^^ NOTE this is an unreachable pattern
};
}

View File

@ -40,6 +40,5 @@ fn main() {
box NodeKind::Element(ed) => match ed.kind { //~ ERROR non-exhaustive patterns
box ElementKind::HTMLImageElement(ref d) if d.image.is_some() => { true }
},
_ => panic!("WAT") //~ ERROR unreachable pattern
};
}

View File

@ -0,0 +1,71 @@
// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use self::Direction::{North, East, South, West};
struct NewBool(bool);
enum Direction {
North,
East,
South,
West
}
const TRUE_TRUE: (bool, bool) = (true, true);
fn nonexhaustive_1() {
match (true, false) {
//~^ ERROR non-exhaustive patterns: `(true, false)` not covered
TRUE_TRUE => (),
(false, false) => (),
(false, true) => ()
}
}
const NONE: Option<Direction> = None;
const EAST: Direction = East;
fn nonexhaustive_2() {
match Some(Some(North)) {
//~^ ERROR non-exhaustive patterns: `Some(Some(West))` not covered
Some(NONE) => (),
Some(Some(North)) => (),
Some(Some(EAST)) => (),
Some(Some(South)) => (),
None => ()
}
}
const NEW_FALSE: NewBool = NewBool(false);
struct Foo {
bar: Option<Direction>,
baz: NewBool
}
const STATIC_FOO: Foo = Foo { bar: None, baz: NEW_FALSE };
fn nonexhaustive_3() {
match (Foo { bar: Some(North), baz: NewBool(true) }) {
//~^ ERROR non-exhaustive patterns: `Foo { bar: Some(North), baz: NewBool(true) }`
Foo { bar: None, baz: NewBool(true) } => (),
Foo { bar: _, baz: NEW_FALSE } => (),
Foo { bar: Some(West), baz: NewBool(true) } => (),
Foo { bar: Some(South), .. } => (),
Foo { bar: Some(EAST), .. } => ()
}
}
fn main() {
nonexhaustive_1();
nonexhaustive_2();
nonexhaustive_3();
}

View File

@ -7,10 +7,16 @@
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#![allow(dead_code)]
#![deny(unreachable_patterns)]
use self::Direction::{North, East, South, West};
#[derive(PartialEq, Eq)]
struct NewBool(bool);
#[derive(PartialEq, Eq)]
enum Direction {
North,
East,
@ -20,15 +26,6 @@ enum Direction {
const TRUE_TRUE: (bool, bool) = (true, true);
fn nonexhaustive_1() {
match (true, false) {
//~^ ERROR non-exhaustive patterns: `(true, false)` not covered
TRUE_TRUE => (),
(false, false) => (),
(false, true) => ()
}
}
fn unreachable_1() {
match (true, false) {
TRUE_TRUE => (),
@ -43,17 +40,6 @@ fn unreachable_1() {
const NONE: Option<Direction> = None;
const EAST: Direction = East;
fn nonexhaustive_2() {
match Some(Some(North)) {
//~^ ERROR non-exhaustive patterns: `Some(Some(West))` not covered
Some(NONE) => (),
Some(Some(North)) => (),
Some(Some(EAST)) => (),
Some(Some(South)) => (),
None => ()
}
}
fn unreachable_2() {
match Some(Some(North)) {
Some(NONE) => (),
@ -73,19 +59,6 @@ struct Foo {
baz: NewBool
}
const STATIC_FOO: Foo = Foo { bar: None, baz: NEW_FALSE };
fn nonexhaustive_3() {
match (Foo { bar: Some(North), baz: NewBool(true) }) {
//~^ ERROR non-exhaustive patterns: `Foo { bar: Some(North), baz: NewBool(true) }`
Foo { bar: None, baz: NewBool(true) } => (),
Foo { bar: _, baz: NEW_FALSE } => (),
Foo { bar: Some(West), baz: NewBool(true) } => (),
Foo { bar: Some(South), .. } => (),
Foo { bar: Some(EAST), .. } => ()
}
}
fn unreachable_3() {
match (Foo { bar: Some(EAST), baz: NewBool(true) }) {
Foo { bar: None, baz: NewBool(true) } => (),
@ -100,9 +73,6 @@ fn unreachable_3() {
}
fn main() {
nonexhaustive_1();
nonexhaustive_2();
nonexhaustive_3();
unreachable_1();
unreachable_2();
unreachable_3();

View File

@ -0,0 +1,26 @@
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#![feature(advanced_slice_patterns, slice_patterns)]
fn main() {
let buf = &[0, 1, 2, 3];
match buf { //~ ERROR non-exhaustive
b"AAAA" => {}
}
let buf: &[u8] = buf;
match buf { //~ ERROR non-exhaustive
b"AAAA" => {}
}
}

View File

@ -9,6 +9,7 @@
// except according to those terms.
#![feature(advanced_slice_patterns, slice_patterns)]
#![deny(unreachable_patterns)]
fn main() {
let buf = &[0, 1, 2, 3];
@ -37,10 +38,6 @@ fn main() {
_ => {}
}
match buf { //~ ERROR non-exhaustive
b"AAAA" => {}
}
let buf: &[u8] = buf;
match buf {
@ -66,8 +63,4 @@ fn main() {
b"AAAA" => {}, //~ ERROR unreachable pattern
_ => {}
}
match buf { //~ ERROR non-exhaustive
b"AAAA" => {}
}
}

View File

@ -14,6 +14,8 @@
//error-pattern: unreachable
//error-pattern: unreachable
#![deny(unreachable_patterns)]
fn main() {
match 5 {
1 ... 10 => { }

View File

@ -9,6 +9,7 @@
// except according to those terms.
#![feature(slice_patterns)]
#![deny(unreachable_patterns)]
// The arity of `ref x` is always 1. If the pattern is compared to some non-structural type whose
// arity is always 0, an ICE occurs.
@ -19,7 +20,7 @@ fn main() {
let homura = [1, 2, 3];
match homura {
[1, ref madoka, 3] => (),
[1, ref _madoka, 3] => (),
[1, 2, 3] => (), //~ ERROR unreachable pattern
[_, _, _] => (),
}

View File

@ -9,6 +9,7 @@
// except according to those terms.
#![feature(slice_patterns)]
#![deny(unreachable_patterns)]
fn a() {
let v = [1, 2, 3];

View File

@ -9,13 +9,14 @@
// except according to those terms.
#![feature(slice_patterns)]
#![deny(unreachable_patterns)]
fn main() {
let x: Vec<(isize, isize)> = Vec::new();
let x: &[(isize, isize)] = &x;
match *x {
[a, (2, 3), _] => (),
[(1, 2), (2, 3), b] => (), //~ ERROR unreachable pattern
[_, (2, 3), _] => (),
[(1, 2), (2, 3), _] => (), //~ ERROR unreachable pattern
_ => ()
}
@ -24,7 +25,7 @@ fn main() {
"baz".to_string()];
let x: &[String] = &x;
match *x {
[a, _, _, ..] => { println!("{}", a); }
[ref a, _, _, ..] => { println!("{}", a); }
[_, _, _, _, _] => { } //~ ERROR unreachable pattern
_ => { }
}

View File

@ -8,6 +8,8 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#![deny(unreachable_patterns)]
struct Foo {
x: isize,
y: isize,
@ -16,7 +18,7 @@ struct Foo {
pub fn main() {
let a = Foo { x: 1, y: 2 };
match a {
Foo { x: x, y: y } => (),
Foo { x: _x, y: _y } => (),
Foo { .. } => () //~ ERROR unreachable pattern
}

View File

@ -12,7 +12,16 @@
#![feature(box_patterns)]
#![feature(box_syntax)]
#![allow(dead_code)]
#![deny(unreachable_patterns)]
enum foo { a(Box<foo>, isize), b(usize), }
enum Foo { A(Box<Foo>, isize), B(usize), }
fn main() {
match Foo::B(1) {
Foo::B(_) | Foo::A(box _, 1) => { }
Foo::A(_, 1) => { }
_ => { }
}
}
fn main() { match foo::b(1) { foo::b(_) | foo::a(box _, 1) => { } foo::a(_, 1) => { } } }