diff --git a/unified/extractor/src/languages/swift/swift.rs b/unified/extractor/src/languages/swift/swift.rs index 13f1a6beadf0..0a2ab2cd4b47 100644 --- a/unified/extractor/src/languages/swift/swift.rs +++ b/unified/extractor/src/languages/swift/swift.rs @@ -735,21 +735,36 @@ fn translation_rules() -> Vec> { (switch_entry pattern: (switch_pattern pattern: @first) pattern: (switch_pattern pattern: @rest)+ + where: (where_clause expr: @guard)? statement: _* @body) => - (switch_case pattern: (or_pattern pattern: {first} pattern: {rest}) body: (block stmt: {body})) + (switch_case + pattern: (or_pattern pattern: {first} pattern: {rest}) + guard: {guard} + body: (block stmt: {body})) ), // Switch entry with exactly one pattern and body rule!( - (switch_entry pattern: (switch_pattern pattern: @pat) statement: _* @body) + (switch_entry + pattern: (switch_pattern pattern: @pat) + where: (where_clause expr: @guard)? + statement: _* @body) => - (switch_case pattern: {pat} body: (block stmt: {body})) + (switch_case + pattern: {pat} + guard: {guard} + body: (block stmt: {body})) ), // Switch entry: default case (no patterns) rule!( - (switch_entry default: (default_keyword) statement: _* @body) + (switch_entry + default: (default_keyword) + where: (where_clause expr: @guard)? + statement: _* @body) => - (switch_case body: (block stmt: {body})) + (switch_case + guard: {guard} + body: (block stmt: {body})) ), // if case PATTERN = expr — preserve the pattern directly (no Optional wrapping) rule!( diff --git a/unified/extractor/tests/corpus/swift/control-flow/switch-case-where-clauses.output b/unified/extractor/tests/corpus/swift/control-flow/switch-case-where-clauses.output new file mode 100644 index 000000000000..8c78888da4c0 --- /dev/null +++ b/unified/extractor/tests/corpus/swift/control-flow/switch-case-where-clauses.output @@ -0,0 +1,140 @@ +switch x { +case 1 where x > 0: + print("positive one") +case 2, 3: + print("small two or three") +default: + print("other") +} + +--- + +source_file + statement: + switch_statement + entry: + switch_entry + pattern: + switch_pattern + pattern: + pattern + kind: integer_literal "1" + statement: + call_expression + function: simple_identifier "print" + suffix: + call_suffix + arguments: + value_arguments + argument: + value_argument + value: + line_string_literal + text: line_str_text "positive one" + where: + where_clause + expr: + comparison_expression + lhs: simple_identifier "x" + op: > + rhs: integer_literal "0" + keyword: where_keyword "where" + switch_entry + pattern: + switch_pattern + pattern: + pattern + kind: integer_literal "2" + switch_pattern + pattern: + pattern + kind: integer_literal "3" + statement: + call_expression + function: simple_identifier "print" + suffix: + call_suffix + arguments: + value_arguments + argument: + value_argument + value: + line_string_literal + text: line_str_text "small two or three" + switch_entry + default: default_keyword "default" + statement: + call_expression + function: simple_identifier "print" + suffix: + call_suffix + arguments: + value_arguments + argument: + value_argument + value: + line_string_literal + text: line_str_text "other" + expr: simple_identifier "x" + +--- + +top_level + body: + block + stmt: + switch_expr + case: + switch_case + body: + block + stmt: + call_expr + argument: + argument + value: string_literal "\"positive one\"" + callee: + name_expr + identifier: identifier "print" + pattern: + expr_equality_pattern + expr: int_literal "1" + guard: + binary_expr + operator: infix_operator ">" + left: + name_expr + identifier: identifier "x" + right: int_literal "0" + switch_case + body: + block + stmt: + call_expr + argument: + argument + value: string_literal "\"small two or three\"" + callee: + name_expr + identifier: identifier "print" + pattern: + or_pattern + pattern: + expr_equality_pattern + expr: int_literal "2" + expr_equality_pattern + expr: int_literal "3" + switch_case + body: + block + stmt: + call_expr + argument: + argument + value: string_literal "\"other\"" + callee: + name_expr + identifier: identifier "print" + value: + name_expr + identifier: identifier "x" diff --git a/unified/extractor/tests/corpus/swift/control-flow/switch-case-where-clauses.swift b/unified/extractor/tests/corpus/swift/control-flow/switch-case-where-clauses.swift new file mode 100644 index 000000000000..53a6cf073913 --- /dev/null +++ b/unified/extractor/tests/corpus/swift/control-flow/switch-case-where-clauses.swift @@ -0,0 +1,8 @@ +switch x { +case 1 where x > 0: + print("positive one") +case 2, 3: + print("small two or three") +default: + print("other") +} diff --git a/unified/ql/lib/codeql/unified/internal/Variables.qll b/unified/ql/lib/codeql/unified/internal/Variables.qll index 97629f2c57ff..a22cd9009dda 100644 --- a/unified/ql/lib/codeql/unified/internal/Variables.qll +++ b/unified/ql/lib/codeql/unified/internal/Variables.qll @@ -262,8 +262,6 @@ private module LocalNameBindingInput implements LocalNameBindingInputSig; diff --git a/unified/ql/test/library-tests/variables/test.swift b/unified/ql/test/library-tests/variables/test.swift index 0e6c23782b0d..35aac75d393f 100644 --- a/unified/ql/test/library-tests/variables/test.swift +++ b/unified/ql/test/library-tests/variables/test.swift @@ -80,7 +80,7 @@ func t10(value: Int) { // name=value1 // Switch with multiple cases func t11(value: Int) { // name=value1 switch value { // $ access=value1 - case let x where x > 0: // name=x1 + case let x where x > 0: // $ access=x1 // name=x1 print(x) // $ access=x1 case let x: // name=x2 print(x) // $ access=x2 @@ -187,6 +187,8 @@ func t22() { } inner() // $ access=inner1 print(x) // $ access=x1 + let inner = 2 // name=inner2 + print(inner) // $ access=inner2 } // Three levels of shadowing @@ -214,7 +216,7 @@ func t24(optional: Int?) { // name=optional1 } } -// Switch with same variable name in different cases +// Switch with variable shadowed within body of case func t25(value: Int) { // name=value1 switch value { // $ access=value1 case let x: // name=x1