From 7e1f6b98d4bd815d695f10cdf55e03a09d8b3e96 Mon Sep 17 00:00:00 2001 From: Asger F Date: Wed, 29 Jul 2026 11:48:39 +0200 Subject: [PATCH] unified: Factor catch/case guards into conditional_pattern The where-clause needs to be attached to the individual pattern, not the catch/case. --- unified/extractor/ast_types.yml | 12 +++++++++--- unified/ql/lib/codeql/unified/Ast.qll | 28 +++++++++++++++++++++++++++ unified/ql/lib/unified.dbscheme | 15 +++++++++++++- 3 files changed, 51 insertions(+), 4 deletions(-) diff --git a/unified/extractor/ast_types.yml b/unified/extractor/ast_types.yml index 4fa1ff16942..99a8b62986f 100644 --- a/unified/extractor/ast_types.yml +++ b/unified/extractor/ast_types.yml @@ -49,6 +49,7 @@ supertypes: - tuple_pattern - constructor_pattern - or_pattern + - conditional_pattern - ignore_pattern - expr_equality_pattern - bulk_importing_pattern @@ -369,7 +370,6 @@ named: catch_clause: modifier*: modifier pattern?: pattern - guard?: expr body: block # `switch value { case pattern: body case ...: default: body }` @@ -381,11 +381,9 @@ named: # A single `case ...:` (or `default:`) entry in a switch. # An entry with multiple `case p1, p2:` patterns uses an `or_pattern`. # A `default:` entry has no pattern. - # An optional `guard` corresponds to a `where`-clause on the case. switch_case: modifier*: modifier pattern?: pattern - guard?: expr body: block # Evaluate 'expr' and match its result against 'pattern', and return true if it matches. @@ -446,6 +444,14 @@ named: modifier*: modifier pattern*: pattern + # A pattern that matches against a nested pattern, and subsequently checks a condition. + # The match is rejected if the condition does not hold. + # Variables bound in the nested pattern are in scope within the condition. + conditional_pattern: + modifier*: modifier + condition: expr + pattern: pattern + # A pattern with an optional associated name. pattern_element: modifier*: modifier diff --git a/unified/ql/lib/codeql/unified/Ast.qll b/unified/ql/lib/codeql/unified/Ast.qll index e9a827269cc..2f84aedd412 100644 --- a/unified/ql/lib/codeql/unified/Ast.qll +++ b/unified/ql/lib/codeql/unified/Ast.qll @@ -427,6 +427,28 @@ module Unified { } } + /** A class representing `conditional_pattern` nodes. */ + final class ConditionalPattern extends @unified_conditional_pattern, AstNodeImpl { + /** Gets the name of the primary QL class for this element. */ + final override string getAPrimaryQlClass() { result = "ConditionalPattern" } + + /** Gets the node corresponding to the field `condition`. */ + final Expr getCondition() { unified_conditional_pattern_def(this, result, _) } + + /** Gets the node corresponding to the field `modifier`. */ + final Modifier getModifier(int i) { unified_conditional_pattern_modifier(this, i, result) } + + /** Gets the node corresponding to the field `pattern`. */ + final Pattern getPattern() { unified_conditional_pattern_def(this, _, result) } + + /** Gets a field or child node of this node. */ + final override AstNode getAFieldOrChild() { + unified_conditional_pattern_def(this, result, _) or + unified_conditional_pattern_modifier(this, _, result) or + unified_conditional_pattern_def(this, _, result) + } + } + /** A class representing `constructor_declaration` nodes. */ final class ConstructorDeclaration extends @unified_constructor_declaration, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ @@ -1565,6 +1587,12 @@ module Unified { or result = node.(CompoundAssignExpr).getValue() and i = -1 and name = "getValue" or + result = node.(ConditionalPattern).getCondition() and i = -1 and name = "getCondition" + or + result = node.(ConditionalPattern).getModifier(i) and name = "getModifier" + or + result = node.(ConditionalPattern).getPattern() and i = -1 and name = "getPattern" + or result = node.(ConstructorDeclaration).getBody() and i = -1 and name = "getBody" or result = node.(ConstructorDeclaration).getModifier(i) and name = "getModifier" diff --git a/unified/ql/lib/unified.dbscheme b/unified/ql/lib/unified.dbscheme index 3aafb2a494f..8ff0a75cc67 100644 --- a/unified/ql/lib/unified.dbscheme +++ b/unified/ql/lib/unified.dbscheme @@ -365,6 +365,19 @@ unified_compound_assign_expr_def( int value: @unified_expr ref ); +#keyset[unified_conditional_pattern, index] +unified_conditional_pattern_modifier( + int unified_conditional_pattern: @unified_conditional_pattern ref, + int index: int ref, + unique int modifier: @unified_token_modifier ref +); + +unified_conditional_pattern_def( + unique int id: @unified_conditional_pattern, + int condition: @unified_expr ref, + int pattern: @unified_pattern ref +); + #keyset[unified_constructor_declaration, index] unified_constructor_declaration_modifier( int unified_constructor_declaration: @unified_constructor_declaration ref, @@ -1085,7 +1098,7 @@ unified_trivia_tokeninfo( string value: string ref ); -@unified_ast_node = @unified_accessor_declaration | @unified_argument | @unified_array_literal | @unified_assign_expr | @unified_associated_type_declaration | @unified_base_type | @unified_binary_expr | @unified_block | @unified_bound_type_constraint | @unified_break_expr | @unified_bulk_importing_pattern | @unified_call_expr | @unified_catch_clause | @unified_class_like_declaration | @unified_compound_assign_expr | @unified_constructor_declaration | @unified_constructor_pattern | @unified_continue_expr | @unified_destructor_declaration | @unified_do_while_stmt | @unified_equality_type_constraint | @unified_expr_equality_pattern | @unified_for_each_stmt | @unified_function_declaration | @unified_function_expr | @unified_function_type_expr | @unified_generic_type_expr | @unified_guard_if_stmt | @unified_if_expr | @unified_import_declaration | @unified_initializer_declaration | @unified_key_value_pair | @unified_labeled_stmt | @unified_map_literal | @unified_member_access_expr | @unified_name_expr | @unified_name_pattern | @unified_named_type_expr | @unified_operator_syntax_declaration | @unified_or_pattern | @unified_parameter | @unified_pattern_element | @unified_pattern_guard_expr | @unified_return_expr | @unified_switch_case | @unified_switch_expr | @unified_throw_expr | @unified_token | @unified_top_level | @unified_trivia_token | @unified_try_expr | @unified_tuple_expr | @unified_tuple_pattern | @unified_tuple_type_element | @unified_tuple_type_expr | @unified_type_alias_declaration | @unified_type_cast_expr | @unified_type_parameter | @unified_type_test_expr | @unified_type_test_pattern | @unified_unary_expr | @unified_unresolved_operator_sequence | @unified_variable_declaration | @unified_while_stmt +@unified_ast_node = @unified_accessor_declaration | @unified_argument | @unified_array_literal | @unified_assign_expr | @unified_associated_type_declaration | @unified_base_type | @unified_binary_expr | @unified_block | @unified_bound_type_constraint | @unified_break_expr | @unified_bulk_importing_pattern | @unified_call_expr | @unified_catch_clause | @unified_class_like_declaration | @unified_compound_assign_expr | @unified_conditional_pattern | @unified_constructor_declaration | @unified_constructor_pattern | @unified_continue_expr | @unified_destructor_declaration | @unified_do_while_stmt | @unified_equality_type_constraint | @unified_expr_equality_pattern | @unified_for_each_stmt | @unified_function_declaration | @unified_function_expr | @unified_function_type_expr | @unified_generic_type_expr | @unified_guard_if_stmt | @unified_if_expr | @unified_import_declaration | @unified_initializer_declaration | @unified_key_value_pair | @unified_labeled_stmt | @unified_map_literal | @unified_member_access_expr | @unified_name_expr | @unified_name_pattern | @unified_named_type_expr | @unified_operator_syntax_declaration | @unified_or_pattern | @unified_parameter | @unified_pattern_element | @unified_pattern_guard_expr | @unified_return_expr | @unified_switch_case | @unified_switch_expr | @unified_throw_expr | @unified_token | @unified_top_level | @unified_trivia_token | @unified_try_expr | @unified_tuple_expr | @unified_tuple_pattern | @unified_tuple_type_element | @unified_tuple_type_expr | @unified_type_alias_declaration | @unified_type_cast_expr | @unified_type_parameter | @unified_type_test_expr | @unified_type_test_pattern | @unified_unary_expr | @unified_unresolved_operator_sequence | @unified_variable_declaration | @unified_while_stmt unified_ast_node_location( unique int node: @unified_ast_node ref,