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