Skip to content

Commit 48dc66f

Browse files
committed
fix(clap_lex): Deprecate unsound OsStrExt::split_at
1 parent 56dc953 commit 48dc66f

File tree

2 files changed

+20
-5
lines changed

2 files changed

+20
-5
lines changed

clap_lex/src/ext.rs

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,7 @@ pub trait OsStrExt: private::Sealed {
193193
/// assert_eq!("Per", first);
194194
/// assert_eq!(" Martin-Löf", last);
195195
/// ```
196+
#[deprecated(since = "4.1.0", note = "This is not sound for all `index`")]
196197
fn split_at(&self, index: usize) -> (&OsStr, &OsStr);
197198
/// Splits the string on the first occurrence of the specified delimiter and
198199
/// returns prefix before delimiter and suffix after delimiter.
@@ -251,7 +252,7 @@ impl OsStrExt for OsStr {
251252
}
252253

253254
fn split_at(&self, index: usize) -> (&OsStr, &OsStr) {
254-
// BUG: This is unsafe
255+
// BUG: This is unsafe and has been deprecated
255256
unsafe {
256257
let bytes = to_bytes(self);
257258
let (first, second) = bytes.split_at(index);
@@ -294,7 +295,7 @@ unsafe fn to_bytes(s: &OsStr) -> &[u8] {
294295
//
295296
// There is a proposal to support this natively (https://github.com/rust-lang/rust/pull/95290)
296297
// but its in limbo
297-
unsafe { std::mem::transmute(s) }
298+
std::mem::transmute(s)
298299
}
299300

300301
/// Restore raw bytes as `OsStr`
@@ -313,7 +314,7 @@ unsafe fn to_os_str(s: &[u8]) -> &OsStr {
313314
//
314315
// There is a proposal to support this natively (https://github.com/rust-lang/rust/pull/95290)
315316
// but its in limbo
316-
unsafe { std::mem::transmute(s) }
317+
std::mem::transmute(s)
317318
}
318319

319320
pub struct Split<'s, 'n> {
@@ -341,3 +342,14 @@ impl<'s, 'n> Iterator for Split<'s, 'n> {
341342
}
342343
}
343344
}
345+
346+
/// Split an `OsStr`
347+
///
348+
/// # Safety
349+
///
350+
/// `index` must be at a valid UTF-8 boundary
351+
pub(crate) unsafe fn split_at(os: &OsStr, index: usize) -> (&OsStr, &OsStr) {
352+
let bytes = to_bytes(os);
353+
let (first, second) = bytes.split_at(index);
354+
(to_os_str(first), to_os_str(second))
355+
}

clap_lex/src/lib.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -433,7 +433,9 @@ impl<'s> ShortFlags<'s> {
433433
if let Some((index, _)) = self.utf8_prefix.next() {
434434
self.utf8_prefix = "".char_indices();
435435
self.invalid_suffix = None;
436-
return Some(self.inner.split_at(index).1);
436+
// SAFETY: `char_indices` ensures `index` is at a valid UTF-8 boundary
437+
let remainder = unsafe { ext::split_at(self.inner, index).1 };
438+
return Some(remainder);
437439
}
438440

439441
if let Some(suffix) = self.invalid_suffix {
@@ -457,7 +459,8 @@ fn split_nonutf8_once(b: &OsStr) -> (&str, Option<&OsStr>) {
457459
match b.try_str() {
458460
Ok(s) => (s, None),
459461
Err(err) => {
460-
let (valid, after_valid) = b.split_at(err.valid_up_to());
462+
// SAFETY: `char_indices` ensures `index` is at a valid UTF-8 boundary
463+
let (valid, after_valid) = unsafe { ext::split_at(b, err.valid_up_to()) };
461464
let valid = valid.try_str().unwrap();
462465
(valid, Some(after_valid))
463466
}

0 commit comments

Comments
 (0)