diff options
Diffstat (limited to 'crates/ssr/src/matching.rs')
-rw-r--r-- | crates/ssr/src/matching.rs | 95 |
1 files changed, 66 insertions, 29 deletions
diff --git a/crates/ssr/src/matching.rs b/crates/ssr/src/matching.rs index ffc7202ae..8bb5ced90 100644 --- a/crates/ssr/src/matching.rs +++ b/crates/ssr/src/matching.rs | |||
@@ -2,7 +2,7 @@ | |||
2 | //! process of matching, placeholder values are recorded. | 2 | //! process of matching, placeholder values are recorded. |
3 | 3 | ||
4 | use crate::{ | 4 | use crate::{ |
5 | parsing::{Constraint, NodeKind, Placeholder}, | 5 | parsing::{Constraint, NodeKind, Placeholder, Var}, |
6 | resolving::{ResolvedPattern, ResolvedRule, UfcsCallInfo}, | 6 | resolving::{ResolvedPattern, ResolvedRule, UfcsCallInfo}, |
7 | SsrMatches, | 7 | SsrMatches, |
8 | }; | 8 | }; |
@@ -56,10 +56,6 @@ pub struct Match { | |||
56 | pub(crate) rendered_template_paths: FxHashMap<SyntaxNode, hir::ModPath>, | 56 | pub(crate) rendered_template_paths: FxHashMap<SyntaxNode, hir::ModPath>, |
57 | } | 57 | } |
58 | 58 | ||
59 | /// Represents a `$var` in an SSR query. | ||
60 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] | ||
61 | pub(crate) struct Var(pub String); | ||
62 | |||
63 | /// Information about a placeholder bound in a match. | 59 | /// Information about a placeholder bound in a match. |
64 | #[derive(Debug)] | 60 | #[derive(Debug)] |
65 | pub(crate) struct PlaceholderMatch { | 61 | pub(crate) struct PlaceholderMatch { |
@@ -69,6 +65,10 @@ pub(crate) struct PlaceholderMatch { | |||
69 | pub(crate) range: FileRange, | 65 | pub(crate) range: FileRange, |
70 | /// More matches, found within `node`. | 66 | /// More matches, found within `node`. |
71 | 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, | ||
72 | } | 72 | } |
73 | 73 | ||
74 | #[derive(Debug)] | 74 | #[derive(Debug)] |
@@ -173,7 +173,7 @@ impl<'db, 'sema> Matcher<'db, 'sema> { | |||
173 | code: &SyntaxNode, | 173 | code: &SyntaxNode, |
174 | ) -> Result<(), MatchFailed> { | 174 | ) -> Result<(), MatchFailed> { |
175 | // Handle placeholders. | 175 | // Handle placeholders. |
176 | if let Some(placeholder) = self.get_placeholder(&SyntaxElement::Node(pattern.clone())) { | 176 | if let Some(placeholder) = self.get_placeholder_for_node(pattern) { |
177 | for constraint in &placeholder.constraints { | 177 | for constraint in &placeholder.constraints { |
178 | self.check_constraint(constraint, code)?; | 178 | self.check_constraint(constraint, code)?; |
179 | } | 179 | } |
@@ -183,8 +183,8 @@ impl<'db, 'sema> Matcher<'db, 'sema> { | |||
183 | // probably can't fail range validation, but just to be safe... | 183 | // probably can't fail range validation, but just to be safe... |
184 | self.validate_range(&original_range)?; | 184 | self.validate_range(&original_range)?; |
185 | matches_out.placeholder_values.insert( | 185 | matches_out.placeholder_values.insert( |
186 | Var(placeholder.ident.to_string()), | 186 | placeholder.ident.clone(), |
187 | PlaceholderMatch::new(code, original_range), | 187 | PlaceholderMatch::new(Some(code), original_range), |
188 | ); | 188 | ); |
189 | } | 189 | } |
190 | return Ok(()); | 190 | return Ok(()); |
@@ -487,7 +487,7 @@ impl<'db, 'sema> Matcher<'db, 'sema> { | |||
487 | } | 487 | } |
488 | if let Phase::Second(match_out) = phase { | 488 | if let Phase::Second(match_out) = phase { |
489 | match_out.placeholder_values.insert( | 489 | match_out.placeholder_values.insert( |
490 | Var(placeholder.ident.to_string()), | 490 | placeholder.ident.clone(), |
491 | PlaceholderMatch::from_range(FileRange { | 491 | PlaceholderMatch::from_range(FileRange { |
492 | file_id: self.sema.original_range(code).file_id, | 492 | file_id: self.sema.original_range(code).file_id, |
493 | range: first_matched_token | 493 | range: first_matched_token |
@@ -536,18 +536,40 @@ impl<'db, 'sema> Matcher<'db, 'sema> { | |||
536 | if pattern_ufcs.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 | } | ||
544 | // Check arguments. | 539 | // Check arguments. |
545 | let mut pattern_args = pattern_ufcs | 540 | let mut pattern_args = pattern_ufcs |
546 | .call_expr | 541 | .call_expr |
547 | .arg_list() | 542 | .arg_list() |
548 | .ok_or_else(|| match_error!("Pattern function call has no args"))? | 543 | .ok_or_else(|| match_error!("Pattern function call has no args"))? |
549 | .args(); | 544 | .args(); |
550 | 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 | } | ||
551 | let mut code_args = | 573 | let mut code_args = |
552 | 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(); |
553 | loop { | 575 | loop { |
@@ -575,26 +597,35 @@ impl<'db, 'sema> Matcher<'db, 'sema> { | |||
575 | 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()) |
576 | } | 598 | } |
577 | 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. | ||
578 | fn check_expr_type( | 602 | fn check_expr_type( |
579 | &self, | 603 | &self, |
580 | pattern_type: &hir::Type, | 604 | pattern_type: &hir::Type, |
581 | expr: &ast::Expr, | 605 | expr: &ast::Expr, |
582 | ) -> Result<(), MatchFailed> { | 606 | ) -> Result<usize, MatchFailed> { |
583 | use hir::HirDisplay; | 607 | use hir::HirDisplay; |
584 | 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(|| { |
585 | match_error!("Failed to get receiver type for `{}`", expr.syntax().text()) | 609 | match_error!("Failed to get receiver type for `{}`", expr.syntax().text()) |
586 | })?; | 610 | })?; |
587 | if !code_type | 611 | // Temporary needed to make the borrow checker happy. |
612 | let res = code_type | ||
588 | .autoderef(self.sema.db) | 613 | .autoderef(self.sema.db) |
589 | .any(|deref_code_type| *pattern_type == deref_code_type) | 614 | .enumerate() |
590 | { | 615 | .find(|(_, deref_code_type)| pattern_type == deref_code_type) |
591 | fail_match!( | 616 | .map(|(count, _)| count) |
592 | "Pattern type `{}` didn't match code type `{}`", | 617 | .ok_or_else(|| { |
593 | pattern_type.display(self.sema.db), | 618 | match_error!( |
594 | code_type.display(self.sema.db) | 619 | "Pattern type `{}` didn't match code type `{}`", |
595 | ); | 620 | pattern_type.display(self.sema.db), |
596 | } | 621 | code_type.display(self.sema.db) |
597 | 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())) | ||
598 | } | 629 | } |
599 | 630 | ||
600 | fn get_placeholder(&self, element: &SyntaxElement) -> Option<&Placeholder> { | 631 | fn get_placeholder(&self, element: &SyntaxElement) -> Option<&Placeholder> { |
@@ -676,12 +707,18 @@ fn recording_match_fail_reasons() -> bool { | |||
676 | } | 707 | } |
677 | 708 | ||
678 | impl PlaceholderMatch { | 709 | impl PlaceholderMatch { |
679 | fn new(node: &SyntaxNode, range: FileRange) -> Self { | 710 | fn new(node: Option<&SyntaxNode>, range: FileRange) -> Self { |
680 | 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 | } | ||
681 | } | 718 | } |
682 | 719 | ||
683 | fn from_range(range: FileRange) -> Self { | 720 | fn from_range(range: FileRange) -> Self { |
684 | Self { node: None, range, inner_matches: SsrMatches::default() } | 721 | Self::new(None, range) |
685 | } | 722 | } |
686 | } | 723 | } |
687 | 724 | ||