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