syn/
path.rs

1#[cfg(feature = "parsing")]
2use crate::error::Result;
3use crate::expr::Expr;
4use crate::generics::TypeParamBound;
5use crate::ident::Ident;
6use crate::lifetime::Lifetime;
7use crate::punctuated::Punctuated;
8use crate::token;
9use crate::ty::{ReturnType, Type};
10
11ast_struct! {
12    /// A path at which a named item is exported (e.g. `std::collections::HashMap`).
13    #[cfg_attr(docsrs, doc(cfg(any(feature = "full", feature = "derive"))))]
14    pub struct Path {
15        pub leading_colon: Option<Token![::]>,
16        pub segments: Punctuated<PathSegment, Token![::]>,
17    }
18}
19
20impl<T> From<T> for Path
21where
22    T: Into<PathSegment>,
23{
24    fn from(segment: T) -> Self {
25        let mut path = Path {
26            leading_colon: None,
27            segments: Punctuated::new(),
28        };
29        path.segments.push_value(segment.into());
30        path
31    }
32}
33
34impl Path {
35    /// Determines whether this is a path of length 1 equal to the given
36    /// ident.
37    ///
38    /// For them to compare equal, it must be the case that:
39    ///
40    /// - the path has no leading colon,
41    /// - the number of path segments is 1,
42    /// - the first path segment has no angle bracketed or parenthesized
43    ///   path arguments, and
44    /// - the ident of the first path segment is equal to the given one.
45    ///
46    /// # Example
47    ///
48    /// ```
49    /// use proc_macro2::TokenStream;
50    /// use syn::{Attribute, Error, Meta, Result};
51    ///
52    /// fn get_serde_meta_item(attr: &Attribute) -> Result<Option<&TokenStream>> {
53    ///     if attr.path().is_ident("serde") {
54    ///         match &attr.meta {
55    ///             Meta::List(meta) => Ok(Some(&meta.tokens)),
56    ///             bad => Err(Error::new_spanned(bad, "unrecognized attribute")),
57    ///         }
58    ///     } else {
59    ///         Ok(None)
60    ///     }
61    /// }
62    /// ```
63    pub fn is_ident<I>(&self, ident: &I) -> bool
64    where
65        I: ?Sized,
66        Ident: PartialEq<I>,
67    {
68        match self.get_ident() {
69            Some(id) => id == ident,
70            None => false,
71        }
72    }
73
74    /// If this path consists of a single ident, returns the ident.
75    ///
76    /// A path is considered an ident if:
77    ///
78    /// - the path has no leading colon,
79    /// - the number of path segments is 1, and
80    /// - the first path segment has no angle bracketed or parenthesized
81    ///   path arguments.
82    pub fn get_ident(&self) -> Option<&Ident> {
83        if self.leading_colon.is_none()
84            && self.segments.len() == 1
85            && self.segments[0].arguments.is_none()
86        {
87            Some(&self.segments[0].ident)
88        } else {
89            None
90        }
91    }
92
93    /// An error if this path is not a single ident, as defined in `get_ident`.
94    #[cfg(feature = "parsing")]
95    #[cfg_attr(docsrs, doc(cfg(feature = "parsing")))]
96    pub fn require_ident(&self) -> Result<&Ident> {
97        self.get_ident().ok_or_else(|| {
98            crate::error::new2(
99                self.segments.first().unwrap().ident.span(),
100                self.segments.last().unwrap().ident.span(),
101                "expected this path to be an identifier",
102            )
103        })
104    }
105}
106
107ast_struct! {
108    /// A segment of a path together with any path arguments on that segment.
109    #[cfg_attr(docsrs, doc(cfg(any(feature = "full", feature = "derive"))))]
110    pub struct PathSegment {
111        pub ident: Ident,
112        pub arguments: PathArguments,
113    }
114}
115
116impl<T> From<T> for PathSegment
117where
118    T: Into<Ident>,
119{
120    fn from(ident: T) -> Self {
121        PathSegment {
122            ident: ident.into(),
123            arguments: PathArguments::None,
124        }
125    }
126}
127
128ast_enum! {
129    /// Angle bracketed or parenthesized arguments of a path segment.
130    ///
131    /// ## Angle bracketed
132    ///
133    /// The `<'a, T>` in `std::slice::iter<'a, T>`.
134    ///
135    /// ## Parenthesized
136    ///
137    /// The `(A, B) -> C` in `Fn(A, B) -> C`.
138    #[cfg_attr(docsrs, doc(cfg(any(feature = "full", feature = "derive"))))]
139    pub enum PathArguments {
140        None,
141        /// The `<'a, T>` in `std::slice::iter<'a, T>`.
142        AngleBracketed(AngleBracketedGenericArguments),
143        /// The `(A, B) -> C` in `Fn(A, B) -> C`.
144        Parenthesized(ParenthesizedGenericArguments),
145    }
146}
147
148impl Default for PathArguments {
149    fn default() -> Self {
150        PathArguments::None
151    }
152}
153
154impl PathArguments {
155    pub fn is_empty(&self) -> bool {
156        match self {
157            PathArguments::None => true,
158            PathArguments::AngleBracketed(bracketed) => bracketed.args.is_empty(),
159            PathArguments::Parenthesized(_) => false,
160        }
161    }
162
163    pub fn is_none(&self) -> bool {
164        match self {
165            PathArguments::None => true,
166            PathArguments::AngleBracketed(_) | PathArguments::Parenthesized(_) => false,
167        }
168    }
169}
170
171ast_enum! {
172    /// An individual generic argument, like `'a`, `T`, or `Item = T`.
173    #[cfg_attr(docsrs, doc(cfg(any(feature = "full", feature = "derive"))))]
174    #[non_exhaustive]
175    pub enum GenericArgument {
176        /// A lifetime argument.
177        Lifetime(Lifetime),
178        /// A type argument.
179        Type(Type),
180        /// A const expression. Must be inside of a block.
181        ///
182        /// NOTE: Identity expressions are represented as Type arguments, as
183        /// they are indistinguishable syntactically.
184        Const(Expr),
185        /// A binding (equality constraint) on an associated type: the `Item =
186        /// u8` in `Iterator<Item = u8>`.
187        AssocType(AssocType),
188        /// An equality constraint on an associated constant: the `PANIC =
189        /// false` in `Trait<PANIC = false>`.
190        AssocConst(AssocConst),
191        /// An associated type bound: `Iterator<Item: Display>`.
192        Constraint(Constraint),
193    }
194}
195
196ast_struct! {
197    /// Angle bracketed arguments of a path segment: the `<K, V>` in `HashMap<K,
198    /// V>`.
199    #[cfg_attr(docsrs, doc(cfg(any(feature = "full", feature = "derive"))))]
200    pub struct AngleBracketedGenericArguments {
201        pub colon2_token: Option<Token![::]>,
202        pub lt_token: Token![<],
203        pub args: Punctuated<GenericArgument, Token![,]>,
204        pub gt_token: Token![>],
205    }
206}
207
208ast_struct! {
209    /// A binding (equality constraint) on an associated type: the `Item = u8`
210    /// in `Iterator<Item = u8>`.
211    #[cfg_attr(docsrs, doc(cfg(any(feature = "full", feature = "derive"))))]
212    pub struct AssocType {
213        pub ident: Ident,
214        pub generics: Option<AngleBracketedGenericArguments>,
215        pub eq_token: Token![=],
216        pub ty: Type,
217    }
218}
219
220ast_struct! {
221    /// An equality constraint on an associated constant: the `PANIC = false` in
222    /// `Trait<PANIC = false>`.
223    #[cfg_attr(docsrs, doc(cfg(any(feature = "full", feature = "derive"))))]
224    pub struct AssocConst {
225        pub ident: Ident,
226        pub generics: Option<AngleBracketedGenericArguments>,
227        pub eq_token: Token![=],
228        pub value: Expr,
229    }
230}
231
232ast_struct! {
233    /// An associated type bound: `Iterator<Item: Display>`.
234    #[cfg_attr(docsrs, doc(cfg(any(feature = "full", feature = "derive"))))]
235    pub struct Constraint {
236        pub ident: Ident,
237        pub generics: Option<AngleBracketedGenericArguments>,
238        pub colon_token: Token![:],
239        pub bounds: Punctuated<TypeParamBound, Token![+]>,
240    }
241}
242
243ast_struct! {
244    /// Arguments of a function path segment: the `(A, B) -> C` in `Fn(A,B) ->
245    /// C`.
246    #[cfg_attr(docsrs, doc(cfg(any(feature = "full", feature = "derive"))))]
247    pub struct ParenthesizedGenericArguments {
248        pub paren_token: token::Paren,
249        /// `(A, B)`
250        pub inputs: Punctuated<Type, Token![,]>,
251        /// `C`
252        pub output: ReturnType,
253    }
254}
255
256ast_struct! {
257    /// The explicit Self type in a qualified path: the `T` in `<T as
258    /// Display>::fmt`.
259    ///
260    /// The actual path, including the trait and the associated item, is stored
261    /// separately. The `position` field represents the index of the associated
262    /// item qualified with this Self type.
263    ///
264    /// ```text
265    /// <Vec<T> as a::b::Trait>::AssociatedItem
266    ///  ^~~~~~    ~~~~~~~~~~~~~~^
267    ///  ty        position = 3
268    ///
269    /// <Vec<T>>::AssociatedItem
270    ///  ^~~~~~   ^
271    ///  ty       position = 0
272    /// ```
273    #[cfg_attr(docsrs, doc(cfg(any(feature = "full", feature = "derive"))))]
274    pub struct QSelf {
275        pub lt_token: Token![<],
276        pub ty: Box<Type>,
277        pub position: usize,
278        pub as_token: Option<Token![as]>,
279        pub gt_token: Token![>],
280    }
281}
282
283#[cfg(feature = "parsing")]
284pub(crate) mod parsing {
285    use crate::error::Result;
286    #[cfg(feature = "full")]
287    use crate::expr::ExprBlock;
288    use crate::expr::{Expr, ExprPath};
289    use crate::ext::IdentExt as _;
290    #[cfg(feature = "full")]
291    use crate::generics::TypeParamBound;
292    use crate::ident::Ident;
293    use crate::lifetime::Lifetime;
294    use crate::lit::Lit;
295    use crate::parse::{Parse, ParseStream};
296    #[cfg(feature = "full")]
297    use crate::path::Constraint;
298    use crate::path::{
299        AngleBracketedGenericArguments, AssocConst, AssocType, GenericArgument,
300        ParenthesizedGenericArguments, Path, PathArguments, PathSegment, QSelf,
301    };
302    use crate::punctuated::Punctuated;
303    use crate::token;
304    use crate::ty::{ReturnType, Type};
305    #[cfg(not(feature = "full"))]
306    use crate::verbatim;
307
308    #[cfg_attr(docsrs, doc(cfg(feature = "parsing")))]
309    impl Parse for Path {
310        fn parse(input: ParseStream) -> Result<Self> {
311            Self::parse_helper(input, false)
312        }
313    }
314
315    #[cfg_attr(docsrs, doc(cfg(feature = "parsing")))]
316    impl Parse for GenericArgument {
317        fn parse(input: ParseStream) -> Result<Self> {
318            if input.peek(Lifetime) && !input.peek2(Token![+]) {
319                return Ok(GenericArgument::Lifetime(input.parse()?));
320            }
321
322            if input.peek(Lit) || input.peek(token::Brace) {
323                return const_argument(input).map(GenericArgument::Const);
324            }
325
326            let mut argument: Type = input.parse()?;
327
328            match argument {
329                Type::Path(mut ty)
330                    if ty.qself.is_none()
331                        && ty.path.leading_colon.is_none()
332                        && ty.path.segments.len() == 1
333                        && match &ty.path.segments[0].arguments {
334                            PathArguments::None | PathArguments::AngleBracketed(_) => true,
335                            PathArguments::Parenthesized(_) => false,
336                        } =>
337                {
338                    if let Some(eq_token) = input.parse::<Option<Token![=]>>()? {
339                        let segment = ty.path.segments.pop().unwrap().into_value();
340                        let ident = segment.ident;
341                        let generics = match segment.arguments {
342                            PathArguments::None => None,
343                            PathArguments::AngleBracketed(arguments) => Some(arguments),
344                            PathArguments::Parenthesized(_) => unreachable!(),
345                        };
346                        return if input.peek(Lit) || input.peek(token::Brace) {
347                            Ok(GenericArgument::AssocConst(AssocConst {
348                                ident,
349                                generics,
350                                eq_token,
351                                value: const_argument(input)?,
352                            }))
353                        } else {
354                            Ok(GenericArgument::AssocType(AssocType {
355                                ident,
356                                generics,
357                                eq_token,
358                                ty: input.parse()?,
359                            }))
360                        };
361                    }
362
363                    #[cfg(feature = "full")]
364                    if let Some(colon_token) = input.parse::<Option<Token![:]>>()? {
365                        let segment = ty.path.segments.pop().unwrap().into_value();
366                        return Ok(GenericArgument::Constraint(Constraint {
367                            ident: segment.ident,
368                            generics: match segment.arguments {
369                                PathArguments::None => None,
370                                PathArguments::AngleBracketed(arguments) => Some(arguments),
371                                PathArguments::Parenthesized(_) => unreachable!(),
372                            },
373                            colon_token,
374                            bounds: {
375                                let mut bounds = Punctuated::new();
376                                loop {
377                                    if input.peek(Token![,]) || input.peek(Token![>]) {
378                                        break;
379                                    }
380                                    bounds.push_value({
381                                        let allow_precise_capture = false;
382                                        let allow_tilde_const = true;
383                                        TypeParamBound::parse_single(
384                                            input,
385                                            allow_precise_capture,
386                                            allow_tilde_const,
387                                        )?
388                                    });
389                                    if !input.peek(Token![+]) {
390                                        break;
391                                    }
392                                    let punct: Token![+] = input.parse()?;
393                                    bounds.push_punct(punct);
394                                }
395                                bounds
396                            },
397                        }));
398                    }
399
400                    argument = Type::Path(ty);
401                }
402                _ => {}
403            }
404
405            Ok(GenericArgument::Type(argument))
406        }
407    }
408
409    pub(crate) fn const_argument(input: ParseStream) -> Result<Expr> {
410        let lookahead = input.lookahead1();
411
412        if input.peek(Lit) {
413            let lit = input.parse()?;
414            return Ok(Expr::Lit(lit));
415        }
416
417        if input.peek(Ident) {
418            let ident: Ident = input.parse()?;
419            return Ok(Expr::Path(ExprPath {
420                attrs: Vec::new(),
421                qself: None,
422                path: Path::from(ident),
423            }));
424        }
425
426        if input.peek(token::Brace) {
427            #[cfg(feature = "full")]
428            {
429                let block: ExprBlock = input.parse()?;
430                return Ok(Expr::Block(block));
431            }
432
433            #[cfg(not(feature = "full"))]
434            {
435                let begin = input.fork();
436                let content;
437                braced!(content in input);
438                content.parse::<Expr>()?;
439                let verbatim = verbatim::between(&begin, input);
440                return Ok(Expr::Verbatim(verbatim));
441            }
442        }
443
444        Err(lookahead.error())
445    }
446
447    impl AngleBracketedGenericArguments {
448        /// Parse `::<…>` with mandatory leading `::`.
449        ///
450        /// The ordinary [`Parse`] impl for `AngleBracketedGenericArguments`
451        /// parses optional leading `::`.
452        #[cfg(feature = "full")]
453        #[cfg_attr(docsrs, doc(cfg(all(feature = "parsing", feature = "full"))))]
454        pub fn parse_turbofish(input: ParseStream) -> Result<Self> {
455            let colon2_token: Token![::] = input.parse()?;
456            Self::do_parse(Some(colon2_token), input)
457        }
458
459        pub(crate) fn do_parse(
460            colon2_token: Option<Token![::]>,
461            input: ParseStream,
462        ) -> Result<Self> {
463            Ok(AngleBracketedGenericArguments {
464                colon2_token,
465                lt_token: input.parse()?,
466                args: {
467                    let mut args = Punctuated::new();
468                    loop {
469                        if input.peek(Token![>]) {
470                            break;
471                        }
472                        let value: GenericArgument = input.parse()?;
473                        args.push_value(value);
474                        if input.peek(Token![>]) {
475                            break;
476                        }
477                        let punct: Token![,] = input.parse()?;
478                        args.push_punct(punct);
479                    }
480                    args
481                },
482                gt_token: input.parse()?,
483            })
484        }
485    }
486
487    #[cfg_attr(docsrs, doc(cfg(feature = "parsing")))]
488    impl Parse for AngleBracketedGenericArguments {
489        fn parse(input: ParseStream) -> Result<Self> {
490            let colon2_token: Option<Token![::]> = input.parse()?;
491            Self::do_parse(colon2_token, input)
492        }
493    }
494
495    #[cfg_attr(docsrs, doc(cfg(feature = "parsing")))]
496    impl Parse for ParenthesizedGenericArguments {
497        fn parse(input: ParseStream) -> Result<Self> {
498            let content;
499            Ok(ParenthesizedGenericArguments {
500                paren_token: parenthesized!(content in input),
501                inputs: content.parse_terminated(Type::parse, Token![,])?,
502                output: input.call(ReturnType::without_plus)?,
503            })
504        }
505    }
506
507    #[cfg_attr(docsrs, doc(cfg(feature = "parsing")))]
508    impl Parse for PathSegment {
509        fn parse(input: ParseStream) -> Result<Self> {
510            Self::parse_helper(input, false)
511        }
512    }
513
514    impl PathSegment {
515        fn parse_helper(input: ParseStream, expr_style: bool) -> Result<Self> {
516            if input.peek(Token![super])
517                || input.peek(Token![self])
518                || input.peek(Token![crate])
519                || cfg!(feature = "full") && input.peek(Token![try])
520            {
521                let ident = input.call(Ident::parse_any)?;
522                return Ok(PathSegment::from(ident));
523            }
524
525            let ident = if input.peek(Token![Self]) {
526                input.call(Ident::parse_any)?
527            } else {
528                input.parse()?
529            };
530
531            if !expr_style
532                && input.peek(Token![<])
533                && !input.peek(Token![<=])
534                && !input.peek(Token![<<=])
535                || input.peek(Token![::]) && input.peek3(Token![<])
536            {
537                Ok(PathSegment {
538                    ident,
539                    arguments: PathArguments::AngleBracketed(input.parse()?),
540                })
541            } else {
542                Ok(PathSegment::from(ident))
543            }
544        }
545    }
546
547    impl Path {
548        /// Parse a `Path` containing no path arguments on any of its segments.
549        ///
550        /// # Example
551        ///
552        /// ```
553        /// use syn::{Path, Result, Token};
554        /// use syn::parse::{Parse, ParseStream};
555        ///
556        /// // A simplified single `use` statement like:
557        /// //
558        /// //     use std::collections::HashMap;
559        /// //
560        /// // Note that generic parameters are not allowed in a `use` statement
561        /// // so the following must not be accepted.
562        /// //
563        /// //     use a::<b>::c;
564        /// struct SingleUse {
565        ///     use_token: Token![use],
566        ///     path: Path,
567        /// }
568        ///
569        /// impl Parse for SingleUse {
570        ///     fn parse(input: ParseStream) -> Result<Self> {
571        ///         Ok(SingleUse {
572        ///             use_token: input.parse()?,
573        ///             path: input.call(Path::parse_mod_style)?,
574        ///         })
575        ///     }
576        /// }
577        /// ```
578        #[cfg_attr(docsrs, doc(cfg(feature = "parsing")))]
579        pub fn parse_mod_style(input: ParseStream) -> Result<Self> {
580            Ok(Path {
581                leading_colon: input.parse()?,
582                segments: {
583                    let mut segments = Punctuated::new();
584                    loop {
585                        if !input.peek(Ident)
586                            && !input.peek(Token![super])
587                            && !input.peek(Token![self])
588                            && !input.peek(Token![Self])
589                            && !input.peek(Token![crate])
590                        {
591                            break;
592                        }
593                        let ident = Ident::parse_any(input)?;
594                        segments.push_value(PathSegment::from(ident));
595                        if !input.peek(Token![::]) {
596                            break;
597                        }
598                        let punct = input.parse()?;
599                        segments.push_punct(punct);
600                    }
601                    if segments.is_empty() {
602                        return Err(input.parse::<Ident>().unwrap_err());
603                    } else if segments.trailing_punct() {
604                        return Err(input.error("expected path segment after `::`"));
605                    }
606                    segments
607                },
608            })
609        }
610
611        pub(crate) fn parse_helper(input: ParseStream, expr_style: bool) -> Result<Self> {
612            let mut path = Path {
613                leading_colon: input.parse()?,
614                segments: {
615                    let mut segments = Punctuated::new();
616                    let value = PathSegment::parse_helper(input, expr_style)?;
617                    segments.push_value(value);
618                    segments
619                },
620            };
621            Path::parse_rest(input, &mut path, expr_style)?;
622            Ok(path)
623        }
624
625        pub(crate) fn parse_rest(
626            input: ParseStream,
627            path: &mut Self,
628            expr_style: bool,
629        ) -> Result<()> {
630            while input.peek(Token![::]) && !input.peek3(token::Paren) {
631                let punct: Token![::] = input.parse()?;
632                path.segments.push_punct(punct);
633                let value = PathSegment::parse_helper(input, expr_style)?;
634                path.segments.push_value(value);
635            }
636            Ok(())
637        }
638
639        pub(crate) fn is_mod_style(&self) -> bool {
640            self.segments
641                .iter()
642                .all(|segment| segment.arguments.is_none())
643        }
644    }
645
646    pub(crate) fn qpath(input: ParseStream, expr_style: bool) -> Result<(Option<QSelf>, Path)> {
647        if input.peek(Token![<]) {
648            let lt_token: Token![<] = input.parse()?;
649            let this: Type = input.parse()?;
650            let path = if input.peek(Token![as]) {
651                let as_token: Token![as] = input.parse()?;
652                let path: Path = input.parse()?;
653                Some((as_token, path))
654            } else {
655                None
656            };
657            let gt_token: Token![>] = input.parse()?;
658            let colon2_token: Token![::] = input.parse()?;
659            let mut rest = Punctuated::new();
660            loop {
661                let path = PathSegment::parse_helper(input, expr_style)?;
662                rest.push_value(path);
663                if !input.peek(Token![::]) {
664                    break;
665                }
666                let punct: Token![::] = input.parse()?;
667                rest.push_punct(punct);
668            }
669            let (position, as_token, path) = match path {
670                Some((as_token, mut path)) => {
671                    let pos = path.segments.len();
672                    path.segments.push_punct(colon2_token);
673                    path.segments.extend(rest.into_pairs());
674                    (pos, Some(as_token), path)
675                }
676                None => {
677                    let path = Path {
678                        leading_colon: Some(colon2_token),
679                        segments: rest,
680                    };
681                    (0, None, path)
682                }
683            };
684            let qself = QSelf {
685                lt_token,
686                ty: Box::new(this),
687                position,
688                as_token,
689                gt_token,
690            };
691            Ok((Some(qself), path))
692        } else {
693            let path = Path::parse_helper(input, expr_style)?;
694            Ok((None, path))
695        }
696    }
697}
698
699#[cfg(feature = "printing")]
700pub(crate) mod printing {
701    use crate::generics;
702    use crate::path::{
703        AngleBracketedGenericArguments, AssocConst, AssocType, Constraint, GenericArgument,
704        ParenthesizedGenericArguments, Path, PathArguments, PathSegment, QSelf,
705    };
706    use crate::print::TokensOrDefault;
707    #[cfg(feature = "parsing")]
708    use crate::spanned::Spanned;
709    #[cfg(feature = "parsing")]
710    use proc_macro2::Span;
711    use proc_macro2::TokenStream;
712    use quote::ToTokens;
713    use std::cmp;
714
715    pub(crate) enum PathStyle {
716        Expr,
717        Mod,
718        AsWritten,
719    }
720
721    impl Copy for PathStyle {}
722
723    impl Clone for PathStyle {
724        fn clone(&self) -> Self {
725            *self
726        }
727    }
728
729    #[cfg_attr(docsrs, doc(cfg(feature = "printing")))]
730    impl ToTokens for Path {
731        fn to_tokens(&self, tokens: &mut TokenStream) {
732            print_path(tokens, self, PathStyle::AsWritten);
733        }
734    }
735
736    pub(crate) fn print_path(tokens: &mut TokenStream, path: &Path, style: PathStyle) {
737        path.leading_colon.to_tokens(tokens);
738        for segment in path.segments.pairs() {
739            print_path_segment(tokens, segment.value(), style);
740            segment.punct().to_tokens(tokens);
741        }
742    }
743
744    #[cfg_attr(docsrs, doc(cfg(feature = "printing")))]
745    impl ToTokens for PathSegment {
746        fn to_tokens(&self, tokens: &mut TokenStream) {
747            print_path_segment(tokens, self, PathStyle::AsWritten);
748        }
749    }
750
751    fn print_path_segment(tokens: &mut TokenStream, segment: &PathSegment, style: PathStyle) {
752        segment.ident.to_tokens(tokens);
753        print_path_arguments(tokens, &segment.arguments, style);
754    }
755
756    #[cfg_attr(docsrs, doc(cfg(feature = "printing")))]
757    impl ToTokens for PathArguments {
758        fn to_tokens(&self, tokens: &mut TokenStream) {
759            print_path_arguments(tokens, self, PathStyle::AsWritten);
760        }
761    }
762
763    fn print_path_arguments(tokens: &mut TokenStream, arguments: &PathArguments, style: PathStyle) {
764        match arguments {
765            PathArguments::None => {}
766            PathArguments::AngleBracketed(arguments) => {
767                print_angle_bracketed_generic_arguments(tokens, arguments, style);
768            }
769            PathArguments::Parenthesized(arguments) => {
770                print_parenthesized_generic_arguments(tokens, arguments, style);
771            }
772        }
773    }
774
775    #[cfg_attr(docsrs, doc(cfg(feature = "printing")))]
776    impl ToTokens for GenericArgument {
777        #[allow(clippy::match_same_arms)]
778        fn to_tokens(&self, tokens: &mut TokenStream) {
779            match self {
780                GenericArgument::Lifetime(lt) => lt.to_tokens(tokens),
781                GenericArgument::Type(ty) => ty.to_tokens(tokens),
782                GenericArgument::Const(expr) => {
783                    generics::printing::print_const_argument(expr, tokens);
784                }
785                GenericArgument::AssocType(assoc) => assoc.to_tokens(tokens),
786                GenericArgument::AssocConst(assoc) => assoc.to_tokens(tokens),
787                GenericArgument::Constraint(constraint) => constraint.to_tokens(tokens),
788            }
789        }
790    }
791
792    #[cfg_attr(docsrs, doc(cfg(feature = "printing")))]
793    impl ToTokens for AngleBracketedGenericArguments {
794        fn to_tokens(&self, tokens: &mut TokenStream) {
795            print_angle_bracketed_generic_arguments(tokens, self, PathStyle::AsWritten);
796        }
797    }
798
799    pub(crate) fn print_angle_bracketed_generic_arguments(
800        tokens: &mut TokenStream,
801        arguments: &AngleBracketedGenericArguments,
802        style: PathStyle,
803    ) {
804        if let PathStyle::Mod = style {
805            return;
806        }
807
808        conditionally_print_turbofish(tokens, &arguments.colon2_token, style);
809        arguments.lt_token.to_tokens(tokens);
810
811        // Print lifetimes before types/consts/bindings, regardless of their
812        // order in args.
813        let mut trailing_or_empty = true;
814        for param in arguments.args.pairs() {
815            match param.value() {
816                GenericArgument::Lifetime(_) => {
817                    param.to_tokens(tokens);
818                    trailing_or_empty = param.punct().is_some();
819                }
820                GenericArgument::Type(_)
821                | GenericArgument::Const(_)
822                | GenericArgument::AssocType(_)
823                | GenericArgument::AssocConst(_)
824                | GenericArgument::Constraint(_) => {}
825            }
826        }
827        for param in arguments.args.pairs() {
828            match param.value() {
829                GenericArgument::Type(_)
830                | GenericArgument::Const(_)
831                | GenericArgument::AssocType(_)
832                | GenericArgument::AssocConst(_)
833                | GenericArgument::Constraint(_) => {
834                    if !trailing_or_empty {
835                        <Token![,]>::default().to_tokens(tokens);
836                    }
837                    param.to_tokens(tokens);
838                    trailing_or_empty = param.punct().is_some();
839                }
840                GenericArgument::Lifetime(_) => {}
841            }
842        }
843
844        arguments.gt_token.to_tokens(tokens);
845    }
846
847    #[cfg_attr(docsrs, doc(cfg(feature = "printing")))]
848    impl ToTokens for AssocType {
849        fn to_tokens(&self, tokens: &mut TokenStream) {
850            self.ident.to_tokens(tokens);
851            self.generics.to_tokens(tokens);
852            self.eq_token.to_tokens(tokens);
853            self.ty.to_tokens(tokens);
854        }
855    }
856
857    #[cfg_attr(docsrs, doc(cfg(feature = "printing")))]
858    impl ToTokens for AssocConst {
859        fn to_tokens(&self, tokens: &mut TokenStream) {
860            self.ident.to_tokens(tokens);
861            self.generics.to_tokens(tokens);
862            self.eq_token.to_tokens(tokens);
863            generics::printing::print_const_argument(&self.value, tokens);
864        }
865    }
866
867    #[cfg_attr(docsrs, doc(cfg(feature = "printing")))]
868    impl ToTokens for Constraint {
869        fn to_tokens(&self, tokens: &mut TokenStream) {
870            self.ident.to_tokens(tokens);
871            self.generics.to_tokens(tokens);
872            self.colon_token.to_tokens(tokens);
873            self.bounds.to_tokens(tokens);
874        }
875    }
876
877    #[cfg_attr(docsrs, doc(cfg(feature = "printing")))]
878    impl ToTokens for ParenthesizedGenericArguments {
879        fn to_tokens(&self, tokens: &mut TokenStream) {
880            print_parenthesized_generic_arguments(tokens, self, PathStyle::AsWritten);
881        }
882    }
883
884    fn print_parenthesized_generic_arguments(
885        tokens: &mut TokenStream,
886        arguments: &ParenthesizedGenericArguments,
887        style: PathStyle,
888    ) {
889        if let PathStyle::Mod = style {
890            return;
891        }
892
893        conditionally_print_turbofish(tokens, &None, style);
894        arguments.paren_token.surround(tokens, |tokens| {
895            arguments.inputs.to_tokens(tokens);
896        });
897        arguments.output.to_tokens(tokens);
898    }
899
900    pub(crate) fn print_qpath(
901        tokens: &mut TokenStream,
902        qself: &Option<QSelf>,
903        path: &Path,
904        style: PathStyle,
905    ) {
906        let qself = match qself {
907            Some(qself) => qself,
908            None => {
909                print_path(tokens, path, style);
910                return;
911            }
912        };
913        qself.lt_token.to_tokens(tokens);
914        qself.ty.to_tokens(tokens);
915
916        let pos = cmp::min(qself.position, path.segments.len());
917        let mut segments = path.segments.pairs();
918        if pos > 0 {
919            TokensOrDefault(&qself.as_token).to_tokens(tokens);
920            path.leading_colon.to_tokens(tokens);
921            for (i, segment) in segments.by_ref().take(pos).enumerate() {
922                print_path_segment(tokens, segment.value(), PathStyle::AsWritten);
923                if i + 1 == pos {
924                    qself.gt_token.to_tokens(tokens);
925                }
926                segment.punct().to_tokens(tokens);
927            }
928        } else {
929            qself.gt_token.to_tokens(tokens);
930            path.leading_colon.to_tokens(tokens);
931        }
932        for segment in segments {
933            print_path_segment(tokens, segment.value(), style);
934            segment.punct().to_tokens(tokens);
935        }
936    }
937
938    fn conditionally_print_turbofish(
939        tokens: &mut TokenStream,
940        colon2_token: &Option<Token![::]>,
941        style: PathStyle,
942    ) {
943        match style {
944            PathStyle::Expr => TokensOrDefault(colon2_token).to_tokens(tokens),
945            PathStyle::Mod => unreachable!(),
946            PathStyle::AsWritten => colon2_token.to_tokens(tokens),
947        }
948    }
949
950    #[cfg(feature = "parsing")]
951    #[cfg_attr(docsrs, doc(cfg(all(feature = "parsing", feature = "printing"))))]
952    impl Spanned for QSelf {
953        fn span(&self) -> Span {
954            struct QSelfDelimiters<'a>(&'a QSelf);
955
956            impl<'a> ToTokens for QSelfDelimiters<'a> {
957                fn to_tokens(&self, tokens: &mut TokenStream) {
958                    self.0.lt_token.to_tokens(tokens);
959                    self.0.gt_token.to_tokens(tokens);
960                }
961            }
962
963            QSelfDelimiters(self).span()
964        }
965    }
966}