Skip to content

Commit 623ee59

Browse files
authored
Merge pull request #106 from github/self
2 parents 6423ea3 + 30804f7 commit 623ee59

File tree

7 files changed

+140
-4
lines changed

7 files changed

+140
-4
lines changed

ql/src/codeql_ruby/ast/Call.qll

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,22 @@ class YieldCall extends Call, @yield {
108108
final override string getAPrimaryQlClass() { result = "YieldCall" }
109109
}
110110

111+
/**
112+
* A call to `super`.
113+
* ```rb
114+
* class Foo < Bar
115+
* def baz
116+
* super
117+
* end
118+
* end
119+
* ```
120+
*/
121+
class SuperCall extends Call {
122+
final override SuperCall::Range range;
123+
124+
final override string getAPrimaryQlClass() { result = "SuperCall" }
125+
}
126+
111127
/**
112128
* A block argument in a method call.
113129
* ```rb

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

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,46 @@ module YieldCall {
6666
}
6767
}
6868

69+
module SuperCall {
70+
abstract class Range extends Call::Range { }
71+
72+
private class SuperTokenCallRange extends SuperCall::Range, @token_super {
73+
final override Generated::Super generated;
74+
75+
// N.B. `super` tokens can never be accesses, so any vcall with `super` must
76+
// be a call.
77+
SuperTokenCallRange() { vcall(this) }
78+
79+
final override Expr getReceiver() { none() }
80+
81+
final override string getMethodName() { result = generated.getValue() }
82+
83+
final override ScopeResolution getMethodScopeResolution() { none() }
84+
85+
final override Expr getArgument(int n) { none() }
86+
87+
final override Block getBlock() { none() }
88+
}
89+
90+
private class RegularSuperCallRange extends SuperCall::Range, @call {
91+
final override Generated::Call generated;
92+
93+
RegularSuperCallRange() { generated.getMethod() instanceof Generated::Super }
94+
95+
final override Expr getReceiver() { none() }
96+
97+
final override string getMethodName() {
98+
result = generated.getMethod().(Generated::Super).getValue()
99+
}
100+
101+
final override ScopeResolution getMethodScopeResolution() { none() }
102+
103+
final override Expr getArgument(int n) { result = generated.getArguments().getChild(n) }
104+
105+
final override Block getBlock() { result = generated.getBlock() }
106+
}
107+
}
108+
69109
module BlockArgument {
70110
class Range extends Expr::Range, @block_argument {
71111
final override Generated::BlockArgument generated;

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

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,9 @@ private module Cached {
125125
not scope.(CapturingScope).inherits(name, _)
126126
}
127127

128+
// Token types that can be vcalls
129+
private class VcallToken = @token_identifier or @token_super;
130+
128131
/**
129132
* Holds if `i` is an `identifier` node occurring in the context where it
130133
* should be considered a VCALL. VCALL is the term that MRI/Ripper uses
@@ -139,7 +142,7 @@ private module Cached {
139142
* ```
140143
*/
141144
cached
142-
predicate vcall(Generated::Identifier i) {
145+
predicate vcall(VcallToken i) {
143146
i = any(Generated::ArgumentList x).getChild(_)
144147
or
145148
i = any(Generated::Array x).getChild(_)

ql/test/library-tests/ast/calls/calls.expected

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,11 @@ callsWithNoReceiverArgumentsOrBlock
7272
| calls.rb:204:6:204:8 | call to bar | bar |
7373
| calls.rb:207:7:207:9 | call to bar | bar |
7474
| calls.rb:210:11:210:13 | call to bar | bar |
75+
| calls.rb:217:5:217:9 | call to super | super |
76+
| calls.rb:218:5:218:11 | call to super | super |
77+
| calls.rb:234:5:234:7 | call to foo | foo |
78+
| calls.rb:235:5:235:14 | call to super | super |
79+
| calls.rb:236:5:236:9 | call to super | super |
7580
callsWithScopeResolutionName
7681
| calls.rb:5:1:5:10 | call to bar | calls.rb:5:1:5:8 | ...::bar |
7782
callsWithArguments
@@ -86,16 +91,54 @@ callsWithArguments
8691
| calls.rb:204:1:204:9 | call to foo | foo | 0 | calls.rb:204:5:204:8 | *... |
8792
| calls.rb:207:1:207:10 | call to foo | foo | 0 | calls.rb:207:5:207:9 | **... |
8893
| calls.rb:210:1:210:14 | call to foo | foo | 0 | calls.rb:210:5:210:13 | Pair |
94+
| calls.rb:219:5:219:16 | call to super | super | 0 | calls.rb:219:11:219:16 | blah |
95+
| calls.rb:220:5:220:17 | call to super | super | 0 | calls.rb:220:11:220:11 | 1 |
96+
| calls.rb:220:5:220:17 | call to super | super | 1 | calls.rb:220:14:220:14 | 2 |
97+
| calls.rb:220:5:220:17 | call to super | super | 2 | calls.rb:220:17:220:17 | 3 |
98+
| calls.rb:223:5:223:30 | call to super | super | 0 | calls.rb:223:11:223:11 | 4 |
99+
| calls.rb:223:5:223:30 | call to super | super | 1 | calls.rb:223:14:223:14 | 5 |
100+
| calls.rb:224:5:224:33 | call to super | super | 0 | calls.rb:224:11:224:11 | 6 |
101+
| calls.rb:224:5:224:33 | call to super | super | 1 | calls.rb:224:14:224:14 | 7 |
89102
callsWithReceiver
90103
| calls.rb:8:1:8:7 | call to bar | calls.rb:8:1:8:3 | 123 |
91104
| calls.rb:22:1:24:3 | call to bar | calls.rb:22:1:22:3 | 123 |
92105
| calls.rb:86:1:86:9 | call to bar | calls.rb:86:1:86:3 | call to foo |
106+
| calls.rb:234:5:234:13 | call to super | calls.rb:234:5:234:7 | call to foo |
107+
| calls.rb:236:5:236:15 | call to super | calls.rb:236:5:236:9 | call to super |
93108
callsWithBlock
94109
| calls.rb:14:1:14:17 | call to foo | calls.rb:14:5:14:17 | { ... } |
95110
| calls.rb:17:1:19:3 | call to foo | calls.rb:17:5:19:3 | do ... end |
96111
| calls.rb:22:1:24:3 | call to bar | calls.rb:22:16:24:3 | do ... end |
97112
| calls.rb:80:1:80:13 | call to foo | calls.rb:80:7:80:13 | { ... } |
98113
| calls.rb:83:1:83:16 | call to foo | calls.rb:83:7:83:16 | do ... end |
114+
| calls.rb:221:5:221:23 | call to super | calls.rb:221:11:221:23 | { ... } |
115+
| calls.rb:222:5:222:26 | call to super | calls.rb:222:11:222:26 | do ... end |
116+
| calls.rb:223:5:223:30 | call to super | calls.rb:223:16:223:30 | { ... } |
117+
| calls.rb:224:5:224:33 | call to super | calls.rb:224:16:224:33 | do ... end |
99118
yieldCalls
100119
| calls.rb:28:3:28:7 | call to yield |
101120
| calls.rb:33:3:33:16 | call to yield |
121+
superCalls
122+
| calls.rb:217:5:217:9 | call to super |
123+
| calls.rb:218:5:218:11 | call to super |
124+
| calls.rb:219:5:219:16 | call to super |
125+
| calls.rb:220:5:220:17 | call to super |
126+
| calls.rb:221:5:221:23 | call to super |
127+
| calls.rb:222:5:222:26 | call to super |
128+
| calls.rb:223:5:223:30 | call to super |
129+
| calls.rb:224:5:224:33 | call to super |
130+
| calls.rb:236:5:236:9 | call to super |
131+
superCallsWithArguments
132+
| calls.rb:219:5:219:16 | call to super | 0 | calls.rb:219:11:219:16 | blah |
133+
| calls.rb:220:5:220:17 | call to super | 0 | calls.rb:220:11:220:11 | 1 |
134+
| calls.rb:220:5:220:17 | call to super | 1 | calls.rb:220:14:220:14 | 2 |
135+
| calls.rb:220:5:220:17 | call to super | 2 | calls.rb:220:17:220:17 | 3 |
136+
| calls.rb:223:5:223:30 | call to super | 0 | calls.rb:223:11:223:11 | 4 |
137+
| calls.rb:223:5:223:30 | call to super | 1 | calls.rb:223:14:223:14 | 5 |
138+
| calls.rb:224:5:224:33 | call to super | 0 | calls.rb:224:11:224:11 | 6 |
139+
| calls.rb:224:5:224:33 | call to super | 1 | calls.rb:224:14:224:14 | 7 |
140+
superCallsWithBlock
141+
| calls.rb:221:5:221:23 | call to super | calls.rb:221:11:221:23 | { ... } |
142+
| calls.rb:222:5:222:26 | call to super | calls.rb:222:11:222:26 | do ... end |
143+
| calls.rb:223:5:223:30 | call to super | calls.rb:223:16:223:30 | { ... } |
144+
| calls.rb:224:5:224:33 | call to super | calls.rb:224:16:224:33 | do ... end |

ql/test/library-tests/ast/calls/calls.ql

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,3 +22,9 @@ query predicate callsWithReceiver(Call c, Expr rcv) { rcv = c.getReceiver() }
2222
query predicate callsWithBlock(Call c, Block b) { b = c.getBlock() }
2323

2424
query predicate yieldCalls(YieldCall c) { any() }
25+
26+
query predicate superCalls(SuperCall c) { any() }
27+
28+
query predicate superCallsWithArguments(SuperCall c, int n, Expr argN) { argN = c.getArgument(n) }
29+
30+
query predicate superCallsWithBlock(SuperCall c, Block b) { b = c.getBlock() }

ql/test/library-tests/ast/calls/calls.rb

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -207,4 +207,32 @@ module SomeModule
207207
foo(**bar)
208208

209209
# the value in a keyword argument
210-
foo(blah: bar)
210+
foo(blah: bar)
211+
212+
# ------------------------------------------------------------------------------
213+
# calls to `super`
214+
215+
class MyClass
216+
def my_method
217+
super
218+
super()
219+
super 'blah'
220+
super 1, 2, 3
221+
super { |x| x + 1 }
222+
super do |x| x * 2 end
223+
super 4, 5 { |x| x + 100 }
224+
super 6, 7 do |x| x + 200 end
225+
end
226+
end
227+
228+
# ------------------------------------------------------------------------------
229+
# calls to methods simply named `super`, i.e. *not* calls to the same method in
230+
# a parent classs, so these should be Call but not SuperCall
231+
232+
class AnotherClass
233+
def another_method
234+
foo.super
235+
self.super # TODO: this shows up as a call without a receiver, but that should be fixed once we handle `self` expressions
236+
super.super # we expect the receiver to be a SuperCall, while the outer call should not (it's just a regular Call)
237+
end
238+
end

ql/test/library-tests/controlflow/graph/Cfg.expected

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1631,12 +1631,12 @@ cfg.rb:
16311631
#-----| -> exit print (normal)
16321632

16331633
# 144| puts
1634-
#-----| -> super
1634+
#-----| -> call to super
16351635

16361636
# 144| call to print
16371637
#-----| -> call to puts
16381638

1639-
# 144| super
1639+
# 144| call to super
16401640
#-----| -> print
16411641

16421642
# 144| print

0 commit comments

Comments
 (0)