blob: 14d0ccf9ebb2af7bbd34475a24cdfecb513eb9d2 [file] [log] [blame]
David Tolnayb79ee962016-09-04 09:39:20 -07001use super::*;
2
David Tolnay4a51dc72016-10-01 00:40:31 -07003use std::iter;
4
David Tolnayaed77b02016-09-23 20:50:31 -07005/// Doc-comments are promoted to attributes that have `is_sugared_doc` = true
David Tolnay9bf4af82017-01-07 11:17:46 -08006#[derive(Debug, Clone, Eq, PartialEq, Hash)]
David Tolnayb79ee962016-09-04 09:39:20 -07007pub struct Attribute {
David Tolnay4a51dc72016-10-01 00:40:31 -07008 pub style: AttrStyle,
Arnavione43b1b02017-04-19 02:47:45 -07009 pub path: Path,
Arnavionbf395bf2017-04-15 15:35:22 -070010 pub tts: Vec<TokenTree>,
David Tolnayb79ee962016-09-04 09:39:20 -070011 pub is_sugared_doc: bool,
12}
13
David Tolnay02d77cc2016-10-02 09:52:08 -070014impl Attribute {
Arnavionbf395bf2017-04-15 15:35:22 -070015 pub fn meta_item(&self) -> Option<MetaItem> {
16 if self.tts.is_empty() {
17 return Some(MetaItem::Word);
18 }
19
20 if self.tts.len() == 1 {
21 if let TokenTree::Delimited(Delimited { delim: DelimToken::Paren, ref tts }) = self.tts[0] {
22 fn nested_meta_item_from_tokens(tts: &[TokenTree]) -> Option<(NestedMetaItem, &[TokenTree])> {
23 assert!(!tts.is_empty());
24
25 match tts[0] {
26 TokenTree::Token(Token::Literal(ref lit)) => {
27 Some((NestedMetaItem::Literal(lit.clone()), &tts[1..]))
28 }
29
30 TokenTree::Token(Token::Ident(ref ident)) => {
31 if tts.len() >= 3 {
32 match (&tts[1], &tts[2]) {
33 (&TokenTree::Token(Token::Eq), &TokenTree::Token(Token::Literal(ref lit))) => {
34 return Some((NestedMetaItem::MetaItem(ident.clone(), MetaItem::NameValue(lit.clone())), &tts[3..]));
35 }
36
37 _ => {}
38 }
39 }
40
41 if tts.len() >= 2 {
42 if let TokenTree::Delimited(Delimited { delim: DelimToken::Paren, tts: ref inner_tts }) = tts[1] {
43 return match list_of_nested_meta_items_from_tokens(vec![], inner_tts) {
44 Some(nested_meta_items) => {
45 Some((NestedMetaItem::MetaItem(ident.clone(), MetaItem::List(nested_meta_items)), &tts[2..]))
46 }
47
48 None => None
49 };
50 }
51 }
52
53 Some((NestedMetaItem::MetaItem(ident.clone(), MetaItem::Word), &tts[1..]))
54 }
55
56 _ => None
57 }
58 }
59
60 fn list_of_nested_meta_items_from_tokens(mut result: Vec<NestedMetaItem>, tts: &[TokenTree]) -> Option<Vec<NestedMetaItem>> {
61 if tts.is_empty() {
62 return Some(result);
63 }
64
65 match nested_meta_item_from_tokens(tts) {
66 Some((nested_meta_item, rest)) => {
67 result.push(nested_meta_item);
68 if rest.is_empty() {
69 list_of_nested_meta_items_from_tokens(result, rest)
70 }
Arnavionaebc06e2017-04-15 16:17:13 -070071 else if let TokenTree::Token(Token::Comma) = rest[0] {
72 list_of_nested_meta_items_from_tokens(result, &rest[1..])
73 }
Arnavionbf395bf2017-04-15 15:35:22 -070074 else {
Arnavionaebc06e2017-04-15 16:17:13 -070075 None
Arnavionbf395bf2017-04-15 15:35:22 -070076 }
77 }
78
79 None => None
80 }
81 }
82
83 if let Some(nested_meta_items) = list_of_nested_meta_items_from_tokens(vec![], tts) {
84 return Some(MetaItem::List(nested_meta_items));
85 }
86 }
87 }
88
89 if self.tts.len() == 2 {
90 match (&self.tts[0], &self.tts[1]) {
91 (&TokenTree::Token(Token::Eq), &TokenTree::Token(Token::Literal(ref lit))) => {
92 return Some(MetaItem::NameValue(lit.clone()));
93 }
94
95 _ => {}
96 }
97 }
98
99 None
David Tolnay02d77cc2016-10-02 09:52:08 -0700100 }
101}
102
David Tolnay4a51dc72016-10-01 00:40:31 -0700103/// Distinguishes between Attributes that decorate items and Attributes that
104/// are contained as statements within items. These two cases need to be
105/// distinguished for pretty-printing.
David Tolnay9bf4af82017-01-07 11:17:46 -0800106#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
David Tolnay4a51dc72016-10-01 00:40:31 -0700107pub enum AttrStyle {
Clar Charrd22b5702017-03-10 15:24:56 -0500108 /// Attribute of the form `#![...]`.
David Tolnay4a51dc72016-10-01 00:40:31 -0700109 Outer,
Clar Charrd22b5702017-03-10 15:24:56 -0500110
111 /// Attribute of the form `#[...]`.
David Tolnay4a51dc72016-10-01 00:40:31 -0700112 Inner,
113}
114
David Tolnayb79ee962016-09-04 09:39:20 -0700115/// A compile-time attribute item.
116///
117/// E.g. `#[test]`, `#[derive(..)]` or `#[feature = "foo"]`
David Tolnay9bf4af82017-01-07 11:17:46 -0800118#[derive(Debug, Clone, Eq, PartialEq, Hash)]
David Tolnayb79ee962016-09-04 09:39:20 -0700119pub enum MetaItem {
120 /// Word meta item.
121 ///
122 /// E.g. `test` as in `#[test]`
Arnavionbf395bf2017-04-15 15:35:22 -0700123 Word,
Clar Charrd22b5702017-03-10 15:24:56 -0500124
David Tolnayb79ee962016-09-04 09:39:20 -0700125 /// List meta item.
126 ///
127 /// E.g. `derive(..)` as in `#[derive(..)]`
Arnavionbf395bf2017-04-15 15:35:22 -0700128 List(Vec<NestedMetaItem>),
Clar Charrd22b5702017-03-10 15:24:56 -0500129
130 /// Name-value meta item.
David Tolnayb79ee962016-09-04 09:39:20 -0700131 ///
132 /// E.g. `feature = "foo"` as in `#[feature = "foo"]`
Arnavionbf395bf2017-04-15 15:35:22 -0700133 NameValue(Lit),
David Tolnay8e661e22016-09-27 00:00:04 -0700134}
135
David Tolnayb7fa2b62016-10-30 10:50:47 -0700136/// Possible values inside of compile-time attribute lists.
137///
138/// E.g. the '..' in `#[name(..)]`.
David Tolnay9bf4af82017-01-07 11:17:46 -0800139#[derive(Debug, Clone, Eq, PartialEq, Hash)]
David Tolnayb7fa2b62016-10-30 10:50:47 -0700140pub enum NestedMetaItem {
Clar Charrd22b5702017-03-10 15:24:56 -0500141 /// A full `MetaItem`.
David Tolnayb7fa2b62016-10-30 10:50:47 -0700142 ///
Arnavionbf395bf2017-04-15 15:35:22 -0700143 /// E.g. `Copy` in `#[derive(Copy)]` would be a `(Ident::from("Copy"), MetaItem::Word)`.
144 MetaItem(Ident, MetaItem),
Clar Charrd22b5702017-03-10 15:24:56 -0500145
146 /// A Rust literal.
147 ///
148 /// E.g. `"name"` in `#[rename("name")]`.
David Tolnayb7fa2b62016-10-30 10:50:47 -0700149 Literal(Lit),
150}
151
David Tolnay4a51dc72016-10-01 00:40:31 -0700152pub trait FilterAttrs<'a> {
153 type Ret: Iterator<Item = &'a Attribute>;
154
155 fn outer(self) -> Self::Ret;
156 fn inner(self) -> Self::Ret;
157}
158
David Tolnaydaaf7742016-10-03 11:11:43 -0700159impl<'a, T> FilterAttrs<'a> for T
160 where T: IntoIterator<Item = &'a Attribute>
161{
David Tolnay4a51dc72016-10-01 00:40:31 -0700162 type Ret = iter::Filter<T::IntoIter, fn(&&Attribute) -> bool>;
163
164 fn outer(self) -> Self::Ret {
165 fn is_outer(attr: &&Attribute) -> bool {
166 attr.style == AttrStyle::Outer
167 }
168 self.into_iter().filter(is_outer)
169 }
170
171 fn inner(self) -> Self::Ret {
172 fn is_inner(attr: &&Attribute) -> bool {
173 attr.style == AttrStyle::Inner
174 }
175 self.into_iter().filter(is_inner)
176 }
177}
178
David Tolnay86eca752016-09-04 11:26:41 -0700179#[cfg(feature = "parsing")]
David Tolnay9d8f1972016-09-04 11:58:48 -0700180pub mod parsing {
181 use super::*;
Arnavionbb252732017-04-15 16:15:15 -0700182 use lit::{Lit, StrStyle};
183 use mac::{Token, TokenTree};
Arnavioneaa2cff2017-04-15 03:32:13 -0700184 use mac::parsing::token_trees;
David Tolnay5fe14fc2017-01-27 16:22:08 -0800185 use synom::space::{block_comment, whitespace};
Arnavione43b1b02017-04-19 02:47:45 -0700186 use ty::parsing::path;
David Tolnayb79ee962016-09-04 09:39:20 -0700187
David Tolnay2a2e67c2016-10-01 14:02:01 -0700188 #[cfg(feature = "full")]
David Tolnay14cbdeb2016-10-01 12:13:59 -0700189 named!(pub inner_attr -> Attribute, alt!(
190 do_parse!(
191 punct!("#") >>
192 punct!("!") >>
Arnavione43b1b02017-04-19 02:47:45 -0700193 path_and_tts: delimited!(
Arnavionbf395bf2017-04-15 15:35:22 -0700194 punct!("["),
Arnavione43b1b02017-04-19 02:47:45 -0700195 tuple!(path, token_trees),
Arnavionbf395bf2017-04-15 15:35:22 -0700196 punct!("]")
197 ) >>
198 ({
Arnavione43b1b02017-04-19 02:47:45 -0700199 let (path, tts) = path_and_tts;
Arnavionbf395bf2017-04-15 15:35:22 -0700200
201 Attribute {
202 style: AttrStyle::Inner,
Arnavione43b1b02017-04-19 02:47:45 -0700203 path: path,
Arnavionbf395bf2017-04-15 15:35:22 -0700204 tts: tts,
205 is_sugared_doc: false,
206 }
David Tolnay14cbdeb2016-10-01 12:13:59 -0700207 })
208 )
209 |
210 do_parse!(
211 punct!("//!") >>
212 content: take_until!("\n") >>
213 (Attribute {
214 style: AttrStyle::Inner,
Arnavione43b1b02017-04-19 02:47:45 -0700215 path: "doc".into(),
Arnavionbf395bf2017-04-15 15:35:22 -0700216 tts: vec![
217 TokenTree::Token(Token::Eq),
218 TokenTree::Token(Token::Literal(Lit::Str(format!("//!{}", content).into(), StrStyle::Cooked))),
219 ],
David Tolnay14cbdeb2016-10-01 12:13:59 -0700220 is_sugared_doc: true,
221 })
222 )
223 |
224 do_parse!(
225 option!(whitespace) >>
226 peek!(tag!("/*!")) >>
227 com: block_comment >>
228 (Attribute {
229 style: AttrStyle::Inner,
Arnavione43b1b02017-04-19 02:47:45 -0700230 path: "doc".into(),
Arnavionbf395bf2017-04-15 15:35:22 -0700231 tts: vec![
232 TokenTree::Token(Token::Eq),
233 TokenTree::Token(Token::Literal(Lit::Str(com.into(), StrStyle::Cooked))),
234 ],
David Tolnay14cbdeb2016-10-01 12:13:59 -0700235 is_sugared_doc: true,
236 })
237 )
David Tolnay4a51dc72016-10-01 00:40:31 -0700238 ));
239
240 named!(pub outer_attr -> Attribute, alt!(
David Tolnay9d8f1972016-09-04 11:58:48 -0700241 do_parse!(
242 punct!("#") >>
Arnavione43b1b02017-04-19 02:47:45 -0700243 path_and_tts: delimited!(
Arnavionbf395bf2017-04-15 15:35:22 -0700244 punct!("["),
Arnavione43b1b02017-04-19 02:47:45 -0700245 tuple!(path, token_trees),
Arnavionbf395bf2017-04-15 15:35:22 -0700246 punct!("]")
Arnavioneaa2cff2017-04-15 03:32:13 -0700247 ) >>
Arnavionbf395bf2017-04-15 15:35:22 -0700248 ({
Arnavione43b1b02017-04-19 02:47:45 -0700249 let (path, tts) = path_and_tts;
Arnavionbf395bf2017-04-15 15:35:22 -0700250
251 Attribute {
252 style: AttrStyle::Outer,
Arnavione43b1b02017-04-19 02:47:45 -0700253 path: path,
Arnavionbf395bf2017-04-15 15:35:22 -0700254 tts: tts,
255 is_sugared_doc: false,
256 }
Arnavioneaa2cff2017-04-15 03:32:13 -0700257 })
258 )
259 |
260 do_parse!(
David Tolnay9d8f1972016-09-04 11:58:48 -0700261 punct!("///") >>
David Tolnay1f16b602017-02-07 20:06:55 -0500262 not!(tag!("/")) >>
David Tolnayb5a7b142016-09-13 22:46:39 -0700263 content: take_until!("\n") >>
David Tolnay9d8f1972016-09-04 11:58:48 -0700264 (Attribute {
David Tolnay4a51dc72016-10-01 00:40:31 -0700265 style: AttrStyle::Outer,
Arnavione43b1b02017-04-19 02:47:45 -0700266 path: "doc".into(),
Arnavionbf395bf2017-04-15 15:35:22 -0700267 tts: vec![
268 TokenTree::Token(Token::Eq),
269 TokenTree::Token(Token::Literal(Lit::Str(format!("///{}", content).into(), StrStyle::Cooked))),
270 ],
David Tolnay9d8f1972016-09-04 11:58:48 -0700271 is_sugared_doc: true,
272 })
273 )
David Tolnay14cbdeb2016-10-01 12:13:59 -0700274 |
275 do_parse!(
276 option!(whitespace) >>
David Tolnay84aa0752016-10-02 23:01:13 -0700277 peek!(tuple!(tag!("/**"), not!(tag!("*")))) >>
David Tolnay14cbdeb2016-10-01 12:13:59 -0700278 com: block_comment >>
279 (Attribute {
280 style: AttrStyle::Outer,
Arnavione43b1b02017-04-19 02:47:45 -0700281 path: "doc".into(),
Arnavionbf395bf2017-04-15 15:35:22 -0700282 tts: vec![
283 TokenTree::Token(Token::Eq),
284 TokenTree::Token(Token::Literal(Lit::Str(com.into(), StrStyle::Cooked))),
285 ],
David Tolnay14cbdeb2016-10-01 12:13:59 -0700286 is_sugared_doc: true,
287 })
288 )
David Tolnay9d8f1972016-09-04 11:58:48 -0700289 ));
David Tolnay9d8f1972016-09-04 11:58:48 -0700290}
David Tolnay87d0b442016-09-04 11:52:12 -0700291
292#[cfg(feature = "printing")]
293mod printing {
294 use super::*;
David Tolnayc91dd672016-10-01 01:03:56 -0700295 use lit::{Lit, StrStyle};
Arnavionbb252732017-04-15 16:15:15 -0700296 use mac::{Token, TokenTree};
David Tolnay87d0b442016-09-04 11:52:12 -0700297 use quote::{Tokens, ToTokens};
298
299 impl ToTokens for Attribute {
300 fn to_tokens(&self, tokens: &mut Tokens) {
Arnavione43b1b02017-04-19 02:47:45 -0700301 match *self {
302 Attribute {
303 style,
304 path: Path { global: false, ref segments },
305 ref tts,
306 is_sugared_doc: true,
307 } if segments.len() == 1 &&
308 segments[0].ident == "doc" &&
309 segments[0].parameters.is_empty() &&
310 tts.len() == 2 =>
311 {
312 match (&self.tts[0], &self.tts[1]) {
Arnavionbf395bf2017-04-15 15:35:22 -0700313 (&TokenTree::Token(Token::Eq),
Arnavione43b1b02017-04-19 02:47:45 -0700314 &TokenTree::Token(Token::Literal(Lit::Str(ref value, StrStyle::Cooked)))) =>
315 {
Arnavionbf395bf2017-04-15 15:35:22 -0700316 match style {
317 AttrStyle::Inner if value.starts_with("//!") => {
318 tokens.append(&format!("{}\n", value));
319 return;
320 }
321 AttrStyle::Inner if value.starts_with("/*!") => {
322 tokens.append(value);
323 return;
324 }
325 AttrStyle::Outer if value.starts_with("///") => {
326 tokens.append(&format!("{}\n", value));
327 return;
328 }
329 AttrStyle::Outer if value.starts_with("/**") => {
330 tokens.append(value);
331 return;
332 }
333 _ => {}
334 }
David Tolnay14cbdeb2016-10-01 12:13:59 -0700335 }
Arnavionbf395bf2017-04-15 15:35:22 -0700336
David Tolnay14cbdeb2016-10-01 12:13:59 -0700337 _ => {}
David Tolnay4a51dc72016-10-01 00:40:31 -0700338 }
David Tolnayc91dd672016-10-01 01:03:56 -0700339 }
Arnavione43b1b02017-04-19 02:47:45 -0700340
341 _ => {}
David Tolnayc91dd672016-10-01 01:03:56 -0700342 }
David Tolnay14cbdeb2016-10-01 12:13:59 -0700343
344 tokens.append("#");
345 if let AttrStyle::Inner = self.style {
346 tokens.append("!");
347 }
348 tokens.append("[");
Arnavione43b1b02017-04-19 02:47:45 -0700349 self.path.to_tokens(tokens);
Arnavionbf395bf2017-04-15 15:35:22 -0700350 tokens.append_all(&self.tts);
David Tolnay14cbdeb2016-10-01 12:13:59 -0700351 tokens.append("]");
David Tolnay87d0b442016-09-04 11:52:12 -0700352 }
353 }
354
355 impl ToTokens for MetaItem {
356 fn to_tokens(&self, tokens: &mut Tokens) {
357 match *self {
Arnavionbf395bf2017-04-15 15:35:22 -0700358 MetaItem::Word => {}
359 MetaItem::List(ref inner) => {
David Tolnay87d0b442016-09-04 11:52:12 -0700360 tokens.append("(");
David Tolnay94ebdf92016-09-04 13:33:16 -0700361 tokens.append_separated(inner, ",");
David Tolnay87d0b442016-09-04 11:52:12 -0700362 tokens.append(")");
363 }
Arnavionbf395bf2017-04-15 15:35:22 -0700364 MetaItem::NameValue(ref value) => {
David Tolnay87d0b442016-09-04 11:52:12 -0700365 tokens.append("=");
366 value.to_tokens(tokens);
367 }
368 }
369 }
370 }
David Tolnayb7fa2b62016-10-30 10:50:47 -0700371
372 impl ToTokens for NestedMetaItem {
373 fn to_tokens(&self, tokens: &mut Tokens) {
374 match *self {
Arnavionbf395bf2017-04-15 15:35:22 -0700375 NestedMetaItem::MetaItem(ref ident, ref nested) => {
376 ident.to_tokens(tokens);
David Tolnayb7fa2b62016-10-30 10:50:47 -0700377 nested.to_tokens(tokens);
378 }
379 NestedMetaItem::Literal(ref lit) => {
380 lit.to_tokens(tokens);
381 }
382 }
383 }
384 }
David Tolnay87d0b442016-09-04 11:52:12 -0700385}