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