Auto merge of #38066 - bluss:string-slice-error, r=sfackler

Use more specific panic message for &str slicing errors

Separate out of bounds errors from character boundary errors, and print
more details for character boundary errors.

It reports the first error it finds in:

1. begin out of bounds
2. end out of bounds
3. begin <= end violated
3. begin not char boundary
5. end not char boundary.

Example:

    &"abcαβγ"[..4]

    thread 'str::test_slice_fail_boundary_1' panicked at 'byte index 4 is not
    a char boundary; it is inside 'α' (bytes 3..5) of `abcαβγ`'

Fixes #38052
This commit is contained in:
bors 2017-01-03 23:51:42 +00:00
commit 468227129d
3 changed files with 38 additions and 8 deletions

View File

@ -163,8 +163,8 @@ let hachi = &dog[0..2];
with this error:
```text
thread 'main' panicked at 'index 0 and/or 2 in `忠犬ハチ公` do not lie on
character boundary'
thread 'main' panicked at 'byte index 2 is not a char boundary; it is inside '忠'
(bytes 0..3) of `忠犬ハチ公`'
```
## Concatenation

View File

@ -383,17 +383,29 @@ fn test_is_char_boundary() {
// check the panic includes the prefix of the sliced string
#[test]
#[should_panic(expected="Lorem ipsum dolor sit amet")]
#[should_panic(expected="byte index 1024 is out of bounds of `Lorem ipsum dolor sit amet")]
fn test_slice_fail_truncated_1() {
&LOREM_PARAGRAPH[..1024];
}
// check the truncation in the panic message
#[test]
#[should_panic(expected="luctus, im`[...] do not lie on character boundary")]
#[should_panic(expected="luctus, im`[...]")]
fn test_slice_fail_truncated_2() {
&LOREM_PARAGRAPH[..1024];
}
#[test]
#[should_panic(expected="byte index 4 is not a char boundary; it is inside 'α' (bytes 3..5) of")]
fn test_slice_fail_boundary_1() {
&"abcαβγ"[4..];
}
#[test]
#[should_panic(expected="byte index 6 is not a char boundary; it is inside 'β' (bytes 5..7) of")]
fn test_slice_fail_boundary_2() {
&"abcαβγ"[2..6];
}
#[test]
fn test_slice_from() {
assert_eq!(&"abcd"[0..], "abcd");

View File

@ -1746,13 +1746,31 @@ fn truncate_to_char_boundary(s: &str, mut max: usize) -> (bool, &str) {
#[cold]
fn slice_error_fail(s: &str, begin: usize, end: usize) -> ! {
const MAX_DISPLAY_LENGTH: usize = 256;
let (truncated, s) = truncate_to_char_boundary(s, MAX_DISPLAY_LENGTH);
let (truncated, s_trunc) = truncate_to_char_boundary(s, MAX_DISPLAY_LENGTH);
let ellipsis = if truncated { "[...]" } else { "" };
// 1. out of bounds
if begin > s.len() || end > s.len() {
let oob_index = if begin > s.len() { begin } else { end };
panic!("byte index {} is out of bounds of `{}`{}", oob_index, s_trunc, ellipsis);
}
// 2. begin <= end
assert!(begin <= end, "begin <= end ({} <= {}) when slicing `{}`{}",
begin, end, s, ellipsis);
panic!("index {} and/or {} in `{}`{} do not lie on character boundary",
begin, end, s, ellipsis);
begin, end, s_trunc, ellipsis);
// 3. character boundary
let index = if !s.is_char_boundary(begin) { begin } else { end };
// find the character
let mut char_start = index;
while !s.is_char_boundary(char_start) {
char_start -= 1;
}
// `char_start` must be less than len and a char boundary
let ch = s[char_start..].chars().next().unwrap();
let char_range = char_start .. char_start + ch.len_utf8();
panic!("byte index {} is not a char boundary; it is inside {:?} (bytes {:?}) of `{}`{}",
index, ch, char_range, s_trunc, ellipsis);
}
#[stable(feature = "core", since = "1.6.0")]