blob: f839ec4931c8e60d3d211497701a976be56b0d68 [file] [log] [blame]
David Tolnayb79ee962016-09-04 09:39:20 -07001use super::*;
2
David Tolnay4a51dc72016-10-01 00:40:31 -07003use std::iter;
4
Alex Crichton62a0a592017-05-22 13:58:53 -07005ast_struct! {
6 /// Doc-comments are promoted to attributes that have `is_sugared_doc` = true
7 pub struct Attribute {
8 pub style: AttrStyle,
Arnavion44d2bf32017-04-19 02:47:55 -07009
Alex Crichton62a0a592017-05-22 13:58:53 -070010 /// The path of the attribute.
11 ///
12 /// E.g. `derive` in `#[derive(Copy)]`
13 /// E.g. `crate::precondition` in `#[crate::precondition x < 5]`
14 pub path: Path,
Arnavion44d2bf32017-04-19 02:47:55 -070015
Alex Crichton62a0a592017-05-22 13:58:53 -070016 /// Any tokens after the path.
17 ///
18 /// E.g. `( Copy )` in `#[derive(Copy)]`
19 /// E.g. `x < 5` in `#[crate::precondition x < 5]`
20 pub tts: Vec<TokenTree>,
Arnavion44d2bf32017-04-19 02:47:55 -070021
Alex Crichton62a0a592017-05-22 13:58:53 -070022 pub is_sugared_doc: bool,
23 }
David Tolnayb79ee962016-09-04 09:39:20 -070024}
25
David Tolnay02d77cc2016-10-02 09:52:08 -070026impl Attribute {
Arnavion44d2bf32017-04-19 02:47:55 -070027 /// Parses the tokens after the path as a [`MetaItem`](enum.MetaItem.html) if possible.
Arnavionbf395bf2017-04-15 15:35:22 -070028 pub fn meta_item(&self) -> Option<MetaItem> {
Arnavion95f8a7a2017-04-19 03:29:56 -070029 let name = if self.path.segments.len() == 1 {
30 &self.path.segments[0].ident
31 } else {
32 return None;
33 };
34
Arnavionbf395bf2017-04-15 15:35:22 -070035 if self.tts.is_empty() {
Arnavion95f8a7a2017-04-19 03:29:56 -070036 return Some(MetaItem::Word(name.clone()));
Arnavionbf395bf2017-04-15 15:35:22 -070037 }
38
39 if self.tts.len() == 1 {
40 if let TokenTree::Delimited(Delimited { delim: DelimToken::Paren, ref tts }) = self.tts[0] {
41 fn nested_meta_item_from_tokens(tts: &[TokenTree]) -> Option<(NestedMetaItem, &[TokenTree])> {
42 assert!(!tts.is_empty());
43
44 match tts[0] {
45 TokenTree::Token(Token::Literal(ref lit)) => {
46 Some((NestedMetaItem::Literal(lit.clone()), &tts[1..]))
47 }
48
49 TokenTree::Token(Token::Ident(ref ident)) => {
50 if tts.len() >= 3 {
Arnavion24770612017-04-19 11:41:12 -070051 if let TokenTree::Token(Token::Eq) = tts[1] {
52 if let TokenTree::Token(Token::Literal(ref lit)) = tts[2] {
Alex Crichton62a0a592017-05-22 13:58:53 -070053 let pair = MetaNameValue {
54 ident: ident.clone(),
55 lit: lit.clone(),
56 };
57 return Some((NestedMetaItem::MetaItem(MetaItem::NameValue(pair)), &tts[3..]));
Arnavionbf395bf2017-04-15 15:35:22 -070058 }
Arnavionbf395bf2017-04-15 15:35:22 -070059 }
60 }
61
62 if tts.len() >= 2 {
63 if let TokenTree::Delimited(Delimited { delim: DelimToken::Paren, tts: ref inner_tts }) = tts[1] {
64 return match list_of_nested_meta_items_from_tokens(vec![], inner_tts) {
65 Some(nested_meta_items) => {
Alex Crichton62a0a592017-05-22 13:58:53 -070066 let list = MetaItemList {
67 ident: ident.clone(),
68 nested: nested_meta_items,
69 };
70 Some((NestedMetaItem::MetaItem(MetaItem::List(list)), &tts[2..]))
Arnavionbf395bf2017-04-15 15:35:22 -070071 }
72
73 None => None
74 };
75 }
76 }
77
Arnavion95f8a7a2017-04-19 03:29:56 -070078 Some((NestedMetaItem::MetaItem(MetaItem::Word(ident.clone())), &tts[1..]))
Arnavionbf395bf2017-04-15 15:35:22 -070079 }
80
81 _ => None
82 }
83 }
84
85 fn list_of_nested_meta_items_from_tokens(mut result: Vec<NestedMetaItem>, tts: &[TokenTree]) -> Option<Vec<NestedMetaItem>> {
86 if tts.is_empty() {
87 return Some(result);
88 }
89
90 match nested_meta_item_from_tokens(tts) {
91 Some((nested_meta_item, rest)) => {
92 result.push(nested_meta_item);
93 if rest.is_empty() {
94 list_of_nested_meta_items_from_tokens(result, rest)
95 }
Arnavionaebc06e2017-04-15 16:17:13 -070096 else if let TokenTree::Token(Token::Comma) = rest[0] {
97 list_of_nested_meta_items_from_tokens(result, &rest[1..])
98 }
Arnavionbf395bf2017-04-15 15:35:22 -070099 else {
Arnavionaebc06e2017-04-15 16:17:13 -0700100 None
Arnavionbf395bf2017-04-15 15:35:22 -0700101 }
102 }
103
104 None => None
105 }
106 }
107
108 if let Some(nested_meta_items) = list_of_nested_meta_items_from_tokens(vec![], tts) {
Alex Crichton62a0a592017-05-22 13:58:53 -0700109 return Some(MetaItem::List(MetaItemList {
110 ident: name.clone(),
111 nested: nested_meta_items,
112 }));
Arnavionbf395bf2017-04-15 15:35:22 -0700113 }
114 }
115 }
116
117 if self.tts.len() == 2 {
Arnavion24770612017-04-19 11:41:12 -0700118 if let TokenTree::Token(Token::Eq) = self.tts[0] {
119 if let TokenTree::Token(Token::Literal(ref lit)) = self.tts[1] {
Alex Crichton62a0a592017-05-22 13:58:53 -0700120 return Some(MetaItem::NameValue(MetaNameValue {
121 ident: name.clone(),
122 lit: lit.clone(),
123 }));
Arnavionbf395bf2017-04-15 15:35:22 -0700124 }
Arnavionbf395bf2017-04-15 15:35:22 -0700125 }
126 }
127
128 None
David Tolnay02d77cc2016-10-02 09:52:08 -0700129 }
130}
131
Alex Crichton62a0a592017-05-22 13:58:53 -0700132ast_enum! {
133 /// Distinguishes between Attributes that decorate items and Attributes that
134 /// are contained as statements within items. These two cases need to be
135 /// distinguished for pretty-printing.
Alex Crichton2e0229c2017-05-23 09:34:50 -0700136 #[cfg_attr(feature = "clone-impls", derive(Copy))]
Alex Crichton62a0a592017-05-22 13:58:53 -0700137 pub enum AttrStyle {
138 /// Attribute of the form `#![...]`.
139 Outer,
Clar Charrd22b5702017-03-10 15:24:56 -0500140
Alex Crichton62a0a592017-05-22 13:58:53 -0700141 /// Attribute of the form `#[...]`.
142 Inner,
143 }
David Tolnay4a51dc72016-10-01 00:40:31 -0700144}
145
Alex Crichton62a0a592017-05-22 13:58:53 -0700146ast_enum_of_structs! {
147 /// A compile-time attribute item.
David Tolnayb79ee962016-09-04 09:39:20 -0700148 ///
Alex Crichton62a0a592017-05-22 13:58:53 -0700149 /// E.g. `#[test]`, `#[derive(..)]` or `#[feature = "foo"]`
150 pub enum MetaItem {
151 /// Word meta item.
152 ///
153 /// E.g. `test` as in `#[test]`
154 pub Word(Ident),
Clar Charrd22b5702017-03-10 15:24:56 -0500155
Alex Crichton62a0a592017-05-22 13:58:53 -0700156 /// List meta item.
157 ///
158 /// E.g. `derive(..)` as in `#[derive(..)]`
159 pub List(MetaItemList {
160 /// Name of this attribute.
161 ///
162 /// E.g. `derive` in `#[derive(..)]`
163 pub ident: Ident,
Clar Charrd22b5702017-03-10 15:24:56 -0500164
Alex Crichton62a0a592017-05-22 13:58:53 -0700165 /// Arguments to this attribute
166 ///
167 /// E.g. `..` in `#[derive(..)]`
168 pub nested: Vec<NestedMetaItem>,
169 }),
170
171 /// Name-value meta item.
172 ///
173 /// E.g. `feature = "foo"` as in `#[feature = "foo"]`
174 pub NameValue(MetaNameValue {
175 /// Name of this attribute.
176 ///
177 /// E.g. `feature` in `#[feature = "foo"]`
178 pub ident: Ident,
179
180 /// Arguments to this attribute
181 ///
182 /// E.g. `"foo"` in `#[feature = "foo"]`
183 pub lit: Lit,
184 }),
185 }
Arnavion95f8a7a2017-04-19 03:29:56 -0700186}
187
188impl MetaItem {
189 /// Name of the item.
190 ///
191 /// E.g. `test` as in `#[test]`, `derive` as in `#[derive(..)]`, and
192 /// `feature` as in `#[feature = "foo"]`.
193 pub fn name(&self) -> &str {
194 match *self {
Alex Crichton62a0a592017-05-22 13:58:53 -0700195 MetaItem::Word(ref name) => name.as_ref(),
196 MetaItem::NameValue(ref pair) => pair.ident.as_ref(),
197 MetaItem::List(ref list) => list.ident.as_ref(),
Arnavion95f8a7a2017-04-19 03:29:56 -0700198 }
199 }
David Tolnay8e661e22016-09-27 00:00:04 -0700200}
201
Alex Crichton62a0a592017-05-22 13:58:53 -0700202ast_enum_of_structs! {
203 /// Possible values inside of compile-time attribute lists.
David Tolnayb7fa2b62016-10-30 10:50:47 -0700204 ///
Alex Crichton62a0a592017-05-22 13:58:53 -0700205 /// E.g. the '..' in `#[name(..)]`.
206 pub enum NestedMetaItem {
207 /// A full `MetaItem`.
208 ///
209 /// E.g. `Copy` in `#[derive(Copy)]` would be a `MetaItem::Word(Ident::from("Copy"))`.
210 pub MetaItem(MetaItem),
Clar Charrd22b5702017-03-10 15:24:56 -0500211
Alex Crichton62a0a592017-05-22 13:58:53 -0700212 /// A Rust literal.
213 ///
214 /// E.g. `"name"` in `#[rename("name")]`.
215 pub Literal(Lit),
216 }
David Tolnayb7fa2b62016-10-30 10:50:47 -0700217}
218
David Tolnay4a51dc72016-10-01 00:40:31 -0700219pub trait FilterAttrs<'a> {
220 type Ret: Iterator<Item = &'a Attribute>;
221
222 fn outer(self) -> Self::Ret;
223 fn inner(self) -> Self::Ret;
224}
225
David Tolnaydaaf7742016-10-03 11:11:43 -0700226impl<'a, T> FilterAttrs<'a> for T
227 where T: IntoIterator<Item = &'a Attribute>
228{
David Tolnay4a51dc72016-10-01 00:40:31 -0700229 type Ret = iter::Filter<T::IntoIter, fn(&&Attribute) -> bool>;
230
231 fn outer(self) -> Self::Ret {
232 fn is_outer(attr: &&Attribute) -> bool {
Alex Crichton2e0229c2017-05-23 09:34:50 -0700233 match attr.style {
234 AttrStyle::Outer => true,
235 _ => false,
236 }
David Tolnay4a51dc72016-10-01 00:40:31 -0700237 }
238 self.into_iter().filter(is_outer)
239 }
240
241 fn inner(self) -> Self::Ret {
242 fn is_inner(attr: &&Attribute) -> bool {
Alex Crichton2e0229c2017-05-23 09:34:50 -0700243 match attr.style {
244 AttrStyle::Inner => true,
245 _ => false,
246 }
David Tolnay4a51dc72016-10-01 00:40:31 -0700247 }
248 self.into_iter().filter(is_inner)
249 }
250}
251
David Tolnay86eca752016-09-04 11:26:41 -0700252#[cfg(feature = "parsing")]
David Tolnay9d8f1972016-09-04 11:58:48 -0700253pub mod parsing {
254 use super::*;
Arnavionbb252732017-04-15 16:15:15 -0700255 use lit::{Lit, StrStyle};
256 use mac::{Token, TokenTree};
Arnavioneaa2cff2017-04-15 03:32:13 -0700257 use mac::parsing::token_trees;
David Tolnay5fe14fc2017-01-27 16:22:08 -0800258 use synom::space::{block_comment, whitespace};
Arnavionf2dada12017-04-20 23:55:20 -0700259 use ty::parsing::mod_style_path;
David Tolnayb79ee962016-09-04 09:39:20 -0700260
David Tolnay2a2e67c2016-10-01 14:02:01 -0700261 #[cfg(feature = "full")]
David Tolnay14cbdeb2016-10-01 12:13:59 -0700262 named!(pub inner_attr -> Attribute, alt!(
263 do_parse!(
264 punct!("#") >>
265 punct!("!") >>
Arnavione43b1b02017-04-19 02:47:45 -0700266 path_and_tts: delimited!(
Arnavionbf395bf2017-04-15 15:35:22 -0700267 punct!("["),
Arnavionf2dada12017-04-20 23:55:20 -0700268 tuple!(mod_style_path, token_trees),
Arnavionbf395bf2017-04-15 15:35:22 -0700269 punct!("]")
270 ) >>
271 ({
Arnavione43b1b02017-04-19 02:47:45 -0700272 let (path, tts) = path_and_tts;
Arnavionbf395bf2017-04-15 15:35:22 -0700273
274 Attribute {
275 style: AttrStyle::Inner,
Arnavione43b1b02017-04-19 02:47:45 -0700276 path: path,
Arnavionbf395bf2017-04-15 15:35:22 -0700277 tts: tts,
278 is_sugared_doc: false,
279 }
David Tolnay14cbdeb2016-10-01 12:13:59 -0700280 })
281 )
282 |
283 do_parse!(
284 punct!("//!") >>
285 content: take_until!("\n") >>
286 (Attribute {
287 style: AttrStyle::Inner,
Arnavione43b1b02017-04-19 02:47:45 -0700288 path: "doc".into(),
Arnavionbf395bf2017-04-15 15:35:22 -0700289 tts: vec![
290 TokenTree::Token(Token::Eq),
291 TokenTree::Token(Token::Literal(Lit::Str(format!("//!{}", content).into(), StrStyle::Cooked))),
292 ],
David Tolnay14cbdeb2016-10-01 12:13:59 -0700293 is_sugared_doc: true,
294 })
295 )
296 |
297 do_parse!(
298 option!(whitespace) >>
299 peek!(tag!("/*!")) >>
300 com: block_comment >>
301 (Attribute {
302 style: AttrStyle::Inner,
Arnavione43b1b02017-04-19 02:47:45 -0700303 path: "doc".into(),
Arnavionbf395bf2017-04-15 15:35:22 -0700304 tts: vec![
305 TokenTree::Token(Token::Eq),
306 TokenTree::Token(Token::Literal(Lit::Str(com.into(), StrStyle::Cooked))),
307 ],
David Tolnay14cbdeb2016-10-01 12:13:59 -0700308 is_sugared_doc: true,
309 })
310 )
David Tolnay4a51dc72016-10-01 00:40:31 -0700311 ));
312
313 named!(pub outer_attr -> Attribute, alt!(
David Tolnay9d8f1972016-09-04 11:58:48 -0700314 do_parse!(
315 punct!("#") >>
Arnavione43b1b02017-04-19 02:47:45 -0700316 path_and_tts: delimited!(
Arnavionbf395bf2017-04-15 15:35:22 -0700317 punct!("["),
Arnavionf2dada12017-04-20 23:55:20 -0700318 tuple!(mod_style_path, token_trees),
Arnavionbf395bf2017-04-15 15:35:22 -0700319 punct!("]")
Arnavioneaa2cff2017-04-15 03:32:13 -0700320 ) >>
Arnavionbf395bf2017-04-15 15:35:22 -0700321 ({
Arnavione43b1b02017-04-19 02:47:45 -0700322 let (path, tts) = path_and_tts;
Arnavionbf395bf2017-04-15 15:35:22 -0700323
324 Attribute {
325 style: AttrStyle::Outer,
Arnavione43b1b02017-04-19 02:47:45 -0700326 path: path,
Arnavionbf395bf2017-04-15 15:35:22 -0700327 tts: tts,
328 is_sugared_doc: false,
329 }
Arnavioneaa2cff2017-04-15 03:32:13 -0700330 })
331 )
332 |
333 do_parse!(
David Tolnay9d8f1972016-09-04 11:58:48 -0700334 punct!("///") >>
David Tolnay1f16b602017-02-07 20:06:55 -0500335 not!(tag!("/")) >>
David Tolnayb5a7b142016-09-13 22:46:39 -0700336 content: take_until!("\n") >>
David Tolnay9d8f1972016-09-04 11:58:48 -0700337 (Attribute {
David Tolnay4a51dc72016-10-01 00:40:31 -0700338 style: AttrStyle::Outer,
Arnavione43b1b02017-04-19 02:47:45 -0700339 path: "doc".into(),
Arnavionbf395bf2017-04-15 15:35:22 -0700340 tts: vec![
341 TokenTree::Token(Token::Eq),
342 TokenTree::Token(Token::Literal(Lit::Str(format!("///{}", content).into(), StrStyle::Cooked))),
343 ],
David Tolnay9d8f1972016-09-04 11:58:48 -0700344 is_sugared_doc: true,
345 })
346 )
David Tolnay14cbdeb2016-10-01 12:13:59 -0700347 |
348 do_parse!(
349 option!(whitespace) >>
David Tolnay84aa0752016-10-02 23:01:13 -0700350 peek!(tuple!(tag!("/**"), not!(tag!("*")))) >>
David Tolnay14cbdeb2016-10-01 12:13:59 -0700351 com: block_comment >>
352 (Attribute {
353 style: AttrStyle::Outer,
Arnavione43b1b02017-04-19 02:47:45 -0700354 path: "doc".into(),
Arnavionbf395bf2017-04-15 15:35:22 -0700355 tts: vec![
356 TokenTree::Token(Token::Eq),
357 TokenTree::Token(Token::Literal(Lit::Str(com.into(), StrStyle::Cooked))),
358 ],
David Tolnay14cbdeb2016-10-01 12:13:59 -0700359 is_sugared_doc: true,
360 })
361 )
David Tolnay9d8f1972016-09-04 11:58:48 -0700362 ));
David Tolnay9d8f1972016-09-04 11:58:48 -0700363}
David Tolnay87d0b442016-09-04 11:52:12 -0700364
365#[cfg(feature = "printing")]
366mod printing {
367 use super::*;
David Tolnayc91dd672016-10-01 01:03:56 -0700368 use lit::{Lit, StrStyle};
Arnavionbb252732017-04-15 16:15:15 -0700369 use mac::{Token, TokenTree};
David Tolnay87d0b442016-09-04 11:52:12 -0700370 use quote::{Tokens, ToTokens};
Arnaviona115eba2017-04-19 03:05:52 -0700371 use ty::Path;
David Tolnay87d0b442016-09-04 11:52:12 -0700372
373 impl ToTokens for Attribute {
374 fn to_tokens(&self, tokens: &mut Tokens) {
Arnavion44d2bf32017-04-19 02:47:55 -0700375 // If this was a sugared doc, emit it in its original form instead of `#[doc = "..."]`
Arnavione43b1b02017-04-19 02:47:45 -0700376 match *self {
377 Attribute {
Alex Crichton2e0229c2017-05-23 09:34:50 -0700378 ref style,
Arnavione43b1b02017-04-19 02:47:45 -0700379 path: Path { global: false, ref segments },
380 ref tts,
381 is_sugared_doc: true,
382 } if segments.len() == 1 &&
383 segments[0].ident == "doc" &&
384 segments[0].parameters.is_empty() &&
385 tts.len() == 2 =>
386 {
Arnavion24770612017-04-19 11:41:12 -0700387 if let TokenTree::Token(Token::Eq) = self.tts[0] {
388 if let TokenTree::Token(Token::Literal(Lit::Str(ref value, StrStyle::Cooked))) = self.tts[1] {
Alex Crichton2e0229c2017-05-23 09:34:50 -0700389 match *style {
Arnavionbf395bf2017-04-15 15:35:22 -0700390 AttrStyle::Inner if value.starts_with("//!") => {
391 tokens.append(&format!("{}\n", value));
392 return;
393 }
394 AttrStyle::Inner if value.starts_with("/*!") => {
395 tokens.append(value);
396 return;
397 }
398 AttrStyle::Outer if value.starts_with("///") => {
399 tokens.append(&format!("{}\n", value));
400 return;
401 }
402 AttrStyle::Outer if value.starts_with("/**") => {
403 tokens.append(value);
404 return;
405 }
406 _ => {}
407 }
David Tolnay14cbdeb2016-10-01 12:13:59 -0700408 }
David Tolnay4a51dc72016-10-01 00:40:31 -0700409 }
David Tolnayc91dd672016-10-01 01:03:56 -0700410 }
Arnavione43b1b02017-04-19 02:47:45 -0700411
412 _ => {}
David Tolnayc91dd672016-10-01 01:03:56 -0700413 }
David Tolnay14cbdeb2016-10-01 12:13:59 -0700414
415 tokens.append("#");
416 if let AttrStyle::Inner = self.style {
417 tokens.append("!");
418 }
419 tokens.append("[");
Arnavione43b1b02017-04-19 02:47:45 -0700420 self.path.to_tokens(tokens);
Arnavionbf395bf2017-04-15 15:35:22 -0700421 tokens.append_all(&self.tts);
David Tolnay14cbdeb2016-10-01 12:13:59 -0700422 tokens.append("]");
David Tolnay87d0b442016-09-04 11:52:12 -0700423 }
424 }
425
Alex Crichton62a0a592017-05-22 13:58:53 -0700426 impl ToTokens for MetaItemList {
David Tolnay87d0b442016-09-04 11:52:12 -0700427 fn to_tokens(&self, tokens: &mut Tokens) {
Alex Crichton62a0a592017-05-22 13:58:53 -0700428 self.ident.to_tokens(tokens);
429 tokens.append("(");
430 tokens.append_separated(&self.nested, ",");
431 tokens.append(")");
David Tolnay87d0b442016-09-04 11:52:12 -0700432 }
433 }
David Tolnayb7fa2b62016-10-30 10:50:47 -0700434
Alex Crichton62a0a592017-05-22 13:58:53 -0700435 impl ToTokens for MetaNameValue {
David Tolnayb7fa2b62016-10-30 10:50:47 -0700436 fn to_tokens(&self, tokens: &mut Tokens) {
Alex Crichton62a0a592017-05-22 13:58:53 -0700437 self.ident.to_tokens(tokens);
438 tokens.append("=");
439 self.lit.to_tokens(tokens);
David Tolnayb7fa2b62016-10-30 10:50:47 -0700440 }
441 }
David Tolnay87d0b442016-09-04 11:52:12 -0700442}