Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions unified/extractor/ast_types.yml
Original file line number Diff line number Diff line change
Expand Up @@ -387,10 +387,10 @@ 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.
# A `default:` entry uses an `ignore_pattern`.
switch_case:
modifier*: modifier
pattern?: expr
pattern: expr
body: block

# Evaluate 'expr' and match its result against 'pattern', and return true if it matches.
Expand Down
8 changes: 5 additions & 3 deletions unified/extractor/src/languages/swift/swift.rs
Original file line number Diff line number Diff line change
Expand Up @@ -918,7 +918,7 @@ fn translation_rules() -> Vec<Rule<SwiftContext>> {
),
// A `switch` statement. Each `switchCase` becomes a `switch_case` with a
// pattern (or an `or_pattern` for comma-separated `case a, b:`) and a
// body; a `default:` case has a body but no pattern. The case items and
// body; a `default:` case uses an `ignore_pattern`. The case items and
// body are auto-translated; the Rust block only picks the pattern shape
// by arity (the query engine can't branch on list length).
rule!(
Expand All @@ -934,9 +934,11 @@ fn translation_rules() -> Vec<Rule<SwiftContext>> {
body: (block stmt: {body}))
),
rule!(
(switchCase label: (switchDefaultLabel) statements: _* @body)
(switchCase label: (switchDefaultLabel defaultKeyword: @@keyword) statements: _* @body)
=>
(switch_case body: (block stmt: {body}))
(switch_case
pattern: (ignore_pattern #{keyword})
body: (block stmt: {body}))
),
// A single case item unwraps to its pattern, possibly boxed in conditional_pattern
rule!(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,8 @@ top_level source="⟨body⟩"
argument:
argument source="⟨value⟩"
value: string_literal "\"matched\"" source="\"matched\""
switch_case source="default:\n ⟨body⟩"
switch_case source="⟨pattern⟩:\n ⟨body⟩"
pattern: ignore_pattern "default" source="default"
body:
block source="⟨stmt⟩"
stmt: break_expr "break" source="break"
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,8 @@ top_level source="⟨body⟩"
body:
block source="⟨stmt⟩"
stmt: unsupported_node "fallthrough" source="fallthrough"
switch_case source="default:\n ⟨body⟩"
switch_case source="⟨pattern⟩:\n ⟨body⟩"
pattern: ignore_pattern "default" source="default"
body:
block source="⟨stmt⟩"
stmt: break_expr "break" source="break"
Original file line number Diff line number Diff line change
Expand Up @@ -286,7 +286,8 @@ top_level source="⟨body⟩"
body:
block source="⟨stmt⟩"
stmt: break_expr "break" source="break"
switch_case source="default:\n ⟨body⟩"
switch_case source="⟨pattern⟩:\n ⟨body⟩"
pattern: ignore_pattern "default" source="default"
body:
block source="⟨stmt⟩"
stmt: break_expr "break" source="break"
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,8 @@ top_level source="⟨body⟩"
argument:
argument source="⟨value⟩"
value: string_literal "\"non-positive\"" source="\"non-positive\""
switch_case source="default:\n ⟨body⟩"
switch_case source="⟨pattern⟩:\n ⟨body⟩"
pattern: ignore_pattern "default" source="default"
body:
block source="⟨stmt⟩"
stmt:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -393,7 +393,8 @@ top_level source="⟨body⟩"
argument:
argument source="⟨value⟩"
value: identifier "fallback" source="fallback"
switch_case source="default:\n ⟨body⟩"
switch_case source="⟨pattern⟩:\n ⟨body⟩"
pattern: ignore_pattern "default" source="default"
body:
block source="⟨stmt⟩"
stmt: break_expr "break" source="break"
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,8 @@ top_level source="⟨body⟩"
argument:
argument source="⟨value⟩"
value: string_literal "\"two or three\"" source="\"two or three\""
switch_case source="default:\n ⟨body⟩"
switch_case source="⟨pattern⟩:\n ⟨body⟩"
pattern: ignore_pattern "default" source="default"
body:
block source="⟨stmt⟩"
stmt:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,8 @@ top_level source="var p: Int {\n ⟨body⟩\n}"
stmt:
return_expr source="return ⟨value⟩"
value: int_literal "1" source="1"
switch_case source="default:\n ⟨body⟩"
switch_case source="⟨pattern⟩:\n ⟨body⟩"
pattern: ignore_pattern "default" source="default"
body:
block source="⟨stmt⟩"
stmt:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,8 @@ top_level source="⟨body⟩"
body:
block source="⟨stmt⟩"
stmt: int_literal "1" source="1"
switch_case source="default: ⟨body⟩"
switch_case source="⟨pattern⟩: ⟨body⟩"
pattern: ignore_pattern "default" source="default"
body:
block source="⟨stmt⟩"
stmt: int_literal "2" source="2"
9 changes: 9 additions & 0 deletions unified/extractor/tests/location_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,15 @@ fn synthesized_condition_and_switch_nodes_use_child_ranges() {
assert_has_span(&ast, source, "block", None, "c");
}

#[test]
fn switch_default_patterns_span_the_default_keyword() {
let source = "switch x { case _: break; @unknown default: break }";
let ast = desugar(source);

assert_has_span(&ast, source, "ignore_pattern", Some("_"), "_");
assert_has_span(&ast, source, "ignore_pattern", Some("default"), "default");
}

#[test]
fn declaration_and_operator_tokens_keep_precise_ranges() {
let source = "func f() { return x }\nlet y = try? await value! as? T\nlet z = value is T";
Expand Down
8 changes: 4 additions & 4 deletions unified/ql/lib/codeql/unified/internal/Ast.qll
Original file line number Diff line number Diff line change
Expand Up @@ -1278,7 +1278,7 @@ module Unified {
final override string getAPrimaryQlClass() { result = "SwitchCase" }

/** Gets the node corresponding to the field `body`. */
final F::Block getBody() { unified_switch_case_def(this, result) }
final F::Block getBody() { unified_switch_case_def(this, result, _) }

/** Gets the node corresponding to the field `modifier`. */
final F::Modifier getModifier(int i) { unified_switch_case_modifier(this, i, result) }
Expand All @@ -1287,13 +1287,13 @@ module Unified {
final F::Modifier getAModifier() { result = this.getModifier(_) }

/** Gets the node corresponding to the field `pattern`. */
final F::Expr getPattern() { unified_switch_case_pattern(this, result) }
final F::Expr getPattern() { unified_switch_case_def(this, _, result) }

/** Gets a field or child node of this node. */
final override F::AstNode getAFieldOrChild() {
unified_switch_case_def(this, result) or
unified_switch_case_def(this, result, _) or
unified_switch_case_modifier(this, _, result) or
unified_switch_case_pattern(this, result)
unified_switch_case_def(this, _, result)
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -247,7 +247,7 @@ module Ast implements AstSig<Location> {
}

class DefaultCase extends Case {
DefaultCase() { not exists(super.getPattern()) }
DefaultCase() { super.getPattern() instanceof U::IgnorePattern }
}

class ConditionalExpr = U::IfExpr;
Expand Down
8 changes: 2 additions & 6 deletions unified/ql/lib/unified.dbscheme
Original file line number Diff line number Diff line change
Expand Up @@ -809,14 +809,10 @@ unified_switch_case_modifier(
unique int modifier: @unified_token_modifier ref
);

unified_switch_case_pattern(
unique int unified_switch_case: @unified_switch_case ref,
unique int pattern: @unified_expr ref
);

unified_switch_case_def(
unique int id: @unified_switch_case,
int body: @unified_block ref
int body: @unified_block ref,
int pattern: @unified_expr ref
);

#keyset[unified_switch_expr, index]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@
| 155 | cfg.swift:155:5:157:17 | SwitchCase | 'SwitchCase -V 42' |
| 156 | cfg.swift:156:7:157:17 | Block | 'Block -V print -> Argument -V "42!" -^ print(...)' |
| 157 | cfg.swift:157:7:157:17 | | '' |
| 158 | cfg.swift:158:5:159:34 | SwitchCase | 'SwitchCase' |
| 158 | cfg.swift:158:5:159:34 | SwitchCase | 'SwitchCase -V default' |
| 159 | cfg.swift:159:7:159:34 | Block | 'Block -V IfExpr -V x -> 10 -^ ... > ...' |
| 159 | cfg.swift:159:19:159:34 | Block | 'Block -V false -^ ReturnExpr' |
| 162 | cfg.swift:162:3:162:31 | var obj | 'var obj -V obj -> AnyObject -> C -> Argument -V x -^ C(...)' |
Expand Down Expand Up @@ -482,7 +482,7 @@
| 595 | cfg.swift:595:3:598:3 | let a | 'let a -V a -> SwitchExpr -V x' |
| 596 | cfg.swift:596:5:596:17 | SwitchCase | 'SwitchCase -V 0 -> 5 -^ ... ..< ...' |
| 596 | cfg.swift:596:17:596:17 | Block | 'Block -V 1' |
| 597 | cfg.swift:597:5:597:14 | SwitchCase | 'SwitchCase -V Block -V 2' |
| 597 | cfg.swift:597:5:597:14 | SwitchCase | 'SwitchCase -V default -> Block -V 2' |
| 599 | cfg.swift:599:3:601:18 | let b | 'let b -V b' |
| 600 | cfg.swift:600:9:601:18 | IfExpr | 'IfExpr -V x -> 42 -^ ... < ...' |
| 600 | cfg.swift:600:21:600:25 | Block | 'Block -V 1' |
Expand Down
Loading