Make a common `Item` -> `UnsupportedItem` path.
This removes the myriad ways to create an `UnsupportedItem`, and will make it easier to convert an item to "unsupported" for new reasons, as well. (In particular, I want to convert arbitrary items to unsupported in a followup CL if they are filtered out by the features flags checks.)
I was tempted to use a custom type implementing `Display`, rather than just `Rc<str>`, but it's actually an annoying amount of work and doesn't hide the type completely. (You end up with `type DebugName<'a> : Display; fn debug_name<'a>(&'a self, ir: &'a IR) -> Self::DebugName<'a>;` so that the Display impl can hold onto the two references.)
PiperOrigin-RevId: 518732262
diff --git a/common/ffi_types.rs b/common/ffi_types.rs
index 3f334f9..ee75fdf 100644
--- a/common/ffi_types.rs
+++ b/common/ffi_types.rs
@@ -94,7 +94,7 @@
/// Whether or not the generated binding will have doc comments indicating their
/// source location.
#[repr(C)]
-#[derive(Debug, Copy, Clone)]
+#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub enum SourceLocationDocComment {
Disabled,
Enabled,
diff --git a/rs_bindings_from_cc/ir.rs b/rs_bindings_from_cc/ir.rs
index e7f0e94..894f532 100644
--- a/rs_bindings_from_cc/ir.rs
+++ b/rs_bindings_from_cc/ir.rs
@@ -13,11 +13,38 @@
use serde::Deserialize;
use std::collections::hash_map::{Entry, HashMap};
use std::convert::TryFrom;
-use std::fmt::{self, Debug, Formatter};
+use std::fmt::{self, Debug, Display, Formatter};
use std::hash::{Hash, Hasher};
use std::io::Read;
use std::rc::Rc;
+/// Common data about all items.
+pub trait GenericItem {
+ fn id(&self) -> ItemId;
+ /// The name of the item, readable by programmers.
+ ///
+ /// For example, `void Foo();` should have name `Foo`.
+ fn debug_name(&self, ir: &IR) -> Rc<str>;
+
+ /// The recorded source location, or None if none is present.
+ fn source_loc(&self) -> Option<Rc<str>>;
+}
+
+impl<T> GenericItem for Rc<T>
+where
+ T: GenericItem + ?Sized,
+{
+ fn id(&self) -> ItemId {
+ (**self).id()
+ }
+ fn debug_name(&self, ir: &IR) -> Rc<str> {
+ (**self).debug_name(ir)
+ }
+ fn source_loc(&self) -> Option<Rc<str>> {
+ (**self).source_loc()
+ }
+}
+
/// Deserialize `IR` from JSON given as a reader.
pub fn deserialize_ir<R: Read>(reader: R) -> Result<IR> {
let flat_ir = serde_json::from_reader(reader)?;
@@ -199,9 +226,15 @@
pub identifier: Rc<str>,
}
-impl fmt::Debug for Identifier {
+impl Display for Identifier {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- f.write_str(&format!("\"{}\"", &self.identifier))
+ write!(f, "{}", self.identifier)
+ }
+}
+
+impl Debug for Identifier {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ write!(f, "\"{}\"", self.identifier)
}
}
@@ -228,9 +261,9 @@
}
}
-impl fmt::Debug for Operator {
+impl Debug for Operator {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- f.write_str(&format!("\"{}\"", &self.cc_name()))
+ write!(f, "\"{}\"", self.cc_name())
}
}
@@ -274,8 +307,8 @@
}
}
-impl std::fmt::Display for BazelLabel {
- fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+impl Display for BazelLabel {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", &*self.0)
}
}
@@ -297,11 +330,11 @@
}
}
-impl fmt::Debug for UnqualifiedIdentifier {
+impl Debug for UnqualifiedIdentifier {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
- UnqualifiedIdentifier::Identifier(identifier) => fmt::Debug::fmt(identifier, f),
- UnqualifiedIdentifier::Operator(op) => fmt::Debug::fmt(op, f),
+ UnqualifiedIdentifier::Identifier(identifier) => Debug::fmt(identifier, f),
+ UnqualifiedIdentifier::Operator(op) => Debug::fmt(op, f),
UnqualifiedIdentifier::Constructor => f.write_str("Constructor"),
UnqualifiedIdentifier::Destructor => f.write_str("Destructor"),
}
@@ -363,6 +396,36 @@
pub adl_enclosing_record: Option<ItemId>,
}
+impl GenericItem for Func {
+ fn id(&self) -> ItemId {
+ self.id
+ }
+ fn debug_name(&self, ir: &IR) -> Rc<str> {
+ let record: Option<Rc<str>> = ir.record_for_member_func(self).map(|r| r.debug_name(ir));
+ let record: Option<&str> = record.as_deref();
+
+ let func_name = match &self.name {
+ UnqualifiedIdentifier::Identifier(id) => id.identifier.to_string(),
+ UnqualifiedIdentifier::Operator(op) => op.cc_name(),
+ UnqualifiedIdentifier::Destructor => {
+ format!("~{}", record.expect("destructor must be associated with a record"))
+ }
+ UnqualifiedIdentifier::Constructor => {
+ record.expect("constructor must be associated with a record").to_string()
+ }
+ };
+
+ if let Some(record_name) = record {
+ format!("{}::{}", record_name, func_name).into()
+ } else {
+ func_name.into()
+ }
+ }
+ fn source_loc(&self) -> Option<Rc<str>> {
+ Some(self.source_loc.clone())
+ }
+}
+
impl Func {
pub fn is_instance_method(&self) -> bool {
self.member_func_metadata
@@ -422,6 +485,18 @@
pub enclosing_namespace_id: Option<ItemId>,
}
+impl GenericItem for IncompleteRecord {
+ fn id(&self) -> ItemId {
+ self.id
+ }
+ fn debug_name(&self, _: &IR) -> Rc<str> {
+ self.cc_name.clone()
+ }
+ fn source_loc(&self) -> Option<Rc<str>> {
+ None
+ }
+}
+
#[derive(Debug, PartialEq, Eq, Hash, Copy, Clone, Deserialize)]
pub enum RecordType {
Struct,
@@ -471,6 +546,18 @@
pub enclosing_namespace_id: Option<ItemId>,
}
+impl GenericItem for Record {
+ fn id(&self) -> ItemId {
+ self.id
+ }
+ fn debug_name(&self, _: &IR) -> Rc<str> {
+ self.cc_name.clone()
+ }
+ fn source_loc(&self) -> Option<Rc<str>> {
+ Some(self.source_loc.clone())
+ }
+}
+
impl Record {
/// Whether this type has Rust-like object semantics for mutating
/// assignment, and can be passed by mut reference as a result.
@@ -522,6 +609,18 @@
pub enclosing_namespace_id: Option<ItemId>,
}
+impl GenericItem for Enum {
+ fn id(&self) -> ItemId {
+ self.id
+ }
+ fn debug_name(&self, _: &IR) -> Rc<str> {
+ self.identifier.to_string().into()
+ }
+ fn source_loc(&self) -> Option<Rc<str>> {
+ Some(self.source_loc.clone())
+ }
+}
+
#[derive(Debug, PartialEq, Eq, Hash, Clone, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct Enumerator {
@@ -542,6 +641,18 @@
pub enclosing_namespace_id: Option<ItemId>,
}
+impl GenericItem for TypeAlias {
+ fn id(&self) -> ItemId {
+ self.id
+ }
+ fn debug_name(&self, _: &IR) -> Rc<str> {
+ self.identifier.to_string().into()
+ }
+ fn source_loc(&self) -> Option<Rc<str>> {
+ Some(self.source_loc.clone())
+ }
+}
+
/// A wrapper type that does not contribute to equality or hashing. All
/// instances are equal.
#[derive(Clone, Copy, Default)]
@@ -570,31 +681,42 @@
pub struct UnsupportedItem {
pub name: Rc<str>,
message: Rc<str>,
- pub source_loc: Rc<str>,
+ pub source_loc: Option<Rc<str>>,
pub id: ItemId,
#[serde(skip)]
cause: IgnoredField<OnceCell<Error>>,
}
+impl GenericItem for UnsupportedItem {
+ fn id(&self) -> ItemId {
+ self.id
+ }
+ fn debug_name(&self, _: &IR) -> Rc<str> {
+ self.name.clone()
+ }
+ fn source_loc(&self) -> Option<Rc<str>> {
+ self.source_loc.clone()
+ }
+}
+
impl UnsupportedItem {
- pub fn new_with_message(name: &str, message: &str, source_loc: Rc<str>, id: ItemId) -> Self {
+ fn new(ir: &IR, item: &impl GenericItem, message: Rc<str>, cause: Option<Error>) -> Self {
Self {
- name: name.into(),
- message: message.into(),
- source_loc,
- id,
- cause: Default::default(),
+ name: item.debug_name(ir),
+ message,
+ source_loc: item.source_loc(),
+ id: item.id(),
+ cause: IgnoredField(cause.map(OnceCell::from).unwrap_or_default()),
}
}
- pub fn new_with_cause(name: String, cause: Error, source_loc: Rc<str>, id: ItemId) -> Self {
- Self {
- name: name.into(),
- message: cause.to_string().into(),
- source_loc,
- id,
- cause: IgnoredField(cause.into()),
- }
+
+ pub fn new_with_message(ir: &IR, item: &impl GenericItem, message: impl Into<Rc<str>>) -> Self {
+ Self::new(ir, item, message.into(), None)
}
+ pub fn new_with_cause(ir: &IR, item: &impl GenericItem, cause: Error) -> Self {
+ Self::new(ir, item, cause.to_string().into(), Some(cause))
+ }
+
pub fn message(&self) -> &str {
self.message.as_ref()
}
@@ -611,6 +733,18 @@
pub id: ItemId,
}
+impl GenericItem for Comment {
+ fn id(&self) -> ItemId {
+ self.id
+ }
+ fn debug_name(&self, _: &IR) -> Rc<str> {
+ "comment".into()
+ }
+ fn source_loc(&self) -> Option<Rc<str>> {
+ None
+ }
+}
+
#[derive(Debug, PartialEq, Eq, Hash, Clone, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct Namespace {
@@ -624,6 +758,18 @@
pub is_inline: bool,
}
+impl GenericItem for Namespace {
+ fn id(&self) -> ItemId {
+ self.id
+ }
+ fn debug_name(&self, _: &IR) -> Rc<str> {
+ self.name.to_string().into()
+ }
+ fn source_loc(&self) -> Option<Rc<str>> {
+ None
+ }
+}
+
#[derive(Debug, PartialEq, Eq, Hash, Clone, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct UseMod {
@@ -632,6 +778,18 @@
pub id: ItemId,
}
+impl GenericItem for UseMod {
+ fn id(&self) -> ItemId {
+ self.id
+ }
+ fn debug_name(&self, _: &IR) -> Rc<str> {
+ format!("[internal] use mod {}::* = {}", self.mod_name, self.path).into()
+ }
+ fn source_loc(&self) -> Option<Rc<str>> {
+ None
+ }
+}
+
#[derive(Debug, PartialEq, Eq, Hash, Clone, Deserialize)]
pub enum Item {
Func(Rc<Func>),
@@ -645,20 +803,47 @@
UseMod(Rc<UseMod>),
}
-impl Item {
+macro_rules! forward_item {
+ (match $item:ident { _($item_name:ident) => $expr:expr $(,)? }) => {
+ match $item {
+ Item::Func($item_name) => $expr,
+ Item::IncompleteRecord($item_name) => $expr,
+ Item::Record($item_name) => $expr,
+ Item::Enum($item_name) => $expr,
+ Item::TypeAlias($item_name) => $expr,
+ Item::UnsupportedItem($item_name) => $expr,
+ Item::Comment($item_name) => $expr,
+ Item::Namespace($item_name) => $expr,
+ Item::UseMod($item_name) => $expr,
+ }
+ };
+}
+
+impl GenericItem for Item {
fn id(&self) -> ItemId {
- match self {
- Item::Func(func) => func.id,
- Item::IncompleteRecord(record) => record.id,
- Item::Record(record) => record.id,
- Item::Enum(enum_) => enum_.id,
- Item::TypeAlias(type_alias) => type_alias.id,
- Item::UnsupportedItem(unsupported) => unsupported.id,
- Item::Comment(comment) => comment.id,
- Item::Namespace(namespace) => namespace.id,
- Item::UseMod(use_mod) => use_mod.id,
+ forward_item! {
+ match self {
+ _(x) => x.id()
+ }
}
}
+ fn debug_name(&self, ir: &IR) -> Rc<str> {
+ forward_item! {
+ match self {
+ _(x) => x.debug_name(ir)
+ }
+ }
+ }
+ fn source_loc(&self) -> Option<Rc<str>> {
+ forward_item! {
+ match self {
+ _(x) => x.source_loc()
+ }
+ }
+ }
+}
+
+impl Item {
pub fn enclosing_namespace_id(&self) -> Option<ItemId> {
match self {
Item::Record(record) => record.enclosing_namespace_id,
@@ -823,16 +1008,16 @@
/// 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 {
+impl Debug for FlatIR {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> 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 {
+ impl<T: Debug> Debug for DebugHashMap<T> {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> 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)
+ Debug::fmt(&self.0, f)
}
}
// exhaustive-match so we don't forget to add fields to Debug when we add to
@@ -1028,9 +1213,13 @@
/// in `self`.
pub fn record_for_member_func(&self, func: &Func) -> Option<&Rc<Record>> {
if let Some(meta) = func.member_func_metadata.as_ref() {
- Some(self.find_decl(meta.record_id).with_context(|| {
- format!("Failed to retrieve Record for MemberFuncMetadata of {:?}", func)
- }).unwrap())
+ Some(
+ self.find_decl(meta.record_id)
+ .with_context(|| {
+ format!("Failed to retrieve Record for MemberFuncMetadata of {:?}", func)
+ })
+ .unwrap(),
+ )
} else {
None
}
diff --git a/rs_bindings_from_cc/ir_from_cc_test.rs b/rs_bindings_from_cc/ir_from_cc_test.rs
index 8823b89..9468d1f 100644
--- a/rs_bindings_from_cc/ir_from_cc_test.rs
+++ b/rs_bindings_from_cc/ir_from_cc_test.rs
@@ -3838,109 +3838,86 @@
#[test]
fn test_source_location_with_macro() {
- for (ir_type, cc_snippet, expected_source_loc) in [
- (
- quote! {Func},
- r#"
+ let assert_matches = |cc_snippet: &str, expected: proc_macro2::TokenStream| {
+ let ir = ir_from_cc(cc_snippet).unwrap();
+ assert_ir_matches!(ir, expected);
+ };
+ let loc = "Generated from: google3/ir_from_cc_virtual_header.h;l=5\n\
+ Expanded at: google3/ir_from_cc_virtual_header.h;l=7";
+ assert_matches(
+ r#"
#define NO_OP_FUNC(func_name) \
void fun_name();
NO_OP_FUNC(no_op_func_to_test_source_location_with_macro);"#,
- "Generated from: google3/ir_from_cc_virtual_header.h;l=5\n\
- Expanded at: google3/ir_from_cc_virtual_header.h;l=7",
- ),
- (
- quote! {TypeAlias},
- r#"
+ quote! {Func { ..., source_loc: #loc, ... } },
+ );
+
+ let loc = "Generated from: google3/ir_from_cc_virtual_header.h;l=4\n\
+ Expanded at: google3/ir_from_cc_virtual_header.h;l=5";
+ assert_matches(
+ r#"
#define TYPE_ALIAS_TO_INT(type_alias) using type_alias = int;
TYPE_ALIAS_TO_INT(MyIntToTestSourceLocationWithMacro);"#,
- "Generated from: google3/ir_from_cc_virtual_header.h;l=4\n\
- Expanded at: google3/ir_from_cc_virtual_header.h;l=5",
- ),
- (
- quote! {UnsupportedItem},
- r#"
+ quote! {TypeAlias { ..., source_loc: #loc, ... } },
+ );
+ let loc = "Generated from: google3/ir_from_cc_virtual_header.h;l=4\n\
+ Expanded at: google3/ir_from_cc_virtual_header.h;l=6";
+ assert_matches(
+ r#"
#define TEMPLATE_NO_OP_FUNC(func_name) \
template <typename T> void func_name() {};
TEMPLATE_NO_OP_FUNC(unsupported_templated_no_op_func_to_test_source_location_with_macro);"#,
- "Generated from: google3/ir_from_cc_virtual_header.h;l=4\n\
- Expanded at: google3/ir_from_cc_virtual_header.h;l=6",
- ),
- (
- quote! {Enum},
- r#"
+ quote! {UnsupportedItem { ..., source_loc: Some(#loc,), ... } },
+ );
+
+ let loc = "Generated from: google3/ir_from_cc_virtual_header.h;l=5\n\
+ Expanded at: google3/ir_from_cc_virtual_header.h;l=6";
+ assert_matches(
+ r#"
#define DEFINE_EMPTY_ENUM(enum_name) \
enum enum_name {};
DEFINE_EMPTY_ENUM(EmptyEnumToTestSourceLocationWithMacro);"#,
- "Generated from: google3/ir_from_cc_virtual_header.h;l=5\n\
- Expanded at: google3/ir_from_cc_virtual_header.h;l=6",
- ),
- (
- quote! {Record},
- r#"
+ quote! {Enum { ..., source_loc: #loc, ... } },
+ );
+
+ let loc = "Generated from: google3/ir_from_cc_virtual_header.h;l=5\n\
+ Expanded at: google3/ir_from_cc_virtual_header.h;l=6";
+ assert_matches(
+ r#"
#define DEFINE_EMPTY_STRUCT(struct_name) \
struct struct_name {};
DEFINE_EMPTY_STRUCT(EmptyStructToTestSourceLocationWithMacro);"#,
- "Generated from: google3/ir_from_cc_virtual_header.h;l=5\n\
- Expanded at: google3/ir_from_cc_virtual_header.h;l=6",
- ),
- ] {
- let ir = ir_from_cc(cc_snippet).unwrap();
- assert_ir_matches!(
- ir,
- quote! {
- #ir_type {
- ...
- source_loc: #expected_source_loc,
- ...
- }
- }
- );
- }
+ quote! {Record { ..., source_loc: #loc, ... } },
+ );
}
#[test]
fn test_source_location() {
- for (ir_type, cc_snippet, expected_source_loc) in [
- (
- quote! {Func},
- "void no_op_func_to_test_source_location();",
- "Generated from: google3/ir_from_cc_virtual_header.h;l=3",
- ),
- (
- quote! {TypeAlias},
- r#"
-typedef float SomeTypedefToTestSourceLocation;"#,
- "Generated from: google3/ir_from_cc_virtual_header.h;l=4",
- ),
- (
- quote! {UnsupportedItem},
- r#" template <typename T> void unsupported_templated_func_to_test_source_location() {}"#,
- "Generated from: google3/ir_from_cc_virtual_header.h;l=3",
- ),
- (
- quote! {Enum},
- r#"enum SomeEmptyEnumToTestSourceLocation {};"#,
- "Generated from: google3/ir_from_cc_virtual_header.h;l=3",
- ),
- (
- quote! {Record},
- r#"struct SomeEmptyStructToTestSourceLocation {};"#,
- "Generated from: google3/ir_from_cc_virtual_header.h;l=3",
- ),
- ] {
+ let assert_matches = |cc_snippet: &str, expected: proc_macro2::TokenStream| {
let ir = ir_from_cc(cc_snippet).unwrap();
- assert_ir_matches!(
- ir,
- quote! {
- #ir_type {
- ...
- source_loc: #expected_source_loc,
- ...
- }
- }
- );
- }
+ assert_ir_matches!(ir, expected);
+ };
+ assert_matches(
+ "void no_op_func_to_test_source_location();",
+ quote! {Func { ..., source_loc: "Generated from: google3/ir_from_cc_virtual_header.h;l=3", ... } },
+ );
+ assert_matches(
+ r#"typedef float SomeTypedefToTestSourceLocation;"#,
+ quote! {TypeAlias { ..., source_loc: "Generated from: google3/ir_from_cc_virtual_header.h;l=3", ... } },
+ );
+ assert_matches(
+ r#" template <typename T> void unsupported_templated_func_to_test_source_location() {}"#,
+ quote! {UnsupportedItem { ..., source_loc: Some("Generated from: google3/ir_from_cc_virtual_header.h;l=3"), ... } },
+ );
+ assert_matches(
+ r#"enum SomeEmptyEnumToTestSourceLocation {};"#,
+ quote! {Enum { ..., source_loc: "Generated from: google3/ir_from_cc_virtual_header.h;l=3", ... } },
+ );
+ assert_matches(
+ r#"struct SomeEmptyStructToTestSourceLocation {};"#,
+ quote! {Record { ..., source_loc: "Generated from: google3/ir_from_cc_virtual_header.h;l=3", ... } },
+ );
}
#[test]
diff --git a/rs_bindings_from_cc/src_code_gen.rs b/rs_bindings_from_cc/src_code_gen.rs
index d4836dd..eb926bc 100644
--- a/rs_bindings_from_cc/src_code_gen.rs
+++ b/rs_bindings_from_cc/src_code_gen.rs
@@ -345,47 +345,6 @@
function_path: syn::Path,
}
-/// Returns the name of `func` in C++ syntax.
-fn cxx_function_name(func: &Func, ir: &IR) -> String {
- let record: Option<&str> = ir.record_for_member_func(func).map(|r| r.cc_name.as_ref());
-
- let func_name = match &func.name {
- UnqualifiedIdentifier::Identifier(id) => id.identifier.to_string(),
- UnqualifiedIdentifier::Operator(op) => op.cc_name(),
- UnqualifiedIdentifier::Destructor => {
- format!("~{}", record.expect("destructor must be associated with a record"))
- }
- UnqualifiedIdentifier::Constructor => {
- record.expect("constructor must be associated with a record").to_string()
- }
- };
-
- if let Some(record_name) = record {
- format!("{}::{}", record_name, func_name)
- } else {
- func_name
- }
-}
-
-fn make_unsupported_fn(func: &Func, ir: &IR, message: &str) -> Result<UnsupportedItem> {
- Ok(UnsupportedItem::new_with_message(
- cxx_function_name(func, ir).as_ref(),
- message,
- func.source_loc.clone(),
- func.id,
- ))
-}
-
-fn make_unsupported_nested_type_alias(type_alias: &TypeAlias) -> Result<UnsupportedItem> {
- Ok(UnsupportedItem::new_with_message(
- // TODO(jeanpierreda): It would be nice to include the enclosing record name here too.
- type_alias.identifier.identifier.as_ref(),
- "Typedefs nested in classes are not supported yet",
- type_alias.source_loc.clone(),
- type_alias.id,
- ))
-}
-
/// The name of a one-function trait, with extra entries for
/// specially-understood traits and families of traits.
#[derive(Clone, Debug, Eq, PartialEq)]
@@ -2606,16 +2565,17 @@
) -> Result<GeneratedItem> {
errors.insert(item.cause());
+ let source_loc = item.source_loc();
+ let source_loc = match &source_loc {
+ Some(loc) if generate_source_loc_doc_comment == SourceLocationDocComment::Enabled => {
+ loc.as_ref()
+ }
+ _ => "",
+ };
+
let message = format!(
- "{}{}Error while generating bindings for item '{}':\n{}",
- match generate_source_loc_doc_comment {
- SourceLocationDocComment::Enabled => item.source_loc.as_ref(),
- SourceLocationDocComment::Disabled => "",
- },
- match generate_source_loc_doc_comment {
- SourceLocationDocComment::Enabled => "\n",
- SourceLocationDocComment::Disabled => "",
- },
+ "{source_loc}{}Error while generating bindings for item '{}':\n{}",
+ if source_loc.len() > 0 { "\n" } else { "" },
item.name.as_ref(),
item.message()
);
@@ -2796,7 +2756,7 @@
let generated_item = match item {
Item::Func(func) => match db.generate_func(func.clone()) {
Err(e) => generate_unsupported(
- &make_unsupported_fn(func, &ir, format!("{e}").as_str())?,
+ &UnsupportedItem::new_with_message(&ir, func, format!("{e}")),
errors,
db.generate_source_loc_doc_comment(),
)?,
@@ -2804,11 +2764,11 @@
Ok(Some((item, function_id))) => {
if overloaded_funcs.contains(&function_id) {
generate_unsupported(
- &make_unsupported_fn(
- func,
+ &UnsupportedItem::new_with_message(
&ir,
+ func,
"Cannot generate bindings for overloaded function",
- )?,
+ ),
errors,
db.generate_source_loc_doc_comment(),
)?
@@ -2824,7 +2784,11 @@
if type_alias.enclosing_record_id.is_some() {
// TODO(b/200067824): support nested type aliases.
generate_unsupported(
- &make_unsupported_nested_type_alias(type_alias)?,
+ &UnsupportedItem::new_with_message(
+ &ir,
+ type_alias,
+ "Typedefs nested in classes are not supported yet",
+ ),
errors,
db.generate_source_loc_doc_comment(),
)?
@@ -8742,41 +8706,70 @@
assert_rs_matches!(actual, quote! {#[doc = " Some doc comment"]});
}
- #[test]
- fn test_generate_unsupported_item_with_source_loc_enabled() {
- let unsupported_item = UnsupportedItem::new_with_message(
- "unsupported_item",
- "unsupported_message",
- "Generated from: google3/some/header;l=1".into(),
- ItemId::new_for_testing(123),
- );
- let actual = generate_unsupported(
- &unsupported_item,
- &mut ErrorReport::new(),
- SourceLocationDocComment::Enabled,
- )
- .unwrap();
- let expected = "Generated from: google3/some/header;l=1\nError while generating bindings for item 'unsupported_item':\nunsupported_message";
- assert_rs_matches!(actual.item, quote! { __COMMENT__ #expected});
+ struct TestItem {
+ source_loc: Option<Rc<str>>,
+ }
+ impl ir::GenericItem for TestItem {
+ fn id(&self) -> ItemId {
+ ItemId::new_for_testing(123)
+ }
+ fn debug_name(&self, _: &IR) -> Rc<str> {
+ "test_item".into()
+ }
+ fn source_loc(&self) -> Option<Rc<str>> {
+ self.source_loc.clone()
+ }
}
#[test]
- fn test_generate_unsupported_item_with_source_loc_disabled() {
- let unsupported_item = UnsupportedItem::new_with_message(
- "unsupported_item2",
- "unsupported_message2",
- "Generated from: google3/some/header;l=2".into(),
- ItemId::new_for_testing(1234),
- );
+ fn test_generate_unsupported_item_with_source_loc_enabled() -> Result<()> {
let actual = generate_unsupported(
- &unsupported_item,
+ &UnsupportedItem::new_with_message(
+ &make_ir_from_items([])?,
+ &TestItem {source_loc: Some("Generated from: google3/some/header;l=1".into())},
+ "unsupported_message",
+ ),
+ &mut ErrorReport::new(),
+ SourceLocationDocComment::Enabled,
+ )?;
+ let expected = "Generated from: google3/some/header;l=1\nError while generating bindings for item 'test_item':\nunsupported_message";
+ assert_rs_matches!(actual.item, quote! { __COMMENT__ #expected});
+ Ok(())
+ }
+
+ /// Not all items currently have source_loc(), e.g. comments.
+ ///
+ /// For these, we omit the mention of the location.
+ #[test]
+ fn test_generate_unsupported_item_with_missing_source_loc() -> Result<()> {
+ let actual = generate_unsupported(
+ &UnsupportedItem::new_with_message(
+ &make_ir_from_items([])?,
+ &TestItem {source_loc: None},
+ "unsupported_message",
+ ),
+ &mut ErrorReport::new(),
+ SourceLocationDocComment::Enabled,
+ )?;
+ let expected = "Error while generating bindings for item 'test_item':\nunsupported_message";
+ assert_rs_matches!(actual.item, quote! { __COMMENT__ #expected});
+ Ok(())
+ }
+
+ #[test]
+ fn test_generate_unsupported_item_with_source_loc_disabled() -> Result<()> {
+ let actual = generate_unsupported(
+ &UnsupportedItem::new_with_message(
+ &make_ir_from_items([])?,
+ &TestItem {source_loc: Some("Generated from: google3/some/header;l=1".into())},
+ "unsupported_message",
+ ),
&mut ErrorReport::new(),
SourceLocationDocComment::Disabled,
- )
- .unwrap();
- let expected =
- "Error while generating bindings for item 'unsupported_item2':\nunsupported_message2";
+ )?;
+ let expected = "Error while generating bindings for item 'test_item':\nunsupported_message";
assert_rs_matches!(actual.item, quote! { __COMMENT__ #expected});
+ Ok(())
}
/// The default crubit feature set currently results in no bindings at all.