Module resolution

This commit is contained in:
Tom Hvitved
2021-05-26 14:35:50 +02:00
parent c89e924b67
commit ebb1cd3f8f
4 changed files with 285 additions and 3 deletions

View File

@@ -0,0 +1 @@
import codeql_ql.ast.internal.Module::Consistency

View File

@@ -1,5 +1,6 @@
import ql
private import codeql_ql.ast.internal.AstNodes
private import codeql_ql.ast.internal.Module
/** An AST node of a QL program */
class AstNode extends TAstNode {
@@ -45,6 +46,21 @@ class Predicate extends TPredicate, AstNode {
// TODO: ReturnType.
}
class PredicateExpr extends TPredicateExpr, AstNode {
Generated::PredicateExpr pe;
PredicateExpr() { this = TPredicateExpr(pe) }
override string toString() { result = "predicate" }
ModuleExpr getQualifier() {
exists(Generated::AritylessPredicateExpr ape |
ape.getParent() = pe and
toGenerated(result).getParent() = ape
)
}
}
/**
* A classless predicate.
*/
@@ -54,6 +70,13 @@ class ClasslessPredicate extends TClasslessPredicate, Predicate, ModuleMember {
ClasslessPredicate() { this = TClasslessPredicate(member, pred) }
final PredicateExpr getAlias() {
exists(Generated::PredicateAliasBody alias |
alias.getParent() = pred and
toGenerated(result).getParent() = alias
)
}
final override predicate isPrivate() {
member.getAFieldOrChild().(Generated::Annotation).getName().getValue() = "private"
}
@@ -340,6 +363,8 @@ class Call extends TCall, Expr {
Expr getArgument(int i) {
none() // overriden in sublcasses.
}
ModuleExpr getQualifier() { none() }
}
class PredicateCall extends TPredicateCall, Call {
@@ -353,6 +378,13 @@ class PredicateCall extends TPredicateCall, Call {
)
}
final override ModuleExpr getQualifier() {
exists(Generated::AritylessPredicateExpr ape |
ape.getParent() = expr and
toGenerated(result).getParent() = ape
)
}
override string getAPrimaryQlClass() { result = "PredicateCall" }
string getPredicateName() {
@@ -411,10 +443,16 @@ class InlineCast extends TInlineCast, Expr {
Expr getBase() { toGenerated(result) = expr.getChild(0) }
}
/** An entity that resolves to a module. */
class ModuleRef extends AstNode, TModuleRef {
/** Gets the module that this entity resolves to. */
FileOrModule getResolvedModule() { none() }
}
/**
* An import statement.
*/
class Import extends TImport, ModuleMember {
class Import extends TImport, ModuleMember, ModuleRef {
Generated::ImportDirective imp;
Import() { this = TImport(imp) }
@@ -456,6 +494,8 @@ class Import extends TImport, ModuleMember {
member.getAFieldOrChild().(Generated::Annotation).getName().getValue() = "private"
)
}
final override FileOrModule getResolvedModule() { resolve(this, result) }
}
/** A formula, such as `x = 6 and y < 5`. */
@@ -813,7 +853,7 @@ class DontCare extends TDontCare, Expr {
}
/** A module expression. */
class ModuleExpr extends TModuleExpr, AstNode {
class ModuleExpr extends TModuleExpr, ModuleRef {
Generated::ModuleExpr me;
ModuleExpr() { this = TModuleExpr(me) }
@@ -844,5 +884,7 @@ class ModuleExpr extends TModuleExpr, AstNode {
*/
ModuleExpr getQualifier() { result = TModuleExpr(me.getChild()) }
final override FileOrModule getResolvedModule() { resolveModuleExpr(this, result) }
final override string toString() { result = this.getName() }
}

View File

@@ -44,7 +44,8 @@ newtype TAstNode =
TLiteral(Generated::Literal lit) or
TUnaryExpr(Generated::UnaryExpr unaryexpr) or
TDontCare(Generated::Underscore dontcare) or
TModuleExpr(Generated::ModuleExpr me)
TModuleExpr(Generated::ModuleExpr me) or
TPredicateExpr(Generated::PredicateExpr pe)
class TFormula =
TDisjunction or TConjunction or TComparisonFormula or TQuantifier or TNegation or TIfFormula or
@@ -58,6 +59,8 @@ class TExpr =
class TCall = TPredicateCall or TMemberCall or TNoneCall or TAnyCall;
class TModuleRef = TImport or TModuleExpr;
Generated::AstNode toGeneratedFormula(AST::AstNode n) {
n = TConjunction(result) or
n = TDisjunction(result) or
@@ -115,6 +118,8 @@ Generated::AstNode toGenerated(AST::AstNode n) {
or
n = TModuleExpr(result)
or
n = TPredicateExpr(result)
or
n = TPredicateCall(result)
or
n = TMemberCall(result)

View File

@@ -0,0 +1,234 @@
import ql
private import codeql_ql.ast.internal.AstNodes as AstNodes
private import codeql_ql.ast.internal.TreeSitter
private newtype TContainerOrModule =
TFile(File f) or
TFolder(Folder f) or
TModule(Module m)
private class ContainerOrModule extends TContainerOrModule {
string getName() { none() }
ContainerOrModule getEnclosing() { none() }
string toString() { none() }
predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
) {
none()
}
}
private class TFileOrModule = TFile or TModule;
/** A file or a module. */
class FileOrModule extends TFileOrModule, ContainerOrModule { }
private class File_ extends FileOrModule, TFile {
File f;
File_() { this = TFile(f) }
override ContainerOrModule getEnclosing() { result = TFolder(f.getParentContainer()) }
override string getName() { result = f.getStem() }
override string toString() { result = f.toString() }
override predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
) {
filepath = f.getAbsolutePath() and
startline = 0 and
startcolumn = 0 and
endline = 0 and
endcolumn = 0
}
}
private class Folder_ extends ContainerOrModule, TFolder {
Folder f;
Folder_() { this = TFolder(f) }
override ContainerOrModule getEnclosing() { result = TFolder(f.getParentContainer()) }
override string getName() { result = f.getStem() }
override string toString() { result = f.toString() }
override predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
) {
filepath = f.getAbsolutePath() and
startline = 0 and
startcolumn = 0 and
endline = 0 and
endcolumn = 0
}
}
// TODO: Use `AstNode::getParent` once it is total
private Generated::AstNode parent(Generated::AstNode n) {
result = n.getParent() and
not n instanceof Generated::Module
}
private Module getEnclosingModule0(AstNode n) {
AstNodes::toGenerated(result) = parent*(AstNodes::toGenerated(n).getParent())
}
private ContainerOrModule getEnclosingModule(AstNode n) {
result = TModule(getEnclosingModule0(n))
or
not exists(getEnclosingModule0(n)) and
result = TFile(n.getLocation().getFile())
}
class Module_ extends FileOrModule, TModule {
Module m;
Module_() { this = TModule(m) }
override ContainerOrModule getEnclosing() { result = getEnclosingModule(m) }
override string getName() { result = m.getName() }
override string toString() { result = m.toString() }
override predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
) {
m.getLocation().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
}
}
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) |
i = 0 and
(
exists(Container c, Container parent |
// should ideally look at `qlpack.yml` files
parent = imp.getLocation().getFile().getParentContainer+() and
c.getParentContainer() = parent and
q = m.getName()
|
m = TFile(c)
or
m = TFolder(c)
)
or
defines(getEnclosingModule(imp).getEnclosing*(), q, m, _)
)
or
exists(Folder_ mid |
resolveQualifiedName(imp, mid, i - 1) and
m.getEnclosing() = mid and
q = m.getName()
)
)
}
private predicate resolveSelectionName(Import imp, ContainerOrModule m, int i) {
exists(int last |
resolveQualifiedName(imp, m, last) and
last = count(int j | exists(imp.getQualifiedName(j))) - 1
) and
not m instanceof Folder_ and
i = -1
or
exists(ContainerOrModule mid |
resolveSelectionName(imp, mid, i - 1) and
defines(mid, imp.getSelectionName(i), m, true)
)
}
/** Holds if import statement `imp` resolves to `m`. */
predicate resolve(Import imp, FileOrModule m) {
exists(int last |
resolveSelectionName(imp, m, last) and
last = count(int j | exists(imp.getSelectionName(j))) - 1
)
}
/** Holds if module expression `me` resolves to `m`. */
predicate resolveModuleExpr(ModuleExpr me, FileOrModule m) {
not m = TFile(any(File f | f.getExtension() = "ql")) and
not exists(me.getQualifier()) and
defines(getEnclosingModule(me).getEnclosing*(), me.getName(), m, _)
or
exists(FileOrModule mid |
resolveModuleExpr(me.getQualifier(), mid) and
defines(mid, me.getName(), m, true)
)
}
private boolean getPublicBool(ModuleMember m) {
if m.isPrivate() then result = false else result = true
}
/** Holds if `container` defines module `m` with name `name`. */
private predicate defines(
ContainerOrModule container, string name, ContainerOrModule m, boolean public
) {
container = m.getEnclosing() and
name = m.getName() and
(
(m instanceof File_ or m instanceof Folder_) and
public = true
or
m = TModule(any(Module mod | public = getPublicBool(mod)))
)
or
// import X
exists(Import imp, ContainerOrModule m0 |
container = getEnclosingModule(imp) and
resolve(imp, m0) and
not exists(imp.importedAs()) and
defines(m0, name, m, true) and
public = getPublicBool(imp)
)
or
// import X as Y
exists(Import imp |
container = getEnclosingModule(imp) and
name = imp.importedAs() and
resolve(imp, m) and
public = getPublicBool(imp)
)
or
// module X = Y
exists(Module alias |
container = getEnclosingModule(alias) and
name = alias.getName() and
resolveModuleExpr(alias.getAlias(), m) and
public = getPublicBool(alias)
)
}
module Consistency {
query predicate noResolve(Import imp) {
not resolve(imp, _) and
not imp.getLocation().getFile().getAbsolutePath().regexpMatch(".*/(test|examples)/.*")
}
query predicate noResolveModuleExpr(ModuleExpr me) {
not resolveModuleExpr(me, _) and
not me.getLocation().getFile().getAbsolutePath().regexpMatch(".*/(test|examples)/.*")
}
query predicate multipleResolve(Import imp, int c, ContainerOrModule m) {
c = strictcount(ContainerOrModule m0 | resolve(imp, m0)) and
c > 1 and
resolve(imp, m)
}
query predicate multipleResolveModuleExpr(ModuleExpr me, int c, ContainerOrModule m) {
c = strictcount(ContainerOrModule m0 | resolveModuleExpr(me, m0)) and
c > 1 and
resolveModuleExpr(me, m)
}
}