add support for signature modules (warning: ugly commit)

This commit is contained in:
Erik Krogh Kristensen
2022-06-16 16:08:08 +02:00
parent 98690c8a2e
commit 2a7a0f398a
8 changed files with 150 additions and 37 deletions

View File

@@ -712,6 +712,9 @@ class Module extends TModule, ModuleDeclaration {
exists(int i | result = this.getMember(i) and m = this.getMember(i + 1))
}
/** Gets a ref to the module that this module implements. */
ModuleParameterRef getImplements(int i) { toQL(result) = mod.getImplements(i).getTypeExpr() }
/** Gets the module expression that this module is an alias for, if any. */
ModuleExpr getAlias() { toQL(result) = mod.getAFieldOrChild().(QL::ModuleAliasBody).getChild() }
@@ -722,6 +725,8 @@ class Module extends TModule, ModuleDeclaration {
or
pred = directMember("getAMember") and result = this.getAMember()
or
exists(int i | pred = indexedMember("getImplements", i) and result = this.getImplements(i))
or
exists(int i | pred = indexedMember("hasParameter", i) and this.hasParameter(i, _, result))
}
@@ -1110,6 +1115,8 @@ class InlineCast extends TInlineCast, Expr {
class ModuleRef extends AstNode, TModuleRef {
/** Gets the module that this entity resolves to. */
FileOrModule getResolvedModule() { none() }
string getName() { none() }
}
/**
@@ -2184,6 +2191,19 @@ class DontCare extends TDontCare, Expr {
override string getAPrimaryQlClass() { result = "DontCare" }
}
/** A reference to a module as part of a parameterized module (or it's instantiation) */
class ModuleParameterRef extends ModuleRef, TModuleParameterRef {
QL::TypeExpr type;
ModuleParameterRef() { this = TModuleParameterRef(type) }
final override FileOrModule getResolvedModule() { resolveModuleRef(this, result) }
override string getName() { result = type.getName().getValue() }
override string getAPrimaryQlClass() { result = "ModuleParameterRef" }
}
/** A module expression. Such as `DataFlow` in `DataFlow::Node` */
class ModuleExpr extends TModuleExpr, ModuleRef {
QL::ModuleExpr me;
@@ -2199,15 +2219,7 @@ class ModuleExpr extends TModuleExpr, ModuleRef {
*
* is `Bar`.
*/
string getName() {
result = me.getName().(QL::SimpleId).getValue()
or
not exists(me.getName()) and result = me.getChild().(QL::SimpleId).getValue()
or
exists(QL::ModuleInstantiation instantiation | instantiation.getParent() = me |
result = instantiation.getName().getChild().getValue()
)
}
override string getName() { result = getNameForModuleExpr(me) }
/**
* Gets the qualifier of this module expression. For example, the qualifier of
@@ -2220,7 +2232,7 @@ class ModuleExpr extends TModuleExpr, ModuleRef {
*/
ModuleExpr getQualifier() { result = TModuleExpr(me.getChild()) }
final override FileOrModule getResolvedModule() { resolveModuleExpr(this, result) }
final override FileOrModule getResolvedModule() { resolveModuleRef(this, result) }
final override string toString() { result = this.getName() }
@@ -2252,7 +2264,7 @@ class SignatureExpr extends TSignatureExpr, AstNode {
SignatureExpr() {
toQL(this) = sig.getPredicate()
or
toQL(this) = sig.getTypeExpr()
toQL(this) = sig.getTypeExpr() // both `TypeExpr` and `ModuleParameterRef`
}
/** Gets the generated AST node that contains this signature expression. */
@@ -2263,6 +2275,8 @@ class SignatureExpr extends TSignatureExpr, AstNode {
/** Gets this signature expression if it represents a type expression. */
TypeExpr asType() { result = this }
ModuleParameterRef asModuleRef() { result = this }
}
/** An argument to an annotation. */

View File

@@ -19,7 +19,11 @@ newtype TAstNode =
TNewType(QL::Datatype dt) or
TNewTypeBranch(QL::DatatypeBranch branch) or
TImport(QL::ImportDirective imp) or
TType(QL::TypeExpr type) or
// splitting up the TypeExpr based on whether they are a reference to a type or to a module, with some duplicates.
TType(QL::TypeExpr type) { not isDefinitelyModuleParameter(type) } or
TModuleParameterRef(QL::TypeExpr type) {
isDefinitelyModuleParameter(type) or mightBeModuleParameter(type)
} or
TDisjunction(QL::Disjunction disj) or
TConjunction(QL::Conjunction conj) or
TComparisonFormula(QL::CompTerm comp) or
@@ -83,11 +87,43 @@ class TExpr =
class TCall = TPredicateCall or TMemberCall or TNoneCall or TAnyCall;
class TModuleRef = TImport or TModuleExpr;
class TModuleRef = TImport or TModuleExpr or TModuleParameterRef;
class TYamlNode = TYamlCommemt or TYamlEntry or TYamlKey or TYamlListitem or TYamlValue;
class TSignatureExpr = TPredicateExpr or TType;
class TSignatureExpr = TPredicateExpr or TType or TModuleParameterRef;
private predicate isDefinitelyModuleParameter(QL::TypeExpr type) {
// the signature of a parameterized module
exists(QL::SignatureExpr expr, QL::Module m, QL::ModuleParam param, string name |
param = m.getParameter(_) and
name = param.getParameter().getValue() and
expr = param.getSignature() and
type = expr.getTypeExpr() and
// we have a ref to it, to confirm that it's actually a module.
exists(QL::ModuleExpr ref | getNameForModuleExpr(ref) = name | ref.getParent+() = m)
)
or
// the implements clause of a module.
exists(QL::Module qlmod | qlmod.getImplements(_).getTypeExpr() = type)
}
private predicate mightBeModuleParameter(QL::TypeExpr type) {
// 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.
// we spuriously have both TypeExpr and ModuleParameterRef for the same thing.
exists(QL::ModuleInstantiation inst | type = inst.getChild(_).getTypeExpr())
}
// ensuring non-monotonic recursion by outlining predicate out of `ModuleExpr`
string getNameForModuleExpr(QL::ModuleExpr me) {
result = me.getName().(QL::SimpleId).getValue()
or
not exists(me.getName()) and result = me.getChild().(QL::SimpleId).getValue()
or
exists(QL::ModuleInstantiation instantiation | instantiation.getParent() = me |
result = instantiation.getName().getChild().getValue()
)
}
/** DEPRECATED: Alias for TYamlNode */
deprecated class TYAMLNode = TYamlNode;
@@ -176,6 +212,8 @@ QL::AstNode toQL(AST::AstNode n) {
or
n = TNewTypeBranch(result)
or
n = TModuleParameterRef(result)
or
n = TImport(result)
or
n = TType(result)

View File

@@ -23,18 +23,18 @@ private class ContainerOrModule extends TContainerOrModule {
or
this = TFolder(_) and result = "folder"
}
/** Gets the module for this imported module. */
Module asModule() { this = TModule(result) }
/** Gets the file for this file. */
File asFile() { this = TFile(result) }
}
private class TFileOrModule = TFile or TModule;
/** A file or a module. */
class FileOrModule extends TFileOrModule, ContainerOrModule {
/** Gets the module for this imported module. */
Module asModule() { this = TModule(result) }
/** Gets the file for this file. */
File asFile() { this = TFile(result) }
/** Gets the file that contains this module/file. */
File getFile() {
result = this.asFile()
@@ -114,6 +114,8 @@ class Module_ extends FileOrModule, TModule {
}
}
// class ModuleRef = AstNodes::TModuleExpr or AstNodes::TType;
private predicate resolveQualifiedName(Import imp, ContainerOrModule m, int i) {
not m = TFile(any(File f | f.getExtension() = "ql")) and
exists(string q | q = imp.getQualifiedName(i) |
@@ -207,21 +209,21 @@ private module Cached {
/** Holds if module expression `me` resolves to `m`. */
cached
predicate resolveModuleExpr(ModuleExpr me, FileOrModule m) {
predicate resolveModuleRef(ModuleRef me, FileOrModule m) { // TODO: name.
not m = TFile(any(File f | f.getExtension() = "ql")) and
not exists(me.getQualifier()) and
exists(ContainerOrModule enclosing, string name | resolveModuleExprHelper(me, enclosing, name) |
not exists(me.(ModuleExpr).getQualifier()) and
exists(ContainerOrModule enclosing, string name | resolveModuleRefHelper(me, enclosing, name) |
definesModule(enclosing, name, m, _)
)
or
exists(FileOrModule mid |
resolveModuleExpr(me.getQualifier(), mid) and
definesModule(mid, me.getName(), m, true)
resolveModuleRef(me.(ModuleExpr).getQualifier(), mid) and
definesModule(mid, me.(ModuleExpr).getName(), m, true)
)
}
pragma[noinline]
private predicate resolveModuleExprHelper(ModuleExpr me, ContainerOrModule enclosing, string name) {
private predicate resolveModuleRefHelper(ModuleRef me, ContainerOrModule enclosing, string name) {
enclosing = getEnclosingModule(me).getEnclosing*() and
name = me.getName()
}
@@ -231,7 +233,10 @@ import Cached
private import NewType
boolean getPublicBool(AstNode n) {
if n.(ModuleMember).isPrivate() or n.(NewTypeBranch).getNewType().isPrivate()
if
n.(ModuleMember).isPrivate() or
n.(NewTypeBranch).getNewType().isPrivate() or
n.(Module).isPrivate()
then result = false
else result = true
}
@@ -251,6 +256,20 @@ private predicate definesModule(
public = true
or
m = TModule(any(Module mod | public = getPublicBool(mod)))
)
or
// signature module in a paramertized module
exists(Module mod, SignatureExpr sig, ModuleParameterRef ty, int i |
mod = container.asModule() and
mod.hasParameter(i, name, sig) and
public = false and
ty = sig.asModuleRef()
|
m = ty.getResolvedModule()
or
exists(ModuleExpr inst | inst.getResolvedModule().asModule() = mod |
m = inst.getArgument(i).asModuleRef().getResolvedModule()
)
)
or
// import X
@@ -274,7 +293,7 @@ private predicate definesModule(
exists(Module alias |
container = getEnclosingModule(alias) and
name = alias.getName() and
resolveModuleExpr(alias.getAlias(), m) and
resolveModuleRef(alias.getAlias(), m) and
public = getPublicBool(alias)
)
}
@@ -288,8 +307,8 @@ module ModConsistency {
.regexpMatch(".*/(test|examples|ql-training|recorded-call-graph-metrics)/.*")
}
query predicate noResolveModuleExpr(ModuleExpr me) {
not resolveModuleExpr(me, _) and
query predicate noResolveModuleRef(ModuleRef me) { // TODO: name?
not exists(me.getResolvedModule()) and
not me.getLocation()
.getFile()
.getAbsolutePath()
@@ -306,10 +325,10 @@ module ModConsistency {
.regexpMatch(".*/(test|examples|ql-training|recorded-call-graph-metrics)/.*")
}
query predicate multipleResolveModuleExpr(ModuleExpr me, int c, ContainerOrModule m) {
c = strictcount(ContainerOrModule m0 | resolveModuleExpr(me, m0)) and
query predicate multipleResolveModuleRef(ModuleExpr me, int c, ContainerOrModule m) {
c = strictcount(ContainerOrModule m0 | resolveModuleRef(me, m0)) and
c > 1 and
resolveModuleExpr(me, m)
resolveModuleRef(me, m)
}
query predicate noName(Module mod) { not exists(mod.getName()) }

View File

@@ -73,7 +73,7 @@ private module Cached {
exists(Module mod, PredicateExpr sig |
mod.hasParameter(_, pc.getPredicateName(), sig) and
p = sig.getResolvedPredicate() and // <- this is a `signature predicate`, but that's fine.
sig.getArity() = pc.getNumberOfArguments()
sig.getArity() = pc.getNumberOfArguments() // TODO: resolve all instantiations?
)
}

View File

@@ -348,7 +348,12 @@ module TyConsistency {
not te.getLocation()
.getFile()
.getAbsolutePath()
.regexpMatch(".*/(test|examples|ql-training|recorded-call-graph-metrics)/.*")
.regexpMatch(".*/(test|examples|ql-training|recorded-call-graph-metrics)/.*") and
// we have some duplicate with moduleRef, so that might be resolved correctly.
// TODO: Collapse both ModuleRef and TypeExpr into one class?
not exists(ModuleRef ref | AstNodes::toQL(te) = AstNodes::toQL(ref) |
exists(ref.getResolvedModule())
)
}
query predicate multipleResolve(TypeExpr te, int c, Type t) {

View File

@@ -39,7 +39,7 @@ where
or
ModConsistency::noResolve(node) and msg = "ModConsistency::noResolve"
or
ModConsistency::noResolveModuleExpr(node) and msg = "ModConsistency::noResolveModuleExpr"
ModConsistency::noResolveModuleRef(node) and msg = "ModConsistency::noResolveModuleRef"
or
ModConsistency::noName(node) and msg = "ModConsistency::noName"
or

View File

@@ -27,5 +27,37 @@ module ClassSig {
}
module ModuleSig {
// TODO:
signature module FooSig {
class A;
A getThing();
}
module UsesFoo<FooSig FooImpl> {
B getThing() { result = FooImpl::getThing() }
class B = FooImpl::A;
}
module MyFoo implements FooSig {
class C extends int {
C() { this = [0 .. 10] }
string myFoo() { result = "myFoo" }
}
class A = C;
C getThing() { any() }
}
module ImplStuff {
module Inst = UsesFoo<MyFoo>;
class D = Inst::B;
string use1() { result = Inst::getThing().myFoo() }
string use2(Inst::B b) { result = b.myFoo() }
}
}

View File

@@ -25,6 +25,11 @@ getTarget
| ParamModules.qll:10:26:10:49 | PredicateCall | ParamModules.qll:5:5:5:43 | ClasslessPredicate bar |
| ParamModules.qll:26:27:26:53 | PredicateCall | ParamModules.qll:17:5:17:42 | ClasslessPredicate getAnEven |
| ParamModules.qll:26:27:26:61 | MemberCall | ParamModules.qll:23:5:23:39 | ClassPredicate myFoo |
| ParamModules.qll:37:29:37:47 | PredicateCall | ParamModules.qll:33:5:33:17 | ClasslessPredicate getThing |
| ParamModules.qll:37:29:37:47 | PredicateCall | ParamModules.qll:51:5:51:26 | ClasslessPredicate getThing |
| ParamModules.qll:59:30:59:45 | PredicateCall | ParamModules.qll:37:5:37:49 | ClasslessPredicate getThing |
| ParamModules.qll:59:30:59:53 | MemberCall | ParamModules.qll:46:7:46:41 | ClassPredicate myFoo |
| ParamModules.qll:61:39:61:47 | MemberCall | ParamModules.qll:46:7:46:41 | ClassPredicate myFoo |
| packs/other/OtherThing.qll:5:3:5:8 | PredicateCall | packs/lib/LibThing/Foo.qll:1:1:1:30 | ClasslessPredicate foo |
| packs/other/OtherThing.qll:6:3:6:8 | PredicateCall | packs/src/SrcThing.qll:8:1:8:30 | ClasslessPredicate bar |
| packs/src/SrcThing.qll:4:3:4:8 | PredicateCall | packs/lib/LibThing/Foo.qll:1:1:1:30 | ClasslessPredicate foo |