Skip to content

Commit ec905e0

Browse files
authored
Merge pull request #168 from github/aibaars/typetrack-method
Call graph
2 parents 4dc182d + bacbd5e commit ec905e0

16 files changed

Lines changed: 1213 additions & 91 deletions

File tree

ql/src/codeql_ruby/ast/Module.qll

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,10 @@ class Module extends TModule {
2020
/** Gets an `include`d module. */
2121
Module getAnIncludedModule() { result = getAnIncludedModule(this) }
2222

23+
/** Holds if this module is a class. */
24+
pragma[noinline]
25+
predicate isClass() { this.getADeclaration() instanceof ClassDeclaration }
26+
2327
/** Gets a textual representation of this module. */
2428
string toString() {
2529
this = TResolved(result)

ql/src/codeql_ruby/ast/internal/Module.qll

Lines changed: 47 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,13 @@ private import codeql_ruby.ast.Operation
88
private import codeql_ruby.ast.Scope
99

1010
// Names of built-in modules and classes
11-
private string builtin() { result = ["Object", "Kernel", "BasicObject", "Class", "Module"] }
11+
private string builtin() {
12+
result =
13+
[
14+
"Object", "Kernel", "BasicObject", "Class", "Module", "NilClass", "FalseClass", "TrueClass",
15+
"Numeric", "Integer", "Float", "Rational", "Complex", "Array", "Hash", "Symbol", "Proc"
16+
]
17+
}
1218

1319
cached
1420
private module Cached {
@@ -39,9 +45,14 @@ private module Cached {
3945
Module getSuperClass(Module cls) {
4046
cls = TResolved("Object") and result = TResolved("BasicObject")
4147
or
42-
cls = TResolved("Module") and result = TResolved("Object")
48+
cls = TResolved(["Module", "Numeric", "Array", "Hash", "FalseClass", "TrueClass", "NilClass"]) and
49+
result = TResolved("Object")
50+
or
51+
cls = TResolved(["Integer", "Float", "Rational", "Complex"]) and
52+
result = TResolved("Numeric")
4353
or
44-
cls = TResolved("Class") and result = TResolved("Module")
54+
cls = TResolved("Class") and
55+
result = TResolved("Module")
4556
or
4657
not cls = TResolved(builtin()) and
4758
(
@@ -86,6 +97,39 @@ private module Cached {
8697
result = resolveScopeExpr(c.getAnArgument())
8798
)
8899
}
100+
101+
/**
102+
* Resolve constant read access (typically a scope expression) to a qualified module name.
103+
* `resolveScopeExpr/1` picks the best (lowest priority number) result of
104+
* `resolveScopeExpr/2` that resolves to a constant definition. If the constant
105+
* definition is a Namespace then it is returned, if it's a constant assignment then
106+
* the right-hand side of the assignment is resolved.
107+
*/
108+
cached
109+
TResolved resolveScopeExpr(ConstantReadAccess r) {
110+
exists(string qname |
111+
qname =
112+
min(string qn, int p |
113+
isDefinedConstant(qn) and
114+
qn = resolveScopeExpr(r, p) and
115+
// prevent classes/modules that contain/extend themselves
116+
not exists(ConstantWriteAccess w | qn = constantDefinition0(w) |
117+
r = w.getScopeExpr()
118+
or
119+
r = w.(ClassDeclaration).getSuperclassExpr()
120+
)
121+
|
122+
qn order by p
123+
)
124+
|
125+
result = TResolved(qname)
126+
or
127+
exists(ConstantAssignment a |
128+
qname = constantDefinition0(a) and
129+
result = resolveScopeExpr(a.getParent().(Assignment).getRightOperand())
130+
)
131+
)
132+
}
89133
}
90134

91135
import Cached
@@ -103,38 +147,6 @@ private predicate isDefinedConstant(string qualifiedModuleName) {
103147
qualifiedModuleName = [builtin(), constantDefinition0(_)]
104148
}
105149

106-
/**
107-
* Resolve constant read access (typically a scope expression) to a qualified module name.
108-
* `resolveScopeExpr/1` picks the best (lowest priority number) result of
109-
* `resolveScopeExpr/2` that resolves to a constant definition. If the constant
110-
* definition is a Namespace then it is returned, if it's a constant assignment then
111-
* the right-hand side of the assignment is resolved.
112-
*/
113-
private TResolved resolveScopeExpr(ConstantReadAccess r) {
114-
exists(string qname |
115-
qname =
116-
min(string qn, int p |
117-
isDefinedConstant(qn) and
118-
qn = resolveScopeExpr(r, p) and
119-
// prevent classes/modules that contain/extend themselves
120-
not exists(ConstantWriteAccess w | qn = constantDefinition0(w) |
121-
r = w.getScopeExpr()
122-
or
123-
r = w.(ClassDeclaration).getSuperclassExpr()
124-
)
125-
|
126-
qn order by p
127-
)
128-
|
129-
result = TResolved(qname)
130-
or
131-
exists(ConstantAssignment a |
132-
qname = constantDefinition0(a) and
133-
result = resolveScopeExpr(a.getParent().(Assignment).getRightOperand())
134-
)
135-
)
136-
}
137-
138150
private int maxDepth() { result = 1 + max(int level | exists(enclosing(_, level))) }
139151

140152
private ModuleBase enclosing(ModuleBase m, int level) {

ql/src/codeql_ruby/controlflow/CfgNodes.qll

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -123,15 +123,9 @@ abstract private class ExprChildMapping extends Expr {
123123
*/
124124
abstract predicate relevantChild(Expr child);
125125

126-
private AstNode getAChildStar() {
127-
result = this
128-
or
129-
result.getParent() = this.getAChildStar()
130-
}
131-
132126
pragma[noinline]
133127
private BasicBlock getABasicBlockInScope() {
134-
result.getANode() = TAstCfgNode(this.getAChildStar(), _)
128+
result.getANode() = TAstCfgNode(this.getAChild*(), _)
135129
}
136130

137131
pragma[nomagic]
@@ -231,9 +225,23 @@ module ExprNodes {
231225
final ExprCfgNode getRightOperand() { e.hasCfgChild(e.getRightOperand(), this, result) }
232226
}
233227

228+
private class BlockArgumentChildMapping extends ExprChildMapping, BlockArgument {
229+
override predicate relevantChild(Expr e) { e = this.getValue() }
230+
}
231+
232+
/** A control-flow node that wraps a `BlockArgument` AST expression. */
233+
class BlockArgumentCfgNode extends ExprCfgNode {
234+
override BlockArgumentChildMapping e;
235+
236+
final override BlockArgument getExpr() { result = ExprCfgNode.super.getExpr() }
237+
238+
/** Gets the value of this block argument. */
239+
final ExprCfgNode getValue() { e.hasCfgChild(e.getValue(), this, result) }
240+
}
241+
234242
private class CallExprChildMapping extends ExprChildMapping, Call {
235243
override predicate relevantChild(Expr e) {
236-
e = [this.getAnArgument(), this.(MethodCall).getReceiver()]
244+
e = [this.getAnArgument(), this.(MethodCall).getReceiver(), this.(MethodCall).getBlock()]
237245
}
238246
}
239247

@@ -248,6 +256,9 @@ module ExprNodes {
248256

249257
/** Gets the receiver of this call. */
250258
final ExprCfgNode getReceiver() { e.hasCfgChild(e.(MethodCall).getReceiver(), this, result) }
259+
260+
/** Gets the block of this call. */
261+
final ExprCfgNode getBlock() { e.hasCfgChild(e.(MethodCall).getBlock(), this, result) }
251262
}
252263

253264
private class CaseExprChildMapping extends ExprChildMapping, CaseExpr {

0 commit comments

Comments
 (0)