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}
663