syn_pub_items/
path.rs

1use super::*;
2use punctuated::Punctuated;
3
4ast_struct! {
5    /// A path at which a named item is exported: `std::collections::HashMap`.
6    ///
7    /// *This type is available if Syn is built with the `"derive"` or `"full"`
8    /// feature.*
9    pub struct Path {
10        pub leading_colon: Option<Token![::]>,
11        pub segments: Punctuated<PathSegment, Token![::]>,
12    }
13}
14
15impl<T> From<T> for Path
16where
17    T: Into<PathSegment>,
18{
19    fn from(segment: T) -> Self {
20        let mut path = Path {
21            leading_colon: None,
22            segments: Punctuated::new(),
23        };
24        path.segments.push_value(segment.into());
25        path
26    }
27}
28
29ast_struct! {
30    /// A segment of a path together with any path arguments on that segment.
31    ///
32    /// *This type is available if Syn is built with the `"derive"` or `"full"`
33    /// feature.*
34    pub struct PathSegment {
35        pub ident: Ident,
36        pub arguments: PathArguments,
37    }
38}
39
40impl<T> From<T> for PathSegment
41where
42    T: Into<Ident>,
43{
44    fn from(ident: T) -> Self {
45        PathSegment {
46            ident: ident.into(),
47            arguments: PathArguments::None,
48        }
49    }
50}
51
52ast_enum! {
53    /// Angle bracketed or parenthesized arguments of a path segment.
54    ///
55    /// *This type is available if Syn is built with the `"derive"` or `"full"`
56    /// feature.*
57    ///
58    /// ## Angle bracketed
59    ///
60    /// The `<'a, T>` in `std::slice::iter<'a, T>`.
61    ///
62    /// ## Parenthesized
63    ///
64    /// The `(A, B) -> C` in `Fn(A, B) -> C`.
65    pub enum PathArguments {
66        None,
67        /// The `<'a, T>` in `std::slice::iter<'a, T>`.
68        AngleBracketed(AngleBracketedGenericArguments),
69        /// The `(A, B) -> C` in `Fn(A, B) -> C`.
70        Parenthesized(ParenthesizedGenericArguments),
71    }
72}
73
74impl Default for PathArguments {
75    fn default() -> Self {
76        PathArguments::None
77    }
78}
79
80impl PathArguments {
81    pub fn is_empty(&self) -> bool {
82        match *self {
83            PathArguments::None => true,
84            PathArguments::AngleBracketed(ref bracketed) => bracketed.args.is_empty(),
85            PathArguments::Parenthesized(_) => false,
86        }
87    }
88
89    #[cfg(feature = "parsing")]
90    fn is_none(&self) -> bool {
91        match *self {
92            PathArguments::None => true,
93            PathArguments::AngleBracketed(_) | PathArguments::Parenthesized(_) => false,
94        }
95    }
96}
97
98ast_enum! {
99    /// An individual generic argument, like `'a`, `T`, or `Item = T`.
100    ///
101    /// *This type is available if Syn is built with the `"derive"` or `"full"`
102    /// feature.*
103    pub enum GenericArgument {
104        /// A lifetime argument.
105        Lifetime(Lifetime),
106        /// A type argument.
107        Type(Type),
108        /// A binding (equality constraint) on an associated type: the `Item =
109        /// u8` in `Iterator<Item = u8>`.
110        Binding(Binding),
111        /// An associated type bound: `Iterator<Item: Display>`.
112        Constraint(Constraint),
113        /// A 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    /// Angle bracketed arguments of a path segment: the `<K, V>` in `HashMap<K,
123    /// V>`.
124    ///
125    /// *This type is available if Syn is built with the `"derive"` or `"full"`
126    /// feature.*
127    pub struct AngleBracketedGenericArguments {
128        pub colon2_token: Option<Token![::]>,
129        pub lt_token: Token![<],
130        pub args: Punctuated<GenericArgument, Token![,]>,
131        pub gt_token: Token![>],
132    }
133}
134
135ast_struct! {
136    /// A binding (equality constraint) on an associated type: `Item = u8`.
137    ///
138    /// *This type is available if Syn is built with the `"derive"` or `"full"`
139    /// feature.*
140    pub struct Binding {
141        pub ident: Ident,
142        pub eq_token: Token![=],
143        pub ty: Type,
144    }
145}
146
147ast_struct! {
148    /// An associated type bound: `Iterator<Item: Display>`.
149    ///
150    /// *This type is available if Syn is built with the `"derive"` or `"full"`
151    /// feature.*
152    pub struct Constraint {
153        pub ident: Ident,
154        pub colon_token: Token![:],
155        pub bounds: Punctuated<TypeParamBound, Token![+]>,
156    }
157}
158
159ast_struct! {
160    /// Arguments of a function path segment: the `(A, B) -> C` in `Fn(A,B) ->
161    /// C`.
162    ///
163    /// *This type is available if Syn is built with the `"derive"` or `"full"`
164    /// feature.*
165    pub struct ParenthesizedGenericArguments {
166        pub paren_token: token::Paren,
167        /// `(A, B)`
168        pub inputs: Punctuated<Type, Token![,]>,
169        /// `C`
170        pub output: ReturnType,
171    }
172}
173
174ast_struct! {
175    /// The explicit Self type in a qualified path: the `T` in `<T as
176    /// Display>::fmt`.
177    ///
178    /// The actual path, including the trait and the associated item, is stored
179    /// separately. The `position` field represents the index of the associated
180    /// item qualified with this Self type.
181    ///
182    /// ```text
183    /// <Vec<T> as a::b::Trait>::AssociatedItem
184    ///  ^~~~~~    ~~~~~~~~~~~~~~^
185    ///  ty        position = 3
186    ///
187    /// <Vec<T>>::AssociatedItem
188    ///  ^~~~~~   ^
189    ///  ty       position = 0
190    /// ```
191    ///
192    /// *This type is available if Syn is built with the `"derive"` or `"full"`
193    /// feature.*
194    pub struct QSelf {
195        pub lt_token: Token![<],
196        pub ty: Box<Type>,
197        pub position: usize,
198        pub as_token: Option<Token![as]>,
199        pub gt_token: Token![>],
200    }
201}
202
203#[cfg(feature = "parsing")]
204pub mod parsing {
205    use super::*;
206
207    #[cfg(feature = "full")]
208    use expr;
209    use ext::IdentExt;
210    use parse::{Parse, ParseStream, Result};
211
212    impl Parse for Path {
213        fn parse(input: ParseStream) -> Result<Self> {
214            Self::parse_helper(input, false)
215        }
216    }
217
218    impl Parse for GenericArgument {
219        fn parse(input: ParseStream) -> Result<Self> {
220            if input.peek(Lifetime) && !input.peek2(Token![+]) {
221                return Ok(GenericArgument::Lifetime(input.parse()?));
222            }
223
224            if input.peek(Ident) && input.peek2(Token![=]) {
225                return Ok(GenericArgument::Binding(input.parse()?));
226            }
227
228            #[cfg(feature = "full")]
229            {
230                if input.peek(Ident) && input.peek2(Token![:]) && !input.peek2(Token![::]) {
231                    return Ok(GenericArgument::Constraint(input.parse()?));
232                }
233
234                if input.peek(Lit) {
235                    let lit = input.parse()?;
236                    return Ok(GenericArgument::Const(Expr::Lit(lit)));
237                }
238
239                if input.peek(token::Brace) {
240                    let block = input.call(expr::parsing::expr_block)?;
241                    return Ok(GenericArgument::Const(Expr::Block(block)));
242                }
243            }
244
245            input.parse().map(GenericArgument::Type)
246        }
247    }
248
249    impl Parse for AngleBracketedGenericArguments {
250        fn parse(input: ParseStream) -> Result<Self> {
251            Ok(AngleBracketedGenericArguments {
252                colon2_token: input.parse()?,
253                lt_token: input.parse()?,
254                args: {
255                    let mut args = Punctuated::new();
256                    loop {
257                        if input.peek(Token![>]) {
258                            break;
259                        }
260                        let value = input.parse()?;
261                        args.push_value(value);
262                        if input.peek(Token![>]) {
263                            break;
264                        }
265                        let punct = input.parse()?;
266                        args.push_punct(punct);
267                    }
268                    args
269                },
270                gt_token: input.parse()?,
271            })
272        }
273    }
274
275    impl Parse for ParenthesizedGenericArguments {
276        fn parse(input: ParseStream) -> Result<Self> {
277            let content;
278            Ok(ParenthesizedGenericArguments {
279                paren_token: parenthesized!(content in input),
280                inputs: content.parse_terminated(Type::parse)?,
281                output: input.call(ReturnType::without_plus)?,
282            })
283        }
284    }
285
286    impl Parse for PathSegment {
287        fn parse(input: ParseStream) -> Result<Self> {
288            Self::parse_helper(input, false)
289        }
290    }
291
292    impl PathSegment {
293        pub fn parse_helper(input: ParseStream, expr_style: bool) -> Result<Self> {
294            if input.peek(Token![super])
295                || input.peek(Token![self])
296                || input.peek(Token![Self])
297                || input.peek(Token![crate])
298                || input.peek(Token![extern])
299            {
300                let ident = input.call(Ident::parse_any)?;
301                return Ok(PathSegment::from(ident));
302            }
303
304            let ident = input.parse()?;
305            if !expr_style && input.peek(Token![<]) && !input.peek(Token![<=])
306                || input.peek(Token![::]) && input.peek3(Token![<])
307            {
308                Ok(PathSegment {
309                    ident: ident,
310                    arguments: PathArguments::AngleBracketed(input.parse()?),
311                })
312            } else {
313                Ok(PathSegment::from(ident))
314            }
315        }
316    }
317
318    impl Parse for Binding {
319        fn parse(input: ParseStream) -> Result<Self> {
320            Ok(Binding {
321                ident: input.parse()?,
322                eq_token: input.parse()?,
323                ty: input.parse()?,
324            })
325        }
326    }
327
328    #[cfg(feature = "full")]
329    impl Parse for Constraint {
330        fn parse(input: ParseStream) -> Result<Self> {
331            Ok(Constraint {
332                ident: input.parse()?,
333                colon_token: input.parse()?,
334                bounds: {
335                    let mut bounds = Punctuated::new();
336                    loop {
337                        if input.peek(Token![,]) || input.peek(Token![>]) {
338                            break;
339                        }
340                        let value = input.parse()?;
341                        bounds.push_value(value);
342                        if !input.peek(Token![+]) {
343                            break;
344                        }
345                        let punct = input.parse()?;
346                        bounds.push_punct(punct);
347                    }
348                    bounds
349                },
350            })
351        }
352    }
353
354    impl Path {
355        /// Parse a `Path` containing no path arguments on any of its segments.
356        ///
357        /// *This function is available if Syn is built with the `"parsing"`
358        /// feature.*
359        ///
360        /// # Example
361        ///
362        /// ```edition2018
363        /// use syn::{Path, Result, Token};
364        /// use syn::parse::{Parse, ParseStream};
365        ///
366        /// // A simplified single `use` statement like:
367        /// //
368        /// //     use std::collections::HashMap;
369        /// //
370        /// // Note that generic parameters are not allowed in a `use` statement
371        /// // so the following must not be accepted.
372        /// //
373        /// //     use a::<b>::c;
374        /// struct SingleUse {
375        ///     use_token: Token![use],
376        ///     path: Path,
377        /// }
378        ///
379        /// impl Parse for SingleUse {
380        ///     fn parse(input: ParseStream) -> Result<Self> {
381        ///         Ok(SingleUse {
382        ///             use_token: input.parse()?,
383        ///             path: input.call(Path::parse_mod_style)?,
384        ///         })
385        ///     }
386        /// }
387        /// ```
388        pub fn parse_mod_style(input: ParseStream) -> Result<Self> {
389            Ok(Path {
390                leading_colon: input.parse()?,
391                segments: {
392                    let mut segments = Punctuated::new();
393                    loop {
394                        if !input.peek(Ident)
395                            && !input.peek(Token![super])
396                            && !input.peek(Token![self])
397                            && !input.peek(Token![Self])
398                            && !input.peek(Token![crate])
399                            && !input.peek(Token![extern])
400                        {
401                            break;
402                        }
403                        let ident = Ident::parse_any(input)?;
404                        segments.push_value(PathSegment::from(ident));
405                        if !input.peek(Token![::]) {
406                            break;
407                        }
408                        let punct = input.parse()?;
409                        segments.push_punct(punct);
410                    }
411                    if segments.is_empty() {
412                        return Err(input.error("expected path"));
413                    } else if segments.trailing_punct() {
414                        return Err(input.error("expected path segment"));
415                    }
416                    segments
417                },
418            })
419        }
420
421        /// Determines whether this is a path of length 1 equal to the given
422        /// ident.
423        ///
424        /// For them to compare equal, it must be the case that:
425        ///
426        /// - the path has no leading colon,
427        /// - the number of path segments is 1,
428        /// - the first path segment has no angle bracketed or parenthesized
429        ///   path arguments
430        /// - and the ident of the first path segment is equal to the given one.
431        ///
432        /// *This function is available if Syn is built with the `"parsing"`
433        /// feature.*
434        pub fn is_ident<I>(&self, ident: I) -> bool
435        where
436            Ident: PartialEq<I>,
437        {
438            self.leading_colon.is_none()
439                && self.segments.len() == 1
440                && self.segments[0].arguments.is_none()
441                && self.segments[0].ident == ident
442        }
443
444        fn parse_helper(input: ParseStream, expr_style: bool) -> Result<Self> {
445            if input.peek(Token![dyn]) {
446                return Err(input.error("expected path"));
447            }
448
449            Ok(Path {
450                leading_colon: input.parse()?,
451                segments: {
452                    let mut segments = Punctuated::new();
453                    let value = PathSegment::parse_helper(input, expr_style)?;
454                    segments.push_value(value);
455                    while input.peek(Token![::]) {
456                        let punct: Token![::] = input.parse()?;
457                        segments.push_punct(punct);
458                        let value = PathSegment::parse_helper(input, expr_style)?;
459                        segments.push_value(value);
460                    }
461                    segments
462                },
463            })
464        }
465    }
466
467    pub fn qpath(input: ParseStream, expr_style: bool) -> Result<(Option<QSelf>, Path)> {
468        if input.peek(Token![<]) {
469            let lt_token: Token![<] = input.parse()?;
470            let this: Type = input.parse()?;
471            let path = if input.peek(Token![as]) {
472                let as_token: Token![as] = input.parse()?;
473                let path: Path = input.parse()?;
474                Some((as_token, path))
475            } else {
476                None
477            };
478            let gt_token: Token![>] = input.parse()?;
479            let colon2_token: Token![::] = input.parse()?;
480            let mut rest = Punctuated::new();
481            loop {
482                let path = PathSegment::parse_helper(input, expr_style)?;
483                rest.push_value(path);
484                if !input.peek(Token![::]) {
485                    break;
486                }
487                let punct: Token![::] = input.parse()?;
488                rest.push_punct(punct);
489            }
490            let (position, as_token, path) = match path {
491                Some((as_token, mut path)) => {
492                    let pos = path.segments.len();
493                    path.segments.push_punct(colon2_token);
494                    path.segments.extend(rest.into_pairs());
495                    (pos, Some(as_token), path)
496                }
497                None => {
498                    let path = Path {
499                        leading_colon: Some(colon2_token),
500                        segments: rest,
501                    };
502                    (0, None, path)
503                }
504            };
505            let qself = QSelf {
506                lt_token: lt_token,
507                ty: Box::new(this),
508                position: position,
509                as_token: as_token,
510                gt_token: gt_token,
511            };
512            Ok((Some(qself), path))
513        } else {
514            let path = Path::parse_helper(input, expr_style)?;
515            Ok((None, path))
516        }
517    }
518}
519
520#[cfg(feature = "printing")]
521mod printing {
522    use super::*;
523
524    use proc_macro2::TokenStream;
525    use quote::ToTokens;
526
527    use print::TokensOrDefault;
528
529    impl ToTokens for Path {
530        fn to_tokens(&self, tokens: &mut TokenStream) {
531            self.leading_colon.to_tokens(tokens);
532            self.segments.to_tokens(tokens);
533        }
534    }
535
536    impl ToTokens for PathSegment {
537        fn to_tokens(&self, tokens: &mut TokenStream) {
538            self.ident.to_tokens(tokens);
539            self.arguments.to_tokens(tokens);
540        }
541    }
542
543    impl ToTokens for PathArguments {
544        fn to_tokens(&self, tokens: &mut TokenStream) {
545            match *self {
546                PathArguments::None => {}
547                PathArguments::AngleBracketed(ref arguments) => {
548                    arguments.to_tokens(tokens);
549                }
550                PathArguments::Parenthesized(ref arguments) => {
551                    arguments.to_tokens(tokens);
552                }
553            }
554        }
555    }
556
557    impl ToTokens for GenericArgument {
558        #[cfg_attr(feature = "cargo-clippy", allow(match_same_arms))]
559        fn to_tokens(&self, tokens: &mut TokenStream) {
560            match *self {
561                GenericArgument::Lifetime(ref lt) => lt.to_tokens(tokens),
562                GenericArgument::Type(ref ty) => ty.to_tokens(tokens),
563                GenericArgument::Binding(ref tb) => tb.to_tokens(tokens),
564                GenericArgument::Constraint(ref tc) => tc.to_tokens(tokens),
565                GenericArgument::Const(ref e) => match *e {
566                    Expr::Lit(_) => e.to_tokens(tokens),
567
568                    // NOTE: We should probably support parsing blocks with only
569                    // expressions in them without the full feature for const
570                    // generics.
571                    #[cfg(feature = "full")]
572                    Expr::Block(_) => e.to_tokens(tokens),
573
574                    // ERROR CORRECTION: Add braces to make sure that the
575                    // generated code is valid.
576                    _ => token::Brace::default().surround(tokens, |tokens| {
577                        e.to_tokens(tokens);
578                    }),
579                },
580            }
581        }
582    }
583
584    impl ToTokens for AngleBracketedGenericArguments {
585        fn to_tokens(&self, tokens: &mut TokenStream) {
586            self.colon2_token.to_tokens(tokens);
587            self.lt_token.to_tokens(tokens);
588
589            // Print lifetimes before types and consts, all before bindings,
590            // regardless of their order in self.args.
591            //
592            // TODO: ordering rules for const arguments vs type arguments have
593            // not been settled yet. https://github.com/rust-lang/rust/issues/44580
594            let mut trailing_or_empty = true;
595            for param in self.args.pairs() {
596                match **param.value() {
597                    GenericArgument::Lifetime(_) => {
598                        param.to_tokens(tokens);
599                        trailing_or_empty = param.punct().is_some();
600                    }
601                    GenericArgument::Type(_)
602                    | GenericArgument::Binding(_)
603                    | GenericArgument::Constraint(_)
604                    | GenericArgument::Const(_) => {}
605                }
606            }
607            for param in self.args.pairs() {
608                match **param.value() {
609                    GenericArgument::Type(_) | GenericArgument::Const(_) => {
610                        if !trailing_or_empty {
611                            <Token![,]>::default().to_tokens(tokens);
612                        }
613                        param.to_tokens(tokens);
614                        trailing_or_empty = param.punct().is_some();
615                    }
616                    GenericArgument::Lifetime(_)
617                    | GenericArgument::Binding(_)
618                    | GenericArgument::Constraint(_) => {}
619                }
620            }
621            for param in self.args.pairs() {
622                match **param.value() {
623                    GenericArgument::Binding(_) | GenericArgument::Constraint(_) => {
624                        if !trailing_or_empty {
625                            <Token![,]>::default().to_tokens(tokens);
626                            trailing_or_empty = true;
627                        }
628                        param.to_tokens(tokens);
629                    }
630                    GenericArgument::Lifetime(_)
631                    | GenericArgument::Type(_)
632                    | GenericArgument::Const(_) => {}
633                }
634            }
635
636            self.gt_token.to_tokens(tokens);
637        }
638    }
639
640    impl ToTokens for Binding {
641        fn to_tokens(&self, tokens: &mut TokenStream) {
642            self.ident.to_tokens(tokens);
643            self.eq_token.to_tokens(tokens);
644            self.ty.to_tokens(tokens);
645        }
646    }
647
648    impl ToTokens for Constraint {
649        fn to_tokens(&self, tokens: &mut TokenStream) {
650            self.ident.to_tokens(tokens);
651            self.colon_token.to_tokens(tokens);
652            self.bounds.to_tokens(tokens);
653        }
654    }
655
656    impl ToTokens for ParenthesizedGenericArguments {
657        fn to_tokens(&self, tokens: &mut TokenStream) {
658            self.paren_token.surround(tokens, |tokens| {
659                self.inputs.to_tokens(tokens);
660            });
661            self.output.to_tokens(tokens);
662        }
663    }
664
665    impl private {
666        pub fn print_path(tokens: &mut TokenStream, qself: &Option<QSelf>, path: &Path) {
667            let qself = match *qself {
668                Some(ref qself) => qself,
669                None => {
670                    path.to_tokens(tokens);
671                    return;
672                }
673            };
674            qself.lt_token.to_tokens(tokens);
675            qself.ty.to_tokens(tokens);
676
677            let pos = if qself.position > 0 && qself.position >= path.segments.len() {
678                path.segments.len() - 1
679            } else {
680                qself.position
681            };
682            let mut segments = path.segments.pairs();
683            if pos > 0 {
684                TokensOrDefault(&qself.as_token).to_tokens(tokens);
685                path.leading_colon.to_tokens(tokens);
686                for (i, segment) in segments.by_ref().take(pos).enumerate() {
687                    if i + 1 == pos {
688                        segment.value().to_tokens(tokens);
689                        qself.gt_token.to_tokens(tokens);
690                        segment.punct().to_tokens(tokens);
691                    } else {
692                        segment.to_tokens(tokens);
693                    }
694                }
695            } else {
696                qself.gt_token.to_tokens(tokens);
697                path.leading_colon.to_tokens(tokens);
698            }
699            for segment in segments {
700                segment.to_tokens(tokens);
701            }
702        }
703    }
704}