diff options
Diffstat (limited to 'crates')
49 files changed, 1027 insertions, 979 deletions
diff --git a/crates/ra_assists/src/assist_ctx.rs b/crates/ra_assists/src/assist_ctx.rs index 5aab5fb8b..c25d2e323 100644 --- a/crates/ra_assists/src/assist_ctx.rs +++ b/crates/ra_assists/src/assist_ctx.rs | |||
@@ -1,6 +1,6 @@ | |||
1 | //! This module defines `AssistCtx` -- the API surface that is exposed to assists. | 1 | //! This module defines `AssistCtx` -- the API surface that is exposed to assists. |
2 | use hir::{InFile, SourceAnalyzer, SourceBinder}; | 2 | use hir::Semantics; |
3 | use ra_db::{FileRange, SourceDatabase}; | 3 | use ra_db::FileRange; |
4 | use ra_fmt::{leading_indent, reindent}; | 4 | use ra_fmt::{leading_indent, reindent}; |
5 | use ra_ide_db::RootDatabase; | 5 | use ra_ide_db::RootDatabase; |
6 | use ra_syntax::{ | 6 | use ra_syntax::{ |
@@ -74,29 +74,23 @@ pub(crate) type AssistHandler = fn(AssistCtx) -> Option<Assist>; | |||
74 | /// Note, however, that we don't actually use such two-phase logic at the | 74 | /// Note, however, that we don't actually use such two-phase logic at the |
75 | /// moment, because the LSP API is pretty awkward in this place, and it's much | 75 | /// moment, because the LSP API is pretty awkward in this place, and it's much |
76 | /// easier to just compute the edit eagerly :-) | 76 | /// easier to just compute the edit eagerly :-) |
77 | #[derive(Debug)] | 77 | #[derive(Clone)] |
78 | pub(crate) struct AssistCtx<'a> { | 78 | pub(crate) struct AssistCtx<'a> { |
79 | pub(crate) sema: &'a Semantics<'a, RootDatabase>, | ||
79 | pub(crate) db: &'a RootDatabase, | 80 | pub(crate) db: &'a RootDatabase, |
80 | pub(crate) frange: FileRange, | 81 | pub(crate) frange: FileRange, |
81 | source_file: SourceFile, | 82 | source_file: SourceFile, |
82 | should_compute_edit: bool, | 83 | should_compute_edit: bool, |
83 | } | 84 | } |
84 | 85 | ||
85 | impl Clone for AssistCtx<'_> { | ||
86 | fn clone(&self) -> Self { | ||
87 | AssistCtx { | ||
88 | db: self.db, | ||
89 | frange: self.frange, | ||
90 | source_file: self.source_file.clone(), | ||
91 | should_compute_edit: self.should_compute_edit, | ||
92 | } | ||
93 | } | ||
94 | } | ||
95 | |||
96 | impl<'a> AssistCtx<'a> { | 86 | impl<'a> AssistCtx<'a> { |
97 | pub fn new(db: &RootDatabase, frange: FileRange, should_compute_edit: bool) -> AssistCtx { | 87 | pub fn new( |
98 | let parse = db.parse(frange.file_id); | 88 | sema: &'a Semantics<'a, RootDatabase>, |
99 | AssistCtx { db, frange, source_file: parse.tree(), should_compute_edit } | 89 | frange: FileRange, |
90 | should_compute_edit: bool, | ||
91 | ) -> AssistCtx<'a> { | ||
92 | let source_file = sema.parse(frange.file_id); | ||
93 | AssistCtx { sema, db: sema.db, frange, source_file, should_compute_edit } | ||
100 | } | 94 | } |
101 | 95 | ||
102 | pub(crate) fn add_assist( | 96 | pub(crate) fn add_assist( |
@@ -138,18 +132,6 @@ impl<'a> AssistCtx<'a> { | |||
138 | pub(crate) fn covering_element(&self) -> SyntaxElement { | 132 | pub(crate) fn covering_element(&self) -> SyntaxElement { |
139 | find_covering_element(self.source_file.syntax(), self.frange.range) | 133 | find_covering_element(self.source_file.syntax(), self.frange.range) |
140 | } | 134 | } |
141 | pub(crate) fn source_binder(&self) -> SourceBinder<'a, RootDatabase> { | ||
142 | SourceBinder::new(self.db) | ||
143 | } | ||
144 | pub(crate) fn source_analyzer( | ||
145 | &self, | ||
146 | node: &SyntaxNode, | ||
147 | offset: Option<TextUnit>, | ||
148 | ) -> SourceAnalyzer { | ||
149 | let src = InFile::new(self.frange.file_id.into(), node); | ||
150 | self.source_binder().analyze(src, offset) | ||
151 | } | ||
152 | |||
153 | pub(crate) fn covering_node_for_range(&self, range: TextRange) -> SyntaxElement { | 135 | pub(crate) fn covering_node_for_range(&self, range: TextRange) -> SyntaxElement { |
154 | find_covering_element(self.source_file.syntax(), range) | 136 | find_covering_element(self.source_file.syntax(), range) |
155 | } | 137 | } |
diff --git a/crates/ra_assists/src/ast_transform.rs b/crates/ra_assists/src/ast_transform.rs index c6d15af5f..7846e9798 100644 --- a/crates/ra_assists/src/ast_transform.rs +++ b/crates/ra_assists/src/ast_transform.rs | |||
@@ -1,15 +1,12 @@ | |||
1 | //! `AstTransformer`s are functions that replace nodes in an AST and can be easily combined. | 1 | //! `AstTransformer`s are functions that replace nodes in an AST and can be easily combined. |
2 | use rustc_hash::FxHashMap; | 2 | use rustc_hash::FxHashMap; |
3 | 3 | ||
4 | use hir::{InFile, PathResolution}; | 4 | use hir::{PathResolution, SemanticsScope}; |
5 | use ra_ide_db::RootDatabase; | 5 | use ra_ide_db::RootDatabase; |
6 | use ra_syntax::ast::{self, AstNode}; | 6 | use ra_syntax::ast::{self, AstNode}; |
7 | 7 | ||
8 | pub trait AstTransform<'a> { | 8 | pub trait AstTransform<'a> { |
9 | fn get_substitution( | 9 | fn get_substitution(&self, node: &ra_syntax::SyntaxNode) -> Option<ra_syntax::SyntaxNode>; |
10 | &self, | ||
11 | node: InFile<&ra_syntax::SyntaxNode>, | ||
12 | ) -> Option<ra_syntax::SyntaxNode>; | ||
13 | 10 | ||
14 | fn chain_before(self, other: Box<dyn AstTransform<'a> + 'a>) -> Box<dyn AstTransform<'a> + 'a>; | 11 | fn chain_before(self, other: Box<dyn AstTransform<'a> + 'a>) -> Box<dyn AstTransform<'a> + 'a>; |
15 | fn or<T: AstTransform<'a> + 'a>(self, other: T) -> Box<dyn AstTransform<'a> + 'a> | 12 | fn or<T: AstTransform<'a> + 'a>(self, other: T) -> Box<dyn AstTransform<'a> + 'a> |
@@ -23,10 +20,7 @@ pub trait AstTransform<'a> { | |||
23 | struct NullTransformer; | 20 | struct NullTransformer; |
24 | 21 | ||
25 | impl<'a> AstTransform<'a> for NullTransformer { | 22 | impl<'a> AstTransform<'a> for NullTransformer { |
26 | fn get_substitution( | 23 | fn get_substitution(&self, _node: &ra_syntax::SyntaxNode) -> Option<ra_syntax::SyntaxNode> { |
27 | &self, | ||
28 | _node: InFile<&ra_syntax::SyntaxNode>, | ||
29 | ) -> Option<ra_syntax::SyntaxNode> { | ||
30 | None | 24 | None |
31 | } | 25 | } |
32 | fn chain_before(self, other: Box<dyn AstTransform<'a> + 'a>) -> Box<dyn AstTransform<'a> + 'a> { | 26 | fn chain_before(self, other: Box<dyn AstTransform<'a> + 'a>) -> Box<dyn AstTransform<'a> + 'a> { |
@@ -35,14 +29,16 @@ impl<'a> AstTransform<'a> for NullTransformer { | |||
35 | } | 29 | } |
36 | 30 | ||
37 | pub struct SubstituteTypeParams<'a> { | 31 | pub struct SubstituteTypeParams<'a> { |
38 | db: &'a RootDatabase, | 32 | source_scope: &'a SemanticsScope<'a, RootDatabase>, |
39 | substs: FxHashMap<hir::TypeParam, ast::TypeRef>, | 33 | substs: FxHashMap<hir::TypeParam, ast::TypeRef>, |
40 | previous: Box<dyn AstTransform<'a> + 'a>, | 34 | previous: Box<dyn AstTransform<'a> + 'a>, |
41 | } | 35 | } |
42 | 36 | ||
43 | impl<'a> SubstituteTypeParams<'a> { | 37 | impl<'a> SubstituteTypeParams<'a> { |
44 | pub fn for_trait_impl( | 38 | pub fn for_trait_impl( |
39 | source_scope: &'a SemanticsScope<'a, RootDatabase>, | ||
45 | db: &'a RootDatabase, | 40 | db: &'a RootDatabase, |
41 | // FIXME: there's implicit invariant that `trait_` and `source_scope` match... | ||
46 | trait_: hir::Trait, | 42 | trait_: hir::Trait, |
47 | impl_block: ast::ImplBlock, | 43 | impl_block: ast::ImplBlock, |
48 | ) -> SubstituteTypeParams<'a> { | 44 | ) -> SubstituteTypeParams<'a> { |
@@ -56,7 +52,7 @@ impl<'a> SubstituteTypeParams<'a> { | |||
56 | .zip(substs.into_iter()) | 52 | .zip(substs.into_iter()) |
57 | .collect(); | 53 | .collect(); |
58 | return SubstituteTypeParams { | 54 | return SubstituteTypeParams { |
59 | db, | 55 | source_scope, |
60 | substs: substs_by_param, | 56 | substs: substs_by_param, |
61 | previous: Box::new(NullTransformer), | 57 | previous: Box::new(NullTransformer), |
62 | }; | 58 | }; |
@@ -80,15 +76,15 @@ impl<'a> SubstituteTypeParams<'a> { | |||
80 | } | 76 | } |
81 | fn get_substitution_inner( | 77 | fn get_substitution_inner( |
82 | &self, | 78 | &self, |
83 | node: InFile<&ra_syntax::SyntaxNode>, | 79 | node: &ra_syntax::SyntaxNode, |
84 | ) -> Option<ra_syntax::SyntaxNode> { | 80 | ) -> Option<ra_syntax::SyntaxNode> { |
85 | let type_ref = ast::TypeRef::cast(node.value.clone())?; | 81 | let type_ref = ast::TypeRef::cast(node.clone())?; |
86 | let path = match &type_ref { | 82 | let path = match &type_ref { |
87 | ast::TypeRef::PathType(path_type) => path_type.path()?, | 83 | ast::TypeRef::PathType(path_type) => path_type.path()?, |
88 | _ => return None, | 84 | _ => return None, |
89 | }; | 85 | }; |
90 | let analyzer = hir::SourceAnalyzer::new(self.db, node, None); | 86 | let path = hir::Path::from_ast(path)?; |
91 | let resolution = analyzer.resolve_path(self.db, &path)?; | 87 | let resolution = self.source_scope.resolve_hir_path(&path)?; |
92 | match resolution { | 88 | match resolution { |
93 | hir::PathResolution::TypeParam(tp) => Some(self.substs.get(&tp)?.syntax().clone()), | 89 | hir::PathResolution::TypeParam(tp) => Some(self.substs.get(&tp)?.syntax().clone()), |
94 | _ => None, | 90 | _ => None, |
@@ -97,10 +93,7 @@ impl<'a> SubstituteTypeParams<'a> { | |||
97 | } | 93 | } |
98 | 94 | ||
99 | impl<'a> AstTransform<'a> for SubstituteTypeParams<'a> { | 95 | impl<'a> AstTransform<'a> for SubstituteTypeParams<'a> { |
100 | fn get_substitution( | 96 | fn get_substitution(&self, node: &ra_syntax::SyntaxNode) -> Option<ra_syntax::SyntaxNode> { |
101 | &self, | ||
102 | node: InFile<&ra_syntax::SyntaxNode>, | ||
103 | ) -> Option<ra_syntax::SyntaxNode> { | ||
104 | self.get_substitution_inner(node).or_else(|| self.previous.get_substitution(node)) | 97 | self.get_substitution_inner(node).or_else(|| self.previous.get_substitution(node)) |
105 | } | 98 | } |
106 | fn chain_before(self, other: Box<dyn AstTransform<'a> + 'a>) -> Box<dyn AstTransform<'a> + 'a> { | 99 | fn chain_before(self, other: Box<dyn AstTransform<'a> + 'a>) -> Box<dyn AstTransform<'a> + 'a> { |
@@ -109,29 +102,34 @@ impl<'a> AstTransform<'a> for SubstituteTypeParams<'a> { | |||
109 | } | 102 | } |
110 | 103 | ||
111 | pub struct QualifyPaths<'a> { | 104 | pub struct QualifyPaths<'a> { |
105 | target_scope: &'a SemanticsScope<'a, RootDatabase>, | ||
106 | source_scope: &'a SemanticsScope<'a, RootDatabase>, | ||
112 | db: &'a RootDatabase, | 107 | db: &'a RootDatabase, |
113 | from: Option<hir::Module>, | ||
114 | previous: Box<dyn AstTransform<'a> + 'a>, | 108 | previous: Box<dyn AstTransform<'a> + 'a>, |
115 | } | 109 | } |
116 | 110 | ||
117 | impl<'a> QualifyPaths<'a> { | 111 | impl<'a> QualifyPaths<'a> { |
118 | pub fn new(db: &'a RootDatabase, from: Option<hir::Module>) -> Self { | 112 | pub fn new( |
119 | Self { db, from, previous: Box::new(NullTransformer) } | 113 | target_scope: &'a SemanticsScope<'a, RootDatabase>, |
114 | source_scope: &'a SemanticsScope<'a, RootDatabase>, | ||
115 | db: &'a RootDatabase, | ||
116 | ) -> Self { | ||
117 | Self { target_scope, source_scope, db, previous: Box::new(NullTransformer) } | ||
120 | } | 118 | } |
121 | 119 | ||
122 | fn get_substitution_inner( | 120 | fn get_substitution_inner( |
123 | &self, | 121 | &self, |
124 | node: InFile<&ra_syntax::SyntaxNode>, | 122 | node: &ra_syntax::SyntaxNode, |
125 | ) -> Option<ra_syntax::SyntaxNode> { | 123 | ) -> Option<ra_syntax::SyntaxNode> { |
126 | // FIXME handle value ns? | 124 | // FIXME handle value ns? |
127 | let from = self.from?; | 125 | let from = self.target_scope.module()?; |
128 | let p = ast::Path::cast(node.value.clone())?; | 126 | let p = ast::Path::cast(node.clone())?; |
129 | if p.segment().and_then(|s| s.param_list()).is_some() { | 127 | if p.segment().and_then(|s| s.param_list()).is_some() { |
130 | // don't try to qualify `Fn(Foo) -> Bar` paths, they are in prelude anyway | 128 | // don't try to qualify `Fn(Foo) -> Bar` paths, they are in prelude anyway |
131 | return None; | 129 | return None; |
132 | } | 130 | } |
133 | let analyzer = hir::SourceAnalyzer::new(self.db, node, None); | 131 | let hir_path = hir::Path::from_ast(p.clone()); |
134 | let resolution = analyzer.resolve_path(self.db, &p)?; | 132 | let resolution = self.source_scope.resolve_hir_path(&hir_path?)?; |
135 | match resolution { | 133 | match resolution { |
136 | PathResolution::Def(def) => { | 134 | PathResolution::Def(def) => { |
137 | let found_path = from.find_use_path(self.db, def)?; | 135 | let found_path = from.find_use_path(self.db, def)?; |
@@ -140,7 +138,7 @@ impl<'a> QualifyPaths<'a> { | |||
140 | let type_args = p | 138 | let type_args = p |
141 | .segment() | 139 | .segment() |
142 | .and_then(|s| s.type_arg_list()) | 140 | .and_then(|s| s.type_arg_list()) |
143 | .map(|arg_list| apply(self, node.with_value(arg_list))); | 141 | .map(|arg_list| apply(self, arg_list)); |
144 | if let Some(type_args) = type_args { | 142 | if let Some(type_args) = type_args { |
145 | let last_segment = path.segment().unwrap(); | 143 | let last_segment = path.segment().unwrap(); |
146 | path = path.with_segment(last_segment.with_type_args(type_args)) | 144 | path = path.with_segment(last_segment.with_type_args(type_args)) |
@@ -157,11 +155,11 @@ impl<'a> QualifyPaths<'a> { | |||
157 | } | 155 | } |
158 | } | 156 | } |
159 | 157 | ||
160 | pub fn apply<'a, N: AstNode>(transformer: &dyn AstTransform<'a>, node: InFile<N>) -> N { | 158 | pub fn apply<'a, N: AstNode>(transformer: &dyn AstTransform<'a>, node: N) -> N { |
161 | let syntax = node.value.syntax(); | 159 | let syntax = node.syntax(); |
162 | let result = ra_syntax::algo::replace_descendants(syntax, &|element| match element { | 160 | let result = ra_syntax::algo::replace_descendants(syntax, &|element| match element { |
163 | ra_syntax::SyntaxElement::Node(n) => { | 161 | ra_syntax::SyntaxElement::Node(n) => { |
164 | let replacement = transformer.get_substitution(node.with_value(&n))?; | 162 | let replacement = transformer.get_substitution(&n)?; |
165 | Some(replacement.into()) | 163 | Some(replacement.into()) |
166 | } | 164 | } |
167 | _ => None, | 165 | _ => None, |
@@ -170,10 +168,7 @@ pub fn apply<'a, N: AstNode>(transformer: &dyn AstTransform<'a>, node: InFile<N> | |||
170 | } | 168 | } |
171 | 169 | ||
172 | impl<'a> AstTransform<'a> for QualifyPaths<'a> { | 170 | impl<'a> AstTransform<'a> for QualifyPaths<'a> { |
173 | fn get_substitution( | 171 | fn get_substitution(&self, node: &ra_syntax::SyntaxNode) -> Option<ra_syntax::SyntaxNode> { |
174 | &self, | ||
175 | node: InFile<&ra_syntax::SyntaxNode>, | ||
176 | ) -> Option<ra_syntax::SyntaxNode> { | ||
177 | self.get_substitution_inner(node).or_else(|| self.previous.get_substitution(node)) | 172 | self.get_substitution_inner(node).or_else(|| self.previous.get_substitution(node)) |
178 | } | 173 | } |
179 | fn chain_before(self, other: Box<dyn AstTransform<'a> + 'a>) -> Box<dyn AstTransform<'a> + 'a> { | 174 | fn chain_before(self, other: Box<dyn AstTransform<'a> + 'a>) -> Box<dyn AstTransform<'a> + 'a> { |
diff --git a/crates/ra_assists/src/handlers/add_explicit_type.rs b/crates/ra_assists/src/handlers/add_explicit_type.rs index 2cb9d2f48..a63ef48b1 100644 --- a/crates/ra_assists/src/handlers/add_explicit_type.rs +++ b/crates/ra_assists/src/handlers/add_explicit_type.rs | |||
@@ -51,14 +51,13 @@ pub(crate) fn add_explicit_type(ctx: AssistCtx) -> Option<Assist> { | |||
51 | } | 51 | } |
52 | } | 52 | } |
53 | // Infer type | 53 | // Infer type |
54 | let db = ctx.db; | 54 | let ty = ctx.sema.type_of_expr(&expr)?; |
55 | let analyzer = ctx.source_analyzer(stmt.syntax(), None); | ||
56 | let ty = analyzer.type_of(db, &expr)?; | ||
57 | // Assist not applicable if the type is unknown | 55 | // Assist not applicable if the type is unknown |
58 | if ty.contains_unknown() { | 56 | if ty.contains_unknown() { |
59 | return None; | 57 | return None; |
60 | } | 58 | } |
61 | 59 | ||
60 | let db = ctx.db; | ||
62 | ctx.add_assist( | 61 | ctx.add_assist( |
63 | AssistId("add_explicit_type"), | 62 | AssistId("add_explicit_type"), |
64 | format!("Insert explicit type '{}'", ty.display(db)), | 63 | format!("Insert explicit type '{}'", ty.display(db)), |
diff --git a/crates/ra_assists/src/handlers/add_missing_impl_members.rs b/crates/ra_assists/src/handlers/add_missing_impl_members.rs index ab21388c8..4005014bd 100644 --- a/crates/ra_assists/src/handlers/add_missing_impl_members.rs +++ b/crates/ra_assists/src/handlers/add_missing_impl_members.rs | |||
@@ -1,4 +1,4 @@ | |||
1 | use hir::{HasSource, InFile}; | 1 | use hir::HasSource; |
2 | use ra_syntax::{ | 2 | use ra_syntax::{ |
3 | ast::{self, edit, make, AstNode, NameOwner}, | 3 | ast::{self, edit, make, AstNode, NameOwner}, |
4 | SmolStr, | 4 | SmolStr, |
@@ -104,9 +104,7 @@ fn add_missing_impl_members_inner( | |||
104 | let impl_node = ctx.find_node_at_offset::<ast::ImplBlock>()?; | 104 | let impl_node = ctx.find_node_at_offset::<ast::ImplBlock>()?; |
105 | let impl_item_list = impl_node.item_list()?; | 105 | let impl_item_list = impl_node.item_list()?; |
106 | 106 | ||
107 | let analyzer = ctx.source_analyzer(impl_node.syntax(), None); | 107 | let trait_ = resolve_target_trait(&ctx.sema, &impl_node)?; |
108 | |||
109 | let trait_ = resolve_target_trait(ctx.db, &analyzer, &impl_node)?; | ||
110 | 108 | ||
111 | let def_name = |item: &ast::ImplItem| -> Option<SmolStr> { | 109 | let def_name = |item: &ast::ImplItem| -> Option<SmolStr> { |
112 | match item { | 110 | match item { |
@@ -117,7 +115,7 @@ fn add_missing_impl_members_inner( | |||
117 | .map(|it| it.text().clone()) | 115 | .map(|it| it.text().clone()) |
118 | }; | 116 | }; |
119 | 117 | ||
120 | let missing_items = get_missing_impl_items(ctx.db, &analyzer, &impl_node) | 118 | let missing_items = get_missing_impl_items(&ctx.sema, &impl_node) |
121 | .iter() | 119 | .iter() |
122 | .map(|i| match i { | 120 | .map(|i| match i { |
123 | hir::AssocItem::Function(i) => ast::ImplItem::FnDef(i.source(ctx.db).value), | 121 | hir::AssocItem::Function(i) => ast::ImplItem::FnDef(i.source(ctx.db).value), |
@@ -138,23 +136,17 @@ fn add_missing_impl_members_inner( | |||
138 | return None; | 136 | return None; |
139 | } | 137 | } |
140 | 138 | ||
141 | let db = ctx.db; | 139 | let sema = ctx.sema; |
142 | let file_id = ctx.frange.file_id; | ||
143 | let trait_file_id = trait_.source(db).file_id; | ||
144 | 140 | ||
145 | ctx.add_assist(AssistId(assist_id), label, |edit| { | 141 | ctx.add_assist(AssistId(assist_id), label, |edit| { |
146 | let n_existing_items = impl_item_list.impl_items().count(); | 142 | let n_existing_items = impl_item_list.impl_items().count(); |
147 | let module = hir::SourceAnalyzer::new( | 143 | let source_scope = sema.scope_for_def(trait_); |
148 | db, | 144 | let target_scope = sema.scope(impl_item_list.syntax()); |
149 | hir::InFile::new(file_id.into(), impl_node.syntax()), | 145 | let ast_transform = QualifyPaths::new(&target_scope, &source_scope, sema.db) |
150 | None, | 146 | .or(SubstituteTypeParams::for_trait_impl(&source_scope, sema.db, trait_, impl_node)); |
151 | ) | ||
152 | .module(); | ||
153 | let ast_transform = QualifyPaths::new(db, module) | ||
154 | .or(SubstituteTypeParams::for_trait_impl(db, trait_, impl_node)); | ||
155 | let items = missing_items | 147 | let items = missing_items |
156 | .into_iter() | 148 | .into_iter() |
157 | .map(|it| ast_transform::apply(&*ast_transform, InFile::new(trait_file_id, it))) | 149 | .map(|it| ast_transform::apply(&*ast_transform, it)) |
158 | .map(|it| match it { | 150 | .map(|it| match it { |
159 | ast::ImplItem::FnDef(def) => ast::ImplItem::FnDef(add_body(def)), | 151 | ast::ImplItem::FnDef(def) => ast::ImplItem::FnDef(add_body(def)), |
160 | _ => it, | 152 | _ => it, |
@@ -181,9 +173,10 @@ fn add_body(fn_def: ast::FnDef) -> ast::FnDef { | |||
181 | 173 | ||
182 | #[cfg(test)] | 174 | #[cfg(test)] |
183 | mod tests { | 175 | mod tests { |
184 | use super::*; | ||
185 | use crate::helpers::{check_assist, check_assist_not_applicable}; | 176 | use crate::helpers::{check_assist, check_assist_not_applicable}; |
186 | 177 | ||
178 | use super::*; | ||
179 | |||
187 | #[test] | 180 | #[test] |
188 | fn test_add_missing_impl_members() { | 181 | fn test_add_missing_impl_members() { |
189 | check_assist( | 182 | check_assist( |
diff --git a/crates/ra_assists/src/handlers/add_new.rs b/crates/ra_assists/src/handlers/add_new.rs index dd070e8ec..166e907fb 100644 --- a/crates/ra_assists/src/handlers/add_new.rs +++ b/crates/ra_assists/src/handlers/add_new.rs | |||
@@ -1,5 +1,5 @@ | |||
1 | use format_buf::format; | 1 | use format_buf::format; |
2 | use hir::{Adt, InFile}; | 2 | use hir::Adt; |
3 | use join_to_string::join; | 3 | use join_to_string::join; |
4 | use ra_syntax::{ | 4 | use ra_syntax::{ |
5 | ast::{ | 5 | ast::{ |
@@ -133,16 +133,11 @@ fn find_struct_impl(ctx: &AssistCtx, strukt: &ast::StructDef) -> Option<Option<a | |||
133 | let module = strukt.syntax().ancestors().find(|node| { | 133 | let module = strukt.syntax().ancestors().find(|node| { |
134 | ast::Module::can_cast(node.kind()) || ast::SourceFile::can_cast(node.kind()) | 134 | ast::Module::can_cast(node.kind()) || ast::SourceFile::can_cast(node.kind()) |
135 | })?; | 135 | })?; |
136 | let mut sb = ctx.source_binder(); | ||
137 | 136 | ||
138 | let struct_def = { | 137 | let struct_def = ctx.sema.to_def(strukt)?; |
139 | let src = InFile { file_id: ctx.frange.file_id.into(), value: strukt.clone() }; | ||
140 | sb.to_def(src)? | ||
141 | }; | ||
142 | 138 | ||
143 | let block = module.descendants().filter_map(ast::ImplBlock::cast).find_map(|impl_blk| { | 139 | let block = module.descendants().filter_map(ast::ImplBlock::cast).find_map(|impl_blk| { |
144 | let src = InFile { file_id: ctx.frange.file_id.into(), value: impl_blk.clone() }; | 140 | let blk = ctx.sema.to_def(&impl_blk)?; |
145 | let blk = sb.to_def(src)?; | ||
146 | 141 | ||
147 | // FIXME: handle e.g. `struct S<T>; impl<U> S<U> {}` | 142 | // FIXME: handle e.g. `struct S<T>; impl<U> S<U> {}` |
148 | // (we currently use the wrong type parameter) | 143 | // (we currently use the wrong type parameter) |
diff --git a/crates/ra_assists/src/handlers/auto_import.rs b/crates/ra_assists/src/handlers/auto_import.rs index c4aea2a06..edf0cf6d0 100644 --- a/crates/ra_assists/src/handlers/auto_import.rs +++ b/crates/ra_assists/src/handlers/auto_import.rs | |||
@@ -3,8 +3,8 @@ use crate::{ | |||
3 | insert_use_statement, AssistId, | 3 | insert_use_statement, AssistId, |
4 | }; | 4 | }; |
5 | use hir::{ | 5 | use hir::{ |
6 | db::HirDatabase, AsAssocItem, AssocItemContainer, ModPath, Module, ModuleDef, PathResolution, | 6 | AsAssocItem, AssocItemContainer, ModPath, Module, ModuleDef, PathResolution, Semantics, Trait, |
7 | SourceAnalyzer, Trait, Type, | 7 | Type, |
8 | }; | 8 | }; |
9 | use ra_ide_db::{imports_locator::ImportsLocator, RootDatabase}; | 9 | use ra_ide_db::{imports_locator::ImportsLocator, RootDatabase}; |
10 | use ra_prof::profile; | 10 | use ra_prof::profile; |
@@ -78,14 +78,9 @@ impl AutoImportAssets { | |||
78 | 78 | ||
79 | fn for_method_call(method_call: ast::MethodCallExpr, ctx: &AssistCtx) -> Option<Self> { | 79 | fn for_method_call(method_call: ast::MethodCallExpr, ctx: &AssistCtx) -> Option<Self> { |
80 | let syntax_under_caret = method_call.syntax().to_owned(); | 80 | let syntax_under_caret = method_call.syntax().to_owned(); |
81 | let source_analyzer = ctx.source_analyzer(&syntax_under_caret, None); | 81 | let module_with_name_to_import = ctx.sema.scope(&syntax_under_caret).module()?; |
82 | let module_with_name_to_import = source_analyzer.module()?; | ||
83 | Some(Self { | 82 | Some(Self { |
84 | import_candidate: ImportCandidate::for_method_call( | 83 | import_candidate: ImportCandidate::for_method_call(&ctx.sema, &method_call)?, |
85 | &method_call, | ||
86 | &source_analyzer, | ||
87 | ctx.db, | ||
88 | )?, | ||
89 | module_with_name_to_import, | 84 | module_with_name_to_import, |
90 | syntax_under_caret, | 85 | syntax_under_caret, |
91 | }) | 86 | }) |
@@ -97,14 +92,9 @@ impl AutoImportAssets { | |||
97 | return None; | 92 | return None; |
98 | } | 93 | } |
99 | 94 | ||
100 | let source_analyzer = ctx.source_analyzer(&syntax_under_caret, None); | 95 | let module_with_name_to_import = ctx.sema.scope(&syntax_under_caret).module()?; |
101 | let module_with_name_to_import = source_analyzer.module()?; | ||
102 | Some(Self { | 96 | Some(Self { |
103 | import_candidate: ImportCandidate::for_regular_path( | 97 | import_candidate: ImportCandidate::for_regular_path(&ctx.sema, &path_under_caret)?, |
104 | &path_under_caret, | ||
105 | &source_analyzer, | ||
106 | ctx.db, | ||
107 | )?, | ||
108 | module_with_name_to_import, | 98 | module_with_name_to_import, |
109 | syntax_under_caret, | 99 | syntax_under_caret, |
110 | }) | 100 | }) |
@@ -229,25 +219,23 @@ enum ImportCandidate { | |||
229 | 219 | ||
230 | impl ImportCandidate { | 220 | impl ImportCandidate { |
231 | fn for_method_call( | 221 | fn for_method_call( |
222 | sema: &Semantics<RootDatabase>, | ||
232 | method_call: &ast::MethodCallExpr, | 223 | method_call: &ast::MethodCallExpr, |
233 | source_analyzer: &SourceAnalyzer, | ||
234 | db: &impl HirDatabase, | ||
235 | ) -> Option<Self> { | 224 | ) -> Option<Self> { |
236 | if source_analyzer.resolve_method_call(method_call).is_some() { | 225 | if sema.resolve_method_call(method_call).is_some() { |
237 | return None; | 226 | return None; |
238 | } | 227 | } |
239 | Some(Self::TraitMethod( | 228 | Some(Self::TraitMethod( |
240 | source_analyzer.type_of(db, &method_call.expr()?)?, | 229 | sema.type_of_expr(&method_call.expr()?)?, |
241 | method_call.name_ref()?.syntax().to_string(), | 230 | method_call.name_ref()?.syntax().to_string(), |
242 | )) | 231 | )) |
243 | } | 232 | } |
244 | 233 | ||
245 | fn for_regular_path( | 234 | fn for_regular_path( |
235 | sema: &Semantics<RootDatabase>, | ||
246 | path_under_caret: &ast::Path, | 236 | path_under_caret: &ast::Path, |
247 | source_analyzer: &SourceAnalyzer, | ||
248 | db: &impl HirDatabase, | ||
249 | ) -> Option<Self> { | 237 | ) -> Option<Self> { |
250 | if source_analyzer.resolve_path(db, path_under_caret).is_some() { | 238 | if sema.resolve_path(path_under_caret).is_some() { |
251 | return None; | 239 | return None; |
252 | } | 240 | } |
253 | 241 | ||
@@ -256,17 +244,15 @@ impl ImportCandidate { | |||
256 | let qualifier_start = qualifier.syntax().descendants().find_map(ast::NameRef::cast)?; | 244 | let qualifier_start = qualifier.syntax().descendants().find_map(ast::NameRef::cast)?; |
257 | let qualifier_start_path = | 245 | let qualifier_start_path = |
258 | qualifier_start.syntax().ancestors().find_map(ast::Path::cast)?; | 246 | qualifier_start.syntax().ancestors().find_map(ast::Path::cast)?; |
259 | if let Some(qualifier_start_resolution) = | 247 | if let Some(qualifier_start_resolution) = sema.resolve_path(&qualifier_start_path) { |
260 | source_analyzer.resolve_path(db, &qualifier_start_path) | ||
261 | { | ||
262 | let qualifier_resolution = if qualifier_start_path == qualifier { | 248 | let qualifier_resolution = if qualifier_start_path == qualifier { |
263 | qualifier_start_resolution | 249 | qualifier_start_resolution |
264 | } else { | 250 | } else { |
265 | source_analyzer.resolve_path(db, &qualifier)? | 251 | sema.resolve_path(&qualifier)? |
266 | }; | 252 | }; |
267 | if let PathResolution::Def(ModuleDef::Adt(assoc_item_path)) = qualifier_resolution { | 253 | if let PathResolution::Def(ModuleDef::Adt(assoc_item_path)) = qualifier_resolution { |
268 | Some(ImportCandidate::TraitAssocItem( | 254 | Some(ImportCandidate::TraitAssocItem( |
269 | assoc_item_path.ty(db), | 255 | assoc_item_path.ty(sema.db), |
270 | segment.syntax().to_string(), | 256 | segment.syntax().to_string(), |
271 | )) | 257 | )) |
272 | } else { | 258 | } else { |
diff --git a/crates/ra_assists/src/handlers/fill_match_arms.rs b/crates/ra_assists/src/handlers/fill_match_arms.rs index ae2437ed3..e5d8c639d 100644 --- a/crates/ra_assists/src/handlers/fill_match_arms.rs +++ b/crates/ra_assists/src/handlers/fill_match_arms.rs | |||
@@ -2,10 +2,11 @@ | |||
2 | 2 | ||
3 | use std::iter; | 3 | use std::iter; |
4 | 4 | ||
5 | use hir::{db::HirDatabase, Adt, HasSource}; | 5 | use hir::{db::HirDatabase, Adt, HasSource, Semantics}; |
6 | use ra_syntax::ast::{self, edit::IndentLevel, make, AstNode, NameOwner}; | 6 | use ra_syntax::ast::{self, edit::IndentLevel, make, AstNode, NameOwner}; |
7 | 7 | ||
8 | use crate::{Assist, AssistCtx, AssistId}; | 8 | use crate::{Assist, AssistCtx, AssistId}; |
9 | use ra_ide_db::RootDatabase; | ||
9 | 10 | ||
10 | // Assist: fill_match_arms | 11 | // Assist: fill_match_arms |
11 | // | 12 | // |
@@ -46,10 +47,9 @@ pub(crate) fn fill_match_arms(ctx: AssistCtx) -> Option<Assist> { | |||
46 | }; | 47 | }; |
47 | 48 | ||
48 | let expr = match_expr.expr()?; | 49 | let expr = match_expr.expr()?; |
49 | let (enum_def, module) = { | 50 | let enum_def = resolve_enum_def(&ctx.sema, &expr)?; |
50 | let analyzer = ctx.source_analyzer(expr.syntax(), None); | 51 | let module = ctx.sema.scope(expr.syntax()).module()?; |
51 | (resolve_enum_def(ctx.db, &analyzer, &expr)?, analyzer.module()?) | 52 | |
52 | }; | ||
53 | let variants = enum_def.variants(ctx.db); | 53 | let variants = enum_def.variants(ctx.db); |
54 | if variants.is_empty() { | 54 | if variants.is_empty() { |
55 | return None; | 55 | return None; |
@@ -81,18 +81,11 @@ fn is_trivial(arm: &ast::MatchArm) -> bool { | |||
81 | } | 81 | } |
82 | } | 82 | } |
83 | 83 | ||
84 | fn resolve_enum_def( | 84 | fn resolve_enum_def(sema: &Semantics<RootDatabase>, expr: &ast::Expr) -> Option<hir::Enum> { |
85 | db: &impl HirDatabase, | 85 | sema.type_of_expr(&expr)?.autoderef(sema.db).find_map(|ty| match ty.as_adt() { |
86 | analyzer: &hir::SourceAnalyzer, | ||
87 | expr: &ast::Expr, | ||
88 | ) -> Option<hir::Enum> { | ||
89 | let expr_ty = analyzer.type_of(db, &expr)?; | ||
90 | |||
91 | let result = expr_ty.autoderef(db).find_map(|ty| match ty.as_adt() { | ||
92 | Some(Adt::Enum(e)) => Some(e), | 86 | Some(Adt::Enum(e)) => Some(e), |
93 | _ => None, | 87 | _ => None, |
94 | }); | 88 | }) |
95 | result | ||
96 | } | 89 | } |
97 | 90 | ||
98 | fn build_pat( | 91 | fn build_pat( |
diff --git a/crates/ra_assists/src/handlers/inline_local_variable.rs b/crates/ra_assists/src/handlers/inline_local_variable.rs index 91b588243..53a72309b 100644 --- a/crates/ra_assists/src/handlers/inline_local_variable.rs +++ b/crates/ra_assists/src/handlers/inline_local_variable.rs | |||
@@ -44,8 +44,7 @@ pub(crate) fn inline_local_variable(ctx: AssistCtx) -> Option<Assist> { | |||
44 | } else { | 44 | } else { |
45 | let_stmt.syntax().text_range() | 45 | let_stmt.syntax().text_range() |
46 | }; | 46 | }; |
47 | let analyzer = ctx.source_analyzer(bind_pat.syntax(), None); | 47 | let refs = ctx.sema.find_all_refs(&bind_pat); |
48 | let refs = analyzer.find_all_refs(&bind_pat); | ||
49 | if refs.is_empty() { | 48 | if refs.is_empty() { |
50 | return None; | 49 | return None; |
51 | }; | 50 | }; |
diff --git a/crates/ra_assists/src/lib.rs b/crates/ra_assists/src/lib.rs index 79fe43aa4..c28a9b92b 100644 --- a/crates/ra_assists/src/lib.rs +++ b/crates/ra_assists/src/lib.rs | |||
@@ -19,6 +19,7 @@ use ra_text_edit::TextEdit; | |||
19 | 19 | ||
20 | pub(crate) use crate::assist_ctx::{Assist, AssistCtx, AssistHandler}; | 20 | pub(crate) use crate::assist_ctx::{Assist, AssistCtx, AssistHandler}; |
21 | pub use crate::handlers::replace_qualified_name_with_use::insert_use_statement; | 21 | pub use crate::handlers::replace_qualified_name_with_use::insert_use_statement; |
22 | use hir::Semantics; | ||
22 | 23 | ||
23 | /// Unique identifier of the assist, should not be shown to the user | 24 | /// Unique identifier of the assist, should not be shown to the user |
24 | /// directly. | 25 | /// directly. |
@@ -63,7 +64,8 @@ pub struct ResolvedAssist { | |||
63 | /// Assists are returned in the "unresolved" state, that is only labels are | 64 | /// Assists are returned in the "unresolved" state, that is only labels are |
64 | /// returned, without actual edits. | 65 | /// returned, without actual edits. |
65 | pub fn unresolved_assists(db: &RootDatabase, range: FileRange) -> Vec<AssistLabel> { | 66 | pub fn unresolved_assists(db: &RootDatabase, range: FileRange) -> Vec<AssistLabel> { |
66 | let ctx = AssistCtx::new(db, range, false); | 67 | let sema = Semantics::new(db); |
68 | let ctx = AssistCtx::new(&sema, range, false); | ||
67 | handlers::all() | 69 | handlers::all() |
68 | .iter() | 70 | .iter() |
69 | .filter_map(|f| f(ctx.clone())) | 71 | .filter_map(|f| f(ctx.clone())) |
@@ -77,7 +79,8 @@ pub fn unresolved_assists(db: &RootDatabase, range: FileRange) -> Vec<AssistLabe | |||
77 | /// Assists are returned in the "resolved" state, that is with edit fully | 79 | /// Assists are returned in the "resolved" state, that is with edit fully |
78 | /// computed. | 80 | /// computed. |
79 | pub fn resolved_assists(db: &RootDatabase, range: FileRange) -> Vec<ResolvedAssist> { | 81 | pub fn resolved_assists(db: &RootDatabase, range: FileRange) -> Vec<ResolvedAssist> { |
80 | let ctx = AssistCtx::new(db, range, true); | 82 | let sema = Semantics::new(db); |
83 | let ctx = AssistCtx::new(&sema, range, true); | ||
81 | let mut a = handlers::all() | 84 | let mut a = handlers::all() |
82 | .iter() | 85 | .iter() |
83 | .filter_map(|f| f(ctx.clone())) | 86 | .filter_map(|f| f(ctx.clone())) |
@@ -165,6 +168,7 @@ mod helpers { | |||
165 | use test_utils::{add_cursor, assert_eq_text, extract_range_or_offset, RangeOrOffset}; | 168 | use test_utils::{add_cursor, assert_eq_text, extract_range_or_offset, RangeOrOffset}; |
166 | 169 | ||
167 | use crate::{AssistCtx, AssistHandler}; | 170 | use crate::{AssistCtx, AssistHandler}; |
171 | use hir::Semantics; | ||
168 | 172 | ||
169 | pub(crate) fn with_single_file(text: &str) -> (RootDatabase, FileId) { | 173 | pub(crate) fn with_single_file(text: &str) -> (RootDatabase, FileId) { |
170 | let (mut db, file_id) = RootDatabase::with_single_file(text); | 174 | let (mut db, file_id) = RootDatabase::with_single_file(text); |
@@ -202,7 +206,8 @@ mod helpers { | |||
202 | 206 | ||
203 | let (db, file_id) = with_single_file(&before); | 207 | let (db, file_id) = with_single_file(&before); |
204 | let frange = FileRange { file_id, range }; | 208 | let frange = FileRange { file_id, range }; |
205 | let assist_ctx = AssistCtx::new(&db, frange, true); | 209 | let sema = Semantics::new(&db); |
210 | let assist_ctx = AssistCtx::new(&sema, frange, true); | ||
206 | 211 | ||
207 | match (assist(assist_ctx), expected) { | 212 | match (assist(assist_ctx), expected) { |
208 | (Some(assist), ExpectedResult::After(after)) => { | 213 | (Some(assist), ExpectedResult::After(after)) => { |
diff --git a/crates/ra_assists/src/utils.rs b/crates/ra_assists/src/utils.rs index 6ff44c95c..92d3ed471 100644 --- a/crates/ra_assists/src/utils.rs +++ b/crates/ra_assists/src/utils.rs | |||
@@ -1,16 +1,15 @@ | |||
1 | //! Assorted functions shared by several assists. | 1 | //! Assorted functions shared by several assists. |
2 | 2 | ||
3 | use hir::Semantics; | ||
4 | use ra_ide_db::RootDatabase; | ||
3 | use ra_syntax::{ | 5 | use ra_syntax::{ |
4 | ast::{self, make, NameOwner}, | 6 | ast::{self, make, NameOwner}, |
5 | AstNode, T, | 7 | AstNode, T, |
6 | }; | 8 | }; |
7 | |||
8 | use hir::db::HirDatabase; | ||
9 | use rustc_hash::FxHashSet; | 9 | use rustc_hash::FxHashSet; |
10 | 10 | ||
11 | pub fn get_missing_impl_items( | 11 | pub fn get_missing_impl_items( |
12 | db: &impl HirDatabase, | 12 | sema: &Semantics<RootDatabase>, |
13 | analyzer: &hir::SourceAnalyzer, | ||
14 | impl_block: &ast::ImplBlock, | 13 | impl_block: &ast::ImplBlock, |
15 | ) -> Vec<hir::AssocItem> { | 14 | ) -> Vec<hir::AssocItem> { |
16 | // Names must be unique between constants and functions. However, type aliases | 15 | // Names must be unique between constants and functions. However, type aliases |
@@ -42,15 +41,17 @@ pub fn get_missing_impl_items( | |||
42 | } | 41 | } |
43 | } | 42 | } |
44 | 43 | ||
45 | resolve_target_trait(db, analyzer, impl_block).map_or(vec![], |target_trait| { | 44 | resolve_target_trait(sema, impl_block).map_or(vec![], |target_trait| { |
46 | target_trait | 45 | target_trait |
47 | .items(db) | 46 | .items(sema.db) |
48 | .iter() | 47 | .iter() |
49 | .filter(|i| match i { | 48 | .filter(|i| match i { |
50 | hir::AssocItem::Function(f) => !impl_fns_consts.contains(&f.name(db).to_string()), | 49 | hir::AssocItem::Function(f) => { |
51 | hir::AssocItem::TypeAlias(t) => !impl_type.contains(&t.name(db).to_string()), | 50 | !impl_fns_consts.contains(&f.name(sema.db).to_string()) |
51 | } | ||
52 | hir::AssocItem::TypeAlias(t) => !impl_type.contains(&t.name(sema.db).to_string()), | ||
52 | hir::AssocItem::Const(c) => c | 53 | hir::AssocItem::Const(c) => c |
53 | .name(db) | 54 | .name(sema.db) |
54 | .map(|n| !impl_fns_consts.contains(&n.to_string())) | 55 | .map(|n| !impl_fns_consts.contains(&n.to_string())) |
55 | .unwrap_or_default(), | 56 | .unwrap_or_default(), |
56 | }) | 57 | }) |
@@ -60,8 +61,7 @@ pub fn get_missing_impl_items( | |||
60 | } | 61 | } |
61 | 62 | ||
62 | pub(crate) fn resolve_target_trait( | 63 | pub(crate) fn resolve_target_trait( |
63 | db: &impl HirDatabase, | 64 | sema: &Semantics<RootDatabase>, |
64 | analyzer: &hir::SourceAnalyzer, | ||
65 | impl_block: &ast::ImplBlock, | 65 | impl_block: &ast::ImplBlock, |
66 | ) -> Option<hir::Trait> { | 66 | ) -> Option<hir::Trait> { |
67 | let ast_path = impl_block | 67 | let ast_path = impl_block |
@@ -70,7 +70,7 @@ pub(crate) fn resolve_target_trait( | |||
70 | .and_then(ast::PathType::cast)? | 70 | .and_then(ast::PathType::cast)? |
71 | .path()?; | 71 | .path()?; |
72 | 72 | ||
73 | match analyzer.resolve_path(db, &ast_path) { | 73 | match sema.resolve_path(&ast_path) { |
74 | Some(hir::PathResolution::Def(hir::ModuleDef::Trait(def))) => Some(def), | 74 | Some(hir::PathResolution::Def(hir::ModuleDef::Trait(def))) => Some(def), |
75 | _ => None, | 75 | _ => None, |
76 | } | 76 | } |
diff --git a/crates/ra_hir/src/lib.rs b/crates/ra_hir/src/lib.rs index 7a9745ebe..004a2185f 100644 --- a/crates/ra_hir/src/lib.rs +++ b/crates/ra_hir/src/lib.rs | |||
@@ -26,6 +26,7 @@ macro_rules! impl_froms { | |||
26 | } | 26 | } |
27 | } | 27 | } |
28 | 28 | ||
29 | mod semantics; | ||
29 | pub mod db; | 30 | pub mod db; |
30 | pub mod source_analyzer; | 31 | pub mod source_analyzer; |
31 | pub mod source_binder; | 32 | pub mod source_binder; |
@@ -45,8 +46,8 @@ pub use crate::{ | |||
45 | StructField, Trait, Type, TypeAlias, TypeParam, Union, VariantDef, | 46 | StructField, Trait, Type, TypeAlias, TypeParam, Union, VariantDef, |
46 | }, | 47 | }, |
47 | has_source::HasSource, | 48 | has_source::HasSource, |
48 | source_analyzer::{PathResolution, ScopeEntryWithSyntax, SourceAnalyzer}, | 49 | semantics::{original_range, Semantics, SemanticsScope}, |
49 | source_binder::SourceBinder, | 50 | source_analyzer::{PathResolution, ScopeEntryWithSyntax}, |
50 | }; | 51 | }; |
51 | 52 | ||
52 | pub use hir_def::{ | 53 | pub use hir_def::{ |
diff --git a/crates/ra_hir/src/semantics.rs b/crates/ra_hir/src/semantics.rs new file mode 100644 index 000000000..22a7e7588 --- /dev/null +++ b/crates/ra_hir/src/semantics.rs | |||
@@ -0,0 +1,335 @@ | |||
1 | //! See `Semantics`. | ||
2 | |||
3 | use std::{cell::RefCell, fmt, iter::successors}; | ||
4 | |||
5 | use hir_def::{ | ||
6 | resolver::{self, HasResolver, Resolver}, | ||
7 | TraitId, | ||
8 | }; | ||
9 | use ra_db::{FileId, FileRange}; | ||
10 | use ra_syntax::{ast, AstNode, SyntaxNode, SyntaxToken, TextRange, TextUnit}; | ||
11 | use rustc_hash::{FxHashMap, FxHashSet}; | ||
12 | |||
13 | use crate::{ | ||
14 | db::HirDatabase, | ||
15 | source_analyzer::{resolve_hir_path, ReferenceDescriptor, SourceAnalyzer}, | ||
16 | source_binder::{ChildContainer, SourceBinder, ToDef}, | ||
17 | Function, HirFileId, InFile, Local, MacroDef, Module, Name, Origin, Path, PathResolution, | ||
18 | ScopeDef, StructField, Trait, Type, TypeParam, VariantDef, | ||
19 | }; | ||
20 | use ra_prof::profile; | ||
21 | |||
22 | /// Primary API to get semantic information, like types, from syntax trees. | ||
23 | pub struct Semantics<'db, DB> { | ||
24 | pub db: &'db DB, | ||
25 | pub(crate) sb: RefCell<SourceBinder>, | ||
26 | cache: RefCell<FxHashMap<SyntaxNode, HirFileId>>, | ||
27 | } | ||
28 | |||
29 | impl<DB> fmt::Debug for Semantics<'_, DB> { | ||
30 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||
31 | write!(f, "Semantics {{ ... }}") | ||
32 | } | ||
33 | } | ||
34 | |||
35 | impl<'db, DB: HirDatabase> Semantics<'db, DB> { | ||
36 | pub fn new(db: &DB) -> Semantics<DB> { | ||
37 | let sb = RefCell::new(SourceBinder::new()); | ||
38 | Semantics { db, sb, cache: RefCell::default() } | ||
39 | } | ||
40 | |||
41 | pub fn parse(&self, file_id: FileId) -> ast::SourceFile { | ||
42 | let tree = self.db.parse(file_id).tree(); | ||
43 | self.cache(tree.syntax().clone(), file_id.into()); | ||
44 | tree | ||
45 | } | ||
46 | |||
47 | pub fn expand(&self, macro_call: &ast::MacroCall) -> Option<SyntaxNode> { | ||
48 | let macro_call = self.find_file(macro_call.syntax().clone()).with_value(macro_call); | ||
49 | let sa = self.analyze2(macro_call.map(|it| it.syntax()), None); | ||
50 | let file_id = sa.expand(self.db, macro_call)?; | ||
51 | let node = self.db.parse_or_expand(file_id)?; | ||
52 | self.cache(node.clone(), file_id); | ||
53 | Some(node) | ||
54 | } | ||
55 | |||
56 | pub fn descend_into_macros(&self, token: SyntaxToken) -> SyntaxToken { | ||
57 | let parent = token.parent(); | ||
58 | let parent = self.find_file(parent); | ||
59 | let sa = self.analyze2(parent.as_ref(), None); | ||
60 | |||
61 | let token = successors(Some(parent.with_value(token)), |token| { | ||
62 | let macro_call = token.value.ancestors().find_map(ast::MacroCall::cast)?; | ||
63 | let tt = macro_call.token_tree()?; | ||
64 | if !token.value.text_range().is_subrange(&tt.syntax().text_range()) { | ||
65 | return None; | ||
66 | } | ||
67 | let file_id = sa.expand(self.db, token.with_value(¯o_call))?; | ||
68 | let token = file_id.expansion_info(self.db)?.map_token_down(token.as_ref())?; | ||
69 | |||
70 | self.cache(find_root(&token.value.parent()), token.file_id); | ||
71 | |||
72 | Some(token) | ||
73 | }) | ||
74 | .last() | ||
75 | .unwrap(); | ||
76 | |||
77 | token.value | ||
78 | } | ||
79 | |||
80 | pub fn original_range(&self, node: &SyntaxNode) -> FileRange { | ||
81 | let node = self.find_file(node.clone()); | ||
82 | original_range(self.db, node.as_ref()) | ||
83 | } | ||
84 | |||
85 | pub fn ancestors_with_macros(&self, node: SyntaxNode) -> impl Iterator<Item = SyntaxNode> + '_ { | ||
86 | let node = self.find_file(node); | ||
87 | node.ancestors_with_macros(self.db).map(|it| it.value) | ||
88 | } | ||
89 | |||
90 | pub fn type_of_expr(&self, expr: &ast::Expr) -> Option<Type> { | ||
91 | self.analyze(expr.syntax()).type_of(self.db, &expr) | ||
92 | } | ||
93 | |||
94 | pub fn type_of_pat(&self, pat: &ast::Pat) -> Option<Type> { | ||
95 | self.analyze(pat.syntax()).type_of_pat(self.db, &pat) | ||
96 | } | ||
97 | |||
98 | pub fn resolve_method_call(&self, call: &ast::MethodCallExpr) -> Option<Function> { | ||
99 | self.analyze(call.syntax()).resolve_method_call(call) | ||
100 | } | ||
101 | |||
102 | pub fn resolve_field(&self, field: &ast::FieldExpr) -> Option<StructField> { | ||
103 | self.analyze(field.syntax()).resolve_field(field) | ||
104 | } | ||
105 | |||
106 | pub fn resolve_record_field(&self, field: &ast::RecordField) -> Option<StructField> { | ||
107 | self.analyze(field.syntax()).resolve_record_field(field) | ||
108 | } | ||
109 | |||
110 | pub fn resolve_record_literal(&self, record_lit: &ast::RecordLit) -> Option<VariantDef> { | ||
111 | self.analyze(record_lit.syntax()).resolve_record_literal(record_lit) | ||
112 | } | ||
113 | |||
114 | pub fn resolve_record_pattern(&self, record_pat: &ast::RecordPat) -> Option<VariantDef> { | ||
115 | self.analyze(record_pat.syntax()).resolve_record_pattern(record_pat) | ||
116 | } | ||
117 | |||
118 | pub fn resolve_macro_call(&self, macro_call: &ast::MacroCall) -> Option<MacroDef> { | ||
119 | let sa = self.analyze(macro_call.syntax()); | ||
120 | let macro_call = self.find_file(macro_call.syntax().clone()).with_value(macro_call); | ||
121 | sa.resolve_macro_call(self.db, macro_call) | ||
122 | } | ||
123 | |||
124 | pub fn resolve_path(&self, path: &ast::Path) -> Option<PathResolution> { | ||
125 | self.analyze(path.syntax()).resolve_path(self.db, path) | ||
126 | } | ||
127 | |||
128 | // FIXME: use this instead? | ||
129 | // pub fn resolve_name_ref(&self, name_ref: &ast::NameRef) -> Option<???>; | ||
130 | |||
131 | pub fn to_def<T: ToDef + Clone>(&self, src: &T) -> Option<T::Def> { | ||
132 | let src = self.find_file(src.syntax().clone()).with_value(src.clone()); | ||
133 | let mut sb = self.sb.borrow_mut(); | ||
134 | T::to_def(self.db, &mut sb, src) | ||
135 | } | ||
136 | |||
137 | pub fn to_module_def(&self, file: FileId) -> Option<Module> { | ||
138 | let mut sb = self.sb.borrow_mut(); | ||
139 | sb.to_module_def(self.db, file) | ||
140 | } | ||
141 | |||
142 | pub fn scope(&self, node: &SyntaxNode) -> SemanticsScope<'db, DB> { | ||
143 | let node = self.find_file(node.clone()); | ||
144 | let resolver = self.analyze2(node.as_ref(), None).resolver; | ||
145 | SemanticsScope { db: self.db, resolver } | ||
146 | } | ||
147 | |||
148 | pub fn scope_at_offset(&self, node: &SyntaxNode, offset: TextUnit) -> SemanticsScope<'db, DB> { | ||
149 | let node = self.find_file(node.clone()); | ||
150 | let resolver = self.analyze2(node.as_ref(), Some(offset)).resolver; | ||
151 | SemanticsScope { db: self.db, resolver } | ||
152 | } | ||
153 | |||
154 | pub fn scope_for_def(&self, def: Trait) -> SemanticsScope<'db, DB> { | ||
155 | let resolver = def.id.resolver(self.db); | ||
156 | SemanticsScope { db: self.db, resolver } | ||
157 | } | ||
158 | |||
159 | // FIXME: we only use this in `inline_local_variable` assist, ideally, we | ||
160 | // should switch to general reference search infra there. | ||
161 | pub fn find_all_refs(&self, pat: &ast::BindPat) -> Vec<ReferenceDescriptor> { | ||
162 | self.analyze(pat.syntax()).find_all_refs(pat) | ||
163 | } | ||
164 | |||
165 | fn analyze(&self, node: &SyntaxNode) -> SourceAnalyzer { | ||
166 | let src = self.find_file(node.clone()); | ||
167 | self.analyze2(src.as_ref(), None) | ||
168 | } | ||
169 | |||
170 | fn analyze2(&self, src: InFile<&SyntaxNode>, offset: Option<TextUnit>) -> SourceAnalyzer { | ||
171 | let _p = profile("Semantics::analyze2"); | ||
172 | |||
173 | let container = match self.sb.borrow_mut().find_container(self.db, src) { | ||
174 | Some(it) => it, | ||
175 | None => return SourceAnalyzer::new_for_resolver(Resolver::default(), src), | ||
176 | }; | ||
177 | |||
178 | let resolver = match container { | ||
179 | ChildContainer::DefWithBodyId(def) => { | ||
180 | return SourceAnalyzer::new_for_body(self.db, def, src, offset) | ||
181 | } | ||
182 | ChildContainer::TraitId(it) => it.resolver(self.db), | ||
183 | ChildContainer::ImplId(it) => it.resolver(self.db), | ||
184 | ChildContainer::ModuleId(it) => it.resolver(self.db), | ||
185 | ChildContainer::EnumId(it) => it.resolver(self.db), | ||
186 | ChildContainer::VariantId(it) => it.resolver(self.db), | ||
187 | ChildContainer::GenericDefId(it) => it.resolver(self.db), | ||
188 | }; | ||
189 | SourceAnalyzer::new_for_resolver(resolver, src) | ||
190 | } | ||
191 | |||
192 | fn cache(&self, root_node: SyntaxNode, file_id: HirFileId) { | ||
193 | assert!(root_node.parent().is_none()); | ||
194 | let mut cache = self.cache.borrow_mut(); | ||
195 | let prev = cache.insert(root_node, file_id); | ||
196 | assert!(prev == None || prev == Some(file_id)) | ||
197 | } | ||
198 | |||
199 | pub fn assert_contains_node(&self, node: &SyntaxNode) { | ||
200 | self.find_file(node.clone()); | ||
201 | } | ||
202 | |||
203 | fn lookup(&self, root_node: &SyntaxNode) -> Option<HirFileId> { | ||
204 | let cache = self.cache.borrow(); | ||
205 | cache.get(root_node).copied() | ||
206 | } | ||
207 | |||
208 | fn find_file(&self, node: SyntaxNode) -> InFile<SyntaxNode> { | ||
209 | let root_node = find_root(&node); | ||
210 | let file_id = self.lookup(&root_node).unwrap_or_else(|| { | ||
211 | panic!( | ||
212 | "\n\nFailed to lookup {:?} in this Semantics.\n\ | ||
213 | Make sure to use only query nodes, derived from this instance of Semantics.\n\ | ||
214 | root node: {:?}\n\ | ||
215 | known nodes: {}\n\n", | ||
216 | node, | ||
217 | root_node, | ||
218 | self.cache | ||
219 | .borrow() | ||
220 | .keys() | ||
221 | .map(|it| format!("{:?}", it)) | ||
222 | .collect::<Vec<_>>() | ||
223 | .join(", ") | ||
224 | ) | ||
225 | }); | ||
226 | InFile::new(file_id, node) | ||
227 | } | ||
228 | } | ||
229 | |||
230 | fn find_root(node: &SyntaxNode) -> SyntaxNode { | ||
231 | node.ancestors().last().unwrap() | ||
232 | } | ||
233 | |||
234 | pub struct SemanticsScope<'a, DB> { | ||
235 | pub db: &'a DB, | ||
236 | resolver: Resolver, | ||
237 | } | ||
238 | |||
239 | impl<'a, DB: HirDatabase> SemanticsScope<'a, DB> { | ||
240 | pub fn module(&self) -> Option<Module> { | ||
241 | Some(Module { id: self.resolver.module()? }) | ||
242 | } | ||
243 | |||
244 | /// Note: `FxHashSet<TraitId>` should be treated as an opaque type, passed into `Type | ||
245 | // FIXME: rename to visible_traits to not repeat scope? | ||
246 | pub fn traits_in_scope(&self) -> FxHashSet<TraitId> { | ||
247 | let resolver = &self.resolver; | ||
248 | resolver.traits_in_scope(self.db) | ||
249 | } | ||
250 | |||
251 | pub fn process_all_names(&self, f: &mut dyn FnMut(Name, ScopeDef)) { | ||
252 | let resolver = &self.resolver; | ||
253 | |||
254 | resolver.process_all_names(self.db, &mut |name, def| { | ||
255 | let def = match def { | ||
256 | resolver::ScopeDef::PerNs(it) => it.into(), | ||
257 | resolver::ScopeDef::ImplSelfType(it) => ScopeDef::ImplSelfType(it.into()), | ||
258 | resolver::ScopeDef::AdtSelfType(it) => ScopeDef::AdtSelfType(it.into()), | ||
259 | resolver::ScopeDef::GenericParam(id) => ScopeDef::GenericParam(TypeParam { id }), | ||
260 | resolver::ScopeDef::Local(pat_id) => { | ||
261 | let parent = resolver.body_owner().unwrap().into(); | ||
262 | ScopeDef::Local(Local { parent, pat_id }) | ||
263 | } | ||
264 | }; | ||
265 | f(name, def) | ||
266 | }) | ||
267 | } | ||
268 | |||
269 | pub fn resolve_hir_path(&self, path: &Path) -> Option<PathResolution> { | ||
270 | resolve_hir_path(self.db, &self.resolver, path) | ||
271 | } | ||
272 | } | ||
273 | |||
274 | // FIXME: Change `HasSource` trait to work with `Semantics` and remove this? | ||
275 | pub fn original_range(db: &impl HirDatabase, node: InFile<&SyntaxNode>) -> FileRange { | ||
276 | if let Some((range, Origin::Call)) = original_range_and_origin(db, node) { | ||
277 | return range; | ||
278 | } | ||
279 | |||
280 | if let Some(expansion) = node.file_id.expansion_info(db) { | ||
281 | if let Some(call_node) = expansion.call_node() { | ||
282 | return FileRange { | ||
283 | file_id: call_node.file_id.original_file(db), | ||
284 | range: call_node.value.text_range(), | ||
285 | }; | ||
286 | } | ||
287 | } | ||
288 | |||
289 | FileRange { file_id: node.file_id.original_file(db), range: node.value.text_range() } | ||
290 | } | ||
291 | |||
292 | fn original_range_and_origin( | ||
293 | db: &impl HirDatabase, | ||
294 | node: InFile<&SyntaxNode>, | ||
295 | ) -> Option<(FileRange, Origin)> { | ||
296 | let expansion = node.file_id.expansion_info(db)?; | ||
297 | |||
298 | // the input node has only one token ? | ||
299 | let single = node.value.first_token()? == node.value.last_token()?; | ||
300 | |||
301 | // FIXME: We should handle recurside macro expansions | ||
302 | let (range, origin) = node.value.descendants().find_map(|it| { | ||
303 | let first = it.first_token()?; | ||
304 | let last = it.last_token()?; | ||
305 | |||
306 | if !single && first == last { | ||
307 | return None; | ||
308 | } | ||
309 | |||
310 | // Try to map first and last tokens of node, and, if success, return the union range of mapped tokens | ||
311 | let (first, first_origin) = expansion.map_token_up(node.with_value(&first))?; | ||
312 | let (last, last_origin) = expansion.map_token_up(node.with_value(&last))?; | ||
313 | |||
314 | if first.file_id != last.file_id || first_origin != last_origin { | ||
315 | return None; | ||
316 | } | ||
317 | |||
318 | // FIXME: Add union method in TextRange | ||
319 | Some(( | ||
320 | first.with_value(union_range(first.value.text_range(), last.value.text_range())), | ||
321 | first_origin, | ||
322 | )) | ||
323 | })?; | ||
324 | |||
325 | return Some(( | ||
326 | FileRange { file_id: range.file_id.original_file(db), range: range.value }, | ||
327 | origin, | ||
328 | )); | ||
329 | |||
330 | fn union_range(a: TextRange, b: TextRange) -> TextRange { | ||
331 | let start = a.start().min(b.start()); | ||
332 | let end = a.end().max(b.end()); | ||
333 | TextRange::from_to(start, end) | ||
334 | } | ||
335 | } | ||
diff --git a/crates/ra_hir/src/source_analyzer.rs b/crates/ra_hir/src/source_analyzer.rs index efa3f8a79..bff1ecd14 100644 --- a/crates/ra_hir/src/source_analyzer.rs +++ b/crates/ra_hir/src/source_analyzer.rs | |||
@@ -14,29 +14,27 @@ use hir_def::{ | |||
14 | BodySourceMap, | 14 | BodySourceMap, |
15 | }, | 15 | }, |
16 | expr::{ExprId, PatId}, | 16 | expr::{ExprId, PatId}, |
17 | resolver::{self, resolver_for_scope, Resolver, TypeNs, ValueNs}, | 17 | resolver::{resolver_for_scope, Resolver, TypeNs, ValueNs}, |
18 | AsMacroCall, DefWithBodyId, TraitId, | 18 | AsMacroCall, DefWithBodyId, |
19 | }; | 19 | }; |
20 | use hir_expand::{hygiene::Hygiene, name::AsName, HirFileId, InFile, MacroCallId}; | 20 | use hir_expand::{hygiene::Hygiene, name::AsName, HirFileId, InFile}; |
21 | use hir_ty::{InEnvironment, InferenceResult, TraitEnvironment}; | 21 | use hir_ty::{InEnvironment, InferenceResult, TraitEnvironment}; |
22 | use ra_syntax::{ | 22 | use ra_syntax::{ |
23 | ast::{self, AstNode}, | 23 | ast::{self, AstNode}, |
24 | AstPtr, SyntaxNode, SyntaxNodePtr, SyntaxToken, TextRange, TextUnit, | 24 | AstPtr, SyntaxNode, SyntaxNodePtr, TextRange, TextUnit, |
25 | }; | 25 | }; |
26 | use rustc_hash::FxHashSet; | ||
27 | 26 | ||
28 | use crate::{ | 27 | use crate::{ |
29 | db::HirDatabase, Adt, Const, DefWithBody, EnumVariant, Function, Local, MacroDef, Name, Path, | 28 | db::HirDatabase, Adt, Const, EnumVariant, Function, Local, MacroDef, Name, Path, Static, |
30 | ScopeDef, Static, Struct, Trait, Type, TypeAlias, TypeParam, | 29 | Struct, Trait, Type, TypeAlias, TypeParam, |
31 | }; | 30 | }; |
32 | 31 | ||
33 | /// `SourceAnalyzer` is a convenience wrapper which exposes HIR API in terms of | 32 | /// `SourceAnalyzer` is a convenience wrapper which exposes HIR API in terms of |
34 | /// original source files. It should not be used inside the HIR itself. | 33 | /// original source files. It should not be used inside the HIR itself. |
35 | #[derive(Debug)] | 34 | #[derive(Debug)] |
36 | pub struct SourceAnalyzer { | 35 | pub(crate) struct SourceAnalyzer { |
37 | file_id: HirFileId, | 36 | file_id: HirFileId, |
38 | resolver: Resolver, | 37 | pub(crate) resolver: Resolver, |
39 | body_owner: Option<DefWithBody>, | ||
40 | body_source_map: Option<Arc<BodySourceMap>>, | 38 | body_source_map: Option<Arc<BodySourceMap>>, |
41 | infer: Option<Arc<InferenceResult>>, | 39 | infer: Option<Arc<InferenceResult>>, |
42 | scopes: Option<Arc<ExprScopes>>, | 40 | scopes: Option<Arc<ExprScopes>>, |
@@ -77,35 +75,7 @@ pub struct ReferenceDescriptor { | |||
77 | pub name: String, | 75 | pub name: String, |
78 | } | 76 | } |
79 | 77 | ||
80 | #[derive(Debug)] | ||
81 | pub struct Expansion { | ||
82 | macro_call_id: MacroCallId, | ||
83 | } | ||
84 | |||
85 | impl Expansion { | ||
86 | pub fn map_token_down( | ||
87 | &self, | ||
88 | db: &impl HirDatabase, | ||
89 | token: InFile<&SyntaxToken>, | ||
90 | ) -> Option<InFile<SyntaxToken>> { | ||
91 | let exp_info = self.file_id().expansion_info(db)?; | ||
92 | exp_info.map_token_down(token) | ||
93 | } | ||
94 | |||
95 | pub fn file_id(&self) -> HirFileId { | ||
96 | self.macro_call_id.as_file() | ||
97 | } | ||
98 | } | ||
99 | |||
100 | impl SourceAnalyzer { | 78 | impl SourceAnalyzer { |
101 | pub fn new( | ||
102 | db: &impl HirDatabase, | ||
103 | node: InFile<&SyntaxNode>, | ||
104 | offset: Option<TextUnit>, | ||
105 | ) -> SourceAnalyzer { | ||
106 | crate::source_binder::SourceBinder::new(db).analyze(node, offset) | ||
107 | } | ||
108 | |||
109 | pub(crate) fn new_for_body( | 79 | pub(crate) fn new_for_body( |
110 | db: &impl HirDatabase, | 80 | db: &impl HirDatabase, |
111 | def: DefWithBodyId, | 81 | def: DefWithBodyId, |
@@ -121,7 +91,6 @@ impl SourceAnalyzer { | |||
121 | let resolver = resolver_for_scope(db, def, scope); | 91 | let resolver = resolver_for_scope(db, def, scope); |
122 | SourceAnalyzer { | 92 | SourceAnalyzer { |
123 | resolver, | 93 | resolver, |
124 | body_owner: Some(def.into()), | ||
125 | body_source_map: Some(source_map), | 94 | body_source_map: Some(source_map), |
126 | infer: Some(db.infer(def)), | 95 | infer: Some(db.infer(def)), |
127 | scopes: Some(scopes), | 96 | scopes: Some(scopes), |
@@ -135,7 +104,6 @@ impl SourceAnalyzer { | |||
135 | ) -> SourceAnalyzer { | 104 | ) -> SourceAnalyzer { |
136 | SourceAnalyzer { | 105 | SourceAnalyzer { |
137 | resolver, | 106 | resolver, |
138 | body_owner: None, | ||
139 | body_source_map: None, | 107 | body_source_map: None, |
140 | infer: None, | 108 | infer: None, |
141 | scopes: None, | 109 | scopes: None, |
@@ -143,10 +111,6 @@ impl SourceAnalyzer { | |||
143 | } | 111 | } |
144 | } | 112 | } |
145 | 113 | ||
146 | pub fn module(&self) -> Option<crate::code_model::Module> { | ||
147 | Some(crate::code_model::Module { id: self.resolver.module()? }) | ||
148 | } | ||
149 | |||
150 | fn expr_id(&self, expr: &ast::Expr) -> Option<ExprId> { | 114 | fn expr_id(&self, expr: &ast::Expr) -> Option<ExprId> { |
151 | let src = InFile { file_id: self.file_id, value: expr }; | 115 | let src = InFile { file_id: self.file_id, value: expr }; |
152 | self.body_source_map.as_ref()?.node_expr(src) | 116 | self.body_source_map.as_ref()?.node_expr(src) |
@@ -180,7 +144,7 @@ impl SourceAnalyzer { | |||
180 | TraitEnvironment::lower(db, &self.resolver) | 144 | TraitEnvironment::lower(db, &self.resolver) |
181 | } | 145 | } |
182 | 146 | ||
183 | pub fn type_of(&self, db: &impl HirDatabase, expr: &ast::Expr) -> Option<Type> { | 147 | pub(crate) fn type_of(&self, db: &impl HirDatabase, expr: &ast::Expr) -> Option<Type> { |
184 | let expr_id = if let Some(expr) = self.expand_expr(db, InFile::new(self.file_id, expr)) { | 148 | let expr_id = if let Some(expr) = self.expand_expr(db, InFile::new(self.file_id, expr)) { |
185 | self.body_source_map.as_ref()?.node_expr(expr.as_ref())? | 149 | self.body_source_map.as_ref()?.node_expr(expr.as_ref())? |
186 | } else { | 150 | } else { |
@@ -192,24 +156,27 @@ impl SourceAnalyzer { | |||
192 | Some(Type { krate: self.resolver.krate()?, ty: InEnvironment { value: ty, environment } }) | 156 | Some(Type { krate: self.resolver.krate()?, ty: InEnvironment { value: ty, environment } }) |
193 | } | 157 | } |
194 | 158 | ||
195 | pub fn type_of_pat(&self, db: &impl HirDatabase, pat: &ast::Pat) -> Option<Type> { | 159 | pub(crate) fn type_of_pat(&self, db: &impl HirDatabase, pat: &ast::Pat) -> Option<Type> { |
196 | let pat_id = self.pat_id(pat)?; | 160 | let pat_id = self.pat_id(pat)?; |
197 | let ty = self.infer.as_ref()?[pat_id].clone(); | 161 | let ty = self.infer.as_ref()?[pat_id].clone(); |
198 | let environment = self.trait_env(db); | 162 | let environment = self.trait_env(db); |
199 | Some(Type { krate: self.resolver.krate()?, ty: InEnvironment { value: ty, environment } }) | 163 | Some(Type { krate: self.resolver.krate()?, ty: InEnvironment { value: ty, environment } }) |
200 | } | 164 | } |
201 | 165 | ||
202 | pub fn resolve_method_call(&self, call: &ast::MethodCallExpr) -> Option<Function> { | 166 | pub(crate) fn resolve_method_call(&self, call: &ast::MethodCallExpr) -> Option<Function> { |
203 | let expr_id = self.expr_id(&call.clone().into())?; | 167 | let expr_id = self.expr_id(&call.clone().into())?; |
204 | self.infer.as_ref()?.method_resolution(expr_id).map(Function::from) | 168 | self.infer.as_ref()?.method_resolution(expr_id).map(Function::from) |
205 | } | 169 | } |
206 | 170 | ||
207 | pub fn resolve_field(&self, field: &ast::FieldExpr) -> Option<crate::StructField> { | 171 | pub(crate) fn resolve_field(&self, field: &ast::FieldExpr) -> Option<crate::StructField> { |
208 | let expr_id = self.expr_id(&field.clone().into())?; | 172 | let expr_id = self.expr_id(&field.clone().into())?; |
209 | self.infer.as_ref()?.field_resolution(expr_id).map(|it| it.into()) | 173 | self.infer.as_ref()?.field_resolution(expr_id).map(|it| it.into()) |
210 | } | 174 | } |
211 | 175 | ||
212 | pub fn resolve_record_field(&self, field: &ast::RecordField) -> Option<crate::StructField> { | 176 | pub(crate) fn resolve_record_field( |
177 | &self, | ||
178 | field: &ast::RecordField, | ||
179 | ) -> Option<crate::StructField> { | ||
213 | let expr_id = match field.expr() { | 180 | let expr_id = match field.expr() { |
214 | Some(it) => self.expr_id(&it)?, | 181 | Some(it) => self.expr_id(&it)?, |
215 | None => { | 182 | None => { |
@@ -220,17 +187,23 @@ impl SourceAnalyzer { | |||
220 | self.infer.as_ref()?.record_field_resolution(expr_id).map(|it| it.into()) | 187 | self.infer.as_ref()?.record_field_resolution(expr_id).map(|it| it.into()) |
221 | } | 188 | } |
222 | 189 | ||
223 | pub fn resolve_record_literal(&self, record_lit: &ast::RecordLit) -> Option<crate::VariantDef> { | 190 | pub(crate) fn resolve_record_literal( |
191 | &self, | ||
192 | record_lit: &ast::RecordLit, | ||
193 | ) -> Option<crate::VariantDef> { | ||
224 | let expr_id = self.expr_id(&record_lit.clone().into())?; | 194 | let expr_id = self.expr_id(&record_lit.clone().into())?; |
225 | self.infer.as_ref()?.variant_resolution_for_expr(expr_id).map(|it| it.into()) | 195 | self.infer.as_ref()?.variant_resolution_for_expr(expr_id).map(|it| it.into()) |
226 | } | 196 | } |
227 | 197 | ||
228 | pub fn resolve_record_pattern(&self, record_pat: &ast::RecordPat) -> Option<crate::VariantDef> { | 198 | pub(crate) fn resolve_record_pattern( |
199 | &self, | ||
200 | record_pat: &ast::RecordPat, | ||
201 | ) -> Option<crate::VariantDef> { | ||
229 | let pat_id = self.pat_id(&record_pat.clone().into())?; | 202 | let pat_id = self.pat_id(&record_pat.clone().into())?; |
230 | self.infer.as_ref()?.variant_resolution_for_pat(pat_id).map(|it| it.into()) | 203 | self.infer.as_ref()?.variant_resolution_for_pat(pat_id).map(|it| it.into()) |
231 | } | 204 | } |
232 | 205 | ||
233 | pub fn resolve_macro_call( | 206 | pub(crate) fn resolve_macro_call( |
234 | &self, | 207 | &self, |
235 | db: &impl HirDatabase, | 208 | db: &impl HirDatabase, |
236 | macro_call: InFile<&ast::MacroCall>, | 209 | macro_call: InFile<&ast::MacroCall>, |
@@ -240,52 +213,11 @@ impl SourceAnalyzer { | |||
240 | self.resolver.resolve_path_as_macro(db, path.mod_path()).map(|it| it.into()) | 213 | self.resolver.resolve_path_as_macro(db, path.mod_path()).map(|it| it.into()) |
241 | } | 214 | } |
242 | 215 | ||
243 | pub fn resolve_hir_path( | 216 | pub(crate) fn resolve_path( |
244 | &self, | 217 | &self, |
245 | db: &impl HirDatabase, | 218 | db: &impl HirDatabase, |
246 | path: &crate::Path, | 219 | path: &ast::Path, |
247 | ) -> Option<PathResolution> { | 220 | ) -> Option<PathResolution> { |
248 | let types = | ||
249 | self.resolver.resolve_path_in_type_ns_fully(db, path.mod_path()).map(|ty| match ty { | ||
250 | TypeNs::SelfType(it) => PathResolution::SelfType(it.into()), | ||
251 | TypeNs::GenericParam(id) => PathResolution::TypeParam(TypeParam { id }), | ||
252 | TypeNs::AdtSelfType(it) | TypeNs::AdtId(it) => { | ||
253 | PathResolution::Def(Adt::from(it).into()) | ||
254 | } | ||
255 | TypeNs::EnumVariantId(it) => PathResolution::Def(EnumVariant::from(it).into()), | ||
256 | TypeNs::TypeAliasId(it) => PathResolution::Def(TypeAlias::from(it).into()), | ||
257 | TypeNs::BuiltinType(it) => PathResolution::Def(it.into()), | ||
258 | TypeNs::TraitId(it) => PathResolution::Def(Trait::from(it).into()), | ||
259 | }); | ||
260 | let values = | ||
261 | self.resolver.resolve_path_in_value_ns_fully(db, path.mod_path()).and_then(|val| { | ||
262 | let res = match val { | ||
263 | ValueNs::LocalBinding(pat_id) => { | ||
264 | let var = Local { parent: self.body_owner?, pat_id }; | ||
265 | PathResolution::Local(var) | ||
266 | } | ||
267 | ValueNs::FunctionId(it) => PathResolution::Def(Function::from(it).into()), | ||
268 | ValueNs::ConstId(it) => PathResolution::Def(Const::from(it).into()), | ||
269 | ValueNs::StaticId(it) => PathResolution::Def(Static::from(it).into()), | ||
270 | ValueNs::StructId(it) => PathResolution::Def(Struct::from(it).into()), | ||
271 | ValueNs::EnumVariantId(it) => PathResolution::Def(EnumVariant::from(it).into()), | ||
272 | }; | ||
273 | Some(res) | ||
274 | }); | ||
275 | |||
276 | let items = self | ||
277 | .resolver | ||
278 | .resolve_module_path_in_items(db, path.mod_path()) | ||
279 | .take_types() | ||
280 | .map(|it| PathResolution::Def(it.into())); | ||
281 | types.or(values).or(items).or_else(|| { | ||
282 | self.resolver | ||
283 | .resolve_path_as_macro(db, path.mod_path()) | ||
284 | .map(|def| PathResolution::Macro(def.into())) | ||
285 | }) | ||
286 | } | ||
287 | |||
288 | pub fn resolve_path(&self, db: &impl HirDatabase, path: &ast::Path) -> Option<PathResolution> { | ||
289 | if let Some(path_expr) = path.syntax().parent().and_then(ast::PathExpr::cast) { | 221 | if let Some(path_expr) = path.syntax().parent().and_then(ast::PathExpr::cast) { |
290 | let expr_id = self.expr_id(&path_expr.into())?; | 222 | let expr_id = self.expr_id(&path_expr.into())?; |
291 | if let Some(assoc) = self.infer.as_ref()?.assoc_resolutions_for_expr(expr_id) { | 223 | if let Some(assoc) = self.infer.as_ref()?.assoc_resolutions_for_expr(expr_id) { |
@@ -300,7 +232,7 @@ impl SourceAnalyzer { | |||
300 | } | 232 | } |
301 | // This must be a normal source file rather than macro file. | 233 | // This must be a normal source file rather than macro file. |
302 | let hir_path = crate::Path::from_ast(path.clone())?; | 234 | let hir_path = crate::Path::from_ast(path.clone())?; |
303 | self.resolve_hir_path(db, &hir_path) | 235 | resolve_hir_path(db, &self.resolver, &hir_path) |
304 | } | 236 | } |
305 | 237 | ||
306 | fn resolve_local_name(&self, name_ref: &ast::NameRef) -> Option<ScopeEntryWithSyntax> { | 238 | fn resolve_local_name(&self, name_ref: &ast::NameRef) -> Option<ScopeEntryWithSyntax> { |
@@ -315,25 +247,9 @@ impl SourceAnalyzer { | |||
315 | }) | 247 | }) |
316 | } | 248 | } |
317 | 249 | ||
318 | pub fn process_all_names(&self, db: &impl HirDatabase, f: &mut dyn FnMut(Name, ScopeDef)) { | ||
319 | self.resolver.process_all_names(db, &mut |name, def| { | ||
320 | let def = match def { | ||
321 | resolver::ScopeDef::PerNs(it) => it.into(), | ||
322 | resolver::ScopeDef::ImplSelfType(it) => ScopeDef::ImplSelfType(it.into()), | ||
323 | resolver::ScopeDef::AdtSelfType(it) => ScopeDef::AdtSelfType(it.into()), | ||
324 | resolver::ScopeDef::GenericParam(id) => ScopeDef::GenericParam(TypeParam { id }), | ||
325 | resolver::ScopeDef::Local(pat_id) => { | ||
326 | let parent = self.resolver.body_owner().unwrap().into(); | ||
327 | ScopeDef::Local(Local { parent, pat_id }) | ||
328 | } | ||
329 | }; | ||
330 | f(name, def) | ||
331 | }) | ||
332 | } | ||
333 | |||
334 | // FIXME: we only use this in `inline_local_variable` assist, ideally, we | 250 | // FIXME: we only use this in `inline_local_variable` assist, ideally, we |
335 | // should switch to general reference search infra there. | 251 | // should switch to general reference search infra there. |
336 | pub fn find_all_refs(&self, pat: &ast::BindPat) -> Vec<ReferenceDescriptor> { | 252 | pub(crate) fn find_all_refs(&self, pat: &ast::BindPat) -> Vec<ReferenceDescriptor> { |
337 | let fn_def = pat.syntax().ancestors().find_map(ast::FnDef::cast).unwrap(); | 253 | let fn_def = pat.syntax().ancestors().find_map(ast::FnDef::cast).unwrap(); |
338 | let ptr = Either::Left(AstPtr::new(&ast::Pat::from(pat.clone()))); | 254 | let ptr = Either::Left(AstPtr::new(&ast::Pat::from(pat.clone()))); |
339 | fn_def | 255 | fn_def |
@@ -351,19 +267,14 @@ impl SourceAnalyzer { | |||
351 | .collect() | 267 | .collect() |
352 | } | 268 | } |
353 | 269 | ||
354 | /// Note: `FxHashSet<TraitId>` should be treated as an opaque type, passed into `Type | 270 | pub(crate) fn expand( |
355 | pub fn traits_in_scope(&self, db: &impl HirDatabase) -> FxHashSet<TraitId> { | ||
356 | self.resolver.traits_in_scope(db) | ||
357 | } | ||
358 | |||
359 | pub fn expand( | ||
360 | &self, | 271 | &self, |
361 | db: &impl HirDatabase, | 272 | db: &impl HirDatabase, |
362 | macro_call: InFile<&ast::MacroCall>, | 273 | macro_call: InFile<&ast::MacroCall>, |
363 | ) -> Option<Expansion> { | 274 | ) -> Option<HirFileId> { |
364 | let macro_call_id = | 275 | let macro_call_id = |
365 | macro_call.as_call_id(db, |path| self.resolver.resolve_path_as_macro(db, &path))?; | 276 | macro_call.as_call_id(db, |path| self.resolver.resolve_path_as_macro(db, &path))?; |
366 | Some(Expansion { macro_call_id }) | 277 | Some(macro_call_id.as_file()) |
367 | } | 278 | } |
368 | } | 279 | } |
369 | 280 | ||
@@ -409,6 +320,47 @@ fn scope_for_offset( | |||
409 | }) | 320 | }) |
410 | } | 321 | } |
411 | 322 | ||
323 | pub(crate) fn resolve_hir_path( | ||
324 | db: &impl HirDatabase, | ||
325 | resolver: &Resolver, | ||
326 | path: &crate::Path, | ||
327 | ) -> Option<PathResolution> { | ||
328 | let types = resolver.resolve_path_in_type_ns_fully(db, path.mod_path()).map(|ty| match ty { | ||
329 | TypeNs::SelfType(it) => PathResolution::SelfType(it.into()), | ||
330 | TypeNs::GenericParam(id) => PathResolution::TypeParam(TypeParam { id }), | ||
331 | TypeNs::AdtSelfType(it) | TypeNs::AdtId(it) => PathResolution::Def(Adt::from(it).into()), | ||
332 | TypeNs::EnumVariantId(it) => PathResolution::Def(EnumVariant::from(it).into()), | ||
333 | TypeNs::TypeAliasId(it) => PathResolution::Def(TypeAlias::from(it).into()), | ||
334 | TypeNs::BuiltinType(it) => PathResolution::Def(it.into()), | ||
335 | TypeNs::TraitId(it) => PathResolution::Def(Trait::from(it).into()), | ||
336 | }); | ||
337 | let body_owner = resolver.body_owner(); | ||
338 | let values = resolver.resolve_path_in_value_ns_fully(db, path.mod_path()).and_then(|val| { | ||
339 | let res = match val { | ||
340 | ValueNs::LocalBinding(pat_id) => { | ||
341 | let var = Local { parent: body_owner?.into(), pat_id }; | ||
342 | PathResolution::Local(var) | ||
343 | } | ||
344 | ValueNs::FunctionId(it) => PathResolution::Def(Function::from(it).into()), | ||
345 | ValueNs::ConstId(it) => PathResolution::Def(Const::from(it).into()), | ||
346 | ValueNs::StaticId(it) => PathResolution::Def(Static::from(it).into()), | ||
347 | ValueNs::StructId(it) => PathResolution::Def(Struct::from(it).into()), | ||
348 | ValueNs::EnumVariantId(it) => PathResolution::Def(EnumVariant::from(it).into()), | ||
349 | }; | ||
350 | Some(res) | ||
351 | }); | ||
352 | |||
353 | let items = resolver | ||
354 | .resolve_module_path_in_items(db, path.mod_path()) | ||
355 | .take_types() | ||
356 | .map(|it| PathResolution::Def(it.into())); | ||
357 | types.or(values).or(items).or_else(|| { | ||
358 | resolver | ||
359 | .resolve_path_as_macro(db, path.mod_path()) | ||
360 | .map(|def| PathResolution::Macro(def.into())) | ||
361 | }) | ||
362 | } | ||
363 | |||
412 | // XXX: during completion, cursor might be outside of any particular | 364 | // XXX: during completion, cursor might be outside of any particular |
413 | // expression. Try to figure out the correct scope... | 365 | // expression. Try to figure out the correct scope... |
414 | fn adjust( | 366 | fn adjust( |
diff --git a/crates/ra_hir/src/source_binder.rs b/crates/ra_hir/src/source_binder.rs index f3150f578..0b8a641f9 100644 --- a/crates/ra_hir/src/source_binder.rs +++ b/crates/ra_hir/src/source_binder.rs | |||
@@ -5,112 +5,85 @@ use hir_def::{ | |||
5 | child_by_source::ChildBySource, | 5 | child_by_source::ChildBySource, |
6 | dyn_map::DynMap, | 6 | dyn_map::DynMap, |
7 | keys::{self, Key}, | 7 | keys::{self, Key}, |
8 | resolver::{HasResolver, Resolver}, | ||
9 | ConstId, DefWithBodyId, EnumId, EnumVariantId, FunctionId, GenericDefId, ImplId, ModuleId, | 8 | ConstId, DefWithBodyId, EnumId, EnumVariantId, FunctionId, GenericDefId, ImplId, ModuleId, |
10 | StaticId, StructFieldId, StructId, TraitId, TypeAliasId, UnionId, VariantId, | 9 | StaticId, StructFieldId, StructId, TraitId, TypeAliasId, UnionId, VariantId, |
11 | }; | 10 | }; |
12 | use hir_expand::{name::AsName, AstId, InFile, MacroDefId, MacroDefKind}; | 11 | use hir_expand::{name::AsName, AstId, InFile, MacroDefId, MacroDefKind}; |
12 | use ra_db::FileId; | ||
13 | use ra_prof::profile; | 13 | use ra_prof::profile; |
14 | use ra_syntax::{ | 14 | use ra_syntax::{ |
15 | ast::{self, NameOwner}, | 15 | ast::{self, NameOwner}, |
16 | match_ast, AstNode, SyntaxNode, TextUnit, | 16 | match_ast, AstNode, SyntaxNode, |
17 | }; | 17 | }; |
18 | use rustc_hash::FxHashMap; | 18 | use rustc_hash::FxHashMap; |
19 | 19 | ||
20 | use crate::{db::HirDatabase, Local, Module, SourceAnalyzer, TypeParam}; | 20 | use crate::{db::HirDatabase, Local, Module, TypeParam}; |
21 | use ra_db::FileId; | ||
22 | 21 | ||
23 | pub struct SourceBinder<'a, DB> { | 22 | pub struct SourceBinder { |
24 | pub db: &'a DB, | ||
25 | child_by_source_cache: FxHashMap<ChildContainer, DynMap>, | 23 | child_by_source_cache: FxHashMap<ChildContainer, DynMap>, |
26 | } | 24 | } |
27 | 25 | ||
28 | impl<DB: HirDatabase> SourceBinder<'_, DB> { | 26 | impl SourceBinder { |
29 | pub fn new(db: &DB) -> SourceBinder<DB> { | 27 | pub(crate) fn new() -> SourceBinder { |
30 | SourceBinder { db, child_by_source_cache: FxHashMap::default() } | 28 | SourceBinder { child_by_source_cache: FxHashMap::default() } |
31 | } | ||
32 | |||
33 | pub fn analyze( | ||
34 | &mut self, | ||
35 | src: InFile<&SyntaxNode>, | ||
36 | offset: Option<TextUnit>, | ||
37 | ) -> SourceAnalyzer { | ||
38 | let _p = profile("SourceBinder::analyzer"); | ||
39 | let container = match self.find_container(src) { | ||
40 | Some(it) => it, | ||
41 | None => return SourceAnalyzer::new_for_resolver(Resolver::default(), src), | ||
42 | }; | ||
43 | |||
44 | let resolver = match container { | ||
45 | ChildContainer::DefWithBodyId(def) => { | ||
46 | return SourceAnalyzer::new_for_body(self.db, def, src, offset) | ||
47 | } | ||
48 | ChildContainer::TraitId(it) => it.resolver(self.db), | ||
49 | ChildContainer::ImplId(it) => it.resolver(self.db), | ||
50 | ChildContainer::ModuleId(it) => it.resolver(self.db), | ||
51 | ChildContainer::EnumId(it) => it.resolver(self.db), | ||
52 | ChildContainer::VariantId(it) => it.resolver(self.db), | ||
53 | ChildContainer::GenericDefId(it) => it.resolver(self.db), | ||
54 | }; | ||
55 | SourceAnalyzer::new_for_resolver(resolver, src) | ||
56 | } | 29 | } |
57 | 30 | ||
58 | pub fn to_def<T: ToDef>(&mut self, src: InFile<T>) -> Option<T::Def> { | 31 | pub(crate) fn to_module_def(&mut self, db: &impl HirDatabase, file: FileId) -> Option<Module> { |
59 | T::to_def(self, src) | ||
60 | } | ||
61 | |||
62 | pub fn to_module_def(&mut self, file: FileId) -> Option<Module> { | ||
63 | let _p = profile("SourceBinder::to_module_def"); | 32 | let _p = profile("SourceBinder::to_module_def"); |
64 | let (krate, local_id) = self.db.relevant_crates(file).iter().find_map(|&crate_id| { | 33 | let (krate, local_id) = db.relevant_crates(file).iter().find_map(|&crate_id| { |
65 | let crate_def_map = self.db.crate_def_map(crate_id); | 34 | let crate_def_map = db.crate_def_map(crate_id); |
66 | let local_id = crate_def_map.modules_for_file(file).next()?; | 35 | let local_id = crate_def_map.modules_for_file(file).next()?; |
67 | Some((crate_id, local_id)) | 36 | Some((crate_id, local_id)) |
68 | })?; | 37 | })?; |
69 | Some(Module { id: ModuleId { krate, local_id } }) | 38 | Some(Module { id: ModuleId { krate, local_id } }) |
70 | } | 39 | } |
71 | 40 | ||
72 | fn to_id<T: ToId>(&mut self, src: InFile<T>) -> Option<T::ID> { | 41 | fn to_id<T: ToId>(&mut self, db: &impl HirDatabase, src: InFile<T>) -> Option<T::ID> { |
73 | T::to_id(self, src) | 42 | T::to_id(db, self, src) |
74 | } | 43 | } |
75 | 44 | ||
76 | fn find_container(&mut self, src: InFile<&SyntaxNode>) -> Option<ChildContainer> { | 45 | pub(crate) fn find_container( |
77 | for container in src.cloned().ancestors_with_macros(self.db).skip(1) { | 46 | &mut self, |
47 | db: &impl HirDatabase, | ||
48 | src: InFile<&SyntaxNode>, | ||
49 | ) -> Option<ChildContainer> { | ||
50 | for container in src.cloned().ancestors_with_macros(db).skip(1) { | ||
78 | let res: ChildContainer = match_ast! { | 51 | let res: ChildContainer = match_ast! { |
79 | match (container.value) { | 52 | match (container.value) { |
80 | ast::TraitDef(it) => { | 53 | ast::TraitDef(it) => { |
81 | let def: TraitId = self.to_id(container.with_value(it))?; | 54 | let def: TraitId = self.to_id(db, container.with_value(it))?; |
82 | def.into() | 55 | def.into() |
83 | }, | 56 | }, |
84 | ast::ImplBlock(it) => { | 57 | ast::ImplBlock(it) => { |
85 | let def: ImplId = self.to_id(container.with_value(it))?; | 58 | let def: ImplId = self.to_id(db, container.with_value(it))?; |
86 | def.into() | 59 | def.into() |
87 | }, | 60 | }, |
88 | ast::FnDef(it) => { | 61 | ast::FnDef(it) => { |
89 | let def: FunctionId = self.to_id(container.with_value(it))?; | 62 | let def: FunctionId = self.to_id(db, container.with_value(it))?; |
90 | DefWithBodyId::from(def).into() | 63 | DefWithBodyId::from(def).into() |
91 | }, | 64 | }, |
92 | ast::StaticDef(it) => { | 65 | ast::StaticDef(it) => { |
93 | let def: StaticId = self.to_id(container.with_value(it))?; | 66 | let def: StaticId = self.to_id(db, container.with_value(it))?; |
94 | DefWithBodyId::from(def).into() | 67 | DefWithBodyId::from(def).into() |
95 | }, | 68 | }, |
96 | ast::ConstDef(it) => { | 69 | ast::ConstDef(it) => { |
97 | let def: ConstId = self.to_id(container.with_value(it))?; | 70 | let def: ConstId = self.to_id(db, container.with_value(it))?; |
98 | DefWithBodyId::from(def).into() | 71 | DefWithBodyId::from(def).into() |
99 | }, | 72 | }, |
100 | ast::EnumDef(it) => { | 73 | ast::EnumDef(it) => { |
101 | let def: EnumId = self.to_id(container.with_value(it))?; | 74 | let def: EnumId = self.to_id(db, container.with_value(it))?; |
102 | def.into() | 75 | def.into() |
103 | }, | 76 | }, |
104 | ast::StructDef(it) => { | 77 | ast::StructDef(it) => { |
105 | let def: StructId = self.to_id(container.with_value(it))?; | 78 | let def: StructId = self.to_id(db, container.with_value(it))?; |
106 | VariantId::from(def).into() | 79 | VariantId::from(def).into() |
107 | }, | 80 | }, |
108 | ast::UnionDef(it) => { | 81 | ast::UnionDef(it) => { |
109 | let def: UnionId = self.to_id(container.with_value(it))?; | 82 | let def: UnionId = self.to_id(db, container.with_value(it))?; |
110 | VariantId::from(def).into() | 83 | VariantId::from(def).into() |
111 | }, | 84 | }, |
112 | ast::Module(it) => { | 85 | ast::Module(it) => { |
113 | let def: ModuleId = self.to_id(container.with_value(it))?; | 86 | let def: ModuleId = self.to_id(db, container.with_value(it))?; |
114 | def.into() | 87 | def.into() |
115 | }, | 88 | }, |
116 | _ => { continue }, | 89 | _ => { continue }, |
@@ -119,12 +92,11 @@ impl<DB: HirDatabase> SourceBinder<'_, DB> { | |||
119 | return Some(res); | 92 | return Some(res); |
120 | } | 93 | } |
121 | 94 | ||
122 | let c = self.to_module_def(src.file_id.original_file(self.db))?; | 95 | let c = self.to_module_def(db, src.file_id.original_file(db))?; |
123 | Some(c.id.into()) | 96 | Some(c.id.into()) |
124 | } | 97 | } |
125 | 98 | ||
126 | fn child_by_source(&mut self, container: ChildContainer) -> &DynMap { | 99 | fn child_by_source(&mut self, db: &impl HirDatabase, container: ChildContainer) -> &DynMap { |
127 | let db = self.db; | ||
128 | self.child_by_source_cache.entry(container).or_insert_with(|| match container { | 100 | self.child_by_source_cache.entry(container).or_insert_with(|| match container { |
129 | ChildContainer::DefWithBodyId(it) => it.child_by_source(db), | 101 | ChildContainer::DefWithBodyId(it) => it.child_by_source(db), |
130 | ChildContainer::ModuleId(it) => it.child_by_source(db), | 102 | ChildContainer::ModuleId(it) => it.child_by_source(db), |
@@ -137,16 +109,20 @@ impl<DB: HirDatabase> SourceBinder<'_, DB> { | |||
137 | } | 109 | } |
138 | } | 110 | } |
139 | 111 | ||
140 | pub trait ToId: Sized { | 112 | pub(crate) trait ToId: Sized { |
141 | type ID: Sized + Copy + 'static; | 113 | type ID: Sized + Copy + 'static; |
142 | fn to_id<DB: HirDatabase>(sb: &mut SourceBinder<'_, DB>, src: InFile<Self>) | 114 | fn to_id<DB: HirDatabase>( |
143 | -> Option<Self::ID>; | 115 | db: &DB, |
116 | sb: &mut SourceBinder, | ||
117 | src: InFile<Self>, | ||
118 | ) -> Option<Self::ID>; | ||
144 | } | 119 | } |
145 | 120 | ||
146 | pub trait ToDef: Sized + AstNode + 'static { | 121 | pub trait ToDef: Sized + AstNode + 'static { |
147 | type Def; | 122 | type Def; |
148 | fn to_def<DB: HirDatabase>( | 123 | fn to_def<DB: HirDatabase>( |
149 | sb: &mut SourceBinder<'_, DB>, | 124 | db: &DB, |
125 | sb: &mut SourceBinder, | ||
150 | src: InFile<Self>, | 126 | src: InFile<Self>, |
151 | ) -> Option<Self::Def>; | 127 | ) -> Option<Self::Def>; |
152 | } | 128 | } |
@@ -155,9 +131,9 @@ macro_rules! to_def_impls { | |||
155 | ($(($def:path, $ast:path)),* ,) => {$( | 131 | ($(($def:path, $ast:path)),* ,) => {$( |
156 | impl ToDef for $ast { | 132 | impl ToDef for $ast { |
157 | type Def = $def; | 133 | type Def = $def; |
158 | fn to_def<DB: HirDatabase>(sb: &mut SourceBinder<'_, DB>, src: InFile<Self>) | 134 | fn to_def<DB: HirDatabase>(db: &DB, sb: &mut SourceBinder, src: InFile<Self>) |
159 | -> Option<Self::Def> | 135 | -> Option<Self::Def> |
160 | { sb.to_id(src).map(Into::into) } | 136 | { sb.to_id(db, src).map(Into::into) } |
161 | } | 137 | } |
162 | )*} | 138 | )*} |
163 | } | 139 | } |
@@ -179,7 +155,7 @@ to_def_impls![ | |||
179 | ]; | 155 | ]; |
180 | 156 | ||
181 | #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)] | 157 | #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)] |
182 | enum ChildContainer { | 158 | pub(crate) enum ChildContainer { |
183 | DefWithBodyId(DefWithBodyId), | 159 | DefWithBodyId(DefWithBodyId), |
184 | ModuleId(ModuleId), | 160 | ModuleId(ModuleId), |
185 | TraitId(TraitId), | 161 | TraitId(TraitId), |
@@ -201,7 +177,7 @@ impl_froms! { | |||
201 | GenericDefId | 177 | GenericDefId |
202 | } | 178 | } |
203 | 179 | ||
204 | pub trait ToIdByKey: Sized + AstNode + 'static { | 180 | pub(crate) trait ToIdByKey: Sized + AstNode + 'static { |
205 | type ID: Sized + Copy + 'static; | 181 | type ID: Sized + Copy + 'static; |
206 | const KEY: Key<Self, Self::ID>; | 182 | const KEY: Key<Self, Self::ID>; |
207 | } | 183 | } |
@@ -209,11 +185,11 @@ pub trait ToIdByKey: Sized + AstNode + 'static { | |||
209 | impl<T: ToIdByKey> ToId for T { | 185 | impl<T: ToIdByKey> ToId for T { |
210 | type ID = <T as ToIdByKey>::ID; | 186 | type ID = <T as ToIdByKey>::ID; |
211 | fn to_id<DB: HirDatabase>( | 187 | fn to_id<DB: HirDatabase>( |
212 | sb: &mut SourceBinder<'_, DB>, | 188 | db: &DB, |
189 | sb: &mut SourceBinder, | ||
213 | src: InFile<Self>, | 190 | src: InFile<Self>, |
214 | ) -> Option<Self::ID> { | 191 | ) -> Option<Self::ID> { |
215 | let container = sb.find_container(src.as_ref().map(|it| it.syntax()))?; | 192 | let container = sb.find_container(db, src.as_ref().map(|it| it.syntax()))?; |
216 | let db = sb.db; | ||
217 | let dyn_map = | 193 | let dyn_map = |
218 | &*sb.child_by_source_cache.entry(container).or_insert_with(|| match container { | 194 | &*sb.child_by_source_cache.entry(container).or_insert_with(|| match container { |
219 | ChildContainer::DefWithBodyId(it) => it.child_by_source(db), | 195 | ChildContainer::DefWithBodyId(it) => it.child_by_source(db), |
@@ -255,15 +231,15 @@ to_id_key_impls![ | |||
255 | impl ToId for ast::MacroCall { | 231 | impl ToId for ast::MacroCall { |
256 | type ID = MacroDefId; | 232 | type ID = MacroDefId; |
257 | fn to_id<DB: HirDatabase>( | 233 | fn to_id<DB: HirDatabase>( |
258 | sb: &mut SourceBinder<'_, DB>, | 234 | db: &DB, |
235 | sb: &mut SourceBinder, | ||
259 | src: InFile<Self>, | 236 | src: InFile<Self>, |
260 | ) -> Option<Self::ID> { | 237 | ) -> Option<Self::ID> { |
261 | let kind = MacroDefKind::Declarative; | 238 | let kind = MacroDefKind::Declarative; |
262 | 239 | ||
263 | let krate = sb.to_module_def(src.file_id.original_file(sb.db))?.id.krate; | 240 | let krate = sb.to_module_def(db, src.file_id.original_file(db))?.id.krate; |
264 | 241 | ||
265 | let ast_id = | 242 | let ast_id = Some(AstId::new(src.file_id, db.ast_id_map(src.file_id).ast_id(&src.value))); |
266 | Some(AstId::new(src.file_id, sb.db.ast_id_map(src.file_id).ast_id(&src.value))); | ||
267 | 243 | ||
268 | Some(MacroDefId { krate: Some(krate), ast_id, kind }) | 244 | Some(MacroDefId { krate: Some(krate), ast_id, kind }) |
269 | } | 245 | } |
@@ -272,20 +248,20 @@ impl ToId for ast::MacroCall { | |||
272 | impl ToDef for ast::BindPat { | 248 | impl ToDef for ast::BindPat { |
273 | type Def = Local; | 249 | type Def = Local; |
274 | 250 | ||
275 | fn to_def<DB: HirDatabase>(sb: &mut SourceBinder<'_, DB>, src: InFile<Self>) -> Option<Local> { | 251 | fn to_def<DB: HirDatabase>(db: &DB, sb: &mut SourceBinder, src: InFile<Self>) -> Option<Local> { |
276 | let file_id = src.file_id; | 252 | let file_id = src.file_id; |
277 | let parent: DefWithBodyId = src.value.syntax().ancestors().find_map(|it| { | 253 | let parent: DefWithBodyId = src.value.syntax().ancestors().find_map(|it| { |
278 | let res = match_ast! { | 254 | let res = match_ast! { |
279 | match it { | 255 | match it { |
280 | ast::ConstDef(value) => { sb.to_id(InFile { value, file_id})?.into() }, | 256 | ast::ConstDef(value) => { sb.to_id(db, InFile { value, file_id})?.into() }, |
281 | ast::StaticDef(value) => { sb.to_id(InFile { value, file_id})?.into() }, | 257 | ast::StaticDef(value) => { sb.to_id(db, InFile { value, file_id})?.into() }, |
282 | ast::FnDef(value) => { sb.to_id(InFile { value, file_id})?.into() }, | 258 | ast::FnDef(value) => { sb.to_id(db, InFile { value, file_id})?.into() }, |
283 | _ => return None, | 259 | _ => return None, |
284 | } | 260 | } |
285 | }; | 261 | }; |
286 | Some(res) | 262 | Some(res) |
287 | })?; | 263 | })?; |
288 | let (_body, source_map) = sb.db.body_with_source_map(parent); | 264 | let (_body, source_map) = db.body_with_source_map(parent); |
289 | let src = src.map(ast::Pat::from); | 265 | let src = src.map(ast::Pat::from); |
290 | let pat_id = source_map.node_pat(src.as_ref())?; | 266 | let pat_id = source_map.node_pat(src.as_ref())?; |
291 | Some(Local { parent: parent.into(), pat_id }) | 267 | Some(Local { parent: parent.into(), pat_id }) |
@@ -296,26 +272,26 @@ impl ToDef for ast::TypeParam { | |||
296 | type Def = TypeParam; | 272 | type Def = TypeParam; |
297 | 273 | ||
298 | fn to_def<DB: HirDatabase>( | 274 | fn to_def<DB: HirDatabase>( |
299 | sb: &mut SourceBinder<'_, DB>, | 275 | db: &DB, |
276 | sb: &mut SourceBinder, | ||
300 | src: InFile<ast::TypeParam>, | 277 | src: InFile<ast::TypeParam>, |
301 | ) -> Option<TypeParam> { | 278 | ) -> Option<TypeParam> { |
302 | let mut sb = SourceBinder::new(sb.db); | ||
303 | let file_id = src.file_id; | 279 | let file_id = src.file_id; |
304 | let parent: GenericDefId = src.value.syntax().ancestors().find_map(|it| { | 280 | let parent: GenericDefId = src.value.syntax().ancestors().find_map(|it| { |
305 | let res = match_ast! { | 281 | let res = match_ast! { |
306 | match it { | 282 | match it { |
307 | ast::FnDef(value) => { sb.to_id(InFile { value, file_id})?.into() }, | 283 | ast::FnDef(value) => { sb.to_id(db, InFile { value, file_id})?.into() }, |
308 | ast::StructDef(value) => { sb.to_id(InFile { value, file_id})?.into() }, | 284 | ast::StructDef(value) => { sb.to_id(db, InFile { value, file_id})?.into() }, |
309 | ast::EnumDef(value) => { sb.to_id(InFile { value, file_id})?.into() }, | 285 | ast::EnumDef(value) => { sb.to_id(db, InFile { value, file_id})?.into() }, |
310 | ast::TraitDef(value) => { sb.to_id(InFile { value, file_id})?.into() }, | 286 | ast::TraitDef(value) => { sb.to_id(db, InFile { value, file_id})?.into() }, |
311 | ast::TypeAliasDef(value) => { sb.to_id(InFile { value, file_id})?.into() }, | 287 | ast::TypeAliasDef(value) => { sb.to_id(db, InFile { value, file_id})?.into() }, |
312 | ast::ImplBlock(value) => { sb.to_id(InFile { value, file_id})?.into() }, | 288 | ast::ImplBlock(value) => { sb.to_id(db, InFile { value, file_id})?.into() }, |
313 | _ => return None, | 289 | _ => return None, |
314 | } | 290 | } |
315 | }; | 291 | }; |
316 | Some(res) | 292 | Some(res) |
317 | })?; | 293 | })?; |
318 | let &id = sb.child_by_source(parent.into())[keys::TYPE_PARAM].get(&src)?; | 294 | let &id = sb.child_by_source(db, parent.into())[keys::TYPE_PARAM].get(&src)?; |
319 | Some(TypeParam { id }) | 295 | Some(TypeParam { id }) |
320 | } | 296 | } |
321 | } | 297 | } |
@@ -324,7 +300,8 @@ impl ToId for ast::Module { | |||
324 | type ID = ModuleId; | 300 | type ID = ModuleId; |
325 | 301 | ||
326 | fn to_id<DB: HirDatabase>( | 302 | fn to_id<DB: HirDatabase>( |
327 | sb: &mut SourceBinder<'_, DB>, | 303 | db: &DB, |
304 | sb: &mut SourceBinder, | ||
328 | src: InFile<ast::Module>, | 305 | src: InFile<ast::Module>, |
329 | ) -> Option<ModuleId> { | 306 | ) -> Option<ModuleId> { |
330 | { | 307 | { |
@@ -333,7 +310,7 @@ impl ToId for ast::Module { | |||
333 | .as_ref() | 310 | .as_ref() |
334 | .map(|it| it.syntax()) | 311 | .map(|it| it.syntax()) |
335 | .cloned() | 312 | .cloned() |
336 | .ancestors_with_macros(sb.db) | 313 | .ancestors_with_macros(db) |
337 | .skip(1) | 314 | .skip(1) |
338 | .find_map(|it| { | 315 | .find_map(|it| { |
339 | let m = ast::Module::cast(it.value.clone())?; | 316 | let m = ast::Module::cast(it.value.clone())?; |
@@ -341,15 +318,15 @@ impl ToId for ast::Module { | |||
341 | }); | 318 | }); |
342 | 319 | ||
343 | let parent_module = match parent_declaration { | 320 | let parent_module = match parent_declaration { |
344 | Some(parent_declaration) => sb.to_id(parent_declaration)?, | 321 | Some(parent_declaration) => sb.to_id(db, parent_declaration)?, |
345 | None => { | 322 | None => { |
346 | let file_id = src.file_id.original_file(sb.db); | 323 | let file_id = src.file_id.original_file(db); |
347 | sb.to_module_def(file_id)?.id | 324 | sb.to_module_def(db, file_id)?.id |
348 | } | 325 | } |
349 | }; | 326 | }; |
350 | 327 | ||
351 | let child_name = src.value.name()?.as_name(); | 328 | let child_name = src.value.name()?.as_name(); |
352 | let def_map = sb.db.crate_def_map(parent_module.krate); | 329 | let def_map = db.crate_def_map(parent_module.krate); |
353 | let child_id = *def_map[parent_module.local_id].children.get(&child_name)?; | 330 | let child_id = *def_map[parent_module.local_id].children.get(&child_name)?; |
354 | Some(ModuleId { krate: parent_module.krate, local_id: child_id }) | 331 | Some(ModuleId { krate: parent_module.krate, local_id: child_id }) |
355 | } | 332 | } |
diff --git a/crates/ra_ide/src/call_hierarchy.rs b/crates/ra_ide/src/call_hierarchy.rs index 51ac59a71..b00b6d431 100644 --- a/crates/ra_ide/src/call_hierarchy.rs +++ b/crates/ra_ide/src/call_hierarchy.rs | |||
@@ -2,13 +2,13 @@ | |||
2 | 2 | ||
3 | use indexmap::IndexMap; | 3 | use indexmap::IndexMap; |
4 | 4 | ||
5 | use hir::db::AstDatabase; | 5 | use hir::Semantics; |
6 | use ra_ide_db::RootDatabase; | 6 | use ra_ide_db::RootDatabase; |
7 | use ra_syntax::{ast, match_ast, AstNode, TextRange}; | 7 | use ra_syntax::{ast, match_ast, AstNode, TextRange}; |
8 | 8 | ||
9 | use crate::{ | 9 | use crate::{ |
10 | call_info::FnCallNode, display::ToNav, expand::descend_into_macros, goto_definition, | 10 | call_info::FnCallNode, display::ToNav, goto_definition, references, FilePosition, |
11 | references, FilePosition, NavigationTarget, RangeInfo, | 11 | NavigationTarget, RangeInfo, |
12 | }; | 12 | }; |
13 | 13 | ||
14 | #[derive(Debug, Clone)] | 14 | #[derive(Debug, Clone)] |
@@ -38,30 +38,31 @@ pub(crate) fn call_hierarchy( | |||
38 | } | 38 | } |
39 | 39 | ||
40 | pub(crate) fn incoming_calls(db: &RootDatabase, position: FilePosition) -> Option<Vec<CallItem>> { | 40 | pub(crate) fn incoming_calls(db: &RootDatabase, position: FilePosition) -> Option<Vec<CallItem>> { |
41 | let sema = Semantics::new(db); | ||
41 | // 1. Find all refs | 42 | // 1. Find all refs |
42 | // 2. Loop through refs and determine unique fndef. This will become our `from: CallHierarchyItem,` in the reply. | 43 | // 2. Loop through refs and determine unique fndef. This will become our `from: CallHierarchyItem,` in the reply. |
43 | // 3. Add ranges relative to the start of the fndef. | 44 | // 3. Add ranges relative to the start of the fndef. |
44 | let refs = references::find_all_refs(db, position, None)?; | 45 | let refs = references::find_all_refs(db, position, None)?; |
45 | 46 | ||
46 | let mut calls = CallLocations::default(); | 47 | let mut calls = CallLocations::default(); |
47 | let mut sb = hir::SourceBinder::new(db); | ||
48 | 48 | ||
49 | for reference in refs.info.references() { | 49 | for reference in refs.info.references() { |
50 | let file_id = reference.file_range.file_id; | 50 | let file_id = reference.file_range.file_id; |
51 | let file = db.parse_or_expand(file_id.into())?; | 51 | let file = sema.parse(file_id); |
52 | let file = file.syntax(); | ||
52 | let token = file.token_at_offset(reference.file_range.range.start()).next()?; | 53 | let token = file.token_at_offset(reference.file_range.range.start()).next()?; |
53 | let token = descend_into_macros(db, file_id, token); | 54 | let token = sema.descend_into_macros(token); |
54 | let syntax = token.value.parent(); | 55 | let syntax = token.parent(); |
55 | 56 | ||
56 | // This target is the containing function | 57 | // This target is the containing function |
57 | if let Some(nav) = syntax.ancestors().find_map(|node| { | 58 | if let Some(nav) = syntax.ancestors().find_map(|node| { |
58 | match_ast! { | 59 | match_ast! { |
59 | match node { | 60 | match node { |
60 | ast::FnDef(it) => { | 61 | ast::FnDef(it) => { |
61 | let def = sb.to_def(token.with_value(it))?; | 62 | let def = sema.to_def(&it)?; |
62 | Some(def.to_nav(sb.db)) | 63 | Some(def.to_nav(sema.db)) |
63 | }, | 64 | }, |
64 | _ => { None }, | 65 | _ => None, |
65 | } | 66 | } |
66 | } | 67 | } |
67 | }) { | 68 | }) { |
@@ -74,11 +75,13 @@ pub(crate) fn incoming_calls(db: &RootDatabase, position: FilePosition) -> Optio | |||
74 | } | 75 | } |
75 | 76 | ||
76 | pub(crate) fn outgoing_calls(db: &RootDatabase, position: FilePosition) -> Option<Vec<CallItem>> { | 77 | pub(crate) fn outgoing_calls(db: &RootDatabase, position: FilePosition) -> Option<Vec<CallItem>> { |
78 | let sema = Semantics::new(db); | ||
77 | let file_id = position.file_id; | 79 | let file_id = position.file_id; |
78 | let file = db.parse_or_expand(file_id.into())?; | 80 | let file = sema.parse(file_id); |
81 | let file = file.syntax(); | ||
79 | let token = file.token_at_offset(position.offset).next()?; | 82 | let token = file.token_at_offset(position.offset).next()?; |
80 | let token = descend_into_macros(db, file_id, token); | 83 | let token = sema.descend_into_macros(token); |
81 | let syntax = token.value.parent(); | 84 | let syntax = token.parent(); |
82 | 85 | ||
83 | let mut calls = CallLocations::default(); | 86 | let mut calls = CallLocations::default(); |
84 | 87 | ||
@@ -87,14 +90,11 @@ pub(crate) fn outgoing_calls(db: &RootDatabase, position: FilePosition) -> Optio | |||
87 | .filter_map(|node| FnCallNode::with_node_exact(&node)) | 90 | .filter_map(|node| FnCallNode::with_node_exact(&node)) |
88 | .filter_map(|call_node| { | 91 | .filter_map(|call_node| { |
89 | let name_ref = call_node.name_ref()?; | 92 | let name_ref = call_node.name_ref()?; |
90 | let name_ref = token.with_value(name_ref.syntax()); | ||
91 | |||
92 | let analyzer = hir::SourceAnalyzer::new(db, name_ref, None); | ||
93 | 93 | ||
94 | if let Some(func_target) = match &call_node { | 94 | if let Some(func_target) = match &call_node { |
95 | FnCallNode::CallExpr(expr) => { | 95 | FnCallNode::CallExpr(expr) => { |
96 | //FIXME: Type::as_callable is broken | 96 | //FIXME: Type::as_callable is broken |
97 | let callable_def = analyzer.type_of(db, &expr.expr()?)?.as_callable()?; | 97 | let callable_def = sema.type_of_expr(&expr.expr()?)?.as_callable()?; |
98 | match callable_def { | 98 | match callable_def { |
99 | hir::CallableDef::FunctionId(it) => { | 99 | hir::CallableDef::FunctionId(it) => { |
100 | let fn_def: hir::Function = it.into(); | 100 | let fn_def: hir::Function = it.into(); |
@@ -105,15 +105,15 @@ pub(crate) fn outgoing_calls(db: &RootDatabase, position: FilePosition) -> Optio | |||
105 | } | 105 | } |
106 | } | 106 | } |
107 | FnCallNode::MethodCallExpr(expr) => { | 107 | FnCallNode::MethodCallExpr(expr) => { |
108 | let function = analyzer.resolve_method_call(&expr)?; | 108 | let function = sema.resolve_method_call(&expr)?; |
109 | Some(function.to_nav(db)) | 109 | Some(function.to_nav(db)) |
110 | } | 110 | } |
111 | FnCallNode::MacroCallExpr(expr) => { | 111 | FnCallNode::MacroCallExpr(macro_call) => { |
112 | let macro_def = analyzer.resolve_macro_call(db, name_ref.with_value(&expr))?; | 112 | let macro_def = sema.resolve_macro_call(¯o_call)?; |
113 | Some(macro_def.to_nav(db)) | 113 | Some(macro_def.to_nav(db)) |
114 | } | 114 | } |
115 | } { | 115 | } { |
116 | Some((func_target, name_ref.value.text_range())) | 116 | Some((func_target, name_ref.syntax().text_range())) |
117 | } else { | 117 | } else { |
118 | None | 118 | None |
119 | } | 119 | } |
diff --git a/crates/ra_ide/src/call_info.rs b/crates/ra_ide/src/call_info.rs index 7c6322cb4..9a1fc0d35 100644 --- a/crates/ra_ide/src/call_info.rs +++ b/crates/ra_ide/src/call_info.rs | |||
@@ -1,5 +1,5 @@ | |||
1 | //! FIXME: write short doc here | 1 | //! FIXME: write short doc here |
2 | use hir::db::AstDatabase; | 2 | use hir::Semantics; |
3 | use ra_ide_db::RootDatabase; | 3 | use ra_ide_db::RootDatabase; |
4 | use ra_syntax::{ | 4 | use ra_syntax::{ |
5 | ast::{self, ArgListOwner}, | 5 | ast::{self, ArgListOwner}, |
@@ -7,24 +7,23 @@ use ra_syntax::{ | |||
7 | }; | 7 | }; |
8 | use test_utils::tested_by; | 8 | use test_utils::tested_by; |
9 | 9 | ||
10 | use crate::{expand::descend_into_macros, CallInfo, FilePosition, FunctionSignature}; | 10 | use crate::{CallInfo, FilePosition, FunctionSignature}; |
11 | 11 | ||
12 | /// Computes parameter information for the given call expression. | 12 | /// Computes parameter information for the given call expression. |
13 | pub(crate) fn call_info(db: &RootDatabase, position: FilePosition) -> Option<CallInfo> { | 13 | pub(crate) fn call_info(db: &RootDatabase, position: FilePosition) -> Option<CallInfo> { |
14 | let file = db.parse_or_expand(position.file_id.into())?; | 14 | let sema = Semantics::new(db); |
15 | let file = sema.parse(position.file_id); | ||
16 | let file = file.syntax(); | ||
15 | let token = file.token_at_offset(position.offset).next()?; | 17 | let token = file.token_at_offset(position.offset).next()?; |
16 | let token = descend_into_macros(db, position.file_id, token); | 18 | let token = sema.descend_into_macros(token); |
17 | 19 | ||
18 | // Find the calling expression and it's NameRef | 20 | // Find the calling expression and it's NameRef |
19 | let calling_node = FnCallNode::with_node(&token.value.parent())?; | 21 | let calling_node = FnCallNode::with_node(&token.parent())?; |
20 | let name_ref = calling_node.name_ref()?; | ||
21 | let name_ref = token.with_value(name_ref.syntax()); | ||
22 | 22 | ||
23 | let analyzer = hir::SourceAnalyzer::new(db, name_ref, None); | ||
24 | let (mut call_info, has_self) = match &calling_node { | 23 | let (mut call_info, has_self) = match &calling_node { |
25 | FnCallNode::CallExpr(expr) => { | 24 | FnCallNode::CallExpr(call) => { |
26 | //FIXME: Type::as_callable is broken | 25 | //FIXME: Type::as_callable is broken |
27 | let callable_def = analyzer.type_of(db, &expr.expr()?)?.as_callable()?; | 26 | let callable_def = sema.type_of_expr(&call.expr()?)?.as_callable()?; |
28 | match callable_def { | 27 | match callable_def { |
29 | hir::CallableDef::FunctionId(it) => { | 28 | hir::CallableDef::FunctionId(it) => { |
30 | let fn_def = it.into(); | 29 | let fn_def = it.into(); |
@@ -36,12 +35,12 @@ pub(crate) fn call_info(db: &RootDatabase, position: FilePosition) -> Option<Cal | |||
36 | } | 35 | } |
37 | } | 36 | } |
38 | } | 37 | } |
39 | FnCallNode::MethodCallExpr(expr) => { | 38 | FnCallNode::MethodCallExpr(method_call) => { |
40 | let function = analyzer.resolve_method_call(&expr)?; | 39 | let function = sema.resolve_method_call(&method_call)?; |
41 | (CallInfo::with_fn(db, function), function.has_self_param(db)) | 40 | (CallInfo::with_fn(db, function), function.has_self_param(db)) |
42 | } | 41 | } |
43 | FnCallNode::MacroCallExpr(expr) => { | 42 | FnCallNode::MacroCallExpr(macro_call) => { |
44 | let macro_def = analyzer.resolve_macro_call(db, name_ref.with_value(&expr))?; | 43 | let macro_def = sema.resolve_macro_call(¯o_call)?; |
45 | (CallInfo::with_macro(db, macro_def)?, false) | 44 | (CallInfo::with_macro(db, macro_def)?, false) |
46 | } | 45 | } |
47 | }; | 46 | }; |
diff --git a/crates/ra_ide/src/completion.rs b/crates/ra_ide/src/completion.rs index 4bdc6ba23..c378c2c62 100644 --- a/crates/ra_ide/src/completion.rs +++ b/crates/ra_ide/src/completion.rs | |||
@@ -17,7 +17,6 @@ mod complete_postfix; | |||
17 | mod complete_macro_in_item_position; | 17 | mod complete_macro_in_item_position; |
18 | mod complete_trait_impl; | 18 | mod complete_trait_impl; |
19 | 19 | ||
20 | use ra_db::SourceDatabase; | ||
21 | use ra_ide_db::RootDatabase; | 20 | use ra_ide_db::RootDatabase; |
22 | 21 | ||
23 | #[cfg(test)] | 22 | #[cfg(test)] |
@@ -57,8 +56,7 @@ pub use crate::completion::completion_item::{ | |||
57 | /// identifier prefix/fuzzy match should be done higher in the stack, together | 56 | /// identifier prefix/fuzzy match should be done higher in the stack, together |
58 | /// with ordering of completions (currently this is done by the client). | 57 | /// with ordering of completions (currently this is done by the client). |
59 | pub(crate) fn completions(db: &RootDatabase, position: FilePosition) -> Option<Completions> { | 58 | pub(crate) fn completions(db: &RootDatabase, position: FilePosition) -> Option<Completions> { |
60 | let original_parse = db.parse(position.file_id); | 59 | let ctx = CompletionContext::new(db, position)?; |
61 | let ctx = CompletionContext::new(db, &original_parse, position)?; | ||
62 | 60 | ||
63 | let mut acc = Completions::default(); | 61 | let mut acc = Completions::default(); |
64 | 62 | ||
diff --git a/crates/ra_ide/src/completion/complete_dot.rs b/crates/ra_ide/src/completion/complete_dot.rs index 2ca78c927..a6e0158b2 100644 --- a/crates/ra_ide/src/completion/complete_dot.rs +++ b/crates/ra_ide/src/completion/complete_dot.rs | |||
@@ -16,7 +16,7 @@ pub(super) fn complete_dot(acc: &mut Completions, ctx: &CompletionContext) { | |||
16 | _ => return, | 16 | _ => return, |
17 | }; | 17 | }; |
18 | 18 | ||
19 | let receiver_ty = match ctx.analyzer.type_of(ctx.db, &dot_receiver) { | 19 | let receiver_ty = match ctx.sema.type_of_expr(&dot_receiver) { |
20 | Some(ty) => ty, | 20 | Some(ty) => ty, |
21 | _ => return, | 21 | _ => return, |
22 | }; | 22 | }; |
@@ -55,7 +55,7 @@ fn complete_fields(acc: &mut Completions, ctx: &CompletionContext, receiver: &Ty | |||
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.module.map(|it| it.krate()) { |
57 | let mut seen_methods = FxHashSet::default(); | 57 | let mut seen_methods = FxHashSet::default(); |
58 | let traits_in_scope = ctx.analyzer.traits_in_scope(ctx.db); | 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| { |
60 | if func.has_self_param(ctx.db) && seen_methods.insert(func.name(ctx.db)) { | 60 | if func.has_self_param(ctx.db) && seen_methods.insert(func.name(ctx.db)) { |
61 | acc.add_function(ctx, func); | 61 | acc.add_function(ctx, func); |
diff --git a/crates/ra_ide/src/completion/complete_macro_in_item_position.rs b/crates/ra_ide/src/completion/complete_macro_in_item_position.rs index faadd1e3f..1866d9e6c 100644 --- a/crates/ra_ide/src/completion/complete_macro_in_item_position.rs +++ b/crates/ra_ide/src/completion/complete_macro_in_item_position.rs | |||
@@ -5,7 +5,7 @@ use crate::completion::{CompletionContext, Completions}; | |||
5 | pub(super) fn complete_macro_in_item_position(acc: &mut Completions, ctx: &CompletionContext) { | 5 | pub(super) fn complete_macro_in_item_position(acc: &mut Completions, ctx: &CompletionContext) { |
6 | // Show only macros in top level. | 6 | // Show only macros in top level. |
7 | if ctx.is_new_item { | 7 | if ctx.is_new_item { |
8 | ctx.analyzer.process_all_names(ctx.db, &mut |name, res| { | 8 | ctx.scope().process_all_names(&mut |name, res| { |
9 | if let hir::ScopeDef::MacroDef(mac) = res { | 9 | if let hir::ScopeDef::MacroDef(mac) = res { |
10 | acc.add_macro(ctx, Some(name.to_string()), mac); | 10 | acc.add_macro(ctx, Some(name.to_string()), mac); |
11 | } | 11 | } |
diff --git a/crates/ra_ide/src/completion/complete_path.rs b/crates/ra_ide/src/completion/complete_path.rs index 2d7f09a6c..c626e90cc 100644 --- a/crates/ra_ide/src/completion/complete_path.rs +++ b/crates/ra_ide/src/completion/complete_path.rs | |||
@@ -11,7 +11,7 @@ pub(super) fn complete_path(acc: &mut Completions, ctx: &CompletionContext) { | |||
11 | Some(path) => path.clone(), | 11 | Some(path) => path.clone(), |
12 | _ => return, | 12 | _ => return, |
13 | }; | 13 | }; |
14 | let def = match ctx.analyzer.resolve_hir_path(ctx.db, &path) { | 14 | let def = match ctx.scope().resolve_hir_path(&path) { |
15 | Some(PathResolution::Def(def)) => def, | 15 | Some(PathResolution::Def(def)) => def, |
16 | _ => return, | 16 | _ => return, |
17 | }; | 17 | }; |
@@ -49,7 +49,7 @@ pub(super) fn complete_path(acc: &mut Completions, ctx: &CompletionContext) { | |||
49 | // FIXME: complete T::AssocType | 49 | // FIXME: complete T::AssocType |
50 | let krate = ctx.module.map(|m| m.krate()); | 50 | let krate = ctx.module.map(|m| m.krate()); |
51 | if let Some(krate) = krate { | 51 | if let Some(krate) = krate { |
52 | let traits_in_scope = ctx.analyzer.traits_in_scope(ctx.db); | 52 | let traits_in_scope = ctx.scope().traits_in_scope(); |
53 | ty.iterate_path_candidates(ctx.db, krate, &traits_in_scope, None, |_ty, item| { | 53 | ty.iterate_path_candidates(ctx.db, krate, &traits_in_scope, None, |_ty, item| { |
54 | match item { | 54 | match item { |
55 | hir::AssocItem::Function(func) => { | 55 | hir::AssocItem::Function(func) => { |
diff --git a/crates/ra_ide/src/completion/complete_pattern.rs b/crates/ra_ide/src/completion/complete_pattern.rs index fd03b1c40..c2c6ca002 100644 --- a/crates/ra_ide/src/completion/complete_pattern.rs +++ b/crates/ra_ide/src/completion/complete_pattern.rs | |||
@@ -9,7 +9,7 @@ pub(super) fn complete_pattern(acc: &mut Completions, ctx: &CompletionContext) { | |||
9 | } | 9 | } |
10 | // FIXME: ideally, we should look at the type we are matching against and | 10 | // FIXME: ideally, we should look at the type we are matching against and |
11 | // suggest variants + auto-imports | 11 | // suggest variants + auto-imports |
12 | ctx.analyzer.process_all_names(ctx.db, &mut |name, res| { | 12 | ctx.scope().process_all_names(&mut |name, res| { |
13 | let def = match &res { | 13 | let def = match &res { |
14 | hir::ScopeDef::ModuleDef(def) => def, | 14 | hir::ScopeDef::ModuleDef(def) => def, |
15 | _ => return, | 15 | _ => return, |
diff --git a/crates/ra_ide/src/completion/complete_postfix.rs b/crates/ra_ide/src/completion/complete_postfix.rs index 5470dc291..8a74f993a 100644 --- a/crates/ra_ide/src/completion/complete_postfix.rs +++ b/crates/ra_ide/src/completion/complete_postfix.rs | |||
@@ -29,7 +29,7 @@ pub(super) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) { | |||
29 | dot_receiver.syntax().text().to_string() | 29 | dot_receiver.syntax().text().to_string() |
30 | }; | 30 | }; |
31 | 31 | ||
32 | let receiver_ty = match ctx.analyzer.type_of(ctx.db, &dot_receiver) { | 32 | let receiver_ty = match ctx.sema.type_of_expr(&dot_receiver) { |
33 | Some(it) => it, | 33 | Some(it) => it, |
34 | None => return, | 34 | None => return, |
35 | }; | 35 | }; |
diff --git a/crates/ra_ide/src/completion/complete_record_literal.rs b/crates/ra_ide/src/completion/complete_record_literal.rs index 577c394d2..f98353d76 100644 --- a/crates/ra_ide/src/completion/complete_record_literal.rs +++ b/crates/ra_ide/src/completion/complete_record_literal.rs | |||
@@ -5,10 +5,7 @@ use crate::completion::{CompletionContext, Completions}; | |||
5 | /// Complete fields in fields literals. | 5 | /// Complete fields in fields literals. |
6 | pub(super) fn complete_record_literal(acc: &mut Completions, ctx: &CompletionContext) { | 6 | pub(super) fn complete_record_literal(acc: &mut Completions, ctx: &CompletionContext) { |
7 | let (ty, variant) = match ctx.record_lit_syntax.as_ref().and_then(|it| { | 7 | let (ty, variant) = match ctx.record_lit_syntax.as_ref().and_then(|it| { |
8 | Some(( | 8 | Some((ctx.sema.type_of_expr(&it.clone().into())?, ctx.sema.resolve_record_literal(it)?)) |
9 | ctx.analyzer.type_of(ctx.db, &it.clone().into())?, | ||
10 | ctx.analyzer.resolve_record_literal(it)?, | ||
11 | )) | ||
12 | }) { | 9 | }) { |
13 | Some(it) => it, | 10 | Some(it) => it, |
14 | _ => return, | 11 | _ => return, |
diff --git a/crates/ra_ide/src/completion/complete_record_pattern.rs b/crates/ra_ide/src/completion/complete_record_pattern.rs index a56c7e3a1..9bdeae49f 100644 --- a/crates/ra_ide/src/completion/complete_record_pattern.rs +++ b/crates/ra_ide/src/completion/complete_record_pattern.rs | |||
@@ -4,10 +4,7 @@ use crate::completion::{CompletionContext, Completions}; | |||
4 | 4 | ||
5 | pub(super) fn complete_record_pattern(acc: &mut Completions, ctx: &CompletionContext) { | 5 | pub(super) fn complete_record_pattern(acc: &mut Completions, ctx: &CompletionContext) { |
6 | let (ty, variant) = match ctx.record_lit_pat.as_ref().and_then(|it| { | 6 | let (ty, variant) = match ctx.record_lit_pat.as_ref().and_then(|it| { |
7 | Some(( | 7 | Some((ctx.sema.type_of_pat(&it.clone().into())?, ctx.sema.resolve_record_pattern(it)?)) |
8 | ctx.analyzer.type_of_pat(ctx.db, &it.clone().into())?, | ||
9 | ctx.analyzer.resolve_record_pattern(it)?, | ||
10 | )) | ||
11 | }) { | 8 | }) { |
12 | Some(it) => it, | 9 | Some(it) => it, |
13 | _ => return, | 10 | _ => return, |
diff --git a/crates/ra_ide/src/completion/complete_scope.rs b/crates/ra_ide/src/completion/complete_scope.rs index e2ee86dd1..aad016d4a 100644 --- a/crates/ra_ide/src/completion/complete_scope.rs +++ b/crates/ra_ide/src/completion/complete_scope.rs | |||
@@ -7,9 +7,7 @@ pub(super) fn complete_scope(acc: &mut Completions, ctx: &CompletionContext) { | |||
7 | return; | 7 | return; |
8 | } | 8 | } |
9 | 9 | ||
10 | ctx.analyzer.process_all_names(ctx.db, &mut |name, res| { | 10 | ctx.scope().process_all_names(&mut |name, res| acc.add_resolution(ctx, name.to_string(), &res)); |
11 | acc.add_resolution(ctx, name.to_string(), &res) | ||
12 | }); | ||
13 | } | 11 | } |
14 | 12 | ||
15 | #[cfg(test)] | 13 | #[cfg(test)] |
diff --git a/crates/ra_ide/src/completion/complete_trait_impl.rs b/crates/ra_ide/src/completion/complete_trait_impl.rs index 83628e35c..9a27c164b 100644 --- a/crates/ra_ide/src/completion/complete_trait_impl.rs +++ b/crates/ra_ide/src/completion/complete_trait_impl.rs | |||
@@ -64,11 +64,12 @@ pub(crate) fn complete_trait_impl(acc: &mut Completions, ctx: &CompletionContext | |||
64 | if let (Some(trigger), Some(impl_block)) = (trigger, impl_block) { | 64 | if let (Some(trigger), Some(impl_block)) = (trigger, impl_block) { |
65 | match trigger.kind() { | 65 | match trigger.kind() { |
66 | SyntaxKind::FN_DEF => { | 66 | SyntaxKind::FN_DEF => { |
67 | for missing_fn in get_missing_impl_items(ctx.db, &ctx.analyzer, &impl_block) | 67 | for missing_fn in |
68 | .iter() | 68 | get_missing_impl_items(&ctx.sema, &impl_block).iter().filter_map(|item| { |
69 | .filter_map(|item| match item { | 69 | match item { |
70 | hir::AssocItem::Function(fn_item) => Some(fn_item), | 70 | hir::AssocItem::Function(fn_item) => Some(fn_item), |
71 | _ => None, | 71 | _ => None, |
72 | } | ||
72 | }) | 73 | }) |
73 | { | 74 | { |
74 | add_function_impl(&trigger, acc, ctx, &missing_fn); | 75 | add_function_impl(&trigger, acc, ctx, &missing_fn); |
@@ -76,11 +77,12 @@ pub(crate) fn complete_trait_impl(acc: &mut Completions, ctx: &CompletionContext | |||
76 | } | 77 | } |
77 | 78 | ||
78 | SyntaxKind::TYPE_ALIAS_DEF => { | 79 | SyntaxKind::TYPE_ALIAS_DEF => { |
79 | for missing_fn in get_missing_impl_items(ctx.db, &ctx.analyzer, &impl_block) | 80 | for missing_fn in |
80 | .iter() | 81 | get_missing_impl_items(&ctx.sema, &impl_block).iter().filter_map(|item| { |
81 | .filter_map(|item| match item { | 82 | match item { |
82 | hir::AssocItem::TypeAlias(type_item) => Some(type_item), | 83 | hir::AssocItem::TypeAlias(type_item) => Some(type_item), |
83 | _ => None, | 84 | _ => None, |
85 | } | ||
84 | }) | 86 | }) |
85 | { | 87 | { |
86 | add_type_alias_impl(&trigger, acc, ctx, &missing_fn); | 88 | add_type_alias_impl(&trigger, acc, ctx, &missing_fn); |
@@ -88,11 +90,12 @@ pub(crate) fn complete_trait_impl(acc: &mut Completions, ctx: &CompletionContext | |||
88 | } | 90 | } |
89 | 91 | ||
90 | SyntaxKind::CONST_DEF => { | 92 | SyntaxKind::CONST_DEF => { |
91 | for missing_fn in get_missing_impl_items(ctx.db, &ctx.analyzer, &impl_block) | 93 | for missing_fn in |
92 | .iter() | 94 | get_missing_impl_items(&ctx.sema, &impl_block).iter().filter_map(|item| { |
93 | .filter_map(|item| match item { | 95 | match item { |
94 | hir::AssocItem::Const(const_item) => Some(const_item), | 96 | hir::AssocItem::Const(const_item) => Some(const_item), |
95 | _ => None, | 97 | _ => None, |
98 | } | ||
96 | }) | 99 | }) |
97 | { | 100 | { |
98 | add_const_impl(&trigger, acc, ctx, &missing_fn); | 101 | add_const_impl(&trigger, acc, ctx, &missing_fn); |
diff --git a/crates/ra_ide/src/completion/completion_context.rs b/crates/ra_ide/src/completion/completion_context.rs index 8678a3234..81321a897 100644 --- a/crates/ra_ide/src/completion/completion_context.rs +++ b/crates/ra_ide/src/completion/completion_context.rs | |||
@@ -1,9 +1,11 @@ | |||
1 | //! FIXME: write short doc here | 1 | //! FIXME: write short doc here |
2 | 2 | ||
3 | use hir::{Semantics, SemanticsScope}; | ||
4 | use ra_db::SourceDatabase; | ||
3 | use ra_ide_db::RootDatabase; | 5 | use ra_ide_db::RootDatabase; |
4 | use ra_syntax::{ | 6 | use ra_syntax::{ |
5 | algo::{find_covering_element, find_node_at_offset}, | 7 | algo::{find_covering_element, find_node_at_offset}, |
6 | ast, AstNode, Parse, SourceFile, | 8 | ast, AstNode, SourceFile, |
7 | SyntaxKind::*, | 9 | SyntaxKind::*, |
8 | SyntaxNode, SyntaxToken, TextRange, TextUnit, | 10 | SyntaxNode, SyntaxToken, TextRange, TextUnit, |
9 | }; | 11 | }; |
@@ -15,8 +17,8 @@ use crate::FilePosition; | |||
15 | /// exactly is the cursor, syntax-wise. | 17 | /// exactly is the cursor, syntax-wise. |
16 | #[derive(Debug)] | 18 | #[derive(Debug)] |
17 | pub(crate) struct CompletionContext<'a> { | 19 | pub(crate) struct CompletionContext<'a> { |
20 | pub(super) sema: Semantics<'a, RootDatabase>, | ||
18 | pub(super) db: &'a RootDatabase, | 21 | pub(super) db: &'a RootDatabase, |
19 | pub(super) analyzer: hir::SourceAnalyzer, | ||
20 | pub(super) offset: TextUnit, | 22 | pub(super) offset: TextUnit, |
21 | pub(super) token: SyntaxToken, | 23 | pub(super) token: SyntaxToken, |
22 | pub(super) module: Option<hir::Module>, | 24 | pub(super) module: Option<hir::Module>, |
@@ -51,20 +53,26 @@ pub(crate) struct CompletionContext<'a> { | |||
51 | impl<'a> CompletionContext<'a> { | 53 | impl<'a> CompletionContext<'a> { |
52 | pub(super) fn new( | 54 | pub(super) fn new( |
53 | db: &'a RootDatabase, | 55 | db: &'a RootDatabase, |
54 | original_parse: &'a Parse<ast::SourceFile>, | ||
55 | position: FilePosition, | 56 | position: FilePosition, |
56 | ) -> Option<CompletionContext<'a>> { | 57 | ) -> Option<CompletionContext<'a>> { |
57 | let mut sb = hir::SourceBinder::new(db); | 58 | let sema = Semantics::new(db); |
58 | let module = sb.to_module_def(position.file_id); | 59 | |
59 | let token = | 60 | let original_file = sema.parse(position.file_id); |
60 | original_parse.tree().syntax().token_at_offset(position.offset).left_biased()?; | 61 | |
61 | let analyzer = sb.analyze( | 62 | // Insert a fake ident to get a valid parse tree. We will use this file |
62 | hir::InFile::new(position.file_id.into(), &token.parent()), | 63 | // to determine context, though the original_file will be used for |
63 | Some(position.offset), | 64 | // actual completion. |
64 | ); | 65 | let file_with_fake_ident = { |
66 | let parse = db.parse(position.file_id); | ||
67 | let edit = AtomTextEdit::insert(position.offset, "intellijRulezz".to_string()); | ||
68 | parse.reparse(&edit).tree() | ||
69 | }; | ||
70 | |||
71 | let module = sema.to_module_def(position.file_id); | ||
72 | let token = original_file.syntax().token_at_offset(position.offset).left_biased()?; | ||
65 | let mut ctx = CompletionContext { | 73 | let mut ctx = CompletionContext { |
74 | sema, | ||
66 | db, | 75 | db, |
67 | analyzer, | ||
68 | token, | 76 | token, |
69 | offset: position.offset, | 77 | offset: position.offset, |
70 | module, | 78 | module, |
@@ -87,7 +95,7 @@ impl<'a> CompletionContext<'a> { | |||
87 | has_type_args: false, | 95 | has_type_args: false, |
88 | dot_receiver_is_ambiguous_float_literal: false, | 96 | dot_receiver_is_ambiguous_float_literal: false, |
89 | }; | 97 | }; |
90 | ctx.fill(&original_parse, position.offset); | 98 | ctx.fill(&original_file, file_with_fake_ident, position.offset); |
91 | Some(ctx) | 99 | Some(ctx) |
92 | } | 100 | } |
93 | 101 | ||
@@ -100,29 +108,33 @@ impl<'a> CompletionContext<'a> { | |||
100 | } | 108 | } |
101 | } | 109 | } |
102 | 110 | ||
103 | fn fill(&mut self, original_parse: &'a Parse<ast::SourceFile>, offset: TextUnit) { | 111 | pub(crate) fn scope(&self) -> SemanticsScope<'_, RootDatabase> { |
104 | // Insert a fake ident to get a valid parse tree. We will use this file | 112 | self.sema.scope_at_offset(&self.token.parent(), self.offset) |
105 | // to determine context, though the original_file will be used for | 113 | } |
106 | // actual completion. | ||
107 | let file = { | ||
108 | let edit = AtomTextEdit::insert(offset, "intellijRulezz".to_string()); | ||
109 | original_parse.reparse(&edit).tree() | ||
110 | }; | ||
111 | 114 | ||
115 | fn fill( | ||
116 | &mut self, | ||
117 | original_file: &ast::SourceFile, | ||
118 | file_with_fake_ident: ast::SourceFile, | ||
119 | offset: TextUnit, | ||
120 | ) { | ||
112 | // First, let's try to complete a reference to some declaration. | 121 | // First, let's try to complete a reference to some declaration. |
113 | if let Some(name_ref) = find_node_at_offset::<ast::NameRef>(file.syntax(), offset) { | 122 | if let Some(name_ref) = |
123 | find_node_at_offset::<ast::NameRef>(file_with_fake_ident.syntax(), offset) | ||
124 | { | ||
114 | // Special case, `trait T { fn foo(i_am_a_name_ref) {} }`. | 125 | // Special case, `trait T { fn foo(i_am_a_name_ref) {} }`. |
115 | // See RFC#1685. | 126 | // See RFC#1685. |
116 | if is_node::<ast::Param>(name_ref.syntax()) { | 127 | if is_node::<ast::Param>(name_ref.syntax()) { |
117 | self.is_param = true; | 128 | self.is_param = true; |
118 | return; | 129 | return; |
119 | } | 130 | } |
120 | self.classify_name_ref(original_parse.tree(), name_ref); | 131 | self.classify_name_ref(original_file, name_ref); |
121 | } | 132 | } |
122 | 133 | ||
123 | // Otherwise, see if this is a declaration. We can use heuristics to | 134 | // Otherwise, see if this is a declaration. We can use heuristics to |
124 | // suggest declaration names, see `CompletionKind::Magic`. | 135 | // suggest declaration names, see `CompletionKind::Magic`. |
125 | if let Some(name) = find_node_at_offset::<ast::Name>(file.syntax(), offset) { | 136 | if let Some(name) = find_node_at_offset::<ast::Name>(file_with_fake_ident.syntax(), offset) |
137 | { | ||
126 | if let Some(bind_pat) = name.syntax().ancestors().find_map(ast::BindPat::cast) { | 138 | if let Some(bind_pat) = name.syntax().ancestors().find_map(ast::BindPat::cast) { |
127 | let parent = bind_pat.syntax().parent(); | 139 | let parent = bind_pat.syntax().parent(); |
128 | if parent.clone().and_then(ast::MatchArm::cast).is_some() | 140 | if parent.clone().and_then(ast::MatchArm::cast).is_some() |
@@ -136,13 +148,12 @@ impl<'a> CompletionContext<'a> { | |||
136 | return; | 148 | return; |
137 | } | 149 | } |
138 | if name.syntax().ancestors().find_map(ast::RecordFieldPatList::cast).is_some() { | 150 | if name.syntax().ancestors().find_map(ast::RecordFieldPatList::cast).is_some() { |
139 | self.record_lit_pat = | 151 | self.record_lit_pat = find_node_at_offset(original_file.syntax(), self.offset); |
140 | find_node_at_offset(original_parse.tree().syntax(), self.offset); | ||
141 | } | 152 | } |
142 | } | 153 | } |
143 | } | 154 | } |
144 | 155 | ||
145 | fn classify_name_ref(&mut self, original_file: SourceFile, name_ref: ast::NameRef) { | 156 | fn classify_name_ref(&mut self, original_file: &SourceFile, name_ref: ast::NameRef) { |
146 | self.name_ref_syntax = | 157 | self.name_ref_syntax = |
147 | find_node_at_offset(original_file.syntax(), name_ref.syntax().text_range().start()); | 158 | find_node_at_offset(original_file.syntax(), name_ref.syntax().text_range().start()); |
148 | let name_range = name_ref.syntax().text_range(); | 159 | let name_range = name_ref.syntax().text_range(); |
diff --git a/crates/ra_ide/src/diagnostics.rs b/crates/ra_ide/src/diagnostics.rs index 9cf86b26d..a52f7fdd9 100644 --- a/crates/ra_ide/src/diagnostics.rs +++ b/crates/ra_ide/src/diagnostics.rs | |||
@@ -2,7 +2,10 @@ | |||
2 | 2 | ||
3 | use std::cell::RefCell; | 3 | use std::cell::RefCell; |
4 | 4 | ||
5 | use hir::diagnostics::{AstDiagnostic, Diagnostic as _, DiagnosticSink}; | 5 | use hir::{ |
6 | diagnostics::{AstDiagnostic, Diagnostic as _, DiagnosticSink}, | ||
7 | Semantics, | ||
8 | }; | ||
6 | use itertools::Itertools; | 9 | use itertools::Itertools; |
7 | use ra_db::{RelativePath, SourceDatabase, SourceDatabaseExt}; | 10 | use ra_db::{RelativePath, SourceDatabase, SourceDatabaseExt}; |
8 | use ra_ide_db::RootDatabase; | 11 | use ra_ide_db::RootDatabase; |
@@ -24,7 +27,7 @@ pub enum Severity { | |||
24 | 27 | ||
25 | pub(crate) fn diagnostics(db: &RootDatabase, file_id: FileId) -> Vec<Diagnostic> { | 28 | pub(crate) fn diagnostics(db: &RootDatabase, file_id: FileId) -> Vec<Diagnostic> { |
26 | let _p = profile("diagnostics"); | 29 | let _p = profile("diagnostics"); |
27 | let mut sb = hir::SourceBinder::new(db); | 30 | let sema = Semantics::new(db); |
28 | let parse = db.parse(file_id); | 31 | let parse = db.parse(file_id); |
29 | let mut res = Vec::new(); | 32 | let mut res = Vec::new(); |
30 | 33 | ||
@@ -110,7 +113,7 @@ pub(crate) fn diagnostics(db: &RootDatabase, file_id: FileId) -> Vec<Diagnostic> | |||
110 | fix: Some(fix), | 113 | fix: Some(fix), |
111 | }) | 114 | }) |
112 | }); | 115 | }); |
113 | if let Some(m) = sb.to_module_def(file_id) { | 116 | if let Some(m) = sema.to_module_def(file_id) { |
114 | m.diagnostics(db, &mut sink); | 117 | m.diagnostics(db, &mut sink); |
115 | }; | 118 | }; |
116 | drop(sink); | 119 | drop(sink); |
diff --git a/crates/ra_ide/src/display/navigation_target.rs b/crates/ra_ide/src/display/navigation_target.rs index c9d0058a6..5afb23764 100644 --- a/crates/ra_ide/src/display/navigation_target.rs +++ b/crates/ra_ide/src/display/navigation_target.rs | |||
@@ -1,7 +1,7 @@ | |||
1 | //! FIXME: write short doc here | 1 | //! FIXME: write short doc here |
2 | 2 | ||
3 | use either::Either; | 3 | use either::Either; |
4 | use hir::{AssocItem, FieldSource, HasSource, InFile, ModuleSource}; | 4 | use hir::{original_range, AssocItem, FieldSource, HasSource, InFile, ModuleSource}; |
5 | use ra_db::{FileId, SourceDatabase}; | 5 | use ra_db::{FileId, SourceDatabase}; |
6 | use ra_ide_db::RootDatabase; | 6 | use ra_ide_db::RootDatabase; |
7 | use ra_syntax::{ | 7 | use ra_syntax::{ |
@@ -11,7 +11,11 @@ use ra_syntax::{ | |||
11 | TextRange, | 11 | TextRange, |
12 | }; | 12 | }; |
13 | 13 | ||
14 | use crate::{expand::original_range, references::NameDefinition, FileSymbol}; | 14 | use crate::{ |
15 | // expand::original_range, | ||
16 | references::NameDefinition, | ||
17 | FileSymbol, | ||
18 | }; | ||
15 | 19 | ||
16 | use super::short_label::ShortLabel; | 20 | use super::short_label::ShortLabel; |
17 | 21 | ||
diff --git a/crates/ra_ide/src/expand.rs b/crates/ra_ide/src/expand.rs deleted file mode 100644 index 9f3aaa3a3..000000000 --- a/crates/ra_ide/src/expand.rs +++ /dev/null | |||
@@ -1,102 +0,0 @@ | |||
1 | //! Utilities to work with files, produced by macros. | ||
2 | use std::iter::successors; | ||
3 | |||
4 | use hir::{InFile, Origin}; | ||
5 | use ra_db::FileId; | ||
6 | use ra_ide_db::RootDatabase; | ||
7 | use ra_syntax::{ast, AstNode, SyntaxNode, SyntaxToken, TextRange}; | ||
8 | |||
9 | use crate::FileRange; | ||
10 | |||
11 | pub(crate) fn original_range(db: &RootDatabase, node: InFile<&SyntaxNode>) -> FileRange { | ||
12 | if let Some((range, Origin::Call)) = original_range_and_origin(db, node) { | ||
13 | return range; | ||
14 | } | ||
15 | |||
16 | if let Some(expansion) = node.file_id.expansion_info(db) { | ||
17 | if let Some(call_node) = expansion.call_node() { | ||
18 | return FileRange { | ||
19 | file_id: call_node.file_id.original_file(db), | ||
20 | range: call_node.value.text_range(), | ||
21 | }; | ||
22 | } | ||
23 | } | ||
24 | |||
25 | FileRange { file_id: node.file_id.original_file(db), range: node.value.text_range() } | ||
26 | } | ||
27 | |||
28 | fn original_range_and_origin( | ||
29 | db: &RootDatabase, | ||
30 | node: InFile<&SyntaxNode>, | ||
31 | ) -> Option<(FileRange, Origin)> { | ||
32 | let expansion = node.file_id.expansion_info(db)?; | ||
33 | |||
34 | // the input node has only one token ? | ||
35 | let single = node.value.first_token()? == node.value.last_token()?; | ||
36 | |||
37 | // FIXME: We should handle recurside macro expansions | ||
38 | let (range, origin) = node.value.descendants().find_map(|it| { | ||
39 | let first = it.first_token()?; | ||
40 | let last = it.last_token()?; | ||
41 | |||
42 | if !single && first == last { | ||
43 | return None; | ||
44 | } | ||
45 | |||
46 | // Try to map first and last tokens of node, and, if success, return the union range of mapped tokens | ||
47 | let (first, first_origin) = expansion.map_token_up(node.with_value(&first))?; | ||
48 | let (last, last_origin) = expansion.map_token_up(node.with_value(&last))?; | ||
49 | |||
50 | if first.file_id != last.file_id || first_origin != last_origin { | ||
51 | return None; | ||
52 | } | ||
53 | |||
54 | // FIXME: Add union method in TextRange | ||
55 | Some(( | ||
56 | first.with_value(union_range(first.value.text_range(), last.value.text_range())), | ||
57 | first_origin, | ||
58 | )) | ||
59 | })?; | ||
60 | |||
61 | return Some(( | ||
62 | FileRange { file_id: range.file_id.original_file(db), range: range.value }, | ||
63 | origin, | ||
64 | )); | ||
65 | |||
66 | fn union_range(a: TextRange, b: TextRange) -> TextRange { | ||
67 | let start = a.start().min(b.start()); | ||
68 | let end = a.end().max(b.end()); | ||
69 | TextRange::from_to(start, end) | ||
70 | } | ||
71 | } | ||
72 | |||
73 | pub(crate) fn descend_into_macros( | ||
74 | db: &RootDatabase, | ||
75 | file_id: FileId, | ||
76 | token: SyntaxToken, | ||
77 | ) -> InFile<SyntaxToken> { | ||
78 | let src = InFile::new(file_id.into(), token); | ||
79 | |||
80 | let source_analyzer = | ||
81 | hir::SourceAnalyzer::new(db, src.with_value(src.value.parent()).as_ref(), None); | ||
82 | |||
83 | descend_into_macros_with_analyzer(db, &source_analyzer, src) | ||
84 | } | ||
85 | |||
86 | pub(crate) fn descend_into_macros_with_analyzer( | ||
87 | db: &RootDatabase, | ||
88 | source_analyzer: &hir::SourceAnalyzer, | ||
89 | src: InFile<SyntaxToken>, | ||
90 | ) -> InFile<SyntaxToken> { | ||
91 | successors(Some(src), |token| { | ||
92 | let macro_call = token.value.ancestors().find_map(ast::MacroCall::cast)?; | ||
93 | let tt = macro_call.token_tree()?; | ||
94 | if !token.value.text_range().is_subrange(&tt.syntax().text_range()) { | ||
95 | return None; | ||
96 | } | ||
97 | let exp = source_analyzer.expand(db, token.with_value(¯o_call))?; | ||
98 | exp.map_token_down(db, token.as_ref()) | ||
99 | }) | ||
100 | .last() | ||
101 | .unwrap() | ||
102 | } | ||
diff --git a/crates/ra_ide/src/expand_macro.rs b/crates/ra_ide/src/expand_macro.rs index af2783bef..f2814e684 100644 --- a/crates/ra_ide/src/expand_macro.rs +++ b/crates/ra_ide/src/expand_macro.rs | |||
@@ -1,7 +1,6 @@ | |||
1 | //! This modules implements "expand macro" functionality in the IDE | 1 | //! This modules implements "expand macro" functionality in the IDE |
2 | 2 | ||
3 | use hir::db::AstDatabase; | 3 | use hir::Semantics; |
4 | use ra_db::SourceDatabase; | ||
5 | use ra_ide_db::RootDatabase; | 4 | use ra_ide_db::RootDatabase; |
6 | use ra_syntax::{ | 5 | use ra_syntax::{ |
7 | algo::{find_node_at_offset, replace_descendants}, | 6 | algo::{find_node_at_offset, replace_descendants}, |
@@ -17,13 +16,12 @@ pub struct ExpandedMacro { | |||
17 | } | 16 | } |
18 | 17 | ||
19 | pub(crate) fn expand_macro(db: &RootDatabase, position: FilePosition) -> Option<ExpandedMacro> { | 18 | pub(crate) fn expand_macro(db: &RootDatabase, position: FilePosition) -> Option<ExpandedMacro> { |
20 | let parse = db.parse(position.file_id); | 19 | let sema = Semantics::new(db); |
21 | let file = parse.tree(); | 20 | let file = sema.parse(position.file_id); |
22 | let name_ref = find_node_at_offset::<ast::NameRef>(file.syntax(), position.offset)?; | 21 | let name_ref = find_node_at_offset::<ast::NameRef>(file.syntax(), position.offset)?; |
23 | let mac = name_ref.syntax().ancestors().find_map(ast::MacroCall::cast)?; | 22 | let mac = name_ref.syntax().ancestors().find_map(ast::MacroCall::cast)?; |
24 | 23 | ||
25 | let source = hir::InFile::new(position.file_id.into(), mac.syntax()); | 24 | let expanded = expand_macro_recur(&sema, &mac)?; |
26 | let expanded = expand_macro_recur(db, source, source.with_value(&mac))?; | ||
27 | 25 | ||
28 | // FIXME: | 26 | // FIXME: |
29 | // macro expansion may lose all white space information | 27 | // macro expansion may lose all white space information |
@@ -33,21 +31,16 @@ pub(crate) fn expand_macro(db: &RootDatabase, position: FilePosition) -> Option< | |||
33 | } | 31 | } |
34 | 32 | ||
35 | fn expand_macro_recur( | 33 | fn expand_macro_recur( |
36 | db: &RootDatabase, | 34 | sema: &Semantics<RootDatabase>, |
37 | source: hir::InFile<&SyntaxNode>, | 35 | macro_call: &ast::MacroCall, |
38 | macro_call: hir::InFile<&ast::MacroCall>, | ||
39 | ) -> Option<SyntaxNode> { | 36 | ) -> Option<SyntaxNode> { |
40 | let analyzer = hir::SourceAnalyzer::new(db, source, None); | 37 | let mut expanded = sema.expand(macro_call)?; |
41 | let expansion = analyzer.expand(db, macro_call)?; | ||
42 | let macro_file_id = expansion.file_id(); | ||
43 | let mut expanded: SyntaxNode = db.parse_or_expand(macro_file_id)?; | ||
44 | 38 | ||
45 | let children = expanded.descendants().filter_map(ast::MacroCall::cast); | 39 | let children = expanded.descendants().filter_map(ast::MacroCall::cast); |
46 | let mut replaces: FxHashMap<SyntaxElement, SyntaxElement> = FxHashMap::default(); | 40 | let mut replaces: FxHashMap<SyntaxElement, SyntaxElement> = FxHashMap::default(); |
47 | 41 | ||
48 | for child in children.into_iter() { | 42 | for child in children.into_iter() { |
49 | let node = hir::InFile::new(macro_file_id, &child); | 43 | if let Some(new_node) = expand_macro_recur(sema, &child) { |
50 | if let Some(new_node) = expand_macro_recur(db, source, node) { | ||
51 | // Replace the whole node if it is root | 44 | // Replace the whole node if it is root |
52 | // `replace_descendants` will not replace the parent node | 45 | // `replace_descendants` will not replace the parent node |
53 | // but `SyntaxNode::descendants include itself | 46 | // but `SyntaxNode::descendants include itself |
@@ -120,10 +113,12 @@ fn insert_whitespaces(syn: SyntaxNode) -> String { | |||
120 | 113 | ||
121 | #[cfg(test)] | 114 | #[cfg(test)] |
122 | mod tests { | 115 | mod tests { |
123 | use super::*; | ||
124 | use crate::mock_analysis::analysis_and_position; | ||
125 | use insta::assert_snapshot; | 116 | use insta::assert_snapshot; |
126 | 117 | ||
118 | use crate::mock_analysis::analysis_and_position; | ||
119 | |||
120 | use super::*; | ||
121 | |||
127 | fn check_expand_macro(fixture: &str) -> ExpandedMacro { | 122 | fn check_expand_macro(fixture: &str) -> ExpandedMacro { |
128 | let (analysis, pos) = analysis_and_position(fixture); | 123 | let (analysis, pos) = analysis_and_position(fixture); |
129 | analysis.expand_macro(pos).unwrap().unwrap() | 124 | analysis.expand_macro(pos).unwrap().unwrap() |
diff --git a/crates/ra_ide/src/extend_selection.rs b/crates/ra_ide/src/extend_selection.rs index 1e7d0621a..86e6f12d7 100644 --- a/crates/ra_ide/src/extend_selection.rs +++ b/crates/ra_ide/src/extend_selection.rs | |||
@@ -2,26 +2,26 @@ | |||
2 | 2 | ||
3 | use std::iter::successors; | 3 | use std::iter::successors; |
4 | 4 | ||
5 | use hir::db::AstDatabase; | 5 | use hir::Semantics; |
6 | use ra_db::SourceDatabase; | ||
7 | use ra_ide_db::RootDatabase; | 6 | use ra_ide_db::RootDatabase; |
8 | use ra_syntax::{ | 7 | use ra_syntax::{ |
9 | algo::find_covering_element, | 8 | algo::{self, find_covering_element}, |
10 | ast::{self, AstNode, AstToken}, | 9 | ast::{self, AstNode, AstToken}, |
11 | Direction, NodeOrToken, SyntaxElement, | 10 | Direction, NodeOrToken, |
12 | SyntaxKind::{self, *}, | 11 | SyntaxKind::{self, *}, |
13 | SyntaxNode, SyntaxToken, TextRange, TextUnit, TokenAtOffset, T, | 12 | SyntaxNode, SyntaxToken, TextRange, TextUnit, TokenAtOffset, T, |
14 | }; | 13 | }; |
15 | 14 | ||
16 | use crate::{expand::descend_into_macros, FileId, FileRange}; | 15 | use crate::FileRange; |
17 | 16 | ||
18 | pub(crate) fn extend_selection(db: &RootDatabase, frange: FileRange) -> TextRange { | 17 | pub(crate) fn extend_selection(db: &RootDatabase, frange: FileRange) -> TextRange { |
19 | let src = db.parse(frange.file_id).tree(); | 18 | let sema = Semantics::new(db); |
20 | try_extend_selection(db, src.syntax(), frange).unwrap_or(frange.range) | 19 | let src = sema.parse(frange.file_id); |
20 | try_extend_selection(&sema, src.syntax(), frange).unwrap_or(frange.range) | ||
21 | } | 21 | } |
22 | 22 | ||
23 | fn try_extend_selection( | 23 | fn try_extend_selection( |
24 | db: &RootDatabase, | 24 | sema: &Semantics<RootDatabase>, |
25 | root: &SyntaxNode, | 25 | root: &SyntaxNode, |
26 | frange: FileRange, | 26 | frange: FileRange, |
27 | ) -> Option<TextRange> { | 27 | ) -> Option<TextRange> { |
@@ -86,7 +86,7 @@ fn try_extend_selection( | |||
86 | // if we are in single token_tree, we maybe live in macro or attr | 86 | // if we are in single token_tree, we maybe live in macro or attr |
87 | if node.kind() == TOKEN_TREE { | 87 | if node.kind() == TOKEN_TREE { |
88 | if let Some(macro_call) = node.ancestors().find_map(ast::MacroCall::cast) { | 88 | if let Some(macro_call) = node.ancestors().find_map(ast::MacroCall::cast) { |
89 | if let Some(range) = extend_tokens_from_range(db, frange.file_id, macro_call, range) { | 89 | if let Some(range) = extend_tokens_from_range(sema, macro_call, range) { |
90 | return Some(range); | 90 | return Some(range); |
91 | } | 91 | } |
92 | } | 92 | } |
@@ -96,7 +96,7 @@ fn try_extend_selection( | |||
96 | return Some(node.text_range()); | 96 | return Some(node.text_range()); |
97 | } | 97 | } |
98 | 98 | ||
99 | let node = shallowest_node(&node.into()).unwrap(); | 99 | let node = shallowest_node(&node.into()); |
100 | 100 | ||
101 | if node.parent().map(|n| list_kinds.contains(&n.kind())) == Some(true) { | 101 | if node.parent().map(|n| list_kinds.contains(&n.kind())) == Some(true) { |
102 | if let Some(range) = extend_list_item(&node) { | 102 | if let Some(range) = extend_list_item(&node) { |
@@ -108,8 +108,7 @@ fn try_extend_selection( | |||
108 | } | 108 | } |
109 | 109 | ||
110 | fn extend_tokens_from_range( | 110 | fn extend_tokens_from_range( |
111 | db: &RootDatabase, | 111 | sema: &Semantics<RootDatabase>, |
112 | file_id: FileId, | ||
113 | macro_call: ast::MacroCall, | 112 | macro_call: ast::MacroCall, |
114 | original_range: TextRange, | 113 | original_range: TextRange, |
115 | ) -> Option<TextRange> { | 114 | ) -> Option<TextRange> { |
@@ -130,25 +129,21 @@ fn extend_tokens_from_range( | |||
130 | } | 129 | } |
131 | 130 | ||
132 | // compute original mapped token range | 131 | // compute original mapped token range |
133 | let expanded = { | 132 | let extended = { |
134 | let first_node = descend_into_macros(db, file_id, first_token.clone()); | 133 | let fst_expanded = sema.descend_into_macros(first_token.clone()); |
135 | let first_node = first_node.map(|it| it.text_range()); | 134 | let lst_expanded = sema.descend_into_macros(last_token.clone()); |
136 | 135 | let mut lca = algo::least_common_ancestor(&fst_expanded.parent(), &lst_expanded.parent())?; | |
137 | let last_node = descend_into_macros(db, file_id, last_token.clone()); | 136 | lca = shallowest_node(&lca); |
138 | if last_node.file_id == file_id.into() || first_node.file_id != last_node.file_id { | 137 | if lca.first_token() == Some(fst_expanded) && lca.last_token() == Some(lst_expanded) { |
139 | return None; | 138 | lca = lca.parent()?; |
140 | } | 139 | } |
141 | first_node.map(|it| union_range(it, last_node.value.text_range())) | 140 | lca |
142 | }; | 141 | }; |
143 | 142 | ||
144 | // Compute parent node range | 143 | // Compute parent node range |
145 | let src = db.parse_or_expand(expanded.file_id)?; | ||
146 | let parent = shallowest_node(&find_covering_element(&src, expanded.value))?.parent()?; | ||
147 | |||
148 | let validate = |token: &SyntaxToken| { | 144 | let validate = |token: &SyntaxToken| { |
149 | let node = descend_into_macros(db, file_id, token.clone()); | 145 | let expanded = sema.descend_into_macros(token.clone()); |
150 | node.file_id == expanded.file_id | 146 | algo::least_common_ancestor(&extended, &expanded.parent()).as_ref() == Some(&extended) |
151 | && node.value.text_range().is_subrange(&parent.text_range()) | ||
152 | }; | 147 | }; |
153 | 148 | ||
154 | // Find the first and last text range under expanded parent | 149 | // Find the first and last text range under expanded parent |
@@ -191,8 +186,8 @@ fn union_range(range: TextRange, r: TextRange) -> TextRange { | |||
191 | } | 186 | } |
192 | 187 | ||
193 | /// Find the shallowest node with same range, which allows us to traverse siblings. | 188 | /// Find the shallowest node with same range, which allows us to traverse siblings. |
194 | fn shallowest_node(node: &SyntaxElement) -> Option<SyntaxNode> { | 189 | fn shallowest_node(node: &SyntaxNode) -> SyntaxNode { |
195 | node.ancestors().take_while(|n| n.text_range() == node.text_range()).last() | 190 | node.ancestors().take_while(|n| n.text_range() == node.text_range()).last().unwrap() |
196 | } | 191 | } |
197 | 192 | ||
198 | fn extend_single_word_in_comment_or_string( | 193 | fn extend_single_word_in_comment_or_string( |
diff --git a/crates/ra_ide/src/goto_definition.rs b/crates/ra_ide/src/goto_definition.rs index feff1ec3f..6053c1bb6 100644 --- a/crates/ra_ide/src/goto_definition.rs +++ b/crates/ra_ide/src/goto_definition.rs | |||
@@ -1,7 +1,7 @@ | |||
1 | //! FIXME: write short doc here | 1 | //! FIXME: write short doc here |
2 | 2 | ||
3 | use hir::{db::AstDatabase, InFile, SourceBinder}; | 3 | use hir::Semantics; |
4 | use ra_ide_db::{symbol_index, RootDatabase}; | 4 | use ra_ide_db::{defs::classify_name, symbol_index, RootDatabase}; |
5 | use ra_syntax::{ | 5 | use ra_syntax::{ |
6 | ast::{self}, | 6 | ast::{self}, |
7 | match_ast, AstNode, | 7 | match_ast, AstNode, |
@@ -11,8 +11,7 @@ use ra_syntax::{ | |||
11 | 11 | ||
12 | use crate::{ | 12 | use crate::{ |
13 | display::{ToNav, TryToNav}, | 13 | display::{ToNav, TryToNav}, |
14 | expand::descend_into_macros, | 14 | references::classify_name_ref, |
15 | references::{classify_name, classify_name_ref}, | ||
16 | FilePosition, NavigationTarget, RangeInfo, | 15 | FilePosition, NavigationTarget, RangeInfo, |
17 | }; | 16 | }; |
18 | 17 | ||
@@ -20,18 +19,18 @@ pub(crate) fn goto_definition( | |||
20 | db: &RootDatabase, | 19 | db: &RootDatabase, |
21 | position: FilePosition, | 20 | position: FilePosition, |
22 | ) -> Option<RangeInfo<Vec<NavigationTarget>>> { | 21 | ) -> Option<RangeInfo<Vec<NavigationTarget>>> { |
23 | let file = db.parse_or_expand(position.file_id.into())?; | 22 | let sema = Semantics::new(db); |
23 | let file = sema.parse(position.file_id).syntax().clone(); | ||
24 | let original_token = pick_best(file.token_at_offset(position.offset))?; | 24 | let original_token = pick_best(file.token_at_offset(position.offset))?; |
25 | let token = descend_into_macros(db, position.file_id, original_token.clone()); | 25 | let token = sema.descend_into_macros(original_token.clone()); |
26 | 26 | ||
27 | let mut sb = SourceBinder::new(db); | ||
28 | let nav_targets = match_ast! { | 27 | let nav_targets = match_ast! { |
29 | match (token.value.parent()) { | 28 | match (token.parent()) { |
30 | ast::NameRef(name_ref) => { | 29 | ast::NameRef(name_ref) => { |
31 | reference_definition(&mut sb, token.with_value(&name_ref)).to_vec() | 30 | reference_definition(&sema, &name_ref).to_vec() |
32 | }, | 31 | }, |
33 | ast::Name(name) => { | 32 | ast::Name(name) => { |
34 | name_definition(&mut sb, token.with_value(&name))? | 33 | name_definition(&sema, &name)? |
35 | }, | 34 | }, |
36 | _ => return None, | 35 | _ => return None, |
37 | } | 36 | } |
@@ -68,33 +67,33 @@ impl ReferenceResult { | |||
68 | } | 67 | } |
69 | 68 | ||
70 | pub(crate) fn reference_definition( | 69 | pub(crate) fn reference_definition( |
71 | sb: &mut SourceBinder<RootDatabase>, | 70 | sema: &Semantics<RootDatabase>, |
72 | name_ref: InFile<&ast::NameRef>, | 71 | name_ref: &ast::NameRef, |
73 | ) -> ReferenceResult { | 72 | ) -> ReferenceResult { |
74 | use self::ReferenceResult::*; | 73 | use self::ReferenceResult::*; |
75 | 74 | ||
76 | let name_kind = classify_name_ref(sb, name_ref); | 75 | let name_kind = classify_name_ref(sema, name_ref); |
77 | if let Some(def) = name_kind { | 76 | if let Some(def) = name_kind { |
78 | return match def.try_to_nav(sb.db) { | 77 | return match def.try_to_nav(sema.db) { |
79 | Some(nav) => ReferenceResult::Exact(nav), | 78 | Some(nav) => ReferenceResult::Exact(nav), |
80 | None => ReferenceResult::Approximate(Vec::new()), | 79 | None => ReferenceResult::Approximate(Vec::new()), |
81 | }; | 80 | }; |
82 | } | 81 | } |
83 | 82 | ||
84 | // Fallback index based approach: | 83 | // Fallback index based approach: |
85 | let navs = symbol_index::index_resolve(sb.db, name_ref.value) | 84 | let navs = symbol_index::index_resolve(sema.db, name_ref) |
86 | .into_iter() | 85 | .into_iter() |
87 | .map(|s| s.to_nav(sb.db)) | 86 | .map(|s| s.to_nav(sema.db)) |
88 | .collect(); | 87 | .collect(); |
89 | Approximate(navs) | 88 | Approximate(navs) |
90 | } | 89 | } |
91 | 90 | ||
92 | fn name_definition( | 91 | fn name_definition( |
93 | sb: &mut SourceBinder<RootDatabase>, | 92 | sema: &Semantics<RootDatabase>, |
94 | name: InFile<&ast::Name>, | 93 | name: &ast::Name, |
95 | ) -> Option<Vec<NavigationTarget>> { | 94 | ) -> Option<Vec<NavigationTarget>> { |
96 | let def = classify_name(sb, name)?; | 95 | let def = classify_name(sema, name)?; |
97 | let nav = def.try_to_nav(sb.db)?; | 96 | let nav = def.try_to_nav(sema.db)?; |
98 | Some(vec![nav]) | 97 | Some(vec![nav]) |
99 | } | 98 | } |
100 | 99 | ||
diff --git a/crates/ra_ide/src/goto_type_definition.rs b/crates/ra_ide/src/goto_type_definition.rs index 69940fc36..869a4708b 100644 --- a/crates/ra_ide/src/goto_type_definition.rs +++ b/crates/ra_ide/src/goto_type_definition.rs | |||
@@ -1,31 +1,31 @@ | |||
1 | //! FIXME: write short doc here | 1 | //! FIXME: write short doc here |
2 | 2 | ||
3 | use hir::db::AstDatabase; | ||
4 | use ra_ide_db::RootDatabase; | 3 | use ra_ide_db::RootDatabase; |
5 | use ra_syntax::{ast, AstNode, SyntaxKind::*, SyntaxToken, TokenAtOffset}; | 4 | use ra_syntax::{ast, match_ast, AstNode, SyntaxKind::*, SyntaxToken, TokenAtOffset}; |
6 | 5 | ||
7 | use crate::{ | 6 | use crate::{display::ToNav, FilePosition, NavigationTarget, RangeInfo}; |
8 | display::ToNav, expand::descend_into_macros, FilePosition, NavigationTarget, RangeInfo, | ||
9 | }; | ||
10 | 7 | ||
11 | pub(crate) fn goto_type_definition( | 8 | pub(crate) fn goto_type_definition( |
12 | db: &RootDatabase, | 9 | db: &RootDatabase, |
13 | position: FilePosition, | 10 | position: FilePosition, |
14 | ) -> Option<RangeInfo<Vec<NavigationTarget>>> { | 11 | ) -> Option<RangeInfo<Vec<NavigationTarget>>> { |
15 | let file = db.parse_or_expand(position.file_id.into())?; | 12 | let sema = hir::Semantics::new(db); |
16 | let token = pick_best(file.token_at_offset(position.offset))?; | 13 | |
17 | let token = descend_into_macros(db, position.file_id, token); | 14 | let file: ast::SourceFile = sema.parse(position.file_id); |
18 | 15 | let token: SyntaxToken = pick_best(file.syntax().token_at_offset(position.offset))?; | |
19 | let node = token | 16 | let token: SyntaxToken = sema.descend_into_macros(token); |
20 | .value | 17 | |
21 | .ancestors() | 18 | let (ty, node) = sema.ancestors_with_macros(token.parent()).find_map(|node| { |
22 | .find(|n| ast::Expr::cast(n.clone()).is_some() || ast::Pat::cast(n.clone()).is_some())?; | 19 | let ty = match_ast! { |
23 | 20 | match node { | |
24 | let analyzer = hir::SourceAnalyzer::new(db, token.with_value(&node), None); | 21 | ast::Expr(expr) => { sema.type_of_expr(&expr)? }, |
22 | ast::Pat(pat) => { sema.type_of_pat(&pat)? }, | ||
23 | _ => { return None }, | ||
24 | } | ||
25 | }; | ||
25 | 26 | ||
26 | let ty: hir::Type = ast::Expr::cast(node.clone()) | 27 | Some((ty, node)) |
27 | .and_then(|e| analyzer.type_of(db, &e)) | 28 | })?; |
28 | .or_else(|| ast::Pat::cast(node.clone()).and_then(|p| analyzer.type_of_pat(db, &p)))?; | ||
29 | 29 | ||
30 | let adt_def = ty.autoderef(db).find_map(|ty| ty.as_adt())?; | 30 | let adt_def = ty.autoderef(db).find_map(|ty| ty.as_adt())?; |
31 | 31 | ||
diff --git a/crates/ra_ide/src/hover.rs b/crates/ra_ide/src/hover.rs index 1c6ca36df..ace33c079 100644 --- a/crates/ra_ide/src/hover.rs +++ b/crates/ra_ide/src/hover.rs | |||
@@ -1,8 +1,10 @@ | |||
1 | //! FIXME: write short doc here | 1 | //! FIXME: write short doc here |
2 | 2 | ||
3 | use hir::{db::AstDatabase, Adt, HasSource, HirDisplay, SourceBinder}; | 3 | use hir::{Adt, HasSource, HirDisplay, Semantics}; |
4 | use ra_db::SourceDatabase; | 4 | use ra_ide_db::{ |
5 | use ra_ide_db::{defs::NameDefinition, RootDatabase}; | 5 | defs::{classify_name, NameDefinition}, |
6 | RootDatabase, | ||
7 | }; | ||
6 | use ra_syntax::{ | 8 | use ra_syntax::{ |
7 | algo::find_covering_element, | 9 | algo::find_covering_element, |
8 | ast::{self, DocCommentsOwner}, | 10 | ast::{self, DocCommentsOwner}, |
@@ -13,8 +15,7 @@ use ra_syntax::{ | |||
13 | 15 | ||
14 | use crate::{ | 16 | use crate::{ |
15 | display::{macro_label, rust_code_markup, rust_code_markup_with_doc, ShortLabel}, | 17 | display::{macro_label, rust_code_markup, rust_code_markup_with_doc, ShortLabel}, |
16 | expand::{descend_into_macros, original_range}, | 18 | references::classify_name_ref, |
17 | references::{classify_name, classify_name_ref}, | ||
18 | FilePosition, FileRange, RangeInfo, | 19 | FilePosition, FileRange, RangeInfo, |
19 | }; | 20 | }; |
20 | 21 | ||
@@ -143,25 +144,25 @@ fn hover_text_from_name_kind(db: &RootDatabase, def: NameDefinition) -> Option<S | |||
143 | } | 144 | } |
144 | 145 | ||
145 | pub(crate) fn hover(db: &RootDatabase, position: FilePosition) -> Option<RangeInfo<HoverResult>> { | 146 | pub(crate) fn hover(db: &RootDatabase, position: FilePosition) -> Option<RangeInfo<HoverResult>> { |
146 | let file = db.parse_or_expand(position.file_id.into())?; | 147 | let sema = Semantics::new(db); |
148 | let file = sema.parse(position.file_id).syntax().clone(); | ||
147 | let token = pick_best(file.token_at_offset(position.offset))?; | 149 | let token = pick_best(file.token_at_offset(position.offset))?; |
148 | let token = descend_into_macros(db, position.file_id, token); | 150 | let token = sema.descend_into_macros(token); |
149 | 151 | ||
150 | let mut res = HoverResult::new(); | 152 | let mut res = HoverResult::new(); |
151 | 153 | ||
152 | let mut sb = SourceBinder::new(db); | ||
153 | if let Some((node, name_kind)) = match_ast! { | 154 | if let Some((node, name_kind)) = match_ast! { |
154 | match (token.value.parent()) { | 155 | match (token.parent()) { |
155 | ast::NameRef(name_ref) => { | 156 | ast::NameRef(name_ref) => { |
156 | classify_name_ref(&mut sb, token.with_value(&name_ref)).map(|d| (name_ref.syntax().clone(), d)) | 157 | classify_name_ref(&sema, &name_ref).map(|d| (name_ref.syntax().clone(), d)) |
157 | }, | 158 | }, |
158 | ast::Name(name) => { | 159 | ast::Name(name) => { |
159 | classify_name(&mut sb, token.with_value(&name)).map(|d| (name.syntax().clone(), d)) | 160 | classify_name(&sema, &name).map(|d| (name.syntax().clone(), d)) |
160 | }, | 161 | }, |
161 | _ => None, | 162 | _ => None, |
162 | } | 163 | } |
163 | } { | 164 | } { |
164 | let range = original_range(db, token.with_value(&node)).range; | 165 | let range = sema.original_range(&node).range; |
165 | res.extend(hover_text_from_name_kind(db, name_kind)); | 166 | res.extend(hover_text_from_name_kind(db, name_kind)); |
166 | 167 | ||
167 | if !res.is_empty() { | 168 | if !res.is_empty() { |
@@ -170,11 +171,10 @@ pub(crate) fn hover(db: &RootDatabase, position: FilePosition) -> Option<RangeIn | |||
170 | } | 171 | } |
171 | 172 | ||
172 | let node = token | 173 | let node = token |
173 | .value | ||
174 | .ancestors() | 174 | .ancestors() |
175 | .find(|n| ast::Expr::cast(n.clone()).is_some() || ast::Pat::cast(n.clone()).is_some())?; | 175 | .find(|n| ast::Expr::cast(n.clone()).is_some() || ast::Pat::cast(n.clone()).is_some())?; |
176 | 176 | ||
177 | let frange = original_range(db, token.with_value(&node)); | 177 | let frange = sema.original_range(&node); |
178 | res.extend(type_of(db, frange).map(rust_code_markup)); | 178 | res.extend(type_of(db, frange).map(rust_code_markup)); |
179 | if res.is_empty() { | 179 | if res.is_empty() { |
180 | return None; | 180 | return None; |
@@ -197,19 +197,17 @@ fn pick_best(tokens: TokenAtOffset<SyntaxToken>) -> Option<SyntaxToken> { | |||
197 | } | 197 | } |
198 | 198 | ||
199 | pub(crate) fn type_of(db: &RootDatabase, frange: FileRange) -> Option<String> { | 199 | pub(crate) fn type_of(db: &RootDatabase, frange: FileRange) -> Option<String> { |
200 | let parse = db.parse(frange.file_id); | 200 | let sema = Semantics::new(db); |
201 | let leaf_node = find_covering_element(parse.tree().syntax(), frange.range); | 201 | let source_file = sema.parse(frange.file_id); |
202 | let leaf_node = find_covering_element(source_file.syntax(), frange.range); | ||
202 | // if we picked identifier, expand to pattern/expression | 203 | // if we picked identifier, expand to pattern/expression |
203 | let node = leaf_node | 204 | let node = leaf_node |
204 | .ancestors() | 205 | .ancestors() |
205 | .take_while(|it| it.text_range() == leaf_node.text_range()) | 206 | .take_while(|it| it.text_range() == leaf_node.text_range()) |
206 | .find(|it| ast::Expr::cast(it.clone()).is_some() || ast::Pat::cast(it.clone()).is_some())?; | 207 | .find(|it| ast::Expr::cast(it.clone()).is_some() || ast::Pat::cast(it.clone()).is_some())?; |
207 | let analyzer = | 208 | let ty = if let Some(ty) = ast::Expr::cast(node.clone()).and_then(|e| sema.type_of_expr(&e)) { |
208 | hir::SourceAnalyzer::new(db, hir::InFile::new(frange.file_id.into(), &node), None); | ||
209 | let ty = if let Some(ty) = ast::Expr::cast(node.clone()).and_then(|e| analyzer.type_of(db, &e)) | ||
210 | { | ||
211 | ty | 209 | ty |
212 | } else if let Some(ty) = ast::Pat::cast(node).and_then(|p| analyzer.type_of_pat(db, &p)) { | 210 | } else if let Some(ty) = ast::Pat::cast(node).and_then(|p| sema.type_of_pat(&p)) { |
213 | ty | 211 | ty |
214 | } else { | 212 | } else { |
215 | return None; | 213 | return None; |
@@ -219,11 +217,12 @@ pub(crate) fn type_of(db: &RootDatabase, frange: FileRange) -> Option<String> { | |||
219 | 217 | ||
220 | #[cfg(test)] | 218 | #[cfg(test)] |
221 | mod tests { | 219 | mod tests { |
220 | use ra_db::FileLoader; | ||
221 | use ra_syntax::TextRange; | ||
222 | |||
222 | use crate::mock_analysis::{ | 223 | use crate::mock_analysis::{ |
223 | analysis_and_position, single_file_with_position, single_file_with_range, | 224 | analysis_and_position, single_file_with_position, single_file_with_range, |
224 | }; | 225 | }; |
225 | use ra_db::FileLoader; | ||
226 | use ra_syntax::TextRange; | ||
227 | 226 | ||
228 | fn trim_markup(s: &str) -> &str { | 227 | fn trim_markup(s: &str) -> &str { |
229 | s.trim_start_matches("```rust\n").trim_end_matches("\n```") | 228 | s.trim_start_matches("```rust\n").trim_end_matches("\n```") |
diff --git a/crates/ra_ide/src/impls.rs b/crates/ra_ide/src/impls.rs index 64a2dadc8..bf82b2a16 100644 --- a/crates/ra_ide/src/impls.rs +++ b/crates/ra_ide/src/impls.rs | |||
@@ -1,7 +1,6 @@ | |||
1 | //! FIXME: write short doc here | 1 | //! FIXME: write short doc here |
2 | 2 | ||
3 | use hir::{Crate, ImplBlock, SourceBinder}; | 3 | use hir::{Crate, ImplBlock, Semantics}; |
4 | use ra_db::SourceDatabase; | ||
5 | use ra_ide_db::RootDatabase; | 4 | use ra_ide_db::RootDatabase; |
6 | use ra_syntax::{algo::find_node_at_offset, ast, AstNode}; | 5 | use ra_syntax::{algo::find_node_at_offset, ast, AstNode}; |
7 | 6 | ||
@@ -11,21 +10,21 @@ pub(crate) fn goto_implementation( | |||
11 | db: &RootDatabase, | 10 | db: &RootDatabase, |
12 | position: FilePosition, | 11 | position: FilePosition, |
13 | ) -> Option<RangeInfo<Vec<NavigationTarget>>> { | 12 | ) -> Option<RangeInfo<Vec<NavigationTarget>>> { |
14 | let parse = db.parse(position.file_id); | 13 | let sema = Semantics::new(db); |
15 | let syntax = parse.tree().syntax().clone(); | 14 | let source_file = sema.parse(position.file_id); |
16 | let mut sb = SourceBinder::new(db); | 15 | let syntax = source_file.syntax().clone(); |
17 | 16 | ||
18 | let krate = sb.to_module_def(position.file_id)?.krate(); | 17 | let krate = sema.to_module_def(position.file_id)?.krate(); |
19 | 18 | ||
20 | if let Some(nominal_def) = find_node_at_offset::<ast::NominalDef>(&syntax, position.offset) { | 19 | if let Some(nominal_def) = find_node_at_offset::<ast::NominalDef>(&syntax, position.offset) { |
21 | return Some(RangeInfo::new( | 20 | return Some(RangeInfo::new( |
22 | nominal_def.syntax().text_range(), | 21 | nominal_def.syntax().text_range(), |
23 | impls_for_def(&mut sb, position, &nominal_def, krate)?, | 22 | impls_for_def(&sema, &nominal_def, krate)?, |
24 | )); | 23 | )); |
25 | } else if let Some(trait_def) = find_node_at_offset::<ast::TraitDef>(&syntax, position.offset) { | 24 | } else if let Some(trait_def) = find_node_at_offset::<ast::TraitDef>(&syntax, position.offset) { |
26 | return Some(RangeInfo::new( | 25 | return Some(RangeInfo::new( |
27 | trait_def.syntax().text_range(), | 26 | trait_def.syntax().text_range(), |
28 | impls_for_trait(&mut sb, position, &trait_def, krate)?, | 27 | impls_for_trait(&sema, &trait_def, krate)?, |
29 | )); | 28 | )); |
30 | } | 29 | } |
31 | 30 | ||
@@ -33,49 +32,37 @@ pub(crate) fn goto_implementation( | |||
33 | } | 32 | } |
34 | 33 | ||
35 | fn impls_for_def( | 34 | fn impls_for_def( |
36 | sb: &mut SourceBinder<RootDatabase>, | 35 | sema: &Semantics<RootDatabase>, |
37 | position: FilePosition, | ||
38 | node: &ast::NominalDef, | 36 | node: &ast::NominalDef, |
39 | krate: Crate, | 37 | krate: Crate, |
40 | ) -> Option<Vec<NavigationTarget>> { | 38 | ) -> Option<Vec<NavigationTarget>> { |
41 | let ty = match node { | 39 | let ty = match node { |
42 | ast::NominalDef::StructDef(def) => { | 40 | ast::NominalDef::StructDef(def) => sema.to_def(def)?.ty(sema.db), |
43 | let src = hir::InFile { file_id: position.file_id.into(), value: def.clone() }; | 41 | ast::NominalDef::EnumDef(def) => sema.to_def(def)?.ty(sema.db), |
44 | sb.to_def(src)?.ty(sb.db) | 42 | ast::NominalDef::UnionDef(def) => sema.to_def(def)?.ty(sema.db), |
45 | } | ||
46 | ast::NominalDef::EnumDef(def) => { | ||
47 | let src = hir::InFile { file_id: position.file_id.into(), value: def.clone() }; | ||
48 | sb.to_def(src)?.ty(sb.db) | ||
49 | } | ||
50 | ast::NominalDef::UnionDef(def) => { | ||
51 | let src = hir::InFile { file_id: position.file_id.into(), value: def.clone() }; | ||
52 | sb.to_def(src)?.ty(sb.db) | ||
53 | } | ||
54 | }; | 43 | }; |
55 | 44 | ||
56 | let impls = ImplBlock::all_in_crate(sb.db, krate); | 45 | let impls = ImplBlock::all_in_crate(sema.db, krate); |
57 | 46 | ||
58 | Some( | 47 | Some( |
59 | impls | 48 | impls |
60 | .into_iter() | 49 | .into_iter() |
61 | .filter(|impl_block| ty.is_equal_for_find_impls(&impl_block.target_ty(sb.db))) | 50 | .filter(|impl_block| ty.is_equal_for_find_impls(&impl_block.target_ty(sema.db))) |
62 | .map(|imp| imp.to_nav(sb.db)) | 51 | .map(|imp| imp.to_nav(sema.db)) |
63 | .collect(), | 52 | .collect(), |
64 | ) | 53 | ) |
65 | } | 54 | } |
66 | 55 | ||
67 | fn impls_for_trait( | 56 | fn impls_for_trait( |
68 | sb: &mut SourceBinder<RootDatabase>, | 57 | sema: &Semantics<RootDatabase>, |
69 | position: FilePosition, | ||
70 | node: &ast::TraitDef, | 58 | node: &ast::TraitDef, |
71 | krate: Crate, | 59 | krate: Crate, |
72 | ) -> Option<Vec<NavigationTarget>> { | 60 | ) -> Option<Vec<NavigationTarget>> { |
73 | let src = hir::InFile { file_id: position.file_id.into(), value: node.clone() }; | 61 | let tr = sema.to_def(node)?; |
74 | let tr = sb.to_def(src)?; | ||
75 | 62 | ||
76 | let impls = ImplBlock::for_trait(sb.db, krate, tr); | 63 | let impls = ImplBlock::for_trait(sema.db, krate, tr); |
77 | 64 | ||
78 | Some(impls.into_iter().map(|imp| imp.to_nav(sb.db)).collect()) | 65 | Some(impls.into_iter().map(|imp| imp.to_nav(sema.db)).collect()) |
79 | } | 66 | } |
80 | 67 | ||
81 | #[cfg(test)] | 68 | #[cfg(test)] |
diff --git a/crates/ra_ide/src/inlay_hints.rs b/crates/ra_ide/src/inlay_hints.rs index b42aa1523..35e3f782d 100644 --- a/crates/ra_ide/src/inlay_hints.rs +++ b/crates/ra_ide/src/inlay_hints.rs | |||
@@ -1,12 +1,11 @@ | |||
1 | //! FIXME: write short doc here | 1 | //! FIXME: write short doc here |
2 | 2 | ||
3 | use hir::{Adt, HirDisplay, SourceAnalyzer, SourceBinder, Type}; | 3 | use hir::{Adt, HirDisplay, Semantics, Type}; |
4 | use once_cell::unsync::Lazy; | ||
5 | use ra_ide_db::RootDatabase; | 4 | use ra_ide_db::RootDatabase; |
6 | use ra_prof::profile; | 5 | use ra_prof::profile; |
7 | use ra_syntax::{ | 6 | use ra_syntax::{ |
8 | ast::{self, ArgListOwner, AstNode, TypeAscriptionOwner}, | 7 | ast::{self, ArgListOwner, AstNode, TypeAscriptionOwner}, |
9 | match_ast, SmolStr, SourceFile, SyntaxNode, TextRange, | 8 | match_ast, SmolStr, SyntaxNode, TextRange, |
10 | }; | 9 | }; |
11 | 10 | ||
12 | use crate::{FileId, FunctionSignature}; | 11 | use crate::{FileId, FunctionSignature}; |
@@ -27,38 +26,36 @@ pub struct InlayHint { | |||
27 | pub(crate) fn inlay_hints( | 26 | pub(crate) fn inlay_hints( |
28 | db: &RootDatabase, | 27 | db: &RootDatabase, |
29 | file_id: FileId, | 28 | file_id: FileId, |
30 | file: &SourceFile, | ||
31 | max_inlay_hint_length: Option<usize>, | 29 | max_inlay_hint_length: Option<usize>, |
32 | ) -> Vec<InlayHint> { | 30 | ) -> Vec<InlayHint> { |
33 | let mut sb = SourceBinder::new(db); | 31 | let sema = Semantics::new(db); |
32 | let file = sema.parse(file_id); | ||
34 | let mut res = Vec::new(); | 33 | let mut res = Vec::new(); |
35 | for node in file.syntax().descendants() { | 34 | for node in file.syntax().descendants() { |
36 | get_inlay_hints(&mut res, &mut sb, file_id, &node, max_inlay_hint_length); | 35 | get_inlay_hints(&mut res, &sema, &node, max_inlay_hint_length); |
37 | } | 36 | } |
38 | res | 37 | res |
39 | } | 38 | } |
40 | 39 | ||
41 | fn get_inlay_hints( | 40 | fn get_inlay_hints( |
42 | acc: &mut Vec<InlayHint>, | 41 | acc: &mut Vec<InlayHint>, |
43 | sb: &mut SourceBinder<RootDatabase>, | 42 | sema: &Semantics<RootDatabase>, |
44 | file_id: FileId, | ||
45 | node: &SyntaxNode, | 43 | node: &SyntaxNode, |
46 | max_inlay_hint_length: Option<usize>, | 44 | max_inlay_hint_length: Option<usize>, |
47 | ) -> Option<()> { | 45 | ) -> Option<()> { |
48 | let _p = profile("get_inlay_hints"); | 46 | let _p = profile("get_inlay_hints"); |
49 | let db = sb.db; | 47 | let db = sema.db; |
50 | let analyzer = Lazy::new(move || sb.analyze(hir::InFile::new(file_id.into(), node), None)); | ||
51 | match_ast! { | 48 | match_ast! { |
52 | match node { | 49 | match node { |
53 | ast::CallExpr(it) => { | 50 | ast::CallExpr(it) => { |
54 | get_param_name_hints(acc, db, &analyzer, ast::Expr::from(it)); | 51 | get_param_name_hints(acc, sema, ast::Expr::from(it)); |
55 | }, | 52 | }, |
56 | ast::MethodCallExpr(it) => { | 53 | ast::MethodCallExpr(it) => { |
57 | get_param_name_hints(acc, db, &analyzer, ast::Expr::from(it)); | 54 | get_param_name_hints(acc, sema, ast::Expr::from(it)); |
58 | }, | 55 | }, |
59 | ast::BindPat(it) => { | 56 | ast::BindPat(it) => { |
60 | let pat = ast::Pat::from(it.clone()); | 57 | let pat = ast::Pat::from(it.clone()); |
61 | let ty = analyzer.type_of_pat(db, &pat)?; | 58 | let ty = sema.type_of_pat(&pat)?; |
62 | 59 | ||
63 | if should_not_display_type_hint(db, &it, &ty) { | 60 | if should_not_display_type_hint(db, &it, &ty) { |
64 | return None; | 61 | return None; |
@@ -125,8 +122,7 @@ fn should_not_display_type_hint(db: &RootDatabase, bind_pat: &ast::BindPat, pat_ | |||
125 | 122 | ||
126 | fn get_param_name_hints( | 123 | fn get_param_name_hints( |
127 | acc: &mut Vec<InlayHint>, | 124 | acc: &mut Vec<InlayHint>, |
128 | db: &RootDatabase, | 125 | sema: &Semantics<RootDatabase>, |
129 | analyzer: &SourceAnalyzer, | ||
130 | expr: ast::Expr, | 126 | expr: ast::Expr, |
131 | ) -> Option<()> { | 127 | ) -> Option<()> { |
132 | let args = match &expr { | 128 | let args = match &expr { |
@@ -138,7 +134,7 @@ fn get_param_name_hints( | |||
138 | // we need args len to determine whether to skip or not the &self parameter | 134 | // we need args len to determine whether to skip or not the &self parameter |
139 | .collect::<Vec<_>>(); | 135 | .collect::<Vec<_>>(); |
140 | 136 | ||
141 | let fn_signature = get_fn_signature(db, analyzer, &expr)?; | 137 | let fn_signature = get_fn_signature(sema, &expr)?; |
142 | let n_params_to_skip = | 138 | let n_params_to_skip = |
143 | if fn_signature.has_self_param && fn_signature.parameter_names.len() > args.len() { | 139 | if fn_signature.has_self_param && fn_signature.parameter_names.len() > args.len() { |
144 | 1 | 140 | 1 |
@@ -184,28 +180,26 @@ fn should_show_param_hint( | |||
184 | true | 180 | true |
185 | } | 181 | } |
186 | 182 | ||
187 | fn get_fn_signature( | 183 | fn get_fn_signature(sema: &Semantics<RootDatabase>, expr: &ast::Expr) -> Option<FunctionSignature> { |
188 | db: &RootDatabase, | ||
189 | analyzer: &SourceAnalyzer, | ||
190 | expr: &ast::Expr, | ||
191 | ) -> Option<FunctionSignature> { | ||
192 | match expr { | 184 | match expr { |
193 | ast::Expr::CallExpr(expr) => { | 185 | ast::Expr::CallExpr(expr) => { |
194 | // FIXME: Type::as_callable is broken for closures | 186 | // FIXME: Type::as_callable is broken for closures |
195 | let callable_def = analyzer.type_of(db, &expr.expr()?)?.as_callable()?; | 187 | let callable_def = sema.type_of_expr(&expr.expr()?)?.as_callable()?; |
196 | match callable_def { | 188 | match callable_def { |
197 | hir::CallableDef::FunctionId(it) => { | 189 | hir::CallableDef::FunctionId(it) => { |
198 | Some(FunctionSignature::from_hir(db, it.into())) | 190 | Some(FunctionSignature::from_hir(sema.db, it.into())) |
191 | } | ||
192 | hir::CallableDef::StructId(it) => { | ||
193 | FunctionSignature::from_struct(sema.db, it.into()) | ||
199 | } | 194 | } |
200 | hir::CallableDef::StructId(it) => FunctionSignature::from_struct(db, it.into()), | ||
201 | hir::CallableDef::EnumVariantId(it) => { | 195 | hir::CallableDef::EnumVariantId(it) => { |
202 | FunctionSignature::from_enum_variant(db, it.into()) | 196 | FunctionSignature::from_enum_variant(sema.db, it.into()) |
203 | } | 197 | } |
204 | } | 198 | } |
205 | } | 199 | } |
206 | ast::Expr::MethodCallExpr(expr) => { | 200 | ast::Expr::MethodCallExpr(expr) => { |
207 | let fn_def = analyzer.resolve_method_call(&expr)?; | 201 | let fn_def = sema.resolve_method_call(&expr)?; |
208 | Some(FunctionSignature::from_hir(db, fn_def)) | 202 | Some(FunctionSignature::from_hir(sema.db, fn_def)) |
209 | } | 203 | } |
210 | _ => None, | 204 | _ => None, |
211 | } | 205 | } |
diff --git a/crates/ra_ide/src/lib.rs b/crates/ra_ide/src/lib.rs index d22870669..f31d3c295 100644 --- a/crates/ra_ide/src/lib.rs +++ b/crates/ra_ide/src/lib.rs | |||
@@ -35,7 +35,6 @@ mod typing; | |||
35 | mod matching_brace; | 35 | mod matching_brace; |
36 | mod display; | 36 | mod display; |
37 | mod inlay_hints; | 37 | mod inlay_hints; |
38 | mod expand; | ||
39 | mod expand_macro; | 38 | mod expand_macro; |
40 | mod ssr; | 39 | mod ssr; |
41 | 40 | ||
@@ -319,9 +318,7 @@ impl Analysis { | |||
319 | file_id: FileId, | 318 | file_id: FileId, |
320 | max_inlay_hint_length: Option<usize>, | 319 | max_inlay_hint_length: Option<usize>, |
321 | ) -> Cancelable<Vec<InlayHint>> { | 320 | ) -> Cancelable<Vec<InlayHint>> { |
322 | self.with_db(|db| { | 321 | self.with_db(|db| inlay_hints::inlay_hints(db, file_id, max_inlay_hint_length)) |
323 | inlay_hints::inlay_hints(db, file_id, &db.parse(file_id).tree(), max_inlay_hint_length) | ||
324 | }) | ||
325 | } | 322 | } |
326 | 323 | ||
327 | /// Returns the set of folding ranges. | 324 | /// Returns the set of folding ranges. |
diff --git a/crates/ra_ide/src/marks.rs b/crates/ra_ide/src/marks.rs index bcb67e373..7b8b727b4 100644 --- a/crates/ra_ide/src/marks.rs +++ b/crates/ra_ide/src/marks.rs | |||
@@ -11,4 +11,5 @@ test_utils::marks!( | |||
11 | call_info_bad_offset | 11 | call_info_bad_offset |
12 | dont_complete_current_use | 12 | dont_complete_current_use |
13 | test_resolve_parent_module_on_module_decl | 13 | test_resolve_parent_module_on_module_decl |
14 | search_filters_by_range | ||
14 | ); | 15 | ); |
diff --git a/crates/ra_ide/src/parent_module.rs b/crates/ra_ide/src/parent_module.rs index af14d6ab3..2c4bdb039 100644 --- a/crates/ra_ide/src/parent_module.rs +++ b/crates/ra_ide/src/parent_module.rs | |||
@@ -1,6 +1,7 @@ | |||
1 | //! FIXME: write short doc here | 1 | //! FIXME: write short doc here |
2 | 2 | ||
3 | use ra_db::{CrateId, FileId, FilePosition, SourceDatabase}; | 3 | use hir::Semantics; |
4 | use ra_db::{CrateId, FileId, FilePosition}; | ||
4 | use ra_ide_db::RootDatabase; | 5 | use ra_ide_db::RootDatabase; |
5 | use ra_syntax::{ | 6 | use ra_syntax::{ |
6 | algo::find_node_at_offset, | 7 | algo::find_node_at_offset, |
@@ -13,10 +14,10 @@ use crate::NavigationTarget; | |||
13 | /// This returns `Vec` because a module may be included from several places. We | 14 | /// This returns `Vec` because a module may be included from several places. We |
14 | /// don't handle this case yet though, so the Vec has length at most one. | 15 | /// don't handle this case yet though, so the Vec has length at most one. |
15 | pub(crate) fn parent_module(db: &RootDatabase, position: FilePosition) -> Vec<NavigationTarget> { | 16 | pub(crate) fn parent_module(db: &RootDatabase, position: FilePosition) -> Vec<NavigationTarget> { |
16 | let mut sb = hir::SourceBinder::new(db); | 17 | let sema = Semantics::new(db); |
17 | let parse = db.parse(position.file_id); | 18 | let source_file = sema.parse(position.file_id); |
18 | 19 | ||
19 | let mut module = find_node_at_offset::<ast::Module>(parse.tree().syntax(), position.offset); | 20 | let mut module = find_node_at_offset::<ast::Module>(source_file.syntax(), position.offset); |
20 | 21 | ||
21 | // If cursor is literally on `mod foo`, go to the grandpa. | 22 | // If cursor is literally on `mod foo`, go to the grandpa. |
22 | if let Some(m) = &module { | 23 | if let Some(m) = &module { |
@@ -30,8 +31,8 @@ pub(crate) fn parent_module(db: &RootDatabase, position: FilePosition) -> Vec<Na | |||
30 | } | 31 | } |
31 | 32 | ||
32 | let module = match module { | 33 | let module = match module { |
33 | Some(module) => sb.to_def(hir::InFile::new(position.file_id.into(), module)), | 34 | Some(module) => sema.to_def(&module), |
34 | None => sb.to_module_def(position.file_id), | 35 | None => sema.to_module_def(position.file_id), |
35 | }; | 36 | }; |
36 | let module = match module { | 37 | let module = match module { |
37 | None => return Vec::new(), | 38 | None => return Vec::new(), |
@@ -43,8 +44,8 @@ pub(crate) fn parent_module(db: &RootDatabase, position: FilePosition) -> Vec<Na | |||
43 | 44 | ||
44 | /// Returns `Vec` for the same reason as `parent_module` | 45 | /// Returns `Vec` for the same reason as `parent_module` |
45 | pub(crate) fn crate_for(db: &RootDatabase, file_id: FileId) -> Vec<CrateId> { | 46 | pub(crate) fn crate_for(db: &RootDatabase, file_id: FileId) -> Vec<CrateId> { |
46 | let mut sb = hir::SourceBinder::new(db); | 47 | let sema = Semantics::new(db); |
47 | let module = match sb.to_module_def(file_id) { | 48 | let module = match sema.to_module_def(file_id) { |
48 | Some(it) => it, | 49 | Some(it) => it, |
49 | None => return Vec::new(), | 50 | None => return Vec::new(), |
50 | }; | 51 | }; |
diff --git a/crates/ra_ide/src/references.rs b/crates/ra_ide/src/references.rs index aadc2dbcb..baa8a4d29 100644 --- a/crates/ra_ide/src/references.rs +++ b/crates/ra_ide/src/references.rs | |||
@@ -13,25 +13,22 @@ mod classify; | |||
13 | mod rename; | 13 | mod rename; |
14 | mod search_scope; | 14 | mod search_scope; |
15 | 15 | ||
16 | use crate::expand::descend_into_macros_with_analyzer; | 16 | use hir::Semantics; |
17 | use hir::{InFile, SourceBinder}; | ||
18 | use once_cell::unsync::Lazy; | 17 | use once_cell::unsync::Lazy; |
19 | use ra_db::{SourceDatabase, SourceDatabaseExt}; | 18 | use ra_db::SourceDatabaseExt; |
20 | use ra_ide_db::RootDatabase; | 19 | use ra_ide_db::RootDatabase; |
21 | use ra_prof::profile; | 20 | use ra_prof::profile; |
22 | use ra_syntax::{ | 21 | use ra_syntax::{ |
23 | algo::find_node_at_offset, | 22 | algo::find_node_at_offset, |
24 | ast::{self, NameOwner}, | 23 | ast::{self, NameOwner}, |
25 | match_ast, AstNode, SourceFile, SyntaxKind, SyntaxNode, TextRange, TextUnit, TokenAtOffset, | 24 | match_ast, AstNode, SyntaxKind, SyntaxNode, TextRange, TextUnit, TokenAtOffset, |
26 | }; | 25 | }; |
26 | use test_utils::tested_by; | ||
27 | 27 | ||
28 | use crate::{display::TryToNav, FilePosition, FileRange, NavigationTarget, RangeInfo}; | 28 | use crate::{display::TryToNav, FilePosition, FileRange, NavigationTarget, RangeInfo}; |
29 | 29 | ||
30 | pub(crate) use self::{ | 30 | pub(crate) use self::{classify::classify_name_ref, rename::rename}; |
31 | classify::{classify_name, classify_name_ref}, | 31 | pub(crate) use ra_ide_db::defs::{classify_name, NameDefinition}; |
32 | rename::rename, | ||
33 | }; | ||
34 | pub(crate) use ra_ide_db::defs::NameDefinition; | ||
35 | 32 | ||
36 | pub use self::search_scope::SearchScope; | 33 | pub use self::search_scope::SearchScope; |
37 | 34 | ||
@@ -114,8 +111,8 @@ pub(crate) fn find_all_refs( | |||
114 | position: FilePosition, | 111 | position: FilePosition, |
115 | search_scope: Option<SearchScope>, | 112 | search_scope: Option<SearchScope>, |
116 | ) -> Option<RangeInfo<ReferenceSearchResult>> { | 113 | ) -> Option<RangeInfo<ReferenceSearchResult>> { |
117 | let parse = db.parse(position.file_id); | 114 | let sema = Semantics::new(db); |
118 | let syntax = parse.tree().syntax().clone(); | 115 | let syntax = sema.parse(position.file_id).syntax().clone(); |
119 | 116 | ||
120 | let (opt_name, search_kind) = | 117 | let (opt_name, search_kind) = |
121 | if let Some(name) = get_struct_def_name_for_struc_litetal_search(&syntax, position) { | 118 | if let Some(name) = get_struct_def_name_for_struc_litetal_search(&syntax, position) { |
@@ -124,7 +121,7 @@ pub(crate) fn find_all_refs( | |||
124 | (find_node_at_offset::<ast::Name>(&syntax, position.offset), ReferenceKind::Other) | 121 | (find_node_at_offset::<ast::Name>(&syntax, position.offset), ReferenceKind::Other) |
125 | }; | 122 | }; |
126 | 123 | ||
127 | let RangeInfo { range, info: (name, def) } = find_name(db, &syntax, position, opt_name)?; | 124 | let RangeInfo { range, info: (name, def) } = find_name(&sema, &syntax, position, opt_name)?; |
128 | let declaration = def.try_to_nav(db)?; | 125 | let declaration = def.try_to_nav(db)?; |
129 | 126 | ||
130 | let search_scope = { | 127 | let search_scope = { |
@@ -152,19 +149,18 @@ pub(crate) fn find_all_refs( | |||
152 | } | 149 | } |
153 | 150 | ||
154 | fn find_name( | 151 | fn find_name( |
155 | db: &RootDatabase, | 152 | sema: &Semantics<RootDatabase>, |
156 | syntax: &SyntaxNode, | 153 | syntax: &SyntaxNode, |
157 | position: FilePosition, | 154 | position: FilePosition, |
158 | opt_name: Option<ast::Name>, | 155 | opt_name: Option<ast::Name>, |
159 | ) -> Option<RangeInfo<(String, NameDefinition)>> { | 156 | ) -> Option<RangeInfo<(String, NameDefinition)>> { |
160 | let mut sb = SourceBinder::new(db); | ||
161 | if let Some(name) = opt_name { | 157 | if let Some(name) = opt_name { |
162 | let def = classify_name(&mut sb, InFile::new(position.file_id.into(), &name))?; | 158 | let def = classify_name(sema, &name)?; |
163 | let range = name.syntax().text_range(); | 159 | let range = name.syntax().text_range(); |
164 | return Some(RangeInfo::new(range, (name.text().to_string(), def))); | 160 | return Some(RangeInfo::new(range, (name.text().to_string(), def))); |
165 | } | 161 | } |
166 | let name_ref = find_node_at_offset::<ast::NameRef>(&syntax, position.offset)?; | 162 | let name_ref = find_node_at_offset::<ast::NameRef>(&syntax, position.offset)?; |
167 | let def = classify_name_ref(&mut sb, InFile::new(position.file_id.into(), &name_ref))?; | 163 | let def = classify_name_ref(sema, &name_ref)?; |
168 | let range = name_ref.syntax().text_range(); | 164 | let range = name_ref.syntax().text_range(); |
169 | Some(RangeInfo::new(range, (name_ref.text().to_string(), def))) | 165 | Some(RangeInfo::new(range, (name_ref.text().to_string(), def))) |
170 | } | 166 | } |
@@ -182,64 +178,53 @@ fn process_definition( | |||
182 | 178 | ||
183 | for (file_id, search_range) in scope { | 179 | for (file_id, search_range) in scope { |
184 | let text = db.file_text(file_id); | 180 | let text = db.file_text(file_id); |
181 | let search_range = | ||
182 | search_range.unwrap_or(TextRange::offset_len(0.into(), TextUnit::of_str(&text))); | ||
185 | 183 | ||
186 | let parse = Lazy::new(|| SourceFile::parse(&text)); | 184 | let sema = Semantics::new(db); |
187 | let mut sb = Lazy::new(|| SourceBinder::new(db)); | 185 | let tree = Lazy::new(|| sema.parse(file_id).syntax().clone()); |
188 | let mut analyzer = None; | ||
189 | 186 | ||
190 | for (idx, _) in text.match_indices(pat) { | 187 | for (idx, _) in text.match_indices(pat) { |
191 | let offset = TextUnit::from_usize(idx); | 188 | let offset = TextUnit::from_usize(idx); |
189 | if !search_range.contains_inclusive(offset) { | ||
190 | tested_by!(search_filters_by_range); | ||
191 | continue; | ||
192 | } | ||
192 | 193 | ||
193 | let (name_ref, range) = if let Some(name_ref) = | 194 | let name_ref = |
194 | find_node_at_offset::<ast::NameRef>(parse.tree().syntax(), offset) | 195 | if let Some(name_ref) = find_node_at_offset::<ast::NameRef>(&tree, offset) { |
195 | { | 196 | name_ref |
196 | let range = name_ref.syntax().text_range(); | ||
197 | (InFile::new(file_id.into(), name_ref), range) | ||
198 | } else { | ||
199 | // Handle macro token cases | ||
200 | let t = match parse.tree().syntax().token_at_offset(offset) { | ||
201 | TokenAtOffset::None => continue, | ||
202 | TokenAtOffset::Single(t) => t, | ||
203 | TokenAtOffset::Between(_, t) => t, | ||
204 | }; | ||
205 | let range = t.text_range(); | ||
206 | let analyzer = analyzer.get_or_insert_with(|| { | ||
207 | sb.analyze(InFile::new(file_id.into(), parse.tree().syntax()), None) | ||
208 | }); | ||
209 | let expanded = descend_into_macros_with_analyzer( | ||
210 | db, | ||
211 | &analyzer, | ||
212 | InFile::new(file_id.into(), t), | ||
213 | ); | ||
214 | if let Some(token) = ast::NameRef::cast(expanded.value.parent()) { | ||
215 | (expanded.with_value(token), range) | ||
216 | } else { | 197 | } else { |
217 | continue; | 198 | // Handle macro token cases |
218 | } | 199 | let token = match tree.token_at_offset(offset) { |
219 | }; | 200 | TokenAtOffset::None => continue, |
201 | TokenAtOffset::Single(t) => t, | ||
202 | TokenAtOffset::Between(_, t) => t, | ||
203 | }; | ||
204 | let expanded = sema.descend_into_macros(token); | ||
205 | match ast::NameRef::cast(expanded.parent()) { | ||
206 | Some(name_ref) => name_ref, | ||
207 | _ => continue, | ||
208 | } | ||
209 | }; | ||
220 | 210 | ||
221 | if let Some(search_range) = search_range { | ||
222 | if !range.is_subrange(&search_range) { | ||
223 | continue; | ||
224 | } | ||
225 | } | ||
226 | // FIXME: reuse sb | 211 | // FIXME: reuse sb |
227 | // See https://github.com/rust-lang/rust/pull/68198#issuecomment-574269098 | 212 | // See https://github.com/rust-lang/rust/pull/68198#issuecomment-574269098 |
228 | 213 | ||
229 | if let Some(d) = classify_name_ref(&mut sb, name_ref.as_ref()) { | 214 | if let Some(d) = classify_name_ref(&sema, &name_ref) { |
230 | if d == def { | 215 | if d == def { |
231 | let kind = if is_record_lit_name_ref(&name_ref.value) | 216 | let kind = |
232 | || is_call_expr_name_ref(&name_ref.value) | 217 | if is_record_lit_name_ref(&name_ref) || is_call_expr_name_ref(&name_ref) { |
233 | { | 218 | ReferenceKind::StructLiteral |
234 | ReferenceKind::StructLiteral | 219 | } else { |
235 | } else { | 220 | ReferenceKind::Other |
236 | ReferenceKind::Other | 221 | }; |
237 | }; | 222 | |
238 | 223 | let file_range = sema.original_range(name_ref.syntax()); | |
239 | refs.push(Reference { | 224 | refs.push(Reference { |
240 | file_range: FileRange { file_id, range }, | 225 | file_range, |
241 | kind, | 226 | kind, |
242 | access: reference_access(&d, &name_ref.value), | 227 | access: reference_access(&d, &name_ref), |
243 | }); | 228 | }); |
244 | } | 229 | } |
245 | } | 230 | } |
@@ -348,6 +333,8 @@ fn is_call_expr_name_ref(name_ref: &ast::NameRef) -> bool { | |||
348 | 333 | ||
349 | #[cfg(test)] | 334 | #[cfg(test)] |
350 | mod tests { | 335 | mod tests { |
336 | use test_utils::covers; | ||
337 | |||
351 | use crate::{ | 338 | use crate::{ |
352 | mock_analysis::{analysis_and_position, single_file_with_position, MockAnalysis}, | 339 | mock_analysis::{analysis_and_position, single_file_with_position, MockAnalysis}, |
353 | Declaration, Reference, ReferenceSearchResult, SearchScope, | 340 | Declaration, Reference, ReferenceSearchResult, SearchScope, |
@@ -456,6 +443,27 @@ mod tests { | |||
456 | } | 443 | } |
457 | 444 | ||
458 | #[test] | 445 | #[test] |
446 | fn search_filters_by_range() { | ||
447 | covers!(search_filters_by_range); | ||
448 | let code = r#" | ||
449 | fn foo() { | ||
450 | let spam<|> = 92; | ||
451 | spam + spam | ||
452 | } | ||
453 | fn bar() { | ||
454 | let spam = 92; | ||
455 | spam + spam | ||
456 | } | ||
457 | "#; | ||
458 | let refs = get_all_refs(code); | ||
459 | check_result( | ||
460 | refs, | ||
461 | "spam BIND_PAT FileId(1) [44; 48) Other Write", | ||
462 | &["FileId(1) [71; 75) Other Read", "FileId(1) [78; 82) Other Read"], | ||
463 | ); | ||
464 | } | ||
465 | |||
466 | #[test] | ||
459 | fn test_find_all_refs_for_param_inside() { | 467 | fn test_find_all_refs_for_param_inside() { |
460 | let code = r#" | 468 | let code = r#" |
461 | fn foo(i : u32) -> u32 { | 469 | fn foo(i : u32) -> u32 { |
diff --git a/crates/ra_ide/src/references/classify.rs b/crates/ra_ide/src/references/classify.rs index 478e18871..91b21429a 100644 --- a/crates/ra_ide/src/references/classify.rs +++ b/crates/ra_ide/src/references/classify.rs | |||
@@ -1,34 +1,32 @@ | |||
1 | //! Functions that are used to classify an element from its definition or reference. | 1 | //! Functions that are used to classify an element from its definition or reference. |
2 | 2 | ||
3 | use hir::{InFile, PathResolution, SourceBinder}; | 3 | use hir::{PathResolution, Semantics}; |
4 | use ra_ide_db::defs::NameDefinition; | ||
5 | use ra_ide_db::RootDatabase; | ||
4 | use ra_prof::profile; | 6 | use ra_prof::profile; |
5 | use ra_syntax::{ast, AstNode}; | 7 | use ra_syntax::{ast, AstNode}; |
6 | use test_utils::tested_by; | 8 | use test_utils::tested_by; |
7 | 9 | ||
8 | use super::NameDefinition; | 10 | pub use ra_ide_db::defs::{from_module_def, from_struct_field}; |
9 | use ra_ide_db::RootDatabase; | ||
10 | |||
11 | pub use ra_ide_db::defs::{classify_name, from_module_def, from_struct_field}; | ||
12 | 11 | ||
13 | pub(crate) fn classify_name_ref( | 12 | pub(crate) fn classify_name_ref( |
14 | sb: &mut SourceBinder<RootDatabase>, | 13 | sema: &Semantics<RootDatabase>, |
15 | name_ref: InFile<&ast::NameRef>, | 14 | name_ref: &ast::NameRef, |
16 | ) -> Option<NameDefinition> { | 15 | ) -> Option<NameDefinition> { |
17 | let _p = profile("classify_name_ref"); | 16 | let _p = profile("classify_name_ref"); |
18 | 17 | ||
19 | let parent = name_ref.value.syntax().parent()?; | 18 | let parent = name_ref.syntax().parent()?; |
20 | let analyzer = sb.analyze(name_ref.map(|it| it.syntax()), None); | ||
21 | 19 | ||
22 | if let Some(method_call) = ast::MethodCallExpr::cast(parent.clone()) { | 20 | if let Some(method_call) = ast::MethodCallExpr::cast(parent.clone()) { |
23 | tested_by!(goto_def_for_methods); | 21 | tested_by!(goto_def_for_methods); |
24 | if let Some(func) = analyzer.resolve_method_call(&method_call) { | 22 | if let Some(func) = sema.resolve_method_call(&method_call) { |
25 | return Some(from_module_def(func.into())); | 23 | return Some(from_module_def(func.into())); |
26 | } | 24 | } |
27 | } | 25 | } |
28 | 26 | ||
29 | if let Some(field_expr) = ast::FieldExpr::cast(parent.clone()) { | 27 | if let Some(field_expr) = ast::FieldExpr::cast(parent.clone()) { |
30 | tested_by!(goto_def_for_fields); | 28 | tested_by!(goto_def_for_fields); |
31 | if let Some(field) = analyzer.resolve_field(&field_expr) { | 29 | if let Some(field) = sema.resolve_field(&field_expr) { |
32 | return Some(from_struct_field(field)); | 30 | return Some(from_struct_field(field)); |
33 | } | 31 | } |
34 | } | 32 | } |
@@ -36,22 +34,20 @@ pub(crate) fn classify_name_ref( | |||
36 | if let Some(record_field) = ast::RecordField::cast(parent.clone()) { | 34 | if let Some(record_field) = ast::RecordField::cast(parent.clone()) { |
37 | tested_by!(goto_def_for_record_fields); | 35 | tested_by!(goto_def_for_record_fields); |
38 | tested_by!(goto_def_for_field_init_shorthand); | 36 | tested_by!(goto_def_for_field_init_shorthand); |
39 | if let Some(field_def) = analyzer.resolve_record_field(&record_field) { | 37 | if let Some(field_def) = sema.resolve_record_field(&record_field) { |
40 | return Some(from_struct_field(field_def)); | 38 | return Some(from_struct_field(field_def)); |
41 | } | 39 | } |
42 | } | 40 | } |
43 | 41 | ||
44 | if let Some(macro_call) = parent.ancestors().find_map(ast::MacroCall::cast) { | 42 | if let Some(macro_call) = parent.ancestors().find_map(ast::MacroCall::cast) { |
45 | tested_by!(goto_def_for_macros); | 43 | tested_by!(goto_def_for_macros); |
46 | if let Some(macro_def) = | 44 | if let Some(macro_def) = sema.resolve_macro_call(¯o_call) { |
47 | analyzer.resolve_macro_call(sb.db, name_ref.with_value(¯o_call)) | ||
48 | { | ||
49 | return Some(NameDefinition::Macro(macro_def)); | 45 | return Some(NameDefinition::Macro(macro_def)); |
50 | } | 46 | } |
51 | } | 47 | } |
52 | 48 | ||
53 | let path = name_ref.value.syntax().ancestors().find_map(ast::Path::cast)?; | 49 | let path = name_ref.syntax().ancestors().find_map(ast::Path::cast)?; |
54 | let resolved = analyzer.resolve_path(sb.db, &path)?; | 50 | let resolved = sema.resolve_path(&path)?; |
55 | let res = match resolved { | 51 | let res = match resolved { |
56 | PathResolution::Def(def) => from_module_def(def), | 52 | PathResolution::Def(def) => from_module_def(def), |
57 | PathResolution::AssocItem(item) => { | 53 | PathResolution::AssocItem(item) => { |
diff --git a/crates/ra_ide/src/references/rename.rs b/crates/ra_ide/src/references/rename.rs index bdb90020b..5b4bcf434 100644 --- a/crates/ra_ide/src/references/rename.rs +++ b/crates/ra_ide/src/references/rename.rs | |||
@@ -1,7 +1,7 @@ | |||
1 | //! FIXME: write short doc here | 1 | //! FIXME: write short doc here |
2 | 2 | ||
3 | use hir::ModuleSource; | 3 | use hir::{ModuleSource, Semantics}; |
4 | use ra_db::{RelativePath, RelativePathBuf, SourceDatabase, SourceDatabaseExt}; | 4 | use ra_db::{RelativePath, RelativePathBuf, SourceDatabaseExt}; |
5 | use ra_ide_db::RootDatabase; | 5 | use ra_ide_db::RootDatabase; |
6 | use ra_syntax::{ | 6 | use ra_syntax::{ |
7 | algo::find_node_at_offset, ast, lex_single_valid_syntax_kind, AstNode, SyntaxKind, SyntaxNode, | 7 | algo::find_node_at_offset, ast, lex_single_valid_syntax_kind, AstNode, SyntaxKind, SyntaxNode, |
@@ -24,15 +24,16 @@ pub(crate) fn rename( | |||
24 | _ => return None, | 24 | _ => return None, |
25 | } | 25 | } |
26 | 26 | ||
27 | let parse = db.parse(position.file_id); | 27 | let sema = Semantics::new(db); |
28 | let source_file = sema.parse(position.file_id); | ||
28 | if let Some((ast_name, ast_module)) = | 29 | if let Some((ast_name, ast_module)) = |
29 | find_name_and_module_at_offset(parse.tree().syntax(), position) | 30 | find_name_and_module_at_offset(source_file.syntax(), position) |
30 | { | 31 | { |
31 | let range = ast_name.syntax().text_range(); | 32 | let range = ast_name.syntax().text_range(); |
32 | rename_mod(db, &ast_name, &ast_module, position, new_name) | 33 | rename_mod(&sema, &ast_name, &ast_module, position, new_name) |
33 | .map(|info| RangeInfo::new(range, info)) | 34 | .map(|info| RangeInfo::new(range, info)) |
34 | } else { | 35 | } else { |
35 | rename_reference(db, position, new_name) | 36 | rename_reference(sema.db, position, new_name) |
36 | } | 37 | } |
37 | } | 38 | } |
38 | 39 | ||
@@ -54,7 +55,7 @@ fn source_edit_from_file_id_range( | |||
54 | } | 55 | } |
55 | 56 | ||
56 | fn rename_mod( | 57 | fn rename_mod( |
57 | db: &RootDatabase, | 58 | sema: &Semantics<RootDatabase>, |
58 | ast_name: &ast::Name, | 59 | ast_name: &ast::Name, |
59 | ast_module: &ast::Module, | 60 | ast_module: &ast::Module, |
60 | position: FilePosition, | 61 | position: FilePosition, |
@@ -62,13 +63,12 @@ fn rename_mod( | |||
62 | ) -> Option<SourceChange> { | 63 | ) -> Option<SourceChange> { |
63 | let mut source_file_edits = Vec::new(); | 64 | let mut source_file_edits = Vec::new(); |
64 | let mut file_system_edits = Vec::new(); | 65 | let mut file_system_edits = Vec::new(); |
65 | let module_src = hir::InFile { file_id: position.file_id.into(), value: ast_module.clone() }; | 66 | if let Some(module) = sema.to_def(ast_module) { |
66 | if let Some(module) = hir::SourceBinder::new(db).to_def(module_src) { | 67 | let src = module.definition_source(sema.db); |
67 | let src = module.definition_source(db); | 68 | let file_id = src.file_id.original_file(sema.db); |
68 | let file_id = src.file_id.original_file(db); | ||
69 | match src.value { | 69 | match src.value { |
70 | ModuleSource::SourceFile(..) => { | 70 | ModuleSource::SourceFile(..) => { |
71 | let mod_path: RelativePathBuf = db.file_relative_path(file_id); | 71 | let mod_path: RelativePathBuf = sema.db.file_relative_path(file_id); |
72 | // mod is defined in path/to/dir/mod.rs | 72 | // mod is defined in path/to/dir/mod.rs |
73 | let dst_path = if mod_path.file_stem() == Some("mod") { | 73 | let dst_path = if mod_path.file_stem() == Some("mod") { |
74 | mod_path | 74 | mod_path |
@@ -82,7 +82,7 @@ fn rename_mod( | |||
82 | if let Some(path) = dst_path { | 82 | if let Some(path) = dst_path { |
83 | let move_file = FileSystemEdit::MoveFile { | 83 | let move_file = FileSystemEdit::MoveFile { |
84 | src: file_id, | 84 | src: file_id, |
85 | dst_source_root: db.file_source_root(position.file_id), | 85 | dst_source_root: sema.db.file_source_root(position.file_id), |
86 | dst_path: path, | 86 | dst_path: path, |
87 | }; | 87 | }; |
88 | file_system_edits.push(move_file); | 88 | file_system_edits.push(move_file); |
@@ -98,7 +98,7 @@ fn rename_mod( | |||
98 | }; | 98 | }; |
99 | source_file_edits.push(edit); | 99 | source_file_edits.push(edit); |
100 | 100 | ||
101 | if let Some(RangeInfo { range: _, info: refs }) = find_all_refs(db, position, None) { | 101 | if let Some(RangeInfo { range: _, info: refs }) = find_all_refs(sema.db, position, None) { |
102 | let ref_edits = refs.references.into_iter().map(|reference| { | 102 | let ref_edits = refs.references.into_iter().map(|reference| { |
103 | source_edit_from_file_id_range( | 103 | source_edit_from_file_id_range( |
104 | reference.file_range.file_id, | 104 | reference.file_range.file_id, |
diff --git a/crates/ra_ide/src/runnables.rs b/crates/ra_ide/src/runnables.rs index be2a67d0a..74877e90f 100644 --- a/crates/ra_ide/src/runnables.rs +++ b/crates/ra_ide/src/runnables.rs | |||
@@ -1,8 +1,7 @@ | |||
1 | //! FIXME: write short doc here | 1 | //! FIXME: write short doc here |
2 | 2 | ||
3 | use hir::{InFile, SourceBinder}; | 3 | use hir::Semantics; |
4 | use itertools::Itertools; | 4 | use itertools::Itertools; |
5 | use ra_db::SourceDatabase; | ||
6 | use ra_ide_db::RootDatabase; | 5 | use ra_ide_db::RootDatabase; |
7 | use ra_syntax::{ | 6 | use ra_syntax::{ |
8 | ast::{self, AstNode, AttrsOwner, ModuleItemOwner, NameOwner}, | 7 | ast::{self, AstNode, AttrsOwner, ModuleItemOwner, NameOwner}, |
@@ -42,46 +41,33 @@ pub enum RunnableKind { | |||
42 | } | 41 | } |
43 | 42 | ||
44 | pub(crate) fn runnables(db: &RootDatabase, file_id: FileId) -> Vec<Runnable> { | 43 | pub(crate) fn runnables(db: &RootDatabase, file_id: FileId) -> Vec<Runnable> { |
45 | let parse = db.parse(file_id); | 44 | let sema = Semantics::new(db); |
46 | let mut sb = SourceBinder::new(db); | 45 | let source_file = sema.parse(file_id); |
47 | parse.tree().syntax().descendants().filter_map(|i| runnable(db, &mut sb, file_id, i)).collect() | 46 | source_file.syntax().descendants().filter_map(|i| runnable(&sema, i)).collect() |
48 | } | 47 | } |
49 | 48 | ||
50 | fn runnable( | 49 | fn runnable(sema: &Semantics<RootDatabase>, item: SyntaxNode) -> Option<Runnable> { |
51 | db: &RootDatabase, | ||
52 | source_binder: &mut SourceBinder<RootDatabase>, | ||
53 | file_id: FileId, | ||
54 | item: SyntaxNode, | ||
55 | ) -> Option<Runnable> { | ||
56 | match_ast! { | 50 | match_ast! { |
57 | match item { | 51 | match item { |
58 | ast::FnDef(it) => { runnable_fn(db, source_binder, file_id, it) }, | 52 | ast::FnDef(it) => { runnable_fn(sema, it) }, |
59 | ast::Module(it) => { runnable_mod(db, source_binder, file_id, it) }, | 53 | ast::Module(it) => { runnable_mod(sema, it) }, |
60 | _ => { None }, | 54 | _ => None, |
61 | } | 55 | } |
62 | } | 56 | } |
63 | } | 57 | } |
64 | 58 | ||
65 | fn runnable_fn( | 59 | fn runnable_fn(sema: &Semantics<RootDatabase>, fn_def: ast::FnDef) -> Option<Runnable> { |
66 | db: &RootDatabase, | ||
67 | source_binder: &mut SourceBinder<RootDatabase>, | ||
68 | file_id: FileId, | ||
69 | fn_def: ast::FnDef, | ||
70 | ) -> Option<Runnable> { | ||
71 | let name_string = fn_def.name()?.text().to_string(); | 60 | let name_string = fn_def.name()?.text().to_string(); |
72 | 61 | ||
73 | let kind = if name_string == "main" { | 62 | let kind = if name_string == "main" { |
74 | RunnableKind::Bin | 63 | RunnableKind::Bin |
75 | } else { | 64 | } else { |
76 | let test_id = if let Some(module) = source_binder | 65 | let test_id = if let Some(module) = sema.to_def(&fn_def).map(|def| def.module(sema.db)) { |
77 | .to_def(InFile::new(file_id.into(), fn_def.clone())) | ||
78 | .map(|def| def.module(db)) | ||
79 | { | ||
80 | let path = module | 66 | let path = module |
81 | .path_to_root(db) | 67 | .path_to_root(sema.db) |
82 | .into_iter() | 68 | .into_iter() |
83 | .rev() | 69 | .rev() |
84 | .filter_map(|it| it.name(db)) | 70 | .filter_map(|it| it.name(sema.db)) |
85 | .map(|name| name.to_string()) | 71 | .map(|name| name.to_string()) |
86 | .chain(std::iter::once(name_string)) | 72 | .chain(std::iter::once(name_string)) |
87 | .join("::"); | 73 | .join("::"); |
@@ -115,12 +101,7 @@ fn has_test_related_attribute(fn_def: &ast::FnDef) -> bool { | |||
115 | .any(|attribute_text| attribute_text.contains("test")) | 101 | .any(|attribute_text| attribute_text.contains("test")) |
116 | } | 102 | } |
117 | 103 | ||
118 | fn runnable_mod( | 104 | fn runnable_mod(sema: &Semantics<RootDatabase>, module: ast::Module) -> Option<Runnable> { |
119 | db: &RootDatabase, | ||
120 | source_binder: &mut SourceBinder<RootDatabase>, | ||
121 | file_id: FileId, | ||
122 | module: ast::Module, | ||
123 | ) -> Option<Runnable> { | ||
124 | let has_test_function = module | 105 | let has_test_function = module |
125 | .item_list()? | 106 | .item_list()? |
126 | .items() | 107 | .items() |
@@ -133,9 +114,10 @@ fn runnable_mod( | |||
133 | return None; | 114 | return None; |
134 | } | 115 | } |
135 | let range = module.syntax().text_range(); | 116 | let range = module.syntax().text_range(); |
136 | let module = source_binder.to_def(InFile::new(file_id.into(), module))?; | 117 | let module = sema.to_def(&module)?; |
137 | 118 | ||
138 | let path = module.path_to_root(db).into_iter().rev().filter_map(|it| it.name(db)).join("::"); | 119 | let path = |
120 | module.path_to_root(sema.db).into_iter().rev().filter_map(|it| it.name(sema.db)).join("::"); | ||
139 | Some(Runnable { range, kind: RunnableKind::TestMod { path } }) | 121 | Some(Runnable { range, kind: RunnableKind::TestMod { path } }) |
140 | } | 122 | } |
141 | 123 | ||
diff --git a/crates/ra_ide/src/snapshots/rainbow_highlighting.html b/crates/ra_ide/src/snapshots/rainbow_highlighting.html index 95f038f00..d6a7da953 100644 --- a/crates/ra_ide/src/snapshots/rainbow_highlighting.html +++ b/crates/ra_ide/src/snapshots/rainbow_highlighting.html | |||
@@ -25,14 +25,14 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd | |||
25 | .keyword\.control { color: #F0DFAF; font-weight: bold; } | 25 | .keyword\.control { color: #F0DFAF; font-weight: bold; } |
26 | </style> | 26 | </style> |
27 | <pre><code><span class="keyword">fn</span> <span class="function">main</span>() { | 27 | <pre><code><span class="keyword">fn</span> <span class="function">main</span>() { |
28 | <span class="keyword">let</span> <span class="variable" data-binding-hash="2217585909179791122" style="color: hsl(280,74%,48%);">hello</span> = <span class="string">"hello"</span>; | 28 | <span class="keyword">let</span> <span class="variable" data-binding-hash="8121853618659664005" style="color: hsl(261,57%,61%);">hello</span> = <span class="string">"hello"</span>; |
29 | <span class="keyword">let</span> <span class="variable" data-binding-hash="4303609361109701698" style="color: hsl(242,75%,88%);">x</span> = <span class="variable" data-binding-hash="2217585909179791122" style="color: hsl(280,74%,48%);">hello</span>.to_string(); | 29 | <span class="keyword">let</span> <span class="variable" data-binding-hash="2705725358298919760" style="color: hsl(17,51%,74%);">x</span> = <span class="variable" data-binding-hash="8121853618659664005" style="color: hsl(261,57%,61%);">hello</span>.to_string(); |
30 | <span class="keyword">let</span> <span class="variable" data-binding-hash="13865792086344377029" style="color: hsl(340,64%,86%);">y</span> = <span class="variable" data-binding-hash="2217585909179791122" style="color: hsl(280,74%,48%);">hello</span>.to_string(); | 30 | <span class="keyword">let</span> <span class="variable" data-binding-hash="3365759661443752373" style="color: hsl(127,76%,66%);">y</span> = <span class="variable" data-binding-hash="8121853618659664005" style="color: hsl(261,57%,61%);">hello</span>.to_string(); |
31 | 31 | ||
32 | <span class="keyword">let</span> <span class="variable" data-binding-hash="7011301204224269512" style="color: hsl(198,45%,40%);">x</span> = <span class="string">"other color please!"</span>; | 32 | <span class="keyword">let</span> <span class="variable" data-binding-hash="794745962933817518" style="color: hsl(19,74%,76%);">x</span> = <span class="string">"other color please!"</span>; |
33 | <span class="keyword">let</span> <span class="variable" data-binding-hash="12461245066629867975" style="color: hsl(132,91%,68%);">y</span> = <span class="variable" data-binding-hash="7011301204224269512" style="color: hsl(198,45%,40%);">x</span>.to_string(); | 33 | <span class="keyword">let</span> <span class="variable" data-binding-hash="6717528807933952652" style="color: hsl(85,49%,84%);">y</span> = <span class="variable" data-binding-hash="794745962933817518" style="color: hsl(19,74%,76%);">x</span>.to_string(); |
34 | } | 34 | } |
35 | 35 | ||
36 | <span class="keyword">fn</span> <span class="function">bar</span>() { | 36 | <span class="keyword">fn</span> <span class="function">bar</span>() { |
37 | <span class="keyword">let</span> <span class="keyword">mut</span> <span class="variable.mut" data-binding-hash="2217585909179791122" style="color: hsl(280,74%,48%);">hello</span> = <span class="string">"hello"</span>; | 37 | <span class="keyword">let</span> <span class="keyword">mut</span> <span class="variable.mut" data-binding-hash="8121853618659664005" style="color: hsl(261,57%,61%);">hello</span> = <span class="string">"hello"</span>; |
38 | }</code></pre> \ No newline at end of file | 38 | }</code></pre> \ No newline at end of file |
diff --git a/crates/ra_ide/src/syntax_highlighting.rs b/crates/ra_ide/src/syntax_highlighting.rs index 9bc3ad448..987476d2c 100644 --- a/crates/ra_ide/src/syntax_highlighting.rs +++ b/crates/ra_ide/src/syntax_highlighting.rs | |||
@@ -1,8 +1,11 @@ | |||
1 | //! FIXME: write short doc here | 1 | //! FIXME: write short doc here |
2 | 2 | ||
3 | use hir::{HirFileId, InFile, Name, SourceAnalyzer, SourceBinder}; | 3 | use hir::{Name, Semantics}; |
4 | use ra_db::SourceDatabase; | 4 | use ra_db::SourceDatabase; |
5 | use ra_ide_db::{defs::NameDefinition, RootDatabase}; | 5 | use ra_ide_db::{ |
6 | defs::{classify_name, NameDefinition}, | ||
7 | RootDatabase, | ||
8 | }; | ||
6 | use ra_prof::profile; | 9 | use ra_prof::profile; |
7 | use ra_syntax::{ | 10 | use ra_syntax::{ |
8 | ast, AstNode, Direction, NodeOrToken, SyntaxElement, SyntaxKind, SyntaxKind::*, SyntaxToken, | 11 | ast, AstNode, Direction, NodeOrToken, SyntaxElement, SyntaxKind, SyntaxKind::*, SyntaxToken, |
@@ -10,11 +13,7 @@ use ra_syntax::{ | |||
10 | }; | 13 | }; |
11 | use rustc_hash::FxHashMap; | 14 | use rustc_hash::FxHashMap; |
12 | 15 | ||
13 | use crate::{ | 16 | use crate::{references::classify_name_ref, FileId}; |
14 | expand::descend_into_macros_with_analyzer, | ||
15 | references::{classify_name, classify_name_ref}, | ||
16 | FileId, | ||
17 | }; | ||
18 | 17 | ||
19 | pub mod tags { | 18 | pub mod tags { |
20 | pub const FIELD: &str = "field"; | 19 | pub const FIELD: &str = "field"; |
@@ -73,14 +72,11 @@ pub(crate) fn highlight( | |||
73 | range: Option<TextRange>, | 72 | range: Option<TextRange>, |
74 | ) -> Vec<HighlightedRange> { | 73 | ) -> Vec<HighlightedRange> { |
75 | let _p = profile("highlight"); | 74 | let _p = profile("highlight"); |
75 | let sema = Semantics::new(db); | ||
76 | let root = sema.parse(file_id).syntax().clone(); | ||
76 | 77 | ||
77 | let parse = db.parse(file_id); | ||
78 | let root = parse.tree().syntax().clone(); | ||
79 | |||
80 | let mut sb = SourceBinder::new(db); | ||
81 | let mut bindings_shadow_count: FxHashMap<Name, u32> = FxHashMap::default(); | 78 | let mut bindings_shadow_count: FxHashMap<Name, u32> = FxHashMap::default(); |
82 | let mut res = Vec::new(); | 79 | let mut res = Vec::new(); |
83 | let analyzer = sb.analyze(InFile::new(file_id.into(), &root), None); | ||
84 | 80 | ||
85 | let mut in_macro_call = None; | 81 | let mut in_macro_call = None; |
86 | 82 | ||
@@ -105,7 +101,7 @@ pub(crate) fn highlight( | |||
105 | match node.kind() { | 101 | match node.kind() { |
106 | MACRO_CALL => { | 102 | MACRO_CALL => { |
107 | in_macro_call = Some(node.clone()); | 103 | in_macro_call = Some(node.clone()); |
108 | if let Some(range) = highlight_macro(InFile::new(file_id.into(), node)) { | 104 | if let Some(range) = highlight_macro(node) { |
109 | res.push(HighlightedRange { | 105 | res.push(HighlightedRange { |
110 | range, | 106 | range, |
111 | tag: tags::MACRO, | 107 | tag: tags::MACRO, |
@@ -116,10 +112,9 @@ pub(crate) fn highlight( | |||
116 | _ if in_macro_call.is_some() => { | 112 | _ if in_macro_call.is_some() => { |
117 | if let Some(token) = node.as_token() { | 113 | if let Some(token) = node.as_token() { |
118 | if let Some((tag, binding_hash)) = highlight_token_tree( | 114 | if let Some((tag, binding_hash)) = highlight_token_tree( |
119 | &mut sb, | 115 | &sema, |
120 | &analyzer, | ||
121 | &mut bindings_shadow_count, | 116 | &mut bindings_shadow_count, |
122 | InFile::new(file_id.into(), token.clone()), | 117 | token.clone(), |
123 | ) { | 118 | ) { |
124 | res.push(HighlightedRange { | 119 | res.push(HighlightedRange { |
125 | range: node.text_range(), | 120 | range: node.text_range(), |
@@ -130,11 +125,9 @@ pub(crate) fn highlight( | |||
130 | } | 125 | } |
131 | } | 126 | } |
132 | _ => { | 127 | _ => { |
133 | if let Some((tag, binding_hash)) = highlight_node( | 128 | if let Some((tag, binding_hash)) = |
134 | &mut sb, | 129 | highlight_node(&sema, &mut bindings_shadow_count, node.clone()) |
135 | &mut bindings_shadow_count, | 130 | { |
136 | InFile::new(file_id.into(), node.clone()), | ||
137 | ) { | ||
138 | res.push(HighlightedRange { | 131 | res.push(HighlightedRange { |
139 | range: node.text_range(), | 132 | range: node.text_range(), |
140 | tag, | 133 | tag, |
@@ -161,8 +154,8 @@ pub(crate) fn highlight( | |||
161 | res | 154 | res |
162 | } | 155 | } |
163 | 156 | ||
164 | fn highlight_macro(node: InFile<SyntaxElement>) -> Option<TextRange> { | 157 | fn highlight_macro(node: SyntaxElement) -> Option<TextRange> { |
165 | let macro_call = ast::MacroCall::cast(node.value.as_node()?.clone())?; | 158 | let macro_call = ast::MacroCall::cast(node.as_node()?.clone())?; |
166 | let path = macro_call.path()?; | 159 | let path = macro_call.path()?; |
167 | let name_ref = path.segment()?.name_ref()?; | 160 | let name_ref = path.segment()?.name_ref()?; |
168 | 161 | ||
@@ -179,35 +172,34 @@ fn highlight_macro(node: InFile<SyntaxElement>) -> Option<TextRange> { | |||
179 | } | 172 | } |
180 | 173 | ||
181 | fn highlight_token_tree( | 174 | fn highlight_token_tree( |
182 | sb: &mut SourceBinder<RootDatabase>, | 175 | sema: &Semantics<RootDatabase>, |
183 | analyzer: &SourceAnalyzer, | ||
184 | bindings_shadow_count: &mut FxHashMap<Name, u32>, | 176 | bindings_shadow_count: &mut FxHashMap<Name, u32>, |
185 | token: InFile<SyntaxToken>, | 177 | token: SyntaxToken, |
186 | ) -> Option<(&'static str, Option<u64>)> { | 178 | ) -> Option<(&'static str, Option<u64>)> { |
187 | if token.value.parent().kind() != TOKEN_TREE { | 179 | if token.parent().kind() != TOKEN_TREE { |
188 | return None; | 180 | return None; |
189 | } | 181 | } |
190 | let token = descend_into_macros_with_analyzer(sb.db, analyzer, token); | 182 | let token = sema.descend_into_macros(token.clone()); |
191 | let expanded = { | 183 | let expanded = { |
192 | let parent = token.value.parent(); | 184 | let parent = token.parent(); |
193 | // We only care Name and Name_ref | 185 | // We only care Name and Name_ref |
194 | match (token.value.kind(), parent.kind()) { | 186 | match (token.kind(), parent.kind()) { |
195 | (IDENT, NAME) | (IDENT, NAME_REF) => token.with_value(parent.into()), | 187 | (IDENT, NAME) | (IDENT, NAME_REF) => parent.into(), |
196 | _ => token.map(|it| it.into()), | 188 | _ => token.into(), |
197 | } | 189 | } |
198 | }; | 190 | }; |
199 | 191 | ||
200 | highlight_node(sb, bindings_shadow_count, expanded) | 192 | highlight_node(sema, bindings_shadow_count, expanded) |
201 | } | 193 | } |
202 | 194 | ||
203 | fn highlight_node( | 195 | fn highlight_node( |
204 | sb: &mut SourceBinder<RootDatabase>, | 196 | sema: &Semantics<RootDatabase>, |
205 | bindings_shadow_count: &mut FxHashMap<Name, u32>, | 197 | bindings_shadow_count: &mut FxHashMap<Name, u32>, |
206 | node: InFile<SyntaxElement>, | 198 | node: SyntaxElement, |
207 | ) -> Option<(&'static str, Option<u64>)> { | 199 | ) -> Option<(&'static str, Option<u64>)> { |
208 | let db = sb.db; | 200 | let db = sema.db; |
209 | let mut binding_hash = None; | 201 | let mut binding_hash = None; |
210 | let tag = match node.value.kind() { | 202 | let tag = match node.kind() { |
211 | FN_DEF => { | 203 | FN_DEF => { |
212 | bindings_shadow_count.clear(); | 204 | bindings_shadow_count.clear(); |
213 | return None; | 205 | return None; |
@@ -216,19 +208,18 @@ fn highlight_node( | |||
216 | STRING | RAW_STRING | RAW_BYTE_STRING | BYTE_STRING => tags::LITERAL_STRING, | 208 | STRING | RAW_STRING | RAW_BYTE_STRING | BYTE_STRING => tags::LITERAL_STRING, |
217 | ATTR => tags::LITERAL_ATTRIBUTE, | 209 | ATTR => tags::LITERAL_ATTRIBUTE, |
218 | // Special-case field init shorthand | 210 | // Special-case field init shorthand |
219 | NAME_REF if node.value.parent().and_then(ast::RecordField::cast).is_some() => tags::FIELD, | 211 | NAME_REF if node.parent().and_then(ast::RecordField::cast).is_some() => tags::FIELD, |
220 | NAME_REF if node.value.ancestors().any(|it| it.kind() == ATTR) => return None, | 212 | NAME_REF if node.ancestors().any(|it| it.kind() == ATTR) => return None, |
221 | NAME_REF => { | 213 | NAME_REF => { |
222 | let name_ref = node.value.as_node().cloned().and_then(ast::NameRef::cast).unwrap(); | 214 | let name_ref = node.as_node().cloned().and_then(ast::NameRef::cast).unwrap(); |
223 | let name_kind = classify_name_ref(sb, node.with_value(&name_ref)); | 215 | let name_kind = classify_name_ref(sema, &name_ref); |
224 | match name_kind { | 216 | match name_kind { |
225 | Some(name_kind) => { | 217 | Some(name_kind) => { |
226 | if let NameDefinition::Local(local) = &name_kind { | 218 | if let NameDefinition::Local(local) = &name_kind { |
227 | if let Some(name) = local.name(db) { | 219 | if let Some(name) = local.name(db) { |
228 | let shadow_count = | 220 | let shadow_count = |
229 | bindings_shadow_count.entry(name.clone()).or_default(); | 221 | bindings_shadow_count.entry(name.clone()).or_default(); |
230 | binding_hash = | 222 | binding_hash = Some(calc_binding_hash(&name, *shadow_count)) |
231 | Some(calc_binding_hash(node.file_id, &name, *shadow_count)) | ||
232 | } | 223 | } |
233 | }; | 224 | }; |
234 | 225 | ||
@@ -238,14 +229,14 @@ fn highlight_node( | |||
238 | } | 229 | } |
239 | } | 230 | } |
240 | NAME => { | 231 | NAME => { |
241 | let name = node.value.as_node().cloned().and_then(ast::Name::cast).unwrap(); | 232 | let name = node.as_node().cloned().and_then(ast::Name::cast).unwrap(); |
242 | let name_kind = classify_name(sb, node.with_value(&name)); | 233 | let name_kind = classify_name(sema, &name); |
243 | 234 | ||
244 | if let Some(NameDefinition::Local(local)) = &name_kind { | 235 | if let Some(NameDefinition::Local(local)) = &name_kind { |
245 | if let Some(name) = local.name(db) { | 236 | if let Some(name) = local.name(db) { |
246 | let shadow_count = bindings_shadow_count.entry(name.clone()).or_default(); | 237 | let shadow_count = bindings_shadow_count.entry(name.clone()).or_default(); |
247 | *shadow_count += 1; | 238 | *shadow_count += 1; |
248 | binding_hash = Some(calc_binding_hash(node.file_id, &name, *shadow_count)) | 239 | binding_hash = Some(calc_binding_hash(&name, *shadow_count)) |
249 | } | 240 | } |
250 | }; | 241 | }; |
251 | 242 | ||
@@ -272,7 +263,7 @@ fn highlight_node( | |||
272 | 263 | ||
273 | return Some((tag, binding_hash)); | 264 | return Some((tag, binding_hash)); |
274 | 265 | ||
275 | fn calc_binding_hash(file_id: HirFileId, name: &Name, shadow_count: u32) -> u64 { | 266 | fn calc_binding_hash(name: &Name, shadow_count: u32) -> u64 { |
276 | fn hash<T: std::hash::Hash + std::fmt::Debug>(x: T) -> u64 { | 267 | fn hash<T: std::hash::Hash + std::fmt::Debug>(x: T) -> u64 { |
277 | use std::{collections::hash_map::DefaultHasher, hash::Hasher}; | 268 | use std::{collections::hash_map::DefaultHasher, hash::Hasher}; |
278 | 269 | ||
@@ -281,7 +272,7 @@ fn highlight_node( | |||
281 | hasher.finish() | 272 | hasher.finish() |
282 | } | 273 | } |
283 | 274 | ||
284 | hash((file_id, name, shadow_count)) | 275 | hash((name, shadow_count)) |
285 | } | 276 | } |
286 | } | 277 | } |
287 | 278 | ||
diff --git a/crates/ra_ide_db/src/defs.rs b/crates/ra_ide_db/src/defs.rs index 04c214624..e10e72f71 100644 --- a/crates/ra_ide_db/src/defs.rs +++ b/crates/ra_ide_db/src/defs.rs | |||
@@ -6,8 +6,8 @@ | |||
6 | // FIXME: this badly needs rename/rewrite (matklad, 2020-02-06). | 6 | // FIXME: this badly needs rename/rewrite (matklad, 2020-02-06). |
7 | 7 | ||
8 | use hir::{ | 8 | use hir::{ |
9 | Adt, FieldSource, HasSource, ImplBlock, InFile, Local, MacroDef, Module, ModuleDef, | 9 | Adt, FieldSource, HasSource, ImplBlock, Local, MacroDef, Module, ModuleDef, Semantics, |
10 | SourceBinder, StructField, TypeParam, | 10 | StructField, TypeParam, |
11 | }; | 11 | }; |
12 | use ra_prof::profile; | 12 | use ra_prof::profile; |
13 | use ra_syntax::{ | 13 | use ra_syntax::{ |
@@ -68,78 +68,62 @@ impl NameDefinition { | |||
68 | } | 68 | } |
69 | } | 69 | } |
70 | 70 | ||
71 | pub fn classify_name( | 71 | pub fn classify_name(sema: &Semantics<RootDatabase>, name: &ast::Name) -> Option<NameDefinition> { |
72 | sb: &mut SourceBinder<RootDatabase>, | ||
73 | name: InFile<&ast::Name>, | ||
74 | ) -> Option<NameDefinition> { | ||
75 | let _p = profile("classify_name"); | 72 | let _p = profile("classify_name"); |
76 | let parent = name.value.syntax().parent()?; | 73 | let parent = name.syntax().parent()?; |
77 | 74 | ||
78 | match_ast! { | 75 | match_ast! { |
79 | match parent { | 76 | match parent { |
80 | ast::BindPat(it) => { | 77 | ast::BindPat(it) => { |
81 | let src = name.with_value(it); | 78 | let local = sema.to_def(&it)?; |
82 | let local = sb.to_def(src)?; | ||
83 | Some(NameDefinition::Local(local)) | 79 | Some(NameDefinition::Local(local)) |
84 | }, | 80 | }, |
85 | ast::RecordFieldDef(it) => { | 81 | ast::RecordFieldDef(it) => { |
86 | let src = name.with_value(it); | 82 | let field: hir::StructField = sema.to_def(&it)?; |
87 | let field: hir::StructField = sb.to_def(src)?; | ||
88 | Some(from_struct_field(field)) | 83 | Some(from_struct_field(field)) |
89 | }, | 84 | }, |
90 | ast::Module(it) => { | 85 | ast::Module(it) => { |
91 | let def = sb.to_def(name.with_value(it))?; | 86 | let def = sema.to_def(&it)?; |
92 | Some(from_module_def(def.into())) | 87 | Some(from_module_def(def.into())) |
93 | }, | 88 | }, |
94 | ast::StructDef(it) => { | 89 | ast::StructDef(it) => { |
95 | let src = name.with_value(it); | 90 | let def: hir::Struct = sema.to_def(&it)?; |
96 | let def: hir::Struct = sb.to_def(src)?; | ||
97 | Some(from_module_def(def.into())) | 91 | Some(from_module_def(def.into())) |
98 | }, | 92 | }, |
99 | ast::EnumDef(it) => { | 93 | ast::EnumDef(it) => { |
100 | let src = name.with_value(it); | 94 | let def: hir::Enum = sema.to_def(&it)?; |
101 | let def: hir::Enum = sb.to_def(src)?; | ||
102 | Some(from_module_def(def.into())) | 95 | Some(from_module_def(def.into())) |
103 | }, | 96 | }, |
104 | ast::TraitDef(it) => { | 97 | ast::TraitDef(it) => { |
105 | let src = name.with_value(it); | 98 | let def: hir::Trait = sema.to_def(&it)?; |
106 | let def: hir::Trait = sb.to_def(src)?; | ||
107 | Some(from_module_def(def.into())) | 99 | Some(from_module_def(def.into())) |
108 | }, | 100 | }, |
109 | ast::StaticDef(it) => { | 101 | ast::StaticDef(it) => { |
110 | let src = name.with_value(it); | 102 | let def: hir::Static = sema.to_def(&it)?; |
111 | let def: hir::Static = sb.to_def(src)?; | ||
112 | Some(from_module_def(def.into())) | 103 | Some(from_module_def(def.into())) |
113 | }, | 104 | }, |
114 | ast::EnumVariant(it) => { | 105 | ast::EnumVariant(it) => { |
115 | let src = name.with_value(it); | 106 | let def: hir::EnumVariant = sema.to_def(&it)?; |
116 | let def: hir::EnumVariant = sb.to_def(src)?; | ||
117 | Some(from_module_def(def.into())) | 107 | Some(from_module_def(def.into())) |
118 | }, | 108 | }, |
119 | ast::FnDef(it) => { | 109 | ast::FnDef(it) => { |
120 | let src = name.with_value(it); | 110 | let def: hir::Function = sema.to_def(&it)?; |
121 | let def: hir::Function = sb.to_def(src)?; | ||
122 | Some(from_module_def(def.into())) | 111 | Some(from_module_def(def.into())) |
123 | }, | 112 | }, |
124 | ast::ConstDef(it) => { | 113 | ast::ConstDef(it) => { |
125 | let src = name.with_value(it); | 114 | let def: hir::Const = sema.to_def(&it)?; |
126 | let def: hir::Const = sb.to_def(src)?; | ||
127 | Some(from_module_def(def.into())) | 115 | Some(from_module_def(def.into())) |
128 | }, | 116 | }, |
129 | ast::TypeAliasDef(it) => { | 117 | ast::TypeAliasDef(it) => { |
130 | let src = name.with_value(it); | 118 | let def: hir::TypeAlias = sema.to_def(&it)?; |
131 | let def: hir::TypeAlias = sb.to_def(src)?; | ||
132 | Some(from_module_def(def.into())) | 119 | Some(from_module_def(def.into())) |
133 | }, | 120 | }, |
134 | ast::MacroCall(it) => { | 121 | ast::MacroCall(it) => { |
135 | let src = name.with_value(it); | 122 | let def = sema.to_def(&it)?; |
136 | let def = sb.to_def(src.clone())?; | ||
137 | |||
138 | Some(NameDefinition::Macro(def)) | 123 | Some(NameDefinition::Macro(def)) |
139 | }, | 124 | }, |
140 | ast::TypeParam(it) => { | 125 | ast::TypeParam(it) => { |
141 | let src = name.with_value(it); | 126 | let def = sema.to_def(&it)?; |
142 | let def = sb.to_def(src)?; | ||
143 | Some(NameDefinition::TypeParam(def)) | 127 | Some(NameDefinition::TypeParam(def)) |
144 | }, | 128 | }, |
145 | _ => None, | 129 | _ => None, |
diff --git a/crates/ra_ide_db/src/imports_locator.rs b/crates/ra_ide_db/src/imports_locator.rs index b8dd358a9..e590d2a5c 100644 --- a/crates/ra_ide_db/src/imports_locator.rs +++ b/crates/ra_ide_db/src/imports_locator.rs | |||
@@ -1,7 +1,7 @@ | |||
1 | //! This module contains an import search funcionality that is provided to the ra_assists module. | 1 | //! This module contains an import search funcionality that is provided to the ra_assists module. |
2 | //! Later, this should be moved away to a separate crate that is accessible from the ra_assists module. | 2 | //! Later, this should be moved away to a separate crate that is accessible from the ra_assists module. |
3 | 3 | ||
4 | use hir::{db::HirDatabase, ModuleDef, SourceBinder}; | 4 | use hir::{ModuleDef, Semantics}; |
5 | use ra_prof::profile; | 5 | use ra_prof::profile; |
6 | use ra_syntax::{ast, AstNode, SyntaxKind::NAME}; | 6 | use ra_syntax::{ast, AstNode, SyntaxKind::NAME}; |
7 | 7 | ||
@@ -12,17 +12,17 @@ use crate::{ | |||
12 | }; | 12 | }; |
13 | 13 | ||
14 | pub struct ImportsLocator<'a> { | 14 | pub struct ImportsLocator<'a> { |
15 | source_binder: SourceBinder<'a, RootDatabase>, | 15 | sema: Semantics<'a, RootDatabase>, |
16 | } | 16 | } |
17 | 17 | ||
18 | impl<'a> ImportsLocator<'a> { | 18 | impl<'a> ImportsLocator<'a> { |
19 | pub fn new(db: &'a RootDatabase) -> Self { | 19 | pub fn new(db: &'a RootDatabase) -> Self { |
20 | Self { source_binder: SourceBinder::new(db) } | 20 | Self { sema: Semantics::new(db) } |
21 | } | 21 | } |
22 | 22 | ||
23 | pub fn find_imports(&mut self, name_to_import: &str) -> Vec<ModuleDef> { | 23 | pub fn find_imports(&mut self, name_to_import: &str) -> Vec<ModuleDef> { |
24 | let _p = profile("search_for_imports"); | 24 | let _p = profile("search_for_imports"); |
25 | let db = self.source_binder.db; | 25 | let db = self.sema.db; |
26 | 26 | ||
27 | let project_results = { | 27 | let project_results = { |
28 | let mut query = Query::new(name_to_import.to_string()); | 28 | let mut query = Query::new(name_to_import.to_string()); |
@@ -41,7 +41,7 @@ impl<'a> ImportsLocator<'a> { | |||
41 | project_results | 41 | project_results |
42 | .into_iter() | 42 | .into_iter() |
43 | .chain(lib_results.into_iter()) | 43 | .chain(lib_results.into_iter()) |
44 | .filter_map(|import_candidate| self.get_name_definition(db, &import_candidate)) | 44 | .filter_map(|import_candidate| self.get_name_definition(&import_candidate)) |
45 | .filter_map(|name_definition_to_import| match name_definition_to_import { | 45 | .filter_map(|name_definition_to_import| match name_definition_to_import { |
46 | NameDefinition::ModuleDef(module_def) => Some(module_def), | 46 | NameDefinition::ModuleDef(module_def) => Some(module_def), |
47 | _ => None, | 47 | _ => None, |
@@ -49,22 +49,16 @@ impl<'a> ImportsLocator<'a> { | |||
49 | .collect() | 49 | .collect() |
50 | } | 50 | } |
51 | 51 | ||
52 | fn get_name_definition( | 52 | fn get_name_definition(&mut self, import_candidate: &FileSymbol) -> Option<NameDefinition> { |
53 | &mut self, | ||
54 | db: &impl HirDatabase, | ||
55 | import_candidate: &FileSymbol, | ||
56 | ) -> Option<NameDefinition> { | ||
57 | let _p = profile("get_name_definition"); | 53 | let _p = profile("get_name_definition"); |
58 | let file_id = import_candidate.file_id.into(); | 54 | let file_id = import_candidate.file_id; |
59 | let candidate_node = import_candidate.ptr.to_node(&db.parse_or_expand(file_id)?); | 55 | |
56 | let candidate_node = import_candidate.ptr.to_node(self.sema.parse(file_id).syntax()); | ||
60 | let candidate_name_node = if candidate_node.kind() != NAME { | 57 | let candidate_name_node = if candidate_node.kind() != NAME { |
61 | candidate_node.children().find(|it| it.kind() == NAME)? | 58 | candidate_node.children().find(|it| it.kind() == NAME)? |
62 | } else { | 59 | } else { |
63 | candidate_node | 60 | candidate_node |
64 | }; | 61 | }; |
65 | classify_name( | 62 | classify_name(&self.sema, &ast::Name::cast(candidate_name_node)?) |
66 | &mut self.source_binder, | ||
67 | hir::InFile { file_id, value: &ast::Name::cast(candidate_name_node)? }, | ||
68 | ) | ||
69 | } | 63 | } |
70 | } | 64 | } |
diff --git a/crates/ra_syntax/src/algo.rs b/crates/ra_syntax/src/algo.rs index 21fca99a6..f14bcbb35 100644 --- a/crates/ra_syntax/src/algo.rs +++ b/crates/ra_syntax/src/algo.rs | |||
@@ -4,7 +4,7 @@ use std::ops::RangeInclusive; | |||
4 | 4 | ||
5 | use itertools::Itertools; | 5 | use itertools::Itertools; |
6 | use ra_text_edit::TextEditBuilder; | 6 | use ra_text_edit::TextEditBuilder; |
7 | use rustc_hash::FxHashMap; | 7 | use rustc_hash::{FxHashMap, FxHashSet}; |
8 | 8 | ||
9 | use crate::{ | 9 | use crate::{ |
10 | AstNode, Direction, NodeOrToken, SyntaxElement, SyntaxNode, SyntaxNodePtr, TextRange, TextUnit, | 10 | AstNode, Direction, NodeOrToken, SyntaxElement, SyntaxNode, SyntaxNodePtr, TextRange, TextUnit, |
@@ -56,6 +56,11 @@ pub fn find_covering_element(root: &SyntaxNode, range: TextRange) -> SyntaxEleme | |||
56 | root.covering_element(range) | 56 | root.covering_element(range) |
57 | } | 57 | } |
58 | 58 | ||
59 | pub fn least_common_ancestor(u: &SyntaxNode, v: &SyntaxNode) -> Option<SyntaxNode> { | ||
60 | let u_ancestors = u.ancestors().collect::<FxHashSet<SyntaxNode>>(); | ||
61 | v.ancestors().find(|it| u_ancestors.contains(it)) | ||
62 | } | ||
63 | |||
59 | #[derive(Debug, PartialEq, Eq, Clone, Copy)] | 64 | #[derive(Debug, PartialEq, Eq, Clone, Copy)] |
60 | pub enum InsertPosition<T> { | 65 | pub enum InsertPosition<T> { |
61 | First, | 66 | First, |