Skip to content

Commit

Permalink
feat: Allow to use meta variable captured outside of rewrite rule ins…
Browse files Browse the repository at this point in the history
…ide the rewriter

fix #1072
  • Loading branch information
HerringtonDarkholme committed May 2, 2024
1 parent 76db03a commit 2deb8c4
Show file tree
Hide file tree
Showing 3 changed files with 55 additions and 27 deletions.
45 changes: 30 additions & 15 deletions crates/config/src/rule_core.rs
Expand Up @@ -205,6 +205,35 @@ impl<L: Language> RuleCore<L> {
}
ret
}

pub(crate) fn do_match<'tree, D: Doc<Lang = L>>(
&self,
node: Node<'tree, D>,
env: &mut Cow<MetaVarEnv<'tree, D>>,
enclosing_env: Option<&MetaVarEnv<'tree, D>>,
) -> Option<Node<'tree, D>> {
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<L: Language> Deref for RuleCore<L> {
type Target = Rule<L>;
Expand Down Expand Up @@ -233,21 +262,7 @@ impl<L: Language> Matcher<L> for RuleCore<L> {
node: Node<'tree, D>,
env: &mut Cow<MetaVarEnv<'tree, D>>,
) -> Option<Node<'tree, D>> {
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<BitSet> {
Expand Down
9 changes: 7 additions & 2 deletions crates/config/src/transform/mod.rs
Expand Up @@ -174,19 +174,22 @@ struct Ctx<'b, 'c, D: Doc> {
lang: &'b D::Lang,
rewriters: GlobalRules<D::Lang>,
env: &'b mut MetaVarEnv<'c, D>,
enclosing_env: &'b MetaVarEnv<'c, D>,
}

pub fn apply_env_transform<D: Doc>(
pub fn apply_env_transform<'c, D: Doc>(
transforms: &HashMap<String, Transformation>,
lang: &D::Lang,
env: &mut MetaVarEnv<D>,
env: &mut MetaVarEnv<'c, D>,
rewriters: GlobalRules<D::Lang>,
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);
Expand All @@ -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)
}
Expand Down Expand Up @@ -296,6 +300,7 @@ mod test {
&TypeScript::Tsx,
nm.get_env_mut(),
Default::default(),
&Default::default(),
);
nm.get_env().clone().into()
}
Expand Down
28 changes: 18 additions & 10 deletions crates/config/src/transform/rewrite.rs
Expand Up @@ -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();
Expand Down Expand Up @@ -80,30 +80,30 @@ type Bytes<D> = [<<D as Doc>::Source as Content>::Underlying];
fn find_and_make_edits<'n, D: Doc>(
nodes: Vec<Node<'n, D>>,
rules: &[&RuleCore<D::Lang>],
env: &MetaVarEnv<'n, D>,
ctx: &Ctx<'_, 'n, D>,
) -> Vec<Edit<D::Source>> {
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<D::Lang>],
env: &MetaVarEnv<'n, D>,
ctx: &Ctx<'_, 'n, D>,
) -> Vec<Edit<D::Source>> {
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
Expand Down Expand Up @@ -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!(
Expand Down Expand Up @@ -270,7 +273,6 @@ mod test {
}

#[test]
#[ignore = "TODO: fix the recursion bug"]
fn test_recursive_rewriters() {
let rewrite = Rewrite {
source: "$A".into(),
Expand Down Expand Up @@ -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);
Expand All @@ -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()));
Expand Down

0 comments on commit 2deb8c4

Please sign in to comment.