aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_hir/src/source_binder.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_hir/src/source_binder.rs')
-rw-r--r--crates/ra_hir/src/source_binder.rs96
1 files changed, 96 insertions, 0 deletions
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".
8use ra_db::{FileId, FilePosition, Cancelable};
9use ra_editor::find_node_at_offset;
10use ra_syntax::{
11 ast::{self, AstNode},
12 SyntaxNodeRef,
13};
14
15use 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.
22pub 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.
28pub 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
41pub 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
58fn 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
71pub 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
89pub 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}