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