use super::super::Transpiler;
use crate::frontend::ast::Expr;
use proc_macro2::TokenStream;
use quote::{format_ident, quote};
impl Transpiler {
pub(in crate::backend::transpiler) fn transpile_identifier(&self, name: &str) -> TokenStream {
if name.contains("::") {
let parts: Vec<&str> = name.split("::").collect();
let mut tokens = Vec::new();
for (i, part) in parts.iter().enumerate() {
if part.starts_with('<') && part.ends_with('>') {
let turbofish_tokens = Self::transpile_turbofish(part);
tokens.push(turbofish_tokens);
} else {
let safe_part = if matches!(*part, "self" | "Self" | "super" | "crate") {
(*part).to_string()
} else if Self::is_rust_reserved_keyword(part) {
format!("r#{part}")
} else {
(*part).to_string()
};
let ident = format_ident!("{}", safe_part);
tokens.push(quote! { #ident });
}
if i < parts.len() - 1 {
tokens.push(quote! { :: });
}
}
quote! { #(#tokens)* }
} else {
let safe_name = if matches!(name, "self" | "Self" | "super" | "crate") {
name.to_string()
} else if Self::is_rust_reserved_keyword(name) {
format!("r#{name}")
} else {
name.to_string()
};
let ident = format_ident!("{}", safe_name);
if self
.global_vars
.read()
.expect(
"RwLock poisoned in transpile_identifier - indicates panic in another thread",
)
.contains(name)
{
quote! { *#ident.lock().expect("LazyLock Mutex poisoned - indicates panic while accessing global variable") }
} else {
quote! { #ident }
}
}
}
pub(in crate::backend::transpiler) fn transpile_turbofish(turbofish: &str) -> TokenStream {
let inner = &turbofish[1..turbofish.len() - 1];
let type_args: Vec<&str> = inner.split(',').map(str::trim).collect();
let type_tokens: Vec<TokenStream> = type_args
.iter()
.map(|type_arg| {
if type_arg.contains("::") {
let ident = format_ident!("{}", type_arg);
quote! { #ident }
} else {
let ident = format_ident!("{}", type_arg);
quote! { #ident }
}
})
.collect();
quote! { < #(#type_tokens),* > }
}
pub(in crate::backend::transpiler) fn transpile_qualified_name(
module: &str,
name: &str,
) -> TokenStream {
let module_parts: Vec<&str> = module.split("::").collect();
let name_ident = format_ident!("{}", name);
if module_parts.len() == 1 {
let module_ident = format_ident!("{}", module_parts[0]);
quote! { #module_ident::#name_ident }
} else {
let mut tokens = TokenStream::new();
for (i, part) in module_parts.iter().enumerate() {
if i > 0 {
tokens.extend(quote! { :: });
}
let part_ident = format_ident!("{}", part);
tokens.extend(quote! { #part_ident });
}
quote! { #tokens::#name_ident }
}
}
pub(in crate::backend::transpiler) fn transpile_external_mod_declaration(
&self,
name: &str,
expr: &Expr,
) -> TokenStream {
let module_ident = format_ident!("{}", name);
let visibility_tokens = expr.attributes.iter().find_map(|attr| {
if attr.name == "pub" {
if attr.args.is_empty() {
Some(quote! { pub })
} else {
let vis_arg = &attr.args[0];
let vis_ident = format_ident!("{}", vis_arg);
Some(quote! { pub(#vis_ident) })
}
} else {
None
}
});
if let Some(vis) = visibility_tokens {
quote! { #vis mod #module_ident ; }
} else {
quote! { mod #module_ident ; }
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::frontend::ast::{Attribute, Expr, ExprKind, Span};
fn test_transpiler() -> Transpiler {
Transpiler::new()
}
#[test]
fn test_transpile_identifier_simple() {
let transpiler = test_transpiler();
let result = transpiler.transpile_identifier("foo");
assert_eq!(result.to_string(), "foo");
}
#[test]
fn test_transpile_identifier_module_path() {
let transpiler = test_transpiler();
let result = transpiler.transpile_identifier("std::collections::HashMap");
assert_eq!(result.to_string(), "std :: collections :: HashMap");
}
#[test]
fn test_transpile_identifier_turbofish() {
let transpiler = test_transpiler();
let result = transpiler.transpile_identifier("Vec::<i32>::new");
let result_str = result.to_string();
assert!(result_str.contains("Vec"));
assert!(result_str.contains("i32"));
assert!(result_str.contains("new"));
}
#[test]
fn test_transpile_identifier_self_keyword() {
let transpiler = test_transpiler();
let result = transpiler.transpile_identifier("self");
assert_eq!(result.to_string(), "self");
}
#[test]
fn test_transpile_identifier_self_type() {
let transpiler = test_transpiler();
let result = transpiler.transpile_identifier("Self");
assert_eq!(result.to_string(), "Self");
}
#[test]
fn test_transpile_identifier_super_keyword() {
let transpiler = test_transpiler();
let result = transpiler.transpile_identifier("super");
assert_eq!(result.to_string(), "super");
}
#[test]
fn test_transpile_identifier_crate_keyword() {
let transpiler = test_transpiler();
let result = transpiler.transpile_identifier("crate");
assert_eq!(result.to_string(), "crate");
}
#[test]
fn test_transpile_identifier_reserved_keyword() {
let transpiler = test_transpiler();
let result = transpiler.transpile_identifier("type");
let result_str = result.to_string();
assert!(result_str.contains("r#") || result_str.contains("type"));
}
#[test]
fn test_transpile_turbofish_single_type() {
let result = Transpiler::transpile_turbofish("<i32>");
assert_eq!(result.to_string(), "< i32 >");
}
#[test]
fn test_transpile_turbofish_multiple_types() {
let result = Transpiler::transpile_turbofish("<String, i32>");
let result_str = result.to_string();
assert!(result_str.contains("String"));
assert!(result_str.contains("i32"));
assert!(result_str.contains(','));
}
#[test]
fn test_transpile_turbofish_with_whitespace() {
let result = Transpiler::transpile_turbofish("< String , usize >");
let result_str = result.to_string();
assert!(result_str.contains("String"));
assert!(result_str.contains("usize"));
}
#[test]
fn test_transpile_qualified_name_simple() {
let result = Transpiler::transpile_qualified_name("math", "add");
assert_eq!(result.to_string(), "math :: add");
}
#[test]
fn test_transpile_qualified_name_nested() {
let result = Transpiler::transpile_qualified_name("net::tcp", "TcpListener");
let result_str = result.to_string();
assert!(result_str.contains("net"));
assert!(result_str.contains("tcp"));
assert!(result_str.contains("TcpListener"));
}
#[test]
fn test_transpile_external_mod_declaration_basic() {
let transpiler = test_transpiler();
let expr = Expr {
kind: ExprKind::Identifier("utils".to_string()),
span: Span::default(),
attributes: vec![],
leading_comments: vec![],
trailing_comment: None,
};
let result = transpiler.transpile_external_mod_declaration("utils", &expr);
assert_eq!(result.to_string(), "mod utils ;");
}
#[test]
fn test_transpile_external_mod_declaration_pub() {
let transpiler = test_transpiler();
let expr = Expr {
kind: ExprKind::Identifier("api".to_string()),
span: Span::default(),
attributes: vec![Attribute {
name: "pub".to_string(),
args: vec![],
span: Span::default(),
}],
leading_comments: vec![],
trailing_comment: None,
};
let result = transpiler.transpile_external_mod_declaration("api", &expr);
assert_eq!(result.to_string(), "pub mod api ;");
}
#[test]
fn test_transpile_external_mod_declaration_pub_crate() {
let transpiler = test_transpiler();
let expr = Expr {
kind: ExprKind::Identifier("internal".to_string()),
span: Span::default(),
attributes: vec![Attribute {
name: "pub".to_string(),
args: vec!["crate".to_string()],
span: Span::default(),
}],
leading_comments: vec![],
trailing_comment: None,
};
let result = transpiler.transpile_external_mod_declaration("internal", &expr);
let result_str = result.to_string();
assert!(result_str.contains("pub"));
assert!(result_str.contains("crate"));
assert!(result_str.contains("internal"));
}
#[test]
fn test_transpile_identifier_path_with_reserved() {
let transpiler = test_transpiler();
let result = transpiler.transpile_identifier("mod::type");
let result_str = result.to_string();
assert!(
result_str.contains("r#") || result_str.contains("mod") || result_str.contains("type")
);
}
#[test]
fn test_transpile_turbofish_three_types() {
let result = Transpiler::transpile_turbofish("<K, V, H>");
let result_str = result.to_string();
assert!(result_str.contains('K'));
assert!(result_str.contains('V'));
assert!(result_str.contains('H'));
}
#[test]
fn test_transpile_identifier_reserved_match() {
let transpiler = test_transpiler();
let result = transpiler.transpile_identifier("match");
let result_str = result.to_string();
assert!(result_str.contains("r#") || result_str.contains("match"));
}
#[test]
fn test_transpile_identifier_reserved_fn() {
let transpiler = test_transpiler();
let result = transpiler.transpile_identifier("fn");
let result_str = result.to_string();
assert!(result_str.contains("r#") || result_str.contains("fn"));
}
#[test]
fn test_transpile_identifier_reserved_impl() {
let transpiler = test_transpiler();
let result = transpiler.transpile_identifier("impl");
let result_str = result.to_string();
assert!(result_str.contains("r#") || result_str.contains("impl"));
}
#[test]
fn test_transpile_identifier_long_path() {
let transpiler = test_transpiler();
let result = transpiler.transpile_identifier("std::collections::hash::map::HashMap");
let result_str = result.to_string();
assert!(result_str.contains("std"));
assert!(result_str.contains("collections"));
assert!(result_str.contains("hash"));
assert!(result_str.contains("map"));
assert!(result_str.contains("HashMap"));
}
#[test]
fn test_transpile_qualified_name_deep_nested() {
let result = Transpiler::transpile_qualified_name("std::io::prelude", "Read");
let result_str = result.to_string();
assert!(result_str.contains("std"));
assert!(result_str.contains("io"));
assert!(result_str.contains("prelude"));
assert!(result_str.contains("Read"));
}
#[test]
fn test_transpile_qualified_name_single_char() {
let result = Transpiler::transpile_qualified_name("a", "b");
assert_eq!(result.to_string(), "a :: b");
}
#[test]
fn test_transpile_external_mod_declaration_pub_super() {
let transpiler = test_transpiler();
let expr = Expr {
kind: ExprKind::Identifier("helpers".to_string()),
span: Span::default(),
attributes: vec![Attribute {
name: "pub".to_string(),
args: vec!["super".to_string()],
span: Span::default(),
}],
leading_comments: vec![],
trailing_comment: None,
};
let result = transpiler.transpile_external_mod_declaration("helpers", &expr);
let result_str = result.to_string();
assert!(result_str.contains("pub"));
assert!(result_str.contains("super"));
assert!(result_str.contains("helpers"));
}
#[test]
fn test_transpile_turbofish_four_types() {
let result = Transpiler::transpile_turbofish("<A, B, C, D>");
let result_str = result.to_string();
assert!(result_str.contains('A'));
assert!(result_str.contains('B'));
assert!(result_str.contains('C'));
assert!(result_str.contains('D'));
}
#[test]
fn test_transpile_turbofish_five_types() {
let result = Transpiler::transpile_turbofish("<T, U, V, W, X>");
let result_str = result.to_string();
assert!(result_str.contains('T'));
assert!(result_str.contains('U'));
assert!(result_str.contains('V'));
assert!(result_str.contains('W'));
assert!(result_str.contains('X'));
}
#[test]
fn test_transpile_identifier_multiple_turbofishes() {
let transpiler = test_transpiler();
let result = transpiler.transpile_identifier("HashMap::<String, i32>::new");
let result_str = result.to_string();
assert!(result_str.contains("HashMap"));
assert!(result_str.contains("String"));
assert!(result_str.contains("i32"));
assert!(result_str.contains("new"));
}
#[test]
fn test_transpile_qualified_name_very_deep() {
let result = Transpiler::transpile_qualified_name("a::b::c::d", "Function");
let result_str = result.to_string();
assert!(result_str.contains('a'));
assert!(result_str.contains('b'));
assert!(result_str.contains('c'));
assert!(result_str.contains('d'));
assert!(result_str.contains("Function"));
}
#[test]
fn test_transpile_identifier_reserved_struct() {
let transpiler = test_transpiler();
let result = transpiler.transpile_identifier("struct");
let result_str = result.to_string();
assert!(result_str.contains("r#") || result_str.contains("struct"));
}
#[test]
fn test_transpile_identifier_reserved_enum() {
let transpiler = test_transpiler();
let result = transpiler.transpile_identifier("enum");
let result_str = result.to_string();
assert!(result_str.contains("r#") || result_str.contains("enum"));
}
#[test]
fn test_transpile_external_mod_declaration_non_pub_attr() {
let transpiler = test_transpiler();
let expr = Expr {
kind: ExprKind::Identifier("module".to_string()),
span: Span::default(),
attributes: vec![Attribute {
name: "allow".to_string(),
args: vec!["dead_code".to_string()],
span: Span::default(),
}],
leading_comments: vec![],
trailing_comment: None,
};
let result = transpiler.transpile_external_mod_declaration("module", &expr);
assert_eq!(result.to_string(), "mod module ;");
}
#[test]
fn test_transpile_identifier_self_in_path() {
let transpiler = test_transpiler();
let result = transpiler.transpile_identifier("crate::self::module");
let result_str = result.to_string();
assert!(result_str.contains("crate"));
assert!(result_str.contains("self"));
assert!(result_str.contains("module"));
}
#[test]
fn test_transpile_identifier_reserved_loop() {
let transpiler = test_transpiler();
let result = transpiler.transpile_identifier("loop");
let result_str = result.to_string();
assert!(result_str.contains("r#") || result_str.contains("loop"));
}
#[test]
fn test_transpile_identifier_reserved_trait() {
let transpiler = test_transpiler();
let result = transpiler.transpile_identifier("trait");
let result_str = result.to_string();
assert!(result_str.contains("r#") || result_str.contains("trait"));
}
#[test]
fn test_transpile_identifier_reserved_const() {
let transpiler = test_transpiler();
let result = transpiler.transpile_identifier("const");
let result_str = result.to_string();
assert!(result_str.contains("r#") || result_str.contains("const"));
}
#[test]
fn test_transpile_identifier_reserved_while() {
let transpiler = test_transpiler();
let result = transpiler.transpile_identifier("while");
let result_str = result.to_string();
assert!(result_str.contains("r#") || result_str.contains("while"));
}
#[test]
fn test_transpile_identifier_reserved_for() {
let transpiler = test_transpiler();
let result = transpiler.transpile_identifier("for");
let result_str = result.to_string();
assert!(result_str.contains("r#") || result_str.contains("for"));
}
#[test]
fn test_transpile_identifier_reserved_async() {
let transpiler = test_transpiler();
let result = transpiler.transpile_identifier("async");
let result_str = result.to_string();
assert!(result_str.contains("r#") || result_str.contains("async"));
}
#[test]
fn test_transpile_identifier_reserved_await() {
let transpiler = test_transpiler();
let result = transpiler.transpile_identifier("await");
let result_str = result.to_string();
assert!(result_str.contains("r#") || result_str.contains("await"));
}
#[test]
fn test_transpile_identifier_reserved_dyn() {
let transpiler = test_transpiler();
let result = transpiler.transpile_identifier("dyn");
let result_str = result.to_string();
assert!(result_str.contains("r#") || result_str.contains("dyn"));
}
#[test]
fn test_transpile_identifier_reserved_unsafe() {
let transpiler = test_transpiler();
let result = transpiler.transpile_identifier("unsafe");
let result_str = result.to_string();
assert!(result_str.contains("r#") || result_str.contains("unsafe"));
}
#[test]
fn test_transpile_turbofish_nested() {
let result = Transpiler::transpile_turbofish("<Vec>");
let result_str = result.to_string();
assert!(result_str.contains("Vec"));
}
#[test]
fn test_transpile_qualified_name_numeric_suffix() {
let result = Transpiler::transpile_qualified_name("module", "version2");
let result_str = result.to_string();
assert!(result_str.contains("module"));
assert!(result_str.contains("version2"));
}
#[test]
fn test_transpile_qualified_name_underscore() {
let result = Transpiler::transpile_qualified_name("_internal", "_helper");
let result_str = result.to_string();
assert!(result_str.contains("_internal"));
assert!(result_str.contains("_helper"));
}
#[test]
fn test_transpile_external_mod_declaration_pub_in() {
let transpiler = test_transpiler();
let expr = Expr {
kind: ExprKind::Identifier("restricted".to_string()),
span: Span::default(),
attributes: vec![Attribute {
name: "pub".to_string(),
args: vec!["in".to_string()],
span: Span::default(),
}],
leading_comments: vec![],
trailing_comment: None,
};
let result = transpiler.transpile_external_mod_declaration("restricted", &expr);
let result_str = result.to_string();
assert!(result_str.contains("pub"));
assert!(result_str.contains("in") || result_str.contains("restricted"));
}
#[test]
fn test_transpile_identifier_very_long() {
let transpiler = test_transpiler();
let long_name = "this_is_a_very_long_identifier_name_with_many_words_and_underscores";
let result = transpiler.transpile_identifier(long_name);
assert!(result
.to_string()
.contains("this_is_a_very_long_identifier"));
}
#[test]
fn test_transpile_turbofish_extra_whitespace() {
let result = Transpiler::transpile_turbofish("< String , i32 >");
let result_str = result.to_string();
assert!(result_str.contains("String"));
assert!(result_str.contains("i32"));
}
}