blob: df51583e0e0d0b9b16ab38820bae4c81652d96aa [file] [log] [blame]
David Tolnayb79ee962016-09-04 09:39:20 -07001use super::*;
Alex Crichtonccbb45d2017-05-23 10:58:24 -07002use delimited::Delimited;
David Tolnayb79ee962016-09-04 09:39:20 -07003
David Tolnay4a51dc72016-10-01 00:40:31 -07004use std::iter;
5
Alex Crichtonccbb45d2017-05-23 10:58:24 -07006use proc_macro2::{self, Delimiter, TokenKind, OpKind};
7
Alex Crichton62a0a592017-05-22 13:58:53 -07008ast_struct! {
9 /// Doc-comments are promoted to attributes that have `is_sugared_doc` = true
10 pub struct Attribute {
11 pub style: AttrStyle,
Alex Crichtonccbb45d2017-05-23 10:58:24 -070012 pub pound_token: tokens::Pound,
13 pub bracket_token: tokens::Bracket,
Arnavion44d2bf32017-04-19 02:47:55 -070014
Alex Crichton62a0a592017-05-22 13:58:53 -070015 /// The path of the attribute.
16 ///
17 /// E.g. `derive` in `#[derive(Copy)]`
18 /// E.g. `crate::precondition` in `#[crate::precondition x < 5]`
19 pub path: Path,
Arnavion44d2bf32017-04-19 02:47:55 -070020
Alex Crichton62a0a592017-05-22 13:58:53 -070021 /// Any tokens after the path.
22 ///
23 /// E.g. `( Copy )` in `#[derive(Copy)]`
24 /// E.g. `x < 5` in `#[crate::precondition x < 5]`
25 pub tts: Vec<TokenTree>,
Arnavion44d2bf32017-04-19 02:47:55 -070026
Alex Crichton62a0a592017-05-22 13:58:53 -070027 pub is_sugared_doc: bool,
28 }
David Tolnayb79ee962016-09-04 09:39:20 -070029}
30
David Tolnay02d77cc2016-10-02 09:52:08 -070031impl Attribute {
Arnavion44d2bf32017-04-19 02:47:55 -070032 /// Parses the tokens after the path as a [`MetaItem`](enum.MetaItem.html) if possible.
Arnavionbf395bf2017-04-15 15:35:22 -070033 pub fn meta_item(&self) -> Option<MetaItem> {
Arnavion95f8a7a2017-04-19 03:29:56 -070034 let name = if self.path.segments.len() == 1 {
Alex Crichtonccbb45d2017-05-23 10:58:24 -070035 &self.path.segments.get(0).item().ident
Arnavion95f8a7a2017-04-19 03:29:56 -070036 } else {
37 return None;
38 };
39
Arnavionbf395bf2017-04-15 15:35:22 -070040 if self.tts.is_empty() {
Arnavion95f8a7a2017-04-19 03:29:56 -070041 return Some(MetaItem::Word(name.clone()));
Arnavionbf395bf2017-04-15 15:35:22 -070042 }
43
44 if self.tts.len() == 1 {
Alex Crichtonccbb45d2017-05-23 10:58:24 -070045 if let TokenKind::Sequence(Delimiter::Parenthesis, ref ts) = self.tts[0].0.kind {
46 let tokens = ts.clone().into_iter().collect::<Vec<_>>();
47 if let Some(nested_meta_items) = list_of_nested_meta_items_from_tokens(&tokens) {
Alex Crichton62a0a592017-05-22 13:58:53 -070048 return Some(MetaItem::List(MetaItemList {
Alex Crichtonccbb45d2017-05-23 10:58:24 -070049 paren_token: tokens::Paren(Span(self.tts[0].0.span)),
Alex Crichton62a0a592017-05-22 13:58:53 -070050 ident: name.clone(),
51 nested: nested_meta_items,
52 }));
Arnavionbf395bf2017-04-15 15:35:22 -070053 }
54 }
55 }
56
57 if self.tts.len() == 2 {
Alex Crichtonccbb45d2017-05-23 10:58:24 -070058 if let TokenKind::Op('=', OpKind::Alone) = self.tts[0].0.kind {
59 if let TokenKind::Literal(ref lit) = self.tts[1].0.kind {
Alex Crichton62a0a592017-05-22 13:58:53 -070060 return Some(MetaItem::NameValue(MetaNameValue {
61 ident: name.clone(),
Alex Crichtonccbb45d2017-05-23 10:58:24 -070062 eq_token: tokens::Eq([Span(self.tts[0].0.span)]),
63 lit: Lit {
64 value: LitKind::Other(lit.clone()),
65 span: Span(self.tts[1].0.span),
66 },
Alex Crichton62a0a592017-05-22 13:58:53 -070067 }));
Arnavionbf395bf2017-04-15 15:35:22 -070068 }
Arnavionbf395bf2017-04-15 15:35:22 -070069 }
70 }
71
72 None
David Tolnay02d77cc2016-10-02 09:52:08 -070073 }
74}
75
Alex Crichtonccbb45d2017-05-23 10:58:24 -070076fn nested_meta_item_from_tokens(tts: &[proc_macro2::TokenTree])
77 -> Option<(NestedMetaItem, &[proc_macro2::TokenTree])>
78{
79 assert!(!tts.is_empty());
80
81 match tts[0].kind {
82 TokenKind::Literal(ref lit) => {
83 let lit = Lit {
84 value: LitKind::Other(lit.clone()),
85 span: Span(tts[0].span),
86 };
87 Some((NestedMetaItem::Literal(lit), &tts[1..]))
88 }
89
90 TokenKind::Word(sym) => {
91 let ident = Ident::new(sym, Span(tts[0].span));
92 if tts.len() >= 3 {
93 if let TokenKind::Op('=', OpKind::Alone) = tts[1].kind {
94 if let TokenKind::Literal(ref lit) = tts[2].kind {
95 let pair = MetaNameValue {
96 ident: Ident::new(sym, Span(tts[0].span)),
97 eq_token: tokens::Eq([Span(tts[1].span)]),
98 lit: Lit {
99 value: LitKind::Other(lit.clone()),
100 span: Span(tts[2].span),
101 },
102 };
103 return Some((MetaItem::NameValue(pair).into(), &tts[3..]));
104 }
105 }
106 }
107
108 if tts.len() >= 2 {
109 if let TokenKind::Sequence(Delimiter::Parenthesis, ref inner_tts) = tts[1].kind {
110 let inner_tts = inner_tts.clone().into_iter().collect::<Vec<_>>();
111 return match list_of_nested_meta_items_from_tokens(&inner_tts) {
112 Some(nested_meta_items) => {
113 let list = MetaItemList {
114 ident: ident,
115 paren_token: tokens::Paren(Span(tts[1].span)),
116 nested: nested_meta_items,
117 };
118 Some((MetaItem::List(list).into(), &tts[2..]))
119 }
120
121 None => None
122 };
123 }
124 }
125
126 Some((MetaItem::Word(ident).into(), &tts[1..]))
127 }
128
129 _ => None
130 }
131}
132
133fn list_of_nested_meta_items_from_tokens(mut tts: &[proc_macro2::TokenTree])
134 -> Option<Delimited<NestedMetaItem, tokens::Comma>>
135{
136 let mut delimited = Delimited::new();
137 let mut first = true;
138
139 while !tts.is_empty() {
140 let prev_comma = if first {
141 first = false;
142 None
143 } else if let TokenKind::Op(',', OpKind::Alone) = tts[0].kind {
144 let tok = tokens::Comma([Span(tts[0].span)]);
145 tts = &tts[1..];
146 if tts.is_empty() {
147 break
148 }
149 Some(tok)
150 } else {
151 return None
152 };
153 let (nested, rest) = match nested_meta_item_from_tokens(tts) {
154 Some(pair) => pair,
155 None => return None,
156 };
157 match prev_comma {
158 Some(comma) => delimited.push_next(nested, comma),
159 None => delimited.push_first(nested),
160 }
161 tts = rest;
162 }
163
164 Some(delimited)
165}
166
167
Alex Crichton62a0a592017-05-22 13:58:53 -0700168ast_enum! {
169 /// Distinguishes between Attributes that decorate items and Attributes that
170 /// are contained as statements within items. These two cases need to be
171 /// distinguished for pretty-printing.
Alex Crichton2e0229c2017-05-23 09:34:50 -0700172 #[cfg_attr(feature = "clone-impls", derive(Copy))]
Alex Crichton62a0a592017-05-22 13:58:53 -0700173 pub enum AttrStyle {
Alex Crichtonccbb45d2017-05-23 10:58:24 -0700174 /// Attribute of the form `#[...]`.
Alex Crichton62a0a592017-05-22 13:58:53 -0700175 Outer,
Clar Charrd22b5702017-03-10 15:24:56 -0500176
Alex Crichtonccbb45d2017-05-23 10:58:24 -0700177 /// Attribute of the form `#![...]`.
178 Inner(tokens::Bang),
Alex Crichton62a0a592017-05-22 13:58:53 -0700179 }
David Tolnay4a51dc72016-10-01 00:40:31 -0700180}
181
Alex Crichton62a0a592017-05-22 13:58:53 -0700182ast_enum_of_structs! {
183 /// A compile-time attribute item.
David Tolnayb79ee962016-09-04 09:39:20 -0700184 ///
Alex Crichton62a0a592017-05-22 13:58:53 -0700185 /// E.g. `#[test]`, `#[derive(..)]` or `#[feature = "foo"]`
186 pub enum MetaItem {
187 /// Word meta item.
188 ///
189 /// E.g. `test` as in `#[test]`
190 pub Word(Ident),
Clar Charrd22b5702017-03-10 15:24:56 -0500191
Alex Crichton62a0a592017-05-22 13:58:53 -0700192 /// List meta item.
193 ///
194 /// E.g. `derive(..)` as in `#[derive(..)]`
195 pub List(MetaItemList {
196 /// Name of this attribute.
197 ///
198 /// E.g. `derive` in `#[derive(..)]`
199 pub ident: Ident,
Clar Charrd22b5702017-03-10 15:24:56 -0500200
Alex Crichtonccbb45d2017-05-23 10:58:24 -0700201 pub paren_token: tokens::Paren,
202
Alex Crichton62a0a592017-05-22 13:58:53 -0700203 /// Arguments to this attribute
204 ///
205 /// E.g. `..` in `#[derive(..)]`
Alex Crichtonccbb45d2017-05-23 10:58:24 -0700206 pub nested: Delimited<NestedMetaItem, tokens::Comma>,
Alex Crichton62a0a592017-05-22 13:58:53 -0700207 }),
208
209 /// Name-value meta item.
210 ///
211 /// E.g. `feature = "foo"` as in `#[feature = "foo"]`
212 pub NameValue(MetaNameValue {
213 /// Name of this attribute.
214 ///
215 /// E.g. `feature` in `#[feature = "foo"]`
216 pub ident: Ident,
217
Alex Crichtonccbb45d2017-05-23 10:58:24 -0700218 pub eq_token: tokens::Eq,
219
Alex Crichton62a0a592017-05-22 13:58:53 -0700220 /// Arguments to this attribute
221 ///
222 /// E.g. `"foo"` in `#[feature = "foo"]`
223 pub lit: Lit,
224 }),
225 }
Arnavion95f8a7a2017-04-19 03:29:56 -0700226}
227
228impl MetaItem {
229 /// Name of the item.
230 ///
231 /// E.g. `test` as in `#[test]`, `derive` as in `#[derive(..)]`, and
232 /// `feature` as in `#[feature = "foo"]`.
233 pub fn name(&self) -> &str {
234 match *self {
Alex Crichton62a0a592017-05-22 13:58:53 -0700235 MetaItem::Word(ref name) => name.as_ref(),
236 MetaItem::NameValue(ref pair) => pair.ident.as_ref(),
237 MetaItem::List(ref list) => list.ident.as_ref(),
Arnavion95f8a7a2017-04-19 03:29:56 -0700238 }
239 }
David Tolnay8e661e22016-09-27 00:00:04 -0700240}
241
Alex Crichton62a0a592017-05-22 13:58:53 -0700242ast_enum_of_structs! {
243 /// Possible values inside of compile-time attribute lists.
David Tolnayb7fa2b62016-10-30 10:50:47 -0700244 ///
Alex Crichton62a0a592017-05-22 13:58:53 -0700245 /// E.g. the '..' in `#[name(..)]`.
246 pub enum NestedMetaItem {
247 /// A full `MetaItem`.
248 ///
249 /// E.g. `Copy` in `#[derive(Copy)]` would be a `MetaItem::Word(Ident::from("Copy"))`.
250 pub MetaItem(MetaItem),
Clar Charrd22b5702017-03-10 15:24:56 -0500251
Alex Crichton62a0a592017-05-22 13:58:53 -0700252 /// A Rust literal.
253 ///
254 /// E.g. `"name"` in `#[rename("name")]`.
255 pub Literal(Lit),
256 }
David Tolnayb7fa2b62016-10-30 10:50:47 -0700257}
258
David Tolnay4a51dc72016-10-01 00:40:31 -0700259pub trait FilterAttrs<'a> {
260 type Ret: Iterator<Item = &'a Attribute>;
261
262 fn outer(self) -> Self::Ret;
263 fn inner(self) -> Self::Ret;
264}
265
David Tolnaydaaf7742016-10-03 11:11:43 -0700266impl<'a, T> FilterAttrs<'a> for T
267 where T: IntoIterator<Item = &'a Attribute>
268{
David Tolnay4a51dc72016-10-01 00:40:31 -0700269 type Ret = iter::Filter<T::IntoIter, fn(&&Attribute) -> bool>;
270
271 fn outer(self) -> Self::Ret {
272 fn is_outer(attr: &&Attribute) -> bool {
Alex Crichton2e0229c2017-05-23 09:34:50 -0700273 match attr.style {
274 AttrStyle::Outer => true,
275 _ => false,
276 }
David Tolnay4a51dc72016-10-01 00:40:31 -0700277 }
278 self.into_iter().filter(is_outer)
279 }
280
281 fn inner(self) -> Self::Ret {
282 fn is_inner(attr: &&Attribute) -> bool {
Alex Crichton2e0229c2017-05-23 09:34:50 -0700283 match attr.style {
Alex Crichtonccbb45d2017-05-23 10:58:24 -0700284 AttrStyle::Inner(_) => true,
Alex Crichton2e0229c2017-05-23 09:34:50 -0700285 _ => false,
286 }
David Tolnay4a51dc72016-10-01 00:40:31 -0700287 }
288 self.into_iter().filter(is_inner)
289 }
290}
291
David Tolnay86eca752016-09-04 11:26:41 -0700292#[cfg(feature = "parsing")]
David Tolnay9d8f1972016-09-04 11:58:48 -0700293pub mod parsing {
294 use super::*;
Alex Crichtonccbb45d2017-05-23 10:58:24 -0700295 use mac::TokenTree;
Arnavioneaa2cff2017-04-15 03:32:13 -0700296 use mac::parsing::token_trees;
David Tolnay5fe14fc2017-01-27 16:22:08 -0800297 use synom::space::{block_comment, whitespace};
Arnavionf2dada12017-04-20 23:55:20 -0700298 use ty::parsing::mod_style_path;
Alex Crichtonccbb45d2017-05-23 10:58:24 -0700299 use proc_macro2::{self, TokenKind, OpKind, Literal};
300
301 fn eq() -> TokenTree {
302 TokenTree(proc_macro2::TokenTree {
303 span: Default::default(),
304 kind: TokenKind::Op('=', OpKind::Alone),
305 })
306 }
307
308 fn doc(s: &str) -> TokenTree {
309 TokenTree(proc_macro2::TokenTree {
310 span: Default::default(),
311 kind: TokenKind::Literal(Literal::doccomment(s)),
312 })
313 }
David Tolnayb79ee962016-09-04 09:39:20 -0700314
David Tolnay2a2e67c2016-10-01 14:02:01 -0700315 #[cfg(feature = "full")]
David Tolnay14cbdeb2016-10-01 12:13:59 -0700316 named!(pub inner_attr -> Attribute, alt!(
317 do_parse!(
318 punct!("#") >>
319 punct!("!") >>
Arnavione43b1b02017-04-19 02:47:45 -0700320 path_and_tts: delimited!(
Arnavionbf395bf2017-04-15 15:35:22 -0700321 punct!("["),
Arnavionf2dada12017-04-20 23:55:20 -0700322 tuple!(mod_style_path, token_trees),
Arnavionbf395bf2017-04-15 15:35:22 -0700323 punct!("]")
324 ) >>
325 ({
Arnavione43b1b02017-04-19 02:47:45 -0700326 let (path, tts) = path_and_tts;
Arnavionbf395bf2017-04-15 15:35:22 -0700327
328 Attribute {
Alex Crichtonccbb45d2017-05-23 10:58:24 -0700329 style: AttrStyle::Inner(tokens::Bang::default()),
Arnavione43b1b02017-04-19 02:47:45 -0700330 path: path,
Arnavionbf395bf2017-04-15 15:35:22 -0700331 tts: tts,
332 is_sugared_doc: false,
Alex Crichtonccbb45d2017-05-23 10:58:24 -0700333 pound_token: tokens::Pound::default(),
334 bracket_token: tokens::Bracket::default(),
Arnavionbf395bf2017-04-15 15:35:22 -0700335 }
David Tolnay14cbdeb2016-10-01 12:13:59 -0700336 })
337 )
338 |
339 do_parse!(
340 punct!("//!") >>
341 content: take_until!("\n") >>
342 (Attribute {
Alex Crichtonccbb45d2017-05-23 10:58:24 -0700343 style: AttrStyle::Inner(tokens::Bang::default()),
Arnavione43b1b02017-04-19 02:47:45 -0700344 path: "doc".into(),
Arnavionbf395bf2017-04-15 15:35:22 -0700345 tts: vec![
Alex Crichtonccbb45d2017-05-23 10:58:24 -0700346 eq(),
347 doc(&format!("//!{}", content)),
Arnavionbf395bf2017-04-15 15:35:22 -0700348 ],
David Tolnay14cbdeb2016-10-01 12:13:59 -0700349 is_sugared_doc: true,
Alex Crichtonccbb45d2017-05-23 10:58:24 -0700350 pound_token: tokens::Pound::default(),
351 bracket_token: tokens::Bracket::default(),
David Tolnay14cbdeb2016-10-01 12:13:59 -0700352 })
353 )
354 |
355 do_parse!(
356 option!(whitespace) >>
357 peek!(tag!("/*!")) >>
358 com: block_comment >>
359 (Attribute {
Alex Crichtonccbb45d2017-05-23 10:58:24 -0700360 style: AttrStyle::Inner(tokens::Bang::default()),
Arnavione43b1b02017-04-19 02:47:45 -0700361 path: "doc".into(),
Arnavionbf395bf2017-04-15 15:35:22 -0700362 tts: vec![
Alex Crichtonccbb45d2017-05-23 10:58:24 -0700363 eq(),
364 doc(com),
Arnavionbf395bf2017-04-15 15:35:22 -0700365 ],
David Tolnay14cbdeb2016-10-01 12:13:59 -0700366 is_sugared_doc: true,
Alex Crichtonccbb45d2017-05-23 10:58:24 -0700367 pound_token: tokens::Pound::default(),
368 bracket_token: tokens::Bracket::default(),
David Tolnay14cbdeb2016-10-01 12:13:59 -0700369 })
370 )
David Tolnay4a51dc72016-10-01 00:40:31 -0700371 ));
372
373 named!(pub outer_attr -> Attribute, alt!(
David Tolnay9d8f1972016-09-04 11:58:48 -0700374 do_parse!(
375 punct!("#") >>
Arnavione43b1b02017-04-19 02:47:45 -0700376 path_and_tts: delimited!(
Arnavionbf395bf2017-04-15 15:35:22 -0700377 punct!("["),
Arnavionf2dada12017-04-20 23:55:20 -0700378 tuple!(mod_style_path, token_trees),
Arnavionbf395bf2017-04-15 15:35:22 -0700379 punct!("]")
Arnavioneaa2cff2017-04-15 03:32:13 -0700380 ) >>
Arnavionbf395bf2017-04-15 15:35:22 -0700381 ({
Arnavione43b1b02017-04-19 02:47:45 -0700382 let (path, tts) = path_and_tts;
Arnavionbf395bf2017-04-15 15:35:22 -0700383
384 Attribute {
385 style: AttrStyle::Outer,
Arnavione43b1b02017-04-19 02:47:45 -0700386 path: path,
Arnavionbf395bf2017-04-15 15:35:22 -0700387 tts: tts,
388 is_sugared_doc: false,
Alex Crichtonccbb45d2017-05-23 10:58:24 -0700389 pound_token: tokens::Pound::default(),
390 bracket_token: tokens::Bracket::default(),
Arnavionbf395bf2017-04-15 15:35:22 -0700391 }
Arnavioneaa2cff2017-04-15 03:32:13 -0700392 })
393 )
394 |
395 do_parse!(
David Tolnay9d8f1972016-09-04 11:58:48 -0700396 punct!("///") >>
David Tolnay1f16b602017-02-07 20:06:55 -0500397 not!(tag!("/")) >>
David Tolnayb5a7b142016-09-13 22:46:39 -0700398 content: take_until!("\n") >>
David Tolnay9d8f1972016-09-04 11:58:48 -0700399 (Attribute {
David Tolnay4a51dc72016-10-01 00:40:31 -0700400 style: AttrStyle::Outer,
Arnavione43b1b02017-04-19 02:47:45 -0700401 path: "doc".into(),
Arnavionbf395bf2017-04-15 15:35:22 -0700402 tts: vec![
Alex Crichtonccbb45d2017-05-23 10:58:24 -0700403 eq(),
404 doc(&format!("///{}", content)),
Arnavionbf395bf2017-04-15 15:35:22 -0700405 ],
David Tolnay9d8f1972016-09-04 11:58:48 -0700406 is_sugared_doc: true,
Alex Crichtonccbb45d2017-05-23 10:58:24 -0700407 pound_token: tokens::Pound::default(),
408 bracket_token: tokens::Bracket::default(),
David Tolnay9d8f1972016-09-04 11:58:48 -0700409 })
410 )
David Tolnay14cbdeb2016-10-01 12:13:59 -0700411 |
412 do_parse!(
413 option!(whitespace) >>
David Tolnay84aa0752016-10-02 23:01:13 -0700414 peek!(tuple!(tag!("/**"), not!(tag!("*")))) >>
David Tolnay14cbdeb2016-10-01 12:13:59 -0700415 com: block_comment >>
416 (Attribute {
417 style: AttrStyle::Outer,
Arnavione43b1b02017-04-19 02:47:45 -0700418 path: "doc".into(),
Arnavionbf395bf2017-04-15 15:35:22 -0700419 tts: vec![
Alex Crichtonccbb45d2017-05-23 10:58:24 -0700420 eq(),
421 doc(com),
Arnavionbf395bf2017-04-15 15:35:22 -0700422 ],
David Tolnay14cbdeb2016-10-01 12:13:59 -0700423 is_sugared_doc: true,
Alex Crichtonccbb45d2017-05-23 10:58:24 -0700424 pound_token: tokens::Pound::default(),
425 bracket_token: tokens::Bracket::default(),
David Tolnay14cbdeb2016-10-01 12:13:59 -0700426 })
427 )
David Tolnay9d8f1972016-09-04 11:58:48 -0700428 ));
David Tolnay9d8f1972016-09-04 11:58:48 -0700429}
David Tolnay87d0b442016-09-04 11:52:12 -0700430
431#[cfg(feature = "printing")]
432mod printing {
433 use super::*;
434 use quote::{Tokens, ToTokens};
Alex Crichtonccbb45d2017-05-23 10:58:24 -0700435 use proc_macro2::{Literal, TokenTree};
David Tolnay87d0b442016-09-04 11:52:12 -0700436
437 impl ToTokens for Attribute {
438 fn to_tokens(&self, tokens: &mut Tokens) {
Arnavion44d2bf32017-04-19 02:47:55 -0700439 // If this was a sugared doc, emit it in its original form instead of `#[doc = "..."]`
Alex Crichtonccbb45d2017-05-23 10:58:24 -0700440 if self.is_sugared_doc {
441 if let Some(MetaItem::NameValue(ref pair)) = self.meta_item() {
442 if pair.ident == "doc" {
443 let value = pair.lit.value.to_string();
444 match self.style {
445 AttrStyle::Inner(_) if value.starts_with("//!") => {
446 let doc = Literal::doccomment(&format!("{}\n", value));
447 tokens.append(TokenTree {
448 span: pair.lit.span.0,
449 kind: TokenKind::Literal(doc),
450 });
451 return;
Arnavionbf395bf2017-04-15 15:35:22 -0700452 }
Alex Crichtonccbb45d2017-05-23 10:58:24 -0700453 AttrStyle::Inner(_) if value.starts_with("/*!") => {
454 pair.lit.to_tokens(tokens);
455 return;
456 }
457 AttrStyle::Outer if value.starts_with("///") => {
458 let doc = Literal::doccomment(&format!("{}\n", value));
459 tokens.append(TokenTree {
460 span: pair.lit.span.0,
461 kind: TokenKind::Literal(doc),
462 });
463 return;
464 }
465 AttrStyle::Outer if value.starts_with("/**") => {
466 pair.lit.to_tokens(tokens);
467 return;
468 }
469 _ => {}
David Tolnay14cbdeb2016-10-01 12:13:59 -0700470 }
David Tolnay4a51dc72016-10-01 00:40:31 -0700471 }
David Tolnayc91dd672016-10-01 01:03:56 -0700472 }
473 }
David Tolnay14cbdeb2016-10-01 12:13:59 -0700474
Alex Crichtonccbb45d2017-05-23 10:58:24 -0700475 self.pound_token.to_tokens(tokens);
476 if let AttrStyle::Inner(ref b) = self.style {
477 b.to_tokens(tokens);
David Tolnay14cbdeb2016-10-01 12:13:59 -0700478 }
Alex Crichtonccbb45d2017-05-23 10:58:24 -0700479 self.bracket_token.surround(tokens, |tokens| {
480 self.path.to_tokens(tokens);
481 tokens.append_all(&self.tts);
482 });
David Tolnay87d0b442016-09-04 11:52:12 -0700483 }
484 }
485
Alex Crichton62a0a592017-05-22 13:58:53 -0700486 impl ToTokens for MetaItemList {
David Tolnay87d0b442016-09-04 11:52:12 -0700487 fn to_tokens(&self, tokens: &mut Tokens) {
Alex Crichton62a0a592017-05-22 13:58:53 -0700488 self.ident.to_tokens(tokens);
Alex Crichtonccbb45d2017-05-23 10:58:24 -0700489 self.paren_token.surround(tokens, |tokens| {
490 self.nested.to_tokens(tokens);
491 })
David Tolnay87d0b442016-09-04 11:52:12 -0700492 }
493 }
David Tolnayb7fa2b62016-10-30 10:50:47 -0700494
Alex Crichton62a0a592017-05-22 13:58:53 -0700495 impl ToTokens for MetaNameValue {
David Tolnayb7fa2b62016-10-30 10:50:47 -0700496 fn to_tokens(&self, tokens: &mut Tokens) {
Alex Crichton62a0a592017-05-22 13:58:53 -0700497 self.ident.to_tokens(tokens);
Alex Crichtonccbb45d2017-05-23 10:58:24 -0700498 self.eq_token.to_tokens(tokens);
Alex Crichton62a0a592017-05-22 13:58:53 -0700499 self.lit.to_tokens(tokens);
David Tolnayb7fa2b62016-10-30 10:50:47 -0700500 }
501 }
David Tolnay87d0b442016-09-04 11:52:12 -0700502}