aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_hir_expand/src
diff options
context:
space:
mode:
authorbors[bot] <26634292+bors[bot]@users.noreply.github.com>2020-03-17 09:41:30 +0000
committerGitHub <[email protected]>2020-03-17 09:41:30 +0000
commit6aa432d86b7e4fb691600032ebdf6f2301152447 (patch)
treeaa012212c11e43a95547f553b5116922631f9896 /crates/ra_hir_expand/src
parentcf4ae9aa591729dde25a7df3fa5c22ca0fd94145 (diff)
parent6c20d7e979b967eb20207414c0a0bf875bbcb98d (diff)
Merge #3580
3580: More error-resilient MBE expansion r=matklad a=flodiebold This is the beginning of an attempt to make macro-by-example expansion more resilient, so that we still get an expansion even if no rule exactly matches, with the goal to make completion work better in macro calls. The general idea is to make everything return `(T, Option<ExpandError>)` instead of `Result<T, ExpandError>`; and then to try each macro arm in turn, and somehow choose the 'best' matching rule if none matches without errors. Finding that 'best' match isn't done yet; I'm currently counting how many tokens were consumed from the args before an error, but it also needs to take into account whether there were further patterns that had nothing to match. I'll continue this later, but I'm interested whether you think this is the right path, @matklad & @edwin0cheng. Co-authored-by: Florian Diebold <[email protected]> Co-authored-by: Florian Diebold <[email protected]>
Diffstat (limited to 'crates/ra_hir_expand/src')
-rw-r--r--crates/ra_hir_expand/src/db.rs99
1 files changed, 53 insertions, 46 deletions
diff --git a/crates/ra_hir_expand/src/db.rs b/crates/ra_hir_expand/src/db.rs
index c3e1c68b7..d171d2dfd 100644
--- a/crates/ra_hir_expand/src/db.rs
+++ b/crates/ra_hir_expand/src/db.rs
@@ -2,7 +2,7 @@
2 2
3use std::sync::Arc; 3use std::sync::Arc;
4 4
5use mbe::MacroRules; 5use 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;
@@ -27,11 +27,12 @@ impl TokenExpander {
27 db: &dyn AstDatabase, 27 db: &dyn AstDatabase,
28 id: LazyMacroId, 28 id: LazyMacroId,
29 tt: &tt::Subtree, 29 tt: &tt::Subtree,
30 ) -> Result<tt::Subtree, mbe::ExpandError> { 30 ) -> mbe::ExpandResult<tt::Subtree> {
31 match self { 31 match self {
32 TokenExpander::MacroRules(it) => it.expand(tt), 32 TokenExpander::MacroRules(it) => it.expand(tt),
33 TokenExpander::Builtin(it) => it.expand(db, id, tt), 33 // FIXME switch these to ExpandResult as well
34 TokenExpander::BuiltinDerive(it) => it.expand(db, id, tt), 34 TokenExpander::Builtin(it) => it.expand(db, id, tt).into(),
35 TokenExpander::BuiltinDerive(it) => it.expand(db, id, tt).into(),
35 } 36 }
36 } 37 }
37 38
@@ -66,7 +67,7 @@ pub trait AstDatabase: SourceDatabase {
66 fn macro_def(&self, id: MacroDefId) -> Option<Arc<(TokenExpander, mbe::TokenMap)>>; 67 fn macro_def(&self, id: MacroDefId) -> Option<Arc<(TokenExpander, mbe::TokenMap)>>;
67 fn parse_macro(&self, macro_file: MacroFile) 68 fn parse_macro(&self, macro_file: MacroFile)
68 -> Option<(Parse<SyntaxNode>, Arc<mbe::TokenMap>)>; 69 -> Option<(Parse<SyntaxNode>, Arc<mbe::TokenMap>)>;
69 fn macro_expand(&self, macro_call: MacroCallId) -> Result<Arc<tt::Subtree>, String>; 70 fn macro_expand(&self, macro_call: MacroCallId) -> (Option<Arc<tt::Subtree>>, Option<String>);
70 71
71 #[salsa::interned] 72 #[salsa::interned]
72 fn intern_eager_expansion(&self, eager: EagerCallLoc) -> EagerMacroId; 73 fn intern_eager_expansion(&self, eager: EagerCallLoc) -> EagerMacroId;
@@ -153,7 +154,7 @@ pub(crate) fn macro_arg(
153pub(crate) fn macro_expand( 154pub(crate) fn macro_expand(
154 db: &dyn AstDatabase, 155 db: &dyn AstDatabase,
155 id: MacroCallId, 156 id: MacroCallId,
156) -> Result<Arc<tt::Subtree>, String> { 157) -> (Option<Arc<tt::Subtree>>, Option<String>) {
157 macro_expand_with_arg(db, id, None) 158 macro_expand_with_arg(db, id, None)
158} 159}
159 160
@@ -174,31 +175,38 @@ fn macro_expand_with_arg(
174 db: &dyn AstDatabase, 175 db: &dyn AstDatabase,
175 id: MacroCallId, 176 id: MacroCallId,
176 arg: Option<Arc<(tt::Subtree, mbe::TokenMap)>>, 177 arg: Option<Arc<(tt::Subtree, mbe::TokenMap)>>,
177) -> Result<Arc<tt::Subtree>, String> { 178) -> (Option<Arc<tt::Subtree>>, Option<String>) {
178 let lazy_id = match id { 179 let lazy_id = match id {
179 MacroCallId::LazyMacro(id) => id, 180 MacroCallId::LazyMacro(id) => id,
180 MacroCallId::EagerMacro(id) => { 181 MacroCallId::EagerMacro(id) => {
181 if arg.is_some() { 182 if arg.is_some() {
182 return Err( 183 return (
183 "hypothetical macro expansion not implemented for eager macro".to_owned() 184 None,
185 Some("hypothetical macro expansion not implemented for eager macro".to_owned()),
184 ); 186 );
185 } else { 187 } else {
186 return Ok(db.lookup_intern_eager_expansion(id).subtree); 188 return (Some(db.lookup_intern_eager_expansion(id).subtree), None);
187 } 189 }
188 } 190 }
189 }; 191 };
190 192
191 let loc = db.lookup_intern_macro(lazy_id); 193 let loc = db.lookup_intern_macro(lazy_id);
192 let macro_arg = arg.or_else(|| db.macro_arg(id)).ok_or("Fail to args in to tt::TokenTree")?; 194 let macro_arg = match arg.or_else(|| db.macro_arg(id)) {
195 Some(it) => it,
196 None => return (None, Some("Fail to args in to tt::TokenTree".into())),
197 };
193 198
194 let macro_rules = db.macro_def(loc.def).ok_or("Fail to find macro definition")?; 199 let macro_rules = match db.macro_def(loc.def) {
195 let tt = macro_rules.0.expand(db, lazy_id, &macro_arg.0).map_err(|err| format!("{:?}", err))?; 200 Some(it) => it,
201 None => return (None, Some("Fail to find macro definition".into())),
202 };
203 let ExpandResult(tt, err) = macro_rules.0.expand(db, lazy_id, &macro_arg.0);
196 // Set a hard limit for the expanded tt 204 // Set a hard limit for the expanded tt
197 let count = tt.count(); 205 let count = tt.count();
198 if count > 65536 { 206 if count > 65536 {
199 return Err(format!("Total tokens count exceed limit : count = {}", count)); 207 return (None, Some(format!("Total tokens count exceed limit : count = {}", count)));
200 } 208 }
201 Ok(Arc::new(tt)) 209 (Some(Arc::new(tt)), err.map(|e| format!("{:?}", e)))
202} 210}
203 211
204pub(crate) fn parse_or_expand(db: &dyn AstDatabase, file_id: HirFileId) -> Option<SyntaxNode> { 212pub(crate) fn parse_or_expand(db: &dyn AstDatabase, file_id: HirFileId) -> Option<SyntaxNode> {
@@ -225,42 +233,41 @@ pub fn parse_macro_with_arg(
225 let _p = profile("parse_macro_query"); 233 let _p = profile("parse_macro_query");
226 234
227 let macro_call_id = macro_file.macro_call_id; 235 let macro_call_id = macro_file.macro_call_id;
228 let expansion = if let Some(arg) = arg { 236 let (tt, err) = if let Some(arg) = arg {
229 macro_expand_with_arg(db, macro_call_id, Some(arg)) 237 macro_expand_with_arg(db, macro_call_id, Some(arg))
230 } else { 238 } else {
231 db.macro_expand(macro_call_id) 239 db.macro_expand(macro_call_id)
232 }; 240 };
233 let tt = expansion 241 if let Some(err) = err {
234 .map_err(|err| { 242 // Note:
235 // Note: 243 // 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, 244 // such that the following log will not call anyway.
237 // such that the following log will not call anyway. 245 match macro_call_id {
238 match macro_call_id { 246 MacroCallId::LazyMacro(id) => {
239 MacroCallId::LazyMacro(id) => { 247 let loc: MacroCallLoc = db.lookup_intern_macro(id);
240 let loc: MacroCallLoc = db.lookup_intern_macro(id); 248 let node = loc.kind.node(db);
241 let node = loc.kind.node(db); 249
242 250 // collect parent information for warning log
243 // collect parent information for warning log 251 let parents = std::iter::successors(loc.kind.file_id().call_node(db), |it| {
244 let parents = std::iter::successors(loc.kind.file_id().call_node(db), |it| { 252 it.file_id.call_node(db)
245 it.file_id.call_node(db) 253 })
246 }) 254 .map(|n| format!("{:#}", n.value))
247 .map(|n| format!("{:#}", n.value)) 255 .collect::<Vec<_>>()
248 .collect::<Vec<_>>() 256 .join("\n");
249 .join("\n"); 257
250 258 log::warn!(
251 log::warn!( 259 "fail on macro_parse: (reason: {} macro_call: {:#}) parents: {}",
252 "fail on macro_parse: (reason: {} macro_call: {:#}) parents: {}", 260 err,
253 err, 261 node.value,
254 node.value, 262 parents
255 parents 263 );
256 ); 264 }
257 } 265 _ => {
258 _ => { 266 log::warn!("fail on macro_parse: (reason: {})", err);
259 log::warn!("fail on macro_parse: (reason: {})", err);
260 }
261 } 267 }
262 }) 268 }
263 .ok()?; 269 };
270 let tt = tt?;
264 271
265 let fragment_kind = to_fragment_kind(db, macro_call_id); 272 let fragment_kind = to_fragment_kind(db, macro_call_id);
266 273