blob: 64bcc4efa9f4374c77ced3ed64cb2907c0731499 [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 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.
22#[derive(Debug, Copy, Clone, Eq, PartialEq)]
23pub 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"]`
31#[derive(Debug, Clone, Eq, PartialEq)]
32pub 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(..)]`
40 List(Ident, Vec<MetaItem>),
41 /// 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 Tolnay4a51dc72016-10-01 00:40:31 -070057pub trait FilterAttrs<'a> {
58 type Ret: Iterator<Item = &'a Attribute>;
59
60 fn outer(self) -> Self::Ret;
61 fn inner(self) -> Self::Ret;
62}
63
David Tolnaydaaf7742016-10-03 11:11:43 -070064impl<'a, T> FilterAttrs<'a> for T
65 where T: IntoIterator<Item = &'a Attribute>
66{
David Tolnay4a51dc72016-10-01 00:40:31 -070067 type Ret = iter::Filter<T::IntoIter, fn(&&Attribute) -> bool>;
68
69 fn outer(self) -> Self::Ret {
70 fn is_outer(attr: &&Attribute) -> bool {
71 attr.style == AttrStyle::Outer
72 }
73 self.into_iter().filter(is_outer)
74 }
75
76 fn inner(self) -> Self::Ret {
77 fn is_inner(attr: &&Attribute) -> bool {
78 attr.style == AttrStyle::Inner
79 }
80 self.into_iter().filter(is_inner)
81 }
82}
83
David Tolnay86eca752016-09-04 11:26:41 -070084#[cfg(feature = "parsing")]
David Tolnay9d8f1972016-09-04 11:58:48 -070085pub mod parsing {
86 use super::*;
David Tolnay55337722016-09-11 12:58:56 -070087 use ident::parsing::ident;
David Tolnayf4bbbd92016-09-23 14:41:55 -070088 use lit::parsing::lit;
David Tolnay14cbdeb2016-10-01 12:13:59 -070089 use space::{block_comment, whitespace};
David Tolnayb79ee962016-09-04 09:39:20 -070090
David Tolnay2a2e67c2016-10-01 14:02:01 -070091 #[cfg(feature = "full")]
David Tolnay14cbdeb2016-10-01 12:13:59 -070092 named!(pub inner_attr -> Attribute, alt!(
93 do_parse!(
94 punct!("#") >>
95 punct!("!") >>
96 punct!("[") >>
97 meta_item: meta_item >>
98 punct!("]") >>
99 (Attribute {
100 style: AttrStyle::Inner,
101 value: meta_item,
102 is_sugared_doc: false,
103 })
104 )
105 |
106 do_parse!(
107 punct!("//!") >>
108 content: take_until!("\n") >>
109 (Attribute {
110 style: AttrStyle::Inner,
111 value: MetaItem::NameValue(
112 "doc".into(),
Pascal Hertleif36342c52016-10-19 10:31:42 +0200113 format!("//!{}", content).into(),
David Tolnay14cbdeb2016-10-01 12:13:59 -0700114 ),
115 is_sugared_doc: true,
116 })
117 )
118 |
119 do_parse!(
120 option!(whitespace) >>
121 peek!(tag!("/*!")) >>
122 com: block_comment >>
123 (Attribute {
124 style: AttrStyle::Inner,
125 value: MetaItem::NameValue(
126 "doc".into(),
Pascal Hertleif36342c52016-10-19 10:31:42 +0200127 com.into(),
David Tolnay14cbdeb2016-10-01 12:13:59 -0700128 ),
129 is_sugared_doc: true,
130 })
131 )
David Tolnay4a51dc72016-10-01 00:40:31 -0700132 ));
133
134 named!(pub outer_attr -> Attribute, alt!(
David Tolnay9d8f1972016-09-04 11:58:48 -0700135 do_parse!(
136 punct!("#") >>
137 punct!("[") >>
138 meta_item: meta_item >>
139 punct!("]") >>
140 (Attribute {
David Tolnay4a51dc72016-10-01 00:40:31 -0700141 style: AttrStyle::Outer,
David Tolnay9d8f1972016-09-04 11:58:48 -0700142 value: meta_item,
143 is_sugared_doc: false,
144 })
145 )
146 |
147 do_parse!(
148 punct!("///") >>
David Tolnayc91dd672016-10-01 01:03:56 -0700149 not!(peek!(tag!("/"))) >>
David Tolnayb5a7b142016-09-13 22:46:39 -0700150 content: take_until!("\n") >>
David Tolnay9d8f1972016-09-04 11:58:48 -0700151 (Attribute {
David Tolnay4a51dc72016-10-01 00:40:31 -0700152 style: AttrStyle::Outer,
David Tolnay9d8f1972016-09-04 11:58:48 -0700153 value: MetaItem::NameValue(
David Tolnay42f50292016-09-04 13:54:21 -0700154 "doc".into(),
Pascal Hertleif36342c52016-10-19 10:31:42 +0200155 format!("///{}", content).into(),
David Tolnay9d8f1972016-09-04 11:58:48 -0700156 ),
157 is_sugared_doc: true,
158 })
159 )
David Tolnay14cbdeb2016-10-01 12:13:59 -0700160 |
161 do_parse!(
162 option!(whitespace) >>
David Tolnay84aa0752016-10-02 23:01:13 -0700163 peek!(tuple!(tag!("/**"), not!(tag!("*")))) >>
David Tolnay14cbdeb2016-10-01 12:13:59 -0700164 com: block_comment >>
165 (Attribute {
166 style: AttrStyle::Outer,
167 value: MetaItem::NameValue(
168 "doc".into(),
Pascal Hertleif36342c52016-10-19 10:31:42 +0200169 com.into(),
David Tolnay14cbdeb2016-10-01 12:13:59 -0700170 ),
171 is_sugared_doc: true,
172 })
173 )
David Tolnay9d8f1972016-09-04 11:58:48 -0700174 ));
David Tolnayb79ee962016-09-04 09:39:20 -0700175
David Tolnayb5a7b142016-09-13 22:46:39 -0700176 named!(meta_item -> MetaItem, alt!(
David Tolnay9d8f1972016-09-04 11:58:48 -0700177 do_parse!(
David Tolnay55337722016-09-11 12:58:56 -0700178 id: ident >>
David Tolnay9d8f1972016-09-04 11:58:48 -0700179 punct!("(") >>
David Tolnayff46fd22016-10-08 13:53:28 -0700180 inner: terminated_list!(punct!(","), meta_item) >>
David Tolnay9d8f1972016-09-04 11:58:48 -0700181 punct!(")") >>
David Tolnay55337722016-09-11 12:58:56 -0700182 (MetaItem::List(id, inner))
David Tolnay9d8f1972016-09-04 11:58:48 -0700183 )
184 |
185 do_parse!(
David Tolnayf4bbbd92016-09-23 14:41:55 -0700186 name: ident >>
David Tolnay9d8f1972016-09-04 11:58:48 -0700187 punct!("=") >>
David Tolnayf4bbbd92016-09-23 14:41:55 -0700188 value: lit >>
189 (MetaItem::NameValue(name, value))
David Tolnay9d8f1972016-09-04 11:58:48 -0700190 )
191 |
David Tolnay55337722016-09-11 12:58:56 -0700192 map!(ident, MetaItem::Word)
David Tolnay9d8f1972016-09-04 11:58:48 -0700193 ));
194}
David Tolnay87d0b442016-09-04 11:52:12 -0700195
196#[cfg(feature = "printing")]
197mod printing {
198 use super::*;
David Tolnayc91dd672016-10-01 01:03:56 -0700199 use lit::{Lit, StrStyle};
David Tolnay87d0b442016-09-04 11:52:12 -0700200 use quote::{Tokens, ToTokens};
201
202 impl ToTokens for Attribute {
203 fn to_tokens(&self, tokens: &mut Tokens) {
David Tolnaydaaf7742016-10-03 11:11:43 -0700204 if let Attribute { style,
205 value: MetaItem::NameValue(ref name,
206 Lit::Str(ref value, StrStyle::Cooked)),
207 is_sugared_doc: true } = *self {
David Tolnay14cbdeb2016-10-01 12:13:59 -0700208 if name == "doc" {
209 match style {
210 AttrStyle::Inner if value.starts_with("//!") => {
211 tokens.append(&format!("{}\n", value));
212 return;
213 }
214 AttrStyle::Inner if value.starts_with("/*!") => {
215 tokens.append(value);
216 return;
217 }
218 AttrStyle::Outer if value.starts_with("///") => {
219 tokens.append(&format!("{}\n", value));
220 return;
221 }
222 AttrStyle::Outer if value.starts_with("/**") => {
223 tokens.append(value);
224 return;
225 }
226 _ => {}
David Tolnay4a51dc72016-10-01 00:40:31 -0700227 }
David Tolnayc91dd672016-10-01 01:03:56 -0700228 }
229 }
David Tolnay14cbdeb2016-10-01 12:13:59 -0700230
231 tokens.append("#");
232 if let AttrStyle::Inner = self.style {
233 tokens.append("!");
234 }
235 tokens.append("[");
236 self.value.to_tokens(tokens);
237 tokens.append("]");
David Tolnay87d0b442016-09-04 11:52:12 -0700238 }
239 }
240
241 impl ToTokens for MetaItem {
242 fn to_tokens(&self, tokens: &mut Tokens) {
243 match *self {
David Tolnay26469072016-09-04 13:59:48 -0700244 MetaItem::Word(ref ident) => {
245 ident.to_tokens(tokens);
246 }
David Tolnay87d0b442016-09-04 11:52:12 -0700247 MetaItem::List(ref ident, ref inner) => {
David Tolnay26469072016-09-04 13:59:48 -0700248 ident.to_tokens(tokens);
David Tolnay87d0b442016-09-04 11:52:12 -0700249 tokens.append("(");
David Tolnay94ebdf92016-09-04 13:33:16 -0700250 tokens.append_separated(inner, ",");
David Tolnay87d0b442016-09-04 11:52:12 -0700251 tokens.append(")");
252 }
253 MetaItem::NameValue(ref name, ref value) => {
David Tolnay26469072016-09-04 13:59:48 -0700254 name.to_tokens(tokens);
David Tolnay87d0b442016-09-04 11:52:12 -0700255 tokens.append("=");
256 value.to_tokens(tokens);
257 }
258 }
259 }
260 }
261}