mirror of
https://github.com/github/codeql.git
synced 2026-02-08 19:21:07 +01:00
Module resolution
This commit is contained in:
1
ql/consistency-queries/ModuleResolution.ql
Normal file
1
ql/consistency-queries/ModuleResolution.ql
Normal file
@@ -0,0 +1 @@
|
||||
import codeql_ql.ast.internal.Module::Consistency
|
||||
@@ -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() }
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
234
ql/src/codeql_ql/ast/internal/Module.qll
Normal file
234
ql/src/codeql_ql/ast/internal/Module.qll
Normal 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)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user