Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
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
28 changes: 10 additions & 18 deletions library/alloctests/tests/str.rs
Original file line number Diff line number Diff line change
Expand Up @@ -612,14 +612,14 @@ mod slice_index {
data: "abcdef";
good: data[4..4] == "";
bad: data[4..3];
message: "begin > end (4 > 3)";
message: "byte range starts at 4 but ends at 3";
}

in mod rangeinclusive_neg_width {
data: "abcdef";
good: data[4..=3] == "";
bad: data[4..=2];
message: "begin > end (4 > 3)";
message: "byte range starts at 4 but ends at 3";
}
}

Expand Down Expand Up @@ -659,49 +659,49 @@ mod slice_index {
data: super::DATA;
bad: data[super::BAD_START..super::GOOD_END];
message:
"start byte index 4 is not a char boundary; it is inside 'α' (bytes 3..5) of";
"start byte index 4 is not a char boundary; it is inside 'α' (bytes 3..5 of string)";
}

in mod range_2 {
data: super::DATA;
bad: data[super::GOOD_START..super::BAD_END];
message:
"end byte index 6 is not a char boundary; it is inside 'β' (bytes 5..7) of";
"end byte index 6 is not a char boundary; it is inside 'β' (bytes 5..7 of string)";
}

in mod rangefrom {
data: super::DATA;
bad: data[super::BAD_START..];
message:
"start byte index 4 is not a char boundary; it is inside 'α' (bytes 3..5) of";
"start byte index 4 is not a char boundary; it is inside 'α' (bytes 3..5 of string)";
}

in mod rangeto {
data: super::DATA;
bad: data[..super::BAD_END];
message:
"end byte index 6 is not a char boundary; it is inside 'β' (bytes 5..7) of";
"end byte index 6 is not a char boundary; it is inside 'β' (bytes 5..7 of string)";
}

in mod rangeinclusive_1 {
data: super::DATA;
bad: data[super::BAD_START..=super::GOOD_END_INCL];
message:
"start byte index 4 is not a char boundary; it is inside 'α' (bytes 3..5) of";
"start byte index 4 is not a char boundary; it is inside 'α' (bytes 3..5 of string)";
}

in mod rangeinclusive_2 {
data: super::DATA;
bad: data[super::GOOD_START..=super::BAD_END_INCL];
message:
"end byte index 6 is not a char boundary; it is inside 'β' (bytes 5..7) of";
"end byte index 6 is not a char boundary; it is inside 'β' (bytes 5..7 of string)";
}

in mod rangetoinclusive {
data: super::DATA;
bad: data[..=super::BAD_END_INCL];
message:
"end byte index 6 is not a char boundary; it is inside 'β' (bytes 5..7) of";
"end byte index 6 is not a char boundary; it is inside 'β' (bytes 5..7 of string)";
}
}
}
Expand All @@ -716,18 +716,10 @@ mod slice_index {

// check the panic includes the prefix of the sliced string
#[test]
#[should_panic(
expected = "end byte index 1024 is out of bounds of `Lorem ipsum dolor sit amet"
)]
#[should_panic(expected = "end byte index 1024 is out of bounds for string of length 416")]
fn test_slice_fail_truncated_1() {
let _ = &LOREM_PARAGRAPH[..1024];
}
// check the truncation in the panic message
#[test]
#[should_panic(expected = "luctus, im`[...]")]
fn test_slice_fail_truncated_2() {
let _ = &LOREM_PARAGRAPH[..1024];
}
}

