blob: 443daa5a46512d5283c670fb923c491ef140de1a [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),
David Tolnayb79ee962016-09-04 09:39:20 -070050}
51
David Tolnay8e661e22016-09-27 00:00:04 -070052impl MetaItem {
Clar Charrd22b5702017-03-10 15:24:56 -050053 /// Name of the item.
54 ///
55 /// E.g. `test` as in `#[test]`, `derive` as in `#[derive(..)]`, and
56 /// `feature` as in `#[feature = "foo"]`.
David Tolnay8e661e22016-09-27 00:00:04 -070057 pub fn name(&self) -> &str {
58 match *self {
David Tolnay590cdfd2016-10-01 08:51:55 -070059 MetaItem::Word(ref name) |
60 MetaItem::List(ref name, _) |
David Tolnay8e661e22016-09-27 00:00:04 -070061 MetaItem::NameValue(ref name, _) => name.as_ref(),
62 }
63 }
64}
65
David Tolnayb7fa2b62016-10-30 10:50:47 -070066/// Possible values inside of compile-time attribute lists.
67///
68/// E.g. the '..' in `#[name(..)]`.
David Tolnay9bf4af82017-01-07 11:17:46 -080069#[derive(Debug, Clone, Eq, PartialEq, Hash)]
David Tolnayb7fa2b62016-10-30 10:50:47 -070070pub enum NestedMetaItem {
Clar Charrd22b5702017-03-10 15:24:56 -050071 /// A full `MetaItem`.
David Tolnayb7fa2b62016-10-30 10:50:47 -070072 ///
Clar Charrd22b5702017-03-10 15:24:56 -050073 /// E.g. `Copy` in `#[derive(Copy)]` would be a `MetaItem::Word(Ident::from("Copy"))`.
74 MetaItem(MetaItem),
75
76 /// A Rust literal.
77 ///
78 /// E.g. `"name"` in `#[rename("name")]`.
David Tolnayb7fa2b62016-10-30 10:50:47 -070079 Literal(Lit),
80}
81
David Tolnay4a51dc72016-10-01 00:40:31 -070082pub trait FilterAttrs<'a> {
83 type Ret: Iterator<Item = &'a Attribute>;
84
85 fn outer(self) -> Self::Ret;
86 fn inner(self) -> Self::Ret;
87}
88
David Tolnaydaaf7742016-10-03 11:11:43 -070089impl<'a, T> FilterAttrs<'a> for T
90 where T: IntoIterator<Item = &'a Attribute>
91{
David Tolnay4a51dc72016-10-01 00:40:31 -070092 type Ret = iter::Filter<T::IntoIter, fn(&&Attribute) -> bool>;
93
94 fn outer(self) -> Self::Ret {
95 fn is_outer(attr: &&Attribute) -> bool {
96 attr.style == AttrStyle::Outer
97 }
98 self.into_iter().filter(is_outer)
99 }
100
101 fn inner(self) -> Self::Ret {
102 fn is_inner(attr: &&Attribute) -> bool {
103 attr.style == AttrStyle::Inner
104 }
105 self.into_iter().filter(is_inner)
106 }
107}
108
David Tolnay86eca752016-09-04 11:26:41 -0700109#[cfg(feature = "parsing")]
David Tolnay9d8f1972016-09-04 11:58:48 -0700110pub mod parsing {
111 use super::*;
David Tolnay55337722016-09-11 12:58:56 -0700112 use ident::parsing::ident;
David Tolnayf4bbbd92016-09-23 14:41:55 -0700113 use lit::parsing::lit;
David Tolnay5fe14fc2017-01-27 16:22:08 -0800114 use synom::space::{block_comment, whitespace};
David Tolnayb79ee962016-09-04 09:39:20 -0700115
David Tolnay2a2e67c2016-10-01 14:02:01 -0700116 #[cfg(feature = "full")]
David Tolnay14cbdeb2016-10-01 12:13:59 -0700117 named!(pub inner_attr -> Attribute, alt!(
118 do_parse!(
119 punct!("#") >>
120 punct!("!") >>
121 punct!("[") >>
122 meta_item: meta_item >>
123 punct!("]") >>
124 (Attribute {
125 style: AttrStyle::Inner,
126 value: meta_item,
127 is_sugared_doc: false,
128 })
129 )
130 |
131 do_parse!(
132 punct!("//!") >>
133 content: take_until!("\n") >>
134 (Attribute {
135 style: AttrStyle::Inner,
136 value: MetaItem::NameValue(
137 "doc".into(),
Pascal Hertleif36342c52016-10-19 10:31:42 +0200138 format!("//!{}", content).into(),
David Tolnay14cbdeb2016-10-01 12:13:59 -0700139 ),
140 is_sugared_doc: true,
141 })
142 )
143 |
144 do_parse!(
145 option!(whitespace) >>
146 peek!(tag!("/*!")) >>
147 com: block_comment >>
148 (Attribute {
149 style: AttrStyle::Inner,
150 value: MetaItem::NameValue(
151 "doc".into(),
Pascal Hertleif36342c52016-10-19 10:31:42 +0200152 com.into(),
David Tolnay14cbdeb2016-10-01 12:13:59 -0700153 ),
154 is_sugared_doc: true,
155 })
156 )
David Tolnay4a51dc72016-10-01 00:40:31 -0700157 ));
158
159 named!(pub outer_attr -> Attribute, alt!(
David Tolnay9d8f1972016-09-04 11:58:48 -0700160 do_parse!(
161 punct!("#") >>
162 punct!("[") >>
163 meta_item: meta_item >>
164 punct!("]") >>
165 (Attribute {
David Tolnay4a51dc72016-10-01 00:40:31 -0700166 style: AttrStyle::Outer,
David Tolnay9d8f1972016-09-04 11:58:48 -0700167 value: meta_item,
168 is_sugared_doc: false,
169 })
170 )
171 |
172 do_parse!(
173 punct!("///") >>
David Tolnay1f16b602017-02-07 20:06:55 -0500174 not!(tag!("/")) >>
David Tolnayb5a7b142016-09-13 22:46:39 -0700175 content: take_until!("\n") >>
David Tolnay9d8f1972016-09-04 11:58:48 -0700176 (Attribute {
David Tolnay4a51dc72016-10-01 00:40:31 -0700177 style: AttrStyle::Outer,
David Tolnay9d8f1972016-09-04 11:58:48 -0700178 value: MetaItem::NameValue(
David Tolnay42f50292016-09-04 13:54:21 -0700179 "doc".into(),
Pascal Hertleif36342c52016-10-19 10:31:42 +0200180 format!("///{}", content).into(),
David Tolnay9d8f1972016-09-04 11:58:48 -0700181 ),
182 is_sugared_doc: true,
183 })
184 )
David Tolnay14cbdeb2016-10-01 12:13:59 -0700185 |
186 do_parse!(
187 option!(whitespace) >>
David Tolnay84aa0752016-10-02 23:01:13 -0700188 peek!(tuple!(tag!("/**"), not!(tag!("*")))) >>
David Tolnay14cbdeb2016-10-01 12:13:59 -0700189 com: block_comment >>
190 (Attribute {
191 style: AttrStyle::Outer,
192 value: MetaItem::NameValue(
193 "doc".into(),
Pascal Hertleif36342c52016-10-19 10:31:42 +0200194 com.into(),
David Tolnay14cbdeb2016-10-01 12:13:59 -0700195 ),
196 is_sugared_doc: true,
197 })
198 )
David Tolnay9d8f1972016-09-04 11:58:48 -0700199 ));
David Tolnayb79ee962016-09-04 09:39:20 -0700200
David Tolnayb5a7b142016-09-13 22:46:39 -0700201 named!(meta_item -> MetaItem, alt!(
David Tolnay9d8f1972016-09-04 11:58:48 -0700202 do_parse!(
David Tolnay55337722016-09-11 12:58:56 -0700203 id: ident >>
David Tolnay9d8f1972016-09-04 11:58:48 -0700204 punct!("(") >>
David Tolnayb7fa2b62016-10-30 10:50:47 -0700205 inner: terminated_list!(punct!(","), nested_meta_item) >>
David Tolnay9d8f1972016-09-04 11:58:48 -0700206 punct!(")") >>
David Tolnay55337722016-09-11 12:58:56 -0700207 (MetaItem::List(id, inner))
David Tolnay9d8f1972016-09-04 11:58:48 -0700208 )
209 |
210 do_parse!(
David Tolnayf4bbbd92016-09-23 14:41:55 -0700211 name: ident >>
David Tolnay9d8f1972016-09-04 11:58:48 -0700212 punct!("=") >>
David Tolnayf4bbbd92016-09-23 14:41:55 -0700213 value: lit >>
214 (MetaItem::NameValue(name, value))
David Tolnay9d8f1972016-09-04 11:58:48 -0700215 )
216 |
David Tolnay55337722016-09-11 12:58:56 -0700217 map!(ident, MetaItem::Word)
David Tolnay9d8f1972016-09-04 11:58:48 -0700218 ));
David Tolnayb7fa2b62016-10-30 10:50:47 -0700219
220 named!(nested_meta_item -> NestedMetaItem, alt!(
221 meta_item => { NestedMetaItem::MetaItem }
222 |
223 lit => { NestedMetaItem::Literal }
224 ));
David Tolnay9d8f1972016-09-04 11:58:48 -0700225}
David Tolnay87d0b442016-09-04 11:52:12 -0700226
227#[cfg(feature = "printing")]
228mod printing {
229 use super::*;
David Tolnayc91dd672016-10-01 01:03:56 -0700230 use lit::{Lit, StrStyle};
David Tolnay87d0b442016-09-04 11:52:12 -0700231 use quote::{Tokens, ToTokens};
232
233 impl ToTokens for Attribute {
234 fn to_tokens(&self, tokens: &mut Tokens) {
David Tolnaydaaf7742016-10-03 11:11:43 -0700235 if let Attribute { style,
236 value: MetaItem::NameValue(ref name,
237 Lit::Str(ref value, StrStyle::Cooked)),
238 is_sugared_doc: true } = *self {
David Tolnay14cbdeb2016-10-01 12:13:59 -0700239 if name == "doc" {
240 match style {
241 AttrStyle::Inner if value.starts_with("//!") => {
242 tokens.append(&format!("{}\n", value));
243 return;
244 }
245 AttrStyle::Inner if value.starts_with("/*!") => {
246 tokens.append(value);
247 return;
248 }
249 AttrStyle::Outer if value.starts_with("///") => {
250 tokens.append(&format!("{}\n", value));
251 return;
252 }
253 AttrStyle::Outer if value.starts_with("/**") => {
254 tokens.append(value);
255 return;
256 }
257 _ => {}
David Tolnay4a51dc72016-10-01 00:40:31 -0700258 }
David Tolnayc91dd672016-10-01 01:03:56 -0700259 }
260 }
David Tolnay14cbdeb2016-10-01 12:13:59 -0700261
262 tokens.append("#");
263 if let AttrStyle::Inner = self.style {
264 tokens.append("!");
265 }
266 tokens.append("[");
267 self.value.to_tokens(tokens);
268 tokens.append("]");
David Tolnay87d0b442016-09-04 11:52:12 -0700269 }
270 }
271
272 impl ToTokens for MetaItem {
273 fn to_tokens(&self, tokens: &mut Tokens) {
274 match *self {
David Tolnay26469072016-09-04 13:59:48 -0700275 MetaItem::Word(ref ident) => {
276 ident.to_tokens(tokens);
277 }
David Tolnay87d0b442016-09-04 11:52:12 -0700278 MetaItem::List(ref ident, ref inner) => {
David Tolnay26469072016-09-04 13:59:48 -0700279 ident.to_tokens(tokens);
David Tolnay87d0b442016-09-04 11:52:12 -0700280 tokens.append("(");
David Tolnay94ebdf92016-09-04 13:33:16 -0700281 tokens.append_separated(inner, ",");
David Tolnay87d0b442016-09-04 11:52:12 -0700282 tokens.append(")");
283 }
284 MetaItem::NameValue(ref name, ref value) => {
David Tolnay26469072016-09-04 13:59:48 -0700285 name.to_tokens(tokens);
David Tolnay87d0b442016-09-04 11:52:12 -0700286 tokens.append("=");
287 value.to_tokens(tokens);
288 }
289 }
290 }
291 }
David Tolnayb7fa2b62016-10-30 10:50:47 -0700292
293 impl ToTokens for NestedMetaItem {
294 fn to_tokens(&self, tokens: &mut Tokens) {
295 match *self {
296 NestedMetaItem::MetaItem(ref nested) => {
297 nested.to_tokens(tokens);
298 }
299 NestedMetaItem::Literal(ref lit) => {
300 lit.to_tokens(tokens);
301 }
302 }
303 }
304 }
David Tolnay87d0b442016-09-04 11:52:12 -0700305}