syn/
attr.rs

1use super::*;
2use crate::punctuated::Punctuated;
3
4use std::iter;
5
6use proc_macro2::TokenStream;
7
8#[cfg(feature = "parsing")]
9use crate::parse::{Parse, ParseBuffer, ParseStream, Parser, Result};
10#[cfg(feature = "parsing")]
11use crate::punctuated::Pair;
12#[cfg(feature = "extra-traits")]
13use crate::tt::TokenStreamHelper;
14#[cfg(feature = "extra-traits")]
15use std::hash::{Hash, Hasher};
16
17ast_struct! {
18    /// An attribute like `#[repr(transparent)]`.
19    ///
20    /// *This type is available if Syn is built with the `"derive"` or `"full"`
21    /// feature.*
22    ///
23    /// <br>
24    ///
25    /// # Syntax
26    ///
27    /// Rust has six types of attributes.
28    ///
29    /// - Outer attributes like `#[repr(transparent)]`. These appear outside or
30    ///   in front of the item they describe.
31    /// - Inner attributes like `#![feature(proc_macro)]`. These appear inside
32    ///   of the item they describe, usually a module.
33    /// - Outer doc comments like `/// # Example`.
34    /// - Inner doc comments like `//! Please file an issue`.
35    /// - Outer block comments `/** # Example */`.
36    /// - Inner block comments `/*! Please file an issue */`.
37    ///
38    /// The `style` field of type `AttrStyle` distinguishes whether an attribute
39    /// is outer or inner. Doc comments and block comments are promoted to
40    /// attributes, as this is how they are processed by the compiler and by
41    /// `macro_rules!` macros.
42    ///
43    /// The `path` field gives the possibly colon-delimited path against which
44    /// the attribute is resolved. It is equal to `"doc"` for desugared doc
45    /// comments. The `tokens` field contains the rest of the attribute body as
46    /// tokens.
47    ///
48    /// ```text
49    /// #[derive(Copy)]      #[crate::precondition x < 5]
50    ///   ^^^^^^~~~~~~         ^^^^^^^^^^^^^^^^^^^ ~~~~~
51    ///   path  tokens                 path        tokens
52    /// ```
53    ///
54    /// <br>
55    ///
56    /// # Parsing from tokens to Attribute
57    ///
58    /// This type does not implement the [`Parse`] trait and thus cannot be
59    /// parsed directly by [`ParseStream::parse`]. Instead use
60    /// [`ParseStream::call`] with one of the two parser functions
61    /// [`Attribute::parse_outer`] or [`Attribute::parse_inner`] depending on
62    /// which you intend to parse.
63    ///
64    /// [`Parse`]: parse::Parse
65    /// [`ParseStream::parse`]: parse::ParseBuffer::parse
66    /// [`ParseStream::call`]: parse::ParseBuffer::call
67    ///
68    /// ```
69    /// use syn::{Attribute, Ident, Result, Token};
70    /// use syn::parse::{Parse, ParseStream};
71    ///
72    /// // Parses a unit struct with attributes.
73    /// //
74    /// //     #[path = "s.tmpl"]
75    /// //     struct S;
76    /// struct UnitStruct {
77    ///     attrs: Vec<Attribute>,
78    ///     struct_token: Token![struct],
79    ///     name: Ident,
80    ///     semi_token: Token![;],
81    /// }
82    ///
83    /// impl Parse for UnitStruct {
84    ///     fn parse(input: ParseStream) -> Result<Self> {
85    ///         Ok(UnitStruct {
86    ///             attrs: input.call(Attribute::parse_outer)?,
87    ///             struct_token: input.parse()?,
88    ///             name: input.parse()?,
89    ///             semi_token: input.parse()?,
90    ///         })
91    ///     }
92    /// }
93    /// ```
94    ///
95    /// <p><br></p>
96    ///
97    /// # Parsing from Attribute to structured arguments
98    ///
99    /// The grammar of attributes in Rust is very flexible, which makes the
100    /// syntax tree not that useful on its own. In particular, arguments of the
101    /// attribute are held in an arbitrary `tokens: TokenStream`. Macros are
102    /// expected to check the `path` of the attribute, decide whether they
103    /// recognize it, and then parse the remaining tokens according to whatever
104    /// grammar they wish to require for that kind of attribute.
105    ///
106    /// If the attribute you are parsing is expected to conform to the
107    /// conventional structured form of attribute, use [`parse_meta()`] to
108    /// obtain that structured representation. If the attribute follows some
109    /// other grammar of its own, use [`parse_args()`] to parse that into the
110    /// expected data structure.
111    ///
112    /// [`parse_meta()`]: Attribute::parse_meta
113    /// [`parse_args()`]: Attribute::parse_args
114    ///
115    /// <p><br></p>
116    ///
117    /// # Doc comments
118    ///
119    /// The compiler transforms doc comments, such as `/// comment` and `/*!
120    /// comment */`, into attributes before macros are expanded. Each comment is
121    /// expanded into an attribute of the form `#[doc = r"comment"]`.
122    ///
123    /// As an example, the following `mod` items are expanded identically:
124    ///
125    /// ```
126    /// # use syn::{ItemMod, parse_quote};
127    /// let doc: ItemMod = parse_quote! {
128    ///     /// Single line doc comments
129    ///     /// We write so many!
130    ///     /**
131    ///      * Multi-line comments...
132    ///      * May span many lines
133    ///      */
134    ///     mod example {
135    ///         //! Of course, they can be inner too
136    ///         /*! And fit in a single line */
137    ///     }
138    /// };
139    /// let attr: ItemMod = parse_quote! {
140    ///     #[doc = r" Single line doc comments"]
141    ///     #[doc = r" We write so many!"]
142    ///     #[doc = r" Multi-line comments...
143    ///  May span many lines"]
144    ///     mod example {
145    ///         #![doc = r" Of course, they can be inner too"]
146    ///         #![doc = r" And fit in a single line "]
147    ///     }
148    /// };
149    /// assert_eq!(doc, attr);
150    /// ```
151    pub struct Attribute #manual_extra_traits {
152        pub pound_token: Token![#],
153        pub style: AttrStyle,
154        pub bracket_token: token::Bracket,
155        pub path: Path,
156        pub tokens: TokenStream,
157    }
158}
159
160#[cfg(feature = "extra-traits")]
161impl Eq for Attribute {}
162
163#[cfg(feature = "extra-traits")]
164impl PartialEq for Attribute {
165    fn eq(&self, other: &Self) -> bool {
166        self.style == other.style
167            && self.pound_token == other.pound_token
168            && self.bracket_token == other.bracket_token
169            && self.path == other.path
170            && TokenStreamHelper(&self.tokens) == TokenStreamHelper(&other.tokens)
171    }
172}
173
174#[cfg(feature = "extra-traits")]
175impl Hash for Attribute {
176    fn hash<H>(&self, state: &mut H)
177    where
178        H: Hasher,
179    {
180        self.style.hash(state);
181        self.pound_token.hash(state);
182        self.bracket_token.hash(state);
183        self.path.hash(state);
184        TokenStreamHelper(&self.tokens).hash(state);
185    }
186}
187
188impl Attribute {
189    /// Parses the content of the attribute, consisting of the path and tokens,
190    /// as a [`Meta`] if possible.
191    ///
192    /// *This function is available if Syn is built with the `"parsing"`
193    /// feature.*
194    #[cfg(feature = "parsing")]
195    pub fn parse_meta(&self) -> Result<Meta> {
196        fn clone_ident_segment(segment: &PathSegment) -> PathSegment {
197            PathSegment {
198                ident: segment.ident.clone(),
199                arguments: PathArguments::None,
200            }
201        }
202
203        let path = Path {
204            leading_colon: self
205                .path
206                .leading_colon
207                .as_ref()
208                .map(|colon| Token![::](colon.spans)),
209            segments: self
210                .path
211                .segments
212                .pairs()
213                .map(|pair| match pair {
214                    Pair::Punctuated(seg, punct) => {
215                        Pair::Punctuated(clone_ident_segment(seg), Token![::](punct.spans))
216                    }
217                    Pair::End(seg) => Pair::End(clone_ident_segment(seg)),
218                })
219                .collect(),
220        };
221
222        let parser = |input: ParseStream| parsing::parse_meta_after_path(path, input);
223        parse::Parser::parse2(parser, self.tokens.clone())
224    }
225
226    /// Parse the arguments to the attribute as a syntax tree.
227    ///
228    /// This is similar to `syn::parse2::<T>(attr.tokens)` except that:
229    ///
230    /// - the surrounding delimiters are *not* included in the input to the
231    ///   parser; and
232    /// - the error message has a more useful span when `tokens` is empty.
233    ///
234    /// ```text
235    /// #[my_attr(value < 5)]
236    ///           ^^^^^^^^^ what gets parsed
237    /// ```
238    ///
239    /// *This function is available if Syn is built with the `"parsing"`
240    /// feature.*
241    #[cfg(feature = "parsing")]
242    pub fn parse_args<T: Parse>(&self) -> Result<T> {
243        self.parse_args_with(T::parse)
244    }
245
246    /// Parse the arguments to the attribute using the given parser.
247    ///
248    /// *This function is available if Syn is built with the `"parsing"`
249    /// feature.*
250    #[cfg(feature = "parsing")]
251    pub fn parse_args_with<F: Parser>(&self, parser: F) -> Result<F::Output> {
252        let parser = |input: ParseStream| {
253            let args = enter_args(self, input)?;
254            parse::parse_stream(parser, &args)
255        };
256        parser.parse2(self.tokens.clone())
257    }
258
259    /// Parses zero or more outer attributes from the stream.
260    ///
261    /// *This function is available if Syn is built with the `"parsing"`
262    /// feature.*
263    #[cfg(feature = "parsing")]
264    pub fn parse_outer(input: ParseStream) -> Result<Vec<Self>> {
265        let mut attrs = Vec::new();
266        while input.peek(Token![#]) {
267            attrs.push(input.call(parsing::single_parse_outer)?);
268        }
269        Ok(attrs)
270    }
271
272    /// Parses zero or more inner attributes from the stream.
273    ///
274    /// *This function is available if Syn is built with the `"parsing"`
275    /// feature.*
276    #[cfg(feature = "parsing")]
277    pub fn parse_inner(input: ParseStream) -> Result<Vec<Self>> {
278        let mut attrs = Vec::new();
279        while input.peek(Token![#]) && input.peek2(Token![!]) {
280            attrs.push(input.call(parsing::single_parse_inner)?);
281        }
282        Ok(attrs)
283    }
284}
285
286#[cfg(feature = "parsing")]
287fn error_expected_args(attr: &Attribute) -> Error {
288    let style = match attr.style {
289        AttrStyle::Outer => "#",
290        AttrStyle::Inner(_) => "#!",
291    };
292
293    let mut path = String::new();
294    for segment in &attr.path.segments {
295        if !path.is_empty() || attr.path.leading_colon.is_some() {
296            path += "::";
297        }
298        path += &segment.ident.to_string();
299    }
300
301    let msg = format!("expected attribute arguments: {}[{}(...)]", style, path);
302
303    #[cfg(feature = "printing")]
304    return Error::new_spanned(attr, msg);
305
306    #[cfg(not(feature = "printing"))]
307    return Error::new(attr.bracket_token.span, msg);
308}
309
310#[cfg(feature = "parsing")]
311fn enter_args<'a>(attr: &Attribute, input: ParseStream<'a>) -> Result<ParseBuffer<'a>> {
312    if input.is_empty() {
313        return Err(error_expected_args(attr));
314    };
315
316    let content;
317    if input.peek(token::Paren) {
318        parenthesized!(content in input);
319    } else if input.peek(token::Bracket) {
320        bracketed!(content in input);
321    } else if input.peek(token::Brace) {
322        braced!(content in input);
323    } else {
324        return Err(input.error("unexpected token in attribute arguments"));
325    }
326
327    if input.is_empty() {
328        Ok(content)
329    } else {
330        Err(input.error("unexpected token in attribute arguments"))
331    }
332}
333
334ast_enum! {
335    /// Distinguishes between attributes that decorate an item and attributes
336    /// that are contained within an item.
337    ///
338    /// *This type is available if Syn is built with the `"derive"` or `"full"`
339    /// feature.*
340    ///
341    /// # Outer attributes
342    ///
343    /// - `#[repr(transparent)]`
344    /// - `/// # Example`
345    /// - `/** Please file an issue */`
346    ///
347    /// # Inner attributes
348    ///
349    /// - `#![feature(proc_macro)]`
350    /// - `//! # Example`
351    /// - `/*! Please file an issue */`
352    #[cfg_attr(feature = "clone-impls", derive(Copy))]
353    pub enum AttrStyle {
354        Outer,
355        Inner(Token![!]),
356    }
357}
358
359ast_enum_of_structs! {
360    /// Content of a compile-time structured attribute.
361    ///
362    /// *This type is available if Syn is built with the `"derive"` or `"full"`
363    /// feature.*
364    ///
365    /// ## Path
366    ///
367    /// A meta path is like the `test` in `#[test]`.
368    ///
369    /// ## List
370    ///
371    /// A meta list is like the `derive(Copy)` in `#[derive(Copy)]`.
372    ///
373    /// ## NameValue
374    ///
375    /// A name-value meta is like the `path = "..."` in `#[path =
376    /// "sys/windows.rs"]`.
377    ///
378    /// # Syntax tree enum
379    ///
380    /// This type is a [syntax tree enum].
381    ///
382    /// [syntax tree enum]: enum.Expr.html#syntax-tree-enums
383    //
384    // TODO: change syntax-tree-enum link to an intra rustdoc link, currently
385    // blocked on https://github.com/rust-lang/rust/issues/62833
386    pub enum Meta {
387        Path(Path),
388
389        /// A structured list within an attribute, like `derive(Copy, Clone)`.
390        List(MetaList),
391
392        /// A name-value pair within an attribute, like `feature = "nightly"`.
393        NameValue(MetaNameValue),
394    }
395}
396
397ast_struct! {
398    /// A structured list within an attribute, like `derive(Copy, Clone)`.
399    ///
400    /// *This type is available if Syn is built with the `"derive"` or
401    /// `"full"` feature.*
402    pub struct MetaList {
403        pub path: Path,
404        pub paren_token: token::Paren,
405        pub nested: Punctuated<NestedMeta, Token![,]>,
406    }
407}
408
409ast_struct! {
410    /// A name-value pair within an attribute, like `feature = "nightly"`.
411    ///
412    /// *This type is available if Syn is built with the `"derive"` or
413    /// `"full"` feature.*
414    pub struct MetaNameValue {
415        pub path: Path,
416        pub eq_token: Token![=],
417        pub lit: Lit,
418    }
419}
420
421impl Meta {
422    /// Returns the identifier that begins this structured meta item.
423    ///
424    /// For example this would return the `test` in `#[test]`, the `derive` in
425    /// `#[derive(Copy)]`, and the `path` in `#[path = "sys/windows.rs"]`.
426    pub fn path(&self) -> &Path {
427        match self {
428            Meta::Path(path) => path,
429            Meta::List(meta) => &meta.path,
430            Meta::NameValue(meta) => &meta.path,
431        }
432    }
433}
434
435ast_enum_of_structs! {
436    /// Element of a compile-time attribute list.
437    ///
438    /// *This type is available if Syn is built with the `"derive"` or `"full"`
439    /// feature.*
440    pub enum NestedMeta {
441        /// A structured meta item, like the `Copy` in `#[derive(Copy)]` which
442        /// would be a nested `Meta::Path`.
443        Meta(Meta),
444
445        /// A Rust literal, like the `"new_name"` in `#[rename("new_name")]`.
446        Lit(Lit),
447    }
448}
449
450/// Conventional argument type associated with an invocation of an attribute
451/// macro.
452///
453/// For example if we are developing an attribute macro that is intended to be
454/// invoked on function items as follows:
455///
456/// ```
457/// # const IGNORE: &str = stringify! {
458/// #[my_attribute(path = "/v1/refresh")]
459/// # };
460/// pub fn refresh() {
461///     /* ... */
462/// }
463/// ```
464///
465/// The implementation of this macro would want to parse its attribute arguments
466/// as type `AttributeArgs`.
467///
468/// ```
469/// extern crate proc_macro;
470///
471/// use proc_macro::TokenStream;
472/// use syn::{parse_macro_input, AttributeArgs, ItemFn};
473///
474/// # const IGNORE: &str = stringify! {
475/// #[proc_macro_attribute]
476/// # };
477/// pub fn my_attribute(args: TokenStream, input: TokenStream) -> TokenStream {
478///     let args = parse_macro_input!(args as AttributeArgs);
479///     let input = parse_macro_input!(input as ItemFn);
480///
481///     /* ... */
482/// #   "".parse().unwrap()
483/// }
484/// ```
485pub type AttributeArgs = Vec<NestedMeta>;
486
487pub trait FilterAttrs<'a> {
488    type Ret: Iterator<Item = &'a Attribute>;
489
490    fn outer(self) -> Self::Ret;
491    fn inner(self) -> Self::Ret;
492}
493
494impl<'a, T> FilterAttrs<'a> for T
495where
496    T: IntoIterator<Item = &'a Attribute>,
497{
498    type Ret = iter::Filter<T::IntoIter, fn(&&Attribute) -> bool>;
499
500    fn outer(self) -> Self::Ret {
501        fn is_outer(attr: &&Attribute) -> bool {
502            match attr.style {
503                AttrStyle::Outer => true,
504                _ => false,
505            }
506        }
507        self.into_iter().filter(is_outer)
508    }
509
510    fn inner(self) -> Self::Ret {
511        fn is_inner(attr: &&Attribute) -> bool {
512            match attr.style {
513                AttrStyle::Inner(_) => true,
514                _ => false,
515            }
516        }
517        self.into_iter().filter(is_inner)
518    }
519}
520
521#[cfg(feature = "parsing")]
522pub mod parsing {
523    use super::*;
524
525    use crate::ext::IdentExt;
526    use crate::parse::{Parse, ParseStream, Result};
527    #[cfg(feature = "full")]
528    use crate::private;
529
530    pub fn single_parse_inner(input: ParseStream) -> Result<Attribute> {
531        let content;
532        Ok(Attribute {
533            pound_token: input.parse()?,
534            style: AttrStyle::Inner(input.parse()?),
535            bracket_token: bracketed!(content in input),
536            path: content.call(Path::parse_mod_style)?,
537            tokens: content.parse()?,
538        })
539    }
540
541    pub fn single_parse_outer(input: ParseStream) -> Result<Attribute> {
542        let content;
543        Ok(Attribute {
544            pound_token: input.parse()?,
545            style: AttrStyle::Outer,
546            bracket_token: bracketed!(content in input),
547            path: content.call(Path::parse_mod_style)?,
548            tokens: content.parse()?,
549        })
550    }
551
552    #[cfg(feature = "full")]
553    impl private {
554        pub fn attrs(outer: Vec<Attribute>, inner: Vec<Attribute>) -> Vec<Attribute> {
555            let mut attrs = outer;
556            attrs.extend(inner);
557            attrs
558        }
559    }
560
561    // Like Path::parse_mod_style but accepts keywords in the path.
562    fn parse_meta_path(input: ParseStream) -> Result<Path> {
563        Ok(Path {
564            leading_colon: input.parse()?,
565            segments: {
566                let mut segments = Punctuated::new();
567                while input.peek(Ident::peek_any) {
568                    let ident = Ident::parse_any(input)?;
569                    segments.push_value(PathSegment::from(ident));
570                    if !input.peek(Token![::]) {
571                        break;
572                    }
573                    let punct = input.parse()?;
574                    segments.push_punct(punct);
575                }
576                if segments.is_empty() {
577                    return Err(input.error("expected path"));
578                } else if segments.trailing_punct() {
579                    return Err(input.error("expected path segment"));
580                }
581                segments
582            },
583        })
584    }
585
586    impl Parse for Meta {
587        fn parse(input: ParseStream) -> Result<Self> {
588            let path = input.call(parse_meta_path)?;
589            parse_meta_after_path(path, input)
590        }
591    }
592
593    impl Parse for MetaList {
594        fn parse(input: ParseStream) -> Result<Self> {
595            let path = input.call(parse_meta_path)?;
596            parse_meta_list_after_path(path, input)
597        }
598    }
599
600    impl Parse for MetaNameValue {
601        fn parse(input: ParseStream) -> Result<Self> {
602            let path = input.call(parse_meta_path)?;
603            parse_meta_name_value_after_path(path, input)
604        }
605    }
606
607    impl Parse for NestedMeta {
608        fn parse(input: ParseStream) -> Result<Self> {
609            if input.peek(Lit) && !(input.peek(LitBool) && input.peek2(Token![=])) {
610                input.parse().map(NestedMeta::Lit)
611            } else if input.peek(Ident::peek_any) {
612                input.parse().map(NestedMeta::Meta)
613            } else {
614                Err(input.error("expected identifier or literal"))
615            }
616        }
617    }
618
619    pub fn parse_meta_after_path(path: Path, input: ParseStream) -> Result<Meta> {
620        if input.peek(token::Paren) {
621            parse_meta_list_after_path(path, input).map(Meta::List)
622        } else if input.peek(Token![=]) {
623            parse_meta_name_value_after_path(path, input).map(Meta::NameValue)
624        } else {
625            Ok(Meta::Path(path))
626        }
627    }
628
629    fn parse_meta_list_after_path(path: Path, input: ParseStream) -> Result<MetaList> {
630        let content;
631        Ok(MetaList {
632            path,
633            paren_token: parenthesized!(content in input),
634            nested: content.parse_terminated(NestedMeta::parse)?,
635        })
636    }
637
638    fn parse_meta_name_value_after_path(path: Path, input: ParseStream) -> Result<MetaNameValue> {
639        Ok(MetaNameValue {
640            path,
641            eq_token: input.parse()?,
642            lit: input.parse()?,
643        })
644    }
645}
646
647#[cfg(feature = "printing")]
648mod printing {
649    use super::*;
650    use proc_macro2::TokenStream;
651    use quote::ToTokens;
652
653    impl ToTokens for Attribute {
654        fn to_tokens(&self, tokens: &mut TokenStream) {
655            self.pound_token.to_tokens(tokens);
656            if let AttrStyle::Inner(b) = &self.style {
657                b.to_tokens(tokens);
658            }
659            self.bracket_token.surround(tokens, |tokens| {
660                self.path.to_tokens(tokens);
661                self.tokens.to_tokens(tokens);
662            });
663        }
664    }
665
666    impl ToTokens for MetaList {
667        fn to_tokens(&self, tokens: &mut TokenStream) {
668            self.path.to_tokens(tokens);
669            self.paren_token.surround(tokens, |tokens| {
670                self.nested.to_tokens(tokens);
671            })
672        }
673    }
674
675    impl ToTokens for MetaNameValue {
676        fn to_tokens(&self, tokens: &mut TokenStream) {
677            self.path.to_tokens(tokens);
678            self.eq_token.to_tokens(tokens);
679            self.lit.to_tokens(tokens);
680        }
681    }
682}