diff --git a/c2rust-refactor/src/context.rs b/c2rust-refactor/src/context.rs index e7758c6149..f1ae1eb4f2 100644 --- a/c2rust-refactor/src/context.rs +++ b/c2rust-refactor/src/context.rs @@ -944,6 +944,11 @@ impl<'a, 'tcx> RefactorCtxt<'a, 'tcx> { smallvec![Namespace::TypeNS, Namespace::ValueNS] } + // A macro shares no namespace with a type or a value of the same + // name, so it must not be reported as occupying `TypeNS`; doing so + // makes an unrelated type of the same name look like a collision. + ItemKind::MacroDef(..) => smallvec![Namespace::MacroNS], + _ => smallvec![Namespace::TypeNS], } } diff --git a/c2rust-refactor/src/transform/reorganize_definitions.rs b/c2rust-refactor/src/transform/reorganize_definitions.rs index 6de7fde65f..5ee98271ad 100644 --- a/c2rust-refactor/src/transform/reorganize_definitions.rs +++ b/c2rust-refactor/src/transform/reorganize_definitions.rs @@ -519,6 +519,12 @@ impl<'a, 'tcx> Reorganizer<'a, 'tcx> { // Values ItemKind::Static(..) | ItemKind::Const(..) | ItemKind::Fn(..) => Namespace::ValueNS, + // Macros. Kept in sync with `item_namespaces`, which decides + // the namespaces the declarations searched below were filed + // under; classifying a macro here as a type would look for it + // among the type declarations and never find it. + ItemKind::MacroDef(..) => Namespace::MacroNS, + // Types _ => Namespace::TypeNS, }; diff --git a/c2rust-refactor/tests/snapshots.rs b/c2rust-refactor/tests/snapshots.rs index f4a5738beb..0f85c1a24d 100644 --- a/c2rust-refactor/tests/snapshots.rs +++ b/c2rust-refactor/tests/snapshots.rs @@ -535,6 +535,16 @@ fn test_reorganize_forward_decl_with_local_definition() { .test(); } +/// A macro and a struct sharing a name live in different namespaces and do +/// not collide, so the struct must still move into the module holding the +/// macro rather than being banished to a new `thing_h` module. +#[test] +fn test_reorganize_macro_namespace() { + refactor("reorganize_definitions") + .named("reorganize_macro_namespace.rs") + .test(); +} + #[test] fn test_reorganize_multi_namespace() { refactor("reorganize_definitions") diff --git a/c2rust-refactor/tests/snapshots/reorganize_macro_namespace.rs b/c2rust-refactor/tests/snapshots/reorganize_macro_namespace.rs new file mode 100644 index 0000000000..97bcdcc56e --- /dev/null +++ b/c2rust-refactor/tests/snapshots/reorganize_macro_namespace.rs @@ -0,0 +1,40 @@ +#![feature(register_tool)] +#![register_tool(c2rust)] +#![allow(non_camel_case_types)] +#![allow(dead_code)] + +pub mod thing { + #[c2rust::header_src = "/home/user/some/workspace/thing.h:1"] + pub mod thing_h { + #[c2rust::src_loc = "2:0"] + #[derive(Copy, Clone)] + #[repr(C)] + pub struct point { + pub x: i32, + } + } + + // A macro sharing the `point` spelling with the struct in the header + // above. Macros live in the macro namespace, so the two names do not + // collide and the struct still moves into this module. Classifying + // `MacroDef` as a type instead would make `update_module_info_items` + // record `point` as occupying this module's *type* namespace, and + // `find_destination_id` would reject `thing` as a destination. + // + // Note there is deliberately no `use self::thing_h::point;` here: an + // import of the struct would land in `import_targets` and let the + // conflict check see that the two `point`s are the same definition, + // masking the misclassification. + macro_rules! point { + () => { + 0 + }; + } + + pub fn go() -> i32 { + let p = thing_h::point { x: point!() }; + p.x + } +} + +fn main() {} diff --git a/c2rust-refactor/tests/snapshots/snapshots__refactor-reorganize_definitions-reorganize_macro_namespace.rs.snap b/c2rust-refactor/tests/snapshots/snapshots__refactor-reorganize_definitions-reorganize_macro_namespace.rs.snap new file mode 100644 index 0000000000..20b4f5af7c --- /dev/null +++ b/c2rust-refactor/tests/snapshots/snapshots__refactor-reorganize_definitions-reorganize_macro_namespace.rs.snap @@ -0,0 +1,42 @@ +--- +source: c2rust-refactor/tests/snapshots.rs +expression: c2rust-refactor reorganize_definitions --rewrite-mode alongside -- tests/snapshots/reorganize_macro_namespace.rs --edition 2021 +--- +#![feature(register_tool)] +#![register_tool(c2rust)] +#![allow(non_camel_case_types)] +#![allow(dead_code)] + +pub mod thing { + + // =============== BEGIN thing_h ================ + #[derive(Copy, Clone)] + #[repr(C)] + pub struct point { + pub x: i32, + } + + // A macro sharing the `point` spelling with the struct in the header + // above. Macros live in the macro namespace, so the two names do not + // collide and the struct still moves into this module. Classifying + // `MacroDef` as a type instead would make `update_module_info_items` + // record `point` as occupying this module's *type* namespace, and + // `find_destination_id` would reject `thing` as a destination. + // + // Note there is deliberately no `use self::thing_h::point;` here: an + // import of the struct would land in `import_targets` and let the + // conflict check see that the two `point`s are the same definition, + // masking the misclassification. + macro_rules! point { + () => { + 0 + }; + } + + pub fn go() -> i32 { + let p = crate::thing::point { x: point!() }; + p.x + } +} + +fn main() {}