Improve diagnostic messages for range patterns.
This commit is contained in:
parent
448ce12bc1
commit
25d0ef347a
6 changed files with 143 additions and 67 deletions
|
@ -24,7 +24,6 @@ use std::fmt;
|
|||
use std::fs::{self, File};
|
||||
use std::io::BufReader;
|
||||
use std::io::prelude::*;
|
||||
use std::iter::repeat;
|
||||
use std::net::TcpStream;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::process::{Command, Output, ExitStatus};
|
||||
|
@ -928,12 +927,12 @@ fn check_forbid_output(props: &TestProps,
|
|||
}
|
||||
}
|
||||
|
||||
fn check_expected_errors(expected_errors: Vec<errors::ExpectedError> ,
|
||||
fn check_expected_errors(expected_errors: Vec<errors::ExpectedError>,
|
||||
testfile: &Path,
|
||||
proc_res: &ProcRes) {
|
||||
|
||||
// true if we found the error in question
|
||||
let mut found_flags: Vec<_> = repeat(false).take(expected_errors.len()).collect();
|
||||
let mut found_flags = vec![false; expected_errors.len()];
|
||||
|
||||
if proc_res.status.success() {
|
||||
fatal("process did not return an error status");
|
||||
|
@ -954,14 +953,10 @@ fn check_expected_errors(expected_errors: Vec<errors::ExpectedError> ,
|
|||
}
|
||||
}
|
||||
|
||||
// A multi-line error will have followup lines which will always
|
||||
// start with one of these strings.
|
||||
// A multi-line error will have followup lines which start with a space
|
||||
// or open paren.
|
||||
fn continuation( line: &str) -> bool {
|
||||
line.starts_with(" expected") ||
|
||||
line.starts_with(" found") ||
|
||||
// 1234
|
||||
// Should have 4 spaces: see issue 18946
|
||||
line.starts_with("(")
|
||||
line.starts_with(" ") || line.starts_with("(")
|
||||
}
|
||||
|
||||
// Scan and extract our error/warning messages,
|
||||
|
|
|
@ -83,41 +83,64 @@ pub fn check_pat<'a, 'tcx>(pcx: &pat_ctxt<'a, 'tcx>,
|
|||
demand::suptype(fcx, pat.span, expected, pat_ty);
|
||||
}
|
||||
ast::PatRange(ref begin, ref end) => {
|
||||
check_expr(fcx, &**begin);
|
||||
check_expr(fcx, &**end);
|
||||
check_expr(fcx, begin);
|
||||
check_expr(fcx, end);
|
||||
|
||||
let lhs_ty = fcx.expr_ty(&**begin);
|
||||
let rhs_ty = fcx.expr_ty(&**end);
|
||||
let lhs_ty = fcx.expr_ty(begin);
|
||||
let rhs_ty = fcx.expr_ty(end);
|
||||
|
||||
let lhs_eq_rhs =
|
||||
require_same_types(
|
||||
tcx, Some(fcx.infcx()), false, pat.span, lhs_ty, rhs_ty,
|
||||
|| "mismatched types in range".to_string());
|
||||
// Check that both end-points are of numeric or char type.
|
||||
let numeric_or_char = |t| ty::type_is_numeric(t) || ty::type_is_char(t);
|
||||
let lhs_compat = numeric_or_char(lhs_ty);
|
||||
let rhs_compat = numeric_or_char(rhs_ty);
|
||||
|
||||
let numeric_or_char =
|
||||
lhs_eq_rhs && (ty::type_is_numeric(lhs_ty) || ty::type_is_char(lhs_ty));
|
||||
if !lhs_compat || !rhs_compat {
|
||||
let span = if !lhs_compat && !rhs_compat {
|
||||
pat.span
|
||||
} else if !lhs_compat {
|
||||
begin.span
|
||||
} else {
|
||||
end.span
|
||||
};
|
||||
|
||||
if numeric_or_char {
|
||||
match const_eval::compare_lit_exprs(tcx, &**begin, &**end, Some(lhs_ty),
|
||||
|id| {fcx.item_substs()[&id].substs
|
||||
.clone()}) {
|
||||
Some(Ordering::Less) |
|
||||
Some(Ordering::Equal) => {}
|
||||
Some(Ordering::Greater) => {
|
||||
span_err!(tcx.sess, begin.span, E0030,
|
||||
"lower range bound must be less than upper");
|
||||
}
|
||||
None => {
|
||||
span_err!(tcx.sess, begin.span, E0031,
|
||||
"mismatched types in range");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
span_err!(tcx.sess, begin.span, E0029,
|
||||
"only char and numeric types are allowed in range");
|
||||
// Note: spacing here is intentional, we want a space before "start" and "end".
|
||||
span_err!(tcx.sess, span, E0029,
|
||||
"only char and numeric types are allowed in range patterns\n \
|
||||
start type: {}\n end type: {}",
|
||||
fcx.infcx().ty_to_string(lhs_ty),
|
||||
fcx.infcx().ty_to_string(rhs_ty)
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
fcx.write_ty(pat.id, lhs_ty);
|
||||
// Check that the types of the end-points can be unified.
|
||||
let types_unify = require_same_types(
|
||||
tcx, Some(fcx.infcx()), false, pat.span, rhs_ty, lhs_ty,
|
||||
|| "mismatched types in range".to_string()
|
||||
);
|
||||
|
||||
// It's ok to return without a message as `require_same_types` prints an error.
|
||||
if !types_unify {
|
||||
return;
|
||||
}
|
||||
|
||||
// Now that we know the types can be unified we find the unified type and use
|
||||
// it to type the entire expression.
|
||||
let common_type = fcx.infcx().resolve_type_vars_if_possible(&lhs_ty);
|
||||
|
||||
fcx.write_ty(pat.id, common_type);
|
||||
|
||||
// Finally we evaluate the constants and check that the range is non-empty.
|
||||
let get_substs = |id| fcx.item_substs()[&id].substs.clone();
|
||||
match const_eval::compare_lit_exprs(tcx, begin, end, Some(&common_type), get_substs) {
|
||||
Some(Ordering::Less) |
|
||||
Some(Ordering::Equal) => {}
|
||||
Some(Ordering::Greater) => {
|
||||
span_err!(tcx.sess, begin.span, E0030,
|
||||
"lower range bound must be less than or equal to upper");
|
||||
}
|
||||
None => tcx.sess.span_bug(begin.span, "literals of different types in range pat")
|
||||
}
|
||||
|
||||
// subtyping doesn't matter here, as the value is some kind of scalar
|
||||
demand::eqtype(fcx, pat.span, expected, lhs_ty);
|
||||
|
|
|
@ -146,6 +146,47 @@ match d {
|
|||
```
|
||||
"##,
|
||||
|
||||
E0029: r##"
|
||||
In a match expression, only numbers and characters can be matched against a
|
||||
range. This is because the compiler checks that the range is non-empty at
|
||||
compile-time, and is unable to evaluate arbitrary comparison functions. If you
|
||||
want to capture values of an orderable type between two end-points, you can use
|
||||
a guard.
|
||||
|
||||
```
|
||||
// The ordering relation for strings can't be evaluated at compile time,
|
||||
// so this doesn't work:
|
||||
match string {
|
||||
"hello" ... "world" => ...
|
||||
_ => ...
|
||||
}
|
||||
|
||||
// This is a more general version, using a guard:
|
||||
match string {
|
||||
s if s >= "hello" && s <= "world" => ...
|
||||
_ => ...
|
||||
}
|
||||
```
|
||||
"##,
|
||||
|
||||
E0030: r##"
|
||||
When matching against a range, the compiler verifies that the range is
|
||||
non-empty. Range patterns include both end-points, so this is equivalent to
|
||||
requiring the start of the range to be less than or equal to the end of the
|
||||
range.
|
||||
|
||||
For example:
|
||||
|
||||
```
|
||||
match 5u32 {
|
||||
// This range is ok, albeit pointless.
|
||||
1 ... 1 => ...
|
||||
// This range is empty, and the compiler can tell.
|
||||
1000 ... 5 => ...
|
||||
}
|
||||
```
|
||||
"##,
|
||||
|
||||
E0033: r##"
|
||||
This error indicates that a pointer to a trait type cannot be implicitly
|
||||
dereferenced by a pattern. Every trait defines a type, but because the
|
||||
|
@ -1107,9 +1148,6 @@ For more information see the [opt-in builtin traits RFC](https://github.com/rust
|
|||
}
|
||||
|
||||
register_diagnostics! {
|
||||
E0029,
|
||||
E0030,
|
||||
E0031,
|
||||
E0034, // multiple applicable methods in scope
|
||||
E0035, // does not take type parameters
|
||||
E0036, // incorrect number of type parameters given for this method
|
||||
|
|
|
@ -1,16 +0,0 @@
|
|||
// 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.
|
||||
|
||||
fn main() {
|
||||
match 1 {
|
||||
1...2_usize => 1, //~ ERROR mismatched types in range
|
||||
_ => 2,
|
||||
};
|
||||
}
|
|
@ -8,22 +8,32 @@
|
|||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
//error-pattern: lower range bound
|
||||
//error-pattern: only char and numeric types
|
||||
//error-pattern: mismatched types
|
||||
|
||||
fn main() {
|
||||
match 5 {
|
||||
6 ... 1 => { }
|
||||
_ => { }
|
||||
6 ... 1 => { }
|
||||
_ => { }
|
||||
};
|
||||
//~^^^ ERROR lower range bound must be less than or equal to upper
|
||||
|
||||
match "wow" {
|
||||
"bar" ... "foo" => { }
|
||||
"bar" ... "foo" => { }
|
||||
};
|
||||
//~^^ ERROR only char and numeric types are allowed in range
|
||||
//~| start type: &'static str
|
||||
//~| end type: &'static str
|
||||
|
||||
match "wow" {
|
||||
10 ... "what" => ()
|
||||
};
|
||||
//~^^ ERROR only char and numeric types are allowed in range
|
||||
//~| start type: _
|
||||
//~| end type: &'static str
|
||||
|
||||
match 5 {
|
||||
'c' ... 100 => { }
|
||||
_ => { }
|
||||
'c' ... 100 => { }
|
||||
_ => { }
|
||||
};
|
||||
//~^^^ ERROR mismatched types in range
|
||||
//~| expected char
|
||||
//~| found integral variable
|
||||
}
|
||||
|
|
26
src/test/run-pass/match-range-infer.rs
Normal file
26
src/test/run-pass/match-range-infer.rs
Normal file
|
@ -0,0 +1,26 @@
|
|||
// Copyright 2015 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.
|
||||
|
||||
// Test that type inference for range patterns works correctly (is bi-directional).
|
||||
|
||||
pub fn main() {
|
||||
match 1 {
|
||||
1 ... 3 => {}
|
||||
_ => panic!("should match range")
|
||||
}
|
||||
match 1 {
|
||||
1 ... 3u16 => {}
|
||||
_ => panic!("should match range with inferred start type")
|
||||
}
|
||||
match 1 {
|
||||
1u16 ... 3 => {}
|
||||
_ => panic!("should match range with inferred end type")
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue