diff options
-rw-r--r-- | crates/ra_hir_expand/src/db.rs | 90 | ||||
-rw-r--r-- | crates/ra_hir_ty/src/tests/macros.rs | 8 | ||||
-rw-r--r-- | crates/ra_ide/src/completion/complete_dot.rs | 37 | ||||
-rw-r--r-- | crates/ra_ide/src/expand_macro.rs | 2 | ||||
-rw-r--r-- | crates/ra_mbe/src/lib.rs | 4 | ||||
-rw-r--r-- | crates/ra_mbe/src/mbe_expander.rs | 29 | ||||
-rw-r--r-- | crates/ra_mbe/src/mbe_expander/matcher.rs | 11 | ||||
-rw-r--r-- | crates/ra_mbe/src/mbe_expander/transcriber.rs | 69 | ||||
-rw-r--r-- | crates/ra_mbe/src/tests.rs | 3 | ||||
-rw-r--r-- | crates/ra_tt/src/lib.rs | 6 |
10 files changed, 168 insertions, 91 deletions
diff --git a/crates/ra_hir_expand/src/db.rs b/crates/ra_hir_expand/src/db.rs index c3e1c68b7..e7b81a1e6 100644 --- a/crates/ra_hir_expand/src/db.rs +++ b/crates/ra_hir_expand/src/db.rs | |||
@@ -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).map_or_else(|e| (tt::Subtree::default(), Some(e)), |r| (r, None)), |
35 | TokenExpander::BuiltinDerive(it) => it.expand(db, id, tt).map_or_else(|e| (tt::Subtree::default(), Some(e)), |r| (r, None)), | ||
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( | |||
153 | pub(crate) fn macro_expand( | 154 | pub(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,39 @@ 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, ¯o_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 (tt, err) = macro_rules.0.expand(db, lazy_id, ¯o_arg.0); | ||
196 | // Set a hard limit for the expanded tt | 204 | // Set a hard limit for the expanded tt |
205 | eprintln!("expansion size: {}", tt.count()); | ||
197 | let count = tt.count(); | 206 | let count = tt.count(); |
198 | if count > 65536 { | 207 | if count > 65536 { |
199 | return Err(format!("Total tokens count exceed limit : count = {}", count)); | 208 | return (None, Some(format!("Total tokens count exceed limit : count = {}", count))); |
200 | } | 209 | } |
201 | Ok(Arc::new(tt)) | 210 | (Some(Arc::new(tt)), err.map(|e| format!("{:?}", e))) |
202 | } | 211 | } |
203 | 212 | ||
204 | pub(crate) fn parse_or_expand(db: &dyn AstDatabase, file_id: HirFileId) -> Option<SyntaxNode> { | 213 | pub(crate) fn parse_or_expand(db: &dyn AstDatabase, file_id: HirFileId) -> Option<SyntaxNode> { |
@@ -225,42 +234,41 @@ pub fn parse_macro_with_arg( | |||
225 | let _p = profile("parse_macro_query"); | 234 | let _p = profile("parse_macro_query"); |
226 | 235 | ||
227 | let macro_call_id = macro_file.macro_call_id; | 236 | let macro_call_id = macro_file.macro_call_id; |
228 | let expansion = if let Some(arg) = arg { | 237 | let (tt, err) = if let Some(arg) = arg { |
229 | macro_expand_with_arg(db, macro_call_id, Some(arg)) | 238 | macro_expand_with_arg(db, macro_call_id, Some(arg)) |
230 | } else { | 239 | } else { |
231 | db.macro_expand(macro_call_id) | 240 | db.macro_expand(macro_call_id) |
232 | }; | 241 | }; |
233 | let tt = expansion | 242 | if let Some(err) = err { |
234 | .map_err(|err| { | 243 | // Note: |
235 | // Note: | 244 | // 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, | 245 | // such that the following log will not call anyway. |
237 | // such that the following log will not call anyway. | 246 | match macro_call_id { |
238 | match macro_call_id { | 247 | MacroCallId::LazyMacro(id) => { |
239 | MacroCallId::LazyMacro(id) => { | 248 | let loc: MacroCallLoc = db.lookup_intern_macro(id); |
240 | let loc: MacroCallLoc = db.lookup_intern_macro(id); | 249 | let node = loc.kind.node(db); |
241 | let node = loc.kind.node(db); | 250 | |
242 | 251 | // collect parent information for warning log | |
243 | // collect parent information for warning log | 252 | 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| { | 253 | it.file_id.call_node(db) |
245 | it.file_id.call_node(db) | 254 | }) |
246 | }) | ||
247 | .map(|n| format!("{:#}", n.value)) | 255 | .map(|n| format!("{:#}", n.value)) |
248 | .collect::<Vec<_>>() | 256 | .collect::<Vec<_>>() |
249 | .join("\n"); | 257 | .join("\n"); |
250 | 258 | ||
251 | log::warn!( | 259 | log::warn!( |
252 | "fail on macro_parse: (reason: {} macro_call: {:#}) parents: {}", | 260 | "fail on macro_parse: (reason: {} macro_call: {:#}) parents: {}", |
253 | err, | 261 | err, |
254 | node.value, | 262 | node.value, |
255 | parents | 263 | parents |
256 | ); | 264 | ); |
257 | } | 265 | } |
258 | _ => { | 266 | _ => { |
259 | log::warn!("fail on macro_parse: (reason: {})", err); | 267 | log::warn!("fail on macro_parse: (reason: {})", err); |
260 | } | ||
261 | } | 268 | } |
262 | }) | 269 | } |
263 | .ok()?; | 270 | }; |
271 | let tt = tt?; | ||
264 | 272 | ||
265 | let fragment_kind = to_fragment_kind(db, macro_call_id); | 273 | let fragment_kind = to_fragment_kind(db, macro_call_id); |
266 | 274 | ||
diff --git a/crates/ra_hir_ty/src/tests/macros.rs b/crates/ra_hir_ty/src/tests/macros.rs index 3b7022ad5..2e309a379 100644 --- a/crates/ra_hir_ty/src/tests/macros.rs +++ b/crates/ra_hir_ty/src/tests/macros.rs | |||
@@ -462,7 +462,7 @@ fn main() { | |||
462 | fn infer_builtin_macros_include() { | 462 | fn infer_builtin_macros_include() { |
463 | let (db, pos) = TestDB::with_position( | 463 | let (db, pos) = TestDB::with_position( |
464 | r#" | 464 | r#" |
465 | //- /main.rs | 465 | //- /main.rs |
466 | #[rustc_builtin_macro] | 466 | #[rustc_builtin_macro] |
467 | macro_rules! include {() => {}} | 467 | macro_rules! include {() => {}} |
468 | 468 | ||
@@ -483,7 +483,7 @@ fn bar() -> u32 {0} | |||
483 | fn infer_builtin_macros_include_concat() { | 483 | fn infer_builtin_macros_include_concat() { |
484 | let (db, pos) = TestDB::with_position( | 484 | let (db, pos) = TestDB::with_position( |
485 | r#" | 485 | r#" |
486 | //- /main.rs | 486 | //- /main.rs |
487 | #[rustc_builtin_macro] | 487 | #[rustc_builtin_macro] |
488 | macro_rules! include {() => {}} | 488 | macro_rules! include {() => {}} |
489 | 489 | ||
@@ -507,7 +507,7 @@ fn bar() -> u32 {0} | |||
507 | fn infer_builtin_macros_include_concat_with_bad_env_should_failed() { | 507 | fn infer_builtin_macros_include_concat_with_bad_env_should_failed() { |
508 | let (db, pos) = TestDB::with_position( | 508 | let (db, pos) = TestDB::with_position( |
509 | r#" | 509 | r#" |
510 | //- /main.rs | 510 | //- /main.rs |
511 | #[rustc_builtin_macro] | 511 | #[rustc_builtin_macro] |
512 | macro_rules! include {() => {}} | 512 | macro_rules! include {() => {}} |
513 | 513 | ||
@@ -534,7 +534,7 @@ fn bar() -> u32 {0} | |||
534 | fn infer_builtin_macros_include_itself_should_failed() { | 534 | fn infer_builtin_macros_include_itself_should_failed() { |
535 | let (db, pos) = TestDB::with_position( | 535 | let (db, pos) = TestDB::with_position( |
536 | r#" | 536 | r#" |
537 | //- /main.rs | 537 | //- /main.rs |
538 | #[rustc_builtin_macro] | 538 | #[rustc_builtin_macro] |
539 | macro_rules! include {() => {}} | 539 | macro_rules! include {() => {}} |
540 | 540 | ||
diff --git a/crates/ra_ide/src/completion/complete_dot.rs b/crates/ra_ide/src/completion/complete_dot.rs index f07611d88..a30d1c2de 100644 --- a/crates/ra_ide/src/completion/complete_dot.rs +++ b/crates/ra_ide/src/completion/complete_dot.rs | |||
@@ -752,6 +752,43 @@ mod tests { | |||
752 | } | 752 | } |
753 | 753 | ||
754 | #[test] | 754 | #[test] |
755 | fn macro_expansion_resilient() { | ||
756 | assert_debug_snapshot!( | ||
757 | do_ref_completion( | ||
758 | r" | ||
759 | macro_rules! dbg { | ||
760 | () => {}; | ||
761 | ($val:expr) => { | ||
762 | match $val { tmp => { tmp } } | ||
763 | }; | ||
764 | // Trailing comma with single argument is ignored | ||
765 | ($val:expr,) => { $crate::dbg!($val) }; | ||
766 | ($($val:expr),+ $(,)?) => { | ||
767 | ($($crate::dbg!($val)),+,) | ||
768 | }; | ||
769 | } | ||
770 | struct A { the_field: u32 } | ||
771 | fn foo(a: A) { | ||
772 | dbg!(a.<|>) | ||
773 | } | ||
774 | ", | ||
775 | ), | ||
776 | @r###" | ||
777 | [ | ||
778 | CompletionItem { | ||
779 | label: "the_field", | ||
780 | source_range: [552; 553), | ||
781 | delete: [552; 553), | ||
782 | insert: "the_field", | ||
783 | kind: Field, | ||
784 | detail: "u32", | ||
785 | }, | ||
786 | ] | ||
787 | "### | ||
788 | ); | ||
789 | } | ||
790 | |||
791 | #[test] | ||
755 | fn test_method_completion_3547() { | 792 | fn test_method_completion_3547() { |
756 | assert_debug_snapshot!( | 793 | assert_debug_snapshot!( |
757 | do_ref_completion( | 794 | do_ref_completion( |
diff --git a/crates/ra_ide/src/expand_macro.rs b/crates/ra_ide/src/expand_macro.rs index f6667cb33..e58526f31 100644 --- a/crates/ra_ide/src/expand_macro.rs +++ b/crates/ra_ide/src/expand_macro.rs | |||
@@ -259,7 +259,7 @@ fn some_thing() -> u32 { | |||
259 | ); | 259 | ); |
260 | 260 | ||
261 | assert_eq!(res.name, "foo"); | 261 | assert_eq!(res.name, "foo"); |
262 | assert_snapshot!(res.expansion, @r###"bar!()"###); | 262 | assert_snapshot!(res.expansion, @r###""###); |
263 | } | 263 | } |
264 | 264 | ||
265 | #[test] | 265 | #[test] |
diff --git a/crates/ra_mbe/src/lib.rs b/crates/ra_mbe/src/lib.rs index 43afe24cc..3adec4978 100644 --- a/crates/ra_mbe/src/lib.rs +++ b/crates/ra_mbe/src/lib.rs | |||
@@ -30,6 +30,8 @@ pub enum ExpandError { | |||
30 | InvalidRepeat, | 30 | InvalidRepeat, |
31 | } | 31 | } |
32 | 32 | ||
33 | pub type ExpandResult<T> = (T, Option<ExpandError>); | ||
34 | |||
33 | pub use crate::syntax_bridge::{ | 35 | pub use crate::syntax_bridge::{ |
34 | ast_to_token_tree, parse_to_token_tree, syntax_node_to_token_tree, token_tree_to_syntax_node, | 36 | ast_to_token_tree, parse_to_token_tree, syntax_node_to_token_tree, token_tree_to_syntax_node, |
35 | TokenMap, | 37 | TokenMap, |
@@ -150,7 +152,7 @@ impl MacroRules { | |||
150 | Ok(MacroRules { rules, shift: Shift::new(tt) }) | 152 | Ok(MacroRules { rules, shift: Shift::new(tt) }) |
151 | } | 153 | } |
152 | 154 | ||
153 | pub fn expand(&self, tt: &tt::Subtree) -> Result<tt::Subtree, ExpandError> { | 155 | pub fn expand(&self, tt: &tt::Subtree) -> ExpandResult<tt::Subtree> { |
154 | // apply shift | 156 | // apply shift |
155 | let mut tt = tt.clone(); | 157 | let mut tt = tt.clone(); |
156 | self.shift.shift_all(&mut tt); | 158 | self.shift.shift_all(&mut tt); |
diff --git a/crates/ra_mbe/src/mbe_expander.rs b/crates/ra_mbe/src/mbe_expander.rs index b455b7321..da3952428 100644 --- a/crates/ra_mbe/src/mbe_expander.rs +++ b/crates/ra_mbe/src/mbe_expander.rs | |||
@@ -8,19 +8,30 @@ mod transcriber; | |||
8 | use ra_syntax::SmolStr; | 8 | use ra_syntax::SmolStr; |
9 | use rustc_hash::FxHashMap; | 9 | use rustc_hash::FxHashMap; |
10 | 10 | ||
11 | use crate::ExpandError; | 11 | use crate::{ExpandResult, ExpandError}; |
12 | 12 | ||
13 | pub(crate) fn expand( | 13 | pub(crate) fn expand( |
14 | rules: &crate::MacroRules, | 14 | rules: &crate::MacroRules, |
15 | input: &tt::Subtree, | 15 | input: &tt::Subtree, |
16 | ) -> Result<tt::Subtree, ExpandError> { | 16 | ) -> ExpandResult<tt::Subtree> { |
17 | rules.rules.iter().find_map(|it| expand_rule(it, input).ok()).ok_or(ExpandError::NoMatchingRule) | 17 | let (mut result, mut err) = (tt::Subtree::default(), Some(ExpandError::NoMatchingRule)); |
18 | for rule in &rules.rules { | ||
19 | let (res, e) = expand_rule(rule, input); | ||
20 | if e.is_none() { | ||
21 | // if we find a rule that applies without errors, we're done | ||
22 | return (res, None); | ||
23 | } | ||
24 | // TODO decide which result is better | ||
25 | result = res; | ||
26 | err = e; | ||
27 | } | ||
28 | (result, err) | ||
18 | } | 29 | } |
19 | 30 | ||
20 | fn expand_rule(rule: &crate::Rule, input: &tt::Subtree) -> Result<tt::Subtree, ExpandError> { | 31 | fn expand_rule(rule: &crate::Rule, input: &tt::Subtree) -> ExpandResult<tt::Subtree> { |
21 | let bindings = matcher::match_(&rule.lhs, input)?; | 32 | let (bindings, bindings_err) = dbg!(matcher::match_(&rule.lhs, input)); |
22 | let res = transcriber::transcribe(&rule.rhs, &bindings)?; | 33 | let (res, transcribe_err) = dbg!(transcriber::transcribe(&rule.rhs, &bindings)); |
23 | Ok(res) | 34 | (res, bindings_err.or(transcribe_err)) |
24 | } | 35 | } |
25 | 36 | ||
26 | /// The actual algorithm for expansion is not too hard, but is pretty tricky. | 37 | /// The actual algorithm for expansion is not too hard, but is pretty tricky. |
@@ -111,7 +122,7 @@ mod tests { | |||
111 | } | 122 | } |
112 | 123 | ||
113 | fn assert_err(macro_body: &str, invocation: &str, err: ExpandError) { | 124 | fn assert_err(macro_body: &str, invocation: &str, err: ExpandError) { |
114 | assert_eq!(expand_first(&create_rules(&format_macro(macro_body)), invocation), Err(err)); | 125 | assert_eq!(expand_first(&create_rules(&format_macro(macro_body)), invocation).1, Some(err)); |
115 | } | 126 | } |
116 | 127 | ||
117 | fn format_macro(macro_body: &str) -> String { | 128 | fn format_macro(macro_body: &str) -> String { |
@@ -138,7 +149,7 @@ mod tests { | |||
138 | fn expand_first( | 149 | fn expand_first( |
139 | rules: &crate::MacroRules, | 150 | rules: &crate::MacroRules, |
140 | invocation: &str, | 151 | invocation: &str, |
141 | ) -> Result<tt::Subtree, ExpandError> { | 152 | ) -> ExpandResult<tt::Subtree> { |
142 | let source_file = ast::SourceFile::parse(invocation).ok().unwrap(); | 153 | let source_file = ast::SourceFile::parse(invocation).ok().unwrap(); |
143 | let macro_invocation = | 154 | let macro_invocation = |
144 | source_file.syntax().descendants().find_map(ast::MacroCall::cast).unwrap(); | 155 | source_file.syntax().descendants().find_map(ast::MacroCall::cast).unwrap(); |
diff --git a/crates/ra_mbe/src/mbe_expander/matcher.rs b/crates/ra_mbe/src/mbe_expander/matcher.rs index 49c53183a..f9d4952c6 100644 --- a/crates/ra_mbe/src/mbe_expander/matcher.rs +++ b/crates/ra_mbe/src/mbe_expander/matcher.rs | |||
@@ -11,6 +11,7 @@ use crate::{ | |||
11 | use ra_parser::{FragmentKind::*, TreeSink}; | 11 | use ra_parser::{FragmentKind::*, TreeSink}; |
12 | use ra_syntax::{SmolStr, SyntaxKind}; | 12 | use ra_syntax::{SmolStr, SyntaxKind}; |
13 | use tt::buffer::{Cursor, TokenBuffer}; | 13 | use tt::buffer::{Cursor, TokenBuffer}; |
14 | use super::ExpandResult; | ||
14 | 15 | ||
15 | impl Bindings { | 16 | impl Bindings { |
16 | fn push_optional(&mut self, name: &SmolStr) { | 17 | fn push_optional(&mut self, name: &SmolStr) { |
@@ -64,19 +65,19 @@ macro_rules! bail { | |||
64 | }; | 65 | }; |
65 | } | 66 | } |
66 | 67 | ||
67 | pub(super) fn match_(pattern: &tt::Subtree, src: &tt::Subtree) -> Result<Bindings, ExpandError> { | 68 | pub(super) fn match_(pattern: &tt::Subtree, src: &tt::Subtree) -> ExpandResult<Bindings> { |
68 | assert!(pattern.delimiter == None); | 69 | assert!(pattern.delimiter == None); |
69 | 70 | ||
70 | let mut res = Bindings::default(); | 71 | let mut res = Bindings::default(); |
71 | let mut src = TtIter::new(src); | 72 | let mut src = TtIter::new(src); |
72 | 73 | ||
73 | match_subtree(&mut res, pattern, &mut src)?; | 74 | let mut err = match_subtree(&mut res, pattern, &mut src).err(); |
74 | 75 | ||
75 | if src.len() > 0 { | 76 | if src.len() > 0 && err.is_none() { |
76 | bail!("leftover tokens"); | 77 | err = Some(err!("leftover tokens")); |
77 | } | 78 | } |
78 | 79 | ||
79 | Ok(res) | 80 | (res, err) |
80 | } | 81 | } |
81 | 82 | ||
82 | fn match_subtree( | 83 | fn match_subtree( |
diff --git a/crates/ra_mbe/src/mbe_expander/transcriber.rs b/crates/ra_mbe/src/mbe_expander/transcriber.rs index 7662020f3..c53c2d35e 100644 --- a/crates/ra_mbe/src/mbe_expander/transcriber.rs +++ b/crates/ra_mbe/src/mbe_expander/transcriber.rs | |||
@@ -3,6 +3,7 @@ | |||
3 | 3 | ||
4 | use ra_syntax::SmolStr; | 4 | use ra_syntax::SmolStr; |
5 | 5 | ||
6 | use super::ExpandResult; | ||
6 | use crate::{ | 7 | use crate::{ |
7 | mbe_expander::{Binding, Bindings, Fragment}, | 8 | mbe_expander::{Binding, Bindings, Fragment}, |
8 | parser::{parse_template, Op, RepeatKind, Separator}, | 9 | parser::{parse_template, Op, RepeatKind, Separator}, |
@@ -49,10 +50,7 @@ impl Bindings { | |||
49 | } | 50 | } |
50 | } | 51 | } |
51 | 52 | ||
52 | pub(super) fn transcribe( | 53 | pub(super) fn transcribe(template: &tt::Subtree, bindings: &Bindings) -> ExpandResult<tt::Subtree> { |
53 | template: &tt::Subtree, | ||
54 | bindings: &Bindings, | ||
55 | ) -> Result<tt::Subtree, ExpandError> { | ||
56 | assert!(template.delimiter == None); | 54 | assert!(template.delimiter == None); |
57 | let mut ctx = ExpandCtx { bindings: &bindings, nesting: Vec::new() }; | 55 | let mut ctx = ExpandCtx { bindings: &bindings, nesting: Vec::new() }; |
58 | expand_subtree(&mut ctx, template) | 56 | expand_subtree(&mut ctx, template) |
@@ -75,35 +73,46 @@ struct ExpandCtx<'a> { | |||
75 | nesting: Vec<NestingState>, | 73 | nesting: Vec<NestingState>, |
76 | } | 74 | } |
77 | 75 | ||
78 | fn expand_subtree(ctx: &mut ExpandCtx, template: &tt::Subtree) -> Result<tt::Subtree, ExpandError> { | 76 | fn expand_subtree(ctx: &mut ExpandCtx, template: &tt::Subtree) -> ExpandResult<tt::Subtree> { |
79 | let mut buf: Vec<tt::TokenTree> = Vec::new(); | 77 | let mut buf: Vec<tt::TokenTree> = Vec::new(); |
78 | let mut err = None; | ||
80 | for op in parse_template(template) { | 79 | for op in parse_template(template) { |
81 | match op? { | 80 | let op = match op { |
81 | Ok(op) => op, | ||
82 | Err(e) => { | ||
83 | err = Some(e); | ||
84 | break; | ||
85 | } | ||
86 | }; | ||
87 | match op { | ||
82 | Op::TokenTree(tt @ tt::TokenTree::Leaf(..)) => buf.push(tt.clone()), | 88 | Op::TokenTree(tt @ tt::TokenTree::Leaf(..)) => buf.push(tt.clone()), |
83 | Op::TokenTree(tt::TokenTree::Subtree(tt)) => { | 89 | Op::TokenTree(tt::TokenTree::Subtree(tt)) => { |
84 | let tt = expand_subtree(ctx, tt)?; | 90 | let (tt, e) = expand_subtree(ctx, tt); |
91 | err = err.or(e); | ||
85 | buf.push(tt.into()); | 92 | buf.push(tt.into()); |
86 | } | 93 | } |
87 | Op::Var { name, kind: _ } => { | 94 | Op::Var { name, kind: _ } => { |
88 | let fragment = expand_var(ctx, name)?; | 95 | let (fragment, e) = expand_var(ctx, name); |
96 | err = err.or(e); | ||
89 | push_fragment(&mut buf, fragment); | 97 | push_fragment(&mut buf, fragment); |
90 | } | 98 | } |
91 | Op::Repeat { subtree, kind, separator } => { | 99 | Op::Repeat { subtree, kind, separator } => { |
92 | let fragment = expand_repeat(ctx, subtree, kind, separator)?; | 100 | let (fragment, e) = expand_repeat(ctx, subtree, kind, separator); |
101 | err = err.or(e); | ||
93 | push_fragment(&mut buf, fragment) | 102 | push_fragment(&mut buf, fragment) |
94 | } | 103 | } |
95 | } | 104 | } |
96 | } | 105 | } |
97 | Ok(tt::Subtree { delimiter: template.delimiter, token_trees: buf }) | 106 | (tt::Subtree { delimiter: template.delimiter, token_trees: buf }, err) |
98 | } | 107 | } |
99 | 108 | ||
100 | fn expand_var(ctx: &mut ExpandCtx, v: &SmolStr) -> Result<Fragment, ExpandError> { | 109 | fn expand_var(ctx: &mut ExpandCtx, v: &SmolStr) -> ExpandResult<Fragment> { |
101 | let res = if v == "crate" { | 110 | if v == "crate" { |
102 | // We simply produce identifier `$crate` here. And it will be resolved when lowering ast to Path. | 111 | // We simply produce identifier `$crate` here. And it will be resolved when lowering ast to Path. |
103 | let tt = | 112 | let tt = |
104 | tt::Leaf::from(tt::Ident { text: "$crate".into(), id: tt::TokenId::unspecified() }) | 113 | tt::Leaf::from(tt::Ident { text: "$crate".into(), id: tt::TokenId::unspecified() }) |
105 | .into(); | 114 | .into(); |
106 | Fragment::Tokens(tt) | 115 | (Fragment::Tokens(tt), None) |
107 | } else if !ctx.bindings.contains(v) { | 116 | } else if !ctx.bindings.contains(v) { |
108 | // Note that it is possible to have a `$var` inside a macro which is not bound. | 117 | // Note that it is possible to have a `$var` inside a macro which is not bound. |
109 | // For example: | 118 | // For example: |
@@ -132,11 +141,13 @@ fn expand_var(ctx: &mut ExpandCtx, v: &SmolStr) -> Result<Fragment, ExpandError> | |||
132 | ], | 141 | ], |
133 | } | 142 | } |
134 | .into(); | 143 | .into(); |
135 | Fragment::Tokens(tt) | 144 | (Fragment::Tokens(tt), None) |
136 | } else { | 145 | } else { |
137 | ctx.bindings.get(&v, &mut ctx.nesting)?.clone() | 146 | ctx.bindings.get(&v, &mut ctx.nesting).map_or_else( |
138 | }; | 147 | |e| (Fragment::Tokens(tt::TokenTree::empty()), Some(e)), |
139 | Ok(res) | 148 | |b| (b.clone(), None), |
149 | ) | ||
150 | } | ||
140 | } | 151 | } |
141 | 152 | ||
142 | fn expand_repeat( | 153 | fn expand_repeat( |
@@ -144,17 +155,17 @@ fn expand_repeat( | |||
144 | template: &tt::Subtree, | 155 | template: &tt::Subtree, |
145 | kind: RepeatKind, | 156 | kind: RepeatKind, |
146 | separator: Option<Separator>, | 157 | separator: Option<Separator>, |
147 | ) -> Result<Fragment, ExpandError> { | 158 | ) -> ExpandResult<Fragment> { |
148 | let mut buf: Vec<tt::TokenTree> = Vec::new(); | 159 | let mut buf: Vec<tt::TokenTree> = Vec::new(); |
149 | ctx.nesting.push(NestingState { idx: 0, at_end: false, hit: false }); | 160 | ctx.nesting.push(NestingState { idx: 0, at_end: false, hit: false }); |
150 | // Dirty hack to make macro-expansion terminate. | 161 | // Dirty hack to make macro-expansion terminate. |
151 | // This should be replaced by a propper macro-by-example implementation | 162 | // This should be replaced by a proper macro-by-example implementation |
152 | let limit = 65536; | 163 | let limit = 65536; |
153 | let mut has_seps = 0; | 164 | let mut has_seps = 0; |
154 | let mut counter = 0; | 165 | let mut counter = 0; |
155 | 166 | ||
156 | loop { | 167 | loop { |
157 | let res = expand_subtree(ctx, template); | 168 | let (mut t, e) = expand_subtree(ctx, template); |
158 | let nesting_state = ctx.nesting.last_mut().unwrap(); | 169 | let nesting_state = ctx.nesting.last_mut().unwrap(); |
159 | if nesting_state.at_end || !nesting_state.hit { | 170 | if nesting_state.at_end || !nesting_state.hit { |
160 | break; | 171 | break; |
@@ -172,10 +183,10 @@ fn expand_repeat( | |||
172 | break; | 183 | break; |
173 | } | 184 | } |
174 | 185 | ||
175 | let mut t = match res { | 186 | if e.is_some() { |
176 | Ok(t) => t, | 187 | continue; |
177 | Err(_) => continue, | 188 | } |
178 | }; | 189 | |
179 | t.delimiter = None; | 190 | t.delimiter = None; |
180 | push_subtree(&mut buf, t); | 191 | push_subtree(&mut buf, t); |
181 | 192 | ||
@@ -209,14 +220,14 @@ fn expand_repeat( | |||
209 | buf.pop(); | 220 | buf.pop(); |
210 | } | 221 | } |
211 | 222 | ||
212 | if RepeatKind::OneOrMore == kind && counter == 0 { | ||
213 | return Err(ExpandError::UnexpectedToken); | ||
214 | } | ||
215 | |||
216 | // Check if it is a single token subtree without any delimiter | 223 | // Check if it is a single token subtree without any delimiter |
217 | // e.g {Delimiter:None> ['>'] /Delimiter:None>} | 224 | // e.g {Delimiter:None> ['>'] /Delimiter:None>} |
218 | let tt = tt::Subtree { delimiter: None, token_trees: buf }.into(); | 225 | let tt = tt::Subtree { delimiter: None, token_trees: buf }.into(); |
219 | Ok(Fragment::Tokens(tt)) | 226 | |
227 | if RepeatKind::OneOrMore == kind && counter == 0 { | ||
228 | return (Fragment::Tokens(tt), Some(ExpandError::UnexpectedToken)); | ||
229 | } | ||
230 | (Fragment::Tokens(tt), None) | ||
220 | } | 231 | } |
221 | 232 | ||
222 | fn push_fragment(buf: &mut Vec<tt::TokenTree>, fragment: Fragment) { | 233 | fn push_fragment(buf: &mut Vec<tt::TokenTree>, fragment: Fragment) { |
diff --git a/crates/ra_mbe/src/tests.rs b/crates/ra_mbe/src/tests.rs index 6d5d1e9e6..4d3140fa9 100644 --- a/crates/ra_mbe/src/tests.rs +++ b/crates/ra_mbe/src/tests.rs | |||
@@ -1430,7 +1430,8 @@ impl MacroFixture { | |||
1430 | let (invocation_tt, _) = | 1430 | let (invocation_tt, _) = |
1431 | ast_to_token_tree(¯o_invocation.token_tree().unwrap()).unwrap(); | 1431 | ast_to_token_tree(¯o_invocation.token_tree().unwrap()).unwrap(); |
1432 | 1432 | ||
1433 | self.rules.expand(&invocation_tt) | 1433 | let (tt, err) = self.rules.expand(&invocation_tt); |
1434 | err.map(Err).unwrap_or(Ok(tt)) | ||
1434 | } | 1435 | } |
1435 | 1436 | ||
1436 | fn assert_expand_err(&self, invocation: &str, err: &ExpandError) { | 1437 | fn assert_expand_err(&self, invocation: &str, err: &ExpandError) { |
diff --git a/crates/ra_tt/src/lib.rs b/crates/ra_tt/src/lib.rs index 10f424aae..1e2fb8b91 100644 --- a/crates/ra_tt/src/lib.rs +++ b/crates/ra_tt/src/lib.rs | |||
@@ -40,6 +40,12 @@ pub enum TokenTree { | |||
40 | } | 40 | } |
41 | impl_froms!(TokenTree: Leaf, Subtree); | 41 | impl_froms!(TokenTree: Leaf, Subtree); |
42 | 42 | ||
43 | impl TokenTree { | ||
44 | pub fn empty() -> Self { | ||
45 | TokenTree::Subtree(Subtree::default()) | ||
46 | } | ||
47 | } | ||
48 | |||
43 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] | 49 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] |
44 | pub enum Leaf { | 50 | pub enum Leaf { |
45 | Literal(Literal), | 51 | Literal(Literal), |