From 2deb8c4c0e7857b7f40e5cec4ccd58613c12b79b Mon Sep 17 00:00:00 2001 From: HerringtonDarkholme <2883231+HerringtonDarkholme@users.noreply.github.com> Date: Wed, 1 May 2024 21:34:36 -0700 Subject: [PATCH] feat: Allow to use meta variable captured outside of rewrite rule inside the rewriter fix #1072 --- crates/config/src/rule_core.rs | 45 +++++++++++++++++--------- crates/config/src/transform/mod.rs | 9 ++++-- crates/config/src/transform/rewrite.rs | 28 ++++++++++------ 3 files changed, 55 insertions(+), 27 deletions(-) diff --git a/crates/config/src/rule_core.rs b/crates/config/src/rule_core.rs index 13f2857b3f..ee2378c05b 100644 --- a/crates/config/src/rule_core.rs +++ b/crates/config/src/rule_core.rs @@ -205,6 +205,35 @@ impl RuleCore { } ret } + + pub(crate) fn do_match<'tree, D: Doc>( + &self, + node: Node<'tree, D>, + env: &mut Cow>, + enclosing_env: Option<&MetaVarEnv<'tree, D>>, + ) -> Option> { + if let Some(kinds) = &self.kinds { + if !kinds.contains(node.kind_id().into()) { + return None; + } + } + let ret = self.rule.match_node_with_env(node, env)?; + if !env.to_mut().match_constraints(&self.constraints) { + return None; + } + if let Some(trans) = &self.transform { + let lang = ret.lang(); + let rewriters = self.utils.get_rewriters(); + let env = env.to_mut(); + if let Some(enclosing) = enclosing_env { + apply_env_transform(trans, lang, env, rewriters, enclosing); + } else { + let enclosing = env.clone(); + apply_env_transform(trans, lang, env, rewriters, &enclosing); + }; + } + Some(ret) + } } impl Deref for RuleCore { type Target = Rule; @@ -233,21 +262,7 @@ impl Matcher for RuleCore { node: Node<'tree, D>, env: &mut Cow>, ) -> Option> { - if let Some(kinds) = &self.kinds { - if !kinds.contains(node.kind_id().into()) { - return None; - } - } - let ret = self.rule.match_node_with_env(node, env)?; - if !env.to_mut().match_constraints(&self.constraints) { - return None; - } - if let Some(trans) = &self.transform { - let lang = ret.lang(); - let rewriters = self.utils.get_rewriters(); - apply_env_transform(trans, lang, env.to_mut(), rewriters); - } - Some(ret) + self.do_match(node, env, None) } fn potential_kinds(&self) -> Option { diff --git a/crates/config/src/transform/mod.rs b/crates/config/src/transform/mod.rs index b07a550a7d..817d6a03ff 100644 --- a/crates/config/src/transform/mod.rs +++ b/crates/config/src/transform/mod.rs @@ -174,19 +174,22 @@ struct Ctx<'b, 'c, D: Doc> { lang: &'b D::Lang, rewriters: GlobalRules, env: &'b mut MetaVarEnv<'c, D>, + enclosing_env: &'b MetaVarEnv<'c, D>, } -pub fn apply_env_transform( +pub fn apply_env_transform<'c, D: Doc>( transforms: &HashMap, lang: &D::Lang, - env: &mut MetaVarEnv, + env: &mut MetaVarEnv<'c, D>, rewriters: GlobalRules, + enclosing_env: &MetaVarEnv<'c, D>, ) { let mut ctx = Ctx { transforms, lang, env, rewriters, + enclosing_env, }; for (key, tr) in transforms { tr.insert(key, &mut ctx); @@ -210,6 +213,7 @@ mod test { transforms: &HashMap::new(), env: nm.get_env_mut(), rewriters: Default::default(), + enclosing_env: &Default::default(), }; trans.compute(&mut ctx) } @@ -296,6 +300,7 @@ mod test { &TypeScript::Tsx, nm.get_env_mut(), Default::default(), + &Default::default(), ); nm.get_env().clone().into() } diff --git a/crates/config/src/transform/rewrite.rs b/crates/config/src/transform/rewrite.rs index 169cdc8668..9efff613a6 100644 --- a/crates/config/src/transform/rewrite.rs +++ b/crates/config/src/transform/rewrite.rs @@ -47,7 +47,7 @@ impl Rewrite { .iter() .filter_map(|id| rewriters.get(id)) // TODO: better handling .collect(); - let edits = find_and_make_edits(nodes, &rules, ctx.env); + let edits = find_and_make_edits(nodes, &rules, ctx); let rewritten = if let Some(joiner) = &self.join_by { let mut ret = vec![]; let mut edits = edits.into_iter(); @@ -80,30 +80,30 @@ type Bytes = [<::Source as Content>::Underlying]; fn find_and_make_edits<'n, D: Doc>( nodes: Vec>, rules: &[&RuleCore], - env: &MetaVarEnv<'n, D>, + ctx: &Ctx<'_, 'n, D>, ) -> Vec> { nodes .into_iter() - .flat_map(|n| replace_one(n, rules, env)) + .flat_map(|n| replace_one(n, rules, ctx)) .collect() } fn replace_one<'n, D: Doc>( node: Node<'n, D>, rules: &[&RuleCore], - env: &MetaVarEnv<'n, D>, + ctx: &Ctx<'_, 'n, D>, ) -> Vec> { let mut edits = vec![]; for child in node.dfs() { for rule in rules { - let mut env = std::borrow::Cow::Borrowed(env); + let mut env = std::borrow::Cow::Borrowed(ctx.enclosing_env); // NOTE: we inherit meta_var_env from enclosing rule // but match env will NOT inherited recursively! // e.g. $B is matched in parent linter and it is inherited. // $C is matched in rewriter but is NOT inherited in recursive rewriter // this is to enable recursive rewriter to match sub nodes // in future, we can use the explict `expose` to control env inheritance - if let Some(n) = rule.match_node_with_env(child.clone(), &mut env) { + if let Some(n) = rule.do_match(child.clone(), &mut env, Some(ctx.enclosing_env)) { let nm = NodeMatch::new(n, env.into_owned()); edits.push(nm.make_edit(rule, rule.fixer.as_ref().expect("TODO"))); // stop at first fix, skip duplicate fix @@ -155,11 +155,14 @@ mod test { let root = grep.root(); let mut nm = root.find(pat).expect("should find"); let before_vars: Vec<_> = nm.get_env().get_matched_variables().collect(); + let env = nm.get_env_mut(); + let enclosing = env.clone(); let mut ctx = Ctx { lang: &TypeScript::Tsx, transforms: &Default::default(), - env: nm.get_env_mut(), + env, rewriters, + enclosing_env: &enclosing, }; let after_vars: Vec<_> = ctx.env.get_matched_variables().collect(); assert_eq!( @@ -270,7 +273,6 @@ mod test { } #[test] - #[ignore = "TODO: fix the recursion bug"] fn test_recursive_rewriters() { let rewrite = Rewrite { source: "$A".into(), @@ -319,11 +321,14 @@ fix: $D let grep = TypeScript::Tsx.ast_grep("[1, 2]"); let root = grep.root(); let mut nm = root.find("[$B, $C]").expect("should find"); + let env = nm.get_env_mut(); + let enclosing = env.clone(); let mut ctx = Ctx { lang: &TypeScript::Tsx, transforms: &Default::default(), - env: nm.get_env_mut(), + env, rewriters, + enclosing_env: &enclosing, }; let ret = rewrite.compute(&mut ctx); assert_eq!(ret, None); @@ -347,11 +352,14 @@ fix: $D let grep = TypeScript::Tsx.ast_grep("[1, 2]"); let root = grep.root(); let mut nm = root.find("[$A, $C]").expect("should find"); + let env = nm.get_env_mut(); + let enclosing = env.clone(); let mut ctx = Ctx { lang: &TypeScript::Tsx, transforms: &Default::default(), - env: nm.get_env_mut(), + env, rewriters, + enclosing_env: &enclosing, }; let ret = rewrite.compute(&mut ctx); assert_eq!(ret, Some("1 == 2".into()));