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 | |
16 | 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] |
28 | 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] |
67 | 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] |
99 | 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] |
121 | 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] |
147 | 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] |
187 | 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] |
213 | 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] |
226 | 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] |
254 | 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] |
267 | 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 | |