#![feature(proc_macro_quote)] #![feature(proc_macro_totokens)] extern crate proc_macro; use std::borrow::Cow; use std::collections::BTreeSet; use std::ffi::{CStr, CString}; use proc_macro::*; #[proc_macro] pub fn run_tests(_: TokenStream) -> TokenStream { test_quote_impl(); test_substitution(); test_iter(); test_array(); test_advanced(); test_integer(); test_floating(); test_char(); test_str(); test_string(); test_c_str(); test_c_string(); test_interpolated_literal(); test_ident(); test_underscore(); test_duplicate(); test_fancy_repetition(); test_nested_fancy_repetition(); test_duplicate_name_repetition(); test_duplicate_name_repetition_no_copy(); test_btreeset_repetition(); test_variable_name_conflict(); test_nonrep_in_repetition(); test_empty_quote(); test_box_str(); test_cow(); test_append_tokens(); test_outer_line_comment(); test_inner_line_comment(); test_outer_block_comment(); test_inner_block_comment(); test_outer_attr(); test_inner_attr(); test_star_after_repetition(); test_quote_raw_id(); TokenStream::new() } // Based on https://github.com/dtolnay/quote/blob/0245506323a3616daa2ee41c6ad0b871e4d78ae4/tests/test.rs // // FIXME(quote): // The following tests are removed because they are not supported yet in `proc_macro::quote!` // // - quote_spanned: // - fn test_quote_spanned_impl // - fn test_type_inference_for_span // - wrong-type-span.rs // - format_ident: // - fn test_closure // - fn test_format_ident // - fn test_format_ident_strip_raw struct X; impl ToTokens for X { fn to_tokens(&self, tokens: &mut TokenStream) { Ident::new("X", Span::call_site()).to_tokens(tokens) } } fn test_quote_impl() { let tokens = quote! { impl<'a, T: ToTokens> ToTokens for &'a T { fn to_tokens(&self, tokens: &mut TokenStream) { (**self).to_tokens(tokens) } } }; let expected = r#"impl < 'a, T : ToTokens > ToTokens for & 'a T { fn to_tokens(& self, tokens : & mut TokenStream) { (** self).to_tokens(tokens) } }"#; assert_eq!(expected, tokens.to_string()); } fn test_substitution() { let x = X; let tokens = quote!($x <$x> ($x) [$x] {$x}); let expected = "X (X) [X] { X }"; assert_eq!(expected, tokens.to_string()); } fn test_iter() { let primes = &[X, X, X, X]; assert_eq!("X X X X", quote!($($primes)*).to_string()); assert_eq!("X, X, X, X,", quote!($($primes,)*).to_string()); assert_eq!("X, X, X, X", quote!($($primes),*).to_string()); } fn test_array() { let array: [u8; 40] = [0; 40]; let _ = quote!($($array $array)*); let ref_array: &[u8; 40] = &[0; 40]; let _ = quote!($($ref_array $ref_array)*); let ref_slice: &[u8] = &[0; 40]; let _ = quote!($($ref_slice $ref_slice)*); let array: [X; 2] = [X, X]; // !Copy let _ = quote!($($array $array)*); let ref_array: &[X; 2] = &[X, X]; let _ = quote!($($ref_array $ref_array)*); let ref_slice: &[X] = &[X, X]; let _ = quote!($($ref_slice $ref_slice)*); let array_of_array: [[u8; 2]; 2] = [[0; 2]; 2]; let _ = quote!($($($array_of_array)*)*); } fn test_advanced() { let generics = quote!( <'a, T> ); let where_clause = quote!( where T: Serialize ); let field_ty = quote!(String); let item_ty = quote!(Cow<'a, str>); let path = quote!(SomeTrait::serialize_with); let value = quote!(self.x); let tokens = quote! { struct SerializeWith $generics $where_clause { value: &'a $field_ty, phantom: ::std::marker::PhantomData<$item_ty>, } impl $generics ::serde::Serialize for SerializeWith $generics $where_clause { fn serialize(&self, s: &mut S) -> Result<(), S::Error> where S: ::serde::Serializer { $path(self.value, s) } } SerializeWith { value: $value, phantom: ::std::marker::PhantomData::<$item_ty>, } }; let expected = r#"struct SerializeWith < 'a, T > where T : Serialize { value : & 'a String, phantom : :: std :: marker :: PhantomData >, } impl < 'a, T > :: serde :: Serialize for SerializeWith < 'a, T > where T : Serialize { fn serialize < S > (& self, s : & mut S) -> Result < (), S :: Error > where S : :: serde :: Serializer { SomeTrait :: serialize_with(self.value, s) } } SerializeWith { value : self.x, phantom : :: std :: marker :: PhantomData :: >, }"#; assert_eq!(expected, tokens.to_string()); } fn test_integer() { let ii8 = -1i8; let ii16 = -1i16; let ii32 = -1i32; let ii64 = -1i64; let ii128 = -1i128; let iisize = -1isize; let uu8 = 1u8; let uu16 = 1u16; let uu32 = 1u32; let uu64 = 1u64; let uu128 = 1u128; let uusize = 1usize; let tokens = quote! { 1 1i32 1u256 $ii8 $ii16 $ii32 $ii64 $ii128 $iisize $uu8 $uu16 $uu32 $uu64 $uu128 $uusize }; let expected = r#"1 1i32 1u256 -1i8 -1i16 -1i32 -1i64 -1i128 -1isize 1u8 1u16 1u32 1u64 1u128 1usize"#; assert_eq!(expected, tokens.to_string()); } fn test_floating() { let e32 = 2.345f32; let e64 = 2.345f64; let tokens = quote! { $e32 $e64 }; let expected = concat!("2.345f32 2.345f64"); assert_eq!(expected, tokens.to_string()); } fn test_char() { let zero = '\u{1}'; let dollar = '$'; let pound = '#'; let quote = '"'; let apost = '\''; let newline = '\n'; let heart = '\u{2764}'; let tokens = quote! { $zero $dollar $pound $quote $apost $newline $heart }; let expected = "'\\u{1}' '$' '#' '\"' '\\'' '\\n' '\u{2764}'"; assert_eq!(expected, tokens.to_string()); } fn test_str() { let s = "\u{1} a 'b \" c"; let tokens = quote!($s); let expected = "\"\\u{1} a 'b \\\" c\""; assert_eq!(expected, tokens.to_string()); } fn test_string() { let s = "\u{1} a 'b \" c".to_string(); let tokens = quote!($s); let expected = "\"\\u{1} a 'b \\\" c\""; assert_eq!(expected, tokens.to_string()); } fn test_c_str() { let s = CStr::from_bytes_with_nul(b"\x01 a 'b \" c\0").unwrap(); let tokens = quote!($s); let expected = "c\"\\u{1} a 'b \\\" c\""; assert_eq!(expected, tokens.to_string()); } fn test_c_string() { let s = CString::new(&b"\x01 a 'b \" c"[..]).unwrap(); let tokens = quote!($s); let expected = "c\"\\u{1} a 'b \\\" c\""; assert_eq!(expected, tokens.to_string()); } fn test_interpolated_literal() { macro_rules! m { ($literal:literal) => { quote!($literal) }; } let tokens = m!(1); let expected = "1"; assert_eq!(expected, tokens.to_string()); let tokens = m!(-1); let expected = "- 1"; assert_eq!(expected, tokens.to_string()); let tokens = m!(true); let expected = "true"; assert_eq!(expected, tokens.to_string()); let tokens = m!(-true); let expected = "- true"; assert_eq!(expected, tokens.to_string()); } fn test_ident() { let foo = Ident::new("Foo", Span::call_site()); let bar = Ident::new(&format!("Bar{}", 7), Span::call_site()); let tokens = quote!(struct $foo; enum $bar {}); let expected = "struct Foo; enum Bar7 {}"; assert_eq!(expected, tokens.to_string()); } fn test_underscore() { let tokens = quote!(let _;); let expected = "let _;"; assert_eq!(expected, tokens.to_string()); } fn test_duplicate() { let ch = 'x'; let tokens = quote!($ch $ch); let expected = "'x' 'x'"; assert_eq!(expected, tokens.to_string()); } fn test_fancy_repetition() { let foo = vec!["a", "b"]; let bar = vec![true, false]; let tokens = quote! { $($foo: $bar),* }; let expected = r#""a" : true, "b" : false"#; assert_eq!(expected, tokens.to_string()); } fn test_nested_fancy_repetition() { let nested = vec![vec!['a', 'b', 'c'], vec!['x', 'y', 'z']]; let tokens = quote! { $( $($nested)* ),* }; let expected = "'a' 'b' 'c', 'x' 'y' 'z'"; assert_eq!(expected, tokens.to_string()); } fn test_duplicate_name_repetition() { let foo = &["a", "b"]; let tokens = quote! { $($foo: $foo),* $($foo: $foo),* }; let expected = r#""a" : "a", "b" : "b" "a" : "a", "b" : "b""#; assert_eq!(expected, tokens.to_string()); } fn test_duplicate_name_repetition_no_copy() { let foo = vec!["a".to_owned(), "b".to_owned()]; let tokens = quote! { $($foo: $foo),* }; let expected = r#""a" : "a", "b" : "b""#; assert_eq!(expected, tokens.to_string()); } fn test_btreeset_repetition() { let mut set = BTreeSet::new(); set.insert("a".to_owned()); set.insert("b".to_owned()); let tokens = quote! { $($set: $set),* }; let expected = r#""a" : "a", "b" : "b""#; assert_eq!(expected, tokens.to_string()); } fn test_variable_name_conflict() { // The implementation of `#(...),*` uses the variable `_i` but it should be // fine, if a little confusing when debugging. let _i = vec!['a', 'b']; let tokens = quote! { $($_i),* }; let expected = "'a', 'b'"; assert_eq!(expected, tokens.to_string()); } fn test_nonrep_in_repetition() { let rep = vec!["a", "b"]; let nonrep = "c"; let tokens = quote! { $($rep $rep : $nonrep $nonrep),* }; let expected = r#""a" "a" : "c" "c", "b" "b" : "c" "c""#; assert_eq!(expected, tokens.to_string()); } fn test_empty_quote() { let tokens = quote!(); assert_eq!("", tokens.to_string()); } fn test_box_str() { let b = "str".to_owned().into_boxed_str(); let tokens = quote! { $b }; assert_eq!("\"str\"", tokens.to_string()); } fn test_cow() { let owned: Cow = Cow::Owned(Ident::new("owned", Span::call_site())); let ident = Ident::new("borrowed", Span::call_site()); let borrowed = Cow::Borrowed(&ident); let tokens = quote! { $owned $borrowed }; assert_eq!("owned borrowed", tokens.to_string()); } fn test_append_tokens() { let mut a = quote!(a); let b = quote!(b); a.extend(b); assert_eq!("a b", a.to_string()); } fn test_outer_line_comment() { let tokens = quote! { /// doc }; let expected = "#[doc = \" doc\"]"; assert_eq!(expected, tokens.to_string()); } fn test_inner_line_comment() { let tokens = quote! { //! doc }; let expected = "# ! [doc = \" doc\"]"; assert_eq!(expected, tokens.to_string()); } fn test_outer_block_comment() { let tokens = quote! { /** doc */ }; let expected = "#[doc = \" doc \"]"; assert_eq!(expected, tokens.to_string()); } fn test_inner_block_comment() { let tokens = quote! { /*! doc */ }; let expected = "# ! [doc = \" doc \"]"; assert_eq!(expected, tokens.to_string()); } fn test_outer_attr() { let tokens = quote! { #[inline] }; let expected = "#[inline]"; assert_eq!(expected, tokens.to_string()); } fn test_inner_attr() { let tokens = quote! { #![no_std] }; let expected = "#! [no_std]"; assert_eq!(expected, tokens.to_string()); } // https://github.com/dtolnay/quote/issues/130 fn test_star_after_repetition() { let c = vec!['0', '1']; let tokens = quote! { $( f($c); )* *out = None; }; let expected = "f('0'); f('1'); * out = None;"; assert_eq!(expected, tokens.to_string()); } fn test_quote_raw_id() { let id = quote!(r#raw_id); assert_eq!(id.to_string(), "r#raw_id"); }