aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--crates/ra_mbe/src/mbe_expander.rs61
-rw-r--r--crates/ra_mbe/src/tests.rs13
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 {
81enum Binding { 81enum Binding {
82 Simple(tt::TokenTree), 82 Simple(tt::TokenTree),
83 Nested(Vec<Binding>), 83 Nested(Vec<Binding>),
84 Empty,
84} 85}
85 86
86impl Bindings { 87impl 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
169fn 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
143fn match_lhs(pattern: &crate::Subtree, input: &mut TtCursor) -> Result<Bindings, ExpandError> { 187fn 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#"
1272cfg_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]