]> git.proxmox.com Git - cargo.git/blob - vendor/syn/src/attr.rs
New upstream version 0.52.0
[cargo.git] / vendor / syn / src / attr.rs
1 use super::*;
2 use crate::punctuated::Punctuated;
3 use proc_macro2::TokenStream;
4 use std::iter;
5
6 #[cfg(feature = "parsing")]
7 use crate::parse::{Parse, ParseBuffer, ParseStream, Parser, Result};
8 #[cfg(feature = "parsing")]
9 use crate::punctuated::Pair;
10
11 ast_struct! {
12 /// An attribute like `#[repr(transparent)]`.
13 ///
14 /// *This type is available only if Syn is built with the `"derive"` or `"full"`
15 /// feature.*
16 ///
17 /// <br>
18 ///
19 /// # Syntax
20 ///
21 /// Rust has six types of attributes.
22 ///
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 */`.
31 ///
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.
36 ///
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
40 /// tokens.
41 ///
42 /// ```text
43 /// #[derive(Copy)] #[crate::precondition x < 5]
44 /// ^^^^^^~~~~~~ ^^^^^^^^^^^^^^^^^^^ ~~~~~
45 /// path tokens path tokens
46 /// ```
47 ///
48 /// <br>
49 ///
50 /// # Parsing from tokens to Attribute
51 ///
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.
57 ///
58 /// [`Parse`]: parse::Parse
59 /// [`ParseStream::parse`]: parse::ParseBuffer::parse
60 /// [`ParseStream::call`]: parse::ParseBuffer::call
61 ///
62 /// ```
63 /// use syn::{Attribute, Ident, Result, Token};
64 /// use syn::parse::{Parse, ParseStream};
65 ///
66 /// // Parses a unit struct with attributes.
67 /// //
68 /// // #[path = "s.tmpl"]
69 /// // struct S;
70 /// struct UnitStruct {
71 /// attrs: Vec<Attribute>,
72 /// struct_token: Token![struct],
73 /// name: Ident,
74 /// semi_token: Token![;],
75 /// }
76 ///
77 /// impl Parse for UnitStruct {
78 /// fn parse(input: ParseStream) -> Result<Self> {
79 /// Ok(UnitStruct {
80 /// attrs: input.call(Attribute::parse_outer)?,
81 /// struct_token: input.parse()?,
82 /// name: input.parse()?,
83 /// semi_token: input.parse()?,
84 /// })
85 /// }
86 /// }
87 /// ```
88 ///
89 /// <p><br></p>
90 ///
91 /// # Parsing from Attribute to structured arguments
92 ///
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.
99 ///
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.
105 ///
106 /// [`parse_meta()`]: Attribute::parse_meta
107 /// [`parse_args()`]: Attribute::parse_args
108 ///
109 /// <p><br></p>
110 ///
111 /// # Doc comments
112 ///
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"]`.
116 ///
117 /// As an example, the following `mod` items are expanded identically:
118 ///
119 /// ```
120 /// # use syn::{ItemMod, parse_quote};
121 /// let doc: ItemMod = parse_quote! {
122 /// /// Single line doc comments
123 /// /// We write so many!
124 /// /**
125 /// * Multi-line comments...
126 /// * May span many lines
127 /// */
128 /// mod example {
129 /// //! Of course, they can be inner too
130 /// /*! And fit in a single line */
131 /// }
132 /// };
133 /// let attr: ItemMod = parse_quote! {
134 /// #[doc = r" Single line doc comments"]
135 /// #[doc = r" We write so many!"]
136 /// #[doc = r"
137 /// * Multi-line comments...
138 /// * May span many lines
139 /// "]
140 /// mod example {
141 /// #![doc = r" Of course, they can be inner too"]
142 /// #![doc = r" And fit in a single line "]
143 /// }
144 /// };
145 /// assert_eq!(doc, attr);
146 /// ```
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,
152 pub path: Path,
153 pub tokens: TokenStream,
154 }
155 }
156
157 impl Attribute {
158 /// Parses the content of the attribute, consisting of the path and tokens,
159 /// as a [`Meta`] if possible.
160 ///
161 /// *This function is available only if Syn is built with the `"parsing"`
162 /// feature.*
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 {
167 PathSegment {
168 ident: segment.ident.clone(),
169 arguments: PathArguments::None,
170 }
171 }
172
173 let path = Path {
174 leading_colon: self
175 .path
176 .leading_colon
177 .as_ref()
178 .map(|colon| Token![::](colon.spans)),
179 segments: self
180 .path
181 .segments
182 .pairs()
183 .map(|pair| match pair {
184 Pair::Punctuated(seg, punct) => {
185 Pair::Punctuated(clone_ident_segment(seg), Token![::](punct.spans))
186 }
187 Pair::End(seg) => Pair::End(clone_ident_segment(seg)),
188 })
189 .collect(),
190 };
191
192 let parser = |input: ParseStream| parsing::parse_meta_after_path(path, input);
193 parse::Parser::parse2(parser, self.tokens.clone())
194 }
195
196 /// Parse the arguments to the attribute as a syntax tree.
197 ///
198 /// This is similar to `syn::parse2::<T>(attr.tokens)` except that:
199 ///
200 /// - the surrounding delimiters are *not* included in the input to the
201 /// parser; and
202 /// - the error message has a more useful span when `tokens` is empty.
203 ///
204 /// ```text
205 /// #[my_attr(value < 5)]
206 /// ^^^^^^^^^ what gets parsed
207 /// ```
208 ///
209 /// *This function is available only if Syn is built with the `"parsing"`
210 /// feature.*
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)
215 }
216
217 /// Parse the arguments to the attribute using the given parser.
218 ///
219 /// *This function is available only if Syn is built with the `"parsing"`
220 /// feature.*
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)
227 };
228 parser.parse2(self.tokens.clone())
229 }
230
231 /// Parses zero or more outer attributes from the stream.
232 ///
233 /// *This function is available only if Syn is built with the `"parsing"`
234 /// feature.*
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)?);
241 }
242 Ok(attrs)
243 }
244
245 /// Parses zero or more inner attributes from the stream.
246 ///
247 /// *This function is available only if Syn is built with the `"parsing"`
248 /// feature.*
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)?;
254 Ok(attrs)
255 }
256 }
257
258 #[cfg(feature = "parsing")]
259 fn expected_parentheses(attr: &Attribute) -> String {
260 let style = match attr.style {
261 AttrStyle::Outer => "#",
262 AttrStyle::Inner(_) => "#!",
263 };
264
265 let mut path = String::new();
266 for segment in &attr.path.segments {
267 if !path.is_empty() || attr.path.leading_colon.is_some() {
268 path += "::";
269 }
270 path += &segment.ident.to_string();
271 }
272
273 format!("{}[{}(...)]", style, path)
274 }
275
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,
284 msg,
285 ));
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));
290 };
291
292 let content;
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);
299 } else {
300 return Err(input.error("unexpected token in attribute arguments"));
301 }
302
303 if input.is_empty() {
304 Ok(content)
305 } else {
306 Err(input.error("unexpected token in attribute arguments"))
307 }
308 }
309
310 ast_enum! {
311 /// Distinguishes between attributes that decorate an item and attributes
312 /// that are contained within an item.
313 ///
314 /// *This type is available only if Syn is built with the `"derive"` or `"full"`
315 /// feature.*
316 ///
317 /// # Outer attributes
318 ///
319 /// - `#[repr(transparent)]`
320 /// - `/// # Example`
321 /// - `/** Please file an issue */`
322 ///
323 /// # Inner attributes
324 ///
325 /// - `#![feature(proc_macro)]`
326 /// - `//! # Example`
327 /// - `/*! Please file an issue */`
328 #[cfg_attr(doc_cfg, doc(cfg(any(feature = "full", feature = "derive"))))]
329 pub enum AttrStyle {
330 Outer,
331 Inner(Token![!]),
332 }
333 }
334
335 ast_enum_of_structs! {
336 /// Content of a compile-time structured attribute.
337 ///
338 /// *This type is available only if Syn is built with the `"derive"` or `"full"`
339 /// feature.*
340 ///
341 /// ## Path
342 ///
343 /// A meta path is like the `test` in `#[test]`.
344 ///
345 /// ## List
346 ///
347 /// A meta list is like the `derive(Copy)` in `#[derive(Copy)]`.
348 ///
349 /// ## NameValue
350 ///
351 /// A name-value meta is like the `path = "..."` in `#[path =
352 /// "sys/windows.rs"]`.
353 ///
354 /// # Syntax tree enum
355 ///
356 /// This type is a [syntax tree enum].
357 ///
358 /// [syntax tree enum]: Expr#syntax-tree-enums
359 #[cfg_attr(doc_cfg, doc(cfg(any(feature = "full", feature = "derive"))))]
360 pub enum Meta {
361 Path(Path),
362
363 /// A structured list within an attribute, like `derive(Copy, Clone)`.
364 List(MetaList),
365
366 /// A name-value pair within an attribute, like `feature = "nightly"`.
367 NameValue(MetaNameValue),
368 }
369 }
370
371 ast_struct! {
372 /// A structured list within an attribute, like `derive(Copy, Clone)`.
373 ///
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 {
378 pub path: Path,
379 pub paren_token: token::Paren,
380 pub nested: Punctuated<NestedMeta, Token![,]>,
381 }
382 }
383
384 ast_struct! {
385 /// A name-value pair within an attribute, like `feature = "nightly"`.
386 ///
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 {
391 pub path: Path,
392 pub eq_token: Token![=],
393 pub lit: Lit,
394 }
395 }
396
397 impl Meta {
398 /// Returns the identifier that begins this structured meta item.
399 ///
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 {
403 match self {
404 Meta::Path(path) => path,
405 Meta::List(meta) => &meta.path,
406 Meta::NameValue(meta) => &meta.path,
407 }
408 }
409 }
410
411 ast_enum_of_structs! {
412 /// Element of a compile-time attribute list.
413 ///
414 /// *This type is available only if Syn is built with the `"derive"` or `"full"`
415 /// feature.*
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`.
420 Meta(Meta),
421
422 /// A Rust literal, like the `"new_name"` in `#[rename("new_name")]`.
423 Lit(Lit),
424 }
425 }
426
427 /// Conventional argument type associated with an invocation of an attribute
428 /// macro.
429 ///
430 /// For example if we are developing an attribute macro that is intended to be
431 /// invoked on function items as follows:
432 ///
433 /// ```
434 /// # const IGNORE: &str = stringify! {
435 /// #[my_attribute(path = "/v1/refresh")]
436 /// # };
437 /// pub fn refresh() {
438 /// /* ... */
439 /// }
440 /// ```
441 ///
442 /// The implementation of this macro would want to parse its attribute arguments
443 /// as type `AttributeArgs`.
444 ///
445 /// ```
446 /// # extern crate proc_macro;
447 /// #
448 /// use proc_macro::TokenStream;
449 /// use syn::{parse_macro_input, AttributeArgs, ItemFn};
450 ///
451 /// # const IGNORE: &str = stringify! {
452 /// #[proc_macro_attribute]
453 /// # };
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);
457 ///
458 /// /* ... */
459 /// # "".parse().unwrap()
460 /// }
461 /// ```
462 #[cfg_attr(doc_cfg, doc(cfg(any(feature = "full", feature = "derive"))))]
463 pub type AttributeArgs = Vec<NestedMeta>;
464
465 pub trait FilterAttrs<'a> {
466 type Ret: Iterator<Item = &'a Attribute>;
467
468 fn outer(self) -> Self::Ret;
469 fn inner(self) -> Self::Ret;
470 }
471
472 impl<'a, T> FilterAttrs<'a> for T
473 where
474 T: IntoIterator<Item = &'a Attribute>,
475 {
476 type Ret = iter::Filter<T::IntoIter, fn(&&Attribute) -> bool>;
477
478 fn outer(self) -> Self::Ret {
479 fn is_outer(attr: &&Attribute) -> bool {
480 match attr.style {
481 AttrStyle::Outer => true,
482 AttrStyle::Inner(_) => false,
483 }
484 }
485 self.into_iter().filter(is_outer)
486 }
487
488 fn inner(self) -> Self::Ret {
489 fn is_inner(attr: &&Attribute) -> bool {
490 match attr.style {
491 AttrStyle::Inner(_) => true,
492 AttrStyle::Outer => false,
493 }
494 }
495 self.into_iter().filter(is_inner)
496 }
497 }
498
499 #[cfg(feature = "parsing")]
500 pub mod parsing {
501 use super::*;
502 use crate::ext::IdentExt;
503 use crate::parse::{Parse, ParseStream, Result};
504
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)?);
508 }
509 Ok(())
510 }
511
512 pub fn single_parse_inner(input: ParseStream) -> Result<Attribute> {
513 let content;
514 Ok(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()?,
520 })
521 }
522
523 pub fn single_parse_outer(input: ParseStream) -> Result<Attribute> {
524 let content;
525 Ok(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()?,
531 })
532 }
533
534 // Like Path::parse_mod_style but accepts keywords in the path.
535 fn parse_meta_path(input: ParseStream) -> Result<Path> {
536 Ok(Path {
537 leading_colon: input.parse()?,
538 segments: {
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![::]) {
544 break;
545 }
546 let punct = input.parse()?;
547 segments.push_punct(punct);
548 }
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"));
553 }
554 segments
555 },
556 })
557 }
558
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)
564 }
565 }
566
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)
572 }
573 }
574
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)
580 }
581 }
582
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)
590 {
591 input.parse().map(NestedMeta::Meta)
592 } else {
593 Err(input.error("expected identifier or literal"))
594 }
595 }
596 }
597
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)
603 } else {
604 Ok(Meta::Path(path))
605 }
606 }
607
608 fn parse_meta_list_after_path(path: Path, input: ParseStream) -> Result<MetaList> {
609 let content;
610 Ok(MetaList {
611 path,
612 paren_token: parenthesized!(content in input),
613 nested: content.parse_terminated(NestedMeta::parse)?,
614 })
615 }
616
617 fn parse_meta_name_value_after_path(path: Path, input: ParseStream) -> Result<MetaNameValue> {
618 Ok(MetaNameValue {
619 path,
620 eq_token: input.parse()?,
621 lit: input.parse()?,
622 })
623 }
624 }
625
626 #[cfg(feature = "printing")]
627 mod printing {
628 use super::*;
629 use proc_macro2::TokenStream;
630 use quote::ToTokens;
631
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 {
637 b.to_tokens(tokens);
638 }
639 self.bracket_token.surround(tokens, |tokens| {
640 self.path.to_tokens(tokens);
641 self.tokens.to_tokens(tokens);
642 });
643 }
644 }
645
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);
652 })
653 }
654 }
655
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);
662 }
663 }
664 }