diff options
| author | bors[bot] <bors[bot]@users.noreply.github.com> | 2018-12-05 10:21:28 +0000 |
|---|---|---|
| committer | bors[bot] <bors[bot]@users.noreply.github.com> | 2018-12-05 10:21:28 +0000 |
| commit | d1b993c0aacb2a29fc87283a8b35fe1b4a542459 (patch) | |
| tree | 615c68873a325a02be8e21e1bd4dcb9ce8ab6a1e /crates | |
| parent | 7960c8b27681ebeb72d83930c4a2bbf43a982c52 (diff) | |
| parent | db456749a85e87cdbe3f87b85bf44446d9d5694b (diff) | |
Merge #255
255: Binders r=matklad a=matklad
Binding sources to hir is a fuzzy operation, so let's move it to a special enclave in the source code.
Co-authored-by: Aleksey Kladov <[email protected]>
Diffstat (limited to 'crates')
| -rw-r--r-- | crates/ra_analysis/src/completion/mod.rs | 3 | ||||
| -rw-r--r-- | crates/ra_analysis/src/imp.rs | 27 | ||||
| -rw-r--r-- | crates/ra_hir/src/function/mod.rs | 52 | ||||
| -rw-r--r-- | crates/ra_hir/src/lib.rs | 1 | ||||
| -rw-r--r-- | crates/ra_hir/src/module/mod.rs | 82 | ||||
| -rw-r--r-- | crates/ra_hir/src/module/nameres.rs | 4 | ||||
| -rw-r--r-- | crates/ra_hir/src/source_binder.rs | 96 |
7 files changed, 125 insertions, 140 deletions
diff --git a/crates/ra_analysis/src/completion/mod.rs b/crates/ra_analysis/src/completion/mod.rs index 124da486a..0f154112a 100644 --- a/crates/ra_analysis/src/completion/mod.rs +++ b/crates/ra_analysis/src/completion/mod.rs | |||
| @@ -9,6 +9,7 @@ use ra_syntax::{ | |||
| 9 | }; | 9 | }; |
| 10 | use ra_db::SyntaxDatabase; | 10 | use ra_db::SyntaxDatabase; |
| 11 | use rustc_hash::{FxHashMap}; | 11 | use rustc_hash::{FxHashMap}; |
| 12 | use hir::source_binder; | ||
| 12 | 13 | ||
| 13 | use crate::{ | 14 | use crate::{ |
| 14 | db, | 15 | db, |
| @@ -36,7 +37,7 @@ pub(crate) fn completions( | |||
| 36 | original_file.reparse(&edit) | 37 | original_file.reparse(&edit) |
| 37 | }; | 38 | }; |
| 38 | 39 | ||
| 39 | let module = ctry!(hir::Module::guess_from_position(db, position)?); | 40 | let module = ctry!(source_binder::module_from_position(db, position)?); |
| 40 | 41 | ||
| 41 | let mut res = Vec::new(); | 42 | let mut res = Vec::new(); |
| 42 | let mut has_completions = false; | 43 | let mut has_completions = false; |
diff --git a/crates/ra_analysis/src/imp.rs b/crates/ra_analysis/src/imp.rs index ab6d111c2..975afc145 100644 --- a/crates/ra_analysis/src/imp.rs +++ b/crates/ra_analysis/src/imp.rs | |||
| @@ -16,6 +16,7 @@ use rustc_hash::FxHashSet; | |||
| 16 | use salsa::{Database, ParallelDatabase}; | 16 | use salsa::{Database, ParallelDatabase}; |
| 17 | use hir::{ | 17 | use hir::{ |
| 18 | self, | 18 | self, |
| 19 | source_binder, | ||
| 19 | FnSignatureInfo, | 20 | FnSignatureInfo, |
| 20 | Problem, | 21 | Problem, |
| 21 | }; | 22 | }; |
| @@ -166,7 +167,7 @@ impl AnalysisImpl { | |||
| 166 | /// This return `Vec`: a module may be included from several places. We | 167 | /// This return `Vec`: a module may be included from several places. We |
| 167 | /// don't handle this case yet though, so the Vec has length at most one. | 168 | /// don't handle this case yet though, so the Vec has length at most one. |
| 168 | pub fn parent_module(&self, position: FilePosition) -> Cancelable<Vec<(FileId, FileSymbol)>> { | 169 | pub fn parent_module(&self, position: FilePosition) -> Cancelable<Vec<(FileId, FileSymbol)>> { |
| 169 | let descr = match hir::Module::guess_from_position(&*self.db, position)? { | 170 | let descr = match source_binder::module_from_position(&*self.db, position)? { |
| 170 | None => return Ok(Vec::new()), | 171 | None => return Ok(Vec::new()), |
| 171 | Some(it) => it, | 172 | Some(it) => it, |
| 172 | }; | 173 | }; |
| @@ -185,7 +186,7 @@ impl AnalysisImpl { | |||
| 185 | } | 186 | } |
| 186 | /// Returns `Vec` for the same reason as `parent_module` | 187 | /// Returns `Vec` for the same reason as `parent_module` |
| 187 | pub fn crate_for(&self, file_id: FileId) -> Cancelable<Vec<CrateId>> { | 188 | pub fn crate_for(&self, file_id: FileId) -> Cancelable<Vec<CrateId>> { |
| 188 | let descr = match hir::Module::guess_from_file_id(&*self.db, file_id)? { | 189 | let descr = match source_binder::module_from_file_id(&*self.db, file_id)? { |
| 189 | None => return Ok(Vec::new()), | 190 | None => return Ok(Vec::new()), |
| 190 | Some(it) => it, | 191 | Some(it) => it, |
| 191 | }; | 192 | }; |
| @@ -209,9 +210,11 @@ impl AnalysisImpl { | |||
| 209 | let file = self.db.source_file(position.file_id); | 210 | let file = self.db.source_file(position.file_id); |
| 210 | let syntax = file.syntax(); | 211 | let syntax = file.syntax(); |
| 211 | if let Some(name_ref) = find_node_at_offset::<ast::NameRef>(syntax, position.offset) { | 212 | if let Some(name_ref) = find_node_at_offset::<ast::NameRef>(syntax, position.offset) { |
| 212 | if let Some(fn_descr) = | 213 | if let Some(fn_descr) = source_binder::function_from_child_node( |
| 213 | hir::Function::guess_for_name_ref(&*self.db, position.file_id, name_ref)? | 214 | &*self.db, |
| 214 | { | 215 | position.file_id, |
| 216 | name_ref.syntax(), | ||
| 217 | )? { | ||
| 215 | let scope = fn_descr.scope(&*self.db); | 218 | let scope = fn_descr.scope(&*self.db); |
| 216 | // First try to resolve the symbol locally | 219 | // First try to resolve the symbol locally |
| 217 | if let Some(entry) = scope.resolve_local_name(name_ref) { | 220 | if let Some(entry) = scope.resolve_local_name(name_ref) { |
| @@ -234,7 +237,7 @@ impl AnalysisImpl { | |||
| 234 | if let Some(module) = name.syntax().parent().and_then(ast::Module::cast) { | 237 | if let Some(module) = name.syntax().parent().and_then(ast::Module::cast) { |
| 235 | if module.has_semi() { | 238 | if module.has_semi() { |
| 236 | let parent_module = | 239 | let parent_module = |
| 237 | hir::Module::guess_from_file_id(&*self.db, position.file_id)?; | 240 | source_binder::module_from_file_id(&*self.db, position.file_id)?; |
| 238 | let child_name = module.name(); | 241 | let child_name = module.name(); |
| 239 | match (parent_module, child_name) { | 242 | match (parent_module, child_name) { |
| 240 | (Some(parent_module), Some(child_name)) => { | 243 | (Some(parent_module), Some(child_name)) => { |
| @@ -282,18 +285,18 @@ impl AnalysisImpl { | |||
| 282 | ) -> Cancelable<Option<(ast::BindPat<'a>, hir::Function)>> { | 285 | ) -> Cancelable<Option<(ast::BindPat<'a>, hir::Function)>> { |
| 283 | let syntax = source_file.syntax(); | 286 | let syntax = source_file.syntax(); |
| 284 | if let Some(binding) = find_node_at_offset::<ast::BindPat>(syntax, position.offset) { | 287 | if let Some(binding) = find_node_at_offset::<ast::BindPat>(syntax, position.offset) { |
| 285 | let descr = ctry!(hir::Function::guess_for_bind_pat( | 288 | let descr = ctry!(source_binder::function_from_child_node( |
| 286 | db, | 289 | db, |
| 287 | position.file_id, | 290 | position.file_id, |
| 288 | binding | 291 | binding.syntax(), |
| 289 | )?); | 292 | )?); |
| 290 | return Ok(Some((binding, descr))); | 293 | return Ok(Some((binding, descr))); |
| 291 | }; | 294 | }; |
| 292 | let name_ref = ctry!(find_node_at_offset::<ast::NameRef>(syntax, position.offset)); | 295 | let name_ref = ctry!(find_node_at_offset::<ast::NameRef>(syntax, position.offset)); |
| 293 | let descr = ctry!(hir::Function::guess_for_name_ref( | 296 | let descr = ctry!(source_binder::function_from_child_node( |
| 294 | db, | 297 | db, |
| 295 | position.file_id, | 298 | position.file_id, |
| 296 | name_ref | 299 | name_ref.syntax(), |
| 297 | )?); | 300 | )?); |
| 298 | let scope = descr.scope(db); | 301 | let scope = descr.scope(db); |
| 299 | let resolved = ctry!(scope.resolve_local_name(name_ref)); | 302 | let resolved = ctry!(scope.resolve_local_name(name_ref)); |
| @@ -327,7 +330,7 @@ impl AnalysisImpl { | |||
| 327 | fix: None, | 330 | fix: None, |
| 328 | }) | 331 | }) |
| 329 | .collect::<Vec<_>>(); | 332 | .collect::<Vec<_>>(); |
| 330 | if let Some(m) = hir::Module::guess_from_file_id(&*self.db, file_id)? { | 333 | if let Some(m) = source_binder::module_from_file_id(&*self.db, file_id)? { |
| 331 | for (name_node, problem) in m.problems(&*self.db) { | 334 | for (name_node, problem) in m.problems(&*self.db) { |
| 332 | let diag = match problem { | 335 | let diag = match problem { |
| 333 | Problem::UnresolvedModule { candidate } => { | 336 | Problem::UnresolvedModule { candidate } => { |
| @@ -418,7 +421,7 @@ impl AnalysisImpl { | |||
| 418 | if fs.kind == FN_DEF { | 421 | if fs.kind == FN_DEF { |
| 419 | let fn_file = self.db.source_file(fn_file_id); | 422 | let fn_file = self.db.source_file(fn_file_id); |
| 420 | if let Some(fn_def) = find_node_at_offset(fn_file.syntax(), fs.node_range.start()) { | 423 | if let Some(fn_def) = find_node_at_offset(fn_file.syntax(), fs.node_range.start()) { |
| 421 | let descr = ctry!(hir::Function::guess_from_source( | 424 | let descr = ctry!(source_binder::function_from_source( |
| 422 | &*self.db, fn_file_id, fn_def | 425 | &*self.db, fn_file_id, fn_def |
| 423 | )?); | 426 | )?); |
| 424 | if let Some(descriptor) = descr.signature_info(&*self.db) { | 427 | if let Some(descriptor) = descr.signature_info(&*self.db) { |
diff --git a/crates/ra_hir/src/function/mod.rs b/crates/ra_hir/src/function/mod.rs index e00bca6e3..5187dc051 100644 --- a/crates/ra_hir/src/function/mod.rs +++ b/crates/ra_hir/src/function/mod.rs | |||
| @@ -6,16 +6,11 @@ use std::{ | |||
| 6 | }; | 6 | }; |
| 7 | 7 | ||
| 8 | use ra_syntax::{ | 8 | use ra_syntax::{ |
| 9 | TextRange, TextUnit, SyntaxNodeRef, | 9 | TextRange, TextUnit, |
| 10 | ast::{self, AstNode, DocCommentsOwner, NameOwner}, | 10 | ast::{self, AstNode, DocCommentsOwner, NameOwner}, |
| 11 | }; | 11 | }; |
| 12 | use ra_db::FileId; | ||
| 13 | 12 | ||
| 14 | use crate::{ | 13 | use crate::{ DefId, HirDatabase }; |
| 15 | Cancelable, | ||
| 16 | DefLoc, DefKind, DefId, HirDatabase, SourceItemId, | ||
| 17 | Module, | ||
| 18 | }; | ||
| 19 | 14 | ||
| 20 | pub use self::scope::FnScopes; | 15 | pub use self::scope::FnScopes; |
| 21 | 16 | ||
| @@ -32,49 +27,6 @@ impl Function { | |||
| 32 | Function { fn_id } | 27 | Function { fn_id } |
| 33 | } | 28 | } |
| 34 | 29 | ||
| 35 | pub fn guess_from_source( | ||
| 36 | db: &impl HirDatabase, | ||
| 37 | file_id: FileId, | ||
| 38 | fn_def: ast::FnDef, | ||
| 39 | ) -> Cancelable<Option<Function>> { | ||
| 40 | let module = ctry!(Module::guess_from_child_node(db, file_id, fn_def.syntax())?); | ||
| 41 | let file_items = db.file_items(file_id); | ||
| 42 | let item_id = file_items.id_of(fn_def.syntax()); | ||
| 43 | let source_item_id = SourceItemId { file_id, item_id }; | ||
| 44 | let def_loc = DefLoc { | ||
| 45 | kind: DefKind::Function, | ||
| 46 | source_root_id: module.source_root_id, | ||
| 47 | module_id: module.module_id, | ||
| 48 | source_item_id, | ||
| 49 | }; | ||
| 50 | Ok(Some(Function::new(def_loc.id(db)))) | ||
| 51 | } | ||
| 52 | |||
| 53 | pub fn guess_for_name_ref( | ||
| 54 | db: &impl HirDatabase, | ||
| 55 | file_id: FileId, | ||
| 56 | name_ref: ast::NameRef, | ||
| 57 | ) -> Cancelable<Option<Function>> { | ||
| 58 | Function::guess_for_node(db, file_id, name_ref.syntax()) | ||
| 59 | } | ||
| 60 | |||
| 61 | pub fn guess_for_bind_pat( | ||
| 62 | db: &impl HirDatabase, | ||
| 63 | file_id: FileId, | ||
| 64 | bind_pat: ast::BindPat, | ||
| 65 | ) -> Cancelable<Option<Function>> { | ||
| 66 | Function::guess_for_node(db, file_id, bind_pat.syntax()) | ||
| 67 | } | ||
| 68 | |||
| 69 | fn guess_for_node( | ||
| 70 | db: &impl HirDatabase, | ||
| 71 | file_id: FileId, | ||
| 72 | node: SyntaxNodeRef, | ||
| 73 | ) -> Cancelable<Option<Function>> { | ||
| 74 | let fn_def = ctry!(node.ancestors().find_map(ast::FnDef::cast)); | ||
| 75 | Function::guess_from_source(db, file_id, fn_def) | ||
| 76 | } | ||
| 77 | |||
| 78 | pub fn scope(&self, db: &impl HirDatabase) -> Arc<FnScopes> { | 30 | pub fn scope(&self, db: &impl HirDatabase) -> Arc<FnScopes> { |
| 79 | db.fn_scopes(self.fn_id) | 31 | db.fn_scopes(self.fn_id) |
| 80 | } | 32 | } |
diff --git a/crates/ra_hir/src/lib.rs b/crates/ra_hir/src/lib.rs index 9168dad3b..983ce99cb 100644 --- a/crates/ra_hir/src/lib.rs +++ b/crates/ra_hir/src/lib.rs | |||
| @@ -22,6 +22,7 @@ mod function; | |||
| 22 | mod module; | 22 | mod module; |
| 23 | mod path; | 23 | mod path; |
| 24 | mod arena; | 24 | mod arena; |
| 25 | pub mod source_binder; | ||
| 25 | 26 | ||
| 26 | use std::ops::Index; | 27 | use std::ops::Index; |
| 27 | 28 | ||
diff --git a/crates/ra_hir/src/module/mod.rs b/crates/ra_hir/src/module/mod.rs index 11e6e8e75..580c737c3 100644 --- a/crates/ra_hir/src/module/mod.rs +++ b/crates/ra_hir/src/module/mod.rs | |||
| @@ -3,14 +3,12 @@ pub(super) mod nameres; | |||
| 3 | 3 | ||
| 4 | use std::sync::Arc; | 4 | use std::sync::Arc; |
| 5 | 5 | ||
| 6 | use ra_editor::find_node_at_offset; | ||
| 7 | |||
| 8 | use ra_syntax::{ | 6 | use ra_syntax::{ |
| 9 | algo::generate, | 7 | algo::generate, |
| 10 | ast::{self, AstNode, NameOwner}, | 8 | ast::{self, AstNode, NameOwner}, |
| 11 | SmolStr, SyntaxNode, SyntaxNodeRef, | 9 | SmolStr, SyntaxNode, |
| 12 | }; | 10 | }; |
| 13 | use ra_db::{SourceRootId, FileId, FilePosition, Cancelable}; | 11 | use ra_db::{SourceRootId, FileId, Cancelable}; |
| 14 | use relative_path::RelativePathBuf; | 12 | use relative_path::RelativePathBuf; |
| 15 | 13 | ||
| 16 | use crate::{ | 14 | use crate::{ |
| @@ -30,68 +28,6 @@ pub struct Module { | |||
| 30 | } | 28 | } |
| 31 | 29 | ||
| 32 | impl Module { | 30 | impl Module { |
| 33 | /// Lookup `Module` by `FileId`. Note that this is inherently | ||
| 34 | /// lossy transformation: in general, a single source might correspond to | ||
| 35 | /// several modules. | ||
| 36 | pub fn guess_from_file_id( | ||
| 37 | db: &impl HirDatabase, | ||
| 38 | file_id: FileId, | ||
| 39 | ) -> Cancelable<Option<Module>> { | ||
| 40 | let module_source = ModuleSource::new_file(db, file_id); | ||
| 41 | Module::guess_from_source(db, module_source) | ||
| 42 | } | ||
| 43 | |||
| 44 | /// Lookup `Module` by position in the source code. Note that this | ||
| 45 | /// is inherently lossy transformation: in general, a single source might | ||
| 46 | /// correspond to several modules. | ||
| 47 | pub fn guess_from_position( | ||
| 48 | db: &impl HirDatabase, | ||
| 49 | position: FilePosition, | ||
| 50 | ) -> Cancelable<Option<Module>> { | ||
| 51 | let file = db.source_file(position.file_id); | ||
| 52 | let module_source = match find_node_at_offset::<ast::Module>(file.syntax(), position.offset) | ||
| 53 | { | ||
| 54 | Some(m) if !m.has_semi() => ModuleSource::new_inline(db, position.file_id, m), | ||
| 55 | _ => ModuleSource::new_file(db, position.file_id), | ||
| 56 | }; | ||
| 57 | Module::guess_from_source(db, module_source) | ||
| 58 | } | ||
| 59 | |||
| 60 | pub fn guess_from_child_node( | ||
| 61 | db: &impl HirDatabase, | ||
| 62 | file_id: FileId, | ||
| 63 | node: SyntaxNodeRef, | ||
| 64 | ) -> Cancelable<Option<Module>> { | ||
| 65 | let module_source = if let Some(m) = node | ||
| 66 | .ancestors() | ||
| 67 | .filter_map(ast::Module::cast) | ||
| 68 | .find(|it| !it.has_semi()) | ||
| 69 | { | ||
| 70 | ModuleSource::new_inline(db, file_id, m) | ||
| 71 | } else { | ||
| 72 | ModuleSource::new_file(db, file_id) | ||
| 73 | }; | ||
| 74 | Module::guess_from_source(db, module_source) | ||
| 75 | } | ||
| 76 | |||
| 77 | fn guess_from_source( | ||
| 78 | db: &impl HirDatabase, | ||
| 79 | module_source: ModuleSource, | ||
| 80 | ) -> Cancelable<Option<Module>> { | ||
| 81 | let source_root_id = db.file_source_root(module_source.file_id()); | ||
| 82 | let module_tree = db.module_tree(source_root_id)?; | ||
| 83 | |||
| 84 | let res = match module_tree.any_module_for_source(module_source) { | ||
| 85 | None => None, | ||
| 86 | Some(module_id) => Some(Module { | ||
| 87 | tree: module_tree, | ||
| 88 | source_root_id, | ||
| 89 | module_id, | ||
| 90 | }), | ||
| 91 | }; | ||
| 92 | Ok(res) | ||
| 93 | } | ||
| 94 | |||
| 95 | pub(super) fn new( | 31 | pub(super) fn new( |
| 96 | db: &impl HirDatabase, | 32 | db: &impl HirDatabase, |
| 97 | source_root_id: SourceRootId, | 33 | source_root_id: SourceRootId, |
| @@ -217,16 +153,10 @@ impl ModuleTree { | |||
| 217 | self.mods.iter().map(|(id, _)| id) | 153 | self.mods.iter().map(|(id, _)| id) |
| 218 | } | 154 | } |
| 219 | 155 | ||
| 220 | fn modules_for_source(&self, source: ModuleSource) -> Vec<ModuleId> { | 156 | pub(crate) fn modules_with_sources<'a>( |
| 221 | self.mods | 157 | &'a self, |
| 222 | .iter() | 158 | ) -> impl Iterator<Item = (ModuleId, ModuleSource)> + 'a { |
| 223 | .filter(|(_idx, it)| it.source == source) | 159 | self.mods.iter().map(|(id, m)| (id, m.source)) |
| 224 | .map(|(idx, _)| idx) | ||
| 225 | .collect() | ||
| 226 | } | ||
| 227 | |||
| 228 | fn any_module_for_source(&self, source: ModuleSource) -> Option<ModuleId> { | ||
| 229 | self.modules_for_source(source).pop() | ||
| 230 | } | 160 | } |
| 231 | } | 161 | } |
| 232 | 162 | ||
diff --git a/crates/ra_hir/src/module/nameres.rs b/crates/ra_hir/src/module/nameres.rs index c2b380a80..61a1acfe6 100644 --- a/crates/ra_hir/src/module/nameres.rs +++ b/crates/ra_hir/src/module/nameres.rs | |||
| @@ -363,7 +363,9 @@ mod tests { | |||
| 363 | fn item_map(fixture: &str) -> (Arc<hir::ItemMap>, hir::ModuleId) { | 363 | fn item_map(fixture: &str) -> (Arc<hir::ItemMap>, hir::ModuleId) { |
| 364 | let (db, pos) = MockDatabase::with_position(fixture); | 364 | let (db, pos) = MockDatabase::with_position(fixture); |
| 365 | let source_root = db.file_source_root(pos.file_id); | 365 | let source_root = db.file_source_root(pos.file_id); |
| 366 | let module = hir::Module::guess_from_position(&db, pos).unwrap().unwrap(); | 366 | let module = hir::source_binder::module_from_position(&db, pos) |
| 367 | .unwrap() | ||
| 368 | .unwrap(); | ||
| 367 | let module_id = module.module_id; | 369 | let module_id = module.module_id; |
| 368 | (db.item_map(source_root).unwrap(), module_id) | 370 | (db.item_map(source_root).unwrap(), module_id) |
| 369 | } | 371 | } |
diff --git a/crates/ra_hir/src/source_binder.rs b/crates/ra_hir/src/source_binder.rs new file mode 100644 index 000000000..479155805 --- /dev/null +++ b/crates/ra_hir/src/source_binder.rs | |||
| @@ -0,0 +1,96 @@ | |||
| 1 | /// Lookup hir elements using position 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 ra_db::{FileId, FilePosition, Cancelable}; | ||
| 9 | use ra_editor::find_node_at_offset; | ||
| 10 | use ra_syntax::{ | ||
| 11 | ast::{self, AstNode}, | ||
| 12 | SyntaxNodeRef, | ||
| 13 | }; | ||
| 14 | |||
| 15 | use crate::{ | ||
| 16 | HirDatabase, Module, Function, SourceItemId, | ||
| 17 | module::ModuleSource, | ||
| 18 | DefKind, DefLoc | ||
| 19 | }; | ||
| 20 | |||
| 21 | /// Locates the module by `FileId`. Picks topmost module in the file. | ||
| 22 | pub fn module_from_file_id(db: &impl HirDatabase, file_id: FileId) -> Cancelable<Option<Module>> { | ||
| 23 | let module_source = ModuleSource::new_file(db, file_id); | ||
| 24 | module_from_source(db, module_source) | ||
| 25 | } | ||
| 26 | |||
| 27 | /// Locates the module by position in the source code. | ||
| 28 | pub fn module_from_position( | ||
| 29 | db: &impl HirDatabase, | ||
| 30 | position: FilePosition, | ||
| 31 | ) -> Cancelable<Option<Module>> { | ||
| 32 | let file = db.source_file(position.file_id); | ||
| 33 | let module_source = match find_node_at_offset::<ast::Module>(file.syntax(), position.offset) { | ||
| 34 | Some(m) if !m.has_semi() => ModuleSource::new_inline(db, position.file_id, m), | ||
| 35 | _ => ModuleSource::new_file(db, position.file_id), | ||
| 36 | }; | ||
| 37 | module_from_source(db, module_source) | ||
| 38 | } | ||
| 39 | |||
| 40 | /// Locates the module by child syntax element within the module | ||
| 41 | pub fn module_from_child_node( | ||
| 42 | db: &impl HirDatabase, | ||
| 43 | file_id: FileId, | ||
| 44 | child: SyntaxNodeRef, | ||
| 45 | ) -> Cancelable<Option<Module>> { | ||
| 46 | let module_source = if let Some(m) = child | ||
| 47 | .ancestors() | ||
| 48 | .filter_map(ast::Module::cast) | ||
| 49 | .find(|it| !it.has_semi()) | ||
| 50 | { | ||
| 51 | ModuleSource::new_inline(db, file_id, m) | ||
| 52 | } else { | ||
| 53 | ModuleSource::new_file(db, file_id) | ||
| 54 | }; | ||
| 55 | module_from_source(db, module_source) | ||
| 56 | } | ||
| 57 | |||
| 58 | fn module_from_source( | ||
| 59 | db: &impl HirDatabase, | ||
| 60 | module_source: ModuleSource, | ||
| 61 | ) -> Cancelable<Option<Module>> { | ||
| 62 | let source_root_id = db.file_source_root(module_source.file_id()); | ||
| 63 | let module_tree = db.module_tree(source_root_id)?; | ||
| 64 | let m = module_tree | ||
| 65 | .modules_with_sources() | ||
| 66 | .find(|(_id, src)| src == &module_source); | ||
| 67 | let module_id = ctry!(m).0; | ||
| 68 | Ok(Some(Module::new(db, source_root_id, module_id)?)) | ||
| 69 | } | ||
| 70 | |||
| 71 | pub fn function_from_source( | ||
| 72 | db: &impl HirDatabase, | ||
| 73 | file_id: FileId, | ||
| 74 | fn_def: ast::FnDef, | ||
| 75 | ) -> Cancelable<Option<Function>> { | ||
| 76 | let module = ctry!(module_from_child_node(db, file_id, fn_def.syntax())?); | ||
| 77 | let file_items = db.file_items(file_id); | ||
| 78 | let item_id = file_items.id_of(fn_def.syntax()); | ||
| 79 | let source_item_id = SourceItemId { file_id, item_id }; | ||
| 80 | let def_loc = DefLoc { | ||
| 81 | kind: DefKind::Function, | ||
| 82 | source_root_id: module.source_root_id, | ||
| 83 | module_id: module.module_id, | ||
| 84 | source_item_id, | ||
| 85 | }; | ||
| 86 | Ok(Some(Function::new(def_loc.id(db)))) | ||
| 87 | } | ||
| 88 | |||
| 89 | pub fn function_from_child_node( | ||
| 90 | db: &impl HirDatabase, | ||
| 91 | file_id: FileId, | ||
| 92 | node: SyntaxNodeRef, | ||
| 93 | ) -> Cancelable<Option<Function>> { | ||
| 94 | let fn_def = ctry!(node.ancestors().find_map(ast::FnDef::cast)); | ||
| 95 | function_from_source(db, file_id, fn_def) | ||
| 96 | } | ||
