diff options
author | bors[bot] <26634292+bors[bot]@users.noreply.github.com> | 2020-03-09 08:56:58 +0000 |
---|---|---|
committer | GitHub <[email protected]> | 2020-03-09 08:56:58 +0000 |
commit | beb4f4954179998e317db33e47a48a9bb7374977 (patch) | |
tree | e2af54ce707bfcdd0a0cd3fc5d547f43fc9fb7d1 | |
parent | 30062da6284052deac04c759540f81d5b326689c (diff) | |
parent | afdf08e964345ac4a884a5630772611ba81f6969 (diff) |
Merge #3513
3513: Completion in macros r=matklad a=flodiebold
I experimented a bit with completion in macros. It's kind of working, but there are a lot of rough edges.
- I'm trying to expand the macro call with the inserted fake token. This requires some hacky additions on the HIR level to be able to do "hypothetical" expansions. There should probably be a nicer API for this, if we want to do it this way. I'm not sure whether it's worth it, because we still can't do a lot if the original macro call didn't expand in nearly the same way. E.g. if we have something like `println!("", x<|>)` the expansions will look the same and everything is fine; but in that case we could maybe have achieved the same result in a simpler way. If we have something like `m!(<|>)` where `m!()` doesn't even expand or expands to something very different, we don't really know what to do anyway.
- Relatedly, there are a lot of cases where this doesn't work because either the original call or the hypothetical call doesn't expand. E.g. if we have `m!(x.<|>)` the original token tree doesn't parse as an expression; if we have `m!(match x { <|> })` the hypothetical token tree doesn't parse. It would be nice if we could have better error recovery in these cases.
Co-authored-by: Florian Diebold <[email protected]>
-rw-r--r-- | Cargo.lock | 1 | ||||
-rw-r--r-- | crates/ra_hir/Cargo.toml | 2 | ||||
-rw-r--r-- | crates/ra_hir/src/semantics.rs | 35 | ||||
-rw-r--r-- | crates/ra_hir_expand/src/db.rs | 71 | ||||
-rw-r--r-- | crates/ra_ide/src/completion/complete_dot.rs | 102 | ||||
-rw-r--r-- | crates/ra_ide/src/completion/complete_keyword.rs | 1 | ||||
-rw-r--r-- | crates/ra_ide/src/completion/complete_path.rs | 37 | ||||
-rw-r--r-- | crates/ra_ide/src/completion/complete_pattern.rs | 18 | ||||
-rw-r--r-- | crates/ra_ide/src/completion/complete_postfix.rs | 65 | ||||
-rw-r--r-- | crates/ra_ide/src/completion/complete_record_literal.rs | 25 | ||||
-rw-r--r-- | crates/ra_ide/src/completion/complete_record_pattern.rs | 28 | ||||
-rw-r--r-- | crates/ra_ide/src/completion/complete_scope.rs | 70 | ||||
-rw-r--r-- | crates/ra_ide/src/completion/completion_context.rs | 111 | ||||
-rw-r--r-- | crates/ra_mbe/src/mbe_expander/matcher.rs | 4 |
14 files changed, 528 insertions, 42 deletions
diff --git a/Cargo.lock b/Cargo.lock index 80e778bcf..2e052d267 100644 --- a/Cargo.lock +++ b/Cargo.lock | |||
@@ -960,6 +960,7 @@ name = "ra_hir" | |||
960 | version = "0.1.0" | 960 | version = "0.1.0" |
961 | dependencies = [ | 961 | dependencies = [ |
962 | "either", | 962 | "either", |
963 | "itertools", | ||
963 | "log", | 964 | "log", |
964 | "ra_db", | 965 | "ra_db", |
965 | "ra_hir_def", | 966 | "ra_hir_def", |
diff --git a/crates/ra_hir/Cargo.toml b/crates/ra_hir/Cargo.toml index 0555a0de7..266c4cff3 100644 --- a/crates/ra_hir/Cargo.toml +++ b/crates/ra_hir/Cargo.toml | |||
@@ -12,6 +12,8 @@ log = "0.4.8" | |||
12 | rustc-hash = "1.1.0" | 12 | rustc-hash = "1.1.0" |
13 | either = "1.5.3" | 13 | either = "1.5.3" |
14 | 14 | ||
15 | itertools = "0.8.2" | ||
16 | |||
15 | ra_syntax = { path = "../ra_syntax" } | 17 | ra_syntax = { path = "../ra_syntax" } |
16 | ra_db = { path = "../ra_db" } | 18 | ra_db = { path = "../ra_db" } |
17 | ra_prof = { path = "../ra_prof" } | 19 | ra_prof = { path = "../ra_prof" } |
diff --git a/crates/ra_hir/src/semantics.rs b/crates/ra_hir/src/semantics.rs index 965d185a4..3782a9984 100644 --- a/crates/ra_hir/src/semantics.rs +++ b/crates/ra_hir/src/semantics.rs | |||
@@ -6,7 +6,7 @@ use std::{cell::RefCell, fmt, iter::successors}; | |||
6 | 6 | ||
7 | use hir_def::{ | 7 | use hir_def::{ |
8 | resolver::{self, HasResolver, Resolver}, | 8 | resolver::{self, HasResolver, Resolver}, |
9 | TraitId, | 9 | AsMacroCall, TraitId, |
10 | }; | 10 | }; |
11 | use hir_expand::ExpansionInfo; | 11 | use hir_expand::ExpansionInfo; |
12 | use ra_db::{FileId, FileRange}; | 12 | use ra_db::{FileId, FileRange}; |
@@ -70,6 +70,20 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> { | |||
70 | Some(node) | 70 | Some(node) |
71 | } | 71 | } |
72 | 72 | ||
73 | pub fn expand_hypothetical( | ||
74 | &self, | ||
75 | actual_macro_call: &ast::MacroCall, | ||
76 | hypothetical_args: &ast::TokenTree, | ||
77 | token_to_map: SyntaxToken, | ||
78 | ) -> Option<(SyntaxNode, SyntaxToken)> { | ||
79 | let macro_call = | ||
80 | self.find_file(actual_macro_call.syntax().clone()).with_value(actual_macro_call); | ||
81 | let sa = self.analyze2(macro_call.map(|it| it.syntax()), None); | ||
82 | let macro_call_id = macro_call | ||
83 | .as_call_id(self.db, |path| sa.resolver.resolve_path_as_macro(self.db, &path))?; | ||
84 | hir_expand::db::expand_hypothetical(self.db, macro_call_id, hypothetical_args, token_to_map) | ||
85 | } | ||
86 | |||
73 | pub fn descend_into_macros(&self, token: SyntaxToken) -> SyntaxToken { | 87 | pub fn descend_into_macros(&self, token: SyntaxToken) -> SyntaxToken { |
74 | let parent = token.parent(); | 88 | let parent = token.parent(); |
75 | let parent = self.find_file(parent); | 89 | let parent = self.find_file(parent); |
@@ -104,6 +118,25 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> { | |||
104 | node.ancestors_with_macros(self.db).map(|it| it.value) | 118 | node.ancestors_with_macros(self.db).map(|it| it.value) |
105 | } | 119 | } |
106 | 120 | ||
121 | pub fn ancestors_at_offset_with_macros( | ||
122 | &self, | ||
123 | node: &SyntaxNode, | ||
124 | offset: TextUnit, | ||
125 | ) -> impl Iterator<Item = SyntaxNode> + '_ { | ||
126 | use itertools::Itertools; | ||
127 | node.token_at_offset(offset) | ||
128 | .map(|token| self.ancestors_with_macros(token.parent())) | ||
129 | .kmerge_by(|node1, node2| node1.text_range().len() < node2.text_range().len()) | ||
130 | } | ||
131 | |||
132 | pub fn find_node_at_offset_with_macros<N: AstNode>( | ||
133 | &self, | ||
134 | node: &SyntaxNode, | ||
135 | offset: TextUnit, | ||
136 | ) -> Option<N> { | ||
137 | self.ancestors_at_offset_with_macros(node, offset).find_map(N::cast) | ||
138 | } | ||
139 | |||
107 | pub fn type_of_expr(&self, expr: &ast::Expr) -> Option<Type> { | 140 | pub fn type_of_expr(&self, expr: &ast::Expr) -> Option<Type> { |
108 | self.analyze(expr.syntax()).type_of(self.db, &expr) | 141 | self.analyze(expr.syntax()).type_of(self.db, &expr) |
109 | } | 142 | } |
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. | ||
79 | pub 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 | |||
75 | pub(crate) fn ast_id_map(db: &dyn AstDatabase, file_id: HirFileId) -> Arc<AstIdMap> { | 99 | pub(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 | |||
160 | fn 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 | |||
173 | fn 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, ¯o_arg.0).map_err(|err| format!("{:?}", err))?; | 195 | let tt = macro_rules.0.expand(db, lazy_id, ¯o_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 | |||
220 | pub 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, |
diff --git a/crates/ra_ide/src/completion/complete_dot.rs b/crates/ra_ide/src/completion/complete_dot.rs index acada48ae..f275305e2 100644 --- a/crates/ra_ide/src/completion/complete_dot.rs +++ b/crates/ra_ide/src/completion/complete_dot.rs | |||
@@ -38,7 +38,7 @@ pub(super) fn complete_dot(acc: &mut Completions, ctx: &CompletionContext) { | |||
38 | fn complete_fields(acc: &mut Completions, ctx: &CompletionContext, receiver: &Type) { | 38 | fn complete_fields(acc: &mut Completions, ctx: &CompletionContext, receiver: &Type) { |
39 | for receiver in receiver.autoderef(ctx.db) { | 39 | for receiver in receiver.autoderef(ctx.db) { |
40 | for (field, ty) in receiver.fields(ctx.db) { | 40 | for (field, ty) in receiver.fields(ctx.db) { |
41 | if ctx.module.map_or(false, |m| !field.is_visible_from(ctx.db, m)) { | 41 | if ctx.scope().module().map_or(false, |m| !field.is_visible_from(ctx.db, m)) { |
42 | // Skip private field. FIXME: If the definition location of the | 42 | // Skip private field. FIXME: If the definition location of the |
43 | // field is editable, we should show the completion | 43 | // field is editable, we should show the completion |
44 | continue; | 44 | continue; |
@@ -53,7 +53,7 @@ fn complete_fields(acc: &mut Completions, ctx: &CompletionContext, receiver: &Ty | |||
53 | } | 53 | } |
54 | 54 | ||
55 | fn complete_methods(acc: &mut Completions, ctx: &CompletionContext, receiver: &Type) { | 55 | fn complete_methods(acc: &mut Completions, ctx: &CompletionContext, receiver: &Type) { |
56 | if let Some(krate) = ctx.module.map(|it| it.krate()) { | 56 | if let Some(krate) = ctx.krate { |
57 | let mut seen_methods = FxHashSet::default(); | 57 | let mut seen_methods = FxHashSet::default(); |
58 | let traits_in_scope = ctx.scope().traits_in_scope(); | 58 | let traits_in_scope = ctx.scope().traits_in_scope(); |
59 | receiver.iterate_method_candidates(ctx.db, krate, &traits_in_scope, None, |_ty, func| { | 59 | receiver.iterate_method_candidates(ctx.db, krate, &traits_in_scope, None, |_ty, func| { |
@@ -620,4 +620,102 @@ mod tests { | |||
620 | "### | 620 | "### |
621 | ); | 621 | ); |
622 | } | 622 | } |
623 | |||
624 | #[test] | ||
625 | fn works_in_simple_macro_1() { | ||
626 | assert_debug_snapshot!( | ||
627 | do_ref_completion( | ||
628 | r" | ||
629 | macro_rules! m { ($e:expr) => { $e } } | ||
630 | struct A { the_field: u32 } | ||
631 | fn foo(a: A) { | ||
632 | m!(a.x<|>) | ||
633 | } | ||
634 | ", | ||
635 | ), | ||
636 | @r###" | ||
637 | [ | ||
638 | CompletionItem { | ||
639 | label: "the_field", | ||
640 | source_range: [156; 157), | ||
641 | delete: [156; 157), | ||
642 | insert: "the_field", | ||
643 | kind: Field, | ||
644 | detail: "u32", | ||
645 | }, | ||
646 | ] | ||
647 | "### | ||
648 | ); | ||
649 | } | ||
650 | |||
651 | #[test] | ||
652 | fn works_in_simple_macro_recursive() { | ||
653 | assert_debug_snapshot!( | ||
654 | do_ref_completion( | ||
655 | r" | ||
656 | macro_rules! m { ($e:expr) => { $e } } | ||
657 | struct A { the_field: u32 } | ||
658 | fn foo(a: A) { | ||
659 | m!(a.x<|>) | ||
660 | } | ||
661 | ", | ||
662 | ), | ||
663 | @r###" | ||
664 | [ | ||
665 | CompletionItem { | ||
666 | label: "the_field", | ||
667 | source_range: [156; 157), | ||
668 | delete: [156; 157), | ||
669 | insert: "the_field", | ||
670 | kind: Field, | ||
671 | detail: "u32", | ||
672 | }, | ||
673 | ] | ||
674 | "### | ||
675 | ); | ||
676 | } | ||
677 | |||
678 | #[test] | ||
679 | fn works_in_simple_macro_2() { | ||
680 | // this doesn't work yet because the macro doesn't expand without the token -- maybe it can be fixed with better recovery | ||
681 | assert_debug_snapshot!( | ||
682 | do_ref_completion( | ||
683 | r" | ||
684 | macro_rules! m { ($e:expr) => { $e } } | ||
685 | struct A { the_field: u32 } | ||
686 | fn foo(a: A) { | ||
687 | m!(a.<|>) | ||
688 | } | ||
689 | ", | ||
690 | ), | ||
691 | @r###"[]"### | ||
692 | ); | ||
693 | } | ||
694 | |||
695 | #[test] | ||
696 | fn works_in_simple_macro_recursive_1() { | ||
697 | assert_debug_snapshot!( | ||
698 | do_ref_completion( | ||
699 | r" | ||
700 | macro_rules! m { ($e:expr) => { $e } } | ||
701 | struct A { the_field: u32 } | ||
702 | fn foo(a: A) { | ||
703 | m!(m!(m!(a.x<|>))) | ||
704 | } | ||
705 | ", | ||
706 | ), | ||
707 | @r###" | ||
708 | [ | ||
709 | CompletionItem { | ||
710 | label: "the_field", | ||
711 | source_range: [162; 163), | ||
712 | delete: [162; 163), | ||
713 | insert: "the_field", | ||
714 | kind: Field, | ||
715 | detail: "u32", | ||
716 | }, | ||
717 | ] | ||
718 | "### | ||
719 | ); | ||
720 | } | ||
623 | } | 721 | } |
diff --git a/crates/ra_ide/src/completion/complete_keyword.rs b/crates/ra_ide/src/completion/complete_keyword.rs index eb7cd9ac2..e1c0ffb1f 100644 --- a/crates/ra_ide/src/completion/complete_keyword.rs +++ b/crates/ra_ide/src/completion/complete_keyword.rs | |||
@@ -79,6 +79,7 @@ pub(super) fn complete_expr_keyword(acc: &mut Completions, ctx: &CompletionConte | |||
79 | } | 79 | } |
80 | 80 | ||
81 | fn is_in_loop_body(leaf: &SyntaxToken) -> bool { | 81 | fn is_in_loop_body(leaf: &SyntaxToken) -> bool { |
82 | // FIXME move this to CompletionContext and make it handle macros | ||
82 | for node in leaf.parent().ancestors() { | 83 | for node in leaf.parent().ancestors() { |
83 | if node.kind() == FN_DEF || node.kind() == LAMBDA_EXPR { | 84 | if node.kind() == FN_DEF || node.kind() == LAMBDA_EXPR { |
84 | break; | 85 | break; |
diff --git a/crates/ra_ide/src/completion/complete_path.rs b/crates/ra_ide/src/completion/complete_path.rs index d2c758571..3c4a70561 100644 --- a/crates/ra_ide/src/completion/complete_path.rs +++ b/crates/ra_ide/src/completion/complete_path.rs | |||
@@ -1,4 +1,4 @@ | |||
1 | //! Completion of paths, including when writing a single name. | 1 | //! Completion of paths, i.e. `some::prefix::<|>`. |
2 | 2 | ||
3 | use hir::{Adt, HasVisibility, PathResolution, ScopeDef}; | 3 | use hir::{Adt, HasVisibility, PathResolution, ScopeDef}; |
4 | use ra_syntax::AstNode; | 4 | use ra_syntax::AstNode; |
@@ -48,7 +48,7 @@ pub(super) fn complete_path(acc: &mut Completions, ctx: &CompletionContext) { | |||
48 | }; | 48 | }; |
49 | // Iterate assoc types separately | 49 | // Iterate assoc types separately |
50 | // FIXME: complete T::AssocType | 50 | // FIXME: complete T::AssocType |
51 | let krate = ctx.module.map(|m| m.krate()); | 51 | let krate = ctx.krate; |
52 | if let Some(krate) = krate { | 52 | if let Some(krate) = krate { |
53 | let traits_in_scope = ctx.scope().traits_in_scope(); | 53 | let traits_in_scope = ctx.scope().traits_in_scope(); |
54 | ty.iterate_path_candidates(ctx.db, krate, &traits_in_scope, None, |_ty, item| { | 54 | ty.iterate_path_candidates(ctx.db, krate, &traits_in_scope, None, |_ty, item| { |
@@ -934,4 +934,37 @@ mod tests { | |||
934 | "### | 934 | "### |
935 | ); | 935 | ); |
936 | } | 936 | } |
937 | |||
938 | #[test] | ||
939 | fn completes_in_simple_macro_call() { | ||
940 | let completions = do_reference_completion( | ||
941 | r#" | ||
942 | macro_rules! m { ($e:expr) => { $e } } | ||
943 | fn main() { m!(self::f<|>); } | ||
944 | fn foo() {} | ||
945 | "#, | ||
946 | ); | ||
947 | assert_debug_snapshot!(completions, @r###" | ||
948 | [ | ||
949 | CompletionItem { | ||
950 | label: "foo()", | ||
951 | source_range: [93; 94), | ||
952 | delete: [93; 94), | ||
953 | insert: "foo()$0", | ||
954 | kind: Function, | ||
955 | lookup: "foo", | ||
956 | detail: "fn foo()", | ||
957 | }, | ||
958 | CompletionItem { | ||
959 | label: "main()", | ||
960 | source_range: [93; 94), | ||
961 | delete: [93; 94), | ||
962 | insert: "main()$0", | ||
963 | kind: Function, | ||
964 | lookup: "main", | ||
965 | detail: "fn main()", | ||
966 | }, | ||
967 | ] | ||
968 | "###); | ||
969 | } | ||
937 | } | 970 | } |
diff --git a/crates/ra_ide/src/completion/complete_pattern.rs b/crates/ra_ide/src/completion/complete_pattern.rs index c2c6ca002..fa8aeceda 100644 --- a/crates/ra_ide/src/completion/complete_pattern.rs +++ b/crates/ra_ide/src/completion/complete_pattern.rs | |||
@@ -86,4 +86,22 @@ mod tests { | |||
86 | ] | 86 | ] |
87 | "###); | 87 | "###); |
88 | } | 88 | } |
89 | |||
90 | #[test] | ||
91 | fn completes_in_simple_macro_call() { | ||
92 | // FIXME: doesn't work yet because of missing error recovery in macro expansion | ||
93 | let completions = complete( | ||
94 | r" | ||
95 | macro_rules! m { ($e:expr) => { $e } } | ||
96 | enum E { X } | ||
97 | |||
98 | fn foo() { | ||
99 | m!(match E::X { | ||
100 | <|> | ||
101 | }) | ||
102 | } | ||
103 | ", | ||
104 | ); | ||
105 | assert_debug_snapshot!(completions, @r###"[]"###); | ||
106 | } | ||
89 | } | 107 | } |
diff --git a/crates/ra_ide/src/completion/complete_postfix.rs b/crates/ra_ide/src/completion/complete_postfix.rs index 8a74f993a..65ecea125 100644 --- a/crates/ra_ide/src/completion/complete_postfix.rs +++ b/crates/ra_ide/src/completion/complete_postfix.rs | |||
@@ -67,8 +67,8 @@ pub(super) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) { | |||
67 | 67 | ||
68 | fn postfix_snippet(ctx: &CompletionContext, label: &str, detail: &str, snippet: &str) -> Builder { | 68 | fn postfix_snippet(ctx: &CompletionContext, label: &str, detail: &str, snippet: &str) -> Builder { |
69 | let edit = { | 69 | let edit = { |
70 | let receiver_range = | 70 | let receiver_syntax = ctx.dot_receiver.as_ref().expect("no receiver available").syntax(); |
71 | ctx.dot_receiver.as_ref().expect("no receiver available").syntax().text_range(); | 71 | let receiver_range = ctx.sema.original_range(receiver_syntax).range; |
72 | let delete_range = TextRange::from_to(receiver_range.start(), ctx.source_range().end()); | 72 | let delete_range = TextRange::from_to(receiver_range.start(), ctx.source_range().end()); |
73 | TextEdit::replace(delete_range, snippet.to_string()) | 73 | TextEdit::replace(delete_range, snippet.to_string()) |
74 | }; | 74 | }; |
@@ -279,4 +279,65 @@ mod tests { | |||
279 | "### | 279 | "### |
280 | ); | 280 | ); |
281 | } | 281 | } |
282 | |||
283 | #[test] | ||
284 | fn works_in_simple_macro() { | ||
285 | assert_debug_snapshot!( | ||
286 | do_postfix_completion( | ||
287 | r#" | ||
288 | macro_rules! m { ($e:expr) => { $e } } | ||
289 | fn main() { | ||
290 | let bar: u8 = 12; | ||
291 | m!(bar.b<|>) | ||
292 | } | ||
293 | "#, | ||
294 | ), | ||
295 | @r###" | ||
296 | [ | ||
297 | CompletionItem { | ||
298 | label: "box", | ||
299 | source_range: [149; 150), | ||
300 | delete: [145; 150), | ||
301 | insert: "Box::new(bar)", | ||
302 | detail: "Box::new(expr)", | ||
303 | }, | ||
304 | CompletionItem { | ||
305 | label: "dbg", | ||
306 | source_range: [149; 150), | ||
307 | delete: [145; 150), | ||
308 | insert: "dbg!(bar)", | ||
309 | detail: "dbg!(expr)", | ||
310 | }, | ||
311 | CompletionItem { | ||
312 | label: "match", | ||
313 | source_range: [149; 150), | ||
314 | delete: [145; 150), | ||
315 | insert: "match bar {\n ${1:_} => {$0\\},\n}", | ||
316 | detail: "match expr {}", | ||
317 | }, | ||
318 | CompletionItem { | ||
319 | label: "not", | ||
320 | source_range: [149; 150), | ||
321 | delete: [145; 150), | ||
322 | insert: "!bar", | ||
323 | detail: "!expr", | ||
324 | }, | ||
325 | CompletionItem { | ||
326 | label: "ref", | ||
327 | source_range: [149; 150), | ||
328 | delete: [145; 150), | ||
329 | insert: "&bar", | ||
330 | detail: "&expr", | ||
331 | }, | ||
332 | CompletionItem { | ||
333 | label: "refm", | ||
334 | source_range: [149; 150), | ||
335 | delete: [145; 150), | ||
336 | insert: "&mut bar", | ||
337 | detail: "&mut expr", | ||
338 | }, | ||
339 | ] | ||
340 | "### | ||
341 | ); | ||
342 | } | ||
282 | } | 343 | } |
diff --git a/crates/ra_ide/src/completion/complete_record_literal.rs b/crates/ra_ide/src/completion/complete_record_literal.rs index f98353d76..be6e4194f 100644 --- a/crates/ra_ide/src/completion/complete_record_literal.rs +++ b/crates/ra_ide/src/completion/complete_record_literal.rs | |||
@@ -153,4 +153,29 @@ mod tests { | |||
153 | ] | 153 | ] |
154 | "###); | 154 | "###); |
155 | } | 155 | } |
156 | |||
157 | #[test] | ||
158 | fn test_record_literal_field_in_simple_macro() { | ||
159 | let completions = complete( | ||
160 | r" | ||
161 | macro_rules! m { ($e:expr) => { $e } } | ||
162 | struct A { the_field: u32 } | ||
163 | fn foo() { | ||
164 | m!(A { the<|> }) | ||
165 | } | ||
166 | ", | ||
167 | ); | ||
168 | assert_debug_snapshot!(completions, @r###" | ||
169 | [ | ||
170 | CompletionItem { | ||
171 | label: "the_field", | ||
172 | source_range: [137; 140), | ||
173 | delete: [137; 140), | ||
174 | insert: "the_field", | ||
175 | kind: Field, | ||
176 | detail: "u32", | ||
177 | }, | ||
178 | ] | ||
179 | "###); | ||
180 | } | ||
156 | } | 181 | } |
diff --git a/crates/ra_ide/src/completion/complete_record_pattern.rs b/crates/ra_ide/src/completion/complete_record_pattern.rs index 9bdeae49f..687c57d3e 100644 --- a/crates/ra_ide/src/completion/complete_record_pattern.rs +++ b/crates/ra_ide/src/completion/complete_record_pattern.rs | |||
@@ -87,4 +87,32 @@ mod tests { | |||
87 | ] | 87 | ] |
88 | "###); | 88 | "###); |
89 | } | 89 | } |
90 | |||
91 | #[test] | ||
92 | fn test_record_pattern_field_in_simple_macro() { | ||
93 | let completions = complete( | ||
94 | r" | ||
95 | macro_rules! m { ($e:expr) => { $e } } | ||
96 | struct S { foo: u32 } | ||
97 | |||
98 | fn process(f: S) { | ||
99 | m!(match f { | ||
100 | S { f<|>: 92 } => (), | ||
101 | }) | ||
102 | } | ||
103 | ", | ||
104 | ); | ||
105 | assert_debug_snapshot!(completions, @r###" | ||
106 | [ | ||
107 | CompletionItem { | ||
108 | label: "foo", | ||
109 | source_range: [171; 172), | ||
110 | delete: [171; 172), | ||
111 | insert: "foo", | ||
112 | kind: Field, | ||
113 | detail: "u32", | ||
114 | }, | ||
115 | ] | ||
116 | "###); | ||
117 | } | ||
90 | } | 118 | } |
diff --git a/crates/ra_ide/src/completion/complete_scope.rs b/crates/ra_ide/src/completion/complete_scope.rs index 2b9a0e556..eb3c8cf1b 100644 --- a/crates/ra_ide/src/completion/complete_scope.rs +++ b/crates/ra_ide/src/completion/complete_scope.rs | |||
@@ -1,4 +1,4 @@ | |||
1 | //! FIXME: write short doc here | 1 | //! Completion of names from the current scope, e.g. locals and imported items. |
2 | 2 | ||
3 | use crate::completion::{CompletionContext, Completions}; | 3 | use crate::completion::{CompletionContext, Completions}; |
4 | 4 | ||
@@ -797,4 +797,72 @@ mod tests { | |||
797 | "### | 797 | "### |
798 | ) | 798 | ) |
799 | } | 799 | } |
800 | |||
801 | #[test] | ||
802 | fn completes_in_simple_macro_1() { | ||
803 | assert_debug_snapshot!( | ||
804 | do_reference_completion( | ||
805 | r" | ||
806 | macro_rules! m { ($e:expr) => { $e } } | ||
807 | fn quux(x: i32) { | ||
808 | let y = 92; | ||
809 | m!(<|>); | ||
810 | } | ||
811 | " | ||
812 | ), | ||
813 | @"[]" | ||
814 | ); | ||
815 | } | ||
816 | |||
817 | #[test] | ||
818 | fn completes_in_simple_macro_2() { | ||
819 | assert_debug_snapshot!( | ||
820 | do_reference_completion( | ||
821 | r" | ||
822 | macro_rules! m { ($e:expr) => { $e } } | ||
823 | fn quux(x: i32) { | ||
824 | let y = 92; | ||
825 | m!(x<|>); | ||
826 | } | ||
827 | " | ||
828 | ), | ||
829 | @r###" | ||
830 | [ | ||
831 | CompletionItem { | ||
832 | label: "m!", | ||
833 | source_range: [145; 146), | ||
834 | delete: [145; 146), | ||
835 | insert: "m!($0)", | ||
836 | kind: Macro, | ||
837 | detail: "macro_rules! m", | ||
838 | }, | ||
839 | CompletionItem { | ||
840 | label: "quux(…)", | ||
841 | source_range: [145; 146), | ||
842 | delete: [145; 146), | ||
843 | insert: "quux(${1:x})$0", | ||
844 | kind: Function, | ||
845 | lookup: "quux", | ||
846 | detail: "fn quux(x: i32)", | ||
847 | }, | ||
848 | CompletionItem { | ||
849 | label: "x", | ||
850 | source_range: [145; 146), | ||
851 | delete: [145; 146), | ||
852 | insert: "x", | ||
853 | kind: Binding, | ||
854 | detail: "i32", | ||
855 | }, | ||
856 | CompletionItem { | ||
857 | label: "y", | ||
858 | source_range: [145; 146), | ||
859 | delete: [145; 146), | ||
860 | insert: "y", | ||
861 | kind: Binding, | ||
862 | detail: "i32", | ||
863 | }, | ||
864 | ] | ||
865 | "### | ||
866 | ); | ||
867 | } | ||
800 | } | 868 | } |
diff --git a/crates/ra_ide/src/completion/completion_context.rs b/crates/ra_ide/src/completion/completion_context.rs index 9aa5a705d..40535c09e 100644 --- a/crates/ra_ide/src/completion/completion_context.rs +++ b/crates/ra_ide/src/completion/completion_context.rs | |||
@@ -5,7 +5,7 @@ use ra_db::SourceDatabase; | |||
5 | use ra_ide_db::RootDatabase; | 5 | use ra_ide_db::RootDatabase; |
6 | use ra_syntax::{ | 6 | use ra_syntax::{ |
7 | algo::{find_covering_element, find_node_at_offset}, | 7 | algo::{find_covering_element, find_node_at_offset}, |
8 | ast, AstNode, SourceFile, | 8 | ast, AstNode, |
9 | SyntaxKind::*, | 9 | SyntaxKind::*, |
10 | SyntaxNode, SyntaxToken, TextRange, TextUnit, | 10 | SyntaxNode, SyntaxToken, TextRange, TextUnit, |
11 | }; | 11 | }; |
@@ -20,8 +20,11 @@ pub(crate) struct CompletionContext<'a> { | |||
20 | pub(super) sema: Semantics<'a, RootDatabase>, | 20 | pub(super) sema: Semantics<'a, RootDatabase>, |
21 | pub(super) db: &'a RootDatabase, | 21 | pub(super) db: &'a RootDatabase, |
22 | pub(super) offset: TextUnit, | 22 | pub(super) offset: TextUnit, |
23 | /// The token before the cursor, in the original file. | ||
24 | pub(super) original_token: SyntaxToken, | ||
25 | /// The token before the cursor, in the macro-expanded file. | ||
23 | pub(super) token: SyntaxToken, | 26 | pub(super) token: SyntaxToken, |
24 | pub(super) module: Option<hir::Module>, | 27 | pub(super) krate: Option<hir::Crate>, |
25 | pub(super) name_ref_syntax: Option<ast::NameRef>, | 28 | pub(super) name_ref_syntax: Option<ast::NameRef>, |
26 | pub(super) function_syntax: Option<ast::FnDef>, | 29 | pub(super) function_syntax: Option<ast::FnDef>, |
27 | pub(super) use_item_syntax: Option<ast::UseItem>, | 30 | pub(super) use_item_syntax: Option<ast::UseItem>, |
@@ -67,15 +70,20 @@ impl<'a> CompletionContext<'a> { | |||
67 | let edit = AtomTextEdit::insert(position.offset, "intellijRulezz".to_string()); | 70 | let edit = AtomTextEdit::insert(position.offset, "intellijRulezz".to_string()); |
68 | parse.reparse(&edit).tree() | 71 | parse.reparse(&edit).tree() |
69 | }; | 72 | }; |
73 | let fake_ident_token = | ||
74 | file_with_fake_ident.syntax().token_at_offset(position.offset).right_biased().unwrap(); | ||
70 | 75 | ||
71 | let module = sema.to_module_def(position.file_id); | 76 | let krate = sema.to_module_def(position.file_id).map(|m| m.krate()); |
72 | let token = original_file.syntax().token_at_offset(position.offset).left_biased()?; | 77 | let original_token = |
78 | original_file.syntax().token_at_offset(position.offset).left_biased()?; | ||
79 | let token = sema.descend_into_macros(original_token.clone()); | ||
73 | let mut ctx = CompletionContext { | 80 | let mut ctx = CompletionContext { |
74 | sema, | 81 | sema, |
75 | db, | 82 | db, |
83 | original_token, | ||
76 | token, | 84 | token, |
77 | offset: position.offset, | 85 | offset: position.offset, |
78 | module, | 86 | krate, |
79 | name_ref_syntax: None, | 87 | name_ref_syntax: None, |
80 | function_syntax: None, | 88 | function_syntax: None, |
81 | use_item_syntax: None, | 89 | use_item_syntax: None, |
@@ -95,15 +103,57 @@ impl<'a> CompletionContext<'a> { | |||
95 | has_type_args: false, | 103 | has_type_args: false, |
96 | dot_receiver_is_ambiguous_float_literal: false, | 104 | dot_receiver_is_ambiguous_float_literal: false, |
97 | }; | 105 | }; |
98 | ctx.fill(&original_file, file_with_fake_ident, position.offset); | 106 | |
107 | let mut original_file = original_file.syntax().clone(); | ||
108 | let mut hypothetical_file = file_with_fake_ident.syntax().clone(); | ||
109 | let mut offset = position.offset; | ||
110 | let mut fake_ident_token = fake_ident_token; | ||
111 | |||
112 | // Are we inside a macro call? | ||
113 | while let (Some(actual_macro_call), Some(macro_call_with_fake_ident)) = ( | ||
114 | find_node_at_offset::<ast::MacroCall>(&original_file, offset), | ||
115 | find_node_at_offset::<ast::MacroCall>(&hypothetical_file, offset), | ||
116 | ) { | ||
117 | if actual_macro_call.path().as_ref().map(|s| s.syntax().text()) | ||
118 | != macro_call_with_fake_ident.path().as_ref().map(|s| s.syntax().text()) | ||
119 | { | ||
120 | break; | ||
121 | } | ||
122 | let hypothetical_args = match macro_call_with_fake_ident.token_tree() { | ||
123 | Some(tt) => tt, | ||
124 | None => break, | ||
125 | }; | ||
126 | if let (Some(actual_expansion), Some(hypothetical_expansion)) = ( | ||
127 | ctx.sema.expand(&actual_macro_call), | ||
128 | ctx.sema.expand_hypothetical( | ||
129 | &actual_macro_call, | ||
130 | &hypothetical_args, | ||
131 | fake_ident_token, | ||
132 | ), | ||
133 | ) { | ||
134 | let new_offset = hypothetical_expansion.1.text_range().start(); | ||
135 | if new_offset >= actual_expansion.text_range().end() { | ||
136 | break; | ||
137 | } | ||
138 | original_file = actual_expansion; | ||
139 | hypothetical_file = hypothetical_expansion.0; | ||
140 | fake_ident_token = hypothetical_expansion.1; | ||
141 | offset = new_offset; | ||
142 | } else { | ||
143 | break; | ||
144 | } | ||
145 | } | ||
146 | |||
147 | ctx.fill(&original_file, hypothetical_file, offset); | ||
99 | Some(ctx) | 148 | Some(ctx) |
100 | } | 149 | } |
101 | 150 | ||
102 | // The range of the identifier that is being completed. | 151 | // The range of the identifier that is being completed. |
103 | pub(crate) fn source_range(&self) -> TextRange { | 152 | pub(crate) fn source_range(&self) -> TextRange { |
153 | // check kind of macro-expanded token, but use range of original token | ||
104 | match self.token.kind() { | 154 | match self.token.kind() { |
105 | // workaroud when completion is triggered by trigger characters. | 155 | // workaroud when completion is triggered by trigger characters. |
106 | IDENT => self.token.text_range(), | 156 | IDENT => self.original_token.text_range(), |
107 | _ => TextRange::offset_len(self.offset, 0.into()), | 157 | _ => TextRange::offset_len(self.offset, 0.into()), |
108 | } | 158 | } |
109 | } | 159 | } |
@@ -114,27 +164,24 @@ impl<'a> CompletionContext<'a> { | |||
114 | 164 | ||
115 | fn fill( | 165 | fn fill( |
116 | &mut self, | 166 | &mut self, |
117 | original_file: &ast::SourceFile, | 167 | original_file: &SyntaxNode, |
118 | file_with_fake_ident: ast::SourceFile, | 168 | file_with_fake_ident: SyntaxNode, |
119 | offset: TextUnit, | 169 | offset: TextUnit, |
120 | ) { | 170 | ) { |
121 | // First, let's try to complete a reference to some declaration. | 171 | // First, let's try to complete a reference to some declaration. |
122 | if let Some(name_ref) = | 172 | if let Some(name_ref) = find_node_at_offset::<ast::NameRef>(&file_with_fake_ident, offset) { |
123 | find_node_at_offset::<ast::NameRef>(file_with_fake_ident.syntax(), offset) | ||
124 | { | ||
125 | // Special case, `trait T { fn foo(i_am_a_name_ref) {} }`. | 173 | // Special case, `trait T { fn foo(i_am_a_name_ref) {} }`. |
126 | // See RFC#1685. | 174 | // See RFC#1685. |
127 | if is_node::<ast::Param>(name_ref.syntax()) { | 175 | if is_node::<ast::Param>(name_ref.syntax()) { |
128 | self.is_param = true; | 176 | self.is_param = true; |
129 | return; | 177 | return; |
130 | } | 178 | } |
131 | self.classify_name_ref(original_file, name_ref); | 179 | self.classify_name_ref(original_file, name_ref, offset); |
132 | } | 180 | } |
133 | 181 | ||
134 | // Otherwise, see if this is a declaration. We can use heuristics to | 182 | // Otherwise, see if this is a declaration. We can use heuristics to |
135 | // suggest declaration names, see `CompletionKind::Magic`. | 183 | // suggest declaration names, see `CompletionKind::Magic`. |
136 | if let Some(name) = find_node_at_offset::<ast::Name>(file_with_fake_ident.syntax(), offset) | 184 | if let Some(name) = find_node_at_offset::<ast::Name>(&file_with_fake_ident, offset) { |
137 | { | ||
138 | if let Some(bind_pat) = name.syntax().ancestors().find_map(ast::BindPat::cast) { | 185 | if let Some(bind_pat) = name.syntax().ancestors().find_map(ast::BindPat::cast) { |
139 | let parent = bind_pat.syntax().parent(); | 186 | let parent = bind_pat.syntax().parent(); |
140 | if parent.clone().and_then(ast::MatchArm::cast).is_some() | 187 | if parent.clone().and_then(ast::MatchArm::cast).is_some() |
@@ -148,23 +195,29 @@ impl<'a> CompletionContext<'a> { | |||
148 | return; | 195 | return; |
149 | } | 196 | } |
150 | if name.syntax().ancestors().find_map(ast::RecordFieldPatList::cast).is_some() { | 197 | if name.syntax().ancestors().find_map(ast::RecordFieldPatList::cast).is_some() { |
151 | self.record_lit_pat = find_node_at_offset(original_file.syntax(), self.offset); | 198 | self.record_lit_pat = |
199 | self.sema.find_node_at_offset_with_macros(&original_file, offset); | ||
152 | } | 200 | } |
153 | } | 201 | } |
154 | } | 202 | } |
155 | 203 | ||
156 | fn classify_name_ref(&mut self, original_file: &SourceFile, name_ref: ast::NameRef) { | 204 | fn classify_name_ref( |
205 | &mut self, | ||
206 | original_file: &SyntaxNode, | ||
207 | name_ref: ast::NameRef, | ||
208 | offset: TextUnit, | ||
209 | ) { | ||
157 | self.name_ref_syntax = | 210 | self.name_ref_syntax = |
158 | find_node_at_offset(original_file.syntax(), name_ref.syntax().text_range().start()); | 211 | find_node_at_offset(&original_file, name_ref.syntax().text_range().start()); |
159 | let name_range = name_ref.syntax().text_range(); | 212 | let name_range = name_ref.syntax().text_range(); |
160 | if name_ref.syntax().parent().and_then(ast::RecordField::cast).is_some() { | 213 | if name_ref.syntax().parent().and_then(ast::RecordField::cast).is_some() { |
161 | self.record_lit_syntax = find_node_at_offset(original_file.syntax(), self.offset); | 214 | self.record_lit_syntax = |
215 | self.sema.find_node_at_offset_with_macros(&original_file, offset); | ||
162 | } | 216 | } |
163 | 217 | ||
164 | self.impl_def = self | 218 | self.impl_def = self |
165 | .token | 219 | .sema |
166 | .parent() | 220 | .ancestors_with_macros(self.token.parent()) |
167 | .ancestors() | ||
168 | .take_while(|it| it.kind() != SOURCE_FILE && it.kind() != MODULE) | 221 | .take_while(|it| it.kind() != SOURCE_FILE && it.kind() != MODULE) |
169 | .find_map(ast::ImplDef::cast); | 222 | .find_map(ast::ImplDef::cast); |
170 | 223 | ||
@@ -183,12 +236,12 @@ impl<'a> CompletionContext<'a> { | |||
183 | _ => (), | 236 | _ => (), |
184 | } | 237 | } |
185 | 238 | ||
186 | self.use_item_syntax = self.token.parent().ancestors().find_map(ast::UseItem::cast); | 239 | self.use_item_syntax = |
240 | self.sema.ancestors_with_macros(self.token.parent()).find_map(ast::UseItem::cast); | ||
187 | 241 | ||
188 | self.function_syntax = self | 242 | self.function_syntax = self |
189 | .token | 243 | .sema |
190 | .parent() | 244 | .ancestors_with_macros(self.token.parent()) |
191 | .ancestors() | ||
192 | .take_while(|it| it.kind() != SOURCE_FILE && it.kind() != MODULE) | 245 | .take_while(|it| it.kind() != SOURCE_FILE && it.kind() != MODULE) |
193 | .find_map(ast::FnDef::cast); | 246 | .find_map(ast::FnDef::cast); |
194 | 247 | ||
@@ -242,7 +295,7 @@ impl<'a> CompletionContext<'a> { | |||
242 | 295 | ||
243 | if let Some(off) = name_ref.syntax().text_range().start().checked_sub(2.into()) { | 296 | if let Some(off) = name_ref.syntax().text_range().start().checked_sub(2.into()) { |
244 | if let Some(if_expr) = | 297 | if let Some(if_expr) = |
245 | find_node_at_offset::<ast::IfExpr>(original_file.syntax(), off) | 298 | self.sema.find_node_at_offset_with_macros::<ast::IfExpr>(original_file, off) |
246 | { | 299 | { |
247 | if if_expr.syntax().text_range().end() | 300 | if if_expr.syntax().text_range().end() |
248 | < name_ref.syntax().text_range().start() | 301 | < name_ref.syntax().text_range().start() |
@@ -259,7 +312,7 @@ impl<'a> CompletionContext<'a> { | |||
259 | self.dot_receiver = field_expr | 312 | self.dot_receiver = field_expr |
260 | .expr() | 313 | .expr() |
261 | .map(|e| e.syntax().text_range()) | 314 | .map(|e| e.syntax().text_range()) |
262 | .and_then(|r| find_node_with_range(original_file.syntax(), r)); | 315 | .and_then(|r| find_node_with_range(original_file, r)); |
263 | self.dot_receiver_is_ambiguous_float_literal = | 316 | self.dot_receiver_is_ambiguous_float_literal = |
264 | if let Some(ast::Expr::Literal(l)) = &self.dot_receiver { | 317 | if let Some(ast::Expr::Literal(l)) = &self.dot_receiver { |
265 | match l.kind() { | 318 | match l.kind() { |
@@ -275,7 +328,7 @@ impl<'a> CompletionContext<'a> { | |||
275 | self.dot_receiver = method_call_expr | 328 | self.dot_receiver = method_call_expr |
276 | .expr() | 329 | .expr() |
277 | .map(|e| e.syntax().text_range()) | 330 | .map(|e| e.syntax().text_range()) |
278 | .and_then(|r| find_node_with_range(original_file.syntax(), r)); | 331 | .and_then(|r| find_node_with_range(original_file, r)); |
279 | self.is_call = true; | 332 | self.is_call = true; |
280 | } | 333 | } |
281 | } | 334 | } |
diff --git a/crates/ra_mbe/src/mbe_expander/matcher.rs b/crates/ra_mbe/src/mbe_expander/matcher.rs index ffba03898..49c53183a 100644 --- a/crates/ra_mbe/src/mbe_expander/matcher.rs +++ b/crates/ra_mbe/src/mbe_expander/matcher.rs | |||
@@ -247,6 +247,7 @@ impl<'a> TtIter<'a> { | |||
247 | ra_parser::parse_fragment(&mut src, &mut sink, fragment_kind); | 247 | ra_parser::parse_fragment(&mut src, &mut sink, fragment_kind); |
248 | 248 | ||
249 | if !sink.cursor.is_root() || sink.error { | 249 | if !sink.cursor.is_root() || sink.error { |
250 | // FIXME better recovery in this case would help completion inside macros immensely | ||
250 | return Err(()); | 251 | return Err(()); |
251 | } | 252 | } |
252 | 253 | ||
@@ -375,7 +376,8 @@ fn match_meta_var(kind: &str, input: &mut TtIter) -> Result<Option<Fragment>, Ex | |||
375 | return Ok(Some(Fragment::Tokens(tt))); | 376 | return Ok(Some(Fragment::Tokens(tt))); |
376 | } | 377 | } |
377 | }; | 378 | }; |
378 | let tt = input.expect_fragment(fragment).map_err(|()| err!())?; | 379 | let tt = |
380 | input.expect_fragment(fragment).map_err(|()| err!("fragment did not parse as {}", kind))?; | ||
379 | let fragment = if kind == "expr" { Fragment::Ast(tt) } else { Fragment::Tokens(tt) }; | 381 | let fragment = if kind == "expr" { Fragment::Ast(tt) } else { Fragment::Tokens(tt) }; |
380 | Ok(Some(fragment)) | 382 | Ok(Some(fragment)) |
381 | } | 383 | } |