syn/
path.rs

1use super::*;
2use crate::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(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 crate::expr;
209    use crate::ext::IdentExt;
210    use crate::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        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![crate])
297                || input.peek(Token![extern])
298            {
299                let ident = input.call(Ident::parse_any)?;
300                return Ok(PathSegment::from(ident));
301            }
302
303            let ident = if input.peek(Token![Self]) {
304                input.call(Ident::parse_any)?
305            } else {
306                input.parse()?
307            };
308
309            if !expr_style && input.peek(Token![<]) && !input.peek(Token![<=])
310                || input.peek(Token![::]) && input.peek3(Token![<])
311            {
312                Ok(PathSegment {
313                    ident,
314                    arguments: PathArguments::AngleBracketed(input.parse()?),
315                })
316            } else {
317                Ok(PathSegment::from(ident))
318            }
319        }
320    }
321
322    impl Parse for Binding {
323        fn parse(input: ParseStream) -> Result<Self> {
324            Ok(Binding {
325                ident: input.parse()?,
326                eq_token: input.parse()?,
327                ty: input.parse()?,
328            })
329        }
330    }
331
332    #[cfg(feature = "full")]
333    impl Parse for Constraint {
334        fn parse(input: ParseStream) -> Result<Self> {
335            Ok(Constraint {
336                ident: input.parse()?,
337                colon_token: input.parse()?,
338                bounds: {
339                    let mut bounds = Punctuated::new();
340                    loop {
341                        if input.peek(Token![,]) || input.peek(Token![>]) {
342                            break;
343                        }
344                        let value = input.parse()?;
345                        bounds.push_value(value);
346                        if !input.peek(Token![+]) {
347                            break;
348                        }
349                        let punct = input.parse()?;
350                        bounds.push_punct(punct);
351                    }
352                    bounds
353                },
354            })
355        }
356    }
357
358    impl Path {
359        /// Parse a `Path` containing no path arguments on any of its segments.
360        ///
361        /// *This function is available if Syn is built with the `"parsing"`
362        /// feature.*
363        ///
364        /// # Example
365        ///
366        /// ```
367        /// use syn::{Path, Result, Token};
368        /// use syn::parse::{Parse, ParseStream};
369        ///
370        /// // A simplified single `use` statement like:
371        /// //
372        /// //     use std::collections::HashMap;
373        /// //
374        /// // Note that generic parameters are not allowed in a `use` statement
375        /// // so the following must not be accepted.
376        /// //
377        /// //     use a::<b>::c;
378        /// struct SingleUse {
379        ///     use_token: Token![use],
380        ///     path: Path,
381        /// }
382        ///
383        /// impl Parse for SingleUse {
384        ///     fn parse(input: ParseStream) -> Result<Self> {
385        ///         Ok(SingleUse {
386        ///             use_token: input.parse()?,
387        ///             path: input.call(Path::parse_mod_style)?,
388        ///         })
389        ///     }
390        /// }
391        /// ```
392        pub fn parse_mod_style(input: ParseStream) -> Result<Self> {
393            Ok(Path {
394                leading_colon: input.parse()?,
395                segments: {
396                    let mut segments = Punctuated::new();
397                    loop {
398                        if !input.peek(Ident)
399                            && !input.peek(Token![super])
400                            && !input.peek(Token![self])
401                            && !input.peek(Token![Self])
402                            && !input.peek(Token![crate])
403                            && !input.peek(Token![extern])
404                        {
405                            break;
406                        }
407                        let ident = Ident::parse_any(input)?;
408                        segments.push_value(PathSegment::from(ident));
409                        if !input.peek(Token![::]) {
410                            break;
411                        }
412                        let punct = input.parse()?;
413                        segments.push_punct(punct);
414                    }
415                    if segments.is_empty() {
416                        return Err(input.error("expected path"));
417                    } else if segments.trailing_punct() {
418                        return Err(input.error("expected path segment"));
419                    }
420                    segments
421                },
422            })
423        }
424
425        /// Determines whether this is a path of length 1 equal to the given
426        /// ident.
427        ///
428        /// For them to compare equal, it must be the case that:
429        ///
430        /// - the path has no leading colon,
431        /// - the number of path segments is 1,
432        /// - the first path segment has no angle bracketed or parenthesized
433        ///   path arguments, and
434        /// - the ident of the first path segment is equal to the given one.
435        ///
436        /// *This function is available if Syn is built with the `"parsing"`
437        /// feature.*
438        ///
439        /// # Example
440        ///
441        /// ```
442        /// use syn::{Attribute, Error, Meta, NestedMeta, Result};
443        /// # use std::iter::FromIterator;
444        ///
445        /// fn get_serde_meta_items(attr: &Attribute) -> Result<Vec<NestedMeta>> {
446        ///     if attr.path.is_ident("serde") {
447        ///         match attr.parse_meta()? {
448        ///             Meta::List(meta) => Ok(Vec::from_iter(meta.nested)),
449        ///             bad => Err(Error::new_spanned(bad, "unrecognized attribute")),
450        ///         }
451        ///     } else {
452        ///         Ok(Vec::new())
453        ///     }
454        /// }
455        /// ```
456        pub fn is_ident<I: ?Sized>(&self, ident: &I) -> bool
457        where
458            Ident: PartialEq<I>,
459        {
460            match self.get_ident() {
461                Some(id) => id == ident,
462                None => false,
463            }
464        }
465
466        /// If this path consists of a single ident, returns the ident.
467        ///
468        /// A path is considered an ident if:
469        ///
470        /// - the path has no leading colon,
471        /// - the number of path segments is 1, and
472        /// - the first path segment has no angle bracketed or parenthesized
473        ///   path arguments.
474        ///
475        /// *This function is available if Syn is built with the `"parsing"`
476        /// feature.*
477        pub fn get_ident(&self) -> Option<&Ident> {
478            if self.leading_colon.is_none()
479                && self.segments.len() == 1
480                && self.segments[0].arguments.is_none()
481            {
482                Some(&self.segments[0].ident)
483            } else {
484                None
485            }
486        }
487
488        fn parse_helper(input: ParseStream, expr_style: bool) -> Result<Self> {
489            Ok(Path {
490                leading_colon: input.parse()?,
491                segments: {
492                    let mut segments = Punctuated::new();
493                    let value = PathSegment::parse_helper(input, expr_style)?;
494                    segments.push_value(value);
495                    while input.peek(Token![::]) {
496                        let punct: Token![::] = input.parse()?;
497                        segments.push_punct(punct);
498                        let value = PathSegment::parse_helper(input, expr_style)?;
499                        segments.push_value(value);
500                    }
501                    segments
502                },
503            })
504        }
505    }
506
507    pub fn qpath(input: ParseStream, expr_style: bool) -> Result<(Option<QSelf>, Path)> {
508        if input.peek(Token![<]) {
509            let lt_token: Token![<] = input.parse()?;
510            let this: Type = input.parse()?;
511            let path = if input.peek(Token![as]) {
512                let as_token: Token![as] = input.parse()?;
513                let path: Path = input.parse()?;
514                Some((as_token, path))
515            } else {
516                None
517            };
518            let gt_token: Token![>] = input.parse()?;
519            let colon2_token: Token![::] = input.parse()?;
520            let mut rest = Punctuated::new();
521            loop {
522                let path = PathSegment::parse_helper(input, expr_style)?;
523                rest.push_value(path);
524                if !input.peek(Token![::]) {
525                    break;
526                }
527                let punct: Token![::] = input.parse()?;
528                rest.push_punct(punct);
529            }
530            let (position, as_token, path) = match path {
531                Some((as_token, mut path)) => {
532                    let pos = path.segments.len();
533                    path.segments.push_punct(colon2_token);
534                    path.segments.extend(rest.into_pairs());
535                    (pos, Some(as_token), path)
536                }
537                None => {
538                    let path = Path {
539                        leading_colon: Some(colon2_token),
540                        segments: rest,
541                    };
542                    (0, None, path)
543                }
544            };
545            let qself = QSelf {
546                lt_token,
547                ty: Box::new(this),
548                position,
549                as_token,
550                gt_token,
551            };
552            Ok((Some(qself), path))
553        } else {
554            let path = Path::parse_helper(input, expr_style)?;
555            Ok((None, path))
556        }
557    }
558}
559
560#[cfg(feature = "printing")]
561mod printing {
562    use super::*;
563
564    use proc_macro2::TokenStream;
565    use quote::ToTokens;
566
567    use crate::print::TokensOrDefault;
568
569    impl ToTokens for Path {
570        fn to_tokens(&self, tokens: &mut TokenStream) {
571            self.leading_colon.to_tokens(tokens);
572            self.segments.to_tokens(tokens);
573        }
574    }
575
576    impl ToTokens for PathSegment {
577        fn to_tokens(&self, tokens: &mut TokenStream) {
578            self.ident.to_tokens(tokens);
579            self.arguments.to_tokens(tokens);
580        }
581    }
582
583    impl ToTokens for PathArguments {
584        fn to_tokens(&self, tokens: &mut TokenStream) {
585            match self {
586                PathArguments::None => {}
587                PathArguments::AngleBracketed(arguments) => {
588                    arguments.to_tokens(tokens);
589                }
590                PathArguments::Parenthesized(arguments) => {
591                    arguments.to_tokens(tokens);
592                }
593            }
594        }
595    }
596
597    impl ToTokens for GenericArgument {
598        #[allow(clippy::match_same_arms)]
599        fn to_tokens(&self, tokens: &mut TokenStream) {
600            match self {
601                GenericArgument::Lifetime(lt) => lt.to_tokens(tokens),
602                GenericArgument::Type(ty) => ty.to_tokens(tokens),
603                GenericArgument::Binding(tb) => tb.to_tokens(tokens),
604                GenericArgument::Constraint(tc) => tc.to_tokens(tokens),
605                GenericArgument::Const(e) => match *e {
606                    Expr::Lit(_) => e.to_tokens(tokens),
607
608                    // NOTE: We should probably support parsing blocks with only
609                    // expressions in them without the full feature for const
610                    // generics.
611                    #[cfg(feature = "full")]
612                    Expr::Block(_) => e.to_tokens(tokens),
613
614                    // ERROR CORRECTION: Add braces to make sure that the
615                    // generated code is valid.
616                    _ => token::Brace::default().surround(tokens, |tokens| {
617                        e.to_tokens(tokens);
618                    }),
619                },
620            }
621        }
622    }
623
624    impl ToTokens for AngleBracketedGenericArguments {
625        fn to_tokens(&self, tokens: &mut TokenStream) {
626            self.colon2_token.to_tokens(tokens);
627            self.lt_token.to_tokens(tokens);
628
629            // Print lifetimes before types and consts, all before bindings,
630            // regardless of their order in self.args.
631            //
632            // TODO: ordering rules for const arguments vs type arguments have
633            // not been settled yet. https://github.com/rust-lang/rust/issues/44580
634            let mut trailing_or_empty = true;
635            for param in self.args.pairs() {
636                match **param.value() {
637                    GenericArgument::Lifetime(_) => {
638                        param.to_tokens(tokens);
639                        trailing_or_empty = param.punct().is_some();
640                    }
641                    GenericArgument::Type(_)
642                    | GenericArgument::Binding(_)
643                    | GenericArgument::Constraint(_)
644                    | GenericArgument::Const(_) => {}
645                }
646            }
647            for param in self.args.pairs() {
648                match **param.value() {
649                    GenericArgument::Type(_) | GenericArgument::Const(_) => {
650                        if !trailing_or_empty {
651                            <Token![,]>::default().to_tokens(tokens);
652                        }
653                        param.to_tokens(tokens);
654                        trailing_or_empty = param.punct().is_some();
655                    }
656                    GenericArgument::Lifetime(_)
657                    | GenericArgument::Binding(_)
658                    | GenericArgument::Constraint(_) => {}
659                }
660            }
661            for param in self.args.pairs() {
662                match **param.value() {
663                    GenericArgument::Binding(_) | GenericArgument::Constraint(_) => {
664                        if !trailing_or_empty {
665                            <Token![,]>::default().to_tokens(tokens);
666                            trailing_or_empty = true;
667                        }
668                        param.to_tokens(tokens);
669                    }
670                    GenericArgument::Lifetime(_)
671                    | GenericArgument::Type(_)
672                    | GenericArgument::Const(_) => {}
673                }
674            }
675
676            self.gt_token.to_tokens(tokens);
677        }
678    }
679
680    impl ToTokens for Binding {
681        fn to_tokens(&self, tokens: &mut TokenStream) {
682            self.ident.to_tokens(tokens);
683            self.eq_token.to_tokens(tokens);
684            self.ty.to_tokens(tokens);
685        }
686    }
687
688    impl ToTokens for Constraint {
689        fn to_tokens(&self, tokens: &mut TokenStream) {
690            self.ident.to_tokens(tokens);
691            self.colon_token.to_tokens(tokens);
692            self.bounds.to_tokens(tokens);
693        }
694    }
695
696    impl ToTokens for ParenthesizedGenericArguments {
697        fn to_tokens(&self, tokens: &mut TokenStream) {
698            self.paren_token.surround(tokens, |tokens| {
699                self.inputs.to_tokens(tokens);
700            });
701            self.output.to_tokens(tokens);
702        }
703    }
704
705    impl private {
706        pub fn print_path(tokens: &mut TokenStream, qself: &Option<QSelf>, path: &Path) {
707            let qself = match qself {
708                Some(qself) => qself,
709                None => {
710                    path.to_tokens(tokens);
711                    return;
712                }
713            };
714            qself.lt_token.to_tokens(tokens);
715            qself.ty.to_tokens(tokens);
716
717            let pos = if qself.position > 0 && qself.position >= path.segments.len() {
718                path.segments.len() - 1
719            } else {
720                qself.position
721            };
722            let mut segments = path.segments.pairs();
723            if pos > 0 {
724                TokensOrDefault(&qself.as_token).to_tokens(tokens);
725                path.leading_colon.to_tokens(tokens);
726                for (i, segment) in segments.by_ref().take(pos).enumerate() {
727                    if i + 1 == pos {
728                        segment.value().to_tokens(tokens);
729                        qself.gt_token.to_tokens(tokens);
730                        segment.punct().to_tokens(tokens);
731                    } else {
732                        segment.to_tokens(tokens);
733                    }
734                }
735            } else {
736                qself.gt_token.to_tokens(tokens);
737                path.leading_colon.to_tokens(tokens);
738            }
739            for segment in segments {
740                segment.to_tokens(tokens);
741            }
742        }
743    }
744}