blob: 58405b69198df701f3433ef6deeb1777bdd4ef05 [file] [log] [blame]
David Tolnayb79ee962016-09-04 09:39:20 -07001use super::*;
David Tolnayf2cfd722017-12-31 18:02:51 -05002use punctuated::Punctuated;
David Tolnayb79ee962016-09-04 09:39:20 -07003
David Tolnay4a51dc72016-10-01 00:40:31 -07004use std::iter;
5
David Tolnay51382052017-12-27 13:46:21 -05006use proc_macro2::{Delimiter, Spacing, TokenNode, TokenStream, TokenTree};
David Tolnay9c76bcb2017-12-26 23:14:59 -05007
8#[cfg(feature = "extra-traits")]
9use std::hash::{Hash, Hasher};
10#[cfg(feature = "extra-traits")]
David Tolnayc43b44e2017-12-30 23:55:54 -050011use tt::TokenStreamHelper;
Alex Crichtonccbb45d2017-05-23 10:58:24 -070012
Alex Crichton62a0a592017-05-22 13:58:53 -070013ast_struct! {
14 /// Doc-comments are promoted to attributes that have `is_sugared_doc` = true
David Tolnay9c76bcb2017-12-26 23:14:59 -050015 pub struct Attribute #manual_extra_traits {
David Tolnayf8db7ba2017-11-11 22:52:16 -080016 pub pound_token: Token![#],
David Tolnay4a3f59a2017-12-28 21:21:12 -050017 pub style: AttrStyle,
David Tolnay32954ef2017-12-26 22:43:16 -050018 pub bracket_token: token::Bracket,
Arnavion44d2bf32017-04-19 02:47:55 -070019
Alex Crichton62a0a592017-05-22 13:58:53 -070020 /// The path of the attribute.
21 ///
22 /// E.g. `derive` in `#[derive(Copy)]`
23 /// E.g. `crate::precondition` in `#[crate::precondition x < 5]`
24 pub path: Path,
Arnavion44d2bf32017-04-19 02:47:55 -070025
Alex Crichton62a0a592017-05-22 13:58:53 -070026 /// Any tokens after the path.
27 ///
28 /// E.g. `( Copy )` in `#[derive(Copy)]`
29 /// E.g. `x < 5` in `#[crate::precondition x < 5]`
David Tolnay369f0c52017-12-27 01:50:45 -050030 pub tts: TokenStream,
Arnavion44d2bf32017-04-19 02:47:55 -070031
Alex Crichton62a0a592017-05-22 13:58:53 -070032 pub is_sugared_doc: bool,
33 }
David Tolnayb79ee962016-09-04 09:39:20 -070034}
35
David Tolnay9c76bcb2017-12-26 23:14:59 -050036#[cfg(feature = "extra-traits")]
37impl Eq for Attribute {}
38
39#[cfg(feature = "extra-traits")]
40impl PartialEq for Attribute {
41 fn eq(&self, other: &Self) -> bool {
David Tolnay51382052017-12-27 13:46:21 -050042 self.style == other.style && self.pound_token == other.pound_token
43 && self.bracket_token == other.bracket_token && self.path == other.path
David Tolnay369f0c52017-12-27 01:50:45 -050044 && TokenStreamHelper(&self.tts) == TokenStreamHelper(&other.tts)
David Tolnay9c76bcb2017-12-26 23:14:59 -050045 && self.is_sugared_doc == other.is_sugared_doc
46 }
47}
48
49#[cfg(feature = "extra-traits")]
50impl Hash for Attribute {
51 fn hash<H>(&self, state: &mut H)
David Tolnay51382052017-12-27 13:46:21 -050052 where
53 H: Hasher,
David Tolnay9c76bcb2017-12-26 23:14:59 -050054 {
55 self.style.hash(state);
56 self.pound_token.hash(state);
57 self.bracket_token.hash(state);
58 self.path.hash(state);
David Tolnay369f0c52017-12-27 01:50:45 -050059 TokenStreamHelper(&self.tts).hash(state);
David Tolnay9c76bcb2017-12-26 23:14:59 -050060 self.is_sugared_doc.hash(state);
61 }
62}
63
David Tolnay02d77cc2016-10-02 09:52:08 -070064impl Attribute {
Arnavion44d2bf32017-04-19 02:47:55 -070065 /// Parses the tokens after the path as a [`MetaItem`](enum.MetaItem.html) if possible.
Arnavionbf395bf2017-04-15 15:35:22 -070066 pub fn meta_item(&self) -> Option<MetaItem> {
Arnavion95f8a7a2017-04-19 03:29:56 -070067 let name = if self.path.segments.len() == 1 {
David Tolnay660fd1f2017-12-31 01:52:57 -050068 &self.path.segments.first().unwrap().item().ident
Arnavion95f8a7a2017-04-19 03:29:56 -070069 } else {
70 return None;
71 };
72
Arnavionbf395bf2017-04-15 15:35:22 -070073 if self.tts.is_empty() {
David Tolnaybb4ca9f2017-12-26 12:28:58 -050074 return Some(MetaItem::Term(*name));
Arnavionbf395bf2017-04-15 15:35:22 -070075 }
76
David Tolnay369f0c52017-12-27 01:50:45 -050077 let tts = self.tts.clone().into_iter().collect::<Vec<_>>();
78
79 if tts.len() == 1 {
80 if let TokenNode::Group(Delimiter::Parenthesis, ref ts) = tts[0].kind {
Alex Crichtonccbb45d2017-05-23 10:58:24 -070081 let tokens = ts.clone().into_iter().collect::<Vec<_>>();
82 if let Some(nested_meta_items) = list_of_nested_meta_items_from_tokens(&tokens) {
Alex Crichton62a0a592017-05-22 13:58:53 -070083 return Some(MetaItem::List(MetaItemList {
David Tolnay369f0c52017-12-27 01:50:45 -050084 paren_token: token::Paren(tts[0].span),
David Tolnaybb4ca9f2017-12-26 12:28:58 -050085 ident: *name,
Alex Crichton62a0a592017-05-22 13:58:53 -070086 nested: nested_meta_items,
87 }));
Arnavionbf395bf2017-04-15 15:35:22 -070088 }
89 }
90 }
91
David Tolnay369f0c52017-12-27 01:50:45 -050092 if tts.len() == 2 {
93 if let TokenNode::Op('=', Spacing::Alone) = tts[0].kind {
94 if let TokenNode::Literal(ref lit) = tts[1].kind {
Alex Crichton62a0a592017-05-22 13:58:53 -070095 return Some(MetaItem::NameValue(MetaNameValue {
David Tolnaybb4ca9f2017-12-26 12:28:58 -050096 ident: *name,
David Tolnay369f0c52017-12-27 01:50:45 -050097 eq_token: Token![=]([tts[0].span]),
David Tolnay360efd22018-01-04 23:35:26 -080098 lit: Lit::new(lit.clone(), tts[1].span),
Alex Crichton62a0a592017-05-22 13:58:53 -070099 }));
Arnavionbf395bf2017-04-15 15:35:22 -0700100 }
Arnavionbf395bf2017-04-15 15:35:22 -0700101 }
102 }
103
104 None
David Tolnay02d77cc2016-10-02 09:52:08 -0700105 }
106}
107
David Tolnay51382052017-12-27 13:46:21 -0500108fn nested_meta_item_from_tokens(tts: &[TokenTree]) -> Option<(NestedMetaItem, &[TokenTree])> {
Alex Crichtonccbb45d2017-05-23 10:58:24 -0700109 assert!(!tts.is_empty());
110
111 match tts[0].kind {
Alex Crichtonf9e8f1a2017-07-05 18:20:44 -0700112 TokenNode::Literal(ref lit) => {
David Tolnay360efd22018-01-04 23:35:26 -0800113 let lit = Lit::new(lit.clone(), tts[0].span);
Alex Crichtonccbb45d2017-05-23 10:58:24 -0700114 Some((NestedMetaItem::Literal(lit), &tts[1..]))
115 }
116
Alex Crichtonf9e8f1a2017-07-05 18:20:44 -0700117 TokenNode::Term(sym) => {
David Tolnayeb771d72017-12-27 22:11:06 -0500118 let ident = Ident::new(sym.as_str(), tts[0].span);
Alex Crichtonccbb45d2017-05-23 10:58:24 -0700119 if tts.len() >= 3 {
Alex Crichtonf9e8f1a2017-07-05 18:20:44 -0700120 if let TokenNode::Op('=', Spacing::Alone) = tts[1].kind {
121 if let TokenNode::Literal(ref lit) = tts[2].kind {
Alex Crichtonccbb45d2017-05-23 10:58:24 -0700122 let pair = MetaNameValue {
David Tolnayeb771d72017-12-27 22:11:06 -0500123 ident: Ident::new(sym.as_str(), tts[0].span),
David Tolnay98942562017-12-26 21:24:35 -0500124 eq_token: Token![=]([tts[1].span]),
David Tolnay360efd22018-01-04 23:35:26 -0800125 lit: Lit::new(lit.clone(), tts[2].span),
Alex Crichtonccbb45d2017-05-23 10:58:24 -0700126 };
127 return Some((MetaItem::NameValue(pair).into(), &tts[3..]));
128 }
129 }
130 }
131
132 if tts.len() >= 2 {
Alex Crichtonf9e8f1a2017-07-05 18:20:44 -0700133 if let TokenNode::Group(Delimiter::Parenthesis, ref inner_tts) = tts[1].kind {
Alex Crichtonccbb45d2017-05-23 10:58:24 -0700134 let inner_tts = inner_tts.clone().into_iter().collect::<Vec<_>>();
135 return match list_of_nested_meta_items_from_tokens(&inner_tts) {
136 Some(nested_meta_items) => {
137 let list = MetaItemList {
138 ident: ident,
David Tolnay32954ef2017-12-26 22:43:16 -0500139 paren_token: token::Paren(tts[1].span),
Alex Crichtonccbb45d2017-05-23 10:58:24 -0700140 nested: nested_meta_items,
141 };
142 Some((MetaItem::List(list).into(), &tts[2..]))
143 }
144
David Tolnay51382052017-12-27 13:46:21 -0500145 None => None,
Alex Crichtonccbb45d2017-05-23 10:58:24 -0700146 };
147 }
148 }
149
Alex Crichtonf9e8f1a2017-07-05 18:20:44 -0700150 Some((MetaItem::Term(ident).into(), &tts[1..]))
Alex Crichtonccbb45d2017-05-23 10:58:24 -0700151 }
152
David Tolnay51382052017-12-27 13:46:21 -0500153 _ => None,
Alex Crichtonccbb45d2017-05-23 10:58:24 -0700154 }
155}
156
David Tolnay51382052017-12-27 13:46:21 -0500157fn list_of_nested_meta_items_from_tokens(
158 mut tts: &[TokenTree],
David Tolnayf2cfd722017-12-31 18:02:51 -0500159) -> Option<Punctuated<NestedMetaItem, Token![,]>> {
160 let mut nested_meta_items = Punctuated::new();
Alex Crichtonccbb45d2017-05-23 10:58:24 -0700161 let mut first = true;
162
163 while !tts.is_empty() {
164 let prev_comma = if first {
165 first = false;
166 None
Alex Crichtonf9e8f1a2017-07-05 18:20:44 -0700167 } else if let TokenNode::Op(',', Spacing::Alone) = tts[0].kind {
David Tolnay98942562017-12-26 21:24:35 -0500168 let tok = Token![,]([tts[0].span]);
Alex Crichtonccbb45d2017-05-23 10:58:24 -0700169 tts = &tts[1..];
170 if tts.is_empty() {
David Tolnay51382052017-12-27 13:46:21 -0500171 break;
Alex Crichtonccbb45d2017-05-23 10:58:24 -0700172 }
173 Some(tok)
174 } else {
David Tolnay51382052017-12-27 13:46:21 -0500175 return None;
Alex Crichtonccbb45d2017-05-23 10:58:24 -0700176 };
177 let (nested, rest) = match nested_meta_item_from_tokens(tts) {
178 Some(pair) => pair,
179 None => return None,
180 };
David Tolnay660fd1f2017-12-31 01:52:57 -0500181 if let Some(comma) = prev_comma {
David Tolnaya0834b42018-01-01 21:30:02 -0800182 nested_meta_items.push_punct(comma);
Alex Crichtonccbb45d2017-05-23 10:58:24 -0700183 }
David Tolnaya0834b42018-01-01 21:30:02 -0800184 nested_meta_items.push_item(nested);
Alex Crichtonccbb45d2017-05-23 10:58:24 -0700185 tts = rest;
186 }
187
David Tolnayf2cfd722017-12-31 18:02:51 -0500188 Some(nested_meta_items)
Alex Crichtonccbb45d2017-05-23 10:58:24 -0700189}
190
Alex Crichton62a0a592017-05-22 13:58:53 -0700191ast_enum! {
192 /// Distinguishes between Attributes that decorate items and Attributes that
193 /// are contained as statements within items. These two cases need to be
194 /// distinguished for pretty-printing.
Alex Crichton2e0229c2017-05-23 09:34:50 -0700195 #[cfg_attr(feature = "clone-impls", derive(Copy))]
Alex Crichton62a0a592017-05-22 13:58:53 -0700196 pub enum AttrStyle {
Alex Crichtonccbb45d2017-05-23 10:58:24 -0700197 /// Attribute of the form `#[...]`.
Alex Crichton62a0a592017-05-22 13:58:53 -0700198 Outer,
Clar Charrd22b5702017-03-10 15:24:56 -0500199
Alex Crichtonccbb45d2017-05-23 10:58:24 -0700200 /// Attribute of the form `#![...]`.
David Tolnayf8db7ba2017-11-11 22:52:16 -0800201 Inner(Token![!]),
Alex Crichton62a0a592017-05-22 13:58:53 -0700202 }
David Tolnay4a51dc72016-10-01 00:40:31 -0700203}
204
Alex Crichton62a0a592017-05-22 13:58:53 -0700205ast_enum_of_structs! {
206 /// A compile-time attribute item.
David Tolnayb79ee962016-09-04 09:39:20 -0700207 ///
Alex Crichton62a0a592017-05-22 13:58:53 -0700208 /// E.g. `#[test]`, `#[derive(..)]` or `#[feature = "foo"]`
209 pub enum MetaItem {
Alex Crichtonf9e8f1a2017-07-05 18:20:44 -0700210 /// Term meta item.
Alex Crichton62a0a592017-05-22 13:58:53 -0700211 ///
212 /// E.g. `test` as in `#[test]`
Alex Crichtonf9e8f1a2017-07-05 18:20:44 -0700213 pub Term(Ident),
Clar Charrd22b5702017-03-10 15:24:56 -0500214
Alex Crichton62a0a592017-05-22 13:58:53 -0700215 /// List meta item.
216 ///
217 /// E.g. `derive(..)` as in `#[derive(..)]`
218 pub List(MetaItemList {
219 /// Name of this attribute.
220 ///
221 /// E.g. `derive` in `#[derive(..)]`
222 pub ident: Ident,
Clar Charrd22b5702017-03-10 15:24:56 -0500223
David Tolnay32954ef2017-12-26 22:43:16 -0500224 pub paren_token: token::Paren,
Alex Crichtonccbb45d2017-05-23 10:58:24 -0700225
Alex Crichton62a0a592017-05-22 13:58:53 -0700226 /// Arguments to this attribute
227 ///
228 /// E.g. `..` in `#[derive(..)]`
David Tolnayf2cfd722017-12-31 18:02:51 -0500229 pub nested: Punctuated<NestedMetaItem, Token![,]>,
Alex Crichton62a0a592017-05-22 13:58:53 -0700230 }),
231
232 /// Name-value meta item.
233 ///
234 /// E.g. `feature = "foo"` as in `#[feature = "foo"]`
235 pub NameValue(MetaNameValue {
236 /// Name of this attribute.
237 ///
238 /// E.g. `feature` in `#[feature = "foo"]`
239 pub ident: Ident,
240
David Tolnayf8db7ba2017-11-11 22:52:16 -0800241 pub eq_token: Token![=],
Alex Crichtonccbb45d2017-05-23 10:58:24 -0700242
Alex Crichton62a0a592017-05-22 13:58:53 -0700243 /// Arguments to this attribute
244 ///
245 /// E.g. `"foo"` in `#[feature = "foo"]`
246 pub lit: Lit,
247 }),
248 }
Arnavion95f8a7a2017-04-19 03:29:56 -0700249}
250
251impl MetaItem {
252 /// Name of the item.
253 ///
254 /// E.g. `test` as in `#[test]`, `derive` as in `#[derive(..)]`, and
255 /// `feature` as in `#[feature = "foo"]`.
256 pub fn name(&self) -> &str {
257 match *self {
Alex Crichtonf9e8f1a2017-07-05 18:20:44 -0700258 MetaItem::Term(ref name) => name.as_ref(),
Alex Crichton62a0a592017-05-22 13:58:53 -0700259 MetaItem::NameValue(ref pair) => pair.ident.as_ref(),
260 MetaItem::List(ref list) => list.ident.as_ref(),
Arnavion95f8a7a2017-04-19 03:29:56 -0700261 }
262 }
David Tolnay8e661e22016-09-27 00:00:04 -0700263}
264
Alex Crichton62a0a592017-05-22 13:58:53 -0700265ast_enum_of_structs! {
266 /// Possible values inside of compile-time attribute lists.
David Tolnayb7fa2b62016-10-30 10:50:47 -0700267 ///
Alex Crichton62a0a592017-05-22 13:58:53 -0700268 /// E.g. the '..' in `#[name(..)]`.
269 pub enum NestedMetaItem {
270 /// A full `MetaItem`.
271 ///
Alex Crichtonf9e8f1a2017-07-05 18:20:44 -0700272 /// E.g. `Copy` in `#[derive(Copy)]` would be a `MetaItem::Term(Ident::from("Copy"))`.
Alex Crichton62a0a592017-05-22 13:58:53 -0700273 pub MetaItem(MetaItem),
Clar Charrd22b5702017-03-10 15:24:56 -0500274
Alex Crichton62a0a592017-05-22 13:58:53 -0700275 /// A Rust literal.
276 ///
277 /// E.g. `"name"` in `#[rename("name")]`.
278 pub Literal(Lit),
279 }
David Tolnayb7fa2b62016-10-30 10:50:47 -0700280}
281
David Tolnay4a51dc72016-10-01 00:40:31 -0700282pub trait FilterAttrs<'a> {
283 type Ret: Iterator<Item = &'a Attribute>;
284
285 fn outer(self) -> Self::Ret;
286 fn inner(self) -> Self::Ret;
287}
288
David Tolnaydaaf7742016-10-03 11:11:43 -0700289impl<'a, T> FilterAttrs<'a> for T
David Tolnay51382052017-12-27 13:46:21 -0500290where
291 T: IntoIterator<Item = &'a Attribute>,
David Tolnaydaaf7742016-10-03 11:11:43 -0700292{
David Tolnay4a51dc72016-10-01 00:40:31 -0700293 type Ret = iter::Filter<T::IntoIter, fn(&&Attribute) -> bool>;
294
295 fn outer(self) -> Self::Ret {
296 fn is_outer(attr: &&Attribute) -> bool {
Alex Crichton2e0229c2017-05-23 09:34:50 -0700297 match attr.style {
298 AttrStyle::Outer => true,
299 _ => false,
300 }
David Tolnay4a51dc72016-10-01 00:40:31 -0700301 }
302 self.into_iter().filter(is_outer)
303 }
304
305 fn inner(self) -> Self::Ret {
306 fn is_inner(attr: &&Attribute) -> bool {
Alex Crichton2e0229c2017-05-23 09:34:50 -0700307 match attr.style {
Alex Crichtonccbb45d2017-05-23 10:58:24 -0700308 AttrStyle::Inner(_) => true,
Alex Crichton2e0229c2017-05-23 09:34:50 -0700309 _ => false,
310 }
David Tolnay4a51dc72016-10-01 00:40:31 -0700311 }
312 self.into_iter().filter(is_inner)
313 }
314}
315
David Tolnay86eca752016-09-04 11:26:41 -0700316#[cfg(feature = "parsing")]
David Tolnay9d8f1972016-09-04 11:58:48 -0700317pub mod parsing {
318 use super::*;
David Tolnayc5ab8c62017-12-26 16:43:39 -0500319 use cursor::Cursor;
David Tolnay203557a2017-12-27 23:59:33 -0500320 use parse_error;
321 use synom::PResult;
David Tolnay360efd22018-01-04 23:35:26 -0800322 use proc_macro2::{Spacing, Span, TokenNode, TokenTree, Literal};
Alex Crichtonccbb45d2017-05-23 10:58:24 -0700323
David Tolnayf800fc12017-12-27 22:08:48 -0500324 fn eq(span: Span) -> TokenTree {
Alex Crichton954046c2017-05-30 21:49:42 -0700325 TokenTree {
David Tolnayf800fc12017-12-27 22:08:48 -0500326 span: span,
Alex Crichtonf9e8f1a2017-07-05 18:20:44 -0700327 kind: TokenNode::Op('=', Spacing::Alone),
Alex Crichton954046c2017-05-30 21:49:42 -0700328 }
Alex Crichtonccbb45d2017-05-23 10:58:24 -0700329 }
330
Alex Crichton954046c2017-05-30 21:49:42 -0700331 impl Attribute {
Michael Layzell92639a52017-06-01 00:07:44 -0400332 named!(pub parse_inner -> Self, alt!(
333 do_parse!(
David Tolnayf8db7ba2017-11-11 22:52:16 -0800334 pound: punct!(#) >>
335 bang: punct!(!) >>
Michael Layzell92639a52017-06-01 00:07:44 -0400336 path_and_tts: brackets!(tuple!(
David Tolnaye64213b2017-12-30 00:24:20 -0500337 call!(Path::parse_mod_style),
David Tolnay369f0c52017-12-27 01:50:45 -0500338 syn!(TokenStream)
Michael Layzell92639a52017-06-01 00:07:44 -0400339 )) >>
340 ({
David Tolnay8875fca2017-12-31 13:52:37 -0500341 let (bracket, (path, tts)) = path_and_tts;
Alex Crichton954046c2017-05-30 21:49:42 -0700342
Michael Layzell92639a52017-06-01 00:07:44 -0400343 Attribute {
344 style: AttrStyle::Inner(bang),
345 path: path,
346 tts: tts,
347 is_sugared_doc: false,
348 pound_token: pound,
349 bracket_token: bracket,
Alex Crichton954046c2017-05-30 21:49:42 -0700350 }
Michael Layzell92639a52017-06-01 00:07:44 -0400351 })
352 )
353 |
354 map!(
David Tolnay201ef212018-01-01 00:09:14 -0500355 call!(lit_doc_comment, Comment::Inner),
David Tolnayf800fc12017-12-27 22:08:48 -0500356 |lit| {
357 let span = lit.span;
358 Attribute {
359 style: AttrStyle::Inner(<Token![!]>::new(span)),
360 path: Ident::new("doc", span).into(),
361 tts: vec![
362 eq(span),
363 lit,
364 ].into_iter().collect(),
365 is_sugared_doc: true,
366 pound_token: <Token![#]>::new(span),
367 bracket_token: token::Bracket(span),
368 }
Michael Layzell92639a52017-06-01 00:07:44 -0400369 }
370 )
371 ));
Alex Crichton954046c2017-05-30 21:49:42 -0700372
Michael Layzell92639a52017-06-01 00:07:44 -0400373 named!(pub parse_outer -> Self, alt!(
374 do_parse!(
David Tolnayf8db7ba2017-11-11 22:52:16 -0800375 pound: punct!(#) >>
Michael Layzell92639a52017-06-01 00:07:44 -0400376 path_and_tts: brackets!(tuple!(
David Tolnaye64213b2017-12-30 00:24:20 -0500377 call!(Path::parse_mod_style),
David Tolnay369f0c52017-12-27 01:50:45 -0500378 syn!(TokenStream)
Michael Layzell92639a52017-06-01 00:07:44 -0400379 )) >>
380 ({
David Tolnay8875fca2017-12-31 13:52:37 -0500381 let (bracket, (path, tts)) = path_and_tts;
Alex Crichton954046c2017-05-30 21:49:42 -0700382
Michael Layzell92639a52017-06-01 00:07:44 -0400383 Attribute {
Alex Crichton954046c2017-05-30 21:49:42 -0700384 style: AttrStyle::Outer,
Michael Layzell92639a52017-06-01 00:07:44 -0400385 path: path,
386 tts: tts,
387 is_sugared_doc: false,
388 pound_token: pound,
389 bracket_token: bracket,
Alex Crichton954046c2017-05-30 21:49:42 -0700390 }
Michael Layzell92639a52017-06-01 00:07:44 -0400391 })
392 )
393 |
394 map!(
David Tolnay201ef212018-01-01 00:09:14 -0500395 call!(lit_doc_comment, Comment::Outer),
David Tolnayf800fc12017-12-27 22:08:48 -0500396 |lit| {
397 let span = lit.span;
398 Attribute {
399 style: AttrStyle::Outer,
400 path: Ident::new("doc", span).into(),
401 tts: vec![
402 eq(span),
403 lit,
404 ].into_iter().collect(),
405 is_sugared_doc: true,
406 pound_token: <Token![#]>::new(span),
407 bracket_token: token::Bracket(span),
408 }
Michael Layzell92639a52017-06-01 00:07:44 -0400409 }
410 )
411 ));
Alex Crichtonccbb45d2017-05-23 10:58:24 -0700412 }
David Tolnayb79ee962016-09-04 09:39:20 -0700413
David Tolnay201ef212018-01-01 00:09:14 -0500414 enum Comment {
415 Inner,
416 Outer,
417 }
418
419 fn lit_doc_comment(input: Cursor, style: Comment) -> PResult<TokenTree> {
Michael Layzell589a8f42017-06-02 19:47:01 -0400420 match input.literal() {
David Tolnay65729482017-12-31 16:14:50 -0500421 Some((span, lit, rest)) => {
David Tolnay201ef212018-01-01 00:09:14 -0500422 let string = lit.to_string();
423 let ok = match style {
424 Comment::Inner => {
425 string.starts_with("//!") || string.starts_with("/*!")
426 }
427 Comment::Outer => {
428 string.starts_with("///") || string.starts_with("/**")
429 }
430 };
431 if ok {
David Tolnay51382052017-12-27 13:46:21 -0500432 Ok((
David Tolnay51382052017-12-27 13:46:21 -0500433 TokenTree {
434 span: span,
David Tolnay360efd22018-01-04 23:35:26 -0800435 kind: TokenNode::Literal(Literal::string(&string)),
David Tolnay51382052017-12-27 13:46:21 -0500436 },
David Tolnayf4aa6b42017-12-31 16:40:33 -0500437 rest,
David Tolnay51382052017-12-27 13:46:21 -0500438 ))
Michael Layzell589a8f42017-06-02 19:47:01 -0400439 } else {
440 parse_error()
441 }
442 }
David Tolnay51382052017-12-27 13:46:21 -0500443 _ => parse_error(),
Alex Crichton954046c2017-05-30 21:49:42 -0700444 }
445 }
David Tolnay9d8f1972016-09-04 11:58:48 -0700446}
David Tolnay87d0b442016-09-04 11:52:12 -0700447
448#[cfg(feature = "printing")]
449mod printing {
450 use super::*;
David Tolnay51382052017-12-27 13:46:21 -0500451 use quote::{ToTokens, Tokens};
David Tolnay360efd22018-01-04 23:35:26 -0800452 use proc_macro2::Literal;
David Tolnay87d0b442016-09-04 11:52:12 -0700453
454 impl ToTokens for Attribute {
455 fn to_tokens(&self, tokens: &mut Tokens) {
Arnavion44d2bf32017-04-19 02:47:55 -0700456 // If this was a sugared doc, emit it in its original form instead of `#[doc = "..."]`
Alex Crichtonccbb45d2017-05-23 10:58:24 -0700457 if self.is_sugared_doc {
458 if let Some(MetaItem::NameValue(ref pair)) = self.meta_item() {
459 if pair.ident == "doc" {
David Tolnay360efd22018-01-04 23:35:26 -0800460 if let Lit::Str(ref comment) = pair.lit {
461 tokens.append(TokenTree {
462 span: comment.span,
463 kind: TokenNode::Literal(Literal::doccomment(&comment.value())),
464 });
David Tolnay51382052017-12-27 13:46:21 -0500465 return;
David Tolnay14cbdeb2016-10-01 12:13:59 -0700466 }
David Tolnay4a51dc72016-10-01 00:40:31 -0700467 }
David Tolnayc91dd672016-10-01 01:03:56 -0700468 }
469 }
David Tolnay14cbdeb2016-10-01 12:13:59 -0700470
Alex Crichtonccbb45d2017-05-23 10:58:24 -0700471 self.pound_token.to_tokens(tokens);
472 if let AttrStyle::Inner(ref b) = self.style {
473 b.to_tokens(tokens);
David Tolnay14cbdeb2016-10-01 12:13:59 -0700474 }
Alex Crichtonccbb45d2017-05-23 10:58:24 -0700475 self.bracket_token.surround(tokens, |tokens| {
476 self.path.to_tokens(tokens);
David Tolnay360efd22018-01-04 23:35:26 -0800477 self.tts.to_tokens(tokens);
Alex Crichtonccbb45d2017-05-23 10:58:24 -0700478 });
David Tolnay87d0b442016-09-04 11:52:12 -0700479 }
480 }
481
Alex Crichton62a0a592017-05-22 13:58:53 -0700482 impl ToTokens for MetaItemList {
David Tolnay87d0b442016-09-04 11:52:12 -0700483 fn to_tokens(&self, tokens: &mut Tokens) {
Alex Crichton62a0a592017-05-22 13:58:53 -0700484 self.ident.to_tokens(tokens);
Alex Crichtonccbb45d2017-05-23 10:58:24 -0700485 self.paren_token.surround(tokens, |tokens| {
486 self.nested.to_tokens(tokens);
487 })
David Tolnay87d0b442016-09-04 11:52:12 -0700488 }
489 }
David Tolnayb7fa2b62016-10-30 10:50:47 -0700490
Alex Crichton62a0a592017-05-22 13:58:53 -0700491 impl ToTokens for MetaNameValue {
David Tolnayb7fa2b62016-10-30 10:50:47 -0700492 fn to_tokens(&self, tokens: &mut Tokens) {
Alex Crichton62a0a592017-05-22 13:58:53 -0700493 self.ident.to_tokens(tokens);
Alex Crichtonccbb45d2017-05-23 10:58:24 -0700494 self.eq_token.to_tokens(tokens);
Alex Crichton62a0a592017-05-22 13:58:53 -0700495 self.lit.to_tokens(tokens);
David Tolnayb7fa2b62016-10-30 10:50:47 -0700496 }
497 }
David Tolnay87d0b442016-09-04 11:52:12 -0700498}