Skip to content

Commit 6eb81a1

Browse files
committed
Auto merge of #32064 - bluss:str-slice-panic, r=alexcrichton
Fix panic on string slicing error to truncate the string The string may be arbitrarily long, but we want to limit the panic message to a reasonable length. Truncate the string if it is too long (simply to char boundary). Also add details to the start <= end message. I think it's ok to flesh out the code here, since it's in a cold function. Fixes #32063
2 parents 998a672 + 4594f0f commit 6eb81a1

File tree

2 files changed

+41
-3
lines changed

2 files changed

+41
-3
lines changed

src/libcollectionstest/str.rs

+20
Original file line numberDiff line numberDiff line change
@@ -346,6 +346,26 @@ fn test_slice_fail() {
346346
&"中华Việt Nam"[0..2];
347347
}
348348

349+
const LOREM_PARAGRAPH: &'static str = "\
350+
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse quis lorem sit amet dolor \
351+
ultricies condimentum. Praesent iaculis purus elit, ac malesuada quam malesuada in. Duis sed orci \
352+
eros. Suspendisse sit amet magna mollis, mollis nunc luctus, imperdiet mi. Integer fringilla non \
353+
sem ut lacinia. Fusce varius tortor a risus porttitor hendrerit. Morbi mauris dui, ultricies nec \
354+
tempus vel, gravida nec quam.";
355+
356+
// check the panic includes the prefix of the sliced string
357+
#[test]
358+
#[should_panic(expected="Lorem ipsum dolor sit amet")]
359+
fn test_slice_fail_truncated_1() {
360+
&LOREM_PARAGRAPH[..1024];
361+
}
362+
// check the truncation in the panic message
363+
#[test]
364+
#[should_panic(expected="luctus, im`[...] do not lie on character boundary")]
365+
fn test_slice_fail_truncated_2() {
366+
&LOREM_PARAGRAPH[..1024];
367+
}
368+
349369
#[test]
350370
fn test_slice_from() {
351371
assert_eq!(&"abcd"[0..], "abcd");

src/libcore/str/mod.rs

+21-3
Original file line numberDiff line numberDiff line change
@@ -1645,12 +1645,30 @@ pub trait StrExt {
16451645
fn parse<T: FromStr>(&self) -> Result<T, T::Err>;
16461646
}
16471647

1648+
// truncate `&str` to length at most equal to `max`
1649+
// return `true` if it were truncated, and the new str.
1650+
fn truncate_to_char_boundary(s: &str, mut max: usize) -> (bool, &str) {
1651+
if max >= s.len() {
1652+
(false, s)
1653+
} else {
1654+
while !s.is_char_boundary(max) {
1655+
max -= 1;
1656+
}
1657+
(true, &s[..max])
1658+
}
1659+
}
1660+
16481661
#[inline(never)]
16491662
#[cold]
16501663
fn slice_error_fail(s: &str, begin: usize, end: usize) -> ! {
1651-
assert!(begin <= end);
1652-
panic!("index {} and/or {} in `{}` do not lie on character boundary",
1653-
begin, end, s);
1664+
const MAX_DISPLAY_LENGTH: usize = 256;
1665+
let (truncated, s) = truncate_to_char_boundary(s, MAX_DISPLAY_LENGTH);
1666+
let ellipsis = if truncated { "[...]" } else { "" };
1667+
1668+
assert!(begin <= end, "begin <= end ({} <= {}) when slicing `{}`{}",
1669+
begin, end, s, ellipsis);
1670+
panic!("index {} and/or {} in `{}`{} do not lie on character boundary",
1671+
begin, end, s, ellipsis);
16541672
}
16551673

16561674
#[stable(feature = "core", since = "1.6.0")]

0 commit comments

Comments
 (0)