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