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