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