diff options
Diffstat (limited to 'crates/ra_hir')
25 files changed, 1895 insertions, 958 deletions
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..6866fc2ac --- /dev/null +++ b/crates/ra_hir/src/expr.rs | |||
@@ -0,0 +1,745 @@ | |||
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}; | ||
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: FxHashMap<ExprId, LocalSyntaxPtr>, | ||
43 | pat_syntax_mapping: FxHashMap<LocalSyntaxPtr, PatId>, | ||
44 | pat_syntax_mapping_back: FxHashMap<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 pat_syntax(&self, pat: PatId) -> Option<LocalSyntaxPtr> { | ||
81 | self.pat_syntax_mapping_back.get(&pat).cloned() | ||
82 | } | ||
83 | pub fn syntax_pat(&self, ptr: LocalSyntaxPtr) -> Option<PatId> { | ||
84 | self.pat_syntax_mapping.get(&ptr).cloned() | ||
85 | } | ||
86 | |||
87 | pub fn body(&self) -> &Arc<Body> { | ||
88 | &self.body | ||
89 | } | ||
90 | } | ||
91 | |||
92 | #[derive(Debug, Clone, Eq, PartialEq)] | ||
93 | pub enum Expr { | ||
94 | /// This is produced if syntax tree does not have a required expression piece. | ||
95 | Missing, | ||
96 | Path(Path), | ||
97 | If { | ||
98 | condition: ExprId, | ||
99 | then_branch: ExprId, | ||
100 | else_branch: Option<ExprId>, | ||
101 | }, | ||
102 | Block { | ||
103 | statements: Vec<Statement>, | ||
104 | tail: Option<ExprId>, | ||
105 | }, | ||
106 | Loop { | ||
107 | body: ExprId, | ||
108 | }, | ||
109 | While { | ||
110 | condition: ExprId, | ||
111 | body: ExprId, | ||
112 | }, | ||
113 | For { | ||
114 | iterable: ExprId, | ||
115 | pat: PatId, | ||
116 | body: ExprId, | ||
117 | }, | ||
118 | Call { | ||
119 | callee: ExprId, | ||
120 | args: Vec<ExprId>, | ||
121 | }, | ||
122 | MethodCall { | ||
123 | receiver: ExprId, | ||
124 | method_name: Name, | ||
125 | args: Vec<ExprId>, | ||
126 | }, | ||
127 | Match { | ||
128 | expr: ExprId, | ||
129 | arms: Vec<MatchArm>, | ||
130 | }, | ||
131 | Continue, | ||
132 | Break { | ||
133 | expr: Option<ExprId>, | ||
134 | }, | ||
135 | Return { | ||
136 | expr: Option<ExprId>, | ||
137 | }, | ||
138 | StructLit { | ||
139 | path: Option<Path>, | ||
140 | fields: Vec<StructLitField>, | ||
141 | spread: Option<ExprId>, | ||
142 | }, | ||
143 | Field { | ||
144 | expr: ExprId, | ||
145 | name: Name, | ||
146 | }, | ||
147 | Try { | ||
148 | expr: ExprId, | ||
149 | }, | ||
150 | Cast { | ||
151 | expr: ExprId, | ||
152 | type_ref: TypeRef, | ||
153 | }, | ||
154 | Ref { | ||
155 | expr: ExprId, | ||
156 | mutability: Mutability, | ||
157 | }, | ||
158 | UnaryOp { | ||
159 | expr: ExprId, | ||
160 | op: Option<UnaryOp>, | ||
161 | }, | ||
162 | Lambda { | ||
163 | args: Vec<PatId>, | ||
164 | arg_types: Vec<Option<TypeRef>>, | ||
165 | body: ExprId, | ||
166 | }, | ||
167 | } | ||
168 | |||
169 | pub type UnaryOp = ast::PrefixOp; | ||
170 | |||
171 | #[derive(Debug, Clone, Eq, PartialEq)] | ||
172 | pub struct MatchArm { | ||
173 | pub pats: Vec<PatId>, | ||
174 | // guard: Option<ExprId>, // TODO | ||
175 | pub expr: ExprId, | ||
176 | } | ||
177 | |||
178 | #[derive(Debug, Clone, Eq, PartialEq)] | ||
179 | pub struct StructLitField { | ||
180 | pub name: Name, | ||
181 | pub expr: ExprId, | ||
182 | } | ||
183 | |||
184 | #[derive(Debug, Clone, Eq, PartialEq)] | ||
185 | pub enum Statement { | ||
186 | Let { | ||
187 | pat: PatId, | ||
188 | type_ref: Option<TypeRef>, | ||
189 | initializer: Option<ExprId>, | ||
190 | }, | ||
191 | Expr(ExprId), | ||
192 | } | ||
193 | |||
194 | impl Expr { | ||
195 | pub fn walk_child_exprs(&self, mut f: impl FnMut(ExprId)) { | ||
196 | match self { | ||
197 | Expr::Missing => {} | ||
198 | Expr::Path(_) => {} | ||
199 | Expr::If { | ||
200 | condition, | ||
201 | then_branch, | ||
202 | else_branch, | ||
203 | } => { | ||
204 | f(*condition); | ||
205 | f(*then_branch); | ||
206 | if let Some(else_branch) = else_branch { | ||
207 | f(*else_branch); | ||
208 | } | ||
209 | } | ||
210 | Expr::Block { statements, tail } => { | ||
211 | for stmt in statements { | ||
212 | match stmt { | ||
213 | Statement::Let { initializer, .. } => { | ||
214 | if let Some(expr) = initializer { | ||
215 | f(*expr); | ||
216 | } | ||
217 | } | ||
218 | Statement::Expr(e) => f(*e), | ||
219 | } | ||
220 | } | ||
221 | if let Some(expr) = tail { | ||
222 | f(*expr); | ||
223 | } | ||
224 | } | ||
225 | Expr::Loop { body } => f(*body), | ||
226 | Expr::While { condition, body } => { | ||
227 | f(*condition); | ||
228 | f(*body); | ||
229 | } | ||
230 | Expr::For { iterable, body, .. } => { | ||
231 | f(*iterable); | ||
232 | f(*body); | ||
233 | } | ||
234 | Expr::Call { callee, args } => { | ||
235 | f(*callee); | ||
236 | for arg in args { | ||
237 | f(*arg); | ||
238 | } | ||
239 | } | ||
240 | Expr::MethodCall { receiver, args, .. } => { | ||
241 | f(*receiver); | ||
242 | for arg in args { | ||
243 | f(*arg); | ||
244 | } | ||
245 | } | ||
246 | Expr::Match { expr, arms } => { | ||
247 | f(*expr); | ||
248 | for arm in arms { | ||
249 | f(arm.expr); | ||
250 | } | ||
251 | } | ||
252 | Expr::Continue => {} | ||
253 | Expr::Break { expr } | Expr::Return { expr } => { | ||
254 | if let Some(expr) = expr { | ||
255 | f(*expr); | ||
256 | } | ||
257 | } | ||
258 | Expr::StructLit { fields, spread, .. } => { | ||
259 | for field in fields { | ||
260 | f(field.expr); | ||
261 | } | ||
262 | if let Some(expr) = spread { | ||
263 | f(*expr); | ||
264 | } | ||
265 | } | ||
266 | Expr::Lambda { body, .. } => { | ||
267 | f(*body); | ||
268 | } | ||
269 | Expr::Field { expr, .. } | ||
270 | | Expr::Try { expr } | ||
271 | | Expr::Cast { expr, .. } | ||
272 | | Expr::Ref { expr, .. } | ||
273 | | Expr::UnaryOp { expr, .. } => { | ||
274 | f(*expr); | ||
275 | } | ||
276 | } | ||
277 | } | ||
278 | } | ||
279 | |||
280 | #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] | ||
281 | pub struct PatId(RawId); | ||
282 | impl_arena_id!(PatId); | ||
283 | |||
284 | #[derive(Debug, Clone, Eq, PartialEq)] | ||
285 | pub enum Pat { | ||
286 | Missing, | ||
287 | Bind { | ||
288 | name: Name, | ||
289 | }, | ||
290 | TupleStruct { | ||
291 | path: Option<Path>, | ||
292 | args: Vec<PatId>, | ||
293 | }, | ||
294 | } | ||
295 | |||
296 | impl Pat { | ||
297 | pub fn walk_child_pats(&self, f: impl FnMut(PatId)) { | ||
298 | match self { | ||
299 | Pat::Missing | Pat::Bind { .. } => {} | ||
300 | Pat::TupleStruct { args, .. } => { | ||
301 | args.iter().map(|pat| *pat).for_each(f); | ||
302 | } | ||
303 | } | ||
304 | } | ||
305 | } | ||
306 | |||
307 | // Queries | ||
308 | |||
309 | pub(crate) fn body_hir(db: &impl HirDatabase, def_id: DefId) -> Cancelable<Arc<Body>> { | ||
310 | Ok(Arc::clone(&body_syntax_mapping(db, def_id)?.body)) | ||
311 | } | ||
312 | |||
313 | struct ExprCollector { | ||
314 | exprs: Arena<ExprId, Expr>, | ||
315 | pats: Arena<PatId, Pat>, | ||
316 | expr_syntax_mapping: FxHashMap<LocalSyntaxPtr, ExprId>, | ||
317 | expr_syntax_mapping_back: FxHashMap<ExprId, LocalSyntaxPtr>, | ||
318 | pat_syntax_mapping: FxHashMap<LocalSyntaxPtr, PatId>, | ||
319 | pat_syntax_mapping_back: FxHashMap<PatId, LocalSyntaxPtr>, | ||
320 | } | ||
321 | |||
322 | impl ExprCollector { | ||
323 | fn new() -> Self { | ||
324 | ExprCollector { | ||
325 | exprs: Arena::default(), | ||
326 | pats: Arena::default(), | ||
327 | expr_syntax_mapping: FxHashMap::default(), | ||
328 | expr_syntax_mapping_back: FxHashMap::default(), | ||
329 | pat_syntax_mapping: FxHashMap::default(), | ||
330 | pat_syntax_mapping_back: FxHashMap::default(), | ||
331 | } | ||
332 | } | ||
333 | |||
334 | fn alloc_expr(&mut self, expr: Expr, syntax_ptr: LocalSyntaxPtr) -> ExprId { | ||
335 | let id = self.exprs.alloc(expr); | ||
336 | self.expr_syntax_mapping.insert(syntax_ptr, id); | ||
337 | self.expr_syntax_mapping_back.insert(id, syntax_ptr); | ||
338 | id | ||
339 | } | ||
340 | |||
341 | fn alloc_pat(&mut self, pat: Pat, syntax_ptr: LocalSyntaxPtr) -> PatId { | ||
342 | let id = self.pats.alloc(pat); | ||
343 | self.pat_syntax_mapping.insert(syntax_ptr, id); | ||
344 | self.pat_syntax_mapping_back.insert(id, syntax_ptr); | ||
345 | id | ||
346 | } | ||
347 | |||
348 | fn empty_block(&mut self) -> ExprId { | ||
349 | let block = Expr::Block { | ||
350 | statements: Vec::new(), | ||
351 | tail: None, | ||
352 | }; | ||
353 | self.exprs.alloc(block) | ||
354 | } | ||
355 | |||
356 | fn collect_expr(&mut self, expr: ast::Expr) -> ExprId { | ||
357 | let syntax_ptr = LocalSyntaxPtr::new(expr.syntax()); | ||
358 | match expr { | ||
359 | ast::Expr::IfExpr(e) => { | ||
360 | if let Some(pat) = e.condition().and_then(|c| c.pat()) { | ||
361 | // if let -- desugar to match | ||
362 | let pat = self.collect_pat(pat); | ||
363 | let match_expr = | ||
364 | self.collect_expr_opt(e.condition().expect("checked above").expr()); | ||
365 | let then_branch = self.collect_block_opt(e.then_branch()); | ||
366 | let else_branch = e | ||
367 | .else_branch() | ||
368 | .map(|e| self.collect_block(e)) | ||
369 | .unwrap_or_else(|| self.empty_block()); | ||
370 | let placeholder_pat = self.pats.alloc(Pat::Missing); | ||
371 | let arms = vec![ | ||
372 | MatchArm { | ||
373 | pats: vec![pat], | ||
374 | expr: then_branch, | ||
375 | }, | ||
376 | MatchArm { | ||
377 | pats: vec![placeholder_pat], | ||
378 | expr: else_branch, | ||
379 | }, | ||
380 | ]; | ||
381 | self.alloc_expr( | ||
382 | Expr::Match { | ||
383 | expr: match_expr, | ||
384 | arms, | ||
385 | }, | ||
386 | syntax_ptr, | ||
387 | ) | ||
388 | } else { | ||
389 | let condition = self.collect_expr_opt(e.condition().and_then(|c| c.expr())); | ||
390 | let then_branch = self.collect_block_opt(e.then_branch()); | ||
391 | let else_branch = e.else_branch().map(|e| self.collect_block(e)); | ||
392 | self.alloc_expr( | ||
393 | Expr::If { | ||
394 | condition, | ||
395 | then_branch, | ||
396 | else_branch, | ||
397 | }, | ||
398 | syntax_ptr, | ||
399 | ) | ||
400 | } | ||
401 | } | ||
402 | ast::Expr::BlockExpr(e) => self.collect_block_opt(e.block()), | ||
403 | ast::Expr::LoopExpr(e) => { | ||
404 | let body = self.collect_block_opt(e.loop_body()); | ||
405 | self.alloc_expr(Expr::Loop { body }, syntax_ptr) | ||
406 | } | ||
407 | ast::Expr::WhileExpr(e) => { | ||
408 | let condition = if let Some(condition) = e.condition() { | ||
409 | if condition.pat().is_none() { | ||
410 | self.collect_expr_opt(condition.expr()) | ||
411 | } else { | ||
412 | // TODO handle while let | ||
413 | return self.alloc_expr(Expr::Missing, syntax_ptr); | ||
414 | } | ||
415 | } else { | ||
416 | self.exprs.alloc(Expr::Missing) | ||
417 | }; | ||
418 | let body = self.collect_block_opt(e.loop_body()); | ||
419 | self.alloc_expr(Expr::While { condition, body }, syntax_ptr) | ||
420 | } | ||
421 | ast::Expr::ForExpr(e) => { | ||
422 | let iterable = self.collect_expr_opt(e.iterable()); | ||
423 | let pat = self.collect_pat_opt(e.pat()); | ||
424 | let body = self.collect_block_opt(e.loop_body()); | ||
425 | self.alloc_expr( | ||
426 | Expr::For { | ||
427 | iterable, | ||
428 | pat, | ||
429 | body, | ||
430 | }, | ||
431 | syntax_ptr, | ||
432 | ) | ||
433 | } | ||
434 | ast::Expr::CallExpr(e) => { | ||
435 | let callee = self.collect_expr_opt(e.expr()); | ||
436 | let args = if let Some(arg_list) = e.arg_list() { | ||
437 | arg_list.args().map(|e| self.collect_expr(e)).collect() | ||
438 | } else { | ||
439 | Vec::new() | ||
440 | }; | ||
441 | self.alloc_expr(Expr::Call { callee, args }, syntax_ptr) | ||
442 | } | ||
443 | ast::Expr::MethodCallExpr(e) => { | ||
444 | let receiver = self.collect_expr_opt(e.expr()); | ||
445 | let args = if let Some(arg_list) = e.arg_list() { | ||
446 | arg_list.args().map(|e| self.collect_expr(e)).collect() | ||
447 | } else { | ||
448 | Vec::new() | ||
449 | }; | ||
450 | let method_name = e | ||
451 | .name_ref() | ||
452 | .map(|nr| nr.as_name()) | ||
453 | .unwrap_or_else(Name::missing); | ||
454 | self.alloc_expr( | ||
455 | Expr::MethodCall { | ||
456 | receiver, | ||
457 | method_name, | ||
458 | args, | ||
459 | }, | ||
460 | syntax_ptr, | ||
461 | ) | ||
462 | } | ||
463 | ast::Expr::MatchExpr(e) => { | ||
464 | let expr = self.collect_expr_opt(e.expr()); | ||
465 | let arms = if let Some(match_arm_list) = e.match_arm_list() { | ||
466 | match_arm_list | ||
467 | .arms() | ||
468 | .map(|arm| MatchArm { | ||
469 | pats: arm.pats().map(|p| self.collect_pat(p)).collect(), | ||
470 | expr: self.collect_expr_opt(arm.expr()), | ||
471 | }) | ||
472 | .collect() | ||
473 | } else { | ||
474 | Vec::new() | ||
475 | }; | ||
476 | self.alloc_expr(Expr::Match { expr, arms }, syntax_ptr) | ||
477 | } | ||
478 | ast::Expr::PathExpr(e) => { | ||
479 | let path = e | ||
480 | .path() | ||
481 | .and_then(Path::from_ast) | ||
482 | .map(Expr::Path) | ||
483 | .unwrap_or(Expr::Missing); | ||
484 | self.alloc_expr(path, syntax_ptr) | ||
485 | } | ||
486 | ast::Expr::ContinueExpr(_e) => { | ||
487 | // TODO: labels | ||
488 | self.alloc_expr(Expr::Continue, syntax_ptr) | ||
489 | } | ||
490 | ast::Expr::BreakExpr(e) => { | ||
491 | let expr = e.expr().map(|e| self.collect_expr(e)); | ||
492 | self.alloc_expr(Expr::Break { expr }, syntax_ptr) | ||
493 | } | ||
494 | ast::Expr::ParenExpr(e) => { | ||
495 | let inner = self.collect_expr_opt(e.expr()); | ||
496 | // make the paren expr point to the inner expression as well | ||
497 | self.expr_syntax_mapping.insert(syntax_ptr, inner); | ||
498 | inner | ||
499 | } | ||
500 | ast::Expr::ReturnExpr(e) => { | ||
501 | let expr = e.expr().map(|e| self.collect_expr(e)); | ||
502 | self.alloc_expr(Expr::Return { expr }, syntax_ptr) | ||
503 | } | ||
504 | ast::Expr::StructLit(e) => { | ||
505 | let path = e.path().and_then(Path::from_ast); | ||
506 | let fields = if let Some(nfl) = e.named_field_list() { | ||
507 | nfl.fields() | ||
508 | .map(|field| StructLitField { | ||
509 | name: field | ||
510 | .name_ref() | ||
511 | .map(|nr| nr.as_name()) | ||
512 | .unwrap_or_else(Name::missing), | ||
513 | expr: if let Some(e) = field.expr() { | ||
514 | self.collect_expr(e) | ||
515 | } else if let Some(nr) = field.name_ref() { | ||
516 | // field shorthand | ||
517 | let id = self.exprs.alloc(Expr::Path(Path::from_name_ref(nr))); | ||
518 | self.expr_syntax_mapping | ||
519 | .insert(LocalSyntaxPtr::new(nr.syntax()), id); | ||
520 | self.expr_syntax_mapping_back | ||
521 | .insert(id, LocalSyntaxPtr::new(nr.syntax())); | ||
522 | id | ||
523 | } else { | ||
524 | self.exprs.alloc(Expr::Missing) | ||
525 | }, | ||
526 | }) | ||
527 | .collect() | ||
528 | } else { | ||
529 | Vec::new() | ||
530 | }; | ||
531 | let spread = e.spread().map(|s| self.collect_expr(s)); | ||
532 | self.alloc_expr( | ||
533 | Expr::StructLit { | ||
534 | path, | ||
535 | fields, | ||
536 | spread, | ||
537 | }, | ||
538 | syntax_ptr, | ||
539 | ) | ||
540 | } | ||
541 | ast::Expr::FieldExpr(e) => { | ||
542 | let expr = self.collect_expr_opt(e.expr()); | ||
543 | let name = e | ||
544 | .name_ref() | ||
545 | .map(|nr| nr.as_name()) | ||
546 | .unwrap_or_else(Name::missing); | ||
547 | self.alloc_expr(Expr::Field { expr, name }, syntax_ptr) | ||
548 | } | ||
549 | ast::Expr::TryExpr(e) => { | ||
550 | let expr = self.collect_expr_opt(e.expr()); | ||
551 | self.alloc_expr(Expr::Try { expr }, syntax_ptr) | ||
552 | } | ||
553 | ast::Expr::CastExpr(e) => { | ||
554 | let expr = self.collect_expr_opt(e.expr()); | ||
555 | let type_ref = TypeRef::from_ast_opt(e.type_ref()); | ||
556 | self.alloc_expr(Expr::Cast { expr, type_ref }, syntax_ptr) | ||
557 | } | ||
558 | ast::Expr::RefExpr(e) => { | ||
559 | let expr = self.collect_expr_opt(e.expr()); | ||
560 | let mutability = Mutability::from_mutable(e.is_mut()); | ||
561 | self.alloc_expr(Expr::Ref { expr, mutability }, syntax_ptr) | ||
562 | } | ||
563 | ast::Expr::PrefixExpr(e) => { | ||
564 | let expr = self.collect_expr_opt(e.expr()); | ||
565 | let op = e.op(); | ||
566 | self.alloc_expr(Expr::UnaryOp { expr, op }, syntax_ptr) | ||
567 | } | ||
568 | ast::Expr::LambdaExpr(e) => { | ||
569 | let mut args = Vec::new(); | ||
570 | let mut arg_types = Vec::new(); | ||
571 | if let Some(pl) = e.param_list() { | ||
572 | for param in pl.params() { | ||
573 | let pat = self.collect_pat_opt(param.pat()); | ||
574 | let type_ref = param.type_ref().map(TypeRef::from_ast); | ||
575 | args.push(pat); | ||
576 | arg_types.push(type_ref); | ||
577 | } | ||
578 | } | ||
579 | let body = self.collect_expr_opt(e.body()); | ||
580 | self.alloc_expr( | ||
581 | Expr::Lambda { | ||
582 | args, | ||
583 | arg_types, | ||
584 | body, | ||
585 | }, | ||
586 | syntax_ptr, | ||
587 | ) | ||
588 | } | ||
589 | |||
590 | // TODO implement HIR for these: | ||
591 | ast::Expr::Label(_e) => self.alloc_expr(Expr::Missing, syntax_ptr), | ||
592 | ast::Expr::IndexExpr(_e) => self.alloc_expr(Expr::Missing, syntax_ptr), | ||
593 | ast::Expr::TupleExpr(_e) => self.alloc_expr(Expr::Missing, syntax_ptr), | ||
594 | ast::Expr::ArrayExpr(_e) => self.alloc_expr(Expr::Missing, syntax_ptr), | ||
595 | ast::Expr::RangeExpr(_e) => self.alloc_expr(Expr::Missing, syntax_ptr), | ||
596 | ast::Expr::BinExpr(_e) => self.alloc_expr(Expr::Missing, syntax_ptr), | ||
597 | ast::Expr::Literal(_e) => self.alloc_expr(Expr::Missing, syntax_ptr), | ||
598 | } | ||
599 | } | ||
600 | |||
601 | fn collect_expr_opt(&mut self, expr: Option<ast::Expr>) -> ExprId { | ||
602 | if let Some(expr) = expr { | ||
603 | self.collect_expr(expr) | ||
604 | } else { | ||
605 | self.exprs.alloc(Expr::Missing) | ||
606 | } | ||
607 | } | ||
608 | |||
609 | fn collect_block(&mut self, block: ast::Block) -> ExprId { | ||
610 | let statements = block | ||
611 | .statements() | ||
612 | .map(|s| match s { | ||
613 | ast::Stmt::LetStmt(stmt) => { | ||
614 | let pat = self.collect_pat_opt(stmt.pat()); | ||
615 | let type_ref = stmt.type_ref().map(TypeRef::from_ast); | ||
616 | let initializer = stmt.initializer().map(|e| self.collect_expr(e)); | ||
617 | Statement::Let { | ||
618 | pat, | ||
619 | type_ref, | ||
620 | initializer, | ||
621 | } | ||
622 | } | ||
623 | ast::Stmt::ExprStmt(stmt) => Statement::Expr(self.collect_expr_opt(stmt.expr())), | ||
624 | }) | ||
625 | .collect(); | ||
626 | let tail = block.expr().map(|e| self.collect_expr(e)); | ||
627 | self.alloc_expr( | ||
628 | Expr::Block { statements, tail }, | ||
629 | LocalSyntaxPtr::new(block.syntax()), | ||
630 | ) | ||
631 | } | ||
632 | |||
633 | fn collect_block_opt(&mut self, block: Option<ast::Block>) -> ExprId { | ||
634 | if let Some(block) = block { | ||
635 | self.collect_block(block) | ||
636 | } else { | ||
637 | self.exprs.alloc(Expr::Missing) | ||
638 | } | ||
639 | } | ||
640 | |||
641 | fn collect_pat(&mut self, pat: ast::Pat) -> PatId { | ||
642 | let syntax_ptr = LocalSyntaxPtr::new(pat.syntax()); | ||
643 | match pat { | ||
644 | ast::Pat::BindPat(bp) => { | ||
645 | let name = bp | ||
646 | .name() | ||
647 | .map(|nr| nr.as_name()) | ||
648 | .unwrap_or_else(Name::missing); | ||
649 | self.alloc_pat(Pat::Bind { name }, syntax_ptr) | ||
650 | } | ||
651 | ast::Pat::TupleStructPat(p) => { | ||
652 | let path = p.path().and_then(Path::from_ast); | ||
653 | let args = p.args().map(|p| self.collect_pat(p)).collect(); | ||
654 | self.alloc_pat(Pat::TupleStruct { path, args }, syntax_ptr) | ||
655 | } | ||
656 | _ => { | ||
657 | // TODO | ||
658 | self.alloc_pat(Pat::Missing, syntax_ptr) | ||
659 | } | ||
660 | } | ||
661 | } | ||
662 | |||
663 | fn collect_pat_opt(&mut self, pat: Option<ast::Pat>) -> PatId { | ||
664 | if let Some(pat) = pat { | ||
665 | self.collect_pat(pat) | ||
666 | } else { | ||
667 | self.pats.alloc(Pat::Missing) | ||
668 | } | ||
669 | } | ||
670 | |||
671 | fn into_body_syntax_mapping(self, args: Vec<PatId>, body_expr: ExprId) -> BodySyntaxMapping { | ||
672 | let body = Body { | ||
673 | exprs: self.exprs, | ||
674 | pats: self.pats, | ||
675 | args, | ||
676 | body_expr, | ||
677 | }; | ||
678 | BodySyntaxMapping { | ||
679 | body: Arc::new(body), | ||
680 | expr_syntax_mapping: self.expr_syntax_mapping, | ||
681 | expr_syntax_mapping_back: self.expr_syntax_mapping_back, | ||
682 | pat_syntax_mapping: self.pat_syntax_mapping, | ||
683 | pat_syntax_mapping_back: self.pat_syntax_mapping_back, | ||
684 | } | ||
685 | } | ||
686 | } | ||
687 | |||
688 | pub(crate) fn collect_fn_body_syntax(node: ast::FnDef) -> BodySyntaxMapping { | ||
689 | let mut collector = ExprCollector::new(); | ||
690 | |||
691 | let args = if let Some(param_list) = node.param_list() { | ||
692 | let mut args = Vec::new(); | ||
693 | |||
694 | if let Some(self_param) = param_list.self_param() { | ||
695 | let self_param = LocalSyntaxPtr::new( | ||
696 | self_param | ||
697 | .self_kw() | ||
698 | .expect("self param without self keyword") | ||
699 | .syntax(), | ||
700 | ); | ||
701 | let arg = collector.alloc_pat( | ||
702 | Pat::Bind { | ||
703 | name: Name::self_param(), | ||
704 | }, | ||
705 | self_param, | ||
706 | ); | ||
707 | args.push(arg); | ||
708 | } | ||
709 | |||
710 | for param in param_list.params() { | ||
711 | let pat = if let Some(pat) = param.pat() { | ||
712 | pat | ||
713 | } else { | ||
714 | continue; | ||
715 | }; | ||
716 | args.push(collector.collect_pat(pat)); | ||
717 | } | ||
718 | args | ||
719 | } else { | ||
720 | Vec::new() | ||
721 | }; | ||
722 | |||
723 | let body = collector.collect_block_opt(node.body()); | ||
724 | collector.into_body_syntax_mapping(args, body) | ||
725 | } | ||
726 | |||
727 | pub(crate) fn body_syntax_mapping( | ||
728 | db: &impl HirDatabase, | ||
729 | def_id: DefId, | ||
730 | ) -> Cancelable<Arc<BodySyntaxMapping>> { | ||
731 | let def = def_id.resolve(db)?; | ||
732 | |||
733 | let body_syntax_mapping = match def { | ||
734 | Def::Function(f) => { | ||
735 | let node = f.syntax(db); | ||
736 | let node = node.borrowed(); | ||
737 | |||
738 | collect_fn_body_syntax(node) | ||
739 | } | ||
740 | // TODO: consts, etc. | ||
741 | _ => panic!("Trying to get body for item type without body"), | ||
742 | }; | ||
743 | |||
744 | Ok(Arc::new(body_syntax_mapping)) | ||
745 | } | ||
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 8f56cdb15..b685259d7 100644 --- a/crates/ra_hir/src/ty.rs +++ b/crates/ra_hir/src/ty.rs | |||
@@ -31,10 +31,11 @@ use ra_syntax::{ | |||
31 | }; | 31 | }; |
32 | 32 | ||
33 | use crate::{ | 33 | use crate::{ |
34 | Def, DefId, FnScopes, Module, Function, Struct, Enum, Path, Name, AsName, ImplBlock, | 34 | Def, DefId, Module, Function, Struct, Enum, Path, Name, AsName, ImplBlock, |
35 | db::HirDatabase, | 35 | db::HirDatabase, |
36 | type_ref::{TypeRef, Mutability}, | 36 | type_ref::{TypeRef, Mutability}, |
37 | name::KnownName, | 37 | name::KnownName, |
38 | ScopesWithSyntaxMapping, | ||
38 | }; | 39 | }; |
39 | 40 | ||
40 | /// The ID of a type variable. | 41 | /// The ID of a type variable. |
@@ -305,7 +306,7 @@ impl Ty { | |||
305 | return Ok(Ty::Uint(uint_ty)); | 306 | return Ok(Ty::Uint(uint_ty)); |
306 | } else if let Some(float_ty) = primitive::FloatTy::from_name(name) { | 307 | } else if let Some(float_ty) = primitive::FloatTy::from_name(name) { |
307 | return Ok(Ty::Float(float_ty)); | 308 | return Ok(Ty::Float(float_ty)); |
308 | } else if name.as_known_name() == Some(KnownName::Self_) { | 309 | } 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())); | 310 | return Ty::from_hir_opt(db, module, None, impl_block.map(|i| i.target_type())); |
310 | } | 311 | } |
311 | } | 312 | } |
@@ -515,7 +516,7 @@ impl InferenceResult { | |||
515 | #[derive(Clone, Debug)] | 516 | #[derive(Clone, Debug)] |
516 | struct InferenceContext<'a, D: HirDatabase> { | 517 | struct InferenceContext<'a, D: HirDatabase> { |
517 | db: &'a D, | 518 | db: &'a D, |
518 | scopes: Arc<FnScopes>, | 519 | scopes: ScopesWithSyntaxMapping, |
519 | /// The self param for the current method, if it exists. | 520 | /// The self param for the current method, if it exists. |
520 | self_param: Option<LocalSyntaxPtr>, | 521 | self_param: Option<LocalSyntaxPtr>, |
521 | module: Module, | 522 | module: Module, |
@@ -543,7 +544,7 @@ fn is_boolean_operator(op: BinOp) -> bool { | |||
543 | impl<'a, D: HirDatabase> InferenceContext<'a, D> { | 544 | impl<'a, D: HirDatabase> InferenceContext<'a, D> { |
544 | fn new( | 545 | fn new( |
545 | db: &'a D, | 546 | db: &'a D, |
546 | scopes: Arc<FnScopes>, | 547 | scopes: ScopesWithSyntaxMapping, |
547 | module: Module, | 548 | module: Module, |
548 | impl_block: Option<ImplBlock>, | 549 | impl_block: Option<ImplBlock>, |
549 | ) -> Self { | 550 | ) -> Self { |
@@ -840,10 +841,6 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { | |||
840 | self.infer_expr_opt(e.expr(), &Expectation::none())?; | 841 | self.infer_expr_opt(e.expr(), &Expectation::none())?; |
841 | Ty::Never | 842 | Ty::Never |
842 | } | 843 | } |
843 | ast::Expr::MatchArmList(_) | ast::Expr::MatchArm(_) | ast::Expr::MatchGuard(_) => { | ||
844 | // Can this even occur outside of a match expression? | ||
845 | Ty::Unknown | ||
846 | } | ||
847 | ast::Expr::StructLit(e) => { | 844 | ast::Expr::StructLit(e) => { |
848 | let (ty, def_id) = self.resolve_variant(e.path())?; | 845 | let (ty, def_id) = self.resolve_variant(e.path())?; |
849 | if let Some(nfl) = e.named_field_list() { | 846 | if let Some(nfl) = e.named_field_list() { |
@@ -859,10 +856,6 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { | |||
859 | } | 856 | } |
860 | ty | 857 | ty |
861 | } | 858 | } |
862 | ast::Expr::NamedFieldList(_) | ast::Expr::NamedField(_) => { | ||
863 | // Can this even occur outside of a struct literal? | ||
864 | Ty::Unknown | ||
865 | } | ||
866 | ast::Expr::IndexExpr(_e) => Ty::Unknown, | 859 | ast::Expr::IndexExpr(_e) => Ty::Unknown, |
867 | ast::Expr::FieldExpr(e) => { | 860 | ast::Expr::FieldExpr(e) => { |
868 | let receiver_ty = self.infer_expr_opt(e.expr(), &Expectation::none())?; | 861 | let receiver_ty = self.infer_expr_opt(e.expr(), &Expectation::none())?; |
@@ -1047,7 +1040,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { | |||
1047 | 1040 | ||
1048 | pub fn infer(db: &impl HirDatabase, def_id: DefId) -> Cancelable<Arc<InferenceResult>> { | 1041 | pub fn infer(db: &impl HirDatabase, def_id: DefId) -> Cancelable<Arc<InferenceResult>> { |
1049 | let function = Function::new(def_id); // TODO: consts also need inference | 1042 | let function = Function::new(def_id); // TODO: consts also need inference |
1050 | let scopes = function.scopes(db); | 1043 | let scopes = function.scopes(db)?; |
1051 | let module = function.module(db)?; | 1044 | let module = function.module(db)?; |
1052 | let impl_block = function.impl_block(db)?; | 1045 | let impl_block = function.impl_block(db)?; |
1053 | let mut ctx = InferenceContext::new(db, scopes, module, impl_block); | 1046 | let mut ctx = InferenceContext::new(db, scopes, module, impl_block); |
diff --git a/crates/ra_hir/src/ty/tests.rs b/crates/ra_hir/src/ty/tests.rs index 1650606b7..25a354947 100644 --- a/crates/ra_hir/src/ty/tests.rs +++ b/crates/ra_hir/src/ty/tests.rs | |||
@@ -1,7 +1,10 @@ | |||
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 | ||
6 | use salsa::Database; | ||
7 | |||
5 | use ra_db::{SyntaxDatabase}; | 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}; |
@@ -241,3 +244,44 @@ fn ellipsize(mut text: String, max_len: usize) -> String { | |||
241 | fn test_data_dir() -> PathBuf { | 244 | fn test_data_dir() -> PathBuf { |
242 | project_dir().join("crates/ra_hir/src/ty/tests/data") | 245 | project_dir().join("crates/ra_hir/src/ty/tests/data") |
243 | } | 246 | } |
247 | |||
248 | #[test] | ||
249 | #[should_panic] // TODO this should work once hir::Expr is used | ||
250 | fn typing_whitespace_inside_a_function_should_not_invalidate_types() { | ||
251 | let (mut db, pos) = MockDatabase::with_position( | ||
252 | " | ||
253 | //- /lib.rs | ||
254 | fn foo() -> i32 { | ||
255 | <|>1 + 1 | ||
256 | } | ||
257 | ", | ||
258 | ); | ||
259 | let func = source_binder::function_from_position(&db, pos) | ||
260 | .unwrap() | ||
261 | .unwrap(); | ||
262 | { | ||
263 | let events = db.log_executed(|| { | ||
264 | func.infer(&db).unwrap(); | ||
265 | }); | ||
266 | assert!(format!("{:?}", events).contains("infer")) | ||
267 | } | ||
268 | |||
269 | let new_text = " | ||
270 | fn foo() -> i32 { | ||
271 | 1 | ||
272 | + | ||
273 | 1 | ||
274 | } | ||
275 | " | ||
276 | .to_string(); | ||
277 | |||
278 | db.query_mut(ra_db::FileTextQuery) | ||
279 | .set(pos.file_id, Arc::new(new_text)); | ||
280 | |||
281 | { | ||
282 | let events = db.log_executed(|| { | ||
283 | func.infer(&db).unwrap(); | ||
284 | }); | ||
285 | assert!(!format!("{:?}", events).contains("infer"), "{:#?}", events) | ||
286 | } | ||
287 | } | ||
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 | } |