]>
Commit | Line | Data |
---|---|---|
e74abb32 XL |
1 | use super::*; |
2 | use crate::punctuated::Punctuated; | |
3 | ||
4 | use std::iter; | |
5 | ||
6 | use proc_macro2::TokenStream; | |
7 | ||
8 | #[cfg(feature = "parsing")] | |
9 | use crate::parse::{Parse, ParseBuffer, ParseStream, Parser, Result}; | |
10 | #[cfg(feature = "parsing")] | |
11 | use crate::punctuated::Pair; | |
12 | #[cfg(feature = "extra-traits")] | |
13 | use crate::tt::TokenStreamHelper; | |
14 | #[cfg(feature = "extra-traits")] | |
15 | use std::hash::{Hash, Hasher}; | |
16 | ||
17 | ast_struct! { | |
18 | /// An attribute like `#[repr(transparent)]`. | |
19 | /// | |
20 | /// *This type is available if Syn is built with the `"derive"` or `"full"` | |
21 | /// feature.* | |
22 | /// | |
23 | /// <br> | |
24 | /// | |
25 | /// # Syntax | |
26 | /// | |
27 | /// Rust has six types of attributes. | |
28 | /// | |
29 | /// - Outer attributes like `#[repr(transparent)]`. These appear outside or | |
30 | /// in front of the item they describe. | |
31 | /// - Inner attributes like `#![feature(proc_macro)]`. These appear inside | |
32 | /// of the item they describe, usually a module. | |
33 | /// - Outer doc comments like `/// # Example`. | |
34 | /// - Inner doc comments like `//! Please file an issue`. | |
35 | /// - Outer block comments `/** # Example */`. | |
36 | /// - Inner block comments `/*! Please file an issue */`. | |
37 | /// | |
38 | /// The `style` field of type `AttrStyle` distinguishes whether an attribute | |
39 | /// is outer or inner. Doc comments and block comments are promoted to | |
40 | /// attributes, as this is how they are processed by the compiler and by | |
41 | /// `macro_rules!` macros. | |
42 | /// | |
43 | /// The `path` field gives the possibly colon-delimited path against which | |
44 | /// the attribute is resolved. It is equal to `"doc"` for desugared doc | |
45 | /// comments. The `tokens` field contains the rest of the attribute body as | |
46 | /// tokens. | |
47 | /// | |
48 | /// ```text | |
49 | /// #[derive(Copy)] #[crate::precondition x < 5] | |
50 | /// ^^^^^^~~~~~~ ^^^^^^^^^^^^^^^^^^^ ~~~~~ | |
51 | /// path tokens path tokens | |
52 | /// ``` | |
53 | /// | |
54 | /// <br> | |
55 | /// | |
56 | /// # Parsing from tokens to Attribute | |
57 | /// | |
58 | /// This type does not implement the [`Parse`] trait and thus cannot be | |
59 | /// parsed directly by [`ParseStream::parse`]. Instead use | |
60 | /// [`ParseStream::call`] with one of the two parser functions | |
61 | /// [`Attribute::parse_outer`] or [`Attribute::parse_inner`] depending on | |
62 | /// which you intend to parse. | |
63 | /// | |
64 | /// [`Parse`]: parse::Parse | |
65 | /// [`ParseStream::parse`]: parse::ParseBuffer::parse | |
66 | /// [`ParseStream::call`]: parse::ParseBuffer::call | |
67 | /// | |
68 | /// ``` | |
69 | /// use syn::{Attribute, Ident, Result, Token}; | |
70 | /// use syn::parse::{Parse, ParseStream}; | |
71 | /// | |
72 | /// // Parses a unit struct with attributes. | |
73 | /// // | |
74 | /// // #[path = "s.tmpl"] | |
75 | /// // struct S; | |
76 | /// struct UnitStruct { | |
77 | /// attrs: Vec<Attribute>, | |
78 | /// struct_token: Token![struct], | |
79 | /// name: Ident, | |
80 | /// semi_token: Token![;], | |
81 | /// } | |
82 | /// | |
83 | /// impl Parse for UnitStruct { | |
84 | /// fn parse(input: ParseStream) -> Result<Self> { | |
85 | /// Ok(UnitStruct { | |
86 | /// attrs: input.call(Attribute::parse_outer)?, | |
87 | /// struct_token: input.parse()?, | |
88 | /// name: input.parse()?, | |
89 | /// semi_token: input.parse()?, | |
90 | /// }) | |
91 | /// } | |
92 | /// } | |
93 | /// ``` | |
94 | /// | |
95 | /// <p><br></p> | |
96 | /// | |
97 | /// # Parsing from Attribute to structured arguments | |
98 | /// | |
99 | /// The grammar of attributes in Rust is very flexible, which makes the | |
100 | /// syntax tree not that useful on its own. In particular, arguments of the | |
101 | /// attribute are held in an arbitrary `tokens: TokenStream`. Macros are | |
102 | /// expected to check the `path` of the attribute, decide whether they | |
103 | /// recognize it, and then parse the remaining tokens according to whatever | |
104 | /// grammar they wish to require for that kind of attribute. | |
105 | /// | |
106 | /// If the attribute you are parsing is expected to conform to the | |
107 | /// conventional structured form of attribute, use [`parse_meta()`] to | |
108 | /// obtain that structured representation. If the attribute follows some | |
109 | /// other grammar of its own, use [`parse_args()`] to parse that into the | |
110 | /// expected data structure. | |
111 | /// | |
112 | /// [`parse_meta()`]: Attribute::parse_meta | |
113 | /// [`parse_args()`]: Attribute::parse_args | |
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")] | |
161 | impl Eq for Attribute {} | |
162 | ||
163 | #[cfg(feature = "extra-traits")] | |
164 | impl 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")] | |
175 | impl 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 | ||
188 | impl 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")] | |
287 | fn 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")] | |
311 | fn 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 | ||
334 | ast_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 | ||
359 | ast_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 | ||
397 | ast_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 | ||
409 | ast_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 | ||
421 | impl 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 | ||
435 | ast_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 | /// ``` | |
485 | pub type AttributeArgs = Vec<NestedMeta>; | |
486 | ||
487 | pub 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 | ||
494 | impl<'a, T> FilterAttrs<'a> for T | |
495 | where | |
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")] | |
522 | pub 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")] | |
648 | mod 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 | } |