aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_hir_expand/src/db.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_hir_expand/src/db.rs')
-rw-r--r--crates/ra_hir_expand/src/db.rs71
1 files changed, 67 insertions, 4 deletions
diff --git a/crates/ra_hir_expand/src/db.rs b/crates/ra_hir_expand/src/db.rs
index f3a84cacc..29dde3d80 100644
--- a/crates/ra_hir_expand/src/db.rs
+++ b/crates/ra_hir_expand/src/db.rs
@@ -72,6 +72,30 @@ pub trait AstDatabase: SourceDatabase {
72 fn intern_eager_expansion(&self, eager: EagerCallLoc) -> EagerMacroId; 72 fn intern_eager_expansion(&self, eager: EagerCallLoc) -> EagerMacroId;
73} 73}
74 74
75/// This expands the given macro call, but with different arguments. This is
76/// used for completion, where we want to see what 'would happen' if we insert a
77/// token. The `token_to_map` mapped down into the expansion, with the mapped
78/// token returned.
79pub fn expand_hypothetical(
80 db: &impl AstDatabase,
81 actual_macro_call: MacroCallId,
82 hypothetical_args: &ra_syntax::ast::TokenTree,
83 token_to_map: ra_syntax::SyntaxToken,
84) -> Option<(SyntaxNode, ra_syntax::SyntaxToken)> {
85 let macro_file = MacroFile { macro_call_id: actual_macro_call };
86 let (tt, tmap_1) = mbe::syntax_node_to_token_tree(hypothetical_args.syntax()).unwrap();
87 let range =
88 token_to_map.text_range().checked_sub(hypothetical_args.syntax().text_range().start())?;
89 let token_id = tmap_1.token_by_range(range)?;
90 let macro_def = expander(db, actual_macro_call)?;
91 let (node, tmap_2) =
92 parse_macro_with_arg(db, macro_file, Some(std::sync::Arc::new((tt, tmap_1))))?;
93 let token_id = macro_def.0.map_id_down(token_id);
94 let range = tmap_2.range_by_token(token_id)?.by_kind(token_to_map.kind())?;
95 let token = ra_syntax::algo::find_covering_element(&node.syntax_node(), range).into_token()?;
96 Some((node.syntax_node(), token))
97}
98
75pub(crate) fn ast_id_map(db: &dyn AstDatabase, file_id: HirFileId) -> Arc<AstIdMap> { 99pub(crate) fn ast_id_map(db: &dyn AstDatabase, file_id: HirFileId) -> Arc<AstIdMap> {
76 let map = 100 let map =
77 db.parse_or_expand(file_id).map_or_else(AstIdMap::default, |it| AstIdMap::from_source(&it)); 101 db.parse_or_expand(file_id).map_or_else(AstIdMap::default, |it| AstIdMap::from_source(&it));
@@ -130,15 +154,42 @@ pub(crate) fn macro_expand(
130 db: &dyn AstDatabase, 154 db: &dyn AstDatabase,
131 id: MacroCallId, 155 id: MacroCallId,
132) -> Result<Arc<tt::Subtree>, String> { 156) -> Result<Arc<tt::Subtree>, String> {
157 macro_expand_with_arg(db, id, None)
158}
159
160fn expander(db: &dyn AstDatabase, id: MacroCallId) -> Option<Arc<(TokenExpander, mbe::TokenMap)>> {
161 let lazy_id = match id {
162 MacroCallId::LazyMacro(id) => id,
163 MacroCallId::EagerMacro(_id) => {
164 return None;
165 }
166 };
167
168 let loc = db.lookup_intern_macro(lazy_id);
169 let macro_rules = db.macro_def(loc.def)?;
170 Some(macro_rules)
171}
172
173fn macro_expand_with_arg(
174 db: &dyn AstDatabase,
175 id: MacroCallId,
176 arg: Option<Arc<(tt::Subtree, mbe::TokenMap)>>,
177) -> Result<Arc<tt::Subtree>, String> {
133 let lazy_id = match id { 178 let lazy_id = match id {
134 MacroCallId::LazyMacro(id) => id, 179 MacroCallId::LazyMacro(id) => id,
135 MacroCallId::EagerMacro(id) => { 180 MacroCallId::EagerMacro(id) => {
136 return Ok(db.lookup_intern_eager_expansion(id).subtree); 181 if arg.is_some() {
182 return Err(
183 "hypothetical macro expansion not implemented for eager macro".to_owned()
184 );
185 } else {
186 return Ok(db.lookup_intern_eager_expansion(id).subtree);
187 }
137 } 188 }
138 }; 189 };
139 190
140 let loc = db.lookup_intern_macro(lazy_id); 191 let loc = db.lookup_intern_macro(lazy_id);
141 let macro_arg = db.macro_arg(id).ok_or("Fail to args in to tt::TokenTree")?; 192 let macro_arg = arg.or_else(|| db.macro_arg(id)).ok_or("Fail to args in to tt::TokenTree")?;
142 193
143 let macro_rules = db.macro_def(loc.def).ok_or("Fail to find macro definition")?; 194 let macro_rules = db.macro_def(loc.def).ok_or("Fail to find macro definition")?;
144 let tt = macro_rules.0.expand(db, lazy_id, &macro_arg.0).map_err(|err| format!("{:?}", err))?; 195 let tt = macro_rules.0.expand(db, lazy_id, &macro_arg.0).map_err(|err| format!("{:?}", err))?;
@@ -163,11 +214,23 @@ pub(crate) fn parse_macro(
163 db: &dyn AstDatabase, 214 db: &dyn AstDatabase,
164 macro_file: MacroFile, 215 macro_file: MacroFile,
165) -> Option<(Parse<SyntaxNode>, Arc<mbe::TokenMap>)> { 216) -> Option<(Parse<SyntaxNode>, Arc<mbe::TokenMap>)> {
217 parse_macro_with_arg(db, macro_file, None)
218}
219
220pub fn parse_macro_with_arg(
221 db: &dyn AstDatabase,
222 macro_file: MacroFile,
223 arg: Option<Arc<(tt::Subtree, mbe::TokenMap)>>,
224) -> Option<(Parse<SyntaxNode>, Arc<mbe::TokenMap>)> {
166 let _p = profile("parse_macro_query"); 225 let _p = profile("parse_macro_query");
167 226
168 let macro_call_id = macro_file.macro_call_id; 227 let macro_call_id = macro_file.macro_call_id;
169 let tt = db 228 let expansion = if let Some(arg) = arg {
170 .macro_expand(macro_call_id) 229 macro_expand_with_arg(db, macro_call_id, Some(arg))
230 } else {
231 db.macro_expand(macro_call_id)
232 };
233 let tt = expansion
171 .map_err(|err| { 234 .map_err(|err| {
172 // Note: 235 // Note:
173 // The final goal we would like to make all parse_macro success, 236 // The final goal we would like to make all parse_macro success,