1use super::*;
2use punctuated::Punctuated;
3
4use std::iter;
5
6use proc_macro2::TokenStream;
7#[cfg(not(feature = "parsing"))]
8use proc_macro2::{Delimiter, Spacing, TokenTree};
9
10#[cfg(feature = "parsing")]
11use parse::{ParseStream, Result};
12#[cfg(feature = "extra-traits")]
13use std::hash::{Hash, Hasher};
14#[cfg(feature = "extra-traits")]
15use tt::TokenStreamHelper;
16
17ast_struct! {
18 pub struct Attribute #manual_extra_traits {
99 pub pound_token: Token![#],
100 pub style: AttrStyle,
101 pub bracket_token: token::Bracket,
102 pub path: Path,
103 pub tts: TokenStream,
104 }
105}
106
107#[cfg(feature = "extra-traits")]
108impl Eq for Attribute {}
109
110#[cfg(feature = "extra-traits")]
111impl PartialEq for Attribute {
112 fn eq(&self, other: &Self) -> bool {
113 self.style == other.style
114 && self.pound_token == other.pound_token
115 && self.bracket_token == other.bracket_token
116 && self.path == other.path
117 && TokenStreamHelper(&self.tts) == TokenStreamHelper(&other.tts)
118 }
119}
120
121#[cfg(feature = "extra-traits")]
122impl Hash for Attribute {
123 fn hash<H>(&self, state: &mut H)
124 where
125 H: Hasher,
126 {
127 self.style.hash(state);
128 self.pound_token.hash(state);
129 self.bracket_token.hash(state);
130 self.path.hash(state);
131 TokenStreamHelper(&self.tts).hash(state);
132 }
133}
134
135impl Attribute {
136 #[doc(hidden)]
141 pub fn interpret_meta(&self) -> Option<Meta> {
142 #[cfg(feature = "parsing")]
143 {
144 self.parse_meta().ok()
145 }
146
147 #[cfg(not(feature = "parsing"))]
148 {
149 let name = if self.path.segments.len() == 1 {
150 &self.path.segments.first().unwrap().value().ident
151 } else {
152 return None;
153 };
154
155 if self.tts.is_empty() {
156 return Some(Meta::Word(name.clone()));
157 }
158
159 let tts = self.tts.clone().into_iter().collect::<Vec<_>>();
160
161 if tts.len() == 1 {
162 if let Some(meta) = Attribute::extract_meta_list(name.clone(), &tts[0]) {
163 return Some(meta);
164 }
165 }
166
167 if tts.len() == 2 {
168 if let Some(meta) = Attribute::extract_name_value(name.clone(), &tts[0], &tts[1]) {
169 return Some(meta);
170 }
171 }
172
173 None
174 }
175 }
176
177 #[cfg(feature = "parsing")]
180 pub fn parse_meta(&self) -> Result<Meta> {
181 if let Some(ref colon) = self.path.leading_colon {
182 return Err(Error::new(colon.spans[0], "expected meta identifier"));
183 }
184
185 let first_segment = self
186 .path
187 .segments
188 .first()
189 .expect("paths have at least one segment");
190 if let Some(colon) = first_segment.punct() {
191 return Err(Error::new(colon.spans[0], "expected meta value"));
192 }
193 let ident = first_segment.value().ident.clone();
194
195 let parser = |input: ParseStream| parsing::parse_meta_after_ident(ident, input);
196 parse::Parser::parse2(parser, self.tts.clone())
197 }
198
199 #[cfg(feature = "parsing")]
204 pub fn parse_outer(input: ParseStream) -> Result<Vec<Self>> {
205 let mut attrs = Vec::new();
206 while input.peek(Token![#]) {
207 attrs.push(input.call(parsing::single_parse_outer)?);
208 }
209 Ok(attrs)
210 }
211
212 #[cfg(feature = "parsing")]
217 pub fn parse_inner(input: ParseStream) -> Result<Vec<Self>> {
218 let mut attrs = Vec::new();
219 while input.peek(Token![#]) && input.peek2(Token![!]) {
220 attrs.push(input.call(parsing::single_parse_inner)?);
221 }
222 Ok(attrs)
223 }
224
225 #[cfg(not(feature = "parsing"))]
226 fn extract_meta_list(ident: Ident, tt: &TokenTree) -> Option<Meta> {
227 let g = match *tt {
228 TokenTree::Group(ref g) => g,
229 _ => return None,
230 };
231 if g.delimiter() != Delimiter::Parenthesis {
232 return None;
233 }
234 let tokens = g.stream().clone().into_iter().collect::<Vec<_>>();
235 let nested = match list_of_nested_meta_items_from_tokens(&tokens) {
236 Some(n) => n,
237 None => return None,
238 };
239 Some(Meta::List(MetaList {
240 paren_token: token::Paren(g.span()),
241 ident: ident,
242 nested: nested,
243 }))
244 }
245
246 #[cfg(not(feature = "parsing"))]
247 fn extract_name_value(ident: Ident, a: &TokenTree, b: &TokenTree) -> Option<Meta> {
248 let a = match *a {
249 TokenTree::Punct(ref o) => o,
250 _ => return None,
251 };
252 if a.spacing() != Spacing::Alone {
253 return None;
254 }
255 if a.as_char() != '=' {
256 return None;
257 }
258
259 match *b {
260 TokenTree::Literal(ref l) if !l.to_string().starts_with('/') => {
261 Some(Meta::NameValue(MetaNameValue {
262 ident: ident,
263 eq_token: Token]),
264 lit: Lit::new(l.clone()),
265 }))
266 }
267 TokenTree::Ident(ref v) => match &v.to_string()[..] {
268 v @ "true" | v @ "false" => Some(Meta::NameValue(MetaNameValue {
269 ident: ident,
270 eq_token: Token]),
271 lit: Lit::Bool(LitBool {
272 value: v == "true",
273 span: b.span(),
274 }),
275 })),
276 _ => None,
277 },
278 _ => None,
279 }
280 }
281}
282
283#[cfg(not(feature = "parsing"))]
284fn nested_meta_item_from_tokens(tts: &[TokenTree]) -> Option<(NestedMeta, &[TokenTree])> {
285 assert!(!tts.is_empty());
286
287 match tts[0] {
288 TokenTree::Literal(ref lit) => {
289 if lit.to_string().starts_with('/') {
290 None
291 } else {
292 let lit = Lit::new(lit.clone());
293 Some((NestedMeta::Literal(lit), &tts[1..]))
294 }
295 }
296
297 TokenTree::Ident(ref ident) => {
298 if tts.len() >= 3 {
299 if let Some(meta) = Attribute::extract_name_value(ident.clone(), &tts[1], &tts[2]) {
300 return Some((NestedMeta::Meta(meta), &tts[3..]));
301 }
302 }
303
304 if tts.len() >= 2 {
305 if let Some(meta) = Attribute::extract_meta_list(ident.clone(), &tts[1]) {
306 return Some((NestedMeta::Meta(meta), &tts[2..]));
307 }
308 }
309
310 let nested_meta = if ident == "true" || ident == "false" {
311 NestedMeta::Literal(Lit::Bool(LitBool {
312 value: ident == "true",
313 span: ident.span(),
314 }))
315 } else {
316 NestedMeta::Meta(Meta::Word(ident.clone()))
317 };
318 Some((nested_meta, &tts[1..]))
319 }
320
321 _ => None,
322 }
323}
324
325#[cfg(not(feature = "parsing"))]
326fn list_of_nested_meta_items_from_tokens(
327 mut tts: &[TokenTree],
328) -> Option<Punctuated<NestedMeta, Token![,]>> {
329 let mut nested_meta_items = Punctuated::new();
330 let mut first = true;
331
332 while !tts.is_empty() {
333 let prev_comma = if first {
334 first = false;
335 None
336 } else if let TokenTree::Punct(ref op) = tts[0] {
337 if op.spacing() != Spacing::Alone {
338 return None;
339 }
340 if op.as_char() != ',' {
341 return None;
342 }
343 let tok = Token]);
344 tts = &tts[1..];
345 if tts.is_empty() {
346 break;
347 }
348 Some(tok)
349 } else {
350 return None;
351 };
352 let (nested, rest) = match nested_meta_item_from_tokens(tts) {
353 Some(pair) => pair,
354 None => return None,
355 };
356 if let Some(comma) = prev_comma {
357 nested_meta_items.push_punct(comma);
358 }
359 nested_meta_items.push_value(nested);
360 tts = rest;
361 }
362
363 Some(nested_meta_items)
364}
365
366ast_enum! {
367 #[cfg_attr(feature = "clone-impls", derive(Copy))]
385 pub enum AttrStyle {
386 Outer,
387 Inner(Token![!]),
388 }
389}
390
391ast_enum_of_structs! {
392 pub enum Meta {
416 pub Word(Ident),
417 pub List(MetaList {
422 pub ident: Ident,
423 pub paren_token: token::Paren,
424 pub nested: Punctuated<NestedMeta, Token![,]>,
425 }),
426 pub NameValue(MetaNameValue {
431 pub ident: Ident,
432 pub eq_token: Token![=],
433 pub lit: Lit,
434 }),
435 }
436}
437
438impl Meta {
439 pub fn name(&self) -> Ident {
444 match *self {
445 Meta::Word(ref meta) => meta.clone(),
446 Meta::List(ref meta) => meta.ident.clone(),
447 Meta::NameValue(ref meta) => meta.ident.clone(),
448 }
449 }
450}
451
452ast_enum_of_structs! {
453 pub enum NestedMeta {
458 pub Meta(Meta),
461
462 pub Literal(Lit),
464 }
465}
466
467pub type AttributeArgs = Vec<NestedMeta>;
503
504pub trait FilterAttrs<'a> {
505 type Ret: Iterator<Item = &'a Attribute>;
506
507 fn outer(self) -> Self::Ret;
508 fn inner(self) -> Self::Ret;
509}
510
511impl<'a, T> FilterAttrs<'a> for T
512where
513 T: IntoIterator<Item = &'a Attribute>,
514{
515 type Ret = iter::Filter<T::IntoIter, fn(&&Attribute) -> bool>;
516
517 fn outer(self) -> Self::Ret {
518 #[cfg_attr(feature = "cargo-clippy", allow(trivially_copy_pass_by_ref))]
519 fn is_outer(attr: &&Attribute) -> bool {
520 match attr.style {
521 AttrStyle::Outer => true,
522 _ => false,
523 }
524 }
525 self.into_iter().filter(is_outer)
526 }
527
528 fn inner(self) -> Self::Ret {
529 #[cfg_attr(feature = "cargo-clippy", allow(trivially_copy_pass_by_ref))]
530 fn is_inner(attr: &&Attribute) -> bool {
531 match attr.style {
532 AttrStyle::Inner(_) => true,
533 _ => false,
534 }
535 }
536 self.into_iter().filter(is_inner)
537 }
538}
539
540#[cfg(feature = "parsing")]
541pub mod parsing {
542 use super::*;
543
544 use ext::IdentExt;
545 use parse::{Parse, ParseStream, Result};
546 #[cfg(feature = "full")]
547 use private;
548
549 pub fn single_parse_inner(input: ParseStream) -> Result<Attribute> {
550 let content;
551 Ok(Attribute {
552 pound_token: input.parse()?,
553 style: AttrStyle::Inner(input.parse()?),
554 bracket_token: bracketed!(content in input),
555 path: content.call(Path::parse_mod_style)?,
556 tts: content.parse()?,
557 })
558 }
559
560 pub fn single_parse_outer(input: ParseStream) -> Result<Attribute> {
561 let content;
562 Ok(Attribute {
563 pound_token: input.parse()?,
564 style: AttrStyle::Outer,
565 bracket_token: bracketed!(content in input),
566 path: content.call(Path::parse_mod_style)?,
567 tts: content.parse()?,
568 })
569 }
570
571 #[cfg(feature = "full")]
572 impl private {
573 pub fn attrs(outer: Vec<Attribute>, inner: Vec<Attribute>) -> Vec<Attribute> {
574 let mut attrs = outer;
575 attrs.extend(inner);
576 attrs
577 }
578 }
579
580 impl Parse for Meta {
581 fn parse(input: ParseStream) -> Result<Self> {
582 let ident = input.call(Ident::parse_any)?;
583 parse_meta_after_ident(ident, input)
584 }
585 }
586
587 impl Parse for MetaList {
588 fn parse(input: ParseStream) -> Result<Self> {
589 let ident = input.call(Ident::parse_any)?;
590 parse_meta_list_after_ident(ident, input)
591 }
592 }
593
594 impl Parse for MetaNameValue {
595 fn parse(input: ParseStream) -> Result<Self> {
596 let ident = input.call(Ident::parse_any)?;
597 parse_meta_name_value_after_ident(ident, input)
598 }
599 }
600
601 impl Parse for NestedMeta {
602 fn parse(input: ParseStream) -> Result<Self> {
603 let ahead = input.fork();
604
605 if ahead.peek(Lit) && !(ahead.peek(LitBool) && ahead.peek2(Token![=])) {
606 input.parse().map(NestedMeta::Literal)
607 } else if ahead.call(Ident::parse_any).is_ok() {
608 input.parse().map(NestedMeta::Meta)
609 } else {
610 Err(input.error("expected identifier or literal"))
611 }
612 }
613 }
614
615 pub fn parse_meta_after_ident(ident: Ident, input: ParseStream) -> Result<Meta> {
616 if input.peek(token::Paren) {
617 parse_meta_list_after_ident(ident, input).map(Meta::List)
618 } else if input.peek(Token![=]) {
619 parse_meta_name_value_after_ident(ident, input).map(Meta::NameValue)
620 } else {
621 Ok(Meta::Word(ident))
622 }
623 }
624
625 fn parse_meta_list_after_ident(ident: Ident, input: ParseStream) -> Result<MetaList> {
626 let content;
627 Ok(MetaList {
628 ident: ident,
629 paren_token: parenthesized!(content in input),
630 nested: content.parse_terminated(NestedMeta::parse)?,
631 })
632 }
633
634 fn parse_meta_name_value_after_ident(
635 ident: Ident,
636 input: ParseStream,
637 ) -> Result<MetaNameValue> {
638 Ok(MetaNameValue {
639 ident: ident,
640 eq_token: input.parse()?,
641 lit: input.parse()?,
642 })
643 }
644}
645
646#[cfg(feature = "printing")]
647mod printing {
648 use super::*;
649 use proc_macro2::TokenStream;
650 use quote::ToTokens;
651
652 impl ToTokens for Attribute {
653 fn to_tokens(&self, tokens: &mut TokenStream) {
654 self.pound_token.to_tokens(tokens);
655 if let AttrStyle::Inner(ref b) = self.style {
656 b.to_tokens(tokens);
657 }
658 self.bracket_token.surround(tokens, |tokens| {
659 self.path.to_tokens(tokens);
660 self.tts.to_tokens(tokens);
661 });
662 }
663 }
664
665 impl ToTokens for MetaList {
666 fn to_tokens(&self, tokens: &mut TokenStream) {
667 self.ident.to_tokens(tokens);
668 self.paren_token.surround(tokens, |tokens| {
669 self.nested.to_tokens(tokens);
670 })
671 }
672 }
673
674 impl ToTokens for MetaNameValue {
675 fn to_tokens(&self, tokens: &mut TokenStream) {
676 self.ident.to_tokens(tokens);
677 self.eq_token.to_tokens(tokens);
678 self.lit.to_tokens(tokens);
679 }
680 }
681}