David Tolnay | e767892 | 2016-10-13 20:44:03 -0700 | [diff] [blame] | 1 | extern crate quote; |
David Tolnay | 7d8b331 | 2019-03-10 01:26:11 -0800 | [diff] [blame] | 2 | extern crate syn; |
David Tolnay | b153dbc | 2016-10-04 23:39:10 -0700 | [diff] [blame] | 3 | |
David Tolnay | 7d8b331 | 2019-03-10 01:26:11 -0800 | [diff] [blame] | 4 | mod features; |
Alex Crichton | 605643b | 2017-07-05 18:35:14 -0700 | [diff] [blame] | 5 | |
David Tolnay | dd12556 | 2017-12-31 02:16:22 -0500 | [diff] [blame] | 6 | #[macro_use] |
| 7 | mod macros; |
| 8 | |
David Tolnay | 7d8b331 | 2019-03-10 01:26:11 -0800 | [diff] [blame] | 9 | use quote::quote; |
| 10 | use syn::{DeriveInput, ItemFn, TypeParamBound, WhereClause, WherePredicate}; |
Alex Crichton | eed4bc7 | 2018-05-17 10:59:15 -0700 | [diff] [blame] | 11 | |
David Tolnay | b153dbc | 2016-10-04 23:39:10 -0700 | [diff] [blame] | 12 | #[test] |
| 13 | fn test_split_for_impl() { |
David Tolnay | fcd53cf | 2019-05-09 13:06:59 -0700 | [diff] [blame] | 14 | let input = quote! { |
David Tolnay | 7d8b331 | 2019-03-10 01:26:11 -0800 | [diff] [blame] | 15 | struct S<'a, 'b: 'a, #[may_dangle] T: 'a = ()> where T: Debug; |
David Tolnay | b153dbc | 2016-10-04 23:39:10 -0700 | [diff] [blame] | 16 | }; |
| 17 | |
David Tolnay | fcd53cf | 2019-05-09 13:06:59 -0700 | [diff] [blame] | 18 | snapshot!(input as DeriveInput, @r###" |
| 19 | ⋮DeriveInput { |
| 20 | ⋮ vis: Inherited, |
| 21 | ⋮ ident: "S", |
| 22 | ⋮ generics: Generics { |
| 23 | ⋮ lt_token: Some, |
| 24 | ⋮ params: [ |
| 25 | ⋮ Lifetime(LifetimeDef { |
| 26 | ⋮ lifetime: Lifetime { |
| 27 | ⋮ ident: "a", |
| 28 | ⋮ }, |
| 29 | ⋮ }), |
| 30 | ⋮ Lifetime(LifetimeDef { |
| 31 | ⋮ lifetime: Lifetime { |
| 32 | ⋮ ident: "b", |
| 33 | ⋮ }, |
| 34 | ⋮ colon_token: Some, |
| 35 | ⋮ bounds: [ |
| 36 | ⋮ Lifetime { |
| 37 | ⋮ ident: "a", |
| 38 | ⋮ }, |
| 39 | ⋮ ], |
| 40 | ⋮ }), |
| 41 | ⋮ Type(TypeParam { |
| 42 | ⋮ attrs: [ |
| 43 | ⋮ Attribute { |
| 44 | ⋮ style: Outer, |
| 45 | ⋮ path: Path { |
| 46 | ⋮ segments: [ |
| 47 | ⋮ PathSegment { |
| 48 | ⋮ ident: "may_dangle", |
| 49 | ⋮ arguments: None, |
| 50 | ⋮ }, |
| 51 | ⋮ ], |
| 52 | ⋮ }, |
| 53 | ⋮ tts: ``, |
| 54 | ⋮ }, |
| 55 | ⋮ ], |
| 56 | ⋮ ident: "T", |
| 57 | ⋮ colon_token: Some, |
| 58 | ⋮ bounds: [ |
| 59 | ⋮ Lifetime(Lifetime { |
| 60 | ⋮ ident: "a", |
| 61 | ⋮ }), |
| 62 | ⋮ ], |
| 63 | ⋮ eq_token: Some, |
| 64 | ⋮ default: Some(Type::Tuple), |
| 65 | ⋮ }), |
| 66 | ⋮ ], |
| 67 | ⋮ gt_token: Some, |
| 68 | ⋮ where_clause: Some(WhereClause { |
| 69 | ⋮ predicates: [ |
| 70 | ⋮ Type(PredicateType { |
| 71 | ⋮ bounded_ty: Type::Path { |
| 72 | ⋮ path: Path { |
| 73 | ⋮ segments: [ |
| 74 | ⋮ PathSegment { |
| 75 | ⋮ ident: "T", |
| 76 | ⋮ arguments: None, |
| 77 | ⋮ }, |
| 78 | ⋮ ], |
| 79 | ⋮ }, |
| 80 | ⋮ }, |
| 81 | ⋮ bounds: [ |
| 82 | ⋮ Trait(TraitBound { |
| 83 | ⋮ modifier: None, |
| 84 | ⋮ path: Path { |
| 85 | ⋮ segments: [ |
| 86 | ⋮ PathSegment { |
| 87 | ⋮ ident: "Debug", |
| 88 | ⋮ arguments: None, |
| 89 | ⋮ }, |
| 90 | ⋮ ], |
| 91 | ⋮ }, |
| 92 | ⋮ }), |
| 93 | ⋮ ], |
| 94 | ⋮ }), |
| 95 | ⋮ ], |
| 96 | ⋮ }), |
| 97 | ⋮ }, |
| 98 | ⋮ data: Data::Struct { |
| 99 | ⋮ fields: Unit, |
| 100 | ⋮ semi_token: Some, |
| 101 | ⋮ }, |
| 102 | ⋮} |
| 103 | "###); |
David Tolnay | 7d8b331 | 2019-03-10 01:26:11 -0800 | [diff] [blame] | 104 | |
David Tolnay | fcd53cf | 2019-05-09 13:06:59 -0700 | [diff] [blame] | 105 | let generics = input.generics; |
David Tolnay | b153dbc | 2016-10-04 23:39:10 -0700 | [diff] [blame] | 106 | let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); |
David Tolnay | 7d8b331 | 2019-03-10 01:26:11 -0800 | [diff] [blame] | 107 | |
| 108 | let generated = quote! { |
David Tolnay | e767892 | 2016-10-13 20:44:03 -0700 | [diff] [blame] | 109 | impl #impl_generics MyTrait for Test #ty_generics #where_clause {} |
David Tolnay | b153dbc | 2016-10-04 23:39:10 -0700 | [diff] [blame] | 110 | }; |
David Tolnay | 7d8b331 | 2019-03-10 01:26:11 -0800 | [diff] [blame] | 111 | let expected = quote! { |
| 112 | impl<'a, 'b: 'a, #[may_dangle] T: 'a> MyTrait |
| 113 | for Test<'a, 'b, T> |
| 114 | where |
| 115 | T: Debug |
| 116 | {} |
| 117 | }; |
| 118 | assert_eq!(generated.to_string(), expected.to_string()); |
David Tolnay | b153dbc | 2016-10-04 23:39:10 -0700 | [diff] [blame] | 119 | |
David Tolnay | c879a50 | 2017-01-25 15:51:32 -0800 | [diff] [blame] | 120 | let turbofish = ty_generics.as_turbofish(); |
David Tolnay | 7d8b331 | 2019-03-10 01:26:11 -0800 | [diff] [blame] | 121 | let generated = quote! { |
David Tolnay | c879a50 | 2017-01-25 15:51:32 -0800 | [diff] [blame] | 122 | Test #turbofish |
| 123 | }; |
David Tolnay | 7d8b331 | 2019-03-10 01:26:11 -0800 | [diff] [blame] | 124 | let expected = quote! { |
| 125 | Test::<'a, 'b, T> |
| 126 | }; |
| 127 | assert_eq!(generated.to_string(), expected.to_string()); |
David Tolnay | b153dbc | 2016-10-04 23:39:10 -0700 | [diff] [blame] | 128 | } |
David Tolnay | 23d83f9 | 2017-01-25 15:41:47 -0800 | [diff] [blame] | 129 | |
| 130 | #[test] |
| 131 | fn test_ty_param_bound() { |
| 132 | let tokens = quote!('a); |
David Tolnay | fcd53cf | 2019-05-09 13:06:59 -0700 | [diff] [blame] | 133 | snapshot!(tokens as TypeParamBound, @r###" |
| 134 | ⋮Lifetime(Lifetime { |
| 135 | ⋮ ident: "a", |
| 136 | ⋮}) |
| 137 | "###); |
David Tolnay | 23d83f9 | 2017-01-25 15:41:47 -0800 | [diff] [blame] | 138 | |
Bastien Orivel | 340553a | 2018-02-15 23:57:38 +0100 | [diff] [blame] | 139 | let tokens = quote!('_); |
David Tolnay | fcd53cf | 2019-05-09 13:06:59 -0700 | [diff] [blame] | 140 | snapshot!(tokens as TypeParamBound, @r###" |
| 141 | ⋮Lifetime(Lifetime { |
| 142 | ⋮ ident: "_", |
| 143 | ⋮}) |
| 144 | "###); |
Bastien Orivel | 340553a | 2018-02-15 23:57:38 +0100 | [diff] [blame] | 145 | |
David Tolnay | 23d83f9 | 2017-01-25 15:41:47 -0800 | [diff] [blame] | 146 | let tokens = quote!(Debug); |
David Tolnay | fcd53cf | 2019-05-09 13:06:59 -0700 | [diff] [blame] | 147 | snapshot!(tokens as TypeParamBound, @r###" |
| 148 | ⋮Trait(TraitBound { |
| 149 | ⋮ modifier: None, |
| 150 | ⋮ path: Path { |
| 151 | ⋮ segments: [ |
| 152 | ⋮ PathSegment { |
| 153 | ⋮ ident: "Debug", |
| 154 | ⋮ arguments: None, |
| 155 | ⋮ }, |
| 156 | ⋮ ], |
| 157 | ⋮ }, |
| 158 | ⋮}) |
| 159 | "###); |
David Tolnay | 23d83f9 | 2017-01-25 15:41:47 -0800 | [diff] [blame] | 160 | |
| 161 | let tokens = quote!(?Sized); |
David Tolnay | fcd53cf | 2019-05-09 13:06:59 -0700 | [diff] [blame] | 162 | snapshot!(tokens as TypeParamBound, @r###" |
| 163 | ⋮Trait(TraitBound { |
| 164 | ⋮ modifier: Maybe, |
| 165 | ⋮ path: Path { |
| 166 | ⋮ segments: [ |
| 167 | ⋮ PathSegment { |
| 168 | ⋮ ident: "Sized", |
| 169 | ⋮ arguments: None, |
| 170 | ⋮ }, |
| 171 | ⋮ ], |
| 172 | ⋮ }, |
| 173 | ⋮}) |
| 174 | "###); |
David Tolnay | 23d83f9 | 2017-01-25 15:41:47 -0800 | [diff] [blame] | 175 | } |
Geoffry Song | ac02b18 | 2018-05-19 22:11:31 -0700 | [diff] [blame] | 176 | |
| 177 | #[test] |
| 178 | fn test_fn_precedence_in_where_clause() { |
David Tolnay | c8b0e0f | 2019-03-07 22:46:32 -0800 | [diff] [blame] | 179 | // This should parse as two separate bounds, `FnOnce() -> i32` and `Send` - not |
| 180 | // `FnOnce() -> (i32 + Send)`. |
David Tolnay | fcd53cf | 2019-05-09 13:06:59 -0700 | [diff] [blame] | 181 | let input = quote! { |
David Tolnay | 65fb566 | 2018-05-20 20:02:28 -0700 | [diff] [blame] | 182 | fn f<G>() |
| 183 | where |
| 184 | G: FnOnce() -> i32 + Send, |
| 185 | { |
| 186 | } |
| 187 | }; |
David Tolnay | 7d8b331 | 2019-03-10 01:26:11 -0800 | [diff] [blame] | 188 | |
David Tolnay | fcd53cf | 2019-05-09 13:06:59 -0700 | [diff] [blame] | 189 | snapshot!(input as ItemFn, @r###" |
| 190 | ⋮ItemFn { |
| 191 | ⋮ vis: Inherited, |
| 192 | ⋮ ident: "f", |
| 193 | ⋮ decl: FnDecl { |
| 194 | ⋮ generics: Generics { |
| 195 | ⋮ lt_token: Some, |
| 196 | ⋮ params: [ |
| 197 | ⋮ Type(TypeParam { |
| 198 | ⋮ ident: "G", |
| 199 | ⋮ }), |
| 200 | ⋮ ], |
| 201 | ⋮ gt_token: Some, |
| 202 | ⋮ where_clause: Some(WhereClause { |
| 203 | ⋮ predicates: [ |
| 204 | ⋮ Type(PredicateType { |
| 205 | ⋮ bounded_ty: Type::Path { |
| 206 | ⋮ path: Path { |
| 207 | ⋮ segments: [ |
| 208 | ⋮ PathSegment { |
| 209 | ⋮ ident: "G", |
| 210 | ⋮ arguments: None, |
| 211 | ⋮ }, |
| 212 | ⋮ ], |
| 213 | ⋮ }, |
| 214 | ⋮ }, |
| 215 | ⋮ bounds: [ |
| 216 | ⋮ Trait(TraitBound { |
| 217 | ⋮ modifier: None, |
| 218 | ⋮ path: Path { |
| 219 | ⋮ segments: [ |
| 220 | ⋮ PathSegment { |
| 221 | ⋮ ident: "FnOnce", |
| 222 | ⋮ arguments: PathArguments::Parenthesized { |
| 223 | ⋮ output: Type( |
| 224 | ⋮ Type::Path { |
| 225 | ⋮ path: Path { |
| 226 | ⋮ segments: [ |
| 227 | ⋮ PathSegment { |
| 228 | ⋮ ident: "i32", |
| 229 | ⋮ arguments: None, |
| 230 | ⋮ }, |
| 231 | ⋮ ], |
| 232 | ⋮ }, |
| 233 | ⋮ }, |
| 234 | ⋮ ), |
| 235 | ⋮ }, |
| 236 | ⋮ }, |
| 237 | ⋮ ], |
| 238 | ⋮ }, |
| 239 | ⋮ }), |
| 240 | ⋮ Trait(TraitBound { |
| 241 | ⋮ modifier: None, |
| 242 | ⋮ path: Path { |
| 243 | ⋮ segments: [ |
| 244 | ⋮ PathSegment { |
| 245 | ⋮ ident: "Send", |
| 246 | ⋮ arguments: None, |
| 247 | ⋮ }, |
| 248 | ⋮ ], |
| 249 | ⋮ }, |
| 250 | ⋮ }), |
| 251 | ⋮ ], |
| 252 | ⋮ }), |
| 253 | ⋮ ], |
| 254 | ⋮ }), |
| 255 | ⋮ }, |
| 256 | ⋮ output: Default, |
| 257 | ⋮ }, |
| 258 | ⋮ block: Block, |
| 259 | ⋮} |
| 260 | "###); |
David Tolnay | 7d8b331 | 2019-03-10 01:26:11 -0800 | [diff] [blame] | 261 | |
David Tolnay | fcd53cf | 2019-05-09 13:06:59 -0700 | [diff] [blame] | 262 | let where_clause = input.decl.generics.where_clause.as_ref().unwrap(); |
Geoffry Song | ac02b18 | 2018-05-19 22:11:31 -0700 | [diff] [blame] | 263 | assert_eq!(where_clause.predicates.len(), 1); |
David Tolnay | 7d8b331 | 2019-03-10 01:26:11 -0800 | [diff] [blame] | 264 | |
| 265 | let predicate = match &where_clause.predicates[0] { |
| 266 | WherePredicate::Type(pred) => pred, |
Geoffry Song | ac02b18 | 2018-05-19 22:11:31 -0700 | [diff] [blame] | 267 | _ => panic!("wrong predicate kind"), |
| 268 | }; |
David Tolnay | 7d8b331 | 2019-03-10 01:26:11 -0800 | [diff] [blame] | 269 | |
Geoffry Song | ac02b18 | 2018-05-19 22:11:31 -0700 | [diff] [blame] | 270 | assert_eq!(predicate.bounds.len(), 2, "{:#?}", predicate.bounds); |
David Tolnay | 7d8b331 | 2019-03-10 01:26:11 -0800 | [diff] [blame] | 271 | |
Geoffry Song | ac02b18 | 2018-05-19 22:11:31 -0700 | [diff] [blame] | 272 | let first_bound = &predicate.bounds[0]; |
| 273 | assert_eq!(quote!(#first_bound).to_string(), "FnOnce ( ) -> i32"); |
David Tolnay | 7d8b331 | 2019-03-10 01:26:11 -0800 | [diff] [blame] | 274 | |
Geoffry Song | ac02b18 | 2018-05-19 22:11:31 -0700 | [diff] [blame] | 275 | let second_bound = &predicate.bounds[1]; |
| 276 | assert_eq!(quote!(#second_bound).to_string(), "Send"); |
| 277 | } |
David Tolnay | 38012de | 2018-09-02 13:32:47 -0700 | [diff] [blame] | 278 | |
| 279 | #[test] |
| 280 | fn test_where_clause_at_end_of_input() { |
David Tolnay | fcd53cf | 2019-05-09 13:06:59 -0700 | [diff] [blame] | 281 | let input = quote! { |
David Tolnay | 38012de | 2018-09-02 13:32:47 -0700 | [diff] [blame] | 282 | where |
| 283 | }; |
David Tolnay | 7d8b331 | 2019-03-10 01:26:11 -0800 | [diff] [blame] | 284 | |
David Tolnay | fcd53cf | 2019-05-09 13:06:59 -0700 | [diff] [blame] | 285 | snapshot!(input as WhereClause, @"WhereClause"); |
| 286 | |
| 287 | assert_eq!(input.predicates.len(), 0); |
David Tolnay | 38012de | 2018-09-02 13:32:47 -0700 | [diff] [blame] | 288 | } |