blob: 30d8d96276a8525a70f64a1be0fe6cc1ac2d1a6d [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 Tolnayb79ee962016-09-04 09:39:20 -07006#[derive(Debug, Clone, Eq, PartialEq)]
7pub struct Attribute {
David Tolnay4a51dc72016-10-01 00:40:31 -07008 pub style: AttrStyle,
David Tolnayb79ee962016-09-04 09:39:20 -07009 pub value: MetaItem,
10 pub is_sugared_doc: bool,
11}
12
David Tolnay4a51dc72016-10-01 00:40:31 -070013/// Distinguishes between Attributes that decorate items and Attributes that
14/// are contained as statements within items. These two cases need to be
15/// distinguished for pretty-printing.
16#[derive(Debug, Copy, Clone, Eq, PartialEq)]
17pub enum AttrStyle {
18 Outer,
19 Inner,
20}
21
David Tolnayb79ee962016-09-04 09:39:20 -070022/// A compile-time attribute item.
23///
24/// E.g. `#[test]`, `#[derive(..)]` or `#[feature = "foo"]`
25#[derive(Debug, Clone, Eq, PartialEq)]
26pub enum MetaItem {
27 /// Word meta item.
28 ///
29 /// E.g. `test` as in `#[test]`
30 Word(Ident),
31 /// List meta item.
32 ///
33 /// E.g. `derive(..)` as in `#[derive(..)]`
34 List(Ident, Vec<MetaItem>),
35 /// Name value meta item.
36 ///
37 /// E.g. `feature = "foo"` as in `#[feature = "foo"]`
David Tolnayf4bbbd92016-09-23 14:41:55 -070038 NameValue(Ident, Lit),
David Tolnayb79ee962016-09-04 09:39:20 -070039}
40
David Tolnay8e661e22016-09-27 00:00:04 -070041impl MetaItem {
42 pub fn name(&self) -> &str {
43 match *self {
David Tolnay590cdfd2016-10-01 08:51:55 -070044 MetaItem::Word(ref name) |
45 MetaItem::List(ref name, _) |
David Tolnay8e661e22016-09-27 00:00:04 -070046 MetaItem::NameValue(ref name, _) => name.as_ref(),
47 }
48 }
49}
50
David Tolnay4a51dc72016-10-01 00:40:31 -070051pub trait FilterAttrs<'a> {
52 type Ret: Iterator<Item = &'a Attribute>;
53
54 fn outer(self) -> Self::Ret;
55 fn inner(self) -> Self::Ret;
56}
57
58impl<'a, T> FilterAttrs<'a> for T where T: IntoIterator<Item = &'a Attribute> {
59 type Ret = iter::Filter<T::IntoIter, fn(&&Attribute) -> bool>;
60
61 fn outer(self) -> Self::Ret {
62 fn is_outer(attr: &&Attribute) -> bool {
63 attr.style == AttrStyle::Outer
64 }
65 self.into_iter().filter(is_outer)
66 }
67
68 fn inner(self) -> Self::Ret {
69 fn is_inner(attr: &&Attribute) -> bool {
70 attr.style == AttrStyle::Inner
71 }
72 self.into_iter().filter(is_inner)
73 }
74}
75
David Tolnay86eca752016-09-04 11:26:41 -070076#[cfg(feature = "parsing")]
David Tolnay9d8f1972016-09-04 11:58:48 -070077pub mod parsing {
78 use super::*;
David Tolnay55337722016-09-11 12:58:56 -070079 use ident::parsing::ident;
David Tolnayf4bbbd92016-09-23 14:41:55 -070080 use lit::{Lit, StrStyle};
81 use lit::parsing::lit;
David Tolnay14cbdeb2016-10-01 12:13:59 -070082 use space::{block_comment, whitespace};
David Tolnayb79ee962016-09-04 09:39:20 -070083
David Tolnay2a2e67c2016-10-01 14:02:01 -070084 #[cfg(feature = "full")]
David Tolnay14cbdeb2016-10-01 12:13:59 -070085 named!(pub inner_attr -> Attribute, alt!(
86 do_parse!(
87 punct!("#") >>
88 punct!("!") >>
89 punct!("[") >>
90 meta_item: meta_item >>
91 punct!("]") >>
92 (Attribute {
93 style: AttrStyle::Inner,
94 value: meta_item,
95 is_sugared_doc: false,
96 })
97 )
98 |
99 do_parse!(
100 punct!("//!") >>
101 content: take_until!("\n") >>
102 (Attribute {
103 style: AttrStyle::Inner,
104 value: MetaItem::NameValue(
105 "doc".into(),
106 Lit::Str(
107 format!("//!{}", content),
108 StrStyle::Cooked,
109 ),
110 ),
111 is_sugared_doc: true,
112 })
113 )
114 |
115 do_parse!(
116 option!(whitespace) >>
117 peek!(tag!("/*!")) >>
118 com: block_comment >>
119 (Attribute {
120 style: AttrStyle::Inner,
121 value: MetaItem::NameValue(
122 "doc".into(),
123 Lit::Str(com.to_owned(), StrStyle::Cooked),
124 ),
125 is_sugared_doc: true,
126 })
127 )
David Tolnay4a51dc72016-10-01 00:40:31 -0700128 ));
129
130 named!(pub outer_attr -> Attribute, alt!(
David Tolnay9d8f1972016-09-04 11:58:48 -0700131 do_parse!(
132 punct!("#") >>
133 punct!("[") >>
134 meta_item: meta_item >>
135 punct!("]") >>
136 (Attribute {
David Tolnay4a51dc72016-10-01 00:40:31 -0700137 style: AttrStyle::Outer,
David Tolnay9d8f1972016-09-04 11:58:48 -0700138 value: meta_item,
139 is_sugared_doc: false,
140 })
141 )
142 |
143 do_parse!(
144 punct!("///") >>
David Tolnayc91dd672016-10-01 01:03:56 -0700145 not!(peek!(tag!("/"))) >>
David Tolnayb5a7b142016-09-13 22:46:39 -0700146 content: take_until!("\n") >>
David Tolnay9d8f1972016-09-04 11:58:48 -0700147 (Attribute {
David Tolnay4a51dc72016-10-01 00:40:31 -0700148 style: AttrStyle::Outer,
David Tolnay9d8f1972016-09-04 11:58:48 -0700149 value: MetaItem::NameValue(
David Tolnay42f50292016-09-04 13:54:21 -0700150 "doc".into(),
David Tolnayf4bbbd92016-09-23 14:41:55 -0700151 Lit::Str(
David Tolnayc91dd672016-10-01 01:03:56 -0700152 format!("///{}", content),
David Tolnayf4bbbd92016-09-23 14:41:55 -0700153 StrStyle::Cooked,
154 ),
David Tolnay9d8f1972016-09-04 11:58:48 -0700155 ),
156 is_sugared_doc: true,
157 })
158 )
David Tolnay14cbdeb2016-10-01 12:13:59 -0700159 |
160 do_parse!(
161 option!(whitespace) >>
162 peek!(tag!("/**")) >>
163 com: block_comment >>
164 (Attribute {
165 style: AttrStyle::Outer,
166 value: MetaItem::NameValue(
167 "doc".into(),
168 Lit::Str(com.to_owned(), StrStyle::Cooked),
169 ),
170 is_sugared_doc: true,
171 })
172 )
David Tolnay9d8f1972016-09-04 11:58:48 -0700173 ));
David Tolnayb79ee962016-09-04 09:39:20 -0700174
David Tolnayb5a7b142016-09-13 22:46:39 -0700175 named!(meta_item -> MetaItem, alt!(
David Tolnay9d8f1972016-09-04 11:58:48 -0700176 do_parse!(
David Tolnay55337722016-09-11 12:58:56 -0700177 id: ident >>
David Tolnay9d8f1972016-09-04 11:58:48 -0700178 punct!("(") >>
179 inner: separated_list!(punct!(","), meta_item) >>
180 punct!(")") >>
David Tolnay55337722016-09-11 12:58:56 -0700181 (MetaItem::List(id, inner))
David Tolnay9d8f1972016-09-04 11:58:48 -0700182 )
183 |
184 do_parse!(
David Tolnayf4bbbd92016-09-23 14:41:55 -0700185 name: ident >>
David Tolnay9d8f1972016-09-04 11:58:48 -0700186 punct!("=") >>
David Tolnayf4bbbd92016-09-23 14:41:55 -0700187 value: lit >>
188 (MetaItem::NameValue(name, value))
David Tolnay9d8f1972016-09-04 11:58:48 -0700189 )
190 |
David Tolnay55337722016-09-11 12:58:56 -0700191 map!(ident, MetaItem::Word)
David Tolnay9d8f1972016-09-04 11:58:48 -0700192 ));
193}
David Tolnay87d0b442016-09-04 11:52:12 -0700194
195#[cfg(feature = "printing")]
196mod printing {
197 use super::*;
David Tolnayc91dd672016-10-01 01:03:56 -0700198 use lit::{Lit, StrStyle};
David Tolnay87d0b442016-09-04 11:52:12 -0700199 use quote::{Tokens, ToTokens};
200
201 impl ToTokens for Attribute {
202 fn to_tokens(&self, tokens: &mut Tokens) {
David Tolnay14cbdeb2016-10-01 12:13:59 -0700203 if let Attribute {
204 style,
205 value: MetaItem::NameValue(
206 ref name,
207 Lit::Str(ref value, StrStyle::Cooked),
208 ),
209 is_sugared_doc: true,
210 } = *self {
211 if name == "doc" {
212 match style {
213 AttrStyle::Inner if value.starts_with("//!") => {
214 tokens.append(&format!("{}\n", value));
215 return;
216 }
217 AttrStyle::Inner if value.starts_with("/*!") => {
218 tokens.append(value);
219 return;
220 }
221 AttrStyle::Outer if value.starts_with("///") => {
222 tokens.append(&format!("{}\n", value));
223 return;
224 }
225 AttrStyle::Outer if value.starts_with("/**") => {
226 tokens.append(value);
227 return;
228 }
229 _ => {}
David Tolnay4a51dc72016-10-01 00:40:31 -0700230 }
David Tolnayc91dd672016-10-01 01:03:56 -0700231 }
232 }
David Tolnay14cbdeb2016-10-01 12:13:59 -0700233
234 tokens.append("#");
235 if let AttrStyle::Inner = self.style {
236 tokens.append("!");
237 }
238 tokens.append("[");
239 self.value.to_tokens(tokens);
240 tokens.append("]");
David Tolnay87d0b442016-09-04 11:52:12 -0700241 }
242 }
243
244 impl ToTokens for MetaItem {
245 fn to_tokens(&self, tokens: &mut Tokens) {
246 match *self {
David Tolnay26469072016-09-04 13:59:48 -0700247 MetaItem::Word(ref ident) => {
248 ident.to_tokens(tokens);
249 }
David Tolnay87d0b442016-09-04 11:52:12 -0700250 MetaItem::List(ref ident, ref inner) => {
David Tolnay26469072016-09-04 13:59:48 -0700251 ident.to_tokens(tokens);
David Tolnay87d0b442016-09-04 11:52:12 -0700252 tokens.append("(");
David Tolnay94ebdf92016-09-04 13:33:16 -0700253 tokens.append_separated(inner, ",");
David Tolnay87d0b442016-09-04 11:52:12 -0700254 tokens.append(")");
255 }
256 MetaItem::NameValue(ref name, ref value) => {
David Tolnay26469072016-09-04 13:59:48 -0700257 name.to_tokens(tokens);
David Tolnay87d0b442016-09-04 11:52:12 -0700258 tokens.append("=");
259 value.to_tokens(tokens);
260 }
261 }
262 }
263 }
264}