2 use crate::punctuated
::Punctuated
;
3 use proc_macro2
::TokenStream
;
7 #[cfg(feature = "parsing")]
8 use crate::parse
::{Parse, ParseBuffer, ParseStream, Parser, Result}
;
9 #[cfg(feature = "parsing")]
10 use crate::punctuated
::Pair
;
13 /// An attribute like `#[repr(transparent)]`.
15 /// *This type is available only if Syn is built with the `"derive"` or `"full"`
22 /// Rust has six types of attributes.
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 */`.
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.
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
44 /// #[derive(Copy)] #[crate::precondition x < 5]
45 /// ^^^^^^~~~~~~ ^^^^^^^^^^^^^^^^^^^ ~~~~~
46 /// path tokens path tokens
51 /// # Parsing from tokens to Attribute
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.
59 /// [`Parse`]: parse::Parse
60 /// [`ParseStream::parse`]: parse::ParseBuffer::parse
61 /// [`ParseStream::call`]: parse::ParseBuffer::call
64 /// use syn::{Attribute, Ident, Result, Token};
65 /// use syn::parse::{Parse, ParseStream};
67 /// // Parses a unit struct with attributes.
69 /// // #[path = "s.tmpl"]
71 /// struct UnitStruct {
72 /// attrs: Vec<Attribute>,
73 /// struct_token: Token![struct],
75 /// semi_token: Token![;],
78 /// impl Parse for UnitStruct {
79 /// fn parse(input: ParseStream) -> Result<Self> {
81 /// attrs: input.call(Attribute::parse_outer)?,
82 /// struct_token: input.parse()?,
83 /// name: input.parse()?,
84 /// semi_token: input.parse()?,
92 /// # Parsing from Attribute to structured arguments
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.
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.
107 /// [`parse_meta()`]: Attribute::parse_meta
108 /// [`parse_args()`]: Attribute::parse_args
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"]`.
118 /// As an example, the following `mod` items are expanded identically:
121 /// # use syn::{ItemMod, parse_quote};
122 /// let doc: ItemMod = parse_quote! {
123 /// /// Single line doc comments
124 /// /// We write so many!
126 /// * Multi-line comments...
127 /// * May span many lines
130 /// //! Of course, they can be inner too
131 /// /*! And fit in a single line */
134 /// let attr: ItemMod = parse_quote! {
135 /// #[doc = r" Single line doc comments"]
136 /// #[doc = r" We write so many!"]
138 /// * Multi-line comments...
139 /// * May span many lines
142 /// #![doc = r" Of course, they can be inner too"]
143 /// #![doc = r" And fit in a single line "]
146 /// assert_eq!(doc, attr);
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
,
154 pub tokens
: TokenStream
,
159 /// Parses the content of the attribute, consisting of the path and tokens,
160 /// as a [`Meta`] if possible.
162 /// *This function is available only if Syn is built with the `"parsing"`
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
{
169 ident
: segment
.ident
.clone(),
170 arguments
: PathArguments
::None
,
179 .map(|colon
| Token
![::](colon
.spans
)),
184 .map(|pair
| match pair
{
185 Pair
::Punctuated(seg
, punct
) => {
186 Pair
::Punctuated(clone_ident_segment(seg
), Token
![::](punct
.spans
))
188 Pair
::End(seg
) => Pair
::End(clone_ident_segment(seg
)),
193 let parser
= |input
: ParseStream
| parsing
::parse_meta_after_path(path
, input
);
194 parse
::Parser
::parse2(parser
, self.tokens
.clone())
197 /// Parse the arguments to the attribute as a syntax tree.
199 /// This is similar to `syn::parse2::<T>(attr.tokens)` except that:
201 /// - the surrounding delimiters are *not* included in the input to the
203 /// - the error message has a more useful span when `tokens` is empty.
206 /// #[my_attr(value < 5)]
207 /// ^^^^^^^^^ what gets parsed
210 /// *This function is available only if Syn is built with the `"parsing"`
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
)
218 /// Parse the arguments to the attribute using the given parser.
220 /// *This function is available only if Syn is built with the `"parsing"`
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
)
229 parser
.parse2(self.tokens
.clone())
232 /// Parses zero or more outer attributes from the stream.
234 /// *This function is available only if Syn is built with the `"parsing"`
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
)?
);
246 /// Parses zero or more inner attributes from the stream.
248 /// *This function is available only if Syn is built with the `"parsing"`
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
)?
;
259 #[cfg(feature = "parsing")]
260 fn expected_parentheses(attr
: &Attribute
) -> String
{
261 let style
= match attr
.style
{
262 AttrStyle
::Outer
=> "#",
263 AttrStyle
::Inner(_
) => "#!",
266 let mut path
= String
::new();
267 for segment
in &attr
.path
.segments
{
268 if !path
.is_empty() || attr
.path
.leading_colon
.is_some() {
271 path
+= &segment
.ident
.to_string();
274 format
!("{}[{}(...)]", style
, path
)
277 #[cfg(feature = "parsing")]
278 fn 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
,
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
));
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
);
301 return Err(input
.error("unexpected token in attribute arguments"));
304 if input
.is_empty() {
307 Err(input
.error("unexpected token in attribute arguments"))
312 /// Distinguishes between attributes that decorate an item and attributes
313 /// that are contained within an item.
315 /// *This type is available only if Syn is built with the `"derive"` or `"full"`
318 /// # Outer attributes
320 /// - `#[repr(transparent)]`
321 /// - `/// # Example`
322 /// - `/** Please file an issue */`
324 /// # Inner attributes
326 /// - `#![feature(proc_macro)]`
327 /// - `//! # Example`
328 /// - `/*! Please file an issue */`
329 #[cfg_attr(doc_cfg, doc(cfg(any(feature = "full", feature = "derive"))))]
336 ast_enum_of_structs
! {
337 /// Content of a compile-time structured attribute.
339 /// *This type is available only if Syn is built with the `"derive"` or `"full"`
344 /// A meta path is like the `test` in `#[test]`.
348 /// A meta list is like the `derive(Copy)` in `#[derive(Copy)]`.
352 /// A name-value meta is like the `path = "..."` in `#[path =
353 /// "sys/windows.rs"]`.
355 /// # Syntax tree enum
357 /// This type is a [syntax tree enum].
359 /// [syntax tree enum]: Expr#syntax-tree-enums
360 #[cfg_attr(doc_cfg, doc(cfg(any(feature = "full", feature = "derive"))))]
364 /// A structured list within an attribute, like `derive(Copy, Clone)`.
367 /// A name-value pair within an attribute, like `feature = "nightly"`.
368 NameValue(MetaNameValue
),
373 /// A structured list within an attribute, like `derive(Copy, Clone)`.
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
{
380 pub paren_token
: token
::Paren
,
381 pub nested
: Punctuated
<NestedMeta
, Token
![,]>,
386 /// A name-value pair within an attribute, like `feature = "nightly"`.
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
{
393 pub eq_token
: Token
![=],
399 /// Returns the identifier that begins this structured meta item.
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
{
405 Meta
::Path(path
) => path
,
406 Meta
::List(meta
) => &meta
.path
,
407 Meta
::NameValue(meta
) => &meta
.path
,
412 ast_enum_of_structs
! {
413 /// Element of a compile-time attribute list.
415 /// *This type is available only if Syn is built with the `"derive"` or `"full"`
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`.
423 /// A Rust literal, like the `"new_name"` in `#[rename("new_name")]`.
428 /// Conventional argument type associated with an invocation of an attribute
431 /// For example if we are developing an attribute macro that is intended to be
432 /// invoked on function items as follows:
435 /// # const IGNORE: &str = stringify! {
436 /// #[my_attribute(path = "/v1/refresh")]
438 /// pub fn refresh() {
443 /// The implementation of this macro would want to parse its attribute arguments
444 /// as type `AttributeArgs`.
447 /// # extern crate proc_macro;
449 /// use proc_macro::TokenStream;
450 /// use syn::{parse_macro_input, AttributeArgs, ItemFn};
452 /// # const IGNORE: &str = stringify! {
453 /// #[proc_macro_attribute]
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);
460 /// # "".parse().unwrap()
463 #[cfg_attr(doc_cfg, doc(cfg(any(feature = "full", feature = "derive"))))]
464 pub type AttributeArgs
= Vec
<NestedMeta
>;
466 pub trait FilterAttrs
<'a
> {
467 type Ret
: Iterator
<Item
= &'a Attribute
>;
469 fn outer(self) -> Self::Ret
;
470 fn inner(self) -> Self::Ret
;
473 impl<'a
> FilterAttrs
<'a
> for &'a
[Attribute
] {
474 type Ret
= iter
::Filter
<slice
::Iter
<'a
, Attribute
>, fn(&&Attribute
) -> bool
>;
476 fn outer(self) -> Self::Ret
{
477 fn is_outer(attr
: &&Attribute
) -> bool
{
479 AttrStyle
::Outer
=> true,
480 AttrStyle
::Inner(_
) => false,
483 self.iter().filter(is_outer
)
486 fn inner(self) -> Self::Ret
{
487 fn is_inner(attr
: &&Attribute
) -> bool
{
489 AttrStyle
::Inner(_
) => true,
490 AttrStyle
::Outer
=> false,
493 self.iter().filter(is_inner
)
497 #[cfg(feature = "parsing")]
500 use crate::ext
::IdentExt
;
501 use crate::parse
::{Parse, ParseStream, Result}
;
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
)?
);
510 pub fn single_parse_inner(input
: ParseStream
) -> Result
<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()?
,
521 pub fn single_parse_outer(input
: ParseStream
) -> Result
<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()?
,
532 // Like Path::parse_mod_style but accepts keywords in the path.
533 fn parse_meta_path(input
: ParseStream
) -> Result
<Path
> {
535 leading_colon
: input
.parse()?
,
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
![::]) {
544 let punct
= input
.parse()?
;
545 segments
.push_punct(punct
);
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"));
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
)
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
)
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
)
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
)
589 input
.parse().map(NestedMeta
::Meta
)
591 Err(input
.error("expected identifier or literal"))
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
)
606 fn parse_meta_list_after_path(path
: Path
, input
: ParseStream
) -> Result
<MetaList
> {
610 paren_token
: parenthesized
!(content
in input
),
611 nested
: content
.parse_terminated(NestedMeta
::parse
)?
,
615 fn parse_meta_name_value_after_path(path
: Path
, input
: ParseStream
) -> Result
<MetaNameValue
> {
618 eq_token
: input
.parse()?
,
624 #[cfg(feature = "printing")]
627 use proc_macro2
::TokenStream
;
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
{
637 self.bracket_token
.surround(tokens
, |tokens
| {
638 self.path
.to_tokens(tokens
);
639 self.tokens
.to_tokens(tokens
);
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
);
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
);