Skip to main content

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