When converting types, don't desugar typedefs for builtin types.
This has required changing the treatment of the typedefs from <cstdint> for
fixed-width and pointer-sized integer types, as we want to treat these
differently from other typedefs.
See comments on the new test for details.
PiperOrigin-RevId: 419796164
diff --git a/rs_bindings_from_cc/ast_visitor.cc b/rs_bindings_from_cc/ast_visitor.cc
index f8e5b06..fa1f363 100644
--- a/rs_bindings_from_cc/ast_visitor.cc
+++ b/rs_bindings_from_cc/ast_visitor.cc
@@ -38,6 +38,7 @@
#include "third_party/llvm/llvm-project/clang/include/clang/Basic/Specifiers.h"
#include "third_party/llvm/llvm-project/clang/include/clang/Sema/Sema.h"
#include "third_party/llvm/llvm-project/llvm/include/llvm/Support/Casting.h"
+#include "util/gtl/flat_map.h"
namespace rs_bindings_from_cc {
@@ -420,12 +421,38 @@
clang::QualType qual_type,
std::optional<devtools_rust::TypeLifetimes> lifetimes,
bool nullable) const {
+ // A mapping of C++ standard types to their equivalent Rust types.
+ // To produce more idiomatic results, these types receive special handling
+ // instead of using the generic type mapping mechanism.
+ static constexpr auto well_known_types =
+ gtl::fixed_flat_map_of<absl::string_view, absl::string_view>({
+ {"ptrdiff_t", "isize"},
+ {"intptr_t", "isize"},
+ {"size_t", "usize"},
+ {"uintptr_t", "usize"},
+ {"int8_t", "i8"},
+ {"uint8_t", "u8"},
+ {"int16_t", "i16"},
+ {"uint16_t", "u16"},
+ {"int32_t", "i32"},
+ {"uint32_t", "u32"},
+ {"int64_t", "i64"},
+ {"uint64_t", "u64"},
+ {"char16_t", "u16"},
+ {"char32_t", "u32"},
+ {"wchar_t", "i32"},
+ });
+
std::optional<MappedType> type = std::nullopt;
// When converting the type to a string, don't include qualifiers -- we handle
// these separately.
std::string type_string = qual_type.getUnqualifiedType().getAsString();
- if (const auto* pointer_type = qual_type->getAs<clang::PointerType>()) {
+ if (auto iter = well_known_types.find(type_string);
+ iter != well_known_types.end()) {
+ type = MappedType::Simple(std::string(iter->second), type_string);
+ } else if (const auto* pointer_type =
+ qual_type->getAs<clang::PointerType>()) {
std::optional<LifetimeId> lifetime;
if (lifetimes.has_value()) {
CHECK(!lifetimes->empty());
@@ -450,7 +477,9 @@
type = MappedType::LValueReferenceTo(*pointee_type, lifetime);
}
} else if (const auto* builtin_type =
- qual_type->getAs<clang::BuiltinType>()) {
+ // Use getAsAdjusted instead of getAs so we don't desugar
+ // typedefs.
+ qual_type->getAsAdjusted<clang::BuiltinType>()) {
switch (builtin_type->getKind()) {
case clang::BuiltinType::Bool:
type = MappedType::Simple("bool", "bool");
@@ -467,13 +496,7 @@
default:
if (builtin_type->isIntegerType()) {
auto size = ctx_->getTypeSize(builtin_type);
- if (size == 64 &&
- (type_string == "ptrdiff_t" || type_string == "intptr_t")) {
- type = MappedType::Simple("isize", type_string);
- } else if (size == 64 &&
- (type_string == "size_t" || type_string == "uintptr_t")) {
- type = MappedType::Simple("usize", type_string);
- } else if (size == 8 || size == 16 || size == 32 || size == 64) {
+ if (size == 8 || size == 16 || size == 32 || size == 64) {
type = MappedType::Simple(
absl::Substitute(
"$0$1", builtin_type->isSignedInteger() ? 'i' : 'u', size),
diff --git a/rs_bindings_from_cc/ir_from_cc_test.rs b/rs_bindings_from_cc/ir_from_cc_test.rs
index 0d7c566..fd62881 100644
--- a/rs_bindings_from_cc/ir_from_cc_test.rs
+++ b/rs_bindings_from_cc/ir_from_cc_test.rs
@@ -432,6 +432,27 @@
}
#[test]
+fn test_integer_typedef_usage() -> Result<()> {
+ // This is a regression test. We used to incorrectly desugar typedefs of
+ // builtin types and treat them as if they were the underlying builtin type.
+ // As a result, this test would produce a binding for f(MyTypedef) (with an
+ // `int` parameter), even though we currently don't support typedefs.
+ // Once we add support for typedefs, this test should be converted to a
+ // code generation test that verifies that the binding for f() has a
+ // parameter of type `MyTypedef`.
+ let ir = ir_from_cc(
+ r#"
+ typedef int MyTypedef;
+ void f(MyTypedef my_typedef);
+ "#,
+ )?;
+ let unsupported = ir.unsupported_items().map(|i| i.name.as_str()).collect_vec();
+ assert_strings_contain(&unsupported, "f");
+
+ Ok(())
+}
+
+#[test]
fn test_struct_forward_declaration() {
let ir = ir_from_cc("struct Struct;").unwrap();
assert!(!ir.records().any(|r| r.identifier.identifier == "Struct"));