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
12#[doc =
r" A path at which a named item is exported (e.g. `alloc::collections::HashMap`)."]
pub struct Path {
    pub leading_colon: Option<crate::token::PathSep>,
    pub segments: Punctuated<PathSegment, crate::token::PathSep>,
}ast_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
108#[doc =
r" A segment of a path together with any path arguments on that segment."]
pub struct PathSegment {
    pub ident: Ident,
    pub arguments: PathArguments,
}ast_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
129#[doc = r" Angle bracketed or parenthesized arguments of a path segment."]
#[doc = r""]
#[doc = r" ## Angle bracketed"]
#[doc = r""]
#[doc = r" The `<'a, T>` in `core::slice::iter<'a, T>`."]
#[doc = r""]
#[doc = r" ## Parenthesized"]
#[doc = r""]
#[doc = r" The `(A, B) -> C` in `Fn(A, B) -> C`."]
pub enum PathArguments {
    None,

    #[doc = r" The `<'a, T>` in `core::slice::iter<'a, T>`."]
    AngleBracketed(AngleBracketedGenericArguments),

    #[doc = r" The `(A, B) -> C` in `Fn(A, B) -> C`."]
    Parenthesized(ParenthesizedGenericArguments),
}ast_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
172#[doc = r" An individual generic argument, like `'a`, `T`, or `Item = T`."]
#[non_exhaustive]
pub enum GenericArgument {

    #[doc = r" A lifetime argument."]
    Lifetime(Lifetime),

    #[doc = r" A type argument."]
    Type(Type),

