diff --git a/unified/extractor/ast_types.yml b/unified/extractor/ast_types.yml index b293820d9cd8..1d8500447696 100644 --- a/unified/extractor/ast_types.yml +++ b/unified/extractor/ast_types.yml @@ -37,6 +37,7 @@ supertypes: - or_pattern - conditional_pattern - bulk_importing_pattern + - ignore_pattern - generic_type_expr - inferred_type_expr # An element of an `unresolved_operator_sequence`: either an operand (`expr`) @@ -411,6 +412,9 @@ named: bulk_importing_pattern: modifier*: modifier + # A pattern that accepts any incoming value, usually written as an `_`. + ignore_pattern: + # A simple unqualified name token identifier: diff --git a/unified/extractor/src/languages/swift/swift.rs b/unified/extractor/src/languages/swift/swift.rs index 5176f0a815b6..62561c6a1620 100644 --- a/unified/extractor/src/languages/swift/swift.rs +++ b/unified/extractor/src/languages/swift/swift.rs @@ -206,9 +206,8 @@ fn translation_rules() -> Vec> { // referenced identifier / operator symbol. rule!((declReferenceExpr baseName: @name) => (identifier #{name})), // A discard `_` used as an expression — e.g. the target of a discarding - // assignment `_ = x`. swift-syntax models it as a `discardAssignmentExpr`; - // the target AST represents it as a `name_node` over the `_` token. - rule!((discardAssignmentExpr wildcard: @@w) => (identifier #{w})), + // assignment `_ = x`. swift-syntax models it as a `discardAssignmentExpr`. + rule!((discardAssignmentExpr wildcard: @@w) => (ignore_pattern #{w})), // A generic specialization in expression position (`C`, // `Array`) is represented by swift-syntax as a // `genericSpecializationExpr`. When used as a call target @@ -560,7 +559,7 @@ fn translation_rules() -> Vec> { // A wildcard *binding* pattern (`let _ = x`, `for _ in xs`). swift-syntax // models this as a `wildcardPattern`, distinct from the `_` match form // handled by the context-aware `discardAssignmentExpr` rule. - rule!((wildcardPattern) @@wildcard => (identifier #{wildcard})), + rule!((wildcardPattern) @@wildcard => (ignore_pattern #{wildcard})), // An expression pattern only establishes pattern context; its child // determines the concrete pattern shape. rule!((expressionPattern expression: @@e) => expr { diff --git a/unified/extractor/tests/corpus/swift/control-flow/switch-with-labeled-case-pattern-arguments.output b/unified/extractor/tests/corpus/swift/control-flow/switch-with-labeled-case-pattern-arguments.output index f700495a6c91..623d44545d37 100644 --- a/unified/extractor/tests/corpus/swift/control-flow/switch-with-labeled-case-pattern-arguments.output +++ b/unified/extractor/tests/corpus/swift/control-flow/switch-with-labeled-case-pattern-arguments.output @@ -159,7 +159,7 @@ top_level source="⟨body⟩" argument: argument source="⟨name_node⟩: ⟨value⟩" name_node: identifier "threadRowId" source="threadRowId" - value: identifier "_" source="_" + value: ignore_pattern "_" source="_" argument source="⟨value⟩" value: expr_pattern source="⟨modifier⟩ ⟨expr⟩" diff --git a/unified/extractor/tests/corpus/swift/expressions/super-expression.output b/unified/extractor/tests/corpus/swift/expressions/super-expression.output index 8b7da208df1d..c50a81b89ec6 100644 --- a/unified/extractor/tests/corpus/swift/expressions/super-expression.output +++ b/unified/extractor/tests/corpus/swift/expressions/super-expression.output @@ -171,7 +171,7 @@ top_level source="⟨body⟩" block source="{\n ⟨stmt⟩\n ⟨stmt⟩\n }" stmt: binary_expr source="⟨left⟩ ⟨operator⟩ ⟨right⟩" - left: identifier "_" source="_" + left: ignore_pattern "_" source="_" operator: infix_operator "=" source="=" right: member_access_expr source="⟨base⟩.⟨member_name_node⟩" diff --git a/unified/extractor/tests/corpus/swift/operators/unresolved-operator-sequence-with-casts.output b/unified/extractor/tests/corpus/swift/operators/unresolved-operator-sequence-with-casts.output index 9349b1f231dc..8dddba738a54 100644 --- a/unified/extractor/tests/corpus/swift/operators/unresolved-operator-sequence-with-casts.output +++ b/unified/extractor/tests/corpus/swift/operators/unresolved-operator-sequence-with-casts.output @@ -109,7 +109,7 @@ top_level source="⟨body⟩" stmt: unresolved_operator_sequence source="⟨element⟩ ⟨element⟩ ⟨element⟩ ⟨element⟩ ⟨element⟩ ⟨element⟩ ⟨element⟩" element: - identifier "_" source="_" + ignore_pattern "_" source="_" infix_operator "=" source="=" identifier "a" source="a" infix_operator "as" source="as" @@ -118,7 +118,7 @@ top_level source="⟨body⟩" identifier "b" source="b" unresolved_operator_sequence source="⟨element⟩ ⟨element⟩ ⟨element⟩ ⟨element⟩ ⟨element⟩ ⟨element⟩ ⟨element⟩" element: - identifier "_" source="_" + ignore_pattern "_" source="_" infix_operator "=" source="=" identifier "a" source="a" infix_operator "is" source="is" diff --git a/unified/extractor/tests/corpus/swift/operators/unresolved-operator-sequence.output b/unified/extractor/tests/corpus/swift/operators/unresolved-operator-sequence.output index 8e0f15b58b0e..cbe6f488db6a 100644 --- a/unified/extractor/tests/corpus/swift/operators/unresolved-operator-sequence.output +++ b/unified/extractor/tests/corpus/swift/operators/unresolved-operator-sequence.output @@ -82,7 +82,7 @@ top_level source="⟨body⟩" stmt: unresolved_operator_sequence source="⟨element⟩ ⟨element⟩ ⟨element⟩ ⟨element⟩ ⟨element⟩" element: - identifier "_" source="_" + ignore_pattern "_" source="_" infix_operator "=" source="=" identifier "a" source="a" infix_operator ".&" source=".&" diff --git a/unified/ql/lib/codeql/unified/internal/Ast.qll b/unified/ql/lib/codeql/unified/internal/Ast.qll index ea296e4d3dd7..0c4c9f65a3c6 100644 --- a/unified/ql/lib/codeql/unified/internal/Ast.qll +++ b/unified/ql/lib/codeql/unified/internal/Ast.qll @@ -836,6 +836,12 @@ module Unified { } } + /** A class representing `ignore_pattern` tokens. */ + class IgnorePattern extends @unified_token_ignore_pattern, F::Expr, F::Token { + /** Gets the name of the primary QL class for this element. */ + final override string getAPrimaryQlClass() { result = "IgnorePattern" } + } + /** A class representing `import_declaration` nodes. */ class ImportDeclaration extends @unified_import_declaration, F::Stmt { /** Gets the name of the primary QL class for this element. */ @@ -1894,6 +1900,8 @@ module UnifiedFinal { final class IfExpr = F::IfExpr; + final class IgnorePattern = F::IgnorePattern; + final class ImportDeclaration = F::ImportDeclaration; final class InferredTypeExpr = F::InferredTypeExpr; diff --git a/unified/ql/lib/unified.dbscheme b/unified/ql/lib/unified.dbscheme index c5f144e8b661..8916955b5408 100644 --- a/unified/ql/lib/unified.dbscheme +++ b/unified/ql/lib/unified.dbscheme @@ -435,7 +435,7 @@ unified_equality_type_constraint_def( int right: @unified_expr ref ); -@unified_expr = @unified_array_literal | @unified_binary_expr | @unified_block | @unified_break_expr | @unified_bulk_importing_pattern | @unified_call_expr | @unified_conditional_pattern | @unified_continue_expr | @unified_expr_pattern | @unified_function_expr | @unified_generic_type_expr | @unified_if_expr | @unified_key_value_pair | @unified_map_literal | @unified_member_access_expr | @unified_named_pattern | @unified_or_pattern | @unified_pattern_guard_expr | @unified_return_expr | @unified_string_interpolation_expr | @unified_switch_expr | @unified_throw_expr | @unified_token_boolean_literal | @unified_token_builtin_expr | @unified_token_empty_expr | @unified_token_float_literal | @unified_token_identifier | @unified_token_inferred_type_expr | @unified_token_int_literal | @unified_token_regex_literal | @unified_token_string_literal | @unified_token_super_expr | @unified_token_unsupported_node | @unified_try_expr | @unified_tuple_expr | @unified_type_cast_expr | @unified_type_test_expr | @unified_unary_expr | @unified_unresolved_operator_sequence +@unified_expr = @unified_array_literal | @unified_binary_expr | @unified_block | @unified_break_expr | @unified_bulk_importing_pattern | @unified_call_expr | @unified_conditional_pattern | @unified_continue_expr | @unified_expr_pattern | @unified_function_expr | @unified_generic_type_expr | @unified_if_expr | @unified_key_value_pair | @unified_map_literal | @unified_member_access_expr | @unified_named_pattern | @unified_or_pattern | @unified_pattern_guard_expr | @unified_return_expr | @unified_string_interpolation_expr | @unified_switch_expr | @unified_throw_expr | @unified_token_boolean_literal | @unified_token_builtin_expr | @unified_token_empty_expr | @unified_token_float_literal | @unified_token_identifier | @unified_token_ignore_pattern | @unified_token_inferred_type_expr | @unified_token_int_literal | @unified_token_regex_literal | @unified_token_string_literal | @unified_token_super_expr | @unified_token_unsupported_node | @unified_try_expr | @unified_tuple_expr | @unified_type_cast_expr | @unified_type_test_expr | @unified_unary_expr | @unified_unresolved_operator_sequence @unified_expr_or_operator = @unified_expr | @unified_token_infix_operator @@ -980,16 +980,17 @@ case @unified_token.kind of | 5 = @unified_token_fixity | 6 = @unified_token_float_literal | 7 = @unified_token_identifier -| 8 = @unified_token_inferred_type_expr -| 9 = @unified_token_infix_operator -| 10 = @unified_token_int_literal -| 11 = @unified_token_modifier -| 12 = @unified_token_postfix_operator -| 13 = @unified_token_prefix_operator -| 14 = @unified_token_regex_literal -| 15 = @unified_token_string_literal -| 16 = @unified_token_super_expr -| 17 = @unified_token_unsupported_node +| 8 = @unified_token_ignore_pattern +| 9 = @unified_token_inferred_type_expr +| 10 = @unified_token_infix_operator +| 11 = @unified_token_int_literal +| 12 = @unified_token_modifier +| 13 = @unified_token_postfix_operator +| 14 = @unified_token_prefix_operator +| 15 = @unified_token_regex_literal +| 16 = @unified_token_string_literal +| 17 = @unified_token_super_expr +| 18 = @unified_token_unsupported_node ;