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