blob: 12336630c14dfdf69fa82cd1963b10f4aaee0dea [file] [log] [blame]
David Tolnay55535012018-01-05 16:39:23 -08001// Copyright 2018 Syn Developers
2//
3// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
4// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
5// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
6// option. This file may not be copied, modified, or distributed
7// except according to those terms.
8
David Tolnayb79ee962016-09-04 09:39:20 -07009use super::*;
David Tolnayf2cfd722017-12-31 18:02:51 -050010use punctuated::Punctuated;
David Tolnayb79ee962016-09-04 09:39:20 -070011
David Tolnay4a51dc72016-10-01 00:40:31 -070012use std::iter;
13
David Tolnay51382052017-12-27 13:46:21 -050014use proc_macro2::{Delimiter, Spacing, TokenNode, TokenStream, TokenTree};
David Tolnay9c76bcb2017-12-26 23:14:59 -050015
16#[cfg(feature = "extra-traits")]
17use std::hash::{Hash, Hasher};
18#[cfg(feature = "extra-traits")]
David Tolnayc43b44e2017-12-30 23:55:54 -050019use tt::TokenStreamHelper;
Alex Crichtonccbb45d2017-05-23 10:58:24 -070020
Alex Crichton62a0a592017-05-22 13:58:53 -070021ast_struct! {
22 /// Doc-comments are promoted to attributes that have `is_sugared_doc` = true
David Tolnay9c76bcb2017-12-26 23:14:59 -050023 pub struct Attribute #manual_extra_traits {
David Tolnayf8db7ba2017-11-11 22:52:16 -080024 pub pound_token: Token![#],
David Tolnay4a3f59a2017-12-28 21:21:12 -050025 pub style: AttrStyle,
David Tolnay32954ef2017-12-26 22:43:16 -050026 pub bracket_token: token::Bracket,
Arnavion44d2bf32017-04-19 02:47:55 -070027
Alex Crichton62a0a592017-05-22 13:58:53 -070028 /// The path of the attribute.
29 ///
30 /// E.g. `derive` in `#[derive(Copy)]`
31 /// E.g. `crate::precondition` in `#[crate::precondition x < 5]`
32 pub path: Path,
Arnavion44d2bf32017-04-19 02:47:55 -070033
Alex Crichton62a0a592017-05-22 13:58:53 -070034 /// Any tokens after the path.
35 ///
36 /// E.g. `( Copy )` in `#[derive(Copy)]`
37 /// E.g. `x < 5` in `#[crate::precondition x < 5]`
David Tolnay369f0c52017-12-27 01:50:45 -050038 pub tts: TokenStream,
Arnavion44d2bf32017-04-19 02:47:55 -070039
Alex Crichton62a0a592017-05-22 13:58:53 -070040 pub is_sugared_doc: bool,
41 }
David Tolnayb79ee962016-09-04 09:39:20 -070042}
43
David Tolnay9c76bcb2017-12-26 23:14:59 -050044#[cfg(feature = "extra-traits")]
45impl Eq for Attribute {}
46
47#[cfg(feature = "extra-traits")]
48impl PartialEq for Attribute {
49 fn eq(&self, other: &Self) -> bool {
David Tolnay51382052017-12-27 13:46:21 -050050 self.style == other.style && self.pound_token == other.pound_token
51 && self.bracket_token == other.bracket_token && self.path == other.path
David Tolnay369f0c52017-12-27 01:50:45 -050052 && TokenStreamHelper(&self.tts) == TokenStreamHelper(&other.tts)
David Tolnay9c76bcb2017-12-26 23:14:59 -050053 && self.is_sugared_doc == other.is_sugared_doc
54 }
55}
56
57#[cfg(feature = "extra-traits")]
58impl Hash for Attribute {
59 fn hash<H>(&self, state: &mut H)
David Tolnay51382052017-12-27 13:46:21 -050060 where
61 H: Hasher,
David Tolnay9c76bcb2017-12-26 23:14:59 -050062 {
63 self.style.hash(state);
64 self.pound_token.hash(state);
65 self.bracket_token.hash(state);
66 self.path.hash(state);
David Tolnay369f0c52017-12-27 01:50:45 -050067 TokenStreamHelper(&self.tts).hash(state);
David Tolnay9c76bcb2017-12-26 23:14:59 -050068 self.is_sugared_doc.hash(state);
69 }
70}
71
David Tolnay02d77cc2016-10-02 09:52:08 -070072impl Attribute {
Arnavion44d2bf32017-04-19 02:47:55 -070073 /// Parses the tokens after the path as a [`MetaItem`](enum.MetaItem.html) if possible.
Arnavionbf395bf2017-04-15 15:35:22 -070074 pub fn meta_item(&self) -> Option<MetaItem> {
Arnavion95f8a7a2017-04-19 03:29:56 -070075 let name = if self.path.segments.len() == 1 {
David Tolnay56080682018-01-06 14:01:52 -080076 &self.path.segments.first().unwrap().value().ident
Arnavion95f8a7a2017-04-19 03:29:56 -070077 } else {
78 return None;
79 };
80
Arnavionbf395bf2017-04-15 15:35:22 -070081 if self.tts.is_empty() {
David Tolnaybb4ca9f2017-12-26 12:28:58 -050082 return Some(MetaItem::Term(*name));
Arnavionbf395bf2017-04-15 15:35:22 -070083 }
84
David Tolnay369f0c52017-12-27 01:50:45 -050085 let tts = self.tts.clone().into_iter().collect::<Vec<_>>();
86
87 if tts.len() == 1 {
88 if let TokenNode::Group(Delimiter::Parenthesis, ref ts) = tts[0].kind {
Alex Crichtonccbb45d2017-05-23 10:58:24 -070089 let tokens = ts.clone().into_iter().collect::<Vec<_>>();
90 if let Some(nested_meta_items) = list_of_nested_meta_items_from_tokens(&tokens) {
Alex Crichton62a0a592017-05-22 13:58:53 -070091 return Some(MetaItem::List(MetaItemList {
David Tolnay369f0c52017-12-27 01:50:45 -050092 paren_token: token::Paren(tts[0].span),
David Tolnaybb4ca9f2017-12-26 12:28:58 -050093 ident: *name,
Alex Crichton62a0a592017-05-22 13:58:53 -070094 nested: nested_meta_items,
95 }));
Arnavionbf395bf2017-04-15 15:35:22 -070096 }
97 }
98 }
99
David Tolnay369f0c52017-12-27 01:50:45 -0500100 if tts.len() == 2 {
101 if let TokenNode::Op('=', Spacing::Alone) = tts[0].kind {
102 if let TokenNode::Literal(ref lit) = tts[1].kind {
Alex Crichton62a0a592017-05-22 13:58:53 -0700103 return Some(MetaItem::NameValue(MetaNameValue {
David Tolnaybb4ca9f2017-12-26 12:28:58 -0500104 ident: *name,
David Tolnay369f0c52017-12-27 01:50:45 -0500105 eq_token: Token![=]([tts[0].span]),
David Tolnay360efd22018-01-04 23:35:26 -0800106 lit: Lit::new(lit.clone(), tts[1].span),
Alex Crichton62a0a592017-05-22 13:58:53 -0700107 }));
Arnavionbf395bf2017-04-15 15:35:22 -0700108 }
Arnavionbf395bf2017-04-15 15:35:22 -0700109 }
110 }
111
112 None
David Tolnay02d77cc2016-10-02 09:52:08 -0700113 }
114}
115
David Tolnay51382052017-12-27 13:46:21 -0500116fn nested_meta_item_from_tokens(tts: &[TokenTree]) -> Option<(NestedMetaItem, &[TokenTree])> {
Alex Crichtonccbb45d2017-05-23 10:58:24 -0700117 assert!(!tts.is_empty());
118
119 match tts[0].kind {
Alex Crichtonf9e8f1a2017-07-05 18:20:44 -0700120 TokenNode::Literal(ref lit) => {
David Tolnay360efd22018-01-04 23:35:26 -0800121 let lit = Lit::new(lit.clone(), tts[0].span);
Alex Crichtonccbb45d2017-05-23 10:58:24 -0700122 Some((NestedMetaItem::Literal(lit), &tts[1..]))
123 }
124
Alex Crichtonf9e8f1a2017-07-05 18:20:44 -0700125 TokenNode::Term(sym) => {
David Tolnayeb771d72017-12-27 22:11:06 -0500126 let ident = Ident::new(sym.as_str(), tts[0].span);
Alex Crichtonccbb45d2017-05-23 10:58:24 -0700127 if tts.len() >= 3 {
Alex Crichtonf9e8f1a2017-07-05 18:20:44 -0700128 if let TokenNode::Op('=', Spacing::Alone) = tts[1].kind {
129 if let TokenNode::Literal(ref lit) = tts[2].kind {
Alex Crichtonccbb45d2017-05-23 10:58:24 -0700130 let pair = MetaNameValue {
David Tolnayeb771d72017-12-27 22:11:06 -0500131 ident: Ident::new(sym.as_str(), tts[0].span),
David Tolnay98942562017-12-26 21:24:35 -0500132 eq_token: Token![=]([tts[1].span]),
David Tolnay360efd22018-01-04 23:35:26 -0800133 lit: Lit::new(lit.clone(), tts[2].span),
Alex Crichtonccbb45d2017-05-23 10:58:24 -0700134 };
135 return Some((MetaItem::NameValue(pair).into(), &tts[3..]));
136 }
137 }
138 }
139
140 if tts.len() >= 2 {
Alex Crichtonf9e8f1a2017-07-05 18:20:44 -0700141 if let TokenNode::Group(Delimiter::Parenthesis, ref inner_tts) = tts[1].kind {
Alex Crichtonccbb45d2017-05-23 10:58:24 -0700142 let inner_tts = inner_tts.clone().into_iter().collect::<Vec<_>>();
143 return match list_of_nested_meta_items_from_tokens(&inner_tts) {
144 Some(nested_meta_items) => {
145 let list = MetaItemList {
146 ident: ident,
David Tolnay32954ef2017-12-26 22:43:16 -0500147 paren_token: token::Paren(tts[1].span),
Alex Crichtonccbb45d2017-05-23 10:58:24 -0700148 nested: nested_meta_items,
149 };
150 Some((MetaItem::List(list).into(), &tts[2..]))
151 }
152
David Tolnay51382052017-12-27 13:46:21 -0500153 None => None,
Alex Crichtonccbb45d2017-05-23 10:58:24 -0700154 };
155 }
156 }
157
Alex Crichtonf9e8f1a2017-07-05 18:20:44 -0700158 Some((MetaItem::Term(ident).into(), &tts[1..]))
Alex Crichtonccbb45d2017-05-23 10:58:24 -0700159 }
160
David Tolnay51382052017-12-27 13:46:21 -0500161 _ => None,
Alex Crichtonccbb45d2017-05-23 10:58:24 -0700162 }
163}
164
David Tolnay51382052017-12-27 13:46:21 -0500165fn list_of_nested_meta_items_from_tokens(
166 mut tts: &[TokenTree],
David Tolnayf2cfd722017-12-31 18:02:51 -0500167) -> Option<Punctuated<NestedMetaItem, Token![,]>> {
168 let mut nested_meta_items = Punctuated::new();
Alex Crichtonccbb45d2017-05-23 10:58:24 -0700169 let mut first = true;
170
171 while !tts.is_empty() {
172 let prev_comma = if first {
173 first = false;
174 None
Alex Crichtonf9e8f1a2017-07-05 18:20:44 -0700175 } else if let TokenNode::Op(',', Spacing::Alone) = tts[0].kind {
David Tolnay98942562017-12-26 21:24:35 -0500176 let tok = Token![,]([tts[0].span]);
Alex Crichtonccbb45d2017-05-23 10:58:24 -0700177 tts = &tts[1..];
178 if tts.is_empty() {
David Tolnay51382052017-12-27 13:46:21 -0500179 break;
Alex Crichtonccbb45d2017-05-23 10:58:24 -0700180 }
181 Some(tok)
182 } else {
David Tolnay51382052017-12-27 13:46:21 -0500183 return None;
Alex Crichtonccbb45d2017-05-23 10:58:24 -0700184 };
185 let (nested, rest) = match nested_meta_item_from_tokens(tts) {
186 Some(pair) => pair,
187 None => return None,
188 };
David Tolnay660fd1f2017-12-31 01:52:57 -0500189 if let Some(comma) = prev_comma {
David Tolnaya0834b42018-01-01 21:30:02 -0800190 nested_meta_items.push_punct(comma);
Alex Crichtonccbb45d2017-05-23 10:58:24 -0700191 }
David Tolnay56080682018-01-06 14:01:52 -0800192 nested_meta_items.push_value(nested);
Alex Crichtonccbb45d2017-05-23 10:58:24 -0700193 tts = rest;
194 }
195
David Tolnayf2cfd722017-12-31 18:02:51 -0500196 Some(nested_meta_items)
Alex Crichtonccbb45d2017-05-23 10:58:24 -0700197}
198
Alex Crichton62a0a592017-05-22 13:58:53 -0700199ast_enum! {
200 /// Distinguishes between Attributes that decorate items and Attributes that
201 /// are contained as statements within items. These two cases need to be
202 /// distinguished for pretty-printing.
Alex Crichton2e0229c2017-05-23 09:34:50 -0700203 #[cfg_attr(feature = "clone-impls", derive(Copy))]
Alex Crichton62a0a592017-05-22 13:58:53 -0700204 pub enum AttrStyle {
Alex Crichtonccbb45d2017-05-23 10:58:24 -0700205 /// Attribute of the form `#[...]`.
Alex Crichton62a0a592017-05-22 13:58:53 -0700206 Outer,
Clar Charrd22b5702017-03-10 15:24:56 -0500207
Alex Crichtonccbb45d2017-05-23 10:58:24 -0700208 /// Attribute of the form `#![...]`.
David Tolnayf8db7ba2017-11-11 22:52:16 -0800209 Inner(Token![!]),
Alex Crichton62a0a592017-05-22 13:58:53 -0700210 }
David Tolnay4a51dc72016-10-01 00:40:31 -0700211}
212
Alex Crichton62a0a592017-05-22 13:58:53 -0700213ast_enum_of_structs! {
214 /// A compile-time attribute item.
David Tolnayb79ee962016-09-04 09:39:20 -0700215 ///
Alex Crichton62a0a592017-05-22 13:58:53 -0700216 /// E.g. `#[test]`, `#[derive(..)]` or `#[feature = "foo"]`
217 pub enum MetaItem {
Alex Crichtonf9e8f1a2017-07-05 18:20:44 -0700218 /// Term meta item.
Alex Crichton62a0a592017-05-22 13:58:53 -0700219 ///
220 /// E.g. `test` as in `#[test]`
Alex Crichtonf9e8f1a2017-07-05 18:20:44 -0700221 pub Term(Ident),
Clar Charrd22b5702017-03-10 15:24:56 -0500222
Alex Crichton62a0a592017-05-22 13:58:53 -0700223 /// List meta item.
224 ///
225 /// E.g. `derive(..)` as in `#[derive(..)]`
226 pub List(MetaItemList {
227 /// Name of this attribute.
228 ///
229 /// E.g. `derive` in `#[derive(..)]`
230 pub ident: Ident,
Clar Charrd22b5702017-03-10 15:24:56 -0500231
David Tolnay32954ef2017-12-26 22:43:16 -0500232 pub paren_token: token::Paren,
Alex Crichtonccbb45d2017-05-23 10:58:24 -0700233
Alex Crichton62a0a592017-05-22 13:58:53 -0700234 /// Arguments to this attribute
235 ///
236 /// E.g. `..` in `#[derive(..)]`
David Tolnayf2cfd722017-12-31 18:02:51 -0500237 pub nested: Punctuated<NestedMetaItem, Token![,]>,
Alex Crichton62a0a592017-05-22 13:58:53 -0700238 }),
239
240 /// Name-value meta item.
241 ///
242 /// E.g. `feature = "foo"` as in `#[feature = "foo"]`
243 pub NameValue(MetaNameValue {
244 /// Name of this attribute.
245 ///
246 /// E.g. `feature` in `#[feature = "foo"]`
247 pub ident: Ident,
248
David Tolnayf8db7ba2017-11-11 22:52:16 -0800249 pub eq_token: Token![=],
Alex Crichtonccbb45d2017-05-23 10:58:24 -0700250
Alex Crichton62a0a592017-05-22 13:58:53 -0700251 /// Arguments to this attribute
252 ///
253 /// E.g. `"foo"` in `#[feature = "foo"]`
254 pub lit: Lit,
255 }),
256 }
Arnavion95f8a7a2017-04-19 03:29:56 -0700257}
258
259impl MetaItem {
260 /// Name of the item.
261 ///
262 /// E.g. `test` as in `#[test]`, `derive` as in `#[derive(..)]`, and
263 /// `feature` as in `#[feature = "foo"]`.
264 pub fn name(&self) -> &str {
265 match *self {
Alex Crichtonf9e8f1a2017-07-05 18:20:44 -0700266 MetaItem::Term(ref name) => name.as_ref(),
Alex Crichton62a0a592017-05-22 13:58:53 -0700267 MetaItem::NameValue(ref pair) => pair.ident.as_ref(),
268 MetaItem::List(ref list) => list.ident.as_ref(),
Arnavion95f8a7a2017-04-19 03:29:56 -0700269 }
270 }
David Tolnay8e661e22016-09-27 00:00:04 -0700271}
272
Alex Crichton62a0a592017-05-22 13:58:53 -0700273ast_enum_of_structs! {
274 /// Possible values inside of compile-time attribute lists.
David Tolnayb7fa2b62016-10-30 10:50:47 -0700275 ///
Alex Crichton62a0a592017-05-22 13:58:53 -0700276 /// E.g. the '..' in `#[name(..)]`.
277 pub enum NestedMetaItem {
278 /// A full `MetaItem`.
279 ///
Alex Crichtonf9e8f1a2017-07-05 18:20:44 -0700280 /// E.g. `Copy` in `#[derive(Copy)]` would be a `MetaItem::Term(Ident::from("Copy"))`.
Alex Crichton62a0a592017-05-22 13:58:53 -0700281 pub MetaItem(MetaItem),
Clar Charrd22b5702017-03-10 15:24:56 -0500282
Alex Crichton62a0a592017-05-22 13:58:53 -0700283 /// A Rust literal.
284 ///
285 /// E.g. `"name"` in `#[rename("name")]`.
286 pub Literal(Lit),
287 }
David Tolnayb7fa2b62016-10-30 10:50:47 -0700288}
289
David Tolnay4a51dc72016-10-01 00:40:31 -0700290pub trait FilterAttrs<'a> {
291 type Ret: Iterator<Item = &'a Attribute>;
292
293 fn outer(self) -> Self::Ret;
294 fn inner(self) -> Self::Ret;
295}
296
David Tolnaydaaf7742016-10-03 11:11:43 -0700297impl<'a, T> FilterAttrs<'a> for T
David Tolnay51382052017-12-27 13:46:21 -0500298where
299 T: IntoIterator<Item = &'a Attribute>,
David Tolnaydaaf7742016-10-03 11:11:43 -0700300{
David Tolnay4a51dc72016-10-01 00:40:31 -0700301 type Ret = iter::Filter<T::IntoIter, fn(&&Attribute) -> bool>;
302
303 fn outer(self) -> Self::Ret {
304 fn is_outer(attr: &&Attribute) -> bool {
Alex Crichton2e0229c2017-05-23 09:34:50 -0700305 match attr.style {
306 AttrStyle::Outer => true,
307 _ => false,
308 }
David Tolnay4a51dc72016-10-01 00:40:31 -0700309 }
310 self.into_iter().filter(is_outer)
311 }
312
313 fn inner(self) -> Self::Ret {
314 fn is_inner(attr: &&Attribute) -> bool {
Alex Crichton2e0229c2017-05-23 09:34:50 -0700315 match attr.style {
Alex Crichtonccbb45d2017-05-23 10:58:24 -0700316 AttrStyle::Inner(_) => true,
Alex Crichton2e0229c2017-05-23 09:34:50 -0700317 _ => false,
318 }
David Tolnay4a51dc72016-10-01 00:40:31 -0700319 }
320 self.into_iter().filter(is_inner)
321 }
322}
323
David Tolnay86eca752016-09-04 11:26:41 -0700324#[cfg(feature = "parsing")]
David Tolnay9d8f1972016-09-04 11:58:48 -0700325pub mod parsing {
326 use super::*;
David Tolnaydfc886b2018-01-06 08:03:09 -0800327 use buffer::Cursor;
David Tolnay203557a2017-12-27 23:59:33 -0500328 use parse_error;
329 use synom::PResult;
David Tolnay61037c62018-01-05 16:21:03 -0800330 use proc_macro2::{Literal, Spacing, Span, TokenNode, TokenTree};
Alex Crichtonccbb45d2017-05-23 10:58:24 -0700331
David Tolnayf800fc12017-12-27 22:08:48 -0500332 fn eq(span: Span) -> TokenTree {
Alex Crichton954046c2017-05-30 21:49:42 -0700333 TokenTree {
David Tolnayf800fc12017-12-27 22:08:48 -0500334 span: span,
Alex Crichtonf9e8f1a2017-07-05 18:20:44 -0700335 kind: TokenNode::Op('=', Spacing::Alone),
Alex Crichton954046c2017-05-30 21:49:42 -0700336 }
Alex Crichtonccbb45d2017-05-23 10:58:24 -0700337 }
338
Alex Crichton954046c2017-05-30 21:49:42 -0700339 impl Attribute {
Michael Layzell92639a52017-06-01 00:07:44 -0400340 named!(pub parse_inner -> Self, alt!(
341 do_parse!(
David Tolnayf8db7ba2017-11-11 22:52:16 -0800342 pound: punct!(#) >>
343 bang: punct!(!) >>
Michael Layzell92639a52017-06-01 00:07:44 -0400344 path_and_tts: brackets!(tuple!(
David Tolnaye64213b2017-12-30 00:24:20 -0500345 call!(Path::parse_mod_style),
David Tolnay369f0c52017-12-27 01:50:45 -0500346 syn!(TokenStream)
Michael Layzell92639a52017-06-01 00:07:44 -0400347 )) >>
348 ({
David Tolnay8875fca2017-12-31 13:52:37 -0500349 let (bracket, (path, tts)) = path_and_tts;
Alex Crichton954046c2017-05-30 21:49:42 -0700350
Michael Layzell92639a52017-06-01 00:07:44 -0400351 Attribute {
352 style: AttrStyle::Inner(bang),
353 path: path,
354 tts: tts,
355 is_sugared_doc: false,
356 pound_token: pound,
357 bracket_token: bracket,
Alex Crichton954046c2017-05-30 21:49:42 -0700358 }
Michael Layzell92639a52017-06-01 00:07:44 -0400359 })
360 )
361 |
362 map!(
David Tolnay201ef212018-01-01 00:09:14 -0500363 call!(lit_doc_comment, Comment::Inner),
David Tolnayf800fc12017-12-27 22:08:48 -0500364 |lit| {
365 let span = lit.span;
366 Attribute {
367 style: AttrStyle::Inner(<Token![!]>::new(span)),
368 path: Ident::new("doc", span).into(),
369 tts: vec![
370 eq(span),
371 lit,
372 ].into_iter().collect(),
373 is_sugared_doc: true,
374 pound_token: <Token![#]>::new(span),
375 bracket_token: token::Bracket(span),
376 }
Michael Layzell92639a52017-06-01 00:07:44 -0400377 }
378 )
379 ));
Alex Crichton954046c2017-05-30 21:49:42 -0700380
Michael Layzell92639a52017-06-01 00:07:44 -0400381 named!(pub parse_outer -> Self, alt!(
382 do_parse!(
David Tolnayf8db7ba2017-11-11 22:52:16 -0800383 pound: punct!(#) >>
Michael Layzell92639a52017-06-01 00:07:44 -0400384 path_and_tts: brackets!(tuple!(
David Tolnaye64213b2017-12-30 00:24:20 -0500385 call!(Path::parse_mod_style),
David Tolnay369f0c52017-12-27 01:50:45 -0500386 syn!(TokenStream)
Michael Layzell92639a52017-06-01 00:07:44 -0400387 )) >>
388 ({
David Tolnay8875fca2017-12-31 13:52:37 -0500389 let (bracket, (path, tts)) = path_and_tts;
Alex Crichton954046c2017-05-30 21:49:42 -0700390
Michael Layzell92639a52017-06-01 00:07:44 -0400391 Attribute {
Alex Crichton954046c2017-05-30 21:49:42 -0700392 style: AttrStyle::Outer,
Michael Layzell92639a52017-06-01 00:07:44 -0400393 path: path,
394 tts: tts,
395 is_sugared_doc: false,
396 pound_token: pound,
397 bracket_token: bracket,
Alex Crichton954046c2017-05-30 21:49:42 -0700398 }
Michael Layzell92639a52017-06-01 00:07:44 -0400399 })
400 )
401 |
402 map!(
David Tolnay201ef212018-01-01 00:09:14 -0500403 call!(lit_doc_comment, Comment::Outer),
David Tolnayf800fc12017-12-27 22:08:48 -0500404 |lit| {
405 let span = lit.span;
406 Attribute {
407 style: AttrStyle::Outer,
408 path: Ident::new("doc", span).into(),
409 tts: vec![
410 eq(span),
411 lit,
412 ].into_iter().collect(),
413 is_sugared_doc: true,
414 pound_token: <Token![#]>::new(span),
415 bracket_token: token::Bracket(span),
416 }
Michael Layzell92639a52017-06-01 00:07:44 -0400417 }
418 )
419 ));
Alex Crichtonccbb45d2017-05-23 10:58:24 -0700420 }
David Tolnayb79ee962016-09-04 09:39:20 -0700421
David Tolnay201ef212018-01-01 00:09:14 -0500422 enum Comment {
423 Inner,
424 Outer,
425 }
426
427 fn lit_doc_comment(input: Cursor, style: Comment) -> PResult<TokenTree> {
Michael Layzell589a8f42017-06-02 19:47:01 -0400428 match input.literal() {
David Tolnay65729482017-12-31 16:14:50 -0500429 Some((span, lit, rest)) => {
David Tolnay201ef212018-01-01 00:09:14 -0500430 let string = lit.to_string();
431 let ok = match style {
David Tolnay61037c62018-01-05 16:21:03 -0800432 Comment::Inner => string.starts_with("//!") || string.starts_with("/*!"),
433 Comment::Outer => string.starts_with("///") || string.starts_with("/**"),
David Tolnay201ef212018-01-01 00:09:14 -0500434 };
435 if ok {
David Tolnay51382052017-12-27 13:46:21 -0500436 Ok((
David Tolnay51382052017-12-27 13:46:21 -0500437 TokenTree {
438 span: span,
David Tolnay360efd22018-01-04 23:35:26 -0800439 kind: TokenNode::Literal(Literal::string(&string)),
David Tolnay51382052017-12-27 13:46:21 -0500440 },
David Tolnayf4aa6b42017-12-31 16:40:33 -0500441 rest,
David Tolnay51382052017-12-27 13:46:21 -0500442 ))
Michael Layzell589a8f42017-06-02 19:47:01 -0400443 } else {
444 parse_error()
445 }
446 }
David Tolnay51382052017-12-27 13:46:21 -0500447 _ => parse_error(),
Alex Crichton954046c2017-05-30 21:49:42 -0700448 }
449 }
David Tolnay9d8f1972016-09-04 11:58:48 -0700450}
David Tolnay87d0b442016-09-04 11:52:12 -0700451
452#[cfg(feature = "printing")]
453mod printing {
454 use super::*;
David Tolnay51382052017-12-27 13:46:21 -0500455 use quote::{ToTokens, Tokens};
David Tolnay360efd22018-01-04 23:35:26 -0800456 use proc_macro2::Literal;
David Tolnay87d0b442016-09-04 11:52:12 -0700457
458 impl ToTokens for Attribute {
459 fn to_tokens(&self, tokens: &mut Tokens) {
Arnavion44d2bf32017-04-19 02:47:55 -0700460 // If this was a sugared doc, emit it in its original form instead of `#[doc = "..."]`
Alex Crichtonccbb45d2017-05-23 10:58:24 -0700461 if self.is_sugared_doc {
462 if let Some(MetaItem::NameValue(ref pair)) = self.meta_item() {
463 if pair.ident == "doc" {
David Tolnay360efd22018-01-04 23:35:26 -0800464 if let Lit::Str(ref comment) = pair.lit {
465 tokens.append(TokenTree {
466 span: comment.span,
467 kind: TokenNode::Literal(Literal::doccomment(&comment.value())),
468 });
David Tolnay51382052017-12-27 13:46:21 -0500469 return;
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);
David Tolnay360efd22018-01-04 23:35:26 -0800481 self.tts.to_tokens(tokens);
Alex Crichtonccbb45d2017-05-23 10:58:24 -0700482 });
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}