diff options
author | David Lattimore <[email protected]> | 2020-07-24 11:53:48 +0100 |
---|---|---|
committer | David Lattimore <[email protected]> | 2020-07-24 12:34:00 +0100 |
commit | 3dac31fe80b9d7279e87b94615b0d55805e83412 (patch) | |
tree | d172c092d9ca93c182b2d88c30c3086a9ea797b0 /crates/ra_ssr/src/matching.rs | |
parent | 8d09ab86edfc01405fd0045bef82e0642efd5f01 (diff) |
SSR: Allow function calls to match method calls
This differs from how this used to work before I removed it in that:
a) It's only one direction. Function calls in the pattern can match
method calls in the code, but not the other way around.
b) We now check that the function call in the pattern resolves to the
same function as the method call in the code.
The lack of (b) was the reason I felt the need to remove the feature
before.
Diffstat (limited to 'crates/ra_ssr/src/matching.rs')
-rw-r--r-- | crates/ra_ssr/src/matching.rs | 42 |
1 files changed, 40 insertions, 2 deletions
diff --git a/crates/ra_ssr/src/matching.rs b/crates/ra_ssr/src/matching.rs index f3cc60c29..4862622bd 100644 --- a/crates/ra_ssr/src/matching.rs +++ b/crates/ra_ssr/src/matching.rs | |||
@@ -189,10 +189,17 @@ impl<'db, 'sema> Matcher<'db, 'sema> { | |||
189 | } | 189 | } |
190 | return Ok(()); | 190 | return Ok(()); |
191 | } | 191 | } |
192 | // Non-placeholders. | 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) { | ||
194 | if let (Some(pattern), Some(code)) = | ||
195 | (ast::CallExpr::cast(pattern.clone()), ast::MethodCallExpr::cast(code.clone())) | ||
196 | { | ||
197 | return self.attempt_match_ufcs(phase, &pattern, &code, *pattern_function); | ||
198 | } | ||
199 | } | ||
193 | if pattern.kind() != code.kind() { | 200 | if pattern.kind() != code.kind() { |
194 | fail_match!( | 201 | fail_match!( |
195 | "Pattern had a `{}` ({:?}), code had `{}` ({:?})", | 202 | "Pattern had `{}` ({:?}), code had `{}` ({:?})", |
196 | pattern.text(), | 203 | pattern.text(), |
197 | pattern.kind(), | 204 | pattern.kind(), |
198 | code.text(), | 205 | code.text(), |
@@ -514,6 +521,37 @@ impl<'db, 'sema> Matcher<'db, 'sema> { | |||
514 | Ok(()) | 521 | Ok(()) |
515 | } | 522 | } |
516 | 523 | ||
524 | fn attempt_match_ufcs( | ||
525 | &self, | ||
526 | phase: &mut Phase, | ||
527 | pattern: &ast::CallExpr, | ||
528 | code: &ast::MethodCallExpr, | ||
529 | pattern_function: hir::Function, | ||
530 | ) -> Result<(), MatchFailed> { | ||
531 | use ast::ArgListOwner; | ||
532 | let code_resolved_function = self | ||
533 | .sema | ||
534 | .resolve_method_call(code) | ||
535 | .ok_or_else(|| match_error!("Failed to resolve method call"))?; | ||
536 | if pattern_function != code_resolved_function { | ||
537 | fail_match!("Method call resolved to a different function"); | ||
538 | } | ||
539 | // Check arguments. | ||
540 | let mut pattern_args = pattern | ||
541 | .arg_list() | ||
542 | .ok_or_else(|| match_error!("Pattern function call has no args"))? | ||
543 | .args(); | ||
544 | self.attempt_match_opt(phase, pattern_args.next(), code.expr())?; | ||
545 | let mut code_args = | ||
546 | code.arg_list().ok_or_else(|| match_error!("Code method call has no args"))?.args(); | ||
547 | loop { | ||
548 | match (pattern_args.next(), code_args.next()) { | ||
549 | (None, None) => return Ok(()), | ||
550 | (p, c) => self.attempt_match_opt(phase, p, c)?, | ||
551 | } | ||
552 | } | ||
553 | } | ||
554 | |||
517 | fn get_placeholder(&self, element: &SyntaxElement) -> Option<&Placeholder> { | 555 | fn get_placeholder(&self, element: &SyntaxElement) -> Option<&Placeholder> { |
518 | only_ident(element.clone()).and_then(|ident| self.rule.get_placeholder(&ident)) | 556 | only_ident(element.clone()).and_then(|ident| self.rule.get_placeholder(&ident)) |
519 | } | 557 | } |