Move `make_rs_ident` to the `common/code_gen_utils` crate.
PiperOrigin-RevId: 488667812
diff --git a/common/BUILD b/common/BUILD
index eeab30b..d9a0a35 100644
--- a/common/BUILD
+++ b/common/BUILD
@@ -31,6 +31,7 @@
"@crate_index//:once_cell",
"@crate_index//:proc-macro2",
"@crate_index//:quote",
+ "@crate_index//:syn",
],
)
diff --git a/common/code_gen_utils.rs b/common/code_gen_utils.rs
index e441062..2c9d878 100644
--- a/common/code_gen_utils.rs
+++ b/common/code_gen_utils.rs
@@ -4,8 +4,8 @@
use anyhow::{anyhow, ensure, Result};
use once_cell::sync::Lazy;
-use proc_macro2::TokenStream;
-use quote::{quote, ToTokens};
+use proc_macro2::{Ident, TokenStream};
+use quote::{format_ident, quote, ToTokens};
use std::collections::{BTreeSet, HashSet};
use std::rc::Rc;
@@ -15,7 +15,8 @@
// - `make_rs_ident`
// - `NamespaceQualifier`
-/// Formats a C++ identifier. Panics when `ident` is a C++ reserved keyword.
+/// Formats a C++ identifier. Returns an error when `ident` is a C++ reserved
+/// keyword or is an invalid identifier.
pub fn format_cc_ident(ident: &str) -> Result<TokenStream> {
ensure!(!ident.is_empty(), "Empty string is not a valid C++ identifier");
@@ -36,6 +37,20 @@
)
}
+/// Makes an 'Ident' to be used in the Rust source code. Escapes Rust keywords.
+/// Panics if `ident` is empty or is otherwise an invalid identifier.
+pub fn make_rs_ident(ident: &str) -> Ident {
+ // TODO(https://github.com/dtolnay/syn/pull/1098): Remove the hardcoded list once syn recognizes
+ // 2018 and 2021 keywords.
+ if ["async", "await", "try", "dyn"].contains(&ident) {
+ return format_ident!("r#{}", ident);
+ }
+ match syn::parse_str::<syn::Ident>(ident) {
+ Ok(_) => format_ident!("{}", ident),
+ Err(_) => format_ident!("r#{}", ident),
+ }
+}
+
/// `CcInclude` represents a single `#include ...` directive in C++.
#[derive(Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub enum CcInclude {
@@ -212,7 +227,7 @@
pub mod tests {
use super::*;
use quote::quote;
- use token_stream_matchers::assert_cc_matches;
+ use token_stream_matchers::{assert_cc_matches, assert_rs_matches};
use token_stream_printer::cc_tokens_to_formatted_string;
#[test]
@@ -299,6 +314,36 @@
}
#[test]
+ fn test_make_rs_ident_basic() {
+ let id = make_rs_ident("foo");
+ assert_rs_matches!(quote! { #id }, quote! { foo });
+ }
+
+ #[test]
+ fn test_make_rs_ident_reserved_cc_keyword() {
+ let id = make_rs_ident("reinterpret_cast");
+ assert_rs_matches!(quote! { #id }, quote! { reinterpret_cast });
+ }
+
+ #[test]
+ fn test_make_rs_ident_reserved_rust_keyword() {
+ let id = make_rs_ident("impl");
+ assert_rs_matches!(quote! { #id }, quote! { r#impl });
+ }
+
+ #[test]
+ #[should_panic]
+ fn test_make_rs_ident_unfinished_group() {
+ make_rs_ident("(foo"); // No closing `)`.
+ }
+
+ #[test]
+ #[should_panic]
+ fn test_make_rs_ident_empty() {
+ make_rs_ident("");
+ }
+
+ #[test]
fn test_cc_include_to_tokens_for_system_header() {
let include = CcInclude::cstddef();
assert_cc_matches!(
diff --git a/rs_bindings_from_cc/BUILD b/rs_bindings_from_cc/BUILD
index d41dbc0..308bf8b 100644
--- a/rs_bindings_from_cc/BUILD
+++ b/rs_bindings_from_cc/BUILD
@@ -309,7 +309,6 @@
"@crate_index//:quote",
"@crate_index//:serde",
"@crate_index//:serde_json",
- "@crate_index//:syn",
],
)
diff --git a/rs_bindings_from_cc/src_code_gen.rs b/rs_bindings_from_cc/src_code_gen.rs
index b57c20c..f25d295 100644
--- a/rs_bindings_from_cc/src_code_gen.rs
+++ b/rs_bindings_from_cc/src_code_gen.rs
@@ -3,7 +3,7 @@
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
use arc_anyhow::{Context, Result};
-use code_gen_utils::{format_cc_includes, CcInclude};
+use code_gen_utils::{format_cc_includes, make_rs_ident, CcInclude};
use error_report::{anyhow, bail, ensure, ErrorReport, ErrorReporting, IgnoreErrors};
use ffi_types::*;
use ir::*;
@@ -2766,19 +2766,6 @@
})
}
-/// Makes an 'Ident' to be used in the Rust source code. Escapes Rust keywords.
-fn make_rs_ident(ident: &str) -> Ident {
- // TODO(https://github.com/dtolnay/syn/pull/1098): Remove the hardcoded list once syn recognizes
- // 2018 and 2021 keywords.
- if ["async", "await", "try", "dyn"].contains(&ident) {
- return format_ident!("r#{}", ident);
- }
- match syn::parse_str::<syn::Ident>(ident) {
- Ok(_) => format_ident!("{}", ident),
- Err(_) => format_ident!("r#{}", ident),
- }
-}
-
/// Formats a C++ identifier. Panics if `ident` is a C++ reserved keyword.
fn format_cc_ident(ident: &str) -> TokenStream {
code_gen_utils::format_cc_ident(ident).expect("IR should only contain valid C++ identifiers")