aboutsummaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
authorDavid Lattimore <[email protected]>2020-08-01 03:43:10 +0100
committerDavid Lattimore <[email protected]>2020-08-01 03:55:26 +0100
commit21d2cebcf1a417bce72da98aa638a20235c050db (patch)
tree5b89fe4b74fef8ce71da396d903f18e2a2ea9da2 /crates
parent5af32aeb2b83c7ae8adf3e088bf4f3691aa45eb1 (diff)
SSR: Matching trait associated constants, types and functions
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.
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 6f62000f4..d5b65eaac 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> {
@@ -111,8 +111,10 @@ impl Resolver<'_, '_> {
111 .resolution_scope 111 .resolution_scope
112 .resolve_path(&path) 112 .resolve_path(&path)
113 .ok_or_else(|| error!("Failed to resolve path `{}`", node.text()))?; 113 .ok_or_else(|| error!("Failed to resolve path `{}`", node.text()))?;
114 resolved_paths.insert(node, ResolvedPath { resolution, depth }); 114 if self.ok_to_use_path_resolution(&resolution) {
115 return Ok(()); 115 resolved_paths.insert(node, ResolvedPath { resolution, depth });
116 return Ok(());
117 }
116 } 118 }
117 } 119 }
118 for node in node.children() { 120 for node in node.children() {
@@ -136,6 +138,27 @@ impl Resolver<'_, '_> {
136 } 138 }
137 false 139 false
138 } 140 }
141
142 fn ok_to_use_path_resolution(&self, resolution: &hir::PathResolution) -> bool {
143 match resolution {
144 hir::PathResolution::AssocItem(hir::AssocItem::Function(function)) => {
145 if function.has_self_param(self.resolution_scope.scope.db) {
146 // If we don't use this path resolution, then we won't be able to match method
147 // calls. e.g. `Foo::bar($s)` should match `x.bar()`.
148 true
149 } else {
150 mark::hit!(replace_associated_trait_default_function_call);
151 false
152 }
153 }
154 hir::PathResolution::AssocItem(_) => {
155 // Not a function. Could be a constant or an associated type.
156 mark::hit!(replace_associated_trait_constant);
157 false
158 }
159 _ => true,
160 }
161 }
139} 162}
140 163
141impl<'db> ResolutionScope<'db> { 164impl<'db> ResolutionScope<'db> {
@@ -176,7 +199,7 @@ impl<'db> ResolutionScope<'db> {
176 adt.ty(self.scope.db).iterate_path_candidates( 199 adt.ty(self.scope.db).iterate_path_candidates(
177 self.scope.db, 200 self.scope.db,
178 self.scope.module()?.krate(), 201 self.scope.module()?.krate(),
179 &FxHashSet::default(), 202 &self.scope.traits_in_scope(),
180 Some(hir_path.segments().last()?.name), 203 Some(hir_path.segments().last()?.name),
181 |_ty, assoc_item| Some(hir::PathResolution::AssocItem(assoc_item)), 204 |_ty, assoc_item| Some(hir::PathResolution::AssocItem(assoc_item)),
182 ) 205 )
diff --git a/crates/ra_ssr/src/tests.rs b/crates/ra_ssr/src/tests.rs
index 2ae03c64c..0a49a46e3 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,