mirror of
https://github.com/github/codeql.git
synced 2025-12-17 01:03:14 +01:00
Merge pull request #9575 from erik-krogh/paramModules
QL: support for parameterized modules
This commit is contained in:
BIN
ql/Cargo.lock
generated
BIN
ql/Cargo.lock
generated
Binary file not shown.
@@ -10,7 +10,7 @@ edition = "2018"
|
|||||||
flate2 = "1.0"
|
flate2 = "1.0"
|
||||||
node-types = { path = "../node-types" }
|
node-types = { path = "../node-types" }
|
||||||
tree-sitter = "0.19"
|
tree-sitter = "0.19"
|
||||||
tree-sitter-ql = { git = "https://github.com/tausbn/tree-sitter-ql.git", rev = "c3d626a77cf5acc5d8c11aaf91c12348880c8eca" }
|
tree-sitter-ql = { git = "https://github.com/erik-krogh/tree-sitter-ql.git", rev = "343cc5873e20510586ade803659ef8ce153bd603" }
|
||||||
clap = "2.33"
|
clap = "2.33"
|
||||||
tracing = "0.1"
|
tracing = "0.1"
|
||||||
tracing-subscriber = { version = "0.3.3", features = ["env-filter"] }
|
tracing-subscriber = { version = "0.3.3", features = ["env-filter"] }
|
||||||
|
|||||||
@@ -11,4 +11,4 @@ clap = "2.33"
|
|||||||
node-types = { path = "../node-types" }
|
node-types = { path = "../node-types" }
|
||||||
tracing = "0.1"
|
tracing = "0.1"
|
||||||
tracing-subscriber = { version = "0.3.3", features = ["env-filter"] }
|
tracing-subscriber = { version = "0.3.3", features = ["env-filter"] }
|
||||||
tree-sitter-ql = { git = "https://github.com/tausbn/tree-sitter-ql.git", rev = "c3d626a77cf5acc5d8c11aaf91c12348880c8eca" }
|
tree-sitter-ql = { git = "https://github.com/erik-krogh/tree-sitter-ql.git", rev = "343cc5873e20510586ade803659ef8ce153bd603" }
|
||||||
|
|||||||
@@ -162,6 +162,8 @@ class QLDoc extends TQLDoc, AstNode {
|
|||||||
string getContents() { result = qldoc.getValue() }
|
string getContents() { result = qldoc.getValue() }
|
||||||
|
|
||||||
override string getAPrimaryQlClass() { result = "QLDoc" }
|
override string getAPrimaryQlClass() { result = "QLDoc" }
|
||||||
|
|
||||||
|
override AstNode getParent() { result.getQLDoc() = this }
|
||||||
}
|
}
|
||||||
|
|
||||||
class BlockComment extends TBlockComment, AstNode {
|
class BlockComment extends TBlockComment, AstNode {
|
||||||
@@ -638,7 +640,7 @@ class FieldDecl extends TFieldDecl, AstNode {
|
|||||||
/**
|
/**
|
||||||
* A type reference, such as `DataFlow::Node`.
|
* A type reference, such as `DataFlow::Node`.
|
||||||
*/
|
*/
|
||||||
class TypeExpr extends TType, AstNode {
|
class TypeExpr extends TType, TypeRef {
|
||||||
QL::TypeExpr type;
|
QL::TypeExpr type;
|
||||||
|
|
||||||
TypeExpr() { this = TType(type) }
|
TypeExpr() { this = TType(type) }
|
||||||
@@ -675,10 +677,14 @@ class TypeExpr extends TType, AstNode {
|
|||||||
*/
|
*/
|
||||||
ModuleExpr getModule() { toQL(result) = type.getQualifier() }
|
ModuleExpr getModule() { toQL(result) = type.getQualifier() }
|
||||||
|
|
||||||
/**
|
/** Gets the type that this type reference refers to. */
|
||||||
* Gets the type that this type reference refers to.
|
override Type getResolvedType() {
|
||||||
*/
|
// resolve type
|
||||||
Type getResolvedType() { resolveTypeExpr(this, result) }
|
resolveTypeExpr(this, result)
|
||||||
|
or
|
||||||
|
// if it resolves to a module
|
||||||
|
exists(FileOrModule mod | resolveModuleRef(this, mod) | result = mod.toType())
|
||||||
|
}
|
||||||
|
|
||||||
override AstNode getAChild(string pred) {
|
override AstNode getAChild(string pred) {
|
||||||
result = super.getAChild(pred)
|
result = super.getAChild(pred)
|
||||||
@@ -710,6 +716,9 @@ class Module extends TModule, ModuleDeclaration {
|
|||||||
exists(int i | result = this.getMember(i) and m = this.getMember(i + 1))
|
exists(int i | result = this.getMember(i) and m = this.getMember(i + 1))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Gets a ref to the module that this module implements. */
|
||||||
|
TypeExpr getImplements(int i) { toQL(result) = mod.getImplements(i).getTypeExpr() }
|
||||||
|
|
||||||
/** Gets the module expression that this module is an alias for, if any. */
|
/** Gets the module expression that this module is an alias for, if any. */
|
||||||
ModuleExpr getAlias() { toQL(result) = mod.getAFieldOrChild().(QL::ModuleAliasBody).getChild() }
|
ModuleExpr getAlias() { toQL(result) = mod.getAFieldOrChild().(QL::ModuleAliasBody).getChild() }
|
||||||
|
|
||||||
@@ -719,6 +728,19 @@ class Module extends TModule, ModuleDeclaration {
|
|||||||
pred = directMember("getAlias") and result = this.getAlias()
|
pred = directMember("getAlias") and result = this.getAlias()
|
||||||
or
|
or
|
||||||
pred = directMember("getAMember") and result = this.getAMember()
|
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))
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Holds if the `i`th parameter of this module has `name` and type `sig`. */
|
||||||
|
predicate hasParameter(int i, string name, SignatureExpr sig) {
|
||||||
|
exists(QL::ModuleParam param |
|
||||||
|
param = mod.getParameter(i) and
|
||||||
|
name = param.getParameter().getValue() and
|
||||||
|
sig.toQL() = param.getSignature()
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1093,16 +1115,18 @@ class InlineCast extends TInlineCast, Expr {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** An entity that resolves to a module. */
|
/** An entity that resolves to a type. */
|
||||||
class ModuleRef extends AstNode, TModuleRef {
|
class TypeRef extends AstNode, TTypeRef {
|
||||||
/** Gets the module that this entity resolves to. */
|
abstract Type getResolvedType();
|
||||||
FileOrModule getResolvedModule() { none() }
|
|
||||||
|
/** Gets the module that this entity resolves to, if this reference resolves to a module */
|
||||||
|
final FileOrModule getResolvedModule() { result.toType() = this.getResolvedType() }
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An import statement.
|
* An import statement.
|
||||||
*/
|
*/
|
||||||
class Import extends TImport, ModuleMember, ModuleRef {
|
class Import extends TImport, ModuleMember, TypeRef {
|
||||||
QL::ImportDirective imp;
|
QL::ImportDirective imp;
|
||||||
|
|
||||||
Import() { this = TImport(imp) }
|
Import() { this = TImport(imp) }
|
||||||
@@ -1153,7 +1177,9 @@ class Import extends TImport, ModuleMember, ModuleRef {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
final override FileOrModule getResolvedModule() { resolve(this, result) }
|
override Type getResolvedType() {
|
||||||
|
exists(FileOrModule mod | resolve(this, mod) | result = mod.toType())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** A formula, such as `x = 6 and y < 5`. */
|
/** A formula, such as `x = 6 and y < 5`. */
|
||||||
@@ -2172,7 +2198,7 @@ class DontCare extends TDontCare, Expr {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** A module expression. Such as `DataFlow` in `DataFlow::Node` */
|
/** A module expression. Such as `DataFlow` in `DataFlow::Node` */
|
||||||
class ModuleExpr extends TModuleExpr, ModuleRef {
|
class ModuleExpr extends TModuleExpr, TypeRef {
|
||||||
QL::ModuleExpr me;
|
QL::ModuleExpr me;
|
||||||
|
|
||||||
ModuleExpr() { this = TModuleExpr(me) }
|
ModuleExpr() { this = TModuleExpr(me) }
|
||||||
@@ -2190,6 +2216,10 @@ class ModuleExpr extends TModuleExpr, ModuleRef {
|
|||||||
result = me.getName().(QL::SimpleId).getValue()
|
result = me.getName().(QL::SimpleId).getValue()
|
||||||
or
|
or
|
||||||
not exists(me.getName()) and result = me.getChild().(QL::SimpleId).getValue()
|
not exists(me.getName()) and result = me.getChild().(QL::SimpleId).getValue()
|
||||||
|
or
|
||||||
|
exists(QL::ModuleInstantiation instantiation | instantiation.getParent() = me |
|
||||||
|
result = instantiation.getName().getChild().getValue()
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -2203,7 +2233,9 @@ class ModuleExpr extends TModuleExpr, ModuleRef {
|
|||||||
*/
|
*/
|
||||||
ModuleExpr getQualifier() { result = TModuleExpr(me.getChild()) }
|
ModuleExpr getQualifier() { result = TModuleExpr(me.getChild()) }
|
||||||
|
|
||||||
final override FileOrModule getResolvedModule() { resolveModuleExpr(this, result) }
|
override Type getResolvedType() {
|
||||||
|
exists(FileOrModule mod | resolveModuleRef(this, mod) | result = mod.toType())
|
||||||
|
}
|
||||||
|
|
||||||
final override string toString() { result = this.getName() }
|
final override string toString() { result = this.getName() }
|
||||||
|
|
||||||
@@ -2213,7 +2245,39 @@ class ModuleExpr extends TModuleExpr, ModuleRef {
|
|||||||
result = super.getAChild(pred)
|
result = super.getAChild(pred)
|
||||||
or
|
or
|
||||||
pred = directMember("getQualifier") and result = this.getQualifier()
|
pred = directMember("getQualifier") and result = this.getQualifier()
|
||||||
|
or
|
||||||
|
exists(int i | pred = indexedMember("getArgument", i) and result = this.getArgument(i))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the `i`th type argument if this module is a module instantiation.
|
||||||
|
* The result is either a `PredicateExpr` or a `TypeExpr`.
|
||||||
|
*/
|
||||||
|
SignatureExpr getArgument(int i) {
|
||||||
|
exists(QL::ModuleInstantiation instantiation | instantiation.getParent() = me |
|
||||||
|
result.toQL() = instantiation.getChild(i)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** A signature expression, either a `PredicateExpr` or a `TypeExpr`. */
|
||||||
|
class SignatureExpr extends TSignatureExpr, AstNode {
|
||||||
|
QL::SignatureExpr sig;
|
||||||
|
|
||||||
|
SignatureExpr() {
|
||||||
|
toQL(this) = sig.getPredicate()
|
||||||
|
or
|
||||||
|
toQL(this) = sig.getTypeExpr()
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Gets the generated AST node that contains this signature expression. */
|
||||||
|
QL::SignatureExpr toQL() { result = sig }
|
||||||
|
|
||||||
|
/** Gets this signature expression if it represents a predicate expression. */
|
||||||
|
PredicateExpr asPredicate() { result = this }
|
||||||
|
|
||||||
|
/** Gets this signature expression if it represents a type expression. */
|
||||||
|
TypeExpr asType() { result = this }
|
||||||
}
|
}
|
||||||
|
|
||||||
/** An argument to an annotation. */
|
/** An argument to an annotation. */
|
||||||
@@ -2272,7 +2336,7 @@ class Annotation extends TAnnotation, AstNode {
|
|||||||
/** Gets the node corresponding to the field `name`. */
|
/** Gets the node corresponding to the field `name`. */
|
||||||
string getName() { result = annot.getName().getValue() }
|
string getName() { result = annot.getName().getValue() }
|
||||||
|
|
||||||
override AstNode getParent() { result = AstNode.super.getParent() }
|
override AstNode getParent() { result.getAnAnnotation() = this }
|
||||||
|
|
||||||
override AstNode getAChild(string pred) {
|
override AstNode getAChild(string pred) {
|
||||||
result = super.getAChild(pred)
|
result = super.getAChild(pred)
|
||||||
|
|||||||
@@ -83,10 +83,12 @@ class TExpr =
|
|||||||
|
|
||||||
class TCall = TPredicateCall or TMemberCall or TNoneCall or TAnyCall;
|
class TCall = TPredicateCall or TMemberCall or TNoneCall or TAnyCall;
|
||||||
|
|
||||||
class TModuleRef = TImport or TModuleExpr;
|
class TTypeRef = TImport or TModuleExpr or TType;
|
||||||
|
|
||||||
class TYamlNode = TYamlCommemt or TYamlEntry or TYamlKey or TYamlListitem or TYamlValue;
|
class TYamlNode = TYamlCommemt or TYamlEntry or TYamlKey or TYamlListitem or TYamlValue;
|
||||||
|
|
||||||
|
class TSignatureExpr = TPredicateExpr or TType;
|
||||||
|
|
||||||
/** DEPRECATED: Alias for TYamlNode */
|
/** DEPRECATED: Alias for TYamlNode */
|
||||||
deprecated class TYAMLNode = TYamlNode;
|
deprecated class TYAMLNode = TYamlNode;
|
||||||
|
|
||||||
@@ -229,4 +231,6 @@ module AstConsistency {
|
|||||||
not node instanceof YAML::YAMLNode and // parents for YAML doens't work
|
not node instanceof YAML::YAMLNode and // parents for YAML doens't work
|
||||||
not (node instanceof QLDoc and node.getLocation().getFile().getExtension() = "dbscheme") // qldoc in dbschemes are not hooked up
|
not (node instanceof QLDoc and node.getLocation().getFile().getExtension() = "dbscheme") // qldoc in dbschemes are not hooked up
|
||||||
}
|
}
|
||||||
|
|
||||||
|
query predicate nonUniqueParent(AstNode node) { count(node.getParent()) >= 2 }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,24 +23,30 @@ private class ContainerOrModule extends TContainerOrModule {
|
|||||||
or
|
or
|
||||||
this = TFolder(_) and result = "folder"
|
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;
|
private class TFileOrModule = TFile or TModule;
|
||||||
|
|
||||||
/** A file or a module. */
|
/** A file or a module. */
|
||||||
class FileOrModule extends TFileOrModule, ContainerOrModule {
|
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. */
|
/** Gets the file that contains this module/file. */
|
||||||
File getFile() {
|
File getFile() {
|
||||||
result = this.asFile()
|
result = this.asFile()
|
||||||
or
|
or
|
||||||
result = this.asModule().getLocation().getFile()
|
result = this.asModule().getLocation().getFile()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Type toType() {
|
||||||
|
result.(FileType).getDeclaration().getLocation().getFile() = this.asFile()
|
||||||
|
or
|
||||||
|
result.(ModuleType).getDeclaration() = this.asModule()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class File_ extends FileOrModule, TFile {
|
private class File_ extends FileOrModule, TFile {
|
||||||
@@ -207,23 +213,23 @@ private module Cached {
|
|||||||
|
|
||||||
/** Holds if module expression `me` resolves to `m`. */
|
/** Holds if module expression `me` resolves to `m`. */
|
||||||
cached
|
cached
|
||||||
predicate resolveModuleExpr(ModuleExpr me, FileOrModule m) {
|
predicate resolveModuleRef(TypeRef me, FileOrModule m) {
|
||||||
not m = TFile(any(File f | f.getExtension() = "ql")) and
|
not m = TFile(any(File f | f.getExtension() = "ql")) and
|
||||||
not exists(me.getQualifier()) and
|
not exists(me.(ModuleExpr).getQualifier()) and
|
||||||
exists(ContainerOrModule enclosing, string name | resolveModuleExprHelper(me, enclosing, name) |
|
exists(ContainerOrModule enclosing, string name | resolveModuleRefHelper(me, enclosing, name) |
|
||||||
definesModule(enclosing, name, m, _)
|
definesModule(enclosing, name, m, _)
|
||||||
)
|
)
|
||||||
or
|
or
|
||||||
exists(FileOrModule mid |
|
exists(FileOrModule mid |
|
||||||
resolveModuleExpr(me.getQualifier(), mid) and
|
resolveModuleRef(me.(ModuleExpr).getQualifier(), mid) and
|
||||||
definesModule(mid, me.getName(), m, true)
|
definesModule(mid, me.(ModuleExpr).getName(), m, true)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pragma[noinline]
|
pragma[noinline]
|
||||||
private predicate resolveModuleExprHelper(ModuleExpr me, ContainerOrModule enclosing, string name) {
|
private predicate resolveModuleRefHelper(TypeRef me, ContainerOrModule enclosing, string name) {
|
||||||
enclosing = getEnclosingModule(me).getEnclosing*() and
|
enclosing = getEnclosingModule(me).getEnclosing*() and
|
||||||
name = me.getName()
|
name = [me.(ModuleExpr).getName(), me.(TypeExpr).getClassName()]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -231,7 +237,10 @@ import Cached
|
|||||||
private import NewType
|
private import NewType
|
||||||
|
|
||||||
boolean getPublicBool(AstNode n) {
|
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
|
then result = false
|
||||||
else result = true
|
else result = true
|
||||||
}
|
}
|
||||||
@@ -253,6 +262,22 @@ private predicate definesModule(
|
|||||||
m = TModule(any(Module mod | public = getPublicBool(mod)))
|
m = TModule(any(Module mod | public = getPublicBool(mod)))
|
||||||
)
|
)
|
||||||
or
|
or
|
||||||
|
// signature module in a paramertized module
|
||||||
|
exists(Module mod, SignatureExpr sig, TypeExpr ty, int i |
|
||||||
|
mod = container.asModule() and
|
||||||
|
mod.hasParameter(i, name, sig) and
|
||||||
|
public = false and
|
||||||
|
ty = sig.asType()
|
||||||
|
|
|
||||||
|
// resolve to the signature module
|
||||||
|
m = ty.getResolvedModule()
|
||||||
|
or
|
||||||
|
// resolve to the arguments of the instantiated module
|
||||||
|
exists(ModuleExpr inst | inst.getResolvedModule().asModule() = mod |
|
||||||
|
m = inst.getArgument(i).asType().getResolvedModule()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
or
|
||||||
// import X
|
// import X
|
||||||
exists(Import imp, ContainerOrModule m0 |
|
exists(Import imp, ContainerOrModule m0 |
|
||||||
container = getEnclosingModule(imp) and
|
container = getEnclosingModule(imp) and
|
||||||
@@ -274,7 +299,7 @@ private predicate definesModule(
|
|||||||
exists(Module alias |
|
exists(Module alias |
|
||||||
container = getEnclosingModule(alias) and
|
container = getEnclosingModule(alias) and
|
||||||
name = alias.getName() and
|
name = alias.getName() and
|
||||||
resolveModuleExpr(alias.getAlias(), m) and
|
resolveModuleRef(alias.getAlias(), m) and
|
||||||
public = getPublicBool(alias)
|
public = getPublicBool(alias)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -288,14 +313,6 @@ module ModConsistency {
|
|||||||
.regexpMatch(".*/(test|examples|ql-training|recorded-call-graph-metrics)/.*")
|
.regexpMatch(".*/(test|examples|ql-training|recorded-call-graph-metrics)/.*")
|
||||||
}
|
}
|
||||||
|
|
||||||
query predicate noResolveModuleExpr(ModuleExpr me) {
|
|
||||||
not resolveModuleExpr(me, _) and
|
|
||||||
not me.getLocation()
|
|
||||||
.getFile()
|
|
||||||
.getAbsolutePath()
|
|
||||||
.regexpMatch(".*/(test|examples|ql-training|recorded-call-graph-metrics)/.*")
|
|
||||||
}
|
|
||||||
|
|
||||||
query predicate multipleResolve(Import imp, int c, ContainerOrModule m) {
|
query predicate multipleResolve(Import imp, int c, ContainerOrModule m) {
|
||||||
c = strictcount(ContainerOrModule m0 | resolve(imp, m0)) and
|
c = strictcount(ContainerOrModule m0 | resolve(imp, m0)) and
|
||||||
c > 1 and
|
c > 1 and
|
||||||
@@ -306,9 +323,16 @@ module ModConsistency {
|
|||||||
.regexpMatch(".*/(test|examples|ql-training|recorded-call-graph-metrics)/.*")
|
.regexpMatch(".*/(test|examples|ql-training|recorded-call-graph-metrics)/.*")
|
||||||
}
|
}
|
||||||
|
|
||||||
query predicate multipleResolveModuleExpr(ModuleExpr me, int c, ContainerOrModule m) {
|
// This can happen with parameterized modules.
|
||||||
c = strictcount(ContainerOrModule m0 | resolveModuleExpr(me, m0)) and
|
/*
|
||||||
c > 1 and
|
* query predicate multipleResolveModuleRef(ModuleExpr me, int c, ContainerOrModule m) {
|
||||||
resolveModuleExpr(me, m)
|
* c = strictcount(ContainerOrModule m0 | resolveModuleRef(me, m0)) and
|
||||||
}
|
* c > 1 and
|
||||||
|
* resolveModuleRef(me, m)
|
||||||
|
* }
|
||||||
|
*/
|
||||||
|
|
||||||
|
query predicate noName(Module mod) { not exists(mod.getName()) }
|
||||||
|
|
||||||
|
query predicate nonUniqueName(Module mod) { count(mod.getName()) >= 2 }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -69,6 +69,20 @@ private module Cached {
|
|||||||
|
|
|
|
||||||
definesPredicate(m, pc.getPredicateName(), pc.getNumberOfArguments(), p, public)
|
definesPredicate(m, pc.getPredicateName(), pc.getNumberOfArguments(), p, public)
|
||||||
)
|
)
|
||||||
|
or
|
||||||
|
exists(Module mod, PredicateExpr sig, int i |
|
||||||
|
mod.hasParameter(i, pc.getPredicateName(), sig) and
|
||||||
|
sig.getArity() = pc.getNumberOfArguments()
|
||||||
|
|
|
||||||
|
// resolve to the signature predicate
|
||||||
|
p = sig.getResolvedPredicate() // <- this is a `signature predicate`, but that's fine.
|
||||||
|
or
|
||||||
|
// resolve to the instantiations
|
||||||
|
exists(ModuleExpr inst, SignatureExpr arg | inst.getResolvedModule().asModule() = mod |
|
||||||
|
arg = inst.getArgument(i) and
|
||||||
|
p = arg.asPredicate().getResolvedPredicate()
|
||||||
|
)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private predicate resolveMemberCall(MemberCall mc, PredicateOrBuiltin p) {
|
private predicate resolveMemberCall(MemberCall mc, PredicateOrBuiltin p) {
|
||||||
@@ -162,17 +176,20 @@ module PredConsistency {
|
|||||||
c > 1 and
|
c > 1 and
|
||||||
resolvePredicateExpr(pe, p)
|
resolvePredicateExpr(pe, p)
|
||||||
}
|
}
|
||||||
|
// This can happen with parametarized modules
|
||||||
|
/*
|
||||||
|
* query predicate multipleResolveCall(Call call, int c, PredicateOrBuiltin p) {
|
||||||
|
* c =
|
||||||
|
* strictcount(PredicateOrBuiltin p0 |
|
||||||
|
* resolveCall(call, p0) and
|
||||||
|
* // aliases are expected to resolve to multiple.
|
||||||
|
* not exists(p0.(ClasslessPredicate).getAlias()) and
|
||||||
|
* // overridden predicates may have multiple targets
|
||||||
|
* not p0.(ClassPredicate).isOverride()
|
||||||
|
* ) and
|
||||||
|
* c > 1 and
|
||||||
|
* resolveCall(call, p)
|
||||||
|
* }
|
||||||
|
*/
|
||||||
|
|
||||||
query predicate multipleResolveCall(Call call, int c, PredicateOrBuiltin p) {
|
|
||||||
c =
|
|
||||||
strictcount(PredicateOrBuiltin p0 |
|
|
||||||
resolveCall(call, p0) and
|
|
||||||
// aliases are expected to resolve to multiple.
|
|
||||||
not exists(p0.(ClasslessPredicate).getAlias()) and
|
|
||||||
// overridden predicates may have multiple targets
|
|
||||||
not p0.(ClassPredicate).isOverride()
|
|
||||||
) and
|
|
||||||
c > 1 and
|
|
||||||
resolveCall(call, p)
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|||||||
@@ -999,11 +999,16 @@ module QL {
|
|||||||
/** Gets the name of the primary QL class for this element. */
|
/** Gets the name of the primary QL class for this element. */
|
||||||
final override string getAPrimaryQlClass() { result = "ModuleInstantiation" }
|
final override string getAPrimaryQlClass() { result = "ModuleInstantiation" }
|
||||||
|
|
||||||
|
/** Gets the node corresponding to the field `name`. */
|
||||||
|
final ModuleName getName() { ql_module_instantiation_def(this, result) }
|
||||||
|
|
||||||
/** Gets the `i`th child of this node. */
|
/** Gets the `i`th child of this node. */
|
||||||
final SignatureExpr getChild(int i) { ql_module_instantiation_child(this, i, result) }
|
final SignatureExpr getChild(int i) { ql_module_instantiation_child(this, i, result) }
|
||||||
|
|
||||||
/** Gets a field or child node of this node. */
|
/** Gets a field or child node of this node. */
|
||||||
final override AstNode getAFieldOrChild() { ql_module_instantiation_child(this, _, result) }
|
final override AstNode getAFieldOrChild() {
|
||||||
|
ql_module_instantiation_def(this, result) or ql_module_instantiation_child(this, _, result)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** A class representing `moduleMember` nodes. */
|
/** A class representing `moduleMember` nodes. */
|
||||||
|
|||||||
@@ -14,7 +14,9 @@ private newtype TType =
|
|||||||
TDontCare() or
|
TDontCare() or
|
||||||
TClassChar(Class c) { isActualClass(c) } or
|
TClassChar(Class c) { isActualClass(c) } or
|
||||||
TClassDomain(Class c) { isActualClass(c) } or
|
TClassDomain(Class c) { isActualClass(c) } or
|
||||||
TDatabase(string s) { exists(TypeExpr t | t.isDBType() and s = t.getClassName()) }
|
TDatabase(string s) { exists(TypeExpr t | t.isDBType() and s = t.getClassName()) } or
|
||||||
|
TFile(TopLevel t) or
|
||||||
|
TModule(Module m)
|
||||||
|
|
||||||
private predicate primTypeName(string s) { s = ["int", "float", "string", "boolean", "date"] }
|
private predicate primTypeName(string s) { s = ["int", "float", "string", "boolean", "date"] }
|
||||||
|
|
||||||
@@ -110,6 +112,26 @@ class ClassType extends Type, TClass {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class FileType extends Type, TFile {
|
||||||
|
TopLevel decl;
|
||||||
|
|
||||||
|
FileType() { this = TFile(decl) }
|
||||||
|
|
||||||
|
override string getName() { result = decl.getLocation().getFile().getBaseName() }
|
||||||
|
|
||||||
|
override TopLevel getDeclaration() { result = decl }
|
||||||
|
}
|
||||||
|
|
||||||
|
class ModuleType extends Type, TModule {
|
||||||
|
Module decl;
|
||||||
|
|
||||||
|
ModuleType() { this = TModule(decl) }
|
||||||
|
|
||||||
|
override string getName() { result = decl.getName() }
|
||||||
|
|
||||||
|
override Module getDeclaration() { result = decl }
|
||||||
|
}
|
||||||
|
|
||||||
private PredicateOrBuiltin declaredPred(Type ty, string name, int arity) {
|
private PredicateOrBuiltin declaredPred(Type ty, string name, int arity) {
|
||||||
result.getDeclaringType() = ty and
|
result.getDeclaringType() = ty and
|
||||||
result.getName() = name and
|
result.getName() = name and
|
||||||
@@ -323,22 +345,42 @@ private predicate defines(FileOrModule m, string name, Type t, boolean public) {
|
|||||||
public = getPublicBool(im) and
|
public = getPublicBool(im) and
|
||||||
defines(im.getResolvedModule(), name, t, true)
|
defines(im.getResolvedModule(), name, t, true)
|
||||||
)
|
)
|
||||||
|
or
|
||||||
|
// classes in parameterized modules.
|
||||||
|
exists(Module mod, SignatureExpr param, int i |
|
||||||
|
m.asModule() = mod and
|
||||||
|
mod.hasParameter(i, name, param) and
|
||||||
|
public = true
|
||||||
|
|
|
||||||
|
// resolve to the signature class
|
||||||
|
t = param.asType().getResolvedType()
|
||||||
|
or
|
||||||
|
// resolve to the instantiations
|
||||||
|
exists(ModuleExpr inst, SignatureExpr arg |
|
||||||
|
inst.getArgument(i) = arg and
|
||||||
|
inst.getResolvedModule() = m and
|
||||||
|
t = arg.asType().getResolvedType()
|
||||||
|
)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
module TyConsistency {
|
module TyConsistency {
|
||||||
query predicate noResolve(TypeExpr te) {
|
query predicate noResolve(TypeRef te) {
|
||||||
not resolveTypeExpr(te, _) and
|
not exists(te.getResolvedType()) and
|
||||||
not te.getLocation()
|
not te.getLocation()
|
||||||
.getFile()
|
.getFile()
|
||||||
.getAbsolutePath()
|
.getAbsolutePath()
|
||||||
.regexpMatch(".*/(test|examples|ql-training|recorded-call-graph-metrics)/.*")
|
.regexpMatch(".*/(test|examples|ql-training|recorded-call-graph-metrics)/.*")
|
||||||
}
|
}
|
||||||
|
|
||||||
query predicate multipleResolve(TypeExpr te, int c, Type t) {
|
// This can happen with parameterized modules.
|
||||||
c = strictcount(Type t0 | resolveTypeExpr(te, t0)) and
|
/*
|
||||||
c > 1 and
|
* query predicate multipleResolve(TypeExpr te, int c, Type t) {
|
||||||
resolveTypeExpr(te, t)
|
* c = strictcount(Type t0 | resolveTypeExpr(te, t0)) and
|
||||||
}
|
* c > 1 and
|
||||||
|
* resolveTypeExpr(te, t)
|
||||||
|
* }
|
||||||
|
*/
|
||||||
|
|
||||||
query predicate multiplePrimitives(TypeExpr te, int c, PrimitiveType t) {
|
query predicate multiplePrimitives(TypeExpr te, int c, PrimitiveType t) {
|
||||||
c = strictcount(PrimitiveType t0 | resolveTypeExpr(te, t0)) and
|
c = strictcount(PrimitiveType t0 | resolveTypeExpr(te, t0)) and
|
||||||
|
|||||||
@@ -164,6 +164,12 @@ private AstNode aliveStep(AstNode prev) {
|
|||||||
result = prev.(Annotation).getAChild()
|
result = prev.(Annotation).getAChild()
|
||||||
or
|
or
|
||||||
result = prev.(Predicate).getReturnType().getDeclaration()
|
result = prev.(Predicate).getReturnType().getDeclaration()
|
||||||
|
or
|
||||||
|
// a module parameter is alive is the module is alive
|
||||||
|
prev.(Module).hasParameter(_, _, result)
|
||||||
|
or
|
||||||
|
// the implements of a module
|
||||||
|
result = prev.(Module).getImplements(_)
|
||||||
}
|
}
|
||||||
|
|
||||||
private AstNode deprecated() {
|
private AstNode deprecated() {
|
||||||
|
|||||||
@@ -28,14 +28,15 @@ class Loc extends TLoc {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private predicate resolveModule(ModuleRef ref, FileOrModule target, string kind) {
|
private predicate resolveModule(TypeRef ref, FileOrModule target, string kind) {
|
||||||
target = ref.getResolvedModule() and
|
target = ref.getResolvedModule() and
|
||||||
kind = "module"
|
kind = "module"
|
||||||
}
|
}
|
||||||
|
|
||||||
private predicate resolveType(TypeExpr ref, AstNode target, string kind) {
|
private predicate resolveType(TypeExpr ref, AstNode target, string kind) {
|
||||||
target = ref.getResolvedType().getDeclaration() and
|
target = ref.getResolvedType().getDeclaration() and
|
||||||
kind = "type"
|
kind = "type" and
|
||||||
|
not resolveModule(ref, _, _) // modules are types, so we exclude them here.
|
||||||
}
|
}
|
||||||
|
|
||||||
private predicate resolvePredicate(PredicateExpr ref, Predicate target, string kind) {
|
private predicate resolvePredicate(PredicateExpr ref, Predicate target, string kind) {
|
||||||
|
|||||||
@@ -656,7 +656,8 @@ ql_module_instantiation_child(
|
|||||||
);
|
);
|
||||||
|
|
||||||
ql_module_instantiation_def(
|
ql_module_instantiation_def(
|
||||||
unique int id: @ql_module_instantiation
|
unique int id: @ql_module_instantiation,
|
||||||
|
int name: @ql_module_name ref
|
||||||
);
|
);
|
||||||
|
|
||||||
@ql_moduleMember_child_type = @ql_annotation | @ql_classless_predicate | @ql_dataclass | @ql_datatype | @ql_import_directive | @ql_module | @ql_select | @ql_token_qldoc
|
@ql_moduleMember_child_type = @ql_annotation | @ql_classless_predicate | @ql_dataclass | @ql_datatype | @ql_import_directive | @ql_module | @ql_select | @ql_token_qldoc
|
||||||
|
|||||||
@@ -33,11 +33,15 @@ where
|
|||||||
or
|
or
|
||||||
AstConsistency::nonTotalGetParent(node) and msg = "AstConsistency::nonTotalGetParent"
|
AstConsistency::nonTotalGetParent(node) and msg = "AstConsistency::nonTotalGetParent"
|
||||||
or
|
or
|
||||||
|
AstConsistency::nonUniqueParent(node) and msg = "AstConsistency::nonUniqueParent"
|
||||||
|
or
|
||||||
TypeConsistency::noResolve(node) and msg = "TypeConsistency::noResolve"
|
TypeConsistency::noResolve(node) and msg = "TypeConsistency::noResolve"
|
||||||
or
|
or
|
||||||
ModConsistency::noResolve(node) and msg = "ModConsistency::noResolve"
|
ModConsistency::noResolve(node) and msg = "ModConsistency::noResolve"
|
||||||
or
|
or
|
||||||
ModConsistency::noResolveModuleExpr(node) and msg = "ModConsistency::noResolveModuleExpr"
|
ModConsistency::noName(node) and msg = "ModConsistency::noName"
|
||||||
|
or
|
||||||
|
ModConsistency::nonUniqueName(node) and msg = "ModConsistency::nonUniqueName"
|
||||||
or
|
or
|
||||||
VarConsistency::noFieldDef(node) and msg = "VarConsistency::noFieldDef"
|
VarConsistency::noFieldDef(node) and msg = "VarConsistency::noFieldDef"
|
||||||
or
|
or
|
||||||
|
|||||||
63
ql/ql/test/callgraph/ParamModules.qll
Normal file
63
ql/ql/test/callgraph/ParamModules.qll
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
module PredicateSig {
|
||||||
|
signature predicate fooSig(int i);
|
||||||
|
|
||||||
|
module UsesFoo<fooSig/1 fooImpl> {
|
||||||
|
predicate bar(int i) { fooImpl(i + 1) }
|
||||||
|
}
|
||||||
|
|
||||||
|
predicate myFoo(int i) { i = 42 }
|
||||||
|
|
||||||
|
predicate use(int i) { UsesFoo<myFoo/1>::bar(i) }
|
||||||
|
}
|
||||||
|
|
||||||
|
module ClassSig {
|
||||||
|
signature class FooSig extends int;
|
||||||
|
|
||||||
|
module UsesFoo<FooSig FooImpl> {
|
||||||
|
FooImpl getAnEven() { result % 2 = 0 }
|
||||||
|
}
|
||||||
|
|
||||||
|
class MyFoo extends int {
|
||||||
|
MyFoo() { this = [0 .. 10] }
|
||||||
|
|
||||||
|
string myFoo() { result = "myFoo" }
|
||||||
|
}
|
||||||
|
|
||||||
|
string use() { result = UsesFoo<MyFoo>::getAnEven().myFoo() }
|
||||||
|
}
|
||||||
|
|
||||||
|
module ModuleSig {
|
||||||
|
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() }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -21,6 +21,16 @@ getTarget
|
|||||||
| Overrides.qll:24:39:24:48 | MemberCall | Overrides.qll:22:12:22:44 | ClassPredicate bar |
|
| Overrides.qll:24:39:24:48 | MemberCall | Overrides.qll:22:12:22:44 | ClassPredicate bar |
|
||||||
| Overrides.qll:28:3:28:9 | MemberCall | Overrides.qll:6:3:6:29 | ClassPredicate bar |
|
| Overrides.qll:28:3:28:9 | MemberCall | Overrides.qll:6:3:6:29 | ClassPredicate bar |
|
||||||
| Overrides.qll:29:3:29:10 | MemberCall | Overrides.qll:8:3:8:41 | ClassPredicate baz |
|
| Overrides.qll:29:3:29:10 | MemberCall | Overrides.qll:8:3:8:41 | ClassPredicate baz |
|
||||||
|
| ParamModules.qll:5:28:5:41 | PredicateCall | ParamModules.qll:2:13:2:36 | ClasslessPredicate fooSig |
|
||||||
|
| ParamModules.qll:5:28:5:41 | PredicateCall | ParamModules.qll:8:3:8:35 | ClasslessPredicate myFoo |
|
||||||
|
| 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: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/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 |
|
| packs/src/SrcThing.qll:4:3:4:8 | PredicateCall | packs/lib/LibThing/Foo.qll:1:1:1:30 | ClasslessPredicate foo |
|
||||||
@@ -33,3 +43,5 @@ exprPredicate
|
|||||||
| Foo.qll:26:22:26:31 | predicate | Foo.qll:20:3:20:54 | ClasslessPredicate myThing2 |
|
| Foo.qll:26:22:26:31 | predicate | Foo.qll:20:3:20:54 | ClasslessPredicate myThing2 |
|
||||||
| Foo.qll:47:55:47:62 | predicate | Foo.qll:42:20:42:27 | NewTypeBranch MkRoot |
|
| Foo.qll:47:55:47:62 | predicate | Foo.qll:42:20:42:27 | NewTypeBranch MkRoot |
|
||||||
| Foo.qll:47:65:47:70 | predicate | Foo.qll:44:9:44:56 | ClasslessPredicate edge |
|
| Foo.qll:47:65:47:70 | predicate | Foo.qll:44:9:44:56 | ClasslessPredicate edge |
|
||||||
|
| ParamModules.qll:4:18:4:25 | predicate | ParamModules.qll:2:13:2:36 | ClasslessPredicate fooSig |
|
||||||
|
| ParamModules.qll:10:34:10:40 | predicate | ParamModules.qll:8:3:8:35 | ClasslessPredicate myFoo |
|
||||||
|
|||||||
Reference in New Issue
Block a user