Use `supported | experimental` features everywhere in tests.
This actually was a bit tricky to implement, because it broke the formatting -- I discovered we use rustfmt for formatting the `Debug` output, which, while it mkaes sense, seems probably unnecessary if we do some plumbing. But I couldn't figure out the plumbing right now, so I filed b/272530008 instead.
After reflecting for this bug for a bit, I decided the sensible thing is to try to share the logic for both in one helper function, even if it seems a little awkward to do so -- having _no_ shared functionality is essentially what caused it.
PiperOrigin-RevId: 516681109
diff --git a/rs_bindings_from_cc/BUILD b/rs_bindings_from_cc/BUILD
index a30710c..b5b0139 100644
--- a/rs_bindings_from_cc/BUILD
+++ b/rs_bindings_from_cc/BUILD
@@ -329,7 +329,20 @@
":json_from_cc",
"//common:arc_anyhow",
"//common:ffi_types",
+ "@crate_index//:flagset",
"@crate_index//:itertools",
+ "@crate_index//:once_cell",
+ ],
+)
+
+rust_test(
+ name = "ir_testing_test",
+ crate = ":ir_testing",
+ deps = [
+ ":ir_matchers",
+ ":ir_testing",
+ "//common:rust_allocator_shims",
+ "@crate_index//:quote",
],
)
diff --git a/rs_bindings_from_cc/ir.rs b/rs_bindings_from_cc/ir.rs
index d2cf11b..43f033e 100644
--- a/rs_bindings_from_cc/ir.rs
+++ b/rs_bindings_from_cc/ir.rs
@@ -779,7 +779,7 @@
}
}
-#[derive(Debug, PartialEq, Eq, Clone, Deserialize)]
+#[derive(PartialEq, Eq, Clone, Deserialize)]
#[serde(deny_unknown_fields, rename(deserialize = "IR"))]
struct FlatIR {
#[serde(default)]
@@ -795,6 +795,42 @@
crubit_features: HashMap<BazelLabel, CrubitFeaturesIR>,
}
+/// A custom debug impl that wraps the HashMap in rustfmt-friendly notation.
+///
+/// See b/272530008.
+impl std::fmt::Debug for FlatIR {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ struct DebugHashMap<T: Debug>(pub T);
+ impl<T: Debug> std::fmt::Debug for DebugHashMap<T> {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ // prefix the hash map with `hash_map!` so that the output can be fed to
+ // rustfmt. The end result is something like `hash_map!{k:v,
+ // k2:v2}`, which reads well.
+ write!(f, "hash_map!")?;
+ std::fmt::Debug::fmt(&self.0, f)
+ }
+ }
+ // exhaustive-match so we don't forget to add fields to Debug when we add to
+ // FlatIR.
+ let FlatIR {
+ public_headers,
+ current_target,
+ items,
+ top_level_item_ids,
+ crate_root_path,
+ crubit_features,
+ } = self;
+ f.debug_struct("FlatIR")
+ .field("public_headers", public_headers)
+ .field("current_target", current_target)
+ .field("items", items)
+ .field("top_level_item_ids", top_level_item_ids)
+ .field("crate_root_path", crate_root_path)
+ .field("crubit_features", &DebugHashMap(crubit_features))
+ .finish()
+ }
+}
+
/// Struct providing the necessary information about the API of a C++ target to
/// enable generation of Rust bindings source code (both `rs_api.rs` and
/// `rs_api_impl.cc` files).
@@ -905,6 +941,22 @@
self.flat_ir.crubit_features.get(target).cloned().unwrap_or_default().0
}
+ /// Returns a mutable reference to the Crubit features enabled for the given
+ /// `target`.
+ ///
+ /// Since IR is generally only held immutably, this is only useful for
+ /// testing.
+ #[must_use]
+ pub fn target_crubit_features_mut(
+ &mut self,
+ target: &BazelLabel,
+ ) -> &mut flagset::FlagSet<CrubitFeature> {
+ // TODO(jeanpierreda): migrate to raw_entry_mut when stable.
+ // (target is taken by reference exactly because ideally this function would use
+ // the raw entry API.)
+ &mut self.flat_ir.crubit_features.entry(target.clone()).or_default().0
+ }
+
pub fn current_target(&self) -> &BazelLabel {
&self.flat_ir.current_target
}
diff --git a/rs_bindings_from_cc/ir_testing.rs b/rs_bindings_from_cc/ir_testing.rs
index 0b23230..087e24b 100644
--- a/rs_bindings_from_cc/ir_testing.rs
+++ b/rs_bindings_from_cc/ir_testing.rs
@@ -2,11 +2,14 @@
// Exceptions. See /LICENSE for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+use std::collections::hash_map::HashMap;
+
use arc_anyhow::Result;
+use itertools::Itertools;
+use once_cell::sync::Lazy;
use ffi_types::{FfiU8Slice, FfiU8SliceBox};
use ir::{self, make_ir_from_parts, Func, Identifier, Item, Record, IR};
-use itertools::Itertools;
/// Generates `IR` from a header containing `header_source`.
pub fn ir_from_cc(header_source: &str) -> Result<IR> {
@@ -30,20 +33,31 @@
/// Name of the current target used by `ir_from_cc` and `ir_from_cc_dependency`.
pub const TESTING_TARGET: &str = "//test:testing_target";
+static TESTING_FEATURES: Lazy<flagset::FlagSet<ir::CrubitFeature>> =
+ Lazy::new(|| ir::CrubitFeature::Experimental | ir::CrubitFeature::Supported);
+
+/// Update the IR to have common test-only items.
+///
+/// This provides one place to update the IR that affects both
+/// `make_ir_from_items` and `ir_from_cc_dependency`.
+fn update_test_ir(ir: &mut IR) {
+ *ir.target_crubit_features_mut(&ir.current_target().clone()) = *TESTING_FEATURES;
+}
/// Create a testing `IR` instance from given items, using mock values for other
/// fields.
pub fn make_ir_from_items(items: impl IntoIterator<Item = Item>) -> Result<IR> {
- let target: ir::BazelLabel = TESTING_TARGET.into();
- make_ir_from_parts(
+ let mut ir = make_ir_from_parts(
items.into_iter().collect_vec(),
/* public_headers= */ vec![],
- /* current_target= */ target.clone(),
+ /* current_target= */ TESTING_TARGET.into(),
/* top_level_item_ids= */ vec![],
/* crate_root_path= */ None,
/* crubit_features= */
- [(target, ir::CrubitFeature::Experimental | ir::CrubitFeature::Supported)].into(),
- )
+ <HashMap<ir::BazelLabel, flagset::FlagSet<ir::CrubitFeature>>>::new(),
+ )?;
+ update_test_ir(&mut ir);
+ Ok(ir)
}
/// Target of the dependency used by `ir_from_cc_dependency`.
@@ -76,7 +90,9 @@
)
.into_boxed_slice()
};
- ir::deserialize_ir(&*json_utf8)
+ let mut ir = ir::deserialize_ir(&*json_utf8)?;
+ update_test_ir(&mut ir);
+ Ok(ir)
}
/// Creates an identifier
@@ -119,3 +135,35 @@
}
panic!("Didn't find record with cc_name {}", cc_name);
}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use ir_matchers::assert_ir_matches;
+ use quote::quote;
+
+ #[test]
+ fn test_features_ir_from_cc() -> Result<()> {
+ assert_ir_matches!(
+ ir_from_cc("")?,
+ quote! {
+ crubit_features: hash_map!{
+ BazelLabel("//test:testing_target"): CrubitFeaturesIR(FlagSet(Supported|Experimental))
+ }
+ }
+ );
+ Ok(())
+ }
+ #[test]
+ fn test_features_ir_from_items() -> Result<()> {
+ assert_ir_matches!(
+ make_ir_from_items([])?,
+ quote! {
+ crubit_features: hash_map!{
+ BazelLabel("//test:testing_target"): CrubitFeaturesIR(FlagSet(Supported|Experimental))
+ }
+ }
+ );
+ Ok(())
+ }
+}