Skip to content

Commit 2a7a0f3

Browse files
committed
add support for signature modules (warning: ugly commit)
1 parent 98690c8 commit 2a7a0f3

File tree

8 files changed

+150
-37
lines changed

8 files changed

+150
-37
lines changed

ql/ql/src/codeql_ql/ast/Ast.qll

Lines changed: 25 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -712,6 +712,9 @@ class Module extends TModule, ModuleDeclaration {
712712
exists(int i | result = this.getMember(i) and m = this.getMember(i + 1))
713713
}
714714

715+
/** Gets a ref to the module that this module implements. */
716+
ModuleParameterRef getImplements(int i) { toQL(result) = mod.getImplements(i).getTypeExpr() }
717+
715718
/** Gets the module expression that this module is an alias for, if any. */
716719
ModuleExpr getAlias() { toQL(result) = mod.getAFieldOrChild().(QL::ModuleAliasBody).getChild() }
717720

@@ -722,6 +725,8 @@ class Module extends TModule, ModuleDeclaration {
722725
or
723726
pred = directMember("getAMember") and result = this.getAMember()
724727
or
728+
exists(int i | pred = indexedMember("getImplements", i) and result = this.getImplements(i))
729+
or
725730
exists(int i | pred = indexedMember("hasParameter", i) and this.hasParameter(i, _, result))
726731
}
727732

