xref: /linux/rust/syn/path.rs (revision 784faa8eca8270671e0ed6d9d21f04bbb80fc5f7)
1 // SPDX-License-Identifier: Apache-2.0 OR MIT
2 
3 #[cfg(feature = "parsing")]
4 use crate::error::Result;
5 use crate::expr::Expr;
6 use crate::generics::TypeParamBound;
7 use crate::ident::Ident;
8 use crate::lifetime::Lifetime;
9 use crate::punctuated::Punctuated;
10 use crate::token;
11 use crate::ty::{ReturnType, Type};
12 
13 ast_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 
22 impl<T> From<T> for Path
23 where
24     T: Into<PathSegment>,
25 {
from(segment: T) -> Self26     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 
36 impl 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     /// ```
is_ident<I>(&self, ident: &I) -> bool where I: ?Sized, Ident: PartialEq<I>,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.
get_ident(&self) -> Option<&Ident>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")))]
require_ident(&self) -> Result<&Ident>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 
109 ast_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 
118 impl<T> From<T> for PathSegment
119 where
120     T: Into<Ident>,
121 {
from(ident: T) -> Self122     fn from(ident: T) -> Self {
123         PathSegment {
124             ident: ident.into(),
125             arguments: PathArguments::None,
126         }
127     }
128 }
129 
130 ast_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 
150 impl Default for PathArguments {
default() -> Self151     fn default() -> Self {
152         PathArguments::None
153     }
154 }
155 
156 impl PathArguments {
is_empty(&self) -> bool157     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 
is_none(&self) -> bool165     pub fn is_none(&self) -> bool {
166         match self {
167             PathArguments::None => true,
168             PathArguments::AngleBracketed(_) | PathArguments::Parenthesized(_) => false,
169         }
170     }
171 }
172 
173 ast_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 
198 ast_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 
210 ast_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 
222 ast_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 
234 ast_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 
245 ast_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 
258 ast_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")]
286 pub(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 {
parse(input: ParseStream) -> Result<Self>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 {
parse(input: ParseStream) -> Result<Self>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 
const_argument(input: ParseStream) -> Result<Expr>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"))))]
parse_turbofish(input: ParseStream) -> Result<Self>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 
do_parse( colon2_token: Option<Token![::]>, input: ParseStream, ) -> Result<Self>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 {
parse(input: ParseStream) -> Result<Self>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 {
parse(input: ParseStream) -> Result<Self>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 {
parse(input: ParseStream) -> Result<Self>511         fn parse(input: ParseStream) -> Result<Self> {
512             Self::parse_helper(input, false)
513         }
514     }
515 
516     impl PathSegment {
parse_helper(input: ParseStream, expr_style: bool) -> Result<Self>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")))]
parse_mod_style(input: ParseStream) -> Result<Self>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 
parse_helper(input: ParseStream, expr_style: bool) -> Result<Self>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 
parse_rest( input: ParseStream, path: &mut Self, expr_style: bool, ) -> Result<()>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 
is_mod_style(&self) -> bool641         pub(crate) fn is_mod_style(&self) -> bool {
642             self.segments
643                 .iter()
644                 .all(|segment| segment.arguments.is_none())
645         }
646     }
647 
qpath(input: ParseStream, expr_style: bool) -> Result<(Option<QSelf>, Path)>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")]
702 pub(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 {
clone(&self) -> Self726         fn clone(&self) -> Self {
727             *self
728         }
729     }
730 
731     #[cfg_attr(docsrs, doc(cfg(feature = "printing")))]
732     impl ToTokens for Path {
to_tokens(&self, tokens: &mut TokenStream)733         fn to_tokens(&self, tokens: &mut TokenStream) {
734             print_path(tokens, self, PathStyle::AsWritten);
735         }
736     }
737 
print_path(tokens: &mut TokenStream, path: &Path, style: PathStyle)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 {
to_tokens(&self, tokens: &mut TokenStream)748         fn to_tokens(&self, tokens: &mut TokenStream) {
749             print_path_segment(tokens, self, PathStyle::AsWritten);
750         }
751     }
752 
print_path_segment(tokens: &mut TokenStream, segment: &PathSegment, style: PathStyle)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 {
to_tokens(&self, tokens: &mut TokenStream)760         fn to_tokens(&self, tokens: &mut TokenStream) {
761             print_path_arguments(tokens, self, PathStyle::AsWritten);
762         }
763     }
764 
print_path_arguments(tokens: &mut TokenStream, arguments: &PathArguments, style: PathStyle)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)]
to_tokens(&self, tokens: &mut TokenStream)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 {
to_tokens(&self, tokens: &mut TokenStream)796         fn to_tokens(&self, tokens: &mut TokenStream) {
797             print_angle_bracketed_generic_arguments(tokens, self, PathStyle::AsWritten);
798         }
799     }
800 
print_angle_bracketed_generic_arguments( tokens: &mut TokenStream, arguments: &AngleBracketedGenericArguments, style: PathStyle, )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 {
to_tokens(&self, tokens: &mut TokenStream)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 {
to_tokens(&self, tokens: &mut TokenStream)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 {
to_tokens(&self, tokens: &mut TokenStream)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 {
to_tokens(&self, tokens: &mut TokenStream)881         fn to_tokens(&self, tokens: &mut TokenStream) {
882             print_parenthesized_generic_arguments(tokens, self, PathStyle::AsWritten);
883         }
884     }
885 
print_parenthesized_generic_arguments( tokens: &mut TokenStream, arguments: &ParenthesizedGenericArguments, style: PathStyle, )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 
print_qpath( tokens: &mut TokenStream, qself: &Option<QSelf>, path: &Path, style: PathStyle, )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 
conditionally_print_turbofish( tokens: &mut TokenStream, colon2_token: &Option<Token![::]>, style: PathStyle, )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 {
span(&self) -> Span955         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 }
969