aboutsummaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
authorbors[bot] <bors[bot]@users.noreply.github.com>2019-05-04 16:54:04 +0100
committerbors[bot] <bors[bot]@users.noreply.github.com>2019-05-04 16:54:04 +0100
commite1ea2500fcc5e136fbe6a14c488f558e28519c45 (patch)
tree1cbcefd57c0dd75cd1c834e34dd00f9f89e9dd17 /crates
parentfcdb387f0d7e76f325a858e4463efd5d7ed3efc3 (diff)
parentbcf45371ff19882e67300cc483b481450ee129fb (diff)
Merge #1238
1238: Macro queries r=edwin0cheng a=matklad In https://github.com/rust-analyzer/rust-analyzer/pull/1231, I've added aggressive clean up of `ast_id_to_node` query. The result of this query is a `SyntaxTree`, and we don't want to retain syntax trees in memory unless absolutely necessary. Moreover, `SyntaxTree` has identity equality semantics, meaning that we'll get a diffferent syntax tree for a file after every reparse. That means that `ast_id_to_node` query should not genereally be used in HIR, unless it is behind some kind of salsa firewall, like the `raw` module of name resoulution. However, that PR resulted in the abysmal performance: turns out we were using ast_id_to_node quite heavily in hir when expanding macros! So this PR installs the more incremental-friendly query structure: * converting source to token tree is now a query; changing source without affecting token-trees will now preserve macro expansions * expand macro (tt -> tt) is now a query as well, so we cache macro expansions *before* parsing them into item lists or expressions, which is nice: we can cache expansion without knowing the calling context! r? @edwin0cheng Co-authored-by: Aleksey Kladov <[email protected]>
Diffstat (limited to 'crates')
-rw-r--r--crates/ra_hir/src/db.rs10
-rw-r--r--crates/ra_hir/src/expr.rs42
-rw-r--r--crates/ra_hir/src/ids.rs77
-rw-r--r--crates/ra_hir/src/source_id.rs2
-rw-r--r--crates/ra_syntax/src/grammar.ron2
5 files changed, 64 insertions, 69 deletions
diff --git a/crates/ra_hir/src/db.rs b/crates/ra_hir/src/db.rs
index 3ac3c79a3..398e00c42 100644
--- a/crates/ra_hir/src/db.rs
+++ b/crates/ra_hir/src/db.rs
@@ -42,7 +42,13 @@ pub trait DefDatabase: SourceDatabase {
42 #[salsa::invoke(crate::ids::macro_def_query)] 42 #[salsa::invoke(crate::ids::macro_def_query)]
43 fn macro_def(&self, macro_id: MacroDefId) -> Option<Arc<mbe::MacroRules>>; 43 fn macro_def(&self, macro_id: MacroDefId) -> Option<Arc<mbe::MacroRules>>;
44 44
45 #[salsa::invoke(HirFileId::hir_parse_query)] 45 #[salsa::invoke(crate::ids::macro_arg_query)]
46 fn macro_arg(&self, macro_call: ids::MacroCallId) -> Option<Arc<tt::Subtree>>;
47
48 #[salsa::invoke(crate::ids::macro_expand_query)]
49 fn macro_expand(&self, macro_call: ids::MacroCallId) -> Result<Arc<tt::Subtree>, String>;
50
51 #[salsa::invoke(crate::ids::HirFileId::hir_parse_query)]
46 fn hir_parse(&self, file_id: HirFileId) -> TreeArc<SourceFile>; 52 fn hir_parse(&self, file_id: HirFileId) -> TreeArc<SourceFile>;
47 53
48 #[salsa::invoke(crate::adt::StructData::struct_data_query)] 54 #[salsa::invoke(crate::adt::StructData::struct_data_query)]
@@ -60,7 +66,7 @@ pub trait DefDatabase: SourceDatabase {
60 #[salsa::invoke(crate::source_id::AstIdMap::ast_id_map_query)] 66 #[salsa::invoke(crate::source_id::AstIdMap::ast_id_map_query)]
61 fn ast_id_map(&self, file_id: HirFileId) -> Arc<AstIdMap>; 67 fn ast_id_map(&self, file_id: HirFileId) -> Arc<AstIdMap>;
62 68
63 #[salsa::invoke(crate::source_id::AstIdMap::ast_id_to_node_query)] 69 #[salsa::invoke(crate::source_id::AstIdMap::file_item_query)]
64 fn ast_id_to_node(&self, file_id: HirFileId, ast_id: ErasedFileAstId) -> TreeArc<SyntaxNode>; 70 fn ast_id_to_node(&self, file_id: HirFileId, ast_id: ErasedFileAstId) -> TreeArc<SyntaxNode>;
65 71
66 #[salsa::invoke(RawItems::raw_items_query)] 72 #[salsa::invoke(RawItems::raw_items_query)]
diff --git a/crates/ra_hir/src/expr.rs b/crates/ra_hir/src/expr.rs
index cdadf3ba6..692da2895 100644
--- a/crates/ra_hir/src/expr.rs
+++ b/crates/ra_hir/src/expr.rs
@@ -5,14 +5,13 @@ use rustc_hash::FxHashMap;
5 5
6use ra_arena::{Arena, RawId, impl_arena_id, map::ArenaMap}; 6use ra_arena::{Arena, RawId, impl_arena_id, map::ArenaMap};
7use ra_syntax::{ 7use ra_syntax::{
8 SyntaxNodePtr, AstPtr, AstNode,TreeArc, 8 SyntaxNodePtr, AstPtr, AstNode,
9 ast::{self, LoopBodyOwner, ArgListOwner, NameOwner, LiteralKind,ArrayExprKind, TypeAscriptionOwner} 9 ast::{self, LoopBodyOwner, ArgListOwner, NameOwner, LiteralKind,ArrayExprKind, TypeAscriptionOwner}
10}; 10};
11 11
12use crate::{ 12use crate::{
13 Path, Name, HirDatabase, Resolver,DefWithBody, Either, HirFileId, 13 Path, Name, HirDatabase, Resolver,DefWithBody, Either, HirFileId,
14 name::AsName, 14 name::AsName,
15 ids::{MacroCallId},
16 type_ref::{Mutability, TypeRef}, 15 type_ref::{Mutability, TypeRef},
17}; 16};
18use crate::{path::GenericArgs, ty::primitive::{IntTy, UncertainIntTy, FloatTy, UncertainFloatTy}}; 17use crate::{path::GenericArgs, ty::primitive::{IntTy, UncertainIntTy, FloatTy, UncertainFloatTy}};
@@ -826,21 +825,20 @@ where
826 .with_file_id(self.current_file_id); 825 .with_file_id(self.current_file_id);
827 826
828 if let Some(call_id) = self.resolver.resolve_macro_call(self.db, path, ast_id) { 827 if let Some(call_id) = self.resolver.resolve_macro_call(self.db, path, ast_id) {
829 if let Some(expr) = expand_macro_to_expr(self.db, call_id, e.token_tree()) { 828 if let Some(tt) = self.db.macro_expand(call_id).ok() {
830 log::debug!("macro expansion {}", expr.syntax().debug_dump()); 829 if let Some(expr) = mbe::token_tree_to_expr(&tt).ok() {
831 let old_file_id = 830 log::debug!("macro expansion {}", expr.syntax().debug_dump());
832 std::mem::replace(&mut self.current_file_id, call_id.into()); 831 let old_file_id =
833 let id = self.collect_expr(&expr); 832 std::mem::replace(&mut self.current_file_id, call_id.into());
834 self.current_file_id = old_file_id; 833 let id = self.collect_expr(&expr);
835 id 834 self.current_file_id = old_file_id;
836 } else { 835 return id;
837 // FIXME: Instead of just dropping the error from expansion 836 }
838 // report it
839 self.alloc_expr(Expr::Missing, syntax_ptr)
840 } 837 }
841 } else {
842 self.alloc_expr(Expr::Missing, syntax_ptr)
843 } 838 }
839 // FIXME: Instead of just dropping the error from expansion
840 // report it
841 self.alloc_expr(Expr::Missing, syntax_ptr)
844 } 842 }
845 } 843 }
846 } 844 }
@@ -999,20 +997,6 @@ where
999 } 997 }
1000} 998}
1001 999
1002fn expand_macro_to_expr(
1003 db: &impl HirDatabase,
1004 macro_call: MacroCallId,
1005 args: Option<&ast::TokenTree>,
1006) -> Option<TreeArc<ast::Expr>> {
1007 let rules = db.macro_def(macro_call.loc(db).def)?;
1008
1009 let args = mbe::ast_to_token_tree(args?)?.0;
1010
1011 let expanded = rules.expand(&args).ok()?;
1012
1013 mbe::token_tree_to_expr(&expanded).ok()
1014}
1015
1016pub(crate) fn body_with_source_map_query( 1000pub(crate) fn body_with_source_map_query(
1017 db: &impl HirDatabase, 1001 db: &impl HirDatabase,
1018 def: DefWithBody, 1002 def: DefWithBody,
diff --git a/crates/ra_hir/src/ids.rs b/crates/ra_hir/src/ids.rs
index b0e9b1f9a..4102951c9 100644
--- a/crates/ra_hir/src/ids.rs
+++ b/crates/ra_hir/src/ids.rs
@@ -63,47 +63,27 @@ 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 parse_macro(db, macro_call_id).unwrap_or_else(|err| { 66 match db.macro_expand(macro_call_id) {
67 // Note: 67 Ok(tt) => mbe::token_tree_to_ast_item_list(&tt),
68 // The final goal we would like to make all parse_macro success, 68 Err(err) => {
69 // such that the following log will not call anyway. 69 // Note:
70 log::warn!( 70 // The final goal we would like to make all parse_macro success,
71 "fail on macro_parse: (reason: {}) {}", 71 // such that the following log will not call anyway.
72 err, 72 log::warn!(
73 macro_call_id.debug_dump(db) 73 "fail on macro_parse: (reason: {}) {}",
74 ); 74 err,
75 75 macro_call_id.debug_dump(db)
76 // returning an empty string looks fishy... 76 );
77 SourceFile::parse("") 77
78 }) 78 // returning an empty string looks fishy...
79 SourceFile::parse("")
80 }
81 }
79 } 82 }
80 } 83 }
81 } 84 }
82} 85}
83 86
84fn parse_macro(
85 db: &impl DefDatabase,
86 macro_call_id: MacroCallId,
87) -> Result<TreeArc<SourceFile>, String> {
88 let loc = macro_call_id.loc(db);
89 let macro_call = loc.ast_id.to_node(db);
90 let (macro_arg, _) = macro_call
91 .token_tree()
92 .and_then(mbe::ast_to_token_tree)
93 .ok_or("Fail to args in to tt::TokenTree")?;
94
95 let macro_rules = db.macro_def(loc.def).ok_or("Fail to find macro definition")?;
96 let tt = macro_rules.expand(&macro_arg).map_err(|err| format!("{:?}", err))?;
97
98 // Set a hard limit for the expanded tt
99 let count = tt.count();
100 if count > 65536 {
101 return Err(format!("Total tokens count exceed limit : count = {}", count));
102 }
103
104 Ok(mbe::token_tree_to_ast_item_list(&tt))
105}
106
107#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] 87#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
108enum HirFileIdRepr { 88enum HirFileIdRepr {
109 File(FileId), 89 File(FileId),
@@ -139,6 +119,31 @@ pub(crate) fn macro_def_query(db: &impl DefDatabase, id: MacroDefId) -> Option<A
139 Some(Arc::new(rules)) 119 Some(Arc::new(rules))
140} 120}
141 121
122pub(crate) fn macro_arg_query(db: &impl DefDatabase, id: MacroCallId) -> Option<Arc<tt::Subtree>> {
123 let loc = id.loc(db);
124 let macro_call = loc.ast_id.to_node(db);
125 let arg = macro_call.token_tree()?;
126 let (tt, _) = mbe::ast_to_token_tree(arg)?;
127 Some(Arc::new(tt))
128}
129
130pub(crate) fn macro_expand_query(
131 db: &impl DefDatabase,
132 id: MacroCallId,
133) -> Result<Arc<tt::Subtree>, String> {
134 let loc = id.loc(db);
135 let macro_arg = db.macro_arg(id).ok_or("Fail to args in to tt::TokenTree")?;
136
137 let macro_rules = db.macro_def(loc.def).ok_or("Fail to find macro definition")?;
138 let tt = macro_rules.expand(&macro_arg).map_err(|err| format!("{:?}", err))?;
139 // Set a hard limit for the expanded tt
140 let count = tt.count();
141 if count > 65536 {
142 return Err(format!("Total tokens count exceed limit : count = {}", count));
143 }
144 Ok(Arc::new(tt))
145}
146
142macro_rules! impl_intern_key { 147macro_rules! impl_intern_key {
143 ($name:ident) => { 148 ($name:ident) => {
144 impl salsa::InternKey for $name { 149 impl salsa::InternKey for $name {
diff --git a/crates/ra_hir/src/source_id.rs b/crates/ra_hir/src/source_id.rs
index a2bc9a799..0a8fb6d32 100644
--- a/crates/ra_hir/src/source_id.rs
+++ b/crates/ra_hir/src/source_id.rs
@@ -92,7 +92,7 @@ impl AstIdMap {
92 Arc::new(AstIdMap::from_source_file(&source_file)) 92 Arc::new(AstIdMap::from_source_file(&source_file))
93 } 93 }
94 94
95 pub(crate) fn ast_id_to_node_query( 95 pub(crate) fn file_item_query(
96 db: &impl DefDatabase, 96 db: &impl DefDatabase,
97 file_id: HirFileId, 97 file_id: HirFileId,
98 ast_id: ErasedFileAstId, 98 ast_id: ErasedFileAstId,
diff --git a/crates/ra_syntax/src/grammar.ron b/crates/ra_syntax/src/grammar.ron
index cae5d8774..011ef0616 100644
--- a/crates/ra_syntax/src/grammar.ron
+++ b/crates/ra_syntax/src/grammar.ron
@@ -674,7 +674,7 @@ Grammar(
674 "LifetimeArg": (), 674 "LifetimeArg": (),
675 675
676 "MacroItems": ( 676 "MacroItems": (
677 traits: [ "ModuleItemOwner", "FnDefOwner" ], 677 traits: [ "ModuleItemOwner", "FnDefOwner" ],
678 ), 678 ),
679 679
680 "MacroStmts" : ( 680 "MacroStmts" : (