diff options
author | bors[bot] <bors[bot]@users.noreply.github.com> | 2019-01-06 14:51:10 +0000 |
---|---|---|
committer | bors[bot] <bors[bot]@users.noreply.github.com> | 2019-01-06 14:51:10 +0000 |
commit | cf0ce14351af03c620aca784ee2c03aad86b866e (patch) | |
tree | bffd84981df9cca1143807796dc6772ddcfe8e0b /crates/ra_analysis/src | |
parent | eaf553dade9a28b41631387d7c88b09fd0ba64e2 (diff) | |
parent | 733383446fc229a35d4432d14c295c5a01e5a87f (diff) |
Merge #429
429: Reorganize hir public API in terms of code model r=matklad a=matklad
Recently, I've been thinking about introducing "object orient code model" API for rust: a set of APIs with types like `Function`, `Module`, etc, with methods like `get_containing_declaration()`, `get_type()`, etc.
Here's how a similar API might look like in .Net land:
https://docs.microsoft.com/en-us/dotnet/api/microsoft.codeanalysis.semanticmodel?view=roslyn-dotnet
https://docs.microsoft.com/en-us/dotnet/api/microsoft.codeanalysis.imethodsymbol?view=roslyn-dotnet
The main feature of such API is that it can be powered by different backends. For example, one can imagine a backend based on salsa, and a backend which reads all the data from a specially prepared JSON file. The "OO" bit is interesting mostly in this "can swap implementations via dynamic dispatch" aspect, the actual API could have a more database/ECS flavored feeling.
It's not clear at this moment how exactly should we implement such a dynamically (or if we even need dynamism in the first pace) swapable API in Rust, but I'd love to experiment with this a bit.
For starters, I propose creating a `code_model_api` which contains various definition types and their public methods (mandatory implemented as one-liners, so that the API has a header-file feel). Specifically, I propose that each type is a wrapper around some integer ID, and that all methods of it accept a `&db` argument. The actual impl goes elsewhere: into the db queries or, absent a better place, into the `code_model_api_impl`. In the first commit, I've moved the simplest type, `Crate`, over to this pattern.
I *think* that we, at least initially, will be used types from `code_model_api` *inside* `hir` as well, but this is not required: we might pick a different implementation down the line, while preserving the API.
Long term I'd love to replace the `db: &impl HirDatabase` argument by a `mp: &dyn ModelProvider`, implement `ModelProvider` for `T: HirDatabase`, and move `code_model_api` into the separate crate, which does not depend on `hir`.
@flodiebold you've recently done some `Def`s work, would do you think of this plan? Could it become a good API in the future, or is it just a useless boilerplate duplicating method signatures between `code_model_api` and `code_model_impl`?
Co-authored-by: Aleksey Kladov <[email protected]>
Diffstat (limited to 'crates/ra_analysis/src')
-rw-r--r-- | crates/ra_analysis/src/completion/complete_scope.rs | 5 | ||||
-rw-r--r-- | crates/ra_analysis/src/goto_defenition.rs | 4 | ||||
-rw-r--r-- | crates/ra_analysis/src/imp.rs | 30 | ||||
-rw-r--r-- | crates/ra_analysis/src/lib.rs | 2 | ||||
-rw-r--r-- | crates/ra_analysis/src/runnables.rs | 9 |
5 files changed, 26 insertions, 24 deletions
diff --git a/crates/ra_analysis/src/completion/complete_scope.rs b/crates/ra_analysis/src/completion/complete_scope.rs index 21d77aa97..ee9052d3d 100644 --- a/crates/ra_analysis/src/completion/complete_scope.rs +++ b/crates/ra_analysis/src/completion/complete_scope.rs | |||
@@ -20,14 +20,17 @@ pub(super) fn complete_scope(acc: &mut Completions, ctx: &CompletionContext) -> | |||
20 | } | 20 | } |
21 | 21 | ||
22 | let module_scope = module.scope(ctx.db)?; | 22 | let module_scope = module.scope(ctx.db)?; |
23 | let (file_id, _) = module.defenition_source(ctx.db)?; | ||
23 | module_scope | 24 | module_scope |
24 | .entries() | 25 | .entries() |
25 | .filter(|(_name, res)| { | 26 | .filter(|(_name, res)| { |
26 | // Don't expose this item | 27 | // Don't expose this item |
28 | // FIXME: this penetrates through all kinds of abstractions, | ||
29 | // we need to figura out the way to do it less ugly. | ||
27 | match res.import { | 30 | match res.import { |
28 | None => true, | 31 | None => true, |
29 | Some(import) => { | 32 | Some(import) => { |
30 | let range = import.range(ctx.db, module.file_id()); | 33 | let range = import.range(ctx.db, file_id); |
31 | !range.is_subrange(&ctx.leaf.range()) | 34 | !range.is_subrange(&ctx.leaf.range()) |
32 | } | 35 | } |
33 | } | 36 | } |
diff --git a/crates/ra_analysis/src/goto_defenition.rs b/crates/ra_analysis/src/goto_defenition.rs index 68b6ac3ba..aa0616e3b 100644 --- a/crates/ra_analysis/src/goto_defenition.rs +++ b/crates/ra_analysis/src/goto_defenition.rs | |||
@@ -60,8 +60,8 @@ fn name_defenition( | |||
60 | if let Some(child_module) = | 60 | if let Some(child_module) = |
61 | hir::source_binder::module_from_declaration(db, file_id, module)? | 61 | hir::source_binder::module_from_declaration(db, file_id, module)? |
62 | { | 62 | { |
63 | let file_id = child_module.file_id(); | 63 | let (file_id, _) = child_module.defenition_source(db)?; |
64 | let name = match child_module.name() { | 64 | let name = match child_module.name(db)? { |
65 | Some(name) => name.to_string().into(), | 65 | Some(name) => name.to_string().into(), |
66 | None => "".into(), | 66 | None => "".into(), |
67 | }; | 67 | }; |
diff --git a/crates/ra_analysis/src/imp.rs b/crates/ra_analysis/src/imp.rs index 5988fb779..07a966290 100644 --- a/crates/ra_analysis/src/imp.rs +++ b/crates/ra_analysis/src/imp.rs | |||
@@ -105,39 +105,35 @@ impl db::RootDatabase { | |||
105 | &self, | 105 | &self, |
106 | position: FilePosition, | 106 | position: FilePosition, |
107 | ) -> Cancelable<Vec<NavigationTarget>> { | 107 | ) -> Cancelable<Vec<NavigationTarget>> { |
108 | let descr = match source_binder::module_from_position(self, position)? { | 108 | let module = match source_binder::module_from_position(self, position)? { |
109 | None => return Ok(Vec::new()), | 109 | None => return Ok(Vec::new()), |
110 | Some(it) => it, | 110 | Some(it) => it, |
111 | }; | 111 | }; |
112 | let (file_id, decl) = match descr.parent_link_source(self) { | 112 | let (file_id, ast_module) = match module.declaration_source(self)? { |
113 | None => return Ok(Vec::new()), | 113 | None => return Ok(Vec::new()), |
114 | Some(it) => it, | 114 | Some(it) => it, |
115 | }; | 115 | }; |
116 | let decl = decl.borrowed(); | 116 | let ast_module = ast_module.borrowed(); |
117 | let decl_name = decl.name().unwrap(); | 117 | let name = ast_module.name().unwrap(); |
118 | Ok(vec![NavigationTarget { | 118 | Ok(vec![NavigationTarget { |
119 | file_id, | 119 | file_id, |
120 | name: decl_name.text(), | 120 | name: name.text(), |
121 | range: decl_name.syntax().range(), | 121 | range: name.syntax().range(), |
122 | kind: MODULE, | 122 | kind: MODULE, |
123 | ptr: None, | 123 | ptr: None, |
124 | }]) | 124 | }]) |
125 | } | 125 | } |
126 | /// Returns `Vec` for the same reason as `parent_module` | 126 | /// Returns `Vec` for the same reason as `parent_module` |
127 | pub(crate) fn crate_for(&self, file_id: FileId) -> Cancelable<Vec<CrateId>> { | 127 | pub(crate) fn crate_for(&self, file_id: FileId) -> Cancelable<Vec<CrateId>> { |
128 | let descr = match source_binder::module_from_file_id(self, file_id)? { | 128 | let module = match source_binder::module_from_file_id(self, file_id)? { |
129 | Some(it) => it, | ||
129 | None => return Ok(Vec::new()), | 130 | None => return Ok(Vec::new()), |
131 | }; | ||
132 | let krate = match module.krate(self)? { | ||
130 | Some(it) => it, | 133 | Some(it) => it, |
134 | None => return Ok(Vec::new()), | ||
131 | }; | 135 | }; |
132 | let root = descr.crate_root(); | 136 | Ok(vec![krate.crate_id()]) |
133 | let file_id = root.file_id(); | ||
134 | |||
135 | let crate_graph = self.crate_graph(); | ||
136 | let crate_id = crate_graph.crate_id_for_crate_root(file_id); | ||
137 | Ok(crate_id.into_iter().collect()) | ||
138 | } | ||
139 | pub(crate) fn crate_root(&self, crate_id: CrateId) -> FileId { | ||
140 | self.crate_graph().crate_root(crate_id) | ||
141 | } | 137 | } |
142 | pub(crate) fn find_all_refs( | 138 | pub(crate) fn find_all_refs( |
143 | &self, | 139 | &self, |
@@ -209,7 +205,7 @@ impl db::RootDatabase { | |||
209 | }) | 205 | }) |
210 | .collect::<Vec<_>>(); | 206 | .collect::<Vec<_>>(); |
211 | if let Some(m) = source_binder::module_from_file_id(self, file_id)? { | 207 | if let Some(m) = source_binder::module_from_file_id(self, file_id)? { |
212 | for (name_node, problem) in m.problems(self) { | 208 | for (name_node, problem) in m.problems(self)? { |
213 | let source_root = self.file_source_root(file_id); | 209 | let source_root = self.file_source_root(file_id); |
214 | let diag = match problem { | 210 | let diag = match problem { |
215 | Problem::UnresolvedModule { candidate } => { | 211 | Problem::UnresolvedModule { candidate } => { |
diff --git a/crates/ra_analysis/src/lib.rs b/crates/ra_analysis/src/lib.rs index 390c31c3f..77f77e9a8 100644 --- a/crates/ra_analysis/src/lib.rs +++ b/crates/ra_analysis/src/lib.rs | |||
@@ -397,7 +397,7 @@ impl Analysis { | |||
397 | } | 397 | } |
398 | /// Returns the root file of the given crate. | 398 | /// Returns the root file of the given crate. |
399 | pub fn crate_root(&self, crate_id: CrateId) -> Cancelable<FileId> { | 399 | pub fn crate_root(&self, crate_id: CrateId) -> Cancelable<FileId> { |
400 | Ok(self.db.crate_root(crate_id)) | 400 | Ok(self.db.crate_graph().crate_root(crate_id)) |
401 | } | 401 | } |
402 | /// Returns the set of possible targets to run for the current file. | 402 | /// Returns the set of possible targets to run for the current file. |
403 | pub fn runnables(&self, file_id: FileId) -> Cancelable<Vec<Runnable>> { | 403 | pub fn runnables(&self, file_id: FileId) -> Cancelable<Vec<Runnable>> { |
diff --git a/crates/ra_analysis/src/runnables.rs b/crates/ra_analysis/src/runnables.rs index 474267605..216209098 100644 --- a/crates/ra_analysis/src/runnables.rs +++ b/crates/ra_analysis/src/runnables.rs | |||
@@ -72,12 +72,15 @@ fn runnable_mod(db: &RootDatabase, file_id: FileId, module: ast::Module) -> Opti | |||
72 | let range = module.syntax().range(); | 72 | let range = module.syntax().range(); |
73 | let module = | 73 | let module = |
74 | hir::source_binder::module_from_child_node(db, file_id, module.syntax()).ok()??; | 74 | hir::source_binder::module_from_child_node(db, file_id, module.syntax()).ok()??; |
75 | |||
76 | // FIXME: thread cancellation instead of `.ok`ing | ||
75 | let path = module | 77 | let path = module |
76 | .path_to_root() | 78 | .path_to_root(db) |
79 | .ok()? | ||
77 | .into_iter() | 80 | .into_iter() |
78 | .rev() | 81 | .rev() |
79 | .into_iter() | 82 | .filter_map(|it| it.name(db).ok()) |
80 | .filter_map(|it| it.name().map(Clone::clone)) | 83 | .filter_map(|it| it) |
81 | .join("::"); | 84 | .join("::"); |
82 | Some(Runnable { | 85 | Some(Runnable { |
83 | range, | 86 | range, |