blob: 8b5ee18a90d2f1de3116be327e680d0838941201 [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,
David Tolnayb79ee962016-09-04 09:39:20 -07009 pub value: MetaItem,
10 pub is_sugared_doc: bool,
11}
12
David Tolnay02d77cc2016-10-02 09:52:08 -070013impl Attribute {
14 pub fn name(&self) -> &str {
15 self.value.name()
16 }
17}
18
David Tolnay4a51dc72016-10-01 00:40:31 -070019/// Distinguishes between Attributes that decorate items and Attributes that
20/// are contained as statements within items. These two cases need to be
21/// distinguished for pretty-printing.
David Tolnay9bf4af82017-01-07 11:17:46 -080022#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
David Tolnay4a51dc72016-10-01 00:40:31 -070023pub enum AttrStyle {
Clar Charrd22b5702017-03-10 15:24:56 -050024 /// Attribute of the form `#![...]`.
David Tolnay4a51dc72016-10-01 00:40:31 -070025 Outer,
Clar Charrd22b5702017-03-10 15:24:56 -050026
27 /// Attribute of the form `#[...]`.
David Tolnay4a51dc72016-10-01 00:40:31 -070028 Inner,
29}
30
David Tolnayb79ee962016-09-04 09:39:20 -070031/// A compile-time attribute item.
32///
33/// E.g. `#[test]`, `#[derive(..)]` or `#[feature = "foo"]`
David Tolnay9bf4af82017-01-07 11:17:46 -080034#[derive(Debug, Clone, Eq, PartialEq, Hash)]
David Tolnayb79ee962016-09-04 09:39:20 -070035pub enum MetaItem {
36 /// Word meta item.
37 ///
38 /// E.g. `test` as in `#[test]`
39 Word(Ident),
Clar Charrd22b5702017-03-10 15:24:56 -050040
David Tolnayb79ee962016-09-04 09:39:20 -070041 /// List meta item.
42 ///
43 /// E.g. `derive(..)` as in `#[derive(..)]`
David Tolnayb7fa2b62016-10-30 10:50:47 -070044 List(Ident, Vec<NestedMetaItem>),
Clar Charrd22b5702017-03-10 15:24:56 -050045
46 /// Name-value meta item.
David Tolnayb79ee962016-09-04 09:39:20 -070047 ///
48 /// E.g. `feature = "foo"` as in `#[feature = "foo"]`
David Tolnayf4bbbd92016-09-23 14:41:55 -070049 NameValue(Ident, Lit),
Arnavioneaa2cff2017-04-15 03:32:13 -070050
51 /// Tokens meta item.
52 ///
53 /// E.g. `test foo bar` as in `#[test foo bar]`
54 Tokens(Ident, Vec<TokenTree>),
David Tolnayb79ee962016-09-04 09:39:20 -070055}
56
David Tolnay8e661e22016-09-27 00:00:04 -070057impl MetaItem {
Clar Charrd22b5702017-03-10 15:24:56 -050058 /// Name of the item.
59 ///
60 /// E.g. `test` as in `#[test]`, `derive` as in `#[derive(..)]`, and
61 /// `feature` as in `#[feature = "foo"]`.
David Tolnay8e661e22016-09-27 00:00:04 -070062 pub fn name(&self) -> &str {
63 match *self {
David Tolnay590cdfd2016-10-01 08:51:55 -070064 MetaItem::Word(ref name) |
65 MetaItem::List(ref name, _) |
Arnavioneaa2cff2017-04-15 03:32:13 -070066 MetaItem::NameValue(ref name, _) |
67 MetaItem::Tokens(ref name, _) => name.as_ref(),
David Tolnay8e661e22016-09-27 00:00:04 -070068 }
69 }
70}
71
David Tolnayb7fa2b62016-10-30 10:50:47 -070072/// Possible values inside of compile-time attribute lists.
73///
74/// E.g. the '..' in `#[name(..)]`.
David Tolnay9bf4af82017-01-07 11:17:46 -080075#[derive(Debug, Clone, Eq, PartialEq, Hash)]
David Tolnayb7fa2b62016-10-30 10:50:47 -070076pub enum NestedMetaItem {
Clar Charrd22b5702017-03-10 15:24:56 -050077 /// A full `MetaItem`.
David Tolnayb7fa2b62016-10-30 10:50:47 -070078 ///
Clar Charrd22b5702017-03-10 15:24:56 -050079 /// E.g. `Copy` in `#[derive(Copy)]` would be a `MetaItem::Word(Ident::from("Copy"))`.
80 MetaItem(MetaItem),
81
82 /// A Rust literal.
83 ///
84 /// E.g. `"name"` in `#[rename("name")]`.
David Tolnayb7fa2b62016-10-30 10:50:47 -070085 Literal(Lit),
86}
87
David Tolnay4a51dc72016-10-01 00:40:31 -070088pub trait FilterAttrs<'a> {
89 type Ret: Iterator<Item = &'a Attribute>;
90
91 fn outer(self) -> Self::Ret;
92 fn inner(self) -> Self::Ret;
93}
94
David Tolnaydaaf7742016-10-03 11:11:43 -070095impl<'a, T> FilterAttrs<'a> for T
96 where T: IntoIterator<Item = &'a Attribute>
97{
David Tolnay4a51dc72016-10-01 00:40:31 -070098 type Ret = iter::Filter<T::IntoIter, fn(&&Attribute) -> bool>;
99
100 fn outer(self) -> Self::Ret {
101 fn is_outer(attr: &&Attribute) -> bool {
102 attr.style == AttrStyle::Outer
103 }
104 self.into_iter().filter(is_outer)
105 }
106
107 fn inner(self) -> Self::Ret {
108 fn is_inner(attr: &&Attribute) -> bool {
109 attr.style == AttrStyle::Inner
110 }
111 self.into_iter().filter(is_inner)
112 }
113}
114
David Tolnay86eca752016-09-04 11:26:41 -0700115#[cfg(feature = "parsing")]
David Tolnay9d8f1972016-09-04 11:58:48 -0700116pub mod parsing {
117 use super::*;
David Tolnay55337722016-09-11 12:58:56 -0700118 use ident::parsing::ident;
David Tolnayf4bbbd92016-09-23 14:41:55 -0700119 use lit::parsing::lit;
Arnavioneaa2cff2017-04-15 03:32:13 -0700120 use mac::parsing::token_trees;
David Tolnay5fe14fc2017-01-27 16:22:08 -0800121 use synom::space::{block_comment, whitespace};
David Tolnayb79ee962016-09-04 09:39:20 -0700122
David Tolnay2a2e67c2016-10-01 14:02:01 -0700123 #[cfg(feature = "full")]
David Tolnay14cbdeb2016-10-01 12:13:59 -0700124 named!(pub inner_attr -> Attribute, alt!(
125 do_parse!(
126 punct!("#") >>
127 punct!("!") >>
128 punct!("[") >>
129 meta_item: meta_item >>
130 punct!("]") >>
131 (Attribute {
132 style: AttrStyle::Inner,
133 value: meta_item,
134 is_sugared_doc: false,
135 })
136 )
137 |
138 do_parse!(
139 punct!("//!") >>
140 content: take_until!("\n") >>
141 (Attribute {
142 style: AttrStyle::Inner,
143 value: MetaItem::NameValue(
144 "doc".into(),
Pascal Hertleif36342c52016-10-19 10:31:42 +0200145 format!("//!{}", content).into(),
David Tolnay14cbdeb2016-10-01 12:13:59 -0700146 ),
147 is_sugared_doc: true,
148 })
149 )
150 |
151 do_parse!(
152 option!(whitespace) >>
153 peek!(tag!("/*!")) >>
154 com: block_comment >>
155 (Attribute {
156 style: AttrStyle::Inner,
157 value: MetaItem::NameValue(
158 "doc".into(),
Pascal Hertleif36342c52016-10-19 10:31:42 +0200159 com.into(),
David Tolnay14cbdeb2016-10-01 12:13:59 -0700160 ),
161 is_sugared_doc: true,
162 })
163 )
David Tolnay4a51dc72016-10-01 00:40:31 -0700164 ));
165
166 named!(pub outer_attr -> Attribute, alt!(
David Tolnay9d8f1972016-09-04 11:58:48 -0700167 do_parse!(
168 punct!("#") >>
169 punct!("[") >>
170 meta_item: meta_item >>
171 punct!("]") >>
172 (Attribute {
David Tolnay4a51dc72016-10-01 00:40:31 -0700173 style: AttrStyle::Outer,
David Tolnay9d8f1972016-09-04 11:58:48 -0700174 value: meta_item,
175 is_sugared_doc: false,
176 })
177 )
178 |
179 do_parse!(
Arnavioneaa2cff2017-04-15 03:32:13 -0700180 name_and_token_trees: preceded!(
181 punct!("#"),
182 delimited!(
183 punct!("["),
184 tuple!(ident, token_trees),
185 punct!("]")
186 )
187 ) >>
188 (Attribute {
189 style: AttrStyle::Outer,
190 value: {
191 let (name, token_trees) = name_and_token_trees;
192 MetaItem::Tokens(name, token_trees)
193 },
194 is_sugared_doc: false,
195 })
196 )
197 |
198 do_parse!(
David Tolnay9d8f1972016-09-04 11:58:48 -0700199 punct!("///") >>
David Tolnay1f16b602017-02-07 20:06:55 -0500200 not!(tag!("/")) >>
David Tolnayb5a7b142016-09-13 22:46:39 -0700201 content: take_until!("\n") >>
David Tolnay9d8f1972016-09-04 11:58:48 -0700202 (Attribute {
David Tolnay4a51dc72016-10-01 00:40:31 -0700203 style: AttrStyle::Outer,
David Tolnay9d8f1972016-09-04 11:58:48 -0700204 value: MetaItem::NameValue(
David Tolnay42f50292016-09-04 13:54:21 -0700205 "doc".into(),
Pascal Hertleif36342c52016-10-19 10:31:42 +0200206 format!("///{}", content).into(),
David Tolnay9d8f1972016-09-04 11:58:48 -0700207 ),
208 is_sugared_doc: true,
209 })
210 )
David Tolnay14cbdeb2016-10-01 12:13:59 -0700211 |
212 do_parse!(
213 option!(whitespace) >>
David Tolnay84aa0752016-10-02 23:01:13 -0700214 peek!(tuple!(tag!("/**"), not!(tag!("*")))) >>
David Tolnay14cbdeb2016-10-01 12:13:59 -0700215 com: block_comment >>
216 (Attribute {
217 style: AttrStyle::Outer,
218 value: MetaItem::NameValue(
219 "doc".into(),
Pascal Hertleif36342c52016-10-19 10:31:42 +0200220 com.into(),
David Tolnay14cbdeb2016-10-01 12:13:59 -0700221 ),
222 is_sugared_doc: true,
223 })
224 )
David Tolnay9d8f1972016-09-04 11:58:48 -0700225 ));
David Tolnayb79ee962016-09-04 09:39:20 -0700226
David Tolnayb5a7b142016-09-13 22:46:39 -0700227 named!(meta_item -> MetaItem, alt!(
David Tolnay9d8f1972016-09-04 11:58:48 -0700228 do_parse!(
David Tolnay55337722016-09-11 12:58:56 -0700229 id: ident >>
David Tolnay9d8f1972016-09-04 11:58:48 -0700230 punct!("(") >>
David Tolnayb7fa2b62016-10-30 10:50:47 -0700231 inner: terminated_list!(punct!(","), nested_meta_item) >>
David Tolnay9d8f1972016-09-04 11:58:48 -0700232 punct!(")") >>
David Tolnay55337722016-09-11 12:58:56 -0700233 (MetaItem::List(id, inner))
David Tolnay9d8f1972016-09-04 11:58:48 -0700234 )
235 |
236 do_parse!(
David Tolnayf4bbbd92016-09-23 14:41:55 -0700237 name: ident >>
David Tolnay9d8f1972016-09-04 11:58:48 -0700238 punct!("=") >>
David Tolnayf4bbbd92016-09-23 14:41:55 -0700239 value: lit >>
240 (MetaItem::NameValue(name, value))
David Tolnay9d8f1972016-09-04 11:58:48 -0700241 )
242 |
David Tolnay55337722016-09-11 12:58:56 -0700243 map!(ident, MetaItem::Word)
David Tolnay9d8f1972016-09-04 11:58:48 -0700244 ));
David Tolnayb7fa2b62016-10-30 10:50:47 -0700245
246 named!(nested_meta_item -> NestedMetaItem, alt!(
247 meta_item => { NestedMetaItem::MetaItem }
248 |
249 lit => { NestedMetaItem::Literal }
250 ));
David Tolnay9d8f1972016-09-04 11:58:48 -0700251}
David Tolnay87d0b442016-09-04 11:52:12 -0700252
253#[cfg(feature = "printing")]
254mod printing {
255 use super::*;
David Tolnayc91dd672016-10-01 01:03:56 -0700256 use lit::{Lit, StrStyle};
David Tolnay87d0b442016-09-04 11:52:12 -0700257 use quote::{Tokens, ToTokens};
258
259 impl ToTokens for Attribute {
260 fn to_tokens(&self, tokens: &mut Tokens) {
David Tolnaydaaf7742016-10-03 11:11:43 -0700261 if let Attribute { style,
262 value: MetaItem::NameValue(ref name,
263 Lit::Str(ref value, StrStyle::Cooked)),
264 is_sugared_doc: true } = *self {
David Tolnay14cbdeb2016-10-01 12:13:59 -0700265 if name == "doc" {
266 match style {
267 AttrStyle::Inner if value.starts_with("//!") => {
268 tokens.append(&format!("{}\n", value));
269 return;
270 }
271 AttrStyle::Inner if value.starts_with("/*!") => {
272 tokens.append(value);
273 return;
274 }
275 AttrStyle::Outer if value.starts_with("///") => {
276 tokens.append(&format!("{}\n", value));
277 return;
278 }
279 AttrStyle::Outer if value.starts_with("/**") => {
280 tokens.append(value);
281 return;
282 }
283 _ => {}
David Tolnay4a51dc72016-10-01 00:40:31 -0700284 }
David Tolnayc91dd672016-10-01 01:03:56 -0700285 }
286 }
David Tolnay14cbdeb2016-10-01 12:13:59 -0700287
288 tokens.append("#");
289 if let AttrStyle::Inner = self.style {
290 tokens.append("!");
291 }
292 tokens.append("[");
293 self.value.to_tokens(tokens);
294 tokens.append("]");
David Tolnay87d0b442016-09-04 11:52:12 -0700295 }
296 }
297
298 impl ToTokens for MetaItem {
299 fn to_tokens(&self, tokens: &mut Tokens) {
300 match *self {
David Tolnay26469072016-09-04 13:59:48 -0700301 MetaItem::Word(ref ident) => {
302 ident.to_tokens(tokens);
303 }
David Tolnay87d0b442016-09-04 11:52:12 -0700304 MetaItem::List(ref ident, ref inner) => {
David Tolnay26469072016-09-04 13:59:48 -0700305 ident.to_tokens(tokens);
David Tolnay87d0b442016-09-04 11:52:12 -0700306 tokens.append("(");
David Tolnay94ebdf92016-09-04 13:33:16 -0700307 tokens.append_separated(inner, ",");
David Tolnay87d0b442016-09-04 11:52:12 -0700308 tokens.append(")");
309 }
310 MetaItem::NameValue(ref name, ref value) => {
David Tolnay26469072016-09-04 13:59:48 -0700311 name.to_tokens(tokens);
David Tolnay87d0b442016-09-04 11:52:12 -0700312 tokens.append("=");
313 value.to_tokens(tokens);
314 }
Arnavioneaa2cff2017-04-15 03:32:13 -0700315 MetaItem::Tokens(ref name, ref token_trees) => {
316 name.to_tokens(tokens);
317 tokens.append_all(token_trees);
318 }
David Tolnay87d0b442016-09-04 11:52:12 -0700319 }
320 }
321 }
David Tolnayb7fa2b62016-10-30 10:50:47 -0700322
323 impl ToTokens for NestedMetaItem {
324 fn to_tokens(&self, tokens: &mut Tokens) {
325 match *self {
326 NestedMetaItem::MetaItem(ref nested) => {
327 nested.to_tokens(tokens);
328 }
329 NestedMetaItem::Literal(ref lit) => {
330 lit.to_tokens(tokens);
331 }
332 }
333 }
334 }
David Tolnay87d0b442016-09-04 11:52:12 -0700335}