aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--crates/ra_analysis/src/completion/complete_dot.rs10
-rw-r--r--crates/ra_analysis/src/completion/complete_scope.rs14
-rw-r--r--crates/ra_analysis/src/db.rs3
-rw-r--r--crates/ra_analysis/src/goto_defenition.rs6
-rw-r--r--crates/ra_analysis/src/hover.rs28
-rw-r--r--crates/ra_analysis/src/imp.rs34
-rw-r--r--crates/ra_analysis/src/lib.rs2
-rw-r--r--crates/ra_analysis/src/runnables.rs9
-rw-r--r--crates/ra_analysis/tests/test/main.rs3
-rw-r--r--crates/ra_arena/src/lib.rs2
-rw-r--r--crates/ra_arena/src/map.rs70
-rw-r--r--crates/ra_db/src/syntax_ptr.rs2
-rw-r--r--crates/ra_editor/src/assists/split_import.rs14
-rw-r--r--crates/ra_hir/src/code_model_api.rs110
-rw-r--r--crates/ra_hir/src/code_model_impl.rs2
-rw-r--r--crates/ra_hir/src/code_model_impl/krate.rs47
-rw-r--r--crates/ra_hir/src/code_model_impl/module.rs154
-rw-r--r--crates/ra_hir/src/db.rs29
-rw-r--r--crates/ra_hir/src/expr.rs770
-rw-r--r--crates/ra_hir/src/function.rs79
-rw-r--r--crates/ra_hir/src/function/scope.rs368
-rw-r--r--crates/ra_hir/src/ids.rs10
-rw-r--r--crates/ra_hir/src/impl_block.rs32
-rw-r--r--crates/ra_hir/src/krate.rs48
-rw-r--r--crates/ra_hir/src/lib.rs20
-rw-r--r--crates/ra_hir/src/mock.rs3
-rw-r--r--crates/ra_hir/src/module.rs376
-rw-r--r--crates/ra_hir/src/module/imp.rs190
-rw-r--r--crates/ra_hir/src/module_tree.rs368
-rw-r--r--crates/ra_hir/src/name.rs14
-rw-r--r--crates/ra_hir/src/nameres.rs (renamed from crates/ra_hir/src/module/nameres.rs)16
-rw-r--r--crates/ra_hir/src/nameres/tests.rs (renamed from crates/ra_hir/src/module/nameres/tests.rs)4
-rw-r--r--crates/ra_hir/src/path.rs14
-rw-r--r--crates/ra_hir/src/query_definitions.rs77
-rw-r--r--crates/ra_hir/src/source_binder.rs80
-rw-r--r--crates/ra_hir/src/ty.rs556
-rw-r--r--crates/ra_hir/src/ty/tests.rs103
-rw-r--r--crates/ra_hir/src/ty/tests/data/backwards.txt (renamed from crates/ra_hir/src/ty/tests/data/0006_backwards.txt)30
-rw-r--r--crates/ra_hir/src/ty/tests/data/basics.txt (renamed from crates/ra_hir/src/ty/tests/data/0001_basics.txt)16
-rw-r--r--crates/ra_hir/src/ty/tests/data/boolean_op.txt31
-rw-r--r--crates/ra_hir/src/ty/tests/data/let.txt (renamed from crates/ra_hir/src/ty/tests/data/0002_let.txt)6
-rw-r--r--crates/ra_hir/src/ty/tests/data/paths.txt (renamed from crates/ra_hir/src/ty/tests/data/0003_paths.txt)4
-rw-r--r--crates/ra_hir/src/ty/tests/data/refs_and_ptrs.txt (renamed from crates/ra_hir/src/ty/tests/data/0005_refs.txt)32
-rw-r--r--crates/ra_hir/src/ty/tests/data/self.txt (renamed from crates/ra_hir/src/ty/tests/data/0007_self.txt)4
-rw-r--r--crates/ra_hir/src/ty/tests/data/struct.txt (renamed from crates/ra_hir/src/ty/tests/data/0004_struct.txt)22
-rw-r--r--crates/ra_hir/src/type_ref.rs4
-rw-r--r--crates/ra_syntax/src/ast.rs52
-rw-r--r--crates/ra_syntax/src/ast/generated.rs35
-rw-r--r--crates/ra_syntax/src/grammar.ron14
49 files changed, 2525 insertions, 1392 deletions
diff --git a/crates/ra_analysis/src/completion/complete_dot.rs b/crates/ra_analysis/src/completion/complete_dot.rs
index 031d8b98f..54ce1b638 100644
--- a/crates/ra_analysis/src/completion/complete_dot.rs
+++ b/crates/ra_analysis/src/completion/complete_dot.rs
@@ -1,4 +1,3 @@
1use ra_syntax::ast::AstNode;
2use hir::{Ty, Def}; 1use hir::{Ty, Def};
3 2
4use crate::Cancelable; 3use crate::Cancelable;
@@ -11,11 +10,12 @@ pub(super) fn complete_dot(acc: &mut Completions, ctx: &CompletionContext) -> Ca
11 _ => return Ok(()), 10 _ => return Ok(()),
12 }; 11 };
13 let infer_result = function.infer(ctx.db)?; 12 let infer_result = function.infer(ctx.db)?;
14 let receiver_ty = if let Some(ty) = infer_result.type_of_node(receiver.syntax()) { 13 let syntax_mapping = function.body_syntax_mapping(ctx.db)?;
15 ty 14 let expr = match syntax_mapping.node_expr(receiver) {
16 } else { 15 Some(expr) => expr,
17 return Ok(()); 16 None => return Ok(()),
18 }; 17 };
18 let receiver_ty = infer_result[expr].clone();
19 if !ctx.is_method_call { 19 if !ctx.is_method_call {
20 complete_fields(acc, ctx, receiver_ty)?; 20 complete_fields(acc, ctx, receiver_ty)?;
21 } 21 }
diff --git a/crates/ra_analysis/src/completion/complete_scope.rs b/crates/ra_analysis/src/completion/complete_scope.rs
index 4dead3689..ee9052d3d 100644
--- a/crates/ra_analysis/src/completion/complete_scope.rs
+++ b/crates/ra_analysis/src/completion/complete_scope.rs
@@ -15,19 +15,22 @@ pub(super) fn complete_scope(acc: &mut Completions, ctx: &CompletionContext) ->
15 None => return Ok(()), 15 None => return Ok(()),
16 }; 16 };
17 if let Some(function) = &ctx.function { 17 if let Some(function) = &ctx.function {
18 let scopes = function.scopes(ctx.db); 18 let scopes = function.scopes(ctx.db)?;
19 complete_fn(acc, &scopes, ctx.offset); 19 complete_fn(acc, &scopes, ctx.offset);
20 } 20 }
21 21
22 let module_scope = module.scope(ctx.db)?; 22 let module_scope = module.scope(ctx.db)?;
23 let (file_id, _) = module.defenition_source(ctx.db)?;
23 module_scope 24 module_scope
24 .entries() 25 .entries()
25 .filter(|(_name, res)| { 26 .filter(|(_name, res)| {
26 // Don't expose this item 27 // Don't expose this item
28 // FIXME: this penetrates through all kinds of abstractions,
29 // we need to figura out the way to do it less ugly.
27 match res.import { 30 match res.import {
28 None => true, 31 None => true,
29 Some(import) => { 32 Some(import) => {
30 let range = import.range(ctx.db, module.file_id()); 33 let range = import.range(ctx.db, file_id);
31 !range.is_subrange(&ctx.leaf.range()) 34 !range.is_subrange(&ctx.leaf.range())
32 } 35 }
33 } 36 }
@@ -40,20 +43,17 @@ pub(super) fn complete_scope(acc: &mut Completions, ctx: &CompletionContext) ->
40 Ok(()) 43 Ok(())
41} 44}
42 45
43fn complete_fn(acc: &mut Completions, scopes: &hir::FnScopes, offset: TextUnit) { 46fn complete_fn(acc: &mut Completions, scopes: &hir::ScopesWithSyntaxMapping, offset: TextUnit) {
44 let mut shadowed = FxHashSet::default(); 47 let mut shadowed = FxHashSet::default();
45 scopes 48 scopes
46 .scope_chain_for_offset(offset) 49 .scope_chain_for_offset(offset)
47 .flat_map(|scope| scopes.entries(scope).iter()) 50 .flat_map(|scope| scopes.scopes.entries(scope).iter())
48 .filter(|entry| shadowed.insert(entry.name())) 51 .filter(|entry| shadowed.insert(entry.name()))
49 .for_each(|entry| { 52 .for_each(|entry| {
50 CompletionItem::new(CompletionKind::Reference, entry.name().to_string()) 53 CompletionItem::new(CompletionKind::Reference, entry.name().to_string())
51 .kind(CompletionItemKind::Binding) 54 .kind(CompletionItemKind::Binding)
52 .add_to(acc) 55 .add_to(acc)
53 }); 56 });
54 if scopes.self_param.is_some() {
55 CompletionItem::new(CompletionKind::Reference, "self").add_to(acc);
56 }
57} 57}
58 58
59#[cfg(test)] 59#[cfg(test)]
diff --git a/crates/ra_analysis/src/db.rs b/crates/ra_analysis/src/db.rs
index 5422a400b..1709be5cf 100644
--- a/crates/ra_analysis/src/db.rs
+++ b/crates/ra_analysis/src/db.rs
@@ -106,6 +106,9 @@ salsa::database_storage! {
106 fn struct_data() for hir::db::StructDataQuery; 106 fn struct_data() for hir::db::StructDataQuery;
107 fn enum_data() for hir::db::EnumDataQuery; 107 fn enum_data() for hir::db::EnumDataQuery;
108 fn impls_in_module() for hir::db::ImplsInModuleQuery; 108 fn impls_in_module() for hir::db::ImplsInModuleQuery;
109 fn body_hir() for hir::db::BodyHirQuery;
110 fn body_syntax_mapping() for hir::db::BodySyntaxMappingQuery;
111 fn fn_signature() for hir::db::FnSignatureQuery;
109 } 112 }
110 } 113 }
111} 114}
diff --git a/crates/ra_analysis/src/goto_defenition.rs b/crates/ra_analysis/src/goto_defenition.rs
index e37421f8d..aa0616e3b 100644
--- a/crates/ra_analysis/src/goto_defenition.rs
+++ b/crates/ra_analysis/src/goto_defenition.rs
@@ -28,7 +28,7 @@ pub(crate) fn reference_defenition(
28 if let Some(fn_descr) = 28 if let Some(fn_descr) =
29 hir::source_binder::function_from_child_node(db, file_id, name_ref.syntax())? 29 hir::source_binder::function_from_child_node(db, file_id, name_ref.syntax())?
30 { 30 {
31 let scope = fn_descr.scopes(db); 31 let scope = fn_descr.scopes(db)?;
32 // First try to resolve the symbol locally 32 // First try to resolve the symbol locally
33 if let Some(entry) = scope.resolve_local_name(name_ref) { 33 if let Some(entry) = scope.resolve_local_name(name_ref) {
34 let nav = NavigationTarget { 34 let nav = NavigationTarget {
@@ -60,8 +60,8 @@ fn name_defenition(
60 if let Some(child_module) = 60 if let Some(child_module) =
61 hir::source_binder::module_from_declaration(db, file_id, module)? 61 hir::source_binder::module_from_declaration(db, file_id, module)?
62 { 62 {
63 let file_id = child_module.file_id(); 63 let (file_id, _) = child_module.defenition_source(db)?;
64 let name = match child_module.name() { 64 let name = match child_module.name(db)? {
65 Some(name) => name.to_string().into(), 65 Some(name) => name.to_string().into(),
66 None => "".into(), 66 None => "".into(),
67 }; 67 };
diff --git a/crates/ra_analysis/src/hover.rs b/crates/ra_analysis/src/hover.rs
index 2cf79eebf..06632df4f 100644
--- a/crates/ra_analysis/src/hover.rs
+++ b/crates/ra_analysis/src/hover.rs
@@ -3,7 +3,7 @@ use ra_editor::find_node_at_offset;
3use ra_syntax::{ 3use ra_syntax::{
4 AstNode, SyntaxNode, 4 AstNode, SyntaxNode,
5 ast::{self, NameOwner}, 5 ast::{self, NameOwner},
6 algo::{find_covering_node, visit::{visitor, Visitor}}, 6 algo::{find_covering_node, find_leaf_at_offset, visit::{visitor, Visitor}},
7}; 7};
8 8
9use crate::{db::RootDatabase, RangeInfo, FilePosition, FileRange, NavigationTarget}; 9use crate::{db::RootDatabase, RangeInfo, FilePosition, FileRange, NavigationTarget};
@@ -26,13 +26,17 @@ pub(crate) fn hover(
26 } 26 }
27 } 27 }
28 if range.is_none() { 28 if range.is_none() {
29 let expr: ast::Expr = ctry!(find_node_at_offset(file.syntax(), position.offset)); 29 let node = find_leaf_at_offset(file.syntax(), position.offset).find_map(|leaf| {
30 leaf.ancestors()
31 .find(|n| ast::Expr::cast(*n).is_some() || ast::Pat::cast(*n).is_some())
32 });
33 let node = ctry!(node);
30 let frange = FileRange { 34 let frange = FileRange {
31 file_id: position.file_id, 35 file_id: position.file_id,
32 range: expr.syntax().range(), 36 range: node.range(),
33 }; 37 };
34 res.extend(type_of(db, frange)?); 38 res.extend(type_of(db, frange)?);
35 range = Some(expr.syntax().range()); 39 range = Some(node.range());
36 }; 40 };
37 41
38 let range = ctry!(range); 42 let range = ctry!(range);
@@ -60,7 +64,14 @@ pub(crate) fn type_of(db: &RootDatabase, frange: FileRange) -> Cancelable<Option
60 parent_fn 64 parent_fn
61 )?); 65 )?);
62 let infer = function.infer(db)?; 66 let infer = function.infer(db)?;
63 Ok(infer.type_of_node(node).map(|t| t.to_string())) 67 let syntax_mapping = function.body_syntax_mapping(db)?;
68 if let Some(expr) = ast::Expr::cast(node).and_then(|e| syntax_mapping.node_expr(e)) {
69 Ok(Some(infer[expr].to_string()))
70 } else if let Some(pat) = ast::Pat::cast(node).and_then(|p| syntax_mapping.node_pat(p)) {
71 Ok(Some(infer[pat].to_string()))
72 } else {
73 Ok(None)
74 }
64} 75}
65 76
66// FIXME: this should not really use navigation target. Rather, approximatelly 77// FIXME: this should not really use navigation target. Rather, approximatelly
@@ -193,6 +204,13 @@ mod tests {
193 } 204 }
194 205
195 #[test] 206 #[test]
207 fn hover_for_local_variable_pat() {
208 let (analysis, position) = single_file_with_position("fn func(fo<|>o: i32) {}");
209 let hover = analysis.hover(position).unwrap().unwrap();
210 assert_eq!(hover.info, "i32");
211 }
212
213 #[test]
196 fn test_type_of_for_function() { 214 fn test_type_of_for_function() {
197 let (analysis, range) = single_file_with_range( 215 let (analysis, range) = single_file_with_range(
198 " 216 "
diff --git a/crates/ra_analysis/src/imp.rs b/crates/ra_analysis/src/imp.rs
index 6ab3c5476..07a966290 100644
--- a/crates/ra_analysis/src/imp.rs
+++ b/crates/ra_analysis/src/imp.rs
@@ -105,39 +105,35 @@ impl db::RootDatabase {
105 &self, 105 &self,
106 position: FilePosition, 106 position: FilePosition,
107 ) -> Cancelable<Vec<NavigationTarget>> { 107 ) -> Cancelable<Vec<NavigationTarget>> {
108 let descr = match source_binder::module_from_position(self, position)? { 108 let module = match source_binder::module_from_position(self, position)? {
109 None => return Ok(Vec::new()), 109 None => return Ok(Vec::new()),
110 Some(it) => it, 110 Some(it) => it,
111 }; 111 };
112 let (file_id, decl) = match descr.parent_link_source(self) { 112 let (file_id, ast_module) = match module.declaration_source(self)? {
113 None => return Ok(Vec::new()), 113 None => return Ok(Vec::new()),
114 Some(it) => it, 114 Some(it) => it,
115 }; 115 };
116 let decl = decl.borrowed(); 116 let ast_module = ast_module.borrowed();
117 let decl_name = decl.name().unwrap(); 117 let name = ast_module.name().unwrap();
118 Ok(vec![NavigationTarget { 118 Ok(vec![NavigationTarget {
119 file_id, 119 file_id,
120 name: decl_name.text(), 120 name: name.text(),
121 range: decl_name.syntax().range(), 121 range: name.syntax().range(),
122 kind: MODULE, 122 kind: MODULE,
123 ptr: None, 123 ptr: None,
124 }]) 124 }])
125 } 125 }
126 /// Returns `Vec` for the same reason as `parent_module` 126 /// Returns `Vec` for the same reason as `parent_module`
127 pub(crate) fn crate_for(&self, file_id: FileId) -> Cancelable<Vec<CrateId>> { 127 pub(crate) fn crate_for(&self, file_id: FileId) -> Cancelable<Vec<CrateId>> {
128 let descr = match source_binder::module_from_file_id(self, file_id)? { 128 let module = match source_binder::module_from_file_id(self, file_id)? {
129 Some(it) => it,
129 None => return Ok(Vec::new()), 130 None => return Ok(Vec::new()),
131 };
132 let krate = match module.krate(self)? {
130 Some(it) => it, 133 Some(it) => it,
134 None => return Ok(Vec::new()),
131 }; 135 };
132 let root = descr.crate_root(); 136 Ok(vec![krate.crate_id()])
133 let file_id = root.file_id();
134
135 let crate_graph = self.crate_graph();
136 let crate_id = crate_graph.crate_id_for_crate_root(file_id);
137 Ok(crate_id.into_iter().collect())
138 }
139 pub(crate) fn crate_root(&self, crate_id: CrateId) -> FileId {
140 self.crate_graph().crate_root(crate_id)
141 } 137 }
142 pub(crate) fn find_all_refs( 138 pub(crate) fn find_all_refs(
143 &self, 139 &self,
@@ -157,7 +153,7 @@ impl db::RootDatabase {
157 .collect::<Vec<_>>(); 153 .collect::<Vec<_>>();
158 ret.extend( 154 ret.extend(
159 descr 155 descr
160 .scopes(self) 156 .scopes(self)?
161 .find_all_refs(binding) 157 .find_all_refs(binding)
162 .into_iter() 158 .into_iter()
163 .map(|ref_desc| (position.file_id, ref_desc.range)), 159 .map(|ref_desc| (position.file_id, ref_desc.range)),
@@ -185,7 +181,7 @@ impl db::RootDatabase {
185 position.file_id, 181 position.file_id,
186 name_ref.syntax(), 182 name_ref.syntax(),
187 )?); 183 )?);
188 let scope = descr.scopes(db); 184 let scope = descr.scopes(db)?;
189 let resolved = ctry!(scope.resolve_local_name(name_ref)); 185 let resolved = ctry!(scope.resolve_local_name(name_ref));
190 let resolved = resolved.ptr().resolve(source_file); 186 let resolved = resolved.ptr().resolve(source_file);
191 let binding = ctry!(find_node_at_offset::<ast::BindPat>( 187 let binding = ctry!(find_node_at_offset::<ast::BindPat>(
@@ -209,7 +205,7 @@ impl db::RootDatabase {
209 }) 205 })
210 .collect::<Vec<_>>(); 206 .collect::<Vec<_>>();
211 if let Some(m) = source_binder::module_from_file_id(self, file_id)? { 207 if let Some(m) = source_binder::module_from_file_id(self, file_id)? {
212 for (name_node, problem) in m.problems(self) { 208 for (name_node, problem) in m.problems(self)? {
213 let source_root = self.file_source_root(file_id); 209 let source_root = self.file_source_root(file_id);
214 let diag = match problem { 210 let diag = match problem {
215 Problem::UnresolvedModule { candidate } => { 211 Problem::UnresolvedModule { candidate } => {
diff --git a/crates/ra_analysis/src/lib.rs b/crates/ra_analysis/src/lib.rs
index feed44b2d..c8f846c56 100644
--- a/crates/ra_analysis/src/lib.rs
+++ b/crates/ra_analysis/src/lib.rs
@@ -401,7 +401,7 @@ impl Analysis {
401 } 401 }
402 /// Returns the root file of the given crate. 402 /// Returns the root file of the given crate.
403 pub fn crate_root(&self, crate_id: CrateId) -> Cancelable<FileId> { 403 pub fn crate_root(&self, crate_id: CrateId) -> Cancelable<FileId> {
404 Ok(self.db.crate_root(crate_id)) 404 Ok(self.db.crate_graph().crate_root(crate_id))
405 } 405 }
406 /// Returns the set of possible targets to run for the current file. 406 /// Returns the set of possible targets to run for the current file.
407 pub fn runnables(&self, file_id: FileId) -> Cancelable<Vec<Runnable>> { 407 pub fn runnables(&self, file_id: FileId) -> Cancelable<Vec<Runnable>> {
diff --git a/crates/ra_analysis/src/runnables.rs b/crates/ra_analysis/src/runnables.rs
index 474267605..216209098 100644
--- a/crates/ra_analysis/src/runnables.rs
+++ b/crates/ra_analysis/src/runnables.rs
@@ -72,12 +72,15 @@ fn runnable_mod(db: &RootDatabase, file_id: FileId, module: ast::Module) -> Opti
72 let range = module.syntax().range(); 72 let range = module.syntax().range();
73 let module = 73 let module =
74 hir::source_binder::module_from_child_node(db, file_id, module.syntax()).ok()??; 74 hir::source_binder::module_from_child_node(db, file_id, module.syntax()).ok()??;
75
76 // FIXME: thread cancellation instead of `.ok`ing
75 let path = module 77 let path = module
76 .path_to_root() 78 .path_to_root(db)
79 .ok()?
77 .into_iter() 80 .into_iter()
78 .rev() 81 .rev()
79 .into_iter() 82 .filter_map(|it| it.name(db).ok())
80 .filter_map(|it| it.name().map(Clone::clone)) 83 .filter_map(|it| it)
81 .join("::"); 84 .join("::");
82 Some(Runnable { 85 Some(Runnable {
83 range, 86 range,
diff --git a/crates/ra_analysis/tests/test/main.rs b/crates/ra_analysis/tests/test/main.rs
index 26da7c10c..1f70af12a 100644
--- a/crates/ra_analysis/tests/test/main.rs
+++ b/crates/ra_analysis/tests/test/main.rs
@@ -31,6 +31,7 @@ fn test_unresolved_module_diagnostic() {
31 ); 31 );
32} 32}
33 33
34// FIXME: move this test to hir
34#[test] 35#[test]
35fn test_unresolved_module_diagnostic_no_diag_for_inline_mode() { 36fn test_unresolved_module_diagnostic_no_diag_for_inline_mode() {
36 let (analysis, file_id) = single_file("mod foo {}"); 37 let (analysis, file_id) = single_file("mod foo {}");
@@ -69,7 +70,7 @@ fn test_resolve_parent_module_for_inline() {
69 ); 70 );
70 let symbols = analysis.parent_module(pos).unwrap(); 71 let symbols = analysis.parent_module(pos).unwrap();
71 assert_eq_dbg( 72 assert_eq_dbg(
72 r#"[NavigationTarget { file_id: FileId(1), name: "bar", kind: MODULE, range: [18; 21), ptr: None }]"#, 73 r#"[NavigationTarget { file_id: FileId(1), name: "baz", kind: MODULE, range: [36; 39), ptr: None }]"#,
73 &symbols, 74 &symbols,
74 ); 75 );
75} 76}
diff --git a/crates/ra_arena/src/lib.rs b/crates/ra_arena/src/lib.rs
index a5eeb4118..040977dc4 100644
--- a/crates/ra_arena/src/lib.rs
+++ b/crates/ra_arena/src/lib.rs
@@ -6,6 +6,8 @@ use std::{
6 ops::{Index, IndexMut}, 6 ops::{Index, IndexMut},
7}; 7};
8 8
9pub mod map;
10
9#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] 11#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
10pub struct RawId(u32); 12pub struct RawId(u32);
11 13
diff --git a/crates/ra_arena/src/map.rs b/crates/ra_arena/src/map.rs
new file mode 100644
index 000000000..2f09d677f
--- /dev/null
+++ b/crates/ra_arena/src/map.rs
@@ -0,0 +1,70 @@
1//! A map from arena IDs to some other type. Space requirement is O(highest ID).
2
3use std::marker::PhantomData;
4
5use super::ArenaId;
6
7/// A map from arena IDs to some other type. Space requirement is O(highest ID).
8#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
9pub struct ArenaMap<ID, T> {
10 v: Vec<Option<T>>,
11 _ty: PhantomData<ID>,
12}
13
14impl<ID: ArenaId, T> ArenaMap<ID, T> {
15 pub fn insert(&mut self, id: ID, t: T) {
16 let idx = Self::to_idx(id);
17 if self.v.capacity() <= idx {
18 self.v.reserve(idx + 1 - self.v.capacity());
19 }
20 if self.v.len() <= idx {
21 while self.v.len() <= idx {
22 self.v.push(None);
23 }
24 }
25 self.v[idx] = Some(t);
26 }
27
28 pub fn get(&self, id: ID) -> Option<&T> {
29 self.v.get(Self::to_idx(id)).and_then(|it| it.as_ref())
30 }
31
32 pub fn values(&self) -> impl Iterator<Item = &T> {
33 self.v.iter().filter_map(|o| o.as_ref())
34 }
35
36 pub fn values_mut(&mut self) -> impl Iterator<Item = &mut T> {
37 self.v.iter_mut().filter_map(|o| o.as_mut())
38 }
39
40 pub fn iter(&self) -> impl Iterator<Item = (ID, &T)> {
41 self.v
42 .iter()
43 .enumerate()
44 .filter_map(|(idx, o)| Some((Self::from_idx(idx), o.as_ref()?)))
45 }
46
47 fn to_idx(id: ID) -> usize {
48 u32::from(id.into_raw()) as usize
49 }
50
51 fn from_idx(idx: usize) -> ID {
52 ID::from_raw((idx as u32).into())
53 }
54}
55
56impl<ID: ArenaId, T> std::ops::Index<ID> for ArenaMap<ID, T> {
57 type Output = T;
58 fn index(&self, id: ID) -> &T {
59 self.v[Self::to_idx(id)].as_ref().unwrap()
60 }
61}
62
63impl<ID, T> Default for ArenaMap<ID, T> {
64 fn default() -> Self {
65 ArenaMap {
66 v: Vec::new(),
67 _ty: PhantomData,
68 }
69 }
70}
diff --git a/crates/ra_db/src/syntax_ptr.rs b/crates/ra_db/src/syntax_ptr.rs
index 744cb2352..5bfcedf2b 100644
--- a/crates/ra_db/src/syntax_ptr.rs
+++ b/crates/ra_db/src/syntax_ptr.rs
@@ -1,6 +1,6 @@
1use ra_syntax::{SourceFileNode, SyntaxKind, SyntaxNode, SyntaxNodeRef, TextRange}; 1use ra_syntax::{SourceFileNode, SyntaxKind, SyntaxNode, SyntaxNodeRef, TextRange};
2 2
3/// A pionter to a syntax node inside a file. 3/// A pointer to a syntax node inside a file.
4#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] 4#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
5pub struct LocalSyntaxPtr { 5pub struct LocalSyntaxPtr {
6 range: TextRange, 6 range: TextRange,
diff --git a/crates/ra_editor/src/assists/split_import.rs b/crates/ra_editor/src/assists/split_import.rs
index 75f9e8dfb..e4015f07d 100644
--- a/crates/ra_editor/src/assists/split_import.rs
+++ b/crates/ra_editor/src/assists/split_import.rs
@@ -19,7 +19,10 @@ pub fn split_import(ctx: AssistCtx) -> Option<Assist> {
19 } 19 }
20 20
21 let l_curly = colon_colon.range().end(); 21 let l_curly = colon_colon.range().end();
22 let r_curly = top_path.syntax().range().end(); 22 let r_curly = match top_path.syntax().parent().and_then(ast::UseTree::cast) {
23 Some(tree) => tree.syntax().range().end(),
24 None => top_path.syntax().range().end(),
25 };
23 26
24 ctx.build("split import", |edit| { 27 ctx.build("split import", |edit| {
25 edit.insert(l_curly, "{"); 28 edit.insert(l_curly, "{");
@@ -41,4 +44,13 @@ mod tests {
41 "use crate::{<|>db::RootDatabase};", 44 "use crate::{<|>db::RootDatabase};",
42 ) 45 )
43 } 46 }
47
48 #[test]
49 fn split_import_works_with_trees() {
50 check_assist(
51 split_import,
52 "use algo:<|>:visitor::{Visitor, visit}",
53 "use algo::{<|>visitor::{Visitor, visit}}",
54 )
55 }
44} 56}
diff --git a/crates/ra_hir/src/code_model_api.rs b/crates/ra_hir/src/code_model_api.rs
new file mode 100644
index 000000000..09b532f74
--- /dev/null
+++ b/crates/ra_hir/src/code_model_api.rs
@@ -0,0 +1,110 @@
1use relative_path::RelativePathBuf;
2use ra_db::{CrateId, Cancelable, FileId};
3use ra_syntax::{ast, SyntaxNode};
4
5use crate::{Name, db::HirDatabase, DefId, Path, PerNs, nameres::ModuleScope};
6
7/// hir::Crate describes a single crate. It's the main inteface with which
8/// crate's dependencies interact. Mostly, it should be just a proxy for the
9/// root module.
10#[derive(Debug, Clone, PartialEq, Eq, Hash)]
11pub struct Crate {
12 pub(crate) crate_id: CrateId,
13}
14
15#[derive(Debug)]
16pub struct CrateDependency {
17 pub krate: Crate,
18 pub name: Name,
19}
20
21impl Crate {
22 pub fn crate_id(&self) -> CrateId {
23 self.crate_id
24 }
25 pub fn dependencies(&self, db: &impl HirDatabase) -> Cancelable<Vec<CrateDependency>> {
26 Ok(self.dependencies_impl(db))
27 }
28 pub fn root_module(&self, db: &impl HirDatabase) -> Cancelable<Option<Module>> {
29 self.root_module_impl(db)
30 }
31}
32
33#[derive(Debug, Clone, PartialEq, Eq, Hash)]
34pub struct Module {
35 pub(crate) def_id: DefId,
36}
37
38pub enum ModuleSource {
39 SourceFile(ast::SourceFileNode),
40 Module(ast::ModuleNode),
41}
42
43#[derive(Clone, Debug, Hash, PartialEq, Eq)]
44pub enum Problem {
45 UnresolvedModule {
46 candidate: RelativePathBuf,
47 },
48 NotDirOwner {
49 move_to: RelativePathBuf,
50 candidate: RelativePathBuf,
51 },
52}
53
54impl Module {
55 /// Name of this module.
56 pub fn name(&self, db: &impl HirDatabase) -> Cancelable<Option<Name>> {
57 self.name_impl(db)
58 }
59
60 /// Returns a node which defines this module. That is, a file or a `mod foo {}` with items.
61 pub fn defenition_source(&self, db: &impl HirDatabase) -> Cancelable<(FileId, ModuleSource)> {
62 self.defenition_source_impl(db)
63 }
64 /// Returns a node which declares this module, either a `mod foo;` or a `mod foo {}`.
65 /// `None` for the crate root.
66 pub fn declaration_source(
67 &self,
68 db: &impl HirDatabase,
69 ) -> Cancelable<Option<(FileId, ast::ModuleNode)>> {
70 self.declaration_source_impl(db)
71 }
72
73 /// Returns the crate this module is part of.
74 pub fn krate(&self, db: &impl HirDatabase) -> Cancelable<Option<Crate>> {
75 self.krate_impl(db)
76 }
77 /// Topmost parent of this module. Every module has a `crate_root`, but some
78 /// might miss `krate`. This can happen if a module's file is not included
79 /// into any module tree of any target from Cargo.toml.
80 pub fn crate_root(&self, db: &impl HirDatabase) -> Cancelable<Module> {
81 self.crate_root_impl(db)
82 }
83 /// Finds a child module with the specified name.
84 pub fn child(&self, db: &impl HirDatabase, name: &Name) -> Cancelable<Option<Module>> {
85 self.child_impl(db, name)
86 }
87 /// Finds a parent module.
88 pub fn parent(&self, db: &impl HirDatabase) -> Cancelable<Option<Module>> {
89 self.parent_impl(db)
90 }
91 pub fn path_to_root(&self, db: &impl HirDatabase) -> Cancelable<Vec<Module>> {
92 let mut res = vec![self.clone()];
93 let mut curr = self.clone();
94 while let Some(next) = curr.parent(db)? {
95 res.push(next.clone());
96 curr = next
97 }
98 Ok(res)
99 }
100 /// Returns a `ModuleScope`: a set of items, visible in this module.
101 pub fn scope(&self, db: &impl HirDatabase) -> Cancelable<ModuleScope> {
102 self.scope_impl(db)
103 }
104 pub fn resolve_path(&self, db: &impl HirDatabase, path: &Path) -> Cancelable<PerNs<DefId>> {
105 self.resolve_path_impl(db, path)
106 }
107 pub fn problems(&self, db: &impl HirDatabase) -> Cancelable<Vec<(SyntaxNode, Problem)>> {
108 self.problems_impl(db)
109 }
110}
diff --git a/crates/ra_hir/src/code_model_impl.rs b/crates/ra_hir/src/code_model_impl.rs
new file mode 100644
index 000000000..157b0c616
--- /dev/null
+++ b/crates/ra_hir/src/code_model_impl.rs
@@ -0,0 +1,2 @@
1mod krate; // `crate` is invalid ident :(
2mod module;
diff --git a/crates/ra_hir/src/code_model_impl/krate.rs b/crates/ra_hir/src/code_model_impl/krate.rs
new file mode 100644
index 000000000..3275eafed
--- /dev/null
+++ b/crates/ra_hir/src/code_model_impl/krate.rs
@@ -0,0 +1,47 @@
1use ra_db::{CrateId, Cancelable};
2
3use crate::{
4 HirFileId, Crate, CrateDependency, AsName, DefLoc, DefKind, Module, SourceItemId,
5 db::HirDatabase,
6};
7
8impl Crate {
9 pub(crate) fn new(crate_id: CrateId) -> Crate {
10 Crate { crate_id }
11 }
12 pub(crate) fn dependencies_impl(&self, db: &impl HirDatabase) -> Vec<CrateDependency> {
13 let crate_graph = db.crate_graph();
14 crate_graph
15 .dependencies(self.crate_id)
16 .map(|dep| {
17 let krate = Crate::new(dep.crate_id());
18 let name = dep.as_name();
19 CrateDependency { krate, name }
20 })
21 .collect()
22 }
23 pub(crate) fn root_module_impl(&self, db: &impl HirDatabase) -> Cancelable<Option<Module>> {
24 let crate_graph = db.crate_graph();
25 let file_id = crate_graph.crate_root(self.crate_id);
26 let source_root_id = db.file_source_root(file_id);
27 let file_id = HirFileId::from(file_id);
28 let module_tree = db.module_tree(source_root_id)?;
29 // FIXME: teach module tree about crate roots instead of guessing
30 let source = SourceItemId {
31 file_id,
32 item_id: None,
33 };
34 let module_id = ctry!(module_tree.find_module_by_source(source));
35
36 let def_loc = DefLoc {
37 kind: DefKind::Module,
38 source_root_id,
39 module_id,
40 source_item_id: module_id.source(&module_tree),
41 };
42 let def_id = def_loc.id(db);
43
44 let module = Module::new(def_id);
45 Ok(Some(module))
46 }
47}
diff --git a/crates/ra_hir/src/code_model_impl/module.rs b/crates/ra_hir/src/code_model_impl/module.rs
new file mode 100644
index 000000000..eb35779f1
--- /dev/null
+++ b/crates/ra_hir/src/code_model_impl/module.rs
@@ -0,0 +1,154 @@
1use ra_db::{Cancelable, SourceRootId, FileId};
2use ra_syntax::{ast, SyntaxNode, AstNode};
3
4use crate::{
5 Module, ModuleSource, Problem,
6 Crate, DefId, DefLoc, DefKind, Name, Path, PathKind, PerNs, Def, ModuleId,
7 nameres::ModuleScope,
8 db::HirDatabase,
9};
10
11impl Module {
12 pub(crate) fn new(def_id: DefId) -> Self {
13 crate::code_model_api::Module { def_id }
14 }
15 pub(crate) fn from_module_id(
16 db: &impl HirDatabase,
17 source_root_id: SourceRootId,
18 module_id: ModuleId,
19 ) -> Cancelable<Self> {
20 let module_tree = db.module_tree(source_root_id)?;
21 let def_loc = DefLoc {
22 kind: DefKind::Module,
23 source_root_id,
24 module_id,
25 source_item_id: module_id.source(&module_tree),
26 };
27 let def_id = def_loc.id(db);
28 let module = Module::new(def_id);
29 Ok(module)
30 }
31
32 pub(crate) fn name_impl(&self, db: &impl HirDatabase) -> Cancelable<Option<Name>> {
33 let loc = self.def_id.loc(db);
34 let module_tree = db.module_tree(loc.source_root_id)?;
35 let link = ctry!(loc.module_id.parent_link(&module_tree));
36 Ok(Some(link.name(&module_tree).clone()))
37 }
38
39 pub fn defenition_source_impl(
40 &self,
41 db: &impl HirDatabase,
42 ) -> Cancelable<(FileId, ModuleSource)> {
43 let loc = self.def_id.loc(db);
44 let file_id = loc.source_item_id.file_id.as_original_file();
45 let syntax_node = db.file_item(loc.source_item_id);
46 let syntax_node = syntax_node.borrowed();
47 let module_source = if let Some(source_file) = ast::SourceFile::cast(syntax_node) {
48 ModuleSource::SourceFile(source_file.owned())
49 } else {
50 let module = ast::Module::cast(syntax_node).unwrap();
51 ModuleSource::Module(module.owned())
52 };
53 Ok((file_id, module_source))
54 }
55
56 pub fn declaration_source_impl(
57 &self,
58 db: &impl HirDatabase,
59 ) -> Cancelable<Option<(FileId, ast::ModuleNode)>> {
60 let loc = self.def_id.loc(db);
61 let module_tree = db.module_tree(loc.source_root_id)?;
62 let link = ctry!(loc.module_id.parent_link(&module_tree));
63 let file_id = link
64 .owner(&module_tree)
65 .source(&module_tree)
66 .file_id
67 .as_original_file();
68 let src = link.source(&module_tree, db);
69 Ok(Some((file_id, src)))
70 }
71
72 pub(crate) fn krate_impl(&self, db: &impl HirDatabase) -> Cancelable<Option<Crate>> {
73 let root = self.crate_root(db)?;
74 let loc = root.def_id.loc(db);
75 let file_id = loc.source_item_id.file_id.as_original_file();
76
77 let crate_graph = db.crate_graph();
78 let crate_id = ctry!(crate_graph.crate_id_for_crate_root(file_id));
79 Ok(Some(Crate::new(crate_id)))
80 }
81
82 pub(crate) fn crate_root_impl(&self, db: &impl HirDatabase) -> Cancelable<Module> {
83 let loc = self.def_id.loc(db);
84 let module_tree = db.module_tree(loc.source_root_id)?;
85 let module_id = loc.module_id.crate_root(&module_tree);
86 Module::from_module_id(db, loc.source_root_id, module_id)
87 }
88 /// Finds a child module with the specified name.
89 pub fn child_impl(&self, db: &impl HirDatabase, name: &Name) -> Cancelable<Option<Module>> {
90 let loc = self.def_id.loc(db);
91 let module_tree = db.module_tree(loc.source_root_id)?;
92 let child_id = ctry!(loc.module_id.child(&module_tree, name));
93 Module::from_module_id(db, loc.source_root_id, child_id).map(Some)
94 }
95 pub fn parent_impl(&self, db: &impl HirDatabase) -> Cancelable<Option<Module>> {
96 let loc = self.def_id.loc(db);
97 let module_tree = db.module_tree(loc.source_root_id)?;
98 let parent_id = ctry!(loc.module_id.parent(&module_tree));
99 Module::from_module_id(db, loc.source_root_id, parent_id).map(Some)
100 }
101 /// Returns a `ModuleScope`: a set of items, visible in this module.
102 pub fn scope_impl(&self, db: &impl HirDatabase) -> Cancelable<ModuleScope> {
103 let loc = self.def_id.loc(db);
104 let item_map = db.item_map(loc.source_root_id)?;
105 let res = item_map.per_module[&loc.module_id].clone();
106 Ok(res)
107 }
108 pub fn resolve_path_impl(
109 &self,
110 db: &impl HirDatabase,
111 path: &Path,
112 ) -> Cancelable<PerNs<DefId>> {
113 let mut curr_per_ns = PerNs::types(
114 match path.kind {
115 PathKind::Crate => self.crate_root(db)?,
116 PathKind::Self_ | PathKind::Plain => self.clone(),
117 PathKind::Super => {
118 if let Some(p) = self.parent(db)? {
119 p
120 } else {
121 return Ok(PerNs::none());
122 }
123 }
124 }
125 .def_id,
126 );
127
128 let segments = &path.segments;
129 for name in segments.iter() {
130 let curr = if let Some(r) = curr_per_ns.as_ref().take_types() {
131 r
132 } else {
133 return Ok(PerNs::none());
134 };
135 let module = match curr.resolve(db)? {
136 Def::Module(it) => it,
137 // TODO here would be the place to handle enum variants...
138 _ => return Ok(PerNs::none()),
139 };
140 let scope = module.scope(db)?;
141 curr_per_ns = if let Some(r) = scope.get(&name) {
142 r.def_id
143 } else {
144 return Ok(PerNs::none());
145 };
146 }
147 Ok(curr_per_ns)
148 }
149 pub fn problems_impl(&self, db: &impl HirDatabase) -> Cancelable<Vec<(SyntaxNode, Problem)>> {
150 let loc = self.def_id.loc(db);
151 let module_tree = db.module_tree(loc.source_root_id)?;
152 Ok(loc.module_id.problems(&module_tree, db))
153 }
154}
diff --git a/crates/ra_hir/src/db.rs b/crates/ra_hir/src/db.rs
index 58296fc6f..033f9d25f 100644
--- a/crates/ra_hir/src/db.rs
+++ b/crates/ra_hir/src/db.rs
@@ -7,10 +7,10 @@ use crate::{
7 DefLoc, DefId, MacroCallLoc, MacroCallId, Name, HirFileId, 7 DefLoc, DefId, MacroCallLoc, MacroCallId, Name, HirFileId,
8 SourceFileItems, SourceItemId, 8 SourceFileItems, SourceItemId,
9 query_definitions, 9 query_definitions,
10 FnScopes, 10 FnSignature, FnScopes,
11 macros::MacroExpansion, 11 macros::MacroExpansion,
12 module::{ModuleId, ModuleTree, ModuleSource, 12 module_tree::{ModuleId, ModuleTree},
13 nameres::{ItemMap, InputModuleItems}}, 13 nameres::{ItemMap, InputModuleItems},
14 ty::{InferenceResult, Ty}, 14 ty::{InferenceResult, Ty},
15 adt::{StructData, EnumData}, 15 adt::{StructData, EnumData},
16 impl_block::ModuleImplBlocks, 16 impl_block::ModuleImplBlocks,
@@ -31,7 +31,7 @@ pub trait HirDatabase: SyntaxDatabase
31 use fn crate::macros::expand_macro_invocation; 31 use fn crate::macros::expand_macro_invocation;
32 } 32 }
33 33
34 fn fn_scopes(def_id: DefId) -> Arc<FnScopes> { 34 fn fn_scopes(def_id: DefId) -> Cancelable<Arc<FnScopes>> {
35 type FnScopesQuery; 35 type FnScopesQuery;
36 use fn query_definitions::fn_scopes; 36 use fn query_definitions::fn_scopes;
37 } 37 }
@@ -71,9 +71,9 @@ pub trait HirDatabase: SyntaxDatabase
71 use fn query_definitions::file_item; 71 use fn query_definitions::file_item;
72 } 72 }
73 73
74 fn submodules(source: ModuleSource) -> Cancelable<Arc<Vec<crate::module::imp::Submodule>>> { 74 fn submodules(source: SourceItemId) -> Cancelable<Arc<Vec<crate::module_tree::Submodule>>> {
75 type SubmodulesQuery; 75 type SubmodulesQuery;
76 use fn query_definitions::submodules; 76 use fn crate::module_tree::Submodule::submodules_query;
77 } 77 }
78 78
79 fn input_module_items(source_root_id: SourceRootId, module_id: ModuleId) -> Cancelable<Arc<InputModuleItems>> { 79 fn input_module_items(source_root_id: SourceRootId, module_id: ModuleId) -> Cancelable<Arc<InputModuleItems>> {
@@ -86,13 +86,28 @@ pub trait HirDatabase: SyntaxDatabase
86 } 86 }
87 fn module_tree(source_root_id: SourceRootId) -> Cancelable<Arc<ModuleTree>> { 87 fn module_tree(source_root_id: SourceRootId) -> Cancelable<Arc<ModuleTree>> {
88 type ModuleTreeQuery; 88 type ModuleTreeQuery;
89 use fn crate::module::imp::module_tree; 89 use fn crate::module_tree::ModuleTree::module_tree_query;
90 } 90 }
91 91
92 fn impls_in_module(source_root_id: SourceRootId, module_id: ModuleId) -> Cancelable<Arc<ModuleImplBlocks>> { 92 fn impls_in_module(source_root_id: SourceRootId, module_id: ModuleId) -> Cancelable<Arc<ModuleImplBlocks>> {
93 type ImplsInModuleQuery; 93 type ImplsInModuleQuery;
94 use fn crate::impl_block::impls_in_module; 94 use fn crate::impl_block::impls_in_module;
95 } 95 }
96
97 fn body_hir(def_id: DefId) -> Cancelable<Arc<crate::expr::Body>> {
98 type BodyHirQuery;
99 use fn crate::expr::body_hir;
100 }
101
102 fn body_syntax_mapping(def_id: DefId) -> Cancelable<Arc<crate::expr::BodySyntaxMapping>> {
103 type BodySyntaxMappingQuery;
104 use fn crate::expr::body_syntax_mapping;
105 }
106
107 fn fn_signature(def_id: DefId) -> Arc<FnSignature> {
108 type FnSignatureQuery;
109 use fn crate::function::fn_signature;
110 }
96} 111}
97 112
98} 113}
diff --git a/crates/ra_hir/src/expr.rs b/crates/ra_hir/src/expr.rs
new file mode 100644
index 000000000..b0063cad2
--- /dev/null
+++ b/crates/ra_hir/src/expr.rs
@@ -0,0 +1,770 @@
1use std::ops::Index;
2use std::sync::Arc;
3
4use rustc_hash::FxHashMap;
5
6use ra_arena::{Arena, RawId, impl_arena_id, map::ArenaMap};
7use ra_db::{LocalSyntaxPtr, Cancelable};
8use ra_syntax::ast::{self, AstNode, LoopBodyOwner, ArgListOwner, NameOwner};
9
10use crate::{Path, type_ref::{Mutability, TypeRef}, Name, HirDatabase, DefId, Def, name::AsName};
11
12#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
13pub struct ExprId(RawId);
14impl_arena_id!(ExprId);
15
16/// The body of an item (function, const etc.).
17#[derive(Debug, Eq, PartialEq)]
18pub struct Body {
19 exprs: Arena<ExprId, Expr>,
20 pats: Arena<PatId, Pat>,
21 /// The patterns for the function's arguments. While the argument types are
22 /// part of the function signature, the patterns are not (they don't change
23 /// the external type of the function).
24 ///
25 /// If this `Body` is for the body of a constant, this will just be
26 /// empty.
27 args: Vec<PatId>,
28 /// The `ExprId` of the actual body expression.
29 body_expr: ExprId,
30}
31
32/// An item body together with the mapping from syntax nodes to HIR expression
33/// IDs. This is needed to go from e.g. a position in a file to the HIR
34/// expression containing it; but for type inference etc., we want to operate on
35/// a structure that is agnostic to the actual positions of expressions in the
36/// file, so that we don't recompute the type inference whenever some whitespace
37/// is typed.
38#[derive(Debug, Eq, PartialEq)]
39pub struct BodySyntaxMapping {
40 body: Arc<Body>,
41 expr_syntax_mapping: FxHashMap<LocalSyntaxPtr, ExprId>,
42 expr_syntax_mapping_back: ArenaMap<ExprId, LocalSyntaxPtr>,
43 pat_syntax_mapping: FxHashMap<LocalSyntaxPtr, PatId>,
44 pat_syntax_mapping_back: ArenaMap<PatId, LocalSyntaxPtr>,
45}
46
47impl Body {
48 pub fn args(&self) -> &[PatId] {
49 &self.args
50 }
51
52 pub fn body_expr(&self) -> ExprId {
53 self.body_expr
54 }
55}
56
57impl Index<ExprId> for Body {
58 type Output = Expr;
59
60 fn index(&self, expr: ExprId) -> &Expr {
61 &self.exprs[expr]
62 }
63}
64
65impl Index<PatId> for Body {
66 type Output = Pat;
67
68 fn index(&self, pat: PatId) -> &Pat {
69 &self.pats[pat]
70 }
71}
72
73impl BodySyntaxMapping {
74 pub fn expr_syntax(&self, expr: ExprId) -> Option<LocalSyntaxPtr> {
75 self.expr_syntax_mapping_back.get(expr).cloned()
76 }
77 pub fn syntax_expr(&self, ptr: LocalSyntaxPtr) -> Option<ExprId> {
78 self.expr_syntax_mapping.get(&ptr).cloned()
79 }
80 pub fn node_expr(&self, node: ast::Expr) -> Option<ExprId> {
81 self.expr_syntax_mapping
82 .get(&LocalSyntaxPtr::new(node.syntax()))
83 .cloned()
84 }
85 pub fn pat_syntax(&self, pat: PatId) -> Option<LocalSyntaxPtr> {
86 self.pat_syntax_mapping_back.get(pat).cloned()
87 }
88 pub fn syntax_pat(&self, ptr: LocalSyntaxPtr) -> Option<PatId> {
89 self.pat_syntax_mapping.get(&ptr).cloned()
90 }
91 pub fn node_pat(&self, node: ast::Pat) -> Option<PatId> {
92 self.pat_syntax_mapping
93 .get(&LocalSyntaxPtr::new(node.syntax()))
94 .cloned()
95 }
96
97 pub fn body(&self) -> &Arc<Body> {
98 &self.body
99 }
100}
101
102#[derive(Debug, Clone, Eq, PartialEq)]
103pub enum Expr {
104 /// This is produced if syntax tree does not have a required expression piece.
105 Missing,
106 Path(Path),
107 If {
108 condition: ExprId,
109 then_branch: ExprId,
110 else_branch: Option<ExprId>,
111 },
112 Block {
113 statements: Vec<Statement>,
114 tail: Option<ExprId>,
115 },
116 Loop {
117 body: ExprId,
118 },
119 While {
120 condition: ExprId,
121 body: ExprId,
122 },
123 For {
124 iterable: ExprId,
125 pat: PatId,
126 body: ExprId,
127 },
128 Call {
129 callee: ExprId,
130 args: Vec<ExprId>,
131 },
132 MethodCall {
133 receiver: ExprId,
134 method_name: Name,
135 args: Vec<ExprId>,
136 },
137 Match {
138 expr: ExprId,
139 arms: Vec<MatchArm>,
140 },
141 Continue,
142 Break {
143 expr: Option<ExprId>,
144 },
145 Return {
146 expr: Option<ExprId>,
147 },
148 StructLit {
149 path: Option<Path>,
150 fields: Vec<StructLitField>,
151 spread: Option<ExprId>,
152 },
153 Field {
154 expr: ExprId,
155 name: Name,
156 },
157 Try {
158 expr: ExprId,
159 },
160 Cast {
161 expr: ExprId,
162 type_ref: TypeRef,
163 },
164 Ref {
165 expr: ExprId,
166 mutability: Mutability,
167 },
168 UnaryOp {
169 expr: ExprId,
170 op: Option<UnaryOp>,
171 },
172 BinaryOp {
173 lhs: ExprId,
174 rhs: ExprId,
175 op: Option<BinaryOp>,
176 },
177 Lambda {
178 args: Vec<PatId>,
179 arg_types: Vec<Option<TypeRef>>,
180 body: ExprId,
181 },
182}
183
184pub use ra_syntax::ast::PrefixOp as UnaryOp;
185pub use ra_syntax::ast::BinOp as BinaryOp;
186
187#[derive(Debug, Clone, Eq, PartialEq)]
188pub struct MatchArm {
189 pub pats: Vec<PatId>,
190 // guard: Option<ExprId>, // TODO
191 pub expr: ExprId,
192}
193
194#[derive(Debug, Clone, Eq, PartialEq)]
195pub struct StructLitField {
196 pub name: Name,
197 pub expr: ExprId,
198}
199
200#[derive(Debug, Clone, Eq, PartialEq)]
201pub enum Statement {
202 Let {
203 pat: PatId,
204 type_ref: Option<TypeRef>,
205 initializer: Option<ExprId>,
206 },
207 Expr(ExprId),
208}
209
210impl Expr {
211 pub fn walk_child_exprs(&self, mut f: impl FnMut(ExprId)) {
212 match self {
213 Expr::Missing => {}
214 Expr::Path(_) => {}
215 Expr::If {
216 condition,
217 then_branch,
218 else_branch,
219 } => {
220 f(*condition);
221 f(*then_branch);
222 if let Some(else_branch) = else_branch {
223 f(*else_branch);
224 }
225 }
226 Expr::Block { statements, tail } => {
227 for stmt in statements {
228 match stmt {
229 Statement::Let { initializer, .. } => {
230 if let Some(expr) = initializer {
231 f(*expr);
232 }
233 }
234 Statement::Expr(e) => f(*e),
235 }
236 }
237 if let Some(expr) = tail {
238 f(*expr);
239 }
240 }
241 Expr::Loop { body } => f(*body),
242 Expr::While { condition, body } => {
243 f(*condition);
244 f(*body);
245 }
246 Expr::For { iterable, body, .. } => {
247 f(*iterable);
248 f(*body);
249 }
250 Expr::Call { callee, args } => {
251 f(*callee);
252 for arg in args {
253 f(*arg);
254 }
255 }
256 Expr::MethodCall { receiver, args, .. } => {
257 f(*receiver);
258 for arg in args {
259 f(*arg);
260 }
261 }
262 Expr::Match { expr, arms } => {
263 f(*expr);
264 for arm in arms {
265 f(arm.expr);
266 }
267 }
268 Expr::Continue => {}
269 Expr::Break { expr } | Expr::Return { expr } => {
270 if let Some(expr) = expr {
271 f(*expr);
272 }
273 }
274 Expr::StructLit { fields, spread, .. } => {
275 for field in fields {
276 f(field.expr);
277 }
278 if let Some(expr) = spread {
279 f(*expr);
280 }
281 }
282 Expr::Lambda { body, .. } => {
283 f(*body);
284 }
285 Expr::BinaryOp { lhs, rhs, .. } => {
286 f(*lhs);
287 f(*rhs);
288 }
289 Expr::Field { expr, .. }
290 | Expr::Try { expr }
291 | Expr::Cast { expr, .. }
292 | Expr::Ref { expr, .. }
293 | Expr::UnaryOp { expr, .. } => {
294 f(*expr);
295 }
296 }
297 }
298}
299
300#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
301pub struct PatId(RawId);
302impl_arena_id!(PatId);
303
304#[derive(Debug, Clone, Eq, PartialEq)]
305pub enum Pat {
306 Missing,
307 Bind {
308 name: Name,
309 },
310 TupleStruct {
311 path: Option<Path>,
312 args: Vec<PatId>,
313 },
314}
315
316impl Pat {
317 pub fn walk_child_pats(&self, f: impl FnMut(PatId)) {
318 match self {
319 Pat::Missing | Pat::Bind { .. } => {}
320 Pat::TupleStruct { args, .. } => {
321 args.iter().map(|pat| *pat).for_each(f);
322 }
323 }
324 }
325}
326
327// Queries
328
329pub(crate) fn body_hir(db: &impl HirDatabase, def_id: DefId) -> Cancelable<Arc<Body>> {
330 Ok(Arc::clone(&body_syntax_mapping(db, def_id)?.body))
331}
332
333struct ExprCollector {
334 exprs: Arena<ExprId, Expr>,
335 pats: Arena<PatId, Pat>,
336 expr_syntax_mapping: FxHashMap<LocalSyntaxPtr, ExprId>,
337 expr_syntax_mapping_back: ArenaMap<ExprId, LocalSyntaxPtr>,
338 pat_syntax_mapping: FxHashMap<LocalSyntaxPtr, PatId>,
339 pat_syntax_mapping_back: ArenaMap<PatId, LocalSyntaxPtr>,
340}
341
342impl ExprCollector {
343 fn new() -> Self {
344 ExprCollector {
345 exprs: Arena::default(),
346 pats: Arena::default(),
347 expr_syntax_mapping: FxHashMap::default(),
348 expr_syntax_mapping_back: ArenaMap::default(),
349 pat_syntax_mapping: FxHashMap::default(),
350 pat_syntax_mapping_back: ArenaMap::default(),
351 }
352 }
353
354 fn alloc_expr(&mut self, expr: Expr, syntax_ptr: LocalSyntaxPtr) -> ExprId {
355 let id = self.exprs.alloc(expr);
356 self.expr_syntax_mapping.insert(syntax_ptr, id);
357 self.expr_syntax_mapping_back.insert(id, syntax_ptr);
358 id
359 }
360
361 fn alloc_pat(&mut self, pat: Pat, syntax_ptr: LocalSyntaxPtr) -> PatId {
362 let id = self.pats.alloc(pat);
363 self.pat_syntax_mapping.insert(syntax_ptr, id);
364 self.pat_syntax_mapping_back.insert(id, syntax_ptr);
365 id
366 }
367
368 fn empty_block(&mut self) -> ExprId {
369 let block = Expr::Block {
370 statements: Vec::new(),
371 tail: None,
372 };
373 self.exprs.alloc(block)
374 }
375
376 fn collect_expr(&mut self, expr: ast::Expr) -> ExprId {
377 let syntax_ptr = LocalSyntaxPtr::new(expr.syntax());
378 match expr {
379 ast::Expr::IfExpr(e) => {
380 if let Some(pat) = e.condition().and_then(|c| c.pat()) {
381 // if let -- desugar to match
382 let pat = self.collect_pat(pat);
383 let match_expr =
384 self.collect_expr_opt(e.condition().expect("checked above").expr());
385 let then_branch = self.collect_block_opt(e.then_branch());
386 let else_branch = e
387 .else_branch()
388 .map(|e| self.collect_block(e))
389 .unwrap_or_else(|| self.empty_block());
390 let placeholder_pat = self.pats.alloc(Pat::Missing);
391 let arms = vec![
392 MatchArm {
393 pats: vec![pat],
394 expr: then_branch,
395 },
396 MatchArm {
397 pats: vec![placeholder_pat],
398 expr: else_branch,
399 },
400 ];
401 self.alloc_expr(
402 Expr::Match {
403 expr: match_expr,
404 arms,
405 },
406 syntax_ptr,
407 )
408 } else {
409 let condition = self.collect_expr_opt(e.condition().and_then(|c| c.expr()));
410 let then_branch = self.collect_block_opt(e.then_branch());
411 let else_branch = e.else_branch().map(|e| self.collect_block(e));
412 self.alloc_expr(
413 Expr::If {
414 condition,
415 then_branch,
416 else_branch,
417 },
418 syntax_ptr,
419 )
420 }
421 }
422 ast::Expr::BlockExpr(e) => self.collect_block_opt(e.block()),
423 ast::Expr::LoopExpr(e) => {
424 let body = self.collect_block_opt(e.loop_body());
425 self.alloc_expr(Expr::Loop { body }, syntax_ptr)
426 }
427 ast::Expr::WhileExpr(e) => {
428 let condition = if let Some(condition) = e.condition() {
429 if condition.pat().is_none() {
430 self.collect_expr_opt(condition.expr())
431 } else {
432 // TODO handle while let
433 return self.alloc_expr(Expr::Missing, syntax_ptr);
434 }
435 } else {
436 self.exprs.alloc(Expr::Missing)
437 };
438 let body = self.collect_block_opt(e.loop_body());
439 self.alloc_expr(Expr::While { condition, body }, syntax_ptr)
440 }
441 ast::Expr::ForExpr(e) => {
442 let iterable = self.collect_expr_opt(e.iterable());
443 let pat = self.collect_pat_opt(e.pat());
444 let body = self.collect_block_opt(e.loop_body());
445 self.alloc_expr(
446 Expr::For {
447 iterable,
448 pat,
449 body,
450 },
451 syntax_ptr,
452 )
453 }
454 ast::Expr::CallExpr(e) => {
455 let callee = self.collect_expr_opt(e.expr());
456 let args = if let Some(arg_list) = e.arg_list() {
457 arg_list.args().map(|e| self.collect_expr(e)).collect()
458 } else {
459 Vec::new()
460 };
461 self.alloc_expr(Expr::Call { callee, args }, syntax_ptr)
462 }
463 ast::Expr::MethodCallExpr(e) => {
464 let receiver = self.collect_expr_opt(e.expr());
465 let args = if let Some(arg_list) = e.arg_list() {
466 arg_list.args().map(|e| self.collect_expr(e)).collect()
467 } else {
468 Vec::new()
469 };
470 let method_name = e
471 .name_ref()
472 .map(|nr| nr.as_name())
473 .unwrap_or_else(Name::missing);
474 self.alloc_expr(
475 Expr::MethodCall {
476 receiver,
477 method_name,
478 args,
479 },
480 syntax_ptr,
481 )
482 }
483 ast::Expr::MatchExpr(e) => {
484 let expr = self.collect_expr_opt(e.expr());
485 let arms = if let Some(match_arm_list) = e.match_arm_list() {
486 match_arm_list
487 .arms()
488 .map(|arm| MatchArm {
489 pats: arm.pats().map(|p| self.collect_pat(p)).collect(),
490 expr: self.collect_expr_opt(arm.expr()),
491 })
492 .collect()
493 } else {
494 Vec::new()
495 };
496 self.alloc_expr(Expr::Match { expr, arms }, syntax_ptr)
497 }
498 ast::Expr::PathExpr(e) => {
499 let path = e
500 .path()
501 .and_then(Path::from_ast)
502 .map(Expr::Path)
503 .unwrap_or(Expr::Missing);
504 self.alloc_expr(path, syntax_ptr)
505 }
506 ast::Expr::ContinueExpr(_e) => {
507 // TODO: labels
508 self.alloc_expr(Expr::Continue, syntax_ptr)
509 }
510 ast::Expr::BreakExpr(e) => {
511 let expr = e.expr().map(|e| self.collect_expr(e));
512 self.alloc_expr(Expr::Break { expr }, syntax_ptr)
513 }
514 ast::Expr::ParenExpr(e) => {
515 let inner = self.collect_expr_opt(e.expr());
516 // make the paren expr point to the inner expression as well
517 self.expr_syntax_mapping.insert(syntax_ptr, inner);
518 inner
519 }
520 ast::Expr::ReturnExpr(e) => {
521 let expr = e.expr().map(|e| self.collect_expr(e));
522 self.alloc_expr(Expr::Return { expr }, syntax_ptr)
523 }
524 ast::Expr::StructLit(e) => {
525 let path = e.path().and_then(Path::from_ast);
526 let fields = if let Some(nfl) = e.named_field_list() {
527 nfl.fields()
528 .map(|field| StructLitField {
529 name: field
530 .name_ref()
531 .map(|nr| nr.as_name())
532 .unwrap_or_else(Name::missing),
533 expr: if let Some(e) = field.expr() {
534 self.collect_expr(e)
535 } else if let Some(nr) = field.name_ref() {
536 // field shorthand
537 let id = self.exprs.alloc(Expr::Path(Path::from_name_ref(nr)));
538 self.expr_syntax_mapping
539 .insert(LocalSyntaxPtr::new(nr.syntax()), id);
540 self.expr_syntax_mapping_back
541 .insert(id, LocalSyntaxPtr::new(nr.syntax()));
542 id
543 } else {
544 self.exprs.alloc(Expr::Missing)
545 },
546 })
547 .collect()
548 } else {
549 Vec::new()
550 };
551 let spread = e.spread().map(|s| self.collect_expr(s));
552 self.alloc_expr(
553 Expr::StructLit {
554 path,
555 fields,
556 spread,
557 },
558 syntax_ptr,
559 )
560 }
561 ast::Expr::FieldExpr(e) => {
562 let expr = self.collect_expr_opt(e.expr());
563 let name = e
564 .name_ref()
565 .map(|nr| nr.as_name())
566 .unwrap_or_else(Name::missing);
567 self.alloc_expr(Expr::Field { expr, name }, syntax_ptr)
568 }
569 ast::Expr::TryExpr(e) => {
570 let expr = self.collect_expr_opt(e.expr());
571 self.alloc_expr(Expr::Try { expr }, syntax_ptr)
572 }
573 ast::Expr::CastExpr(e) => {
574 let expr = self.collect_expr_opt(e.expr());
575 let type_ref = TypeRef::from_ast_opt(e.type_ref());
576 self.alloc_expr(Expr::Cast { expr, type_ref }, syntax_ptr)
577 }
578 ast::Expr::RefExpr(e) => {
579 let expr = self.collect_expr_opt(e.expr());
580 let mutability = Mutability::from_mutable(e.is_mut());
581 self.alloc_expr(Expr::Ref { expr, mutability }, syntax_ptr)
582 }
583 ast::Expr::PrefixExpr(e) => {
584 let expr = self.collect_expr_opt(e.expr());
585 let op = e.op();
586 self.alloc_expr(Expr::UnaryOp { expr, op }, syntax_ptr)
587 }
588 ast::Expr::LambdaExpr(e) => {
589 let mut args = Vec::new();
590 let mut arg_types = Vec::new();
591 if let Some(pl) = e.param_list() {
592 for param in pl.params() {
593 let pat = self.collect_pat_opt(param.pat());
594 let type_ref = param.type_ref().map(TypeRef::from_ast);
595 args.push(pat);
596 arg_types.push(type_ref);
597 }
598 }
599 let body = self.collect_expr_opt(e.body());
600 self.alloc_expr(
601 Expr::Lambda {
602 args,
603 arg_types,
604 body,
605 },
606 syntax_ptr,
607 )
608 }
609 ast::Expr::BinExpr(e) => {
610 let lhs = self.collect_expr_opt(e.lhs());
611 let rhs = self.collect_expr_opt(e.rhs());
612 let op = e.op();
613 self.alloc_expr(Expr::BinaryOp { lhs, rhs, op }, syntax_ptr)
614 }
615
616 // TODO implement HIR for these:
617 ast::Expr::Label(_e) => self.alloc_expr(Expr::Missing, syntax_ptr),
618 ast::Expr::IndexExpr(_e) => self.alloc_expr(Expr::Missing, syntax_ptr),
619 ast::Expr::TupleExpr(_e) => self.alloc_expr(Expr::Missing, syntax_ptr),
620 ast::Expr::ArrayExpr(_e) => self.alloc_expr(Expr::Missing, syntax_ptr),
621 ast::Expr::RangeExpr(_e) => self.alloc_expr(Expr::Missing, syntax_ptr),
622 ast::Expr::Literal(_e) => self.alloc_expr(Expr::Missing, syntax_ptr),
623 }
624 }
625
626 fn collect_expr_opt(&mut self, expr: Option<ast::Expr>) -> ExprId {
627 if let Some(expr) = expr {
628 self.collect_expr(expr)
629 } else {
630 self.exprs.alloc(Expr::Missing)
631 }
632 }
633
634 fn collect_block(&mut self, block: ast::Block) -> ExprId {
635 let statements = block
636 .statements()
637 .map(|s| match s {
638 ast::Stmt::LetStmt(stmt) => {
639 let pat = self.collect_pat_opt(stmt.pat());
640 let type_ref = stmt.type_ref().map(TypeRef::from_ast);
641 let initializer = stmt.initializer().map(|e| self.collect_expr(e));
642 Statement::Let {
643 pat,
644 type_ref,
645 initializer,
646 }
647 }
648 ast::Stmt::ExprStmt(stmt) => Statement::Expr(self.collect_expr_opt(stmt.expr())),
649 })
650 .collect();
651 let tail = block.expr().map(|e| self.collect_expr(e));
652 self.alloc_expr(
653 Expr::Block { statements, tail },
654 LocalSyntaxPtr::new(block.syntax()),
655 )
656 }
657
658 fn collect_block_opt(&mut self, block: Option<ast::Block>) -> ExprId {
659 if let Some(block) = block {
660 self.collect_block(block)
661 } else {
662 self.exprs.alloc(Expr::Missing)
663 }
664 }
665
666 fn collect_pat(&mut self, pat: ast::Pat) -> PatId {
667 let syntax_ptr = LocalSyntaxPtr::new(pat.syntax());
668 match pat {
669 ast::Pat::BindPat(bp) => {
670 let name = bp
671 .name()
672 .map(|nr| nr.as_name())
673 .unwrap_or_else(Name::missing);
674 self.alloc_pat(Pat::Bind { name }, syntax_ptr)
675 }
676 ast::Pat::TupleStructPat(p) => {
677 let path = p.path().and_then(Path::from_ast);
678 let args = p.args().map(|p| self.collect_pat(p)).collect();
679 self.alloc_pat(Pat::TupleStruct { path, args }, syntax_ptr)
680 }
681 _ => {
682 // TODO
683 self.alloc_pat(Pat::Missing, syntax_ptr)
684 }
685 }
686 }
687
688 fn collect_pat_opt(&mut self, pat: Option<ast::Pat>) -> PatId {
689 if let Some(pat) = pat {
690 self.collect_pat(pat)
691 } else {
692 self.pats.alloc(Pat::Missing)
693 }
694 }
695
696 fn into_body_syntax_mapping(self, args: Vec<PatId>, body_expr: ExprId) -> BodySyntaxMapping {
697 let body = Body {
698 exprs: self.exprs,
699 pats: self.pats,
700 args,
701 body_expr,
702 };
703 BodySyntaxMapping {
704 body: Arc::new(body),
705 expr_syntax_mapping: self.expr_syntax_mapping,
706 expr_syntax_mapping_back: self.expr_syntax_mapping_back,
707 pat_syntax_mapping: self.pat_syntax_mapping,
708 pat_syntax_mapping_back: self.pat_syntax_mapping_back,
709 }
710 }
711}
712
713pub(crate) fn collect_fn_body_syntax(node: ast::FnDef) -> BodySyntaxMapping {
714 let mut collector = ExprCollector::new();
715
716 let args = if let Some(param_list) = node.param_list() {
717 let mut args = Vec::new();
718
719 if let Some(self_param) = param_list.self_param() {
720 let self_param = LocalSyntaxPtr::new(
721 self_param
722 .self_kw()
723 .expect("self param without self keyword")
724 .syntax(),
725 );
726 let arg = collector.alloc_pat(
727 Pat::Bind {
728 name: Name::self_param(),
729 },
730 self_param,
731 );
732 args.push(arg);
733 }
734
735 for param in param_list.params() {
736 let pat = if let Some(pat) = param.pat() {
737 pat
738 } else {
739 continue;
740 };
741 args.push(collector.collect_pat(pat));
742 }
743 args
744 } else {
745 Vec::new()
746 };
747
748 let body = collector.collect_block_opt(node.body());
749 collector.into_body_syntax_mapping(args, body)
750}
751
752pub(crate) fn body_syntax_mapping(
753 db: &impl HirDatabase,
754 def_id: DefId,
755) -> Cancelable<Arc<BodySyntaxMapping>> {
756 let def = def_id.resolve(db)?;
757
758 let body_syntax_mapping = match def {
759 Def::Function(f) => {
760 let node = f.syntax(db);
761 let node = node.borrowed();
762
763 collect_fn_body_syntax(node)
764 }
765 // TODO: consts, etc.
766 _ => panic!("Trying to get body for item type without body"),
767 };
768
769 Ok(Arc::new(body_syntax_mapping))
770}
diff --git a/crates/ra_hir/src/function.rs b/crates/ra_hir/src/function.rs
index 75ef308ae..4627be071 100644
--- a/crates/ra_hir/src/function.rs
+++ b/crates/ra_hir/src/function.rs
@@ -11,9 +11,9 @@ use ra_syntax::{
11 ast::{self, AstNode, DocCommentsOwner, NameOwner}, 11 ast::{self, AstNode, DocCommentsOwner, NameOwner},
12}; 12};
13 13
14use crate::{DefId, DefKind, HirDatabase, ty::InferenceResult, Module, Crate, impl_block::ImplBlock}; 14use crate::{DefId, DefKind, HirDatabase, ty::InferenceResult, Module, Crate, impl_block::ImplBlock, expr::{Body, BodySyntaxMapping}, type_ref::{TypeRef, Mutability}, Name};
15 15
16pub use self::scope::FnScopes; 16pub use self::scope::{FnScopes, ScopesWithSyntaxMapping};
17 17
18#[derive(Debug, Clone, PartialEq, Eq)] 18#[derive(Debug, Clone, PartialEq, Eq)]
19pub struct Function { 19pub struct Function {
@@ -36,8 +36,25 @@ impl Function {
36 ast::FnDef::cast(syntax.borrowed()).unwrap().owned() 36 ast::FnDef::cast(syntax.borrowed()).unwrap().owned()
37 } 37 }
38 38
39 pub fn scopes(&self, db: &impl HirDatabase) -> Arc<FnScopes> { 39 pub fn body(&self, db: &impl HirDatabase) -> Cancelable<Arc<Body>> {
40 db.fn_scopes(self.def_id) 40 db.body_hir(self.def_id)
41 }
42
43 pub fn body_syntax_mapping(&self, db: &impl HirDatabase) -> Cancelable<Arc<BodySyntaxMapping>> {
44 db.body_syntax_mapping(self.def_id)
45 }
46
47 pub fn scopes(&self, db: &impl HirDatabase) -> Cancelable<ScopesWithSyntaxMapping> {
48 let scopes = db.fn_scopes(self.def_id)?;
49 let syntax_mapping = db.body_syntax_mapping(self.def_id)?;
50 Ok(ScopesWithSyntaxMapping {
51 scopes,
52 syntax_mapping,
53 })
54 }
55
56 pub fn signature(&self, db: &impl HirDatabase) -> Arc<FnSignature> {
57 db.fn_signature(self.def_id)
41 } 58 }
42 59
43 pub fn signature_info(&self, db: &impl HirDatabase) -> Option<FnSignatureInfo> { 60 pub fn signature_info(&self, db: &impl HirDatabase) -> Option<FnSignatureInfo> {
@@ -63,6 +80,60 @@ impl Function {
63 } 80 }
64} 81}
65 82
83/// The declared signature of a function.
84#[derive(Debug, Clone, PartialEq, Eq)]
85pub struct FnSignature {
86 args: Vec<TypeRef>,
87 ret_type: TypeRef,
88}
89
90impl FnSignature {
91 pub fn args(&self) -> &[TypeRef] {
92 &self.args
93 }
94
95 pub fn ret_type(&self) -> &TypeRef {
96 &self.ret_type
97 }
98}
99
100pub(crate) fn fn_signature(db: &impl HirDatabase, def_id: DefId) -> Arc<FnSignature> {
101 let func = Function::new(def_id);
102 let syntax = func.syntax(db);
103 let node = syntax.borrowed();
104 let mut args = Vec::new();
105 if let Some(param_list) = node.param_list() {
106 if let Some(self_param) = param_list.self_param() {
107 let self_type = if let Some(type_ref) = self_param.type_ref() {
108 TypeRef::from_ast(type_ref)
109 } else {
110 let self_type = TypeRef::Path(Name::self_type().into());
111 match self_param.flavor() {
112 ast::SelfParamFlavor::Owned => self_type,
113 ast::SelfParamFlavor::Ref => {
114 TypeRef::Reference(Box::new(self_type), Mutability::Shared)
115 }
116 ast::SelfParamFlavor::MutRef => {
117 TypeRef::Reference(Box::new(self_type), Mutability::Mut)
118 }
119 }
120 };
121 args.push(self_type);
122 }
123 for param in param_list.params() {
124 let type_ref = TypeRef::from_ast_opt(param.type_ref());
125 args.push(type_ref);
126 }
127 }
128 let ret_type = if let Some(type_ref) = node.ret_type().and_then(|rt| rt.type_ref()) {
129 TypeRef::from_ast(type_ref)
130 } else {
131 TypeRef::unit()
132 };
133 let sig = FnSignature { args, ret_type };
134 Arc::new(sig)
135}
136
66#[derive(Debug, Clone)] 137#[derive(Debug, Clone)]
67pub struct FnSignatureInfo { 138pub struct FnSignatureInfo {
68 pub name: String, 139 pub name: String,
diff --git a/crates/ra_hir/src/function/scope.rs b/crates/ra_hir/src/function/scope.rs
index 42bfe4f32..0a12f0b35 100644
--- a/crates/ra_hir/src/function/scope.rs
+++ b/crates/ra_hir/src/function/scope.rs
@@ -1,14 +1,16 @@
1use std::sync::Arc;
2
1use rustc_hash::{FxHashMap, FxHashSet}; 3use rustc_hash::{FxHashMap, FxHashSet};
2 4
3use ra_syntax::{ 5use ra_syntax::{
4 AstNode, SyntaxNodeRef, TextUnit, TextRange, 6 AstNode, SyntaxNodeRef, TextUnit, TextRange,
5 algo::generate, 7 algo::generate,
6 ast::{self, ArgListOwner, LoopBodyOwner, NameOwner}, 8 ast,
7}; 9};
8use ra_arena::{Arena, RawId, impl_arena_id}; 10use ra_arena::{Arena, RawId, impl_arena_id};
9use ra_db::LocalSyntaxPtr; 11use ra_db::LocalSyntaxPtr;
10 12
11use crate::{Name, AsName}; 13use crate::{Name, AsName, expr::{PatId, ExprId, Pat, Expr, Body, Statement, BodySyntaxMapping}};
12 14
13#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] 15#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
14pub struct ScopeId(RawId); 16pub struct ScopeId(RawId);
@@ -16,15 +18,15 @@ impl_arena_id!(ScopeId);
16 18
17#[derive(Debug, PartialEq, Eq)] 19#[derive(Debug, PartialEq, Eq)]
18pub struct FnScopes { 20pub struct FnScopes {
19 pub self_param: Option<LocalSyntaxPtr>, 21 body: Arc<Body>,
20 scopes: Arena<ScopeId, ScopeData>, 22 scopes: Arena<ScopeId, ScopeData>,
21 scope_for: FxHashMap<LocalSyntaxPtr, ScopeId>, 23 scope_for: FxHashMap<ExprId, ScopeId>,
22} 24}
23 25
24#[derive(Debug, PartialEq, Eq)] 26#[derive(Debug, PartialEq, Eq)]
25pub struct ScopeEntry { 27pub struct ScopeEntry {
26 name: Name, 28 name: Name,
27 ptr: LocalSyntaxPtr, 29 pat: PatId,
28} 30}
29 31
30#[derive(Debug, PartialEq, Eq)] 32#[derive(Debug, PartialEq, Eq)]
@@ -34,28 +36,100 @@ pub struct ScopeData {
34} 36}
35 37
36impl FnScopes { 38impl FnScopes {
37 pub(crate) fn new(fn_def: ast::FnDef) -> FnScopes { 39 pub(crate) fn new(body: Arc<Body>) -> FnScopes {
38 let mut scopes = FnScopes { 40 let mut scopes = FnScopes {
39 self_param: fn_def 41 body: body.clone(),
40 .param_list()
41 .and_then(|it| it.self_param())
42 .map(|it| LocalSyntaxPtr::new(it.syntax())),
43 scopes: Arena::default(), 42 scopes: Arena::default(),
44 scope_for: FxHashMap::default(), 43 scope_for: FxHashMap::default(),
45 }; 44 };
46 let root = scopes.root_scope(); 45 let root = scopes.root_scope();
47 scopes.add_params_bindings(root, fn_def.param_list()); 46 scopes.add_params_bindings(root, body.args());
48 if let Some(body) = fn_def.body() { 47 compute_expr_scopes(body.body_expr(), &body, &mut scopes, root);
49 compute_block_scopes(body, &mut scopes, root)
50 }
51 scopes 48 scopes
52 } 49 }
53 pub fn entries(&self, scope: ScopeId) -> &[ScopeEntry] { 50 pub fn entries(&self, scope: ScopeId) -> &[ScopeEntry] {
54 &self.scopes[scope].entries 51 &self.scopes[scope].entries
55 } 52 }
53 pub fn scope_chain_for<'a>(&'a self, expr: ExprId) -> impl Iterator<Item = ScopeId> + 'a {
54 generate(self.scope_for(expr), move |&scope| {
55 self.scopes[scope].parent
56 })
57 }
58
59 pub fn resolve_local_name<'a>(
60 &'a self,
61 context_expr: ExprId,
62 name: Name,
63 ) -> Option<&'a ScopeEntry> {
64 let mut shadowed = FxHashSet::default();
65 let ret = self
66 .scope_chain_for(context_expr)
67 .flat_map(|scope| self.entries(scope).iter())
68 .filter(|entry| shadowed.insert(entry.name()))
69 .find(|entry| entry.name() == &name);
70 ret
71 }
72
73 fn root_scope(&mut self) -> ScopeId {
74 self.scopes.alloc(ScopeData {
75 parent: None,
76 entries: vec![],
77 })
78 }
79 fn new_scope(&mut self, parent: ScopeId) -> ScopeId {
80 self.scopes.alloc(ScopeData {
81 parent: Some(parent),
82 entries: vec![],
83 })
84 }
85 fn add_bindings(&mut self, body: &Body, scope: ScopeId, pat: PatId) {
86 match &body[pat] {
87 Pat::Bind { name } => self.scopes[scope].entries.push(ScopeEntry {
88 name: name.clone(),
89 pat,
90 }),
91 p => p.walk_child_pats(|pat| self.add_bindings(body, scope, pat)),
92 }
93 }
94 fn add_params_bindings(&mut self, scope: ScopeId, params: &[PatId]) {
95 let body = Arc::clone(&self.body);
96 params
97 .into_iter()
98 .for_each(|pat| self.add_bindings(&body, scope, *pat));
99 }
100 fn set_scope(&mut self, node: ExprId, scope: ScopeId) {
101 self.scope_for.insert(node, scope);
102 }
103 fn scope_for(&self, expr: ExprId) -> Option<ScopeId> {
104 self.scope_for.get(&expr).map(|&scope| scope)
105 }
106}
107
108#[derive(Debug, Clone, PartialEq, Eq)]
109pub struct ScopesWithSyntaxMapping {
110 pub syntax_mapping: Arc<BodySyntaxMapping>,
111 pub scopes: Arc<FnScopes>,
112}
113
114#[derive(Debug, Clone, PartialEq, Eq)]
115pub struct ScopeEntryWithSyntax {
116 name: Name,
117 ptr: LocalSyntaxPtr,
118}
119
120impl ScopeEntryWithSyntax {
121 pub fn name(&self) -> &Name {
122 &self.name
123 }
124 pub fn ptr(&self) -> LocalSyntaxPtr {
125 self.ptr
126 }
127}
128
129impl ScopesWithSyntaxMapping {
56 pub fn scope_chain<'a>(&'a self, node: SyntaxNodeRef) -> impl Iterator<Item = ScopeId> + 'a { 130 pub fn scope_chain<'a>(&'a self, node: SyntaxNodeRef) -> impl Iterator<Item = ScopeId> + 'a {
57 generate(self.scope_for(node), move |&scope| { 131 generate(self.scope_for(node), move |&scope| {
58 self.scopes[scope].parent 132 self.scopes.scopes[scope].parent
59 }) 133 })
60 } 134 }
61 pub fn scope_chain_for_offset<'a>( 135 pub fn scope_chain_for_offset<'a>(
@@ -63,26 +137,30 @@ impl FnScopes {
63 offset: TextUnit, 137 offset: TextUnit,
64 ) -> impl Iterator<Item = ScopeId> + 'a { 138 ) -> impl Iterator<Item = ScopeId> + 'a {
65 let scope = self 139 let scope = self
140 .scopes
66 .scope_for 141 .scope_for
67 .iter() 142 .iter()
68 // find containin scope 143 .filter_map(|(id, scope)| Some((self.syntax_mapping.expr_syntax(*id)?, scope)))
144 // find containing scope
69 .min_by_key(|(ptr, _scope)| { 145 .min_by_key(|(ptr, _scope)| {
70 ( 146 (
71 !(ptr.range().start() <= offset && offset <= ptr.range().end()), 147 !(ptr.range().start() <= offset && offset <= ptr.range().end()),
72 ptr.range().len(), 148 ptr.range().len(),
73 ) 149 )
74 }) 150 })
75 .map(|(ptr, scope)| self.adjust(*ptr, *scope, offset)); 151 .map(|(ptr, scope)| self.adjust(ptr, *scope, offset));
76 152
77 generate(scope, move |&scope| self.scopes[scope].parent) 153 generate(scope, move |&scope| self.scopes.scopes[scope].parent)
78 } 154 }
79 // XXX: during completion, cursor might be outside of any particular 155 // XXX: during completion, cursor might be outside of any particular
80 // expression. Try to figure out the correct scope... 156 // expression. Try to figure out the correct scope...
81 fn adjust(&self, ptr: LocalSyntaxPtr, original_scope: ScopeId, offset: TextUnit) -> ScopeId { 157 fn adjust(&self, ptr: LocalSyntaxPtr, original_scope: ScopeId, offset: TextUnit) -> ScopeId {
82 let r = ptr.range(); 158 let r = ptr.range();
83 let child_scopes = self 159 let child_scopes = self
160 .scopes
84 .scope_for 161 .scope_for
85 .iter() 162 .iter()
163 .filter_map(|(id, scope)| Some((self.syntax_mapping.expr_syntax(*id)?, scope)))
86 .map(|(ptr, scope)| (ptr.range(), scope)) 164 .map(|(ptr, scope)| (ptr.range(), scope))
87 .filter(|(range, _)| range.start() <= offset && range.is_subrange(&r) && *range != r); 165 .filter(|(range, _)| range.start() <= offset && range.is_subrange(&r) && *range != r);
88 166
@@ -100,22 +178,27 @@ impl FnScopes {
100 .unwrap_or(original_scope) 178 .unwrap_or(original_scope)
101 } 179 }
102 180
103 pub fn resolve_local_name<'a>(&'a self, name_ref: ast::NameRef) -> Option<&'a ScopeEntry> { 181 pub fn resolve_local_name(&self, name_ref: ast::NameRef) -> Option<ScopeEntryWithSyntax> {
104 let mut shadowed = FxHashSet::default(); 182 let mut shadowed = FxHashSet::default();
105 let name = name_ref.as_name(); 183 let name = name_ref.as_name();
106 let ret = self 184 let ret = self
107 .scope_chain(name_ref.syntax()) 185 .scope_chain(name_ref.syntax())
108 .flat_map(|scope| self.entries(scope).iter()) 186 .flat_map(|scope| self.scopes.entries(scope).iter())
109 .filter(|entry| shadowed.insert(entry.name())) 187 .filter(|entry| shadowed.insert(entry.name()))
110 .filter(|entry| entry.name() == &name) 188 .filter(|entry| entry.name() == &name)
111 .nth(0); 189 .nth(0);
112 ret 190 ret.and_then(|entry| {
191 Some(ScopeEntryWithSyntax {
192 name: entry.name().clone(),
193 ptr: self.syntax_mapping.pat_syntax(entry.pat())?,
194 })
195 })
113 } 196 }
114 197
115 pub fn find_all_refs(&self, pat: ast::BindPat) -> Vec<ReferenceDescriptor> { 198 pub fn find_all_refs(&self, pat: ast::BindPat) -> Vec<ReferenceDescriptor> {
116 let fn_def = pat.syntax().ancestors().find_map(ast::FnDef::cast).unwrap(); 199 let fn_def = pat.syntax().ancestors().find_map(ast::FnDef::cast).unwrap();
117 let name_ptr = LocalSyntaxPtr::new(pat.syntax()); 200 let name_ptr = LocalSyntaxPtr::new(pat.syntax());
118 let refs: Vec<_> = fn_def 201 fn_def
119 .syntax() 202 .syntax()
120 .descendants() 203 .descendants()
121 .filter_map(ast::NameRef::cast) 204 .filter_map(ast::NameRef::cast)
@@ -127,203 +210,94 @@ impl FnScopes {
127 name: name_ref.syntax().text().to_string(), 210 name: name_ref.syntax().text().to_string(),
128 range: name_ref.syntax().range(), 211 range: name_ref.syntax().range(),
129 }) 212 })
130 .collect(); 213 .collect()
131
132 refs
133 } 214 }
134 215
135 fn root_scope(&mut self) -> ScopeId {
136 self.scopes.alloc(ScopeData {
137 parent: None,
138 entries: vec![],
139 })
140 }
141 fn new_scope(&mut self, parent: ScopeId) -> ScopeId {
142 self.scopes.alloc(ScopeData {
143 parent: Some(parent),
144 entries: vec![],
145 })
146 }
147 fn add_bindings(&mut self, scope: ScopeId, pat: ast::Pat) {
148 let entries = pat
149 .syntax()
150 .descendants()
151 .filter_map(ast::BindPat::cast)
152 .filter_map(ScopeEntry::new);
153 self.scopes[scope].entries.extend(entries);
154 }
155 fn add_params_bindings(&mut self, scope: ScopeId, params: Option<ast::ParamList>) {
156 params
157 .into_iter()
158 .flat_map(|it| it.params())
159 .filter_map(|it| it.pat())
160 .for_each(|it| self.add_bindings(scope, it));
161 }
162 fn set_scope(&mut self, node: SyntaxNodeRef, scope: ScopeId) {
163 self.scope_for.insert(LocalSyntaxPtr::new(node), scope);
164 }
165 fn scope_for(&self, node: SyntaxNodeRef) -> Option<ScopeId> { 216 fn scope_for(&self, node: SyntaxNodeRef) -> Option<ScopeId> {
166 node.ancestors() 217 node.ancestors()
167 .map(LocalSyntaxPtr::new) 218 .map(LocalSyntaxPtr::new)
168 .filter_map(|it| self.scope_for.get(&it).map(|&scope| scope)) 219 .filter_map(|ptr| self.syntax_mapping.syntax_expr(ptr))
169 .next() 220 .find_map(|it| self.scopes.scope_for(it))
170 } 221 }
171} 222}
172 223
173impl ScopeEntry { 224impl ScopeEntry {
174 fn new(pat: ast::BindPat) -> Option<ScopeEntry> {
175 let name = pat.name()?.as_name();
176 let res = ScopeEntry {
177 name,
178 ptr: LocalSyntaxPtr::new(pat.syntax()),
179 };
180 Some(res)
181 }
182 pub fn name(&self) -> &Name { 225 pub fn name(&self) -> &Name {
183 &self.name 226 &self.name
184 } 227 }
185 pub fn ptr(&self) -> LocalSyntaxPtr { 228 pub fn pat(&self) -> PatId {
186 self.ptr 229 self.pat
187 } 230 }
188} 231}
189 232
190fn compute_block_scopes(block: ast::Block, scopes: &mut FnScopes, mut scope: ScopeId) { 233fn compute_block_scopes(
191 // A hack for completion :( 234 statements: &[Statement],
192 scopes.set_scope(block.syntax(), scope); 235 tail: Option<ExprId>,
193 for stmt in block.statements() { 236 body: &Body,
237 scopes: &mut FnScopes,
238 mut scope: ScopeId,
239) {
240 for stmt in statements {
194 match stmt { 241 match stmt {
195 ast::Stmt::LetStmt(stmt) => { 242 Statement::Let {
196 if let Some(expr) = stmt.initializer() { 243 pat, initializer, ..
197 scopes.set_scope(expr.syntax(), scope); 244 } => {
198 compute_expr_scopes(expr, scopes, scope); 245 if let Some(expr) = initializer {
246 scopes.set_scope(*expr, scope);
247 compute_expr_scopes(*expr, body, scopes, scope);
199 } 248 }
200 scope = scopes.new_scope(scope); 249 scope = scopes.new_scope(scope);
201 if let Some(pat) = stmt.pat() { 250 scopes.add_bindings(body, scope, *pat);
202 scopes.add_bindings(scope, pat);
203 }
204 } 251 }
205 ast::Stmt::ExprStmt(expr_stmt) => { 252 Statement::Expr(expr) => {
206 if let Some(expr) = expr_stmt.expr() { 253 scopes.set_scope(*expr, scope);
207 scopes.set_scope(expr.syntax(), scope); 254 compute_expr_scopes(*expr, body, scopes, scope);
208 compute_expr_scopes(expr, scopes, scope);
209 }
210 } 255 }
211 } 256 }
212 } 257 }
213 if let Some(expr) = block.expr() { 258 if let Some(expr) = tail {
214 scopes.set_scope(expr.syntax(), scope); 259 compute_expr_scopes(expr, body, scopes, scope);
215 compute_expr_scopes(expr, scopes, scope);
216 } 260 }
217} 261}
218 262
219fn compute_expr_scopes(expr: ast::Expr, scopes: &mut FnScopes, scope: ScopeId) { 263fn compute_expr_scopes(expr: ExprId, body: &Body, scopes: &mut FnScopes, scope: ScopeId) {
220 match expr { 264 scopes.set_scope(expr, scope);
221 ast::Expr::IfExpr(e) => { 265 match &body[expr] {
222 let cond_scope = e 266 Expr::Block { statements, tail } => {
223 .condition() 267 compute_block_scopes(&statements, *tail, body, scopes, scope);
224 .and_then(|cond| compute_cond_scopes(cond, scopes, scope));
225 if let Some(block) = e.then_branch() {
226 compute_block_scopes(block, scopes, cond_scope.unwrap_or(scope));
227 }
228 if let Some(block) = e.else_branch() {
229 compute_block_scopes(block, scopes, scope);
230 }
231 }
232 ast::Expr::BlockExpr(e) => {
233 if let Some(block) = e.block() {
234 compute_block_scopes(block, scopes, scope);
235 }
236 }
237 ast::Expr::LoopExpr(e) => {
238 if let Some(block) = e.loop_body() {
239 compute_block_scopes(block, scopes, scope);
240 }
241 }
242 ast::Expr::WhileExpr(e) => {
243 let cond_scope = e
244 .condition()
245 .and_then(|cond| compute_cond_scopes(cond, scopes, scope));
246 if let Some(block) = e.loop_body() {
247 compute_block_scopes(block, scopes, cond_scope.unwrap_or(scope));
248 }
249 } 268 }
250 ast::Expr::ForExpr(e) => { 269 Expr::For {
251 if let Some(expr) = e.iterable() { 270 iterable,
252 compute_expr_scopes(expr, scopes, scope); 271 pat,
253 } 272 body: body_expr,
254 let mut scope = scope; 273 } => {
255 if let Some(pat) = e.pat() { 274 compute_expr_scopes(*iterable, body, scopes, scope);
256 scope = scopes.new_scope(scope);
257 scopes.add_bindings(scope, pat);
258 }
259 if let Some(block) = e.loop_body() {
260 compute_block_scopes(block, scopes, scope);
261 }
262 }
263 ast::Expr::LambdaExpr(e) => {
264 let scope = scopes.new_scope(scope); 275 let scope = scopes.new_scope(scope);
265 scopes.add_params_bindings(scope, e.param_list()); 276 scopes.add_bindings(body, scope, *pat);
266 if let Some(body) = e.body() { 277 compute_expr_scopes(*body_expr, body, scopes, scope);
267 scopes.set_scope(body.syntax(), scope);
268 compute_expr_scopes(body, scopes, scope);
269 }
270 } 278 }
271 ast::Expr::CallExpr(e) => { 279 Expr::Lambda {
272 compute_call_scopes(e.expr(), e.arg_list(), scopes, scope); 280 args,
273 } 281 body: body_expr,
274 ast::Expr::MethodCallExpr(e) => { 282 ..
275 compute_call_scopes(e.expr(), e.arg_list(), scopes, scope); 283 } => {
284 let scope = scopes.new_scope(scope);
285 scopes.add_params_bindings(scope, &args);
286 compute_expr_scopes(*body_expr, body, scopes, scope);
276 } 287 }
277 ast::Expr::MatchExpr(e) => { 288 Expr::Match { expr, arms } => {
278 if let Some(expr) = e.expr() { 289 compute_expr_scopes(*expr, body, scopes, scope);
279 compute_expr_scopes(expr, scopes, scope); 290 for arm in arms {
280 }
281 for arm in e.match_arm_list().into_iter().flat_map(|it| it.arms()) {
282 let scope = scopes.new_scope(scope); 291 let scope = scopes.new_scope(scope);
283 for pat in arm.pats() { 292 for pat in &arm.pats {
284 scopes.add_bindings(scope, pat); 293 scopes.add_bindings(body, scope, *pat);
285 }
286 if let Some(expr) = arm.expr() {
287 compute_expr_scopes(expr, scopes, scope);
288 } 294 }
295 scopes.set_scope(arm.expr, scope);
296 compute_expr_scopes(arm.expr, body, scopes, scope);
289 } 297 }
290 } 298 }
291 _ => expr 299 e => e.walk_child_exprs(|e| compute_expr_scopes(e, body, scopes, scope)),
292 .syntax()
293 .children()
294 .filter_map(ast::Expr::cast)
295 .for_each(|expr| compute_expr_scopes(expr, scopes, scope)),
296 }; 300 };
297
298 fn compute_call_scopes(
299 receiver: Option<ast::Expr>,
300 arg_list: Option<ast::ArgList>,
301 scopes: &mut FnScopes,
302 scope: ScopeId,
303 ) {
304 arg_list
305 .into_iter()
306 .flat_map(|it| it.args())
307 .chain(receiver)
308 .for_each(|expr| compute_expr_scopes(expr, scopes, scope));
309 }
310
311 fn compute_cond_scopes(
312 cond: ast::Condition,
313 scopes: &mut FnScopes,
314 scope: ScopeId,
315 ) -> Option<ScopeId> {
316 if let Some(expr) = cond.expr() {
317 compute_expr_scopes(expr, scopes, scope);
318 }
319 if let Some(pat) = cond.pat() {
320 let s = scopes.new_scope(scope);
321 scopes.add_bindings(s, pat);
322 Some(s)
323 } else {
324 None
325 }
326 }
327} 301}
328 302
329#[derive(Debug)] 303#[derive(Debug)]
@@ -338,6 +312,8 @@ mod tests {
338 use ra_syntax::SourceFileNode; 312 use ra_syntax::SourceFileNode;
339 use test_utils::{extract_offset, assert_eq_text}; 313 use test_utils::{extract_offset, assert_eq_text};
340 314
315 use crate::expr;
316
341 use super::*; 317 use super::*;
342 318
343 fn do_check(code: &str, expected: &[&str]) { 319 fn do_check(code: &str, expected: &[&str]) {
@@ -353,15 +329,20 @@ mod tests {
353 let file = SourceFileNode::parse(&code); 329 let file = SourceFileNode::parse(&code);
354 let marker: ast::PathExpr = find_node_at_offset(file.syntax(), off).unwrap(); 330 let marker: ast::PathExpr = find_node_at_offset(file.syntax(), off).unwrap();
355 let fn_def: ast::FnDef = find_node_at_offset(file.syntax(), off).unwrap(); 331 let fn_def: ast::FnDef = find_node_at_offset(file.syntax(), off).unwrap();
356 let scopes = FnScopes::new(fn_def); 332 let body_hir = expr::collect_fn_body_syntax(fn_def);
333 let scopes = FnScopes::new(Arc::clone(body_hir.body()));
334 let scopes = ScopesWithSyntaxMapping {
335 scopes: Arc::new(scopes),
336 syntax_mapping: Arc::new(body_hir),
337 };
357 let actual = scopes 338 let actual = scopes
358 .scope_chain(marker.syntax()) 339 .scope_chain(marker.syntax())
359 .flat_map(|scope| scopes.entries(scope)) 340 .flat_map(|scope| scopes.scopes.entries(scope))
360 .map(|it| it.name().to_string()) 341 .map(|it| it.name().to_string())
361 .collect::<Vec<_>>() 342 .collect::<Vec<_>>()
362 .join("\n"); 343 .join("\n");
363 let expected = expected.join("\n"); 344 let expected = expected.join("\n");
364 assert_eq_text!(&actual, &expected); 345 assert_eq_text!(&expected, &actual);
365 } 346 }
366 347
367 #[test] 348 #[test]
@@ -389,7 +370,7 @@ mod tests {
389 } 370 }
390 371
391 #[test] 372 #[test]
392 fn test_metod_call_scope() { 373 fn test_method_call_scope() {
393 do_check( 374 do_check(
394 r" 375 r"
395 fn quux() { 376 fn quux() {
@@ -445,10 +426,15 @@ mod tests {
445 let fn_def: ast::FnDef = find_node_at_offset(file.syntax(), off).unwrap(); 426 let fn_def: ast::FnDef = find_node_at_offset(file.syntax(), off).unwrap();
446 let name_ref: ast::NameRef = find_node_at_offset(file.syntax(), off).unwrap(); 427 let name_ref: ast::NameRef = find_node_at_offset(file.syntax(), off).unwrap();
447 428
448 let scopes = FnScopes::new(fn_def); 429 let body_hir = expr::collect_fn_body_syntax(fn_def);
430 let scopes = FnScopes::new(Arc::clone(body_hir.body()));
431 let scopes = ScopesWithSyntaxMapping {
432 scopes: Arc::new(scopes),
433 syntax_mapping: Arc::new(body_hir),
434 };
449 435
450 let local_name_entry = scopes.resolve_local_name(name_ref).unwrap(); 436 let local_name_entry = scopes.resolve_local_name(name_ref).unwrap();
451 let local_name = local_name_entry.ptr().resolve(&file); 437 let local_name = local_name_entry.ptr();
452 let expected_name = 438 let expected_name =
453 find_node_at_offset::<ast::Name>(file.syntax(), expected_offset.into()).unwrap(); 439 find_node_at_offset::<ast::Name>(file.syntax(), expected_offset.into()).unwrap();
454 assert_eq!(local_name.range(), expected_name.syntax().range()); 440 assert_eq!(local_name.range(), expected_name.syntax().range());
diff --git a/crates/ra_hir/src/ids.rs b/crates/ra_hir/src/ids.rs
index 4d6378e02..c7391ee05 100644
--- a/crates/ra_hir/src/ids.rs
+++ b/crates/ra_hir/src/ids.rs
@@ -2,7 +2,9 @@ use ra_db::{SourceRootId, LocationIntener, Cancelable, FileId};
2use ra_syntax::{SourceFileNode, SyntaxKind, SyntaxNode, SyntaxNodeRef, SourceFile, AstNode, ast}; 2use ra_syntax::{SourceFileNode, SyntaxKind, SyntaxNode, SyntaxNodeRef, SourceFile, AstNode, ast};
3use ra_arena::{Arena, RawId, impl_arena_id}; 3use ra_arena::{Arena, RawId, impl_arena_id};
4 4
5use crate::{HirDatabase, PerNs, ModuleId, Module, Def, Function, Struct, Enum, ImplBlock, Crate}; 5use crate::{HirDatabase, PerNs, ModuleId, Def, Function, Struct, Enum, ImplBlock, Crate};
6
7use crate::code_model_api::Module;
6 8
7/// hir makes a heavy use of ids: integer (u32) handlers to various things. You 9/// hir makes a heavy use of ids: integer (u32) handlers to various things. You
8/// can think of id as a pointer (but without a lifetime) or a file descriptor 10/// can think of id as a pointer (but without a lifetime) or a file descriptor
@@ -151,7 +153,7 @@ impl DefId {
151 let loc = self.loc(db); 153 let loc = self.loc(db);
152 let res = match loc.kind { 154 let res = match loc.kind {
153 DefKind::Module => { 155 DefKind::Module => {
154 let module = Module::new(db, loc.source_root_id, loc.module_id)?; 156 let module = Module::from_module_id(db, loc.source_root_id, loc.module_id)?;
155 Def::Module(module) 157 Def::Module(module)
156 } 158 }
157 DefKind::Function => { 159 DefKind::Function => {
@@ -175,12 +177,12 @@ impl DefId {
175 /// For a module, returns that module; for any other def, returns the containing module. 177 /// For a module, returns that module; for any other def, returns the containing module.
176 pub fn module(self, db: &impl HirDatabase) -> Cancelable<Module> { 178 pub fn module(self, db: &impl HirDatabase) -> Cancelable<Module> {
177 let loc = self.loc(db); 179 let loc = self.loc(db);
178 Module::new(db, loc.source_root_id, loc.module_id) 180 Module::from_module_id(db, loc.source_root_id, loc.module_id)
179 } 181 }
180 182
181 /// Returns the containing crate. 183 /// Returns the containing crate.
182 pub fn krate(&self, db: &impl HirDatabase) -> Cancelable<Option<Crate>> { 184 pub fn krate(&self, db: &impl HirDatabase) -> Cancelable<Option<Crate>> {
183 Ok(self.module(db)?.krate(db)) 185 Ok(self.module(db)?.krate(db)?)
184 } 186 }
185 187
186 /// Returns the containing impl block, if this is an impl item. 188 /// Returns the containing impl block, if this is an impl item.
diff --git a/crates/ra_hir/src/impl_block.rs b/crates/ra_hir/src/impl_block.rs
index 01afa84c4..7ce8d17e6 100644
--- a/crates/ra_hir/src/impl_block.rs
+++ b/crates/ra_hir/src/impl_block.rs
@@ -7,12 +7,14 @@ use ra_db::{LocationIntener, Cancelable, SourceRootId};
7 7
8use crate::{ 8use crate::{
9 DefId, DefLoc, DefKind, SourceItemId, SourceFileItems, 9 DefId, DefLoc, DefKind, SourceItemId, SourceFileItems,
10 Module, Function, 10 Function,
11 db::HirDatabase, 11 db::HirDatabase,
12 type_ref::TypeRef, 12 type_ref::TypeRef,
13 module::{ModuleSourceNode, ModuleId}, 13 module_tree::ModuleId,
14}; 14};
15 15
16use crate::code_model_api::{Module, ModuleSource};
17
16#[derive(Debug, Clone, PartialEq, Eq)] 18#[derive(Debug, Clone, PartialEq, Eq)]
17pub struct ImplBlock { 19pub struct ImplBlock {
18 module_impl_blocks: Arc<ModuleImplBlocks>, 20 module_impl_blocks: Arc<ModuleImplBlocks>,
@@ -64,7 +66,7 @@ impl ImplData {
64 ) -> Self { 66 ) -> Self {
65 let target_trait = node.target_type().map(TypeRef::from_ast); 67 let target_trait = node.target_type().map(TypeRef::from_ast);
66 let target_type = TypeRef::from_ast_opt(node.target_type()); 68 let target_type = TypeRef::from_ast_opt(node.target_type());
67 let file_id = module.source().file_id(); 69 let module_loc = module.def_id.loc(db);
68 let items = if let Some(item_list) = node.item_list() { 70 let items = if let Some(item_list) = node.item_list() {
69 item_list 71 item_list
70 .impl_items() 72 .impl_items()
@@ -75,14 +77,14 @@ impl ImplData {
75 ast::ImplItem::TypeDef(..) => DefKind::Item, 77 ast::ImplItem::TypeDef(..) => DefKind::Item,
76 }; 78 };
77 let item_id = file_items.id_of_unchecked(item_node.syntax()); 79 let item_id = file_items.id_of_unchecked(item_node.syntax());
80 let source_item_id = SourceItemId {
81 file_id: module_loc.source_item_id.file_id,
82 item_id: Some(item_id),
83 };
78 let def_loc = DefLoc { 84 let def_loc = DefLoc {
79 kind, 85 kind,
80 source_root_id: module.source_root_id, 86 source_item_id,
81 module_id: module.module_id, 87 ..module_loc
82 source_item_id: SourceItemId {
83 file_id,
84 item_id: Some(item_id),
85 },
86 }; 88 };
87 let def_id = def_loc.id(db); 89 let def_id = def_loc.id(db);
88 match item_node { 90 match item_node {
@@ -148,13 +150,13 @@ impl ModuleImplBlocks {
148 } 150 }
149 151
150 fn collect(&mut self, db: &impl HirDatabase, module: Module) -> Cancelable<()> { 152 fn collect(&mut self, db: &impl HirDatabase, module: Module) -> Cancelable<()> {
151 let module_source_node = module.source().resolve(db); 153 let (file_id, module_source) = module.defenition_source(db)?;
152 let node = match &module_source_node { 154 let node = match &module_source {
153 ModuleSourceNode::SourceFile(node) => node.borrowed().syntax(), 155 ModuleSource::SourceFile(node) => node.borrowed().syntax(),
154 ModuleSourceNode::Module(node) => node.borrowed().syntax(), 156 ModuleSource::Module(node) => node.borrowed().syntax(),
155 }; 157 };
156 158
157 let source_file_items = db.file_items(module.source().file_id()); 159 let source_file_items = db.file_items(file_id.into());
158 160
159 for impl_block_ast in node.children().filter_map(ast::ImplBlock::cast) { 161 for impl_block_ast in node.children().filter_map(ast::ImplBlock::cast) {
160 let impl_block = ImplData::from_ast(db, &source_file_items, &module, impl_block_ast); 162 let impl_block = ImplData::from_ast(db, &source_file_items, &module, impl_block_ast);
@@ -174,7 +176,7 @@ pub(crate) fn impls_in_module(
174 module_id: ModuleId, 176 module_id: ModuleId,
175) -> Cancelable<Arc<ModuleImplBlocks>> { 177) -> Cancelable<Arc<ModuleImplBlocks>> {
176 let mut result = ModuleImplBlocks::new(); 178 let mut result = ModuleImplBlocks::new();
177 let module = Module::new(db, source_root_id, module_id)?; 179 let module = Module::from_module_id(db, source_root_id, module_id)?;
178 result.collect(db, module)?; 180 result.collect(db, module)?;
179 Ok(Arc::new(result)) 181 Ok(Arc::new(result))
180} 182}
diff --git a/crates/ra_hir/src/krate.rs b/crates/ra_hir/src/krate.rs
deleted file mode 100644
index 5194e280b..000000000
--- a/crates/ra_hir/src/krate.rs
+++ /dev/null
@@ -1,48 +0,0 @@
1pub use ra_db::{CrateId, Cancelable};
2
3use crate::{HirDatabase, Module, Name, AsName, HirFileId};
4
5/// hir::Crate describes a single crate. It's the main inteface with which
6/// crate's dependencies interact. Mostly, it should be just a proxy for the
7/// root module.
8#[derive(Debug, Clone, PartialEq, Eq, Hash)]
9pub struct Crate {
10 crate_id: CrateId,
11}
12
13#[derive(Debug)]
14pub struct CrateDependency {
15 pub krate: Crate,
16 pub name: Name,
17}
18
19impl Crate {
20 pub(crate) fn new(crate_id: CrateId) -> Crate {
21 Crate { crate_id }
22 }
23 pub fn dependencies(&self, db: &impl HirDatabase) -> Vec<CrateDependency> {
24 let crate_graph = db.crate_graph();
25 crate_graph
26 .dependencies(self.crate_id)
27 .map(|dep| {
28 let krate = Crate::new(dep.crate_id());
29 let name = dep.as_name();
30 CrateDependency { krate, name }
31 })
32 .collect()
33 }
34 pub fn root_module(&self, db: &impl HirDatabase) -> Cancelable<Option<Module>> {
35 let crate_graph = db.crate_graph();
36 let file_id = crate_graph.crate_root(self.crate_id);
37 let source_root_id = db.file_source_root(file_id);
38 let file_id = HirFileId::from(file_id);
39 let module_tree = db.module_tree(source_root_id)?;
40 // FIXME: teach module tree about crate roots instead of guessing
41 let (module_id, _) = ctry!(module_tree
42 .modules_with_sources()
43 .find(|(_, src)| src.file_id() == file_id));
44
45 let module = Module::new(db, source_root_id, module_id)?;
46 Ok(Some(module))
47 }
48}
diff --git a/crates/ra_hir/src/lib.rs b/crates/ra_hir/src/lib.rs
index 2abcec441..9f133f174 100644
--- a/crates/ra_hir/src/lib.rs
+++ b/crates/ra_hir/src/lib.rs
@@ -24,14 +24,17 @@ pub mod source_binder;
24mod ids; 24mod ids;
25mod macros; 25mod macros;
26mod name; 26mod name;
27// can't use `crate` or `r#crate` here :( 27mod module_tree;
28mod krate; 28mod nameres;
29mod module;
30mod function; 29mod function;
31mod adt; 30mod adt;
32mod type_ref; 31mod type_ref;
33mod ty; 32mod ty;
34mod impl_block; 33mod impl_block;
34mod expr;
35
36mod code_model_api;
37mod code_model_impl;
35 38
36use crate::{ 39use crate::{
37 db::HirDatabase, 40 db::HirDatabase,
@@ -42,11 +45,11 @@ use crate::{
42pub use self::{ 45pub use self::{
43 path::{Path, PathKind}, 46 path::{Path, PathKind},
44 name::Name, 47 name::Name,
45 krate::Crate,
46 ids::{HirFileId, DefId, DefLoc, MacroCallId, MacroCallLoc}, 48 ids::{HirFileId, DefId, DefLoc, MacroCallId, MacroCallLoc},
47 macros::{MacroDef, MacroInput, MacroExpansion}, 49 macros::{MacroDef, MacroInput, MacroExpansion},
48 module::{Module, ModuleId, Problem, nameres::{ItemMap, PerNs, Namespace}, ModuleScope, Resolution}, 50 module_tree::ModuleId,
49 function::{Function, FnScopes}, 51 nameres::{ItemMap, PerNs, Namespace, Resolution},
52 function::{Function, FnSignature, FnScopes, ScopesWithSyntaxMapping},
50 adt::{Struct, Enum}, 53 adt::{Struct, Enum},
51 ty::Ty, 54 ty::Ty,
52 impl_block::{ImplBlock, ImplItem}, 55 impl_block::{ImplBlock, ImplItem},
@@ -54,6 +57,11 @@ pub use self::{
54 57
55pub use self::function::FnSignatureInfo; 58pub use self::function::FnSignatureInfo;
56 59
60pub use self::code_model_api::{
61 Crate, CrateDependency,
62 Module, ModuleSource, Problem,
63};
64
57pub enum Def { 65pub enum Def {
58 Module(Module), 66 Module(Module),
59 Function(Function), 67 Function(Function),
diff --git a/crates/ra_hir/src/mock.rs b/crates/ra_hir/src/mock.rs
index a9db932ff..8d176662c 100644
--- a/crates/ra_hir/src/mock.rs
+++ b/crates/ra_hir/src/mock.rs
@@ -208,6 +208,9 @@ salsa::database_storage! {
208 fn struct_data() for db::StructDataQuery; 208 fn struct_data() for db::StructDataQuery;
209 fn enum_data() for db::EnumDataQuery; 209 fn enum_data() for db::EnumDataQuery;
210 fn impls_in_module() for db::ImplsInModuleQuery; 210 fn impls_in_module() for db::ImplsInModuleQuery;
211 fn body_hir() for db::BodyHirQuery;
212 fn body_syntax_mapping() for db::BodySyntaxMappingQuery;
213 fn fn_signature() for db::FnSignatureQuery;
211 } 214 }
212 } 215 }
213} 216}
diff --git a/crates/ra_hir/src/module.rs b/crates/ra_hir/src/module.rs
deleted file mode 100644
index b9821115c..000000000
--- a/crates/ra_hir/src/module.rs
+++ /dev/null
@@ -1,376 +0,0 @@
1pub(super) mod imp;
2pub(super) mod nameres;
3
4use std::sync::Arc;
5use log;
6
7use ra_syntax::{
8 algo::generate,
9 ast::{self, AstNode, NameOwner},
10 SyntaxNode,
11};
12use ra_arena::{Arena, RawId, impl_arena_id};
13use ra_db::{SourceRootId, FileId, Cancelable};
14use relative_path::RelativePathBuf;
15
16use crate::{
17 Def, DefKind, DefLoc, DefId,
18 Name, Path, PathKind, HirDatabase, SourceItemId, SourceFileItemId, Crate,
19 HirFileId,
20};
21
22pub use self::nameres::{ModuleScope, Resolution, Namespace, PerNs};
23
24/// `Module` is API entry point to get all the information
25/// about a particular module.
26#[derive(Debug, Clone)]
27pub struct Module {
28 tree: Arc<ModuleTree>,
29 pub(crate) source_root_id: SourceRootId,
30 pub(crate) module_id: ModuleId,
31}
32
33impl Module {
34 pub(super) fn new(
35 db: &impl HirDatabase,
36 source_root_id: SourceRootId,
37 module_id: ModuleId,
38 ) -> Cancelable<Module> {
39 let module_tree = db.module_tree(source_root_id)?;
40 let res = Module {
41 tree: module_tree,
42 source_root_id,
43 module_id,
44 };
45 Ok(res)
46 }
47
48 /// Returns `mod foo;` or `mod foo {}` node whihc declared this module.
49 /// Returns `None` for the root module
50 pub fn parent_link_source(&self, db: &impl HirDatabase) -> Option<(FileId, ast::ModuleNode)> {
51 let link = self.module_id.parent_link(&self.tree)?;
52 let file_id = link
53 .owner(&self.tree)
54 .source(&self.tree)
55 .file_id()
56 .as_original_file();
57 let src = link.bind_source(&self.tree, db);
58 Some((file_id, src))
59 }
60
61 pub fn file_id(&self) -> FileId {
62 self.source().file_id().as_original_file()
63 }
64
65 /// Parent module. Returns `None` if this is a root module.
66 pub fn parent(&self) -> Option<Module> {
67 let parent_id = self.module_id.parent(&self.tree)?;
68 Some(Module {
69 module_id: parent_id,
70 ..self.clone()
71 })
72 }
73
74 /// Returns an iterator of all children of this module.
75 pub fn children<'a>(&'a self) -> impl Iterator<Item = (Name, Module)> + 'a {
76 self.module_id
77 .children(&self.tree)
78 .map(move |(name, module_id)| {
79 (
80 name,
81 Module {
82 module_id,
83 ..self.clone()
84 },
85 )
86 })
87 }
88
89 /// Returns the crate this module is part of.
90 pub fn krate(&self, db: &impl HirDatabase) -> Option<Crate> {
91 let root_id = self.module_id.crate_root(&self.tree);
92 let file_id = root_id.source(&self.tree).file_id().as_original_file();
93 let crate_graph = db.crate_graph();
94 let crate_id = crate_graph.crate_id_for_crate_root(file_id)?;
95 Some(Crate::new(crate_id))
96 }
97
98 /// Returns the all modules on the way to the root.
99 pub fn path_to_root(&self) -> Vec<Module> {
100 generate(Some(self.clone()), move |it| it.parent()).collect::<Vec<Module>>()
101 }
102
103 /// The root of the tree this module is part of
104 pub fn crate_root(&self) -> Module {
105 let root_id = self.module_id.crate_root(&self.tree);
106 Module {
107 module_id: root_id,
108 ..self.clone()
109 }
110 }
111
112 /// `name` is `None` for the crate's root module
113 pub fn name(&self) -> Option<&Name> {
114 let link = self.module_id.parent_link(&self.tree)?;
115 Some(link.name(&self.tree))
116 }
117
118 pub fn def_id(&self, db: &impl HirDatabase) -> DefId {
119 let def_loc = DefLoc {
120 kind: DefKind::Module,
121 source_root_id: self.source_root_id,
122 module_id: self.module_id,
123 source_item_id: self.module_id.source(&self.tree).0,
124 };
125 def_loc.id(db)
126 }
127
128 /// Finds a child module with the specified name.
129 pub fn child(&self, name: &Name) -> Option<Module> {
130 let child_id = self.module_id.child(&self.tree, name)?;
131 Some(Module {
132 module_id: child_id,
133 ..self.clone()
134 })
135 }
136
137 /// Returns a `ModuleScope`: a set of items, visible in this module.
138 pub fn scope(&self, db: &impl HirDatabase) -> Cancelable<ModuleScope> {
139 let item_map = db.item_map(self.source_root_id)?;
140 let res = item_map.per_module[&self.module_id].clone();
141 Ok(res)
142 }
143
144 pub fn resolve_path(&self, db: &impl HirDatabase, path: &Path) -> Cancelable<PerNs<DefId>> {
145 let mut curr_per_ns = PerNs::types(
146 match path.kind {
147 PathKind::Crate => self.crate_root(),
148 PathKind::Self_ | PathKind::Plain => self.clone(),
149 PathKind::Super => {
150 if let Some(p) = self.parent() {
151 p
152 } else {
153 return Ok(PerNs::none());
154 }
155 }
156 }
157 .def_id(db),
158 );
159
160 let segments = &path.segments;
161 for name in segments.iter() {
162 let curr = if let Some(r) = curr_per_ns.as_ref().take(Namespace::Types) {
163 r
164 } else {
165 return Ok(PerNs::none());
166 };
167 let module = match curr.resolve(db)? {
168 Def::Module(it) => it,
169 // TODO here would be the place to handle enum variants...
170 _ => return Ok(PerNs::none()),
171 };
172 let scope = module.scope(db)?;
173 curr_per_ns = if let Some(r) = scope.get(&name) {
174 r.def_id
175 } else {
176 return Ok(PerNs::none());
177 };
178 }
179 Ok(curr_per_ns)
180 }
181
182 pub fn problems(&self, db: &impl HirDatabase) -> Vec<(SyntaxNode, Problem)> {
183 self.module_id.problems(&self.tree, db)
184 }
185
186 pub(crate) fn source(&self) -> ModuleSource {
187 self.module_id.source(&self.tree)
188 }
189}
190
191#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
192pub struct ModuleId(RawId);
193impl_arena_id!(ModuleId);
194
195#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
196pub struct LinkId(RawId);
197impl_arena_id!(LinkId);
198
199/// Physically, rust source is organized as a set of files, but logically it is
200/// organized as a tree of modules. Usually, a single file corresponds to a
201/// single module, but it is not nessary the case.
202///
203/// Module encapsulate the logic of transitioning from the fuzzy world of files
204/// (which can have multiple parents) to the precise world of modules (which
205/// always have one parent).
206#[derive(Default, Debug, PartialEq, Eq)]
207pub struct ModuleTree {
208 mods: Arena<ModuleId, ModuleData>,
209 links: Arena<LinkId, LinkData>,
210}
211
212impl ModuleTree {
213 pub(crate) fn modules<'a>(&'a self) -> impl Iterator<Item = ModuleId> + 'a {
214 self.mods.iter().map(|(id, _)| id)
215 }
216
217 pub(crate) fn modules_with_sources<'a>(
218 &'a self,
219 ) -> impl Iterator<Item = (ModuleId, ModuleSource)> + 'a {
220 self.mods.iter().map(|(id, m)| (id, m.source))
221 }
222}
223
224/// `ModuleSource` is the syntax tree element that produced this module:
225/// either a file, or an inlinde module.
226#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
227pub struct ModuleSource(SourceItemId);
228
229/// An owned syntax node for a module. Unlike `ModuleSource`,
230/// this holds onto the AST for the whole file.
231pub(crate) enum ModuleSourceNode {
232 SourceFile(ast::SourceFileNode),
233 Module(ast::ModuleNode),
234}
235
236#[derive(Clone, Debug, Hash, PartialEq, Eq)]
237pub enum Problem {
238 UnresolvedModule {
239 candidate: RelativePathBuf,
240 },
241 NotDirOwner {
242 move_to: RelativePathBuf,
243 candidate: RelativePathBuf,
244 },
245}
246
247impl ModuleId {
248 pub(crate) fn source(self, tree: &ModuleTree) -> ModuleSource {
249 tree.mods[self].source
250 }
251 fn parent_link(self, tree: &ModuleTree) -> Option<LinkId> {
252 tree.mods[self].parent
253 }
254 fn parent(self, tree: &ModuleTree) -> Option<ModuleId> {
255 let link = self.parent_link(tree)?;
256 Some(tree.links[link].owner)
257 }
258 fn crate_root(self, tree: &ModuleTree) -> ModuleId {
259 generate(Some(self), move |it| it.parent(tree))
260 .last()
261 .unwrap()
262 }
263 fn child(self, tree: &ModuleTree, name: &Name) -> Option<ModuleId> {
264 let link = tree.mods[self]
265 .children
266 .iter()
267 .map(|&it| &tree.links[it])
268 .find(|it| it.name == *name)?;
269 Some(*link.points_to.first()?)
270 }
271 fn children<'a>(self, tree: &'a ModuleTree) -> impl Iterator<Item = (Name, ModuleId)> + 'a {
272 tree.mods[self].children.iter().filter_map(move |&it| {
273 let link = &tree.links[it];
274 let module = *link.points_to.first()?;
275 Some((link.name.clone(), module))
276 })
277 }
278 fn problems(self, tree: &ModuleTree, db: &impl HirDatabase) -> Vec<(SyntaxNode, Problem)> {
279 tree.mods[self]
280 .children
281 .iter()
282 .filter_map(|&it| {
283 let p = tree.links[it].problem.clone()?;
284 let s = it.bind_source(tree, db);
285 let s = s.borrowed().name().unwrap().syntax().owned();
286 Some((s, p))
287 })
288 .collect()
289 }
290}
291
292impl LinkId {
293 fn owner(self, tree: &ModuleTree) -> ModuleId {
294 tree.links[self].owner
295 }
296 fn name(self, tree: &ModuleTree) -> &Name {
297 &tree.links[self].name
298 }
299 fn bind_source<'a>(self, tree: &ModuleTree, db: &impl HirDatabase) -> ast::ModuleNode {
300 let owner = self.owner(tree);
301 match owner.source(tree).resolve(db) {
302 ModuleSourceNode::SourceFile(root) => {
303 let ast = imp::modules(root.borrowed())
304 .find(|(name, _)| name == &tree.links[self].name)
305 .unwrap()
306 .1;
307 ast.owned()
308 }
309 ModuleSourceNode::Module(it) => it,
310 }
311 }
312}
313
314#[derive(Debug, PartialEq, Eq, Hash)]
315pub struct ModuleData {
316 source: ModuleSource,
317 parent: Option<LinkId>,
318 children: Vec<LinkId>,
319}
320
321impl ModuleSource {
322 // precondition: item_id **must** point to module
323 fn new(file_id: HirFileId, item_id: Option<SourceFileItemId>) -> ModuleSource {
324 let source_item_id = SourceItemId { file_id, item_id };
325 ModuleSource(source_item_id)
326 }
327
328 pub(crate) fn new_file(file_id: HirFileId) -> ModuleSource {
329 ModuleSource::new(file_id, None)
330 }
331
332 pub(crate) fn new_inline(
333 db: &impl HirDatabase,
334 file_id: HirFileId,
335 m: ast::Module,
336 ) -> ModuleSource {
337 assert!(!m.has_semi());
338 let file_items = db.file_items(file_id);
339 let item_id = file_items.id_of(file_id, m.syntax());
340 ModuleSource::new(file_id, Some(item_id))
341 }
342
343 pub(crate) fn file_id(self) -> HirFileId {
344 self.0.file_id
345 }
346
347 pub(crate) fn resolve(self, db: &impl HirDatabase) -> ModuleSourceNode {
348 let syntax_node = db.file_item(self.0);
349 let syntax_node = syntax_node.borrowed();
350 if let Some(file) = ast::SourceFile::cast(syntax_node) {
351 return ModuleSourceNode::SourceFile(file.owned());
352 }
353 let module = ast::Module::cast(syntax_node).unwrap();
354 ModuleSourceNode::Module(module.owned())
355 }
356}
357
358#[derive(Hash, Debug, PartialEq, Eq)]
359struct LinkData {
360 owner: ModuleId,
361 name: Name,
362 points_to: Vec<ModuleId>,
363 problem: Option<Problem>,
364}
365
366impl ModuleTree {
367 fn push_mod(&mut self, data: ModuleData) -> ModuleId {
368 self.mods.alloc(data)
369 }
370 fn push_link(&mut self, data: LinkData) -> LinkId {
371 let owner = data.owner;
372 let id = self.links.alloc(data);
373 self.mods[owner].children.push(id);
374 id
375 }
376}
diff --git a/crates/ra_hir/src/module/imp.rs b/crates/ra_hir/src/module/imp.rs
deleted file mode 100644
index 3849026db..000000000
--- a/crates/ra_hir/src/module/imp.rs
+++ /dev/null
@@ -1,190 +0,0 @@
1use std::sync::Arc;
2
3use ra_syntax::ast::{self, NameOwner};
4use relative_path::RelativePathBuf;
5use rustc_hash::{FxHashMap, FxHashSet};
6use arrayvec::ArrayVec;
7use ra_db::{SourceRoot, SourceRootId, Cancelable, FileId};
8
9use crate::{
10 HirDatabase, Name, AsName,
11};
12
13use super::{
14 LinkData, LinkId, ModuleData, ModuleId, ModuleSource,
15 ModuleTree, Problem,
16};
17
18#[derive(Clone, Hash, PartialEq, Eq, Debug)]
19pub enum Submodule {
20 Declaration(Name),
21 Definition(Name, ModuleSource),
22}
23
24impl Submodule {
25 fn name(&self) -> &Name {
26 match self {
27 Submodule::Declaration(name) => name,
28 Submodule::Definition(name, _) => name,
29 }
30 }
31}
32
33pub(crate) fn modules<'a>(
34 root: impl ast::ModuleItemOwner<'a>,
35) -> impl Iterator<Item = (Name, ast::Module<'a>)> {
36 root.items()
37 .filter_map(|item| match item {
38 ast::ModuleItem::Module(m) => Some(m),
39 _ => None,
40 })
41 .filter_map(|module| {
42 let name = module.name()?.as_name();
43 Some((name, module))
44 })
45}
46
47pub(crate) fn module_tree(
48 db: &impl HirDatabase,
49 source_root: SourceRootId,
50) -> Cancelable<Arc<ModuleTree>> {
51 db.check_canceled()?;
52 let res = create_module_tree(db, source_root)?;
53 Ok(Arc::new(res))
54}
55
56fn create_module_tree<'a>(
57 db: &impl HirDatabase,
58 source_root: SourceRootId,
59) -> Cancelable<ModuleTree> {
60 let mut tree = ModuleTree::default();
61
62 let mut roots = FxHashMap::default();
63 let mut visited = FxHashSet::default();
64
65 let source_root = db.source_root(source_root);
66 for &file_id in source_root.files.values() {
67 let source = ModuleSource::new_file(file_id.into());
68 if visited.contains(&source) {
69 continue; // TODO: use explicit crate_roots here
70 }
71 assert!(!roots.contains_key(&file_id));
72 let module_id = build_subtree(
73 db,
74 &source_root,
75 &mut tree,
76 &mut visited,
77 &mut roots,
78 None,
79 source,
80 )?;
81 roots.insert(file_id, module_id);
82 }
83 Ok(tree)
84}
85
86fn build_subtree(
87 db: &impl HirDatabase,
88 source_root: &SourceRoot,
89 tree: &mut ModuleTree,
90 visited: &mut FxHashSet<ModuleSource>,
91 roots: &mut FxHashMap<FileId, ModuleId>,
92 parent: Option<LinkId>,
93 source: ModuleSource,
94) -> Cancelable<ModuleId> {
95 visited.insert(source);
96 let id = tree.push_mod(ModuleData {
97 source,
98 parent,
99 children: Vec::new(),
100 });
101 for sub in db.submodules(source)?.iter() {
102 let link = tree.push_link(LinkData {
103 name: sub.name().clone(),
104 owner: id,
105 points_to: Vec::new(),
106 problem: None,
107 });
108
109 let (points_to, problem) = match sub {
110 Submodule::Declaration(name) => {
111 let (points_to, problem) = resolve_submodule(db, source, &name);
112 let points_to = points_to
113 .into_iter()
114 .map(|file_id| match roots.remove(&file_id) {
115 Some(module_id) => {
116 tree.mods[module_id].parent = Some(link);
117 Ok(module_id)
118 }
119 None => build_subtree(
120 db,
121 source_root,
122 tree,
123 visited,
124 roots,
125 Some(link),
126 ModuleSource::new_file(file_id.into()),
127 ),
128 })
129 .collect::<Cancelable<Vec<_>>>()?;
130 (points_to, problem)
131 }
132 Submodule::Definition(_name, submodule_source) => {
133 let points_to = build_subtree(
134 db,
135 source_root,
136 tree,
137 visited,
138 roots,
139 Some(link),
140 *submodule_source,
141 )?;
142 (vec![points_to], None)
143 }
144 };
145
146 tree.links[link].points_to = points_to;
147 tree.links[link].problem = problem;
148 }
149 Ok(id)
150}
151
152fn resolve_submodule(
153 db: &impl HirDatabase,
154 source: ModuleSource,
155 name: &Name,
156) -> (Vec<FileId>, Option<Problem>) {
157 // FIXME: handle submodules of inline modules properly
158 let file_id = source.file_id().original_file(db);
159 let source_root_id = db.file_source_root(file_id);
160 let path = db.file_relative_path(file_id);
161 let root = RelativePathBuf::default();
162 let dir_path = path.parent().unwrap_or(&root);
163 let mod_name = path.file_stem().unwrap_or("unknown");
164 let is_dir_owner = mod_name == "mod" || mod_name == "lib" || mod_name == "main";
165
166 let file_mod = dir_path.join(format!("{}.rs", name));
167 let dir_mod = dir_path.join(format!("{}/mod.rs", name));
168 let file_dir_mod = dir_path.join(format!("{}/{}.rs", mod_name, name));
169 let mut candidates = ArrayVec::<[_; 2]>::new();
170 if is_dir_owner {
171 candidates.push(file_mod.clone());
172 candidates.push(dir_mod);
173 } else {
174 candidates.push(file_dir_mod.clone());
175 };
176 let sr = db.source_root(source_root_id);
177 let points_to = candidates
178 .into_iter()
179 .filter_map(|path| sr.files.get(&path))
180 .map(|&it| it)
181 .collect::<Vec<_>>();
182 let problem = if points_to.is_empty() {
183 Some(Problem::UnresolvedModule {
184 candidate: if is_dir_owner { file_mod } else { file_dir_mod },
185 })
186 } else {
187 None
188 };
189 (points_to, problem)
190}
diff --git a/crates/ra_hir/src/module_tree.rs b/crates/ra_hir/src/module_tree.rs
new file mode 100644
index 000000000..c7a442319
--- /dev/null
+++ b/crates/ra_hir/src/module_tree.rs
@@ -0,0 +1,368 @@
1use std::sync::Arc;
2
3use rustc_hash::{FxHashMap, FxHashSet};
4use arrayvec::ArrayVec;
5use relative_path::RelativePathBuf;
6use ra_db::{FileId, SourceRootId, Cancelable, SourceRoot};
7use ra_syntax::{
8 algo::generate,
9 ast::{self, AstNode, NameOwner},
10 SyntaxNode,
11};
12use ra_arena::{Arena, RawId, impl_arena_id};
13
14use crate::{Name, AsName, HirDatabase, SourceItemId, HirFileId, Problem, SourceFileItems, ModuleSource};
15
16impl ModuleSource {
17 pub fn from_source_item_id(
18 db: &impl HirDatabase,
19 source_item_id: SourceItemId,
20 ) -> ModuleSource {
21 let module_syntax = db.file_item(source_item_id);
22 let module_syntax = module_syntax.borrowed();
23 if let Some(source_file) = ast::SourceFile::cast(module_syntax) {
24 ModuleSource::SourceFile(source_file.owned())
25 } else if let Some(module) = ast::Module::cast(module_syntax) {
26 assert!(module.item_list().is_some(), "expected inline module");
27 ModuleSource::Module(module.owned())
28 } else {
29 panic!("expected file or inline module")
30 }
31 }
32}
33
34#[derive(Clone, Hash, PartialEq, Eq, Debug)]
35pub struct Submodule {
36 name: Name,
37 is_declaration: bool,
38 source: SourceItemId,
39}
40
41impl Submodule {
42 pub(crate) fn submodules_query(
43 db: &impl HirDatabase,
44 source: SourceItemId,
45 ) -> Cancelable<Arc<Vec<Submodule>>> {
46 db.check_canceled()?;
47 let file_id = source.file_id;
48 let file_items = db.file_items(file_id);
49 let module_source = ModuleSource::from_source_item_id(db, source);
50 let submodules = match module_source {
51 ModuleSource::SourceFile(source_file) => {
52 collect_submodules(file_id, &file_items, source_file.borrowed())
53 }
54 ModuleSource::Module(module) => {
55 let module = module.borrowed();
56 collect_submodules(file_id, &file_items, module.item_list().unwrap())
57 }
58 };
59 return Ok(Arc::new(submodules));
60
61 fn collect_submodules<'a>(
62 file_id: HirFileId,
63 file_items: &SourceFileItems,
64 root: impl ast::ModuleItemOwner<'a>,
65 ) -> Vec<Submodule> {
66 modules(root)
67 .map(|(name, m)| Submodule {
68 name,
69 is_declaration: m.has_semi(),
70 source: SourceItemId {
71 file_id,
72 item_id: Some(file_items.id_of(file_id, m.syntax())),
73 },
74 })
75 .collect()
76 }
77 }
78}
79
80#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
81pub struct ModuleId(RawId);
82impl_arena_id!(ModuleId);
83
84#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
85pub struct LinkId(RawId);
86impl_arena_id!(LinkId);
87
88/// Physically, rust source is organized as a set of files, but logically it is
89/// organized as a tree of modules. Usually, a single file corresponds to a
90/// single module, but it is not nessary the case.
91///
92/// Module encapsulate the logic of transitioning from the fuzzy world of files
93/// (which can have multiple parents) to the precise world of modules (which
94/// always have one parent).
95#[derive(Default, Debug, PartialEq, Eq)]
96pub struct ModuleTree {
97 mods: Arena<ModuleId, ModuleData>,
98 links: Arena<LinkId, LinkData>,
99}
100
101#[derive(Debug, PartialEq, Eq, Hash)]
102pub struct ModuleData {
103 source: SourceItemId,
104 parent: Option<LinkId>,
105 children: Vec<LinkId>,
106}
107
108#[derive(Hash, Debug, PartialEq, Eq)]
109struct LinkData {
110 source: SourceItemId,
111 owner: ModuleId,
112 name: Name,
113 points_to: Vec<ModuleId>,
114 problem: Option<Problem>,
115}
116
117impl ModuleTree {
118 pub(crate) fn module_tree_query(
119 db: &impl HirDatabase,
120 source_root: SourceRootId,
121 ) -> Cancelable<Arc<ModuleTree>> {
122 db.check_canceled()?;
123 let res = create_module_tree(db, source_root)?;
124 Ok(Arc::new(res))
125 }
126
127 pub(crate) fn modules<'a>(&'a self) -> impl Iterator<Item = ModuleId> + 'a {
128 self.mods.iter().map(|(id, _)| id)
129 }
130
131 pub(crate) fn find_module_by_source(&self, source: SourceItemId) -> Option<ModuleId> {
132 let (res, _) = self.mods.iter().find(|(_, m)| m.source == source)?;
133 Some(res)
134 }
135}
136
137impl ModuleId {
138 pub(crate) fn source(self, tree: &ModuleTree) -> SourceItemId {
139 tree.mods[self].source
140 }
141 pub(crate) fn parent_link(self, tree: &ModuleTree) -> Option<LinkId> {
142 tree.mods[self].parent
143 }
144 pub(crate) fn parent(self, tree: &ModuleTree) -> Option<ModuleId> {
145 let link = self.parent_link(tree)?;
146 Some(tree.links[link].owner)
147 }
148 pub(crate) fn crate_root(self, tree: &ModuleTree) -> ModuleId {
149 generate(Some(self), move |it| it.parent(tree))
150 .last()
151 .unwrap()
152 }
153 pub(crate) fn child(self, tree: &ModuleTree, name: &Name) -> Option<ModuleId> {
154 let link = tree.mods[self]
155 .children
156 .iter()
157 .map(|&it| &tree.links[it])
158 .find(|it| it.name == *name)?;
159 Some(*link.points_to.first()?)
160 }
161 pub(crate) fn children<'a>(
162 self,
163 tree: &'a ModuleTree,
164 ) -> impl Iterator<Item = (Name, ModuleId)> + 'a {
165 tree.mods[self].children.iter().filter_map(move |&it| {
166 let link = &tree.links[it];
167 let module = *link.points_to.first()?;
168 Some((link.name.clone(), module))
169 })
170 }
171 pub(crate) fn problems(
172 self,
173 tree: &ModuleTree,
174 db: &impl HirDatabase,
175 ) -> Vec<(SyntaxNode, Problem)> {
176 tree.mods[self]
177 .children
178 .iter()
179 .filter_map(|&link| {
180 let p = tree.links[link].problem.clone()?;
181 let s = link.source(tree, db);
182 let s = s.borrowed().name().unwrap().syntax().owned();
183 Some((s, p))
184 })
185 .collect()
186 }
187}
188
189impl LinkId {
190 pub(crate) fn owner(self, tree: &ModuleTree) -> ModuleId {
191 tree.links[self].owner
192 }
193 pub(crate) fn name(self, tree: &ModuleTree) -> &Name {
194 &tree.links[self].name
195 }
196 pub(crate) fn source(self, tree: &ModuleTree, db: &impl HirDatabase) -> ast::ModuleNode {
197 let syntax_node = db.file_item(tree.links[self].source);
198 ast::ModuleNode::cast(syntax_node.borrowed())
199 .unwrap()
200 .owned()
201 }
202}
203
204impl ModuleTree {
205 fn push_mod(&mut self, data: ModuleData) -> ModuleId {
206 self.mods.alloc(data)
207 }
208 fn push_link(&mut self, data: LinkData) -> LinkId {
209 let owner = data.owner;
210 let id = self.links.alloc(data);
211 self.mods[owner].children.push(id);
212 id
213 }
214}
215
216fn modules<'a>(
217 root: impl ast::ModuleItemOwner<'a>,
218) -> impl Iterator<Item = (Name, ast::Module<'a>)> {
219 root.items()
220 .filter_map(|item| match item {
221 ast::ModuleItem::Module(m) => Some(m),
222 _ => None,
223 })
224 .filter_map(|module| {
225 let name = module.name()?.as_name();
226 Some((name, module))
227 })
228}
229
230fn create_module_tree<'a>(
231 db: &impl HirDatabase,
232 source_root: SourceRootId,
233) -> Cancelable<ModuleTree> {
234 let mut tree = ModuleTree::default();
235
236 let mut roots = FxHashMap::default();
237 let mut visited = FxHashSet::default();
238
239 let source_root = db.source_root(source_root);
240 for &file_id in source_root.files.values() {
241 let source = SourceItemId {
242 file_id: file_id.into(),
243 item_id: None,
244 };
245 if visited.contains(&source) {
246 continue; // TODO: use explicit crate_roots here
247 }
248 assert!(!roots.contains_key(&file_id));
249 let module_id = build_subtree(
250 db,
251 &source_root,
252 &mut tree,
253 &mut visited,
254 &mut roots,
255 None,
256 source,
257 )?;
258 roots.insert(file_id, module_id);
259 }
260 Ok(tree)
261}
262
263fn build_subtree(
264 db: &impl HirDatabase,
265 source_root: &SourceRoot,
266 tree: &mut ModuleTree,
267 visited: &mut FxHashSet<SourceItemId>,
268 roots: &mut FxHashMap<FileId, ModuleId>,
269 parent: Option<LinkId>,
270 source: SourceItemId,
271) -> Cancelable<ModuleId> {
272 visited.insert(source);
273 let id = tree.push_mod(ModuleData {
274 source,
275 parent,
276 children: Vec::new(),
277 });
278 for sub in db.submodules(source)?.iter() {
279 let link = tree.push_link(LinkData {
280 source: sub.source,
281 name: sub.name.clone(),
282 owner: id,
283 points_to: Vec::new(),
284 problem: None,
285 });
286
287 let (points_to, problem) = if sub.is_declaration {
288 let (points_to, problem) = resolve_submodule(db, source.file_id, &sub.name);
289 let points_to = points_to
290 .into_iter()
291 .map(|file_id| match roots.remove(&file_id) {
292 Some(module_id) => {
293 tree.mods[module_id].parent = Some(link);
294 Ok(module_id)
295 }
296 None => build_subtree(
297 db,
298 source_root,
299 tree,
300 visited,
301 roots,
302 Some(link),
303 SourceItemId {
304 file_id: file_id.into(),
305 item_id: None,
306 },
307 ),
308 })
309 .collect::<Cancelable<Vec<_>>>()?;
310 (points_to, problem)
311 } else {
312 let points_to = build_subtree(
313 db,
314 source_root,
315 tree,
316 visited,
317 roots,
318 Some(link),
319 sub.source,
320 )?;
321 (vec![points_to], None)
322 };
323
324 tree.links[link].points_to = points_to;
325 tree.links[link].problem = problem;
326 }
327 Ok(id)
328}
329
330fn resolve_submodule(
331 db: &impl HirDatabase,
332 file_id: HirFileId,
333 name: &Name,
334) -> (Vec<FileId>, Option<Problem>) {
335 // FIXME: handle submodules of inline modules properly
336 let file_id = file_id.original_file(db);
337 let source_root_id = db.file_source_root(file_id);
338 let