aboutsummaryrefslogtreecommitdiff
path: root/crates/ide
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ide')
-rw-r--r--crates/ide/src/completion.rs2
-rw-r--r--crates/ide/src/completion/complete_attribute.rs4
-rw-r--r--crates/ide/src/completion/complete_keyword.rs22
-rw-r--r--crates/ide/src/completion/complete_mod.rs324
-rw-r--r--crates/ide/src/completion/complete_qualified_path.rs24
-rw-r--r--crates/ide/src/completion/complete_trait_impl.rs377
-rw-r--r--crates/ide/src/completion/complete_unqualified_path.rs1
-rw-r--r--crates/ide/src/completion/completion_context.rs7
-rw-r--r--crates/ide/src/completion/patterns.rs1
-rw-r--r--crates/ide/src/diagnostics.rs62
-rw-r--r--crates/ide/src/display.rs9
-rw-r--r--crates/ide/src/hover.rs64
-rw-r--r--crates/ide/src/lib.rs4
-rw-r--r--crates/ide/src/syntax_highlighting.rs63
-rw-r--r--crates/ide/src/syntax_highlighting/test_data/highlighting.html13
-rw-r--r--crates/ide/src/syntax_highlighting/tests.rs13
-rw-r--r--crates/ide/src/typing.rs8
17 files changed, 900 insertions, 98 deletions
diff --git a/crates/ide/src/completion.rs b/crates/ide/src/completion.rs
index 33bed6991..daea2aa95 100644
--- a/crates/ide/src/completion.rs
+++ b/crates/ide/src/completion.rs
@@ -19,6 +19,7 @@ mod complete_unqualified_path;
19mod complete_postfix; 19mod complete_postfix;
20mod complete_macro_in_item_position; 20mod complete_macro_in_item_position;
21mod complete_trait_impl; 21mod complete_trait_impl;
22mod complete_mod;
22 23
23use ide_db::RootDatabase; 24use ide_db::RootDatabase;
24 25
@@ -124,6 +125,7 @@ pub(crate) fn completions(
124 complete_postfix::complete_postfix(&mut acc, &ctx); 125 complete_postfix::complete_postfix(&mut acc, &ctx);
125 complete_macro_in_item_position::complete_macro_in_item_position(&mut acc, &ctx); 126 complete_macro_in_item_position::complete_macro_in_item_position(&mut acc, &ctx);
126 complete_trait_impl::complete_trait_impl(&mut acc, &ctx); 127 complete_trait_impl::complete_trait_impl(&mut acc, &ctx);
128 complete_mod::complete_mod(&mut acc, &ctx);
127 129
128 Some(acc) 130 Some(acc)
129} 131}
diff --git a/crates/ide/src/completion/complete_attribute.rs b/crates/ide/src/completion/complete_attribute.rs
index 0abfaebcb..f4a9864d1 100644
--- a/crates/ide/src/completion/complete_attribute.rs
+++ b/crates/ide/src/completion/complete_attribute.rs
@@ -13,6 +13,10 @@ use crate::completion::{
13}; 13};
14 14
15pub(super) fn complete_attribute(acc: &mut Completions, ctx: &CompletionContext) -> Option<()> { 15pub(super) fn complete_attribute(acc: &mut Completions, ctx: &CompletionContext) -> Option<()> {
16 if ctx.mod_declaration_under_caret.is_some() {
17 return None;
18 }
19
16 let attribute = ctx.attribute_under_caret.as_ref()?; 20 let attribute = ctx.attribute_under_caret.as_ref()?;
17 match (attribute.path(), attribute.token_tree()) { 21 match (attribute.path(), attribute.token_tree()) {
18 (Some(path), Some(token_tree)) if path.to_string() == "derive" => { 22 (Some(path), Some(token_tree)) if path.to_string() == "derive" => {
diff --git a/crates/ide/src/completion/complete_keyword.rs b/crates/ide/src/completion/complete_keyword.rs
index 53ba76e0e..5645b41fa 100644
--- a/crates/ide/src/completion/complete_keyword.rs
+++ b/crates/ide/src/completion/complete_keyword.rs
@@ -510,6 +510,28 @@ pub mod future {
510 expect![[r#" 510 expect![[r#"
511 kw await expr.await 511 kw await expr.await
512 "#]], 512 "#]],
513 );
514
515 check(
516 r#"
517//- /main.rs
518use std::future::*;
519fn foo() {
520 let a = async {};
521 a.<|>
522}
523
524//- /std/lib.rs
525pub mod future {
526 #[lang = "future_trait"]
527 pub trait Future {
528 type Output;
529 }
530}
531"#,
532 expect![[r#"
533 kw await expr.await
534 "#]],
513 ) 535 )
514 } 536 }
515 537
diff --git a/crates/ide/src/completion/complete_mod.rs b/crates/ide/src/completion/complete_mod.rs
new file mode 100644
index 000000000..3cfc2e131
--- /dev/null
+++ b/crates/ide/src/completion/complete_mod.rs
@@ -0,0 +1,324 @@
1//! Completes mod declarations.
2
3use base_db::{SourceDatabaseExt, VfsPath};
4use hir::{Module, ModuleSource};
5use ide_db::RootDatabase;
6use rustc_hash::FxHashSet;
7
8use crate::{CompletionItem, CompletionItemKind};
9
10use super::{
11 completion_context::CompletionContext, completion_item::CompletionKind,
12 completion_item::Completions,
13};
14
15/// Complete mod declaration, i.e. `mod <|> ;`
16pub(super) fn complete_mod(acc: &mut Completions, ctx: &CompletionContext) -> Option<()> {
17 let mod_under_caret = match &ctx.mod_declaration_under_caret {
18 Some(mod_under_caret) if mod_under_caret.item_list().is_some() => return None,
19 Some(mod_under_caret) => mod_under_caret,
20 None => return None,
21 };
22
23 let _p = profile::span("completion::complete_mod");
24
25 let current_module = ctx.scope.module()?;
26
27 let module_definition_file =
28 current_module.definition_source(ctx.db).file_id.original_file(ctx.db);
29 let source_root = ctx.db.source_root(ctx.db.file_source_root(module_definition_file));
30 let directory_to_look_for_submodules = directory_to_look_for_submodules(
31 current_module,
32 ctx.db,
33 source_root.path_for_file(&module_definition_file)?,
34 )?;
35
36 let existing_mod_declarations = current_module
37 .children(ctx.db)
38 .filter_map(|module| Some(module.name(ctx.db)?.to_string()))
39 .collect::<FxHashSet<_>>();
40
41 let module_declaration_file =
42 current_module.declaration_source(ctx.db).map(|module_declaration_source_file| {
43 module_declaration_source_file.file_id.original_file(ctx.db)
44 });
45
46 source_root
47 .iter()
48 .filter(|submodule_candidate_file| submodule_candidate_file != &module_definition_file)
49 .filter(|submodule_candidate_file| {
50 Some(submodule_candidate_file) != module_declaration_file.as_ref()
51 })
52 .filter_map(|submodule_file| {
53 let submodule_path = source_root.path_for_file(&submodule_file)?;
54 let directory_with_submodule = submodule_path.parent()?;
55 match submodule_path.name_and_extension()? {
56 ("lib", Some("rs")) | ("main", Some("rs")) => None,
57 ("mod", Some("rs")) => {
58 if directory_with_submodule.parent()? == directory_to_look_for_submodules {
59 match directory_with_submodule.name_and_extension()? {
60 (directory_name, None) => Some(directory_name.to_owned()),
61 _ => None,
62 }
63 } else {
64 None
65 }
66 }
67 (file_name, Some("rs"))
68 if directory_with_submodule == directory_to_look_for_submodules =>
69 {
70 Some(file_name.to_owned())
71 }
72 _ => None,
73 }
74 })
75 .filter(|name| !existing_mod_declarations.contains(name))
76 .for_each(|submodule_name| {
77 let mut label = submodule_name;
78 if mod_under_caret.semicolon_token().is_none() {
79 label.push(';')
80 }
81 acc.add(
82 CompletionItem::new(CompletionKind::Magic, ctx.source_range(), &label)
83 .kind(CompletionItemKind::Module),
84 )
85 });
86
87 Some(())
88}
89
90fn directory_to_look_for_submodules(
91 module: Module,
92 db: &RootDatabase,
93 module_file_path: &VfsPath,
94) -> Option<VfsPath> {
95 let directory_with_module_path = module_file_path.parent()?;
96 let base_directory = match module_file_path.name_and_extension()? {
97 ("mod", Some("rs")) | ("lib", Some("rs")) | ("main", Some("rs")) => {
98 Some(directory_with_module_path)
99 }
100 (regular_rust_file_name, Some("rs")) => {
101 if matches!(
102 (
103 directory_with_module_path
104 .parent()
105 .as_ref()
106 .and_then(|path| path.name_and_extension()),
107 directory_with_module_path.name_and_extension(),
108 ),
109 (Some(("src", None)), Some(("bin", None)))
110 ) {
111 // files in /src/bin/ can import each other directly
112 Some(directory_with_module_path)
113 } else {
114 directory_with_module_path.join(regular_rust_file_name)
115 }
116 }
117 _ => None,
118 }?;
119
120 let mut resulting_path = base_directory;
121 for module in module_chain_to_containing_module_file(module, db) {
122 if let Some(name) = module.name(db) {
123 resulting_path = resulting_path.join(&name.to_string())?;
124 }
125 }
126
127 Some(resulting_path)
128}
129
130fn module_chain_to_containing_module_file(
131 current_module: Module,
132 db: &RootDatabase,
133) -> Vec<Module> {
134 let mut path = Vec::new();
135
136 let mut current_module = Some(current_module);
137 while let Some(ModuleSource::Module(_)) =
138 current_module.map(|module| module.definition_source(db).value)
139 {
140 if let Some(module) = current_module {
141 path.insert(0, module);
142 current_module = module.parent(db);
143 } else {
144 current_module = None;
145 }
146 }
147
148 path
149}
150
151#[cfg(test)]
152mod tests {
153 use crate::completion::{test_utils::completion_list, CompletionKind};
154 use expect_test::{expect, Expect};
155
156 fn check(ra_fixture: &str, expect: Expect) {
157 let actual = completion_list(ra_fixture, CompletionKind::Magic);
158 expect.assert_eq(&actual);
159 }
160
161 #[test]
162 fn lib_module_completion() {
163 check(
164 r#"
165 //- /lib.rs
166 mod <|>
167 //- /foo.rs
168 fn foo() {}
169 //- /foo/ignored_foo.rs
170 fn ignored_foo() {}
171 //- /bar/mod.rs
172 fn bar() {}
173 //- /bar/ignored_bar.rs
174 fn ignored_bar() {}
175 "#,
176 expect![[r#"
177 md bar;
178 md foo;
179 "#]],
180 );
181 }
182
183 #[test]
184 fn no_module_completion_with_module_body() {
185 check(
186 r#"
187 //- /lib.rs
188 mod <|> {
189
190 }
191 //- /foo.rs
192 fn foo() {}
193 "#,
194 expect![[r#""#]],
195 );
196 }
197
198 #[test]
199 fn main_module_completion() {
200 check(
201 r#"
202 //- /main.rs
203 mod <|>
204 //- /foo.rs
205 fn foo() {}
206 //- /foo/ignored_foo.rs
207 fn ignored_foo() {}
208 //- /bar/mod.rs
209 fn bar() {}
210 //- /bar/ignored_bar.rs
211 fn ignored_bar() {}
212 "#,
213 expect![[r#"
214 md bar;
215 md foo;
216 "#]],
217 );
218 }
219
220 #[test]
221 fn main_test_module_completion() {
222 check(
223 r#"
224 //- /main.rs
225 mod tests {
226 mod <|>;
227 }
228 //- /tests/foo.rs
229 fn foo() {}
230 "#,
231 expect![[r#"
232 md foo
233 "#]],
234 );
235 }
236
237 #[test]
238 fn directly_nested_module_completion() {
239 check(
240 r#"
241 //- /lib.rs
242 mod foo;
243 //- /foo.rs
244 mod <|>;
245 //- /foo/bar.rs
246 fn bar() {}
247 //- /foo/bar/ignored_bar.rs
248 fn ignored_bar() {}
249 //- /foo/baz/mod.rs
250 fn baz() {}
251 //- /foo/moar/ignored_moar.rs
252 fn ignored_moar() {}
253 "#,
254 expect![[r#"
255 md bar
256 md baz
257 "#]],
258 );
259 }
260
261 #[test]
262 fn nested_in_source_module_completion() {
263 check(
264 r#"
265 //- /lib.rs
266 mod foo;
267 //- /foo.rs
268 mod bar {
269 mod <|>
270 }
271 //- /foo/bar/baz.rs
272 fn baz() {}
273 "#,
274 expect![[r#"
275 md baz;
276 "#]],
277 );
278 }
279
280 // FIXME binary modules are not supported in tests properly
281 // Binary modules are a bit special, they allow importing the modules from `/src/bin`
282 // and that's why are good to test two things:
283 // * no cycles are allowed in mod declarations
284 // * no modules from the parent directory are proposed
285 // Unfortunately, binary modules support is in cargo not rustc,
286 // hence the test does not work now
287 //
288 // #[test]
289 // fn regular_bin_module_completion() {
290 // check(
291 // r#"
292 // //- /src/bin.rs
293 // fn main() {}
294 // //- /src/bin/foo.rs
295 // mod <|>
296 // //- /src/bin/bar.rs
297 // fn bar() {}
298 // //- /src/bin/bar/bar_ignored.rs
299 // fn bar_ignored() {}
300 // "#,
301 // expect![[r#"
302 // md bar;
303 // "#]],
304 // );
305 // }
306
307 #[test]
308 fn already_declared_bin_module_completion_omitted() {
309 check(
310 r#"
311 //- /src/bin.rs
312 fn main() {}
313 //- /src/bin/foo.rs
314 mod <|>
315 //- /src/bin/bar.rs
316 mod foo;
317 fn bar() {}
318 //- /src/bin/bar/bar_ignored.rs
319 fn bar_ignored() {}
320 "#,
321 expect![[r#""#]],
322 );
323 }
324}
diff --git a/crates/ide/src/completion/complete_qualified_path.rs b/crates/ide/src/completion/complete_qualified_path.rs
index accb09f7e..00e89f0fd 100644
--- a/crates/ide/src/completion/complete_qualified_path.rs
+++ b/crates/ide/src/completion/complete_qualified_path.rs
@@ -13,7 +13,7 @@ pub(super) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionCon
13 None => return, 13 None => return,
14 }; 14 };
15 15
16 if ctx.attribute_under_caret.is_some() { 16 if ctx.attribute_under_caret.is_some() || ctx.mod_declaration_under_caret.is_some() {
17 return; 17 return;
18 } 18 }
19 19
@@ -730,4 +730,26 @@ fn f() {}
730 expect![[""]], 730 expect![[""]],
731 ); 731 );
732 } 732 }
733
734 #[test]
735 fn completes_function() {
736 check(
737 r#"
738fn foo(
739 a: i32,
740 b: i32
741) {
742
743}
744
745fn main() {
746 fo<|>
747}
748"#,
749 expect![[r#"
750 fn foo(…) fn foo(a: i32, b: i32)
751 fn main() fn main()
752 "#]],
753 );
754 }
733} 755}
diff --git a/crates/ide/src/completion/complete_trait_impl.rs b/crates/ide/src/completion/complete_trait_impl.rs
index 26f268bd1..ff115df92 100644
--- a/crates/ide/src/completion/complete_trait_impl.rs
+++ b/crates/ide/src/completion/complete_trait_impl.rs
@@ -46,76 +46,86 @@ use crate::{
46 display::function_declaration, 46 display::function_declaration,
47}; 47};
48 48
49#[derive(Debug, PartialEq, Eq)]
50enum ImplCompletionKind {
51 All,
52 Fn,
53 TypeAlias,
54 Const,
55}
56
49pub(crate) fn complete_trait_impl(acc: &mut Completions, ctx: &CompletionContext) { 57pub(crate) fn complete_trait_impl(acc: &mut Completions, ctx: &CompletionContext) {
50 if let Some((trigger, impl_def)) = completion_match(ctx) { 58 if let Some((kind, trigger, impl_def)) = completion_match(ctx) {
51 match trigger.kind() { 59 get_missing_assoc_items(&ctx.sema, &impl_def).into_iter().for_each(|item| match item {
52 SyntaxKind::NAME_REF => get_missing_assoc_items(&ctx.sema, &impl_def) 60 hir::AssocItem::Function(fn_item)
53 .into_iter() 61 if kind == ImplCompletionKind::All || kind == ImplCompletionKind::Fn =>
54 .for_each(|item| match item { 62 {
55 hir::AssocItem::Function(fn_item) => { 63 add_function_impl(&trigger, acc, ctx, fn_item)
56 add_function_impl(&trigger, acc, ctx, fn_item)
57 }
58 hir::AssocItem::TypeAlias(type_item) => {
59 add_type_alias_impl(&trigger, acc, ctx, type_item)
60 }
61 hir::AssocItem::Const(const_item) => {
62 add_const_impl(&trigger, acc, ctx, const_item)
63 }
64 }),
65
66 SyntaxKind::FN => {
67 for missing_fn in get_missing_assoc_items(&ctx.sema, &impl_def)
68 .into_iter()
69 .filter_map(|item| match item {
70 hir::AssocItem::Function(fn_item) => Some(fn_item),
71 _ => None,
72 })
73 {
74 add_function_impl(&trigger, acc, ctx, missing_fn);
75 }
76 } 64 }
77 65 hir::AssocItem::TypeAlias(type_item)
78 SyntaxKind::TYPE_ALIAS => { 66 if kind == ImplCompletionKind::All || kind == ImplCompletionKind::TypeAlias =>
79 for missing_fn in get_missing_assoc_items(&ctx.sema, &impl_def) 67 {
80 .into_iter() 68 add_type_alias_impl(&trigger, acc, ctx, type_item)
81 .filter_map(|item| match item {
82 hir::AssocItem::TypeAlias(type_item) => Some(type_item),
83 _ => None,
84 })
85 {
86 add_type_alias_impl(&trigger, acc, ctx, missing_fn);
87 }
88 } 69 }
89 70 hir::AssocItem::Const(const_item)
90 SyntaxKind::CONST => { 71 if kind == ImplCompletionKind::All || kind == ImplCompletionKind::Const =>
91 for missing_fn in get_missing_assoc_items(&ctx.sema, &impl_def) 72 {
92 .into_iter() 73 add_const_impl(&trigger, acc, ctx, const_item)
93 .filter_map(|item| match item {
94 hir::AssocItem::Const(const_item) => Some(const_item),
95 _ => None,
96 })
97 {
98 add_const_impl(&trigger, acc, ctx, missing_fn);
99 }
100 } 74 }
101
102 _ => {} 75 _ => {}
103 } 76 });
104 } 77 }
105} 78}
106 79
107fn completion_match(ctx: &CompletionContext) -> Option<(SyntaxNode, Impl)> { 80fn completion_match(ctx: &CompletionContext) -> Option<(ImplCompletionKind, SyntaxNode, Impl)> {
108 let (trigger, impl_def_offset) = ctx.token.ancestors().find_map(|p| match p.kind() { 81 let mut token = ctx.token.clone();
109 SyntaxKind::FN | SyntaxKind::TYPE_ALIAS | SyntaxKind::CONST | SyntaxKind::BLOCK_EXPR => { 82 // For keywork without name like `impl .. { fn <|> }`, the current position is inside
110 Some((p, 2)) 83 // the whitespace token, which is outside `FN` syntax node.
111 } 84 // We need to follow the previous token in this case.
112 SyntaxKind::NAME_REF => Some((p, 5)), 85 if token.kind() == SyntaxKind::WHITESPACE {
113 _ => None, 86 token = token.prev_token()?;
114 })?; 87 }
115 let impl_def = (0..impl_def_offset - 1) 88
116 .try_fold(trigger.parent()?, |t, _| t.parent()) 89 let impl_item_offset = match token.kind() {
117 .and_then(ast::Impl::cast)?; 90 // `impl .. { const <|> }`
118 Some((trigger, impl_def)) 91 // ERROR 0
92 // CONST_KW <- *
93 SyntaxKind::CONST_KW => 0,
94 // `impl .. { fn/type <|> }`
95 // FN/TYPE_ALIAS 0
96 // FN_KW <- *
97 SyntaxKind::FN_KW | SyntaxKind::TYPE_KW => 0,
98 // `impl .. { fn/type/const foo<|> }`
99 // FN/TYPE_ALIAS/CONST 1
100 // NAME 0
101 // IDENT <- *
102 SyntaxKind::IDENT if token.parent().kind() == SyntaxKind::NAME => 1,
103 // `impl .. { foo<|> }`
104 // MACRO_CALL 3
105 // PATH 2
106 // PATH_SEGMENT 1
107 // NAME_REF 0
108 // IDENT <- *
109 SyntaxKind::IDENT if token.parent().kind() == SyntaxKind::NAME_REF => 3,
110 _ => return None,
111 };
112
113 let impl_item = token.ancestors().nth(impl_item_offset)?;
114 // Must directly belong to an impl block.
115 // IMPL
116 // ASSOC_ITEM_LIST
117 // <item>
118 let impl_def = ast::Impl::cast(impl_item.parent()?.parent()?)?;
119 let kind = match impl_item.kind() {
120 // `impl ... { const <|> fn/type/const }`
121 _ if token.kind() == SyntaxKind::CONST_KW => ImplCompletionKind::Const,
122 SyntaxKind::CONST | SyntaxKind::ERROR => ImplCompletionKind::Const,
123 SyntaxKind::TYPE_ALIAS => ImplCompletionKind::TypeAlias,
124 SyntaxKind::FN => ImplCompletionKind::Fn,
125 SyntaxKind::MACRO_CALL => ImplCompletionKind::All,
126 _ => return None,
127 };
128 Some((kind, impl_item, impl_def))
119} 129}
120 130
121fn add_function_impl( 131fn add_function_impl(
@@ -261,19 +271,191 @@ ta type TestType = \n\
261 } 271 }
262 272
263 #[test] 273 #[test]
264 fn no_nested_fn_completions() { 274 fn no_completion_inside_fn() {
265 check( 275 check(
266 r" 276 r"
267trait Test { 277trait Test { fn test(); fn test2(); }
268 fn test(); 278struct T;
269 fn test2(); 279
280impl Test for T {
281 fn test() {
282 t<|>
283 }
284}
285",
286 expect![[""]],
287 );
288
289 check(
290 r"
291trait Test { fn test(); fn test2(); }
292struct T;
293
294impl Test for T {
295 fn test() {
296 fn t<|>
297 }
298}
299",
300 expect![[""]],
301 );
302
303 check(
304 r"
305trait Test { fn test(); fn test2(); }
306struct T;
307
308impl Test for T {
309 fn test() {
310 fn <|>
311 }
270} 312}
313",
314 expect![[""]],
315 );
316
317 // https://github.com/rust-analyzer/rust-analyzer/pull/5976#issuecomment-692332191
318 check(
319 r"
320trait Test { fn test(); fn test2(); }
271struct T; 321struct T;
272 322
273impl Test for T { 323impl Test for T {
274 fn test() { 324 fn test() {
325 foo.<|>
326 }
327}
328",
329 expect![[""]],
330 );
331
332 check(
333 r"
334trait Test { fn test(_: i32); fn test2(); }
335struct T;
336
337impl Test for T {
338 fn test(t<|>)
339}
340",
341 expect![[""]],
342 );
343
344 check(
345 r"
346trait Test { fn test(_: fn()); fn test2(); }
347struct T;
348
349impl Test for T {
350 fn test(f: fn <|>)
351}
352",
353 expect![[""]],
354 );
355 }
356
357 #[test]
358 fn no_completion_inside_const() {
359 check(
360 r"
361trait Test { const TEST: fn(); const TEST2: u32; type Test; fn test(); }
362struct T;
363
364impl Test for T {
365 const TEST: fn <|>
366}
367",
368 expect![[""]],
369 );
370
371 check(
372 r"
373trait Test { const TEST: u32; const TEST2: u32; type Test; fn test(); }
374struct T;
375
376impl Test for T {
377 const TEST: T<|>
378}
379",
380 expect![[""]],
381 );
382
383 check(
384 r"
385trait Test { const TEST: u32; const TEST2: u32; type Test; fn test(); }
386struct T;
387
388impl Test for T {
389 const TEST: u32 = f<|>
390}
391",
392 expect![[""]],
393 );
394
395 check(
396 r"
397trait Test { const TEST: u32; const TEST2: u32; type Test; fn test(); }
398struct T;
399
400impl Test for T {
401 const TEST: u32 = {
275 t<|> 402 t<|>
403 };
404}
405",
406 expect![[""]],
407 );
408
409 check(
410 r"
411trait Test { const TEST: u32; const TEST2: u32; type Test; fn test(); }
412struct T;
413
414impl Test for T {
415 const TEST: u32 = {
416 fn <|>
417 };
418}
419",
420 expect![[""]],
421 );
422
423 check(
424 r"
425trait Test { const TEST: u32; const TEST2: u32; type Test; fn test(); }
426struct T;
427
428impl Test for T {
429 const TEST: u32 = {
430 fn t<|>
431 };
432}
433",
434 expect![[""]],
435 );
276 } 436 }
437
438 #[test]
439 fn no_completion_inside_type() {
440 check(
441 r"
442trait Test { type Test; type Test2; fn test(); }
443struct T;
444
445impl Test for T {
446 type Test = T<|>;
447}
448",
449 expect![[""]],
450 );
451
452 check(
453 r"
454trait Test { type Test; type Test2; fn test(); }
455struct T;
456
457impl Test for T {
458 type Test = fn <|>;
277} 459}
278", 460",
279 expect![[""]], 461 expect![[""]],
@@ -485,4 +667,67 @@ impl Test for () {
485", 667",
486 ); 668 );
487 } 669 }
670
671 #[test]
672 fn complete_without_name() {
673 let test = |completion: &str, hint: &str, completed: &str, next_sibling: &str| {
674 println!(
675 "completion='{}', hint='{}', next_sibling='{}'",
676 completion, hint, next_sibling
677 );
678
679 check_edit(
680 completion,
681 &format!(
682 r#"
683trait Test {{
684 type Foo;
685 const CONST: u16;
686 fn bar();
687}}
688struct T;
689
690impl Test for T {{
691 {}
692 {}
693}}
694"#,
695 hint, next_sibling
696 ),
697 &format!(
698 r#"
699trait Test {{
700 type Foo;
701 const CONST: u16;
702 fn bar();
703}}
704struct T;
705
706impl Test for T {{
707 {}
708 {}
709}}
710"#,
711 completed, next_sibling
712 ),
713 )
714 };
715
716 // Enumerate some possible next siblings.
717 for next_sibling in &[
718 "",
719 "fn other_fn() {}", // `const <|> fn` -> `const fn`
720 "type OtherType = i32;",
721 "const OTHER_CONST: i32 = 0;",
722 "async fn other_fn() {}",
723 "unsafe fn other_fn() {}",
724 "default fn other_fn() {}",
725 "default type OtherType = i32;",
726 "default const OTHER_CONST: i32 = 0;",
727 ] {
728 test("bar", "fn <|>", "fn bar() {\n $0\n}", next_sibling);
729 test("Foo", "type <|>", "type Foo = ", next_sibling);
730 test("CONST", "const <|>", "const CONST: u16 = ", next_sibling);
731 }
732 }
488} 733}
diff --git a/crates/ide/src/completion/complete_unqualified_path.rs b/crates/ide/src/completion/complete_unqualified_path.rs
index 1f1b682a7..8eda4b64d 100644
--- a/crates/ide/src/completion/complete_unqualified_path.rs
+++ b/crates/ide/src/completion/complete_unqualified_path.rs
@@ -13,6 +13,7 @@ pub(super) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionC
13 if ctx.record_lit_syntax.is_some() 13 if ctx.record_lit_syntax.is_some()
14 || ctx.record_pat_syntax.is_some() 14 || ctx.record_pat_syntax.is_some()
15 || ctx.attribute_under_caret.is_some() 15 || ctx.attribute_under_caret.is_some()
16 || ctx.mod_declaration_under_caret.is_some()
16 { 17 {
17 return; 18 return;
18 } 19 }
diff --git a/crates/ide/src/completion/completion_context.rs b/crates/ide/src/completion/completion_context.rs
index 3ef1b97cf..671b13328 100644
--- a/crates/ide/src/completion/completion_context.rs
+++ b/crates/ide/src/completion/completion_context.rs
@@ -77,6 +77,7 @@ pub(crate) struct CompletionContext<'a> {
77 pub(super) is_path_type: bool, 77 pub(super) is_path_type: bool,
78 pub(super) has_type_args: bool, 78 pub(super) has_type_args: bool,
79 pub(super) attribute_under_caret: Option<ast::Attr>, 79 pub(super) attribute_under_caret: Option<ast::Attr>,
80 pub(super) mod_declaration_under_caret: Option<ast::Module>,
80 pub(super) unsafe_is_prev: bool, 81 pub(super) unsafe_is_prev: bool,
81 pub(super) if_is_prev: bool, 82 pub(super) if_is_prev: bool,
82 pub(super) block_expr_parent: bool, 83 pub(super) block_expr_parent: bool,
@@ -159,6 +160,7 @@ impl<'a> CompletionContext<'a> {
159 has_type_args: false, 160 has_type_args: false,
160 dot_receiver_is_ambiguous_float_literal: false, 161 dot_receiver_is_ambiguous_float_literal: false,
161 attribute_under_caret: None, 162 attribute_under_caret: None,
163 mod_declaration_under_caret: None,
162 unsafe_is_prev: false, 164 unsafe_is_prev: false,
163 in_loop_body: false, 165 in_loop_body: false,
164 ref_pat_parent: false, 166 ref_pat_parent: false,
@@ -246,7 +248,10 @@ impl<'a> CompletionContext<'a> {
246 self.trait_as_prev_sibling = has_trait_as_prev_sibling(syntax_element.clone()); 248 self.trait_as_prev_sibling = has_trait_as_prev_sibling(syntax_element.clone());
247 self.is_match_arm = is_match_arm(syntax_element.clone()); 249 self.is_match_arm = is_match_arm(syntax_element.clone());
248 self.has_item_list_or_source_file_parent = 250 self.has_item_list_or_source_file_parent =
249 has_item_list_or_source_file_parent(syntax_element); 251 has_item_list_or_source_file_parent(syntax_element.clone());
252 self.mod_declaration_under_caret =
253 find_node_at_offset::<ast::Module>(&file_with_fake_ident, offset)
254 .filter(|module| module.item_list().is_none());
250 } 255 }
251 256
252 fn fill( 257 fn fill(
diff --git a/crates/ide/src/completion/patterns.rs b/crates/ide/src/completion/patterns.rs
index c6ae589db..b17ddf133 100644
--- a/crates/ide/src/completion/patterns.rs
+++ b/crates/ide/src/completion/patterns.rs
@@ -115,6 +115,7 @@ pub(crate) fn if_is_prev(element: SyntaxElement) -> bool {
115 .filter(|it| it.kind() == IF_KW) 115 .filter(|it| it.kind() == IF_KW)
116 .is_some() 116 .is_some()
117} 117}
118
118#[test] 119#[test]
119fn test_if_is_prev() { 120fn test_if_is_prev() {
120 check_pattern_is_applicable(r"if l<|>", if_is_prev); 121 check_pattern_is_applicable(r"if l<|>", if_is_prev);
diff --git a/crates/ide/src/diagnostics.rs b/crates/ide/src/diagnostics.rs
index b2b972b02..dc815a483 100644
--- a/crates/ide/src/diagnostics.rs
+++ b/crates/ide/src/diagnostics.rs
@@ -622,13 +622,65 @@ pub struct Foo { pub a: i32, pub b: i32 }
622 r#" 622 r#"
623use a; 623use a;
624use a::{c, d::e}; 624use a::{c, d::e};
625
626mod a {
627 mod c {}
628 mod d {
629 mod e {}
630 }
631}
625"#, 632"#,
626 ); 633 );
627 check_fix(r#"use {<|>b};"#, r#"use b;"#); 634 check_fix(
628 check_fix(r#"use {b<|>};"#, r#"use b;"#); 635 r"
629 check_fix(r#"use a::{c<|>};"#, r#"use a::c;"#); 636 mod b {}
630 check_fix(r#"use a::{self<|>};"#, r#"use a;"#); 637 use {<|>b};
631 check_fix(r#"use a::{c, d::{e<|>}};"#, r#"use a::{c, d::e};"#); 638 ",
639 r"
640 mod b {}
641 use b;
642 ",
643 );
644 check_fix(
645 r"
646 mod b {}
647 use {b<|>};
648 ",
649 r"
650 mod b {}
651 use b;
652 ",
653 );
654 check_fix(
655 r"
656 mod a { mod c {} }
657 use a::{c<|>};
658 ",
659 r"
660 mod a { mod c {} }
661 use a::c;
662 ",
663 );
664 check_fix(
665 r"
666 mod a {}
667 use a::{self<|>};
668 ",
669 r"
670 mod a {}
671 use a;
672 ",
673 );
674 check_fix(
675 r"
676 mod a { mod c {} mod d { mod e {} } }
677 use a::{c, d::{e<|>}};
678 ",
679 r"
680 mod a { mod c {} mod d { mod e {} } }
681 use a::{c, d::e};
682 ",
683 );
632 } 684 }
633 685
634 #[test] 686 #[test]
diff --git a/crates/ide/src/display.rs b/crates/ide/src/display.rs
index 41b5bdc49..2484dbbf1 100644
--- a/crates/ide/src/display.rs
+++ b/crates/ide/src/display.rs
@@ -41,7 +41,14 @@ pub(crate) fn function_declaration(node: &ast::Fn) -> String {
41 format_to!(buf, "{}", type_params); 41 format_to!(buf, "{}", type_params);
42 } 42 }
43 if let Some(param_list) = node.param_list() { 43 if let Some(param_list) = node.param_list() {
44 format_to!(buf, "{}", param_list); 44 let params: Vec<String> = param_list
45 .self_param()
46 .into_iter()
47 .map(|self_param| self_param.to_string())
48 .chain(param_list.params().map(|param| param.to_string()))
49 .collect();
50 // Useful to inline parameters
51 format_to!(buf, "({})", params.join(", "));
45 } 52 }
46 if let Some(ret_type) = node.ret_type() { 53 if let Some(ret_type) = node.ret_type() {
47 if ret_type.ty().is_some() { 54 if ret_type.ty().is_some() {
diff --git a/crates/ide/src/hover.rs b/crates/ide/src/hover.rs
index efec0184e..37171cbef 100644
--- a/crates/ide/src/hover.rs
+++ b/crates/ide/src/hover.rs
@@ -2647,6 +2647,70 @@ fn foo(ar<|>g: &impl Foo + Bar<S>) {}
2647 } 2647 }
2648 2648
2649 #[test] 2649 #[test]
2650 fn test_hover_async_block_impl_trait_has_goto_type_action() {
2651 check_actions(
2652 r#"
2653struct S;
2654fn foo() {
2655 let fo<|>o = async { S };
2656}
2657
2658#[prelude_import] use future::*;
2659mod future {
2660 #[lang = "future_trait"]
2661 pub trait Future { type Output; }
2662}
2663"#,
2664 expect![[r#"
2665 [
2666 GoToType(
2667 [
2668 HoverGotoTypeData {
2669 mod_path: "test::future::Future",
2670 nav: NavigationTarget {
2671 file_id: FileId(
2672 1,
2673 ),
2674 full_range: 101..163,
2675 focus_range: Some(
2676 140..146,
2677 ),
2678 name: "Future",
2679 kind: TRAIT,
2680 container_name: None,
2681 description: Some(
2682 "pub trait Future",
2683 ),
2684 docs: None,
2685 },
2686 },
2687 HoverGotoTypeData {
2688 mod_path: "test::S",
2689 nav: NavigationTarget {
2690 file_id: FileId(
2691 1,
2692 ),
2693 full_range: 0..9,
2694 focus_range: Some(
2695 7..8,
2696 ),
2697 name: "S",
2698 kind: STRUCT,
2699 container_name: None,
2700 description: Some(
2701 "struct S",
2702 ),
2703 docs: None,
2704 },
2705 },
2706 ],
2707 ),
2708 ]
2709 "#]],
2710 );
2711 }
2712
2713 #[test]
2650 fn test_hover_arg_generic_impl_trait_has_goto_type_action() { 2714 fn test_hover_arg_generic_impl_trait_has_goto_type_action() {
2651 check_actions( 2715 check_actions(
2652 r#" 2716 r#"
diff --git a/crates/ide/src/lib.rs b/crates/ide/src/lib.rs
index 570790384..3b97e087f 100644
--- a/crates/ide/src/lib.rs
+++ b/crates/ide/src/lib.rs
@@ -81,7 +81,9 @@ pub use crate::{
81 }, 81 },
82}; 82};
83 83
84pub use assists::{Assist, AssistConfig, AssistId, AssistKind, ResolvedAssist}; 84pub use assists::{
85 utils::MergeBehaviour, Assist, AssistConfig, AssistId, AssistKind, ResolvedAssist,
86};
85pub use base_db::{ 87pub use base_db::{
86 Canceled, CrateGraph, CrateId, Edition, FileId, FilePosition, FileRange, SourceRoot, 88 Canceled, CrateGraph, CrateId, Edition, FileId, FilePosition, FileRange, SourceRoot,
87 SourceRootId, 89 SourceRootId,
diff --git a/crates/ide/src/syntax_highlighting.rs b/crates/ide/src/syntax_highlighting.rs
index 25d6f7abd..d9fc25d88 100644
--- a/crates/ide/src/syntax_highlighting.rs
+++ b/crates/ide/src/syntax_highlighting.rs
@@ -4,7 +4,7 @@ mod injection;
4#[cfg(test)] 4#[cfg(test)]
5mod tests; 5mod tests;
6 6
7use hir::{Name, Semantics, VariantDef}; 7use hir::{Local, Name, Semantics, VariantDef};
8use ide_db::{ 8use ide_db::{
9 defs::{classify_name, classify_name_ref, Definition, NameClass, NameRefClass}, 9 defs::{classify_name, classify_name_ref, Definition, NameClass, NameRefClass},
10 RootDatabase, 10 RootDatabase,
@@ -13,8 +13,8 @@ use rustc_hash::FxHashMap;
13use syntax::{ 13use syntax::{
14 ast::{self, HasFormatSpecifier}, 14 ast::{self, HasFormatSpecifier},
15 AstNode, AstToken, Direction, NodeOrToken, SyntaxElement, 15 AstNode, AstToken, Direction, NodeOrToken, SyntaxElement,
16 SyntaxKind::*, 16 SyntaxKind::{self, *},
17 TextRange, WalkEvent, T, 17 SyntaxNode, SyntaxToken, TextRange, WalkEvent, T,
18}; 18};
19 19
20use crate::FileId; 20use crate::FileId;
@@ -454,6 +454,32 @@ fn macro_call_range(macro_call: &ast::MacroCall) -> Option<TextRange> {
454 Some(TextRange::new(range_start, range_end)) 454 Some(TextRange::new(range_start, range_end))
455} 455}
456 456
457/// Returns true if the parent nodes of `node` all match the `SyntaxKind`s in `kinds` exactly.
458fn parents_match(mut node: NodeOrToken<SyntaxNode, SyntaxToken>, mut kinds: &[SyntaxKind]) -> bool {
459 while let (Some(parent), [kind, rest @ ..]) = (&node.parent(), kinds) {
460 if parent.kind() != *kind {
461 return false;
462 }
463
464 // FIXME: Would be nice to get parent out of the match, but binding by-move and by-value
465 // in the same pattern is unstable: rust-lang/rust#68354.
466 node = node.parent().unwrap().into();
467 kinds = rest;
468 }
469
470 // Only true if we matched all expected kinds
471 kinds.len() == 0
472}
473
474fn is_consumed_lvalue(
475 node: NodeOrToken<SyntaxNode, SyntaxToken>,
476 local: &Local,
477 db: &RootDatabase,
478) -> bool {
479 // When lvalues are passed as arguments and they're not Copy, then mark them as Consuming.
480 parents_match(node, &[PATH_SEGMENT, PATH, PATH_EXPR, ARG_LIST]) && !local.ty(db).is_copy(db)
481}
482
457fn highlight_element( 483fn highlight_element(
458 sema: &Semantics<RootDatabase>, 484 sema: &Semantics<RootDatabase>,
459 bindings_shadow_count: &mut FxHashMap<Name, u32>, 485 bindings_shadow_count: &mut FxHashMap<Name, u32>,
@@ -522,6 +548,12 @@ fn highlight_element(
522 548
523 let mut h = highlight_def(db, def); 549 let mut h = highlight_def(db, def);
524 550
551 if let Definition::Local(local) = &def {
552 if is_consumed_lvalue(name_ref.syntax().clone().into(), local, db) {
553 h |= HighlightModifier::Consuming;
554 }
555 }
556
525 if let Some(parent) = name_ref.syntax().parent() { 557 if let Some(parent) = name_ref.syntax().parent() {
526 if matches!(parent.kind(), FIELD_EXPR | RECORD_PAT_FIELD) { 558 if matches!(parent.kind(), FIELD_EXPR | RECORD_PAT_FIELD) {
527 if let Definition::Field(field) = def { 559 if let Definition::Field(field) = def {
@@ -645,21 +677,30 @@ fn highlight_element(
645 .and_then(ast::SelfParam::cast) 677 .and_then(ast::SelfParam::cast)
646 .and_then(|p| p.mut_token()) 678 .and_then(|p| p.mut_token())
647 .is_some(); 679 .is_some();
648 // closure to enforce lazyness 680 let self_path = &element
649 let self_path = || { 681 .parent()
650 sema.resolve_path(&element.parent()?.parent().and_then(ast::Path::cast)?) 682 .as_ref()
651 }; 683 .and_then(SyntaxNode::parent)
684 .and_then(ast::Path::cast)
685 .and_then(|p| sema.resolve_path(&p));
686 let mut h = HighlightTag::SelfKeyword.into();
652 if self_param_is_mut 687 if self_param_is_mut
653 || matches!(self_path(), 688 || matches!(self_path,
654 Some(hir::PathResolution::Local(local)) 689 Some(hir::PathResolution::Local(local))
655 if local.is_self(db) 690 if local.is_self(db)
656 && (local.is_mut(db) || local.ty(db).is_mutable_reference()) 691 && (local.is_mut(db) || local.ty(db).is_mutable_reference())
657 ) 692 )
658 { 693 {
659 HighlightTag::SelfKeyword | HighlightModifier::Mutable 694 h |= HighlightModifier::Mutable
660 } else {
661 HighlightTag::SelfKeyword.into()
662 } 695 }
696
697 if let Some(hir::PathResolution::Local(local)) = self_path {
698 if is_consumed_lvalue(element, &local, db) {
699 h |= HighlightModifier::Consuming;
700 }
701 }
702
703 h
663 } 704 }
664 T![ref] => element 705 T![ref] => element
665 .parent() 706 .parent()
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlighting.html b/crates/ide/src/syntax_highlighting/test_data/highlighting.html
index d0df2e0ec..cde42024c 100644
--- a/crates/ide/src/syntax_highlighting/test_data/highlighting.html
+++ b/crates/ide/src/syntax_highlighting/test_data/highlighting.html
@@ -61,8 +61,8 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
61<span class="punctuation">}</span> 61<span class="punctuation">}</span>
62 62
63<span class="keyword">impl</span> <span class="struct">Foo</span> <span class="punctuation">{</span> 63<span class="keyword">impl</span> <span class="struct">Foo</span> <span class="punctuation">{</span>
64 <span class="keyword">fn</span> <span class="function declaration">baz</span><span class="punctuation">(</span><span class="keyword">mut</span> <span class="self_keyword mutable">self</span><span class="punctuation">)</span> <span class="operator">-&gt;</span> <span class="builtin_type">i32</span> <span class="punctuation">{</span> 64 <span class="keyword">fn</span> <span class="function declaration">baz</span><span class="punctuation">(</span><span class="keyword">mut</span> <span class="self_keyword mutable">self</span><span class="punctuation">,</span> <span class="value_param declaration">f</span><span class="punctuation">:</span> <span class="struct">Foo</span><span class="punctuation">)</span> <span class="operator">-&gt;</span> <span class="builtin_type">i32</span> <span class="punctuation">{</span>
65 <span class="self_keyword">self</span><span class="punctuation">.</span><span class="field">x</span> 65 <span class="value_param">f</span><span class="punctuation">.</span><span class="function consuming">baz</span><span class="punctuation">(</span><span class="self_keyword consuming">self</span><span class="punctuation">)</span>
66 <span class="punctuation">}</span> 66 <span class="punctuation">}</span>
67 67
68 <span class="keyword">fn</span> <span class="function declaration">qux</span><span class="punctuation">(</span><span class="operator">&</span><span class="keyword">mut</span> <span class="self_keyword mutable">self</span><span class="punctuation">)</span> <span class="punctuation">{</span> 68 <span class="keyword">fn</span> <span class="function declaration">qux</span><span class="punctuation">(</span><span class="operator">&</span><span class="keyword">mut</span> <span class="self_keyword mutable">self</span><span class="punctuation">)</span> <span class="punctuation">{</span>
@@ -80,8 +80,8 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
80<span class="punctuation">}</span> 80<span class="punctuation">}</span>
81 81
82<span class="keyword">impl</span> <span class="struct">FooCopy</span> <span class="punctuation">{</span> 82<span class="keyword">impl</span> <span class="struct">FooCopy</span> <span class="punctuation">{</span>
83 <span class="keyword">fn</span> <span class="function declaration">baz</span><span class="punctuation">(</span><span class="self_keyword">self</span><span class="punctuation">)</span> <span class="operator">-&gt;</span> <span class="builtin_type">u32</span> <span class="punctuation">{</span> 83 <span class="keyword">fn</span> <span class="function declaration">baz</span><span class="punctuation">(</span><span class="self_keyword">self</span><span class="punctuation">,</span> <span class="value_param declaration">f</span><span class="punctuation">:</span> <span class="struct">FooCopy</span><span class="punctuation">)</span> <span class="operator">-&gt;</span> <span class="builtin_type">u32</span> <span class="punctuation">{</span>
84 <span class="self_keyword">self</span><span class="punctuation">.</span><span class="field">x</span> 84 <span class="value_param">f</span><span class="punctuation">.</span><span class="function">baz</span><span class="punctuation">(</span><span class="self_keyword">self</span><span class="punctuation">)</span>
85 <span class="punctuation">}</span> 85 <span class="punctuation">}</span>
86 86
87 <span class="keyword">fn</span> <span class="function declaration">qux</span><span class="punctuation">(</span><span class="operator">&</span><span class="keyword">mut</span> <span class="self_keyword mutable">self</span><span class="punctuation">)</span> <span class="punctuation">{</span> 87 <span class="keyword">fn</span> <span class="function declaration">qux</span><span class="punctuation">(</span><span class="operator">&</span><span class="keyword">mut</span> <span class="self_keyword mutable">self</span><span class="punctuation">)</span> <span class="punctuation">{</span>
@@ -144,14 +144,15 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
144 <span class="variable">y</span><span class="punctuation">;</span> 144 <span class="variable">y</span><span class="punctuation">;</span>
145 145
146 <span class="keyword">let</span> <span class="keyword">mut</span> <span class="variable declaration mutable">foo</span> <span class="operator">=</span> <span class="struct">Foo</span> <span class="punctuation">{</span> <span class="field">x</span><span class="punctuation">,</span> <span class="field">y</span><span class="punctuation">:</span> <span class="variable mutable">x</span> <span class="punctuation">}</span><span class="punctuation">;</span> 146 <span class="keyword">let</span> <span class="keyword">mut</span> <span class="variable declaration mutable">foo</span> <span class="operator">=</span> <span class="struct">Foo</span> <span class="punctuation">{</span> <span class="field">x</span><span class="punctuation">,</span> <span class="field">y</span><span class="punctuation">:</span> <span class="variable mutable">x</span> <span class="punctuation">}</span><span class="punctuation">;</span>
147 <span class="keyword">let</span> <span class="variable declaration">foo2</span> <span class="operator">=</span> <span class="variable mutable">foo</span><span class="punctuation">.</span><span class="unresolved_reference">clone</span><span class="punctuation">(</span><span class="punctuation">)</span><span class="punctuation">;</span>
147 <span class="variable mutable">foo</span><span class="punctuation">.</span><span class="function">quop</span><span class="punctuation">(</span><span class="punctuation">)</span><span class="punctuation">;</span> 148 <span class="variable mutable">foo</span><span class="punctuation">.</span><span class="function">quop</span><span class="punctuation">(</span><span class="punctuation">)</span><span class="punctuation">;</span>
148 <span class="variable mutable">foo</span><span class="punctuation">.</span><span class="function mutable">qux</span><span class="punctuation">(</span><span class="punctuation">)</span><span class="punctuation">;</span> 149 <span class="variable mutable">foo</span><span class="punctuation">.</span><span class="function mutable">qux</span><span class="punctuation">(</span><span class="punctuation">)</span><span class="punctuation">;</span>
149 <span class="variable mutable">foo</span><span class="punctuation">.</span><span class="function consuming">baz</span><span class="punctuation">(</span><span class="punctuation">)</span><span class="punctuation">;</span> 150 <span class="variable mutable">foo</span><span class="punctuation">.</span><span class="function consuming">baz</span><span class="punctuation">(</span><span class="variable consuming">foo2</span><span class="punctuation">)</span><span class="punctuation">;</span>
150 151
151 <span class="keyword">let</span> <span class="keyword">mut</span> <span class="variable declaration mutable">copy</span> <span class="operator">=</span> <span class="struct">FooCopy</span> <span class="punctuation">{</span> <span class="field">x</span> <span class="punctuation">}</span><span class="punctuation">;</span> 152 <span class="keyword">let</span> <span class="keyword">mut</span> <span class="variable declaration mutable">copy</span> <span class="operator">=</span> <span class="struct">FooCopy</span> <span class="punctuation">{</span> <span class="field">x</span> <span class="punctuation">}</span><span class="punctuation">;</span>
152 <span class="variable mutable">copy</span><span class="punctuation">.</span><span class="function">quop</span><span class="punctuation">(</span><span class="punctuation">)</span><span class="punctuation">;</span> 153 <span class="variable mutable">copy</span><span class="punctuation">.</span><span class="function">quop</span><span class="punctuation">(</span><span class="punctuation">)</span><span class="punctuation">;</span>
153 <span class="variable mutable">copy</span><span class="punctuation">.</span><span class="function mutable">qux</span><span class="punctuation">(</span><span class="punctuation">)</span><span class="punctuation">;</span> 154 <span class="variable mutable">copy</span><span class="punctuation">.</span><span class="function mutable">qux</span><span class="punctuation">(</span><span class="punctuation">)</span><span class="punctuation">;</span>
154 <span class="variable mutable">copy</span><span class="punctuation">.</span><span class="function">baz</span><span class="punctuation">(</span><span class="punctuation">)</span><span class="punctuation">;</span> 155 <span class="variable mutable">copy</span><span class="punctuation">.</span><span class="function">baz</span><span class="punctuation">(</span><span class="variable mutable">copy</span><span class="punctuation">)</span><span class="punctuation">;</span>
155<span class="punctuation">}</span> 156<span class="punctuation">}</span>
156 157
157<span class="keyword">enum</span> <span class="enum declaration">Option</span><span class="punctuation">&lt;</span><span class="type_param declaration">T</span><span class="punctuation">&gt;</span> <span class="punctuation">{</span> 158<span class="keyword">enum</span> <span class="enum declaration">Option</span><span class="punctuation">&lt;</span><span class="type_param declaration">T</span><span class="punctuation">&gt;</span> <span class="punctuation">{</span>
diff --git a/crates/ide/src/syntax_highlighting/tests.rs b/crates/ide/src/syntax_highlighting/tests.rs
index 6f72a29bd..57d4e1252 100644
--- a/crates/ide/src/syntax_highlighting/tests.rs
+++ b/crates/ide/src/syntax_highlighting/tests.rs
@@ -35,8 +35,8 @@ impl Bar for Foo {
35} 35}
36 36
37impl Foo { 37impl Foo {
38 fn baz(mut self) -> i32 { 38 fn baz(mut self, f: Foo) -> i32 {
39 self.x 39 f.baz(self)
40 } 40 }
41 41
42 fn qux(&mut self) { 42 fn qux(&mut self) {
@@ -54,8 +54,8 @@ struct FooCopy {
54} 54}
55 55
56impl FooCopy { 56impl FooCopy {
57 fn baz(self) -> u32 { 57 fn baz(self, f: FooCopy) -> u32 {
58 self.x 58 f.baz(self)
59 } 59 }
60 60
61 fn qux(&mut self) { 61 fn qux(&mut self) {
@@ -118,14 +118,15 @@ fn main() {
118 y; 118 y;
119 119
120 let mut foo = Foo { x, y: x }; 120 let mut foo = Foo { x, y: x };
121 let foo2 = foo.clone();
121 foo.quop(); 122 foo.quop();
122 foo.qux(); 123 foo.qux();
123 foo.baz(); 124 foo.baz(foo2);
124 125
125 let mut copy = FooCopy { x }; 126 let mut copy = FooCopy { x };
126 copy.quop(); 127 copy.quop();
127 copy.qux(); 128 copy.qux();
128 copy.baz(); 129 copy.baz(copy);
129} 130}
130 131
131enum Option<T> { 132enum Option<T> {
diff --git a/crates/ide/src/typing.rs b/crates/ide/src/typing.rs
index 899ce5f26..94b91f049 100644
--- a/crates/ide/src/typing.rs
+++ b/crates/ide/src/typing.rs
@@ -39,6 +39,14 @@ pub(crate) const TRIGGER_CHARS: &str = ".=>";
39// 39//
40// - typing `let =` tries to smartly add `;` if `=` is followed by an existing expression 40// - typing `let =` tries to smartly add `;` if `=` is followed by an existing expression
41// - typing `.` in a chain method call auto-indents 41// - typing `.` in a chain method call auto-indents
42//
43// VS Code::
44//
45// Add the following to `settings.json`:
46// [source,json]
47// ----
48// "editor.formatOnType": true,
49// ----
42pub(crate) fn on_char_typed( 50pub(crate) fn on_char_typed(
43 db: &RootDatabase, 51 db: &RootDatabase,
44 position: FilePosition, 52 position: FilePosition,