2 use crate::punctuated
::Punctuated
;
3 use proc_macro2
::TokenStream
;
6 #[cfg(feature = "parsing")]
7 use crate::parse
::{Parse, ParseBuffer, ParseStream, Parser, Result}
;
8 #[cfg(feature = "parsing")]
9 use crate::punctuated
::Pair
;
12 /// An attribute like `#[repr(transparent)]`.
14 /// *This type is available only if Syn is built with the `"derive"` or `"full"`
21 /// Rust has six types of attributes.
23 /// - Outer attributes like `#[repr(transparent)]`. These appear outside or
24 /// in front of the item they describe.
25 /// - Inner attributes like `#![feature(proc_macro)]`. These appear inside
26 /// of the item they describe, usually a module.
27 /// - Outer doc comments like `/// # Example`.
28 /// - Inner doc comments like `//! Please file an issue`.
29 /// - Outer block comments `/** # Example */`.
30 /// - Inner block comments `/*! Please file an issue */`.
32 /// The `style` field of type `AttrStyle` distinguishes whether an attribute
33 /// is outer or inner. Doc comments and block comments are promoted to
34 /// attributes, as this is how they are processed by the compiler and by
35 /// `macro_rules!` macros.
37 /// The `path` field gives the possibly colon-delimited path against which
38 /// the attribute is resolved. It is equal to `"doc"` for desugared doc
39 /// comments. The `tokens` field contains the rest of the attribute body as
43 /// #[derive(Copy)] #[crate::precondition x < 5]
44 /// ^^^^^^~~~~~~ ^^^^^^^^^^^^^^^^^^^ ~~~~~
45 /// path tokens path tokens
50 /// # Parsing from tokens to Attribute
52 /// This type does not implement the [`Parse`] trait and thus cannot be
53 /// parsed directly by [`ParseStream::parse`]. Instead use
54 /// [`ParseStream::call`] with one of the two parser functions
55 /// [`Attribute::parse_outer`] or [`Attribute::parse_inner`] depending on
56 /// which you intend to parse.
58 /// [`Parse`]: parse::Parse
59 /// [`ParseStream::parse`]: parse::ParseBuffer::parse
60 /// [`ParseStream::call`]: parse::ParseBuffer::call
63 /// use syn::{Attribute, Ident, Result, Token};
64 /// use syn::parse::{Parse, ParseStream};
66 /// // Parses a unit struct with attributes.
68 /// // #[path = "s.tmpl"]
70 /// struct UnitStruct {
71 /// attrs: Vec<Attribute>,
72 /// struct_token: Token![struct],
74 /// semi_token: Token![;],
77 /// impl Parse for UnitStruct {
78 /// fn parse(input: ParseStream) -> Result<Self> {
80 /// attrs: input.call(Attribute::parse_outer)?,
81 /// struct_token: input.parse()?,
82 /// name: input.parse()?,
83 /// semi_token: input.parse()?,
91 /// # Parsing from Attribute to structured arguments
93 /// The grammar of attributes in Rust is very flexible, which makes the
94 /// syntax tree not that useful on its own. In particular, arguments of the
95 /// attribute are held in an arbitrary `tokens: TokenStream`. Macros are
96 /// expected to check the `path` of the attribute, decide whether they
97 /// recognize it, and then parse the remaining tokens according to whatever
98 /// grammar they wish to require for that kind of attribute.
100 /// If the attribute you are parsing is expected to conform to the
101 /// conventional structured form of attribute, use [`parse_meta()`] to
102 /// obtain that structured representation. If the attribute follows some
103 /// other grammar of its own, use [`parse_args()`] to parse that into the
104 /// expected data structure.
106 /// [`parse_meta()`]: Attribute::parse_meta
107 /// [`parse_args()`]: Attribute::parse_args
113 /// The compiler transforms doc comments, such as `/// comment` and `/*!
114 /// comment */`, into attributes before macros are expanded. Each comment is
115 /// expanded into an attribute of the form `#[doc = r"comment"]`.
117 /// As an example, the following `mod` items are expanded identically:
120 /// # use syn::{ItemMod, parse_quote};
121 /// let doc: ItemMod = parse_quote! {
122 /// /// Single line doc comments
123 /// /// We write so many!
125 /// * Multi-line comments...
126 /// * May span many lines
129 /// //! Of course, they can be inner too
130 /// /*! And fit in a single line */
133 /// let attr: ItemMod = parse_quote! {
134 /// #[doc = r" Single line doc comments"]
135 /// #[doc = r" We write so many!"]
137 /// * Multi-line comments...
138 /// * May span many lines
141 /// #![doc = r" Of course, they can be inner too"]
142 /// #![doc = r" And fit in a single line "]
145 /// assert_eq!(doc, attr);
147 #[cfg_attr(doc_cfg, doc(cfg(any(feature = "full", feature = "derive"))))]
148 pub struct Attribute
{
149 pub pound_token
: Token
![#],
150 pub style
: AttrStyle
,
151 pub bracket_token
: token
::Bracket
,
153 pub tokens
: TokenStream
,
158 /// Parses the content of the attribute, consisting of the path and tokens,
159 /// as a [`Meta`] if possible.
161 /// *This function is available only if Syn is built with the `"parsing"`
163 #[cfg(feature = "parsing")]
164 #[cfg_attr(doc_cfg, doc(cfg(feature = "parsing")))]
165 pub fn parse_meta(&self) -> Result
<Meta
> {
166 fn clone_ident_segment(segment
: &PathSegment
) -> PathSegment
{
168 ident
: segment
.ident
.clone(),
169 arguments
: PathArguments
::None
,
178 .map(|colon
| Token
![::](colon
.spans
)),
183 .map(|pair
| match pair
{
184 Pair
::Punctuated(seg
, punct
) => {
185 Pair
::Punctuated(clone_ident_segment(seg
), Token
![::](punct
.spans
))
187 Pair
::End(seg
) => Pair
::End(clone_ident_segment(seg
)),
192 let parser
= |input
: ParseStream
| parsing
::parse_meta_after_path(path
, input
);
193 parse
::Parser
::parse2(parser
, self.tokens
.clone())
196 /// Parse the arguments to the attribute as a syntax tree.
198 /// This is similar to `syn::parse2::<T>(attr.tokens)` except that:
200 /// - the surrounding delimiters are *not* included in the input to the
202 /// - the error message has a more useful span when `tokens` is empty.
205 /// #[my_attr(value < 5)]
206 /// ^^^^^^^^^ what gets parsed
209 /// *This function is available only if Syn is built with the `"parsing"`
211 #[cfg(feature = "parsing")]
212 #[cfg_attr(doc_cfg, doc(cfg(feature = "parsing")))]
213 pub fn parse_args
<T
: Parse
>(&self) -> Result
<T
> {
214 self.parse_args_with(T
::parse
)
217 /// Parse the arguments to the attribute using the given parser.
219 /// *This function is available only if Syn is built with the `"parsing"`
221 #[cfg(feature = "parsing")]
222 #[cfg_attr(doc_cfg, doc(cfg(feature = "parsing")))]
223 pub fn parse_args_with
<F
: Parser
>(&self, parser
: F
) -> Result
<F
::Output
> {
224 let parser
= |input
: ParseStream
| {
225 let args
= enter_args(self, input
)?
;
226 parse
::parse_stream(parser
, &args
)
228 parser
.parse2(self.tokens
.clone())
231 /// Parses zero or more outer attributes from the stream.
233 /// *This function is available only if Syn is built with the `"parsing"`
235 #[cfg(feature = "parsing")]
236 #[cfg_attr(doc_cfg, doc(cfg(feature = "parsing")))]
237 pub fn parse_outer(input
: ParseStream
) -> Result
<Vec
<Self>> {
238 let mut attrs
= Vec
::new();
239 while input
.peek(Token
![#]) {
240 attrs
.push(input
.call(parsing
::single_parse_outer
)?
);
245 /// Parses zero or more inner attributes from the stream.
247 /// *This function is available only if Syn is built with the `"parsing"`
249 #[cfg(feature = "parsing")]
250 #[cfg_attr(doc_cfg, doc(cfg(feature = "parsing")))]
251 pub fn parse_inner(input
: ParseStream
) -> Result
<Vec
<Self>> {
252 let mut attrs
= Vec
::new();
253 parsing
::parse_inner(input
, &mut attrs
)?
;
258 #[cfg(feature = "parsing")]
259 fn expected_parentheses(attr
: &Attribute
) -> String
{
260 let style
= match attr
.style
{
261 AttrStyle
::Outer
=> "#",
262 AttrStyle
::Inner(_
) => "#!",
265 let mut path
= String
::new();
266 for segment
in &attr
.path
.segments
{
267 if !path
.is_empty() || attr
.path
.leading_colon
.is_some() {
270 path
+= &segment
.ident
.to_string();
273 format
!("{}[{}(...)]", style
, path
)
276 #[cfg(feature = "parsing")]
277 fn enter_args
<'a
>(attr
: &Attribute
, input
: ParseStream
<'a
>) -> Result
<ParseBuffer
<'a
>> {
278 if input
.is_empty() {
279 let expected
= expected_parentheses(attr
);
280 let msg
= format
!("expected attribute arguments in parentheses: {}", expected
);
281 return Err(crate::error
::new2(
282 attr
.pound_token
.span
,
283 attr
.bracket_token
.span
,
286 } else if input
.peek(Token
![=]) {
287 let expected
= expected_parentheses(attr
);
288 let msg
= format
!("expected parentheses: {}", expected
);
289 return Err(input
.error(msg
));
293 if input
.peek(token
::Paren
) {
294 parenthesized
!(content
in input
);
295 } else if input
.peek(token
::Bracket
) {
296 bracketed
!(content
in input
);
297 } else if input
.peek(token
::Brace
) {
298 braced
!(content
in input
);
300 return Err(input
.error("unexpected token in attribute arguments"));
303 if input
.is_empty() {
306 Err(input
.error("unexpected token in attribute arguments"))
311 /// Distinguishes between attributes that decorate an item and attributes
312 /// that are contained within an item.
314 /// *This type is available only if Syn is built with the `"derive"` or `"full"`
317 /// # Outer attributes
319 /// - `#[repr(transparent)]`
320 /// - `/// # Example`
321 /// - `/** Please file an issue */`
323 /// # Inner attributes
325 /// - `#![feature(proc_macro)]`
326 /// - `//! # Example`
327 /// - `/*! Please file an issue */`
328 #[cfg_attr(doc_cfg, doc(cfg(any(feature = "full", feature = "derive"))))]
335 ast_enum_of_structs
! {
336 /// Content of a compile-time structured attribute.
338 /// *This type is available only if Syn is built with the `"derive"` or `"full"`
343 /// A meta path is like the `test` in `#[test]`.
347 /// A meta list is like the `derive(Copy)` in `#[derive(Copy)]`.
351 /// A name-value meta is like the `path = "..."` in `#[path =
352 /// "sys/windows.rs"]`.
354 /// # Syntax tree enum
356 /// This type is a [syntax tree enum].
358 /// [syntax tree enum]: Expr#syntax-tree-enums
359 #[cfg_attr(doc_cfg, doc(cfg(any(feature = "full", feature = "derive"))))]
363 /// A structured list within an attribute, like `derive(Copy, Clone)`.
366 /// A name-value pair within an attribute, like `feature = "nightly"`.
367 NameValue(MetaNameValue
),
372 /// A structured list within an attribute, like `derive(Copy, Clone)`.
374 /// *This type is available only if Syn is built with the `"derive"` or
375 /// `"full"` feature.*
376 #[cfg_attr(doc_cfg, doc(cfg(any(feature = "full", feature = "derive"))))]
377 pub struct MetaList
{
379 pub paren_token
: token
::Paren
,
380 pub nested
: Punctuated
<NestedMeta
, Token
![,]>,
385 /// A name-value pair within an attribute, like `feature = "nightly"`.
387 /// *This type is available only if Syn is built with the `"derive"` or
388 /// `"full"` feature.*
389 #[cfg_attr(doc_cfg, doc(cfg(any(feature = "full", feature = "derive"))))]
390 pub struct MetaNameValue
{
392 pub eq_token
: Token
![=],
398 /// Returns the identifier that begins this structured meta item.
400 /// For example this would return the `test` in `#[test]`, the `derive` in
401 /// `#[derive(Copy)]`, and the `path` in `#[path = "sys/windows.rs"]`.
402 pub fn path(&self) -> &Path
{
404 Meta
::Path(path
) => path
,
405 Meta
::List(meta
) => &meta
.path
,
406 Meta
::NameValue(meta
) => &meta
.path
,
411 ast_enum_of_structs
! {
412 /// Element of a compile-time attribute list.
414 /// *This type is available only if Syn is built with the `"derive"` or `"full"`
416 #[cfg_attr(doc_cfg, doc(cfg(any(feature = "full", feature = "derive"))))]
417 pub enum NestedMeta
{
418 /// A structured meta item, like the `Copy` in `#[derive(Copy)]` which
419 /// would be a nested `Meta::Path`.
422 /// A Rust literal, like the `"new_name"` in `#[rename("new_name")]`.
427 /// Conventional argument type associated with an invocation of an attribute
430 /// For example if we are developing an attribute macro that is intended to be
431 /// invoked on function items as follows:
434 /// # const IGNORE: &str = stringify! {
435 /// #[my_attribute(path = "/v1/refresh")]
437 /// pub fn refresh() {
442 /// The implementation of this macro would want to parse its attribute arguments
443 /// as type `AttributeArgs`.
446 /// # extern crate proc_macro;
448 /// use proc_macro::TokenStream;
449 /// use syn::{parse_macro_input, AttributeArgs, ItemFn};
451 /// # const IGNORE: &str = stringify! {
452 /// #[proc_macro_attribute]
454 /// pub fn my_attribute(args: TokenStream, input: TokenStream) -> TokenStream {
455 /// let args = parse_macro_input!(args as AttributeArgs);
456 /// let input = parse_macro_input!(input as ItemFn);
459 /// # "".parse().unwrap()
462 #[cfg_attr(doc_cfg, doc(cfg(any(feature = "full", feature = "derive"))))]
463 pub type AttributeArgs
= Vec
<NestedMeta
>;
465 pub trait FilterAttrs
<'a
> {
466 type Ret
: Iterator
<Item
= &'a Attribute
>;
468 fn outer(self) -> Self::Ret
;
469 fn inner(self) -> Self::Ret
;
472 impl<'a
, T
> FilterAttrs
<'a
> for T
474 T
: IntoIterator
<Item
= &'a Attribute
>,
476 type Ret
= iter
::Filter
<T
::IntoIter
, fn(&&Attribute
) -> bool
>;
478 fn outer(self) -> Self::Ret
{
479 fn is_outer(attr
: &&Attribute
) -> bool
{
481 AttrStyle
::Outer
=> true,
482 AttrStyle
::Inner(_
) => false,
485 self.into_iter().filter(is_outer
)
488 fn inner(self) -> Self::Ret
{
489 fn is_inner(attr
: &&Attribute
) -> bool
{
491 AttrStyle
::Inner(_
) => true,
492 AttrStyle
::Outer
=> false,
495 self.into_iter().filter(is_inner
)
499 #[cfg(feature = "parsing")]
502 use crate::ext
::IdentExt
;
503 use crate::parse
::{Parse, ParseStream, Result}
;
505 pub fn parse_inner(input
: ParseStream
, attrs
: &mut Vec
<Attribute
>) -> Result
<()> {
506 while input
.peek(Token
![#]) && input.peek2(Token![!]) {
507 attrs
.push(input
.call(parsing
::single_parse_inner
)?
);
512 pub fn single_parse_inner(input
: ParseStream
) -> Result
<Attribute
> {
515 pound_token
: input
.parse()?
,
516 style
: AttrStyle
::Inner(input
.parse()?
),
517 bracket_token
: bracketed
!(content
in input
),
518 path
: content
.call(Path
::parse_mod_style
)?
,
519 tokens
: content
.parse()?
,
523 pub fn single_parse_outer(input
: ParseStream
) -> Result
<Attribute
> {
526 pound_token
: input
.parse()?
,
527 style
: AttrStyle
::Outer
,
528 bracket_token
: bracketed
!(content
in input
),
529 path
: content
.call(Path
::parse_mod_style
)?
,
530 tokens
: content
.parse()?
,
534 // Like Path::parse_mod_style but accepts keywords in the path.
535 fn parse_meta_path(input
: ParseStream
) -> Result
<Path
> {
537 leading_colon
: input
.parse()?
,
539 let mut segments
= Punctuated
::new();
540 while input
.peek(Ident
::peek_any
) {
541 let ident
= Ident
::parse_any(input
)?
;
542 segments
.push_value(PathSegment
::from(ident
));
543 if !input
.peek(Token
![::]) {
546 let punct
= input
.parse()?
;
547 segments
.push_punct(punct
);
549 if segments
.is_empty() {
550 return Err(input
.error("expected path"));
551 } else if segments
.trailing_punct() {
552 return Err(input
.error("expected path segment"));
559 #[cfg_attr(doc_cfg, doc(cfg(feature = "parsing")))]
560 impl Parse
for Meta
{
561 fn parse(input
: ParseStream
) -> Result
<Self> {
562 let path
= input
.call(parse_meta_path
)?
;
563 parse_meta_after_path(path
, input
)
567 #[cfg_attr(doc_cfg, doc(cfg(feature = "parsing")))]
568 impl Parse
for MetaList
{
569 fn parse(input
: ParseStream
) -> Result
<Self> {
570 let path
= input
.call(parse_meta_path
)?
;
571 parse_meta_list_after_path(path
, input
)
575 #[cfg_attr(doc_cfg, doc(cfg(feature = "parsing")))]
576 impl Parse
for MetaNameValue
{
577 fn parse(input
: ParseStream
) -> Result
<Self> {
578 let path
= input
.call(parse_meta_path
)?
;
579 parse_meta_name_value_after_path(path
, input
)
583 #[cfg_attr(doc_cfg, doc(cfg(feature = "parsing")))]
584 impl Parse
for NestedMeta
{
585 fn parse(input
: ParseStream
) -> Result
<Self> {
586 if input
.peek(Lit
) && !(input
.peek(LitBool
) && input
.peek2(Token
![=])) {
587 input
.parse().map(NestedMeta
::Lit
)
588 } else if input
.peek(Ident
::peek_any
)
589 || input
.peek(Token
![::]) && input
.peek3(Ident
::peek_any
)
591 input
.parse().map(NestedMeta
::Meta
)
593 Err(input
.error("expected identifier or literal"))
598 pub fn parse_meta_after_path(path
: Path
, input
: ParseStream
) -> Result
<Meta
> {
599 if input
.peek(token
::Paren
) {
600 parse_meta_list_after_path(path
, input
).map(Meta
::List
)
601 } else if input
.peek(Token
![=]) {
602 parse_meta_name_value_after_path(path
, input
).map(Meta
::NameValue
)
608 fn parse_meta_list_after_path(path
: Path
, input
: ParseStream
) -> Result
<MetaList
> {
612 paren_token
: parenthesized
!(content
in input
),
613 nested
: content
.parse_terminated(NestedMeta
::parse
)?
,
617 fn parse_meta_name_value_after_path(path
: Path
, input
: ParseStream
) -> Result
<MetaNameValue
> {
620 eq_token
: input
.parse()?
,
626 #[cfg(feature = "printing")]
629 use proc_macro2
::TokenStream
;
632 #[cfg_attr(doc_cfg, doc(cfg(feature = "printing")))]
633 impl ToTokens
for Attribute
{
634 fn to_tokens(&self, tokens
: &mut TokenStream
) {
635 self.pound_token
.to_tokens(tokens
);
636 if let AttrStyle
::Inner(b
) = &self.style
{
639 self.bracket_token
.surround(tokens
, |tokens
| {
640 self.path
.to_tokens(tokens
);
641 self.tokens
.to_tokens(tokens
);
646 #[cfg_attr(doc_cfg, doc(cfg(feature = "printing")))]
647 impl ToTokens
for MetaList
{
648 fn to_tokens(&self, tokens
: &mut TokenStream
) {
649 self.path
.to_tokens(tokens
);
650 self.paren_token
.surround(tokens
, |tokens
| {
651 self.nested
.to_tokens(tokens
);
656 #[cfg_attr(doc_cfg, doc(cfg(feature = "printing")))]
657 impl ToTokens
for MetaNameValue
{
658 fn to_tokens(&self, tokens
: &mut TokenStream
) {
659 self.path
.to_tokens(tokens
);
660 self.eq_token
.to_tokens(tokens
);
661 self.lit
.to_tokens(tokens
);