Skip to content

Commit 2b8bcc0

Browse files
committed
Unified: Introduce CallableEx and ParameterEx
1 parent 605dc1c commit 2b8bcc0

6 files changed

Lines changed: 522 additions & 0 deletions

File tree

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

0 commit comments

Comments
 (0)