• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 #![allow(
2     clippy::elidable_lifetime_names,
3     clippy::float_cmp,
4     clippy::needless_lifetimes,
5     clippy::needless_raw_string_hashes,
6     clippy::non_ascii_literal,
7     clippy::single_match_else,
8     clippy::uninlined_format_args
9 )]
10 
11 #[macro_use]
12 mod macros;
13 
14 use proc_macro2::{Delimiter, Group, Literal, Span, TokenStream, TokenTree};
15 use quote::ToTokens;
16 use std::ffi::CStr;
17 use std::str::FromStr;
18 use syn::{Lit, LitFloat, LitInt, LitStr};
19 
20 #[track_caller]
lit(s: &str) -> Lit21 fn lit(s: &str) -> Lit {
22     let mut tokens = TokenStream::from_str(s).unwrap().into_iter();
23     match tokens.next().unwrap() {
24         TokenTree::Literal(lit) => {
25             assert!(tokens.next().is_none());
26             Lit::new(lit)
27         }
28         wrong => panic!("{:?}", wrong),
29     }
30 }
31 
32 #[test]
strings()33 fn strings() {
34     #[track_caller]
35     fn test_string(s: &str, value: &str) {
36         let s = s.trim();
37         match lit(s) {
38             Lit::Str(lit) => {
39                 assert_eq!(lit.value(), value);
40                 let again = lit.into_token_stream().to_string();
41                 if again != s {
42                     test_string(&again, value);
43                 }
44             }
45             wrong => panic!("{:?}", wrong),
46         }
47     }
48 
49     test_string(r#"  ""  "#, "");
50     test_string(r#"  "a"  "#, "a");
51     test_string(r#"  "\n"  "#, "\n");
52     test_string(r#"  "\r"  "#, "\r");
53     test_string(r#"  "\t"  "#, "\t");
54     test_string(r#"  "��"  "#, "��"); // NOTE: This is an emoji
55     test_string(r#"  "\""  "#, "\"");
56     test_string(r#"  "'"  "#, "'");
57     test_string(r#"  "\u{1F415}"  "#, "\u{1F415}");
58     test_string(r#"  "\u{1_2__3_}"  "#, "\u{123}");
59     test_string(
60         "\"contains\nnewlines\\\nescaped newlines\"",
61         "contains\nnewlinesescaped newlines",
62     );
63     test_string(
64         "\"escaped newline\\\n \x0C unsupported whitespace\"",
65         "escaped newline\x0C unsupported whitespace",
66     );
67     test_string("r\"raw\nstring\\\nhere\"", "raw\nstring\\\nhere");
68     test_string("\"...\"q", "...");
69     test_string("r\"...\"q", "...");
70     test_string("r##\"...\"##q", "...");
71 }
72 
73 #[test]
byte_strings()74 fn byte_strings() {
75     #[track_caller]
76     fn test_byte_string(s: &str, value: &[u8]) {
77         let s = s.trim();
78         match lit(s) {
79             Lit::ByteStr(lit) => {
80                 assert_eq!(lit.value(), value);
81                 let again = lit.into_token_stream().to_string();
82                 if again != s {
83                     test_byte_string(&again, value);
84                 }
85             }
86             wrong => panic!("{:?}", wrong),
87         }
88     }
89 
90     test_byte_string(r#"  b""  "#, b"");
91     test_byte_string(r#"  b"a"  "#, b"a");
92     test_byte_string(r#"  b"\n"  "#, b"\n");
93     test_byte_string(r#"  b"\r"  "#, b"\r");
94     test_byte_string(r#"  b"\t"  "#, b"\t");
95     test_byte_string(r#"  b"\""  "#, b"\"");
96     test_byte_string(r#"  b"'"  "#, b"'");
97     test_byte_string(
98         "b\"contains\nnewlines\\\nescaped newlines\"",
99         b"contains\nnewlinesescaped newlines",
100     );
101     test_byte_string("br\"raw\nstring\\\nhere\"", b"raw\nstring\\\nhere");
102     test_byte_string("b\"...\"q", b"...");
103     test_byte_string("br\"...\"q", b"...");
104     test_byte_string("br##\"...\"##q", b"...");
105 }
106 
107 #[test]
c_strings()108 fn c_strings() {
109     #[track_caller]
110     fn test_c_string(s: &str, value: &CStr) {
111         let s = s.trim();
112         match lit(s) {
113             Lit::CStr(lit) => {
114                 assert_eq!(*lit.value(), *value);
115                 let again = lit.into_token_stream().to_string();
116                 if again != s {
117                     test_c_string(&again, value);
118                 }
119             }
120             wrong => panic!("{:?}", wrong),
121         }
122     }
123 
124     test_c_string(r#"  c""  "#, c"");
125     test_c_string(r#"  c"a"  "#, c"a");
126     test_c_string(r#"  c"\n"  "#, c"\n");
127     test_c_string(r#"  c"\r"  "#, c"\r");
128     test_c_string(r#"  c"\t"  "#, c"\t");
129     test_c_string(r#"  c"\\"  "#, c"\\");
130     test_c_string(r#"  c"\'"  "#, c"'");
131     test_c_string(r#"  c"\""  "#, c"\"");
132     test_c_string(
133         "c\"contains\nnewlines\\\nescaped newlines\"",
134         c"contains\nnewlinesescaped newlines",
135     );
136     test_c_string("cr\"raw\nstring\\\nhere\"", c"raw\nstring\\\nhere");
137     test_c_string("c\"...\"q", c"...");
138     test_c_string("cr\"...\"", c"...");
139     test_c_string("cr##\"...\"##", c"...");
140     test_c_string(
141         r#"  c"hello\x80我叫\u{1F980}"  "#, // from the RFC
142         c"hello\x80我叫\u{1F980}",
143     );
144 }
145 
146 #[test]
bytes()147 fn bytes() {
148     #[track_caller]
149     fn test_byte(s: &str, value: u8) {
150         let s = s.trim();
151         match lit(s) {
152             Lit::Byte(lit) => {
153                 assert_eq!(lit.value(), value);
154                 let again = lit.into_token_stream().to_string();
155                 assert_eq!(again, s);
156             }
157             wrong => panic!("{:?}", wrong),
158         }
159     }
160 
161     test_byte(r#"  b'a'  "#, b'a');
162     test_byte(r#"  b'\n'  "#, b'\n');
163     test_byte(r#"  b'\r'  "#, b'\r');
164     test_byte(r#"  b'\t'  "#, b'\t');
165     test_byte(r#"  b'\''  "#, b'\'');
166     test_byte(r#"  b'"'  "#, b'"');
167     test_byte(r#"  b'a'q  "#, b'a');
168 }
169 
170 #[test]
chars()171 fn chars() {
172     #[track_caller]
173     fn test_char(s: &str, value: char) {
174         let s = s.trim();
175         match lit(s) {
176             Lit::Char(lit) => {
177                 assert_eq!(lit.value(), value);
178                 let again = lit.into_token_stream().to_string();
179                 if again != s {
180                     test_char(&again, value);
181                 }
182             }
183             wrong => panic!("{:?}", wrong),
184         }
185     }
186 
187     test_char(r#"  'a'  "#, 'a');
188     test_char(r#"  '\n'  "#, '\n');
189     test_char(r#"  '\r'  "#, '\r');
190     test_char(r#"  '\t'  "#, '\t');
191     test_char(r#"  '��'  "#, '��'); // NOTE: This is an emoji
192     test_char(r#"  '\''  "#, '\'');
193     test_char(r#"  '"'  "#, '"');
194     test_char(r#"  '\u{1F415}'  "#, '\u{1F415}');
195     test_char(r#"  'a'q  "#, 'a');
196 }
197 
198 #[test]
ints()199 fn ints() {
200     #[track_caller]
201     fn test_int(s: &str, value: u64, suffix: &str) {
202         match lit(s) {
203             Lit::Int(lit) => {
204                 assert_eq!(lit.base10_digits().parse::<u64>().unwrap(), value);
205                 assert_eq!(lit.suffix(), suffix);
206                 let again = lit.into_token_stream().to_string();
207                 if again != s {
208                     test_int(&again, value, suffix);
209                 }
210             }
211             wrong => panic!("{:?}", wrong),
212         }
213     }
214 
215     test_int("5", 5, "");
216     test_int("5u32", 5, "u32");
217     test_int("0E", 0, "E");
218     test_int("0ECMA", 0, "ECMA");
219     test_int("0o0A", 0, "A");
220     test_int("5_0", 50, "");
221     test_int("5_____0_____", 50, "");
222     test_int("0x7f", 127, "");
223     test_int("0x7F", 127, "");
224     test_int("0b1001", 9, "");
225     test_int("0o73", 59, "");
226     test_int("0x7Fu8", 127, "u8");
227     test_int("0b1001i8", 9, "i8");
228     test_int("0o73u32", 59, "u32");
229     test_int("0x__7___f_", 127, "");
230     test_int("0x__7___F_", 127, "");
231     test_int("0b_1_0__01", 9, "");
232     test_int("0o_7__3", 59, "");
233     test_int("0x_7F__u8", 127, "u8");
234     test_int("0b__10__0_1i8", 9, "i8");
235     test_int("0o__7__________________3u32", 59, "u32");
236     test_int("0e1\u{5c5}", 0, "e1\u{5c5}");
237 }
238 
239 #[test]
floats()240 fn floats() {
241     #[track_caller]
242     fn test_float(s: &str, value: f64, suffix: &str) {
243         match lit(s) {
244             Lit::Float(lit) => {
245                 assert_eq!(lit.base10_digits().parse::<f64>().unwrap(), value);
246                 assert_eq!(lit.suffix(), suffix);
247                 let again = lit.into_token_stream().to_string();
248                 if again != s {
249                     test_float(&again, value, suffix);
250                 }
251             }
252             wrong => panic!("{:?}", wrong),
253         }
254     }
255 
256     test_float("5.5", 5.5, "");
257     test_float("5.5E12", 5.5e12, "");
258     test_float("5.5e12", 5.5e12, "");
259     test_float("1.0__3e-12", 1.03e-12, "");
260     test_float("1.03e+12", 1.03e12, "");
261     test_float("9e99e99", 9e99, "e99");
262     test_float("1e_0", 1.0, "");
263     test_float("0.0ECMA", 0.0, "ECMA");
264 }
265 
266 #[test]
negative()267 fn negative() {
268     let span = Span::call_site();
269     assert_eq!("-1", LitInt::new("-1", span).to_string());
270     assert_eq!("-1i8", LitInt::new("-1i8", span).to_string());
271     assert_eq!("-1i16", LitInt::new("-1i16", span).to_string());
272     assert_eq!("-1i32", LitInt::new("-1i32", span).to_string());
273     assert_eq!("-1i64", LitInt::new("-1i64", span).to_string());
274     assert_eq!("-1.5", LitFloat::new("-1.5", span).to_string());
275     assert_eq!("-1.5f32", LitFloat::new("-1.5f32", span).to_string());
276     assert_eq!("-1.5f64", LitFloat::new("-1.5f64", span).to_string());
277 }
278 
279 #[test]
suffix()280 fn suffix() {
281     #[track_caller]
282     fn get_suffix(token: &str) -> String {
283         let lit = syn::parse_str::<Lit>(token).unwrap();
284         match lit {
285             Lit::Str(lit) => lit.suffix().to_owned(),
286             Lit::ByteStr(lit) => lit.suffix().to_owned(),
287             Lit::CStr(lit) => lit.suffix().to_owned(),
288             Lit::Byte(lit) => lit.suffix().to_owned(),
289             Lit::Char(lit) => lit.suffix().to_owned(),
290             Lit::Int(lit) => lit.suffix().to_owned(),
291             Lit::Float(lit) => lit.suffix().to_owned(),
292             _ => unimplemented!(),
293         }
294     }
295 
296     assert_eq!(get_suffix("\"\"s"), "s");
297     assert_eq!(get_suffix("r\"\"r"), "r");
298     assert_eq!(get_suffix("r#\"\"#r"), "r");
299     assert_eq!(get_suffix("b\"\"b"), "b");
300     assert_eq!(get_suffix("br\"\"br"), "br");
301     assert_eq!(get_suffix("br#\"\"#br"), "br");
302     assert_eq!(get_suffix("c\"\"c"), "c");
303     assert_eq!(get_suffix("cr\"\"cr"), "cr");
304     assert_eq!(get_suffix("cr#\"\"#cr"), "cr");
305     assert_eq!(get_suffix("'c'c"), "c");
306     assert_eq!(get_suffix("b'b'b"), "b");
307     assert_eq!(get_suffix("1i32"), "i32");
308     assert_eq!(get_suffix("1_i32"), "i32");
309     assert_eq!(get_suffix("1.0f32"), "f32");
310     assert_eq!(get_suffix("1.0_f32"), "f32");
311 }
312 
313 #[test]
test_deep_group_empty()314 fn test_deep_group_empty() {
315     let tokens = TokenStream::from_iter([TokenTree::Group(Group::new(
316         Delimiter::None,
317         TokenStream::from_iter([TokenTree::Group(Group::new(
318             Delimiter::None,
319             TokenStream::from_iter([TokenTree::Literal(Literal::string("hi"))]),
320         ))]),
321     ))]);
322 
323     snapshot!(tokens as Lit, @r#""hi""# );
324 }
325 
326 #[test]
test_error()327 fn test_error() {
328     let err = syn::parse_str::<LitStr>("...").unwrap_err();
329     assert_eq!("expected string literal", err.to_string());
330 
331     let err = syn::parse_str::<LitStr>("5").unwrap_err();
332     assert_eq!("expected string literal", err.to_string());
333 }
334