From d064ed5f63b746277895ae2aad37b5fd1516cce7 Mon Sep 17 00:00:00 2001 From: ivan770 Date: Sat, 13 Mar 2021 14:34:11 +0200 Subject: Count derefs as matched types if possible --- crates/ide_completion/src/render.rs | 128 ++++++++++++++++++++++++++++++++++- crates/rust-analyzer/src/to_proto.rs | 2 +- 2 files changed, 126 insertions(+), 4 deletions(-) (limited to 'crates') diff --git a/crates/ide_completion/src/render.rs b/crates/ide_completion/src/render.rs index db31896e5..027dee4f0 100644 --- a/crates/ide_completion/src/render.rs +++ b/crates/ide_completion/src/render.rs @@ -10,8 +10,10 @@ pub(crate) mod type_alias; mod builder_ext; +use base_db::Upcast; use hir::{ - AsAssocItem, Documentation, HasAttrs, HirDisplay, ModuleDef, Mutability, ScopeDef, Type, + db::HirDatabase, AsAssocItem, Documentation, HasAttrs, HirDisplay, ModuleDef, Mutability, + ScopeDef, Type, }; use ide_db::{ helpers::{item_name, SnippetCap}, @@ -325,11 +327,20 @@ impl<'a> Render<'a> { fn compute_relevance(ctx: &RenderContext, ty: &Type, name: &str) -> Option { let (expected_name, expected_type) = ctx.expected_name_and_type()?; let mut res = CompletionRelevance::default(); - res.exact_type_match = ty == &expected_type; + res.exact_type_match = ty == &expected_type + || autoderef_relevance( + ctx.db().upcast(), + ty, + expected_type.remove_ref().as_ref().unwrap_or(&expected_type), + ); res.exact_name_match = name == &expected_name; Some(res) } +fn autoderef_relevance(db: &dyn HirDatabase, ty: &Type, expected_type: &Type) -> bool { + ty.autoderef(db).any(|deref_ty| &deref_ty == expected_type) +} + #[cfg(test)] mod tests { use std::cmp::Reverse; @@ -979,7 +990,7 @@ fn main() { detail: "S", relevance: CompletionRelevance { exact_name_match: true, - exact_type_match: false, + exact_type_match: true, }, ref_match: "&mut ", }, @@ -987,4 +998,115 @@ fn main() { "#]], ) } + + #[test] + fn suggest_deref() { + check( + r#" +#[lang = "deref"] +trait Deref { + type Target; + fn deref(&self) -> &Self::Target; +} + +struct S; +struct T(S); + +impl Deref for T { + type Target = S; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +fn foo(s: &S) {} + +fn main() { + let t = T(S); + let m = 123; + + foo($0); +} + "#, + expect![[r#" + [ + CompletionItem { + label: "Deref", + source_range: 293..293, + delete: 293..293, + insert: "Deref", + kind: SymbolKind( + Trait, + ), + }, + CompletionItem { + label: "S", + source_range: 293..293, + delete: 293..293, + insert: "S", + kind: SymbolKind( + Struct, + ), + }, + CompletionItem { + label: "T", + source_range: 293..293, + delete: 293..293, + insert: "T", + kind: SymbolKind( + Struct, + ), + }, + CompletionItem { + label: "foo(…)", + source_range: 293..293, + delete: 293..293, + insert: "foo(${1:s})$0", + kind: SymbolKind( + Function, + ), + lookup: "foo", + detail: "-> ()", + trigger_call_info: true, + }, + CompletionItem { + label: "m", + source_range: 293..293, + delete: 293..293, + insert: "m", + kind: SymbolKind( + Local, + ), + detail: "i32", + }, + CompletionItem { + label: "main()", + source_range: 293..293, + delete: 293..293, + insert: "main()$0", + kind: SymbolKind( + Function, + ), + lookup: "main", + detail: "-> ()", + }, + CompletionItem { + label: "t", + source_range: 293..293, + delete: 293..293, + insert: "t", + kind: SymbolKind( + Local, + ), + detail: "T", + relevance: CompletionRelevance { + exact_name_match: false, + exact_type_match: true, + }, + }, + ] + "#]], + ) + } } diff --git a/crates/rust-analyzer/src/to_proto.rs b/crates/rust-analyzer/src/to_proto.rs index 1a8cdadad..fecae1259 100644 --- a/crates/rust-analyzer/src/to_proto.rs +++ b/crates/rust-analyzer/src/to_proto.rs @@ -1123,7 +1123,7 @@ mod tests { ( "arg", Some( - "fffffffe", + "fffffffd", ), ), ] -- cgit v1.2.3 From 75cb441fba111bed153f519f1f2906843360cfc7 Mon Sep 17 00:00:00 2001 From: ivan770 Date: Sat, 13 Mar 2021 15:32:37 +0200 Subject: Simplify call site and deref completion test --- crates/ide_completion/src/render.rs | 101 ++++++------------------------------ 1 file changed, 16 insertions(+), 85 deletions(-) (limited to 'crates') diff --git a/crates/ide_completion/src/render.rs b/crates/ide_completion/src/render.rs index 027dee4f0..e72f54ef4 100644 --- a/crates/ide_completion/src/render.rs +++ b/crates/ide_completion/src/render.rs @@ -327,18 +327,18 @@ impl<'a> Render<'a> { fn compute_relevance(ctx: &RenderContext, ty: &Type, name: &str) -> Option { let (expected_name, expected_type) = ctx.expected_name_and_type()?; let mut res = CompletionRelevance::default(); - res.exact_type_match = ty == &expected_type - || autoderef_relevance( - ctx.db().upcast(), - ty, - expected_type.remove_ref().as_ref().unwrap_or(&expected_type), - ); + res.exact_type_match = relevance_type_match(ctx.db().upcast(), ty, expected_type); res.exact_name_match = name == &expected_name; Some(res) } -fn autoderef_relevance(db: &dyn HirDatabase, ty: &Type, expected_type: &Type) -> bool { - ty.autoderef(db).any(|deref_ty| &deref_ty == expected_type) +fn relevance_type_match(db: &dyn HirDatabase, ty: &Type, expected_type: Type) -> bool { + if ty == &expected_type { + return true; + } + + let ty_without_ref = expected_type.remove_ref().unwrap_or(expected_type); + ty.autoderef(db).any(|deref_ty| deref_ty == ty_without_ref) } #[cfg(test)] @@ -1001,7 +1001,7 @@ fn main() { #[test] fn suggest_deref() { - check( + check_relevance( r#" #[lang = "deref"] trait Deref { @@ -1030,82 +1030,13 @@ fn main() { } "#, expect![[r#" - [ - CompletionItem { - label: "Deref", - source_range: 293..293, - delete: 293..293, - insert: "Deref", - kind: SymbolKind( - Trait, - ), - }, - CompletionItem { - label: "S", - source_range: 293..293, - delete: 293..293, - insert: "S", - kind: SymbolKind( - Struct, - ), - }, - CompletionItem { - label: "T", - source_range: 293..293, - delete: 293..293, - insert: "T", - kind: SymbolKind( - Struct, - ), - }, - CompletionItem { - label: "foo(…)", - source_range: 293..293, - delete: 293..293, - insert: "foo(${1:s})$0", - kind: SymbolKind( - Function, - ), - lookup: "foo", - detail: "-> ()", - trigger_call_info: true, - }, - CompletionItem { - label: "m", - source_range: 293..293, - delete: 293..293, - insert: "m", - kind: SymbolKind( - Local, - ), - detail: "i32", - }, - CompletionItem { - label: "main()", - source_range: 293..293, - delete: 293..293, - insert: "main()$0", - kind: SymbolKind( - Function, - ), - lookup: "main", - detail: "-> ()", - }, - CompletionItem { - label: "t", - source_range: 293..293, - delete: 293..293, - insert: "t", - kind: SymbolKind( - Local, - ), - detail: "T", - relevance: CompletionRelevance { - exact_name_match: false, - exact_type_match: true, - }, - }, - ] + lc t [type] + tt Deref [] + st S [] + st T [] + fn foo(…) [] + lc m [] + fn main() [] "#]], ) } -- cgit v1.2.3 From 3bc5d81a33c98da2a5b450c817c09f5a41b03e98 Mon Sep 17 00:00:00 2001 From: ivan770 Date: Sat, 13 Mar 2021 17:25:41 +0200 Subject: Make relevance tests display references, suggest derefs only when needed --- crates/ide_completion/src/render.rs | 98 ++++++++++++++++++++++++++++++------ crates/rust-analyzer/src/to_proto.rs | 2 +- 2 files changed, 84 insertions(+), 16 deletions(-) (limited to 'crates') diff --git a/crates/ide_completion/src/render.rs b/crates/ide_completion/src/render.rs index e72f54ef4..1f4a8173a 100644 --- a/crates/ide_completion/src/render.rs +++ b/crates/ide_completion/src/render.rs @@ -253,12 +253,14 @@ impl<'a> Render<'a> { if let ScopeDef::Local(local) = resolution { let ty = local.ty(self.ctx.db()); + if let Some(relevance) = compute_relevance(&self.ctx, &ty, &local_name) { item.set_relevance(relevance); } + if let Some((_expected_name, expected_type)) = self.ctx.expected_name_and_type() { if let Some(ty_without_ref) = expected_type.remove_ref() { - if ty_without_ref == ty { + if relevance_type_match(self.ctx.db().upcast(), &ty, &ty_without_ref) { cov_mark::hit!(suggest_ref); let mutability = if expected_type.is_mutable_reference() { Mutability::Mut @@ -327,18 +329,13 @@ impl<'a> Render<'a> { fn compute_relevance(ctx: &RenderContext, ty: &Type, name: &str) -> Option { let (expected_name, expected_type) = ctx.expected_name_and_type()?; let mut res = CompletionRelevance::default(); - res.exact_type_match = relevance_type_match(ctx.db().upcast(), ty, expected_type); + res.exact_type_match = ty == &expected_type; res.exact_name_match = name == &expected_name; Some(res) } -fn relevance_type_match(db: &dyn HirDatabase, ty: &Type, expected_type: Type) -> bool { - if ty == &expected_type { - return true; - } - - let ty_without_ref = expected_type.remove_ref().unwrap_or(expected_type); - ty.autoderef(db).any(|deref_ty| deref_ty == ty_without_ref) +fn relevance_type_match(db: &dyn HirDatabase, ty: &Type, expected_type: &Type) -> bool { + ty == expected_type || ty.autoderef(db).any(|deref_ty| &deref_ty == expected_type) } #[cfg(test)] @@ -346,6 +343,7 @@ mod tests { use std::cmp::Reverse; use expect_test::{expect, Expect}; + use hir::Mutability; use crate::{ test_utils::{check_edit, do_completion, get_all_items, TEST_CONFIG}, @@ -369,15 +367,31 @@ mod tests { } } + fn display_label(label: &str, mutability: Option) -> String { + let mutability_label = match mutability { + Some(Mutability::Shared) => "&", + Some(Mutability::Mut) => "&mut ", + None => "", + }; + + format!("{}{}", mutability_label, label) + } + let mut completions = get_all_items(TEST_CONFIG, ra_fixture); - completions.sort_by_key(|it| (Reverse(it.relevance()), it.label().to_string())); + completions.sort_by_key(|it| { + (Reverse(it.ref_match().map(|m| m.1).unwrap_or(it.relevance())), it.label().to_string()) + }); let actual = completions .into_iter() .filter(|it| it.completion_kind == CompletionKind::Reference) .map(|it| { let tag = it.kind().unwrap().tag(); - let relevance = display_relevance(it.relevance()); - format!("{} {} {}\n", tag, it.label(), relevance) + let (mutability, relevance) = it + .ref_match() + .map(|(mutability, relevance)| (Some(mutability), relevance)) + .unwrap_or((None, it.relevance())); + let relevance = display_relevance(relevance); + format!("{} {} {}\n", tag, display_label(it.label(), mutability), relevance) }) .collect::(); expect.assert_eq(&actual); @@ -911,7 +925,7 @@ struct WorldSnapshot { _f: () }; fn go(world: &WorldSnapshot) { go(w$0) } "#, expect![[r#" - lc world [type+name] + lc &world [type+name] st WorldSnapshot [] fn go(…) [] "#]], @@ -990,7 +1004,7 @@ fn main() { detail: "S", relevance: CompletionRelevance { exact_name_match: true, - exact_type_match: true, + exact_type_match: false, }, ref_match: "&mut ", }, @@ -1030,8 +1044,62 @@ fn main() { } "#, expect![[r#" - lc t [type] + lc &t [type] + tt Deref [] + st S [] + st T [] + fn foo(…) [] + lc m [] + fn main() [] + "#]], + ) + } + + #[test] + fn suggest_deref_mut() { + check_relevance( + r#" +#[lang = "deref"] +trait Deref { + type Target; + fn deref(&self) -> &Self::Target; +} + +#[lang = "deref_mut"] +pub trait DerefMut: Deref { + fn deref_mut(&mut self) -> &mut Self::Target; +} + +struct S; +struct T(S); + +impl Deref for T { + type Target = S; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl DerefMut for T { + fn deref_mut(&self) -> &mut Self::Target { + &mut self.0 + } +} + +fn foo(s: &mut S) {} + +fn main() { + let t = T(S); + let m = 123; + + foo($0); +} + "#, + expect![[r#" + lc &mut t [type] tt Deref [] + tt DerefMut [] st S [] st T [] fn foo(…) [] diff --git a/crates/rust-analyzer/src/to_proto.rs b/crates/rust-analyzer/src/to_proto.rs index fecae1259..1a8cdadad 100644 --- a/crates/rust-analyzer/src/to_proto.rs +++ b/crates/rust-analyzer/src/to_proto.rs @@ -1123,7 +1123,7 @@ mod tests { ( "arg", Some( - "fffffffd", + "fffffffe", ), ), ] -- cgit v1.2.3 From 32ad929b82b64a49c0c4df891967ac82786c6682 Mon Sep 17 00:00:00 2001 From: ivan770 Date: Sat, 13 Mar 2021 17:28:05 +0200 Subject: Fix incorrect DerefMut test reference type --- crates/ide_completion/src/render.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'crates') diff --git a/crates/ide_completion/src/render.rs b/crates/ide_completion/src/render.rs index 1f4a8173a..0c1169ecb 100644 --- a/crates/ide_completion/src/render.rs +++ b/crates/ide_completion/src/render.rs @@ -1082,7 +1082,7 @@ impl Deref for T { } impl DerefMut for T { - fn deref_mut(&self) -> &mut Self::Target { + fn deref_mut(&mut self) -> &mut Self::Target { &mut self.0 } } -- cgit v1.2.3 From 661cc7f0c865bad772fda41647219e47ad113d3c Mon Sep 17 00:00:00 2001 From: ivan770 Date: Sat, 13 Mar 2021 18:31:52 +0200 Subject: Added both references and original matches to tests --- crates/ide_completion/src/render.rs | 76 ++++++++++++++++++++----------------- 1 file changed, 42 insertions(+), 34 deletions(-) (limited to 'crates') diff --git a/crates/ide_completion/src/render.rs b/crates/ide_completion/src/render.rs index 0c1169ecb..ede4aa807 100644 --- a/crates/ide_completion/src/render.rs +++ b/crates/ide_completion/src/render.rs @@ -343,7 +343,6 @@ mod tests { use std::cmp::Reverse; use expect_test::{expect, Expect}; - use hir::Mutability; use crate::{ test_utils::{check_edit, do_completion, get_all_items, TEST_CONFIG}, @@ -367,33 +366,39 @@ mod tests { } } - fn display_label(label: &str, mutability: Option) -> String { - let mutability_label = match mutability { - Some(Mutability::Shared) => "&", - Some(Mutability::Mut) => "&mut ", - None => "", - }; - - format!("{}{}", mutability_label, label) - } - let mut completions = get_all_items(TEST_CONFIG, ra_fixture); completions.sort_by_key(|it| { - (Reverse(it.ref_match().map(|m| m.1).unwrap_or(it.relevance())), it.label().to_string()) + // (Reverse(it.ref_match().map(|m| m.1).unwrap_or(it.relevance())), it.label().to_string()) + if let Some((mutability, relevance)) = it.ref_match() { + let label = format!("&{}{}", mutability.as_keyword_for_ref(), it.label()); + + Reverse((relevance, label)) + } else { + Reverse((it.relevance(), it.label().to_string())) + } }); + let actual = completions .into_iter() .filter(|it| it.completion_kind == CompletionKind::Reference) - .map(|it| { + .flat_map(|it| { + let mut items = vec![]; + let tag = it.kind().unwrap().tag(); - let (mutability, relevance) = it - .ref_match() - .map(|(mutability, relevance)| (Some(mutability), relevance)) - .unwrap_or((None, it.relevance())); - let relevance = display_relevance(relevance); - format!("{} {} {}\n", tag, display_label(it.label(), mutability), relevance) + let relevance = display_relevance(it.relevance()); + items.push(format!("{} {} {}\n", tag, it.label(), relevance)); + + if let Some((mutability, relevance)) = it.ref_match() { + let label = format!("&{}{}", mutability.as_keyword_for_ref(), it.label()); + let relevance = display_relevance(relevance); + + items.push(format!("{} {} {}\n", tag, label, relevance)); + } + + items }) .collect::(); + expect.assert_eq(&actual); } @@ -898,8 +903,8 @@ fn foo(a: A) { B { bar: f(a.$0) }; } "#, expect![[r#" fd foo [type+name] - fd bar [] fd baz [] + fd bar [] "#]], ); check_relevance( @@ -925,9 +930,10 @@ struct WorldSnapshot { _f: () }; fn go(world: &WorldSnapshot) { go(w$0) } "#, expect![[r#" + lc world [type+name] lc &world [type+name] - st WorldSnapshot [] fn go(…) [] + st WorldSnapshot [] "#]], ); } @@ -940,9 +946,9 @@ struct Foo; fn f(foo: &Foo) { f(foo, w$0) } "#, expect![[r#" - st Foo [] - fn f(…) [] lc foo [] + fn f(…) [] + st Foo [] "#]], ); } @@ -1044,13 +1050,14 @@ fn main() { } "#, expect![[r#" + lc t [] lc &t [type] - tt Deref [] - st S [] - st T [] - fn foo(…) [] - lc m [] fn main() [] + lc m [] + fn foo(…) [] + st T [] + st S [] + tt Deref [] "#]], ) } @@ -1097,14 +1104,15 @@ fn main() { } "#, expect![[r#" + lc t [] lc &mut t [type] - tt Deref [] - tt DerefMut [] - st S [] - st T [] - fn foo(…) [] - lc m [] fn main() [] + lc m [] + fn foo(…) [] + st T [] + st S [] + tt DerefMut [] + tt Deref [] "#]], ) } -- cgit v1.2.3 From 8a9ebe62a114ef83d46771b28e483f6b8ea5478e Mon Sep 17 00:00:00 2001 From: ivan770 Date: Sun, 14 Mar 2021 12:25:37 +0200 Subject: Skip ref_match on same types, remove sorting in tests --- crates/ide_completion/src/render.rs | 63 +++++++++++++++---------------------- 1 file changed, 25 insertions(+), 38 deletions(-) (limited to 'crates') diff --git a/crates/ide_completion/src/render.rs b/crates/ide_completion/src/render.rs index ede4aa807..fcb8115fb 100644 --- a/crates/ide_completion/src/render.rs +++ b/crates/ide_completion/src/render.rs @@ -259,15 +259,17 @@ impl<'a> Render<'a> { } if let Some((_expected_name, expected_type)) = self.ctx.expected_name_and_type() { - if let Some(ty_without_ref) = expected_type.remove_ref() { - if relevance_type_match(self.ctx.db().upcast(), &ty, &ty_without_ref) { - cov_mark::hit!(suggest_ref); - let mutability = if expected_type.is_mutable_reference() { - Mutability::Mut - } else { - Mutability::Shared - }; - item.ref_match(mutability); + if ty != expected_type { + if let Some(ty_without_ref) = expected_type.remove_ref() { + if relevance_type_match(self.ctx.db().upcast(), &ty, &ty_without_ref) { + cov_mark::hit!(suggest_ref); + let mutability = if expected_type.is_mutable_reference() { + Mutability::Mut + } else { + Mutability::Shared + }; + item.ref_match(mutability); + } } } } @@ -340,8 +342,6 @@ fn relevance_type_match(db: &dyn HirDatabase, ty: &Type, expected_type: &Type) - #[cfg(test)] mod tests { - use std::cmp::Reverse; - use expect_test::{expect, Expect}; use crate::{ @@ -366,19 +366,7 @@ mod tests { } } - let mut completions = get_all_items(TEST_CONFIG, ra_fixture); - completions.sort_by_key(|it| { - // (Reverse(it.ref_match().map(|m| m.1).unwrap_or(it.relevance())), it.label().to_string()) - if let Some((mutability, relevance)) = it.ref_match() { - let label = format!("&{}{}", mutability.as_keyword_for_ref(), it.label()); - - Reverse((relevance, label)) - } else { - Reverse((it.relevance(), it.label().to_string())) - } - }); - - let actual = completions + let actual = get_all_items(TEST_CONFIG, ra_fixture) .into_iter() .filter(|it| it.completion_kind == CompletionKind::Reference) .flat_map(|it| { @@ -868,9 +856,9 @@ fn test(bar: u32) { } fn foo(s: S) { test(s.$0) } "#, expect![[r#" + fd foo [] fd bar [type+name] fd baz [type] - fd foo [] "#]], ); } @@ -885,9 +873,9 @@ struct B { x: (), y: f32, bar: u32 } fn foo(a: A) { B { bar: a.$0 }; } "#, expect![[r#" + fd foo [] fd bar [type+name] fd baz [type] - fd foo [] "#]], ) } @@ -903,8 +891,8 @@ fn foo(a: A) { B { bar: f(a.$0) }; } "#, expect![[r#" fd foo [type+name] - fd baz [] fd bar [] + fd baz [] "#]], ); check_relevance( @@ -915,9 +903,9 @@ fn f(foo: i64) { } fn foo(a: A) { f(B { bar: a.$0 }); } "#, expect![[r#" + fd foo [] fd bar [type+name] fd baz [type] - fd foo [] "#]], ); } @@ -931,9 +919,8 @@ fn go(world: &WorldSnapshot) { go(w$0) } "#, expect![[r#" lc world [type+name] - lc &world [type+name] - fn go(…) [] st WorldSnapshot [] + fn go(…) [] "#]], ); } @@ -947,8 +934,8 @@ fn f(foo: &Foo) { f(foo, w$0) } "#, expect![[r#" lc foo [] - fn f(…) [] st Foo [] + fn f(…) [] "#]], ); } @@ -1050,14 +1037,14 @@ fn main() { } "#, expect![[r#" + lc m [] lc t [] lc &t [type] - fn main() [] - lc m [] - fn foo(…) [] st T [] st S [] + fn main() [] tt Deref [] + fn foo(…) [] "#]], ) } @@ -1104,15 +1091,15 @@ fn main() { } "#, expect![[r#" + lc m [] lc t [] lc &mut t [type] - fn main() [] - lc m [] + tt DerefMut [] + tt Deref [] fn foo(…) [] st T [] st S [] - tt DerefMut [] - tt Deref [] + fn main() [] "#]], ) } -- cgit v1.2.3