Skip to content

Commit 5419e65

Browse files
committed
Swift: add NamedPattern.getVarDecl()
1 parent feb8243 commit 5419e65

File tree

8 files changed

+215
-7
lines changed

8 files changed

+215
-7
lines changed

swift/ql/lib/codeql/swift/controlflow/internal/Completion.qll

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -222,19 +222,19 @@ predicate catchMatchingPattern(DoCatchStmt s, CaseStmt c, Pattern pattern) {
222222

223223
/** Holds if `sub` is a subpattern of `p`. */
224224
private predicate isSubPattern(Pattern p, Pattern sub) {
225-
sub = p.(BindingPattern).getResolveStep()
225+
sub = p.(BindingPattern).getImmediateSubPattern()
226226
or
227-
sub = p.(EnumElementPattern).getSubPattern().getFullyUnresolved()
227+
sub = p.(EnumElementPattern).getImmediateSubPattern()
228228
or
229-
sub = p.(IsPattern).getSubPattern().getFullyUnresolved()
229+
sub = p.(IsPattern).getImmediateSubPattern()
230230
or
231-
sub = p.(OptionalSomePattern).getFullyUnresolved()
231+
sub = p.(OptionalSomePattern).getImmediateSubPattern()
232232
or
233-
sub = p.(ParenPattern).getResolveStep()
233+
sub = p.(ParenPattern).getImmediateSubPattern()
234234
or
235-
sub = p.(TuplePattern).getAnElement().getFullyUnresolved()
235+
sub = p.(TuplePattern).getImmediateElement(_)
236236
or
237-
sub = p.(TypedPattern).getSubPattern().getFullyUnresolved()
237+
sub = p.(TypedPattern).getImmediateSubPattern()
238238
}
239239

240240
/** Gets the value of `e` if it is a constant value, disregarding conversions. */
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,40 @@
11
private import codeql.swift.generated.pattern.NamedPattern
2+
private import codeql.swift.elements.pattern.BindingPattern
3+
private import codeql.swift.elements.pattern.EnumElementPattern
4+
private import codeql.swift.elements.pattern.IsPattern
5+
private import codeql.swift.elements.pattern.OptionalSomePattern
6+
private import codeql.swift.elements.pattern.ParenPattern
7+
private import codeql.swift.elements.pattern.TuplePattern
8+
private import codeql.swift.elements.pattern.TypedPattern
9+
private import codeql.swift.elements.decl.VarDecl
10+
private import codeql.swift.elements.stmt.LabeledConditionalStmt
11+
private import codeql.swift.elements.stmt.ConditionElement
212

313
class NamedPattern extends Generated::NamedPattern {
14+
/** Holds if this named pattern has a corresponding `VarDecl` */
15+
predicate hasVarDecl() { exists(this.getVarDecl()) }
16+
17+
/** Gets the `VarDecl` bound by this named pattern, if any. */
18+
VarDecl getVarDecl() {
19+
isSubPattern*(result.getParentPattern().getFullyUnresolved(), this) and
20+
result.getName() = this.getName()
21+
}
22+
423
override string toString() { result = this.getName() }
524
}
25+
26+
private predicate isSubPattern(Pattern p, Pattern sub) {
27+
sub = p.(BindingPattern).getImmediateSubPattern()
28+
or
29+
sub = p.(EnumElementPattern).getImmediateSubPattern()
30+
or
31+
sub = p.(IsPattern).getImmediateSubPattern()
32+
or
33+
sub = p.(OptionalSomePattern).getImmediateSubPattern()
34+
or
35+
sub = p.(ParenPattern).getImmediateSubPattern()
36+
or
37+
sub = p.(TuplePattern).getImmediateElement(_)
38+
or
39+
sub = p.(TypedPattern).getImmediateSubPattern()
40+
}

swift/ql/test/extractor-tests/patterns/all.expected

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,3 +39,23 @@
3939
| patterns.swift:46:9:46:9 | b |
4040
| patterns.swift:49:10:49:10 | true |
4141
| patterns.swift:50:10:50:10 | false |
42+
| patterns.swift:55:9:55:9 | a |
43+
| patterns.swift:55:16:55:16 | b |
44+
| patterns.swift:55:23:55:23 | c |
45+
| patterns.swift:55:23:55:26 | ... as ... |
46+
| patterns.swift:57:8:57:20 | let ... |
47+
| patterns.swift:57:8:57:20 | let ...? |
48+
| patterns.swift:57:12:57:20 | (...) |
49+
| patterns.swift:57:13:57:13 | a |
50+
| patterns.swift:57:16:57:16 | b |
51+
| patterns.swift:57:19:57:19 | c |
52+
| patterns.swift:58:13:58:29 | (...) |
53+
| patterns.swift:58:14:58:14 | =~ ... |
54+
| patterns.swift:58:17:58:21 | let ... |
55+
| patterns.swift:58:21:58:21 | b |
56+
| patterns.swift:58:24:58:28 | let ... |
57+
| patterns.swift:58:28:58:28 | c |
58+
| patterns.swift:61:14:61:14 | =~ ... |
59+
| patterns.swift:62:14:62:18 | let ... |
60+
| patterns.swift:62:18:62:18 | c |
61+
| patterns.swift:63:9:63:9 | _ |
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
bound
2+
| patterns.swift:2:9:2:9 | an_int |
3+
| patterns.swift:3:9:3:9 | a_string |
4+
| patterns.swift:4:10:4:10 | x |
5+
| patterns.swift:4:13:4:13 | y |
6+
| patterns.swift:4:16:4:16 | z |
7+
| patterns.swift:10:9:10:9 | point |
8+
| patterns.swift:12:15:12:15 | xx |
9+
| patterns.swift:12:19:12:19 | yy |
10+
| patterns.swift:24:9:24:9 | v |
11+
| patterns.swift:28:19:28:19 | i |
12+
| patterns.swift:28:22:28:22 | s |
13+
| patterns.swift:31:9:31:9 | w |
14+
| patterns.swift:34:14:34:14 | n |
15+
| patterns.swift:38:9:38:9 | a |
16+
| patterns.swift:42:14:42:14 | x |
17+
| patterns.swift:46:9:46:9 | b |
18+
| patterns.swift:55:9:55:9 | a |
19+
| patterns.swift:55:16:55:16 | b |
20+
| patterns.swift:55:23:55:23 | c |
21+
| patterns.swift:57:13:57:13 | a |
22+
| patterns.swift:57:19:57:19 | c |
23+
| patterns.swift:58:21:58:21 | b |
24+
| patterns.swift:62:18:62:18 | c |
25+
unbound
26+
| patterns.swift:57:16:57:16 | b |
27+
| patterns.swift:58:28:58:28 | c |
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import swift
2+
3+
query predicate bound(NamedPattern p) {
4+
p.getFile().getBaseName() = "patterns.swift" and
5+
p.hasVarDecl()
6+
}
7+
8+
query predicate unbound(NamedPattern p) {
9+
p.getFile().getBaseName() = "patterns.swift" and
10+
not p.hasVarDecl()
11+
}

swift/ql/test/extractor-tests/patterns/patterns.swift

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,3 +50,16 @@ func switch_patterns() {
5050
case false: "false"
5151
}
5252
}
53+
54+
func bound_and_unbound() {
55+
let a = 1, b = 2, c: Int = 3
56+
57+
if let (a, b, c) = Optional.some((a, b, c)) { _ = (a, c) }
58+
if case (a, let b, let c) = (a, b, c) { _ = (b) }
59+
60+
switch a {
61+
case c: "equals c"
62+
case let c: "binds c"
63+
default: "default"
64+
}
65+
}

