Handling of identifiers that can't be escaped in Rust.

This CL special-cases how Crubit handles C++ identifiers that can't
_really_ be escape using Rust's `r#foo` syntax.  There are 4 such
identifiers: `self`, `super`, `crate`, and `Self`.

The CL detects the 4 special identifiers in C++ (in
`Importer::GetTranslatedName`) rather than in Rust.  This implementation
choice has been made because:
- We already handle some naming choices on C++ side (detecting empty
  parameter names, detecting template parameter packs, injecting names
  like `__param_3`, detecting type aliases of anonymous structs).
- Some of the naming scenarios are easier to handle on C++ side because
  of access to full Clang AST information (replicating this information
  via IR is possible but burdensome).
- Moving the existing logic from C++ side to Rust seems difficult:
    - The immutable nature of IR makes it difficult to replace
      identifiers directly in `FuncParam::identifier`.
    - Replacing identifiers on-the-fly is difficult, because identifing
      duplicate parameter names (originating from template parameter
      packs) requires looking at more than a single parameter.

There are arguments for handling the 4 special Rust identifiers on
Rust side (in `src_code_gen.rs`), but they seem less important than
the items above.  These arguments are:
- Some names are already handled on Rust side - e.g. anonymous members
  result in field names like `__unnamed_field3`.
- It seems natural to handle Rust quirks on Rust side and
  `make_rs_ident` seems like a natural place for this.
- Most of the source code generation happens in `src_code_gen.rs`.

PiperOrigin-RevId: 511919489
diff --git a/rs_bindings_from_cc/decl_importer.h b/rs_bindings_from_cc/decl_importer.h
index 3d075d3..f196e57 100644
--- a/rs_bindings_from_cc/decl_importer.h
+++ b/rs_bindings_from_cc/decl_importer.h
@@ -116,12 +116,13 @@
   // If the decl is a special member function or operator overload, this returns
   // a SpecialName.
   //
-  // If the translated name is not yet implemented, this returns null.
-  virtual std::optional<UnqualifiedIdentifier> GetTranslatedName(
+  // If the name can't be translated (or is empty), this returns an error.
+  virtual absl::StatusOr<UnqualifiedIdentifier> GetTranslatedName(
       const clang::NamedDecl* named_decl) const = 0;
 
   // GetTranslatedName, but only for identifier names. This is the common case.
-  virtual std::optional<Identifier> GetTranslatedIdentifier(
+  // If the name can't be translated (or is empty), this returns an error.
+  virtual absl::StatusOr<Identifier> GetTranslatedIdentifier(
       const clang::NamedDecl* named_decl) const = 0;
 
   // Gets the doc comment of the declaration.
diff --git a/rs_bindings_from_cc/importer.cc b/rs_bindings_from_cc/importer.cc
index 397fe2c..4e84328 100644
--- a/rs_bindings_from_cc/importer.cc
+++ b/rs_bindings_from_cc/importer.cc
@@ -1017,35 +1017,24 @@
   }
 }
 
