aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_ssr/src/matching.rs
diff options
context:
space:
mode:
authorDavid Lattimore <[email protected]>2020-08-05 10:48:52 +0100
committerDavid Lattimore <[email protected]>2020-08-13 11:24:55 +0100
commit3100de842b3cc33c9ad364f10c7f740ac760f564 (patch)
tree7713e2aea0b47ec8141fcefba101871137137c09 /crates/ra_ssr/src/matching.rs
parentde1d93455f85747410efb69c28e0c1379e8e328a (diff)
Structured search replace now handles UFCS calls to trait methods
Diffstat (limited to 'crates/ra_ssr/src/matching.rs')
-rw-r--r--crates/ra_ssr/src/matching.rs67
1 files changed, 56 insertions, 11 deletions
diff --git a/crates/ra_ssr/src/matching.rs b/crates/ra_ssr/src/matching.rs
index 125bf3895..6e0b92352 100644
--- a/crates/ra_ssr/src/matching.rs
+++ b/crates/ra_ssr/src/matching.rs
@@ -3,7 +3,7 @@
3 3
4use crate::{ 4use crate::{
5 parsing::{Constraint, NodeKind, Placeholder}, 5 parsing::{Constraint, NodeKind, Placeholder},
6 resolving::{ResolvedPattern, ResolvedRule}, 6 resolving::{ResolvedPattern, ResolvedRule, UfcsCallInfo},
7 SsrMatches, 7 SsrMatches,
8}; 8};
9use hir::Semantics; 9use hir::Semantics;
@@ -190,11 +190,12 @@ impl<'db, 'sema> Matcher<'db, 'sema> {
190 return Ok(()); 190 return Ok(());
191 } 191 }
192 // We allow a UFCS call to match a method call, provided they resolve to the same function. 192 // We allow a UFCS call to match a method call, provided they resolve to the same function.
193 if let Some(pattern_function) = self.rule.pattern.ufcs_function_calls.get(pattern) { 193 if let Some(pattern_ufcs) = self.rule.pattern.ufcs_function_calls.get(pattern) {
194 if let (Some(pattern), Some(code)) = 194 if let Some(code) = ast::MethodCallExpr::cast(code.clone()) {
195 (ast::CallExpr::cast(pattern.clone()), ast::MethodCallExpr::cast(code.clone())) 195 return self.attempt_match_ufcs_to_method_call(phase, pattern_ufcs, &code);
196 { 196 }
197 return self.attempt_match_ufcs(phase, &pattern, &code, *pattern_function); 197 if let Some(code) = ast::CallExpr::cast(code.clone()) {
198 return self.attempt_match_ufcs_to_ufcs(phase, pattern_ufcs, &code);
198 } 199 }
199 } 200 }
200 if pattern.kind() != code.kind() { 201 if pattern.kind() != code.kind() {
@@ -521,23 +522,28 @@ impl<'db, 'sema> Matcher<'db, 'sema> {
521 Ok(()) 522 Ok(())
522 } 523 }
523 524
524 fn attempt_match_ufcs( 525 fn attempt_match_ufcs_to_method_call(
525 &self, 526 &self,
526 phase: &mut Phase, 527 phase: &mut Phase,
527 pattern: &ast::CallExpr, 528 pattern_ufcs: &UfcsCallInfo,
528 code: &ast::MethodCallExpr, 529 code: &ast::MethodCallExpr,
529 pattern_function: hir::Function,
530 ) -> Result<(), MatchFailed> { 530 ) -> Result<(), MatchFailed> {
531 use ast::ArgListOwner; 531 use ast::ArgListOwner;
532 let code_resolved_function = self 532 let code_resolved_function = self
533 .sema 533 .sema
534 .resolve_method_call(code) 534 .resolve_method_call(code)
535 .ok_or_else(|| match_error!("Failed to resolve method call"))?; 535 .ok_or_else(|| match_error!("Failed to resolve method call"))?;
536 if pattern_function != code_resolved_function { 536 if pattern_ufcs.function != code_resolved_function {
537 fail_match!("Method call resolved to a different function"); 537 fail_match!("Method call resolved to a different function");
538 } 538 }
539 if code_resolved_function.has_self_param(self.sema.db) {
540 if let (Some(pattern_type), Some(expr)) = (&pattern_ufcs.qualifier_type, &code.expr()) {
541 self.check_expr_type(pattern_type, expr)?;
542 }
543 }
539 // Check arguments. 544 // Check arguments.
540 let mut pattern_args = pattern 545 let mut pattern_args = pattern_ufcs
546 .call_expr
541 .arg_list() 547 .arg_list()
542 .ok_or_else(|| match_error!("Pattern function call has no args"))? 548 .ok_or_else(|| match_error!("Pattern function call has no args"))?
543 .args(); 549 .args();
@@ -552,6 +558,45 @@ impl<'db, 'sema> Matcher<'db, 'sema> {
552 } 558 }
553 } 559 }
554 560
561 fn attempt_match_ufcs_to_ufcs(
562 &self,
563 phase: &mut Phase,
564 pattern_ufcs: &UfcsCallInfo,
565 code: &ast::CallExpr,
566 ) -> Result<(), MatchFailed> {
567 use ast::ArgListOwner;
568 // Check that the first argument is the expected type.
569 if let (Some(pattern_type), Some(expr)) = (
570 &pattern_ufcs.qualifier_type,
571 &code.arg_list().and_then(|code_args| code_args.args().next()),
572 ) {
573 self.check_expr_type(pattern_type, expr)?;
574 }
575 self.attempt_match_node_children(phase, pattern_ufcs.call_expr.syntax(), code.syntax())
576 }
577
578 fn check_expr_type(
579 &self,
580 pattern_type: &hir::Type,
581 expr: &ast::Expr,
582 ) -> Result<(), MatchFailed> {
583 use hir::HirDisplay;
584 let code_type = self.sema.type_of_expr(&expr).ok_or_else(|| {
585 match_error!("Failed to get receiver type for `{}`", expr.syntax().text())
586 })?;
587 if !code_type
588 .autoderef(self.sema.db)
589 .any(|deref_code_type| *pattern_type == deref_code_type)
590 {
591 fail_match!(
592 "Pattern type `{}` didn't match code type `{}`",
593 pattern_type.display(self.sema.db),
594 code_type.display(self.sema.db)
595 );
596 }
597 Ok(())
598 }
599
555 fn get_placeholder(&self, element: &SyntaxElement) -> Option<&Placeholder> { 600 fn get_placeholder(&self, element: &SyntaxElement) -> Option<&Placeholder> {
556 only_ident(element.clone()).and_then(|ident| self.rule.get_placeholder(&ident)) 601 only_ident(element.clone()).and_then(|ident| self.rule.get_placeholder(&ident))
557 } 602 }