diff options
Diffstat (limited to 'crates/ra_hir')
-rw-r--r-- | crates/ra_hir/src/code_model.rs | 94 | ||||
-rw-r--r-- | crates/ra_hir/src/from_source.rs | 265 | ||||
-rw-r--r-- | crates/ra_hir/src/lib.rs | 8 | ||||
-rw-r--r-- | crates/ra_hir/src/source_analyzer.rs | 450 | ||||
-rw-r--r-- | crates/ra_hir/src/source_binder.rs | 828 |
5 files changed, 850 insertions, 795 deletions
diff --git a/crates/ra_hir/src/code_model.rs b/crates/ra_hir/src/code_model.rs index a177cebca..500b34c17 100644 --- a/crates/ra_hir/src/code_model.rs +++ b/crates/ra_hir/src/code_model.rs | |||
@@ -7,7 +7,6 @@ use hir_def::{ | |||
7 | builtin_type::BuiltinType, | 7 | builtin_type::BuiltinType, |
8 | docs::Documentation, | 8 | docs::Documentation, |
9 | expr::{BindingAnnotation, Pat, PatId}, | 9 | expr::{BindingAnnotation, Pat, PatId}, |
10 | nameres::ModuleSource, | ||
11 | per_ns::PerNs, | 10 | per_ns::PerNs, |
12 | resolver::HasResolver, | 11 | resolver::HasResolver, |
13 | type_ref::{Mutability, TypeRef}, | 12 | type_ref::{Mutability, TypeRef}, |
@@ -21,8 +20,8 @@ use hir_expand::{ | |||
21 | MacroDefId, | 20 | MacroDefId, |
22 | }; | 21 | }; |
23 | use hir_ty::{ | 22 | use hir_ty::{ |
24 | autoderef, display::HirFormatter, expr::ExprValidator, ApplicationTy, Canonical, InEnvironment, | 23 | autoderef, display::HirFormatter, expr::ExprValidator, method_resolution, ApplicationTy, |
25 | TraitEnvironment, Ty, TyDefId, TypeCtor, TypeWalk, | 24 | Canonical, InEnvironment, TraitEnvironment, Ty, TyDefId, TypeCtor, TypeWalk, |
26 | }; | 25 | }; |
27 | use ra_db::{CrateId, Edition, FileId}; | 26 | use ra_db::{CrateId, Edition, FileId}; |
28 | use ra_prof::profile; | 27 | use ra_prof::profile; |
@@ -120,7 +119,8 @@ impl_froms!( | |||
120 | BuiltinType | 119 | BuiltinType |
121 | ); | 120 | ); |
122 | 121 | ||
123 | pub use hir_def::{attr::Attrs, visibility::Visibility}; | 122 | pub use hir_def::{attr::Attrs, visibility::Visibility, AssocItemId}; |
123 | use rustc_hash::FxHashSet; | ||
124 | 124 | ||
125 | impl Module { | 125 | impl Module { |
126 | pub(crate) fn new(krate: Crate, crate_module_id: LocalModuleId) -> Module { | 126 | pub(crate) fn new(krate: Crate, crate_module_id: LocalModuleId) -> Module { |
@@ -192,13 +192,14 @@ impl Module { | |||
192 | 192 | ||
193 | pub fn diagnostics(self, db: &impl HirDatabase, sink: &mut DiagnosticSink) { | 193 | pub fn diagnostics(self, db: &impl HirDatabase, sink: &mut DiagnosticSink) { |
194 | let _p = profile("Module::diagnostics"); | 194 | let _p = profile("Module::diagnostics"); |
195 | db.crate_def_map(self.id.krate).add_diagnostics(db, self.id.local_id, sink); | 195 | let crate_def_map = db.crate_def_map(self.id.krate); |
196 | crate_def_map.add_diagnostics(db, self.id.local_id, sink); | ||
196 | for decl in self.declarations(db) { | 197 | for decl in self.declarations(db) { |
197 | match decl { | 198 | match decl { |
198 | crate::ModuleDef::Function(f) => f.diagnostics(db, sink), | 199 | crate::ModuleDef::Function(f) => f.diagnostics(db, sink), |
199 | crate::ModuleDef::Module(m) => { | 200 | crate::ModuleDef::Module(m) => { |
200 | // Only add diagnostics from inline modules | 201 | // Only add diagnostics from inline modules |
201 | if let ModuleSource::Module(_) = m.definition_source(db).value { | 202 | if crate_def_map[m.id.local_id].origin.is_inline() { |
202 | m.diagnostics(db, sink) | 203 | m.diagnostics(db, sink) |
203 | } | 204 | } |
204 | } | 205 | } |
@@ -878,6 +879,28 @@ impl Type { | |||
878 | } | 879 | } |
879 | } | 880 | } |
880 | 881 | ||
882 | /// Checks that particular type `ty` implements `std::future::Future`. | ||
883 | /// This function is used in `.await` syntax completion. | ||
884 | pub fn impls_future(&self, db: &impl HirDatabase) -> bool { | ||
885 | let krate = self.krate; | ||
886 | |||
887 | let std_future_trait = | ||
888 | db.lang_item(krate, "future_trait".into()).and_then(|it| it.as_trait()); | ||
889 | let std_future_trait = match std_future_trait { | ||
890 | Some(it) => it, | ||
891 | None => return false, | ||
892 | }; | ||
893 | |||
894 | let canonical_ty = Canonical { value: self.ty.value.clone(), num_vars: 0 }; | ||
895 | method_resolution::implements_trait( | ||
896 | &canonical_ty, | ||
897 | db, | ||
898 | self.ty.environment.clone(), | ||
899 | krate, | ||
900 | std_future_trait, | ||
901 | ) | ||
902 | } | ||
903 | |||
881 | // FIXME: this method is broken, as it doesn't take closures into account. | 904 | // FIXME: this method is broken, as it doesn't take closures into account. |
882 | pub fn as_callable(&self) -> Option<CallableDef> { | 905 | pub fn as_callable(&self) -> Option<CallableDef> { |
883 | Some(self.ty.value.as_callable()?.0) | 906 | Some(self.ty.value.as_callable()?.0) |
@@ -986,6 +1009,65 @@ impl Type { | |||
986 | None | 1009 | None |
987 | } | 1010 | } |
988 | 1011 | ||
1012 | pub fn iterate_method_candidates<T>( | ||
1013 | &self, | ||
1014 | db: &impl HirDatabase, | ||
1015 | krate: Crate, | ||
1016 | traits_in_scope: &FxHashSet<TraitId>, | ||
1017 | name: Option<&Name>, | ||
1018 | mut callback: impl FnMut(&Ty, Function) -> Option<T>, | ||
1019 | ) -> Option<T> { | ||
1020 | // There should be no inference vars in types passed here | ||
1021 | // FIXME check that? | ||
1022 | // FIXME replace Unknown by bound vars here | ||
1023 | let canonical = Canonical { value: self.ty.value.clone(), num_vars: 0 }; | ||
1024 | |||
1025 | let env = self.ty.environment.clone(); | ||
1026 | let krate = krate.id; | ||
1027 | |||
1028 | method_resolution::iterate_method_candidates( | ||
1029 | &canonical, | ||
1030 | db, | ||
1031 | env, | ||
1032 | krate, | ||
1033 | traits_in_scope, | ||
1034 | name, | ||
1035 | method_resolution::LookupMode::MethodCall, | ||
1036 | |ty, it| match it { | ||
1037 | AssocItemId::FunctionId(f) => callback(ty, f.into()), | ||
1038 | _ => None, | ||
1039 | }, | ||
1040 | ) | ||
1041 | } | ||
1042 | |||
1043 | pub fn iterate_path_candidates<T>( | ||
1044 | &self, | ||
1045 | db: &impl HirDatabase, | ||
1046 | krate: Crate, | ||
1047 | traits_in_scope: &FxHashSet<TraitId>, | ||
1048 | name: Option<&Name>, | ||
1049 | mut callback: impl FnMut(&Ty, AssocItem) -> Option<T>, | ||
1050 | ) -> Option<T> { | ||
1051 | // There should be no inference vars in types passed here | ||
1052 | // FIXME check that? | ||
1053 | // FIXME replace Unknown by bound vars here | ||
1054 | let canonical = Canonical { value: self.ty.value.clone(), num_vars: 0 }; | ||
1055 | |||
1056 | let env = self.ty.environment.clone(); | ||
1057 | let krate = krate.id; | ||
1058 | |||
1059 | method_resolution::iterate_method_candidates( | ||
1060 | &canonical, | ||
1061 | db, | ||
1062 | env, | ||
1063 | krate, | ||
1064 | traits_in_scope, | ||
1065 | name, | ||
1066 | method_resolution::LookupMode::Path, | ||
1067 | |ty, it| callback(ty, it.into()), | ||
1068 | ) | ||
1069 | } | ||
1070 | |||
989 | pub fn as_adt(&self) -> Option<Adt> { | 1071 | pub fn as_adt(&self) -> Option<Adt> { |
990 | let (adt, _subst) = self.ty.value.as_adt()?; | 1072 | let (adt, _subst) = self.ty.value.as_adt()?; |
991 | Some(adt.into()) | 1073 | Some(adt.into()) |
diff --git a/crates/ra_hir/src/from_source.rs b/crates/ra_hir/src/from_source.rs deleted file mode 100644 index 6314be8d4..000000000 --- a/crates/ra_hir/src/from_source.rs +++ /dev/null | |||
@@ -1,265 +0,0 @@ | |||
1 | //! Finds a corresponding hir data structure for a syntax node in a specific | ||
2 | //! file. | ||
3 | |||
4 | use hir_def::{ | ||
5 | child_by_source::ChildBySource, dyn_map::DynMap, keys, keys::Key, nameres::ModuleSource, | ||
6 | ConstId, DefWithBodyId, EnumId, EnumVariantId, FunctionId, GenericDefId, ImplId, ModuleId, | ||
7 | StaticId, StructId, TraitId, TypeAliasId, UnionId, VariantId, | ||
8 | }; | ||
9 | use hir_expand::{name::AsName, AstId, MacroDefId, MacroDefKind}; | ||
10 | use ra_db::FileId; | ||
11 | use ra_prof::profile; | ||
12 | use ra_syntax::{ | ||
13 | ast::{self, AstNode, NameOwner}, | ||
14 | match_ast, SyntaxNode, | ||
15 | }; | ||
16 | |||
17 | use crate::{ | ||
18 | db::{DefDatabase, HirDatabase}, | ||
19 | Const, DefWithBody, Enum, EnumVariant, FieldSource, Function, ImplBlock, InFile, Local, | ||
20 | MacroDef, Module, Static, Struct, StructField, Trait, TypeAlias, TypeParam, Union, | ||
21 | }; | ||
22 | |||
23 | pub trait FromSource: Sized { | ||
24 | type Ast; | ||
25 | fn from_source(db: &impl DefDatabase, src: InFile<Self::Ast>) -> Option<Self>; | ||
26 | } | ||
27 | |||
28 | pub trait FromSourceByContainer: Sized { | ||
29 | type Ast: AstNode + 'static; | ||
30 | type Id: Copy + 'static; | ||
31 | const KEY: Key<Self::Ast, Self::Id>; | ||
32 | } | ||
33 | |||
34 | impl<T: FromSourceByContainer> FromSource for T | ||
35 | where | ||
36 | T: From<<T as FromSourceByContainer>::Id>, | ||
37 | { | ||
38 | type Ast = <T as FromSourceByContainer>::Ast; | ||
39 | fn from_source(db: &impl DefDatabase, src: InFile<Self::Ast>) -> Option<Self> { | ||
40 | analyze_container(db, src.as_ref().map(|it| it.syntax()))[T::KEY] | ||
41 | .get(&src) | ||
42 | .copied() | ||
43 | .map(Self::from) | ||
44 | } | ||
45 | } | ||
46 | |||
47 | macro_rules! from_source_by_container_impls { | ||
48 | ($(($hir:ident, $id:ident, $ast:path, $key:path)),* ,) => {$( | ||
49 | impl FromSourceByContainer for $hir { | ||
50 | type Ast = $ast; | ||
51 | type Id = $id; | ||
52 | const KEY: Key<Self::Ast, Self::Id> = $key; | ||
53 | } | ||
54 | )*} | ||
55 | } | ||
56 | |||
57 | from_source_by_container_impls![ | ||
58 | (Struct, StructId, ast::StructDef, keys::STRUCT), | ||
59 | (Union, UnionId, ast::UnionDef, keys::UNION), | ||
60 | (Enum, EnumId, ast::EnumDef, keys::ENUM), | ||
61 | (Trait, TraitId, ast::TraitDef, keys::TRAIT), | ||
62 | (Function, FunctionId, ast::FnDef, keys::FUNCTION), | ||
63 | (Static, StaticId, ast::StaticDef, keys::STATIC), | ||
64 | (Const, ConstId, ast::ConstDef, keys::CONST), | ||
65 | (TypeAlias, TypeAliasId, ast::TypeAliasDef, keys::TYPE_ALIAS), | ||
66 | (ImplBlock, ImplId, ast::ImplBlock, keys::IMPL), | ||
67 | ]; | ||
68 | |||
69 | impl FromSource for MacroDef { | ||
70 | type Ast = ast::MacroCall; | ||
71 | fn from_source(db: &impl DefDatabase, src: InFile<Self::Ast>) -> Option<Self> { | ||
72 | let kind = MacroDefKind::Declarative; | ||
73 | |||
74 | let module_src = ModuleSource::from_child_node(db, src.as_ref().map(|it| it.syntax())); | ||
75 | let module = Module::from_definition(db, InFile::new(src.file_id, module_src))?; | ||
76 | let krate = Some(module.krate().id); | ||
77 | |||
78 | let ast_id = Some(AstId::new(src.file_id, db.ast_id_map(src.file_id).ast_id(&src.value))); | ||
79 | |||
80 | let id: MacroDefId = MacroDefId { krate, ast_id, kind }; | ||
81 | Some(MacroDef { id }) | ||
82 | } | ||
83 | } | ||
84 | |||
85 | impl FromSource for EnumVariant { | ||
86 | type Ast = ast::EnumVariant; | ||
87 | fn from_source(db: &impl DefDatabase, src: InFile<Self::Ast>) -> Option<Self> { | ||
88 | let parent_enum = src.value.parent_enum(); | ||
89 | let src_enum = InFile { file_id: src.file_id, value: parent_enum }; | ||
90 | let parent_enum = Enum::from_source(db, src_enum)?; | ||
91 | parent_enum.id.child_by_source(db)[keys::ENUM_VARIANT] | ||
92 | .get(&src) | ||
93 | .copied() | ||
94 | .map(EnumVariant::from) | ||
95 | } | ||
96 | } | ||
97 | |||
98 | impl FromSource for StructField { | ||
99 | type Ast = FieldSource; | ||
100 | fn from_source(db: &impl DefDatabase, src: InFile<Self::Ast>) -> Option<Self> { | ||
101 | let src = src.as_ref(); | ||
102 | |||
103 | // FIXME this is buggy | ||
104 | let variant_id: VariantId = match src.value { | ||
105 | FieldSource::Named(field) => { | ||
106 | let value = field.syntax().ancestors().find_map(ast::StructDef::cast)?; | ||
107 | let src = InFile { file_id: src.file_id, value }; | ||
108 | let def = Struct::from_source(db, src)?; | ||
109 | def.id.into() | ||
110 | } | ||
111 | FieldSource::Pos(field) => { | ||
112 | let value = field.syntax().ancestors().find_map(ast::EnumVariant::cast)?; | ||
113 | let src = InFile { file_id: src.file_id, value }; | ||
114 | let def = EnumVariant::from_source(db, src)?; | ||
115 | EnumVariantId::from(def).into() | ||
116 | } | ||
117 | }; | ||
118 | |||
119 | let dyn_map = variant_id.child_by_source(db); | ||
120 | match src.value { | ||
121 | FieldSource::Pos(it) => dyn_map[keys::TUPLE_FIELD].get(&src.with_value(it.clone())), | ||
122 | FieldSource::Named(it) => dyn_map[keys::RECORD_FIELD].get(&src.with_value(it.clone())), | ||
123 | } | ||
124 | .copied() | ||
125 | .map(StructField::from) | ||
126 | } | ||
127 | } | ||
128 | |||
129 | impl Local { | ||
130 | pub fn from_source(db: &impl HirDatabase, src: InFile<ast::BindPat>) -> Option<Self> { | ||
131 | let file_id = src.file_id; | ||
132 | let parent: DefWithBody = src.value.syntax().ancestors().find_map(|it| { | ||
133 | let res = match_ast! { | ||
134 | match it { | ||
135 | ast::ConstDef(value) => { Const::from_source(db, InFile { value, file_id})?.into() }, | ||
136 | ast::StaticDef(value) => { Static::from_source(db, InFile { value, file_id})?.into() }, | ||
137 | ast::FnDef(value) => { Function::from_source(db, InFile { value, file_id})?.into() }, | ||
138 | _ => return None, | ||
139 | } | ||
140 | }; | ||
141 | Some(res) | ||
142 | })?; | ||
143 | let (_body, source_map) = db.body_with_source_map(parent.into()); | ||
144 | let src = src.map(ast::Pat::from); | ||
145 | let pat_id = source_map.node_pat(src.as_ref())?; | ||
146 | Some(Local { parent, pat_id }) | ||
147 | } | ||
148 | } | ||
149 | |||
150 | impl TypeParam { | ||
151 | pub fn from_source(db: &impl HirDatabase, src: InFile<ast::TypeParam>) -> Option<Self> { | ||
152 | let file_id = src.file_id; | ||
153 | let parent: GenericDefId = src.value.syntax().ancestors().find_map(|it| { | ||
154 | let res = match_ast! { | ||
155 | match it { | ||
156 | ast::FnDef(value) => { Function::from_source(db, InFile { value, file_id})?.id.into() }, | ||
157 | ast::StructDef(value) => { Struct::from_source(db, InFile { value, file_id})?.id.into() }, | ||
158 | ast::EnumDef(value) => { Enum::from_source(db, InFile { value, file_id})?.id.into() }, | ||
159 | ast::TraitDef(value) => { Trait::from_source(db, InFile { value, file_id})?.id.into() }, | ||
160 | ast::TypeAliasDef(value) => { TypeAlias::from_source(db, InFile { value, file_id})?.id.into() }, | ||
161 | ast::ImplBlock(value) => { ImplBlock::from_source(db, InFile { value, file_id})?.id.into() }, | ||
162 | _ => return None, | ||
163 | } | ||
164 | }; | ||
165 | Some(res) | ||
166 | })?; | ||
167 | let &id = parent.child_by_source(db)[keys::TYPE_PARAM].get(&src)?; | ||
168 | Some(TypeParam { id }) | ||
169 | } | ||
170 | } | ||
171 | |||
172 | impl Module { | ||
173 | pub fn from_declaration(db: &impl DefDatabase, src: InFile<ast::Module>) -> Option<Self> { | ||
174 | let _p = profile("Module::from_declaration"); | ||
175 | let parent_declaration = src.value.syntax().ancestors().skip(1).find_map(ast::Module::cast); | ||
176 | |||
177 | let parent_module = match parent_declaration { | ||
178 | Some(parent_declaration) => { | ||
179 | let src_parent = InFile { file_id: src.file_id, value: parent_declaration }; | ||
180 | Module::from_declaration(db, src_parent) | ||
181 | } | ||
182 | None => { | ||
183 | let source_file = db.parse(src.file_id.original_file(db)).tree(); | ||
184 | let src_parent = | ||
185 | InFile { file_id: src.file_id, value: ModuleSource::SourceFile(source_file) }; | ||
186 | Module::from_definition(db, src_parent) | ||
187 | } | ||
188 | }?; | ||
189 | |||
190 | let child_name = src.value.name()?.as_name(); | ||
191 | let def_map = db.crate_def_map(parent_module.id.krate); | ||
192 | let child_id = def_map[parent_module.id.local_id].children.get(&child_name)?; | ||
193 | Some(parent_module.with_module_id(*child_id)) | ||
194 | } | ||
195 | |||
196 | pub fn from_definition(db: &impl DefDatabase, src: InFile<ModuleSource>) -> Option<Self> { | ||
197 | let _p = profile("Module::from_definition"); | ||
198 | match src.value { | ||
199 | ModuleSource::Module(ref module) => { | ||
200 | assert!(!module.has_semi()); | ||
201 | return Module::from_declaration( | ||
202 | db, | ||
203 | InFile { file_id: src.file_id, value: module.clone() }, | ||
204 | ); | ||
205 | } | ||
206 | ModuleSource::SourceFile(_) => (), | ||
207 | }; | ||
208 | |||
209 | let original_file = src.file_id.original_file(db); | ||
210 | Module::from_file(db, original_file) | ||
211 | } | ||
212 | |||
213 | fn from_file(db: &impl DefDatabase, file: FileId) -> Option<Self> { | ||
214 | let _p = profile("Module::from_file"); | ||
215 | let (krate, local_id) = db.relevant_crates(file).iter().find_map(|&crate_id| { | ||
216 | let crate_def_map = db.crate_def_map(crate_id); | ||
217 | let local_id = crate_def_map.modules_for_file(file).next()?; | ||
218 | Some((crate_id, local_id)) | ||
219 | })?; | ||
220 | Some(Module { id: ModuleId { krate, local_id } }) | ||
221 | } | ||
222 | } | ||
223 | |||
224 | fn analyze_container(db: &impl DefDatabase, src: InFile<&SyntaxNode>) -> DynMap { | ||
225 | let _p = profile("analyze_container"); | ||
226 | return child_by_source(db, src).unwrap_or_default(); | ||
227 | |||
228 | fn child_by_source(db: &impl DefDatabase, src: InFile<&SyntaxNode>) -> Option<DynMap> { | ||
229 | for container in src.value.ancestors().skip(1) { | ||
230 | let res = match_ast! { | ||
231 | match container { | ||
232 | ast::TraitDef(it) => { | ||
233 | let def = Trait::from_source(db, src.with_value(it))?; | ||
234 | def.id.child_by_source(db) | ||
235 | }, | ||
236 | ast::ImplBlock(it) => { | ||
237 | let def = ImplBlock::from_source(db, src.with_value(it))?; | ||
238 | def.id.child_by_source(db) | ||
239 | }, | ||
240 | ast::FnDef(it) => { | ||
241 | let def = Function::from_source(db, src.with_value(it))?; | ||
242 | DefWithBodyId::from(def.id) | ||
243 | .child_by_source(db) | ||
244 | }, | ||
245 | ast::StaticDef(it) => { | ||
246 | let def = Static::from_source(db, src.with_value(it))?; | ||
247 | DefWithBodyId::from(def.id) | ||
248 | .child_by_source(db) | ||
249 | }, | ||
250 | ast::ConstDef(it) => { | ||
251 | let def = Const::from_source(db, src.with_value(it))?; | ||
252 | DefWithBodyId::from(def.id) | ||
253 | .child_by_source(db) | ||
254 | }, | ||
255 | _ => { continue }, | ||
256 | } | ||
257 | }; | ||
258 | return Some(res); | ||
259 | } | ||
260 | |||
261 | let module_source = ModuleSource::from_child_node(db, src); | ||
262 | let c = Module::from_definition(db, src.with_value(module_source))?; | ||
263 | Some(c.id.child_by_source(db)) | ||
264 | } | ||
265 | } | ||
diff --git a/crates/ra_hir/src/lib.rs b/crates/ra_hir/src/lib.rs index 3d13978d4..e1c7b7a20 100644 --- a/crates/ra_hir/src/lib.rs +++ b/crates/ra_hir/src/lib.rs | |||
@@ -8,7 +8,7 @@ | |||
8 | #![recursion_limit = "512"] | 8 | #![recursion_limit = "512"] |
9 | 9 | ||
10 | macro_rules! impl_froms { | 10 | macro_rules! impl_froms { |
11 | ($e:ident: $($v:ident $(($($sv:ident),*))?),*) => { | 11 | ($e:ident: $($v:ident $(($($sv:ident),*))?),*$(,)?) => { |
12 | $( | 12 | $( |
13 | impl From<$v> for $e { | 13 | impl From<$v> for $e { |
14 | fn from(it: $v) -> $e { | 14 | fn from(it: $v) -> $e { |
@@ -27,6 +27,7 @@ macro_rules! impl_froms { | |||
27 | } | 27 | } |
28 | 28 | ||
29 | pub mod db; | 29 | pub mod db; |
30 | pub mod source_analyzer; | ||
30 | pub mod source_binder; | 31 | pub mod source_binder; |
31 | 32 | ||
32 | pub mod diagnostics; | 33 | pub mod diagnostics; |
@@ -35,7 +36,6 @@ mod from_id; | |||
35 | mod code_model; | 36 | mod code_model; |
36 | 37 | ||
37 | mod has_source; | 38 | mod has_source; |
38 | mod from_source; | ||
39 | 39 | ||
40 | pub use crate::{ | 40 | pub use crate::{ |
41 | code_model::{ | 41 | code_model::{ |
@@ -44,9 +44,9 @@ pub use crate::{ | |||
44 | MacroDef, Module, ModuleDef, ScopeDef, Static, Struct, StructField, Trait, Type, TypeAlias, | 44 | MacroDef, Module, ModuleDef, ScopeDef, Static, Struct, StructField, Trait, Type, TypeAlias, |
45 | TypeParam, Union, VariantDef, | 45 | TypeParam, Union, VariantDef, |
46 | }, | 46 | }, |
47 | from_source::FromSource, | ||
48 | has_source::HasSource, | 47 | has_source::HasSource, |
49 | source_binder::{PathResolution, ScopeEntryWithSyntax, SourceAnalyzer}, | 48 | source_analyzer::{PathResolution, ScopeEntryWithSyntax, SourceAnalyzer}, |
49 | source_binder::SourceBinder, | ||
50 | }; | 50 | }; |
51 | 51 | ||
52 | pub use hir_def::{ | 52 | pub use hir_def::{ |
diff --git a/crates/ra_hir/src/source_analyzer.rs b/crates/ra_hir/src/source_analyzer.rs new file mode 100644 index 000000000..4f8fc9602 --- /dev/null +++ b/crates/ra_hir/src/source_analyzer.rs | |||
@@ -0,0 +1,450 @@ | |||
1 | //! Lookup hir elements using positions in the source code. This is a lossy | ||
2 | //! transformation: in general, a single source might correspond to several | ||
3 | //! modules, functions, etc, due to macros, cfgs and `#[path=]` attributes on | ||
4 | //! modules. | ||
5 | //! | ||
6 | //! So, this modules should not be used during hir construction, it exists | ||
7 | //! purely for "IDE needs". | ||
8 | use std::sync::Arc; | ||
9 | |||
10 | use either::Either; | ||
11 | use hir_def::{ | ||
12 | body::{ | ||
13 | scope::{ExprScopes, ScopeId}, | ||
14 | BodySourceMap, | ||
15 | }, | ||
16 | expr::{ExprId, PatId}, | ||
17 | resolver::{self, resolver_for_scope, Resolver, TypeNs, ValueNs}, | ||
18 | DefWithBodyId, TraitId, | ||
19 | }; | ||
20 | use hir_expand::{ | ||
21 | hygiene::Hygiene, name::AsName, AstId, HirFileId, InFile, MacroCallId, MacroCallKind, | ||
22 | }; | ||
23 | use hir_ty::{InEnvironment, InferenceResult, TraitEnvironment}; | ||
24 | use ra_syntax::{ | ||
25 | ast::{self, AstNode}, | ||
26 | AstPtr, SyntaxNode, SyntaxNodePtr, SyntaxToken, TextRange, TextUnit, | ||
27 | }; | ||
28 | use rustc_hash::FxHashSet; | ||
29 | |||
30 | use crate::{ | ||
31 | db::HirDatabase, Adt, Const, DefWithBody, EnumVariant, Function, Local, MacroDef, Name, Path, | ||
32 | ScopeDef, Static, Struct, Trait, Type, TypeAlias, TypeParam, | ||
33 | }; | ||
34 | |||
35 | /// `SourceAnalyzer` is a convenience wrapper which exposes HIR API in terms of | ||
36 | /// original source files. It should not be used inside the HIR itself. | ||
37 | #[derive(Debug)] | ||
38 | pub struct SourceAnalyzer { | ||
39 | file_id: HirFileId, | ||
40 | resolver: Resolver, | ||
41 | body_owner: Option<DefWithBody>, | ||
42 | body_source_map: Option<Arc<BodySourceMap>>, | ||
43 | infer: Option<Arc<InferenceResult>>, | ||
44 | scopes: Option<Arc<ExprScopes>>, | ||
45 | } | ||
46 | |||
47 | #[derive(Debug, Clone, PartialEq, Eq)] | ||
48 | pub enum PathResolution { | ||
49 | /// An item | ||
50 | Def(crate::ModuleDef), | ||
51 | /// A local binding (only value namespace) | ||
52 | Local(Local), | ||
53 | /// A generic parameter | ||
54 | TypeParam(TypeParam), | ||
55 | SelfType(crate::ImplBlock), | ||
56 | Macro(MacroDef), | ||
57 | AssocItem(crate::AssocItem), | ||
58 | } | ||
59 | |||
60 | #[derive(Debug, Clone, PartialEq, Eq)] | ||
61 | pub struct ScopeEntryWithSyntax { | ||
62 | pub(crate) name: Name, | ||
63 | pub(crate) ptr: Either<AstPtr<ast::Pat>, AstPtr<ast::SelfParam>>, | ||
64 | } | ||
65 | |||
66 | impl ScopeEntryWithSyntax { | ||
67 | pub fn name(&self) -> &Name { | ||
68 | &self.name | ||
69 | } | ||
70 | |||
71 | pub fn ptr(&self) -> Either<AstPtr<ast::Pat>, AstPtr<ast::SelfParam>> { | ||
72 | self.ptr | ||
73 | } | ||
74 | } | ||
75 | |||
76 | #[derive(Debug)] | ||
77 | pub struct ReferenceDescriptor { | ||
78 | pub range: TextRange, | ||
79 | pub name: String, | ||
80 | } | ||
81 | |||
82 | #[derive(Debug)] | ||
83 | pub struct Expansion { | ||
84 | macro_call_id: MacroCallId, | ||
85 | } | ||
86 | |||
87 | impl Expansion { | ||
88 | pub fn map_token_down( | ||
89 | &self, | ||
90 | db: &impl HirDatabase, | ||
91 | token: InFile<&SyntaxToken>, | ||
92 | ) -> Option<InFile<SyntaxToken>> { | ||
93 | let exp_info = self.file_id().expansion_info(db)?; | ||
94 | exp_info.map_token_down(token) | ||
95 | } | ||
96 | |||
97 | pub fn file_id(&self) -> HirFileId { | ||
98 | self.macro_call_id.as_file() | ||
99 | } | ||
100 | } | ||
101 | |||
102 | impl SourceAnalyzer { | ||
103 | pub fn new( | ||
104 | db: &impl HirDatabase, | ||
105 | node: InFile<&SyntaxNode>, | ||
106 | offset: Option<TextUnit>, | ||
107 | ) -> SourceAnalyzer { | ||
108 | crate::source_binder::SourceBinder::new(db).analyze(node, offset) | ||
109 | } | ||
110 | |||
111 | pub(crate) fn new_for_body( | ||
112 | db: &impl HirDatabase, | ||
113 | def: DefWithBodyId, | ||
114 | node: InFile<&SyntaxNode>, | ||
115 | offset: Option<TextUnit>, | ||
116 | ) -> SourceAnalyzer { | ||
117 | let (_body, source_map) = db.body_with_source_map(def); | ||
118 | let scopes = db.expr_scopes(def); | ||
119 | let scope = match offset { | ||
120 | None => scope_for(&scopes, &source_map, node), | ||
121 | Some(offset) => scope_for_offset(&scopes, &source_map, node.with_value(offset)), | ||
122 | }; | ||
123 | let resolver = resolver_for_scope(db, def, scope); | ||
124 | SourceAnalyzer { | ||
125 | resolver, | ||
126 | body_owner: Some(def.into()), | ||
127 | body_source_map: Some(source_map), | ||
128 | infer: Some(db.infer(def)), | ||
129 | scopes: Some(scopes), | ||
130 | file_id: node.file_id, | ||
131 | } | ||
132 | } | ||
133 | |||
134 | pub(crate) fn new_for_resolver( | ||
135 | resolver: Resolver, | ||
136 | node: InFile<&SyntaxNode>, | ||
137 | ) -> SourceAnalyzer { | ||
138 | SourceAnalyzer { | ||
139 | resolver, | ||
140 | body_owner: None, | ||
141 | body_source_map: None, | ||
142 | infer: None, | ||
143 | scopes: None, | ||
144 | file_id: node.file_id, | ||
145 | } | ||
146 | } | ||
147 | |||
148 | pub fn module(&self) -> Option<crate::code_model::Module> { | ||
149 | Some(crate::code_model::Module { id: self.resolver.module()? }) | ||
150 | } | ||
151 | |||
152 | fn expr_id(&self, expr: &ast::Expr) -> Option<ExprId> { | ||
153 | let src = InFile { file_id: self.file_id, value: expr }; | ||
154 | self.body_source_map.as_ref()?.node_expr(src) | ||
155 | } | ||
156 | |||
157 | fn pat_id(&self, pat: &ast::Pat) -> Option<PatId> { | ||
158 | let src = InFile { file_id: self.file_id, value: pat }; | ||
159 | self.body_source_map.as_ref()?.node_pat(src) | ||
160 | } | ||
161 | |||
162 | fn expand_expr( | ||
163 | &self, | ||
164 | db: &impl HirDatabase, | ||
165 | expr: InFile<&ast::Expr>, | ||
166 | ) -> Option<InFile<ast::Expr>> { | ||
167 | let macro_call = ast::MacroCall::cast(expr.value.syntax().clone())?; | ||
168 | let macro_file = | ||
169 | self.body_source_map.as_ref()?.node_macro_file(expr.with_value(¯o_call))?; | ||
170 | let expanded = db.parse_or_expand(macro_file)?; | ||
171 | let kind = expanded.kind(); | ||
172 | let expr = InFile::new(macro_file, ast::Expr::cast(expanded)?); | ||
173 | |||
174 | if ast::MacroCall::can_cast(kind) { | ||
175 | self.expand_expr(db, expr.as_ref()) | ||
176 | } else { | ||
177 | Some(expr) | ||
178 | } | ||
179 | } | ||
180 | |||
181 | pub fn type_of(&self, db: &impl HirDatabase, expr: &ast::Expr) -> Option<Type> { | ||
182 | let expr_id = if let Some(expr) = self.expand_expr(db, InFile::new(self.file_id, expr)) { | ||
183 | self.body_source_map.as_ref()?.node_expr(expr.as_ref())? | ||
184 | } else { | ||
185 | self.expr_id(expr)? | ||
186 | }; | ||
187 | |||
188 | let ty = self.infer.as_ref()?[expr_id].clone(); | ||
189 | let environment = TraitEnvironment::lower(db, &self.resolver); | ||
190 | Some(Type { krate: self.resolver.krate()?, ty: InEnvironment { value: ty, environment } }) | ||
191 | } | ||
192 | |||
193 | pub fn type_of_pat(&self, db: &impl HirDatabase, pat: &ast::Pat) -> Option<Type> { | ||
194 | let pat_id = self.pat_id(pat)?; | ||
195 | let ty = self.infer.as_ref()?[pat_id].clone(); | ||
196 | let environment = TraitEnvironment::lower(db, &self.resolver); | ||
197 | Some(Type { krate: self.resolver.krate()?, ty: InEnvironment { value: ty, environment } }) | ||
198 | } | ||
199 | |||
200 | pub fn resolve_method_call(&self, call: &ast::MethodCallExpr) -> Option<Function> { | ||
201 | let expr_id = self.expr_id(&call.clone().into())?; | ||
202 | self.infer.as_ref()?.method_resolution(expr_id).map(Function::from) | ||
203 | } | ||
204 | |||
205 | pub fn resolve_field(&self, field: &ast::FieldExpr) -> Option<crate::StructField> { | ||
206 | let expr_id = self.expr_id(&field.clone().into())?; | ||
207 | self.infer.as_ref()?.field_resolution(expr_id).map(|it| it.into()) | ||
208 | } | ||
209 | |||
210 | pub fn resolve_record_field(&self, field: &ast::RecordField) -> Option<crate::StructField> { | ||
211 | let expr_id = match field.expr() { | ||
212 | Some(it) => self.expr_id(&it)?, | ||
213 | None => { | ||
214 | let src = InFile { file_id: self.file_id, value: field }; | ||
215 | self.body_source_map.as_ref()?.field_init_shorthand_expr(src)? | ||
216 | } | ||
217 | }; | ||
218 | self.infer.as_ref()?.record_field_resolution(expr_id).map(|it| it.into()) | ||
219 | } | ||
220 | |||
221 | pub fn resolve_record_literal(&self, record_lit: &ast::RecordLit) -> Option<crate::VariantDef> { | ||
222 | let expr_id = self.expr_id(&record_lit.clone().into())?; | ||
223 | self.infer.as_ref()?.variant_resolution_for_expr(expr_id).map(|it| it.into()) | ||
224 | } | ||
225 | |||
226 | pub fn resolve_record_pattern(&self, record_pat: &ast::RecordPat) -> Option<crate::VariantDef> { | ||
227 | let pat_id = self.pat_id(&record_pat.clone().into())?; | ||
228 | self.infer.as_ref()?.variant_resolution_for_pat(pat_id).map(|it| it.into()) | ||
229 | } | ||
230 | |||
231 | pub fn resolve_macro_call( | ||
232 | &self, | ||
233 | db: &impl HirDatabase, | ||
234 | macro_call: InFile<&ast::MacroCall>, | ||
235 | ) -> Option<MacroDef> { | ||
236 | let hygiene = Hygiene::new(db, macro_call.file_id); | ||
237 | let path = macro_call.value.path().and_then(|ast| Path::from_src(ast, &hygiene))?; | ||
238 | self.resolver.resolve_path_as_macro(db, path.mod_path()).map(|it| it.into()) | ||
239 | } | ||
240 | |||
241 | pub fn resolve_hir_path( | ||
242 | &self, | ||
243 | db: &impl HirDatabase, | ||
244 | path: &crate::Path, | ||
245 | ) -> Option<PathResolution> { | ||
246 | let types = | ||
247 | self.resolver.resolve_path_in_type_ns_fully(db, path.mod_path()).map(|ty| match ty { | ||
248 | TypeNs::SelfType(it) => PathResolution::SelfType(it.into()), | ||
249 | TypeNs::GenericParam(id) => PathResolution::TypeParam(TypeParam { id }), | ||
250 | TypeNs::AdtSelfType(it) | TypeNs::AdtId(it) => { | ||
251 | PathResolution::Def(Adt::from(it).into()) | ||
252 | } | ||
253 | TypeNs::EnumVariantId(it) => PathResolution::Def(EnumVariant::from(it).into()), | ||
254 | TypeNs::TypeAliasId(it) => PathResolution::Def(TypeAlias::from(it).into()), | ||
255 | TypeNs::BuiltinType(it) => PathResolution::Def(it.into()), | ||
256 | TypeNs::TraitId(it) => PathResolution::Def(Trait::from(it).into()), | ||
257 | }); | ||
258 | let values = | ||
259 | self.resolver.resolve_path_in_value_ns_fully(db, path.mod_path()).and_then(|val| { | ||
260 | let res = match val { | ||
261 | ValueNs::LocalBinding(pat_id) => { | ||
262 | let var = Local { parent: self.body_owner?, pat_id }; | ||
263 | PathResolution::Local(var) | ||
264 | } | ||
265 | ValueNs::FunctionId(it) => PathResolution::Def(Function::from(it).into()), | ||
266 | ValueNs::ConstId(it) => PathResolution::Def(Const::from(it).into()), | ||
267 | ValueNs::StaticId(it) => PathResolution::Def(Static::from(it).into()), | ||
268 | ValueNs::StructId(it) => PathResolution::Def(Struct::from(it).into()), | ||
269 | ValueNs::EnumVariantId(it) => PathResolution::Def(EnumVariant::from(it).into()), | ||
270 | }; | ||
271 | Some(res) | ||
272 | }); | ||
273 | |||
274 | let items = self | ||
275 | .resolver | ||
276 | .resolve_module_path_in_items(db, path.mod_path()) | ||
277 | .take_types() | ||
278 | .map(|it| PathResolution::Def(it.into())); | ||
279 | types.or(values).or(items).or_else(|| { | ||
280 | self.resolver | ||
281 | .resolve_path_as_macro(db, path.mod_path()) | ||
282 | .map(|def| PathResolution::Macro(def.into())) | ||
283 | }) | ||
284 | } | ||
285 | |||
286 | pub fn resolve_path(&self, db: &impl HirDatabase, path: &ast::Path) -> Option<PathResolution> { | ||
287 | if let Some(path_expr) = path.syntax().parent().and_then(ast::PathExpr::cast) { | ||
288 | let expr_id = self.expr_id(&path_expr.into())?; | ||
289 | if let Some(assoc) = self.infer.as_ref()?.assoc_resolutions_for_expr(expr_id) { | ||
290 | return Some(PathResolution::AssocItem(assoc.into())); | ||
291 | } | ||
292 | } | ||
293 | if let Some(path_pat) = path.syntax().parent().and_then(ast::PathPat::cast) { | ||
294 | let pat_id = self.pat_id(&path_pat.into())?; | ||
295 | if let Some(assoc) = self.infer.as_ref()?.assoc_resolutions_for_pat(pat_id) { | ||
296 | return Some(PathResolution::AssocItem(assoc.into())); | ||
297 | } | ||
298 | } | ||
299 | // This must be a normal source file rather than macro file. | ||
300 | let hir_path = crate::Path::from_ast(path.clone())?; | ||
301 | self.resolve_hir_path(db, &hir_path) | ||
302 | } | ||
303 | |||
304 | fn resolve_local_name(&self, name_ref: &ast::NameRef) -> Option<ScopeEntryWithSyntax> { | ||
305 | let name = name_ref.as_name(); | ||
306 | let source_map = self.body_source_map.as_ref()?; | ||
307 | let scopes = self.scopes.as_ref()?; | ||
308 | let scope = scope_for(scopes, source_map, InFile::new(self.file_id, name_ref.syntax()))?; | ||
309 | let entry = scopes.resolve_name_in_scope(scope, &name)?; | ||
310 | Some(ScopeEntryWithSyntax { | ||
311 | name: entry.name().clone(), | ||
312 | ptr: source_map.pat_syntax(entry.pat())?.value, | ||
313 | }) | ||
314 | } | ||
315 | |||
316 | pub fn process_all_names(&self, db: &impl HirDatabase, f: &mut dyn FnMut(Name, ScopeDef)) { | ||
317 | self.resolver.process_all_names(db, &mut |name, def| { | ||
318 | let def = match def { | ||
319 | resolver::ScopeDef::PerNs(it) => it.into(), | ||
320 | resolver::ScopeDef::ImplSelfType(it) => ScopeDef::ImplSelfType(it.into()), | ||
321 | resolver::ScopeDef::AdtSelfType(it) => ScopeDef::AdtSelfType(it.into()), | ||
322 | resolver::ScopeDef::GenericParam(id) => ScopeDef::GenericParam(TypeParam { id }), | ||
323 | resolver::ScopeDef::Local(pat_id) => { | ||
324 | let parent = self.resolver.body_owner().unwrap().into(); | ||
325 | ScopeDef::Local(Local { parent, pat_id }) | ||
326 | } | ||
327 | }; | ||
328 | f(name, def) | ||
329 | }) | ||
330 | } | ||
331 | |||
332 | // FIXME: we only use this in `inline_local_variable` assist, ideally, we | ||
333 | // should switch to general reference search infra there. | ||
334 | pub fn find_all_refs(&self, pat: &ast::BindPat) -> Vec<ReferenceDescriptor> { | ||
335 | let fn_def = pat.syntax().ancestors().find_map(ast::FnDef::cast).unwrap(); | ||
336 | let ptr = Either::Left(AstPtr::new(&ast::Pat::from(pat.clone()))); | ||
337 | fn_def | ||
338 | .syntax() | ||
339 | .descendants() | ||
340 | .filter_map(ast::NameRef::cast) | ||
341 | .filter(|name_ref| match self.resolve_local_name(&name_ref) { | ||
342 | None => false, | ||
343 | Some(entry) => entry.ptr() == ptr, | ||
344 | }) | ||
345 | .map(|name_ref| ReferenceDescriptor { | ||
346 | name: name_ref.text().to_string(), | ||
347 | range: name_ref.syntax().text_range(), | ||
348 | }) | ||
349 | .collect() | ||
350 | } | ||
351 | |||
352 | /// Note: `FxHashSet<TraitId>` should be treated as an opaque type, passed into `Type | ||
353 | pub fn traits_in_scope(&self, db: &impl HirDatabase) -> FxHashSet<TraitId> { | ||
354 | self.resolver.traits_in_scope(db) | ||
355 | } | ||
356 | |||
357 | pub fn expand( | ||
358 | &self, | ||
359 | db: &impl HirDatabase, | ||
360 | macro_call: InFile<&ast::MacroCall>, | ||
361 | ) -> Option<Expansion> { | ||
362 | let def = self.resolve_macro_call(db, macro_call)?.id; | ||
363 | let ast_id = AstId::new( | ||
364 | macro_call.file_id, | ||
365 | db.ast_id_map(macro_call.file_id).ast_id(macro_call.value), | ||
366 | ); | ||
367 | Some(Expansion { macro_call_id: def.as_call_id(db, MacroCallKind::FnLike(ast_id)) }) | ||
368 | } | ||
369 | } | ||
370 | |||
371 | fn scope_for( | ||
372 | scopes: &ExprScopes, | ||
373 | source_map: &BodySourceMap, | ||
374 | node: InFile<&SyntaxNode>, | ||
375 | ) -> Option<ScopeId> { | ||
376 | node.value | ||
377 | .ancestors() | ||
378 | .filter_map(ast::Expr::cast) | ||
379 | .filter_map(|it| source_map.node_expr(InFile::new(node.file_id, &it))) | ||
380 | .find_map(|it| scopes.scope_for(it)) | ||
381 | } | ||
382 | |||
383 | fn scope_for_offset( | ||
384 | scopes: &ExprScopes, | ||
385 | source_map: &BodySourceMap, | ||
386 | offset: InFile<TextUnit>, | ||
387 | ) -> Option<ScopeId> { | ||
388 | scopes | ||
389 | .scope_by_expr() | ||
390 | .iter() | ||
391 | .filter_map(|(id, scope)| { | ||
392 | let source = source_map.expr_syntax(*id)?; | ||
393 | // FIXME: correctly handle macro expansion | ||
394 | if source.file_id != offset.file_id { | ||
395 | return None; | ||
396 | } | ||
397 | let syntax_node_ptr = | ||
398 | source.value.either(|it| it.syntax_node_ptr(), |it| it.syntax_node_ptr()); | ||
399 | Some((syntax_node_ptr, scope)) | ||
400 | }) | ||
401 | // find containing scope | ||
402 | .min_by_key(|(ptr, _scope)| { | ||
403 | ( | ||
404 | !(ptr.range().start() <= offset.value && offset.value <= ptr.range().end()), | ||
405 | ptr.range().len(), | ||
406 | ) | ||
407 | }) | ||
408 | .map(|(ptr, scope)| { | ||
409 | adjust(scopes, source_map, ptr, offset.file_id, offset.value).unwrap_or(*scope) | ||
410 | }) | ||
411 | } | ||
412 | |||
413 | // XXX: during completion, cursor might be outside of any particular | ||
414 | // expression. Try to figure out the correct scope... | ||
415 | fn adjust( | ||
416 | scopes: &ExprScopes, | ||
417 | source_map: &BodySourceMap, | ||
418 | ptr: SyntaxNodePtr, | ||
419 | file_id: HirFileId, | ||
420 | offset: TextUnit, | ||
421 | ) -> Option<ScopeId> { | ||
422 | let r = ptr.range(); | ||
423 | let child_scopes = scopes | ||
424 | .scope_by_expr() | ||
425 | .iter() | ||
426 | .filter_map(|(id, scope)| { | ||
427 | let source = source_map.expr_syntax(*id)?; | ||
428 | // FIXME: correctly handle macro expansion | ||
429 | if source.file_id != file_id { | ||
430 | return None; | ||
431 | } | ||
432 | let syntax_node_ptr = | ||
433 | source.value.either(|it| it.syntax_node_ptr(), |it| it.syntax_node_ptr()); | ||
434 | Some((syntax_node_ptr, scope)) | ||
435 | }) | ||
436 | .map(|(ptr, scope)| (ptr.range(), scope)) | ||
437 | .filter(|(range, _)| range.start() <= offset && range.is_subrange(&r) && *range != r); | ||
438 | |||
439 | child_scopes | ||
440 | .max_by(|(r1, _), (r2, _)| { | ||
441 | if r2.is_subrange(&r1) { | ||
442 | std::cmp::Ordering::Greater | ||
443 | } else if r1.is_subrange(&r2) { | ||
444 | std::cmp::Ordering::Less | ||
445 | } else { | ||
446 | r1.start().cmp(&r2.start()) | ||
447 | } | ||
448 | }) | ||
449 | .map(|(_ptr, scope)| *scope) | ||
450 | } | ||
diff --git a/crates/ra_hir/src/source_binder.rs b/crates/ra_hir/src/source_binder.rs index a2a9d968c..f3150f578 100644 --- a/crates/ra_hir/src/source_binder.rs +++ b/crates/ra_hir/src/source_binder.rs | |||
@@ -1,569 +1,357 @@ | |||
1 | //! Lookup hir elements using positions in the source code. This is a lossy | 1 | //! `SourceBinder` is the main entry point for getting info about source code. |
2 | //! transformation: in general, a single source might correspond to several | 2 | //! It's main task is to map source syntax trees to hir-level IDs. |
3 | //! modules, functions, etc, due to macros, cfgs and `#[path=]` attributes on | 3 | |
4 | //! modules. | ||
5 | //! | ||
6 | //! So, this modules should not be used during hir construction, it exists | ||
7 | //! purely for "IDE needs". | ||
8 | use std::sync::Arc; | ||
9 | |||
10 | use either::Either; | ||
11 | use hir_def::{ | 4 | use hir_def::{ |
12 | body::{ | 5 | child_by_source::ChildBySource, |
13 | scope::{ExprScopes, ScopeId}, | 6 | dyn_map::DynMap, |
14 | BodySourceMap, | 7 | keys::{self, Key}, |
15 | }, | 8 | resolver::{HasResolver, Resolver}, |
16 | expr::{ExprId, PatId}, | 9 | ConstId, DefWithBodyId, EnumId, EnumVariantId, FunctionId, GenericDefId, ImplId, ModuleId, |
17 | nameres::ModuleSource, | 10 | StaticId, StructFieldId, StructId, TraitId, TypeAliasId, UnionId, VariantId, |
18 | path::path, | ||
19 | resolver::{self, resolver_for_scope, HasResolver, Resolver, TypeNs, ValueNs}, | ||
20 | AssocItemId, DefWithBodyId, | ||
21 | }; | ||
22 | use hir_expand::{ | ||
23 | hygiene::Hygiene, name::AsName, AstId, HirFileId, InFile, MacroCallId, MacroCallKind, | ||
24 | }; | ||
25 | use hir_ty::{ | ||
26 | method_resolution::{self, implements_trait}, | ||
27 | Canonical, InEnvironment, InferenceResult, TraitEnvironment, Ty, | ||
28 | }; | 11 | }; |
12 | use hir_expand::{name::AsName, AstId, InFile, MacroDefId, MacroDefKind}; | ||
29 | use ra_prof::profile; | 13 | use ra_prof::profile; |
30 | use ra_syntax::{ | 14 | use ra_syntax::{ |
31 | ast::{self, AstNode}, | 15 | ast::{self, NameOwner}, |
32 | match_ast, AstPtr, | 16 | match_ast, AstNode, SyntaxNode, TextUnit, |
33 | SyntaxKind::*, | ||
34 | SyntaxNode, SyntaxNodePtr, SyntaxToken, TextRange, TextUnit, | ||
35 | }; | 17 | }; |
18 | use rustc_hash::FxHashMap; | ||
36 | 19 | ||
37 | use crate::{ | 20 | use crate::{db::HirDatabase, Local, Module, SourceAnalyzer, TypeParam}; |
38 | db::HirDatabase, Adt, AssocItem, Const, DefWithBody, Enum, EnumVariant, FromSource, Function, | 21 | use ra_db::FileId; |
39 | ImplBlock, Local, MacroDef, Name, Path, ScopeDef, Static, Struct, Trait, Type, TypeAlias, | ||
40 | TypeParam, | ||
41 | }; | ||
42 | |||
43 | fn try_get_resolver_for_node(db: &impl HirDatabase, node: InFile<&SyntaxNode>) -> Option<Resolver> { | ||
44 | match_ast! { | ||
45 | match (node.value) { | ||
46 | ast::Module(it) => { | ||
47 | let src = node.with_value(it); | ||
48 | Some(crate::Module::from_declaration(db, src)?.id.resolver(db)) | ||
49 | }, | ||
50 | ast::SourceFile(it) => { | ||
51 | let src = node.with_value(ModuleSource::SourceFile(it)); | ||
52 | Some(crate::Module::from_definition(db, src)?.id.resolver(db)) | ||
53 | }, | ||
54 | ast::StructDef(it) => { | ||
55 | let src = node.with_value(it); | ||
56 | Some(Struct::from_source(db, src)?.id.resolver(db)) | ||
57 | }, | ||
58 | ast::EnumDef(it) => { | ||
59 | let src = node.with_value(it); | ||
60 | Some(Enum::from_source(db, src)?.id.resolver(db)) | ||
61 | }, | ||
62 | ast::ImplBlock(it) => { | ||
63 | let src = node.with_value(it); | ||
64 | Some(ImplBlock::from_source(db, src)?.id.resolver(db)) | ||
65 | }, | ||
66 | ast::TraitDef(it) => { | ||
67 | let src = node.with_value(it); | ||
68 | Some(Trait::from_source(db, src)?.id.resolver(db)) | ||
69 | }, | ||
70 | _ => match node.value.kind() { | ||
71 | FN_DEF | CONST_DEF | STATIC_DEF => { | ||
72 | let def = def_with_body_from_child_node(db, node)?; | ||
73 | let def = DefWithBodyId::from(def); | ||
74 | Some(def.resolver(db)) | ||
75 | } | ||
76 | // FIXME add missing cases | ||
77 | _ => None | ||
78 | } | ||
79 | } | ||
80 | } | ||
81 | } | ||
82 | |||
83 | fn def_with_body_from_child_node( | ||
84 | db: &impl HirDatabase, | ||
85 | child: InFile<&SyntaxNode>, | ||
86 | ) -> Option<DefWithBody> { | ||
87 | let _p = profile("def_with_body_from_child_node"); | ||
88 | child.cloned().ancestors_with_macros(db).find_map(|node| { | ||
89 | let n = &node.value; | ||
90 | match_ast! { | ||
91 | match n { | ||
92 | ast::FnDef(def) => { return Function::from_source(db, node.with_value(def)).map(DefWithBody::from); }, | ||
93 | ast::ConstDef(def) => { return Const::from_source(db, node.with_value(def)).map(DefWithBody::from); }, | ||
94 | ast::StaticDef(def) => { return Static::from_source(db, node.with_value(def)).map(DefWithBody::from); }, | ||
95 | _ => { None }, | ||
96 | } | ||
97 | } | ||
98 | }) | ||
99 | } | ||
100 | |||
101 | /// `SourceAnalyzer` is a convenience wrapper which exposes HIR API in terms of | ||
102 | /// original source files. It should not be used inside the HIR itself. | ||
103 | #[derive(Debug)] | ||
104 | pub struct SourceAnalyzer { | ||
105 | file_id: HirFileId, | ||
106 | resolver: Resolver, | ||
107 | body_owner: Option<DefWithBody>, | ||
108 | body_source_map: Option<Arc<BodySourceMap>>, | ||
109 | infer: Option<Arc<InferenceResult>>, | ||
110 | scopes: Option<Arc<ExprScopes>>, | ||
111 | } | ||
112 | |||
113 | #[derive(Debug, Clone, PartialEq, Eq)] | ||
114 | pub enum PathResolution { | ||
115 | /// An item | ||
116 | Def(crate::ModuleDef), | ||
117 | /// A local binding (only value namespace) | ||
118 | Local(Local), | ||
119 | /// A generic parameter | ||
120 | TypeParam(TypeParam), | ||
121 | SelfType(crate::ImplBlock), | ||
122 | Macro(MacroDef), | ||
123 | AssocItem(crate::AssocItem), | ||
124 | } | ||
125 | |||
126 | #[derive(Debug, Clone, PartialEq, Eq)] | ||
127 | pub struct ScopeEntryWithSyntax { | ||
128 | pub(crate) name: Name, | ||
129 | pub(crate) ptr: Either<AstPtr<ast::Pat>, AstPtr<ast::SelfParam>>, | ||
130 | } | ||
131 | |||
132 | impl ScopeEntryWithSyntax { | ||
133 | pub fn name(&self) -> &Name { | ||
134 | &self.name | ||
135 | } | ||
136 | |||
137 | pub fn ptr(&self) -> Either<AstPtr<ast::Pat>, AstPtr<ast::SelfParam>> { | ||
138 | self.ptr | ||
139 | } | ||
140 | } | ||
141 | |||
142 | #[derive(Debug)] | ||
143 | pub struct ReferenceDescriptor { | ||
144 | pub range: TextRange, | ||
145 | pub name: String, | ||
146 | } | ||
147 | 22 | ||
148 | #[derive(Debug)] | 23 | pub struct SourceBinder<'a, DB> { |
149 | pub struct Expansion { | 24 | pub db: &'a DB, |
150 | macro_call_id: MacroCallId, | 25 | child_by_source_cache: FxHashMap<ChildContainer, DynMap>, |
151 | } | 26 | } |
152 | 27 | ||
153 | impl Expansion { | 28 | impl<DB: HirDatabase> SourceBinder<'_, DB> { |
154 | pub fn map_token_down( | 29 | pub fn new(db: &DB) -> SourceBinder<DB> { |
155 | &self, | 30 | SourceBinder { db, child_by_source_cache: FxHashMap::default() } |
156 | db: &impl HirDatabase, | ||
157 | token: InFile<&SyntaxToken>, | ||
158 | ) -> Option<InFile<SyntaxToken>> { | ||
159 | let exp_info = self.file_id().expansion_info(db)?; | ||
160 | exp_info.map_token_down(token) | ||
161 | } | ||
162 | |||
163 | pub fn file_id(&self) -> HirFileId { | ||
164 | self.macro_call_id.as_file() | ||
165 | } | 31 | } |
166 | } | ||
167 | 32 | ||
168 | impl SourceAnalyzer { | 33 | pub fn analyze( |
169 | pub fn new( | 34 | &mut self, |
170 | db: &impl HirDatabase, | 35 | src: InFile<&SyntaxNode>, |
171 | node: InFile<&SyntaxNode>, | ||
172 | offset: Option<TextUnit>, | 36 | offset: Option<TextUnit>, |
173 | ) -> SourceAnalyzer { | 37 | ) -> SourceAnalyzer { |
174 | let _p = profile("SourceAnalyzer::new"); | 38 | let _p = profile("SourceBinder::analyzer"); |
175 | let def_with_body = def_with_body_from_child_node(db, node); | 39 | let container = match self.find_container(src) { |
176 | if let Some(def) = def_with_body { | 40 | Some(it) => it, |
177 | let (_body, source_map) = db.body_with_source_map(def.into()); | 41 | None => return SourceAnalyzer::new_for_resolver(Resolver::default(), src), |
178 | let scopes = db.expr_scopes(def.into()); | 42 | }; |
179 | let scope = match offset { | 43 | |
180 | None => scope_for(&scopes, &source_map, node), | 44 | let resolver = match container { |
181 | Some(offset) => scope_for_offset(&scopes, &source_map, node.with_value(offset)), | 45 | ChildContainer::DefWithBodyId(def) => { |
182 | }; | 46 | return SourceAnalyzer::new_for_body(self.db, def, src, offset) |
183 | let resolver = resolver_for_scope(db, def.into(), scope); | ||
184 | SourceAnalyzer { | ||
185 | resolver, | ||
186 | body_owner: Some(def), | ||
187 | body_source_map: Some(source_map), | ||
188 | infer: Some(db.infer(def.into())), | ||
189 | scopes: Some(scopes), | ||
190 | file_id: node.file_id, | ||
191 | } | ||
192 | } else { | ||
193 | SourceAnalyzer { | ||
194 | resolver: node | ||
195 | .value | ||
196 | .ancestors() | ||
197 | .find_map(|it| try_get_resolver_for_node(db, node.with_value(&it))) | ||
198 | .unwrap_or_default(), | ||
199 | body_owner: None, | ||
200 | body_source_map: None, | ||
201 | infer: None, | ||
202 | scopes: None, | ||
203 | file_id: node.file_id, | ||
204 | } | 47 | } |
205 | } | 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) | ||
206 | } | 56 | } |
207 | 57 | ||
208 | pub fn module(&self) -> Option<crate::code_model::Module> { | 58 | pub fn to_def<T: ToDef>(&mut self, src: InFile<T>) -> Option<T::Def> { |
209 | Some(crate::code_model::Module { id: self.resolver.module()? }) | 59 | T::to_def(self, src) |
210 | } | 60 | } |
211 | 61 | ||
212 | fn expr_id(&self, expr: &ast::Expr) -> Option<ExprId> { | 62 | pub fn to_module_def(&mut self, file: FileId) -> Option<Module> { |
213 | let src = InFile { file_id: self.file_id, value: expr }; | 63 | let _p = profile("SourceBinder::to_module_def"); |
214 | self.body_source_map.as_ref()?.node_expr(src) | 64 | let (krate, local_id) = self.db.relevant_crates(file).iter().find_map(|&crate_id| { |
65 | let crate_def_map = self.db.crate_def_map(crate_id); | ||
66 | let local_id = crate_def_map.modules_for_file(file).next()?; | ||
67 | Some((crate_id, local_id)) | ||
68 | })?; | ||
69 | Some(Module { id: ModuleId { krate, local_id } }) | ||
215 | } | 70 | } |
216 | 71 | ||
217 | fn pat_id(&self, pat: &ast::Pat) -> Option<PatId> { | 72 | fn to_id<T: ToId>(&mut self, src: InFile<T>) -> Option<T::ID> { |
218 | let src = InFile { file_id: self.file_id, value: pat }; | 73 | T::to_id(self, src) |
219 | self.body_source_map.as_ref()?.node_pat(src) | ||
220 | } | 74 | } |
221 | 75 | ||
222 | fn expand_expr( | 76 | fn find_container(&mut self, src: InFile<&SyntaxNode>) -> Option<ChildContainer> { |
223 | &self, | 77 | for container in src.cloned().ancestors_with_macros(self.db).skip(1) { |
224 | db: &impl HirDatabase, | 78 | let res: ChildContainer = match_ast! { |
225 | expr: InFile<&ast::Expr>, | 79 | match (container.value) { |
226 | ) -> Option<InFile<ast::Expr>> { | 80 | ast::TraitDef(it) => { |
227 | let macro_call = ast::MacroCall::cast(expr.value.syntax().clone())?; | 81 | let def: TraitId = self.to_id(container.with_value(it))?; |
228 | let macro_file = | 82 | def.into() |
229 | self.body_source_map.as_ref()?.node_macro_file(expr.with_value(¯o_call))?; | 83 | }, |
230 | let expanded = db.parse_or_expand(macro_file)?; | 84 | ast::ImplBlock(it) => { |
231 | let kind = expanded.kind(); | 85 | let def: ImplId = self.to_id(container.with_value(it))?; |
232 | let expr = InFile::new(macro_file, ast::Expr::cast(expanded)?); | 86 | def.into() |
233 | 87 | }, | |
234 | if ast::MacroCall::can_cast(kind) { | 88 | ast::FnDef(it) => { |
235 | self.expand_expr(db, expr.as_ref()) | 89 | let def: FunctionId = self.to_id(container.with_value(it))?; |
236 | } else { | 90 | DefWithBodyId::from(def).into() |
237 | Some(expr) | 91 | }, |
92 | ast::StaticDef(it) => { | ||
93 | let def: StaticId = self.to_id(container.with_value(it))?; | ||
94 | DefWithBodyId::from(def).into() | ||
95 | }, | ||
96 | ast::ConstDef(it) => { | ||
97 | let def: ConstId = self.to_id(container.with_value(it))?; | ||
98 | DefWithBodyId::from(def).into() | ||
99 | }, | ||
100 | ast::EnumDef(it) => { | ||
101 | let def: EnumId = self.to_id(container.with_value(it))?; | ||
102 | def.into() | ||
103 | }, | ||
104 | ast::StructDef(it) => { | ||
105 | let def: StructId = self.to_id(container.with_value(it))?; | ||
106 | VariantId::from(def).into() | ||
107 | }, | ||
108 | ast::UnionDef(it) => { | ||
109 | let def: UnionId = self.to_id(container.with_value(it))?; | ||
110 | VariantId::from(def).into() | ||
111 | }, | ||
112 | ast::Module(it) => { | ||
113 | let def: ModuleId = self.to_id(container.with_value(it))?; | ||
114 | def.into() | ||
115 | }, | ||
116 | _ => { continue }, | ||
117 | } | ||
118 | }; | ||
119 | return Some(res); | ||
238 | } | 120 | } |
239 | } | ||
240 | 121 | ||
241 | pub fn type_of(&self, db: &impl HirDatabase, expr: &ast::Expr) -> Option<Type> { | 122 | let c = self.to_module_def(src.file_id.original_file(self.db))?; |
242 | let expr_id = if let Some(expr) = self.expand_expr(db, InFile::new(self.file_id, expr)) { | 123 | Some(c.id.into()) |
243 | self.body_source_map.as_ref()?.node_expr(expr.as_ref())? | ||
244 | } else { | ||
245 | self.expr_id(expr)? | ||
246 | }; | ||
247 | |||
248 | let ty = self.infer.as_ref()?[expr_id].clone(); | ||
249 | let environment = TraitEnvironment::lower(db, &self.resolver); | ||
250 | Some(Type { krate: self.resolver.krate()?, ty: InEnvironment { value: ty, environment } }) | ||
251 | } | ||
252 | |||
253 | pub fn type_of_pat(&self, db: &impl HirDatabase, pat: &ast::Pat) -> Option<Type> { | ||
254 | let pat_id = self.pat_id(pat)?; | ||
255 | let ty = self.infer.as_ref()?[pat_id].clone(); | ||
256 | let environment = TraitEnvironment::lower(db, &self.resolver); | ||
257 | Some(Type { krate: self.resolver.krate()?, ty: InEnvironment { value: ty, environment } }) | ||
258 | } | 124 | } |
259 | 125 | ||
260 | pub fn resolve_method_call(&self, call: &ast::MethodCallExpr) -> Option<Function> { | 126 | fn child_by_source(&mut self, container: ChildContainer) -> &DynMap { |
261 | let expr_id = self.expr_id(&call.clone().into())?; | 127 | let db = self.db; |
262 | self.infer.as_ref()?.method_resolution(expr_id).map(Function::from) | 128 | self.child_by_source_cache.entry(container).or_insert_with(|| match container { |
129 | ChildContainer::DefWithBodyId(it) => it.child_by_source(db), | ||
130 | ChildContainer::ModuleId(it) => it.child_by_source(db), | ||
131 | ChildContainer::TraitId(it) => it.child_by_source(db), | ||
132 | ChildContainer::ImplId(it) => it.child_by_source(db), | ||
133 | ChildContainer::EnumId(it) => it.child_by_source(db), | ||
134 | ChildContainer::VariantId(it) => it.child_by_source(db), | ||
135 | ChildContainer::GenericDefId(it) => it.child_by_source(db), | ||
136 | }) | ||
263 | } | 137 | } |
138 | } | ||
264 | 139 | ||
265 | pub fn resolve_field(&self, field: &ast::FieldExpr) -> Option<crate::StructField> { | 140 | pub trait ToId: Sized { |
266 | let expr_id = self.expr_id(&field.clone().into())?; | 141 | type ID: Sized + Copy + 'static; |
267 | self.infer.as_ref()?.field_resolution(expr_id).map(|it| it.into()) | 142 | fn to_id<DB: HirDatabase>(sb: &mut SourceBinder<'_, DB>, src: InFile<Self>) |
268 | } | 143 | -> Option<Self::ID>; |
144 | } | ||
269 | 145 | ||
270 | pub fn resolve_record_field(&self, field: &ast::RecordField) -> Option<crate::StructField> { | 146 | pub trait ToDef: Sized + AstNode + 'static { |
271 | let expr_id = match field.expr() { | 147 | type Def; |
272 | Some(it) => self.expr_id(&it)?, | 148 | fn to_def<DB: HirDatabase>( |
273 | None => { | 149 | sb: &mut SourceBinder<'_, DB>, |
274 | let src = InFile { file_id: self.file_id, value: field }; | 150 | src: InFile<Self>, |
275 | self.body_source_map.as_ref()?.field_init_shorthand_expr(src)? | 151 | ) -> Option<Self::Def>; |
276 | } | 152 | } |
277 | }; | ||
278 | self.infer.as_ref()?.record_field_resolution(expr_id).map(|it| it.into()) | ||
279 | } | ||
280 | 153 | ||
281 | pub fn resolve_record_literal(&self, record_lit: &ast::RecordLit) -> Option<crate::VariantDef> { | 154 | macro_rules! to_def_impls { |
282 | let expr_id = self.expr_id(&record_lit.clone().into())?; | 155 | ($(($def:path, $ast:path)),* ,) => {$( |
283 | self.infer.as_ref()?.variant_resolution_for_expr(expr_id).map(|it| it.into()) | 156 | impl ToDef for $ast { |
284 | } | 157 | type Def = $def; |
158 | fn to_def<DB: HirDatabase>(sb: &mut SourceBinder<'_, DB>, src: InFile<Self>) | ||
159 | -> Option<Self::Def> | ||
160 | { sb.to_id(src).map(Into::into) } | ||
161 | } | ||
162 | )*} | ||
163 | } | ||
285 | 164 | ||
286 | pub fn resolve_record_pattern(&self, record_pat: &ast::RecordPat) -> Option<crate::VariantDef> { | 165 | to_def_impls![ |
287 | let pat_id = self.pat_id(&record_pat.clone().into())?; | 166 | (crate::Module, ast::Module), |
288 | self.infer.as_ref()?.variant_resolution_for_pat(pat_id).map(|it| it.into()) | 167 | (crate::Struct, ast::StructDef), |
289 | } | 168 | (crate::Enum, ast::EnumDef), |
169 | (crate::Union, ast::UnionDef), | ||
170 | (crate::Trait, ast::TraitDef), | ||
171 | (crate::ImplBlock, ast::ImplBlock), | ||
172 | (crate::TypeAlias, ast::TypeAliasDef), | ||
173 | (crate::Const, ast::ConstDef), | ||
174 | (crate::Static, ast::StaticDef), | ||
175 | (crate::Function, ast::FnDef), | ||
176 | (crate::StructField, ast::RecordFieldDef), | ||
177 | (crate::EnumVariant, ast::EnumVariant), | ||
178 | (crate::MacroDef, ast::MacroCall), // this one is dubious, not all calls are macros | ||
179 | ]; | ||
180 | |||
181 | #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)] | ||
182 | enum ChildContainer { | ||
183 | DefWithBodyId(DefWithBodyId), | ||
184 | ModuleId(ModuleId), | ||
185 | TraitId(TraitId), | ||
186 | ImplId(ImplId), | ||
187 | EnumId(EnumId), | ||
188 | VariantId(VariantId), | ||
189 | /// XXX: this might be the same def as, for example an `EnumId`. However, | ||
190 | /// here the children generic parameters, and not, eg enum variants. | ||
191 | GenericDefId(GenericDefId), | ||
192 | } | ||
193 | impl_froms! { | ||
194 | ChildContainer: | ||
195 | DefWithBodyId, | ||
196 | ModuleId, | ||
197 | TraitId, | ||
198 | ImplId, | ||
199 | EnumId, | ||
200 | VariantId, | ||
201 | GenericDefId | ||
202 | } | ||
290 | 203 | ||
291 | pub fn resolve_macro_call( | 204 | pub trait ToIdByKey: Sized + AstNode + 'static { |
292 | &self, | 205 | type ID: Sized + Copy + 'static; |
293 | db: &impl HirDatabase, | 206 | const KEY: Key<Self, Self::ID>; |
294 | macro_call: InFile<&ast::MacroCall>, | 207 | } |
295 | ) -> Option<MacroDef> { | ||
296 | let hygiene = Hygiene::new(db, macro_call.file_id); | ||
297 | let path = macro_call.value.path().and_then(|ast| Path::from_src(ast, &hygiene))?; | ||
298 | self.resolver.resolve_path_as_macro(db, path.mod_path()).map(|it| it.into()) | ||
299 | } | ||
300 | 208 | ||
301 | pub fn resolve_hir_path( | 209 | impl<T: ToIdByKey> ToId for T { |
302 | &self, | 210 | type ID = <T as ToIdByKey>::ID; |
303 | db: &impl HirDatabase, | 211 | fn to_id<DB: HirDatabase>( |
304 | path: &crate::Path, | 212 | sb: &mut SourceBinder<'_, DB>, |
305 | ) -> Option<PathResolution> { | 213 | src: InFile<Self>, |
306 | let types = | 214 | ) -> Option<Self::ID> { |
307 | self.resolver.resolve_path_in_type_ns_fully(db, path.mod_path()).map(|ty| match ty { | 215 | let container = sb.find_container(src.as_ref().map(|it| it.syntax()))?; |
308 | TypeNs::SelfType(it) => PathResolution::SelfType(it.into()), | 216 | let db = sb.db; |
309 | TypeNs::GenericParam(id) => PathResolution::TypeParam(TypeParam { id }), | 217 | let dyn_map = |
310 | TypeNs::AdtSelfType(it) | TypeNs::AdtId(it) => { | 218 | &*sb.child_by_source_cache.entry(container).or_insert_with(|| match container { |
311 | PathResolution::Def(Adt::from(it).into()) | 219 | ChildContainer::DefWithBodyId(it) => it.child_by_source(db), |
312 | } | 220 | ChildContainer::ModuleId(it) => it.child_by_source(db), |
313 | TypeNs::EnumVariantId(it) => PathResolution::Def(EnumVariant::from(it).into()), | 221 | ChildContainer::TraitId(it) => it.child_by_source(db), |
314 | TypeNs::TypeAliasId(it) => PathResolution::Def(TypeAlias::from(it).into()), | 222 | ChildContainer::ImplId(it) => it.child_by_source(db), |
315 | TypeNs::BuiltinType(it) => PathResolution::Def(it.into()), | 223 | ChildContainer::EnumId(it) => it.child_by_source(db), |
316 | TypeNs::TraitId(it) => PathResolution::Def(Trait::from(it).into()), | 224 | ChildContainer::VariantId(it) => it.child_by_source(db), |
317 | }); | 225 | ChildContainer::GenericDefId(it) => it.child_by_source(db), |
318 | let values = | ||
319 | self.resolver.resolve_path_in_value_ns_fully(db, path.mod_path()).and_then(|val| { | ||
320 | let res = match val { | ||
321 | ValueNs::LocalBinding(pat_id) => { | ||
322 | let var = Local { parent: self.body_owner?, pat_id }; | ||
323 | PathResolution::Local(var) | ||
324 | } | ||
325 | ValueNs::FunctionId(it) => PathResolution::Def(Function::from(it).into()), | ||
326 | ValueNs::ConstId(it) => PathResolution::Def(Const::from(it).into()), | ||
327 | ValueNs::StaticId(it) => PathResolution::Def(Static::from(it).into()), | ||
328 | ValueNs::StructId(it) => PathResolution::Def(Struct::from(it).into()), | ||
329 | ValueNs::EnumVariantId(it) => PathResolution::Def(EnumVariant::from(it).into()), | ||
330 | }; | ||
331 | Some(res) | ||
332 | }); | 226 | }); |
333 | 227 | dyn_map[T::KEY].get(&src).copied() | |
334 | let items = self | ||
335 | .resolver | ||
336 | .resolve_module_path_in_items(db, path.mod_path()) | ||
337 | .take_types() | ||
338 | .map(|it| PathResolution::Def(it.into())); | ||
339 | types.or(values).or(items).or_else(|| { | ||
340 | self.resolver | ||
341 | .resolve_path_as_macro(db, path.mod_path()) | ||
342 | .map(|def| PathResolution::Macro(def.into())) | ||
343 | }) | ||
344 | } | 228 | } |
229 | } | ||
345 | 230 | ||
346 | pub fn resolve_path(&self, db: &impl HirDatabase, path: &ast::Path) -> Option<PathResolution> { | 231 | macro_rules! to_id_key_impls { |
347 | if let Some(path_expr) = path.syntax().parent().and_then(ast::PathExpr::cast) { | 232 | ($(($id:ident, $ast:path, $key:path)),* ,) => {$( |
348 | let expr_id = self.expr_id(&path_expr.into())?; | 233 | impl ToIdByKey for $ast { |
349 | if let Some(assoc) = self.infer.as_ref()?.assoc_resolutions_for_expr(expr_id) { | 234 | type ID = $id; |
350 | return Some(PathResolution::AssocItem(assoc.into())); | 235 | const KEY: Key<Self, Self::ID> = $key; |
351 | } | ||
352 | } | 236 | } |
353 | if let Some(path_pat) = path.syntax().parent().and_then(ast::PathPat::cast) { | 237 | )*} |
354 | let pat_id = self.pat_id(&path_pat.into())?; | 238 | } |
355 | if let Some(assoc) = self.infer.as_ref()?.assoc_resolutions_for_pat(pat_id) { | ||
356 | return Some(PathResolution::AssocItem(assoc.into())); | ||
357 | } | ||
358 | } | ||
359 | // This must be a normal source file rather than macro file. | ||
360 | let hir_path = crate::Path::from_ast(path.clone())?; | ||
361 | self.resolve_hir_path(db, &hir_path) | ||
362 | } | ||
363 | 239 | ||
364 | fn resolve_local_name(&self, name_ref: &ast::NameRef) -> Option<ScopeEntryWithSyntax> { | 240 | to_id_key_impls![ |
365 | let name = name_ref.as_name(); | 241 | (StructId, ast::StructDef, keys::STRUCT), |
366 | let source_map = self.body_source_map.as_ref()?; | 242 | (UnionId, ast::UnionDef, keys::UNION), |
367 | let scopes = self.scopes.as_ref()?; | 243 | (EnumId, ast::EnumDef, keys::ENUM), |
368 | let scope = scope_for(scopes, source_map, InFile::new(self.file_id, name_ref.syntax()))?; | 244 | (TraitId, ast::TraitDef, keys::TRAIT), |
369 | let entry = scopes.resolve_name_in_scope(scope, &name)?; | 245 | (FunctionId, ast::FnDef, keys::FUNCTION), |
370 | Some(ScopeEntryWithSyntax { | 246 | (StaticId, ast::StaticDef, keys::STATIC), |
371 | name: entry.name().clone(), | 247 | (ConstId, ast::ConstDef, keys::CONST), |
372 | ptr: source_map.pat_syntax(entry.pat())?.value, | 248 | (TypeAliasId, ast::TypeAliasDef, keys::TYPE_ALIAS), |
373 | }) | 249 | (ImplId, ast::ImplBlock, keys::IMPL), |
250 | (StructFieldId, ast::RecordFieldDef, keys::RECORD_FIELD), | ||
251 | (EnumVariantId, ast::EnumVariant, keys::ENUM_VARIANT), | ||
252 | ]; | ||
253 | |||
254 | // FIXME: use DynMap as well? | ||
255 | impl ToId for ast::MacroCall { | ||
256 | type ID = MacroDefId; | ||
257 | fn to_id<DB: HirDatabase>( | ||
258 | sb: &mut SourceBinder<'_, DB>, | ||
259 | src: InFile<Self>, | ||
260 | ) -> Option<Self::ID> { | ||
261 | let kind = MacroDefKind::Declarative; | ||
262 | |||
263 | let krate = sb.to_module_def(src.file_id.original_file(sb.db))?.id.krate; | ||
264 | |||
265 | let ast_id = | ||
266 | Some(AstId::new(src.file_id, sb.db.ast_id_map(src.file_id).ast_id(&src.value))); | ||
267 | |||
268 | Some(MacroDefId { krate: Some(krate), ast_id, kind }) | ||
374 | } | 269 | } |
270 | } | ||
375 | 271 | ||
376 | pub fn process_all_names(&self, db: &impl HirDatabase, f: &mut dyn FnMut(Name, ScopeDef)) { | 272 | impl ToDef for ast::BindPat { |
377 | self.resolver.process_all_names(db, &mut |name, def| { | 273 | type Def = Local; |
378 | let def = match def { | 274 | |
379 | resolver::ScopeDef::PerNs(it) => it.into(), | 275 | fn to_def<DB: HirDatabase>(sb: &mut SourceBinder<'_, DB>, src: InFile<Self>) -> Option<Local> { |
380 | resolver::ScopeDef::ImplSelfType(it) => ScopeDef::ImplSelfType(it.into()), | 276 | let file_id = src.file_id; |
381 | resolver::ScopeDef::AdtSelfType(it) => ScopeDef::AdtSelfType(it.into()), | 277 | let parent: DefWithBodyId = src.value.syntax().ancestors().find_map(|it| { |
382 | resolver::ScopeDef::GenericParam(id) => ScopeDef::GenericParam(TypeParam { id }), | 278 | let res = match_ast! { |
383 | resolver::ScopeDef::Local(pat_id) => { | 279 | match it { |
384 | let parent = self.resolver.body_owner().unwrap().into(); | 280 | ast::ConstDef(value) => { sb.to_id(InFile { value, file_id})?.into() }, |
385 | ScopeDef::Local(Local { parent, pat_id }) | 281 | ast::StaticDef(value) => { sb.to_id(InFile { value, file_id})?.into() }, |
282 | ast::FnDef(value) => { sb.to_id(InFile { value, file_id})?.into() }, | ||
283 | _ => return None, | ||
386 | } | 284 | } |
387 | }; | 285 | }; |
388 | f(name, def) | 286 | Some(res) |
389 | }) | 287 | })?; |
390 | } | 288 | let (_body, source_map) = sb.db.body_with_source_map(parent); |
391 | 289 | let src = src.map(ast::Pat::from); | |
392 | // FIXME: we only use this in `inline_local_variable` assist, ideally, we | 290 | let pat_id = source_map.node_pat(src.as_ref())?; |
393 | // should switch to general reference search infra there. | 291 | Some(Local { parent: parent.into(), pat_id }) |
394 | pub fn find_all_refs(&self, pat: &ast::BindPat) -> Vec<ReferenceDescriptor> { | ||
395 | let fn_def = pat.syntax().ancestors().find_map(ast::FnDef::cast).unwrap(); | ||
396 | let ptr = Either::Left(AstPtr::new(&ast::Pat::from(pat.clone()))); | ||
397 | fn_def | ||
398 | .syntax() | ||
399 | .descendants() | ||
400 | .filter_map(ast::NameRef::cast) | ||
401 | .filter(|name_ref| match self.resolve_local_name(&name_ref) { | ||
402 | None => false, | ||
403 | Some(entry) => entry.ptr() == ptr, | ||
404 | }) | ||
405 | .map(|name_ref| ReferenceDescriptor { | ||
406 | name: name_ref.text().to_string(), | ||
407 | range: name_ref.syntax().text_range(), | ||
408 | }) | ||
409 | .collect() | ||
410 | } | ||
411 | |||
412 | pub fn iterate_method_candidates<T>( | ||
413 | &self, | ||
414 | db: &impl HirDatabase, | ||
415 | ty: &Type, | ||
416 | name: Option<&Name>, | ||
417 | mut callback: impl FnMut(&Ty, Function) -> Option<T>, | ||
418 | ) -> Option<T> { | ||
419 | // There should be no inference vars in types passed here | ||
420 | // FIXME check that? | ||
421 | // FIXME replace Unknown by bound vars here | ||
422 | let canonical = Canonical { value: ty.ty.value.clone(), num_vars: 0 }; | ||
423 | method_resolution::iterate_method_candidates( | ||
424 | &canonical, | ||
425 | db, | ||
426 | &self.resolver, | ||
427 | name, | ||
428 | method_resolution::LookupMode::MethodCall, | ||
429 | |ty, it| match it { | ||
430 | AssocItemId::FunctionId(f) => callback(ty, f.into()), | ||
431 | _ => None, | ||
432 | }, | ||
433 | ) | ||
434 | } | ||
435 | |||
436 | pub fn iterate_path_candidates<T>( | ||
437 | &self, | ||
438 | db: &impl HirDatabase, | ||
439 | ty: &Type, | ||
440 | name: Option<&Name>, | ||
441 | mut callback: impl FnMut(&Ty, AssocItem) -> Option<T>, | ||
442 | ) -> Option<T> { | ||
443 | // There should be no inference vars in types passed here | ||
444 | // FIXME check that? | ||
445 | // FIXME replace Unknown by bound vars here | ||
446 | let canonical = Canonical { value: ty.ty.value.clone(), num_vars: 0 }; | ||
447 | method_resolution::iterate_method_candidates( | ||
448 | &canonical, | ||
449 | db, | ||
450 | &self.resolver, | ||
451 | name, | ||
452 | method_resolution::LookupMode::Path, | ||
453 | |ty, it| callback(ty, it.into()), | ||
454 | ) | ||
455 | } | ||
456 | |||
457 | /// Checks that particular type `ty` implements `std::future::Future`. | ||
458 | /// This function is used in `.await` syntax completion. | ||
459 | pub fn impls_future(&self, db: &impl HirDatabase, ty: Type) -> bool { | ||
460 | let std_future_path = path![std::future::Future]; | ||
461 | |||
462 | let std_future_trait = match self.resolver.resolve_known_trait(db, &std_future_path) { | ||
463 | Some(it) => it.into(), | ||
464 | _ => return false, | ||
465 | }; | ||
466 | |||
467 | let krate = match self.resolver.krate() { | ||
468 | Some(krate) => krate, | ||
469 | _ => return false, | ||
470 | }; | ||
471 | |||
472 | let canonical_ty = Canonical { value: ty.ty.value, num_vars: 0 }; | ||
473 | implements_trait(&canonical_ty, db, &self.resolver, krate.into(), std_future_trait) | ||
474 | } | ||
475 | |||
476 | pub fn expand( | ||
477 | &self, | ||
478 | db: &impl HirDatabase, | ||
479 | macro_call: InFile<&ast::MacroCall>, | ||
480 | ) -> Option<Expansion> { | ||
481 | let def = self.resolve_macro_call(db, macro_call)?.id; | ||
482 | let ast_id = AstId::new( | ||
483 | macro_call.file_id, | ||
484 | db.ast_id_map(macro_call.file_id).ast_id(macro_call.value), | ||
485 | ); | ||
486 | Some(Expansion { macro_call_id: def.as_call_id(db, MacroCallKind::FnLike(ast_id)) }) | ||
487 | } | 292 | } |
488 | } | 293 | } |
489 | 294 | ||
490 | fn scope_for( | 295 | impl ToDef for ast::TypeParam { |
491 | scopes: &ExprScopes, | 296 | type Def = TypeParam; |
492 | source_map: &BodySourceMap, | 297 | |
493 | node: InFile<&SyntaxNode>, | 298 | fn to_def<DB: HirDatabase>( |
494 | ) -> Option<ScopeId> { | 299 | sb: &mut SourceBinder<'_, DB>, |
495 | node.value | 300 | src: InFile<ast::TypeParam>, |
496 | .ancestors() | 301 | ) -> Option<TypeParam> { |
497 | .filter_map(ast::Expr::cast) | 302 | let mut sb = SourceBinder::new(sb.db); |
498 | .filter_map(|it| source_map.node_expr(InFile::new(node.file_id, &it))) | 303 | let file_id = src.file_id; |
499 | .find_map(|it| scopes.scope_for(it)) | 304 | let parent: GenericDefId = src.value.syntax().ancestors().find_map(|it| { |
305 | let res = match_ast! { | ||
306 | match it { | ||
307 | ast::FnDef(value) => { sb.to_id(InFile { value, file_id})?.into() }, | ||
308 | ast::StructDef(value) => { sb.to_id(InFile { value, file_id})?.into() }, | ||
309 | ast::EnumDef(value) => { sb.to_id(InFile { value, file_id})?.into() }, | ||
310 | ast::TraitDef(value) => { sb.to_id(InFile { value, file_id})?.into() }, | ||
311 | ast::TypeAliasDef(value) => { sb.to_id(InFile { value, file_id})?.into() }, | ||
312 | ast::ImplBlock(value) => { sb.to_id(InFile { value, file_id})?.into() }, | ||
313 | _ => return None, | ||
314 | } | ||
315 | }; | ||
316 | Some(res) | ||
317 | })?; | ||
318 | let &id = sb.child_by_source(parent.into())[keys::TYPE_PARAM].get(&src)?; | ||
319 | Some(TypeParam { id }) | ||
320 | } | ||
500 | } | 321 | } |
501 | 322 | ||
502 | fn scope_for_offset( | 323 | impl ToId for ast::Module { |
503 | scopes: &ExprScopes, | 324 | type ID = ModuleId; |
504 | source_map: &BodySourceMap, | 325 | |
505 | offset: InFile<TextUnit>, | 326 | fn to_id<DB: HirDatabase>( |
506 | ) -> Option<ScopeId> { | 327 | sb: &mut SourceBinder<'_, DB>, |
507 | scopes | 328 | src: InFile<ast::Module>, |
508 | .scope_by_expr() | 329 | ) -> Option<ModuleId> { |
509 | .iter() | 330 | { |
510 | .filter_map(|(id, scope)| { | 331 | let _p = profile("ast::Module::to_def"); |
511 | let source = source_map.expr_syntax(*id)?; | 332 | let parent_declaration = src |
512 | // FIXME: correctly handle macro expansion | 333 | .as_ref() |
513 | if source.file_id != offset.file_id { | 334 | .map(|it| it.syntax()) |
514 | return None; | 335 | .cloned() |
515 | } | 336 | .ancestors_with_macros(sb.db) |
516 | let syntax_node_ptr = | 337 | .skip(1) |
517 | source.value.either(|it| it.syntax_node_ptr(), |it| it.syntax_node_ptr()); | 338 | .find_map(|it| { |
518 | Some((syntax_node_ptr, scope)) | 339 | let m = ast::Module::cast(it.value.clone())?; |
519 | }) | 340 | Some(it.with_value(m)) |
520 | // find containing scope | 341 | }); |
521 | .min_by_key(|(ptr, _scope)| { | 342 | |
522 | ( | 343 | let parent_module = match parent_declaration { |
523 | !(ptr.range().start() <= offset.value && offset.value <= ptr.range().end()), | 344 | Some(parent_declaration) => sb.to_id(parent_declaration)?, |
524 | ptr.range().len(), | 345 | None => { |
525 | ) | 346 | let file_id = src.file_id.original_file(sb.db); |
526 | }) | 347 | sb.to_module_def(file_id)?.id |
527 | .map(|(ptr, scope)| { | 348 | } |
528 | adjust(scopes, source_map, ptr, offset.file_id, offset.value).unwrap_or(*scope) | 349 | }; |
529 | }) | ||
530 | } | ||
531 | 350 | ||
532 | // XXX: during completion, cursor might be outside of any particular | 351 | let child_name = src.value.name()?.as_name(); |
533 | // expression. Try to figure out the correct scope... | 352 | let def_map = sb.db.crate_def_map(parent_module.krate); |
534 | fn adjust( | 353 | let child_id = *def_map[parent_module.local_id].children.get(&child_name)?; |
535 | scopes: &ExprScopes, | 354 | Some(ModuleId { krate: parent_module.krate, local_id: child_id }) |
536 | source_map: &BodySourceMap, | 355 | } |
537 | ptr: SyntaxNodePtr, | 356 | } |
538 | file_id: HirFileId, | ||
539 | offset: TextUnit, | ||
540 | ) -> Option<ScopeId> { | ||
541 | let r = ptr.range(); | ||
542 | let child_scopes = scopes | ||
543 | .scope_by_expr() | ||
544 | .iter() | ||
545 | .filter_map(|(id, scope)| { | ||
546 | let source = source_map.expr_syntax(*id)?; | ||
547 | // FIXME: correctly handle macro expansion | ||
548 | if source.file_id != file_id { | ||
549 | return None; | ||
550 | } | ||
551 | let syntax_node_ptr = | ||
552 | source.value.either(|it| it.syntax_node_ptr(), |it| it.syntax_node_ptr()); | ||
553 | Some((syntax_node_ptr, scope)) | ||
554 | }) | ||
555 | .map(|(ptr, scope)| (ptr.range(), scope)) | ||
556 | .filter(|(range, _)| range.start() <= offset && range.is_subrange(&r) && *range != r); | ||
557 | |||
558 | child_scopes | ||
559 | .max_by(|(r1, _), (r2, _)| { | ||
560 | if r2.is_subrange(&r1) { | ||
561 | std::cmp::Ordering::Greater | ||
562 | } else if r1.is_subrange(&r2) { | ||
563 | std::cmp::Ordering::Less | ||
564 | } else { | ||
565 | r1.start().cmp(&r2.start()) | ||
566 | } | ||
567 | }) | ||
568 | .map(|(_ptr, scope)| *scope) | ||
569 | } | 357 | } |