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_hir/src/code_model_api.rs | |
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_hir/src/code_model_api.rs')
-rw-r--r-- | crates/ra_hir/src/code_model_api.rs | 110 |
1 files changed, 110 insertions, 0 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 | } | ||