1 use super::*;
2 use crate::punctuated::Punctuated;
3 
4 use std::iter;
5 
6 use proc_macro2::TokenStream;
7 
8 #[cfg(feature = "parsing")]
9 use crate::parse::{Parse, ParseBuffer, ParseStream, Parser, Result};
10 #[cfg(feature = "parsing")]
11 use crate::punctuated::Pair;
12 #[cfg(feature = "extra-traits")]
13 use crate::tt::TokenStreamHelper;
14 #[cfg(feature = "extra-traits")]
15 use std::hash::{Hash, Hasher};
16 
17 ast_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     pub struct Attribute #manual_extra_traits {
115         pub pound_token: Token![#],
116         pub style: AttrStyle,
117         pub bracket_token: token::Bracket,
118         pub path: Path,
119         pub tokens: TokenStream,
120     }
121 }
122 
123 #[cfg(feature = "extra-traits")]
124 impl Eq for Attribute {}
125 
126 #[cfg(feature = "extra-traits")]
127 impl PartialEq for Attribute {
eq(&self, other: &Self) -> bool128     fn eq(&self, other: &Self) -> bool {
129         self.style == other.style
130             && self.pound_token == other.pound_token
131             && self.bracket_token == other.bracket_token
132             && self.path == other.path
133             && TokenStreamHelper(&self.tokens) == TokenStreamHelper(&other.tokens)
134     }
135 }
136 
137 #[cfg(feature = "extra-traits")]
138 impl Hash for Attribute {
hash<H>(&self, state: &mut H) where H: Hasher,139     fn hash<H>(&self, state: &mut H)
140     where
141         H: Hasher,
142     {
143         self.style.hash(state);
144         self.pound_token.hash(state);
145         self.bracket_token.hash(state);
146         self.path.hash(state);
147         TokenStreamHelper(&self.tokens).hash(state);
148     }
149 }
150 
151 impl Attribute {
152     /// Parses the content of the attribute, consisting of the path and tokens,
153     /// as a [`Meta`] if possible.
154     ///
155     /// *This function is available if Syn is built with the `"parsing"`
156     /// feature.*
157     #[cfg(feature = "parsing")]
parse_meta(&self) -> Result<Meta>158     pub fn parse_meta(&self) -> Result<Meta> {
159         fn clone_ident_segment(segment: &PathSegment) -> PathSegment {
160             PathSegment {
161                 ident: segment.ident.clone(),
162                 arguments: PathArguments::None,
163             }
164         }
165 
166         let path = Path {
167             leading_colon: self
168                 .path
169                 .leading_colon
170                 .as_ref()
171                 .map(|colon| Token![::](colon.spans)),
172             segments: self
173                 .path
174                 .segments
175                 .pairs()
176                 .map(|pair| match pair {
177                     Pair::Punctuated(seg, punct) => {
178                         Pair::Punctuated(clone_ident_segment(seg), Token![::](punct.spans))
179                     }
180                     Pair::End(seg) => Pair::End(clone_ident_segment(seg)),
181                 })
182                 .collect(),
183         };
184 
185         let parser = |input: ParseStream| parsing::parse_meta_after_path(path, input);
186         parse::Parser::parse2(parser, self.tokens.clone())
187     }
188 
189     /// Parse the arguments to the attribute as a syntax tree.
190     ///
191     /// This is similar to `syn::parse2::<T>(attr.tokens)` except that:
192     ///
193     /// - the surrounding delimiters are *not* included in the input to the
194     ///   parser; and
195     /// - the error message has a more useful span when `tokens` is empty.
196     ///
197     /// ```text
198     /// #[my_attr(value < 5)]
199     ///           ^^^^^^^^^ what gets parsed
200     /// ```
201     ///
202     /// *This function is available if Syn is built with the `"parsing"`
203     /// feature.*
204     #[cfg(feature = "parsing")]
parse_args<T: Parse>(&self) -> Result<T>205     pub fn parse_args<T: Parse>(&self) -> Result<T> {
206         self.parse_args_with(T::parse)
207     }
208 
209     /// Parse the arguments to the attribute using the given parser.
210     ///
211     /// *This function is available if Syn is built with the `"parsing"`
212     /// feature.*
213     #[cfg(feature = "parsing")]
parse_args_with<F: Parser>(&self, parser: F) -> Result<F::Output>214     pub fn parse_args_with<F: Parser>(&self, parser: F) -> Result<F::Output> {
215         let parser = |input: ParseStream| {
216             let args = enter_args(self, input)?;
217             parse::parse_stream(parser, &args)
218         };
219         parser.parse2(self.tokens.clone())
220     }
221 
222     /// Parses zero or more outer attributes from the stream.
223     ///
224     /// *This function is available if Syn is built with the `"parsing"`
225     /// feature.*
226     #[cfg(feature = "parsing")]
parse_outer(input: ParseStream) -> Result<Vec<Self>>227     pub fn parse_outer(input: ParseStream) -> Result<Vec<Self>> {
228         let mut attrs = Vec::new();
229         while input.peek(Token![#]) {
230             attrs.push(input.call(parsing::single_parse_outer)?);
231         }
232         Ok(attrs)
233     }
234 
235     /// Parses zero or more inner attributes from the stream.
236     ///
237     /// *This function is available if Syn is built with the `"parsing"`
238     /// feature.*
239     #[cfg(feature = "parsing")]
parse_inner(input: ParseStream) -> Result<Vec<Self>>240     pub fn parse_inner(input: ParseStream) -> Result<Vec<Self>> {
241         let mut attrs = Vec::new();
242         while input.peek(Token![#]) && input.peek2(Token![!]) {
243             attrs.push(input.call(parsing::single_parse_inner)?);
244         }
245         Ok(attrs)
246     }
247 }
248 
249 #[cfg(feature = "parsing")]
error_expected_args(attr: &Attribute) -> Error250 fn error_expected_args(attr: &Attribute) -> Error {
251     let style = match attr.style {
252         AttrStyle::Outer => "#",
253         AttrStyle::Inner(_) => "#!",
254     };
255 
256     let mut path = String::new();
257     for segment in &attr.path.segments {
258         if !path.is_empty() || attr.path.leading_colon.is_some() {
259             path += "::";
260         }
261         path += &segment.ident.to_string();
262     }
263 
264     let msg = format!("expected attribute arguments: {}[{}(...)]", style, path);
265 
266     #[cfg(feature = "printing")]
267     return Error::new_spanned(attr, msg);
268 
269     #[cfg(not(feature = "printing"))]
270     return Error::new(attr.bracket_token.span, msg);
271 }
272 
273 #[cfg(feature = "parsing")]
enter_args<'a>(attr: &Attribute, input: ParseStream<'a>) -> Result<ParseBuffer<'a>>274 fn enter_args<'a>(attr: &Attribute, input: ParseStream<'a>) -> Result<ParseBuffer<'a>> {
275     if input.is_empty() {
276         return Err(error_expected_args(attr));
277     };
278 
279     let content;
280     if input.peek(token::Paren) {
281         parenthesized!(content in input);
282     } else if input.peek(token::Bracket) {
283         bracketed!(content in input);
284     } else if input.peek(token::Brace) {
285         braced!(content in input);
286     } else {
287         return Err(input.error("unexpected token in attribute arguments"));
288     }
289 
290     if input.is_empty() {
291         Ok(content)
292     } else {
293         Err(input.error("unexpected token in attribute arguments"))
294     }
295 }
296 
297 ast_enum! {
298     /// Distinguishes between attributes that decorate an item and attributes
299     /// that are contained within an item.
300     ///
301     /// *This type is available if Syn is built with the `"derive"` or `"full"`
302     /// feature.*
303     ///
304     /// # Outer attributes
305     ///
306     /// - `#[repr(transparent)]`
307     /// - `/// # Example`
308     /// - `/** Please file an issue */`
309     ///
310     /// # Inner attributes
311     ///
312     /// - `#![feature(proc_macro)]`
313     /// - `//! # Example`
314     /// - `/*! Please file an issue */`
315     #[cfg_attr(feature = "clone-impls", derive(Copy))]
316     pub enum AttrStyle {
317         Outer,
318         Inner(Token![!]),
319     }
320 }
321 
322 ast_enum_of_structs! {
323     /// Content of a compile-time structured attribute.
324     ///
325     /// *This type is available if Syn is built with the `"derive"` or `"full"`
326     /// feature.*
327     ///
328     /// ## Path
329     ///
330     /// A meta path is like the `test` in `#[test]`.
331     ///
332     /// ## List
333     ///
334     /// A meta list is like the `derive(Copy)` in `#[derive(Copy)]`.
335     ///
336     /// ## NameValue
337     ///
338     /// A name-value meta is like the `path = "..."` in `#[path =
339     /// "sys/windows.rs"]`.
340     ///
341     /// # Syntax tree enum
342     ///
343     /// This type is a [syntax tree enum].
344     ///
345     /// [syntax tree enum]: enum.Expr.html#syntax-tree-enums
346     //
347     // TODO: change syntax-tree-enum link to an intra rustdoc link, currently
348     // blocked on https://github.com/rust-lang/rust/issues/62833
349     pub enum Meta {
350         Path(Path),
351 
352         /// A structured list within an attribute, like `derive(Copy, Clone)`.
353         List(MetaList),
354 
355         /// A name-value pair within an attribute, like `feature = "nightly"`.
356         NameValue(MetaNameValue),
357     }
358 }
359 
360 ast_struct! {
361     /// A structured list within an attribute, like `derive(Copy, Clone)`.
362     ///
363     /// *This type is available if Syn is built with the `"derive"` or
364     /// `"full"` feature.*
365     pub struct MetaList {
366         pub path: Path,
367         pub paren_token: token::Paren,
368         pub nested: Punctuated<NestedMeta, Token![,]>,
369     }
370 }
371 
372 ast_struct! {
373     /// A name-value pair within an attribute, like `feature = "nightly"`.
374     ///
375     /// *This type is available if Syn is built with the `"derive"` or
376     /// `"full"` feature.*
377     pub struct MetaNameValue {
378         pub path: Path,
379         pub eq_token: Token![=],
380         pub lit: Lit,
381     }
382 }
383 
384 impl Meta {
385     /// Returns the identifier that begins this structured meta item.
386     ///
387     /// For example this would return the `test` in `#[test]`, the `derive` in
388     /// `#[derive(Copy)]`, and the `path` in `#[path = "sys/windows.rs"]`.
path(&self) -> &Path389     pub fn path(&self) -> &Path {
390         match self {
391             Meta::Path(path) => path,
392             Meta::List(meta) => &meta.path,
393             Meta::NameValue(meta) => &meta.path,
394         }
395     }
396 }
397 
398 ast_enum_of_structs! {
399     /// Element of a compile-time attribute list.
400     ///
401     /// *This type is available if Syn is built with the `"derive"` or `"full"`
402     /// feature.*
403     pub enum NestedMeta {
404         /// A structured meta item, like the `Copy` in `#[derive(Copy)]` which
405         /// would be a nested `Meta::Path`.
406         Meta(Meta),
407 
408         /// A Rust literal, like the `"new_name"` in `#[rename("new_name")]`.
409         Lit(Lit),
410     }
411 }
412 
413 /// Conventional argument type associated with an invocation of an attribute
414 /// macro.
415 ///
416 /// For example if we are developing an attribute macro that is intended to be
417 /// invoked on function items as follows:
418 ///
419 /// ```
420 /// # const IGNORE: &str = stringify! {
421 /// #[my_attribute(path = "/v1/refresh")]
422 /// # };
423 /// pub fn refresh() {
424 ///     /* ... */
425 /// }
426 /// ```
427 ///
428 /// The implementation of this macro would want to parse its attribute arguments
429 /// as type `AttributeArgs`.
430 ///
431 /// ```
432 /// extern crate proc_macro;
433 ///
434 /// use proc_macro::TokenStream;
435 /// use syn::{parse_macro_input, AttributeArgs, ItemFn};
436 ///
437 /// # const IGNORE: &str = stringify! {
438 /// #[proc_macro_attribute]
439 /// # };
440 /// pub fn my_attribute(args: TokenStream, input: TokenStream) -> TokenStream {
441 ///     let args = parse_macro_input!(args as AttributeArgs);
442 ///     let input = parse_macro_input!(input as ItemFn);
443 ///
444 ///     /* ... */
445 /// #   "".parse().unwrap()
446 /// }
447 /// ```
448 pub type AttributeArgs = Vec<NestedMeta>;
449 
450 pub trait FilterAttrs<'a> {
451     type Ret: Iterator<Item = &'a Attribute>;
452 
outer(self) -> Self::Ret453     fn outer(self) -> Self::Ret;
inner(self) -> Self::Ret454     fn inner(self) -> Self::Ret;
455 }
456 
457 impl<'a, T> FilterAttrs<'a> for T
458 where
459     T: IntoIterator<Item = &'a Attribute>,
460 {
461     type Ret = iter::Filter<T::IntoIter, fn(&&Attribute) -> bool>;
462 
outer(self) -> Self::Ret463     fn outer(self) -> Self::Ret {
464         fn is_outer(attr: &&Attribute) -> bool {
465             match attr.style {
466                 AttrStyle::Outer => true,
467                 _ => false,
468             }
469         }
470         self.into_iter().filter(is_outer)
471     }
472 
inner(self) -> Self::Ret473     fn inner(self) -> Self::Ret {
474         fn is_inner(attr: &&Attribute) -> bool {
475             match attr.style {
476                 AttrStyle::Inner(_) => true,
477                 _ => false,
478             }
479         }
480         self.into_iter().filter(is_inner)
481     }
482 }
483 
484 #[cfg(feature = "parsing")]
485 pub mod parsing {
486     use super::*;
487 
488     use crate::ext::IdentExt;
489     use crate::parse::{Parse, ParseStream, Result};
490     #[cfg(feature = "full")]
491     use crate::private;
492 
single_parse_inner(input: ParseStream) -> Result<Attribute>493     pub fn single_parse_inner(input: ParseStream) -> Result<Attribute> {
494         let content;
495         Ok(Attribute {
496             pound_token: input.parse()?,
497             style: AttrStyle::Inner(input.parse()?),
498             bracket_token: bracketed!(content in input),
499             path: content.call(Path::parse_mod_style)?,
500             tokens: content.parse()?,
501         })
502     }
503 
single_parse_outer(input: ParseStream) -> Result<Attribute>504     pub fn single_parse_outer(input: ParseStream) -> Result<Attribute> {
505         let content;
506         Ok(Attribute {
507             pound_token: input.parse()?,
508             style: AttrStyle::Outer,
509             bracket_token: bracketed!(content in input),
510             path: content.call(Path::parse_mod_style)?,
511             tokens: content.parse()?,
512         })
513     }
514 
515     #[cfg(feature = "full")]
516     impl private {
attrs(outer: Vec<Attribute>, inner: Vec<Attribute>) -> Vec<Attribute>517         pub fn attrs(outer: Vec<Attribute>, inner: Vec<Attribute>) -> Vec<Attribute> {
518             let mut attrs = outer;
519             attrs.extend(inner);
520             attrs
521         }
522     }
523 
524     // Like Path::parse_mod_style but accepts keywords in the path.
parse_meta_path(input: ParseStream) -> Result<Path>525     fn parse_meta_path(input: ParseStream) -> Result<Path> {
526         Ok(Path {
527             leading_colon: input.parse()?,
528             segments: {
529                 let mut segments = Punctuated::new();
530                 while input.peek(Ident::peek_any) {
531                     let ident = Ident::parse_any(input)?;
532                     segments.push_value(PathSegment::from(ident));
533                     if !input.peek(Token![::]) {
534                         break;
535                     }
536                     let punct = input.parse()?;
537                     segments.push_punct(punct);
538                 }
539                 if segments.is_empty() {
540                     return Err(input.error("expected path"));
541                 } else if segments.trailing_punct() {
542                     return Err(input.error("expected path segment"));
543                 }
544                 segments
545             },
546         })
547     }
548 
549     impl Parse for Meta {
parse(input: ParseStream) -> Result<Self>550         fn parse(input: ParseStream) -> Result<Self> {
551             let path = input.call(parse_meta_path)?;
552             parse_meta_after_path(path, input)
553         }
554     }
555 
556     impl Parse for MetaList {
parse(input: ParseStream) -> Result<Self>557         fn parse(input: ParseStream) -> Result<Self> {
558             let path = input.call(parse_meta_path)?;
559             parse_meta_list_after_path(path, input)
560         }
561     }
562 
563     impl Parse for MetaNameValue {
parse(input: ParseStream) -> Result<Self>564         fn parse(input: ParseStream) -> Result<Self> {
565             let path = input.call(parse_meta_path)?;
566             parse_meta_name_value_after_path(path, input)
567         }
568     }
569 
570     impl Parse for NestedMeta {
parse(input: ParseStream) -> Result<Self>571         fn parse(input: ParseStream) -> Result<Self> {
572             if input.peek(Lit) && !(input.peek(LitBool) && input.peek2(Token![=])) {
573                 input.parse().map(NestedMeta::Lit)
574             } else if input.peek(Ident::peek_any) {
575                 input.parse().map(NestedMeta::Meta)
576             } else {
577                 Err(input.error("expected identifier or literal"))
578             }
579         }
580     }
581 
parse_meta_after_path(path: Path, input: ParseStream) -> Result<Meta>582     pub fn parse_meta_after_path(path: Path, input: ParseStream) -> Result<Meta> {
583         if input.peek(token::Paren) {
584             parse_meta_list_after_path(path, input).map(Meta::List)
585         } else if input.peek(Token![=]) {
586             parse_meta_name_value_after_path(path, input).map(Meta::NameValue)
587         } else {
588             Ok(Meta::Path(path))
589         }
590     }
591 
parse_meta_list_after_path(path: Path, input: ParseStream) -> Result<MetaList>592     fn parse_meta_list_after_path(path: Path, input: ParseStream) -> Result<MetaList> {
593         let content;
594         Ok(MetaList {
595             path,
596             paren_token: parenthesized!(content in input),
597             nested: content.parse_terminated(NestedMeta::parse)?,
598         })
599     }
600 
parse_meta_name_value_after_path(path: Path, input: ParseStream) -> Result<MetaNameValue>601     fn parse_meta_name_value_after_path(path: Path, input: ParseStream) -> Result<MetaNameValue> {
602         Ok(MetaNameValue {
603             path,
604             eq_token: input.parse()?,
605             lit: input.parse()?,
606         })
607     }
608 }
609 
610 #[cfg(feature = "printing")]
611 mod printing {
612     use super::*;
613     use proc_macro2::TokenStream;
614     use quote::ToTokens;
615 
616     impl ToTokens for Attribute {
to_tokens(&self, tokens: &mut TokenStream)617         fn to_tokens(&self, tokens: &mut TokenStream) {
618             self.pound_token.to_tokens(tokens);
619             if let AttrStyle::Inner(b) = &self.style {
620                 b.to_tokens(tokens);
621             }
622             self.bracket_token.surround(tokens, |tokens| {
623                 self.path.to_tokens(tokens);
624                 self.tokens.to_tokens(tokens);
625             });
626         }
627     }
628 
629     impl ToTokens for MetaList {
to_tokens(&self, tokens: &mut TokenStream)630         fn to_tokens(&self, tokens: &mut TokenStream) {
631             self.path.to_tokens(tokens);
632             self.paren_token.surround(tokens, |tokens| {
633                 self.nested.to_tokens(tokens);
634             })
635         }
636     }
637 
638     impl ToTokens for MetaNameValue {
to_tokens(&self, tokens: &mut TokenStream)639         fn to_tokens(&self, tokens: &mut TokenStream) {
640             self.path.to_tokens(tokens);
641             self.eq_token.to_tokens(tokens);
642             self.lit.to_tokens(tokens);
643         }
644     }
645 }
646