diff options
author | Mikhail Rakhmanov <[email protected]> | 2020-06-03 19:10:54 +0100 |
---|---|---|
committer | Mikhail Rakhmanov <[email protected]> | 2020-06-03 19:10:54 +0100 |
commit | eefa10bc6bff3624ddd0bbb6bc89d8beb4bed186 (patch) | |
tree | 15c38c2993c52f4065d338090ca9185cc1fcd3da /crates | |
parent | a9d567584857b1be4ca8eaa5ef2c7d85f7b2845e (diff) | |
parent | 794f6da821c5d6e2490b996baffe162e4753262d (diff) |
Merge branch 'master' into assists_extract_enum
Diffstat (limited to 'crates')
118 files changed, 3844 insertions, 1320 deletions
diff --git a/crates/ra_assists/src/handlers/add_from_impl_for_enum.rs b/crates/ra_assists/src/handlers/add_from_impl_for_enum.rs index 6a675e812..776bddf91 100644 --- a/crates/ra_assists/src/handlers/add_from_impl_for_enum.rs +++ b/crates/ra_assists/src/handlers/add_from_impl_for_enum.rs | |||
@@ -4,9 +4,9 @@ use test_utils::mark; | |||
4 | 4 | ||
5 | use crate::{utils::FamousDefs, AssistContext, AssistId, Assists}; | 5 | use crate::{utils::FamousDefs, AssistContext, AssistId, Assists}; |
6 | 6 | ||
7 | // Assist add_from_impl_for_enum | 7 | // Assist: add_from_impl_for_enum |
8 | // | 8 | // |
9 | // Adds a From impl for an enum variant with one tuple field | 9 | // Adds a From impl for an enum variant with one tuple field. |
10 | // | 10 | // |
11 | // ``` | 11 | // ``` |
12 | // enum A { <|>One(u32) } | 12 | // enum A { <|>One(u32) } |
diff --git a/crates/ra_assists/src/handlers/introduce_named_lifetime.rs b/crates/ra_assists/src/handlers/introduce_named_lifetime.rs new file mode 100644 index 000000000..beb5b7366 --- /dev/null +++ b/crates/ra_assists/src/handlers/introduce_named_lifetime.rs | |||
@@ -0,0 +1,303 @@ | |||
1 | use ra_syntax::{ | ||
2 | ast::{self, NameOwner, TypeAscriptionOwner, TypeParamsOwner}, | ||
3 | AstNode, SyntaxKind, TextRange, TextSize, | ||
4 | }; | ||
5 | use rustc_hash::FxHashSet; | ||
6 | |||
7 | use crate::{assist_context::AssistBuilder, AssistContext, AssistId, Assists}; | ||
8 | |||
9 | static ASSIST_NAME: &str = "introduce_named_lifetime"; | ||
10 | static ASSIST_LABEL: &str = "Introduce named lifetime"; | ||
11 | |||
12 | // Assist: introduce_named_lifetime | ||
13 | // | ||
14 | // Change an anonymous lifetime to a named lifetime. | ||
15 | // | ||
16 | // ``` | ||
17 | // impl Cursor<'_<|>> { | ||
18 | // fn node(self) -> &SyntaxNode { | ||
19 | // match self { | ||
20 | // Cursor::Replace(node) | Cursor::Before(node) => node, | ||
21 | // } | ||
22 | // } | ||
23 | // } | ||
24 | // ``` | ||
25 | // -> | ||
26 | // ``` | ||
27 | // impl<'a> Cursor<'a> { | ||
28 | // fn node(self) -> &SyntaxNode { | ||
29 | // match self { | ||
30 | // Cursor::Replace(node) | Cursor::Before(node) => node, | ||
31 | // } | ||
32 | // } | ||
33 | // } | ||
34 | // ``` | ||
35 | // FIXME: How can we handle renaming any one of multiple anonymous lifetimes? | ||
36 | // FIXME: should also add support for the case fun(f: &Foo) -> &<|>Foo | ||
37 | pub(crate) fn introduce_named_lifetime(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { | ||
38 | let lifetime_token = ctx | ||
39 | .find_token_at_offset(SyntaxKind::LIFETIME) | ||
40 | .filter(|lifetime| lifetime.text() == "'_")?; | ||
41 | if let Some(fn_def) = lifetime_token.ancestors().find_map(ast::FnDef::cast) { | ||
42 | generate_fn_def_assist(acc, &fn_def, lifetime_token.text_range()) | ||
43 | } else if let Some(impl_def) = lifetime_token.ancestors().find_map(ast::ImplDef::cast) { | ||
44 | // only allow naming the last anonymous lifetime | ||
45 | lifetime_token.next_token().filter(|tok| tok.kind() == SyntaxKind::R_ANGLE)?; | ||
46 | generate_impl_def_assist(acc, &impl_def, lifetime_token.text_range()) | ||
47 | } else { | ||
48 | None | ||
49 | } | ||
50 | } | ||
51 | |||
52 | /// Generate the assist for the fn def case | ||
53 | fn generate_fn_def_assist( | ||
54 | acc: &mut Assists, | ||
55 | fn_def: &ast::FnDef, | ||
56 | lifetime_loc: TextRange, | ||
57 | ) -> Option<()> { | ||
58 | let param_list: ast::ParamList = fn_def.param_list()?; | ||
59 | let new_lifetime_param = generate_unique_lifetime_param_name(&fn_def.type_param_list())?; | ||
60 | let end_of_fn_ident = fn_def.name()?.ident_token()?.text_range().end(); | ||
61 | let self_param = | ||
62 | // use the self if it's a reference and has no explicit lifetime | ||
63 | param_list.self_param().filter(|p| p.lifetime_token().is_none() && p.amp_token().is_some()); | ||
64 | // compute the location which implicitly has the same lifetime as the anonymous lifetime | ||
65 | let loc_needing_lifetime = if let Some(self_param) = self_param { | ||
66 | // if we have a self reference, use that | ||
67 | Some(self_param.self_token()?.text_range().start()) | ||
68 | } else { | ||
69 | // otherwise, if there's a single reference parameter without a named liftime, use that | ||
70 | let fn_params_without_lifetime: Vec<_> = param_list | ||
71 | .params() | ||
72 | .filter_map(|param| match param.ascribed_type() { | ||
73 | Some(ast::TypeRef::ReferenceType(ascribed_type)) | ||
74 | if ascribed_type.lifetime_token() == None => | ||
75 | { | ||
76 | Some(ascribed_type.amp_token()?.text_range().end()) | ||
77 | } | ||
78 | _ => None, | ||
79 | }) | ||
80 | .collect(); | ||
81 | match fn_params_without_lifetime.len() { | ||
82 | 1 => Some(fn_params_without_lifetime.into_iter().nth(0)?), | ||
83 | 0 => None, | ||
84 | // multiple unnnamed is invalid. assist is not applicable | ||
85 | _ => return None, | ||
86 | } | ||
87 | }; | ||
88 | acc.add(AssistId(ASSIST_NAME), ASSIST_LABEL, lifetime_loc, |builder| { | ||
89 | add_lifetime_param(fn_def, builder, end_of_fn_ident, new_lifetime_param); | ||
90 | builder.replace(lifetime_loc, format!("'{}", new_lifetime_param)); | ||
91 | loc_needing_lifetime.map(|loc| builder.insert(loc, format!("'{} ", new_lifetime_param))); | ||
92 | }) | ||
93 | } | ||
94 | |||
95 | /// Generate the assist for the impl def case | ||
96 | fn generate_impl_def_assist( | ||
97 | acc: &mut Assists, | ||
98 | impl_def: &ast::ImplDef, | ||
99 | lifetime_loc: TextRange, | ||
100 | ) -> Option<()> { | ||
101 | let new_lifetime_param = generate_unique_lifetime_param_name(&impl_def.type_param_list())?; | ||
102 | let end_of_impl_kw = impl_def.impl_token()?.text_range().end(); | ||
103 | acc.add(AssistId(ASSIST_NAME), ASSIST_LABEL, lifetime_loc, |builder| { | ||
104 | add_lifetime_param(impl_def, builder, end_of_impl_kw, new_lifetime_param); | ||
105 | builder.replace(lifetime_loc, format!("'{}", new_lifetime_param)); | ||
106 | }) | ||
107 | } | ||
108 | |||
109 | /// Given a type parameter list, generate a unique lifetime parameter name | ||
110 | /// which is not in the list | ||
111 | fn generate_unique_lifetime_param_name( | ||
112 | existing_type_param_list: &Option<ast::TypeParamList>, | ||
113 | ) -> Option<char> { | ||
114 | match existing_type_param_list { | ||
115 | Some(type_params) => { | ||
116 | let used_lifetime_params: FxHashSet<_> = type_params | ||
117 | .lifetime_params() | ||
118 | .map(|p| p.syntax().text().to_string()[1..].to_owned()) | ||
119 | .collect(); | ||
120 | (b'a'..=b'z').map(char::from).find(|c| !used_lifetime_params.contains(&c.to_string())) | ||
121 | } | ||
122 | None => Some('a'), | ||
123 | } | ||
124 | } | ||
125 | |||
126 | /// Add the lifetime param to `builder`. If there are type parameters in `type_params_owner`, add it to the end. Otherwise | ||
127 | /// add new type params brackets with the lifetime parameter at `new_type_params_loc`. | ||
128 | fn add_lifetime_param<TypeParamsOwner: ast::TypeParamsOwner>( | ||
129 | type_params_owner: &TypeParamsOwner, | ||
130 | builder: &mut AssistBuilder, | ||
131 | new_type_params_loc: TextSize, | ||
132 | new_lifetime_param: char, | ||
133 | ) { | ||
134 | match type_params_owner.type_param_list() { | ||
135 | // add the new lifetime parameter to an existing type param list | ||
136 | Some(type_params) => { | ||
137 | builder.insert( | ||
138 | (u32::from(type_params.syntax().text_range().end()) - 1).into(), | ||
139 | format!(", '{}", new_lifetime_param), | ||
140 | ); | ||
141 | } | ||
142 | // create a new type param list containing only the new lifetime parameter | ||
143 | None => { | ||
144 | builder.insert(new_type_params_loc, format!("<'{}>", new_lifetime_param)); | ||
145 | } | ||
146 | } | ||
147 | } | ||
148 | |||
149 | #[cfg(test)] | ||
150 | mod tests { | ||
151 | use super::*; | ||
152 | use crate::tests::{check_assist, check_assist_not_applicable}; | ||
153 | |||
154 | #[test] | ||
155 | fn test_example_case() { | ||
156 | check_assist( | ||
157 | introduce_named_lifetime, | ||
158 | r#"impl Cursor<'_<|>> { | ||
159 | fn node(self) -> &SyntaxNode { | ||
160 | match self { | ||
161 | Cursor::Replace(node) | Cursor::Before(node) => node, | ||
162 | } | ||
163 | } | ||
164 | }"#, | ||
165 | r#"impl<'a> Cursor<'a> { | ||
166 | fn node(self) -> &SyntaxNode { | ||
167 | match self { | ||
168 | Cursor::Replace(node) | Cursor::Before(node) => node, | ||
169 | } | ||
170 | } | ||
171 | }"#, | ||
172 | ); | ||
173 | } | ||
174 | |||
175 | #[test] | ||
176 | fn test_example_case_simplified() { | ||
177 | check_assist( | ||
178 | introduce_named_lifetime, | ||
179 | r#"impl Cursor<'_<|>> {"#, | ||
180 | r#"impl<'a> Cursor<'a> {"#, | ||
181 | ); | ||
182 | } | ||
183 | |||
184 | #[test] | ||
185 | fn test_example_case_cursor_after_tick() { | ||
186 | check_assist( | ||
187 | introduce_named_lifetime, | ||
188 | r#"impl Cursor<'<|>_> {"#, | ||
189 | r#"impl<'a> Cursor<'a> {"#, | ||
190 | ); | ||
191 | } | ||
192 | |||
193 | #[test] | ||
194 | fn test_example_case_cursor_before_tick() { | ||
195 | check_assist( | ||
196 | introduce_named_lifetime, | ||
197 | r#"impl Cursor<<|>'_> {"#, | ||
198 | r#"impl<'a> Cursor<'a> {"#, | ||
199 | ); | ||
200 | } | ||
201 | |||
202 | #[test] | ||
203 | fn test_not_applicable_cursor_position() { | ||
204 | check_assist_not_applicable(introduce_named_lifetime, r#"impl Cursor<'_><|> {"#); | ||
205 | check_assist_not_applicable(introduce_named_lifetime, r#"impl Cursor<|><'_> {"#); | ||
206 | } | ||
207 | |||
208 | #[test] | ||
209 | fn test_not_applicable_lifetime_already_name() { | ||
210 | check_assist_not_applicable(introduce_named_lifetime, r#"impl Cursor<'a<|>> {"#); | ||
211 | check_assist_not_applicable(introduce_named_lifetime, r#"fn my_fun<'a>() -> X<'a<|>>"#); | ||
212 | } | ||
213 | |||
214 | #[test] | ||
215 | fn test_with_type_parameter() { | ||
216 | check_assist( | ||
217 | introduce_named_lifetime, | ||
218 | r#"impl<T> Cursor<T, '_<|>>"#, | ||
219 | r#"impl<T, 'a> Cursor<T, 'a>"#, | ||
220 | ); | ||
221 | } | ||
222 | |||
223 | #[test] | ||
224 | fn test_with_existing_lifetime_name_conflict() { | ||
225 | check_assist( | ||
226 | introduce_named_lifetime, | ||
227 | r#"impl<'a, 'b> Cursor<'a, 'b, '_<|>>"#, | ||
228 | r#"impl<'a, 'b, 'c> Cursor<'a, 'b, 'c>"#, | ||
229 | ); | ||
230 | } | ||
231 | |||
232 | #[test] | ||
233 | fn test_function_return_value_anon_lifetime_param() { | ||
234 | check_assist( | ||
235 | introduce_named_lifetime, | ||
236 | r#"fn my_fun() -> X<'_<|>>"#, | ||
237 | r#"fn my_fun<'a>() -> X<'a>"#, | ||
238 | ); | ||
239 | } | ||
240 | |||
241 | #[test] | ||
242 | fn test_function_return_value_anon_reference_lifetime() { | ||
243 | check_assist( | ||
244 | introduce_named_lifetime, | ||
245 | r#"fn my_fun() -> &'_<|> X"#, | ||
246 | r#"fn my_fun<'a>() -> &'a X"#, | ||
247 | ); | ||
248 | } | ||
249 | |||
250 | #[test] | ||
251 | fn test_function_param_anon_lifetime() { | ||
252 | check_assist( | ||
253 | introduce_named_lifetime, | ||
254 | r#"fn my_fun(x: X<'_<|>>)"#, | ||
255 | r#"fn my_fun<'a>(x: X<'a>)"#, | ||
256 | ); | ||
257 | } | ||
258 | |||
259 | #[test] | ||
260 | fn test_function_add_lifetime_to_params() { | ||
261 | check_assist( | ||
262 | introduce_named_lifetime, | ||
263 | r#"fn my_fun(f: &Foo) -> X<'_<|>>"#, | ||
264 | r#"fn my_fun<'a>(f: &'a Foo) -> X<'a>"#, | ||
265 | ); | ||
266 | } | ||
267 | |||
268 | #[test] | ||
269 | fn test_function_add_lifetime_to_params_in_presence_of_other_lifetime() { | ||
270 | check_assist( | ||
271 | introduce_named_lifetime, | ||
272 | r#"fn my_fun<'other>(f: &Foo, b: &'other Bar) -> X<'_<|>>"#, | ||
273 | r#"fn my_fun<'other, 'a>(f: &'a Foo, b: &'other Bar) -> X<'a>"#, | ||
274 | ); | ||
275 | } | ||
276 | |||
277 | #[test] | ||
278 | fn test_function_not_applicable_without_self_and_multiple_unnamed_param_lifetimes() { | ||
279 | // this is not permitted under lifetime elision rules | ||
280 | check_assist_not_applicable( | ||
281 | introduce_named_lifetime, | ||
282 | r#"fn my_fun(f: &Foo, b: &Bar) -> X<'_<|>>"#, | ||
283 | ); | ||
284 | } | ||
285 | |||
286 | #[test] | ||
287 | fn test_function_add_lifetime_to_self_ref_param() { | ||
288 | check_assist( | ||
289 | introduce_named_lifetime, | ||
290 | r#"fn my_fun<'other>(&self, f: &Foo, b: &'other Bar) -> X<'_<|>>"#, | ||
291 | r#"fn my_fun<'other, 'a>(&'a self, f: &Foo, b: &'other Bar) -> X<'a>"#, | ||
292 | ); | ||
293 | } | ||
294 | |||
295 | #[test] | ||
296 | fn test_function_add_lifetime_to_param_with_non_ref_self() { | ||
297 | check_assist( | ||
298 | introduce_named_lifetime, | ||
299 | r#"fn my_fun<'other>(self, f: &Foo, b: &'other Bar) -> X<'_<|>>"#, | ||
300 | r#"fn my_fun<'other, 'a>(self, f: &'a Foo, b: &'other Bar) -> X<'a>"#, | ||
301 | ); | ||
302 | } | ||
303 | } | ||
diff --git a/crates/ra_assists/src/handlers/reorder_fields.rs b/crates/ra_assists/src/handlers/reorder_fields.rs index 30229edc2..897da2832 100644 --- a/crates/ra_assists/src/handlers/reorder_fields.rs +++ b/crates/ra_assists/src/handlers/reorder_fields.rs | |||
@@ -23,7 +23,7 @@ use crate::{AssistContext, AssistId, Assists}; | |||
23 | // ``` | 23 | // ``` |
24 | // | 24 | // |
25 | pub(crate) fn reorder_fields(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { | 25 | pub(crate) fn reorder_fields(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { |
26 | reorder::<ast::RecordLit>(acc, ctx.clone()).or_else(|| reorder::<ast::RecordPat>(acc, ctx)) | 26 | reorder::<ast::RecordLit>(acc, ctx).or_else(|| reorder::<ast::RecordPat>(acc, ctx)) |
27 | } | 27 | } |
28 | 28 | ||
29 | fn reorder<R: AstNode>(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { | 29 | fn reorder<R: AstNode>(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { |
diff --git a/crates/ra_assists/src/lib.rs b/crates/ra_assists/src/lib.rs index 9933f7a50..88ce9b62e 100644 --- a/crates/ra_assists/src/lib.rs +++ b/crates/ra_assists/src/lib.rs | |||
@@ -122,6 +122,7 @@ mod handlers { | |||
122 | mod flip_comma; | 122 | mod flip_comma; |
123 | mod flip_trait_bound; | 123 | mod flip_trait_bound; |
124 | mod inline_local_variable; | 124 | mod inline_local_variable; |
125 | mod introduce_named_lifetime; | ||
125 | mod introduce_variable; | 126 | mod introduce_variable; |
126 | mod invert_if; | 127 | mod invert_if; |
127 | mod merge_imports; | 128 | mod merge_imports; |
@@ -162,6 +163,7 @@ mod handlers { | |||
162 | flip_comma::flip_comma, | 163 | flip_comma::flip_comma, |
163 | flip_trait_bound::flip_trait_bound, | 164 | flip_trait_bound::flip_trait_bound, |
164 | inline_local_variable::inline_local_variable, | 165 | inline_local_variable::inline_local_variable, |
166 | introduce_named_lifetime::introduce_named_lifetime, | ||
165 | introduce_variable::introduce_variable, | 167 | introduce_variable::introduce_variable, |
166 | invert_if::invert_if, | 168 | invert_if::invert_if, |
167 | merge_imports::merge_imports, | 169 | merge_imports::merge_imports, |
diff --git a/crates/ra_assists/src/tests/generated.rs b/crates/ra_assists/src/tests/generated.rs index 250e56a69..d17504529 100644 --- a/crates/ra_assists/src/tests/generated.rs +++ b/crates/ra_assists/src/tests/generated.rs | |||
@@ -59,6 +59,25 @@ fn main() { | |||
59 | } | 59 | } |
60 | 60 | ||
61 | #[test] | 61 | #[test] |
62 | fn doctest_add_from_impl_for_enum() { | ||
63 | check_doc_test( | ||
64 | "add_from_impl_for_enum", | ||
65 | r#####" | ||
66 | enum A { <|>One(u32) } | ||
67 | "#####, | ||
68 | r#####" | ||
69 | enum A { One(u32) } | ||
70 | |||
71 | impl From<u32> for A { | ||
72 | fn from(v: u32) -> Self { | ||
73 | A::One(v) | ||
74 | } | ||
75 | } | ||
76 | "#####, | ||
77 | ) | ||
78 | } | ||
79 | |||
80 | #[test] | ||
62 | fn doctest_add_function() { | 81 | fn doctest_add_function() { |
63 | check_doc_test( | 82 | check_doc_test( |
64 | "add_function", | 83 | "add_function", |
@@ -433,6 +452,31 @@ fn main() { | |||
433 | } | 452 | } |
434 | 453 | ||
435 | #[test] | 454 | #[test] |
455 | fn doctest_introduce_named_lifetime() { | ||
456 | check_doc_test( | ||
457 | "introduce_named_lifetime", | ||
458 | r#####" | ||
459 | impl Cursor<'_<|>> { | ||
460 | fn node(self) -> &SyntaxNode { | ||
461 | match self { | ||
462 | Cursor::Replace(node) | Cursor::Before(node) => node, | ||
463 | } | ||
464 | } | ||
465 | } | ||
466 | "#####, | ||
467 | r#####" | ||
468 | impl<'a> Cursor<'a> { | ||
469 | fn node(self) -> &SyntaxNode { | ||
470 | match self { | ||
471 | Cursor::Replace(node) | Cursor::Before(node) => node, | ||
472 | } | ||
473 | } | ||
474 | } | ||
475 | "#####, | ||
476 | ) | ||
477 | } | ||
478 | |||
479 | #[test] | ||
436 | fn doctest_introduce_variable() { | 480 | fn doctest_introduce_variable() { |
437 | check_doc_test( | 481 | check_doc_test( |
438 | "introduce_variable", | 482 | "introduce_variable", |
diff --git a/crates/ra_cfg/src/cfg_expr.rs b/crates/ra_cfg/src/cfg_expr.rs index 39d71851c..85b100c6a 100644 --- a/crates/ra_cfg/src/cfg_expr.rs +++ b/crates/ra_cfg/src/cfg_expr.rs | |||
@@ -88,13 +88,17 @@ fn next_cfg_expr(it: &mut SliceIter<tt::TokenTree>) -> Option<CfgExpr> { | |||
88 | mod tests { | 88 | mod tests { |
89 | use super::*; | 89 | use super::*; |
90 | 90 | ||
91 | use mbe::ast_to_token_tree; | 91 | use mbe::{ast_to_token_tree, TokenMap}; |
92 | use ra_syntax::ast::{self, AstNode}; | 92 | use ra_syntax::ast::{self, AstNode}; |
93 | 93 | ||
94 | fn assert_parse_result(input: &str, expected: CfgExpr) { | 94 | fn get_token_tree_generated(input: &str) -> (tt::Subtree, TokenMap) { |
95 | let source_file = ast::SourceFile::parse(input).ok().unwrap(); | 95 | let source_file = ast::SourceFile::parse(input).ok().unwrap(); |
96 | let tt = source_file.syntax().descendants().find_map(ast::TokenTree::cast).unwrap(); | 96 | let tt = source_file.syntax().descendants().find_map(ast::TokenTree::cast).unwrap(); |
97 | let (tt, _) = ast_to_token_tree(&tt).unwrap(); | 97 | ast_to_token_tree(&tt).unwrap() |
98 | } | ||
99 | |||
100 | fn assert_parse_result(input: &str, expected: CfgExpr) { | ||
101 | let (tt, _) = get_token_tree_generated(input); | ||
98 | assert_eq!(parse_cfg(&tt), expected); | 102 | assert_eq!(parse_cfg(&tt), expected); |
99 | } | 103 | } |
100 | 104 | ||
diff --git a/crates/ra_db/src/fixture.rs b/crates/ra_db/src/fixture.rs index f8f767091..482a2f3e6 100644 --- a/crates/ra_db/src/fixture.rs +++ b/crates/ra_db/src/fixture.rs | |||
@@ -63,7 +63,7 @@ use std::sync::Arc; | |||
63 | 63 | ||
64 | use ra_cfg::CfgOptions; | 64 | use ra_cfg::CfgOptions; |
65 | use rustc_hash::FxHashMap; | 65 | use rustc_hash::FxHashMap; |
66 | use test_utils::{extract_offset, parse_fixture, parse_single_fixture, CURSOR_MARKER}; | 66 | use test_utils::{extract_offset, parse_fixture, parse_single_fixture, FixtureMeta, CURSOR_MARKER}; |
67 | 67 | ||
68 | use crate::{ | 68 | use crate::{ |
69 | input::CrateName, CrateGraph, CrateId, Edition, Env, FileId, FilePosition, RelativePathBuf, | 69 | input::CrateName, CrateGraph, CrateId, Edition, Env, FileId, FilePosition, RelativePathBuf, |
@@ -113,7 +113,7 @@ fn with_single_file(db: &mut dyn SourceDatabaseExt, ra_fixture: &str) -> FileId | |||
113 | let fixture = parse_single_fixture(ra_fixture); | 113 | let fixture = parse_single_fixture(ra_fixture); |
114 | 114 | ||
115 | let crate_graph = if let Some(entry) = fixture { | 115 | let crate_graph = if let Some(entry) = fixture { |
116 | let meta = match parse_meta(&entry.meta) { | 116 | let meta = match ParsedMeta::from(&entry.meta) { |
117 | ParsedMeta::File(it) => it, | 117 | ParsedMeta::File(it) => it, |
118 | _ => panic!("with_single_file only support file meta"), | 118 | _ => panic!("with_single_file only support file meta"), |
119 | }; | 119 | }; |
@@ -170,7 +170,7 @@ fn with_files(db: &mut dyn SourceDatabaseExt, fixture: &str) -> Option<FilePosit | |||
170 | let mut file_position = None; | 170 | let mut file_position = None; |
171 | 171 | ||
172 | for entry in fixture.iter() { | 172 | for entry in fixture.iter() { |
173 | let meta = match parse_meta(&entry.meta) { | 173 | let meta = match ParsedMeta::from(&entry.meta) { |
174 | ParsedMeta::Root { path } => { | 174 | ParsedMeta::Root { path } => { |
175 | let source_root = std::mem::replace(&mut source_root, SourceRoot::new_local()); | 175 | let source_root = std::mem::replace(&mut source_root, SourceRoot::new_local()); |
176 | db.set_source_root(source_root_id, Arc::new(source_root)); | 176 | db.set_source_root(source_root_id, Arc::new(source_root)); |
@@ -258,53 +258,25 @@ struct FileMeta { | |||
258 | env: Env, | 258 | env: Env, |
259 | } | 259 | } |
260 | 260 | ||
261 | //- /lib.rs crate:foo deps:bar,baz cfg:foo=a,bar=b env:OUTDIR=path/to,OTHER=foo) | 261 | impl From<&FixtureMeta> for ParsedMeta { |
262 | fn parse_meta(meta: &str) -> ParsedMeta { | 262 | fn from(meta: &FixtureMeta) -> Self { |
263 | let components = meta.split_ascii_whitespace().collect::<Vec<_>>(); | 263 | match meta { |
264 | 264 | FixtureMeta::Root { path } => { | |
265 | if components[0] == "root" { | 265 | // `Self::Root` causes a false warning: 'variant is never constructed: `Root` ' |
266 | let path: RelativePathBuf = components[1].into(); | 266 | // see https://github.com/rust-lang/rust/issues/69018 |
267 | assert!(path.starts_with("/") && path.ends_with("/")); | 267 | ParsedMeta::Root { path: path.to_owned() } |
268 | return ParsedMeta::Root { path }; | ||
269 | } | ||
270 | |||
271 | let path: RelativePathBuf = components[0].into(); | ||
272 | assert!(path.starts_with("/")); | ||
273 | |||
274 | let mut krate = None; | ||
275 | let mut deps = Vec::new(); | ||
276 | let mut edition = Edition::Edition2018; | ||
277 | let mut cfg = CfgOptions::default(); | ||
278 | let mut env = Env::default(); | ||
279 | for component in components[1..].iter() { | ||
280 | let (key, value) = split1(component, ':').unwrap(); | ||
281 | match key { | ||
282 | "crate" => krate = Some(value.to_string()), | ||
283 | "deps" => deps = value.split(',').map(|it| it.to_string()).collect(), | ||
284 | "edition" => edition = Edition::from_str(&value).unwrap(), | ||
285 | "cfg" => { | ||
286 | for key in value.split(',') { | ||
287 | match split1(key, '=') { | ||
288 | None => cfg.insert_atom(key.into()), | ||
289 | Some((k, v)) => cfg.insert_key_value(k.into(), v.into()), | ||
290 | } | ||
291 | } | ||
292 | } | ||
293 | "env" => { | ||
294 | for key in value.split(',') { | ||
295 | if let Some((k, v)) = split1(key, '=') { | ||
296 | env.set(k, v.into()); | ||
297 | } | ||
298 | } | ||
299 | } | 268 | } |
300 | _ => panic!("bad component: {:?}", component), | 269 | FixtureMeta::File(f) => Self::File(FileMeta { |
270 | path: f.path.to_owned(), | ||
271 | krate: f.crate_name.to_owned(), | ||
272 | deps: f.deps.to_owned(), | ||
273 | cfg: f.cfg.to_owned(), | ||
274 | edition: f | ||
275 | .edition | ||
276 | .as_ref() | ||
277 | .map_or(Edition::Edition2018, |v| Edition::from_str(&v).unwrap()), | ||
278 | env: Env::from(f.env.iter()), | ||
279 | }), | ||
301 | } | 280 | } |
302 | } | 281 | } |
303 | |||
304 | ParsedMeta::File(FileMeta { path, krate, deps, edition, cfg, env }) | ||
305 | } | ||
306 | |||
307 | fn split1(haystack: &str, delim: char) -> Option<(&str, &str)> { | ||
308 | let idx = haystack.find(delim)?; | ||
309 | Some((&haystack[..idx], &haystack[idx + delim.len_utf8()..])) | ||
310 | } | 282 | } |
diff --git a/crates/ra_db/src/input.rs b/crates/ra_db/src/input.rs index ab14e2d5e..4d2d3b48a 100644 --- a/crates/ra_db/src/input.rs +++ b/crates/ra_db/src/input.rs | |||
@@ -311,6 +311,21 @@ impl fmt::Display for Edition { | |||
311 | } | 311 | } |
312 | } | 312 | } |
313 | 313 | ||
314 | impl<'a, T> From<T> for Env | ||
315 | where | ||
316 | T: Iterator<Item = (&'a String, &'a String)>, | ||
317 | { | ||
318 | fn from(iter: T) -> Self { | ||
319 | let mut result = Self::default(); | ||
320 | |||
321 | for (k, v) in iter { | ||
322 | result.entries.insert(k.to_owned(), v.to_owned()); | ||
323 | } | ||
324 | |||
325 | result | ||
326 | } | ||
327 | } | ||
328 | |||
314 | impl Env { | 329 | impl Env { |
315 | pub fn set(&mut self, env: &str, value: String) { | 330 | pub fn set(&mut self, env: &str, value: String) { |
316 | self.entries.insert(env.to_owned(), value); | 331 | self.entries.insert(env.to_owned(), value); |
diff --git a/crates/ra_hir/src/code_model.rs b/crates/ra_hir/src/code_model.rs index 840cfdfc8..4a06f3bcd 100644 --- a/crates/ra_hir/src/code_model.rs +++ b/crates/ra_hir/src/code_model.rs | |||
@@ -532,7 +532,7 @@ impl Adt { | |||
532 | Some(self.module(db).krate()) | 532 | Some(self.module(db).krate()) |
533 | } | 533 | } |
534 | 534 | ||
535 | pub fn name(&self, db: &dyn HirDatabase) -> Name { | 535 | pub fn name(self, db: &dyn HirDatabase) -> Name { |
536 | match self { | 536 | match self { |
537 | Adt::Struct(s) => s.name(db), | 537 | Adt::Struct(s) => s.name(db), |
538 | Adt::Union(u) => u.name(db), | 538 | Adt::Union(u) => u.name(db), |
@@ -637,6 +637,10 @@ impl Function { | |||
637 | db.function_data(self.id).params.clone() | 637 | db.function_data(self.id).params.clone() |
638 | } | 638 | } |
639 | 639 | ||
640 | pub fn is_unsafe(self, db: &dyn HirDatabase) -> bool { | ||
641 | db.function_data(self.id).is_unsafe | ||
642 | } | ||
643 | |||
640 | pub fn diagnostics(self, db: &dyn HirDatabase, sink: &mut DiagnosticSink) { | 644 | pub fn diagnostics(self, db: &dyn HirDatabase, sink: &mut DiagnosticSink) { |
641 | let _p = profile("Function::diagnostics"); | 645 | let _p = profile("Function::diagnostics"); |
642 | let infer = db.infer(self.id.into()); | 646 | let infer = db.infer(self.id.into()); |
@@ -1018,15 +1022,15 @@ impl ImplDef { | |||
1018 | impls.lookup_impl_defs_for_trait(trait_.id).map(Self::from).collect() | 1022 | impls.lookup_impl_defs_for_trait(trait_.id).map(Self::from).collect() |
1019 | } | 1023 | } |
1020 | 1024 | ||
1021 | pub fn target_trait(&self, db: &dyn HirDatabase) -> Option<TypeRef> { | 1025 | pub fn target_trait(self, db: &dyn HirDatabase) -> Option<TypeRef> { |
1022 | db.impl_data(self.id).target_trait.clone() | 1026 | db.impl_data(self.id).target_trait.clone() |
1023 | } | 1027 | } |
1024 | 1028 | ||
1025 | pub fn target_type(&self, db: &dyn HirDatabase) -> TypeRef { | 1029 | pub fn target_type(self, db: &dyn HirDatabase) -> TypeRef { |
1026 | db.impl_data(self.id).target_type.clone() | 1030 | db.impl_data(self.id).target_type.clone() |
1027 | } | 1031 | } |
1028 | 1032 | ||
1029 | pub fn target_ty(&self, db: &dyn HirDatabase) -> Type { | 1033 | pub fn target_ty(self, db: &dyn HirDatabase) -> Type { |
1030 | let impl_data = db.impl_data(self.id); | 1034 | let impl_data = db.impl_data(self.id); |
1031 | let resolver = self.id.resolver(db.upcast()); | 1035 | let resolver = self.id.resolver(db.upcast()); |
1032 | let ctx = hir_ty::TyLoweringContext::new(db, &resolver); | 1036 | let ctx = hir_ty::TyLoweringContext::new(db, &resolver); |
@@ -1038,23 +1042,23 @@ impl ImplDef { | |||
1038 | } | 1042 | } |
1039 | } | 1043 | } |
1040 | 1044 | ||
1041 | pub fn items(&self, db: &dyn HirDatabase) -> Vec<AssocItem> { | 1045 | pub fn items(self, db: &dyn HirDatabase) -> Vec<AssocItem> { |
1042 | db.impl_data(self.id).items.iter().map(|it| (*it).into()).collect() | 1046 | db.impl_data(self.id).items.iter().map(|it| (*it).into()).collect() |
1043 | } | 1047 | } |
1044 | 1048 | ||
1045 | pub fn is_negative(&self, db: &dyn HirDatabase) -> bool { | 1049 | pub fn is_negative(self, db: &dyn HirDatabase) -> bool { |
1046 | db.impl_data(self.id).is_negative | 1050 | db.impl_data(self.id).is_negative |
1047 | } | 1051 | } |
1048 | 1052 | ||
1049 | pub fn module(&self, db: &dyn HirDatabase) -> Module { | 1053 | pub fn module(self, db: &dyn HirDatabase) -> Module { |
1050 | self.id.lookup(db.upcast()).container.module(db.upcast()).into() | 1054 | self.id.lookup(db.upcast()).container.module(db.upcast()).into() |
1051 | } | 1055 | } |
1052 | 1056 | ||
1053 | pub fn krate(&self, db: &dyn HirDatabase) -> Crate { | 1057 | pub fn krate(self, db: &dyn HirDatabase) -> Crate { |
1054 | Crate { id: self.module(db).id.krate } | 1058 | Crate { id: self.module(db).id.krate } |
1055 | } | 1059 | } |
1056 | 1060 | ||
1057 | pub fn is_builtin_derive(&self, db: &dyn HirDatabase) -> Option<InFile<ast::Attr>> { | 1061 | pub fn is_builtin_derive(self, db: &dyn HirDatabase) -> Option<InFile<ast::Attr>> { |
1058 | let src = self.source(db); | 1062 | let src = self.source(db); |
1059 | let item = src.file_id.is_builtin_derive(db.upcast())?; | 1063 | let item = src.file_id.is_builtin_derive(db.upcast())?; |
1060 | let hygenic = hir_expand::hygiene::Hygiene::new(db.upcast(), item.file_id); | 1064 | let hygenic = hir_expand::hygiene::Hygiene::new(db.upcast(), item.file_id); |
@@ -1190,6 +1194,10 @@ impl Type { | |||
1190 | ) | 1194 | ) |
1191 | } | 1195 | } |
1192 | 1196 | ||
1197 | pub fn is_raw_ptr(&self) -> bool { | ||
1198 | matches!(&self.ty.value, Ty::Apply(ApplicationTy { ctor: TypeCtor::RawPtr(..), .. })) | ||
1199 | } | ||
1200 | |||
1193 | pub fn contains_unknown(&self) -> bool { | 1201 | pub fn contains_unknown(&self) -> bool { |
1194 | return go(&self.ty.value); | 1202 | return go(&self.ty.value); |
1195 | 1203 | ||
@@ -1363,6 +1371,7 @@ impl HirDisplay for Type { | |||
1363 | } | 1371 | } |
1364 | 1372 | ||
1365 | /// For IDE only | 1373 | /// For IDE only |
1374 | #[derive(Debug)] | ||
1366 | pub enum ScopeDef { | 1375 | pub enum ScopeDef { |
1367 | ModuleDef(ModuleDef), | 1376 | ModuleDef(ModuleDef), |
1368 | MacroDef(MacroDef), | 1377 | MacroDef(MacroDef), |
diff --git a/crates/ra_hir/src/lib.rs b/crates/ra_hir/src/lib.rs index 4af0f046a..f4a6b0503 100644 --- a/crates/ra_hir/src/lib.rs +++ b/crates/ra_hir/src/lib.rs | |||
@@ -62,6 +62,7 @@ pub use crate::{ | |||
62 | 62 | ||
63 | pub use hir_def::{ | 63 | pub use hir_def::{ |
64 | adt::StructKind, | 64 | adt::StructKind, |
65 | attr::Attrs, | ||
65 | body::scope::ExprScopes, | 66 | body::scope::ExprScopes, |
66 | builtin_type::BuiltinType, | 67 | builtin_type::BuiltinType, |
67 | docs::Documentation, | 68 | docs::Documentation, |
diff --git a/crates/ra_hir_def/src/attr.rs b/crates/ra_hir_def/src/attr.rs index 576cd0c65..2eeba0572 100644 --- a/crates/ra_hir_def/src/attr.rs +++ b/crates/ra_hir_def/src/attr.rs | |||
@@ -81,18 +81,24 @@ impl Attrs { | |||
81 | } | 81 | } |
82 | } | 82 | } |
83 | 83 | ||
84 | fn from_attrs_owner(db: &dyn DefDatabase, owner: InFile<&dyn AttrsOwner>) -> Attrs { | 84 | pub fn from_attrs_owner(db: &dyn DefDatabase, owner: InFile<&dyn AttrsOwner>) -> Attrs { |
85 | let hygiene = Hygiene::new(db.upcast(), owner.file_id); | 85 | let hygiene = Hygiene::new(db.upcast(), owner.file_id); |
86 | Attrs::new(owner.value, &hygiene) | 86 | Attrs::new(owner.value, &hygiene) |
87 | } | 87 | } |
88 | 88 | ||
89 | pub(crate) fn new(owner: &dyn AttrsOwner, hygiene: &Hygiene) -> Attrs { | 89 | pub(crate) fn new(owner: &dyn AttrsOwner, hygiene: &Hygiene) -> Attrs { |
90 | let docs = ast::CommentIter::from_syntax_node(owner.syntax()).doc_comment_text().map( | ||
91 | |docs_text| Attr { | ||
92 | input: Some(AttrInput::Literal(SmolStr::new(docs_text))), | ||
93 | path: ModPath::from(hir_expand::name!(doc)), | ||
94 | }, | ||
95 | ); | ||
90 | let mut attrs = owner.attrs().peekable(); | 96 | let mut attrs = owner.attrs().peekable(); |
91 | let entries = if attrs.peek().is_none() { | 97 | let entries = if attrs.peek().is_none() { |
92 | // Avoid heap allocation | 98 | // Avoid heap allocation |
93 | None | 99 | None |
94 | } else { | 100 | } else { |
95 | Some(attrs.flat_map(|ast| Attr::from_src(ast, hygiene)).collect()) | 101 | Some(attrs.flat_map(|ast| Attr::from_src(ast, hygiene)).chain(docs).collect()) |
96 | }; | 102 | }; |
97 | Attrs { entries } | 103 | Attrs { entries } |
98 | } | 104 | } |
diff --git a/crates/ra_hir_def/src/body.rs b/crates/ra_hir_def/src/body.rs index f5a7305dc..273036cee 100644 --- a/crates/ra_hir_def/src/body.rs +++ b/crates/ra_hir_def/src/body.rs | |||
@@ -29,7 +29,7 @@ use crate::{ | |||
29 | AsMacroCall, DefWithBodyId, HasModule, Lookup, ModuleId, | 29 | AsMacroCall, DefWithBodyId, HasModule, Lookup, ModuleId, |
30 | }; | 30 | }; |
31 | 31 | ||
32 | /// A subset of Exander that only deals with cfg attributes. We only need it to | 32 | /// A subset of Expander that only deals with cfg attributes. We only need it to |
33 | /// avoid cyclic queries in crate def map during enum processing. | 33 | /// avoid cyclic queries in crate def map during enum processing. |
34 | pub(crate) struct CfgExpander { | 34 | pub(crate) struct CfgExpander { |
35 | cfg_options: CfgOptions, | 35 | cfg_options: CfgOptions, |
diff --git a/crates/ra_hir_def/src/body/lower.rs b/crates/ra_hir_def/src/body/lower.rs index e08d62dd6..f159f80af 100644 --- a/crates/ra_hir_def/src/body/lower.rs +++ b/crates/ra_hir_def/src/body/lower.rs | |||
@@ -28,7 +28,7 @@ use crate::{ | |||
28 | }, | 28 | }, |
29 | item_scope::BuiltinShadowMode, | 29 | item_scope::BuiltinShadowMode, |
30 | path::{GenericArgs, Path}, | 30 | path::{GenericArgs, Path}, |
31 | type_ref::{Mutability, TypeRef}, | 31 | type_ref::{Mutability, Rawness, TypeRef}, |
32 | AdtId, ConstLoc, ContainerId, DefWithBodyId, EnumLoc, FunctionLoc, Intern, ModuleDefId, | 32 | AdtId, ConstLoc, ContainerId, DefWithBodyId, EnumLoc, FunctionLoc, Intern, ModuleDefId, |
33 | StaticLoc, StructLoc, TraitLoc, TypeAliasLoc, UnionLoc, | 33 | StaticLoc, StructLoc, TraitLoc, TypeAliasLoc, UnionLoc, |
34 | }; | 34 | }; |
@@ -134,7 +134,7 @@ impl ExprCollector<'_> { | |||
134 | self.make_expr(expr, Err(SyntheticSyntax)) | 134 | self.make_expr(expr, Err(SyntheticSyntax)) |
135 | } | 135 | } |
136 | fn empty_block(&mut self) -> ExprId { | 136 | fn empty_block(&mut self) -> ExprId { |
137 | self.alloc_expr_desugared(Expr::Block { statements: Vec::new(), tail: None }) | 137 | self.alloc_expr_desugared(Expr::Block { statements: Vec::new(), tail: None, label: None }) |
138 | } | 138 | } |
139 | fn missing_expr(&mut self) -> ExprId { | 139 | fn missing_expr(&mut self) -> ExprId { |
140 | self.alloc_expr_desugared(Expr::Missing) | 140 | self.alloc_expr_desugared(Expr::Missing) |
@@ -215,7 +215,16 @@ impl ExprCollector<'_> { | |||
215 | ast::Expr::BlockExpr(e) => self.collect_block(e), | 215 | ast::Expr::BlockExpr(e) => self.collect_block(e), |
216 | ast::Expr::LoopExpr(e) => { | 216 | ast::Expr::LoopExpr(e) => { |
217 | let body = self.collect_block_opt(e.loop_body()); | 217 | let body = self.collect_block_opt(e.loop_body()); |
218 | self.alloc_expr(Expr::Loop { body }, syntax_ptr) | 218 | self.alloc_expr( |
219 | Expr::Loop { | ||
220 | body, | ||
221 | label: e | ||
222 | .label() | ||
223 | .and_then(|l| l.lifetime_token()) | ||
224 | .map(|l| Name::new_lifetime(&l)), | ||
225 | }, | ||
226 | syntax_ptr, | ||
227 | ) | ||
219 | } | 228 | } |
220 | ast::Expr::WhileExpr(e) => { | 229 | ast::Expr::WhileExpr(e) => { |
221 | let body = self.collect_block_opt(e.loop_body()); | 230 | let body = self.collect_block_opt(e.loop_body()); |
@@ -230,25 +239,56 @@ impl ExprCollector<'_> { | |||
230 | let pat = self.collect_pat(pat); | 239 | let pat = self.collect_pat(pat); |
231 | let match_expr = self.collect_expr_opt(condition.expr()); | 240 | let match_expr = self.collect_expr_opt(condition.expr()); |
232 | let placeholder_pat = self.missing_pat(); | 241 | let placeholder_pat = self.missing_pat(); |
233 | let break_ = self.alloc_expr_desugared(Expr::Break { expr: None }); | 242 | let break_ = |
243 | self.alloc_expr_desugared(Expr::Break { expr: None, label: None }); | ||
234 | let arms = vec![ | 244 | let arms = vec![ |
235 | MatchArm { pat, expr: body, guard: None }, | 245 | MatchArm { pat, expr: body, guard: None }, |
236 | MatchArm { pat: placeholder_pat, expr: break_, guard: None }, | 246 | MatchArm { pat: placeholder_pat, expr: break_, guard: None }, |
237 | ]; | 247 | ]; |
238 | let match_expr = | 248 | let match_expr = |
239 | self.alloc_expr_desugared(Expr::Match { expr: match_expr, arms }); | 249 | self.alloc_expr_desugared(Expr::Match { expr: match_expr, arms }); |
240 | return self.alloc_expr(Expr::Loop { body: match_expr }, syntax_ptr); | 250 | return self.alloc_expr( |
251 | Expr::Loop { | ||
252 | body: match_expr, | ||
253 | label: e | ||
254 | .label() | ||
255 | .and_then(|l| l.lifetime_token()) | ||
256 | .map(|l| Name::new_lifetime(&l)), | ||
257 | }, | ||
258 | syntax_ptr, | ||
259 | ); | ||
241 | } | 260 | } |
242 | }, | 261 | }, |
243 | }; | 262 | }; |
244 | 263 | ||
245 | self.alloc_expr(Expr::While { condition, body }, syntax_ptr) | 264 | self.alloc_expr( |
265 | Expr::While { | ||
266 | condition, | ||
267 | body, | ||
268 | label: e | ||
269 | .label() | ||
270 | .and_then(|l| l.lifetime_token()) | ||
271 | .map(|l| Name::new_lifetime(&l)), | ||
272 | }, | ||
273 | syntax_ptr, | ||
274 | ) | ||
246 | } | 275 | } |
247 | ast::Expr::ForExpr(e) => { | 276 | ast::Expr::ForExpr(e) => { |
248 | let iterable = self.collect_expr_opt(e.iterable()); | 277 | let iterable = self.collect_expr_opt(e.iterable()); |
249 | let pat = self.collect_pat_opt(e.pat()); | 278 | let pat = self.collect_pat_opt(e.pat()); |
250 | let body = self.collect_block_opt(e.loop_body()); | 279 | let body = self.collect_block_opt(e.loop_body()); |
251 | self.alloc_expr(Expr::For { iterable, pat, body }, syntax_ptr) | 280 | self.alloc_expr( |
281 | Expr::For { | ||
282 | iterable, | ||
283 | pat, | ||
284 | body, | ||
285 | label: e | ||
286 | .label() | ||
287 | .and_then(|l| l.lifetime_token()) | ||
288 | .map(|l| Name::new_lifetime(&l)), | ||
289 | }, | ||
290 | syntax_ptr, | ||
291 | ) | ||
252 | } | 292 | } |
253 | ast::Expr::CallExpr(e) => { | 293 | ast::Expr::CallExpr(e) => { |
254 | let callee = self.collect_expr_opt(e.expr()); | 294 | let callee = self.collect_expr_opt(e.expr()); |
@@ -301,13 +341,16 @@ impl ExprCollector<'_> { | |||
301 | .unwrap_or(Expr::Missing); | 341 | .unwrap_or(Expr::Missing); |
302 | self.alloc_expr(path, syntax_ptr) | 342 | self.alloc_expr(path, syntax_ptr) |
303 | } | 343 | } |
304 | ast::Expr::ContinueExpr(_e) => { | 344 | ast::Expr::ContinueExpr(e) => self.alloc_expr( |
305 | // FIXME: labels | 345 | Expr::Continue { label: e.lifetime_token().map(|l| Name::new_lifetime(&l)) }, |
306 | self.alloc_expr(Expr::Continue, syntax_ptr) | 346 | syntax_ptr, |
307 | } | 347 | ), |
308 | ast::Expr::BreakExpr(e) => { | 348 | ast::Expr::BreakExpr(e) => { |
309 | let expr = e.expr().map(|e| self.collect_expr(e)); | 349 | let expr = e.expr().map(|e| self.collect_expr(e)); |
310 | self.alloc_expr(Expr::Break { expr }, syntax_ptr) | 350 | self.alloc_expr( |
351 | Expr::Break { expr, label: e.lifetime_token().map(|l| Name::new_lifetime(&l)) }, | ||
352 | syntax_ptr, | ||
353 | ) | ||
311 | } | 354 | } |
312 | ast::Expr::ParenExpr(e) => { | 355 | ast::Expr::ParenExpr(e) => { |
313 | let inner = self.collect_expr_opt(e.expr()); | 356 | let inner = self.collect_expr_opt(e.expr()); |
@@ -378,8 +421,21 @@ impl ExprCollector<'_> { | |||
378 | } | 421 | } |
379 | ast::Expr::RefExpr(e) => { | 422 | ast::Expr::RefExpr(e) => { |
380 | let expr = self.collect_expr_opt(e.expr()); | 423 | let expr = self.collect_expr_opt(e.expr()); |
381 | let mutability = Mutability::from_mutable(e.mut_token().is_some()); | 424 | let raw_tok = e.raw_token().is_some(); |
382 | self.alloc_expr(Expr::Ref { expr, mutability }, syntax_ptr) | 425 | let mutability = if raw_tok { |
426 | if e.mut_token().is_some() { | ||
427 | Mutability::Mut | ||
428 | } else if e.const_token().is_some() { | ||
429 | Mutability::Shared | ||
430 | } else { | ||
431 | unreachable!("parser only remaps to raw_token() if matching mutability token follows") | ||
432 | } | ||
433 | } else { | ||
434 | Mutability::from_mutable(e.mut_token().is_some()) | ||
435 | }; | ||
436 | let rawness = Rawness::from_raw(raw_tok); | ||
437 | |||
438 | self.alloc_expr(Expr::Ref { expr, rawness, mutability }, syntax_ptr) | ||
383 | } | 439 | } |
384 | ast::Expr::PrefixExpr(e) => { | 440 | ast::Expr::PrefixExpr(e) => { |
385 | let expr = self.collect_expr_opt(e.expr()); | 441 | let expr = self.collect_expr_opt(e.expr()); |
@@ -516,7 +572,8 @@ impl ExprCollector<'_> { | |||
516 | }) | 572 | }) |
517 | .collect(); | 573 | .collect(); |
518 | let tail = block.expr().map(|e| self.collect_expr(e)); | 574 | let tail = block.expr().map(|e| self.collect_expr(e)); |
519 | self.alloc_expr(Expr::Block { statements, tail }, syntax_node_ptr) | 575 | let label = block.label().and_then(|l| l.lifetime_token()).map(|t| Name::new_lifetime(&t)); |
576 | self.alloc_expr(Expr::Block { statements, tail, label }, syntax_node_ptr) | ||
520 | } | 577 | } |
521 | 578 | ||
522 | fn collect_block_items(&mut self, block: &ast::BlockExpr) { | 579 | fn collect_block_items(&mut self, block: &ast::BlockExpr) { |
diff --git a/crates/ra_hir_def/src/body/scope.rs b/crates/ra_hir_def/src/body/scope.rs index 09e92b74e..e48ff38f9 100644 --- a/crates/ra_hir_def/src/body/scope.rs +++ b/crates/ra_hir_def/src/body/scope.rs | |||
@@ -138,10 +138,10 @@ fn compute_block_scopes( | |||
138 | fn compute_expr_scopes(expr: ExprId, body: &Body, scopes: &mut ExprScopes, scope: ScopeId) { | 138 | fn compute_expr_scopes(expr: ExprId, body: &Body, scopes: &mut ExprScopes, scope: ScopeId) { |
139 | scopes.set_scope(expr, scope); | 139 | scopes.set_scope(expr, scope); |
140 | match &body[expr] { | 140 | match &body[expr] { |
141 | Expr::Block { statements, tail } => { | 141 | Expr::Block { statements, tail, .. } => { |
142 | compute_block_scopes(&statements, *tail, body, scopes, scope); | 142 | compute_block_scopes(&statements, *tail, body, scopes, scope); |
143 | } | 143 | } |
144 | Expr::For { iterable, pat, body: body_expr } => { | 144 | Expr::For { iterable, pat, body: body_expr, .. } => { |
145 | compute_expr_scopes(*iterable, body, scopes, scope); | 145 | compute_expr_scopes(*iterable, body, scopes, scope); |
146 | let scope = scopes.new_scope(scope); | 146 | let scope = scopes.new_scope(scope); |
147 | scopes.add_bindings(body, scope, *pat); | 147 | scopes.add_bindings(body, scope, *pat); |
diff --git a/crates/ra_hir_def/src/data.rs b/crates/ra_hir_def/src/data.rs index e2130d931..807195d25 100644 --- a/crates/ra_hir_def/src/data.rs +++ b/crates/ra_hir_def/src/data.rs | |||
@@ -34,6 +34,7 @@ pub struct FunctionData { | |||
34 | /// True if the first param is `self`. This is relevant to decide whether this | 34 | /// True if the first param is `self`. This is relevant to decide whether this |
35 | /// can be called as a method. | 35 | /// can be called as a method. |
36 | pub has_self_param: bool, | 36 | pub has_self_param: bool, |
37 | pub is_unsafe: bool, | ||
37 | pub visibility: RawVisibility, | 38 | pub visibility: RawVisibility, |
38 | } | 39 | } |
39 | 40 | ||
@@ -85,11 +86,14 @@ impl FunctionData { | |||
85 | ret_type | 86 | ret_type |
86 | }; | 87 | }; |
87 | 88 | ||
89 | let is_unsafe = src.value.unsafe_token().is_some(); | ||
90 | |||
88 | let vis_default = RawVisibility::default_for_container(loc.container); | 91 | let vis_default = RawVisibility::default_for_container(loc.container); |
89 | let visibility = | 92 | let visibility = |
90 | RawVisibility::from_ast_with_default(db, vis_default, src.map(|s| s.visibility())); | 93 | RawVisibility::from_ast_with_default(db, vis_default, src.map(|s| s.visibility())); |
91 | 94 | ||
92 | let sig = FunctionData { name, params, ret_type, has_self_param, visibility, attrs }; | 95 | let sig = |
96 | FunctionData { name, params, ret_type, has_self_param, is_unsafe, visibility, attrs }; | ||
93 | Arc::new(sig) | 97 | Arc::new(sig) |
94 | } | 98 | } |
95 | } | 99 | } |
diff --git a/crates/ra_hir_def/src/docs.rs b/crates/ra_hir_def/src/docs.rs index b221ae1ce..2630b3d89 100644 --- a/crates/ra_hir_def/src/docs.rs +++ b/crates/ra_hir_def/src/docs.rs | |||
@@ -29,6 +29,13 @@ impl Documentation { | |||
29 | Documentation(s.into()) | 29 | Documentation(s.into()) |
30 | } | 30 | } |
31 | 31 | ||
32 | pub fn from_ast<N>(node: &N) -> Option<Documentation> | ||
33 | where | ||
34 | N: ast::DocCommentsOwner + ast::AttrsOwner, | ||
35 | { | ||
36 | docs_from_ast(node) | ||
37 | } | ||
38 | |||
32 | pub fn as_str(&self) -> &str { | 39 | pub fn as_str(&self) -> &str { |
33 | &*self.0 | 40 | &*self.0 |
34 | } | 41 | } |
@@ -70,6 +77,45 @@ impl Documentation { | |||
70 | } | 77 | } |
71 | } | 78 | } |
72 | 79 | ||
73 | pub(crate) fn docs_from_ast(node: &impl ast::DocCommentsOwner) -> Option<Documentation> { | 80 | pub(crate) fn docs_from_ast<N>(node: &N) -> Option<Documentation> |
74 | node.doc_comment_text().map(|it| Documentation::new(&it)) | 81 | where |
82 | N: ast::DocCommentsOwner + ast::AttrsOwner, | ||
83 | { | ||
84 | let doc_comment_text = node.doc_comment_text(); | ||
85 | let doc_attr_text = expand_doc_attrs(node); | ||
86 | let docs = merge_doc_comments_and_attrs(doc_comment_text, doc_attr_text); | ||
87 | docs.map(|it| Documentation::new(&it)) | ||
88 | } | ||
89 | |||
90 | fn merge_doc_comments_and_attrs( | ||
91 | doc_comment_text: Option<String>, | ||
92 | doc_attr_text: Option<String>, | ||
93 | ) -> Option<String> { | ||
94 | match (doc_comment_text, doc_attr_text) { | ||
95 | (Some(mut comment_text), Some(attr_text)) => { | ||
96 | comment_text.push_str("\n\n"); | ||
97 | comment_text.push_str(&attr_text); | ||
98 | Some(comment_text) | ||
99 | } | ||
100 | (Some(comment_text), None) => Some(comment_text), | ||
101 | (None, Some(attr_text)) => Some(attr_text), | ||
102 | (None, None) => None, | ||
103 | } | ||
104 | } | ||
105 | |||
106 | fn expand_doc_attrs(owner: &dyn ast::AttrsOwner) -> Option<String> { | ||
107 | let mut docs = String::new(); | ||
108 | for attr in owner.attrs() { | ||
109 | if let Some(("doc", value)) = | ||
110 | attr.as_simple_key_value().as_ref().map(|(k, v)| (k.as_str(), v.as_str())) | ||
111 | { | ||
112 | docs.push_str(value); | ||
113 | docs.push_str("\n\n"); | ||
114 | } | ||
115 | } | ||
116 | if docs.is_empty() { | ||
117 | None | ||
118 | } else { | ||
119 | Some(docs.trim_end_matches("\n\n").to_owned()) | ||
120 | } | ||
75 | } | 121 | } |
diff --git a/crates/ra_hir_def/src/expr.rs b/crates/ra_hir_def/src/expr.rs index a0cdad529..ca49b26d1 100644 --- a/crates/ra_hir_def/src/expr.rs +++ b/crates/ra_hir_def/src/expr.rs | |||
@@ -19,7 +19,7 @@ use ra_syntax::ast::RangeOp; | |||
19 | use crate::{ | 19 | use crate::{ |
20 | builtin_type::{BuiltinFloat, BuiltinInt}, | 20 | builtin_type::{BuiltinFloat, BuiltinInt}, |
21 | path::{GenericArgs, Path}, | 21 | path::{GenericArgs, Path}, |
22 | type_ref::{Mutability, TypeRef}, | 22 | type_ref::{Mutability, Rawness, TypeRef}, |
23 | }; | 23 | }; |
24 | 24 | ||
25 | pub type ExprId = Idx<Expr>; | 25 | pub type ExprId = Idx<Expr>; |
@@ -52,18 +52,22 @@ pub enum Expr { | |||
52 | Block { | 52 | Block { |
53 | statements: Vec<Statement>, | 53 | statements: Vec<Statement>, |
54 | tail: Option<ExprId>, | 54 | tail: Option<ExprId>, |
55 | label: Option<Name>, | ||
55 | }, | 56 | }, |
56 | Loop { | 57 | Loop { |
57 | body: ExprId, | 58 | body: ExprId, |
59 | label: Option<Name>, | ||
58 | }, | 60 | }, |
59 | While { | 61 | While { |
60 | condition: ExprId, | 62 | condition: ExprId, |
61 | body: ExprId, | 63 | body: ExprId, |
64 | label: Option<Name>, | ||
62 | }, | 65 | }, |
63 | For { | 66 | For { |
64 | iterable: ExprId, | 67 | iterable: ExprId, |
65 | pat: PatId, | 68 | pat: PatId, |
66 | body: ExprId, | 69 | body: ExprId, |
70 | label: Option<Name>, | ||
67 | }, | 71 | }, |
68 | Call { | 72 | Call { |
69 | callee: ExprId, | 73 | callee: ExprId, |
@@ -79,9 +83,12 @@ pub enum Expr { | |||
79 | expr: ExprId, | 83 | expr: ExprId, |
80 | arms: Vec<MatchArm>, | 84 | arms: Vec<MatchArm>, |
81 | }, | 85 | }, |
82 | Continue, | 86 | Continue { |
87 | label: Option<Name>, | ||
88 | }, | ||
83 | Break { | 89 | Break { |
84 | expr: Option<ExprId>, | 90 | expr: Option<ExprId>, |
91 | label: Option<Name>, | ||
85 | }, | 92 | }, |
86 | Return { | 93 | Return { |
87 | expr: Option<ExprId>, | 94 | expr: Option<ExprId>, |
@@ -110,6 +117,7 @@ pub enum Expr { | |||
110 | }, | 117 | }, |
111 | Ref { | 118 | Ref { |
112 | expr: ExprId, | 119 | expr: ExprId, |
120 | rawness: Rawness, | ||
113 | mutability: Mutability, | 121 | mutability: Mutability, |
114 | }, | 122 | }, |
115 | Box { | 123 | Box { |
@@ -224,7 +232,7 @@ impl Expr { | |||
224 | f(*else_branch); | 232 | f(*else_branch); |
225 | } | 233 | } |
226 | } | 234 | } |
227 | Expr::Block { statements, tail } => { | 235 | Expr::Block { statements, tail, .. } => { |
228 | for stmt in statements { | 236 | for stmt in statements { |
229 | match stmt { | 237 | match stmt { |
230 | Statement::Let { initializer, .. } => { | 238 | Statement::Let { initializer, .. } => { |
@@ -240,8 +248,8 @@ impl Expr { | |||
240 | } | 248 | } |
241 | } | 249 | } |
242 | Expr::TryBlock { body } => f(*body), | 250 | Expr::TryBlock { body } => f(*body), |
243 | Expr::Loop { body } => f(*body), | 251 | Expr::Loop { body, .. } => f(*body), |
244 | Expr::While { condition, body } => { | 252 | Expr::While { condition, body, .. } => { |
245 | f(*condition); | 253 | f(*condition); |
246 | f(*body); | 254 | f(*body); |
247 | } | 255 | } |
@@ -267,8 +275,8 @@ impl Expr { | |||
267 | f(arm.expr); | 275 | f(arm.expr); |
268 | } | 276 | } |
269 | } | 277 | } |
270 | Expr::Continue => {} | 278 | Expr::Continue { .. } => {} |
271 | Expr::Break { expr } | Expr::Return { expr } => { | 279 | Expr::Break { expr, .. } | Expr::Return { expr } => { |
272 | if let Some(expr) = expr { | 280 | if let Some(expr) = expr { |
273 | f(*expr); | 281 | f(*expr); |
274 | } | 282 | } |
diff --git a/crates/ra_hir_def/src/lang_item.rs b/crates/ra_hir_def/src/lang_item.rs index d962db3cc..3516784b8 100644 --- a/crates/ra_hir_def/src/lang_item.rs +++ b/crates/ra_hir_def/src/lang_item.rs | |||
@@ -164,7 +164,7 @@ impl LangItems { | |||
164 | T: Into<AttrDefId> + Copy, | 164 | T: Into<AttrDefId> + Copy, |
165 | { | 165 | { |
166 | if let Some(lang_item_name) = lang_attr(db, item) { | 166 | if let Some(lang_item_name) = lang_attr(db, item) { |
167 | self.items.entry(lang_item_name.clone()).or_insert_with(|| constructor(item)); | 167 | self.items.entry(lang_item_name).or_insert_with(|| constructor(item)); |
168 | } | 168 | } |
169 | } | 169 | } |
170 | } | 170 | } |
diff --git a/crates/ra_hir_def/src/nameres/raw.rs b/crates/ra_hir_def/src/nameres/raw.rs index 4e628b14d..f44baa579 100644 --- a/crates/ra_hir_def/src/nameres/raw.rs +++ b/crates/ra_hir_def/src/nameres/raw.rs | |||
@@ -175,7 +175,7 @@ pub(super) enum DefKind { | |||
175 | } | 175 | } |
176 | 176 | ||
177 | impl DefKind { | 177 | impl DefKind { |
178 | pub fn ast_id(&self) -> FileAstId<ast::ModuleItem> { | 178 | pub fn ast_id(self) -> FileAstId<ast::ModuleItem> { |
179 | match self { | 179 | match self { |
180 | DefKind::Function(it) => it.upcast(), | 180 | DefKind::Function(it) => it.upcast(), |
181 | DefKind::Struct(it, _) => it.upcast(), | 181 | DefKind::Struct(it, _) => it.upcast(), |
diff --git a/crates/ra_hir_def/src/type_ref.rs b/crates/ra_hir_def/src/type_ref.rs index 5bdad9efd..86a77b704 100644 --- a/crates/ra_hir_def/src/type_ref.rs +++ b/crates/ra_hir_def/src/type_ref.rs | |||
@@ -35,6 +35,22 @@ impl Mutability { | |||
35 | } | 35 | } |
36 | } | 36 | } |
37 | 37 | ||
38 | #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] | ||
39 | pub enum Rawness { | ||
40 | RawPtr, | ||
41 | Ref, | ||
42 | } | ||
43 | |||
44 | impl Rawness { | ||
45 | pub fn from_raw(is_raw: bool) -> Rawness { | ||
46 | if is_raw { | ||
47 | Rawness::RawPtr | ||
48 | } else { | ||
49 | Rawness::Ref | ||
50 | } | ||
51 | } | ||
52 | } | ||
53 | |||
38 | /// Compare ty::Ty | 54 | /// Compare ty::Ty |
39 | #[derive(Clone, PartialEq, Eq, Hash, Debug)] | 55 | #[derive(Clone, PartialEq, Eq, Hash, Debug)] |
40 | pub enum TypeRef { | 56 | pub enum TypeRef { |
diff --git a/crates/ra_hir_expand/src/name.rs b/crates/ra_hir_expand/src/name.rs index fecce224e..660bdfe33 100644 --- a/crates/ra_hir_expand/src/name.rs +++ b/crates/ra_hir_expand/src/name.rs | |||
@@ -37,6 +37,11 @@ impl Name { | |||
37 | Name(Repr::TupleField(idx)) | 37 | Name(Repr::TupleField(idx)) |
38 | } | 38 | } |
39 | 39 | ||
40 | pub fn new_lifetime(lt: &ra_syntax::SyntaxToken) -> Name { | ||
41 | assert!(lt.kind() == ra_syntax::SyntaxKind::LIFETIME); | ||
42 | Name(Repr::Text(lt.text().clone())) | ||
43 | } | ||
44 | |||
40 | /// Shortcut to create inline plain text name | 45 | /// Shortcut to create inline plain text name |
41 | const fn new_inline_ascii(text: &[u8]) -> Name { | 46 | const fn new_inline_ascii(text: &[u8]) -> Name { |
42 | Name::new_text(SmolStr::new_inline_from_ascii(text.len(), text)) | 47 | Name::new_text(SmolStr::new_inline_from_ascii(text.len(), text)) |
@@ -148,6 +153,7 @@ pub mod known { | |||
148 | str, | 153 | str, |
149 | // Special names | 154 | // Special names |
150 | macro_rules, | 155 | macro_rules, |
156 | doc, | ||
151 | // Components of known path (value or mod name) | 157 | // Components of known path (value or mod name) |
152 | std, | 158 | std, |
153 | core, | 159 | core, |
diff --git a/crates/ra_hir_expand/src/proc_macro.rs b/crates/ra_hir_expand/src/proc_macro.rs index 4e0e069c8..04c026004 100644 --- a/crates/ra_hir_expand/src/proc_macro.rs +++ b/crates/ra_hir_expand/src/proc_macro.rs | |||
@@ -25,7 +25,7 @@ impl ProcMacroExpander { | |||
25 | } | 25 | } |
26 | 26 | ||
27 | pub fn expand( | 27 | pub fn expand( |
28 | &self, | 28 | self, |
29 | db: &dyn AstDatabase, | 29 | db: &dyn AstDatabase, |
30 | _id: LazyMacroId, | 30 | _id: LazyMacroId, |
31 | tt: &tt::Subtree, | 31 | tt: &tt::Subtree, |
diff --git a/crates/ra_hir_ty/Cargo.toml b/crates/ra_hir_ty/Cargo.toml index b2de7fa34..4b8dcdc07 100644 --- a/crates/ra_hir_ty/Cargo.toml +++ b/crates/ra_hir_ty/Cargo.toml | |||
@@ -27,9 +27,8 @@ test_utils = { path = "../test_utils" } | |||
27 | 27 | ||
28 | scoped-tls = "1" | 28 | scoped-tls = "1" |
29 | 29 | ||
30 | chalk-solve = { git = "https://github.com/rust-lang/chalk.git", rev = "eaab84b394007d1bed15f5470409a6ea02900a96" } | 30 | chalk-solve = { git = "https://github.com/rust-lang/chalk.git", rev = "329b7f3fdd2431ed6f6778cde53f22374c7d094c" } |
31 | chalk-rust-ir = { git = "https://github.com/rust-lang/chalk.git", rev = "eaab84b394007d1bed15f5470409a6ea02900a96" } | 31 | chalk-ir = { git = "https://github.com/rust-lang/chalk.git", rev = "329b7f3fdd2431ed6f6778cde53f22374c7d094c" } |
32 | chalk-ir = { git = "https://github.com/rust-lang/chalk.git", rev = "eaab84b394007d1bed15f5470409a6ea02900a96" } | ||
33 | 32 | ||
34 | [dev-dependencies] | 33 | [dev-dependencies] |
35 | insta = "0.16.0" | 34 | insta = "0.16.0" |
diff --git a/crates/ra_hir_ty/src/db.rs b/crates/ra_hir_ty/src/db.rs index dfc6c7dd6..0a8bb24ac 100644 --- a/crates/ra_hir_ty/src/db.rs +++ b/crates/ra_hir_ty/src/db.rs | |||
@@ -76,6 +76,8 @@ pub trait HirDatabase: DefDatabase + Upcast<dyn DefDatabase> { | |||
76 | #[salsa::interned] | 76 | #[salsa::interned] |
77 | fn intern_type_ctor(&self, type_ctor: TypeCtor) -> crate::TypeCtorId; | 77 | fn intern_type_ctor(&self, type_ctor: TypeCtor) -> crate::TypeCtorId; |
78 | #[salsa::interned] | 78 | #[salsa::interned] |
79 | fn intern_callable_def(&self, callable_def: CallableDef) -> crate::CallableDefId; | ||
80 | #[salsa::interned] | ||
79 | fn intern_type_param_id(&self, param_id: TypeParamId) -> GlobalTypeParamId; | 81 | fn intern_type_param_id(&self, param_id: TypeParamId) -> GlobalTypeParamId; |
80 | #[salsa::interned] | 82 | #[salsa::interned] |
81 | fn intern_chalk_impl(&self, impl_: Impl) -> crate::traits::GlobalImplId; | 83 | fn intern_chalk_impl(&self, impl_: Impl) -> crate::traits::GlobalImplId; |
@@ -94,6 +96,9 @@ pub trait HirDatabase: DefDatabase + Upcast<dyn DefDatabase> { | |||
94 | #[salsa::invoke(crate::traits::chalk::impl_datum_query)] | 96 | #[salsa::invoke(crate::traits::chalk::impl_datum_query)] |
95 | fn impl_datum(&self, krate: CrateId, impl_id: chalk::ImplId) -> Arc<chalk::ImplDatum>; | 97 | fn impl_datum(&self, krate: CrateId, impl_id: chalk::ImplId) -> Arc<chalk::ImplDatum>; |
96 | 98 | ||
99 | #[salsa::invoke(crate::traits::chalk::fn_def_datum_query)] | ||
100 | fn fn_def_datum(&self, krate: CrateId, fn_def_id: chalk::FnDefId) -> Arc<chalk::FnDefDatum>; | ||
101 | |||
97 | #[salsa::invoke(crate::traits::chalk::associated_ty_value_query)] | 102 | #[salsa::invoke(crate::traits::chalk::associated_ty_value_query)] |
98 | fn associated_ty_value( | 103 | fn associated_ty_value( |
99 | &self, | 104 | &self, |
diff --git a/crates/ra_hir_ty/src/diagnostics.rs b/crates/ra_hir_ty/src/diagnostics.rs index 41ac70272..2c7298714 100644 --- a/crates/ra_hir_ty/src/diagnostics.rs +++ b/crates/ra_hir_ty/src/diagnostics.rs | |||
@@ -40,7 +40,7 @@ impl Diagnostic for MissingFields { | |||
40 | fn message(&self) -> String { | 40 | fn message(&self) -> String { |
41 | let mut buf = String::from("Missing structure fields:\n"); | 41 | let mut buf = String::from("Missing structure fields:\n"); |
42 | for field in &self.missed_fields { | 42 | for field in &self.missed_fields { |
43 | format_to!(buf, "- {}", field); | 43 | format_to!(buf, "- {}\n", field); |
44 | } | 44 | } |
45 | buf | 45 | buf |
46 | } | 46 | } |
@@ -73,7 +73,7 @@ impl Diagnostic for MissingPatFields { | |||
73 | fn message(&self) -> String { | 73 | fn message(&self) -> String { |
74 | let mut buf = String::from("Missing structure fields:\n"); | 74 | let mut buf = String::from("Missing structure fields:\n"); |
75 | for field in &self.missed_fields { | 75 | for field in &self.missed_fields { |
76 | format_to!(buf, "- {}", field); | 76 | format_to!(buf, "- {}\n", field); |
77 | } | 77 | } |
78 | buf | 78 | buf |
79 | } | 79 | } |
diff --git a/crates/ra_hir_ty/src/infer.rs b/crates/ra_hir_ty/src/infer.rs index 957d6e0b5..dc77e88e5 100644 --- a/crates/ra_hir_ty/src/infer.rs +++ b/crates/ra_hir_ty/src/infer.rs | |||
@@ -219,6 +219,17 @@ struct InferenceContext<'a> { | |||
219 | struct BreakableContext { | 219 | struct BreakableContext { |
220 | pub may_break: bool, | 220 | pub may_break: bool, |
221 | pub break_ty: Ty, | 221 | pub break_ty: Ty, |
222 | pub label: Option<name::Name>, | ||
223 | } | ||
224 | |||
225 | fn find_breakable<'c>( | ||
226 | ctxs: &'c mut [BreakableContext], | ||
227 | label: Option<&name::Name>, | ||
228 | ) -> Option<&'c mut BreakableContext> { | ||
229 | match label { | ||
230 | Some(_) => ctxs.iter_mut().rev().find(|ctx| ctx.label.as_ref() == label), | ||
231 | None => ctxs.last_mut(), | ||
232 | } | ||
222 | } | 233 | } |
223 | 234 | ||
224 | impl<'a> InferenceContext<'a> { | 235 | impl<'a> InferenceContext<'a> { |
diff --git a/crates/ra_hir_ty/src/infer/coerce.rs b/crates/ra_hir_ty/src/infer/coerce.rs index 2ee9adb16..32c7c57cd 100644 --- a/crates/ra_hir_ty/src/infer/coerce.rs +++ b/crates/ra_hir_ty/src/infer/coerce.rs | |||
@@ -45,9 +45,7 @@ impl<'a> InferenceContext<'a> { | |||
45 | self.coerce_merge_branch(&ptr_ty1, &ptr_ty2) | 45 | self.coerce_merge_branch(&ptr_ty1, &ptr_ty2) |
46 | } else { | 46 | } else { |
47 | mark::hit!(coerce_merge_fail_fallback); | 47 | mark::hit!(coerce_merge_fail_fallback); |
48 | // For incompatible types, we use the latter one as result | 48 | ty1.clone() |
49 | // to be better recovery for `if` without `else`. | ||
50 | ty2.clone() | ||
51 | } | 49 | } |
52 | } | 50 | } |
53 | } | 51 | } |
diff --git a/crates/ra_hir_ty/src/infer/expr.rs b/crates/ra_hir_ty/src/infer/expr.rs index b28724f0e..4a98e2deb 100644 --- a/crates/ra_hir_ty/src/infer/expr.rs +++ b/crates/ra_hir_ty/src/infer/expr.rs | |||
@@ -17,13 +17,13 @@ use crate::{ | |||
17 | autoderef, method_resolution, op, | 17 | autoderef, method_resolution, op, |
18 | traits::InEnvironment, | 18 | traits::InEnvironment, |
19 | utils::{generics, variant_data, Generics}, | 19 | utils::{generics, variant_data, Generics}, |
20 | ApplicationTy, Binders, CallableDef, InferTy, IntTy, Mutability, Obligation, Substs, TraitRef, | 20 | ApplicationTy, Binders, CallableDef, InferTy, IntTy, Mutability, Obligation, Rawness, Substs, |
21 | Ty, TypeCtor, Uncertain, | 21 | TraitRef, Ty, TypeCtor, Uncertain, |
22 | }; | 22 | }; |
23 | 23 | ||
24 | use super::{ | 24 | use super::{ |
25 | BindingMode, BreakableContext, Diverges, Expectation, InferenceContext, InferenceDiagnostic, | 25 | find_breakable, BindingMode, BreakableContext, Diverges, Expectation, InferenceContext, |
26 | TypeMismatch, | 26 | InferenceDiagnostic, TypeMismatch, |
27 | }; | 27 | }; |
28 | 28 | ||
29 | impl<'a> InferenceContext<'a> { | 29 | impl<'a> InferenceContext<'a> { |
@@ -86,16 +86,20 @@ impl<'a> InferenceContext<'a> { | |||
86 | 86 | ||
87 | self.coerce_merge_branch(&then_ty, &else_ty) | 87 | self.coerce_merge_branch(&then_ty, &else_ty) |
88 | } | 88 | } |
89 | Expr::Block { statements, tail } => self.infer_block(statements, *tail, expected), | 89 | Expr::Block { statements, tail, .. } => { |
90 | // FIXME: Breakable block inference | ||
91 | self.infer_block(statements, *tail, expected) | ||
92 | } | ||
90 | Expr::TryBlock { body } => { | 93 | Expr::TryBlock { body } => { |
91 | let _inner = self.infer_expr(*body, expected); | 94 | let _inner = self.infer_expr(*body, expected); |
92 | // FIXME should be std::result::Result<{inner}, _> | 95 | // FIXME should be std::result::Result<{inner}, _> |
93 | Ty::Unknown | 96 | Ty::Unknown |
94 | } | 97 | } |
95 | Expr::Loop { body } => { | 98 | Expr::Loop { body, label } => { |
96 | self.breakables.push(BreakableContext { | 99 | self.breakables.push(BreakableContext { |
97 | may_break: false, | 100 | may_break: false, |
98 | break_ty: self.table.new_type_var(), | 101 | break_ty: self.table.new_type_var(), |
102 | label: label.clone(), | ||
99 | }); | 103 | }); |
100 | self.infer_expr(*body, &Expectation::has_type(Ty::unit())); | 104 | self.infer_expr(*body, &Expectation::has_type(Ty::unit())); |
101 | 105 | ||
@@ -110,8 +114,12 @@ impl<'a> InferenceContext<'a> { | |||
110 | Ty::simple(TypeCtor::Never) | 114 | Ty::simple(TypeCtor::Never) |
111 | } | 115 | } |
112 | } | 116 | } |
113 | Expr::While { condition, body } => { | 117 | Expr::While { condition, body, label } => { |
114 | self.breakables.push(BreakableContext { may_break: false, break_ty: Ty::Unknown }); | 118 | self.breakables.push(BreakableContext { |
119 | may_break: false, | ||
120 | break_ty: Ty::Unknown, | ||
121 | label: label.clone(), | ||
122 | }); | ||
115 | // while let is desugared to a match loop, so this is always simple while | 123 | // while let is desugared to a match loop, so this is always simple while |
116 | self.infer_expr(*condition, &Expectation::has_type(Ty::simple(TypeCtor::Bool))); | 124 | self.infer_expr(*condition, &Expectation::has_type(Ty::simple(TypeCtor::Bool))); |
117 | self.infer_expr(*body, &Expectation::has_type(Ty::unit())); | 125 | self.infer_expr(*body, &Expectation::has_type(Ty::unit())); |
@@ -120,10 +128,14 @@ impl<'a> InferenceContext<'a> { | |||
120 | self.diverges = Diverges::Maybe; | 128 | self.diverges = Diverges::Maybe; |
121 | Ty::unit() | 129 | Ty::unit() |
122 | } | 130 | } |
123 | Expr::For { iterable, body, pat } => { | 131 | Expr::For { iterable, body, pat, label } => { |
124 | let iterable_ty = self.infer_expr(*iterable, &Expectation::none()); | 132 | let iterable_ty = self.infer_expr(*iterable, &Expectation::none()); |
125 | 133 | ||
126 | self.breakables.push(BreakableContext { may_break: false, break_ty: Ty::Unknown }); | 134 | self.breakables.push(BreakableContext { |
135 | may_break: false, | ||
136 | break_ty: Ty::Unknown, | ||
137 | label: label.clone(), | ||
138 | }); | ||
127 | let pat_ty = | 139 | let pat_ty = |
128 | self.resolve_associated_type(iterable_ty, self.resolve_into_iter_item()); | 140 | self.resolve_associated_type(iterable_ty, self.resolve_into_iter_item()); |
129 | 141 | ||
@@ -140,13 +152,13 @@ impl<'a> InferenceContext<'a> { | |||
140 | 152 | ||
141 | let mut sig_tys = Vec::new(); | 153 | let mut sig_tys = Vec::new(); |
142 | 154 | ||
143 | for (arg_pat, arg_type) in args.iter().zip(arg_types.iter()) { | 155 | // collect explicitly written argument types |
144 | let expected = if let Some(type_ref) = arg_type { | 156 | for arg_type in arg_types.iter() { |
157 | let arg_ty = if let Some(type_ref) = arg_type { | ||
145 | self.make_ty(type_ref) | 158 | self.make_ty(type_ref) |
146 | } else { | 159 | } else { |
147 | Ty::Unknown | 160 | self.table.new_type_var() |
148 | }; | 161 | }; |
149 | let arg_ty = self.infer_pat(*arg_pat, &expected, BindingMode::default()); | ||
150 | sig_tys.push(arg_ty); | 162 | sig_tys.push(arg_ty); |
151 | } | 163 | } |
152 | 164 | ||
@@ -158,7 +170,7 @@ impl<'a> InferenceContext<'a> { | |||
158 | sig_tys.push(ret_ty.clone()); | 170 | sig_tys.push(ret_ty.clone()); |
159 | let sig_ty = Ty::apply( | 171 | let sig_ty = Ty::apply( |
160 | TypeCtor::FnPtr { num_args: sig_tys.len() as u16 - 1 }, | 172 | TypeCtor::FnPtr { num_args: sig_tys.len() as u16 - 1 }, |
161 | Substs(sig_tys.into()), | 173 | Substs(sig_tys.clone().into()), |
162 | ); | 174 | ); |
163 | let closure_ty = | 175 | let closure_ty = |
164 | Ty::apply_one(TypeCtor::Closure { def: self.owner, expr: tgt_expr }, sig_ty); | 176 | Ty::apply_one(TypeCtor::Closure { def: self.owner, expr: tgt_expr }, sig_ty); |
@@ -168,6 +180,12 @@ impl<'a> InferenceContext<'a> { | |||
168 | // infer the body. | 180 | // infer the body. |
169 | self.coerce(&closure_ty, &expected.ty); | 181 | self.coerce(&closure_ty, &expected.ty); |
170 | 182 | ||
183 | // Now go through the argument patterns | ||
184 | for (arg_pat, arg_ty) in args.iter().zip(sig_tys) { | ||
185 | let resolved = self.resolve_ty_as_possible(arg_ty); | ||
186 | self.infer_pat(*arg_pat, &resolved, BindingMode::default()); | ||
187 | } | ||
188 | |||
171 | let prev_diverges = mem::replace(&mut self.diverges, Diverges::Maybe); | 189 | let prev_diverges = mem::replace(&mut self.diverges, Diverges::Maybe); |
172 | let prev_ret_ty = mem::replace(&mut self.return_ty, ret_ty.clone()); | 190 | let prev_ret_ty = mem::replace(&mut self.return_ty, ret_ty.clone()); |
173 | 191 | ||
@@ -230,23 +248,24 @@ impl<'a> InferenceContext<'a> { | |||
230 | let resolver = resolver_for_expr(self.db.upcast(), self.owner, tgt_expr); | 248 | let resolver = resolver_for_expr(self.db.upcast(), self.owner, tgt_expr); |
231 | self.infer_path(&resolver, p, tgt_expr.into()).unwrap_or(Ty::Unknown) | 249 | self.infer_path(&resolver, p, tgt_expr.into()).unwrap_or(Ty::Unknown) |
232 | } | 250 | } |
233 | Expr::Continue => Ty::simple(TypeCtor::Never), | 251 | Expr::Continue { .. } => Ty::simple(TypeCtor::Never), |
234 | Expr::Break { expr } => { | 252 | Expr::Break { expr, label } => { |
235 | let val_ty = if let Some(expr) = expr { | 253 | let val_ty = if let Some(expr) = expr { |
236 | self.infer_expr(*expr, &Expectation::none()) | 254 | self.infer_expr(*expr, &Expectation::none()) |
237 | } else { | 255 | } else { |
238 | Ty::unit() | 256 | Ty::unit() |
239 | }; | 257 | }; |
240 | 258 | ||
241 | let last_ty = if let Some(ctxt) = self.breakables.last() { | 259 | let last_ty = |
242 | ctxt.break_ty.clone() | 260 | if let Some(ctxt) = find_breakable(&mut self.breakables, label.as_ref()) { |
243 | } else { | 261 | ctxt.break_ty.clone() |
244 | Ty::Unknown | 262 | } else { |
245 | }; | 263 | Ty::Unknown |
264 | }; | ||
246 | 265 | ||
247 | let merged_type = self.coerce_merge_branch(&last_ty, &val_ty); | 266 | let merged_type = self.coerce_merge_branch(&last_ty, &val_ty); |
248 | 267 | ||
249 | if let Some(ctxt) = self.breakables.last_mut() { | 268 | if let Some(ctxt) = find_breakable(&mut self.breakables, label.as_ref()) { |
250 | ctxt.break_ty = merged_type; | 269 | ctxt.break_ty = merged_type; |
251 | ctxt.may_break = true; | 270 | ctxt.may_break = true; |
252 | } else { | 271 | } else { |
@@ -350,19 +369,28 @@ impl<'a> InferenceContext<'a> { | |||
350 | // FIXME check the cast... | 369 | // FIXME check the cast... |
351 | cast_ty | 370 | cast_ty |
352 | } | 371 | } |
353 | Expr::Ref { expr, mutability } => { | 372 | Expr::Ref { expr, rawness, mutability } => { |
354 | let expectation = | 373 | let expectation = if let Some((exp_inner, exp_rawness, exp_mutability)) = |
355 | if let Some((exp_inner, exp_mutability)) = &expected.ty.as_reference() { | 374 | &expected.ty.as_reference_or_ptr() |
356 | if *exp_mutability == Mutability::Mut && *mutability == Mutability::Shared { | 375 | { |
357 | // FIXME: throw type error - expected mut reference but found shared ref, | 376 | if *exp_mutability == Mutability::Mut && *mutability == Mutability::Shared { |
358 | // which cannot be coerced | 377 | // FIXME: throw type error - expected mut reference but found shared ref, |
359 | } | 378 | // which cannot be coerced |
360 | Expectation::rvalue_hint(Ty::clone(exp_inner)) | 379 | } |
361 | } else { | 380 | if *exp_rawness == Rawness::Ref && *rawness == Rawness::RawPtr { |
362 | Expectation::none() | 381 | // FIXME: throw type error - expected reference but found ptr, |
363 | }; | 382 | // which cannot be coerced |
383 | } | ||
384 | Expectation::rvalue_hint(Ty::clone(exp_inner)) | ||
385 | } else { | ||
386 | Expectation::none() | ||
387 | }; | ||
364 | let inner_ty = self.infer_expr_inner(*expr, &expectation); | 388 | let inner_ty = self.infer_expr_inner(*expr, &expectation); |
365 | Ty::apply_one(TypeCtor::Ref(*mutability), inner_ty) | 389 | let ty = match rawness { |
390 | Rawness::RawPtr => TypeCtor::RawPtr(*mutability), | ||
391 | Rawness::Ref => TypeCtor::Ref(*mutability), | ||
392 | }; | ||
393 | Ty::apply_one(ty, inner_ty) | ||
366 | } | 394 | } |
367 | Expr::Box { expr } => { | 395 | Expr::Box { expr } => { |
368 | let inner_ty = self.infer_expr_inner(*expr, &Expectation::none()); | 396 | let inner_ty = self.infer_expr_inner(*expr, &Expectation::none()); |
diff --git a/crates/ra_hir_ty/src/lib.rs b/crates/ra_hir_ty/src/lib.rs index e91c9be04..9fa8d3bdc 100644 --- a/crates/ra_hir_ty/src/lib.rs +++ b/crates/ra_hir_ty/src/lib.rs | |||
@@ -49,8 +49,10 @@ use std::sync::Arc; | |||
49 | use std::{iter, mem}; | 49 | use std::{iter, mem}; |
50 | 50 | ||
51 | use hir_def::{ | 51 | use hir_def::{ |
52 | expr::ExprId, type_ref::Mutability, AdtId, AssocContainerId, DefWithBodyId, GenericDefId, | 52 | expr::ExprId, |
53 | HasModule, Lookup, TraitId, TypeAliasId, TypeParamId, | 53 | type_ref::{Mutability, Rawness}, |
54 | AdtId, AssocContainerId, DefWithBodyId, GenericDefId, HasModule, Lookup, TraitId, TypeAliasId, | ||
55 | TypeParamId, | ||
54 | }; | 56 | }; |
55 | use ra_db::{impl_intern_key, salsa, CrateId}; | 57 | use ra_db::{impl_intern_key, salsa, CrateId}; |
56 | 58 | ||
@@ -159,6 +161,12 @@ pub enum TypeCtor { | |||
159 | pub struct TypeCtorId(salsa::InternId); | 161 | pub struct TypeCtorId(salsa::InternId); |
160 | impl_intern_key!(TypeCtorId); | 162 | impl_intern_key!(TypeCtorId); |
161 | 163 | ||
164 | /// This exists just for Chalk, because Chalk just has a single `FnDefId` where | ||
165 | /// we have different IDs for struct and enum variant constructors. | ||
166 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd)] | ||
167 | pub struct CallableDefId(salsa::InternId); | ||
168 | impl_intern_key!(CallableDefId); | ||
169 | |||
162 | impl TypeCtor { | 170 | impl TypeCtor { |
163 | pub fn num_ty_params(self, db: &dyn HirDatabase) -> usize { | 171 | pub fn num_ty_params(self, db: &dyn HirDatabase) -> usize { |
164 | match self { | 172 | match self { |
@@ -703,6 +711,18 @@ impl Ty { | |||
703 | } | 711 | } |
704 | } | 712 | } |
705 | 713 | ||
714 | pub fn as_reference_or_ptr(&self) -> Option<(&Ty, Rawness, Mutability)> { | ||
715 | match self { | ||
716 | Ty::Apply(ApplicationTy { ctor: TypeCtor::Ref(mutability), parameters }) => { | ||
717 | Some((parameters.as_single(), Rawness::Ref, *mutability)) | ||
718 | } | ||
719 | Ty::Apply(ApplicationTy { ctor: TypeCtor::RawPtr(mutability), parameters }) => { | ||
720 | Some((parameters.as_single(), Rawness::RawPtr, *mutability)) | ||
721 | } | ||
722 | _ => None, | ||
723 | } | ||
724 | } | ||
725 | |||
706 | pub fn strip_references(&self) -> &Ty { | 726 | pub fn strip_references(&self) -> &Ty { |
707 | let mut t: &Ty = self; | 727 | let mut t: &Ty = self; |
708 | 728 | ||
diff --git a/crates/ra_hir_ty/src/tests/coercion.rs b/crates/ra_hir_ty/src/tests/coercion.rs index 2cc4f4bf9..6f777ed8c 100644 --- a/crates/ra_hir_ty/src/tests/coercion.rs +++ b/crates/ra_hir_ty/src/tests/coercion.rs | |||
@@ -116,15 +116,20 @@ fn infer_let_stmt_coerce() { | |||
116 | assert_snapshot!( | 116 | assert_snapshot!( |
117 | infer(r#" | 117 | infer(r#" |
118 | fn test() { | 118 | fn test() { |
119 | let x: &[i32] = &[1]; | 119 | let x: &[isize] = &[1]; |
120 | let x: *const [isize] = &[1]; | ||
120 | } | 121 | } |
121 | "#), | 122 | "#), |
122 | @r###" | 123 | @r###" |
123 | 11..40 '{ ...[1]; }': () | 124 | 11..76 '{ ...[1]; }': () |
124 | 21..22 'x': &[i32] | 125 | 21..22 'x': &[isize] |
125 | 33..37 '&[1]': &[i32; _] | 126 | 35..39 '&[1]': &[isize; _] |
126 | 34..37 '[1]': [i32; _] | 127 | 36..39 '[1]': [isize; _] |
127 | 35..36 '1': i32 | 128 | 37..38 '1': isize |
129 | 49..50 'x': *const [isize] | ||
130 | 69..73 '&[1]': &[isize; _] | ||
131 | 70..73 '[1]': [isize; _] | ||
132 | 71..72 '1': isize | ||
128 | "###); | 133 | "###); |
129 | } | 134 | } |
130 | 135 | ||
diff --git a/crates/ra_hir_ty/src/tests/patterns.rs b/crates/ra_hir_ty/src/tests/patterns.rs index 0c5f972a2..fe62587c0 100644 --- a/crates/ra_hir_ty/src/tests/patterns.rs +++ b/crates/ra_hir_ty/src/tests/patterns.rs | |||
@@ -520,3 +520,53 @@ fn main() { | |||
520 | 105..107 '()': () | 520 | 105..107 '()': () |
521 | ") | 521 | ") |
522 | } | 522 | } |
523 | |||
524 | #[test] | ||
525 | fn match_ergonomics_in_closure_params() { | ||
526 | assert_snapshot!( | ||
527 | infer(r#" | ||
528 | #[lang = "fn_once"] | ||
529 | trait FnOnce<Args> { | ||
530 | type Output; | ||
531 | } | ||
532 | |||
533 | fn foo<T, U, F: FnOnce(T) -> U>(t: T, f: F) -> U { loop {} } | ||
534 | |||
535 | fn test() { | ||
536 | foo(&(1, "a"), |&(x, y)| x); // normal, no match ergonomics | ||
537 | foo(&(1, "a"), |(x, y)| x); | ||
538 | } | ||
539 | "#), | ||
540 | @r###" | ||
541 | 94..95 't': T | ||
542 | 100..101 'f': F | ||
543 | 111..122 '{ loop {} }': U | ||
544 | 113..120 'loop {}': ! | ||
545 | 118..120 '{}': () | ||
546 | 134..233 '{ ... x); }': () | ||
547 | 140..143 'foo': fn foo<&(i32, &str), i32, |&(i32, &str)| -> i32>(&(i32, &str), |&(i32, &str)| -> i32) -> i32 | ||
548 | 140..167 'foo(&(...y)| x)': i32 | ||
549 | 144..153 '&(1, "a")': &(i32, &str) | ||
550 | 145..153 '(1, "a")': (i32, &str) | ||
551 | 146..147 '1': i32 | ||
552 | 149..152 '"a"': &str | ||
553 | 155..166 '|&(x, y)| x': |&(i32, &str)| -> i32 | ||
554 | 156..163 '&(x, y)': &(i32, &str) | ||
555 | 157..163 '(x, y)': (i32, &str) | ||
556 | 158..159 'x': i32 | ||
557 | 161..162 'y': &str | ||
558 | 165..166 'x': i32 | ||
559 | 204..207 'foo': fn foo<&(i32, &str), &i32, |&(i32, &str)| -> &i32>(&(i32, &str), |&(i32, &str)| -> &i32) -> &i32 | ||
560 | 204..230 'foo(&(...y)| x)': &i32 | ||
561 | 208..217 '&(1, "a")': &(i32, &str) | ||
562 | 209..217 '(1, "a")': (i32, &str) | ||
563 | 210..211 '1': i32 | ||
564 | 213..216 '"a"': &str | ||
565 | 219..229 '|(x, y)| x': |&(i32, &str)| -> &i32 | ||
566 | 220..226 '(x, y)': (i32, &str) | ||
567 | 221..222 'x': &i32 | ||
568 | 224..225 'y': &&str | ||
569 | 228..229 'x': &i32 | ||
570 | "### | ||
571 | ); | ||
572 | } | ||
diff --git a/crates/ra_hir_ty/src/tests/simple.rs b/crates/ra_hir_ty/src/tests/simple.rs index fd2208af2..88309157b 100644 --- a/crates/ra_hir_ty/src/tests/simple.rs +++ b/crates/ra_hir_ty/src/tests/simple.rs | |||
@@ -385,6 +385,26 @@ fn test(a: &u32, b: &mut u32, c: *const u32, d: *mut u32) { | |||
385 | } | 385 | } |
386 | 386 | ||
387 | #[test] | 387 | #[test] |
388 | fn infer_raw_ref() { | ||
389 | assert_snapshot!( | ||
390 | infer(r#" | ||
391 | fn test(a: i32) { | ||
392 | &raw mut a; | ||
393 | &raw const a; | ||
394 | } | ||
395 | "#), | ||
396 | @r###" | ||
397 | 9..10 'a': i32 | ||
398 | 17..54 '{ ...t a; }': () | ||
399 | 23..33 '&raw mut a': *mut i32 | ||
400 | 32..33 'a': i32 | ||
401 | 39..51 '&raw const a': *const i32 | ||
402 | 50..51 'a': i32 | ||
403 | "### | ||
404 | ); | ||
405 | } | ||
406 | |||
407 | #[test] | ||
388 | fn infer_literals() { | 408 | fn infer_literals() { |
389 | assert_snapshot!( | 409 | assert_snapshot!( |
390 | infer(r##" | 410 | infer(r##" |
@@ -937,7 +957,7 @@ fn main(foo: Foo) { | |||
937 | 51..107 'if tru... }': () | 957 | 51..107 'if tru... }': () |
938 | 54..58 'true': bool | 958 | 54..58 'true': bool |
939 | 59..67 '{ }': () | 959 | 59..67 '{ }': () |
940 | 73..107 'if fal... }': () | 960 | 73..107 'if fal... }': i32 |
941 | 76..81 'false': bool | 961 | 76..81 'false': bool |
942 | 82..107 '{ ... }': i32 | 962 | 82..107 '{ ... }': i32 |
943 | 92..95 'foo': Foo | 963 | 92..95 'foo': Foo |
@@ -1923,3 +1943,57 @@ fn test() { | |||
1923 | "### | 1943 | "### |
1924 | ); | 1944 | ); |
1925 | } | 1945 | } |
1946 | |||
1947 | #[test] | ||
1948 | fn infer_labelled_break_with_val() { | ||
1949 | assert_snapshot!( | ||
1950 | infer(r#" | ||
1951 | fn foo() { | ||
1952 | let _x = || 'outer: loop { | ||
1953 | let inner = 'inner: loop { | ||
1954 | let i = Default::default(); | ||
1955 | if (break 'outer i) { | ||
1956 | loop { break 'inner 5i8; }; | ||
1957 | } else if true { | ||
1958 | break 'inner 6; | ||
1959 | } | ||
1960 | break 7; | ||
1961 | }; | ||
1962 | break inner < 8; | ||
1963 | }; | ||
1964 | } | ||
1965 | "#), | ||
1966 | @r###" | ||
1967 | 10..336 '{ ... }; }': () | ||
1968 | 20..22 '_x': || -> bool | ||
1969 | 25..333 '|| 'ou... }': || -> bool | ||
1970 | 28..333 ''outer... }': bool | ||
1971 | 41..333 '{ ... }': () | ||
1972 | 55..60 'inner': i8 | ||
1973 | 63..301 ''inner... }': i8 | ||
1974 | 76..301 '{ ... }': () | ||
1975 | 94..95 'i': bool | ||
1976 | 98..114 'Defaul...efault': {unknown} | ||
1977 | 98..116 'Defaul...ault()': bool | ||
1978 | 130..270 'if (br... }': () | ||
1979 | 134..148 'break 'outer i': ! | ||
1980 | 147..148 'i': bool | ||
1981 | 150..209 '{ ... }': () | ||
1982 | 168..194 'loop {...5i8; }': ! | ||
1983 | 173..194 '{ brea...5i8; }': () | ||
1984 | 175..191 'break ...er 5i8': ! | ||
1985 | 188..191 '5i8': i8 | ||
1986 | 215..270 'if tru... }': () | ||
1987 | 218..222 'true': bool | ||
1988 | 223..270 '{ ... }': () | ||
1989 | 241..255 'break 'inner 6': ! | ||
1990 | 254..255 '6': i8 | ||
1991 | 283..290 'break 7': ! | ||
1992 | 289..290 '7': i8 | ||
1993 | 311..326 'break inner < 8': ! | ||
1994 | 317..322 'inner': i8 | ||
1995 | 317..326 'inner < 8': bool | ||
1996 | 325..326 '8': i8 | ||
1997 | "### | ||
1998 | ); | ||
1999 | } | ||
diff --git a/crates/ra_hir_ty/src/tests/traits.rs b/crates/ra_hir_ty/src/tests/traits.rs index 6826610cb..e8778d419 100644 --- a/crates/ra_hir_ty/src/tests/traits.rs +++ b/crates/ra_hir_ty/src/tests/traits.rs | |||
@@ -2644,6 +2644,79 @@ fn test() { | |||
2644 | } | 2644 | } |
2645 | 2645 | ||
2646 | #[test] | 2646 | #[test] |
2647 | fn builtin_fn_def_copy() { | ||
2648 | assert_snapshot!( | ||
2649 | infer_with_mismatches(r#" | ||
2650 | #[lang = "copy"] | ||
2651 | trait Copy {} | ||
2652 | |||
2653 | fn foo() {} | ||
2654 | fn bar<T: Copy>(T) -> T {} | ||
2655 | struct Struct(usize); | ||
2656 | enum Enum { Variant(usize) } | ||
2657 | |||
2658 | trait Test { fn test(&self) -> bool; } | ||
2659 | impl<T: Copy> Test for T {} | ||
2660 | |||
2661 | fn test() { | ||
2662 | foo.test(); | ||
2663 | bar.test(); | ||
2664 | Struct.test(); | ||
2665 | Enum::Variant.test(); | ||
2666 | } | ||
2667 | "#, true), | ||
2668 | @r###" | ||
2669 | 42..44 '{}': () | ||
2670 | 61..62 'T': {unknown} | ||
2671 | 69..71 '{}': () | ||
2672 | 69..71: expected T, got () | ||
2673 | 146..150 'self': &Self | ||
2674 | 202..282 '{ ...t(); }': () | ||
2675 | 208..211 'foo': fn foo() | ||
2676 | 208..218 'foo.test()': bool | ||
2677 | 224..227 'bar': fn bar<{unknown}>({unknown}) -> {unknown} | ||
2678 | 224..234 'bar.test()': bool | ||
2679 | 240..246 'Struct': Struct(usize) -> Struct | ||
2680 | 240..253 'Struct.test()': bool | ||
2681 | 259..272 'Enum::Variant': Variant(usize) -> Enum | ||
2682 | 259..279 'Enum::...test()': bool | ||
2683 | "### | ||
2684 | ); | ||
2685 | } | ||
2686 | |||
2687 | #[test] | ||
2688 | fn builtin_fn_ptr_copy() { | ||
2689 | assert_snapshot!( | ||
2690 | infer_with_mismatches(r#" | ||
2691 | #[lang = "copy"] | ||
2692 | trait Copy {} | ||
2693 | |||
2694 | trait Test { fn test(&self) -> bool; } | ||
2695 | impl<T: Copy> Test for T {} | ||
2696 | |||
2697 | fn test(f1: fn(), f2: fn(usize) -> u8, f3: fn(u8, u8) -> &u8) { | ||
2698 | f1.test(); | ||
2699 | f2.test(); | ||
2700 | f3.test(); | ||
2701 | } | ||
2702 | "#, true), | ||
2703 | @r###" | ||
2704 | 55..59 'self': &Self | ||
2705 | 109..111 'f1': fn() | ||
2706 | 119..121 'f2': fn(usize) -> u8 | ||
2707 | 140..142 'f3': fn(u8, u8) -> &u8 | ||
2708 | 163..211 '{ ...t(); }': () | ||
2709 | 169..171 'f1': fn() | ||
2710 | 169..178 'f1.test()': bool | ||
2711 | 184..186 'f2': fn(usize) -> u8 | ||
2712 | 184..193 'f2.test()': bool | ||
2713 | 199..201 'f3': fn(u8, u8) -> &u8 | ||
2714 | 199..208 'f3.test()': bool | ||
2715 | "### | ||
2716 | ); | ||
2717 | } | ||
2718 | |||
2719 | #[test] | ||
2647 | fn builtin_sized() { | 2720 | fn builtin_sized() { |
2648 | assert_snapshot!( | 2721 | assert_snapshot!( |
2649 | infer_with_mismatches(r#" | 2722 | infer_with_mismatches(r#" |
@@ -2680,3 +2753,48 @@ fn test() { | |||
2680 | "### | 2753 | "### |
2681 | ); | 2754 | ); |
2682 | } | 2755 | } |
2756 | |||
2757 | #[test] | ||
2758 | fn integer_range_iterate() { | ||
2759 | let t = type_at( | ||
2760 | r#" | ||
2761 | //- /main.rs crate:main deps:std | ||
2762 | fn test() { | ||
2763 | for x in 0..100 { x<|>; } | ||
2764 | } | ||
2765 | |||
2766 | //- /std.rs crate:std | ||
2767 | pub mod ops { | ||
2768 | pub struct Range<Idx> { | ||
2769 | pub start: Idx, | ||
2770 | pub end: Idx, | ||
2771 | } | ||
2772 | } | ||
2773 | |||
2774 | pub mod iter { | ||
2775 | pub trait Iterator { | ||
2776 | type Item; | ||
2777 | } | ||
2778 | |||
2779 | pub trait IntoIterator { | ||
2780 | type Item; | ||
2781 | type IntoIter: Iterator<Item = Self::Item>; | ||
2782 | } | ||
2783 | |||
2784 | impl<T> IntoIterator for T where T: Iterator { | ||
2785 | type Item = <T as Iterator>::Item; | ||
2786 | type IntoIter = Self; | ||
2787 | } | ||
2788 | } | ||
2789 | |||
2790 | trait Step {} | ||
2791 | impl Step for i32 {} | ||
2792 | impl Step for i64 {} | ||
2793 | |||
2794 | impl<A: Step> iter::Iterator for ops::Range<A> { | ||
2795 | type Item = A; | ||
2796 | } | ||
2797 | "#, | ||
2798 | ); | ||
2799 | assert_eq!(t, "i32"); | ||
2800 | } | ||
diff --git a/crates/ra_hir_ty/src/traits/builtin.rs b/crates/ra_hir_ty/src/traits/builtin.rs index ccab246bf..88a422d2c 100644 --- a/crates/ra_hir_ty/src/traits/builtin.rs +++ b/crates/ra_hir_ty/src/traits/builtin.rs | |||
@@ -290,8 +290,7 @@ fn trait_object_unsize_impl_datum( | |||
290 | let self_trait_ref = TraitRef { trait_, substs: self_substs }; | 290 | let self_trait_ref = TraitRef { trait_, substs: self_substs }; |
291 | let where_clauses = vec![GenericPredicate::Implemented(self_trait_ref)]; | 291 | let where_clauses = vec![GenericPredicate::Implemented(self_trait_ref)]; |
292 | 292 | ||
293 | let impl_substs = | 293 | let impl_substs = Substs::builder(2).push(self_ty).push(Ty::Dyn(target_bounds.into())).build(); |
294 | Substs::builder(2).push(self_ty).push(Ty::Dyn(target_bounds.clone().into())).build(); | ||
295 | 294 | ||
296 | let trait_ref = TraitRef { trait_: unsize_trait, substs: impl_substs }; | 295 | let trait_ref = TraitRef { trait_: unsize_trait, substs: impl_substs }; |
297 | 296 | ||
diff --git a/crates/ra_hir_ty/src/traits/chalk.rs b/crates/ra_hir_ty/src/traits/chalk.rs index e2f2a9ccb..61de3cc30 100644 --- a/crates/ra_hir_ty/src/traits/chalk.rs +++ b/crates/ra_hir_ty/src/traits/chalk.rs | |||
@@ -4,6 +4,7 @@ use std::sync::Arc; | |||
4 | use log::debug; | 4 | use log::debug; |
5 | 5 | ||
6 | use chalk_ir::{fold::shift::Shift, GenericArg, TypeName}; | 6 | use chalk_ir::{fold::shift::Shift, GenericArg, TypeName}; |
7 | use chalk_solve::rust_ir::{self, WellKnownTrait}; | ||
7 | 8 | ||
8 | use hir_def::{ | 9 | use hir_def::{ |
9 | lang_item::{lang_attr, LangItemTarget}, | 10 | lang_item::{lang_attr, LangItemTarget}, |
@@ -14,9 +15,8 @@ use ra_db::{salsa::InternKey, CrateId}; | |||
14 | use super::{builtin, AssocTyValue, ChalkContext, Impl}; | 15 | use super::{builtin, AssocTyValue, ChalkContext, Impl}; |
15 | use crate::{ | 16 | use crate::{ |
16 | db::HirDatabase, display::HirDisplay, method_resolution::TyFingerprint, utils::generics, | 17 | db::HirDatabase, display::HirDisplay, method_resolution::TyFingerprint, utils::generics, |
17 | DebruijnIndex, GenericPredicate, Substs, Ty, TypeCtor, | 18 | CallableDef, DebruijnIndex, GenericPredicate, Substs, Ty, TypeCtor, |
18 | }; | 19 | }; |
19 | use chalk_rust_ir::WellKnownTrait; | ||
20 | use mapping::{convert_where_clauses, generic_predicate_to_inline_bound, make_binders}; | 20 | use mapping::{convert_where_clauses, generic_predicate_to_inline_bound, make_binders}; |
21 | 21 | ||
22 | pub use self::interner::*; | 22 | pub use self::interner::*; |
@@ -54,10 +54,9 @@ impl<'a> chalk_solve::RustIrDatabase<Interner> for ChalkContext<'a> { | |||
54 | 54 | ||
55 | fn fn_def_datum( | 55 | fn fn_def_datum( |
56 | &self, | 56 | &self, |
57 | _fn_def_id: chalk_ir::FnDefId<Interner>, | 57 | fn_def_id: chalk_ir::FnDefId<Interner>, |
58 | ) -> Arc<chalk_rust_ir::FnDefDatum<Interner>> { | 58 | ) -> Arc<rust_ir::FnDefDatum<Interner>> { |
59 | // We don't yet provide any FnDefs to Chalk | 59 | self.db.fn_def_datum(self.krate, fn_def_id) |
60 | unimplemented!() | ||
61 | } | 60 | } |
62 | 61 | ||
63 | fn impls_for_trait( | 62 | fn impls_for_trait( |
@@ -113,7 +112,7 @@ impl<'a> chalk_solve::RustIrDatabase<Interner> for ChalkContext<'a> { | |||
113 | } | 112 | } |
114 | fn well_known_trait_id( | 113 | fn well_known_trait_id( |
115 | &self, | 114 | &self, |
116 | well_known_trait: chalk_rust_ir::WellKnownTrait, | 115 | well_known_trait: rust_ir::WellKnownTrait, |
117 | ) -> Option<chalk_ir::TraitId<Interner>> { | 116 | ) -> Option<chalk_ir::TraitId<Interner>> { |
118 | let lang_attr = lang_attr_from_well_known_trait(well_known_trait); | 117 | let lang_attr = lang_attr_from_well_known_trait(well_known_trait); |
119 | let lang_items = self.db.crate_lang_items(self.krate); | 118 | let lang_items = self.db.crate_lang_items(self.krate); |
@@ -134,13 +133,13 @@ impl<'a> chalk_solve::RustIrDatabase<Interner> for ChalkContext<'a> { | |||
134 | fn opaque_ty_data( | 133 | fn opaque_ty_data( |
135 | &self, | 134 | &self, |
136 | _id: chalk_ir::OpaqueTyId<Interner>, | 135 | _id: chalk_ir::OpaqueTyId<Interner>, |
137 | ) -> Arc<chalk_rust_ir::OpaqueTyDatum<Interner>> { | 136 | ) -> Arc<rust_ir::OpaqueTyDatum<Interner>> { |
138 | unimplemented!() | 137 | unimplemented!() |
139 | } | 138 | } |
140 | 139 | ||
141 | fn force_impl_for( | 140 | fn force_impl_for( |
142 | &self, | 141 | &self, |
143 | _well_known: chalk_rust_ir::WellKnownTrait, | 142 | _well_known: rust_ir::WellKnownTrait, |
144 | _ty: &chalk_ir::TyData<Interner>, | 143 | _ty: &chalk_ir::TyData<Interner>, |
145 | ) -> Option<bool> { | 144 | ) -> Option<bool> { |
146 | // this method is mostly for rustc | 145 | // this method is mostly for rustc |
@@ -151,6 +150,10 @@ impl<'a> chalk_solve::RustIrDatabase<Interner> for ChalkContext<'a> { | |||
151 | // FIXME: implement actual object safety | 150 | // FIXME: implement actual object safety |
152 | true | 151 | true |
153 | } | 152 | } |
153 | |||
154 | fn hidden_opaque_type(&self, _id: chalk_ir::OpaqueTyId<Interner>) -> chalk_ir::Ty<Interner> { | ||
155 | Ty::Unknown.to_chalk(self.db) | ||
156 | } | ||
154 | } | 157 | } |
155 | 158 | ||
156 | pub(crate) fn program_clauses_for_chalk_env_query( | 159 | pub(crate) fn program_clauses_for_chalk_env_query( |
@@ -189,7 +192,7 @@ pub(crate) fn associated_ty_data_query( | |||
189 | .collect(); | 192 | .collect(); |
190 | 193 | ||
191 | let where_clauses = convert_where_clauses(db, type_alias.into(), &bound_vars); | 194 | let where_clauses = convert_where_clauses(db, type_alias.into(), &bound_vars); |
192 | let bound_data = chalk_rust_ir::AssociatedTyDatumBound { bounds, where_clauses }; | 195 | let bound_data = rust_ir::AssociatedTyDatumBound { bounds, where_clauses }; |
193 | let datum = AssociatedTyDatum { | 196 | let datum = AssociatedTyDatum { |
194 | trait_id: trait_.to_chalk(db), | 197 | trait_id: trait_.to_chalk(db), |
195 | id, | 198 | id, |
@@ -210,7 +213,7 @@ pub(crate) fn trait_datum_query( | |||
210 | debug!("trait {:?} = {:?}", trait_id, trait_data.name); | 213 | debug!("trait {:?} = {:?}", trait_id, trait_data.name); |
211 | let generic_params = generics(db.upcast(), trait_.into()); | 214 | let generic_params = generics(db.upcast(), trait_.into()); |
212 | let bound_vars = Substs::bound_vars(&generic_params, DebruijnIndex::INNERMOST); | 215 | let bound_vars = Substs::bound_vars(&generic_params, DebruijnIndex::INNERMOST); |
213 | let flags = chalk_rust_ir::TraitFlags { | 216 | let flags = rust_ir::TraitFlags { |
214 | auto: trait_data.auto, | 217 | auto: trait_data.auto, |
215 | upstream: trait_.lookup(db.upcast()).container.module(db.upcast()).krate != krate, | 218 | upstream: trait_.lookup(db.upcast()).container.module(db.upcast()).krate != krate, |
216 | non_enumerable: true, | 219 | non_enumerable: true, |
@@ -222,7 +225,7 @@ pub(crate) fn trait_datum_query( | |||
222 | let where_clauses = convert_where_clauses(db, trait_.into(), &bound_vars); | 225 | let where_clauses = convert_where_clauses(db, trait_.into(), &bound_vars); |
223 | let associated_ty_ids = | 226 | let associated_ty_ids = |
224 | trait_data.associated_types().map(|type_alias| type_alias.to_chalk(db)).collect(); | 227 | trait_data.associated_types().map(|type_alias| type_alias.to_chalk(db)).collect(); |
225 | let trait_datum_bound = chalk_rust_ir::TraitDatumBound { where_clauses }; | 228 | let trait_datum_bound = rust_ir::TraitDatumBound { where_clauses }; |
226 | let well_known = | 229 | let well_known = |
227 | lang_attr(db.upcast(), trait_).and_then(|name| well_known_trait_from_lang_attr(&name)); | 230 | lang_attr(db.upcast(), trait_).and_then(|name| well_known_trait_from_lang_attr(&name)); |
228 | let trait_datum = TraitDatum { | 231 | let trait_datum = TraitDatum { |
@@ -272,12 +275,12 @@ pub(crate) fn struct_datum_query( | |||
272 | convert_where_clauses(db, generic_def, &bound_vars) | 275 | convert_where_clauses(db, generic_def, &bound_vars) |
273 | }) | 276 | }) |
274 | .unwrap_or_else(Vec::new); | 277 | .unwrap_or_else(Vec::new); |
275 | let flags = chalk_rust_ir::AdtFlags { | 278 | let flags = rust_ir::AdtFlags { |
276 | upstream, | 279 | upstream, |
277 | // FIXME set fundamental flag correctly | 280 | // FIXME set fundamental flag correctly |
278 | fundamental: false, | 281 | fundamental: false, |
279 | }; | 282 | }; |
280 | let struct_datum_bound = chalk_rust_ir::AdtDatumBound { | 283 | let struct_datum_bound = rust_ir::AdtDatumBound { |
281 | fields: Vec::new(), // FIXME add fields (only relevant for auto traits) | 284 | fields: Vec::new(), // FIXME add fields (only relevant for auto traits) |
282 | where_clauses, | 285 | where_clauses, |
283 | }; | 286 | }; |
@@ -317,9 +320,9 @@ fn impl_def_datum( | |||
317 | let bound_vars = Substs::bound_vars(&generic_params, DebruijnIndex::INNERMOST); | 320 | let bound_vars = Substs::bound_vars(&generic_params, DebruijnIndex::INNERMOST); |
318 | let trait_ = trait_ref.trait_; | 321 | let trait_ = trait_ref.trait_; |
319 | let impl_type = if impl_id.lookup(db.upcast()).container.module(db.upcast()).krate == krate { | 322 | let impl_type = if impl_id.lookup(db.upcast()).container.module(db.upcast()).krate == krate { |
320 | chalk_rust_ir::ImplType::Local | 323 | rust_ir::ImplType::Local |
321 | } else { | 324 | } else { |
322 | chalk_rust_ir::ImplType::External | 325 | rust_ir::ImplType::External |
323 | }; | 326 | }; |
324 | let where_clauses = convert_where_clauses(db, impl_id.into(), &bound_vars); | 327 | let where_clauses = convert_where_clauses(db, impl_id.into(), &bound_vars); |
325 | let negative = impl_data.is_negative; | 328 | let negative = impl_data.is_negative; |
@@ -332,13 +335,9 @@ fn impl_def_datum( | |||
332 | ); | 335 | ); |
333 | let trait_ref = trait_ref.to_chalk(db); | 336 | let trait_ref = trait_ref.to_chalk(db); |
334 | 337 | ||
335 | let polarity = if negative { | 338 | let polarity = if negative { rust_ir::Polarity::Negative } else { rust_ir::Polarity::Positive }; |
336 | chalk_rust_ir::Polarity::Negative | ||
337 | } else { | ||
338 | chalk_rust_ir::Polarity::Positive | ||
339 | }; | ||
340 | 339 | ||
341 | let impl_datum_bound = chalk_rust_ir::ImplDatumBound { trait_ref, where_clauses }; | 340 | let impl_datum_bound = rust_ir::ImplDatumBound { trait_ref, where_clauses }; |
342 | let trait_data = db.trait_data(trait_); | 341 | let trait_data = db.trait_data(trait_); |
343 | let associated_ty_value_ids = impl_data | 342 | let associated_ty_value_ids = impl_data |
344 | .items | 343 | .items |
@@ -396,8 +395,8 @@ fn type_alias_associated_ty_value( | |||
396 | .associated_type_by_name(&type_alias_data.name) | 395 | .associated_type_by_name(&type_alias_data.name) |
397 | .expect("assoc ty value should not exist"); // validated when building the impl data as well | 396 | .expect("assoc ty value should not exist"); // validated when building the impl data as well |
398 | let ty = db.ty(type_alias.into()); | 397 | let ty = db.ty(type_alias.into()); |
399 | let value_bound = chalk_rust_ir::AssociatedTyValueBound { ty: ty.value.to_chalk(db) }; | 398 | let value_bound = rust_ir::AssociatedTyValueBound { ty: ty.value.to_chalk(db) }; |
400 | let value = chalk_rust_ir::AssociatedTyValue { | 399 | let value = rust_ir::AssociatedTyValue { |
401 | impl_id: Impl::ImplDef(impl_id).to_chalk(db), | 400 | impl_id: Impl::ImplDef(impl_id).to_chalk(db), |
402 | associated_ty_id: assoc_ty.to_chalk(db), | 401 | associated_ty_id: assoc_ty.to_chalk(db), |
403 | value: make_binders(value_bound, ty.num_binders), | 402 | value: make_binders(value_bound, ty.num_binders), |
@@ -405,6 +404,26 @@ fn type_alias_associated_ty_value( | |||
405 | Arc::new(value) | 404 | Arc::new(value) |
406 | } | 405 | } |
407 | 406 | ||
407 | pub(crate) fn fn_def_datum_query( | ||
408 | db: &dyn HirDatabase, | ||
409 | _krate: CrateId, | ||
410 | fn_def_id: FnDefId, | ||
411 | ) -> Arc<FnDefDatum> { | ||
412 | let callable_def: CallableDef = from_chalk(db, fn_def_id); | ||
413 | let generic_params = generics(db.upcast(), callable_def.into()); | ||
414 | let sig = db.callable_item_signature(callable_def); | ||
415 | let bound_vars = Substs::bound_vars(&generic_params, DebruijnIndex::INNERMOST); | ||
416 | let where_clauses = convert_where_clauses(db, callable_def.into(), &bound_vars); | ||
417 | let bound = rust_ir::FnDefDatumBound { | ||
418 | // Note: Chalk doesn't actually use this information yet as far as I am aware, but we provide it anyway | ||
419 | argument_types: sig.value.params().iter().map(|ty| ty.clone().to_chalk(db)).collect(), | ||
420 | return_type: sig.value.ret().clone().to_chalk(db), | ||
421 | where_clauses, | ||
422 | }; | ||
423 | let datum = FnDefDatum { id: fn_def_id, binders: make_binders(bound, sig.num_binders) }; | ||
424 | Arc::new(datum) | ||
425 | } | ||
426 | |||
408 | impl From<AdtId> for crate::TypeCtorId { | 427 | impl From<AdtId> for crate::TypeCtorId { |
409 | fn from(struct_id: AdtId) -> Self { | 428 | fn from(struct_id: AdtId) -> Self { |
410 | struct_id.0 | 429 | struct_id.0 |
@@ -417,6 +436,18 @@ impl From<crate::TypeCtorId> for AdtId { | |||
417 | } | 436 | } |
418 | } | 437 | } |
419 | 438 | ||
439 | impl From<FnDefId> for crate::CallableDefId { | ||
440 | fn from(fn_def_id: FnDefId) -> Self { | ||
441 | InternKey::from_intern_id(fn_def_id.0) | ||
442 | } | ||
443 | } | ||
444 | |||
445 | impl From<crate::CallableDefId> for FnDefId { | ||
446 | fn from(callable_def_id: crate::CallableDefId) -> Self { | ||
447 | chalk_ir::FnDefId(callable_def_id.as_intern_id()) | ||
448 | } | ||
449 | } | ||
450 | |||
420 | impl From<ImplId> for crate::traits::GlobalImplId { | 451 | impl From<ImplId> for crate::traits::GlobalImplId { |
421 | fn from(impl_id: ImplId) -> Self { | 452 | fn from(impl_id: ImplId) -> Self { |
422 | InternKey::from_intern_id(impl_id.0) | 453 | InternKey::from_intern_id(impl_id.0) |
@@ -429,14 +460,14 @@ impl From<crate::traits::GlobalImplId> for ImplId { | |||
429 | } | 460 | } |
430 | } | 461 | } |
431 | 462 | ||
432 | impl From<chalk_rust_ir::AssociatedTyValueId<Interner>> for crate::traits::AssocTyValueId { | 463 | impl From<rust_ir::AssociatedTyValueId<Interner>> for crate::traits::AssocTyValueId { |
433 | fn from(id: chalk_rust_ir::AssociatedTyValueId<Interner>) -> Self { | 464 | fn from(id: rust_ir::AssociatedTyValueId<Interner>) -> Self { |
434 | Self::from_intern_id(id.0) | 465 | Self::from_intern_id(id.0) |
435 | } | 466 | } |
436 | } | 467 | } |
437 | 468 | ||
438 | impl From<crate::traits::AssocTyValueId> for chalk_rust_ir::AssociatedTyValueId<Interner> { | 469 | impl From<crate::traits::AssocTyValueId> for rust_ir::AssociatedTyValueId<Interner> { |
439 | fn from(assoc_ty_value_id: crate::traits::AssocTyValueId) -> Self { | 470 | fn from(assoc_ty_value_id: crate::traits::AssocTyValueId) -> Self { |
440 | chalk_rust_ir::AssociatedTyValueId(assoc_ty_value_id.as_intern_id()) | 471 | rust_ir::AssociatedTyValueId(assoc_ty_value_id.as_intern_id()) |
441 | } | 472 | } |
442 | } | 473 | } |
diff --git a/crates/ra_hir_ty/src/traits/chalk/interner.rs b/crates/ra_hir_ty/src/traits/chalk/interner.rs index 060372819..e27074ba6 100644 --- a/crates/ra_hir_ty/src/traits/chalk/interner.rs +++ b/crates/ra_hir_ty/src/traits/chalk/interner.rs | |||
@@ -11,15 +11,17 @@ use std::{fmt, sync::Arc}; | |||
11 | pub struct Interner; | 11 | pub struct Interner; |
12 | 12 | ||
13 | pub type AssocTypeId = chalk_ir::AssocTypeId<Interner>; | 13 | pub type AssocTypeId = chalk_ir::AssocTypeId<Interner>; |
14 | pub type AssociatedTyDatum = chalk_rust_ir::AssociatedTyDatum<Interner>; | 14 | pub type AssociatedTyDatum = chalk_solve::rust_ir::AssociatedTyDatum<Interner>; |
15 | pub type TraitId = chalk_ir::TraitId<Interner>; | 15 | pub type TraitId = chalk_ir::TraitId<Interner>; |
16 | pub type TraitDatum = chalk_rust_ir::TraitDatum<Interner>; | 16 | pub type TraitDatum = chalk_solve::rust_ir::TraitDatum<Interner>; |
17 | pub type AdtId = chalk_ir::AdtId<Interner>; | 17 | pub type AdtId = chalk_ir::AdtId<Interner>; |
18 | pub type StructDatum = chalk_rust_ir::AdtDatum<Interner>; | 18 | pub type StructDatum = chalk_solve::rust_ir::AdtDatum<Interner>; |
19 | pub type ImplId = chalk_ir::ImplId<Interner>; | 19 | pub type ImplId = chalk_ir::ImplId<Interner>; |
20 | pub type ImplDatum = chalk_rust_ir::ImplDatum<Interner>; | 20 | pub type ImplDatum = chalk_solve::rust_ir::ImplDatum<Interner>; |
21 | pub type AssociatedTyValueId = chalk_rust_ir::AssociatedTyValueId<Interner>; | 21 | pub type AssociatedTyValueId = chalk_solve::rust_ir::AssociatedTyValueId<Interner>; |
22 | pub type AssociatedTyValue = chalk_rust_ir::AssociatedTyValue<Interner>; | 22 | pub type AssociatedTyValue = chalk_solve::rust_ir::AssociatedTyValue<Interner>; |
23 | pub type FnDefId = chalk_ir::FnDefId<Interner>; | ||
24 | pub type FnDefDatum = chalk_solve::rust_ir::FnDefDatum<Interner>; | ||
23 | 25 | ||
24 | impl chalk_ir::interner::Interner for Interner { | 26 | impl chalk_ir::interner::Interner for Interner { |
25 | type InternedType = Box<chalk_ir::TyData<Self>>; // FIXME use Arc? | 27 | type InternedType = Box<chalk_ir::TyData<Self>>; // FIXME use Arc? |
diff --git a/crates/ra_hir_ty/src/traits/chalk/mapping.rs b/crates/ra_hir_ty/src/traits/chalk/mapping.rs index a83d82fd8..5f6daf842 100644 --- a/crates/ra_hir_ty/src/traits/chalk/mapping.rs +++ b/crates/ra_hir_ty/src/traits/chalk/mapping.rs | |||
@@ -7,6 +7,7 @@ use chalk_ir::{ | |||
7 | cast::Cast, fold::shift::Shift, interner::HasInterner, PlaceholderIndex, Scalar, TypeName, | 7 | cast::Cast, fold::shift::Shift, interner::HasInterner, PlaceholderIndex, Scalar, TypeName, |
8 | UniverseIndex, | 8 | UniverseIndex, |
9 | }; | 9 | }; |
10 | use chalk_solve::rust_ir; | ||
10 | 11 | ||
11 | use hir_def::{type_ref::Mutability, AssocContainerId, GenericDefId, Lookup, TypeAliasId}; | 12 | use hir_def::{type_ref::Mutability, AssocContainerId, GenericDefId, Lookup, TypeAliasId}; |
12 | use ra_db::salsa::InternKey; | 13 | use ra_db::salsa::InternKey; |
@@ -15,8 +16,8 @@ use crate::{ | |||
15 | db::HirDatabase, | 16 | db::HirDatabase, |
16 | primitive::{FloatBitness, FloatTy, IntBitness, IntTy, Signedness, Uncertain}, | 17 | primitive::{FloatBitness, FloatTy, IntBitness, IntTy, Signedness, Uncertain}, |
17 | traits::{builtin, AssocTyValue, Canonical, Impl, Obligation}, | 18 | traits::{builtin, AssocTyValue, Canonical, Impl, Obligation}, |
18 | ApplicationTy, GenericPredicate, InEnvironment, ProjectionPredicate, ProjectionTy, Substs, | 19 | ApplicationTy, CallableDef, GenericPredicate, InEnvironment, ProjectionPredicate, ProjectionTy, |
19 | TraitEnvironment, TraitRef, Ty, TypeCtor, | 20 | Substs, TraitEnvironment, TraitRef, Ty, TypeCtor, |
20 | }; | 21 | }; |
21 | 22 | ||
22 | use super::interner::*; | 23 | use super::interner::*; |
@@ -26,14 +27,19 @@ impl ToChalk for Ty { | |||
26 | type Chalk = chalk_ir::Ty<Interner>; | 27 | type Chalk = chalk_ir::Ty<Interner>; |
27 | fn to_chalk(self, db: &dyn HirDatabase) -> chalk_ir::Ty<Interner> { | 28 | fn to_chalk(self, db: &dyn HirDatabase) -> chalk_ir::Ty<Interner> { |
28 | match self { | 29 | match self { |
29 | Ty::Apply(apply_ty) => { | 30 | Ty::Apply(apply_ty) => match apply_ty.ctor { |
30 | if let TypeCtor::Ref(m) = apply_ty.ctor { | 31 | TypeCtor::Ref(m) => ref_to_chalk(db, m, apply_ty.parameters), |
31 | return ref_to_chalk(db, m, apply_ty.parameters); | 32 | TypeCtor::FnPtr { num_args: _ } => { |
33 | let substitution = apply_ty.parameters.to_chalk(db).shifted_in(&Interner); | ||
34 | chalk_ir::TyData::Function(chalk_ir::Fn { num_binders: 0, substitution }) | ||
35 | .intern(&Interner) | ||
32 | } | 36 | } |
33 | let name = apply_ty.ctor.to_chalk(db); | 37 | _ => { |
34 | let substitution = apply_ty.parameters.to_chalk(db); | 38 | let name = apply_ty.ctor.to_chalk(db); |
35 | chalk_ir::ApplicationTy { name, substitution }.cast(&Interner).intern(&Interner) | 39 | let substitution = apply_ty.parameters.to_chalk(db); |
36 | } | 40 | chalk_ir::ApplicationTy { name, substitution }.cast(&Interner).intern(&Interner) |
41 | } | ||
42 | }, | ||
37 | Ty::Projection(proj_ty) => { | 43 | Ty::Projection(proj_ty) => { |
38 | let associated_ty_id = proj_ty.associated_ty.to_chalk(db); | 44 | let associated_ty_id = proj_ty.associated_ty.to_chalk(db); |
39 | let substitution = proj_ty.parameters.to_chalk(db); | 45 | let substitution = proj_ty.parameters.to_chalk(db); |
@@ -93,9 +99,15 @@ impl ToChalk for Ty { | |||
93 | Ty::Projection(ProjectionTy { associated_ty, parameters }) | 99 | Ty::Projection(ProjectionTy { associated_ty, parameters }) |
94 | } | 100 | } |
95 | chalk_ir::TyData::Alias(chalk_ir::AliasTy::Opaque(_)) => unimplemented!(), | 101 | chalk_ir::TyData::Alias(chalk_ir::AliasTy::Opaque(_)) => unimplemented!(), |
96 | chalk_ir::TyData::Function(_) => unimplemented!(), | 102 | chalk_ir::TyData::Function(chalk_ir::Fn { num_binders: _, substitution }) => { |
103 | let parameters: Substs = from_chalk(db, substitution); | ||
104 | Ty::Apply(ApplicationTy { | ||
105 | ctor: TypeCtor::FnPtr { num_args: (parameters.len() - 1) as u16 }, | ||
106 | parameters, | ||
107 | }) | ||
108 | } | ||
97 | chalk_ir::TyData::BoundVar(idx) => Ty::Bound(idx), | 109 | chalk_ir::TyData::BoundVar(idx) => Ty::Bound(idx), |
98 | chalk_ir::TyData::InferenceVar(_iv) => Ty::Unknown, | 110 | chalk_ir::TyData::InferenceVar(_iv, _kind) => Ty::Unknown, |
99 | chalk_ir::TyData::Dyn(where_clauses) => { | 111 | chalk_ir::TyData::Dyn(where_clauses) => { |
100 | assert_eq!(where_clauses.bounds.binders.len(&Interner), 1); | 112 | assert_eq!(where_clauses.bounds.binders.len(&Interner), 1); |
101 | let predicates = where_clauses | 113 | let predicates = where_clauses |
@@ -217,13 +229,17 @@ impl ToChalk for TypeCtor { | |||
217 | TypeCtor::Slice => TypeName::Slice, | 229 | TypeCtor::Slice => TypeName::Slice, |
218 | TypeCtor::Ref(mutability) => TypeName::Ref(mutability.to_chalk(db)), | 230 | TypeCtor::Ref(mutability) => TypeName::Ref(mutability.to_chalk(db)), |
219 | TypeCtor::Str => TypeName::Str, | 231 | TypeCtor::Str => TypeName::Str, |
232 | TypeCtor::FnDef(callable_def) => { | ||
233 | let id = callable_def.to_chalk(db); | ||
234 | TypeName::FnDef(id) | ||
235 | } | ||
236 | TypeCtor::Never => TypeName::Never, | ||
237 | |||
220 | TypeCtor::Int(Uncertain::Unknown) | 238 | TypeCtor::Int(Uncertain::Unknown) |
221 | | TypeCtor::Float(Uncertain::Unknown) | 239 | | TypeCtor::Float(Uncertain::Unknown) |
222 | | TypeCtor::Adt(_) | 240 | | TypeCtor::Adt(_) |
223 | | TypeCtor::Array | 241 | | TypeCtor::Array |
224 | | TypeCtor::FnDef(_) | ||
225 | | TypeCtor::FnPtr { .. } | 242 | | TypeCtor::FnPtr { .. } |
226 | | TypeCtor::Never | ||
227 | | TypeCtor::Closure { .. } => { | 243 | | TypeCtor::Closure { .. } => { |
228 | // other TypeCtors get interned and turned into a chalk StructId | 244 | // other TypeCtors get interned and turned into a chalk StructId |
229 | let struct_id = db.intern_type_ctor(self).into(); | 245 | let struct_id = db.intern_type_ctor(self).into(); |
@@ -259,10 +275,14 @@ impl ToChalk for TypeCtor { | |||
259 | TypeName::Slice => TypeCtor::Slice, | 275 | TypeName::Slice => TypeCtor::Slice, |
260 | TypeName::Ref(mutability) => TypeCtor::Ref(from_chalk(db, mutability)), | 276 | TypeName::Ref(mutability) => TypeCtor::Ref(from_chalk(db, mutability)), |
261 | TypeName::Str => TypeCtor::Str, | 277 | TypeName::Str => TypeCtor::Str, |
278 | TypeName::Never => TypeCtor::Never, | ||
262 | 279 | ||
263 | TypeName::FnDef(_) => unreachable!(), | 280 | TypeName::FnDef(fn_def_id) => { |
281 | let callable_def = from_chalk(db, fn_def_id); | ||
282 | TypeCtor::FnDef(callable_def) | ||
283 | } | ||
264 | 284 | ||
265 | TypeName::Error => { | 285 | TypeName::Array | TypeName::Error => { |
266 | // this should not be reached, since we don't represent TypeName::Error with TypeCtor | 286 | // this should not be reached, since we don't represent TypeName::Error with TypeCtor |
267 | unreachable!() | 287 | unreachable!() |
268 | } | 288 | } |
@@ -347,6 +367,18 @@ impl ToChalk for Impl { | |||
347 | } | 367 | } |
348 | } | 368 | } |
349 | 369 | ||
370 | impl ToChalk for CallableDef { | ||
371 | type Chalk = FnDefId; | ||
372 | |||
373 | fn to_chalk(self, db: &dyn HirDatabase) -> FnDefId { | ||
374 | db.intern_callable_def(self).into() | ||
375 | } | ||
376 | |||
377 | fn from_chalk(db: &dyn HirDatabase, fn_def_id: FnDefId) -> CallableDef { | ||
378 | db.lookup_intern_callable_def(fn_def_id.into()) | ||
379 | } | ||
380 | } | ||
381 | |||
350 | impl ToChalk for TypeAliasId { | 382 | impl ToChalk for TypeAliasId { |
351 | type Chalk = AssocTypeId; | 383 | type Chalk = AssocTypeId; |
352 | 384 | ||
@@ -479,7 +511,7 @@ where | |||
479 | 511 | ||
480 | fn to_chalk(self, db: &dyn HirDatabase) -> chalk_ir::Canonical<T::Chalk> { | 512 | fn to_chalk(self, db: &dyn HirDatabase) -> chalk_ir::Canonical<T::Chalk> { |
481 | let parameter = chalk_ir::CanonicalVarKind::new( | 513 | let parameter = chalk_ir::CanonicalVarKind::new( |
482 | chalk_ir::VariableKind::Ty, | 514 | chalk_ir::VariableKind::Ty(chalk_ir::TyKind::General), |
483 | chalk_ir::UniverseIndex::ROOT, | 515 | chalk_ir::UniverseIndex::ROOT, |
484 | ); | 516 | ); |
485 | let value = self.value.to_chalk(db); | 517 | let value = self.value.to_chalk(db); |
@@ -550,17 +582,17 @@ impl ToChalk for builtin::BuiltinImplData { | |||
550 | type Chalk = ImplDatum; | 582 | type Chalk = ImplDatum; |
551 | 583 | ||
552 | fn to_chalk(self, db: &dyn HirDatabase) -> ImplDatum { | 584 | fn to_chalk(self, db: &dyn HirDatabase) -> ImplDatum { |
553 | let impl_type = chalk_rust_ir::ImplType::External; | 585 | let impl_type = rust_ir::ImplType::External; |
554 | let where_clauses = self.where_clauses.into_iter().map(|w| w.to_chalk(db)).collect(); | 586 | let where_clauses = self.where_clauses.into_iter().map(|w| w.to_chalk(db)).collect(); |
555 | 587 | ||
556 | let impl_datum_bound = | 588 | let impl_datum_bound = |
557 | chalk_rust_ir::ImplDatumBound { trait_ref: self.trait_ref.to_chalk(db), where_clauses }; | 589 | rust_ir::ImplDatumBound { trait_ref: self.trait_ref.to_chalk(db), where_clauses }; |
558 | let associated_ty_value_ids = | 590 | let associated_ty_value_ids = |
559 | self.assoc_ty_values.into_iter().map(|v| v.to_chalk(db)).collect(); | 591 | self.assoc_ty_values.into_iter().map(|v| v.to_chalk(db)).collect(); |
560 | chalk_rust_ir::ImplDatum { | 592 | rust_ir::ImplDatum { |
561 | binders: make_binders(impl_datum_bound, self.num_vars), | 593 | binders: make_binders(impl_datum_bound, self.num_vars), |
562 | impl_type, | 594 | impl_type, |
563 | polarity: chalk_rust_ir::Polarity::Positive, | 595 | polarity: rust_ir::Polarity::Positive, |
564 | associated_ty_value_ids, | 596 | associated_ty_value_ids, |
565 | } | 597 | } |
566 | } | 598 | } |
@@ -575,9 +607,9 @@ impl ToChalk for builtin::BuiltinImplAssocTyValueData { | |||
575 | 607 | ||
576 | fn to_chalk(self, db: &dyn HirDatabase) -> AssociatedTyValue { | 608 | fn to_chalk(self, db: &dyn HirDatabase) -> AssociatedTyValue { |
577 | let ty = self.value.to_chalk(db); | 609 | let ty = self.value.to_chalk(db); |
578 | let value_bound = chalk_rust_ir::AssociatedTyValueBound { ty }; | 610 | let value_bound = rust_ir::AssociatedTyValueBound { ty }; |
579 | 611 | ||
580 | chalk_rust_ir::AssociatedTyValue { | 612 | rust_ir::AssociatedTyValue { |
581 | associated_ty_id: self.assoc_ty_id.to_chalk(db), | 613 | associated_ty_id: self.assoc_ty_id.to_chalk(db), |
582 | impl_id: self.impl_.to_chalk(db), | 614 | impl_id: self.impl_.to_chalk(db), |
583 | value: make_binders(value_bound, self.num_vars), | 615 | value: make_binders(value_bound, self.num_vars), |
@@ -599,7 +631,7 @@ where | |||
599 | chalk_ir::Binders::new( | 631 | chalk_ir::Binders::new( |
600 | chalk_ir::VariableKinds::from( | 632 | chalk_ir::VariableKinds::from( |
601 | &Interner, | 633 | &Interner, |
602 | std::iter::repeat(chalk_ir::VariableKind::Ty).take(num_vars), | 634 | std::iter::repeat(chalk_ir::VariableKind::Ty(chalk_ir::TyKind::General)).take(num_vars), |
603 | ), | 635 | ), |
604 | value, | 636 | value, |
605 | ) | 637 | ) |
@@ -626,7 +658,7 @@ pub(super) fn generic_predicate_to_inline_bound( | |||
626 | db: &dyn HirDatabase, | 658 | db: &dyn HirDatabase, |
627 | pred: &GenericPredicate, | 659 | pred: &GenericPredicate, |
628 | self_ty: &Ty, | 660 | self_ty: &Ty, |
629 | ) -> Option<chalk_rust_ir::InlineBound<Interner>> { | 661 | ) -> Option<rust_ir::InlineBound<Interner>> { |
630 | // An InlineBound is like a GenericPredicate, except the self type is left out. | 662 | // An InlineBound is like a GenericPredicate, except the self type is left out. |
631 | // We don't have a special type for this, but Chalk does. | 663 | // We don't have a special type for this, but Chalk does. |
632 | match pred { | 664 | match pred { |
@@ -641,8 +673,8 @@ pub(super) fn generic_predicate_to_inline_bound( | |||
641 | .map(|ty| ty.clone().to_chalk(db).cast(&Interner)) | 673 | .map(|ty| ty.clone().to_chalk(db).cast(&Interner)) |
642 | .collect(); | 674 | .collect(); |
643 | let trait_bound = | 675 | let trait_bound = |
644 | chalk_rust_ir::TraitBound { trait_id: trait_ref.trait_.to_chalk(db), args_no_self }; | 676 | rust_ir::TraitBound { trait_id: trait_ref.trait_.to_chalk(db), args_no_self }; |
645 | Some(chalk_rust_ir::InlineBound::TraitBound(trait_bound)) | 677 | Some(rust_ir::InlineBound::TraitBound(trait_bound)) |
646 | } | 678 | } |
647 | GenericPredicate::Projection(proj) => { | 679 | GenericPredicate::Projection(proj) => { |
648 | if &proj.projection_ty.parameters[0] != self_ty { | 680 | if &proj.projection_ty.parameters[0] != self_ty { |
@@ -656,16 +688,13 @@ pub(super) fn generic_predicate_to_inline_bound( | |||
656 | .iter() | 688 | .iter() |
657 | .map(|ty| ty.clone().to_chalk(db).cast(&Interner)) | 689 | .map(|ty| ty.clone().to_chalk(db).cast(&Interner)) |
658 | .collect(); | 690 | .collect(); |
659 | let alias_eq_bound = chalk_rust_ir::AliasEqBound { | 691 | let alias_eq_bound = rust_ir::AliasEqBound { |
660 | value: proj.ty.clone().to_chalk(db), | 692 | value: proj.ty.clone().to_chalk(db), |
661 | trait_bound: chalk_rust_ir::TraitBound { | 693 | trait_bound: rust_ir::TraitBound { trait_id: trait_.to_chalk(db), args_no_self }, |
662 | trait_id: trait_.to_chalk(db), | ||
663 | args_no_self, | ||
664 | }, | ||
665 | associated_ty_id: proj.projection_ty.associated_ty.to_chalk(db), | 694 | associated_ty_id: proj.projection_ty.associated_ty.to_chalk(db), |
666 | parameters: Vec::new(), // FIXME we don't support generic associated types yet | 695 | parameters: Vec::new(), // FIXME we don't support generic associated types yet |
667 | }; | 696 | }; |
668 | Some(chalk_rust_ir::InlineBound::AliasEqBound(alias_eq_bound)) | 697 | Some(rust_ir::InlineBound::AliasEqBound(alias_eq_bound)) |
669 | } | 698 | } |
670 | GenericPredicate::Error => None, | 699 | GenericPredicate::Error => None, |
671 | } | 700 | } |
diff --git a/crates/ra_hir_ty/src/traits/chalk/tls.rs b/crates/ra_hir_ty/src/traits/chalk/tls.rs index ebf402a07..d88828c7c 100644 --- a/crates/ra_hir_ty/src/traits/chalk/tls.rs +++ b/crates/ra_hir_ty/src/traits/chalk/tls.rs | |||
@@ -247,10 +247,24 @@ impl DebugContext<'_> { | |||
247 | 247 | ||
248 | pub fn debug_fn_def_id( | 248 | pub fn debug_fn_def_id( |
249 | &self, | 249 | &self, |
250 | _fn_def_id: chalk_ir::FnDefId<Interner>, | 250 | fn_def_id: chalk_ir::FnDefId<Interner>, |
251 | fmt: &mut fmt::Formatter<'_>, | 251 | fmt: &mut fmt::Formatter<'_>, |
252 | ) -> Result<(), fmt::Error> { | 252 | ) -> Result<(), fmt::Error> { |
253 | write!(fmt, "fn") | 253 | let def: CallableDef = from_chalk(self.0, fn_def_id); |
254 | let name = match def { | ||
255 | CallableDef::FunctionId(ff) => self.0.function_data(ff).name.clone(), | ||
256 | CallableDef::StructId(s) => self.0.struct_data(s).name.clone(), | ||
257 | CallableDef::EnumVariantId(e) => { | ||
258 | let enum_data = self.0.enum_data(e.parent); | ||
259 | enum_data.variants[e.local_id].name.clone() | ||
260 | } | ||
261 | }; | ||
262 | match def { | ||
263 | CallableDef::FunctionId(_) => write!(fmt, "{{fn {}}}", name), | ||
264 | CallableDef::StructId(_) | CallableDef::EnumVariantId(_) => { | ||
265 | write!(fmt, "{{ctor {}}}", name) | ||
266 | } | ||
267 | } | ||
254 | } | 268 | } |
255 | 269 | ||
256 | pub fn debug_const( | 270 | pub fn debug_const( |
diff --git a/crates/ra_ide/src/call_hierarchy.rs b/crates/ra_ide/src/call_hierarchy.rs index 85d1f0cb1..defd8176f 100644 --- a/crates/ra_ide/src/call_hierarchy.rs +++ b/crates/ra_ide/src/call_hierarchy.rs | |||
@@ -246,6 +246,35 @@ mod tests { | |||
246 | } | 246 | } |
247 | 247 | ||
248 | #[test] | 248 | #[test] |
249 | fn test_call_hierarchy_in_tests_mod() { | ||
250 | check_hierarchy( | ||
251 | r#" | ||
252 | //- /lib.rs cfg:test | ||
253 | fn callee() {} | ||
254 | fn caller1() { | ||
255 | call<|>ee(); | ||
256 | } | ||
257 | |||
258 | #[cfg(test)] | ||
259 | mod tests { | ||
260 | use super::*; | ||
261 | |||
262 | #[test] | ||
263 | fn test_caller() { | ||
264 | callee(); | ||
265 | } | ||
266 | } | ||
267 | "#, | ||
268 | "callee FN_DEF FileId(1) 0..14 3..9", | ||
269 | &[ | ||
270 | "caller1 FN_DEF FileId(1) 15..45 18..25 : [34..40]", | ||
271 | "test_caller FN_DEF FileId(1) 93..147 108..119 : [132..138]", | ||
272 | ], | ||
273 | &[], | ||
274 | ); | ||
275 | } | ||
276 | |||
277 | #[test] | ||
249 | fn test_call_hierarchy_in_different_files() { | 278 | fn test_call_hierarchy_in_different_files() { |
250 | check_hierarchy( | 279 | check_hierarchy( |
251 | r#" | 280 | r#" |
diff --git a/crates/ra_ide/src/completion.rs b/crates/ra_ide/src/completion.rs index 191300704..a721e23c6 100644 --- a/crates/ra_ide/src/completion.rs +++ b/crates/ra_ide/src/completion.rs | |||
@@ -1,5 +1,3 @@ | |||
1 | //! FIXME: write short doc here | ||
2 | |||
3 | mod completion_config; | 1 | mod completion_config; |
4 | mod completion_item; | 2 | mod completion_item; |
5 | mod completion_context; | 3 | mod completion_context; |
@@ -35,6 +33,51 @@ pub use crate::completion::{ | |||
35 | completion_item::{CompletionItem, CompletionItemKind, CompletionScore, InsertTextFormat}, | 33 | completion_item::{CompletionItem, CompletionItemKind, CompletionScore, InsertTextFormat}, |
36 | }; | 34 | }; |
37 | 35 | ||
36 | //FIXME: split the following feature into fine-grained features. | ||
37 | |||
38 | // Feature: Magic Completions | ||
39 | // | ||
40 | // In addition to usual reference completion, rust-analyzer provides some ✨magic✨ | ||
41 | // completions as well: | ||
42 | // | ||
43 | // Keywords like `if`, `else` `while`, `loop` are completed with braces, and cursor | ||
44 | // is placed at the appropriate position. Even though `if` is easy to type, you | ||
45 | // still want to complete it, to get ` { }` for free! `return` is inserted with a | ||
46 | // space or `;` depending on the return type of the function. | ||
47 | // | ||
48 | // When completing a function call, `()` are automatically inserted. If a function | ||
49 | // takes arguments, the cursor is positioned inside the parenthesis. | ||
50 | // | ||
51 | // There are postfix completions, which can be triggered by typing something like | ||
52 | // `foo().if`. The word after `.` determines postfix completion. Possible variants are: | ||
53 | // | ||
54 | // - `expr.if` -> `if expr {}` or `if let ... {}` for `Option` or `Result` | ||
55 | // - `expr.match` -> `match expr {}` | ||
56 | // - `expr.while` -> `while expr {}` or `while let ... {}` for `Option` or `Result` | ||
57 | // - `expr.ref` -> `&expr` | ||
58 | // - `expr.refm` -> `&mut expr` | ||
59 | // - `expr.not` -> `!expr` | ||
60 | // - `expr.dbg` -> `dbg!(expr)` | ||
61 | // | ||
62 | // There also snippet completions: | ||
63 | // | ||
64 | // .Expressions | ||
65 | // - `pd` -> `println!("{:?}")` | ||
66 | // - `ppd` -> `println!("{:#?}")` | ||
67 | // | ||
68 | // .Items | ||
69 | // - `tfn` -> `#[test] fn f(){}` | ||
70 | // - `tmod` -> | ||
71 | // ```rust | ||
72 | // #[cfg(test)] | ||
73 | // mod tests { | ||
74 | // use super::*; | ||
75 | // | ||
76 | // #[test] | ||
77 | // fn test_fn() {} | ||
78 | // } | ||
79 | // ``` | ||
80 | |||
38 | /// Main entry point for completion. We run completion as a two-phase process. | 81 | /// Main entry point for completion. We run completion as a two-phase process. |
39 | /// | 82 | /// |
40 | /// First, we look at the position and collect a so-called `CompletionContext. | 83 | /// First, we look at the position and collect a so-called `CompletionContext. |
@@ -82,3 +125,81 @@ pub(crate) fn completions( | |||
82 | 125 | ||
83 | Some(acc) | 126 | Some(acc) |
84 | } | 127 | } |
128 | |||
129 | #[cfg(test)] | ||
130 | mod tests { | ||
131 | use crate::completion::completion_config::CompletionConfig; | ||
132 | use crate::mock_analysis::analysis_and_position; | ||
133 | |||
134 | struct DetailAndDocumentation<'a> { | ||
135 | detail: &'a str, | ||
136 | documentation: &'a str, | ||
137 | } | ||
138 | |||
139 | fn check_detail_and_documentation(fixture: &str, expected: DetailAndDocumentation) { | ||
140 | let (analysis, position) = analysis_and_position(fixture); | ||
141 | let config = CompletionConfig::default(); | ||
142 | let completions = analysis.completions(&config, position).unwrap().unwrap(); | ||
143 | for item in completions { | ||
144 | if item.detail() == Some(expected.detail) { | ||
145 | let opt = item.documentation(); | ||
146 | let doc = opt.as_ref().map(|it| it.as_str()); | ||
147 | assert_eq!(doc, Some(expected.documentation)); | ||
148 | return; | ||
149 | } | ||
150 | } | ||
151 | panic!("completion detail not found: {}", expected.detail) | ||
152 | } | ||
153 | |||
154 | #[test] | ||
155 | fn test_completion_detail_from_macro_generated_struct_fn_doc_attr() { | ||
156 | check_detail_and_documentation( | ||
157 | r#" | ||
158 | //- /lib.rs | ||
159 | macro_rules! bar { | ||
160 | () => { | ||
161 | struct Bar; | ||
162 | impl Bar { | ||
163 | #[doc = "Do the foo"] | ||
164 | fn foo(&self) {} | ||
165 | } | ||
166 | } | ||
167 | } | ||
168 | |||
169 | bar!(); | ||
170 | |||
171 | fn foo() { | ||
172 | let bar = Bar; | ||
173 | bar.fo<|>; | ||
174 | } | ||
175 | "#, | ||
176 | DetailAndDocumentation { detail: "fn foo(&self)", documentation: "Do the foo" }, | ||
177 | ); | ||
178 | } | ||
179 | |||
180 | #[test] | ||
181 | fn test_completion_detail_from_macro_generated_struct_fn_doc_comment() { | ||
182 | check_detail_and_documentation( | ||
183 | r#" | ||
184 | //- /lib.rs | ||
185 | macro_rules! bar { | ||
186 | () => { | ||
187 | struct Bar; | ||
188 | impl Bar { | ||
189 | /// Do the foo | ||
190 | fn foo(&self) {} | ||
191 | } | ||
192 | } | ||
193 | } | ||
194 | |||
195 | bar!(); | ||
196 | |||
197 | fn foo() { | ||
198 | let bar = Bar; | ||
199 | bar.fo<|>; | ||
200 | } | ||
201 | "#, | ||
202 | DetailAndDocumentation { detail: "fn foo(&self)", documentation: " Do the foo" }, | ||
203 | ); | ||
204 | } | ||
205 | } | ||
diff --git a/crates/ra_ide/src/completion/complete_attribute.rs b/crates/ra_ide/src/completion/complete_attribute.rs index f17266221..fb3f0b743 100644 --- a/crates/ra_ide/src/completion/complete_attribute.rs +++ b/crates/ra_ide/src/completion/complete_attribute.rs | |||
@@ -112,7 +112,7 @@ const ATTRIBUTES: &[AttrCompletion] = &[ | |||
112 | AttrCompletion { label: "repr", snippet: Some("repr(${0:C})"), should_be_inner: false }, | 112 | AttrCompletion { label: "repr", snippet: Some("repr(${0:C})"), should_be_inner: false }, |
113 | AttrCompletion { | 113 | AttrCompletion { |
114 | label: "should_panic", | 114 | label: "should_panic", |
115 | snippet: Some(r#"expected = "${0:reason}""#), | 115 | snippet: Some(r#"should_panic(expected = "${0:reason}")"#), |
116 | should_be_inner: false, | 116 | should_be_inner: false, |
117 | }, | 117 | }, |
118 | AttrCompletion { | 118 | AttrCompletion { |
@@ -571,7 +571,7 @@ mod tests { | |||
571 | label: "should_panic", | 571 | label: "should_panic", |
572 | source_range: 19..19, | 572 | source_range: 19..19, |
573 | delete: 19..19, | 573 | delete: 19..19, |
574 | insert: "expected = \"${0:reason}\"", | 574 | insert: "should_panic(expected = \"${0:reason}\")", |
575 | kind: Attribute, | 575 | kind: Attribute, |
576 | }, | 576 | }, |
577 | CompletionItem { | 577 | CompletionItem { |
@@ -810,7 +810,7 @@ mod tests { | |||
810 | label: "should_panic", | 810 | label: "should_panic", |
811 | source_range: 20..20, | 811 | source_range: 20..20, |
812 | delete: 20..20, | 812 | delete: 20..20, |
813 | insert: "expected = \"${0:reason}\"", | 813 | insert: "should_panic(expected = \"${0:reason}\")", |
814 | kind: Attribute, | 814 | kind: Attribute, |
815 | }, | 815 | }, |
816 | CompletionItem { | 816 | CompletionItem { |
diff --git a/crates/ra_ide/src/completion/complete_postfix.rs b/crates/ra_ide/src/completion/complete_postfix.rs index f2a52a407..59b58bf98 100644 --- a/crates/ra_ide/src/completion/complete_postfix.rs +++ b/crates/ra_ide/src/completion/complete_postfix.rs | |||
@@ -1,12 +1,11 @@ | |||
1 | //! FIXME: write short doc here | 1 | //! FIXME: write short doc here |
2 | 2 | use ra_assists::utils::TryEnum; | |
3 | use ra_syntax::{ | 3 | use ra_syntax::{ |
4 | ast::{self, AstNode}, | 4 | ast::{self, AstNode}, |
5 | TextRange, TextSize, | 5 | TextRange, TextSize, |
6 | }; | 6 | }; |
7 | use ra_text_edit::TextEdit; | 7 | use ra_text_edit::TextEdit; |
8 | 8 | ||
9 | use super::completion_config::SnippetCap; | ||
10 | use crate::{ | 9 | use crate::{ |
11 | completion::{ | 10 | completion::{ |
12 | completion_context::CompletionContext, | 11 | completion_context::CompletionContext, |
@@ -14,7 +13,8 @@ use crate::{ | |||
14 | }, | 13 | }, |
15 | CompletionItem, | 14 | CompletionItem, |
16 | }; | 15 | }; |
17 | use ra_assists::utils::TryEnum; | 16 | |
17 | use super::completion_config::SnippetCap; | ||
18 | 18 | ||
19 | pub(super) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) { | 19 | pub(super) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) { |
20 | if !ctx.config.enable_postfix_completions { | 20 | if !ctx.config.enable_postfix_completions { |
@@ -184,6 +184,16 @@ pub(super) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) { | |||
184 | &format!("dbg!({})", receiver_text), | 184 | &format!("dbg!({})", receiver_text), |
185 | ) | 185 | ) |
186 | .add_to(acc); | 186 | .add_to(acc); |
187 | |||
188 | postfix_snippet( | ||
189 | ctx, | ||
190 | cap, | ||
191 | &dot_receiver, | ||
192 | "call", | ||
193 | "function(expr)", | ||
194 | &format!("${{1}}({})", receiver_text), | ||
195 | ) | ||
196 | .add_to(acc); | ||
187 | } | 197 | } |
188 | 198 | ||
189 | fn get_receiver_text(receiver: &ast::Expr, receiver_is_ambiguous_float_literal: bool) -> String { | 199 | fn get_receiver_text(receiver: &ast::Expr, receiver_is_ambiguous_float_literal: bool) -> String { |
@@ -256,6 +266,13 @@ mod tests { | |||
256 | detail: "Box::new(expr)", | 266 | detail: "Box::new(expr)", |
257 | }, | 267 | }, |
258 | CompletionItem { | 268 | CompletionItem { |
269 | label: "call", | ||
270 | source_range: 89..89, | ||
271 | delete: 85..89, | ||
272 | insert: "${1}(bar)", | ||
273 | detail: "function(expr)", | ||
274 | }, | ||
275 | CompletionItem { | ||
259 | label: "dbg", | 276 | label: "dbg", |
260 | source_range: 89..89, | 277 | source_range: 89..89, |
261 | delete: 85..89, | 278 | delete: 85..89, |
@@ -335,6 +352,13 @@ mod tests { | |||
335 | detail: "Box::new(expr)", | 352 | detail: "Box::new(expr)", |
336 | }, | 353 | }, |
337 | CompletionItem { | 354 | CompletionItem { |
355 | label: "call", | ||
356 | source_range: 210..210, | ||
357 | delete: 206..210, | ||
358 | insert: "${1}(bar)", | ||
359 | detail: "function(expr)", | ||
360 | }, | ||
361 | CompletionItem { | ||
338 | label: "dbg", | 362 | label: "dbg", |
339 | source_range: 210..210, | 363 | source_range: 210..210, |
340 | delete: 206..210, | 364 | delete: 206..210, |
@@ -414,6 +438,13 @@ mod tests { | |||
414 | detail: "Box::new(expr)", | 438 | detail: "Box::new(expr)", |
415 | }, | 439 | }, |
416 | CompletionItem { | 440 | CompletionItem { |
441 | label: "call", | ||
442 | source_range: 211..211, | ||
443 | delete: 207..211, | ||
444 | insert: "${1}(bar)", | ||
445 | detail: "function(expr)", | ||
446 | }, | ||
447 | CompletionItem { | ||
417 | label: "dbg", | 448 | label: "dbg", |
418 | source_range: 211..211, | 449 | source_range: 211..211, |
419 | delete: 207..211, | 450 | delete: 207..211, |
@@ -488,6 +519,13 @@ mod tests { | |||
488 | detail: "Box::new(expr)", | 519 | detail: "Box::new(expr)", |
489 | }, | 520 | }, |
490 | CompletionItem { | 521 | CompletionItem { |
522 | label: "call", | ||
523 | source_range: 91..91, | ||
524 | delete: 87..91, | ||
525 | insert: "${1}(bar)", | ||
526 | detail: "function(expr)", | ||
527 | }, | ||
528 | CompletionItem { | ||
491 | label: "dbg", | 529 | label: "dbg", |
492 | source_range: 91..91, | 530 | source_range: 91..91, |
493 | delete: 87..91, | 531 | delete: 87..91, |
@@ -547,6 +585,13 @@ mod tests { | |||
547 | detail: "Box::new(expr)", | 585 | detail: "Box::new(expr)", |
548 | }, | 586 | }, |
549 | CompletionItem { | 587 | CompletionItem { |
588 | label: "call", | ||
589 | source_range: 52..52, | ||
590 | delete: 49..52, | ||
591 | insert: "${1}(42)", | ||
592 | detail: "function(expr)", | ||
593 | }, | ||
594 | CompletionItem { | ||
550 | label: "dbg", | 595 | label: "dbg", |
551 | source_range: 52..52, | 596 | source_range: 52..52, |
552 | delete: 49..52, | 597 | delete: 49..52, |
@@ -608,6 +653,13 @@ mod tests { | |||
608 | detail: "Box::new(expr)", | 653 | detail: "Box::new(expr)", |
609 | }, | 654 | }, |
610 | CompletionItem { | 655 | CompletionItem { |
656 | label: "call", | ||
657 | source_range: 149..150, | ||
658 | delete: 145..150, | ||
659 | insert: "${1}(bar)", | ||
660 | detail: "function(expr)", | ||
661 | }, | ||
662 | CompletionItem { | ||
611 | label: "dbg", | 663 | label: "dbg", |
612 | source_range: 149..150, | 664 | source_range: 149..150, |
613 | delete: 145..150, | 665 | delete: 145..150, |
@@ -667,6 +719,13 @@ mod tests { | |||
667 | detail: "Box::new(expr)", | 719 | detail: "Box::new(expr)", |
668 | }, | 720 | }, |
669 | CompletionItem { | 721 | CompletionItem { |
722 | label: "call", | ||
723 | source_range: 56..56, | ||
724 | delete: 49..56, | ||
725 | insert: "${1}(&&&&42)", | ||
726 | detail: "function(expr)", | ||
727 | }, | ||
728 | CompletionItem { | ||
670 | label: "dbg", | 729 | label: "dbg", |
671 | source_range: 56..56, | 730 | source_range: 56..56, |
672 | delete: 49..56, | 731 | delete: 49..56, |
diff --git a/crates/ra_ide/src/completion/complete_trait_impl.rs b/crates/ra_ide/src/completion/complete_trait_impl.rs index 039df03e0..21c9316e6 100644 --- a/crates/ra_ide/src/completion/complete_trait_impl.rs +++ b/crates/ra_ide/src/completion/complete_trait_impl.rs | |||
@@ -49,56 +49,53 @@ use crate::{ | |||
49 | pub(crate) fn complete_trait_impl(acc: &mut Completions, ctx: &CompletionContext) { | 49 | pub(crate) fn complete_trait_impl(acc: &mut Completions, ctx: &CompletionContext) { |
50 | if let Some((trigger, impl_def)) = completion_match(ctx) { | 50 | if let Some((trigger, impl_def)) = completion_match(ctx) { |
51 | match trigger.kind() { | 51 | match trigger.kind() { |
52 | SyntaxKind::NAME_REF => { | 52 | SyntaxKind::NAME_REF => get_missing_assoc_items(&ctx.sema, &impl_def) |
53 | get_missing_assoc_items(&ctx.sema, &impl_def).iter().for_each(|item| match item { | 53 | .into_iter() |
54 | .for_each(|item| match item { | ||
54 | hir::AssocItem::Function(fn_item) => { | 55 | hir::AssocItem::Function(fn_item) => { |
55 | add_function_impl(&trigger, acc, ctx, &fn_item) | 56 | add_function_impl(&trigger, acc, ctx, fn_item) |
56 | } | 57 | } |
57 | hir::AssocItem::TypeAlias(type_item) => { | 58 | hir::AssocItem::TypeAlias(type_item) => { |
58 | add_type_alias_impl(&trigger, acc, ctx, &type_item) | 59 | add_type_alias_impl(&trigger, acc, ctx, type_item) |
59 | } | 60 | } |
60 | hir::AssocItem::Const(const_item) => { | 61 | hir::AssocItem::Const(const_item) => { |
61 | add_const_impl(&trigger, acc, ctx, &const_item) | 62 | add_const_impl(&trigger, acc, ctx, const_item) |
62 | } | 63 | } |
63 | }) | 64 | }), |
64 | } | ||
65 | 65 | ||
66 | SyntaxKind::FN_DEF => { | 66 | SyntaxKind::FN_DEF => { |
67 | for missing_fn in | 67 | for missing_fn in get_missing_assoc_items(&ctx.sema, &impl_def) |
68 | get_missing_assoc_items(&ctx.sema, &impl_def).iter().filter_map(|item| { | 68 | .into_iter() |
69 | match item { | 69 | .filter_map(|item| match item { |
70 | hir::AssocItem::Function(fn_item) => Some(fn_item), | 70 | hir::AssocItem::Function(fn_item) => Some(fn_item), |
71 | _ => None, | 71 | _ => None, |
72 | } | ||
73 | }) | 72 | }) |
74 | { | 73 | { |
75 | add_function_impl(&trigger, acc, ctx, &missing_fn); | 74 | add_function_impl(&trigger, acc, ctx, missing_fn); |
76 | } | 75 | } |
77 | } | 76 | } |
78 | 77 | ||
79 | SyntaxKind::TYPE_ALIAS_DEF => { | 78 | SyntaxKind::TYPE_ALIAS_DEF => { |
80 | for missing_fn in | 79 | for missing_fn in get_missing_assoc_items(&ctx.sema, &impl_def) |
81 | get_missing_assoc_items(&ctx.sema, &impl_def).iter().filter_map(|item| { | 80 | .into_iter() |
82 | match item { | 81 | .filter_map(|item| match item { |
83 | hir::AssocItem::TypeAlias(type_item) => Some(type_item), | 82 | hir::AssocItem::TypeAlias(type_item) => Some(type_item), |
84 | _ => None, | 83 | _ => None, |
85 | } | ||
86 | }) | 84 | }) |
87 | { | 85 | { |
88 | add_type_alias_impl(&trigger, acc, ctx, &missing_fn); | 86 | add_type_alias_impl(&trigger, acc, ctx, missing_fn); |
89 | } | 87 | } |
90 | } | 88 | } |
91 | 89 | ||
92 | SyntaxKind::CONST_DEF => { | 90 | SyntaxKind::CONST_DEF => { |
93 | for missing_fn in | 91 | for missing_fn in get_missing_assoc_items(&ctx.sema, &impl_def) |
94 | get_missing_assoc_items(&ctx.sema, &impl_def).iter().filter_map(|item| { | 92 | .into_iter() |
95 | match item { | 93 | .filter_map(|item| match item { |
96 | hir::AssocItem::Const(const_item) => Some(const_item), | 94 | hir::AssocItem::Const(const_item) => Some(const_item), |
97 | _ => None, | 95 | _ => None, |
98 | } | ||
99 | }) | 96 | }) |
100 | { | 97 | { |
101 | add_const_impl(&trigger, acc, ctx, &missing_fn); | 98 | add_const_impl(&trigger, acc, ctx, missing_fn); |
102 | } | 99 | } |
103 | } | 100 | } |
104 | 101 | ||
@@ -126,9 +123,9 @@ fn add_function_impl( | |||
126 | fn_def_node: &SyntaxNode, | 123 | fn_def_node: &SyntaxNode, |
127 | acc: &mut Completions, | 124 | acc: &mut Completions, |
128 | ctx: &CompletionContext, | 125 | ctx: &CompletionContext, |
129 | func: &hir::Function, | 126 | func: hir::Function, |
130 | ) { | 127 | ) { |
131 | let signature = FunctionSignature::from_hir(ctx.db, *func); | 128 | let signature = FunctionSignature::from_hir(ctx.db, func); |
132 | 129 | ||
133 | let fn_name = func.name(ctx.db).to_string(); | 130 | let fn_name = func.name(ctx.db).to_string(); |
134 | 131 | ||
@@ -167,7 +164,7 @@ fn add_type_alias_impl( | |||
167 | type_def_node: &SyntaxNode, | 164 | type_def_node: &SyntaxNode, |
168 | acc: &mut Completions, | 165 | acc: &mut Completions, |
169 | ctx: &CompletionContext, | 166 | ctx: &CompletionContext, |
170 | type_alias: &hir::TypeAlias, | 167 | type_alias: hir::TypeAlias, |
171 | ) { | 168 | ) { |
172 | let alias_name = type_alias.name(ctx.db).to_string(); | 169 | let alias_name = type_alias.name(ctx.db).to_string(); |
173 | 170 | ||
@@ -187,7 +184,7 @@ fn add_const_impl( | |||
187 | const_def_node: &SyntaxNode, | 184 | const_def_node: &SyntaxNode, |
188 | acc: &mut Completions, | 185 | acc: &mut Completions, |
189 | ctx: &CompletionContext, | 186 | ctx: &CompletionContext, |
190 | const_: &hir::Const, | 187 | const_: hir::Const, |
191 | ) { | 188 | ) { |
192 | let const_name = const_.name(ctx.db).map(|n| n.to_string()); | 189 | let const_name = const_.name(ctx.db).map(|n| n.to_string()); |
193 | 190 | ||
diff --git a/crates/ra_ide/src/completion/complete_unqualified_path.rs b/crates/ra_ide/src/completion/complete_unqualified_path.rs index db791660a..68032c37e 100644 --- a/crates/ra_ide/src/completion/complete_unqualified_path.rs +++ b/crates/ra_ide/src/completion/complete_unqualified_path.rs | |||
@@ -298,6 +298,42 @@ mod tests { | |||
298 | } | 298 | } |
299 | 299 | ||
300 | #[test] | 300 | #[test] |
301 | fn completes_bindings_from_for_with_in_prefix() { | ||
302 | mark::check!(completes_bindings_from_for_with_in_prefix); | ||
303 | assert_debug_snapshot!( | ||
304 | do_reference_completion( | ||
305 | r" | ||
306 | fn test() { | ||
307 | for index in &[1, 2, 3] { | ||
308 | let t = in<|> | ||
309 | } | ||
310 | } | ||
311 | " | ||
312 | ), | ||
313 | @r###" | ||
314 | [ | ||
315 | CompletionItem { | ||
316 | label: "index", | ||
317 | source_range: 107..107, | ||
318 | delete: 107..107, | ||
319 | insert: "index", | ||
320 | kind: Binding, | ||
321 | }, | ||
322 | CompletionItem { | ||
323 | label: "test()", | ||
324 | source_range: 107..107, | ||
325 | delete: 107..107, | ||
326 | insert: "test()$0", | ||
327 | kind: Function, | ||
328 | lookup: "test", | ||
329 | detail: "fn test()", | ||
330 | }, | ||
331 | ] | ||
332 | "### | ||
333 | ); | ||
334 | } | ||
335 | |||
336 | #[test] | ||
301 | fn completes_generic_params() { | 337 | fn completes_generic_params() { |
302 | assert_debug_snapshot!( | 338 | assert_debug_snapshot!( |
303 | do_reference_completion( | 339 | do_reference_completion( |
diff --git a/crates/ra_ide/src/completion/completion_context.rs b/crates/ra_ide/src/completion/completion_context.rs index da336973c..c4646b727 100644 --- a/crates/ra_ide/src/completion/completion_context.rs +++ b/crates/ra_ide/src/completion/completion_context.rs | |||
@@ -12,6 +12,7 @@ use ra_syntax::{ | |||
12 | use ra_text_edit::Indel; | 12 | use ra_text_edit::Indel; |
13 | 13 | ||
14 | use crate::{call_info::ActiveParameter, completion::CompletionConfig, FilePosition}; | 14 | use crate::{call_info::ActiveParameter, completion::CompletionConfig, FilePosition}; |
15 | use test_utils::mark; | ||
15 | 16 | ||
16 | /// `CompletionContext` is created early during completion to figure out, where | 17 | /// `CompletionContext` is created early during completion to figure out, where |
17 | /// exactly is the cursor, syntax-wise. | 18 | /// exactly is the cursor, syntax-wise. |
@@ -169,7 +170,17 @@ impl<'a> CompletionContext<'a> { | |||
169 | match self.token.kind() { | 170 | match self.token.kind() { |
170 | // workaroud when completion is triggered by trigger characters. | 171 | // workaroud when completion is triggered by trigger characters. |
171 | IDENT => self.original_token.text_range(), | 172 | IDENT => self.original_token.text_range(), |
172 | _ => TextRange::empty(self.offset), | 173 | _ => { |
174 | // If we haven't characters between keyword and our cursor we take the keyword start range to edit | ||
175 | if self.token.kind().is_keyword() | ||
176 | && self.offset == self.original_token.text_range().end() | ||
177 | { | ||
178 | mark::hit!(completes_bindings_from_for_with_in_prefix); | ||
179 | TextRange::empty(self.original_token.text_range().start()) | ||
180 | } else { | ||
181 | TextRange::empty(self.offset) | ||
182 | } | ||
183 | } | ||
173 | } | 184 | } |
174 | } | 185 | } |
175 | 186 | ||
diff --git a/crates/ra_ide/src/completion/presentation.rs b/crates/ra_ide/src/completion/presentation.rs index 440ffa31d..61565c84f 100644 --- a/crates/ra_ide/src/completion/presentation.rs +++ b/crates/ra_ide/src/completion/presentation.rs | |||
@@ -211,7 +211,7 @@ impl Completions { | |||
211 | .parameter_names | 211 | .parameter_names |
212 | .iter() | 212 | .iter() |
213 | .skip(if function_signature.has_self_param { 1 } else { 0 }) | 213 | .skip(if function_signature.has_self_param { 1 } else { 0 }) |
214 | .cloned() | 214 | .map(|name| name.trim_start_matches('_').into()) |
215 | .collect(); | 215 | .collect(); |
216 | 216 | ||
217 | builder = builder.add_call_parens(ctx, name, Params::Named(params)); | 217 | builder = builder.add_call_parens(ctx, name, Params::Named(params)); |
@@ -672,6 +672,37 @@ mod tests { | |||
672 | assert_debug_snapshot!( | 672 | assert_debug_snapshot!( |
673 | do_reference_completion( | 673 | do_reference_completion( |
674 | r" | 674 | r" |
675 | fn with_ignored_args(_foo: i32, ___bar: bool, ho_ge_: String) {} | ||
676 | fn main() { with_<|> } | ||
677 | " | ||
678 | ), | ||
679 | @r###" | ||
680 | [ | ||
681 | CompletionItem { | ||
682 | label: "main()", | ||
683 | source_range: 110..115, | ||
684 | delete: 110..115, | ||
685 | insert: "main()$0", | ||
686 | kind: Function, | ||
687 | lookup: "main", | ||
688 | detail: "fn main()", | ||
689 | }, | ||
690 | CompletionItem { | ||
691 | label: "with_ignored_args(…)", | ||
692 | source_range: 110..115, | ||
693 | delete: 110..115, | ||
694 | insert: "with_ignored_args(${1:foo}, ${2:bar}, ${3:ho_ge_})$0", | ||
695 | kind: Function, | ||
696 | lookup: "with_ignored_args", | ||
697 | detail: "fn with_ignored_args(_foo: i32, ___bar: bool, ho_ge_: String)", | ||
698 | trigger_call_info: true, | ||
699 | }, | ||
700 | ] | ||
701 | "### | ||
702 | ); | ||
703 | assert_debug_snapshot!( | ||
704 | do_reference_completion( | ||
705 | r" | ||
675 | struct S {} | 706 | struct S {} |
676 | impl S { | 707 | impl S { |
677 | fn foo(&self) {} | 708 | fn foo(&self) {} |
@@ -695,6 +726,33 @@ mod tests { | |||
695 | ] | 726 | ] |
696 | "### | 727 | "### |
697 | ); | 728 | ); |
729 | assert_debug_snapshot!( | ||
730 | do_reference_completion( | ||
731 | r" | ||
732 | struct S {} | ||
733 | impl S { | ||
734 | fn foo_ignored_args(&self, _a: bool, b: i32) {} | ||
735 | } | ||
736 | fn bar(s: &S) { | ||
737 | s.f<|> | ||
738 | } | ||
739 | " | ||
740 | ), | ||
741 | @r###" | ||
742 | [ | ||
743 | CompletionItem { | ||
744 | label: "foo_ignored_args(…)", | ||
745 | source_range: 194..195, | ||
746 | delete: 194..195, | ||
747 | insert: "foo_ignored_args(${1:a}, ${2:b})$0", | ||
748 | kind: Method, | ||
749 | lookup: "foo_ignored_args", | ||
750 | detail: "fn foo_ignored_args(&self, _a: bool, b: i32)", | ||
751 | trigger_call_info: true, | ||
752 | }, | ||
753 | ] | ||
754 | "### | ||
755 | ); | ||
698 | } | 756 | } |
699 | 757 | ||
700 | #[test] | 758 | #[test] |
diff --git a/crates/ra_ide/src/diagnostics.rs b/crates/ra_ide/src/diagnostics.rs index 3d83c0f71..15dc50cf1 100644 --- a/crates/ra_ide/src/diagnostics.rs +++ b/crates/ra_ide/src/diagnostics.rs | |||
@@ -664,7 +664,7 @@ mod tests { | |||
664 | assert_debug_snapshot!(diagnostics, @r###" | 664 | assert_debug_snapshot!(diagnostics, @r###" |
665 | [ | 665 | [ |
666 | Diagnostic { | 666 | Diagnostic { |
667 | message: "Missing structure fields:\n- b", | 667 | message: "Missing structure fields:\n- b\n", |
668 | range: 224..233, | 668 | range: 224..233, |
669 | severity: Error, | 669 | severity: Error, |
670 | fix: Some( | 670 | fix: Some( |
diff --git a/crates/ra_ide/src/display.rs b/crates/ra_ide/src/display.rs index 722092de9..827c094e7 100644 --- a/crates/ra_ide/src/display.rs +++ b/crates/ra_ide/src/display.rs | |||
@@ -79,16 +79,17 @@ pub(crate) fn rust_code_markup_with_doc( | |||
79 | doc: Option<&str>, | 79 | doc: Option<&str>, |
80 | mod_path: Option<&str>, | 80 | mod_path: Option<&str>, |
81 | ) -> String { | 81 | ) -> String { |
82 | let mut buf = "```rust\n".to_owned(); | 82 | let mut buf = String::new(); |
83 | 83 | ||
84 | if let Some(mod_path) = mod_path { | 84 | if let Some(mod_path) = mod_path { |
85 | if !mod_path.is_empty() { | 85 | if !mod_path.is_empty() { |
86 | format_to!(buf, "{}\n", mod_path); | 86 | format_to!(buf, "```rust\n{}\n```\n\n", mod_path); |
87 | } | 87 | } |
88 | } | 88 | } |
89 | format_to!(buf, "{}\n```", code); | 89 | format_to!(buf, "```rust\n{}\n```", code); |
90 | 90 | ||
91 | if let Some(doc) = doc { | 91 | if let Some(doc) = doc { |
92 | format_to!(buf, "\n___"); | ||
92 | format_to!(buf, "\n\n{}", doc); | 93 | format_to!(buf, "\n\n{}", doc); |
93 | } | 94 | } |
94 | 95 | ||
diff --git a/crates/ra_ide/src/display/function_signature.rs b/crates/ra_ide/src/display/function_signature.rs index 9572debd8..ca8a6a650 100644 --- a/crates/ra_ide/src/display/function_signature.rs +++ b/crates/ra_ide/src/display/function_signature.rs | |||
@@ -10,7 +10,7 @@ use std::{ | |||
10 | use hir::{Docs, Documentation, HasSource, HirDisplay}; | 10 | use hir::{Docs, Documentation, HasSource, HirDisplay}; |
11 | use ra_ide_db::RootDatabase; | 11 | use ra_ide_db::RootDatabase; |
12 | use ra_syntax::ast::{self, AstNode, NameOwner, VisibilityOwner}; | 12 | use ra_syntax::ast::{self, AstNode, NameOwner, VisibilityOwner}; |
13 | use stdx::SepBy; | 13 | use stdx::{split1, SepBy}; |
14 | 14 | ||
15 | use crate::display::{generic_parameters, where_predicates}; | 15 | use crate::display::{generic_parameters, where_predicates}; |
16 | 16 | ||
@@ -207,7 +207,16 @@ impl From<&'_ ast::FnDef> for FunctionSignature { | |||
207 | res.push(raw_param); | 207 | res.push(raw_param); |
208 | } | 208 | } |
209 | 209 | ||
210 | res.extend(param_list.params().map(|param| param.syntax().text().to_string())); | 210 | // macro-generated functions are missing whitespace |
211 | fn fmt_param(param: ast::Param) -> String { | ||
212 | let text = param.syntax().text().to_string(); | ||
213 | match split1(&text, ':') { | ||
214 | Some((left, right)) => format!("{}: {}", left.trim(), right.trim()), | ||
215 | _ => text, | ||
216 | } | ||
217 | } | ||
218 | |||
219 | res.extend(param_list.params().map(fmt_param)); | ||
211 | res_types.extend(param_list.params().map(|param| { | 220 | res_types.extend(param_list.params().map(|param| { |
212 | let param_text = param.syntax().text().to_string(); | 221 | let param_text = param.syntax().text().to_string(); |
213 | match param_text.split(':').nth(1).and_then(|it| it.get(1..)) { | 222 | match param_text.split(':').nth(1).and_then(|it| it.get(1..)) { |
diff --git a/crates/ra_ide/src/display/navigation_target.rs b/crates/ra_ide/src/display/navigation_target.rs index 5da28edd2..c7bb1e69f 100644 --- a/crates/ra_ide/src/display/navigation_target.rs +++ b/crates/ra_ide/src/display/navigation_target.rs | |||
@@ -92,15 +92,16 @@ impl NavigationTarget { | |||
92 | let name = module.name(db).map(|it| it.to_string().into()).unwrap_or_default(); | 92 | let name = module.name(db).map(|it| it.to_string().into()).unwrap_or_default(); |
93 | if let Some(src) = module.declaration_source(db) { | 93 | if let Some(src) = module.declaration_source(db) { |
94 | let frange = original_range(db, src.as_ref().map(|it| it.syntax())); | 94 | let frange = original_range(db, src.as_ref().map(|it| it.syntax())); |
95 | return NavigationTarget::from_syntax( | 95 | let mut res = NavigationTarget::from_syntax( |
96 | frange.file_id, | 96 | frange.file_id, |
97 | name, | 97 | name, |
98 | None, | 98 | None, |
99 | frange.range, | 99 | frange.range, |
100 | src.value.syntax().kind(), | 100 | src.value.syntax().kind(), |
101 | src.value.doc_comment_text(), | ||
102 | src.value.short_label(), | ||
103 | ); | 101 | ); |
102 | res.docs = src.value.doc_comment_text(); | ||
103 | res.description = src.value.short_label(); | ||
104 | return res; | ||
104 | } | 105 | } |
105 | module.to_nav(db) | 106 | module.to_nav(db) |
106 | } | 107 | } |
@@ -130,11 +131,9 @@ impl NavigationTarget { | |||
130 | } | 131 | } |
131 | 132 | ||
132 | /// Allows `NavigationTarget` to be created from a `NameOwner` | 133 | /// Allows `NavigationTarget` to be created from a `NameOwner` |
133 | fn from_named( | 134 | pub(crate) fn from_named( |
134 | db: &RootDatabase, | 135 | db: &RootDatabase, |
135 | node: InFile<&dyn ast::NameOwner>, | 136 | node: InFile<&dyn ast::NameOwner>, |
136 | docs: Option<String>, | ||
137 | description: Option<String>, | ||
138 | ) -> NavigationTarget { | 137 | ) -> NavigationTarget { |
139 | //FIXME: use `_` instead of empty string | 138 | //FIXME: use `_` instead of empty string |
140 | let name = node.value.name().map(|it| it.text().clone()).unwrap_or_default(); | 139 | let name = node.value.name().map(|it| it.text().clone()).unwrap_or_default(); |
@@ -148,8 +147,6 @@ impl NavigationTarget { | |||
148 | focus_range, | 147 | focus_range, |
149 | frange.range, | 148 | frange.range, |
150 | node.value.syntax().kind(), | 149 | node.value.syntax().kind(), |
151 | docs, | ||
152 | description, | ||
153 | ) | 150 | ) |
154 | } | 151 | } |
155 | 152 | ||
@@ -159,8 +156,6 @@ impl NavigationTarget { | |||
159 | focus_range: Option<TextRange>, | 156 | focus_range: Option<TextRange>, |
160 | full_range: TextRange, | 157 | full_range: TextRange, |
161 | kind: SyntaxKind, | 158 | kind: SyntaxKind, |
162 | docs: Option<String>, | ||
163 | description: Option<String>, | ||
164 | ) -> NavigationTarget { | 159 | ) -> NavigationTarget { |
165 | NavigationTarget { | 160 | NavigationTarget { |
166 | file_id, | 161 | file_id, |
@@ -169,8 +164,8 @@ impl NavigationTarget { | |||
169 | full_range, | 164 | full_range, |
170 | focus_range, | 165 | focus_range, |
171 | container_name: None, | 166 | container_name: None, |
172 | description, | 167 | description: None, |
173 | docs, | 168 | docs: None, |
174 | } | 169 | } |
175 | } | 170 | } |
176 | } | 171 | } |
@@ -238,12 +233,11 @@ where | |||
238 | { | 233 | { |
239 | fn to_nav(&self, db: &RootDatabase) -> NavigationTarget { | 234 | fn to_nav(&self, db: &RootDatabase) -> NavigationTarget { |
240 | let src = self.source(db); | 235 | let src = self.source(db); |
241 | NavigationTarget::from_named( | 236 | let mut res = |
242 | db, | 237 | NavigationTarget::from_named(db, src.as_ref().map(|it| it as &dyn ast::NameOwner)); |
243 | src.as_ref().map(|it| it as &dyn ast::NameOwner), | 238 | res.docs = src.value.doc_comment_text(); |
244 | src.value.doc_comment_text(), | 239 | res.description = src.value.short_label(); |
245 | src.value.short_label(), | 240 | res |
246 | ) | ||
247 | } | 241 | } |
248 | } | 242 | } |
249 | 243 | ||
@@ -258,15 +252,7 @@ impl ToNav for hir::Module { | |||
258 | } | 252 | } |
259 | }; | 253 | }; |
260 | let frange = original_range(db, src.with_value(syntax)); | 254 | let frange = original_range(db, src.with_value(syntax)); |
261 | NavigationTarget::from_syntax( | 255 | NavigationTarget::from_syntax(frange.file_id, name, focus, frange.range, syntax.kind()) |
262 | frange.file_id, | ||
263 | name, | ||
264 | focus, | ||
265 | frange.range, | ||
266 | syntax.kind(), | ||
267 | None, | ||
268 | None, | ||
269 | ) | ||
270 | } | 256 | } |
271 | } | 257 | } |
272 | 258 | ||
@@ -285,8 +271,6 @@ impl ToNav for hir::ImplDef { | |||
285 | None, | 271 | None, |
286 | frange.range, | 272 | frange.range, |
287 | src.value.syntax().kind(), | 273 | src.value.syntax().kind(), |
288 | None, | ||
289 | None, | ||
290 | ) | 274 | ) |
291 | } | 275 | } |
292 | } | 276 | } |
@@ -296,12 +280,12 @@ impl ToNav for hir::Field { | |||
296 | let src = self.source(db); | 280 | let src = self.source(db); |
297 | 281 | ||
298 | match &src.value { | 282 | match &src.value { |
299 | FieldSource::Named(it) => NavigationTarget::from_named( | 283 | FieldSource::Named(it) => { |
300 | db, | 284 | let mut res = NavigationTarget::from_named(db, src.with_value(it)); |
301 | src.with_value(it), | 285 | res.docs = it.doc_comment_text(); |
302 | it.doc_comment_text(), | 286 | res.description = it.short_label(); |
303 | it.short_label(), | 287 | res |
304 | ), | 288 | } |
305 | FieldSource::Pos(it) => { | 289 | FieldSource::Pos(it) => { |
306 | let frange = original_range(db, src.with_value(it.syntax())); | 290 | let frange = original_range(db, src.with_value(it.syntax())); |
307 | NavigationTarget::from_syntax( | 291 | NavigationTarget::from_syntax( |
@@ -310,8 +294,6 @@ impl ToNav for hir::Field { | |||
310 | None, | 294 | None, |
311 | frange.range, | 295 | frange.range, |
312 | it.syntax().kind(), | 296 | it.syntax().kind(), |
313 | None, | ||
314 | None, | ||
315 | ) | 297 | ) |
316 | } | 298 | } |
317 | } | 299 | } |
@@ -322,12 +304,10 @@ impl ToNav for hir::MacroDef { | |||
322 | fn to_nav(&self, db: &RootDatabase) -> NavigationTarget { | 304 | fn to_nav(&self, db: &RootDatabase) -> NavigationTarget { |
323 | let src = self.source(db); | 305 | let src = self.source(db); |
324 | log::debug!("nav target {:#?}", src.value.syntax()); | 306 | log::debug!("nav target {:#?}", src.value.syntax()); |
325 | NavigationTarget::from_named( | 307 | let mut res = |
326 | db, | 308 | NavigationTarget::from_named(db, src.as_ref().map(|it| it as &dyn ast::NameOwner)); |
327 | src.as_ref().map(|it| it as &dyn ast::NameOwner), | 309 | res.docs = src.value.doc_comment_text(); |
328 | src.value.doc_comment_text(), | 310 | res |
329 | None, | ||
330 | ) | ||
331 | } | 311 | } |
332 | } | 312 | } |
333 | 313 | ||
diff --git a/crates/ra_ide/src/display/structure.rs b/crates/ra_ide/src/display/structure.rs index 967eee5d2..aad5a8e4d 100644 --- a/crates/ra_ide/src/display/structure.rs +++ b/crates/ra_ide/src/display/structure.rs | |||
@@ -1,10 +1,6 @@ | |||
1 | //! FIXME: write short doc here | ||
2 | |||
3 | use crate::TextRange; | ||
4 | |||
5 | use ra_syntax::{ | 1 | use ra_syntax::{ |
6 | ast::{self, AttrsOwner, NameOwner, TypeAscriptionOwner, TypeParamsOwner}, | 2 | ast::{self, AttrsOwner, NameOwner, TypeAscriptionOwner, TypeParamsOwner}, |
7 | match_ast, AstNode, SourceFile, SyntaxKind, SyntaxNode, WalkEvent, | 3 | match_ast, AstNode, SourceFile, SyntaxKind, SyntaxNode, TextRange, WalkEvent, |
8 | }; | 4 | }; |
9 | 5 | ||
10 | #[derive(Debug, Clone)] | 6 | #[derive(Debug, Clone)] |
@@ -18,6 +14,19 @@ pub struct StructureNode { | |||
18 | pub deprecated: bool, | 14 | pub deprecated: bool, |
19 | } | 15 | } |
20 | 16 | ||
17 | // Feature: File Structure | ||
18 | // | ||
19 | // Provides a tree of the symbols defined in the file. Can be used to | ||
20 | // | ||
21 | // * fuzzy search symbol in a file (super useful) | ||
22 | // * draw breadcrumbs to describe the context around the cursor | ||
23 | // * draw outline of the file | ||
24 | // | ||
25 | // |=== | ||
26 | // | Editor | Shortcut | ||
27 | // | ||
28 | // | VS Code | kbd:[Ctrl+Shift+O] | ||
29 | // |=== | ||
21 | pub fn file_structure(file: &SourceFile) -> Vec<StructureNode> { | 30 | pub fn file_structure(file: &SourceFile) -> Vec<StructureNode> { |
22 | let mut res = Vec::new(); | 31 | let mut res = Vec::new(); |
23 | let mut stack = Vec::new(); | 32 | let mut stack = Vec::new(); |
diff --git a/crates/ra_ide/src/expand_macro.rs b/crates/ra_ide/src/expand_macro.rs index f536ba3e7..54a47aac0 100644 --- a/crates/ra_ide/src/expand_macro.rs +++ b/crates/ra_ide/src/expand_macro.rs | |||
@@ -1,5 +1,3 @@ | |||
1 | //! This modules implements "expand macro" functionality in the IDE | ||
2 | |||
3 | use hir::Semantics; | 1 | use hir::Semantics; |
4 | use ra_ide_db::RootDatabase; | 2 | use ra_ide_db::RootDatabase; |
5 | use ra_syntax::{ | 3 | use ra_syntax::{ |
@@ -14,6 +12,15 @@ pub struct ExpandedMacro { | |||
14 | pub expansion: String, | 12 | pub expansion: String, |
15 | } | 13 | } |
16 | 14 | ||
15 | // Feature: Expand Macro Recursively | ||
16 | // | ||
17 | // Shows the full macro expansion of the macro at current cursor. | ||
18 | // | ||
19 | // |=== | ||
20 | // | Editor | Action Name | ||
21 | // | ||
22 | // | VS Code | **Rust Analyzer: Expand macro recursively** | ||
23 | // |=== | ||
17 | pub(crate) fn expand_macro(db: &RootDatabase, position: FilePosition) -> Option<ExpandedMacro> { | 24 | pub(crate) fn expand_macro(db: &RootDatabase, position: FilePosition) -> Option<ExpandedMacro> { |
18 | let sema = Semantics::new(db); | 25 | let sema = Semantics::new(db); |
19 | let file = sema.parse(position.file_id); | 26 | let file = sema.parse(position.file_id); |
diff --git a/crates/ra_ide/src/extend_selection.rs b/crates/ra_ide/src/extend_selection.rs index 554594a43..a4bc93cdb 100644 --- a/crates/ra_ide/src/extend_selection.rs +++ b/crates/ra_ide/src/extend_selection.rs | |||
@@ -1,5 +1,3 @@ | |||
1 | //! FIXME: write short doc here | ||
2 | |||
3 | use std::iter::successors; | 1 | use std::iter::successors; |
4 | 2 | ||
5 | use hir::Semantics; | 3 | use hir::Semantics; |
@@ -14,6 +12,16 @@ use ra_syntax::{ | |||
14 | 12 | ||
15 | use crate::FileRange; | 13 | use crate::FileRange; |
16 | 14 | ||
15 | // Feature: Extend Selection | ||
16 | // | ||
17 | // Extends the current selection to the encompassing syntactic construct | ||
18 | // (expression, statement, item, module, etc). It works with multiple cursors. | ||
19 | // | ||
20 | // |=== | ||
21 | // | Editor | Shortcut | ||
22 | // | ||
23 | // | VS Code | kbd:[Ctrl+Shift+→] | ||
24 | // |=== | ||
17 | pub(crate) fn extend_selection(db: &RootDatabase, frange: FileRange) -> TextRange { | 25 | pub(crate) fn extend_selection(db: &RootDatabase, frange: FileRange) -> TextRange { |
18 | let sema = Semantics::new(db); | 26 | let sema = Semantics::new(db); |
19 | let src = sema.parse(frange.file_id); | 27 | let src = sema.parse(frange.file_id); |
diff --git a/crates/ra_ide/src/goto_definition.rs b/crates/ra_ide/src/goto_definition.rs index 90e85d419..a6c86e99c 100644 --- a/crates/ra_ide/src/goto_definition.rs +++ b/crates/ra_ide/src/goto_definition.rs | |||
@@ -1,5 +1,3 @@ | |||
1 | //! FIXME: write short doc here | ||
2 | |||
3 | use hir::Semantics; | 1 | use hir::Semantics; |
4 | use ra_ide_db::{ | 2 | use ra_ide_db::{ |
5 | defs::{classify_name, classify_name_ref}, | 3 | defs::{classify_name, classify_name_ref}, |
@@ -17,6 +15,15 @@ use crate::{ | |||
17 | FilePosition, NavigationTarget, RangeInfo, | 15 | FilePosition, NavigationTarget, RangeInfo, |
18 | }; | 16 | }; |
19 | 17 | ||
18 | // Feature: Go to Definition | ||
19 | // | ||
20 | // Navigates to the definition of an identifier. | ||
21 | // | ||
22 | // |=== | ||
23 | // | Editor | Shortcut | ||
24 | // | ||
25 | // | VS Code | kbd:[F12] | ||
26 | // |=== | ||
20 | pub(crate) fn goto_definition( | 27 | pub(crate) fn goto_definition( |
21 | db: &RootDatabase, | 28 | db: &RootDatabase, |
22 | position: FilePosition, | 29 | position: FilePosition, |
diff --git a/crates/ra_ide/src/impls.rs b/crates/ra_ide/src/goto_implementation.rs index ea2225f70..0cec0657e 100644 --- a/crates/ra_ide/src/impls.rs +++ b/crates/ra_ide/src/goto_implementation.rs | |||
@@ -1,11 +1,18 @@ | |||
1 | //! FIXME: write short doc here | ||
2 | |||
3 | use hir::{Crate, ImplDef, Semantics}; | 1 | use hir::{Crate, ImplDef, Semantics}; |
4 | use ra_ide_db::RootDatabase; | 2 | use ra_ide_db::RootDatabase; |
5 | use ra_syntax::{algo::find_node_at_offset, ast, AstNode}; | 3 | use ra_syntax::{algo::find_node_at_offset, ast, AstNode}; |
6 | 4 | ||
7 | use crate::{display::ToNav, FilePosition, NavigationTarget, RangeInfo}; | 5 | use crate::{display::ToNav, FilePosition, NavigationTarget, RangeInfo}; |
8 | 6 | ||
7 | // Feature: Go to Implementation | ||
8 | // | ||
9 | // Navigates to the impl block of structs, enums or traits. Also implemented as a code lens. | ||
10 | // | ||
11 | // |=== | ||
12 | // | Editor | Shortcut | ||
13 | // | ||
14 | // | VS Code | kbd:[Ctrl+F12] | ||
15 | // |=== | ||
9 | pub(crate) fn goto_implementation( | 16 | pub(crate) fn goto_implementation( |
10 | db: &RootDatabase, | 17 | db: &RootDatabase, |
11 | position: FilePosition, | 18 | position: FilePosition, |
diff --git a/crates/ra_ide/src/goto_type_definition.rs b/crates/ra_ide/src/goto_type_definition.rs index a84637489..91a3097fb 100644 --- a/crates/ra_ide/src/goto_type_definition.rs +++ b/crates/ra_ide/src/goto_type_definition.rs | |||
@@ -1,10 +1,17 @@ | |||
1 | //! FIXME: write short doc here | ||
2 | |||
3 | use ra_ide_db::RootDatabase; | 1 | use ra_ide_db::RootDatabase; |
4 | use ra_syntax::{ast, match_ast, AstNode, SyntaxKind::*, SyntaxToken, TokenAtOffset}; | 2 | use ra_syntax::{ast, match_ast, AstNode, SyntaxKind::*, SyntaxToken, TokenAtOffset}; |
5 | 3 | ||
6 | use crate::{display::ToNav, FilePosition, NavigationTarget, RangeInfo}; | 4 | use crate::{display::ToNav, FilePosition, NavigationTarget, RangeInfo}; |
7 | 5 | ||
6 | // Feature: Go to Type Definition | ||
7 | // | ||
8 | // Navigates to the type of an identifier. | ||
9 | // | ||
10 | // |=== | ||
11 | // | Editor | Action Name | ||
12 | // | ||
13 | // | VS Code | **Go to Type Definition* | ||
14 | // |=== | ||
8 | pub(crate) fn goto_type_definition( | 15 | pub(crate) fn goto_type_definition( |
9 | db: &RootDatabase, | 16 | db: &RootDatabase, |
10 | position: FilePosition, | 17 | position: FilePosition, |
diff --git a/crates/ra_ide/src/hover.rs b/crates/ra_ide/src/hover.rs index befa977c7..9636cd0d6 100644 --- a/crates/ra_ide/src/hover.rs +++ b/crates/ra_ide/src/hover.rs | |||
@@ -1,28 +1,21 @@ | |||
1 | //! Logic for computing info that is displayed when the user hovers over any | 1 | use std::iter::once; |
2 | //! source code items (e.g. function call, struct field, variable symbol...) | ||
3 | 2 | ||
4 | use hir::{ | 3 | use hir::{ |
5 | Adt, AsAssocItem, AssocItemContainer, FieldSource, HasSource, HirDisplay, ModuleDef, | 4 | Adt, AsAssocItem, AssocItemContainer, Documentation, FieldSource, HasSource, HirDisplay, |
6 | ModuleSource, Semantics, | 5 | ModuleDef, ModuleSource, Semantics, |
7 | }; | 6 | }; |
7 | use itertools::Itertools; | ||
8 | use ra_db::SourceDatabase; | 8 | use ra_db::SourceDatabase; |
9 | use ra_ide_db::{ | 9 | use ra_ide_db::{ |
10 | defs::{classify_name, classify_name_ref, Definition}, | 10 | defs::{classify_name, classify_name_ref, Definition}, |
11 | RootDatabase, | 11 | RootDatabase, |
12 | }; | 12 | }; |
13 | use ra_syntax::{ | 13 | use ra_syntax::{ast, match_ast, AstNode, SyntaxKind::*, SyntaxToken, TokenAtOffset}; |
14 | ast::{self, DocCommentsOwner}, | ||
15 | match_ast, AstNode, | ||
16 | SyntaxKind::*, | ||
17 | SyntaxToken, TokenAtOffset, | ||
18 | }; | ||
19 | 14 | ||
20 | use crate::{ | 15 | use crate::{ |
21 | display::{macro_label, rust_code_markup, rust_code_markup_with_doc, ShortLabel}, | 16 | display::{macro_label, rust_code_markup, rust_code_markup_with_doc, ShortLabel}, |
22 | FilePosition, RangeInfo, | 17 | FilePosition, RangeInfo, |
23 | }; | 18 | }; |
24 | use itertools::Itertools; | ||
25 | use std::iter::once; | ||
26 | 19 | ||
27 | /// Contains the results when hovering over an item | 20 | /// Contains the results when hovering over an item |
28 | #[derive(Debug, Default)] | 21 | #[derive(Debug, Default)] |
@@ -62,6 +55,63 @@ impl HoverResult { | |||
62 | } | 55 | } |
63 | } | 56 | } |
64 | 57 | ||
58 | // Feature: Hover | ||
59 | // | ||
60 | // Shows additional information, like type of an expression or documentation for definition when "focusing" code. | ||
61 | // Focusing is usually hovering with a mouse, but can also be triggered with a shortcut. | ||
62 | pub(crate) fn hover(db: &RootDatabase, position: FilePosition) -> Option<RangeInfo<HoverResult>> { | ||
63 | let sema = Semantics::new(db); | ||
64 | let file = sema.parse(position.file_id).syntax().clone(); | ||
65 | let token = pick_best(file.token_at_offset(position.offset))?; | ||
66 | let token = sema.descend_into_macros(token); | ||
67 | |||
68 | let mut res = HoverResult::new(); | ||
69 | |||
70 | if let Some((node, name_kind)) = match_ast! { | ||
71 | match (token.parent()) { | ||
72 | ast::NameRef(name_ref) => { | ||
73 | classify_name_ref(&sema, &name_ref).map(|d| (name_ref.syntax().clone(), d.definition())) | ||
74 | }, | ||
75 | ast::Name(name) => { | ||
76 | classify_name(&sema, &name).map(|d| (name.syntax().clone(), d.definition())) | ||
77 | }, | ||
78 | _ => None, | ||
79 | } | ||
80 | } { | ||
81 | let range = sema.original_range(&node).range; | ||
82 | res.extend(hover_text_from_name_kind(db, name_kind)); | ||
83 | |||
84 | if !res.is_empty() { | ||
85 | return Some(RangeInfo::new(range, res)); | ||
86 | } | ||
87 | } | ||
88 | |||
89 | let node = token | ||
90 | .ancestors() | ||
91 | .find(|n| ast::Expr::cast(n.clone()).is_some() || ast::Pat::cast(n.clone()).is_some())?; | ||
92 | |||
93 | let ty = match_ast! { | ||
94 | match node { | ||
95 | ast::MacroCall(_it) => { | ||
96 | // If this node is a MACRO_CALL, it means that `descend_into_macros` failed to resolve. | ||
97 | // (e.g expanding a builtin macro). So we give up here. | ||
98 | return None; | ||
99 | }, | ||
100 | ast::Expr(it) => { | ||
101 | sema.type_of_expr(&it) | ||
102 | }, | ||
103 | ast::Pat(it) => { | ||
104 | sema.type_of_pat(&it) | ||
105 | }, | ||
106 | _ => None, | ||
107 | } | ||
108 | }?; | ||
109 | |||
110 | res.extend(Some(rust_code_markup(&ty.display(db)))); | ||
111 | let range = sema.original_range(&node).range; | ||
112 | Some(RangeInfo::new(range, res)) | ||
113 | } | ||
114 | |||
65 | fn hover_text( | 115 | fn hover_text( |
66 | docs: Option<String>, | 116 | docs: Option<String>, |
67 | desc: Option<String>, | 117 | desc: Option<String>, |
@@ -114,13 +164,15 @@ fn hover_text_from_name_kind(db: &RootDatabase, def: Definition) -> Option<Strin | |||
114 | return match def { | 164 | return match def { |
115 | Definition::Macro(it) => { | 165 | Definition::Macro(it) => { |
116 | let src = it.source(db); | 166 | let src = it.source(db); |
117 | hover_text(src.value.doc_comment_text(), Some(macro_label(&src.value)), mod_path) | 167 | let docs = Documentation::from_ast(&src.value).map(Into::into); |
168 | hover_text(docs, Some(macro_label(&src.value)), mod_path) | ||
118 | } | 169 | } |
119 | Definition::Field(it) => { | 170 | Definition::Field(it) => { |
120 | let src = it.source(db); | 171 | let src = it.source(db); |
121 | match src.value { | 172 | match src.value { |
122 | FieldSource::Named(it) => { | 173 | FieldSource::Named(it) => { |
123 | hover_text(it.doc_comment_text(), it.short_label(), mod_path) | 174 | let docs = Documentation::from_ast(&it).map(Into::into); |
175 | hover_text(docs, it.short_label(), mod_path) | ||
124 | } | 176 | } |
125 | _ => None, | 177 | _ => None, |
126 | } | 178 | } |
@@ -128,7 +180,8 @@ fn hover_text_from_name_kind(db: &RootDatabase, def: Definition) -> Option<Strin | |||
128 | Definition::ModuleDef(it) => match it { | 180 | Definition::ModuleDef(it) => match it { |
129 | ModuleDef::Module(it) => match it.definition_source(db).value { | 181 | ModuleDef::Module(it) => match it.definition_source(db).value { |
130 | ModuleSource::Module(it) => { | 182 | ModuleSource::Module(it) => { |
131 | hover_text(it.doc_comment_text(), it.short_label(), mod_path) | 183 | let docs = Documentation::from_ast(&it).map(Into::into); |
184 | hover_text(docs, it.short_label(), mod_path) | ||
132 | } | 185 | } |
133 | _ => None, | 186 | _ => None, |
134 | }, | 187 | }, |
@@ -153,66 +206,14 @@ fn hover_text_from_name_kind(db: &RootDatabase, def: Definition) -> Option<Strin | |||
153 | fn from_def_source<A, D>(db: &RootDatabase, def: D, mod_path: Option<String>) -> Option<String> | 206 | fn from_def_source<A, D>(db: &RootDatabase, def: D, mod_path: Option<String>) -> Option<String> |
154 | where | 207 | where |
155 | D: HasSource<Ast = A>, | 208 | D: HasSource<Ast = A>, |
156 | A: ast::DocCommentsOwner + ast::NameOwner + ShortLabel, | 209 | A: ast::DocCommentsOwner + ast::NameOwner + ShortLabel + ast::AttrsOwner, |
157 | { | 210 | { |
158 | let src = def.source(db); | 211 | let src = def.source(db); |
159 | hover_text(src.value.doc_comment_text(), src.value.short_label(), mod_path) | 212 | let docs = Documentation::from_ast(&src.value).map(Into::into); |
213 | hover_text(docs, src.value.short_label(), mod_path) | ||
160 | } | 214 | } |
161 | } | 215 | } |
162 | 216 | ||
163 | pub(crate) fn hover(db: &RootDatabase, position: FilePosition) -> Option<RangeInfo<HoverResult>> { | ||
164 | let sema = Semantics::new(db); | ||
165 | let file = sema.parse(position.file_id).syntax().clone(); | ||
166 | let token = pick_best(file.token_at_offset(position.offset))?; | ||
167 | let token = sema.descend_into_macros(token); | ||
168 | |||
169 | let mut res = HoverResult::new(); | ||
170 | |||
171 | if let Some((node, name_kind)) = match_ast! { | ||
172 | match (token.parent()) { | ||
173 | ast::NameRef(name_ref) => { | ||
174 | classify_name_ref(&sema, &name_ref).map(|d| (name_ref.syntax().clone(), d.definition())) | ||
175 | }, | ||
176 | ast::Name(name) => { | ||
177 | classify_name(&sema, &name).map(|d| (name.syntax().clone(), d.definition())) | ||
178 | }, | ||
179 | _ => None, | ||
180 | } | ||
181 | } { | ||
182 | let range = sema.original_range(&node).range; | ||
183 | res.extend(hover_text_from_name_kind(db, name_kind)); | ||
184 | |||
185 | if !res.is_empty() { | ||
186 | return Some(RangeInfo::new(range, res)); | ||
187 | } | ||
188 | } | ||
189 | |||
190 | let node = token | ||
191 | .ancestors() | ||
192 | .find(|n| ast::Expr::cast(n.clone()).is_some() || ast::Pat::cast(n.clone()).is_some())?; | ||
193 | |||
194 | let ty = match_ast! { | ||
195 | match node { | ||
196 | ast::MacroCall(_it) => { | ||
197 | // If this node is a MACRO_CALL, it means that `descend_into_macros` failed to resolve. | ||
198 | // (e.g expanding a builtin macro). So we give up here. | ||
199 | return None; | ||
200 | }, | ||
201 | ast::Expr(it) => { | ||
202 | sema.type_of_expr(&it) | ||
203 | }, | ||
204 | ast::Pat(it) => { | ||
205 | sema.type_of_pat(&it) | ||
206 | }, | ||
207 | _ => None, | ||
208 | } | ||
209 | }?; | ||
210 | |||
211 | res.extend(Some(rust_code_markup(&ty.display(db)))); | ||
212 | let range = sema.original_range(&node).range; | ||
213 | Some(RangeInfo::new(range, res)) | ||
214 | } | ||
215 | |||
216 | fn pick_best(tokens: TokenAtOffset<SyntaxToken>) -> Option<SyntaxToken> { | 217 | fn pick_best(tokens: TokenAtOffset<SyntaxToken>) -> Option<SyntaxToken> { |
217 | return tokens.max_by_key(priority); | 218 | return tokens.max_by_key(priority); |
218 | fn priority(n: &SyntaxToken) -> usize { | 219 | fn priority(n: &SyntaxToken) -> usize { |
@@ -405,7 +406,7 @@ mod tests { | |||
405 | }; | 406 | }; |
406 | } | 407 | } |
407 | "#, | 408 | "#, |
408 | &["Foo\nfield_a: u32"], | 409 | &["Foo\n```\n\n```rust\nfield_a: u32"], |
409 | ); | 410 | ); |
410 | 411 | ||
411 | // Hovering over the field in the definition | 412 | // Hovering over the field in the definition |
@@ -422,7 +423,7 @@ mod tests { | |||
422 | }; | 423 | }; |
423 | } | 424 | } |
424 | "#, | 425 | "#, |
425 | &["Foo\nfield_a: u32"], | 426 | &["Foo\n```\n\n```rust\nfield_a: u32"], |
426 | ); | 427 | ); |
427 | } | 428 | } |
428 | 429 | ||
@@ -475,7 +476,7 @@ fn main() { | |||
475 | ", | 476 | ", |
476 | ); | 477 | ); |
477 | let hover = analysis.hover(position).unwrap().unwrap(); | 478 | let hover = analysis.hover(position).unwrap().unwrap(); |
478 | assert_eq!(trim_markup_opt(hover.info.first()), Some("Option\nSome")); | 479 | assert_eq!(trim_markup_opt(hover.info.first()), Some("Option\n```\n\n```rust\nSome")); |
479 | 480 | ||
480 | let (analysis, position) = single_file_with_position( | 481 | let (analysis, position) = single_file_with_position( |
481 | " | 482 | " |
@@ -503,8 +504,12 @@ fn main() { | |||
503 | "#, | 504 | "#, |
504 | &[" | 505 | &[" |
505 | Option | 506 | Option |
507 | ``` | ||
508 | |||
509 | ```rust | ||
506 | None | 510 | None |
507 | ``` | 511 | ``` |
512 | ___ | ||
508 | 513 | ||
509 | The None variant | 514 | The None variant |
510 | " | 515 | " |
@@ -524,8 +529,12 @@ The None variant | |||
524 | "#, | 529 | "#, |
525 | &[" | 530 | &[" |
526 | Option | 531 | Option |
532 | ``` | ||
533 | |||
534 | ```rust | ||
527 | Some | 535 | Some |
528 | ``` | 536 | ``` |
537 | ___ | ||
529 | 538 | ||
530 | The Some variant | 539 | The Some variant |
531 | " | 540 | " |
@@ -606,7 +615,10 @@ fn func(foo: i32) { if true { <|>foo; }; } | |||
606 | ", | 615 | ", |
607 | ); | 616 | ); |
608 | let hover = analysis.hover(position).unwrap().unwrap(); | 617 | let hover = analysis.hover(position).unwrap().unwrap(); |
609 | assert_eq!(trim_markup_opt(hover.info.first()), Some("wrapper::Thing\nfn new() -> Thing")); | 618 | assert_eq!( |
619 | trim_markup_opt(hover.info.first()), | ||
620 | Some("wrapper::Thing\n```\n\n```rust\nfn new() -> Thing") | ||
621 | ); | ||
610 | } | 622 | } |
611 | 623 | ||
612 | #[test] | 624 | #[test] |
@@ -882,7 +894,7 @@ fn func(foo: i32) { if true { <|>foo; }; } | |||
882 | fo<|>o(); | 894 | fo<|>o(); |
883 | } | 895 | } |
884 | ", | 896 | ", |
885 | &["fn foo()\n```\n\n<- `\u{3000}` here"], | 897 | &["fn foo()\n```\n___\n\n<- `\u{3000}` here"], |
886 | ); | 898 | ); |
887 | } | 899 | } |
888 | 900 | ||
@@ -938,4 +950,106 @@ fn func(foo: i32) { if true { <|>foo; }; } | |||
938 | &["mod my"], | 950 | &["mod my"], |
939 | ); | 951 | ); |
940 | } | 952 | } |
953 | |||
954 | #[test] | ||
955 | fn test_hover_struct_doc_comment() { | ||
956 | check_hover_result( | ||
957 | r#" | ||
958 | //- /lib.rs | ||
959 | /// bar docs | ||
960 | struct Bar; | ||
961 | |||
962 | fn foo() { | ||
963 | let bar = Ba<|>r; | ||
964 | } | ||
965 | "#, | ||
966 | &["struct Bar\n```\n___\n\nbar docs"], | ||
967 | ); | ||
968 | } | ||
969 | |||
970 | #[test] | ||
971 | fn test_hover_struct_doc_attr() { | ||
972 | check_hover_result( | ||
973 | r#" | ||
974 | //- /lib.rs | ||
975 | #[doc = "bar docs"] | ||
976 | struct Bar; | ||
977 | |||
978 | fn foo() { | ||
979 | let bar = Ba<|>r; | ||
980 | } | ||
981 | "#, | ||
982 | &["struct Bar\n```\n___\n\nbar docs"], | ||
983 | ); | ||
984 | } | ||
985 | |||
986 | #[test] | ||
987 | fn test_hover_struct_doc_attr_multiple_and_mixed() { | ||
988 | check_hover_result( | ||
989 | r#" | ||
990 | //- /lib.rs | ||
991 | /// bar docs 0 | ||
992 | #[doc = "bar docs 1"] | ||
993 | #[doc = "bar docs 2"] | ||
994 | struct Bar; | ||
995 | |||
996 | fn foo() { | ||
997 | let bar = Ba<|>r; | ||
998 | } | ||
999 | "#, | ||
1000 | &["struct Bar\n```\n___\n\nbar docs 0\n\nbar docs 1\n\nbar docs 2"], | ||
1001 | ); | ||
1002 | } | ||
1003 | |||
1004 | #[test] | ||
1005 | fn test_hover_macro_generated_struct_fn_doc_comment() { | ||
1006 | check_hover_result( | ||
1007 | r#" | ||
1008 | //- /lib.rs | ||
1009 | macro_rules! bar { | ||
1010 | () => { | ||
1011 | struct Bar; | ||
1012 | impl Bar { | ||
1013 | /// Do the foo | ||
1014 | fn foo(&self) {} | ||
1015 | } | ||
1016 | } | ||
1017 | } | ||
1018 | |||
1019 | bar!(); | ||
1020 | |||
1021 | fn foo() { | ||
1022 | let bar = Bar; | ||
1023 | bar.fo<|>o(); | ||
1024 | } | ||
1025 | "#, | ||
1026 | &["Bar\n```\n\n```rust\nfn foo(&self)\n```\n___\n\n Do the foo"], | ||
1027 | ); | ||
1028 | } | ||
1029 | |||
1030 | #[test] | ||
1031 | fn test_hover_macro_generated_struct_fn_doc_attr() { | ||
1032 | check_hover_result( | ||
1033 | r#" | ||
1034 | //- /lib.rs | ||
1035 | macro_rules! bar { | ||
1036 | () => { | ||
1037 | struct Bar; | ||
1038 | impl Bar { | ||
1039 | #[doc = "Do the foo"] | ||
1040 | fn foo(&self) {} | ||
1041 | } | ||
1042 | } | ||
1043 | } | ||
1044 | |||
1045 | bar!(); | ||
1046 | |||
1047 | fn foo() { | ||
1048 | let bar = Bar; | ||
1049 | bar.fo<|>o(); | ||
1050 | } | ||
1051 | "#, | ||
1052 | &["Bar\n```\n\n```rust\nfn foo(&self)\n```\n___\n\nDo the foo"], | ||
1053 | ); | ||
1054 | } | ||
941 | } | 1055 | } |
diff --git a/crates/ra_ide/src/inlay_hints.rs b/crates/ra_ide/src/inlay_hints.rs index b391f903a..75bd3c96b 100644 --- a/crates/ra_ide/src/inlay_hints.rs +++ b/crates/ra_ide/src/inlay_hints.rs | |||
@@ -1,5 +1,3 @@ | |||
1 | //! This module defines multiple types of inlay hints and their visibility | ||
2 | |||
3 | use hir::{Adt, HirDisplay, Semantics, Type}; | 1 | use hir::{Adt, HirDisplay, Semantics, Type}; |
4 | use ra_ide_db::RootDatabase; | 2 | use ra_ide_db::RootDatabase; |
5 | use ra_prof::profile; | 3 | use ra_prof::profile; |
@@ -39,6 +37,26 @@ pub struct InlayHint { | |||
39 | pub label: SmolStr, | 37 | pub label: SmolStr, |
40 | } | 38 | } |
41 | 39 | ||
40 | // Feature: Inlay Hints | ||
41 | // | ||
42 | // rust-analyzer shows additional information inline with the source code. | ||
43 | // Editors usually render this using read-only virtual text snippets interspersed with code. | ||
44 | // | ||
45 | // rust-analyzer shows hits for | ||
46 | // | ||
47 | // * types of local variables | ||
48 | // * names of function arguments | ||
49 | // * types of chained expressions | ||
50 | // | ||
51 | // **Note:** VS Code does not have native support for inlay hints https://github.com/microsoft/vscode/issues/16221[yet] and the hints are implemented using decorations. | ||
52 | // This approach has limitations, the caret movement and bracket highlighting near the edges of the hint may be weird: | ||
53 | // https://github.com/rust-analyzer/rust-analyzer/issues/1623[1], https://github.com/rust-analyzer/rust-analyzer/issues/3453[2]. | ||
54 | // | ||
55 | // |=== | ||
56 | // | Editor | Action Name | ||
57 | // | ||
58 | // | VS Code | **Rust Analyzer: Toggle inlay hints* | ||
59 | // |=== | ||
42 | pub(crate) fn inlay_hints( | 60 | pub(crate) fn inlay_hints( |
43 | db: &RootDatabase, | 61 | db: &RootDatabase, |
44 | file_id: FileId, | 62 | file_id: FileId, |
diff --git a/crates/ra_ide/src/join_lines.rs b/crates/ra_ide/src/join_lines.rs index af1ade8a1..5036c1fb0 100644 --- a/crates/ra_ide/src/join_lines.rs +++ b/crates/ra_ide/src/join_lines.rs | |||
@@ -1,5 +1,3 @@ | |||
1 | //! FIXME: write short doc here | ||
2 | |||
3 | use itertools::Itertools; | 1 | use itertools::Itertools; |
4 | use ra_fmt::{compute_ws, extract_trivial_expression}; | 2 | use ra_fmt::{compute_ws, extract_trivial_expression}; |
5 | use ra_syntax::{ | 3 | use ra_syntax::{ |
@@ -11,6 +9,15 @@ use ra_syntax::{ | |||
11 | }; | 9 | }; |
12 | use ra_text_edit::{TextEdit, TextEditBuilder}; | 10 | use ra_text_edit::{TextEdit, TextEditBuilder}; |
13 | 11 | ||
12 | // Feature: Join Lines | ||
13 | // | ||
14 | // Join selected lines into one, smartly fixing up whitespace, trailing commas, and braces. | ||
15 | // | ||
16 | // |=== | ||
17 | // | Editor | Action Name | ||
18 | // | ||
19 | // | VS Code | **Rust Analyzer: Join lines** | ||
20 | // |=== | ||
14 | pub fn join_lines(file: &SourceFile, range: TextRange) -> TextEdit { | 21 | pub fn join_lines(file: &SourceFile, range: TextRange) -> TextEdit { |
15 | let range = if range.is_empty() { | 22 | let range = if range.is_empty() { |
16 | let syntax = file.syntax(); | 23 | let syntax = file.syntax(); |
diff --git a/crates/ra_ide/src/lib.rs b/crates/ra_ide/src/lib.rs index 5ac002d82..12d5716e8 100644 --- a/crates/ra_ide/src/lib.rs +++ b/crates/ra_ide/src/lib.rs | |||
@@ -23,6 +23,7 @@ mod completion; | |||
23 | mod runnables; | 23 | mod runnables; |
24 | mod goto_definition; | 24 | mod goto_definition; |
25 | mod goto_type_definition; | 25 | mod goto_type_definition; |
26 | mod goto_implementation; | ||
26 | mod extend_selection; | 27 | mod extend_selection; |
27 | mod hover; | 28 | mod hover; |
28 | mod call_hierarchy; | 29 | mod call_hierarchy; |
@@ -30,7 +31,6 @@ mod call_info; | |||
30 | mod syntax_highlighting; | 31 | mod syntax_highlighting; |
31 | mod parent_module; | 32 | mod parent_module; |
32 | mod references; | 33 | mod references; |
33 | mod impls; | ||
34 | mod diagnostics; | 34 | mod diagnostics; |
35 | mod syntax_tree; | 35 | mod syntax_tree; |
36 | mod folding_ranges; | 36 | mod folding_ranges; |
@@ -309,7 +309,8 @@ impl Analysis { | |||
309 | 309 | ||
310 | /// Returns an edit which should be applied when opening a new line, fixing | 310 | /// Returns an edit which should be applied when opening a new line, fixing |
311 | /// up minor stuff like continuing the comment. | 311 | /// up minor stuff like continuing the comment. |
312 | pub fn on_enter(&self, position: FilePosition) -> Cancelable<Option<SourceChange>> { | 312 | /// The edit will be a snippet (with `$0`). |
313 | pub fn on_enter(&self, position: FilePosition) -> Cancelable<Option<TextEdit>> { | ||
313 | self.with_db(|db| typing::on_enter(&db, position)) | 314 | self.with_db(|db| typing::on_enter(&db, position)) |
314 | } | 315 | } |
315 | 316 | ||
@@ -372,7 +373,7 @@ impl Analysis { | |||
372 | &self, | 373 | &self, |
373 | position: FilePosition, | 374 | position: FilePosition, |
374 | ) -> Cancelable<Option<RangeInfo<Vec<NavigationTarget>>>> { | 375 | ) -> Cancelable<Option<RangeInfo<Vec<NavigationTarget>>>> { |
375 | self.with_db(|db| impls::goto_implementation(db, position)) | 376 | self.with_db(|db| goto_implementation::goto_implementation(db, position)) |
376 | } | 377 | } |
377 | 378 | ||
378 | /// Returns the type definitions for the symbol at `position`. | 379 | /// Returns the type definitions for the symbol at `position`. |
diff --git a/crates/ra_ide/src/matching_brace.rs b/crates/ra_ide/src/matching_brace.rs index b85348706..407a9636d 100644 --- a/crates/ra_ide/src/matching_brace.rs +++ b/crates/ra_ide/src/matching_brace.rs | |||
@@ -1,7 +1,16 @@ | |||
1 | //! FIXME: write short doc here | ||
2 | |||
3 | use ra_syntax::{ast::AstNode, SourceFile, SyntaxKind, TextSize, T}; | 1 | use ra_syntax::{ast::AstNode, SourceFile, SyntaxKind, TextSize, T}; |
4 | 2 | ||
3 | // Feature: Matching Brace | ||
4 | // | ||
5 | // If the cursor is on any brace (`<>(){}[]`) which is a part of a brace-pair, | ||
6 | // moves cursor to the matching brace. It uses the actual parser to determine | ||
7 | // braces, so it won't confuse generics with comparisons. | ||
8 | // | ||
9 | // |=== | ||
10 | // | Editor | Action Name | ||
11 | // | ||
12 | // | VS Code | **Rust Analyzer: Find matching brace** | ||
13 | // |=== | ||
5 | pub fn matching_brace(file: &SourceFile, offset: TextSize) -> Option<TextSize> { | 14 | pub fn matching_brace(file: &SourceFile, offset: TextSize) -> Option<TextSize> { |
6 | const BRACES: &[SyntaxKind] = | 15 | const BRACES: &[SyntaxKind] = |
7 | &[T!['{'], T!['}'], T!['['], T![']'], T!['('], T![')'], T![<], T![>]]; | 16 | &[T!['{'], T!['}'], T!['['], T![']'], T!['('], T![')'], T![<], T![>]]; |
diff --git a/crates/ra_ide/src/mock_analysis.rs b/crates/ra_ide/src/mock_analysis.rs index 2c13f206a..ad78d2d93 100644 --- a/crates/ra_ide/src/mock_analysis.rs +++ b/crates/ra_ide/src/mock_analysis.rs | |||
@@ -1,21 +1,81 @@ | |||
1 | //! FIXME: write short doc here | 1 | //! FIXME: write short doc here |
2 | 2 | ||
3 | use std::str::FromStr; | ||
3 | use std::sync::Arc; | 4 | use std::sync::Arc; |
4 | 5 | ||
5 | use ra_cfg::CfgOptions; | 6 | use ra_cfg::CfgOptions; |
6 | use ra_db::{CrateName, Env, RelativePathBuf}; | 7 | use ra_db::{CrateName, Env, RelativePathBuf}; |
7 | use test_utils::{extract_offset, extract_range, parse_fixture, CURSOR_MARKER}; | 8 | use test_utils::{extract_offset, extract_range, parse_fixture, FixtureEntry, CURSOR_MARKER}; |
8 | 9 | ||
9 | use crate::{ | 10 | use crate::{ |
10 | Analysis, AnalysisChange, AnalysisHost, CrateGraph, Edition::Edition2018, FileId, FilePosition, | 11 | Analysis, AnalysisChange, AnalysisHost, CrateGraph, Edition, FileId, FilePosition, FileRange, |
11 | FileRange, SourceRootId, | 12 | SourceRootId, |
12 | }; | 13 | }; |
13 | 14 | ||
15 | #[derive(Debug)] | ||
16 | enum MockFileData { | ||
17 | Plain { path: String, content: String }, | ||
18 | Fixture(FixtureEntry), | ||
19 | } | ||
20 | |||
21 | impl MockFileData { | ||
22 | fn new(path: String, content: String) -> Self { | ||
23 | // `Self::Plain` causes a false warning: 'variant is never constructed: `Plain` ' | ||
24 | // see https://github.com/rust-lang/rust/issues/69018 | ||
25 | MockFileData::Plain { path, content } | ||
26 | } | ||
27 | |||
28 | fn path(&self) -> &str { | ||
29 | match self { | ||
30 | MockFileData::Plain { path, .. } => path.as_str(), | ||
31 | MockFileData::Fixture(f) => f.meta.path().as_str(), | ||
32 | } | ||
33 | } | ||
34 | |||
35 | fn content(&self) -> &str { | ||
36 | match self { | ||
37 | MockFileData::Plain { content, .. } => content, | ||
38 | MockFileData::Fixture(f) => f.text.as_str(), | ||
39 | } | ||
40 | } | ||
41 | |||
42 | fn cfg_options(&self) -> CfgOptions { | ||
43 | match self { | ||
44 | MockFileData::Fixture(f) => { | ||
45 | f.meta.cfg_options().map_or_else(Default::default, |o| o.clone()) | ||
46 | } | ||
47 | _ => CfgOptions::default(), | ||
48 | } | ||
49 | } | ||
50 | |||
51 | fn edition(&self) -> Edition { | ||
52 | match self { | ||
53 | MockFileData::Fixture(f) => { | ||
54 | f.meta.edition().map_or(Edition::Edition2018, |v| Edition::from_str(v).unwrap()) | ||
55 | } | ||
56 | _ => Edition::Edition2018, | ||
57 | } | ||
58 | } | ||
59 | |||
60 | fn env(&self) -> Env { | ||
61 | match self { | ||
62 | MockFileData::Fixture(f) => Env::from(f.meta.env()), | ||
63 | _ => Env::default(), | ||
64 | } | ||
65 | } | ||
66 | } | ||
67 | |||
68 | impl From<FixtureEntry> for MockFileData { | ||
69 | fn from(fixture: FixtureEntry) -> Self { | ||
70 | Self::Fixture(fixture) | ||
71 | } | ||
72 | } | ||
73 | |||
14 | /// Mock analysis is used in test to bootstrap an AnalysisHost/Analysis | 74 | /// Mock analysis is used in test to bootstrap an AnalysisHost/Analysis |
15 | /// from a set of in-memory files. | 75 | /// from a set of in-memory files. |
16 | #[derive(Debug, Default)] | 76 | #[derive(Debug, Default)] |
17 | pub struct MockAnalysis { | 77 | pub struct MockAnalysis { |
18 | files: Vec<(String, String)>, | 78 | files: Vec<MockFileData>, |
19 | } | 79 | } |
20 | 80 | ||
21 | impl MockAnalysis { | 81 | impl MockAnalysis { |
@@ -35,7 +95,7 @@ impl MockAnalysis { | |||
35 | pub fn with_files(fixture: &str) -> MockAnalysis { | 95 | pub fn with_files(fixture: &str) -> MockAnalysis { |
36 | let mut res = MockAnalysis::new(); | 96 | let mut res = MockAnalysis::new(); |
37 | for entry in parse_fixture(fixture) { | 97 | for entry in parse_fixture(fixture) { |
38 | res.add_file(&entry.meta, &entry.text); | 98 | res.add_file_fixture(entry); |
39 | } | 99 | } |
40 | res | 100 | res |
41 | } | 101 | } |
@@ -48,30 +108,44 @@ impl MockAnalysis { | |||
48 | for entry in parse_fixture(fixture) { | 108 | for entry in parse_fixture(fixture) { |
49 | if entry.text.contains(CURSOR_MARKER) { | 109 | if entry.text.contains(CURSOR_MARKER) { |
50 | assert!(position.is_none(), "only one marker (<|>) per fixture is allowed"); | 110 | assert!(position.is_none(), "only one marker (<|>) per fixture is allowed"); |
51 | position = Some(res.add_file_with_position(&entry.meta, &entry.text)); | 111 | position = Some(res.add_file_fixture_with_position(entry)); |
52 | } else { | 112 | } else { |
53 | res.add_file(&entry.meta, &entry.text); | 113 | res.add_file_fixture(entry); |
54 | } | 114 | } |
55 | } | 115 | } |
56 | let position = position.expect("expected a marker (<|>)"); | 116 | let position = position.expect("expected a marker (<|>)"); |
57 | (res, position) | 117 | (res, position) |
58 | } | 118 | } |
59 | 119 | ||
120 | pub fn add_file_fixture(&mut self, fixture: FixtureEntry) -> FileId { | ||
121 | let file_id = self.next_id(); | ||
122 | self.files.push(MockFileData::from(fixture)); | ||
123 | file_id | ||
124 | } | ||
125 | |||
126 | pub fn add_file_fixture_with_position(&mut self, mut fixture: FixtureEntry) -> FilePosition { | ||
127 | let (offset, text) = extract_offset(&fixture.text); | ||
128 | fixture.text = text; | ||
129 | let file_id = self.next_id(); | ||
130 | self.files.push(MockFileData::from(fixture)); | ||
131 | FilePosition { file_id, offset } | ||
132 | } | ||
133 | |||
60 | pub fn add_file(&mut self, path: &str, text: &str) -> FileId { | 134 | pub fn add_file(&mut self, path: &str, text: &str) -> FileId { |
61 | let file_id = FileId((self.files.len() + 1) as u32); | 135 | let file_id = self.next_id(); |
62 | self.files.push((path.to_string(), text.to_string())); | 136 | self.files.push(MockFileData::new(path.to_string(), text.to_string())); |
63 | file_id | 137 | file_id |
64 | } | 138 | } |
65 | pub fn add_file_with_position(&mut self, path: &str, text: &str) -> FilePosition { | 139 | pub fn add_file_with_position(&mut self, path: &str, text: &str) -> FilePosition { |
66 | let (offset, text) = extract_offset(text); | 140 | let (offset, text) = extract_offset(text); |
67 | let file_id = FileId((self.files.len() + 1) as u32); | 141 | let file_id = self.next_id(); |
68 | self.files.push((path.to_string(), text)); | 142 | self.files.push(MockFileData::new(path.to_string(), text)); |
69 | FilePosition { file_id, offset } | 143 | FilePosition { file_id, offset } |
70 | } | 144 | } |
71 | pub fn add_file_with_range(&mut self, path: &str, text: &str) -> FileRange { | 145 | pub fn add_file_with_range(&mut self, path: &str, text: &str) -> FileRange { |
72 | let (range, text) = extract_range(text); | 146 | let (range, text) = extract_range(text); |
73 | let file_id = FileId((self.files.len() + 1) as u32); | 147 | let file_id = self.next_id(); |
74 | self.files.push((path.to_string(), text)); | 148 | self.files.push(MockFileData::new(path.to_string(), text)); |
75 | FileRange { file_id, range } | 149 | FileRange { file_id, range } |
76 | } | 150 | } |
77 | pub fn id_of(&self, path: &str) -> FileId { | 151 | pub fn id_of(&self, path: &str) -> FileId { |
@@ -79,7 +153,7 @@ impl MockAnalysis { | |||
79 | .files | 153 | .files |
80 | .iter() | 154 | .iter() |
81 | .enumerate() | 155 | .enumerate() |
82 | .find(|(_, (p, _text))| path == p) | 156 | .find(|(_, data)| path == data.path()) |
83 | .expect("no file in this mock"); | 157 | .expect("no file in this mock"); |
84 | FileId(idx as u32 + 1) | 158 | FileId(idx as u32 + 1) |
85 | } | 159 | } |
@@ -90,18 +164,21 @@ impl MockAnalysis { | |||
90 | change.add_root(source_root, true); | 164 | change.add_root(source_root, true); |
91 | let mut crate_graph = CrateGraph::default(); | 165 | let mut crate_graph = CrateGraph::default(); |
92 | let mut root_crate = None; | 166 | let mut root_crate = None; |
93 | for (i, (path, contents)) in self.files.into_iter().enumerate() { | 167 | for (i, data) in self.files.into_iter().enumerate() { |
168 | let path = data.path(); | ||
94 | assert!(path.starts_with('/')); | 169 | assert!(path.starts_with('/')); |
95 | let path = RelativePathBuf::from_path(&path[1..]).unwrap(); | 170 | let path = RelativePathBuf::from_path(&path[1..]).unwrap(); |
171 | let cfg_options = data.cfg_options(); | ||
96 | let file_id = FileId(i as u32 + 1); | 172 | let file_id = FileId(i as u32 + 1); |
97 | let cfg_options = CfgOptions::default(); | 173 | let edition = data.edition(); |
174 | let env = data.env(); | ||
98 | if path == "/lib.rs" || path == "/main.rs" { | 175 | if path == "/lib.rs" || path == "/main.rs" { |
99 | root_crate = Some(crate_graph.add_crate_root( | 176 | root_crate = Some(crate_graph.add_crate_root( |
100 | file_id, | 177 | file_id, |
101 | Edition2018, | 178 | edition, |
102 | None, | 179 | None, |
103 | cfg_options, | 180 | cfg_options, |
104 | Env::default(), | 181 | env, |
105 | Default::default(), | 182 | Default::default(), |
106 | Default::default(), | 183 | Default::default(), |
107 | )); | 184 | )); |
@@ -109,10 +186,10 @@ impl MockAnalysis { | |||
109 | let crate_name = path.parent().unwrap().file_name().unwrap(); | 186 | let crate_name = path.parent().unwrap().file_name().unwrap(); |
110 | let other_crate = crate_graph.add_crate_root( | 187 | let other_crate = crate_graph.add_crate_root( |
111 | file_id, | 188 | file_id, |
112 | Edition2018, | 189 | edition, |
113 | Some(CrateName::new(crate_name).unwrap()), | 190 | Some(CrateName::new(crate_name).unwrap()), |
114 | cfg_options, | 191 | cfg_options, |
115 | Env::default(), | 192 | env, |
116 | Default::default(), | 193 | Default::default(), |
117 | Default::default(), | 194 | Default::default(), |
118 | ); | 195 | ); |
@@ -122,7 +199,7 @@ impl MockAnalysis { | |||
122 | .unwrap(); | 199 | .unwrap(); |
123 | } | 200 | } |
124 | } | 201 | } |
125 | change.add_file(source_root, file_id, path, Arc::new(contents)); | 202 | change.add_file(source_root, file_id, path, Arc::new(data.content().to_owned())); |
126 | } | 203 | } |
127 | change.set_crate_graph(crate_graph); | 204 | change.set_crate_graph(crate_graph); |
128 | host.apply_change(change); | 205 | host.apply_change(change); |
@@ -131,6 +208,10 @@ impl MockAnalysis { | |||
131 | pub fn analysis(self) -> Analysis { | 208 | pub fn analysis(self) -> Analysis { |
132 | self.analysis_host().analysis() | 209 | self.analysis_host().analysis() |
133 | } | 210 | } |
211 | |||
212 | fn next_id(&self) -> FileId { | ||
213 | FileId((self.files.len() + 1) as u32) | ||
214 | } | ||
134 | } | 215 | } |
135 | 216 | ||
136 | /// Creates analysis from a multi-file fixture, returns positions marked with <|>. | 217 | /// Creates analysis from a multi-file fixture, returns positions marked with <|>. |
diff --git a/crates/ra_ide/src/parent_module.rs b/crates/ra_ide/src/parent_module.rs index a083fb1eb..fa1535da5 100644 --- a/crates/ra_ide/src/parent_module.rs +++ b/crates/ra_ide/src/parent_module.rs | |||
@@ -1,5 +1,3 @@ | |||
1 | //! FIXME: write short doc here | ||
2 | |||
3 | use hir::Semantics; | 1 | use hir::Semantics; |
4 | use ra_db::{CrateId, FileId, FilePosition}; | 2 | use ra_db::{CrateId, FileId, FilePosition}; |
5 | use ra_ide_db::RootDatabase; | 3 | use ra_ide_db::RootDatabase; |
@@ -11,6 +9,16 @@ use test_utils::mark; | |||
11 | 9 | ||
12 | use crate::NavigationTarget; | 10 | use crate::NavigationTarget; |
13 | 11 | ||
12 | // Feature: Parent Module | ||
13 | // | ||
14 | // Navigates to the parent module of the current module. | ||
15 | // | ||
16 | // |=== | ||
17 | // | Editor | Action Name | ||
18 | // | ||
19 | // | VS Code | **Rust Analyzer: Locate parent module** | ||
20 | // |=== | ||
21 | |||
14 | /// This returns `Vec` because a module may be included from several places. We | 22 | /// This returns `Vec` because a module may be included from several places. We |
15 | /// don't handle this case yet though, so the Vec has length at most one. | 23 | /// don't handle this case yet though, so the Vec has length at most one. |
16 | pub(crate) fn parent_module(db: &RootDatabase, position: FilePosition) -> Vec<NavigationTarget> { | 24 | pub(crate) fn parent_module(db: &RootDatabase, position: FilePosition) -> Vec<NavigationTarget> { |
diff --git a/crates/ra_ide/src/references.rs b/crates/ra_ide/src/references.rs index 96444bf6a..bb40d2043 100644 --- a/crates/ra_ide/src/references.rs +++ b/crates/ra_ide/src/references.rs | |||
@@ -615,6 +615,33 @@ mod tests { | |||
615 | ); | 615 | ); |
616 | } | 616 | } |
617 | 617 | ||
618 | #[test] | ||
619 | fn test_find_all_refs_nested_module() { | ||
620 | let code = r#" | ||
621 | //- /lib.rs | ||
622 | mod foo { | ||
623 | mod bar; | ||
624 | } | ||
625 | |||
626 | fn f<|>() {} | ||
627 | |||
628 | //- /foo/bar.rs | ||
629 | use crate::f; | ||
630 | |||
631 | fn g() { | ||
632 | f(); | ||
633 | } | ||
634 | "#; | ||
635 | |||
636 | let (analysis, pos) = analysis_and_position(code); | ||
637 | let refs = analysis.find_all_refs(pos, None).unwrap().unwrap(); | ||
638 | check_result( | ||
639 | refs, | ||
640 | "f FN_DEF FileId(1) 25..34 28..29 Other", | ||
641 | &["FileId(2) 11..12 Other", "FileId(2) 27..28 StructLiteral"], | ||
642 | ); | ||
643 | } | ||
644 | |||
618 | fn get_all_refs(text: &str) -> ReferenceSearchResult { | 645 | fn get_all_refs(text: &str) -> ReferenceSearchResult { |
619 | let (analysis, position) = single_file_with_position(text); | 646 | let (analysis, position) = single_file_with_position(text); |
620 | analysis.find_all_refs(position, None).unwrap().unwrap() | 647 | analysis.find_all_refs(position, None).unwrap().unwrap() |
diff --git a/crates/ra_ide/src/runnables.rs b/crates/ra_ide/src/runnables.rs index 131b8f307..f32ce0d22 100644 --- a/crates/ra_ide/src/runnables.rs +++ b/crates/ra_ide/src/runnables.rs | |||
@@ -1,21 +1,21 @@ | |||
1 | //! FIXME: write short doc here | 1 | use std::fmt; |
2 | 2 | ||
3 | use hir::{AsAssocItem, Semantics}; | 3 | use hir::{AsAssocItem, Attrs, HirFileId, InFile, Semantics}; |
4 | use itertools::Itertools; | 4 | use itertools::Itertools; |
5 | use ra_cfg::CfgExpr; | ||
5 | use ra_ide_db::RootDatabase; | 6 | use ra_ide_db::RootDatabase; |
6 | use ra_syntax::{ | 7 | use ra_syntax::{ |
7 | ast::{self, AstNode, AttrsOwner, ModuleItemOwner, NameOwner}, | 8 | ast::{self, AstNode, AttrsOwner, DocCommentsOwner, ModuleItemOwner, NameOwner}, |
8 | match_ast, SyntaxNode, TextRange, | 9 | match_ast, SyntaxNode, |
9 | }; | 10 | }; |
10 | 11 | ||
11 | use crate::FileId; | 12 | use crate::{display::ToNav, FileId, NavigationTarget}; |
12 | use ast::DocCommentsOwner; | ||
13 | use std::fmt::Display; | ||
14 | 13 | ||
15 | #[derive(Debug)] | 14 | #[derive(Debug)] |
16 | pub struct Runnable { | 15 | pub struct Runnable { |
17 | pub range: TextRange, | 16 | pub nav: NavigationTarget, |
18 | pub kind: RunnableKind, | 17 | pub kind: RunnableKind, |
18 | pub cfg_exprs: Vec<CfgExpr>, | ||
19 | } | 19 | } |
20 | 20 | ||
21 | #[derive(Debug)] | 21 | #[derive(Debug)] |
@@ -24,8 +24,8 @@ pub enum TestId { | |||
24 | Path(String), | 24 | Path(String), |
25 | } | 25 | } |
26 | 26 | ||
27 | impl Display for TestId { | 27 | impl fmt::Display for TestId { |
28 | fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { | 28 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
29 | match self { | 29 | match self { |
30 | TestId::Name(name) => write!(f, "{}", name), | 30 | TestId::Name(name) => write!(f, "{}", name), |
31 | TestId::Path(path) => write!(f, "{}", path), | 31 | TestId::Path(path) => write!(f, "{}", path), |
@@ -42,32 +42,47 @@ pub enum RunnableKind { | |||
42 | Bin, | 42 | Bin, |
43 | } | 43 | } |
44 | 44 | ||
45 | // Feature: Run | ||
46 | // | ||
47 | // Shows a popup suggesting to run a test/benchmark/binary **at the current cursor | ||
48 | // location**. Super useful for repeatedly running just a single test. Do bind this | ||
49 | // to a shortcut! | ||
50 | // | ||
51 | // |=== | ||
52 | // | Editor | Action Name | ||
53 | // | ||
54 | // | VS Code | **Rust Analyzer: Run** | ||
55 | // |=== | ||
45 | pub(crate) fn runnables(db: &RootDatabase, file_id: FileId) -> Vec<Runnable> { | 56 | pub(crate) fn runnables(db: &RootDatabase, file_id: FileId) -> Vec<Runnable> { |
46 | let sema = Semantics::new(db); | 57 | let sema = Semantics::new(db); |
47 | let source_file = sema.parse(file_id); | 58 | let source_file = sema.parse(file_id); |
48 | source_file.syntax().descendants().filter_map(|i| runnable(&sema, i)).collect() | 59 | source_file.syntax().descendants().filter_map(|i| runnable(&sema, i, file_id)).collect() |
49 | } | 60 | } |
50 | 61 | ||
51 | fn runnable(sema: &Semantics<RootDatabase>, item: SyntaxNode) -> Option<Runnable> { | 62 | fn runnable(sema: &Semantics<RootDatabase>, item: SyntaxNode, file_id: FileId) -> Option<Runnable> { |
52 | match_ast! { | 63 | match_ast! { |
53 | match item { | 64 | match item { |
54 | ast::FnDef(it) => runnable_fn(sema, it), | 65 | ast::FnDef(it) => runnable_fn(sema, it, file_id), |
55 | ast::Module(it) => runnable_mod(sema, it), | 66 | ast::Module(it) => runnable_mod(sema, it, file_id), |
56 | _ => None, | 67 | _ => None, |
57 | } | 68 | } |
58 | } | 69 | } |
59 | } | 70 | } |
60 | 71 | ||
61 | fn runnable_fn(sema: &Semantics<RootDatabase>, fn_def: ast::FnDef) -> Option<Runnable> { | 72 | fn runnable_fn( |
73 | sema: &Semantics<RootDatabase>, | ||
74 | fn_def: ast::FnDef, | ||
75 | file_id: FileId, | ||
76 | ) -> Option<Runnable> { | ||
62 | let name_string = fn_def.name()?.text().to_string(); | 77 | let name_string = fn_def.name()?.text().to_string(); |
63 | 78 | ||
64 | let kind = if name_string == "main" { | 79 | let kind = if name_string == "main" { |
65 | RunnableKind::Bin | 80 | RunnableKind::Bin |
66 | } else { | 81 | } else { |
67 | let test_id = if let Some(module) = sema.to_def(&fn_def).map(|def| def.module(sema.db)) { | 82 | let test_id = match sema.to_def(&fn_def).map(|def| def.module(sema.db)) { |
68 | let def = sema.to_def(&fn_def)?; | 83 | Some(module) => { |
69 | let impl_trait_name = | 84 | let def = sema.to_def(&fn_def)?; |
70 | def.as_assoc_item(sema.db).and_then(|assoc_item| { | 85 | let impl_trait_name = def.as_assoc_item(sema.db).and_then(|assoc_item| { |
71 | match assoc_item.container(sema.db) { | 86 | match assoc_item.container(sema.db) { |
72 | hir::AssocItemContainer::Trait(trait_item) => { | 87 | hir::AssocItemContainer::Trait(trait_item) => { |
73 | Some(trait_item.name(sema.db).to_string()) | 88 | Some(trait_item.name(sema.db).to_string()) |
@@ -79,25 +94,25 @@ fn runnable_fn(sema: &Semantics<RootDatabase>, fn_def: ast::FnDef) -> Option<Run | |||
79 | } | 94 | } |
80 | }); | 95 | }); |
81 | 96 | ||
82 | let path_iter = module | 97 | let path_iter = module |
83 | .path_to_root(sema.db) | 98 | .path_to_root(sema.db) |
84 | .into_iter() | 99 | .into_iter() |
85 | .rev() | 100 | .rev() |
86 | .filter_map(|it| it.name(sema.db)) | 101 | .filter_map(|it| it.name(sema.db)) |
87 | .map(|name| name.to_string()); | 102 | .map(|name| name.to_string()); |
88 | 103 | ||
89 | let path = if let Some(impl_trait_name) = impl_trait_name { | 104 | let path = if let Some(impl_trait_name) = impl_trait_name { |
90 | path_iter | 105 | path_iter |
91 | .chain(std::iter::once(impl_trait_name)) | 106 | .chain(std::iter::once(impl_trait_name)) |
92 | .chain(std::iter::once(name_string)) | 107 | .chain(std::iter::once(name_string)) |
93 | .join("::") | 108 | .join("::") |
94 | } else { | 109 | } else { |
95 | path_iter.chain(std::iter::once(name_string)).join("::") | 110 | path_iter.chain(std::iter::once(name_string)).join("::") |
96 | }; | 111 | }; |
97 | 112 | ||
98 | TestId::Path(path) | 113 | TestId::Path(path) |
99 | } else { | 114 | } |
100 | TestId::Name(name_string) | 115 | None => TestId::Name(name_string), |
101 | }; | 116 | }; |
102 | 117 | ||
103 | if has_test_related_attribute(&fn_def) { | 118 | if has_test_related_attribute(&fn_def) { |
@@ -111,7 +126,13 @@ fn runnable_fn(sema: &Semantics<RootDatabase>, fn_def: ast::FnDef) -> Option<Run | |||
111 | return None; | 126 | return None; |
112 | } | 127 | } |
113 | }; | 128 | }; |
114 | Some(Runnable { range: fn_def.syntax().text_range(), kind }) | 129 | |
130 | let attrs = Attrs::from_attrs_owner(sema.db, InFile::new(HirFileId::from(file_id), &fn_def)); | ||
131 | let cfg_exprs = | ||
132 | attrs.by_key("cfg").tt_values().map(|subtree| ra_cfg::parse_cfg(subtree)).collect(); | ||
133 | |||
134 | let nav = NavigationTarget::from_named(sema.db, InFile::new(file_id.into(), &fn_def)); | ||
135 | Some(Runnable { nav, kind, cfg_exprs }) | ||
115 | } | 136 | } |
116 | 137 | ||
117 | #[derive(Debug)] | 138 | #[derive(Debug)] |
@@ -147,7 +168,11 @@ fn has_doc_test(fn_def: &ast::FnDef) -> bool { | |||
147 | fn_def.doc_comment_text().map_or(false, |comment| comment.contains("```")) | 168 | fn_def.doc_comment_text().map_or(false, |comment| comment.contains("```")) |
148 | } | 169 | } |
149 | 170 | ||
150 | fn runnable_mod(sema: &Semantics<RootDatabase>, module: ast::Module) -> Option<Runnable> { | 171 | fn runnable_mod( |
172 | sema: &Semantics<RootDatabase>, | ||
173 | module: ast::Module, | ||
174 | file_id: FileId, | ||
175 | ) -> Option<Runnable> { | ||
151 | let has_test_function = module | 176 | let has_test_function = module |
152 | .item_list()? | 177 | .item_list()? |
153 | .items() | 178 | .items() |
@@ -159,12 +184,21 @@ fn runnable_mod(sema: &Semantics<RootDatabase>, module: ast::Module) -> Option<R | |||
159 | if !has_test_function { | 184 | if !has_test_function { |
160 | return None; | 185 | return None; |
161 | } | 186 | } |
162 | let range = module.syntax().text_range(); | 187 | let module_def = sema.to_def(&module)?; |
163 | let module = sema.to_def(&module)?; | ||
164 | 188 | ||
165 | let path = | 189 | let path = module_def |
166 | module.path_to_root(sema.db).into_iter().rev().filter_map(|it| it.name(sema.db)).join("::"); | 190 | .path_to_root(sema.db) |
167 | Some(Runnable { range, kind: RunnableKind::TestMod { path } }) | 191 | .into_iter() |
192 | .rev() | ||
193 | .filter_map(|it| it.name(sema.db)) | ||
194 | .join("::"); | ||
195 | |||
196 | let attrs = Attrs::from_attrs_owner(sema.db, InFile::new(HirFileId::from(file_id), &module)); | ||
197 | let cfg_exprs = | ||
198 | attrs.by_key("cfg").tt_values().map(|subtree| ra_cfg::parse_cfg(subtree)).collect(); | ||
199 | |||
200 | let nav = module_def.to_nav(sema.db); | ||
201 | Some(Runnable { nav, kind: RunnableKind::TestMod { path }, cfg_exprs }) | ||
168 | } | 202 | } |
169 | 203 | ||
170 | #[cfg(test)] | 204 | #[cfg(test)] |
@@ -194,11 +228,38 @@ mod tests { | |||
194 | @r###" | 228 | @r###" |
195 | [ | 229 | [ |
196 | Runnable { | 230 | Runnable { |
197 | range: 1..21, | 231 | nav: NavigationTarget { |
232 | file_id: FileId( | ||
233 | 1, | ||
234 | ), | ||
235 | full_range: 1..21, | ||
236 | name: "main", | ||
237 | kind: FN_DEF, | ||
238 | focus_range: Some( | ||
239 | 12..16, | ||
240 | ), | ||
241 | container_name: None, | ||
242 | description: None, | ||
243 | docs: None, | ||
244 | }, | ||
198 | kind: Bin, | 245 | kind: Bin, |
246 | cfg_exprs: [], | ||
199 | }, | 247 | }, |
200 | Runnable { | 248 | Runnable { |
201 | range: 22..46, | 249 | nav: NavigationTarget { |
250 | file_id: FileId( | ||
251 | 1, | ||
252 | ), | ||
253 | full_range: 22..46, | ||
254 | name: "test_foo", | ||
255 | kind: FN_DEF, | ||
256 | focus_range: Some( | ||
257 | 33..41, | ||
258 | ), | ||
259 | container_name: None, | ||
260 | description: None, | ||
261 | docs: None, | ||
262 | }, | ||
202 | kind: Test { | 263 | kind: Test { |
203 | test_id: Path( | 264 | test_id: Path( |
204 | "test_foo", | 265 | "test_foo", |
@@ -207,9 +268,23 @@ mod tests { | |||
207 | ignore: false, | 268 | ignore: false, |
208 | }, | 269 | }, |
209 | }, | 270 | }, |
271 | cfg_exprs: [], | ||
210 | }, | 272 | }, |
211 | Runnable { | 273 | Runnable { |
212 | range: 47..81, | 274 | nav: NavigationTarget { |
275 | file_id: FileId( | ||
276 | 1, | ||
277 | ), | ||
278 | full_range: 47..81, | ||
279 | name: "test_foo", | ||
280 | kind: FN_DEF, | ||
281 | focus_range: Some( | ||
282 | 68..76, | ||
283 | ), | ||
284 | container_name: None, | ||
285 | description: None, | ||
286 | docs: None, | ||
287 | }, | ||
213 | kind: Test { | 288 | kind: Test { |
214 | test_id: Path( | 289 | test_id: Path( |
215 | "test_foo", | 290 | "test_foo", |
@@ -218,6 +293,7 @@ mod tests { | |||
218 | ignore: true, | 293 | ignore: true, |
219 | }, | 294 | }, |
220 | }, | 295 | }, |
296 | cfg_exprs: [], | ||
221 | }, | 297 | }, |
222 | ] | 298 | ] |
223 | "### | 299 | "### |
@@ -243,16 +319,44 @@ mod tests { | |||
243 | @r###" | 319 | @r###" |
244 | [ | 320 | [ |
245 | Runnable { | 321 | Runnable { |
246 | range: 1..21, | 322 | nav: NavigationTarget { |
323 | file_id: FileId( | ||
324 | 1, | ||
325 | ), | ||
326 | full_range: 1..21, | ||
327 | name: "main", | ||
328 | kind: FN_DEF, | ||
329 | focus_range: Some( | ||
330 | 12..16, | ||
331 | ), | ||
332 | container_name: None, | ||
333 | description: None, | ||
334 | docs: None, | ||
335 | }, | ||
247 | kind: Bin, | 336 | kind: Bin, |
337 | cfg_exprs: [], | ||
248 | }, | 338 | }, |
249 | Runnable { | 339 | Runnable { |
250 | range: 22..64, | 340 | nav: NavigationTarget { |
341 | file_id: FileId( | ||
342 | 1, | ||
343 | ), | ||
344 | full_range: 22..64, | ||
345 | name: "foo", | ||
346 | kind: FN_DEF, | ||
347 | focus_range: Some( | ||
348 | 56..59, | ||
349 | ), | ||
350 | container_name: None, | ||
351 | description: None, | ||
352 | docs: None, | ||
353 | }, | ||
251 | kind: DocTest { | 354 | kind: DocTest { |
252 | test_id: Path( | 355 | test_id: Path( |
253 | "foo", | 356 | "foo", |
254 | ), | 357 | ), |
255 | }, | 358 | }, |
359 | cfg_exprs: [], | ||
256 | }, | 360 | }, |
257 | ] | 361 | ] |
258 | "### | 362 | "### |
@@ -281,16 +385,44 @@ mod tests { | |||
281 | @r###" | 385 | @r###" |
282 | [ | 386 | [ |
283 | Runnable { | 387 | Runnable { |
284 | range: 1..21, | 388 | nav: NavigationTarget { |
389 | file_id: FileId( | ||
390 | 1, | ||
391 | ), | ||
392 | full_range: 1..21, | ||
393 | name: "main", | ||
394 | kind: FN_DEF, | ||
395 | focus_range: Some( | ||
396 | 12..16, | ||
397 | ), | ||
398 | container_name: None, | ||
399 | description: None, | ||
400 | docs: None, | ||
401 | }, | ||
285 | kind: Bin, | 402 | kind: Bin, |
403 | cfg_exprs: [], | ||
286 | }, | 404 | }, |
287 | Runnable { | 405 | Runnable { |
288 | range: 51..105, | 406 | nav: NavigationTarget { |
407 | file_id: FileId( | ||
408 | 1, | ||
409 | ), | ||
410 | full_range: 51..105, | ||
411 | name: "foo", | ||
412 | kind: FN_DEF, | ||
413 | focus_range: Some( | ||
414 | 97..100, | ||
415 | ), | ||
416 | container_name: None, | ||
417 | description: None, | ||
418 | docs: None, | ||
419 | }, | ||
289 | kind: DocTest { | 420 | kind: DocTest { |
290 | test_id: Path( | 421 | test_id: Path( |
291 | "Data::foo", | 422 | "Data::foo", |
292 | ), | 423 | ), |
293 | }, | 424 | }, |
425 | cfg_exprs: [], | ||
294 | }, | 426 | }, |
295 | ] | 427 | ] |
296 | "### | 428 | "### |
@@ -314,13 +446,40 @@ mod tests { | |||
314 | @r###" | 446 | @r###" |
315 | [ | 447 | [ |
316 | Runnable { | 448 | Runnable { |
317 | range: 1..59, | 449 | nav: NavigationTarget { |
450 | file_id: FileId( | ||
451 | 1, | ||
452 | ), | ||
453 | full_range: 1..59, | ||
454 | name: "test_mod", | ||
455 | kind: MODULE, | ||
456 | focus_range: Some( | ||
457 | 13..21, | ||
458 | ), | ||
459 | container_name: None, | ||
460 | description: None, | ||
461 | docs: None, | ||
462 | }, | ||
318 | kind: TestMod { | 463 | kind: TestMod { |
319 | path: "test_mod", | 464 | path: "test_mod", |
320 | }, | 465 | }, |
466 | cfg_exprs: [], | ||
321 | }, | 467 | }, |
322 | Runnable { | 468 | Runnable { |
323 | range: 28..57, | 469 | nav: NavigationTarget { |
470 | file_id: FileId( | ||
471 | 1, | ||
472 | ), | ||
473 | full_range: 28..57, | ||
474 | name: "test_foo1", | ||
475 | kind: FN_DEF, | ||
476 | focus_range: Some( | ||
477 | 43..52, | ||
478 | ), | ||
479 | container_name: None, | ||
480 | description: None, | ||
481 | docs: None, | ||
482 | }, | ||
324 | kind: Test { | 483 | kind: Test { |
325 | test_id: Path( | 484 | test_id: Path( |
326 | "test_mod::test_foo1", | 485 | "test_mod::test_foo1", |
@@ -329,6 +488,7 @@ mod tests { | |||
329 | ignore: false, | 488 | ignore: false, |
330 | }, | 489 | }, |
331 | }, | 490 | }, |
491 | cfg_exprs: [], | ||
332 | }, | 492 | }, |
333 | ] | 493 | ] |
334 | "### | 494 | "### |
@@ -354,13 +514,40 @@ mod tests { | |||
354 | @r###" | 514 | @r###" |
355 | [ | 515 | [ |
356 | Runnable { | 516 | Runnable { |
357 | range: 23..85, | 517 | nav: NavigationTarget { |
518 | file_id: FileId( | ||
519 | 1, | ||
520 | ), | ||
521 | full_range: 23..85, | ||
522 | name: "test_mod", | ||
523 | kind: MODULE, | ||
524 | focus_range: Some( | ||
525 | 27..35, | ||
526 | ), | ||
527 | container_name: None, | ||
528 | description: None, | ||
529 | docs: None, | ||
530 | }, | ||
358 | kind: TestMod { | 531 | kind: TestMod { |
359 | path: "foo::test_mod", | 532 | path: "foo::test_mod", |
360 | }, | 533 | }, |
534 | cfg_exprs: [], | ||
361 | }, | 535 | }, |
362 | Runnable { | 536 | Runnable { |
363 | range: 46..79, | 537 | nav: NavigationTarget { |
538 | file_id: FileId( | ||
539 | 1, | ||
540 | ), | ||
541 | full_range: 46..79, | ||
542 | name: "test_foo1", | ||
543 | kind: FN_DEF, | ||
544 | focus_range: Some( | ||
545 | 65..74, | ||
546 | ), | ||
547 | container_name: None, | ||
548 | description: None, | ||
549 | docs: None, | ||
550 | }, | ||
364 | kind: Test { | 551 | kind: Test { |
365 | test_id: Path( | 552 | test_id: Path( |
366 | "foo::test_mod::test_foo1", | 553 | "foo::test_mod::test_foo1", |
@@ -369,6 +556,7 @@ mod tests { | |||
369 | ignore: false, | 556 | ignore: false, |
370 | }, | 557 | }, |
371 | }, | 558 | }, |
559 | cfg_exprs: [], | ||
372 | }, | 560 | }, |
373 | ] | 561 | ] |
374 | "### | 562 | "### |
@@ -396,13 +584,40 @@ mod tests { | |||
396 | @r###" | 584 | @r###" |
397 | [ | 585 | [ |
398 | Runnable { | 586 | Runnable { |
399 | range: 41..115, | 587 | nav: NavigationTarget { |
588 | file_id: FileId( | ||
589 | 1, | ||
590 | ), | ||
591 | full_range: 41..115, | ||
592 | name: "test_mod", | ||
593 | kind: MODULE, | ||
594 | focus_range: Some( | ||
595 | 45..53, | ||
596 | ), | ||
597 | container_name: None, | ||
598 | description: None, | ||
599 | docs: None, | ||
600 | }, | ||
400 | kind: TestMod { | 601 | kind: TestMod { |
401 | path: "foo::bar::test_mod", | 602 | path: "foo::bar::test_mod", |
402 | }, | 603 | }, |
604 | cfg_exprs: [], | ||
403 | }, | 605 | }, |
404 | Runnable { | 606 | Runnable { |
405 | range: 68..105, | 607 | nav: NavigationTarget { |
608 | file_id: FileId( | ||
609 | 1, | ||
610 | ), | ||
611 | full_range: 68..105, | ||
612 | name: "test_foo1", | ||
613 | kind: FN_DEF, | ||
614 | focus_range: Some( | ||
615 | 91..100, | ||
616 | ), | ||
617 | container_name: None, | ||
618 | description: None, | ||
619 | docs: None, | ||
620 | }, | ||
406 | kind: Test { | 621 | kind: Test { |
407 | test_id: Path( | 622 | test_id: Path( |
408 | "foo::bar::test_mod::test_foo1", | 623 | "foo::bar::test_mod::test_foo1", |
@@ -411,6 +626,115 @@ mod tests { | |||
411 | ignore: false, | 626 | ignore: false, |
412 | }, | 627 | }, |
413 | }, | 628 | }, |
629 | cfg_exprs: [], | ||
630 | }, | ||
631 | ] | ||
632 | "### | ||
633 | ); | ||
634 | } | ||
635 | |||
636 | #[test] | ||
637 | fn test_runnables_with_feature() { | ||
638 | let (analysis, pos) = analysis_and_position( | ||
639 | r#" | ||
640 | //- /lib.rs crate:foo cfg:feature=foo | ||
641 | <|> //empty | ||
642 | #[test] | ||
643 | #[cfg(feature = "foo")] | ||
644 | fn test_foo1() {} | ||
645 | "#, | ||
646 | ); | ||
647 | let runnables = analysis.runnables(pos.file_id).unwrap(); | ||
648 | assert_debug_snapshot!(&runnables, | ||
649 | @r###" | ||
650 | [ | ||
651 | Runnable { | ||
652 | nav: NavigationTarget { | ||
653 | file_id: FileId( | ||
654 | 1, | ||
655 | ), | ||
656 | full_range: 1..58, | ||
657 | name: "test_foo1", | ||
658 | kind: FN_DEF, | ||
659 | focus_range: Some( | ||
660 | 44..53, | ||
661 | ), | ||
662 | container_name: None, | ||
663 | description: None, | ||
664 | docs: None, | ||
665 | }, | ||
666 | kind: Test { | ||
667 | test_id: Path( | ||
668 | "test_foo1", | ||
669 | ), | ||
670 | attr: TestAttr { | ||
671 | ignore: false, | ||
672 | }, | ||
673 | }, | ||
674 | cfg_exprs: [ | ||
675 | KeyValue { | ||
676 | key: "feature", | ||
677 | value: "foo", | ||
678 | }, | ||
679 | ], | ||
680 | }, | ||
681 | ] | ||
682 | "### | ||
683 | ); | ||
684 | } | ||
685 | |||
686 | #[test] | ||
687 | fn test_runnables_with_features() { | ||
688 | let (analysis, pos) = analysis_and_position( | ||
689 | r#" | ||
690 | //- /lib.rs crate:foo cfg:feature=foo,feature=bar | ||
691 | <|> //empty | ||
692 | #[test] | ||
693 | #[cfg(all(feature = "foo", feature = "bar"))] | ||
694 | fn test_foo1() {} | ||
695 | "#, | ||
696 | ); | ||
697 | let runnables = analysis.runnables(pos.file_id).unwrap(); | ||
698 | assert_debug_snapshot!(&runnables, | ||
699 | @r###" | ||
700 | [ | ||
701 | Runnable { | ||
702 | nav: NavigationTarget { | ||
703 | file_id: FileId( | ||
704 | 1, | ||
705 | ), | ||
706 | full_range: 1..80, | ||
707 | name: "test_foo1", | ||
708 | kind: FN_DEF, | ||
709 | focus_range: Some( | ||
710 | 66..75, | ||
711 | ), | ||
712 | container_name: None, | ||
713 | description: None, | ||
714 | docs: None, | ||
715 | }, | ||
716 | kind: Test { | ||
717 | test_id: Path( | ||
718 | "test_foo1", | ||
719 | ), | ||
720 | attr: TestAttr { | ||
721 | ignore: false, | ||
722 | }, | ||
723 | }, | ||
724 | cfg_exprs: [ | ||
725 | All( | ||
726 | [ | ||
727 | KeyValue { | ||
728 | key: "feature", | ||
729 | value: "foo", | ||
730 | }, | ||
731 | KeyValue { | ||
732 | key: "feature", | ||
733 | value: "bar", | ||
734 | }, | ||
735 | ], | ||
736 | ), | ||
737 | ], | ||
414 | }, | 738 | }, |
415 | ] | 739 | ] |
416 | "### | 740 | "### |
diff --git a/crates/ra_ide/src/snapshots/highlight_injection.html b/crates/ra_ide/src/snapshots/highlight_injection.html index ea026d7a0..fcdc98201 100644 --- a/crates/ra_ide/src/snapshots/highlight_injection.html +++ b/crates/ra_ide/src/snapshots/highlight_injection.html | |||
@@ -10,6 +10,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd | |||
10 | .string_literal { color: #CC9393; } | 10 | .string_literal { color: #CC9393; } |
11 | .field { color: #94BFF3; } | 11 | .field { color: #94BFF3; } |
12 | .function { color: #93E0E3; } | 12 | .function { color: #93E0E3; } |
13 | .operator.unsafe { color: #E28C14; } | ||
13 | .parameter { color: #94BFF3; } | 14 | .parameter { color: #94BFF3; } |
14 | .text { color: #DCDCCC; } | 15 | .text { color: #DCDCCC; } |
15 | .type { color: #7CB8BB; } | 16 | .type { color: #7CB8BB; } |
@@ -17,6 +18,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd | |||
17 | .type_param { color: #DFAF8F; } | 18 | .type_param { color: #DFAF8F; } |
18 | .attribute { color: #94BFF3; } | 19 | .attribute { color: #94BFF3; } |
19 | .numeric_literal { color: #BFEBBF; } | 20 | .numeric_literal { color: #BFEBBF; } |
21 | .bool_literal { color: #BFE6EB; } | ||
20 | .macro { color: #94BFF3; } | 22 | .macro { color: #94BFF3; } |
21 | .module { color: #AFD8AF; } | 23 | .module { color: #AFD8AF; } |
22 | .variable { color: #DCDCCC; } | 24 | .variable { color: #DCDCCC; } |
diff --git a/crates/ra_ide/src/snapshots/highlight_strings.html b/crates/ra_ide/src/snapshots/highlight_strings.html index 752b487e8..e97192b61 100644 --- a/crates/ra_ide/src/snapshots/highlight_strings.html +++ b/crates/ra_ide/src/snapshots/highlight_strings.html | |||
@@ -10,6 +10,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd | |||
10 | .string_literal { color: #CC9393; } | 10 | .string_literal { color: #CC9393; } |
11 | .field { color: #94BFF3; } | 11 | .field { color: #94BFF3; } |
12 | .function { color: #93E0E3; } | 12 | .function { color: #93E0E3; } |
13 | .operator.unsafe { color: #E28C14; } | ||
13 | .parameter { color: #94BFF3; } | 14 | .parameter { color: #94BFF3; } |
14 | .text { color: #DCDCCC; } | 15 | .text { color: #DCDCCC; } |
15 | .type { color: #7CB8BB; } | 16 | .type { color: #7CB8BB; } |
@@ -17,6 +18,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd | |||
17 | .type_param { color: #DFAF8F; } | 18 | .type_param { color: #DFAF8F; } |
18 | .attribute { color: #94BFF3; } | 19 | .attribute { color: #94BFF3; } |
19 | .numeric_literal { color: #BFEBBF; } | 20 | .numeric_literal { color: #BFEBBF; } |
21 | .bool_literal { color: #BFE6EB; } | ||
20 | .macro { color: #94BFF3; } | 22 | .macro { color: #94BFF3; } |
21 | .module { color: #AFD8AF; } | 23 | .module { color: #AFD8AF; } |
22 | .variable { color: #DCDCCC; } | 24 | .variable { color: #DCDCCC; } |
@@ -51,6 +53,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd | |||
51 | <span class="macro">println!</span>(<span class="string_literal">"</span><span class="format_specifier">{</span><span class="variable">argument</span><span class="format_specifier">}</span><span class="string_literal">"</span>, argument = <span class="string_literal">"test"</span>); <span class="comment">// => "test"</span> | 53 | <span class="macro">println!</span>(<span class="string_literal">"</span><span class="format_specifier">{</span><span class="variable">argument</span><span class="format_specifier">}</span><span class="string_literal">"</span>, argument = <span class="string_literal">"test"</span>); <span class="comment">// => "test"</span> |
52 | <span class="macro">println!</span>(<span class="string_literal">"</span><span class="format_specifier">{</span><span class="variable">name</span><span class="format_specifier">}</span><span class="string_literal"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal">"</span>, <span class="numeric_literal">1</span>, name = <span class="numeric_literal">2</span>); <span class="comment">// => "2 1"</span> | 54 | <span class="macro">println!</span>(<span class="string_literal">"</span><span class="format_specifier">{</span><span class="variable">name</span><span class="format_specifier">}</span><span class="string_literal"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal">"</span>, <span class="numeric_literal">1</span>, name = <span class="numeric_literal">2</span>); <span class="comment">// => "2 1"</span> |
53 | <span class="macro">println!</span>(<span class="string_literal">"</span><span class="format_specifier">{</span><span class="variable">a</span><span class="format_specifier">}</span><span class="string_literal"> </span><span class="format_specifier">{</span><span class="variable">c</span><span class="format_specifier">}</span><span class="string_literal"> </span><span class="format_specifier">{</span><span class="variable">b</span><span class="format_specifier">}</span><span class="string_literal">"</span>, a=<span class="string_literal">"a"</span>, b=<span class="char_literal">'b'</span>, c=<span class="numeric_literal">3</span>); <span class="comment">// => "a 3 b"</span> | 55 | <span class="macro">println!</span>(<span class="string_literal">"</span><span class="format_specifier">{</span><span class="variable">a</span><span class="format_specifier">}</span><span class="string_literal"> </span><span class="format_specifier">{</span><span class="variable">c</span><span class="format_specifier">}</span><span class="string_literal"> </span><span class="format_specifier">{</span><span class="variable">b</span><span class="format_specifier">}</span><span class="string_literal">"</span>, a=<span class="string_literal">"a"</span>, b=<span class="char_literal">'b'</span>, c=<span class="numeric_literal">3</span>); <span class="comment">// => "a 3 b"</span> |
56 | <span class="macro">println!</span>(<span class="string_literal">"{{</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal">}}"</span>, <span class="numeric_literal">2</span>); <span class="comment">// => "{2}"</span> | ||
54 | <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal">!"</span>, <span class="string_literal">"x"</span>); | 57 | <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal">!"</span>, <span class="string_literal">"x"</span>); |
55 | <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="numeric_literal">1</span><span class="format_specifier">$</span><span class="format_specifier">}</span><span class="string_literal">!"</span>, <span class="string_literal">"x"</span>, <span class="numeric_literal">5</span>); | 58 | <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="numeric_literal">1</span><span class="format_specifier">$</span><span class="format_specifier">}</span><span class="string_literal">!"</span>, <span class="string_literal">"x"</span>, <span class="numeric_literal">5</span>); |
56 | <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="numeric_literal">1</span><span class="format_specifier">:</span><span class="numeric_literal">0</span><span class="format_specifier">$</span><span class="format_specifier">}</span><span class="string_literal">!"</span>, <span class="numeric_literal">5</span>, <span class="string_literal">"x"</span>); | 59 | <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="numeric_literal">1</span><span class="format_specifier">:</span><span class="numeric_literal">0</span><span class="format_specifier">$</span><span class="format_specifier">}</span><span class="string_literal">!"</span>, <span class="numeric_literal">5</span>, <span class="string_literal">"x"</span>); |
diff --git a/crates/ra_ide/src/snapshots/highlight_unsafe.html b/crates/ra_ide/src/snapshots/highlight_unsafe.html new file mode 100644 index 000000000..17ffc727c --- /dev/null +++ b/crates/ra_ide/src/snapshots/highlight_unsafe.html | |||
@@ -0,0 +1,48 @@ | |||
1 | |||
2 | <style> | ||
3 | body { margin: 0; } | ||
4 | pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padding: 0.4em; } | ||
5 | |||
6 | .lifetime { color: #DFAF8F; font-style: italic; } | ||
7 | .comment { color: #7F9F7F; } | ||
8 | .struct, .enum { color: #7CB8BB; } | ||
9 | .enum_variant { color: #BDE0F3; } | ||
10 | .string_literal { color: #CC9393; } | ||
11 | .field { color: #94BFF3; } | ||
12 | .function { color: #93E0E3; } | ||
13 | .operator.unsafe { color: #E28C14; } | ||
14 | .parameter { color: #94BFF3; } | ||
15 | .text { color: #DCDCCC; } | ||
16 | .type { color: #7CB8BB; } | ||
17 | .builtin_type { color: #8CD0D3; } | ||
18 | .type_param { color: #DFAF8F; } | ||
19 | .attribute { color: #94BFF3; } | ||
20 | .numeric_literal { color: #BFEBBF; } | ||
21 | .bool_literal { color: #BFE6EB; } | ||
22 | .macro { color: #94BFF3; } | ||
23 | .module { color: #AFD8AF; } | ||
24 | .variable { color: #DCDCCC; } | ||
25 | .format_specifier { color: #CC696B; } | ||
26 | .mutable { text-decoration: underline; } | ||
27 | |||
28 | .keyword { color: #F0DFAF; font-weight: bold; } | ||
29 | .keyword.unsafe { color: #BC8383; font-weight: bold; } | ||
30 | .control { font-style: italic; } | ||
31 | </style> | ||
32 | <pre><code><span class="keyword unsafe">unsafe</span> <span class="keyword">fn</span> <span class="function declaration unsafe">unsafe_fn</span>() {} | ||
33 | |||
34 | <span class="keyword">struct</span> <span class="struct declaration">HasUnsafeFn</span>; | ||
35 | |||
36 | <span class="keyword">impl</span> <span class="struct">HasUnsafeFn</span> { | ||
37 | <span class="keyword unsafe">unsafe</span> <span class="keyword">fn</span> <span class="function declaration unsafe">unsafe_method</span>(&<span class="self_keyword">self</span>) {} | ||
38 | } | ||
39 | |||
40 | <span class="keyword">fn</span> <span class="function declaration">main</span>() { | ||
41 | <span class="keyword">let</span> <span class="variable declaration">x</span> = &<span class="numeric_literal">5</span> <span class="keyword">as</span> *<span class="keyword">const</span> <span class="builtin_type">usize</span>; | ||
42 | <span class="keyword unsafe">unsafe</span> { | ||
43 | <span class="function unsafe">unsafe_fn</span>(); | ||
44 | <span class="struct">HasUnsafeFn</span>.<span class="function unsafe">unsafe_method</span>(); | ||
45 | <span class="keyword">let</span> <span class="variable declaration">y</span> = <span class="operator unsafe">*</span><span class="variable">x</span>; | ||
46 | <span class="keyword">let</span> <span class="variable declaration">z</span> = -<span class="variable">x</span>; | ||
47 | } | ||
48 | }</code></pre> \ No newline at end of file | ||
diff --git a/crates/ra_ide/src/snapshots/highlighting.html b/crates/ra_ide/src/snapshots/highlighting.html index 635fe5cf9..42c5f3e55 100644 --- a/crates/ra_ide/src/snapshots/highlighting.html +++ b/crates/ra_ide/src/snapshots/highlighting.html | |||
@@ -10,6 +10,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd | |||
10 | .string_literal { color: #CC9393; } | 10 | .string_literal { color: #CC9393; } |
11 | .field { color: #94BFF3; } | 11 | .field { color: #94BFF3; } |
12 | .function { color: #93E0E3; } | 12 | .function { color: #93E0E3; } |
13 | .operator.unsafe { color: #E28C14; } | ||
13 | .parameter { color: #94BFF3; } | 14 | .parameter { color: #94BFF3; } |
14 | .text { color: #DCDCCC; } | 15 | .text { color: #DCDCCC; } |
15 | .type { color: #7CB8BB; } | 16 | .type { color: #7CB8BB; } |
@@ -17,6 +18,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd | |||
17 | .type_param { color: #DFAF8F; } | 18 | .type_param { color: #DFAF8F; } |
18 | .attribute { color: #94BFF3; } | 19 | .attribute { color: #94BFF3; } |
19 | .numeric_literal { color: #BFEBBF; } | 20 | .numeric_literal { color: #BFEBBF; } |
21 | .bool_literal { color: #BFE6EB; } | ||
20 | .macro { color: #94BFF3; } | 22 | .macro { color: #94BFF3; } |
21 | .module { color: #AFD8AF; } | 23 | .module { color: #AFD8AF; } |
22 | .variable { color: #DCDCCC; } | 24 | .variable { color: #DCDCCC; } |
@@ -27,19 +29,19 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd | |||
27 | .keyword.unsafe { color: #BC8383; font-weight: bold; } | 29 | .keyword.unsafe { color: #BC8383; font-weight: bold; } |
28 | .control { font-style: italic; } | 30 | .control { font-style: italic; } |
29 | </style> | 31 | </style> |
30 | <pre><code><span class="attribute">#[derive(Clone, Debug)]</span> | 32 | <pre><code><span class="attribute">#[</span><span class="function attribute">derive</span><span class="attribute">(Clone, Debug)]</span> |
31 | <span class="keyword">struct</span> <span class="struct declaration">Foo</span> { | 33 | <span class="keyword">struct</span> <span class="struct declaration">Foo</span> { |
32 | <span class="keyword">pub</span> <span class="field declaration">x</span>: <span class="builtin_type">i32</span>, | 34 | <span class="keyword">pub</span> <span class="field declaration">x</span>: <span class="builtin_type">i32</span>, |
33 | <span class="keyword">pub</span> <span class="field declaration">y</span>: <span class="builtin_type">i32</span>, | 35 | <span class="keyword">pub</span> <span class="field declaration">y</span>: <span class="builtin_type">i32</span>, |
34 | } | 36 | } |
35 | 37 | ||
36 | <span class="keyword">trait</span> <span class="trait declaration">Bar</span> { | 38 | <span class="keyword">trait</span> <span class="trait declaration">Bar</span> { |
37 | <span class="keyword">fn</span> <span class="function declaration">bar</span>(&<span class="keyword">self</span>) -> <span class="builtin_type">i32</span>; | 39 | <span class="keyword">fn</span> <span class="function declaration">bar</span>(&<span class="self_keyword">self</span>) -> <span class="builtin_type">i32</span>; |
38 | } | 40 | } |
39 | 41 | ||
40 | <span class="keyword">impl</span> <span class="trait">Bar</span> <span class="keyword">for</span> <span class="struct">Foo</span> { | 42 | <span class="keyword">impl</span> <span class="trait">Bar</span> <span class="keyword">for</span> <span class="struct">Foo</span> { |
41 | <span class="keyword">fn</span> <span class="function declaration">bar</span>(&<span class="keyword">self</span>) -> <span class="builtin_type">i32</span> { | 43 | <span class="keyword">fn</span> <span class="function declaration">bar</span>(&<span class="self_keyword">self</span>) -> <span class="builtin_type">i32</span> { |
42 | <span class="keyword">self</span>.<span class="field">x</span> | 44 | <span class="self_keyword">self</span>.<span class="field">x</span> |
43 | } | 45 | } |
44 | } | 46 | } |
45 | 47 | ||
@@ -64,7 +66,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd | |||
64 | <span class="macro">println!</span>(<span class="string_literal">"Hello, {}!"</span>, <span class="numeric_literal">92</span>); | 66 | <span class="macro">println!</span>(<span class="string_literal">"Hello, {}!"</span>, <span class="numeric_literal">92</span>); |
65 | 67 | ||
66 | <span class="keyword">let</span> <span class="keyword">mut</span> <span class="variable declaration mutable">vec</span> = <span class="unresolved_reference">Vec</span>::<span class="unresolved_reference">new</span>(); | 68 | <span class="keyword">let</span> <span class="keyword">mut</span> <span class="variable declaration mutable">vec</span> = <span class="unresolved_reference">Vec</span>::<span class="unresolved_reference">new</span>(); |
67 | <span class="keyword control">if</span> <span class="keyword">true</span> { | 69 | <span class="keyword control">if</span> <span class="bool_literal">true</span> { |
68 | <span class="keyword">let</span> <span class="variable declaration">x</span> = <span class="numeric_literal">92</span>; | 70 | <span class="keyword">let</span> <span class="variable declaration">x</span> = <span class="numeric_literal">92</span>; |
69 | <span class="variable mutable">vec</span>.<span class="unresolved_reference">push</span>(<span class="struct">Foo</span> { <span class="field">x</span>, <span class="field">y</span>: <span class="numeric_literal">1</span> }); | 71 | <span class="variable mutable">vec</span>.<span class="unresolved_reference">push</span>(<span class="struct">Foo</span> { <span class="field">x</span>, <span class="field">y</span>: <span class="numeric_literal">1</span> }); |
70 | } | 72 | } |
@@ -91,7 +93,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd | |||
91 | <span class="keyword">use</span> <span class="enum">Option</span>::*; | 93 | <span class="keyword">use</span> <span class="enum">Option</span>::*; |
92 | 94 | ||
93 | <span class="keyword">impl</span><<span class="type_param declaration">T</span>> <span class="enum">Option</span><<span class="type_param">T</span>> { | 95 | <span class="keyword">impl</span><<span class="type_param declaration">T</span>> <span class="enum">Option</span><<span class="type_param">T</span>> { |
94 | <span class="keyword">fn</span> <span class="function declaration">and</span><<span class="type_param declaration">U</span>>(<span class="keyword">self</span>, <span class="variable declaration">other</span>: <span class="enum">Option</span><<span class="type_param">U</span>>) -> <span class="enum">Option</span><(<span class="type_param">T</span>, <span class="type_param">U</span>)> { | 96 | <span class="keyword">fn</span> <span class="function declaration">and</span><<span class="type_param declaration">U</span>>(<span class="self_keyword">self</span>, <span class="variable declaration">other</span>: <span class="enum">Option</span><<span class="type_param">U</span>>) -> <span class="enum">Option</span><(<span class="type_param">T</span>, <span class="type_param">U</span>)> { |
95 | <span class="keyword control">match</span> <span class="variable">other</span> { | 97 | <span class="keyword control">match</span> <span class="variable">other</span> { |
96 | <span class="enum_variant">None</span> => <span class="macro">unimplemented!</span>(), | 98 | <span class="enum_variant">None</span> => <span class="macro">unimplemented!</span>(), |
97 | <span class="variable declaration">Nope</span> => <span class="variable">Nope</span>, | 99 | <span class="variable declaration">Nope</span> => <span class="variable">Nope</span>, |
diff --git a/crates/ra_ide/src/snapshots/rainbow_highlighting.html b/crates/ra_ide/src/snapshots/rainbow_highlighting.html index 11e1f3e44..2dd61d20d 100644 --- a/crates/ra_ide/src/snapshots/rainbow_highlighting.html +++ b/crates/ra_ide/src/snapshots/rainbow_highlighting.html | |||
@@ -10,6 +10,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd | |||
10 | .string_literal { color: #CC9393; } | 10 | .string_literal { color: #CC9393; } |
11 | .field { color: #94BFF3; } | 11 | .field { color: #94BFF3; } |
12 | .function { color: #93E0E3; } | 12 | .function { color: #93E0E3; } |
13 | .operator.unsafe { color: #E28C14; } | ||
13 | .parameter { color: #94BFF3; } | 14 | .parameter { color: #94BFF3; } |
14 | .text { color: #DCDCCC; } | 15 | .text { color: #DCDCCC; } |
15 | .type { color: #7CB8BB; } | 16 | .type { color: #7CB8BB; } |
@@ -17,6 +18,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd | |||
17 | .type_param { color: #DFAF8F; } | 18 | .type_param { color: #DFAF8F; } |
18 | .attribute { color: #94BFF3; } | 19 | .attribute { color: #94BFF3; } |
19 | .numeric_literal { color: #BFEBBF; } | 20 | .numeric_literal { color: #BFEBBF; } |
21 | .bool_literal { color: #BFE6EB; } | ||
20 | .macro { color: #94BFF3; } | 22 | .macro { color: #94BFF3; } |
21 | .module { color: #AFD8AF; } | 23 | .module { color: #AFD8AF; } |
22 | .variable { color: #DCDCCC; } | 24 | .variable { color: #DCDCCC; } |
diff --git a/crates/ra_ide/src/ssr.rs b/crates/ra_ide/src/ssr.rs index 1873d1d0d..93e9aee1d 100644 --- a/crates/ra_ide/src/ssr.rs +++ b/crates/ra_ide/src/ssr.rs | |||
@@ -1,5 +1,3 @@ | |||
1 | //! structural search replace | ||
2 | |||
3 | use std::{collections::HashMap, iter::once, str::FromStr}; | 1 | use std::{collections::HashMap, iter::once, str::FromStr}; |
4 | 2 | ||
5 | use ra_db::{SourceDatabase, SourceDatabaseExt}; | 3 | use ra_db::{SourceDatabase, SourceDatabaseExt}; |
@@ -25,6 +23,28 @@ impl std::fmt::Display for SsrError { | |||
25 | 23 | ||
26 | impl std::error::Error for SsrError {} | 24 | impl std::error::Error for SsrError {} |
27 | 25 | ||
26 | // Feature: Structural Seach and Replace | ||
27 | // | ||
28 | // Search and replace with named wildcards that will match any expression. | ||
29 | // The syntax for a structural search replace command is `<search_pattern> ==>> <replace_pattern>`. | ||
30 | // A `$<name>:expr` placeholder in the search pattern will match any expression and `$<name>` will reference it in the replacement. | ||
31 | // Available via the command `rust-analyzer.ssr`. | ||
32 | // | ||
33 | // ```rust | ||
34 | // // Using structural search replace command [foo($a:expr, $b:expr) ==>> ($a).foo($b)] | ||
35 | // | ||
36 | // // BEFORE | ||
37 | // String::from(foo(y + 5, z)) | ||
38 | // | ||
39 | // // AFTER | ||
40 | // String::from((y + 5).foo(z)) | ||
41 | // ``` | ||
42 | // | ||
43 | // |=== | ||
44 | // | Editor | Action Name | ||
45 | // | ||
46 | // | VS Code | **Rust Analyzer: Structural Search Replace** | ||
47 | // |=== | ||
28 | pub fn parse_search_replace( | 48 | pub fn parse_search_replace( |
29 | query: &str, | 49 | query: &str, |
30 | parse_only: bool, | 50 | parse_only: bool, |
@@ -196,10 +216,10 @@ fn find(pattern: &SsrPattern, code: &SyntaxNode) -> SsrMatches { | |||
196 | ) -> Option<Match> { | 216 | ) -> Option<Match> { |
197 | let match_ = check_opt_nodes(pattern.path(), code.path(), placeholders, match_)?; | 217 | let match_ = check_opt_nodes(pattern.path(), code.path(), placeholders, match_)?; |
198 | 218 | ||
199 | let mut pattern_fields = | 219 | let mut pattern_fields: Vec<RecordField> = |
200 | pattern.record_field_list().map(|x| x.fields().collect()).unwrap_or(vec![]); | 220 | pattern.record_field_list().map(|x| x.fields().collect()).unwrap_or_default(); |
201 | let mut code_fields = | 221 | let mut code_fields: Vec<RecordField> = |
202 | code.record_field_list().map(|x| x.fields().collect()).unwrap_or(vec![]); | 222 | code.record_field_list().map(|x| x.fields().collect()).unwrap_or_default(); |
203 | 223 | ||
204 | if pattern_fields.len() != code_fields.len() { | 224 | if pattern_fields.len() != code_fields.len() { |
205 | return None; | 225 | return None; |
diff --git a/crates/ra_ide/src/status.rs b/crates/ra_ide/src/status.rs index 30eb5c995..5b7992920 100644 --- a/crates/ra_ide/src/status.rs +++ b/crates/ra_ide/src/status.rs | |||
@@ -1,5 +1,3 @@ | |||
1 | //! FIXME: write short doc here | ||
2 | |||
3 | use std::{fmt, iter::FromIterator, sync::Arc}; | 1 | use std::{fmt, iter::FromIterator, sync::Arc}; |
4 | 2 | ||
5 | use hir::MacroFile; | 3 | use hir::MacroFile; |
@@ -26,6 +24,15 @@ fn macro_syntax_tree_stats(db: &RootDatabase) -> SyntaxTreeStats { | |||
26 | db.query(hir::db::ParseMacroQuery).entries::<SyntaxTreeStats>() | 24 | db.query(hir::db::ParseMacroQuery).entries::<SyntaxTreeStats>() |
27 | } | 25 | } |
28 | 26 | ||
27 | // Feature: Status | ||
28 | // | ||
29 | // Shows internal statistic about memory usage of rust-analyzer. | ||
30 | // | ||
31 | // |=== | ||
32 | // | Editor | Action Name | ||
33 | // | ||
34 | // | VS Code | **Rust Analyzer: Status** | ||
35 | // |=== | ||
29 | pub(crate) fn status(db: &RootDatabase) -> String { | 36 | pub(crate) fn status(db: &RootDatabase) -> String { |
30 | let files_stats = db.query(FileTextQuery).entries::<FilesStats>(); | 37 | let files_stats = db.query(FileTextQuery).entries::<FilesStats>(); |
31 | let syntax_tree_stats = syntax_tree_stats(db); | 38 | let syntax_tree_stats = syntax_tree_stats(db); |
diff --git a/crates/ra_ide/src/syntax_highlighting.rs b/crates/ra_ide/src/syntax_highlighting.rs index be57eeb0a..19ecd54d6 100644 --- a/crates/ra_ide/src/syntax_highlighting.rs +++ b/crates/ra_ide/src/syntax_highlighting.rs | |||
@@ -1,5 +1,3 @@ | |||
1 | //! Implements syntax highlighting. | ||
2 | |||
3 | mod tags; | 1 | mod tags; |
4 | mod html; | 2 | mod html; |
5 | #[cfg(test)] | 3 | #[cfg(test)] |
@@ -32,81 +30,15 @@ pub struct HighlightedRange { | |||
32 | pub binding_hash: Option<u64>, | 30 | pub binding_hash: Option<u64>, |
33 | } | 31 | } |
34 | 32 | ||
35 | #[derive(Debug)] | 33 | // Feature: Semantic Syntax Highlighting |
36 | struct HighlightedRangeStack { | 34 | // |
37 | stack: Vec<Vec<HighlightedRange>>, | 35 | // rust-analyzer highlights the code semantically. |
38 | } | 36 | // For example, `bar` in `foo::Bar` might be colored differently depending on whether `Bar` is an enum or a trait. |
39 | 37 | // rust-analyzer does not specify colors directly, instead it assigns tag (like `struct`) and a set of modifiers (like `declaration`) to each token. | |
40 | /// We use a stack to implement the flattening logic for the highlighted | 38 | // It's up to the client to map those to specific colors. |
41 | /// syntax ranges. | 39 | // |
42 | impl HighlightedRangeStack { | 40 | // The general rule is that a reference to an entity gets colored the same way as the entity itself. |
43 | fn new() -> Self { | 41 | // We also give special modifier for `mut` and `&mut` local variables. |
44 | Self { stack: vec![Vec::new()] } | ||
45 | } | ||
46 | |||
47 | fn push(&mut self) { | ||
48 | self.stack.push(Vec::new()); | ||
49 | } | ||
50 | |||
51 | /// Flattens the highlighted ranges. | ||
52 | /// | ||
53 | /// For example `#[cfg(feature = "foo")]` contains the nested ranges: | ||
54 | /// 1) parent-range: Attribute [0, 23) | ||
55 | /// 2) child-range: String [16, 21) | ||
56 | /// | ||
57 | /// The following code implements the flattening, for our example this results to: | ||
58 | /// `[Attribute [0, 16), String [16, 21), Attribute [21, 23)]` | ||
59 | fn pop(&mut self) { | ||
60 | let children = self.stack.pop().unwrap(); | ||
61 | let prev = self.stack.last_mut().unwrap(); | ||
62 | let needs_flattening = !children.is_empty() | ||
63 | && !prev.is_empty() | ||
64 | && prev.last().unwrap().range.contains_range(children.first().unwrap().range); | ||
65 | if !needs_flattening { | ||
66 | prev.extend(children); | ||
67 | } else { | ||
68 | let mut parent = prev.pop().unwrap(); | ||
69 | for ele in children { | ||
70 | assert!(parent.range.contains_range(ele.range)); | ||
71 | let mut cloned = parent.clone(); | ||
72 | parent.range = TextRange::new(parent.range.start(), ele.range.start()); | ||
73 | cloned.range = TextRange::new(ele.range.end(), cloned.range.end()); | ||
74 | if !parent.range.is_empty() { | ||
75 | prev.push(parent); | ||
76 | } | ||
77 | prev.push(ele); | ||
78 | parent = cloned; | ||
79 | } | ||
80 | if !parent.range.is_empty() { | ||
81 | prev.push(parent); | ||
82 | } | ||
83 | } | ||
84 | } | ||
85 | |||
86 | fn add(&mut self, range: HighlightedRange) { | ||
87 | self.stack | ||
88 | .last_mut() | ||
89 | .expect("during DFS traversal, the stack must not be empty") | ||
90 | .push(range) | ||
91 | } | ||
92 | |||
93 | fn flattened(mut self) -> Vec<HighlightedRange> { | ||
94 | assert_eq!( | ||
95 | self.stack.len(), | ||
96 | 1, | ||
97 | "after DFS traversal, the stack should only contain a single element" | ||
98 | ); | ||
99 | let mut res = self.stack.pop().unwrap(); | ||
100 | res.sort_by_key(|range| range.range.start()); | ||
101 | // Check that ranges are sorted and disjoint | ||
102 | assert!(res | ||
103 | .iter() | ||
104 | .zip(res.iter().skip(1)) | ||
105 | .all(|(left, right)| left.range.end() <= right.range.start())); | ||
106 | res | ||
107 | } | ||
108 | } | ||
109 | |||
110 | pub(crate) fn highlight( | 42 | pub(crate) fn highlight( |
111 | db: &RootDatabase, | 43 | db: &RootDatabase, |
112 | file_id: FileId, | 44 | file_id: FileId, |
@@ -291,6 +223,81 @@ pub(crate) fn highlight( | |||
291 | stack.flattened() | 223 | stack.flattened() |
292 | } | 224 | } |
293 | 225 | ||
226 | #[derive(Debug)] | ||
227 | struct HighlightedRangeStack { | ||
228 | stack: Vec<Vec<HighlightedRange>>, | ||
229 | } | ||
230 | |||
231 | /// We use a stack to implement the flattening logic for the highlighted | ||
232 | /// syntax ranges. | ||
233 | impl HighlightedRangeStack { | ||
234 | fn new() -> Self { | ||
235 | Self { stack: vec![Vec::new()] } | ||
236 | } | ||
237 | |||
238 | fn push(&mut self) { | ||
239 | self.stack.push(Vec::new()); | ||
240 | } | ||
241 | |||
242 | /// Flattens the highlighted ranges. | ||
243 | /// | ||
244 | /// For example `#[cfg(feature = "foo")]` contains the nested ranges: | ||
245 | /// 1) parent-range: Attribute [0, 23) | ||
246 | /// 2) child-range: String [16, 21) | ||
247 | /// | ||
248 | /// The following code implements the flattening, for our example this results to: | ||
249 | /// `[Attribute [0, 16), String [16, 21), Attribute [21, 23)]` | ||
250 | fn pop(&mut self) { | ||
251 | let children = self.stack.pop().unwrap(); | ||
252 | let prev = self.stack.last_mut().unwrap(); | ||
253 | let needs_flattening = !children.is_empty() | ||
254 | && !prev.is_empty() | ||
255 | && prev.last().unwrap().range.contains_range(children.first().unwrap().range); | ||
256 | if !needs_flattening { | ||
257 | prev.extend(children); | ||
258 | } else { | ||
259 | let mut parent = prev.pop().unwrap(); | ||
260 | for ele in children { | ||
261 | assert!(parent.range.contains_range(ele.range)); | ||
262 | let mut cloned = parent.clone(); | ||
263 | parent.range = TextRange::new(parent.range.start(), ele.range.start()); | ||
264 | cloned.range = TextRange::new(ele.range.end(), cloned.range.end()); | ||
265 | if !parent.range.is_empty() { | ||
266 | prev.push(parent); | ||
267 | } | ||
268 | prev.push(ele); | ||
269 | parent = cloned; | ||
270 | } | ||
271 | if !parent.range.is_empty() { | ||
272 | prev.push(parent); | ||
273 | } | ||
274 | } | ||
275 | } | ||
276 | |||
277 | fn add(&mut self, range: HighlightedRange) { | ||
278 | self.stack | ||
279 | .last_mut() | ||
280 | .expect("during DFS traversal, the stack must not be empty") | ||
281 | .push(range) | ||
282 | } | ||
283 | |||
284 | fn flattened(mut self) -> Vec<HighlightedRange> { | ||
285 | assert_eq!( | ||
286 | self.stack.len(), | ||
287 | 1, | ||
288 | "after DFS traversal, the stack should only contain a single element" | ||
289 | ); | ||
290 | let mut res = self.stack.pop().unwrap(); | ||
291 | res.sort_by_key(|range| range.range.start()); | ||
292 | // Check that ranges are sorted and disjoint | ||
293 | assert!(res | ||
294 | .iter() | ||
295 | .zip(res.iter().skip(1)) | ||
296 | .all(|(left, right)| left.range.end() <= right.range.start())); | ||
297 | res | ||
298 | } | ||
299 | } | ||
300 | |||
294 | fn highlight_format_specifier(kind: FormatSpecifier) -> Option<HighlightTag> { | 301 | fn highlight_format_specifier(kind: FormatSpecifier) -> Option<HighlightTag> { |
295 | Some(match kind { | 302 | Some(match kind { |
296 | FormatSpecifier::Open | 303 | FormatSpecifier::Open |
@@ -361,7 +368,9 @@ fn highlight_element( | |||
361 | } | 368 | } |
362 | 369 | ||
363 | // Highlight references like the definitions they resolve to | 370 | // Highlight references like the definitions they resolve to |
364 | NAME_REF if element.ancestors().any(|it| it.kind() == ATTR) => return None, | 371 | NAME_REF if element.ancestors().any(|it| it.kind() == ATTR) => { |
372 | Highlight::from(HighlightTag::Function) | HighlightModifier::Attribute | ||
373 | } | ||
365 | NAME_REF => { | 374 | NAME_REF => { |
366 | let name_ref = element.into_node().and_then(ast::NameRef::cast).unwrap(); | 375 | let name_ref = element.into_node().and_then(ast::NameRef::cast).unwrap(); |
367 | match classify_name_ref(sema, &name_ref) { | 376 | match classify_name_ref(sema, &name_ref) { |
@@ -389,6 +398,7 @@ fn highlight_element( | |||
389 | INT_NUMBER | FLOAT_NUMBER => HighlightTag::NumericLiteral.into(), | 398 | INT_NUMBER | FLOAT_NUMBER => HighlightTag::NumericLiteral.into(), |
390 | BYTE => HighlightTag::ByteLiteral.into(), | 399 | BYTE => HighlightTag::ByteLiteral.into(), |
391 | CHAR => HighlightTag::CharLiteral.into(), | 400 | CHAR => HighlightTag::CharLiteral.into(), |
401 | QUESTION => Highlight::new(HighlightTag::Operator) | HighlightModifier::ControlFlow, | ||
392 | LIFETIME => { | 402 | LIFETIME => { |
393 | let h = Highlight::new(HighlightTag::Lifetime); | 403 | let h = Highlight::new(HighlightTag::Lifetime); |
394 | match element.parent().map(|it| it.kind()) { | 404 | match element.parent().map(|it| it.kind()) { |
@@ -396,6 +406,23 @@ fn highlight_element( | |||
396 | _ => h, | 406 | _ => h, |
397 | } | 407 | } |
398 | } | 408 | } |
409 | PREFIX_EXPR => { | ||
410 | let prefix_expr = element.into_node().and_then(ast::PrefixExpr::cast)?; | ||
411 | match prefix_expr.op_kind() { | ||
412 | Some(ast::PrefixOp::Deref) => {} | ||
413 | _ => return None, | ||
414 | } | ||
415 | |||
416 | let expr = prefix_expr.expr()?; | ||
417 | let ty = sema.type_of_expr(&expr)?; | ||
418 | if !ty.is_raw_ptr() { | ||
419 | return None; | ||
420 | } | ||
421 | |||
422 | let mut h = Highlight::new(HighlightTag::Operator); | ||
423 | h |= HighlightModifier::Unsafe; | ||
424 | h | ||
425 | } | ||
399 | 426 | ||
400 | k if k.is_keyword() => { | 427 | k if k.is_keyword() => { |
401 | let h = Highlight::new(HighlightTag::Keyword); | 428 | let h = Highlight::new(HighlightTag::Keyword); |
@@ -411,6 +438,8 @@ fn highlight_element( | |||
411 | | T![in] => h | HighlightModifier::ControlFlow, | 438 | | T![in] => h | HighlightModifier::ControlFlow, |
412 | T![for] if !is_child_of_impl(element) => h | HighlightModifier::ControlFlow, | 439 | T![for] if !is_child_of_impl(element) => h | HighlightModifier::ControlFlow, |
413 | T![unsafe] => h | HighlightModifier::Unsafe, | 440 | T![unsafe] => h | HighlightModifier::Unsafe, |
441 | T![true] | T![false] => HighlightTag::BoolLiteral.into(), | ||
442 | T![self] => HighlightTag::SelfKeyword.into(), | ||
414 | _ => h, | 443 | _ => h, |
415 | } | 444 | } |
416 | } | 445 | } |
@@ -446,7 +475,13 @@ fn highlight_name(db: &RootDatabase, def: Definition) -> Highlight { | |||
446 | Definition::Field(_) => HighlightTag::Field, | 475 | Definition::Field(_) => HighlightTag::Field, |
447 | Definition::ModuleDef(def) => match def { | 476 | Definition::ModuleDef(def) => match def { |
448 | hir::ModuleDef::Module(_) => HighlightTag::Module, | 477 | hir::ModuleDef::Module(_) => HighlightTag::Module, |
449 | hir::ModuleDef::Function(_) => HighlightTag::Function, | 478 | hir::ModuleDef::Function(func) => { |
479 | let mut h = HighlightTag::Function.into(); | ||
480 | if func.is_unsafe(db) { | ||
481 | h |= HighlightModifier::Unsafe; | ||
482 | } | ||
483 | return h; | ||
484 | } | ||
450 | hir::ModuleDef::Adt(hir::Adt::Struct(_)) => HighlightTag::Struct, | 485 | hir::ModuleDef::Adt(hir::Adt::Struct(_)) => HighlightTag::Struct, |
451 | hir::ModuleDef::Adt(hir::Adt::Enum(_)) => HighlightTag::Enum, | 486 | hir::ModuleDef::Adt(hir::Adt::Enum(_)) => HighlightTag::Enum, |
452 | hir::ModuleDef::Adt(hir::Adt::Union(_)) => HighlightTag::Union, | 487 | hir::ModuleDef::Adt(hir::Adt::Union(_)) => HighlightTag::Union, |
@@ -478,23 +513,31 @@ fn highlight_name(db: &RootDatabase, def: Definition) -> Highlight { | |||
478 | } | 513 | } |
479 | 514 | ||
480 | fn highlight_name_by_syntax(name: ast::Name) -> Highlight { | 515 | fn highlight_name_by_syntax(name: ast::Name) -> Highlight { |
481 | let default = HighlightTag::Function.into(); | 516 | let default = HighlightTag::UnresolvedReference; |
482 | 517 | ||
483 | let parent = match name.syntax().parent() { | 518 | let parent = match name.syntax().parent() { |
484 | Some(it) => it, | 519 | Some(it) => it, |
485 | _ => return default, | 520 | _ => return default.into(), |
486 | }; | 521 | }; |
487 | 522 | ||
488 | match parent.kind() { | 523 | let tag = match parent.kind() { |
489 | STRUCT_DEF => HighlightTag::Struct.into(), | 524 | STRUCT_DEF => HighlightTag::Struct, |
490 | ENUM_DEF => HighlightTag::Enum.into(), | 525 | ENUM_DEF => HighlightTag::Enum, |
491 | UNION_DEF => HighlightTag::Union.into(), | 526 | UNION_DEF => HighlightTag::Union, |
492 | TRAIT_DEF => HighlightTag::Trait.into(), | 527 | TRAIT_DEF => HighlightTag::Trait, |
493 | TYPE_ALIAS_DEF => HighlightTag::TypeAlias.into(), | 528 | TYPE_ALIAS_DEF => HighlightTag::TypeAlias, |
494 | TYPE_PARAM => HighlightTag::TypeParam.into(), | 529 | TYPE_PARAM => HighlightTag::TypeParam, |
495 | RECORD_FIELD_DEF => HighlightTag::Field.into(), | 530 | RECORD_FIELD_DEF => HighlightTag::Field, |
531 | MODULE => HighlightTag::Module, | ||
532 | FN_DEF => HighlightTag::Function, | ||
533 | CONST_DEF => HighlightTag::Constant, | ||
534 | STATIC_DEF => HighlightTag::Static, | ||
535 | ENUM_VARIANT => HighlightTag::EnumVariant, | ||
536 | BIND_PAT => HighlightTag::Local, | ||
496 | _ => default, | 537 | _ => default, |
497 | } | 538 | }; |
539 | |||
540 | tag.into() | ||
498 | } | 541 | } |
499 | 542 | ||
500 | fn highlight_injection( | 543 | fn highlight_injection( |
diff --git a/crates/ra_ide/src/syntax_highlighting/html.rs b/crates/ra_ide/src/syntax_highlighting/html.rs index ff0eeeb52..7d946c98d 100644 --- a/crates/ra_ide/src/syntax_highlighting/html.rs +++ b/crates/ra_ide/src/syntax_highlighting/html.rs | |||
@@ -69,6 +69,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd | |||
69 | .string_literal { color: #CC9393; } | 69 | .string_literal { color: #CC9393; } |
70 | .field { color: #94BFF3; } | 70 | .field { color: #94BFF3; } |
71 | .function { color: #93E0E3; } | 71 | .function { color: #93E0E3; } |
72 | .operator.unsafe { color: #E28C14; } | ||
72 | .parameter { color: #94BFF3; } | 73 | .parameter { color: #94BFF3; } |
73 | .text { color: #DCDCCC; } | 74 | .text { color: #DCDCCC; } |
74 | .type { color: #7CB8BB; } | 75 | .type { color: #7CB8BB; } |
@@ -76,6 +77,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd | |||
76 | .type_param { color: #DFAF8F; } | 77 | .type_param { color: #DFAF8F; } |
77 | .attribute { color: #94BFF3; } | 78 | .attribute { color: #94BFF3; } |
78 | .numeric_literal { color: #BFEBBF; } | 79 | .numeric_literal { color: #BFEBBF; } |
80 | .bool_literal { color: #BFE6EB; } | ||
79 | .macro { color: #94BFF3; } | 81 | .macro { color: #94BFF3; } |
80 | .module { color: #AFD8AF; } | 82 | .module { color: #AFD8AF; } |
81 | .variable { color: #DCDCCC; } | 83 | .variable { color: #DCDCCC; } |
diff --git a/crates/ra_ide/src/syntax_highlighting/tags.rs b/crates/ra_ide/src/syntax_highlighting/tags.rs index be1a0f12b..94f466966 100644 --- a/crates/ra_ide/src/syntax_highlighting/tags.rs +++ b/crates/ra_ide/src/syntax_highlighting/tags.rs | |||
@@ -15,6 +15,7 @@ pub struct HighlightModifiers(u32); | |||
15 | #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] | 15 | #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] |
16 | pub enum HighlightTag { | 16 | pub enum HighlightTag { |
17 | Attribute, | 17 | Attribute, |
18 | BoolLiteral, | ||
18 | BuiltinType, | 19 | BuiltinType, |
19 | ByteLiteral, | 20 | ByteLiteral, |
20 | CharLiteral, | 21 | CharLiteral, |
@@ -23,12 +24,15 @@ pub enum HighlightTag { | |||
23 | Enum, | 24 | Enum, |
24 | EnumVariant, | 25 | EnumVariant, |
25 | Field, | 26 | Field, |
27 | FormatSpecifier, | ||
26 | Function, | 28 | Function, |
27 | Keyword, | 29 | Keyword, |
28 | Lifetime, | 30 | Lifetime, |
29 | Macro, | 31 | Macro, |
30 | Module, | 32 | Module, |
31 | NumericLiteral, | 33 | NumericLiteral, |
34 | Operator, | ||
35 | SelfKeyword, | ||
32 | SelfType, | 36 | SelfType, |
33 | Static, | 37 | Static, |
34 | StringLiteral, | 38 | StringLiteral, |
@@ -39,14 +43,15 @@ pub enum HighlightTag { | |||
39 | Union, | 43 | Union, |
40 | Local, | 44 | Local, |
41 | UnresolvedReference, | 45 | UnresolvedReference, |
42 | FormatSpecifier, | ||
43 | } | 46 | } |
44 | 47 | ||
45 | #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] | 48 | #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] |
46 | #[repr(u8)] | 49 | #[repr(u8)] |
47 | pub enum HighlightModifier { | 50 | pub enum HighlightModifier { |
51 | /// Used to differentiate individual elements within attributes. | ||
52 | Attribute = 0, | ||
48 | /// Used with keywords like `if` and `break`. | 53 | /// Used with keywords like `if` and `break`. |
49 | ControlFlow = 0, | 54 | ControlFlow, |
50 | /// `foo` in `fn foo(x: i32)` is a definition, `foo` in `foo(90 + 2)` is | 55 | /// `foo` in `fn foo(x: i32)` is a definition, `foo` in `foo(90 + 2)` is |
51 | /// not. | 56 | /// not. |
52 | Definition, | 57 | Definition, |
@@ -58,6 +63,7 @@ impl HighlightTag { | |||
58 | fn as_str(self) -> &'static str { | 63 | fn as_str(self) -> &'static str { |
59 | match self { | 64 | match self { |
60 | HighlightTag::Attribute => "attribute", | 65 | HighlightTag::Attribute => "attribute", |
66 | HighlightTag::BoolLiteral => "bool_literal", | ||
61 | HighlightTag::BuiltinType => "builtin_type", | 67 | HighlightTag::BuiltinType => "builtin_type", |
62 | HighlightTag::ByteLiteral => "byte_literal", | 68 | HighlightTag::ByteLiteral => "byte_literal", |
63 | HighlightTag::CharLiteral => "char_literal", | 69 | HighlightTag::CharLiteral => "char_literal", |
@@ -66,12 +72,15 @@ impl HighlightTag { | |||
66 | HighlightTag::Enum => "enum", | 72 | HighlightTag::Enum => "enum", |
67 | HighlightTag::EnumVariant => "enum_variant", | 73 | HighlightTag::EnumVariant => "enum_variant", |
68 | HighlightTag::Field => "field", | 74 | HighlightTag::Field => "field", |
75 | HighlightTag::FormatSpecifier => "format_specifier", | ||
69 | HighlightTag::Function => "function", | 76 | HighlightTag::Function => "function", |
70 | HighlightTag::Keyword => "keyword", | 77 | HighlightTag::Keyword => "keyword", |
71 | HighlightTag::Lifetime => "lifetime", | 78 | HighlightTag::Lifetime => "lifetime", |
72 | HighlightTag::Macro => "macro", | 79 | HighlightTag::Macro => "macro", |
73 | HighlightTag::Module => "module", | 80 | HighlightTag::Module => "module", |
74 | HighlightTag::NumericLiteral => "numeric_literal", | 81 | HighlightTag::NumericLiteral => "numeric_literal", |
82 | HighlightTag::Operator => "operator", | ||
83 | HighlightTag::SelfKeyword => "self_keyword", | ||
75 | HighlightTag::SelfType => "self_type", | 84 | HighlightTag::SelfType => "self_type", |
76 | HighlightTag::Static => "static", | 85 | HighlightTag::Static => "static", |
77 | HighlightTag::StringLiteral => "string_literal", | 86 | HighlightTag::StringLiteral => "string_literal", |
@@ -82,7 +91,6 @@ impl HighlightTag { | |||
82 | HighlightTag::Union => "union", | 91 | HighlightTag::Union => "union", |
83 | HighlightTag::Local => "variable", | 92 | HighlightTag::Local => "variable", |
84 | HighlightTag::UnresolvedReference => "unresolved_reference", | 93 | HighlightTag::UnresolvedReference => "unresolved_reference", |
85 | HighlightTag::FormatSpecifier => "format_specifier", | ||
86 | } | 94 | } |
87 | } | 95 | } |
88 | } | 96 | } |
@@ -95,6 +103,7 @@ impl fmt::Display for HighlightTag { | |||
95 | 103 | ||
96 | impl HighlightModifier { | 104 | impl HighlightModifier { |
97 | const ALL: &'static [HighlightModifier] = &[ | 105 | const ALL: &'static [HighlightModifier] = &[ |
106 | HighlightModifier::Attribute, | ||
98 | HighlightModifier::ControlFlow, | 107 | HighlightModifier::ControlFlow, |
99 | HighlightModifier::Definition, | 108 | HighlightModifier::Definition, |
100 | HighlightModifier::Mutable, | 109 | HighlightModifier::Mutable, |
@@ -103,6 +112,7 @@ impl HighlightModifier { | |||
103 | 112 | ||
104 | fn as_str(self) -> &'static str { | 113 | fn as_str(self) -> &'static str { |
105 | match self { | 114 | match self { |
115 | HighlightModifier::Attribute => "attribute", | ||
106 | HighlightModifier::ControlFlow => "control", | 116 | HighlightModifier::ControlFlow => "control", |
107 | HighlightModifier::Definition => "declaration", | 117 | HighlightModifier::Definition => "declaration", |
108 | HighlightModifier::Mutable => "mutable", | 118 | HighlightModifier::Mutable => "mutable", |
diff --git a/crates/ra_ide/src/syntax_highlighting/tests.rs b/crates/ra_ide/src/syntax_highlighting/tests.rs index eb43a23da..36a1aa419 100644 --- a/crates/ra_ide/src/syntax_highlighting/tests.rs +++ b/crates/ra_ide/src/syntax_highlighting/tests.rs | |||
@@ -218,6 +218,7 @@ fn main() { | |||
218 | println!("{argument}", argument = "test"); // => "test" | 218 | println!("{argument}", argument = "test"); // => "test" |
219 | println!("{name} {}", 1, name = 2); // => "2 1" | 219 | println!("{name} {}", 1, name = 2); // => "2 1" |
220 | println!("{a} {c} {b}", a="a", b='b', c=3); // => "a 3 b" | 220 | println!("{a} {c} {b}", a="a", b='b', c=3); // => "a 3 b" |
221 | println!("{{{}}}", 2); // => "{2}" | ||
221 | println!("Hello {:5}!", "x"); | 222 | println!("Hello {:5}!", "x"); |
222 | println!("Hello {:1$}!", "x", 5); | 223 | println!("Hello {:1$}!", "x", 5); |
223 | println!("Hello {1:0$}!", 5, "x"); | 224 | println!("Hello {1:0$}!", 5, "x"); |
@@ -257,3 +258,34 @@ fn main() { | |||
257 | fs::write(dst_file, &actual_html).unwrap(); | 258 | fs::write(dst_file, &actual_html).unwrap(); |
258 | assert_eq_text!(expected_html, actual_html); | 259 | assert_eq_text!(expected_html, actual_html); |
259 | } | 260 | } |
261 | |||
262 | #[test] | ||
263 | fn test_unsafe_highlighting() { | ||
264 | let (analysis, file_id) = single_file( | ||
265 | r#" | ||
266 | unsafe fn unsafe_fn() {} | ||
267 | |||
268 | struct HasUnsafeFn; | ||
269 | |||
270 | impl HasUnsafeFn { | ||
271 | unsafe fn unsafe_method(&self) {} | ||
272 | } | ||
273 | |||
274 | fn main() { | ||
275 | let x = &5 as *const usize; | ||
276 | unsafe { | ||
277 | unsafe_fn(); | ||
278 | HasUnsafeFn.unsafe_method(); | ||
279 | let y = *x; | ||
280 | let z = -x; | ||
281 | } | ||
282 | } | ||
283 | "# | ||
284 | .trim(), | ||
285 | ); | ||
286 | let dst_file = project_dir().join("crates/ra_ide/src/snapshots/highlight_unsafe.html"); | ||
287 | let actual_html = &analysis.highlight_as_html(file_id, false).unwrap(); | ||
288 | let expected_html = &read_text(&dst_file); | ||
289 | fs::write(dst_file, &actual_html).unwrap(); | ||
290 | assert_eq_text!(expected_html, actual_html); | ||
291 | } | ||
diff --git a/crates/ra_ide/src/syntax_tree.rs b/crates/ra_ide/src/syntax_tree.rs index 86c70ff83..a341684fd 100644 --- a/crates/ra_ide/src/syntax_tree.rs +++ b/crates/ra_ide/src/syntax_tree.rs | |||
@@ -1,6 +1,4 @@ | |||
1 | //! FIXME: write short doc here | 1 | use ra_db::{FileId, SourceDatabase}; |
2 | |||
3 | use ra_db::SourceDatabase; | ||
4 | use ra_ide_db::RootDatabase; | 2 | use ra_ide_db::RootDatabase; |
5 | use ra_syntax::{ | 3 | use ra_syntax::{ |
6 | algo, AstNode, NodeOrToken, SourceFile, | 4 | algo, AstNode, NodeOrToken, SourceFile, |
@@ -8,8 +6,16 @@ use ra_syntax::{ | |||
8 | SyntaxToken, TextRange, TextSize, | 6 | SyntaxToken, TextRange, TextSize, |
9 | }; | 7 | }; |
10 | 8 | ||
11 | pub use ra_db::FileId; | 9 | // Feature: Show Syntax Tree |
12 | 10 | // | |
11 | // Shows the parse tree of the current file. It exists mostly for debugging | ||
12 | // rust-analyzer itself. | ||
13 | // | ||
14 | // |=== | ||
15 | // | Editor | Action Name | ||
16 | // | ||
17 | // | VS Code | **Rust Analyzer: Show Syntax Tree** | ||
18 | // |=== | ||
13 | pub(crate) fn syntax_tree( | 19 | pub(crate) fn syntax_tree( |
14 | db: &RootDatabase, | 20 | db: &RootDatabase, |
15 | file_id: FileId, | 21 | file_id: FileId, |
diff --git a/crates/ra_ide/src/typing.rs b/crates/ra_ide/src/typing.rs index 39bb3b357..67e2c33a0 100644 --- a/crates/ra_ide/src/typing.rs +++ b/crates/ra_ide/src/typing.rs | |||
@@ -32,6 +32,13 @@ pub(crate) use on_enter::on_enter; | |||
32 | 32 | ||
33 | pub(crate) const TRIGGER_CHARS: &str = ".=>"; | 33 | pub(crate) const TRIGGER_CHARS: &str = ".=>"; |
34 | 34 | ||
35 | // Feature: On Typing Assists | ||
36 | // | ||
37 | // Some features trigger on typing certain characters: | ||
38 | // | ||
39 | // - typing `let =` tries to smartly add `;` if `=` is followed by an existing expression | ||
40 | // - Enter inside comments automatically inserts `///` | ||
41 | // - typing `.` in a chain method call auto-indents | ||
35 | pub(crate) fn on_char_typed( | 42 | pub(crate) fn on_char_typed( |
36 | db: &RootDatabase, | 43 | db: &RootDatabase, |
37 | position: FilePosition, | 44 | position: FilePosition, |
diff --git a/crates/ra_ide/src/typing/on_enter.rs b/crates/ra_ide/src/typing/on_enter.rs index e7d64b4f6..a40d8af9c 100644 --- a/crates/ra_ide/src/typing/on_enter.rs +++ b/crates/ra_ide/src/typing/on_enter.rs | |||
@@ -11,9 +11,7 @@ use ra_syntax::{ | |||
11 | }; | 11 | }; |
12 | use ra_text_edit::TextEdit; | 12 | use ra_text_edit::TextEdit; |
13 | 13 | ||
14 | use crate::{SourceChange, SourceFileEdit}; | 14 | pub(crate) fn on_enter(db: &RootDatabase, position: FilePosition) -> Option<TextEdit> { |
15 | |||
16 | pub(crate) fn on_enter(db: &RootDatabase, position: FilePosition) -> Option<SourceChange> { | ||
17 | let parse = db.parse(position.file_id); | 15 | let parse = db.parse(position.file_id); |
18 | let file = parse.tree(); | 16 | let file = parse.tree(); |
19 | let comment = file | 17 | let comment = file |
@@ -41,9 +39,7 @@ pub(crate) fn on_enter(db: &RootDatabase, position: FilePosition) -> Option<Sour | |||
41 | let inserted = format!("\n{}{} $0", indent, prefix); | 39 | let inserted = format!("\n{}{} $0", indent, prefix); |
42 | let edit = TextEdit::insert(position.offset, inserted); | 40 | let edit = TextEdit::insert(position.offset, inserted); |
43 | 41 | ||
44 | let mut res = SourceChange::from(SourceFileEdit { edit, file_id: position.file_id }); | 42 | Some(edit) |
45 | res.is_snippet = true; | ||
46 | Some(res) | ||
47 | } | 43 | } |
48 | 44 | ||
49 | fn followed_by_comment(comment: &ast::Comment) -> bool { | 45 | fn followed_by_comment(comment: &ast::Comment) -> bool { |
@@ -90,9 +86,8 @@ mod tests { | |||
90 | let (analysis, file_id) = single_file(&before); | 86 | let (analysis, file_id) = single_file(&before); |
91 | let result = analysis.on_enter(FilePosition { offset, file_id }).unwrap()?; | 87 | let result = analysis.on_enter(FilePosition { offset, file_id }).unwrap()?; |
92 | 88 | ||
93 | assert_eq!(result.source_file_edits.len(), 1); | ||
94 | let mut actual = before.to_string(); | 89 | let mut actual = before.to_string(); |
95 | result.source_file_edits[0].edit.apply(&mut actual); | 90 | result.apply(&mut actual); |
96 | Some(actual) | 91 | Some(actual) |
97 | } | 92 | } |
98 | 93 | ||
diff --git a/crates/ra_ide_db/src/search.rs b/crates/ra_ide_db/src/search.rs index 589f44771..335a1ad03 100644 --- a/crates/ra_ide_db/src/search.rs +++ b/crates/ra_ide_db/src/search.rs | |||
@@ -124,29 +124,33 @@ impl Definition { | |||
124 | 124 | ||
125 | let vis = self.visibility(db); | 125 | let vis = self.visibility(db); |
126 | 126 | ||
127 | // FIXME: | ||
128 | // The following logic are wrong that it does not search | ||
129 | // for submodules within other files recursively. | ||
130 | |||
131 | if let Some(Visibility::Module(module)) = vis.and_then(|it| it.into()) { | 127 | if let Some(Visibility::Module(module)) = vis.and_then(|it| it.into()) { |
132 | let module: Module = module.into(); | 128 | let module: Module = module.into(); |
133 | let mut res = FxHashMap::default(); | 129 | let mut res = FxHashMap::default(); |
134 | let src = module.definition_source(db); | ||
135 | let file_id = src.file_id.original_file(db); | ||
136 | 130 | ||
137 | match src.value { | 131 | let mut to_visit = vec![module]; |
138 | ModuleSource::Module(m) => { | 132 | let mut is_first = true; |
139 | let range = Some(m.syntax().text_range()); | 133 | while let Some(module) = to_visit.pop() { |
140 | res.insert(file_id, range); | 134 | let src = module.definition_source(db); |
141 | } | 135 | let file_id = src.file_id.original_file(db); |
142 | ModuleSource::SourceFile(_) => { | 136 | match src.value { |
143 | res.insert(file_id, None); | 137 | ModuleSource::Module(m) => { |
144 | res.extend(module.children(db).map(|m| { | 138 | if is_first { |
145 | let src = m.definition_source(db); | 139 | let range = Some(m.syntax().text_range()); |
146 | (src.file_id.original_file(db), None) | 140 | res.insert(file_id, range); |
147 | })); | 141 | } else { |
148 | } | 142 | // We have already added the enclosing file to the search scope, |
143 | // so do nothing. | ||
144 | } | ||
145 | } | ||
146 | ModuleSource::SourceFile(_) => { | ||
147 | res.insert(file_id, None); | ||
148 | } | ||
149 | }; | ||
150 | is_first = false; | ||
151 | to_visit.extend(module.children(db)); | ||
149 | } | 152 | } |
153 | |||
150 | return SearchScope::new(res); | 154 | return SearchScope::new(res); |
151 | } | 155 | } |
152 | 156 | ||
diff --git a/crates/ra_ide_db/src/symbol_index.rs b/crates/ra_ide_db/src/symbol_index.rs index 95be11134..acc31fe3b 100644 --- a/crates/ra_ide_db/src/symbol_index.rs +++ b/crates/ra_ide_db/src/symbol_index.rs | |||
@@ -110,6 +110,27 @@ fn file_symbols(db: &impl SymbolsDatabase, file_id: FileId) -> Arc<SymbolIndex> | |||
110 | Arc::new(SymbolIndex::new(symbols)) | 110 | Arc::new(SymbolIndex::new(symbols)) |
111 | } | 111 | } |
112 | 112 | ||
113 | // Feature: Workspace Symbol | ||
114 | // | ||
115 | // Uses fuzzy-search to find types, modules and functions by name across your | ||
116 | // project and dependencies. This is **the** most useful feature, which improves code | ||
117 | // navigation tremendously. It mostly works on top of the built-in LSP | ||
118 | // functionality, however `#` and `*` symbols can be used to narrow down the | ||
119 | // search. Specifically, | ||
120 | // | ||
121 | // - `Foo` searches for `Foo` type in the current workspace | ||
122 | // - `foo#` searches for `foo` function in the current workspace | ||
123 | // - `Foo*` searches for `Foo` type among dependencies, including `stdlib` | ||
124 | // - `foo#*` searches for `foo` function among dependencies | ||
125 | // | ||
126 | // That is, `#` switches from "types" to all symbols, `*` switches from the current | ||
127 | // workspace to dependencies. | ||
128 | // | ||
129 | // |=== | ||
130 | // | Editor | Shortcut | ||
131 | // | ||
132 | // | VS Code | kbd:[Ctrl+T] | ||
133 | // |=== | ||
113 | pub fn world_symbols(db: &RootDatabase, query: Query) -> Vec<FileSymbol> { | 134 | pub fn world_symbols(db: &RootDatabase, query: Query) -> Vec<FileSymbol> { |
114 | /// Need to wrap Snapshot to provide `Clone` impl for `map_with` | 135 | /// Need to wrap Snapshot to provide `Clone` impl for `map_with` |
115 | struct Snap(salsa::Snapshot<RootDatabase>); | 136 | struct Snap(salsa::Snapshot<RootDatabase>); |
diff --git a/crates/ra_parser/src/grammar.rs b/crates/ra_parser/src/grammar.rs index be0cd5661..293baecf6 100644 --- a/crates/ra_parser/src/grammar.rs +++ b/crates/ra_parser/src/grammar.rs | |||
@@ -18,9 +18,10 @@ | |||
18 | //! // fn foo() {} | 18 | //! // fn foo() {} |
19 | //! ``` | 19 | //! ``` |
20 | //! | 20 | //! |
21 | //! After adding a new inline-test, run `cargo collect-tests` to extract | 21 | //! After adding a new inline-test, run `cargo xtask codegen` to |
22 | //! it as a standalone text-fixture into `tests/data/parser/inline`, and | 22 | //! extract it as a standalone text-fixture into |
23 | //! run `cargo test` once to create the "gold" value. | 23 | //! `crates/ra_syntax/test_data/parser/`, and run `cargo test` once to |
24 | //! create the "gold" value. | ||
24 | //! | 25 | //! |
25 | //! Coding convention: rules like `where_clause` always produce either a | 26 | //! Coding convention: rules like `where_clause` always produce either a |
26 | //! node or an error, rules like `opt_where_clause` may produce nothing. | 27 | //! node or an error, rules like `opt_where_clause` may produce nothing. |
diff --git a/crates/ra_parser/src/grammar/expressions.rs b/crates/ra_parser/src/grammar/expressions.rs index 34f039768..d6e8df32a 100644 --- a/crates/ra_parser/src/grammar/expressions.rs +++ b/crates/ra_parser/src/grammar/expressions.rs | |||
@@ -325,13 +325,27 @@ fn lhs(p: &mut Parser, r: Restrictions) -> Option<(CompletedMarker, BlockLike)> | |||
325 | let kind = match p.current() { | 325 | let kind = match p.current() { |
326 | // test ref_expr | 326 | // test ref_expr |
327 | // fn foo() { | 327 | // fn foo() { |
328 | // // reference operator | ||
328 | // let _ = &1; | 329 | // let _ = &1; |
329 | // let _ = &mut &f(); | 330 | // let _ = &mut &f(); |
331 | // let _ = &raw; | ||
332 | // let _ = &raw.0; | ||
333 | // // raw reference operator | ||
334 | // let _ = &raw mut foo; | ||
335 | // let _ = &raw const foo; | ||
330 | // } | 336 | // } |
331 | T![&] => { | 337 | T![&] => { |
332 | m = p.start(); | 338 | m = p.start(); |
333 | p.bump(T![&]); | 339 | p.bump(T![&]); |
334 | p.eat(T![mut]); | 340 | if p.at(IDENT) |
341 | && p.at_contextual_kw("raw") | ||
342 | && (p.nth_at(1, T![mut]) || p.nth_at(1, T![const])) | ||
343 | { | ||
344 | p.bump_remap(T![raw]); | ||
345 | p.bump_any(); | ||
346 | } else { | ||
347 | p.eat(T![mut]); | ||
348 | } | ||
335 | REF_EXPR | 349 | REF_EXPR |
336 | } | 350 | } |
337 | // test unary_expr | 351 | // test unary_expr |
diff --git a/crates/ra_proc_macro_srv/Cargo.toml b/crates/ra_proc_macro_srv/Cargo.toml index bb3003278..582102945 100644 --- a/crates/ra_proc_macro_srv/Cargo.toml +++ b/crates/ra_proc_macro_srv/Cargo.toml | |||
@@ -22,3 +22,4 @@ cargo_metadata = "0.10.0" | |||
22 | difference = "2.0.0" | 22 | difference = "2.0.0" |
23 | # used as proc macro test target | 23 | # used as proc macro test target |
24 | serde_derive = "1.0.106" | 24 | serde_derive = "1.0.106" |
25 | ra_toolchain = { path = "../ra_toolchain" } | ||
diff --git a/crates/ra_proc_macro_srv/src/tests/utils.rs b/crates/ra_proc_macro_srv/src/tests/utils.rs index 84348b5de..8d85f2d8a 100644 --- a/crates/ra_proc_macro_srv/src/tests/utils.rs +++ b/crates/ra_proc_macro_srv/src/tests/utils.rs | |||
@@ -2,7 +2,6 @@ | |||
2 | 2 | ||
3 | use crate::dylib; | 3 | use crate::dylib; |
4 | use crate::ProcMacroSrv; | 4 | use crate::ProcMacroSrv; |
5 | pub use difference::Changeset as __Changeset; | ||
6 | use ra_proc_macro::ListMacrosTask; | 5 | use ra_proc_macro::ListMacrosTask; |
7 | use std::str::FromStr; | 6 | use std::str::FromStr; |
8 | use test_utils::assert_eq_text; | 7 | use test_utils::assert_eq_text; |
@@ -13,7 +12,7 @@ mod fixtures { | |||
13 | 12 | ||
14 | // Use current project metadata to get the proc-macro dylib path | 13 | // Use current project metadata to get the proc-macro dylib path |
15 | pub fn dylib_path(crate_name: &str, version: &str) -> std::path::PathBuf { | 14 | pub fn dylib_path(crate_name: &str, version: &str) -> std::path::PathBuf { |
16 | let command = Command::new("cargo") | 15 | let command = Command::new(ra_toolchain::cargo()) |
17 | .args(&["check", "--message-format", "json"]) | 16 | .args(&["check", "--message-format", "json"]) |
18 | .output() | 17 | .output() |
19 | .unwrap() | 18 | .unwrap() |
diff --git a/crates/ra_project_model/src/cargo_workspace.rs b/crates/ra_project_model/src/cargo_workspace.rs index a306ce95f..4b7444039 100644 --- a/crates/ra_project_model/src/cargo_workspace.rs +++ b/crates/ra_project_model/src/cargo_workspace.rs | |||
@@ -64,7 +64,7 @@ impl Default for CargoConfig { | |||
64 | fn default() -> Self { | 64 | fn default() -> Self { |
65 | CargoConfig { | 65 | CargoConfig { |
66 | no_default_features: false, | 66 | no_default_features: false, |
67 | all_features: true, | 67 | all_features: false, |
68 | features: Vec::new(), | 68 | features: Vec::new(), |
69 | load_out_dirs_from_check: false, | 69 | load_out_dirs_from_check: false, |
70 | target: None, | 70 | target: None, |
diff --git a/crates/ra_project_model/src/json_project.rs b/crates/ra_project_model/src/json_project.rs index b030c8a6a..09c06fef9 100644 --- a/crates/ra_project_model/src/json_project.rs +++ b/crates/ra_project_model/src/json_project.rs | |||
@@ -5,6 +5,13 @@ use std::path::PathBuf; | |||
5 | use rustc_hash::{FxHashMap, FxHashSet}; | 5 | use rustc_hash::{FxHashMap, FxHashSet}; |
6 | use serde::Deserialize; | 6 | use serde::Deserialize; |
7 | 7 | ||
8 | /// Roots and crates that compose this Rust project. | ||
9 | #[derive(Clone, Debug, Deserialize)] | ||
10 | pub struct JsonProject { | ||
11 | pub(crate) roots: Vec<Root>, | ||
12 | pub(crate) crates: Vec<Crate>, | ||
13 | } | ||
14 | |||
8 | /// A root points to the directory which contains Rust crates. rust-analyzer watches all files in | 15 | /// A root points to the directory which contains Rust crates. rust-analyzer watches all files in |
9 | /// all roots. Roots might be nested. | 16 | /// all roots. Roots might be nested. |
10 | #[derive(Clone, Debug, Deserialize)] | 17 | #[derive(Clone, Debug, Deserialize)] |
@@ -20,8 +27,17 @@ pub struct Crate { | |||
20 | pub(crate) root_module: PathBuf, | 27 | pub(crate) root_module: PathBuf, |
21 | pub(crate) edition: Edition, | 28 | pub(crate) edition: Edition, |
22 | pub(crate) deps: Vec<Dep>, | 29 | pub(crate) deps: Vec<Dep>, |
30 | |||
31 | // This is the preferred method of providing cfg options. | ||
32 | #[serde(default)] | ||
33 | pub(crate) cfg: FxHashSet<String>, | ||
34 | |||
35 | // These two are here for transition only. | ||
36 | #[serde(default)] | ||
23 | pub(crate) atom_cfgs: FxHashSet<String>, | 37 | pub(crate) atom_cfgs: FxHashSet<String>, |
38 | #[serde(default)] | ||
24 | pub(crate) key_value_cfgs: FxHashMap<String, String>, | 39 | pub(crate) key_value_cfgs: FxHashMap<String, String>, |
40 | |||
25 | pub(crate) out_dir: Option<PathBuf>, | 41 | pub(crate) out_dir: Option<PathBuf>, |
26 | pub(crate) proc_macro_dylib_path: Option<PathBuf>, | 42 | pub(crate) proc_macro_dylib_path: Option<PathBuf>, |
27 | } | 43 | } |
@@ -48,9 +64,72 @@ pub struct Dep { | |||
48 | pub(crate) name: String, | 64 | pub(crate) name: String, |
49 | } | 65 | } |
50 | 66 | ||
51 | /// Roots and crates that compose this Rust project. | 67 | #[cfg(test)] |
52 | #[derive(Clone, Debug, Deserialize)] | 68 | mod tests { |
53 | pub struct JsonProject { | 69 | use super::*; |
54 | pub(crate) roots: Vec<Root>, | 70 | use serde_json::json; |
55 | pub(crate) crates: Vec<Crate>, | 71 | |
72 | #[test] | ||
73 | fn test_crate_deserialization() { | ||
74 | let raw_json = json!( { | ||
75 | "crate_id": 2, | ||
76 | "root_module": "this/is/a/file/path.rs", | ||
77 | "deps": [ | ||
78 | { | ||
79 | "crate": 1, | ||
80 | "name": "some_dep_crate" | ||
81 | }, | ||
82 | ], | ||
83 | "edition": "2015", | ||
84 | "cfg": [ | ||
85 | "atom_1", | ||
86 | "atom_2", | ||
87 | "feature=feature_1", | ||
88 | "feature=feature_2", | ||
89 | "other=value", | ||
90 | ], | ||
91 | |||
92 | }); | ||
93 | |||
94 | let krate: Crate = serde_json::from_value(raw_json).unwrap(); | ||
95 | |||
96 | assert!(krate.cfg.contains(&"atom_1".to_string())); | ||
97 | assert!(krate.cfg.contains(&"atom_2".to_string())); | ||
98 | assert!(krate.cfg.contains(&"feature=feature_1".to_string())); | ||
99 | assert!(krate.cfg.contains(&"feature=feature_2".to_string())); | ||
100 | assert!(krate.cfg.contains(&"other=value".to_string())); | ||
101 | } | ||
102 | |||
103 | #[test] | ||
104 | fn test_crate_deserialization_old_json() { | ||
105 | let raw_json = json!( { | ||
106 | "crate_id": 2, | ||
107 | "root_module": "this/is/a/file/path.rs", | ||
108 | "deps": [ | ||
109 | { | ||
110 | "crate": 1, | ||
111 | "name": "some_dep_crate" | ||
112 | }, | ||
113 | ], | ||
114 | "edition": "2015", | ||
115 | "atom_cfgs": [ | ||
116 | "atom_1", | ||
117 | "atom_2", | ||
118 | ], | ||
119 | "key_value_cfgs": { | ||
120 | "feature": "feature_1", | ||
121 | "feature": "feature_2", | ||
122 | "other": "value", | ||
123 | }, | ||
124 | }); | ||
125 | |||
126 | let krate: Crate = serde_json::from_value(raw_json).unwrap(); | ||
127 | |||
128 | assert!(krate.atom_cfgs.contains(&"atom_1".to_string())); | ||
129 | assert!(krate.atom_cfgs.contains(&"atom_2".to_string())); | ||
130 | assert!(krate.key_value_cfgs.contains_key(&"feature".to_string())); | ||
131 | assert_eq!(krate.key_value_cfgs.get("feature"), Some(&"feature_2".to_string())); | ||
132 | assert!(krate.key_value_cfgs.contains_key(&"other".to_string())); | ||
133 | assert_eq!(krate.key_value_cfgs.get("other"), Some(&"value".to_string())); | ||
134 | } | ||
56 | } | 135 | } |
diff --git a/crates/ra_project_model/src/lib.rs b/crates/ra_project_model/src/lib.rs index a2e9f65ef..7ad941279 100644 --- a/crates/ra_project_model/src/lib.rs +++ b/crates/ra_project_model/src/lib.rs | |||
@@ -14,7 +14,7 @@ use std::{ | |||
14 | use anyhow::{bail, Context, Result}; | 14 | use anyhow::{bail, Context, Result}; |
15 | use ra_cfg::CfgOptions; | 15 | use ra_cfg::CfgOptions; |
16 | use ra_db::{CrateGraph, CrateName, Edition, Env, ExternSource, ExternSourceId, FileId}; | 16 | use ra_db::{CrateGraph, CrateName, Edition, Env, ExternSource, ExternSourceId, FileId}; |
17 | use rustc_hash::FxHashMap; | 17 | use rustc_hash::{FxHashMap, FxHashSet}; |
18 | use serde_json::from_reader; | 18 | use serde_json::from_reader; |
19 | 19 | ||
20 | pub use crate::{ | 20 | pub use crate::{ |
@@ -32,6 +32,12 @@ pub enum ProjectWorkspace { | |||
32 | Json { project: JsonProject }, | 32 | Json { project: JsonProject }, |
33 | } | 33 | } |
34 | 34 | ||
35 | impl From<JsonProject> for ProjectWorkspace { | ||
36 | fn from(project: JsonProject) -> ProjectWorkspace { | ||
37 | ProjectWorkspace::Json { project } | ||
38 | } | ||
39 | } | ||
40 | |||
35 | /// `PackageRoot` describes a package root folder. | 41 | /// `PackageRoot` describes a package root folder. |
36 | /// Which may be an external dependency, or a member of | 42 | /// Which may be an external dependency, or a member of |
37 | /// the current workspace. | 43 | /// the current workspace. |
@@ -57,25 +63,25 @@ impl PackageRoot { | |||
57 | } | 63 | } |
58 | } | 64 | } |
59 | 65 | ||
60 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] | 66 | #[derive(Debug, Clone, PartialEq, Eq, Hash, Ord, PartialOrd)] |
61 | pub enum ProjectRoot { | 67 | pub enum ProjectManifest { |
62 | ProjectJson(PathBuf), | 68 | ProjectJson(PathBuf), |
63 | CargoToml(PathBuf), | 69 | CargoToml(PathBuf), |
64 | } | 70 | } |
65 | 71 | ||
66 | impl ProjectRoot { | 72 | impl ProjectManifest { |
67 | pub fn from_manifest_file(path: PathBuf) -> Result<ProjectRoot> { | 73 | pub fn from_manifest_file(path: PathBuf) -> Result<ProjectManifest> { |
68 | if path.ends_with("rust-project.json") { | 74 | if path.ends_with("rust-project.json") { |
69 | return Ok(ProjectRoot::ProjectJson(path)); | 75 | return Ok(ProjectManifest::ProjectJson(path)); |
70 | } | 76 | } |
71 | if path.ends_with("Cargo.toml") { | 77 | if path.ends_with("Cargo.toml") { |
72 | return Ok(ProjectRoot::CargoToml(path)); | 78 | return Ok(ProjectManifest::CargoToml(path)); |
73 | } | 79 | } |
74 | bail!("project root must point to Cargo.toml or rust-project.json: {}", path.display()) | 80 | bail!("project root must point to Cargo.toml or rust-project.json: {}", path.display()) |
75 | } | 81 | } |
76 | 82 | ||
77 | pub fn discover_single(path: &Path) -> Result<ProjectRoot> { | 83 | pub fn discover_single(path: &Path) -> Result<ProjectManifest> { |
78 | let mut candidates = ProjectRoot::discover(path)?; | 84 | let mut candidates = ProjectManifest::discover(path)?; |
79 | let res = match candidates.pop() { | 85 | let res = match candidates.pop() { |
80 | None => bail!("no projects"), | 86 | None => bail!("no projects"), |
81 | Some(it) => it, | 87 | Some(it) => it, |
@@ -87,12 +93,12 @@ impl ProjectRoot { | |||
87 | Ok(res) | 93 | Ok(res) |
88 | } | 94 | } |
89 | 95 | ||
90 | pub fn discover(path: &Path) -> io::Result<Vec<ProjectRoot>> { | 96 | pub fn discover(path: &Path) -> io::Result<Vec<ProjectManifest>> { |
91 | if let Some(project_json) = find_in_parent_dirs(path, "rust-project.json") { | 97 | if let Some(project_json) = find_in_parent_dirs(path, "rust-project.json") { |
92 | return Ok(vec![ProjectRoot::ProjectJson(project_json)]); | 98 | return Ok(vec![ProjectManifest::ProjectJson(project_json)]); |
93 | } | 99 | } |
94 | return find_cargo_toml(path) | 100 | return find_cargo_toml(path) |
95 | .map(|paths| paths.into_iter().map(ProjectRoot::CargoToml).collect()); | 101 | .map(|paths| paths.into_iter().map(ProjectManifest::CargoToml).collect()); |
96 | 102 | ||
97 | fn find_cargo_toml(path: &Path) -> io::Result<Vec<PathBuf>> { | 103 | fn find_cargo_toml(path: &Path) -> io::Result<Vec<PathBuf>> { |
98 | match find_in_parent_dirs(path, "Cargo.toml") { | 104 | match find_in_parent_dirs(path, "Cargo.toml") { |
@@ -128,16 +134,28 @@ impl ProjectRoot { | |||
128 | .collect() | 134 | .collect() |
129 | } | 135 | } |
130 | } | 136 | } |
137 | |||
138 | pub fn discover_all(paths: &[impl AsRef<Path>]) -> Vec<ProjectManifest> { | ||
139 | let mut res = paths | ||
140 | .iter() | ||
141 | .filter_map(|it| ProjectManifest::discover(it.as_ref()).ok()) | ||
142 | .flatten() | ||
143 | .collect::<FxHashSet<_>>() | ||
144 | .into_iter() | ||
145 | .collect::<Vec<_>>(); | ||
146 | res.sort(); | ||
147 | res | ||
148 | } | ||
131 | } | 149 | } |
132 | 150 | ||
133 | impl ProjectWorkspace { | 151 | impl ProjectWorkspace { |
134 | pub fn load( | 152 | pub fn load( |
135 | root: ProjectRoot, | 153 | manifest: ProjectManifest, |
136 | cargo_features: &CargoConfig, | 154 | cargo_features: &CargoConfig, |
137 | with_sysroot: bool, | 155 | with_sysroot: bool, |
138 | ) -> Result<ProjectWorkspace> { | 156 | ) -> Result<ProjectWorkspace> { |
139 | let res = match root { | 157 | let res = match manifest { |
140 | ProjectRoot::ProjectJson(project_json) => { | 158 | ProjectManifest::ProjectJson(project_json) => { |
141 | let file = File::open(&project_json).with_context(|| { | 159 | let file = File::open(&project_json).with_context(|| { |
142 | format!("Failed to open json file {}", project_json.display()) | 160 | format!("Failed to open json file {}", project_json.display()) |
143 | })?; | 161 | })?; |
@@ -148,7 +166,7 @@ impl ProjectWorkspace { | |||
148 | })?, | 166 | })?, |
149 | } | 167 | } |
150 | } | 168 | } |
151 | ProjectRoot::CargoToml(cargo_toml) => { | 169 | ProjectManifest::CargoToml(cargo_toml) => { |
152 | let cargo = CargoWorkspace::from_cargo_metadata(&cargo_toml, cargo_features) | 170 | let cargo = CargoWorkspace::from_cargo_metadata(&cargo_toml, cargo_features) |
153 | .with_context(|| { | 171 | .with_context(|| { |
154 | format!( | 172 | format!( |
@@ -252,6 +270,16 @@ impl ProjectWorkspace { | |||
252 | }; | 270 | }; |
253 | let cfg_options = { | 271 | let cfg_options = { |
254 | let mut opts = default_cfg_options.clone(); | 272 | let mut opts = default_cfg_options.clone(); |
273 | for cfg in &krate.cfg { | ||
274 | match cfg.find('=') { | ||
275 | None => opts.insert_atom(cfg.into()), | ||
276 | Some(pos) => { | ||
277 | let key = &cfg[..pos]; | ||
278 | let value = cfg[pos + 1..].trim_matches('"'); | ||
279 | opts.insert_key_value(key.into(), value.into()); | ||
280 | } | ||
281 | } | ||
282 | } | ||
255 | for name in &krate.atom_cfgs { | 283 | for name in &krate.atom_cfgs { |
256 | opts.insert_atom(name.into()); | 284 | opts.insert_atom(name.into()); |
257 | } | 285 | } |
diff --git a/crates/ra_syntax/Cargo.toml b/crates/ra_syntax/Cargo.toml index c07ff488e..a8ff2e74f 100644 --- a/crates/ra_syntax/Cargo.toml +++ b/crates/ra_syntax/Cargo.toml | |||
@@ -13,7 +13,7 @@ doctest = false | |||
13 | [dependencies] | 13 | [dependencies] |
14 | itertools = "0.9.0" | 14 | itertools = "0.9.0" |
15 | rowan = "0.10.0" | 15 | rowan = "0.10.0" |
16 | rustc_lexer = { version = "656.0.0", package = "rustc-ap-rustc_lexer" } | 16 | rustc_lexer = { version = "661.0.0", package = "rustc-ap-rustc_lexer" } |
17 | rustc-hash = "1.1.0" | 17 | rustc-hash = "1.1.0" |
18 | arrayvec = "0.5.1" | 18 | arrayvec = "0.5.1" |
19 | once_cell = "1.3.1" | 19 | once_cell = "1.3.1" |
diff --git a/crates/ra_syntax/src/ast.rs b/crates/ra_syntax/src/ast.rs index 1876afe95..eddc807d5 100644 --- a/crates/ra_syntax/src/ast.rs +++ b/crates/ra_syntax/src/ast.rs | |||
@@ -75,7 +75,7 @@ impl<N> AstChildren<N> { | |||
75 | impl<N: AstNode> Iterator for AstChildren<N> { | 75 | impl<N: AstNode> Iterator for AstChildren<N> { |
76 | type Item = N; | 76 | type Item = N; |
77 | fn next(&mut self) -> Option<N> { | 77 | fn next(&mut self) -> Option<N> { |
78 | self.inner.by_ref().find_map(N::cast) | 78 | self.inner.find_map(N::cast) |
79 | } | 79 | } |
80 | } | 80 | } |
81 | 81 | ||
diff --git a/crates/ra_syntax/src/ast/generated/nodes.rs b/crates/ra_syntax/src/ast/generated/nodes.rs index cf6067e57..cb430ca01 100644 --- a/crates/ra_syntax/src/ast/generated/nodes.rs +++ b/crates/ra_syntax/src/ast/generated/nodes.rs | |||
@@ -1081,6 +1081,7 @@ pub struct BlockExpr { | |||
1081 | impl ast::AttrsOwner for BlockExpr {} | 1081 | impl ast::AttrsOwner for BlockExpr {} |
1082 | impl ast::ModuleItemOwner for BlockExpr {} | 1082 | impl ast::ModuleItemOwner for BlockExpr {} |
1083 | impl BlockExpr { | 1083 | impl BlockExpr { |
1084 | pub fn label(&self) -> Option<Label> { support::child(&self.syntax) } | ||
1084 | pub fn l_curly_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T!['{']) } | 1085 | pub fn l_curly_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T!['{']) } |
1085 | pub fn statements(&self) -> AstChildren<Stmt> { support::children(&self.syntax) } | 1086 | pub fn statements(&self) -> AstChildren<Stmt> { support::children(&self.syntax) } |
1086 | pub fn expr(&self) -> Option<Expr> { support::child(&self.syntax) } | 1087 | pub fn expr(&self) -> Option<Expr> { support::child(&self.syntax) } |
@@ -1235,6 +1236,8 @@ impl CastExpr { | |||
1235 | /// ``` | 1236 | /// ``` |
1236 | /// ❰ &foo ❱; | 1237 | /// ❰ &foo ❱; |
1237 | /// ❰ &mut bar ❱; | 1238 | /// ❰ &mut bar ❱; |
1239 | /// ❰ &raw const bar ❱; | ||
1240 | /// ❰ &raw mut bar ❱; | ||
1238 | /// ``` | 1241 | /// ``` |
1239 | /// | 1242 | /// |
1240 | /// [Reference](https://doc.rust-lang.org/reference/expressions/operator-expr.html#borrow-operators) | 1243 | /// [Reference](https://doc.rust-lang.org/reference/expressions/operator-expr.html#borrow-operators) |
@@ -1247,6 +1250,7 @@ impl RefExpr { | |||
1247 | pub fn amp_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![&]) } | 1250 | pub fn amp_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![&]) } |
1248 | pub fn raw_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![raw]) } | 1251 | pub fn raw_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![raw]) } |
1249 | pub fn mut_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![mut]) } | 1252 | pub fn mut_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![mut]) } |
1253 | pub fn const_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![const]) } | ||
1250 | pub fn expr(&self) -> Option<Expr> { support::child(&self.syntax) } | 1254 | pub fn expr(&self) -> Option<Expr> { support::child(&self.syntax) } |
1251 | } | 1255 | } |
1252 | /// Prefix operator call. This is either `!` or `*` or `-`. | 1256 | /// Prefix operator call. This is either `!` or `*` or `-`. |
diff --git a/crates/ra_syntax/src/ast/tokens.rs b/crates/ra_syntax/src/ast/tokens.rs index 74906d8a6..04b0a4480 100644 --- a/crates/ra_syntax/src/ast/tokens.rs +++ b/crates/ra_syntax/src/ast/tokens.rs | |||
@@ -6,6 +6,7 @@ use crate::{ | |||
6 | ast::{AstToken, Comment, RawString, String, Whitespace}, | 6 | ast::{AstToken, Comment, RawString, String, Whitespace}, |
7 | TextRange, TextSize, | 7 | TextRange, TextSize, |
8 | }; | 8 | }; |
9 | use rustc_lexer::unescape::{unescape_literal, Mode}; | ||
9 | 10 | ||
10 | impl Comment { | 11 | impl Comment { |
11 | pub fn kind(&self) -> CommentKind { | 12 | pub fn kind(&self) -> CommentKind { |
@@ -147,7 +148,7 @@ impl HasStringValue for String { | |||
147 | 148 | ||
148 | let mut buf = std::string::String::with_capacity(text.len()); | 149 | let mut buf = std::string::String::with_capacity(text.len()); |
149 | let mut has_error = false; | 150 | let mut has_error = false; |
150 | rustc_lexer::unescape::unescape_str(text, &mut |_, unescaped_char| match unescaped_char { | 151 | unescape_literal(text, Mode::Str, &mut |_, unescaped_char| match unescaped_char { |
151 | Ok(c) => buf.push(c), | 152 | Ok(c) => buf.push(c), |
152 | Err(_) => has_error = true, | 153 | Err(_) => has_error = true, |
153 | }); | 154 | }); |
@@ -417,14 +418,9 @@ pub trait HasFormatSpecifier: AstToken { | |||
417 | 418 | ||
418 | let mut cloned = chars.clone().take(2); | 419 | let mut cloned = chars.clone().take(2); |
419 | let first = cloned.next().and_then(|next| next.1.as_ref().ok()).copied(); | 420 | let first = cloned.next().and_then(|next| next.1.as_ref().ok()).copied(); |
420 | let second = cloned.next().and_then(|next| next.1.as_ref().ok()).copied(); | ||
421 | if first != Some('}') { | 421 | if first != Some('}') { |
422 | continue; | 422 | continue; |
423 | } | 423 | } |
424 | if second == Some('}') { | ||
425 | // Escaped format end specifier, `}}` | ||
426 | continue; | ||
427 | } | ||
428 | skip_char_and_emit(&mut chars, FormatSpecifier::Close, &mut callback); | 424 | skip_char_and_emit(&mut chars, FormatSpecifier::Close, &mut callback); |
429 | } | 425 | } |
430 | _ => { | 426 | _ => { |
@@ -498,7 +494,7 @@ impl HasFormatSpecifier for String { | |||
498 | let offset = self.text_range_between_quotes()?.start() - self.syntax().text_range().start(); | 494 | let offset = self.text_range_between_quotes()?.start() - self.syntax().text_range().start(); |
499 | 495 | ||
500 | let mut res = Vec::with_capacity(text.len()); | 496 | let mut res = Vec::with_capacity(text.len()); |
501 | rustc_lexer::unescape::unescape_str(text, &mut |range, unescaped_char| { | 497 | unescape_literal(text, Mode::Str, &mut |range, unescaped_char| { |
502 | res.push(( | 498 | res.push(( |
503 | TextRange::new(range.start.try_into().unwrap(), range.end.try_into().unwrap()) | 499 | TextRange::new(range.start.try_into().unwrap(), range.end.try_into().unwrap()) |
504 | + offset, | 500 | + offset, |
diff --git a/crates/ra_syntax/src/ast/traits.rs b/crates/ra_syntax/src/ast/traits.rs index bfc05e08b..a8f2454fd 100644 --- a/crates/ra_syntax/src/ast/traits.rs +++ b/crates/ra_syntax/src/ast/traits.rs | |||
@@ -83,13 +83,22 @@ pub trait DocCommentsOwner: AstNode { | |||
83 | CommentIter { iter: self.syntax().children_with_tokens() } | 83 | CommentIter { iter: self.syntax().children_with_tokens() } |
84 | } | 84 | } |
85 | 85 | ||
86 | fn doc_comment_text(&self) -> Option<String> { | ||
87 | self.doc_comments().doc_comment_text() | ||
88 | } | ||
89 | } | ||
90 | |||
91 | impl CommentIter { | ||
92 | pub fn from_syntax_node(syntax_node: &ast::SyntaxNode) -> CommentIter { | ||
93 | CommentIter { iter: syntax_node.children_with_tokens() } | ||
94 | } | ||
95 | |||
86 | /// Returns the textual content of a doc comment block as a single string. | 96 | /// Returns the textual content of a doc comment block as a single string. |
87 | /// That is, strips leading `///` (+ optional 1 character of whitespace), | 97 | /// That is, strips leading `///` (+ optional 1 character of whitespace), |
88 | /// trailing `*/`, trailing whitespace and then joins the lines. | 98 | /// trailing `*/`, trailing whitespace and then joins the lines. |
89 | fn doc_comment_text(&self) -> Option<String> { | 99 | pub fn doc_comment_text(self) -> Option<String> { |
90 | let mut has_comments = false; | 100 | let mut has_comments = false; |
91 | let docs = self | 101 | let docs = self |
92 | .doc_comments() | ||
93 | .filter(|comment| comment.kind().doc.is_some()) | 102 | .filter(|comment| comment.kind().doc.is_some()) |
94 | .map(|comment| { | 103 | .map(|comment| { |
95 | has_comments = true; | 104 | has_comments = true; |
diff --git a/crates/ra_syntax/src/syntax_node.rs b/crates/ra_syntax/src/syntax_node.rs index e566af7e8..9650b8781 100644 --- a/crates/ra_syntax/src/syntax_node.rs +++ b/crates/ra_syntax/src/syntax_node.rs | |||
@@ -48,11 +48,11 @@ impl SyntaxTreeBuilder { | |||
48 | 48 | ||
49 | pub fn finish(self) -> Parse<SyntaxNode> { | 49 | pub fn finish(self) -> Parse<SyntaxNode> { |
50 | let (green, errors) = self.finish_raw(); | 50 | let (green, errors) = self.finish_raw(); |
51 | let node = SyntaxNode::new_root(green); | ||
52 | if cfg!(debug_assertions) { | 51 | if cfg!(debug_assertions) { |
52 | let node = SyntaxNode::new_root(green.clone()); | ||
53 | crate::validation::validate_block_structure(&node); | 53 | crate::validation::validate_block_structure(&node); |
54 | } | 54 | } |
55 | Parse::new(node.green().clone(), errors) | 55 | Parse::new(green, errors) |
56 | } | 56 | } |
57 | 57 | ||
58 | pub fn token(&mut self, kind: SyntaxKind, text: SmolStr) { | 58 | pub fn token(&mut self, kind: SyntaxKind, text: SmolStr) { |
diff --git a/crates/ra_syntax/src/validation.rs b/crates/ra_syntax/src/validation.rs index d68cf0a82..fdec48fb0 100644 --- a/crates/ra_syntax/src/validation.rs +++ b/crates/ra_syntax/src/validation.rs | |||
@@ -2,15 +2,15 @@ | |||
2 | 2 | ||
3 | mod block; | 3 | mod block; |
4 | 4 | ||
5 | use std::convert::TryFrom; | ||
6 | |||
7 | use rustc_lexer::unescape; | ||
8 | |||
9 | use crate::{ | 5 | use crate::{ |
10 | ast, match_ast, AstNode, SyntaxError, | 6 | ast, match_ast, AstNode, SyntaxError, |
11 | SyntaxKind::{BYTE, BYTE_STRING, CHAR, CONST_DEF, FN_DEF, INT_NUMBER, STRING, TYPE_ALIAS_DEF}, | 7 | SyntaxKind::{BYTE, BYTE_STRING, CHAR, CONST_DEF, FN_DEF, INT_NUMBER, STRING, TYPE_ALIAS_DEF}, |
12 | SyntaxNode, SyntaxToken, TextSize, T, | 8 | SyntaxNode, SyntaxToken, TextSize, T, |
13 | }; | 9 | }; |
10 | use rustc_lexer::unescape::{ | ||
11 | self, unescape_byte, unescape_byte_literal, unescape_char, unescape_literal, Mode, | ||
12 | }; | ||
13 | use std::convert::TryFrom; | ||
14 | 14 | ||
15 | fn rustc_unescape_error_to_string(err: unescape::EscapeError) -> &'static str { | 15 | fn rustc_unescape_error_to_string(err: unescape::EscapeError) -> &'static str { |
16 | use unescape::EscapeError as EE; | 16 | use unescape::EscapeError as EE; |
@@ -81,10 +81,8 @@ fn rustc_unescape_error_to_string(err: unescape::EscapeError) -> &'static str { | |||
81 | 81 | ||
82 | pub(crate) fn validate(root: &SyntaxNode) -> Vec<SyntaxError> { | 82 | pub(crate) fn validate(root: &SyntaxNode) -> Vec<SyntaxError> { |
83 | // FIXME: | 83 | // FIXME: |
84 | // * Add validation of character literal containing only a single char | 84 | // * Add unescape validation of raw string literals and raw byte string literals |
85 | // * Add validation of `crate` keyword not appearing in the middle of the symbol path | ||
86 | // * Add validation of doc comments are being attached to nodes | 85 | // * Add validation of doc comments are being attached to nodes |
87 | // * Remove validation of unterminated literals (it is already implemented in `tokenize()`) | ||
88 | 86 | ||
89 | let mut errors = Vec::new(); | 87 | let mut errors = Vec::new(); |
90 | for node in root.descendants() { | 88 | for node in root.descendants() { |
@@ -121,18 +119,18 @@ fn validate_literal(literal: ast::Literal, acc: &mut Vec<SyntaxError>) { | |||
121 | 119 | ||
122 | match token.kind() { | 120 | match token.kind() { |
123 | BYTE => { | 121 | BYTE => { |
124 | if let Some(Err(e)) = unquote(text, 2, '\'').map(unescape::unescape_byte) { | 122 | if let Some(Err(e)) = unquote(text, 2, '\'').map(unescape_byte) { |
125 | push_err(2, e); | 123 | push_err(2, e); |
126 | } | 124 | } |
127 | } | 125 | } |
128 | CHAR => { | 126 | CHAR => { |
129 | if let Some(Err(e)) = unquote(text, 1, '\'').map(unescape::unescape_char) { | 127 | if let Some(Err(e)) = unquote(text, 1, '\'').map(unescape_char) { |
130 | push_err(1, e); | 128 | push_err(1, e); |
131 | } | 129 | } |
132 | } | 130 | } |
133 | BYTE_STRING => { | 131 | BYTE_STRING => { |
134 | if let Some(without_quotes) = unquote(text, 2, '"') { | 132 | if let Some(without_quotes) = unquote(text, 2, '"') { |
135 | unescape::unescape_byte_str(without_quotes, &mut |range, char| { | 133 | unescape_byte_literal(without_quotes, Mode::ByteStr, &mut |range, char| { |
136 | if let Err(err) = char { | 134 | if let Err(err) = char { |
137 | push_err(2, (range.start, err)); | 135 | push_err(2, (range.start, err)); |
138 | } | 136 | } |
@@ -141,7 +139,7 @@ fn validate_literal(literal: ast::Literal, acc: &mut Vec<SyntaxError>) { | |||
141 | } | 139 | } |
142 | STRING => { | 140 | STRING => { |
143 | if let Some(without_quotes) = unquote(text, 1, '"') { | 141 | if let Some(without_quotes) = unquote(text, 1, '"') { |
144 | unescape::unescape_str(without_quotes, &mut |range, char| { | 142 | unescape_literal(without_quotes, Mode::Str, &mut |range, char| { |
145 | if let Err(err) = char { | 143 | if let Err(err) = char { |
146 | push_err(1, (range.start, err)); | 144 | push_err(1, (range.start, err)); |
147 | } | 145 | } |
diff --git a/crates/ra_syntax/test_data/parser/inline/ok/0082_ref_expr.rast b/crates/ra_syntax/test_data/parser/inline/ok/0082_ref_expr.rast index 7fe96e17d..58bdf7e34 100644 --- a/crates/ra_syntax/test_data/parser/inline/ok/0082_ref_expr.rast +++ b/crates/ra_syntax/test_data/parser/inline/ok/0082_ref_expr.rast | |||
@@ -1,5 +1,5 @@ | |||
1 | SOURCE_FILE@0..52 | 1 | [email protected]00 |
2 | FN_DEF@0..51 | 2 | [email protected]99 |
3 | [email protected] "fn" | 3 | [email protected] "fn" |
4 | [email protected] " " | 4 | [email protected] " " |
5 | [email protected] | 5 | [email protected] |
@@ -8,47 +8,131 @@ [email protected] | |||
8 | [email protected] "(" | 8 | [email protected] "(" |
9 | [email protected] ")" | 9 | [email protected] ")" |
10 | [email protected] " " | 10 | [email protected] " " |
11 | BLOCK_EXPR@9..51 | 11 | [email protected]99 |
12 | [email protected] "{" | 12 | [email protected] "{" |
13 | [email protected] "\n " | 13 | [email protected] "\n " |
14 | [email protected] | 14 | [email protected] "// reference operator" |
15 | [email protected] "let" | 15 | [email protected] "\n " |
16 | [email protected] " " | 16 | [email protected] |
17 | [email protected] | 17 | [email protected] "let" |
18 | [email protected] "_" | 18 | [email protected] " " |
19 | [email protected] " " | 19 | [email protected] |
20 | [email protected] "=" | 20 | [email protected] "_" |
21 | [email protected] " " | 21 | [email protected] " " |
22 | [email protected] | 22 | [email protected] "=" |
23 | [email protected] "&" | 23 | [email protected] " " |
24 | [email protected] | 24 | [email protected] |
25 | [email protected] "1" | 25 | [email protected] "&" |
26 | [email protected] ";" | 26 | [email protected] |
27 | [email protected] "\n " | 27 | [email protected] "1" |
28 | [email protected] | 28 | [email protected] ";" |
29 | [email protected] "let" | 29 | [email protected] "\n " |
30 | [email protected] " " | 30 | [email protected] |
31 | [email protected] | 31 | [email protected] "let" |
32 | [email protected] "_" | 32 | [email protected] " " |
33 | [email protected] " " | 33 | [email protected] |
34 | [email protected] "=" | 34 | [email protected] "_" |
35 | [email protected] " " | 35 | [email protected] " " |
36 | [email protected] | 36 | [email protected] "=" |
37 | [email protected] "&" | 37 | [email protected] " " |
38 | [email protected] "mut" | 38 | [email protected] |
39 | [email protected] " " | 39 | [email protected] "&" |
40 | [email protected] | 40 | [email protected] "mut" |
41 | [email protected] "&" | 41 | [email protected] " " |
42 | [email protected] | 42 | [email protected] |
43 | [email protected] | 43 | [email protected] "&" |
44 | [email protected] | 44 | [email protected] |
45 | [email protected] | 45 | [email protected] |
46 | [email protected] | 46 | [email protected] |
47 | [email protected] "f" | 47 | [email protected] |
48 | [email protected] | 48 | [email protected] |
49 | [email protected] "(" | 49 | [email protected] "f" |
50 | [email protected] ")" | 50 | [email protected] |
51 | [email protected] ";" | 51 | [email protected] "(" |
52 | [email protected] "\n" | 52 | [email protected] ")" |
53 | [email protected] "}" | 53 | [email protected] ";" |
54 | [email protected] "\n" | 54 | [email protected] "\n " |
55 | [email protected] | ||
56 | [email protected] "let" | ||
57 | [email protected] " " | ||
58 | [email protected] | ||
59 | [email protected] "_" | ||
60 | [email protected] " " | ||
61 | [email protected] "=" | ||
62 | [email protected] " " | ||
63 | [email protected] | ||
64 | [email protected] "&" | ||
65 | [email protected] | ||
66 | [email protected] | ||
67 | [email protected] | ||
68 | [email protected] | ||
69 | [email protected] "raw" | ||
70 | [email protected] ";" | ||
71 | [email protected] "\n " | ||
72 | [email protected] | ||
73 | [email protected] "let" | ||
74 | [email protected] " " | ||
75 | [email protected] | ||
76 | [email protected] "_" | ||
77 | [email protected] " " | ||
78 | [email protected] "=" | ||
79 | [email protected] " " | ||
80 | [email protected] | ||
81 | [email protected] "&" | ||
82 | [email protected] | ||
83 | [email protected] | ||
84 | [email protected] | ||
85 | [email protected] | ||
86 | [email protected] | ||
87 | [email protected] "raw" | ||
88 | [email protected] "." | ||
89 | [email protected] | ||
90 | [email protected] "0" | ||
91 | [email protected] ";" | ||
92 | [email protected] "\n " | ||
93 | [email protected] "// raw reference oper ..." | ||
94 | [email protected] "\n " | ||
95 | [email protected] | ||
96 | [email protected] "let" | ||
97 | [email protected] " " | ||
98 | [email protected] | ||
99 | [email protected] "_" | ||
100 | [email protected] " " | ||
101 | [email protected] "=" | ||
102 | [email protected] " " | ||
103 | [email protected] | ||
104 | [email protected] "&" | ||
105 | [email protected] "raw" | ||
106 | [email protected] " " | ||
107 | [email protected] "mut" | ||
108 | [email protected] " " | ||
109 | [email protected] | ||
110 | [email protected] | ||
111 | [email protected] | ||
112 | [email protected] | ||
113 | [email protected] "foo" | ||
114 | [email protected] ";" | ||
115 | [email protected] "\n " | ||
116 | [email protected] | ||
117 | [email protected] "let" | ||
118 | [email protected] " " | ||
119 | [email protected] | ||
120 | [email protected] "_" | ||
121 | [email protected] " " | ||
122 | [email protected] "=" | ||
123 | [email protected] " " | ||
124 | [email protected] | ||
125 | [email protected] "&" | ||
126 | [email protected] "raw" | ||
127 | [email protected] " " | ||
128 | [email protected] "const" | ||
129 | [email protected] " " | ||
130 | [email protected] | ||
131 | [email protected] | ||
132 | [email protected] | ||
133 | [email protected] | ||
134 | [email protected] "foo" | ||
135 | [email protected] ";" | ||
136 | [email protected] "\n" | ||
137 | [email protected] "}" | ||
138 | [email protected] "\n" | ||
diff --git a/crates/ra_syntax/test_data/parser/inline/ok/0082_ref_expr.rs b/crates/ra_syntax/test_data/parser/inline/ok/0082_ref_expr.rs index 2dac6be95..c5262f446 100644 --- a/crates/ra_syntax/test_data/parser/inline/ok/0082_ref_expr.rs +++ b/crates/ra_syntax/test_data/parser/inline/ok/0082_ref_expr.rs | |||
@@ -1,4 +1,10 @@ | |||
1 | fn foo() { | 1 | fn foo() { |
2 | // reference operator | ||
2 | let _ = &1; | 3 | let _ = &1; |
3 | let _ = &mut &f(); | 4 | let _ = &mut &f(); |
5 | let _ = &raw; | ||
6 | let _ = &raw.0; | ||
7 | // raw reference operator | ||
8 | let _ = &raw mut foo; | ||
9 | let _ = &raw const foo; | ||
4 | } | 10 | } |
diff --git a/crates/rust-analyzer/Cargo.toml b/crates/rust-analyzer/Cargo.toml index 9b2d29b1d..65b487db3 100644 --- a/crates/rust-analyzer/Cargo.toml +++ b/crates/rust-analyzer/Cargo.toml | |||
@@ -40,6 +40,7 @@ ra_project_model = { path = "../ra_project_model" } | |||
40 | ra_syntax = { path = "../ra_syntax" } | 40 | ra_syntax = { path = "../ra_syntax" } |
41 | ra_text_edit = { path = "../ra_text_edit" } | 41 | ra_text_edit = { path = "../ra_text_edit" } |
42 | ra_vfs = "0.6.0" | 42 | ra_vfs = "0.6.0" |
43 | ra_cfg = { path = "../ra_cfg"} | ||
43 | 44 | ||
44 | # This should only be used in CLI | 45 | # This should only be used in CLI |
45 | ra_db = { path = "../ra_db" } | 46 | ra_db = { path = "../ra_db" } |
@@ -55,6 +56,8 @@ winapi = "0.3.8" | |||
55 | tempfile = "3.1.0" | 56 | tempfile = "3.1.0" |
56 | insta = "0.16.0" | 57 | insta = "0.16.0" |
57 | test_utils = { path = "../test_utils" } | 58 | test_utils = { path = "../test_utils" } |
59 | mbe = { path = "../ra_mbe", package = "ra_mbe" } | ||
60 | tt = { path = "../ra_tt", package = "ra_tt" } | ||
58 | 61 | ||
59 | [features] | 62 | [features] |
60 | jemalloc = [ "ra_prof/jemalloc" ] | 63 | jemalloc = [ "ra_prof/jemalloc" ] |
diff --git a/crates/rust-analyzer/src/bin/main.rs b/crates/rust-analyzer/src/bin/main.rs index e82fd57de..8d071ab1c 100644 --- a/crates/rust-analyzer/src/bin/main.rs +++ b/crates/rust-analyzer/src/bin/main.rs | |||
@@ -4,9 +4,14 @@ | |||
4 | mod args; | 4 | mod args; |
5 | 5 | ||
6 | use lsp_server::Connection; | 6 | use lsp_server::Connection; |
7 | use rust_analyzer::{cli, config::Config, from_json, Result}; | 7 | use rust_analyzer::{ |
8 | cli, | ||
9 | config::{Config, LinkedProject}, | ||
10 | from_json, Result, | ||
11 | }; | ||
8 | 12 | ||
9 | use crate::args::HelpPrinted; | 13 | use crate::args::HelpPrinted; |
14 | use ra_project_model::ProjectManifest; | ||
10 | 15 | ||
11 | fn main() -> Result<()> { | 16 | fn main() -> Result<()> { |
12 | setup_logging()?; | 17 | setup_logging()?; |
@@ -97,17 +102,6 @@ fn run_server() -> Result<()> { | |||
97 | log::info!("Client '{}' {}", client_info.name, client_info.version.unwrap_or_default()); | 102 | log::info!("Client '{}' {}", client_info.name, client_info.version.unwrap_or_default()); |
98 | } | 103 | } |
99 | 104 | ||
100 | let cwd = std::env::current_dir()?; | ||
101 | let root = initialize_params.root_uri.and_then(|it| it.to_file_path().ok()).unwrap_or(cwd); | ||
102 | |||
103 | let workspace_roots = initialize_params | ||
104 | .workspace_folders | ||
105 | .map(|workspaces| { | ||
106 | workspaces.into_iter().filter_map(|it| it.uri.to_file_path().ok()).collect::<Vec<_>>() | ||
107 | }) | ||
108 | .filter(|workspaces| !workspaces.is_empty()) | ||
109 | .unwrap_or_else(|| vec![root]); | ||
110 | |||
111 | let config = { | 105 | let config = { |
112 | let mut config = Config::default(); | 106 | let mut config = Config::default(); |
113 | if let Some(value) = &initialize_params.initialization_options { | 107 | if let Some(value) = &initialize_params.initialization_options { |
@@ -115,10 +109,31 @@ fn run_server() -> Result<()> { | |||
115 | } | 109 | } |
116 | config.update_caps(&initialize_params.capabilities); | 110 | config.update_caps(&initialize_params.capabilities); |
117 | 111 | ||
112 | if config.linked_projects.is_empty() { | ||
113 | let cwd = std::env::current_dir()?; | ||
114 | let root = | ||
115 | initialize_params.root_uri.and_then(|it| it.to_file_path().ok()).unwrap_or(cwd); | ||
116 | let workspace_roots = initialize_params | ||
117 | .workspace_folders | ||
118 | .map(|workspaces| { | ||
119 | workspaces | ||
120 | .into_iter() | ||
121 | .filter_map(|it| it.uri.to_file_path().ok()) | ||
122 | .collect::<Vec<_>>() | ||
123 | }) | ||
124 | .filter(|workspaces| !workspaces.is_empty()) | ||
125 | .unwrap_or_else(|| vec![root]); | ||
126 | |||
127 | config.linked_projects = ProjectManifest::discover_all(&workspace_roots) | ||
128 | .into_iter() | ||
129 | .map(LinkedProject::from) | ||
130 | .collect(); | ||
131 | } | ||
132 | |||
118 | config | 133 | config |
119 | }; | 134 | }; |
120 | 135 | ||
121 | rust_analyzer::main_loop(workspace_roots, config, connection)?; | 136 | rust_analyzer::main_loop(config, connection)?; |
122 | 137 | ||
123 | log::info!("shutting down IO..."); | 138 | log::info!("shutting down IO..."); |
124 | io_threads.join()?; | 139 | io_threads.join()?; |
diff --git a/crates/rust-analyzer/src/caps.rs b/crates/rust-analyzer/src/caps.rs index 780fc9317..673795e78 100644 --- a/crates/rust-analyzer/src/caps.rs +++ b/crates/rust-analyzer/src/caps.rs | |||
@@ -85,6 +85,11 @@ pub fn server_capabilities(client_caps: &ClientCapabilities) -> ServerCapabiliti | |||
85 | experimental: Some(json!({ | 85 | experimental: Some(json!({ |
86 | "joinLines": true, | 86 | "joinLines": true, |
87 | "ssr": true, | 87 | "ssr": true, |
88 | "onEnter": true, | ||
89 | "parentModule": true, | ||
90 | "runnables": { | ||
91 | "kinds": [ "cargo" ], | ||
92 | }, | ||
88 | })), | 93 | })), |
89 | } | 94 | } |
90 | } | 95 | } |
diff --git a/crates/rust-analyzer/src/cargo_target_spec.rs b/crates/rust-analyzer/src/cargo_target_spec.rs index 5e5a17943..44f856f6b 100644 --- a/crates/rust-analyzer/src/cargo_target_spec.rs +++ b/crates/rust-analyzer/src/cargo_target_spec.rs | |||
@@ -1,9 +1,10 @@ | |||
1 | //! See `CargoTargetSpec` | 1 | //! See `CargoTargetSpec` |
2 | 2 | ||
3 | use ra_cfg::CfgExpr; | ||
3 | use ra_ide::{FileId, RunnableKind, TestId}; | 4 | use ra_ide::{FileId, RunnableKind, TestId}; |
4 | use ra_project_model::{self, ProjectWorkspace, TargetKind}; | 5 | use ra_project_model::{self, ProjectWorkspace, TargetKind}; |
5 | 6 | ||
6 | use crate::{world::WorldSnapshot, Result}; | 7 | use crate::{global_state::GlobalStateSnapshot, Result}; |
7 | 8 | ||
8 | /// Abstract representation of Cargo target. | 9 | /// Abstract representation of Cargo target. |
9 | /// | 10 | /// |
@@ -20,6 +21,7 @@ impl CargoTargetSpec { | |||
20 | pub(crate) fn runnable_args( | 21 | pub(crate) fn runnable_args( |
21 | spec: Option<CargoTargetSpec>, | 22 | spec: Option<CargoTargetSpec>, |
22 | kind: &RunnableKind, | 23 | kind: &RunnableKind, |
24 | cfgs: &[CfgExpr], | ||
23 | ) -> Result<(Vec<String>, Vec<String>)> { | 25 | ) -> Result<(Vec<String>, Vec<String>)> { |
24 | let mut args = Vec::new(); | 26 | let mut args = Vec::new(); |
25 | let mut extra_args = Vec::new(); | 27 | let mut extra_args = Vec::new(); |
@@ -73,11 +75,21 @@ impl CargoTargetSpec { | |||
73 | } | 75 | } |
74 | } | 76 | } |
75 | } | 77 | } |
78 | |||
79 | let mut features = Vec::new(); | ||
80 | for cfg in cfgs { | ||
81 | required_features(cfg, &mut features); | ||
82 | } | ||
83 | for feature in features { | ||
84 | args.push("--features".to_string()); | ||
85 | args.push(feature); | ||
86 | } | ||
87 | |||
76 | Ok((args, extra_args)) | 88 | Ok((args, extra_args)) |
77 | } | 89 | } |
78 | 90 | ||
79 | pub(crate) fn for_file( | 91 | pub(crate) fn for_file( |
80 | world: &WorldSnapshot, | 92 | world: &GlobalStateSnapshot, |
81 | file_id: FileId, | 93 | file_id: FileId, |
82 | ) -> Result<Option<CargoTargetSpec>> { | 94 | ) -> Result<Option<CargoTargetSpec>> { |
83 | let &crate_id = match world.analysis().crate_for(file_id)?.first() { | 95 | let &crate_id = match world.analysis().crate_for(file_id)?.first() { |
@@ -132,3 +144,74 @@ impl CargoTargetSpec { | |||
132 | } | 144 | } |
133 | } | 145 | } |
134 | } | 146 | } |
147 | |||
148 | /// Fill minimal features needed | ||
149 | fn required_features(cfg_expr: &CfgExpr, features: &mut Vec<String>) { | ||
150 | match cfg_expr { | ||
151 | CfgExpr::KeyValue { key, value } if key == "feature" => features.push(value.to_string()), | ||
152 | CfgExpr::All(preds) => { | ||
153 | preds.iter().for_each(|cfg| required_features(cfg, features)); | ||
154 | } | ||
155 | CfgExpr::Any(preds) => { | ||
156 | for cfg in preds { | ||
157 | let len_features = features.len(); | ||
158 | required_features(cfg, features); | ||
159 | if len_features != features.len() { | ||
160 | break; | ||
161 | } | ||
162 | } | ||
163 | } | ||
164 | _ => {} | ||
165 | } | ||
166 | } | ||
167 | |||
168 | #[cfg(test)] | ||
169 | mod tests { | ||
170 | use super::*; | ||
171 | |||
172 | use mbe::{ast_to_token_tree, TokenMap}; | ||
173 | use ra_cfg::parse_cfg; | ||
174 | use ra_syntax::{ | ||
175 | ast::{self, AstNode}, | ||
176 | SmolStr, | ||
177 | }; | ||
178 | |||
179 | fn get_token_tree_generated(input: &str) -> (tt::Subtree, TokenMap) { | ||
180 | let source_file = ast::SourceFile::parse(input).ok().unwrap(); | ||
181 | let tt = source_file.syntax().descendants().find_map(ast::TokenTree::cast).unwrap(); | ||
182 | ast_to_token_tree(&tt).unwrap() | ||
183 | } | ||
184 | |||
185 | #[test] | ||
186 | fn test_cfg_expr_minimal_features_needed() { | ||
187 | let (subtree, _) = get_token_tree_generated(r#"#![cfg(feature = "baz")]"#); | ||
188 | let cfg_expr = parse_cfg(&subtree); | ||
189 | let mut min_features = vec![]; | ||
190 | required_features(&cfg_expr, &mut min_features); | ||
191 | |||
192 | assert_eq!(min_features, vec![SmolStr::new("baz")]); | ||
193 | |||
194 | let (subtree, _) = | ||
195 | get_token_tree_generated(r#"#![cfg(all(feature = "baz", feature = "foo"))]"#); | ||
196 | let cfg_expr = parse_cfg(&subtree); | ||
197 | |||
198 | let mut min_features = vec![]; | ||
199 | required_features(&cfg_expr, &mut min_features); | ||
200 | assert_eq!(min_features, vec![SmolStr::new("baz"), SmolStr::new("foo")]); | ||
201 | |||
202 | let (subtree, _) = | ||
203 | get_token_tree_generated(r#"#![cfg(any(feature = "baz", feature = "foo", unix))]"#); | ||
204 | let cfg_expr = parse_cfg(&subtree); | ||
205 | |||
206 | let mut min_features = vec![]; | ||
207 | required_features(&cfg_expr, &mut min_features); | ||
208 | assert_eq!(min_features, vec![SmolStr::new("baz")]); | ||
209 | |||
210 | let (subtree, _) = get_token_tree_generated(r#"#![cfg(foo)]"#); | ||
211 | let cfg_expr = parse_cfg(&subtree); | ||
212 | |||
213 | let mut min_features = vec![]; | ||
214 | required_features(&cfg_expr, &mut min_features); | ||
215 | assert!(min_features.is_empty()); | ||
216 | } | ||
217 | } | ||
diff --git a/crates/rust-analyzer/src/cli/load_cargo.rs b/crates/rust-analyzer/src/cli/load_cargo.rs index 8eaf75ff6..c7e86fe0c 100644 --- a/crates/rust-analyzer/src/cli/load_cargo.rs +++ b/crates/rust-analyzer/src/cli/load_cargo.rs | |||
@@ -8,7 +8,8 @@ use crossbeam_channel::{unbounded, Receiver}; | |||
8 | use ra_db::{ExternSourceId, FileId, SourceRootId}; | 8 | use ra_db::{ExternSourceId, FileId, SourceRootId}; |
9 | use ra_ide::{AnalysisChange, AnalysisHost}; | 9 | use ra_ide::{AnalysisChange, AnalysisHost}; |
10 | use ra_project_model::{ | 10 | use ra_project_model::{ |
11 | get_rustc_cfg_options, CargoConfig, PackageRoot, ProcMacroClient, ProjectRoot, ProjectWorkspace, | 11 | get_rustc_cfg_options, CargoConfig, PackageRoot, ProcMacroClient, ProjectManifest, |
12 | ProjectWorkspace, | ||
12 | }; | 13 | }; |
13 | use ra_vfs::{RootEntry, Vfs, VfsChange, VfsTask, Watch}; | 14 | use ra_vfs::{RootEntry, Vfs, VfsChange, VfsTask, Watch}; |
14 | use rustc_hash::{FxHashMap, FxHashSet}; | 15 | use rustc_hash::{FxHashMap, FxHashSet}; |
@@ -28,7 +29,7 @@ pub fn load_cargo( | |||
28 | with_proc_macro: bool, | 29 | with_proc_macro: bool, |
29 | ) -> Result<(AnalysisHost, FxHashMap<SourceRootId, PackageRoot>)> { | 30 | ) -> Result<(AnalysisHost, FxHashMap<SourceRootId, PackageRoot>)> { |
30 | let root = std::env::current_dir()?.join(root); | 31 | let root = std::env::current_dir()?.join(root); |
31 | let root = ProjectRoot::discover_single(&root)?; | 32 | let root = ProjectManifest::discover_single(&root)?; |
32 | let ws = ProjectWorkspace::load( | 33 | let ws = ProjectWorkspace::load( |
33 | root, | 34 | root, |
34 | &CargoConfig { load_out_dirs_from_check, ..Default::default() }, | 35 | &CargoConfig { load_out_dirs_from_check, ..Default::default() }, |
diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs index 0e4412ade..0e5dc56fd 100644 --- a/crates/rust-analyzer/src/config.rs +++ b/crates/rust-analyzer/src/config.rs | |||
@@ -12,14 +12,13 @@ use std::{ffi::OsString, path::PathBuf}; | |||
12 | use lsp_types::ClientCapabilities; | 12 | use lsp_types::ClientCapabilities; |
13 | use ra_flycheck::FlycheckConfig; | 13 | use ra_flycheck::FlycheckConfig; |
14 | use ra_ide::{AssistConfig, CompletionConfig, InlayHintsConfig}; | 14 | use ra_ide::{AssistConfig, CompletionConfig, InlayHintsConfig}; |
15 | use ra_project_model::CargoConfig; | 15 | use ra_project_model::{CargoConfig, JsonProject, ProjectManifest}; |
16 | use serde::Deserialize; | 16 | use serde::Deserialize; |
17 | 17 | ||
18 | #[derive(Debug, Clone)] | 18 | #[derive(Debug, Clone)] |
19 | pub struct Config { | 19 | pub struct Config { |
20 | pub client_caps: ClientCapsConfig, | 20 | pub client_caps: ClientCapsConfig, |
21 | 21 | ||
22 | pub with_sysroot: bool, | ||
23 | pub publish_diagnostics: bool, | 22 | pub publish_diagnostics: bool, |
24 | pub lru_capacity: Option<usize>, | 23 | pub lru_capacity: Option<usize>, |
25 | pub proc_macro_srv: Option<(PathBuf, Vec<OsString>)>, | 24 | pub proc_macro_srv: Option<(PathBuf, Vec<OsString>)>, |
@@ -35,6 +34,27 @@ pub struct Config { | |||
35 | pub assist: AssistConfig, | 34 | pub assist: AssistConfig, |
36 | pub call_info_full: bool, | 35 | pub call_info_full: bool, |
37 | pub lens: LensConfig, | 36 | pub lens: LensConfig, |
37 | |||
38 | pub with_sysroot: bool, | ||
39 | pub linked_projects: Vec<LinkedProject>, | ||
40 | } | ||
41 | |||
42 | #[derive(Debug, Clone)] | ||
43 | pub enum LinkedProject { | ||
44 | ProjectManifest(ProjectManifest), | ||
45 | JsonProject(JsonProject), | ||
46 | } | ||
47 | |||
48 | impl From<ProjectManifest> for LinkedProject { | ||
49 | fn from(v: ProjectManifest) -> Self { | ||
50 | LinkedProject::ProjectManifest(v) | ||
51 | } | ||
52 | } | ||
53 | |||
54 | impl From<JsonProject> for LinkedProject { | ||
55 | fn from(v: JsonProject) -> Self { | ||
56 | LinkedProject::JsonProject(v) | ||
57 | } | ||
38 | } | 58 | } |
39 | 59 | ||
40 | #[derive(Clone, Debug, PartialEq, Eq)] | 60 | #[derive(Clone, Debug, PartialEq, Eq)] |
@@ -122,7 +142,7 @@ impl Default for Config { | |||
122 | check: Some(FlycheckConfig::CargoCommand { | 142 | check: Some(FlycheckConfig::CargoCommand { |
123 | command: "check".to_string(), | 143 | command: "check".to_string(), |
124 | all_targets: true, | 144 | all_targets: true, |
125 | all_features: true, | 145 | all_features: false, |
126 | extra_args: Vec::new(), | 146 | extra_args: Vec::new(), |
127 | }), | 147 | }), |
128 | 148 | ||
@@ -141,6 +161,7 @@ impl Default for Config { | |||
141 | assist: AssistConfig::default(), | 161 | assist: AssistConfig::default(), |
142 | call_info_full: true, | 162 | call_info_full: true, |
143 | lens: LensConfig::default(), | 163 | lens: LensConfig::default(), |
164 | linked_projects: Vec::new(), | ||
144 | } | 165 | } |
145 | } | 166 | } |
146 | } | 167 | } |
@@ -240,6 +261,22 @@ impl Config { | |||
240 | self.lens = LensConfig::NO_LENS; | 261 | self.lens = LensConfig::NO_LENS; |
241 | } | 262 | } |
242 | 263 | ||
264 | if let Some(linked_projects) = get::<Vec<ManifestOrJsonProject>>(value, "/linkedProjects") { | ||
265 | if !linked_projects.is_empty() { | ||
266 | self.linked_projects.clear(); | ||
267 | for linked_project in linked_projects { | ||
268 | let linked_project = match linked_project { | ||
269 | ManifestOrJsonProject::Manifest(it) => match ProjectManifest::from_manifest_file(it) { | ||
270 | Ok(it) => it.into(), | ||
271 | Err(_) => continue, | ||
272 | } | ||
273 | ManifestOrJsonProject::JsonProject(it) => it.into(), | ||
274 | }; | ||
275 | self.linked_projects.push(linked_project); | ||
276 | } | ||
277 | } | ||
278 | } | ||
279 | |||
243 | log::info!("Config::update() = {:#?}", self); | 280 | log::info!("Config::update() = {:#?}", self); |
244 | 281 | ||
245 | fn get<'a, T: Deserialize<'a>>(value: &'a serde_json::Value, pointer: &str) -> Option<T> { | 282 | fn get<'a, T: Deserialize<'a>>(value: &'a serde_json::Value, pointer: &str) -> Option<T> { |
@@ -269,10 +306,8 @@ impl Config { | |||
269 | { | 306 | { |
270 | self.client_caps.hierarchical_symbols = value | 307 | self.client_caps.hierarchical_symbols = value |
271 | } | 308 | } |
272 | if let Some(value) = doc_caps | 309 | if let Some(value) = |
273 | .code_action | 310 | doc_caps.code_action.as_ref().map(|it| it.code_action_literal_support.is_some()) |
274 | .as_ref() | ||
275 | .and_then(|it| Some(it.code_action_literal_support.is_some())) | ||
276 | { | 311 | { |
277 | self.client_caps.code_action_literals = value; | 312 | self.client_caps.code_action_literals = value; |
278 | } | 313 | } |
@@ -305,3 +340,10 @@ impl Config { | |||
305 | } | 340 | } |
306 | } | 341 | } |
307 | } | 342 | } |
343 | |||
344 | #[derive(Deserialize)] | ||
345 | #[serde(untagged)] | ||
346 | enum ManifestOrJsonProject { | ||
347 | Manifest(PathBuf), | ||
348 | JsonProject(JsonProject), | ||
349 | } | ||
diff --git a/crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_rustc_unused_variable.snap b/crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_rustc_unused_variable.snap index 6dd3fcb2e..33b516e26 100644 --- a/crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_rustc_unused_variable.snap +++ b/crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_rustc_unused_variable.snap | |||
@@ -29,7 +29,7 @@ expression: diag | |||
29 | }, | 29 | }, |
30 | }, | 30 | }, |
31 | severity: Some( | 31 | severity: Some( |
32 | Warning, | 32 | Hint, |
33 | ), | 33 | ), |
34 | code: Some( | 34 | code: Some( |
35 | String( | 35 | String( |
diff --git a/crates/rust-analyzer/src/diagnostics/to_proto.rs b/crates/rust-analyzer/src/diagnostics/to_proto.rs index a500d670a..6a6e7b457 100644 --- a/crates/rust-analyzer/src/diagnostics/to_proto.rs +++ b/crates/rust-analyzer/src/diagnostics/to_proto.rs | |||
@@ -183,7 +183,7 @@ pub(crate) fn map_rust_diagnostic_to_lsp( | |||
183 | return Vec::new(); | 183 | return Vec::new(); |
184 | } | 184 | } |
185 | 185 | ||
186 | let severity = map_level_to_severity(rd.level); | 186 | let mut severity = map_level_to_severity(rd.level); |
187 | 187 | ||
188 | let mut source = String::from("rustc"); | 188 | let mut source = String::from("rustc"); |
189 | let mut code = rd.code.as_ref().map(|c| c.code.clone()); | 189 | let mut code = rd.code.as_ref().map(|c| c.code.clone()); |
@@ -225,6 +225,7 @@ pub(crate) fn map_rust_diagnostic_to_lsp( | |||
225 | } | 225 | } |
226 | 226 | ||
227 | if is_unused_or_unnecessary(rd) { | 227 | if is_unused_or_unnecessary(rd) { |
228 | severity = Some(DiagnosticSeverity::Hint); | ||
228 | tags.push(DiagnosticTag::Unnecessary); | 229 | tags.push(DiagnosticTag::Unnecessary); |
229 | } | 230 | } |
230 | 231 | ||
diff --git a/crates/rust-analyzer/src/from_proto.rs b/crates/rust-analyzer/src/from_proto.rs index 4bb16a496..206673829 100644 --- a/crates/rust-analyzer/src/from_proto.rs +++ b/crates/rust-analyzer/src/from_proto.rs | |||
@@ -3,7 +3,7 @@ use ra_db::{FileId, FilePosition, FileRange}; | |||
3 | use ra_ide::{LineCol, LineIndex}; | 3 | use ra_ide::{LineCol, LineIndex}; |
4 | use ra_syntax::{TextRange, TextSize}; | 4 | use ra_syntax::{TextRange, TextSize}; |
5 | 5 | ||
6 | use crate::{world::WorldSnapshot, Result}; | 6 | use crate::{global_state::GlobalStateSnapshot, Result}; |
7 | 7 | ||
8 | pub(crate) fn offset(line_index: &LineIndex, position: lsp_types::Position) -> TextSize { | 8 | pub(crate) fn offset(line_index: &LineIndex, position: lsp_types::Position) -> TextSize { |
9 | let line_col = LineCol { line: position.line as u32, col_utf16: position.character as u32 }; | 9 | let line_col = LineCol { line: position.line as u32, col_utf16: position.character as u32 }; |
@@ -16,12 +16,12 @@ pub(crate) fn text_range(line_index: &LineIndex, range: lsp_types::Range) -> Tex | |||
16 | TextRange::new(start, end) | 16 | TextRange::new(start, end) |
17 | } | 17 | } |
18 | 18 | ||
19 | pub(crate) fn file_id(world: &WorldSnapshot, url: &lsp_types::Url) -> Result<FileId> { | 19 | pub(crate) fn file_id(world: &GlobalStateSnapshot, url: &lsp_types::Url) -> Result<FileId> { |
20 | world.uri_to_file_id(url) | 20 | world.uri_to_file_id(url) |
21 | } | 21 | } |
22 | 22 | ||
23 | pub(crate) fn file_position( | 23 | pub(crate) fn file_position( |
24 | world: &WorldSnapshot, | 24 | world: &GlobalStateSnapshot, |
25 | tdpp: lsp_types::TextDocumentPositionParams, | 25 | tdpp: lsp_types::TextDocumentPositionParams, |
26 | ) -> Result<FilePosition> { | 26 | ) -> Result<FilePosition> { |
27 | let file_id = file_id(world, &tdpp.text_document.uri)?; | 27 | let file_id = file_id(world, &tdpp.text_document.uri)?; |
@@ -31,7 +31,7 @@ pub(crate) fn file_position( | |||
31 | } | 31 | } |
32 | 32 | ||
33 | pub(crate) fn file_range( | 33 | pub(crate) fn file_range( |
34 | world: &WorldSnapshot, | 34 | world: &GlobalStateSnapshot, |
35 | text_document_identifier: lsp_types::TextDocumentIdentifier, | 35 | text_document_identifier: lsp_types::TextDocumentIdentifier, |
36 | range: lsp_types::Range, | 36 | range: lsp_types::Range, |
37 | ) -> Result<FileRange> { | 37 | ) -> Result<FileRange> { |
diff --git a/crates/rust-analyzer/src/world.rs b/crates/rust-analyzer/src/global_state.rs index 367272925..0bebb5bf6 100644 --- a/crates/rust-analyzer/src/world.rs +++ b/crates/rust-analyzer/src/global_state.rs | |||
@@ -50,15 +50,15 @@ fn create_flycheck(workspaces: &[ProjectWorkspace], config: &FlycheckConfig) -> | |||
50 | }) | 50 | }) |
51 | } | 51 | } |
52 | 52 | ||
53 | /// `WorldState` is the primary mutable state of the language server | 53 | /// `GlobalState` is the primary mutable state of the language server |
54 | /// | 54 | /// |
55 | /// The most interesting components are `vfs`, which stores a consistent | 55 | /// The most interesting components are `vfs`, which stores a consistent |
56 | /// snapshot of the file systems, and `analysis_host`, which stores our | 56 | /// snapshot of the file systems, and `analysis_host`, which stores our |
57 | /// incremental salsa database. | 57 | /// incremental salsa database. |
58 | #[derive(Debug)] | 58 | #[derive(Debug)] |
59 | pub struct WorldState { | 59 | pub struct GlobalState { |
60 | pub config: Config, | 60 | pub config: Config, |
61 | pub roots: Vec<PathBuf>, | 61 | pub local_roots: Vec<PathBuf>, |
62 | pub workspaces: Arc<Vec<ProjectWorkspace>>, | 62 | pub workspaces: Arc<Vec<ProjectWorkspace>>, |
63 | pub analysis_host: AnalysisHost, | 63 | pub analysis_host: AnalysisHost, |
64 | pub vfs: Arc<RwLock<Vfs>>, | 64 | pub vfs: Arc<RwLock<Vfs>>, |
@@ -70,7 +70,7 @@ pub struct WorldState { | |||
70 | } | 70 | } |
71 | 71 | ||
72 | /// An immutable snapshot of the world's state at a point in time. | 72 | /// An immutable snapshot of the world's state at a point in time. |
73 | pub struct WorldSnapshot { | 73 | pub struct GlobalStateSnapshot { |
74 | pub config: Config, | 74 | pub config: Config, |
75 | pub workspaces: Arc<Vec<ProjectWorkspace>>, | 75 | pub workspaces: Arc<Vec<ProjectWorkspace>>, |
76 | pub analysis: Analysis, | 76 | pub analysis: Analysis, |
@@ -79,20 +79,20 @@ pub struct WorldSnapshot { | |||
79 | vfs: Arc<RwLock<Vfs>>, | 79 | vfs: Arc<RwLock<Vfs>>, |
80 | } | 80 | } |
81 | 81 | ||
82 | impl WorldState { | 82 | impl GlobalState { |
83 | pub fn new( | 83 | pub fn new( |
84 | folder_roots: Vec<PathBuf>, | ||
85 | workspaces: Vec<ProjectWorkspace>, | 84 | workspaces: Vec<ProjectWorkspace>, |
86 | lru_capacity: Option<usize>, | 85 | lru_capacity: Option<usize>, |
87 | exclude_globs: &[Glob], | 86 | exclude_globs: &[Glob], |
88 | watch: Watch, | 87 | watch: Watch, |
89 | config: Config, | 88 | config: Config, |
90 | ) -> WorldState { | 89 | ) -> GlobalState { |
91 | let mut change = AnalysisChange::new(); | 90 | let mut change = AnalysisChange::new(); |
92 | 91 | ||
93 | let extern_dirs: FxHashSet<_> = | 92 | let extern_dirs: FxHashSet<_> = |
94 | workspaces.iter().flat_map(ProjectWorkspace::out_dirs).collect(); | 93 | workspaces.iter().flat_map(ProjectWorkspace::out_dirs).collect(); |
95 | 94 | ||
95 | let mut local_roots = Vec::new(); | ||
96 | let roots: Vec<_> = { | 96 | let roots: Vec<_> = { |
97 | let create_filter = |is_member| { | 97 | let create_filter = |is_member| { |
98 | RustPackageFilterBuilder::default() | 98 | RustPackageFilterBuilder::default() |
@@ -100,12 +100,16 @@ impl WorldState { | |||
100 | .exclude(exclude_globs.iter().cloned()) | 100 | .exclude(exclude_globs.iter().cloned()) |
101 | .into_vfs_filter() | 101 | .into_vfs_filter() |
102 | }; | 102 | }; |
103 | folder_roots | 103 | workspaces |
104 | .iter() | 104 | .iter() |
105 | .map(|path| RootEntry::new(path.clone(), create_filter(true))) | 105 | .flat_map(ProjectWorkspace::to_roots) |
106 | .chain(workspaces.iter().flat_map(ProjectWorkspace::to_roots).map(|pkg_root| { | 106 | .map(|pkg_root| { |
107 | RootEntry::new(pkg_root.path().to_owned(), create_filter(pkg_root.is_member())) | 107 | let path = pkg_root.path().to_owned(); |
108 | })) | 108 | if pkg_root.is_member() { |
109 | local_roots.push(path.clone()); | ||
110 | } | ||
111 | RootEntry::new(path, create_filter(pkg_root.is_member())) | ||
112 | }) | ||
109 | .chain( | 113 | .chain( |
110 | extern_dirs | 114 | extern_dirs |
111 | .iter() | 115 | .iter() |
@@ -121,7 +125,7 @@ impl WorldState { | |||
121 | let mut extern_source_roots = FxHashMap::default(); | 125 | let mut extern_source_roots = FxHashMap::default(); |
122 | for r in vfs_roots { | 126 | for r in vfs_roots { |
123 | let vfs_root_path = vfs.root2path(r); | 127 | let vfs_root_path = vfs.root2path(r); |
124 | let is_local = folder_roots.iter().any(|it| vfs_root_path.starts_with(it)); | 128 | let is_local = local_roots.iter().any(|it| vfs_root_path.starts_with(it)); |
125 | change.add_root(SourceRootId(r.0), is_local); | 129 | change.add_root(SourceRootId(r.0), is_local); |
126 | change.set_debug_root_path(SourceRootId(r.0), vfs_root_path.display().to_string()); | 130 | change.set_debug_root_path(SourceRootId(r.0), vfs_root_path.display().to_string()); |
127 | 131 | ||
@@ -176,9 +180,9 @@ impl WorldState { | |||
176 | 180 | ||
177 | let mut analysis_host = AnalysisHost::new(lru_capacity); | 181 | let mut analysis_host = AnalysisHost::new(lru_capacity); |
178 | analysis_host.apply_change(change); | 182 | analysis_host.apply_change(change); |
179 | WorldState { | 183 | GlobalState { |
180 | config, | 184 | config, |
181 | roots: folder_roots, | 185 | local_roots, |
182 | workspaces: Arc::new(workspaces), | 186 | workspaces: Arc::new(workspaces), |
183 | analysis_host, | 187 | analysis_host, |
184 | vfs: Arc::new(RwLock::new(vfs)), | 188 | vfs: Arc::new(RwLock::new(vfs)), |
@@ -216,7 +220,7 @@ impl WorldState { | |||
216 | match c { | 220 | match c { |
217 | VfsChange::AddRoot { root, files } => { | 221 | VfsChange::AddRoot { root, files } => { |
218 | let root_path = self.vfs.read().root2path(root); | 222 | let root_path = self.vfs.read().root2path(root); |
219 | let is_local = self.roots.iter().any(|r| root_path.starts_with(r)); | 223 | let is_local = self.local_roots.iter().any(|r| root_path.starts_with(r)); |
220 | if is_local { | 224 | if is_local { |
221 | *roots_scanned += 1; | 225 | *roots_scanned += 1; |
222 | for (file, path, text) in files { | 226 | for (file, path, text) in files { |
@@ -251,8 +255,8 @@ impl WorldState { | |||
251 | self.analysis_host.apply_change(change); | 255 | self.analysis_host.apply_change(change); |
252 | } | 256 | } |
253 | 257 | ||
254 | pub fn snapshot(&self) -> WorldSnapshot { | 258 | pub fn snapshot(&self) -> GlobalStateSnapshot { |
255 | WorldSnapshot { | 259 | GlobalStateSnapshot { |
256 | config: self.config.clone(), | 260 | config: self.config.clone(), |
257 | workspaces: Arc::clone(&self.workspaces), | 261 | workspaces: Arc::clone(&self.workspaces), |
258 | analysis: self.analysis_host.analysis(), | 262 | analysis: self.analysis_host.analysis(), |
@@ -275,7 +279,7 @@ impl WorldState { | |||
275 | } | 279 | } |
276 | } | 280 | } |
277 | 281 | ||
278 | impl WorldSnapshot { | 282 | impl GlobalStateSnapshot { |
279 | pub fn analysis(&self) -> &Analysis { | 283 | pub fn analysis(&self) -> &Analysis { |
280 | &self.analysis | 284 | &self.analysis |
281 | } | 285 | } |
diff --git a/crates/rust-analyzer/src/lib.rs b/crates/rust-analyzer/src/lib.rs index 57d0e9218..609cb69d3 100644 --- a/crates/rust-analyzer/src/lib.rs +++ b/crates/rust-analyzer/src/lib.rs | |||
@@ -26,7 +26,7 @@ mod main_loop; | |||
26 | mod markdown; | 26 | mod markdown; |
27 | pub mod lsp_ext; | 27 | pub mod lsp_ext; |
28 | pub mod config; | 28 | pub mod config; |
29 | mod world; | 29 | mod global_state; |
30 | mod diagnostics; | 30 | mod diagnostics; |
31 | mod semantic_tokens; | 31 | mod semantic_tokens; |
32 | 32 | ||
diff --git a/crates/rust-analyzer/src/lsp_ext.rs b/crates/rust-analyzer/src/lsp_ext.rs index c25d90a50..ec24ce5e0 100644 --- a/crates/rust-analyzer/src/lsp_ext.rs +++ b/crates/rust-analyzer/src/lsp_ext.rs | |||
@@ -3,8 +3,7 @@ | |||
3 | use std::{collections::HashMap, path::PathBuf}; | 3 | use std::{collections::HashMap, path::PathBuf}; |
4 | 4 | ||
5 | use lsp_types::request::Request; | 5 | use lsp_types::request::Request; |
6 | use lsp_types::{Location, Position, Range, TextDocumentIdentifier}; | 6 | use lsp_types::{Position, Range, TextDocumentIdentifier}; |
7 | use rustc_hash::FxHashMap; | ||
8 | use serde::{Deserialize, Serialize}; | 7 | use serde::{Deserialize, Serialize}; |
9 | 8 | ||
10 | pub enum AnalyzerStatus {} | 9 | pub enum AnalyzerStatus {} |
@@ -38,13 +37,6 @@ pub struct SyntaxTreeParams { | |||
38 | pub range: Option<Range>, | 37 | pub range: Option<Range>, |
39 | } | 38 | } |
40 | 39 | ||
41 | #[derive(Deserialize, Serialize, Debug)] | ||
42 | #[serde(rename_all = "camelCase")] | ||
43 | pub struct ExpandedMacro { | ||
44 | pub name: String, | ||
45 | pub expansion: String, | ||
46 | } | ||
47 | |||
48 | pub enum ExpandMacro {} | 40 | pub enum ExpandMacro {} |
49 | 41 | ||
50 | impl Request for ExpandMacro { | 42 | impl Request for ExpandMacro { |
@@ -57,30 +49,37 @@ impl Request for ExpandMacro { | |||
57 | #[serde(rename_all = "camelCase")] | 49 | #[serde(rename_all = "camelCase")] |
58 | pub struct ExpandMacroParams { | 50 | pub struct ExpandMacroParams { |
59 | pub text_document: TextDocumentIdentifier, | 51 | pub text_document: TextDocumentIdentifier, |
60 | pub position: Option<Position>, | 52 | pub position: Position, |
61 | } | 53 | } |
62 | 54 | ||
63 | pub enum FindMatchingBrace {} | 55 | #[derive(Deserialize, Serialize, Debug)] |
56 | #[serde(rename_all = "camelCase")] | ||
57 | pub struct ExpandedMacro { | ||
58 | pub name: String, | ||
59 | pub expansion: String, | ||
60 | } | ||
61 | |||
62 | pub enum MatchingBrace {} | ||
64 | 63 | ||
65 | impl Request for FindMatchingBrace { | 64 | impl Request for MatchingBrace { |
66 | type Params = FindMatchingBraceParams; | 65 | type Params = MatchingBraceParams; |
67 | type Result = Vec<Position>; | 66 | type Result = Vec<Position>; |
68 | const METHOD: &'static str = "rust-analyzer/findMatchingBrace"; | 67 | const METHOD: &'static str = "experimental/matchingBrace"; |
69 | } | 68 | } |
70 | 69 | ||
71 | #[derive(Deserialize, Serialize, Debug)] | 70 | #[derive(Deserialize, Serialize, Debug)] |
72 | #[serde(rename_all = "camelCase")] | 71 | #[serde(rename_all = "camelCase")] |
73 | pub struct FindMatchingBraceParams { | 72 | pub struct MatchingBraceParams { |
74 | pub text_document: TextDocumentIdentifier, | 73 | pub text_document: TextDocumentIdentifier, |
75 | pub offsets: Vec<Position>, | 74 | pub positions: Vec<Position>, |
76 | } | 75 | } |
77 | 76 | ||
78 | pub enum ParentModule {} | 77 | pub enum ParentModule {} |
79 | 78 | ||
80 | impl Request for ParentModule { | 79 | impl Request for ParentModule { |
81 | type Params = lsp_types::TextDocumentPositionParams; | 80 | type Params = lsp_types::TextDocumentPositionParams; |
82 | type Result = Vec<Location>; | 81 | type Result = Option<lsp_types::GotoDefinitionResponse>; |
83 | const METHOD: &'static str = "rust-analyzer/parentModule"; | 82 | const METHOD: &'static str = "experimental/parentModule"; |
84 | } | 83 | } |
85 | 84 | ||
86 | pub enum JoinLines {} | 85 | pub enum JoinLines {} |
@@ -102,8 +101,8 @@ pub enum OnEnter {} | |||
102 | 101 | ||
103 | impl Request for OnEnter { | 102 | impl Request for OnEnter { |
104 | type Params = lsp_types::TextDocumentPositionParams; | 103 | type Params = lsp_types::TextDocumentPositionParams; |
105 | type Result = Option<SnippetWorkspaceEdit>; | 104 | type Result = Option<Vec<SnippetTextEdit>>; |
106 | const METHOD: &'static str = "rust-analyzer/onEnter"; | 105 | const METHOD: &'static str = "experimental/onEnter"; |
107 | } | 106 | } |
108 | 107 | ||
109 | pub enum Runnables {} | 108 | pub enum Runnables {} |
@@ -111,7 +110,7 @@ pub enum Runnables {} | |||
111 | impl Request for Runnables { | 110 | impl Request for Runnables { |
112 | type Params = RunnablesParams; | 111 | type Params = RunnablesParams; |
113 | type Result = Vec<Runnable>; | 112 | type Result = Vec<Runnable>; |
114 | const METHOD: &'static str = "rust-analyzer/runnables"; | 113 | const METHOD: &'static str = "experimental/runnables"; |
115 | } | 114 | } |
116 | 115 | ||
117 | #[derive(Serialize, Deserialize, Debug)] | 116 | #[derive(Serialize, Deserialize, Debug)] |
@@ -124,13 +123,28 @@ pub struct RunnablesParams { | |||
124 | #[derive(Deserialize, Serialize, Debug)] | 123 | #[derive(Deserialize, Serialize, Debug)] |
125 | #[serde(rename_all = "camelCase")] | 124 | #[serde(rename_all = "camelCase")] |
126 | pub struct Runnable { | 125 | pub struct Runnable { |
127 | pub range: Range, | ||
128 | pub label: String, | 126 | pub label: String, |
129 | pub bin: String, | 127 | #[serde(skip_serializing_if = "Option::is_none")] |
130 | pub args: Vec<String>, | 128 | pub location: Option<lsp_types::LocationLink>, |
131 | pub extra_args: Vec<String>, | 129 | pub kind: RunnableKind, |
132 | pub env: FxHashMap<String, String>, | 130 | pub args: CargoRunnable, |
133 | pub cwd: Option<PathBuf>, | 131 | } |
132 | |||
133 | #[derive(Serialize, Deserialize, Debug)] | ||
134 | #[serde(rename_all = "lowercase")] | ||
135 | pub enum RunnableKind { | ||
136 | Cargo, | ||
137 | } | ||
138 | |||
139 | #[derive(Deserialize, Serialize, Debug)] | ||
140 | #[serde(rename_all = "camelCase")] | ||
141 | pub struct CargoRunnable { | ||
142 | #[serde(skip_serializing_if = "Option::is_none")] | ||
143 | pub workspace_root: Option<PathBuf>, | ||
144 | // command, --package and --lib stuff | ||
145 | pub cargo_args: Vec<String>, | ||
146 | // stuff after -- | ||
147 | pub executable_args: Vec<String>, | ||
134 | } | 148 | } |
135 | 149 | ||
136 | pub enum InlayHints {} | 150 | pub enum InlayHints {} |
diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs index 87795fffb..1f8f6b978 100644 --- a/crates/rust-analyzer/src/main_loop.rs +++ b/crates/rust-analyzer/src/main_loop.rs | |||
@@ -12,13 +12,11 @@ use std::{ | |||
12 | fmt, | 12 | fmt, |
13 | ops::Range, | 13 | ops::Range, |
14 | panic, | 14 | panic, |
15 | path::PathBuf, | ||
16 | sync::Arc, | 15 | sync::Arc, |
17 | time::{Duration, Instant}, | 16 | time::{Duration, Instant}, |
18 | }; | 17 | }; |
19 | 18 | ||
20 | use crossbeam_channel::{never, select, unbounded, RecvError, Sender}; | 19 | use crossbeam_channel::{never, select, unbounded, RecvError, Sender}; |
21 | use itertools::Itertools; | ||
22 | use lsp_server::{Connection, ErrorCode, Message, Notification, Request, RequestId, Response}; | 20 | use lsp_server::{Connection, ErrorCode, Message, Notification, Request, RequestId, Response}; |
23 | use lsp_types::{ | 21 | use lsp_types::{ |
24 | DidChangeTextDocumentParams, NumberOrString, TextDocumentContentChangeEvent, WorkDoneProgress, | 22 | DidChangeTextDocumentParams, NumberOrString, TextDocumentContentChangeEvent, WorkDoneProgress, |
@@ -36,14 +34,15 @@ use serde::{de::DeserializeOwned, Serialize}; | |||
36 | use threadpool::ThreadPool; | 34 | use threadpool::ThreadPool; |
37 | 35 | ||
38 | use crate::{ | 36 | use crate::{ |
39 | config::{Config, FilesWatcher}, | 37 | config::{Config, FilesWatcher, LinkedProject}, |
40 | diagnostics::{to_proto::url_from_path_with_drive_lowercasing, DiagnosticTask}, | 38 | diagnostics::{to_proto::url_from_path_with_drive_lowercasing, DiagnosticTask}, |
41 | from_proto, lsp_ext, | 39 | from_proto, |
40 | global_state::{GlobalState, GlobalStateSnapshot}, | ||
41 | lsp_ext, | ||
42 | main_loop::{ | 42 | main_loop::{ |
43 | pending_requests::{PendingRequest, PendingRequests}, | 43 | pending_requests::{PendingRequest, PendingRequests}, |
44 | subscriptions::Subscriptions, | 44 | subscriptions::Subscriptions, |
45 | }, | 45 | }, |
46 | world::{WorldSnapshot, WorldState}, | ||
47 | Result, | 46 | Result, |
48 | }; | 47 | }; |
49 | 48 | ||
@@ -69,7 +68,7 @@ impl fmt::Display for LspError { | |||
69 | 68 | ||
70 | impl Error for LspError {} | 69 | impl Error for LspError {} |
71 | 70 | ||
72 | pub fn main_loop(ws_roots: Vec<PathBuf>, config: Config, connection: Connection) -> Result<()> { | 71 | pub fn main_loop(config: Config, connection: Connection) -> Result<()> { |
73 | log::info!("initial config: {:#?}", config); | 72 | log::info!("initial config: {:#?}", config); |
74 | 73 | ||
75 | // Windows scheduler implements priority boosts: if thread waits for an | 74 | // Windows scheduler implements priority boosts: if thread waits for an |
@@ -92,43 +91,37 @@ pub fn main_loop(ws_roots: Vec<PathBuf>, config: Config, connection: Connection) | |||
92 | } | 91 | } |
93 | 92 | ||
94 | let mut loop_state = LoopState::default(); | 93 | let mut loop_state = LoopState::default(); |
95 | let mut world_state = { | 94 | let mut global_state = { |
96 | let workspaces = { | 95 | let workspaces = { |
97 | // FIXME: support dynamic workspace loading. | 96 | if config.linked_projects.is_empty() && config.notifications.cargo_toml_not_found { |
98 | let project_roots: FxHashSet<_> = ws_roots | ||
99 | .iter() | ||
100 | .filter_map(|it| ra_project_model::ProjectRoot::discover(it).ok()) | ||
101 | .flatten() | ||
102 | .collect(); | ||
103 | |||
104 | if project_roots.is_empty() && config.notifications.cargo_toml_not_found { | ||
105 | show_message( | 97 | show_message( |
106 | lsp_types::MessageType::Error, | 98 | lsp_types::MessageType::Error, |
107 | format!( | 99 | "rust-analyzer failed to discover workspace".to_string(), |
108 | "rust-analyzer failed to discover workspace, no Cargo.toml found, dirs searched: {}", | ||
109 | ws_roots.iter().format_with(", ", |it, f| f(&it.display())) | ||
110 | ), | ||
111 | &connection.sender, | 100 | &connection.sender, |
112 | ); | 101 | ); |
113 | }; | 102 | }; |
114 | 103 | ||
115 | project_roots | 104 | config |
116 | .into_iter() | 105 | .linked_projects |
117 | .filter_map(|root| { | 106 | .iter() |
118 | ra_project_model::ProjectWorkspace::load( | 107 | .filter_map(|project| match project { |
119 | root, | 108 | LinkedProject::ProjectManifest(manifest) => { |
120 | &config.cargo, | 109 | ra_project_model::ProjectWorkspace::load( |
121 | config.with_sysroot, | 110 | manifest.clone(), |
122 | ) | 111 | &config.cargo, |
123 | .map_err(|err| { | 112 | config.with_sysroot, |
124 | log::error!("failed to load workspace: {:#}", err); | 113 | ) |
125 | show_message( | 114 | .map_err(|err| { |
126 | lsp_types::MessageType::Error, | 115 | log::error!("failed to load workspace: {:#}", err); |
127 | format!("rust-analyzer failed to load workspace: {:#}", err), | 116 | show_message( |
128 | &connection.sender, | 117 | lsp_types::MessageType::Error, |
129 | ); | 118 | format!("rust-analyzer failed to load workspace: {:#}", err), |
130 | }) | 119 | &connection.sender, |
131 | .ok() | 120 | ); |
121 | }) | ||
122 | .ok() | ||
123 | } | ||
124 | LinkedProject::JsonProject(it) => Some(it.clone().into()), | ||
132 | }) | 125 | }) |
133 | .collect::<Vec<_>>() | 126 | .collect::<Vec<_>>() |
134 | }; | 127 | }; |
@@ -163,8 +156,7 @@ pub fn main_loop(ws_roots: Vec<PathBuf>, config: Config, connection: Connection) | |||
163 | connection.sender.send(request.into()).unwrap(); | 156 | connection.sender.send(request.into()).unwrap(); |
164 | } | 157 | } |
165 | 158 | ||
166 | WorldState::new( | 159 | GlobalState::new( |
167 | ws_roots, | ||
168 | workspaces, | 160 | workspaces, |
169 | config.lru_capacity, | 161 | config.lru_capacity, |
170 | &globs, | 162 | &globs, |
@@ -173,7 +165,7 @@ pub fn main_loop(ws_roots: Vec<PathBuf>, config: Config, connection: Connection) | |||
173 | ) | 165 | ) |
174 | }; | 166 | }; |
175 | 167 | ||
176 | loop_state.roots_total = world_state.vfs.read().n_roots(); | 168 | loop_state.roots_total = global_state.vfs.read().n_roots(); |
177 | 169 | ||
178 | let pool = ThreadPool::default(); | 170 | let pool = ThreadPool::default(); |
179 | let (task_sender, task_receiver) = unbounded::<Task>(); | 171 | let (task_sender, task_receiver) = unbounded::<Task>(); |
@@ -191,12 +183,12 @@ pub fn main_loop(ws_roots: Vec<PathBuf>, config: Config, connection: Connection) | |||
191 | Err(RecvError) => return Err("client exited without shutdown".into()), | 183 | Err(RecvError) => return Err("client exited without shutdown".into()), |
192 | }, | 184 | }, |
193 | recv(task_receiver) -> task => Event::Task(task.unwrap()), | 185 | recv(task_receiver) -> task => Event::Task(task.unwrap()), |
194 | recv(world_state.task_receiver) -> task => match task { | 186 | recv(global_state.task_receiver) -> task => match task { |
195 | Ok(task) => Event::Vfs(task), | 187 | Ok(task) => Event::Vfs(task), |
196 | Err(RecvError) => return Err("vfs died".into()), | 188 | Err(RecvError) => return Err("vfs died".into()), |
197 | }, | 189 | }, |
198 | recv(libdata_receiver) -> data => Event::Lib(data.unwrap()), | 190 | recv(libdata_receiver) -> data => Event::Lib(data.unwrap()), |
199 | recv(world_state.flycheck.as_ref().map_or(&never(), |it| &it.task_recv)) -> task => match task { | 191 | recv(global_state.flycheck.as_ref().map_or(&never(), |it| &it.task_recv)) -> task => match task { |
200 | Ok(task) => Event::CheckWatcher(task), | 192 | Ok(task) => Event::CheckWatcher(task), |
201 | Err(RecvError) => return Err("check watcher died".into()), | 193 | Err(RecvError) => return Err("check watcher died".into()), |
202 | } | 194 | } |
@@ -211,16 +203,16 @@ pub fn main_loop(ws_roots: Vec<PathBuf>, config: Config, connection: Connection) | |||
211 | &task_sender, | 203 | &task_sender, |
212 | &libdata_sender, | 204 | &libdata_sender, |
213 | &connection, | 205 | &connection, |
214 | &mut world_state, | 206 | &mut global_state, |
215 | &mut loop_state, | 207 | &mut loop_state, |
216 | event, | 208 | event, |
217 | )?; | 209 | )?; |
218 | } | 210 | } |
219 | } | 211 | } |
220 | world_state.analysis_host.request_cancellation(); | 212 | global_state.analysis_host.request_cancellation(); |
221 | log::info!("waiting for tasks to finish..."); | 213 | log::info!("waiting for tasks to finish..."); |
222 | task_receiver.into_iter().for_each(|task| { | 214 | task_receiver.into_iter().for_each(|task| { |
223 | on_task(task, &connection.sender, &mut loop_state.pending_requests, &mut world_state) | 215 | on_task(task, &connection.sender, &mut loop_state.pending_requests, &mut global_state) |
224 | }); | 216 | }); |
225 | libdata_receiver.into_iter().for_each(drop); | 217 | libdata_receiver.into_iter().for_each(drop); |
226 | log::info!("...tasks have finished"); | 218 | log::info!("...tasks have finished"); |
@@ -229,7 +221,7 @@ pub fn main_loop(ws_roots: Vec<PathBuf>, config: Config, connection: Connection) | |||
229 | drop(pool); | 221 | drop(pool); |
230 | log::info!("...threadpool has finished"); | 222 | log::info!("...threadpool has finished"); |
231 | 223 | ||
232 | let vfs = Arc::try_unwrap(world_state.vfs).expect("all snapshots should be dead"); | 224 | let vfs = Arc::try_unwrap(global_state.vfs).expect("all snapshots should be dead"); |
233 | drop(vfs); | 225 | drop(vfs); |
234 | 226 | ||
235 | Ok(()) | 227 | Ok(()) |
@@ -320,7 +312,7 @@ fn loop_turn( | |||
320 | task_sender: &Sender<Task>, | 312 | task_sender: &Sender<Task>, |
321 | libdata_sender: &Sender<LibraryData>, | 313 | libdata_sender: &Sender<LibraryData>, |
322 | connection: &Connection, | 314 | connection: &Connection, |
323 | world_state: &mut WorldState, | 315 | global_state: &mut GlobalState, |
324 | loop_state: &mut LoopState, | 316 | loop_state: &mut LoopState, |
325 | event: Event, | 317 | event: Event, |
326 | ) -> Result<()> { | 318 | ) -> Result<()> { |
@@ -336,22 +328,22 @@ fn loop_turn( | |||
336 | 328 | ||
337 | match event { | 329 | match event { |
338 | Event::Task(task) => { | 330 | Event::Task(task) => { |
339 | on_task(task, &connection.sender, &mut loop_state.pending_requests, world_state); | 331 | on_task(task, &connection.sender, &mut loop_state.pending_requests, global_state); |
340 | world_state.maybe_collect_garbage(); | 332 | global_state.maybe_collect_garbage(); |
341 | } | 333 | } |
342 | Event::Vfs(task) => { | 334 | Event::Vfs(task) => { |
343 | world_state.vfs.write().handle_task(task); | 335 | global_state.vfs.write().handle_task(task); |
344 | } | 336 | } |
345 | Event::Lib(lib) => { | 337 | Event::Lib(lib) => { |
346 | world_state.add_lib(lib); | 338 | global_state.add_lib(lib); |
347 | world_state.maybe_collect_garbage(); | 339 | global_state.maybe_collect_garbage(); |
348 | loop_state.in_flight_libraries -= 1; | 340 | loop_state.in_flight_libraries -= 1; |
349 | loop_state.roots_scanned += 1; | 341 | loop_state.roots_scanned += 1; |
350 | } | 342 | } |
351 | Event::CheckWatcher(task) => on_check_task(task, world_state, task_sender)?, | 343 | Event::CheckWatcher(task) => on_check_task(task, global_state, task_sender)?, |
352 | Event::Msg(msg) => match msg { | 344 | Event::Msg(msg) => match msg { |
353 | Message::Request(req) => on_request( | 345 | Message::Request(req) => on_request( |
354 | world_state, | 346 | global_state, |
355 | &mut loop_state.pending_requests, | 347 | &mut loop_state.pending_requests, |
356 | pool, | 348 | pool, |
357 | task_sender, | 349 | task_sender, |
@@ -360,7 +352,7 @@ fn loop_turn( | |||
360 | req, | 352 | req, |
361 | )?, | 353 | )?, |
362 | Message::Notification(not) => { | 354 | Message::Notification(not) => { |
363 | on_notification(&connection.sender, world_state, loop_state, not)?; | 355 | on_notification(&connection.sender, global_state, loop_state, not)?; |
364 | } | 356 | } |
365 | Message::Response(resp) => { | 357 | Message::Response(resp) => { |
366 | let removed = loop_state.pending_responses.remove(&resp.id); | 358 | let removed = loop_state.pending_responses.remove(&resp.id); |
@@ -379,9 +371,9 @@ fn loop_turn( | |||
379 | } | 371 | } |
380 | (None, Some(configs)) => { | 372 | (None, Some(configs)) => { |
381 | if let Some(new_config) = configs.get(0) { | 373 | if let Some(new_config) = configs.get(0) { |
382 | let mut config = world_state.config.clone(); | 374 | let mut config = global_state.config.clone(); |
383 | config.update(&new_config); | 375 | config.update(&new_config); |
384 | world_state.update_configuration(config); | 376 | global_state.update_configuration(config); |
385 | } | 377 | } |
386 | } | 378 | } |
387 | (None, None) => { | 379 | (None, None) => { |
@@ -394,7 +386,7 @@ fn loop_turn( | |||
394 | }; | 386 | }; |
395 | 387 | ||
396 | let mut state_changed = false; | 388 | let mut state_changed = false; |
397 | if let Some(changes) = world_state.process_changes(&mut loop_state.roots_scanned) { | 389 | if let Some(changes) = global_state.process_changes(&mut loop_state.roots_scanned) { |
398 | state_changed = true; | 390 | state_changed = true; |
399 | loop_state.pending_libraries.extend(changes); | 391 | loop_state.pending_libraries.extend(changes); |
400 | } | 392 | } |
@@ -416,7 +408,7 @@ fn loop_turn( | |||
416 | } | 408 | } |
417 | 409 | ||
418 | let show_progress = | 410 | let show_progress = |
419 | !loop_state.workspace_loaded && world_state.config.client_caps.work_done_progress; | 411 | !loop_state.workspace_loaded && global_state.config.client_caps.work_done_progress; |
420 | 412 | ||
421 | if !loop_state.workspace_loaded | 413 | if !loop_state.workspace_loaded |
422 | && loop_state.roots_scanned == loop_state.roots_total | 414 | && loop_state.roots_scanned == loop_state.roots_total |
@@ -425,7 +417,7 @@ fn loop_turn( | |||
425 | { | 417 | { |
426 | state_changed = true; | 418 | state_changed = true; |
427 | loop_state.workspace_loaded = true; | 419 | loop_state.workspace_loaded = true; |
428 | if let Some(flycheck) = &world_state.flycheck { | 420 | if let Some(flycheck) = &global_state.flycheck { |
429 | flycheck.update(); | 421 | flycheck.update(); |
430 | } | 422 | } |
431 | } | 423 | } |
@@ -437,13 +429,13 @@ fn loop_turn( | |||
437 | if state_changed && loop_state.workspace_loaded { | 429 | if state_changed && loop_state.workspace_loaded { |
438 | update_file_notifications_on_threadpool( | 430 | update_file_notifications_on_threadpool( |
439 | pool, | 431 | pool, |
440 | world_state.snapshot(), | 432 | global_state.snapshot(), |
441 | task_sender.clone(), | 433 | task_sender.clone(), |
442 | loop_state.subscriptions.subscriptions(), | 434 | loop_state.subscriptions.subscriptions(), |
443 | ); | 435 | ); |
444 | pool.execute({ | 436 | pool.execute({ |
445 | let subs = loop_state.subscriptions.subscriptions(); | 437 | let subs = loop_state.subscriptions.subscriptions(); |
446 | let snap = world_state.snapshot(); | 438 | let snap = global_state.snapshot(); |
447 | move || snap.analysis().prime_caches(subs).unwrap_or_else(|_: Canceled| ()) | 439 | move || snap.analysis().prime_caches(subs).unwrap_or_else(|_: Canceled| ()) |
448 | }); | 440 | }); |
449 | } | 441 | } |
@@ -467,7 +459,7 @@ fn on_task( | |||
467 | task: Task, | 459 | task: Task, |
468 | msg_sender: &Sender<Message>, | 460 | msg_sender: &Sender<Message>, |
469 | pending_requests: &mut PendingRequests, | 461 | pending_requests: &mut PendingRequests, |
470 | state: &mut WorldState, | 462 | state: &mut GlobalState, |
471 | ) { | 463 | ) { |
472 | match task { | 464 | match task { |
473 | Task::Respond(response) => { | 465 | Task::Respond(response) => { |
@@ -485,7 +477,7 @@ fn on_task( | |||
485 | } | 477 | } |
486 | 478 | ||
487 | fn on_request( | 479 | fn on_request( |
488 | world: &mut WorldState, | 480 | global_state: &mut GlobalState, |
489 | pending_requests: &mut PendingRequests, | 481 | pending_requests: &mut PendingRequests, |
490 | pool: &ThreadPool, | 482 | pool: &ThreadPool, |
491 | task_sender: &Sender<Task>, | 483 | task_sender: &Sender<Task>, |
@@ -496,7 +488,7 @@ fn on_request( | |||
496 | let mut pool_dispatcher = PoolDispatcher { | 488 | let mut pool_dispatcher = PoolDispatcher { |
497 | req: Some(req), | 489 | req: Some(req), |
498 | pool, | 490 | pool, |
499 | world, | 491 | global_state, |
500 | task_sender, | 492 | task_sender, |
501 | msg_sender, | 493 | msg_sender, |
502 | pending_requests, | 494 | pending_requests, |
@@ -509,9 +501,7 @@ fn on_request( | |||
509 | .on_sync::<lsp_types::request::SelectionRangeRequest>(|s, p| { | 501 | .on_sync::<lsp_types::request::SelectionRangeRequest>(|s, p| { |
510 | handlers::handle_selection_range(s.snapshot(), p) | 502 | handlers::handle_selection_range(s.snapshot(), p) |
511 | })? | 503 | })? |
512 | .on_sync::<lsp_ext::FindMatchingBrace>(|s, p| { | 504 | .on_sync::<lsp_ext::MatchingBrace>(|s, p| handlers::handle_matching_brace(s.snapshot(), p))? |
513 | handlers::handle_find_matching_brace(s.snapshot(), p) | ||
514 | })? | ||
515 | .on::<lsp_ext::AnalyzerStatus>(handlers::handle_analyzer_status)? | 505 | .on::<lsp_ext::AnalyzerStatus>(handlers::handle_analyzer_status)? |
516 | .on::<lsp_ext::SyntaxTree>(handlers::handle_syntax_tree)? | 506 | .on::<lsp_ext::SyntaxTree>(handlers::handle_syntax_tree)? |
517 | .on::<lsp_ext::ExpandMacro>(handlers::handle_expand_macro)? | 507 | .on::<lsp_ext::ExpandMacro>(handlers::handle_expand_macro)? |
@@ -554,7 +544,7 @@ fn on_request( | |||
554 | 544 | ||
555 | fn on_notification( | 545 | fn on_notification( |
556 | msg_sender: &Sender<Message>, | 546 | msg_sender: &Sender<Message>, |
557 | state: &mut WorldState, | 547 | state: &mut GlobalState, |
558 | loop_state: &mut LoopState, | 548 | loop_state: &mut LoopState, |
559 | not: Notification, | 549 | not: Notification, |
560 | ) -> Result<()> { | 550 | ) -> Result<()> { |
@@ -728,7 +718,7 @@ fn apply_document_changes( | |||
728 | 718 | ||
729 | fn on_check_task( | 719 | fn on_check_task( |
730 | task: CheckTask, | 720 | task: CheckTask, |
731 | world_state: &mut WorldState, | 721 | global_state: &mut GlobalState, |
732 | task_sender: &Sender<Task>, | 722 | task_sender: &Sender<Task>, |
733 | ) -> Result<()> { | 723 | ) -> Result<()> { |
734 | match task { | 724 | match task { |
@@ -747,7 +737,7 @@ fn on_check_task( | |||
747 | .uri | 737 | .uri |
748 | .to_file_path() | 738 | .to_file_path() |
749 | .map_err(|()| format!("invalid uri: {}", diag.location.uri))?; | 739 | .map_err(|()| format!("invalid uri: {}", diag.location.uri))?; |
750 | let file_id = match world_state.vfs.read().path2file(&path) { | 740 | let file_id = match global_state.vfs.read().path2file(&path) { |
751 | Some(file) => FileId(file.0), | 741 | Some(file) => FileId(file.0), |
752 | None => { | 742 | None => { |
753 | log::error!( | 743 | log::error!( |
@@ -767,7 +757,7 @@ fn on_check_task( | |||
767 | } | 757 | } |
768 | 758 | ||
769 | CheckTask::Status(status) => { | 759 | CheckTask::Status(status) => { |
770 | if world_state.config.client_caps.work_done_progress { | 760 | if global_state.config.client_caps.work_done_progress { |
771 | let progress = match status { | 761 | let progress = match status { |
772 | Status::Being => { | 762 | Status::Being => { |
773 | lsp_types::WorkDoneProgress::Begin(lsp_types::WorkDoneProgressBegin { | 763 | lsp_types::WorkDoneProgress::Begin(lsp_types::WorkDoneProgressBegin { |
@@ -806,7 +796,7 @@ fn on_check_task( | |||
806 | Ok(()) | 796 | Ok(()) |
807 | } | 797 | } |
808 | 798 | ||
809 | fn on_diagnostic_task(task: DiagnosticTask, msg_sender: &Sender<Message>, state: &mut WorldState) { | 799 | fn on_diagnostic_task(task: DiagnosticTask, msg_sender: &Sender<Message>, state: &mut GlobalState) { |
810 | let subscriptions = state.diagnostics.handle_task(task); | 800 | let subscriptions = state.diagnostics.handle_task(task); |
811 | 801 | ||
812 | for file_id in subscriptions { | 802 | for file_id in subscriptions { |
@@ -881,7 +871,7 @@ fn send_startup_progress(sender: &Sender<Message>, loop_state: &mut LoopState) { | |||
881 | struct PoolDispatcher<'a> { | 871 | struct PoolDispatcher<'a> { |
882 | req: Option<Request>, | 872 | req: Option<Request>, |
883 | pool: &'a ThreadPool, | 873 | pool: &'a ThreadPool, |
884 | world: &'a mut WorldState, | 874 | global_state: &'a mut GlobalState, |
885 | pending_requests: &'a mut PendingRequests, | 875 | pending_requests: &'a mut PendingRequests, |
886 | msg_sender: &'a Sender<Message>, | 876 | msg_sender: &'a Sender<Message>, |
887 | task_sender: &'a Sender<Task>, | 877 | task_sender: &'a Sender<Task>, |
@@ -892,7 +882,7 @@ impl<'a> PoolDispatcher<'a> { | |||
892 | /// Dispatches the request onto the current thread | 882 | /// Dispatches the request onto the current thread |
893 | fn on_sync<R>( | 883 | fn on_sync<R>( |
894 | &mut self, | 884 | &mut self, |
895 | f: fn(&mut WorldState, R::Params) -> Result<R::Result>, | 885 | f: fn(&mut GlobalState, R::Params) -> Result<R::Result>, |
896 | ) -> Result<&mut Self> | 886 | ) -> Result<&mut Self> |
897 | where | 887 | where |
898 | R: lsp_types::request::Request + 'static, | 888 | R: lsp_types::request::Request + 'static, |
@@ -905,18 +895,21 @@ impl<'a> PoolDispatcher<'a> { | |||
905 | return Ok(self); | 895 | return Ok(self); |
906 | } | 896 | } |
907 | }; | 897 | }; |
908 | let world = panic::AssertUnwindSafe(&mut *self.world); | 898 | let world = panic::AssertUnwindSafe(&mut *self.global_state); |
909 | let task = panic::catch_unwind(move || { | 899 | let task = panic::catch_unwind(move || { |
910 | let result = f(world.0, params); | 900 | let result = f(world.0, params); |
911 | result_to_task::<R>(id, result) | 901 | result_to_task::<R>(id, result) |
912 | }) | 902 | }) |
913 | .map_err(|_| format!("sync task {:?} panicked", R::METHOD))?; | 903 | .map_err(|_| format!("sync task {:?} panicked", R::METHOD))?; |
914 | on_task(task, self.msg_sender, self.pending_requests, self.world); | 904 | on_task(task, self.msg_sender, self.pending_requests, self.global_state); |
915 | Ok(self) | 905 | Ok(self) |
916 | } | 906 | } |
917 | 907 | ||
918 | /// Dispatches the request onto thread pool | 908 | /// Dispatches the request onto thread pool |
919 | fn on<R>(&mut self, f: fn(WorldSnapshot, R::Params) -> Result<R::Result>) -> Result<&mut Self> | 909 | fn on<R>( |
910 | &mut self, | ||
911 | f: fn(GlobalStateSnapshot, R::Params) -> Result<R::Result>, | ||
912 | ) -> Result<&mut Self> | ||
920 | where | 913 | where |
921 | R: lsp_types::request::Request + 'static, | 914 | R: lsp_types::request::Request + 'static, |
922 | R::Params: DeserializeOwned + Send + 'static, | 915 | R::Params: DeserializeOwned + Send + 'static, |
@@ -930,7 +923,7 @@ impl<'a> PoolDispatcher<'a> { | |||
930 | }; | 923 | }; |
931 | 924 | ||
932 | self.pool.execute({ | 925 | self.pool.execute({ |
933 | let world = self.world.snapshot(); | 926 | let world = self.global_state.snapshot(); |
934 | let sender = self.task_sender.clone(); | 927 | let sender = self.task_sender.clone(); |
935 | move || { | 928 | move || { |
936 | let result = f(world, params); | 929 | let result = f(world, params); |
@@ -1014,7 +1007,7 @@ where | |||
1014 | 1007 | ||
1015 | fn update_file_notifications_on_threadpool( | 1008 | fn update_file_notifications_on_threadpool( |
1016 | pool: &ThreadPool, | 1009 | pool: &ThreadPool, |
1017 | world: WorldSnapshot, | 1010 | world: GlobalStateSnapshot, |
1018 | task_sender: Sender<Task>, | 1011 | task_sender: Sender<Task>, |
1019 | subscriptions: Vec<FileId>, | 1012 | subscriptions: Vec<FileId>, |
1020 | ) { | 1013 | ) { |
diff --git a/crates/rust-analyzer/src/main_loop/handlers.rs b/crates/rust-analyzer/src/main_loop/handlers.rs index a9703e1d6..c2a5bf4d6 100644 --- a/crates/rust-analyzer/src/main_loop/handlers.rs +++ b/crates/rust-analyzer/src/main_loop/handlers.rs | |||
@@ -18,13 +18,11 @@ use lsp_types::{ | |||
18 | SemanticTokensResult, SymbolInformation, TextDocumentIdentifier, Url, WorkspaceEdit, | 18 | SemanticTokensResult, SymbolInformation, TextDocumentIdentifier, Url, WorkspaceEdit, |
19 | }; | 19 | }; |
20 | use ra_ide::{ | 20 | use ra_ide::{ |
21 | FileId, FilePosition, FileRange, Query, RangeInfo, Runnable, RunnableKind, SearchScope, | 21 | FileId, FilePosition, FileRange, Query, RangeInfo, RunnableKind, SearchScope, TextEdit, |
22 | TextEdit, | ||
23 | }; | 22 | }; |
24 | use ra_prof::profile; | 23 | use ra_prof::profile; |
25 | use ra_project_model::TargetKind; | 24 | use ra_project_model::TargetKind; |
26 | use ra_syntax::{AstNode, SyntaxKind, TextRange, TextSize}; | 25 | use ra_syntax::{AstNode, SyntaxKind, TextRange, TextSize}; |
27 | use rustc_hash::FxHashMap; | ||
28 | use serde::{Deserialize, Serialize}; | 26 | use serde::{Deserialize, Serialize}; |
29 | use serde_json::to_value; | 27 | use serde_json::to_value; |
30 | use stdx::format_to; | 28 | use stdx::format_to; |
@@ -34,17 +32,16 @@ use crate::{ | |||
34 | config::RustfmtConfig, | 32 | config::RustfmtConfig, |
35 | diagnostics::DiagnosticTask, | 33 | diagnostics::DiagnosticTask, |
36 | from_json, from_proto, | 34 | from_json, from_proto, |
35 | global_state::GlobalStateSnapshot, | ||
37 | lsp_ext::{self, InlayHint, InlayHintsParams}, | 36 | lsp_ext::{self, InlayHint, InlayHintsParams}, |
38 | to_proto, | 37 | to_proto, LspError, Result, |
39 | world::WorldSnapshot, | ||
40 | LspError, Result, | ||
41 | }; | 38 | }; |
42 | 39 | ||
43 | pub fn handle_analyzer_status(world: WorldSnapshot, _: ()) -> Result<String> { | 40 | pub fn handle_analyzer_status(snap: GlobalStateSnapshot, _: ()) -> Result<String> { |
44 | let _p = profile("handle_analyzer_status"); | 41 | let _p = profile("handle_analyzer_status"); |
45 | let mut buf = world.status(); | 42 | let mut buf = snap.status(); |
46 | format_to!(buf, "\n\nrequests:\n"); | 43 | format_to!(buf, "\n\nrequests:\n"); |
47 | let requests = world.latest_requests.read(); | 44 | let requests = snap.latest_requests.read(); |
48 | for (is_last, r) in requests.iter() { | 45 | for (is_last, r) in requests.iter() { |
49 | let mark = if is_last { "*" } else { " " }; | 46 | let mark = if is_last { "*" } else { " " }; |
50 | format_to!(buf, "{}{:4} {:<36}{}ms\n", mark, r.id, r.method, r.duration.as_millis()); | 47 | format_to!(buf, "{}{:4} {:<36}{}ms\n", mark, r.id, r.method, r.duration.as_millis()); |
@@ -53,42 +50,37 @@ pub fn handle_analyzer_status(world: WorldSnapshot, _: ()) -> Result<String> { | |||
53 | } | 50 | } |
54 | 51 | ||
55 | pub fn handle_syntax_tree( | 52 | pub fn handle_syntax_tree( |
56 | world: WorldSnapshot, | 53 | snap: GlobalStateSnapshot, |
57 | params: lsp_ext::SyntaxTreeParams, | 54 | params: lsp_ext::SyntaxTreeParams, |
58 | ) -> Result<String> { | 55 | ) -> Result<String> { |
59 | let _p = profile("handle_syntax_tree"); | 56 | let _p = profile("handle_syntax_tree"); |
60 | let id = from_proto::file_id(&world, ¶ms.text_document.uri)?; | 57 | let id = from_proto::file_id(&snap, ¶ms.text_document.uri)?; |
61 | let line_index = world.analysis().file_line_index(id)?; | 58 | let line_index = snap.analysis().file_line_index(id)?; |
62 | let text_range = params.range.map(|r| from_proto::text_range(&line_index, r)); | 59 | let text_range = params.range.map(|r| from_proto::text_range(&line_index, r)); |
63 | let res = world.analysis().syntax_tree(id, text_range)?; | 60 | let res = snap.analysis().syntax_tree(id, text_range)?; |
64 | Ok(res) | 61 | Ok(res) |
65 | } | 62 | } |
66 | 63 | ||
67 | pub fn handle_expand_macro( | 64 | pub fn handle_expand_macro( |
68 | world: WorldSnapshot, | 65 | snap: GlobalStateSnapshot, |
69 | params: lsp_ext::ExpandMacroParams, | 66 | params: lsp_ext::ExpandMacroParams, |
70 | ) -> Result<Option<lsp_ext::ExpandedMacro>> { | 67 | ) -> Result<Option<lsp_ext::ExpandedMacro>> { |
71 | let _p = profile("handle_expand_macro"); | 68 | let _p = profile("handle_expand_macro"); |
72 | let file_id = from_proto::file_id(&world, ¶ms.text_document.uri)?; | 69 | let file_id = from_proto::file_id(&snap, ¶ms.text_document.uri)?; |
73 | let line_index = world.analysis().file_line_index(file_id)?; | 70 | let line_index = snap.analysis().file_line_index(file_id)?; |
74 | let offset = params.position.map(|p| from_proto::offset(&line_index, p)); | 71 | let offset = from_proto::offset(&line_index, params.position); |
75 | 72 | ||
76 | match offset { | 73 | let res = snap.analysis().expand_macro(FilePosition { file_id, offset })?; |
77 | None => Ok(None), | 74 | Ok(res.map(|it| lsp_ext::ExpandedMacro { name: it.name, expansion: it.expansion })) |
78 | Some(offset) => { | ||
79 | let res = world.analysis().expand_macro(FilePosition { file_id, offset })?; | ||
80 | Ok(res.map(|it| lsp_ext::ExpandedMacro { name: it.name, expansion: it.expansion })) | ||
81 | } | ||
82 | } | ||
83 | } | 75 | } |
84 | 76 | ||
85 | pub fn handle_selection_range( | 77 | pub fn handle_selection_range( |
86 | world: WorldSnapshot, | 78 | snap: GlobalStateSnapshot, |
87 | params: lsp_types::SelectionRangeParams, | 79 | params: lsp_types::SelectionRangeParams, |
88 | ) -> Result<Option<Vec<lsp_types::SelectionRange>>> { | 80 | ) -> Result<Option<Vec<lsp_types::SelectionRange>>> { |
89 | let _p = profile("handle_selection_range"); | 81 | let _p = profile("handle_selection_range"); |
90 | let file_id = from_proto::file_id(&world, ¶ms.text_document.uri)?; | 82 | let file_id = from_proto::file_id(&snap, ¶ms.text_document.uri)?; |
91 | let line_index = world.analysis().file_line_index(file_id)?; | 83 | let line_index = snap.analysis().file_line_index(file_id)?; |
92 | let res: Result<Vec<lsp_types::SelectionRange>> = params | 84 | let res: Result<Vec<lsp_types::SelectionRange>> = params |
93 | .positions | 85 | .positions |
94 | .into_iter() | 86 | .into_iter() |
@@ -100,7 +92,7 @@ pub fn handle_selection_range( | |||
100 | loop { | 92 | loop { |
101 | ranges.push(range); | 93 | ranges.push(range); |
102 | let frange = FileRange { file_id, range }; | 94 | let frange = FileRange { file_id, range }; |
103 | let next = world.analysis().extend_selection(frange)?; | 95 | let next = snap.analysis().extend_selection(frange)?; |
104 | if next == range { | 96 | if next == range { |
105 | break; | 97 | break; |
106 | } else { | 98 | } else { |
@@ -125,19 +117,19 @@ pub fn handle_selection_range( | |||
125 | Ok(Some(res?)) | 117 | Ok(Some(res?)) |
126 | } | 118 | } |
127 | 119 | ||
128 | pub fn handle_find_matching_brace( | 120 | pub fn handle_matching_brace( |
129 | world: WorldSnapshot, | 121 | snap: GlobalStateSnapshot, |
130 | params: lsp_ext::FindMatchingBraceParams, | 122 | params: lsp_ext::MatchingBraceParams, |
131 | ) -> Result<Vec<Position>> { | 123 | ) -> Result<Vec<Position>> { |
132 | let _p = profile("handle_find_matching_brace"); | 124 | let _p = profile("handle_matching_brace"); |
133 | let file_id = from_proto::file_id(&world, ¶ms.text_document.uri)?; | 125 | let file_id = from_proto::file_id(&snap, ¶ms.text_document.uri)?; |
134 | let line_index = world.analysis().file_line_index(file_id)?; | 126 | let line_index = snap.analysis().file_line_index(file_id)?; |
135 | let res = params | 127 | let res = params |
136 | .offsets | 128 | .positions |
137 | .into_iter() | 129 | .into_iter() |
138 | .map(|position| { | 130 | .map(|position| { |
139 | let offset = from_proto::offset(&line_index, position); | 131 | let offset = from_proto::offset(&line_index, position); |
140 | let offset = match world.analysis().matching_brace(FilePosition { file_id, offset }) { | 132 | let offset = match snap.analysis().matching_brace(FilePosition { file_id, offset }) { |
141 | Ok(Some(matching_brace_offset)) => matching_brace_offset, | 133 | Ok(Some(matching_brace_offset)) => matching_brace_offset, |
142 | Err(_) | Ok(None) => offset, | 134 | Err(_) | Ok(None) => offset, |
143 | }; | 135 | }; |
@@ -148,17 +140,17 @@ pub fn handle_find_matching_brace( | |||
148 | } | 140 | } |
149 | 141 | ||
150 | pub fn handle_join_lines( | 142 | pub fn handle_join_lines( |
151 | world: WorldSnapshot, | 143 | snap: GlobalStateSnapshot, |
152 | params: lsp_ext::JoinLinesParams, | 144 | params: lsp_ext::JoinLinesParams, |
153 | ) -> Result<Vec<lsp_types::TextEdit>> { | 145 | ) -> Result<Vec<lsp_types::TextEdit>> { |
154 | let _p = profile("handle_join_lines"); | 146 | let _p = profile("handle_join_lines"); |
155 | let file_id = from_proto::file_id(&world, ¶ms.text_document.uri)?; | 147 | let file_id = from_proto::file_id(&snap, ¶ms.text_document.uri)?; |
156 | let line_index = world.analysis().file_line_index(file_id)?; | 148 | let line_index = snap.analysis().file_line_index(file_id)?; |
157 | let line_endings = world.file_line_endings(file_id); | 149 | let line_endings = snap.file_line_endings(file_id); |
158 | let mut res = TextEdit::default(); | 150 | let mut res = TextEdit::default(); |
159 | for range in params.ranges { | 151 | for range in params.ranges { |
160 | let range = from_proto::text_range(&line_index, range); | 152 | let range = from_proto::text_range(&line_index, range); |
161 | let edit = world.analysis().join_lines(FileRange { file_id, range })?; | 153 | let edit = snap.analysis().join_lines(FileRange { file_id, range })?; |
162 | match res.union(edit) { | 154 | match res.union(edit) { |
163 | Ok(()) => (), | 155 | Ok(()) => (), |
164 | Err(_edit) => { | 156 | Err(_edit) => { |
@@ -171,33 +163,37 @@ pub fn handle_join_lines( | |||
171 | } | 163 | } |
172 | 164 | ||
173 | pub fn handle_on_enter( | 165 | pub fn handle_on_enter( |
174 | world: WorldSnapshot, | 166 | snap: GlobalStateSnapshot, |
175 | params: lsp_types::TextDocumentPositionParams, | 167 | params: lsp_types::TextDocumentPositionParams, |
176 | ) -> Result<Option<lsp_ext::SnippetWorkspaceEdit>> { | 168 | ) -> Result<Option<Vec<lsp_ext::SnippetTextEdit>>> { |
177 | let _p = profile("handle_on_enter"); | 169 | let _p = profile("handle_on_enter"); |
178 | let position = from_proto::file_position(&world, params)?; | 170 | let position = from_proto::file_position(&snap, params)?; |
179 | match world.analysis().on_enter(position)? { | 171 | let edit = match snap.analysis().on_enter(position)? { |
180 | None => Ok(None), | 172 | None => return Ok(None), |
181 | Some(source_change) => to_proto::snippet_workspace_edit(&world, source_change).map(Some), | 173 | Some(it) => it, |
182 | } | 174 | }; |
175 | let line_index = snap.analysis().file_line_index(position.file_id)?; | ||
176 | let line_endings = snap.file_line_endings(position.file_id); | ||
177 | let edit = to_proto::snippet_text_edit_vec(&line_index, line_endings, true, edit); | ||
178 | Ok(Some(edit)) | ||
183 | } | 179 | } |
184 | 180 | ||
185 | // Don't forget to add new trigger characters to `ServerCapabilities` in `caps.rs`. | 181 | // Don't forget to add new trigger characters to `ServerCapabilities` in `caps.rs`. |
186 | pub fn handle_on_type_formatting( | 182 | pub fn handle_on_type_formatting( |
187 | world: WorldSnapshot, | 183 | snap: GlobalStateSnapshot, |
188 | params: lsp_types::DocumentOnTypeFormattingParams, | 184 | params: lsp_types::DocumentOnTypeFormattingParams, |
189 | ) -> Result<Option<Vec<lsp_types::TextEdit>>> { | 185 | ) -> Result<Option<Vec<lsp_types::TextEdit>>> { |
190 | let _p = profile("handle_on_type_formatting"); | 186 | let _p = profile("handle_on_type_formatting"); |
191 | let mut position = from_proto::file_position(&world, params.text_document_position)?; | 187 | let mut position = from_proto::file_position(&snap, params.text_document_position)?; |
192 | let line_index = world.analysis().file_line_index(position.file_id)?; | 188 | let line_index = snap.analysis().file_line_index(position.file_id)?; |
193 | let line_endings = world.file_line_endings(position.file_id); | 189 | let line_endings = snap.file_line_endings(position.file_id); |
194 | 190 | ||
195 | // in `ra_ide`, the `on_type` invariant is that | 191 | // in `ra_ide`, the `on_type` invariant is that |
196 | // `text.char_at(position) == typed_char`. | 192 | // `text.char_at(position) == typed_char`. |
197 | position.offset -= TextSize::of('.'); | 193 | position.offset -= TextSize::of('.'); |
198 | let char_typed = params.ch.chars().next().unwrap_or('\0'); | 194 | let char_typed = params.ch.chars().next().unwrap_or('\0'); |
199 | assert!({ | 195 | assert!({ |
200 | let text = world.analysis().file_text(position.file_id)?; | 196 | let text = snap.analysis().file_text(position.file_id)?; |
201 | text[usize::from(position.offset)..].starts_with(char_typed) | 197 | text[usize::from(position.offset)..].starts_with(char_typed) |
202 | }); | 198 | }); |
203 | 199 | ||
@@ -209,7 +205,7 @@ pub fn handle_on_type_formatting( | |||
209 | return Ok(None); | 205 | return Ok(None); |
210 | } | 206 | } |
211 | 207 | ||
212 | let edit = world.analysis().on_char_typed(position, char_typed)?; | 208 | let edit = snap.analysis().on_char_typed(position, char_typed)?; |
213 | let mut edit = match edit { | 209 | let mut edit = match edit { |
214 | Some(it) => it, | 210 | Some(it) => it, |
215 | None => return Ok(None), | 211 | None => return Ok(None), |
@@ -223,16 +219,16 @@ pub fn handle_on_type_formatting( | |||
223 | } | 219 | } |
224 | 220 | ||
225 | pub fn handle_document_symbol( | 221 | pub fn handle_document_symbol( |
226 | world: WorldSnapshot, | 222 | snap: GlobalStateSnapshot, |
227 | params: lsp_types::DocumentSymbolParams, | 223 | params: lsp_types::DocumentSymbolParams, |
228 | ) -> Result<Option<lsp_types::DocumentSymbolResponse>> { | 224 | ) -> Result<Option<lsp_types::DocumentSymbolResponse>> { |
229 | let _p = profile("handle_document_symbol"); | 225 | let _p = profile("handle_document_symbol"); |
230 | let file_id = from_proto::file_id(&world, ¶ms.text_document.uri)?; | 226 | let file_id = from_proto::file_id(&snap, ¶ms.text_document.uri)?; |
231 | let line_index = world.analysis().file_line_index(file_id)?; | 227 | let line_index = snap.analysis().file_line_index(file_id)?; |
232 | 228 | ||
233 | let mut parents: Vec<(DocumentSymbol, Option<usize>)> = Vec::new(); | 229 | let mut parents: Vec<(DocumentSymbol, Option<usize>)> = Vec::new(); |
234 | 230 | ||
235 | for symbol in world.analysis().file_structure(file_id)? { | 231 | for symbol in snap.analysis().file_structure(file_id)? { |
236 | let doc_symbol = DocumentSymbol { | 232 | let doc_symbol = DocumentSymbol { |
237 | name: symbol.label, | 233 | name: symbol.label, |
238 | detail: symbol.detail, | 234 | detail: symbol.detail, |
@@ -258,10 +254,10 @@ pub fn handle_document_symbol( | |||
258 | } | 254 | } |
259 | } | 255 | } |
260 | 256 | ||
261 | let res = if world.config.client_caps.hierarchical_symbols { | 257 | let res = if snap.config.client_caps.hierarchical_symbols { |
262 | document_symbols.into() | 258 | document_symbols.into() |
263 | } else { | 259 | } else { |
264 | let url = to_proto::url(&world, file_id)?; | 260 | let url = to_proto::url(&snap, file_id)?; |
265 | let mut symbol_information = Vec::<SymbolInformation>::new(); | 261 | let mut symbol_information = Vec::<SymbolInformation>::new(); |
266 | for symbol in document_symbols { | 262 | for symbol in document_symbols { |
267 | flatten_document_symbol(&symbol, None, &url, &mut symbol_information); | 263 | flatten_document_symbol(&symbol, None, &url, &mut symbol_information); |
@@ -281,7 +277,7 @@ pub fn handle_document_symbol( | |||
281 | kind: symbol.kind, | 277 | kind: symbol.kind, |
282 | deprecated: symbol.deprecated, | 278 | deprecated: symbol.deprecated, |
283 | location: Location::new(url.clone(), symbol.range), | 279 | location: Location::new(url.clone(), symbol.range), |
284 | container_name: container_name, | 280 | container_name, |
285 | }); | 281 | }); |
286 | 282 | ||
287 | for child in symbol.children.iter().flatten() { | 283 | for child in symbol.children.iter().flatten() { |
@@ -291,7 +287,7 @@ pub fn handle_document_symbol( | |||
291 | } | 287 | } |
292 | 288 | ||
293 | pub fn handle_workspace_symbol( | 289 | pub fn handle_workspace_symbol( |
294 | world: WorldSnapshot, | 290 | snap: GlobalStateSnapshot, |
295 | params: lsp_types::WorkspaceSymbolParams, | 291 | params: lsp_types::WorkspaceSymbolParams, |
296 | ) -> Result<Option<Vec<SymbolInformation>>> { | 292 | ) -> Result<Option<Vec<SymbolInformation>>> { |
297 | let _p = profile("handle_workspace_symbol"); | 293 | let _p = profile("handle_workspace_symbol"); |
@@ -309,22 +305,22 @@ pub fn handle_workspace_symbol( | |||
309 | q.limit(128); | 305 | q.limit(128); |
310 | q | 306 | q |
311 | }; | 307 | }; |
312 | let mut res = exec_query(&world, query)?; | 308 | let mut res = exec_query(&snap, query)?; |
313 | if res.is_empty() && !all_symbols { | 309 | if res.is_empty() && !all_symbols { |
314 | let mut query = Query::new(params.query); | 310 | let mut query = Query::new(params.query); |
315 | query.limit(128); | 311 | query.limit(128); |
316 | res = exec_query(&world, query)?; | 312 | res = exec_query(&snap, query)?; |
317 | } | 313 | } |
318 | 314 | ||
319 | return Ok(Some(res)); | 315 | return Ok(Some(res)); |
320 | 316 | ||
321 | fn exec_query(world: &WorldSnapshot, query: Query) -> Result<Vec<SymbolInformation>> { | 317 | fn exec_query(snap: &GlobalStateSnapshot, query: Query) -> Result<Vec<SymbolInformation>> { |
322 | let mut res = Vec::new(); | 318 | let mut res = Vec::new(); |
323 | for nav in world.analysis().symbol_search(query)? { | 319 | for nav in snap.analysis().symbol_search(query)? { |
324 | let info = SymbolInformation { | 320 | let info = SymbolInformation { |
325 | name: nav.name().to_string(), | 321 | name: nav.name().to_string(), |
326 | kind: to_proto::symbol_kind(nav.kind()), | 322 | kind: to_proto::symbol_kind(nav.kind()), |
327 | location: to_proto::location(world, nav.file_range())?, | 323 | location: to_proto::location(snap, nav.file_range())?, |
328 | container_name: nav.container_name().map(|v| v.to_string()), | 324 | container_name: nav.container_name().map(|v| v.to_string()), |
329 | deprecated: None, | 325 | deprecated: None, |
330 | }; | 326 | }; |
@@ -335,87 +331,75 @@ pub fn handle_workspace_symbol( | |||
335 | } | 331 | } |
336 | 332 | ||
337 | pub fn handle_goto_definition( | 333 | pub fn handle_goto_definition( |
338 | world: WorldSnapshot, | 334 | snap: GlobalStateSnapshot, |
339 | params: lsp_types::GotoDefinitionParams, | 335 | params: lsp_types::GotoDefinitionParams, |
340 | ) -> Result<Option<lsp_types::GotoDefinitionResponse>> { | 336 | ) -> Result<Option<lsp_types::GotoDefinitionResponse>> { |
341 | let _p = profile("handle_goto_definition"); | 337 | let _p = profile("handle_goto_definition"); |
342 | let position = from_proto::file_position(&world, params.text_document_position_params)?; | 338 | let position = from_proto::file_position(&snap, params.text_document_position_params)?; |
343 | let nav_info = match world.analysis().goto_definition(position)? { | 339 | let nav_info = match snap.analysis().goto_definition(position)? { |
344 | None => return Ok(None), | 340 | None => return Ok(None), |
345 | Some(it) => it, | 341 | Some(it) => it, |
346 | }; | 342 | }; |
347 | let res = to_proto::goto_definition_response( | 343 | let src = FileRange { file_id: position.file_id, range: nav_info.range }; |
348 | &world, | 344 | let res = to_proto::goto_definition_response(&snap, Some(src), nav_info.info)?; |
349 | FileRange { file_id: position.file_id, range: nav_info.range }, | ||
350 | nav_info.info, | ||
351 | )?; | ||
352 | Ok(Some(res)) | 345 | Ok(Some(res)) |
353 | } | 346 | } |
354 | 347 | ||
355 | pub fn handle_goto_implementation( | 348 | pub fn handle_goto_implementation( |
356 | world: WorldSnapshot, | 349 | snap: GlobalStateSnapshot, |
357 | params: lsp_types::request::GotoImplementationParams, | 350 | params: lsp_types::request::GotoImplementationParams, |
358 | ) -> Result<Option<lsp_types::request::GotoImplementationResponse>> { | 351 | ) -> Result<Option<lsp_types::request::GotoImplementationResponse>> { |
359 | let _p = profile("handle_goto_implementation"); | 352 | let _p = profile("handle_goto_implementation"); |
360 | let position = from_proto::file_position(&world, params.text_document_position_params)?; | 353 | let position = from_proto::file_position(&snap, params.text_document_position_params)?; |
361 | let nav_info = match world.analysis().goto_implementation(position)? { | 354 | let nav_info = match snap.analysis().goto_implementation(position)? { |
362 | None => return Ok(None), | 355 | None => return Ok(None), |
363 | Some(it) => it, | 356 | Some(it) => it, |
364 | }; | 357 | }; |
365 | let res = to_proto::goto_definition_response( | 358 | let src = FileRange { file_id: position.file_id, range: nav_info.range }; |
366 | &world, | 359 | let res = to_proto::goto_definition_response(&snap, Some(src), nav_info.info)?; |
367 | FileRange { file_id: position.file_id, range: nav_info.range }, | ||
368 | nav_info.info, | ||
369 | )?; | ||
370 | Ok(Some(res)) | 360 | Ok(Some(res)) |
371 | } | 361 | } |
372 | 362 | ||
373 | pub fn handle_goto_type_definition( | 363 | pub fn handle_goto_type_definition( |
374 | world: WorldSnapshot, | 364 | snap: GlobalStateSnapshot, |
375 | params: lsp_types::request::GotoTypeDefinitionParams, | 365 | params: lsp_types::request::GotoTypeDefinitionParams, |
376 | ) -> Result<Option<lsp_types::request::GotoTypeDefinitionResponse>> { | 366 | ) -> Result<Option<lsp_types::request::GotoTypeDefinitionResponse>> { |
377 | let _p = profile("handle_goto_type_definition"); | 367 | let _p = profile("handle_goto_type_definition"); |
378 | let position = from_proto::file_position(&world, params.text_document_position_params)?; | 368 | let position = from_proto::file_position(&snap, params.text_document_position_params)?; |
379 | let nav_info = match world.analysis().goto_type_definition(position)? { | 369 | let nav_info = match snap.analysis().goto_type_definition(position)? { |
380 | None => return Ok(None), | 370 | None => return Ok(None), |
381 | Some(it) => it, | 371 | Some(it) => it, |
382 | }; | 372 | }; |
383 | let res = to_proto::goto_definition_response( | 373 | let src = FileRange { file_id: position.file_id, range: nav_info.range }; |
384 | &world, | 374 | let res = to_proto::goto_definition_response(&snap, Some(src), nav_info.info)?; |
385 | FileRange { file_id: position.file_id, range: nav_info.range }, | ||
386 | nav_info.info, | ||
387 | )?; | ||
388 | Ok(Some(res)) | 375 | Ok(Some(res)) |
389 | } | 376 | } |
390 | 377 | ||
391 | pub fn handle_parent_module( | 378 | pub fn handle_parent_module( |
392 | world: WorldSnapshot, | 379 | snap: GlobalStateSnapshot, |
393 | params: lsp_types::TextDocumentPositionParams, | 380 | params: lsp_types::TextDocumentPositionParams, |
394 | ) -> Result<Vec<Location>> { | 381 | ) -> Result<Option<lsp_types::GotoDefinitionResponse>> { |
395 | let _p = profile("handle_parent_module"); | 382 | let _p = profile("handle_parent_module"); |
396 | let position = from_proto::file_position(&world, params)?; | 383 | let position = from_proto::file_position(&snap, params)?; |
397 | world | 384 | let navs = snap.analysis().parent_module(position)?; |
398 | .analysis() | 385 | let res = to_proto::goto_definition_response(&snap, None, navs)?; |
399 | .parent_module(position)? | 386 | Ok(Some(res)) |
400 | .into_iter() | ||
401 | .map(|it| to_proto::location(&world, it.file_range())) | ||
402 | .collect::<Result<Vec<_>>>() | ||
403 | } | 387 | } |
404 | 388 | ||
405 | pub fn handle_runnables( | 389 | pub fn handle_runnables( |
406 | world: WorldSnapshot, | 390 | snap: GlobalStateSnapshot, |
407 | params: lsp_ext::RunnablesParams, | 391 | params: lsp_ext::RunnablesParams, |
408 | ) -> Result<Vec<lsp_ext::Runnable>> { | 392 | ) -> Result<Vec<lsp_ext::Runnable>> { |
409 | let _p = profile("handle_runnables"); | 393 | let _p = profile("handle_runnables"); |
410 | let file_id = from_proto::file_id(&world, ¶ms.text_document.uri)?; | 394 | let file_id = from_proto::file_id(&snap, ¶ms.text_document.uri)?; |
411 | let line_index = world.analysis().file_line_index(file_id)?; | 395 | let line_index = snap.analysis().file_line_index(file_id)?; |
412 | let offset = params.position.map(|it| from_proto::offset(&line_index, it)); | 396 | let offset = params.position.map(|it| from_proto::offset(&line_index, it)); |
413 | let mut res = Vec::new(); | 397 | let mut res = Vec::new(); |
414 | let workspace_root = world.workspace_root_for(file_id); | 398 | let workspace_root = snap.workspace_root_for(file_id); |
415 | let cargo_spec = CargoTargetSpec::for_file(&world, file_id)?; | 399 | let cargo_spec = CargoTargetSpec::for_file(&snap, file_id)?; |
416 | for runnable in world.analysis().runnables(file_id)? { | 400 | for runnable in snap.analysis().runnables(file_id)? { |
417 | if let Some(offset) = offset { | 401 | if let Some(offset) = offset { |
418 | if !runnable.range.contains_inclusive(offset) { | 402 | if !runnable.nav.full_range().contains_inclusive(offset) { |
419 | continue; | 403 | continue; |
420 | } | 404 | } |
421 | } | 405 | } |
@@ -428,7 +412,7 @@ pub fn handle_runnables( | |||
428 | } | 412 | } |
429 | } | 413 | } |
430 | } | 414 | } |
431 | res.push(to_lsp_runnable(&world, file_id, runnable)?); | 415 | res.push(to_proto::runnable(&snap, file_id, runnable)?); |
432 | } | 416 | } |
433 | 417 | ||
434 | // Add `cargo check` and `cargo test` for the whole package | 418 | // Add `cargo check` and `cargo test` for the whole package |
@@ -436,25 +420,31 @@ pub fn handle_runnables( | |||
436 | Some(spec) => { | 420 | Some(spec) => { |
437 | for &cmd in ["check", "test"].iter() { | 421 | for &cmd in ["check", "test"].iter() { |
438 | res.push(lsp_ext::Runnable { | 422 | res.push(lsp_ext::Runnable { |
439 | range: Default::default(), | ||
440 | label: format!("cargo {} -p {}", cmd, spec.package), | 423 | label: format!("cargo {} -p {}", cmd, spec.package), |
441 | bin: "cargo".to_string(), | 424 | location: None, |
442 | args: vec![cmd.to_string(), "--package".to_string(), spec.package.clone()], | 425 | kind: lsp_ext::RunnableKind::Cargo, |
443 | extra_args: Vec::new(), | 426 | args: lsp_ext::CargoRunnable { |
444 | env: FxHashMap::default(), | 427 | workspace_root: workspace_root.map(|root| root.to_owned()), |
445 | cwd: workspace_root.map(|root| root.to_owned()), | 428 | cargo_args: vec![ |
429 | cmd.to_string(), | ||
430 | "--package".to_string(), | ||
431 | spec.package.clone(), | ||
432 | ], | ||
433 | executable_args: Vec::new(), | ||
434 | }, | ||
446 | }) | 435 | }) |
447 | } | 436 | } |
448 | } | 437 | } |
449 | None => { | 438 | None => { |
450 | res.push(lsp_ext::Runnable { | 439 | res.push(lsp_ext::Runnable { |
451 | range: Default::default(), | ||
452 | label: "cargo check --workspace".to_string(), | 440 | label: "cargo check --workspace".to_string(), |
453 | bin: "cargo".to_string(), | 441 | location: None, |
454 | args: vec!["check".to_string(), "--workspace".to_string()], | 442 | kind: lsp_ext::RunnableKind::Cargo, |
455 | extra_args: Vec::new(), | 443 | args: lsp_ext::CargoRunnable { |
456 | env: FxHashMap::default(), | 444 | workspace_root: workspace_root.map(|root| root.to_owned()), |
457 | cwd: workspace_root.map(|root| root.to_owned()), | 445 | cargo_args: vec!["check".to_string(), "--workspace".to_string()], |
446 | executable_args: Vec::new(), | ||
447 | }, | ||
458 | }); | 448 | }); |
459 | } | 449 | } |
460 | } | 450 | } |
@@ -462,16 +452,16 @@ pub fn handle_runnables( | |||
462 | } | 452 | } |
463 | 453 | ||
464 | pub fn handle_completion( | 454 | pub fn handle_completion( |
465 | world: WorldSnapshot, | 455 | snap: GlobalStateSnapshot, |
466 | params: lsp_types::CompletionParams, | 456 | params: lsp_types::CompletionParams, |
467 | ) -> Result<Option<lsp_types::CompletionResponse>> { | 457 | ) -> Result<Option<lsp_types::CompletionResponse>> { |
468 | let _p = profile("handle_completion"); | 458 | let _p = profile("handle_completion"); |
469 | let position = from_proto::file_position(&world, params.text_document_position)?; | 459 | let position = from_proto::file_position(&snap, params.text_document_position)?; |
470 | let completion_triggered_after_single_colon = { | 460 | let completion_triggered_after_single_colon = { |
471 | let mut res = false; | 461 | let mut res = false; |
472 | if let Some(ctx) = params.context { | 462 | if let Some(ctx) = params.context { |
473 | if ctx.trigger_character.unwrap_or_default() == ":" { | 463 | if ctx.trigger_character.unwrap_or_default() == ":" { |
474 | let source_file = world.analysis().parse(position.file_id)?; | 464 | let source_file = snap.analysis().parse(position.file_id)?; |
475 | let syntax = source_file.syntax(); | 465 | let syntax = source_file.syntax(); |
476 | let text = syntax.text(); | 466 | let text = syntax.text(); |
477 | if let Some(next_char) = text.char_at(position.offset) { | 467 | if let Some(next_char) = text.char_at(position.offset) { |
@@ -489,12 +479,12 @@ pub fn handle_completion( | |||
489 | return Ok(None); | 479 | return Ok(None); |
490 | } | 480 | } |
491 | 481 | ||
492 | let items = match world.analysis().completions(&world.config.completion, position)? { | 482 | let items = match snap.analysis().completions(&snap.config.completion, position)? { |
493 | None => return Ok(None), | 483 | None => return Ok(None), |
494 | Some(items) => items, | 484 | Some(items) => items, |
495 | }; | 485 | }; |
496 | let line_index = world.analysis().file_line_index(position.file_id)?; | 486 | let line_index = snap.analysis().file_line_index(position.file_id)?; |
497 | let line_endings = world.file_line_endings(position.file_id); | 487 | let line_endings = snap.file_line_endings(position.file_id); |
498 | let items: Vec<CompletionItem> = items | 488 | let items: Vec<CompletionItem> = items |
499 | .into_iter() | 489 | .into_iter() |
500 | .map(|item| to_proto::completion_item(&line_index, line_endings, item)) | 490 | .map(|item| to_proto::completion_item(&line_index, line_endings, item)) |
@@ -504,15 +494,15 @@ pub fn handle_completion( | |||
504 | } | 494 | } |
505 | 495 | ||
506 | pub fn handle_folding_range( | 496 | pub fn handle_folding_range( |
507 | world: WorldSnapshot, | 497 | snap: GlobalStateSnapshot, |
508 | params: FoldingRangeParams, | 498 | params: FoldingRangeParams, |
509 | ) -> Result<Option<Vec<FoldingRange>>> { | 499 | ) -> Result<Option<Vec<FoldingRange>>> { |
510 | let _p = profile("handle_folding_range"); | 500 | let _p = profile("handle_folding_range"); |
511 | let file_id = from_proto::file_id(&world, ¶ms.text_document.uri)?; | 501 | let file_id = from_proto::file_id(&snap, ¶ms.text_document.uri)?; |
512 | let folds = world.analysis().folding_ranges(file_id)?; | 502 | let folds = snap.analysis().folding_ranges(file_id)?; |
513 | let text = world.analysis().file_text(file_id)?; | 503 | let text = snap.analysis().file_text(file_id)?; |
514 | let line_index = world.analysis().file_line_index(file_id)?; | 504 | let line_index = snap.analysis().file_line_index(file_id)?; |
515 | let line_folding_only = world.config.client_caps.line_folding_only; | 505 | let line_folding_only = snap.config.client_caps.line_folding_only; |
516 | let res = folds | 506 | let res = folds |
517 | .into_iter() | 507 | .into_iter() |
518 | .map(|it| to_proto::folding_range(&*text, &line_index, line_folding_only, it)) | 508 | .map(|it| to_proto::folding_range(&*text, &line_index, line_folding_only, it)) |
@@ -521,16 +511,16 @@ pub fn handle_folding_range( | |||
521 | } | 511 | } |
522 | 512 | ||
523 | pub fn handle_signature_help( | 513 | pub fn handle_signature_help( |
524 | world: WorldSnapshot, | 514 | snap: GlobalStateSnapshot, |
525 | params: lsp_types::SignatureHelpParams, | 515 | params: lsp_types::SignatureHelpParams, |
526 | ) -> Result<Option<lsp_types::SignatureHelp>> { | 516 | ) -> Result<Option<lsp_types::SignatureHelp>> { |
527 | let _p = profile("handle_signature_help"); | 517 | let _p = profile("handle_signature_help"); |
528 | let position = from_proto::file_position(&world, params.text_document_position_params)?; | 518 | let position = from_proto::file_position(&snap, params.text_document_position_params)?; |
529 | let call_info = match world.analysis().call_info(position)? { | 519 | let call_info = match snap.analysis().call_info(position)? { |
530 | None => return Ok(None), | 520 | None => return Ok(None), |
531 | Some(it) => it, | 521 | Some(it) => it, |
532 | }; | 522 | }; |
533 | let concise = !world.config.call_info_full; | 523 | let concise = !snap.config.call_info_full; |
534 | let mut active_parameter = call_info.active_parameter.map(|it| it as i64); | 524 | let mut active_parameter = call_info.active_parameter.map(|it| it as i64); |
535 | if concise && call_info.signature.has_self_param { | 525 | if concise && call_info.signature.has_self_param { |
536 | active_parameter = active_parameter.map(|it| it.saturating_sub(1)); | 526 | active_parameter = active_parameter.map(|it| it.saturating_sub(1)); |
@@ -544,14 +534,17 @@ pub fn handle_signature_help( | |||
544 | })) | 534 | })) |
545 | } | 535 | } |
546 | 536 | ||
547 | pub fn handle_hover(world: WorldSnapshot, params: lsp_types::HoverParams) -> Result<Option<Hover>> { | 537 | pub fn handle_hover( |
538 | snap: GlobalStateSnapshot, | ||
539 | params: lsp_types::HoverParams, | ||
540 | ) -> Result<Option<Hover>> { | ||
548 | let _p = profile("handle_hover"); | 541 | let _p = profile("handle_hover"); |
549 | let position = from_proto::file_position(&world, params.text_document_position_params)?; | 542 | let position = from_proto::file_position(&snap, params.text_document_position_params)?; |
550 | let info = match world.analysis().hover(position)? { | 543 | let info = match snap.analysis().hover(position)? { |
551 | None => return Ok(None), | 544 | None => return Ok(None), |
552 | Some(info) => info, | 545 | Some(info) => info, |
553 | }; | 546 | }; |
554 | let line_index = world.analysis.file_line_index(position.file_id)?; | 547 | let line_index = snap.analysis.file_line_index(position.file_id)?; |
555 | let range = to_proto::range(&line_index, info.range); | 548 | let range = to_proto::range(&line_index, info.range); |
556 | let res = Hover { | 549 | let res = Hover { |
557 | contents: HoverContents::Markup(MarkupContent { | 550 | contents: HoverContents::Markup(MarkupContent { |
@@ -564,26 +557,29 @@ pub fn handle_hover(world: WorldSnapshot, params: lsp_types::HoverParams) -> Res | |||
564 | } | 557 | } |
565 | 558 | ||
566 | pub fn handle_prepare_rename( | 559 | pub fn handle_prepare_rename( |
567 | world: WorldSnapshot, | 560 | snap: GlobalStateSnapshot, |
568 | params: lsp_types::TextDocumentPositionParams, | 561 | params: lsp_types::TextDocumentPositionParams, |
569 | ) -> Result<Option<PrepareRenameResponse>> { | 562 | ) -> Result<Option<PrepareRenameResponse>> { |
570 | let _p = profile("handle_prepare_rename"); | 563 | let _p = profile("handle_prepare_rename"); |
571 | let position = from_proto::file_position(&world, params)?; | 564 | let position = from_proto::file_position(&snap, params)?; |
572 | 565 | ||
573 | let optional_change = world.analysis().rename(position, "dummy")?; | 566 | let optional_change = snap.analysis().rename(position, "dummy")?; |
574 | let range = match optional_change { | 567 | let range = match optional_change { |
575 | None => return Ok(None), | 568 | None => return Ok(None), |
576 | Some(it) => it.range, | 569 | Some(it) => it.range, |
577 | }; | 570 | }; |
578 | 571 | ||
579 | let line_index = world.analysis().file_line_index(position.file_id)?; | 572 | let line_index = snap.analysis().file_line_index(position.file_id)?; |
580 | let range = to_proto::range(&line_index, range); | 573 | let range = to_proto::range(&line_index, range); |
581 | Ok(Some(PrepareRenameResponse::Range(range))) | 574 | Ok(Some(PrepareRenameResponse::Range(range))) |
582 | } | 575 | } |
583 | 576 | ||
584 | pub fn handle_rename(world: WorldSnapshot, params: RenameParams) -> Result<Option<WorkspaceEdit>> { | 577 | pub fn handle_rename( |
578 | snap: GlobalStateSnapshot, | ||
579 | params: RenameParams, | ||
580 | ) -> Result<Option<WorkspaceEdit>> { | ||
585 | let _p = profile("handle_rename"); | 581 | let _p = profile("handle_rename"); |
586 | let position = from_proto::file_position(&world, params.text_document_position)?; | 582 | let position = from_proto::file_position(&snap, params.text_document_position)?; |
587 | 583 | ||
588 | if params.new_name.is_empty() { | 584 | if params.new_name.is_empty() { |
589 | return Err(LspError::new( | 585 | return Err(LspError::new( |
@@ -593,36 +589,36 @@ pub fn handle_rename(world: WorldSnapshot, params: RenameParams) -> Result<Optio | |||
593 | .into()); | 589 | .into()); |
594 | } | 590 | } |
595 | 591 | ||
596 | let optional_change = world.analysis().rename(position, &*params.new_name)?; | 592 | let optional_change = snap.analysis().rename(position, &*params.new_name)?; |
597 | let source_change = match optional_change { | 593 | let source_change = match optional_change { |
598 | None => return Ok(None), | 594 | None => return Ok(None), |
599 | Some(it) => it.info, | 595 | Some(it) => it.info, |
600 | }; | 596 | }; |
601 | let workspace_edit = to_proto::workspace_edit(&world, source_change)?; | 597 | let workspace_edit = to_proto::workspace_edit(&snap, source_change)?; |
602 | Ok(Some(workspace_edit)) | 598 | Ok(Some(workspace_edit)) |
603 | } | 599 | } |
604 | 600 | ||
605 | pub fn handle_references( | 601 | pub fn handle_references( |
606 | world: WorldSnapshot, | 602 | snap: GlobalStateSnapshot, |
607 | params: lsp_types::ReferenceParams, | 603 | params: lsp_types::ReferenceParams, |
608 | ) -> Result<Option<Vec<Location>>> { | 604 | ) -> Result<Option<Vec<Location>>> { |
609 | let _p = profile("handle_references"); | 605 | let _p = profile("handle_references"); |
610 | let position = from_proto::file_position(&world, params.text_document_position)?; | 606 | let position = from_proto::file_position(&snap, params.text_document_position)?; |
611 | 607 | ||
612 | let refs = match world.analysis().find_all_refs(position, None)? { | 608 | let refs = match snap.analysis().find_all_refs(position, None)? { |
613 | None => return Ok(None), | 609 | None => return Ok(None), |
614 | Some(refs) => refs, | 610 | Some(refs) => refs, |
615 | }; | 611 | }; |
616 | 612 | ||
617 | let locations = if params.context.include_declaration { | 613 | let locations = if params.context.include_declaration { |
618 | refs.into_iter() | 614 | refs.into_iter() |
619 | .filter_map(|reference| to_proto::location(&world, reference.file_range).ok()) | 615 | .filter_map(|reference| to_proto::location(&snap, reference.file_range).ok()) |
620 | .collect() | 616 | .collect() |
621 | } else { | 617 | } else { |
622 | // Only iterate over the references if include_declaration was false | 618 | // Only iterate over the references if include_declaration was false |
623 | refs.references() | 619 | refs.references() |
624 | .iter() | 620 | .iter() |
625 | .filter_map(|reference| to_proto::location(&world, reference.file_range).ok()) | 621 | .filter_map(|reference| to_proto::location(&snap, reference.file_range).ok()) |
626 | .collect() | 622 | .collect() |
627 | }; | 623 | }; |
628 | 624 | ||
@@ -630,24 +626,24 @@ pub fn handle_references( | |||
630 | } | 626 | } |
631 | 627 | ||
632 | pub fn handle_formatting( | 628 | pub fn handle_formatting( |
633 | world: WorldSnapshot, | 629 | snap: GlobalStateSnapshot, |
634 | params: DocumentFormattingParams, | 630 | params: DocumentFormattingParams, |
635 | ) -> Result<Option<Vec<lsp_types::TextEdit>>> { | 631 | ) -> Result<Option<Vec<lsp_types::TextEdit>>> { |
636 | let _p = profile("handle_formatting"); | 632 | let _p = profile("handle_formatting"); |
637 | let file_id = from_proto::file_id(&world, ¶ms.text_document.uri)?; | 633 | let file_id = from_proto::file_id(&snap, ¶ms.text_document.uri)?; |
638 | let file = world.analysis().file_text(file_id)?; | 634 | let file = snap.analysis().file_text(file_id)?; |
639 | let crate_ids = world.analysis().crate_for(file_id)?; | 635 | let crate_ids = snap.analysis().crate_for(file_id)?; |
640 | 636 | ||
641 | let file_line_index = world.analysis().file_line_index(file_id)?; | 637 | let file_line_index = snap.analysis().file_line_index(file_id)?; |
642 | let end_position = to_proto::position(&file_line_index, TextSize::of(file.as_str())); | 638 | let end_position = to_proto::position(&file_line_index, TextSize::of(file.as_str())); |
643 | 639 | ||
644 | let mut rustfmt = match &world.config.rustfmt { | 640 | let mut rustfmt = match &snap.config.rustfmt { |
645 | RustfmtConfig::Rustfmt { extra_args } => { | 641 | RustfmtConfig::Rustfmt { extra_args } => { |
646 | let mut cmd = process::Command::new("rustfmt"); | 642 | let mut cmd = process::Command::new("rustfmt"); |
647 | cmd.args(extra_args); | 643 | cmd.args(extra_args); |
648 | if let Some(&crate_id) = crate_ids.first() { | 644 | if let Some(&crate_id) = crate_ids.first() { |
649 | // Assume all crates are in the same edition | 645 | // Assume all crates are in the same edition |
650 | let edition = world.analysis().crate_edition(crate_id)?; | 646 | let edition = snap.analysis().crate_edition(crate_id)?; |
651 | cmd.arg("--edition"); | 647 | cmd.arg("--edition"); |
652 | cmd.arg(edition.to_string()); | 648 | cmd.arg(edition.to_string()); |
653 | } | 649 | } |
@@ -706,23 +702,23 @@ pub fn handle_formatting( | |||
706 | } | 702 | } |
707 | 703 | ||
708 | pub fn handle_code_action( | 704 | pub fn handle_code_action( |
709 | world: WorldSnapshot, | 705 | snap: GlobalStateSnapshot, |
710 | params: lsp_types::CodeActionParams, | 706 | params: lsp_types::CodeActionParams, |
711 | ) -> Result<Option<Vec<lsp_ext::CodeAction>>> { | 707 | ) -> Result<Option<Vec<lsp_ext::CodeAction>>> { |
712 | let _p = profile("handle_code_action"); | 708 | let _p = profile("handle_code_action"); |
713 | // We intentionally don't support command-based actions, as those either | 709 | // We intentionally don't support command-based actions, as those either |
714 | // requires custom client-code anyway, or requires server-initiated edits. | 710 | // requires custom client-code anyway, or requires server-initiated edits. |
715 | // Server initiated edits break causality, so we avoid those as well. | 711 | // Server initiated edits break causality, so we avoid those as well. |
716 | if !world.config.client_caps.code_action_literals { | 712 | if !snap.config.client_caps.code_action_literals { |
717 | return Ok(None); | 713 | return Ok(None); |
718 | } | 714 | } |
719 | 715 | ||
720 | let file_id = from_proto::file_id(&world, ¶ms.text_document.uri)?; | 716 | let file_id = from_proto::file_id(&snap, ¶ms.text_document.uri)?; |
721 | let line_index = world.analysis().file_line_index(file_id)?; | 717 | let line_index = snap.analysis().file_line_index(file_id)?; |
722 | let range = from_proto::text_range(&line_index, params.range); | 718 | let range = from_proto::text_range(&line_index, params.range); |
723 | let frange = FileRange { file_id, range }; | 719 | let frange = FileRange { file_id, range }; |
724 | 720 | ||
725 | let diagnostics = world.analysis().diagnostics(file_id)?; | 721 | let diagnostics = snap.analysis().diagnostics(file_id)?; |
726 | let mut res: Vec<lsp_ext::CodeAction> = Vec::new(); | 722 | let mut res: Vec<lsp_ext::CodeAction> = Vec::new(); |
727 | 723 | ||
728 | let fixes_from_diagnostics = diagnostics | 724 | let fixes_from_diagnostics = diagnostics |
@@ -733,13 +729,18 @@ pub fn handle_code_action( | |||
733 | 729 | ||
734 | for fix in fixes_from_diagnostics { | 730 | for fix in fixes_from_diagnostics { |
735 | let title = fix.label; | 731 | let title = fix.label; |
736 | let edit = to_proto::snippet_workspace_edit(&world, fix.source_change)?; | 732 | let edit = to_proto::snippet_workspace_edit(&snap, fix.source_change)?; |
737 | let action = | 733 | let action = lsp_ext::CodeAction { |
738 | lsp_ext::CodeAction { title, group: None, kind: None, edit: Some(edit), command: None }; | 734 | title, |
735 | group: None, | ||
736 | kind: Some(lsp_types::code_action_kind::QUICKFIX.into()), | ||
737 | edit: Some(edit), | ||
738 | command: None, | ||
739 | }; | ||
739 | res.push(action); | 740 | res.push(action); |
740 | } | 741 | } |
741 | 742 | ||
742 | for fix in world.check_fixes.get(&file_id).into_iter().flatten() { | 743 | for fix in snap.check_fixes.get(&file_id).into_iter().flatten() { |
743 | let fix_range = from_proto::text_range(&line_index, fix.range); | 744 | let fix_range = from_proto::text_range(&line_index, fix.range); |
744 | if fix_range.intersect(range).is_none() { | 745 | if fix_range.intersect(range).is_none() { |
745 | continue; | 746 | continue; |
@@ -747,31 +748,31 @@ pub fn handle_code_action( | |||
747 | res.push(fix.action.clone()); | 748 | res.push(fix.action.clone()); |
748 | } | 749 | } |
749 | 750 | ||
750 | for assist in world.analysis().assists(&world.config.assist, frange)?.into_iter() { | 751 | for assist in snap.analysis().assists(&snap.config.assist, frange)?.into_iter() { |
751 | res.push(to_proto::code_action(&world, assist)?.into()); | 752 | res.push(to_proto::code_action(&snap, assist)?.into()); |
752 | } | 753 | } |
753 | Ok(Some(res)) | 754 | Ok(Some(res)) |
754 | } | 755 | } |
755 | 756 | ||
756 | pub fn handle_code_lens( | 757 | pub fn handle_code_lens( |
757 | world: WorldSnapshot, | 758 | snap: GlobalStateSnapshot, |
758 | params: lsp_types::CodeLensParams, | 759 | params: lsp_types::CodeLensParams, |
759 | ) -> Result<Option<Vec<CodeLens>>> { | 760 | ) -> Result<Option<Vec<CodeLens>>> { |
760 | let _p = profile("handle_code_lens"); | 761 | let _p = profile("handle_code_lens"); |
761 | let mut lenses: Vec<CodeLens> = Default::default(); | 762 | let mut lenses: Vec<CodeLens> = Default::default(); |
762 | 763 | ||
763 | if world.config.lens.none() { | 764 | if snap.config.lens.none() { |
764 | // early return before any db query! | 765 | // early return before any db query! |
765 | return Ok(Some(lenses)); | 766 | return Ok(Some(lenses)); |
766 | } | 767 | } |
767 | 768 | ||
768 | let file_id = from_proto::file_id(&world, ¶ms.text_document.uri)?; | 769 | let file_id = from_proto::file_id(&snap, ¶ms.text_document.uri)?; |
769 | let line_index = world.analysis().file_line_index(file_id)?; | 770 | let line_index = snap.analysis().file_line_index(file_id)?; |
770 | let cargo_spec = CargoTargetSpec::for_file(&world, file_id)?; | 771 | let cargo_spec = CargoTargetSpec::for_file(&snap, file_id)?; |
771 | 772 | ||
772 | if world.config.lens.runnable() { | 773 | if snap.config.lens.runnable() { |
773 | // Gather runnables | 774 | // Gather runnables |
774 | for runnable in world.analysis().runnables(file_id)? { | 775 | for runnable in snap.analysis().runnables(file_id)? { |
775 | let (run_title, debugee) = match &runnable.kind { | 776 | let (run_title, debugee) = match &runnable.kind { |
776 | RunnableKind::Test { .. } | RunnableKind::TestMod { .. } => { | 777 | RunnableKind::Test { .. } | RunnableKind::TestMod { .. } => { |
777 | ("▶\u{fe0e} Run Test", true) | 778 | ("▶\u{fe0e} Run Test", true) |
@@ -796,10 +797,11 @@ pub fn handle_code_lens( | |||
796 | } | 797 | } |
797 | }; | 798 | }; |
798 | 799 | ||
799 | let mut r = to_lsp_runnable(&world, file_id, runnable)?; | 800 | let range = to_proto::range(&line_index, runnable.nav.range()); |
800 | if world.config.lens.run { | 801 | let r = to_proto::runnable(&snap, file_id, runnable)?; |
802 | if snap.config.lens.run { | ||
801 | let lens = CodeLens { | 803 | let lens = CodeLens { |
802 | range: r.range, | 804 | range, |
803 | command: Some(Command { | 805 | command: Some(Command { |
804 | title: run_title.to_string(), | 806 | title: run_title.to_string(), |
805 | command: "rust-analyzer.runSingle".into(), | 807 | command: "rust-analyzer.runSingle".into(), |
@@ -810,14 +812,9 @@ pub fn handle_code_lens( | |||
810 | lenses.push(lens); | 812 | lenses.push(lens); |
811 | } | 813 | } |
812 | 814 | ||
813 | if debugee && world.config.lens.debug { | 815 | if debugee && snap.config.lens.debug { |
814 | if r.args[0] == "run" { | ||
815 | r.args[0] = "build".into(); | ||
816 | } else { | ||
817 | r.args.push("--no-run".into()); | ||
818 | } | ||
819 | let debug_lens = CodeLens { | 816 | let debug_lens = CodeLens { |
820 | range: r.range, | 817 | range, |
821 | command: Some(Command { | 818 | command: Some(Command { |
822 | title: "Debug".into(), | 819 | title: "Debug".into(), |
823 | command: "rust-analyzer.debugSingle".into(), | 820 | command: "rust-analyzer.debugSingle".into(), |
@@ -830,11 +827,10 @@ pub fn handle_code_lens( | |||
830 | } | 827 | } |
831 | } | 828 | } |
832 | 829 | ||
833 | if world.config.lens.impementations { | 830 | if snap.config.lens.impementations { |
834 | // Handle impls | 831 | // Handle impls |
835 | lenses.extend( | 832 | lenses.extend( |
836 | world | 833 | snap.analysis() |
837 | .analysis() | ||
838 | .file_structure(file_id)? | 834 | .file_structure(file_id)? |
839 | .into_iter() | 835 | .into_iter() |
840 | .filter(|it| match it.kind { | 836 | .filter(|it| match it.kind { |
@@ -869,14 +865,17 @@ enum CodeLensResolveData { | |||
869 | Impls(lsp_types::request::GotoImplementationParams), | 865 | Impls(lsp_types::request::GotoImplementationParams), |
870 | } | 866 | } |
871 | 867 | ||
872 | pub fn handle_code_lens_resolve(world: WorldSnapshot, code_lens: CodeLens) -> Result<CodeLens> { | 868 | pub fn handle_code_lens_resolve( |
869 | snap: GlobalStateSnapshot, | ||
870 | code_lens: CodeLens, | ||
871 | ) -> Result<CodeLens> { | ||
873 | let _p = profile("handle_code_lens_resolve"); | 872 | let _p = profile("handle_code_lens_resolve"); |
874 | let data = code_lens.data.unwrap(); | 873 | let data = code_lens.data.unwrap(); |
875 | let resolve = from_json::<Option<CodeLensResolveData>>("CodeLensResolveData", data)?; | 874 | let resolve = from_json::<Option<CodeLensResolveData>>("CodeLensResolveData", data)?; |
876 | match resolve { | 875 | match resolve { |
877 | Some(CodeLensResolveData::Impls(lens_params)) => { | 876 | Some(CodeLensResolveData::Impls(lens_params)) => { |
878 | let locations: Vec<Location> = | 877 | let locations: Vec<Location> = |
879 | match handle_goto_implementation(world, lens_params.clone())? { | 878 | match handle_goto_implementation(snap, lens_params.clone())? { |
880 | Some(lsp_types::GotoDefinitionResponse::Scalar(loc)) => vec![loc], | 879 | Some(lsp_types::GotoDefinitionResponse::Scalar(loc)) => vec![loc], |
881 | Some(lsp_types::GotoDefinitionResponse::Array(locs)) => locs, | 880 | Some(lsp_types::GotoDefinitionResponse::Array(locs)) => locs, |
882 | Some(lsp_types::GotoDefinitionResponse::Link(links)) => links | 881 | Some(lsp_types::GotoDefinitionResponse::Link(links)) => links |
@@ -915,14 +914,14 @@ pub fn handle_code_lens_resolve(world: WorldSnapshot, code_lens: CodeLens) -> Re | |||
915 | } | 914 | } |
916 | 915 | ||
917 | pub fn handle_document_highlight( | 916 | pub fn handle_document_highlight( |
918 | world: WorldSnapshot, | 917 | snap: GlobalStateSnapshot, |
919 | params: lsp_types::DocumentHighlightParams, | 918 | params: lsp_types::DocumentHighlightParams, |
920 | ) -> Result<Option<Vec<DocumentHighlight>>> { | 919 | ) -> Result<Option<Vec<DocumentHighlight>>> { |
921 | let _p = profile("handle_document_highlight"); | 920 | let _p = profile("handle_document_highlight"); |
922 | let position = from_proto::file_position(&world, params.text_document_position_params)?; | 921 | let position = from_proto::file_position(&snap, params.text_document_position_params)?; |
923 | let line_index = world.analysis().file_line_index(position.file_id)?; | 922 | let line_index = snap.analysis().file_line_index(position.file_id)?; |
924 | 923 | ||
925 | let refs = match world | 924 | let refs = match snap |
926 | .analysis() | 925 | .analysis() |
927 | .find_all_refs(position, Some(SearchScope::single_file(position.file_id)))? | 926 | .find_all_refs(position, Some(SearchScope::single_file(position.file_id)))? |
928 | { | 927 | { |
@@ -942,19 +941,19 @@ pub fn handle_document_highlight( | |||
942 | } | 941 | } |
943 | 942 | ||
944 | pub fn handle_ssr( | 943 | pub fn handle_ssr( |
945 | world: WorldSnapshot, | 944 | snap: GlobalStateSnapshot, |
946 | params: lsp_ext::SsrParams, | 945 | params: lsp_ext::SsrParams, |
947 | ) -> Result<lsp_types::WorkspaceEdit> { | 946 | ) -> Result<lsp_types::WorkspaceEdit> { |
948 | let _p = profile("handle_ssr"); | 947 | let _p = profile("handle_ssr"); |
949 | let source_change = | 948 | let source_change = |
950 | world.analysis().structural_search_replace(¶ms.query, params.parse_only)??; | 949 | snap.analysis().structural_search_replace(¶ms.query, params.parse_only)??; |
951 | to_proto::workspace_edit(&world, source_change) | 950 | to_proto::workspace_edit(&snap, source_change) |
952 | } | 951 | } |
953 | 952 | ||
954 | pub fn publish_diagnostics(world: &WorldSnapshot, file_id: FileId) -> Result<DiagnosticTask> { | 953 | pub fn publish_diagnostics(snap: &GlobalStateSnapshot, file_id: FileId) -> Result<DiagnosticTask> { |
955 | let _p = profile("publish_diagnostics"); | 954 | let _p = profile("publish_diagnostics"); |
956 | let line_index = world.analysis().file_line_index(file_id)?; | 955 | let line_index = snap.analysis().file_line_index(file_id)?; |
957 | let diagnostics: Vec<Diagnostic> = world | 956 | let diagnostics: Vec<Diagnostic> = snap |
958 | .analysis() | 957 | .analysis() |
959 | .diagnostics(file_id)? | 958 | .diagnostics(file_id)? |
960 | .into_iter() | 959 | .into_iter() |
@@ -971,62 +970,29 @@ pub fn publish_diagnostics(world: &WorldSnapshot, file_id: FileId) -> Result<Dia | |||
971 | Ok(DiagnosticTask::SetNative(file_id, diagnostics)) | 970 | Ok(DiagnosticTask::SetNative(file_id, diagnostics)) |
972 | } | 971 | } |
973 | 972 | ||
974 | fn to_lsp_runnable( | ||
975 | world: &WorldSnapshot, | ||
976 | file_id: FileId, | ||
977 | runnable: Runnable, | ||
978 | ) -> Result<lsp_ext::Runnable> { | ||
979 | let spec = CargoTargetSpec::for_file(world, file_id)?; | ||
980 | let target = spec.as_ref().map(|s| s.target.clone()); | ||
981 | let (args, extra_args) = CargoTargetSpec::runnable_args(spec, &runnable.kind)?; | ||
982 | let line_index = world.analysis().file_line_index(file_id)?; | ||
983 | let label = match &runnable.kind { | ||
984 | RunnableKind::Test { test_id, .. } => format!("test {}", test_id), | ||
985 | RunnableKind::TestMod { path } => format!("test-mod {}", path), | ||
986 | RunnableKind::Bench { test_id } => format!("bench {}", test_id), | ||
987 | RunnableKind::DocTest { test_id, .. } => format!("doctest {}", test_id), | ||
988 | RunnableKind::Bin => { | ||
989 | target.map_or_else(|| "run binary".to_string(), |t| format!("run {}", t)) | ||
990 | } | ||
991 | }; | ||
992 | Ok(lsp_ext::Runnable { | ||
993 | range: to_proto::range(&line_index, runnable.range), | ||
994 | label, | ||
995 | bin: "cargo".to_string(), | ||
996 | args, | ||
997 | extra_args, | ||
998 | env: { | ||
999 | let mut m = FxHashMap::default(); | ||
1000 | m.insert("RUST_BACKTRACE".to_string(), "short".to_string()); | ||
1001 | m | ||
1002 | }, | ||
1003 | cwd: world.workspace_root_for(file_id).map(|root| root.to_owned()), | ||
1004 | }) | ||
1005 | } | ||
1006 | |||
1007 | pub fn handle_inlay_hints( | 973 | pub fn handle_inlay_hints( |
1008 | world: WorldSnapshot, | 974 | snap: GlobalStateSnapshot, |
1009 | params: InlayHintsParams, | 975 | params: InlayHintsParams, |
1010 | ) -> Result<Vec<InlayHint>> { | 976 | ) -> Result<Vec<InlayHint>> { |
1011 | let _p = profile("handle_inlay_hints"); | 977 | let _p = profile("handle_inlay_hints"); |
1012 | let file_id = from_proto::file_id(&world, ¶ms.text_document.uri)?; | 978 | let file_id = from_proto::file_id(&snap, ¶ms.text_document.uri)?; |
1013 | let analysis = world.analysis(); | 979 | let analysis = snap.analysis(); |
1014 | let line_index = analysis.file_line_index(file_id)?; | 980 | let line_index = analysis.file_line_index(file_id)?; |
1015 | Ok(analysis | 981 | Ok(analysis |
1016 | .inlay_hints(file_id, &world.config.inlay_hints)? | 982 | .inlay_hints(file_id, &snap.config.inlay_hints)? |
1017 | .into_iter() | 983 | .into_iter() |
1018 | .map(|it| to_proto::inlay_int(&line_index, it)) | 984 | .map(|it| to_proto::inlay_int(&line_index, it)) |
1019 | .collect()) | 985 | .collect()) |
1020 | } | 986 | } |
1021 | 987 | ||
1022 | pub fn handle_call_hierarchy_prepare( | 988 | pub fn handle_call_hierarchy_prepare( |
1023 | world: WorldSnapshot, | 989 | snap: GlobalStateSnapshot, |
1024 | params: CallHierarchyPrepareParams, | 990 | params: CallHierarchyPrepareParams, |
1025 | ) -> Result<Option<Vec<CallHierarchyItem>>> { | 991 | ) -> Result<Option<Vec<CallHierarchyItem>>> { |
1026 | let _p = profile("handle_call_hierarchy_prepare"); | 992 | let _p = profile("handle_call_hierarchy_prepare"); |
1027 | let position = from_proto::file_position(&world, params.text_document_position_params)?; | 993 | let position = from_proto::file_position(&snap, params.text_document_position_params)?; |
1028 | 994 | ||
1029 | let nav_info = match world.analysis().call_hierarchy(position)? { | 995 | let nav_info = match snap.analysis().call_hierarchy(position)? { |
1030 | None => return Ok(None), | 996 | None => return Ok(None), |
1031 | Some(it) => it, | 997 | Some(it) => it, |
1032 | }; | 998 | }; |
@@ -1035,24 +1001,24 @@ pub fn handle_call_hierarchy_prepare( | |||
1035 | let res = navs | 1001 | let res = navs |
1036 | .into_iter() | 1002 | .into_iter() |
1037 | .filter(|it| it.kind() == SyntaxKind::FN_DEF) | 1003 | .filter(|it| it.kind() == SyntaxKind::FN_DEF) |
1038 | .map(|it| to_proto::call_hierarchy_item(&world, it)) | 1004 | .map(|it| to_proto::call_hierarchy_item(&snap, it)) |
1039 | .collect::<Result<Vec<_>>>()?; | 1005 | .collect::<Result<Vec<_>>>()?; |
1040 | 1006 | ||
1041 | Ok(Some(res)) | 1007 | Ok(Some(res)) |
1042 | } | 1008 | } |
1043 | 1009 | ||
1044 | pub fn handle_call_hierarchy_incoming( | 1010 | pub fn handle_call_hierarchy_incoming( |
1045 | world: WorldSnapshot, | 1011 | snap: GlobalStateSnapshot, |
1046 | params: CallHierarchyIncomingCallsParams, | 1012 | params: CallHierarchyIncomingCallsParams, |
1047 | ) -> Result<Option<Vec<CallHierarchyIncomingCall>>> { | 1013 | ) -> Result<Option<Vec<CallHierarchyIncomingCall>>> { |
1048 | let _p = profile("handle_call_hierarchy_incoming"); | 1014 | let _p = profile("handle_call_hierarchy_incoming"); |
1049 | let item = params.item; | 1015 | let item = params.item; |
1050 | 1016 | ||
1051 | let doc = TextDocumentIdentifier::new(item.uri); | 1017 | let doc = TextDocumentIdentifier::new(item.uri); |
1052 | let frange = from_proto::file_range(&world, doc, item.range)?; | 1018 | let frange = from_proto::file_range(&snap, doc, item.range)?; |
1053 | let fpos = FilePosition { file_id: frange.file_id, offset: frange.range.start() }; | 1019 | let fpos = FilePosition { file_id: frange.file_id, offset: frange.range.start() }; |
1054 | 1020 | ||
1055 | let call_items = match world.analysis().incoming_calls(fpos)? { | 1021 | let call_items = match snap.analysis().incoming_calls(fpos)? { |
1056 | None => return Ok(None), | 1022 | None => return Ok(None), |
1057 | Some(it) => it, | 1023 | Some(it) => it, |
1058 | }; | 1024 | }; |
@@ -1061,8 +1027,8 @@ pub fn handle_call_hierarchy_incoming( | |||
1061 | 1027 | ||
1062 | for call_item in call_items.into_iter() { | 1028 | for call_item in call_items.into_iter() { |
1063 | let file_id = call_item.target.file_id(); | 1029 | let file_id = call_item.target.file_id(); |
1064 | let line_index = world.analysis().file_line_index(file_id)?; | 1030 | let line_index = snap.analysis().file_line_index(file_id)?; |
1065 | let item = to_proto::call_hierarchy_item(&world, call_item.target)?; | 1031 | let item = to_proto::call_hierarchy_item(&snap, call_item.target)?; |
1066 | res.push(CallHierarchyIncomingCall { | 1032 | res.push(CallHierarchyIncomingCall { |
1067 | from: item, | 1033 | from: item, |
1068 | from_ranges: call_item | 1034 | from_ranges: call_item |
@@ -1077,17 +1043,17 @@ pub fn handle_call_hierarchy_incoming( | |||
1077 | } | 1043 | } |
1078 | 1044 | ||
1079 | pub fn handle_call_hierarchy_outgoing( | 1045 | pub fn handle_call_hierarchy_outgoing( |
1080 | world: WorldSnapshot, | 1046 | snap: GlobalStateSnapshot, |
1081 | params: CallHierarchyOutgoingCallsParams, | 1047 | params: CallHierarchyOutgoingCallsParams, |
1082 | ) -> Result<Option<Vec<CallHierarchyOutgoingCall>>> { | 1048 | ) -> Result<Option<Vec<CallHierarchyOutgoingCall>>> { |
1083 | let _p = profile("handle_call_hierarchy_outgoing"); | 1049 | let _p = profile("handle_call_hierarchy_outgoing"); |
1084 | let item = params.item; | 1050 | let item = params.item; |
1085 | 1051 | ||
1086 | let doc = TextDocumentIdentifier::new(item.uri); | 1052 | let doc = TextDocumentIdentifier::new(item.uri); |
1087 | let frange = from_proto::file_range(&world, doc, item.range)?; | 1053 | let frange = from_proto::file_range(&snap, doc, item.range)?; |
1088 | let fpos = FilePosition { file_id: frange.file_id, offset: frange.range.start() }; | 1054 | let fpos = FilePosition { file_id: frange.file_id, offset: frange.range.start() }; |
1089 | 1055 | ||
1090 | let call_items = match world.analysis().outgoing_calls(fpos)? { | 1056 | let call_items = match snap.analysis().outgoing_calls(fpos)? { |
1091 | None => return Ok(None), | 1057 | None => return Ok(None), |
1092 | Some(it) => it, | 1058 | Some(it) => it, |
1093 | }; | 1059 | }; |
@@ -1096,8 +1062,8 @@ pub fn handle_call_hierarchy_outgoing( | |||
1096 | 1062 | ||
1097 | for call_item in call_items.into_iter() { | 1063 | for call_item in call_items.into_iter() { |
1098 | let file_id = call_item.target.file_id(); | 1064 | let file_id = call_item.target.file_id(); |
1099 | let line_index = world.analysis().file_line_index(file_id)?; | 1065 | let line_index = snap.analysis().file_line_index(file_id)?; |
1100 | let item = to_proto::call_hierarchy_item(&world, call_item.target)?; | 1066 | let item = to_proto::call_hierarchy_item(&snap, call_item.target)?; |
1101 | res.push(CallHierarchyOutgoingCall { | 1067 | res.push(CallHierarchyOutgoingCall { |
1102 | to: item, | 1068 | to: item, |
1103 | from_ranges: call_item | 1069 | from_ranges: call_item |
@@ -1112,31 +1078,31 @@ pub fn handle_call_hierarchy_outgoing( | |||
1112 | } | 1078 | } |
1113 | 1079 | ||
1114 | pub fn handle_semantic_tokens( | 1080 | pub fn handle_semantic_tokens( |
1115 | world: WorldSnapshot, | 1081 | snap: GlobalStateSnapshot, |
1116 | params: SemanticTokensParams, | 1082 | params: SemanticTokensParams, |
1117 | ) -> Result<Option<SemanticTokensResult>> { | 1083 | ) -> Result<Option<SemanticTokensResult>> { |
1118 | let _p = profile("handle_semantic_tokens"); | 1084 | let _p = profile("handle_semantic_tokens"); |
1119 | 1085 | ||
1120 | let file_id = from_proto::file_id(&world, ¶ms.text_document.uri)?; | 1086 | let file_id = from_proto::file_id(&snap, ¶ms.text_document.uri)?; |
1121 | let text = world.analysis().file_text(file_id)?; | 1087 | let text = snap.analysis().file_text(file_id)?; |
1122 | let line_index = world.analysis().file_line_index(file_id)?; | 1088 | let line_index = snap.analysis().file_line_index(file_id)?; |
1123 | 1089 | ||
1124 | let highlights = world.analysis().highlight(file_id)?; | 1090 | let highlights = snap.analysis().highlight(file_id)?; |
1125 | let semantic_tokens = to_proto::semantic_tokens(&text, &line_index, highlights); | 1091 | let semantic_tokens = to_proto::semantic_tokens(&text, &line_index, highlights); |
1126 | Ok(Some(semantic_tokens.into())) | 1092 | Ok(Some(semantic_tokens.into())) |
1127 | } | 1093 | } |
1128 | 1094 | ||
1129 | pub fn handle_semantic_tokens_range( | 1095 | pub fn handle_semantic_tokens_range( |
1130 | world: WorldSnapshot, | 1096 | snap: GlobalStateSnapshot, |
1131 | params: SemanticTokensRangeParams, | 1097 | params: SemanticTokensRangeParams, |
1132 | ) -> Result<Option<SemanticTokensRangeResult>> { | 1098 | ) -> Result<Option<SemanticTokensRangeResult>> { |
1133 | let _p = profile("handle_semantic_tokens_range"); | 1099 | let _p = profile("handle_semantic_tokens_range"); |
1134 | 1100 | ||
1135 | let frange = from_proto::file_range(&world, params.text_document, params.range)?; | 1101 | let frange = from_proto::file_range(&snap, params.text_document, params.range)?; |
1136 | let text = world.analysis().file_text(frange.file_id)?; | 1102 | let text = snap.analysis().file_text(frange.file_id)?; |
1137 | let line_index = world.analysis().file_line_index(frange.file_id)?; | 1103 | let line_index = snap.analysis().file_line_index(frange.file_id)?; |
1138 | 1104 | ||
1139 | let highlights = world.analysis().highlight_range(frange)?; | 1105 | let highlights = snap.analysis().highlight_range(frange)?; |
1140 | let semantic_tokens = to_proto::semantic_tokens(&text, &line_index, highlights); | 1106 | let semantic_tokens = to_proto::semantic_tokens(&text, &line_index, highlights); |
1141 | Ok(Some(semantic_tokens.into())) | 1107 | Ok(Some(semantic_tokens.into())) |
1142 | } | 1108 | } |
diff --git a/crates/rust-analyzer/src/semantic_tokens.rs b/crates/rust-analyzer/src/semantic_tokens.rs index 2dc5cb119..6f125c37c 100644 --- a/crates/rust-analyzer/src/semantic_tokens.rs +++ b/crates/rust-analyzer/src/semantic_tokens.rs | |||
@@ -36,9 +36,11 @@ macro_rules! define_semantic_token_types { | |||
36 | 36 | ||
37 | define_semantic_token_types![ | 37 | define_semantic_token_types![ |
38 | (ATTRIBUTE, "attribute"), | 38 | (ATTRIBUTE, "attribute"), |
39 | (BOOLEAN, "boolean"), | ||
39 | (BUILTIN_TYPE, "builtinType"), | 40 | (BUILTIN_TYPE, "builtinType"), |
40 | (ENUM_MEMBER, "enumMember"), | 41 | (ENUM_MEMBER, "enumMember"), |
41 | (LIFETIME, "lifetime"), | 42 | (LIFETIME, "lifetime"), |
43 | (SELF_KEYWORD, "selfKeyword"), | ||
42 | (TYPE_ALIAS, "typeAlias"), | 44 | (TYPE_ALIAS, "typeAlias"), |
43 | (UNION, "union"), | 45 | (UNION, "union"), |
44 | (UNRESOLVED_REFERENCE, "unresolvedReference"), | 46 | (UNRESOLVED_REFERENCE, "unresolvedReference"), |
@@ -67,6 +69,7 @@ define_semantic_token_modifiers![ | |||
67 | (CONTROL_FLOW, "controlFlow"), | 69 | (CONTROL_FLOW, "controlFlow"), |
68 | (MUTABLE, "mutable"), | 70 | (MUTABLE, "mutable"), |
69 | (UNSAFE, "unsafe"), | 71 | (UNSAFE, "unsafe"), |
72 | (ATTRIBUTE_MODIFIER, "attribute"), | ||
70 | ]; | 73 | ]; |
71 | 74 | ||
72 | #[derive(Default)] | 75 | #[derive(Default)] |
diff --git a/crates/rust-analyzer/src/to_proto.rs b/crates/rust-analyzer/src/to_proto.rs index 461944ada..0915a7fcb 100644 --- a/crates/rust-analyzer/src/to_proto.rs +++ b/crates/rust-analyzer/src/to_proto.rs | |||
@@ -3,13 +3,16 @@ use ra_db::{FileId, FileRange}; | |||
3 | use ra_ide::{ | 3 | use ra_ide::{ |
4 | Assist, CompletionItem, CompletionItemKind, Documentation, FileSystemEdit, Fold, FoldKind, | 4 | Assist, CompletionItem, CompletionItemKind, Documentation, FileSystemEdit, Fold, FoldKind, |
5 | FunctionSignature, Highlight, HighlightModifier, HighlightTag, HighlightedRange, Indel, | 5 | FunctionSignature, Highlight, HighlightModifier, HighlightTag, HighlightedRange, Indel, |
6 | InlayHint, InlayKind, InsertTextFormat, LineIndex, NavigationTarget, ReferenceAccess, Severity, | 6 | InlayHint, InlayKind, InsertTextFormat, LineIndex, NavigationTarget, ReferenceAccess, Runnable, |
7 | SourceChange, SourceFileEdit, TextEdit, | 7 | RunnableKind, Severity, SourceChange, SourceFileEdit, TextEdit, |
8 | }; | 8 | }; |
9 | use ra_syntax::{SyntaxKind, TextRange, TextSize}; | 9 | use ra_syntax::{SyntaxKind, TextRange, TextSize}; |
10 | use ra_vfs::LineEndings; | 10 | use ra_vfs::LineEndings; |
11 | 11 | ||
12 | use crate::{lsp_ext, semantic_tokens, world::WorldSnapshot, Result}; | 12 | use crate::{ |
13 | cargo_target_spec::CargoTargetSpec, global_state::GlobalStateSnapshot, lsp_ext, | ||
14 | semantic_tokens, Result, | ||
15 | }; | ||
13 | 16 | ||
14 | pub(crate) fn position(line_index: &LineIndex, offset: TextSize) -> lsp_types::Position { | 17 | pub(crate) fn position(line_index: &LineIndex, offset: TextSize) -> lsp_types::Position { |
15 | let line_col = line_index.line_col(offset); | 18 | let line_col = line_index.line_col(offset); |
@@ -135,6 +138,18 @@ pub(crate) fn text_edit_vec( | |||
135 | text_edit.into_iter().map(|indel| self::text_edit(line_index, line_endings, indel)).collect() | 138 | text_edit.into_iter().map(|indel| self::text_edit(line_index, line_endings, indel)).collect() |
136 | } | 139 | } |
137 | 140 | ||
141 | pub(crate) fn snippet_text_edit_vec( | ||
142 | line_index: &LineIndex, | ||
143 | line_endings: LineEndings, | ||
144 | is_snippet: bool, | ||
145 | text_edit: TextEdit, | ||
146 | ) -> Vec<lsp_ext::SnippetTextEdit> { | ||
147 | text_edit | ||
148 | .into_iter() | ||
149 | .map(|indel| self::snippet_text_edit(line_index, line_endings, is_snippet, indel)) | ||
150 | .collect() | ||
151 | } | ||
152 | |||
138 | pub(crate) fn completion_item( | 153 | pub(crate) fn completion_item( |
139 | line_index: &LineIndex, | 154 | line_index: &LineIndex, |
140 | line_endings: LineEndings, | 155 | line_endings: LineEndings, |
@@ -274,6 +289,7 @@ fn semantic_token_type_and_modifiers( | |||
274 | HighlightTag::TypeAlias => semantic_tokens::TYPE_ALIAS, | 289 | HighlightTag::TypeAlias => semantic_tokens::TYPE_ALIAS, |
275 | HighlightTag::Trait => lsp_types::SemanticTokenType::INTERFACE, | 290 | HighlightTag::Trait => lsp_types::SemanticTokenType::INTERFACE, |
276 | HighlightTag::BuiltinType => semantic_tokens::BUILTIN_TYPE, | 291 | HighlightTag::BuiltinType => semantic_tokens::BUILTIN_TYPE, |
292 | HighlightTag::SelfKeyword => semantic_tokens::SELF_KEYWORD, | ||
277 | HighlightTag::SelfType => lsp_types::SemanticTokenType::TYPE, | 293 | HighlightTag::SelfType => lsp_types::SemanticTokenType::TYPE, |
278 | HighlightTag::Field => lsp_types::SemanticTokenType::PROPERTY, | 294 | HighlightTag::Field => lsp_types::SemanticTokenType::PROPERTY, |
279 | HighlightTag::Function => lsp_types::SemanticTokenType::FUNCTION, | 295 | HighlightTag::Function => lsp_types::SemanticTokenType::FUNCTION, |
@@ -295,6 +311,7 @@ fn semantic_token_type_and_modifiers( | |||
295 | HighlightTag::ByteLiteral | HighlightTag::NumericLiteral => { | 311 | HighlightTag::ByteLiteral | HighlightTag::NumericLiteral => { |
296 | lsp_types::SemanticTokenType::NUMBER | 312 | lsp_types::SemanticTokenType::NUMBER |
297 | } | 313 | } |
314 | HighlightTag::BoolLiteral => semantic_tokens::BOOLEAN, | ||
298 | HighlightTag::CharLiteral | HighlightTag::StringLiteral => { | 315 | HighlightTag::CharLiteral | HighlightTag::StringLiteral => { |
299 | lsp_types::SemanticTokenType::STRING | 316 | lsp_types::SemanticTokenType::STRING |
300 | } | 317 | } |
@@ -303,10 +320,12 @@ fn semantic_token_type_and_modifiers( | |||
303 | HighlightTag::Keyword => lsp_types::SemanticTokenType::KEYWORD, | 320 | HighlightTag::Keyword => lsp_types::SemanticTokenType::KEYWORD, |
304 | HighlightTag::UnresolvedReference => semantic_tokens::UNRESOLVED_REFERENCE, | 321 | HighlightTag::UnresolvedReference => semantic_tokens::UNRESOLVED_REFERENCE, |
305 | HighlightTag::FormatSpecifier => semantic_tokens::FORMAT_SPECIFIER, | 322 | HighlightTag::FormatSpecifier => semantic_tokens::FORMAT_SPECIFIER, |
323 | HighlightTag::Operator => lsp_types::SemanticTokenType::OPERATOR, | ||
306 | }; | 324 | }; |
307 | 325 | ||
308 | for modifier in highlight.modifiers.iter() { | 326 | for modifier in highlight.modifiers.iter() { |
309 | let modifier = match modifier { | 327 | let modifier = match modifier { |
328 | HighlightModifier::Attribute => semantic_tokens::ATTRIBUTE_MODIFIER, | ||
310 | HighlightModifier::Definition => lsp_types::SemanticTokenModifier::DECLARATION, | 329 | HighlightModifier::Definition => lsp_types::SemanticTokenModifier::DECLARATION, |
311 | HighlightModifier::ControlFlow => semantic_tokens::CONTROL_FLOW, | 330 | HighlightModifier::ControlFlow => semantic_tokens::CONTROL_FLOW, |
312 | HighlightModifier::Mutable => semantic_tokens::MUTABLE, | 331 | HighlightModifier::Mutable => semantic_tokens::MUTABLE, |
@@ -366,36 +385,46 @@ pub(crate) fn folding_range( | |||
366 | } | 385 | } |
367 | } | 386 | } |
368 | 387 | ||
369 | pub(crate) fn url(world: &WorldSnapshot, file_id: FileId) -> Result<lsp_types::Url> { | 388 | pub(crate) fn url(snap: &GlobalStateSnapshot, file_id: FileId) -> Result<lsp_types::Url> { |
370 | world.file_id_to_uri(file_id) | 389 | snap.file_id_to_uri(file_id) |
371 | } | 390 | } |
372 | 391 | ||
373 | pub(crate) fn versioned_text_document_identifier( | 392 | pub(crate) fn versioned_text_document_identifier( |
374 | world: &WorldSnapshot, | 393 | snap: &GlobalStateSnapshot, |
375 | file_id: FileId, | 394 | file_id: FileId, |
376 | version: Option<i64>, | 395 | version: Option<i64>, |
377 | ) -> Result<lsp_types::VersionedTextDocumentIdentifier> { | 396 | ) -> Result<lsp_types::VersionedTextDocumentIdentifier> { |
378 | let res = lsp_types::VersionedTextDocumentIdentifier { uri: url(world, file_id)?, version }; | 397 | let res = lsp_types::VersionedTextDocumentIdentifier { uri: url(snap, file_id)?, version }; |
379 | Ok(res) | 398 | Ok(res) |
380 | } | 399 | } |
381 | 400 | ||
382 | pub(crate) fn location(world: &WorldSnapshot, frange: FileRange) -> Result<lsp_types::Location> { | 401 | pub(crate) fn location( |
383 | let url = url(world, frange.file_id)?; | 402 | snap: &GlobalStateSnapshot, |
384 | let line_index = world.analysis().file_line_index(frange.file_id)?; | 403 | frange: FileRange, |
404 | ) -> Result<lsp_types::Location> { | ||
405 | let url = url(snap, frange.file_id)?; | ||
406 | let line_index = snap.analysis().file_line_index(frange.file_id)?; | ||
385 | let range = range(&line_index, frange.range); | 407 | let range = range(&line_index, frange.range); |
386 | let loc = lsp_types::Location::new(url, range); | 408 | let loc = lsp_types::Location::new(url, range); |
387 | Ok(loc) | 409 | Ok(loc) |
388 | } | 410 | } |
389 | 411 | ||
390 | pub(crate) fn location_link( | 412 | pub(crate) fn location_link( |
391 | world: &WorldSnapshot, | 413 | snap: &GlobalStateSnapshot, |
392 | src: FileRange, | 414 | src: Option<FileRange>, |
393 | target: NavigationTarget, | 415 | target: NavigationTarget, |
394 | ) -> Result<lsp_types::LocationLink> { | 416 | ) -> Result<lsp_types::LocationLink> { |
395 | let src_location = location(world, src)?; | 417 | let origin_selection_range = match src { |
396 | let (target_uri, target_range, target_selection_range) = location_info(world, target)?; | 418 | Some(src) => { |
419 | let line_index = snap.analysis().file_line_index(src.file_id)?; | ||
420 | let range = range(&line_index, src.range); | ||
421 | Some(range) | ||
422 | } | ||
423 | None => None, | ||
424 | }; | ||
425 | let (target_uri, target_range, target_selection_range) = location_info(snap, target)?; | ||
397 | let res = lsp_types::LocationLink { | 426 | let res = lsp_types::LocationLink { |
398 | origin_selection_range: Some(src_location.range), | 427 | origin_selection_range, |
399 | target_uri, | 428 | target_uri, |
400 | target_range, | 429 | target_range, |
401 | target_selection_range, | 430 | target_selection_range, |
@@ -404,12 +433,12 @@ pub(crate) fn location_link( | |||
404 | } | 433 | } |
405 | 434 | ||
406 | fn location_info( | 435 | fn location_info( |
407 | world: &WorldSnapshot, | 436 | snap: &GlobalStateSnapshot, |
408 | target: NavigationTarget, | 437 | target: NavigationTarget, |
409 | ) -> Result<(lsp_types::Url, lsp_types::Range, lsp_types::Range)> { | 438 | ) -> Result<(lsp_types::Url, lsp_types::Range, lsp_types::Range)> { |
410 | let line_index = world.analysis().file_line_index(target.file_id())?; | 439 | let line_index = snap.analysis().file_line_index(target.file_id())?; |
411 | 440 | ||
412 | let target_uri = url(world, target.file_id())?; | 441 | let target_uri = url(snap, target.file_id())?; |
413 | let target_range = range(&line_index, target.full_range()); | 442 | let target_range = range(&line_index, target.full_range()); |
414 | let target_selection_range = | 443 | let target_selection_range = |
415 | target.focus_range().map(|it| range(&line_index, it)).unwrap_or(target_range); | 444 | target.focus_range().map(|it| range(&line_index, it)).unwrap_or(target_range); |
@@ -417,14 +446,14 @@ fn location_info( | |||
417 | } | 446 | } |
418 | 447 | ||
419 | pub(crate) fn goto_definition_response( | 448 | pub(crate) fn goto_definition_response( |
420 | world: &WorldSnapshot, | 449 | snap: &GlobalStateSnapshot, |
421 | src: FileRange, | 450 | src: Option<FileRange>, |
422 | targets: Vec<NavigationTarget>, | 451 | targets: Vec<NavigationTarget>, |
423 | ) -> Result<lsp_types::GotoDefinitionResponse> { | 452 | ) -> Result<lsp_types::GotoDefinitionResponse> { |
424 | if world.config.client_caps.location_link { | 453 | if snap.config.client_caps.location_link { |
425 | let links = targets | 454 | let links = targets |
426 | .into_iter() | 455 | .into_iter() |
427 | .map(|nav| location_link(world, src, nav)) | 456 | .map(|nav| location_link(snap, src, nav)) |
428 | .collect::<Result<Vec<_>>>()?; | 457 | .collect::<Result<Vec<_>>>()?; |
429 | Ok(links.into()) | 458 | Ok(links.into()) |
430 | } else { | 459 | } else { |
@@ -432,7 +461,7 @@ pub(crate) fn goto_definition_response( | |||
432 | .into_iter() | 461 | .into_iter() |
433 | .map(|nav| { | 462 | .map(|nav| { |
434 | location( | 463 | location( |
435 | world, | 464 | snap, |
436 | FileRange { | 465 | FileRange { |
437 | file_id: nav.file_id(), | 466 | file_id: nav.file_id(), |
438 | range: nav.focus_range().unwrap_or(nav.range()), | 467 | range: nav.focus_range().unwrap_or(nav.range()), |
@@ -445,33 +474,33 @@ pub(crate) fn goto_definition_response( | |||
445 | } | 474 | } |
446 | 475 | ||
447 | pub(crate) fn snippet_text_document_edit( | 476 | pub(crate) fn snippet_text_document_edit( |
448 | world: &WorldSnapshot, | 477 | snap: &GlobalStateSnapshot, |
449 | is_snippet: bool, | 478 | is_snippet: bool, |
450 | source_file_edit: SourceFileEdit, | 479 | source_file_edit: SourceFileEdit, |
451 | ) -> Result<lsp_ext::SnippetTextDocumentEdit> { | 480 | ) -> Result<lsp_ext::SnippetTextDocumentEdit> { |
452 | let text_document = versioned_text_document_identifier(world, source_file_edit.file_id, None)?; | 481 | let text_document = versioned_text_document_identifier(snap, source_file_edit.file_id, None)?; |
453 | let line_index = world.analysis().file_line_index(source_file_edit.file_id)?; | 482 | let line_index = snap.analysis().file_line_index(source_file_edit.file_id)?; |
454 | let line_endings = world.file_line_endings(source_file_edit.file_id); | 483 | let line_endings = snap.file_line_endings(source_file_edit.file_id); |
455 | let edits = source_file_edit | 484 | let edits = source_file_edit |
456 | .edit | 485 | .edit |
457 | .into_iter() | 486 | .into_iter() |
458 | .map(|it| snippet_text_edit(&line_index, line_endings, is_snippet, it.clone())) | 487 | .map(|it| snippet_text_edit(&line_index, line_endings, is_snippet, it)) |
459 | .collect(); | 488 | .collect(); |
460 | Ok(lsp_ext::SnippetTextDocumentEdit { text_document, edits }) | 489 | Ok(lsp_ext::SnippetTextDocumentEdit { text_document, edits }) |
461 | } | 490 | } |
462 | 491 | ||
463 | pub(crate) fn resource_op( | 492 | pub(crate) fn resource_op( |
464 | world: &WorldSnapshot, | 493 | snap: &GlobalStateSnapshot, |
465 | file_system_edit: FileSystemEdit, | 494 | file_system_edit: FileSystemEdit, |
466 | ) -> Result<lsp_types::ResourceOp> { | 495 | ) -> Result<lsp_types::ResourceOp> { |
467 | let res = match file_system_edit { | 496 | let res = match file_system_edit { |
468 | FileSystemEdit::CreateFile { source_root, path } => { | 497 | FileSystemEdit::CreateFile { source_root, path } => { |
469 | let uri = world.path_to_uri(source_root, &path)?; | 498 | let uri = snap.path_to_uri(source_root, &path)?; |
470 | lsp_types::ResourceOp::Create(lsp_types::CreateFile { uri, options: None }) | 499 | lsp_types::ResourceOp::Create(lsp_types::CreateFile { uri, options: None }) |
471 | } | 500 | } |
472 | FileSystemEdit::MoveFile { src, dst_source_root, dst_path } => { | 501 | FileSystemEdit::MoveFile { src, dst_source_root, dst_path } => { |
473 | let old_uri = world.file_id_to_uri(src)?; | 502 | let old_uri = snap.file_id_to_uri(src)?; |
474 | let new_uri = world.path_to_uri(dst_source_root, &dst_path)?; | 503 | let new_uri = snap.path_to_uri(dst_source_root, &dst_path)?; |
475 | lsp_types::ResourceOp::Rename(lsp_types::RenameFile { old_uri, new_uri, options: None }) | 504 | lsp_types::ResourceOp::Rename(lsp_types::RenameFile { old_uri, new_uri, options: None }) |
476 | } | 505 | } |
477 | }; | 506 | }; |
@@ -479,16 +508,16 @@ pub(crate) fn resource_op( | |||
479 | } | 508 | } |
480 | 509 | ||
481 | pub(crate) fn snippet_workspace_edit( | 510 | pub(crate) fn snippet_workspace_edit( |
482 | world: &WorldSnapshot, | 511 | snap: &GlobalStateSnapshot, |
483 | source_change: SourceChange, | 512 | source_change: SourceChange, |
484 | ) -> Result<lsp_ext::SnippetWorkspaceEdit> { | 513 | ) -> Result<lsp_ext::SnippetWorkspaceEdit> { |
485 | let mut document_changes: Vec<lsp_ext::SnippetDocumentChangeOperation> = Vec::new(); | 514 | let mut document_changes: Vec<lsp_ext::SnippetDocumentChangeOperation> = Vec::new(); |
486 | for op in source_change.file_system_edits { | 515 | for op in source_change.file_system_edits { |
487 | let op = resource_op(&world, op)?; | 516 | let op = resource_op(&snap, op)?; |
488 | document_changes.push(lsp_ext::SnippetDocumentChangeOperation::Op(op)); | 517 | document_changes.push(lsp_ext::SnippetDocumentChangeOperation::Op(op)); |
489 | } | 518 | } |
490 | for edit in source_change.source_file_edits { | 519 | for edit in source_change.source_file_edits { |
491 | let edit = snippet_text_document_edit(&world, source_change.is_snippet, edit)?; | 520 | let edit = snippet_text_document_edit(&snap, source_change.is_snippet, edit)?; |
492 | document_changes.push(lsp_ext::SnippetDocumentChangeOperation::Edit(edit)); | 521 | document_changes.push(lsp_ext::SnippetDocumentChangeOperation::Edit(edit)); |
493 | } | 522 | } |
494 | let workspace_edit = | 523 | let workspace_edit = |
@@ -497,11 +526,11 @@ pub(crate) fn snippet_workspace_edit( | |||
497 | } | 526 | } |
498 | 527 | ||
499 | pub(crate) fn workspace_edit( | 528 | pub(crate) fn workspace_edit( |
500 | world: &WorldSnapshot, | 529 | snap: &GlobalStateSnapshot, |
501 | source_change: SourceChange, | 530 | source_change: SourceChange, |
502 | ) -> Result<lsp_types::WorkspaceEdit> { | 531 | ) -> Result<lsp_types::WorkspaceEdit> { |
503 | assert!(!source_change.is_snippet); | 532 | assert!(!source_change.is_snippet); |
504 | snippet_workspace_edit(world, source_change).map(|it| it.into()) | 533 | snippet_workspace_edit(snap, source_change).map(|it| it.into()) |
505 | } | 534 | } |
506 | 535 | ||
507 | impl From<lsp_ext::SnippetWorkspaceEdit> for lsp_types::WorkspaceEdit { | 536 | impl From<lsp_ext::SnippetWorkspaceEdit> for lsp_types::WorkspaceEdit { |
@@ -540,13 +569,13 @@ impl From<lsp_ext::SnippetWorkspaceEdit> for lsp_types::WorkspaceEdit { | |||
540 | } | 569 | } |
541 | 570 | ||
542 | pub fn call_hierarchy_item( | 571 | pub fn call_hierarchy_item( |
543 | world: &WorldSnapshot, | 572 | snap: &GlobalStateSnapshot, |
544 | target: NavigationTarget, | 573 | target: NavigationTarget, |
545 | ) -> Result<lsp_types::CallHierarchyItem> { | 574 | ) -> Result<lsp_types::CallHierarchyItem> { |
546 | let name = target.name().to_string(); | 575 | let name = target.name().to_string(); |
547 | let detail = target.description().map(|it| it.to_string()); | 576 | let detail = target.description().map(|it| it.to_string()); |
548 | let kind = symbol_kind(target.kind()); | 577 | let kind = symbol_kind(target.kind()); |
549 | let (uri, range, selection_range) = location_info(world, target)?; | 578 | let (uri, range, selection_range) = location_info(snap, target)?; |
550 | Ok(lsp_types::CallHierarchyItem { name, kind, tags: None, detail, uri, range, selection_range }) | 579 | Ok(lsp_types::CallHierarchyItem { name, kind, tags: None, detail, uri, range, selection_range }) |
551 | } | 580 | } |
552 | 581 | ||
@@ -594,13 +623,48 @@ fn main() <fold>{ | |||
594 | } | 623 | } |
595 | } | 624 | } |
596 | 625 | ||
597 | pub(crate) fn code_action(world: &WorldSnapshot, assist: Assist) -> Result<lsp_ext::CodeAction> { | 626 | pub(crate) fn code_action( |
627 | snap: &GlobalStateSnapshot, | ||
628 | assist: Assist, | ||
629 | ) -> Result<lsp_ext::CodeAction> { | ||
598 | let res = lsp_ext::CodeAction { | 630 | let res = lsp_ext::CodeAction { |
599 | title: assist.label, | 631 | title: assist.label, |
600 | group: if world.config.client_caps.code_action_group { assist.group_label } else { None }, | 632 | group: if snap.config.client_caps.code_action_group { assist.group_label } else { None }, |
601 | kind: Some(String::new()), | 633 | kind: Some(String::new()), |
602 | edit: Some(snippet_workspace_edit(world, assist.source_change)?), | 634 | edit: Some(snippet_workspace_edit(snap, assist.source_change)?), |
603 | command: None, | 635 | command: None, |
604 | }; | 636 | }; |
605 | Ok(res) | 637 | Ok(res) |
606 | } | 638 | } |
639 | |||
640 | pub(crate) fn runnable( | ||
641 | snap: &GlobalStateSnapshot, | ||
642 | file_id: FileId, | ||
643 | runnable: Runnable, | ||
644 | ) -> Result<lsp_ext::Runnable> { | ||
645 | let spec = CargoTargetSpec::for_file(snap, file_id)?; | ||
646 | let target = spec.as_ref().map(|s| s.target.clone()); | ||
647 | let (cargo_args, executable_args) = | ||
648 | CargoTargetSpec::runnable_args(spec, &runnable.kind, &runnable.cfg_exprs)?; | ||
649 | let label = match &runnable.kind { | ||
650 | RunnableKind::Test { test_id, .. } => format!("test {}", test_id), | ||
651 | RunnableKind::TestMod { path } => format!("test-mod {}", path), | ||
652 | RunnableKind::Bench { test_id } => format!("bench {}", test_id), | ||
653 | RunnableKind::DocTest { test_id, .. } => format!("doctest {}", test_id), | ||
654 | RunnableKind::Bin => { | ||
655 | target.map_or_else(|| "run binary".to_string(), |t| format!("run {}", t)) | ||
656 | } | ||
657 | }; | ||
658 | let location = location_link(snap, None, runnable.nav)?; | ||
659 | |||
660 | Ok(lsp_ext::Runnable { | ||
661 | label, | ||
662 | location: Some(location), | ||
663 | kind: lsp_ext::RunnableKind::Cargo, | ||
664 | args: lsp_ext::CargoRunnable { | ||
665 | workspace_root: snap.workspace_root_for(file_id).map(|root| root.to_owned()), | ||
666 | cargo_args, | ||
667 | executable_args, | ||
668 | }, | ||
669 | }) | ||
670 | } | ||
diff --git a/crates/rust-analyzer/tests/heavy_tests/main.rs b/crates/rust-analyzer/tests/heavy_tests/main.rs index 4e94c37e1..ad3476310 100644 --- a/crates/rust-analyzer/tests/heavy_tests/main.rs +++ b/crates/rust-analyzer/tests/heavy_tests/main.rs | |||
@@ -59,52 +59,6 @@ use std::collections::Spam; | |||
59 | } | 59 | } |
60 | 60 | ||
61 | #[test] | 61 | #[test] |
62 | fn test_runnables_no_project() { | ||
63 | if skip_slow_tests() { | ||
64 | return; | ||
65 | } | ||
66 | |||
67 | let server = project( | ||
68 | r" | ||
69 | //- lib.rs | ||
70 | #[test] | ||
71 | fn foo() { | ||
72 | } | ||
73 | ", | ||
74 | ); | ||
75 | server.wait_until_workspace_is_loaded(); | ||
76 | server.request::<Runnables>( | ||
77 | RunnablesParams { text_document: server.doc_id("lib.rs"), position: None }, | ||
78 | json!([ | ||
79 | { | ||
80 | "args": [ "test" ], | ||
81 | "extraArgs": [ "foo", "--nocapture" ], | ||
82 | "bin": "cargo", | ||
83 | "env": { "RUST_BACKTRACE": "short" }, | ||
84 | "cwd": null, | ||
85 | "label": "test foo", | ||
86 | "range": { | ||
87 | "end": { "character": 1, "line": 2 }, | ||
88 | "start": { "character": 0, "line": 0 } | ||
89 | } | ||
90 | }, | ||
91 | { | ||
92 | "args": ["check", "--workspace"], | ||
93 | "extraArgs": [], | ||
94 | "bin": "cargo", | ||
95 | "env": {}, | ||
96 | "cwd": null, | ||
97 | "label": "cargo check --workspace", | ||
98 | "range": { | ||
99 | "end": { "character": 0, "line": 0 }, | ||
100 | "start": { "character": 0, "line": 0 } | ||
101 | } | ||
102 | } | ||
103 | ]), | ||
104 | ); | ||
105 | } | ||
106 | |||
107 | #[test] | ||
108 | fn test_runnables_project() { | 62 | fn test_runnables_project() { |
109 | if skip_slow_tests() { | 63 | if skip_slow_tests() { |
110 | return; | 64 | return; |
@@ -138,42 +92,44 @@ fn main() {} | |||
138 | server.request::<Runnables>( | 92 | server.request::<Runnables>( |
139 | RunnablesParams { text_document: server.doc_id("foo/tests/spam.rs"), position: None }, | 93 | RunnablesParams { text_document: server.doc_id("foo/tests/spam.rs"), position: None }, |
140 | json!([ | 94 | json!([ |
141 | { | 95 | { |
142 | "args": [ "test", "--package", "foo", "--test", "spam" ], | 96 | "args": { |
143 | "extraArgs": [ "test_eggs", "--exact", "--nocapture" ], | 97 | "cargoArgs": ["test", "--package", "foo", "--test", "spam"], |
144 | "bin": "cargo", | 98 | "executableArgs": ["test_eggs", "--exact", "--nocapture"], |
145 | "env": { "RUST_BACKTRACE": "short" }, | 99 | "workspaceRoot": server.path().join("foo") |
146 | "label": "test test_eggs", | ||
147 | "range": { | ||
148 | "end": { "character": 17, "line": 1 }, | ||
149 | "start": { "character": 0, "line": 0 } | ||
150 | }, | ||
151 | "cwd": server.path().join("foo") | ||
152 | }, | 100 | }, |
153 | { | 101 | "kind": "cargo", |
154 | "args": [ "check", "--package", "foo" ], | 102 | "label": "test test_eggs", |
155 | "extraArgs": [], | 103 | "location": { |
156 | "bin": "cargo", | 104 | "targetRange": { |
157 | "env": {}, | 105 | "end": { "character": 17, "line": 1 }, |
158 | "label": "cargo check -p foo", | ||
159 | "range": { | ||
160 | "end": { "character": 0, "line": 0 }, | ||
161 | "start": { "character": 0, "line": 0 } | 106 | "start": { "character": 0, "line": 0 } |
162 | }, | 107 | }, |
163 | "cwd": server.path().join("foo") | 108 | "targetSelectionRange": { |
164 | }, | 109 | "end": { "character": 12, "line": 1 }, |
165 | { | 110 | "start": { "character": 3, "line": 1 } |
166 | "args": [ "test", "--package", "foo" ], | ||
167 | "extraArgs": [], | ||
168 | "bin": "cargo", | ||
169 | "env": {}, | ||
170 | "label": "cargo test -p foo", | ||
171 | "range": { | ||
172 | "end": { "character": 0, "line": 0 }, | ||
173 | "start": { "character": 0, "line": 0 } | ||
174 | }, | 111 | }, |
175 | "cwd": server.path().join("foo") | 112 | "targetUri": "file:///[..]/tests/spam.rs" |
176 | } | 113 | } |
114 | }, | ||
115 | { | ||
116 | "args": { | ||
117 | "cargoArgs": ["check", "--package", "foo"], | ||
118 | "executableArgs": [], | ||
119 | "workspaceRoot": server.path().join("foo") | ||
120 | }, | ||
121 | "kind": "cargo", | ||
122 | "label": "cargo check -p foo" | ||
123 | }, | ||
124 | { | ||
125 | "args": { | ||
126 | "cargoArgs": ["test", "--package", "foo"], | ||
127 | "executableArgs": [], | ||
128 | "workspaceRoot": server.path().join("foo") | ||
129 | }, | ||
130 | "kind": "cargo", | ||
131 | "label": "cargo test -p foo" | ||
132 | } | ||
177 | ]), | 133 | ]), |
178 | ); | 134 | ); |
179 | } | 135 | } |
@@ -342,6 +298,7 @@ fn main() {} | |||
342 | } | 298 | } |
343 | ] | 299 | ] |
344 | }, | 300 | }, |
301 | "kind": "quickfix", | ||
345 | "title": "Create module" | 302 | "title": "Create module" |
346 | }]), | 303 | }]), |
347 | ); | 304 | ); |
@@ -374,8 +331,7 @@ fn test_missing_module_code_action_in_json_project() { | |||
374 | "root_module": path.join("src/lib.rs"), | 331 | "root_module": path.join("src/lib.rs"), |
375 | "deps": [], | 332 | "deps": [], |
376 | "edition": "2015", | 333 | "edition": "2015", |
377 | "atom_cfgs": [], | 334 | "cfg": [ "cfg_atom_1", "feature=cfg_1"], |
378 | "key_value_cfgs": {} | ||
379 | } ] | 335 | } ] |
380 | }); | 336 | }); |
381 | 337 | ||
@@ -413,6 +369,7 @@ fn main() {{}} | |||
413 | } | 369 | } |
414 | ] | 370 | ] |
415 | }, | 371 | }, |
372 | "kind": "quickfix", | ||
416 | "title": "Create module" | 373 | "title": "Create module" |
417 | }]), | 374 | }]), |
418 | ); | 375 | ); |
@@ -473,23 +430,14 @@ fn main() {{}} | |||
473 | text_document: server.doc_id("src/m0.rs"), | 430 | text_document: server.doc_id("src/m0.rs"), |
474 | position: Position { line: 0, character: 5 }, | 431 | position: Position { line: 0, character: 5 }, |
475 | }, | 432 | }, |
476 | json!({ | 433 | json!([{ |
477 | "documentChanges": [ | 434 | "insertTextFormat": 2, |
478 | { | 435 | "newText": "\n/// $0", |
479 | "edits": [ | 436 | "range": { |
480 | { | 437 | "end": { "character": 5, "line": 0 }, |
481 | "insertTextFormat": 2, | 438 | "start": { "character": 5, "line": 0 } |
482 | "newText": "\n/// $0", | ||
483 | "range": { | ||
484 | "end": { "character": 5, "line": 0 }, | ||
485 | "start": { "character": 5, "line": 0 } | ||
486 | } | ||
487 | } | ||
488 | ], | ||
489 | "textDocument": { "uri": "file:///[..]src/m0.rs", "version": null } | ||
490 | } | 439 | } |
491 | ] | 440 | }]), |
492 | }), | ||
493 | ); | 441 | ); |
494 | let elapsed = start.elapsed(); | 442 | let elapsed = start.elapsed(); |
495 | assert!(elapsed.as_millis() < 2000, "typing enter took {:?}", elapsed); | 443 | assert!(elapsed.as_millis() < 2000, "typing enter took {:?}", elapsed); |
@@ -519,23 +467,14 @@ version = \"0.0.0\" | |||
519 | text_document: server.doc_id("src/main.rs"), | 467 | text_document: server.doc_id("src/main.rs"), |
520 | position: Position { line: 0, character: 8 }, | 468 | position: Position { line: 0, character: 8 }, |
521 | }, | 469 | }, |
522 | json!({ | 470 | json!([{ |
523 | "documentChanges": [ | 471 | "insertTextFormat": 2, |
524 | { | 472 | "newText": "\r\n/// $0", |
525 | "edits": [ | 473 | "range": { |
526 | { | 474 | "end": { "line": 0, "character": 8 }, |
527 | "insertTextFormat": 2, | 475 | "start": { "line": 0, "character": 8 } |
528 | "newText": "\r\n/// $0", | ||
529 | "range": { | ||
530 | "end": { "line": 0, "character": 8 }, | ||
531 | "start": { "line": 0, "character": 8 } | ||
532 | } | ||
533 | } | ||
534 | ], | ||
535 | "textDocument": { "uri": "file:///[..]src/main.rs", "version": null } | ||
536 | } | 476 | } |
537 | ] | 477 | }]), |
538 | }), | ||
539 | ); | 478 | ); |
540 | } | 479 | } |
541 | 480 | ||
@@ -774,5 +713,5 @@ pub fn foo(_input: TokenStream) -> TokenStream { | |||
774 | }); | 713 | }); |
775 | 714 | ||
776 | let value = res.get("contents").unwrap().get("value").unwrap().to_string(); | 715 | let value = res.get("contents").unwrap().get("value").unwrap().to_string(); |
777 | assert_eq!(value, r#""```rust\nfoo::Bar\nfn bar()\n```""#) | 716 | assert_eq!(value, r#""```rust\nfoo::Bar\n```\n\n```rust\nfn bar()\n```""#) |
778 | } | 717 | } |
diff --git a/crates/rust-analyzer/tests/heavy_tests/support.rs b/crates/rust-analyzer/tests/heavy_tests/support.rs index 9acbae066..30d03b622 100644 --- a/crates/rust-analyzer/tests/heavy_tests/support.rs +++ b/crates/rust-analyzer/tests/heavy_tests/support.rs | |||
@@ -19,8 +19,9 @@ use serde_json::{to_string_pretty, Value}; | |||
19 | use tempfile::TempDir; | 19 | use tempfile::TempDir; |
20 | use test_utils::{find_mismatch, parse_fixture}; | 20 | use test_utils::{find_mismatch, parse_fixture}; |
21 | 21 | ||
22 | use ra_project_model::ProjectManifest; | ||
22 | use rust_analyzer::{ | 23 | use rust_analyzer::{ |
23 | config::{ClientCapsConfig, Config}, | 24 | config::{ClientCapsConfig, Config, LinkedProject}, |
24 | main_loop, | 25 | main_loop, |
25 | }; | 26 | }; |
26 | 27 | ||
@@ -42,7 +43,7 @@ impl<'a> Project<'a> { | |||
42 | self | 43 | self |
43 | } | 44 | } |
44 | 45 | ||
45 | pub fn root(mut self, path: &str) -> Project<'a> { | 46 | pub(crate) fn root(mut self, path: &str) -> Project<'a> { |
46 | self.roots.push(path.into()); | 47 | self.roots.push(path.into()); |
47 | self | 48 | self |
48 | } | 49 | } |
@@ -68,13 +69,22 @@ impl<'a> Project<'a> { | |||
68 | let mut paths = vec![]; | 69 | let mut paths = vec![]; |
69 | 70 | ||
70 | for entry in parse_fixture(self.fixture) { | 71 | for entry in parse_fixture(self.fixture) { |
71 | let path = tmp_dir.path().join(entry.meta); | 72 | let path = tmp_dir.path().join(entry.meta.path().as_str()); |
72 | fs::create_dir_all(path.parent().unwrap()).unwrap(); | 73 | fs::create_dir_all(path.parent().unwrap()).unwrap(); |
73 | fs::write(path.as_path(), entry.text.as_bytes()).unwrap(); | 74 | fs::write(path.as_path(), entry.text.as_bytes()).unwrap(); |
74 | paths.push((path, entry.text)); | 75 | paths.push((path, entry.text)); |
75 | } | 76 | } |
76 | 77 | ||
77 | let roots = self.roots.into_iter().map(|root| tmp_dir.path().join(root)).collect(); | 78 | let mut roots = |
79 | self.roots.into_iter().map(|root| tmp_dir.path().join(root)).collect::<Vec<_>>(); | ||
80 | if roots.is_empty() { | ||
81 | roots.push(tmp_dir.path().to_path_buf()); | ||
82 | } | ||
83 | let linked_projects = roots | ||
84 | .into_iter() | ||
85 | .map(|it| ProjectManifest::discover_single(&it).unwrap()) | ||
86 | .map(LinkedProject::from) | ||
87 | .collect::<Vec<_>>(); | ||
78 | 88 | ||
79 | let mut config = Config { | 89 | let mut config = Config { |
80 | client_caps: ClientCapsConfig { | 90 | client_caps: ClientCapsConfig { |
@@ -84,6 +94,7 @@ impl<'a> Project<'a> { | |||
84 | ..Default::default() | 94 | ..Default::default() |
85 | }, | 95 | }, |
86 | with_sysroot: self.with_sysroot, | 96 | with_sysroot: self.with_sysroot, |
97 | linked_projects, | ||
87 | ..Config::default() | 98 | ..Config::default() |
88 | }; | 99 | }; |
89 | 100 | ||
@@ -91,7 +102,7 @@ impl<'a> Project<'a> { | |||
91 | f(&mut config) | 102 | f(&mut config) |
92 | } | 103 | } |
93 | 104 | ||
94 | Server::new(tmp_dir, config, roots, paths) | 105 | Server::new(tmp_dir, config, paths) |
95 | } | 106 | } |
96 | } | 107 | } |
97 | 108 | ||
@@ -109,20 +120,12 @@ pub struct Server { | |||
109 | } | 120 | } |
110 | 121 | ||
111 | impl Server { | 122 | impl Server { |
112 | fn new( | 123 | fn new(dir: TempDir, config: Config, files: Vec<(PathBuf, String)>) -> Server { |
113 | dir: TempDir, | ||
114 | config: Config, | ||
115 | roots: Vec<PathBuf>, | ||
116 | files: Vec<(PathBuf, String)>, | ||
117 | ) -> Server { | ||
118 | let path = dir.path().to_path_buf(); | ||
119 | |||
120 | let roots = if roots.is_empty() { vec![path] } else { roots }; | ||
121 | let (connection, client) = Connection::memory(); | 124 | let (connection, client) = Connection::memory(); |
122 | 125 | ||
123 | let _thread = jod_thread::Builder::new() | 126 | let _thread = jod_thread::Builder::new() |
124 | .name("test server".to_string()) | 127 | .name("test server".to_string()) |
125 | .spawn(move || main_loop(roots, config, connection).unwrap()) | 128 | .spawn(move || main_loop(config, connection).unwrap()) |
126 | .expect("failed to spawn a thread"); | 129 | .expect("failed to spawn a thread"); |
127 | 130 | ||
128 | let res = | 131 | let res = |
diff --git a/crates/stdx/src/lib.rs b/crates/stdx/src/lib.rs index 71a57fba2..c0356344c 100644 --- a/crates/stdx/src/lib.rs +++ b/crates/stdx/src/lib.rs | |||
@@ -124,3 +124,8 @@ pub fn replace(buf: &mut String, from: char, to: &str) { | |||
124 | // FIXME: do this in place. | 124 | // FIXME: do this in place. |
125 | *buf = buf.replace(from, to) | 125 | *buf = buf.replace(from, to) |
126 | } | 126 | } |
127 | |||
128 | pub fn split1(haystack: &str, delim: char) -> Option<(&str, &str)> { | ||
129 | let idx = haystack.find(delim)?; | ||
130 | Some((&haystack[..idx], &haystack[idx + delim.len_utf8()..])) | ||
131 | } | ||
diff --git a/crates/test_utils/Cargo.toml b/crates/test_utils/Cargo.toml index 8ec986bcb..8840bf36a 100644 --- a/crates/test_utils/Cargo.toml +++ b/crates/test_utils/Cargo.toml | |||
@@ -11,3 +11,8 @@ doctest = false | |||
11 | difference = "2.0.0" | 11 | difference = "2.0.0" |
12 | text-size = "1.0.0" | 12 | text-size = "1.0.0" |
13 | serde_json = "1.0.48" | 13 | serde_json = "1.0.48" |
14 | relative-path = "1.0.0" | ||
15 | rustc-hash = "1.1.0" | ||
16 | |||
17 | ra_cfg = { path = "../ra_cfg" } | ||
18 | stdx = { path = "../stdx" } \ No newline at end of file | ||
diff --git a/crates/test_utils/src/lib.rs b/crates/test_utils/src/lib.rs index be2cfbaa2..2141bfc20 100644 --- a/crates/test_utils/src/lib.rs +++ b/crates/test_utils/src/lib.rs | |||
@@ -14,6 +14,11 @@ use std::{ | |||
14 | path::{Path, PathBuf}, | 14 | path::{Path, PathBuf}, |
15 | }; | 15 | }; |
16 | 16 | ||
17 | pub use ra_cfg::CfgOptions; | ||
18 | use stdx::split1; | ||
19 | |||
20 | pub use relative_path::{RelativePath, RelativePathBuf}; | ||
21 | pub use rustc_hash::FxHashMap; | ||
17 | use serde_json::Value; | 22 | use serde_json::Value; |
18 | use text_size::{TextRange, TextSize}; | 23 | use text_size::{TextRange, TextSize}; |
19 | 24 | ||
@@ -157,10 +162,82 @@ pub fn add_cursor(text: &str, offset: TextSize) -> String { | |||
157 | 162 | ||
158 | #[derive(Debug, Eq, PartialEq)] | 163 | #[derive(Debug, Eq, PartialEq)] |
159 | pub struct FixtureEntry { | 164 | pub struct FixtureEntry { |
160 | pub meta: String, | 165 | pub meta: FixtureMeta, |
161 | pub text: String, | 166 | pub text: String, |
162 | } | 167 | } |
163 | 168 | ||
169 | #[derive(Debug, Eq, PartialEq)] | ||
170 | pub enum FixtureMeta { | ||
171 | Root { path: RelativePathBuf }, | ||
172 | File(FileMeta), | ||
173 | } | ||
174 | |||
175 | #[derive(Debug, Eq, PartialEq)] | ||
176 | pub struct FileMeta { | ||
177 | pub path: RelativePathBuf, | ||
178 | pub crate_name: Option<String>, | ||
179 | pub deps: Vec<String>, | ||
180 | pub cfg: CfgOptions, | ||
181 | pub edition: Option<String>, | ||
182 | pub env: FxHashMap<String, String>, | ||
183 | } | ||
184 | |||
185 | impl FixtureMeta { | ||
186 | pub fn path(&self) -> &RelativePath { | ||
187 | match self { | ||
188 | FixtureMeta::Root { path } => &path, | ||
189 | FixtureMeta::File(f) => &f.path, | ||
190 | } | ||
191 | } | ||
192 | |||
193 | pub fn crate_name(&self) -> Option<&String> { | ||
194 | match self { | ||
195 | FixtureMeta::File(f) => f.crate_name.as_ref(), | ||
196 | _ => None, | ||
197 | } | ||
198 | } | ||
199 | |||
200 | pub fn cfg_options(&self) -> Option<&CfgOptions> { | ||
201 | match self { | ||
202 | FixtureMeta::File(f) => Some(&f.cfg), | ||
203 | _ => None, | ||
204 | } | ||
205 | } | ||
206 | |||
207 | pub fn edition(&self) -> Option<&String> { | ||
208 | match self { | ||
209 | FixtureMeta::File(f) => f.edition.as_ref(), | ||
210 | _ => None, | ||
211 | } | ||
212 | } | ||
213 | |||
214 | pub fn env(&self) -> impl Iterator<Item = (&String, &String)> { | ||
215 | struct EnvIter<'a> { | ||
216 | iter: Option<std::collections::hash_map::Iter<'a, String, String>>, | ||
217 | } | ||
218 | |||
219 | impl<'a> EnvIter<'a> { | ||
220 | fn new(meta: &'a FixtureMeta) -> Self { | ||
221 | Self { | ||
222 | iter: match meta { | ||
223 | FixtureMeta::File(f) => Some(f.env.iter()), | ||
224 | _ => None, | ||
225 | }, | ||
226 | } | ||
227 | } | ||
228 | } | ||
229 | |||
230 | impl<'a> Iterator for EnvIter<'a> { | ||
231 | type Item = (&'a String, &'a String); | ||
232 | fn next(&mut self) -> Option<Self::Item> { | ||
233 | self.iter.as_mut().and_then(|i| i.next()) | ||
234 | } | ||
235 | } | ||
236 | |||
237 | EnvIter::new(self) | ||
238 | } | ||
239 | } | ||
240 | |||
164 | /// Parses text which looks like this: | 241 | /// Parses text which looks like this: |
165 | /// | 242 | /// |
166 | /// ```not_rust | 243 | /// ```not_rust |
@@ -169,8 +246,8 @@ pub struct FixtureEntry { | |||
169 | /// line 2 | 246 | /// line 2 |
170 | /// // - other meta | 247 | /// // - other meta |
171 | /// ``` | 248 | /// ``` |
172 | pub fn parse_fixture(fixture: &str) -> Vec<FixtureEntry> { | 249 | pub fn parse_fixture(ra_fixture: &str) -> Vec<FixtureEntry> { |
173 | let fixture = indent_first_line(fixture); | 250 | let fixture = indent_first_line(ra_fixture); |
174 | let margin = fixture_margin(&fixture); | 251 | let margin = fixture_margin(&fixture); |
175 | 252 | ||
176 | let mut lines = fixture | 253 | let mut lines = fixture |
@@ -200,6 +277,7 @@ The offending line: {:?}"#, | |||
200 | for line in lines.by_ref() { | 277 | for line in lines.by_ref() { |
201 | if line.starts_with("//-") { | 278 | if line.starts_with("//-") { |
202 | let meta = line["//-".len()..].trim().to_string(); | 279 | let meta = line["//-".len()..].trim().to_string(); |
280 | let meta = parse_meta(&meta); | ||
203 | res.push(FixtureEntry { meta, text: String::new() }) | 281 | res.push(FixtureEntry { meta, text: String::new() }) |
204 | } else if let Some(entry) = res.last_mut() { | 282 | } else if let Some(entry) = res.last_mut() { |
205 | entry.text.push_str(line); | 283 | entry.text.push_str(line); |
@@ -209,6 +287,52 @@ The offending line: {:?}"#, | |||
209 | res | 287 | res |
210 | } | 288 | } |
211 | 289 | ||
290 | //- /lib.rs crate:foo deps:bar,baz cfg:foo=a,bar=b env:OUTDIR=path/to,OTHER=foo | ||
291 | fn parse_meta(meta: &str) -> FixtureMeta { | ||
292 | let components = meta.split_ascii_whitespace().collect::<Vec<_>>(); | ||
293 | |||
294 | if components[0] == "root" { | ||
295 | let path: RelativePathBuf = components[1].into(); | ||
296 | assert!(path.starts_with("/") && path.ends_with("/")); | ||
297 | return FixtureMeta::Root { path }; | ||
298 | } | ||
299 | |||
300 | let path: RelativePathBuf = components[0].into(); | ||
301 | assert!(path.starts_with("/")); | ||
302 | |||
303 | let mut krate = None; | ||
304 | let mut deps = Vec::new(); | ||
305 | let mut edition = None; | ||
306 | let mut cfg = CfgOptions::default(); | ||
307 | let mut env = FxHashMap::default(); | ||
308 | for component in components[1..].iter() { | ||
309 | let (key, value) = split1(component, ':').unwrap(); | ||
310 | match key { | ||
311 | "crate" => krate = Some(value.to_string()), | ||
312 | "deps" => deps = value.split(',').map(|it| it.to_string()).collect(), | ||
313 | "edition" => edition = Some(value.to_string()), | ||
314 | "cfg" => { | ||
315 | for key in value.split(',') { | ||
316 | match split1(key, '=') { | ||
317 | None => cfg.insert_atom(key.into()), | ||
318 | Some((k, v)) => cfg.insert_key_value(k.into(), v.into()), | ||
319 | } | ||
320 | } | ||
321 | } | ||
322 | "env" => { | ||
323 | for key in value.split(',') { | ||
324 | if let Some((k, v)) = split1(key, '=') { | ||
325 | env.insert(k.into(), v.into()); | ||
326 | } | ||
327 | } | ||
328 | } | ||
329 | _ => panic!("bad component: {:?}", component), | ||
330 | } | ||
331 | } | ||
332 | |||
333 | FixtureMeta::File(FileMeta { path, crate_name: krate, deps, edition, cfg, env }) | ||
334 | } | ||
335 | |||
212 | /// Adjusts the indentation of the first line to the minimum indentation of the rest of the lines. | 336 | /// Adjusts the indentation of the first line to the minimum indentation of the rest of the lines. |
213 | /// This allows fixtures to start off in a different indentation, e.g. to align the first line with | 337 | /// This allows fixtures to start off in a different indentation, e.g. to align the first line with |
214 | /// the other lines visually: | 338 | /// the other lines visually: |
@@ -288,13 +412,33 @@ struct Bar; | |||
288 | ) | 412 | ) |
289 | } | 413 | } |
290 | 414 | ||
415 | #[test] | ||
416 | fn parse_fixture_gets_full_meta() { | ||
417 | let parsed = parse_fixture( | ||
418 | r" | ||
419 | //- /lib.rs crate:foo deps:bar,baz cfg:foo=a,bar=b,atom env:OUTDIR=path/to,OTHER=foo | ||
420 | mod m; | ||
421 | ", | ||
422 | ); | ||
423 | assert_eq!(1, parsed.len()); | ||
424 | |||
425 | let parsed = &parsed[0]; | ||
426 | assert_eq!("mod m;\n\n", parsed.text); | ||
427 | |||
428 | let meta = &parsed.meta; | ||
429 | assert_eq!("foo", meta.crate_name().unwrap()); | ||
430 | assert_eq!("/lib.rs", meta.path()); | ||
431 | assert!(meta.cfg_options().is_some()); | ||
432 | assert_eq!(2, meta.env().count()); | ||
433 | } | ||
434 | |||
291 | /// Same as `parse_fixture`, except it allow empty fixture | 435 | /// Same as `parse_fixture`, except it allow empty fixture |
292 | pub fn parse_single_fixture(fixture: &str) -> Option<FixtureEntry> { | 436 | pub fn parse_single_fixture(ra_fixture: &str) -> Option<FixtureEntry> { |
293 | if !fixture.lines().any(|it| it.trim_start().starts_with("//-")) { | 437 | if !ra_fixture.lines().any(|it| it.trim_start().starts_with("//-")) { |
294 | return None; | 438 | return None; |
295 | } | 439 | } |
296 | 440 | ||
297 | let fixtures = parse_fixture(fixture); | 441 | let fixtures = parse_fixture(ra_fixture); |
298 | if fixtures.len() > 1 { | 442 | if fixtures.len() > 1 { |
299 | panic!("too many fixtures"); | 443 | panic!("too many fixtures"); |
300 | } | 444 | } |