1#![allow(clippy::uninlined_format_args)] 2 3#[macro_use] 4mod macros; 5 6use proc_macro2::{Delimiter, Group, Ident, Punct, Spacing, Span, TokenStream, TokenTree}; 7use quote::{quote, ToTokens as _}; 8use syn::punctuated::Punctuated; 9use syn::{parse_quote, token, Token, Type, TypeTuple}; 10 11#[test] 12fn test_mut_self() { 13 syn::parse_str::<Type>("fn(mut self)").unwrap(); 14 syn::parse_str::<Type>("fn(mut self,)").unwrap(); 15 syn::parse_str::<Type>("fn(mut self: ())").unwrap(); 16 syn::parse_str::<Type>("fn(mut self: ...)").unwrap_err(); 17 syn::parse_str::<Type>("fn(mut self: mut self)").unwrap_err(); 18 syn::parse_str::<Type>("fn(mut self::T)").unwrap_err(); 19} 20 21#[test] 22fn test_macro_variable_type() { 23 // mimics the token stream corresponding to `$ty<T>` 24 let tokens = TokenStream::from_iter(vec![ 25 TokenTree::Group(Group::new(Delimiter::None, quote! { ty })), 26 TokenTree::Punct(Punct::new('<', Spacing::Alone)), 27 TokenTree::Ident(Ident::new("T", Span::call_site())), 28 TokenTree::Punct(Punct::new('>', Spacing::Alone)), 29 ]); 30 31 snapshot!(tokens as Type, @r###" 32 Type::Path { 33 path: Path { 34 segments: [ 35 PathSegment { 36 ident: "ty", 37 arguments: PathArguments::AngleBracketed { 38 args: [ 39 GenericArgument::Type(Type::Path { 40 path: Path { 41 segments: [ 42 PathSegment { 43 ident: "T", 44 }, 45 ], 46 }, 47 }), 48 ], 49 }, 50 }, 51 ], 52 }, 53 } 54 "###); 55 56 // mimics the token stream corresponding to `$ty::<T>` 57 let tokens = TokenStream::from_iter(vec![ 58 TokenTree::Group(Group::new(Delimiter::None, quote! { ty })), 59 TokenTree::Punct(Punct::new(':', Spacing::Joint)), 60 TokenTree::Punct(Punct::new(':', Spacing::Alone)), 61 TokenTree::Punct(Punct::new('<', Spacing::Alone)), 62 TokenTree::Ident(Ident::new("T", Span::call_site())), 63 TokenTree::Punct(Punct::new('>', Spacing::Alone)), 64 ]); 65 66 snapshot!(tokens as Type, @r###" 67 Type::Path { 68 path: Path { 69 segments: [ 70 PathSegment { 71 ident: "ty", 72 arguments: PathArguments::AngleBracketed { 73 colon2_token: Some, 74 args: [ 75 GenericArgument::Type(Type::Path { 76 path: Path { 77 segments: [ 78 PathSegment { 79 ident: "T", 80 }, 81 ], 82 }, 83 }), 84 ], 85 }, 86 }, 87 ], 88 }, 89 } 90 "###); 91} 92 93#[test] 94fn test_group_angle_brackets() { 95 // mimics the token stream corresponding to `Option<$ty>` 96 let tokens = TokenStream::from_iter(vec![ 97 TokenTree::Ident(Ident::new("Option", Span::call_site())), 98 TokenTree::Punct(Punct::new('<', Spacing::Alone)), 99 TokenTree::Group(Group::new(Delimiter::None, quote! { Vec<u8> })), 100 TokenTree::Punct(Punct::new('>', Spacing::Alone)), 101 ]); 102 103 snapshot!(tokens as Type, @r###" 104 Type::Path { 105 path: Path { 106 segments: [ 107 PathSegment { 108 ident: "Option", 109 arguments: PathArguments::AngleBracketed { 110 args: [ 111 GenericArgument::Type(Type::Group { 112 elem: Type::Path { 113 path: Path { 114 segments: [ 115 PathSegment { 116 ident: "Vec", 117 arguments: PathArguments::AngleBracketed { 118 args: [ 119 GenericArgument::Type(Type::Path { 120 path: Path { 121 segments: [ 122 PathSegment { 123 ident: "u8", 124 }, 125 ], 126 }, 127 }), 128 ], 129 }, 130 }, 131 ], 132 }, 133 }, 134 }), 135 ], 136 }, 137 }, 138 ], 139 }, 140 } 141 "###); 142} 143 144#[test] 145fn test_group_colons() { 146 // mimics the token stream corresponding to `$ty::Item` 147 let tokens = TokenStream::from_iter(vec![ 148 TokenTree::Group(Group::new(Delimiter::None, quote! { Vec<u8> })), 149 TokenTree::Punct(Punct::new(':', Spacing::Joint)), 150 TokenTree::Punct(Punct::new(':', Spacing::Alone)), 151 TokenTree::Ident(Ident::new("Item", Span::call_site())), 152 ]); 153 154 snapshot!(tokens as Type, @r###" 155 Type::Path { 156 path: Path { 157 segments: [ 158 PathSegment { 159 ident: "Vec", 160 arguments: PathArguments::AngleBracketed { 161 args: [ 162 GenericArgument::Type(Type::Path { 163 path: Path { 164 segments: [ 165 PathSegment { 166 ident: "u8", 167 }, 168 ], 169 }, 170 }), 171 ], 172 }, 173 }, 174 Token![::], 175 PathSegment { 176 ident: "Item", 177 }, 178 ], 179 }, 180 } 181 "###); 182 183 let tokens = TokenStream::from_iter(vec![ 184 TokenTree::Group(Group::new(Delimiter::None, quote! { [T] })), 185 TokenTree::Punct(Punct::new(':', Spacing::Joint)), 186 TokenTree::Punct(Punct::new(':', Spacing::Alone)), 187 TokenTree::Ident(Ident::new("Element", Span::call_site())), 188 ]); 189 190 snapshot!(tokens as Type, @r###" 191 Type::Path { 192 qself: Some(QSelf { 193 ty: Type::Slice { 194 elem: Type::Path { 195 path: Path { 196 segments: [ 197 PathSegment { 198 ident: "T", 199 }, 200 ], 201 }, 202 }, 203 }, 204 position: 0, 205 }), 206 path: Path { 207 leading_colon: Some, 208 segments: [ 209 PathSegment { 210 ident: "Element", 211 }, 212 ], 213 }, 214 } 215 "###); 216} 217 218#[test] 219fn test_trait_object() { 220 let tokens = quote!(dyn for<'a> Trait<'a> + 'static); 221 snapshot!(tokens as Type, @r###" 222 Type::TraitObject { 223 dyn_token: Some, 224 bounds: [ 225 TypeParamBound::Trait(TraitBound { 226 lifetimes: Some(BoundLifetimes { 227 lifetimes: [ 228 GenericParam::Lifetime(LifetimeParam { 229 lifetime: Lifetime { 230 ident: "a", 231 }, 232 }), 233 ], 234 }), 235 path: Path { 236 segments: [ 237 PathSegment { 238 ident: "Trait", 239 arguments: PathArguments::AngleBracketed { 240 args: [ 241 GenericArgument::Lifetime(Lifetime { 242 ident: "a", 243 }), 244 ], 245 }, 246 }, 247 ], 248 }, 249 }), 250 Token![+], 251 TypeParamBound::Lifetime { 252 ident: "static", 253 }, 254 ], 255 } 256 "###); 257 258 let tokens = quote!(dyn 'a + Trait); 259 snapshot!(tokens as Type, @r###" 260 Type::TraitObject { 261 dyn_token: Some, 262 bounds: [ 263 TypeParamBound::Lifetime { 264 ident: "a", 265 }, 266 Token![+], 267 TypeParamBound::Trait(TraitBound { 268 path: Path { 269 segments: [ 270 PathSegment { 271 ident: "Trait", 272 }, 273 ], 274 }, 275 }), 276 ], 277 } 278 "###); 279 280 // None of the following are valid Rust types. 281 syn::parse_str::<Type>("for<'a> dyn Trait<'a>").unwrap_err(); 282 syn::parse_str::<Type>("dyn for<'a> 'a + Trait").unwrap_err(); 283} 284 285#[test] 286fn test_trailing_plus() { 287 #[rustfmt::skip] 288 let tokens = quote!(impl Trait +); 289 snapshot!(tokens as Type, @r###" 290 Type::ImplTrait { 291 bounds: [ 292 TypeParamBound::Trait(TraitBound { 293 path: Path { 294 segments: [ 295 PathSegment { 296 ident: "Trait", 297 }, 298 ], 299 }, 300 }), 301 Token![+], 302 ], 303 } 304 "###); 305 306 #[rustfmt::skip] 307 let tokens = quote!(dyn Trait +); 308 snapshot!(tokens as Type, @r###" 309 Type::TraitObject { 310 dyn_token: Some, 311 bounds: [ 312 TypeParamBound::Trait(TraitBound { 313 path: Path { 314 segments: [ 315 PathSegment { 316 ident: "Trait", 317 }, 318 ], 319 }, 320 }), 321 Token![+], 322 ], 323 } 324 "###); 325 326 #[rustfmt::skip] 327 let tokens = quote!(Trait +); 328 snapshot!(tokens as Type, @r###" 329 Type::TraitObject { 330 bounds: [ 331 TypeParamBound::Trait(TraitBound { 332 path: Path { 333 segments: [ 334 PathSegment { 335 ident: "Trait", 336 }, 337 ], 338 }, 339 }), 340 Token![+], 341 ], 342 } 343 "###); 344} 345 346#[test] 347fn test_tuple_comma() { 348 let mut expr = TypeTuple { 349 paren_token: token::Paren::default(), 350 elems: Punctuated::new(), 351 }; 352 snapshot!(expr.to_token_stream() as Type, @"Type::Tuple"); 353 354 expr.elems.push_value(parse_quote!(_)); 355 // Must not parse to Type::Paren 356 snapshot!(expr.to_token_stream() as Type, @r###" 357 Type::Tuple { 358 elems: [ 359 Type::Infer, 360 Token![,], 361 ], 362 } 363 "###); 364 365 expr.elems.push_punct(<Token![,]>::default()); 366 snapshot!(expr.to_token_stream() as Type, @r###" 367 Type::Tuple { 368 elems: [ 369 Type::Infer, 370 Token![,], 371 ], 372 } 373 "###); 374 375 expr.elems.push_value(parse_quote!(_)); 376 snapshot!(expr.to_token_stream() as Type, @r###" 377 Type::Tuple { 378 elems: [ 379 Type::Infer, 380 Token![,], 381 Type::Infer, 382 ], 383 } 384 "###); 385 386 expr.elems.push_punct(<Token![,]>::default()); 387 snapshot!(expr.to_token_stream() as Type, @r###" 388 Type::Tuple { 389 elems: [ 390 Type::Infer, 391 Token![,], 392 Type::Infer, 393 Token![,], 394 ], 395 } 396 "###); 397} 398