Skip to content

Commit 809eada

Browse files
committed
Optimize string handling in header-translator a bit
1 parent 152d135 commit 809eada

7 files changed

Lines changed: 135 additions & 56 deletions

File tree

Cargo.lock

Lines changed: 0 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/header-translator/Cargo.toml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@ tracing = { version = "0.1.37", default-features = false, features = ["std"] }
2222
tracing-subscriber = { version = "0.3.16", features = ["fmt", "env-filter"] }
2323
tracing-tree = "0.4.1"
2424
proc-macro2 = "1.0.66"
25-
syn = { version = "2.0", features = ["parsing"] }
2625
heck = "0.5"
2726
semver = { version = "1.0", features = ["serde"] }
2827
lenient_semver_parser = "0.4"

crates/header-translator/src/config.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -487,11 +487,11 @@ impl LibraryConfig {
487487
let allowed_in = self.class_data.values();
488488
for data in all.clone().filter(filter_ptr(allowed_in)) {
489489
assert_eq!(data.derives, Default::default());
490-
assert_eq!(data.definition_skipped, Default::default());
490+
assert_eq!(data.definition_skipped, bool::default());
491491
assert_eq!(data.categories, Default::default());
492492
assert_eq!(data.counterpart, Default::default());
493493
assert_eq!(data.skipped_protocols, Default::default());
494-
assert_eq!(data.main_thread_only, Default::default());
494+
assert_eq!(data.main_thread_only, bool::default());
495495
assert_eq!(data.bridged_to, Default::default());
496496
}
497497

@@ -507,7 +507,7 @@ impl LibraryConfig {
507507

508508
let allowed_in = self.fns.values();
509509
for data in all.clone().filter(filter_ptr(allowed_in)) {
510-
assert_eq!(data.no_implementor, Default::default());
510+
assert_eq!(data.no_implementor, bool::default());
511511
assert_eq!(data.implementor, Default::default());
512512
assert_eq!(data.arguments, Default::default());
513513
assert_eq!(data.return_, Default::default());

crates/header-translator/src/lib.rs

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -95,14 +95,5 @@ fn immediate_children<'tu>(
9595
});
9696
}
9797

98-
pub(crate) fn to_snake_case(input: impl AsRef<str>) -> String {
99-
let input = input.as_ref();
100-
if input == "_" {
101-
String::from("_")
102-
} else {
103-
heck::ToSnakeCase::to_snake_case(input)
104-
}
105-
}
106-
10798
/// The version of the framework crates.
10899
pub const VERSION: &str = "0.3.2";

crates/header-translator/src/method.rs

Lines changed: 7 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,9 @@ use crate::context::Context;
1010
use crate::display_helper::FormatterFn;
1111
use crate::documentation::Documentation;
1212
use crate::id::{cfg_gate_ln, ItemTree};
13-
use crate::name_translation::{self, is_likely_bounds_affecting};
13+
use crate::name_translation::{
14+
self, handle_keyword, is_likely_bounds_affecting, param_name, to_snake_case,
15+
};
1416
use crate::objc2_utils::in_selector_family;
1517
use crate::rust_type::{MethodArgumentQualifier, SafetyProperty, Ty};
1618
use crate::stmt::parse_param_children;
@@ -636,7 +638,7 @@ impl Method {
636638
"`{arg_name}` might not be bounds-checked"
637639
)));
638640
}
639-
safety.merge(arg_ty.safety_in_method_argument(&crate::to_snake_case(arg_name)))
641+
safety.merge(arg_ty.safety_in_method_argument(&to_snake_case(arg_name)))
640642
})
641643
.merge(result_type.safety_in_fn_return());
642644

@@ -907,7 +909,7 @@ impl Method {
907909
is_class,
908910
);
909911