@@ -1110,6 +1115,8 @@ class InlineCast extends TInlineCast, Expr {
11101115
class ModuleRef extends AstNode, TModuleRef {
11111116
/** Gets the module that this entity resolves to. */
11121117
FileOrModule getResolvedModule() { none() }
1118+
1119+
string getName() { none() }
11131120
}
11141121

11151122
/**
@@ -2184,6 +2191,19 @@ class DontCare extends TDontCare, Expr {
21842191
override string getAPrimaryQlClass() { result = "DontCare" }
21852192
}
21862193

2194+
/** A reference to a module as part of a parameterized module (or it's instantiation) */
2195+
class ModuleParameterRef extends ModuleRef, TModuleParameterRef {
2196+
QL::TypeExpr type;
2197+
2198+
ModuleParameterRef() { this = TModuleParameterRef(type) }
2199+
2200+
final override FileOrModule getResolvedModule() { resolveModuleRef(this, result) }
2201+
2202+
override string getName() { result = type.getName().getValue() }
2203+
2204+
override string getAPrimaryQlClass() { result = "ModuleParameterRef" }
2205+
}
2206+
21872207
/** A module expression. Such as `DataFlow` in `DataFlow::Node` */
21882208
class ModuleExpr extends TModuleExpr, ModuleRef {
21892209
QL::ModuleExpr me;
@@ -2199,15 +2219,7 @@ class ModuleExpr extends TModuleExpr, ModuleRef {
21992219
*
22002220
* is `Bar`.
22012221
*/
2202-
string getName() {
2203-
result = me.getName().(QL::SimpleId).getValue()
2204-
or
2205-
not exists(me.getName()) and result = me.getChild().(QL::SimpleId).getValue()
2206-
or
2207-
exists(QL::ModuleInstantiation instantiation | instantiation.getParent() = me |
2208-
result = instantiation.getName().getChild().getValue()
2209-
)
2210-
}
2222+
override string getName() { result = getNameForModuleExpr(me) }
22112223

22122224
/**
22132225
* Gets the qualifier of this module expression. For example, the qualifier of
@@ -2220,7 +2232,7 @@ class ModuleExpr extends TModuleExpr, ModuleRef {
22202232
*/
22212233
ModuleExpr getQualifier() { result = TModuleExpr(me.getChild()) }
22222234

2223-
final override FileOrModule getResolvedModule() { resolveModuleExpr(this, result) }
2235+
final override FileOrModule getResolvedModule() { resolveModuleRef(this, result) }
22242236

22252237
final override string toString() { result = this.getName() }
22262238

@@ -2252,7 +2264,7 @@ class SignatureExpr extends TSignatureExpr, AstNode {
22522264
SignatureExpr() {
22532265
toQL(this) = sig.getPredicate()
22542266
or
2255-
toQL(this) = sig.getTypeExpr()
2267+
toQL(this) = sig.getTypeExpr() // both `TypeExpr` and `ModuleParameterRef`
22562268
}
22572269

22582270
/** Gets the generated AST node that contains this signature expression. */
@@ -2263,6 +2275,8 @@ class SignatureExpr extends TSignatureExpr, AstNode {
22632275

22642276
/** Gets this signature expression if it represents a type expression. */
22652277
TypeExpr asType() { result = this }
2278+
2279+
ModuleParameterRef asModuleRef() { result = this }
22662280
}
22672281

22682282
/** An argument to an annotation. */

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

Lines changed: 41 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,11 @@ newtype TAstNode =
1919
TNewType(QL::Datatype dt) or
2020
TNewTypeBranch(QL::DatatypeBranch branch) or
2121
TImport(QL::ImportDirective imp) or
22-
TType(QL::TypeExpr type) or
22+
// splitting up the TypeExpr based on whether they are a reference to a type or to a module, with some duplicates.
23+
TType(QL::TypeExpr type) { not isDefinitelyModuleParameter(type) } or
24+
TModuleParameterRef(QL::TypeExpr type) {
25+
isDefinitelyModuleParameter(type) or mightBeModuleParameter(type)
26+
} or
2327
TDisjunction(QL::Disjunction disj) or
2428
TConjunction(QL::Conjunction conj) or
2529
TComparisonFormula(QL::CompTerm comp) or
@@ -83,11 +87,43 @@ class TExpr =
8387

8488
class TCall = TPredicateCall or TMemberCall or TNoneCall or TAnyCall;
8589

86-
class TModuleRef = TImport or TModuleExpr;
90+
class TModuleRef = TImport or TModuleExpr or TModuleParameterRef;
8791

8892
class TYamlNode = TYamlCommemt or TYamlEntry or TYamlKey or TYamlListitem or TYamlValue;
8993

90-
class TSignatureExpr = TPredicateExpr or TType;
94+
class TSignatureExpr = TPredicateExpr or TType or TModuleParameterRef;
95+
96+
private predicate isDefinitelyModuleParameter(QL::TypeExpr type) {
97+
// the signature of a parameterized module
98+
exists(QL::SignatureExpr expr, QL::Module m, QL::ModuleParam param, string name |
99+
param = m.getParameter(_) and
100+
name = param.getParameter().getValue() and
101+
expr = param.getSignature() and
102+
type = expr.getTypeExpr() and
103+
// we have a ref to it, to confirm that it's actually a module.
104+
exists(QL::ModuleExpr ref | getNameForModuleExpr(ref) = name | ref.getParent+() = m)
105+
)
106+
or
107+
// the implements clause of a module.
108+
exists(QL::Module qlmod | qlmod.getImplements(_).getTypeExpr() = type)
109+
}
110+
111+
private predicate mightBeModuleParameter(QL::TypeExpr type) {
112+
// or it's in an instantiation. The only way to know for sure if it's a module ref or not is to try, so we just have duplicates in the AST.
113+
// we spuriously have both TypeExpr and ModuleParameterRef for the same thing.
114+
exists(QL::ModuleInstantiation inst | type = inst.getChild(_).getTypeExpr())
115+
}
116+
117+
// ensuring non-monotonic recursion by outlining predicate out of `ModuleExpr`
118+
string getNameForModuleExpr(QL::ModuleExpr me) {
119+
result = me.getName().(QL::SimpleId).getValue()
120+
or
121+
not exists(me.getName()) and result = me.getChild().(QL::SimpleId).getValue()
122+
or
123+
exists(QL::ModuleInstantiation instantiation | instantiation.getParent() = me |
124+
result = instantiation.getName().getChild().getValue()
125+
)
126+
}
91127

92128
/** DEPRECATED: Alias for TYamlNode */
93129
deprecated class TYAMLNode = TYamlNode;
@@ -176,6 +212,8 @@ QL::AstNode toQL(AST::AstNode n) {
176212
or
177213
n = TNewTypeBranch(result)
178214
or
215+
n = TModuleParameterRef(result)
216+
or
179217
n = TImport(result)
180218
or
181219
n = TType(result)

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

Lines changed: 38 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -23,18 +23,18 @@ private class ContainerOrModule extends TContainerOrModule {
2323
or
2424
this = TFolder(_) and result = "folder"
2525
}
26+
27+
/** Gets the module for this imported module. */
28+
Module asModule() { this = TModule(result) }
29+
30+
/** Gets the file for this file. */
31+
File asFile() { this = TFile(result) }
2632
}
2733

2834
private class TFileOrModule = TFile or TModule;
2935

3036
/** A file or a module. */
3137
class FileOrModule extends TFileOrModule, ContainerOrModule {
32-
/** Gets the module for this imported module. */
33-
Module asModule() { this = TModule(result) }
34-
35-
/** Gets the file for this file. */
36-
File asFile() { this = TFile(result) }
37-
3838
/** Gets the file that contains this module/file. */
3939
File getFile() {
4040
result = this.asFile()
@@ -114,6 +114,8 @@ class Module_ extends FileOrModule, TModule {
114114
}
115115
}
116116

117+
// class ModuleRef = AstNodes::TModuleExpr or AstNodes::TType;
118+
117119
private predicate resolveQualifiedName(Import imp, ContainerOrModule m, int i) {
118120
not m = TFile(any(File f | f.getExtension() = "ql")) and
119121
exists(string q | q = imp.getQualifiedName(i) |
@@ -207,21 +209,21 @@ private module Cached {
207209

208210
/** Holds if module expression `me` resolves to `m`. */
209211
cached
210-
predicate resolveModuleExpr(ModuleExpr me, FileOrModule m) {
212+
predicate resolveModuleRef(ModuleRef me, FileOrModule m) { // TODO: name.
211213
not m = TFile(any(File f | f.getExtension() = "ql")) and
212-
not exists(me.getQualifier()) and
213-
exists(ContainerOrModule enclosing, string name | resolveModuleExprHelper(me, enclosing, name) |
214+
not exists(me.(ModuleExpr).getQualifier()) and
215+
exists(ContainerOrModule enclosing, string name | resolveModuleRefHelper(me, enclosing, name) |
214216
definesModule(enclosing, name, m, _)
215217
)
216218
or
217219
exists(FileOrModule mid |
218-
resolveModuleExpr(me.getQualifier(), mid) and
219-
definesModule(mid, me.getName(), m, true)
220+
resolveModuleRef(me.(ModuleExpr).getQualifier(), mid) and
221+
definesModule(mid, me.(ModuleExpr).getName(), m, true)
220222
)
221223
}
222224

223225
pragma[noinline]
224-
private predicate resolveModuleExprHelper(ModuleExpr me, ContainerOrModule enclosing, string name) {
226+
private predicate resolveModuleRefHelper(ModuleRef me, ContainerOrModule enclosing, string name) {
225227
enclosing = getEnclosingModule(me).getEnclosing*() and
226228
name = me.getName()
227229
}
@@ -231,7 +233,10 @@ import Cached
231233
private import NewType
232234

233235
boolean getPublicBool(AstNode n) {
234-
if n.(ModuleMember).isPrivate() or n.(NewTypeBranch).getNewType().isPrivate()
236+
if
237+
n.(ModuleMember).isPrivate() or
238+
n.(NewTypeBranch).getNewType().isPrivate() or
239+
n.(Module).isPrivate()
235240
then result = false
236241
else result = true
237242
}
@@ -251,6 +256,20 @@ private predicate definesModule(
251256
public = true
252257
or
253258
m = TModule(any(Module mod | public = getPublicBool(mod)))
259+
)
260+
or
261+
// signature module in a paramertized module
262+
exists(Module mod, SignatureExpr sig, ModuleParameterRef ty, int i |
263+
mod = container.asModule() and
264+
mod.hasParameter(i, name, sig) and
265+
public = false and
266+
ty = sig.asModuleRef()
267+
|
268+
m = ty.getResolvedModule()
269+
or
270+
exists(ModuleExpr inst | inst.getResolvedModule().asModule() = mod |
271+
m = inst.getArgument(i).asModuleRef().getResolvedModule()
272+
)
254273
)
255274
or
256275
// import X
@@ -274,7 +293,7 @@ private predicate definesModule(
274293
exists(Module alias |
275294
container = getEnclosingModule(alias) and
276295
name = alias.getName() and
277-
resolveModuleExpr(alias.getAlias(), m) and
296+
resolveModuleRef(alias.getAlias(), m) and
278297
public = getPublicBool(alias)
279298
)
280299
}
@@ -288,8 +307,8 @@ module ModConsistency {
288307
.regexpMatch(".*/(test|examples|ql-training|recorded-call-graph-metrics)/.*")
289308
}
290309

291-
query predicate noResolveModuleExpr(ModuleExpr me) {
292-
not resolveModuleExpr(me, _) and
310+
query predicate noResolveModuleRef(ModuleRef me) { // TODO: name?
311+
not exists(me.getResolvedModule()) and
293312
not me.getLocation()
294313
.getFile()
295314
.getAbsolutePath()
@@ -306,10 +325,10 @@ module ModConsistency {
306325
.regexpMatch(".*/(test|examples|ql-training|recorded-call-graph-metrics)/.*")
307326
}
308327

309-
query predicate multipleResolveModuleExpr(ModuleExpr me, int c, ContainerOrModule m) {
310-
c = strictcount(ContainerOrModule m0 | resolveModuleExpr(me, m0)) and
328+
query predicate multipleResolveModuleRef(ModuleExpr me, int c, ContainerOrModule m) {
329+
c = strictcount(ContainerOrModule m0 | resolveModuleRef(me, m0)) and
311330
c > 1 and
312-
resolveModuleExpr(me, m)
331+
resolveModuleRef(me, m)
313332
}
314333

315334
query predicate noName(Module mod) { not exists(mod.getName()) }

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ private module Cached {
7373
exists(Module mod, PredicateExpr sig |
7474
mod.hasParameter(_, pc.getPredicateName(), sig) and
7575
p = sig.getResolvedPredicate() and // <- this is a `signature predicate`, but that's fine.
76-
sig.getArity() = pc.getNumberOfArguments()
76+
sig.getArity() = pc.getNumberOfArguments() // TODO: resolve all instantiations?
7777
)
7878
}
7979

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

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -348,7 +348,12 @@ module TyConsistency {
348348
not te.getLocation()
349349
.getFile()
350350
.getAbsolutePath()
351-
.regexpMatch(".*/(test|examples|ql-training|recorded-call-graph-metrics)/.*")
351+
.regexpMatch(".*/(test|examples|ql-training|recorded-call-graph-metrics)/.*") and
352+
// we have some duplicate with moduleRef, so that might be resolved correctly.
353+
// TODO: Collapse both ModuleRef and TypeExpr into one class?
354+
not exists(ModuleRef ref | AstNodes::toQL(te) = AstNodes::toQL(ref) |
355+
exists(ref.getResolvedModule())
356+
)
352357
}
353358

354359
query predicate multipleResolve(TypeExpr te, int c, Type t) {

ql/ql/src/queries/diagnostics/EmptyConsistencies.ql

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ where
3939
or
4040
ModConsistency::noResolve(node) and msg = "ModConsistency::noResolve"
4141
or
42-
ModConsistency::noResolveModuleExpr(node) and msg = "ModConsistency::noResolveModuleExpr"
42+
ModConsistency::noResolveModuleRef(node) and msg = "ModConsistency::noResolveModuleRef"
4343
or
4444
ModConsistency::noName(node) and msg = "ModConsistency::noName"
4545
or

ql/ql/test/callgraph/ParamModules.qll

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,5 +27,37 @@ module ClassSig {
2727
}
2828

2929
module ModuleSig {
30-
// TODO:
30+
signature module FooSig {
31+
class A;
32+
33+
A getThing();
34+
}
35+
36+
module UsesFoo<FooSig FooImpl> {
37+
B getThing() { result = FooImpl::getThing() }
38+
39+
class B = FooImpl::A;
40+
}
41+
42+
module MyFoo implements FooSig {
43+
class C extends int {
44+
C() { this = [0 .. 10] }
45+
46+
string myFoo() { result = "myFoo" }
47+
}
48+
49+
class A = C;
50+
51+
C getThing() { any() }
52+
}
53+
54+
module ImplStuff {
55+
module Inst = UsesFoo<MyFoo>;
56+
57+
class D = Inst::B;
58+
59+
string use1() { result = Inst::getThing().myFoo() }
60+
61+
string use2(Inst::B b) { result = b.myFoo() }
62+
}
3163
}

ql/ql/test/callgraph/callgraph.expected

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,11 @@ getTarget
2525
| ParamModules.qll:10:26:10:49 | PredicateCall | ParamModules.qll:5:5:5:43 | ClasslessPredicate bar |
2626
| ParamModules.qll:26:27:26:53 | PredicateCall | ParamModules.qll:17:5:17:42 | ClasslessPredicate getAnEven |
2727
| ParamModules.qll:26:27:26:61 | MemberCall | ParamModules.qll:23:5:23:39 | ClassPredicate myFoo |
28+
| ParamModules.qll:37:29:37:47 | PredicateCall | ParamModules.qll:33:5:33:17 | ClasslessPredicate getThing |
29+
| ParamModules.qll:37:29:37:47 | PredicateCall | ParamModules.qll:51:5:51:26 | ClasslessPredicate getThing |
30+
| ParamModules.qll:59:30:59:45 | PredicateCall | ParamModules.qll:37:5:37:49 | ClasslessPredicate getThing |
31+
| ParamModules.qll:59:30:59:53 | MemberCall | ParamModules.qll:46:7:46:41 | ClassPredicate myFoo |
32+
| ParamModules.qll:61:39:61:47 | MemberCall | ParamModules.qll:46:7:46:41 | ClassPredicate myFoo |
2833
| packs/other/OtherThing.qll:5:3:5:8 | PredicateCall | packs/lib/LibThing/Foo.qll:1:1:1:30 | ClasslessPredicate foo |
2934
| packs/other/OtherThing.qll:6:3:6:8 | PredicateCall | packs/src/SrcThing.qll:8:1:8:30 | ClasslessPredicate bar |
3035
| packs/src/SrcThing.qll:4:3:4:8 | PredicateCall | packs/lib/LibThing/Foo.qll:1:1:1:30 | ClasslessPredicate foo |

0 commit comments

Comments
 (0)