aboutsummaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
authorMikhail Rakhmanov <[email protected]>2020-06-03 19:10:54 +0100
committerMikhail Rakhmanov <[email protected]>2020-06-03 19:10:54 +0100
commiteefa10bc6bff3624ddd0bbb6bc89d8beb4bed186 (patch)
tree15c38c2993c52f4065d338090ca9185cc1fcd3da /crates
parenta9d567584857b1be4ca8eaa5ef2c7d85f7b2845e (diff)
parent794f6da821c5d6e2490b996baffe162e4753262d (diff)
Merge branch 'master' into assists_extract_enum
Diffstat (limited to 'crates')
-rw-r--r--crates/ra_assists/src/handlers/add_from_impl_for_enum.rs4
-rw-r--r--crates/ra_assists/src/handlers/introduce_named_lifetime.rs303
-rw-r--r--crates/ra_assists/src/handlers/reorder_fields.rs2
-rw-r--r--crates/ra_assists/src/lib.rs2
-rw-r--r--crates/ra_assists/src/tests/generated.rs44
-rw-r--r--crates/ra_cfg/src/cfg_expr.rs10
-rw-r--r--crates/ra_db/src/fixture.rs70
-rw-r--r--crates/ra_db/src/input.rs15
-rw-r--r--crates/ra_hir/src/code_model.rs27
-rw-r--r--crates/ra_hir/src/lib.rs1
-rw-r--r--crates/ra_hir_def/src/attr.rs10
-rw-r--r--crates/ra_hir_def/src/body.rs2
-rw-r--r--crates/ra_hir_def/src/body/lower.rs87
-rw-r--r--crates/ra_hir_def/src/body/scope.rs4
-rw-r--r--crates/ra_hir_def/src/data.rs6
-rw-r--r--crates/ra_hir_def/src/docs.rs50
-rw-r--r--crates/ra_hir_def/src/expr.rs22
-rw-r--r--crates/ra_hir_def/src/lang_item.rs2
-rw-r--r--crates/ra_hir_def/src/nameres/raw.rs2
-rw-r--r--crates/ra_hir_def/src/type_ref.rs16
-rw-r--r--crates/ra_hir_expand/src/name.rs6
-rw-r--r--crates/ra_hir_expand/src/proc_macro.rs2
-rw-r--r--crates/ra_hir_ty/Cargo.toml5
-rw-r--r--crates/ra_hir_ty/src/db.rs5
-rw-r--r--crates/ra_hir_ty/src/diagnostics.rs4
-rw-r--r--crates/ra_hir_ty/src/infer.rs11
-rw-r--r--crates/ra_hir_ty/src/infer/coerce.rs4
-rw-r--r--crates/ra_hir_ty/src/infer/expr.rs98
-rw-r--r--crates/ra_hir_ty/src/lib.rs24
-rw-r--r--crates/ra_hir_ty/src/tests/coercion.rs17
-rw-r--r--crates/ra_hir_ty/src/tests/patterns.rs50
-rw-r--r--crates/ra_hir_ty/src/tests/simple.rs76
-rw-r--r--crates/ra_hir_ty/src/tests/traits.rs118
-rw-r--r--crates/ra_hir_ty/src/traits/builtin.rs3
-rw-r--r--crates/ra_hir_ty/src/traits/chalk.rs87
-rw-r--r--crates/ra_hir_ty/src/traits/chalk/interner.rs14
-rw-r--r--crates/ra_hir_ty/src/traits/chalk/mapping.rs93
-rw-r--r--crates/ra_hir_ty/src/traits/chalk/tls.rs18
-rw-r--r--crates/ra_ide/src/call_hierarchy.rs29
-rw-r--r--crates/ra_ide/src/completion.rs125
-rw-r--r--crates/ra_ide/src/completion/complete_attribute.rs6
-rw-r--r--crates/ra_ide/src/completion/complete_postfix.rs65
-rw-r--r--crates/ra_ide/src/completion/complete_trait_impl.rs61
-rw-r--r--crates/ra_ide/src/completion/complete_unqualified_path.rs36
-rw-r--r--crates/ra_ide/src/completion/completion_context.rs13
-rw-r--r--crates/ra_ide/src/completion/presentation.rs60
-rw-r--r--crates/ra_ide/src/diagnostics.rs2
-rw-r--r--crates/ra_ide/src/display.rs7
-rw-r--r--crates/ra_ide/src/display/function_signature.rs13
-rw-r--r--crates/ra_ide/src/display/navigation_target.rs66
-rw-r--r--crates/ra_ide/src/display/structure.rs19
-rw-r--r--crates/ra_ide/src/expand_macro.rs11
-rw-r--r--crates/ra_ide/src/extend_selection.rs12
-rw-r--r--crates/ra_ide/src/goto_definition.rs11
-rw-r--r--crates/ra_ide/src/goto_implementation.rs (renamed from crates/ra_ide/src/impls.rs)11
-rw-r--r--crates/ra_ide/src/goto_type_definition.rs11
-rw-r--r--crates/ra_ide/src/hover.rs264
-rw-r--r--crates/ra_ide/src/inlay_hints.rs22
-rw-r--r--crates/ra_ide/src/join_lines.rs11
-rw-r--r--crates/ra_ide/src/lib.rs7
-rw-r--r--crates/ra_ide/src/matching_brace.rs13
-rw-r--r--crates/ra_ide/src/mock_analysis.rs123
-rw-r--r--crates/ra_ide/src/parent_module.rs12
-rw-r--r--crates/ra_ide/src/references.rs27
-rw-r--r--crates/ra_ide/src/runnables.rs440
-rw-r--r--crates/ra_ide/src/snapshots/highlight_injection.html2
-rw-r--r--crates/ra_ide/src/snapshots/highlight_strings.html3
-rw-r--r--crates/ra_ide/src/snapshots/highlight_unsafe.html48
-rw-r--r--crates/ra_ide/src/snapshots/highlighting.html14
-rw-r--r--crates/ra_ide/src/snapshots/rainbow_highlighting.html2
-rw-r--r--crates/ra_ide/src/ssr.rs32
-rw-r--r--crates/ra_ide/src/status.rs11
-rw-r--r--crates/ra_ide/src/syntax_highlighting.rs223
-rw-r--r--crates/ra_ide/src/syntax_highlighting/html.rs2
-rw-r--r--crates/ra_ide/src/syntax_highlighting/tags.rs16
-rw-r--r--crates/ra_ide/src/syntax_highlighting/tests.rs32
-rw-r--r--crates/ra_ide/src/syntax_tree.rs16
-rw-r--r--crates/ra_ide/src/typing.rs7
-rw-r--r--crates/ra_ide/src/typing/on_enter.rs11
-rw-r--r--crates/ra_ide_db/src/search.rs40
-rw-r--r--crates/ra_ide_db/src/symbol_index.rs21
-rw-r--r--crates/ra_parser/src/grammar.rs7
-rw-r--r--crates/ra_parser/src/grammar/expressions.rs16
-rw-r--r--crates/ra_proc_macro_srv/Cargo.toml1
-rw-r--r--crates/ra_proc_macro_srv/src/tests/utils.rs3
-rw-r--r--crates/ra_project_model/src/cargo_workspace.rs2
-rw-r--r--crates/ra_project_model/src/json_project.rs89
-rw-r--r--crates/ra_project_model/src/lib.rs60
-rw-r--r--crates/ra_syntax/Cargo.toml2
-rw-r--r--crates/ra_syntax/src/ast.rs2
-rw-r--r--crates/ra_syntax/src/ast/generated/nodes.rs4
-rw-r--r--crates/ra_syntax/src/ast/tokens.rs10
-rw-r--r--crates/ra_syntax/src/ast/traits.rs13
-rw-r--r--crates/ra_syntax/src/syntax_node.rs4
-rw-r--r--crates/ra_syntax/src/validation.rs20
-rw-r--r--crates/ra_syntax/test_data/parser/inline/ok/0082_ref_expr.rast172
-rw-r--r--crates/ra_syntax/test_data/parser/inline/ok/0082_ref_expr.rs6
-rw-r--r--crates/rust-analyzer/Cargo.toml3
-rw-r--r--crates/rust-analyzer/src/bin/main.rs41
-rw-r--r--crates/rust-analyzer/src/caps.rs5
-rw-r--r--crates/rust-analyzer/src/cargo_target_spec.rs87
-rw-r--r--crates/rust-analyzer/src/cli/load_cargo.rs5
-rw-r--r--crates/rust-analyzer/src/config.rs56
-rw-r--r--crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_rustc_unused_variable.snap2
-rw-r--r--crates/rust-analyzer/src/diagnostics/to_proto.rs3
-rw-r--r--crates/rust-analyzer/src/from_proto.rs8
-rw-r--r--crates/rust-analyzer/src/global_state.rs (renamed from crates/rust-analyzer/src/world.rs)42
-rw-r--r--crates/rust-analyzer/src/lib.rs2
-rw-r--r--crates/rust-analyzer/src/lsp_ext.rs68
-rw-r--r--crates/rust-analyzer/src/main_loop.rs151
-rw-r--r--crates/rust-analyzer/src/main_loop/handlers.rs496
-rw-r--r--crates/rust-analyzer/src/semantic_tokens.rs3
-rw-r--r--crates/rust-analyzer/src/to_proto.rs148
-rw-r--r--crates/rust-analyzer/tests/heavy_tests/main.rs163
-rw-r--r--crates/rust-analyzer/tests/heavy_tests/support.rs33
-rw-r--r--crates/stdx/src/lib.rs5
-rw-r--r--crates/test_utils/Cargo.toml5
-rw-r--r--crates/test_utils/src/lib.rs156
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
5use crate::{utils::FamousDefs, AssistContext, AssistId, Assists}; 5use 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 @@
1use ra_syntax::{
2 ast::{self, NameOwner, TypeAscriptionOwner, TypeParamsOwner},
3 AstNode, SyntaxKind, TextRange, TextSize,
4};
5use rustc_hash::FxHashSet;
6
7use crate::{assist_context::AssistBuilder, AssistContext, AssistId, Assists};
8
9static ASSIST_NAME: &str = "introduce_named_lifetime";
10static 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
37pub(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
53fn 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
96fn 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
111fn 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`.
128fn 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)]
150mod 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//
25pub(crate) fn reorder_fields(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { 25pub(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
29fn reorder<R: AstNode>(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { 29fn 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]
62fn doctest_add_from_impl_for_enum() {
63 check_doc_test(
64 "add_from_impl_for_enum",
65 r#####"
66enum A { <|>One(u32) }
67"#####,
68 r#####"
69enum A { One(u32) }
70
71impl From<u32> for A {
72 fn from(v: u32) -> Self {
73 A::One(v)
74 }
75}
76"#####,
77 )
78}
79
80#[test]
62fn doctest_add_function() { 81fn 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]
455fn doctest_introduce_named_lifetime() {
456 check_doc_test(
457 "introduce_named_lifetime",
458 r#####"
459impl Cursor<'_<|>> {
460 fn node(self) -> &SyntaxNode {
461 match self {
462 Cursor::Replace(node) | Cursor::Before(node) => node,
463 }
464 }
465}
466"#####,
467 r#####"
468impl<'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]
436fn doctest_introduce_variable() { 480fn 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> {
88mod tests { 88mod 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
64use ra_cfg::CfgOptions; 64use ra_cfg::CfgOptions;
65use rustc_hash::FxHashMap; 65use rustc_hash::FxHashMap;
66use test_utils::{extract_offset, parse_fixture, parse_single_fixture, CURSOR_MARKER}; 66use test_utils::{extract_offset, parse_fixture, parse_single_fixture, FixtureMeta, CURSOR_MARKER};
67 67
68use crate::{ 68use 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) 261impl From<&FixtureMeta> for ParsedMeta {
262fn 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
307fn 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
314impl<'a, T> From<T> for Env
315where
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
314impl Env { 329impl 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)]
1366pub enum ScopeDef { 1375pub 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
63pub use hir_def::{ 63pub 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.
34pub(crate) struct CfgExpander { 34pub(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(
138fn compute_expr_scopes(expr: ExprId, body: &Body, scopes: &mut ExprScopes, scope: ScopeId) { 138fn 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
73pub(crate) fn docs_from_ast(node: &impl ast::DocCommentsOwner) -> Option<Documentation> { 80pub(crate) fn docs_from_ast<N>(node: &N) -> Option<Documentation>
74 node.doc_comment_text().map(|it| Documentation::new(&it)) 81where
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
90fn 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
106fn 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;
19use crate::{ 19use 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
25pub type ExprId = Idx<Expr>; 25pub 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
177impl DefKind { 177impl 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)]
39pub enum Rawness {
40 RawPtr,
41 Ref,
42}
43
44impl 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)]
40pub enum TypeRef { 56pub 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
28scoped-tls = "1" 28scoped-tls = "1"
29 29
30chalk-solve = { git = "https://github.com/rust-lang/chalk.git", rev = "eaab84b394007d1bed15f5470409a6ea02900a96" } 30chalk-solve = { git = "https://github.com/rust-lang/chalk.git", rev = "329b7f3fdd2431ed6f6778cde53f22374c7d094c" }
31chalk-rust-ir = { git = "https://github.com/rust-lang/chalk.git", rev = "eaab84b394007d1bed15f5470409a6ea02900a96" } 31chalk-ir = { git = "https://github.com/rust-lang/chalk.git", rev = "329b7f3fdd2431ed6f6778cde53f22374c7d094c" }
32chalk-ir = { git = "https://github.com/rust-lang/chalk.git", rev = "eaab84b394007d1bed15f5470409a6ea02900a96" }
33 32
34[dev-dependencies] 33[dev-dependencies]
35insta = "0.16.0" 34insta = "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> {
219struct BreakableContext { 219struct 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
225fn 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
224impl<'a> InferenceContext<'a> { 235impl<'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
24use super::{ 24use 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
29impl<'a> InferenceContext<'a> { 29impl<'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;
49use std::{iter, mem}; 49use std::{iter, mem};
50 50
51use hir_def::{ 51use 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};
55use ra_db::{impl_intern_key, salsa, CrateId}; 57use ra_db::{impl_intern_key, salsa, CrateId};
56 58
@@ -159,6 +161,12 @@ pub enum TypeCtor {
159pub struct TypeCtorId(salsa::InternId); 161pub struct TypeCtorId(salsa::InternId);
160impl_intern_key!(TypeCtorId); 162impl_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)]
167pub struct CallableDefId(salsa::InternId);
168impl_intern_key!(CallableDefId);
169
162impl TypeCtor { 170impl 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#"
118fn test() { 118fn 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]
525fn match_ergonomics_in_closure_params() {
526 assert_snapshot!(
527 infer(r#"
528#[lang = "fn_once"]
529trait FnOnce<Args> {
530 type Output;
531}
532
533fn foo<T, U, F: FnOnce(T) -> U>(t: T, f: F) -> U { loop {} }
534
535fn 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]
388fn infer_raw_ref() {
389 assert_snapshot!(
390 infer(r#"
391fn 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]
388fn infer_literals() { 408fn 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]
1948fn infer_labelled_break_with_val() {
1949 assert_snapshot!(
1950 infer(r#"
1951fn 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]
2647fn builtin_fn_def_copy() {
2648 assert_snapshot!(
2649 infer_with_mismatches(r#"
2650#[lang = "copy"]
2651trait Copy {}
2652
2653fn foo() {}
2654fn bar<T: Copy>(T) -> T {}
2655struct Struct(usize);
2656enum Enum { Variant(usize) }
2657
2658trait Test { fn test(&self) -> bool; }
2659impl<T: Copy> Test for T {}
2660
2661fn 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]
2688fn builtin_fn_ptr_copy() {
2689 assert_snapshot!(
2690 infer_with_mismatches(r#"
2691#[lang = "copy"]
2692trait Copy {}
2693
2694trait Test { fn test(&self) -> bool; }
2695impl<T: Copy> Test for T {}
2696
2697fn 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]
2647fn builtin_sized() { 2720fn 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]
2758fn integer_range_iterate() {
2759 let t = type_at(
2760 r#"
2761//- /main.rs crate:main deps:std
2762fn test() {
2763 for x in 0..100 { x<|>; }
2764}
2765
2766//- /std.rs crate:std
2767pub mod ops {
2768 pub struct Range<Idx> {
2769 pub start: Idx,
2770 pub end: Idx,
2771 }
2772}
2773
2774pub 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
2790trait Step {}
2791impl Step for i32 {}
2792impl Step for i64 {}
2793
2794impl<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;
4use log::debug; 4use log::debug;
5 5
6use chalk_ir::{fold::shift::Shift, GenericArg, TypeName}; 6use chalk_ir::{fold::shift::Shift, GenericArg, TypeName};
7use chalk_solve::rust_ir::{self, WellKnownTrait};
7 8
8use hir_def::{ 9use hir_def::{
9 lang_item::{lang_attr, LangItemTarget}, 10 lang_item::{lang_attr, LangItemTarget},
@@ -14,9 +15,8 @@ use ra_db::{salsa::InternKey, CrateId};
14use super::{builtin, AssocTyValue, ChalkContext, Impl}; 15use super::{builtin, AssocTyValue, ChalkContext, Impl};
15use crate::{ 16use 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};
19use chalk_rust_ir::WellKnownTrait;
20use mapping::{convert_where_clauses, generic_predicate_to_inline_bound, make_binders}; 20use mapping::{convert_where_clauses, generic_predicate_to_inline_bound, make_binders};
21 21
22pub use self::interner::*; 22pub 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
156pub(crate) fn program_clauses_for_chalk_env_query( 159pub(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
407pub(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
408impl From<AdtId> for crate::TypeCtorId { 427impl 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
439impl 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
445impl 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
420impl From<ImplId> for crate::traits::GlobalImplId { 451impl 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
432impl From<chalk_rust_ir::AssociatedTyValueId<Interner>> for crate::traits::AssocTyValueId { 463impl 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
438impl From<crate::traits::AssocTyValueId> for chalk_rust_ir::AssociatedTyValueId<Interner> { 469impl 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};
11pub struct Interner; 11pub struct Interner;
12 12
13pub type AssocTypeId = chalk_ir::AssocTypeId<Interner>; 13pub type AssocTypeId = chalk_ir::AssocTypeId<Interner>;
14pub type AssociatedTyDatum = chalk_rust_ir::AssociatedTyDatum<Interner>; 14pub type AssociatedTyDatum = chalk_solve::rust_ir::AssociatedTyDatum<Interner>;
15pub type TraitId = chalk_ir::TraitId<Interner>; 15pub type TraitId = chalk_ir::TraitId<Interner>;
16pub type TraitDatum = chalk_rust_ir::TraitDatum<Interner>; 16pub type TraitDatum = chalk_solve::rust_ir::TraitDatum<Interner>;
17pub type AdtId = chalk_ir::AdtId<Interner>; 17pub type AdtId = chalk_ir::AdtId<Interner>;
18pub type StructDatum = chalk_rust_ir::AdtDatum<Interner>; 18pub type StructDatum = chalk_solve::rust_ir::AdtDatum<Interner>;
19pub type ImplId = chalk_ir::ImplId<Interner>; 19pub type ImplId = chalk_ir::ImplId<Interner>;
20pub type ImplDatum = chalk_rust_ir::ImplDatum<Interner>; 20pub type ImplDatum = chalk_solve::rust_ir::ImplDatum<Interner>;
21pub type AssociatedTyValueId = chalk_rust_ir::AssociatedTyValueId<Interner>; 21pub type AssociatedTyValueId = chalk_solve::rust_ir::AssociatedTyValueId<Interner>;
22pub type AssociatedTyValue = chalk_rust_ir::AssociatedTyValue<Interner>; 22pub type AssociatedTyValue = chalk_solve::rust_ir::AssociatedTyValue<Interner>;
23pub type FnDefId = chalk_ir::FnDefId<Interner>;
24pub type FnDefDatum = chalk_solve::rust_ir::FnDefDatum<Interner>;
23 25
24impl chalk_ir::interner::Interner for Interner { 26impl 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};
10use chalk_solve::rust_ir;
10 11
11use hir_def::{type_ref::Mutability, AssocContainerId, GenericDefId, Lookup, TypeAliasId}; 12use hir_def::{type_ref::Mutability, AssocContainerId, GenericDefId, Lookup, TypeAliasId};
12use ra_db::salsa::InternKey; 13use 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
22use super::interner::*; 23use 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
370impl 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
350impl ToChalk for TypeAliasId { 382impl 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
3mod completion_config; 1mod completion_config;
4mod completion_item; 2mod completion_item;
5mod completion_context; 3mod 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)]
130mod 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 2use ra_assists::utils::TryEnum;
3use ra_syntax::{ 3use ra_syntax::{
4 ast::{self, AstNode}, 4 ast::{self, AstNode},
5 TextRange, TextSize, 5 TextRange, TextSize,
6}; 6};
7use ra_text_edit::TextEdit; 7use ra_text_edit::TextEdit;
8 8
9use super::completion_config::SnippetCap;
10use crate::{ 9use 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};
17use ra_assists::utils::TryEnum; 16
17use super::completion_config::SnippetCap;
18 18
19pub(super) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) { 19pub(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
189fn get_receiver_text(receiver: &ast::Expr, receiver_is_ambiguous_float_literal: bool) -> String { 199fn 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::{
49pub(crate) fn complete_trait_impl(acc: &mut Completions, ctx: &CompletionContext) { 49pub(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::{
12use ra_text_edit::Indel; 12use ra_text_edit::Indel;
13 13
14use crate::{call_info::ActiveParameter, completion::CompletionConfig, FilePosition}; 14use crate::{call_info::ActiveParameter, completion::CompletionConfig, FilePosition};
15use 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::{
10use hir::{Docs, Documentation, HasSource, HirDisplay}; 10use hir::{Docs, Documentation, HasSource, HirDisplay};
11use ra_ide_db::RootDatabase; 11use ra_ide_db::RootDatabase;
12use ra_syntax::ast::{self, AstNode, NameOwner, VisibilityOwner}; 12use ra_syntax::ast::{self, AstNode, NameOwner, VisibilityOwner};
13use stdx::SepBy; 13use stdx::{split1, SepBy};
14 14
15use crate::display::{generic_parameters, where_predicates}; 15use 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
3use crate::TextRange;
4
5use ra_syntax::{ 1use 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// |===
21pub fn file_structure(file: &SourceFile) -> Vec<StructureNode> { 30pub 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
3use hir::Semantics; 1use hir::Semantics;
4use ra_ide_db::RootDatabase; 2use ra_ide_db::RootDatabase;
5use ra_syntax::{ 3use 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// |===
17pub(crate) fn expand_macro(db: &RootDatabase, position: FilePosition) -> Option<ExpandedMacro> { 24pub(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
3use std::iter::successors; 1use std::iter::successors;
4 2
5use hir::Semantics; 3use hir::Semantics;
@@ -14,6 +12,16 @@ use ra_syntax::{
14 12
15use crate::FileRange; 13use 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// |===
17pub(crate) fn extend_selection(db: &RootDatabase, frange: FileRange) -> TextRange { 25pub(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
3use hir::Semantics; 1use hir::Semantics;
4use ra_ide_db::{ 2use 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// |===
20pub(crate) fn goto_definition( 27pub(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
3use hir::{Crate, ImplDef, Semantics}; 1use hir::{Crate, ImplDef, Semantics};
4use ra_ide_db::RootDatabase; 2use ra_ide_db::RootDatabase;
5use ra_syntax::{algo::find_node_at_offset, ast, AstNode}; 3use ra_syntax::{algo::find_node_at_offset, ast, AstNode};
6 4
7use crate::{display::ToNav, FilePosition, NavigationTarget, RangeInfo}; 5use 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// |===
9pub(crate) fn goto_implementation( 16pub(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
3use ra_ide_db::RootDatabase; 1use ra_ide_db::RootDatabase;
4use ra_syntax::{ast, match_ast, AstNode, SyntaxKind::*, SyntaxToken, TokenAtOffset}; 2use ra_syntax::{ast, match_ast, AstNode, SyntaxKind::*, SyntaxToken, TokenAtOffset};
5 3
6use crate::{display::ToNav, FilePosition, NavigationTarget, RangeInfo}; 4use 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// |===
8pub(crate) fn goto_type_definition( 15pub(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 1use std::iter::once;
2//! source code items (e.g. function call, struct field, variable symbol...)
3 2
4use hir::{ 3use 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};
7use itertools::Itertools;
8use ra_db::SourceDatabase; 8use ra_db::SourceDatabase;
9use ra_ide_db::{ 9use 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};
13use ra_syntax::{ 13use 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
20use crate::{ 15use 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};
24use itertools::Itertools;
25use 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.
62pub(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
65fn hover_text( 115fn 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
163pub(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
216fn pick_best(tokens: TokenAtOffset<SyntaxToken>) -> Option<SyntaxToken> { 217fn 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 &["
505Option 506Option
507```
508
509```rust
506None 510None
507``` 511```
512___
508 513
509The None variant 514The None variant
510 " 515 "
@@ -524,8 +529,12 @@ The None variant
524 "#, 529 "#,
525 &[" 530 &["
526Option 531Option
532```
533
534```rust
527Some 535Some
528``` 536```
537___
529 538
530The Some variant 539The 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
3use hir::{Adt, HirDisplay, Semantics, Type}; 1use hir::{Adt, HirDisplay, Semantics, Type};
4use ra_ide_db::RootDatabase; 2use ra_ide_db::RootDatabase;
5use ra_prof::profile; 3use 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// |===
42pub(crate) fn inlay_hints( 60pub(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
3use itertools::Itertools; 1use itertools::Itertools;
4use ra_fmt::{compute_ws, extract_trivial_expression}; 2use ra_fmt::{compute_ws, extract_trivial_expression};
5use ra_syntax::{ 3use ra_syntax::{
@@ -11,6 +9,15 @@ use ra_syntax::{
11}; 9};
12use ra_text_edit::{TextEdit, TextEditBuilder}; 10use 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// |===
14pub fn join_lines(file: &SourceFile, range: TextRange) -> TextEdit { 21pub 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;
23mod runnables; 23mod runnables;
24mod goto_definition; 24mod goto_definition;
25mod goto_type_definition; 25mod goto_type_definition;
26mod goto_implementation;
26mod extend_selection; 27mod extend_selection;
27mod hover; 28mod hover;
28mod call_hierarchy; 29mod call_hierarchy;
@@ -30,7 +31,6 @@ mod call_info;
30mod syntax_highlighting; 31mod syntax_highlighting;
31mod parent_module; 32mod parent_module;
32mod references; 33mod references;
33mod impls;
34mod diagnostics; 34mod diagnostics;
35mod syntax_tree; 35mod syntax_tree;
36mod folding_ranges; 36mod 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
3use ra_syntax::{ast::AstNode, SourceFile, SyntaxKind, TextSize, T}; 1use 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// |===
5pub fn matching_brace(file: &SourceFile, offset: TextSize) -> Option<TextSize> { 14pub 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
3use std::str::FromStr;
3use std::sync::Arc; 4use std::sync::Arc;
4 5
5use ra_cfg::CfgOptions; 6use ra_cfg::CfgOptions;
6use ra_db::{CrateName, Env, RelativePathBuf}; 7use ra_db::{CrateName, Env, RelativePathBuf};
7use test_utils::{extract_offset, extract_range, parse_fixture, CURSOR_MARKER}; 8use test_utils::{extract_offset, extract_range, parse_fixture, FixtureEntry, CURSOR_MARKER};
8 9
9use crate::{ 10use 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)]
16enum MockFileData {
17 Plain { path: String, content: String },
18 Fixture(FixtureEntry),
19}
20
21impl 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
68impl 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)]
17pub struct MockAnalysis { 77pub struct MockAnalysis {
18 files: Vec<(String, String)>, 78 files: Vec<MockFileData>,
19} 79}
20 80
21impl MockAnalysis { 81impl 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
3use hir::Semantics; 1use hir::Semantics;
4use ra_db::{CrateId, FileId, FilePosition}; 2use ra_db::{CrateId, FileId, FilePosition};
5use ra_ide_db::RootDatabase; 3use ra_ide_db::RootDatabase;
@@ -11,6 +9,16 @@ use test_utils::mark;
11 9
12use crate::NavigationTarget; 10use 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.
16pub(crate) fn parent_module(db: &RootDatabase, position: FilePosition) -> Vec<NavigationTarget> { 24pub(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 1use std::fmt;
2 2
3use hir::{AsAssocItem, Semantics}; 3use hir::{AsAssocItem, Attrs, HirFileId, InFile, Semantics};
4use itertools::Itertools; 4use itertools::Itertools;
5use ra_cfg::CfgExpr;
5use ra_ide_db::RootDatabase; 6use ra_ide_db::RootDatabase;
6use ra_syntax::{ 7use 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
11use crate::FileId; 12use crate::{display::ToNav, FileId, NavigationTarget};
12use ast::DocCommentsOwner;
13use std::fmt::Display;
14 13
15#[derive(Debug)] 14#[derive(Debug)]
16pub struct Runnable { 15pub 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
27impl Display for TestId { 27impl 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// |===
45pub(crate) fn runnables(db: &RootDatabase, file_id: FileId) -> Vec<Runnable> { 56pub(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
51fn runnable(sema: &Semantics<RootDatabase>, item: SyntaxNode) -> Option<Runnable> { 62fn 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
61fn runnable_fn(sema: &Semantics<RootDatabase>, fn_def: ast::FnDef) -> Option<Runnable> { 72fn 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
150fn runnable_mod(sema: &Semantics<RootDatabase>, module: ast::Module) -> Option<Runnable> { 171fn 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">// =&gt; "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">// =&gt; "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">// =&gt; "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">// =&gt; "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">// =&gt; "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">// =&gt; "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">// =&gt; "{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>
3body { margin: 0; }
4pre { 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>) -&gt; <span class="builtin_type">i32</span>; 39 <span class="keyword">fn</span> <span class="function declaration">bar</span>(&<span class="self_keyword">self</span>) -&gt; <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>) -&gt; <span class="builtin_type">i32</span> { 43 <span class="keyword">fn</span> <span class="function declaration">bar</span>(&<span class="self_keyword">self</span>) -&gt; <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>&lt;<span class="type_param declaration">T</span>&gt; <span class="enum">Option</span>&lt;<span class="type_param">T</span>&gt; { 95<span class="keyword">impl</span>&lt;<span class="type_param declaration">T</span>&gt; <span class="enum">Option</span>&lt;<span class="type_param">T</span>&gt; {
94 <span class="keyword">fn</span> <span class="function declaration">and</span>&lt;<span class="type_param declaration">U</span>&gt;(<span class="keyword">self</span>, <span class="variable declaration">other</span>: <span class="enum">Option</span>&lt;<span class="type_param">U</span>&gt;) -&gt; <span class="enum">Option</span>&lt;(<span class="type_param">T</span>, <span class="type_param">U</span>)&gt; { 96 <span class="keyword">fn</span> <span class="function declaration">and</span>&lt;<span class="type_param declaration">U</span>&gt;(<span class="self_keyword">self</span>, <span class="variable declaration">other</span>: <span class="enum">Option</span>&lt;<span class="type_param">U</span>&gt;) -&gt; <span class="enum">Option</span>&lt;(<span class="type_param">T</span>, <span class="type_param">U</span>)&gt; {
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> =&gt; <span class="macro">unimplemented!</span>(), 98 <span class="enum_variant">None</span> =&gt; <span class="macro">unimplemented!</span>(),
97 <span class="variable declaration">Nope</span> =&gt; <span class="variable">Nope</span>, 99 <span class="variable declaration">Nope</span> =&gt; <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
3use std::{collections::HashMap, iter::once, str::FromStr}; 1use std::{collections::HashMap, iter::once, str::FromStr};
4 2
5use ra_db::{SourceDatabase, SourceDatabaseExt}; 3use ra_db::{SourceDatabase, SourceDatabaseExt};
@@ -25,6 +23,28 @@ impl std::fmt::Display for SsrError {
25 23
26impl std::error::Error for SsrError {} 24impl 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// |===
28pub fn parse_search_replace( 48pub 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
3use std::{fmt, iter::FromIterator, sync::Arc}; 1use std::{fmt, iter::FromIterator, sync::Arc};
4 2
5use hir::MacroFile; 3use 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// |===
29pub(crate) fn status(db: &RootDatabase) -> String { 36pub(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
3mod tags; 1mod tags;
4mod html; 2mod 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
36struct 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//
42impl 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
110pub(crate) fn highlight( 42pub(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)]
227struct HighlightedRangeStack {
228 stack: Vec<Vec<HighlightedRange>>,
229}
230
231/// We use a stack to implement the flattening logic for the highlighted
232/// syntax ranges.
233impl 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
294fn highlight_format_specifier(kind: FormatSpecifier) -> Option<HighlightTag> { 301fn 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
480fn highlight_name_by_syntax(name: ast::Name) -> Highlight { 515fn 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
500fn highlight_injection( 543fn 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)]
16pub enum HighlightTag { 16pub 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)]
47pub enum HighlightModifier { 50pub 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
96impl HighlightModifier { 104impl 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]
263fn test_unsafe_highlighting() {
264 let (analysis, file_id) = single_file(
265 r#"
266unsafe fn unsafe_fn() {}
267
268struct HasUnsafeFn;
269
270impl HasUnsafeFn {
271 unsafe fn unsafe_method(&self) {}
272}
273
274fn 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 1use ra_db::{FileId, SourceDatabase};
2
3use ra_db::SourceDatabase;
4use ra_ide_db::RootDatabase; 2use ra_ide_db::RootDatabase;
5use ra_syntax::{ 3use 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
11pub 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// |===
13pub(crate) fn syntax_tree( 19pub(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
33pub(crate) const TRIGGER_CHARS: &str = ".=>"; 33pub(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
35pub(crate) fn on_char_typed( 42pub(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};
12use ra_text_edit::TextEdit; 12use ra_text_edit::TextEdit;
13 13
14use crate::{SourceChange, SourceFileEdit}; 14pub(crate) fn on_enter(db: &RootDatabase, position: FilePosition) -> Option<TextEdit> {
15
16pub(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
49fn followed_by_comment(comment: &ast::Comment) -> bool { 45fn 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// |===
113pub fn world_symbols(db: &RootDatabase, query: Query) -> Vec<FileSymbol> { 134pub 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"
22difference = "2.0.0" 22difference = "2.0.0"
23# used as proc macro test target 23# used as proc macro test target
24serde_derive = "1.0.106" 24serde_derive = "1.0.106"
25ra_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
3use crate::dylib; 3use crate::dylib;
4use crate::ProcMacroSrv; 4use crate::ProcMacroSrv;
5pub use difference::Changeset as __Changeset;
6use ra_proc_macro::ListMacrosTask; 5use ra_proc_macro::ListMacrosTask;
7use std::str::FromStr; 6use std::str::FromStr;
8use test_utils::assert_eq_text; 7use 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;
5use rustc_hash::{FxHashMap, FxHashSet}; 5use rustc_hash::{FxHashMap, FxHashSet};
6use serde::Deserialize; 6use serde::Deserialize;
7 7
8/// Roots and crates that compose this Rust project.
9#[derive(Clone, Debug, Deserialize)]
10pub 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)] 68mod tests {
53pub 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::{
14use anyhow::{bail, Context, Result}; 14use anyhow::{bail, Context, Result};
15use ra_cfg::CfgOptions; 15use ra_cfg::CfgOptions;
16use ra_db::{CrateGraph, CrateName, Edition, Env, ExternSource, ExternSourceId, FileId}; 16use ra_db::{CrateGraph, CrateName, Edition, Env, ExternSource, ExternSourceId, FileId};
17use rustc_hash::FxHashMap; 17use rustc_hash::{FxHashMap, FxHashSet};
18use serde_json::from_reader; 18use serde_json::from_reader;
19 19
20pub use crate::{ 20pub use crate::{
@@ -32,6 +32,12 @@ pub enum ProjectWorkspace {
32 Json { project: JsonProject }, 32 Json { project: JsonProject },
33} 33}
34 34
35impl 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)]
61pub enum ProjectRoot { 67pub enum ProjectManifest {
62 ProjectJson(PathBuf), 68 ProjectJson(PathBuf),
63 CargoToml(PathBuf), 69 CargoToml(PathBuf),
64} 70}
65 71
66impl ProjectRoot { 72impl 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
133impl ProjectWorkspace { 151impl 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]
14itertools = "0.9.0" 14itertools = "0.9.0"
15rowan = "0.10.0" 15rowan = "0.10.0"
16rustc_lexer = { version = "656.0.0", package = "rustc-ap-rustc_lexer" } 16rustc_lexer = { version = "661.0.0", package = "rustc-ap-rustc_lexer" }
17rustc-hash = "1.1.0" 17rustc-hash = "1.1.0"
18arrayvec = "0.5.1" 18arrayvec = "0.5.1"
19once_cell = "1.3.1" 19once_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> {
75impl<N: AstNode> Iterator for AstChildren<N> { 75impl<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 {
1081impl ast::AttrsOwner for BlockExpr {} 1081impl ast::AttrsOwner for BlockExpr {}
1082impl ast::ModuleItemOwner for BlockExpr {} 1082impl ast::ModuleItemOwner for BlockExpr {}
1083impl BlockExpr { 1083impl 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};
9use rustc_lexer::unescape::{unescape_literal, Mode};
9 10
10impl Comment { 11impl 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
91impl 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
3mod block; 3mod block;
4 4
5use std::convert::TryFrom;
6
7use rustc_lexer::unescape;
8
9use crate::{ 5use 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};
10use rustc_lexer::unescape::{
11 self, unescape_byte, unescape_byte_literal, unescape_char, unescape_literal, Mode,
12};
13use std::convert::TryFrom;
14 14
15fn rustc_unescape_error_to_string(err: unescape::EscapeError) -> &'static str { 15fn 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
82pub(crate) fn validate(root: &SyntaxNode) -> Vec<SyntaxError> { 82pub(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 @@
1SOURCE_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 @@
1fn foo() { 1fn 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" }
40ra_syntax = { path = "../ra_syntax" } 40ra_syntax = { path = "../ra_syntax" }
41ra_text_edit = { path = "../ra_text_edit" } 41ra_text_edit = { path = "../ra_text_edit" }
42ra_vfs = "0.6.0" 42ra_vfs = "0.6.0"
43ra_cfg = { path = "../ra_cfg"}
43 44
44# This should only be used in CLI 45# This should only be used in CLI
45ra_db = { path = "../ra_db" } 46ra_db = { path = "../ra_db" }
@@ -55,6 +56,8 @@ winapi = "0.3.8"
55tempfile = "3.1.0" 56tempfile = "3.1.0"
56insta = "0.16.0" 57insta = "0.16.0"
57test_utils = { path = "../test_utils" } 58test_utils = { path = "../test_utils" }
59mbe = { path = "../ra_mbe", package = "ra_mbe" }
60tt = { path = "../ra_tt", package = "ra_tt" }
58 61
59[features] 62[features]
60jemalloc = [ "ra_prof/jemalloc" ] 63jemalloc = [ "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 @@
4mod args; 4mod args;
5 5
6use lsp_server::Connection; 6use lsp_server::Connection;
7use rust_analyzer::{cli, config::Config, from_json, Result}; 7use rust_analyzer::{
8 cli,
9 config::{Config, LinkedProject},
10 from_json, Result,
11};
8 12
9use crate::args::HelpPrinted; 13use crate::args::HelpPrinted;
14use ra_project_model::ProjectManifest;
10 15
11fn main() -> Result<()> { 16fn 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
3use ra_cfg::CfgExpr;
3use ra_ide::{FileId, RunnableKind, TestId}; 4use ra_ide::{FileId, RunnableKind, TestId};
4use ra_project_model::{self, ProjectWorkspace, TargetKind}; 5use ra_project_model::{self, ProjectWorkspace, TargetKind};
5 6
6use crate::{world::WorldSnapshot, Result}; 7use 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
149fn 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)]
169mod 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};
8use ra_db::{ExternSourceId, FileId, SourceRootId}; 8use ra_db::{ExternSourceId, FileId, SourceRootId};
9use ra_ide::{AnalysisChange, AnalysisHost}; 9use ra_ide::{AnalysisChange, AnalysisHost};
10use ra_project_model::{ 10use 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};
13use ra_vfs::{RootEntry, Vfs, VfsChange, VfsTask, Watch}; 14use ra_vfs::{RootEntry, Vfs, VfsChange, VfsTask, Watch};
14use rustc_hash::{FxHashMap, FxHashSet}; 15use 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};
12use lsp_types::ClientCapabilities; 12use lsp_types::ClientCapabilities;
13use ra_flycheck::FlycheckConfig; 13use ra_flycheck::FlycheckConfig;
14use ra_ide::{AssistConfig, CompletionConfig, InlayHintsConfig}; 14use ra_ide::{AssistConfig, CompletionConfig, InlayHintsConfig};
15use ra_project_model::CargoConfig; 15use ra_project_model::{CargoConfig, JsonProject, ProjectManifest};
16use serde::Deserialize; 16use serde::Deserialize;
17 17
18#[derive(Debug, Clone)] 18#[derive(Debug, Clone)]
19pub struct Config { 19pub 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)]
43pub enum LinkedProject {
44 ProjectManifest(ProjectManifest),
45 JsonProject(JsonProject),
46}
47
48impl From<ProjectManifest> for LinkedProject {
49 fn from(v: ProjectManifest) -> Self {
50 LinkedProject::ProjectManifest(v)
51 }
52}
53
54impl 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)]
346enum 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};
3use ra_ide::{LineCol, LineIndex}; 3use ra_ide::{LineCol, LineIndex};
4use ra_syntax::{TextRange, TextSize}; 4use ra_syntax::{TextRange, TextSize};
5 5
6use crate::{world::WorldSnapshot, Result}; 6use crate::{global_state::GlobalStateSnapshot, Result};
7 7
8pub(crate) fn offset(line_index: &LineIndex, position: lsp_types::Position) -> TextSize { 8pub(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
19pub(crate) fn file_id(world: &WorldSnapshot, url: &lsp_types::Url) -> Result<FileId> { 19pub(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
23pub(crate) fn file_position( 23pub(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
33pub(crate) fn file_range( 33pub(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)]
59pub struct WorldState { 59pub 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.
73pub struct WorldSnapshot { 73pub 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
82impl WorldState { 82impl 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
278impl WorldSnapshot { 282impl 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;
26mod markdown; 26mod markdown;
27pub mod lsp_ext; 27pub mod lsp_ext;
28pub mod config; 28pub mod config;
29mod world; 29mod global_state;
30mod diagnostics; 30mod diagnostics;
31mod semantic_tokens; 31mod 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 @@
3use std::{collections::HashMap, path::PathBuf}; 3use std::{collections::HashMap, path::PathBuf};
4 4
5use lsp_types::request::Request; 5use lsp_types::request::Request;
6use lsp_types::{Location, Position, Range, TextDocumentIdentifier}; 6use lsp_types::{Position, Range, TextDocumentIdentifier};
7use rustc_hash::FxHashMap;
8use serde::{Deserialize, Serialize}; 7use serde::{Deserialize, Serialize};
9 8
10pub enum AnalyzerStatus {} 9pub 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")]
43pub struct ExpandedMacro {
44 pub name: String,
45 pub expansion: String,
46}
47
48pub enum ExpandMacro {} 40pub enum ExpandMacro {}
49 41
50impl Request for ExpandMacro { 42impl Request for ExpandMacro {
@@ -57,30 +49,37 @@ impl Request for ExpandMacro {
57#[serde(rename_all = "camelCase")] 49#[serde(rename_all = "camelCase")]
58pub struct ExpandMacroParams { 50pub 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
63pub enum FindMatchingBrace {} 55#[derive(Deserialize, Serialize, Debug)]
56#[serde(rename_all = "camelCase")]
57pub struct ExpandedMacro {
58 pub name: String,
59 pub expansion: String,
60}
61
62pub enum MatchingBrace {}
64 63
65impl Request for FindMatchingBrace { 64impl 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")]
73pub struct FindMatchingBraceParams { 72pub 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
78pub enum ParentModule {} 77pub enum ParentModule {}
79 78
80impl Request for ParentModule { 79impl 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
86pub enum JoinLines {} 85pub enum JoinLines {}
@@ -102,8 +101,8 @@ pub enum OnEnter {}
102 101
103impl Request for OnEnter { 102impl 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
109pub enum Runnables {} 108pub enum Runnables {}
@@ -111,7 +110,7 @@ pub enum Runnables {}
111impl Request for Runnables { 110impl 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")]
126pub struct Runnable { 125pub 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")]
135pub enum RunnableKind {
136 Cargo,
137}
138
139#[derive(Deserialize, Serialize, Debug)]
140#[serde(rename_all = "camelCase")]
141pub 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
136pub enum InlayHints {} 150pub 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
20use crossbeam_channel::{never, select, unbounded, RecvError, Sender}; 19use crossbeam_channel::{never, select, unbounded, RecvError, Sender};
21use itertools::Itertools;
22use lsp_server::{Connection, ErrorCode, Message, Notification, Request, RequestId, Response}; 20use lsp_server::{Connection, ErrorCode, Message, Notification, Request, RequestId, Response};
23use lsp_types::{ 21use lsp_types::{
24 DidChangeTextDocumentParams, NumberOrString, TextDocumentContentChangeEvent, WorkDoneProgress, 22 DidChangeTextDocumentParams, NumberOrString, TextDocumentContentChangeEvent, WorkDoneProgress,
@@ -36,14 +34,15 @@ use serde::{de::DeserializeOwned, Serialize};
36use threadpool::ThreadPool; 34use threadpool::ThreadPool;
37 35
38use crate::{ 36use 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
70impl Error for LspError {} 69impl Error for LspError {}
71 70
72pub fn main_loop(ws_roots: Vec<PathBuf>, config: Config, connection: Connection) -> Result<()> { 71pub 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
487fn on_request( 479fn 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
555fn on_notification( 545fn 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
729fn on_check_task( 719fn 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
809fn on_diagnostic_task(task: DiagnosticTask, msg_sender: &Sender<Message>, state: &mut WorldState) { 799fn 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) {
881struct PoolDispatcher<'a> { 871struct 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
1015fn update_file_notifications_on_threadpool( 1008fn 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};
20use ra_ide::{ 20use ra_ide::{
21 FileId, FilePosition, FileRange, Query, RangeInfo, Runnable, RunnableKind, SearchScope, 21 FileId, FilePosition, FileRange, Query, RangeInfo, RunnableKind, SearchScope, TextEdit,
22 TextEdit,
23}; 22};
24use ra_prof::profile; 23use ra_prof::profile;
25use ra_project_model::TargetKind; 24use ra_project_model::TargetKind;
26use ra_syntax::{AstNode, SyntaxKind, TextRange, TextSize}; 25use ra_syntax::{AstNode, SyntaxKind, TextRange, TextSize};
27use rustc_hash::FxHashMap;
28use serde::{Deserialize, Serialize}; 26use serde::{Deserialize, Serialize};
29use serde_json::to_value; 27use serde_json::to_value;
30use stdx::format_to; 28use 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
43pub fn handle_analyzer_status(world: WorldSnapshot, _: ()) -> Result<String> { 40pub 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
55pub fn handle_syntax_tree( 52pub 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, &params.text_document.uri)?; 57 let id = from_proto::file_id(&snap, &params.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
67pub fn handle_expand_macro( 64pub 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, &params.text_document.uri)?; 69 let file_id = from_proto::file_id(&snap, &params.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
85pub fn handle_selection_range( 77pub 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, &params.text_document.uri)?; 82 let file_id = from_proto::file_id(&snap, &params.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
128pub fn handle_find_matching_brace( 120pub 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, &params.text_document.uri)?; 125 let file_id = from_proto::file_id(&snap, &params.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
150pub fn handle_join_lines( 142pub 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, &params.text_document.uri)?; 147 let file_id = from_proto::file_id(&snap, &params.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
173pub fn handle_on_enter( 165pub 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`.
186pub fn handle_on_type_formatting( 182pub 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
225pub fn handle_document_symbol( 221pub 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, &params.text_document.uri)?; 226 let file_id = from_proto::file_id(&snap, &params.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
293pub fn handle_workspace_symbol( 289pub 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
337pub fn handle_goto_definition( 333pub 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
355pub fn handle_goto_implementation( 348pub 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
373pub fn handle_goto_type_definition( 363pub 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
391pub fn handle_parent_module( 378pub 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
405pub fn handle_runnables( 389pub 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, &params.text_document.uri)?; 394 let file_id = from_proto::file_id(&snap, &params.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
464pub fn handle_completion( 454pub 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
506pub fn handle_folding_range( 496pub 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, &params.text_document.uri)?; 501 let file_id = from_proto::file_id(&snap, &params.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
523pub fn handle_signature_help( 513pub 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
547pub fn handle_hover(world: WorldSnapshot, params: lsp_types::HoverParams) -> Result<Option<Hover>> { 537pub 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
566pub fn handle_prepare_rename( 559pub 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
584pub fn handle_rename(world: WorldSnapshot, params: RenameParams) -> Result<Option<WorkspaceEdit>> { 577pub 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
605pub fn handle_references( 601pub 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
632pub fn handle_formatting( 628pub 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, &params.text_document.uri)?; 633 let file_id = from_proto::file_id(&snap, &params.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
708pub fn handle_code_action( 704pub 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, &params.text_document.uri)?; 716 let file_id = from_proto::file_id(&snap, &params.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
756pub fn handle_code_lens( 757pub 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, &params.text_document.uri)?; 769 let file_id = from_proto::file_id(&snap, &params.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
872pub fn handle_code_lens_resolve(world: WorldSnapshot, code_lens: CodeLens) -> Result<CodeLens> { 868pub 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
917pub fn handle_document_highlight( 916pub 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
944pub fn handle_ssr( 943pub 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(&params.query, params.parse_only)??; 949 snap.analysis().structural_search_replace(&params.query, params.parse_only)??;
951 to_proto::workspace_edit(&world, source_change) 950 to_proto::workspace_edit(&snap, source_change)
952} 951}
953 952
954pub fn publish_diagnostics(world: &WorldSnapshot, file_id: FileId) -> Result<DiagnosticTask> { 953pub 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
974fn 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
1007pub fn handle_inlay_hints( 973pub 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, &params.text_document.uri)?; 978 let file_id = from_proto::file_id(&snap, &params.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
1022pub fn handle_call_hierarchy_prepare( 988pub 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
1044pub fn handle_call_hierarchy_incoming( 1010pub 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
1079pub fn handle_call_hierarchy_outgoing( 1045pub 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
1114pub fn handle_semantic_tokens( 1080pub 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, &params.text_document.uri)?; 1086 let file_id = from_proto::file_id(&snap, &params.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
1129pub fn handle_semantic_tokens_range( 1095pub 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
37define_semantic_token_types![ 37define_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};
3use ra_ide::{ 3use 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};
9use ra_syntax::{SyntaxKind, TextRange, TextSize}; 9use ra_syntax::{SyntaxKind, TextRange, TextSize};
10use ra_vfs::LineEndings; 10use ra_vfs::LineEndings;
11 11
12use crate::{lsp_ext, semantic_tokens, world::WorldSnapshot, Result}; 12use crate::{
13 cargo_target_spec::CargoTargetSpec, global_state::GlobalStateSnapshot, lsp_ext,
14 semantic_tokens, Result,
15};
13 16
14pub(crate) fn position(line_index: &LineIndex, offset: TextSize) -> lsp_types::Position { 17pub(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
141pub(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
138pub(crate) fn completion_item( 153pub(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
369pub(crate) fn url(world: &WorldSnapshot, file_id: FileId) -> Result<lsp_types::Url> { 388pub(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
373pub(crate) fn versioned_text_document_identifier( 392pub(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
382pub(crate) fn location(world: &WorldSnapshot, frange: FileRange) -> Result<lsp_types::Location> { 401pub(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
390pub(crate) fn location_link( 412pub(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
406fn location_info( 435fn 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
419pub(crate) fn goto_definition_response( 448pub(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
447pub(crate) fn snippet_text_document_edit( 476pub(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
463pub(crate) fn resource_op( 492pub(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
481pub(crate) fn snippet_workspace_edit( 510pub(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
499pub(crate) fn workspace_edit( 528pub(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
507impl From<lsp_ext::SnippetWorkspaceEdit> for lsp_types::WorkspaceEdit { 536impl 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
542pub fn call_hierarchy_item( 571pub 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
597pub(crate) fn code_action(world: &WorldSnapshot, assist: Assist) -> Result<lsp_ext::CodeAction> { 626pub(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
640pub(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]
62fn test_runnables_no_project() {
63 if skip_slow_tests() {
64 return;
65 }
66
67 let server = project(
68 r"
69//- lib.rs
70#[test]
71fn 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]
108fn test_runnables_project() { 62fn 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};
19use tempfile::TempDir; 19use tempfile::TempDir;
20use test_utils::{find_mismatch, parse_fixture}; 20use test_utils::{find_mismatch, parse_fixture};
21 21
22use ra_project_model::ProjectManifest;
22use rust_analyzer::{ 23use 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
111impl Server { 122impl 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
128pub 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
11difference = "2.0.0" 11difference = "2.0.0"
12text-size = "1.0.0" 12text-size = "1.0.0"
13serde_json = "1.0.48" 13serde_json = "1.0.48"
14relative-path = "1.0.0"
15rustc-hash = "1.1.0"
16
17ra_cfg = { path = "../ra_cfg" }
18stdx = { 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
17pub use ra_cfg::CfgOptions;
18use stdx::split1;
19
20pub use relative_path::{RelativePath, RelativePathBuf};
21pub use rustc_hash::FxHashMap;
17use serde_json::Value; 22use serde_json::Value;
18use text_size::{TextRange, TextSize}; 23use 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)]
159pub struct FixtureEntry { 164pub 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)]
170pub enum FixtureMeta {
171 Root { path: RelativePathBuf },
172 File(FileMeta),
173}
174
175#[derive(Debug, Eq, PartialEq)]
176pub 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
185impl 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/// ```
172pub fn parse_fixture(fixture: &str) -> Vec<FixtureEntry> { 249pub 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
291fn 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]
416fn 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
292pub fn parse_single_fixture(fixture: &str) -> Option<FixtureEntry> { 436pub 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 }