Skip to content

Commit

Permalink
ssr: improve matching for PatternMap iterator
Browse files Browse the repository at this point in the history
Summary:
For SSR matching within a map, we use a `PatternMap` structure, which maps first the name of the map in order, then the fields, allowing them to be in any order.

The matching algorithm partitions the field matches into those the have keys that are placeholders, which can match any key in the target map, and those that are not which have a precise match.

It first matches the non-placeholders, then arbitrarily assigns the placeholder matches to the remaining fields in the target.

Unfortunately this last step had been left out, this diff adds it in.

Reviewed By: TD5

Differential Revision: D69058907

fbshipit-source-id: 377c2fd907547b5d7a522cecbe0ed22ce2423753
  • Loading branch information
alanz authored and facebook-github-bot committed Feb 4, 2025
1 parent 0e7598e commit d73b355
Show file tree
Hide file tree
Showing 2 changed files with 39 additions and 5 deletions.
32 changes: 27 additions & 5 deletions crates/ide_ssr/src/matching.rs
Original file line number Diff line number Diff line change
Expand Up @@ -537,12 +537,13 @@ impl<'a> Matcher<'a> {
// What do we do if we have more than one possible match?
// e.g. two placeholder field names, with placeholder RHS?
// Pathological, ignore for now.

let non_placeholders: Vec<(&SubId, &Vec<SubId>)> = pattern_it
let (placeholders, non_placeholders): (
Vec<(&SubId, &Vec<SubId>)>,
Vec<(&SubId, &Vec<SubId>)>,
) = pattern_it
.children
.iter()
.filter(|(k, _v)| !self.is_placeholder(k))
.collect();
.partition(|(k, _v)| self.is_placeholder_expr(k));

// We are likely to have best results matching in order
// (heuristic). So pulling something out of the set is not
Expand All @@ -554,7 +555,17 @@ impl<'a> Matcher<'a> {
if let Ok(index) = self.attempt_match_vec(phase, &p, &code_keys) {
code_keys.remove(index);
} else {
fail_match!("no match in child vector");
fail_match!("no non-placeholder match in child vector");
};
}
// We have dealt with the non-placeholders, which give an explicit match.
// Now we assign the placeholders in the order they appear,
// with the fields in the order they appear.
for p in placeholders {
if let Ok(index) = self.attempt_match_vec(phase, &p, &code_keys) {
code_keys.remove(index);
} else {
fail_match!("no placeholder match in child vector");
};
}
if !code_keys.is_empty() {
Expand Down Expand Up @@ -747,6 +758,17 @@ impl<'a> Matcher<'a> {
|| pattern_str == "TypeExpr::SsrPlaceholder"
|| pattern_str == "Term::SsrPlaceholder"
}

fn is_placeholder_expr(&self, id: &SubId) -> bool {
match id {
SubId::AnyExprId(any_expr_id) => match self.pattern_body.get_any(*any_expr_id) {
AnyExprRef::Expr(Expr::SsrPlaceholder(_)) => true,
AnyExprRef::Pat(Pat::SsrPlaceholder(_)) => true,
_ => false,
},
_ => false,
}
}
}

fn render_str(sema: &Semantic, lit: &Literal) -> String {
Expand Down
12 changes: 12 additions & 0 deletions crates/ide_ssr/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -544,6 +544,7 @@ fn ssr_expr_match_map() {
"bar() -> XX = 1, #{another => 3, field => XX}.",
&["#{another => 3, field => XX}"],
);
assert_matches("ssr: #{ }.", "bar() -> #{}.", &["#{}"]);
}

#[test]
Expand Down Expand Up @@ -1270,3 +1271,14 @@ fn ssr_do_not_match_pattern_missing() {
&[],
);
}

#[test]
fn ssr_complex_match() {
assert_matches(
"ssr: lists:foldl(fun({_@Key,_@Value}, _@Acc) -> _@Acc#{_@Key => _@Value} end, #{}, _@List).",
r#"
fn(List) -> lists:foldl(fun({K,V}, Acc) -> Acc#{K => V} end, #{}, List).
"#,
&["lists:foldl(fun({K,V}, Acc) -> Acc#{K => V} end, #{}, List)"],
);
}

0 comments on commit d73b355

Please sign in to comment.