mirror of
https://github.com/github/codeql.git
synced 2025-12-16 16:53:25 +01:00
add support for signature modules (warning: ugly commit)
This commit is contained in:
@@ -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. */
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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()) }
|
||||
|
||||
@@ -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?
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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() }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 |
|
||||
|
||||
Reference in New Issue
Block a user