Merge pull request #9575 from erik-krogh/paramModules

QL: support for parameterized modules
This commit is contained in:
Erik Krogh Kristensen
2022-06-17 15:07:48 +02:00
committed by GitHub
15 changed files with 313 additions and 70 deletions

BIN
ql/Cargo.lock generated

Binary file not shown.

View File

@@ -10,7 +10,7 @@ edition = "2018"
flate2 = "1.0"
node-types = { path = "../node-types" }
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"
tracing = "0.1"
tracing-subscriber = { version = "0.3.3", features = ["env-filter"] }

View File

@@ -11,4 +11,4 @@ clap = "2.33"
node-types = { path = "../node-types" }
tracing = "0.1"
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" }

View File

@@ -162,6 +162,8 @@ class QLDoc extends TQLDoc, AstNode {
string getContents() { result = qldoc.getValue() }
override string getAPrimaryQlClass() { result = "QLDoc" }
override AstNode getParent() { result.getQLDoc() = this }
}
class BlockComment extends TBlockComment, AstNode {
@@ -638,7 +640,7 @@ class FieldDecl extends TFieldDecl, AstNode {
/**
* A type reference, such as `DataFlow::Node`.
*/
class TypeExpr extends TType, AstNode {
class TypeExpr extends TType, TypeRef {
QL::TypeExpr type;
TypeExpr() { this = TType(type) }
@@ -675,10 +677,14 @@ class TypeExpr extends TType, AstNode {
*/
ModuleExpr getModule() { toQL(result) = type.getQualifier() }
/**
* Gets the type that this type reference refers to.
*/
Type getResolvedType() { resolveTypeExpr(this, result) }
/** Gets the type that this type reference refers to. */
override Type getResolvedType() {
// resolve type
resolveTypeExpr(this, result)
or
// if it resolves to a module
exists(FileOrModule mod | resolveModuleRef(this, mod) | result = mod.toType())
}
override AstNode getAChild(string 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))
}
/** 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. */
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()
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))
}
/** 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. */
class ModuleRef extends AstNode, TModuleRef {
/** Gets the module that this entity resolves to. */
FileOrModule getResolvedModule() { none() }
/** An entity that resolves to a type. */
class TypeRef extends AstNode, TTypeRef {
abstract Type getResolvedType();
/** 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.
*/
class Import extends TImport, ModuleMember, ModuleRef {
class Import extends TImport, ModuleMember, TypeRef {
QL::ImportDirective 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`. */
@@ -2172,7 +2198,7 @@ class DontCare extends TDontCare, Expr {
}
/** A module expression. Such as `DataFlow` in `DataFlow::Node` */
class ModuleExpr extends TModuleExpr, ModuleRef {
class ModuleExpr extends TModuleExpr, TypeRef {
QL::ModuleExpr me;
ModuleExpr() { this = TModuleExpr(me) }
@@ -2190,6 +2216,10 @@ class ModuleExpr extends TModuleExpr, ModuleRef {
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()
)
}
/**
@@ -2203,7 +2233,9 @@ class ModuleExpr extends TModuleExpr, ModuleRef {
*/
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() }
@@ -2213,7 +2245,39 @@ class ModuleExpr extends TModuleExpr, ModuleRef {
result = super.getAChild(pred)
or
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. */
@@ -2272,7 +2336,7 @@ class Annotation extends TAnnotation, AstNode {
/** Gets the node corresponding to the field `name`. */
string getName() { result = annot.getName().getValue() }
override AstNode getParent() { result = AstNode.super.getParent() }
override AstNode getParent() { result.getAnAnnotation() = this }
override AstNode getAChild(string pred) {
result = super.getAChild(pred)

View File

@@ -83,10 +83,12 @@ class TExpr =
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 TSignatureExpr = TPredicateExpr or TType;
/** DEPRECATED: Alias for 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 QLDoc and node.getLocation().getFile().getExtension() = "dbscheme") // qldoc in dbschemes are not hooked up
}
query predicate nonUniqueParent(AstNode node) { count(node.getParent()) >= 2 }
}

View File

@@ -23,24 +23,30 @@ 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()
or
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 {
@@ -207,23 +213,23 @@ private module Cached {
/** Holds if module expression `me` resolves to `m`. */
cached
predicate resolveModuleExpr(ModuleExpr me, FileOrModule m) {
predicate resolveModuleRef(TypeRef me, FileOrModule m) {
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(TypeRef me, ContainerOrModule enclosing, string name) {
enclosing = getEnclosingModule(me).getEnclosing*() and
name = me.getName()
name = [me.(ModuleExpr).getName(), me.(TypeExpr).getClassName()]
}
}
@@ -231,7 +237,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
}
@@ -253,6 +262,22 @@ private predicate definesModule(
m = TModule(any(Module mod | public = getPublicBool(mod)))
)
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
exists(Import imp, ContainerOrModule m0 |
container = getEnclosingModule(imp) and
@@ -274,7 +299,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,14 +313,6 @@ module ModConsistency {
.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) {
c = strictcount(ContainerOrModule m0 | resolve(imp, m0)) and
c > 1 and
@@ -306,9 +323,16 @@ 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
c > 1 and
resolveModuleExpr(me, m)
}
// This can happen with parameterized modules.
/*
* query predicate multipleResolveModuleRef(ModuleExpr me, int c, ContainerOrModule 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 }
}

View File

@@ -69,6 +69,20 @@ private module Cached {
|
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) {
@@ -162,17 +176,20 @@ module PredConsistency {
c > 1 and
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)
}
}

View File

@@ -999,11 +999,16 @@ module QL {
/** Gets the name of the primary QL class for this element. */
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. */
final SignatureExpr getChild(int i) { ql_module_instantiation_child(this, i, result) }
/** 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. */

View File

@@ -14,7 +14,9 @@ private newtype TType =
TDontCare() or
TClassChar(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"] }
@@ -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) {
result.getDeclaringType() = ty and
result.getName() = name and
@@ -323,22 +345,42 @@ private predicate defines(FileOrModule m, string name, Type t, boolean public) {
public = getPublicBool(im) and
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 {
query predicate noResolve(TypeExpr te) {
not resolveTypeExpr(te, _) and
query predicate noResolve(TypeRef te) {
not exists(te.getResolvedType()) and
not te.getLocation()
.getFile()
.getAbsolutePath()
.regexpMatch(".*/(test|examples|ql-training|recorded-call-graph-metrics)/.*")
}
query predicate multipleResolve(TypeExpr te, int c, Type t) {
c = strictcount(Type t0 | resolveTypeExpr(te, t0)) and
c > 1 and
resolveTypeExpr(te, t)
}
// This can happen with parameterized modules.
/*
* query predicate multipleResolve(TypeExpr te, int c, Type t) {
* c = strictcount(Type t0 | resolveTypeExpr(te, t0)) and
* c > 1 and
* resolveTypeExpr(te, t)
* }
*/
query predicate multiplePrimitives(TypeExpr te, int c, PrimitiveType t) {
c = strictcount(PrimitiveType t0 | resolveTypeExpr(te, t0)) and

View File

@@ -164,6 +164,12 @@ private AstNode aliveStep(AstNode prev) {
result = prev.(Annotation).getAChild()
or
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() {

View File

@@ -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
kind = "module"
}
private predicate resolveType(TypeExpr ref, AstNode target, string kind) {
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) {

View File

@@ -656,7 +656,8 @@ ql_module_instantiation_child(
);
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

View File

@@ -33,11 +33,15 @@ where
or
AstConsistency::nonTotalGetParent(node) and msg = "AstConsistency::nonTotalGetParent"
or
AstConsistency::nonUniqueParent(node) and msg = "AstConsistency::nonUniqueParent"
or
TypeConsistency::noResolve(node) and msg = "TypeConsistency::noResolve"
or
ModConsistency::noResolve(node) and msg = "ModConsistency::noResolve"
or
ModConsistency::noResolveModuleExpr(node) and msg = "ModConsistency::noResolveModuleExpr"
ModConsistency::noName(node) and msg = "ModConsistency::noName"
or
ModConsistency::nonUniqueName(node) and msg = "ModConsistency::nonUniqueName"
or
VarConsistency::noFieldDef(node) and msg = "VarConsistency::noFieldDef"
or

View 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() }
}
}

View File

@@ -21,6 +21,16 @@ getTarget
| 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: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: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 |
@@ -33,3 +43,5 @@ exprPredicate
| 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: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 |