    #[doc = r" A const expression. Must be inside of a block."]
    #[doc = r""]
    #[doc =
    r" NOTE: Identity expressions are represented as Type arguments, as"]
    #[doc = r" they are indistinguishable syntactically."]
    Const(Expr),

    #[doc =
    r" A binding (equality constraint) on an associated type: the `Item ="]
    #[doc = r" u8` in `Iterator<Item = u8>`."]
    AssocType(AssocType),

    #[doc =
    r" An equality constraint on an associated constant: the `PANIC ="]
    #[doc = r" false` in `Trait<PANIC = false>`."]
    AssocConst(AssocConst),

    #[doc = r" An associated type bound: `Iterator<Item: Display>`."]
    Constraint(Constraint),
}ast_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
197#[doc =
r" Angle bracketed arguments of a path segment: the `<K, V>` in `HashMap<K,"]
#[doc = r" V>`."]
pub struct AngleBracketedGenericArguments {
    pub colon2_token: Option<crate::token::PathSep>,
    pub lt_token: crate::token::Lt,
    pub args: Punctuated<GenericArgument, crate::token::Comma>,
    pub gt_token: crate::token::Gt,
}ast_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
209#[doc =
r" A binding (equality constraint) on an associated type: the `Item = u8`"]
#[doc = r" in `Iterator<Item = u8>`."]
pub struct AssocType {
    pub ident: Ident,
    pub generics: Option<AngleBracketedGenericArguments>,
    pub eq_token: crate::token::Eq,
    pub ty: Type,
}ast_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
221#[doc =
r" An equality constraint on an associated constant: the `PANIC = false` in"]
#[doc = r" `Trait<PANIC = false>`."]
pub struct AssocConst {
    pub ident: Ident,
    pub generics: Option<AngleBracketedGenericArguments>,
    pub eq_token: crate::token::Eq,
    pub value: Expr,
}ast_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
233#[doc = r" An associated type bound: `Iterator<Item: Display>`."]
pub struct Constraint {
    pub ident: Ident,
    pub generics: Option<AngleBracketedGenericArguments>,
    pub colon_token: crate::token::Colon,
    pub bounds: Punctuated<TypeParamBound, crate::token::Plus>,
}ast_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
244#[doc =
r" Arguments of a function path segment: the `(A, B) -> C` in `Fn(A,B) ->"]
#[doc = r" C`."]
pub struct ParenthesizedGenericArguments {
    pub paren_token: token::Paren,
    #[doc = r" `(A, B)`"]
    pub inputs: Punctuated<Type, crate::token::Comma>,
    #[doc = r" `C`"]
    pub output: ReturnType,
}ast_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
257#[doc = r" The explicit Self type in a qualified path: the `T` in `<T as"]
#[doc = r" Display>::fmt`."]
#[doc = r""]
#[doc =
r" The actual path, including the trait and the associated item, is stored"]
#[doc =
r" separately. The `position` field represents the index of the associated"]
#[doc = r" item qualified with this Self type."]
#[doc = r""]
#[doc = r" ```text"]
#[doc = r" <Vec<T> as a::b::Trait>::AssociatedItem"]
#[doc = r"  ^~~~~~    ~~~~~~~~~~~~~~^"]
#[doc = r"  ty        position = 3"]
#[doc = r""]
#[doc = r" <Vec<T>>::AssociatedItem"]
#[doc = r"  ^~~~~~   ^"]
#[doc = r"  ty       position = 0"]
#[doc = r" ```"]
pub struct QSelf {
    pub lt_token: crate::token::Lt,
    pub ty: Box<Type>,
    pub position: usize,
    pub as_token: Option<crate::token::As>,
    pub gt_token: crate::token::Gt,
}ast_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    #[cfg(feature = "full")]
292    use crate::generics::TypeParamBound;
293    use crate::ident::Ident;
294    use crate::lifetime::Lifetime;
295    use crate::lit::Lit;
296    use crate::parse::{Parse, ParseStream};
297    #[cfg(feature = "full")]
298    use crate::path::Constraint;
299    use crate::path::{
300        AngleBracketedGenericArguments, AssocConst, AssocType, GenericArgument,
301        ParenthesizedGenericArguments, Path, PathArguments, PathSegment, QSelf,
302    };
303    use crate::punctuated::Punctuated;
304    use crate::token;
305    use crate::ty::{ReturnType, Type};
306    #[cfg(not(feature = "full"))]
307    use crate::verbatim;
308    use alloc::boxed::Box;
309    use alloc::vec::Vec;
310
311    #[cfg_attr(docsrs, doc(cfg(feature = "parsing")))]
312    impl Parse for Path {
313        fn parse(input: ParseStream) -> Result<Self> {
314            Self::parse_helper(input, false)
315        }
316    }
317
318    #[cfg_attr(docsrs, doc(cfg(feature = "parsing")))]
319    impl Parse for GenericArgument {
320        fn parse(input: ParseStream) -> Result<Self> {
321            if input.peek(Lifetime) && !input.peek2(crate::token::PlusToken![+]) {
322                return Ok(GenericArgument::Lifetime(input.parse()?));
323            }
324
325            if input.peek(Lit) || input.peek(token::Brace) {
326                return const_argument(input).map(GenericArgument::Const);
327            }
328
329            let mut argument: Type = input.parse()?;
330
331            match argument {
332                Type::Path(mut ty)
333                    if ty.qself.is_none()
334                        && ty.path.leading_colon.is_none()
335                        && ty.path.segments.len() == 1
336                        && match &ty.path.segments[0].arguments {
337                            PathArguments::None | PathArguments::AngleBracketed(_) => true,
338                            PathArguments::Parenthesized(_) => false,
339                        } =>
340                {
341                    if let Some(eq_token) = input.parse::<Option<crate::token::EqToken![=]>>()? {
342                        let segment = ty.path.segments.pop().unwrap().into_value();
343                        let ident = segment.ident;
344                        let generics = match segment.arguments {
345                            PathArguments::None => None,
346                            PathArguments::AngleBracketed(arguments) => Some(arguments),
347                            PathArguments::Parenthesized(_) => ::core::panicking::panic("internal error: entered unreachable code")unreachable!(),
348                        };
349                        return if input.peek(Lit) || input.peek(token::Brace) {
350                            Ok(GenericArgument::AssocConst(AssocConst {
351                                ident,
352                                generics,
353                                eq_token,
354                                value: const_argument(input)?,
355                            }))
356                        } else {
357                            Ok(GenericArgument::AssocType(AssocType {
358                                ident,
359                                generics,
360                                eq_token,
361                                ty: input.parse()?,
362                            }))
363                        };
364                    }
365
366                    #[cfg(feature = "full")]
367                    if let Some(colon_token) = input.parse::<Option<crate::token::ColonToken![:]>>()? {
368                        let segment = ty.path.segments.pop().unwrap().into_value();
369                        return Ok(GenericArgument::Constraint(Constraint {
370                            ident: segment.ident,
371                            generics: match segment.arguments {
372                                PathArguments::None => None,
373                                PathArguments::AngleBracketed(arguments) => Some(arguments),
374                                PathArguments::Parenthesized(_) => ::core::panicking::panic("internal error: entered unreachable code")unreachable!(),
375                            },
376                            colon_token,
377                            bounds: {
378                                let mut bounds = Punctuated::new();
379                                loop {
380                                    if input.peek(crate::token::CommaToken![,]) || input.peek(crate::token::GtToken![>]) {
381                                        break;
382                                    }
383                                    bounds.push_value({
384                                        let allow_precise_capture = false;
385                                        let allow_const = true;
386                                        TypeParamBound::parse_single(
387                                            input,
388                                            allow_precise_capture,
389                                            allow_const,
390                                        )?
391                                    });
392                                    if !input.peek(crate::token::PlusToken![+]) {
393                                        break;
394                                    }
395                                    let punct: crate::token::PlusToken![+] = input.parse()?;
396                                    bounds.push_punct(punct);
397                                }
398                                bounds
399                            },
400                        }));
401                    }
402
403                    argument = Type::Path(ty);
404                }
405                _ => {}
406            }
407
408            Ok(GenericArgument::Type(argument))
409        }
410    }
411
412    pub(crate) fn const_argument(input: ParseStream) -> Result<Expr> {
413        let lookahead = input.lookahead1();
414
415        if input.peek(Lit) {
416            let lit = input.parse()?;
417            return Ok(Expr::Lit(lit));
418        }
419
420        if input.peek(Ident) {
421            let ident: Ident = input.parse()?;
422            return Ok(Expr::Path(ExprPath {
423                attrs: Vec::new(),
424                qself: None,
425                path: Path::from(ident),
426            }));
427        }
428
429        if input.peek(token::Brace) {
430            #[cfg(feature = "full")]
431            {
432                let block: ExprBlock = input.parse()?;
433                return Ok(Expr::Block(block));
434            }
435
436            #[cfg(not(feature = "full"))]
437            {
438                let begin = input.fork();
439                let content;
440                braced!(content in input);
441                content.parse::<Expr>()?;
442                let verbatim = verbatim::between(&begin, input);
443                return Ok(Expr::Verbatim(verbatim));
444            }
445        }
446
447        Err(lookahead.error())
448    }
449
450    impl AngleBracketedGenericArguments {
451        /// Parse `::<…>` with mandatory leading `::`.
452        ///
453        /// The ordinary [`Parse`] impl for `AngleBracketedGenericArguments`
454        /// parses optional leading `::`.
455        #[cfg(feature = "full")]
456        #[cfg_attr(docsrs, doc(cfg(all(feature = "parsing", feature = "full"))))]
457        pub fn parse_turbofish(input: ParseStream) -> Result<Self> {
458            let colon2_token: crate::token::PathSepToken![::] = input.parse()?;
459            Self::do_parse(Some(colon2_token), input)
460        }
461
462        pub(crate) fn do_parse(
463            colon2_token: Option<crate::token::PathSepToken![::]>,
464            input: ParseStream,
465        ) -> Result<Self> {
466            Ok(AngleBracketedGenericArguments {
467                colon2_token,
468                lt_token: input.parse()?,
469                args: {
470                    let mut args = Punctuated::new();
471                    loop {
472                        if input.peek(crate::token::GtToken![>]) {
473                            break;
474                        }
475                        let value: GenericArgument = input.parse()?;
476                        args.push_value(value);
477                        if input.peek(crate::token::GtToken![>]) {
478                            break;
479                        }
480                        let punct: crate::token::CommaToken![,] = input.parse()?;
481                        args.push_punct(punct);
482                    }
483                    args
484                },
485                gt_token: input.parse()?,
486            })
487        }
488    }
489
490    #[cfg_attr(docsrs, doc(cfg(feature = "parsing")))]
491    impl Parse for AngleBracketedGenericArguments {
492        fn parse(input: ParseStream) -> Result<Self> {
493            let colon2_token: Option<crate::token::PathSepToken![::]> = input.parse()?;
494            Self::do_parse(colon2_token, input)
495        }
496    }
497
498    #[cfg_attr(docsrs, doc(cfg(feature = "parsing")))]
499    impl Parse for ParenthesizedGenericArguments {
500        fn parse(input: ParseStream) -> Result<Self> {
501            let content;
502            Ok(ParenthesizedGenericArguments {
503                paren_token: match crate::__private::parse_parens(&input) {
    crate::__private::Ok(parens) => {
        content = parens.content;
        _ = content;
        parens.token
    }
    crate::__private::Err(error) => { return crate::__private::Err(error); }
}parenthesized!(content in input),
504                inputs: content.parse_terminated(Type::parse, crate::token::CommaToken![,])?,
505                output: input.call(ReturnType::without_plus)?,
506            })
507        }
508    }
509
510    #[cfg_attr(docsrs, doc(cfg(feature = "parsing")))]
511    impl Parse for PathSegment {
512        fn parse(input: ParseStream) -> Result<Self> {
513            Self::parse_helper(input, false)
514        }
515    }
516
517    impl PathSegment {
518        fn parse_helper(input: ParseStream, expr_style: bool) -> Result<Self> {
519            if input.peek(crate::token::SuperToken![super])
520                || input.peek(crate::token::SelfValueToken![self])
521                || input.peek(crate::token::CrateToken![crate])
522                || truecfg!(feature = "full") && input.peek(crate::token::TryToken![try])
523            {
524                let ident = input.call(Ident::parse_any)?;
525                return Ok(PathSegment::from(ident));
526            }
527
528            let ident = if input.peek(crate::token::SelfTypeToken![Self]) {
529                input.call(Ident::parse_any)?
530            } else {
531                input.parse()?
532            };
533
534            if !expr_style
535                && input.peek(crate::token::LtToken![<])
536                && !input.peek(crate::token::LeToken![<=])
537                && !input.peek(crate::token::ShlEqToken![<<=])
538                || input.peek(crate::token::PathSepToken![::]) && input.peek3(crate::token::LtToken![<])
539            {
540                Ok(PathSegment {
541                    ident,
542                    arguments: PathArguments::AngleBracketed(input.parse()?),
543                })
544            } else {
545                Ok(PathSegment::from(ident))
546            }
547        }
548    }
549
550    impl Path {
551        /// Parse a `Path` containing no path arguments on any of its segments.
552        ///
553        /// # Example
554        ///
555        /// ```
556        /// use syn::{Path, Result, Token};
557        /// use syn::parse::{Parse, ParseStream};
558        ///
559        /// // A simplified single `use` statement like:
560        /// //
561        /// //     use alloc::collections::HashMap;
562        /// //
563        /// // Note that generic parameters are not allowed in a `use` statement
564        /// // so the following must not be accepted.
565        /// //
566        /// //     use a::<b>::c;
567        /// struct SingleUse {
568        ///     use_token: Token![use],
569        ///     path: Path,
570        /// }
571        ///
572        /// impl Parse for SingleUse {
573        ///     fn parse(input: ParseStream) -> Result<Self> {
574        ///         Ok(SingleUse {
575        ///             use_token: input.parse()?,
576        ///             path: input.call(Path::parse_mod_style)?,
577        ///         })
578        ///     }
579        /// }
580        /// ```
581        #[cfg_attr(docsrs, doc(cfg(feature = "parsing")))]
582        pub fn parse_mod_style(input: ParseStream) -> Result<Self> {
583            Ok(Path {
584                leading_colon: input.parse()?,
585                segments: {
586                    let mut segments = Punctuated::new();
587                    loop {
588                        if !input.peek(Ident)
589                            && !input.peek(crate::token::SuperToken![super])
590                            && !input.peek(crate::token::SelfValueToken![self])
591                            && !input.peek(crate::token::SelfTypeToken![Self])
592                            && !input.peek(crate::token::CrateToken![crate])
593                        {
594                            break;
595                        }
596                        let ident = Ident::parse_any(input)?;
597                        segments.push_value(PathSegment::from(ident));
598                        if !input.peek(crate::token::PathSepToken![::]) {
599                            break;
600                        }
601                        let punct = input.parse()?;
602                        segments.push_punct(punct);
603                    }
604                    if segments.is_empty() {
605                        return Err(input.parse::<Ident>().unwrap_err());
606                    } else if segments.trailing_punct() {
607                        return Err(input.error("expected path segment after `::`"));
608                    }
609                    segments
610                },
611            })
612        }
613
614        pub(crate) fn parse_helper(input: ParseStream, expr_style: bool) -> Result<Self> {
615            let mut path = Path {
616                leading_colon: input.parse()?,
617                segments: {
618                    let mut segments = Punctuated::new();
619                    let value = PathSegment::parse_helper(input, expr_style)?;
620                    segments.push_value(value);
621                    segments
622                },
623            };
624            Path::parse_rest(input, &mut path, expr_style)?;
625            Ok(path)
626        }
627
628        pub(crate) fn parse_rest(
629            input: ParseStream,
630            path: &mut Self,
631            expr_style: bool,
632        ) -> Result<()> {
633            while input.peek(crate::token::PathSepToken![::]) && !input.peek3(token::Paren) {
634                let punct: crate::token::PathSepToken![::] = input.parse()?;
635                path.segments.push_punct(punct);
636                let value = PathSegment::parse_helper(input, expr_style)?;
637                path.segments.push_value(value);
638            }
639            Ok(())
640        }
641
642        pub(crate) fn is_mod_style(&self) -> bool {
643            self.segments
644                .iter()
645                .all(|segment| segment.arguments.is_none())
646        }
647    }
648
649    pub(crate) fn qpath(input: ParseStream, expr_style: bool) -> Result<(Option<QSelf>, Path)> {
650        if input.peek(crate::token::LtToken![<]) {
651            let lt_token: crate::token::LtToken![<] = input.parse()?;
652            let this: Type = input.parse()?;
653            let path = if input.peek(crate::token::AsToken![as]) {
654                let as_token: crate::token::AsToken![as] = input.parse()?;
655                let path: Path = input.parse()?;
656                Some((as_token, path))
657            } else {
658                None
659            };
660            let gt_token: crate::token::GtToken![>] = input.parse()?;
661            let colon2_token: crate::token::PathSepToken![::] = input.parse()?;
662            let mut rest = Punctuated::new();
663            loop {
664                let path = PathSegment::parse_helper(input, expr_style)?;
665                rest.push_value(path);
666                if !input.peek(crate::token::PathSepToken![::]) {
667                    break;
668                }
669                let punct: crate::token::PathSepToken![::] = input.parse()?;
670                rest.push_punct(punct);
671            }
672            let (position, as_token, path) = match path {
673                Some((as_token, mut path)) => {
674                    let pos = path.segments.len();
675                    path.segments.push_punct(colon2_token);
676                    path.segments.extend(rest.into_pairs());
677                    (pos, Some(as_token), path)
678                }
679                None => {
680                    let path = Path {
681                        leading_colon: Some(colon2_token),
682                        segments: rest,
683                    };
684                    (0, None, path)
685                }
686            };
687            let qself = QSelf {
688                lt_token,
689                ty: Box::new(this),
690                position,
691                as_token,
692                gt_token,
693            };
694            Ok((Some(qself), path))
695        } else {
696            let path = Path::parse_helper(input, expr_style)?;
697            Ok((None, path))
698        }
699    }
700}
701
702#[cfg(feature = "printing")]
703pub(crate) mod printing {
704    use crate::generics;
705    use crate::path::{
706        AngleBracketedGenericArguments, AssocConst, AssocType, Constraint, GenericArgument,
707        ParenthesizedGenericArguments, Path, PathArguments, PathSegment, QSelf,
708    };
709    use crate::print::TokensOrDefault;
710    #[cfg(feature = "parsing")]
711    use crate::spanned::Spanned;
712    use core::cmp;
713    #[cfg(feature = "parsing")]
714    use proc_macro2::Span;
715    use proc_macro2::TokenStream;
716    use quote::ToTokens;
717
718    pub(crate) enum PathStyle {
719        Expr,
720        Mod,
721        AsWritten,
722    }
723
724    impl Copy for PathStyle {}
725
726    impl Clone for PathStyle {
727        fn clone(&self) -> Self {
728            *self
729        }
730    }
731
732    #[cfg_attr(docsrs, doc(cfg(feature = "printing")))]
733    impl ToTokens for Path {
734        fn to_tokens(&self, tokens: &mut TokenStream) {
735            print_path(tokens, self, PathStyle::AsWritten);
736        }
737    }
738
739    pub(crate) fn print_path(tokens: &mut TokenStream, path: &Path, style: PathStyle) {
740        path.leading_colon.to_tokens(tokens);
741        for segment in path.segments.pairs() {
742            print_path_segment(tokens, segment.value(), style);
743            segment.punct().to_tokens(tokens);
744        }
745    }
746
747    #[cfg_attr(docsrs, doc(cfg(feature = "printing")))]
748    impl ToTokens for PathSegment {
749        fn to_tokens(&self, tokens: &mut TokenStream) {
750            print_path_segment(tokens, self, PathStyle::AsWritten);
751        }
752    }
753
754    fn print_path_segment(tokens: &mut TokenStream, segment: &PathSegment, style: PathStyle) {
755        segment.ident.to_tokens(tokens);
756        print_path_arguments(tokens, &segment.arguments, style);
757    }
758
759    #[cfg_attr(docsrs, doc(cfg(feature = "printing")))]
760    impl ToTokens for PathArguments {
761        fn to_tokens(&self, tokens: &mut TokenStream) {
762            print_path_arguments(tokens, self, PathStyle::AsWritten);
763        }
764    }
765
766    fn print_path_arguments(tokens: &mut TokenStream, arguments: &PathArguments, style: PathStyle) {
767        match arguments {
768            PathArguments::None => {}
769            PathArguments::AngleBracketed(arguments) => {
770                print_angle_bracketed_generic_arguments(tokens, arguments, style);
771            }
772            PathArguments::Parenthesized(arguments) => {
773                print_parenthesized_generic_arguments(tokens, arguments, style);
774            }
775        }
776    }
777
778    #[cfg_attr(docsrs, doc(cfg(feature = "printing")))]
779    impl ToTokens for GenericArgument {
780        #[allow(clippy::match_same_arms)]
781        fn to_tokens(&self, tokens: &mut TokenStream) {
782            match self {
783                GenericArgument::Lifetime(lt) => lt.to_tokens(tokens),
784                GenericArgument::Type(ty) => ty.to_tokens(tokens),
785                GenericArgument::Const(expr) => {
786                    generics::printing::print_const_argument(expr, tokens);
787                }
788                GenericArgument::AssocType(assoc) => assoc.to_tokens(tokens),
789                GenericArgument::AssocConst(assoc) => assoc.to_tokens(tokens),
790                GenericArgument::Constraint(constraint) => constraint.to_tokens(tokens),
791            }
792        }
793    }
794
795    #[cfg_attr(docsrs, doc(cfg(feature = "printing")))]
796    impl ToTokens for AngleBracketedGenericArguments {
797        fn to_tokens(&self, tokens: &mut TokenStream) {
798            print_angle_bracketed_generic_arguments(tokens, self, PathStyle::AsWritten);
799        }
800    }
801
802    pub(crate) fn print_angle_bracketed_generic_arguments(
803        tokens: &mut TokenStream,
804        arguments: &AngleBracketedGenericArguments,
805        style: PathStyle,
806    ) {
807        if let PathStyle::Mod = style {
808            return;
809        }
810
811        conditionally_print_turbofish(tokens, &arguments.colon2_token, style);
812        arguments.lt_token.to_tokens(tokens);
813
814        // Print lifetimes before types/consts/bindings, regardless of their
815        // order in args.
816        let mut trailing_or_empty = true;
817        for param in arguments.args.pairs() {
818            match param.value() {
819                GenericArgument::Lifetime(_) => {
820                    param.to_tokens(tokens);
821                    trailing_or_empty = param.punct().is_some();
822                }
823                GenericArgument::Type(_)
824                | GenericArgument::Const(_)
825                | GenericArgument::AssocType(_)
826                | GenericArgument::AssocConst(_)
827                | GenericArgument::Constraint(_) => {}
828            }
829        }
830        for param in arguments.args.pairs() {
831            match param.value() {
832                GenericArgument::Type(_)
833                | GenericArgument::Const(_)
834                | GenericArgument::AssocType(_)
835                | GenericArgument::AssocConst(_)
836                | GenericArgument::Constraint(_) => {
837                    if !trailing_or_empty {
838                        <crate::token::CommaToken![,]>::default().to_tokens(tokens);
839                    }
840                    param.to_tokens(tokens);
841                    trailing_or_empty = param.punct().is_some();
842                }
843                GenericArgument::Lifetime(_) => {}
844            }
845        }
846
847        arguments.gt_token.to_tokens(tokens);
848    }
849
850    #[cfg_attr(docsrs, doc(cfg(feature = "printing")))]
851    impl ToTokens for AssocType {
852        fn to_tokens(&self, tokens: &mut TokenStream) {
853            self.ident.to_tokens(tokens);
854            self.generics.to_tokens(tokens);
855            self.eq_token.to_tokens(tokens);
856            self.ty.to_tokens(tokens);
857        }
858    }
859
860    #[cfg_attr(docsrs, doc(cfg(feature = "printing")))]
861    impl ToTokens for AssocConst {
862        fn to_tokens(&self, tokens: &mut TokenStream) {
863            self.ident.to_tokens(tokens);
864            self.generics.to_tokens(tokens);
865            self.eq_token.to_tokens(tokens);
866            generics::printing::print_const_argument(&self.value, tokens);
867        }
868    }
869
870    #[cfg_attr(docsrs, doc(cfg(feature = "printing")))]
871    impl ToTokens for Constraint {
872        fn to_tokens(&self, tokens: &mut TokenStream) {
873            self.ident.to_tokens(tokens);
874            self.generics.to_tokens(tokens);
875            self.colon_token.to_tokens(tokens);
876            self.bounds.to_tokens(tokens);
877        }
878    }
879
880    #[cfg_attr(docsrs, doc(cfg(feature = "printing")))]
881    impl ToTokens for ParenthesizedGenericArguments {
882        fn to_tokens(&self, tokens: &mut TokenStream) {
883            print_parenthesized_generic_arguments(tokens, self, PathStyle::AsWritten);
884        }
885    }
886
887    fn print_parenthesized_generic_arguments(
888        tokens: &mut TokenStream,
889        arguments: &ParenthesizedGenericArguments,
890        style: PathStyle,
891    ) {
892        if let PathStyle::Mod = style {
893            return;
894        }
895
896        conditionally_print_turbofish(tokens, &None, style);
897        arguments.paren_token.surround(tokens, |tokens| {
898            arguments.inputs.to_tokens(tokens);
899        });
900        arguments.output.to_tokens(tokens);
901    }
902
903    pub(crate) fn print_qpath(
904        tokens: &mut TokenStream,
905        qself: &Option<QSelf>,
906        path: &Path,
907        style: PathStyle,
908    ) {
909        let qself = match qself {
910            Some(qself) => qself,
911            None => {
912                print_path(tokens, path, style);
913                return;
914            }
915        };
916        qself.lt_token.to_tokens(tokens);
917        qself.ty.to_tokens(tokens);
918
919        let pos = cmp::min(qself.position, path.segments.len());
920        let mut segments = path.segments.pairs();
921        if pos > 0 {
922            TokensOrDefault(&qself.as_token).to_tokens(tokens);
923            path.leading_colon.to_tokens(tokens);
924            for (i, segment) in segments.by_ref().take(pos).enumerate() {
925                print_path_segment(tokens, segment.value(), PathStyle::AsWritten);
926                if i + 1 == pos {
927                    qself.gt_token.to_tokens(tokens);
928                }
929                segment.punct().to_tokens(tokens);
930            }
931        } else {
932            qself.gt_token.to_tokens(tokens);
933            path.leading_colon.to_tokens(tokens);
934        }
935        for segment in segments {
936            print_path_segment(tokens, segment.value(), style);
937            segment.punct().to_tokens(tokens);
938        }
939    }
940
941    fn conditionally_print_turbofish(
942        tokens: &mut TokenStream,
943        colon2_token: &Option<crate::token::PathSepToken![::]>,
944        style: PathStyle,
945    ) {
946        match style {
947            PathStyle::Expr => TokensOrDefault(colon2_token).to_tokens(tokens),
948            PathStyle::Mod => ::core::panicking::panic("internal error: entered unreachable code")unreachable!(),
949            PathStyle::AsWritten => colon2_token.to_tokens(tokens),
950        }
951    }
952
953    #[cfg(feature = "parsing")]
954    #[cfg_attr(docsrs, doc(cfg(all(feature = "parsing", feature = "printing"))))]
955    impl Spanned for QSelf {
956        fn span(&self) -> Span {
957            struct QSelfDelimiters<'a>(&'a QSelf);
958
959            impl<'a> ToTokens for QSelfDelimiters<'a> {
960                fn to_tokens(&self, tokens: &mut TokenStream) {
961                    self.0.lt_token.to_tokens(tokens);
962                    self.0.gt_token.to_tokens(tokens);
963                }
964            }
965
966            QSelfDelimiters(self).span()
967        }
968    }
969}