910-
let mut safety = ty.safety_in_fn_argument(&crate::to_snake_case(&name));
912+
let mut safety = ty.safety_in_fn_argument(&to_snake_case(&name));
911913
if kind == PropertyKind::UnsafeRetained && !ty.is_primitive_or_record() {
912914
// We could _possibly_ allow these to be safe, but let's
913915
// not for now, they interact weirdly with the getters
@@ -1245,7 +1247,7 @@ impl Method {
12451247
if !self.safe {
12461248
write!(f, "unsafe ")?;
12471249
}
1248-
write!(f, "fn {}(", handle_reserved(&self.fn_name))?;
1250+
write!(f, "fn {}(", handle_keyword(&self.fn_name))?;
12491251

12501252
// Receiver
12511253
if let MemoryManagement::RetainedInit = self.memory_management {
@@ -1258,7 +1260,7 @@ impl Method {
12581260

12591261
// Arguments
12601262
for (param, arg_ty) in arguments {
1261-
let param = handle_reserved(&crate::to_snake_case(param));
1263+
let param = param_name(param);
12621264
write!(f, "{param}: {}, ", arg_ty.method_argument())?;
12631265
}
12641266
if self.requires_main_thread_marker {
@@ -1286,28 +1288,3 @@ impl Method {
12861288
})
12871289
}
12881290
}
1289-
1290-
pub(crate) fn handle_reserved(name: &str) -> String {
1291-
// try to parse name as an identifier
1292-
if let Ok(ident) = syn::parse_str::<syn::Ident>(name) {
1293-
ident.to_string()
1294-
}
1295-
// try to parse as a raw identifier (NOTE: does not handle `self` or `super`)
1296-
else if let Ok(ident) = syn::parse_str::<syn::Ident>(&format!("r#{name}")) {
1297-
ident.to_string()
1298-
}
1299-
// translate whatever remains unchanged (needed for, e.g., `_`)
1300-
else if name == "self" {
1301-
"self_".into()
1302-
} else if name == "Self" {
1303-
"r#Self".into()
1304-
} else if name == "super" {
1305-
"super_".into()
1306-
} else if name == "_" {
1307-
// Hack: Assumes that there are not other fields / parameters with the
1308-
// name `_`.
1309-
"param1".into()
1310-
} else {
1311-
name.into()
1312-
}
1313-
}

crates/header-translator/src/name_translation.rs

Lines changed: 112 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
//! Kinda ugly and under-tested, may not work for all cases.
66
#![allow(clippy::if_same_then_else)]
77

8-
use std::{collections::VecDeque, iter::FusedIterator};
8+
use std::{borrow::Cow, collections::VecDeque, iter::FusedIterator};
99

1010
use itertools::Itertools;
1111

@@ -526,6 +526,108 @@ pub(crate) fn is_completion_handler_param_name(s: &str) -> bool {
526526
)
527527
}
528528

529+
pub(crate) fn to_snake_case(mut input: &str) -> Cow<'_, str> {
530+
if !input.contains(|c: char| c.is_ascii_uppercase()) {
531+
// No uppercase chars -> assume already snake case
532+
Cow::Borrowed(input)
533+
} else {
534+
// Preserve leading underscores.
535+
let mut leading_underscores = 0;
536+
while let Some(rest) = input.strip_prefix('_') {
537+
leading_underscores += 1;
538+
input = rest;
539+
}
540+
let mut result = heck::ToSnakeCase::to_snake_case(input);
541+
for _ in 0..leading_underscores {
542+
result.insert(0, '_');
543+
}
544+
Cow::Owned(result)
545+
}
546+
}
547+
548+
pub(crate) fn param_name(param: &str) -> Cow<'_, str> {
549+
let param = to_snake_case(param);
550+
if let Some(param) = keyword(&param) {
551+
Cow::Borrowed(param)
552+
} else {
553+
param
554+
}
555+
}
556+
557+
/// Check if a given name is a reserved keyword.
558+
fn keyword(name: &str) -> Option<&'static str> {
559+
// <https://doc.rust-lang.org/reference/keywords.html>
560+
Some(match name {
561+
// Strict keywords.
562+
"_" => "param1", // HACK: Assumes that there are not other fields / parameters with the name `_`.
563+
"as" => "r#as",
564+
"async" => "r#async",
565+
"await" => "r#await",
566+
"break" => "r#break",
567+
"const" => "r#const",
568+
"continue" => "r#continue",
569+
"crate" => "crate_", // Cannot be a raw identifier
570+
"dyn" => "r#dyn",
571+
"else" => "r#else",
572+
"enum" => "r#enum",
573+
"extern" => "r#extern",
574+
"false" => "r#false",
575+
"fn" => "r#fn",
576+
"for" => "r#for",
577+
"if" => "r#if",
578+
"impl" => "r#impl",
579+
"in" => "r#in",
580+
"let" => "r#let",
581+
"loop" => "r#loop",
582+
"match" => "r#match",
583+
"mod" => "r#mod",
584+
"move" => "r#move",
585+
"mut" => "r#mut",
586+
"pub" => "r#pub",
587+
"ref" => "r#ref",
588+
"return" => "r#return",
589+
"self" => "self_", // Cannot be a raw identifier
590+
"Self" => "Self_", // Cannot be a raw identifier
591+
"static" => "r#static",
592+
"struct" => "r#struct",
593+
"super" => "super_", // Cannot be a raw identifier
594+
"trait" => "r#trait",
595+
"true" => "r#true",
596+
"type" => "r#type",
597+
"unsafe" => "r#unsafe",
598+
"use" => "r#use",
599+
"where" => "r#where",
600+
"while" => "r#while",
601+
602+
// Reserved keywords.
603+
"abstract" => "r#abstract",
604+
"become" => "r#become",
605+
"box" => "r#box",
606+
"do" => "r#do",
607+
"final" => "r#final",
608+
"gen" => "r#gen",
609+
"macro" => "r#macro",
610+
"override" => "r#override",
611+
"priv" => "r#priv",
612+
"try" => "r#try",
613+
"typeof" => "r#typeof",
614+
"unsized" => "r#unsized",
615+
"virtual" => "r#virtual",
616+
"yield" => "r#yield",
617+
618+
// Weak keywords. Not necessary to r#-prefix these.
619+
// "macro_rules" => "r#macro_rules",
620+
// "raw" => "r#raw",
621+
// "safe" => "r#safe",
622+
// "union" => "r#union",
623+
_ => return None,
624+
})
625+
}
626+
627+
pub(crate) fn handle_keyword(name: &str) -> &str {
628+
keyword(name).unwrap_or(name)
629+
}
630+
529631
#[cfg(test)]
530632
mod tests {
531633
use super::*;
@@ -772,4 +874,13 @@ mod tests {
772874
"signData:withSecureElementPass:completion:"
773875
)));
774876
}
877+
878+
#[test]
879+
fn snake() {
880+
assert_eq!(to_snake_case("FooBar"), "foo_bar");
881+
assert_eq!(to_snake_case("foo_bar"), "foo_bar");
882+
assert_eq!(to_snake_case("_"), "_");
883+
assert_eq!(to_snake_case("___Foo"), "___foo");
884+
assert_eq!(to_snake_case("fooBar"), "foo_bar");
885+
}
775886
}

