Assert that `format_cc_ident` doesn't return reserved C++ keywords.
PiperOrigin-RevId: 479324790
diff --git a/common/BUILD b/common/BUILD
index 7da3f34..667cb93 100644
--- a/common/BUILD
+++ b/common/BUILD
@@ -27,6 +27,7 @@
name = "code_gen_utils",
srcs = ["code_gen_utils.rs"],
deps = [
+ "@crate_index//:once_cell",
"@crate_index//:proc-macro2",
],
)
diff --git a/common/code_gen_utils.rs b/common/code_gen_utils.rs
index c1161bf..8f05d74 100644
--- a/common/code_gen_utils.rs
+++ b/common/code_gen_utils.rs
@@ -2,7 +2,9 @@
// Exceptions. See /LICENSE for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+use once_cell::sync::Lazy;
use proc_macro2::TokenStream;
+use std::collections::HashSet;
// TODO(lukasza): Consider adding more items into `code_gen_utils` (this crate).
// For example, the following items from `src_code_gen.rs` will be most likely
@@ -10,11 +12,125 @@
// - `make_rs_ident`
// - `NamespaceQualifier`
-/// Formats a C++ identifier. Does not escape C++ keywords.
+/// Formats a C++ identifier. Panics when `ident` is a C++ reserved keyword.
pub fn format_cc_ident(ident: &str) -> TokenStream {
+ // C++ doesn't have an equivalent of
+ // https://doc.rust-lang.org/rust-by-example/compatibility/raw_identifiers.html and therefore
+ // we panic when `ident` is a C++ reserved keyword.
+ assert!(
+ !RESERVED_CC_KEYWORDS.contains(ident),
+ "The following reserved keyword can't be used as a C++ identifier: {}",
+ ident
+ );
+
ident.parse().unwrap()
}
+static RESERVED_CC_KEYWORDS: Lazy<HashSet<&'static str>> = Lazy::new(|| {
+ // `RESERVED_CC_KEYWORDS` are based on https://en.cppreference.com/w/cpp/keyword
+ [
+ "alignas",
+ "alignof",
+ "and",
+ "and_eq",
+ "asm",
+ "atomic_cancel",
+ "atomic_commit",
+ "atomic_noexcept",
+ "auto",
+ "bitand",
+ "bitor",
+ "bool",
+ "break",
+ "case",
+ "catch",
+ "char",
+ "char8_t",
+ "char16_t",
+ "char32_t",
+ "class",
+ "compl",
+ "concept",
+ "const",
+ "consteval",
+ "constexpr",
+ "constinit",
+ "const_cast",
+ "continue",
+ "co_await",
+ "co_return",
+ "co_yield",
+ "decltype",
+ "default",
+ "delete",
+ "do",
+ "double",
+ "dynamic_cast",
+ "else",
+ "enum",
+ "explicit",
+ "export",
+ "extern",
+ "false",
+ "float",
+ "for",
+ "friend",
+ "goto",
+ "if",
+ "inline",
+ "int",
+ "long",
+ "mutable",
+ "namespace",
+ "new",
+ "noexcept",
+ "not",
+ "not_eq",
+ "nullptr",
+ "operator",
+ "or",
+ "or_eq",
+ "private",
+ "protected",
+ "public",
+ "reflexpr",
+ "register",
+ "reinterpret_cast",
+ "requires",
+ "return",
+ "short",
+ "signed",
+ "sizeof",
+ "static",
+ "static_assert",
+ "static_cast",
+ "struct",
+ "switch",
+ "synchronized",
+ "template",
+ "this",
+ "thread_local",
+ "throw",
+ "true",
+ "try",
+ "typedef",
+ "typeid",
+ "typename",
+ "union",
+ "unsigned",
+ "using",
+ "virtual",
+ "void",
+ "volatile",
+ "wchar_t",
+ "while",
+ "xor",
+ "xor_eq",
+ ]
+ .into_iter()
+ .collect()
+});
+
#[cfg(test)]
pub mod tests {
use super::*;
@@ -32,7 +148,8 @@
}
#[test]
+ #[should_panic(expected = "can't be used as a C++ identifier: reinterpret_cast")]
fn test_format_cc_ident_reserved_cc_keyword() {
- assert_cc_matches!(format_cc_ident("int"), quote! { int });
+ format_cc_ident("reinterpret_cast");
}
}
diff --git a/rs_bindings_from_cc/src_code_gen.rs b/rs_bindings_from_cc/src_code_gen.rs
index fb47acd..faea7e1 100644
--- a/rs_bindings_from_cc/src_code_gen.rs
+++ b/rs_bindings_from_cc/src_code_gen.rs
@@ -3136,8 +3136,11 @@
if !ty.type_args.is_empty() {
bail!("Type not yet supported: {:?}", ty);
}
- let idents = cc_type_name.split_whitespace().map(format_cc_ident);
- Ok(quote! {#( #idents )* #const_fragment})
+ // Not using `code_gen_utils::format_cc_ident`, because
+ // `cc_type_name` may be a C++ reserved keyword (e.g.
+ // `int`).
+ let cc_ident: TokenStream = cc_type_name.parse().unwrap();
+ Ok(quote!{ #cc_ident #const_fragment })
}
Some(abi) => match ty.type_args.split_last() {
None => bail!("funcValue type without a return type: {:?}", ty),