Pass error format strings through the IR
These formatted errors exist separately on the C++ and Rust sides of
the generator. This change brings the two sides together.
PiperOrigin-RevId: 653405910
Change-Id: I0b655338cc22a0a8c30315aa01206e4cdb785606
diff --git a/rs_bindings_from_cc/BUILD b/rs_bindings_from_cc/BUILD
index 7cc40c8..11c293b 100644
--- a/rs_bindings_from_cc/BUILD
+++ b/rs_bindings_from_cc/BUILD
@@ -346,6 +346,7 @@
deps = [
"//common:arc_anyhow",
"//common:code_gen_utils",
+ "//common:error_report",
"@crate_index//:flagset",
"@crate_index//:itertools",
"@crate_index//:once_cell",
diff --git a/rs_bindings_from_cc/generate_bindings/lib.rs b/rs_bindings_from_cc/generate_bindings/lib.rs
index 1cabb3e..87e3e6d 100644
--- a/rs_bindings_from_cc/generate_bindings/lib.rs
+++ b/rs_bindings_from_cc/generate_bindings/lib.rs
@@ -335,7 +335,9 @@
/// Generates Rust source code for a given `UnsupportedItem`.
fn generate_unsupported(db: &Database, item: &UnsupportedItem) -> Result<GeneratedItem> {
- db.errors().insert(item.cause());
+ for error in &item.errors {
+ db.errors().insert(&error.to_error());
+ }
let source_loc = item.source_loc();
let source_loc = match &source_loc {
@@ -345,12 +347,14 @@
_ => "",
};
- let message = format!(
- "{source_loc}{}Error while generating bindings for item '{}':\n{}",
+ let mut message = format!(
+ "{source_loc}{}Error while generating bindings for item '{}':\n",
if source_loc.is_empty() { "" } else { "\n" },
item.name.as_ref(),
- item.message()
);
+ for (index, error) in item.errors.iter().enumerate() {
+ message = format!("{message}{}{}", if index == 0 { "" } else { "\n\n" }, error.message,);
+ }
Ok(GeneratedItem { item: quote! { __COMMENT__ #message }, ..Default::default() })
}
diff --git a/rs_bindings_from_cc/ir.cc b/rs_bindings_from_cc/ir.cc
index 0c1106c..654f553 100644
--- a/rs_bindings_from_cc/ir.cc
+++ b/rs_bindings_from_cc/ir.cc
@@ -532,17 +532,15 @@
}
llvm::json::Value UnsupportedItem::ToJson() const {
- std::string message;
+ std::vector<llvm::json::Value> json_errors;
+ json_errors.reserve(errors.size());
for (const auto& error : errors) {
- if (!message.empty()) {
- message += "\n\n";
- }
- message += error.message;
+ json_errors.push_back(error.ToJson());
}
llvm::json::Object unsupported{
{"name", name},
- {"message", message},
+ {"errors", json_errors},
{"source_loc", source_loc},
{"id", id},
};
diff --git a/rs_bindings_from_cc/ir.rs b/rs_bindings_from_cc/ir.rs
index 349f111..a0f6bda 100644
--- a/rs_bindings_from_cc/ir.rs
+++ b/rs_bindings_from_cc/ir.rs
@@ -804,11 +804,28 @@
fn hash<H: Hasher>(&self, _state: &mut H) {}
}
+#[derive(Debug, PartialEq, Eq, Hash, Deserialize)]
+#[serde(deny_unknown_fields)]
+pub struct FormattedError {
+ pub fmt: Rc<str>,
+ pub message: Rc<str>,
+}
+
+impl FormattedError {
+ pub fn to_error(&self) -> Error {
+ error_report::FormattedError {
+ fmt: self.fmt.to_string().into(),
+ message: self.message.to_string().into(),
+ }
+ .into()
+ }
+}
+
#[derive(Debug, PartialEq, Eq, Hash, Clone, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct UnsupportedItem {
pub name: Rc<str>,
- message: Rc<str>,
+ pub errors: Vec<Rc<FormattedError>>,
pub source_loc: Option<Rc<str>>,
pub id: ItemId,
#[serde(skip)]
@@ -834,7 +851,7 @@
fn new(ir: &IR, item: &impl GenericItem, message: Rc<str>, cause: Option<Error>) -> Self {
Self {
name: item.debug_name(ir),
- message,
+ errors: vec![Rc::new(FormattedError { fmt: "{}".into(), message })],
source_loc: item.source_loc(),
id: item.id(),
cause: IgnoredField(cause.map(OnceCell::from).unwrap_or_default()),
@@ -847,14 +864,6 @@
pub fn new_with_cause(ir: &IR, item: &impl GenericItem, cause: Error) -> Self {
Self::new(ir, item, format!("{cause:#}").into(), Some(cause))
}
-
- pub fn message(&self) -> &str {
- self.message.as_ref()
- }
-
- pub fn cause(&self) -> &Error {
- self.cause.0.get_or_init(|| anyhow!(self.message.as_ref().to_owned()))
- }
}
#[derive(Debug, PartialEq, Eq, Hash, Clone, Deserialize)]
diff --git a/rs_bindings_from_cc/ir_from_cc_test.rs b/rs_bindings_from_cc/ir_from_cc_test.rs
index aa78601..38db848 100644
--- a/rs_bindings_from_cc/ir_from_cc_test.rs
+++ b/rs_bindings_from_cc/ir_from_cc_test.rs
@@ -172,9 +172,10 @@
assert_ir_matches!(
ir,
quote! { UnsupportedItem {
- name: "Self", ...
- message: "Record name is not supported: Unescapable identifier: Self"
- ...
+ name: "Self", ...
+ errors: [FormattedError {
+ ... message: "Record name is not supported: Unescapable identifier: Self", ...
+ }], ...
}}
);
}
@@ -185,9 +186,10 @@
assert_ir_matches!(
ir,
quote! { UnsupportedItem {
- name: "Self", ...
- message: "Enum name is not supported: Unescapable identifier: Self"
- ...
+ name: "Self", ...
+ errors: [FormattedError {
+ ... message: "Enum name is not supported: Unescapable identifier: Self", ...
+ }], ...
}}
);
}
@@ -198,9 +200,10 @@
assert_ir_matches!(
ir,
quote! { UnsupportedItem {
- name: "SomeEnum", ...
- message: "Enumerator name is not supported: Unescapable identifier: self"
- ...
+ name: "SomeEnum", ...
+ errors: [FormattedError {
+ ..., message: "Enumerator name is not supported: Unescapable identifier: self", ...
+ }], ...
}}
);
}
@@ -211,9 +214,10 @@
assert_ir_matches!(
ir,
quote! { UnsupportedItem {
- name: "Self", ...
- message: "Record name is not supported: Unescapable identifier: Self"
- ...
+ name: "Self", ...
+ errors: [FormattedError {
+ ..., message: "Record name is not supported: Unescapable identifier: Self", ...
+ }], ...
}}
);
}
@@ -243,9 +247,10 @@
assert_ir_matches!(
ir,
quote! { UnsupportedItem {
- name: "self", ...
- message: "Namespace name is not supported: Unescapable identifier: self"
- ...
+ name: "self", ...
+ errors: [FormattedError {
+ ..., message: "Namespace name is not supported: Unescapable identifier: self", ...
+ }], ...
}}
);
}
@@ -256,9 +261,10 @@
assert_ir_matches!(
ir,
quote! { UnsupportedItem {
- name: "self", ...
- message: "Function name is not supported: Unescapable identifier: self"
- ...
+ name: "self", ...
+ errors: [FormattedError {
+ ..., message: "Function name is not supported: Unescapable identifier: self", ...
+ }], ...
}}
);
}
@@ -269,9 +275,10 @@
assert_ir_matches!(
ir,
quote! { UnsupportedItem {
- name: "Self", ...
- message: "Type alias name is not supported: Unescapable identifier: Self"
- ...
+ name: "Self", ...
+ errors: [FormattedError {
+ ..., message: "Type alias name is not supported: Unescapable identifier: Self", ...
+ }], ...
}}
);
}
@@ -321,8 +328,10 @@
assert_ir_matches!(
ir,
quote! { UnsupportedItem {
- name: "MyTemplate",
- message: "Class templates are not supported yet" ...
+ name: "MyTemplate",
+ errors: [FormattedError {
+ ..., message: "Class templates are not supported yet", ...
+ }], ...
}}
);
}
@@ -333,8 +342,10 @@
assert_ir_matches!(
ir,
quote! { UnsupportedItem {
- name: "SomeFunctionTemplate",
- message: "Function templates are not supported yet" ...
+ name: "SomeFunctionTemplate",
+ errors: [FormattedError {
+ ..., message: "Function templates are not supported yet", ...
+ }], ...
}}
);
}
@@ -482,9 +493,10 @@
assert_ir_matches!(
ir,
quote! { UnsupportedItem {
- name: "PackedStruct", ...
- message: "Records with packed layout are not supported"
- ...
+ name: "PackedStruct", ...
+ errors: [FormattedError {
+ ..., message: "Records with packed layout are not supported", ...
+ }], ...
}}
);
}
@@ -504,9 +516,10 @@
assert_ir_matches!(
ir,
quote! { UnsupportedItem {
- name: "PackedStruct", ...
- message: "Records with packed layout are not supported"
- ...
+ name: "PackedStruct", ...
+ errors: [FormattedError {
+ ..., message: "Records with packed layout are not supported", ...
+ }], ...
}}
);
}
@@ -2323,8 +2336,10 @@
assert_ir_matches!(
ir,
quote! { UnsupportedItem {
- name: "SomeStruct::NestedStruct",
- message: "Nested classes are not supported yet" ...
+ name: "SomeStruct::NestedStruct",
+ errors: [FormattedError {
+ ..., message: "Nested classes are not supported yet", ...
+ }], ...
}}
);
}
@@ -2378,8 +2393,9 @@
quote! {
UnsupportedItem {
name: "StructWithUnsupportedField::NestedStruct",
- message: "Nested classes are not supported yet",
- ...
+ errors: [FormattedError {
+ ..., message: "Nested classes are not supported yet", ...
+ }], ...
}
}
);
@@ -2443,9 +2459,10 @@
ir,
quote! {
UnsupportedItem {
- name: "OuterStruct::NestedStruct",
- message: "Nested classes are not supported yet",
- ...
+ name: "OuterStruct::NestedStruct",
+ errors: [FormattedError {
+ ..., message: "Nested classes are not supported yet", ...
+ }], ...
}
}
);
@@ -2504,8 +2521,10 @@
assert_ir_not_matches!(
ir,
quote! { UnsupportedItem {
- name: "SomeStruct::SomeStruct",
- message: "Nested classes are not supported yet" ...
+ name: "SomeStruct::SomeStruct",
+ errors: [FormattedError {
+ ..., message: "Nested classes are not supported yet", ...
+ }], ...
}}
);
}
@@ -3179,7 +3198,10 @@
#[test]
fn test_volatile_is_unsupported() {
let ir = ir_from_cc("volatile int* foo();").unwrap();
- let f = ir.unsupported_items().find(|i| i.message().contains("volatile")).unwrap();
+ let f = ir
+ .unsupported_items()
+ .find(|i| i.errors.iter().any(|e| e.message.contains("volatile")))
+ .unwrap();
assert_eq!("foo", f.name.as_ref());
}
@@ -3191,7 +3213,9 @@
quote! {
UnsupportedItem {
name: "(unnamed enum at ./ir_from_cc_virtual_header.h:3:1)",
- message: "Unnamed enums are not supported yet" ...
+ errors: [FormattedError {
+ ..., message: "Unnamed enums are not supported yet", ...
+ }], ...
}
}
);
@@ -3211,8 +3235,9 @@
quote! {
UnsupportedItem {
name: "operator\"\"_foobar",
- message: "Function name is not supported: Unsupported name: operator\"\"_foobar"
- ...
+ errors: [FormattedError {
+ ..., message: "Function name is not supported: Unsupported name: operator\"\"_foobar", ...
+ }], ...
}
}
);
@@ -3364,7 +3389,9 @@
quote! {
... UnsupportedItem {
name: "TopLevelStruct::operator int",
- message: "Function name is not supported: Unsupported name: operator int",
+ errors: [FormattedError {
+ ..., message: "Function name is not supported: Unsupported name: operator int",
+ }],
...
}
},