From ff7dc297d53b863aeb63720f70cd86a1a49b7463 Mon Sep 17 00:00:00 2001 From: Asger F Date: Thu, 25 Jun 2026 13:52:58 +0200 Subject: [PATCH 1/7] Shared: Generate PrintAst helper in tree sitter extractor Auto-generating a helper for implementing the PrintAST query on top of the generated AST. --- .../src/generator/mod.rs | 1 + .../tree-sitter-extractor/src/generator/ql.rs | 5 +- .../src/generator/ql_gen.rs | 95 ++++++++++++++++++- 3 files changed, 99 insertions(+), 2 deletions(-) diff --git a/shared/tree-sitter-extractor/src/generator/mod.rs b/shared/tree-sitter-extractor/src/generator/mod.rs index d3880a74579f..6c5fbfabda62 100644 --- a/shared/tree-sitter-extractor/src/generator/mod.rs +++ b/shared/tree-sitter-extractor/src/generator/mod.rs @@ -159,6 +159,7 @@ pub fn generate( )); body.append(&mut ql_gen::convert_nodes(&nodes)); + body.push(ql_gen::create_print_ast_module(&nodes)); ql::write( &mut ql_writer, &[ql::TopLevel::Module(ql::Module { diff --git a/shared/tree-sitter-extractor/src/generator/ql.rs b/shared/tree-sitter-extractor/src/generator/ql.rs index cdfe5d8c639f..24ae25d854bb 100644 --- a/shared/tree-sitter-extractor/src/generator/ql.rs +++ b/shared/tree-sitter-extractor/src/generator/ql.rs @@ -150,12 +150,14 @@ impl fmt::Display for Type<'_> { pub enum Expression<'a> { Var(&'a str), String(&'a str), - Integer(usize), + Integer(i64), Pred(&'a str, Vec>), And(Vec>), Or(Vec>), Equals(Box>, Box>), Dot(Box>, &'a str, Vec>), + /// A type cast, rendered as `x.(Type)`. + Cast(Box>, &'a str), Aggregate { name: &'a str, vars: Vec>, @@ -219,6 +221,7 @@ impl fmt::Display for Expression<'_> { } write!(f, ")") } + Expression::Cast(x, type_name) => write!(f, "{x}.({type_name})"), Expression::Aggregate { name, vars, diff --git a/shared/tree-sitter-extractor/src/generator/ql_gen.rs b/shared/tree-sitter-extractor/src/generator/ql_gen.rs index f827b12580e8..c6feb58ba3f9 100644 --- a/shared/tree-sitter-extractor/src/generator/ql_gen.rs +++ b/shared/tree-sitter-extractor/src/generator/ql_gen.rs @@ -705,7 +705,7 @@ fn create_field_getters<'a>( ), ql::Expression::Equals( Box::new(ql::Expression::Var("value")), - Box::new(ql::Expression::Integer(*value)), + Box::new(ql::Expression::Integer(*value as i64)), ), ]) }) @@ -874,3 +874,96 @@ pub fn convert_nodes(nodes: &node_types::NodeTypeMap) -> Vec> { classes } + +/// Creates a `PrintAst` module containing a `getChild` predicate that maps each +/// AST node to its children together with the name of the member predicate that +/// produced them (and, for indexed fields, the index). This mirrors the +/// information exposed by `getAFieldOrChild`, but keeps the member predicate +/// name and index so that an AST printer can render labelled edges. +pub fn create_print_ast_module(nodes: &node_types::NodeTypeMap) -> ql::TopLevel<'_> { + let mut disjuncts: Vec = Vec::new(); + for node in nodes.values() { + if let node_types::EntryKind::Table { name: _, fields } = &node.kind { + for field in fields { + // `ReservedWordInt` fields have string-valued getters, so they + // are not children and are excluded (just as they are from + // `getAFieldOrChild`). + if matches!(field.type_info, node_types::FieldTypeInfo::ReservedWordInt(_)) { + continue; + } + let has_index = matches!( + field.storage, + node_types::Storage::Table { + has_index: true, + .. + } + ); + let getter_call = ql::Expression::Dot( + Box::new(ql::Expression::Cast( + Box::new(ql::Expression::Var("node")), + &node.ql_class_name, + )), + &field.getter_name, + if has_index { + vec![ql::Expression::Var("i")] + } else { + vec![] + }, + ); + let mut conjuncts = vec![ql::Expression::Equals( + Box::new(ql::Expression::Var("result")), + Box::new(getter_call), + )]; + if !has_index { + conjuncts.push(ql::Expression::Equals( + Box::new(ql::Expression::Var("i")), + Box::new(ql::Expression::Integer(-1)), + )); + } + conjuncts.push(ql::Expression::Equals( + Box::new(ql::Expression::Var("name")), + Box::new(ql::Expression::String(&field.getter_name)), + )); + disjuncts.push(ql::Expression::And(conjuncts)); + } + } + } + + let get_child = ql::Predicate { + qldoc: Some(String::from( + "Gets a child of `node` returned by the member predicate with the given `name`. \ + If the predicate takes an index argument, `i` is bound to that index, otherwise \ + `i` is `-1` (which is never a valid index).", + )), + name: "getChild", + overridden: false, + is_private: false, + is_final: false, + return_type: Some(ql::Type::Normal("AstNode")), + formal_parameters: vec![ + ql::FormalParameter { + name: "node", + param_type: ql::Type::Normal("AstNode"), + }, + ql::FormalParameter { + name: "name", + param_type: ql::Type::String, + }, + ql::FormalParameter { + name: "i", + param_type: ql::Type::Int, + }, + ], + body: ql::Expression::Or(disjuncts), + overlay: None, + }; + + ql::TopLevel::Module(ql::Module { + qldoc: Some(String::from( + "Provides predicates for mapping AST nodes to their named children.", + )), + name: "PrintAst", + body: vec![ql::TopLevel::Predicate(get_child)], + overlay: None, + }) +} From f89f304e501dcf6e1a74ef39a240fd589688af57 Mon Sep 17 00:00:00 2001 From: Asger F Date: Thu, 25 Jun 2026 13:53:23 +0200 Subject: [PATCH 2/7] unified: Regenerate AST --- unified/ql/lib/codeql/unified/Ast.qll | 328 ++++++++++++++++++++++++++ 1 file changed, 328 insertions(+) diff --git a/unified/ql/lib/codeql/unified/Ast.qll b/unified/ql/lib/codeql/unified/Ast.qll index 32ddcd4c16cd..de4a71814c1e 100644 --- a/unified/ql/lib/codeql/unified/Ast.qll +++ b/unified/ql/lib/codeql/unified/Ast.qll @@ -1439,4 +1439,332 @@ module Unified { unified_while_stmt_modifier(this, _, result) } } + + /** Provides predicates for mapping AST nodes to their named children. */ + module PrintAst { + /** Gets a child of `node` returned by the member predicate with the given `name`. If the predicate takes an index argument, `i` is bound to that index, otherwise `i` is `-1` (which is never a valid index). */ + AstNode getChild(AstNode node, string name, int i) { + result = node.(AccessorDeclaration).getAccessorKind() and i = -1 and name = "getAccessorKind" + or + result = node.(AccessorDeclaration).getBody() and i = -1 and name = "getBody" + or + result = node.(AccessorDeclaration).getModifier(i) and name = "getModifier" + or + result = node.(AccessorDeclaration).getName() and i = -1 and name = "getName" + or + result = node.(AccessorDeclaration).getParameter(i) and name = "getParameter" + or + result = node.(AccessorDeclaration).getType() and i = -1 and name = "getType" + or + result = node.(Argument).getModifier(i) and name = "getModifier" + or + result = node.(Argument).getName() and i = -1 and name = "getName" + or + result = node.(Argument).getValue() and i = -1 and name = "getValue" + or + result = node.(ArrayLiteral).getElement(i) and name = "getElement" + or + result = node.(AssignExpr).getTarget() and i = -1 and name = "getTarget" + or + result = node.(AssignExpr).getValue() and i = -1 and name = "getValue" + or + result = node.(AssociatedTypeDeclaration).getBound() and i = -1 and name = "getBound" + or + result = node.(AssociatedTypeDeclaration).getModifier(i) and name = "getModifier" + or + result = node.(AssociatedTypeDeclaration).getName() and i = -1 and name = "getName" + or + result = node.(BaseType).getModifier(i) and name = "getModifier" + or + result = node.(BaseType).getType() and i = -1 and name = "getType" + or + result = node.(BinaryExpr).getLeft() and i = -1 and name = "getLeft" + or + result = node.(BinaryExpr).getOperator() and i = -1 and name = "getOperator" + or + result = node.(BinaryExpr).getRight() and i = -1 and name = "getRight" + or + result = node.(Block).getStmt(i) and name = "getStmt" + or + result = node.(BoundTypeConstraint).getBound() and i = -1 and name = "getBound" + or + result = node.(BoundTypeConstraint).getType() and i = -1 and name = "getType" + or + result = node.(BreakExpr).getLabel() and i = -1 and name = "getLabel" + or + result = node.(BulkImportingPattern).getModifier(i) and name = "getModifier" + or + result = node.(CallExpr).getArgument(i) and name = "getArgument" + or + result = node.(CallExpr).getCallee() and i = -1 and name = "getCallee" + or + result = node.(CallExpr).getModifier(i) and name = "getModifier" + or + result = node.(CatchClause).getBody() and i = -1 and name = "getBody" + or + result = node.(CatchClause).getGuard() and i = -1 and name = "getGuard" + or + result = node.(CatchClause).getModifier(i) and name = "getModifier" + or + result = node.(CatchClause).getPattern() and i = -1 and name = "getPattern" + or + result = node.(ClassLikeDeclaration).getBaseType(i) and name = "getBaseType" + or + result = node.(ClassLikeDeclaration).getMember(i) and name = "getMember" + or + result = node.(ClassLikeDeclaration).getModifier(i) and name = "getModifier" + or + result = node.(ClassLikeDeclaration).getName() and i = -1 and name = "getName" + or + result = node.(ClassLikeDeclaration).getTypeConstraint(i) and name = "getTypeConstraint" + or + result = node.(ClassLikeDeclaration).getTypeParameter(i) and name = "getTypeParameter" + or + result = node.(CompoundAssignExpr).getOperator() and i = -1 and name = "getOperator" + or + result = node.(CompoundAssignExpr).getTarget() and i = -1 and name = "getTarget" + or + result = node.(CompoundAssignExpr).getValue() and i = -1 and name = "getValue" + or + result = node.(ConstructorDeclaration).getBody() and i = -1 and name = "getBody" + or + result = node.(ConstructorDeclaration).getModifier(i) and name = "getModifier" + or + result = node.(ConstructorDeclaration).getName() and i = -1 and name = "getName" + or + result = node.(ConstructorDeclaration).getParameter(i) and name = "getParameter" + or + result = node.(ConstructorPattern).getConstructor() and i = -1 and name = "getConstructor" + or + result = node.(ConstructorPattern).getElement(i) and name = "getElement" + or + result = node.(ConstructorPattern).getModifier(i) and name = "getModifier" + or + result = node.(ContinueExpr).getLabel() and i = -1 and name = "getLabel" + or + result = node.(DestructorDeclaration).getBody() and i = -1 and name = "getBody" + or + result = node.(DestructorDeclaration).getModifier(i) and name = "getModifier" + or + result = node.(DoWhileStmt).getBody() and i = -1 and name = "getBody" + or + result = node.(DoWhileStmt).getCondition() and i = -1 and name = "getCondition" + or + result = node.(DoWhileStmt).getModifier(i) and name = "getModifier" + or + result = node.(EqualityTypeConstraint).getLeft() and i = -1 and name = "getLeft" + or + result = node.(EqualityTypeConstraint).getRight() and i = -1 and name = "getRight" + or + result = node.(ExprEqualityPattern).getExpr() and i = -1 and name = "getExpr" + or + result = node.(ForEachStmt).getBody() and i = -1 and name = "getBody" + or + result = node.(ForEachStmt).getGuard() and i = -1 and name = "getGuard" + or + result = node.(ForEachStmt).getIterable() and i = -1 and name = "getIterable" + or + result = node.(ForEachStmt).getModifier(i) and name = "getModifier" + or + result = node.(ForEachStmt).getPattern() and i = -1 and name = "getPattern" + or + result = node.(FunctionDeclaration).getBody() and i = -1 and name = "getBody" + or + result = node.(FunctionDeclaration).getModifier(i) and name = "getModifier" + or + result = node.(FunctionDeclaration).getName() and i = -1 and name = "getName" + or + result = node.(FunctionDeclaration).getParameter(i) and name = "getParameter" + or + result = node.(FunctionDeclaration).getReturnType() and i = -1 and name = "getReturnType" + or + result = node.(FunctionDeclaration).getTypeConstraint(i) and name = "getTypeConstraint" + or + result = node.(FunctionDeclaration).getTypeParameter(i) and name = "getTypeParameter" + or + result = node.(FunctionExpr).getBody() and i = -1 and name = "getBody" + or + result = node.(FunctionExpr).getCaptureDeclaration(i) and name = "getCaptureDeclaration" + or + result = node.(FunctionExpr).getModifier(i) and name = "getModifier" + or + result = node.(FunctionExpr).getParameter(i) and name = "getParameter" + or + result = node.(FunctionExpr).getReturnType() and i = -1 and name = "getReturnType" + or + result = node.(FunctionTypeExpr).getParameter(i) and name = "getParameter" + or + result = node.(FunctionTypeExpr).getReturnType() and i = -1 and name = "getReturnType" + or + result = node.(GenericTypeExpr).getBase() and i = -1 and name = "getBase" + or + result = node.(GenericTypeExpr).getTypeArgument(i) and name = "getTypeArgument" + or + result = node.(GuardIfStmt).getCondition() and i = -1 and name = "getCondition" + or + result = node.(GuardIfStmt).getElse() and i = -1 and name = "getElse" + or + result = node.(IfExpr).getCondition() and i = -1 and name = "getCondition" + or + result = node.(IfExpr).getElse() and i = -1 and name = "getElse" + or + result = node.(IfExpr).getThen() and i = -1 and name = "getThen" + or + result = node.(ImportDeclaration).getImportedExpr() and i = -1 and name = "getImportedExpr" + or + result = node.(ImportDeclaration).getModifier(i) and name = "getModifier" + or + result = node.(ImportDeclaration).getPattern() and i = -1 and name = "getPattern" + or + result = node.(InitializerDeclaration).getBody() and i = -1 and name = "getBody" + or + result = node.(InitializerDeclaration).getModifier(i) and name = "getModifier" + or + result = node.(KeyValuePair).getKey() and i = -1 and name = "getKey" + or + result = node.(KeyValuePair).getValue() and i = -1 and name = "getValue" + or + result = node.(LabeledStmt).getLabel() and i = -1 and name = "getLabel" + or + result = node.(LabeledStmt).getStmt() and i = -1 and name = "getStmt" + or + result = node.(MapLiteral).getElement(i) and name = "getElement" + or + result = node.(MemberAccessExpr).getBase() and i = -1 and name = "getBase" + or + result = node.(MemberAccessExpr).getMember() and i = -1 and name = "getMember" + or + result = node.(NameExpr).getIdentifier() and i = -1 and name = "getIdentifier" + or + result = node.(NamePattern).getIdentifier() and i = -1 and name = "getIdentifier" + or + result = node.(NamePattern).getModifier(i) and name = "getModifier" + or + result = node.(NamedTypeExpr).getName() and i = -1 and name = "getName" + or + result = node.(NamedTypeExpr).getQualifier() and i = -1 and name = "getQualifier" + or + result = node.(OperatorSyntaxDeclaration).getFixity() and i = -1 and name = "getFixity" + or + result = node.(OperatorSyntaxDeclaration).getModifier(i) and name = "getModifier" + or + result = node.(OperatorSyntaxDeclaration).getName() and i = -1 and name = "getName" + or + result = node.(OperatorSyntaxDeclaration).getPrecedence() and + i = -1 and + name = "getPrecedence" + or + result = node.(OrPattern).getModifier(i) and name = "getModifier" + or + result = node.(OrPattern).getPattern(i) and name = "getPattern" + or + result = node.(Parameter).getDefault() and i = -1 and name = "getDefault" + or + result = node.(Parameter).getExternalName() and i = -1 and name = "getExternalName" + or + result = node.(Parameter).getModifier(i) and name = "getModifier" + or + result = node.(Parameter).getPattern() and i = -1 and name = "getPattern" + or + result = node.(Parameter).getType() and i = -1 and name = "getType" + or + result = node.(PatternElement).getKey() and i = -1 and name = "getKey" + or + result = node.(PatternElement).getModifier(i) and name = "getModifier" + or + result = node.(PatternElement).getPattern() and i = -1 and name = "getPattern" + or + result = node.(PatternGuardExpr).getPattern() and i = -1 and name = "getPattern" + or + result = node.(PatternGuardExpr).getValue() and i = -1 and name = "getValue" + or + result = node.(ReturnExpr).getValue() and i = -1 and name = "getValue" + or + result = node.(SwitchCase).getBody() and i = -1 and name = "getBody" + or + result = node.(SwitchCase).getGuard() and i = -1 and name = "getGuard" + or + result = node.(SwitchCase).getModifier(i) and name = "getModifier" + or + result = node.(SwitchCase).getPattern() and i = -1 and name = "getPattern" + or + result = node.(SwitchExpr).getCase(i) and name = "getCase" + or + result = node.(SwitchExpr).getModifier(i) and name = "getModifier" + or + result = node.(SwitchExpr).getValue() and i = -1 and name = "getValue" + or + result = node.(ThrowExpr).getValue() and i = -1 and name = "getValue" + or + result = node.(TopLevel).getBody() and i = -1 and name = "getBody" + or + result = node.(TryExpr).getBody() and i = -1 and name = "getBody" + or + result = node.(TryExpr).getCatchClause(i) and name = "getCatchClause" + or + result = node.(TryExpr).getModifier(i) and name = "getModifier" + or + result = node.(TupleExpr).getElement(i) and name = "getElement" + or + result = node.(TuplePattern).getElement(i) and name = "getElement" + or + result = node.(TuplePattern).getModifier(i) and name = "getModifier" + or + result = node.(TupleTypeElement).getName() and i = -1 and name = "getName" + or + result = node.(TupleTypeElement).getType() and i = -1 and name = "getType" + or + result = node.(TupleTypeExpr).getElement(i) and name = "getElement" + or + result = node.(TypeAliasDeclaration).getModifier(i) and name = "getModifier" + or + result = node.(TypeAliasDeclaration).getName() and i = -1 and name = "getName" + or + result = node.(TypeAliasDeclaration).getType() and i = -1 and name = "getType" + or + result = node.(TypeAliasDeclaration).getTypeConstraint(i) and name = "getTypeConstraint" + or + result = node.(TypeAliasDeclaration).getTypeParameter(i) and name = "getTypeParameter" + or + result = node.(TypeCastExpr).getExpr() and i = -1 and name = "getExpr" + or + result = node.(TypeCastExpr).getOperator() and i = -1 and name = "getOperator" + or + result = node.(TypeCastExpr).getType() and i = -1 and name = "getType" + or + result = node.(TypeParameter).getBound() and i = -1 and name = "getBound" + or + result = node.(TypeParameter).getModifier(i) and name = "getModifier" + or + result = node.(TypeParameter).getName() and i = -1 and name = "getName" + or + result = node.(TypeTestExpr).getExpr() and i = -1 and name = "getExpr" + or + result = node.(TypeTestExpr).getOperator() and i = -1 and name = "getOperator" + or + result = node.(TypeTestExpr).getType() and i = -1 and name = "getType" + or + result = node.(TypeTestPattern).getPattern() and i = -1 and name = "getPattern" + or + result = node.(TypeTestPattern).getType() and i = -1 and name = "getType" + or + result = node.(UnaryExpr).getOperand() and i = -1 and name = "getOperand" + or + result = node.(UnaryExpr).getOperator() and i = -1 and name = "getOperator" + or + result = node.(VariableDeclaration).getModifier(i) and name = "getModifier" + or + result = node.(VariableDeclaration).getPattern() and i = -1 and name = "getPattern" + or + result = node.(VariableDeclaration).getType() and i = -1 and name = "getType" + or + result = node.(VariableDeclaration).getValue() and i = -1 and name = "getValue" + or + result = node.(WhileStmt).getBody() and i = -1 and name = "getBody" + or + result = node.(WhileStmt).getCondition() and i = -1 and name = "getCondition" + or + result = node.(WhileStmt).getModifier(i) and name = "getModifier" + } + } } From 5348c7d07c334550d68ecf66042e7d1f21c5917b Mon Sep 17 00:00:00 2001 From: Asger F Date: Thu, 25 Jun 2026 16:55:40 +0200 Subject: [PATCH 3/7] unified: Add PrintAst query --- unified/ql/lib/codeql/IDEContextual.qll | 16 ++++ unified/ql/lib/codeql/unified/printAst.qll | 96 +++++++++++++++++++ .../ql/lib/ide-contextual-queries/printAst.ql | 27 ++++++ 3 files changed, 139 insertions(+) create mode 100644 unified/ql/lib/codeql/IDEContextual.qll create mode 100644 unified/ql/lib/codeql/unified/printAst.qll create mode 100644 unified/ql/lib/ide-contextual-queries/printAst.ql diff --git a/unified/ql/lib/codeql/IDEContextual.qll b/unified/ql/lib/codeql/IDEContextual.qll new file mode 100644 index 000000000000..3b8486b45264 --- /dev/null +++ b/unified/ql/lib/codeql/IDEContextual.qll @@ -0,0 +1,16 @@ +/** + * Provides shared predicates related to contextual queries in the code viewer. + */ + +private import codeql.files.FileSystem +private import codeql.util.FileSystem + +/** + * Returns an appropriately encoded version of a filename `name` + * passed by the VS Code extension in order to coincide with the + * output of `.getFile()` on locatable entities. + */ +cached +File getFileBySourceArchiveName(string name) { + result = IdeContextual::getFileBySourceArchiveName(name) +} diff --git a/unified/ql/lib/codeql/unified/printAst.qll b/unified/ql/lib/codeql/unified/printAst.qll new file mode 100644 index 000000000000..93ff11f5c8b1 --- /dev/null +++ b/unified/ql/lib/codeql/unified/printAst.qll @@ -0,0 +1,96 @@ +/** Provides a configurable query for printing AST nodes */ + +private import unified + +/** + * The query can extend this class to control which nodes are printed. + */ +class PrintAstConfiguration extends string { + PrintAstConfiguration() { this = "PrintAstConfiguration" } + + /** + * Holds if the given node should be printed. + */ + predicate shouldPrintNode(AstNode n) { not n instanceof TriviaToken } + + /** + * Holds if the given edge should be printed. + */ + predicate shouldPrintAstEdge(AstNode parent, string edgeName, AstNode child) { + exists(string name, int i | + child = PrintAst::getChild(parent, name, i) and + (if i = -1 then edgeName = name else edgeName = name + "(" + i + ")") + ) + } +} + +private predicate shouldPrintNode(AstNode n) { + any(PrintAstConfiguration config).shouldPrintNode(n) +} + +private predicate shouldPrintAstEdge(AstNode parent, string edgeName, AstNode child) { + any(PrintAstConfiguration config).shouldPrintAstEdge(parent, edgeName, child) and + shouldPrintNode(parent) and + shouldPrintNode(child) +} + +/** + * Get an alias for the predicate `name` to use for ordering purposes, to control where + * in the list of children it should appear. + */ +private string reorderName1(string name) { name = "getModifier" and result = "00_getModifier" } + +bindingset[name] +private string reorderName(string name) { + result = reorderName1(name) + or + not exists(reorderName1(name)) and + result = name +} + +class PrintAstNode extends AstNode { + final int getOrder() { + this = + rank[result](AstNode parent, AstNode child, string name, int i | + child = PrintAst::getChild(parent, name, i) + | + child order by reorderName(name), i + ) + } + + final string getProperty(string key) { + key = "semmle.label" and + result = this.toString() + or + key = "semmle.order" and result = this.getOrder().toString() + } +} + +/** + * Holds if `node` belongs to the output tree, and its property `key` has the + * given `value`. + */ +query predicate nodes(PrintAstNode node, string key, string value) { + shouldPrintNode(node) and + value = node.getProperty(key) +} + +/** + * Holds if `target` is a child of `source` in the AST, and property `key` of + * the edge has the given `value`. + */ +query predicate edges(PrintAstNode source, PrintAstNode target, string key, string value) { + key = "semmle.label" and + shouldPrintAstEdge(source, value, target) + or + key = "semmle.order" and + shouldPrintAstEdge(source, _, target) and + value = target.getProperty("semmle.order") +} + +/** + * Holds if property `key` of the graph has the given `value`. + */ +query predicate graphProperties(string key, string value) { + key = "semmle.graphKind" and value = "tree" +} diff --git a/unified/ql/lib/ide-contextual-queries/printAst.ql b/unified/ql/lib/ide-contextual-queries/printAst.ql new file mode 100644 index 000000000000..3622babe07ff --- /dev/null +++ b/unified/ql/lib/ide-contextual-queries/printAst.ql @@ -0,0 +1,27 @@ +/** + * @name Print AST + * @description Produces a representation of a file's Abstract Syntax Tree. + * This query is used by the VS Code extension. + * @id unified/print-ast + * @kind graph + * @tags ide-contextual-queries/print-ast + */ + +private import codeql.IDEContextual +private import unified +private import codeql.unified.printAst + +/** + * The source file to generate an AST from. + */ +external string selectedSourceFile(); + +/** + * A configuration that only prints nodes in the selected source file. + */ +class Cfg extends PrintAstConfiguration { + override predicate shouldPrintNode(AstNode n) { + super.shouldPrintNode(n) and + n.getLocation().getFile() = getFileBySourceArchiveName(selectedSourceFile()) + } +} From 5735ac330d337398373c54519daab802cdd3b965 Mon Sep 17 00:00:00 2001 From: Asger F Date: Fri, 26 Jun 2026 10:26:14 +0200 Subject: [PATCH 4/7] Ruby: Regenerate raw AST --- .../codeql/ruby/ast/internal/TreeSitter.qll | 350 ++++++++++++++++++ 1 file changed, 350 insertions(+) diff --git a/ruby/ql/lib/codeql/ruby/ast/internal/TreeSitter.qll b/ruby/ql/lib/codeql/ruby/ast/internal/TreeSitter.qll index dbc7b38b84e3..e6b4c63f5486 100644 --- a/ruby/ql/lib/codeql/ruby/ast/internal/TreeSitter.qll +++ b/ruby/ql/lib/codeql/ruby/ast/internal/TreeSitter.qll @@ -1964,6 +1964,340 @@ module Ruby { /** Gets a field or child node of this node. */ final override AstNode getAFieldOrChild() { ruby_yield_child(this, result) } } + + /** Provides predicates for mapping AST nodes to their named children. */ + module PrintAst { + /** Gets a child of `node` returned by the member predicate with the given `name`. If the predicate takes an index argument, `i` is bound to that index, otherwise `i` is `-1` (which is never a valid index). */ + AstNode getChild(AstNode node, string name, int i) { + result = node.(Alias).getAlias() and i = -1 and name = "getAlias" + or + result = node.(Alias).getName() and i = -1 and name = "getName" + or + result = node.(AlternativePattern).getAlternatives(i) and name = "getAlternatives" + or + result = node.(ArgumentList).getChild(i) and name = "getChild" + or + result = node.(Array).getChild(i) and name = "getChild" + or + result = node.(ArrayPattern).getClass() and i = -1 and name = "getClass" + or + result = node.(ArrayPattern).getChild(i) and name = "getChild" + or + result = node.(AsPattern).getName() and i = -1 and name = "getName" + or + result = node.(AsPattern).getValue() and i = -1 and name = "getValue" + or + result = node.(Assignment).getLeft() and i = -1 and name = "getLeft" + or + result = node.(Assignment).getRight() and i = -1 and name = "getRight" + or + result = node.(BareString).getChild(i) and name = "getChild" + or + result = node.(BareSymbol).getChild(i) and name = "getChild" + or + result = node.(Begin).getChild(i) and name = "getChild" + or + result = node.(BeginBlock).getChild(i) and name = "getChild" + or + result = node.(Binary).getLeft() and i = -1 and name = "getLeft" + or + result = node.(Binary).getRight() and i = -1 and name = "getRight" + or + result = node.(Block).getBody() and i = -1 and name = "getBody" + or + result = node.(Block).getParameters() and i = -1 and name = "getParameters" + or + result = node.(BlockArgument).getChild() and i = -1 and name = "getChild" + or + result = node.(BlockBody).getChild(i) and name = "getChild" + or + result = node.(BlockParameter).getName() and i = -1 and name = "getName" + or + result = node.(BlockParameters).getLocals(i) and name = "getLocals" + or + result = node.(BlockParameters).getChild(i) and name = "getChild" + or + result = node.(BodyStatement).getChild(i) and name = "getChild" + or + result = node.(Break).getChild() and i = -1 and name = "getChild" + or + result = node.(Call).getArguments() and i = -1 and name = "getArguments" + or + result = node.(Call).getBlock() and i = -1 and name = "getBlock" + or + result = node.(Call).getMethod() and i = -1 and name = "getMethod" + or + result = node.(Call).getOperator() and i = -1 and name = "getOperator" + or + result = node.(Call).getReceiver() and i = -1 and name = "getReceiver" + or + result = node.(Case).getValue() and i = -1 and name = "getValue" + or + result = node.(Case).getChild(i) and name = "getChild" + or + result = node.(CaseMatch).getClauses(i) and name = "getClauses" + or + result = node.(CaseMatch).getElse() and i = -1 and name = "getElse" + or + result = node.(CaseMatch).getValue() and i = -1 and name = "getValue" + or + result = node.(ChainedString).getChild(i) and name = "getChild" + or + result = node.(Class).getBody() and i = -1 and name = "getBody" + or + result = node.(Class).getName() and i = -1 and name = "getName" + or + result = node.(Class).getSuperclass() and i = -1 and name = "getSuperclass" + or + result = node.(Complex).getChild() and i = -1 and name = "getChild" + or + result = node.(Conditional).getAlternative() and i = -1 and name = "getAlternative" + or + result = node.(Conditional).getCondition() and i = -1 and name = "getCondition" + or + result = node.(Conditional).getConsequence() and i = -1 and name = "getConsequence" + or + result = node.(DelimitedSymbol).getChild(i) and name = "getChild" + or + result = node.(DestructuredLeftAssignment).getChild(i) and name = "getChild" + or + result = node.(DestructuredParameter).getChild(i) and name = "getChild" + or + result = node.(Do).getChild(i) and name = "getChild" + or + result = node.(DoBlock).getBody() and i = -1 and name = "getBody" + or + result = node.(DoBlock).getParameters() and i = -1 and name = "getParameters" + or + result = node.(ElementReference).getBlock() and i = -1 and name = "getBlock" + or + result = node.(ElementReference).getObject() and i = -1 and name = "getObject" + or + result = node.(ElementReference).getChild(i) and name = "getChild" + or + result = node.(Else).getChild(i) and name = "getChild" + or + result = node.(Elsif).getAlternative() and i = -1 and name = "getAlternative" + or + result = node.(Elsif).getCondition() and i = -1 and name = "getCondition" + or + result = node.(Elsif).getConsequence() and i = -1 and name = "getConsequence" + or + result = node.(EndBlock).getChild(i) and name = "getChild" + or + result = node.(Ensure).getChild(i) and name = "getChild" + or + result = node.(ExceptionVariable).getChild() and i = -1 and name = "getChild" + or + result = node.(Exceptions).getChild(i) and name = "getChild" + or + result = node.(ExpressionReferencePattern).getValue() and i = -1 and name = "getValue" + or + result = node.(FindPattern).getClass() and i = -1 and name = "getClass" + or + result = node.(FindPattern).getChild(i) and name = "getChild" + or + result = node.(For).getBody() and i = -1 and name = "getBody" + or + result = node.(For).getPattern() and i = -1 and name = "getPattern" + or + result = node.(For).getValue() and i = -1 and name = "getValue" + or + result = node.(Hash).getChild(i) and name = "getChild" + or + result = node.(HashPattern).getClass() and i = -1 and name = "getClass" + or + result = node.(HashPattern).getChild(i) and name = "getChild" + or + result = node.(HashSplatArgument).getChild() and i = -1 and name = "getChild" + or + result = node.(HashSplatParameter).getName() and i = -1 and name = "getName" + or + result = node.(HeredocBody).getChild(i) and name = "getChild" + or + result = node.(If).getAlternative() and i = -1 and name = "getAlternative" + or + result = node.(If).getCondition() and i = -1 and name = "getCondition" + or + result = node.(If).getConsequence() and i = -1 and name = "getConsequence" + or + result = node.(IfGuard).getCondition() and i = -1 and name = "getCondition" + or + result = node.(IfModifier).getBody() and i = -1 and name = "getBody" + or + result = node.(IfModifier).getCondition() and i = -1 and name = "getCondition" + or + result = node.(In).getChild() and i = -1 and name = "getChild" + or + result = node.(InClause).getBody() and i = -1 and name = "getBody" + or + result = node.(InClause).getGuard() and i = -1 and name = "getGuard" + or + result = node.(InClause).getPattern() and i = -1 and name = "getPattern" + or + result = node.(Interpolation).getChild(i) and name = "getChild" + or + result = node.(KeywordParameter).getName() and i = -1 and name = "getName" + or + result = node.(KeywordParameter).getValue() and i = -1 and name = "getValue" + or + result = node.(KeywordPattern).getKey() and i = -1 and name = "getKey" + or + result = node.(KeywordPattern).getValue() and i = -1 and name = "getValue" + or + result = node.(Lambda).getBody() and i = -1 and name = "getBody" + or + result = node.(Lambda).getParameters() and i = -1 and name = "getParameters" + or + result = node.(LambdaParameters).getChild(i) and name = "getChild" + or + result = node.(LeftAssignmentList).getChild(i) and name = "getChild" + or + result = node.(MatchPattern).getPattern() and i = -1 and name = "getPattern" + or + result = node.(MatchPattern).getValue() and i = -1 and name = "getValue" + or + result = node.(Method).getBody() and i = -1 and name = "getBody" + or + result = node.(Method).getName() and i = -1 and name = "getName" + or + result = node.(Method).getParameters() and i = -1 and name = "getParameters" + or + result = node.(MethodParameters).getChild(i) and name = "getChild" + or + result = node.(Module).getBody() and i = -1 and name = "getBody" + or + result = node.(Module).getName() and i = -1 and name = "getName" + or + result = node.(Next).getChild() and i = -1 and name = "getChild" + or + result = node.(OperatorAssignment).getLeft() and i = -1 and name = "getLeft" + or + result = node.(OperatorAssignment).getRight() and i = -1 and name = "getRight" + or + result = node.(OptionalParameter).getName() and i = -1 and name = "getName" + or + result = node.(OptionalParameter).getValue() and i = -1 and name = "getValue" + or + result = node.(Pair).getKey() and i = -1 and name = "getKey" + or + result = node.(Pair).getValue() and i = -1 and name = "getValue" + or + result = node.(ParenthesizedPattern).getChild() and i = -1 and name = "getChild" + or + result = node.(ParenthesizedStatements).getChild(i) and name = "getChild" + or + result = node.(Pattern).getChild() and i = -1 and name = "getChild" + or + result = node.(Program).getChild(i) and name = "getChild" + or + result = node.(Range).getBegin() and i = -1 and name = "getBegin" + or + result = node.(Range).getEnd() and i = -1 and name = "getEnd" + or + result = node.(Rational).getChild() and i = -1 and name = "getChild" + or + result = node.(Redo).getChild() and i = -1 and name = "getChild" + or + result = node.(Regex).getChild(i) and name = "getChild" + or + result = node.(Rescue).getBody() and i = -1 and name = "getBody" + or + result = node.(Rescue).getExceptions() and i = -1 and name = "getExceptions" + or + result = node.(Rescue).getVariable() and i = -1 and name = "getVariable" + or + result = node.(RescueModifier).getBody() and i = -1 and name = "getBody" + or + result = node.(RescueModifier).getHandler() and i = -1 and name = "getHandler" + or + result = node.(RestAssignment).getChild() and i = -1 and name = "getChild" + or + result = node.(Retry).getChild() and i = -1 and name = "getChild" + or + result = node.(Return).getChild() and i = -1 and name = "getChild" + or + result = node.(RightAssignmentList).getChild(i) and name = "getChild" + or + result = node.(ScopeResolution).getName() and i = -1 and name = "getName" + or + result = node.(ScopeResolution).getScope() and i = -1 and name = "getScope" + or + result = node.(Setter).getName() and i = -1 and name = "getName" + or + result = node.(SingletonClass).getBody() and i = -1 and name = "getBody" + or + result = node.(SingletonClass).getValue() and i = -1 and name = "getValue" + or + result = node.(SingletonMethod).getBody() and i = -1 and name = "getBody" + or + result = node.(SingletonMethod).getName() and i = -1 and name = "getName" + or + result = node.(SingletonMethod).getObject() and i = -1 and name = "getObject" + or + result = node.(SingletonMethod).getParameters() and i = -1 and name = "getParameters" + or + result = node.(SplatArgument).getChild() and i = -1 and name = "getChild" + or + result = node.(SplatParameter).getName() and i = -1 and name = "getName" + or + result = node.(String).getChild(i) and name = "getChild" + or + result = node.(StringArray).getChild(i) and name = "getChild" + or + result = node.(Subshell).getChild(i) and name = "getChild" + or + result = node.(Superclass).getChild() and i = -1 and name = "getChild" + or + result = node.(SymbolArray).getChild(i) and name = "getChild" + or + result = node.(TestPattern).getPattern() and i = -1 and name = "getPattern" + or + result = node.(TestPattern).getValue() and i = -1 and name = "getValue" + or + result = node.(Then).getChild(i) and name = "getChild" + or + result = node.(Unary).getOperand() and i = -1 and name = "getOperand" + or + result = node.(Undef).getChild(i) and name = "getChild" + or + result = node.(Unless).getAlternative() and i = -1 and name = "getAlternative" + or + result = node.(Unless).getCondition() and i = -1 and name = "getCondition" + or + result = node.(Unless).getConsequence() and i = -1 and name = "getConsequence" + or + result = node.(UnlessGuard).getCondition() and i = -1 and name = "getCondition" + or + result = node.(UnlessModifier).getBody() and i = -1 and name = "getBody" + or + result = node.(UnlessModifier).getCondition() and i = -1 and name = "getCondition" + or + result = node.(Until).getBody() and i = -1 and name = "getBody" + or + result = node.(Until).getCondition() and i = -1 and name = "getCondition" + or + result = node.(UntilModifier).getBody() and i = -1 and name = "getBody" + or + result = node.(UntilModifier).getCondition() and i = -1 and name = "getCondition" + or + result = node.(VariableReferencePattern).getName() and i = -1 and name = "getName" + or + result = node.(When).getBody() and i = -1 and name = "getBody" + or + result = node.(When).getPattern(i) and name = "getPattern" + or + result = node.(While).getBody() and i = -1 and name = "getBody" + or + result = node.(While).getCondition() and i = -1 and name = "getCondition" + or + result = node.(WhileModifier).getBody() and i = -1 and name = "getBody" + or + result = node.(WhileModifier).getCondition() and i = -1 and name = "getCondition" + or + result = node.(Yield).getChild() and i = -1 and name = "getChild" + } + } } overlay[local] @@ -2107,4 +2441,20 @@ module Erb { /** Gets a field or child node of this node. */ final override AstNode getAFieldOrChild() { erb_template_child(this, _, result) } } + + /** Provides predicates for mapping AST nodes to their named children. */ + module PrintAst { + /** Gets a child of `node` returned by the member predicate with the given `name`. If the predicate takes an index argument, `i` is bound to that index, otherwise `i` is `-1` (which is never a valid index). */ + AstNode getChild(AstNode node, string name, int i) { + result = node.(CommentDirective).getChild() and i = -1 and name = "getChild" + or + result = node.(Directive).getChild() and i = -1 and name = "getChild" + or + result = node.(GraphqlDirective).getChild() and i = -1 and name = "getChild" + or + result = node.(OutputDirective).getChild() and i = -1 and name = "getChild" + or + result = node.(Template).getChild(i) and name = "getChild" + } + } } From b5ef15c70f2be852c122070fe41787c96a79e039 Mon Sep 17 00:00:00 2001 From: Asger F Date: Fri, 26 Jun 2026 10:26:25 +0200 Subject: [PATCH 5/7] QL4QL: Regenerate raw AST --- .../src/codeql_ql/ast/internal/TreeSitter.qll | 328 ++++++++++++++++++ 1 file changed, 328 insertions(+) diff --git a/ql/ql/src/codeql_ql/ast/internal/TreeSitter.qll b/ql/ql/src/codeql_ql/ast/internal/TreeSitter.qll index c81830e1d59a..402cb23b9104 100644 --- a/ql/ql/src/codeql_ql/ast/internal/TreeSitter.qll +++ b/ql/ql/src/codeql_ql/ast/internal/TreeSitter.qll @@ -1312,6 +1312,244 @@ module QL { /** Gets a field or child node of this node. */ final override AstNode getAFieldOrChild() { ql_variable_def(this, result) } } + + /** Provides predicates for mapping AST nodes to their named children. */ + module PrintAst { + /** Gets a child of `node` returned by the member predicate with the given `name`. If the predicate takes an index argument, `i` is bound to that index, otherwise `i` is `-1` (which is never a valid index). */ + AstNode getChild(AstNode node, string name, int i) { + result = node.(AddExpr).getLeft() and i = -1 and name = "getLeft" + or + result = node.(AddExpr).getRight() and i = -1 and name = "getRight" + or + result = node.(AddExpr).getChild() and i = -1 and name = "getChild" + or + result = node.(Aggregate).getChild(i) and name = "getChild" + or + result = node.(AnnotArg).getChild() and i = -1 and name = "getChild" + or + result = node.(Annotation).getArgs(i) and name = "getArgs" + or + result = node.(Annotation).getName() and i = -1 and name = "getName" + or + result = node.(AritylessPredicateExpr).getName() and i = -1 and name = "getName" + or + result = node.(AritylessPredicateExpr).getQualifier() and i = -1 and name = "getQualifier" + or + result = node.(AsExpr).getChild(i) and name = "getChild" + or + result = node.(AsExprs).getChild(i) and name = "getChild" + or + result = node.(Body).getChild() and i = -1 and name = "getChild" + or + result = node.(Bool).getChild() and i = -1 and name = "getChild" + or + result = node.(CallBody).getChild(i) and name = "getChild" + or + result = node.(CallOrUnqualAggExpr).getChild(i) and name = "getChild" + or + result = node.(Charpred).getBody() and i = -1 and name = "getBody" + or + result = node.(Charpred).getChild() and i = -1 and name = "getChild" + or + result = node.(ClassMember).getChild(i) and name = "getChild" + or + result = node.(ClasslessPredicate).getName() and i = -1 and name = "getName" + or + result = node.(ClasslessPredicate).getReturnType() and i = -1 and name = "getReturnType" + or + result = node.(ClasslessPredicate).getChild(i) and name = "getChild" + or + result = node.(CompTerm).getLeft() and i = -1 and name = "getLeft" + or + result = node.(CompTerm).getRight() and i = -1 and name = "getRight" + or + result = node.(CompTerm).getChild() and i = -1 and name = "getChild" + or + result = node.(Conjunction).getLeft() and i = -1 and name = "getLeft" + or + result = node.(Conjunction).getRight() and i = -1 and name = "getRight" + or + result = node.(Dataclass).getExtends(i) and name = "getExtends" + or + result = node.(Dataclass).getInstanceof(i) and name = "getInstanceof" + or + result = node.(Dataclass).getName() and i = -1 and name = "getName" + or + result = node.(Dataclass).getChild(i) and name = "getChild" + or + result = node.(Datatype).getName() and i = -1 and name = "getName" + or + result = node.(Datatype).getChild() and i = -1 and name = "getChild" + or + result = node.(DatatypeBranch).getName() and i = -1 and name = "getName" + or + result = node.(DatatypeBranch).getChild(i) and name = "getChild" + or + result = node.(DatatypeBranches).getChild(i) and name = "getChild" + or + result = node.(Disjunction).getLeft() and i = -1 and name = "getLeft" + or + result = node.(Disjunction).getRight() and i = -1 and name = "getRight" + or + result = node.(ExprAggregateBody).getAsExprs() and i = -1 and name = "getAsExprs" + or + result = node.(ExprAggregateBody).getOrderBys() and i = -1 and name = "getOrderBys" + or + result = node.(ExprAnnotation).getAnnotArg() and i = -1 and name = "getAnnotArg" + or + result = node.(ExprAnnotation).getName() and i = -1 and name = "getName" + or + result = node.(ExprAnnotation).getChild() and i = -1 and name = "getChild" + or + result = node.(Field).getChild() and i = -1 and name = "getChild" + or + result = node.(FullAggregateBody).getAsExprs() and i = -1 and name = "getAsExprs" + or + result = node.(FullAggregateBody).getGuard() and i = -1 and name = "getGuard" + or + result = node.(FullAggregateBody).getOrderBys() and i = -1 and name = "getOrderBys" + or + result = node.(FullAggregateBody).getChild(i) and name = "getChild" + or + result = node.(HigherOrderTerm).getName() and i = -1 and name = "getName" + or + result = node.(HigherOrderTerm).getChild(i) and name = "getChild" + or + result = node.(IfTerm).getCond() and i = -1 and name = "getCond" + or + result = node.(IfTerm).getFirst() and i = -1 and name = "getFirst" + or + result = node.(IfTerm).getSecond() and i = -1 and name = "getSecond" + or + result = node.(Implication).getLeft() and i = -1 and name = "getLeft" + or + result = node.(Implication).getRight() and i = -1 and name = "getRight" + or + result = node.(ImportDirective).getChild(i) and name = "getChild" + or + result = node.(ImportModuleExpr).getQualName(i) and name = "getQualName" + or + result = node.(ImportModuleExpr).getChild() and i = -1 and name = "getChild" + or + result = node.(InExpr).getLeft() and i = -1 and name = "getLeft" + or + result = node.(InExpr).getRight() and i = -1 and name = "getRight" + or + result = node.(InstanceOf).getChild(i) and name = "getChild" + or + result = node.(Literal).getChild() and i = -1 and name = "getChild" + or + result = node.(MemberPredicate).getName() and i = -1 and name = "getName" + or + result = node.(MemberPredicate).getReturnType() and i = -1 and name = "getReturnType" + or + result = node.(MemberPredicate).getChild(i) and name = "getChild" + or + result = node.(Module).getImplements(i) and name = "getImplements" + or + result = node.(Module).getName() and i = -1 and name = "getName" + or + result = node.(Module).getParameter(i) and name = "getParameter" + or + result = node.(Module).getChild(i) and name = "getChild" + or + result = node.(ModuleAliasBody).getChild() and i = -1 and name = "getChild" + or + result = node.(ModuleExpr).getName() and i = -1 and name = "getName" + or + result = node.(ModuleExpr).getChild() and i = -1 and name = "getChild" + or + result = node.(ModuleInstantiation).getName() and i = -1 and name = "getName" + or + result = node.(ModuleInstantiation).getChild(i) and name = "getChild" + or + result = node.(ModuleMember).getChild(i) and name = "getChild" + or + result = node.(ModuleName).getChild() and i = -1 and name = "getChild" + or + result = node.(ModuleParam).getParameter() and i = -1 and name = "getParameter" + or + result = node.(ModuleParam).getSignature() and i = -1 and name = "getSignature" + or + result = node.(MulExpr).getLeft() and i = -1 and name = "getLeft" + or + result = node.(MulExpr).getRight() and i = -1 and name = "getRight" + or + result = node.(MulExpr).getChild() and i = -1 and name = "getChild" + or + result = node.(Negation).getChild() and i = -1 and name = "getChild" + or + result = node.(OrderBy).getChild(i) and name = "getChild" + or + result = node.(OrderBys).getChild(i) and name = "getChild" + or + result = node.(ParExpr).getChild() and i = -1 and name = "getChild" + or + result = node.(PredicateAliasBody).getChild() and i = -1 and name = "getChild" + or + result = node.(PredicateExpr).getChild(i) and name = "getChild" + or + result = node.(PrefixCast).getChild(i) and name = "getChild" + or + result = node.(Ql).getChild(i) and name = "getChild" + or + result = node.(QualifiedRhs).getName() and i = -1 and name = "getName" + or + result = node.(QualifiedRhs).getChild(i) and name = "getChild" + or + result = node.(QualifiedExpr).getChild(i) and name = "getChild" + or + result = node.(Quantified).getExpr() and i = -1 and name = "getExpr" + or + result = node.(Quantified).getFormula() and i = -1 and name = "getFormula" + or + result = node.(Quantified).getRange() and i = -1 and name = "getRange" + or + result = node.(Quantified).getChild(i) and name = "getChild" + or + result = node.(Range).getLower() and i = -1 and name = "getLower" + or + result = node.(Range).getUpper() and i = -1 and name = "getUpper" + or + result = node.(Select).getChild(i) and name = "getChild" + or + result = node.(SetLiteral).getChild(i) and name = "getChild" + or + result = node.(SignatureExpr).getModExpr() and i = -1 and name = "getModExpr" + or + result = node.(SignatureExpr).getPredicate() and i = -1 and name = "getPredicate" + or + result = node.(SignatureExpr).getTypeExpr() and i = -1 and name = "getTypeExpr" + or + result = node.(SpecialCall).getChild() and i = -1 and name = "getChild" + or + result = node.(SuperRef).getChild(i) and name = "getChild" + or + result = node.(TypeAliasBody).getChild() and i = -1 and name = "getChild" + or + result = node.(TypeExpr).getName() and i = -1 and name = "getName" + or + result = node.(TypeExpr).getQualifier() and i = -1 and name = "getQualifier" + or + result = node.(TypeExpr).getChild() and i = -1 and name = "getChild" + or + result = node.(TypeUnionBody).getChild(i) and name = "getChild" + or + result = node.(UnaryExpr).getChild(i) and name = "getChild" + or + result = node.(UnqualAggBody).getAsExprs(i) and name = "getAsExprs" + or + result = node.(UnqualAggBody).getGuard() and i = -1 and name = "getGuard" + or + result = node.(UnqualAggBody).getChild(i) and name = "getChild" + or + result = node.(VarDecl).getChild(i) and name = "getChild" + or + result = node.(VarName).getChild() and i = -1 and name = "getChild" + or + result = node.(Variable).getChild() and i = -1 and name = "getChild" + } + } } overlay[local] @@ -1669,6 +1907,60 @@ module Dbscheme { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "Varchar" } } + + /** Provides predicates for mapping AST nodes to their named children. */ + module PrintAst { + /** Gets a child of `node` returned by the member predicate with the given `name`. If the predicate takes an index argument, `i` is bound to that index, otherwise `i` is `-1` (which is never a valid index). */ + AstNode getChild(AstNode node, string name, int i) { + result = node.(Annotation).getArgsAnnotation() and i = -1 and name = "getArgsAnnotation" + or + result = node.(Annotation).getSimpleAnnotation() and i = -1 and name = "getSimpleAnnotation" + or + result = node.(ArgsAnnotation).getName() and i = -1 and name = "getName" + or + result = node.(ArgsAnnotation).getChild(i) and name = "getChild" + or + result = node.(Branch).getQldoc() and i = -1 and name = "getQldoc" + or + result = node.(Branch).getChild(i) and name = "getChild" + or + result = node.(CaseDecl).getBase() and i = -1 and name = "getBase" + or + result = node.(CaseDecl).getDiscriminator() and i = -1 and name = "getDiscriminator" + or + result = node.(CaseDecl).getChild(i) and name = "getChild" + or + result = node.(ColType).getChild() and i = -1 and name = "getChild" + or + result = node.(Column).getColName() and i = -1 and name = "getColName" + or + result = node.(Column).getColType() and i = -1 and name = "getColType" + or + result = node.(Column).getIsRef() and i = -1 and name = "getIsRef" + or + result = node.(Column).getIsUnique() and i = -1 and name = "getIsUnique" + or + result = node.(Column).getQldoc() and i = -1 and name = "getQldoc" + or + result = node.(Column).getReprType() and i = -1 and name = "getReprType" + or + result = node.(Dbscheme).getChild(i) and name = "getChild" + or + result = node.(Entry).getChild() and i = -1 and name = "getChild" + or + result = node.(ReprType).getChild(i) and name = "getChild" + or + result = node.(Table).getTableName() and i = -1 and name = "getTableName" + or + result = node.(Table).getChild(i) and name = "getChild" + or + result = node.(TableName).getChild() and i = -1 and name = "getChild" + or + result = node.(UnionDecl).getBase() and i = -1 and name = "getBase" + or + result = node.(UnionDecl).getChild(i) and name = "getChild" + } + } } overlay[local] @@ -1803,6 +2095,24 @@ module Blame { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "Number" } } + + /** Provides predicates for mapping AST nodes to their named children. */ + module PrintAst { + /** Gets a child of `node` returned by the member predicate with the given `name`. If the predicate takes an index argument, `i` is bound to that index, otherwise `i` is `-1` (which is never a valid index). */ + AstNode getChild(AstNode node, string name, int i) { + result = node.(BlameEntry).getDate() and i = -1 and name = "getDate" + or + result = node.(BlameEntry).getLine(i) and name = "getLine" + or + result = node.(BlameInfo).getFileEntry(i) and name = "getFileEntry" + or + result = node.(BlameInfo).getToday() and i = -1 and name = "getToday" + or + result = node.(FileEntry).getBlameEntry(i) and name = "getBlameEntry" + or + result = node.(FileEntry).getFileName() and i = -1 and name = "getFileName" + } + } } overlay[local] @@ -1977,4 +2287,22 @@ module JSON { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "True" } } + + /** Provides predicates for mapping AST nodes to their named children. */ + module PrintAst { + /** Gets a child of `node` returned by the member predicate with the given `name`. If the predicate takes an index argument, `i` is bound to that index, otherwise `i` is `-1` (which is never a valid index). */ + AstNode getChild(AstNode node, string name, int i) { + result = node.(Array).getChild(i) and name = "getChild" + or + result = node.(Document).getChild(i) and name = "getChild" + or + result = node.(Object).getChild(i) and name = "getChild" + or + result = node.(Pair).getKey() and i = -1 and name = "getKey" + or + result = node.(Pair).getValue() and i = -1 and name = "getValue" + or + result = node.(String).getChild(i) and name = "getChild" + } + } } From d6e8555f8ba0388f7cc40f3a7cd2bc0c268ce0ab Mon Sep 17 00:00:00 2001 From: Asger F Date: Fri, 26 Jun 2026 10:48:11 +0200 Subject: [PATCH 6/7] Shared: auto-format tree sitter extractor --- shared/tree-sitter-extractor/src/generator/ql_gen.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/shared/tree-sitter-extractor/src/generator/ql_gen.rs b/shared/tree-sitter-extractor/src/generator/ql_gen.rs index c6feb58ba3f9..bfefdadeaf72 100644 --- a/shared/tree-sitter-extractor/src/generator/ql_gen.rs +++ b/shared/tree-sitter-extractor/src/generator/ql_gen.rs @@ -888,7 +888,10 @@ pub fn create_print_ast_module(nodes: &node_types::NodeTypeMap) -> ql::TopLevel< // `ReservedWordInt` fields have string-valued getters, so they // are not children and are excluded (just as they are from // `getAFieldOrChild`). - if matches!(field.type_info, node_types::FieldTypeInfo::ReservedWordInt(_)) { + if matches!( + field.type_info, + node_types::FieldTypeInfo::ReservedWordInt(_) + ) { continue; } let has_index = matches!( From 14acc7fcab3f12a3658b9c22d6cbf33f4640acaa Mon Sep 17 00:00:00 2001 From: Asger F Date: Fri, 26 Jun 2026 12:04:32 +0200 Subject: [PATCH 7/7] unified: Fixup generated QL The previous commit was generated from a wrong checkout --- unified/ql/lib/codeql/unified/Ast.qll | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/unified/ql/lib/codeql/unified/Ast.qll b/unified/ql/lib/codeql/unified/Ast.qll index de4a71814c1e..1c2d5f3dd4a2 100644 --- a/unified/ql/lib/codeql/unified/Ast.qll +++ b/unified/ql/lib/codeql/unified/Ast.qll @@ -1654,10 +1654,6 @@ module Unified { i = -1 and name = "getPrecedence" or - result = node.(OrPattern).getModifier(i) and name = "getModifier" - or - result = node.(OrPattern).getPattern(i) and name = "getPattern" - or result = node.(Parameter).getDefault() and i = -1 and name = "getDefault" or result = node.(Parameter).getExternalName() and i = -1 and name = "getExternalName" @@ -1686,7 +1682,7 @@ module Unified { or result = node.(SwitchCase).getModifier(i) and name = "getModifier" or - result = node.(SwitchCase).getPattern() and i = -1 and name = "getPattern" + result = node.(SwitchCase).getPattern(i) and name = "getPattern" or result = node.(SwitchExpr).getCase(i) and name = "getCase" or