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] 9mod macros; 10 11use proc_macro2::{Delimiter, Group, Literal, Span, TokenStream, TokenTree}; 12use quote::ToTokens; 13use std::str::FromStr; 14use syn::{Lit, LitFloat, LitInt, LitStr}; 15 16fn 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] 28fn 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] 67fn 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] 99fn 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] 121fn 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] 147fn 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] 187fn 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] 213fn 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] 226fn 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] 254fn 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] 267fn 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