swift/ql/test/library-tests/ast/PrintAst.expected

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5509,6 +5509,95 @@ patterns.swift:
55095509
# 34| Type = Int
55105510
# 42| [ConcreteVarDecl] x
55115511
# 42| Type = String
5512+
# 54| [ConcreteFuncDecl] bound_and_unbound()
5513+
# 54| InterfaceType = () -> ()
5514+
# 54| getBody(): [BraceStmt] { ... }
5515+
# 55| getElement(0): [PatternBindingDecl] var ... = ...
5516+
# 55| getInit(0): [IntegerLiteralExpr] 1
5517+
# 55| getInit(1): [IntegerLiteralExpr] 2
5518+
# 55| getInit(2): [IntegerLiteralExpr] 3
5519+
# 55| getPattern(0): [NamedPattern] a
5520+
# 55| getPattern(1): [NamedPattern] b
5521+
# 55| getPattern(2): [TypedPattern] ... as ...
5522+
# 55| getSubPattern(): [NamedPattern] c
5523+
# 55| getTypeRepr(): [TypeRepr] Int
5524+
# 55| getElement(1): [ConcreteVarDecl] a
5525+
# 55| Type = Int
5526+
# 55| getElement(2): [ConcreteVarDecl] b
5527+
# 55| Type = Int
5528+
# 55| getElement(3): [ConcreteVarDecl] c
5529+
# 55| Type = Int
5530+
# 57| getElement(4): [IfStmt] if ... then { ... }
5531+
# 57| getCondition(): [StmtCondition] StmtCondition
5532+
# 57| getElement(0): [ConditionElement] let ...? = ...
5533+
# 57| getPattern(): [OptionalSomePattern] let ...?
5534+
# 57| getSubPattern(): [TuplePattern] (...)
5535+
# 57| getElement(0): [NamedPattern] a
5536+
# 57| getElement(1): [NamedPattern] b
5537+
# 57| getElement(2): [NamedPattern] c
5538+
# 57| getSubPattern().getFullyUnresolved(): [BindingPattern] let ...
5539+
# 57| getInitializer(): [CallExpr] call to ...
5540+
# 57| getFunction(): [MethodLookupExpr] .some
5541+
# 57| getBase(): [TypeExpr] Optional<(Int, Int, Int)>.Type
5542+
# 57| getTypeRepr(): [TypeRepr] Optional<(Int, Int, Int)>
5543+
# 57| getMethodRef(): [DeclRefExpr] some
5544+
# 57| getArgument(0): [Argument] : (...)
5545+
# 57| getExpr(): [TupleExpr] (...)
5546+
# 57| getElement(0): [DeclRefExpr] a
5547+
# 57| getElement(1): [DeclRefExpr] b
5548+
# 57| getElement(2): [DeclRefExpr] c
5549+
# 57| getThen(): [BraceStmt] { ... }
5550+
# 57| getElement(0): [AssignExpr] ... = ...
5551+
# 57| getDest(): [DiscardAssignmentExpr] _
5552+
# 57| getSource(): [TupleExpr] (...)
5553+
# 57| getElement(0): [DeclRefExpr] a
5554+
# 57| getElement(1): [DeclRefExpr] c
5555+
# 58| getElement(5): [IfStmt] if ... then { ... }
5556+
# 58| getCondition(): [StmtCondition] StmtCondition
5557+
# 58| getElement(0): [ConditionElement] (...) = ...
5558+
# 58| getPattern(): [TuplePattern] (...)
5559+
# 58| getElement(0): [ExprPattern] =~ ...
5560+
# 58| getSubExpr(): [DeclRefExpr] a
5561+
# 58| getElement(1): [NamedPattern] b
5562+
# 58| getElement(1).getFullyUnresolved(): [BindingPattern] let ...
5563+
# 58| getElement(2): [NamedPattern] c
5564+
# 58| getElement(2).getFullyUnresolved(): [BindingPattern] let ...
5565+
# 58| getInitializer(): [TupleExpr] (...)
5566+
# 58| getElement(0): [DeclRefExpr] a
5567+
# 58| getElement(1): [DeclRefExpr] b
5568+
# 58| getElement(2): [DeclRefExpr] c
5569+
# 58| getThen(): [BraceStmt] { ... }
5570+
# 58| getElement(0): [AssignExpr] ... = ...
5571+
# 58| getDest(): [DiscardAssignmentExpr] _
5572+
# 58| getSource(): [DeclRefExpr] b
5573+
# 58| getSource().getFullyConverted(): [ParenExpr] (...)
5574+
# 60| getElement(6): [SwitchStmt] switch a { ... }
5575+
# 60| getExpr(): [DeclRefExpr] a
5576+
# 61| getCase(0): [CaseStmt] case ...
5577+
# 61| getBody(): [BraceStmt] { ... }
5578+
# 61| getElement(0): [StringLiteralExpr] equals c
5579+
# 61| getLabel(0): [CaseLabelItem] =~ ...
5580+
# 61| getPattern(): [ExprPattern] =~ ...
5581+
# 61| getSubExpr(): [DeclRefExpr] c
5582+
# 62| getCase(1): [CaseStmt] case ...
5583+
# 62| getBody(): [BraceStmt] { ... }
5584+
# 62| getElement(0): [StringLiteralExpr] binds c
5585+
# 62| getLabel(0): [CaseLabelItem] c
5586+
# 62| getPattern(): [NamedPattern] c
5587+
# 62| getPattern().getFullyUnresolved(): [BindingPattern] let ...
5588+
# 63| getCase(2): [CaseStmt] case ...
5589+
# 63| getBody(): [BraceStmt] { ... }
5590+
# 63| getElement(0): [StringLiteralExpr] default
5591+
# 63| getLabel(0): [CaseLabelItem] _
5592+
# 63| getPattern(): [AnyPattern] _
5593+
# 57| [ConcreteVarDecl] a
5594+
# 57| Type = Int
5595+
# 57| [ConcreteVarDecl] c
5596+
# 57| Type = Int
5597+
# 58| [ConcreteVarDecl] b
5598+
# 58| Type = Int
5599+
# 62| [ConcreteVarDecl] c
5600+
# 62| Type = Int
55125601
statements.swift:
55135602
# 1| [ConcreteFuncDecl] loop()
55145603
# 1| InterfaceType = () -> ()

swift/ql/test/library-tests/ast/patterns.swift

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,3 +50,16 @@ func switch_patterns() {
5050
case false: "false"
5151
}
5252
}
53+
54+
func bound_and_unbound() {
55+
let a = 1, b = 2, c: Int = 3
56+
57+
if let (a, b, c) = Optional.some((a, b, c)) { _ = (a, c) }
58+
if case (a, let b, let c) = (a, b, c) { _ = (b) }
59+
60+
switch a {
61+
case c: "equals c"
62+
case let c: "binds c"
63+
default: "default"
64+
}
65+
}

0 commit comments

Comments
 (0)