mirror of
https://github.com/github/codeql.git
synced 2026-05-14 19:29:28 +02:00
Yeast: Share FreshScope across rule applications
The fresh identifier counter is now shared across all rule applications within a single Runner::run call. Each rule application gets a fresh "scope" (resolved names are cleared) but the counter keeps incrementing. This ensures that $tmp in different rules produces distinct names: the for-rule gets $tmp-0, the assignment rule gets $tmp-1. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
@@ -623,9 +623,9 @@ pub fn parse_rule_top(input: TokenStream) -> Result<TokenStream> {
|
||||
Ok(quote! {
|
||||
{
|
||||
let __query = #query_code;
|
||||
yeast::Rule::new(__query, Box::new(|__ast: &mut yeast::Ast, __captures: yeast::captures::Captures| {
|
||||
yeast::Rule::new(__query, Box::new(|__ast: &mut yeast::Ast, __captures: yeast::captures::Captures, __fresh: &yeast::tree_builder::FreshScope| {
|
||||
#(#bindings)*
|
||||
let mut #ctx_ident = yeast::build::BuildCtx::new(__ast, &__captures);
|
||||
let mut #ctx_ident = yeast::build::BuildCtx::new(__ast, &__captures, __fresh);
|
||||
#transform_body
|
||||
}))
|
||||
}
|
||||
|
||||
@@ -12,15 +12,15 @@ use crate::{Ast, Id, NodeContent, CHILD_FIELD};
|
||||
pub struct BuildCtx<'a> {
|
||||
pub ast: &'a mut Ast,
|
||||
pub captures: &'a Captures,
|
||||
pub fresh: FreshScope,
|
||||
pub fresh: &'a FreshScope,
|
||||
}
|
||||
|
||||
impl<'a> BuildCtx<'a> {
|
||||
pub fn new(ast: &'a mut Ast, captures: &'a Captures) -> Self {
|
||||
pub fn new(ast: &'a mut Ast, captures: &'a Captures, fresh: &'a FreshScope) -> Self {
|
||||
Self {
|
||||
ast,
|
||||
captures,
|
||||
fresh: FreshScope::new(),
|
||||
fresh,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -500,32 +500,33 @@ impl From<tree_sitter::Range> for NodeContent {
|
||||
|
||||
pub struct Rule {
|
||||
query: QueryNode,
|
||||
transform: Box<dyn Fn(&mut Ast, Captures) -> Vec<Id>>,
|
||||
transform: Box<dyn Fn(&mut Ast, Captures, &tree_builder::FreshScope) -> Vec<Id>>,
|
||||
}
|
||||
|
||||
impl Rule {
|
||||
pub fn new(query: QueryNode, transform: Box<dyn Fn(&mut Ast, Captures) -> Vec<Id>>) -> Self {
|
||||
pub fn new(query: QueryNode, transform: Box<dyn Fn(&mut Ast, Captures, &tree_builder::FreshScope) -> Vec<Id>>) -> Self {
|
||||
Self { query, transform }
|
||||
}
|
||||
|
||||
fn try_rule(&self, ast: &mut Ast, node: Id) -> Option<Vec<Id>> {
|
||||
fn try_rule(&self, ast: &mut Ast, node: Id, fresh: &tree_builder::FreshScope) -> Option<Vec<Id>> {
|
||||
let mut captures = Captures::new();
|
||||
if self.query.do_match(ast, node, &mut captures).unwrap() {
|
||||
Some((self.transform)(ast, captures))
|
||||
fresh.next_scope();
|
||||
Some((self.transform)(ast, captures, fresh))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn apply_rules(rules: &Vec<Rule>, ast: &mut Ast, id: Id) -> Vec<Id> {
|
||||
fn apply_rules(rules: &Vec<Rule>, ast: &mut Ast, id: Id, fresh: &tree_builder::FreshScope) -> Vec<Id> {
|
||||
// apply the transformation rules on this node
|
||||
for rule in rules {
|
||||
if let Some(result_node) = rule.try_rule(ast, id) {
|
||||
if let Some(result_node) = rule.try_rule(ast, id, fresh) {
|
||||
// We transformed it so now recurse into the result
|
||||
return result_node
|
||||
.iter()
|
||||
.flat_map(|node| apply_rules(rules, ast, *node))
|
||||
.flat_map(|node| apply_rules(rules, ast, *node, fresh))
|
||||
.collect();
|
||||
}
|
||||
}
|
||||
@@ -539,7 +540,7 @@ fn apply_rules(rules: &Vec<Rule>, ast: &mut Ast, id: Id) -> Vec<Id> {
|
||||
mem::swap(vec, &mut old);
|
||||
*vec = old
|
||||
.iter()
|
||||
.flat_map(|node| apply_rules(rules, ast, *node))
|
||||
.flat_map(|node| apply_rules(rules, ast, *node, fresh))
|
||||
.collect();
|
||||
}
|
||||
|
||||
@@ -559,8 +560,9 @@ impl Runner {
|
||||
}
|
||||
|
||||
pub fn run_from_tree(&self, tree: &tree_sitter::Tree) -> Ast {
|
||||
let fresh = tree_builder::FreshScope::new();
|
||||
let mut ast = Ast::from_tree(self.language.clone(), tree);
|
||||
let res = apply_rules(&self.rules, &mut ast, 0);
|
||||
let res = apply_rules(&self.rules, &mut ast, 0, &fresh);
|
||||
if res.len() != 1 {
|
||||
panic!("Expected at exactly one result node, got {}", res.len());
|
||||
}
|
||||
@@ -569,13 +571,12 @@ impl Runner {
|
||||
}
|
||||
|
||||
pub fn run(&self, input: &str) -> Ast {
|
||||
// Parse the input into an AST
|
||||
|
||||
let fresh = tree_builder::FreshScope::new();
|
||||
let mut parser = tree_sitter::Parser::new();
|
||||
parser.set_language(&self.language).unwrap();
|
||||
let tree = parser.parse(input, None).unwrap();
|
||||
let mut ast = Ast::from_tree(self.language.clone(), &tree);
|
||||
let res = apply_rules(&self.rules, &mut ast, 0);
|
||||
let res = apply_rules(&self.rules, &mut ast, 0, &fresh);
|
||||
if res.len() != 1 {
|
||||
panic!("Expected at exactly one result node, got {}", res.len());
|
||||
}
|
||||
|
||||
@@ -27,4 +27,11 @@ impl FreshScope {
|
||||
})
|
||||
.clone()
|
||||
}
|
||||
|
||||
/// Clear resolved names but keep the counter. Called between rule
|
||||
/// applications so that `$tmp` in different rules gets different values
|
||||
/// while the counter increases monotonically.
|
||||
pub fn next_scope(&self) {
|
||||
self.resolved.borrow_mut().clear();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -145,7 +145,8 @@ fn test_tree_builder() {
|
||||
query.do_match(&ast, ast.get_root(), &mut captures).unwrap();
|
||||
|
||||
// Swap left and right
|
||||
let mut ctx = yeast::build::BuildCtx::new(&mut ast, &captures);
|
||||
let fresh = yeast::tree_builder::FreshScope::new();
|
||||
let mut ctx = yeast::build::BuildCtx::new(&mut ast, &captures, &fresh);
|
||||
let new_id = yeast::tree!(ctx,
|
||||
(program
|
||||
child: (assignment
|
||||
@@ -324,19 +325,19 @@ program
|
||||
body:
|
||||
block_body
|
||||
assignment
|
||||
left: identifier \"$tmp-0\"
|
||||
left: identifier \"$tmp-1\"
|
||||
right: identifier \"$tmp-0\"
|
||||
assignment
|
||||
left: identifier \"a\"
|
||||
right:
|
||||
element_reference
|
||||
object: identifier \"$tmp-0\"
|
||||
object: identifier \"$tmp-1\"
|
||||
integer \"0\"
|
||||
assignment
|
||||
left: identifier \"b\"
|
||||
right:
|
||||
element_reference
|
||||
object: identifier \"$tmp-0\"
|
||||
object: identifier \"$tmp-1\"
|
||||
integer \"1\"
|
||||
identifier \"x\"
|
||||
parameters:
|
||||
|
||||
Reference in New Issue
Block a user