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