| 1 | #[macro_use ] |
| 2 | mod macros; |
| 3 | |
| 4 | use proc_macro2::{Delimiter, Group, Ident, Span, TokenStream, TokenTree}; |
| 5 | use quote::quote; |
| 6 | use std::iter::FromIterator; |
| 7 | use syn::{Item, ItemTrait}; |
| 8 | |
| 9 | #[test] |
| 10 | fn test_macro_variable_attr() { |
| 11 | // mimics the token stream corresponding to `$attr fn f() {}` |
| 12 | let tokens = TokenStream::from_iter(vec![ |
| 13 | TokenTree::Group(Group::new(Delimiter::None, quote! { #[test] })), |
| 14 | TokenTree::Ident(Ident::new("fn" , Span::call_site())), |
| 15 | TokenTree::Ident(Ident::new("f" , Span::call_site())), |
| 16 | TokenTree::Group(Group::new(Delimiter::Parenthesis, TokenStream::new())), |
| 17 | TokenTree::Group(Group::new(Delimiter::Brace, TokenStream::new())), |
| 18 | ]); |
| 19 | |
| 20 | snapshot!(tokens as Item, @r###" |
| 21 | Item::Fn { |
| 22 | attrs: [ |
| 23 | Attribute { |
| 24 | style: Outer, |
| 25 | path: Path { |
| 26 | segments: [ |
| 27 | PathSegment { |
| 28 | ident: "test", |
| 29 | arguments: None, |
| 30 | }, |
| 31 | ], |
| 32 | }, |
| 33 | tokens: TokenStream(``), |
| 34 | }, |
| 35 | ], |
| 36 | vis: Inherited, |
| 37 | sig: Signature { |
| 38 | ident: "f", |
| 39 | generics: Generics, |
| 40 | output: Default, |
| 41 | }, |
| 42 | block: Block, |
| 43 | } |
| 44 | "### ); |
| 45 | } |
| 46 | |
| 47 | #[test] |
| 48 | fn test_negative_impl() { |
| 49 | // Rustc parses all of the following. |
| 50 | |
| 51 | #[cfg (any())] |
| 52 | impl ! {} |
| 53 | let tokens = quote! { |
| 54 | impl ! {} |
| 55 | }; |
| 56 | snapshot!(tokens as Item, @r###" |
| 57 | Item::Impl { |
| 58 | generics: Generics, |
| 59 | self_ty: Type::Never, |
| 60 | } |
| 61 | "### ); |
| 62 | |
| 63 | #[cfg (any())] |
| 64 | #[rustfmt::skip] |
| 65 | impl !Trait {} |
| 66 | let tokens = quote! { |
| 67 | impl !Trait {} |
| 68 | }; |
| 69 | snapshot!(tokens as Item, @r###" |
| 70 | Item::Impl { |
| 71 | generics: Generics, |
| 72 | self_ty: Verbatim(`! Trait`), |
| 73 | } |
| 74 | "### ); |
| 75 | |
| 76 | #[cfg (any())] |
| 77 | impl !Trait for T {} |
| 78 | let tokens = quote! { |
| 79 | impl !Trait for T {} |
| 80 | }; |
| 81 | snapshot!(tokens as Item, @r###" |
| 82 | Item::Impl { |
| 83 | generics: Generics, |
| 84 | trait_: Some(( |
| 85 | Some, |
| 86 | Path { |
| 87 | segments: [ |
| 88 | PathSegment { |
| 89 | ident: "Trait", |
| 90 | arguments: None, |
| 91 | }, |
| 92 | ], |
| 93 | }, |
| 94 | )), |
| 95 | self_ty: Type::Path { |
| 96 | path: Path { |
| 97 | segments: [ |
| 98 | PathSegment { |
| 99 | ident: "T", |
| 100 | arguments: None, |
| 101 | }, |
| 102 | ], |
| 103 | }, |
| 104 | }, |
| 105 | } |
| 106 | "### ); |
| 107 | |
| 108 | #[cfg (any())] |
| 109 | #[rustfmt::skip] |
| 110 | impl !! {} |
| 111 | let tokens = quote! { |
| 112 | impl !! {} |
| 113 | }; |
| 114 | snapshot!(tokens as Item, @r###" |
| 115 | Item::Impl { |
| 116 | generics: Generics, |
| 117 | self_ty: Verbatim(`! !`), |
| 118 | } |
| 119 | "### ); |
| 120 | } |
| 121 | |
| 122 | #[test] |
| 123 | fn test_macro_variable_impl() { |
| 124 | // mimics the token stream corresponding to `impl $trait for $ty {}` |
| 125 | let tokens = TokenStream::from_iter(vec![ |
| 126 | TokenTree::Ident(Ident::new("impl" , Span::call_site())), |
| 127 | TokenTree::Group(Group::new(Delimiter::None, quote!(Trait))), |
| 128 | TokenTree::Ident(Ident::new("for" , Span::call_site())), |
| 129 | TokenTree::Group(Group::new(Delimiter::None, quote!(Type))), |
| 130 | TokenTree::Group(Group::new(Delimiter::Brace, TokenStream::new())), |
| 131 | ]); |
| 132 | |
| 133 | snapshot!(tokens as Item, @r###" |
| 134 | Item::Impl { |
| 135 | generics: Generics, |
| 136 | trait_: Some(( |
| 137 | None, |
| 138 | Path { |
| 139 | segments: [ |
| 140 | PathSegment { |
| 141 | ident: "Trait", |
| 142 | arguments: None, |
| 143 | }, |
| 144 | ], |
| 145 | }, |
| 146 | )), |
| 147 | self_ty: Type::Group { |
| 148 | elem: Type::Path { |
| 149 | path: Path { |
| 150 | segments: [ |
| 151 | PathSegment { |
| 152 | ident: "Type", |
| 153 | arguments: None, |
| 154 | }, |
| 155 | ], |
| 156 | }, |
| 157 | }, |
| 158 | }, |
| 159 | } |
| 160 | "### ); |
| 161 | } |
| 162 | |
| 163 | #[test] |
| 164 | fn test_supertraits() { |
| 165 | // Rustc parses all of the following. |
| 166 | |
| 167 | #[rustfmt::skip] |
| 168 | let tokens = quote!(trait Trait where {}); |
| 169 | snapshot!(tokens as ItemTrait, @r###" |
| 170 | ItemTrait { |
| 171 | vis: Inherited, |
| 172 | ident: "Trait", |
| 173 | generics: Generics { |
| 174 | where_clause: Some(WhereClause), |
| 175 | }, |
| 176 | } |
| 177 | "### ); |
| 178 | |
| 179 | #[rustfmt::skip] |
| 180 | let tokens = quote!(trait Trait: where {}); |
| 181 | snapshot!(tokens as ItemTrait, @r###" |
| 182 | ItemTrait { |
| 183 | vis: Inherited, |
| 184 | ident: "Trait", |
| 185 | generics: Generics { |
| 186 | where_clause: Some(WhereClause), |
| 187 | }, |
| 188 | colon_token: Some, |
| 189 | } |
| 190 | "### ); |
| 191 | |
| 192 | #[rustfmt::skip] |
| 193 | let tokens = quote!(trait Trait: Sized where {}); |
| 194 | snapshot!(tokens as ItemTrait, @r###" |
| 195 | ItemTrait { |
| 196 | vis: Inherited, |
| 197 | ident: "Trait", |
| 198 | generics: Generics { |
| 199 | where_clause: Some(WhereClause), |
| 200 | }, |
| 201 | colon_token: Some, |
| 202 | supertraits: [ |
| 203 | Trait(TraitBound { |
| 204 | modifier: None, |
| 205 | path: Path { |
| 206 | segments: [ |
| 207 | PathSegment { |
| 208 | ident: "Sized", |
| 209 | arguments: None, |
| 210 | }, |
| 211 | ], |
| 212 | }, |
| 213 | }), |
| 214 | ], |
| 215 | } |
| 216 | "### ); |
| 217 | |
| 218 | #[rustfmt::skip] |
| 219 | let tokens = quote!(trait Trait: Sized + where {}); |
| 220 | snapshot!(tokens as ItemTrait, @r###" |
| 221 | ItemTrait { |
| 222 | vis: Inherited, |
| 223 | ident: "Trait", |
| 224 | generics: Generics { |
| 225 | where_clause: Some(WhereClause), |
| 226 | }, |
| 227 | colon_token: Some, |
| 228 | supertraits: [ |
| 229 | Trait(TraitBound { |
| 230 | modifier: None, |
| 231 | path: Path { |
| 232 | segments: [ |
| 233 | PathSegment { |
| 234 | ident: "Sized", |
| 235 | arguments: None, |
| 236 | }, |
| 237 | ], |
| 238 | }, |
| 239 | }), |
| 240 | ], |
| 241 | } |
| 242 | "### ); |
| 243 | } |
| 244 | |
| 245 | #[test] |
| 246 | fn test_type_empty_bounds() { |
| 247 | #[rustfmt::skip] |
| 248 | let tokens = quote! { |
| 249 | trait Foo { |
| 250 | type Bar: ; |
| 251 | } |
| 252 | }; |
| 253 | |
| 254 | snapshot!(tokens as ItemTrait, @r###" |
| 255 | ItemTrait { |
| 256 | vis: Inherited, |
| 257 | ident: "Foo", |
| 258 | generics: Generics, |
| 259 | items: [ |
| 260 | TraitItem::Type { |
| 261 | ident: "Bar", |
| 262 | generics: Generics, |
| 263 | colon_token: Some, |
| 264 | }, |
| 265 | ], |
| 266 | } |
| 267 | "### ); |
| 268 | } |
| 269 | |
| 270 | #[test] |
| 271 | fn test_impl_visibility() { |
| 272 | let tokens = quote! { |
| 273 | pub default unsafe impl union {} |
| 274 | }; |
| 275 | |
| 276 | snapshot!(tokens as Item, @"Verbatim(`pub default unsafe impl union { }`)" ); |
| 277 | } |
| 278 | |
| 279 | #[test] |
| 280 | fn test_impl_type_parameter_defaults() { |
| 281 | #[cfg (any())] |
| 282 | impl<T = ()> () {} |
| 283 | let tokens = quote! { |
| 284 | impl<T = ()> () {} |
| 285 | }; |
| 286 | snapshot!(tokens as Item, @r###" |
| 287 | Item::Impl { |
| 288 | generics: Generics { |
| 289 | lt_token: Some, |
| 290 | params: [ |
| 291 | Type(TypeParam { |
| 292 | ident: "T", |
| 293 | eq_token: Some, |
| 294 | default: Some(Type::Tuple), |
| 295 | }), |
| 296 | ], |
| 297 | gt_token: Some, |
| 298 | }, |
| 299 | self_ty: Type::Tuple, |
| 300 | }"### ); |
| 301 | } |
| 302 | |
| 303 | #[test] |
| 304 | fn test_impl_trait_trailing_plus() { |
| 305 | let tokens = quote! { |
| 306 | fn f() -> impl Sized + {} |
| 307 | }; |
| 308 | |
| 309 | snapshot!(tokens as Item, @r###" |
| 310 | Item::Fn { |
| 311 | vis: Inherited, |
| 312 | sig: Signature { |
| 313 | ident: "f", |
| 314 | generics: Generics, |
| 315 | output: Type( |
| 316 | Type::ImplTrait { |
| 317 | bounds: [ |
| 318 | Trait(TraitBound { |
| 319 | modifier: None, |
| 320 | path: Path { |
| 321 | segments: [ |
| 322 | PathSegment { |
| 323 | ident: "Sized", |
| 324 | arguments: None, |
| 325 | }, |
| 326 | ], |
| 327 | }, |
| 328 | }), |
| 329 | ], |
| 330 | }, |
| 331 | ), |
| 332 | }, |
| 333 | block: Block, |
| 334 | } |
| 335 | "### ); |
| 336 | } |
| 337 | |