blob: 9e69494948d7c67e75e6a10fc4e19fd536a0e6f4 [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 Tolnaye303b7c2018-05-20 16:46:35 -070014use proc_macro2::{Delimiter, Spacing, 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! {
David Tolnay23557142018-01-06 22:45:40 -080022 /// An attribute like `#[repr(transparent)]`.
23 ///
David Tolnay461d98e2018-01-07 11:07:19 -080024 /// *This type is available if Syn is built with the `"derive"` or `"full"`
25 /// feature.*
26 ///
David Tolnay23557142018-01-06 22:45:40 -080027 /// # Syntax
28 ///
29 /// Rust has six types of attributes.
30 ///
31 /// - Outer attributes like `#[repr(transparent)]`. These appear outside or
32 /// in front of the item they describe.
33 /// - Inner attributes like `#![feature(proc_macro)]`. These appear inside
34 /// of the item they describe, usually a module.
35 /// - Outer doc comments like `/// # Example`.
36 /// - Inner doc comments like `//! Please file an issue`.
37 /// - Outer block comments `/** # Example */`.
38 /// - Inner block comments `/*! Please file an issue */`.
39 ///
40 /// The `style` field of type `AttrStyle` distinguishes whether an attribute
41 /// is outer or inner. Doc comments and block comments are promoted to
42 /// attributes that have `is_sugared_doc` set to true, as this is how they
43 /// are processed by the compiler and by `macro_rules!` macros.
44 ///
45 /// The `path` field gives the possibly colon-delimited path against which
46 /// the attribute is resolved. It is equal to `"doc"` for desugared doc
47 /// comments. The `tts` field contains the rest of the attribute body as
48 /// tokens.
49 ///
50 /// ```text
51 /// #[derive(Copy)] #[crate::precondition x < 5]
52 /// ^^^^^^~~~~~~ ^^^^^^^^^^^^^^^^^^^ ~~~~~
53 /// path tts path tts
54 /// ```
55 ///
David Tolnay068120a2018-01-06 23:17:22 -080056 /// Use the [`interpret_meta`] method to try parsing the tokens of an
57 /// attribute into the structured representation that is used by convention
58 /// across most Rust libraries.
David Tolnay23557142018-01-06 22:45:40 -080059 ///
David Tolnay068120a2018-01-06 23:17:22 -080060 /// [`interpret_meta`]: #method.interpret_meta
David Tolnay9c76bcb2017-12-26 23:14:59 -050061 pub struct Attribute #manual_extra_traits {
David Tolnayf8db7ba2017-11-11 22:52:16 -080062 pub pound_token: Token![#],
David Tolnay4a3f59a2017-12-28 21:21:12 -050063 pub style: AttrStyle,
David Tolnay32954ef2017-12-26 22:43:16 -050064 pub bracket_token: token::Bracket,
Alex Crichton62a0a592017-05-22 13:58:53 -070065 pub path: Path,
David Tolnay369f0c52017-12-27 01:50:45 -050066 pub tts: TokenStream,
Alex Crichton62a0a592017-05-22 13:58:53 -070067 pub is_sugared_doc: bool,
68 }
David Tolnayb79ee962016-09-04 09:39:20 -070069}
70
David Tolnay9c76bcb2017-12-26 23:14:59 -050071#[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 {
David Tolnay65fb5662018-05-20 20:02:28 -070077 self.style == other.style
78 && self.pound_token == other.pound_token
79 && self.bracket_token == other.bracket_token
80 && self.path == other.path
David Tolnay369f0c52017-12-27 01:50:45 -050081 && TokenStreamHelper(&self.tts) == TokenStreamHelper(&other.tts)
David Tolnay9c76bcb2017-12-26 23:14:59 -050082 && self.is_sugared_doc == other.is_sugared_doc
83 }
84}
85
86#[cfg(feature = "extra-traits")]
87impl Hash for Attribute {
88 fn hash<H>(&self, state: &mut H)
David Tolnay51382052017-12-27 13:46:21 -050089 where
90 H: Hasher,
David Tolnay9c76bcb2017-12-26 23:14:59 -050091 {
92 self.style.hash(state);
93 self.pound_token.hash(state);
94 self.bracket_token.hash(state);
95 self.path.hash(state);
David Tolnay369f0c52017-12-27 01:50:45 -050096 TokenStreamHelper(&self.tts).hash(state);
David Tolnay9c76bcb2017-12-26 23:14:59 -050097 self.is_sugared_doc.hash(state);
98 }
99}
100
David Tolnay02d77cc2016-10-02 09:52:08 -0700101impl Attribute {
David Tolnay068120a2018-01-06 23:17:22 -0800102 /// Parses the tokens after the path as a [`Meta`](enum.Meta.html) if
103 /// possible.
David Tolnayaaadd782018-01-06 22:58:13 -0800104 pub fn interpret_meta(&self) -> Option<Meta> {
Arnavion95f8a7a2017-04-19 03:29:56 -0700105 let name = if self.path.segments.len() == 1 {
David Tolnay56080682018-01-06 14:01:52 -0800106 &self.path.segments.first().unwrap().value().ident
Arnavion95f8a7a2017-04-19 03:29:56 -0700107 } else {
108 return None;
109 };
110
Arnavionbf395bf2017-04-15 15:35:22 -0700111 if self.tts.is_empty() {
Alex Crichtona74a1c82018-05-16 10:20:44 -0700112 return Some(Meta::Word(name.clone()));
Arnavionbf395bf2017-04-15 15:35:22 -0700113 }
114
David Tolnay369f0c52017-12-27 01:50:45 -0500115 let tts = self.tts.clone().into_iter().collect::<Vec<_>>();
116
117 if tts.len() == 1 {
Alex Crichtona74a1c82018-05-16 10:20:44 -0700118 if let Some(meta) = Attribute::extract_meta_list(name.clone(), &tts[0]) {
Alex Crichton9a4dca22018-03-28 06:32:19 -0700119 return Some(meta);
Arnavionbf395bf2017-04-15 15:35:22 -0700120 }
121 }
122
David Tolnay369f0c52017-12-27 01:50:45 -0500123 if tts.len() == 2 {
Alex Crichtona74a1c82018-05-16 10:20:44 -0700124 if let Some(meta) = Attribute::extract_name_value(name.clone(), &tts[0], &tts[1]) {
Alex Crichton9a4dca22018-03-28 06:32:19 -0700125 return Some(meta);
Arnavionbf395bf2017-04-15 15:35:22 -0700126 }
127 }
128
129 None
David Tolnay02d77cc2016-10-02 09:52:08 -0700130 }
Alex Crichton9a4dca22018-03-28 06:32:19 -0700131
132 fn extract_meta_list(ident: Ident, tt: &TokenTree) -> Option<Meta> {
133 let g = match *tt {
134 TokenTree::Group(ref g) => g,
135 _ => return None,
136 };
137 if g.delimiter() != Delimiter::Parenthesis {
David Tolnay94d2b792018-04-29 12:26:10 -0700138 return None;
Alex Crichton9a4dca22018-03-28 06:32:19 -0700139 }
140 let tokens = g.stream().clone().into_iter().collect::<Vec<_>>();
141 let nested = match list_of_nested_meta_items_from_tokens(&tokens) {
142 Some(n) => n,
143 None => return None,
144 };
145 Some(Meta::List(MetaList {
146 paren_token: token::Paren(g.span()),
147 ident: ident,
148 nested: nested,
149 }))
150 }
151
152 fn extract_name_value(ident: Ident, a: &TokenTree, b: &TokenTree) -> Option<Meta> {
153 let a = match *a {
Alex Crichtona74a1c82018-05-16 10:20:44 -0700154 TokenTree::Punct(ref o) => o,
Alex Crichton9a4dca22018-03-28 06:32:19 -0700155 _ => return None,
156 };
157 if a.spacing() != Spacing::Alone {
David Tolnay94d2b792018-04-29 12:26:10 -0700158 return None;
Alex Crichton9a4dca22018-03-28 06:32:19 -0700159 }
Alex Crichtona74a1c82018-05-16 10:20:44 -0700160 if a.as_char() != '=' {
David Tolnay94d2b792018-04-29 12:26:10 -0700161 return None;
Alex Crichton9a4dca22018-03-28 06:32:19 -0700162 }
163
164 match *b {
165 TokenTree::Literal(ref l) if !l.to_string().starts_with('/') => {
166 Some(Meta::NameValue(MetaNameValue {
167 ident: ident,
168 eq_token: Token![=]([a.span()]),
169 lit: Lit::new(l.clone()),
170 }))
171 }
David Tolnaya4319b72018-06-02 00:49:15 -0700172 TokenTree::Ident(ref v) => match &v.to_string()[..] {
David Tolnay94d2b792018-04-29 12:26:10 -0700173 v @ "true" | v @ "false" => Some(Meta::NameValue(MetaNameValue {
174 ident: ident,
175 eq_token: Token![=]([a.span()]),
176 lit: Lit::Bool(LitBool {
177 value: v == "true",
178 span: b.span(),
179 }),
180 })),
181 _ => None,
182 },
Alex Crichton9a4dca22018-03-28 06:32:19 -0700183 _ => None,
184 }
185 }
David Tolnay02d77cc2016-10-02 09:52:08 -0700186}
187
David Tolnayaaadd782018-01-06 22:58:13 -0800188fn nested_meta_item_from_tokens(tts: &[TokenTree]) -> Option<(NestedMeta, &[TokenTree])> {
Alex Crichtonccbb45d2017-05-23 10:58:24 -0700189 assert!(!tts.is_empty());
190
Alex Crichton9a4dca22018-03-28 06:32:19 -0700191 match tts[0] {
192 TokenTree::Literal(ref lit) => {
David Tolnay7037c9b2018-01-23 09:34:09 -0800193 if lit.to_string().starts_with('/') {
194 None
195 } else {
Alex Crichton9a4dca22018-03-28 06:32:19 -0700196 let lit = Lit::new(lit.clone());
David Tolnay7037c9b2018-01-23 09:34:09 -0800197 Some((NestedMeta::Literal(lit), &tts[1..]))
198 }
Alex Crichtonccbb45d2017-05-23 10:58:24 -0700199 }
200
Alex Crichtona74a1c82018-05-16 10:20:44 -0700201 TokenTree::Ident(ref ident) => {
Alex Crichtonccbb45d2017-05-23 10:58:24 -0700202 if tts.len() >= 3 {
Alex Crichtona74a1c82018-05-16 10:20:44 -0700203 if let Some(meta) = Attribute::extract_name_value(ident.clone(), &tts[1], &tts[2]) {
David Tolnay94d2b792018-04-29 12:26:10 -0700204 return Some((NestedMeta::Meta(meta), &tts[3..]));
Alex Crichtonccbb45d2017-05-23 10:58:24 -0700205 }
206 }
207
208 if tts.len() >= 2 {
Alex Crichtona74a1c82018-05-16 10:20:44 -0700209 if let Some(meta) = Attribute::extract_meta_list(ident.clone(), &tts[1]) {
David Tolnay94d2b792018-04-29 12:26:10 -0700210 return Some((NestedMeta::Meta(meta), &tts[2..]));
Alex Crichtonccbb45d2017-05-23 10:58:24 -0700211 }
212 }
213
Alex Crichtona74a1c82018-05-16 10:20:44 -0700214 Some((Meta::Word(ident.clone()).into(), &tts[1..]))
Alex Crichtonccbb45d2017-05-23 10:58:24 -0700215 }
216
David Tolnay51382052017-12-27 13:46:21 -0500217 _ => None,
Alex Crichtonccbb45d2017-05-23 10:58:24 -0700218 }
219}
220
David Tolnay51382052017-12-27 13:46:21 -0500221fn list_of_nested_meta_items_from_tokens(
222 mut tts: &[TokenTree],
David Tolnayaaadd782018-01-06 22:58:13 -0800223) -> Option<Punctuated<NestedMeta, Token![,]>> {
David Tolnayf2cfd722017-12-31 18:02:51 -0500224 let mut nested_meta_items = Punctuated::new();
Alex Crichtonccbb45d2017-05-23 10:58:24 -0700225 let mut first = true;
226
227 while !tts.is_empty() {
228 let prev_comma = if first {
229 first = false;
230 None
Alex Crichtona74a1c82018-05-16 10:20:44 -0700231 } else if let TokenTree::Punct(ref op) = tts[0] {
Alex Crichton9a4dca22018-03-28 06:32:19 -0700232 if op.spacing() != Spacing::Alone {
David Tolnay94d2b792018-04-29 12:26:10 -0700233 return None;
Alex Crichton9a4dca22018-03-28 06:32:19 -0700234 }
Alex Crichtona74a1c82018-05-16 10:20:44 -0700235 if op.as_char() != ',' {
David Tolnay94d2b792018-04-29 12:26:10 -0700236 return None;
Alex Crichton9a4dca22018-03-28 06:32:19 -0700237 }
238 let tok = Token![,]([op.span()]);
Alex Crichtonccbb45d2017-05-23 10:58:24 -0700239 tts = &tts[1..];
240 if tts.is_empty() {
David Tolnay51382052017-12-27 13:46:21 -0500241 break;
Alex Crichtonccbb45d2017-05-23 10:58:24 -0700242 }
243 Some(tok)
244 } else {
David Tolnay51382052017-12-27 13:46:21 -0500245 return None;
Alex Crichtonccbb45d2017-05-23 10:58:24 -0700246 };
247 let (nested, rest) = match nested_meta_item_from_tokens(tts) {
248 Some(pair) => pair,
249 None => return None,
250 };
David Tolnay660fd1f2017-12-31 01:52:57 -0500251 if let Some(comma) = prev_comma {
David Tolnaya0834b42018-01-01 21:30:02 -0800252 nested_meta_items.push_punct(comma);
Alex Crichtonccbb45d2017-05-23 10:58:24 -0700253 }
David Tolnay56080682018-01-06 14:01:52 -0800254 nested_meta_items.push_value(nested);
Alex Crichtonccbb45d2017-05-23 10:58:24 -0700255 tts = rest;
256 }
257
David Tolnayf2cfd722017-12-31 18:02:51 -0500258 Some(nested_meta_items)
Alex Crichtonccbb45d2017-05-23 10:58:24 -0700259}
260
Alex Crichton62a0a592017-05-22 13:58:53 -0700261ast_enum! {
David Tolnay05658502018-01-07 09:56:37 -0800262 /// Distinguishes between attributes that decorate an item and attributes
263 /// that are contained within an item.
David Tolnay23557142018-01-06 22:45:40 -0800264 ///
David Tolnay461d98e2018-01-07 11:07:19 -0800265 /// *This type is available if Syn is built with the `"derive"` or `"full"`
266 /// feature.*
267 ///
David Tolnay23557142018-01-06 22:45:40 -0800268 /// # Outer attributes
269 ///
270 /// - `#[repr(transparent)]`
271 /// - `/// # Example`
272 /// - `/** Please file an issue */`
273 ///
274 /// # Inner attributes
275 ///
276 /// - `#![feature(proc_macro)]`
277 /// - `//! # Example`
278 /// - `/*! Please file an issue */`
Alex Crichton2e0229c2017-05-23 09:34:50 -0700279 #[cfg_attr(feature = "clone-impls", derive(Copy))]
Alex Crichton62a0a592017-05-22 13:58:53 -0700280 pub enum AttrStyle {
Alex Crichton62a0a592017-05-22 13:58:53 -0700281 Outer,
David Tolnayf8db7ba2017-11-11 22:52:16 -0800282 Inner(Token![!]),
Alex Crichton62a0a592017-05-22 13:58:53 -0700283 }
David Tolnay4a51dc72016-10-01 00:40:31 -0700284}
285
Alex Crichton62a0a592017-05-22 13:58:53 -0700286ast_enum_of_structs! {
David Tolnay068120a2018-01-06 23:17:22 -0800287 /// Content of a compile-time structured attribute.
David Tolnayb79ee962016-09-04 09:39:20 -0700288 ///
David Tolnay461d98e2018-01-07 11:07:19 -0800289 /// *This type is available if Syn is built with the `"derive"` or `"full"`
290 /// feature.*
291 ///
David Tolnay068120a2018-01-06 23:17:22 -0800292 /// ## Word
293 ///
294 /// A meta word is like the `test` in `#[test]`.
295 ///
296 /// ## List
297 ///
298 /// A meta list is like the `derive(Copy)` in `#[derive(Copy)]`.
299 ///
300 /// ## NameValue
301 ///
302 /// A name-value meta is like the `path = "..."` in `#[path =
303 /// "sys/windows.rs"]`.
David Tolnay614a0142018-01-07 10:25:43 -0800304 ///
305 /// # Syntax tree enum
306 ///
307 /// This type is a [syntax tree enum].
308 ///
309 /// [syntax tree enum]: enum.Expr.html#syntax-tree-enums
David Tolnayaaadd782018-01-06 22:58:13 -0800310 pub enum Meta {
David Tolnayaaadd782018-01-06 22:58:13 -0800311 pub Word(Ident),
David Tolnay068120a2018-01-06 23:17:22 -0800312 /// A structured list within an attribute, like `derive(Copy, Clone)`.
David Tolnay461d98e2018-01-07 11:07:19 -0800313 ///
314 /// *This type is available if Syn is built with the `"derive"` or
315 /// `"full"` feature.*
David Tolnayaaadd782018-01-06 22:58:13 -0800316 pub List(MetaList {
Alex Crichton62a0a592017-05-22 13:58:53 -0700317 pub ident: Ident,
David Tolnay32954ef2017-12-26 22:43:16 -0500318 pub paren_token: token::Paren,
David Tolnayaaadd782018-01-06 22:58:13 -0800319 pub nested: Punctuated<NestedMeta, Token![,]>,
Alex Crichton62a0a592017-05-22 13:58:53 -0700320 }),
David Tolnay068120a2018-01-06 23:17:22 -0800321 /// A name-value pair within an attribute, like `feature = "nightly"`.
David Tolnay461d98e2018-01-07 11:07:19 -0800322 ///
323 /// *This type is available if Syn is built with the `"derive"` or
324 /// `"full"` feature.*
Alex Crichton62a0a592017-05-22 13:58:53 -0700325 pub NameValue(MetaNameValue {
Alex Crichton62a0a592017-05-22 13:58:53 -0700326 pub ident: Ident,
David Tolnayf8db7ba2017-11-11 22:52:16 -0800327 pub eq_token: Token![=],
Alex Crichton62a0a592017-05-22 13:58:53 -0700328 pub lit: Lit,
329 }),
330 }
Arnavion95f8a7a2017-04-19 03:29:56 -0700331}
332
David Tolnayaaadd782018-01-06 22:58:13 -0800333impl Meta {
David Tolnay068120a2018-01-06 23:17:22 -0800334 /// Returns the identifier that begins this structured meta item.
Arnavion95f8a7a2017-04-19 03:29:56 -0700335 ///
David Tolnay068120a2018-01-06 23:17:22 -0800336 /// For example this would return the `test` in `#[test]`, the `derive` in
337 /// `#[derive(Copy)]`, and the `path` in `#[path = "sys/windows.rs"]`.
338 pub fn name(&self) -> Ident {
Arnavion95f8a7a2017-04-19 03:29:56 -0700339 match *self {
Alex Crichtona74a1c82018-05-16 10:20:44 -0700340 Meta::Word(ref meta) => meta.clone(),
341 Meta::List(ref meta) => meta.ident.clone(),
342 Meta::NameValue(ref meta) => meta.ident.clone(),
Arnavion95f8a7a2017-04-19 03:29:56 -0700343 }
344 }
David Tolnay8e661e22016-09-27 00:00:04 -0700345}
346
Alex Crichton62a0a592017-05-22 13:58:53 -0700347ast_enum_of_structs! {
David Tolnay068120a2018-01-06 23:17:22 -0800348 /// Element of a compile-time attribute list.
David Tolnay461d98e2018-01-07 11:07:19 -0800349 ///
350 /// *This type is available if Syn is built with the `"derive"` or `"full"`
351 /// feature.*
David Tolnayaaadd782018-01-06 22:58:13 -0800352 pub enum NestedMeta {
David Tolnay068120a2018-01-06 23:17:22 -0800353 /// A structured meta item, like the `Copy` in `#[derive(Copy)]` which
354 /// would be a nested `Meta::Word`.
David Tolnayaaadd782018-01-06 22:58:13 -0800355 pub Meta(Meta),
Clar Charrd22b5702017-03-10 15:24:56 -0500356
David Tolnay068120a2018-01-06 23:17:22 -0800357 /// A Rust literal, like the `"new_name"` in `#[rename("new_name")]`.
Alex Crichton62a0a592017-05-22 13:58:53 -0700358 pub Literal(Lit),
359 }
David Tolnayb7fa2b62016-10-30 10:50:47 -0700360}
361
David Tolnay4a51dc72016-10-01 00:40:31 -0700362pub trait FilterAttrs<'a> {
363 type Ret: Iterator<Item = &'a Attribute>;
364
365 fn outer(self) -> Self::Ret;
366 fn inner(self) -> Self::Ret;
367}
368
David Tolnaydaaf7742016-10-03 11:11:43 -0700369impl<'a, T> FilterAttrs<'a> for T
David Tolnay51382052017-12-27 13:46:21 -0500370where
371 T: IntoIterator<Item = &'a Attribute>,
David Tolnaydaaf7742016-10-03 11:11:43 -0700372{
David Tolnay4a51dc72016-10-01 00:40:31 -0700373 type Ret = iter::Filter<T::IntoIter, fn(&&Attribute) -> bool>;
374
375 fn outer(self) -> Self::Ret {
376 fn is_outer(attr: &&Attribute) -> bool {
Alex Crichton2e0229c2017-05-23 09:34:50 -0700377 match attr.style {
378 AttrStyle::Outer => true,
379 _ => false,
380 }
David Tolnay4a51dc72016-10-01 00:40:31 -0700381 }
382 self.into_iter().filter(is_outer)
383 }
384
385 fn inner(self) -> Self::Ret {
386 fn is_inner(attr: &&Attribute) -> bool {
Alex Crichton2e0229c2017-05-23 09:34:50 -0700387 match attr.style {
Alex Crichtonccbb45d2017-05-23 10:58:24 -0700388 AttrStyle::Inner(_) => true,
Alex Crichton2e0229c2017-05-23 09:34:50 -0700389 _ => false,
390 }
David Tolnay4a51dc72016-10-01 00:40:31 -0700391 }
392 self.into_iter().filter(is_inner)
393 }
394}
395
David Tolnay86eca752016-09-04 11:26:41 -0700396#[cfg(feature = "parsing")]
David Tolnay9d8f1972016-09-04 11:58:48 -0700397pub mod parsing {
398 use super::*;
David Tolnaydfc886b2018-01-06 08:03:09 -0800399 use buffer::Cursor;
David Tolnay203557a2017-12-27 23:59:33 -0500400 use parse_error;
Alex Crichtona74a1c82018-05-16 10:20:44 -0700401 use proc_macro2::{Literal, Punct, Spacing, Span, TokenTree};
David Tolnay203557a2017-12-27 23:59:33 -0500402 use synom::PResult;
Alex Crichtonccbb45d2017-05-23 10:58:24 -0700403
David Tolnayf800fc12017-12-27 22:08:48 -0500404 fn eq(span: Span) -> TokenTree {
Alex Crichtona74a1c82018-05-16 10:20:44 -0700405 let mut op = Punct::new('=', Spacing::Alone);
Alex Crichton9a4dca22018-03-28 06:32:19 -0700406 op.set_span(span);
407 op.into()
Alex Crichtonccbb45d2017-05-23 10:58:24 -0700408 }
409
Alex Crichton954046c2017-05-30 21:49:42 -0700410 impl Attribute {
Michael Layzell92639a52017-06-01 00:07:44 -0400411 named!(pub parse_inner -> Self, alt!(
412 do_parse!(
David Tolnayf8db7ba2017-11-11 22:52:16 -0800413 pound: punct!(#) >>
414 bang: punct!(!) >>
Michael Layzell92639a52017-06-01 00:07:44 -0400415 path_and_tts: brackets!(tuple!(
David Tolnaye64213b2017-12-30 00:24:20 -0500416 call!(Path::parse_mod_style),
David Tolnay369f0c52017-12-27 01:50:45 -0500417 syn!(TokenStream)
Michael Layzell92639a52017-06-01 00:07:44 -0400418 )) >>
419 ({
David Tolnay8875fca2017-12-31 13:52:37 -0500420 let (bracket, (path, tts)) = path_and_tts;
Alex Crichton954046c2017-05-30 21:49:42 -0700421
Michael Layzell92639a52017-06-01 00:07:44 -0400422 Attribute {
423 style: AttrStyle::Inner(bang),
424 path: path,
425 tts: tts,
426 is_sugared_doc: false,
427 pound_token: pound,
428 bracket_token: bracket,
Alex Crichton954046c2017-05-30 21:49:42 -0700429 }
Michael Layzell92639a52017-06-01 00:07:44 -0400430 })
431 )
432 |
433 map!(
David Tolnay201ef212018-01-01 00:09:14 -0500434 call!(lit_doc_comment, Comment::Inner),
David Tolnayf800fc12017-12-27 22:08:48 -0500435 |lit| {
Alex Crichton9a4dca22018-03-28 06:32:19 -0700436 let span = lit.span();
David Tolnayf800fc12017-12-27 22:08:48 -0500437 Attribute {
438 style: AttrStyle::Inner(<Token![!]>::new(span)),
439 path: Ident::new("doc", span).into(),
440 tts: vec![
441 eq(span),
442 lit,
443 ].into_iter().collect(),
444 is_sugared_doc: true,
445 pound_token: <Token![#]>::new(span),
446 bracket_token: token::Bracket(span),
447 }
Michael Layzell92639a52017-06-01 00:07:44 -0400448 }
449 )
450 ));
Alex Crichton954046c2017-05-30 21:49:42 -0700451
Michael Layzell92639a52017-06-01 00:07:44 -0400452 named!(pub parse_outer -> Self, alt!(
453 do_parse!(
David Tolnayf8db7ba2017-11-11 22:52:16 -0800454 pound: punct!(#) >>
Michael Layzell92639a52017-06-01 00:07:44 -0400455 path_and_tts: brackets!(tuple!(
David Tolnaye64213b2017-12-30 00:24:20 -0500456 call!(Path::parse_mod_style),
David Tolnay369f0c52017-12-27 01:50:45 -0500457 syn!(TokenStream)
Michael Layzell92639a52017-06-01 00:07:44 -0400458 )) >>
459 ({
David Tolnay8875fca2017-12-31 13:52:37 -0500460 let (bracket, (path, tts)) = path_and_tts;
Alex Crichton954046c2017-05-30 21:49:42 -0700461
Michael Layzell92639a52017-06-01 00:07:44 -0400462 Attribute {
Alex Crichton954046c2017-05-30 21:49:42 -0700463 style: AttrStyle::Outer,
Michael Layzell92639a52017-06-01 00:07:44 -0400464 path: path,
465 tts: tts,
466 is_sugared_doc: false,
467 pound_token: pound,
468 bracket_token: bracket,
Alex Crichton954046c2017-05-30 21:49:42 -0700469 }
Michael Layzell92639a52017-06-01 00:07:44 -0400470 })
471 )
472 |
473 map!(
David Tolnay201ef212018-01-01 00:09:14 -0500474 call!(lit_doc_comment, Comment::Outer),
David Tolnayf800fc12017-12-27 22:08:48 -0500475 |lit| {
Alex Crichton9a4dca22018-03-28 06:32:19 -0700476 let span = lit.span();
David Tolnayf800fc12017-12-27 22:08:48 -0500477 Attribute {
478 style: AttrStyle::Outer,
479 path: Ident::new("doc", span).into(),
480 tts: vec![
481 eq(span),
482 lit,
483 ].into_iter().collect(),
484 is_sugared_doc: true,
485 pound_token: <Token![#]>::new(span),
486 bracket_token: token::Bracket(span),
487 }
Michael Layzell92639a52017-06-01 00:07:44 -0400488 }
489 )
490 ));
Alex Crichtonccbb45d2017-05-23 10:58:24 -0700491 }
David Tolnayb79ee962016-09-04 09:39:20 -0700492
David Tolnay201ef212018-01-01 00:09:14 -0500493 enum Comment {
494 Inner,
495 Outer,
496 }
497
498 fn lit_doc_comment(input: Cursor, style: Comment) -> PResult<TokenTree> {
Michael Layzell589a8f42017-06-02 19:47:01 -0400499 match input.literal() {
Alex Crichton9a4dca22018-03-28 06:32:19 -0700500 Some((lit, rest)) => {
David Tolnay201ef212018-01-01 00:09:14 -0500501 let string = lit.to_string();
502 let ok = match style {
David Tolnay61037c62018-01-05 16:21:03 -0800503 Comment::Inner => string.starts_with("//!") || string.starts_with("/*!"),
504 Comment::Outer => string.starts_with("///") || string.starts_with("/**"),
David Tolnay201ef212018-01-01 00:09:14 -0500505 };
506 if ok {
Alex Crichton9a4dca22018-03-28 06:32:19 -0700507 let mut new = Literal::string(&string);
508 new.set_span(lit.span());
509 Ok((new.into(), rest))
Michael Layzell589a8f42017-06-02 19:47:01 -0400510 } else {
511 parse_error()
512 }
513 }
David Tolnay51382052017-12-27 13:46:21 -0500514 _ => parse_error(),
Alex Crichton954046c2017-05-30 21:49:42 -0700515 }
516 }
David Tolnay9d8f1972016-09-04 11:58:48 -0700517}
David Tolnay87d0b442016-09-04 11:52:12 -0700518
519#[cfg(feature = "printing")]
520mod printing {
521 use super::*;
Alex Crichtona74a1c82018-05-16 10:20:44 -0700522 use proc_macro2::TokenStream;
523 use quote::ToTokens;
David Tolnay87d0b442016-09-04 11:52:12 -0700524
525 impl ToTokens for Attribute {
Alex Crichtona74a1c82018-05-16 10:20:44 -0700526 fn to_tokens(&self, tokens: &mut TokenStream) {
Alex Crichtonccbb45d2017-05-23 10:58:24 -0700527 self.pound_token.to_tokens(tokens);
528 if let AttrStyle::Inner(ref b) = self.style {
529 b.to_tokens(tokens);
David Tolnay14cbdeb2016-10-01 12:13:59 -0700530 }
Alex Crichtonccbb45d2017-05-23 10:58:24 -0700531 self.bracket_token.surround(tokens, |tokens| {
532 self.path.to_tokens(tokens);
David Tolnay360efd22018-01-04 23:35:26 -0800533 self.tts.to_tokens(tokens);
Alex Crichtonccbb45d2017-05-23 10:58:24 -0700534 });
David Tolnay87d0b442016-09-04 11:52:12 -0700535 }
536 }
537
David Tolnayaaadd782018-01-06 22:58:13 -0800538 impl ToTokens for MetaList {
Alex Crichtona74a1c82018-05-16 10:20:44 -0700539 fn to_tokens(&self, tokens: &mut TokenStream) {
Alex Crichton62a0a592017-05-22 13:58:53 -0700540 self.ident.to_tokens(tokens);
Alex Crichtonccbb45d2017-05-23 10:58:24 -0700541 self.paren_token.surround(tokens, |tokens| {
542 self.nested.to_tokens(tokens);
543 })
David Tolnay87d0b442016-09-04 11:52:12 -0700544 }
545 }
David Tolnayb7fa2b62016-10-30 10:50:47 -0700546
Alex Crichton62a0a592017-05-22 13:58:53 -0700547 impl ToTokens for MetaNameValue {
Alex Crichtona74a1c82018-05-16 10:20:44 -0700548 fn to_tokens(&self, tokens: &mut TokenStream) {
Alex Crichton62a0a592017-05-22 13:58:53 -0700549 self.ident.to_tokens(tokens);
Alex Crichtonccbb45d2017-05-23 10:58:24 -0700550 self.eq_token.to_tokens(tokens);
Alex Crichton62a0a592017-05-22 13:58:53 -0700551 self.lit.to_tokens(tokens);
David Tolnayb7fa2b62016-10-30 10:50:47 -0700552 }
553 }
David Tolnay87d0b442016-09-04 11:52:12 -0700554}