]> git.proxmox.com Git - rustc.git/blame - vendor/syn/src/attr.rs
New upstream version 1.41.1+dfsg1
[rustc.git] / vendor / syn / src / attr.rs
CommitLineData
e74abb32
XL
1use super::*;
2use crate::punctuated::Punctuated;
3
4use std::iter;
5
6use proc_macro2::TokenStream;
7
8#[cfg(feature = "parsing")]
9use crate::parse::{Parse, ParseBuffer, ParseStream, Parser, Result};
10#[cfg(feature = "parsing")]
11use crate::punctuated::Pair;
12#[cfg(feature = "extra-traits")]
13use crate::tt::TokenStreamHelper;
14#[cfg(feature = "extra-traits")]
15use std::hash::{Hash, Hasher};
16
17ast_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
60c5eb7d
XL
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 /// ```
e74abb32
XL
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")]
161impl Eq for Attribute {}
162
163#[cfg(feature = "extra-traits")]
164impl PartialEq for Attribute {
165 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")]
175impl Hash for Attribute {
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
188impl 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")]
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")]
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")]
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")]
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")]
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")]
287fn error_expected_args(attr: &Attribute) -> Error {
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 let msg = format!("expected attribute arguments: {}[{}(...)]", style, path);
302
303 #[cfg(feature = "printing")]
304 return Error::new_spanned(attr, msg);
305
306 #[cfg(not(feature = "printing"))]
307 return Error::new(attr.bracket_token.span, msg);
308}
309
310#[cfg(feature = "parsing")]
311fn enter_args<'a>(attr: &Attribute, input: ParseStream<'a>) -> Result<ParseBuffer<'a>> {
312 if input.is_empty() {
313 return Err(error_expected_args(attr));
314 };
315
316 let content;
317 if input.peek(token::Paren) {
318 parenthesized!(content in input);
319 } else if input.peek(token::Bracket) {
320 bracketed!(content in input);
321 } else if input.peek(token::Brace) {
322 braced!(content in input);
323 } else {
324 return Err(input.error("unexpected token in attribute arguments"));
325 }
326
327 if input.is_empty() {
328 Ok(content)
329 } else {
330 Err(input.error("unexpected token in attribute arguments"))
331 }
332}
333
334ast_enum! {
335 /// Distinguishes between attributes that decorate an item and attributes
336 /// that are contained within an item.
337 ///
338 /// *This type is available if Syn is built with the `"derive"` or `"full"`
339 /// feature.*
340 ///
341 /// # Outer attributes
342 ///
343 /// - `#[repr(transparent)]`
344 /// - `/// # Example`
345 /// - `/** Please file an issue */`
346 ///
347 /// # Inner attributes
348 ///
349 /// - `#![feature(proc_macro)]`
350 /// - `//! # Example`
351 /// - `/*! Please file an issue */`
352 #[cfg_attr(feature = "clone-impls", derive(Copy))]
353 pub enum AttrStyle {
354 Outer,
355 Inner(Token![!]),
356 }
357}
358
359ast_enum_of_structs! {
360 /// Content of a compile-time structured attribute.
361 ///
362 /// *This type is available if Syn is built with the `"derive"` or `"full"`
363 /// feature.*
364 ///
365 /// ## Path
366 ///
367 /// A meta path is like the `test` in `#[test]`.
368 ///
369 /// ## List
370 ///
371 /// A meta list is like the `derive(Copy)` in `#[derive(Copy)]`.
372 ///
373 /// ## NameValue
374 ///
375 /// A name-value meta is like the `path = "..."` in `#[path =
376 /// "sys/windows.rs"]`.
377 ///
378 /// # Syntax tree enum
379 ///
380 /// This type is a [syntax tree enum].
381 ///
382 /// [syntax tree enum]: enum.Expr.html#syntax-tree-enums
383 //
384 // TODO: change syntax-tree-enum link to an intra rustdoc link, currently
385 // blocked on https://github.com/rust-lang/rust/issues/62833
386 pub enum Meta {
387 Path(Path),
388
389 /// A structured list within an attribute, like `derive(Copy, Clone)`.
390 List(MetaList),
391
392 /// A name-value pair within an attribute, like `feature = "nightly"`.
393 NameValue(MetaNameValue),
394 }
395}
396
397ast_struct! {
398 /// A structured list within an attribute, like `derive(Copy, Clone)`.
399 ///
400 /// *This type is available if Syn is built with the `"derive"` or
401 /// `"full"` feature.*
402 pub struct MetaList {
403 pub path: Path,
404 pub paren_token: token::Paren,
405 pub nested: Punctuated<NestedMeta, Token![,]>,
406 }
407}
408
409ast_struct! {
410 /// A name-value pair within an attribute, like `feature = "nightly"`.
411 ///
412 /// *This type is available if Syn is built with the `"derive"` or
413 /// `"full"` feature.*
414 pub struct MetaNameValue {
415 pub path: Path,
416 pub eq_token: Token![=],
417 pub lit: Lit,
418 }
419}
420
421impl Meta {
422 /// Returns the identifier that begins this structured meta item.
423 ///
424 /// For example this would return the `test` in `#[test]`, the `derive` in
425 /// `#[derive(Copy)]`, and the `path` in `#[path = "sys/windows.rs"]`.
426 pub fn path(&self) -> &Path {
427 match self {
428 Meta::Path(path) => path,
429 Meta::List(meta) => &meta.path,
430 Meta::NameValue(meta) => &meta.path,
431 }
432 }
433}
434
435ast_enum_of_structs! {
436 /// Element of a compile-time attribute list.
437 ///
438 /// *This type is available if Syn is built with the `"derive"` or `"full"`
439 /// feature.*
440 pub enum NestedMeta {
441 /// A structured meta item, like the `Copy` in `#[derive(Copy)]` which
442 /// would be a nested `Meta::Path`.
443 Meta(Meta),
444
445 /// A Rust literal, like the `"new_name"` in `#[rename("new_name")]`.
446 Lit(Lit),
447 }
448}
449
450/// Conventional argument type associated with an invocation of an attribute
451/// macro.
452///
453/// For example if we are developing an attribute macro that is intended to be
454/// invoked on function items as follows:
455///
456/// ```
457/// # const IGNORE: &str = stringify! {
458/// #[my_attribute(path = "/v1/refresh")]
459/// # };
460/// pub fn refresh() {
461/// /* ... */
462/// }
463/// ```
464///
465/// The implementation of this macro would want to parse its attribute arguments
466/// as type `AttributeArgs`.
467///
468/// ```
469/// extern crate proc_macro;
470///
471/// use proc_macro::TokenStream;
472/// use syn::{parse_macro_input, AttributeArgs, ItemFn};
473///
474/// # const IGNORE: &str = stringify! {
475/// #[proc_macro_attribute]
476/// # };
477/// pub fn my_attribute(args: TokenStream, input: TokenStream) -> TokenStream {
478/// let args = parse_macro_input!(args as AttributeArgs);
479/// let input = parse_macro_input!(input as ItemFn);
480///
481/// /* ... */
482/// # "".parse().unwrap()
483/// }
484/// ```
485pub type AttributeArgs = Vec<NestedMeta>;
486
487pub trait FilterAttrs<'a> {
488 type Ret: Iterator<Item = &'a Attribute>;
489
490 fn outer(self) -> Self::Ret;
491 fn inner(self) -> Self::Ret;
492}
493
494impl<'a, T> FilterAttrs<'a> for T
495where
496 T: IntoIterator<Item = &'a Attribute>,
497{
498 type Ret = iter::Filter<T::IntoIter, fn(&&Attribute) -> bool>;
499
500 fn outer(self) -> Self::Ret {
501 fn is_outer(attr: &&Attribute) -> bool {
502 match attr.style {
503 AttrStyle::Outer => true,
504 _ => false,
505 }
506 }
507 self.into_iter().filter(is_outer)
508 }
509
510 fn inner(self) -> Self::Ret {
511 fn is_inner(attr: &&Attribute) -> bool {
512 match attr.style {
513 AttrStyle::Inner(_) => true,
514 _ => false,
515 }
516 }
517 self.into_iter().filter(is_inner)
518 }
519}
520
521#[cfg(feature = "parsing")]
522pub mod parsing {
523 use super::*;
524
525 use crate::ext::IdentExt;
526 use crate::parse::{Parse, ParseStream, Result};
527 #[cfg(feature = "full")]
528 use crate::private;
529
530 pub fn single_parse_inner(input: ParseStream) -> Result<Attribute> {
531 let content;
532 Ok(Attribute {
533 pound_token: input.parse()?,
534 style: AttrStyle::Inner(input.parse()?),
535 bracket_token: bracketed!(content in input),
536 path: content.call(Path::parse_mod_style)?,
537 tokens: content.parse()?,
538 })
539 }
540
541 pub fn single_parse_outer(input: ParseStream) -> Result<Attribute> {
542 let content;
543 Ok(Attribute {
544 pound_token: input.parse()?,
545 style: AttrStyle::Outer,
546 bracket_token: bracketed!(content in input),
547 path: content.call(Path::parse_mod_style)?,
548 tokens: content.parse()?,
549 })
550 }
551
552 #[cfg(feature = "full")]
553 impl private {
554 pub fn attrs(outer: Vec<Attribute>, inner: Vec<Attribute>) -> Vec<Attribute> {
555 let mut attrs = outer;
556 attrs.extend(inner);
557 attrs
558 }
559 }
560
561 // Like Path::parse_mod_style but accepts keywords in the path.
562 fn parse_meta_path(input: ParseStream) -> Result<Path> {
563 Ok(Path {
564 leading_colon: input.parse()?,
565 segments: {
566 let mut segments = Punctuated::new();
567 while input.peek(Ident::peek_any) {
568 let ident = Ident::parse_any(input)?;
569 segments.push_value(PathSegment::from(ident));
570 if !input.peek(Token![::]) {
571 break;
572 }
573 let punct = input.parse()?;
574 segments.push_punct(punct);
575 }
576 if segments.is_empty() {
577 return Err(input.error("expected path"));
578 } else if segments.trailing_punct() {
579 return Err(input.error("expected path segment"));
580 }
581 segments
582 },
583 })
584 }
585
586 impl Parse for Meta {
587 fn parse(input: ParseStream) -> Result<Self> {
588 let path = input.call(parse_meta_path)?;
589 parse_meta_after_path(path, input)
590 }
591 }
592
593 impl Parse for MetaList {
594 fn parse(input: ParseStream) -> Result<Self> {
595 let path = input.call(parse_meta_path)?;
596 parse_meta_list_after_path(path, input)
597 }
598 }
599
600 impl Parse for MetaNameValue {
601 fn parse(input: ParseStream) -> Result<Self> {
602 let path = input.call(parse_meta_path)?;
603 parse_meta_name_value_after_path(path, input)
604 }
605 }
606
607 impl Parse for NestedMeta {
608 fn parse(input: ParseStream) -> Result<Self> {
609 if input.peek(Lit) && !(input.peek(LitBool) && input.peek2(Token![=])) {
610 input.parse().map(NestedMeta::Lit)
611 } else if input.peek(Ident::peek_any) {
612 input.parse().map(NestedMeta::Meta)
613 } else {
614 Err(input.error("expected identifier or literal"))
615 }
616 }
617 }
618
619 pub fn parse_meta_after_path(path: Path, input: ParseStream) -> Result<Meta> {
620 if input.peek(token::Paren) {
621 parse_meta_list_after_path(path, input).map(Meta::List)
622 } else if input.peek(Token![=]) {
623 parse_meta_name_value_after_path(path, input).map(Meta::NameValue)
624 } else {
625 Ok(Meta::Path(path))
626 }
627 }
628
629 fn parse_meta_list_after_path(path: Path, input: ParseStream) -> Result<MetaList> {
630 let content;
631 Ok(MetaList {
632 path,
633 paren_token: parenthesized!(content in input),
634 nested: content.parse_terminated(NestedMeta::parse)?,
635 })
636 }
637
638 fn parse_meta_name_value_after_path(path: Path, input: ParseStream) -> Result<MetaNameValue> {
639 Ok(MetaNameValue {
640 path,
641 eq_token: input.parse()?,
642 lit: input.parse()?,
643 })
644 }
645}
646
647#[cfg(feature = "printing")]
648mod printing {
649 use super::*;
650 use proc_macro2::TokenStream;
651 use quote::ToTokens;
652
653 impl ToTokens for Attribute {
654 fn to_tokens(&self, tokens: &mut TokenStream) {
655 self.pound_token.to_tokens(tokens);
656 if let AttrStyle::Inner(b) = &self.style {
657 b.to_tokens(tokens);
658 }
659 self.bracket_token.surround(tokens, |tokens| {
660 self.path.to_tokens(tokens);
661 self.tokens.to_tokens(tokens);
662 });
663 }
664 }
665
666 impl ToTokens for MetaList {
667 fn to_tokens(&self, tokens: &mut TokenStream) {
668 self.path.to_tokens(tokens);
669 self.paren_token.surround(tokens, |tokens| {
670 self.nested.to_tokens(tokens);
671 })
672 }
673 }
674
675 impl ToTokens for MetaNameValue {
676 fn to_tokens(&self, tokens: &mut TokenStream) {
677 self.path.to_tokens(tokens);
678 self.eq_token.to_tokens(tokens);
679 self.lit.to_tokens(tokens);
680 }
681 }
682}