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