Remove positive is_typechecker guards
[hiphop-php.git] / hphp / hack / src / hhbc / utils / string_utils.rs
blob266fb1a796039cec4c2913291d571a848eaf9956
1 // Copyright (c) Facebook, Inc. and its affiliates.
2 //
3 // This source code is licensed under the MIT license found in the
4 // LICENSE file in the "hack" directory of this source tree.
6 use escaper::*;
7 use lazy_static::lazy_static;
8 use naming_special_names_rust::{classes as ns_classes, members};
9 use regex::Regex;
10 use std::borrow::Cow;
11 use std::cell::Cell;
13 lazy_static! {
14     static ref HH_NS_RE: Regex = Regex::new(r"^\\?HH\\").unwrap();
15     static ref NS_RE: Regex = Regex::new(r".*\\").unwrap();
16     static ref TYPE_RE: Regex = Regex::new(r"<.*>").unwrap();
19 #[derive(Clone)]
20 pub struct GetName {
21     string: Vec<u8>,
23     unescape: fn(String) -> String,
26 impl GetName {
27     pub fn new(string: Vec<u8>, unescape: fn(String) -> String) -> GetName {
28         GetName { string, unescape }
29     }
31     pub fn get(&self) -> &Vec<u8> {
32         &self.string
33     }
34     #[allow(clippy::inherent_to_string)]
35     pub fn to_string(&self) -> String {
36         String::from_utf8_lossy(&self.string).to_string()
37     }
38     pub fn to_unescaped_string(&self) -> String {
39         let unescape = self.unescape;
40         unescape(self.to_string())
41     }
44 impl std::fmt::Debug for GetName {
45     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
46         write!(f, "GetName {{ string: {}, unescape:? }}", self.to_string())
47     }
50 thread_local!(static MANGLE_XHP_MODE: Cell<bool> = Cell::new(true));
52 pub fn without_xhp_mangling<T>(f: impl FnOnce() -> T) -> T {
53     MANGLE_XHP_MODE.with(|cur| {
54         let old = cur.replace(false);
55         let ret = f();
56         cur.set(old); // use old instead of true to support nested calls in the same thread
57         ret
58     })
61 pub fn is_xhp(name: &str) -> bool {
62     name.chars().next().map_or(false, |c| c == ':')
65 pub fn clean(s: &str) -> &str {
66     if is_xhp(s) { strip_colon(s) } else { s }
69 fn strip_colon(s: &str) -> &str {
70     s.trim_start_matches(':')
73 fn ignore_id(name: &str) -> bool {
74     name.starts_with("Closure$")
77 pub fn mangle_xhp_id(mut name: String) -> String {
78     if !ignore_id(&name) && MANGLE_XHP_MODE.with(|x| x.get()) {
79         if is_xhp(&name) {
80             name.replace_range(..1, "xhp_")
81         }
82         name.replace(":", "__").replace("-", "_")
83     } else {
84         name
85     }
88 fn unmangle_xhp_id(name: &str) -> String {
89     if name.starts_with("xhp_") {
90         format!(
91             ":{}",
92             lstrip(name, "xhp_").replace("__", ":").replace("_", "-")
93         )
94     } else {
95         name.replace("__", ":").replace("_", "-")
96     }
99 pub fn mangle(mut name: String) -> String {
100     if !ignore_id(&name) {
101         if let Some(pos) = name.rfind('\\') {
102             name.replace_range(pos + 1.., &mangle_xhp_id(name[pos + 1..].to_string()))
103         } else {
104             name = mangle_xhp_id(name);
105         }
106     }
107     name
110 pub fn unmangle(name: String) -> String {
111     if ignore_id(&name) {
112         return name;
113     }
114     let ids = name.split('\\').collect::<Vec<_>>();
115     match ids.split_last() {
116         None => String::new(),
117         Some((last, rest)) => {
118             if rest.is_empty() {
119                 unmangle_xhp_id(last)
120             } else {
121                 format!("{}\\{}", rest.join("\\"), unmangle_xhp_id(last))
122             }
123         }
124     }
127 pub fn quote_string(s: &str) -> String {
128     format!("\"{}\"", escape(s))
131 pub fn quote_string_with_escape(s: &str) -> String {
132     format!("\\\"{}\\\"", escape(s))
135 pub fn single_quote_string_with_escape(s: &str) -> String {
136     format!("'{}'", escape(s))
139 pub fn triple_quote_string(s: &str) -> String {
140     format!("\"\"\"{}\"\"\"", escape(s))
143 pub fn prefix_namespace(n: &str, s: &str) -> String {
144     format!("{}\\{}", n, s)
147 pub fn strip_global_ns(s: &str) -> &str {
148     if let Some(rest) = s.strip_prefix('\\') {
149         rest
150     } else {
151         s
152     }
155 // Strip zero or more chars followed by a backslash
156 pub fn strip_ns(s: &str) -> &str {
157     s.rfind('\\').map_or(s, |i| &s[i + 1..])
160 // Remove \HH\ or HH\ preceding a string
161 pub fn strip_hh_ns(s: &str) -> Cow<'_, str> {
162     HH_NS_RE.replace(s, "")
165 pub fn has_ns(s: &str) -> bool {
166     NS_RE.is_match(s)
169 pub fn strip_type_list(s: &str) -> Cow<'_, str> {
170     TYPE_RE.replace_all(s, "")
173 pub fn cmp(s1: &str, s2: &str, case_sensitive: bool, ignore_ns: bool) -> bool {
174     fn canon(s: &str, ignore_ns: bool) -> &str {
175         if ignore_ns { strip_ns(s) } else { s }
176     }
178     let s1 = canon(s1, ignore_ns);
179     let s2 = canon(s2, ignore_ns);
181     if case_sensitive {
182         s1 == s2
183     } else {
184         s1.eq_ignore_ascii_case(s2)
185     }
188 pub fn is_self(s: impl AsRef<str>) -> bool {
189     s.as_ref().eq_ignore_ascii_case(ns_classes::SELF)
192 pub fn is_parent(s: impl AsRef<str>) -> bool {
193     s.as_ref().eq_ignore_ascii_case(ns_classes::PARENT)
196 pub fn is_static(s: impl AsRef<str>) -> bool {
197     s.as_ref().eq_ignore_ascii_case(ns_classes::STATIC)
200 pub fn is_class(s: impl AsRef<str>) -> bool {
201     s.as_ref().eq_ignore_ascii_case(members::M_CLASS)
204 pub fn mangle_meth_caller(mangled_cls_name: &str, f_name: &str) -> String {
205     format!("\\MethCaller${}${}", mangled_cls_name, f_name)
208 pub fn lstrip<'a>(s: &'a str, p: &str) -> &'a str {
209     if s.len() < p.len() {
210         s
211     } else {
212         let sb = s.as_bytes();
213         let pb = p.as_bytes();
214         for i in 0..pb.len() {
215             if sb[i] != pb[i] {
216                 return s;
217             }
218         }
219         // s and p are valid then unwrap should never panic.
220         std::str::from_utf8(&sb[pb.len()..]).unwrap()
221     }
224 pub mod types {
225     pub fn fix_casing(s: &str) -> &str {
226         match s.to_lowercase().as_str() {
227             "vector" => "Vector",
228             "immvector" => "ImmVector",
229             "set" => "Set",
230             "immset" => "ImmSet",
231             "map" => "Map",
232             "immmap" => "ImmMap",
233             "pair" => "Pair",
234             _ => s,
235         }
236     }
239 /* Integers are represented as strings */
240 pub mod integer {
241     pub fn to_decimal(s: &str) -> Result<String, ocaml_helper::ParseIntError> {
242         /* Don't accidentally convert 0 to 0o */
243         let r = if s.len() > 1
244             && s.as_bytes()[0] == b'0'
245             && s.as_bytes()[1] >= b'0'
246             && s.as_bytes()[1] <= b'9'
247         {
248             ocaml_helper::int_of_string_wrap(format!("0o{}", &s[1..]).as_bytes())
249         } else {
250             ocaml_helper::int_of_string_wrap(s.as_bytes())
251         };
252         r.map(|n| n.to_string())
253     }
256 pub mod float {
257     fn sprintf(f: f64) -> Option<String> {
258         const BUF_SIZE: usize = 256;
260         let format = "%.17g\0";
261         let mut buffer = [0u8; BUF_SIZE];
262         let n = unsafe {
263             libc::snprintf(
264                 buffer.as_mut_ptr() as *mut libc::c_char,
265                 BUF_SIZE,
266                 format.as_ptr() as *const libc::c_char,
267                 f,
268             ) as usize
269         };
270         if n >= BUF_SIZE {
271             None
272         } else {
273             String::from_utf8(buffer[..n].to_vec()).ok()
274         }
275     }
277     pub fn to_string(f: impl Into<f64>) -> String {
278         let f = f.into();
279         // or_else should not happen, but just in case it does fall back
280         // to Rust native formatting
281         let res = sprintf(f).unwrap_or_else(|| f.to_string());
282         match res.as_ref() {
283             "-nan" => "NAN".to_string(),
284             "nan" => "NAN".to_string(),
285             "-inf" => "-INF".to_string(),
286             "inf" => "INF".to_string(),
287             _ => res,
288         }
289     }
292 pub mod locals {
293     pub fn strip_dollar(s: &str) -> &str {
294         if !s.is_empty() && s.as_bytes()[0] == b'$' {
295             &s[1..]
296         } else {
297             s
298         }
299     }
302 pub mod classes {
303     pub fn mangle_class(prefix: &str, scope: &str, idx: u32) -> String {
304         if idx == 1 {
305             format!("{}${}", prefix.to_string(), scope.to_string())
306         } else {
307             format!(
308                 "{}${}#{}",
309                 prefix.to_string(),
310                 scope.to_string(),
311                 idx.to_string()
312             )
313         }
314     }
317 pub mod closures {
318     pub fn mangle_closure(scope: &str, idx: u32) -> String {
319         super::classes::mangle_class("Closure", scope, idx)
320     }
322     /* Closure classes have names of the form
323      *   Closure$ scope ix ; num
324      * where
325      *   scope  ::=
326      *     <function-name>
327      *   | <class-name> :: <method-name>
328      *   |
329      *   ix ::=
330      *     # <digits>
331      */
332     pub fn unmangle_closure(mangled_name: &str) -> Option<&str> {
333         if is_closure_name(mangled_name) {
334             let prefix_length = "Closure$".chars().count();
335             match mangled_name.find('#') {
336                 Some(pos) => Some(&mangled_name[prefix_length..pos]),
337                 None => Some(&mangled_name[prefix_length..]),
338             }
339         } else {
340             None
341         }
342     }
344     pub fn is_closure_name(s: &str) -> bool {
345         s.starts_with("Closure$")
346     }
349 pub mod reified {
350     #[allow(clippy::redundant_static_lifetimes)]
351     pub static PROP_NAME: &'static str = "86reified_prop";
352     #[allow(clippy::redundant_static_lifetimes)]
353     pub static INIT_METH_NAME: &'static str = "86reifiedinit";
354     #[allow(clippy::redundant_static_lifetimes)]
355     pub static INIT_METH_PARAM_NAME: &'static str = "$__typestructures";
356     #[allow(clippy::redundant_static_lifetimes)]
357     pub static GENERICS_LOCAL_NAME: &'static str = "$0ReifiedGenerics";
358     #[allow(clippy::redundant_static_lifetimes)]
359     pub static CAPTURED_PREFIX: &'static str = "$__captured$reifiedgeneric$";
361     pub fn reified_generic_captured_name(is_fun: bool, i: usize) -> String {
362         let type_ = if is_fun { "function" } else { "class" };
363         // to_string() due to T52404885
364         format!("$__captured$reifiedgeneric${}${}", type_, i.to_string())
365     }
367     pub fn mangle_reified_param(no_dollar: bool, s: &str) -> String {
368         format!("{}__reified${}", if no_dollar { "" } else { "$" }, s)
369     }
371     pub fn captured_name(is_fun: bool, i: usize) -> String {
372         format!(
373             "{}{}${}",
374             CAPTURED_PREFIX,
375             if is_fun { "function" } else { "class" },
376             i
377         )
378     }
380     pub fn is_captured_generic(id: &str) -> Option<(bool, u32)> {
381         if id.starts_with(CAPTURED_PREFIX) {
382             if let [name, i] = id
383                 .trim_start_matches(CAPTURED_PREFIX)
384                 .splitn(2, '$')
385                 .collect::<Vec<_>>()
386                 .as_slice()
387             {
388                 let is_captured = match *name {
389                     "function" => true,
390                     "class" => false,
391                     _ => return None,
392                 };
393                 let captured_id = i.parse();
394                 if let Ok(captured) = captured_id {
395                     return Some((is_captured, captured));
396                 };
397             }
398         };
399         None
400     }
403 pub mod coeffects {
404     #[allow(clippy::redundant_static_lifetimes)]
405     pub static LOCAL_NAME: &'static str = "$0Coeffects";
406     #[allow(clippy::redundant_static_lifetimes)]
407     pub static CALLER: &'static str = "86caller";
410 #[cfg(test)]
411 mod string_utils_tests {
412     use pretty_assertions::assert_eq;
414     #[test]
415     fn quote_string_test() {
416         let some_string = "test";
417         assert_eq!(super::quote_string(some_string), "\"test\"");
418     }
420     #[test]
421     fn quote_string_with_escape_test() {
422         let some_string = "test";
423         assert_eq!(super::quote_string_with_escape(some_string), "\\\"test\\\"");
424     }
426     #[test]
427     fn single_quote_string_with_escape_test() {
428         let some_string = "test";
429         assert_eq!(
430             super::single_quote_string_with_escape(some_string),
431             "'test'"
432         );
433     }
435     #[test]
436     fn triple_quote_string_test() {
437         let some_string = "test";
438         assert_eq!(super::triple_quote_string(some_string), "\"\"\"test\"\"\"");
439     }
441     #[test]
442     fn prefix_namespace_test() {
443         let namespace = "ns";
444         let some_string = "test";
445         assert_eq!(super::prefix_namespace(namespace, some_string), "ns\\test");
446     }
448     #[test]
449     fn strip_global_ns_test() {
450         let some_string = "\\test";
451         let another_string = "\\\\";
452         assert_eq!(super::strip_global_ns(some_string), "test");
453         assert_eq!(super::strip_global_ns(another_string), "\\");
454     }
456     #[test]
457     fn strip_ns_test() {
458         let with_ns = "ns1\\test";
459         let without_ns = "test";
460         assert_eq!(super::strip_ns(with_ns), "test");
461         assert_eq!(super::strip_ns(without_ns), without_ns);
462     }
464     #[test]
465     fn strip_hh_ns() {
466         let with_ns = "HH\\test";
467         let without_ns = "test";
468         assert_eq!(super::strip_ns(with_ns), "test");
469         assert_eq!(super::strip_ns(without_ns), without_ns);
470     }
472     #[test]
473     fn strip_hh_ns_2() {
474         let with_ns = "\\HH\\test";
475         let without_ns = "test";
476         assert_eq!(super::strip_ns(with_ns), "test");
477         assert_eq!(super::strip_ns(without_ns), without_ns);
478     }
480     #[test]
481     fn has_ns_test() {
482         let with_ns = "\\test";
483         let without_ns = "test";
484         assert!(super::has_ns(with_ns));
485         assert!(!super::has_ns(without_ns));
486     }
488     #[test]
489     fn strip_type_list_test() {
490         let s = "MutableMap<Tk, Tv>";
491         assert_eq!(super::strip_type_list(s).into_owned(), "MutableMap");
492     }
494     #[test]
495     fn cmp_test() {
496         let s1 = "ns1\\s1";
497         let s1_uppercase = "NS1\\S1";
499         let ns2_s1 = "ns2\\s1";
500         let ns2_s1_uppercase = "NS2\\S1";
502         let ns2_s2 = "ns2\\s2";
504         assert!(super::cmp(s1, s1_uppercase, false, false));
505         assert!(!super::cmp(s1, s1_uppercase, true, false));
506         assert!(super::cmp(s1, s1_uppercase, false, true));
507         assert!(!super::cmp(s1, s1_uppercase, true, true));
509         assert!(!super::cmp(s1, ns2_s1, false, false));
510         assert!(!super::cmp(s1, ns2_s1, true, false));
511         assert!(super::cmp(s1, ns2_s1, false, true));
512         assert!(super::cmp(s1, ns2_s1, true, true));
514         assert!(!super::cmp(s1, ns2_s1_uppercase, false, false));
515         assert!(!super::cmp(s1, ns2_s1_uppercase, true, false));
516         assert!(super::cmp(s1, ns2_s1_uppercase, false, true));
517         assert!(!super::cmp(s1, ns2_s1_uppercase, true, true));
519         assert!(!super::cmp(s1, ns2_s2, false, false));
520         assert!(!super::cmp(s1, ns2_s2, true, false));
521         assert!(!super::cmp(s1, ns2_s2, false, true));
522         assert!(!super::cmp(s1, ns2_s2, true, true));
523     }
525     #[test]
526     fn is_self_test() {
527         let s1 = "self";
528         let s2 = "not_self";
530         assert!(super::is_self(s1));
531         assert!(!super::is_self(s2));
532     }
534     #[test]
535     fn is_parent_test() {
536         let s1 = "parent";
537         let s2 = "not_parent";
539         assert!(super::is_parent(s1));
540         assert!(!super::is_parent(s2));
541     }
543     #[test]
544     fn is_static_test() {
545         let s1 = "static";
546         let s2 = "not_static";
548         assert!(super::is_static(s1));
549         assert!(!super::is_static(s2));
550     }
552     #[test]
553     fn is_class_test() {
554         let s1 = "class";
555         let s2 = "not_a_class";
557         assert!(super::is_class(s1));
558         assert!(!super::is_class(s2));
559     }
561     #[test]
562     fn mangle_meth_caller_test() {
563         let cls = "SomeClass";
564         let f = "some_function";
566         assert_eq!(
567             super::mangle_meth_caller(cls, f),
568             "\\MethCaller$SomeClass$some_function"
569         );
570     }
572     #[test]
573     fn mangle_test_1() {
574         assert_eq!(
575             super::mangle(":foo:bar-and-baz".into()),
576             "xhp_foo__bar_and_baz"
577         );
578     }
580     #[test]
581     fn mangle_test_2() {
582         assert_eq!(super::mangle("\\:base".into()), "\\xhp_base");
583     }
585     #[test]
586     fn mangle_test_3() {
587         assert_eq!(
588             super::mangle("\\NS1\\NS2\\:base".into()),
589             "\\NS1\\NS2\\xhp_base"
590         );
591     }
593     mod types {
594         mod fix_casing {
595             macro_rules! test_case {
596                 ($name: ident, $input: expr, $expected: expr) => {
597                     #[test]
598                     fn $name() {
599                         assert_eq!(crate::types::fix_casing($input), $expected);
600                     }
601                 };
602             }
604             test_case!(lowercase_vector, "vector", "Vector");
605             test_case!(mixedcase_vector, "vEcTor", "Vector");
606             test_case!(uppercase_vector, "VECTOR", "Vector");
608             test_case!(lowercase_immvector, "immvector", "ImmVector");
609             test_case!(mixedcase_immvector, "immvEcTor", "ImmVector");
610             test_case!(uppercase_immvector, "IMMVECTOR", "ImmVector");
612             test_case!(lowercase_set, "set", "Set");
613             test_case!(mixedcase_set, "SeT", "Set");
614             test_case!(uppercase_set, "SET", "Set");
616             test_case!(lowercase_immset, "immset", "ImmSet");
617             test_case!(mixedcase_immset, "ImMSeT", "ImmSet");
618             test_case!(uppercase_immset, "IMMSET", "ImmSet");
620             test_case!(lowercase_map, "map", "Map");
621             test_case!(mixedcase_map, "MaP", "Map");
622             test_case!(uppercase_map, "MAP", "Map");
624             test_case!(lowercase_immmap, "immmap", "ImmMap");
625             test_case!(mixedcase_immmap, "immMaP", "ImmMap");
626             test_case!(uppercase_immmap, "IMMMAP", "ImmMap");
628             test_case!(lowercase_pair, "pair", "Pair");
629             test_case!(mixedcase_pair, "pAiR", "Pair");
630             test_case!(uppercase_pair, "PAIR", "Pair");
632             test_case!(
633                 non_hack_collection_returns_original_string,
634                 "SomeStRinG",
635                 "SomeStRinG"
636             );
637             test_case!(
638                 hack_collection_with_leading_whitespace_returns_original_string,
639                 " pair",
640                 " pair"
641             );
642             test_case!(
643                 hack_collection_with_trailing_whitespace_returns_original_string,
644                 "pair ",
645                 "pair "
646             );
647         }
648     }
650     mod float {
651         use crate::float::*;
652         #[test]
653         fn test_no_float_part() {
654             assert_eq!(to_string(1.0), "1")
655         }
657         #[test]
658         fn test_precision() {
659             assert_eq!(to_string(1.1), "1.1000000000000001")
660         }
662         #[test]
663         fn test_no_trailing_zeroes() {
664             assert_eq!(to_string(1.2), "1.2")
665         }
667         #[test]
668         fn test_scientific() {
669             assert_eq!(to_string(1e+100), "1e+100")
670         }
672         #[test]
673         fn test_scientific_precision() {
674             assert_eq!(to_string(-2.1474836480001e9), "-2147483648.0001001")
675         }
677         #[test]
678         fn test_negative_nan() {
679             assert_eq!(to_string(-std::f32::NAN), "NAN")
680         }
681     }
683     mod integer {
684         mod to_decimal {
685             use crate::integer::*;
687             #[test]
688             fn decimal_zero() {
689                 assert_eq!(to_decimal("0"), Ok("0".to_string()));
690             }
692             #[test]
693             fn octal_zero() {
694                 assert_eq!(to_decimal("00"), Ok("0".to_string()));
695             }
697             #[test]
698             fn binary_zero_lowercase() {
699                 assert_eq!(to_decimal("0b0"), Ok("0".to_string()));
700             }
702             #[test]
703             fn binary_zero_uppercase() {
704                 assert_eq!(to_decimal("0B0"), Ok("0".to_string()));
705             }
707             #[test]
708             fn hex_zero_lowercase() {
709                 assert_eq!(to_decimal("0x0"), Ok("0".to_string()));
710             }
712             #[test]
713             fn hex_zero_uppercase() {
714                 assert_eq!(to_decimal("0X0"), Ok("0".to_string()));
715             }
717             #[test]
718             fn decimal_random_value() {
719                 assert_eq!(to_decimal("1245"), Ok("1245".to_string()));
720             }
722             #[test]
723             fn octal_random_value() {
724                 assert_eq!(to_decimal("02335"), Ok("1245".to_string()));
725             }
727             #[test]
728             fn binary_random_value_lowercase() {
729                 assert_eq!(to_decimal("0b10011011101"), Ok("1245".to_string()));
730             }
732             #[test]
733             fn binary_random_value_uppercase() {
734                 assert_eq!(to_decimal("0B10011011101"), Ok("1245".to_string()));
735             }
737             #[test]
738             fn hex_random_value_lowercase() {
739                 assert_eq!(to_decimal("0x4DD"), Ok("1245".to_string()));
740             }
742             #[test]
743             fn hex_random_value_uppercase() {
744                 assert_eq!(to_decimal("0X4DD"), Ok("1245".to_string()));
745             }
747             #[test]
748             fn decimal_max_value() {
749                 assert_eq!(
750                     to_decimal("9223372036854775807"),
751                     Ok("9223372036854775807".to_string())
752                 );
753             }
755             #[test]
756             fn octal_max_value() {
757                 assert_eq!(
758                     to_decimal("0777777777777777777777"),
759                     Ok("9223372036854775807".to_string())
760                 );
761             }
763             #[test]
764             fn binary_max_value_lowercase() {
765                 assert_eq!(
766                     to_decimal("0b111111111111111111111111111111111111111111111111111111111111111"),
767                     Ok("9223372036854775807".to_string())
768                 );
769             }
771             #[test]
772             fn binary_max_value_uppercase() {
773                 assert_eq!(
774                     to_decimal("0B111111111111111111111111111111111111111111111111111111111111111"),
775                     Ok("9223372036854775807".to_string())
776                 );
777             }
779             #[test]
780             fn hex_max_value_lowercase() {
781                 assert_eq!(
782                     to_decimal("0x7FFFFFFFFFFFFFFF"),
783                     Ok("9223372036854775807".to_string())
784                 );
785             }
787             #[test]
788             fn hex_max_value_uppercase() {
789                 assert_eq!(
790                     to_decimal("0X7FFFFFFFFFFFFFFF"),
791                     Ok("9223372036854775807".to_string())
792                 );
793             }
795             #[test]
796             fn unparsable_string() {
797                 assert!(to_decimal("bad_string").is_err());
798             }
799         }
800     }
802     mod locals {
803         use crate::locals::*;
805         #[test]
806         fn strip_single_leading_dollar() {
807             assert_eq!(strip_dollar("$foo"), "foo");
808         }
810         #[test]
811         fn return_string_if_no_leading_dollar() {
812             assert_eq!(strip_dollar("foo"), "foo");
813         }
815         #[test]
816         fn empty_string() {
817             assert_eq!(strip_dollar(""), "");
818         }
820         #[test]
821         fn string_of_single_dollar() {
822             assert_eq!(strip_dollar("$"), "");
823         }
824     }
826     mod classes {
827         mod mangle_class {
828             use crate::classes::mangle_class;
830             #[test]
831             fn idx_of_one() {
832                 assert_eq!(mangle_class("foo", "bar", 1), "foo$bar")
833             }
835             #[test]
836             fn idx_of_two() {
837                 assert_eq!(mangle_class("foo", "bar", 2), "foo$bar#2")
838             }
839         }
840     }
842     mod closures {
843         mod mangle_closure {
844             use crate::closures::mangle_closure;
846             #[test]
847             fn idx_of_one() {
848                 assert_eq!(mangle_closure("foo", 1), "Closure$foo")
849             }
851             #[test]
852             fn idx_of_two() {
853                 assert_eq!(mangle_closure("foo", 2), "Closure$foo#2")
854             }
855         }
857         mod unmangle_closure {
858             use crate::closures::unmangle_closure;
860             #[test]
861             fn idx_of_one() {
862                 assert_eq!(unmangle_closure("Closure$foo"), Some("foo"))
863             }
865             #[test]
866             fn idx_of_two() {
867                 assert_eq!(unmangle_closure("Closure$foo#2"), Some("foo"))
868             }
870             #[test]
871             fn non_closure() {
872                 assert_eq!(unmangle_closure("SomePrefix$foo"), None);
873                 assert_eq!(unmangle_closure("SomePrefix$foo#2"), None)
874             }
875         }
877         mod is_closure_name {
878             use crate::closures::is_closure_name;
880             #[test]
881             fn closure_1() {
882                 assert_eq!(is_closure_name("Closure$foo"), true)
883             }
885             #[test]
886             fn closure_2() {
887                 assert_eq!(is_closure_name("Closure$foo#2"), true)
888             }
890             #[test]
891             fn non_closure() {
892                 assert_eq!(is_closure_name("SomePrefix$foo"), false);
893                 assert_eq!(is_closure_name("SomePrefix$foo#2"), false)
894             }
895         }
896     }
898     mod reified {
899         use crate::reified::*;
901         #[test]
902         fn test_mangle_reified_param() {
903             assert_eq!(mangle_reified_param(false, "x"), "$__reified$x");
904             assert_eq!(mangle_reified_param(true, "x"), "__reified$x")
905         }
907         #[test]
908         fn test_is_captured_generic() {
909             assert_eq!(
910                 is_captured_generic("$__captured$reifiedgeneric$function$1"),
911                 Some((true, 1))
912             );
913             assert_eq!(
914                 is_captured_generic("$__captured$reifiedgeneric$class$1"),
915                 Some((false, 1))
916             );
917             assert_eq!(is_captured_generic("function$1"), None);
918             assert_eq!(
919                 is_captured_generic("$__captured$reifiedgeneric$function1"),
920                 None
921             );
922         }
924         #[test]
925         fn test_captured_name() {
926             assert_eq!(
927                 captured_name(true, 1),
928                 "$__captured$reifiedgeneric$function$1"
929             );
930             assert_eq!(
931                 captured_name(false, 1),
932                 "$__captured$reifiedgeneric$class$1"
933             );
934         }
935     }
937     #[test]
938     fn test_lstrip() {
939         use super::lstrip;
940         assert_eq!(lstrip("a", "a"), "");
941         assert_eq!(lstrip("a", "ab"), "a");
942         assert_eq!(lstrip("", "ab"), "");
943         assert_eq!(lstrip("", ""), "");
944         assert_eq!(lstrip("a", ""), "a");
945         assert_eq!(lstrip("aa", "a"), "a");
946         assert_eq!(lstrip("aa", "a"), "a");
947     }