|
| 1 | +#![feature(proc_macro_quote)] |
| 2 | +#![feature(proc_macro_totokens)] |
| 3 | + |
| 4 | +extern crate proc_macro; |
| 5 | + |
| 6 | +use std::borrow::Cow; |
| 7 | +use std::ffi::{CStr, CString}; |
| 8 | + |
| 9 | +use proc_macro::*; |
| 10 | + |
| 11 | +#[proc_macro] |
| 12 | +pub fn run_tests(_: TokenStream) -> TokenStream { |
| 13 | + test_quote_impl(); |
| 14 | + test_substitution(); |
| 15 | + test_advanced(); |
| 16 | + test_integer(); |
| 17 | + test_floating(); |
| 18 | + test_char(); |
| 19 | + test_str(); |
| 20 | + test_string(); |
| 21 | + test_c_str(); |
| 22 | + test_c_string(); |
| 23 | + test_interpolated_literal(); |
| 24 | + test_ident(); |
| 25 | + test_underscore(); |
| 26 | + test_duplicate(); |
| 27 | + test_empty_quote(); |
| 28 | + test_box_str(); |
| 29 | + test_cow(); |
| 30 | + test_append_tokens(); |
| 31 | + test_outer_line_comment(); |
| 32 | + test_inner_line_comment(); |
| 33 | + test_outer_block_comment(); |
| 34 | + test_inner_block_comment(); |
| 35 | + test_outer_attr(); |
| 36 | + test_inner_attr(); |
| 37 | + // test_quote_raw_id(); // FIXME: Fix it in a subsequent commit |
| 38 | + |
| 39 | + TokenStream::new() |
| 40 | +} |
| 41 | + |
| 42 | +// Based on https://github.com/dtolnay/quote/blob/0245506323a3616daa2ee41c6ad0b871e4d78ae4/tests/test.rs |
| 43 | +// |
| 44 | +// FIXME(quote): |
| 45 | +// The following tests are removed because they are not supported yet in `proc_macro::quote!` |
| 46 | +// |
| 47 | +// - quote_spanned: |
| 48 | +// - fn test_quote_spanned_impl |
| 49 | +// - fn test_type_inference_for_span |
| 50 | +// - format_ident: |
| 51 | +// - fn test_format_ident |
| 52 | +// - fn test_format_ident_strip_raw |
| 53 | +// - repetition: |
| 54 | +// - fn test_iter |
| 55 | +// - fn test_array |
| 56 | +// - fn test_fancy_repetition |
| 57 | +// - fn test_nested_fancy_repetition |
| 58 | +// - fn test_duplicate_name_repetition |
| 59 | +// - fn test_duplicate_name_repetition_no_copy |
| 60 | +// - fn test_btreeset_repetition |
| 61 | +// - fn test_variable_name_conflict |
| 62 | +// - fn test_nonrep_in_repetition |
| 63 | +// - fn test_closure |
| 64 | +// - fn test_star_after_repetition |
| 65 | + |
| 66 | +struct X; |
| 67 | + |
| 68 | +impl ToTokens for X { |
| 69 | + fn to_tokens(&self, tokens: &mut TokenStream) { |
| 70 | + Ident::new("X", Span::call_site()).to_tokens(tokens) |
| 71 | + } |
| 72 | +} |
| 73 | + |
| 74 | +fn test_quote_impl() { |
| 75 | + let tokens = quote! { |
| 76 | + impl<'a, T: ToTokens> ToTokens for &'a T { |
| 77 | + fn to_tokens(&self, tokens: &mut TokenStream) { |
| 78 | + (**self).to_tokens(tokens) |
| 79 | + } |
| 80 | + } |
| 81 | + }; |
| 82 | + |
| 83 | + let expected = r#"impl < 'a, T : ToTokens > ToTokens for & 'a T |
| 84 | +{ |
| 85 | + fn to_tokens(& self, tokens : & mut TokenStream) |
| 86 | + { (** self).to_tokens(tokens) } |
| 87 | +}"#; |
| 88 | + |
| 89 | + assert_eq!(expected, tokens.to_string()); |
| 90 | +} |
| 91 | + |
| 92 | +fn test_substitution() { |
| 93 | + let x = X; |
| 94 | + let tokens = quote!($x <$x> ($x) [$x] {$x}); |
| 95 | + |
| 96 | + let expected = "X <X > (X) [X] { X }"; |
| 97 | + |
| 98 | + assert_eq!(expected, tokens.to_string()); |
| 99 | +} |
| 100 | + |
| 101 | +fn test_advanced() { |
| 102 | + let generics = quote!( <'a, T> ); |
| 103 | + |
| 104 | + let where_clause = quote!( where T: Serialize ); |
| 105 | + |
| 106 | + let field_ty = quote!(String); |
| 107 | + |
| 108 | + let item_ty = quote!(Cow<'a, str>); |
| 109 | + |
| 110 | + let path = quote!(SomeTrait::serialize_with); |
| 111 | + |
| 112 | + let value = quote!(self.x); |
| 113 | + |
| 114 | + let tokens = quote! { |
| 115 | + struct SerializeWith $generics $where_clause { |
| 116 | + value: &'a $field_ty, |
| 117 | + phantom: ::std::marker::PhantomData<$item_ty>, |
| 118 | + } |
| 119 | + |
| 120 | + impl $generics ::serde::Serialize for SerializeWith $generics $where_clause { |
| 121 | + fn serialize<S>(&self, s: &mut S) -> Result<(), S::Error> |
| 122 | + where S: ::serde::Serializer |
| 123 | + { |
| 124 | + $path(self.value, s) |
| 125 | + } |
| 126 | + } |
| 127 | + |
| 128 | + SerializeWith { |
| 129 | + value: $value, |
| 130 | + phantom: ::std::marker::PhantomData::<$item_ty>, |
| 131 | + } |
| 132 | + }; |
| 133 | + |
| 134 | + let expected = r#"struct SerializeWith < 'a, T > where T : Serialize |
| 135 | +{ |
| 136 | + value : & 'a String, phantom : :: std :: marker :: PhantomData <Cow < 'a, |
| 137 | + str > >, |
| 138 | +} impl < 'a, T > :: serde :: Serialize for SerializeWith < 'a, T > where T : |
| 139 | +Serialize |
| 140 | +{ |
| 141 | + fn serialize < S > (& self, s : & mut S) -> Result < (), S :: Error > |
| 142 | + where S : :: serde :: Serializer |
| 143 | + { SomeTrait :: serialize_with(self.value, s) } |
| 144 | +} SerializeWith |
| 145 | +{ |
| 146 | + value : self.x, phantom : :: std :: marker :: PhantomData ::<Cow < 'a, str |
| 147 | + > >, |
| 148 | +}"#; |
| 149 | + |
| 150 | + assert_eq!(expected, tokens.to_string()); |
| 151 | +} |
| 152 | + |
| 153 | +fn test_integer() { |
| 154 | + let ii8 = -1i8; |
| 155 | + let ii16 = -1i16; |
| 156 | + let ii32 = -1i32; |
| 157 | + let ii64 = -1i64; |
| 158 | + let ii128 = -1i128; |
| 159 | + let iisize = -1isize; |
| 160 | + let uu8 = 1u8; |
| 161 | + let uu16 = 1u16; |
| 162 | + let uu32 = 1u32; |
| 163 | + let uu64 = 1u64; |
| 164 | + let uu128 = 1u128; |
| 165 | + let uusize = 1usize; |
| 166 | + |
| 167 | + let tokens = quote! { |
| 168 | + 1 1i32 1u256 |
| 169 | + $ii8 $ii16 $ii32 $ii64 $ii128 $iisize |
| 170 | + $uu8 $uu16 $uu32 $uu64 $uu128 $uusize |
| 171 | + }; |
| 172 | + let expected = r#"1 1i32 1u256 -1i8 -1i16 -1i32 -1i64 -1i128 -1isize 1u8 1u16 1u32 1u64 1u128 |
| 173 | +1usize"#; |
| 174 | + assert_eq!(expected, tokens.to_string()); |
| 175 | +} |
| 176 | + |
| 177 | +fn test_floating() { |
| 178 | + let e32 = 2.345f32; |
| 179 | + |
| 180 | + let e64 = 2.345f64; |
| 181 | + |
| 182 | + let tokens = quote! { |
| 183 | + $e32 |
| 184 | + $e64 |
| 185 | + }; |
| 186 | + let expected = concat!("2.345f32 2.345f64"); |
| 187 | + assert_eq!(expected, tokens.to_string()); |
| 188 | +} |
| 189 | + |
| 190 | +fn test_char() { |
| 191 | + let zero = '\u{1}'; |
| 192 | + let dollar = '$'; |
| 193 | + let pound = '#'; |
| 194 | + let quote = '"'; |
| 195 | + let apost = '\''; |
| 196 | + let newline = '\n'; |
| 197 | + let heart = '\u{2764}'; |
| 198 | + |
| 199 | + let tokens = quote! { |
| 200 | + $zero $dollar $pound $quote $apost $newline $heart |
| 201 | + }; |
| 202 | + let expected = "'\\u{1}' '$' '#' '\"' '\\'' '\\n' '\u{2764}'"; |
| 203 | + assert_eq!(expected, tokens.to_string()); |
| 204 | +} |
| 205 | + |
| 206 | +fn test_str() { |
| 207 | + let s = "\u{1} a 'b \" c"; |
| 208 | + let tokens = quote!($s); |
| 209 | + let expected = "\"\\u{1} a 'b \\\" c\""; |
| 210 | + assert_eq!(expected, tokens.to_string()); |
| 211 | +} |
| 212 | + |
| 213 | +fn test_string() { |
| 214 | + let s = "\u{1} a 'b \" c".to_string(); |
| 215 | + let tokens = quote!($s); |
| 216 | + let expected = "\"\\u{1} a 'b \\\" c\""; |
| 217 | + assert_eq!(expected, tokens.to_string()); |
| 218 | +} |
| 219 | + |
| 220 | +fn test_c_str() { |
| 221 | + let s = CStr::from_bytes_with_nul(b"\x01 a 'b \" c\0").unwrap(); |
| 222 | + let tokens = quote!($s); |
| 223 | + let expected = "c\"\\u{1} a 'b \\\" c\""; |
| 224 | + assert_eq!(expected, tokens.to_string()); |
| 225 | +} |
| 226 | + |
| 227 | +fn test_c_string() { |
| 228 | + let s = CString::new(&b"\x01 a 'b \" c"[..]).unwrap(); |
| 229 | + let tokens = quote!($s); |
| 230 | + let expected = "c\"\\u{1} a 'b \\\" c\""; |
| 231 | + assert_eq!(expected, tokens.to_string()); |
| 232 | +} |
| 233 | + |
| 234 | +fn test_interpolated_literal() { |
| 235 | + macro_rules! m { |
| 236 | + ($literal:literal) => { |
| 237 | + quote!($literal) |
| 238 | + }; |
| 239 | + } |
| 240 | + |
| 241 | + let tokens = m!(1); |
| 242 | + let expected = "1"; |
| 243 | + assert_eq!(expected, tokens.to_string()); |
| 244 | + |
| 245 | + let tokens = m!(-1); |
| 246 | + let expected = "- 1"; |
| 247 | + assert_eq!(expected, tokens.to_string()); |
| 248 | + |
| 249 | + let tokens = m!(true); |
| 250 | + let expected = "true"; |
| 251 | + assert_eq!(expected, tokens.to_string()); |
| 252 | + |
| 253 | + let tokens = m!(-true); |
| 254 | + let expected = "- true"; |
| 255 | + assert_eq!(expected, tokens.to_string()); |
| 256 | +} |
| 257 | + |
| 258 | +fn test_ident() { |
| 259 | + let foo = Ident::new("Foo", Span::call_site()); |
| 260 | + let bar = Ident::new(&format!("Bar{}", 7), Span::call_site()); |
| 261 | + let tokens = quote!(struct $foo; enum $bar {}); |
| 262 | + let expected = "struct Foo; enum Bar7 {}"; |
| 263 | + assert_eq!(expected, tokens.to_string()); |
| 264 | +} |
| 265 | + |
| 266 | +fn test_underscore() { |
| 267 | + let tokens = quote!(let _;); |
| 268 | + let expected = "let _;"; |
| 269 | + assert_eq!(expected, tokens.to_string()); |
| 270 | +} |
| 271 | + |
| 272 | +fn test_duplicate() { |
| 273 | + let ch = 'x'; |
| 274 | + |
| 275 | + let tokens = quote!($ch $ch); |
| 276 | + |
| 277 | + let expected = "'x' 'x'"; |
| 278 | + assert_eq!(expected, tokens.to_string()); |
| 279 | +} |
| 280 | + |
| 281 | +fn test_empty_quote() { |
| 282 | + let tokens = quote!(); |
| 283 | + assert_eq!("", tokens.to_string()); |
| 284 | +} |
| 285 | + |
| 286 | +fn test_box_str() { |
| 287 | + let b = "str".to_owned().into_boxed_str(); |
| 288 | + let tokens = quote! { $b }; |
| 289 | + assert_eq!("\"str\"", tokens.to_string()); |
| 290 | +} |
| 291 | + |
| 292 | +fn test_cow() { |
| 293 | + let owned: Cow<Ident> = Cow::Owned(Ident::new("owned", Span::call_site())); |
| 294 | + |
| 295 | + let ident = Ident::new("borrowed", Span::call_site()); |
| 296 | + let borrowed = Cow::Borrowed(&ident); |
| 297 | + |
| 298 | + let tokens = quote! { $owned $borrowed }; |
| 299 | + assert_eq!("owned borrowed", tokens.to_string()); |
| 300 | +} |
| 301 | + |
| 302 | +fn test_append_tokens() { |
| 303 | + let mut a = quote!(a); |
| 304 | + let b = quote!(b); |
| 305 | + a.extend(b); |
| 306 | + assert_eq!("a b", a.to_string()); |
| 307 | +} |
| 308 | + |
| 309 | +fn test_outer_line_comment() { |
| 310 | + let tokens = quote! { |
| 311 | + /// doc |
| 312 | + }; |
| 313 | + let expected = "#[doc = \" doc\"]"; |
| 314 | + assert_eq!(expected, tokens.to_string()); |
| 315 | +} |
| 316 | + |
| 317 | +fn test_inner_line_comment() { |
| 318 | + let tokens = quote! { |
| 319 | + //! doc |
| 320 | + }; |
| 321 | + let expected = "# ! [doc = \" doc\"]"; |
| 322 | + assert_eq!(expected, tokens.to_string()); |
| 323 | +} |
| 324 | + |
| 325 | +fn test_outer_block_comment() { |
| 326 | + let tokens = quote! { |
| 327 | + /** doc */ |
| 328 | + }; |
| 329 | + let expected = "#[doc = \" doc \"]"; |
| 330 | + assert_eq!(expected, tokens.to_string()); |
| 331 | +} |
| 332 | + |
| 333 | +fn test_inner_block_comment() { |
| 334 | + let tokens = quote! { |
| 335 | + /*! doc */ |
| 336 | + }; |
| 337 | + let expected = "# ! [doc = \" doc \"]"; |
| 338 | + assert_eq!(expected, tokens.to_string()); |
| 339 | +} |
| 340 | + |
| 341 | +fn test_outer_attr() { |
| 342 | + let tokens = quote! { |
| 343 | + #[inline] |
| 344 | + }; |
| 345 | + let expected = "#[inline]"; |
| 346 | + assert_eq!(expected, tokens.to_string()); |
| 347 | +} |
| 348 | + |
| 349 | +fn test_inner_attr() { |
| 350 | + let tokens = quote! { |
| 351 | + #![no_std] |
| 352 | + }; |
| 353 | + let expected = "#! [no_std]"; |
| 354 | + assert_eq!(expected, tokens.to_string()); |
| 355 | +} |
| 356 | + |
| 357 | +fn test_quote_raw_id() { |
| 358 | + let id = quote!(r#raw_id); |
| 359 | + assert_eq!(id.to_string(), "r#raw_id"); |
| 360 | +} |
0 commit comments