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.rs237
1 files changed, 198 insertions, 39 deletions
diff --git a/crates/ra_hir/src/expr.rs b/crates/ra_hir/src/expr.rs
index 82955fa55..9262325f2 100644
--- a/crates/ra_hir/src/expr.rs
+++ b/crates/ra_hir/src/expr.rs
@@ -1,74 +1,233 @@
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;
7 6
8use ra_syntax::{ast, AstPtr}; 7use ra_syntax::AstPtr;
9 8
10use crate::{db::HirDatabase, DefWithBody, HasSource, Resolver}; 9use crate::{db::HirDatabase, DefWithBody, HasBody, Resolver};
11
12pub use self::scope::ExprScopes;
13 10
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,
19 }, 19 },
20}; 20};
21 21
22pub(crate) fn body_with_source_map_query(
23 db: &impl HirDatabase,
24 def: DefWithBody,
25) -> (Arc<Body>, Arc<BodySourceMap>) {
26 let mut params = None;
27
28 let (file_id, body) = match def {
29 DefWithBody::Function(f) => {
30 let src = f.source(db);
31 params = src.ast.param_list();
32 (src.file_id, src.ast.body().map(ast::Expr::from))
33 }
34 DefWithBody::Const(c) => {
35 let src = c.source(db);
36 (src.file_id, src.ast.body())
37 }
38 DefWithBody::Static(s) => {
39 let src = s.source(db);
40 (src.file_id, src.ast.body())
41 }
42 };
43 let resolver = hir_def::body::MacroResolver::new(db, def.module(db).id);
44 let (body, source_map) = Body::new(db, resolver, file_id, params, body);
45 (Arc::new(body), Arc::new(source_map))
46}
47
48pub(crate) fn body_query(db: &impl HirDatabase, def: DefWithBody) -> Arc<Body> {
49 db.body_with_source_map(def).0
50}
51
52// needs arbitrary_self_types to be a method... or maybe move to the def? 22// needs arbitrary_self_types to be a method... or maybe move to the def?
53pub(crate) fn resolver_for_expr( 23pub(crate) fn resolver_for_expr(
54 db: &impl HirDatabase, 24 db: &impl HirDatabase,
55 owner: DefWithBody, 25 owner: DefWithBody,
56 expr_id: ExprId, 26 expr_id: ExprId,
57) -> Resolver { 27) -> Resolver {
58 let scopes = db.expr_scopes(owner); 28 let scopes = owner.expr_scopes(db);
59 resolver_for_scope(db, owner, scopes.scope_for(expr_id)) 29 resolver_for_scope(db, owner, scopes.scope_for(expr_id))
60} 30}
61 31
62pub(crate) fn resolver_for_scope( 32pub(crate) fn resolver_for_scope(
63 db: &impl HirDatabase, 33 db: &impl HirDatabase,
64 owner: DefWithBody, 34 owner: DefWithBody,
65 scope_id: Option<scope::ScopeId>, 35 scope_id: Option<ScopeId>,
66) -> Resolver { 36) -> Resolver {
67 let mut r = owner.resolver(db); 37 let mut r = owner.resolver(db);
68 let scopes = db.expr_scopes(owner); 38 let scopes = owner.expr_scopes(db);
69 let scope_chain = scopes.scope_chain(scope_id).collect::<Vec<_>>(); 39 let scope_chain = scopes.scope_chain(scope_id).collect::<Vec<_>>();
70 for scope in scope_chain.into_iter().rev() { 40 for scope in scope_chain.into_iter().rev() {
71 r = r.push_expr_scope(Arc::clone(&scopes), scope); 41 r = r.push_expr_scope(Arc::clone(&scopes), scope);
72 } 42 }
73 r 43 r
74} 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}