crates/header-translator/src/stmt.rs

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,11 @@ use crate::id::ItemIdentifier;
2222
use crate::id::ItemTree;
2323
use crate::id::Location;
2424
use crate::immediate_children;
25-
use crate::method::{handle_reserved, Method};
26-
use crate::name_translation::is_likely_bounds_affecting;
27-
use crate::name_translation::{enum_prefix, split_words};
25+
use crate::method::Method;
26+
use crate::name_translation::handle_keyword;
27+
use crate::name_translation::{
28+
enum_prefix, is_likely_bounds_affecting, param_name, split_words, to_snake_case,
29+
};
2830
use crate::protocol::parse_direct_protocols;
2931
use crate::protocol::ProtocolRef;
3032
use crate::rust_type::PointeeTy;
@@ -2057,7 +2059,7 @@ impl Stmt {
20572059
"`{arg_name}` might not be bounds-checked"
20582060
)));
20592061
}
2060-
safety.merge(arg_ty.safety_in_fn_argument(&crate::to_snake_case(arg_name)))
2062+
safety.merge(arg_ty.safety_in_fn_argument(&to_snake_case(arg_name)))
20612063
})
20622064
.merge(result_type.safety_in_fn_return());
20632065

@@ -3068,7 +3070,7 @@ impl Stmt {
30683070
} else {
30693071
write!(f, "pub ")?;
30703072
}
3071-
let name = handle_reserved(name);
3073+
let name = handle_keyword(name);
30723074
writeln!(f, "{name}: {},", ty.record())?;
30733075
}
30743076
writeln!(f, "}}")?;
@@ -3361,7 +3363,7 @@ impl Stmt {
33613363
write!(f, "// TODO: ")?;
33623364
write!(f, "pub fn {c_name}(")?;
33633365
for (param, arg_ty) in arguments {
3364-
let param = handle_reserved(&crate::to_snake_case(param));
3366+
let param = param_name(param);
33653367
write!(f, "{param}: {},", arg_ty.fn_argument_converted())?;
33663368
}
33673369
writeln!(
@@ -3408,7 +3410,7 @@ impl Stmt {
34083410
}
34093411
writeln!(f, "#[inline]")?;
34103412
let unsafe_ = if *safe { "" } else { "unsafe " };
3411-
let fn_name = handle_reserved(&id.name);
3413+
let fn_name = handle_keyword(&id.name);
34123414
// Wrappers have normal Rust ABI to unclutter docs.
34133415
write!(f, "{vis} {unsafe_}fn {fn_name}(")?;
34143416

@@ -3433,7 +3435,7 @@ impl Stmt {
34333435
continue;
34343436
}
34353437

3436-
let param = handle_reserved(&crate::to_snake_case(param));
3438+
let param = param_name(param);
34373439
write!(f, "{param}: {},", arg_ty.fn_argument_converted())?;
34383440
}
34393441

@@ -3467,7 +3469,7 @@ impl Stmt {
34673469

34683470
write!(f, "fn {c_name}(")?;
34693471
for (param, arg_ty) in arguments {
3470-
let param = handle_reserved(&crate::to_snake_case(param));
3472+
let param = param_name(param);
34713473
write!(f, "{param}: {},", arg_ty.fn_argument_unconverted())?;
34723474
}
34733475
writeln!(
@@ -3480,7 +3482,7 @@ impl Stmt {
34803482

34813483
// Perform argument conversion.
34823484
for (i, (param, ty)) in arguments.iter().enumerate() {
3483-
let param = handle_reserved(&crate::to_snake_case(param));
3485+
let param = param_name(param);
34843486
let param_or_self = if arg_is_self.unwrap_or(usize::MAX) == i {
34853487
"self"
34863488
} else {
@@ -3493,7 +3495,7 @@ impl Stmt {
34933495
let fn_call = FormatterFn(|f| {
34943496
write!(f, "unsafe {{ {c_name}(")?;
34953497
for (i, (param, ty)) in arguments.iter().enumerate() {
3496-
let param = handle_reserved(&crate::to_snake_case(param));
3498+
let param = param_name(param);
34973499
if arg_is_self.unwrap_or(usize::MAX) == i {
34983500
let converter = ty.fn_argument_converter("self", &param);
34993501
if converter.to_string().is_empty() {

0 commit comments

Comments
 (0)