aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_hir/src/nameres
diff options
context:
space:
mode:
authorbors[bot] <bors[bot]@users.noreply.github.com>2019-01-06 14:51:10 +0000
committerbors[bot] <bors[bot]@users.noreply.github.com>2019-01-06 14:51:10 +0000
commitcf0ce14351af03c620aca784ee2c03aad86b866e (patch)
treebffd84981df9cca1143807796dc6772ddcfe8e0b /crates/ra_hir/src/nameres
parenteaf553dade9a28b41631387d7c88b09fd0ba64e2 (diff)
parent733383446fc229a35d4432d14c295c5a01e5a87f (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/nameres')
-rw-r--r--crates/ra_hir/src/nameres/tests.rs273
1 files changed, 273 insertions, 0 deletions
diff --git a/crates/ra_hir/src/nameres/tests.rs b/crates/ra_hir/src/nameres/tests.rs
new file mode 100644
index 000000000..dcbe65aec
--- /dev/null
+++ b/crates/ra_hir/src/nameres/tests.rs
@@ -0,0 +1,273 @@
1use std::sync::Arc;
2
3use salsa::Database;
4use ra_db::{FilesDatabase, CrateGraph};
5use relative_path::RelativePath;
6use test_utils::assert_eq_text;
7
8use crate::{
9 self as hir,
10 db::HirDatabase,
11 mock::MockDatabase,
12};
13
14fn item_map(fixture: &str) -> (Arc<hir::ItemMap>, hir::ModuleId) {
15 let (db, pos) = MockDatabase::with_position(fixture);
16 let source_root = db.file_source_root(pos.file_id);
17 let module = hir::source_binder::module_from_position(&db, pos)
18 .unwrap()
19 .unwrap();
20 let module_id = module.def_id.loc(&db).module_id;
21 (db.item_map(source_root).unwrap(), module_id)
22}
23
24fn check_module_item_map(map: &hir::ItemMap, module_id: hir::ModuleId, expected: &str) {
25 let mut lines = map.per_module[&module_id]
26 .items
27 .iter()
28 .map(|(name, res)| format!("{}: {}", name, dump_resolution(res)))
29 .collect::<Vec<_>>();
30 lines.sort();
31 let actual = lines.join("\n");
32 let expected = expected
33 .trim()
34 .lines()
35 .map(|it| it.trim())
36 .collect::<Vec<_>>()
37 .join("\n");
38 assert_eq_text!(&actual, &expected);
39
40 fn dump_resolution(resolution: &hir::Resolution) -> &'static str {
41 match (
42 resolution.def_id.types.is_some(),
43 resolution.def_id.values.is_some(),
44 ) {
45 (true, true) => "t v",
46 (true, false) => "t",
47 (false, true) => "v",
48 (false, false) => "_",
49 }
50 }
51}
52
53#[test]
54fn item_map_smoke_test() {
55 let (item_map, module_id) = item_map(
56 "
57 //- /lib.rs
58 mod foo;
59
60 use crate::foo::bar::Baz;
61 <|>
62
63 //- /foo/mod.rs
64 pub mod bar;
65
66 //- /foo/bar.rs
67 pub struct Baz;
68 ",
69 );
70 check_module_item_map(
71 &item_map,
72 module_id,
73 "
74 Baz: t v
75 foo: t
76 ",
77 );
78}
79
80#[test]
81fn item_map_contains_items_from_expansions() {
82 let (item_map, module_id) = item_map(
83 "
84 //- /lib.rs
85 mod foo;
86
87 use crate::foo::bar::Baz;
88 <|>
89
90 //- /foo/mod.rs
91 pub mod bar;
92
93 //- /foo/bar.rs
94 salsa::query_group! {
95 trait Baz {}
96 }
97 ",
98 );
99 check_module_item_map(
100 &item_map,
101 module_id,
102 "
103 Baz: t
104 foo: t
105 ",
106 );
107}
108
109#[test]
110fn item_map_using_self() {
111 let (item_map, module_id) = item_map(
112 "
113 //- /lib.rs
114 mod foo;
115 use crate::foo::bar::Baz::{self};
116 <|>
117 //- /foo/mod.rs
118 pub mod bar;
119 //- /foo/bar.rs
120 pub struct Baz;
121 ",
122 );
123 check_module_item_map(
124 &item_map,
125 module_id,
126 "
127 Baz: t v
128 foo: t
129 ",
130 );
131}
132
133#[test]
134fn item_map_across_crates() {
135 let (mut db, sr) = MockDatabase::with_files(
136 "
137 //- /main.rs
138 use test_crate::Baz;
139
140 //- /lib.rs
141 pub struct Baz;
142 ",
143 );
144 let main_id = sr.files[RelativePath::new("/main.rs")];
145 let lib_id = sr.files[RelativePath::new("/lib.rs")];
146
147 let mut crate_graph = CrateGraph::default();
148 let main_crate = crate_graph.add_crate_root(main_id);
149 let lib_crate = crate_graph.add_crate_root(lib_id);
150 crate_graph.add_dep(main_crate, "test_crate".into(), lib_crate);
151
152 db.set_crate_graph(crate_graph);
153
154 let source_root = db.file_source_root(main_id);
155 let module = hir::source_binder::module_from_file_id(&db, main_id)
156 .unwrap()
157 .unwrap();
158 let module_id = module.def_id.loc(&db).module_id;
159 let item_map = db.item_map(source_root).unwrap();
160
161 check_module_item_map(
162 &item_map,
163 module_id,
164 "
165 Baz: t v
166 test_crate: t
167 ",
168 );
169}
170
171#[test]
172fn typing_inside_a_function_should_not_invalidate_item_map() {
173 let (mut db, pos) = MockDatabase::with_position(
174 "
175 //- /lib.rs
176 mod foo;
177
178 use crate::foo::bar::Baz;
179
180 //- /foo/mod.rs
181 pub mod bar;
182
183 //- /foo/bar.rs
184 <|>
185 salsa::query_group! {
186 trait Baz {
187 fn foo() -> i32 { 1 + 1 }
188 }
189 }
190 ",
191 );
192 let source_root = db.file_source_root(pos.file_id);
193 {
194 let events = db.log_executed(|| {
195 db.item_map(source_root).unwrap();
196 });
197 assert!(format!("{:?}", events).contains("item_map"))
198 }
199
200 let new_text = "
201 salsa::query_group! {
202 trait Baz {
203 fn foo() -> i32 { 92 }
204 }
205 }
206 "
207 .to_string();
208
209 db.query_mut(ra_db::FileTextQuery)
210 .set(pos.file_id, Arc::new(new_text));
211
212 {
213 let events = db.log_executed(|| {
214 db.item_map(source_root).unwrap();
215 });
216 assert!(
217 !format!("{:?}", events).contains("item_map"),
218 "{:#?}",
219 events
220 )
221 }
222}
223
224#[test]
225fn typing_inside_a_function_inside_a_macro_should_not_invalidate_item_map() {
226 let (mut db, pos) = MockDatabase::with_position(
227 "
228 //- /lib.rs
229 mod foo;<|>
230
231 use crate::foo::bar::Baz;
232
233 fn foo() -> i32 {
234 1 + 1
235 }
236 //- /foo/mod.rs
237 pub mod bar;
238
239 //- /foo/bar.rs
240 pub struct Baz;
241 ",
242 );
243 let source_root = db.file_source_root(pos.file_id);
244 {
245 let events = db.log_executed(|| {
246 db.item_map(source_root).unwrap();
247 });
248 assert!(format!("{:?}", events).contains("item_map"))
249 }
250
251 let new_text = "
252 mod foo;
253
254 use crate::foo::bar::Baz;
255
256 fn foo() -> i32 { 92 }
257 "
258 .to_string();
259
260 db.query_mut(ra_db::FileTextQuery)
261 .set(pos.file_id, Arc::new(new_text));
262
263 {
264 let events = db.log_executed(|| {
265 db.item_map(source_root).unwrap();
266 });
267 assert!(
268 !format!("{:?}", events).contains("item_map"),
269 "{:#?}",
270 events
271 )
272 }
273}