aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_hir/src/expr.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_hir/src/expr.rs')
-rw-r--r--crates/ra_hir/src/expr.rs204
1 files changed, 199 insertions, 5 deletions
diff --git a/crates/ra_hir/src/expr.rs b/crates/ra_hir/src/expr.rs
index d19f5d14c..f02104b2d 100644
--- a/crates/ra_hir/src/expr.rs
+++ b/crates/ra_hir/src/expr.rs
@@ -1,6 +1,5 @@
1//! FIXME: write short doc here 1//! FIXME: write short doc here
2 2
3pub(crate) mod scope;
4pub(crate) mod validation; 3pub(crate) mod validation;
5 4
6use std::sync::Arc; 5use std::sync::Arc;
@@ -9,10 +8,11 @@ use ra_syntax::{ast, AstPtr};
9 8
10use crate::{db::HirDatabase, DefWithBody, HasSource, Resolver}; 9use crate::{db::HirDatabase, DefWithBody, HasSource, Resolver};
11 10
12pub use self::scope::ExprScopes;
13
14pub use hir_def::{ 11pub use hir_def::{
15 body::{Body, BodySourceMap, ExprPtr, ExprSource, PatPtr, PatSource}, 12 body::{
13 scope::{ExprScopes, ScopeEntry, ScopeId},
14 Body, BodySourceMap, ExprPtr, ExprSource, PatPtr, PatSource,
15 },
16 expr::{ 16 expr::{
17 ArithOp, Array, BinaryOp, BindingAnnotation, CmpOp, Expr, ExprId, Literal, LogicOp, 17 ArithOp, Array, BinaryOp, BindingAnnotation, CmpOp, Expr, ExprId, Literal, LogicOp,
18 MatchArm, Ordering, Pat, PatId, RecordFieldPat, RecordLitField, Statement, UnaryOp, 18 MatchArm, Ordering, Pat, PatId, RecordFieldPat, RecordLitField, Statement, UnaryOp,
@@ -49,6 +49,11 @@ pub(crate) fn body_query(db: &impl HirDatabase, def: DefWithBody) -> Arc<Body> {
49 db.body_with_source_map(def).0 49 db.body_with_source_map(def).0
50} 50}
51 51
52pub(crate) fn expr_scopes_query(db: &impl HirDatabase, def: DefWithBody) -> Arc<ExprScopes> {
53 let body = db.body(def);
54 Arc::new(ExprScopes::new(&*body))
55}
56
52// needs arbitrary_self_types to be a method... or maybe move to the def? 57// needs arbitrary_self_types to be a method... or maybe move to the def?
53pub(crate) fn resolver_for_expr( 58pub(crate) fn resolver_for_expr(
54 db: &impl HirDatabase, 59 db: &impl HirDatabase,
@@ -62,7 +67,7 @@ pub(crate) fn resolver_for_expr(
62pub(crate) fn resolver_for_scope( 67pub(crate) fn resolver_for_scope(
63 db: &impl HirDatabase, 68 db: &impl HirDatabase,
64 owner: DefWithBody, 69 owner: DefWithBody,
65 scope_id: Option<scope::ScopeId>, 70 scope_id: Option<ScopeId>,
66) -> Resolver { 71) -> Resolver {
67 let mut r = owner.resolver(db); 72 let mut r = owner.resolver(db);
68 let scopes = db.expr_scopes(owner); 73 let scopes = db.expr_scopes(owner);
@@ -72,3 +77,192 @@ pub(crate) fn resolver_for_scope(
72 } 77 }
73 r 78 r
74} 79}
80
81#[cfg(test)]
82mod tests {
83 use hir_expand::Source;
84 use ra_db::{fixture::WithFixture, SourceDatabase};
85 use ra_syntax::{algo::find_node_at_offset, ast, AstNode};
86 use test_utils::{assert_eq_text, extract_offset};
87
88 use crate::{source_binder::SourceAnalyzer, test_db::TestDB};
89
90 fn do_check(code: &str, expected: &[&str]) {
91 let (off, code) = extract_offset(code);
92 let code = {
93 let mut buf = String::new();
94 let off = u32::from(off) as usize;
95 buf.push_str(&code[..off]);
96 buf.push_str("marker");
97 buf.push_str(&code[off..]);
98 buf
99 };
100
101 let (db, file_id) = TestDB::with_single_file(&code);
102
103 let file = db.parse(file_id).ok().unwrap();
104 let marker: ast::PathExpr = find_node_at_offset(file.syntax(), off).unwrap();
105 let analyzer = SourceAnalyzer::new(&db, file_id, marker.syntax(), None);
106
107 let scopes = analyzer.scopes();
108 let expr_id = analyzer
109 .body_source_map()
110 .node_expr(Source { file_id: file_id.into(), ast: &marker.into() })
111 .unwrap();
112 let scope = scopes.scope_for(expr_id);
113
114 let actual = scopes
115 .scope_chain(scope)
116 .flat_map(|scope| scopes.entries(scope))
117 .map(|it| it.name().to_string())
118 .collect::<Vec<_>>()
119 .join("\n");
120 let expected = expected.join("\n");
121 assert_eq_text!(&expected, &actual);
122 }
123
124 #[test]
125 fn test_lambda_scope() {
126 do_check(
127 r"
128 fn quux(foo: i32) {
129 let f = |bar, baz: i32| {
130 <|>
131 };
132 }",
133 &["bar", "baz", "foo"],
134 );
135 }
136
137 #[test]
138 fn test_call_scope() {
139 do_check(
140 r"
141 fn quux() {
142 f(|x| <|> );
143 }",
144 &["x"],
145 );
146 }
147
148 #[test]
149 fn test_method_call_scope() {
150 do_check(
151 r"
152 fn quux() {
153 z.f(|x| <|> );
154 }",
155 &["x"],
156 );
157 }
158
159 #[test]
160 fn test_loop_scope() {
161 do_check(
162 r"
163 fn quux() {
164 loop {
165 let x = ();
166 <|>
167 };
168 }",
169 &["x"],
170 );
171 }
172
173 #[test]
174 fn test_match() {
175 do_check(
176 r"
177 fn quux() {
178 match () {
179 Some(x) => {
180 <|>
181 }
182 };
183 }",
184 &["x"],
185 );
186 }
187
188 #[test]
189 fn test_shadow_variable() {
190 do_check(
191 r"
192 fn foo(x: String) {
193 let x : &str = &x<|>;
194 }",
195 &["x"],
196 );
197 }
198
199 fn do_check_local_name(code: &str, expected_offset: u32) {
200 let (off, code) = extract_offset(code);
201
202 let (db, file_id) = TestDB::with_single_file(&code);
203 let file = db.parse(file_id).ok().unwrap();
204 let expected_name = find_node_at_offset::<ast::Name>(file.syntax(), expected_offset.into())
205 .expect("failed to find a name at the target offset");
206 let name_ref: ast::NameRef = find_node_at_offset(file.syntax(), off).unwrap();
207 let analyzer = SourceAnalyzer::new(&db, file_id, name_ref.syntax(), None);
208
209 let local_name_entry = analyzer.resolve_local_name(&name_ref).unwrap();
210 let local_name =
211 local_name_entry.ptr().either(|it| it.syntax_node_ptr(), |it| it.syntax_node_ptr());
212 assert_eq!(local_name.range(), expected_name.syntax().text_range());
213 }
214
215 #[test]
216 fn test_resolve_local_name() {
217 do_check_local_name(
218 r#"
219 fn foo(x: i32, y: u32) {
220 {
221 let z = x * 2;
222 }
223 {
224 let t = x<|> * 3;
225 }
226 }"#,
227 21,
228 );
229 }
230
231 #[test]
232 fn test_resolve_local_name_declaration() {
233 do_check_local_name(
234 r#"
235 fn foo(x: String) {
236 let x : &str = &x<|>;
237 }"#,
238 21,
239 );
240 }
241
242 #[test]
243 fn test_resolve_local_name_shadow() {
244 do_check_local_name(
245 r"
246 fn foo(x: String) {
247 let x : &str = &x;
248 x<|>
249 }
250 ",
251 53,
252 );
253 }
254
255 #[test]
256 fn ref_patterns_contribute_bindings() {
257 do_check_local_name(
258 r"
259 fn foo() {
260 if let Some(&from) = bar() {
261 from<|>;
262 }
263 }
264 ",
265 53,
266 );
267 }
268}