blob: afa611e569312c86331b20898965645ced3ff655 [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 {
24 Outer,
25 Inner,
26}
27
David Tolnayb79ee962016-09-04 09:39:20 -070028/// A compile-time attribute item.
29///
30/// E.g. `#[test]`, `#[derive(..)]` or `#[feature = "foo"]`
David Tolnay9bf4af82017-01-07 11:17:46 -080031#[derive(Debug, Clone, Eq, PartialEq, Hash)]
David Tolnayb79ee962016-09-04 09:39:20 -070032pub enum MetaItem {
33 /// Word meta item.
34 ///
35 /// E.g. `test` as in `#[test]`
36 Word(Ident),
37 /// List meta item.
38 ///
39 /// E.g. `derive(..)` as in `#[derive(..)]`
David Tolnayb7fa2b62016-10-30 10:50:47 -070040 List(Ident, Vec<NestedMetaItem>),
David Tolnayb79ee962016-09-04 09:39:20 -070041 /// Name value meta item.
42 ///
43 /// E.g. `feature = "foo"` as in `#[feature = "foo"]`
David Tolnayf4bbbd92016-09-23 14:41:55 -070044 NameValue(Ident, Lit),
David Tolnayb79ee962016-09-04 09:39:20 -070045}
46
David Tolnay8e661e22016-09-27 00:00:04 -070047impl MetaItem {
48 pub fn name(&self) -> &str {
49 match *self {
David Tolnay590cdfd2016-10-01 08:51:55 -070050 MetaItem::Word(ref name) |
51 MetaItem::List(ref name, _) |
David Tolnay8e661e22016-09-27 00:00:04 -070052 MetaItem::NameValue(ref name, _) => name.as_ref(),
53 }
54 }
55}
56
David Tolnayb7fa2b62016-10-30 10:50:47 -070057/// Possible values inside of compile-time attribute lists.
58///
59/// E.g. the '..' in `#[name(..)]`.
David Tolnay9bf4af82017-01-07 11:17:46 -080060#[derive(Debug, Clone, Eq, PartialEq, Hash)]
David Tolnayb7fa2b62016-10-30 10:50:47 -070061pub enum NestedMetaItem {
62 /// A full MetaItem, for recursive meta items.
63 MetaItem(MetaItem),
64 /// A literal.
65 ///
66 /// E.g. "foo", 64, true
67 Literal(Lit),
68}
69
David Tolnay4a51dc72016-10-01 00:40:31 -070070pub trait FilterAttrs<'a> {
71 type Ret: Iterator<Item = &'a Attribute>;
72
73 fn outer(self) -> Self::Ret;
74 fn inner(self) -> Self::Ret;
75}
76
David Tolnaydaaf7742016-10-03 11:11:43 -070077impl<'a, T> FilterAttrs<'a> for T
78 where T: IntoIterator<Item = &'a Attribute>
79{
David Tolnay4a51dc72016-10-01 00:40:31 -070080 type Ret = iter::Filter<T::IntoIter, fn(&&Attribute) -> bool>;
81
82 fn outer(self) -> Self::Ret {
83 fn is_outer(attr: &&Attribute) -> bool {
84 attr.style == AttrStyle::Outer
85 }
86 self.into_iter().filter(is_outer)
87 }
88
89 fn inner(self) -> Self::Ret {
90 fn is_inner(attr: &&Attribute) -> bool {
91 attr.style == AttrStyle::Inner
92 }
93 self.into_iter().filter(is_inner)
94 }
95}
96
David Tolnay86eca752016-09-04 11:26:41 -070097#[cfg(feature = "parsing")]
David Tolnay9d8f1972016-09-04 11:58:48 -070098pub mod parsing {
99 use super::*;
David Tolnay55337722016-09-11 12:58:56 -0700100 use ident::parsing::ident;
David Tolnayf4bbbd92016-09-23 14:41:55 -0700101 use lit::parsing::lit;
David Tolnay5fe14fc2017-01-27 16:22:08 -0800102 use synom::space::{block_comment, whitespace};
David Tolnayb79ee962016-09-04 09:39:20 -0700103
David Tolnay2a2e67c2016-10-01 14:02:01 -0700104 #[cfg(feature = "full")]
David Tolnay14cbdeb2016-10-01 12:13:59 -0700105 named!(pub inner_attr -> Attribute, alt!(
106 do_parse!(
107 punct!("#") >>
108 punct!("!") >>
109 punct!("[") >>
110 meta_item: meta_item >>
111 punct!("]") >>
112 (Attribute {
113 style: AttrStyle::Inner,
114 value: meta_item,
115 is_sugared_doc: false,
116 })
117 )
118 |
119 do_parse!(
120 punct!("//!") >>
121 content: take_until!("\n") >>
122 (Attribute {
123 style: AttrStyle::Inner,
124 value: MetaItem::NameValue(
125 "doc".into(),
Pascal Hertleif36342c52016-10-19 10:31:42 +0200126 format!("//!{}", content).into(),
David Tolnay14cbdeb2016-10-01 12:13:59 -0700127 ),
128 is_sugared_doc: true,
129 })
130 )
131 |
132 do_parse!(
133 option!(whitespace) >>
134 peek!(tag!("/*!")) >>
135 com: block_comment >>
136 (Attribute {
137 style: AttrStyle::Inner,
138 value: MetaItem::NameValue(
139 "doc".into(),
Pascal Hertleif36342c52016-10-19 10:31:42 +0200140 com.into(),
David Tolnay14cbdeb2016-10-01 12:13:59 -0700141 ),
142 is_sugared_doc: true,
143 })
144 )
David Tolnay4a51dc72016-10-01 00:40:31 -0700145 ));
146
147 named!(pub outer_attr -> Attribute, alt!(
David Tolnay9d8f1972016-09-04 11:58:48 -0700148 do_parse!(
149 punct!("#") >>
150 punct!("[") >>
151 meta_item: meta_item >>
152 punct!("]") >>
153 (Attribute {
David Tolnay4a51dc72016-10-01 00:40:31 -0700154 style: AttrStyle::Outer,
David Tolnay9d8f1972016-09-04 11:58:48 -0700155 value: meta_item,
156 is_sugared_doc: false,
157 })
158 )
159 |
160 do_parse!(
161 punct!("///") >>
David Tolnay1f16b602017-02-07 20:06:55 -0500162 not!(tag!("/")) >>
David Tolnayb5a7b142016-09-13 22:46:39 -0700163 content: take_until!("\n") >>
David Tolnay9d8f1972016-09-04 11:58:48 -0700164 (Attribute {
David Tolnay4a51dc72016-10-01 00:40:31 -0700165 style: AttrStyle::Outer,
David Tolnay9d8f1972016-09-04 11:58:48 -0700166 value: MetaItem::NameValue(
David Tolnay42f50292016-09-04 13:54:21 -0700167 "doc".into(),
Pascal Hertleif36342c52016-10-19 10:31:42 +0200168 format!("///{}", content).into(),
David Tolnay9d8f1972016-09-04 11:58:48 -0700169 ),
170 is_sugared_doc: true,
171 })
172 )
David Tolnay14cbdeb2016-10-01 12:13:59 -0700173 |
174 do_parse!(
175 option!(whitespace) >>
David Tolnay84aa0752016-10-02 23:01:13 -0700176 peek!(tuple!(tag!("/**"), not!(tag!("*")))) >>
David Tolnay14cbdeb2016-10-01 12:13:59 -0700177 com: block_comment >>
178 (Attribute {
179 style: AttrStyle::Outer,
180 value: MetaItem::NameValue(
181 "doc".into(),
Pascal Hertleif36342c52016-10-19 10:31:42 +0200182 com.into(),
David Tolnay14cbdeb2016-10-01 12:13:59 -0700183 ),
184 is_sugared_doc: true,
185 })
186 )
David Tolnay9d8f1972016-09-04 11:58:48 -0700187 ));
David Tolnayb79ee962016-09-04 09:39:20 -0700188
David Tolnayb5a7b142016-09-13 22:46:39 -0700189 named!(meta_item -> MetaItem, alt!(
David Tolnay9d8f1972016-09-04 11:58:48 -0700190 do_parse!(
David Tolnay55337722016-09-11 12:58:56 -0700191 id: ident >>
David Tolnay9d8f1972016-09-04 11:58:48 -0700192 punct!("(") >>
David Tolnayb7fa2b62016-10-30 10:50:47 -0700193 inner: terminated_list!(punct!(","), nested_meta_item) >>
David Tolnay9d8f1972016-09-04 11:58:48 -0700194 punct!(")") >>
David Tolnay55337722016-09-11 12:58:56 -0700195 (MetaItem::List(id, inner))
David Tolnay9d8f1972016-09-04 11:58:48 -0700196 )
197 |
198 do_parse!(
David Tolnayf4bbbd92016-09-23 14:41:55 -0700199 name: ident >>
David Tolnay9d8f1972016-09-04 11:58:48 -0700200 punct!("=") >>
David Tolnayf4bbbd92016-09-23 14:41:55 -0700201 value: lit >>
202 (MetaItem::NameValue(name, value))
David Tolnay9d8f1972016-09-04 11:58:48 -0700203 )
204 |
David Tolnay55337722016-09-11 12:58:56 -0700205 map!(ident, MetaItem::Word)
David Tolnay9d8f1972016-09-04 11:58:48 -0700206 ));
David Tolnayb7fa2b62016-10-30 10:50:47 -0700207
208 named!(nested_meta_item -> NestedMetaItem, alt!(
209 meta_item => { NestedMetaItem::MetaItem }
210 |
211 lit => { NestedMetaItem::Literal }
212 ));
David Tolnay9d8f1972016-09-04 11:58:48 -0700213}
David Tolnay87d0b442016-09-04 11:52:12 -0700214
215#[cfg(feature = "printing")]
216mod printing {
217 use super::*;
David Tolnayc91dd672016-10-01 01:03:56 -0700218 use lit::{Lit, StrStyle};
David Tolnay87d0b442016-09-04 11:52:12 -0700219 use quote::{Tokens, ToTokens};
220
221 impl ToTokens for Attribute {
222 fn to_tokens(&self, tokens: &mut Tokens) {
David Tolnaydaaf7742016-10-03 11:11:43 -0700223 if let Attribute { style,
224 value: MetaItem::NameValue(ref name,
225 Lit::Str(ref value, StrStyle::Cooked)),
226 is_sugared_doc: true } = *self {
David Tolnay14cbdeb2016-10-01 12:13:59 -0700227 if name == "doc" {
228 match style {
229 AttrStyle::Inner if value.starts_with("//!") => {
230 tokens.append(&format!("{}\n", value));
231 return;
232 }
233 AttrStyle::Inner if value.starts_with("/*!") => {
234 tokens.append(value);
235 return;
236 }
237 AttrStyle::Outer if value.starts_with("///") => {
238 tokens.append(&format!("{}\n", value));
239 return;
240 }
241 AttrStyle::Outer if value.starts_with("/**") => {
242 tokens.append(value);
243 return;
244 }
245 _ => {}
David Tolnay4a51dc72016-10-01 00:40:31 -0700246 }
David Tolnayc91dd672016-10-01 01:03:56 -0700247 }
248 }
David Tolnay14cbdeb2016-10-01 12:13:59 -0700249
250 tokens.append("#");
251 if let AttrStyle::Inner = self.style {
252 tokens.append("!");
253 }
254 tokens.append("[");
255 self.value.to_tokens(tokens);
256 tokens.append("]");
David Tolnay87d0b442016-09-04 11:52:12 -0700257 }
258 }
259
260 impl ToTokens for MetaItem {
261 fn to_tokens(&self, tokens: &mut Tokens) {
262 match *self {
David Tolnay26469072016-09-04 13:59:48 -0700263 MetaItem::Word(ref ident) => {
264 ident.to_tokens(tokens);
265 }
David Tolnay87d0b442016-09-04 11:52:12 -0700266 MetaItem::List(ref ident, ref inner) => {
David Tolnay26469072016-09-04 13:59:48 -0700267 ident.to_tokens(tokens);
David Tolnay87d0b442016-09-04 11:52:12 -0700268 tokens.append("(");
David Tolnay94ebdf92016-09-04 13:33:16 -0700269 tokens.append_separated(inner, ",");
David Tolnay87d0b442016-09-04 11:52:12 -0700270 tokens.append(")");
271 }
272 MetaItem::NameValue(ref name, ref value) => {
David Tolnay26469072016-09-04 13:59:48 -0700273 name.to_tokens(tokens);
David Tolnay87d0b442016-09-04 11:52:12 -0700274 tokens.append("=");
275 value.to_tokens(tokens);
276 }
277 }
278 }
279 }
David Tolnayb7fa2b62016-10-30 10:50:47 -0700280
281 impl ToTokens for NestedMetaItem {
282 fn to_tokens(&self, tokens: &mut Tokens) {
283 match *self {
284 NestedMetaItem::MetaItem(ref nested) => {
285 nested.to_tokens(tokens);
286 }
287 NestedMetaItem::Literal(ref lit) => {
288 lit.to_tokens(tokens);
289 }
290 }
291 }
292 }
David Tolnay87d0b442016-09-04 11:52:12 -0700293}