diff options
author | bors[bot] <26634292+bors[bot]@users.noreply.github.com> | 2020-08-13 15:21:35 +0100 |
---|---|---|
committer | GitHub <[email protected]> | 2020-08-13 15:21:35 +0100 |
commit | 902f74c2697cc2a50de9067845814a2a852fccfd (patch) | |
tree | 24c600ea6f47b9ed47964dab244eee696c712a8f /crates/ra_ssr/src/matching.rs | |
parent | c2b1503fe41e6912b45d9ccf15e3c5ae6aad9439 (diff) | |
parent | 3100de842b3cc33c9ad364f10c7f740ac760f564 (diff) |
Merge #5746
5746: Structured search replace now handles UFCS calls to trait methods r=matklad a=davidlattimore
Co-authored-by: David Lattimore <[email protected]>
Diffstat (limited to 'crates/ra_ssr/src/matching.rs')
-rw-r--r-- | crates/ra_ssr/src/matching.rs | 67 |
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 | ||
4 | use crate::{ | 4 | use 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 | }; |
9 | use hir::Semantics; | 9 | use 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 | } |