Skip to content

Commit 6adff6f

Browse files
authored
Merge pull request #171 from github/self_nodes
Create synthetic `self` nodes for calls without explicit receivers
2 parents bc6aec7 + fdccd5d commit 6adff6f

10 files changed

Lines changed: 822 additions & 248 deletions

File tree

ql/src/codeql_ruby/AST.qll

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,14 +32,16 @@ class AstNode extends TAstNode {
3232
/** Gets the enclosing module, if any. */
3333
ModuleBase getEnclosingModule() {
3434
exists(Scope::Range s |
35-
s = scopeOf(toGenerated(this)) and toGenerated(result) = s.getEnclosingModule()
35+
s = scopeOf(toGeneratedInclSynth(this)) and
36+
toGeneratedInclSynth(result) = s.getEnclosingModule()
3637
)
3738
}
3839

3940
/** Gets the enclosing method, if any. */
4041
MethodBase getEnclosingMethod() {
4142
exists(Scope::Range s |
42-
s = scopeOf(toGenerated(this)) and toGenerated(result) = s.getEnclosingMethod()
43+
s = scopeOf(toGeneratedInclSynth(this)) and
44+
toGeneratedInclSynth(result) = s.getEnclosingMethod()
4345
)
4446
}
4547

@@ -48,7 +50,7 @@ class AstNode extends TAstNode {
4850
string toString() { none() }
4951

5052
/** Gets the location of this node. */
51-
Location getLocation() { result = toGenerated(this).getLocation() }
53+
Location getLocation() { result = toGeneratedInclSynth(this).getLocation() }
5254

5355
/** Gets a child node of this `AstNode`. */
5456
final AstNode getAChild() { result = this.getAChild(_) }
@@ -62,4 +64,16 @@ class AstNode extends TAstNode {
6264
*/
6365
cached
6466
AstNode getAChild(string pred) { none() }
67+
68+
/**
69+
* Holds if this node was synthesized to represent an implicit AST node not
70+
* present in the source code. In the following example method call, the
71+
* receiver is an implicit `self` reference, for which there is a synthesized
72+
* `Self` node.
73+
*
74+
* ```rb
75+
* foo(123)
76+
* ```
77+
*/
78+
predicate isSynthesized() { this instanceof TImplicitSelf }
6579
}

ql/src/codeql_ruby/ast/Call.qll

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,8 @@ private class IdentifierMethodCall extends MethodCall, TIdentifierMethodCall {
136136
IdentifierMethodCall() { this = TIdentifierMethodCall(g) }
137137

138138
final override string getMethodName() { result = getMethodName(this, g.getValue()) }
139+
140+
final override Self getReceiver() { result = TImplicitSelf(g) }
139141
}
140142

141143
private class ScopeResolutionMethodCall extends MethodCall, TScopeResolutionMethodCall {
@@ -159,6 +161,13 @@ private class RegularMethodCall extends MethodCall, TRegularMethodCall {
159161
or
160162
not exists(g.getReceiver()) and
161163
toGenerated(result) = g.getMethod().(Generated::ScopeResolution).getScope()
164+
or
165+
// If there's no explicit receiver (or scope resolution that acts like a
166+
// receiver), then the receiver is implicitly `self`. N.B. `::Foo()` is
167+
// not valid Ruby.
168+
not exists(g.getReceiver()) and
169+
not exists(g.getMethod().(Generated::ScopeResolution).getScope()) and
170+
result = TImplicitSelf(g)
162171
}
163172

164173
final override string getMethodName() {

ql/src/codeql_ruby/ast/Expr.qll

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,10 @@ class Expr extends Stmt, TExpr { }
1414
* - `self == other`
1515
* - `self.method_name`
1616
* - `def self.method_name ... end`
17+
*
18+
* This also includes implicit references to the current object in method
19+
* calls. For example, the method call `foo(123)` has an implicit `self`
20+
* receiver, and is equivalent to the explicit `self.foo(123)`.
1721
*/
1822
class Self extends Expr, TSelf {
1923
final override string getAPrimaryQlClass() { result = "Self" }

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

Lines changed: 36 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,7 @@ private module Cached {
103103
TEndBlock(Generated::EndBlock g) or
104104
TEnsure(Generated::Ensure g) or
105105
TEqExpr(Generated::Binary g) { g instanceof @binary_equalequal } or
106+
TExplicitSelf(Generated::Self g) or
106107
TExponentExpr(Generated::Binary g) { g instanceof @binary_starstar } or
107108
TFalseLiteral(Generated::False g) or
108109
TFloatLiteral(Generated::Float g) { not any(Generated::Rational r).getChild() = g } or
@@ -118,9 +119,16 @@ private module Cached {
118119
THashSplatArgument(Generated::HashSplatArgument g) or
119120
THashSplatParameter(Generated::HashSplatParameter g) or
120121
THereDoc(Generated::HeredocBeginning g) or
121-
TIdentifierMethodCall(Generated::Identifier g) { vcall(g) and not access(g, _) } or
122+
TIdentifierMethodCall(Generated::Identifier g) { isIdentifierMethodCall(g) } or
122123
TIf(Generated::If g) or
123124
TIfModifierExpr(Generated::IfModifier g) or
125+
TImplicitSelf(Generated::AstNode g) {
126+
isIdentifierMethodCall(g)
127+
or
128+
isRegularMethodCall(g) and
129+
not exists(g.(Generated::Call).getReceiver()) and
130+
not exists(g.(Generated::Call).getMethod().(Generated::ScopeResolution).getScope())
131+
} or
124132
TInstanceVariableAccess(Generated::InstanceVariable g, AST::InstanceVariable v) {
125133
InstanceVariableAccess::range(g, v)
126134
} or
@@ -157,7 +165,7 @@ private module Cached {
157165
TRegexLiteral(Generated::Regex g) or
158166
TRegexMatchExpr(Generated::Binary g) { g instanceof @binary_equaltilde } or
159167
TRegularArrayLiteral(Generated::Array g) or
160-
TRegularMethodCall(Generated::Call g) { not g.getMethod() instanceof Generated::Super } or
168+
TRegularMethodCall(Generated::Call g) { isRegularMethodCall(g) } or
161169
TRegularStringLiteral(Generated::String g) or
162170
TRegularSuperCall(Generated::Call g) { g.getMethod() instanceof Generated::Super } or
163171
TRescueClause(Generated::Rescue g) or
@@ -179,7 +187,6 @@ private module Cached {
179187
i = g.getName() and
180188
not exists(Generated::Call c | c.getMethod() = g)
181189
} or
182-
TSelf(Generated::Self g) or
183190
TSimpleParameter(Generated::Identifier g) { g instanceof Parameter::Range } or
184191
TSimpleSymbolLiteral(Generated::SimpleSymbol g) or
185192
TSingletonClass(Generated::SingletonClass g) or
@@ -223,7 +230,19 @@ private module Cached {
223230
TWhileModifierExpr(Generated::WhileModifier g) or
224231
TYieldCall(Generated::Yield g)
225232

226-
/** Gets the underlying TreeSitter entity for a given AST node. */
233+
private predicate isIdentifierMethodCall(Generated::Identifier g) {
234+
vcall(g) and not access(g, _)
235+
}
236+
237+
private predicate isRegularMethodCall(Generated::Call g) {
238+
not g.getMethod() instanceof Generated::Super
239+
}
240+
241+
/**
242+
* Gets the underlying TreeSitter entity for a given AST node. This does not
243+
* include synthesized AST nodes, because they are not the primary AST node
244+
* for any given generated node.
245+
*/
227246
cached
228247
Generated::AstNode toGenerated(AST::AstNode n) {
229248
n = TAddExpr(result) or
@@ -274,6 +293,7 @@ private module Cached {
274293
n = TEndBlock(result) or
275294
n = TEnsure(result) or
276295
n = TEqExpr(result) or
296+
n = TExplicitSelf(result) or
277297
n = TExponentExpr(result) or
278298
n = TFalseLiteral(result) or
279299
n = TFloatLiteral(result) or
@@ -329,7 +349,6 @@ private module Cached {
329349
n = TReturnStmt(result) or
330350
n = TScopeResolutionConstantAccess(result, _) or
331351
n = TScopeResolutionMethodCall(result, _) or
332-
n = TSelf(result) or
333352
n = TSimpleParameter(result) or
334353
n = TSimpleSymbolLiteral(result) or
335354
n = TSingletonClass(result) or
@@ -365,6 +384,16 @@ private module Cached {
365384
n = TWhileModifierExpr(result) or
366385
n = TYieldCall(result)
367386
}
387+
388+
/**
389+
* Like `toGenerated`, but also returns generated nodes for synthesized AST
390+
* nodes.
391+
*/
392+
cached
393+
Generated::AstNode toGeneratedInclSynth(AST::AstNode n) {
394+
result = toGenerated(n) or
395+
n = TImplicitSelf(result)
396+
}
368397
}
369398

370399
import Cached
@@ -392,6 +421,8 @@ class TConditionalLoop = TWhileExpr or TUntilExpr or TWhileModifierExpr or TUnti
392421

393422
class TLoop = TConditionalLoop or TForExpr;
394423

424+
class TSelf = TExplicitSelf or TImplicitSelf;
425+
395426
class TExpr =
396427
TSelf or TArgumentList or TRescueClause or TRescueModifierExpr or TPair or TStringConcatenation or
397428
TCall or TBlockArgument or TSplatArgument or THashSplatArgument or TConstantAccess or

ql/src/codeql_ruby/controlflow/BasicBlocks.qll

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -358,9 +358,9 @@ private module JoinBlockPredecessors {
358358
private predicate idOf(Generated::AstNode x, int y) = equivalenceRelation(id/2)(x, y)
359359

360360
int getId(JoinBlockPredecessor jbp) {
361-
idOf(toGenerated(jbp.getFirstNode().(AstCfgNode).getNode()), result)
361+
idOf(toGeneratedInclSynth(jbp.getFirstNode().(AstCfgNode).getNode()), result)
362362
or
363-
idOf(toGenerated(jbp.(EntryBasicBlock).getScope()), result)
363+
idOf(toGeneratedInclSynth(jbp.(EntryBasicBlock).getScope()), result)
364364
}
365365

366366
string getSplitString(JoinBlockPredecessor jbp) {

ql/src/codeql_ruby/controlflow/internal/ControlFlowGraphImpl.qll

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1318,7 +1318,7 @@ private module Cached {
13181318
/** Gets the CFG scope of node `n`. */
13191319
cached
13201320
CfgScope getCfgScopeImpl(AstNode n) {
1321-
result = parent*(ASTInternal::fromGenerated(scopeOf(ASTInternal::toGenerated(n))))
1321+
result = parent*(ASTInternal::fromGenerated(scopeOf(ASTInternal::toGeneratedInclSynth(n))))
13221322
}
13231323

13241324
private predicate isAbnormalExitType(SuccessorType t) {

0 commit comments

Comments
 (0)