syn/
attr.rs

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