standalone_syn/
path.rs

1// 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
9use punctuated::Punctuated;
10use super::*;
11
12ast_struct! {
13    /// A path at which a named item is exported: `std::collections::HashMap`.
14    ///
15    /// *This type is available if Syn is built with the `"derive"` or `"full"`
16    /// feature.*
17    pub struct Path {
18        pub leading_colon: Option<Token![::]>,
19        pub segments: Punctuated<PathSegment, Token![::]>,
20    }
21}
22
23impl Path {
24    pub fn global(&self) -> bool {
25        self.leading_colon.is_some()
26    }
27}
28
29/// A helper for printing a self-type qualified path as tokens.
30///
31/// ```rust
32/// extern crate syn;
33/// extern crate quote;
34///
35/// use syn::{QSelf, Path, PathTokens};
36/// use quote::{Tokens, ToTokens};
37///
38/// struct MyNode {
39///     qself: Option<QSelf>,
40///     path: Path,
41/// }
42///
43/// impl ToTokens for MyNode {
44///     fn to_tokens(&self, tokens: &mut Tokens) {
45///         PathTokens(&self.qself, &self.path).to_tokens(tokens);
46///     }
47/// }
48/// #
49/// # fn main() {}
50/// ```
51///
52/// *This type is available if Syn is built with the `"derive"` or `"full"`
53/// feature and the `"printing"` feature.*
54#[cfg(feature = "printing")]
55#[cfg_attr(feature = "extra-traits", derive(Debug, Eq, PartialEq, Hash))]
56#[cfg_attr(feature = "clone-impls", derive(Clone))]
57pub struct PathTokens<'a>(pub &'a Option<QSelf>, pub &'a Path);
58
59impl<T> From<T> for Path
60where
61    T: Into<PathSegment>,
62{
63    fn from(segment: T) -> Self {
64        let mut path = Path {
65            leading_colon: None,
66            segments: Punctuated::new(),
67        };
68        path.segments.push_value(segment.into());
69        path
70    }
71}
72
73ast_struct! {
74    /// A segment of a path together with any path arguments on that segment.
75    ///
76    /// *This type is available if Syn is built with the `"derive"` or `"full"`
77    /// feature.*
78    pub struct PathSegment {
79        pub ident: Ident,
80        pub arguments: PathArguments,
81    }
82}
83
84impl<T> From<T> for PathSegment
85where
86    T: Into<Ident>,
87{
88    fn from(ident: T) -> Self {
89        PathSegment {
90            ident: ident.into(),
91            arguments: PathArguments::None,
92        }
93    }
94}
95
96ast_enum! {
97    /// Angle bracketed or parenthesized arguments of a path segment.
98    ///
99    /// *This type is available if Syn is built with the `"derive"` or `"full"`
100    /// feature.*
101    ///
102    /// ## Angle bracketed
103    ///
104    /// The `<'a, T>` in `std::slice::iter<'a, T>`.
105    ///
106    /// ## Parenthesized
107    ///
108    /// The `(A, B) -> C` in `Fn(A, B) -> C`.
109    pub enum PathArguments {
110        None,
111        /// The `<'a, T>` in `std::slice::iter<'a, T>`.
112        AngleBracketed(AngleBracketedGenericArguments),
113        /// The `(A, B) -> C` in `Fn(A, B) -> C`.
114        Parenthesized(ParenthesizedGenericArguments),
115    }
116}
117
118impl Default for PathArguments {
119    fn default() -> Self {
120        PathArguments::None
121    }
122}
123
124impl PathArguments {
125    pub fn is_empty(&self) -> bool {
126        match *self {
127            PathArguments::None => true,
128            PathArguments::AngleBracketed(ref bracketed) => bracketed.args.is_empty(),
129            PathArguments::Parenthesized(_) => false,
130        }
131    }
132}
133
134ast_enum! {
135    /// An individual generic argument, like `'a`, `T`, or `Item = T`.
136    ///
137    /// *This type is available if Syn is built with the `"derive"` or `"full"`
138    /// feature.*
139    pub enum GenericArgument {
140        /// A lifetime argument.
141        Lifetime(Lifetime),
142        /// A type argument.
143        Type(Type),
144        /// A binding (equality constraint) on an associated type: the `Item =
145        /// u8` in `Iterator<Item = u8>`.
146        Binding(Binding),
147        /// A const expression. Must be inside of a block.
148        ///
149        /// NOTE: Identity expressions are represented as Type arguments, as
150        /// they are indistinguishable syntactically.
151        Const(Expr),
152    }
153}
154
155ast_struct! {
156    /// Angle bracketed arguments of a path segment: the `<K, V>` in `HashMap<K,
157    /// V>`.
158    ///
159    /// *This type is available if Syn is built with the `"derive"` or `"full"`
160    /// feature.*
161    pub struct AngleBracketedGenericArguments {
162        pub colon2_token: Option<Token![::]>,
163        pub lt_token: Token![<],
164        pub args: Punctuated<GenericArgument, Token![,]>,
165        pub gt_token: Token![>],
166    }
167}
168
169ast_struct! {
170    /// A binding (equality constraint) on an associated type: `Item = u8`.
171    ///
172    /// *This type is available if Syn is built with the `"derive"` or `"full"`
173    /// feature.*
174    pub struct Binding {
175        pub ident: Ident,
176        pub eq_token: Token![=],
177        pub ty: Type,
178    }
179}
180
181ast_struct! {
182    /// Arguments of a function path segment: the `(A, B) -> C` in `Fn(A,B) ->
183    /// C`.
184    ///
185    /// *This type is available if Syn is built with the `"derive"` or `"full"`
186    /// feature.*
187    pub struct ParenthesizedGenericArguments {
188        pub paren_token: token::Paren,
189        /// `(A, B)`
190        pub inputs: Punctuated<Type, Token![,]>,
191        /// `C`
192        pub output: ReturnType,
193    }
194}
195
196ast_struct! {
197    /// The explicit Self type in a qualified path: the `T` in `<T as
198    /// Display>::fmt`.
199    ///
200    /// The actual path, including the trait and the associated item, is stored
201    /// separately. The `position` field represents the index of the associated
202    /// item qualified with this Self type.
203    ///
204    /// ```text
205    /// <Vec<T> as a::b::Trait>::AssociatedItem
206    ///  ^~~~~~    ~~~~~~~~~~~~~~^
207    ///  ty        position = 3
208    ///
209    /// <Vec<T>>::AssociatedItem
210    ///  ^~~~~~   ^
211    ///  ty       position = 0
212    /// ```
213    ///
214    /// *This type is available if Syn is built with the `"derive"` or `"full"`
215    /// feature.*
216    pub struct QSelf {
217        pub lt_token: Token![<],
218        pub ty: Box<Type>,
219        pub position: usize,
220        pub as_token: Option<Token![as]>,
221        pub gt_token: Token![>],
222    }
223}
224
225#[cfg(feature = "parsing")]
226pub mod parsing {
227    use super::*;
228    use synom::Synom;
229
230    impl Synom for Path {
231        named!(parse -> Self, do_parse!(
232            colon: option!(punct!(::)) >>
233            segments: call!(Punctuated::<PathSegment, Token![::]>::parse_separated_nonempty) >>
234            cond_reduce!(segments.first().map_or(true, |seg| seg.value().ident != "dyn")) >>
235            (Path {
236                leading_colon: colon,
237                segments: segments,
238            })
239        ));
240
241        fn description() -> Option<&'static str> {
242            Some("path")
243        }
244    }
245
246    #[cfg(not(feature = "full"))]
247    impl Synom for GenericArgument {
248        named!(parse -> Self, alt!(
249            call!(ty_no_eq_after) => { GenericArgument::Type }
250            |
251            syn!(Lifetime) => { GenericArgument::Lifetime }
252            |
253            syn!(Binding) => { GenericArgument::Binding }
254        ));
255    }
256
257    #[cfg(feature = "full")]
258    impl Synom for GenericArgument {
259        named!(parse -> Self, alt!(
260            call!(ty_no_eq_after) => { GenericArgument::Type }
261            |
262            syn!(Lifetime) => { GenericArgument::Lifetime }
263            |
264            syn!(Binding) => { GenericArgument::Binding }
265            |
266            syn!(ExprLit) => { |l| GenericArgument::Const(Expr::Lit(l)) }
267            |
268            syn!(ExprBlock) => { |b| GenericArgument::Const(Expr::Block(b)) }
269        ));
270
271        fn description() -> Option<&'static str> {
272            Some("generic argument")
273        }
274    }
275
276    impl Synom for AngleBracketedGenericArguments {
277        named!(parse -> Self, do_parse!(
278            colon2: option!(punct!(::)) >>
279            lt: punct!(<) >>
280            args: call!(Punctuated::parse_terminated) >>
281            gt: punct!(>) >>
282            (AngleBracketedGenericArguments {
283                colon2_token: colon2,
284                lt_token: lt,
285                args: args,
286                gt_token: gt,
287            })
288        ));
289
290        fn description() -> Option<&'static str> {
291            Some("angle bracketed generic arguments")
292        }
293    }
294
295    impl Synom for ParenthesizedGenericArguments {
296        named!(parse -> Self, do_parse!(
297            data: parens!(Punctuated::parse_terminated) >>
298            output: syn!(ReturnType) >>
299            (ParenthesizedGenericArguments {
300                paren_token: data.0,
301                inputs: data.1,
302                output: output,
303            })
304        ));
305
306        fn description() -> Option<&'static str> {
307            Some("parenthesized generic arguments: `Foo(A, B, ..) -> T`")
308        }
309    }
310
311    impl Synom for PathSegment {
312        named!(parse -> Self, alt!(
313            do_parse!(
314                ident: syn!(Ident) >>
315                arguments: syn!(AngleBracketedGenericArguments) >>
316                (PathSegment {
317                    ident: ident,
318                    arguments: PathArguments::AngleBracketed(arguments),
319                })
320            )
321            |
322            mod_style_path_segment
323        ));
324
325        fn description() -> Option<&'static str> {
326            Some("path segment")
327        }
328    }
329
330    impl Synom for Binding {
331        named!(parse -> Self, do_parse!(
332            id: syn!(Ident) >>
333            eq: punct!(=) >>
334            ty: syn!(Type) >>
335            (Binding {
336                ident: id,
337                eq_token: eq,
338                ty: ty,
339            })
340        ));
341
342        fn description() -> Option<&'static str> {
343            Some("associated type binding")
344        }
345    }
346
347    impl Path {
348        named!(pub parse_mod_style -> Self, do_parse!(
349            colon: option!(punct!(::)) >>
350            segments: call!(Punctuated::parse_separated_nonempty_with,
351                            mod_style_path_segment) >>
352            (Path {
353                leading_colon: colon,
354                segments: segments,
355            })
356        ));
357    }
358
359    named!(mod_style_path_segment -> PathSegment, alt!(
360        syn!(Ident) => { Into::into }
361        |
362        keyword!(super) => { Into::into }
363        |
364        keyword!(self) => { Into::into }
365        |
366        keyword!(Self) => { Into::into }
367        |
368        keyword!(crate) => { Into::into }
369    ));
370
371    named!(pub qpath -> (Option<QSelf>, Path), alt!(
372        map!(syn!(Path), |p| (None, p))
373        |
374        do_parse!(
375            lt: punct!(<) >>
376            this: syn!(Type) >>
377            path: option!(tuple!(keyword!(as), syn!(Path))) >>
378            gt: punct!(>) >>
379            colon2: punct!(::) >>
380            rest: call!(Punctuated::parse_separated_nonempty) >>
381            ({
382                let (pos, as_, path) = match path {
383                    Some((as_, mut path)) => {
384                        let pos = path.segments.len();
385                        path.segments.push_punct(colon2);
386                        path.segments.extend(rest.into_pairs());
387                        (pos, Some(as_), path)
388                    }
389                    None => {
390                        (0, None, Path {
391                            leading_colon: Some(colon2),
392                            segments: rest,
393                        })
394                    }
395                };
396                (Some(QSelf {
397                    lt_token: lt,
398                    ty: Box::new(this),
399                    position: pos,
400                    as_token: as_,
401                    gt_token: gt,
402                }), path)
403            })
404        )
405        |
406        map!(keyword!(self), |s| (None, s.into()))
407    ));
408
409    named!(pub ty_no_eq_after -> Type, do_parse!(
410        ty: syn!(Type) >>
411        not!(punct!(=)) >>
412        (ty)
413    ));
414}
415
416#[cfg(feature = "printing")]
417mod printing {
418    use super::*;
419    use quote::{ToTokens, Tokens};
420
421    impl ToTokens for Path {
422        fn to_tokens(&self, tokens: &mut Tokens) {
423            self.leading_colon.to_tokens(tokens);
424            self.segments.to_tokens(tokens);
425        }
426    }
427
428    impl ToTokens for PathSegment {
429        fn to_tokens(&self, tokens: &mut Tokens) {
430            self.ident.to_tokens(tokens);
431            self.arguments.to_tokens(tokens);
432        }
433    }
434
435    impl ToTokens for PathArguments {
436        fn to_tokens(&self, tokens: &mut Tokens) {
437            match *self {
438                PathArguments::None => {}
439                PathArguments::AngleBracketed(ref arguments) => {
440                    arguments.to_tokens(tokens);
441                }
442                PathArguments::Parenthesized(ref arguments) => {
443                    arguments.to_tokens(tokens);
444                }
445            }
446        }
447    }
448
449    impl ToTokens for GenericArgument {
450        #[cfg_attr(feature = "cargo-clippy", allow(match_same_arms))]
451        fn to_tokens(&self, tokens: &mut Tokens) {
452            match *self {
453                GenericArgument::Lifetime(ref lt) => lt.to_tokens(tokens),
454                GenericArgument::Type(ref ty) => ty.to_tokens(tokens),
455                GenericArgument::Binding(ref tb) => tb.to_tokens(tokens),
456                GenericArgument::Const(ref e) => match *e {
457                    Expr::Lit(_) => e.to_tokens(tokens),
458
459                    // NOTE: We should probably support parsing blocks with only
460                    // expressions in them without the full feature for const
461                    // generics.
462                    #[cfg(feature = "full")]
463                    Expr::Block(_) => e.to_tokens(tokens),
464
465                    // ERROR CORRECTION: Add braces to make sure that the
466                    // generated code is valid.
467                    _ => token::Brace::default().surround(tokens, |tokens| {
468                        e.to_tokens(tokens);
469                    }),
470                },
471            }
472        }
473    }
474
475    impl ToTokens for AngleBracketedGenericArguments {
476        fn to_tokens(&self, tokens: &mut Tokens) {
477            self.colon2_token.to_tokens(tokens);
478            self.lt_token.to_tokens(tokens);
479
480            // Print lifetimes before types and consts, all before bindings,
481            // regardless of their order in self.args.
482            //
483            // TODO: ordering rules for const arguments vs type arguments have
484            // not been settled yet. https://github.com/rust-lang/rust/issues/44580
485            let mut trailing_or_empty = true;
486            for param in self.args.pairs() {
487                if let GenericArgument::Lifetime(_) = **param.value() {
488                    param.to_tokens(tokens);
489                    trailing_or_empty = param.punct().is_some();
490                }
491            }
492            for param in self.args.pairs() {
493                match **param.value() {
494                    GenericArgument::Type(_) | GenericArgument::Const(_) => {
495                        if !trailing_or_empty {
496                            <Token![,]>::default().to_tokens(tokens);
497                        }
498                        param.to_tokens(tokens);
499                        trailing_or_empty = param.punct().is_some();
500                    }
501                    GenericArgument::Lifetime(_) | GenericArgument::Binding(_) => {}
502                }
503            }
504            for param in self.args.pairs() {
505                if let GenericArgument::Binding(_) = **param.value() {
506                    if !trailing_or_empty {
507                        <Token![,]>::default().to_tokens(tokens);
508                        trailing_or_empty = true;
509                    }
510                    param.to_tokens(tokens);
511                }
512            }
513
514            self.gt_token.to_tokens(tokens);
515        }
516    }
517
518    impl ToTokens for Binding {
519        fn to_tokens(&self, tokens: &mut Tokens) {
520            self.ident.to_tokens(tokens);
521            self.eq_token.to_tokens(tokens);
522            self.ty.to_tokens(tokens);
523        }
524    }
525
526    impl ToTokens for ParenthesizedGenericArguments {
527        fn to_tokens(&self, tokens: &mut Tokens) {
528            self.paren_token.surround(tokens, |tokens| {
529                self.inputs.to_tokens(tokens);
530            });
531            self.output.to_tokens(tokens);
532        }
533    }
534
535    impl<'a> ToTokens for PathTokens<'a> {
536        fn to_tokens(&self, tokens: &mut Tokens) {
537            let qself = match *self.0 {
538                Some(ref qself) => qself,
539                None => return self.1.to_tokens(tokens),
540            };
541            qself.lt_token.to_tokens(tokens);
542            qself.ty.to_tokens(tokens);
543
544            // XXX: Gross.
545            let pos = if qself.position > 0 && qself.position >= self.1.segments.len() {
546                self.1.segments.len() - 1
547            } else {
548                qself.position
549            };
550            let mut segments = self.1.segments.pairs();
551            if pos > 0 {
552                TokensOrDefault(&qself.as_token).to_tokens(tokens);
553                self.1.leading_colon.to_tokens(tokens);
554                for (i, segment) in segments.by_ref().take(pos).enumerate() {
555                    if i + 1 == pos {
556                        segment.value().to_tokens(tokens);
557                        qself.gt_token.to_tokens(tokens);
558                        segment.punct().to_tokens(tokens);
559                    } else {
560                        segment.to_tokens(tokens);
561                    }
562                }
563            } else {
564                qself.gt_token.to_tokens(tokens);
565                self.1.leading_colon.to_tokens(tokens);
566            }
567            for segment in segments {
568                segment.to_tokens(tokens);
569            }
570        }
571    }
572}