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