From 2b9c34e2e1560cba457d9991157d3a729674d604 Mon Sep 17 00:00:00 2001 From: Simon Friis Vindum Date: Wed, 30 Sep 2026 09:21:48 +0200 Subject: [PATCH] Unified: Extract defer statements --- unified/extractor/ast_types.yml | 4 ++++ unified/extractor/src/languages/swift/swift.rs | 3 ++- .../swift/control-flow/defer-statement.output | 10 +++++++++- unified/ql/lib/codeql/unified/internal/Ast.qll | 16 ++++++++++++++++ unified/ql/lib/unified.dbscheme | 9 +++++++-- .../controlflow/basicblock-slices.expected | 11 ++++++++--- 6 files changed, 46 insertions(+), 7 deletions(-) diff --git a/unified/extractor/ast_types.yml b/unified/extractor/ast_types.yml index 5c97b12b2ab0..a49f3cf8e723 100644 --- a/unified/extractor/ast_types.yml +++ b/unified/extractor/ast_types.yml @@ -64,6 +64,7 @@ supertypes: - for_each_stmt - while_stmt - do_while_stmt + - defer_stmt - labeled_stmt callable: - top_level @@ -346,6 +347,9 @@ named: body?: block condition: expr + defer_stmt: + body: expr + # `do { body } catch pattern { ... } catch ...`. Swift uses `do`/`catch` # for error handling; for languages with `try`/`catch`, this is the same shape. try_expr: diff --git a/unified/extractor/src/languages/swift/swift.rs b/unified/extractor/src/languages/swift/swift.rs index 5b961e7d75a9..27e00fba59f3 100644 --- a/unified/extractor/src/languages/swift/swift.rs +++ b/unified/extractor/src/languages/swift/swift.rs @@ -927,6 +927,8 @@ fn translation_rules() -> Vec> { => (do_while_stmt condition: {cond} body: {body}) ), + // A `defer` statement + rule!((deferStmt body: @body) => (defer_stmt body: {body})), // A labeled statement (`outer: for … { }`). swift-syntax stores the // label and colon as separate tokens, so the label token is already the // bare name (no trailing `:` to strip). @@ -1387,7 +1389,6 @@ fn translation_rules() -> Vec> { rule!((compositionType) => (unsupported_node)), rule!((consumeExpr) => (unsupported_node)), rule!((copyExpr) => (unsupported_node)), - rule!((deferStmt) => (unsupported_node)), rule!((discardStmt) => (unsupported_node)), rule!((fallThroughStmt) => (unsupported_node)), rule!((ifConfigDecl) => (unsupported_node)), diff --git a/unified/extractor/tests/corpus/swift/control-flow/defer-statement.output b/unified/extractor/tests/corpus/swift/control-flow/defer-statement.output index 37d29a427dbe..dc073f8be151 100644 --- a/unified/extractor/tests/corpus/swift/control-flow/defer-statement.output +++ b/unified/extractor/tests/corpus/swift/control-flow/defer-statement.output @@ -84,7 +84,15 @@ top_level source="⟨body⟩" body: block source="{\n ⟨stmt⟩\n ⟨stmt⟩\n}" stmt: - unsupported_node "defer { print(\"cleanup\") }" source="defer { print(\"cleanup\") }" + defer_stmt source="defer ⟨body⟩" + body: + block source="{ ⟨stmt⟩ }" + stmt: + call_expr source="⟨callee⟩(⟨argument⟩)" + callee: identifier "print" source="print" + argument: + argument source="⟨value⟩" + value: string_literal "\"cleanup\"" source="\"cleanup\"" call_expr source="⟨callee⟩(⟨argument⟩)" callee: identifier "print" source="print" argument: diff --git a/unified/ql/lib/codeql/unified/internal/Ast.qll b/unified/ql/lib/codeql/unified/internal/Ast.qll index cfc917b85580..583f2194c3eb 100644 --- a/unified/ql/lib/codeql/unified/internal/Ast.qll +++ b/unified/ql/lib/codeql/unified/internal/Ast.qll @@ -528,6 +528,18 @@ module Unified { } } + /** A class representing `defer_stmt` nodes. */ + class DeferStmt extends @unified_defer_stmt, F::Stmt { + /** Gets the name of the primary QL class for this element. */ + final override string getAPrimaryQlClass() { result = "DeferStmt" } + + /** Gets the node corresponding to the field `body`. */ + final F::Expr getBody() { unified_defer_stmt_def(this, result) } + + /** Gets a field or child node of this node. */ + final override F::AstNode getAFieldOrChild() { unified_defer_stmt_def(this, result) } + } + /** A class representing `destructor_declaration` nodes. */ class DestructorDeclaration extends @unified_destructor_declaration, F::Callable, F::Member, F::Stmt @@ -1619,6 +1631,8 @@ module Unified { or result = node.(ContinueExpr).getLabelNameNode() and i = -1 and name = "getLabelNameNode" or + result = node.(DeferStmt).getBody() and i = -1 and name = "getBody" + or result = node.(DestructorDeclaration).getBody() and i = -1 and name = "getBody" or result = node.(DestructorDeclaration).getModifier(i) and name = "getModifier" @@ -1874,6 +1888,8 @@ module UnifiedFinal { final class ContinueExpr = F::ContinueExpr; + final class DeferStmt = F::DeferStmt; + final class DestructorDeclaration = F::DestructorDeclaration; final class DoWhileStmt = F::DoWhileStmt; diff --git a/unified/ql/lib/unified.dbscheme b/unified/ql/lib/unified.dbscheme index fda85ffc96e3..1371099109b6 100644 --- a/unified/ql/lib/unified.dbscheme +++ b/unified/ql/lib/unified.dbscheme @@ -400,6 +400,11 @@ unified_continue_expr_def( unique int id: @unified_continue_expr ); +unified_defer_stmt_def( + unique int id: @unified_defer_stmt, + int body: @unified_expr ref +); + #keyset[unified_destructor_declaration, index] unified_destructor_declaration_modifier( int unified_destructor_declaration: @unified_destructor_declaration ref, @@ -746,7 +751,7 @@ unified_return_expr_def( unique int id: @unified_return_expr ); -@unified_stmt = @unified_accessor_declaration | @unified_class_like_declaration | @unified_constructor_declaration | @unified_destructor_declaration | @unified_do_while_stmt | @unified_expr | @unified_for_each_stmt | @unified_function_declaration | @unified_guard_if_stmt | @unified_import_declaration | @unified_labeled_stmt | @unified_operator_syntax_declaration | @unified_type_alias_declaration | @unified_variable_declaration | @unified_while_stmt +@unified_stmt = @unified_accessor_declaration | @unified_class_like_declaration | @unified_constructor_declaration | @unified_defer_stmt | @unified_destructor_declaration | @unified_do_while_stmt | @unified_expr | @unified_for_each_stmt | @unified_function_declaration | @unified_guard_if_stmt | @unified_import_declaration | @unified_labeled_stmt | @unified_operator_syntax_declaration | @unified_type_alias_declaration | @unified_variable_declaration | @unified_while_stmt #keyset[unified_string_interpolation_expr, index] unified_string_interpolation_expr_element( @@ -1001,7 +1006,7 @@ unified_trivia_tokeninfo( string value: string ref ); -@unified_ast_node = @unified_accessor_declaration | @unified_argument | @unified_array_literal | @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_conditional_pattern | @unified_constructor_declaration | @unified_continue_expr | @unified_destructor_declaration | @unified_do_while_stmt | @unified_equality_type_constraint | @unified_expr_pattern | @unified_for_each_stmt | @unified_function_declaration | @unified_function_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_named_pattern | @unified_operator_syntax_declaration | @unified_or_pattern | @unified_parameter | @unified_pattern_guard_expr | @unified_return_expr | @unified_string_interpolation_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_type_alias_declaration | @unified_type_cast_expr | @unified_type_parameter | @unified_type_test_expr | @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_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_conditional_pattern | @unified_constructor_declaration | @unified_continue_expr | @unified_defer_stmt | @unified_destructor_declaration | @unified_do_while_stmt | @unified_equality_type_constraint | @unified_expr_pattern | @unified_for_each_stmt | @unified_function_declaration | @unified_function_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_named_pattern | @unified_operator_syntax_declaration | @unified_or_pattern | @unified_parameter | @unified_pattern_guard_expr | @unified_return_expr | @unified_string_interpolation_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_type_alias_declaration | @unified_type_cast_expr | @unified_type_parameter | @unified_type_test_expr | @unified_unary_expr | @unified_unresolved_operator_sequence | @unified_variable_declaration | @unified_while_stmt unified_ast_node_location( unique int node: @unified_ast_node ref, diff --git a/unified/ql/test/library-tests/controlflow/basicblock-slices.expected b/unified/ql/test/library-tests/controlflow/basicblock-slices.expected index 2edaee1e3e28..36487bd95dca 100644 --- a/unified/ql/test/library-tests/controlflow/basicblock-slices.expected +++ b/unified/ql/test/library-tests/controlflow/basicblock-slices.expected @@ -129,9 +129,14 @@ | 152 | cfg.swift:152:7:152:18 | Block | 'Block -V false -^ ReturnExpr' | | 168 | cfg.swift:168:1:184:1 | func testDefer | 'func testDefer' | | 168 | cfg.swift:168:16:168:16 | x | 'x -> Block' | -| 170 | cfg.swift:170:3:172:3 | | '' | -| 174 | cfg.swift:174:3:176:3 | | '' | -| 178 | cfg.swift:178:3:183:3 | | '' | +| 170 | cfg.swift:170:3:172:3 | DeferStmt | 'DeferStmt -V Block' | +| 171 | cfg.swift:171:5:171:9 | print | 'print -> Argument -V "4" -^ print(...)' | +| 174 | cfg.swift:174:3:176:3 | DeferStmt | 'DeferStmt -V Block' | +| 175 | cfg.swift:175:5:175:9 | print | 'print -> Argument -V "3" -^ print(...)' | +| 178 | cfg.swift:178:3:183:3 | DeferStmt | 'DeferStmt -V Block' | +| 179 | cfg.swift:179:5:179:9 | print | 'print -> Argument -V "1" -^ print(...)' | +| 180 | cfg.swift:180:6:182:5 | DeferStmt | 'DeferStmt -V Block' | +| 181 | cfg.swift:181:7:181:11 | print | 'print -> Argument -V "2" -^ print(...)' | | 186 | cfg.swift:186:1:198:1 | func m1 | 'func m1' | | 186 | cfg.swift:186:9:186:9 | x | 'x -> Block' | | 187 | cfg.swift:187:3:197:3 | IfExpr | 'IfExpr -V x -> 2 -^ ... > ...' |