aboutsummaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
authorAleksey Kladov <[email protected]>2019-11-15 11:47:26 +0000
committerAleksey Kladov <[email protected]>2019-11-15 11:47:26 +0000
commit2f6c0c314b749e25431f3fd6caaac5d3270751b6 (patch)
tree85d898d41976ea57ef1a7a3f6da0a9bdc7b88031 /crates
parent9167da66acff22b4fe68d7bbe60c25ab0b56ad72 (diff)
Move scope tests to hir_def
Diffstat (limited to 'crates')
-rw-r--r--crates/ra_hir/src/expr.rs189
-rw-r--r--crates/ra_hir/src/source_binder.rs24
-rw-r--r--crates/ra_hir_def/src/body/scope.rs219
-rw-r--r--crates/ra_hir_def/src/nameres.rs9
-rw-r--r--crates/ra_hir_def/src/nameres/collector.rs3
5 files changed, 233 insertions, 211 deletions
diff --git a/crates/ra_hir/src/expr.rs b/crates/ra_hir/src/expr.rs
index 899e0fa04..e4598eec0 100644
--- a/crates/ra_hir/src/expr.rs
+++ b/crates/ra_hir/src/expr.rs
@@ -42,192 +42,3 @@ pub(crate) fn resolver_for_scope(
42 } 42 }
43 r 43 r
44} 44}
45
46#[cfg(test)]
47mod tests {
48 use hir_expand::Source;
49 use ra_db::{fixture::WithFixture, SourceDatabase};
50 use ra_syntax::{algo::find_node_at_offset, ast, AstNode};
51 use test_utils::{assert_eq_text, extract_offset};
52
53 use crate::{source_binder::SourceAnalyzer, test_db::TestDB};
54
55 fn do_check(code: &str, expected: &[&str]) {
56 let (off, code) = extract_offset(code);
57 let code = {
58 let mut buf = String::new();
59 let off = u32::from(off) as usize;
60 buf.push_str(&code[..off]);
61 buf.push_str("marker");
62 buf.push_str(&code[off..]);
63 buf
64 };
65
66 let (db, file_id) = TestDB::with_single_file(&code);
67
68 let file = db.parse(file_id).ok().unwrap();
69 let marker: ast::PathExpr = find_node_at_offset(file.syntax(), off).unwrap();
70 let analyzer = SourceAnalyzer::new(&db, file_id, marker.syntax(), None);
71
72 let scopes = analyzer.scopes();
73 let expr_id = analyzer
74 .body_source_map()
75 .node_expr(Source { file_id: file_id.into(), ast: &marker.into() })
76 .unwrap();
77 let scope = scopes.scope_for(expr_id);
78
79 let actual = scopes
80 .scope_chain(scope)
81 .flat_map(|scope| scopes.entries(scope))
82 .map(|it| it.name().to_string())
83 .collect::<Vec<_>>()
84 .join("\n");
85 let expected = expected.join("\n");
86 assert_eq_text!(&expected, &actual);
87 }
88
89 #[test]
90 fn test_lambda_scope() {
91 do_check(
92 r"
93 fn quux(foo: i32) {
94 let f = |bar, baz: i32| {
95 <|>
96 };
97 }",
98 &["bar", "baz", "foo"],
99 );
100 }
101
102 #[test]
103 fn test_call_scope() {
104 do_check(
105 r"
106 fn quux() {
107 f(|x| <|> );
108 }",
109 &["x"],
110 );
111 }
112
113 #[test]
114 fn test_method_call_scope() {
115 do_check(
116 r"
117 fn quux() {
118 z.f(|x| <|> );
119 }",
120 &["x"],
121 );
122 }
123
124 #[test]
125 fn test_loop_scope() {
126 do_check(
127 r"
128 fn quux() {
129 loop {
130 let x = ();
131 <|>
132 };
133 }",
134 &["x"],
135 );
136 }
137
138 #[test]
139 fn test_match() {
140 do_check(
141 r"
142 fn quux() {
143 match () {
144 Some(x) => {
145 <|>
146 }
147 };
148 }",
149 &["x"],
150 );
151 }
152
153 #[test]
154 fn test_shadow_variable() {
155 do_check(
156 r"
157 fn foo(x: String) {
158 let x : &str = &x<|>;
159 }",
160 &["x"],
161 );
162 }
163
164 fn do_check_local_name(code: &str, expected_offset: u32) {
165 let (off, code) = extract_offset(code);
166
167 let (db, file_id) = TestDB::with_single_file(&code);
168 let file = db.parse(file_id).ok().unwrap();
169 let expected_name = find_node_at_offset::<ast::Name>(file.syntax(), expected_offset.into())
170 .expect("failed to find a name at the target offset");
171 let name_ref: ast::NameRef = find_node_at_offset(file.syntax(), off).unwrap();
172 let analyzer = SourceAnalyzer::new(&db, file_id, name_ref.syntax(), None);
173
174 let local_name_entry = analyzer.resolve_local_name(&name_ref).unwrap();
175 let local_name =
176 local_name_entry.ptr().either(|it| it.syntax_node_ptr(), |it| it.syntax_node_ptr());
177 assert_eq!(local_name.range(), expected_name.syntax().text_range());
178 }
179
180 #[test]
181 fn test_resolve_local_name() {
182 do_check_local_name(
183 r#"
184 fn foo(x: i32, y: u32) {
185 {
186 let z = x * 2;
187 }
188 {
189 let t = x<|> * 3;
190 }
191 }"#,
192 21,
193 );
194 }
195
196 #[test]
197 fn test_resolve_local_name_declaration() {
198 do_check_local_name(
199 r#"
200 fn foo(x: String) {
201 let x : &str = &x<|>;
202 }"#,
203 21,
204 );
205 }
206
207 #[test]
208 fn test_resolve_local_name_shadow() {
209 do_check_local_name(
210 r"
211 fn foo(x: String) {
212 let x : &str = &x;
213 x<|>
214 }
215 ",
216 53,
217 );
218 }
219
220 #[test]
221 fn ref_patterns_contribute_bindings() {
222 do_check_local_name(
223 r"
224 fn foo() {
225 if let Some(&from) = bar() {
226 from<|>;
227 }
228 }
229 ",
230 53,
231 );
232 }
233}
diff --git a/crates/ra_hir/src/source_binder.rs b/crates/ra_hir/src/source_binder.rs
index c1ecf18b9..662d3f880 100644
--- a/crates/ra_hir/src/source_binder.rs
+++ b/crates/ra_hir/src/source_binder.rs
@@ -19,7 +19,6 @@ use ra_syntax::{
19 SyntaxKind::*, 19 SyntaxKind::*,
20 SyntaxNode, SyntaxNodePtr, TextRange, TextUnit, 20 SyntaxNode, SyntaxNodePtr, TextRange, TextUnit,
21}; 21};
22use rustc_hash::FxHashSet;
23 22
24use crate::{ 23use crate::{
25 db::HirDatabase, 24 db::HirDatabase,
@@ -286,22 +285,14 @@ impl SourceAnalyzer {
286 } 285 }
287 286
288 fn resolve_local_name(&self, name_ref: &ast::NameRef) -> Option<ScopeEntryWithSyntax> { 287 fn resolve_local_name(&self, name_ref: &ast::NameRef) -> Option<ScopeEntryWithSyntax> {
289 let mut shadowed = FxHashSet::default();
290 let name = name_ref.as_name(); 288 let name = name_ref.as_name();
291 let source_map = self.body_source_map.as_ref()?; 289 let source_map = self.body_source_map.as_ref()?;
292 let scopes = self.scopes.as_ref()?; 290 let scopes = self.scopes.as_ref()?;
293 let scope = scope_for(scopes, source_map, self.file_id.into(), name_ref.syntax()); 291 let scope = scope_for(scopes, source_map, self.file_id.into(), name_ref.syntax())?;
294 let ret = scopes 292 let entry = scopes.resolve_name_in_scope(scope, &name)?;
295 .scope_chain(scope) 293 Some(ScopeEntryWithSyntax {
296 .flat_map(|scope| scopes.entries(scope).iter()) 294 name: entry.name().clone(),
297 .filter(|entry| shadowed.insert(entry.name())) 295 ptr: source_map.pat_syntax(entry.pat())?.ast,
298 .filter(|entry| entry.name() == &name)
299 .nth(0);
300 ret.and_then(|entry| {
301 Some(ScopeEntryWithSyntax {
302 name: entry.name().clone(),
303 ptr: source_map.pat_syntax(entry.pat())?.ast,
304 })
305 }) 296 })
306 } 297 }
307 298
@@ -413,11 +404,6 @@ impl SourceAnalyzer {
413 pub(crate) fn inference_result(&self) -> Arc<crate::ty::InferenceResult> { 404 pub(crate) fn inference_result(&self) -> Arc<crate::ty::InferenceResult> {
414 self.infer.clone().unwrap() 405 self.infer.clone().unwrap()
415 } 406 }
416
417 #[cfg(test)]
418 pub(crate) fn scopes(&self) -> Arc<ExprScopes> {
419 self.scopes.clone().unwrap()
420 }
421} 407}
422 408
423fn scope_for( 409fn scope_for(
diff --git a/crates/ra_hir_def/src/body/scope.rs b/crates/ra_hir_def/src/body/scope.rs
index 09a39e721..10cb87d37 100644
--- a/crates/ra_hir_def/src/body/scope.rs
+++ b/crates/ra_hir_def/src/body/scope.rs
@@ -67,6 +67,11 @@ impl ExprScopes {
67 std::iter::successors(scope, move |&scope| self.scopes[scope].parent) 67 std::iter::successors(scope, move |&scope| self.scopes[scope].parent)
68 } 68 }
69 69
70 pub fn resolve_name_in_scope(&self, scope: ScopeId, name: &Name) -> Option<&ScopeEntry> {
71 self.scope_chain(Some(scope))
72 .find_map(|scope| self.entries(scope).iter().find(|it| it.name == *name))
73 }
74
70 pub fn scope_for(&self, expr: ExprId) -> Option<ScopeId> { 75 pub fn scope_for(&self, expr: ExprId) -> Option<ScopeId> {
71 self.scope_by_expr.get(&expr).copied() 76 self.scope_by_expr.get(&expr).copied()
72 } 77 }
@@ -163,3 +168,217 @@ fn compute_expr_scopes(expr: ExprId, body: &Body, scopes: &mut ExprScopes, scope
163 e => e.walk_child_exprs(|e| compute_expr_scopes(e, body, scopes, scope)), 168 e => e.walk_child_exprs(|e| compute_expr_scopes(e, body, scopes, scope)),
164 }; 169 };
165} 170}
171
172#[cfg(test)]
173mod tests {
174 use hir_expand::{name::AsName, Source};
175 use ra_db::{fixture::WithFixture, FileId, SourceDatabase};
176 use ra_syntax::{algo::find_node_at_offset, ast, AstNode};
177 use test_utils::{assert_eq_text, extract_offset};
178
179 use crate::{db::DefDatabase2, test_db::TestDB, FunctionId, ModuleDefId};
180
181 fn find_function(db: &TestDB, file_id: FileId) -> FunctionId {
182 let krate = db.test_crate();
183 let crate_def_map = db.crate_def_map(krate);
184
185 let module = crate_def_map.modules_for_file(file_id).next().unwrap();
186 let (_, res) = crate_def_map[module].scope.entries().next().unwrap();
187 match res.def.take_values().unwrap() {
188 ModuleDefId::FunctionId(it) => it,
189 _ => panic!(),
190 }
191 }
192
193 fn do_check(code: &str, expected: &[&str]) {
194 let (off, code) = extract_offset(code);
195 let code = {
196 let mut buf = String::new();
197 let off = u32::from(off) as usize;
198 buf.push_str(&code[..off]);
199 buf.push_str("marker");
200 buf.push_str(&code[off..]);
201 buf
202 };
203
204 let (db, file_id) = TestDB::with_single_file(&code);
205
206 let file_syntax = db.parse(file_id).syntax_node();
207 let marker: ast::PathExpr = find_node_at_offset(&file_syntax, off).unwrap();
208 let function = find_function(&db, file_id);
209
210 let scopes = db.expr_scopes(function.into());
211 let (_body, source_map) = db.body_with_source_map(function.into());
212
213 let expr_id =
214 source_map.node_expr(Source { file_id: file_id.into(), ast: &marker.into() }).unwrap();
215 let scope = scopes.scope_for(expr_id);
216
217 let actual = scopes
218 .scope_chain(scope)
219 .flat_map(|scope| scopes.entries(scope))
220 .map(|it| it.name().to_string())
221 .collect::<Vec<_>>()
222 .join("\n");
223 let expected = expected.join("\n");
224 assert_eq_text!(&expected, &actual);
225 }
226
227 #[test]
228 fn test_lambda_scope() {
229 do_check(
230 r"
231 fn quux(foo: i32) {
232 let f = |bar, baz: i32| {
233 <|>
234 };
235 }",
236 &["bar", "baz", "foo"],
237 );
238 }
239
240 #[test]
241 fn test_call_scope() {
242 do_check(
243 r"
244 fn quux() {
245 f(|x| <|> );
246 }",
247 &["x"],
248 );
249 }
250
251 #[test]
252 fn test_method_call_scope() {
253 do_check(
254 r"
255 fn quux() {
256 z.f(|x| <|> );
257 }",
258 &["x"],
259 );
260 }
261
262 #[test]
263 fn test_loop_scope() {
264 do_check(
265 r"
266 fn quux() {
267 loop {
268 let x = ();
269 <|>
270 };
271 }",
272 &["x"],
273 );
274 }
275
276 #[test]
277 fn test_match() {
278 do_check(
279 r"
280 fn quux() {
281 match () {
282 Some(x) => {
283 <|>
284 }
285 };
286 }",
287 &["x"],
288 );
289 }
290
291 #[test]
292 fn test_shadow_variable() {
293 do_check(
294 r"
295 fn foo(x: String) {
296 let x : &str = &x<|>;
297 }",
298 &["x"],
299 );
300 }
301
302 fn do_check_local_name(code: &str, expected_offset: u32) {
303 let (off, code) = extract_offset(code);
304
305 let (db, file_id) = TestDB::with_single_file(&code);
306
307 let file = db.parse(file_id).ok().unwrap();
308 let expected_name = find_node_at_offset::<ast::Name>(file.syntax(), expected_offset.into())
309 .expect("failed to find a name at the target offset");
310 let name_ref: ast::NameRef = find_node_at_offset(file.syntax(), off).unwrap();
311
312 let function = find_function(&db, file_id);
313
314 let scopes = db.expr_scopes(function.into());
315 let (_body, source_map) = db.body_with_source_map(function.into());
316
317 let expr_scope = {
318 let expr_ast = name_ref.syntax().ancestors().find_map(ast::Expr::cast).unwrap();
319 let expr_id =
320 source_map.node_expr(Source { file_id: file_id.into(), ast: &expr_ast }).unwrap();
321 scopes.scope_for(expr_id).unwrap()
322 };
323
324 let resolved = scopes.resolve_name_in_scope(expr_scope, &name_ref.as_name()).unwrap();
325 let pat_src = source_map.pat_syntax(resolved.pat()).unwrap();
326
327 let local_name = pat_src.ast.either(|it| it.syntax_node_ptr(), |it| it.syntax_node_ptr());
328 assert_eq!(local_name.range(), expected_name.syntax().text_range());
329 }
330
331 #[test]
332 fn test_resolve_local_name() {
333 do_check_local_name(
334 r#"
335 fn foo(x: i32, y: u32) {
336 {
337 let z = x * 2;
338 }
339 {
340 let t = x<|> * 3;
341 }
342 }"#,
343 21,
344 );
345 }
346
347 #[test]
348 fn test_resolve_local_name_declaration() {
349 do_check_local_name(
350 r#"
351 fn foo(x: String) {
352 let x : &str = &x<|>;
353 }"#,
354 21,
355 );
356 }
357
358 #[test]
359 fn test_resolve_local_name_shadow() {
360 do_check_local_name(
361 r"
362 fn foo(x: String) {
363 let x : &str = &x;
364 x<|>
365 }
366 ",
367 53,
368 );
369 }
370
371 #[test]
372 fn ref_patterns_contribute_bindings() {
373 do_check_local_name(
374 r"
375 fn foo() {
376 if let Some(&from) = bar() {
377 from<|>;
378 }
379 }
380 ",
381 53,
382 );
383 }
384}
diff --git a/crates/ra_hir_def/src/nameres.rs b/crates/ra_hir_def/src/nameres.rs
index 5fc592150..21d5f62e0 100644
--- a/crates/ra_hir_def/src/nameres.rs
+++ b/crates/ra_hir_def/src/nameres.rs
@@ -58,7 +58,7 @@ mod tests;
58 58
59use std::sync::Arc; 59use std::sync::Arc;
60 60
61use hir_expand::{diagnostics::DiagnosticSink, name::Name, MacroDefId}; 61use hir_expand::{ast_id_map::FileAstId, diagnostics::DiagnosticSink, name::Name, MacroDefId};
62use once_cell::sync::Lazy; 62use once_cell::sync::Lazy;
63use ra_arena::Arena; 63use ra_arena::Arena;
64use ra_db::{CrateId, Edition, FileId}; 64use ra_db::{CrateId, Edition, FileId};
@@ -73,7 +73,7 @@ use crate::{
73 diagnostics::DefDiagnostic, path_resolution::ResolveMode, per_ns::PerNs, raw::ImportId, 73 diagnostics::DefDiagnostic, path_resolution::ResolveMode, per_ns::PerNs, raw::ImportId,
74 }, 74 },
75 path::Path, 75 path::Path,
76 AstId, CrateModuleId, ModuleDefId, ModuleId, TraitId, 76 AstId, CrateModuleId, FunctionId, ModuleDefId, ModuleId, TraitId,
77}; 77};
78 78
79/// Contains all top-level defs from a macro-expanded crate 79/// Contains all top-level defs from a macro-expanded crate
@@ -124,6 +124,11 @@ pub struct ModuleData {
124 pub definition: Option<FileId>, 124 pub definition: Option<FileId>,
125} 125}
126 126
127#[derive(Default, Debug, PartialEq, Eq, Clone)]
128pub(crate) struct Declarations {
129 fns: FxHashMap<FileAstId<ast::FnDef>, FunctionId>,
130}
131
127#[derive(Debug, Default, PartialEq, Eq, Clone)] 132#[derive(Debug, Default, PartialEq, Eq, Clone)]
128pub struct ModuleScope { 133pub struct ModuleScope {
129 pub items: FxHashMap<Name, Resolution>, 134 pub items: FxHashMap<Name, Resolution>,
diff --git a/crates/ra_hir_def/src/nameres/collector.rs b/crates/ra_hir_def/src/nameres/collector.rs
index 83eef821f..5c899aff3 100644
--- a/crates/ra_hir_def/src/nameres/collector.rs
+++ b/crates/ra_hir_def/src/nameres/collector.rs
@@ -664,7 +664,8 @@ where
664 let name = def.name.clone(); 664 let name = def.name.clone();
665 let def: PerNs = match def.kind { 665 let def: PerNs = match def.kind {
666 raw::DefKind::Function(ast_id) => { 666 raw::DefKind::Function(ast_id) => {
667 PerNs::values(FunctionId::from_ast_id(ctx, ast_id).into()) 667 let f = FunctionId::from_ast_id(ctx, ast_id);
668 PerNs::values(f.into())
668 } 669 }
669 raw::DefKind::Struct(ast_id) => { 670 raw::DefKind::Struct(ast_id) => {
670 let id = StructOrUnionId::from_ast_id(ctx, ast_id).into(); 671 let id = StructOrUnionId::from_ast_id(ctx, ast_id).into();