-std::optional<UnqualifiedIdentifier> Importer::GetTranslatedName(
+absl::StatusOr<UnqualifiedIdentifier> Importer::GetTranslatedName(
     const clang::NamedDecl* named_decl) const {
   switch (named_decl->getDeclName().getNameKind()) {
     case clang::DeclarationName::Identifier: {
       auto name = std::string(named_decl->getName());
-      if (const clang::ParmVarDecl* param_decl =
-              clang::dyn_cast<clang::ParmVarDecl>(named_decl)) {
-        int param_pos = param_decl->getFunctionScopeIndex();
-        if (name.empty()) {
-          return {Identifier(absl::StrCat("__param_", param_pos))};
-        }
-        if (auto* sttpt = param_decl->getType()
-                              ->getAs<clang::SubstTemplateTypeParmType>();
-            sttpt && sttpt->getReplacedParameter()->isParameterPack()) {
-          // Avoid giving the same name to all parameters expanded from a pack.
-          return {Identifier(absl::StrCat("__", name, "_", param_pos))};
-        }
-      }
       if (name.empty()) {
-        if (auto* tag_type = llvm::dyn_cast<clang::TagDecl>(named_decl)) {
-          if (auto* typedef_decl = tag_type->getTypedefNameForAnonDecl()) {
-            std::optional<Identifier> identifier =
-                GetTranslatedIdentifier(typedef_decl);
-            CHECK(identifier.has_value());  // This must always hold.
-            return {*std::move(identifier)};
-          }
-        }
-        return std::nullopt;
+        return absl::InvalidArgumentError("Missing identifier");
       }
+
+      // `r#foo` syntax in Rust can't be used to escape `crate`, `self`,
+      // `super`, not `Self` identifiers - see
+      // https://doc.rust-lang.org/reference/identifiers.html#identifiers
+      if (name == "crate" || name == "self" || name == "super" ||
+          name == "Self") {
+        return absl::InvalidArgumentError(
+            absl::StrCat("Unescapable identifier: ", name));
+      }
+
       return {Identifier(std::move(name))};
     }
     case clang::DeclarationName::CXXConstructorName:
@@ -1056,10 +1045,8 @@
       switch (named_decl->getDeclName().getCXXOverloadedOperator()) {
         case clang::OO_None:
           LOG(FATAL) << "No OO_None expected under CXXOperatorName branch";
-          return std::nullopt;
         case clang::NUM_OVERLOADED_OPERATORS:
           LOG(FATAL) << "No NUM_OVERLOADED_OPERATORS expected at runtime";
-          return std::nullopt;
           // clang-format off
         #define OVERLOADED_OPERATOR(name, spelling, ...)  \
         case clang::OO_##name: {                          \
@@ -1070,13 +1057,12 @@
           // clang-format on
       }
       LOG(FATAL) << "The `switch` above should handle all cases";
-      return std::nullopt;
     default:
       // To be implemented later: CXXConversionFunctionName.
       // There are also e.g. literal operators, deduction guides, etc., but
       // we might not need to implement them at all. Full list at:
       // https://clang.llvm.org/doxygen/classclang_1_1DeclarationName.html#a9ab322d434446b43379d39e41af5cbe3
-      return std::nullopt;
+      return absl::UnimplementedError("Unsupported name kind");
   }
 }
 
diff --git a/rs_bindings_from_cc/importer.h b/rs_bindings_from_cc/importer.h
index 7f332b0..9ac10fc 100644
--- a/rs_bindings_from_cc/importer.h
+++ b/rs_bindings_from_cc/importer.h
@@ -13,6 +13,7 @@
 #include <vector>
 
 #include "absl/log/die_if_null.h"
+#include "common/status_macros.h"
 #include "rs_bindings_from_cc/decl_importer.h"
 #include "rs_bindings_from_cc/importers/class_template.h"
 #include "rs_bindings_from_cc/importers/cxx_record.h"
@@ -66,15 +67,15 @@
   std::string GetMangledName(const clang::NamedDecl* named_decl) const override;
   BazelLabel GetOwningTarget(const clang::Decl* decl) const override;
   bool IsFromCurrentTarget(const clang::Decl* decl) const override;
-  std::optional<UnqualifiedIdentifier> GetTranslatedName(
+  absl::StatusOr<UnqualifiedIdentifier> GetTranslatedName(
       const clang::NamedDecl* named_decl) const override;
-  std::optional<Identifier> GetTranslatedIdentifier(
+  absl::StatusOr<Identifier> GetTranslatedIdentifier(
       const clang::NamedDecl* named_decl) const override {
-    if (std::optional<UnqualifiedIdentifier> name =
-            GetTranslatedName(named_decl)) {
-      return std::move(*std::get_if<Identifier>(&*name));
-    }
-    return std::nullopt;
+    CRUBIT_ASSIGN_OR_RETURN(UnqualifiedIdentifier unqualified,
+                            GetTranslatedName(named_decl));
+    Identifier* identifier = std::get_if<Identifier>(&unqualified);
+    CHECK(identifier) << "Incorrectly called with a special name";
+    return *identifier;
   }
   std::optional<std::string> GetComment(const clang::Decl* decl) const override;
   std::string ConvertSourceLocation(clang::SourceLocation loc) const override;
diff --git a/rs_bindings_from_cc/importers/cxx_record.cc b/rs_bindings_from_cc/importers/cxx_record.cc
index a16cf64..40d0059 100644
--- a/rs_bindings_from_cc/importers/cxx_record.cc
+++ b/rs_bindings_from_cc/importers/cxx_record.cc
@@ -106,6 +106,27 @@
 
 }  // namespace
 
+std::optional<Identifier> CXXRecordDeclImporter::GetTranslatedFieldName(
+    const clang::FieldDecl* field_decl) {
+  if (field_decl->getName().empty()) {
+    CHECK(!field_decl->hasAttr<clang::NoUniqueAddressAttr>() &&
+          "Unnamed fields can't be annotated with [[no_unique_address]]");
+    // We don't just conjure an artificial name for an unnamed field, because
+    // in the future such fields may be elided entirely - see unnamed members
+    // in:
+    // - https://en.cppreference.com/w/c/language/struct
+    // - https://rust-lang.github.io/rfcs/2102-unnamed-fields.html
+    return std::nullopt;
+  }
+
+  absl::StatusOr<Identifier> name = ictx_.GetTranslatedIdentifier(field_decl);
+  if (!name.ok()) {
+    unsigned field_pos = field_decl->getFieldIndex();
+    return {Identifier(absl::StrCat("__field_", field_pos))};
+  }
+  return *name;
+}
+
 std::optional<IR::Item> CXXRecordDeclImporter::Import(
     clang::CXXRecordDecl* record_decl) {
   const clang::DeclContext* decl_context = record_decl->getDeclContext();
@@ -200,14 +221,28 @@
     }
     source_loc = specialization_decl->getBeginLoc();
   } else {
-    std::optional<Identifier> record_name =
-        ictx_.GetTranslatedIdentifier(record_decl);
-    if (!record_name.has_value()) {
-      return std::nullopt;
+    const clang::NamedDecl* named_decl = record_decl;
+    if (record_decl->getName().empty()) {
+      if (auto* typedef_decl = record_decl->getTypedefNameForAnonDecl()) {
+        named_decl = typedef_decl;
+      } else {
+        // Skip anonymous structs that don't get a name via typedecl.
+        return std::nullopt;
+      }
     }
-    rs_name = cc_name = record_name->Ident();
-    doc_comment = ictx_.GetComment(record_decl);
-    source_loc = record_decl->getBeginLoc();
+    CHECK(!named_decl->getName().empty());
+
+    absl::StatusOr<Identifier> record_name =
+        ictx_.GetTranslatedIdentifier(named_decl);
+    if (record_name.ok()) {
+      rs_name = cc_name = record_name->Ident();
+      doc_comment = ictx_.GetComment(record_decl);
+      source_loc = record_decl->getBeginLoc();
+    } else {
+      return ictx_.ImportUnsupportedItem(
+          record_decl, absl::StrCat("Record name is not supported: ",
+                                    record_name.status().message()));
+    }
   }
 
   if (clang::CXXRecordDecl* complete = record_decl->getDefinition()) {
@@ -360,14 +395,8 @@
       }
     }
 
-    std::optional<Identifier> field_name =
-        ictx_.GetTranslatedIdentifier(field_decl);
-    CHECK(field_name ||
-          !field_decl->hasAttr<clang::NoUniqueAddressAttr>() &&
-              "Unnamed fields can't be annotated with [[no_unique_address]]");
     fields.push_back(
-        {.identifier = field_name ? *std::move(field_name)
-                                  : std::optional<Identifier>(std::nullopt),
+        {.identifier = GetTranslatedFieldName(field_decl),
          .doc_comment = ictx_.GetComment(field_decl),
          .type = std::move(type),
          .access = TranslateAccessSpecifier(access),
diff --git a/rs_bindings_from_cc/importers/cxx_record.h b/rs_bindings_from_cc/importers/cxx_record.h
index c798743..852d6be 100644
--- a/rs_bindings_from_cc/importers/cxx_record.h
+++ b/rs_bindings_from_cc/importers/cxx_record.h
@@ -18,6 +18,8 @@
   std::vector<Field> ImportFields(clang::CXXRecordDecl*);
   std::vector<BaseClass> GetUnambiguousPublicBases(
       const clang::CXXRecordDecl& record_decl) const;
+  std::optional<Identifier> GetTranslatedFieldName(
+      const clang::FieldDecl* field);
 };
 
 }  // namespace crubit
diff --git a/rs_bindings_from_cc/importers/enum.cc b/rs_bindings_from_cc/importers/enum.cc
index 498cdc6..f286261 100644
--- a/rs_bindings_from_cc/importers/enum.cc
+++ b/rs_bindings_from_cc/importers/enum.cc
@@ -7,9 +7,7 @@
 namespace crubit {
 
 std::optional<IR::Item> EnumDeclImporter::Import(clang::EnumDecl* enum_decl) {
-  std::optional<Identifier> enum_name =
-      ictx_.GetTranslatedIdentifier(enum_decl);
-  if (!enum_name.has_value()) {
+  if (enum_decl->getName().empty()) {
     // TODO(b/208945197): This corresponds to an unnamed enum declaration like
     // `enum { kFoo = 1 }`, which only exists to provide constants into the
     // surrounding scope and doesn't actually introduce an enum namespace. It
@@ -17,6 +15,13 @@
     return ictx_.ImportUnsupportedItem(enum_decl,
                                        "Unnamed enums are not supported yet");
   }
+  absl::StatusOr<Identifier> enum_name =
+      ictx_.GetTranslatedIdentifier(enum_decl);
+  if (!enum_name.ok()) {
+    return ictx_.ImportUnsupportedItem(
+        enum_decl, absl::StrCat("Enum name is not supported: ",
+                                enum_name.status().message()));
+  }
 
   clang::QualType cc_type = enum_decl->getIntegerType();
   if (cc_type.isNull()) {
@@ -40,12 +45,13 @@
   enumerators.reserve(std::distance(enum_decl->enumerators().begin(),
                                     enum_decl->enumerators().end()));
   for (clang::EnumConstantDecl* enumerator : enum_decl->enumerators()) {
-    std::optional<Identifier> enumerator_name =
+    absl::StatusOr<Identifier> enumerator_name =
         ictx_.GetTranslatedIdentifier(enumerator);
-    if (!enumerator_name.has_value()) {
+    if (!enumerator_name.ok()) {
       // It's not clear that this case is possible
       return ictx_.ImportUnsupportedItem(
-          enum_decl, "importing enum failed: missing enumerator name");
+          enum_decl, absl::StrCat("Enumerator name is not supported: ",
+                                  enumerator_name.status().message()));
     }
 
     enumerators.push_back(Enumerator{
diff --git a/rs_bindings_from_cc/importers/function.cc b/rs_bindings_from_cc/importers/function.cc
index 15bb7c7..864eace 100644
--- a/rs_bindings_from_cc/importers/function.cc
+++ b/rs_bindings_from_cc/importers/function.cc
@@ -12,6 +12,7 @@
 #include "llvm/ADT/StringRef.h"
 
 namespace crubit {
+
 static bool IsInStdNamespace(const clang::FunctionDecl* decl) {
   const clang::DeclContext* context = decl->getDeclContext();
   while (context) {
@@ -23,6 +24,22 @@
   return false;
 }
 
+Identifier FunctionDeclImporter::GetTranslatedParamName(
+    const clang::ParmVarDecl* param_decl) {
+  int param_pos = param_decl->getFunctionScopeIndex();
+  absl::StatusOr<Identifier> name = ictx_.GetTranslatedIdentifier(param_decl);
+  if (!name.ok()) {
+    return {Identifier(absl::StrCat("__param_", param_pos))};
+  }
+  if (auto* sttpt =
+          param_decl->getType()->getAs<clang::SubstTemplateTypeParmType>();
+      sttpt && sttpt->getReplacedParameter()->isParameterPack()) {
+    // Avoid giving the same name to all parameters expanded from a pack.
+    return {Identifier(absl::StrCat("__", name->Ident(), "_", param_pos))};
+  }
+  return *name;
+}
+
 std::optional<IR::Item> FunctionDeclImporter::Import(
     clang::FunctionDecl* function_decl) {
   if (!ictx_.IsFromCurrentTarget(function_decl)) return std::nullopt;
@@ -76,6 +93,14 @@
           function_decl, *ictx_.invocation_.lifetime_context_,
           &lifetime_symbol_table));
 
+  absl::StatusOr<UnqualifiedIdentifier> translated_name =
+      ictx_.GetTranslatedName(function_decl);
+  if (!translated_name.ok()) {
+    return ictx_.ImportUnsupportedItem(
+        function_decl, absl::StrCat("Function name is not supported: ",
+                                    translated_name.status().message()));
+  }
+
   std::vector<FuncParam> params;
   std::set<std::string> errors;
   auto add_error = [&errors](std::string msg) {
@@ -124,7 +149,7 @@
       continue;
     }
 
-    std::optional<Identifier> param_name = ictx_.GetTranslatedIdentifier(param);
+    std::optional<Identifier> param_name = GetTranslatedParamName(param);
     CHECK(param_name.has_value());  // No known failure cases.
     params.push_back({*param_type, *std::move(param_name)});
   }
@@ -205,8 +230,6 @@
       clang::CC_C;
   bool is_member_or_descendant_of_class_template =
       IsFullClassTemplateSpecializationOrChild(function_decl);
-  std::optional<UnqualifiedIdentifier> translated_name =
-      ictx_.GetTranslatedName(function_decl);
 
   std::optional<std::string> doc_comment = ictx_.GetComment(function_decl);
   if (!doc_comment.has_value() && is_member_or_descendant_of_class_template) {
@@ -233,26 +256,23 @@
   // `!return_type.ok()` and returning early if `!errors.empty()`.
   CHECK(return_type.ok());
 
-  if (translated_name.has_value()) {
-    return Func{
-        .name = *translated_name,
-        .owning_target = ictx_.GetOwningTarget(function_decl),
-        .doc_comment = std::move(doc_comment),
-        .mangled_name = std::move(mangled_name),
-        .return_type = *return_type,
-        .params = std::move(params),
-        .lifetime_params = std::move(lifetime_params),
-        .is_inline = function_decl->isInlined(),
-        .member_func_metadata = std::move(member_func_metadata),
-        .has_c_calling_convention = has_c_calling_convention,
-        .is_member_or_descendant_of_class_template =
-            is_member_or_descendant_of_class_template,
-        .source_loc = ictx_.ConvertSourceLocation(function_decl->getBeginLoc()),
-        .id = GenerateItemId(function_decl),
-        .enclosing_namespace_id = GetEnclosingNamespaceId(function_decl),
-    };
-  }
-  return std::nullopt;
+  return Func{
+      .name = *translated_name,
+      .owning_target = ictx_.GetOwningTarget(function_decl),
+      .doc_comment = std::move(doc_comment),
+      .mangled_name = std::move(mangled_name),
+      .return_type = *return_type,
+      .params = std::move(params),
+      .lifetime_params = std::move(lifetime_params),
+      .is_inline = function_decl->isInlined(),
+      .member_func_metadata = std::move(member_func_metadata),
+      .has_c_calling_convention = has_c_calling_convention,
+      .is_member_or_descendant_of_class_template =
+          is_member_or_descendant_of_class_template,
+      .source_loc = ictx_.ConvertSourceLocation(function_decl->getBeginLoc()),
+      .id = GenerateItemId(function_decl),
+      .enclosing_namespace_id = GetEnclosingNamespaceId(function_decl),
+  };
 }
 
 }  // namespace crubit
diff --git a/rs_bindings_from_cc/importers/function.h b/rs_bindings_from_cc/importers/function.h
index e55c64a..99910a9 100644
--- a/rs_bindings_from_cc/importers/function.h
+++ b/rs_bindings_from_cc/importers/function.h
@@ -14,6 +14,9 @@
  public:
   FunctionDeclImporter(ImportContext& context) : DeclImporterBase(context) {}
   std::optional<IR::Item> Import(clang::FunctionDecl*);
+
+ private:
+  Identifier GetTranslatedParamName(const clang::ParmVarDecl* param_decl);
 };
 
 }  // namespace crubit
diff --git a/rs_bindings_from_cc/importers/namespace.cc b/rs_bindings_from_cc/importers/namespace.cc
index 4f934a7..7a5bd41 100644
--- a/rs_bindings_from_cc/importers/namespace.cc
+++ b/rs_bindings_from_cc/importers/namespace.cc
@@ -11,15 +11,20 @@
 
 std::optional<IR::Item> NamespaceDeclImporter::Import(
     clang::NamespaceDecl* namespace_decl) {
-
   if (namespace_decl->isAnonymousNamespace()) {
     return ictx_.ImportUnsupportedItem(
         namespace_decl, "Anonymous namespaces are not supported yet");
   }
 
+  absl::StatusOr<Identifier> identifier =
+      ictx_.GetTranslatedIdentifier(namespace_decl);
+  if (!identifier.ok()) {
+    return ictx_.ImportUnsupportedItem(
+        namespace_decl, absl::StrCat("Namespace name is not supported: ",
+                                     identifier.status().message()));
+  }
+
   ictx_.ImportDeclsFromDeclContext(namespace_decl);
-  auto identifier = ictx_.GetTranslatedIdentifier(namespace_decl);
-  CHECK(identifier.has_value());
   auto item_ids = ictx_.GetItemIdsInSourceOrder(namespace_decl);
   return Namespace{
       .name = *identifier,
diff --git a/rs_bindings_from_cc/importers/typedef_name.cc b/rs_bindings_from_cc/importers/typedef_name.cc
index f10ab67..da11604 100644
--- a/rs_bindings_from_cc/importers/typedef_name.cc
+++ b/rs_bindings_from_cc/importers/typedef_name.cc
@@ -41,9 +41,13 @@
     return std::nullopt;
   }
 
-  std::optional<Identifier> identifier =
+  absl::StatusOr<Identifier> identifier =
       ictx_.GetTranslatedIdentifier(typedef_name_decl);
-  CHECK(identifier.has_value());  // This must always hold.
+  if (!identifier.ok()) {
+    return ictx_.ImportUnsupportedItem(
+        typedef_name_decl, absl::StrCat("Type alias name is not supported: ",
+                                        identifier.status().message()));
+  }
 
   std::optional<clang::tidy::lifetimes::ValueLifetimes> no_lifetimes;
   absl::StatusOr<MappedType> underlying_type = ictx_.ConvertQualType(
diff --git a/rs_bindings_from_cc/ir_from_cc_test.rs b/rs_bindings_from_cc/ir_from_cc_test.rs
index efdf426..c999a8e 100644
--- a/rs_bindings_from_cc/ir_from_cc_test.rs
+++ b/rs_bindings_from_cc/ir_from_cc_test.rs
@@ -110,6 +110,140 @@
 }
 
 #[test]
+fn test_unescapable_rust_keywords_in_function_parameters() {
+    let ir = ir_from_cc("int f(int self, int crate, int super);").unwrap();
+    assert_ir_matches!(
+        ir,
+        quote! {
+            Func {
+                name: "f", ...
+                params: [
+                    FuncParam {
+                        ... identifier: "__param_0", ...
+                    },
+                    FuncParam {
+                        ... identifier: "__param_1", ...
+                    },
+                    FuncParam {
+                        ... identifier: "__param_2", ...
+                    },
+                ], ...
+            }
+        }
+    );
+}
+
+#[test]
+fn test_unescapable_rust_keywords_in_struct_name() {
+    let ir = ir_from_cc("struct Self{ int field; };").unwrap();
+    assert_ir_matches!(
+        ir,
+        quote! { UnsupportedItem {
+          name: "Self", ...
+          message: "Record name is not supported: Unescapable identifier: Self"
+          ...
+        }}
+    );
+}
+
+#[test]
+fn test_unescapable_rust_keywords_in_enum_name() {
+    let ir = ir_from_cc("enum Self{ kFoo = 1 };").unwrap();
+    assert_ir_matches!(
+        ir,
+        quote! { UnsupportedItem {
+          name: "Self", ...
+          message: "Enum name is not supported: Unescapable identifier: Self"
+          ...
+        }}
+    );
+}
+
+#[test]
+fn test_unescapable_rust_keywords_in_enumerator_name() {
+    let ir = ir_from_cc("enum SomeEnum { self = 1 };").unwrap();
+    assert_ir_matches!(
+        ir,
+        quote! { UnsupportedItem {
+          name: "SomeEnum", ...
+          message: "Enumerator name is not supported: Unescapable identifier: self"
+          ...
+        }}
+    );
+}
+
+#[test]
+fn test_unescapable_rust_keywords_in_anonymous_struct_type_alias() {
+    let ir = ir_from_cc("typedef struct { int field; } Self;").unwrap();
+    assert_ir_matches!(
+        ir,
+        quote! { UnsupportedItem {
+          name: "Self", ...
+          message: "Record name is not supported: Unescapable identifier: Self"
+          ...
+        }}
+    );
+}
+
+#[test]
+fn test_unescapable_rust_keywords_in_field_name() {
+    let ir = ir_from_cc("struct SomeStruct { int self; };").unwrap();
+    assert_ir_matches!(
+        ir,
+        quote! {
+           Record {
+               rs_name: "SomeStruct",
+               cc_name: "SomeStruct",
+               ...
+               fields: [Field {
+                   identifier: Some("__field_0"), ...
+               }],
+               ...
+           }
+        }
+    );
+}
+
+#[test]
+fn test_unescapable_rust_keywords_in_namespace_name() {
+    let ir = ir_from_cc("namespace self { void foo(); }").unwrap();
+    assert_ir_matches!(
+        ir,
+        quote! { UnsupportedItem {
+          name: "self", ...
+          message: "Namespace name is not supported: Unescapable identifier: self"
+          ...
+        }}
+    );
+}
+
+#[test]
+fn test_unescapable_rust_keywords_in_function_name() {
+    let ir = ir_from_cc("void self();").unwrap();
+    assert_ir_matches!(
+        ir,
+        quote! { UnsupportedItem {
+          name: "self", ...
+          message: "Function name is not supported: Unescapable identifier: self"
+          ...
+        }}
+    );
+}
+
+#[test]
+fn test_unescapable_rust_keywords_in_type_alias_name() {
+    let ir = ir_from_cc("using Self = int;").unwrap();
+    assert_ir_matches!(
+        ir,
+        quote! { UnsupportedItem {
+          name: "Self", ...
+          message: "Type alias name is not supported: Unescapable identifier: Self"
+          ...
+        }}
+    );
+}
+
+#[test]
 fn test_function_with_custom_calling_convention() {
     let ir = ir_from_cc("int f_vectorcall(int, int) [[clang::vectorcall]];").unwrap();
     assert_ir_matches!(
diff --git a/rs_bindings_from_cc/test/function/simple/simple_functions.cc b/rs_bindings_from_cc/test/function/simple/simple_functions.cc
index fa7c6ff..e339786 100644
--- a/rs_bindings_from_cc/test/function/simple/simple_functions.cc
+++ b/rs_bindings_from_cc/test/function/simple/simple_functions.cc
@@ -32,5 +32,9 @@
 
 int multiply_with_unnamed_parameters(int x, int y) { return x * y; }
 
+int multiply_with_keyword_named_parameters(int self, int crate, int super) {
+  return self * crate * super;
+}
+
 int (*get_pointer_to_multiply_function())(int, int) { return multiply; }
 int (&get_reference_to_multiply_function())(int, int) { return multiply; }
diff --git a/rs_bindings_from_cc/test/function/simple/simple_functions.h b/rs_bindings_from_cc/test/function/simple/simple_functions.h
index 3c88c92..ffc6808 100644
--- a/rs_bindings_from_cc/test/function/simple/simple_functions.h
+++ b/rs_bindings_from_cc/test/function/simple/simple_functions.h
@@ -16,6 +16,7 @@
 const int& forward_reference(const int& i);
 int multiply(int x, int y);
 int multiply_with_unnamed_parameters(int, int);
+int multiply_with_keyword_named_parameters(int self, int crate, int super);
 
 // https://cdecl.org/?q=int+%28*get_multiply_function%28%29%29%28int%2C+int%29:
 // declare foo as function returning pointer to function (int, int) returning
diff --git a/rs_bindings_from_cc/test/function/simple/test.rs b/rs_bindings_from_cc/test/function/simple/test.rs
index 04ee8cf..ad95fc6 100644
--- a/rs_bindings_from_cc/test/function/simple/test.rs
+++ b/rs_bindings_from_cc/test/function/simple/test.rs
@@ -69,6 +69,14 @@
     }
 
     #[test]
+    fn test_multiply_with_keyword_named_parameters() {
+        assert_eq!(
+            42 * 123 * 456,
+            simple_functions::multiply_with_keyword_named_parameters(42, 123, 456)
+        );
+    }
+
+    #[test]
     fn test_function_pointer() {
         let maybe_mul_fn = simple_functions::get_pointer_to_multiply_function();
         let mul_fn = maybe_mul_fn.expect("Expecting non-null / non-None function pointer");