Skip to main content

syn/
path.rs

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