aboutsummaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
authorbors[bot] <26634292+bors[bot]@users.noreply.github.com>2020-08-12 14:50:34 +0100
committerGitHub <[email protected]>2020-08-12 14:50:34 +0100
commit1e8b2c498a01dc2c92d6008cdc5b611cf3a96b1e (patch)
treee1f4652227443b07f592ba156dad938e11ef2e0b /crates
parent5b8fdfe23100b88e4fd8e210ccf6b852f5c9bf2a (diff)
parent21d2cebcf1a417bce72da98aa638a20235c050db (diff)
Merge #5637
5637: SSR: Matching trait associated constants, types and functions r=matklad a=davidlattimore This fixes matching of things like `HashMap::default()` by resolving `HashMap` instead of `default` (which resolves to `Default::default`). Same for associated constants and types that are part of a trait implementation. However, we still don't support matching calls to trait methods. Co-authored-by: David Lattimore <[email protected]>
Diffstat (limited to 'crates')
-rw-r--r--crates/ra_ide/src/ssr.rs4
-rw-r--r--crates/ra_ssr/src/resolving.rs31
-rw-r--r--crates/ra_ssr/src/tests.rs64
3 files changed, 93 insertions, 6 deletions
diff --git a/crates/ra_ide/src/ssr.rs b/crates/ra_ide/src/ssr.rs
index 4348b43be..8be862fd6 100644
--- a/crates/ra_ide/src/ssr.rs
+++ b/crates/ra_ide/src/ssr.rs
@@ -21,8 +21,8 @@ use ra_ssr::{MatchFinder, SsrError, SsrRule};
21// replacement occurs. For example if our replacement template is `foo::Bar` and we match some 21// replacement occurs. For example if our replacement template is `foo::Bar` and we match some
22// code in the `foo` module, we'll insert just `Bar`. 22// code in the `foo` module, we'll insert just `Bar`.
23// 23//
24// Method calls should generally be written in UFCS form. e.g. `foo::Bar::baz($s, $a)` will match 24// Inherent method calls should generally be written in UFCS form. e.g. `foo::Bar::baz($s, $a)` will
25// `$s.baz($a)`, provided the method call `baz` resolves to the method `foo::Bar::baz`. 25// match `$s.baz($a)`, provided the method call `baz` resolves to the method `foo::Bar::baz`.
26// 26//
27// The scope of the search / replace will be restricted to the current selection if any, otherwise 27// The scope of the search / replace will be restricted to the current selection if any, otherwise
28// it will apply to the whole workspace. 28// it will apply to the whole workspace.
diff --git a/crates/ra_ssr/src/resolving.rs b/crates/ra_ssr/src/resolving.rs
index df60048eb..d53bd46c7 100644
--- a/crates/ra_ssr/src/resolving.rs
+++ b/crates/ra_ssr/src/resolving.rs
@@ -5,7 +5,7 @@ use crate::{parsing, SsrError};
5use parsing::Placeholder; 5use parsing::Placeholder;
6use ra_db::FilePosition; 6use ra_db::FilePosition;
7use ra_syntax::{ast, SmolStr, SyntaxKind, SyntaxNode, SyntaxToken}; 7use ra_syntax::{ast, SmolStr, SyntaxKind, SyntaxNode, SyntaxToken};
8use rustc_hash::{FxHashMap, FxHashSet}; 8use rustc_hash::FxHashMap;
9use test_utils::mark; 9use test_utils::mark;
10 10
11pub(crate) struct ResolutionScope<'db> { 11pub(crate) struct ResolutionScope<'db> {
@@ -124,8 +124,10 @@ impl Resolver<'_, '_> {
124 .resolution_scope 124 .resolution_scope
125 .resolve_path(&path) 125 .resolve_path(&path)
126 .ok_or_else(|| error!("Failed to resolve path `{}`", node.text()))?; 126 .ok_or_else(|| error!("Failed to resolve path `{}`", node.text()))?;
127 resolved_paths.insert(node, ResolvedPath { resolution, depth }); 127 if self.ok_to_use_path_resolution(&resolution) {
128 return Ok(()); 128 resolved_paths.insert(node, ResolvedPath { resolution, depth });
129 return Ok(());
130 }
129 } 131 }
130 } 132 }
131 for node in node.children() { 133 for node in node.children() {
@@ -149,6 +151,27 @@ impl Resolver<'_, '_> {
149 } 151 }
150 false 152 false
151 } 153 }
154
155 fn ok_to_use_path_resolution(&self, resolution: &hir::PathResolution) -> bool {
156 match resolution {
157 hir::PathResolution::AssocItem(hir::AssocItem::Function(function)) => {
158 if function.has_self_param(self.resolution_scope.scope.db) {
159 // If we don't use this path resolution, then we won't be able to match method
160 // calls. e.g. `Foo::bar($s)` should match `x.bar()`.
161 true
162 } else {
163 mark::hit!(replace_associated_trait_default_function_call);
164 false
165 }
166 }
167 hir::PathResolution::AssocItem(_) => {
168 // Not a function. Could be a constant or an associated type.
169 mark::hit!(replace_associated_trait_constant);
170 false
171 }
172 _ => true,
173 }
174 }
152} 175}
153 176
154impl<'db> ResolutionScope<'db> { 177impl<'db> ResolutionScope<'db> {
@@ -195,7 +218,7 @@ impl<'db> ResolutionScope<'db> {
195 adt.ty(self.scope.db).iterate_path_candidates( 218 adt.ty(self.scope.db).iterate_path_candidates(
196 self.scope.db, 219 self.scope.db,
197 self.scope.module()?.krate(), 220 self.scope.module()?.krate(),
198 &FxHashSet::default(), 221 &self.scope.traits_in_scope(),
199 Some(hir_path.segments().last()?.name), 222 Some(hir_path.segments().last()?.name),
200 |_ty, assoc_item| Some(hir::PathResolution::AssocItem(assoc_item)), 223 |_ty, assoc_item| Some(hir::PathResolution::AssocItem(assoc_item)),
201 ) 224 )
diff --git a/crates/ra_ssr/src/tests.rs b/crates/ra_ssr/src/tests.rs
index d483640df..7d4d470c0 100644
--- a/crates/ra_ssr/src/tests.rs
+++ b/crates/ra_ssr/src/tests.rs
@@ -550,6 +550,70 @@ fn replace_associated_function_call() {
550} 550}
551 551
552#[test] 552#[test]
553fn replace_associated_trait_default_function_call() {
554 mark::check!(replace_associated_trait_default_function_call);
555 assert_ssr_transform(
556 "Bar2::foo() ==>> Bar2::foo2()",
557 r#"
558 trait Foo { fn foo() {} }
559 pub struct Bar {}
560 impl Foo for Bar {}
561 pub struct Bar2 {}
562 impl Foo for Bar2 {}
563 impl Bar2 { fn foo2() {} }
564 fn main() {
565 Bar::foo();
566 Bar2::foo();
567 }
568 "#,
569 expect![[r#"
570 trait Foo { fn foo() {} }
571 pub struct Bar {}
572 impl Foo for Bar {}
573 pub struct Bar2 {}
574 impl Foo for Bar2 {}
575 impl Bar2 { fn foo2() {} }
576 fn main() {
577 Bar::foo();
578 Bar2::foo2();
579 }
580 "#]],
581 );
582}
583
584#[test]
585fn replace_associated_trait_constant() {
586 mark::check!(replace_associated_trait_constant);
587 assert_ssr_transform(
588 "Bar2::VALUE ==>> Bar2::VALUE_2222",
589 r#"
590 trait Foo { const VALUE: i32; const VALUE_2222: i32; }
591 pub struct Bar {}
592 impl Foo for Bar { const VALUE: i32 = 1; const VALUE_2222: i32 = 2; }
593 pub struct Bar2 {}
594 impl Foo for Bar2 { const VALUE: i32 = 1; const VALUE_2222: i32 = 2; }
595 impl Bar2 { fn foo2() {} }
596 fn main() {
597 Bar::VALUE;
598 Bar2::VALUE;
599 }
600 "#,
601 expect![[r#"
602 trait Foo { const VALUE: i32; const VALUE_2222: i32; }
603 pub struct Bar {}
604 impl Foo for Bar { const VALUE: i32 = 1; const VALUE_2222: i32 = 2; }
605 pub struct Bar2 {}
606 impl Foo for Bar2 { const VALUE: i32 = 1; const VALUE_2222: i32 = 2; }
607 impl Bar2 { fn foo2() {} }
608 fn main() {
609 Bar::VALUE;
610 Bar2::VALUE_2222;
611 }
612 "#]],
613 );
614}
615
616#[test]
553fn replace_path_in_different_contexts() { 617fn replace_path_in_different_contexts() {
554 // Note the <|> inside module a::b which marks the point where the rule is interpreted. We 618 // Note the <|> inside module a::b which marks the point where the rule is interpreted. We
555 // replace foo with bar, but both need different path qualifiers in different contexts. In f4, 619 // replace foo with bar, but both need different path qualifiers in different contexts. In f4,