diff options
Diffstat (limited to 'crates')
49 files changed, 1873 insertions, 1097 deletions
diff --git a/crates/ra_assists/src/flip_binexpr.rs b/crates/ra_assists/src/flip_binexpr.rs new file mode 100644 index 000000000..ec377642e --- /dev/null +++ b/crates/ra_assists/src/flip_binexpr.rs | |||
@@ -0,0 +1,141 @@ | |||
1 | use hir::db::HirDatabase; | ||
2 | use ra_syntax::ast::{AstNode, BinExpr, BinOp}; | ||
3 | |||
4 | use crate::{AssistCtx, Assist, AssistId}; | ||
5 | |||
6 | /// Flip binary expression assist. | ||
7 | pub(crate) fn flip_binexpr(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> { | ||
8 | let expr = ctx.node_at_offset::<BinExpr>()?; | ||
9 | let lhs = expr.lhs()?.syntax(); | ||
10 | let rhs = expr.rhs()?.syntax(); | ||
11 | let op_range = expr.op()?.range(); | ||
12 | // The assist should be applied only if the cursor is on the operator | ||
13 | let cursor_in_range = ctx.frange.range.is_subrange(&op_range); | ||
14 | if !cursor_in_range { | ||
15 | return None; | ||
16 | } | ||
17 | let action: FlipAction = expr.op_kind()?.into(); | ||
18 | // The assist should not be applied for certain operators | ||
19 | if let FlipAction::DontFlip = action { | ||
20 | return None; | ||
21 | } | ||
22 | |||
23 | ctx.add_action(AssistId("flip_binexpr"), "flip binary expression", |edit| { | ||
24 | edit.target(op_range); | ||
25 | if let FlipAction::FlipAndReplaceOp(new_op) = action { | ||
26 | edit.replace(op_range, new_op); | ||
27 | } | ||
28 | edit.replace(lhs.range(), rhs.text()); | ||
29 | edit.replace(rhs.range(), lhs.text()); | ||
30 | }); | ||
31 | |||
32 | ctx.build() | ||
33 | } | ||
34 | |||
35 | enum FlipAction { | ||
36 | // Flip the expression | ||
37 | Flip, | ||
38 | // Flip the expression and replace the operator with this string | ||
39 | FlipAndReplaceOp(&'static str), | ||
40 | // Do not flip the expression | ||
41 | DontFlip, | ||
42 | } | ||
43 | |||
44 | impl From<BinOp> for FlipAction { | ||
45 | fn from(op_kind: BinOp) -> Self { | ||
46 | match op_kind { | ||
47 | BinOp::Assignment => FlipAction::DontFlip, | ||
48 | BinOp::AddAssign => FlipAction::DontFlip, | ||
49 | BinOp::DivAssign => FlipAction::DontFlip, | ||
50 | BinOp::MulAssign => FlipAction::DontFlip, | ||
51 | BinOp::RemAssign => FlipAction::DontFlip, | ||
52 | BinOp::ShrAssign => FlipAction::DontFlip, | ||
53 | BinOp::ShlAssign => FlipAction::DontFlip, | ||
54 | BinOp::SubAssign => FlipAction::DontFlip, | ||
55 | BinOp::BitOrAssign => FlipAction::DontFlip, | ||
56 | BinOp::BitAndAssign => FlipAction::DontFlip, | ||
57 | BinOp::BitXorAssign => FlipAction::DontFlip, | ||
58 | BinOp::GreaterTest => FlipAction::FlipAndReplaceOp("<"), | ||
59 | BinOp::GreaterEqualTest => FlipAction::FlipAndReplaceOp("<="), | ||
60 | BinOp::LesserTest => FlipAction::FlipAndReplaceOp(">"), | ||
61 | BinOp::LesserEqualTest => FlipAction::FlipAndReplaceOp(">="), | ||
62 | _ => FlipAction::Flip, | ||
63 | } | ||
64 | } | ||
65 | } | ||
66 | |||
67 | #[cfg(test)] | ||
68 | mod tests { | ||
69 | use super::*; | ||
70 | |||
71 | use crate::helpers::{ check_assist, check_assist_target, check_assist_not_applicable }; | ||
72 | |||
73 | #[test] | ||
74 | fn flip_binexpr_target_is_the_op() { | ||
75 | check_assist_target(flip_binexpr, "fn f() { let res = 1 ==<|> 2; }", "==") | ||
76 | } | ||
77 | |||
78 | #[test] | ||
79 | fn flip_binexpr_not_applicable_for_assignment() { | ||
80 | check_assist_not_applicable(flip_binexpr, "fn f() { let mut _x = 1; _x +=<|> 2 }") | ||
81 | } | ||
82 | |||
83 | #[test] | ||
84 | fn flip_binexpr_works_for_eq() { | ||
85 | check_assist( | ||
86 | flip_binexpr, | ||
87 | "fn f() { let res = 1 ==<|> 2; }", | ||
88 | "fn f() { let res = 2 ==<|> 1; }", | ||
89 | ) | ||
90 | } | ||
91 | |||
92 | #[test] | ||
93 | fn flip_binexpr_works_for_gt() { | ||
94 | check_assist( | ||
95 | flip_binexpr, | ||
96 | "fn f() { let res = 1 ><|> 2; }", | ||
97 | "fn f() { let res = 2 <<|> 1; }", | ||
98 | ) | ||
99 | } | ||
100 | |||
101 | #[test] | ||
102 | fn flip_binexpr_works_for_lteq() { | ||
103 | check_assist( | ||
104 | flip_binexpr, | ||
105 | "fn f() { let res = 1 <=<|> 2; }", | ||
106 | "fn f() { let res = 2 >=<|> 1; }", | ||
107 | ) | ||
108 | } | ||
109 | |||
110 | #[test] | ||
111 | fn flip_binexpr_works_for_complex_expr() { | ||
112 | check_assist( | ||
113 | flip_binexpr, | ||
114 | "fn f() { let res = (1 + 1) ==<|> (2 + 2); }", | ||
115 | "fn f() { let res = (2 + 2) ==<|> (1 + 1); }", | ||
116 | ) | ||
117 | } | ||
118 | |||
119 | #[test] | ||
120 | fn flip_binexpr_works_inside_match() { | ||
121 | check_assist( | ||
122 | flip_binexpr, | ||
123 | r#" | ||
124 | fn dyn_eq(&self, other: &dyn Diagnostic) -> bool { | ||
125 | match other.downcast_ref::<Self>() { | ||
126 | None => false, | ||
127 | Some(it) => it ==<|> self, | ||
128 | } | ||
129 | } | ||
130 | "#, | ||
131 | r#" | ||
132 | fn dyn_eq(&self, other: &dyn Diagnostic) -> bool { | ||
133 | match other.downcast_ref::<Self>() { | ||
134 | None => false, | ||
135 | Some(it) => self ==<|> it, | ||
136 | } | ||
137 | } | ||
138 | "#, | ||
139 | ) | ||
140 | } | ||
141 | } | ||
diff --git a/crates/ra_assists/src/flip_eq_operands.rs b/crates/ra_assists/src/flip_eq_operands.rs deleted file mode 100644 index df0bb689d..000000000 --- a/crates/ra_assists/src/flip_eq_operands.rs +++ /dev/null | |||
@@ -1,86 +0,0 @@ | |||
1 | use hir::db::HirDatabase; | ||
2 | use ra_syntax::ast::{AstNode, BinExpr, BinOp}; | ||
3 | |||
4 | use crate::{AssistCtx, Assist, AssistId}; | ||
5 | |||
6 | pub(crate) fn flip_eq_operands(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> { | ||
7 | let expr = ctx.node_at_offset::<BinExpr>()?; | ||
8 | let lhs = expr.lhs()?.syntax(); | ||
9 | let rhs = expr.rhs()?.syntax(); | ||
10 | let op_range = expr.op()?.range(); | ||
11 | let cursor_in_range = ctx.frange.range.is_subrange(&op_range); | ||
12 | let allowed_ops = [BinOp::EqualityTest, BinOp::NegatedEqualityTest]; | ||
13 | let expr_op = expr.op_kind()?; | ||
14 | if !cursor_in_range || !allowed_ops.iter().any(|o| *o == expr_op) { | ||
15 | return None; | ||
16 | } | ||
17 | ctx.add_action(AssistId("flip_eq_operands"), "flip equality operands", |edit| { | ||
18 | edit.target(op_range); | ||
19 | edit.replace(lhs.range(), rhs.text()); | ||
20 | edit.replace(rhs.range(), lhs.text()); | ||
21 | }); | ||
22 | |||
23 | ctx.build() | ||
24 | } | ||
25 | |||
26 | #[cfg(test)] | ||
27 | mod tests { | ||
28 | use super::*; | ||
29 | |||
30 | use crate::helpers::{check_assist, check_assist_target}; | ||
31 | |||
32 | #[test] | ||
33 | fn flip_eq_operands_for_simple_stmt() { | ||
34 | check_assist( | ||
35 | flip_eq_operands, | ||
36 | "fn f() { let res = 1 ==<|> 2; }", | ||
37 | "fn f() { let res = 2 ==<|> 1; }", | ||
38 | ) | ||
39 | } | ||
40 | |||
41 | #[test] | ||
42 | fn flip_neq_operands_for_simple_stmt() { | ||
43 | check_assist( | ||
44 | flip_eq_operands, | ||
45 | "fn f() { let res = 1 !=<|> 2; }", | ||
46 | "fn f() { let res = 2 !=<|> 1; }", | ||
47 | ) | ||
48 | } | ||
49 | |||
50 | #[test] | ||
51 | fn flip_eq_operands_for_complex_stmt() { | ||
52 | check_assist( | ||
53 | flip_eq_operands, | ||
54 | "fn f() { let res = (1 + 1) ==<|> (2 + 2); }", | ||
55 | "fn f() { let res = (2 + 2) ==<|> (1 + 1); }", | ||
56 | ) | ||
57 | } | ||
58 | |||
59 | #[test] | ||
60 | fn flip_eq_operands_in_match_expr() { | ||
61 | check_assist( | ||
62 | flip_eq_operands, | ||
63 | r#" | ||
64 | fn dyn_eq(&self, other: &dyn Diagnostic) -> bool { | ||
65 | match other.downcast_ref::<Self>() { | ||
66 | None => false, | ||
67 | Some(it) => it ==<|> self, | ||
68 | } | ||
69 | } | ||
70 | "#, | ||
71 | r#" | ||
72 | fn dyn_eq(&self, other: &dyn Diagnostic) -> bool { | ||
73 | match other.downcast_ref::<Self>() { | ||
74 | None => false, | ||
75 | Some(it) => self ==<|> it, | ||
76 | } | ||
77 | } | ||
78 | "#, | ||
79 | ) | ||
80 | } | ||
81 | |||
82 | #[test] | ||
83 | fn flip_eq_operands_target() { | ||
84 | check_assist_target(flip_eq_operands, "fn f() { let res = 1 ==<|> 2; }", "==") | ||
85 | } | ||
86 | } | ||
diff --git a/crates/ra_assists/src/inline_local_variable.rs b/crates/ra_assists/src/inline_local_variable.rs new file mode 100644 index 000000000..bd3cdb970 --- /dev/null +++ b/crates/ra_assists/src/inline_local_variable.rs | |||
@@ -0,0 +1,298 @@ | |||
1 | use hir::db::HirDatabase; | ||
2 | use hir::source_binder::function_from_child_node; | ||
3 | use ra_syntax::{ast::{self, AstNode}, TextRange}; | ||
4 | use ra_syntax::ast::{PatKind, ExprKind}; | ||
5 | |||
6 | use crate::{Assist, AssistCtx, AssistId}; | ||
7 | use crate::assist_ctx::AssistBuilder; | ||
8 | |||
9 | pub(crate) fn inline_local_varialbe(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> { | ||
10 | let let_stmt = ctx.node_at_offset::<ast::LetStmt>()?; | ||
11 | let bind_pat = match let_stmt.pat()?.kind() { | ||
12 | PatKind::BindPat(pat) => pat, | ||
13 | _ => return None, | ||
14 | }; | ||
15 | if bind_pat.is_mutable() { | ||
16 | return None; | ||
17 | } | ||
18 | let initializer = let_stmt.initializer()?; | ||
19 | let wrap_in_parens = match initializer.kind() { | ||
20 | ExprKind::LambdaExpr(_) | ||
21 | | ExprKind::IfExpr(_) | ||
22 | | ExprKind::LoopExpr(_) | ||
23 | | ExprKind::ForExpr(_) | ||
24 | | ExprKind::WhileExpr(_) | ||
25 | | ExprKind::ContinueExpr(_) | ||
26 | | ExprKind::BreakExpr(_) | ||
27 | | ExprKind::Label(_) | ||
28 | | ExprKind::ReturnExpr(_) | ||
29 | | ExprKind::MatchExpr(_) | ||
30 | | ExprKind::StructLit(_) | ||
31 | | ExprKind::CastExpr(_) | ||
32 | | ExprKind::PrefixExpr(_) | ||
33 | | ExprKind::RangeExpr(_) | ||
34 | | ExprKind::BinExpr(_) => true, | ||
35 | ExprKind::CallExpr(_) | ||
36 | | ExprKind::IndexExpr(_) | ||
37 | | ExprKind::MethodCallExpr(_) | ||
38 | | ExprKind::FieldExpr(_) | ||
39 | | ExprKind::TryExpr(_) | ||
40 | | ExprKind::RefExpr(_) | ||
41 | | ExprKind::Literal(_) | ||
42 | | ExprKind::TupleExpr(_) | ||
43 | | ExprKind::ArrayExpr(_) | ||
44 | | ExprKind::ParenExpr(_) | ||
45 | | ExprKind::PathExpr(_) | ||
46 | | ExprKind::BlockExpr(_) => false, | ||
47 | }; | ||
48 | |||
49 | let delete_range = if let Some(whitespace) = | ||
50 | let_stmt.syntax().next_sibling().and_then(ast::Whitespace::cast) | ||
51 | { | ||
52 | TextRange::from_to(let_stmt.syntax().range().start(), whitespace.syntax().range().end()) | ||
53 | } else { | ||
54 | let_stmt.syntax().range() | ||
55 | }; | ||
56 | |||
57 | let init_str = if wrap_in_parens { | ||
58 | format!("({})", initializer.syntax().text().to_string()) | ||
59 | } else { | ||
60 | initializer.syntax().text().to_string() | ||
61 | }; | ||
62 | let function = function_from_child_node(ctx.db, ctx.frange.file_id, bind_pat.syntax())?; | ||
63 | let scope = function.scopes(ctx.db); | ||
64 | let refs = scope.find_all_refs(bind_pat); | ||
65 | |||
66 | ctx.add_action( | ||
67 | AssistId("inline_local_variable"), | ||
68 | "inline local variable", | ||
69 | move |edit: &mut AssistBuilder| { | ||
70 | edit.delete(delete_range); | ||
71 | for desc in refs { | ||
72 | edit.replace(desc.range, init_str.clone()) | ||
73 | } | ||
74 | edit.set_cursor(delete_range.start()) | ||
75 | }, | ||
76 | ); | ||
77 | |||
78 | ctx.build() | ||
79 | } | ||
80 | |||
81 | #[cfg(test)] | ||
82 | mod tests { | ||
83 | use crate::helpers::{check_assist, check_assist_not_applicable}; | ||
84 | |||
85 | use super::*; | ||
86 | |||
87 | #[test] | ||
88 | fn test_inline_let_bind_literal_expr() { | ||
89 | check_assist( | ||
90 | inline_local_varialbe, | ||
91 | " | ||
92 | fn bar(a: usize) {} | ||
93 | fn foo() { | ||
94 | let a<|> = 1; | ||
95 | a + 1; | ||
96 | if a > 10 { | ||
97 | } | ||
98 | |||
99 | while a > 10 { | ||
100 | |||
101 | } | ||
102 | let b = a * 10; | ||
103 | bar(a); | ||
104 | }", | ||
105 | " | ||
106 | fn bar(a: usize) {} | ||
107 | fn foo() { | ||
108 | <|>1 + 1; | ||
109 | if 1 > 10 { | ||
110 | } | ||
111 | |||
112 | while 1 > 10 { | ||
113 | |||
114 | } | ||
115 | let b = 1 * 10; | ||
116 | bar(1); | ||
117 | }", | ||
118 | ); | ||
119 | } | ||
120 | |||
121 | #[test] | ||
122 | fn test_inline_let_bind_bin_expr() { | ||
123 | check_assist( | ||
124 | inline_local_varialbe, | ||
125 | " | ||
126 | fn bar(a: usize) {} | ||
127 | fn foo() { | ||
128 | let a<|> = 1 + 1; | ||
129 | a + 1; | ||
130 | if a > 10 { | ||
131 | } | ||
132 | |||
133 | while a > 10 { | ||
134 | |||
135 | } | ||
136 | let b = a * 10; | ||
137 | bar(a); | ||
138 | }", | ||
139 | " | ||
140 | fn bar(a: usize) {} | ||
141 | fn foo() { | ||
142 | <|>(1 + 1) + 1; | ||
143 | if (1 + 1) > 10 { | ||
144 | } | ||
145 | |||
146 | while (1 + 1) > 10 { | ||
147 | |||
148 | } | ||
149 | let b = (1 + 1) * 10; | ||
150 | bar((1 + 1)); | ||
151 | }", | ||
152 | ); | ||
153 | } | ||
154 | |||
155 | #[test] | ||
156 | fn test_inline_let_bind_function_call_expr() { | ||
157 | check_assist( | ||
158 | inline_local_varialbe, | ||
159 | " | ||
160 | fn bar(a: usize) {} | ||
161 | fn foo() { | ||
162 | let a<|> = bar(1); | ||
163 | a + 1; | ||
164 | if a > 10 { | ||
165 | } | ||
166 | |||
167 | while a > 10 { | ||
168 | |||
169 | } | ||
170 | let b = a * 10; | ||
171 | bar(a); | ||
172 | }", | ||
173 | " | ||
174 | fn bar(a: usize) {} | ||
175 | fn foo() { | ||
176 | <|>bar(1) + 1; | ||
177 | if bar(1) > 10 { | ||
178 | } | ||
179 | |||
180 | while bar(1) > 10 { | ||
181 | |||
182 | } | ||
183 | let b = bar(1) * 10; | ||
184 | bar(bar(1)); | ||
185 | }", | ||
186 | ); | ||
187 | } | ||
188 | |||
189 | #[test] | ||
190 | fn test_inline_let_bind_cast_expr() { | ||
191 | check_assist( | ||
192 | inline_local_varialbe, | ||
193 | " | ||
194 | fn bar(a: usize): usize { a } | ||
195 | fn foo() { | ||
196 | let a<|> = bar(1) as u64; | ||
197 | a + 1; | ||
198 | if a > 10 { | ||
199 | } | ||
200 | |||
201 | while a > 10 { | ||
202 | |||
203 | } | ||
204 | let b = a * 10; | ||
205 | bar(a); | ||
206 | }", | ||
207 | " | ||
208 | fn bar(a: usize): usize { a } | ||
209 | fn foo() { | ||
210 | <|>(bar(1) as u64) + 1; | ||
211 | if (bar(1) as u64) > 10 { | ||
212 | } | ||
213 | |||
214 | while (bar(1) as u64) > 10 { | ||
215 | |||
216 | } | ||
217 | let b = (bar(1) as u64) * 10; | ||
218 | bar((bar(1) as u64)); | ||
219 | }", | ||
220 | ); | ||
221 | } | ||
222 | |||
223 | #[test] | ||
224 | fn test_inline_let_bind_block_expr() { | ||
225 | check_assist( | ||
226 | inline_local_varialbe, | ||
227 | " | ||
228 | fn foo() { | ||
229 | let a<|> = { 10 + 1 }; | ||
230 | a + 1; | ||
231 | if a > 10 { | ||
232 | } | ||
233 | |||
234 | while a > 10 { | ||
235 | |||
236 | } | ||
237 | let b = a * 10; | ||
238 | bar(a); | ||
239 | }", | ||
240 | " | ||
241 | fn foo() { | ||
242 | <|>{ 10 + 1 } + 1; | ||
243 | if { 10 + 1 } > 10 { | ||
244 | } | ||
245 | |||
246 | while { 10 + 1 } > 10 { | ||
247 | |||
248 | } | ||
249 | let b = { 10 + 1 } * 10; | ||
250 | bar({ 10 + 1 }); | ||
251 | }", | ||
252 | ); | ||
253 | } | ||
254 | |||
255 | #[test] | ||
256 | fn test_inline_let_bind_paren_expr() { | ||
257 | check_assist( | ||
258 | inline_local_varialbe, | ||
259 | " | ||
260 | fn foo() { | ||
261 | let a<|> = ( 10 + 1 ); | ||
262 | a + 1; | ||
263 | if a > 10 { | ||
264 | } | ||
265 | |||
266 | while a > 10 { | ||
267 | |||
268 | } | ||
269 | let b = a * 10; | ||
270 | bar(a); | ||
271 | }", | ||
272 | " | ||
273 | fn foo() { | ||
274 | <|>( 10 + 1 ) + 1; | ||
275 | if ( 10 + 1 ) > 10 { | ||
276 | } | ||
277 | |||
278 | while ( 10 + 1 ) > 10 { | ||
279 | |||
280 | } | ||
281 | let b = ( 10 + 1 ) * 10; | ||
282 | bar(( 10 + 1 )); | ||
283 | }", | ||
284 | ); | ||
285 | } | ||
286 | |||
287 | #[test] | ||
288 | fn test_not_inline_mut_variable() { | ||
289 | check_assist_not_applicable( | ||
290 | inline_local_varialbe, | ||
291 | " | ||
292 | fn foo() { | ||
293 | let mut a<|> = 1 + 1; | ||
294 | a + 1; | ||
295 | }", | ||
296 | ); | ||
297 | } | ||
298 | } | ||
diff --git a/crates/ra_assists/src/lib.rs b/crates/ra_assists/src/lib.rs index 8900fbc4b..c1514f8e5 100644 --- a/crates/ra_assists/src/lib.rs +++ b/crates/ra_assists/src/lib.rs | |||
@@ -88,11 +88,12 @@ where | |||
88 | mod add_derive; | 88 | mod add_derive; |
89 | mod add_impl; | 89 | mod add_impl; |
90 | mod flip_comma; | 90 | mod flip_comma; |
91 | mod flip_eq_operands; | 91 | mod flip_binexpr; |
92 | mod change_visibility; | 92 | mod change_visibility; |
93 | mod fill_match_arms; | 93 | mod fill_match_arms; |
94 | mod fill_struct_fields; | 94 | mod fill_struct_fields; |
95 | mod introduce_variable; | 95 | mod introduce_variable; |
96 | mod inline_local_variable; | ||
96 | mod replace_if_let_with_match; | 97 | mod replace_if_let_with_match; |
97 | mod split_import; | 98 | mod split_import; |
98 | mod remove_dbg; | 99 | mod remove_dbg; |
@@ -107,7 +108,7 @@ fn all_assists<DB: HirDatabase>() -> &'static [fn(AssistCtx<DB>) -> Option<Assis | |||
107 | fill_match_arms::fill_match_arms, | 108 | fill_match_arms::fill_match_arms, |
108 | fill_struct_fields::fill_struct_fields, | 109 | fill_struct_fields::fill_struct_fields, |
109 | flip_comma::flip_comma, | 110 | flip_comma::flip_comma, |
110 | flip_eq_operands::flip_eq_operands, | 111 | flip_binexpr::flip_binexpr, |
111 | introduce_variable::introduce_variable, | 112 | introduce_variable::introduce_variable, |
112 | replace_if_let_with_match::replace_if_let_with_match, | 113 | replace_if_let_with_match::replace_if_let_with_match, |
113 | split_import::split_import, | 114 | split_import::split_import, |
@@ -115,6 +116,7 @@ fn all_assists<DB: HirDatabase>() -> &'static [fn(AssistCtx<DB>) -> Option<Assis | |||
115 | auto_import::auto_import, | 116 | auto_import::auto_import, |
116 | add_missing_impl_members::add_missing_impl_members, | 117 | add_missing_impl_members::add_missing_impl_members, |
117 | add_missing_impl_members::add_missing_default_members, | 118 | add_missing_impl_members::add_missing_default_members, |
119 | inline_local_variable::inline_local_varialbe, | ||
118 | ] | 120 | ] |
119 | } | 121 | } |
120 | 122 | ||
diff --git a/crates/ra_cli/Cargo.toml b/crates/ra_cli/Cargo.toml index a92a63257..328b2436f 100644 --- a/crates/ra_cli/Cargo.toml +++ b/crates/ra_cli/Cargo.toml | |||
@@ -14,7 +14,6 @@ indicatif = "0.11.0" | |||
14 | 14 | ||
15 | ra_syntax = { path = "../ra_syntax" } | 15 | ra_syntax = { path = "../ra_syntax" } |
16 | ra_ide_api = { path = "../ra_ide_api" } | 16 | ra_ide_api = { path = "../ra_ide_api" } |
17 | ra_ide_api_light = { path = "../ra_ide_api_light" } | ||
18 | tools = { path = "../tools" } | 17 | tools = { path = "../tools" } |
19 | ra_batch = { path = "../ra_batch" } | 18 | ra_batch = { path = "../ra_batch" } |
20 | ra_hir = { path = "../ra_hir" } | 19 | ra_hir = { path = "../ra_hir" } |
diff --git a/crates/ra_cli/src/main.rs b/crates/ra_cli/src/main.rs index c13c7910c..ecea516f1 100644 --- a/crates/ra_cli/src/main.rs +++ b/crates/ra_cli/src/main.rs | |||
@@ -5,7 +5,7 @@ use std::{fs, io::Read, path::Path}; | |||
5 | use clap::{App, Arg, SubCommand}; | 5 | use clap::{App, Arg, SubCommand}; |
6 | use join_to_string::join; | 6 | use join_to_string::join; |
7 | use ra_ide_api::{Analysis, FileRange}; | 7 | use ra_ide_api::{Analysis, FileRange}; |
8 | use ra_ide_api_light::file_structure; | 8 | use ra_ide_api::file_structure; |
9 | use ra_syntax::{SourceFile, TextRange, TreeArc, AstNode}; | 9 | use ra_syntax::{SourceFile, TextRange, TreeArc, AstNode}; |
10 | use tools::collect_tests; | 10 | use tools::collect_tests; |
11 | use flexi_logger::Logger; | 11 | use flexi_logger::Logger; |
diff --git a/crates/ra_db/Cargo.toml b/crates/ra_db/Cargo.toml index c9b8f3c8e..581cd32fd 100644 --- a/crates/ra_db/Cargo.toml +++ b/crates/ra_db/Cargo.toml | |||
@@ -5,7 +5,7 @@ version = "0.1.0" | |||
5 | authors = ["rust-analyzer developers"] | 5 | authors = ["rust-analyzer developers"] |
6 | 6 | ||
7 | [dependencies] | 7 | [dependencies] |
8 | salsa = "0.10.0" | 8 | salsa = "0.11.1" |
9 | relative-path = "0.4.0" | 9 | relative-path = "0.4.0" |
10 | rustc-hash = "1.0" | 10 | rustc-hash = "1.0" |
11 | parking_lot = "0.7.0" | 11 | parking_lot = "0.7.0" |
diff --git a/crates/ra_hir/src/code_model_api.rs b/crates/ra_hir/src/code_model_api.rs index 45fa4cd11..624c25c4d 100644 --- a/crates/ra_hir/src/code_model_api.rs +++ b/crates/ra_hir/src/code_model_api.rs | |||
@@ -1,8 +1,7 @@ | |||
1 | use std::sync::Arc; | 1 | use std::sync::Arc; |
2 | 2 | ||
3 | use relative_path::RelativePathBuf; | ||
4 | use ra_db::{CrateId, SourceRootId, Edition}; | 3 | use ra_db::{CrateId, SourceRootId, Edition}; |
5 | use ra_syntax::{ast::self, TreeArc, SyntaxNode}; | 4 | use ra_syntax::{ast::self, TreeArc}; |
6 | 5 | ||
7 | use crate::{ | 6 | use crate::{ |
8 | Name, ScopesWithSourceMap, Ty, HirFileId, | 7 | Name, ScopesWithSourceMap, Ty, HirFileId, |
@@ -14,9 +13,11 @@ use crate::{ | |||
14 | adt::{EnumVariantId, StructFieldId, VariantDef}, | 13 | adt::{EnumVariantId, StructFieldId, VariantDef}, |
15 | generics::GenericParams, | 14 | generics::GenericParams, |
16 | docs::{Documentation, Docs, docs_from_ast}, | 15 | docs::{Documentation, Docs, docs_from_ast}, |
17 | ids::{FunctionId, StructId, EnumId, AstItemDef, ConstId, StaticId, TraitId, TypeId}, | 16 | ids::{FunctionId, StructId, EnumId, AstItemDef, ConstId, StaticId, TraitId, TypeAliasId}, |
18 | impl_block::ImplBlock, | 17 | impl_block::ImplBlock, |
19 | resolve::Resolver, | 18 | resolve::Resolver, |
19 | diagnostics::DiagnosticSink, | ||
20 | traits::{TraitItem, TraitData}, | ||
20 | }; | 21 | }; |
21 | 22 | ||
22 | /// hir::Crate describes a single crate. It's the main interface with which | 23 | /// hir::Crate describes a single crate. It's the main interface with which |
@@ -95,11 +96,6 @@ pub enum ModuleSource { | |||
95 | Module(TreeArc<ast::Module>), | 96 | Module(TreeArc<ast::Module>), |
96 | } | 97 | } |
97 | 98 | ||
98 | #[derive(Clone, Debug, Hash, PartialEq, Eq)] | ||
99 | pub enum Problem { | ||
100 | UnresolvedModule { candidate: RelativePathBuf }, | ||
101 | } | ||
102 | |||
103 | impl Module { | 99 | impl Module { |
104 | /// Name of this module. | 100 | /// Name of this module. |
105 | pub fn name(&self, db: &impl HirDatabase) -> Option<Name> { | 101 | pub fn name(&self, db: &impl HirDatabase) -> Option<Name> { |
@@ -171,8 +167,24 @@ impl Module { | |||
171 | db.crate_def_map(self.krate)[self.module_id].scope.clone() | 167 | db.crate_def_map(self.krate)[self.module_id].scope.clone() |
172 | } | 168 | } |
173 | 169 | ||
174 | pub fn problems(&self, db: &impl HirDatabase) -> Vec<(TreeArc<SyntaxNode>, Problem)> { | 170 | pub fn diagnostics(&self, db: &impl HirDatabase, sink: &mut DiagnosticSink) { |
175 | self.problems_impl(db) | 171 | db.crate_def_map(self.krate).add_diagnostics(db, self.module_id, sink); |
172 | for decl in self.declarations(db) { | ||
173 | match decl { | ||
174 | crate::ModuleDef::Function(f) => f.diagnostics(db, sink), | ||
175 | crate::ModuleDef::Module(f) => f.diagnostics(db, sink), | ||
176 | _ => (), | ||
177 | } | ||
178 | } | ||
179 | |||
180 | for impl_block in self.impl_blocks(db) { | ||
181 | for item in impl_block.items(db) { | ||
182 | match item { | ||
183 | crate::ImplItem::Method(f) => f.diagnostics(db, sink), | ||
184 | _ => (), | ||
185 | } | ||
186 | } | ||
187 | } | ||
176 | } | 188 | } |
177 | 189 | ||
178 | pub fn resolver(&self, db: &impl HirDatabase) -> Resolver { | 190 | pub fn resolver(&self, db: &impl HirDatabase) -> Resolver { |
@@ -519,6 +531,10 @@ impl Function { | |||
519 | let r = if !p.params.is_empty() { r.push_generic_params_scope(p) } else { r }; | 531 | let r = if !p.params.is_empty() { r.push_generic_params_scope(p) } else { r }; |
520 | r | 532 | r |
521 | } | 533 | } |
534 | |||
535 | pub fn diagnostics(&self, db: &impl HirDatabase, sink: &mut DiagnosticSink) { | ||
536 | self.infer(db).add_diagnostics(db, *self, sink); | ||
537 | } | ||
522 | } | 538 | } |
523 | 539 | ||
524 | impl Docs for Function { | 540 | impl Docs for Function { |
@@ -634,6 +650,18 @@ impl Trait { | |||
634 | pub fn generic_params(&self, db: &impl DefDatabase) -> Arc<GenericParams> { | 650 | pub fn generic_params(&self, db: &impl DefDatabase) -> Arc<GenericParams> { |
635 | db.generic_params((*self).into()) | 651 | db.generic_params((*self).into()) |
636 | } | 652 | } |
653 | |||
654 | pub fn name(self, db: &impl DefDatabase) -> Option<Name> { | ||
655 | self.trait_data(db).name().clone() | ||
656 | } | ||
657 | |||
658 | pub fn items(self, db: &impl DefDatabase) -> Vec<TraitItem> { | ||
659 | self.trait_data(db).items().to_vec() | ||
660 | } | ||
661 | |||
662 | pub(crate) fn trait_data(self, db: &impl DefDatabase) -> Arc<TraitData> { | ||
663 | db.trait_data(self) | ||
664 | } | ||
637 | } | 665 | } |
638 | 666 | ||
639 | impl Docs for Trait { | 667 | impl Docs for Trait { |
@@ -644,7 +672,7 @@ impl Docs for Trait { | |||
644 | 672 | ||
645 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | 673 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |
646 | pub struct TypeAlias { | 674 | pub struct TypeAlias { |
647 | pub(crate) id: TypeId, | 675 | pub(crate) id: TypeAliasId, |
648 | } | 676 | } |
649 | 677 | ||
650 | impl TypeAlias { | 678 | impl TypeAlias { |
diff --git a/crates/ra_hir/src/code_model_impl/module.rs b/crates/ra_hir/src/code_model_impl/module.rs index 52a33e981..0edb8ade5 100644 --- a/crates/ra_hir/src/code_model_impl/module.rs +++ b/crates/ra_hir/src/code_model_impl/module.rs | |||
@@ -1,18 +1,18 @@ | |||
1 | use ra_db::FileId; | 1 | use ra_db::FileId; |
2 | use ra_syntax::{ast, SyntaxNode, TreeArc, AstNode}; | 2 | use ra_syntax::{ast, TreeArc}; |
3 | 3 | ||
4 | use crate::{ | 4 | use crate::{ |
5 | Module, ModuleSource, Problem, Name, | 5 | Module, ModuleSource, Name, AstId, |
6 | nameres::{CrateModuleId, ImportId}, | 6 | nameres::{CrateModuleId, ImportId}, |
7 | HirDatabase, DefDatabase, | 7 | HirDatabase, DefDatabase, |
8 | HirFileId, SourceItemId, | 8 | HirFileId, |
9 | }; | 9 | }; |
10 | 10 | ||
11 | impl ModuleSource { | 11 | impl ModuleSource { |
12 | pub(crate) fn new( | 12 | pub(crate) fn new( |
13 | db: &impl DefDatabase, | 13 | db: &impl DefDatabase, |
14 | file_id: Option<FileId>, | 14 | file_id: Option<FileId>, |
15 | decl_id: Option<SourceItemId>, | 15 | decl_id: Option<AstId<ast::Module>>, |
16 | ) -> ModuleSource { | 16 | ) -> ModuleSource { |
17 | match (file_id, decl_id) { | 17 | match (file_id, decl_id) { |
18 | (Some(file_id), _) => { | 18 | (Some(file_id), _) => { |
@@ -20,8 +20,7 @@ impl ModuleSource { | |||
20 | ModuleSource::SourceFile(source_file) | 20 | ModuleSource::SourceFile(source_file) |
21 | } | 21 | } |
22 | (None, Some(item_id)) => { | 22 | (None, Some(item_id)) => { |
23 | let module = db.file_item(item_id); | 23 | let module = item_id.to_node(db); |
24 | let module = ast::Module::cast(&*module).unwrap(); | ||
25 | assert!(module.item_list().is_some(), "expected inline module"); | 24 | assert!(module.item_list().is_some(), "expected inline module"); |
26 | ModuleSource::Module(module.to_owned()) | 25 | ModuleSource::Module(module.to_owned()) |
27 | } | 26 | } |
@@ -55,7 +54,7 @@ impl Module { | |||
55 | let decl_id = def_map[self.module_id].declaration; | 54 | let decl_id = def_map[self.module_id].declaration; |
56 | let file_id = def_map[self.module_id].definition; | 55 | let file_id = def_map[self.module_id].definition; |
57 | let module_source = ModuleSource::new(db, file_id, decl_id); | 56 | let module_source = ModuleSource::new(db, file_id, decl_id); |
58 | let file_id = file_id.map(HirFileId::from).unwrap_or_else(|| decl_id.unwrap().file_id); | 57 | let file_id = file_id.map(HirFileId::from).unwrap_or_else(|| decl_id.unwrap().file_id()); |
59 | (file_id, module_source) | 58 | (file_id, module_source) |
60 | } | 59 | } |
61 | 60 | ||
@@ -65,9 +64,8 @@ impl Module { | |||
65 | ) -> Option<(HirFileId, TreeArc<ast::Module>)> { | 64 | ) -> Option<(HirFileId, TreeArc<ast::Module>)> { |
66 | let def_map = db.crate_def_map(self.krate); | 65 | let def_map = db.crate_def_map(self.krate); |
67 | let decl = def_map[self.module_id].declaration?; | 66 | let decl = def_map[self.module_id].declaration?; |
68 | let syntax_node = db.file_item(decl); | 67 | let ast = decl.to_node(db); |
69 | let ast = ast::Module::cast(&syntax_node).unwrap().to_owned(); | 68 | Some((decl.file_id(), ast)) |
70 | Some((decl.file_id, ast)) | ||
71 | } | 69 | } |
72 | 70 | ||
73 | pub(crate) fn import_source_impl( | 71 | pub(crate) fn import_source_impl( |
@@ -76,7 +74,7 @@ impl Module { | |||
76 | import: ImportId, | 74 | import: ImportId, |
77 | ) -> TreeArc<ast::PathSegment> { | 75 | ) -> TreeArc<ast::PathSegment> { |
78 | let (file_id, source) = self.definition_source(db); | 76 | let (file_id, source) = self.definition_source(db); |
79 | let (_, source_map) = db.raw_items_with_source_map(file_id.original_file(db)); | 77 | let (_, source_map) = db.raw_items_with_source_map(file_id); |
80 | source_map.get(&source, import) | 78 | source_map.get(&source, import) |
81 | } | 79 | } |
82 | 80 | ||
@@ -108,19 +106,4 @@ impl Module { | |||
108 | let parent_id = def_map[self.module_id].parent?; | 106 | let parent_id = def_map[self.module_id].parent?; |
109 | Some(self.with_module_id(parent_id)) | 107 | Some(self.with_module_id(parent_id)) |
110 | } | 108 | } |
111 | |||
112 | pub(crate) fn problems_impl( | ||
113 | &self, | ||
114 | db: &impl HirDatabase, | ||
115 | ) -> Vec<(TreeArc<SyntaxNode>, Problem)> { | ||
116 | let def_map = db.crate_def_map(self.krate); | ||
117 | let (my_file_id, _) = self.definition_source(db); | ||
118 | // FIXME: not entirely corret filterint by module | ||
119 | def_map | ||
120 | .problems() | ||
121 | .iter() | ||
122 | .filter(|(source_item_id, _problem)| my_file_id == source_item_id.file_id) | ||
123 | .map(|(source_item_id, problem)| (db.file_item(*source_item_id), problem.clone())) | ||
124 | .collect() | ||
125 | } | ||
126 | } | 109 | } |
diff --git a/crates/ra_hir/src/db.rs b/crates/ra_hir/src/db.rs index d3908f8ac..147005848 100644 --- a/crates/ra_hir/src/db.rs +++ b/crates/ra_hir/src/db.rs | |||
@@ -1,10 +1,10 @@ | |||
1 | use std::sync::Arc; | 1 | use std::sync::Arc; |
2 | 2 | ||
3 | use ra_syntax::{SyntaxNode, TreeArc, SourceFile}; | 3 | use ra_syntax::{SyntaxNode, TreeArc, SourceFile}; |
4 | use ra_db::{SourceDatabase, salsa, FileId}; | 4 | use ra_db::{SourceDatabase, salsa}; |
5 | 5 | ||
6 | use crate::{ | 6 | use crate::{ |
7 | HirFileId, SourceFileItems, SourceItemId, Crate, Module, HirInterner, | 7 | HirFileId, MacroDefId, AstIdMap, ErasedFileAstId, Crate, Module, HirInterner, |
8 | Function, FnSignature, ExprScopes, TypeAlias, | 8 | Function, FnSignature, ExprScopes, TypeAlias, |
9 | Struct, Enum, StructField, | 9 | Struct, Enum, StructField, |
10 | Const, ConstSignature, Static, | 10 | Const, ConstSignature, Static, |
@@ -14,11 +14,15 @@ use crate::{ | |||
14 | impl_block::{ModuleImplBlocks, ImplSourceMap}, | 14 | impl_block::{ModuleImplBlocks, ImplSourceMap}, |
15 | generics::{GenericParams, GenericDef}, | 15 | generics::{GenericParams, GenericDef}, |
16 | type_ref::TypeRef, | 16 | type_ref::TypeRef, |
17 | traits::TraitData, Trait, ty::TraitRef | ||
17 | }; | 18 | }; |
18 | 19 | ||
19 | #[salsa::query_group(DefDatabaseStorage)] | 20 | #[salsa::query_group(DefDatabaseStorage)] |
20 | pub trait DefDatabase: SourceDatabase + AsRef<HirInterner> { | 21 | pub trait DefDatabase: SourceDatabase + AsRef<HirInterner> { |
21 | #[salsa::invoke(HirFileId::hir_parse)] | 22 | #[salsa::invoke(crate::ids::macro_def_query)] |
23 | fn macro_def(&self, macro_id: MacroDefId) -> Option<Arc<mbe::MacroRules>>; | ||
24 | |||
25 | #[salsa::invoke(HirFileId::hir_parse_query)] | ||
22 | fn hir_parse(&self, file_id: HirFileId) -> TreeArc<SourceFile>; | 26 | fn hir_parse(&self, file_id: HirFileId) -> TreeArc<SourceFile>; |
23 | 27 | ||
24 | #[salsa::invoke(crate::adt::StructData::struct_data_query)] | 28 | #[salsa::invoke(crate::adt::StructData::struct_data_query)] |
@@ -27,17 +31,23 @@ pub trait DefDatabase: SourceDatabase + AsRef<HirInterner> { | |||
27 | #[salsa::invoke(crate::adt::EnumData::enum_data_query)] | 31 | #[salsa::invoke(crate::adt::EnumData::enum_data_query)] |
28 | fn enum_data(&self, e: Enum) -> Arc<EnumData>; | 32 | fn enum_data(&self, e: Enum) -> Arc<EnumData>; |
29 | 33 | ||
30 | #[salsa::invoke(crate::ids::SourceFileItems::file_items_query)] | 34 | #[salsa::invoke(crate::traits::TraitData::trait_data_query)] |
31 | fn file_items(&self, file_id: HirFileId) -> Arc<SourceFileItems>; | 35 | fn trait_data(&self, t: Trait) -> Arc<TraitData>; |
36 | |||
37 | #[salsa::invoke(crate::source_id::AstIdMap::ast_id_map_query)] | ||
38 | fn ast_id_map(&self, file_id: HirFileId) -> Arc<AstIdMap>; | ||
32 | 39 | ||
33 | #[salsa::invoke(crate::ids::SourceFileItems::file_item_query)] | 40 | #[salsa::invoke(crate::source_id::AstIdMap::file_item_query)] |
34 | fn file_item(&self, source_item_id: SourceItemId) -> TreeArc<SyntaxNode>; | 41 | fn ast_id_to_node(&self, file_id: HirFileId, ast_id: ErasedFileAstId) -> TreeArc<SyntaxNode>; |
35 | 42 | ||
36 | #[salsa::invoke(RawItems::raw_items_query)] | 43 | #[salsa::invoke(RawItems::raw_items_query)] |
37 | fn raw_items(&self, file_id: FileId) -> Arc<RawItems>; | 44 | fn raw_items(&self, file_id: HirFileId) -> Arc<RawItems>; |
38 | 45 | ||
39 | #[salsa::invoke(RawItems::raw_items_with_source_map_query)] | 46 | #[salsa::invoke(RawItems::raw_items_with_source_map_query)] |
40 | fn raw_items_with_source_map(&self, file_id: FileId) -> (Arc<RawItems>, Arc<ImportSourceMap>); | 47 | fn raw_items_with_source_map( |
48 | &self, | ||
49 | file_id: HirFileId, | ||
50 | ) -> (Arc<RawItems>, Arc<ImportSourceMap>); | ||
41 | 51 | ||
42 | #[salsa::invoke(CrateDefMap::crate_def_map_query)] | 52 | #[salsa::invoke(CrateDefMap::crate_def_map_query)] |
43 | fn crate_def_map(&self, krate: Crate) -> Arc<CrateDefMap>; | 53 | fn crate_def_map(&self, krate: Crate) -> Arc<CrateDefMap>; |
@@ -98,6 +108,9 @@ pub trait HirDatabase: DefDatabase { | |||
98 | 108 | ||
99 | #[salsa::invoke(crate::ty::method_resolution::CrateImplBlocks::impls_in_crate_query)] | 109 | #[salsa::invoke(crate::ty::method_resolution::CrateImplBlocks::impls_in_crate_query)] |
100 | fn impls_in_crate(&self, krate: Crate) -> Arc<CrateImplBlocks>; | 110 | fn impls_in_crate(&self, krate: Crate) -> Arc<CrateImplBlocks>; |
111 | |||
112 | #[salsa::invoke(crate::ty::method_resolution::implements)] | ||
113 | fn implements(&self, trait_ref: TraitRef) -> bool; | ||
101 | } | 114 | } |
102 | 115 | ||
103 | #[test] | 116 | #[test] |
diff --git a/crates/ra_hir/src/diagnostics.rs b/crates/ra_hir/src/diagnostics.rs new file mode 100644 index 000000000..d6a51b833 --- /dev/null +++ b/crates/ra_hir/src/diagnostics.rs | |||
@@ -0,0 +1,115 @@ | |||
1 | use std::{fmt, any::Any}; | ||
2 | |||
3 | use ra_syntax::{SyntaxNodePtr, TreeArc, AstPtr, TextRange, ast, SyntaxNode}; | ||
4 | use relative_path::RelativePathBuf; | ||
5 | |||
6 | use crate::{HirFileId, HirDatabase}; | ||
7 | |||
8 | /// Diagnostic defines hir API for errors and warnings. | ||
9 | /// | ||
10 | /// It is used as a `dyn` object, which you can downcast to a concrete | ||
11 | /// diagnostic. DiagnosticSink are structured, meaning that they include rich | ||
12 | /// information which can be used by IDE to create fixes. DiagnosticSink are | ||
13 | /// expressed in terms of macro-expanded syntax tree nodes (so, it's a bad idea | ||
14 | /// to diagnostic in a salsa value). | ||
15 | /// | ||
16 | /// Internally, various subsystems of hir produce diagnostics specific to a | ||
17 | /// subsystem (typically, an `enum`), which are safe to store in salsa but do not | ||
18 | /// include source locations. Such internal diagnostic are transformed into an | ||
19 | /// instance of `Diagnostic` on demand. | ||
20 | pub trait Diagnostic: Any + Send + Sync + fmt::Debug + 'static { | ||
21 | fn message(&self) -> String; | ||
22 | fn file(&self) -> HirFileId; | ||
23 | fn syntax_node_ptr(&self) -> SyntaxNodePtr; | ||
24 | fn highlight_range(&self) -> TextRange { | ||
25 | self.syntax_node_ptr().range() | ||
26 | } | ||
27 | fn as_any(&self) -> &(dyn Any + Send + 'static); | ||
28 | } | ||
29 | |||
30 | impl dyn Diagnostic { | ||
31 | pub fn syntax_node(&self, db: &impl HirDatabase) -> TreeArc<SyntaxNode> { | ||
32 | let source_file = db.hir_parse(self.file()); | ||
33 | self.syntax_node_ptr().to_node(&source_file).to_owned() | ||
34 | } | ||
35 | pub fn downcast_ref<D: Diagnostic>(&self) -> Option<&D> { | ||
36 | self.as_any().downcast_ref() | ||
37 | } | ||
38 | } | ||
39 | |||
40 | pub struct DiagnosticSink<'a> { | ||
41 | callbacks: Vec<Box<dyn FnMut(&dyn Diagnostic) -> Result<(), ()> + 'a>>, | ||
42 | default_callback: Box<dyn FnMut(&dyn Diagnostic) + 'a>, | ||
43 | } | ||
44 | |||
45 | impl<'a> DiagnosticSink<'a> { | ||
46 | pub fn new(cb: impl FnMut(&dyn Diagnostic) + 'a) -> DiagnosticSink<'a> { | ||
47 | DiagnosticSink { callbacks: Vec::new(), default_callback: Box::new(cb) } | ||
48 | } | ||
49 | |||
50 | pub fn on<D: Diagnostic, F: FnMut(&D) + 'a>(mut self, mut cb: F) -> DiagnosticSink<'a> { | ||
51 | let cb = move |diag: &dyn Diagnostic| match diag.downcast_ref::<D>() { | ||
52 | Some(d) => { | ||
53 | cb(d); | ||
54 | Ok(()) | ||
55 | } | ||
56 | None => Err(()), | ||
57 | }; | ||
58 | self.callbacks.push(Box::new(cb)); | ||
59 | self | ||
60 | } | ||
61 | |||
62 | pub(crate) fn push(&mut self, d: impl Diagnostic) { | ||
63 | let d: &dyn Diagnostic = &d; | ||
64 | for cb in self.callbacks.iter_mut() { | ||
65 | match cb(d) { | ||
66 | Ok(()) => return, | ||
67 | Err(()) => (), | ||
68 | } | ||
69 | } | ||
70 | (self.default_callback)(d) | ||
71 | } | ||
72 | } | ||
73 | |||
74 | #[derive(Debug)] | ||
75 | pub struct NoSuchField { | ||
76 | pub file: HirFileId, | ||
77 | pub field: AstPtr<ast::NamedField>, | ||
78 | } | ||
79 | |||
80 | impl Diagnostic for NoSuchField { | ||
81 | fn message(&self) -> String { | ||
82 | "no such field".to_string() | ||
83 | } | ||
84 | fn file(&self) -> HirFileId { | ||
85 | self.file | ||
86 | } | ||
87 | fn syntax_node_ptr(&self) -> SyntaxNodePtr { | ||
88 | self.field.into() | ||
89 | } | ||
90 | fn as_any(&self) -> &(Any + Send + 'static) { | ||
91 | self | ||
92 | } | ||
93 | } | ||
94 | |||
95 | #[derive(Debug)] | ||
96 | pub struct UnresolvedModule { | ||
97 | pub file: HirFileId, | ||
98 | pub decl: AstPtr<ast::Module>, | ||
99 | pub candidate: RelativePathBuf, | ||
100 | } | ||
101 | |||
102 | impl Diagnostic for UnresolvedModule { | ||
103 | fn message(&self) -> String { | ||
104 | "unresolved module".to_string() | ||
105 | } | ||
106 | fn file(&self) -> HirFileId { | ||
107 | self.file | ||
108 | } | ||
109 | fn syntax_node_ptr(&self) -> SyntaxNodePtr { | ||
110 | self.decl.into() | ||
111 | } | ||
112 | fn as_any(&self) -> &(Any + Send + 'static) { | ||
113 | self | ||
114 | } | ||
115 | } | ||
diff --git a/crates/ra_hir/src/expr.rs b/crates/ra_hir/src/expr.rs index c37fd0454..a85422955 100644 --- a/crates/ra_hir/src/expr.rs +++ b/crates/ra_hir/src/expr.rs | |||
@@ -5,7 +5,7 @@ use rustc_hash::FxHashMap; | |||
5 | 5 | ||
6 | use ra_arena::{Arena, RawId, impl_arena_id, map::ArenaMap}; | 6 | use ra_arena::{Arena, RawId, impl_arena_id, map::ArenaMap}; |
7 | use ra_syntax::{ | 7 | use ra_syntax::{ |
8 | SyntaxNodePtr, AstNode, | 8 | SyntaxNodePtr, AstPtr, AstNode, |
9 | ast::{self, LoopBodyOwner, ArgListOwner, NameOwner, LiteralFlavor, TypeAscriptionOwner} | 9 | ast::{self, LoopBodyOwner, ArgListOwner, NameOwner, LiteralFlavor, TypeAscriptionOwner} |
10 | }; | 10 | }; |
11 | 11 | ||
@@ -54,6 +54,7 @@ pub struct BodySourceMap { | |||
54 | expr_map_back: ArenaMap<ExprId, SyntaxNodePtr>, | 54 | expr_map_back: ArenaMap<ExprId, SyntaxNodePtr>, |
55 | pat_map: FxHashMap<SyntaxNodePtr, PatId>, | 55 | pat_map: FxHashMap<SyntaxNodePtr, PatId>, |
56 | pat_map_back: ArenaMap<PatId, SyntaxNodePtr>, | 56 | pat_map_back: ArenaMap<PatId, SyntaxNodePtr>, |
57 | field_map: FxHashMap<(ExprId, usize), AstPtr<ast::NamedField>>, | ||
57 | } | 58 | } |
58 | 59 | ||
59 | impl Body { | 60 | impl Body { |
@@ -138,6 +139,10 @@ impl BodySourceMap { | |||
138 | pub fn node_pat(&self, node: &ast::Pat) -> Option<PatId> { | 139 | pub fn node_pat(&self, node: &ast::Pat) -> Option<PatId> { |
139 | self.pat_map.get(&SyntaxNodePtr::new(node.syntax())).cloned() | 140 | self.pat_map.get(&SyntaxNodePtr::new(node.syntax())).cloned() |
140 | } | 141 | } |
142 | |||
143 | pub fn field_syntax(&self, expr: ExprId, field: usize) -> AstPtr<ast::NamedField> { | ||
144 | self.field_map[&(expr, field)].clone() | ||
145 | } | ||
141 | } | 146 | } |
142 | 147 | ||
143 | #[derive(Debug, Clone, Eq, PartialEq)] | 148 | #[derive(Debug, Clone, Eq, PartialEq)] |
@@ -629,8 +634,10 @@ impl ExprCollector { | |||
629 | } | 634 | } |
630 | ast::ExprKind::StructLit(e) => { | 635 | ast::ExprKind::StructLit(e) => { |
631 | let path = e.path().and_then(Path::from_ast); | 636 | let path = e.path().and_then(Path::from_ast); |
637 | let mut field_ptrs = Vec::new(); | ||
632 | let fields = if let Some(nfl) = e.named_field_list() { | 638 | let fields = if let Some(nfl) = e.named_field_list() { |
633 | nfl.fields() | 639 | nfl.fields() |
640 | .inspect(|field| field_ptrs.push(AstPtr::new(*field))) | ||
634 | .map(|field| StructLitField { | 641 | .map(|field| StructLitField { |
635 | name: field | 642 | name: field |
636 | .name_ref() | 643 | .name_ref() |
@@ -657,7 +664,11 @@ impl ExprCollector { | |||
657 | Vec::new() | 664 | Vec::new() |
658 | }; | 665 | }; |
659 | let spread = e.spread().map(|s| self.collect_expr(s)); | 666 | let spread = e.spread().map(|s| self.collect_expr(s)); |
660 | self.alloc_expr(Expr::StructLit { path, fields, spread }, syntax_ptr) | 667 | let res = self.alloc_expr(Expr::StructLit { path, fields, spread }, syntax_ptr); |
668 | for (i, ptr) in field_ptrs.into_iter().enumerate() { | ||
669 | self.source_map.field_map.insert((res, i), ptr); | ||
670 | } | ||
671 | res | ||
661 | } | 672 | } |
662 | ast::ExprKind::FieldExpr(e) => { | 673 | ast::ExprKind::FieldExpr(e) => { |
663 | let expr = self.collect_expr_opt(e.expr()); | 674 | let expr = self.collect_expr_opt(e.expr()); |
diff --git a/crates/ra_hir/src/ids.rs b/crates/ra_hir/src/ids.rs index 18401f865..eb9939df7 100644 --- a/crates/ra_hir/src/ids.rs +++ b/crates/ra_hir/src/ids.rs | |||
@@ -1,16 +1,15 @@ | |||
1 | use std::{ | 1 | use std::{ |
2 | marker::PhantomData, | ||
3 | hash::{Hash, Hasher}, | 2 | hash::{Hash, Hasher}, |
4 | sync::Arc, | 3 | sync::Arc, |
5 | }; | 4 | }; |
6 | 5 | ||
7 | use ra_db::{LocationInterner, FileId}; | 6 | use ra_db::{LocationInterner, FileId}; |
8 | use ra_syntax::{TreeArc, SyntaxNode, SourceFile, AstNode, SyntaxNodePtr, ast}; | 7 | use ra_syntax::{TreeArc, SourceFile, AstNode, ast}; |
9 | use ra_arena::{Arena, RawId, ArenaId, impl_arena_id}; | 8 | use ra_arena::{RawId, ArenaId, impl_arena_id}; |
9 | use mbe::MacroRules; | ||
10 | 10 | ||
11 | use crate::{ | 11 | use crate::{ |
12 | Module, | 12 | Module, DefDatabase, AstId, FileAstId, |
13 | DefDatabase, | ||
14 | }; | 13 | }; |
15 | 14 | ||
16 | #[derive(Debug, Default)] | 15 | #[derive(Debug, Default)] |
@@ -22,7 +21,7 @@ pub struct HirInterner { | |||
22 | consts: LocationInterner<ItemLoc<ast::ConstDef>, ConstId>, | 21 | consts: LocationInterner<ItemLoc<ast::ConstDef>, ConstId>, |
23 | statics: LocationInterner<ItemLoc<ast::StaticDef>, StaticId>, | 22 | statics: LocationInterner<ItemLoc<ast::StaticDef>, StaticId>, |
24 | traits: LocationInterner<ItemLoc<ast::TraitDef>, TraitId>, | 23 | traits: LocationInterner<ItemLoc<ast::TraitDef>, TraitId>, |
25 | types: LocationInterner<ItemLoc<ast::TypeAliasDef>, TypeId>, | 24 | types: LocationInterner<ItemLoc<ast::TypeAliasDef>, TypeAliasId>, |
26 | } | 25 | } |
27 | 26 | ||
28 | impl HirInterner { | 27 | impl HirInterner { |
@@ -68,7 +67,7 @@ impl HirFileId { | |||
68 | HirFileIdRepr::File(file_id) => file_id, | 67 | HirFileIdRepr::File(file_id) => file_id, |
69 | HirFileIdRepr::Macro(macro_call_id) => { | 68 | HirFileIdRepr::Macro(macro_call_id) => { |
70 | let loc = macro_call_id.loc(db); | 69 | let loc = macro_call_id.loc(db); |
71 | loc.source_item_id.file_id.original_file(db) | 70 | loc.ast_id.file_id().original_file(db) |
72 | } | 71 | } |
73 | } | 72 | } |
74 | } | 73 | } |
@@ -83,7 +82,10 @@ impl HirFileId { | |||
83 | } | 82 | } |
84 | } | 83 | } |
85 | 84 | ||
86 | pub(crate) fn hir_parse(db: &impl DefDatabase, file_id: HirFileId) -> TreeArc<SourceFile> { | 85 | pub(crate) fn hir_parse_query( |
86 | db: &impl DefDatabase, | ||
87 | file_id: HirFileId, | ||
88 | ) -> TreeArc<SourceFile> { | ||
87 | match file_id.0 { | 89 | match file_id.0 { |
88 | HirFileIdRepr::File(file_id) => db.parse(file_id), | 90 | HirFileIdRepr::File(file_id) => db.parse(file_id), |
89 | HirFileIdRepr::Macro(macro_call_id) => { | 91 | HirFileIdRepr::Macro(macro_call_id) => { |
@@ -96,14 +98,10 @@ impl HirFileId { | |||
96 | 98 | ||
97 | fn parse_macro(db: &impl DefDatabase, macro_call_id: MacroCallId) -> Option<TreeArc<SourceFile>> { | 99 | fn parse_macro(db: &impl DefDatabase, macro_call_id: MacroCallId) -> Option<TreeArc<SourceFile>> { |
98 | let loc = macro_call_id.loc(db); | 100 | let loc = macro_call_id.loc(db); |
99 | let syntax = db.file_item(loc.source_item_id); | 101 | let macro_call = loc.ast_id.to_node(db); |
100 | let macro_call = ast::MacroCall::cast(&syntax).unwrap(); | ||
101 | let (macro_arg, _) = macro_call.token_tree().and_then(mbe::ast_to_token_tree)?; | 102 | let (macro_arg, _) = macro_call.token_tree().and_then(mbe::ast_to_token_tree)?; |
102 | 103 | ||
103 | let def_map = db.crate_def_map(loc.module.krate); | 104 | let macro_rules = db.macro_def(loc.def)?; |
104 | let (krate, macro_id) = def_map.resolve_macro(macro_call_id)?; | ||
105 | let def_map = db.crate_def_map(krate); | ||
106 | let macro_rules = &def_map[macro_id]; | ||
107 | let tt = macro_rules.expand(¯o_arg).ok()?; | 105 | let tt = macro_rules.expand(¯o_arg).ok()?; |
108 | Some(mbe::token_tree_to_ast_item_list(&tt)) | 106 | Some(mbe::token_tree_to_ast_item_list(&tt)) |
109 | } | 107 | } |
@@ -126,6 +124,17 @@ impl From<MacroCallId> for HirFileId { | |||
126 | } | 124 | } |
127 | } | 125 | } |
128 | 126 | ||
127 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | ||
128 | pub struct MacroDefId(pub(crate) AstId<ast::MacroCall>); | ||
129 | |||
130 | pub(crate) fn macro_def_query(db: &impl DefDatabase, id: MacroDefId) -> Option<Arc<MacroRules>> { | ||
131 | let macro_call = id.0.to_node(db); | ||
132 | let arg = macro_call.token_tree()?; | ||
133 | let (tt, _) = mbe::ast_to_token_tree(arg)?; | ||
134 | let rules = MacroRules::parse(&tt).ok()?; | ||
135 | Some(Arc::new(rules)) | ||
136 | } | ||
137 | |||
129 | /// `MacroCallId` identifies a particular macro invocation, like | 138 | /// `MacroCallId` identifies a particular macro invocation, like |
130 | /// `println!("Hello, {}", world)`. | 139 | /// `println!("Hello, {}", world)`. |
131 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | 140 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |
@@ -134,8 +143,8 @@ impl_arena_id!(MacroCallId); | |||
134 | 143 | ||
135 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] | 144 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] |
136 | pub struct MacroCallLoc { | 145 | pub struct MacroCallLoc { |
137 | pub(crate) module: Module, | 146 | pub(crate) def: MacroDefId, |
138 | pub(crate) source_item_id: SourceItemId, | 147 | pub(crate) ast_id: AstId<ast::MacroCall>, |
139 | } | 148 | } |
140 | 149 | ||
141 | impl MacroCallId { | 150 | impl MacroCallId { |
@@ -145,7 +154,6 @@ impl MacroCallId { | |||
145 | } | 154 | } |
146 | 155 | ||
147 | impl MacroCallLoc { | 156 | impl MacroCallLoc { |
148 | #[allow(unused)] | ||
149 | pub(crate) fn id(&self, db: &impl AsRef<HirInterner>) -> MacroCallId { | 157 | pub(crate) fn id(&self, db: &impl AsRef<HirInterner>) -> MacroCallId { |
150 | db.as_ref().macros.loc2id(&self) | 158 | db.as_ref().macros.loc2id(&self) |
151 | } | 159 | } |
@@ -154,26 +162,25 @@ impl MacroCallLoc { | |||
154 | #[derive(Debug)] | 162 | #[derive(Debug)] |
155 | pub struct ItemLoc<N: AstNode> { | 163 | pub struct ItemLoc<N: AstNode> { |
156 | pub(crate) module: Module, | 164 | pub(crate) module: Module, |
157 | raw: SourceItemId, | 165 | ast_id: AstId<N>, |
158 | _ty: PhantomData<N>, | ||
159 | } | 166 | } |
160 | 167 | ||
161 | impl<N: AstNode> PartialEq for ItemLoc<N> { | 168 | impl<N: AstNode> PartialEq for ItemLoc<N> { |
162 | fn eq(&self, other: &Self) -> bool { | 169 | fn eq(&self, other: &Self) -> bool { |
163 | self.module == other.module && self.raw == other.raw | 170 | self.module == other.module && self.ast_id == other.ast_id |
164 | } | 171 | } |
165 | } | 172 | } |
166 | impl<N: AstNode> Eq for ItemLoc<N> {} | 173 | impl<N: AstNode> Eq for ItemLoc<N> {} |
167 | impl<N: AstNode> Hash for ItemLoc<N> { | 174 | impl<N: AstNode> Hash for ItemLoc<N> { |
168 | fn hash<H: Hasher>(&self, hasher: &mut H) { | 175 | fn hash<H: Hasher>(&self, hasher: &mut H) { |
169 | self.module.hash(hasher); | 176 | self.module.hash(hasher); |
170 | self.raw.hash(hasher); | 177 | self.ast_id.hash(hasher); |
171 | } | 178 | } |
172 | } | 179 | } |
173 | 180 | ||
174 | impl<N: AstNode> Clone for ItemLoc<N> { | 181 | impl<N: AstNode> Clone for ItemLoc<N> { |
175 | fn clone(&self) -> ItemLoc<N> { | 182 | fn clone(&self) -> ItemLoc<N> { |
176 | ItemLoc { module: self.module, raw: self.raw, _ty: PhantomData } | 183 | ItemLoc { module: self.module, ast_id: self.ast_id } |
177 | } | 184 | } |
178 | } | 185 | } |
179 | 186 | ||
@@ -200,26 +207,19 @@ impl<'a, DB: DefDatabase> LocationCtx<&'a DB> { | |||
200 | pub(crate) trait AstItemDef<N: AstNode>: ArenaId + Clone { | 207 | pub(crate) trait AstItemDef<N: AstNode>: ArenaId + Clone { |
201 | fn interner(interner: &HirInterner) -> &LocationInterner<ItemLoc<N>, Self>; | 208 | fn interner(interner: &HirInterner) -> &LocationInterner<ItemLoc<N>, Self>; |
202 | fn from_ast(ctx: LocationCtx<&impl DefDatabase>, ast: &N) -> Self { | 209 | fn from_ast(ctx: LocationCtx<&impl DefDatabase>, ast: &N) -> Self { |
203 | let items = ctx.db.file_items(ctx.file_id); | 210 | let items = ctx.db.ast_id_map(ctx.file_id); |
204 | let item_id = items.id_of(ctx.file_id, ast.syntax()); | 211 | let item_id = items.ast_id(ast); |
205 | Self::from_source_item_id_unchecked(ctx, item_id) | 212 | Self::from_ast_id(ctx, item_id) |
206 | } | 213 | } |
207 | fn from_source_item_id_unchecked( | 214 | fn from_ast_id(ctx: LocationCtx<&impl DefDatabase>, ast_id: FileAstId<N>) -> Self { |
208 | ctx: LocationCtx<&impl DefDatabase>, | 215 | let loc = ItemLoc { module: ctx.module, ast_id: ast_id.with_file_id(ctx.file_id) }; |
209 | item_id: SourceFileItemId, | ||
210 | ) -> Self { | ||
211 | let raw = SourceItemId { file_id: ctx.file_id, item_id }; | ||
212 | let loc = ItemLoc { module: ctx.module, raw, _ty: PhantomData }; | ||
213 | |||
214 | Self::interner(ctx.db.as_ref()).loc2id(&loc) | 216 | Self::interner(ctx.db.as_ref()).loc2id(&loc) |
215 | } | 217 | } |
216 | fn source(self, db: &impl DefDatabase) -> (HirFileId, TreeArc<N>) { | 218 | fn source(self, db: &impl DefDatabase) -> (HirFileId, TreeArc<N>) { |
217 | let int = Self::interner(db.as_ref()); | 219 | let int = Self::interner(db.as_ref()); |
218 | let loc = int.id2loc(self); | 220 | let loc = int.id2loc(self); |
219 | let syntax = db.file_item(loc.raw); | 221 | let ast = loc.ast_id.to_node(db); |
220 | let ast = | 222 | (loc.ast_id.file_id(), ast) |
221 | N::cast(&syntax).unwrap_or_else(|| panic!("invalid ItemLoc: {:?}", loc.raw)).to_owned(); | ||
222 | (loc.raw.file_id, ast) | ||
223 | } | 223 | } |
224 | fn module(self, db: &impl DefDatabase) -> Module { | 224 | fn module(self, db: &impl DefDatabase) -> Module { |
225 | let int = Self::interner(db.as_ref()); | 225 | let int = Self::interner(db.as_ref()); |
@@ -229,7 +229,7 @@ pub(crate) trait AstItemDef<N: AstNode>: ArenaId + Clone { | |||
229 | } | 229 | } |
230 | 230 | ||
231 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | 231 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |
232 | pub struct FunctionId(RawId); | 232 | pub(crate) struct FunctionId(RawId); |
233 | impl_arena_id!(FunctionId); | 233 | impl_arena_id!(FunctionId); |
234 | impl AstItemDef<ast::FnDef> for FunctionId { | 234 | impl AstItemDef<ast::FnDef> for FunctionId { |
235 | fn interner(interner: &HirInterner) -> &LocationInterner<ItemLoc<ast::FnDef>, Self> { | 235 | fn interner(interner: &HirInterner) -> &LocationInterner<ItemLoc<ast::FnDef>, Self> { |
@@ -238,7 +238,7 @@ impl AstItemDef<ast::FnDef> for FunctionId { | |||
238 | } | 238 | } |
239 | 239 | ||
240 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | 240 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |
241 | pub struct StructId(RawId); | 241 | pub(crate) struct StructId(RawId); |
242 | impl_arena_id!(StructId); | 242 | impl_arena_id!(StructId); |
243 | impl AstItemDef<ast::StructDef> for StructId { | 243 | impl AstItemDef<ast::StructDef> for StructId { |
244 | fn interner(interner: &HirInterner) -> &LocationInterner<ItemLoc<ast::StructDef>, Self> { | 244 | fn interner(interner: &HirInterner) -> &LocationInterner<ItemLoc<ast::StructDef>, Self> { |
@@ -247,7 +247,7 @@ impl AstItemDef<ast::StructDef> for StructId { | |||
247 | } | 247 | } |
248 | 248 | ||
249 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | 249 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |
250 | pub struct EnumId(RawId); | 250 | pub(crate) struct EnumId(RawId); |
251 | impl_arena_id!(EnumId); | 251 | impl_arena_id!(EnumId); |
252 | impl AstItemDef<ast::EnumDef> for EnumId { | 252 | impl AstItemDef<ast::EnumDef> for EnumId { |
253 | fn interner(interner: &HirInterner) -> &LocationInterner<ItemLoc<ast::EnumDef>, Self> { | 253 | fn interner(interner: &HirInterner) -> &LocationInterner<ItemLoc<ast::EnumDef>, Self> { |
@@ -256,7 +256,7 @@ impl AstItemDef<ast::EnumDef> for EnumId { | |||
256 | } | 256 | } |
257 | 257 | ||
258 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | 258 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |
259 | pub struct ConstId(RawId); | 259 | pub(crate) struct ConstId(RawId); |
260 | impl_arena_id!(ConstId); | 260 | impl_arena_id!(ConstId); |
261 | impl AstItemDef<ast::ConstDef> for ConstId { | 261 | impl AstItemDef<ast::ConstDef> for ConstId { |
262 | fn interner(interner: &HirInterner) -> &LocationInterner<ItemLoc<ast::ConstDef>, Self> { | 262 | fn interner(interner: &HirInterner) -> &LocationInterner<ItemLoc<ast::ConstDef>, Self> { |
@@ -265,7 +265,7 @@ impl AstItemDef<ast::ConstDef> for ConstId { | |||
265 | } | 265 | } |
266 | 266 | ||
267 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | 267 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |
268 | pub struct StaticId(RawId); | 268 | pub(crate) struct StaticId(RawId); |
269 | impl_arena_id!(StaticId); | 269 | impl_arena_id!(StaticId); |
270 | impl AstItemDef<ast::StaticDef> for StaticId { | 270 | impl AstItemDef<ast::StaticDef> for StaticId { |
271 | fn interner(interner: &HirInterner) -> &LocationInterner<ItemLoc<ast::StaticDef>, Self> { | 271 | fn interner(interner: &HirInterner) -> &LocationInterner<ItemLoc<ast::StaticDef>, Self> { |
@@ -274,7 +274,7 @@ impl AstItemDef<ast::StaticDef> for StaticId { | |||
274 | } | 274 | } |
275 | 275 | ||
276 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | 276 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |
277 | pub struct TraitId(RawId); | 277 | pub(crate) struct TraitId(RawId); |
278 | impl_arena_id!(TraitId); | 278 | impl_arena_id!(TraitId); |
279 | impl AstItemDef<ast::TraitDef> for TraitId { | 279 | impl AstItemDef<ast::TraitDef> for TraitId { |
280 | fn interner(interner: &HirInterner) -> &LocationInterner<ItemLoc<ast::TraitDef>, Self> { | 280 | fn interner(interner: &HirInterner) -> &LocationInterner<ItemLoc<ast::TraitDef>, Self> { |
@@ -283,117 +283,10 @@ impl AstItemDef<ast::TraitDef> for TraitId { | |||
283 | } | 283 | } |
284 | 284 | ||
285 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | 285 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |
286 | pub struct TypeId(RawId); | 286 | pub(crate) struct TypeAliasId(RawId); |
287 | impl_arena_id!(TypeId); | 287 | impl_arena_id!(TypeAliasId); |
288 | impl AstItemDef<ast::TypeAliasDef> for TypeId { | 288 | impl AstItemDef<ast::TypeAliasDef> for TypeAliasId { |
289 | fn interner(interner: &HirInterner) -> &LocationInterner<ItemLoc<ast::TypeAliasDef>, Self> { | 289 | fn interner(interner: &HirInterner) -> &LocationInterner<ItemLoc<ast::TypeAliasDef>, Self> { |
290 | &interner.types | 290 | &interner.types |
291 | } | 291 | } |
292 | } | 292 | } |
293 | |||
294 | /// Identifier of item within a specific file. This is stable over reparses, so | ||
295 | /// it's OK to use it as a salsa key/value. | ||
296 | #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] | ||
297 | pub struct SourceFileItemId(RawId); | ||
298 | impl_arena_id!(SourceFileItemId); | ||
299 | |||
300 | impl SourceFileItemId { | ||
301 | pub(crate) fn with_file_id(self, file_id: HirFileId) -> SourceItemId { | ||
302 | SourceItemId { file_id, item_id: self } | ||
303 | } | ||
304 | } | ||
305 | |||
306 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | ||
307 | pub struct SourceItemId { | ||
308 | pub(crate) file_id: HirFileId, | ||
309 | pub(crate) item_id: SourceFileItemId, | ||
310 | } | ||
311 | |||
312 | /// Maps items' `SyntaxNode`s to `SourceFileItemId`s and back. | ||
313 | #[derive(Debug, PartialEq, Eq)] | ||
314 | pub struct SourceFileItems { | ||
315 | file_id: HirFileId, | ||
316 | arena: Arena<SourceFileItemId, SyntaxNodePtr>, | ||
317 | } | ||
318 | |||
319 | impl SourceFileItems { | ||
320 | pub(crate) fn file_items_query( | ||
321 | db: &impl DefDatabase, | ||
322 | file_id: HirFileId, | ||
323 | ) -> Arc<SourceFileItems> { | ||
324 | let source_file = db.hir_parse(file_id); | ||
325 | Arc::new(SourceFileItems::from_source_file(&source_file, file_id)) | ||
326 | } | ||
327 | |||
328 | pub(crate) fn file_item_query( | ||
329 | db: &impl DefDatabase, | ||
330 | source_item_id: SourceItemId, | ||
331 | ) -> TreeArc<SyntaxNode> { | ||
332 | let source_file = db.hir_parse(source_item_id.file_id); | ||
333 | db.file_items(source_item_id.file_id)[source_item_id.item_id] | ||
334 | .to_node(&source_file) | ||
335 | .to_owned() | ||
336 | } | ||
337 | |||
338 | pub(crate) fn from_source_file( | ||
339 | source_file: &SourceFile, | ||
340 | file_id: HirFileId, | ||
341 | ) -> SourceFileItems { | ||
342 | let mut res = SourceFileItems { file_id, arena: Arena::default() }; | ||
343 | // By walking the tree in bread-first order we make sure that parents | ||
344 | // get lower ids then children. That is, adding a new child does not | ||
345 | // change parent's id. This means that, say, adding a new function to a | ||
346 | // trait does not change ids of top-level items, which helps caching. | ||
347 | bfs(source_file.syntax(), |it| { | ||
348 | if let Some(module_item) = ast::ModuleItem::cast(it) { | ||
349 | res.alloc(module_item.syntax()); | ||
350 | } else if let Some(macro_call) = ast::MacroCall::cast(it) { | ||
351 | res.alloc(macro_call.syntax()); | ||
352 | } | ||
353 | }); | ||
354 | res | ||
355 | } | ||
356 | |||
357 | fn alloc(&mut self, item: &SyntaxNode) -> SourceFileItemId { | ||
358 | self.arena.alloc(SyntaxNodePtr::new(item)) | ||
359 | } | ||
360 | pub(crate) fn id_of(&self, file_id: HirFileId, item: &SyntaxNode) -> SourceFileItemId { | ||
361 | assert_eq!( | ||
362 | self.file_id, file_id, | ||
363 | "SourceFileItems: wrong file, expected {:?}, got {:?}", | ||
364 | self.file_id, file_id | ||
365 | ); | ||
366 | self.id_of_unchecked(item) | ||
367 | } | ||
368 | pub(crate) fn id_of_unchecked(&self, item: &SyntaxNode) -> SourceFileItemId { | ||
369 | let ptr = SyntaxNodePtr::new(item); | ||
370 | if let Some((id, _)) = self.arena.iter().find(|(_id, i)| **i == ptr) { | ||
371 | return id; | ||
372 | } | ||
373 | panic!( | ||
374 | "Can't find {:?} in SourceFileItems:\n{:?}", | ||
375 | item, | ||
376 | self.arena.iter().map(|(_id, i)| i).collect::<Vec<_>>(), | ||
377 | ); | ||
378 | } | ||
379 | } | ||
380 | |||
381 | impl std::ops::Index<SourceFileItemId> for SourceFileItems { | ||
382 | type Output = SyntaxNodePtr; | ||
383 | fn index(&self, idx: SourceFileItemId) -> &SyntaxNodePtr { | ||
384 | &self.arena[idx] | ||
385 | } | ||
386 | } | ||
387 | |||
388 | /// Walks the subtree in bfs order, calling `f` for each node. | ||
389 | fn bfs(node: &SyntaxNode, mut f: impl FnMut(&SyntaxNode)) { | ||
390 | let mut curr_layer = vec![node]; | ||
391 | let mut next_layer = vec![]; | ||
392 | while !curr_layer.is_empty() { | ||
393 | curr_layer.drain(..).for_each(|node| { | ||
394 | next_layer.extend(node.children()); | ||
395 | f(node); | ||
396 | }); | ||
397 | std::mem::swap(&mut curr_layer, &mut next_layer); | ||
398 | } | ||
399 | } | ||
diff --git a/crates/ra_hir/src/lib.rs b/crates/ra_hir/src/lib.rs index a89c916f8..7c603bbd3 100644 --- a/crates/ra_hir/src/lib.rs +++ b/crates/ra_hir/src/lib.rs | |||
@@ -23,10 +23,12 @@ pub mod mock; | |||
23 | mod path; | 23 | mod path; |
24 | pub mod source_binder; | 24 | pub mod source_binder; |
25 | 25 | ||
26 | mod source_id; | ||
26 | mod ids; | 27 | mod ids; |
27 | mod name; | 28 | mod name; |
28 | mod nameres; | 29 | mod nameres; |
29 | mod adt; | 30 | mod adt; |
31 | mod traits; | ||
30 | mod type_alias; | 32 | mod type_alias; |
31 | mod type_ref; | 33 | mod type_ref; |
32 | mod ty; | 34 | mod ty; |
@@ -35,6 +37,7 @@ mod expr; | |||
35 | mod generics; | 37 | mod generics; |
36 | mod docs; | 38 | mod docs; |
37 | mod resolve; | 39 | mod resolve; |
40 | pub mod diagnostics; | ||
38 | 41 | ||
39 | mod code_model_api; | 42 | mod code_model_api; |
40 | mod code_model_impl; | 43 | mod code_model_impl; |
@@ -45,13 +48,14 @@ mod marks; | |||
45 | use crate::{ | 48 | use crate::{ |
46 | db::{HirDatabase, DefDatabase}, | 49 | db::{HirDatabase, DefDatabase}, |
47 | name::{AsName, KnownName}, | 50 | name::{AsName, KnownName}, |
48 | ids::{SourceItemId, SourceFileItems}, | 51 | source_id::{FileAstId, AstId}, |
49 | }; | 52 | }; |
50 | 53 | ||
51 | pub use self::{ | 54 | pub use self::{ |
52 | path::{Path, PathKind}, | 55 | path::{Path, PathKind}, |
53 | name::Name, | 56 | name::Name, |
54 | ids::{HirFileId, MacroCallId, MacroCallLoc, HirInterner}, | 57 | source_id::{AstIdMap, ErasedFileAstId}, |
58 | ids::{HirFileId, MacroDefId, MacroCallId, MacroCallLoc, HirInterner}, | ||
55 | nameres::{PerNs, Namespace}, | 59 | nameres::{PerNs, Namespace}, |
56 | ty::{Ty, ApplicationTy, TypeCtor, Substs, display::HirDisplay}, | 60 | ty::{Ty, ApplicationTy, TypeCtor, Substs, display::HirDisplay}, |
57 | impl_block::{ImplBlock, ImplItem}, | 61 | impl_block::{ImplBlock, ImplItem}, |
@@ -63,7 +67,7 @@ pub use self::{ | |||
63 | 67 | ||
64 | pub use self::code_model_api::{ | 68 | pub use self::code_model_api::{ |
65 | Crate, CrateDependency, | 69 | Crate, CrateDependency, |
66 | Module, ModuleDef, ModuleSource, Problem, | 70 | Module, ModuleDef, ModuleSource, |
67 | Struct, Enum, EnumVariant, | 71 | Struct, Enum, EnumVariant, |
68 | Function, FnSignature, | 72 | Function, FnSignature, |
69 | StructField, FieldSource, | 73 | StructField, FieldSource, |
diff --git a/crates/ra_hir/src/mock.rs b/crates/ra_hir/src/mock.rs index 10d4c1b8c..aeab6b180 100644 --- a/crates/ra_hir/src/mock.rs +++ b/crates/ra_hir/src/mock.rs | |||
@@ -9,7 +9,7 @@ use relative_path::RelativePathBuf; | |||
9 | use test_utils::{parse_fixture, CURSOR_MARKER, extract_offset}; | 9 | use test_utils::{parse_fixture, CURSOR_MARKER, extract_offset}; |
10 | use rustc_hash::FxHashMap; | 10 | use rustc_hash::FxHashMap; |
11 | 11 | ||
12 | use crate::{db, HirInterner}; | 12 | use crate::{db, HirInterner, diagnostics::DiagnosticSink}; |
13 | 13 | ||
14 | pub const WORKSPACE: SourceRootId = SourceRootId(0); | 14 | pub const WORKSPACE: SourceRootId = SourceRootId(0); |
15 | 15 | ||
@@ -70,6 +70,22 @@ impl MockDatabase { | |||
70 | self.set_crate_graph(Arc::new(crate_graph)) | 70 | self.set_crate_graph(Arc::new(crate_graph)) |
71 | } | 71 | } |
72 | 72 | ||
73 | pub fn diagnostics(&self) -> String { | ||
74 | let mut buf = String::from("\n"); | ||
75 | let mut files: Vec<FileId> = self.files.values().map(|&it| it).collect(); | ||
76 | files.sort(); | ||
77 | for file in files { | ||
78 | let module = crate::source_binder::module_from_file_id(self, file).unwrap(); | ||
79 | module.diagnostics( | ||
80 | self, | ||
81 | &mut DiagnosticSink::new(|d| { | ||
82 | buf += &format!("{:?}: {}\n", d.syntax_node(self).text(), d.message()); | ||
83 | }), | ||
84 | ) | ||
85 | } | ||
86 | buf | ||
87 | } | ||
88 | |||
73 | fn from_fixture(fixture: &str) -> (MockDatabase, Option<FilePosition>) { | 89 | fn from_fixture(fixture: &str) -> (MockDatabase, Option<FilePosition>) { |
74 | let mut db = MockDatabase::default(); | 90 | let mut db = MockDatabase::default(); |
75 | 91 | ||
diff --git a/crates/ra_hir/src/nameres.rs b/crates/ra_hir/src/nameres.rs index 5ac878c79..8adc6d368 100644 --- a/crates/ra_hir/src/nameres.rs +++ b/crates/ra_hir/src/nameres.rs | |||
@@ -59,12 +59,16 @@ use rustc_hash::FxHashMap; | |||
59 | use ra_arena::{Arena, RawId, impl_arena_id}; | 59 | use ra_arena::{Arena, RawId, impl_arena_id}; |
60 | use ra_db::{FileId, Edition}; | 60 | use ra_db::{FileId, Edition}; |
61 | use test_utils::tested_by; | 61 | use test_utils::tested_by; |
62 | use ra_syntax::ast; | ||
62 | use ra_prof::profile; | 63 | use ra_prof::profile; |
63 | 64 | ||
64 | use crate::{ | 65 | use crate::{ |
65 | ModuleDef, Name, Crate, Module, Problem, | 66 | ModuleDef, Name, Crate, Module, |
66 | DefDatabase, Path, PathKind, HirFileId, | 67 | DefDatabase, Path, PathKind, HirFileId, Trait, |
67 | ids::{SourceItemId, SourceFileItemId, MacroCallId}, | 68 | ids::MacroDefId, |
69 | diagnostics::DiagnosticSink, | ||
70 | nameres::diagnostics::DefDiagnostic, | ||
71 | AstId, | ||
68 | }; | 72 | }; |
69 | 73 | ||
70 | pub(crate) use self::raw::{RawItems, ImportId, ImportSourceMap}; | 74 | pub(crate) use self::raw::{RawItems, ImportId, ImportSourceMap}; |
@@ -83,10 +87,8 @@ pub struct CrateDefMap { | |||
83 | extern_prelude: FxHashMap<Name, ModuleDef>, | 87 | extern_prelude: FxHashMap<Name, ModuleDef>, |
84 | root: CrateModuleId, | 88 | root: CrateModuleId, |
85 | modules: Arena<CrateModuleId, ModuleData>, | 89 | modules: Arena<CrateModuleId, ModuleData>, |
86 | macros: Arena<CrateMacroId, mbe::MacroRules>, | 90 | public_macros: FxHashMap<Name, MacroDefId>, |
87 | public_macros: FxHashMap<Name, CrateMacroId>, | 91 | diagnostics: Vec<DefDiagnostic>, |
88 | macro_resolutions: FxHashMap<MacroCallId, (Crate, CrateMacroId)>, | ||
89 | problems: CrateDefMapProblems, | ||
90 | } | 92 | } |
91 | 93 | ||
92 | impl std::ops::Index<CrateModuleId> for CrateDefMap { | 94 | impl std::ops::Index<CrateModuleId> for CrateDefMap { |
@@ -96,18 +98,6 @@ impl std::ops::Index<CrateModuleId> for CrateDefMap { | |||
96 | } | 98 | } |
97 | } | 99 | } |
98 | 100 | ||
99 | impl std::ops::Index<CrateMacroId> for CrateDefMap { | ||
100 | type Output = mbe::MacroRules; | ||
101 | fn index(&self, id: CrateMacroId) -> &mbe::MacroRules { | ||
102 | &self.macros[id] | ||
103 | } | ||
104 | } | ||
105 | |||
106 | /// An ID of a macro, **local** to a specific crate | ||
107 | #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] | ||
108 | pub(crate) struct CrateMacroId(RawId); | ||
109 | impl_arena_id!(CrateMacroId); | ||
110 | |||
111 | /// An ID of a module, **local** to a specific crate | 101 | /// An ID of a module, **local** to a specific crate |
112 | #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] | 102 | #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] |
113 | pub(crate) struct CrateModuleId(RawId); | 103 | pub(crate) struct CrateModuleId(RawId); |
@@ -119,28 +109,13 @@ pub(crate) struct ModuleData { | |||
119 | pub(crate) children: FxHashMap<Name, CrateModuleId>, | 109 | pub(crate) children: FxHashMap<Name, CrateModuleId>, |
120 | pub(crate) scope: ModuleScope, | 110 | pub(crate) scope: ModuleScope, |
121 | /// None for root | 111 | /// None for root |
122 | pub(crate) declaration: Option<SourceItemId>, | 112 | pub(crate) declaration: Option<AstId<ast::Module>>, |
123 | /// None for inline modules. | 113 | /// None for inline modules. |
124 | /// | 114 | /// |
125 | /// Note that non-inline modules, by definition, live inside non-macro file. | 115 | /// Note that non-inline modules, by definition, live inside non-macro file. |
126 | pub(crate) definition: Option<FileId>, | 116 | pub(crate) definition: Option<FileId>, |
127 | } | 117 | } |
128 | 118 | ||
129 | #[derive(Default, Debug, PartialEq, Eq)] | ||
130 | pub(crate) struct CrateDefMapProblems { | ||
131 | problems: Vec<(SourceItemId, Problem)>, | ||
132 | } | ||
133 | |||
134 | impl CrateDefMapProblems { | ||
135 | fn add(&mut self, source_item_id: SourceItemId, problem: Problem) { | ||
136 | self.problems.push((source_item_id, problem)) | ||
137 | } | ||
138 | |||
139 | pub(crate) fn iter<'a>(&'a self) -> impl Iterator<Item = (&'a SourceItemId, &'a Problem)> + 'a { | ||
140 | self.problems.iter().map(|(s, p)| (s, p)) | ||
141 | } | ||
142 | } | ||
143 | |||
144 | #[derive(Debug, Default, PartialEq, Eq, Clone)] | 119 | #[derive(Debug, Default, PartialEq, Eq, Clone)] |
145 | pub struct ModuleScope { | 120 | pub struct ModuleScope { |
146 | items: FxHashMap<Name, Resolution>, | 121 | items: FxHashMap<Name, Resolution>, |
@@ -153,6 +128,12 @@ impl ModuleScope { | |||
153 | pub fn get(&self, name: &Name) -> Option<&Resolution> { | 128 | pub fn get(&self, name: &Name) -> Option<&Resolution> { |
154 | self.items.get(name) | 129 | self.items.get(name) |
155 | } | 130 | } |
131 | pub fn traits<'a>(&'a self) -> impl Iterator<Item = Trait> + 'a { | ||
132 | self.items.values().filter_map(|r| match r.def.take_types() { | ||
133 | Some(ModuleDef::Trait(t)) => Some(t), | ||
134 | _ => None, | ||
135 | }) | ||
136 | } | ||
156 | } | 137 | } |
157 | 138 | ||
158 | #[derive(Debug, Clone, PartialEq, Eq, Default)] | 139 | #[derive(Debug, Clone, PartialEq, Eq, Default)] |
@@ -210,10 +191,8 @@ impl CrateDefMap { | |||
210 | prelude: None, | 191 | prelude: None, |
211 | root, | 192 | root, |
212 | modules, | 193 | modules, |
213 | macros: Arena::default(), | ||
214 | public_macros: FxHashMap::default(), | 194 | public_macros: FxHashMap::default(), |
215 | macro_resolutions: FxHashMap::default(), | 195 | diagnostics: Vec::new(), |
216 | problems: CrateDefMapProblems::default(), | ||
217 | } | 196 | } |
218 | }; | 197 | }; |
219 | let def_map = collector::collect_defs(db, def_map); | 198 | let def_map = collector::collect_defs(db, def_map); |
@@ -224,10 +203,6 @@ impl CrateDefMap { | |||
224 | self.root | 203 | self.root |
225 | } | 204 | } |
226 | 205 | ||
227 | pub(crate) fn problems(&self) -> &CrateDefMapProblems { | ||
228 | &self.problems | ||
229 | } | ||
230 | |||
231 | pub(crate) fn mk_module(&self, module_id: CrateModuleId) -> Module { | 206 | pub(crate) fn mk_module(&self, module_id: CrateModuleId) -> Module { |
232 | Module { krate: self.krate, module_id } | 207 | Module { krate: self.krate, module_id } |
233 | } | 208 | } |
@@ -240,19 +215,20 @@ impl CrateDefMap { | |||
240 | &self.extern_prelude | 215 | &self.extern_prelude |
241 | } | 216 | } |
242 | 217 | ||
243 | pub(crate) fn resolve_macro( | 218 | pub(crate) fn add_diagnostics( |
244 | &self, | 219 | &self, |
245 | macro_call_id: MacroCallId, | 220 | db: &impl DefDatabase, |
246 | ) -> Option<(Crate, CrateMacroId)> { | 221 | module: CrateModuleId, |
247 | self.macro_resolutions.get(¯o_call_id).map(|&it| it) | 222 | sink: &mut DiagnosticSink, |
223 | ) { | ||
224 | self.diagnostics.iter().for_each(|it| it.add_to(db, module, sink)) | ||
248 | } | 225 | } |
249 | 226 | ||
250 | pub(crate) fn find_module_by_source( | 227 | pub(crate) fn find_module_by_source( |
251 | &self, | 228 | &self, |
252 | file_id: HirFileId, | 229 | file_id: HirFileId, |
253 | decl_id: Option<SourceFileItemId>, | 230 | decl_id: Option<AstId<ast::Module>>, |
254 | ) -> Option<CrateModuleId> { | 231 | ) -> Option<CrateModuleId> { |
255 | let decl_id = decl_id.map(|it| it.with_file_id(file_id)); | ||
256 | let (module_id, _module_data) = self.modules.iter().find(|(_module_id, module_data)| { | 232 | let (module_id, _module_data) = self.modules.iter().find(|(_module_id, module_data)| { |
257 | if decl_id.is_some() { | 233 | if decl_id.is_some() { |
258 | module_data.declaration == decl_id | 234 | module_data.declaration == decl_id |
@@ -452,3 +428,46 @@ impl CrateDefMap { | |||
452 | } | 428 | } |
453 | } | 429 | } |
454 | } | 430 | } |
431 | |||
432 | mod diagnostics { | ||
433 | use relative_path::RelativePathBuf; | ||
434 | use ra_syntax::{AstPtr, ast}; | ||
435 | |||
436 | use crate::{ | ||
437 | AstId, DefDatabase, | ||
438 | nameres::CrateModuleId, | ||
439 | diagnostics::{DiagnosticSink, UnresolvedModule}, | ||
440 | }; | ||
441 | |||
442 | #[derive(Debug, PartialEq, Eq)] | ||
443 | pub(super) enum DefDiagnostic { | ||
444 | UnresolvedModule { | ||
445 | module: CrateModuleId, | ||
446 | declaration: AstId<ast::Module>, | ||
447 | candidate: RelativePathBuf, | ||
448 | }, | ||
449 | } | ||
450 | |||
451 | impl DefDiagnostic { | ||
452 | pub(super) fn add_to( | ||
453 | &self, | ||
454 | db: &impl DefDatabase, | ||
455 | target_module: CrateModuleId, | ||
456 | sink: &mut DiagnosticSink, | ||
457 | ) { | ||
458 | match self { | ||
459 | DefDiagnostic::UnresolvedModule { module, declaration, candidate } => { | ||
460 | if *module != target_module { | ||
461 | return; | ||
462 | } | ||
463 | let decl = declaration.to_node(db); | ||
464 | sink.push(UnresolvedModule { | ||
465 | file: declaration.file_id(), | ||
466 | decl: AstPtr::new(&decl), | ||
467 | candidate: candidate.clone(), | ||
468 | }) | ||
469 | } | ||
470 | } | ||
471 | } | ||
472 | } | ||
473 | } | ||
diff --git a/crates/ra_hir/src/nameres/collector.rs b/crates/ra_hir/src/nameres/collector.rs index c5b73cfbe..39cadc94a 100644 --- a/crates/ra_hir/src/nameres/collector.rs +++ b/crates/ra_hir/src/nameres/collector.rs | |||
@@ -3,17 +3,22 @@ use rustc_hash::FxHashMap; | |||
3 | use relative_path::RelativePathBuf; | 3 | use relative_path::RelativePathBuf; |
4 | use test_utils::tested_by; | 4 | use test_utils::tested_by; |
5 | use ra_db::FileId; | 5 | use ra_db::FileId; |
6 | use ra_syntax::ast; | ||
6 | 7 | ||
7 | use crate::{ | 8 | use crate::{ |
8 | Function, Module, Struct, Enum, Const, Static, Trait, TypeAlias, | 9 | Function, Module, Struct, Enum, Const, Static, Trait, TypeAlias, |
9 | DefDatabase, HirFileId, Name, Path, Problem, Crate, | 10 | DefDatabase, HirFileId, Name, Path, |
10 | KnownName, | 11 | KnownName, |
11 | nameres::{Resolution, PerNs, ModuleDef, ReachedFixedPoint, ResolveMode, raw}, | 12 | nameres::{ |
12 | ids::{AstItemDef, LocationCtx, MacroCallLoc, SourceItemId, MacroCallId}, | 13 | Resolution, PerNs, ModuleDef, ReachedFixedPoint, ResolveMode, |
14 | CrateDefMap, CrateModuleId, ModuleData, | ||
15 | diagnostics::DefDiagnostic, | ||
16 | raw, | ||
17 | }, | ||
18 | ids::{AstItemDef, LocationCtx, MacroCallLoc, MacroCallId, MacroDefId}, | ||
19 | AstId, | ||
13 | }; | 20 | }; |
14 | 21 | ||
15 | use super::{CrateDefMap, CrateModuleId, ModuleData, CrateMacroId}; | ||
16 | |||
17 | pub(super) fn collect_defs(db: &impl DefDatabase, mut def_map: CrateDefMap) -> CrateDefMap { | 22 | pub(super) fn collect_defs(db: &impl DefDatabase, mut def_map: CrateDefMap) -> CrateDefMap { |
18 | // populate external prelude | 23 | // populate external prelude |
19 | for dep in def_map.krate.dependencies(db) { | 24 | for dep in def_map.krate.dependencies(db) { |
@@ -48,8 +53,8 @@ struct DefCollector<DB> { | |||
48 | def_map: CrateDefMap, | 53 | def_map: CrateDefMap, |
49 | glob_imports: FxHashMap<CrateModuleId, Vec<(CrateModuleId, raw::ImportId)>>, | 54 | glob_imports: FxHashMap<CrateModuleId, Vec<(CrateModuleId, raw::ImportId)>>, |
50 | unresolved_imports: Vec<(CrateModuleId, raw::ImportId, raw::ImportData)>, | 55 | unresolved_imports: Vec<(CrateModuleId, raw::ImportId, raw::ImportData)>, |
51 | unexpanded_macros: Vec<(CrateModuleId, MacroCallId, Path, tt::Subtree)>, | 56 | unexpanded_macros: Vec<(CrateModuleId, AstId<ast::MacroCall>, Path)>, |
52 | global_macro_scope: FxHashMap<Name, CrateMacroId>, | 57 | global_macro_scope: FxHashMap<Name, MacroDefId>, |
53 | } | 58 | } |
54 | 59 | ||
55 | impl<'a, DB> DefCollector<&'a DB> | 60 | impl<'a, DB> DefCollector<&'a DB> |
@@ -59,7 +64,7 @@ where | |||
59 | fn collect(&mut self) { | 64 | fn collect(&mut self) { |
60 | let crate_graph = self.db.crate_graph(); | 65 | let crate_graph = self.db.crate_graph(); |
61 | let file_id = crate_graph.crate_root(self.def_map.krate.crate_id()); | 66 | let file_id = crate_graph.crate_root(self.def_map.krate.crate_id()); |
62 | let raw_items = self.db.raw_items(file_id); | 67 | let raw_items = self.db.raw_items(file_id.into()); |
63 | let module_id = self.def_map.root; | 68 | let module_id = self.def_map.root; |
64 | self.def_map.modules[module_id].definition = Some(file_id); | 69 | self.def_map.modules[module_id].definition = Some(file_id); |
65 | ModCollector { | 70 | ModCollector { |
@@ -90,14 +95,11 @@ where | |||
90 | } | 95 | } |
91 | } | 96 | } |
92 | 97 | ||
93 | fn define_macro(&mut self, name: Name, tt: &tt::Subtree, export: bool) { | 98 | fn define_macro(&mut self, name: Name, macro_id: MacroDefId, export: bool) { |
94 | if let Ok(rules) = mbe::MacroRules::parse(tt) { | 99 | if export { |
95 | let macro_id = self.def_map.macros.alloc(rules); | 100 | self.def_map.public_macros.insert(name.clone(), macro_id); |
96 | if export { | ||
97 | self.def_map.public_macros.insert(name.clone(), macro_id); | ||
98 | } | ||
99 | self.global_macro_scope.insert(name, macro_id); | ||
100 | } | 101 | } |
102 | self.global_macro_scope.insert(name, macro_id); | ||
101 | } | 103 | } |
102 | 104 | ||
103 | fn resolve_imports(&mut self) -> ReachedFixedPoint { | 105 | fn resolve_imports(&mut self) -> ReachedFixedPoint { |
@@ -293,7 +295,7 @@ where | |||
293 | let mut macros = std::mem::replace(&mut self.unexpanded_macros, Vec::new()); | 295 | let mut macros = std::mem::replace(&mut self.unexpanded_macros, Vec::new()); |
294 | let mut resolved = Vec::new(); | 296 | let mut resolved = Vec::new(); |
295 | let mut res = ReachedFixedPoint::Yes; | 297 | let mut res = ReachedFixedPoint::Yes; |
296 | macros.retain(|(module_id, call_id, path, tt)| { | 298 | macros.retain(|(module_id, ast_id, path)| { |
297 | if path.segments.len() != 2 { | 299 | if path.segments.len() != 2 { |
298 | return true; | 300 | return true; |
299 | } | 301 | } |
@@ -309,47 +311,23 @@ where | |||
309 | res = ReachedFixedPoint::No; | 311 | res = ReachedFixedPoint::No; |
310 | let def_map = self.db.crate_def_map(krate); | 312 | let def_map = self.db.crate_def_map(krate); |
311 | if let Some(macro_id) = def_map.public_macros.get(&path.segments[1].name).cloned() { | 313 | if let Some(macro_id) = def_map.public_macros.get(&path.segments[1].name).cloned() { |
312 | resolved.push((*module_id, *call_id, (krate, macro_id), tt.clone())); | 314 | let call_id = MacroCallLoc { def: macro_id, ast_id: *ast_id }.id(self.db); |
315 | resolved.push((*module_id, call_id)); | ||
313 | } | 316 | } |
314 | false | 317 | false |
315 | }); | 318 | }); |
316 | 319 | ||
317 | for (module_id, macro_call_id, macro_def_id, arg) in resolved { | 320 | for (module_id, macro_call_id) in resolved { |
318 | self.collect_macro_expansion(module_id, macro_call_id, macro_def_id, arg); | 321 | self.collect_macro_expansion(module_id, macro_call_id); |
319 | } | 322 | } |
320 | res | 323 | res |
321 | } | 324 | } |
322 | 325 | ||
323 | fn collect_macro_expansion( | 326 | fn collect_macro_expansion(&mut self, module_id: CrateModuleId, macro_call_id: MacroCallId) { |
324 | &mut self, | 327 | let file_id: HirFileId = macro_call_id.into(); |
325 | module_id: CrateModuleId, | 328 | let raw_items = self.db.raw_items(file_id); |
326 | macro_call_id: MacroCallId, | 329 | ModCollector { def_collector: &mut *self, file_id, module_id, raw_items: &raw_items } |
327 | macro_def_id: (Crate, CrateMacroId), | 330 | .collect(raw_items.items()) |
328 | macro_arg: tt::Subtree, | ||
329 | ) { | ||
330 | let (macro_krate, macro_id) = macro_def_id; | ||
331 | let dm; | ||
332 | let rules = if macro_krate == self.def_map.krate { | ||
333 | &self.def_map[macro_id] | ||
334 | } else { | ||
335 | dm = self.db.crate_def_map(macro_krate); | ||
336 | &dm[macro_id] | ||
337 | }; | ||
338 | if let Ok(expansion) = rules.expand(¯o_arg) { | ||
339 | self.def_map.macro_resolutions.insert(macro_call_id, macro_def_id); | ||
340 | // XXX: this **does not** go through a database, because we can't | ||
341 | // identify macro_call without adding the whole state of name resolution | ||
342 | // as a parameter to the query. | ||
343 | // | ||
344 | // So, we run the queries "manually" and we must ensure that | ||
345 | // `db.hir_parse(macro_call_id)` returns the same source_file. | ||
346 | let file_id: HirFileId = macro_call_id.into(); | ||
347 | let source_file = mbe::token_tree_to_ast_item_list(&expansion); | ||
348 | |||
349 | let raw_items = raw::RawItems::from_source_file(&source_file, file_id); | ||
350 | ModCollector { def_collector: &mut *self, file_id, module_id, raw_items: &raw_items } | ||
351 | .collect(raw_items.items()) | ||
352 | } | ||
353 | } | 331 | } |
354 | 332 | ||
355 | fn finish(self) -> CrateDefMap { | 333 | fn finish(self) -> CrateDefMap { |
@@ -387,12 +365,9 @@ where | |||
387 | fn collect_module(&mut self, module: &raw::ModuleData) { | 365 | fn collect_module(&mut self, module: &raw::ModuleData) { |
388 | match module { | 366 | match module { |
389 | // inline module, just recurse | 367 | // inline module, just recurse |
390 | raw::ModuleData::Definition { name, items, source_item_id } => { | 368 | raw::ModuleData::Definition { name, items, ast_id } => { |
391 | let module_id = self.push_child_module( | 369 | let module_id = |
392 | name.clone(), | 370 | self.push_child_module(name.clone(), ast_id.with_file_id(self.file_id), None); |
393 | source_item_id.with_file_id(self.file_id), | ||
394 | None, | ||
395 | ); | ||
396 | ModCollector { | 371 | ModCollector { |
397 | def_collector: &mut *self.def_collector, | 372 | def_collector: &mut *self.def_collector, |
398 | module_id, | 373 | module_id, |
@@ -402,28 +377,29 @@ where | |||
402 | .collect(&*items); | 377 | .collect(&*items); |
403 | } | 378 | } |
404 | // out of line module, resovle, parse and recurse | 379 | // out of line module, resovle, parse and recurse |
405 | raw::ModuleData::Declaration { name, source_item_id } => { | 380 | raw::ModuleData::Declaration { name, ast_id } => { |
406 | let source_item_id = source_item_id.with_file_id(self.file_id); | 381 | let ast_id = ast_id.with_file_id(self.file_id); |
407 | let is_root = self.def_collector.def_map.modules[self.module_id].parent.is_none(); | 382 | let is_root = self.def_collector.def_map.modules[self.module_id].parent.is_none(); |
408 | let (file_ids, problem) = | 383 | match resolve_submodule(self.def_collector.db, self.file_id, name, is_root) { |
409 | resolve_submodule(self.def_collector.db, self.file_id, name, is_root); | 384 | Ok(file_id) => { |
410 | 385 | let module_id = self.push_child_module(name.clone(), ast_id, Some(file_id)); | |
411 | if let Some(problem) = problem { | 386 | let raw_items = self.def_collector.db.raw_items(file_id.into()); |
412 | self.def_collector.def_map.problems.add(source_item_id, problem) | 387 | ModCollector { |
413 | } | 388 | def_collector: &mut *self.def_collector, |
414 | 389 | module_id, | |
415 | if let Some(&file_id) = file_ids.first() { | 390 | file_id: file_id.into(), |
416 | let module_id = | 391 | raw_items: &raw_items, |
417 | self.push_child_module(name.clone(), source_item_id, Some(file_id)); | 392 | } |
418 | let raw_items = self.def_collector.db.raw_items(file_id); | 393 | .collect(raw_items.items()) |
419 | ModCollector { | ||
420 | def_collector: &mut *self.def_collector, | ||
421 | module_id, | ||
422 | file_id: file_id.into(), | ||
423 | raw_items: &raw_items, | ||
424 | } | 394 | } |
425 | .collect(raw_items.items()) | 395 | Err(candidate) => self.def_collector.def_map.diagnostics.push( |
426 | } | 396 | DefDiagnostic::UnresolvedModule { |
397 | module: self.module_id, | ||
398 | declaration: ast_id, | ||
399 | candidate, | ||
400 | }, | ||
401 | ), | ||
402 | }; | ||
427 | } | 403 | } |
428 | } | 404 | } |
429 | } | 405 | } |
@@ -431,7 +407,7 @@ where | |||
431 | fn push_child_module( | 407 | fn push_child_module( |
432 | &mut self, | 408 | &mut self, |
433 | name: Name, | 409 | name: Name, |
434 | declaration: SourceItemId, | 410 | declaration: AstId<ast::Module>, |
435 | definition: Option<FileId>, | 411 | definition: Option<FileId>, |
436 | ) -> CrateModuleId { | 412 | ) -> CrateModuleId { |
437 | let modules = &mut self.def_collector.def_map.modules; | 413 | let modules = &mut self.def_collector.def_map.modules; |
@@ -453,23 +429,24 @@ where | |||
453 | fn define_def(&mut self, def: &raw::DefData) { | 429 | fn define_def(&mut self, def: &raw::DefData) { |
454 | let module = Module { krate: self.def_collector.def_map.krate, module_id: self.module_id }; | 430 | let module = Module { krate: self.def_collector.def_map.krate, module_id: self.module_id }; |
455 | let ctx = LocationCtx::new(self.def_collector.db, module, self.file_id.into()); | 431 | let ctx = LocationCtx::new(self.def_collector.db, module, self.file_id.into()); |
456 | macro_rules! id { | 432 | |
457 | () => { | 433 | macro_rules! def { |
458 | AstItemDef::from_source_item_id_unchecked(ctx, def.source_item_id) | 434 | ($kind:ident, $ast_id:ident) => { |
435 | $kind { id: AstItemDef::from_ast_id(ctx, $ast_id) }.into() | ||
459 | }; | 436 | }; |
460 | } | 437 | } |
461 | let name = def.name.clone(); | 438 | let name = def.name.clone(); |
462 | let def: PerNs<ModuleDef> = match def.kind { | 439 | let def: PerNs<ModuleDef> = match def.kind { |
463 | raw::DefKind::Function => PerNs::values(Function { id: id!() }.into()), | 440 | raw::DefKind::Function(ast_id) => PerNs::values(def!(Function, ast_id)), |
464 | raw::DefKind::Struct => { | 441 | raw::DefKind::Struct(ast_id) => { |
465 | let s = Struct { id: id!() }.into(); | 442 | let s = def!(Struct, ast_id); |
466 | PerNs::both(s, s) | 443 | PerNs::both(s, s) |
467 | } | 444 | } |
468 | raw::DefKind::Enum => PerNs::types(Enum { id: id!() }.into()), | 445 | raw::DefKind::Enum(ast_id) => PerNs::types(def!(Enum, ast_id)), |
469 | raw::DefKind::Const => PerNs::values(Const { id: id!() }.into()), | 446 | raw::DefKind::Const(ast_id) => PerNs::values(def!(Const, ast_id)), |
470 | raw::DefKind::Static => PerNs::values(Static { id: id!() }.into()), | 447 | raw::DefKind::Static(ast_id) => PerNs::values(def!(Static, ast_id)), |
471 | raw::DefKind::Trait => PerNs::types(Trait { id: id!() }.into()), | 448 | raw::DefKind::Trait(ast_id) => PerNs::types(def!(Trait, ast_id)), |
472 | raw::DefKind::TypeAlias => PerNs::types(TypeAlias { id: id!() }.into()), | 449 | raw::DefKind::TypeAlias(ast_id) => PerNs::types(def!(TypeAlias, ast_id)), |
473 | }; | 450 | }; |
474 | let resolution = Resolution { def, import: None }; | 451 | let resolution = Resolution { def, import: None }; |
475 | self.def_collector.update(self.module_id, None, &[(name, resolution)]) | 452 | self.def_collector.update(self.module_id, None, &[(name, resolution)]) |
@@ -479,39 +456,27 @@ where | |||
479 | // Case 1: macro rules, define a macro in crate-global mutable scope | 456 | // Case 1: macro rules, define a macro in crate-global mutable scope |
480 | if is_macro_rules(&mac.path) { | 457 | if is_macro_rules(&mac.path) { |
481 | if let Some(name) = &mac.name { | 458 | if let Some(name) = &mac.name { |
482 | self.def_collector.define_macro(name.clone(), &mac.arg, mac.export) | 459 | let macro_id = MacroDefId(mac.ast_id.with_file_id(self.file_id)); |
460 | self.def_collector.define_macro(name.clone(), macro_id, mac.export) | ||
483 | } | 461 | } |
484 | return; | 462 | return; |
485 | } | 463 | } |
486 | 464 | ||
487 | let source_item_id = SourceItemId { file_id: self.file_id, item_id: mac.source_item_id }; | 465 | let ast_id = mac.ast_id.with_file_id(self.file_id); |
488 | let macro_call_id = MacroCallLoc { | ||
489 | module: Module { krate: self.def_collector.def_map.krate, module_id: self.module_id }, | ||
490 | source_item_id, | ||
491 | } | ||
492 | .id(self.def_collector.db); | ||
493 | 466 | ||
494 | // Case 2: try to expand macro_rules from this crate, triggering | 467 | // Case 2: try to expand macro_rules from this crate, triggering |
495 | // recursive item collection. | 468 | // recursive item collection. |
496 | if let Some(¯o_id) = | 469 | if let Some(¯o_id) = |
497 | mac.path.as_ident().and_then(|name| self.def_collector.global_macro_scope.get(name)) | 470 | mac.path.as_ident().and_then(|name| self.def_collector.global_macro_scope.get(name)) |
498 | { | 471 | { |
499 | self.def_collector.collect_macro_expansion( | 472 | let macro_call_id = MacroCallLoc { def: macro_id, ast_id }.id(self.def_collector.db); |
500 | self.module_id, | 473 | |
501 | macro_call_id, | 474 | self.def_collector.collect_macro_expansion(self.module_id, macro_call_id); |
502 | (self.def_collector.def_map.krate, macro_id), | ||
503 | mac.arg.clone(), | ||
504 | ); | ||
505 | return; | 475 | return; |
506 | } | 476 | } |
507 | 477 | ||
508 | // Case 3: path to a macro from another crate, expand during name resolution | 478 | // Case 3: path to a macro from another crate, expand during name resolution |
509 | self.def_collector.unexpanded_macros.push(( | 479 | self.def_collector.unexpanded_macros.push((self.module_id, ast_id, mac.path.clone())) |
510 | self.module_id, | ||
511 | macro_call_id, | ||
512 | mac.path.clone(), | ||
513 | mac.arg.clone(), | ||
514 | )) | ||
515 | } | 480 | } |
516 | } | 481 | } |
517 | 482 | ||
@@ -524,7 +489,7 @@ fn resolve_submodule( | |||
524 | file_id: HirFileId, | 489 | file_id: HirFileId, |
525 | name: &Name, | 490 | name: &Name, |
526 | is_root: bool, | 491 | is_root: bool, |
527 | ) -> (Vec<FileId>, Option<Problem>) { | 492 | ) -> Result<FileId, RelativePathBuf> { |
528 | // FIXME: handle submodules of inline modules properly | 493 | // FIXME: handle submodules of inline modules properly |
529 | let file_id = file_id.original_file(db); | 494 | let file_id = file_id.original_file(db); |
530 | let source_root_id = db.file_source_root(file_id); | 495 | let source_root_id = db.file_source_root(file_id); |
@@ -545,17 +510,10 @@ fn resolve_submodule( | |||
545 | candidates.push(file_dir_mod.clone()); | 510 | candidates.push(file_dir_mod.clone()); |
546 | }; | 511 | }; |
547 | let sr = db.source_root(source_root_id); | 512 | let sr = db.source_root(source_root_id); |
548 | let points_to = candidates | 513 | let mut points_to = candidates.into_iter().filter_map(|path| sr.files.get(&path)).map(|&it| it); |
549 | .into_iter() | 514 | // FIXME: handle ambiguity |
550 | .filter_map(|path| sr.files.get(&path)) | 515 | match points_to.next() { |
551 | .map(|&it| it) | 516 | Some(file_id) => Ok(file_id), |
552 | .collect::<Vec<_>>(); | 517 | None => Err(if is_dir_owner { file_mod } else { file_dir_mod }), |
553 | let problem = if points_to.is_empty() { | 518 | } |
554 | Some(Problem::UnresolvedModule { | ||
555 | candidate: if is_dir_owner { file_mod } else { file_dir_mod }, | ||
556 | }) | ||
557 | } else { | ||
558 | None | ||
559 | }; | ||
560 | (points_to, problem) | ||
561 | } | 519 | } |
diff --git a/crates/ra_hir/src/nameres/raw.rs b/crates/ra_hir/src/nameres/raw.rs index f8ba398ec..0936229ac 100644 --- a/crates/ra_hir/src/nameres/raw.rs +++ b/crates/ra_hir/src/nameres/raw.rs | |||
@@ -4,7 +4,6 @@ use std::{ | |||
4 | }; | 4 | }; |
5 | 5 | ||
6 | use test_utils::tested_by; | 6 | use test_utils::tested_by; |
7 | use ra_db::FileId; | ||
8 | use ra_arena::{Arena, impl_arena_id, RawId, map::ArenaMap}; | 7 | use ra_arena::{Arena, impl_arena_id, RawId, map::ArenaMap}; |
9 | use ra_syntax::{ | 8 | use ra_syntax::{ |
10 | AstNode, SourceFile, AstPtr, TreeArc, | 9 | AstNode, SourceFile, AstPtr, TreeArc, |
@@ -13,9 +12,13 @@ use ra_syntax::{ | |||
13 | 12 | ||
14 | use crate::{ | 13 | use crate::{ |
15 | DefDatabase, Name, AsName, Path, HirFileId, ModuleSource, | 14 | DefDatabase, Name, AsName, Path, HirFileId, ModuleSource, |
16 | ids::{SourceFileItemId, SourceFileItems}, | 15 | AstIdMap, FileAstId, |
17 | }; | 16 | }; |
18 | 17 | ||
18 | /// `RawItems` is a set of top-level items in a file (except for impls). | ||
19 | /// | ||
20 | /// It is the input to name resolution algorithm. `RawItems` are not invalidated | ||
21 | /// on most edits. | ||
19 | #[derive(Debug, Default, PartialEq, Eq)] | 22 | #[derive(Debug, Default, PartialEq, Eq)] |
20 | pub struct RawItems { | 23 | pub struct RawItems { |
21 | modules: Arena<Module, ModuleData>, | 24 | modules: Arena<Module, ModuleData>, |
@@ -32,11 +35,11 @@ pub struct ImportSourceMap { | |||
32 | } | 35 | } |
33 | 36 | ||
34 | impl ImportSourceMap { | 37 | impl ImportSourceMap { |
35 | pub(crate) fn insert(&mut self, import: ImportId, segment: &ast::PathSegment) { | 38 | fn insert(&mut self, import: ImportId, segment: &ast::PathSegment) { |
36 | self.map.insert(import, AstPtr::new(segment)) | 39 | self.map.insert(import, AstPtr::new(segment)) |
37 | } | 40 | } |
38 | 41 | ||
39 | pub fn get(&self, source: &ModuleSource, import: ImportId) -> TreeArc<ast::PathSegment> { | 42 | pub(crate) fn get(&self, source: &ModuleSource, import: ImportId) -> TreeArc<ast::PathSegment> { |
40 | let file = match source { | 43 | let file = match source { |
41 | ModuleSource::SourceFile(file) => &*file, | 44 | ModuleSource::SourceFile(file) => &*file, |
42 | ModuleSource::Module(m) => m.syntax().ancestors().find_map(SourceFile::cast).unwrap(), | 45 | ModuleSource::Module(m) => m.syntax().ancestors().find_map(SourceFile::cast).unwrap(), |
@@ -47,40 +50,27 @@ impl ImportSourceMap { | |||
47 | } | 50 | } |
48 | 51 | ||
49 | impl RawItems { | 52 | impl RawItems { |
50 | pub(crate) fn raw_items_query(db: &impl DefDatabase, file_id: FileId) -> Arc<RawItems> { | 53 | pub(crate) fn raw_items_query(db: &impl DefDatabase, file_id: HirFileId) -> Arc<RawItems> { |
51 | db.raw_items_with_source_map(file_id).0 | 54 | db.raw_items_with_source_map(file_id).0 |
52 | } | 55 | } |
53 | 56 | ||
54 | pub(crate) fn raw_items_with_source_map_query( | 57 | pub(crate) fn raw_items_with_source_map_query( |
55 | db: &impl DefDatabase, | 58 | db: &impl DefDatabase, |
56 | file_id: FileId, | 59 | file_id: HirFileId, |
57 | ) -> (Arc<RawItems>, Arc<ImportSourceMap>) { | 60 | ) -> (Arc<RawItems>, Arc<ImportSourceMap>) { |
58 | let mut collector = RawItemsCollector { | 61 | let mut collector = RawItemsCollector { |
59 | raw_items: RawItems::default(), | 62 | raw_items: RawItems::default(), |
60 | source_file_items: db.file_items(file_id.into()), | 63 | source_ast_id_map: db.ast_id_map(file_id.into()), |
61 | source_map: ImportSourceMap::default(), | 64 | source_map: ImportSourceMap::default(), |
62 | }; | 65 | }; |
63 | let source_file = db.parse(file_id); | 66 | let source_file = db.hir_parse(file_id); |
64 | collector.process_module(None, &*source_file); | 67 | collector.process_module(None, &*source_file); |
65 | (Arc::new(collector.raw_items), Arc::new(collector.source_map)) | 68 | (Arc::new(collector.raw_items), Arc::new(collector.source_map)) |
66 | } | 69 | } |
67 | 70 | ||
68 | pub(crate) fn items(&self) -> &[RawItem] { | 71 | pub(super) fn items(&self) -> &[RawItem] { |
69 | &self.items | 72 | &self.items |
70 | } | 73 | } |
71 | |||
72 | // We can't use queries during name resolution for fear of cycles, so this | ||
73 | // is a query-less variant of the above function. | ||
74 | pub(crate) fn from_source_file(source_file: &SourceFile, file_id: HirFileId) -> RawItems { | ||
75 | let source_file_items = SourceFileItems::from_source_file(source_file, file_id); | ||
76 | let mut collector = RawItemsCollector { | ||
77 | raw_items: RawItems::default(), | ||
78 | source_file_items: Arc::new(source_file_items), | ||
79 | source_map: ImportSourceMap::default(), | ||
80 | }; | ||
81 | collector.process_module(None, &*source_file); | ||
82 | collector.raw_items | ||
83 | } | ||
84 | } | 74 | } |
85 | 75 | ||
86 | impl Index<Module> for RawItems { | 76 | impl Index<Module> for RawItems { |
@@ -112,7 +102,7 @@ impl Index<Macro> for RawItems { | |||
112 | } | 102 | } |
113 | 103 | ||
114 | #[derive(Debug, PartialEq, Eq, Clone, Copy)] | 104 | #[derive(Debug, PartialEq, Eq, Clone, Copy)] |
115 | pub(crate) enum RawItem { | 105 | pub(super) enum RawItem { |
116 | Module(Module), | 106 | Module(Module), |
117 | Import(ImportId), | 107 | Import(ImportId), |
118 | Def(Def), | 108 | Def(Def), |
@@ -120,13 +110,13 @@ pub(crate) enum RawItem { | |||
120 | } | 110 | } |
121 | 111 | ||
122 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | 112 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |
123 | pub(crate) struct Module(RawId); | 113 | pub(super) struct Module(RawId); |
124 | impl_arena_id!(Module); | 114 | impl_arena_id!(Module); |
125 | 115 | ||
126 | #[derive(Debug, PartialEq, Eq)] | 116 | #[derive(Debug, PartialEq, Eq)] |
127 | pub(crate) enum ModuleData { | 117 | pub(super) enum ModuleData { |
128 | Declaration { name: Name, source_item_id: SourceFileItemId }, | 118 | Declaration { name: Name, ast_id: FileAstId<ast::Module> }, |
129 | Definition { name: Name, source_item_id: SourceFileItemId, items: Vec<RawItem> }, | 119 | Definition { name: Name, ast_id: FileAstId<ast::Module>, items: Vec<RawItem> }, |
130 | } | 120 | } |
131 | 121 | ||
132 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | 122 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |
@@ -135,51 +125,49 @@ impl_arena_id!(ImportId); | |||
135 | 125 | ||
136 | #[derive(Debug, Clone, PartialEq, Eq)] | 126 | #[derive(Debug, Clone, PartialEq, Eq)] |
137 | pub struct ImportData { | 127 | pub struct ImportData { |
138 | pub(crate) path: Path, | 128 | pub(super) path: Path, |
139 | pub(crate) alias: Option<Name>, | 129 | pub(super) alias: Option<Name>, |
140 | pub(crate) is_glob: bool, | 130 | pub(super) is_glob: bool, |
141 | pub(crate) is_prelude: bool, | 131 | pub(super) is_prelude: bool, |
142 | pub(crate) is_extern_crate: bool, | 132 | pub(super) is_extern_crate: bool, |
143 | } | 133 | } |
144 | 134 | ||
145 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | 135 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |
146 | pub(crate) struct Def(RawId); | 136 | pub(super) struct Def(RawId); |
147 | impl_arena_id!(Def); | 137 | impl_arena_id!(Def); |
148 | 138 | ||
149 | #[derive(Debug, PartialEq, Eq)] | 139 | #[derive(Debug, PartialEq, Eq)] |
150 | pub(crate) struct DefData { | 140 | pub(super) struct DefData { |
151 | pub(crate) source_item_id: SourceFileItemId, | 141 | pub(super) name: Name, |
152 | pub(crate) name: Name, | 142 | pub(super) kind: DefKind, |
153 | pub(crate) kind: DefKind, | ||
154 | } | 143 | } |
155 | 144 | ||
156 | #[derive(Debug, PartialEq, Eq, Clone, Copy)] | 145 | #[derive(Debug, PartialEq, Eq, Clone, Copy)] |
157 | pub(crate) enum DefKind { | 146 | pub(super) enum DefKind { |
158 | Function, | 147 | Function(FileAstId<ast::FnDef>), |
159 | Struct, | 148 | Struct(FileAstId<ast::StructDef>), |
160 | Enum, | 149 | Enum(FileAstId<ast::EnumDef>), |
161 | Const, | 150 | Const(FileAstId<ast::ConstDef>), |
162 | Static, | 151 | Static(FileAstId<ast::StaticDef>), |
163 | Trait, | 152 | Trait(FileAstId<ast::TraitDef>), |
164 | TypeAlias, | 153 | TypeAlias(FileAstId<ast::TypeAliasDef>), |
165 | } | 154 | } |
166 | 155 | ||
167 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | 156 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |
168 | pub(crate) struct Macro(RawId); | 157 | pub(super) struct Macro(RawId); |
169 | impl_arena_id!(Macro); | 158 | impl_arena_id!(Macro); |
170 | 159 | ||
171 | #[derive(Debug, PartialEq, Eq)] | 160 | #[derive(Debug, PartialEq, Eq)] |
172 | pub(crate) struct MacroData { | 161 | pub(super) struct MacroData { |
173 | pub(crate) source_item_id: SourceFileItemId, | 162 | pub(super) ast_id: FileAstId<ast::MacroCall>, |
174 | pub(crate) path: Path, | 163 | pub(super) path: Path, |
175 | pub(crate) name: Option<Name>, | 164 | pub(super) name: Option<Name>, |
176 | pub(crate) arg: tt::Subtree, | 165 | pub(super) export: bool, |
177 | pub(crate) export: bool, | ||
178 | } | 166 | } |
179 | 167 | ||
180 | struct RawItemsCollector { | 168 | struct RawItemsCollector { |
181 | raw_items: RawItems, | 169 | raw_items: RawItems, |
182 | source_file_items: Arc<SourceFileItems>, | 170 | source_ast_id_map: Arc<AstIdMap>, |
183 | source_map: ImportSourceMap, | 171 | source_map: ImportSourceMap, |
184 | } | 172 | } |
185 | 173 | ||
@@ -211,18 +199,31 @@ impl RawItemsCollector { | |||
211 | // impls don't participate in name resolution | 199 | // impls don't participate in name resolution |
212 | return; | 200 | return; |
213 | } | 201 | } |
214 | ast::ModuleItemKind::StructDef(it) => (DefKind::Struct, it.name()), | 202 | ast::ModuleItemKind::StructDef(it) => { |
215 | ast::ModuleItemKind::EnumDef(it) => (DefKind::Enum, it.name()), | 203 | (DefKind::Struct(self.source_ast_id_map.ast_id(it)), it.name()) |
216 | ast::ModuleItemKind::FnDef(it) => (DefKind::Function, it.name()), | 204 | } |
217 | ast::ModuleItemKind::TraitDef(it) => (DefKind::Trait, it.name()), | 205 | ast::ModuleItemKind::EnumDef(it) => { |
218 | ast::ModuleItemKind::TypeAliasDef(it) => (DefKind::TypeAlias, it.name()), | 206 | (DefKind::Enum(self.source_ast_id_map.ast_id(it)), it.name()) |
219 | ast::ModuleItemKind::ConstDef(it) => (DefKind::Const, it.name()), | 207 | } |
220 | ast::ModuleItemKind::StaticDef(it) => (DefKind::Static, it.name()), | 208 | ast::ModuleItemKind::FnDef(it) => { |
209 | (DefKind::Function(self.source_ast_id_map.ast_id(it)), it.name()) | ||
210 | } | ||
211 | ast::ModuleItemKind::TraitDef(it) => { | ||
212 | (DefKind::Trait(self.source_ast_id_map.ast_id(it)), it.name()) | ||
213 | } | ||
214 | ast::ModuleItemKind::TypeAliasDef(it) => { | ||
215 | (DefKind::TypeAlias(self.source_ast_id_map.ast_id(it)), it.name()) | ||
216 | } | ||
217 | ast::ModuleItemKind::ConstDef(it) => { | ||
218 | (DefKind::Const(self.source_ast_id_map.ast_id(it)), it.name()) | ||
219 | } | ||
220 | ast::ModuleItemKind::StaticDef(it) => { | ||
221 | (DefKind::Static(self.source_ast_id_map.ast_id(it)), it.name()) | ||
222 | } | ||
221 | }; | 223 | }; |
222 | if let Some(name) = name { | 224 | if let Some(name) = name { |
223 | let name = name.as_name(); | 225 | let name = name.as_name(); |
224 | let source_item_id = self.source_file_items.id_of_unchecked(item.syntax()); | 226 | let def = self.raw_items.defs.alloc(DefData { name, kind }); |
225 | let def = self.raw_items.defs.alloc(DefData { name, kind, source_item_id }); | ||
226 | self.push_item(current_module, RawItem::Def(def)) | 227 | self.push_item(current_module, RawItem::Def(def)) |
227 | } | 228 | } |
228 | } | 229 | } |
@@ -232,10 +233,9 @@ impl RawItemsCollector { | |||
232 | Some(it) => it.as_name(), | 233 | Some(it) => it.as_name(), |
233 | None => return, | 234 | None => return, |
234 | }; | 235 | }; |
235 | let source_item_id = self.source_file_items.id_of_unchecked(module.syntax()); | 236 | let ast_id = self.source_ast_id_map.ast_id(module); |
236 | if module.has_semi() { | 237 | if module.has_semi() { |
237 | let item = | 238 | let item = self.raw_items.modules.alloc(ModuleData::Declaration { name, ast_id }); |
238 | self.raw_items.modules.alloc(ModuleData::Declaration { name, source_item_id }); | ||
239 | self.push_item(current_module, RawItem::Module(item)); | 239 | self.push_item(current_module, RawItem::Module(item)); |
240 | return; | 240 | return; |
241 | } | 241 | } |
@@ -243,7 +243,7 @@ impl RawItemsCollector { | |||
243 | if let Some(item_list) = module.item_list() { | 243 | if let Some(item_list) = module.item_list() { |
244 | let item = self.raw_items.modules.alloc(ModuleData::Definition { | 244 | let item = self.raw_items.modules.alloc(ModuleData::Definition { |
245 | name, | 245 | name, |
246 | source_item_id, | 246 | ast_id, |
247 | items: Vec::new(), | 247 | items: Vec::new(), |
248 | }); | 248 | }); |
249 | self.process_module(Some(item), item_list); | 249 | self.process_module(Some(item), item_list); |
@@ -291,18 +291,15 @@ impl RawItemsCollector { | |||
291 | } | 291 | } |
292 | 292 | ||
293 | fn add_macro(&mut self, current_module: Option<Module>, m: &ast::MacroCall) { | 293 | fn add_macro(&mut self, current_module: Option<Module>, m: &ast::MacroCall) { |
294 | let (path, arg) = match ( | 294 | let path = match m.path().and_then(Path::from_ast) { |
295 | m.path().and_then(Path::from_ast), | 295 | Some(it) => it, |
296 | m.token_tree().and_then(mbe::ast_to_token_tree), | ||
297 | ) { | ||
298 | (Some(path), Some((token_tree, _token_map))) => (path, token_tree), | ||
299 | _ => return, | 296 | _ => return, |
300 | }; | 297 | }; |
301 | 298 | ||
302 | let name = m.name().map(|it| it.as_name()); | 299 | let name = m.name().map(|it| it.as_name()); |
303 | let source_item_id = self.source_file_items.id_of_unchecked(m.syntax()); | 300 | let ast_id = self.source_ast_id_map.ast_id(m); |
304 | let export = m.has_atom_attr("macro_export"); | 301 | let export = m.has_atom_attr("macro_export"); |
305 | let m = self.raw_items.macros.alloc(MacroData { source_item_id, path, arg, name, export }); | 302 | let m = self.raw_items.macros.alloc(MacroData { ast_id, path, name, export }); |
306 | self.push_item(current_module, RawItem::Macro(m)); | 303 | self.push_item(current_module, RawItem::Macro(m)); |
307 | } | 304 | } |
308 | 305 | ||
diff --git a/crates/ra_hir/src/nameres/tests.rs b/crates/ra_hir/src/nameres/tests.rs index ac9b88520..572bd1bf7 100644 --- a/crates/ra_hir/src/nameres/tests.rs +++ b/crates/ra_hir/src/nameres/tests.rs | |||
@@ -552,3 +552,22 @@ foo: v | |||
552 | "### | 552 | "### |
553 | ); | 553 | ); |
554 | } | 554 | } |
555 | |||
556 | #[test] | ||
557 | fn unresolved_module_diagnostics() { | ||
558 | let diagnostics = MockDatabase::with_files( | ||
559 | r" | ||
560 | //- /lib.rs | ||
561 | mod foo; | ||
562 | mod bar; | ||
563 | mod baz {} | ||
564 | //- /foo.rs | ||
565 | ", | ||
566 | ) | ||
567 | .diagnostics(); | ||
568 | |||
569 | assert_snapshot_matches!(diagnostics, @r###" | ||
570 | "mod bar;": unresolved module | ||
571 | "### | ||
572 | ); | ||
573 | } | ||
diff --git a/crates/ra_hir/src/nameres/tests/incremental.rs b/crates/ra_hir/src/nameres/tests/incremental.rs index 698781923..001f76ac3 100644 --- a/crates/ra_hir/src/nameres/tests/incremental.rs +++ b/crates/ra_hir/src/nameres/tests/incremental.rs | |||
@@ -90,34 +90,44 @@ fn adding_inner_items_should_not_invalidate_def_map() { | |||
90 | ); | 90 | ); |
91 | } | 91 | } |
92 | 92 | ||
93 | // It would be awesome to make this work, but it's unclear how | ||
94 | #[test] | 93 | #[test] |
95 | #[ignore] | 94 | fn typing_inside_a_macro_should_not_invalidate_def_map() { |
96 | fn typing_inside_a_function_inside_a_macro_should_not_invalidate_def_map() { | 95 | let (mut db, pos) = MockDatabase::with_position( |
97 | check_def_map_is_not_recomputed( | ||
98 | " | 96 | " |
99 | //- /lib.rs | 97 | //- /lib.rs |
98 | macro_rules! m { | ||
99 | ($ident:ident) => { | ||
100 | fn f() { | ||
101 | $ident + $ident; | ||
102 | }; | ||
103 | } | ||
104 | } | ||
100 | mod foo; | 105 | mod foo; |
101 | 106 | ||
102 | use crate::foo::bar::Baz; | ||
103 | |||
104 | //- /foo/mod.rs | 107 | //- /foo/mod.rs |
105 | pub mod bar; | 108 | pub mod bar; |
106 | 109 | ||
107 | //- /foo/bar.rs | 110 | //- /foo/bar.rs |
108 | <|> | 111 | <|> |
109 | salsa::query_group! { | 112 | m!(X); |
110 | trait Baz { | ||
111 | fn foo() -> i32 { 1 + 1 } | ||
112 | } | ||
113 | } | ||
114 | ", | ||
115 | " | ||
116 | salsa::query_group! { | ||
117 | trait Baz { | ||
118 | fn foo() -> i32 { 92 } | ||
119 | } | ||
120 | } | ||
121 | ", | 113 | ", |
122 | ); | 114 | ); |
115 | { | ||
116 | let events = db.log_executed(|| { | ||
117 | let module = crate::source_binder::module_from_file_id(&db, pos.file_id).unwrap(); | ||
118 | let decls = module.declarations(&db); | ||
119 | assert_eq!(decls.len(), 1); | ||
120 | }); | ||
121 | assert!(format!("{:?}", events).contains("crate_def_map"), "{:#?}", events) | ||
122 | } | ||
123 | db.set_file_text(pos.file_id, Arc::new("m!(Y);".to_string())); | ||
124 | |||
125 | { | ||
126 | let events = db.log_executed(|| { | ||
127 | let module = crate::source_binder::module_from_file_id(&db, pos.file_id).unwrap(); | ||
128 | let decls = module.declarations(&db); | ||
129 | assert_eq!(decls.len(), 1); | ||
130 | }); | ||
131 | assert!(!format!("{:?}", events).contains("crate_def_map"), "{:#?}", events) | ||
132 | } | ||
123 | } | 133 | } |
diff --git a/crates/ra_hir/src/resolve.rs b/crates/ra_hir/src/resolve.rs index f28154517..2609585b1 100644 --- a/crates/ra_hir/src/resolve.rs +++ b/crates/ra_hir/src/resolve.rs | |||
@@ -11,7 +11,7 @@ use crate::{ | |||
11 | generics::GenericParams, | 11 | generics::GenericParams, |
12 | expr::{scope::{ExprScopes, ScopeId}, PatId, Body}, | 12 | expr::{scope::{ExprScopes, ScopeId}, PatId, Body}, |
13 | impl_block::ImplBlock, | 13 | impl_block::ImplBlock, |
14 | path::Path, | 14 | path::Path, Trait |
15 | }; | 15 | }; |
16 | 16 | ||
17 | #[derive(Debug, Clone, Default)] | 17 | #[derive(Debug, Clone, Default)] |
@@ -175,6 +175,21 @@ impl Resolver { | |||
175 | names | 175 | names |
176 | } | 176 | } |
177 | 177 | ||
178 | pub(crate) fn traits_in_scope<'a>(&'a self) -> impl Iterator<Item = Trait> + 'a { | ||
179 | // FIXME prelude | ||
180 | self.scopes | ||
181 | .iter() | ||
182 | .rev() | ||
183 | .flat_map(|scope| { | ||
184 | match scope { | ||
185 | Scope::ModuleScope(m) => Some(m.crate_def_map[m.module_id].scope.traits()), | ||
186 | _ => None, | ||
187 | } | ||
188 | .into_iter() | ||
189 | }) | ||
190 | .flatten() | ||
191 | } | ||
192 | |||
178 | fn module(&self) -> Option<(&CrateDefMap, CrateModuleId)> { | 193 | fn module(&self) -> Option<(&CrateDefMap, CrateModuleId)> { |
179 | self.scopes.iter().rev().find_map(|scope| match scope { | 194 | self.scopes.iter().rev().find_map(|scope| match scope { |
180 | Scope::ModuleScope(m) => Some((&*m.crate_def_map, m.module_id)), | 195 | Scope::ModuleScope(m) => Some((&*m.crate_def_map, m.module_id)), |
diff --git a/crates/ra_hir/src/source_binder.rs b/crates/ra_hir/src/source_binder.rs index 3645b73b4..9dae4c3d1 100644 --- a/crates/ra_hir/src/source_binder.rs +++ b/crates/ra_hir/src/source_binder.rs | |||
@@ -15,8 +15,8 @@ use ra_syntax::{ | |||
15 | use crate::{ | 15 | use crate::{ |
16 | HirDatabase, Function, Struct, Enum, | 16 | HirDatabase, Function, Struct, Enum, |
17 | AsName, Module, HirFileId, Crate, Trait, Resolver, | 17 | AsName, Module, HirFileId, Crate, Trait, Resolver, |
18 | ids::{LocationCtx, SourceFileItemId}, | 18 | ids::LocationCtx, |
19 | expr | 19 | expr, AstId |
20 | }; | 20 | }; |
21 | 21 | ||
22 | /// Locates the module by `FileId`. Picks topmost module in the file. | 22 | /// Locates the module by `FileId`. Picks topmost module in the file. |
@@ -54,8 +54,8 @@ fn module_from_inline( | |||
54 | ) -> Option<Module> { | 54 | ) -> Option<Module> { |
55 | assert!(!module.has_semi()); | 55 | assert!(!module.has_semi()); |
56 | let file_id = file_id.into(); | 56 | let file_id = file_id.into(); |
57 | let file_items = db.file_items(file_id); | 57 | let ast_id_map = db.ast_id_map(file_id); |
58 | let item_id = file_items.id_of(file_id, module.syntax()); | 58 | let item_id = ast_id_map.ast_id(module).with_file_id(file_id); |
59 | module_from_source(db, file_id, Some(item_id)) | 59 | module_from_source(db, file_id, Some(item_id)) |
60 | } | 60 | } |
61 | 61 | ||
@@ -75,7 +75,7 @@ pub fn module_from_child_node( | |||
75 | fn module_from_source( | 75 | fn module_from_source( |
76 | db: &impl HirDatabase, | 76 | db: &impl HirDatabase, |
77 | file_id: HirFileId, | 77 | file_id: HirFileId, |
78 | decl_id: Option<SourceFileItemId>, | 78 | decl_id: Option<AstId<ast::Module>>, |
79 | ) -> Option<Module> { | 79 | ) -> Option<Module> { |
80 | let source_root_id = db.file_source_root(file_id.as_original_file()); | 80 | let source_root_id = db.file_source_root(file_id.as_original_file()); |
81 | db.source_root_crates(source_root_id).iter().map(|&crate_id| Crate { crate_id }).find_map( | 81 | db.source_root_crates(source_root_id).iter().map(|&crate_id| Crate { crate_id }).find_map( |
diff --git a/crates/ra_hir/src/source_id.rs b/crates/ra_hir/src/source_id.rs new file mode 100644 index 000000000..0a8fb6d32 --- /dev/null +++ b/crates/ra_hir/src/source_id.rs | |||
@@ -0,0 +1,150 @@ | |||
1 | use std::{marker::PhantomData, sync::Arc, hash::{Hash, Hasher}}; | ||
2 | |||
3 | use ra_arena::{Arena, RawId, impl_arena_id}; | ||
4 | use ra_syntax::{SyntaxNodePtr, TreeArc, SyntaxNode, SourceFile, AstNode, ast}; | ||
5 | |||
6 | use crate::{HirFileId, DefDatabase}; | ||
7 | |||
8 | /// `AstId` points to an AST node in any file. | ||
9 | /// | ||
10 | /// It is stable across reparses, and can be used as salsa key/value. | ||
11 | #[derive(Debug)] | ||
12 | pub(crate) struct AstId<N: AstNode> { | ||
13 | file_id: HirFileId, | ||
14 | file_ast_id: FileAstId<N>, | ||
15 | } | ||
16 | |||
17 | impl<N: AstNode> Clone for AstId<N> { | ||
18 | fn clone(&self) -> AstId<N> { | ||
19 | *self | ||
20 | } | ||
21 | } | ||
22 | impl<N: AstNode> Copy for AstId<N> {} | ||
23 | |||
24 | impl<N: AstNode> PartialEq for AstId<N> { | ||
25 | fn eq(&self, other: &Self) -> bool { | ||
26 | (self.file_id, self.file_ast_id) == (other.file_id, other.file_ast_id) | ||
27 | } | ||
28 | } | ||
29 | impl<N: AstNode> Eq for AstId<N> {} | ||
30 | impl<N: AstNode> Hash for AstId<N> { | ||
31 | fn hash<H: Hasher>(&self, hasher: &mut H) { | ||
32 | (self.file_id, self.file_ast_id).hash(hasher); | ||
33 | } | ||
34 | } | ||
35 | |||
36 | impl<N: AstNode> AstId<N> { | ||
37 | pub(crate) fn file_id(&self) -> HirFileId { | ||
38 | self.file_id | ||
39 | } | ||
40 | |||
41 | pub(crate) fn to_node(&self, db: &impl DefDatabase) -> TreeArc<N> { | ||
42 | let syntax_node = db.ast_id_to_node(self.file_id, self.file_ast_id.raw); | ||
43 | N::cast(&syntax_node).unwrap().to_owned() | ||
44 | } | ||
45 | } | ||
46 | |||
47 | /// `AstId` points to an AST node in a specific file. | ||
48 | #[derive(Debug)] | ||
49 | pub(crate) struct FileAstId<N: AstNode> { | ||
50 | raw: ErasedFileAstId, | ||
51 | _ty: PhantomData<N>, | ||
52 | } | ||
53 | |||
54 | impl<N: AstNode> Clone for FileAstId<N> { | ||
55 | fn clone(&self) -> FileAstId<N> { | ||
56 | *self | ||
57 | } | ||
58 | } | ||
59 | impl<N: AstNode> Copy for FileAstId<N> {} | ||
60 | |||
61 | impl<N: AstNode> PartialEq for FileAstId<N> { | ||
62 | fn eq(&self, other: &Self) -> bool { | ||
63 | self.raw == other.raw | ||
64 | } | ||
65 | } | ||
66 | impl<N: AstNode> Eq for FileAstId<N> {} | ||
67 | impl<N: AstNode> Hash for FileAstId<N> { | ||
68 | fn hash<H: Hasher>(&self, hasher: &mut H) { | ||
69 | self.raw.hash(hasher); | ||
70 | } | ||
71 | } | ||
72 | |||
73 | impl<N: AstNode> FileAstId<N> { | ||
74 | pub(crate) fn with_file_id(self, file_id: HirFileId) -> AstId<N> { | ||
75 | AstId { file_id, file_ast_id: self } | ||
76 | } | ||
77 | } | ||
78 | |||
79 | #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] | ||
80 | pub struct ErasedFileAstId(RawId); | ||
81 | impl_arena_id!(ErasedFileAstId); | ||
82 | |||
83 | /// Maps items' `SyntaxNode`s to `ErasedFileAstId`s and back. | ||
84 | #[derive(Debug, PartialEq, Eq)] | ||
85 | pub struct AstIdMap { | ||
86 | arena: Arena<ErasedFileAstId, SyntaxNodePtr>, | ||
87 | } | ||
88 | |||
89 | impl AstIdMap { | ||
90 | pub(crate) fn ast_id_map_query(db: &impl DefDatabase, file_id: HirFileId) -> Arc<AstIdMap> { | ||
91 | let source_file = db.hir_parse(file_id); | ||
92 | Arc::new(AstIdMap::from_source_file(&source_file)) | ||
93 | } | ||
94 | |||
95 | pub(crate) fn file_item_query( | ||
96 | db: &impl DefDatabase, | ||
97 | file_id: HirFileId, | ||
98 | ast_id: ErasedFileAstId, | ||
99 | ) -> TreeArc<SyntaxNode> { | ||
100 | let source_file = db.hir_parse(file_id); | ||
101 | db.ast_id_map(file_id).arena[ast_id].to_node(&source_file).to_owned() | ||
102 | } | ||
103 | |||
104 | pub(crate) fn ast_id<N: AstNode>(&self, item: &N) -> FileAstId<N> { | ||
105 | let ptr = SyntaxNodePtr::new(item.syntax()); | ||
106 | let raw = match self.arena.iter().find(|(_id, i)| **i == ptr) { | ||
107 | Some((it, _)) => it, | ||
108 | None => panic!( | ||
109 | "Can't find {:?} in AstIdMap:\n{:?}", | ||
110 | item.syntax(), | ||
111 | self.arena.iter().map(|(_id, i)| i).collect::<Vec<_>>(), | ||
112 | ), | ||
113 | }; | ||
114 | |||
115 | FileAstId { raw, _ty: PhantomData } | ||
116 | } | ||
117 | |||
118 | fn from_source_file(source_file: &SourceFile) -> AstIdMap { | ||
119 | let mut res = AstIdMap { arena: Arena::default() }; | ||
120 | // By walking the tree in bread-first order we make sure that parents | ||
121 | // get lower ids then children. That is, adding a new child does not | ||
122 | // change parent's id. This means that, say, adding a new function to a | ||
123 | // trait does not change ids of top-level items, which helps caching. | ||
124 | bfs(source_file.syntax(), |it| { | ||
125 | if let Some(module_item) = ast::ModuleItem::cast(it) { | ||
126 | res.alloc(module_item.syntax()); | ||
127 | } else if let Some(macro_call) = ast::MacroCall::cast(it) { | ||
128 | res.alloc(macro_call.syntax()); | ||
129 | } | ||
130 | }); | ||
131 | res | ||
132 | } | ||
133 | |||
134 | fn alloc(&mut self, item: &SyntaxNode) -> ErasedFileAstId { | ||
135 | self.arena.alloc(SyntaxNodePtr::new(item)) | ||
136 | } | ||
137 | } | ||
138 | |||
139 | /// Walks the subtree in bfs order, calling `f` for each node. | ||
140 | fn bfs(node: &SyntaxNode, mut f: impl FnMut(&SyntaxNode)) { | ||
141 | let mut curr_layer = vec![node]; | ||
142 | let mut next_layer = vec![]; | ||
143 | while !curr_layer.is_empty() { | ||
144 | curr_layer.drain(..).for_each(|node| { | ||
145 | next_layer.extend(node.children()); | ||
146 | f(node); | ||
147 | }); | ||
148 | std::mem::swap(&mut curr_layer, &mut next_layer); | ||
149 | } | ||
150 | } | ||
diff --git a/crates/ra_hir/src/traits.rs b/crates/ra_hir/src/traits.rs new file mode 100644 index 000000000..725bdd5cb --- /dev/null +++ b/crates/ra_hir/src/traits.rs | |||
@@ -0,0 +1,52 @@ | |||
1 | //! HIR for trait definitions. | ||
2 | |||
3 | use std::sync::Arc; | ||
4 | |||
5 | use ra_syntax::ast::{self, NameOwner}; | ||
6 | |||
7 | use crate::{Function, Const, TypeAlias, Name, DefDatabase, Trait, ids::LocationCtx, name::AsName}; | ||
8 | |||
9 | #[derive(Debug, Clone, PartialEq, Eq)] | ||
10 | pub struct TraitData { | ||
11 | name: Option<Name>, | ||
12 | items: Vec<TraitItem>, | ||
13 | } | ||
14 | |||
15 | impl TraitData { | ||
16 | pub(crate) fn trait_data_query(db: &impl DefDatabase, tr: Trait) -> Arc<TraitData> { | ||
17 | let (file_id, node) = tr.source(db); | ||
18 | let name = node.name().map(|n| n.as_name()); | ||
19 | let module = tr.module(db); | ||
20 | let ctx = LocationCtx::new(db, module, file_id); | ||
21 | let items = if let Some(item_list) = node.item_list() { | ||
22 | item_list | ||
23 | .impl_items() | ||
24 | .map(|item_node| match item_node.kind() { | ||
25 | ast::ImplItemKind::FnDef(it) => Function { id: ctx.to_def(it) }.into(), | ||
26 | ast::ImplItemKind::ConstDef(it) => Const { id: ctx.to_def(it) }.into(), | ||
27 | ast::ImplItemKind::TypeAliasDef(it) => TypeAlias { id: ctx.to_def(it) }.into(), | ||
28 | }) | ||
29 | .collect() | ||
30 | } else { | ||
31 | Vec::new() | ||
32 | }; | ||
33 | Arc::new(TraitData { name, items }) | ||
34 | } | ||
35 | |||
36 | pub(crate) fn name(&self) -> &Option<Name> { | ||
37 | &self.name | ||
38 | } | ||
39 | |||
40 | pub(crate) fn items(&self) -> &[TraitItem] { | ||
41 | &self.items | ||
42 | } | ||
43 | } | ||
44 | |||
45 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | ||
46 | pub enum TraitItem { | ||
47 | Function(Function), | ||
48 | Const(Const), | ||
49 | TypeAlias(TypeAlias), | ||
50 | // Existential | ||
51 | } | ||
52 | impl_froms!(TraitItem: Function, Const, TypeAlias); | ||
diff --git a/crates/ra_hir/src/ty.rs b/crates/ra_hir/src/ty.rs index 7d25ade47..d42c61e9d 100644 --- a/crates/ra_hir/src/ty.rs +++ b/crates/ra_hir/src/ty.rs | |||
@@ -14,7 +14,7 @@ pub(crate) mod display; | |||
14 | use std::sync::Arc; | 14 | use std::sync::Arc; |
15 | use std::{fmt, mem}; | 15 | use std::{fmt, mem}; |
16 | 16 | ||
17 | use crate::{Name, AdtDef, type_ref::Mutability, db::HirDatabase}; | 17 | use crate::{Name, AdtDef, type_ref::Mutability, db::HirDatabase, Trait}; |
18 | 18 | ||
19 | pub(crate) use lower::{TypableDef, CallableDef, type_for_def, type_for_field, callable_item_sig}; | 19 | pub(crate) use lower::{TypableDef, CallableDef, type_for_def, type_for_field, callable_item_sig}; |
20 | pub(crate) use infer::{infer, InferenceResult, InferTy}; | 20 | pub(crate) use infer::{infer, InferenceResult, InferTy}; |
@@ -91,7 +91,7 @@ pub enum TypeCtor { | |||
91 | /// A nominal type with (maybe 0) type parameters. This might be a primitive | 91 | /// A nominal type with (maybe 0) type parameters. This might be a primitive |
92 | /// type like `bool`, a struct, tuple, function pointer, reference or | 92 | /// type like `bool`, a struct, tuple, function pointer, reference or |
93 | /// several other things. | 93 | /// several other things. |
94 | #[derive(Clone, PartialEq, Eq, Debug)] | 94 | #[derive(Clone, PartialEq, Eq, Debug, Hash)] |
95 | pub struct ApplicationTy { | 95 | pub struct ApplicationTy { |
96 | pub ctor: TypeCtor, | 96 | pub ctor: TypeCtor, |
97 | pub parameters: Substs, | 97 | pub parameters: Substs, |
@@ -103,7 +103,7 @@ pub struct ApplicationTy { | |||
103 | /// the same thing (but in a different way). | 103 | /// the same thing (but in a different way). |
104 | /// | 104 | /// |
105 | /// This should be cheap to clone. | 105 | /// This should be cheap to clone. |
106 | #[derive(Clone, PartialEq, Eq, Debug)] | 106 | #[derive(Clone, PartialEq, Eq, Debug, Hash)] |
107 | pub enum Ty { | 107 | pub enum Ty { |
108 | /// A nominal type with (maybe 0) type parameters. This might be a primitive | 108 | /// A nominal type with (maybe 0) type parameters. This might be a primitive |
109 | /// type like `bool`, a struct, tuple, function pointer, reference or | 109 | /// type like `bool`, a struct, tuple, function pointer, reference or |
@@ -132,7 +132,7 @@ pub enum Ty { | |||
132 | } | 132 | } |
133 | 133 | ||
134 | /// A list of substitutions for generic parameters. | 134 | /// A list of substitutions for generic parameters. |
135 | #[derive(Clone, PartialEq, Eq, Debug)] | 135 | #[derive(Clone, PartialEq, Eq, Debug, Hash)] |
136 | pub struct Substs(Arc<[Ty]>); | 136 | pub struct Substs(Arc<[Ty]>); |
137 | 137 | ||
138 | impl Substs { | 138 | impl Substs { |
@@ -169,6 +169,21 @@ impl Substs { | |||
169 | } | 169 | } |
170 | } | 170 | } |
171 | 171 | ||
172 | /// A trait with type parameters. This includes the `Self`, so this represents a concrete type implementing the trait. | ||
173 | /// Name to be bikeshedded: TraitBound? TraitImplements? | ||
174 | #[derive(Clone, PartialEq, Eq, Debug, Hash)] | ||
175 | pub struct TraitRef { | ||
176 | /// FIXME name? | ||
177 | trait_: Trait, | ||
178 | substs: Substs, | ||
179 | } | ||
180 | |||
181 | impl TraitRef { | ||
182 | pub fn self_ty(&self) -> &Ty { | ||
183 | &self.substs.0[0] | ||
184 | } | ||
185 | } | ||
186 | |||
172 | /// A function signature as seen by type inference: Several parameter types and | 187 | /// A function signature as seen by type inference: Several parameter types and |
173 | /// one return type. | 188 | /// one return type. |
174 | #[derive(Clone, PartialEq, Eq, Debug)] | 189 | #[derive(Clone, PartialEq, Eq, Debug)] |
diff --git a/crates/ra_hir/src/ty/infer.rs b/crates/ra_hir/src/ty/infer.rs index cff7e7481..573115321 100644 --- a/crates/ra_hir/src/ty/infer.rs +++ b/crates/ra_hir/src/ty/infer.rs | |||
@@ -36,7 +36,9 @@ use crate::{ | |||
36 | path::{GenericArgs, GenericArg}, | 36 | path::{GenericArgs, GenericArg}, |
37 | adt::VariantDef, | 37 | adt::VariantDef, |
38 | resolve::{Resolver, Resolution}, | 38 | resolve::{Resolver, Resolution}, |
39 | nameres::Namespace | 39 | nameres::Namespace, |
40 | ty::infer::diagnostics::InferenceDiagnostic, | ||
41 | diagnostics::DiagnosticSink, | ||
40 | }; | 42 | }; |
41 | use super::{Ty, TypableDef, Substs, primitive, op, FnSig, ApplicationTy, TypeCtor}; | 43 | use super::{Ty, TypableDef, Substs, primitive, op, FnSig, ApplicationTy, TypeCtor}; |
42 | 44 | ||
@@ -96,6 +98,7 @@ pub struct InferenceResult { | |||
96 | field_resolutions: FxHashMap<ExprId, StructField>, | 98 | field_resolutions: FxHashMap<ExprId, StructField>, |
97 | /// For each associated item record what it resolves to | 99 | /// For each associated item record what it resolves to |
98 | assoc_resolutions: FxHashMap<ExprOrPatId, ImplItem>, | 100 | assoc_resolutions: FxHashMap<ExprOrPatId, ImplItem>, |
101 | diagnostics: Vec<InferenceDiagnostic>, | ||
99 | pub(super) type_of_expr: ArenaMap<ExprId, Ty>, | 102 | pub(super) type_of_expr: ArenaMap<ExprId, Ty>, |
100 | pub(super) type_of_pat: ArenaMap<PatId, Ty>, | 103 | pub(super) type_of_pat: ArenaMap<PatId, Ty>, |
101 | } | 104 | } |
@@ -113,6 +116,14 @@ impl InferenceResult { | |||
113 | pub fn assoc_resolutions_for_pat(&self, id: PatId) -> Option<ImplItem> { | 116 | pub fn assoc_resolutions_for_pat(&self, id: PatId) -> Option<ImplItem> { |
114 | self.assoc_resolutions.get(&id.into()).map(|it| *it) | 117 | self.assoc_resolutions.get(&id.into()).map(|it| *it) |
115 | } | 118 | } |
119 | pub(crate) fn add_diagnostics( | ||
120 | &self, | ||
121 | db: &impl HirDatabase, | ||
122 | owner: Function, | ||
123 | sink: &mut DiagnosticSink, | ||
124 | ) { | ||
125 | self.diagnostics.iter().for_each(|it| it.add_to(db, owner, sink)) | ||
126 | } | ||
116 | } | 127 | } |
117 | 128 | ||
118 | impl Index<ExprId> for InferenceResult { | 129 | impl Index<ExprId> for InferenceResult { |
@@ -143,6 +154,7 @@ struct InferenceContext<'a, D: HirDatabase> { | |||
143 | assoc_resolutions: FxHashMap<ExprOrPatId, ImplItem>, | 154 | assoc_resolutions: FxHashMap<ExprOrPatId, ImplItem>, |
144 | type_of_expr: ArenaMap<ExprId, Ty>, | 155 | type_of_expr: ArenaMap<ExprId, Ty>, |
145 | type_of_pat: ArenaMap<PatId, Ty>, | 156 | type_of_pat: ArenaMap<PatId, Ty>, |
157 | diagnostics: Vec<InferenceDiagnostic>, | ||
146 | /// The return type of the function being inferred. | 158 | /// The return type of the function being inferred. |
147 | return_ty: Ty, | 159 | return_ty: Ty, |
148 | } | 160 | } |
@@ -155,6 +167,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { | |||
155 | assoc_resolutions: FxHashMap::default(), | 167 | assoc_resolutions: FxHashMap::default(), |
156 | type_of_expr: ArenaMap::default(), | 168 | type_of_expr: ArenaMap::default(), |
157 | type_of_pat: ArenaMap::default(), | 169 | type_of_pat: ArenaMap::default(), |
170 | diagnostics: Vec::default(), | ||
158 | var_unification_table: InPlaceUnificationTable::new(), | 171 | var_unification_table: InPlaceUnificationTable::new(), |
159 | return_ty: Ty::Unknown, // set in collect_fn_signature | 172 | return_ty: Ty::Unknown, // set in collect_fn_signature |
160 | db, | 173 | db, |
@@ -181,6 +194,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { | |||
181 | assoc_resolutions: self.assoc_resolutions, | 194 | assoc_resolutions: self.assoc_resolutions, |
182 | type_of_expr: expr_types, | 195 | type_of_expr: expr_types, |
183 | type_of_pat: pat_types, | 196 | type_of_pat: pat_types, |
197 | diagnostics: self.diagnostics, | ||
184 | } | 198 | } |
185 | } | 199 | } |
186 | 200 | ||
@@ -807,7 +821,8 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { | |||
807 | } | 821 | } |
808 | Expr::MethodCall { receiver, args, method_name, generic_args } => { | 822 | Expr::MethodCall { receiver, args, method_name, generic_args } => { |
809 | let receiver_ty = self.infer_expr(*receiver, &Expectation::none()); | 823 | let receiver_ty = self.infer_expr(*receiver, &Expectation::none()); |
810 | let resolved = receiver_ty.clone().lookup_method(self.db, method_name); | 824 | let resolved = |
825 | receiver_ty.clone().lookup_method(self.db, method_name, &self.resolver); | ||
811 | let (derefed_receiver_ty, method_ty, def_generics) = match resolved { | 826 | let (derefed_receiver_ty, method_ty, def_generics) = match resolved { |
812 | Some((ty, func)) => { | 827 | Some((ty, func)) => { |
813 | self.write_method_resolution(tgt_expr, func); | 828 | self.write_method_resolution(tgt_expr, func); |
@@ -915,9 +930,18 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { | |||
915 | Expr::StructLit { path, fields, spread } => { | 930 | Expr::StructLit { path, fields, spread } => { |
916 | let (ty, def_id) = self.resolve_variant(path.as_ref()); | 931 | let (ty, def_id) = self.resolve_variant(path.as_ref()); |
917 | let substs = ty.substs().unwrap_or_else(Substs::empty); | 932 | let substs = ty.substs().unwrap_or_else(Substs::empty); |
918 | for field in fields { | 933 | for (field_idx, field) in fields.into_iter().enumerate() { |
919 | let field_ty = def_id | 934 | let field_ty = def_id |
920 | .and_then(|it| it.field(self.db, &field.name)) | 935 | .and_then(|it| match it.field(self.db, &field.name) { |
936 | Some(field) => Some(field), | ||
937 | None => { | ||
938 | self.diagnostics.push(InferenceDiagnostic::NoSuchField { | ||
939 | expr: tgt_expr, | ||
940 | field: field_idx, | ||
941 | }); | ||
942 | None | ||
943 | } | ||
944 | }) | ||
921 | .map_or(Ty::Unknown, |field| field.ty(self.db)) | 945 | .map_or(Ty::Unknown, |field| field.ty(self.db)) |
922 | .subst(&substs); | 946 | .subst(&substs); |
923 | self.infer_expr(field.expr, &Expectation::has_type(field_ty)); | 947 | self.infer_expr(field.expr, &Expectation::has_type(field_ty)); |
@@ -1244,3 +1268,29 @@ impl Expectation { | |||
1244 | Expectation { ty: Ty::Unknown } | 1268 | Expectation { ty: Ty::Unknown } |
1245 | } | 1269 | } |
1246 | } | 1270 | } |
1271 | |||
1272 | mod diagnostics { | ||
1273 | use crate::{expr::ExprId, diagnostics::{DiagnosticSink, NoSuchField}, HirDatabase, Function}; | ||
1274 | |||
1275 | #[derive(Debug, PartialEq, Eq, Clone)] | ||
1276 | pub(super) enum InferenceDiagnostic { | ||
1277 | NoSuchField { expr: ExprId, field: usize }, | ||
1278 | } | ||
1279 | |||
1280 | impl InferenceDiagnostic { | ||
1281 | pub(super) fn add_to( | ||
1282 | &self, | ||
1283 | db: &impl HirDatabase, | ||
1284 | owner: Function, | ||
1285 | sink: &mut DiagnosticSink, | ||
1286 | ) { | ||
1287 | match self { | ||
1288 | InferenceDiagnostic::NoSuchField { expr, field } => { | ||
1289 | let (file, _) = owner.source(db); | ||
1290 | let field = owner.body_source_map(db).field_syntax(*expr, *field); | ||
1291 | sink.push(NoSuchField { file, field }) | ||
1292 | } | ||
1293 | } | ||
1294 | } | ||
1295 | } | ||
1296 | } | ||
diff --git a/crates/ra_hir/src/ty/method_resolution.rs b/crates/ra_hir/src/ty/method_resolution.rs index b1684acf9..3ac8dc46b 100644 --- a/crates/ra_hir/src/ty/method_resolution.rs +++ b/crates/ra_hir/src/ty/method_resolution.rs | |||
@@ -8,12 +8,12 @@ use rustc_hash::FxHashMap; | |||
8 | 8 | ||
9 | use crate::{ | 9 | use crate::{ |
10 | HirDatabase, Module, Crate, Name, Function, Trait, | 10 | HirDatabase, Module, Crate, Name, Function, Trait, |
11 | ids::TraitId, | ||
12 | impl_block::{ImplId, ImplBlock, ImplItem}, | 11 | impl_block::{ImplId, ImplBlock, ImplItem}, |
13 | ty::{Ty, TypeCtor}, | 12 | ty::{Ty, TypeCtor}, |
14 | nameres::CrateModuleId, | 13 | nameres::CrateModuleId, resolve::Resolver, traits::TraitItem |
15 | 14 | ||
16 | }; | 15 | }; |
16 | use super::{ TraitRef, Substs}; | ||
17 | 17 | ||
18 | /// This is used as a key for indexing impls. | 18 | /// This is used as a key for indexing impls. |
19 | #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] | 19 | #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] |
@@ -38,7 +38,7 @@ pub struct CrateImplBlocks { | |||
38 | /// To make sense of the CrateModuleIds, we need the source root. | 38 | /// To make sense of the CrateModuleIds, we need the source root. |
39 | krate: Crate, | 39 | krate: Crate, |
40 | impls: FxHashMap<TyFingerprint, Vec<(CrateModuleId, ImplId)>>, | 40 | impls: FxHashMap<TyFingerprint, Vec<(CrateModuleId, ImplId)>>, |
41 | impls_by_trait: FxHashMap<TraitId, Vec<(CrateModuleId, ImplId)>>, | 41 | impls_by_trait: FxHashMap<Trait, Vec<(CrateModuleId, ImplId)>>, |
42 | } | 42 | } |
43 | 43 | ||
44 | impl CrateImplBlocks { | 44 | impl CrateImplBlocks { |
@@ -56,8 +56,7 @@ impl CrateImplBlocks { | |||
56 | &'a self, | 56 | &'a self, |
57 | tr: &Trait, | 57 | tr: &Trait, |
58 | ) -> impl Iterator<Item = ImplBlock> + 'a { | 58 | ) -> impl Iterator<Item = ImplBlock> + 'a { |
59 | let id = tr.id; | 59 | self.impls_by_trait.get(&tr).into_iter().flat_map(|i| i.iter()).map( |
60 | self.impls_by_trait.get(&id).into_iter().flat_map(|i| i.iter()).map( | ||
61 | move |(module_id, impl_id)| { | 60 | move |(module_id, impl_id)| { |
62 | let module = Module { krate: self.krate, module_id: *module_id }; | 61 | let module = Module { krate: self.krate, module_id: *module_id }; |
63 | ImplBlock::from_id(module, *impl_id) | 62 | ImplBlock::from_id(module, *impl_id) |
@@ -73,18 +72,18 @@ impl CrateImplBlocks { | |||
73 | 72 | ||
74 | let target_ty = impl_block.target_ty(db); | 73 | let target_ty = impl_block.target_ty(db); |
75 | 74 | ||
76 | if let Some(target_ty_fp) = TyFingerprint::for_impl(&target_ty) { | ||
77 | self.impls | ||
78 | .entry(target_ty_fp) | ||
79 | .or_insert_with(Vec::new) | ||
80 | .push((module.module_id, impl_id)); | ||
81 | } | ||
82 | |||
83 | if let Some(tr) = impl_block.target_trait(db) { | 75 | if let Some(tr) = impl_block.target_trait(db) { |
84 | self.impls_by_trait | 76 | self.impls_by_trait |
85 | .entry(tr.id) | 77 | .entry(tr) |
86 | .or_insert_with(Vec::new) | 78 | .or_insert_with(Vec::new) |
87 | .push((module.module_id, impl_id)); | 79 | .push((module.module_id, impl_id)); |
80 | } else { | ||
81 | if let Some(target_ty_fp) = TyFingerprint::for_impl(&target_ty) { | ||
82 | self.impls | ||
83 | .entry(target_ty_fp) | ||
84 | .or_insert_with(Vec::new) | ||
85 | .push((module.module_id, impl_id)); | ||
86 | } | ||
88 | } | 87 | } |
89 | } | 88 | } |
90 | 89 | ||
@@ -109,6 +108,20 @@ impl CrateImplBlocks { | |||
109 | } | 108 | } |
110 | } | 109 | } |
111 | 110 | ||
111 | /// Rudimentary check whether an impl exists for a given type and trait; this | ||
112 | /// will actually be done by chalk. | ||
113 | pub(crate) fn implements(db: &impl HirDatabase, trait_ref: TraitRef) -> bool { | ||
114 | // FIXME use all trait impls in the whole crate graph | ||
115 | let krate = trait_ref.trait_.module(db).krate(db); | ||
116 | let krate = match krate { | ||
117 | Some(krate) => krate, | ||
118 | None => return false, | ||
119 | }; | ||
120 | let crate_impl_blocks = db.impls_in_crate(krate); | ||
121 | let mut impl_blocks = crate_impl_blocks.lookup_impl_blocks_for_trait(&trait_ref.trait_); | ||
122 | impl_blocks.any(|impl_block| &impl_block.target_ty(db) == trait_ref.self_ty()) | ||
123 | } | ||
124 | |||
112 | fn def_crate(db: &impl HirDatabase, ty: &Ty) -> Option<Crate> { | 125 | fn def_crate(db: &impl HirDatabase, ty: &Ty) -> Option<Crate> { |
113 | match ty { | 126 | match ty { |
114 | Ty::Apply(a_ty) => match a_ty.ctor { | 127 | Ty::Apply(a_ty) => match a_ty.ctor { |
@@ -120,20 +133,64 @@ fn def_crate(db: &impl HirDatabase, ty: &Ty) -> Option<Crate> { | |||
120 | } | 133 | } |
121 | 134 | ||
122 | impl Ty { | 135 | impl Ty { |
123 | // FIXME: cache this as a query? | ||
124 | // - if so, what signature? (TyFingerprint, Name)? | ||
125 | // - or maybe cache all names and def_ids of methods per fingerprint? | ||
126 | /// Look up the method with the given name, returning the actual autoderefed | 136 | /// Look up the method with the given name, returning the actual autoderefed |
127 | /// receiver type (but without autoref applied yet). | 137 | /// receiver type (but without autoref applied yet). |
128 | pub fn lookup_method(self, db: &impl HirDatabase, name: &Name) -> Option<(Ty, Function)> { | 138 | pub fn lookup_method( |
129 | self.iterate_methods(db, |ty, f| { | 139 | self, |
140 | db: &impl HirDatabase, | ||
141 | name: &Name, | ||
142 | resolver: &Resolver, | ||
143 | ) -> Option<(Ty, Function)> { | ||
144 | // FIXME: trait methods should be used before autoderefs | ||
145 | let inherent_method = self.clone().iterate_methods(db, |ty, f| { | ||
130 | let sig = f.signature(db); | 146 | let sig = f.signature(db); |
131 | if sig.name() == name && sig.has_self_param() { | 147 | if sig.name() == name && sig.has_self_param() { |
132 | Some((ty.clone(), f)) | 148 | Some((ty.clone(), f)) |
133 | } else { | 149 | } else { |
134 | None | 150 | None |
135 | } | 151 | } |
136 | }) | 152 | }); |
153 | inherent_method.or_else(|| self.lookup_trait_method(db, name, resolver)) | ||
154 | } | ||
155 | |||
156 | fn lookup_trait_method( | ||
157 | self, | ||
158 | db: &impl HirDatabase, | ||
159 | name: &Name, | ||
160 | resolver: &Resolver, | ||
161 | ) -> Option<(Ty, Function)> { | ||
162 | let mut candidates = Vec::new(); | ||
163 | for t in resolver.traits_in_scope() { | ||
164 | let data = t.trait_data(db); | ||
165 | for item in data.items() { | ||
166 | match item { | ||
167 | &TraitItem::Function(m) => { | ||
168 | let sig = m.signature(db); | ||
169 | if sig.name() == name && sig.has_self_param() { | ||
170 | candidates.push((t, m)); | ||
171 | } | ||
172 | } | ||
173 | _ => {} | ||
174 | } | ||
175 | } | ||
176 | } | ||
177 | // FIXME: | ||
178 | // - we might not actually be able to determine fully that the type | ||
179 | // implements the trait here; it's enough if we (well, Chalk) determine | ||
180 | // that it's possible. | ||
181 | // - when the trait method is picked, we need to register an | ||
182 | // 'obligation' somewhere so that we later check that it's really | ||
183 | // implemented | ||
184 | // - both points go for additional requirements from where clauses as | ||
185 | // well (in fact, the 'implements' condition could just be considered a | ||
186 | // 'where Self: Trait' clause) | ||
187 | candidates.retain(|(t, _m)| { | ||
188 | let trait_ref = TraitRef { trait_: *t, substs: Substs::single(self.clone()) }; | ||
189 | db.implements(trait_ref) | ||
190 | }); | ||
191 | // FIXME if there's multiple candidates here, that's an ambiguity error | ||
192 | let (_chosen_trait, chosen_method) = candidates.first()?; | ||
193 | Some((self.clone(), *chosen_method)) | ||
137 | } | 194 | } |
138 | 195 | ||
139 | // This would be nicer if it just returned an iterator, but that runs into | 196 | // This would be nicer if it just returned an iterator, but that runs into |
diff --git a/crates/ra_hir/src/ty/tests.rs b/crates/ra_hir/src/ty/tests.rs index 5d8ad4aa7..655f3c522 100644 --- a/crates/ra_hir/src/ty/tests.rs +++ b/crates/ra_hir/src/ty/tests.rs | |||
@@ -1272,8 +1272,8 @@ fn test() { | |||
1272 | [241; 252) 'Struct::FOO': u32 | 1272 | [241; 252) 'Struct::FOO': u32 |
1273 | [262; 263) 'y': u32 | 1273 | [262; 263) 'y': u32 |
1274 | [266; 275) 'Enum::BAR': u32 | 1274 | [266; 275) 'Enum::BAR': u32 |
1275 | [285; 286) 'z': u32 | 1275 | [285; 286) 'z': {unknown} |
1276 | [289; 302) 'TraitTest::ID': u32"### | 1276 | [289; 302) 'TraitTest::ID': {unknown}"### |
1277 | ); | 1277 | ); |
1278 | } | 1278 | } |
1279 | 1279 | ||
@@ -1918,9 +1918,9 @@ fn test() { | |||
1918 | [110; 114) 'self': &{unknown} | 1918 | [110; 114) 'self': &{unknown} |
1919 | [170; 228) '{ ...i128 }': () | 1919 | [170; 228) '{ ...i128 }': () |
1920 | [176; 178) 'S1': S1 | 1920 | [176; 178) 'S1': S1 |
1921 | [176; 187) 'S1.method()': {unknown} | 1921 | [176; 187) 'S1.method()': u32 |
1922 | [203; 205) 'S2': S2 | 1922 | [203; 205) 'S2': S2 |
1923 | [203; 214) 'S2.method()': {unknown}"### | 1923 | [203; 214) 'S2.method()': i128"### |
1924 | ); | 1924 | ); |
1925 | } | 1925 | } |
1926 | 1926 | ||
@@ -1964,10 +1964,10 @@ mod bar_test { | |||
1964 | [169; 173) 'self': &{unknown} | 1964 | [169; 173) 'self': &{unknown} |
1965 | [300; 337) '{ ... }': () | 1965 | [300; 337) '{ ... }': () |
1966 | [310; 311) 'S': S | 1966 | [310; 311) 'S': S |
1967 | [310; 320) 'S.method()': {unknown} | 1967 | [310; 320) 'S.method()': u32 |
1968 | [416; 454) '{ ... }': () | 1968 | [416; 454) '{ ... }': () |
1969 | [426; 427) 'S': S | 1969 | [426; 427) 'S': S |
1970 | [426; 436) 'S.method()': {unknown}"### | 1970 | [426; 436) 'S.method()': i128"### |
1971 | ); | 1971 | ); |
1972 | } | 1972 | } |
1973 | 1973 | ||
@@ -2319,3 +2319,27 @@ fn typing_whitespace_inside_a_function_should_not_invalidate_types() { | |||
2319 | assert!(!format!("{:?}", events).contains("infer"), "{:#?}", events) | 2319 | assert!(!format!("{:?}", events).contains("infer"), "{:#?}", events) |
2320 | } | 2320 | } |
2321 | } | 2321 | } |
2322 | |||
2323 | #[test] | ||
2324 | fn no_such_field_diagnostics() { | ||
2325 | let diagnostics = MockDatabase::with_files( | ||
2326 | r" | ||
2327 | //- /lib.rs | ||
2328 | struct S { foo: i32, bar: () } | ||
2329 | impl S { | ||
2330 | fn new() -> S { | ||
2331 | S { | ||
2332 | foo: 92, | ||
2333 | baz: 62, | ||
2334 | } | ||
2335 | } | ||
2336 | } | ||
2337 | ", | ||
2338 | ) | ||
2339 | .diagnostics(); | ||
2340 | |||
2341 | assert_snapshot_matches!(diagnostics, @r###" | ||
2342 | "baz: 62": no such field | ||
2343 | "### | ||
2344 | ); | ||
2345 | } | ||
diff --git a/crates/ra_ide_api/Cargo.toml b/crates/ra_ide_api/Cargo.toml index c64226801..45bab4e28 100644 --- a/crates/ra_ide_api/Cargo.toml +++ b/crates/ra_ide_api/Cargo.toml | |||
@@ -20,7 +20,6 @@ jemallocator = { version = "0.1.9", optional = true } | |||
20 | jemalloc-ctl = { version = "0.2.0", optional = true } | 20 | jemalloc-ctl = { version = "0.2.0", optional = true } |
21 | 21 | ||
22 | ra_syntax = { path = "../ra_syntax" } | 22 | ra_syntax = { path = "../ra_syntax" } |
23 | ra_ide_api_light = { path = "../ra_ide_api_light" } | ||
24 | ra_text_edit = { path = "../ra_text_edit" } | 23 | ra_text_edit = { path = "../ra_text_edit" } |
25 | ra_db = { path = "../ra_db" } | 24 | ra_db = { path = "../ra_db" } |
26 | ra_fmt = { path = "../ra_fmt" } | 25 | ra_fmt = { path = "../ra_fmt" } |
diff --git a/crates/ra_ide_api/src/change.rs b/crates/ra_ide_api/src/change.rs index 26fde91bc..a4a086931 100644 --- a/crates/ra_ide_api/src/change.rs +++ b/crates/ra_ide_api/src/change.rs | |||
@@ -220,8 +220,8 @@ impl RootDatabase { | |||
220 | self.query(ra_db::ParseQuery).sweep(sweep); | 220 | self.query(ra_db::ParseQuery).sweep(sweep); |
221 | 221 | ||
222 | self.query(hir::db::HirParseQuery).sweep(sweep); | 222 | self.query(hir::db::HirParseQuery).sweep(sweep); |
223 | self.query(hir::db::FileItemsQuery).sweep(sweep); | 223 | self.query(hir::db::AstIdMapQuery).sweep(sweep); |
224 | self.query(hir::db::FileItemQuery).sweep(sweep); | 224 | self.query(hir::db::AstIdToNodeQuery).sweep(sweep); |
225 | 225 | ||
226 | self.query(hir::db::RawItemsWithSourceMapQuery).sweep(sweep); | 226 | self.query(hir::db::RawItemsWithSourceMapQuery).sweep(sweep); |
227 | self.query(hir::db::BodyWithSourceMapQuery).sweep(sweep); | 227 | self.query(hir::db::BodyWithSourceMapQuery).sweep(sweep); |
diff --git a/crates/ra_ide_api/src/diagnostics.rs b/crates/ra_ide_api/src/diagnostics.rs index 156f28ca3..5a78e94d8 100644 --- a/crates/ra_ide_api/src/diagnostics.rs +++ b/crates/ra_ide_api/src/diagnostics.rs | |||
@@ -1,10 +1,11 @@ | |||
1 | use std::cell::RefCell; | ||
2 | |||
1 | use itertools::Itertools; | 3 | use itertools::Itertools; |
2 | use hir::{Problem, source_binder}; | 4 | use hir::{source_binder, diagnostics::{Diagnostic as _, DiagnosticSink}}; |
3 | use ra_db::SourceDatabase; | 5 | use ra_db::SourceDatabase; |
4 | use ra_syntax::{ | 6 | use ra_syntax::{ |
5 | Location, SourceFile, SyntaxKind, TextRange, SyntaxNode, | 7 | Location, SourceFile, SyntaxKind, TextRange, SyntaxNode, |
6 | ast::{self, AstNode}, | 8 | ast::{self, AstNode}, |
7 | |||
8 | }; | 9 | }; |
9 | use ra_text_edit::{TextEdit, TextEditBuilder}; | 10 | use ra_text_edit::{TextEdit, TextEditBuilder}; |
10 | 11 | ||
@@ -26,11 +27,31 @@ pub(crate) fn diagnostics(db: &RootDatabase, file_id: FileId) -> Vec<Diagnostic> | |||
26 | check_unnecessary_braces_in_use_statement(&mut res, file_id, node); | 27 | check_unnecessary_braces_in_use_statement(&mut res, file_id, node); |
27 | check_struct_shorthand_initialization(&mut res, file_id, node); | 28 | check_struct_shorthand_initialization(&mut res, file_id, node); |
28 | } | 29 | } |
29 | 30 | let res = RefCell::new(res); | |
31 | let mut sink = DiagnosticSink::new(|d| { | ||
32 | res.borrow_mut().push(Diagnostic { | ||
33 | message: d.message(), | ||
34 | range: d.highlight_range(), | ||
35 | severity: Severity::Error, | ||
36 | fix: None, | ||
37 | }) | ||
38 | }) | ||
39 | .on::<hir::diagnostics::UnresolvedModule, _>(|d| { | ||
40 | let source_root = db.file_source_root(d.file().original_file(db)); | ||
41 | let create_file = FileSystemEdit::CreateFile { source_root, path: d.candidate.clone() }; | ||
42 | let fix = SourceChange::file_system_edit("create module", create_file); | ||
43 | res.borrow_mut().push(Diagnostic { | ||
44 | range: d.highlight_range(), | ||
45 | message: d.message(), | ||
46 | severity: Severity::Error, | ||
47 | fix: Some(fix), | ||
48 | }) | ||
49 | }); | ||
30 | if let Some(m) = source_binder::module_from_file_id(db, file_id) { | 50 | if let Some(m) = source_binder::module_from_file_id(db, file_id) { |
31 | check_module(&mut res, db, file_id, m); | 51 | m.diagnostics(db, &mut sink); |
32 | }; | 52 | }; |
33 | res | 53 | drop(sink); |
54 | res.into_inner() | ||
34 | } | 55 | } |
35 | 56 | ||
36 | fn syntax_errors(acc: &mut Vec<Diagnostic>, source_file: &SourceFile) { | 57 | fn syntax_errors(acc: &mut Vec<Diagnostic>, source_file: &SourceFile) { |
@@ -128,34 +149,12 @@ fn check_struct_shorthand_initialization( | |||
128 | Some(()) | 149 | Some(()) |
129 | } | 150 | } |
130 | 151 | ||
131 | fn check_module( | ||
132 | acc: &mut Vec<Diagnostic>, | ||
133 | db: &RootDatabase, | ||
134 | file_id: FileId, | ||
135 | module: hir::Module, | ||
136 | ) { | ||
137 | let source_root = db.file_source_root(file_id); | ||
138 | for (name_node, problem) in module.problems(db) { | ||
139 | let diag = match problem { | ||
140 | Problem::UnresolvedModule { candidate } => { | ||
141 | let create_file = | ||
142 | FileSystemEdit::CreateFile { source_root, path: candidate.clone() }; | ||
143 | let fix = SourceChange::file_system_edit("create module", create_file); | ||
144 | Diagnostic { | ||
145 | range: name_node.range(), | ||
146 | message: "unresolved module".to_string(), | ||
147 | severity: Severity::Error, | ||
148 | fix: Some(fix), | ||
149 | } | ||
150 | } | ||
151 | }; | ||
152 | acc.push(diag) | ||
153 | } | ||
154 | } | ||
155 | |||
156 | #[cfg(test)] | 152 | #[cfg(test)] |
157 | mod tests { | 153 | mod tests { |
158 | use test_utils::assert_eq_text; | 154 | use test_utils::assert_eq_text; |
155 | use insta::assert_debug_snapshot_matches; | ||
156 | |||
157 | use crate::mock_analysis::single_file; | ||
159 | 158 | ||
160 | use super::*; | 159 | use super::*; |
161 | 160 | ||
@@ -185,6 +184,34 @@ mod tests { | |||
185 | } | 184 | } |
186 | 185 | ||
187 | #[test] | 186 | #[test] |
187 | fn test_unresolved_module_diagnostic() { | ||
188 | let (analysis, file_id) = single_file("mod foo;"); | ||
189 | let diagnostics = analysis.diagnostics(file_id).unwrap(); | ||
190 | assert_debug_snapshot_matches!(diagnostics, @r####"[ | ||
191 | Diagnostic { | ||
192 | message: "unresolved module", | ||
193 | range: [0; 8), | ||
194 | fix: Some( | ||
195 | SourceChange { | ||
196 | label: "create module", | ||
197 | source_file_edits: [], | ||
198 | file_system_edits: [ | ||
199 | CreateFile { | ||
200 | source_root: SourceRootId( | ||
201 | 0 | ||
202 | ), | ||
203 | path: "foo.rs" | ||
204 | } | ||
205 | ], | ||
206 | cursor_position: None | ||
207 | } | ||
208 | ), | ||
209 | severity: Error | ||
210 | } | ||
211 | ]"####); | ||
212 | } | ||
213 | |||
214 | #[test] | ||
188 | fn test_check_unnecessary_braces_in_use_statement() { | 215 | fn test_check_unnecessary_braces_in_use_statement() { |
189 | check_not_applicable( | 216 | check_not_applicable( |
190 | " | 217 | " |
diff --git a/crates/ra_ide_api/src/lib.rs b/crates/ra_ide_api/src/lib.rs index 8aa3eb088..9063f78a9 100644 --- a/crates/ra_ide_api/src/lib.rs +++ b/crates/ra_ide_api/src/lib.rs | |||
@@ -6,9 +6,6 @@ | |||
6 | //! database, and the `ra_hir` crate, where majority of the analysis happens. | 6 | //! database, and the `ra_hir` crate, where majority of the analysis happens. |
7 | //! However, IDE specific bits of the analysis (most notably completion) happen | 7 | //! However, IDE specific bits of the analysis (most notably completion) happen |
8 | //! in this crate. | 8 | //! in this crate. |
9 | //! | ||
10 | //! The sibling `ra_ide_api_light` handles those bits of IDE functionality | ||
11 | //! which are restricted to a single file and need only syntax. | ||
12 | 9 | ||
13 | // For proving that RootDatabase is RefUnwindSafe. | 10 | // For proving that RootDatabase is RefUnwindSafe. |
14 | #![recursion_limit = "128"] | 11 | #![recursion_limit = "128"] |
@@ -33,10 +30,11 @@ mod impls; | |||
33 | mod assists; | 30 | mod assists; |
34 | mod diagnostics; | 31 | mod diagnostics; |
35 | mod syntax_tree; | 32 | mod syntax_tree; |
36 | mod line_index; | ||
37 | mod folding_ranges; | 33 | mod folding_ranges; |
34 | mod line_index; | ||
38 | mod line_index_utils; | 35 | mod line_index_utils; |
39 | mod join_lines; | 36 | mod join_lines; |
37 | mod structure; | ||
40 | mod typing; | 38 | mod typing; |
41 | mod matching_brace; | 39 | mod matching_brace; |
42 | 40 | ||
@@ -72,9 +70,10 @@ pub use crate::{ | |||
72 | line_index_utils::translate_offset_with_edit, | 70 | line_index_utils::translate_offset_with_edit, |
73 | folding_ranges::{Fold, FoldKind}, | 71 | folding_ranges::{Fold, FoldKind}, |
74 | syntax_highlighting::HighlightedRange, | 72 | syntax_highlighting::HighlightedRange, |
73 | structure::{StructureNode, file_structure}, | ||
75 | diagnostics::Severity, | 74 | diagnostics::Severity, |
76 | }; | 75 | }; |
77 | pub use ra_ide_api_light::StructureNode; | 76 | |
78 | pub use ra_db::{ | 77 | pub use ra_db::{ |
79 | Canceled, CrateGraph, CrateId, FileId, FilePosition, FileRange, SourceRootId, | 78 | Canceled, CrateGraph, CrateId, FileId, FilePosition, FileRange, SourceRootId, |
80 | Edition | 79 | Edition |
@@ -388,7 +387,7 @@ impl Analysis { | |||
388 | /// file outline. | 387 | /// file outline. |
389 | pub fn file_structure(&self, file_id: FileId) -> Vec<StructureNode> { | 388 | pub fn file_structure(&self, file_id: FileId) -> Vec<StructureNode> { |
390 | let file = self.db.parse(file_id); | 389 | let file = self.db.parse(file_id); |
391 | ra_ide_api_light::file_structure(&file) | 390 | structure::file_structure(&file) |
392 | } | 391 | } |
393 | 392 | ||
394 | /// Returns the set of folding ranges. | 393 | /// Returns the set of folding ranges. |
diff --git a/crates/ra_ide_api/src/parent_module.rs b/crates/ra_ide_api/src/parent_module.rs index 603c3db6a..27788c984 100644 --- a/crates/ra_ide_api/src/parent_module.rs +++ b/crates/ra_ide_api/src/parent_module.rs | |||
@@ -28,7 +28,11 @@ pub(crate) fn crate_for(db: &RootDatabase, file_id: FileId) -> Vec<CrateId> { | |||
28 | 28 | ||
29 | #[cfg(test)] | 29 | #[cfg(test)] |
30 | mod tests { | 30 | mod tests { |
31 | use crate::mock_analysis::analysis_and_position; | 31 | use crate::{ |
32 | AnalysisChange, CrateGraph, | ||
33 | mock_analysis::{analysis_and_position, MockAnalysis}, | ||
34 | Edition::Edition2018, | ||
35 | }; | ||
32 | 36 | ||
33 | #[test] | 37 | #[test] |
34 | fn test_resolve_parent_module() { | 38 | fn test_resolve_parent_module() { |
@@ -59,4 +63,28 @@ mod tests { | |||
59 | let nav = analysis.parent_module(pos).unwrap().pop().unwrap(); | 63 | let nav = analysis.parent_module(pos).unwrap().pop().unwrap(); |
60 | nav.assert_match("baz MODULE FileId(1) [32; 44)"); | 64 | nav.assert_match("baz MODULE FileId(1) [32; 44)"); |
61 | } | 65 | } |
66 | |||
67 | #[test] | ||
68 | fn test_resolve_crate_root() { | ||
69 | let mock = MockAnalysis::with_files( | ||
70 | " | ||
71 | //- /bar.rs | ||
72 | mod foo; | ||
73 | //- /foo.rs | ||
74 | // empty <|> | ||
75 | ", | ||
76 | ); | ||
77 | let root_file = mock.id_of("/bar.rs"); | ||
78 | let mod_file = mock.id_of("/foo.rs"); | ||
79 | let mut host = mock.analysis_host(); | ||
80 | assert!(host.analysis().crate_for(mod_file).unwrap().is_empty()); | ||
81 | |||
82 | let mut crate_graph = CrateGraph::default(); | ||
83 | let crate_id = crate_graph.add_crate_root(root_file, Edition2018); | ||
84 | let mut change = AnalysisChange::new(); | ||
85 | change.set_crate_graph(crate_graph); | ||
86 | host.apply_change(change); | ||
87 | |||
88 | assert_eq!(host.analysis().crate_for(mod_file).unwrap(), vec![crate_id]); | ||
89 | } | ||
62 | } | 90 | } |
diff --git a/crates/ra_ide_api/src/references.rs b/crates/ra_ide_api/src/references.rs index 22741445a..20bbf11a3 100644 --- a/crates/ra_ide_api/src/references.rs +++ b/crates/ra_ide_api/src/references.rs | |||
@@ -216,10 +216,56 @@ mod tests { | |||
216 | use crate::{ | 216 | use crate::{ |
217 | mock_analysis::single_file_with_position, | 217 | mock_analysis::single_file_with_position, |
218 | mock_analysis::analysis_and_position, | 218 | mock_analysis::analysis_and_position, |
219 | FileId | 219 | FileId, ReferenceSearchResult |
220 | }; | 220 | }; |
221 | 221 | ||
222 | #[test] | 222 | #[test] |
223 | fn test_find_all_refs_for_local() { | ||
224 | let code = r#" | ||
225 | fn main() { | ||
226 | let mut i = 1; | ||
227 | let j = 1; | ||
228 | i = i<|> + j; | ||
229 | |||
230 | { | ||
231 | i = 0; | ||
232 | } | ||
233 | |||
234 | i = 5; | ||
235 | }"#; | ||
236 | |||
237 | let refs = get_all_refs(code); | ||
238 | assert_eq!(refs.len(), 5); | ||
239 | } | ||
240 | |||
241 | #[test] | ||
242 | fn test_find_all_refs_for_param_inside() { | ||
243 | let code = r#" | ||
244 | fn foo(i : u32) -> u32 { | ||
245 | i<|> | ||
246 | }"#; | ||
247 | |||
248 | let refs = get_all_refs(code); | ||
249 | assert_eq!(refs.len(), 2); | ||
250 | } | ||
251 | |||
252 | #[test] | ||
253 | fn test_find_all_refs_for_fn_param() { | ||
254 | let code = r#" | ||
255 | fn foo(i<|> : u32) -> u32 { | ||
256 | i | ||
257 | }"#; | ||
258 | |||
259 | let refs = get_all_refs(code); | ||
260 | assert_eq!(refs.len(), 2); | ||
261 | } | ||
262 | |||
263 | fn get_all_refs(text: &str) -> ReferenceSearchResult { | ||
264 | let (analysis, position) = single_file_with_position(text); | ||
265 | analysis.find_all_refs(position).unwrap().unwrap() | ||
266 | } | ||
267 | |||
268 | #[test] | ||
223 | fn test_rename_for_local() { | 269 | fn test_rename_for_local() { |
224 | test_rename( | 270 | test_rename( |
225 | r#" | 271 | r#" |
diff --git a/crates/ra_ide_api_light/src/snapshots/tests__file_structure.snap b/crates/ra_ide_api/src/snapshots/tests__file_structure.snap index 8e4184b31..2efa8e22c 100644 --- a/crates/ra_ide_api_light/src/snapshots/tests__file_structure.snap +++ b/crates/ra_ide_api/src/snapshots/tests__file_structure.snap | |||
@@ -1,7 +1,7 @@ | |||
1 | --- | 1 | --- |
2 | created: "2019-02-05T22:03:50.763530100Z" | 2 | created: "2019-02-05T22:03:50.763530100Z" |
3 | creator: [email protected] | 3 | creator: [email protected] |
4 | source: crates/ra_ide_api_light/src/structure.rs | 4 | source: crates/ra_ide_api/src/structure.rs |
5 | expression: structure | 5 | expression: structure |
6 | --- | 6 | --- |
7 | [ | 7 | [ |
diff --git a/crates/ra_ide_api_light/src/structure.rs b/crates/ra_ide_api/src/structure.rs index ec2c9bbc6..ec2c9bbc6 100644 --- a/crates/ra_ide_api_light/src/structure.rs +++ b/crates/ra_ide_api/src/structure.rs | |||
diff --git a/crates/ra_ide_api/src/symbol_index.rs b/crates/ra_ide_api/src/symbol_index.rs index 0978d164a..0eadc4e71 100644 --- a/crates/ra_ide_api/src/symbol_index.rs +++ b/crates/ra_ide_api/src/symbol_index.rs | |||
@@ -270,3 +270,61 @@ fn to_file_symbol(node: &SyntaxNode, file_id: FileId) -> Option<FileSymbol> { | |||
270 | container_name: None, | 270 | container_name: None, |
271 | }) | 271 | }) |
272 | } | 272 | } |
273 | |||
274 | #[cfg(test)] | ||
275 | mod tests { | ||
276 | use ra_syntax::SmolStr; | ||
277 | use crate::{ | ||
278 | navigation_target::NavigationTarget, | ||
279 | mock_analysis::single_file, | ||
280 | Query, | ||
281 | }; | ||
282 | |||
283 | #[test] | ||
284 | fn test_world_symbols_with_no_container() { | ||
285 | let code = r#" | ||
286 | enum FooInner { } | ||
287 | "#; | ||
288 | |||
289 | let mut symbols = get_symbols_matching(code, "FooInner"); | ||
290 | |||
291 | let s = symbols.pop().unwrap(); | ||
292 | |||
293 | assert_eq!(s.name(), "FooInner"); | ||
294 | assert!(s.container_name().is_none()); | ||
295 | } | ||
296 | |||
297 | #[test] | ||
298 | fn test_world_symbols_include_container_name() { | ||
299 | let code = r#" | ||
300 | fn foo() { | ||
301 | enum FooInner { } | ||
302 | } | ||
303 | "#; | ||
304 | |||
305 | let mut symbols = get_symbols_matching(code, "FooInner"); | ||
306 | |||
307 | let s = symbols.pop().unwrap(); | ||
308 | |||
309 | assert_eq!(s.name(), "FooInner"); | ||
310 | assert_eq!(s.container_name(), Some(&SmolStr::new("foo"))); | ||
311 | |||
312 | let code = r#" | ||
313 | mod foo { | ||
314 | struct FooInner; | ||
315 | } | ||
316 | "#; | ||
317 | |||
318 | let mut symbols = get_symbols_matching(code, "FooInner"); | ||
319 | |||
320 | let s = symbols.pop().unwrap(); | ||
321 | |||
322 | assert_eq!(s.name(), "FooInner"); | ||
323 | assert_eq!(s.container_name(), Some(&SmolStr::new("foo"))); | ||
324 | } | ||
325 | |||
326 | fn get_symbols_matching(text: &str, query: &str) -> Vec<NavigationTarget> { | ||
327 | let (analysis, _) = single_file(text); | ||
328 | analysis.symbol_search(Query::new(query.into())).unwrap() | ||
329 | } | ||
330 | } | ||
diff --git a/crates/ra_ide_api/src/syntax_tree.rs b/crates/ra_ide_api/src/syntax_tree.rs index bbe9222b4..276f8a8c8 100644 --- a/crates/ra_ide_api/src/syntax_tree.rs +++ b/crates/ra_ide_api/src/syntax_tree.rs | |||
@@ -85,3 +85,260 @@ fn syntax_tree_for_token<T: AstToken>(node: &T, text_range: TextRange) -> Option | |||
85 | 85 | ||
86 | None | 86 | None |
87 | } | 87 | } |
88 | |||
89 | #[cfg(test)] | ||
90 | mod tests { | ||
91 | use crate::mock_analysis::{single_file, single_file_with_range}; | ||
92 | |||
93 | #[test] | ||
94 | fn test_syntax_tree_without_range() { | ||
95 | // Basic syntax | ||
96 | let (analysis, file_id) = single_file(r#"fn foo() {}"#); | ||
97 | let syn = analysis.syntax_tree(file_id, None); | ||
98 | |||
99 | assert_eq!( | ||
100 | syn.trim(), | ||
101 | r#" | ||
102 | SOURCE_FILE@[0; 11) | ||
103 | FN_DEF@[0; 11) | ||
104 | FN_KW@[0; 2) | ||
105 | WHITESPACE@[2; 3) | ||
106 | NAME@[3; 6) | ||
107 | IDENT@[3; 6) "foo" | ||
108 | PARAM_LIST@[6; 8) | ||
109 | L_PAREN@[6; 7) | ||
110 | R_PAREN@[7; 8) | ||
111 | WHITESPACE@[8; 9) | ||
112 | BLOCK@[9; 11) | ||
113 | L_CURLY@[9; 10) | ||
114 | R_CURLY@[10; 11) | ||
115 | "# | ||
116 | .trim() | ||
117 | ); | ||
118 | |||
119 | let (analysis, file_id) = single_file( | ||
120 | r#" | ||
121 | fn test() { | ||
122 | assert!(" | ||
123 | fn foo() { | ||
124 | } | ||
125 | ", ""); | ||
126 | }"# | ||
127 | .trim(), | ||
128 | ); | ||
129 | let syn = analysis.syntax_tree(file_id, None); | ||
130 | |||
131 | assert_eq!( | ||
132 | syn.trim(), | ||
133 | r#" | ||
134 | SOURCE_FILE@[0; 60) | ||
135 | FN_DEF@[0; 60) | ||
136 | FN_KW@[0; 2) | ||
137 | WHITESPACE@[2; 3) | ||
138 | NAME@[3; 7) | ||
139 | IDENT@[3; 7) "test" | ||
140 | PARAM_LIST@[7; 9) | ||
141 | L_PAREN@[7; 8) | ||
142 | R_PAREN@[8; 9) | ||
143 | WHITESPACE@[9; 10) | ||
144 | BLOCK@[10; 60) | ||
145 | L_CURLY@[10; 11) | ||
146 | WHITESPACE@[11; 16) | ||
147 | EXPR_STMT@[16; 58) | ||
148 | MACRO_CALL@[16; 57) | ||
149 | PATH@[16; 22) | ||
150 | PATH_SEGMENT@[16; 22) | ||
151 | NAME_REF@[16; 22) | ||
152 | IDENT@[16; 22) "assert" | ||
153 | EXCL@[22; 23) | ||
154 | TOKEN_TREE@[23; 57) | ||
155 | L_PAREN@[23; 24) | ||
156 | STRING@[24; 52) | ||
157 | COMMA@[52; 53) | ||
158 | WHITESPACE@[53; 54) | ||
159 | STRING@[54; 56) | ||
160 | R_PAREN@[56; 57) | ||
161 | SEMI@[57; 58) | ||
162 | WHITESPACE@[58; 59) | ||
163 | R_CURLY@[59; 60) | ||
164 | "# | ||
165 | .trim() | ||
166 | ); | ||
167 | } | ||
168 | |||
169 | #[test] | ||
170 | fn test_syntax_tree_with_range() { | ||
171 | let (analysis, range) = single_file_with_range(r#"<|>fn foo() {}<|>"#.trim()); | ||
172 | let syn = analysis.syntax_tree(range.file_id, Some(range.range)); | ||
173 | |||
174 | assert_eq!( | ||
175 | syn.trim(), | ||
176 | r#" | ||
177 | FN_DEF@[0; 11) | ||
178 | FN_KW@[0; 2) | ||
179 | WHITESPACE@[2; 3) | ||
180 | NAME@[3; 6) | ||
181 | IDENT@[3; 6) "foo" | ||
182 | PARAM_LIST@[6; 8) | ||
183 | L_PAREN@[6; 7) | ||
184 | R_PAREN@[7; 8) | ||
185 | WHITESPACE@[8; 9) | ||
186 | BLOCK@[9; 11) | ||
187 | L_CURLY@[9; 10) | ||
188 | R_CURLY@[10; 11) | ||
189 | "# | ||
190 | .trim() | ||
191 | ); | ||
192 | |||
193 | let (analysis, range) = single_file_with_range( | ||
194 | r#"fn test() { | ||
195 | <|>assert!(" | ||
196 | fn foo() { | ||
197 | } | ||
198 | ", "");<|> | ||
199 | }"# | ||
200 | .trim(), | ||
201 | ); | ||
202 | let syn = analysis.syntax_tree(range.file_id, Some(range.range)); | ||
203 | |||
204 | assert_eq!( | ||
205 | syn.trim(), | ||
206 | r#" | ||
207 | EXPR_STMT@[16; 58) | ||
208 | MACRO_CALL@[16; 57) | ||
209 | PATH@[16; 22) | ||
210 | PATH_SEGMENT@[16; 22) | ||
211 | NAME_REF@[16; 22) | ||
212 | IDENT@[16; 22) "assert" | ||
213 | EXCL@[22; 23) | ||
214 | TOKEN_TREE@[23; 57) | ||
215 | L_PAREN@[23; 24) | ||
216 | STRING@[24; 52) | ||
217 | COMMA@[52; 53) | ||
218 | WHITESPACE@[53; 54) | ||
219 | STRING@[54; 56) | ||
220 | R_PAREN@[56; 57) | ||
221 | SEMI@[57; 58) | ||
222 | "# | ||
223 | .trim() | ||
224 | ); | ||
225 | } | ||
226 | |||
227 | #[test] | ||
228 | fn test_syntax_tree_inside_string() { | ||
229 | let (analysis, range) = single_file_with_range( | ||
230 | r#"fn test() { | ||
231 | assert!(" | ||
232 | <|>fn foo() { | ||
233 | }<|> | ||
234 | fn bar() { | ||
235 | } | ||
236 | ", ""); | ||
237 | }"# | ||
238 | .trim(), | ||
239 | ); | ||
240 | let syn = analysis.syntax_tree(range.file_id, Some(range.range)); | ||
241 | assert_eq!( | ||
242 | syn.trim(), | ||
243 | r#" | ||
244 | SOURCE_FILE@[0; 12) | ||
245 | FN_DEF@[0; 12) | ||
246 | FN_KW@[0; 2) | ||
247 | WHITESPACE@[2; 3) | ||
248 | NAME@[3; 6) | ||
249 | IDENT@[3; 6) "foo" | ||
250 | PARAM_LIST@[6; 8) | ||
251 | L_PAREN@[6; 7) | ||
252 | R_PAREN@[7; 8) | ||
253 | WHITESPACE@[8; 9) | ||
254 | BLOCK@[9; 12) | ||
255 | L_CURLY@[9; 10) | ||
256 | WHITESPACE@[10; 11) | ||
257 | R_CURLY@[11; 12) | ||
258 | "# | ||
259 | .trim() | ||
260 | ); | ||
261 | |||
262 | // With a raw string | ||
263 | let (analysis, range) = single_file_with_range( | ||
264 | r###"fn test() { | ||
265 | assert!(r#" | ||
266 | <|>fn foo() { | ||
267 | }<|> | ||
268 | fn bar() { | ||
269 | } | ||
270 | "#, ""); | ||
271 | }"### | ||
272 | .trim(), | ||
273 | ); | ||
274 | let syn = analysis.syntax_tree(range.file_id, Some(range.range)); | ||
275 | assert_eq!( | ||
276 | syn.trim(), | ||
277 | r#" | ||
278 | SOURCE_FILE@[0; 12) | ||
279 | FN_DEF@[0; 12) | ||
280 | FN_KW@[0; 2) | ||
281 | WHITESPACE@[2; 3) | ||
282 | NAME@[3; 6) | ||
283 | IDENT@[3; 6) "foo" | ||
284 | PARAM_LIST@[6; 8) | ||
285 | L_PAREN@[6; 7) | ||
286 | R_PAREN@[7; 8) | ||
287 | WHITESPACE@[8; 9) | ||
288 | BLOCK@[9; 12) | ||
289 | L_CURLY@[9; 10) | ||
290 | WHITESPACE@[10; 11) | ||
291 | R_CURLY@[11; 12) | ||
292 | "# | ||
293 | .trim() | ||
294 | ); | ||
295 | |||
296 | // With a raw string | ||
297 | let (analysis, range) = single_file_with_range( | ||
298 | r###"fn test() { | ||
299 | assert!(r<|>#" | ||
300 | fn foo() { | ||
301 | } | ||
302 | fn bar() { | ||
303 | }"<|>#, ""); | ||
304 | }"### | ||
305 | .trim(), | ||
306 | ); | ||
307 | let syn = analysis.syntax_tree(range.file_id, Some(range.range)); | ||
308 | assert_eq!( | ||
309 | syn.trim(), | ||
310 | r#" | ||
311 | SOURCE_FILE@[0; 25) | ||
312 | FN_DEF@[0; 12) | ||
313 | FN_KW@[0; 2) | ||
314 | WHITESPACE@[2; 3) | ||
315 | NAME@[3; 6) | ||
316 | IDENT@[3; 6) "foo" | ||
317 | PARAM_LIST@[6; 8) | ||
318 | L_PAREN@[6; 7) | ||
319 | R_PAREN@[7; 8) | ||
320 | WHITESPACE@[8; 9) | ||
321 | BLOCK@[9; 12) | ||
322 | L_CURLY@[9; 10) | ||
323 | WHITESPACE@[10; 11) | ||
324 | R_CURLY@[11; 12) | ||
325 | WHITESPACE@[12; 13) | ||
326 | FN_DEF@[13; 25) | ||
327 | FN_KW@[13; 15) | ||
328 | WHITESPACE@[15; 16) | ||
329 | NAME@[16; 19) | ||
330 | IDENT@[16; 19) "bar" | ||
331 | PARAM_LIST@[19; 21) | ||
332 | L_PAREN@[19; 20) | ||
333 | R_PAREN@[20; 21) | ||
334 | WHITESPACE@[21; 22) | ||
335 | BLOCK@[22; 25) | ||
336 | L_CURLY@[22; 23) | ||
337 | WHITESPACE@[23; 24) | ||
338 | R_CURLY@[24; 25) | ||
339 | |||
340 | "# | ||
341 | .trim() | ||
342 | ); | ||
343 | } | ||
344 | } | ||
diff --git a/crates/ra_ide_api/tests/test/main.rs b/crates/ra_ide_api/tests/test/main.rs deleted file mode 100644 index 0f0766f62..000000000 --- a/crates/ra_ide_api/tests/test/main.rs +++ /dev/null | |||
@@ -1,392 +0,0 @@ | |||
1 | use insta::assert_debug_snapshot_matches; | ||
2 | use ra_ide_api::{ | ||
3 | mock_analysis::{single_file, single_file_with_position, single_file_with_range, MockAnalysis}, | ||
4 | AnalysisChange, CrateGraph, Edition::Edition2018, Query, NavigationTarget, | ||
5 | ReferenceSearchResult, | ||
6 | }; | ||
7 | use ra_syntax::SmolStr; | ||
8 | |||
9 | #[test] | ||
10 | fn test_unresolved_module_diagnostic() { | ||
11 | let (analysis, file_id) = single_file("mod foo;"); | ||
12 | let diagnostics = analysis.diagnostics(file_id).unwrap(); | ||
13 | assert_debug_snapshot_matches!("unresolved_module_diagnostic", &diagnostics); | ||
14 | } | ||
15 | |||
16 | // FIXME: move this test to hir | ||
17 | #[test] | ||
18 | fn test_unresolved_module_diagnostic_no_diag_for_inline_mode() { | ||
19 | let (analysis, file_id) = single_file("mod foo {}"); | ||
20 | let diagnostics = analysis.diagnostics(file_id).unwrap(); | ||
21 | assert!(diagnostics.is_empty()); | ||
22 | } | ||
23 | |||
24 | #[test] | ||
25 | fn test_resolve_crate_root() { | ||
26 | let mock = MockAnalysis::with_files( | ||
27 | " | ||
28 | //- /bar.rs | ||
29 | mod foo; | ||
30 | //- /foo.rs | ||
31 | // empty <|> | ||
32 | ", | ||
33 | ); | ||
34 | let root_file = mock.id_of("/bar.rs"); | ||
35 | let mod_file = mock.id_of("/foo.rs"); | ||
36 | let mut host = mock.analysis_host(); | ||
37 | assert!(host.analysis().crate_for(mod_file).unwrap().is_empty()); | ||
38 | |||
39 | let mut crate_graph = CrateGraph::default(); | ||
40 | let crate_id = crate_graph.add_crate_root(root_file, Edition2018); | ||
41 | let mut change = AnalysisChange::new(); | ||
42 | change.set_crate_graph(crate_graph); | ||
43 | host.apply_change(change); | ||
44 | |||
45 | assert_eq!(host.analysis().crate_for(mod_file).unwrap(), vec![crate_id]); | ||
46 | } | ||
47 | |||
48 | fn get_all_refs(text: &str) -> ReferenceSearchResult { | ||
49 | let (analysis, position) = single_file_with_position(text); | ||
50 | analysis.find_all_refs(position).unwrap().unwrap() | ||
51 | } | ||
52 | |||
53 | fn get_symbols_matching(text: &str, query: &str) -> Vec<NavigationTarget> { | ||
54 | let (analysis, _) = single_file(text); | ||
55 | analysis.symbol_search(Query::new(query.into())).unwrap() | ||
56 | } | ||
57 | |||
58 | #[test] | ||
59 | fn test_find_all_refs_for_local() { | ||
60 | let code = r#" | ||
61 | fn main() { | ||
62 | let mut i = 1; | ||
63 | let j = 1; | ||
64 | i = i<|> + j; | ||
65 | |||
66 | { | ||
67 | i = 0; | ||
68 | } | ||
69 | |||
70 | i = 5; | ||
71 | }"#; | ||
72 | |||
73 | let refs = get_all_refs(code); | ||
74 | assert_eq!(refs.len(), 5); | ||
75 | } | ||
76 | |||
77 | #[test] | ||
78 | fn test_find_all_refs_for_param_inside() { | ||
79 | let code = r#" | ||
80 | fn foo(i : u32) -> u32 { | ||
81 | i<|> | ||
82 | }"#; | ||
83 | |||
84 | let refs = get_all_refs(code); | ||
85 | assert_eq!(refs.len(), 2); | ||
86 | } | ||
87 | |||
88 | #[test] | ||
89 | fn test_find_all_refs_for_fn_param() { | ||
90 | let code = r#" | ||
91 | fn foo(i<|> : u32) -> u32 { | ||
92 | i | ||
93 | }"#; | ||
94 | |||
95 | let refs = get_all_refs(code); | ||
96 | assert_eq!(refs.len(), 2); | ||
97 | } | ||
98 | |||
99 | #[test] | ||
100 | fn test_world_symbols_with_no_container() { | ||
101 | let code = r#" | ||
102 | enum FooInner { } | ||
103 | "#; | ||
104 | |||
105 | let mut symbols = get_symbols_matching(code, "FooInner"); | ||
106 | |||
107 | let s = symbols.pop().unwrap(); | ||
108 | |||
109 | assert_eq!(s.name(), "FooInner"); | ||
110 | assert!(s.container_name().is_none()); | ||
111 | } | ||
112 | |||
113 | #[test] | ||
114 | fn test_world_symbols_include_container_name() { | ||
115 | let code = r#" | ||
116 | fn foo() { | ||
117 | enum FooInner { } | ||
118 | } | ||
119 | "#; | ||
120 | |||
121 | let mut symbols = get_symbols_matching(code, "FooInner"); | ||
122 | |||
123 | let s = symbols.pop().unwrap(); | ||
124 | |||
125 | assert_eq!(s.name(), "FooInner"); | ||
126 | assert_eq!(s.container_name(), Some(&SmolStr::new("foo"))); | ||
127 | |||
128 | let code = r#" | ||
129 | mod foo { | ||
130 | struct FooInner; | ||
131 | } | ||
132 | "#; | ||
133 | |||
134 | let mut symbols = get_symbols_matching(code, "FooInner"); | ||
135 | |||
136 | let s = symbols.pop().unwrap(); | ||
137 | |||
138 | assert_eq!(s.name(), "FooInner"); | ||
139 | assert_eq!(s.container_name(), Some(&SmolStr::new("foo"))); | ||
140 | } | ||
141 | |||
142 | #[test] | ||
143 | fn test_syntax_tree_without_range() { | ||
144 | // Basic syntax | ||
145 | let (analysis, file_id) = single_file(r#"fn foo() {}"#); | ||
146 | let syn = analysis.syntax_tree(file_id, None); | ||
147 | |||
148 | assert_eq!( | ||
149 | syn.trim(), | ||
150 | r#" | ||
151 | SOURCE_FILE@[0; 11) | ||
152 | FN_DEF@[0; 11) | ||
153 | FN_KW@[0; 2) | ||
154 | WHITESPACE@[2; 3) | ||
155 | NAME@[3; 6) | ||
156 | IDENT@[3; 6) "foo" | ||
157 | PARAM_LIST@[6; 8) | ||
158 | L_PAREN@[6; 7) | ||
159 | R_PAREN@[7; 8) | ||
160 | WHITESPACE@[8; 9) | ||
161 | BLOCK@[9; 11) | ||
162 | L_CURLY@[9; 10) | ||
163 | R_CURLY@[10; 11) | ||
164 | "# | ||
165 | .trim() | ||
166 | ); | ||
167 | |||
168 | let (analysis, file_id) = single_file( | ||
169 | r#" | ||
170 | fn test() { | ||
171 | assert!(" | ||
172 | fn foo() { | ||
173 | } | ||
174 | ", ""); | ||
175 | }"# | ||
176 | .trim(), | ||
177 | ); | ||
178 | let syn = analysis.syntax_tree(file_id, None); | ||
179 | |||
180 | assert_eq!( | ||
181 | syn.trim(), | ||
182 | r#" | ||
183 | SOURCE_FILE@[0; 60) | ||
184 | FN_DEF@[0; 60) | ||
185 | FN_KW@[0; 2) | ||
186 | WHITESPACE@[2; 3) | ||
187 | NAME@[3; 7) | ||
188 | IDENT@[3; 7) "test" | ||
189 | PARAM_LIST@[7; 9) | ||
190 | L_PAREN@[7; 8) | ||
191 | R_PAREN@[8; 9) | ||
192 | WHITESPACE@[9; 10) | ||
193 | BLOCK@[10; 60) | ||
194 | L_CURLY@[10; 11) | ||
195 | WHITESPACE@[11; 16) | ||
196 | EXPR_STMT@[16; 58) | ||
197 | MACRO_CALL@[16; 57) | ||
198 | PATH@[16; 22) | ||
199 | PATH_SEGMENT@[16; 22) | ||
200 | NAME_REF@[16; 22) | ||
201 | IDENT@[16; 22) "assert" | ||
202 | EXCL@[22; 23) | ||
203 | TOKEN_TREE@[23; 57) | ||
204 | L_PAREN@[23; 24) | ||
205 | STRING@[24; 52) | ||
206 | COMMA@[52; 53) | ||
207 | WHITESPACE@[53; 54) | ||
208 | STRING@[54; 56) | ||
209 | R_PAREN@[56; 57) | ||
210 | SEMI@[57; 58) | ||
211 | WHITESPACE@[58; 59) | ||
212 | R_CURLY@[59; 60) | ||
213 | "# | ||
214 | .trim() | ||
215 | ); | ||
216 | } | ||
217 | |||
218 | #[test] | ||
219 | fn test_syntax_tree_with_range() { | ||
220 | let (analysis, range) = single_file_with_range(r#"<|>fn foo() {}<|>"#.trim()); | ||
221 | let syn = analysis.syntax_tree(range.file_id, Some(range.range)); | ||
222 | |||
223 | assert_eq!( | ||
224 | syn.trim(), | ||
225 | r#" | ||
226 | FN_DEF@[0; 11) | ||
227 | FN_KW@[0; 2) | ||
228 | WHITESPACE@[2; 3) | ||
229 | NAME@[3; 6) | ||
230 | IDENT@[3; 6) "foo" | ||
231 | PARAM_LIST@[6; 8) | ||
232 | L_PAREN@[6; 7) | ||
233 | R_PAREN@[7; 8) | ||
234 | WHITESPACE@[8; 9) | ||
235 | BLOCK@[9; 11) | ||
236 | L_CURLY@[9; 10) | ||
237 | R_CURLY@[10; 11) | ||
238 | "# | ||
239 | .trim() | ||
240 | ); | ||
241 | |||
242 | let (analysis, range) = single_file_with_range( | ||
243 | r#"fn test() { | ||
244 | <|>assert!(" | ||
245 | fn foo() { | ||
246 | } | ||
247 | ", "");<|> | ||
248 | }"# | ||
249 | .trim(), | ||
250 | ); | ||
251 | let syn = analysis.syntax_tree(range.file_id, Some(range.range)); | ||
252 | |||
253 | assert_eq!( | ||
254 | syn.trim(), | ||
255 | r#" | ||
256 | EXPR_STMT@[16; 58) | ||
257 | MACRO_CALL@[16; 57) | ||
258 | PATH@[16; 22) | ||
259 | PATH_SEGMENT@[16; 22) | ||
260 | NAME_REF@[16; 22) | ||
261 | IDENT@[16; 22) "assert" | ||
262 | EXCL@[22; 23) | ||
263 | TOKEN_TREE@[23; 57) | ||
264 | L_PAREN@[23; 24) | ||
265 | STRING@[24; 52) | ||
266 | COMMA@[52; 53) | ||
267 | WHITESPACE@[53; 54) | ||
268 | STRING@[54; 56) | ||
269 | R_PAREN@[56; 57) | ||
270 | SEMI@[57; 58) | ||
271 | "# | ||
272 | .trim() | ||
273 | ); | ||
274 | } | ||
275 | |||
276 | #[test] | ||
277 | fn test_syntax_tree_inside_string() { | ||
278 | let (analysis, range) = single_file_with_range( | ||
279 | r#"fn test() { | ||
280 | assert!(" | ||
281 | <|>fn foo() { | ||
282 | }<|> | ||
283 | fn bar() { | ||
284 | } | ||
285 | ", ""); | ||
286 | }"# | ||
287 | .trim(), | ||
288 | ); | ||
289 | let syn = analysis.syntax_tree(range.file_id, Some(range.range)); | ||
290 | assert_eq!( | ||
291 | syn.trim(), | ||
292 | r#" | ||
293 | SOURCE_FILE@[0; 12) | ||
294 | FN_DEF@[0; 12) | ||
295 | FN_KW@[0; 2) | ||
296 | WHITESPACE@[2; 3) | ||
297 | NAME@[3; 6) | ||
298 | IDENT@[3; 6) "foo" | ||
299 | PARAM_LIST@[6; 8) | ||
300 | L_PAREN@[6; 7) | ||
301 | R_PAREN@[7; 8) | ||
302 | WHITESPACE@[8; 9) | ||
303 | BLOCK@[9; 12) | ||
304 | L_CURLY@[9; 10) | ||
305 | WHITESPACE@[10; 11) | ||
306 | R_CURLY@[11; 12) | ||
307 | "# | ||
308 | .trim() | ||
309 | ); | ||
310 | |||
311 | // With a raw string | ||
312 | let (analysis, range) = single_file_with_range( | ||
313 | r###"fn test() { | ||
314 | assert!(r#" | ||
315 | <|>fn foo() { | ||
316 | }<|> | ||
317 | fn bar() { | ||
318 | } | ||
319 | "#, ""); | ||
320 | }"### | ||
321 | .trim(), | ||
322 | ); | ||
323 | let syn = analysis.syntax_tree(range.file_id, Some(range.range)); | ||
324 | assert_eq!( | ||
325 | syn.trim(), | ||
326 | r#" | ||
327 | SOURCE_FILE@[0; 12) | ||
328 | FN_DEF@[0; 12) | ||
329 | FN_KW@[0; 2) | ||
330 | WHITESPACE@[2; 3) | ||
331 | NAME@[3; 6) | ||
332 | IDENT@[3; 6) "foo" | ||
333 | PARAM_LIST@[6; 8) | ||
334 | L_PAREN@[6; 7) | ||
335 | R_PAREN@[7; 8) | ||
336 | WHITESPACE@[8; 9) | ||
337 | BLOCK@[9; 12) | ||
338 | L_CURLY@[9; 10) | ||
339 | WHITESPACE@[10; 11) | ||
340 | R_CURLY@[11; 12) | ||
341 | "# | ||
342 | .trim() | ||
343 | ); | ||
344 | |||
345 | // With a raw string | ||
346 | let (analysis, range) = single_file_with_range( | ||
347 | r###"fn test() { | ||
348 | assert!(r<|>#" | ||
349 | fn foo() { | ||
350 | } | ||
351 | fn bar() { | ||
352 | }"<|>#, ""); | ||
353 | }"### | ||
354 | .trim(), | ||
355 | ); | ||
356 | let syn = analysis.syntax_tree(range.file_id, Some(range.range)); | ||
357 | assert_eq!( | ||
358 | syn.trim(), | ||
359 | r#" | ||
360 | SOURCE_FILE@[0; 25) | ||
361 | FN_DEF@[0; 12) | ||
362 | FN_KW@[0; 2) | ||
363 | WHITESPACE@[2; 3) | ||
364 | NAME@[3; 6) | ||
365 | IDENT@[3; 6) "foo" | ||
366 | PARAM_LIST@[6; 8) | ||
367 | L_PAREN@[6; 7) | ||
368 | R_PAREN@[7; 8) | ||
369 | WHITESPACE@[8; 9) | ||
370 | BLOCK@[9; 12) | ||
371 | L_CURLY@[9; 10) | ||
372 | WHITESPACE@[10; 11) | ||
373 | R_CURLY@[11; 12) | ||
374 | WHITESPACE@[12; 13) | ||
375 | FN_DEF@[13; 25) | ||
376 | FN_KW@[13; 15) | ||
377 | WHITESPACE@[15; 16) | ||
378 | NAME@[16; 19) | ||
379 | IDENT@[16; 19) "bar" | ||
380 | PARAM_LIST@[19; 21) | ||
381 | L_PAREN@[19; 20) | ||
382 | R_PAREN@[20; 21) | ||
383 | WHITESPACE@[21; 22) | ||
384 | BLOCK@[22; 25) | ||
385 | L_CURLY@[22; 23) | ||
386 | WHITESPACE@[23; 24) | ||
387 | R_CURLY@[24; 25) | ||
388 | |||
389 | "# | ||
390 | .trim() | ||
391 | ); | ||
392 | } | ||
diff --git a/crates/ra_ide_api/tests/test/snapshots/test__unresolved_module_diagnostic.snap b/crates/ra_ide_api/tests/test/snapshots/test__unresolved_module_diagnostic.snap deleted file mode 100644 index 5bb953892..000000000 --- a/crates/ra_ide_api/tests/test/snapshots/test__unresolved_module_diagnostic.snap +++ /dev/null | |||
@@ -1,28 +0,0 @@ | |||
1 | --- | ||
2 | created: "2019-01-22T14:45:01.486985900+00:00" | ||
3 | creator: [email protected] | ||
4 | expression: "&diagnostics" | ||
5 | source: "crates\\ra_ide_api\\tests\\test\\main.rs" | ||
6 | --- | ||
7 | [ | ||
8 | Diagnostic { | ||
9 | message: "unresolved module", | ||
10 | range: [0; 8), | ||
11 | fix: Some( | ||
12 | SourceChange { | ||
13 | label: "create module", | ||
14 | source_file_edits: [], | ||
15 | file_system_edits: [ | ||
16 | CreateFile { | ||
17 | source_root: SourceRootId( | ||
18 | 0 | ||
19 | ), | ||
20 | path: "foo.rs" | ||
21 | } | ||
22 | ], | ||
23 | cursor_position: None | ||
24 | } | ||
25 | ), | ||
26 | severity: Error | ||
27 | } | ||
28 | ] | ||
diff --git a/crates/ra_ide_api_light/Cargo.toml b/crates/ra_ide_api_light/Cargo.toml deleted file mode 100644 index 4e69f5325..000000000 --- a/crates/ra_ide_api_light/Cargo.toml +++ /dev/null | |||
@@ -1,26 +0,0 @@ | |||
1 | [package] | ||
2 | edition = "2018" | ||
3 | name = "ra_ide_api_light" | ||
4 | version = "0.1.0" | ||
5 | authors = ["rust-analyzer developers"] | ||
6 | publish = false | ||
7 | |||
8 | [dependencies] | ||
9 | itertools = "0.8.0" | ||
10 | superslice = "1.0.0" | ||
11 | join_to_string = "0.1.1" | ||
12 | rustc-hash = "1.0" | ||
13 | |||
14 | ra_syntax = { path = "../ra_syntax" } | ||
15 | ra_text_edit = { path = "../ra_text_edit" } | ||
16 | ra_fmt = { path = "../ra_fmt" } | ||
17 | |||
18 | [dev-dependencies] | ||
19 | test_utils = { path = "../test_utils" } | ||
20 | insta = "0.7.0" | ||
21 | |||
22 | [dev-dependencies.proptest] | ||
23 | version = "0.9.0" | ||
24 | # Disable `fork` feature to allow compiling on webassembly | ||
25 | default-features = false | ||
26 | features = ["std", "bit-set", "break-dead-code"] | ||
diff --git a/crates/ra_ide_api_light/src/lib.rs b/crates/ra_ide_api_light/src/lib.rs deleted file mode 100644 index df7f144b6..000000000 --- a/crates/ra_ide_api_light/src/lib.rs +++ /dev/null | |||
@@ -1,12 +0,0 @@ | |||
1 | //! This crate provides those IDE features which use only a single file. | ||
2 | //! | ||
3 | //! This usually means functions which take syntax tree as an input and produce | ||
4 | //! an edit or some auxiliary info. | ||
5 | |||
6 | mod structure; | ||
7 | |||
8 | use ra_syntax::TextRange; | ||
9 | |||
10 | pub use crate::{ | ||
11 | structure::{file_structure, StructureNode}, | ||
12 | }; | ||
diff --git a/crates/ra_parser/src/grammar.rs b/crates/ra_parser/src/grammar.rs index e428faffb..b2ffeff8c 100644 --- a/crates/ra_parser/src/grammar.rs +++ b/crates/ra_parser/src/grammar.rs | |||
@@ -184,6 +184,10 @@ fn name_ref(p: &mut Parser) { | |||
184 | let m = p.start(); | 184 | let m = p.start(); |
185 | p.bump(); | 185 | p.bump(); |
186 | m.complete(p, NAME_REF); | 186 | m.complete(p, NAME_REF); |
187 | } else if p.at(SELF_KW) { | ||
188 | let m = p.start(); | ||
189 | p.bump(); | ||
190 | m.complete(p, SELF_KW); | ||
187 | } else { | 191 | } else { |
188 | p.err_and_bump("expected identifier"); | 192 | p.err_and_bump("expected identifier"); |
189 | } | 193 | } |
diff --git a/crates/ra_syntax/src/ast/generated.rs b/crates/ra_syntax/src/ast/generated.rs index 54b72f8c5..47a37e4d1 100644 --- a/crates/ra_syntax/src/ast/generated.rs +++ b/crates/ra_syntax/src/ast/generated.rs | |||
@@ -4061,7 +4061,11 @@ impl ast::NameOwner for TraitDef {} | |||
4061 | impl ast::AttrsOwner for TraitDef {} | 4061 | impl ast::AttrsOwner for TraitDef {} |
4062 | impl ast::DocCommentsOwner for TraitDef {} | 4062 | impl ast::DocCommentsOwner for TraitDef {} |
4063 | impl ast::TypeParamsOwner for TraitDef {} | 4063 | impl ast::TypeParamsOwner for TraitDef {} |
4064 | impl TraitDef {} | 4064 | impl TraitDef { |
4065 | pub fn item_list(&self) -> Option<&ItemList> { | ||
4066 | super::child_opt(self) | ||
4067 | } | ||
4068 | } | ||
4065 | 4069 | ||
4066 | // TrueKw | 4070 | // TrueKw |
4067 | #[derive(Debug, PartialEq, Eq, Hash)] | 4071 | #[derive(Debug, PartialEq, Eq, Hash)] |
diff --git a/crates/ra_syntax/src/grammar.ron b/crates/ra_syntax/src/grammar.ron index 4f8e19bd0..ad6d74162 100644 --- a/crates/ra_syntax/src/grammar.ron +++ b/crates/ra_syntax/src/grammar.ron | |||
@@ -292,7 +292,10 @@ Grammar( | |||
292 | ], options: [["variant_list", "EnumVariantList"]] ), | 292 | ], options: [["variant_list", "EnumVariantList"]] ), |
293 | "EnumVariantList": ( collections: [["variants", "EnumVariant"]] ), | 293 | "EnumVariantList": ( collections: [["variants", "EnumVariant"]] ), |
294 | "EnumVariant": ( traits: ["NameOwner", "DocCommentsOwner", "AttrsOwner"], options: ["Expr"] ), | 294 | "EnumVariant": ( traits: ["NameOwner", "DocCommentsOwner", "AttrsOwner"], options: ["Expr"] ), |
295 | "TraitDef": ( traits: ["VisibilityOwner", "NameOwner", "AttrsOwner", "DocCommentsOwner", "TypeParamsOwner"] ), | 295 | "TraitDef": ( |
296 | traits: ["VisibilityOwner", "NameOwner", "AttrsOwner", "DocCommentsOwner", "TypeParamsOwner"], | ||
297 | options: ["ItemList"] | ||
298 | ), | ||
296 | "Module": ( | 299 | "Module": ( |
297 | traits: ["VisibilityOwner", "NameOwner", "AttrsOwner", "DocCommentsOwner" ], | 300 | traits: ["VisibilityOwner", "NameOwner", "AttrsOwner", "DocCommentsOwner" ], |
298 | options: [ "ItemList" ] | 301 | options: [ "ItemList" ] |
diff --git a/crates/ra_syntax/src/ptr.rs b/crates/ra_syntax/src/ptr.rs index aae590cb6..d8de1c4c1 100644 --- a/crates/ra_syntax/src/ptr.rs +++ b/crates/ra_syntax/src/ptr.rs | |||
@@ -64,6 +64,12 @@ impl<N: AstNode> AstPtr<N> { | |||
64 | } | 64 | } |
65 | } | 65 | } |
66 | 66 | ||
67 | impl<N: AstNode> From<AstPtr<N>> for SyntaxNodePtr { | ||
68 | fn from(ptr: AstPtr<N>) -> SyntaxNodePtr { | ||
69 | ptr.raw | ||
70 | } | ||
71 | } | ||
72 | |||
67 | #[test] | 73 | #[test] |
68 | fn test_local_syntax_ptr() { | 74 | fn test_local_syntax_ptr() { |
69 | use crate::{ast, AstNode}; | 75 | use crate::{ast, AstNode}; |
diff --git a/crates/ra_syntax/tests/data/parser/ok/0007_extern_crate.rs b/crates/ra_syntax/tests/data/parser/ok/0007_extern_crate.rs index 3ce336676..ab81a608c 100644 --- a/crates/ra_syntax/tests/data/parser/ok/0007_extern_crate.rs +++ b/crates/ra_syntax/tests/data/parser/ok/0007_extern_crate.rs | |||
@@ -1,2 +1,3 @@ | |||
1 | extern crate foo; | 1 | extern crate foo; |
2 | extern crate foo as bar; | 2 | extern crate foo as bar; |
3 | extern crate self as baz; | ||
diff --git a/crates/ra_syntax/tests/data/parser/ok/0007_extern_crate.txt b/crates/ra_syntax/tests/data/parser/ok/0007_extern_crate.txt index 5558d952e..0176260c1 100644 --- a/crates/ra_syntax/tests/data/parser/ok/0007_extern_crate.txt +++ b/crates/ra_syntax/tests/data/parser/ok/0007_extern_crate.txt | |||
@@ -1,4 +1,4 @@ | |||
1 | SOURCE_FILE@[0; 43) | 1 | SOURCE_FILE@[0; 69) |
2 | EXTERN_CRATE_ITEM@[0; 17) | 2 | EXTERN_CRATE_ITEM@[0; 17) |
3 | EXTERN_KW@[0; 6) | 3 | EXTERN_KW@[0; 6) |
4 | WHITESPACE@[6; 7) | 4 | WHITESPACE@[6; 7) |
@@ -23,3 +23,18 @@ SOURCE_FILE@[0; 43) | |||
23 | IDENT@[38; 41) "bar" | 23 | IDENT@[38; 41) "bar" |
24 | SEMI@[41; 42) | 24 | SEMI@[41; 42) |
25 | WHITESPACE@[42; 43) | 25 | WHITESPACE@[42; 43) |
26 | EXTERN_CRATE_ITEM@[43; 68) | ||
27 | EXTERN_KW@[43; 49) | ||
28 | WHITESPACE@[49; 50) | ||
29 | CRATE_KW@[50; 55) | ||
30 | WHITESPACE@[55; 56) | ||
31 | SELF_KW@[56; 60) | ||
32 | SELF_KW@[56; 60) | ||
33 | WHITESPACE@[60; 61) | ||
34 | ALIAS@[61; 67) | ||
35 | AS_KW@[61; 63) | ||
36 | WHITESPACE@[63; 64) | ||
37 | NAME@[64; 67) | ||
38 | IDENT@[64; 67) "baz" | ||
39 | SEMI@[67; 68) | ||
40 | WHITESPACE@[68; 69) | ||
diff --git a/crates/tools/src/lib.rs b/crates/tools/src/lib.rs index 1e29c63d4..11b52ccb7 100644 --- a/crates/tools/src/lib.rs +++ b/crates/tools/src/lib.rs | |||
@@ -115,7 +115,11 @@ pub fn install_rustfmt() -> Result<()> { | |||
115 | } | 115 | } |
116 | 116 | ||
117 | pub fn install_format_hook() -> Result<()> { | 117 | pub fn install_format_hook() -> Result<()> { |
118 | let result_path = Path::new("./.git/hooks/pre-commit"); | 118 | let result_path = Path::new(if cfg!(windows) { |
119 | "./.git/hooks/pre-commit.exe" | ||
120 | } else { | ||
121 | "./.git/hooks/pre-commit" | ||
122 | }); | ||
119 | if !result_path.exists() { | 123 | if !result_path.exists() { |
120 | run("cargo build --package tools --bin pre-commit", ".")?; | 124 | run("cargo build --package tools --bin pre-commit", ".")?; |
121 | if cfg!(windows) { | 125 | if cfg!(windows) { |