diff options
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 @@ | |||
1 | use ra_syntax::ast::AstNode; | ||
2 | use hir::{Ty, Def}; | 1 | use hir::{Ty, Def}; |
3 | 2 | ||
4 | use crate::Cancelable; | 3 | use 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 | ||
43 | fn complete_fn(acc: &mut Completions, scopes: &hir::FnScopes, offset: TextUnit) { | 46 | fn 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; | |||
3 | use ra_syntax::{ | 3 | use 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 | ||
9 | use crate::{db::RootDatabase, RangeInfo, FilePosition, FileRange, NavigationTarget}; | 9 | use 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] |
35 | fn test_unresolved_module_diagnostic_no_diag_for_inline_mode() { | 36 | fn 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 | ||
9 | pub mod map; | ||
10 | |||
9 | #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] | 11 | #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] |
10 | pub struct RawId(u32); | 12 | pub 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 | |||
3 | use std::marker::PhantomData; | ||
4 | |||
5 | use 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)] | ||
9 | pub struct ArenaMap<ID, T> { | ||
10 | v: Vec<Option<T>>, | ||
11 | _ty: PhantomData<ID>, | ||
12 | } | ||
13 | |||
14 | impl<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 | |||
56 | impl<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 | |||
63 | impl<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 @@ | |||
1 | use ra_syntax::{SourceFileNode, SyntaxKind, SyntaxNode, SyntaxNodeRef, TextRange}; | 1 | use 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)] |
5 | pub struct LocalSyntaxPtr { | 5 | pub 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 @@ | |||
1 | use relative_path::RelativePathBuf; | ||
2 | use ra_db::{CrateId, Cancelable, FileId}; | ||
3 | use ra_syntax::{ast, SyntaxNode}; | ||
4 | |||
5 | use 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)] | ||
11 | pub struct Crate { | ||
12 | pub(crate) crate_id: CrateId, | ||
13 | } | ||
14 | |||
15 | #[derive(Debug)] | ||
16 | pub struct CrateDependency { | ||
17 | pub krate: Crate, | ||
18 | pub name: Name, | ||
19 | } | ||
20 | |||
21 | impl 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)] | ||
34 | pub struct Module { | ||
35 | pub(crate) def_id: DefId, | ||
36 | } | ||
37 | |||
38 | pub enum ModuleSource { | ||
39 | SourceFile(ast::SourceFileNode), | ||
40 | Module(ast::ModuleNode), | ||
41 | } | ||
42 | |||
43 | #[derive(Clone, Debug, Hash, PartialEq, Eq)] | ||
44 | pub enum Problem { | ||
45 | UnresolvedModule { | ||
46 | candidate: RelativePathBuf, | ||
47 | }, | ||
48 | NotDirOwner { | ||
49 | move_to: RelativePathBuf, | ||
50 | candidate: RelativePathBuf, | ||
51 | }, | ||
52 | } | ||
53 | |||
54 | impl 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 @@ | |||
1 | mod krate; // `crate` is invalid ident :( | ||
2 | mod 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 @@ | |||
1 | use ra_db::{CrateId, Cancelable}; | ||
2 | |||
3 | use crate::{ | ||
4 | HirFileId, Crate, CrateDependency, AsName, DefLoc, DefKind, Module, SourceItemId, | ||
5 | db::HirDatabase, | ||
6 | }; | ||
7 | |||
8 | impl 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 @@ | |||
1 | use ra_db::{Cancelable, SourceRootId, FileId}; | ||
2 | use ra_syntax::{ast, SyntaxNode, AstNode}; | ||
3 | |||
4 | use crate::{ | ||
5 | Module, ModuleSource, Problem, | ||
6 | Crate, DefId, DefLoc, DefKind, Name, Path, PathKind, PerNs, Def, ModuleId, | ||
7 | nameres::ModuleScope, | ||
8 | db::HirDatabase, | ||
9 | }; | ||
10 | |||
11 | impl 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 @@ | |||
1 | use std::ops::Index; | ||
2 | use std::sync::Arc; | ||
3 | |||
4 | use rustc_hash::FxHashMap; | ||
5 | |||
6 | use ra_arena::{Arena, RawId, impl_arena_id, map::ArenaMap}; | ||
7 | use ra_db::{LocalSyntaxPtr, Cancelable}; | ||
8 | use ra_syntax::ast::{self, AstNode, LoopBodyOwner, ArgListOwner, NameOwner}; | ||
9 | |||
10 | use crate::{Path, type_ref::{Mutability, TypeRef}, Name, HirDatabase, DefId, Def, name::AsName}; | ||
11 | |||
12 | #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] | ||
13 | pub struct ExprId(RawId); | ||
14 | impl_arena_id!(ExprId); | ||
15 | |||
16 | /// The body of an item (function, const etc.). | ||
17 | #[derive(Debug, Eq, PartialEq)] | ||
18 | pub 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)] | ||
39 | pub 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 | |||
47 | impl 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 | |||
57 | impl Index<ExprId> for Body { | ||
58 | type Output = Expr; | ||
59 | |||
60 | fn index(&self, expr: ExprId) -> &Expr { | ||
61 | &self.exprs[expr] | ||
62 | } | ||
63 | } | ||
64 | |||
65 | impl Index<PatId> for Body { | ||
66 | type Output = Pat; | ||
67 | |||
68 | fn index(&self, pat: PatId) -> &Pat { | ||
69 | &self.pats[pat] | ||
70 | } | ||
71 | } | ||
72 | |||
73 | impl 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)] | ||
103 | pub 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 | |||
184 | pub use ra_syntax::ast::PrefixOp as UnaryOp; | ||
185 | pub use ra_syntax::ast::BinOp as BinaryOp; | ||
186 | |||
187 | #[derive(Debug, Clone, Eq, PartialEq)] | ||
188 | pub struct MatchArm { | ||
189 | pub pats: Vec<PatId>, | ||
190 | // guard: Option<ExprId>, // TODO | ||
191 | pub expr: ExprId, | ||
192 | } | ||
193 | |||
194 | #[derive(Debug, Clone, Eq, PartialEq)] | ||
195 | pub struct StructLitField { | ||
196 | pub name: Name, | ||
197 | pub expr: ExprId, | ||
198 | } | ||
199 | |||
200 | #[derive(Debug, Clone, Eq, PartialEq)] | ||
201 | pub enum Statement { | ||
202 | Let { | ||
203 | pat: PatId, | ||
204 | type_ref: Option<TypeRef>, | ||
205 | initializer: Option<ExprId>, | ||
206 | }, | ||
207 | Expr(ExprId), | ||
208 | } | ||
209 | |||
210 | impl 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)] | ||
301 | pub struct PatId(RawId); | ||
302 | impl_arena_id!(PatId); | ||
303 | |||
304 | #[derive(Debug, Clone, Eq, PartialEq)] | ||
305 | pub enum Pat { | ||
306 | Missing, | ||
307 | Bind { | ||
308 | name: Name, | ||
309 | }, | ||
310 | TupleStruct { | ||
311 | path: Option<Path>, | ||
312 | args: Vec<PatId>, | ||
313 | }, | ||
314 | } | ||
315 | |||
316 | impl 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 | |||
329 | pub(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 | |||
333 | struct 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 | |||
342 | impl 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 | |||
713 | pub(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 | |||
752 | pub(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 | ||
14 | use crate::{DefId, DefKind, HirDatabase, ty::InferenceResult, Module, Crate, impl_block::ImplBlock}; | 14 | use crate::{DefId, DefKind, HirDatabase, ty::InferenceResult, Module, Crate, impl_block::ImplBlock, expr::{Body, BodySyntaxMapping}, type_ref::{TypeRef, Mutability}, Name}; |
15 | 15 | ||
16 | pub use self::scope::FnScopes; | 16 | pub use self::scope::{FnScopes, ScopesWithSyntaxMapping}; |
17 | 17 | ||
18 | #[derive(Debug, Clone, PartialEq, Eq)] | 18 | #[derive(Debug, Clone, PartialEq, Eq)] |
19 | pub struct Function { | 19 | pub 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)] | ||
85 | pub struct FnSignature { | ||
86 | args: Vec<TypeRef>, | ||
87 | ret_type: TypeRef, | ||
88 | } | ||
89 | |||
90 | impl 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 | |||
100 | pub(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)] |
67 | pub struct FnSignatureInfo { | 138 | pub 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 @@ | |||
1 | use std::sync::Arc; | ||
2 | |||
1 | use rustc_hash::{FxHashMap, FxHashSet}; | 3 | use rustc_hash::{FxHashMap, FxHashSet}; |
2 | 4 | ||
3 | use ra_syntax::{ | 5 | use 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 | }; |
8 | use ra_arena::{Arena, RawId, impl_arena_id}; | 10 | use ra_arena::{Arena, RawId, impl_arena_id}; |
9 | use ra_db::LocalSyntaxPtr; | 11 | use ra_db::LocalSyntaxPtr; |
10 | 12 | ||
11 | use crate::{Name, AsName}; | 13 | use 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)] |
14 | pub struct ScopeId(RawId); | 16 | pub struct ScopeId(RawId); |
@@ -16,15 +18,15 @@ impl_arena_id!(ScopeId); | |||
16 | 18 | ||
17 | #[derive(Debug, PartialEq, Eq)] | 19 | #[derive(Debug, PartialEq, Eq)] |
18 | pub struct FnScopes { | 20 | pub 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)] |
25 | pub struct ScopeEntry { | 27 | pub 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 | ||
36 | impl FnScopes { | 38 | impl 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)] | ||
109 | pub struct ScopesWithSyntaxMapping { | ||
110 | pub syntax_mapping: Arc<BodySyntaxMapping>, | ||
111 | pub scopes: Arc<FnScopes>, | ||
112 | } | ||
113 | |||
114 | #[derive(Debug, Clone, PartialEq, Eq)] | ||
115 | pub struct ScopeEntryWithSyntax { | ||
116 | name: Name, | ||
117 | ptr: LocalSyntaxPtr, | ||
118 | } | ||
119 | |||
120 | impl ScopeEntryWithSyntax { | ||
121 | pub fn name(&self) -> &Name { | ||
122 | &self.name | ||
123 | } | ||
124 | pub fn ptr(&self) -> LocalSyntaxPtr { | ||
125 | self.ptr | ||
126 | } | ||
127 | } | ||
128 | |||
129 | impl 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 | ||
173 | impl ScopeEntry { | 224 | impl 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 | ||
190 | fn compute_block_scopes(block: ast::Block, scopes: &mut FnScopes, mut scope: ScopeId) { | 233 | fn 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 | ||
219 | fn compute_expr_scopes(expr: ast::Expr, scopes: &mut FnScopes, scope: ScopeId) { | 263 | fn 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}; | |||
2 | use ra_syntax::{SourceFileNode, SyntaxKind, SyntaxNode, SyntaxNodeRef, SourceFile, AstNode, ast}; | 2 | use ra_syntax::{SourceFileNode, SyntaxKind, SyntaxNode, SyntaxNodeRef, SourceFile, AstNode, ast}; |
3 | use ra_arena::{Arena, RawId, impl_arena_id}; | 3 | use ra_arena::{Arena, RawId, impl_arena_id}; |
4 | 4 | ||
5 | use crate::{HirDatabase, PerNs, ModuleId, Module, Def, Function, Struct, Enum, ImplBlock, Crate}; | 5 | use crate::{HirDatabase, PerNs, ModuleId, Def, Function, Struct, Enum, ImplBlock, Crate}; |
6 | |||
7 | use 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 | ||
8 | use crate::{ | 8 | use 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 | ||
16 | use crate::code_model_api::{Module, ModuleSource}; | ||
17 | |||
16 | #[derive(Debug, Clone, PartialEq, Eq)] | 18 | #[derive(Debug, Clone, PartialEq, Eq)] |
17 | pub struct ImplBlock { | 19 | pub 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 @@ | |||
1 | pub use ra_db::{CrateId, Cancelable}; | ||
2 | |||
3 | use 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)] | ||
9 | pub struct Crate { | ||
10 | crate_id: CrateId, | ||
11 | } | ||
12 | |||
13 | #[derive(Debug)] | ||
14 | pub struct CrateDependency { | ||
15 | pub krate: Crate, | ||
16 | pub name: Name, | ||
17 | } | ||
18 | |||
19 | impl 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; | |||
24 | mod ids; | 24 | mod ids; |
25 | mod macros; | 25 | mod macros; |
26 | mod name; | 26 | mod name; |
27 | // can't use `crate` or `r#crate` here :( | 27 | mod module_tree; |
28 | mod krate; | 28 | mod nameres; |
29 | mod module; | ||
30 | mod function; | 29 | mod function; |
31 | mod adt; | 30 | mod adt; |
32 | mod type_ref; | 31 | mod type_ref; |
33 | mod ty; | 32 | mod ty; |
34 | mod impl_block; | 33 | mod impl_block; |
34 | mod expr; | ||
35 | |||
36 | mod code_model_api; | ||
37 | mod code_model_impl; | ||
35 | 38 | ||
36 | use crate::{ | 39 | use crate::{ |
37 | db::HirDatabase, | 40 | db::HirDatabase, |
@@ -42,11 +45,11 @@ use crate::{ | |||
42 | pub use self::{ | 45 | pub 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 | ||
55 | pub use self::function::FnSignatureInfo; | 58 | pub use self::function::FnSignatureInfo; |
56 | 59 | ||
60 | pub use self::code_model_api::{ | ||
61 | Crate, CrateDependency, | ||
62 | Module, ModuleSource, Problem, | ||
63 | }; | ||
64 | |||
57 | pub enum Def { | 65 | pub 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 @@ | |||
1 | pub(super) mod imp; | ||
2 | pub(super) mod nameres; | ||
3 | |||
4 | use std::sync::Arc; | ||
5 | use log; | ||
6 | |||
7 | use ra_syntax::{ | ||
8 | algo::generate, | ||
9 | ast::{self, AstNode, NameOwner}, | ||
10 | SyntaxNode, | ||
11 | }; | ||
12 | use ra_arena::{Arena, RawId, impl_arena_id}; | ||
13 | use ra_db::{SourceRootId, FileId, Cancelable}; | ||
14 | use relative_path::RelativePathBuf; | ||
15 | |||
16 | use crate::{ | ||
17 | Def, DefKind, DefLoc, DefId, | ||
18 | Name, Path, PathKind, HirDatabase, SourceItemId, SourceFileItemId, Crate, | ||
19 | HirFileId, | ||
20 | }; | ||
21 | |||
22 | pub 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)] | ||
27 | pub struct Module { | ||
28 | tree: Arc<ModuleTree>, | ||
29 | pub(crate) source_root_id: SourceRootId, | ||
30 | pub(crate) module_id: ModuleId, | ||
31 | } | ||
32 | |||
33 | impl 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)] | ||
192 | pub struct ModuleId(RawId); | ||
193 | impl_arena_id!(ModuleId); | ||
194 | |||
195 | #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] | ||
196 | pub struct LinkId(RawId); | ||
197 | impl_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)] | ||
207 | pub struct ModuleTree { | ||
208 | mods: Arena<ModuleId, ModuleData>, | ||
209 | links: Arena<LinkId, LinkData>, | ||
210 | } | ||
211 | |||
212 | impl 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)] | ||
227 | pub struct ModuleSource(SourceItemId); | ||
228 | |||
229 | /// An owned syntax node for a module. Unlike `ModuleSource`, | ||
230 | /// this holds onto the AST for the whole file. | ||
231 | pub(crate) enum ModuleSourceNode { | ||
232 | SourceFile(ast::SourceFileNode), | ||
233 | Module(ast::ModuleNode), | ||
234 | } | ||
235 | |||
236 | #[derive(Clone, Debug, Hash, PartialEq, Eq)] | ||
237 | pub enum Problem { | ||
238 | UnresolvedModule { | ||
239 | candidate: RelativePathBuf, | ||
240 | }, | ||
241 | NotDirOwner { | ||
242 | move_to: RelativePathBuf, | ||
243 | candidate: RelativePathBuf, | ||
244 | }, | ||
245 | } | ||
246 | |||
247 | impl 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 | |||
292 | impl 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)] | ||
315 | pub struct ModuleData { | ||
316 | source: ModuleSource, | ||
317 | parent: Option<LinkId>, | ||
318 | children: Vec<LinkId>, | ||
319 | } | ||
320 | |||
321 | impl 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)] | ||
359 | struct LinkData { | ||
360 | owner: ModuleId, | ||
361 | name: Name, | ||
362 | points_to: Vec<ModuleId>, | ||
363 | problem: Option<Problem>, | ||
364 | } | ||
365 | |||
366 | impl 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 @@ | |||
1 | use std::sync::Arc; | ||
2 | |||
3 | use ra_syntax::ast::{self, NameOwner}; | ||
4 | use relative_path::RelativePathBuf; | ||
5 | use rustc_hash::{FxHashMap, FxHashSet}; | ||
6 | use arrayvec::ArrayVec; | ||
7 | use ra_db::{SourceRoot, SourceRootId, Cancelable, FileId}; | ||
8 | |||
9 | use crate::{ | ||
10 | HirDatabase, Name, AsName, | ||
11 | }; | ||
12 | |||
13 | use super::{ | ||
14 | LinkData, LinkId, ModuleData, ModuleId, ModuleSource, | ||
15 | ModuleTree, Problem, | ||
16 | }; | ||
17 | |||
18 | #[derive(Clone, Hash, PartialEq, Eq, Debug)] | ||
19 | pub enum Submodule { | ||
20 | Declaration(Name), | ||
21 | Definition(Name, ModuleSource), | ||
22 | } | ||
23 | |||
24 | impl Submodule { | ||
25 | fn name(&self) -> &Name { | ||
26 | match self { | ||
27 | Submodule::Declaration(name) => name, | ||
28 | Submodule::Definition(name, _) => name, | ||
29 | } | ||
30 | } | ||
31 | } | ||
32 | |||
33 | pub(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 | |||
47 | pub(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 | |||
56 | fn 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 | |||
86 | fn 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 | |||
152 | fn 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 @@ | |||
1 | use std::sync::Arc; | ||
2 | |||
3 | use rustc_hash::{FxHashMap, FxHashSet}; | ||
4 | use arrayvec::ArrayVec; | ||
5 | use relative_path::RelativePathBuf; | ||
6 | use ra_db::{FileId, SourceRootId, Cancelable, SourceRoot}; | ||
7 | use ra_syntax::{ | ||
8 | algo::generate, | ||
9 | ast::{self, AstNode, NameOwner}, | ||
10 | SyntaxNode, | ||
11 | }; | ||
12 | use ra_arena::{Arena, RawId, impl_arena_id}; | ||
13 | |||
14 | use crate::{Name, AsName, HirDatabase, SourceItemId, HirFileId, Problem, SourceFileItems, ModuleSource}; | ||
15 | |||
16 | impl 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)] | ||
35 | pub struct Submodule { | ||
36 | name: Name, | ||
37 | is_declaration: bool, | ||
38 | source: SourceItemId, | ||
39 | } | ||
40 | |||
41 | impl 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)] | ||
81 | pub struct ModuleId(RawId); | ||
82 | impl_arena_id!(ModuleId); | ||
83 | |||
84 | #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] | ||
85 | pub struct LinkId(RawId); | ||
86 | impl_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)] | ||
96 | pub struct ModuleTree { | ||
97 | mods: Arena<ModuleId, ModuleData>, | ||
98 | links: Arena<LinkId, LinkData>, | ||
99 | } | ||
100 | |||
101 | #[derive(Debug, PartialEq, Eq, Hash)] | ||
102 | pub struct ModuleData { | ||
103 | source: SourceItemId, | ||
104 | parent: Option<LinkId>, | ||
105 | children: Vec<LinkId>, | ||
106 | } | ||
107 | |||
108 | #[derive(Hash, Debug, PartialEq, Eq)] | ||
109 | struct LinkData { | ||
110 | source: SourceItemId, | ||
111 | owner: ModuleId, | ||
112 | name: Name, | ||
113 | points_to: Vec<ModuleId>, | ||
114 | problem: Option<Problem>, | ||
115 | } | ||
116 | |||
117 | impl 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 | |||
137 | impl 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 | |||
189 | impl 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 | |||
204 | impl 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 | |||
216 | fn 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 | |||
230 | fn 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 | |||
263 | fn 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 | |||
330 | fn 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 path = db.file_relative_path(file_id); | ||
339 | let root = RelativePathBuf::default(); | ||
340 | let dir_path = path.parent().unwrap_or(&root); | ||
341 | let mod_name = path.file_stem().unwrap_or("unknown"); | ||
342 | let is_dir_owner = mod_name == "mod" || mod_name == "lib" || mod_name == "main"; | ||
343 | |||
344 | let file_mod = dir_path.join(format!("{}.rs", name)); | ||
345 | let dir_mod = dir_path.join(format!("{}/mod.rs", name)); | ||
346 | let file_dir_mod = dir_path.join(format!("{}/{}.rs", mod_name, name)); | ||
347 | let mut candidates = ArrayVec::<[_; 2]>::new(); | ||
348 | if is_dir_owner { | ||
349 | candidates.push(file_mod.clone()); | ||
350 | candidates.push(dir_mod); | ||
351 | } else { | ||
352 | candidates.push(file_dir_mod.clone()); | ||
353 | }; | ||
354 | let sr = db.source_root(source_root_id); | ||
355 | let points_to = candidates | ||
356 | .into_iter() | ||
357 | .filter_map(|path| sr.files.get(&path)) | ||
358 | .map(|&it| it) | ||
359 | .collect::<Vec<_>>(); | ||
360 | let problem = if points_to.is_empty() { | ||
361 | Some(Problem::UnresolvedModule { | ||
362 | candidate: if is_dir_owner { file_mod } else { file_dir_mod }, | ||
363 | }) | ||
364 | } else { | ||
365 | None | ||
366 | }; | ||
367 | (points_to, problem) | ||
368 | } | ||
diff --git a/crates/ra_hir/src/name.rs b/crates/ra_hir/src/name.rs index 017caf442..90229bc54 100644 --- a/crates/ra_hir/src/name.rs +++ b/crates/ra_hir/src/name.rs | |||
@@ -31,6 +31,14 @@ impl Name { | |||
31 | Name::new("[missing name]".into()) | 31 | Name::new("[missing name]".into()) |
32 | } | 32 | } |
33 | 33 | ||
34 | pub(crate) fn self_param() -> Name { | ||
35 | Name::new("self".into()) | ||
36 | } | ||
37 | |||
38 | pub(crate) fn self_type() -> Name { | ||
39 | Name::new("Self".into()) | ||
40 | } | ||
41 | |||
34 | pub(crate) fn tuple_field_name(idx: usize) -> Name { | 42 | pub(crate) fn tuple_field_name(idx: usize) -> Name { |
35 | Name::new(idx.to_string().into()) | 43 | Name::new(idx.to_string().into()) |
36 | } | 44 | } |
@@ -51,7 +59,8 @@ impl Name { | |||
51 | "u128" => KnownName::U128, | 59 | "u128" => KnownName::U128, |
52 | "f32" => KnownName::F32, | 60 | "f32" => KnownName::F32, |
53 | "f64" => KnownName::F64, | 61 | "f64" => KnownName::F64, |
54 | "Self" => KnownName::Self_, | 62 | "Self" => KnownName::SelfType, |
63 | "self" => KnownName::SelfParam, | ||
55 | _ => return None, | 64 | _ => return None, |
56 | }; | 65 | }; |
57 | Some(name) | 66 | Some(name) |
@@ -104,5 +113,6 @@ pub(crate) enum KnownName { | |||
104 | F32, | 113 | F32, |
105 | F64, | 114 | F64, |
106 | 115 | ||
107 | Self_, | 116 | SelfType, |
117 | SelfParam, | ||
108 | } | 118 | } |
diff --git a/crates/ra_hir/src/module/nameres.rs b/crates/ra_hir/src/nameres.rs index 3c6851a0a..9a412bc82 100644 --- a/crates/ra_hir/src/module/nameres.rs +++ b/crates/ra_hir/src/nameres.rs | |||
@@ -31,7 +31,7 @@ use crate::{ | |||
31 | Path, PathKind, | 31 | Path, PathKind, |
32 | HirDatabase, Crate, | 32 | HirDatabase, Crate, |
33 | Name, AsName, | 33 | Name, AsName, |
34 | module::{Module, ModuleId, ModuleTree}, | 34 | module_tree::{ModuleId, ModuleTree}, |
35 | }; | 35 | }; |
36 | 36 | ||
37 | /// Item map is the result of the name resolution. Item map contains, for each | 37 | /// Item map is the result of the name resolution. Item map contains, for each |
@@ -177,11 +177,11 @@ impl<T> PerNs<T> { | |||
177 | } | 177 | } |
178 | 178 | ||
179 | pub fn take_types(self) -> Option<T> { | 179 | pub fn take_types(self) -> Option<T> { |
180 | self.types | 180 | self.take(Namespace::Types) |
181 | } | 181 | } |
182 | 182 | ||
183 | pub fn take_values(self) -> Option<T> { | 183 | pub fn take_values(self) -> Option<T> { |
184 | self.values | 184 | self.take(Namespace::Values) |
185 | } | 185 | } |
186 | 186 | ||
187 | pub fn get(&self, namespace: Namespace) -> Option<&T> { | 187 | pub fn get(&self, namespace: Namespace) -> Option<&T> { |
@@ -339,14 +339,14 @@ where | |||
339 | // Populate extern crates prelude | 339 | // Populate extern crates prelude |
340 | { | 340 | { |
341 | let root_id = module_id.crate_root(&self.module_tree); | 341 | let root_id = module_id.crate_root(&self.module_tree); |
342 | let file_id = root_id.source(&self.module_tree).file_id(); | 342 | let file_id = root_id.source(&self.module_tree).file_id; |
343 | let crate_graph = self.db.crate_graph(); | 343 | let crate_graph = self.db.crate_graph(); |
344 | if let Some(crate_id) = crate_graph.crate_id_for_crate_root(file_id.as_original_file()) | 344 | if let Some(crate_id) = crate_graph.crate_id_for_crate_root(file_id.as_original_file()) |
345 | { | 345 | { |
346 | let krate = Crate::new(crate_id); | 346 | let krate = Crate::new(crate_id); |
347 | for dep in krate.dependencies(self.db) { | 347 | for dep in krate.dependencies(self.db)? { |
348 | if let Some(module) = dep.krate.root_module(self.db)? { | 348 | if let Some(module) = dep.krate.root_module(self.db)? { |
349 | let def_id = module.def_id(self.db); | 349 | let def_id = module.def_id; |
350 | self.add_module_item( | 350 | self.add_module_item( |
351 | &mut module_items, | 351 | &mut module_items, |
352 | dep.name.clone(), | 352 | dep.name.clone(), |
@@ -399,7 +399,7 @@ where | |||
399 | kind: DefKind::Module, | 399 | kind: DefKind::Module, |
400 | source_root_id: self.source_root, | 400 | source_root_id: self.source_root, |
401 | module_id, | 401 | module_id, |
402 | source_item_id: module_id.source(&self.module_tree).0, | 402 | source_item_id: module_id.source(&self.module_tree), |
403 | }; | 403 | }; |
404 | let def_id = def_loc.id(self.db); | 404 | let def_id = def_loc.id(self.db); |
405 | self.add_module_item(&mut module_items, name, PerNs::types(def_id)); | 405 | self.add_module_item(&mut module_items, name, PerNs::types(def_id)); |
@@ -466,7 +466,7 @@ where | |||
466 | if source_root_id == self.source_root { | 466 | if source_root_id == self.source_root { |
467 | target_module_id | 467 | target_module_id |
468 | } else { | 468 | } else { |
469 | let module = Module::new(self.db, source_root_id, target_module_id)?; | 469 | let module = crate::code_model_api::Module::new(type_def_id); |
470 | let path = Path { | 470 | let path = Path { |
471 | segments: import.path.segments[i + 1..].iter().cloned().collect(), | 471 | segments: import.path.segments[i + 1..].iter().cloned().collect(), |
472 | kind: PathKind::Crate, | 472 | kind: PathKind::Crate, |
diff --git a/crates/ra_hir/src/module/nameres/tests.rs b/crates/ra_hir/src/nameres/tests.rs index a6a0bea31..dcbe65aec 100644 --- a/crates/ra_hir/src/module/nameres/tests.rs +++ b/crates/ra_hir/src/nameres/tests.rs | |||
@@ -17,7 +17,7 @@ fn item_map(fixture: &str) -> (Arc<hir::ItemMap>, hir::ModuleId) { | |||
17 | let module = hir::source_binder::module_from_position(&db, pos) | 17 | let module = hir::source_binder::module_from_position(&db, pos) |
18 | .unwrap() | 18 | .unwrap() |
19 | .unwrap(); | 19 | .unwrap(); |
20 | let module_id = module.module_id; | 20 | let module_id = module.def_id.loc(&db).module_id; |
21 | (db.item_map(source_root).unwrap(), module_id) | 21 | (db.item_map(source_root).unwrap(), module_id) |
22 | } | 22 | } |
23 | 23 | ||
@@ -155,7 +155,7 @@ fn item_map_across_crates() { | |||
155 | let module = hir::source_binder::module_from_file_id(&db, main_id) | 155 | let module = hir::source_binder::module_from_file_id(&db, main_id) |
156 | .unwrap() | 156 | .unwrap() |
157 | .unwrap(); | 157 | .unwrap(); |
158 | let module_id = module.module_id; | 158 | let module_id = module.def_id.loc(&db).module_id; |
159 | let item_map = db.item_map(source_root).unwrap(); | 159 | let item_map = db.item_map(source_root).unwrap(); |
160 | 160 | ||
161 | check_module_item_map( | 161 | check_module_item_map( |
diff --git a/crates/ra_hir/src/path.rs b/crates/ra_hir/src/path.rs index 9fdfa0d13..dcf4cf8b6 100644 --- a/crates/ra_hir/src/path.rs +++ b/crates/ra_hir/src/path.rs | |||
@@ -65,6 +65,11 @@ impl Path { | |||
65 | } | 65 | } |
66 | } | 66 | } |
67 | 67 | ||
68 | /// Converts an `ast::NameRef` into a single-identifier `Path`. | ||
69 | pub fn from_name_ref(name_ref: ast::NameRef) -> Path { | ||
70 | name_ref.as_name().into() | ||
71 | } | ||
72 | |||
68 | /// `true` is this path is a single identifier, like `foo` | 73 | /// `true` is this path is a single identifier, like `foo` |
69 | pub fn is_ident(&self) -> bool { | 74 | pub fn is_ident(&self) -> bool { |
70 | self.kind == PathKind::Plain && self.segments.len() == 1 | 75 | self.kind == PathKind::Plain && self.segments.len() == 1 |
@@ -84,6 +89,15 @@ impl Path { | |||
84 | } | 89 | } |
85 | } | 90 | } |
86 | 91 | ||
92 | impl From<Name> for Path { | ||
93 | fn from(name: Name) -> Path { | ||
94 | Path { | ||
95 | kind: PathKind::Plain, | ||
96 | segments: vec![name], | ||
97 | } | ||
98 | } | ||
99 | } | ||
100 | |||
87 | fn expand_use_tree( | 101 | fn expand_use_tree( |
88 | prefix: Option<Path>, | 102 | prefix: Option<Path>, |
89 | tree: ast::UseTree, | 103 | tree: ast::UseTree, |
diff --git a/crates/ra_hir/src/query_definitions.rs b/crates/ra_hir/src/query_definitions.rs index a5d99beda..8f2c40669 100644 --- a/crates/ra_hir/src/query_definitions.rs +++ b/crates/ra_hir/src/query_definitions.rs | |||
@@ -6,28 +6,24 @@ use std::{ | |||
6 | use rustc_hash::FxHashMap; | 6 | use rustc_hash::FxHashMap; |
7 | use ra_syntax::{ | 7 | use ra_syntax::{ |
8 | AstNode, SyntaxNode, | 8 | AstNode, SyntaxNode, |
9 | ast::{self, NameOwner, ModuleItemOwner} | 9 | ast::{self, ModuleItemOwner} |
10 | }; | 10 | }; |
11 | use ra_db::{SourceRootId, Cancelable,}; | 11 | use ra_db::{SourceRootId, Cancelable,}; |
12 | 12 | ||
13 | use crate::{ | 13 | use crate::{ |
14 | SourceFileItems, SourceItemId, DefKind, Function, DefId, Name, AsName, HirFileId, | 14 | SourceFileItems, SourceItemId, DefKind, DefId, HirFileId, ModuleSource, |
15 | MacroCallLoc, | 15 | MacroCallLoc, |
16 | db::HirDatabase, | 16 | db::HirDatabase, |
17 | function::FnScopes, | 17 | function::FnScopes, |
18 | module::{ | 18 | module_tree::ModuleId, |
19 | ModuleSource, ModuleSourceNode, ModuleId, | 19 | nameres::{InputModuleItems, ItemMap, Resolver}, |
20 | imp::Submodule, | ||
21 | nameres::{InputModuleItems, ItemMap, Resolver}, | ||
22 | }, | ||
23 | adt::{StructData, EnumData}, | 20 | adt::{StructData, EnumData}, |
24 | }; | 21 | }; |
25 | 22 | ||
26 | pub(super) fn fn_scopes(db: &impl HirDatabase, def_id: DefId) -> Arc<FnScopes> { | 23 | pub(super) fn fn_scopes(db: &impl HirDatabase, def_id: DefId) -> Cancelable<Arc<FnScopes>> { |
27 | let function = Function::new(def_id); | 24 | let body = db.body_hir(def_id)?; |
28 | let syntax = function.syntax(db); | 25 | let res = FnScopes::new(body); |
29 | let res = FnScopes::new(syntax.borrowed()); | 26 | Ok(Arc::new(res)) |
30 | Arc::new(res) | ||
31 | } | 27 | } |
32 | 28 | ||
33 | pub(super) fn struct_data(db: &impl HirDatabase, def_id: DefId) -> Cancelable<Arc<StructData>> { | 29 | pub(super) fn struct_data(db: &impl HirDatabase, def_id: DefId) -> Cancelable<Arc<StructData>> { |
@@ -62,54 +58,6 @@ pub(super) fn file_item(db: &impl HirDatabase, source_item_id: SourceItemId) -> | |||
62 | } | 58 | } |
63 | } | 59 | } |
64 | 60 | ||
65 | pub(crate) fn submodules( | ||
66 | db: &impl HirDatabase, | ||
67 | source: ModuleSource, | ||
68 | ) -> Cancelable<Arc<Vec<Submodule>>> { | ||
69 | db.check_canceled()?; | ||
70 | let file_id = source.file_id(); | ||
71 | let submodules = match source.resolve(db) { | ||
72 | ModuleSourceNode::SourceFile(it) => collect_submodules(db, file_id, it.borrowed()), | ||
73 | ModuleSourceNode::Module(it) => it | ||
74 | .borrowed() | ||
75 | .item_list() | ||
76 | .map(|it| collect_submodules(db, file_id, it)) | ||
77 | .unwrap_or_else(Vec::new), | ||
78 | }; | ||
79 | return Ok(Arc::new(submodules)); | ||
80 | |||
81 | fn collect_submodules<'a>( | ||
82 | db: &impl HirDatabase, | ||
83 | file_id: HirFileId, | ||
84 | root: impl ast::ModuleItemOwner<'a>, | ||
85 | ) -> Vec<Submodule> { | ||
86 | modules(root) | ||
87 | .map(|(name, m)| { | ||
88 | if m.has_semi() { | ||
89 | Submodule::Declaration(name) | ||
90 | } else { | ||
91 | let src = ModuleSource::new_inline(db, file_id, m); | ||
92 | Submodule::Definition(name, src) | ||
93 | } | ||
94 | }) | ||
95 | .collect() | ||
96 | } | ||
97 | } | ||
98 | |||
99 | pub(crate) fn modules<'a>( | ||
100 | root: impl ast::ModuleItemOwner<'a>, | ||
101 | ) -> impl Iterator<Item = (Name, ast::Module<'a>)> { | ||
102 | root.items() | ||
103 | .filter_map(|item| match item { | ||
104 | ast::ModuleItem::Module(m) => Some(m), | ||
105 | _ => None, | ||
106 | }) | ||
107 | .filter_map(|module| { | ||
108 | let name = module.name()?.as_name(); | ||
109 | Some((name, module)) | ||
110 | }) | ||
111 | } | ||
112 | |||
113 | pub(super) fn input_module_items( | 61 | pub(super) fn input_module_items( |
114 | db: &impl HirDatabase, | 62 | db: &impl HirDatabase, |
115 | source_root_id: SourceRootId, | 63 | source_root_id: SourceRootId, |
@@ -117,7 +65,8 @@ pub(super) fn input_module_items( | |||
117 | ) -> Cancelable<Arc<InputModuleItems>> { | 65 | ) -> Cancelable<Arc<InputModuleItems>> { |
118 | let module_tree = db.module_tree(source_root_id)?; | 66 | let module_tree = db.module_tree(source_root_id)?; |
119 | let source = module_id.source(&module_tree); | 67 | let source = module_id.source(&module_tree); |
120 | let file_id = source.file_id(); | 68 | let file_id = source.file_id; |
69 | let source = ModuleSource::from_source_item_id(db, source); | ||
121 | let file_items = db.file_items(file_id); | 70 | let file_items = db.file_items(file_id); |
122 | let fill = |acc: &mut InputModuleItems, items: &mut Iterator<Item = ast::ItemOrMacro>| { | 71 | let fill = |acc: &mut InputModuleItems, items: &mut Iterator<Item = ast::ItemOrMacro>| { |
123 | for item in items { | 72 | for item in items { |
@@ -148,9 +97,9 @@ pub(super) fn input_module_items( | |||
148 | }; | 97 | }; |
149 | 98 | ||
150 | let mut res = InputModuleItems::default(); | 99 | let mut res = InputModuleItems::default(); |
151 | match source.resolve(db) { | 100 | match source { |
152 | ModuleSourceNode::SourceFile(it) => fill(&mut res, &mut it.borrowed().items_with_macros()), | 101 | ModuleSource::SourceFile(it) => fill(&mut res, &mut it.borrowed().items_with_macros()), |
153 | ModuleSourceNode::Module(it) => { | 102 | ModuleSource::Module(it) => { |
154 | if let Some(item_list) = it.borrowed().item_list() { | 103 | if let Some(item_list) = it.borrowed().item_list() { |
155 | fill(&mut res, &mut item_list.items_with_macros()) | 104 | fill(&mut res, &mut item_list.items_with_macros()) |
156 | } | 105 | } |
diff --git a/crates/ra_hir/src/source_binder.rs b/crates/ra_hir/src/source_binder.rs index 85bd84469..4c14650c0 100644 --- a/crates/ra_hir/src/source_binder.rs +++ b/crates/ra_hir/src/source_binder.rs | |||
@@ -13,14 +13,16 @@ use ra_syntax::{ | |||
13 | }; | 13 | }; |
14 | 14 | ||
15 | use crate::{ | 15 | use crate::{ |
16 | HirDatabase, Module, Function, SourceItemId, | 16 | HirDatabase, Function, SourceItemId, |
17 | module::ModuleSource, | 17 | DefKind, DefLoc, AsName, Module, |
18 | DefKind, DefLoc, AsName, | ||
19 | }; | 18 | }; |
20 | 19 | ||
21 | /// Locates the module by `FileId`. Picks topmost module in the file. | 20 | /// Locates the module by `FileId`. Picks topmost module in the file. |
22 | pub fn module_from_file_id(db: &impl HirDatabase, file_id: FileId) -> Cancelable<Option<Module>> { | 21 | pub fn module_from_file_id(db: &impl HirDatabase, file_id: FileId) -> Cancelable<Option<Module>> { |
23 | let module_source = ModuleSource::new_file(file_id.into()); | 22 | let module_source = SourceItemId { |
23 | file_id: file_id.into(), | ||
24 | item_id: None, | ||
25 | }; | ||
24 | module_from_source(db, module_source) | 26 | module_from_source(db, module_source) |
25 | } | 27 | } |
26 | 28 | ||
@@ -34,7 +36,7 @@ pub fn module_from_declaration( | |||
34 | let child_name = decl.name(); | 36 | let child_name = decl.name(); |
35 | match (parent_module, child_name) { | 37 | match (parent_module, child_name) { |
36 | (Some(parent_module), Some(child_name)) => { | 38 | (Some(parent_module), Some(child_name)) => { |
37 | if let Some(child) = parent_module.child(&child_name.as_name()) { | 39 | if let Some(child) = parent_module.child(db, &child_name.as_name())? { |
38 | return Ok(Some(child)); | 40 | return Ok(Some(child)); |
39 | } | 41 | } |
40 | } | 42 | } |
@@ -49,11 +51,26 @@ pub fn module_from_position( | |||
49 | position: FilePosition, | 51 | position: FilePosition, |
50 | ) -> Cancelable<Option<Module>> { | 52 | ) -> Cancelable<Option<Module>> { |
51 | let file = db.source_file(position.file_id); | 53 | let file = db.source_file(position.file_id); |
52 | let module_source = match find_node_at_offset::<ast::Module>(file.syntax(), position.offset) { | 54 | match find_node_at_offset::<ast::Module>(file.syntax(), position.offset) { |
53 | Some(m) if !m.has_semi() => ModuleSource::new_inline(db, position.file_id.into(), m), | 55 | Some(m) if !m.has_semi() => module_from_inline(db, position.file_id.into(), m), |
54 | _ => ModuleSource::new_file(position.file_id.into()), | 56 | _ => module_from_file_id(db, position.file_id.into()), |
57 | } | ||
58 | } | ||
59 | |||
60 | fn module_from_inline( | ||
61 | db: &impl HirDatabase, | ||
62 | file_id: FileId, | ||
63 | module: ast::Module, | ||
64 | ) -> Cancelable<Option<Module>> { | ||
65 | assert!(!module.has_semi()); | ||
66 | let file_id = file_id.into(); | ||
67 | let file_items = db.file_items(file_id); | ||
68 | let item_id = file_items.id_of(file_id, module.syntax()); | ||
69 | let source = SourceItemId { | ||
70 | file_id, | ||
71 | item_id: Some(item_id), | ||
55 | }; | 72 | }; |
56 | module_from_source(db, module_source) | 73 | module_from_source(db, source) |
57 | } | 74 | } |
58 | 75 | ||
59 | /// Locates the module by child syntax element within the module | 76 | /// Locates the module by child syntax element within the module |
@@ -62,29 +79,34 @@ pub fn module_from_child_node( | |||
62 | file_id: FileId, | 79 | file_id: FileId, |
63 | child: SyntaxNodeRef, | 80 | child: SyntaxNodeRef, |
64 | ) -> Cancelable<Option<Module>> { | 81 | ) -> Cancelable<Option<Module>> { |
65 | let module_source = if let Some(m) = child | 82 | if let Some(m) = child |
66 | .ancestors() | 83 | .ancestors() |
67 | .filter_map(ast::Module::cast) | 84 | .filter_map(ast::Module::cast) |
68 | .find(|it| !it.has_semi()) | 85 | .find(|it| !it.has_semi()) |
69 | { | 86 | { |
70 | ModuleSource::new_inline(db, file_id.into(), m) | 87 | module_from_inline(db, file_id.into(), m) |
71 | } else { | 88 | } else { |
72 | ModuleSource::new_file(file_id.into()) | 89 | module_from_file_id(db, file_id.into()) |
73 | }; | 90 | } |
74 | module_from_source(db, module_source) | ||
75 | } | 91 | } |
76 | 92 | ||
77 | fn module_from_source( | 93 | fn module_from_source(db: &impl HirDatabase, source: SourceItemId) -> Cancelable<Option<Module>> { |
78 | db: &impl HirDatabase, | 94 | let source_root_id = db.file_source_root(source.file_id.as_original_file()); |
79 | module_source: ModuleSource, | ||
80 | ) -> Cancelable<Option<Module>> { | ||
81 | let source_root_id = db.file_source_root(module_source.file_id().as_original_file()); | ||
82 | let module_tree = db.module_tree(source_root_id)?; | 95 | let module_tree = db.module_tree(source_root_id)?; |
83 | let m = module_tree | 96 | let module_id = ctry!(module_tree.find_module_by_source(source)); |
84 | .modules_with_sources() | 97 | Ok(Some(Module::from_module_id(db, source_root_id, module_id)?)) |
85 | .find(|(_id, src)| src == &module_source); | 98 | } |
86 | let module_id = ctry!(m).0; | 99 | |
87 | Ok(Some(Module::new(db, source_root_id, module_id)?)) | 100 | pub fn function_from_position( |
101 | db: &impl HirDatabase, | ||
102 | position: FilePosition, | ||
103 | ) -> Cancelable<Option<Function>> { | ||
104 | let file = db.source_file(position.file_id); | ||
105 | let fn_def = ctry!(find_node_at_offset::<ast::FnDef>( | ||
106 | file.syntax(), | ||
107 | position.offset | ||
108 | )); | ||
109 | function_from_source(db, position.file_id, fn_def) | ||
88 | } | 110 | } |
89 | 111 | ||
90 | pub fn function_from_source( | 112 | pub fn function_from_source( |
@@ -102,7 +124,8 @@ pub fn function_from_module( | |||
102 | module: &Module, | 124 | module: &Module, |
103 | fn_def: ast::FnDef, | 125 | fn_def: ast::FnDef, |
104 | ) -> Function { | 126 | ) -> Function { |
105 | let file_id = module.source().file_id(); | 127 | let loc = module.def_id.loc(db); |
128 | let file_id = loc.source_item_id.file_id; | ||
106 | let file_items = db.file_items(file_id); | 129 | let file_items = db.file_items(file_id); |
107 | let item_id = file_items.id_of(file_id, fn_def.syntax()); | 130 | let item_id = file_items.id_of(file_id, fn_def.syntax()); |
108 | let source_item_id = SourceItemId { | 131 | let source_item_id = SourceItemId { |
@@ -111,8 +134,8 @@ pub fn function_from_module( | |||
111 | }; | 134 | }; |
112 | let def_loc = DefLoc { | 135 | let def_loc = DefLoc { |
113 | kind: DefKind::Function, | 136 | kind: DefKind::Function, |
114 | source_root_id: module.source_root_id, | 137 | source_root_id: loc.source_root_id, |
115 | module_id: module.module_id, | 138 | module_id: loc.module_id, |
116 | source_item_id, | 139 | source_item_id, |
117 | }; | 140 | }; |
118 | Function::new(def_loc.id(db)) | 141 | Function::new(def_loc.id(db)) |
@@ -135,7 +158,8 @@ pub fn macro_symbols( | |||
135 | Some(it) => it, | 158 | Some(it) => it, |
136 | None => return Ok(Vec::new()), | 159 | None => return Ok(Vec::new()), |
137 | }; | 160 | }; |
138 | let items = db.input_module_items(module.source_root_id, module.module_id)?; | 161 | let loc = module.def_id.loc(db); |
162 | let items = db.input_module_items(loc.source_root_id, loc.module_id)?; | ||
139 | let mut res = Vec::new(); | 163 | let mut res = Vec::new(); |
140 | 164 | ||
141 | for macro_call_id in items | 165 | for macro_call_id in items |
diff --git a/crates/ra_hir/src/ty.rs b/crates/ra_hir/src/ty.rs index e33762e0d..d57990cd2 100644 --- a/crates/ra_hir/src/ty.rs +++ b/crates/ra_hir/src/ty.rs | |||
@@ -17,24 +17,23 @@ mod primitive; | |||
17 | #[cfg(test)] | 17 | #[cfg(test)] |
18 | mod tests; | 18 | mod tests; |
19 | 19 | ||
20 | use std::ops::Index; | ||
20 | use std::sync::Arc; | 21 | use std::sync::Arc; |
21 | use std::{fmt, mem}; | 22 | use std::{fmt, mem}; |
22 | 23 | ||
23 | use log; | 24 | use log; |
24 | use rustc_hash::FxHashMap; | ||
25 | use ena::unify::{InPlaceUnificationTable, UnifyKey, UnifyValue, NoError}; | 25 | use ena::unify::{InPlaceUnificationTable, UnifyKey, UnifyValue, NoError}; |
26 | use ra_arena::map::ArenaMap; | ||
26 | 27 | ||
27 | use ra_db::{LocalSyntaxPtr, Cancelable}; | 28 | use ra_db::Cancelable; |
28 | use ra_syntax::{ | ||
29 | ast::{self, AstNode, LoopBodyOwner, ArgListOwner, PrefixOp}, | ||
30 | SyntaxNodeRef | ||
31 | }; | ||
32 | 29 | ||
33 | use crate::{ | 30 | use crate::{ |
34 | Def, DefId, FnScopes, Module, Function, Struct, Enum, Path, Name, AsName, ImplBlock, | 31 | Def, DefId, Module, Function, Struct, Enum, Path, Name, ImplBlock, |
32 | FnSignature, FnScopes, | ||
35 | db::HirDatabase, | 33 | db::HirDatabase, |
36 | type_ref::{TypeRef, Mutability}, | 34 | type_ref::{TypeRef, Mutability}, |
37 | name::KnownName, | 35 | name::KnownName, |
36 | expr::{Body, Expr, ExprId, PatId, UnaryOp, BinaryOp, Statement}, | ||
38 | }; | 37 | }; |
39 | 38 | ||
40 | /// The ID of a type variable. | 39 | /// The ID of a type variable. |
@@ -81,9 +80,10 @@ impl UnifyValue for TypeVarValue { | |||
81 | match (value1, value2) { | 80 | match (value1, value2) { |
82 | // We should never equate two type variables, both of which have | 81 | // We should never equate two type variables, both of which have |
83 | // known types. Instead, we recursively equate those types. | 82 | // known types. Instead, we recursively equate those types. |
84 | (TypeVarValue::Known(..), TypeVarValue::Known(..)) => { | 83 | (TypeVarValue::Known(t1), TypeVarValue::Known(t2)) => panic!( |
85 | panic!("equating two type variables, both of which have known types") | 84 | "equating two type variables, both of which have known types: {:?} and {:?}", |
86 | } | 85 | t1, t2 |
86 | ), | ||
87 | 87 | ||
88 | // If one side is known, prefer that one. | 88 | // If one side is known, prefer that one. |
89 | (TypeVarValue::Known(..), TypeVarValue::Unknown) => Ok(value1.clone()), | 89 | (TypeVarValue::Known(..), TypeVarValue::Unknown) => Ok(value1.clone()), |
@@ -305,7 +305,7 @@ impl Ty { | |||
305 | return Ok(Ty::Uint(uint_ty)); | 305 | return Ok(Ty::Uint(uint_ty)); |
306 | } else if let Some(float_ty) = primitive::FloatTy::from_name(name) { | 306 | } else if let Some(float_ty) = primitive::FloatTy::from_name(name) { |
307 | return Ok(Ty::Float(float_ty)); | 307 | return Ok(Ty::Float(float_ty)); |
308 | } else if name.as_known_name() == Some(KnownName::Self_) { | 308 | } else if name.as_known_name() == Some(KnownName::SelfType) { |
309 | return Ty::from_hir_opt(db, module, None, impl_block.map(|i| i.target_type())); | 309 | return Ty::from_hir_opt(db, module, None, impl_block.map(|i| i.target_type())); |
310 | } | 310 | } |
311 | } | 311 | } |
@@ -320,26 +320,6 @@ impl Ty { | |||
320 | Ok(ty) | 320 | Ok(ty) |
321 | } | 321 | } |
322 | 322 | ||
323 | // TODO: These should not be necessary long-term, since everything will work on HIR | ||
324 | pub(crate) fn from_ast_opt( | ||
325 | db: &impl HirDatabase, | ||
326 | module: &Module, | ||
327 | impl_block: Option<&ImplBlock>, | ||
328 | node: Option<ast::TypeRef>, | ||
329 | ) -> Cancelable<Self> { | ||
330 | node.map(|n| Ty::from_ast(db, module, impl_block, n)) | ||
331 | .unwrap_or(Ok(Ty::Unknown)) | ||
332 | } | ||
333 | |||
334 | pub(crate) fn from_ast( | ||
335 | db: &impl HirDatabase, | ||
336 | module: &Module, | ||
337 | impl_block: Option<&ImplBlock>, | ||
338 | node: ast::TypeRef, | ||
339 | ) -> Cancelable<Self> { | ||
340 | Ty::from_hir(db, module, impl_block, &TypeRef::from_ast(node)) | ||
341 | } | ||
342 | |||
343 | pub fn unit() -> Self { | 323 | pub fn unit() -> Self { |
344 | Ty::Tuple(Arc::new([])) | 324 | Ty::Tuple(Arc::new([])) |
345 | } | 325 | } |
@@ -416,26 +396,18 @@ impl fmt::Display for Ty { | |||
416 | // Functions returning declared types for items | 396 | // Functions returning declared types for items |
417 | 397 | ||
418 | /// Compute the declared type of a function. This should not need to look at the | 398 | /// Compute the declared type of a function. This should not need to look at the |
419 | /// function body (but currently uses the function AST, so does anyway - TODO). | 399 | /// function body. |
420 | fn type_for_fn(db: &impl HirDatabase, f: Function) -> Cancelable<Ty> { | 400 | fn type_for_fn(db: &impl HirDatabase, f: Function) -> Cancelable<Ty> { |
421 | let syntax = f.syntax(db); | 401 | let signature = f.signature(db); |
422 | let module = f.module(db)?; | 402 | let module = f.module(db)?; |
423 | let impl_block = f.impl_block(db)?; | 403 | let impl_block = f.impl_block(db)?; |
424 | let node = syntax.borrowed(); | ||
425 | // TODO we ignore type parameters for now | 404 | // TODO we ignore type parameters for now |
426 | let input = node | 405 | let input = signature |
427 | .param_list() | 406 | .args() |
428 | .map(|pl| { | 407 | .iter() |
429 | pl.params() | 408 | .map(|tr| Ty::from_hir(db, &module, impl_block.as_ref(), tr)) |
430 | .map(|p| Ty::from_ast_opt(db, &module, impl_block.as_ref(), p.type_ref())) | 409 | .collect::<Cancelable<Vec<_>>>()?; |
431 | .collect() | 410 | let output = Ty::from_hir(db, &module, impl_block.as_ref(), signature.ret_type())?; |
432 | }) | ||
433 | .unwrap_or_else(|| Ok(Vec::new()))?; | ||
434 | let output = if let Some(type_ref) = node.ret_type().and_then(|rt| rt.type_ref()) { | ||
435 | Ty::from_ast(db, &module, impl_block.as_ref(), type_ref)? | ||
436 | } else { | ||
437 | Ty::unit() | ||
438 | }; | ||
439 | let sig = FnSig { input, output }; | 411 | let sig = FnSig { input, output }; |
440 | Ok(Ty::FnPtr(Arc::new(sig))) | 412 | Ok(Ty::FnPtr(Arc::new(sig))) |
441 | } | 413 | } |
@@ -498,16 +470,23 @@ pub(super) fn type_for_field(db: &impl HirDatabase, def_id: DefId, field: Name) | |||
498 | /// The result of type inference: A mapping from expressions and patterns to types. | 470 | /// The result of type inference: A mapping from expressions and patterns to types. |
499 | #[derive(Clone, PartialEq, Eq, Debug)] | 471 | #[derive(Clone, PartialEq, Eq, Debug)] |
500 | pub struct InferenceResult { | 472 | pub struct InferenceResult { |
501 | type_of: FxHashMap<LocalSyntaxPtr, Ty>, | 473 | type_of_expr: ArenaMap<ExprId, Ty>, |
474 | type_of_pat: ArenaMap<PatId, Ty>, | ||
502 | } | 475 | } |
503 | 476 | ||
504 | impl InferenceResult { | 477 | impl Index<ExprId> for InferenceResult { |
505 | /// Returns the type of the given syntax node, if it was inferred. Will | 478 | type Output = Ty; |
506 | /// return `None` for syntax nodes not in the inferred function or not | 479 | |
507 | /// pointing to an expression/pattern, `Some(Ty::Unknown)` for | 480 | fn index(&self, expr: ExprId) -> &Ty { |
508 | /// expressions/patterns that could not be inferred. | 481 | self.type_of_expr.get(expr).unwrap_or(&Ty::Unknown) |
509 | pub fn type_of_node(&self, node: SyntaxNodeRef) -> Option<Ty> { | 482 | } |
510 | self.type_of.get(&LocalSyntaxPtr::new(node)).cloned() | 483 | } |
484 | |||
485 | impl Index<PatId> for InferenceResult { | ||
486 | type Output = Ty; | ||
487 | |||
488 | fn index(&self, pat: PatId) -> &Ty { | ||
489 | self.type_of_pat.get(pat).unwrap_or(&Ty::Unknown) | ||
511 | } | 490 | } |
512 | } | 491 | } |
513 | 492 | ||
@@ -515,30 +494,46 @@ impl InferenceResult { | |||
515 | #[derive(Clone, Debug)] | 494 | #[derive(Clone, Debug)] |
516 | struct InferenceContext<'a, D: HirDatabase> { | 495 | struct InferenceContext<'a, D: HirDatabase> { |
517 | db: &'a D, | 496 | db: &'a D, |
497 | body: Arc<Body>, | ||
518 | scopes: Arc<FnScopes>, | 498 | scopes: Arc<FnScopes>, |
519 | /// The self param for the current method, if it exists. | ||
520 | self_param: Option<LocalSyntaxPtr>, | ||
521 | module: Module, | 499 | module: Module, |
522 | impl_block: Option<ImplBlock>, | 500 | impl_block: Option<ImplBlock>, |
523 | var_unification_table: InPlaceUnificationTable<TypeVarId>, | 501 | var_unification_table: InPlaceUnificationTable<TypeVarId>, |
524 | type_of: FxHashMap<LocalSyntaxPtr, Ty>, | 502 | type_of_expr: ArenaMap<ExprId, Ty>, |
503 | type_of_pat: ArenaMap<PatId, Ty>, | ||
525 | /// The return type of the function being inferred. | 504 | /// The return type of the function being inferred. |
526 | return_ty: Ty, | 505 | return_ty: Ty, |
527 | } | 506 | } |
528 | 507 | ||
508 | // helper function that determines whether a binary operator | ||
509 | // always returns a boolean | ||
510 | fn is_boolean_operator(op: BinaryOp) -> bool { | ||
511 | match op { | ||
512 | BinaryOp::BooleanOr | ||
513 | | BinaryOp::BooleanAnd | ||
514 | | BinaryOp::EqualityTest | ||
515 | | BinaryOp::LesserEqualTest | ||
516 | | BinaryOp::GreaterEqualTest | ||
517 | | BinaryOp::LesserTest | ||
518 | | BinaryOp::GreaterTest => true, | ||
519 | } | ||
520 | } | ||
521 | |||
529 | impl<'a, D: HirDatabase> InferenceContext<'a, D> { | 522 | impl<'a, D: HirDatabase> InferenceContext<'a, D> { |
530 | fn new( | 523 | fn new( |
531 | db: &'a D, | 524 | db: &'a D, |
525 | body: Arc<Body>, | ||
532 | scopes: Arc<FnScopes>, | 526 | scopes: Arc<FnScopes>, |
533 | module: Module, | 527 | module: Module, |
534 | impl_block: Option<ImplBlock>, | 528 | impl_block: Option<ImplBlock>, |
535 | ) -> Self { | 529 | ) -> Self { |
536 | InferenceContext { | 530 | InferenceContext { |
537 | type_of: FxHashMap::default(), | 531 | type_of_expr: ArenaMap::default(), |
532 | type_of_pat: ArenaMap::default(), | ||
538 | var_unification_table: InPlaceUnificationTable::new(), | 533 | var_unification_table: InPlaceUnificationTable::new(), |
539 | self_param: None, // set during parameter typing | ||
540 | return_ty: Ty::Unknown, // set in collect_fn_signature | 534 | return_ty: Ty::Unknown, // set in collect_fn_signature |
541 | db, | 535 | db, |
536 | body, | ||
542 | scopes, | 537 | scopes, |
543 | module, | 538 | module, |
544 | impl_block, | 539 | impl_block, |
@@ -546,24 +541,32 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { | |||
546 | } | 541 | } |
547 | 542 | ||
548 | fn resolve_all(mut self) -> InferenceResult { | 543 | fn resolve_all(mut self) -> InferenceResult { |
549 | let mut types = mem::replace(&mut self.type_of, FxHashMap::default()); | 544 | let mut expr_types = mem::replace(&mut self.type_of_expr, ArenaMap::default()); |
550 | for ty in types.values_mut() { | 545 | for ty in expr_types.values_mut() { |
546 | let resolved = self.resolve_ty_completely(mem::replace(ty, Ty::Unknown)); | ||
547 | *ty = resolved; | ||
548 | } | ||
549 | let mut pat_types = mem::replace(&mut self.type_of_pat, ArenaMap::default()); | ||
550 | for ty in pat_types.values_mut() { | ||
551 | let resolved = self.resolve_ty_completely(mem::replace(ty, Ty::Unknown)); | 551 | let resolved = self.resolve_ty_completely(mem::replace(ty, Ty::Unknown)); |
552 | *ty = resolved; | 552 | *ty = resolved; |
553 | } | 553 | } |
554 | InferenceResult { type_of: types } | 554 | InferenceResult { |
555 | type_of_expr: expr_types, | ||
556 | type_of_pat: pat_types, | ||
557 | } | ||
555 | } | 558 | } |
556 | 559 | ||
557 | fn write_ty(&mut self, node: SyntaxNodeRef, ty: Ty) { | 560 | fn write_expr_ty(&mut self, expr: ExprId, ty: Ty) { |
558 | self.type_of.insert(LocalSyntaxPtr::new(node), ty); | 561 | self.type_of_expr.insert(expr, ty); |
559 | } | 562 | } |
560 | 563 | ||
561 | fn make_ty(&self, type_ref: &TypeRef) -> Cancelable<Ty> { | 564 | fn write_pat_ty(&mut self, pat: PatId, ty: Ty) { |
562 | Ty::from_hir(self.db, &self.module, self.impl_block.as_ref(), type_ref) | 565 | self.type_of_pat.insert(pat, ty); |
563 | } | 566 | } |
564 | 567 | ||
565 | fn make_ty_opt(&self, type_ref: Option<&TypeRef>) -> Cancelable<Ty> { | 568 | fn make_ty(&self, type_ref: &TypeRef) -> Cancelable<Ty> { |
566 | Ty::from_hir_opt(self.db, &self.module, self.impl_block.as_ref(), type_ref) | 569 | Ty::from_hir(self.db, &self.module, self.impl_block.as_ref(), type_ref) |
567 | } | 570 | } |
568 | 571 | ||
569 | fn unify(&mut self, ty1: &Ty, ty2: &Ty) -> bool { | 572 | fn unify(&mut self, ty1: &Ty, ty2: &Ty) -> bool { |
@@ -658,23 +661,15 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { | |||
658 | }) | 661 | }) |
659 | } | 662 | } |
660 | 663 | ||
661 | fn infer_path_expr(&mut self, expr: ast::PathExpr) -> Cancelable<Option<Ty>> { | 664 | fn infer_path_expr(&mut self, expr: ExprId, path: &Path) -> Cancelable<Option<Ty>> { |
662 | let ast_path = ctry!(expr.path()); | 665 | if path.is_ident() || path.is_self() { |
663 | let path = ctry!(Path::from_ast(ast_path)); | ||
664 | if path.is_ident() { | ||
665 | // resolve locally | 666 | // resolve locally |
666 | let name = ctry!(ast_path.segment().and_then(|s| s.name_ref())); | 667 | let name = path.as_ident().cloned().unwrap_or_else(Name::self_param); |
667 | if let Some(scope_entry) = self.scopes.resolve_local_name(name) { | 668 | if let Some(scope_entry) = self.scopes.resolve_local_name(expr, name) { |
668 | let ty = ctry!(self.type_of.get(&scope_entry.ptr())); | 669 | let ty = ctry!(self.type_of_pat.get(scope_entry.pat())); |
669 | let ty = self.resolve_ty_as_possible(ty.clone()); | 670 | let ty = self.resolve_ty_as_possible(ty.clone()); |
670 | return Ok(Some(ty)); | 671 | return Ok(Some(ty)); |
671 | }; | 672 | }; |
672 | } else if path.is_self() { | ||
673 | // resolve `self` param | ||
674 | let self_param = ctry!(self.self_param); | ||
675 | let ty = ctry!(self.type_of.get(&self_param)); | ||
676 | let ty = self.resolve_ty_as_possible(ty.clone()); | ||
677 | return Ok(Some(ty)); | ||
678 | }; | 673 | }; |
679 | 674 | ||
680 | // resolve in module | 675 | // resolve in module |
@@ -684,8 +679,8 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { | |||
684 | Ok(Some(ty)) | 679 | Ok(Some(ty)) |
685 | } | 680 | } |
686 | 681 | ||
687 | fn resolve_variant(&self, path: Option<ast::Path>) -> Cancelable<(Ty, Option<DefId>)> { | 682 | fn resolve_variant(&self, path: Option<&Path>) -> Cancelable<(Ty, Option<DefId>)> { |
688 | let path = if let Some(path) = path.and_then(Path::from_ast) { | 683 | let path = if let Some(path) = path { |
689 | path | 684 | path |
690 | } else { | 685 | } else { |
691 | return Ok((Ty::Unknown, None)); | 686 | return Ok((Ty::Unknown, None)); |
@@ -704,74 +699,51 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { | |||
704 | }) | 699 | }) |
705 | } | 700 | } |
706 | 701 | ||
707 | fn infer_expr_opt( | 702 | fn infer_expr(&mut self, expr: ExprId, expected: &Expectation) -> Cancelable<Ty> { |
708 | &mut self, | 703 | let body = Arc::clone(&self.body); // avoid borrow checker problem |
709 | expr: Option<ast::Expr>, | 704 | let ty = match &body[expr] { |
710 | expected: &Expectation, | 705 | Expr::Missing => Ty::Unknown, |
711 | ) -> Cancelable<Ty> { | 706 | Expr::If { |
712 | if let Some(e) = expr { | 707 | condition, |
713 | self.infer_expr(e, expected) | 708 | then_branch, |
714 | } else { | 709 | else_branch, |
715 | Ok(Ty::Unknown) | 710 | } => { |
716 | } | 711 | // if let is desugared to match, so this is always simple if |
717 | } | 712 | self.infer_expr(*condition, &Expectation::has_type(Ty::Bool))?; |
718 | 713 | let then_ty = self.infer_expr(*then_branch, expected)?; | |
719 | fn infer_expr(&mut self, expr: ast::Expr, expected: &Expectation) -> Cancelable<Ty> { | 714 | if let Some(else_branch) = else_branch { |
720 | let ty = match expr { | 715 | self.infer_expr(*else_branch, expected)?; |
721 | ast::Expr::IfExpr(e) => { | ||
722 | if let Some(condition) = e.condition() { | ||
723 | let expected = if condition.pat().is_none() { | ||
724 | Expectation::has_type(Ty::Bool) | ||
725 | } else { | ||
726 | Expectation::none() | ||
727 | }; | ||
728 | self.infer_expr_opt(condition.expr(), &expected)?; | ||
729 | // TODO write type for pat | ||
730 | }; | ||
731 | let if_ty = self.infer_block_opt(e.then_branch(), expected)?; | ||
732 | if let Some(else_branch) = e.else_branch() { | ||
733 | self.infer_block(else_branch, expected)?; | ||
734 | } else { | 716 | } else { |
735 | // no else branch -> unit | 717 | // no else branch -> unit |
736 | self.unify(&expected.ty, &Ty::unit()); // actually coerce | 718 | self.unify(&expected.ty, &Ty::unit()); // actually coerce |
737 | } | 719 | } |
738 | if_ty | 720 | then_ty |
739 | } | 721 | } |
740 | ast::Expr::BlockExpr(e) => self.infer_block_opt(e.block(), expected)?, | 722 | Expr::Block { statements, tail } => self.infer_block(statements, *tail, expected)?, |
741 | ast::Expr::LoopExpr(e) => { | 723 | Expr::Loop { body } => { |
742 | self.infer_block_opt(e.loop_body(), &Expectation::has_type(Ty::unit()))?; | 724 | self.infer_expr(*body, &Expectation::has_type(Ty::unit()))?; |
743 | // TODO never, or the type of the break param | 725 | // TODO handle break with value |
744 | Ty::Unknown | 726 | Ty::Never |
745 | } | 727 | } |
746 | ast::Expr::WhileExpr(e) => { | 728 | Expr::While { condition, body } => { |
747 | if let Some(condition) = e.condition() { | 729 | // while let is desugared to a match loop, so this is always simple while |
748 | let expected = if condition.pat().is_none() { | 730 | self.infer_expr(*condition, &Expectation::has_type(Ty::Bool))?; |
749 | Expectation::has_type(Ty::Bool) | 731 | self.infer_expr(*body, &Expectation::has_type(Ty::unit()))?; |
750 | } else { | ||
751 | Expectation::none() | ||
752 | }; | ||
753 | self.infer_expr_opt(condition.expr(), &expected)?; | ||
754 | // TODO write type for pat | ||
755 | }; | ||
756 | self.infer_block_opt(e.loop_body(), &Expectation::has_type(Ty::unit()))?; | ||
757 | // TODO always unit? | ||
758 | Ty::unit() | 732 | Ty::unit() |
759 | } | 733 | } |
760 | ast::Expr::ForExpr(e) => { | 734 | Expr::For { iterable, body, .. } => { |
761 | let _iterable_ty = self.infer_expr_opt(e.iterable(), &Expectation::none()); | 735 | let _iterable_ty = self.infer_expr(*iterable, &Expectation::none()); |
762 | if let Some(_pat) = e.pat() { | 736 | // TODO write type for pat |
763 | // TODO write type for pat | 737 | self.infer_expr(*body, &Expectation::has_type(Ty::unit()))?; |
764 | } | ||
765 | self.infer_block_opt(e.loop_body(), &Expectation::has_type(Ty::unit()))?; | ||
766 | // TODO always unit? | ||
767 | Ty::unit() | 738 | Ty::unit() |
768 | } | 739 | } |
769 | ast::Expr::LambdaExpr(e) => { | 740 | Expr::Lambda { body, .. } => { |
770 | let _body_ty = self.infer_expr_opt(e.body(), &Expectation::none())?; | 741 | // TODO write types for args, infer lambda type etc. |
742 | let _body_ty = self.infer_expr(*body, &Expectation::none())?; | ||
771 | Ty::Unknown | 743 | Ty::Unknown |
772 | } | 744 | } |
773 | ast::Expr::CallExpr(e) => { | 745 | Expr::Call { callee, args } => { |
774 | let callee_ty = self.infer_expr_opt(e.expr(), &Expectation::none())?; | 746 | let callee_ty = self.infer_expr(*callee, &Expectation::none())?; |
775 | let (arg_tys, ret_ty) = match &callee_ty { | 747 | let (arg_tys, ret_ty) = match &callee_ty { |
776 | Ty::FnPtr(sig) => (&sig.input[..], sig.output.clone()), | 748 | Ty::FnPtr(sig) => (&sig.input[..], sig.output.clone()), |
777 | _ => { | 749 | _ => { |
@@ -780,120 +752,102 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { | |||
780 | (&[][..], Ty::Unknown) | 752 | (&[][..], Ty::Unknown) |
781 | } | 753 | } |
782 | }; | 754 | }; |
783 | if let Some(arg_list) = e.arg_list() { | 755 | for (i, arg) in args.iter().enumerate() { |
784 | for (i, arg) in arg_list.args().enumerate() { | 756 | self.infer_expr( |
785 | self.infer_expr( | 757 | *arg, |
786 | arg, | 758 | &Expectation::has_type(arg_tys.get(i).cloned().unwrap_or(Ty::Unknown)), |
787 | &Expectation::has_type(arg_tys.get(i).cloned().unwrap_or(Ty::Unknown)), | 759 | )?; |
788 | )?; | ||
789 | } | ||
790 | } | 760 | } |
791 | ret_ty | 761 | ret_ty |
792 | } | 762 | } |
793 | ast::Expr::MethodCallExpr(e) => { | 763 | Expr::MethodCall { receiver, args, .. } => { |
794 | let _receiver_ty = self.infer_expr_opt(e.expr(), &Expectation::none())?; | 764 | let _receiver_ty = self.infer_expr(*receiver, &Expectation::none())?; |
795 | if let Some(arg_list) = e.arg_list() { | 765 | // TODO resolve method... |
796 | for arg in arg_list.args() { | 766 | for (_i, arg) in args.iter().enumerate() { |
797 | // TODO unify / expect argument type | 767 | // TODO unify / expect argument type |
798 | self.infer_expr(arg, &Expectation::none())?; | 768 | self.infer_expr(*arg, &Expectation::none())?; |
799 | } | ||
800 | } | 769 | } |
801 | Ty::Unknown | 770 | Ty::Unknown |
802 | } | 771 | } |
803 | ast::Expr::MatchExpr(e) => { | 772 | Expr::Match { expr, arms } => { |
804 | let _ty = self.infer_expr_opt(e.expr(), &Expectation::none())?; | 773 | let _ty = self.infer_expr(*expr, &Expectation::none())?; |
805 | if let Some(match_arm_list) = e.match_arm_list() { | 774 | for arm in arms { |
806 | for arm in match_arm_list.arms() { | 775 | // TODO type the bindings in pats |
807 | // TODO type the bindings in pat | 776 | // TODO type the guard |
808 | // TODO type the guard | 777 | let _ty = self.infer_expr(arm.expr, &Expectation::none())?; |
809 | let _ty = self.infer_expr_opt(arm.expr(), &Expectation::none())?; | ||
810 | } | ||
811 | // TODO unify all the match arm types | ||
812 | Ty::Unknown | ||
813 | } else { | ||
814 | Ty::Unknown | ||
815 | } | 778 | } |
816 | } | 779 | // TODO unify all the match arm types |
817 | ast::Expr::TupleExpr(_e) => Ty::Unknown, | ||
818 | ast::Expr::ArrayExpr(_e) => Ty::Unknown, | ||
819 | ast::Expr::PathExpr(e) => self.infer_path_expr(e)?.unwrap_or(Ty::Unknown), | ||
820 | ast::Expr::ContinueExpr(_e) => Ty::Never, | ||
821 | ast::Expr::BreakExpr(_e) => Ty::Never, | ||
822 | ast::Expr::ParenExpr(e) => self.infer_expr_opt(e.expr(), expected)?, | ||
823 | ast::Expr::Label(_e) => Ty::Unknown, | ||
824 | ast::Expr::ReturnExpr(e) => { | ||
825 | // TODO expect return type of function | ||
826 | self.infer_expr_opt(e.expr(), &Expectation::none())?; | ||
827 | Ty::Never | ||
828 | } | ||
829 | ast::Expr::MatchArmList(_) | ast::Expr::MatchArm(_) | ast::Expr::MatchGuard(_) => { | ||
830 | // Can this even occur outside of a match expression? | ||
831 | Ty::Unknown | 780 | Ty::Unknown |
832 | } | 781 | } |
833 | ast::Expr::StructLit(e) => { | 782 | Expr::Path(p) => self.infer_path_expr(expr, p)?.unwrap_or(Ty::Unknown), |
834 | let (ty, def_id) = self.resolve_variant(e.path())?; | 783 | Expr::Continue => Ty::Never, |
835 | if let Some(nfl) = e.named_field_list() { | 784 | Expr::Break { expr } => { |
836 | for field in nfl.fields() { | 785 | if let Some(expr) = expr { |
837 | let field_ty = if let (Some(def_id), Some(nr)) = (def_id, field.name_ref()) | 786 | // TODO handle break with value |
838 | { | 787 | self.infer_expr(*expr, &Expectation::none())?; |
839 | self.db.type_for_field(def_id, nr.as_name())? | ||
840 | } else { | ||
841 | Ty::Unknown | ||
842 | }; | ||
843 | self.infer_expr_opt(field.expr(), &Expectation::has_type(field_ty))?; | ||
844 | } | ||
845 | } | 788 | } |
846 | ty | 789 | Ty::Never |
847 | } | 790 | } |
848 | ast::Expr::NamedFieldList(_) | ast::Expr::NamedField(_) => { | 791 | Expr::Return { expr } => { |
849 | // Can this even occur outside of a struct literal? | 792 | if let Some(expr) = expr { |
850 | Ty::Unknown | 793 | self.infer_expr(*expr, &Expectation::has_type(self.return_ty.clone()))?; |
794 | } | ||
795 | Ty::Never | ||
851 | } | 796 | } |
852 | ast::Expr::IndexExpr(_e) => Ty::Unknown, | 797 | Expr::StructLit { |
853 | ast::Expr::FieldExpr(e) => { | 798 | path, |
854 | let receiver_ty = self.infer_expr_opt(e.expr(), &Expectation::none())?; | 799 | fields, |
855 | if let Some(nr) = e.name_ref() { | 800 | spread, |
856 | let ty = match receiver_ty { | 801 | } => { |
857 | Ty::Tuple(fields) => { | 802 | let (ty, def_id) = self.resolve_variant(path.as_ref())?; |
858 | let i = nr.text().parse::<usize>().ok(); | 803 | for field in fields { |
859 | i.and_then(|i| fields.get(i).cloned()) | 804 | let field_ty = if let Some(def_id) = def_id { |
860 | .unwrap_or(Ty::Unknown) | 805 | self.db.type_for_field(def_id, field.name.clone())? |
861 | } | 806 | } else { |
862 | Ty::Adt { def_id, .. } => self.db.type_for_field(def_id, nr.as_name())?, | 807 | Ty::Unknown |
863 | _ => Ty::Unknown, | ||
864 | }; | 808 | }; |
865 | self.insert_type_vars(ty) | 809 | self.infer_expr(field.expr, &Expectation::has_type(field_ty))?; |
866 | } else { | 810 | } |
867 | Ty::Unknown | 811 | if let Some(expr) = spread { |
812 | self.infer_expr(*expr, &Expectation::has_type(ty.clone()))?; | ||
868 | } | 813 | } |
814 | ty | ||
869 | } | 815 | } |
870 | ast::Expr::TryExpr(e) => { | 816 | Expr::Field { expr, name } => { |
871 | let _inner_ty = self.infer_expr_opt(e.expr(), &Expectation::none())?; | 817 | let receiver_ty = self.infer_expr(*expr, &Expectation::none())?; |
818 | let ty = match receiver_ty { | ||
819 | Ty::Tuple(fields) => { | ||
820 | let i = name.to_string().parse::<usize>().ok(); | ||
821 | i.and_then(|i| fields.get(i).cloned()) | ||
822 | .unwrap_or(Ty::Unknown) | ||
823 | } | ||
824 | Ty::Adt { def_id, .. } => self.db.type_for_field(def_id, name.clone())?, | ||
825 | _ => Ty::Unknown, | ||
826 | }; | ||
827 | self.insert_type_vars(ty) | ||
828 | } | ||
829 | Expr::Try { expr } => { | ||
830 | let _inner_ty = self.infer_expr(*expr, &Expectation::none())?; | ||
872 | Ty::Unknown | 831 | Ty::Unknown |
873 | } | 832 | } |
874 | ast::Expr::CastExpr(e) => { | 833 | Expr::Cast { expr, type_ref } => { |
875 | let _inner_ty = self.infer_expr_opt(e.expr(), &Expectation::none())?; | 834 | let _inner_ty = self.infer_expr(*expr, &Expectation::none())?; |
876 | let cast_ty = Ty::from_ast_opt( | 835 | let cast_ty = |
877 | self.db, | 836 | Ty::from_hir(self.db, &self.module, self.impl_block.as_ref(), type_ref)?; |
878 | &self.module, | ||
879 | self.impl_block.as_ref(), | ||
880 | e.type_ref(), | ||
881 | )?; | ||
882 | let cast_ty = self.insert_type_vars(cast_ty); | 837 | let cast_ty = self.insert_type_vars(cast_ty); |
883 | // TODO do the coercion... | 838 | // TODO check the cast... |
884 | cast_ty | 839 | cast_ty |
885 | } | 840 | } |
886 | ast::Expr::RefExpr(e) => { | 841 | Expr::Ref { expr, mutability } => { |
887 | // TODO pass the expectation down | 842 | // TODO pass the expectation down |
888 | let inner_ty = self.infer_expr_opt(e.expr(), &Expectation::none())?; | 843 | let inner_ty = self.infer_expr(*expr, &Expectation::none())?; |
889 | let m = Mutability::from_mutable(e.is_mut()); | ||
890 | // TODO reference coercions etc. | 844 | // TODO reference coercions etc. |
891 | Ty::Ref(Arc::new(inner_ty), m) | 845 | Ty::Ref(Arc::new(inner_ty), *mutability) |
892 | } | 846 | } |
893 | ast::Expr::PrefixExpr(e) => { | 847 | Expr::UnaryOp { expr, op } => { |
894 | let inner_ty = self.infer_expr_opt(e.expr(), &Expectation::none())?; | 848 | let inner_ty = self.infer_expr(*expr, &Expectation::none())?; |
895 | match e.op() { | 849 | match op { |
896 | Some(PrefixOp::Deref) => { | 850 | Some(UnaryOp::Deref) => { |
897 | match inner_ty { | 851 | match inner_ty { |
898 | // builtin deref: | 852 | // builtin deref: |
899 | Ty::Ref(ref_inner, _) => (*ref_inner).clone(), | 853 | Ty::Ref(ref_inner, _) => (*ref_inner).clone(), |
@@ -905,130 +859,112 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { | |||
905 | _ => Ty::Unknown, | 859 | _ => Ty::Unknown, |
906 | } | 860 | } |
907 | } | 861 | } |
908 | ast::Expr::RangeExpr(_e) => Ty::Unknown, | 862 | Expr::BinaryOp { lhs, rhs, op } => match op { |
909 | ast::Expr::BinExpr(_e) => Ty::Unknown, | 863 | Some(op) => { |
910 | ast::Expr::Literal(_e) => Ty::Unknown, | 864 | let subtype_expectation = match op { |
865 | BinaryOp::BooleanAnd | BinaryOp::BooleanOr => { | ||
866 | Expectation::has_type(Ty::Bool) | ||
867 | } | ||
868 | _ => Expectation::none(), | ||
869 | }; | ||
870 | let _lhs_ty = self.infer_expr(*lhs, &subtype_expectation)?; | ||
871 | let _rhs_ty = self.infer_expr(*rhs, &subtype_expectation)?; | ||
872 | |||
873 | if is_boolean_operator(*op) { | ||
874 | Ty::Bool | ||
875 | } else { | ||
876 | Ty::Unknown | ||
877 | } | ||
878 | } | ||
879 | _ => Ty::Unknown, | ||
880 | }, | ||
911 | }; | 881 | }; |
912 | // use a new type variable if we got Ty::Unknown here | 882 | // use a new type variable if we got Ty::Unknown here |
913 | let ty = self.insert_type_vars_shallow(ty); | 883 | let ty = self.insert_type_vars_shallow(ty); |
914 | self.unify(&ty, &expected.ty); | 884 | self.unify(&ty, &expected.ty); |
915 | self.write_ty(expr.syntax(), ty.clone()); | 885 | let ty = self.resolve_ty_as_possible(ty); |
886 | self.write_expr_ty(expr, ty.clone()); | ||
916 | Ok(ty) | 887 | Ok(ty) |
917 | } | 888 | } |
918 | 889 | ||
919 | fn infer_block_opt( | 890 | fn infer_block( |
920 | &mut self, | 891 | &mut self, |
921 | node: Option<ast::Block>, | 892 | statements: &[Statement], |
893 | tail: Option<ExprId>, | ||
922 | expected: &Expectation, | 894 | expected: &Expectation, |
923 | ) -> Cancelable<Ty> { | 895 | ) -> Cancelable<Ty> { |
924 | if let Some(b) = node { | 896 | for stmt in statements { |
925 | self.infer_block(b, expected) | ||
926 | } else { | ||
927 | Ok(Ty::Unknown) | ||
928 | } | ||
929 | } | ||
930 | |||
931 | fn infer_block(&mut self, node: ast::Block, expected: &Expectation) -> Cancelable<Ty> { | ||
932 | for stmt in node.statements() { | ||
933 | match stmt { | 897 | match stmt { |
934 | ast::Stmt::LetStmt(stmt) => { | 898 | Statement::Let { |
935 | let decl_ty = Ty::from_ast_opt( | 899 | pat, |
900 | type_ref, | ||
901 | initializer, | ||
902 | } => { | ||
903 | let decl_ty = Ty::from_hir_opt( | ||
936 | self.db, | 904 | self.db, |
937 | &self.module, | 905 | &self.module, |
938 | self.impl_block.as_ref(), | 906 | self.impl_block.as_ref(), |
939 | stmt.type_ref(), | 907 | type_ref.as_ref(), |
940 | )?; | 908 | )?; |
941 | let decl_ty = self.insert_type_vars(decl_ty); | 909 | let decl_ty = self.insert_type_vars(decl_ty); |
942 | let ty = if let Some(expr) = stmt.initializer() { | 910 | let ty = if let Some(expr) = initializer { |
943 | let expr_ty = self.infer_expr(expr, &Expectation::has_type(decl_ty))?; | 911 | let expr_ty = self.infer_expr(*expr, &Expectation::has_type(decl_ty))?; |
944 | expr_ty | 912 | expr_ty |
945 | } else { | 913 | } else { |
946 | decl_ty | 914 | decl_ty |
947 | }; | 915 | }; |
948 | 916 | ||
949 | if let Some(pat) = stmt.pat() { | 917 | self.write_pat_ty(*pat, ty); |
950 | self.write_ty(pat.syntax(), ty); | ||
951 | }; | ||
952 | } | 918 | } |
953 | ast::Stmt::ExprStmt(expr_stmt) => { | 919 | Statement::Expr(expr) => { |
954 | self.infer_expr_opt(expr_stmt.expr(), &Expectation::none())?; | 920 | self.infer_expr(*expr, &Expectation::none())?; |
955 | } | 921 | } |
956 | } | 922 | } |
957 | } | 923 | } |
958 | let ty = if let Some(expr) = node.expr() { | 924 | let ty = if let Some(expr) = tail { |
959 | self.infer_expr(expr, expected)? | 925 | self.infer_expr(expr, expected)? |
960 | } else { | 926 | } else { |
961 | Ty::unit() | 927 | Ty::unit() |
962 | }; | 928 | }; |
963 | self.write_ty(node.syntax(), ty.clone()); | ||
964 | Ok(ty) | 929 | Ok(ty) |
965 | } | 930 | } |
966 | 931 | ||
967 | fn collect_fn_signature(&mut self, node: ast::FnDef) -> Cancelable<()> { | 932 | fn collect_fn_signature(&mut self, signature: &FnSignature) -> Cancelable<()> { |
968 | if let Some(param_list) = node.param_list() { | 933 | let body = Arc::clone(&self.body); // avoid borrow checker problem |
969 | if let Some(self_param) = param_list.self_param() { | 934 | for (type_ref, pat) in signature.args().iter().zip(body.args()) { |
970 | let self_type = if let Some(type_ref) = self_param.type_ref() { | 935 | let ty = self.make_ty(type_ref)?; |
971 | let ty = self.make_ty(&TypeRef::from_ast(type_ref))?; | 936 | let ty = self.insert_type_vars(ty); |
972 | self.insert_type_vars(ty) | 937 | self.write_pat_ty(*pat, ty); |
973 | } else { | ||
974 | // TODO this should be handled by desugaring during HIR conversion | ||
975 | let ty = self.make_ty_opt(self.impl_block.as_ref().map(|i| i.target_type()))?; | ||
976 | let ty = match self_param.flavor() { | ||
977 | ast::SelfParamFlavor::Owned => ty, | ||
978 | ast::SelfParamFlavor::Ref => Ty::Ref(Arc::new(ty), Mutability::Shared), | ||
979 | ast::SelfParamFlavor::MutRef => Ty::Ref(Arc::new(ty), Mutability::Mut), | ||
980 | }; | ||
981 | self.insert_type_vars(ty) | ||
982 | }; | ||
983 | if let Some(self_kw) = self_param.self_kw() { | ||
984 | let self_param = LocalSyntaxPtr::new(self_kw.syntax()); | ||
985 | self.self_param = Some(self_param); | ||
986 | self.type_of.insert(self_param, self_type); | ||
987 | } | ||
988 | } | ||
989 | for param in param_list.params() { | ||
990 | let pat = if let Some(pat) = param.pat() { | ||
991 | pat | ||
992 | } else { | ||
993 | continue; | ||
994 | }; | ||
995 | let ty = if let Some(type_ref) = param.type_ref() { | ||
996 | let ty = self.make_ty(&TypeRef::from_ast(type_ref))?; | ||
997 | self.insert_type_vars(ty) | ||
998 | } else { | ||
999 | // missing type annotation | ||
1000 | self.new_type_var() | ||
1001 | }; | ||
1002 | self.type_of.insert(LocalSyntaxPtr::new(pat.syntax()), ty); | ||
1003 | } | ||
1004 | } | 938 | } |
1005 | 939 | self.return_ty = { | |
1006 | self.return_ty = if let Some(type_ref) = node.ret_type().and_then(|n| n.type_ref()) { | 940 | let ty = self.make_ty(signature.ret_type())?; |
1007 | let ty = self.make_ty(&TypeRef::from_ast(type_ref))?; | 941 | let ty = self.insert_type_vars(ty); |
1008 | self.insert_type_vars(ty) | 942 | ty |
1009 | } else { | ||
1010 | Ty::unit() | ||
1011 | }; | 943 | }; |
944 | Ok(()) | ||
945 | } | ||
1012 | 946 | ||
947 | fn infer_body(&mut self) -> Cancelable<()> { | ||
948 | self.infer_expr( | ||
949 | self.body.body_expr(), | ||
950 | &Expectation::has_type(self.return_ty.clone()), | ||
951 | )?; | ||
1013 | Ok(()) | 952 | Ok(()) |
1014 | } | 953 | } |
1015 | } | 954 | } |
1016 | 955 | ||
1017 | pub fn infer(db: &impl HirDatabase, def_id: DefId) -> Cancelable<Arc<InferenceResult>> { | 956 | pub fn infer(db: &impl HirDatabase, def_id: DefId) -> Cancelable<Arc<InferenceResult>> { |
1018 | let function = Function::new(def_id); // TODO: consts also need inference | 957 | let function = Function::new(def_id); // TODO: consts also need inference |
1019 | let scopes = function.scopes(db); | 958 | let body = function.body(db)?; |
959 | let scopes = db.fn_scopes(def_id)?; | ||
1020 | let module = function.module(db)?; | 960 | let module = function.module(db)?; |
1021 | let impl_block = function.impl_block(db)?; | 961 | let impl_block = function.impl_block(db)?; |
1022 | let mut ctx = InferenceContext::new(db, scopes, module, impl_block); | 962 | let mut ctx = InferenceContext::new(db, body, scopes, module, impl_block); |
1023 | |||
1024 | let syntax = function.syntax(db); | ||
1025 | let node = syntax.borrowed(); | ||
1026 | 963 | ||
1027 | ctx.collect_fn_signature(node)?; | 964 | let signature = function.signature(db); |
965 | ctx.collect_fn_signature(&signature)?; | ||
1028 | 966 | ||
1029 | if let Some(block) = node.body() { | 967 | ctx.infer_body()?; |
1030 | ctx.infer_block(block, &Expectation::has_type(ctx.return_ty.clone()))?; | ||
1031 | } | ||
1032 | 968 | ||
1033 | Ok(Arc::new(ctx.resolve_all())) | 969 | Ok(Arc::new(ctx.resolve_all())) |
1034 | } | 970 | } |
diff --git a/crates/ra_hir/src/ty/tests.rs b/crates/ra_hir/src/ty/tests.rs index fb53fcf0b..83aedaa00 100644 --- a/crates/ra_hir/src/ty/tests.rs +++ b/crates/ra_hir/src/ty/tests.rs | |||
@@ -1,8 +1,11 @@ | |||
1 | use std::sync::Arc; | ||
1 | use std::fmt::Write; | 2 | use std::fmt::Write; |
2 | use std::path::{PathBuf, Path}; | 3 | use std::path::{PathBuf, Path}; |
3 | use std::fs; | 4 | use std::fs; |
4 | 5 | ||
5 | use ra_db::{SyntaxDatabase}; | 6 | use salsa::Database; |
7 | |||
8 | use ra_db::SyntaxDatabase; | ||
6 | use ra_syntax::ast::{self, AstNode}; | 9 | use ra_syntax::ast::{self, AstNode}; |
7 | use test_utils::{project_dir, assert_eq_text, read_text}; | 10 | use test_utils::{project_dir, assert_eq_text, read_text}; |
8 | 11 | ||
@@ -31,7 +34,7 @@ fn test(a: u32, b: isize, c: !, d: &str) { | |||
31 | "test"; | 34 | "test"; |
32 | 1.0f32; | 35 | 1.0f32; |
33 | }"#, | 36 | }"#, |
34 | "0001_basics.txt", | 37 | "basics.txt", |
35 | ); | 38 | ); |
36 | } | 39 | } |
37 | 40 | ||
@@ -45,7 +48,7 @@ fn test() { | |||
45 | let c = b; | 48 | let c = b; |
46 | } | 49 | } |
47 | }"#, | 50 | }"#, |
48 | "0002_let.txt", | 51 | "let.txt", |
49 | ); | 52 | ); |
50 | } | 53 | } |
51 | 54 | ||
@@ -64,7 +67,7 @@ fn test() { | |||
64 | b::c(); | 67 | b::c(); |
65 | } | 68 | } |
66 | }"#, | 69 | }"#, |
67 | "0003_paths.txt", | 70 | "paths.txt", |
68 | ); | 71 | ); |
69 | } | 72 | } |
70 | 73 | ||
@@ -87,7 +90,7 @@ fn test() { | |||
87 | a.c; | 90 | a.c; |
88 | } | 91 | } |
89 | "#, | 92 | "#, |
90 | "0004_struct.txt", | 93 | "struct.txt", |
91 | ); | 94 | ); |
92 | } | 95 | } |
93 | 96 | ||
@@ -109,7 +112,7 @@ fn test(a: &u32, b: &mut u32, c: *const u32, d: *mut u32) { | |||
109 | *d; | 112 | *d; |
110 | } | 113 | } |
111 | "#, | 114 | "#, |
112 | "0005_refs.txt", | 115 | "refs_and_ptrs.txt", |
113 | ); | 116 | ); |
114 | } | 117 | } |
115 | 118 | ||
@@ -130,7 +133,7 @@ fn test() -> &mut &f64 { | |||
130 | &mut &c | 133 | &mut &c |
131 | } | 134 | } |
132 | "#, | 135 | "#, |
133 | "0006_backwards.txt", | 136 | "backwards.txt", |
134 | ); | 137 | ); |
135 | } | 138 | } |
136 | 139 | ||
@@ -149,7 +152,31 @@ impl S { | |||
149 | } | 152 | } |
150 | } | 153 | } |
151 | "#, | 154 | "#, |
152 | "0007_self.txt", | 155 | "self.txt", |
156 | ); | ||
157 | } | ||
158 | |||
159 | #[test] | ||
160 | fn infer_boolean_op() { | ||
161 | check_inference( | ||
162 | r#" | ||
163 | fn f(x: bool) -> i32 { | ||
164 | 0i32 | ||
165 | } | ||
166 | |||
167 | fn test() { | ||
168 | let x = a && b; | ||
169 | let y = true || false; | ||
170 | let z = x == y; | ||
171 | let h = CONST_1 <= CONST_2; | ||
172 | let c = f(z || y) + 5; | ||
173 | let d = b; | ||
174 | let e = 3i32 && "hello world"; | ||
175 | |||
176 | 10 < 3 | ||
177 | } | ||
178 | "#, | ||
179 | "boolean_op.txt", | ||
153 | ); | 180 | ); |
154 | } | 181 | } |
155 | 182 | ||
@@ -166,7 +193,25 @@ fn infer(content: &str) -> String { | |||
166 | .unwrap() | 193 | .unwrap() |
167 | .unwrap(); | 194 | .unwrap(); |
168 | let inference_result = func.infer(&db).unwrap(); | 195 | let inference_result = func.infer(&db).unwrap(); |
169 | for (syntax_ptr, ty) in &inference_result.type_of { | 196 | let body_syntax_mapping = func.body_syntax_mapping(&db).unwrap(); |
197 | let mut types = Vec::new(); | ||
198 | for (pat, ty) in inference_result.type_of_pat.iter() { | ||
199 | let syntax_ptr = match body_syntax_mapping.pat_syntax(pat) { | ||
200 | Some(sp) => sp, | ||
201 | None => continue, | ||
202 | }; | ||
203 | types.push((syntax_ptr, ty)); | ||
204 | } | ||
205 | for (expr, ty) in inference_result.type_of_expr.iter() { | ||
206 | let syntax_ptr = match body_syntax_mapping.expr_syntax(expr) { | ||
207 | Some(sp) => sp, | ||
208 | None => continue, | ||
209 | }; | ||
210 | types.push((syntax_ptr, ty)); | ||
211 | } | ||
212 | // sort ranges for consistency | ||
213 | types.sort_by_key(|(ptr, _)| (ptr.range().start(), ptr.range().end())); | ||
214 | for (syntax_ptr, ty) in &types { | ||
170 | let node = syntax_ptr.resolve(&source_file); | 215 | let node = syntax_ptr.resolve(&source_file); |
171 | write!( | 216 | write!( |
172 | acc, | 217 | acc, |
@@ -217,3 +262,43 @@ fn ellipsize(mut text: String, max_len: usize) -> String { | |||
217 | fn test_data_dir() -> PathBuf { | 262 | fn test_data_dir() -> PathBuf { |
218 | project_dir().join("crates/ra_hir/src/ty/tests/data") | 263 | project_dir().join("crates/ra_hir/src/ty/tests/data") |
219 | } | 264 | } |
265 | |||
266 | #[test] | ||
267 | fn typing_whitespace_inside_a_function_should_not_invalidate_types() { | ||
268 | let (mut db, pos) = MockDatabase::with_position( | ||
269 | " | ||
270 | //- /lib.rs | ||
271 | fn foo() -> i32 { | ||
272 | <|>1 + 1 | ||
273 | } | ||
274 | ", | ||
275 | ); | ||
276 | let func = source_binder::function_from_position(&db, pos) | ||
277 | .unwrap() | ||
278 | .unwrap(); | ||
279 | { | ||
280 | let events = db.log_executed(|| { | ||
281 | func.infer(&db).unwrap(); | ||
282 | }); | ||
283 | assert!(format!("{:?}", events).contains("infer")) | ||
284 | } | ||
285 | |||
286 | let new_text = " | ||
287 | fn foo() -> i32 { | ||
288 | 1 | ||
289 | + | ||
290 | 1 | ||
291 | } | ||
292 | " | ||
293 | .to_string(); | ||
294 | |||
295 | db.query_mut(ra_db::FileTextQuery) | ||
296 | .set(pos.file_id, Arc::new(new_text)); | ||
297 | |||
298 | { | ||
299 | let events = db.log_executed(|| { | ||
300 | func.infer(&db).unwrap(); | ||
301 | }); | ||
302 | assert!(!format!("{:?}", events).contains("infer"), "{:#?}", events) | ||
303 | } | ||
304 | } | ||
diff --git a/crates/ra_hir/src/ty/tests/data/0006_backwards.txt b/crates/ra_hir/src/ty/tests/data/backwards.txt index 120069401..b6807fb2a 100644 --- a/crates/ra_hir/src/ty/tests/data/0006_backwards.txt +++ b/crates/ra_hir/src/ty/tests/data/backwards.txt | |||
@@ -1,20 +1,20 @@ | |||
1 | [22; 24) '{}': () | ||
2 | [14; 15) 'x': u32 | 1 | [14; 15) 'x': u32 |
3 | [142; 158) 'unknow...nction': [unknown] | 2 | [22; 24) '{}': () |
4 | [126; 127) 'a': u32 | ||
5 | [198; 216) 'unknow...tion()': f64 | ||
6 | [228; 229) 'c': f64 | ||
7 | [198; 214) 'unknow...nction': [unknown] | ||
8 | [166; 184) 'S { i3...d: b }': S | ||
9 | [222; 229) '&mut &c': &mut &f64 | ||
10 | [194; 195) 'c': f64 | ||
11 | [92; 110) 'unknow...tion()': u32 | ||
12 | [142; 160) 'unknow...tion()': i32 | ||
13 | [92; 108) 'unknow...nction': [unknown] | ||
14 | [116; 128) 'takes_u32(a)': () | ||
15 | [78; 231) '{ ...t &c }': &mut &f64 | 3 | [78; 231) '{ ...t &c }': &mut &f64 |
16 | [227; 229) '&c': &f64 | ||
17 | [88; 89) 'a': u32 | 4 | [88; 89) 'a': u32 |
18 | [181; 182) 'b': i32 | 5 | [92; 108) 'unknow...nction': [unknown] |
6 | [92; 110) 'unknow...tion()': u32 | ||
19 | [116; 125) 'takes_u32': fn(u32,) -> () | 7 | [116; 125) 'takes_u32': fn(u32,) -> () |
8 | [116; 128) 'takes_u32(a)': () | ||
9 | [126; 127) 'a': u32 | ||
20 | [138; 139) 'b': i32 | 10 | [138; 139) 'b': i32 |
11 | [142; 158) 'unknow...nction': [unknown] | ||
12 | [142; 160) 'unknow...tion()': i32 | ||
13 | [166; 184) 'S { i3...d: b }': S | ||
14 | [181; 182) 'b': i32 | ||
15 | [194; 195) 'c': f64 | ||
16 | [198; 214) 'unknow...nction': [unknown] | ||
17 | [198; 216) 'unknow...tion()': f64 | ||
18 | [222; 229) '&mut &c': &mut &f64 | ||
19 | [227; 229) '&c': &f64 | ||
20 | [228; 229) 'c': f64 | ||
diff --git a/crates/ra_hir/src/ty/tests/data/0001_basics.txt b/crates/ra_hir/src/ty/tests/data/basics.txt index 212e92e00..8ea244ba8 100644 --- a/crates/ra_hir/src/ty/tests/data/0001_basics.txt +++ b/crates/ra_hir/src/ty/tests/data/basics.txt | |||
@@ -1,13 +1,13 @@ | |||
1 | [9; 10) 'a': u32 | ||
2 | [17; 18) 'b': isize | ||
3 | [27; 28) 'c': ! | ||
1 | [33; 34) 'd': &[unknown] | 4 | [33; 34) 'd': &[unknown] |
2 | [88; 94) '1isize': [unknown] | 5 | [42; 121) '{ ...f32; }': () |
3 | [48; 49) 'a': u32 | 6 | [48; 49) 'a': u32 |
4 | [55; 56) 'b': isize | 7 | [55; 56) 'b': isize |
5 | [112; 118) '1.0f32': [unknown] | ||
6 | [76; 82) '1usize': [unknown] | ||
7 | [9; 10) 'a': u32 | ||
8 | [27; 28) 'c': ! | ||
9 | [62; 63) 'c': ! | 8 | [62; 63) 'c': ! |
10 | [17; 18) 'b': isize | ||
11 | [100; 106) '"test"': [unknown] | ||
12 | [42; 121) '{ ...f32; }': () | ||
13 | [69; 70) 'd': &[unknown] | 9 | [69; 70) 'd': &[unknown] |
10 | [76; 82) '1usize': [unknown] | ||
11 | [88; 94) '1isize': [unknown] | ||
12 | [100; 106) '"test"': [unknown] | ||
13 | [112; 118) '1.0f32': [unknown] | ||
diff --git a/crates/ra_hir/src/ty/tests/data/boolean_op.txt b/crates/ra_hir/src/ty/tests/data/boolean_op.txt new file mode 100644 index 000000000..cce8d68fb --- /dev/null +++ b/crates/ra_hir/src/ty/tests/data/boolean_op.txt | |||
@@ -0,0 +1,31 @@ | |||
1 | [6; 7) 'x': [unknown] | ||
2 | [22; 34) '{ 0i32 }': i32 | ||
3 | [28; 32) '0i32': i32 | ||
4 | [46; 237) '{ ... < 3 }': bool | ||
5 | [56; 57) 'x': bool | ||
6 | [60; 61) 'a': bool | ||
7 | [60; 66) 'a && b': bool | ||
8 | [65; 66) 'b': bool | ||
9 | [76; 77) 'y': bool | ||
10 | [80; 84) 'true': bool | ||
11 | [80; 93) 'true || false': bool | ||
12 | [88; 93) 'false': bool | ||
13 | [103; 104) 'z': bool | ||
14 | [107; 108) 'x': bool | ||
15 | [107; 113) 'x == y': bool | ||
16 | [112; 113) 'y': bool | ||
17 | [123; 124) 'h': bool | ||
18 | [127; 134) 'CONST_1': [unknown] | ||
19 | [127; 145) 'CONST_...ONST_2': bool | ||
20 | [138; 145) 'CONST_2': [unknown] | ||
21 | [155; 156) 'c': [unknown] | ||
22 | [159; 172) 'f(z || y) + 5': [unknown] | ||
23 | [182; 183) 'd': [unknown] | ||
24 | [186; 187) 'b': [unknown] | ||
25 | [197; 198) 'e': bool | ||
26 | [201; 205) '3i32': bool | ||
27 | [201; 222) '3i32 &...world"': bool | ||
28 | [209; 222) '"hello world"': bool | ||
29 | [229; 231) '10': [unknown] | ||
30 | [229; 235) '10 < 3': bool | ||
31 | [234; 235) '3': [unknown] | ||
diff --git a/crates/ra_hir/src/ty/tests/data/0002_let.txt b/crates/ra_hir/src/ty/tests/data/let.txt index 916ca25a1..30f4a2cf5 100644 --- a/crates/ra_hir/src/ty/tests/data/0002_let.txt +++ b/crates/ra_hir/src/ty/tests/data/let.txt | |||
@@ -1,7 +1,7 @@ | |||
1 | [21; 22) 'a': [unknown] | ||
2 | [52; 53) '1': usize | ||
3 | [11; 71) '{ ...= b; }': () | 1 | [11; 71) '{ ...= b; }': () |
4 | [63; 64) 'c': usize | 2 | [21; 22) 'a': [unknown] |
5 | [25; 31) '1isize': [unknown] | 3 | [25; 31) '1isize': [unknown] |
6 | [41; 42) 'b': usize | 4 | [41; 42) 'b': usize |
5 | [52; 53) '1': usize | ||
6 | [63; 64) 'c': usize | ||
7 | [67; 68) 'b': usize | 7 | [67; 68) 'b': usize |
diff --git a/crates/ra_hir/src/ty/tests/data/0003_paths.txt b/crates/ra_hir/src/ty/tests/data/paths.txt index 2a12d264f..b22f4d4a5 100644 --- a/crates/ra_hir/src/ty/tests/data/0003_paths.txt +++ b/crates/ra_hir/src/ty/tests/data/paths.txt | |||
@@ -1,9 +1,9 @@ | |||
1 | [15; 20) '{ 1 }': u32 | 1 | [15; 20) '{ 1 }': u32 |
2 | [17; 18) '1': u32 | 2 | [17; 18) '1': u32 |
3 | [50; 51) '1': u32 | ||
4 | [48; 53) '{ 1 }': u32 | 3 | [48; 53) '{ 1 }': u32 |
5 | [82; 88) 'b::c()': u32 | 4 | [50; 51) '1': u32 |
6 | [67; 91) '{ ...c(); }': () | 5 | [67; 91) '{ ...c(); }': () |
7 | [73; 74) 'a': fn() -> u32 | 6 | [73; 74) 'a': fn() -> u32 |
8 | [73; 76) 'a()': u32 | 7 | [73; 76) 'a()': u32 |
9 | [82; 86) 'b::c': fn() -> u32 | 8 | [82; 86) 'b::c': fn() -> u32 |
9 | [82; 88) 'b::c()': u32 | ||
diff --git a/crates/ra_hir/src/ty/tests/data/0005_refs.txt b/crates/ra_hir/src/ty/tests/data/refs_and_ptrs.txt index 296e955c1..afab343ea 100644 --- a/crates/ra_hir/src/ty/tests/data/0005_refs.txt +++ b/crates/ra_hir/src/ty/tests/data/refs_and_ptrs.txt | |||
@@ -1,23 +1,23 @@ | |||
1 | [115; 117) '&b': &&mut u32 | 1 | [9; 10) 'a': &u32 |
2 | [88; 94) '&mut a': &mut &u32 | 2 | [18; 19) 'b': &mut u32 |
3 | [146; 147) 'd': *mut u32 | 3 | [31; 32) 'c': *const u32 |
4 | [145; 147) '*d': u32 | ||
5 | [65; 66) 'a': &u32 | ||
6 | [46; 47) 'd': *mut u32 | 4 | [46; 47) 'd': *mut u32 |
7 | [59; 150) '{ ... *d; }': () | 5 | [59; 150) '{ ... *d; }': () |
8 | [116; 117) 'b': &mut u32 | 6 | [65; 66) 'a': &u32 |
9 | [131; 132) 'c': *const u32 | ||
10 | [130; 132) '*c': u32 | ||
11 | [72; 74) '*a': u32 | 7 | [72; 74) '*a': u32 |
12 | [107; 109) '*b': u32 | 8 | [73; 74) 'a': &u32 |
13 | [108; 109) 'b': &mut u32 | 9 | [80; 82) '&a': &&u32 |
14 | [9; 10) 'a': &u32 | 10 | [81; 82) 'a': &u32 |
15 | [18; 19) 'b': &mut u32 | 11 | [88; 94) '&mut a': &mut &u32 |
16 | [93; 94) 'a': &u32 | 12 | [93; 94) 'a': &u32 |
17 | [100; 101) 'b': &mut u32 | 13 | [100; 101) 'b': &mut u32 |
18 | [81; 82) 'a': &u32 | 14 | [107; 109) '*b': u32 |
19 | [80; 82) '&a': &&u32 | 15 | [108; 109) 'b': &mut u32 |
20 | [73; 74) 'a': &u32 | 16 | [115; 117) '&b': &&mut u32 |
17 | [116; 117) 'b': &mut u32 | ||
21 | [123; 124) 'c': *const u32 | 18 | [123; 124) 'c': *const u32 |
22 | [31; 32) 'c': *const u32 | 19 | [130; 132) '*c': u32 |
20 | [131; 132) 'c': *const u32 | ||
23 | [138; 139) 'd': *mut u32 | 21 | [138; 139) 'd': *mut u32 |
22 | [145; 147) '*d': u32 | ||
23 | [146; 147) 'd': *mut u32 | ||
diff --git a/crates/ra_hir/src/ty/tests/data/0007_self.txt b/crates/ra_hir/src/ty/tests/data/self.txt index db4ba17d0..c38029f97 100644 --- a/crates/ra_hir/src/ty/tests/data/0007_self.txt +++ b/crates/ra_hir/src/ty/tests/data/self.txt | |||
@@ -1,6 +1,6 @@ | |||
1 | [50; 54) 'self': &S | ||
2 | [34; 38) 'self': &S | 1 | [34; 38) 'self': &S |
3 | [40; 61) '{ ... }': () | 2 | [40; 61) '{ ... }': () |
3 | [50; 54) 'self': &S | ||
4 | [75; 79) 'self': &S | ||
4 | [88; 109) '{ ... }': () | 5 | [88; 109) '{ ... }': () |
5 | [98; 102) 'self': &S | 6 | [98; 102) 'self': &S |
6 | [75; 79) 'self': &S | ||
diff --git a/crates/ra_hir/src/ty/tests/data/0004_struct.txt b/crates/ra_hir/src/ty/tests/data/struct.txt index b4af18b87..7b324c82f 100644 --- a/crates/ra_hir/src/ty/tests/data/0004_struct.txt +++ b/crates/ra_hir/src/ty/tests/data/struct.txt | |||
@@ -1,16 +1,16 @@ | |||
1 | [86; 90) 'C(1)': [unknown] | 1 | [72; 154) '{ ...a.c; }': () |
2 | [121; 122) 'B': B | 2 | [82; 83) 'c': [unknown] |
3 | [86; 87) 'C': [unknown] | 3 | [86; 87) 'C': [unknown] |
4 | [129; 130) '1': [unknown] | 4 | [86; 90) 'C(1)': [unknown] |
5 | [88; 89) '1': [unknown] | ||
6 | [96; 97) 'B': [unknown] | ||
5 | [107; 108) 'a': A | 7 | [107; 108) 'a': A |
8 | [114; 133) 'A { b:...C(1) }': A | ||
9 | [121; 122) 'B': B | ||
6 | [127; 128) 'C': [unknown] | 10 | [127; 128) 'C': [unknown] |
11 | [127; 131) 'C(1)': C | ||
12 | [129; 130) '1': [unknown] | ||
13 | [139; 140) 'a': A | ||
7 | [139; 142) 'a.b': B | 14 | [139; 142) 'a.b': B |
8 | [114; 133) 'A { b:...C(1) }': A | ||
9 | [148; 151) 'a.c': C | ||
10 | [148; 149) 'a': A | 15 | [148; 149) 'a': A |
11 | [139; 140) 'a': A | 16 | [148; 151) 'a.c': C |
12 | [72; 154) '{ ...a.c; }': () | ||
13 | [96; 97) 'B': [unknown] | ||
14 | [88; 89) '1': [unknown] | ||
15 | [82; 83) 'c': [unknown] | ||
16 | [127; 131) 'C(1)': C | ||
diff --git a/crates/ra_hir/src/type_ref.rs b/crates/ra_hir/src/type_ref.rs index b36bb35d8..859f330c2 100644 --- a/crates/ra_hir/src/type_ref.rs +++ b/crates/ra_hir/src/type_ref.rs | |||
@@ -107,4 +107,8 @@ impl TypeRef { | |||
107 | TypeRef::Error | 107 | TypeRef::Error |
108 | } | 108 | } |
109 | } | 109 | } |
110 | |||
111 | pub fn unit() -> TypeRef { | ||
112 | TypeRef::Tuple(Vec::new()) | ||
113 | } | ||
110 | } | 114 | } |
diff --git a/crates/ra_syntax/src/ast.rs b/crates/ra_syntax/src/ast.rs index c10169d90..9df8ec663 100644 --- a/crates/ra_syntax/src/ast.rs +++ b/crates/ra_syntax/src/ast.rs | |||
@@ -489,6 +489,58 @@ impl<'a> PrefixExpr<'a> { | |||
489 | } | 489 | } |
490 | 490 | ||
491 | #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] | 491 | #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] |
492 | pub enum BinOp { | ||
493 | /// The `||` operator for boolean OR | ||
494 | BooleanOr, | ||
495 | /// The `&&` operator for boolean AND | ||
496 | BooleanAnd, | ||
497 | /// The `==` operator for equality testing | ||
498 | EqualityTest, | ||
499 | /// The `<=` operator for lesser-equal testing | ||
500 | LesserEqualTest, | ||
501 | /// The `>=` operator for greater-equal testing | ||
502 | GreaterEqualTest, | ||
503 | /// The `<` operator for comparison | ||
504 | LesserTest, | ||
505 | /// The `>` operator for comparison | ||
506 | GreaterTest, | ||
507 | // TODO: lots of others | ||
508 | } | ||
509 | |||
510 | impl<'a> BinExpr<'a> { | ||
511 | pub fn op(&self) -> Option<BinOp> { | ||
512 | self.syntax() | ||
513 | .children() | ||
514 | .filter_map(|c| match c.kind() { | ||
515 | PIPEPIPE => Some(BinOp::BooleanOr), | ||
516 | AMPAMP => Some(BinOp::BooleanAnd), | ||
517 | EQEQ => Some(BinOp::EqualityTest), | ||
518 | LTEQ => Some(BinOp::LesserEqualTest), | ||
519 | GTEQ => Some(BinOp::GreaterEqualTest), | ||
520 | L_ANGLE => Some(BinOp::LesserTest), | ||
521 | R_ANGLE => Some(BinOp::GreaterTest), | ||
522 | _ => None, | ||
523 | }) | ||
524 | .next() | ||
525 | } | ||
526 | |||
527 | pub fn lhs(self) -> Option<Expr<'a>> { | ||
528 | children(self).nth(0) | ||
529 | } | ||
530 | |||
531 | pub fn rhs(self) -> Option<Expr<'a>> { | ||
532 | children(self).nth(1) | ||
533 | } | ||
534 | |||
535 | pub fn sub_exprs(self) -> (Option<Expr<'a>>, Option<Expr<'a>>) { | ||
536 | let mut children = children(self); | ||
537 | let first = children.next(); | ||
538 | let second = children.next(); | ||
539 | (first, second) | ||
540 | } | ||
541 | } | ||
542 | |||
543 | #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] | ||
492 | pub enum SelfParamFlavor { | 544 | pub enum SelfParamFlavor { |
493 | /// self | 545 | /// self |
494 | Owned, | 546 | Owned, |
diff --git a/crates/ra_syntax/src/ast/generated.rs b/crates/ra_syntax/src/ast/generated.rs index 7df6a9c46..24f72393a 100644 --- a/crates/ra_syntax/src/ast/generated.rs +++ b/crates/ra_syntax/src/ast/generated.rs | |||
@@ -378,7 +378,11 @@ impl<R: TreeRoot<RaTypes>> BreakExprNode<R> { | |||
378 | } | 378 | } |
379 | 379 | ||
380 | 380 | ||
381 | impl<'a> BreakExpr<'a> {} | 381 | impl<'a> BreakExpr<'a> { |
382 | pub fn expr(self) -> Option<Expr<'a>> { | ||
383 | super::child_opt(self) | ||
384 | } | ||
385 | } | ||
382 | 386 | ||
383 | // Byte | 387 | // Byte |
384 | #[derive(Debug, Clone, Copy,)] | 388 | #[derive(Debug, Clone, Copy,)] |
@@ -923,12 +927,7 @@ pub enum Expr<'a> { | |||
923 | BlockExpr(BlockExpr<'a>), | 927 | BlockExpr(BlockExpr<'a>), |
924 | ReturnExpr(ReturnExpr<'a>), | 928 | ReturnExpr(ReturnExpr<'a>), |
925 | MatchExpr(MatchExpr<'a>), | 929 | MatchExpr(MatchExpr<'a>), |
926 | MatchArmList(MatchArmList<'a>), | ||
927 | MatchArm(MatchArm<'a>), | ||
928 | MatchGuard(MatchGuard<'a>), | ||
929 | StructLit(StructLit<'a>), | 930 | StructLit(StructLit<'a>), |
930 | NamedFieldList(NamedFieldList<'a>), | ||
931 | NamedField(NamedField<'a>), | ||
932 | CallExpr(CallExpr<'a>), | 931 | CallExpr(CallExpr<'a>), |
933 | IndexExpr(IndexExpr<'a>), | 932 | IndexExpr(IndexExpr<'a>), |
934 | MethodCallExpr(MethodCallExpr<'a>), | 933 | MethodCallExpr(MethodCallExpr<'a>), |
@@ -960,12 +959,7 @@ impl<'a> AstNode<'a> for Expr<'a> { | |||
960 | BLOCK_EXPR => Some(Expr::BlockExpr(BlockExpr { syntax })), | 959 | BLOCK_EXPR => Some(Expr::BlockExpr(BlockExpr { syntax })), |
961 | RETURN_EXPR => Some(Expr::ReturnExpr(ReturnExpr { syntax })), | 960 | RETURN_EXPR => Some(Expr::ReturnExpr(ReturnExpr { syntax })), |
962 | MATCH_EXPR => Some(Expr::MatchExpr(MatchExpr { syntax })), | 961 | MATCH_EXPR => Some(Expr::MatchExpr(MatchExpr { syntax })), |
963 | MATCH_ARM_LIST => Some(Expr::MatchArmList(MatchArmList { syntax })), | ||
964 | MATCH_ARM => Some(Expr::MatchArm(MatchArm { syntax })), | ||
965 | MATCH_GUARD => Some(Expr::MatchGuard(MatchGuard { syntax })), | ||
966 | STRUCT_LIT => Some(Expr::StructLit(StructLit { syntax })), | 962 | STRUCT_LIT => Some(Expr::StructLit(StructLit { syntax })), |
967 | NAMED_FIELD_LIST => Some(Expr::NamedFieldList(NamedFieldList { syntax })), | ||
968 | NAMED_FIELD => Some(Expr::NamedField(NamedField { syntax })), | ||
969 | CALL_EXPR => Some(Expr::CallExpr(CallExpr { syntax })), | 963 | CALL_EXPR => Some(Expr::CallExpr(CallExpr { syntax })), |
970 | INDEX_EXPR => Some(Expr::IndexExpr(IndexExpr { syntax })), | 964 | INDEX_EXPR => Some(Expr::IndexExpr(IndexExpr { syntax })), |
971 | METHOD_CALL_EXPR => Some(Expr::MethodCallExpr(MethodCallExpr { syntax })), | 965 | METHOD_CALL_EXPR => Some(Expr::MethodCallExpr(MethodCallExpr { syntax })), |
@@ -997,12 +991,7 @@ impl<'a> AstNode<'a> for Expr<'a> { | |||
997 | Expr::BlockExpr(inner) => inner.syntax(), | 991 | Expr::BlockExpr(inner) => inner.syntax(), |
998 | Expr::ReturnExpr(inner) => inner.syntax(), | 992 | Expr::ReturnExpr(inner) => inner.syntax(), |
999 | Expr::MatchExpr(inner) => inner.syntax(), | 993 | Expr::MatchExpr(inner) => inner.syntax(), |
1000 | Expr::MatchArmList(inner) => inner.syntax(), | ||
1001 | Expr::MatchArm(inner) => inner.syntax(), | ||
1002 | Expr::MatchGuard(inner) => inner.syntax(), | ||
1003 | Expr::StructLit(inner) => inner.syntax(), | 994 | Expr::StructLit(inner) => inner.syntax(), |
1004 | Expr::NamedFieldList(inner) => inner.syntax(), | ||
1005 | Expr::NamedField(inner) => inner.syntax(), | ||
1006 | Expr::CallExpr(inner) => inner.syntax(), | 995 | Expr::CallExpr(inner) => inner.syntax(), |
1007 | Expr::IndexExpr(inner) => inner.syntax(), | 996 | Expr::IndexExpr(inner) => inner.syntax(), |
1008 | Expr::MethodCallExpr(inner) => inner.syntax(), | 997 | Expr::MethodCallExpr(inner) => inner.syntax(), |
@@ -3880,6 +3869,10 @@ impl<'a> StructLit<'a> { | |||
3880 | pub fn named_field_list(self) -> Option<NamedFieldList<'a>> { | 3869 | pub fn named_field_list(self) -> Option<NamedFieldList<'a>> { |
3881 | super::child_opt(self) | 3870 | super::child_opt(self) |
3882 | } | 3871 | } |
3872 | |||
3873 | pub fn spread(self) -> Option<Expr<'a>> { | ||
3874 | super::child_opt(self) | ||
3875 | } | ||
3883 | } | 3876 | } |
3884 | 3877 | ||
3885 | // StructPat | 3878 | // StructPat |
@@ -4147,7 +4140,15 @@ impl<R: TreeRoot<RaTypes>> TupleStructPatNode<R> { | |||
4147 | } | 4140 | } |
4148 | 4141 | ||
4149 | 4142 | ||
4150 | impl<'a> TupleStructPat<'a> {} | 4143 | impl<'a> TupleStructPat<'a> { |
4144 | pub fn args(self) -> impl Iterator<Item = Pat<'a>> + 'a { | ||
4145 | super::children(self) | ||
4146 | } | ||
4147 | |||
4148 | pub fn path(self) -> Option<Path<'a>> { | ||
4149 | super::child_opt(self) | ||
4150 | } | ||
4151 | } | ||
4151 | 4152 | ||
4152 | // TupleType | 4153 | // TupleType |
4153 | #[derive(Debug, Clone, Copy,)] | 4154 | #[derive(Debug, Clone, Copy,)] |
diff --git a/crates/ra_syntax/src/grammar.ron b/crates/ra_syntax/src/grammar.ron index c55e9e07a..3c640ed47 100644 --- a/crates/ra_syntax/src/grammar.ron +++ b/crates/ra_syntax/src/grammar.ron | |||
@@ -384,7 +384,7 @@ Grammar( | |||
384 | options: [ "Condition" ] | 384 | options: [ "Condition" ] |
385 | ), | 385 | ), |
386 | "ContinueExpr": (), | 386 | "ContinueExpr": (), |
387 | "BreakExpr": (), | 387 | "BreakExpr": (options: ["Expr"]), |
388 | "Label": (), | 388 | "Label": (), |
389 | "BlockExpr": ( | 389 | "BlockExpr": ( |
390 | options: [ "Block" ] | 390 | options: [ "Block" ] |
@@ -404,7 +404,7 @@ Grammar( | |||
404 | collections: [ [ "pats", "Pat" ] ] | 404 | collections: [ [ "pats", "Pat" ] ] |
405 | ), | 405 | ), |
406 | "MatchGuard": (), | 406 | "MatchGuard": (), |
407 | "StructLit": (options: ["Path", "NamedFieldList"]), | 407 | "StructLit": (options: ["Path", "NamedFieldList", ["spread", "Expr"]]), |
408 | "NamedFieldList": (collections: [ ["fields", "NamedField"] ]), | 408 | "NamedFieldList": (collections: [ ["fields", "NamedField"] ]), |
409 | "NamedField": (options: ["NameRef", "Expr"]), | 409 | "NamedField": (options: ["NameRef", "Expr"]), |
410 | "CallExpr": ( | 410 | "CallExpr": ( |
@@ -446,12 +446,7 @@ Grammar( | |||
446 | "BlockExpr", | 446 | "BlockExpr", |
447 | "ReturnExpr", | 447 | "ReturnExpr", |
448 | "MatchExpr", | 448 | "MatchExpr", |
449 | "MatchArmList", | ||
450 | "MatchArm", | ||
451 | "MatchGuard", | ||
452 | "StructLit", | 449 | "StructLit", |
453 | "NamedFieldList", | ||
454 | "NamedField", | ||
455 | "CallExpr", | 450 | "CallExpr", |
456 | "IndexExpr", | 451 | "IndexExpr", |
457 | "MethodCallExpr", | 452 | "MethodCallExpr", |
@@ -472,7 +467,10 @@ Grammar( | |||
472 | "PathPat": (), | 467 | "PathPat": (), |
473 | "StructPat": (), | 468 | "StructPat": (), |
474 | "FieldPatList": (), | 469 | "FieldPatList": (), |
475 | "TupleStructPat": (), | 470 | "TupleStructPat": ( |
471 | options: ["Path"], | ||
472 | collections: [["args", "Pat"]], | ||
473 | ), | ||
476 | "TuplePat": (), | 474 | "TuplePat": (), |
477 | "SlicePat": (), | 475 | "SlicePat": (), |
478 | "RangePat": (), | 476 | "RangePat": (), |