Skip to content

Commit 597d12c

Browse files
committed
Unified: Introduce CallableEx and ParameterEx
1 parent 22e050f commit 597d12c

6 files changed

Lines changed: 470 additions & 0 deletions

File tree

Lines changed: 323 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,323 @@
1+
/**
2+
* Provides logic for synthesizing callables and parameters.
3+
*/
4+
5+
private import unified
6+
private import CallableExPlugin
7+
private import NameBinding
8+
9+
private predicate mayHaveImplicitConstructor(ClassLikeDeclaration cls, string name) {
10+
mayHaveConstructor(cls, name) and
11+
not cls.getAMember() instanceof ConstructorDeclaration
12+
}
13+
14+
private predicate mayInheritConstructorFrom(ClassLikeDeclaration cls, ClassLikeDeclaration base) {
15+
mayHaveImplicitConstructor(cls, _) and
16+
base.getNameNode() = getStaticBindingTargetFromRef(cls.getABaseType().getType())
17+
}
18+
19+
private newtype TCallableEx =
20+
TAstCallableEx(Callable c) or
21+
TDefaultConstructor(ClassLikeDeclaration cls, string name) {
22+
mayHaveImplicitConstructor(cls, name) and
23+
forall(ClassLikeDeclaration base | mayInheritConstructorFrom(cls, base) |
24+
not mayHaveConstructor(cls, _)
25+
)
26+
} or
27+
TInheritedConstructor(ClassLikeDeclaration cls, CallableEx baseCtor) {
28+
exists(ClassLikeDeclaration baseClass |
29+
mayInheritConstructorFrom(cls, baseClass) and
30+
baseCtor.isConstructor(baseClass)
31+
)
32+
}
33+
34+
private newtype TParameterEx =
35+
TAstParameter(Parameter p) or
36+
TImplicitReceiverParameter(Callable c) {
37+
exists(LocalVariable v | v.isImplicitReceiverParameter(c))
38+
} or
39+
TDefaultConstructorParameter(ClassLikeDeclaration cls, int i, string name) {
40+
defaultConstructorParameter(cls, i, name)
41+
} or
42+
TInheritedConstructorParameter(CallableEx ctor, CallableEx baseCtor, int i) {
43+
ctor.isInheritedConstructor(_, baseCtor) and
44+
exists(baseCtor.getParameter(i))
45+
}
46+
47+
final class CallableEx = CallableExImpl;
48+
49+
/**
50+
* A callable from source code or a synthesized callable.
51+
*/
52+
abstract private class CallableExImpl extends TCallableEx {
53+
/**
54+
* Gets the source code callable that this entity represents, if any.
55+
*/
56+
Callable asCallable() { this = TAstCallableEx(result) }
57+
58+
/**
59+
* Holds if this entity represents a default constructor for `cls`, for example a
60+
* parameterless Swift `class` constructor or a Swift `struct` constructor
61+
* with parameters for relevant fields.
62+
*/
63+
predicate isDefaultConstructor(ClassLikeDeclaration cls) { this = TDefaultConstructor(cls, _) }
64+
65+
/**
66+
* Holds if this entity represents an inherited constructor for `cls` from the
67+
* base constructor `baseCtor` (which may itself be inherited).
68+
*/
69+
predicate isInheritedConstructor(ClassLikeDeclaration cls, CallableEx baseCtor) {
70+
this = TInheritedConstructor(cls, baseCtor)
71+
}
72+
73+
/**
74+
* Holds if this entity represents a constructor for `cls`.
75+
*/
76+
predicate isConstructor(ClassLikeDeclaration cls) {
77+
this.asCallable().(ConstructorDeclaration) = cls.getAMember()
78+
or
79+
this.isDefaultConstructor(cls)
80+
or
81+
this.isInheritedConstructor(cls, _)
82+
}
83+
84+
/**
85+
* Holds if this entity is a direct member of `cls`.
86+
*/
87+
predicate isMemberOf(ClassLikeDeclaration cls) {
88+
this.asCallable() = cls.getAMember()
89+
or
90+
this.isConstructor(cls)
91+
}
92+
93+
/** Gets the `i`-th type parameter of this callable. */
94+
abstract TypeParameter getTypeParameter(int i);
95+
96+
/**
97+
* Gets the `i`-th parameter of this callable. Implicit receiver parameters are
98+
* at index `0`, and all other parameters start at index `1`.
99+
*/
100+
abstract ParameterEx getParameter(int i);
101+
102+
/** Gets a parameter of this callable. */
103+
final ParameterEx getAParameter() { result = this.getParameter(_) }
104+
105+
/** Gets the declared return type of this callable, if any. */
106+
abstract Expr getReturnType();
107+
108+
/** Gets the body of this callable, if any. */
109+
abstract AstNode getBody();
110+
111+
abstract Identifier getNameNode();
112+
113+
/** Gets the name of this callable. */
114+
abstract string getName();
115+
116+
/** Gets a textual representation of this callable. */
117+
abstract string toString();
118+
119+
/** Gets the location of this callable. */
120+
abstract Location getLocation();
121+
}
122+
123+
private class AstCallableEx extends CallableExImpl, TAstCallableEx {
124+
Callable c;
125+
126+
AstCallableEx() { this = TAstCallableEx(c) }
127+
128+
override TypeParameter getTypeParameter(int i) {
129+
result = c.(FunctionDeclaration).getTypeParameter(i)
130+
}
131+
132+
override ParameterEx getParameter(int i) {
133+
result.isImplicitReceiverParameter(c) and
134+
i = 0
135+
or
136+
exists(int j, Parameter p |
137+
result.asParameter() = p and
138+
j = i - 1
139+
|
140+
p = c.(FunctionDeclaration).getParameter(j)
141+
or
142+
p = c.(ConstructorDeclaration).getParameter(j)
143+
or
144+
p = c.(FunctionExpr).getParameter(j)
145+
)
146+
}
147+
148+
override Expr getReturnType() {
149+
result = c.(FunctionDeclaration).getReturnType()
150+
or
151+
result = c.(FunctionExpr).getReturnType()
152+
}
153+
154+
override AstNode getBody() { result = c.getBody() }
155+
156+
override Identifier getNameNode() {
157+
result = c.(AccessorDeclaration).getNameNode()
158+
or
159+
result = c.(ConstructorDeclaration).getNameNode()
160+
or
161+
result = c.(FunctionDeclaration).getNameNode()
162+
}
163+
164+
override string getName() {
165+
result = this.getNameNode().getValue()
166+
or
167+
c instanceof DestructorDeclaration and
168+
result = "<destructor>"
169+
or
170+
c instanceof InitializerDeclaration and
171+
result = "<initializer>"
172+
}
173+
174+
override string toString() { result = this.getName() }
175+
176+
override Location getLocation() { result = c.getLocation() }
177+
}
178+
179+
private class DefaultConstructor extends CallableExImpl, TDefaultConstructor {
180+
ClassLikeDeclaration c;
181+
string name;
182+
183+
DefaultConstructor() { this = TDefaultConstructor(c, name) }
184+
185+
override TypeParameter getTypeParameter(int i) { none() }
186+
187+
override ParameterEx getParameter(int i) { result = TDefaultConstructorParameter(c, i, _) }
188+
189+
override Expr getReturnType() { none() }
190+
191+
override AstNode getBody() { none() }
192+
193+
override Identifier getNameNode() { none() }
194+
195+
override string getName() { result = name }
196+
197+
override string toString() { result = c.getName() + " [default constructor]" }
198+
199+
override Location getLocation() { result = c.getLocation() }
200+
}
201+
202+
private class InheritedConstructor extends CallableExImpl, TInheritedConstructor {
203+
ClassLikeDeclaration cls;
204+
CallableEx baseCtor;
205+
206+
InheritedConstructor() { this = TInheritedConstructor(cls, baseCtor) }
207+
208+
override TypeParameter getTypeParameter(int i) { none() }
209+
210+
override ParameterEx getParameter(int i) {
211+
result.isInheritedConstructorParameter(this, baseCtor, i)
212+
}
213+
214+
override Expr getReturnType() { none() }
215+
216+
override AstNode getBody() { none() }
217+
218+
override Identifier getNameNode() { none() }
219+
220+
override string getName() { result = baseCtor.getName() }
221+
222+
override string toString() {
223+
exists(ClassLikeDeclaration baseCls |
224+
baseCtor.isMemberOf(baseCls) and
225+
result = cls.getName() + " [inherited from " + baseCls.getName() + "]"
226+
)
227+
}
228+
229+
override Location getLocation() { result = cls.getLocation() }
230+
}
231+
232+
final class ParameterEx = ParameterExImpl;
233+
234+
/**
235+
* A parameter from source code or a synthesized parameter.
236+
*/
237+
abstract private class ParameterExImpl extends TParameterEx {
238+
/**
239+
* Gets the source code parameter that this entity represents, if any.
240+
*/
241+
Parameter asParameter() { this = TAstParameter(result) }
242+
243+
/**
244+
* Holds if this entity represents an implicit receiver parameter of `c`.
245+
*/
246+
predicate isImplicitReceiverParameter(Callable c) { this = TImplicitReceiverParameter(c) }
247+
248+
predicate isDefaultConstructorParameter(ClassLikeDeclaration cls, int i, string name) {
249+
this = TDefaultConstructorParameter(cls, i, name)
250+
}
251+
252+
/**
253+
* Holds if this entity represents the `i`th parameter of the inherited constructor
254+
* `ctor` where `baseCtor` is the base constructor.
255+
*/
256+
predicate isInheritedConstructorParameter(CallableEx ctor, CallableEx baseCtor, int i) {
257+
this = TInheritedConstructorParameter(ctor, baseCtor, i)
258+
}
259+
260+
/** Gets a textual representation of this parameter. */
261+
abstract string toString();
262+
263+
/** Gets the location of this parameter. */
264+
abstract Location getLocation();
265+
}
266+
267+
private class AstParameterEx extends ParameterExImpl, TAstParameter {
268+
Parameter p;
269+
270+
AstParameterEx() { this = TAstParameter(p) }
271+
272+
override string toString() { result = p.toString() }
273+
274+
override Location getLocation() { result = p.getLocation() }
275+
}
276+
277+
private class ImplicitReceiverParameterEx extends ParameterExImpl, TImplicitReceiverParameter {
278+
Callable c;
279+
280+
ImplicitReceiverParameterEx() { this = TImplicitReceiverParameter(c) }
281+
282+
override string toString() {
283+
exists(LocalVariable v |
284+
v.isImplicitReceiverParameter(c) and
285+
result = v.getName()
286+
)
287+
}
288+
289+
override Location getLocation() { result = c.getLocation() }
290+
}
291+
292+
private class DefaultConstructorParameterEx extends ParameterExImpl, TDefaultConstructorParameter {
293+
ClassLikeDeclaration c;
294+
int i;
295+
string name;
296+
297+
DefaultConstructorParameterEx() { this = TDefaultConstructorParameter(c, i, name) }
298+
299+
override string toString() { result = name + " [" + c.getName() + " default constructor]" }
300+
301+
override Location getLocation() { result = c.getLocation() }
302+
}
303+
304+
private class InheritedConstructorParameterEx extends ParameterExImpl,
305+
TInheritedConstructorParameter
306+
{
307+
CallableEx ctor;
308+
CallableEx baseCtor;
309+
int i;
310+
311+
InheritedConstructorParameterEx() { this = TInheritedConstructorParameter(ctor, baseCtor, i) }
312+
313+
override string toString() {
314+
exists(ParameterEx baseParam, ClassLikeDeclaration baseCls |
315+
baseCtor.isMemberOf(baseCls) and
316+
baseParam = baseCtor.getParameter(i) and
317+
result =
318+
"parameter " + i + " of " + ctor.getName() + " [inherited from " + baseCls.getName() + "]"
319+
)
320+
}
321+
322+
override Location getLocation() { result = ctor.getLocation() }
323+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
private import unified
2+
private import codeql.util.Unit
3+
4+
private module Plugins {
5+
private import codeql.unified.internal.CallableExPluginSwift
6+
}
7+
8+
/** Extension point for language-specific inputs to `CallableEx.qll`. */
9+
class CallableExPlugin extends Unit {
10+
/**
11+
* Holds if the class-like declaration `cls` may have a constructor
12+
* with the given name.
13+
*/
14+
predicate mayHaveConstructor(ClassLikeDeclaration cls, string name) { none() }
15+
16+
/**
17+
* Holds if a default constructor for `cls` would need to have a parameter
18+
* at index `i` with the given name. `i = 0` should be the implicit receiver
19+
* parameter.
20+
*/
21+
bindingset[cls]
22+
predicate defaultConstructorParameter(ClassLikeDeclaration cls, int i, string name) { none() }
23+
}
24+
25+
predicate mayHaveConstructor(ClassLikeDeclaration cls, string name) {
26+
any(CallableExPlugin p).mayHaveConstructor(cls, name)
27+
}
28+
29+
bindingset[cls]
30+
predicate defaultConstructorParameter(ClassLikeDeclaration cls, int i, string name) {
31+
any(CallableExPlugin p).defaultConstructorParameter(cls, i, name)
32+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
private import unified
2+
private import codeql.unified.internal.CallableExPlugin
3+
4+
class CallableExPluginSwift extends CallableExPlugin {
5+
override predicate mayHaveConstructor(ClassLikeDeclaration cls, string name) {
6+
cls.hasModifier(["actor", "class", "struct"]) and
7+
name = "init"
8+
}
9+
10+
bindingset[cls]
11+
override predicate defaultConstructorParameter(ClassLikeDeclaration cls, int i, string name) {
12+
i = 0 and
13+
name = "self"
14+
or
15+
cls.hasModifier("struct") and
16+
name =
17+
rank[i](VariableDeclaration decl, int j, string s |
18+
decl = cls.getMember(j) and
19+
(
20+
// if `decl` has an initializer then this parameter has that initalizer as a default value
21+
decl.hasModifier("var")
22+
or
23+
decl.hasModifier("let") and
24+
not exists(decl.getValue())
25+
) and
26+
s = decl.getPattern().(Identifier).getValue()
27+
|
28+
s order by j
29+
)
30+
}
31+
}

0 commit comments

Comments
 (0)