1 | #[cfg (feature = "parsing" )] |
2 | use crate::error::Error; |
3 | #[cfg (feature = "parsing" )] |
4 | use crate::error::Result; |
5 | use crate::expr::Expr; |
6 | use crate::mac::MacroDelimiter; |
7 | #[cfg (feature = "parsing" )] |
8 | use crate::meta::{self, ParseNestedMeta}; |
9 | #[cfg (feature = "parsing" )] |
10 | use crate::parse::{Parse, ParseStream, Parser}; |
11 | use crate::path::Path; |
12 | use crate::token; |
13 | use proc_macro2::TokenStream; |
14 | #[cfg (feature = "printing" )] |
15 | use std::iter; |
16 | #[cfg (feature = "printing" )] |
17 | use std::slice; |
18 | |
19 | ast_struct! { |
20 | /// An attribute, like `#[repr(transparent)]`. |
21 | /// |
22 | /// <br> |
23 | /// |
24 | /// # Syntax |
25 | /// |
26 | /// Rust has six types of attributes. |
27 | /// |
28 | /// - Outer attributes like `#[repr(transparent)]`. These appear outside or |
29 | /// in front of the item they describe. |
30 | /// |
31 | /// - Inner attributes like `#![feature(proc_macro)]`. These appear inside |
32 | /// of the item they describe, usually a module. |
33 | /// |
34 | /// - Outer one-line doc comments like `/// Example`. |
35 | /// |
36 | /// - Inner one-line doc comments like `//! Please file an issue`. |
37 | /// |
38 | /// - Outer documentation blocks `/** Example */`. |
39 | /// |
40 | /// - Inner documentation blocks `/*! Please file an issue */`. |
41 | /// |
42 | /// The `style` field of type `AttrStyle` distinguishes whether an attribute |
43 | /// is outer or inner. |
44 | /// |
45 | /// Every attribute has a `path` that indicates the intended interpretation |
46 | /// of the rest of the attribute's contents. The path and the optional |
47 | /// additional contents are represented together in the `meta` field of the |
48 | /// attribute in three possible varieties: |
49 | /// |
50 | /// - Meta::Path — attributes whose information content conveys just a |
51 | /// path, for example the `#[test]` attribute. |
52 | /// |
53 | /// - Meta::List — attributes that carry arbitrary tokens after the |
54 | /// path, surrounded by a delimiter (parenthesis, bracket, or brace). For |
55 | /// example `#[derive(Copy)]` or `#[precondition(x < 5)]`. |
56 | /// |
57 | /// - Meta::NameValue — attributes with an `=` sign after the path, |
58 | /// followed by a Rust expression. For example `#[path = |
59 | /// "sys/windows.rs"]`. |
60 | /// |
61 | /// All doc comments are represented in the NameValue style with a path of |
62 | /// "doc", as this is how they are processed by the compiler and by |
63 | /// `macro_rules!` macros. |
64 | /// |
65 | /// ```text |
66 | /// #[derive(Copy, Clone)] |
67 | /// ~~~~~~Path |
68 | /// ^^^^^^^^^^^^^^^^^^^Meta::List |
69 | /// |
70 | /// #[path = "sys/windows.rs"] |
71 | /// ~~~~Path |
72 | /// ^^^^^^^^^^^^^^^^^^^^^^^Meta::NameValue |
73 | /// |
74 | /// #[test] |
75 | /// ^^^^Meta::Path |
76 | /// ``` |
77 | /// |
78 | /// <br> |
79 | /// |
80 | /// # Parsing from tokens to Attribute |
81 | /// |
82 | /// This type does not implement the [`Parse`] trait and thus cannot be |
83 | /// parsed directly by [`ParseStream::parse`]. Instead use |
84 | /// [`ParseStream::call`] with one of the two parser functions |
85 | /// [`Attribute::parse_outer`] or [`Attribute::parse_inner`] depending on |
86 | /// which you intend to parse. |
87 | /// |
88 | /// [`Parse`]: crate::parse::Parse |
89 | /// [`ParseStream::parse`]: crate::parse::ParseBuffer::parse |
90 | /// [`ParseStream::call`]: crate::parse::ParseBuffer::call |
91 | /// |
92 | /// ``` |
93 | /// use syn::{Attribute, Ident, Result, Token}; |
94 | /// use syn::parse::{Parse, ParseStream}; |
95 | /// |
96 | /// // Parses a unit struct with attributes. |
97 | /// // |
98 | /// // #[path = "s.tmpl"] |
99 | /// // struct S; |
100 | /// struct UnitStruct { |
101 | /// attrs: Vec<Attribute>, |
102 | /// struct_token: Token![struct], |
103 | /// name: Ident, |
104 | /// semi_token: Token![;], |
105 | /// } |
106 | /// |
107 | /// impl Parse for UnitStruct { |
108 | /// fn parse(input: ParseStream) -> Result<Self> { |
109 | /// Ok(UnitStruct { |
110 | /// attrs: input.call(Attribute::parse_outer)?, |
111 | /// struct_token: input.parse()?, |
112 | /// name: input.parse()?, |
113 | /// semi_token: input.parse()?, |
114 | /// }) |
115 | /// } |
116 | /// } |
117 | /// ``` |
118 | /// |
119 | /// <p><br></p> |
120 | /// |
121 | /// # Parsing from Attribute to structured arguments |
122 | /// |
123 | /// The grammar of attributes in Rust is very flexible, which makes the |
124 | /// syntax tree not that useful on its own. In particular, arguments of the |
125 | /// `Meta::List` variety of attribute are held in an arbitrary `tokens: |
126 | /// TokenStream`. Macros are expected to check the `path` of the attribute, |
127 | /// decide whether they recognize it, and then parse the remaining tokens |
128 | /// according to whatever grammar they wish to require for that kind of |
129 | /// attribute. Use [`parse_args()`] to parse those tokens into the expected |
130 | /// data structure. |
131 | /// |
132 | /// [`parse_args()`]: Attribute::parse_args |
133 | /// |
134 | /// <p><br></p> |
135 | /// |
136 | /// # Doc comments |
137 | /// |
138 | /// The compiler transforms doc comments, such as `/// comment` and `/*! |
139 | /// comment */`, into attributes before macros are expanded. Each comment is |
140 | /// expanded into an attribute of the form `#[doc = r"comment"]`. |
141 | /// |
142 | /// As an example, the following `mod` items are expanded identically: |
143 | /// |
144 | /// ``` |
145 | /// # use syn::{ItemMod, parse_quote}; |
146 | /// let doc: ItemMod = parse_quote! { |
147 | /// /// Single line doc comments |
148 | /// /// We write so many! |
149 | /// /** |
150 | /// * Multi-line comments... |
151 | /// * May span many lines |
152 | /// */ |
153 | /// mod example { |
154 | /// //! Of course, they can be inner too |
155 | /// /*! And fit in a single line */ |
156 | /// } |
157 | /// }; |
158 | /// let attr: ItemMod = parse_quote! { |
159 | /// #[doc = r" Single line doc comments"] |
160 | /// #[doc = r" We write so many!"] |
161 | /// #[doc = r" |
162 | /// * Multi-line comments... |
163 | /// * May span many lines |
164 | /// "] |
165 | /// mod example { |
166 | /// #![doc = r" Of course, they can be inner too"] |
167 | /// #![doc = r" And fit in a single line "] |
168 | /// } |
169 | /// }; |
170 | /// assert_eq!(doc, attr); |
171 | /// ``` |
172 | #[cfg_attr (doc_cfg, doc(cfg(any(feature = "full" , feature = "derive" ))))] |
173 | pub struct Attribute { |
174 | pub pound_token: Token![#], |
175 | pub style: AttrStyle, |
176 | pub bracket_token: token::Bracket, |
177 | pub meta: Meta, |
178 | } |
179 | } |
180 | |
181 | impl Attribute { |
182 | /// Returns the path that identifies the interpretation of this attribute. |
183 | /// |
184 | /// For example this would return the `test` in `#[test]`, the `derive` in |
185 | /// `#[derive(Copy)]`, and the `path` in `#[path = "sys/windows.rs"]`. |
186 | pub fn path(&self) -> &Path { |
187 | self.meta.path() |
188 | } |
189 | |
190 | /// Parse the arguments to the attribute as a syntax tree. |
191 | /// |
192 | /// This is similar to pulling out the `TokenStream` from `Meta::List` and |
193 | /// doing `syn::parse2::<T>(meta_list.tokens)`, except that using |
194 | /// `parse_args` the error message has a more useful span when `tokens` is |
195 | /// empty. |
196 | /// |
197 | /// The surrounding delimiters are *not* included in the input to the |
198 | /// parser. |
199 | /// |
200 | /// ```text |
201 | /// #[my_attr(value < 5)] |
202 | /// ^^^^^^^^^ what gets parsed |
203 | /// ``` |
204 | /// |
205 | /// # Example |
206 | /// |
207 | /// ``` |
208 | /// use syn::{parse_quote, Attribute, Expr}; |
209 | /// |
210 | /// let attr: Attribute = parse_quote! { |
211 | /// #[precondition(value < 5)] |
212 | /// }; |
213 | /// |
214 | /// if attr.path().is_ident("precondition" ) { |
215 | /// let precondition: Expr = attr.parse_args()?; |
216 | /// // ... |
217 | /// } |
218 | /// # anyhow::Ok(()) |
219 | /// ``` |
220 | #[cfg (feature = "parsing" )] |
221 | #[cfg_attr (doc_cfg, doc(cfg(feature = "parsing" )))] |
222 | pub fn parse_args<T: Parse>(&self) -> Result<T> { |
223 | self.parse_args_with(T::parse) |
224 | } |
225 | |
226 | /// Parse the arguments to the attribute using the given parser. |
227 | /// |
228 | /// # Example |
229 | /// |
230 | /// ``` |
231 | /// use syn::{parse_quote, Attribute}; |
232 | /// |
233 | /// let attr: Attribute = parse_quote! { |
234 | /// #[inception { #[brrrrrrraaaaawwwwrwrrrmrmrmmrmrmmmmm] }] |
235 | /// }; |
236 | /// |
237 | /// let bwom = attr.parse_args_with(Attribute::parse_outer)?; |
238 | /// |
239 | /// // Attribute does not have a Parse impl, so we couldn't directly do: |
240 | /// // let bwom: Attribute = attr.parse_args()?; |
241 | /// # anyhow::Ok(()) |
242 | /// ``` |
243 | #[cfg (feature = "parsing" )] |
244 | #[cfg_attr (doc_cfg, doc(cfg(feature = "parsing" )))] |
245 | pub fn parse_args_with<F: Parser>(&self, parser: F) -> Result<F::Output> { |
246 | match &self.meta { |
247 | Meta::Path(path) => Err(crate::error::new2( |
248 | path.segments.first().unwrap().ident.span(), |
249 | path.segments.last().unwrap().ident.span(), |
250 | format!( |
251 | "expected attribute arguments in parentheses: {}[ {}(...)]" , |
252 | parsing::DisplayAttrStyle(&self.style), |
253 | parsing::DisplayPath(path), |
254 | ), |
255 | )), |
256 | Meta::NameValue(meta) => Err(Error::new( |
257 | meta.eq_token.span, |
258 | format_args!( |
259 | "expected parentheses: {}[ {}(...)]" , |
260 | parsing::DisplayAttrStyle(&self.style), |
261 | parsing::DisplayPath(&meta.path), |
262 | ), |
263 | )), |
264 | Meta::List(meta) => meta.parse_args_with(parser), |
265 | } |
266 | } |
267 | |
268 | /// Parse the arguments to the attribute, expecting it to follow the |
269 | /// conventional structure used by most of Rust's built-in attributes. |
270 | /// |
271 | /// The [*Meta Item Attribute Syntax*][syntax] section in the Rust reference |
272 | /// explains the convention in more detail. Not all attributes follow this |
273 | /// convention, so [`parse_args()`][Self::parse_args] is available if you |
274 | /// need to parse arbitrarily goofy attribute syntax. |
275 | /// |
276 | /// [syntax]: https://doc.rust-lang.org/reference/attributes.html#meta-item-attribute-syntax |
277 | /// |
278 | /// # Example |
279 | /// |
280 | /// We'll parse a struct, and then parse some of Rust's `#[repr]` attribute |
281 | /// syntax. |
282 | /// |
283 | /// ``` |
284 | /// use syn::{parenthesized, parse_quote, token, ItemStruct, LitInt}; |
285 | /// |
286 | /// let input: ItemStruct = parse_quote! { |
287 | /// #[repr(C, align(4))] |
288 | /// pub struct MyStruct(u16, u32); |
289 | /// }; |
290 | /// |
291 | /// let mut repr_c = false; |
292 | /// let mut repr_transparent = false; |
293 | /// let mut repr_align = None::<usize>; |
294 | /// let mut repr_packed = None::<usize>; |
295 | /// for attr in &input.attrs { |
296 | /// if attr.path().is_ident("repr" ) { |
297 | /// attr.parse_nested_meta(|meta| { |
298 | /// // #[repr(C)] |
299 | /// if meta.path.is_ident("C" ) { |
300 | /// repr_c = true; |
301 | /// return Ok(()); |
302 | /// } |
303 | /// |
304 | /// // #[repr(transparent)] |
305 | /// if meta.path.is_ident("transparent" ) { |
306 | /// repr_transparent = true; |
307 | /// return Ok(()); |
308 | /// } |
309 | /// |
310 | /// // #[repr(align(N))] |
311 | /// if meta.path.is_ident("align" ) { |
312 | /// let content; |
313 | /// parenthesized!(content in meta.input); |
314 | /// let lit: LitInt = content.parse()?; |
315 | /// let n: usize = lit.base10_parse()?; |
316 | /// repr_align = Some(n); |
317 | /// return Ok(()); |
318 | /// } |
319 | /// |
320 | /// // #[repr(packed)] or #[repr(packed(N))], omitted N means 1 |
321 | /// if meta.path.is_ident("packed" ) { |
322 | /// if meta.input.peek(token::Paren) { |
323 | /// let content; |
324 | /// parenthesized!(content in meta.input); |
325 | /// let lit: LitInt = content.parse()?; |
326 | /// let n: usize = lit.base10_parse()?; |
327 | /// repr_packed = Some(n); |
328 | /// } else { |
329 | /// repr_packed = Some(1); |
330 | /// } |
331 | /// return Ok(()); |
332 | /// } |
333 | /// |
334 | /// Err(meta.error("unrecognized repr" )) |
335 | /// })?; |
336 | /// } |
337 | /// } |
338 | /// # anyhow::Ok(()) |
339 | /// ``` |
340 | /// |
341 | /// # Alternatives |
342 | /// |
343 | /// In some cases, for attributes which have nested layers of structured |
344 | /// content, the following less flexible approach might be more convenient: |
345 | /// |
346 | /// ``` |
347 | /// # use syn::{parse_quote, ItemStruct}; |
348 | /// # |
349 | /// # let input: ItemStruct = parse_quote! { |
350 | /// # #[repr(C, align(4))] |
351 | /// # pub struct MyStruct(u16, u32); |
352 | /// # }; |
353 | /// # |
354 | /// use syn::punctuated::Punctuated; |
355 | /// use syn::{parenthesized, token, Error, LitInt, Meta, Token}; |
356 | /// |
357 | /// let mut repr_c = false; |
358 | /// let mut repr_transparent = false; |
359 | /// let mut repr_align = None::<usize>; |
360 | /// let mut repr_packed = None::<usize>; |
361 | /// for attr in &input.attrs { |
362 | /// if attr.path().is_ident("repr" ) { |
363 | /// let nested = attr.parse_args_with(Punctuated::<Meta, Token![,]>::parse_terminated)?; |
364 | /// for meta in nested { |
365 | /// match meta { |
366 | /// // #[repr(C)] |
367 | /// Meta::Path(path) if path.is_ident("C" ) => { |
368 | /// repr_c = true; |
369 | /// } |
370 | /// |
371 | /// // #[repr(align(N))] |
372 | /// Meta::List(meta) if meta.path.is_ident("align" ) => { |
373 | /// let lit: LitInt = meta.parse_args()?; |
374 | /// let n: usize = lit.base10_parse()?; |
375 | /// repr_align = Some(n); |
376 | /// } |
377 | /// |
378 | /// /* ... */ |
379 | /// |
380 | /// _ => { |
381 | /// return Err(Error::new_spanned(meta, "unrecognized repr" )); |
382 | /// } |
383 | /// } |
384 | /// } |
385 | /// } |
386 | /// } |
387 | /// # Ok(()) |
388 | /// ``` |
389 | #[cfg (feature = "parsing" )] |
390 | #[cfg_attr (doc_cfg, doc(cfg(feature = "parsing" )))] |
391 | pub fn parse_nested_meta( |
392 | &self, |
393 | logic: impl FnMut(ParseNestedMeta) -> Result<()>, |
394 | ) -> Result<()> { |
395 | self.parse_args_with(meta::parser(logic)) |
396 | } |
397 | |
398 | /// Parses zero or more outer attributes from the stream. |
399 | /// |
400 | /// # Example |
401 | /// |
402 | /// See |
403 | /// [*Parsing from tokens to Attribute*](#parsing-from-tokens-to-attribute). |
404 | #[cfg (feature = "parsing" )] |
405 | #[cfg_attr (doc_cfg, doc(cfg(feature = "parsing" )))] |
406 | pub fn parse_outer(input: ParseStream) -> Result<Vec<Self>> { |
407 | let mut attrs = Vec::new(); |
408 | while input.peek(Token![#]) { |
409 | attrs.push(input.call(parsing::single_parse_outer)?); |
410 | } |
411 | Ok(attrs) |
412 | } |
413 | |
414 | /// Parses zero or more inner attributes from the stream. |
415 | /// |
416 | /// # Example |
417 | /// |
418 | /// See |
419 | /// [*Parsing from tokens to Attribute*](#parsing-from-tokens-to-attribute). |
420 | #[cfg (feature = "parsing" )] |
421 | #[cfg_attr (doc_cfg, doc(cfg(feature = "parsing" )))] |
422 | pub fn parse_inner(input: ParseStream) -> Result<Vec<Self>> { |
423 | let mut attrs = Vec::new(); |
424 | parsing::parse_inner(input, &mut attrs)?; |
425 | Ok(attrs) |
426 | } |
427 | } |
428 | |
429 | ast_enum! { |
430 | /// Distinguishes between attributes that decorate an item and attributes |
431 | /// that are contained within an item. |
432 | /// |
433 | /// # Outer attributes |
434 | /// |
435 | /// - `#[repr(transparent)]` |
436 | /// - `/// # Example` |
437 | /// - `/** Please file an issue */` |
438 | /// |
439 | /// # Inner attributes |
440 | /// |
441 | /// - `#![feature(proc_macro)]` |
442 | /// - `//! # Example` |
443 | /// - `/*! Please file an issue */` |
444 | #[cfg_attr (doc_cfg, doc(cfg(any(feature = "full" , feature = "derive" ))))] |
445 | pub enum AttrStyle { |
446 | Outer, |
447 | Inner(Token![!]), |
448 | } |
449 | } |
450 | |
451 | ast_enum_of_structs! { |
452 | /// Content of a compile-time structured attribute. |
453 | /// |
454 | /// ## Path |
455 | /// |
456 | /// A meta path is like the `test` in `#[test]`. |
457 | /// |
458 | /// ## List |
459 | /// |
460 | /// A meta list is like the `derive(Copy)` in `#[derive(Copy)]`. |
461 | /// |
462 | /// ## NameValue |
463 | /// |
464 | /// A name-value meta is like the `path = "..."` in `#[path = |
465 | /// "sys/windows.rs"]`. |
466 | /// |
467 | /// # Syntax tree enum |
468 | /// |
469 | /// This type is a [syntax tree enum]. |
470 | /// |
471 | /// [syntax tree enum]: crate::expr::Expr#syntax-tree-enums |
472 | #[cfg_attr (doc_cfg, doc(cfg(any(feature = "full" , feature = "derive" ))))] |
473 | pub enum Meta { |
474 | Path(Path), |
475 | |
476 | /// A structured list within an attribute, like `derive(Copy, Clone)`. |
477 | List(MetaList), |
478 | |
479 | /// A name-value pair within an attribute, like `feature = "nightly"`. |
480 | NameValue(MetaNameValue), |
481 | } |
482 | } |
483 | |
484 | ast_struct! { |
485 | /// A structured list within an attribute, like `derive(Copy, Clone)`. |
486 | #[cfg_attr (doc_cfg, doc(cfg(any(feature = "full" , feature = "derive" ))))] |
487 | pub struct MetaList { |
488 | pub path: Path, |
489 | pub delimiter: MacroDelimiter, |
490 | pub tokens: TokenStream, |
491 | } |
492 | } |
493 | |
494 | ast_struct! { |
495 | /// A name-value pair within an attribute, like `feature = "nightly"`. |
496 | #[cfg_attr (doc_cfg, doc(cfg(any(feature = "full" , feature = "derive" ))))] |
497 | pub struct MetaNameValue { |
498 | pub path: Path, |
499 | pub eq_token: Token![=], |
500 | pub value: Expr, |
501 | } |
502 | } |
503 | |
504 | impl Meta { |
505 | /// Returns the path that begins this structured meta item. |
506 | /// |
507 | /// For example this would return the `test` in `#[test]`, the `derive` in |
508 | /// `#[derive(Copy)]`, and the `path` in `#[path = "sys/windows.rs"]`. |
509 | pub fn path(&self) -> &Path { |
510 | match self { |
511 | Meta::Path(path) => path, |
512 | Meta::List(meta) => &meta.path, |
513 | Meta::NameValue(meta) => &meta.path, |
514 | } |
515 | } |
516 | |
517 | /// Error if this is a `Meta::List` or `Meta::NameValue`. |
518 | #[cfg (feature = "parsing" )] |
519 | #[cfg_attr (doc_cfg, doc(cfg(feature = "parsing" )))] |
520 | pub fn require_path_only(&self) -> Result<&Path> { |
521 | let error_span = match self { |
522 | Meta::Path(path) => return Ok(path), |
523 | Meta::List(meta) => meta.delimiter.span().open(), |
524 | Meta::NameValue(meta) => meta.eq_token.span, |
525 | }; |
526 | Err(Error::new(error_span, "unexpected token in attribute" )) |
527 | } |
528 | |
529 | /// Error if this is a `Meta::Path` or `Meta::NameValue`. |
530 | #[cfg (feature = "parsing" )] |
531 | #[cfg_attr (doc_cfg, doc(cfg(feature = "parsing" )))] |
532 | pub fn require_list(&self) -> Result<&MetaList> { |
533 | match self { |
534 | Meta::List(meta) => Ok(meta), |
535 | Meta::Path(path) => Err(crate::error::new2( |
536 | path.segments.first().unwrap().ident.span(), |
537 | path.segments.last().unwrap().ident.span(), |
538 | format!( |
539 | "expected attribute arguments in parentheses: ` {}(...)`" , |
540 | parsing::DisplayPath(path), |
541 | ), |
542 | )), |
543 | Meta::NameValue(meta) => Err(Error::new(meta.eq_token.span, "expected `(`" )), |
544 | } |
545 | } |
546 | |
547 | /// Error if this is a `Meta::Path` or `Meta::List`. |
548 | #[cfg (feature = "parsing" )] |
549 | #[cfg_attr (doc_cfg, doc(cfg(feature = "parsing" )))] |
550 | pub fn require_name_value(&self) -> Result<&MetaNameValue> { |
551 | match self { |
552 | Meta::NameValue(meta) => Ok(meta), |
553 | Meta::Path(path) => Err(crate::error::new2( |
554 | path.segments.first().unwrap().ident.span(), |
555 | path.segments.last().unwrap().ident.span(), |
556 | format!( |
557 | "expected a value for this attribute: ` {} = ...`" , |
558 | parsing::DisplayPath(path), |
559 | ), |
560 | )), |
561 | Meta::List(meta) => Err(Error::new(meta.delimiter.span().open(), "expected `=`" )), |
562 | } |
563 | } |
564 | } |
565 | |
566 | impl MetaList { |
567 | /// See [`Attribute::parse_args`]. |
568 | #[cfg (feature = "parsing" )] |
569 | #[cfg_attr (doc_cfg, doc(cfg(feature = "parsing" )))] |
570 | pub fn parse_args<T: Parse>(&self) -> Result<T> { |
571 | self.parse_args_with(T::parse) |
572 | } |
573 | |
574 | /// See [`Attribute::parse_args_with`]. |
575 | #[cfg (feature = "parsing" )] |
576 | #[cfg_attr (doc_cfg, doc(cfg(feature = "parsing" )))] |
577 | pub fn parse_args_with<F: Parser>(&self, parser: F) -> Result<F::Output> { |
578 | let scope = self.delimiter.span().close(); |
579 | crate::parse::parse_scoped(parser, scope, self.tokens.clone()) |
580 | } |
581 | |
582 | /// See [`Attribute::parse_nested_meta`]. |
583 | #[cfg (feature = "parsing" )] |
584 | #[cfg_attr (doc_cfg, doc(cfg(feature = "parsing" )))] |
585 | pub fn parse_nested_meta( |
586 | &self, |
587 | logic: impl FnMut(ParseNestedMeta) -> Result<()>, |
588 | ) -> Result<()> { |
589 | self.parse_args_with(meta::parser(logic)) |
590 | } |
591 | } |
592 | |
593 | #[cfg (feature = "printing" )] |
594 | pub(crate) trait FilterAttrs<'a> { |
595 | type Ret: Iterator<Item = &'a Attribute>; |
596 | |
597 | fn outer(self) -> Self::Ret; |
598 | #[cfg (feature = "full" )] |
599 | fn inner(self) -> Self::Ret; |
600 | } |
601 | |
602 | #[cfg (feature = "printing" )] |
603 | impl<'a> FilterAttrs<'a> for &'a [Attribute] { |
604 | type Ret = iter::Filter<slice::Iter<'a, Attribute>, fn(&&Attribute) -> bool>; |
605 | |
606 | fn outer(self) -> Self::Ret { |
607 | fn is_outer(attr: &&Attribute) -> bool { |
608 | match attr.style { |
609 | AttrStyle::Outer => true, |
610 | AttrStyle::Inner(_) => false, |
611 | } |
612 | } |
613 | self.iter().filter(is_outer) |
614 | } |
615 | |
616 | #[cfg (feature = "full" )] |
617 | fn inner(self) -> Self::Ret { |
618 | fn is_inner(attr: &&Attribute) -> bool { |
619 | match attr.style { |
620 | AttrStyle::Inner(_) => true, |
621 | AttrStyle::Outer => false, |
622 | } |
623 | } |
624 | self.iter().filter(is_inner) |
625 | } |
626 | } |
627 | |
628 | #[cfg (feature = "parsing" )] |
629 | pub(crate) mod parsing { |
630 | use crate::attr::{AttrStyle, Attribute, Meta, MetaList, MetaNameValue}; |
631 | use crate::error::Result; |
632 | use crate::expr::{Expr, ExprLit}; |
633 | use crate::lit::Lit; |
634 | use crate::parse::discouraged::Speculative as _; |
635 | use crate::parse::{Parse, ParseStream}; |
636 | use crate::path::Path; |
637 | use crate::{mac, token}; |
638 | use std::fmt::{self, Display}; |
639 | |
640 | pub(crate) fn parse_inner(input: ParseStream, attrs: &mut Vec<Attribute>) -> Result<()> { |
641 | while input.peek(Token![#]) && input.peek2(Token![!]) { |
642 | attrs.push(input.call(single_parse_inner)?); |
643 | } |
644 | Ok(()) |
645 | } |
646 | |
647 | pub(crate) fn single_parse_inner(input: ParseStream) -> Result<Attribute> { |
648 | let content; |
649 | Ok(Attribute { |
650 | pound_token: input.parse()?, |
651 | style: AttrStyle::Inner(input.parse()?), |
652 | bracket_token: bracketed!(content in input), |
653 | meta: content.parse()?, |
654 | }) |
655 | } |
656 | |
657 | pub(crate) fn single_parse_outer(input: ParseStream) -> Result<Attribute> { |
658 | let content; |
659 | Ok(Attribute { |
660 | pound_token: input.parse()?, |
661 | style: AttrStyle::Outer, |
662 | bracket_token: bracketed!(content in input), |
663 | meta: content.parse()?, |
664 | }) |
665 | } |
666 | |
667 | #[cfg_attr (doc_cfg, doc(cfg(feature = "parsing" )))] |
668 | impl Parse for Meta { |
669 | fn parse(input: ParseStream) -> Result<Self> { |
670 | let path = input.call(Path::parse_mod_style)?; |
671 | parse_meta_after_path(path, input) |
672 | } |
673 | } |
674 | |
675 | #[cfg_attr (doc_cfg, doc(cfg(feature = "parsing" )))] |
676 | impl Parse for MetaList { |
677 | fn parse(input: ParseStream) -> Result<Self> { |
678 | let path = input.call(Path::parse_mod_style)?; |
679 | parse_meta_list_after_path(path, input) |
680 | } |
681 | } |
682 | |
683 | #[cfg_attr (doc_cfg, doc(cfg(feature = "parsing" )))] |
684 | impl Parse for MetaNameValue { |
685 | fn parse(input: ParseStream) -> Result<Self> { |
686 | let path = input.call(Path::parse_mod_style)?; |
687 | parse_meta_name_value_after_path(path, input) |
688 | } |
689 | } |
690 | |
691 | pub(crate) fn parse_meta_after_path(path: Path, input: ParseStream) -> Result<Meta> { |
692 | if input.peek(token::Paren) || input.peek(token::Bracket) || input.peek(token::Brace) { |
693 | parse_meta_list_after_path(path, input).map(Meta::List) |
694 | } else if input.peek(Token![=]) { |
695 | parse_meta_name_value_after_path(path, input).map(Meta::NameValue) |
696 | } else { |
697 | Ok(Meta::Path(path)) |
698 | } |
699 | } |
700 | |
701 | fn parse_meta_list_after_path(path: Path, input: ParseStream) -> Result<MetaList> { |
702 | let (delimiter, tokens) = mac::parse_delimiter(input)?; |
703 | Ok(MetaList { |
704 | path, |
705 | delimiter, |
706 | tokens, |
707 | }) |
708 | } |
709 | |
710 | fn parse_meta_name_value_after_path(path: Path, input: ParseStream) -> Result<MetaNameValue> { |
711 | let eq_token: Token![=] = input.parse()?; |
712 | let ahead = input.fork(); |
713 | let lit: Option<Lit> = ahead.parse()?; |
714 | let value = if let (Some(lit), true) = (lit, ahead.is_empty()) { |
715 | input.advance_to(&ahead); |
716 | Expr::Lit(ExprLit { |
717 | attrs: Vec::new(), |
718 | lit, |
719 | }) |
720 | } else if input.peek(Token![#]) && input.peek2(token::Bracket) { |
721 | return Err(input.error("unexpected attribute inside of attribute" )); |
722 | } else { |
723 | input.parse()? |
724 | }; |
725 | Ok(MetaNameValue { |
726 | path, |
727 | eq_token, |
728 | value, |
729 | }) |
730 | } |
731 | |
732 | pub(super) struct DisplayAttrStyle<'a>(pub &'a AttrStyle); |
733 | |
734 | impl<'a> Display for DisplayAttrStyle<'a> { |
735 | fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { |
736 | formatter.write_str(match self.0 { |
737 | AttrStyle::Outer => "#" , |
738 | AttrStyle::Inner(_) => "#!" , |
739 | }) |
740 | } |
741 | } |
742 | |
743 | pub(super) struct DisplayPath<'a>(pub &'a Path); |
744 | |
745 | impl<'a> Display for DisplayPath<'a> { |
746 | fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { |
747 | for (i, segment) in self.0.segments.iter().enumerate() { |
748 | if i > 0 || self.0.leading_colon.is_some() { |
749 | formatter.write_str("::" )?; |
750 | } |
751 | write!(formatter, " {}" , segment.ident)?; |
752 | } |
753 | Ok(()) |
754 | } |
755 | } |
756 | } |
757 | |
758 | #[cfg (feature = "printing" )] |
759 | mod printing { |
760 | use crate::attr::{AttrStyle, Attribute, MetaList, MetaNameValue}; |
761 | use proc_macro2::TokenStream; |
762 | use quote::ToTokens; |
763 | |
764 | #[cfg_attr (doc_cfg, doc(cfg(feature = "printing" )))] |
765 | impl ToTokens for Attribute { |
766 | fn to_tokens(&self, tokens: &mut TokenStream) { |
767 | self.pound_token.to_tokens(tokens); |
768 | if let AttrStyle::Inner(b) = &self.style { |
769 | b.to_tokens(tokens); |
770 | } |
771 | self.bracket_token.surround(tokens, |tokens| { |
772 | self.meta.to_tokens(tokens); |
773 | }); |
774 | } |
775 | } |
776 | |
777 | #[cfg_attr (doc_cfg, doc(cfg(feature = "printing" )))] |
778 | impl ToTokens for MetaList { |
779 | fn to_tokens(&self, tokens: &mut TokenStream) { |
780 | self.path.to_tokens(tokens); |
781 | self.delimiter.surround(tokens, self.tokens.clone()); |
782 | } |
783 | } |
784 | |
785 | #[cfg_attr (doc_cfg, doc(cfg(feature = "printing" )))] |
786 | impl ToTokens for MetaNameValue { |
787 | fn to_tokens(&self, tokens: &mut TokenStream) { |
788 | self.path.to_tokens(tokens); |
789 | self.eq_token.to_tokens(tokens); |
790 | self.value.to_tokens(tokens); |
791 | } |
792 | } |
793 | } |
794 | |