Implement inline namespaces.
Items in an inline namespace are exported both in the inline namespace's `mod`, and the parent `mod`, via `pub use inline_ns::*;`.
PiperOrigin-RevId: 473316917
diff --git a/rs_bindings_from_cc/importers/namespace.cc b/rs_bindings_from_cc/importers/namespace.cc
index 0fcd9d2..4f934a7 100644
--- a/rs_bindings_from_cc/importers/namespace.cc
+++ b/rs_bindings_from_cc/importers/namespace.cc
@@ -29,7 +29,7 @@
.owning_target = ictx_.GetOwningTarget(namespace_decl),
.child_item_ids = std::move(item_ids),
.enclosing_namespace_id = GetEnclosingNamespaceId(namespace_decl),
- };
+ .is_inline = namespace_decl->isInline()};
}
} // namespace crubit
diff --git a/rs_bindings_from_cc/ir.cc b/rs_bindings_from_cc/ir.cc
index 9e4b77c..7869e42 100644
--- a/rs_bindings_from_cc/ir.cc
+++ b/rs_bindings_from_cc/ir.cc
@@ -512,6 +512,7 @@
{"owning_target", owning_target},
{"child_item_ids", std::move(json_item_ids)},
{"enclosing_namespace_id", enclosing_namespace_id},
+ {"is_inline", is_inline},
};
return llvm::json::Object{
diff --git a/rs_bindings_from_cc/ir.h b/rs_bindings_from_cc/ir.h
index abdc741..868f6ac 100644
--- a/rs_bindings_from_cc/ir.h
+++ b/rs_bindings_from_cc/ir.h
@@ -751,6 +751,7 @@
BazelLabel owning_target;
std::vector<ItemId> child_item_ids;
llvm::Optional<ItemId> enclosing_namespace_id;
+ bool is_inline = false;
};
inline std::ostream& operator<<(std::ostream& o, const Namespace& n) {
diff --git a/rs_bindings_from_cc/ir.rs b/rs_bindings_from_cc/ir.rs
index 05123ee..bb10ce6 100644
--- a/rs_bindings_from_cc/ir.rs
+++ b/rs_bindings_from_cc/ir.rs
@@ -520,6 +520,7 @@
#[serde(default)]
pub child_item_ids: Vec<ItemId>,
pub enclosing_namespace_id: Option<ItemId>,
+ pub is_inline: bool,
}
#[derive(Debug, PartialEq, Eq, Hash, Clone, Deserialize)]
diff --git a/rs_bindings_from_cc/src_code_gen.rs b/rs_bindings_from_cc/src_code_gen.rs
index a6562fd..267fc51 100644
--- a/rs_bindings_from_cc/src_code_gen.rs
+++ b/rs_bindings_from_cc/src_code_gen.rs
@@ -2150,10 +2150,12 @@
}
let reopened_namespace_idx = ir.get_reopened_namespace_idx(namespace.id)?;
- let should_skip_index =
+ // True if this is actually the module with the name `#name`, rather than e.g.
+ // `#name_0`, `#name_1`, etc.
+ let is_canonical_namespace_module =
ir.is_last_reopened_namespace(namespace.id, namespace.canonical_namespace_id)?;
- let name = if should_skip_index {
+ let name = if is_canonical_namespace_module {
make_rs_ident(&namespace.name.identifier)
} else {
make_rs_ident(&format!("{}_{}", &namespace.name.identifier, reopened_namespace_idx))
@@ -2170,12 +2172,20 @@
quote! { pub use super::#previous_namespace_ident::*; __NEWLINE__ __NEWLINE__ }
};
+ let use_stmt_for_inline_namespace = if namespace.is_inline && is_canonical_namespace_module {
+ quote! {pub use #name::*; __NEWLINE__}
+ } else {
+ quote! {}
+ };
+
let namespace_tokens = quote! {
pub mod #name {
#use_stmt_for_previous_namespace
#( #items __NEWLINE__ __NEWLINE__ )*
}
+ __NEWLINE__
+ #use_stmt_for_inline_namespace
};
Ok(GeneratedItem {
@@ -7286,6 +7296,7 @@
...
pub struct MyStruct {...} ...
}
+ pub use inner::*;
...
pub fn processMyStruct(s: crate::test_namespace_bindings::inner::MyStruct)
...
@@ -7301,6 +7312,38 @@
}
#[test]
+ fn test_inline_namespace_not_marked_inline() -> Result<()> {
+ let rs_api = generate_bindings_tokens(ir_from_cc(
+ r#"
+ inline namespace my_inline {}
+ namespace foo {}
+ namespace my_inline { // still an inline namespace!
+ struct MyStruct final {};
+ }
+ "#,
+ )?)?
+ .rs_api;
+
+ assert_rs_matches!(
+ rs_api,
+ quote! {
+ ...
+ pub mod my_inline_0 {}
+ pub mod foo {}
+ pub mod my_inline {
+ pub use super::my_inline_0::*;
+ ...
+ pub struct MyStruct {...}
+ ...
+ }
+ pub use my_inline::*;
+ ...
+ }
+ );
+ Ok(())
+ }
+
+ #[test]
fn test_implicit_template_specializations_are_sorted_by_mangled_name() -> Result<()> {
let bindings = generate_bindings_tokens(ir_from_cc(
r#"
diff --git a/rs_bindings_from_cc/test/cc_std/test.rs b/rs_bindings_from_cc/test/cc_std/test.rs
index 578c639..eed19d2 100644
--- a/rs_bindings_from_cc/test/cc_std/test.rs
+++ b/rs_bindings_from_cc/test/cc_std/test.rs
@@ -29,15 +29,13 @@
}
#[test]
- fn test_limits() {
+ fn test_limits_inline() {
// Tests of items from the `<limits>` header.
// https://en.cppreference.com/w/cpp/types/numeric_limits/float_round_style:
- //
- // TODO(b/244601795): Stop mentioning the `inline` `__u` namespace below
- assert_eq!(0, std::__u::float_round_style::round_toward_zero.into());
- assert_eq!(1, std::__u::float_round_style::round_to_nearest.into());
- assert_eq!(2, std::__u::float_round_style::round_toward_infinity.into());
- assert_eq!(3, std::__u::float_round_style::round_toward_neg_infinity.into());
- assert_eq!(-1, std::__u::float_round_style::round_indeterminate.into());
+ assert_eq!(0, std::float_round_style::round_toward_zero.into());
+ assert_eq!(1, std::float_round_style::round_to_nearest.into());
+ assert_eq!(2, std::float_round_style::round_toward_infinity.into());
+ assert_eq!(3, std::float_round_style::round_toward_neg_infinity.into());
+ assert_eq!(-1, std::float_round_style::round_indeterminate.into());
}
}
diff --git a/rs_bindings_from_cc/test/golden/namespace_rs_api.rs b/rs_bindings_from_cc/test/golden/namespace_rs_api.rs
index 120ac0b..cc60f89 100644
--- a/rs_bindings_from_cc/test/golden/namespace_rs_api.rs
+++ b/rs_bindings_from_cc/test/golden/namespace_rs_api.rs
@@ -229,6 +229,7 @@
// Error while generating bindings for item 'StructInInlineNamespace::operator=':
// operator= for Unpin types is not yet supported.
}
+ pub use inner::*;
// namespace inner
}
diff --git a/rs_bindings_from_cc/test/namespace/inline/test.rs b/rs_bindings_from_cc/test/namespace/inline/test.rs
index 505ca0a..5e3711d 100644
--- a/rs_bindings_from_cc/test/namespace/inline/test.rs
+++ b/rs_bindings_from_cc/test/namespace/inline/test.rs
@@ -19,15 +19,16 @@
// `std::__u::string`. Therefore the test verifies that the
// inner types and functions are also available in the parent
// namespace.
- //
- // TODO(b/244601795): Add test coverage below.
- // > // `foo::MyStruct` should be a type alias for
- // > // `foo::inline1::MyStruct`.
- // > let mut s2 = foo::MyStruct { value: 456 };
- // > s2 = s;
- // > // The functions should be available as `foo::GetStructValue...`
- // > // as well.
- // > assert_eq!(123, foo::GetStructValue1(&s));
- // > assert_eq!(123, foo::GetStructValue2(&s));
+ // `foo::MyStruct` should be a type alias for
+ // `foo::inline1::MyStruct`.
+ #[allow(unused_assignments)]
+ let mut s2 = foo::MyStruct { value: 456 };
+ s2 = s; // these are literally the same type.
+ // The functions should be available as `foo::GetStructValue...`
+ // as well.
+ assert_eq!(123, foo::GetStructValue1(&s2));
+ assert_eq!(123, foo::GetStructValue2(&s2));
+ assert_eq!(123, foo::GetStructValue3(&s2));
+ assert_eq!(123, foo::GetStructValue4(&s2));
}
}