Skip to content

Commit 8cebd55

Browse files
authored
Merge pull request #22725 from hvitved/unified/param-ex
Unified: Introduce `ParameterEx`
2 parents 1912c4a + 3544f46 commit 8cebd55

6 files changed

Lines changed: 199 additions & 1 deletion

File tree

‎unified/ql/lib/codeql/unified/internal/AstPlugin.qll‎

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,13 @@ class AstPlugin extends Unit {
2020

2121
bindingset[decl]
2222
string getVariableDeclarationKeyword(VariableDeclaration decl) { none() }
23+
24+
/**
25+
* Holds if the default constructor `cd` needs a parameter at index `i` with
26+
* the given name. `i = 0` is reserved for the implicit receiver parameter.
27+
*/
28+
bindingset[cd]
29+
predicate defaultConstructorParameter(ConstructorDeclaration cd, int i, string name) { none() }
2330
}
2431

2532
bindingset[f]
@@ -41,3 +48,8 @@ bindingset[decl]
4148
string getVariableDeclarationKeyword(VariableDeclaration decl) {
4249
result = any(AstPlugin p).getVariableDeclarationKeyword(decl)
4350
}
51+
52+
bindingset[cd]
53+
predicate defaultConstructorParameter(ConstructorDeclaration cd, int i, string name) {
54+
any(AstPlugin p).defaultConstructorParameter(cd, i, name)
55+
}

‎unified/ql/lib/codeql/unified/internal/AstPluginSwift.qll‎

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,12 @@ private class AstPluginSwift extends AstPlugin {
1010
exists(f) and result = "func"
1111
}
1212

13+
bindingset[c]
14+
override string getConstructorDeclarationKeyword(ConstructorDeclaration c) {
15+
c.hasModifier(result) and
16+
result = "convenience"
17+
}
18+
1319
override string getClassLikeDeclarationKeyword(ClassLikeDeclaration cls) {
1420
cls.hasModifier(result) and
1521
result in ["class", "struct", "enum", "actor", "extension", "protocol"]
@@ -19,4 +25,27 @@ private class AstPluginSwift extends AstPlugin {
1925
decl.hasModifier(result) and
2026
result in ["var", "let"]
2127
}
28+
29+
bindingset[cd]
30+
override predicate defaultConstructorParameter(ConstructorDeclaration cd, int i, string name) {
31+
exists(ClassLikeDeclaration cls |
32+
cd = cls.getAMember() and
33+
cls.hasModifier("struct") and
34+
name =
35+
rank[i](VariableDeclaration decl, int j, string s |
36+
decl = cls.getMember(j) and
37+
not decl.hasModifier("static") and
38+
(
39+
// if `decl` has an initializer then this parameter has that initializer as a default value
40+
decl.hasModifier("var")
41+
or
42+
decl.hasModifier("let") and
43+
not exists(decl.getValue())
44+
) and
45+
s = decl.getPattern().(Identifier).getValue()
46+
|
47+
s order by j
48+
)
49+
)
50+
}
2251
}
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
/**
2+
* Provides logic for synthesizing parameters.
3+
*/
4+
5+
private import unified
6+
private import AstPlugin
7+
private import NameBinding
8+
9+
private newtype TParameterEx =
10+
TAstParameter(Parameter p) or
11+
TImplicitReceiverParameter(Callable c) {
12+
exists(LocalVariable v | v.isImplicitReceiverParameter(c))
13+
} or
14+
TDefaultConstructorParameter(ConstructorDeclaration cd, int i, string name) {
15+
cd.hasModifier("generated") and
16+
defaultConstructorParameter(cd, i, name)
17+
}
18+
19+
final class ParameterEx = ParameterExImpl;
20+
21+
/**
22+
* A parameter from source code or a synthesized parameter.
23+
*/
24+
abstract private class ParameterExImpl extends TParameterEx {
25+
/**
26+
* Gets the source code parameter that this entity represents, if any.
27+
*/
28+
Parameter asParameter() { this = TAstParameter(result) }
29+
30+
/**
31+
* Holds if this entity represents a regular parameter from source code.
32+
*/
33+
predicate isParameter() { exists(this.asParameter()) }
34+
35+
/**
36+
* Holds if this entity represents an implicit receiver parameter of `c`.
37+
*/
38+
predicate isImplicitReceiverParameter(Callable c) { this = TImplicitReceiverParameter(c) }
39+
40+
/**
41+
* Holds if this entity represents the `i`th parameter of the default
42+
* constructor `cd` with name `name`.
43+
*/
44+
predicate isDefaultConstructorParameter(ConstructorDeclaration cd, int i, string name) {
45+
this = TDefaultConstructorParameter(cd, i, name)
46+
}
47+
48+
/** Gets the callable that this parameter belongs to. */
49+
Callable getCallable() {
50+
this.asParameter() =
51+
[
52+
result.(FunctionDeclaration).getAParameter(),
53+
result.(ConstructorDeclaration).getAParameter(),
54+
result.(AccessorDeclaration).getAParameter(),
55+
result.(FunctionExpr).getAParameter()
56+
]
57+
or
58+
this.isImplicitReceiverParameter(result)
59+
or
60+
this.isDefaultConstructorParameter(result, _, _)
61+
}
62+
63+
/** Gets a textual representation of this parameter. */
64+
abstract string toString();
65+
66+
/** Gets the location of this parameter. */
67+
abstract Location getLocation();
68+
}
69+
70+
private class AstParameterEx extends ParameterExImpl, TAstParameter {
71+
Parameter p;
72+
73+
AstParameterEx() { this = TAstParameter(p) }
74+
75+
override string toString() { result = p.toString() }
76+
77+
override Location getLocation() { result = p.getLocation() }
78+
}
79+
80+
private class ImplicitReceiverParameterEx extends ParameterExImpl, TImplicitReceiverParameter {
81+
Callable c;
82+
83+
ImplicitReceiverParameterEx() { this = TImplicitReceiverParameter(c) }
84+
85+
override string toString() {
86+
exists(LocalVariable v |
87+
v.isImplicitReceiverParameter(c) and
88+
result = v.getName()
89+
)
90+
}
91+
92+
override Location getLocation() { result = c.getLocation() }
93+
}
94+
95+
private class DefaultConstructorParameterEx extends ParameterExImpl, TDefaultConstructorParameter {
96+
ConstructorDeclaration cd;
97+
int i;
98+
string name;
99+
100+
DefaultConstructorParameterEx() { this = TDefaultConstructorParameter(cd, i, name) }
101+
102+
override string toString() { result = name + " [" + cd.getName() + " default constructor]" }
103+
104+
override Location getLocation() { result = cd.getLocation() }
105+
}

