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