aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorbors[bot] <bors[bot]@users.noreply.github.com>2019-04-21 11:34:07 +0100
committerbors[bot] <bors[bot]@users.noreply.github.com>2019-04-21 11:34:07 +0100
commitfa15c4d75e87bd3bf761c91f030c76aec59308ae (patch)
treeba7a3495b13e41231076d5a0e491132bbdb0670e
parent493bf20b3d1a0a890514d5252901f13d2878ff34 (diff)
parent9e35bf91b827900b089a7ea937cb73707bebc420 (diff)
Merge #1175
1175: Fix bugs and add error log about macro expansion r=matklad a=edwin0cheng This PR fixed / add following things: * Add a fused count which stop recursion of macro expansion in name resolution. * Add some logs when macro expansion fails * Add `$crate` meta variable support in mbe, which create a `$crate` ident token in token tree. * Fixed matching a `$REPEAT` pattern inside a subtree, e.g. `(fn $name:ident {$($i:ident)*} ) => {...}` * Remove composite-able punct token in syntax node to token conversion. Co-authored-by: Edwin Cheng <[email protected]>
-rw-r--r--crates/ra_hir/src/ids.rs24
-rw-r--r--crates/ra_hir/src/nameres/collector.rs21
-rw-r--r--crates/ra_mbe/src/lib.rs110
-rw-r--r--crates/ra_mbe/src/mbe_expander.rs30
-rw-r--r--crates/ra_mbe/src/subtree_source.rs19
5 files changed, 154 insertions, 50 deletions
diff --git a/crates/ra_hir/src/ids.rs b/crates/ra_hir/src/ids.rs
index 141c9072f..2a1ed9b81 100644
--- a/crates/ra_hir/src/ids.rs
+++ b/crates/ra_hir/src/ids.rs
@@ -63,8 +63,15 @@ impl HirFileId {
63 match file_id.0 { 63 match file_id.0 {
64 HirFileIdRepr::File(file_id) => db.parse(file_id), 64 HirFileIdRepr::File(file_id) => db.parse(file_id),
65 HirFileIdRepr::Macro(macro_call_id) => { 65 HirFileIdRepr::Macro(macro_call_id) => {
66 // returning an empty string looks fishy... 66 parse_macro(db, macro_call_id).unwrap_or_else(|| {
67 parse_macro(db, macro_call_id).unwrap_or_else(|| SourceFile::parse("")) 67 // Note:
68 // The final goal we would like to make all parse_macro success,
69 // such that the following log will not call anyway.
70 log::warn!("fail on macro_parse: {}", macro_call_id.debug_dump(db));
71
72 // returning an empty string looks fishy...
73 SourceFile::parse("")
74 })
68 } 75 }
69 } 76 }
70 } 77 }
@@ -299,3 +306,16 @@ impl AstItemDef<ast::TypeAliasDef> for TypeAliasId {
299 db.lookup_intern_type_alias(self) 306 db.lookup_intern_type_alias(self)
300 } 307 }
301} 308}
309
310impl MacroCallId {
311 pub fn debug_dump(&self, db: &impl DefDatabase) -> String {
312 let loc = self.clone().loc(db);
313 let node = loc.ast_id.to_node(db);
314 let syntax_str = node.syntax().to_string();
315
316 // dump the file name
317 let file_id: HirFileId = self.clone().into();
318 let original = file_id.original_file(db);
319 format!("macro call [file: {:#?}] : {}", db.file_relative_path(original), syntax_str)
320 }
321}
diff --git a/crates/ra_hir/src/nameres/collector.rs b/crates/ra_hir/src/nameres/collector.rs
index 39cadc94a..6147b3219 100644
--- a/crates/ra_hir/src/nameres/collector.rs
+++ b/crates/ra_hir/src/nameres/collector.rs
@@ -42,6 +42,7 @@ pub(super) fn collect_defs(db: &impl DefDatabase, mut def_map: CrateDefMap) -> C
42 unresolved_imports: Vec::new(), 42 unresolved_imports: Vec::new(),
43 unexpanded_macros: Vec::new(), 43 unexpanded_macros: Vec::new(),
44 global_macro_scope: FxHashMap::default(), 44 global_macro_scope: FxHashMap::default(),
45 marco_stack_count: 0,
45 }; 46 };
46 collector.collect(); 47 collector.collect();
47 collector.finish() 48 collector.finish()
@@ -55,6 +56,10 @@ struct DefCollector<DB> {
55 unresolved_imports: Vec<(CrateModuleId, raw::ImportId, raw::ImportData)>, 56 unresolved_imports: Vec<(CrateModuleId, raw::ImportId, raw::ImportData)>,
56 unexpanded_macros: Vec<(CrateModuleId, AstId<ast::MacroCall>, Path)>, 57 unexpanded_macros: Vec<(CrateModuleId, AstId<ast::MacroCall>, Path)>,
57 global_macro_scope: FxHashMap<Name, MacroDefId>, 58 global_macro_scope: FxHashMap<Name, MacroDefId>,
59
60 /// Some macro use `$tt:tt which mean we have to handle the macro perfectly
61 /// To prevent stackoverflow, we add a deep counter here for prevent that.
62 marco_stack_count: u32,
58} 63}
59 64
60impl<'a, DB> DefCollector<&'a DB> 65impl<'a, DB> DefCollector<&'a DB>
@@ -324,10 +329,18 @@ where
324 } 329 }
325 330
326 fn collect_macro_expansion(&mut self, module_id: CrateModuleId, macro_call_id: MacroCallId) { 331 fn collect_macro_expansion(&mut self, module_id: CrateModuleId, macro_call_id: MacroCallId) {
327 let file_id: HirFileId = macro_call_id.into(); 332 self.marco_stack_count += 1;
328 let raw_items = self.db.raw_items(file_id); 333
329 ModCollector { def_collector: &mut *self, file_id, module_id, raw_items: &raw_items } 334 if self.marco_stack_count < 300 {
330 .collect(raw_items.items()) 335 let file_id: HirFileId = macro_call_id.into();
336 let raw_items = self.db.raw_items(file_id);
337 ModCollector { def_collector: &mut *self, file_id, module_id, raw_items: &raw_items }
338 .collect(raw_items.items())
339 } else {
340 log::error!("Too deep macro expansion: {}", macro_call_id.debug_dump(self.db));
341 }
342
343 self.marco_stack_count -= 1;
331 } 344 }
332 345
333 fn finish(self) -> CrateDefMap { 346 fn finish(self) -> CrateDefMap {
diff --git a/crates/ra_mbe/src/lib.rs b/crates/ra_mbe/src/lib.rs
index d2115bd67..9aad08db9 100644
--- a/crates/ra_mbe/src/lib.rs
+++ b/crates/ra_mbe/src/lib.rs
@@ -214,14 +214,15 @@ impl_froms!(TokenTree: Leaf, Subtree);
214 let tree = token_tree_to_macro_items(&expanded); 214 let tree = token_tree_to_macro_items(&expanded);
215 215
216 // Eat all white space by parse it back and forth 216 // Eat all white space by parse it back and forth
217 let expansion = ast::SourceFile::parse(expansion); 217 // Because $crate will seperate in two token , will do some special treatment here
218 let expansion = expansion.replace("$crate", "C_C__C");
219 let expansion = ast::SourceFile::parse(&expansion);
218 let expansion = syntax_node_to_token_tree(expansion.syntax()).unwrap().0; 220 let expansion = syntax_node_to_token_tree(expansion.syntax()).unwrap().0;
219 let file = token_tree_to_macro_items(&expansion); 221 let file = token_tree_to_macro_items(&expansion);
222 let file = file.unwrap().syntax().debug_dump().trim().to_string();
223 let file = file.replace("C_C__C", "$crate");
220 224
221 assert_eq!( 225 assert_eq!(tree.unwrap().syntax().debug_dump().trim(), file,);
222 tree.unwrap().syntax().debug_dump().trim(),
223 file.unwrap().syntax().debug_dump().trim()
224 );
225 } 226 }
226 227
227 #[test] 228 #[test]
@@ -348,7 +349,36 @@ impl_froms!(TokenTree: Leaf, Subtree);
348 } 349 }
349 350
350 #[test] 351 #[test]
351 fn expand_to_item_list() { 352 fn test_match_group_empty_fixed_token() {
353 let rules = create_rules(
354 r#"
355 macro_rules! foo {
356 ($ ($ i:ident)* #abc) => ( fn baz { $ (
357 $ i ();
358 )*} );
359 }
360"#,
361 );
362
363 assert_expansion(&rules, "foo! {#abc}", "fn baz {}");
364 }
365
366 #[test]
367 fn test_match_group_in_subtree() {
368 let rules = create_rules(
369 r#"
370 macro_rules! foo {
371 (fn $name:ident {$($i:ident)*} ) => ( fn $name() { $ (
372 $ i ();
373 )*} );
374 }"#,
375 );
376
377 assert_expansion(&rules, "foo! {fn baz {a b} }", "fn baz () {a () ; b () ;}");
378 }
379
380 #[test]
381 fn test_expand_to_item_list() {
352 let rules = create_rules( 382 let rules = create_rules(
353 " 383 "
354 macro_rules! structs { 384 macro_rules! structs {
@@ -401,7 +431,7 @@ MACRO_ITEMS@[0; 40)
401 } 431 }
402 432
403 #[test] 433 #[test]
404 fn expand_literals_to_token_tree() { 434 fn test_expand_literals_to_token_tree() {
405 fn to_subtree(tt: &tt::TokenTree) -> &tt::Subtree { 435 fn to_subtree(tt: &tt::TokenTree) -> &tt::Subtree {
406 if let tt::TokenTree::Subtree(subtree) = tt { 436 if let tt::TokenTree::Subtree(subtree) = tt {
407 return &subtree; 437 return &subtree;
@@ -763,30 +793,29 @@ MACRO_ITEMS@[0; 40)
763 ); 793 );
764 } 794 }
765 795
766 // #[test] 796 #[test]
767 // fn test_tt_block() { 797 // fn test_tt_block() {
768 // let rules = create_rules( 798 // let rules = create_rules(
769 // r#" 799 // r#"
770 // macro_rules! foo { 800 // macro_rules! foo {
771 // ($ i:tt) => { fn foo() $ i } 801 // ($ i:tt) => { fn foo() $ i }
772 // } 802 // }
773 // "#, 803 // "#,
774 // ); 804 // );
775 // assert_expansion(&rules, r#"foo! { { 1; } }"#, r#"fn foo () {1 ;}"#); 805 // assert_expansion(&rules, r#"foo! { { 1; } }"#, r#"fn foo () {1 ;}"#);
776 // } 806 // }
777 807
778 // #[test] 808 // #[test]
779 // fn test_tt_group() { 809 // fn test_tt_group() {
780 // let rules = create_rules( 810 // let rules = create_rules(
781 // r#" 811 // r#"
782 // macro_rules! foo { 812 // macro_rules! foo {
783 // ($($ i:tt)*) => { $($ i)* } 813 // ($($ i:tt)*) => { $($ i)* }
784 // } 814 // }
785 // "#, 815 // "#,
786 // ); 816 // );
787 // assert_expansion(&rules, r#"foo! { fn foo() {} }"#, r#"fn foo () {}"#); 817 // assert_expansion(&rules, r#"foo! { fn foo() {} }"#, r#"fn foo () {}"#);
788 // } 818 // }
789
790 #[test] 819 #[test]
791 fn test_lifetime() { 820 fn test_lifetime() {
792 let rules = create_rules( 821 let rules = create_rules(
@@ -822,4 +851,39 @@ MACRO_ITEMS@[0; 40)
822 ); 851 );
823 assert_expansion(&rules, r#"foo!(pub foo);"#, r#"pub fn foo () {}"#); 852 assert_expansion(&rules, r#"foo!(pub foo);"#, r#"pub fn foo () {}"#);
824 } 853 }
854
855 // The following tests are based on real world situations
856 #[test]
857 fn test_winapi_struct() {
858 // from https://github.com/retep998/winapi-rs/blob/a7ef2bca086aae76cf6c4ce4c2552988ed9798ad/src/macros.rs#L366
859
860 let rules = create_rules(
861 r#"
862macro_rules! STRUCT {
863 ($(#[$attrs:meta])* struct $name:ident {
864 $($field:ident: $ftype:ty,)+
865 }) => (
866 #[repr(C)] #[derive(Copy)] $(#[$attrs])*
867 pub struct $name {
868 $(pub $field: $ftype,)+
869 }
870 impl Clone for $name {
871 #[inline]
872 fn clone(&self) -> $name { *self }
873 }
874 #[cfg(feature = "impl-default")]
875 impl Default for $name {
876 #[inline]
877 fn default() -> $name { unsafe { $crate::_core::mem::zeroed() } }
878 }
879 );
880}
881"#,
882 );
883 // from https://github.com/retep998/winapi-rs/blob/a7ef2bca086aae76cf6c4ce4c2552988ed9798ad/src/shared/d3d9caps.rs
884 assert_expansion(&rules, r#"STRUCT!{struct D3DVSHADERCAPS2_0 {Caps: u8,}}"#,
885 "# [repr (C)] # [derive (Copy)] pub struct D3DVSHADERCAPS2_0 {pub Caps : u8 ,} impl Clone for D3DVSHADERCAPS2_0 {# [inline] fn clone (& self) -> D3DVSHADERCAPS2_0 {* self}} # [cfg (feature = \"impl-default\")] impl Default for D3DVSHADERCAPS2_0 {# [inline] fn default () -> D3DVSHADERCAPS2_0 {unsafe {$crate :: _core :: mem :: zeroed ()}}}");
886 assert_expansion(&rules, r#"STRUCT!{#[cfg_attr(target_arch = "x86", repr(packed))] struct D3DCONTENTPROTECTIONCAPS {Caps : u8 ,}}"#,
887 "# [repr (C)] # [derive (Copy)] # [cfg_attr (target_arch = \"x86\" , repr (packed))] pub struct D3DCONTENTPROTECTIONCAPS {pub Caps : u8 ,} impl Clone for D3DCONTENTPROTECTIONCAPS {# [inline] fn clone (& self) -> D3DCONTENTPROTECTIONCAPS {* self}} # [cfg (feature = \"impl-default\")] impl Default for D3DCONTENTPROTECTIONCAPS {# [inline] fn default () -> D3DCONTENTPROTECTIONCAPS {unsafe {$crate :: _core :: mem :: zeroed ()}}}");
888 }
825} 889}
diff --git a/crates/ra_mbe/src/mbe_expander.rs b/crates/ra_mbe/src/mbe_expander.rs
index 86867111f..66ea76698 100644
--- a/crates/ra_mbe/src/mbe_expander.rs
+++ b/crates/ra_mbe/src/mbe_expander.rs
@@ -121,6 +121,10 @@ impl Bindings {
121 } 121 }
122 Ok(()) 122 Ok(())
123 } 123 }
124
125 fn merge(&mut self, nested: Bindings) {
126 self.inner.extend(nested.inner);
127 }
124} 128}
125 129
126fn match_lhs(pattern: &crate::Subtree, input: &mut TtCursor) -> Result<Bindings, ExpandError> { 130fn match_lhs(pattern: &crate::Subtree, input: &mut TtCursor) -> Result<Bindings, ExpandError> {
@@ -236,7 +240,21 @@ fn match_lhs(pattern: &crate::Subtree, input: &mut TtCursor) -> Result<Bindings,
236 } 240 }
237 } 241 }
238 } 242 }
239 _ => {} 243 crate::TokenTree::Subtree(subtree) => {
244 let input_subtree =
245 input.eat_subtree().map_err(|_| ExpandError::UnexpectedToken)?;
246 if subtree.delimiter != input_subtree.delimiter {
247 return Err(ExpandError::UnexpectedToken);
248 }
249
250 let mut input = TtCursor::new(input_subtree);
251 let bindings = match_lhs(&subtree, &mut input)?;
252 if !input.is_eof() {
253 return Err(ExpandError::UnexpectedToken);
254 }
255
256 res.merge(bindings);
257 }
240 } 258 }
241 } 259 }
242 Ok(res) 260 Ok(res)
@@ -287,7 +305,15 @@ fn expand_tt(
287 .into() 305 .into()
288 } 306 }
289 crate::Leaf::Punct(punct) => tt::Leaf::from(punct.clone()).into(), 307 crate::Leaf::Punct(punct) => tt::Leaf::from(punct.clone()).into(),
290 crate::Leaf::Var(v) => bindings.get(&v.text, nesting)?.clone(), 308 crate::Leaf::Var(v) => {
309 if v.text == "crate" {
310 // FIXME: Properly handle $crate token
311 tt::Leaf::from(tt::Ident { text: "$crate".into(), id: TokenId::unspecified() })
312 .into()
313 } else {
314 bindings.get(&v.text, nesting)?.clone()
315 }
316 }
291 crate::Leaf::Literal(l) => tt::Leaf::from(tt::Literal { text: l.text.clone() }).into(), 317 crate::Leaf::Literal(l) => tt::Leaf::from(tt::Literal { text: l.text.clone() }).into(),
292 }, 318 },
293 }; 319 };
diff --git a/crates/ra_mbe/src/subtree_source.rs b/crates/ra_mbe/src/subtree_source.rs
index 0a070b46a..16a053b49 100644
--- a/crates/ra_mbe/src/subtree_source.rs
+++ b/crates/ra_mbe/src/subtree_source.rs
@@ -379,8 +379,6 @@ where
379{ 379{
380 if let Some((m, is_joint_to_next)) = iter.current_punct3(p) { 380 if let Some((m, is_joint_to_next)) = iter.current_punct3(p) {
381 if let Some((kind, text)) = match m { 381 if let Some((kind, text)) = match m {
382 ('<', '<', '=') => Some((SHLEQ, "<<=")),
383 ('>', '>', '=') => Some((SHREQ, ">>=")),
384 ('.', '.', '.') => Some((DOTDOTDOT, "...")), 382 ('.', '.', '.') => Some((DOTDOTDOT, "...")),
385 ('.', '.', '=') => Some((DOTDOTEQ, "..=")), 383 ('.', '.', '=') => Some((DOTDOTEQ, "..=")),
386 _ => None, 384 _ => None,
@@ -391,23 +389,6 @@ where
391 389
392 if let Some((m, is_joint_to_next)) = iter.current_punct2(p) { 390 if let Some((m, is_joint_to_next)) = iter.current_punct2(p) {
393 if let Some((kind, text)) = match m { 391 if let Some((kind, text)) = match m {
394 ('<', '<') => Some((SHL, "<<")),
395 ('>', '>') => Some((SHR, ">>")),
396
397 ('|', '|') => Some((PIPEPIPE, "||")),
398 ('&', '&') => Some((AMPAMP, "&&")),
399 ('%', '=') => Some((PERCENTEQ, "%=")),
400 ('*', '=') => Some((STAREQ, "*=")),
401 ('/', '=') => Some((SLASHEQ, "/=")),
402 ('^', '=') => Some((CARETEQ, "^=")),
403
404 ('&', '=') => Some((AMPEQ, "&=")),
405 ('|', '=') => Some((PIPEEQ, "|=")),
406 ('-', '=') => Some((MINUSEQ, "-=")),
407 ('+', '=') => Some((PLUSEQ, "+=")),
408 ('>', '=') => Some((GTEQ, ">=")),
409 ('<', '=') => Some((LTEQ, "<=")),
410
411 ('-', '>') => Some((THIN_ARROW, "->")), 392 ('-', '>') => Some((THIN_ARROW, "->")),
412 ('!', '=') => Some((NEQ, "!=")), 393 ('!', '=') => Some((NEQ, "!=")),
413 ('=', '>') => Some((FAT_ARROW, "=>")), 394 ('=', '>') => Some((FAT_ARROW, "=>")),