‎unified/ql/test/library-tests/constructors/constructors.swift‎

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,10 +36,26 @@ class C8: C7<Int, String> { // inherits `init(x: String, y: Int)`
3636
}
3737
}
3838

39-
struct S1 { // implicit `init(f1: Int = 0, f2: String, f4: Double)`
39+
struct S1 { // implicit `init(f1: Int = 0, f2: String, f4: Double, f6: Double, f7: Double = 0)`
4040
var f1: Int = 0
4141
var f2: String
4242
let f3: Bool = false
4343
let f4: Double
4444
static var f5: Float = 0.0
45+
var f6: Double { // todo: unlike `f7`, this is not extracted as a `VariableDeclaration`
46+
willSet {
47+
print("Changing from \(f6) to \(newValue)")
48+
}
49+
didSet {
50+
print("Changed from \(oldValue) to \(f6)")
51+
}
52+
}
53+
var f7: Double = 0.0 {
54+
willSet {
55+
print("Changing from \(f7) to \(newValue)")
56+
}
57+
didSet {
58+
print("Changed from \(oldValue) to \(f7)")
59+
}
60+
}
4561
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,28 @@
1+
generatedConstructor
12
| constructors.swift:1:1:1:0 | init |
23
| constructors.swift:3:1:3:0 | init |
34
| constructors.swift:19:1:19:0 | init |
45
| constructors.swift:31:1:31:0 | init |
56
| constructors.swift:39:1:39:0 | init |
7+
implicitReceiverParameter
8+
| constructors.swift:1:1:1:0 | self | constructors.swift:1:1:1:0 | init |
9+
| constructors.swift:3:1:3:0 | self | constructors.swift:3:1:3:0 | init |
10+
| constructors.swift:6:5:6:13 | self | constructors.swift:6:5:6:13 | init |
11+
| constructors.swift:10:5:12:5 | self | constructors.swift:10:5:12:5 | init |
12+
| constructors.swift:14:5:16:5 | self | constructors.swift:14:5:16:5 | convenience init |
13+
| constructors.swift:19:1:19:0 | self | constructors.swift:19:1:19:0 | init |
14+
| constructors.swift:22:5:22:25 | self | constructors.swift:22:5:22:25 | init |
15+
| constructors.swift:24:5:24:30 | self | constructors.swift:24:5:24:30 | init |
16+
| constructors.swift:26:5:28:5 | self | constructors.swift:26:5:28:5 | convenience init |
17+
| constructors.swift:31:1:31:0 | self | constructors.swift:31:1:31:0 | init |
18+
| constructors.swift:34:5:36:5 | self | constructors.swift:34:5:36:5 | convenience init |
19+
| constructors.swift:39:1:39:0 | self | constructors.swift:39:1:39:0 | init |
20+
| constructors.swift:46:9:48:9 | self | constructors.swift:46:9:48:9 | AccessorDeclaration |
21+
| constructors.swift:49:9:51:9 | self | constructors.swift:49:9:51:9 | AccessorDeclaration |
22+
| constructors.swift:54:9:56:9 | self | constructors.swift:54:9:56:9 | AccessorDeclaration |
23+
| constructors.swift:57:9:59:9 | self | constructors.swift:57:9:59:9 | AccessorDeclaration |
24+
callableExDefaultConstructorParameter
25+
| constructors.swift:39:1:39:0 | init | 1 | constructors.swift:39:1:39:0 | f1 [init default constructor] | f1 |
26+
| constructors.swift:39:1:39:0 | init | 2 | constructors.swift:39:1:39:0 | f2 [init default constructor] | f2 |
27+
| constructors.swift:39:1:39:0 | init | 3 | constructors.swift:39:1:39:0 | f4 [init default constructor] | f4 |
28+
| constructors.swift:39:1:39:0 | init | 4 | constructors.swift:39:1:39:0 | f7 [init default constructor] | f7 |
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,19 @@
11
import unified
2+
import codeql.unified.internal.ParameterEx
23

34
query predicate generatedConstructor(ConstructorDeclaration c) {
45
c = any(ClassLikeDeclaration cls | cls.fromSource()).getAMember() and
56
c.hasModifier("generated")
67
}
8+
9+
query predicate implicitReceiverParameter(ParameterEx p, Callable c) {
10+
p.isImplicitReceiverParameter(c) and
11+
c.getFile().fromSource()
12+
}
13+
14+
query predicate callableExDefaultConstructorParameter(
15+
ConstructorDeclaration cd, int i, ParameterEx p, string name
16+
) {
17+
p.isDefaultConstructorParameter(cd, i, name) and
18+
cd.getFile().fromSource()
19+
}

0 commit comments

Comments
 (0)