mirror of
https://github.com/github/codeql.git
synced 2026-06-25 14:47:04 +02:00
Yeast: better support for rewriting unnamed nodes
- Ensure the full wildcard _ supports quantifiers - Also rewrite unnamed nodes in one-shot phases
This commit is contained in:
@@ -259,6 +259,7 @@ fn parse_query_list(tokens: &mut Tokens) -> Result<Vec<TokenStream>> {
|
||||
yeast::query::QueryListElem::SingleNode(#node)
|
||||
},
|
||||
)?;
|
||||
let elem = maybe_wrap_list_capture(tokens, elem)?;
|
||||
elems.push(elem);
|
||||
continue;
|
||||
}
|
||||
@@ -276,6 +277,7 @@ fn parse_query_list(tokens: &mut Tokens) -> Result<Vec<TokenStream>> {
|
||||
yeast::query::QueryListElem::SingleNode(#node)
|
||||
},
|
||||
)?;
|
||||
let elem = maybe_wrap_list_capture(tokens, elem)?;
|
||||
elems.push(elem);
|
||||
continue;
|
||||
}
|
||||
@@ -717,8 +719,11 @@ fn extract_captures_inner(
|
||||
}
|
||||
last_mult = CaptureMultiplicity::Single;
|
||||
}
|
||||
TokenTree::Punct(p) if matches!(p.as_char(), '*' | '+' | '?') => {
|
||||
// Keep last_mult — the @capture follows
|
||||
TokenTree::Punct(p) if p.as_char() == '*' || p.as_char() == '+' => {
|
||||
last_mult = CaptureMultiplicity::Repeated;
|
||||
}
|
||||
TokenTree::Punct(p) if p.as_char() == '?' => {
|
||||
last_mult = CaptureMultiplicity::Optional;
|
||||
}
|
||||
_ => {
|
||||
last_mult = CaptureMultiplicity::Single;
|
||||
|
||||
@@ -825,14 +825,6 @@ fn apply_one_shot_rules_inner(
|
||||
|
||||
let node_kind = ast.get_node(id).map(|n| n.kind()).unwrap_or("");
|
||||
|
||||
// Don't rewrite unnamed nodes (punctuation, keywords, etc.); leave them
|
||||
// as-is. Rules target named nodes only.
|
||||
if let Some(node) = ast.get_node(id) {
|
||||
if !node.is_named() {
|
||||
return Ok(vec![id]);
|
||||
}
|
||||
}
|
||||
|
||||
for rule in index.rules_for_kind(node_kind) {
|
||||
if let Some(mut captures) = rule.try_match(ast, id)? {
|
||||
// Recursively translate every captured node before invoking the
|
||||
|
||||
@@ -389,6 +389,29 @@ fn test_capture_unnamed_node_parenthesized() {
|
||||
assert!(!op_node.is_named());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_capture_bare_underscore_repeated() {
|
||||
// `_` matches named and unnamed nodes in bare-child position. On this
|
||||
// assignment shape, bare children correspond to unnamed tokens (the `=`).
|
||||
let runner = Runner::new(tree_sitter_ruby::LANGUAGE.into(), &[]);
|
||||
let ast = runner.run("x = 1").unwrap();
|
||||
|
||||
let query = yeast::query!((assignment _* @all));
|
||||
|
||||
let mut cursor = AstCursor::new(&ast);
|
||||
cursor.goto_first_child();
|
||||
let assignment_id = cursor.node_id();
|
||||
|
||||
let mut captures = yeast::captures::Captures::new();
|
||||
let matched = query.do_match(&ast, assignment_id, &mut captures).unwrap();
|
||||
assert!(matched);
|
||||
|
||||
let all = captures.get_all("all");
|
||||
assert_eq!(all.len(), 1);
|
||||
assert_eq!(ast.get_node(all[0]).unwrap().kind(), "=");
|
||||
assert!(!ast.get_node(all[0]).unwrap().is_named());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_capture_unnamed_node_bare_literal() {
|
||||
// `"=" @op` (without surrounding parens) is the same as `("=") @op`.
|
||||
|
||||
Reference in New Issue
Block a user