aboutsummaryrefslogtreecommitdiff
path: root/crates/hir_expand/src/db.rs
diff options
context:
space:
mode:
authorJonas Schievink <[email protected]>2020-11-24 18:00:23 +0000
committerJonas Schievink <[email protected]>2020-11-24 18:00:23 +0000
commitd5e9bf80f9e68cabf694226e2bad896c1ee00742 (patch)
tree12fc2464ad36452481fd29b9a174840be3765dd2 /crates/hir_expand/src/db.rs
parentd2f398cd76659425c9d48185a1be92ee9a9332c2 (diff)
hir_expand: propagate expansion errors
Diffstat (limited to 'crates/hir_expand/src/db.rs')
-rw-r--r--crates/hir_expand/src/db.rs123
1 files changed, 83 insertions, 40 deletions
diff --git a/crates/hir_expand/src/db.rs b/crates/hir_expand/src/db.rs
index ade57ac1b..fc512517c 100644
--- a/crates/hir_expand/src/db.rs
+++ b/crates/hir_expand/src/db.rs
@@ -13,6 +13,19 @@ use crate::{
13 MacroFile, ProcMacroExpander, 13 MacroFile, ProcMacroExpander,
14}; 14};
15 15
16/// A result of some macro expansion.
17#[derive(Debug, Clone, Eq, PartialEq)]
18pub struct MacroResult<T> {
19 /// The result of the expansion. Might be `None` when error recovery was impossible and no
20 /// usable result was produced.
21 pub value: Option<T>,
22
23 /// The error that occurred during expansion or processing.
24 ///
25 /// Since we do error recovery, getting an error here does not mean that `value` will be absent.
26 pub error: Option<String>,
27}
28
16#[derive(Debug, Clone, Eq, PartialEq)] 29#[derive(Debug, Clone, Eq, PartialEq)]
17pub enum TokenExpander { 30pub enum TokenExpander {
18 MacroRules(mbe::MacroRules), 31 MacroRules(mbe::MacroRules),
@@ -75,9 +88,11 @@ pub trait AstDatabase: SourceDatabase {
75 #[salsa::transparent] 88 #[salsa::transparent]
76 fn macro_arg(&self, id: MacroCallId) -> Option<Arc<(tt::Subtree, mbe::TokenMap)>>; 89 fn macro_arg(&self, id: MacroCallId) -> Option<Arc<(tt::Subtree, mbe::TokenMap)>>;
77 fn macro_def(&self, id: MacroDefId) -> Option<Arc<(TokenExpander, mbe::TokenMap)>>; 90 fn macro_def(&self, id: MacroDefId) -> Option<Arc<(TokenExpander, mbe::TokenMap)>>;
78 fn parse_macro(&self, macro_file: MacroFile) 91 fn parse_macro(
79 -> Option<(Parse<SyntaxNode>, Arc<mbe::TokenMap>)>; 92 &self,
80 fn macro_expand(&self, macro_call: MacroCallId) -> (Option<Arc<tt::Subtree>>, Option<String>); 93 macro_file: MacroFile,
94 ) -> MacroResult<(Parse<SyntaxNode>, Arc<mbe::TokenMap>)>;
95 fn macro_expand(&self, macro_call: MacroCallId) -> MacroResult<Arc<tt::Subtree>>;
81 96
82 #[salsa::interned] 97 #[salsa::interned]
83 fn intern_eager_expansion(&self, eager: EagerCallLoc) -> EagerMacroId; 98 fn intern_eager_expansion(&self, eager: EagerCallLoc) -> EagerMacroId;
@@ -85,6 +100,20 @@ pub trait AstDatabase: SourceDatabase {
85 fn expand_proc_macro(&self, call: MacroCallId) -> Result<tt::Subtree, mbe::ExpandError>; 100 fn expand_proc_macro(&self, call: MacroCallId) -> Result<tt::Subtree, mbe::ExpandError>;
86} 101}
87 102
103impl<T> MacroResult<T> {
104 fn error(message: String) -> Self {
105 Self { value: None, error: Some(message) }
106 }
107
108 fn map<U>(self, f: impl FnOnce(T) -> U) -> MacroResult<U> {
109 MacroResult { value: self.value.map(f), error: self.error }
110 }
111
112 fn drop_value<U>(self) -> MacroResult<U> {
113 MacroResult { value: None, error: self.error }
114 }
115}
116
88/// This expands the given macro call, but with different arguments. This is 117/// This expands the given macro call, but with different arguments. This is
89/// used for completion, where we want to see what 'would happen' if we insert a 118/// used for completion, where we want to see what 'would happen' if we insert a
90/// token. The `token_to_map` mapped down into the expansion, with the mapped 119/// token. The `token_to_map` mapped down into the expansion, with the mapped
@@ -102,7 +131,7 @@ pub fn expand_hypothetical(
102 let token_id = tmap_1.token_by_range(range)?; 131 let token_id = tmap_1.token_by_range(range)?;
103 let macro_def = expander(db, actual_macro_call)?; 132 let macro_def = expander(db, actual_macro_call)?;
104 let (node, tmap_2) = 133 let (node, tmap_2) =
105 parse_macro_with_arg(db, macro_file, Some(std::sync::Arc::new((tt, tmap_1))))?; 134 parse_macro_with_arg(db, macro_file, Some(std::sync::Arc::new((tt, tmap_1)))).value?;
106 let token_id = macro_def.0.map_id_down(token_id); 135 let token_id = macro_def.0.map_id_down(token_id);
107 let range = tmap_2.range_by_token(token_id)?.by_kind(token_to_map.kind())?; 136 let range = tmap_2.range_by_token(token_id)?.by_kind(token_to_map.kind())?;
108 let token = syntax::algo::find_covering_element(&node.syntax_node(), range).into_token()?; 137 let token = syntax::algo::find_covering_element(&node.syntax_node(), range).into_token()?;
@@ -171,10 +200,7 @@ pub(crate) fn macro_arg(
171 Some(Arc::new((tt, tmap))) 200 Some(Arc::new((tt, tmap)))
172} 201}
173 202
174pub(crate) fn macro_expand( 203pub(crate) fn macro_expand(db: &dyn AstDatabase, id: MacroCallId) -> MacroResult<Arc<tt::Subtree>> {
175 db: &dyn AstDatabase,
176 id: MacroCallId,
177) -> (Option<Arc<tt::Subtree>>, Option<String>) {
178 macro_expand_with_arg(db, id, None) 204 macro_expand_with_arg(db, id, None)
179} 205}
180 206
@@ -195,17 +221,19 @@ fn macro_expand_with_arg(
195 db: &dyn AstDatabase, 221 db: &dyn AstDatabase,
196 id: MacroCallId, 222 id: MacroCallId,
197 arg: Option<Arc<(tt::Subtree, mbe::TokenMap)>>, 223 arg: Option<Arc<(tt::Subtree, mbe::TokenMap)>>,
198) -> (Option<Arc<tt::Subtree>>, Option<String>) { 224) -> MacroResult<Arc<tt::Subtree>> {
199 let lazy_id = match id { 225 let lazy_id = match id {
200 MacroCallId::LazyMacro(id) => id, 226 MacroCallId::LazyMacro(id) => id,
201 MacroCallId::EagerMacro(id) => { 227 MacroCallId::EagerMacro(id) => {
202 if arg.is_some() { 228 if arg.is_some() {
203 return ( 229 return MacroResult::error(
204 None, 230 "hypothetical macro expansion not implemented for eager macro".to_owned(),
205 Some("hypothetical macro expansion not implemented for eager macro".to_owned()),
206 ); 231 );
207 } else { 232 } else {
208 return (Some(db.lookup_intern_eager_expansion(id).subtree), None); 233 return MacroResult {
234 value: Some(db.lookup_intern_eager_expansion(id).subtree),
235 error: None,
236 };
209 } 237 }
210 } 238 }
211 }; 239 };
@@ -213,20 +241,21 @@ fn macro_expand_with_arg(
213 let loc = db.lookup_intern_macro(lazy_id); 241 let loc = db.lookup_intern_macro(lazy_id);
214 let macro_arg = match arg.or_else(|| db.macro_arg(id)) { 242 let macro_arg = match arg.or_else(|| db.macro_arg(id)) {
215 Some(it) => it, 243 Some(it) => it,
216 None => return (None, Some("Fail to args in to tt::TokenTree".into())), 244 None => return MacroResult::error("Fail to args in to tt::TokenTree".into()),
217 }; 245 };
218 246
219 let macro_rules = match db.macro_def(loc.def) { 247 let macro_rules = match db.macro_def(loc.def) {
220 Some(it) => it, 248 Some(it) => it,
221 None => return (None, Some("Fail to find macro definition".into())), 249 None => return MacroResult::error("Fail to find macro definition".into()),
222 }; 250 };
223 let ExpandResult(tt, err) = macro_rules.0.expand(db, lazy_id, &macro_arg.0); 251 let ExpandResult(tt, err) = macro_rules.0.expand(db, lazy_id, &macro_arg.0);
224 // Set a hard limit for the expanded tt 252 // Set a hard limit for the expanded tt
225 let count = tt.count(); 253 let count = tt.count();
226 if count > 262144 { 254 if count > 262144 {
227 return (None, Some(format!("Total tokens count exceed limit : count = {}", count))); 255 return MacroResult::error(format!("Total tokens count exceed limit : count = {}", count));
228 } 256 }
229 (Some(Arc::new(tt)), err.map(|e| format!("{:?}", e))) 257
258 MacroResult { value: Some(Arc::new(tt)), error: err.map(|e| format!("{:?}", e)) }
230} 259}
231 260
232pub(crate) fn expand_proc_macro( 261pub(crate) fn expand_proc_macro(
@@ -260,7 +289,7 @@ pub(crate) fn parse_or_expand(db: &dyn AstDatabase, file_id: HirFileId) -> Optio
260 match file_id.0 { 289 match file_id.0 {
261 HirFileIdRepr::FileId(file_id) => Some(db.parse(file_id).tree().syntax().clone()), 290 HirFileIdRepr::FileId(file_id) => Some(db.parse(file_id).tree().syntax().clone()),
262 HirFileIdRepr::MacroFile(macro_file) => { 291 HirFileIdRepr::MacroFile(macro_file) => {
263 db.parse_macro(macro_file).map(|(it, _)| it.syntax_node()) 292 db.parse_macro(macro_file).map(|(it, _)| it.syntax_node()).value
264 } 293 }
265 } 294 }
266} 295}
@@ -268,7 +297,7 @@ pub(crate) fn parse_or_expand(db: &dyn AstDatabase, file_id: HirFileId) -> Optio
268pub(crate) fn parse_macro( 297pub(crate) fn parse_macro(
269 db: &dyn AstDatabase, 298 db: &dyn AstDatabase,
270 macro_file: MacroFile, 299 macro_file: MacroFile,
271) -> Option<(Parse<SyntaxNode>, Arc<mbe::TokenMap>)> { 300) -> MacroResult<(Parse<SyntaxNode>, Arc<mbe::TokenMap>)> {
272 parse_macro_with_arg(db, macro_file, None) 301 parse_macro_with_arg(db, macro_file, None)
273} 302}
274 303
@@ -276,16 +305,16 @@ pub fn parse_macro_with_arg(
276 db: &dyn AstDatabase, 305 db: &dyn AstDatabase,
277 macro_file: MacroFile, 306 macro_file: MacroFile,
278 arg: Option<Arc<(tt::Subtree, mbe::TokenMap)>>, 307 arg: Option<Arc<(tt::Subtree, mbe::TokenMap)>>,
279) -> Option<(Parse<SyntaxNode>, Arc<mbe::TokenMap>)> { 308) -> MacroResult<(Parse<SyntaxNode>, Arc<mbe::TokenMap>)> {
280 let _p = profile::span("parse_macro_query"); 309 let _p = profile::span("parse_macro_query");
281 310
282 let macro_call_id = macro_file.macro_call_id; 311 let macro_call_id = macro_file.macro_call_id;
283 let (tt, err) = if let Some(arg) = arg { 312 let result = if let Some(arg) = arg {
284 macro_expand_with_arg(db, macro_call_id, Some(arg)) 313 macro_expand_with_arg(db, macro_call_id, Some(arg))
285 } else { 314 } else {
286 db.macro_expand(macro_call_id) 315 db.macro_expand(macro_call_id)
287 }; 316 };
288 if let Some(err) = &err { 317 if let Some(err) = &result.error {
289 // Note: 318 // Note:
290 // The final goal we would like to make all parse_macro success, 319 // The final goal we would like to make all parse_macro success,
291 // such that the following log will not call anyway. 320 // such that the following log will not call anyway.
@@ -313,30 +342,44 @@ pub fn parse_macro_with_arg(
313 log::warn!("fail on macro_parse: (reason: {})", err); 342 log::warn!("fail on macro_parse: (reason: {})", err);
314 } 343 }
315 } 344 }
345 }
346 let tt = match result.value {
347 Some(tt) => tt,
348 None => return result.drop_value(),
316 }; 349 };
317 let tt = tt?;
318 350
319 let fragment_kind = to_fragment_kind(db, macro_call_id); 351 let fragment_kind = to_fragment_kind(db, macro_call_id);
320 352
321 let (parse, rev_token_map) = mbe::token_tree_to_syntax_node(&tt, fragment_kind).ok()?; 353 let (parse, rev_token_map) = match mbe::token_tree_to_syntax_node(&tt, fragment_kind) {
354 Ok(it) => it,
355 Err(err) => {
356 return MacroResult::error(format!("{:?}", err));
357 }
358 };
322 359
323 if err.is_none() { 360 match result.error {
324 Some((parse, Arc::new(rev_token_map))) 361 Some(error) => {
325 } else { 362 // FIXME:
326 // FIXME: 363 // In future, we should propagate the actual error with recovery information
327 // In future, we should propagate the actual error with recovery information 364 // instead of ignore the error here.
328 // instead of ignore the error here. 365
329 366 // Safe check for recurisve identity macro
330 // Safe check for recurisve identity macro 367 let node = parse.syntax_node();
331 let node = parse.syntax_node(); 368 let file: HirFileId = macro_file.into();
332 let file: HirFileId = macro_file.into(); 369 let call_node = match file.call_node(db) {
333 let call_node = file.call_node(db)?; 370 Some(it) => it,
334 371 None => {
335 if !diff(&node, &call_node.value).is_empty() { 372 return MacroResult::error(error);
336 Some((parse, Arc::new(rev_token_map))) 373 }
337 } else { 374 };
338 None 375
376 if !diff(&node, &call_node.value).is_empty() {
377 MacroResult { value: Some((parse, Arc::new(rev_token_map))), error: None }
378 } else {
379 return MacroResult::error(error);
380 }
339 } 381 }
382 None => MacroResult { value: Some((parse, Arc::new(rev_token_map))), error: None },
340 } 383 }
341} 384}
342 385