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"));