1 // SPDX-License-Identifier: Apache-2.0 OR MIT 2 3 #[cfg(feature = "parsing")] 4 use crate::error::Error; 5 #[cfg(feature = "parsing")] 6 use crate::error::Result; 7 use crate::expr::Expr; 8 use crate::mac::MacroDelimiter; 9 #[cfg(feature = "parsing")] 10 use crate::meta::{self, ParseNestedMeta}; 11 #[cfg(feature = "parsing")] 12 use crate::parse::{Parse, ParseStream, Parser}; 13 use crate::path::Path; 14 use crate::token; 15 use proc_macro2::TokenStream; 16 #[cfg(feature = "printing")] 17 use std::iter; 18 #[cfg(feature = "printing")] 19 use std::slice; 20 21 ast_struct! { 22 /// An attribute, like `#[repr(transparent)]`. 23 /// 24 /// <br> 25 /// 26 /// # Syntax 27 /// 28 /// Rust has six types of attributes. 29 /// 30 /// - Outer attributes like `#[repr(transparent)]`. These appear outside or 31 /// in front of the item they describe. 32 /// 33 /// - Inner attributes like `#![feature(proc_macro)]`. These appear inside 34 /// of the item they describe, usually a module. 35 /// 36 /// - Outer one-line doc comments like `/// Example`. 37 /// 38 /// - Inner one-line doc comments like `//! Please file an issue`. 39 /// 40 /// - Outer documentation blocks `/** Example */`. 41 /// 42 /// - Inner documentation blocks `/*! Please file an issue */`. 43 /// 44 /// The `style` field of type `AttrStyle` distinguishes whether an attribute 45 /// is outer or inner. 46 /// 47 /// Every attribute has a `path` that indicates the intended interpretation 48 /// of the rest of the attribute's contents. The path and the optional 49 /// additional contents are represented together in the `meta` field of the 50 /// attribute in three possible varieties: 51 /// 52 /// - Meta::Path — attributes whose information content conveys just a 53 /// path, for example the `#[test]` attribute. 54 /// 55 /// - Meta::List — attributes that carry arbitrary tokens after the 56 /// path, surrounded by a delimiter (parenthesis, bracket, or brace). For 57 /// example `#[derive(Copy)]` or `#[precondition(x < 5)]`. 58 /// 59 /// - Meta::NameValue — attributes with an `=` sign after the path, 60 /// followed by a Rust expression. For example `#[path = 61 /// "sys/windows.rs"]`. 62 /// 63 /// All doc comments are represented in the NameValue style with a path of 64 /// "doc", as this is how they are processed by the compiler and by 65 /// `macro_rules!` macros. 66 /// 67 /// ```text 68 /// #[derive(Copy, Clone)] 69 /// ~~~~~~Path 70 /// ^^^^^^^^^^^^^^^^^^^Meta::List 71 /// 72 /// #[path = "sys/windows.rs"] 73 /// ~~~~Path 74 /// ^^^^^^^^^^^^^^^^^^^^^^^Meta::NameValue 75 /// 76 /// #[test] 77 /// ^^^^Meta::Path 78 /// ``` 79 /// 80 /// <br> 81 /// 82 /// # Parsing from tokens to Attribute 83 /// 84 /// This type does not implement the [`Parse`] trait and thus cannot be 85 /// parsed directly by [`ParseStream::parse`]. Instead use 86 /// [`ParseStream::call`] with one of the two parser functions 87 /// [`Attribute::parse_outer`] or [`Attribute::parse_inner`] depending on 88 /// which you intend to parse. 89 /// 90 /// [`Parse`]: crate::parse::Parse 91 /// [`ParseStream::parse`]: crate::parse::ParseBuffer::parse 92 /// [`ParseStream::call`]: crate::parse::ParseBuffer::call 93 /// 94 /// ``` 95 /// use syn::{Attribute, Ident, Result, Token}; 96 /// use syn::parse::{Parse, ParseStream}; 97 /// 98 /// // Parses a unit struct with attributes. 99 /// // 100 /// // #[path = "s.tmpl"] 101 /// // struct S; 102 /// struct UnitStruct { 103 /// attrs: Vec<Attribute>, 104 /// struct_token: Token![struct], 105 /// name: Ident, 106 /// semi_token: Token![;], 107 /// } 108 /// 109 /// impl Parse for UnitStruct { 110 /// fn parse(input: ParseStream) -> Result<Self> { 111 /// Ok(UnitStruct { 112 /// attrs: input.call(Attribute::parse_outer)?, 113 /// struct_token: input.parse()?, 114 /// name: input.parse()?, 115 /// semi_token: input.parse()?, 116 /// }) 117 /// } 118 /// } 119 /// ``` 120 /// 121 /// <p><br></p> 122 /// 123 /// # Parsing from Attribute to structured arguments 124 /// 125 /// The grammar of attributes in Rust is very flexible, which makes the 126 /// syntax tree not that useful on its own. In particular, arguments of the 127 /// `Meta::List` variety of attribute are held in an arbitrary `tokens: 128 /// TokenStream`. Macros are expected to check the `path` of the attribute, 129 /// decide whether they recognize it, and then parse the remaining tokens 130 /// according to whatever grammar they wish to require for that kind of 131 /// attribute. Use [`parse_args()`] to parse those tokens into the expected 132 /// data structure. 133 /// 134 /// [`parse_args()`]: Attribute::parse_args 135 /// 136 /// <p><br></p> 137 /// 138 /// # Doc comments 139 /// 140 /// The compiler transforms doc comments, such as `/// comment` and `/*! 141 /// comment */`, into attributes before macros are expanded. Each comment is 142 /// expanded into an attribute of the form `#[doc = r"comment"]`. 143 /// 144 /// As an example, the following `mod` items are expanded identically: 145 /// 146 /// ``` 147 /// # use syn::{ItemMod, parse_quote}; 148 /// let doc: ItemMod = parse_quote! { 149 /// /// Single line doc comments 150 /// /// We write so many! 151 /// /** 152 /// * Multi-line comments... 153 /// * May span many lines 154 /// */ 155 /// mod example { 156 /// //! Of course, they can be inner too 157 /// /*! And fit in a single line */ 158 /// } 159 /// }; 160 /// let attr: ItemMod = parse_quote! { 161 /// #[doc = r" Single line doc comments"] 162 /// #[doc = r" We write so many!"] 163 /// #[doc = r" 164 /// * Multi-line comments... 165 /// * May span many lines 166 /// "] 167 /// mod example { 168 /// #![doc = r" Of course, they can be inner too"] 169 /// #![doc = r" And fit in a single line "] 170 /// } 171 /// }; 172 /// assert_eq!(doc, attr); 173 /// ``` 174 #[cfg_attr(docsrs, doc(cfg(any(feature = "full", feature = "derive"))))] 175 pub struct Attribute { 176 pub pound_token: Token![#], 177 pub style: AttrStyle, 178 pub bracket_token: token::Bracket, 179 pub meta: Meta, 180 } 181 } 182 183 impl Attribute { 184 /// Returns the path that identifies the interpretation of this attribute. 185 /// 186 /// For example this would return the `test` in `#[test]`, the `derive` in 187 /// `#[derive(Copy)]`, and the `path` in `#[path = "sys/windows.rs"]`. 188 pub fn path(&self) -> &Path { 189 self.meta.path() 190 } 191 192 /// Parse the arguments to the attribute as a syntax tree. 193 /// 194 /// This is similar to pulling out the `TokenStream` from `Meta::List` and 195 /// doing `syn::parse2::<T>(meta_list.tokens)`, except that using 196 /// `parse_args` the error message has a more useful span when `tokens` is 197 /// empty. 198 /// 199 /// The surrounding delimiters are *not* included in the input to the 200 /// parser. 201 /// 202 /// ```text 203 /// #[my_attr(value < 5)] 204 /// ^^^^^^^^^ what gets parsed 205 /// ``` 206 /// 207 /// # Example 208 /// 209 /// ``` 210 /// use syn::{parse_quote, Attribute, Expr}; 211 /// 212 /// let attr: Attribute = parse_quote! { 213 /// #[precondition(value < 5)] 214 /// }; 215 /// 216 /// if attr.path().is_ident("precondition") { 217 /// let precondition: Expr = attr.parse_args()?; 218 /// // ... 219 /// } 220 /// # anyhow::Ok(()) 221 /// ``` 222 #[cfg(feature = "parsing")] 223 #[cfg_attr(docsrs, doc(cfg(feature = "parsing")))] 224 pub fn parse_args<T: Parse>(&self) -> Result<T> { 225 self.parse_args_with(T::parse) 226 } 227 228 /// Parse the arguments to the attribute using the given parser. 229 /// 230 /// # Example 231 /// 232 /// ``` 233 /// use syn::{parse_quote, Attribute}; 234 /// 235 /// let attr: Attribute = parse_quote! { 236 /// #[inception { #[brrrrrrraaaaawwwwrwrrrmrmrmmrmrmmmmm] }] 237 /// }; 238 /// 239 /// let bwom = attr.parse_args_with(Attribute::parse_outer)?; 240 /// 241 /// // Attribute does not have a Parse impl, so we couldn't directly do: 242 /// // let bwom: Attribute = attr.parse_args()?; 243 /// # anyhow::Ok(()) 244 /// ``` 245 #[cfg(feature = "parsing")] 246 #[cfg_attr(docsrs, doc(cfg(feature = "parsing")))] 247 pub fn parse_args_with<F: Parser>(&self, parser: F) -> Result<F::Output> { 248 match &self.meta { 249 Meta::Path(path) => Err(crate::error::new2( 250 path.segments.first().unwrap().ident.span(), 251 path.segments.last().unwrap().ident.span(), 252 format!( 253 "expected attribute arguments in parentheses: {}[{}(...)]", 254 parsing::DisplayAttrStyle(&self.style), 255 parsing::DisplayPath(path), 256 ), 257 )), 258 Meta::NameValue(meta) => Err(Error::new( 259 meta.eq_token.span, 260 format_args!( 261 "expected parentheses: {}[{}(...)]", 262 parsing::DisplayAttrStyle(&self.style), 263 parsing::DisplayPath(&meta.path), 264 ), 265 )), 266 Meta::List(meta) => meta.parse_args_with(parser), 267 } 268 } 269 270 /// Parse the arguments to the attribute, expecting it to follow the 271 /// conventional structure used by most of Rust's built-in attributes. 272 /// 273 /// The [*Meta Item Attribute Syntax*][syntax] section in the Rust reference 274 /// explains the convention in more detail. Not all attributes follow this 275 /// convention, so [`parse_args()`][Self::parse_args] is available if you 276 /// need to parse arbitrarily goofy attribute syntax. 277 /// 278 /// [syntax]: https://doc.rust-lang.org/reference/attributes.html#meta-item-attribute-syntax 279 /// 280 /// # Example 281 /// 282 /// We'll parse a struct, and then parse some of Rust's `#[repr]` attribute 283 /// syntax. 284 /// 285 /// ``` 286 /// use syn::{parenthesized, parse_quote, token, ItemStruct, LitInt}; 287 /// 288 /// let input: ItemStruct = parse_quote! { 289 /// #[repr(C, align(4))] 290 /// pub struct MyStruct(u16, u32); 291 /// }; 292 /// 293 /// let mut repr_c = false; 294 /// let mut repr_transparent = false; 295 /// let mut repr_align = None::<usize>; 296 /// let mut repr_packed = None::<usize>; 297 /// for attr in &input.attrs { 298 /// if attr.path().is_ident("repr") { 299 /// attr.parse_nested_meta(|meta| { 300 /// // #[repr(C)] 301 /// if meta.path.is_ident("C") { 302 /// repr_c = true; 303 /// return Ok(()); 304 /// } 305 /// 306 /// // #[repr(transparent)] 307 /// if meta.path.is_ident("transparent") { 308 /// repr_transparent = true; 309 /// return Ok(()); 310 /// } 311 /// 312 /// // #[repr(align(N))] 313 /// if meta.path.is_ident("align") { 314 /// let content; 315 /// parenthesized!(content in meta.input); 316 /// let lit: LitInt = content.parse()?; 317 /// let n: usize = lit.base10_parse()?; 318 /// repr_align = Some(n); 319 /// return Ok(()); 320 /// } 321 /// 322 /// // #[repr(packed)] or #[repr(packed(N))], omitted N means 1 323 /// if meta.path.is_ident("packed") { 324 /// if meta.input.peek(token::Paren) { 325 /// let content; 326 /// parenthesized!(content in meta.input); 327 /// let lit: LitInt = content.parse()?; 328 /// let n: usize = lit.base10_parse()?; 329 /// repr_packed = Some(n); 330 /// } else { 331 /// repr_packed = Some(1); 332 /// } 333 /// return Ok(()); 334 /// } 335 /// 336 /// Err(meta.error("unrecognized repr")) 337 /// })?; 338 /// } 339 /// } 340 /// # anyhow::Ok(()) 341 /// ``` 342 /// 343 /// # Alternatives 344 /// 345 /// In some cases, for attributes which have nested layers of structured 346 /// content, the following less flexible approach might be more convenient: 347 /// 348 /// ``` 349 /// # use syn::{parse_quote, ItemStruct}; 350 /// # 351 /// # let input: ItemStruct = parse_quote! { 352 /// # #[repr(C, align(4))] 353 /// # pub struct MyStruct(u16, u32); 354 /// # }; 355 /// # 356 /// use syn::punctuated::Punctuated; 357 /// use syn::{parenthesized, token, Error, LitInt, Meta, Token}; 358 /// 359 /// let mut repr_c = false; 360 /// let mut repr_transparent = false; 361 /// let mut repr_align = None::<usize>; 362 /// let mut repr_packed = None::<usize>; 363 /// for attr in &input.attrs { 364 /// if attr.path().is_ident("repr") { 365 /// let nested = attr.parse_args_with(Punctuated::<Meta, Token![,]>::parse_terminated)?; 366 /// for meta in nested { 367 /// match meta { 368 /// // #[repr(C)] 369 /// Meta::Path(path) if path.is_ident("C") => { 370 /// repr_c = true; 371 /// } 372 /// 373 /// // #[repr(align(N))] 374 /// Meta::List(meta) if meta.path.is_ident("align") => { 375 /// let lit: LitInt = meta.parse_args()?; 376 /// let n: usize = lit.base10_parse()?; 377 /// repr_align = Some(n); 378 /// } 379 /// 380 /// /* ... */ 381 /// 382 /// _ => { 383 /// return Err(Error::new_spanned(meta, "unrecognized repr")); 384 /// } 385 /// } 386 /// } 387 /// } 388 /// } 389 /// # Ok(()) 390 /// ``` 391 #[cfg(feature = "parsing")] 392 #[cfg_attr(docsrs, doc(cfg(feature = "parsing")))] 393 pub fn parse_nested_meta( 394 &self, 395 logic: impl FnMut(ParseNestedMeta) -> Result<()>, 396 ) -> Result<()> { 397 self.parse_args_with(meta::parser(logic)) 398 } 399 400 /// Parses zero or more outer attributes from the stream. 401 /// 402 /// # Example 403 /// 404 /// See 405 /// [*Parsing from tokens to Attribute*](#parsing-from-tokens-to-attribute). 406 #[cfg(feature = "parsing")] 407 #[cfg_attr(docsrs, doc(cfg(feature = "parsing")))] 408 pub fn parse_outer(input: ParseStream) -> Result<Vec<Self>> { 409 let mut attrs = Vec::new(); 410 while input.peek(Token![#]) { 411 attrs.push(input.call(parsing::single_parse_outer)?); 412 } 413 Ok(attrs) 414 } 415 416 /// Parses zero or more inner attributes from the stream. 417 /// 418 /// # Example 419 /// 420 /// See 421 /// [*Parsing from tokens to Attribute*](#parsing-from-tokens-to-attribute). 422 #[cfg(feature = "parsing")] 423 #[cfg_attr(docsrs, doc(cfg(feature = "parsing")))] 424 pub fn parse_inner(input: ParseStream) -> Result<Vec<Self>> { 425 let mut attrs = Vec::new(); 426 parsing::parse_inner(input, &mut attrs)?; 427 Ok(attrs) 428 } 429 } 430 431 ast_enum! { 432 /// Distinguishes between attributes that decorate an item and attributes 433 /// that are contained within an item. 434 /// 435 /// # Outer attributes 436 /// 437 /// - `#[repr(transparent)]` 438 /// - `/// # Example` 439 /// - `/** Please file an issue */` 440 /// 441 /// # Inner attributes 442 /// 443 /// - `#![feature(proc_macro)]` 444 /// - `//! # Example` 445 /// - `/*! Please file an issue */` 446 #[cfg_attr(docsrs, doc(cfg(any(feature = "full", feature = "derive"))))] 447 pub enum AttrStyle { 448 Outer, 449 Inner(Token![!]), 450 } 451 } 452 453 ast_enum! { 454 /// Content of a compile-time structured attribute. 455 /// 456 /// ## Path 457 /// 458 /// A meta path is like the `test` in `#[test]`. 459 /// 460 /// ## List 461 /// 462 /// A meta list is like the `derive(Copy)` in `#[derive(Copy)]`. 463 /// 464 /// ## NameValue 465 /// 466 /// A name-value meta is like the `path = "..."` in `#[path = 467 /// "sys/windows.rs"]`. 468 /// 469 /// # Syntax tree enum 470 /// 471 /// This type is a [syntax tree enum]. 472 /// 473 /// [syntax tree enum]: crate::expr::Expr#syntax-tree-enums 474 #[cfg_attr(docsrs, doc(cfg(any(feature = "full", feature = "derive"))))] 475 pub enum Meta { 476 Path(Path), 477 478 /// A structured list within an attribute, like `derive(Copy, Clone)`. 479 List(MetaList), 480 481 /// A name-value pair within an attribute, like `feature = "nightly"`. 482 NameValue(MetaNameValue), 483 } 484 } 485 486 ast_struct! { 487 /// A structured list within an attribute, like `derive(Copy, Clone)`. 488 #[cfg_attr(docsrs, doc(cfg(any(feature = "full", feature = "derive"))))] 489 pub struct MetaList { 490 pub path: Path, 491 pub delimiter: MacroDelimiter, 492 pub tokens: TokenStream, 493 } 494 } 495 496 ast_struct! { 497 /// A name-value pair within an attribute, like `feature = "nightly"`. 498 #[cfg_attr(docsrs, doc(cfg(any(feature = "full", feature = "derive"))))] 499 pub struct MetaNameValue { 500 pub path: Path, 501 pub eq_token: Token![=], 502 pub value: Expr, 503 } 504 } 505 506 impl Meta { 507 /// Returns the path that begins this structured meta item. 508 /// 509 /// For example this would return the `test` in `#[test]`, the `derive` in 510 /// `#[derive(Copy)]`, and the `path` in `#[path = "sys/windows.rs"]`. 511 pub fn path(&self) -> &Path { 512 match self { 513 Meta::Path(path) => path, 514 Meta::List(meta) => &meta.path, 515 Meta::NameValue(meta) => &meta.path, 516 } 517 } 518 519 /// Error if this is a `Meta::List` or `Meta::NameValue`. 520 #[cfg(feature = "parsing")] 521 #[cfg_attr(docsrs, doc(cfg(feature = "parsing")))] 522 pub fn require_path_only(&self) -> Result<&Path> { 523 let error_span = match self { 524 Meta::Path(path) => return Ok(path), 525 Meta::List(meta) => meta.delimiter.span().open(), 526 Meta::NameValue(meta) => meta.eq_token.span, 527 }; 528 Err(Error::new(error_span, "unexpected token in attribute")) 529 } 530 531 /// Error if this is a `Meta::Path` or `Meta::NameValue`. 532 #[cfg(feature = "parsing")] 533 #[cfg_attr(docsrs, doc(cfg(feature = "parsing")))] 534 pub fn require_list(&self) -> Result<&MetaList> { 535 match self { 536 Meta::List(meta) => Ok(meta), 537 Meta::Path(path) => Err(crate::error::new2( 538 path.segments.first().unwrap().ident.span(), 539 path.segments.last().unwrap().ident.span(), 540 format!( 541 "expected attribute arguments in parentheses: `{}(...)`", 542 parsing::DisplayPath(path), 543 ), 544 )), 545 Meta::NameValue(meta) => Err(Error::new(meta.eq_token.span, "expected `(`")), 546 } 547 } 548 549 /// Error if this is a `Meta::Path` or `Meta::List`. 550 #[cfg(feature = "parsing")] 551 #[cfg_attr(docsrs, doc(cfg(feature = "parsing")))] 552 pub fn require_name_value(&self) -> Result<&MetaNameValue> { 553 match self { 554 Meta::NameValue(meta) => Ok(meta), 555 Meta::Path(path) => Err(crate::error::new2( 556 path.segments.first().unwrap().ident.span(), 557 path.segments.last().unwrap().ident.span(), 558 format!( 559 "expected a value for this attribute: `{} = ...`", 560 parsing::DisplayPath(path), 561 ), 562 )), 563 Meta::List(meta) => Err(Error::new(meta.delimiter.span().open(), "expected `=`")), 564 } 565 } 566 } 567 568 impl MetaList { 569 /// See [`Attribute::parse_args`]. 570 #[cfg(feature = "parsing")] 571 #[cfg_attr(docsrs, doc(cfg(feature = "parsing")))] 572 pub fn parse_args<T: Parse>(&self) -> Result<T> { 573 self.parse_args_with(T::parse) 574 } 575 576 /// See [`Attribute::parse_args_with`]. 577 #[cfg(feature = "parsing")] 578 #[cfg_attr(docsrs, doc(cfg(feature = "parsing")))] 579 pub fn parse_args_with<F: Parser>(&self, parser: F) -> Result<F::Output> { 580 let scope = self.delimiter.span().close(); 581 crate::parse::parse_scoped(parser, scope, self.tokens.clone()) 582 } 583 584 /// See [`Attribute::parse_nested_meta`]. 585 #[cfg(feature = "parsing")] 586 #[cfg_attr(docsrs, doc(cfg(feature = "parsing")))] 587 pub fn parse_nested_meta( 588 &self, 589 logic: impl FnMut(ParseNestedMeta) -> Result<()>, 590 ) -> Result<()> { 591 self.parse_args_with(meta::parser(logic)) 592 } 593 } 594 595 #[cfg(feature = "printing")] 596 pub(crate) trait FilterAttrs<'a> { 597 type Ret: Iterator<Item = &'a Attribute>; 598 599 fn outer(self) -> Self::Ret; 600 #[cfg(feature = "full")] 601 fn inner(self) -> Self::Ret; 602 } 603 604 #[cfg(feature = "printing")] 605 impl<'a> FilterAttrs<'a> for &'a [Attribute] { 606 type Ret = iter::Filter<slice::Iter<'a, Attribute>, fn(&&Attribute) -> bool>; 607 608 fn outer(self) -> Self::Ret { 609 fn is_outer(attr: &&Attribute) -> bool { 610 match attr.style { 611 AttrStyle::Outer => true, 612 AttrStyle::Inner(_) => false, 613 } 614 } 615 self.iter().filter(is_outer) 616 } 617 618 #[cfg(feature = "full")] 619 fn inner(self) -> Self::Ret { 620 fn is_inner(attr: &&Attribute) -> bool { 621 match attr.style { 622 AttrStyle::Inner(_) => true, 623 AttrStyle::Outer => false, 624 } 625 } 626 self.iter().filter(is_inner) 627 } 628 } 629 630 impl From<Path> for Meta { 631 fn from(meta: Path) -> Meta { 632 Meta::Path(meta) 633 } 634 } 635 636 impl From<MetaList> for Meta { 637 fn from(meta: MetaList) -> Meta { 638 Meta::List(meta) 639 } 640 } 641 642 impl From<MetaNameValue> for Meta { 643 fn from(meta: MetaNameValue) -> Meta { 644 Meta::NameValue(meta) 645 } 646 } 647 648 #[cfg(feature = "parsing")] 649 pub(crate) mod parsing { 650 use crate::attr::{AttrStyle, Attribute, Meta, MetaList, MetaNameValue}; 651 use crate::error::Result; 652 use crate::expr::{Expr, ExprLit}; 653 use crate::lit::Lit; 654 use crate::parse::discouraged::Speculative as _; 655 use crate::parse::{Parse, ParseStream}; 656 use crate::path::Path; 657 use crate::{mac, token}; 658 use proc_macro2::Ident; 659 use std::fmt::{self, Display}; 660 661 pub(crate) fn parse_inner(input: ParseStream, attrs: &mut Vec<Attribute>) -> Result<()> { 662 while input.peek(Token![#]) && input.peek2(Token![!]) { 663 attrs.push(input.call(single_parse_inner)?); 664 } 665 Ok(()) 666 } 667 668 pub(crate) fn single_parse_inner(input: ParseStream) -> Result<Attribute> { 669 let content; 670 Ok(Attribute { 671 pound_token: input.parse()?, 672 style: AttrStyle::Inner(input.parse()?), 673 bracket_token: bracketed!(content in input), 674 meta: content.parse()?, 675 }) 676 } 677 678 pub(crate) fn single_parse_outer(input: ParseStream) -> Result<Attribute> { 679 let content; 680 Ok(Attribute { 681 pound_token: input.parse()?, 682 style: AttrStyle::Outer, 683 bracket_token: bracketed!(content in input), 684 meta: content.parse()?, 685 }) 686 } 687 688 #[cfg_attr(docsrs, doc(cfg(feature = "parsing")))] 689 impl Parse for Meta { 690 fn parse(input: ParseStream) -> Result<Self> { 691 let path = parse_outermost_meta_path(input)?; 692 parse_meta_after_path(path, input) 693 } 694 } 695 696 #[cfg_attr(docsrs, doc(cfg(feature = "parsing")))] 697 impl Parse for MetaList { 698 fn parse(input: ParseStream) -> Result<Self> { 699 let path = parse_outermost_meta_path(input)?; 700 parse_meta_list_after_path(path, input) 701 } 702 } 703 704 #[cfg_attr(docsrs, doc(cfg(feature = "parsing")))] 705 impl Parse for MetaNameValue { 706 fn parse(input: ParseStream) -> Result<Self> { 707 let path = parse_outermost_meta_path(input)?; 708 parse_meta_name_value_after_path(path, input) 709 } 710 } 711 712 // Unlike meta::parse_meta_path which accepts arbitrary keywords in the path, 713 // only the `unsafe` keyword is accepted as an attribute's outermost path. 714 fn parse_outermost_meta_path(input: ParseStream) -> Result<Path> { 715 if input.peek(Token![unsafe]) { 716 let unsafe_token: Token![unsafe] = input.parse()?; 717 Ok(Path::from(Ident::new("unsafe", unsafe_token.span))) 718 } else { 719 Path::parse_mod_style(input) 720 } 721 } 722 723 pub(crate) fn parse_meta_after_path(path: Path, input: ParseStream) -> Result<Meta> { 724 if input.peek(token::Paren) || input.peek(token::Bracket) || input.peek(token::Brace) { 725 parse_meta_list_after_path(path, input).map(Meta::List) 726 } else if input.peek(Token![=]) { 727 parse_meta_name_value_after_path(path, input).map(Meta::NameValue) 728 } else { 729 Ok(Meta::Path(path)) 730 } 731 } 732 733 fn parse_meta_list_after_path(path: Path, input: ParseStream) -> Result<MetaList> { 734 let (delimiter, tokens) = mac::parse_delimiter(input)?; 735 Ok(MetaList { 736 path, 737 delimiter, 738 tokens, 739 }) 740 } 741 742 fn parse_meta_name_value_after_path(path: Path, input: ParseStream) -> Result<MetaNameValue> { 743 let eq_token: Token![=] = input.parse()?; 744 let ahead = input.fork(); 745 let lit: Option<Lit> = ahead.parse()?; 746 let value = if let (Some(lit), true) = (lit, ahead.is_empty()) { 747 input.advance_to(&ahead); 748 Expr::Lit(ExprLit { 749 attrs: Vec::new(), 750 lit, 751 }) 752 } else if input.peek(Token![#]) && input.peek2(token::Bracket) { 753 return Err(input.error("unexpected attribute inside of attribute")); 754 } else { 755 input.parse()? 756 }; 757 Ok(MetaNameValue { 758 path, 759 eq_token, 760 value, 761 }) 762 } 763 764 pub(super) struct DisplayAttrStyle<'a>(pub &'a AttrStyle); 765 766 impl<'a> Display for DisplayAttrStyle<'a> { 767 fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { 768 formatter.write_str(match self.0 { 769 AttrStyle::Outer => "#", 770 AttrStyle::Inner(_) => "#!", 771 }) 772 } 773 } 774 775 pub(super) struct DisplayPath<'a>(pub &'a Path); 776 777 impl<'a> Display for DisplayPath<'a> { 778 fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { 779 for (i, segment) in self.0.segments.iter().enumerate() { 780 if i > 0 || self.0.leading_colon.is_some() { 781 formatter.write_str("::")?; 782 } 783 write!(formatter, "{}", segment.ident)?; 784 } 785 Ok(()) 786 } 787 } 788 } 789 790 #[cfg(feature = "printing")] 791 mod printing { 792 use crate::attr::{AttrStyle, Attribute, Meta, MetaList, MetaNameValue}; 793 use crate::path; 794 use crate::path::printing::PathStyle; 795 use proc_macro2::TokenStream; 796 use quote::ToTokens; 797 798 #[cfg_attr(docsrs, doc(cfg(feature = "printing")))] 799 impl ToTokens for Attribute { 800 fn to_tokens(&self, tokens: &mut TokenStream) { 801 self.pound_token.to_tokens(tokens); 802 if let AttrStyle::Inner(b) = &self.style { 803 b.to_tokens(tokens); 804 } 805 self.bracket_token.surround(tokens, |tokens| { 806 self.meta.to_tokens(tokens); 807 }); 808 } 809 } 810 811 #[cfg_attr(docsrs, doc(cfg(feature = "printing")))] 812 impl ToTokens for Meta { 813 fn to_tokens(&self, tokens: &mut TokenStream) { 814 match self { 815 Meta::Path(path) => path::printing::print_path(tokens, path, PathStyle::Mod), 816 Meta::List(meta_list) => meta_list.to_tokens(tokens), 817 Meta::NameValue(meta_name_value) => meta_name_value.to_tokens(tokens), 818 } 819 } 820 } 821 822 #[cfg_attr(docsrs, doc(cfg(feature = "printing")))] 823 impl ToTokens for MetaList { 824 fn to_tokens(&self, tokens: &mut TokenStream) { 825 path::printing::print_path(tokens, &self.path, PathStyle::Mod); 826 self.delimiter.surround(tokens, self.tokens.clone()); 827 } 828 } 829 830 #[cfg_attr(docsrs, doc(cfg(feature = "printing")))] 831 impl ToTokens for MetaNameValue { 832 fn to_tokens(&self, tokens: &mut TokenStream) { 833 path::printing::print_path(tokens, &self.path, PathStyle::Mod); 834 self.eq_token.to_tokens(tokens); 835 self.value.to_tokens(tokens); 836 } 837 } 838 } 839