diff --git a/c2rust-refactor/src/transform/reorganize_definitions.rs b/c2rust-refactor/src/transform/reorganize_definitions.rs index f652329e7b..d77b30678a 100644 --- a/c2rust-refactor/src/transform/reorganize_definitions.rs +++ b/c2rust-refactor/src/transform/reorganize_definitions.rs @@ -267,13 +267,15 @@ impl<'a, 'tcx> Reorganizer<'a, 'tcx> { return false; } + // The header belongs to this module if it is named after it, + // either exactly or with the usual `_h` suffix. Splitting the + // header name at the module name's length in bytes would panic + // when that offset falls inside a multi-byte character. let header_ident = declaration.parent_header.ident.as_str(); let module_ident = dest_module_info.orig_ident.as_str(); - if header_ident.len() >= module_ident.len() { - let (base, ext) = header_ident.split_at(module_ident.len()); - base == &*module_ident && (ext.is_empty() || ext == "_h") - } else { - false + match header_ident.strip_prefix(&*module_ident) { + Some(ext) => ext.is_empty() || ext == "_h", + None => false, } }); let dest_module = match dest_module { diff --git a/c2rust-refactor/tests/snapshots.rs b/c2rust-refactor/tests/snapshots.rs index c2301d72bc..031bbe56f5 100644 --- a/c2rust-refactor/tests/snapshots.rs +++ b/c2rust-refactor/tests/snapshots.rs @@ -518,6 +518,23 @@ fn test_reorganize_multi_namespace() { .test(); } +/// `find_destination_id` decides whether a header belongs to a candidate +/// destination by comparing their names, which must not assume either is +/// ASCII: slicing the header name at the destination name's length in *bytes* +/// panics when that offset falls inside a multi-byte character. +/// +/// Module `a` is one byte long, so comparing it against `ü_h` used to split +/// the `ü` in half; the names don't match, so `thing` moves to a new module. +/// Module `é` is the matching case, pinning down that non-ASCII names are +/// still compared correctly rather than merely never matching: `é_h` is named +/// after its parent, so `other` moves into it. +#[test] +fn test_reorganize_non_ascii_ident() { + refactor("reorganize_definitions") + .named("reorganize_non_ascii_ident.rs") + .test(); +} + #[test] fn test_reorganize_self_import_destination() { refactor("reorganize_definitions") diff --git a/c2rust-refactor/tests/snapshots/reorganize_non_ascii_ident.rs b/c2rust-refactor/tests/snapshots/reorganize_non_ascii_ident.rs new file mode 100644 index 0000000000..ed436addd1 --- /dev/null +++ b/c2rust-refactor/tests/snapshots/reorganize_non_ascii_ident.rs @@ -0,0 +1,40 @@ +#![feature(register_tool)] +#![register_tool(c2rust)] +#![allow(non_camel_case_types)] +#![allow(dead_code)] + +pub mod a { + #[c2rust::header_src = "/home/user/some/workspace/ü.h:1"] + pub mod ü_h { + #[c2rust::src_loc = "2:0"] + #[derive(Copy, Clone)] + #[repr(C)] + pub struct thing { + pub x: i32, + } + } + + pub fn go() -> i32 { + let t = ü_h::thing { x: 1 }; + t.x + } +} + +pub mod é { + #[c2rust::header_src = "/home/user/some/workspace/é.h:3"] + pub mod é_h { + #[c2rust::src_loc = "4:0"] + #[derive(Copy, Clone)] + #[repr(C)] + pub struct other { + pub y: i32, + } + } + + pub fn go() -> i32 { + let o = é_h::other { y: 2 }; + o.y + } +} + +fn main() {} diff --git a/c2rust-refactor/tests/snapshots/snapshots__refactor-reorganize_definitions-reorganize_non_ascii_ident.rs.snap b/c2rust-refactor/tests/snapshots/snapshots__refactor-reorganize_definitions-reorganize_non_ascii_ident.rs.snap new file mode 100644 index 0000000000..85866c767d --- /dev/null +++ b/c2rust-refactor/tests/snapshots/snapshots__refactor-reorganize_definitions-reorganize_non_ascii_ident.rs.snap @@ -0,0 +1,40 @@ +--- +source: c2rust-refactor/tests/snapshots.rs +expression: c2rust-refactor reorganize_definitions --rewrite-mode alongside -- tests/snapshots/reorganize_non_ascii_ident.rs --edition 2021 +--- +#![feature(register_tool)] +#![register_tool(c2rust)] +#![allow(non_camel_case_types)] +#![allow(dead_code)] + +pub mod ü_h { + #[derive(Copy, Clone)] + #[repr(C)] + pub struct thing { + pub x: i32, + } +} +pub mod a { + + pub fn go() -> i32 { + let t = crate::ü_h::thing { x: 1 }; + t.x + } +} + +pub mod é { + + // =============== BEGIN é_h ================ + #[derive(Copy, Clone)] + #[repr(C)] + pub struct other { + pub y: i32, + } + + pub fn go() -> i32 { + let o = crate::é::other { y: 2 }; + o.y + } +} + +fn main() {}