diff options
Diffstat (limited to 'crates/ssr/src/matching.rs')
-rw-r--r-- | crates/ssr/src/matching.rs | 90 |
1 files changed, 66 insertions, 24 deletions
diff --git a/crates/ssr/src/matching.rs b/crates/ssr/src/matching.rs index 7f0b5061e..8bb5ced90 100644 --- a/crates/ssr/src/matching.rs +++ b/crates/ssr/src/matching.rs | |||
@@ -65,6 +65,10 @@ pub(crate) struct PlaceholderMatch { | |||
65 | pub(crate) range: FileRange, | 65 | pub(crate) range: FileRange, |
66 | /// More matches, found within `node`. | 66 | /// More matches, found within `node`. |
67 | pub(crate) inner_matches: SsrMatches, | 67 | pub(crate) inner_matches: SsrMatches, |
68 | /// How many times the code that the placeholder matched needed to be dereferenced. Will only be | ||
69 | /// non-zero if the placeholder matched to the receiver of a method call. | ||
70 | pub(crate) autoderef_count: usize, | ||
71 | pub(crate) autoref_kind: ast::SelfParamKind, | ||
68 | } | 72 | } |
69 | 73 | ||
70 | #[derive(Debug)] | 74 | #[derive(Debug)] |
@@ -169,7 +173,7 @@ impl<'db, 'sema> Matcher<'db, 'sema> { | |||
169 | code: &SyntaxNode, | 173 | code: &SyntaxNode, |
170 | ) -> Result<(), MatchFailed> { | 174 | ) -> Result<(), MatchFailed> { |
171 | // Handle placeholders. | 175 | // Handle placeholders. |
172 | if let Some(placeholder) = self.get_placeholder(&SyntaxElement::Node(pattern.clone())) { | 176 | if let Some(placeholder) = self.get_placeholder_for_node(pattern) { |
173 | for constraint in &placeholder.constraints { | 177 | for constraint in &placeholder.constraints { |
174 | self.check_constraint(constraint, code)?; | 178 | self.check_constraint(constraint, code)?; |
175 | } | 179 | } |
@@ -178,9 +182,10 @@ impl<'db, 'sema> Matcher<'db, 'sema> { | |||
178 | // We validated the range for the node when we started the match, so the placeholder | 182 | // We validated the range for the node when we started the match, so the placeholder |
179 | // probably can't fail range validation, but just to be safe... | 183 | // probably can't fail range validation, but just to be safe... |
180 | self.validate_range(&original_range)?; | 184 | self.validate_range(&original_range)?; |
181 | matches_out | 185 | matches_out.placeholder_values.insert( |
182 | .placeholder_values | 186 | placeholder.ident.clone(), |
183 | .insert(placeholder.ident.clone(), PlaceholderMatch::new(code, original_range)); | 187 | PlaceholderMatch::new(Some(code), original_range), |
188 | ); | ||
184 | } | 189 | } |
185 | return Ok(()); | 190 | return Ok(()); |
186 | } | 191 | } |
@@ -531,18 +536,40 @@ impl<'db, 'sema> Matcher<'db, 'sema> { | |||
531 | if pattern_ufcs.function != code_resolved_function { | 536 | if pattern_ufcs.function != code_resolved_function { |
532 | fail_match!("Method call resolved to a different function"); | 537 | fail_match!("Method call resolved to a different function"); |
533 | } | 538 | } |
534 | if code_resolved_function.has_self_param(self.sema.db) { | ||
535 | if let (Some(pattern_type), Some(expr)) = (&pattern_ufcs.qualifier_type, &code.expr()) { | ||
536 | self.check_expr_type(pattern_type, expr)?; | ||
537 | } | ||
538 | } | ||
539 | // Check arguments. | 539 | // Check arguments. |
540 | let mut pattern_args = pattern_ufcs | 540 | let mut pattern_args = pattern_ufcs |
541 | .call_expr | 541 | .call_expr |
542 | .arg_list() | 542 | .arg_list() |
543 | .ok_or_else(|| match_error!("Pattern function call has no args"))? | 543 | .ok_or_else(|| match_error!("Pattern function call has no args"))? |
544 | .args(); | 544 | .args(); |
545 | self.attempt_match_opt(phase, pattern_args.next(), code.expr())?; | 545 | // If the function we're calling takes a self parameter, then we store additional |
546 | // information on the placeholder match about autoderef and autoref. This allows us to use | ||
547 | // the placeholder in a context where autoderef and autoref don't apply. | ||
548 | if code_resolved_function.has_self_param(self.sema.db) { | ||
549 | if let (Some(pattern_type), Some(expr)) = (&pattern_ufcs.qualifier_type, &code.expr()) { | ||
550 | let deref_count = self.check_expr_type(pattern_type, expr)?; | ||
551 | let pattern_receiver = pattern_args.next(); | ||
552 | self.attempt_match_opt(phase, pattern_receiver.clone(), code.expr())?; | ||
553 | if let Phase::Second(match_out) = phase { | ||
554 | if let Some(placeholder_value) = pattern_receiver | ||
555 | .and_then(|n| self.get_placeholder_for_node(n.syntax())) | ||
556 | .and_then(|placeholder| { | ||
557 | match_out.placeholder_values.get_mut(&placeholder.ident) | ||
558 | }) | ||
559 | { | ||
560 | placeholder_value.autoderef_count = deref_count; | ||
561 | placeholder_value.autoref_kind = self | ||
562 | .sema | ||
563 | .resolve_method_call_as_callable(code) | ||
564 | .and_then(|callable| callable.receiver_param(self.sema.db)) | ||
565 | .map(|self_param| self_param.kind()) | ||
566 | .unwrap_or(ast::SelfParamKind::Owned); | ||
567 | } | ||
568 | } | ||
569 | } | ||
570 | } else { | ||
571 | self.attempt_match_opt(phase, pattern_args.next(), code.expr())?; | ||
572 | } | ||
546 | let mut code_args = | 573 | let mut code_args = |
547 | code.arg_list().ok_or_else(|| match_error!("Code method call has no args"))?.args(); | 574 | code.arg_list().ok_or_else(|| match_error!("Code method call has no args"))?.args(); |
548 | loop { | 575 | loop { |
@@ -570,26 +597,35 @@ impl<'db, 'sema> Matcher<'db, 'sema> { | |||
570 | self.attempt_match_node_children(phase, pattern_ufcs.call_expr.syntax(), code.syntax()) | 597 | self.attempt_match_node_children(phase, pattern_ufcs.call_expr.syntax(), code.syntax()) |
571 | } | 598 | } |
572 | 599 | ||
600 | /// Verifies that `expr` matches `pattern_type`, possibly after dereferencing some number of | ||
601 | /// times. Returns the number of times it needed to be dereferenced. | ||
573 | fn check_expr_type( | 602 | fn check_expr_type( |
574 | &self, | 603 | &self, |
575 | pattern_type: &hir::Type, | 604 | pattern_type: &hir::Type, |
576 | expr: &ast::Expr, | 605 | expr: &ast::Expr, |
577 | ) -> Result<(), MatchFailed> { | 606 | ) -> Result<usize, MatchFailed> { |
578 | use hir::HirDisplay; | 607 | use hir::HirDisplay; |
579 | let code_type = self.sema.type_of_expr(&expr).ok_or_else(|| { | 608 | let code_type = self.sema.type_of_expr(&expr).ok_or_else(|| { |
580 | match_error!("Failed to get receiver type for `{}`", expr.syntax().text()) | 609 | match_error!("Failed to get receiver type for `{}`", expr.syntax().text()) |
581 | })?; | 610 | })?; |
582 | if !code_type | 611 | // Temporary needed to make the borrow checker happy. |
612 | let res = code_type | ||
583 | .autoderef(self.sema.db) | 613 | .autoderef(self.sema.db) |
584 | .any(|deref_code_type| *pattern_type == deref_code_type) | 614 | .enumerate() |
585 | { | 615 | .find(|(_, deref_code_type)| pattern_type == deref_code_type) |
586 | fail_match!( | 616 | .map(|(count, _)| count) |
587 | "Pattern type `{}` didn't match code type `{}`", | 617 | .ok_or_else(|| { |
588 | pattern_type.display(self.sema.db), | 618 | match_error!( |
589 | code_type.display(self.sema.db) | 619 | "Pattern type `{}` didn't match code type `{}`", |
590 | ); | 620 | pattern_type.display(self.sema.db), |
591 | } | 621 | code_type.display(self.sema.db) |
592 | Ok(()) | 622 | ) |
623 | }); | ||
624 | res | ||
625 | } | ||
626 | |||
627 | fn get_placeholder_for_node(&self, node: &SyntaxNode) -> Option<&Placeholder> { | ||
628 | self.get_placeholder(&SyntaxElement::Node(node.clone())) | ||
593 | } | 629 | } |
594 | 630 | ||
595 | fn get_placeholder(&self, element: &SyntaxElement) -> Option<&Placeholder> { | 631 | fn get_placeholder(&self, element: &SyntaxElement) -> Option<&Placeholder> { |
@@ -671,12 +707,18 @@ fn recording_match_fail_reasons() -> bool { | |||
671 | } | 707 | } |
672 | 708 | ||
673 | impl PlaceholderMatch { | 709 | impl PlaceholderMatch { |
674 | fn new(node: &SyntaxNode, range: FileRange) -> Self { | 710 | fn new(node: Option<&SyntaxNode>, range: FileRange) -> Self { |
675 | Self { node: Some(node.clone()), range, inner_matches: SsrMatches::default() } | 711 | Self { |
712 | node: node.cloned(), | ||
713 | range, | ||
714 | inner_matches: SsrMatches::default(), | ||
715 | autoderef_count: 0, | ||
716 | autoref_kind: ast::SelfParamKind::Owned, | ||
717 | } | ||
676 | } | 718 | } |
677 | 719 | ||
678 | fn from_range(range: FileRange) -> Self { | 720 | fn from_range(range: FileRange) -> Self { |
679 | Self { node: None, range, inner_matches: SsrMatches::default() } | 721 | Self::new(None, range) |
680 | } | 722 | } |
681 | } | 723 | } |
682 | 724 | ||