diff options
-rw-r--r-- | crates/ra_mbe/src/mbe_expander.rs | 61 | ||||
-rw-r--r-- | crates/ra_mbe/src/tests.rs | 13 |
2 files changed, 61 insertions, 13 deletions
diff --git a/crates/ra_mbe/src/mbe_expander.rs b/crates/ra_mbe/src/mbe_expander.rs index 8f8a79855..4b007647c 100644 --- a/crates/ra_mbe/src/mbe_expander.rs +++ b/crates/ra_mbe/src/mbe_expander.rs | |||
@@ -81,9 +81,25 @@ struct Bindings { | |||
81 | enum Binding { | 81 | enum Binding { |
82 | Simple(tt::TokenTree), | 82 | Simple(tt::TokenTree), |
83 | Nested(Vec<Binding>), | 83 | Nested(Vec<Binding>), |
84 | Empty, | ||
84 | } | 85 | } |
85 | 86 | ||
86 | impl Bindings { | 87 | impl Bindings { |
88 | fn push_optional(&mut self, name: &SmolStr) { | ||
89 | // FIXME: Do we have a better way to represent an empty token ? | ||
90 | // Insert an empty subtree for empty token | ||
91 | self.inner.insert( | ||
92 | name.clone(), | ||
93 | Binding::Simple( | ||
94 | tt::Subtree { delimiter: tt::Delimiter::None, token_trees: vec![] }.into(), | ||
95 | ), | ||
96 | ); | ||
97 | } | ||
98 | |||
99 | fn push_empty(&mut self, name: &SmolStr) { | ||
100 | self.inner.insert(name.clone(), Binding::Empty); | ||
101 | } | ||
102 | |||
87 | fn contains(&self, name: &SmolStr) -> bool { | 103 | fn contains(&self, name: &SmolStr) -> bool { |
88 | self.inner.contains_key(name) | 104 | self.inner.contains_key(name) |
89 | } | 105 | } |
@@ -100,6 +116,12 @@ impl Bindings { | |||
100 | "could not find nested binding `{}`", | 116 | "could not find nested binding `{}`", |
101 | name | 117 | name |
102 | )))?, | 118 | )))?, |
119 | Binding::Empty => { | ||
120 | return Err(ExpandError::BindingError(format!( | ||
121 | "could not find empty binding `{}`", | ||
122 | name | ||
123 | ))) | ||
124 | } | ||
103 | }; | 125 | }; |
104 | } | 126 | } |
105 | match b { | 127 | match b { |
@@ -108,6 +130,10 @@ impl Bindings { | |||
108 | "expected simple binding, found nested binding `{}`", | 130 | "expected simple binding, found nested binding `{}`", |
109 | name | 131 | name |
110 | ))), | 132 | ))), |
133 | Binding::Empty => Err(ExpandError::BindingError(format!( | ||
134 | "expected simple binding, found empty binding `{}`", | ||
135 | name | ||
136 | ))), | ||
111 | } | 137 | } |
112 | } | 138 | } |
113 | 139 | ||
@@ -140,6 +166,24 @@ impl Bindings { | |||
140 | } | 166 | } |
141 | } | 167 | } |
142 | 168 | ||
169 | fn collect_vars(subtree: &crate::Subtree) -> Vec<SmolStr> { | ||
170 | let mut res = vec![]; | ||
171 | |||
172 | for tkn in subtree.token_trees.iter() { | ||
173 | match tkn { | ||
174 | crate::TokenTree::Leaf(crate::Leaf::Var(crate::Var { text, .. })) => { | ||
175 | res.push(text.clone()); | ||
176 | } | ||
177 | crate::TokenTree::Subtree(subtree) => { | ||
178 | res.extend(collect_vars(subtree)); | ||
179 | } | ||
180 | _ => {} | ||
181 | } | ||
182 | } | ||
183 | |||
184 | res | ||
185 | } | ||
186 | |||
143 | fn match_lhs(pattern: &crate::Subtree, input: &mut TtCursor) -> Result<Bindings, ExpandError> { | 187 | fn match_lhs(pattern: &crate::Subtree, input: &mut TtCursor) -> Result<Bindings, ExpandError> { |
144 | let mut res = Bindings::default(); | 188 | let mut res = Bindings::default(); |
145 | for pat in pattern.token_trees.iter() { | 189 | for pat in pattern.token_trees.iter() { |
@@ -217,18 +261,7 @@ fn match_lhs(pattern: &crate::Subtree, input: &mut TtCursor) -> Result<Bindings, | |||
217 | let vis = vis.clone(); | 261 | let vis = vis.clone(); |
218 | res.inner.insert(text.clone(), Binding::Simple(vis.into())); | 262 | res.inner.insert(text.clone(), Binding::Simple(vis.into())); |
219 | } else { | 263 | } else { |
220 | // FIXME: Do we have a better way to represent an empty token ? | 264 | res.push_optional(&text); |
221 | // Insert an empty subtree for empty token | ||
222 | res.inner.insert( | ||
223 | text.clone(), | ||
224 | Binding::Simple( | ||
225 | tt::Subtree { | ||
226 | delimiter: tt::Delimiter::None, | ||
227 | token_trees: vec![], | ||
228 | } | ||
229 | .into(), | ||
230 | ), | ||
231 | ); | ||
232 | } | 265 | } |
233 | } | 266 | } |
234 | 267 | ||
@@ -295,6 +328,10 @@ fn match_lhs(pattern: &crate::Subtree, input: &mut TtCursor) -> Result<Bindings, | |||
295 | crate::RepeatKind::OneOrMore if counter == 0 => { | 328 | crate::RepeatKind::OneOrMore if counter == 0 => { |
296 | return Err(ExpandError::UnexpectedToken); | 329 | return Err(ExpandError::UnexpectedToken); |
297 | } | 330 | } |
331 | _ if counter == 0 => { | ||
332 | // Collect all empty variables in subtrees | ||
333 | collect_vars(subtree).iter().for_each(|s| res.push_empty(s)); | ||
334 | } | ||
298 | _ => {} | 335 | _ => {} |
299 | } | 336 | } |
300 | } | 337 | } |
diff --git a/crates/ra_mbe/src/tests.rs b/crates/ra_mbe/src/tests.rs index cdbd4dd1c..bd5a44240 100644 --- a/crates/ra_mbe/src/tests.rs +++ b/crates/ra_mbe/src/tests.rs | |||
@@ -1244,7 +1244,12 @@ fn test_cfg_if_main() { | |||
1244 | $( ( ($($meta),*) ($($it)*) ), )* | 1244 | $( ( ($($meta),*) ($($it)*) ), )* |
1245 | ( () ($($it2)*) ), | 1245 | ( () ($($it2)*) ), |
1246 | } | 1246 | } |
1247 | } | 1247 | }; |
1248 | |||
1249 | // Internal macro to Apply a cfg attribute to a list of items | ||
1250 | (@__apply $m:meta, $($it:item)*) => { | ||
1251 | $(#[$m] $it)* | ||
1252 | }; | ||
1248 | } | 1253 | } |
1249 | "#, | 1254 | "#, |
1250 | ); | 1255 | ); |
@@ -1262,6 +1267,12 @@ cfg_if ! { | |||
1262 | } | 1267 | } |
1263 | "#, | 1268 | "#, |
1264 | "__cfg_if_items ! {() ; ((target_env = \"msvc\") ()) , ((all (target_arch = \"wasm32\" , not (target_os = \"emscripten\"))) ()) , (() (mod libunwind ; pub use libunwind :: * ;)) ,}"); | 1269 | "__cfg_if_items ! {() ; ((target_env = \"msvc\") ()) , ((all (target_arch = \"wasm32\" , not (target_os = \"emscripten\"))) ()) , (() (mod libunwind ; pub use libunwind :: * ;)) ,}"); |
1270 | |||
1271 | assert_expansion(MacroKind::Items, &rules, r#" | ||
1272 | cfg_if ! { @ __apply cfg ( all ( not ( any ( not ( any ( target_os = "solaris" , target_os = "illumos" ) ) ) ) ) ) , } | ||
1273 | "#, | ||
1274 | "" | ||
1275 | ); | ||
1265 | } | 1276 | } |
1266 | 1277 | ||
1267 | #[test] | 1278 | #[test] |