blob: 09651e2b18ae660ded0229e0d1c617a08d502c49 [file] [log] [blame]
David Tolnay056de302018-01-05 14:29:05 -08001use punctuated::Punctuated;
2use super::*;
3
4ast_struct! {
5 /// A "Path" is essentially Rust's notion of a name.
6 ///
7 /// It's represented as a sequence of identifiers,
8 /// along with a bunch of supporting information.
9 ///
10 /// E.g. `std::cmp::PartialEq`
11 pub struct Path {
12 /// A `::foo` path, is relative to the crate root rather than current
13 /// module (like paths in an import).
14 pub leading_colon: Option<Token![::]>,
15 /// The segments in the path: the things separated by `::`.
16 pub segments: Punctuated<PathSegment, Token![::]>,
17 }
18}
19
20impl Path {
21 pub fn global(&self) -> bool {
22 self.leading_colon.is_some()
23 }
24}
25
26#[cfg(feature = "printing")]
27#[cfg_attr(feature = "extra-traits", derive(Debug, Eq, PartialEq, Hash))]
28#[cfg_attr(feature = "clone-impls", derive(Clone))]
29pub struct PathTokens<'a>(pub &'a Option<QSelf>, pub &'a Path);
30
31impl<T> From<T> for Path
32where
33 T: Into<PathSegment>,
34{
35 fn from(segment: T) -> Self {
36 let mut path = Path {
37 leading_colon: None,
38 segments: Punctuated::new(),
39 };
40 path.segments.push_item(segment.into());
41 path
42 }
43}
44
45ast_struct! {
46 /// A segment of a path: an identifier, an optional lifetime, and a set of types.
47 ///
48 /// E.g. `std`, `String` or `Box<T>`
49 pub struct PathSegment {
50 /// The identifier portion of this path segment.
51 pub ident: Ident,
52 /// Type/lifetime arguments attached to this path. They come in
53 /// two flavors: `Path<A,B,C>` and `Path(A,B) -> C`. Note that
54 /// this is more than just simple syntactic sugar; the use of
55 /// parens affects the region binding rules, so we preserve the
56 /// distinction.
57 pub arguments: PathArguments,
58 }
59}
60
61impl<T> From<T> for PathSegment
62where
63 T: Into<Ident>,
64{
65 fn from(ident: T) -> Self {
66 PathSegment {
67 ident: ident.into(),
68 arguments: PathArguments::None,
69 }
70 }
71}
72
73ast_enum! {
74 /// Arguments of a path segment.
75 ///
76 /// E.g. `<A, B>` as in `Foo<A, B>` or `(A, B)` as in `Foo(A, B)`
77 pub enum PathArguments {
78 None,
79 /// The `<'a, A, B, C>` in `foo::bar::baz::<'a, A, B, C>`
80 AngleBracketed(AngleBracketedGenericArguments),
81 /// The `(A, B)` and `C` in `Foo(A, B) -> C`
82 Parenthesized(ParenthesizedGenericArguments),
83 }
84}
85
86impl Default for PathArguments {
87 fn default() -> Self {
88 PathArguments::None
89 }
90}
91
92impl PathArguments {
93 pub fn is_empty(&self) -> bool {
94 match *self {
95 PathArguments::None => true,
96 PathArguments::AngleBracketed(ref bracketed) => bracketed.args.is_empty(),
97 PathArguments::Parenthesized(_) => false,
98 }
99 }
100}
101
102ast_enum! {
103 /// A individual generic argument, like `'a`, `T`, or `Item=T`.
104 pub enum GenericArgument {
105 /// The lifetime parameters for this path segment.
106 Lifetime(Lifetime),
107 /// The type parameters for this path segment, if present.
108 Type(Type),
109 /// Bindings (equality constraints) on associated types, if present.
110 ///
111 /// E.g., `Foo<A=Bar>`.
112 Binding(Binding),
113 /// Const expression. Must be inside of a block.
114 ///
115 /// NOTE: Identity expressions are represented as Type arguments, as
116 /// they are indistinguishable syntactically.
117 Const(Expr),
118 }
119}
120
121ast_struct! {
122 /// A path like `Foo<'a, T>`
123 pub struct AngleBracketedGenericArguments {
124 pub colon2_token: Option<Token![::]>,
125 pub lt_token: Token![<],
126 pub args: Punctuated<GenericArgument, Token![,]>,
127 pub gt_token: Token![>],
128 }
129}
130
131ast_struct! {
132 /// Bind a type to an associated type: `A=Foo`.
133 pub struct Binding {
134 pub ident: Ident,
135 pub eq_token: Token![=],
136 pub ty: Type,
137 }
138}
139
140ast_struct! {
141 /// A path like `Foo(A,B) -> C`
142 pub struct ParenthesizedGenericArguments {
143 pub paren_token: token::Paren,
144 /// `(A, B)`
145 pub inputs: Punctuated<Type, Token![,]>,
146 /// `C`
147 pub output: ReturnType,
148 }
149}
150
151ast_struct! {
152 /// The explicit Self type in a "qualified path". The actual
153 /// path, including the trait and the associated item, is stored
154 /// separately. `position` represents the index of the associated
155 /// item qualified with this Self type.
156 ///
157 /// ```text
158 /// <Vec<T> as a::b::Trait>::AssociatedItem
159 /// ^~~~~~ ~~~~~~~~~~~~~~^
160 /// ty position = 3
161 ///
162 /// <Vec<T>>::AssociatedItem
163 /// ^~~~~~ ^
164 /// ty position = 0
165 /// ```
166 pub struct QSelf {
167 pub lt_token: Token![<],
168 pub ty: Box<Type>,
169 pub position: usize,
170 pub as_token: Option<Token![as]>,
171 pub gt_token: Token![>],
172 }
173}
174
175#[cfg(feature = "parsing")]
176pub mod parsing {
177 use super::*;
178 use synom::Synom;
179
180 impl Synom for Path {
181 named!(parse -> Self, do_parse!(
182 colon: option!(punct!(::)) >>
183 segments: call!(Punctuated::<PathSegment, Token![::]>::parse_separated_nonempty) >>
184 cond_reduce!(segments.first().map_or(true, |seg| seg.item().ident != "dyn"), epsilon!()) >>
185 (Path {
186 leading_colon: colon,
187 segments: segments,
188 })
189 ));
190
191 fn description() -> Option<&'static str> {
192 Some("path")
193 }
194 }
195
196 #[cfg(not(feature = "full"))]
197 impl Synom for GenericArgument {
198 named!(parse -> Self, alt!(
199 call!(ty_no_eq_after) => { GenericArgument::Type }
200 |
201 syn!(Lifetime) => { GenericArgument::Lifetime }
202 |
203 syn!(Binding) => { GenericArgument::Binding }
204 ));
205 }
206
207 #[cfg(feature = "full")]
208 impl Synom for GenericArgument {
209 named!(parse -> Self, alt!(
210 call!(ty_no_eq_after) => { GenericArgument::Type }
211 |
212 syn!(Lifetime) => { GenericArgument::Lifetime }
213 |
214 syn!(Binding) => { GenericArgument::Binding }
215 |
216 syn!(ExprLit) => { |l| GenericArgument::Const(Expr::Lit(l)) }
217 |
218 syn!(ExprBlock) => { |b| GenericArgument::Const(Expr::Block(b)) }
219 ));
220
221 fn description() -> Option<&'static str> {
222 Some("generic argument")
223 }
224 }
225
226 impl Synom for AngleBracketedGenericArguments {
227 named!(parse -> Self, do_parse!(
228 colon2: option!(punct!(::)) >>
229 lt: punct!(<) >>
230 args: call!(Punctuated::parse_terminated) >>
231 gt: punct!(>) >>
232 (AngleBracketedGenericArguments {
233 colon2_token: colon2,
234 lt_token: lt,
235 args: args,
236 gt_token: gt,
237 })
238 ));
239
240 fn description() -> Option<&'static str> {
241 Some("angle bracketed generic arguments")
242 }
243 }
244
245 impl Synom for ParenthesizedGenericArguments {
246 named!(parse -> Self, do_parse!(
247 data: parens!(Punctuated::parse_terminated) >>
248 output: syn!(ReturnType) >>
249 (ParenthesizedGenericArguments {
250 paren_token: data.0,
251 inputs: data.1,
252 output: output,
253 })
254 ));
255
256 fn description() -> Option<&'static str> {
257 Some("parenthesized generic arguments: `Foo(A, B, ..) -> T`")
258 }
259 }
260
261 impl Synom for PathSegment {
262 named!(parse -> Self, alt!(
263 do_parse!(
264 ident: syn!(Ident) >>
265 arguments: syn!(AngleBracketedGenericArguments) >>
266 (PathSegment {
267 ident: ident,
268 arguments: PathArguments::AngleBracketed(arguments),
269 })
270 )
271 |
272 mod_style_path_segment
273 ));
274
275 fn description() -> Option<&'static str> {
276 Some("path segment")
277 }
278 }
279
280 impl Synom for Binding {
281 named!(parse -> Self, do_parse!(
282 id: syn!(Ident) >>
283 eq: punct!(=) >>
284 ty: syn!(Type) >>
285 (Binding {
286 ident: id,
287 eq_token: eq,
288 ty: ty,
289 })
290 ));
291
292 fn description() -> Option<&'static str> {
293 Some("associated type binding")
294 }
295 }
296
297 impl Path {
298 named!(pub parse_mod_style -> Self, do_parse!(
299 colon: option!(punct!(::)) >>
300 segments: call!(Punctuated::parse_separated_nonempty_with,
301 mod_style_path_segment) >>
302 (Path {
303 leading_colon: colon,
304 segments: segments,
305 })
306 ));
307 }
308
309 named!(mod_style_path_segment -> PathSegment, alt!(
310 syn!(Ident) => { Into::into }
311 |
312 keyword!(super) => { Into::into }
313 |
314 keyword!(self) => { Into::into }
315 |
316 keyword!(Self) => { Into::into }
317 |
318 keyword!(crate) => { Into::into }
319 ));
320
321 named!(pub qpath -> (Option<QSelf>, Path), alt!(
322 map!(syn!(Path), |p| (None, p))
323 |
324 do_parse!(
325 lt: punct!(<) >>
326 this: syn!(Type) >>
327 path: option!(tuple!(keyword!(as), syn!(Path))) >>
328 gt: punct!(>) >>
329 colon2: punct!(::) >>
330 rest: call!(Punctuated::parse_separated_nonempty) >>
331 ({
332 let (pos, as_, path) = match path {
333 Some((as_, mut path)) => {
334 let pos = path.segments.len();
335 path.segments.push_punct(colon2);
336 path.segments.extend(rest.into_elements());
337 (pos, Some(as_), path)
338 }
339 None => {
340 (0, None, Path {
341 leading_colon: Some(colon2),
342 segments: rest,
343 })
344 }
345 };
346 (Some(QSelf {
347 lt_token: lt,
348 ty: Box::new(this),
349 position: pos,
350 as_token: as_,
351 gt_token: gt,
352 }), path)
353 })
354 )
355 |
356 map!(keyword!(self), |s| (None, s.into()))
357 ));
358
359 named!(pub ty_no_eq_after -> Type, do_parse!(
360 ty: syn!(Type) >>
361 not!(punct!(=)) >>
362 (ty)
363 ));
364}
365
366#[cfg(feature = "printing")]
367mod printing {
368 use super::*;
369 use quote::{ToTokens, Tokens};
370
371 impl ToTokens for Path {
372 fn to_tokens(&self, tokens: &mut Tokens) {
373 self.leading_colon.to_tokens(tokens);
374 self.segments.to_tokens(tokens);
375 }
376 }
377
378 impl ToTokens for PathSegment {
379 fn to_tokens(&self, tokens: &mut Tokens) {
380 self.ident.to_tokens(tokens);
381 self.arguments.to_tokens(tokens);
382 }
383 }
384
385 impl ToTokens for PathArguments {
386 fn to_tokens(&self, tokens: &mut Tokens) {
387 match *self {
388 PathArguments::None => {}
389 PathArguments::AngleBracketed(ref arguments) => {
390 arguments.to_tokens(tokens);
391 }
392 PathArguments::Parenthesized(ref arguments) => {
393 arguments.to_tokens(tokens);
394 }
395 }
396 }
397 }
398
399 impl ToTokens for GenericArgument {
400 #[cfg_attr(feature = "cargo-clippy", allow(match_same_arms))]
401 fn to_tokens(&self, tokens: &mut Tokens) {
402 match *self {
403 GenericArgument::Lifetime(ref lt) => lt.to_tokens(tokens),
404 GenericArgument::Type(ref ty) => ty.to_tokens(tokens),
405 GenericArgument::Binding(ref tb) => tb.to_tokens(tokens),
406 GenericArgument::Const(ref e) => match *e {
407 Expr::Lit(_) => e.to_tokens(tokens),
408
409 // NOTE: We should probably support parsing blocks with only
410 // expressions in them without the full feature for const
411 // generics.
412 #[cfg(feature = "full")]
413 Expr::Block(_) => e.to_tokens(tokens),
414
415 // ERROR CORRECTION: Add braces to make sure that the
416 // generated code is valid.
417 _ => token::Brace::default().surround(tokens, |tokens| {
418 e.to_tokens(tokens);
419 }),
420 },
421 }
422 }
423 }
424
425 impl ToTokens for AngleBracketedGenericArguments {
426 fn to_tokens(&self, tokens: &mut Tokens) {
427 self.colon2_token.to_tokens(tokens);
428 self.lt_token.to_tokens(tokens);
429 self.args.to_tokens(tokens);
430 self.gt_token.to_tokens(tokens);
431 }
432 }
433
434 impl ToTokens for Binding {
435 fn to_tokens(&self, tokens: &mut Tokens) {
436 self.ident.to_tokens(tokens);
437 self.eq_token.to_tokens(tokens);
438 self.ty.to_tokens(tokens);
439 }
440 }
441
442 impl ToTokens for ParenthesizedGenericArguments {
443 fn to_tokens(&self, tokens: &mut Tokens) {
444 self.paren_token.surround(tokens, |tokens| {
445 self.inputs.to_tokens(tokens);
446 });
447 self.output.to_tokens(tokens);
448 }
449 }
450}