diff options
Diffstat (limited to 'crates')
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" |
410 | use std::fmt::{nested::Debug, Display}; | 410 | use std::fmt::{Display, nested::Debug}; |
411 | 411 | ||
412 | impl Display for Foo { | 412 | impl 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 | ||
6 | use crate::{utils::test_related_attribute, AssistContext, AssistId, AssistKind, Assists}; | 6 | use 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 | // ``` |
26 | pub(crate) fn ignore_test(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { | 26 | pub(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 | ||
57 | fn has_ignore_attribute(fn_def: &ast::Fn) -> Option<ast::Attr> { | 57 | fn 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)] |
68 | mod tests { | 62 | mod 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] |
477 | fn doctest_ignore_test() { | ||
478 | check_doc_test( | ||
479 | "ignore_test", | ||
480 | r#####" | ||
481 | <|>#[test] | ||
482 | fn arithmetics { | ||
483 | assert_eq!(2 + 2, 5); | ||
484 | } | ||
485 | "#####, | ||
486 | r#####" | ||
487 | #[test] | ||
488 | #[ignore] | ||
489 | fn arithmetics { | ||
490 | assert_eq!(2 + 2, 5); | ||
491 | } | ||
492 | "#####, | ||
493 | ) | ||
494 | } | ||
495 | |||
496 | #[test] | ||
497 | fn doctest_infer_function_return_type() { | 477 | fn 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] |
962 | fn doctest_toggle_ignore() { | ||
963 | check_doc_test( | ||
964 | "toggle_ignore", | ||
965 | r#####" | ||
966 | <|>#[test] | ||
967 | fn arithmetics { | ||
968 | assert_eq!(2 + 2, 5); | ||
969 | } | ||
970 | "#####, | ||
971 | r#####" | ||
972 | #[test] | ||
973 | #[ignore] | ||
974 | fn arithmetics { | ||
975 | assert_eq!(2 + 2, 5); | ||
976 | } | ||
977 | "#####, | ||
978 | ) | ||
979 | } | ||
980 | |||
981 | #[test] | ||
982 | fn doctest_unwrap_block() { | 982 | fn 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; | |||
5 | use ide_db::ty_filter::TryEnum; | 5 | use ide_db::ty_filter::TryEnum; |
6 | use syntax::{ | 6 | use 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 | }; |
10 | use text_edit::TextEdit; | 11 | use 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#" | ||
340 | fn foo(elt: bool) -> bool { | ||
341 | !elt | ||
342 | } | ||
343 | |||
344 | fn 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#" | ||
395 | fn 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 { | |||
353 | fn foo() { let _ = S::<|> } | 353 | fn 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 { | |||
503 | fn foo<T: Sub>() { T::<|> } | 503 | fn 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 | ||
6 | use crate::{item::Builder, CompletionContext}; | 6 | use crate::{item::Builder, CompletionContext}; |
7 | 7 | ||
8 | #[derive(Debug)] | ||
8 | pub(super) enum Params { | 9 | pub(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 | ||
26 | impl Builder { | 27 | impl 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 | ||
3 | use hir::{HasSource, Type}; | 3 | use hir::{HasSource, Type}; |
4 | use syntax::{ast::Fn, display::function_declaration}; | 4 | use syntax::{ast::Fn, display::function_declaration}; |
5 | use test_utils::mark; | ||
5 | 6 | ||
6 | use crate::{ | 7 | use crate::{ |
7 | item::{CompletionItem, CompletionItemKind, CompletionKind, ImportToAdd}, | 8 | item::{CompletionItem, CompletionItemKind, CompletionKind, ImportToAdd}, |
@@ -22,7 +23,7 @@ pub(crate) fn render_fn<'a>( | |||
22 | struct FunctionRender<'a> { | 23 | struct 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#" | ||
194 | struct S; | ||
195 | impl S { | ||
196 | fn foo(&self) {} | ||
197 | } | ||
198 | fn main() { S::f<|> } | ||
199 | "#, | ||
200 | r#" | ||
201 | struct S; | ||
202 | impl S { | ||
203 | fn foo(&self) {} | ||
204 | } | ||
205 | fn 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)] | ||
802 | pub struct Param { | 810 | pub 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; | |||
14 | use drop_bomb::DropBomb; | 14 | use drop_bomb::DropBomb; |
15 | use either::Either; | 15 | use either::Either; |
16 | use hir_expand::{ | 16 | use 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 | }; |
20 | use rustc_hash::FxHashMap; | 20 | use rustc_hash::FxHashMap; |
21 | use syntax::{ast, AstNode, AstPtr}; | 21 | use 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, ¯o_call); | 112 | let macro_call = InFile::new(self.current_file_id, ¯o_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 | ||
3 | use hir_expand::diagnostics::DiagnosticSink; | 3 | use hir_expand::diagnostics::DiagnosticSink; |
4 | 4 | ||
5 | use crate::diagnostics::InactiveCode; | 5 | use crate::diagnostics::{InactiveCode, MacroError, UnresolvedProcMacro}; |
6 | 6 | ||
7 | #[derive(Debug, Eq, PartialEq)] | 7 | #[derive(Debug, Eq, PartialEq)] |
8 | pub(crate) enum BodyDiagnostic { | 8 | pub(crate) enum BodyDiagnostic { |
9 | InactiveCode(InactiveCode), | 9 | InactiveCode(InactiveCode), |
10 | MacroError(MacroError), | ||
11 | UnresolvedProcMacro(UnresolvedProcMacro), | ||
10 | } | 12 | } |
11 | 13 | ||
12 | impl BodyDiagnostic { | 14 | impl 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; | |||
8 | use hir_expand::{ | 8 | use 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 | }; |
13 | use rustc_hash::FxHashMap; | 13 | use rustc_hash::FxHashMap; |
14 | use syntax::{ | 14 | use 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] | ||
803 | fn abs_path_ignores_local() { | ||
804 | check( | ||
805 | r#" | ||
806 | //- /main.rs crate:main deps:core | ||
807 | pub use ::core::hash::Hash; | ||
808 | pub mod core {} | ||
809 | |||
810 | //- /lib.rs crate:core | ||
811 | pub 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 | ||
7 | use base_db::FileId; | 7 | use base_db::FileId; |
8 | use either::Either; | 8 | use either::Either; |
9 | use mbe::parse_to_token_tree; | 9 | use mbe::{parse_to_token_tree, ExpandResult}; |
10 | use parser::FragmentKind; | 10 | use parser::FragmentKind; |
11 | use syntax::ast::{self, AstToken}; | 11 | use 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 | ||
122 | fn stringify_expand( | 122 | fn 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 | ||
144 | fn column_expand( | 147 | fn 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 | ||
158 | fn assert_expand( | 161 | fn 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 | ||
197 | fn file_expand( | 200 | fn 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 | ||
213 | fn compile_error_expand( | 216 | fn 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 | ||
230 | fn format_args_expand( | 235 | fn 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 | ||
272 | fn unquote_str(lit: &tt::Literal) -> Option<String> { | 277 | fn 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 | ||
297 | fn relative_file( | 307 | fn 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 | ||
341 | fn include_bytes_expand( | 358 | fn 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 | ||
359 | fn include_str_expand( | 378 | fn 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 | ||
383 | fn get_env_inner(db: &dyn AstDatabase, arg_id: EagerMacroId, key: &str) -> Option<String> { | 405 | fn 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 | ||
408 | fn option_env_expand( | 433 | fn 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::{ | |||
21 | use syntax::{ | 21 | use 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 | ||
27 | use crate::{display::TryToNav, FilePosition, FileRange, NavigationTarget, RangeInfo}; | 27 | use 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 | ||
201 | fn 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)] |
198 | mod tests { | 273 | mod 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#" | ||
844 | struct Foo { bar: i32 } | ||
845 | |||
846 | impl 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#" | ||
1112 | struct Foo { i: i32 } | ||
1113 | |||
1114 | impl Foo { | ||
1115 | fn f(foo<|>: Foo) -> i32 { | ||
1116 | foo.i | ||
1117 | } | ||
1118 | } | ||
1119 | "#, | ||
1120 | r#" | ||
1121 | struct Foo { i: i32 } | ||
1122 | |||
1123 | impl 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#" | ||
1137 | struct Foo { i: i32 } | ||
1138 | |||
1139 | fn 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#" | ||
1148 | struct Foo { i: i32 } | ||
1149 | struct Bar; | ||
1150 | |||
1151 | impl 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#" | ||
1166 | struct Foo { i: i32 } | ||
1167 | impl 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#" | ||
1182 | struct Foo { i: i32 } | ||
1183 | impl &Foo { | ||
1184 | fn f(foo<|>: &Foo) -> i32 { | ||
1185 | foo.i | ||
1186 | } | ||
1187 | } | ||
1188 | "#, | ||
1189 | r#" | ||
1190 | struct Foo { i: i32 } | ||
1191 | impl &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#" | ||
1230 | struct Foo { i: i32 } | ||
1231 | |||
1232 | impl Foo { | ||
1233 | fn f(<|>self) -> i32 { | ||
1234 | self.i | ||
1235 | } | ||
1236 | } | ||
1237 | "#, | ||
1238 | r#" | ||
1239 | struct Foo { i: i32 } | ||
1240 | |||
1241 | impl 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">-></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">-></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"><</span><span class="type_param declaration">Args</span><span class="punctuation">></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"><</span><span class="type_param declaration">Args</span><span class="punctuation">></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"><</span><span class="type_param declaration">Args</span><span class="punctuation">></span><span class="punctuation">:</span> <span class="trait">FnOnce</span><span class="punctuation"><</span><span class="type_param">Args</span><span class="punctuation">></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"><</span><span class="type_param declaration">Args</span><span class="punctuation">></span><span class="punctuation">:</span> <span class="trait">FnOnce</span><span class="punctuation"><</span><span class="type_param">Args</span><span class="punctuation">></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"><</span><span class="type_param declaration">Args</span><span class="punctuation">></span><span class="punctuation">:</span> <span class="trait">FnMut</span><span class="punctuation"><</span><span class="type_param">Args</span><span class="punctuation">></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"><</span><span class="type_param declaration">Args</span><span class="punctuation">></span><span class="punctuation">:</span> <span class="trait">FnMut</span><span class="punctuation"><</span><span class="type_param">Args</span><span class="punctuation">></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. |
386 | fn path_cmp_bin_search(lhs: Option<ast::Path>, rhs: Option<ast::Path>) -> Ordering { | 386 | fn 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 } | |||
21 | itertools = "0.9.0" | 21 | itertools = "0.9.0" |
22 | jod-thread = "0.1.0" | 22 | jod-thread = "0.1.0" |
23 | log = "0.4.8" | 23 | log = "0.4.8" |
24 | lsp-types = { version = "0.84.0", features = ["proposed"] } | 24 | lsp-types = { version = "0.85.0", features = ["proposed"] } |
25 | parking_lot = "0.11.0" | 25 | parking_lot = "0.11.0" |
26 | pico-args = "0.3.1" | 26 | pico-args = "0.3.1" |
27 | oorandom = "11.1.2" | 27 | oorandom = "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] |
14 | itertools = "0.9.0" | 14 | itertools = "0.9.0" |
15 | rowan = "0.10.0" | 15 | rowan = "0.10.0" |
16 | rustc_lexer = { version = "688.0.0", package = "rustc-ap-rustc_lexer" } | 16 | rustc_lexer = { version = "691.0.0", package = "rustc-ap-rustc_lexer" } |
17 | rustc-hash = "1.1.0" | 17 | rustc-hash = "1.1.0" |
18 | arrayvec = "0.5.1" | 18 | arrayvec = "0.5.1" |
19 | once_cell = "1.3.1" | 19 | once_cell = "1.3.1" |
diff --git a/crates/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)] |
161 | mod tests { | 161 | mod 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 @@ | |||
1 | use super::*; | ||
2 | |||
3 | #[test] | ||
4 | fn 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] | ||
27 | fn 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)] |
314 | mod tests { | 314 | mod 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 @@ | |||
1 | use super::*; | ||
2 | |||
3 | #[test] | ||
4 | fn 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 | } | ||