aboutsummaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
Diffstat (limited to 'crates')
-rw-r--r--crates/ra_hir_def/src/body.rs9
-rw-r--r--crates/ra_hir_def/src/body/lower.rs12
-rw-r--r--crates/ra_hir_expand/src/db.rs24
-rw-r--r--crates/ra_hir_ty/src/tests/regression.rs31
-rw-r--r--crates/ra_ide_db/src/symbol_index.rs7
-rw-r--r--crates/ra_project_model/src/lib.rs6
-rw-r--r--crates/ra_syntax/src/algo.rs4
-rw-r--r--crates/ra_syntax/src/ast/extensions.rs13
8 files changed, 87 insertions, 19 deletions
diff --git a/crates/ra_hir_def/src/body.rs b/crates/ra_hir_def/src/body.rs
index 27a297e8b..5f9d53ecb 100644
--- a/crates/ra_hir_def/src/body.rs
+++ b/crates/ra_hir_def/src/body.rs
@@ -30,6 +30,7 @@ pub(crate) struct Expander {
30 hygiene: Hygiene, 30 hygiene: Hygiene,
31 ast_id_map: Arc<AstIdMap>, 31 ast_id_map: Arc<AstIdMap>,
32 module: ModuleId, 32 module: ModuleId,
33 recursive_limit: usize,
33} 34}
34 35
35impl Expander { 36impl Expander {
@@ -41,7 +42,7 @@ impl Expander {
41 let crate_def_map = db.crate_def_map(module.krate); 42 let crate_def_map = db.crate_def_map(module.krate);
42 let hygiene = Hygiene::new(db.upcast(), current_file_id); 43 let hygiene = Hygiene::new(db.upcast(), current_file_id);
43 let ast_id_map = db.ast_id_map(current_file_id); 44 let ast_id_map = db.ast_id_map(current_file_id);
44 Expander { crate_def_map, current_file_id, hygiene, ast_id_map, module } 45 Expander { crate_def_map, current_file_id, hygiene, ast_id_map, module, recursive_limit: 0 }
45 } 46 }
46 47
47 pub(crate) fn enter_expand<T: ast::AstNode>( 48 pub(crate) fn enter_expand<T: ast::AstNode>(
@@ -50,6 +51,10 @@ impl Expander {
50 local_scope: Option<&ItemScope>, 51 local_scope: Option<&ItemScope>,
51 macro_call: ast::MacroCall, 52 macro_call: ast::MacroCall,
52 ) -> Option<(Mark, T)> { 53 ) -> Option<(Mark, T)> {
54 if self.recursive_limit > 1024 {
55 return None;
56 }
57
53 let macro_call = InFile::new(self.current_file_id, &macro_call); 58 let macro_call = InFile::new(self.current_file_id, &macro_call);
54 59
55 if let Some(call_id) = macro_call.as_call_id(db, |path| { 60 if let Some(call_id) = macro_call.as_call_id(db, |path| {
@@ -73,6 +78,7 @@ impl Expander {
73 self.hygiene = Hygiene::new(db.upcast(), file_id); 78 self.hygiene = Hygiene::new(db.upcast(), file_id);
74 self.current_file_id = file_id; 79 self.current_file_id = file_id;
75 self.ast_id_map = db.ast_id_map(file_id); 80 self.ast_id_map = db.ast_id_map(file_id);
81 self.recursive_limit += 1;
76 82
77 return Some((mark, expr)); 83 return Some((mark, expr));
78 } 84 }
@@ -88,6 +94,7 @@ impl Expander {
88 self.hygiene = Hygiene::new(db.upcast(), mark.file_id); 94 self.hygiene = Hygiene::new(db.upcast(), mark.file_id);
89 self.current_file_id = mark.file_id; 95 self.current_file_id = mark.file_id;
90 self.ast_id_map = mem::take(&mut mark.ast_id_map); 96 self.ast_id_map = mem::take(&mut mark.ast_id_map);
97 self.recursive_limit -= 1;
91 mark.bomb.defuse(); 98 mark.bomb.defuse();
92 } 99 }
93 100
diff --git a/crates/ra_hir_def/src/body/lower.rs b/crates/ra_hir_def/src/body/lower.rs
index e8c58ed32..3cf0c66ea 100644
--- a/crates/ra_hir_def/src/body/lower.rs
+++ b/crates/ra_hir_def/src/body/lower.rs
@@ -453,7 +453,7 @@ impl ExprCollector<'_> {
453 } 453 }
454 } 454 }
455 ast::Expr::MacroCall(e) => { 455 ast::Expr::MacroCall(e) => {
456 if let Some(name) = is_macro_rules(&e) { 456 if let Some(name) = e.is_macro_rules().map(|it| it.as_name()) {
457 let mac = MacroDefId { 457 let mac = MacroDefId {
458 krate: Some(self.expander.module.krate), 458 krate: Some(self.expander.module.krate),
459 ast_id: Some(self.expander.ast_id(&e)), 459 ast_id: Some(self.expander.ast_id(&e)),
@@ -697,16 +697,6 @@ impl ExprCollector<'_> {
697 } 697 }
698} 698}
699 699
700fn is_macro_rules(m: &ast::MacroCall) -> Option<Name> {
701 let name = m.path()?.segment()?.name_ref()?.as_name();
702
703 if name == name![macro_rules] {
704 Some(m.name()?.as_name())
705 } else {
706 None
707 }
708}
709
710impl From<ast::BinOp> for BinaryOp { 700impl From<ast::BinOp> for BinaryOp {
711 fn from(ast_op: ast::BinOp) -> Self { 701 fn from(ast_op: ast::BinOp) -> Self {
712 match ast_op { 702 match ast_op {
diff --git a/crates/ra_hir_expand/src/db.rs b/crates/ra_hir_expand/src/db.rs
index d171d2dfd..5a696542f 100644
--- a/crates/ra_hir_expand/src/db.rs
+++ b/crates/ra_hir_expand/src/db.rs
@@ -6,7 +6,7 @@ use mbe::{ExpandResult, MacroRules};
6use ra_db::{salsa, SourceDatabase}; 6use ra_db::{salsa, SourceDatabase};
7use ra_parser::FragmentKind; 7use ra_parser::FragmentKind;
8use ra_prof::profile; 8use ra_prof::profile;
9use ra_syntax::{AstNode, Parse, SyntaxKind::*, SyntaxNode}; 9use ra_syntax::{algo::diff, AstNode, Parse, SyntaxKind::*, SyntaxNode};
10 10
11use crate::{ 11use crate::{
12 ast_id_map::AstIdMap, BuiltinDeriveExpander, BuiltinFnLikeExpander, EagerCallLoc, EagerMacroId, 12 ast_id_map::AstIdMap, BuiltinDeriveExpander, BuiltinFnLikeExpander, EagerCallLoc, EagerMacroId,
@@ -238,7 +238,7 @@ pub fn parse_macro_with_arg(
238 } else { 238 } else {
239 db.macro_expand(macro_call_id) 239 db.macro_expand(macro_call_id)
240 }; 240 };
241 if let Some(err) = err { 241 if let Some(err) = &err {
242 // Note: 242 // Note:
243 // The final goal we would like to make all parse_macro success, 243 // The final goal we would like to make all parse_macro success,
244 // such that the following log will not call anyway. 244 // such that the following log will not call anyway.
@@ -272,7 +272,25 @@ pub fn parse_macro_with_arg(
272 let fragment_kind = to_fragment_kind(db, macro_call_id); 272 let fragment_kind = to_fragment_kind(db, macro_call_id);
273 273
274 let (parse, rev_token_map) = mbe::token_tree_to_syntax_node(&tt, fragment_kind).ok()?; 274 let (parse, rev_token_map) = mbe::token_tree_to_syntax_node(&tt, fragment_kind).ok()?;
275 Some((parse, Arc::new(rev_token_map))) 275
276 if err.is_none() {
277 Some((parse, Arc::new(rev_token_map)))
278 } else {
279 // FIXME:
280 // In future, we should propagate the actual error with recovery information
281 // instead of ignore the error here.
282
283 // Safe check for recurisve identity macro
284 let node = parse.syntax_node();
285 let file: HirFileId = macro_file.into();
286 let call_node = file.call_node(db)?;
287
288 if !diff(&node, &call_node.value).is_empty() {
289 Some((parse, Arc::new(rev_token_map)))
290 } else {
291 None
292 }
293 }
276} 294}
277 295
278/// Given a `MacroCallId`, return what `FragmentKind` it belongs to. 296/// Given a `MacroCallId`, return what `FragmentKind` it belongs to.
diff --git a/crates/ra_hir_ty/src/tests/regression.rs b/crates/ra_hir_ty/src/tests/regression.rs
index 14c8ed3a9..a02e3ee05 100644
--- a/crates/ra_hir_ty/src/tests/regression.rs
+++ b/crates/ra_hir_ty/src/tests/regression.rs
@@ -453,3 +453,34 @@ pub mod str {
453 // should be Option<char>, but currently not because of Chalk ambiguity problem 453 // should be Option<char>, but currently not because of Chalk ambiguity problem
454 assert_eq!("(Option<{unknown}>, Option<{unknown}>)", super::type_at_pos(&db, pos)); 454 assert_eq!("(Option<{unknown}>, Option<{unknown}>)", super::type_at_pos(&db, pos));
455} 455}
456
457#[test]
458fn issue_3642_bad_macro_stackover() {
459 let (db, pos) = TestDB::with_position(
460 r#"
461//- /main.rs
462#[macro_export]
463macro_rules! match_ast {
464 (match $node:ident { $($tt:tt)* }) => { match_ast!(match ($node) { $($tt)* }) };
465
466 (match ($node:expr) {
467 $( ast::$ast:ident($it:ident) => $res:expr, )*
468 _ => $catch_all:expr $(,)?
469 }) => {{
470 $( if let Some($it) = ast::$ast::cast($node.clone()) { $res } else )*
471 { $catch_all }
472 }};
473}
474
475fn main() {
476 let anchor<|> = match_ast! {
477 match parent {
478 as => {},
479 _ => return None
480 }
481 };
482}"#,
483 );
484
485 assert_eq!("()", super::type_at_pos(&db, pos));
486}
diff --git a/crates/ra_ide_db/src/symbol_index.rs b/crates/ra_ide_db/src/symbol_index.rs
index 884359ee3..0f46f93c1 100644
--- a/crates/ra_ide_db/src/symbol_index.rs
+++ b/crates/ra_ide_db/src/symbol_index.rs
@@ -362,6 +362,13 @@ fn to_symbol(node: &SyntaxNode) -> Option<(SmolStr, SyntaxNodePtr, TextRange)> {
362 ast::TypeAliasDef(it) => { decl(it) }, 362 ast::TypeAliasDef(it) => { decl(it) },
363 ast::ConstDef(it) => { decl(it) }, 363 ast::ConstDef(it) => { decl(it) },
364 ast::StaticDef(it) => { decl(it) }, 364 ast::StaticDef(it) => { decl(it) },
365 ast::MacroCall(it) => {
366 if it.is_macro_rules().is_some() {
367 decl(it)
368 } else {
369 None
370 }
371 },
365 _ => None, 372 _ => None,
366 } 373 }
367 } 374 }
diff --git a/crates/ra_project_model/src/lib.rs b/crates/ra_project_model/src/lib.rs
index b500a74fb..a3ef9acdc 100644
--- a/crates/ra_project_model/src/lib.rs
+++ b/crates/ra_project_model/src/lib.rs
@@ -336,7 +336,7 @@ impl ProjectWorkspace {
336 extern_source, 336 extern_source,
337 ); 337 );
338 if cargo[tgt].kind == TargetKind::Lib { 338 if cargo[tgt].kind == TargetKind::Lib {
339 lib_tgt = Some(crate_id); 339 lib_tgt = Some((crate_id, cargo[tgt].name.clone()));
340 pkg_to_lib_crate.insert(pkg, crate_id); 340 pkg_to_lib_crate.insert(pkg, crate_id);
341 } 341 }
342 if cargo[tgt].is_proc_macro { 342 if cargo[tgt].is_proc_macro {
@@ -363,7 +363,7 @@ impl ProjectWorkspace {
363 363
364 // Set deps to the core, std and to the lib target of the current package 364 // Set deps to the core, std and to the lib target of the current package
365 for &from in pkg_crates.get(&pkg).into_iter().flatten() { 365 for &from in pkg_crates.get(&pkg).into_iter().flatten() {
366 if let Some(to) = lib_tgt { 366 if let Some((to, name)) = lib_tgt.clone() {
367 if to != from 367 if to != from
368 && crate_graph 368 && crate_graph
369 .add_dep( 369 .add_dep(
@@ -371,7 +371,7 @@ impl ProjectWorkspace {
371 // For root projects with dashes in their name, 371 // For root projects with dashes in their name,
372 // cargo metadata does not do any normalization, 372 // cargo metadata does not do any normalization,
373 // so we do it ourselves currently 373 // so we do it ourselves currently
374 CrateName::normalize_dashes(&cargo[pkg].name), 374 CrateName::normalize_dashes(&name),
375 to, 375 to,
376 ) 376 )
377 .is_err() 377 .is_err()
diff --git a/crates/ra_syntax/src/algo.rs b/crates/ra_syntax/src/algo.rs
index 344cf0fbe..ffdbdc767 100644
--- a/crates/ra_syntax/src/algo.rs
+++ b/crates/ra_syntax/src/algo.rs
@@ -95,6 +95,10 @@ impl TreeDiff {
95 builder.replace(from.text_range(), to.to_string()) 95 builder.replace(from.text_range(), to.to_string())
96 } 96 }
97 } 97 }
98
99 pub fn is_empty(&self) -> bool {
100 self.replacements.is_empty()
101 }
98} 102}
99 103
100/// Finds minimal the diff, which, applied to `from`, will result in `to`. 104/// Finds minimal the diff, which, applied to `from`, will result in `to`.
diff --git a/crates/ra_syntax/src/ast/extensions.rs b/crates/ra_syntax/src/ast/extensions.rs
index c3ae8f90e..392731dac 100644
--- a/crates/ra_syntax/src/ast/extensions.rs
+++ b/crates/ra_syntax/src/ast/extensions.rs
@@ -4,7 +4,7 @@
4use itertools::Itertools; 4use itertools::Itertools;
5 5
6use crate::{ 6use crate::{
7 ast::{self, child_opt, children, AstNode, AttrInput, SyntaxNode}, 7 ast::{self, child_opt, children, AstNode, AttrInput, NameOwner, SyntaxNode},
8 SmolStr, SyntaxElement, 8 SmolStr, SyntaxElement,
9 SyntaxKind::*, 9 SyntaxKind::*,
10 SyntaxToken, T, 10 SyntaxToken, T,
@@ -514,3 +514,14 @@ impl ast::Visibility {
514 self.syntax().children_with_tokens().any(|it| it.kind() == T![super]) 514 self.syntax().children_with_tokens().any(|it| it.kind() == T![super])
515 } 515 }
516} 516}
517
518impl ast::MacroCall {
519 pub fn is_macro_rules(&self) -> Option<ast::Name> {
520 let name_ref = self.path()?.segment()?.name_ref()?;
521 if name_ref.text() == "macro_rules" {
522 self.name()
523 } else {
524 None
525 }
526 }
527}