1*69942c0aSMiguel Ojeda // SPDX-License-Identifier: Apache-2.0 OR MIT 2*69942c0aSMiguel Ojeda 3808c999fSMiguel Ojeda #[cfg(feature = "parsing")] 4808c999fSMiguel Ojeda use crate::error::Error; 5808c999fSMiguel Ojeda #[cfg(feature = "parsing")] 6808c999fSMiguel Ojeda use crate::error::Result; 7808c999fSMiguel Ojeda use crate::expr::Expr; 8808c999fSMiguel Ojeda use crate::mac::MacroDelimiter; 9808c999fSMiguel Ojeda #[cfg(feature = "parsing")] 10808c999fSMiguel Ojeda use crate::meta::{self, ParseNestedMeta}; 11808c999fSMiguel Ojeda #[cfg(feature = "parsing")] 12808c999fSMiguel Ojeda use crate::parse::{Parse, ParseStream, Parser}; 13808c999fSMiguel Ojeda use crate::path::Path; 14808c999fSMiguel Ojeda use crate::token; 15808c999fSMiguel Ojeda use proc_macro2::TokenStream; 16808c999fSMiguel Ojeda #[cfg(feature = "printing")] 17808c999fSMiguel Ojeda use std::iter; 18808c999fSMiguel Ojeda #[cfg(feature = "printing")] 19808c999fSMiguel Ojeda use std::slice; 20808c999fSMiguel Ojeda 21808c999fSMiguel Ojeda ast_struct! { 22808c999fSMiguel Ojeda /// An attribute, like `#[repr(transparent)]`. 23808c999fSMiguel Ojeda /// 24808c999fSMiguel Ojeda /// <br> 25808c999fSMiguel Ojeda /// 26808c999fSMiguel Ojeda /// # Syntax 27808c999fSMiguel Ojeda /// 28808c999fSMiguel Ojeda /// Rust has six types of attributes. 29808c999fSMiguel Ojeda /// 30808c999fSMiguel Ojeda /// - Outer attributes like `#[repr(transparent)]`. These appear outside or 31808c999fSMiguel Ojeda /// in front of the item they describe. 32808c999fSMiguel Ojeda /// 33808c999fSMiguel Ojeda /// - Inner attributes like `#![feature(proc_macro)]`. These appear inside 34808c999fSMiguel Ojeda /// of the item they describe, usually a module. 35808c999fSMiguel Ojeda /// 36808c999fSMiguel Ojeda /// - Outer one-line doc comments like `/// Example`. 37808c999fSMiguel Ojeda /// 38808c999fSMiguel Ojeda /// - Inner one-line doc comments like `//! Please file an issue`. 39808c999fSMiguel Ojeda /// 40808c999fSMiguel Ojeda /// - Outer documentation blocks `/** Example */`. 41808c999fSMiguel Ojeda /// 42808c999fSMiguel Ojeda /// - Inner documentation blocks `/*! Please file an issue */`. 43808c999fSMiguel Ojeda /// 44808c999fSMiguel Ojeda /// The `style` field of type `AttrStyle` distinguishes whether an attribute 45808c999fSMiguel Ojeda /// is outer or inner. 46808c999fSMiguel Ojeda /// 47808c999fSMiguel Ojeda /// Every attribute has a `path` that indicates the intended interpretation 48808c999fSMiguel Ojeda /// of the rest of the attribute's contents. The path and the optional 49808c999fSMiguel Ojeda /// additional contents are represented together in the `meta` field of the 50808c999fSMiguel Ojeda /// attribute in three possible varieties: 51808c999fSMiguel Ojeda /// 52808c999fSMiguel Ojeda /// - Meta::Path — attributes whose information content conveys just a 53808c999fSMiguel Ojeda /// path, for example the `#[test]` attribute. 54808c999fSMiguel Ojeda /// 55808c999fSMiguel Ojeda /// - Meta::List — attributes that carry arbitrary tokens after the 56808c999fSMiguel Ojeda /// path, surrounded by a delimiter (parenthesis, bracket, or brace). For 57808c999fSMiguel Ojeda /// example `#[derive(Copy)]` or `#[precondition(x < 5)]`. 58808c999fSMiguel Ojeda /// 59808c999fSMiguel Ojeda /// - Meta::NameValue — attributes with an `=` sign after the path, 60808c999fSMiguel Ojeda /// followed by a Rust expression. For example `#[path = 61808c999fSMiguel Ojeda /// "sys/windows.rs"]`. 62808c999fSMiguel Ojeda /// 63808c999fSMiguel Ojeda /// All doc comments are represented in the NameValue style with a path of 64808c999fSMiguel Ojeda /// "doc", as this is how they are processed by the compiler and by 65808c999fSMiguel Ojeda /// `macro_rules!` macros. 66808c999fSMiguel Ojeda /// 67808c999fSMiguel Ojeda /// ```text 68808c999fSMiguel Ojeda /// #[derive(Copy, Clone)] 69808c999fSMiguel Ojeda /// ~~~~~~Path 70808c999fSMiguel Ojeda /// ^^^^^^^^^^^^^^^^^^^Meta::List 71808c999fSMiguel Ojeda /// 72808c999fSMiguel Ojeda /// #[path = "sys/windows.rs"] 73808c999fSMiguel Ojeda /// ~~~~Path 74808c999fSMiguel Ojeda /// ^^^^^^^^^^^^^^^^^^^^^^^Meta::NameValue 75808c999fSMiguel Ojeda /// 76808c999fSMiguel Ojeda /// #[test] 77808c999fSMiguel Ojeda /// ^^^^Meta::Path 78808c999fSMiguel Ojeda /// ``` 79808c999fSMiguel Ojeda /// 80808c999fSMiguel Ojeda /// <br> 81808c999fSMiguel Ojeda /// 82808c999fSMiguel Ojeda /// # Parsing from tokens to Attribute 83808c999fSMiguel Ojeda /// 84808c999fSMiguel Ojeda /// This type does not implement the [`Parse`] trait and thus cannot be 85808c999fSMiguel Ojeda /// parsed directly by [`ParseStream::parse`]. Instead use 86808c999fSMiguel Ojeda /// [`ParseStream::call`] with one of the two parser functions 87808c999fSMiguel Ojeda /// [`Attribute::parse_outer`] or [`Attribute::parse_inner`] depending on 88808c999fSMiguel Ojeda /// which you intend to parse. 89808c999fSMiguel Ojeda /// 90808c999fSMiguel Ojeda /// [`Parse`]: crate::parse::Parse 91808c999fSMiguel Ojeda /// [`ParseStream::parse`]: crate::parse::ParseBuffer::parse 92808c999fSMiguel Ojeda /// [`ParseStream::call`]: crate::parse::ParseBuffer::call 93808c999fSMiguel Ojeda /// 94808c999fSMiguel Ojeda /// ``` 95808c999fSMiguel Ojeda /// use syn::{Attribute, Ident, Result, Token}; 96808c999fSMiguel Ojeda /// use syn::parse::{Parse, ParseStream}; 97808c999fSMiguel Ojeda /// 98808c999fSMiguel Ojeda /// // Parses a unit struct with attributes. 99808c999fSMiguel Ojeda /// // 100808c999fSMiguel Ojeda /// // #[path = "s.tmpl"] 101808c999fSMiguel Ojeda /// // struct S; 102808c999fSMiguel Ojeda /// struct UnitStruct { 103808c999fSMiguel Ojeda /// attrs: Vec<Attribute>, 104808c999fSMiguel Ojeda /// struct_token: Token![struct], 105808c999fSMiguel Ojeda /// name: Ident, 106808c999fSMiguel Ojeda /// semi_token: Token![;], 107808c999fSMiguel Ojeda /// } 108808c999fSMiguel Ojeda /// 109808c999fSMiguel Ojeda /// impl Parse for UnitStruct { 110808c999fSMiguel Ojeda /// fn parse(input: ParseStream) -> Result<Self> { 111808c999fSMiguel Ojeda /// Ok(UnitStruct { 112808c999fSMiguel Ojeda /// attrs: input.call(Attribute::parse_outer)?, 113808c999fSMiguel Ojeda /// struct_token: input.parse()?, 114808c999fSMiguel Ojeda /// name: input.parse()?, 115808c999fSMiguel Ojeda /// semi_token: input.parse()?, 116808c999fSMiguel Ojeda /// }) 117808c999fSMiguel Ojeda /// } 118808c999fSMiguel Ojeda /// } 119808c999fSMiguel Ojeda /// ``` 120808c999fSMiguel Ojeda /// 121808c999fSMiguel Ojeda /// <p><br></p> 122808c999fSMiguel Ojeda /// 123808c999fSMiguel Ojeda /// # Parsing from Attribute to structured arguments 124808c999fSMiguel Ojeda /// 125808c999fSMiguel Ojeda /// The grammar of attributes in Rust is very flexible, which makes the 126808c999fSMiguel Ojeda /// syntax tree not that useful on its own. In particular, arguments of the 127808c999fSMiguel Ojeda /// `Meta::List` variety of attribute are held in an arbitrary `tokens: 128808c999fSMiguel Ojeda /// TokenStream`. Macros are expected to check the `path` of the attribute, 129808c999fSMiguel Ojeda /// decide whether they recognize it, and then parse the remaining tokens 130808c999fSMiguel Ojeda /// according to whatever grammar they wish to require for that kind of 131808c999fSMiguel Ojeda /// attribute. Use [`parse_args()`] to parse those tokens into the expected 132808c999fSMiguel Ojeda /// data structure. 133808c999fSMiguel Ojeda /// 134808c999fSMiguel Ojeda /// [`parse_args()`]: Attribute::parse_args 135808c999fSMiguel Ojeda /// 136808c999fSMiguel Ojeda /// <p><br></p> 137808c999fSMiguel Ojeda /// 138808c999fSMiguel Ojeda /// # Doc comments 139808c999fSMiguel Ojeda /// 140808c999fSMiguel Ojeda /// The compiler transforms doc comments, such as `/// comment` and `/*! 141808c999fSMiguel Ojeda /// comment */`, into attributes before macros are expanded. Each comment is 142808c999fSMiguel Ojeda /// expanded into an attribute of the form `#[doc = r"comment"]`. 143808c999fSMiguel Ojeda /// 144808c999fSMiguel Ojeda /// As an example, the following `mod` items are expanded identically: 145808c999fSMiguel Ojeda /// 146808c999fSMiguel Ojeda /// ``` 147808c999fSMiguel Ojeda /// # use syn::{ItemMod, parse_quote}; 148808c999fSMiguel Ojeda /// let doc: ItemMod = parse_quote! { 149808c999fSMiguel Ojeda /// /// Single line doc comments 150808c999fSMiguel Ojeda /// /// We write so many! 151808c999fSMiguel Ojeda /// /** 152808c999fSMiguel Ojeda /// * Multi-line comments... 153808c999fSMiguel Ojeda /// * May span many lines 154808c999fSMiguel Ojeda /// */ 155808c999fSMiguel Ojeda /// mod example { 156808c999fSMiguel Ojeda /// //! Of course, they can be inner too 157808c999fSMiguel Ojeda /// /*! And fit in a single line */ 158808c999fSMiguel Ojeda /// } 159808c999fSMiguel Ojeda /// }; 160808c999fSMiguel Ojeda /// let attr: ItemMod = parse_quote! { 161808c999fSMiguel Ojeda /// #[doc = r" Single line doc comments"] 162808c999fSMiguel Ojeda /// #[doc = r" We write so many!"] 163808c999fSMiguel Ojeda /// #[doc = r" 164808c999fSMiguel Ojeda /// * Multi-line comments... 165808c999fSMiguel Ojeda /// * May span many lines 166808c999fSMiguel Ojeda /// "] 167808c999fSMiguel Ojeda /// mod example { 168808c999fSMiguel Ojeda /// #![doc = r" Of course, they can be inner too"] 169808c999fSMiguel Ojeda /// #![doc = r" And fit in a single line "] 170808c999fSMiguel Ojeda /// } 171808c999fSMiguel Ojeda /// }; 172808c999fSMiguel Ojeda /// assert_eq!(doc, attr); 173808c999fSMiguel Ojeda /// ``` 174808c999fSMiguel Ojeda #[cfg_attr(docsrs, doc(cfg(any(feature = "full", feature = "derive"))))] 175808c999fSMiguel Ojeda pub struct Attribute { 176808c999fSMiguel Ojeda pub pound_token: Token![#], 177808c999fSMiguel Ojeda pub style: AttrStyle, 178808c999fSMiguel Ojeda pub bracket_token: token::Bracket, 179808c999fSMiguel Ojeda pub meta: Meta, 180808c999fSMiguel Ojeda } 181808c999fSMiguel Ojeda } 182808c999fSMiguel Ojeda 183808c999fSMiguel Ojeda impl Attribute { 184808c999fSMiguel Ojeda /// Returns the path that identifies the interpretation of this attribute. 185808c999fSMiguel Ojeda /// 186808c999fSMiguel Ojeda /// For example this would return the `test` in `#[test]`, the `derive` in 187808c999fSMiguel Ojeda /// `#[derive(Copy)]`, and the `path` in `#[path = "sys/windows.rs"]`. path(&self) -> &Path188808c999fSMiguel Ojeda pub fn path(&self) -> &Path { 189808c999fSMiguel Ojeda self.meta.path() 190808c999fSMiguel Ojeda } 191808c999fSMiguel Ojeda 192808c999fSMiguel Ojeda /// Parse the arguments to the attribute as a syntax tree. 193808c999fSMiguel Ojeda /// 194808c999fSMiguel Ojeda /// This is similar to pulling out the `TokenStream` from `Meta::List` and 195808c999fSMiguel Ojeda /// doing `syn::parse2::<T>(meta_list.tokens)`, except that using 196808c999fSMiguel Ojeda /// `parse_args` the error message has a more useful span when `tokens` is 197808c999fSMiguel Ojeda /// empty. 198808c999fSMiguel Ojeda /// 199808c999fSMiguel Ojeda /// The surrounding delimiters are *not* included in the input to the 200808c999fSMiguel Ojeda /// parser. 201808c999fSMiguel Ojeda /// 202808c999fSMiguel Ojeda /// ```text 203808c999fSMiguel Ojeda /// #[my_attr(value < 5)] 204808c999fSMiguel Ojeda /// ^^^^^^^^^ what gets parsed 205808c999fSMiguel Ojeda /// ``` 206808c999fSMiguel Ojeda /// 207808c999fSMiguel Ojeda /// # Example 208808c999fSMiguel Ojeda /// 209808c999fSMiguel Ojeda /// ``` 210808c999fSMiguel Ojeda /// use syn::{parse_quote, Attribute, Expr}; 211808c999fSMiguel Ojeda /// 212808c999fSMiguel Ojeda /// let attr: Attribute = parse_quote! { 213808c999fSMiguel Ojeda /// #[precondition(value < 5)] 214808c999fSMiguel Ojeda /// }; 215808c999fSMiguel Ojeda /// 216808c999fSMiguel Ojeda /// if attr.path().is_ident("precondition") { 217808c999fSMiguel Ojeda /// let precondition: Expr = attr.parse_args()?; 218808c999fSMiguel Ojeda /// // ... 219808c999fSMiguel Ojeda /// } 220808c999fSMiguel Ojeda /// # anyhow::Ok(()) 221808c999fSMiguel Ojeda /// ``` 222808c999fSMiguel Ojeda #[cfg(feature = "parsing")] 223808c999fSMiguel Ojeda #[cfg_attr(docsrs, doc(cfg(feature = "parsing")))] parse_args<T: Parse>(&self) -> Result<T>224808c999fSMiguel Ojeda pub fn parse_args<T: Parse>(&self) -> Result<T> { 225808c999fSMiguel Ojeda self.parse_args_with(T::parse) 226808c999fSMiguel Ojeda } 227808c999fSMiguel Ojeda 228808c999fSMiguel Ojeda /// Parse the arguments to the attribute using the given parser. 229808c999fSMiguel Ojeda /// 230808c999fSMiguel Ojeda /// # Example 231808c999fSMiguel Ojeda /// 232808c999fSMiguel Ojeda /// ``` 233808c999fSMiguel Ojeda /// use syn::{parse_quote, Attribute}; 234808c999fSMiguel Ojeda /// 235808c999fSMiguel Ojeda /// let attr: Attribute = parse_quote! { 236808c999fSMiguel Ojeda /// #[inception { #[brrrrrrraaaaawwwwrwrrrmrmrmmrmrmmmmm] }] 237808c999fSMiguel Ojeda /// }; 238808c999fSMiguel Ojeda /// 239808c999fSMiguel Ojeda /// let bwom = attr.parse_args_with(Attribute::parse_outer)?; 240808c999fSMiguel Ojeda /// 241808c999fSMiguel Ojeda /// // Attribute does not have a Parse impl, so we couldn't directly do: 242808c999fSMiguel Ojeda /// // let bwom: Attribute = attr.parse_args()?; 243808c999fSMiguel Ojeda /// # anyhow::Ok(()) 244808c999fSMiguel Ojeda /// ``` 245808c999fSMiguel Ojeda #[cfg(feature = "parsing")] 246808c999fSMiguel Ojeda #[cfg_attr(docsrs, doc(cfg(feature = "parsing")))] parse_args_with<F: Parser>(&self, parser: F) -> Result<F::Output>247808c999fSMiguel Ojeda pub fn parse_args_with<F: Parser>(&self, parser: F) -> Result<F::Output> { 248808c999fSMiguel Ojeda match &self.meta { 249808c999fSMiguel Ojeda Meta::Path(path) => Err(crate::error::new2( 250808c999fSMiguel Ojeda path.segments.first().unwrap().ident.span(), 251808c999fSMiguel Ojeda path.segments.last().unwrap().ident.span(), 252808c999fSMiguel Ojeda format!( 253808c999fSMiguel Ojeda "expected attribute arguments in parentheses: {}[{}(...)]", 254808c999fSMiguel Ojeda parsing::DisplayAttrStyle(&self.style), 255808c999fSMiguel Ojeda parsing::DisplayPath(path), 256808c999fSMiguel Ojeda ), 257808c999fSMiguel Ojeda )), 258808c999fSMiguel Ojeda Meta::NameValue(meta) => Err(Error::new( 259808c999fSMiguel Ojeda meta.eq_token.span, 260808c999fSMiguel Ojeda format_args!( 261808c999fSMiguel Ojeda "expected parentheses: {}[{}(...)]", 262808c999fSMiguel Ojeda parsing::DisplayAttrStyle(&self.style), 263808c999fSMiguel Ojeda parsing::DisplayPath(&meta.path), 264808c999fSMiguel Ojeda ), 265808c999fSMiguel Ojeda )), 266808c999fSMiguel Ojeda Meta::List(meta) => meta.parse_args_with(parser), 267808c999fSMiguel Ojeda } 268808c999fSMiguel Ojeda } 269808c999fSMiguel Ojeda 270808c999fSMiguel Ojeda /// Parse the arguments to the attribute, expecting it to follow the 271808c999fSMiguel Ojeda /// conventional structure used by most of Rust's built-in attributes. 272808c999fSMiguel Ojeda /// 273808c999fSMiguel Ojeda /// The [*Meta Item Attribute Syntax*][syntax] section in the Rust reference 274808c999fSMiguel Ojeda /// explains the convention in more detail. Not all attributes follow this 275808c999fSMiguel Ojeda /// convention, so [`parse_args()`][Self::parse_args] is available if you 276808c999fSMiguel Ojeda /// need to parse arbitrarily goofy attribute syntax. 277808c999fSMiguel Ojeda /// 278808c999fSMiguel Ojeda /// [syntax]: https://doc.rust-lang.org/reference/attributes.html#meta-item-attribute-syntax 279808c999fSMiguel Ojeda /// 280808c999fSMiguel Ojeda /// # Example 281808c999fSMiguel Ojeda /// 282808c999fSMiguel Ojeda /// We'll parse a struct, and then parse some of Rust's `#[repr]` attribute 283808c999fSMiguel Ojeda /// syntax. 284808c999fSMiguel Ojeda /// 285808c999fSMiguel Ojeda /// ``` 286808c999fSMiguel Ojeda /// use syn::{parenthesized, parse_quote, token, ItemStruct, LitInt}; 287808c999fSMiguel Ojeda /// 288808c999fSMiguel Ojeda /// let input: ItemStruct = parse_quote! { 289808c999fSMiguel Ojeda /// #[repr(C, align(4))] 290808c999fSMiguel Ojeda /// pub struct MyStruct(u16, u32); 291808c999fSMiguel Ojeda /// }; 292808c999fSMiguel Ojeda /// 293808c999fSMiguel Ojeda /// let mut repr_c = false; 294808c999fSMiguel Ojeda /// let mut repr_transparent = false; 295808c999fSMiguel Ojeda /// let mut repr_align = None::<usize>; 296808c999fSMiguel Ojeda /// let mut repr_packed = None::<usize>; 297808c999fSMiguel Ojeda /// for attr in &input.attrs { 298808c999fSMiguel Ojeda /// if attr.path().is_ident("repr") { 299808c999fSMiguel Ojeda /// attr.parse_nested_meta(|meta| { 300808c999fSMiguel Ojeda /// // #[repr(C)] 301808c999fSMiguel Ojeda /// if meta.path.is_ident("C") { 302808c999fSMiguel Ojeda /// repr_c = true; 303808c999fSMiguel Ojeda /// return Ok(()); 304808c999fSMiguel Ojeda /// } 305808c999fSMiguel Ojeda /// 306808c999fSMiguel Ojeda /// // #[repr(transparent)] 307808c999fSMiguel Ojeda /// if meta.path.is_ident("transparent") { 308808c999fSMiguel Ojeda /// repr_transparent = true; 309808c999fSMiguel Ojeda /// return Ok(()); 310808c999fSMiguel Ojeda /// } 311808c999fSMiguel Ojeda /// 312808c999fSMiguel Ojeda /// // #[repr(align(N))] 313808c999fSMiguel Ojeda /// if meta.path.is_ident("align") { 314808c999fSMiguel Ojeda /// let content; 315808c999fSMiguel Ojeda /// parenthesized!(content in meta.input); 316808c999fSMiguel Ojeda /// let lit: LitInt = content.parse()?; 317808c999fSMiguel Ojeda /// let n: usize = lit.base10_parse()?; 318808c999fSMiguel Ojeda /// repr_align = Some(n); 319808c999fSMiguel Ojeda /// return Ok(()); 320808c999fSMiguel Ojeda /// } 321808c999fSMiguel Ojeda /// 322808c999fSMiguel Ojeda /// // #[repr(packed)] or #[repr(packed(N))], omitted N means 1 323808c999fSMiguel Ojeda /// if meta.path.is_ident("packed") { 324808c999fSMiguel Ojeda /// if meta.input.peek(token::Paren) { 325808c999fSMiguel Ojeda /// let content; 326808c999fSMiguel Ojeda /// parenthesized!(content in meta.input); 327808c999fSMiguel Ojeda /// let lit: LitInt = content.parse()?; 328808c999fSMiguel Ojeda /// let n: usize = lit.base10_parse()?; 329808c999fSMiguel Ojeda /// repr_packed = Some(n); 330808c999fSMiguel Ojeda /// } else { 331808c999fSMiguel Ojeda /// repr_packed = Some(1); 332808c999fSMiguel Ojeda /// } 333808c999fSMiguel Ojeda /// return Ok(()); 334808c999fSMiguel Ojeda /// } 335808c999fSMiguel Ojeda /// 336808c999fSMiguel Ojeda /// Err(meta.error("unrecognized repr")) 337808c999fSMiguel Ojeda /// })?; 338808c999fSMiguel Ojeda /// } 339808c999fSMiguel Ojeda /// } 340808c999fSMiguel Ojeda /// # anyhow::Ok(()) 341808c999fSMiguel Ojeda /// ``` 342808c999fSMiguel Ojeda /// 343808c999fSMiguel Ojeda /// # Alternatives 344808c999fSMiguel Ojeda /// 345808c999fSMiguel Ojeda /// In some cases, for attributes which have nested layers of structured 346808c999fSMiguel Ojeda /// content, the following less flexible approach might be more convenient: 347808c999fSMiguel Ojeda /// 348808c999fSMiguel Ojeda /// ``` 349808c999fSMiguel Ojeda /// # use syn::{parse_quote, ItemStruct}; 350808c999fSMiguel Ojeda /// # 351808c999fSMiguel Ojeda /// # let input: ItemStruct = parse_quote! { 352808c999fSMiguel Ojeda /// # #[repr(C, align(4))] 353808c999fSMiguel Ojeda /// # pub struct MyStruct(u16, u32); 354808c999fSMiguel Ojeda /// # }; 355808c999fSMiguel Ojeda /// # 356808c999fSMiguel Ojeda /// use syn::punctuated::Punctuated; 357808c999fSMiguel Ojeda /// use syn::{parenthesized, token, Error, LitInt, Meta, Token}; 358808c999fSMiguel Ojeda /// 359808c999fSMiguel Ojeda /// let mut repr_c = false; 360808c999fSMiguel Ojeda /// let mut repr_transparent = false; 361808c999fSMiguel Ojeda /// let mut repr_align = None::<usize>; 362808c999fSMiguel Ojeda /// let mut repr_packed = None::<usize>; 363808c999fSMiguel Ojeda /// for attr in &input.attrs { 364808c999fSMiguel Ojeda /// if attr.path().is_ident("repr") { 365808c999fSMiguel Ojeda /// let nested = attr.parse_args_with(Punctuated::<Meta, Token![,]>::parse_terminated)?; 366808c999fSMiguel Ojeda /// for meta in nested { 367808c999fSMiguel Ojeda /// match meta { 368808c999fSMiguel Ojeda /// // #[repr(C)] 369808c999fSMiguel Ojeda /// Meta::Path(path) if path.is_ident("C") => { 370808c999fSMiguel Ojeda /// repr_c = true; 371808c999fSMiguel Ojeda /// } 372808c999fSMiguel Ojeda /// 373808c999fSMiguel Ojeda /// // #[repr(align(N))] 374808c999fSMiguel Ojeda /// Meta::List(meta) if meta.path.is_ident("align") => { 375808c999fSMiguel Ojeda /// let lit: LitInt = meta.parse_args()?; 376808c999fSMiguel Ojeda /// let n: usize = lit.base10_parse()?; 377808c999fSMiguel Ojeda /// repr_align = Some(n); 378808c999fSMiguel Ojeda /// } 379808c999fSMiguel Ojeda /// 380808c999fSMiguel Ojeda /// /* ... */ 381808c999fSMiguel Ojeda /// 382808c999fSMiguel Ojeda /// _ => { 383808c999fSMiguel Ojeda /// return Err(Error::new_spanned(meta, "unrecognized repr")); 384808c999fSMiguel Ojeda /// } 385808c999fSMiguel Ojeda /// } 386808c999fSMiguel Ojeda /// } 387808c999fSMiguel Ojeda /// } 388808c999fSMiguel Ojeda /// } 389808c999fSMiguel Ojeda /// # Ok(()) 390808c999fSMiguel Ojeda /// ``` 391808c999fSMiguel Ojeda #[cfg(feature = "parsing")] 392808c999fSMiguel Ojeda #[cfg_attr(docsrs, doc(cfg(feature = "parsing")))] parse_nested_meta( &self, logic: impl FnMut(ParseNestedMeta) -> Result<()>, ) -> Result<()>393808c999fSMiguel Ojeda pub fn parse_nested_meta( 394808c999fSMiguel Ojeda &self, 395808c999fSMiguel Ojeda logic: impl FnMut(ParseNestedMeta) -> Result<()>, 396808c999fSMiguel Ojeda ) -> Result<()> { 397808c999fSMiguel Ojeda self.parse_args_with(meta::parser(logic)) 398808c999fSMiguel Ojeda } 399808c999fSMiguel Ojeda 400808c999fSMiguel Ojeda /// Parses zero or more outer attributes from the stream. 401808c999fSMiguel Ojeda /// 402808c999fSMiguel Ojeda /// # Example 403808c999fSMiguel Ojeda /// 404808c999fSMiguel Ojeda /// See 405808c999fSMiguel Ojeda /// [*Parsing from tokens to Attribute*](#parsing-from-tokens-to-attribute). 406808c999fSMiguel Ojeda #[cfg(feature = "parsing")] 407808c999fSMiguel Ojeda #[cfg_attr(docsrs, doc(cfg(feature = "parsing")))] parse_outer(input: ParseStream) -> Result<Vec<Self>>408808c999fSMiguel Ojeda pub fn parse_outer(input: ParseStream) -> Result<Vec<Self>> { 409808c999fSMiguel Ojeda let mut attrs = Vec::new(); 410808c999fSMiguel Ojeda while input.peek(Token![#]) { 411808c999fSMiguel Ojeda attrs.push(input.call(parsing::single_parse_outer)?); 412808c999fSMiguel Ojeda } 413808c999fSMiguel Ojeda Ok(attrs) 414808c999fSMiguel Ojeda } 415808c999fSMiguel Ojeda 416808c999fSMiguel Ojeda /// Parses zero or more inner attributes from the stream. 417808c999fSMiguel Ojeda /// 418808c999fSMiguel Ojeda /// # Example 419808c999fSMiguel Ojeda /// 420808c999fSMiguel Ojeda /// See 421808c999fSMiguel Ojeda /// [*Parsing from tokens to Attribute*](#parsing-from-tokens-to-attribute). 422808c999fSMiguel Ojeda #[cfg(feature = "parsing")] 423808c999fSMiguel Ojeda #[cfg_attr(docsrs, doc(cfg(feature = "parsing")))] parse_inner(input: ParseStream) -> Result<Vec<Self>>424808c999fSMiguel Ojeda pub fn parse_inner(input: ParseStream) -> Result<Vec<Self>> { 425808c999fSMiguel Ojeda let mut attrs = Vec::new(); 426808c999fSMiguel Ojeda parsing::parse_inner(input, &mut attrs)?; 427808c999fSMiguel Ojeda Ok(attrs) 428808c999fSMiguel Ojeda } 429808c999fSMiguel Ojeda } 430808c999fSMiguel Ojeda 431808c999fSMiguel Ojeda ast_enum! { 432808c999fSMiguel Ojeda /// Distinguishes between attributes that decorate an item and attributes 433808c999fSMiguel Ojeda /// that are contained within an item. 434808c999fSMiguel Ojeda /// 435808c999fSMiguel Ojeda /// # Outer attributes 436808c999fSMiguel Ojeda /// 437808c999fSMiguel Ojeda /// - `#[repr(transparent)]` 438808c999fSMiguel Ojeda /// - `/// # Example` 439808c999fSMiguel Ojeda /// - `/** Please file an issue */` 440808c999fSMiguel Ojeda /// 441808c999fSMiguel Ojeda /// # Inner attributes 442808c999fSMiguel Ojeda /// 443808c999fSMiguel Ojeda /// - `#![feature(proc_macro)]` 444808c999fSMiguel Ojeda /// - `//! # Example` 445808c999fSMiguel Ojeda /// - `/*! Please file an issue */` 446808c999fSMiguel Ojeda #[cfg_attr(docsrs, doc(cfg(any(feature = "full", feature = "derive"))))] 447808c999fSMiguel Ojeda pub enum AttrStyle { 448808c999fSMiguel Ojeda Outer, 449808c999fSMiguel Ojeda Inner(Token![!]), 450808c999fSMiguel Ojeda } 451808c999fSMiguel Ojeda } 452808c999fSMiguel Ojeda 453808c999fSMiguel Ojeda ast_enum! { 454808c999fSMiguel Ojeda /// Content of a compile-time structured attribute. 455808c999fSMiguel Ojeda /// 456808c999fSMiguel Ojeda /// ## Path 457808c999fSMiguel Ojeda /// 458808c999fSMiguel Ojeda /// A meta path is like the `test` in `#[test]`. 459808c999fSMiguel Ojeda /// 460808c999fSMiguel Ojeda /// ## List 461808c999fSMiguel Ojeda /// 462808c999fSMiguel Ojeda /// A meta list is like the `derive(Copy)` in `#[derive(Copy)]`. 463808c999fSMiguel Ojeda /// 464808c999fSMiguel Ojeda /// ## NameValue 465808c999fSMiguel Ojeda /// 466808c999fSMiguel Ojeda /// A name-value meta is like the `path = "..."` in `#[path = 467808c999fSMiguel Ojeda /// "sys/windows.rs"]`. 468808c999fSMiguel Ojeda /// 469808c999fSMiguel Ojeda /// # Syntax tree enum 470808c999fSMiguel Ojeda /// 471808c999fSMiguel Ojeda /// This type is a [syntax tree enum]. 472808c999fSMiguel Ojeda /// 473808c999fSMiguel Ojeda /// [syntax tree enum]: crate::expr::Expr#syntax-tree-enums 474808c999fSMiguel Ojeda #[cfg_attr(docsrs, doc(cfg(any(feature = "full", feature = "derive"))))] 475808c999fSMiguel Ojeda pub enum Meta { 476808c999fSMiguel Ojeda Path(Path), 477808c999fSMiguel Ojeda 478808c999fSMiguel Ojeda /// A structured list within an attribute, like `derive(Copy, Clone)`. 479808c999fSMiguel Ojeda List(MetaList), 480808c999fSMiguel Ojeda 481808c999fSMiguel Ojeda /// A name-value pair within an attribute, like `feature = "nightly"`. 482808c999fSMiguel Ojeda NameValue(MetaNameValue), 483808c999fSMiguel Ojeda } 484808c999fSMiguel Ojeda } 485808c999fSMiguel Ojeda 486808c999fSMiguel Ojeda ast_struct! { 487808c999fSMiguel Ojeda /// A structured list within an attribute, like `derive(Copy, Clone)`. 488808c999fSMiguel Ojeda #[cfg_attr(docsrs, doc(cfg(any(feature = "full", feature = "derive"))))] 489808c999fSMiguel Ojeda pub struct MetaList { 490808c999fSMiguel Ojeda pub path: Path, 491808c999fSMiguel Ojeda pub delimiter: MacroDelimiter, 492808c999fSMiguel Ojeda pub tokens: TokenStream, 493808c999fSMiguel Ojeda } 494808c999fSMiguel Ojeda } 495808c999fSMiguel Ojeda 496808c999fSMiguel Ojeda ast_struct! { 497808c999fSMiguel Ojeda /// A name-value pair within an attribute, like `feature = "nightly"`. 498808c999fSMiguel Ojeda #[cfg_attr(docsrs, doc(cfg(any(feature = "full", feature = "derive"))))] 499808c999fSMiguel Ojeda pub struct MetaNameValue { 500808c999fSMiguel Ojeda pub path: Path, 501808c999fSMiguel Ojeda pub eq_token: Token![=], 502808c999fSMiguel Ojeda pub value: Expr, 503808c999fSMiguel Ojeda } 504808c999fSMiguel Ojeda } 505808c999fSMiguel Ojeda 506808c999fSMiguel Ojeda impl Meta { 507808c999fSMiguel Ojeda /// Returns the path that begins this structured meta item. 508808c999fSMiguel Ojeda /// 509808c999fSMiguel Ojeda /// For example this would return the `test` in `#[test]`, the `derive` in 510808c999fSMiguel Ojeda /// `#[derive(Copy)]`, and the `path` in `#[path = "sys/windows.rs"]`. path(&self) -> &Path511808c999fSMiguel Ojeda pub fn path(&self) -> &Path { 512808c999fSMiguel Ojeda match self { 513808c999fSMiguel Ojeda Meta::Path(path) => path, 514808c999fSMiguel Ojeda Meta::List(meta) => &meta.path, 515808c999fSMiguel Ojeda Meta::NameValue(meta) => &meta.path, 516808c999fSMiguel Ojeda } 517808c999fSMiguel Ojeda } 518808c999fSMiguel Ojeda 519808c999fSMiguel Ojeda /// Error if this is a `Meta::List` or `Meta::NameValue`. 520808c999fSMiguel Ojeda #[cfg(feature = "parsing")] 521808c999fSMiguel Ojeda #[cfg_attr(docsrs, doc(cfg(feature = "parsing")))] require_path_only(&self) -> Result<&Path>522808c999fSMiguel Ojeda pub fn require_path_only(&self) -> Result<&Path> { 523808c999fSMiguel Ojeda let error_span = match self { 524808c999fSMiguel Ojeda Meta::Path(path) => return Ok(path), 525808c999fSMiguel Ojeda Meta::List(meta) => meta.delimiter.span().open(), 526808c999fSMiguel Ojeda Meta::NameValue(meta) => meta.eq_token.span, 527808c999fSMiguel Ojeda }; 528808c999fSMiguel Ojeda Err(Error::new(error_span, "unexpected token in attribute")) 529808c999fSMiguel Ojeda } 530808c999fSMiguel Ojeda 531808c999fSMiguel Ojeda /// Error if this is a `Meta::Path` or `Meta::NameValue`. 532808c999fSMiguel Ojeda #[cfg(feature = "parsing")] 533808c999fSMiguel Ojeda #[cfg_attr(docsrs, doc(cfg(feature = "parsing")))] require_list(&self) -> Result<&MetaList>534808c999fSMiguel Ojeda pub fn require_list(&self) -> Result<&MetaList> { 535808c999fSMiguel Ojeda match self { 536808c999fSMiguel Ojeda Meta::List(meta) => Ok(meta), 537808c999fSMiguel Ojeda Meta::Path(path) => Err(crate::error::new2( 538808c999fSMiguel Ojeda path.segments.first().unwrap().ident.span(), 539808c999fSMiguel Ojeda path.segments.last().unwrap().ident.span(), 540808c999fSMiguel Ojeda format!( 541808c999fSMiguel Ojeda "expected attribute arguments in parentheses: `{}(...)`", 542808c999fSMiguel Ojeda parsing::DisplayPath(path), 543808c999fSMiguel Ojeda ), 544808c999fSMiguel Ojeda )), 545808c999fSMiguel Ojeda Meta::NameValue(meta) => Err(Error::new(meta.eq_token.span, "expected `(`")), 546808c999fSMiguel Ojeda } 547808c999fSMiguel Ojeda } 548808c999fSMiguel Ojeda 549808c999fSMiguel Ojeda /// Error if this is a `Meta::Path` or `Meta::List`. 550808c999fSMiguel Ojeda #[cfg(feature = "parsing")] 551808c999fSMiguel Ojeda #[cfg_attr(docsrs, doc(cfg(feature = "parsing")))] require_name_value(&self) -> Result<&MetaNameValue>552808c999fSMiguel Ojeda pub fn require_name_value(&self) -> Result<&MetaNameValue> { 553808c999fSMiguel Ojeda match self { 554808c999fSMiguel Ojeda Meta::NameValue(meta) => Ok(meta), 555808c999fSMiguel Ojeda Meta::Path(path) => Err(crate::error::new2( 556808c999fSMiguel Ojeda path.segments.first().unwrap().ident.span(), 557808c999fSMiguel Ojeda path.segments.last().unwrap().ident.span(), 558808c999fSMiguel Ojeda format!( 559808c999fSMiguel Ojeda "expected a value for this attribute: `{} = ...`", 560808c999fSMiguel Ojeda parsing::DisplayPath(path), 561808c999fSMiguel Ojeda ), 562808c999fSMiguel Ojeda )), 563808c999fSMiguel Ojeda Meta::List(meta) => Err(Error::new(meta.delimiter.span().open(), "expected `=`")), 564808c999fSMiguel Ojeda } 565808c999fSMiguel Ojeda } 566808c999fSMiguel Ojeda } 567808c999fSMiguel Ojeda 568808c999fSMiguel Ojeda impl MetaList { 569808c999fSMiguel Ojeda /// See [`Attribute::parse_args`]. 570808c999fSMiguel Ojeda #[cfg(feature = "parsing")] 571808c999fSMiguel Ojeda #[cfg_attr(docsrs, doc(cfg(feature = "parsing")))] parse_args<T: Parse>(&self) -> Result<T>572808c999fSMiguel Ojeda pub fn parse_args<T: Parse>(&self) -> Result<T> { 573808c999fSMiguel Ojeda self.parse_args_with(T::parse) 574808c999fSMiguel Ojeda } 575808c999fSMiguel Ojeda 576808c999fSMiguel Ojeda /// See [`Attribute::parse_args_with`]. 577808c999fSMiguel Ojeda #[cfg(feature = "parsing")] 578808c999fSMiguel Ojeda #[cfg_attr(docsrs, doc(cfg(feature = "parsing")))] parse_args_with<F: Parser>(&self, parser: F) -> Result<F::Output>579808c999fSMiguel Ojeda pub fn parse_args_with<F: Parser>(&self, parser: F) -> Result<F::Output> { 580808c999fSMiguel Ojeda let scope = self.delimiter.span().close(); 581808c999fSMiguel Ojeda crate::parse::parse_scoped(parser, scope, self.tokens.clone()) 582808c999fSMiguel Ojeda } 583808c999fSMiguel Ojeda 584808c999fSMiguel Ojeda /// See [`Attribute::parse_nested_meta`]. 585808c999fSMiguel Ojeda #[cfg(feature = "parsing")] 586808c999fSMiguel Ojeda #[cfg_attr(docsrs, doc(cfg(feature = "parsing")))] parse_nested_meta( &self, logic: impl FnMut(ParseNestedMeta) -> Result<()>, ) -> Result<()>587808c999fSMiguel Ojeda pub fn parse_nested_meta( 588808c999fSMiguel Ojeda &self, 589808c999fSMiguel Ojeda logic: impl FnMut(ParseNestedMeta) -> Result<()>, 590808c999fSMiguel Ojeda ) -> Result<()> { 591808c999fSMiguel Ojeda self.parse_args_with(meta::parser(logic)) 592808c999fSMiguel Ojeda } 593808c999fSMiguel Ojeda } 594808c999fSMiguel Ojeda 595808c999fSMiguel Ojeda #[cfg(feature = "printing")] 596808c999fSMiguel Ojeda pub(crate) trait FilterAttrs<'a> { 597808c999fSMiguel Ojeda type Ret: Iterator<Item = &'a Attribute>; 598808c999fSMiguel Ojeda outer(self) -> Self::Ret599808c999fSMiguel Ojeda fn outer(self) -> Self::Ret; 600808c999fSMiguel Ojeda #[cfg(feature = "full")] inner(self) -> Self::Ret601808c999fSMiguel Ojeda fn inner(self) -> Self::Ret; 602808c999fSMiguel Ojeda } 603808c999fSMiguel Ojeda 604808c999fSMiguel Ojeda #[cfg(feature = "printing")] 605808c999fSMiguel Ojeda impl<'a> FilterAttrs<'a> for &'a [Attribute] { 606808c999fSMiguel Ojeda type Ret = iter::Filter<slice::Iter<'a, Attribute>, fn(&&Attribute) -> bool>; 607808c999fSMiguel Ojeda outer(self) -> Self::Ret608808c999fSMiguel Ojeda fn outer(self) -> Self::Ret { 609808c999fSMiguel Ojeda fn is_outer(attr: &&Attribute) -> bool { 610808c999fSMiguel Ojeda match attr.style { 611808c999fSMiguel Ojeda AttrStyle::Outer => true, 612808c999fSMiguel Ojeda AttrStyle::Inner(_) => false, 613808c999fSMiguel Ojeda } 614808c999fSMiguel Ojeda } 615808c999fSMiguel Ojeda self.iter().filter(is_outer) 616808c999fSMiguel Ojeda } 617808c999fSMiguel Ojeda 618808c999fSMiguel Ojeda #[cfg(feature = "full")] inner(self) -> Self::Ret619808c999fSMiguel Ojeda fn inner(self) -> Self::Ret { 620808c999fSMiguel Ojeda fn is_inner(attr: &&Attribute) -> bool { 621808c999fSMiguel Ojeda match attr.style { 622808c999fSMiguel Ojeda AttrStyle::Inner(_) => true, 623808c999fSMiguel Ojeda AttrStyle::Outer => false, 624808c999fSMiguel Ojeda } 625808c999fSMiguel Ojeda } 626808c999fSMiguel Ojeda self.iter().filter(is_inner) 627808c999fSMiguel Ojeda } 628808c999fSMiguel Ojeda } 629808c999fSMiguel Ojeda 630808c999fSMiguel Ojeda impl From<Path> for Meta { from(meta: Path) -> Meta631808c999fSMiguel Ojeda fn from(meta: Path) -> Meta { 632808c999fSMiguel Ojeda Meta::Path(meta) 633808c999fSMiguel Ojeda } 634808c999fSMiguel Ojeda } 635808c999fSMiguel Ojeda 636808c999fSMiguel Ojeda impl From<MetaList> for Meta { from(meta: MetaList) -> Meta637808c999fSMiguel Ojeda fn from(meta: MetaList) -> Meta { 638808c999fSMiguel Ojeda Meta::List(meta) 639808c999fSMiguel Ojeda } 640808c999fSMiguel Ojeda } 641808c999fSMiguel Ojeda 642808c999fSMiguel Ojeda impl From<MetaNameValue> for Meta { from(meta: MetaNameValue) -> Meta643808c999fSMiguel Ojeda fn from(meta: MetaNameValue) -> Meta { 644808c999fSMiguel Ojeda Meta::NameValue(meta) 645808c999fSMiguel Ojeda } 646808c999fSMiguel Ojeda } 647808c999fSMiguel Ojeda 648808c999fSMiguel Ojeda #[cfg(feature = "parsing")] 649808c999fSMiguel Ojeda pub(crate) mod parsing { 650808c999fSMiguel Ojeda use crate::attr::{AttrStyle, Attribute, Meta, MetaList, MetaNameValue}; 651808c999fSMiguel Ojeda use crate::error::Result; 652808c999fSMiguel Ojeda use crate::expr::{Expr, ExprLit}; 653808c999fSMiguel Ojeda use crate::lit::Lit; 654808c999fSMiguel Ojeda use crate::parse::discouraged::Speculative as _; 655808c999fSMiguel Ojeda use crate::parse::{Parse, ParseStream}; 656808c999fSMiguel Ojeda use crate::path::Path; 657808c999fSMiguel Ojeda use crate::{mac, token}; 658808c999fSMiguel Ojeda use proc_macro2::Ident; 659808c999fSMiguel Ojeda use std::fmt::{self, Display}; 660808c999fSMiguel Ojeda parse_inner(input: ParseStream, attrs: &mut Vec<Attribute>) -> Result<()>661808c999fSMiguel Ojeda pub(crate) fn parse_inner(input: ParseStream, attrs: &mut Vec<Attribute>) -> Result<()> { 662808c999fSMiguel Ojeda while input.peek(Token![#]) && input.peek2(Token![!]) { 663808c999fSMiguel Ojeda attrs.push(input.call(single_parse_inner)?); 664808c999fSMiguel Ojeda } 665808c999fSMiguel Ojeda Ok(()) 666808c999fSMiguel Ojeda } 667808c999fSMiguel Ojeda single_parse_inner(input: ParseStream) -> Result<Attribute>668808c999fSMiguel Ojeda pub(crate) fn single_parse_inner(input: ParseStream) -> Result<Attribute> { 669808c999fSMiguel Ojeda let content; 670808c999fSMiguel Ojeda Ok(Attribute { 671808c999fSMiguel Ojeda pound_token: input.parse()?, 672808c999fSMiguel Ojeda style: AttrStyle::Inner(input.parse()?), 673808c999fSMiguel Ojeda bracket_token: bracketed!(content in input), 674808c999fSMiguel Ojeda meta: content.parse()?, 675808c999fSMiguel Ojeda }) 676808c999fSMiguel Ojeda } 677808c999fSMiguel Ojeda single_parse_outer(input: ParseStream) -> Result<Attribute>678808c999fSMiguel Ojeda pub(crate) fn single_parse_outer(input: ParseStream) -> Result<Attribute> { 679808c999fSMiguel Ojeda let content; 680808c999fSMiguel Ojeda Ok(Attribute { 681808c999fSMiguel Ojeda pound_token: input.parse()?, 682808c999fSMiguel Ojeda style: AttrStyle::Outer, 683808c999fSMiguel Ojeda bracket_token: bracketed!(content in input), 684808c999fSMiguel Ojeda meta: content.parse()?, 685808c999fSMiguel Ojeda }) 686808c999fSMiguel Ojeda } 687808c999fSMiguel Ojeda 688808c999fSMiguel Ojeda #[cfg_attr(docsrs, doc(cfg(feature = "parsing")))] 689808c999fSMiguel Ojeda impl Parse for Meta { parse(input: ParseStream) -> Result<Self>690808c999fSMiguel Ojeda fn parse(input: ParseStream) -> Result<Self> { 691808c999fSMiguel Ojeda let path = parse_outermost_meta_path(input)?; 692808c999fSMiguel Ojeda parse_meta_after_path(path, input) 693808c999fSMiguel Ojeda } 694808c999fSMiguel Ojeda } 695808c999fSMiguel Ojeda 696808c999fSMiguel Ojeda #[cfg_attr(docsrs, doc(cfg(feature = "parsing")))] 697808c999fSMiguel Ojeda impl Parse for MetaList { parse(input: ParseStream) -> Result<Self>698808c999fSMiguel Ojeda fn parse(input: ParseStream) -> Result<Self> { 699808c999fSMiguel Ojeda let path = parse_outermost_meta_path(input)?; 700808c999fSMiguel Ojeda parse_meta_list_after_path(path, input) 701808c999fSMiguel Ojeda } 702808c999fSMiguel Ojeda } 703808c999fSMiguel Ojeda 704808c999fSMiguel Ojeda #[cfg_attr(docsrs, doc(cfg(feature = "parsing")))] 705808c999fSMiguel Ojeda impl Parse for MetaNameValue { parse(input: ParseStream) -> Result<Self>706808c999fSMiguel Ojeda fn parse(input: ParseStream) -> Result<Self> { 707808c999fSMiguel Ojeda let path = parse_outermost_meta_path(input)?; 708808c999fSMiguel Ojeda parse_meta_name_value_after_path(path, input) 709808c999fSMiguel Ojeda } 710808c999fSMiguel Ojeda } 711808c999fSMiguel Ojeda 712808c999fSMiguel Ojeda // Unlike meta::parse_meta_path which accepts arbitrary keywords in the path, 713808c999fSMiguel Ojeda // only the `unsafe` keyword is accepted as an attribute's outermost path. parse_outermost_meta_path(input: ParseStream) -> Result<Path>714808c999fSMiguel Ojeda fn parse_outermost_meta_path(input: ParseStream) -> Result<Path> { 715808c999fSMiguel Ojeda if input.peek(Token![unsafe]) { 716808c999fSMiguel Ojeda let unsafe_token: Token![unsafe] = input.parse()?; 717808c999fSMiguel Ojeda Ok(Path::from(Ident::new("unsafe", unsafe_token.span))) 718808c999fSMiguel Ojeda } else { 719808c999fSMiguel Ojeda Path::parse_mod_style(input) 720808c999fSMiguel Ojeda } 721808c999fSMiguel Ojeda } 722808c999fSMiguel Ojeda parse_meta_after_path(path: Path, input: ParseStream) -> Result<Meta>723808c999fSMiguel Ojeda pub(crate) fn parse_meta_after_path(path: Path, input: ParseStream) -> Result<Meta> { 724808c999fSMiguel Ojeda if input.peek(token::Paren) || input.peek(token::Bracket) || input.peek(token::Brace) { 725808c999fSMiguel Ojeda parse_meta_list_after_path(path, input).map(Meta::List) 726808c999fSMiguel Ojeda } else if input.peek(Token![=]) { 727808c999fSMiguel Ojeda parse_meta_name_value_after_path(path, input).map(Meta::NameValue) 728808c999fSMiguel Ojeda } else { 729808c999fSMiguel Ojeda Ok(Meta::Path(path)) 730808c999fSMiguel Ojeda } 731808c999fSMiguel Ojeda } 732808c999fSMiguel Ojeda parse_meta_list_after_path(path: Path, input: ParseStream) -> Result<MetaList>733808c999fSMiguel Ojeda fn parse_meta_list_after_path(path: Path, input: ParseStream) -> Result<MetaList> { 734808c999fSMiguel Ojeda let (delimiter, tokens) = mac::parse_delimiter(input)?; 735808c999fSMiguel Ojeda Ok(MetaList { 736808c999fSMiguel Ojeda path, 737808c999fSMiguel Ojeda delimiter, 738808c999fSMiguel Ojeda tokens, 739808c999fSMiguel Ojeda }) 740808c999fSMiguel Ojeda } 741808c999fSMiguel Ojeda parse_meta_name_value_after_path(path: Path, input: ParseStream) -> Result<MetaNameValue>742808c999fSMiguel Ojeda fn parse_meta_name_value_after_path(path: Path, input: ParseStream) -> Result<MetaNameValue> { 743808c999fSMiguel Ojeda let eq_token: Token![=] = input.parse()?; 744808c999fSMiguel Ojeda let ahead = input.fork(); 745808c999fSMiguel Ojeda let lit: Option<Lit> = ahead.parse()?; 746808c999fSMiguel Ojeda let value = if let (Some(lit), true) = (lit, ahead.is_empty()) { 747808c999fSMiguel Ojeda input.advance_to(&ahead); 748808c999fSMiguel Ojeda Expr::Lit(ExprLit { 749808c999fSMiguel Ojeda attrs: Vec::new(), 750808c999fSMiguel Ojeda lit, 751808c999fSMiguel Ojeda }) 752808c999fSMiguel Ojeda } else if input.peek(Token![#]) && input.peek2(token::Bracket) { 753808c999fSMiguel Ojeda return Err(input.error("unexpected attribute inside of attribute")); 754808c999fSMiguel Ojeda } else { 755808c999fSMiguel Ojeda input.parse()? 756808c999fSMiguel Ojeda }; 757808c999fSMiguel Ojeda Ok(MetaNameValue { 758808c999fSMiguel Ojeda path, 759808c999fSMiguel Ojeda eq_token, 760808c999fSMiguel Ojeda value, 761808c999fSMiguel Ojeda }) 762808c999fSMiguel Ojeda } 763808c999fSMiguel Ojeda 764808c999fSMiguel Ojeda pub(super) struct DisplayAttrStyle<'a>(pub &'a AttrStyle); 765808c999fSMiguel Ojeda 766808c999fSMiguel Ojeda impl<'a> Display for DisplayAttrStyle<'a> { fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result767808c999fSMiguel Ojeda fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { 768808c999fSMiguel Ojeda formatter.write_str(match self.0 { 769808c999fSMiguel Ojeda AttrStyle::Outer => "#", 770808c999fSMiguel Ojeda AttrStyle::Inner(_) => "#!", 771808c999fSMiguel Ojeda }) 772808c999fSMiguel Ojeda } 773808c999fSMiguel Ojeda } 774808c999fSMiguel Ojeda 775808c999fSMiguel Ojeda pub(super) struct DisplayPath<'a>(pub &'a Path); 776808c999fSMiguel Ojeda 777808c999fSMiguel Ojeda impl<'a> Display for DisplayPath<'a> { fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result778808c999fSMiguel Ojeda fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { 779808c999fSMiguel Ojeda for (i, segment) in self.0.segments.iter().enumerate() { 780808c999fSMiguel Ojeda if i > 0 || self.0.leading_colon.is_some() { 781808c999fSMiguel Ojeda formatter.write_str("::")?; 782808c999fSMiguel Ojeda } 783808c999fSMiguel Ojeda write!(formatter, "{}", segment.ident)?; 784808c999fSMiguel Ojeda } 785808c999fSMiguel Ojeda Ok(()) 786808c999fSMiguel Ojeda } 787808c999fSMiguel Ojeda } 788808c999fSMiguel Ojeda } 789808c999fSMiguel Ojeda 790808c999fSMiguel Ojeda #[cfg(feature = "printing")] 791808c999fSMiguel Ojeda mod printing { 792808c999fSMiguel Ojeda use crate::attr::{AttrStyle, Attribute, Meta, MetaList, MetaNameValue}; 793808c999fSMiguel Ojeda use crate::path; 794808c999fSMiguel Ojeda use crate::path::printing::PathStyle; 795808c999fSMiguel Ojeda use proc_macro2::TokenStream; 796808c999fSMiguel Ojeda use quote::ToTokens; 797808c999fSMiguel Ojeda 798808c999fSMiguel Ojeda #[cfg_attr(docsrs, doc(cfg(feature = "printing")))] 799808c999fSMiguel Ojeda impl ToTokens for Attribute { to_tokens(&self, tokens: &mut TokenStream)800808c999fSMiguel Ojeda fn to_tokens(&self, tokens: &mut TokenStream) { 801808c999fSMiguel Ojeda self.pound_token.to_tokens(tokens); 802808c999fSMiguel Ojeda if let AttrStyle::Inner(b) = &self.style { 803808c999fSMiguel Ojeda b.to_tokens(tokens); 804808c999fSMiguel Ojeda } 805808c999fSMiguel Ojeda self.bracket_token.surround(tokens, |tokens| { 806808c999fSMiguel Ojeda self.meta.to_tokens(tokens); 807808c999fSMiguel Ojeda }); 808808c999fSMiguel Ojeda } 809808c999fSMiguel Ojeda } 810808c999fSMiguel Ojeda 811808c999fSMiguel Ojeda #[cfg_attr(docsrs, doc(cfg(feature = "printing")))] 812808c999fSMiguel Ojeda impl ToTokens for Meta { to_tokens(&self, tokens: &mut TokenStream)813808c999fSMiguel Ojeda fn to_tokens(&self, tokens: &mut TokenStream) { 814808c999fSMiguel Ojeda match self { 815808c999fSMiguel Ojeda Meta::Path(path) => path::printing::print_path(tokens, path, PathStyle::Mod), 816808c999fSMiguel Ojeda Meta::List(meta_list) => meta_list.to_tokens(tokens), 817808c999fSMiguel Ojeda Meta::NameValue(meta_name_value) => meta_name_value.to_tokens(tokens), 818808c999fSMiguel Ojeda } 819808c999fSMiguel Ojeda } 820808c999fSMiguel Ojeda } 821808c999fSMiguel Ojeda 822808c999fSMiguel Ojeda #[cfg_attr(docsrs, doc(cfg(feature = "printing")))] 823808c999fSMiguel Ojeda impl ToTokens for MetaList { to_tokens(&self, tokens: &mut TokenStream)824808c999fSMiguel Ojeda fn to_tokens(&self, tokens: &mut TokenStream) { 825808c999fSMiguel Ojeda path::printing::print_path(tokens, &self.path, PathStyle::Mod); 826808c999fSMiguel Ojeda self.delimiter.surround(tokens, self.tokens.clone()); 827808c999fSMiguel Ojeda } 828808c999fSMiguel Ojeda } 829808c999fSMiguel Ojeda 830808c999fSMiguel Ojeda #[cfg_attr(docsrs, doc(cfg(feature = "printing")))] 831808c999fSMiguel Ojeda impl ToTokens for MetaNameValue { to_tokens(&self, tokens: &mut TokenStream)832808c999fSMiguel Ojeda fn to_tokens(&self, tokens: &mut TokenStream) { 833808c999fSMiguel Ojeda path::printing::print_path(tokens, &self.path, PathStyle::Mod); 834808c999fSMiguel Ojeda self.eq_token.to_tokens(tokens); 835808c999fSMiguel Ojeda self.value.to_tokens(tokens); 836808c999fSMiguel Ojeda } 837808c999fSMiguel Ojeda } 838808c999fSMiguel Ojeda } 839