Skip to content

Commit 3508a4b

Browse files
authored
Merge pull request github#12016 from erik-krogh/newEntity
QL: support the NewEntity module in QL-for-QL
2 parents 6a8c570 + 54c4c23 commit 3508a4b

File tree

3 files changed

+155
-9
lines changed

3 files changed

+155
-9
lines changed

ql/ql/src/codeql_ql/ast/internal/Builtins.qll

Lines changed: 104 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,12 @@ module QlBuiltinsMocks {
115115
or
116116
i = 2 and
117117
result instanceof EquivalenceRelation::EquivalenceRelationModule
118+
or
119+
i = 3 and
120+
result instanceof NewEntity::EntityKeySigClass
121+
or
122+
i = 4 and
123+
result instanceof NewEntity::NewEntityModule
118124
}
119125
}
120126

@@ -123,15 +129,16 @@ module QlBuiltinsMocks {
123129
* The equivalent to the following is implemented:
124130
* ```CodeQL
125131
* module QlBuiltins {
126-
* signature class T;
127-
* module EdgeSig<T MyT> { // This might not be needed.
128-
* signature predicate edgeSig(MyT a, MyT b);
129-
* }
130-
* module EquivalenceRelation<T MyT, EdgeSig<MyT>::edgeSig/2 edge> { // the `edge` parameter is not modeled
131-
* class EquivalenceClass;
132-
* EquivalenceClass getEquivalenceClass(MyT a);
133-
* }
134-
*}
132+
* signature class T;
133+
* module EdgeSig<T MyT> { // This might not be needed.
134+
* signature predicate edgeSig(MyT a, MyT b);
135+
* }
136+
* module EquivalenceRelation<T MyT, EdgeSig<MyT>::edgeSig/2 edge> { // the `edge` parameter is not modeled
137+
* class EquivalenceClass;
138+
* EquivalenceClass getEquivalenceClass(MyT a);
139+
* }
140+
* }
141+
* ```
135142
*/
136143
module EquivalenceRelation {
137144
class SigClass extends MockClass::Range {
@@ -259,4 +266,92 @@ module QlBuiltinsMocks {
259266
override string getClassName() { result = "EquivalenceClass" }
260267
}
261268
}
269+
270+
/**
271+
* A mock that implements the `NewEntity` module.
272+
* The equivalent to the following is implemented:
273+
* ```CodeQL
274+
* class EntityKeySig;
275+
* module NewEntity<EntityKeySig EntityKey> {
276+
* class EntityId;
277+
*
278+
* EntityId map(EntityKey key) { none() }
279+
* }
280+
* ```
281+
*/
282+
module NewEntity {
283+
class EntityKeySigClass extends MockClass::Range {
284+
EntityKeySigClass() { this = "Mock: QlBuiltins::NewEntity::EntityKeySig" }
285+
286+
override string getName() { result = "EntityKeySig" }
287+
}
288+
289+
class NewEntityModule extends MockModule::Range {
290+
NewEntityModule() { this = "Mock: QlBuiltins::NewEntity" }
291+
292+
override string getName() { result = "NewEntity" }
293+
294+
override string getMember(int i) {
295+
i = 0 and result instanceof EntityIdClass
296+
or
297+
i = 1 and result instanceof NewEntityMapPredicate
298+
}
299+
300+
/// Holds if the `i`th type parameter has `type` (the ID of the mocked node) with `name`.
301+
override predicate hasTypeParam(int i, string type, string name) {
302+
i = 0 and
303+
name = "EntityKey" and
304+
type instanceof EntityKeySigTypeExpr
305+
}
306+
}
307+
308+
class EntityKeySigTypeExpr extends MockTypeExpr::Range {
309+
EntityKeySigTypeExpr() { this = "Mock: QlBuiltins::NewEntity::EntityKey" }
310+
311+
override string getClassName() { result = "EntityKeySig" }
312+
}
313+
314+
class EntityIdClass extends MockClass::Range {
315+
EntityIdClass() { this = "Mock: QlBuiltins::NewEntity::EntityId" }
316+
317+
override string getName() { result = "EntityId" }
318+
}
319+
320+
class NewEntityMapPredicate extends MockClasslessPredicate::Range {
321+
NewEntityMapPredicate() { this = "Mock: QlBuiltins::NewEntity::map" }
322+
323+
override string getName() { result = "map" }
324+
325+
override string getParameter(int i) {
326+
i = 0 and
327+
result instanceof NewEntityMapPredicateParam
328+
}
329+
330+
override MockTypeExpr::Range getReturnTypeExpr() {
331+
result.(NewEntityMapPredicateTypes).getClassName() = "EntityId"
332+
}
333+
}
334+
335+
// both the TypeExprs used in the `map` predicate.
336+
class NewEntityMapPredicateTypes extends MockTypeExpr::Range {
337+
string type;
338+
339+
NewEntityMapPredicateTypes() {
340+
type = ["EntityId", "EntityKey"] and
341+
this = "Mock: QlBuiltins::NewEntity::map::T#" + type
342+
}
343+
344+
override string getClassName() { result = type }
345+
}
346+
347+
class NewEntityMapPredicateParam extends MockVarDecl::Range {
348+
NewEntityMapPredicateParam() { this = "Mock: QlBuiltins::NewEntity::map::#0" }
349+
350+
override string getName() { result = "key" }
351+
352+
override MockTypeExpr::Range getType() {
353+
result.(NewEntityMapPredicateTypes).getClassName() = "EntityKey"
354+
}
355+
}
356+
}
262357
}

ql/ql/test/modules/Foo.qll

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,3 +34,27 @@ private class TypeFlowScc = Scc::EquivalenceClass;
3434
predicate sccRepr(Node n, TypeFlowScc scc) { scc = Scc::getEquivalenceClass(n) }
3535

3636
predicate sccJoinStep(Node n, TypeFlowScc scc) { none() }
37+
38+
module NewEntity {
39+
newtype TFoo = TFoo1()
40+
41+
newtype EntityKey =
42+
Key1() or
43+
Key2()
44+
45+
// this errors out in normal QL, but QL-for-QL doesn't differentiate between upgrade scripts and "normal" code, and it also doesn't care if the number of type-parameters matches.
46+
// so this should work fine in QL-for-QL
47+
module NewEntityModule = QlBuiltins::NewEntity<EntityKey>;
48+
49+
class Union = TFoo or NewEntityModule::EntityId;
50+
51+
class Foo extends Union {
52+
string toString() { none() }
53+
}
54+
55+
predicate foo(Foo id, string message) {
56+
id = NewEntityModule::map(Key1()) and message = "upgrade-1"
57+
or
58+
id = NewEntityModule::map(Key2()) and message = "upgrade-2"
59+
}
60+
}

ql/ql/test/modules/test.expected

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,14 @@ getTarget
1414
| Foo.qll:31:29:31:31 | Scc | file://:0:0:0:0 | EquivalenceRelation |
1515
| Foo.qll:34:52:34:54 | Scc | Foo.qll:29:16:29:18 | Scc |
1616
| Foo.qll:34:52:34:54 | Scc | file://:0:0:0:0 | EquivalenceRelation |
17+
| Foo.qll:47:28:47:37 | QlBuiltins | file://:0:0:0:0 | QlBuiltins |
18+
| Foo.qll:47:28:47:59 | NewEntity | file://:0:0:0:0 | NewEntity |
19+
| Foo.qll:49:25:49:39 | NewEntityModule | Foo.qll:47:10:47:24 | NewEntityModule |
20+
| Foo.qll:49:25:49:39 | NewEntityModule | file://:0:0:0:0 | NewEntity |
21+
| Foo.qll:56:10:56:24 | NewEntityModule | Foo.qll:47:10:47:24 | NewEntityModule |
22+
| Foo.qll:56:10:56:24 | NewEntityModule | file://:0:0:0:0 | NewEntity |
23+
| Foo.qll:58:10:58:24 | NewEntityModule | Foo.qll:47:10:47:24 | NewEntityModule |
24+
| Foo.qll:58:10:58:24 | NewEntityModule | file://:0:0:0:0 | NewEntity |
1725
getTargetType
1826
| ClassSig.qll:3:23:3:28 | TypeExpr | file://:0:0:0:0 | string |
1927
| ClassSig.qll:7:12:7:17 | TypeExpr | ClassSig.qll:1:17:1:22 | FooSig |
@@ -44,6 +52,25 @@ getTargetType
4452
| Foo.qll:34:52:34:54 | Scc | file://:0:0:0:0 | EquivalenceRelation |
4553
| Foo.qll:36:23:36:26 | TypeExpr | Foo.qll:23:7:23:10 | Node |
4654
| Foo.qll:36:31:36:41 | TypeExpr | file://:0:0:0:0 | EquivalenceClass |
55+
| Foo.qll:47:28:47:37 | QlBuiltins | file://:0:0:0:0 | QlBuiltins |
56+
| Foo.qll:47:28:47:59 | NewEntity | file://:0:0:0:0 | NewEntity |
57+
| Foo.qll:47:50:47:58 | TypeExpr | Foo.qll:41:11:41:19 | EntityKey |
58+
| Foo.qll:49:17:49:20 | TypeExpr | Foo.qll:39:11:39:14 | TFoo |
59+
| Foo.qll:49:25:49:39 | NewEntityModule | Foo.qll:47:10:47:24 | NewEntityModule |
60+
| Foo.qll:49:25:49:39 | NewEntityModule | file://:0:0:0:0 | NewEntity |
61+
| Foo.qll:49:25:49:49 | TypeExpr | file://:0:0:0:0 | EntityId |
62+
| Foo.qll:51:21:51:25 | TypeExpr | Foo.qll:49:9:49:13 | Union |
63+
| Foo.qll:52:5:52:10 | TypeExpr | file://:0:0:0:0 | string |
64+
| Foo.qll:55:17:55:19 | TypeExpr | Foo.qll:51:9:51:11 | Foo |
65+
| Foo.qll:55:25:55:30 | TypeExpr | file://:0:0:0:0 | string |
66+
| Foo.qll:56:10:56:24 | NewEntityModule | Foo.qll:47:10:47:24 | NewEntityModule |
67+
| Foo.qll:56:10:56:24 | NewEntityModule | file://:0:0:0:0 | NewEntity |
68+
| Foo.qll:58:10:58:24 | NewEntityModule | Foo.qll:47:10:47:24 | NewEntityModule |
69+
| Foo.qll:58:10:58:24 | NewEntityModule | file://:0:0:0:0 | NewEntity |
70+
| file://:0:0:0:0 | TypeExpr | Foo.qll:41:11:41:19 | EntityKey |
71+
| file://:0:0:0:0 | TypeExpr | file://:0:0:0:0 | EntityId |
72+
| file://:0:0:0:0 | TypeExpr | file://:0:0:0:0 | EntityKeySig |
73+
| file://:0:0:0:0 | TypeExpr | file://:0:0:0:0 | EntityKeySig |
4774
| file://:0:0:0:0 | TypeExpr | file://:0:0:0:0 | EquivalenceClass |
4875
| file://:0:0:0:0 | TypeExpr | file://:0:0:0:0 | T |
4976
| file://:0:0:0:0 | TypeExpr | file://:0:0:0:0 | T |

0 commit comments

Comments
 (0)