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