diff options
Diffstat (limited to 'crates')
-rw-r--r-- | crates/ra_hir/Cargo.toml | 2 | ||||
-rw-r--r-- | crates/ra_hir/src/semantics.rs | 55 | ||||
-rw-r--r-- | crates/ra_hir_expand/src/db.rs | 53 | ||||
-rw-r--r-- | crates/ra_hir_expand/src/lib.rs | 9 | ||||
-rw-r--r-- | crates/ra_ide/src/completion/complete_dot.rs | 98 | ||||
-rw-r--r-- | crates/ra_ide/src/completion/complete_path.rs | 2 | ||||
-rw-r--r-- | crates/ra_ide/src/completion/complete_scope.rs | 70 | ||||
-rw-r--r-- | crates/ra_ide/src/completion/completion_context.rs | 87 |
8 files changed, 338 insertions, 38 deletions
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..56bd763c7 100644 --- a/crates/ra_hir/src/semantics.rs +++ b/crates/ra_hir/src/semantics.rs | |||
@@ -6,13 +6,14 @@ 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}; |
13 | use ra_prof::profile; | 13 | use ra_prof::profile; |
14 | use ra_syntax::{ | 14 | use ra_syntax::{ |
15 | algo::skip_trivia_token, ast, AstNode, Direction, SyntaxNode, SyntaxToken, TextRange, TextUnit, | 15 | algo::{self, skip_trivia_token}, |
16 | ast, AstNode, Direction, SyntaxNode, SyntaxToken, TextRange, TextUnit, | ||
16 | }; | 17 | }; |
17 | use rustc_hash::{FxHashMap, FxHashSet}; | 18 | use rustc_hash::{FxHashMap, FxHashSet}; |
18 | 19 | ||
@@ -70,6 +71,37 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> { | |||
70 | Some(node) | 71 | Some(node) |
71 | } | 72 | } |
72 | 73 | ||
74 | pub fn expand_hypothetical( | ||
75 | &self, | ||
76 | actual_macro_call: &ast::MacroCall, | ||
77 | hypothetical_call: &ast::MacroCall, | ||
78 | token_to_map: SyntaxToken, | ||
79 | ) -> Option<(SyntaxNode, SyntaxToken)> { | ||
80 | let macro_call = | ||
81 | self.find_file(actual_macro_call.syntax().clone()).with_value(actual_macro_call); | ||
82 | let sa = self.analyze2(macro_call.map(|it| it.syntax()), None); | ||
83 | let macro_call_id = macro_call | ||
84 | .as_call_id(self.db, |path| sa.resolver.resolve_path_as_macro(self.db, &path))?; | ||
85 | let macro_file = macro_call_id.as_file().macro_file().unwrap(); | ||
86 | let (tt, tmap_1) = | ||
87 | hir_expand::syntax_node_to_token_tree(hypothetical_call.token_tree().unwrap().syntax()) | ||
88 | .unwrap(); | ||
89 | let range = token_to_map | ||
90 | .text_range() | ||
91 | .checked_sub(hypothetical_call.token_tree().unwrap().syntax().text_range().start())?; | ||
92 | let token_id = tmap_1.token_by_range(range)?; | ||
93 | let macro_def = hir_expand::db::expander(self.db, macro_call_id)?; | ||
94 | let (node, tmap_2) = hir_expand::db::parse_macro_with_arg( | ||
95 | self.db, | ||
96 | macro_file, | ||
97 | Some(std::sync::Arc::new((tt, tmap_1))), | ||
98 | )?; | ||
99 | let token_id = macro_def.0.map_id_down(token_id); | ||
100 | let range = tmap_2.range_by_token(token_id)?.by_kind(token_to_map.kind())?; | ||
101 | let token = algo::find_covering_element(&node.syntax_node(), range).into_token()?; | ||
102 | Some((node.syntax_node(), token)) | ||
103 | } | ||
104 | |||
73 | pub fn descend_into_macros(&self, token: SyntaxToken) -> SyntaxToken { | 105 | pub fn descend_into_macros(&self, token: SyntaxToken) -> SyntaxToken { |
74 | let parent = token.parent(); | 106 | let parent = token.parent(); |
75 | let parent = self.find_file(parent); | 107 | let parent = self.find_file(parent); |
@@ -104,6 +136,25 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> { | |||
104 | node.ancestors_with_macros(self.db).map(|it| it.value) | 136 | node.ancestors_with_macros(self.db).map(|it| it.value) |
105 | } | 137 | } |
106 | 138 | ||
139 | pub fn ancestors_at_offset_with_macros( | ||
140 | &self, | ||
141 | node: &SyntaxNode, | ||
142 | offset: TextUnit, | ||
143 | ) -> impl Iterator<Item = SyntaxNode> + '_ { | ||
144 | use itertools::Itertools; | ||
145 | node.token_at_offset(offset) | ||
146 | .map(|token| self.ancestors_with_macros(token.parent())) | ||
147 | .kmerge_by(|node1, node2| node1.text_range().len() < node2.text_range().len()) | ||
148 | } | ||
149 | |||
150 | pub fn find_node_at_offset_with_macros<N: AstNode>( | ||
151 | &self, | ||
152 | node: &SyntaxNode, | ||
153 | offset: TextUnit, | ||
154 | ) -> Option<N> { | ||
155 | self.ancestors_at_offset_with_macros(node, offset).find_map(N::cast) | ||
156 | } | ||
157 | |||
107 | pub fn type_of_expr(&self, expr: &ast::Expr) -> Option<Type> { | 158 | pub fn type_of_expr(&self, expr: &ast::Expr) -> Option<Type> { |
108 | self.analyze(expr.syntax()).type_of(self.db, &expr) | 159 | self.analyze(expr.syntax()).type_of(self.db, &expr) |
109 | } | 160 | } |
diff --git a/crates/ra_hir_expand/src/db.rs b/crates/ra_hir_expand/src/db.rs index f3a84cacc..a7aa60fc9 100644 --- a/crates/ra_hir_expand/src/db.rs +++ b/crates/ra_hir_expand/src/db.rs | |||
@@ -130,15 +130,42 @@ pub(crate) fn macro_expand( | |||
130 | db: &dyn AstDatabase, | 130 | db: &dyn AstDatabase, |
131 | id: MacroCallId, | 131 | id: MacroCallId, |
132 | ) -> Result<Arc<tt::Subtree>, String> { | 132 | ) -> Result<Arc<tt::Subtree>, String> { |
133 | macro_expand_with_arg(db, id, None) | ||
134 | } | ||
135 | |||
136 | // TODO hack | ||
137 | pub fn expander( | ||
138 | db: &dyn AstDatabase, | ||
139 | id: MacroCallId, | ||
140 | ) -> Option<Arc<(TokenExpander, mbe::TokenMap)>> { | ||
141 | let lazy_id = match id { | ||
142 | MacroCallId::LazyMacro(id) => id, | ||
143 | MacroCallId::EagerMacro(_id) => { | ||
144 | // TODO | ||
145 | unimplemented!() | ||
146 | } | ||
147 | }; | ||
148 | |||
149 | let loc = db.lookup_intern_macro(lazy_id); | ||
150 | let macro_rules = db.macro_def(loc.def)?; | ||
151 | Some(macro_rules) | ||
152 | } | ||
153 | |||
154 | pub(crate) fn macro_expand_with_arg( | ||
155 | db: &dyn AstDatabase, | ||
156 | id: MacroCallId, | ||
157 | arg: Option<Arc<(tt::Subtree, mbe::TokenMap)>>, | ||
158 | ) -> Result<Arc<tt::Subtree>, String> { | ||
133 | let lazy_id = match id { | 159 | let lazy_id = match id { |
134 | MacroCallId::LazyMacro(id) => id, | 160 | MacroCallId::LazyMacro(id) => id, |
135 | MacroCallId::EagerMacro(id) => { | 161 | MacroCallId::EagerMacro(id) => { |
162 | // TODO | ||
136 | return Ok(db.lookup_intern_eager_expansion(id).subtree); | 163 | return Ok(db.lookup_intern_eager_expansion(id).subtree); |
137 | } | 164 | } |
138 | }; | 165 | }; |
139 | 166 | ||
140 | let loc = db.lookup_intern_macro(lazy_id); | 167 | 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")?; | 168 | let macro_arg = arg.or_else(|| db.macro_arg(id)).ok_or("Fail to args in to tt::TokenTree")?; |
142 | 169 | ||
143 | let macro_rules = db.macro_def(loc.def).ok_or("Fail to find macro definition")?; | 170 | 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))?; | 171 | let tt = macro_rules.0.expand(db, lazy_id, ¯o_arg.0).map_err(|err| format!("{:?}", err))?; |
@@ -163,11 +190,23 @@ pub(crate) fn parse_macro( | |||
163 | db: &dyn AstDatabase, | 190 | db: &dyn AstDatabase, |
164 | macro_file: MacroFile, | 191 | macro_file: MacroFile, |
165 | ) -> Option<(Parse<SyntaxNode>, Arc<mbe::TokenMap>)> { | 192 | ) -> Option<(Parse<SyntaxNode>, Arc<mbe::TokenMap>)> { |
193 | parse_macro_with_arg(db, macro_file, None) | ||
194 | } | ||
195 | |||
196 | pub fn parse_macro_with_arg( | ||
197 | db: &dyn AstDatabase, | ||
198 | macro_file: MacroFile, | ||
199 | arg: Option<Arc<(tt::Subtree, mbe::TokenMap)>>, | ||
200 | ) -> Option<(Parse<SyntaxNode>, Arc<mbe::TokenMap>)> { | ||
166 | let _p = profile("parse_macro_query"); | 201 | let _p = profile("parse_macro_query"); |
167 | 202 | ||
168 | let macro_call_id = macro_file.macro_call_id; | 203 | let macro_call_id = macro_file.macro_call_id; |
169 | let tt = db | 204 | let expansion = if let Some(arg) = arg { |
170 | .macro_expand(macro_call_id) | 205 | macro_expand_with_arg(db, macro_call_id, Some(arg)) |
206 | } else { | ||
207 | db.macro_expand(macro_call_id) | ||
208 | }; | ||
209 | let tt = expansion | ||
171 | .map_err(|err| { | 210 | .map_err(|err| { |
172 | // Note: | 211 | // Note: |
173 | // The final goal we would like to make all parse_macro success, | 212 | // The final goal we would like to make all parse_macro success, |
@@ -185,15 +224,13 @@ pub(crate) fn parse_macro( | |||
185 | .collect::<Vec<_>>() | 224 | .collect::<Vec<_>>() |
186 | .join("\n"); | 225 | .join("\n"); |
187 | 226 | ||
188 | log::warn!( | 227 | eprintln!( |
189 | "fail on macro_parse: (reason: {} macro_call: {:#}) parents: {}", | 228 | "fail on macro_parse: (reason: {} macro_call: {:#}) parents: {}", |
190 | err, | 229 | err, node.value, parents |
191 | node.value, | ||
192 | parents | ||
193 | ); | 230 | ); |
194 | } | 231 | } |
195 | _ => { | 232 | _ => { |
196 | log::warn!("fail on macro_parse: (reason: {})", err); | 233 | eprintln!("fail on macro_parse: (reason: {})", err); |
197 | } | 234 | } |
198 | } | 235 | } |
199 | }) | 236 | }) |
diff --git a/crates/ra_hir_expand/src/lib.rs b/crates/ra_hir_expand/src/lib.rs index 3fce73e8a..92f3902dd 100644 --- a/crates/ra_hir_expand/src/lib.rs +++ b/crates/ra_hir_expand/src/lib.rs | |||
@@ -157,6 +157,13 @@ impl HirFileId { | |||
157 | } | 157 | } |
158 | } | 158 | } |
159 | } | 159 | } |
160 | |||
161 | pub fn macro_file(self) -> Option<MacroFile> { | ||
162 | match self.0 { | ||
163 | HirFileIdRepr::FileId(_) => None, | ||
164 | HirFileIdRepr::MacroFile(m) => Some(m), | ||
165 | } | ||
166 | } | ||
160 | } | 167 | } |
161 | 168 | ||
162 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | 169 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |
@@ -296,7 +303,7 @@ pub struct ExpansionInfo { | |||
296 | exp_map: Arc<mbe::TokenMap>, | 303 | exp_map: Arc<mbe::TokenMap>, |
297 | } | 304 | } |
298 | 305 | ||
299 | pub use mbe::Origin; | 306 | pub use mbe::{syntax_node_to_token_tree, Origin}; |
300 | use ra_parser::FragmentKind; | 307 | use ra_parser::FragmentKind; |
301 | 308 | ||
302 | impl ExpansionInfo { | 309 | impl ExpansionInfo { |
diff --git a/crates/ra_ide/src/completion/complete_dot.rs b/crates/ra_ide/src/completion/complete_dot.rs index 9145aa183..da2c4c1ab 100644 --- a/crates/ra_ide/src/completion/complete_dot.rs +++ b/crates/ra_ide/src/completion/complete_dot.rs | |||
@@ -584,4 +584,102 @@ mod tests { | |||
584 | "### | 584 | "### |
585 | ); | 585 | ); |
586 | } | 586 | } |
587 | |||
588 | #[test] | ||
589 | fn works_in_simple_macro_1() { | ||
590 | assert_debug_snapshot!( | ||
591 | do_ref_completion( | ||
592 | r" | ||
593 | macro_rules! m { ($e:expr) => { $e } } | ||
594 | struct A { the_field: u32 } | ||
595 | fn foo(a: A) { | ||
596 | m!(a.x<|>) | ||
597 | } | ||
598 | ", | ||
599 | ), | ||
600 | @r###" | ||
601 | [ | ||
602 | CompletionItem { | ||
603 | label: "the_field", | ||
604 | source_range: [156; 157), | ||
605 | delete: [156; 157), | ||
606 | insert: "the_field", | ||
607 | kind: Field, | ||
608 | detail: "u32", | ||
609 | }, | ||
610 | ] | ||
611 | "### | ||
612 | ); | ||
613 | } | ||
614 | |||
615 | #[test] | ||
616 | fn works_in_simple_macro_recursive() { | ||
617 | assert_debug_snapshot!( | ||
618 | do_ref_completion( | ||
619 | r" | ||
620 | macro_rules! m { ($e:expr) => { $e } } | ||
621 | struct A { the_field: u32 } | ||
622 | fn foo(a: A) { | ||
623 | m!(a.x<|>) | ||
624 | } | ||
625 | ", | ||
626 | ), | ||
627 | @r###" | ||
628 | [ | ||
629 | CompletionItem { | ||
630 | label: "the_field", | ||
631 | source_range: [156; 157), | ||
632 | delete: [156; 157), | ||
633 | insert: "the_field", | ||
634 | kind: Field, | ||
635 | detail: "u32", | ||
636 | }, | ||
637 | ] | ||
638 | "### | ||
639 | ); | ||
640 | } | ||
641 | |||
642 | #[test] | ||
643 | fn works_in_simple_macro_2() { | ||
644 | // this doesn't work yet because the macro doesn't expand without the token -- maybe it can be fixed with better recovery | ||
645 | assert_debug_snapshot!( | ||
646 | do_ref_completion( | ||
647 | r" | ||
648 | macro_rules! m { ($e:expr) => { $e } } | ||
649 | struct A { the_field: u32 } | ||
650 | fn foo(a: A) { | ||
651 | m!(a.<|>) | ||
652 | } | ||
653 | ", | ||
654 | ), | ||
655 | @r###"[]"### | ||
656 | ); | ||
657 | } | ||
658 | |||
659 | #[test] | ||
660 | fn works_in_simple_macro_recursive_1() { | ||
661 | assert_debug_snapshot!( | ||
662 | do_ref_completion( | ||
663 | r" | ||
664 | macro_rules! m { ($e:expr) => { $e } } | ||
665 | struct A { the_field: u32 } | ||
666 | fn foo(a: A) { | ||
667 | m!(m!(m!(a.x<|>))) | ||
668 | } | ||
669 | ", | ||
670 | ), | ||
671 | @r###" | ||
672 | [ | ||
673 | CompletionItem { | ||
674 | label: "the_field", | ||
675 | source_range: [162; 163), | ||
676 | delete: [162; 163), | ||
677 | insert: "the_field", | ||
678 | kind: Field, | ||
679 | detail: "u32", | ||
680 | }, | ||
681 | ] | ||
682 | "### | ||
683 | ); | ||
684 | } | ||
587 | } | 685 | } |
diff --git a/crates/ra_ide/src/completion/complete_path.rs b/crates/ra_ide/src/completion/complete_path.rs index 1a9699466..4fa47951a 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, PathResolution, ScopeDef}; | 3 | use hir::{Adt, PathResolution, ScopeDef}; |
4 | use ra_syntax::AstNode; | 4 | use ra_syntax::AstNode; |
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..81a033fcb 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,6 +20,9 @@ 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) module: Option<hir::Module>, |
25 | pub(super) name_ref_syntax: Option<ast::NameRef>, | 28 | pub(super) name_ref_syntax: Option<ast::NameRef>, |
@@ -67,12 +70,18 @@ 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 | ||
76 | // TODO: shouldn't this take the position into account? (in case we're inside a mod {}) | ||
71 | let module = sema.to_module_def(position.file_id); | 77 | let module = sema.to_module_def(position.file_id); |
72 | let token = original_file.syntax().token_at_offset(position.offset).left_biased()?; | 78 | let original_token = |
79 | original_file.syntax().token_at_offset(position.offset).left_biased()?; | ||
80 | let token = sema.descend_into_macros(original_token.clone()); | ||
73 | let mut ctx = CompletionContext { | 81 | let mut ctx = CompletionContext { |
74 | sema, | 82 | sema, |
75 | db, | 83 | db, |
84 | original_token, | ||
76 | token, | 85 | token, |
77 | offset: position.offset, | 86 | offset: position.offset, |
78 | module, | 87 | module, |
@@ -95,15 +104,45 @@ impl<'a> CompletionContext<'a> { | |||
95 | has_type_args: false, | 104 | has_type_args: false, |
96 | dot_receiver_is_ambiguous_float_literal: false, | 105 | dot_receiver_is_ambiguous_float_literal: false, |
97 | }; | 106 | }; |
98 | ctx.fill(&original_file, file_with_fake_ident, position.offset); | 107 | |
108 | let mut original_file = original_file.syntax().clone(); | ||
109 | let mut hypothetical_file = file_with_fake_ident.syntax().clone(); | ||
110 | let mut offset = position.offset; | ||
111 | let mut fake_ident_token = fake_ident_token; | ||
112 | |||
113 | // Are we inside a macro call? | ||
114 | while let (Some(actual_macro_call), Some(macro_call_with_fake_ident)) = ( | ||
115 | find_node_at_offset::<ast::MacroCall>(&original_file, offset), | ||
116 | find_node_at_offset::<ast::MacroCall>(&hypothetical_file, offset), | ||
117 | ) { | ||
118 | if let (Some(actual_expansion), Some(hypothetical_expansion)) = ( | ||
119 | ctx.sema.expand(&actual_macro_call), | ||
120 | ctx.sema.expand_hypothetical( | ||
121 | &actual_macro_call, | ||
122 | ¯o_call_with_fake_ident, | ||
123 | fake_ident_token, | ||
124 | ), | ||
125 | ) { | ||
126 | // TODO check that the expansions 'look the same' up to the inserted token? | ||
127 | original_file = actual_expansion; | ||
128 | hypothetical_file = hypothetical_expansion.0; | ||
129 | fake_ident_token = hypothetical_expansion.1; | ||
130 | offset = fake_ident_token.text_range().start(); | ||
131 | } else { | ||
132 | break; | ||
133 | } | ||
134 | } | ||
135 | |||
136 | ctx.fill(&original_file, hypothetical_file, offset); | ||
99 | Some(ctx) | 137 | Some(ctx) |
100 | } | 138 | } |
101 | 139 | ||
102 | // The range of the identifier that is being completed. | 140 | // The range of the identifier that is being completed. |
103 | pub(crate) fn source_range(&self) -> TextRange { | 141 | pub(crate) fn source_range(&self) -> TextRange { |
142 | // check kind of macro-expanded token, but use range of original token | ||
104 | match self.token.kind() { | 143 | match self.token.kind() { |
105 | // workaroud when completion is triggered by trigger characters. | 144 | // workaroud when completion is triggered by trigger characters. |
106 | IDENT => self.token.text_range(), | 145 | IDENT => self.original_token.text_range(), |
107 | _ => TextRange::offset_len(self.offset, 0.into()), | 146 | _ => TextRange::offset_len(self.offset, 0.into()), |
108 | } | 147 | } |
109 | } | 148 | } |
@@ -114,14 +153,12 @@ impl<'a> CompletionContext<'a> { | |||
114 | 153 | ||
115 | fn fill( | 154 | fn fill( |
116 | &mut self, | 155 | &mut self, |
117 | original_file: &ast::SourceFile, | 156 | original_file: &SyntaxNode, |
118 | file_with_fake_ident: ast::SourceFile, | 157 | file_with_fake_ident: SyntaxNode, |
119 | offset: TextUnit, | 158 | offset: TextUnit, |
120 | ) { | 159 | ) { |
121 | // First, let's try to complete a reference to some declaration. | 160 | // First, let's try to complete a reference to some declaration. |
122 | if let Some(name_ref) = | 161 | 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) {} }`. | 162 | // Special case, `trait T { fn foo(i_am_a_name_ref) {} }`. |
126 | // See RFC#1685. | 163 | // See RFC#1685. |
127 | if is_node::<ast::Param>(name_ref.syntax()) { | 164 | if is_node::<ast::Param>(name_ref.syntax()) { |
@@ -133,8 +170,7 @@ impl<'a> CompletionContext<'a> { | |||
133 | 170 | ||
134 | // Otherwise, see if this is a declaration. We can use heuristics to | 171 | // Otherwise, see if this is a declaration. We can use heuristics to |
135 | // suggest declaration names, see `CompletionKind::Magic`. | 172 | // suggest declaration names, see `CompletionKind::Magic`. |
136 | if let Some(name) = find_node_at_offset::<ast::Name>(file_with_fake_ident.syntax(), offset) | 173 | 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) { | 174 | if let Some(bind_pat) = name.syntax().ancestors().find_map(ast::BindPat::cast) { |
139 | let parent = bind_pat.syntax().parent(); | 175 | let parent = bind_pat.syntax().parent(); |
140 | if parent.clone().and_then(ast::MatchArm::cast).is_some() | 176 | if parent.clone().and_then(ast::MatchArm::cast).is_some() |
@@ -148,23 +184,24 @@ impl<'a> CompletionContext<'a> { | |||
148 | return; | 184 | return; |
149 | } | 185 | } |
150 | if name.syntax().ancestors().find_map(ast::RecordFieldPatList::cast).is_some() { | 186 | 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); | 187 | self.record_lit_pat = |
188 | self.sema.find_node_at_offset_with_macros(&original_file, self.offset); | ||
152 | } | 189 | } |
153 | } | 190 | } |
154 | } | 191 | } |
155 | 192 | ||
156 | fn classify_name_ref(&mut self, original_file: &SourceFile, name_ref: ast::NameRef) { | 193 | fn classify_name_ref(&mut self, original_file: &SyntaxNode, name_ref: ast::NameRef) { |
157 | self.name_ref_syntax = | 194 | self.name_ref_syntax = |
158 | find_node_at_offset(original_file.syntax(), name_ref.syntax().text_range().start()); | 195 | find_node_at_offset(&original_file, name_ref.syntax().text_range().start()); |
159 | let name_range = name_ref.syntax().text_range(); | 196 | let name_range = name_ref.syntax().text_range(); |
160 | if name_ref.syntax().parent().and_then(ast::RecordField::cast).is_some() { | 197 | 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); | 198 | self.record_lit_syntax = |
199 | self.sema.find_node_at_offset_with_macros(&original_file, self.offset); | ||
162 | } | 200 | } |
163 | 201 | ||
164 | self.impl_def = self | 202 | self.impl_def = self |
165 | .token | 203 | .sema |
166 | .parent() | 204 | .ancestors_with_macros(self.token.parent()) |
167 | .ancestors() | ||
168 | .take_while(|it| it.kind() != SOURCE_FILE && it.kind() != MODULE) | 205 | .take_while(|it| it.kind() != SOURCE_FILE && it.kind() != MODULE) |
169 | .find_map(ast::ImplDef::cast); | 206 | .find_map(ast::ImplDef::cast); |
170 | 207 | ||
@@ -183,12 +220,12 @@ impl<'a> CompletionContext<'a> { | |||
183 | _ => (), | 220 | _ => (), |
184 | } | 221 | } |
185 | 222 | ||
186 | self.use_item_syntax = self.token.parent().ancestors().find_map(ast::UseItem::cast); | 223 | self.use_item_syntax = |
224 | self.sema.ancestors_with_macros(self.token.parent()).find_map(ast::UseItem::cast); | ||
187 | 225 | ||
188 | self.function_syntax = self | 226 | self.function_syntax = self |
189 | .token | 227 | .sema |
190 | .parent() | 228 | .ancestors_with_macros(self.token.parent()) |
191 | .ancestors() | ||
192 | .take_while(|it| it.kind() != SOURCE_FILE && it.kind() != MODULE) | 229 | .take_while(|it| it.kind() != SOURCE_FILE && it.kind() != MODULE) |
193 | .find_map(ast::FnDef::cast); | 230 | .find_map(ast::FnDef::cast); |
194 | 231 | ||
@@ -242,7 +279,7 @@ impl<'a> CompletionContext<'a> { | |||
242 | 279 | ||
243 | if let Some(off) = name_ref.syntax().text_range().start().checked_sub(2.into()) { | 280 | if let Some(off) = name_ref.syntax().text_range().start().checked_sub(2.into()) { |
244 | if let Some(if_expr) = | 281 | if let Some(if_expr) = |
245 | find_node_at_offset::<ast::IfExpr>(original_file.syntax(), off) | 282 | self.sema.find_node_at_offset_with_macros::<ast::IfExpr>(original_file, off) |
246 | { | 283 | { |
247 | if if_expr.syntax().text_range().end() | 284 | if if_expr.syntax().text_range().end() |
248 | < name_ref.syntax().text_range().start() | 285 | < name_ref.syntax().text_range().start() |
@@ -259,7 +296,7 @@ impl<'a> CompletionContext<'a> { | |||
259 | self.dot_receiver = field_expr | 296 | self.dot_receiver = field_expr |
260 | .expr() | 297 | .expr() |
261 | .map(|e| e.syntax().text_range()) | 298 | .map(|e| e.syntax().text_range()) |
262 | .and_then(|r| find_node_with_range(original_file.syntax(), r)); | 299 | .and_then(|r| find_node_with_range(original_file, r)); |
263 | self.dot_receiver_is_ambiguous_float_literal = | 300 | self.dot_receiver_is_ambiguous_float_literal = |
264 | if let Some(ast::Expr::Literal(l)) = &self.dot_receiver { | 301 | if let Some(ast::Expr::Literal(l)) = &self.dot_receiver { |
265 | match l.kind() { | 302 | match l.kind() { |
@@ -275,7 +312,7 @@ impl<'a> CompletionContext<'a> { | |||
275 | self.dot_receiver = method_call_expr | 312 | self.dot_receiver = method_call_expr |
276 | .expr() | 313 | .expr() |
277 | .map(|e| e.syntax().text_range()) | 314 | .map(|e| e.syntax().text_range()) |
278 | .and_then(|r| find_node_with_range(original_file.syntax(), r)); | 315 | .and_then(|r| find_node_with_range(original_file, r)); |
279 | self.is_call = true; | 316 | self.is_call = true; |
280 | } | 317 | } |
281 | } | 318 | } |