#[test]
Expand Down
16 changes: 6 additions & 10 deletions library/core/src/str/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -81,25 +81,21 @@ const fn slice_error_fail_ct(_: &str, _: usize, _: usize) -> ! {

#[track_caller]
fn slice_error_fail_rt(s: &str, begin: usize, end: usize) -> ! {
const MAX_DISPLAY_LENGTH: usize = 256;
let trunc_len = s.floor_char_boundary(MAX_DISPLAY_LENGTH);
let s_trunc = &s[..trunc_len];
let ellipsis = if trunc_len < s.len() { "[...]" } else { "" };
let len = s.len();

// 1. begin is OOB.
if begin > len {
panic!("start byte index {begin} is out of bounds of `{s_trunc}`{ellipsis}");
panic!("start byte index {begin} is out of bounds for string of length {len}");
}

// 2. end is OOB.
if end > len {
panic!("end byte index {end} is out of bounds of `{s_trunc}`{ellipsis}");
panic!("end byte index {end} is out of bounds for string of length {len}");
}

// 3. range is backwards.
if begin > end {
panic!("begin > end ({begin} > {end}) when slicing `{s_trunc}`{ellipsis}")
panic!("byte range starts at {begin} but ends at {end}");
}

// 4. begin is inside a character.
Expand All @@ -109,7 +105,7 @@ fn slice_error_fail_rt(s: &str, begin: usize, end: usize) -> ! {
let range = floor..ceil;
let ch = s[floor..ceil].chars().next().unwrap();
panic!(
"start byte index {begin} is not a char boundary; it is inside {ch:?} (bytes {range:?}) of `{s_trunc}`{ellipsis}"
"start byte index {begin} is not a char boundary; it is inside {ch:?} (bytes {range:?} of string)"
Copy link
Copy Markdown
Member

@RalfJung RalfJung Mar 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
"start byte index {begin} is not a char boundary; it is inside {ch:?} (bytes {range:?} of string)"
"start byte index {begin} is not a char boundary; it is inside {ch:?} (bytes {range:?})"

"of string" sounds odd to me -- is it needed? (Same for similar messages)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like including the word string because the problem is due to mis-use of a str/String API, which may not otherwise be obvious

)
}

Expand All @@ -120,15 +116,15 @@ fn slice_error_fail_rt(s: &str, begin: usize, end: usize) -> ! {
let range = floor..ceil;
let ch = s[floor..ceil].chars().next().unwrap();
panic!(
"end byte index {end} is not a char boundary; it is inside {ch:?} (bytes {range:?}) of `{s_trunc}`{ellipsis}"
"end byte index {end} is not a char boundary; it is inside {ch:?} (bytes {range:?} of string)"
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"of string" sounds odd to me -- is it needed? (Same for similar messages)
@RalfJung

Suggested change
"end byte index {end} is not a char boundary; it is inside {ch:?} (bytes {range:?} of string)"
"end byte index {end} is not a char boundary; it is inside {ch:?} (bytes {range:?})"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1, 2, 6 describe the type so not redundant right?

)
}

// 6. end is OOB and range is inclusive (end == len).
// This test cannot be combined with 2. above because for cases like
// `"abcαβγ"[4..9]` the error is that 4 is inside 'α', not that 9 is OOB.
debug_assert_eq!(end, len);
panic!("end byte index {end} is out of bounds of `{s_trunc}`{ellipsis}");
panic!("end byte index {end} is out of bounds for string of length {len}");
}

impl str {
Expand Down
17 changes: 14 additions & 3 deletions library/core/src/wtf8.rs
Original file line number Diff line number Diff line change
Expand Up @@ -484,11 +484,22 @@ unsafe fn slice_unchecked(s: &Wtf8, begin: usize, end: usize) -> &Wtf8 {
}
}

/// Copied from core::str::raw::slice_error_fail
#[inline(never)]
fn slice_error_fail(s: &Wtf8, begin: usize, end: usize) -> ! {

This comment was marked as resolved.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

warning: unused variable: `s`
     --> library/core/src/wtf8.rs:488:21
      |
  488 | fn slice_error_fail(s: &Wtf8, begin: usize, end: usize) -> ! {
      |                     ^ help: if this is intentional, prefix it with an underscore: `_s`

@ConradIrwin

assert!(begin <= end);
panic!("index {begin} and/or {end} in `{s:?}` do not lie on character boundary");
let len = s.len();
if begin > len {
panic!("start byte index {begin} is out of bounds for string of length {len}");
}
if end > len {
panic!("end byte index {end} is out of bounds for string of length {len}");
}
if begin > end {
panic!("byte range starts at {begin} but ends at {end}");
}
if !s.is_code_point_boundary(begin) {
panic!("byte index {begin} is not a code point boundary");
}
panic!("byte index {end} is not a code point boundary");
Comment on lines -490 to +502
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎉 This looks like a huge improvement to usability.

}
Comment on lines 487 to 503

This comment was marked as resolved.


/// Iterator for the code points of a WTF-8 string.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@

thread 'main' ($TID) panicked at $DIR/const-eval-select-backtrace-std.rs:6:8:
start byte index 1 is out of bounds of ``
start byte index 1 is out of bounds for string of length 0
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
Loading