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_mod.rs324
-rw-r--r--crates/ide/src/completion/complete_qualified_path.rs2
-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/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
10 files changed, 405 insertions, 25 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_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..79de50792 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
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 47355d5dc..161f59c1e 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,
@@ -152,6 +153,7 @@ impl<'a> CompletionContext<'a> {
152 has_type_args: false, 153 has_type_args: false,
153 dot_receiver_is_ambiguous_float_literal: false, 154 dot_receiver_is_ambiguous_float_literal: false,
154 attribute_under_caret: None, 155 attribute_under_caret: None,
156 mod_declaration_under_caret: None,
155 unsafe_is_prev: false, 157 unsafe_is_prev: false,
156 in_loop_body: false, 158 in_loop_body: false,
157 ref_pat_parent: false, 159 ref_pat_parent: false,
@@ -238,7 +240,10 @@ impl<'a> CompletionContext<'a> {
238 self.trait_as_prev_sibling = has_trait_as_prev_sibling(syntax_element.clone()); 240 self.trait_as_prev_sibling = has_trait_as_prev_sibling(syntax_element.clone());
239 self.is_match_arm = is_match_arm(syntax_element.clone()); 241 self.is_match_arm = is_match_arm(syntax_element.clone());
240 self.has_item_list_or_source_file_parent = 242 self.has_item_list_or_source_file_parent =
241 has_item_list_or_source_file_parent(syntax_element); 243 has_item_list_or_source_file_parent(syntax_element.clone());
244 self.mod_declaration_under_caret =
245 find_node_at_offset::<ast::Module>(&file_with_fake_ident, offset)
246 .filter(|module| module.item_list().is_none());
242 } 247 }
243 248
244 fn fill( 249 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/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> {