aboutsummaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
Diffstat (limited to 'crates')
-rw-r--r--crates/assists/src/handlers/replace_qualified_name_with_use.rs2
-rw-r--r--crates/assists/src/handlers/toggle_ignore.rs (renamed from crates/assists/src/handlers/ignore_test.rs)23
-rw-r--r--crates/assists/src/lib.rs4
-rw-r--r--crates/assists/src/tests/generated.rs40
-rw-r--r--crates/completion/src/completions/postfix.rs86
-rw-r--r--crates/completion/src/completions/qualified_path.rs40
-rw-r--r--crates/completion/src/completions/trait_impl.rs2
-rw-r--r--crates/completion/src/lib.rs2
-rw-r--r--crates/completion/src/render/builder_ext.rs5
-rw-r--r--crates/completion/src/render/function.rs69
-rw-r--r--crates/hir/src/code_model.rs12
-rw-r--r--crates/hir_def/src/body.rs73
-rw-r--r--crates/hir_def/src/body/diagnostics.rs10
-rw-r--r--crates/hir_def/src/body/lower.rs31
-rw-r--r--crates/hir_def/src/data.rs2
-rw-r--r--crates/hir_def/src/nameres/tests/mod_resolution.rs21
-rw-r--r--crates/hir_def/src/path/lower/lower_use.rs2
-rw-r--r--crates/hir_expand/src/builtin_macro.rs129
-rw-r--r--crates/hir_expand/src/db.rs2
-rw-r--r--crates/hir_expand/src/eager.rs2
-rw-r--r--crates/ide/src/references.rs103
-rw-r--r--crates/ide/src/references/rename.rs166
-rw-r--r--crates/ide/src/syntax_highlighting.rs19
-rw-r--r--crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html2
-rw-r--r--crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html2
-rw-r--r--crates/ide/src/syntax_highlighting/test_data/highlighting.html10
-rw-r--r--crates/ide_db/src/helpers/insert_use.rs11
-rw-r--r--crates/ide_db/src/search.rs1
-rw-r--r--crates/rust-analyzer/Cargo.toml2
-rw-r--r--crates/rust-analyzer/src/semantic_tokens.rs2
-rw-r--r--crates/syntax/Cargo.toml2
-rw-r--r--crates/vfs/src/file_set.rs51
-rw-r--r--crates/vfs/src/file_set/tests.rs42
-rw-r--r--crates/vfs/src/vfs_path.rs33
-rw-r--r--crates/vfs/src/vfs_path/tests.rs30
35 files changed, 762 insertions, 271 deletions
diff --git a/crates/assists/src/handlers/replace_qualified_name_with_use.rs b/crates/assists/src/handlers/replace_qualified_name_with_use.rs
index 8bdf9eea5..8193e45a8 100644
--- a/crates/assists/src/handlers/replace_qualified_name_with_use.rs
+++ b/crates/assists/src/handlers/replace_qualified_name_with_use.rs
@@ -407,7 +407,7 @@ impl std::fmt::Display<|> for Foo {
407} 407}
408", 408",
409 r" 409 r"
410use std::fmt::{nested::Debug, Display}; 410use std::fmt::{Display, nested::Debug};
411 411
412impl Display for Foo { 412impl Display for Foo {
413} 413}
diff --git a/crates/assists/src/handlers/ignore_test.rs b/crates/assists/src/handlers/toggle_ignore.rs
index 5096a0005..14b420421 100644
--- a/crates/assists/src/handlers/ignore_test.rs
+++ b/crates/assists/src/handlers/toggle_ignore.rs
@@ -5,7 +5,7 @@ use syntax::{
5 5
6use crate::{utils::test_related_attribute, AssistContext, AssistId, AssistKind, Assists}; 6use crate::{utils::test_related_attribute, AssistContext, AssistId, AssistKind, Assists};
7 7
8// Assist: ignore_test 8// Assist: toggle_ignore
9// 9//
10// Adds `#[ignore]` attribute to the test. 10// Adds `#[ignore]` attribute to the test.
11// 11//
@@ -23,20 +23,20 @@ use crate::{utils::test_related_attribute, AssistContext, AssistId, AssistKind,
23// assert_eq!(2 + 2, 5); 23// assert_eq!(2 + 2, 5);
24// } 24// }
25// ``` 25// ```
26pub(crate) fn ignore_test(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { 26pub(crate) fn toggle_ignore(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
27 let attr: ast::Attr = ctx.find_node_at_offset()?; 27 let attr: ast::Attr = ctx.find_node_at_offset()?;
28 let func = attr.syntax().parent().and_then(ast::Fn::cast)?; 28 let func = attr.syntax().parent().and_then(ast::Fn::cast)?;
29 let attr = test_related_attribute(&func)?; 29 let attr = test_related_attribute(&func)?;
30 30
31 match has_ignore_attribute(&func) { 31 match has_ignore_attribute(&func) {
32 None => acc.add( 32 None => acc.add(
33 AssistId("ignore_test", AssistKind::None), 33 AssistId("toggle_ignore", AssistKind::None),
34 "Ignore this test", 34 "Ignore this test",
35 attr.syntax().text_range(), 35 attr.syntax().text_range(),
36 |builder| builder.insert(attr.syntax().text_range().end(), &format!("\n#[ignore]")), 36 |builder| builder.insert(attr.syntax().text_range().end(), &format!("\n#[ignore]")),
37 ), 37 ),
38 Some(ignore_attr) => acc.add( 38 Some(ignore_attr) => acc.add(
39 AssistId("unignore_test", AssistKind::None), 39 AssistId("toggle_ignore", AssistKind::None),
40 "Re-enable this test", 40 "Re-enable this test",
41 ignore_attr.syntax().text_range(), 41 ignore_attr.syntax().text_range(),
42 |builder| { 42 |builder| {
@@ -55,24 +55,19 @@ pub(crate) fn ignore_test(acc: &mut Assists, ctx: &AssistContext) -> Option<()>
55} 55}
56 56
57fn has_ignore_attribute(fn_def: &ast::Fn) -> Option<ast::Attr> { 57fn has_ignore_attribute(fn_def: &ast::Fn) -> Option<ast::Attr> {
58 fn_def.attrs().find_map(|attr| { 58 fn_def.attrs().find(|attr| attr.path().map(|it| it.syntax().text() == "ignore") == Some(true))
59 if attr.path()?.syntax().text() == "ignore" {
60 Some(attr)
61 } else {
62 None
63 }
64 })
65} 59}
66 60
67#[cfg(test)] 61#[cfg(test)]
68mod tests { 62mod tests {
69 use super::ignore_test;
70 use crate::tests::check_assist; 63 use crate::tests::check_assist;
71 64
65 use super::*;
66
72 #[test] 67 #[test]
73 fn test_base_case() { 68 fn test_base_case() {
74 check_assist( 69 check_assist(
75 ignore_test, 70 toggle_ignore,
76 r#" 71 r#"
77 #[test<|>] 72 #[test<|>]
78 fn test() {} 73 fn test() {}
@@ -88,7 +83,7 @@ mod tests {
88 #[test] 83 #[test]
89 fn test_unignore() { 84 fn test_unignore() {
90 check_assist( 85 check_assist(
91 ignore_test, 86 toggle_ignore,
92 r#" 87 r#"
93 #[test<|>] 88 #[test<|>]
94 #[ignore] 89 #[ignore]
diff --git a/crates/assists/src/lib.rs b/crates/assists/src/lib.rs
index 17e9312db..dfe6c2729 100644
--- a/crates/assists/src/lib.rs
+++ b/crates/assists/src/lib.rs
@@ -141,7 +141,6 @@ mod handlers {
141 mod generate_function; 141 mod generate_function;
142 mod generate_impl; 142 mod generate_impl;
143 mod generate_new; 143 mod generate_new;
144 mod ignore_test;
145 mod infer_function_return_type; 144 mod infer_function_return_type;
146 mod inline_local_variable; 145 mod inline_local_variable;
147 mod introduce_named_lifetime; 146 mod introduce_named_lifetime;
@@ -164,6 +163,7 @@ mod handlers {
164 mod replace_string_with_char; 163 mod replace_string_with_char;
165 mod replace_unwrap_with_match; 164 mod replace_unwrap_with_match;
166 mod split_import; 165 mod split_import;
166 mod toggle_ignore;
167 mod unwrap_block; 167 mod unwrap_block;
168 mod wrap_return_type_in_result; 168 mod wrap_return_type_in_result;
169 169
@@ -190,7 +190,6 @@ mod handlers {
190 generate_function::generate_function, 190 generate_function::generate_function,
191 generate_impl::generate_impl, 191 generate_impl::generate_impl,
192 generate_new::generate_new, 192 generate_new::generate_new,
193 ignore_test::ignore_test,
194 infer_function_return_type::infer_function_return_type, 193 infer_function_return_type::infer_function_return_type,
195 inline_local_variable::inline_local_variable, 194 inline_local_variable::inline_local_variable,
196 introduce_named_lifetime::introduce_named_lifetime, 195 introduce_named_lifetime::introduce_named_lifetime,
@@ -215,6 +214,7 @@ mod handlers {
215 replace_qualified_name_with_use::replace_qualified_name_with_use, 214 replace_qualified_name_with_use::replace_qualified_name_with_use,
216 replace_unwrap_with_match::replace_unwrap_with_match, 215 replace_unwrap_with_match::replace_unwrap_with_match,
217 split_import::split_import, 216 split_import::split_import,
217 toggle_ignore::toggle_ignore,
218 unwrap_block::unwrap_block, 218 unwrap_block::unwrap_block,
219 wrap_return_type_in_result::wrap_return_type_in_result, 219 wrap_return_type_in_result::wrap_return_type_in_result,
220 // These are manually sorted for better priorities 220 // These are manually sorted for better priorities
diff --git a/crates/assists/src/tests/generated.rs b/crates/assists/src/tests/generated.rs
index 5a9d1a01b..8d50c8791 100644
--- a/crates/assists/src/tests/generated.rs
+++ b/crates/assists/src/tests/generated.rs
@@ -474,26 +474,6 @@ impl<T: Clone> Ctx<T> {
474} 474}
475 475
476#[test] 476#[test]
477fn doctest_ignore_test() {
478 check_doc_test(
479 "ignore_test",
480 r#####"
481<|>#[test]
482fn arithmetics {
483 assert_eq!(2 + 2, 5);
484}
485"#####,
486 r#####"
487#[test]
488#[ignore]
489fn arithmetics {
490 assert_eq!(2 + 2, 5);
491}
492"#####,
493 )
494}
495
496#[test]
497fn doctest_infer_function_return_type() { 477fn doctest_infer_function_return_type() {
498 check_doc_test( 478 check_doc_test(
499 "infer_function_return_type", 479 "infer_function_return_type",
@@ -979,6 +959,26 @@ use std::{collections::HashMap};
979} 959}
980 960
981#[test] 961#[test]
962fn doctest_toggle_ignore() {
963 check_doc_test(
964 "toggle_ignore",
965 r#####"
966<|>#[test]
967fn arithmetics {
968 assert_eq!(2 + 2, 5);
969}
970"#####,
971 r#####"
972#[test]
973#[ignore]
974fn arithmetics {
975 assert_eq!(2 + 2, 5);
976}
977"#####,
978 )
979}
980
981#[test]
982fn doctest_unwrap_block() { 982fn doctest_unwrap_block() {
983 check_doc_test( 983 check_doc_test(
984 "unwrap_block", 984 "unwrap_block",
diff --git a/crates/completion/src/completions/postfix.rs b/crates/completion/src/completions/postfix.rs
index 7fbda7a6b..c8ba63cd3 100644
--- a/crates/completion/src/completions/postfix.rs
+++ b/crates/completion/src/completions/postfix.rs
@@ -5,6 +5,7 @@ mod format_like;
5use ide_db::ty_filter::TryEnum; 5use ide_db::ty_filter::TryEnum;
6use syntax::{ 6use syntax::{
7 ast::{self, AstNode, AstToken}, 7 ast::{self, AstNode, AstToken},
8 SyntaxKind::{BLOCK_EXPR, EXPR_STMT},
8 TextRange, TextSize, 9 TextRange, TextSize,
9}; 10};
10use text_edit::TextEdit; 11use text_edit::TextEdit;
@@ -220,6 +221,29 @@ pub(crate) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) {
220 ) 221 )
221 .add_to(acc); 222 .add_to(acc);
222 223
224 if let Some(parent) = dot_receiver.syntax().parent().and_then(|p| p.parent()) {
225 if matches!(parent.kind(), BLOCK_EXPR | EXPR_STMT) {
226 postfix_snippet(
227 ctx,
228 cap,
229 &dot_receiver,
230 "let",
231 "let",
232 &format!("let $0 = {};", receiver_text),
233 )
234 .add_to(acc);
235 postfix_snippet(
236 ctx,
237 cap,
238 &dot_receiver,
239 "letm",
240 "let mut",
241 &format!("let mut $0 = {};", receiver_text),
242 )
243 .add_to(acc);
244 }
245 }
246
223 if let ast::Expr::Literal(literal) = dot_receiver.clone() { 247 if let ast::Expr::Literal(literal) = dot_receiver.clone() {
224 if let Some(literal_text) = ast::String::cast(literal.token()) { 248 if let Some(literal_text) = ast::String::cast(literal.token()) {
225 add_format_like_completions(acc, ctx, &dot_receiver, cap, &literal_text); 249 add_format_like_completions(acc, ctx, &dot_receiver, cap, &literal_text);
@@ -296,6 +320,38 @@ fn main() {
296 sn dbg dbg!(expr) 320 sn dbg dbg!(expr)
297 sn dbgr dbg!(&expr) 321 sn dbgr dbg!(&expr)
298 sn if if expr {} 322 sn if if expr {}
323 sn let let
324 sn letm let mut
325 sn match match expr {}
326 sn not !expr
327 sn ok Ok(expr)
328 sn ref &expr
329 sn refm &mut expr
330 sn some Some(expr)
331 sn while while expr {}
332 "#]],
333 );
334 }
335
336 #[test]
337 fn postfix_completion_works_for_function_calln() {
338 check(
339 r#"
340fn foo(elt: bool) -> bool {
341 !elt
342}
343
344fn main() {
345 let bar = true;
346 foo(bar.<|>)
347}
348"#,
349 expect![[r#"
350 sn box Box::new(expr)
351 sn call function(expr)
352 sn dbg dbg!(expr)
353 sn dbgr dbg!(&expr)
354 sn if if expr {}
299 sn match match expr {} 355 sn match match expr {}
300 sn not !expr 356 sn not !expr
301 sn ok Ok(expr) 357 sn ok Ok(expr)
@@ -321,6 +377,8 @@ fn main() {
321 sn call function(expr) 377 sn call function(expr)
322 sn dbg dbg!(expr) 378 sn dbg dbg!(expr)
323 sn dbgr dbg!(&expr) 379 sn dbgr dbg!(&expr)
380 sn let let
381 sn letm let mut
324 sn match match expr {} 382 sn match match expr {}
325 sn ok Ok(expr) 383 sn ok Ok(expr)
326 sn ref &expr 384 sn ref &expr
@@ -331,6 +389,34 @@ fn main() {
331 } 389 }
332 390
333 #[test] 391 #[test]
392 fn let_middle_block() {
393 check(
394 r#"
395fn main() {
396 baz.l<|>
397 res
398}
399"#,
400 expect![[r#"
401 sn box Box::new(expr)
402 sn call function(expr)
403 sn dbg dbg!(expr)
404 sn dbgr dbg!(&expr)
405 sn if if expr {}
406 sn let let
407 sn letm let mut
408 sn match match expr {}
409 sn not !expr
410 sn ok Ok(expr)
411 sn ref &expr
412 sn refm &mut expr
413 sn some Some(expr)
414 sn while while expr {}
415 "#]],
416 );
417 }
418
419 #[test]
334 fn option_iflet() { 420 fn option_iflet() {
335 check_edit( 421 check_edit(
336 "ifl", 422 "ifl",
diff --git a/crates/completion/src/completions/qualified_path.rs b/crates/completion/src/completions/qualified_path.rs
index d9387054d..bc23bea3f 100644
--- a/crates/completion/src/completions/qualified_path.rs
+++ b/crates/completion/src/completions/qualified_path.rs
@@ -353,10 +353,10 @@ impl S {
353fn foo() { let _ = S::<|> } 353fn foo() { let _ = S::<|> }
354"#, 354"#,
355 expect![[r#" 355 expect![[r#"
356 ct C const C: i32 = 42; 356 ct C const C: i32 = 42;
357 ta T type T = i32; 357 ta T type T = i32;
358 fn a() fn a() 358 fn a() fn a()
359 me b() fn b(&self) 359 me b(…) fn b(&self)
360 "#]], 360 "#]],
361 ); 361 );
362 } 362 }
@@ -503,14 +503,14 @@ trait Sub: Super {
503fn foo<T: Sub>() { T::<|> } 503fn foo<T: Sub>() { T::<|> }
504"#, 504"#,
505 expect![[r#" 505 expect![[r#"
506 ct C2 const C2: (); 506 ct C2 const C2: ();
507 ct CONST const CONST: u8; 507 ct CONST const CONST: u8;
508 ta SubTy type SubTy; 508 ta SubTy type SubTy;
509 ta Ty type Ty; 509 ta Ty type Ty;
510 fn func() fn func() 510 fn func() fn func()
511 me method() fn method(&self) 511 me method(…) fn method(&self)
512 fn subfunc() fn subfunc() 512 fn subfunc() fn subfunc()
513 me submethod() fn submethod(&self) 513 me submethod(…) fn submethod(&self)
514 "#]], 514 "#]],
515 ); 515 );
516 } 516 }
@@ -543,14 +543,14 @@ impl<T> Sub for Wrap<T> {
543} 543}
544"#, 544"#,
545 expect![[r#" 545 expect![[r#"
546 ct C2 const C2: () = (); 546 ct C2 const C2: () = ();
547 ct CONST const CONST: u8 = 0; 547 ct CONST const CONST: u8 = 0;
548 ta SubTy type SubTy; 548 ta SubTy type SubTy;
549 ta Ty type Ty; 549 ta Ty type Ty;
550 fn func() fn func() 550 fn func() fn func()
551 me method() fn method(&self) 551 me method(…) fn method(&self)
552 fn subfunc() fn subfunc() 552 fn subfunc() fn subfunc()
553 me submethod() fn submethod(&self) 553 me submethod(…) fn submethod(&self)
554 "#]], 554 "#]],
555 ); 555 );
556 } 556 }
diff --git a/crates/completion/src/completions/trait_impl.rs b/crates/completion/src/completions/trait_impl.rs
index a14be9c73..e2fe44aff 100644
--- a/crates/completion/src/completions/trait_impl.rs
+++ b/crates/completion/src/completions/trait_impl.rs
@@ -139,7 +139,7 @@ fn add_function_impl(
139) { 139) {
140 let fn_name = func.name(ctx.db).to_string(); 140 let fn_name = func.name(ctx.db).to_string();
141 141
142 let label = if func.params(ctx.db).is_empty() { 142 let label = if func.assoc_fn_params(ctx.db).is_empty() {
143 format!("fn {}()", fn_name) 143 format!("fn {}()", fn_name)
144 } else { 144 } else {
145 format!("fn {}(..)", fn_name) 145 format!("fn {}(..)", fn_name)
diff --git a/crates/completion/src/lib.rs b/crates/completion/src/lib.rs
index aecc1378b..1ec2e9be7 100644
--- a/crates/completion/src/lib.rs
+++ b/crates/completion/src/lib.rs
@@ -44,6 +44,8 @@ pub use crate::{
44// - `expr.while` -> `while expr {}` or `while let ... {}` for `Option` or `Result` 44// - `expr.while` -> `while expr {}` or `while let ... {}` for `Option` or `Result`
45// - `expr.ref` -> `&expr` 45// - `expr.ref` -> `&expr`
46// - `expr.refm` -> `&mut expr` 46// - `expr.refm` -> `&mut expr`
47// - `expr.let` -> `let <|> = expr;`
48// - `expr.letm` -> `let mut <|> = expr;`
47// - `expr.not` -> `!expr` 49// - `expr.not` -> `!expr`
48// - `expr.dbg` -> `dbg!(expr)` 50// - `expr.dbg` -> `dbg!(expr)`
49// - `expr.dbgr` -> `dbg!(&expr)` 51// - `expr.dbgr` -> `dbg!(&expr)`
diff --git a/crates/completion/src/render/builder_ext.rs b/crates/completion/src/render/builder_ext.rs
index 37b0d0459..ce8718bd5 100644
--- a/crates/completion/src/render/builder_ext.rs
+++ b/crates/completion/src/render/builder_ext.rs
@@ -5,6 +5,7 @@ use test_utils::mark;
5 5
6use crate::{item::Builder, CompletionContext}; 6use crate::{item::Builder, CompletionContext};
7 7
8#[derive(Debug)]
8pub(super) enum Params { 9pub(super) enum Params {
9 Named(Vec<String>), 10 Named(Vec<String>),
10 Anonymous(usize), 11 Anonymous(usize),
@@ -24,7 +25,7 @@ impl Params {
24} 25}
25 26
26impl Builder { 27impl Builder {
27 pub(super) fn should_add_parems(&self, ctx: &CompletionContext) -> bool { 28 fn should_add_parens(&self, ctx: &CompletionContext) -> bool {
28 if !ctx.config.add_call_parenthesis { 29 if !ctx.config.add_call_parenthesis {
29 return false; 30 return false;
30 } 31 }
@@ -58,7 +59,7 @@ impl Builder {
58 name: String, 59 name: String,
59 params: Params, 60 params: Params,
60 ) -> Builder { 61 ) -> Builder {
61 if !self.should_add_parems(ctx) { 62 if !self.should_add_parens(ctx) {
62 return self; 63 return self;
63 } 64 }
64 65
diff --git a/crates/completion/src/render/function.rs b/crates/completion/src/render/function.rs
index 542383d7e..00e3eb203 100644
--- a/crates/completion/src/render/function.rs
+++ b/crates/completion/src/render/function.rs
@@ -2,6 +2,7 @@
2 2
3use hir::{HasSource, Type}; 3use hir::{HasSource, Type};
4use syntax::{ast::Fn, display::function_declaration}; 4use syntax::{ast::Fn, display::function_declaration};
5use test_utils::mark;
5 6
6use crate::{ 7use crate::{
7 item::{CompletionItem, CompletionItemKind, CompletionKind, ImportToAdd}, 8 item::{CompletionItem, CompletionItemKind, CompletionKind, ImportToAdd},
@@ -22,7 +23,7 @@ pub(crate) fn render_fn<'a>(
22struct FunctionRender<'a> { 23struct FunctionRender<'a> {
23 ctx: RenderContext<'a>, 24 ctx: RenderContext<'a>,
24 name: String, 25 name: String,
25 fn_: hir::Function, 26 func: hir::Function,
26 ast_node: Fn, 27 ast_node: Fn,
27} 28}
28 29
@@ -35,15 +36,15 @@ impl<'a> FunctionRender<'a> {
35 let name = local_name.unwrap_or_else(|| fn_.name(ctx.db()).to_string()); 36 let name = local_name.unwrap_or_else(|| fn_.name(ctx.db()).to_string());
36 let ast_node = fn_.source(ctx.db()).value; 37 let ast_node = fn_.source(ctx.db()).value;
37 38
38 FunctionRender { ctx, name, fn_, ast_node } 39 FunctionRender { ctx, name, func: fn_, ast_node }
39 } 40 }
40 41
41 fn render(self, import_to_add: Option<ImportToAdd>) -> CompletionItem { 42 fn render(self, import_to_add: Option<ImportToAdd>) -> CompletionItem {
42 let params = self.params(); 43 let params = self.params();
43 CompletionItem::new(CompletionKind::Reference, self.ctx.source_range(), self.name.clone()) 44 CompletionItem::new(CompletionKind::Reference, self.ctx.source_range(), self.name.clone())
44 .kind(self.kind()) 45 .kind(self.kind())
45 .set_documentation(self.ctx.docs(self.fn_)) 46 .set_documentation(self.ctx.docs(self.func))
46 .set_deprecated(self.ctx.is_deprecated(self.fn_)) 47 .set_deprecated(self.ctx.is_deprecated(self.func))
47 .detail(self.detail()) 48 .detail(self.detail())
48 .add_call_parens(self.ctx.completion, self.name, params) 49 .add_call_parens(self.ctx.completion, self.name, params)
49 .add_import(import_to_add) 50 .add_import(import_to_add)
@@ -67,27 +68,39 @@ impl<'a> FunctionRender<'a> {
67 } 68 }
68 69
69 fn params(&self) -> Params { 70 fn params(&self) -> Params {
70 let params_ty = self.fn_.params(self.ctx.db()); 71 let ast_params = match self.ast_node.param_list() {
71 let params = self 72 Some(it) => it,
72 .ast_node 73 None => return Params::Named(Vec::new()),
73 .param_list() 74 };
75
76 let mut params_pats = Vec::new();
77 let params_ty = if self.ctx.completion.dot_receiver.is_some() {
78 self.func.method_params(self.ctx.db()).unwrap_or_default()
79 } else {
80 if let Some(s) = ast_params.self_param() {
81 mark::hit!(parens_for_method_call_as_assoc_fn);
82 params_pats.push(Some(s.to_string()));
83 }
84 self.func.assoc_fn_params(self.ctx.db())
85 };
86 params_pats
87 .extend(ast_params.params().into_iter().map(|it| it.pat().map(|it| it.to_string())));
88
89 let params = params_pats
74 .into_iter() 90 .into_iter()
75 .flat_map(|it| it.params())
76 .zip(params_ty) 91 .zip(params_ty)
77 .flat_map(|(it, param_ty)| { 92 .flat_map(|(pat, param_ty)| {
78 if let Some(pat) = it.pat() { 93 let pat = pat?;
79 let name = pat.to_string(); 94 let name = pat.to_string();
80 let arg = name.trim_start_matches("mut ").trim_start_matches('_'); 95 let arg = name.trim_start_matches("mut ").trim_start_matches('_');
81 return Some(self.add_arg(arg, param_ty.ty())); 96 Some(self.add_arg(arg, param_ty.ty()))
82 }
83 None
84 }) 97 })
85 .collect(); 98 .collect();
86 Params::Named(params) 99 Params::Named(params)
87 } 100 }
88 101
89 fn kind(&self) -> CompletionItemKind { 102 fn kind(&self) -> CompletionItemKind {
90 if self.fn_.self_param(self.ctx.db()).is_some() { 103 if self.func.self_param(self.ctx.db()).is_some() {
91 CompletionItemKind::Method 104 CompletionItemKind::Method
92 } else { 105 } else {
93 CompletionItemKind::Function 106 CompletionItemKind::Function
@@ -173,6 +186,28 @@ fn bar(s: &S) {
173 } 186 }
174 187
175 #[test] 188 #[test]
189 fn parens_for_method_call_as_assoc_fn() {
190 mark::check!(parens_for_method_call_as_assoc_fn);
191 check_edit(
192 "foo",
193 r#"
194struct S;
195impl S {
196 fn foo(&self) {}
197}
198fn main() { S::f<|> }
199"#,
200 r#"
201struct S;
202impl S {
203 fn foo(&self) {}
204}
205fn main() { S::foo(${1:&self})$0 }
206"#,
207 );
208 }
209
210 #[test]
176 fn suppress_arg_snippets() { 211 fn suppress_arg_snippets() {
177 mark::check!(suppress_arg_snippets); 212 mark::check!(suppress_arg_snippets);
178 check_edit_with_config( 213 check_edit_with_config(
diff --git a/crates/hir/src/code_model.rs b/crates/hir/src/code_model.rs
index f06b5cd9f..4500050f1 100644
--- a/crates/hir/src/code_model.rs
+++ b/crates/hir/src/code_model.rs
@@ -744,14 +744,13 @@ impl Function {
744 Some(SelfParam { func: self.id }) 744 Some(SelfParam { func: self.id })
745 } 745 }
746 746
747 pub fn params(self, db: &dyn HirDatabase) -> Vec<Param> { 747 pub fn assoc_fn_params(self, db: &dyn HirDatabase) -> Vec<Param> {
748 let resolver = self.id.resolver(db.upcast()); 748 let resolver = self.id.resolver(db.upcast());
749 let ctx = hir_ty::TyLoweringContext::new(db, &resolver); 749 let ctx = hir_ty::TyLoweringContext::new(db, &resolver);
750 let environment = TraitEnvironment::lower(db, &resolver); 750 let environment = TraitEnvironment::lower(db, &resolver);
751 db.function_data(self.id) 751 db.function_data(self.id)
752 .params 752 .params
753 .iter() 753 .iter()
754 .skip(if self.self_param(db).is_some() { 1 } else { 0 })
755 .map(|type_ref| { 754 .map(|type_ref| {
756 let ty = Type { 755 let ty = Type {
757 krate: self.id.lookup(db.upcast()).container.module(db.upcast()).krate, 756 krate: self.id.lookup(db.upcast()).container.module(db.upcast()).krate,
@@ -764,6 +763,14 @@ impl Function {
764 }) 763 })
765 .collect() 764 .collect()
766 } 765 }
766 pub fn method_params(self, db: &dyn HirDatabase) -> Option<Vec<Param>> {
767 if self.self_param(db).is_none() {
768 return None;
769 }
770 let mut res = self.assoc_fn_params(db);
771 res.remove(0);
772 Some(res)
773 }
767 774
768 pub fn is_unsafe(self, db: &dyn HirDatabase) -> bool { 775 pub fn is_unsafe(self, db: &dyn HirDatabase) -> bool {
769 db.function_data(self.id).is_unsafe 776 db.function_data(self.id).is_unsafe
@@ -799,6 +806,7 @@ impl From<Mutability> for Access {
799 } 806 }
800} 807}
801 808
809#[derive(Debug)]
802pub struct Param { 810pub struct Param {
803 ty: Type, 811 ty: Type,
804} 812}
diff --git a/crates/hir_def/src/body.rs b/crates/hir_def/src/body.rs
index d10b1af01..33eb5e78c 100644
--- a/crates/hir_def/src/body.rs
+++ b/crates/hir_def/src/body.rs
@@ -14,8 +14,8 @@ use cfg::CfgOptions;
14use drop_bomb::DropBomb; 14use drop_bomb::DropBomb;
15use either::Either; 15use either::Either;
16use hir_expand::{ 16use hir_expand::{
17 ast_id_map::AstIdMap, diagnostics::DiagnosticSink, hygiene::Hygiene, AstId, HirFileId, InFile, 17 ast_id_map::AstIdMap, diagnostics::DiagnosticSink, hygiene::Hygiene, AstId, ExpandResult,
18 MacroDefId, 18 HirFileId, InFile, MacroDefId,
19}; 19};
20use rustc_hash::FxHashMap; 20use rustc_hash::FxHashMap;
21use syntax::{ast, AstNode, AstPtr}; 21use syntax::{ast, AstNode, AstPtr};
@@ -102,11 +102,11 @@ impl Expander {
102 db: &dyn DefDatabase, 102 db: &dyn DefDatabase,
103 local_scope: Option<&ItemScope>, 103 local_scope: Option<&ItemScope>,
104 macro_call: ast::MacroCall, 104 macro_call: ast::MacroCall,
105 ) -> Option<(Mark, T)> { 105 ) -> ExpandResult<Option<(Mark, T)>> {
106 self.recursion_limit += 1; 106 self.recursion_limit += 1;
107 if self.recursion_limit > EXPANSION_RECURSION_LIMIT { 107 if self.recursion_limit > EXPANSION_RECURSION_LIMIT {
108 mark::hit!(your_stack_belongs_to_me); 108 mark::hit!(your_stack_belongs_to_me);
109 return None; 109 return ExpandResult::str_err("reached recursion limit during macro expansion".into());
110 } 110 }
111 111
112 let macro_call = InFile::new(self.current_file_id, &macro_call); 112 let macro_call = InFile::new(self.current_file_id, &macro_call);
@@ -120,28 +120,55 @@ impl Expander {
120 self.resolve_path_as_macro(db, &path) 120 self.resolve_path_as_macro(db, &path)
121 }; 121 };
122 122
123 if let Some(call_id) = macro_call.as_call_id(db, self.crate_def_map.krate, resolver) { 123 let call_id = match macro_call.as_call_id(db, self.crate_def_map.krate, resolver) {
124 let file_id = call_id.as_file(); 124 Some(it) => it,
125 if let Some(node) = db.parse_or_expand(file_id) { 125 None => {
126 if let Some(expr) = T::cast(node) { 126 // FIXME: this can mean other things too, but `as_call_id` doesn't provide enough
127 log::debug!("macro expansion {:#?}", expr.syntax()); 127 // info.
128 128 return ExpandResult::only_err(mbe::ExpandError::Other(
129 let mark = Mark { 129 "failed to parse or resolve macro invocation".into(),
130 file_id: self.current_file_id, 130 ));
131 ast_id_map: mem::take(&mut self.ast_id_map), 131 }
132 bomb: DropBomb::new("expansion mark dropped"), 132 };
133 }; 133
134 self.cfg_expander.hygiene = Hygiene::new(db.upcast(), file_id); 134 let err = db.macro_expand_error(call_id);
135 self.current_file_id = file_id; 135
136 self.ast_id_map = db.ast_id_map(file_id); 136 let file_id = call_id.as_file();
137 return Some((mark, expr)); 137
138 let raw_node = match db.parse_or_expand(file_id) {
139 Some(it) => it,
140 None => {
141 // Only `None` if the macro expansion produced no usable AST.
142 if err.is_none() {
143 log::warn!("no error despite `parse_or_expand` failing");
138 } 144 }
145
146 return ExpandResult::only_err(err.unwrap_or_else(|| {
147 mbe::ExpandError::Other("failed to parse macro invocation".into())
148 }));
139 } 149 }
140 } 150 };
151
152 let node = match T::cast(raw_node) {
153 Some(it) => it,
154 None => {
155 // This can happen without being an error, so only forward previous errors.
156 return ExpandResult { value: None, err };
157 }
158 };
159
160 log::debug!("macro expansion {:#?}", node.syntax());
161
162 let mark = Mark {
163 file_id: self.current_file_id,
164 ast_id_map: mem::take(&mut self.ast_id_map),
165 bomb: DropBomb::new("expansion mark dropped"),
166 };
167 self.cfg_expander.hygiene = Hygiene::new(db.upcast(), file_id);
168 self.current_file_id = file_id;
169 self.ast_id_map = db.ast_id_map(file_id);
141 170
142 // FIXME: Instead of just dropping the error from expansion 171 ExpandResult { value: Some((mark, node)), err }
143 // report it
144 None
145 } 172 }
146 173
147 pub(crate) fn exit(&mut self, db: &dyn DefDatabase, mut mark: Mark) { 174 pub(crate) fn exit(&mut self, db: &dyn DefDatabase, mut mark: Mark) {
diff --git a/crates/hir_def/src/body/diagnostics.rs b/crates/hir_def/src/body/diagnostics.rs
index e57bdc133..1de7d30e2 100644
--- a/crates/hir_def/src/body/diagnostics.rs
+++ b/crates/hir_def/src/body/diagnostics.rs
@@ -2,11 +2,13 @@
2 2
3use hir_expand::diagnostics::DiagnosticSink; 3use hir_expand::diagnostics::DiagnosticSink;
4 4
5use crate::diagnostics::InactiveCode; 5use crate::diagnostics::{InactiveCode, MacroError, UnresolvedProcMacro};
6 6
7#[derive(Debug, Eq, PartialEq)] 7#[derive(Debug, Eq, PartialEq)]
8pub(crate) enum BodyDiagnostic { 8pub(crate) enum BodyDiagnostic {
9 InactiveCode(InactiveCode), 9 InactiveCode(InactiveCode),
10 MacroError(MacroError),
11 UnresolvedProcMacro(UnresolvedProcMacro),
10} 12}
11 13
12impl BodyDiagnostic { 14impl BodyDiagnostic {
@@ -15,6 +17,12 @@ impl BodyDiagnostic {
15 BodyDiagnostic::InactiveCode(diag) => { 17 BodyDiagnostic::InactiveCode(diag) => {
16 sink.push(diag.clone()); 18 sink.push(diag.clone());
17 } 19 }
20 BodyDiagnostic::MacroError(diag) => {
21 sink.push(diag.clone());
22 }
23 BodyDiagnostic::UnresolvedProcMacro(diag) => {
24 sink.push(diag.clone());
25 }
18 } 26 }
19 } 27 }
20} 28}
diff --git a/crates/hir_def/src/body/lower.rs b/crates/hir_def/src/body/lower.rs
index cd7958746..2c41c0005 100644
--- a/crates/hir_def/src/body/lower.rs
+++ b/crates/hir_def/src/body/lower.rs
@@ -8,7 +8,7 @@ use either::Either;
8use hir_expand::{ 8use hir_expand::{
9 hygiene::Hygiene, 9 hygiene::Hygiene,
10 name::{name, AsName, Name}, 10 name::{name, AsName, Name},
11 HirFileId, MacroDefId, MacroDefKind, 11 ExpandError, HirFileId, MacroDefId, MacroDefKind,
12}; 12};
13use rustc_hash::FxHashMap; 13use rustc_hash::FxHashMap;
14use syntax::{ 14use syntax::{
@@ -25,7 +25,7 @@ use crate::{
25 body::{Body, BodySourceMap, Expander, PatPtr, SyntheticSyntax}, 25 body::{Body, BodySourceMap, Expander, PatPtr, SyntheticSyntax},
26 builtin_type::{BuiltinFloat, BuiltinInt}, 26 builtin_type::{BuiltinFloat, BuiltinInt},
27 db::DefDatabase, 27 db::DefDatabase,
28 diagnostics::InactiveCode, 28 diagnostics::{InactiveCode, MacroError, UnresolvedProcMacro},
29 expr::{ 29 expr::{
30 dummy_expr_id, ArithOp, Array, BinaryOp, BindingAnnotation, CmpOp, Expr, ExprId, Literal, 30 dummy_expr_id, ArithOp, Array, BinaryOp, BindingAnnotation, CmpOp, Expr, ExprId, Literal,
31 LogicOp, MatchArm, Ordering, Pat, PatId, RecordFieldPat, RecordLitField, Statement, 31 LogicOp, MatchArm, Ordering, Pat, PatId, RecordFieldPat, RecordLitField, Statement,
@@ -561,7 +561,32 @@ impl ExprCollector<'_> {
561 self.alloc_expr(Expr::Missing, syntax_ptr) 561 self.alloc_expr(Expr::Missing, syntax_ptr)
562 } else { 562 } else {
563 let macro_call = self.expander.to_source(AstPtr::new(&e)); 563 let macro_call = self.expander.to_source(AstPtr::new(&e));
564 match self.expander.enter_expand(self.db, Some(&self.body.item_scope), e) { 564 let res = self.expander.enter_expand(self.db, Some(&self.body.item_scope), e);
565
566 match res.err {
567 Some(ExpandError::UnresolvedProcMacro) => {
568 self.source_map.diagnostics.push(BodyDiagnostic::UnresolvedProcMacro(
569 UnresolvedProcMacro {
570 file: self.expander.current_file_id,
571 node: syntax_ptr.clone().into(),
572 precise_location: None,
573 macro_name: None,
574 },
575 ));
576 }
577 Some(err) => {
578 self.source_map.diagnostics.push(BodyDiagnostic::MacroError(
579 MacroError {
580 file: self.expander.current_file_id,
581 node: syntax_ptr.clone().into(),
582 message: err.to_string(),
583 },
584 ));
585 }
586 None => {}
587 }
588
589 match res.value {
565 Some((mark, expansion)) => { 590 Some((mark, expansion)) => {
566 self.source_map 591 self.source_map
567 .expansions 592 .expansions
diff --git a/crates/hir_def/src/data.rs b/crates/hir_def/src/data.rs
index ff1ef0df6..146045938 100644
--- a/crates/hir_def/src/data.rs
+++ b/crates/hir_def/src/data.rs
@@ -257,7 +257,7 @@ fn collect_items(
257 let root = db.parse_or_expand(file_id).unwrap(); 257 let root = db.parse_or_expand(file_id).unwrap();
258 let call = ast_id_map.get(call.ast_id).to_node(&root); 258 let call = ast_id_map.get(call.ast_id).to_node(&root);
259 259
260 if let Some((mark, mac)) = expander.enter_expand(db, None, call) { 260 if let Some((mark, mac)) = expander.enter_expand(db, None, call).value {
261 let src: InFile<ast::MacroItems> = expander.to_source(mac); 261 let src: InFile<ast::MacroItems> = expander.to_source(mac);
262 let item_tree = db.item_tree(src.file_id); 262 let item_tree = db.item_tree(src.file_id);
263 let iter = 263 let iter =
diff --git a/crates/hir_def/src/nameres/tests/mod_resolution.rs b/crates/hir_def/src/nameres/tests/mod_resolution.rs
index ba295fd9e..ef6f85e15 100644
--- a/crates/hir_def/src/nameres/tests/mod_resolution.rs
+++ b/crates/hir_def/src/nameres/tests/mod_resolution.rs
@@ -798,3 +798,24 @@ mod foo;
798"#, 798"#,
799 ); 799 );
800} 800}
801
802#[test]
803fn abs_path_ignores_local() {
804 check(
805 r#"
806//- /main.rs crate:main deps:core
807pub use ::core::hash::Hash;
808pub mod core {}
809
810//- /lib.rs crate:core
811pub mod hash { pub trait Hash {} }
812"#,
813 expect![[r#"
814 crate
815 Hash: t
816 core: t
817
818 crate::core
819 "#]],
820 );
821}
diff --git a/crates/hir_def/src/path/lower/lower_use.rs b/crates/hir_def/src/path/lower/lower_use.rs
index 53cecb05f..ba0d1f0e7 100644
--- a/crates/hir_def/src/path/lower/lower_use.rs
+++ b/crates/hir_def/src/path/lower/lower_use.rs
@@ -76,7 +76,7 @@ fn convert_path(prefix: Option<ModPath>, path: ast::Path, hygiene: &Hygiene) ->
76 Either::Left(name) => { 76 Either::Left(name) => {
77 // no type args in use 77 // no type args in use
78 let mut res = prefix.unwrap_or_else(|| ModPath { 78 let mut res = prefix.unwrap_or_else(|| ModPath {
79 kind: PathKind::Plain, 79 kind: segment.coloncolon_token().map_or(PathKind::Plain, |_| PathKind::Abs),
80 segments: Vec::with_capacity(1), 80 segments: Vec::with_capacity(1),
81 }); 81 });
82 res.segments.push(name); 82 res.segments.push(name);
diff --git a/crates/hir_expand/src/builtin_macro.rs b/crates/hir_expand/src/builtin_macro.rs
index aebbfc4df..7f4db106d 100644
--- a/crates/hir_expand/src/builtin_macro.rs
+++ b/crates/hir_expand/src/builtin_macro.rs
@@ -6,7 +6,7 @@ use crate::{
6 6
7use base_db::FileId; 7use base_db::FileId;
8use either::Either; 8use either::Either;
9use mbe::parse_to_token_tree; 9use mbe::{parse_to_token_tree, ExpandResult};
10use parser::FragmentKind; 10use parser::FragmentKind;
11use syntax::ast::{self, AstToken}; 11use syntax::ast::{self, AstToken};
12 12
@@ -28,7 +28,7 @@ macro_rules! register_builtin {
28 db: &dyn AstDatabase, 28 db: &dyn AstDatabase,
29 id: LazyMacroId, 29 id: LazyMacroId,
30 tt: &tt::Subtree, 30 tt: &tt::Subtree,
31 ) -> Result<tt::Subtree, mbe::ExpandError> { 31 ) -> ExpandResult<tt::Subtree> {
32 let expander = match *self { 32 let expander = match *self {
33 $( BuiltinFnLikeExpander::$kind => $expand, )* 33 $( BuiltinFnLikeExpander::$kind => $expand, )*
34 }; 34 };
@@ -42,7 +42,7 @@ macro_rules! register_builtin {
42 db: &dyn AstDatabase, 42 db: &dyn AstDatabase,
43 arg_id: EagerMacroId, 43 arg_id: EagerMacroId,
44 tt: &tt::Subtree, 44 tt: &tt::Subtree,
45 ) -> Result<(tt::Subtree, FragmentKind), mbe::ExpandError> { 45 ) -> ExpandResult<Option<(tt::Subtree, FragmentKind)>> {
46 let expander = match *self { 46 let expander = match *self {
47 $( EagerExpander::$e_kind => $e_expand, )* 47 $( EagerExpander::$e_kind => $e_expand, )*
48 }; 48 };
@@ -109,25 +109,28 @@ fn line_expand(
109 _db: &dyn AstDatabase, 109 _db: &dyn AstDatabase,
110 _id: LazyMacroId, 110 _id: LazyMacroId,
111 _tt: &tt::Subtree, 111 _tt: &tt::Subtree,
112) -> Result<tt::Subtree, mbe::ExpandError> { 112) -> ExpandResult<tt::Subtree> {
113 // dummy implementation for type-checking purposes 113 // dummy implementation for type-checking purposes
114 let line_num = 0; 114 let line_num = 0;
115 let expanded = quote! { 115 let expanded = quote! {
116 #line_num 116 #line_num
117 }; 117 };
118 118
119 Ok(expanded) 119 ExpandResult::ok(expanded)
120} 120}
121 121
122fn stringify_expand( 122fn stringify_expand(
123 db: &dyn AstDatabase, 123 db: &dyn AstDatabase,
124 id: LazyMacroId, 124 id: LazyMacroId,
125 _tt: &tt::Subtree, 125 _tt: &tt::Subtree,
126) -> Result<tt::Subtree, mbe::ExpandError> { 126) -> ExpandResult<tt::Subtree> {
127 let loc = db.lookup_intern_macro(id); 127 let loc = db.lookup_intern_macro(id);
128 128
129 let macro_content = { 129 let macro_content = {
130 let arg = loc.kind.arg(db).ok_or_else(|| mbe::ExpandError::UnexpectedToken)?; 130 let arg = match loc.kind.arg(db) {
131 Some(arg) => arg,
132 None => return ExpandResult::only_err(mbe::ExpandError::UnexpectedToken),
133 };
131 let macro_args = arg; 134 let macro_args = arg;
132 let text = macro_args.text(); 135 let text = macro_args.text();
133 let without_parens = TextSize::of('(')..text.len() - TextSize::of(')'); 136 let without_parens = TextSize::of('(')..text.len() - TextSize::of(')');
@@ -138,28 +141,28 @@ fn stringify_expand(
138 #macro_content 141 #macro_content
139 }; 142 };
140 143
141 Ok(expanded) 144 ExpandResult::ok(expanded)
142} 145}
143 146
144fn column_expand( 147fn column_expand(
145 _db: &dyn AstDatabase, 148 _db: &dyn AstDatabase,
146 _id: LazyMacroId, 149 _id: LazyMacroId,
147 _tt: &tt::Subtree, 150 _tt: &tt::Subtree,
148) -> Result<tt::Subtree, mbe::ExpandError> { 151) -> ExpandResult<tt::Subtree> {
149 // dummy implementation for type-checking purposes 152 // dummy implementation for type-checking purposes
150 let col_num = 0; 153 let col_num = 0;
151 let expanded = quote! { 154 let expanded = quote! {
152 #col_num 155 #col_num
153 }; 156 };
154 157
155 Ok(expanded) 158 ExpandResult::ok(expanded)
156} 159}
157 160
158fn assert_expand( 161fn assert_expand(
159 _db: &dyn AstDatabase, 162 _db: &dyn AstDatabase,
160 _id: LazyMacroId, 163 _id: LazyMacroId,
161 tt: &tt::Subtree, 164 tt: &tt::Subtree,
162) -> Result<tt::Subtree, mbe::ExpandError> { 165) -> ExpandResult<tt::Subtree> {
163 // A hacky implementation for goto def and hover 166 // A hacky implementation for goto def and hover
164 // We expand `assert!(cond, arg1, arg2)` to 167 // We expand `assert!(cond, arg1, arg2)` to
165 // ``` 168 // ```
@@ -191,14 +194,14 @@ fn assert_expand(
191 let expanded = quote! { 194 let expanded = quote! {
192 { { (##arg_tts); } } 195 { { (##arg_tts); } }
193 }; 196 };
194 Ok(expanded) 197 ExpandResult::ok(expanded)
195} 198}
196 199
197fn file_expand( 200fn file_expand(
198 _db: &dyn AstDatabase, 201 _db: &dyn AstDatabase,
199 _id: LazyMacroId, 202 _id: LazyMacroId,
200 _tt: &tt::Subtree, 203 _tt: &tt::Subtree,
201) -> Result<tt::Subtree, mbe::ExpandError> { 204) -> ExpandResult<tt::Subtree> {
202 // FIXME: RA purposefully lacks knowledge of absolute file names 205 // FIXME: RA purposefully lacks knowledge of absolute file names
203 // so just return "". 206 // so just return "".
204 let file_name = ""; 207 let file_name = "";
@@ -207,31 +210,33 @@ fn file_expand(
207 #file_name 210 #file_name
208 }; 211 };
209 212
210 Ok(expanded) 213 ExpandResult::ok(expanded)
211} 214}
212 215
213fn compile_error_expand( 216fn compile_error_expand(
214 _db: &dyn AstDatabase, 217 _db: &dyn AstDatabase,
215 _id: LazyMacroId, 218 _id: LazyMacroId,
216 tt: &tt::Subtree, 219 tt: &tt::Subtree,
217) -> Result<tt::Subtree, mbe::ExpandError> { 220) -> ExpandResult<tt::Subtree> {
218 if tt.count() == 1 { 221 if tt.count() == 1 {
219 if let tt::TokenTree::Leaf(tt::Leaf::Literal(it)) = &tt.token_trees[0] { 222 if let tt::TokenTree::Leaf(tt::Leaf::Literal(it)) = &tt.token_trees[0] {
220 let s = it.text.as_str(); 223 let s = it.text.as_str();
221 if s.contains('"') { 224 if s.contains('"') {
222 return Ok(quote! { loop { #it }}); 225 return ExpandResult::ok(quote! { loop { #it }});
223 } 226 }
224 }; 227 };
225 } 228 }
226 229
227 Err(mbe::ExpandError::BindingError("Must be a string".into())) 230 ExpandResult::only_err(mbe::ExpandError::BindingError(
231 "`compile_error!` argument be a string".into(),
232 ))
228} 233}
229 234
230fn format_args_expand( 235fn format_args_expand(
231 _db: &dyn AstDatabase, 236 _db: &dyn AstDatabase,
232 _id: LazyMacroId, 237 _id: LazyMacroId,
233 tt: &tt::Subtree, 238 tt: &tt::Subtree,
234) -> Result<tt::Subtree, mbe::ExpandError> { 239) -> ExpandResult<tt::Subtree> {
235 // We expand `format_args!("", a1, a2)` to 240 // We expand `format_args!("", a1, a2)` to
236 // ``` 241 // ```
237 // std::fmt::Arguments::new_v1(&[], &[ 242 // std::fmt::Arguments::new_v1(&[], &[
@@ -257,7 +262,7 @@ fn format_args_expand(
257 args.push(current); 262 args.push(current);
258 } 263 }
259 if args.is_empty() { 264 if args.is_empty() {
260 return Err(mbe::ExpandError::NoMatchingRule); 265 return ExpandResult::only_err(mbe::ExpandError::NoMatchingRule);
261 } 266 }
262 let _format_string = args.remove(0); 267 let _format_string = args.remove(0);
263 let arg_tts = args.into_iter().flat_map(|arg| { 268 let arg_tts = args.into_iter().flat_map(|arg| {
@@ -266,7 +271,7 @@ fn format_args_expand(
266 let expanded = quote! { 271 let expanded = quote! {
267 std::fmt::Arguments::new_v1(&[], &[##arg_tts]) 272 std::fmt::Arguments::new_v1(&[], &[##arg_tts])
268 }; 273 };
269 Ok(expanded) 274 ExpandResult::ok(expanded)
270} 275}
271 276
272fn unquote_str(lit: &tt::Literal) -> Option<String> { 277fn unquote_str(lit: &tt::Literal) -> Option<String> {
@@ -279,19 +284,24 @@ fn concat_expand(
279 _db: &dyn AstDatabase, 284 _db: &dyn AstDatabase,
280 _arg_id: EagerMacroId, 285 _arg_id: EagerMacroId,
281 tt: &tt::Subtree, 286 tt: &tt::Subtree,
282) -> Result<(tt::Subtree, FragmentKind), mbe::ExpandError> { 287) -> ExpandResult<Option<(tt::Subtree, FragmentKind)>> {
283 let mut text = String::new(); 288 let mut text = String::new();
284 for (i, t) in tt.token_trees.iter().enumerate() { 289 for (i, t) in tt.token_trees.iter().enumerate() {
285 match t { 290 match t {
286 tt::TokenTree::Leaf(tt::Leaf::Literal(it)) if i % 2 == 0 => { 291 tt::TokenTree::Leaf(tt::Leaf::Literal(it)) if i % 2 == 0 => {
287 text += &unquote_str(&it).ok_or_else(|| mbe::ExpandError::ConversionError)?; 292 text += &match unquote_str(&it) {
293 Some(s) => s,
294 None => {
295 return ExpandResult::only_err(mbe::ExpandError::ConversionError);
296 }
297 };
288 } 298 }
289 tt::TokenTree::Leaf(tt::Leaf::Punct(punct)) if i % 2 == 1 && punct.char == ',' => (), 299 tt::TokenTree::Leaf(tt::Leaf::Punct(punct)) if i % 2 == 1 && punct.char == ',' => (),
290 _ => return Err(mbe::ExpandError::UnexpectedToken), 300 _ => return ExpandResult::only_err(mbe::ExpandError::UnexpectedToken),
291 } 301 }
292 } 302 }
293 303
294 Ok((quote!(#text), FragmentKind::Expr)) 304 ExpandResult::ok(Some((quote!(#text), FragmentKind::Expr)))
295} 305}
296 306
297fn relative_file( 307fn relative_file(
@@ -324,26 +334,35 @@ fn include_expand(
324 db: &dyn AstDatabase, 334 db: &dyn AstDatabase,
325 arg_id: EagerMacroId, 335 arg_id: EagerMacroId,
326 tt: &tt::Subtree, 336 tt: &tt::Subtree,
327) -> Result<(tt::Subtree, FragmentKind), mbe::ExpandError> { 337) -> ExpandResult<Option<(tt::Subtree, FragmentKind)>> {
328 let path = parse_string(tt)?; 338 let res = (|| {
329 let file_id = relative_file(db, arg_id.into(), &path, false) 339 let path = parse_string(tt)?;
330 .ok_or_else(|| mbe::ExpandError::ConversionError)?; 340 let file_id = relative_file(db, arg_id.into(), &path, false)
331 341 .ok_or_else(|| mbe::ExpandError::ConversionError)?;
332 // FIXME: 342
333 // Handle include as expression 343 Ok(parse_to_token_tree(&db.file_text(file_id))
334 let res = parse_to_token_tree(&db.file_text(file_id)) 344 .ok_or_else(|| mbe::ExpandError::ConversionError)?
335 .ok_or_else(|| mbe::ExpandError::ConversionError)? 345 .0)
336 .0; 346 })();
337 347
338 Ok((res, FragmentKind::Items)) 348 match res {
349 Ok(res) => {
350 // FIXME:
351 // Handle include as expression
352 ExpandResult::ok(Some((res, FragmentKind::Items)))
353 }
354 Err(e) => ExpandResult::only_err(e),
355 }
339} 356}
340 357
341fn include_bytes_expand( 358fn include_bytes_expand(
342 _db: &dyn AstDatabase, 359 _db: &dyn AstDatabase,
343 _arg_id: EagerMacroId, 360 _arg_id: EagerMacroId,
344 tt: &tt::Subtree, 361 tt: &tt::Subtree,
345) -> Result<(tt::Subtree, FragmentKind), mbe::ExpandError> { 362) -> ExpandResult<Option<(tt::Subtree, FragmentKind)>> {
346 let _path = parse_string(tt)?; 363 if let Err(e) = parse_string(tt) {
364 return ExpandResult::only_err(e);
365 }
347 366
348 // FIXME: actually read the file here if the user asked for macro expansion 367 // FIXME: actually read the file here if the user asked for macro expansion
349 let res = tt::Subtree { 368 let res = tt::Subtree {
@@ -353,15 +372,18 @@ fn include_bytes_expand(
353 id: tt::TokenId::unspecified(), 372 id: tt::TokenId::unspecified(),
354 }))], 373 }))],
355 }; 374 };
356 Ok((res, FragmentKind::Expr)) 375 ExpandResult::ok(Some((res, FragmentKind::Expr)))
357} 376}
358 377
359fn include_str_expand( 378fn include_str_expand(
360 db: &dyn AstDatabase, 379 db: &dyn AstDatabase,
361 arg_id: EagerMacroId, 380 arg_id: EagerMacroId,
362 tt: &tt::Subtree, 381 tt: &tt::Subtree,
363) -> Result<(tt::Subtree, FragmentKind), mbe::ExpandError> { 382) -> ExpandResult<Option<(tt::Subtree, FragmentKind)>> {
364 let path = parse_string(tt)?; 383 let path = match parse_string(tt) {
384 Ok(it) => it,
385 Err(e) => return ExpandResult::only_err(e),
386 };
365 387
366 // FIXME: we're not able to read excluded files (which is most of them because 388 // FIXME: we're not able to read excluded files (which is most of them because
367 // it's unusual to `include_str!` a Rust file), but we can return an empty string. 389 // it's unusual to `include_str!` a Rust file), but we can return an empty string.
@@ -370,14 +392,14 @@ fn include_str_expand(
370 let file_id = match relative_file(db, arg_id.into(), &path, true) { 392 let file_id = match relative_file(db, arg_id.into(), &path, true) {
371 Some(file_id) => file_id, 393 Some(file_id) => file_id,
372 None => { 394 None => {
373 return Ok((quote!(""), FragmentKind::Expr)); 395 return ExpandResult::ok(Some((quote!(""), FragmentKind::Expr)));
374 } 396 }
375 }; 397 };
376 398
377 let text = db.file_text(file_id); 399 let text = db.file_text(file_id);
378 let text = &*text; 400 let text = &*text;
379 401
380 Ok((quote!(#text), FragmentKind::Expr)) 402 ExpandResult::ok(Some((quote!(#text), FragmentKind::Expr)))
381} 403}
382 404
383fn get_env_inner(db: &dyn AstDatabase, arg_id: EagerMacroId, key: &str) -> Option<String> { 405fn get_env_inner(db: &dyn AstDatabase, arg_id: EagerMacroId, key: &str) -> Option<String> {
@@ -389,8 +411,11 @@ fn env_expand(
389 db: &dyn AstDatabase, 411 db: &dyn AstDatabase,
390 arg_id: EagerMacroId, 412 arg_id: EagerMacroId,
391 tt: &tt::Subtree, 413 tt: &tt::Subtree,
392) -> Result<(tt::Subtree, FragmentKind), mbe::ExpandError> { 414) -> ExpandResult<Option<(tt::Subtree, FragmentKind)>> {
393 let key = parse_string(tt)?; 415 let key = match parse_string(tt) {
416 Ok(it) => it,
417 Err(e) => return ExpandResult::only_err(e),
418 };
394 419
395 // FIXME: 420 // FIXME:
396 // If the environment variable is not defined int rustc, then a compilation error will be emitted. 421 // If the environment variable is not defined int rustc, then a compilation error will be emitted.
@@ -402,21 +427,25 @@ fn env_expand(
402 let s = get_env_inner(db, arg_id, &key).unwrap_or_else(|| "__RA_UNIMPLEMENTED__".to_string()); 427 let s = get_env_inner(db, arg_id, &key).unwrap_or_else(|| "__RA_UNIMPLEMENTED__".to_string());
403 let expanded = quote! { #s }; 428 let expanded = quote! { #s };
404 429
405 Ok((expanded, FragmentKind::Expr)) 430 ExpandResult::ok(Some((expanded, FragmentKind::Expr)))
406} 431}
407 432
408fn option_env_expand( 433fn option_env_expand(
409 db: &dyn AstDatabase, 434 db: &dyn AstDatabase,
410 arg_id: EagerMacroId, 435 arg_id: EagerMacroId,
411 tt: &tt::Subtree, 436 tt: &tt::Subtree,
412) -> Result<(tt::Subtree, FragmentKind), mbe::ExpandError> { 437) -> ExpandResult<Option<(tt::Subtree, FragmentKind)>> {
413 let key = parse_string(tt)?; 438 let key = match parse_string(tt) {
439 Ok(it) => it,
440 Err(e) => return ExpandResult::only_err(e),
441 };
442
414 let expanded = match get_env_inner(db, arg_id, &key) { 443 let expanded = match get_env_inner(db, arg_id, &key) {
415 None => quote! { std::option::Option::None::<&str> }, 444 None => quote! { std::option::Option::None::<&str> },
416 Some(s) => quote! { std::option::Some(#s) }, 445 Some(s) => quote! { std::option::Some(#s) },
417 }; 446 };
418 447
419 Ok((expanded, FragmentKind::Expr)) 448 ExpandResult::ok(Some((expanded, FragmentKind::Expr)))
420} 449}
421 450
422#[cfg(test)] 451#[cfg(test)]
@@ -485,7 +514,7 @@ mod tests {
485 } 514 }
486 }); 515 });
487 516
488 let (subtree, fragment) = expander.expand(&db, arg_id, &parsed_args).unwrap(); 517 let (subtree, fragment) = expander.expand(&db, arg_id, &parsed_args).value.unwrap();
489 let eager = EagerCallLoc { 518 let eager = EagerCallLoc {
490 def, 519 def,
491 fragment, 520 fragment,
diff --git a/crates/hir_expand/src/db.rs b/crates/hir_expand/src/db.rs
index ff50bfd82..4fd0ba290 100644
--- a/crates/hir_expand/src/db.rs
+++ b/crates/hir_expand/src/db.rs
@@ -30,8 +30,8 @@ impl TokenExpander {
30 ) -> mbe::ExpandResult<tt::Subtree> { 30 ) -> mbe::ExpandResult<tt::Subtree> {
31 match self { 31 match self {
32 TokenExpander::MacroRules(it) => it.expand(tt), 32 TokenExpander::MacroRules(it) => it.expand(tt),
33 TokenExpander::Builtin(it) => it.expand(db, id, tt),
33 // FIXME switch these to ExpandResult as well 34 // FIXME switch these to ExpandResult as well
34 TokenExpander::Builtin(it) => it.expand(db, id, tt).into(),
35 TokenExpander::BuiltinDerive(it) => it.expand(db, id, tt).into(), 35 TokenExpander::BuiltinDerive(it) => it.expand(db, id, tt).into(),
36 TokenExpander::ProcMacro(_) => { 36 TokenExpander::ProcMacro(_) => {
37 // We store the result in salsa db to prevent non-determinisc behavior in 37 // We store the result in salsa db to prevent non-determinisc behavior in
diff --git a/crates/hir_expand/src/eager.rs b/crates/hir_expand/src/eager.rs
index 2f37d7189..ab6b4477c 100644
--- a/crates/hir_expand/src/eager.rs
+++ b/crates/hir_expand/src/eager.rs
@@ -65,7 +65,7 @@ pub fn expand_eager_macro(
65 let subtree = to_subtree(&result)?; 65 let subtree = to_subtree(&result)?;
66 66
67 if let MacroDefKind::BuiltInEager(eager) = def.kind { 67 if let MacroDefKind::BuiltInEager(eager) = def.kind {
68 let (subtree, fragment) = eager.expand(db, arg_id, &subtree).ok()?; 68 let (subtree, fragment) = eager.expand(db, arg_id, &subtree).value?;
69 let eager = EagerCallLoc { 69 let eager = EagerCallLoc {
70 def, 70 def,
71 fragment, 71 fragment,
diff --git a/crates/ide/src/references.rs b/crates/ide/src/references.rs
index 5693dd400..7395b81bd 100644
--- a/crates/ide/src/references.rs
+++ b/crates/ide/src/references.rs
@@ -21,7 +21,7 @@ use ide_db::{
21use syntax::{ 21use syntax::{
22 algo::find_node_at_offset, 22 algo::find_node_at_offset,
23 ast::{self, NameOwner}, 23 ast::{self, NameOwner},
24 AstNode, SyntaxKind, SyntaxNode, TextRange, TokenAtOffset, 24 match_ast, AstNode, SyntaxKind, SyntaxNode, TextRange, TokenAtOffset,
25}; 25};
26 26
27use crate::{display::TryToNav, FilePosition, FileRange, NavigationTarget, RangeInfo}; 27use crate::{display::TryToNav, FilePosition, FileRange, NavigationTarget, RangeInfo};
@@ -89,6 +89,10 @@ pub(crate) fn find_all_refs(
89 let _p = profile::span("find_all_refs"); 89 let _p = profile::span("find_all_refs");
90 let syntax = sema.parse(position.file_id).syntax().clone(); 90 let syntax = sema.parse(position.file_id).syntax().clone();
91 91
92 if let Some(res) = try_find_self_references(&syntax, position) {
93 return Some(res);
94 }
95
92 let (opt_name, search_kind) = if let Some(name) = 96 let (opt_name, search_kind) = if let Some(name) =
93 get_struct_def_name_for_struct_literal_search(&sema, &syntax, position) 97 get_struct_def_name_for_struct_literal_search(&sema, &syntax, position)
94 { 98 {
@@ -194,6 +198,77 @@ fn get_struct_def_name_for_struct_literal_search(
194 None 198 None
195} 199}
196 200
201fn try_find_self_references(
202 syntax: &SyntaxNode,
203 position: FilePosition,
204) -> Option<RangeInfo<ReferenceSearchResult>> {
205 let self_token =
206 syntax.token_at_offset(position.offset).find(|t| t.kind() == SyntaxKind::SELF_KW)?;
207 let parent = self_token.parent();
208 match_ast! {
209 match parent {
210 ast::SelfParam(it) => (),
211 ast::PathSegment(segment) => {
212 segment.self_token()?;
213 let path = segment.parent_path();
214 if path.qualifier().is_some() && !ast::PathExpr::can_cast(path.syntax().parent()?.kind()) {
215 return None;
216 }
217 },
218 _ => return None,
219 }
220 };
221 let function = parent.ancestors().find_map(ast::Fn::cast)?;
222 let self_param = function.param_list()?.self_param()?;
223 let param_self_token = self_param.self_token()?;
224
225 let declaration = Declaration {
226 nav: NavigationTarget {
227 file_id: position.file_id,
228 full_range: self_param.syntax().text_range(),
229 focus_range: Some(param_self_token.text_range()),
230 name: param_self_token.text().clone(),
231 kind: param_self_token.kind(),
232 container_name: None,
233 description: None,
234 docs: None,
235 },
236 kind: ReferenceKind::SelfKw,
237 access: Some(if self_param.mut_token().is_some() {
238 ReferenceAccess::Write
239 } else {
240 ReferenceAccess::Read
241 }),
242 };
243 let references = function
244 .body()
245 .map(|body| {
246 body.syntax()
247 .descendants()
248 .filter_map(ast::PathExpr::cast)
249 .filter_map(|expr| {
250 let path = expr.path()?;
251 if path.qualifier().is_none() {
252 path.segment()?.self_token()
253 } else {
254 None
255 }
256 })
257 .map(|token| Reference {
258 file_range: FileRange { file_id: position.file_id, range: token.text_range() },
259 kind: ReferenceKind::SelfKw,
260 access: declaration.access, // FIXME: properly check access kind here instead of copying it from the declaration
261 })
262 .collect()
263 })
264 .unwrap_or_default();
265
266 Some(RangeInfo::new(
267 param_self_token.text_range(),
268 ReferenceSearchResult { declaration, references },
269 ))
270}
271
197#[cfg(test)] 272#[cfg(test)]
198mod tests { 273mod tests {
199 use expect_test::{expect, Expect}; 274 use expect_test::{expect, Expect};
@@ -762,6 +837,32 @@ fn f() -> m::En {
762 ); 837 );
763 } 838 }
764 839
840 #[test]
841 fn test_find_self_refs() {
842 check(
843 r#"
844struct Foo { bar: i32 }
845
846impl Foo {
847 fn foo(self) {
848 let x = self<|>.bar;
849 if true {
850 let _ = match () {
851 () => self,
852 };
853 }
854 }
855}
856"#,
857 expect![[r#"
858 self SELF_KW FileId(0) 47..51 47..51 SelfKw Read
859
860 FileId(0) 71..75 SelfKw Read
861 FileId(0) 152..156 SelfKw Read
862 "#]],
863 );
864 }
865
765 fn check(ra_fixture: &str, expect: Expect) { 866 fn check(ra_fixture: &str, expect: Expect) {
766 check_with_scope(ra_fixture, None, expect) 867 check_with_scope(ra_fixture, None, expect)
767 } 868 }
diff --git a/crates/ide/src/references/rename.rs b/crates/ide/src/references/rename.rs
index 91c64bd4a..64fe8bd65 100644
--- a/crates/ide/src/references/rename.rs
+++ b/crates/ide/src/references/rename.rs
@@ -221,24 +221,47 @@ fn rename_to_self(
221 let source_file = sema.parse(position.file_id); 221 let source_file = sema.parse(position.file_id);
222 let syn = source_file.syntax(); 222 let syn = source_file.syntax();
223 223
224 let fn_def = find_node_at_offset::<ast::Fn>(syn, position.offset) 224 let (fn_def, fn_ast) = find_node_at_offset::<ast::Fn>(syn, position.offset)
225 .and_then(|fn_ast| sema.to_def(&fn_ast).zip(Some(fn_ast)))
225 .ok_or_else(|| RenameError("No surrounding method declaration found".to_string()))?; 226 .ok_or_else(|| RenameError("No surrounding method declaration found".to_string()))?;
226 let params = 227 let param_range = fn_ast
227 fn_def.param_list().ok_or_else(|| RenameError("Method has no parameters".to_string()))?; 228 .param_list()
228 if params.self_param().is_some() { 229 .and_then(|p| p.params().next())
230 .ok_or_else(|| RenameError("Method has no parameters".to_string()))?
231 .syntax()
232 .text_range();
233 if !param_range.contains(position.offset) {
234 return Err(RenameError("Only the first parameter can be self".to_string()));
235 }
236
237 let impl_block = find_node_at_offset::<ast::Impl>(syn, position.offset)
238 .and_then(|def| sema.to_def(&def))
239 .ok_or_else(|| RenameError("No impl block found for function".to_string()))?;
240 if fn_def.self_param(sema.db).is_some() {
229 return Err(RenameError("Method already has a self parameter".to_string())); 241 return Err(RenameError("Method already has a self parameter".to_string()));
230 } 242 }
243
244 let params = fn_def.assoc_fn_params(sema.db);
231 let first_param = 245 let first_param =
232 params.params().next().ok_or_else(|| RenameError("Method has no parameters".into()))?; 246 params.first().ok_or_else(|| RenameError("Method has no parameters".into()))?;
233 let mutable = match first_param.ty() { 247 let first_param_ty = first_param.ty();
234 Some(ast::Type::RefType(rt)) => rt.mut_token().is_some(), 248 let impl_ty = impl_block.target_ty(sema.db);
235 _ => return Err(RenameError("Not renaming other types".to_string())), 249 let (ty, self_param) = if impl_ty.remove_ref().is_some() {
250 // if the impl is a ref to the type we can just match the `&T` with self directly
251 (first_param_ty.clone(), "self")
252 } else {
253 first_param_ty.remove_ref().map_or((first_param_ty.clone(), "self"), |ty| {
254 (ty, if first_param_ty.is_mutable_reference() { "&mut self" } else { "&self" })
255 })
236 }; 256 };
237 257
258 if ty != impl_ty {
259 return Err(RenameError("Parameter type differs from impl block type".to_string()));
260 }
261
238 let RangeInfo { range, info: refs } = find_all_refs(sema, position, None) 262 let RangeInfo { range, info: refs } = find_all_refs(sema, position, None)
239 .ok_or_else(|| RenameError("No reference found at position".to_string()))?; 263 .ok_or_else(|| RenameError("No reference found at position".to_string()))?;
240 264
241 let param_range = first_param.syntax().text_range();
242 let (param_ref, usages): (Vec<Reference>, Vec<Reference>) = refs 265 let (param_ref, usages): (Vec<Reference>, Vec<Reference>) = refs
243 .into_iter() 266 .into_iter()
244 .partition(|reference| param_range.intersect(reference.file_range.range).is_some()); 267 .partition(|reference| param_range.intersect(reference.file_range.range).is_some());
@@ -254,10 +277,7 @@ fn rename_to_self(
254 277
255 edits.push(SourceFileEdit { 278 edits.push(SourceFileEdit {
256 file_id: position.file_id, 279 file_id: position.file_id,
257 edit: TextEdit::replace( 280 edit: TextEdit::replace(param_range, String::from(self_param)),
258 param_range,
259 String::from(if mutable { "&mut self" } else { "&self" }),
260 ),
261 }); 281 });
262 282
263 Ok(RangeInfo::new(range, SourceChange::from(edits))) 283 Ok(RangeInfo::new(range, SourceChange::from(edits)))
@@ -280,7 +300,11 @@ fn text_edit_from_self_param(
280 300
281 let mut replacement_text = String::from(new_name); 301 let mut replacement_text = String::from(new_name);
282 replacement_text.push_str(": "); 302 replacement_text.push_str(": ");
283 replacement_text.push_str(self_param.mut_token().map_or("&", |_| "&mut ")); 303 match (self_param.amp_token(), self_param.mut_token()) {
304 (None, None) => (),
305 (Some(_), None) => replacement_text.push('&'),
306 (_, Some(_)) => replacement_text.push_str("&mut "),
307 };
284 replacement_text.push_str(type_name.as_str()); 308 replacement_text.push_str(type_name.as_str());
285 309
286 Some(TextEdit::replace(self_param.syntax().text_range(), replacement_text)) 310 Some(TextEdit::replace(self_param.syntax().text_range(), replacement_text))
@@ -1082,6 +1106,95 @@ impl Foo {
1082} 1106}
1083"#, 1107"#,
1084 ); 1108 );
1109 check(
1110 "self",
1111 r#"
1112struct Foo { i: i32 }
1113
1114impl Foo {
1115 fn f(foo<|>: Foo) -> i32 {
1116 foo.i
1117 }
1118}
1119"#,
1120 r#"
1121struct Foo { i: i32 }
1122
1123impl Foo {
1124 fn f(self) -> i32 {
1125 self.i
1126 }
1127}
1128"#,
1129 );
1130 }
1131
1132 #[test]
1133 fn test_parameter_to_self_error_no_impl() {
1134 check(
1135 "self",
1136 r#"
1137struct Foo { i: i32 }
1138
1139fn f(foo<|>: &mut Foo) -> i32 {
1140 foo.i
1141}
1142"#,
1143 "error: No impl block found for function",
1144 );
1145 check(
1146 "self",
1147 r#"
1148struct Foo { i: i32 }
1149struct Bar;
1150
1151impl Bar {
1152 fn f(foo<|>: &mut Foo) -> i32 {
1153 foo.i
1154 }
1155}
1156"#,
1157 "error: Parameter type differs from impl block type",
1158 );
1159 }
1160
1161 #[test]
1162 fn test_parameter_to_self_error_not_first() {
1163 check(
1164 "self",
1165 r#"
1166struct Foo { i: i32 }
1167impl Foo {
1168 fn f(x: (), foo<|>: &mut Foo) -> i32 {
1169 foo.i
1170 }
1171}
1172"#,
1173 "error: Only the first parameter can be self",
1174 );
1175 }
1176
1177 #[test]
1178 fn test_parameter_to_self_impl_ref() {
1179 check(
1180 "self",
1181 r#"
1182struct Foo { i: i32 }
1183impl &Foo {
1184 fn f(foo<|>: &Foo) -> i32 {
1185 foo.i
1186 }
1187}
1188"#,
1189 r#"
1190struct Foo { i: i32 }
1191impl &Foo {
1192 fn f(self) -> i32 {
1193 self.i
1194 }
1195}
1196"#,
1197 );
1085 } 1198 }
1086 1199
1087 #[test] 1200 #[test]
@@ -1110,6 +1223,31 @@ impl Foo {
1110 } 1223 }
1111 1224
1112 #[test] 1225 #[test]
1226 fn test_owned_self_to_parameter() {
1227 check(
1228 "foo",
1229 r#"
1230struct Foo { i: i32 }
1231
1232impl Foo {
1233 fn f(<|>self) -> i32 {
1234 self.i
1235 }
1236}
1237"#,
1238 r#"
1239struct Foo { i: i32 }
1240
1241impl Foo {
1242 fn f(foo: Foo) -> i32 {
1243 foo.i
1244 }
1245}
1246"#,
1247 );
1248 }
1249
1250 #[test]
1113 fn test_self_in_path_to_parameter() { 1251 fn test_self_in_path_to_parameter() {
1114 check( 1252 check(
1115 "foo", 1253 "foo",
diff --git a/crates/ide/src/syntax_highlighting.rs b/crates/ide/src/syntax_highlighting.rs
index 1ed77b40b..5150a970c 100644
--- a/crates/ide/src/syntax_highlighting.rs
+++ b/crates/ide/src/syntax_highlighting.rs
@@ -76,6 +76,7 @@ pub(crate) fn highlight(
76 let mut current_macro_call: Option<ast::MacroCall> = None; 76 let mut current_macro_call: Option<ast::MacroCall> = None;
77 let mut format_string_highlighter = FormatStringHighlighter::default(); 77 let mut format_string_highlighter = FormatStringHighlighter::default();
78 let mut macro_rules_highlighter = MacroRulesHighlighter::default(); 78 let mut macro_rules_highlighter = MacroRulesHighlighter::default();
79 let mut inside_attribute = false;
79 80
80 // Walk all nodes, keeping track of whether we are inside a macro or not. 81 // Walk all nodes, keeping track of whether we are inside a macro or not.
81 // If in macro, expand it first and highlight the expanded code. 82 // If in macro, expand it first and highlight the expanded code.
@@ -132,9 +133,12 @@ pub(crate) fn highlight(
132 _ => (), 133 _ => (),
133 } 134 }
134 135
135 // Check for Rust code in documentation
136 match &event { 136 match &event {
137 // Check for Rust code in documentation
137 WalkEvent::Leave(NodeOrToken::Node(node)) => { 138 WalkEvent::Leave(NodeOrToken::Node(node)) => {
139 if ast::Attr::can_cast(node.kind()) {
140 inside_attribute = false
141 }
138 if let Some((doctest, range_mapping, new_comments)) = 142 if let Some((doctest, range_mapping, new_comments)) =
139 injection::extract_doc_comments(node) 143 injection::extract_doc_comments(node)
140 { 144 {
@@ -146,6 +150,9 @@ pub(crate) fn highlight(
146 ); 150 );
147 } 151 }
148 } 152 }
153 WalkEvent::Enter(NodeOrToken::Node(node)) if ast::Attr::can_cast(node.kind()) => {
154 inside_attribute = true
155 }
149 _ => (), 156 _ => (),
150 } 157 }
151 158
@@ -188,12 +195,16 @@ pub(crate) fn highlight(
188 } 195 }
189 } 196 }
190 197
191 if let Some((highlight, binding_hash)) = highlight_element( 198 if let Some((mut highlight, binding_hash)) = highlight_element(
192 &sema, 199 &sema,
193 &mut bindings_shadow_count, 200 &mut bindings_shadow_count,
194 syntactic_name_ref_highlighting, 201 syntactic_name_ref_highlighting,
195 element_to_highlight.clone(), 202 element_to_highlight.clone(),
196 ) { 203 ) {
204 if inside_attribute {
205 highlight = highlight | HighlightModifier::Attribute;
206 }
207
197 if macro_rules_highlighter.highlight(element_to_highlight.clone()).is_none() { 208 if macro_rules_highlighter.highlight(element_to_highlight.clone()).is_none() {
198 stack.add(HighlightedRange { range, highlight, binding_hash }); 209 stack.add(HighlightedRange { range, highlight, binding_hash });
199 } 210 }
@@ -474,7 +485,9 @@ fn highlight_element(
474 485
475 // Highlight references like the definitions they resolve to 486 // Highlight references like the definitions they resolve to
476 NAME_REF if element.ancestors().any(|it| it.kind() == ATTR) => { 487 NAME_REF if element.ancestors().any(|it| it.kind() == ATTR) => {
477 Highlight::from(HighlightTag::Function) | HighlightModifier::Attribute 488 // even though we track whether we are in an attribute or not we still need this special case
489 // as otherwise we would emit unresolved references for name refs inside attributes
490 Highlight::from(HighlightTag::Function)
478 } 491 }
479 NAME_REF => { 492 NAME_REF => {
480 let name_ref = element.into_node().and_then(ast::NameRef::cast).unwrap(); 493 let name_ref = element.into_node().and_then(ast::NameRef::cast).unwrap();
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html b/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html
index 6be88f856..d79fa6dca 100644
--- a/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html
+++ b/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html
@@ -50,7 +50,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
50 <span class="comment documentation">/// # Examples</span> 50 <span class="comment documentation">/// # Examples</span>
51 <span class="comment documentation">///</span> 51 <span class="comment documentation">///</span>
52 <span class="comment documentation">/// ```</span> 52 <span class="comment documentation">/// ```</span>
53 <span class="comment documentation">/// #</span><span class="generic injected"> </span><span class="attribute injected">#</span><span class="attribute injected">!</span><span class="attribute injected">[</span><span class="function attribute injected">allow</span><span class="punctuation injected">(</span><span class="attribute injected">unused_mut</span><span class="punctuation injected">)</span><span class="attribute injected">]</span> 53 <span class="comment documentation">/// #</span><span class="generic injected"> </span><span class="attribute attribute injected">#</span><span class="attribute attribute injected">!</span><span class="attribute attribute injected">[</span><span class="function attribute injected">allow</span><span class="punctuation attribute injected">(</span><span class="attribute attribute injected">unused_mut</span><span class="punctuation attribute injected">)</span><span class="attribute attribute injected">]</span>
54 <span class="comment documentation">/// </span><span class="keyword injected">let</span><span class="generic injected"> </span><span class="keyword injected">mut</span><span class="generic injected"> </span><span class="variable declaration injected mutable">foo</span><span class="punctuation injected">:</span><span class="generic injected"> </span><span class="struct injected">Foo</span><span class="generic injected"> </span><span class="operator injected">=</span><span class="generic injected"> </span><span class="struct injected">Foo</span><span class="operator injected">::</span><span class="function injected">new</span><span class="punctuation injected">(</span><span class="punctuation injected">)</span><span class="punctuation injected">;</span><span class="punctuation injected"> 54 <span class="comment documentation">/// </span><span class="keyword injected">let</span><span class="generic injected"> </span><span class="keyword injected">mut</span><span class="generic injected"> </span><span class="variable declaration injected mutable">foo</span><span class="punctuation injected">:</span><span class="generic injected"> </span><span class="struct injected">Foo</span><span class="generic injected"> </span><span class="operator injected">=</span><span class="generic injected"> </span><span class="struct injected">Foo</span><span class="operator injected">::</span><span class="function injected">new</span><span class="punctuation injected">(</span><span class="punctuation injected">)</span><span class="punctuation injected">;</span><span class="punctuation injected">
55</span> <span class="comment documentation">/// ```</span> 55</span> <span class="comment documentation">/// ```</span>
56 <span class="keyword">pub</span> <span class="keyword">const</span> <span class="keyword">fn</span> <span class="function declaration static">new</span><span class="punctuation">(</span><span class="punctuation">)</span> <span class="operator">-&gt;</span> <span class="struct">Foo</span> <span class="punctuation">{</span> 56 <span class="keyword">pub</span> <span class="keyword">const</span> <span class="keyword">fn</span> <span class="function declaration static">new</span><span class="punctuation">(</span><span class="punctuation">)</span> <span class="operator">-&gt;</span> <span class="struct">Foo</span> <span class="punctuation">{</span>
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html b/crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html
index 4b6d6adc9..1d05b7713 100644
--- a/crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html
+++ b/crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html
@@ -54,7 +54,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
54 54
55<span class="keyword">static</span> <span class="keyword">mut</span> <span class="static declaration mutable unsafe">global_mut</span><span class="punctuation">:</span> <span class="struct">TypeForStaticMut</span> <span class="operator">=</span> <span class="struct">TypeForStaticMut</span> <span class="punctuation">{</span> <span class="field">a</span><span class="punctuation">:</span> <span class="numeric_literal">0</span> <span class="punctuation">}</span><span class="punctuation">;</span> 55<span class="keyword">static</span> <span class="keyword">mut</span> <span class="static declaration mutable unsafe">global_mut</span><span class="punctuation">:</span> <span class="struct">TypeForStaticMut</span> <span class="operator">=</span> <span class="struct">TypeForStaticMut</span> <span class="punctuation">{</span> <span class="field">a</span><span class="punctuation">:</span> <span class="numeric_literal">0</span> <span class="punctuation">}</span><span class="punctuation">;</span>
56 56
57<span class="attribute">#</span><span class="attribute">[</span><span class="function attribute">repr</span><span class="punctuation">(</span><span class="attribute">packed</span><span class="punctuation">)</span><span class="attribute">]</span> 57<span class="attribute attribute">#</span><span class="attribute attribute">[</span><span class="function attribute">repr</span><span class="punctuation attribute">(</span><span class="attribute attribute">packed</span><span class="punctuation attribute">)</span><span class="attribute attribute">]</span>
58<span class="keyword">struct</span> <span class="struct declaration">Packed</span> <span class="punctuation">{</span> 58<span class="keyword">struct</span> <span class="struct declaration">Packed</span> <span class="punctuation">{</span>
59 <span class="field declaration">a</span><span class="punctuation">:</span> <span class="builtin_type">u16</span><span class="punctuation">,</span> 59 <span class="field declaration">a</span><span class="punctuation">:</span> <span class="builtin_type">u16</span><span class="punctuation">,</span>
60<span class="punctuation">}</span> 60<span class="punctuation">}</span>
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlighting.html b/crates/ide/src/syntax_highlighting/test_data/highlighting.html
index 6a10a9dcd..15fbf2ce3 100644
--- a/crates/ide/src/syntax_highlighting/test_data/highlighting.html
+++ b/crates/ide/src/syntax_highlighting/test_data/highlighting.html
@@ -40,18 +40,18 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
40 40
41<span class="comment">// Needed for function consuming vs normal</span> 41<span class="comment">// Needed for function consuming vs normal</span>
42<span class="keyword">pub</span> <span class="keyword">mod</span> <span class="module declaration">marker</span> <span class="punctuation">{</span> 42<span class="keyword">pub</span> <span class="keyword">mod</span> <span class="module declaration">marker</span> <span class="punctuation">{</span>
43 <span class="attribute">#</span><span class="attribute">[</span><span class="function attribute">lang</span><span class="attribute"> </span><span class="operator">=</span><span class="attribute"> </span><span class="string_literal">"copy"</span><span class="attribute">]</span> 43 <span class="attribute attribute">#</span><span class="attribute attribute">[</span><span class="function attribute">lang</span><span class="attribute attribute"> </span><span class="operator attribute">=</span><span class="attribute attribute"> </span><span class="string_literal attribute">"copy"</span><span class="attribute attribute">]</span>
44 <span class="keyword">pub</span> <span class="keyword">trait</span> <span class="trait declaration">Copy</span> <span class="punctuation">{</span><span class="punctuation">}</span> 44 <span class="keyword">pub</span> <span class="keyword">trait</span> <span class="trait declaration">Copy</span> <span class="punctuation">{</span><span class="punctuation">}</span>
45<span class="punctuation">}</span> 45<span class="punctuation">}</span>
46 46
47<span class="keyword">pub</span> <span class="keyword">mod</span> <span class="module declaration">ops</span> <span class="punctuation">{</span> 47<span class="keyword">pub</span> <span class="keyword">mod</span> <span class="module declaration">ops</span> <span class="punctuation">{</span>
48 <span class="attribute">#</span><span class="attribute">[</span><span class="function attribute">lang</span><span class="attribute"> </span><span class="operator">=</span><span class="attribute"> </span><span class="string_literal">"fn_once"</span><span class="attribute">]</span> 48 <span class="attribute attribute">#</span><span class="attribute attribute">[</span><span class="function attribute">lang</span><span class="attribute attribute"> </span><span class="operator attribute">=</span><span class="attribute attribute"> </span><span class="string_literal attribute">"fn_once"</span><span class="attribute attribute">]</span>
49 <span class="keyword">pub</span> <span class="keyword">trait</span> <span class="trait declaration">FnOnce</span><span class="punctuation">&lt;</span><span class="type_param declaration">Args</span><span class="punctuation">&gt;</span> <span class="punctuation">{</span><span class="punctuation">}</span> 49 <span class="keyword">pub</span> <span class="keyword">trait</span> <span class="trait declaration">FnOnce</span><span class="punctuation">&lt;</span><span class="type_param declaration">Args</span><span class="punctuation">&gt;</span> <span class="punctuation">{</span><span class="punctuation">}</span>
50 50
51 <span class="attribute">#</span><span class="attribute">[</span><span class="function attribute">lang</span><span class="attribute"> </span><span class="operator">=</span><span class="attribute"> </span><span class="string_literal">"fn_mut"</span><span class="attribute">]</span> 51 <span class="attribute attribute">#</span><span class="attribute attribute">[</span><span class="function attribute">lang</span><span class="attribute attribute"> </span><span class="operator attribute">=</span><span class="attribute attribute"> </span><span class="string_literal attribute">"fn_mut"</span><span class="attribute attribute">]</span>
52 <span class="keyword">pub</span> <span class="keyword">trait</span> <span class="trait declaration">FnMut</span><span class="punctuation">&lt;</span><span class="type_param declaration">Args</span><span class="punctuation">&gt;</span><span class="punctuation">:</span> <span class="trait">FnOnce</span><span class="punctuation">&lt;</span><span class="type_param">Args</span><span class="punctuation">&gt;</span> <span class="punctuation">{</span><span class="punctuation">}</span> 52 <span class="keyword">pub</span> <span class="keyword">trait</span> <span class="trait declaration">FnMut</span><span class="punctuation">&lt;</span><span class="type_param declaration">Args</span><span class="punctuation">&gt;</span><span class="punctuation">:</span> <span class="trait">FnOnce</span><span class="punctuation">&lt;</span><span class="type_param">Args</span><span class="punctuation">&gt;</span> <span class="punctuation">{</span><span class="punctuation">}</span>
53 53
54 <span class="attribute">#</span><span class="attribute">[</span><span class="function attribute">lang</span><span class="attribute"> </span><span class="operator">=</span><span class="attribute"> </span><span class="string_literal">"fn"</span><span class="attribute">]</span> 54 <span class="attribute attribute">#</span><span class="attribute attribute">[</span><span class="function attribute">lang</span><span class="attribute attribute"> </span><span class="operator attribute">=</span><span class="attribute attribute"> </span><span class="string_literal attribute">"fn"</span><span class="attribute attribute">]</span>
55 <span class="keyword">pub</span> <span class="keyword">trait</span> <span class="trait declaration">Fn</span><span class="punctuation">&lt;</span><span class="type_param declaration">Args</span><span class="punctuation">&gt;</span><span class="punctuation">:</span> <span class="trait">FnMut</span><span class="punctuation">&lt;</span><span class="type_param">Args</span><span class="punctuation">&gt;</span> <span class="punctuation">{</span><span class="punctuation">}</span> 55 <span class="keyword">pub</span> <span class="keyword">trait</span> <span class="trait declaration">Fn</span><span class="punctuation">&lt;</span><span class="type_param declaration">Args</span><span class="punctuation">&gt;</span><span class="punctuation">:</span> <span class="trait">FnMut</span><span class="punctuation">&lt;</span><span class="type_param">Args</span><span class="punctuation">&gt;</span> <span class="punctuation">{</span><span class="punctuation">}</span>
56<span class="punctuation">}</span> 56<span class="punctuation">}</span>
57 57
@@ -85,7 +85,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
85 <span class="punctuation">}</span> 85 <span class="punctuation">}</span>
86<span class="punctuation">}</span> 86<span class="punctuation">}</span>
87 87
88<span class="attribute">#</span><span class="attribute">[</span><span class="function attribute">derive</span><span class="punctuation">(</span><span class="attribute">Copy</span><span class="punctuation">)</span><span class="attribute">]</span> 88<span class="attribute attribute">#</span><span class="attribute attribute">[</span><span class="function attribute">derive</span><span class="punctuation attribute">(</span><span class="attribute attribute">Copy</span><span class="punctuation attribute">)</span><span class="attribute attribute">]</span>
89<span class="keyword">struct</span> <span class="struct declaration">FooCopy</span> <span class="punctuation">{</span> 89<span class="keyword">struct</span> <span class="struct declaration">FooCopy</span> <span class="punctuation">{</span>
90 <span class="field declaration">x</span><span class="punctuation">:</span> <span class="builtin_type">u32</span><span class="punctuation">,</span> 90 <span class="field declaration">x</span><span class="punctuation">:</span> <span class="builtin_type">u32</span><span class="punctuation">,</span>
91<span class="punctuation">}</span> 91<span class="punctuation">}</span>
diff --git a/crates/ide_db/src/helpers/insert_use.rs b/crates/ide_db/src/helpers/insert_use.rs
index 67e800fad..08d246c16 100644
--- a/crates/ide_db/src/helpers/insert_use.rs
+++ b/crates/ide_db/src/helpers/insert_use.rs
@@ -384,7 +384,7 @@ fn path_cmp_for_sort(a: Option<ast::Path>, b: Option<ast::Path>) -> Ordering {
384 384
385/// Path comparison func for binary searching for merging. 385/// Path comparison func for binary searching for merging.
386fn path_cmp_bin_search(lhs: Option<ast::Path>, rhs: Option<ast::Path>) -> Ordering { 386fn path_cmp_bin_search(lhs: Option<ast::Path>, rhs: Option<ast::Path>) -> Ordering {
387 match (lhs.and_then(|path| path.segment()), rhs.and_then(|path| path.segment())) { 387 match (lhs.as_ref().and_then(first_segment), rhs.as_ref().and_then(first_segment)) {
388 (None, None) => Ordering::Equal, 388 (None, None) => Ordering::Equal,
389 (None, Some(_)) => Ordering::Less, 389 (None, Some(_)) => Ordering::Less,
390 (Some(_), None) => Ordering::Greater, 390 (Some(_), None) => Ordering::Greater,
@@ -1082,6 +1082,15 @@ use std::io;",
1082 } 1082 }
1083 1083
1084 #[test] 1084 #[test]
1085 fn merge_nested_considers_first_segments() {
1086 check_full(
1087 "hir_ty::display::write_bounds_like_dyn_trait",
1088 r"use hir_ty::{autoderef, display::{HirDisplayError, HirFormatter}, method_resolution};",
1089 r"use hir_ty::{autoderef, display::{HirDisplayError, HirFormatter, write_bounds_like_dyn_trait}, method_resolution};",
1090 );
1091 }
1092
1093 #[test]
1085 fn skip_merge_last_too_long() { 1094 fn skip_merge_last_too_long() {
1086 check_last( 1095 check_last(
1087 "foo::bar", 1096 "foo::bar",
diff --git a/crates/ide_db/src/search.rs b/crates/ide_db/src/search.rs
index a3e765d05..607185ca9 100644
--- a/crates/ide_db/src/search.rs
+++ b/crates/ide_db/src/search.rs
@@ -31,6 +31,7 @@ pub enum ReferenceKind {
31 FieldShorthandForLocal, 31 FieldShorthandForLocal,
32 StructLiteral, 32 StructLiteral,
33 RecordFieldExprOrPat, 33 RecordFieldExprOrPat,
34 SelfKw,
34 Other, 35 Other,
35} 36}
36 37
diff --git a/crates/rust-analyzer/Cargo.toml b/crates/rust-analyzer/Cargo.toml
index 08559b53a..0a055b039 100644
--- a/crates/rust-analyzer/Cargo.toml
+++ b/crates/rust-analyzer/Cargo.toml
@@ -21,7 +21,7 @@ env_logger = { version = "0.8.1", default-features = false }
21itertools = "0.9.0" 21itertools = "0.9.0"
22jod-thread = "0.1.0" 22jod-thread = "0.1.0"
23log = "0.4.8" 23log = "0.4.8"
24lsp-types = { version = "0.84.0", features = ["proposed"] } 24lsp-types = { version = "0.85.0", features = ["proposed"] }
25parking_lot = "0.11.0" 25parking_lot = "0.11.0"
26pico-args = "0.3.1" 26pico-args = "0.3.1"
27oorandom = "11.1.2" 27oorandom = "11.1.2"
diff --git a/crates/rust-analyzer/src/semantic_tokens.rs b/crates/rust-analyzer/src/semantic_tokens.rs
index e7991fd28..1daad1c98 100644
--- a/crates/rust-analyzer/src/semantic_tokens.rs
+++ b/crates/rust-analyzer/src/semantic_tokens.rs
@@ -27,7 +27,7 @@ macro_rules! define_semantic_token_types {
27 SemanticTokenType::ENUM_MEMBER, 27 SemanticTokenType::ENUM_MEMBER,
28 SemanticTokenType::TYPE_PARAMETER, 28 SemanticTokenType::TYPE_PARAMETER,
29 SemanticTokenType::FUNCTION, 29 SemanticTokenType::FUNCTION,
30 SemanticTokenType::MEMBER, 30 SemanticTokenType::METHOD,
31 SemanticTokenType::PROPERTY, 31 SemanticTokenType::PROPERTY,
32 SemanticTokenType::MACRO, 32 SemanticTokenType::MACRO,
33 SemanticTokenType::VARIABLE, 33 SemanticTokenType::VARIABLE,
diff --git a/crates/syntax/Cargo.toml b/crates/syntax/Cargo.toml
index ce62babc3..c6a6f11e1 100644
--- a/crates/syntax/Cargo.toml
+++ b/crates/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 = "688.0.0", package = "rustc-ap-rustc_lexer" } 16rustc_lexer = { version = "691.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/vfs/src/file_set.rs b/crates/vfs/src/file_set.rs
index 4aa2d6526..9093fbd97 100644
--- a/crates/vfs/src/file_set.rs
+++ b/crates/vfs/src/file_set.rs
@@ -158,53 +158,4 @@ impl fst::Automaton for PrefixOf<'_> {
158} 158}
159 159
160#[cfg(test)] 160#[cfg(test)]
161mod tests { 161mod tests;
162 use super::*;
163
164 #[test]
165 fn path_prefix() {
166 let mut file_set = FileSetConfig::builder();
167 file_set.add_file_set(vec![VfsPath::new_virtual_path("/foo".into())]);
168 file_set.add_file_set(vec![VfsPath::new_virtual_path("/foo/bar/baz".into())]);
169 let file_set = file_set.build();
170
171 let mut vfs = Vfs::default();
172 vfs.set_file_contents(
173 VfsPath::new_virtual_path("/foo/src/lib.rs".into()),
174 Some(Vec::new()),
175 );
176 vfs.set_file_contents(
177 VfsPath::new_virtual_path("/foo/src/bar/baz/lib.rs".into()),
178 Some(Vec::new()),
179 );
180 vfs.set_file_contents(
181 VfsPath::new_virtual_path("/foo/bar/baz/lib.rs".into()),
182 Some(Vec::new()),
183 );
184 vfs.set_file_contents(VfsPath::new_virtual_path("/quux/lib.rs".into()), Some(Vec::new()));
185
186 let partition = file_set.partition(&vfs).into_iter().map(|it| it.len()).collect::<Vec<_>>();
187 assert_eq!(partition, vec![2, 1, 1]);
188 }
189
190 #[test]
191 fn name_prefix() {
192 let mut file_set = FileSetConfig::builder();
193 file_set.add_file_set(vec![VfsPath::new_virtual_path("/foo".into())]);
194 file_set.add_file_set(vec![VfsPath::new_virtual_path("/foo-things".into())]);
195 let file_set = file_set.build();
196
197 let mut vfs = Vfs::default();
198 vfs.set_file_contents(
199 VfsPath::new_virtual_path("/foo/src/lib.rs".into()),
200 Some(Vec::new()),
201 );
202 vfs.set_file_contents(
203 VfsPath::new_virtual_path("/foo-things/src/lib.rs".into()),
204 Some(Vec::new()),
205 );
206
207 let partition = file_set.partition(&vfs).into_iter().map(|it| it.len()).collect::<Vec<_>>();
208 assert_eq!(partition, vec![1, 1, 0]);
209 }
210}
diff --git a/crates/vfs/src/file_set/tests.rs b/crates/vfs/src/file_set/tests.rs
new file mode 100644
index 000000000..2146df185
--- /dev/null
+++ b/crates/vfs/src/file_set/tests.rs
@@ -0,0 +1,42 @@
1use super::*;
2
3#[test]
4fn path_prefix() {
5 let mut file_set = FileSetConfig::builder();
6 file_set.add_file_set(vec![VfsPath::new_virtual_path("/foo".into())]);
7 file_set.add_file_set(vec![VfsPath::new_virtual_path("/foo/bar/baz".into())]);
8 let file_set = file_set.build();
9
10 let mut vfs = Vfs::default();
11 vfs.set_file_contents(VfsPath::new_virtual_path("/foo/src/lib.rs".into()), Some(Vec::new()));
12 vfs.set_file_contents(
13 VfsPath::new_virtual_path("/foo/src/bar/baz/lib.rs".into()),
14 Some(Vec::new()),
15 );
16 vfs.set_file_contents(
17 VfsPath::new_virtual_path("/foo/bar/baz/lib.rs".into()),
18 Some(Vec::new()),
19 );
20 vfs.set_file_contents(VfsPath::new_virtual_path("/quux/lib.rs".into()), Some(Vec::new()));
21
22 let partition = file_set.partition(&vfs).into_iter().map(|it| it.len()).collect::<Vec<_>>();
23 assert_eq!(partition, vec![2, 1, 1]);
24}
25
26#[test]
27fn name_prefix() {
28 let mut file_set = FileSetConfig::builder();
29 file_set.add_file_set(vec![VfsPath::new_virtual_path("/foo".into())]);
30 file_set.add_file_set(vec![VfsPath::new_virtual_path("/foo-things".into())]);
31 let file_set = file_set.build();
32
33 let mut vfs = Vfs::default();
34 vfs.set_file_contents(VfsPath::new_virtual_path("/foo/src/lib.rs".into()), Some(Vec::new()));
35 vfs.set_file_contents(
36 VfsPath::new_virtual_path("/foo-things/src/lib.rs".into()),
37 Some(Vec::new()),
38 );
39
40 let partition = file_set.partition(&vfs).into_iter().map(|it| it.len()).collect::<Vec<_>>();
41 assert_eq!(partition, vec![1, 1, 0]);
42}
diff --git a/crates/vfs/src/vfs_path.rs b/crates/vfs/src/vfs_path.rs
index 815697597..bd14911c9 100644
--- a/crates/vfs/src/vfs_path.rs
+++ b/crates/vfs/src/vfs_path.rs
@@ -311,35 +311,4 @@ impl VirtualPath {
311} 311}
312 312
313#[cfg(test)] 313#[cfg(test)]
314mod tests { 314mod tests;
315 use super::*;
316
317 #[test]
318 fn virtual_path_extensions() {
319 assert_eq!(VirtualPath("/".to_string()).name_and_extension(), None);
320 assert_eq!(
321 VirtualPath("/directory".to_string()).name_and_extension(),
322 Some(("directory", None))
323 );
324 assert_eq!(
325 VirtualPath("/directory/".to_string()).name_and_extension(),
326 Some(("directory", None))
327 );
328 assert_eq!(
329 VirtualPath("/directory/file".to_string()).name_and_extension(),
330 Some(("file", None))
331 );
332 assert_eq!(
333 VirtualPath("/directory/.file".to_string()).name_and_extension(),
334 Some((".file", None))
335 );
336 assert_eq!(
337 VirtualPath("/directory/.file.rs".to_string()).name_and_extension(),
338 Some((".file", Some("rs")))
339 );
340 assert_eq!(
341 VirtualPath("/directory/file.rs".to_string()).name_and_extension(),
342 Some(("file", Some("rs")))
343 );
344 }
345}
diff --git a/crates/vfs/src/vfs_path/tests.rs b/crates/vfs/src/vfs_path/tests.rs
new file mode 100644
index 000000000..510e021e8
--- /dev/null
+++ b/crates/vfs/src/vfs_path/tests.rs
@@ -0,0 +1,30 @@
1use super::*;
2
3#[test]
4fn virtual_path_extensions() {
5 assert_eq!(VirtualPath("/".to_string()).name_and_extension(), None);
6 assert_eq!(
7 VirtualPath("/directory".to_string()).name_and_extension(),
8 Some(("directory", None))
9 );
10 assert_eq!(
11 VirtualPath("/directory/".to_string()).name_and_extension(),
12 Some(("directory", None))
13 );
14 assert_eq!(
15 VirtualPath("/directory/file".to_string()).name_and_extension(),
16 Some(("file", None))
17 );
18 assert_eq!(
19 VirtualPath("/directory/.file".to_string()).name_and_extension(),
20 Some((".file", None))
21 );
22 assert_eq!(
23 VirtualPath("/directory/.file.rs".to_string()).name_and_extension(),
24 Some((".file", Some("rs")))
25 );
26 assert_eq!(
27 VirtualPath("/directory/file.rs".to_string()).name_and_extension(),
28 Some(("file", Some("rs")))
29 );
30}