1use super::*;
10use punctuated::Punctuated;
11
12use std::iter;
13
14use proc_macro2::{Delimiter, Spacing, TokenNode, TokenStream, TokenTree};
15
16#[cfg(feature = "extra-traits")]
17use std::hash::{Hash, Hasher};
18#[cfg(feature = "extra-traits")]
19use tt::TokenStreamHelper;
20
21ast_struct! {
22 pub struct Attribute #manual_extra_traits {
62 pub pound_token: Token![#],
63 pub style: AttrStyle,
64 pub bracket_token: token::Bracket,
65 pub path: Path,
66 pub tts: TokenStream,
67 pub is_sugared_doc: bool,
68 }
69}
70
71#[cfg(feature = "extra-traits")]
72impl Eq for Attribute {}
73
74#[cfg(feature = "extra-traits")]
75impl PartialEq for Attribute {
76 fn eq(&self, other: &Self) -> bool {
77 self.style == other.style && self.pound_token == other.pound_token
78 && self.bracket_token == other.bracket_token && self.path == other.path
79 && TokenStreamHelper(&self.tts) == TokenStreamHelper(&other.tts)
80 && self.is_sugared_doc == other.is_sugared_doc
81 }
82}
83
84#[cfg(feature = "extra-traits")]
85impl Hash for Attribute {
86 fn hash<H>(&self, state: &mut H)
87 where
88 H: Hasher,
89 {
90 self.style.hash(state);
91 self.pound_token.hash(state);
92 self.bracket_token.hash(state);
93 self.path.hash(state);
94 TokenStreamHelper(&self.tts).hash(state);
95 self.is_sugared_doc.hash(state);
96 }
97}
98
99impl Attribute {
100 pub fn interpret_meta(&self) -> Option<Meta> {
103 let name = if self.path.segments.len() == 1 {
104 &self.path.segments.first().unwrap().value().ident
105 } else {
106 return None;
107 };
108
109 if self.tts.is_empty() {
110 return Some(Meta::Word(*name));
111 }
112
113 let tts = self.tts.clone().into_iter().collect::<Vec<_>>();
114
115 if tts.len() == 1 {
116 if let TokenNode::Group(Delimiter::Parenthesis, ref ts) = tts[0].kind {
117 let tokens = ts.clone().into_iter().collect::<Vec<_>>();
118 if let Some(nested_meta_items) = list_of_nested_meta_items_from_tokens(&tokens) {
119 return Some(Meta::List(MetaList {
120 paren_token: token::Paren(tts[0].span),
121 ident: *name,
122 nested: nested_meta_items,
123 }));
124 }
125 }
126 }
127
128 if tts.len() == 2 {
129 if let TokenNode::Op('=', Spacing::Alone) = tts[0].kind {
130 if let TokenNode::Literal(ref lit) = tts[1].kind {
131 if !lit.to_string().starts_with('/') {
132 return Some(Meta::NameValue(MetaNameValue {
133 ident: *name,
134 eq_token: Token,
135 lit: Lit::new(lit.clone(), tts[1].span),
136 }));
137 }
138 } else if let TokenNode::Term(ref term) = tts[1].kind {
139 match term.as_str() {
140 v @ "true" | v @ "false" => {
141 return Some(Meta::NameValue(MetaNameValue {
142 ident: *name,
143 eq_token: Token,
144 lit: Lit::Bool(LitBool { value: v == "true", span: tts[1].span }),
145 }));
146 },
147 _ => {}
148 }
149 }
150 }
151 }
152
153 None
154 }
155}
156
157fn nested_meta_item_from_tokens(tts: &[TokenTree]) -> Option<(NestedMeta, &[TokenTree])> {
158 assert!(!tts.is_empty());
159
160 match tts[0].kind {
161 TokenNode::Literal(ref lit) => {
162 if lit.to_string().starts_with('/') {
163 None
164 } else {
165 let lit = Lit::new(lit.clone(), tts[0].span);
166 Some((NestedMeta::Literal(lit), &tts[1..]))
167 }
168 }
169
170 TokenNode::Term(sym) => {
171 let ident = Ident::new(sym.as_str(), tts[0].span);
172 if tts.len() >= 3 {
173 if let TokenNode::Op('=', Spacing::Alone) = tts[1].kind {
174 if let TokenNode::Literal(ref lit) = tts[2].kind {
175 if !lit.to_string().starts_with('/') {
176 let pair = MetaNameValue {
177 ident: Ident::new(sym.as_str(), tts[0].span),
178 eq_token: Token,
179 lit: Lit::new(lit.clone(), tts[2].span),
180 };
181 return Some((Meta::NameValue(pair).into(), &tts[3..]));
182 }
183 } else if let TokenNode::Term(ref term) = tts[2].kind {
184 match term.as_str() {
185 v @ "true" | v @ "false" => {
186 let pair = MetaNameValue {
187 ident: Ident::new(sym.as_str(), tts[0].span),
188 eq_token: Token,
189 lit: Lit::Bool(LitBool { value: v == "true", span: tts[2].span }),
190 };
191 return Some((Meta::NameValue(pair).into(), &tts[3..]));
192 },
193 _ => {}
194 }
195 }
196 }
197 }
198
199 if tts.len() >= 2 {
200 if let TokenNode::Group(Delimiter::Parenthesis, ref inner_tts) = tts[1].kind {
201 let inner_tts = inner_tts.clone().into_iter().collect::<Vec<_>>();
202 return match list_of_nested_meta_items_from_tokens(&inner_tts) {
203 Some(nested_meta_items) => {
204 let list = MetaList {
205 ident: ident,
206 paren_token: token::Paren(tts[1].span),
207 nested: nested_meta_items,
208 };
209 Some((Meta::List(list).into(), &tts[2..]))
210 }
211
212 None => None,
213 };
214 }
215 }
216
217 Some((Meta::Word(ident).into(), &tts[1..]))
218 }
219
220 _ => None,
221 }
222}
223
224fn list_of_nested_meta_items_from_tokens(
225 mut tts: &[TokenTree],
226) -> Option<Punctuated<NestedMeta, Token![,]>> {
227 let mut nested_meta_items = Punctuated::new();
228 let mut first = true;
229
230 while !tts.is_empty() {
231 let prev_comma = if first {
232 first = false;
233 None
234 } else if let TokenNode::Op(',', Spacing::Alone) = tts[0].kind {
235 let tok = Token;
236 tts = &tts[1..];
237 if tts.is_empty() {
238 break;
239 }
240 Some(tok)
241 } else {
242 return None;
243 };
244 let (nested, rest) = match nested_meta_item_from_tokens(tts) {
245 Some(pair) => pair,
246 None => return None,
247 };
248 if let Some(comma) = prev_comma {
249 nested_meta_items.push_punct(comma);
250 }
251 nested_meta_items.push_value(nested);
252 tts = rest;
253 }
254
255 Some(nested_meta_items)
256}
257
258ast_enum! {
259 #[cfg_attr(feature = "clone-impls", derive(Copy))]
277 pub enum AttrStyle {
278 Outer,
279 Inner(Token![!]),
280 }
281}
282
283ast_enum_of_structs! {
284 pub enum Meta {
308 pub Word(Ident),
309 pub List(MetaList {
314 pub ident: Ident,
315 pub paren_token: token::Paren,
316 pub nested: Punctuated<NestedMeta, Token![,]>,
317 }),
318 pub NameValue(MetaNameValue {
323 pub ident: Ident,
324 pub eq_token: Token![=],
325 pub lit: Lit,
326 }),
327 }
328}
329
330impl Meta {
331 pub fn name(&self) -> Ident {
336 match *self {
337 Meta::Word(ref meta) => *meta,
338 Meta::List(ref meta) => meta.ident,
339 Meta::NameValue(ref meta) => meta.ident,
340 }
341 }
342}
343
344ast_enum_of_structs! {
345 pub enum NestedMeta {
350 pub Meta(Meta),
353
354 pub Literal(Lit),
356 }
357}
358
359pub trait FilterAttrs<'a> {
360 type Ret: Iterator<Item = &'a Attribute>;
361
362 fn outer(self) -> Self::Ret;
363 fn inner(self) -> Self::Ret;
364}
365
366impl<'a, T> FilterAttrs<'a> for T
367where
368 T: IntoIterator<Item = &'a Attribute>,
369{
370 type Ret = iter::Filter<T::IntoIter, fn(&&Attribute) -> bool>;
371
372 fn outer(self) -> Self::Ret {
373 fn is_outer(attr: &&Attribute) -> bool {
374 match attr.style {
375 AttrStyle::Outer => true,
376 _ => false,
377 }
378 }
379 self.into_iter().filter(is_outer)
380 }
381
382 fn inner(self) -> Self::Ret {
383 fn is_inner(attr: &&Attribute) -> bool {
384 match attr.style {
385 AttrStyle::Inner(_) => true,
386 _ => false,
387 }
388 }
389 self.into_iter().filter(is_inner)
390 }
391}
392
393#[cfg(feature = "parsing")]
394pub mod parsing {
395 use super::*;
396 use buffer::Cursor;
397 use parse_error;
398 use synom::PResult;
399 use proc_macro2::{Literal, Spacing, Span, TokenNode, TokenTree};
400
401 fn eq(span: Span) -> TokenTree {
402 TokenTree {
403 span: span,
404 kind: TokenNode::Op('=', Spacing::Alone),
405 }
406 }
407
408 impl Attribute {
409 named!(pub parse_inner -> Self, alt!(
410 do_parse!(
411 pound: punct!(#) >>
412 bang: punct!(!) >>
413 path_and_tts: brackets!(tuple!(
414 call!(Path::parse_mod_style),
415 syn!(TokenStream)
416 )) >>
417 ({
418 let (bracket, (path, tts)) = path_and_tts;
419
420 Attribute {
421 style: AttrStyle::Inner(bang),
422 path: path,
423 tts: tts,
424 is_sugared_doc: false,
425 pound_token: pound,
426 bracket_token: bracket,
427 }
428 })
429 )
430 |
431 map!(
432 call!(lit_doc_comment, Comment::Inner),
433 |lit| {
434 let span = lit.span;
435 Attribute {
436 style: AttrStyle::Inner(<Token![!]>::new(span)),
437 path: Ident::new("doc", span).into(),
438 tts: vec![
439 eq(span),
440 lit,
441 ].into_iter().collect(),
442 is_sugared_doc: true,
443 pound_token: <Token![#]>::new(span),
444 bracket_token: token::Bracket(span),
445 }
446 }
447 )
448 ));
449
450 named!(pub parse_outer -> Self, alt!(
451 do_parse!(
452 pound: punct!(#) >>
453 path_and_tts: brackets!(tuple!(
454 call!(Path::parse_mod_style),
455 syn!(TokenStream)
456 )) >>
457 ({
458 let (bracket, (path, tts)) = path_and_tts;
459
460 Attribute {
461 style: AttrStyle::Outer,
462 path: path,
463 tts: tts,
464 is_sugared_doc: false,
465 pound_token: pound,
466 bracket_token: bracket,
467 }
468 })
469 )
470 |
471 map!(
472 call!(lit_doc_comment, Comment::Outer),
473 |lit| {
474 let span = lit.span;
475 Attribute {
476 style: AttrStyle::Outer,
477 path: Ident::new("doc", span).into(),
478 tts: vec![
479 eq(span),
480 lit,
481 ].into_iter().collect(),
482 is_sugared_doc: true,
483 pound_token: <Token![#]>::new(span),
484 bracket_token: token::Bracket(span),
485 }
486 }
487 )
488 ));
489 }
490
491 enum Comment {
492 Inner,
493 Outer,
494 }
495
496 fn lit_doc_comment(input: Cursor, style: Comment) -> PResult<TokenTree> {
497 match input.literal() {
498 Some((span, lit, rest)) => {
499 let string = lit.to_string();
500 let ok = match style {
501 Comment::Inner => string.starts_with("//!") || string.starts_with("/*!"),
502 Comment::Outer => string.starts_with("///") || string.starts_with("/**"),
503 };
504 if ok {
505 Ok((
506 TokenTree {
507 span: span,
508 kind: TokenNode::Literal(Literal::string(&string)),
509 },
510 rest,
511 ))
512 } else {
513 parse_error()
514 }
515 }
516 _ => parse_error(),
517 }
518 }
519}
520
521#[cfg(feature = "printing")]
522mod printing {
523 use super::*;
524 use quote::{ToTokens, Tokens};
525 use proc_macro2::Literal;
526
527 impl ToTokens for Attribute {
528 fn to_tokens(&self, tokens: &mut Tokens) {
529 if self.is_sugared_doc {
531 if let Some(Meta::NameValue(ref pair)) = self.interpret_meta() {
532 if pair.ident == "doc" {
533 if let Lit::Str(ref comment) = pair.lit {
534 tokens.append(TokenTree {
535 span: comment.span,
536 kind: TokenNode::Literal(Literal::doccomment(&comment.value())),
537 });
538 return;
539 }
540 }
541 }
542 }
543
544 self.pound_token.to_tokens(tokens);
545 if let AttrStyle::Inner(ref b) = self.style {
546 b.to_tokens(tokens);
547 }
548 self.bracket_token.surround(tokens, |tokens| {
549 self.path.to_tokens(tokens);
550 self.tts.to_tokens(tokens);
551 });
552 }
553 }
554
555 impl ToTokens for MetaList {
556 fn to_tokens(&self, tokens: &mut Tokens) {
557 self.ident.to_tokens(tokens);
558 self.paren_token.surround(tokens, |tokens| {
559 self.nested.to_tokens(tokens);
560 })
561 }
562 }
563
564 impl ToTokens for MetaNameValue {
565 fn to_tokens(&self, tokens: &mut Tokens) {
566 self.ident.to_tokens(tokens);
567 self.eq_token.to_tokens(tokens);
568 self.lit.to_tokens(tokens);
569 }
570 }
571}