Skip to content

Commit c4ec674

Browse files
committed
Ruby: support anonymous (hash)splat parameters/arguments
1 parent 4d3e2bb commit c4ec674

File tree

6 files changed

+62
-22
lines changed

6 files changed

+62
-22
lines changed

ruby/ql/lib/codeql/ruby/ast/Parameter.qll

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -181,7 +181,10 @@ class HashSplatParameter extends NamedParameter, THashSplatParameter {
181181

182182
final override string getAPrimaryQlClass() { result = "HashSplatParameter" }
183183

184-
final override LocalVariable getVariable() { result = TLocalVariableReal(_, _, g.getName()) }
184+
final override LocalVariable getVariable() {
185+
result = TLocalVariableReal(_, _, g.getName()) or
186+
result = TLocalVariableSynth(this, 0)
187+
}
185188

186189
final override string toString() {
187190
result = "**" + this.getName()
@@ -307,7 +310,10 @@ class SplatParameter extends NamedParameter, TSplatParameter {
307310

308311
final override string getAPrimaryQlClass() { result = "SplatParameter" }
309312

310-
final override LocalVariable getVariable() { result = TLocalVariableReal(_, _, g.getName()) }
313+
final override LocalVariable getVariable() {
314+
result = TLocalVariableReal(_, _, g.getName()) or
315+
result = TLocalVariableSynth(this, 0)
316+
}
311317

312318
final override string toString() {
313319
result = "*" + this.getName()

ruby/ql/lib/codeql/ruby/ast/Variable.qll

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ class LocalVariable extends Variable, TLocalVariable {
3636
/** Gets the access where this local variable is first introduced. */
3737
VariableAccess getDefiningAccess() {
3838
result = this.(LocalVariableReal).getDefiningAccessImpl() or
39-
synthChild(any(BlockParameter p | this = p.getVariable()), 0, result)
39+
synthChild(any(NamedParameter p | this = p.getVariable()), 0, result)
4040
}
4141

4242
/**
@@ -120,7 +120,7 @@ class VariableAccess extends Expr instanceof VariableAccessImpl {
120120
or
121121
this = any(HashPattern p).getValue(_)
122122
or
123-
synthChild(any(BlockParameter p), 0, this)
123+
synthChild(any(NamedParameter p), 0, this)
124124
}
125125
}
126126

ruby/ql/lib/codeql/ruby/ast/internal/Operation.qll

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,10 @@ class SplatExprReal extends UnaryOperationImpl, TSplatExprReal {
4242

4343
final override string getOperatorImpl() { result = "*" }
4444

45-
final override Expr getOperandImpl() { toGenerated(result) = g.getChild() }
45+
final override Expr getOperandImpl() {
46+
toGenerated(result) = g.getChild() or
47+
synthChild(this, 0, result)
48+
}
4649
}
4750

4851
class SplatExprSynth extends UnaryOperationImpl, TSplatExprSynth {
@@ -56,7 +59,10 @@ class HashSplatExprImpl extends UnaryOperationImpl, THashSplatExpr {
5659

5760
HashSplatExprImpl() { this = THashSplatExpr(g) }
5861

59-
final override Expr getOperandImpl() { toGenerated(result) = g.getChild() }
62+
final override Expr getOperandImpl() {
63+
toGenerated(result) = g.getChild() or
64+
synthChild(this, 0, result)
65+
}
6066

6167
final override string getOperatorImpl() { result = "**" }
6268
}

ruby/ql/lib/codeql/ruby/ast/internal/Synthesis.qll

Lines changed: 32 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1317,46 +1317,62 @@ private module ImplicitHashValueSynthesis {
13171317

13181318
/**
13191319
* ```rb
1320-
* def foo(&)
1321-
* bar(&)
1320+
* def foo(*, **, &)
1321+
* bar(*, **, &)
13221322
* end
13231323
* ```
13241324
* desugars to,
13251325
* ```rb
1326-
* def foo(&__synth_0)
1327-
* bar(&__synth_0)
1326+
* def foo(*__synth_0, **__synth_1, &__synth_2)
1327+
* bar(*__synth_0, **__synth_1, &__synth_2)
13281328
* end
13291329
* ```
13301330
*/
1331-
private module AnonymousBlockParameterSynth {
1332-
private BlockParameter anonymousBlockParameter() {
1331+
private module AnonymousParameterSynth {
1332+
private class AnonymousParameter = TBlockParameter or THashSplatParameter or TSplatParameter;
1333+
1334+
private class AnonymousArgument = TBlockArgument or THashSplatExpr or TSplatExpr;
1335+
1336+
private AnonymousParameter anonymousParameter() {
13331337
exists(Ruby::BlockParameter p | not exists(p.getName()) and toGenerated(result) = p)
1338+
or
1339+
exists(Ruby::SplatParameter p | not exists(p.getName()) and toGenerated(result) = p)
1340+
or
1341+
exists(Ruby::HashSplatParameter p | not exists(p.getName()) and toGenerated(result) = p)
13341342
}
13351343

1336-
private BlockArgument anonymousBlockArgument() {
1344+
private AnonymousArgument anonymousArgument() {
13371345
exists(Ruby::BlockArgument p | not exists(p.getChild()) and toGenerated(result) = p)
1346+
or
1347+
exists(Ruby::SplatArgument p | not exists(p.getChild()) and toGenerated(result) = p)
1348+
or
1349+
exists(Ruby::HashSplatArgument p | not exists(p.getChild()) and toGenerated(result) = p)
13381350
}
13391351

1340-
private class AnonymousBlockParameterSynthesis extends Synthesis {
1352+
private class AnonymousParameterSynthesis extends Synthesis {
13411353
final override predicate child(AstNode parent, int i, Child child) {
13421354
i = 0 and
1343-
parent = anonymousBlockParameter() and
1355+
parent = anonymousParameter() and
13441356
child = SynthChild(LocalVariableAccessSynthKind(TLocalVariableSynth(parent, 0)))
13451357
}
13461358

1347-
final override predicate localVariable(AstNode n, int i) {
1348-
n = anonymousBlockParameter() and i = 0
1349-
}
1359+
final override predicate localVariable(AstNode n, int i) { n = anonymousParameter() and i = 0 }
13501360
}
13511361

1352-
private class AnonymousBlockArgumentSynthesis extends Synthesis {
1362+
private class AnonymousArgumentSynthesis extends Synthesis {
13531363
final override predicate child(AstNode parent, int i, Child child) {
13541364
i = 0 and
1355-
parent = anonymousBlockArgument() and
1356-
exists(BlockParameter param |
1357-
param = anonymousBlockParameter() and
1365+
parent = anonymousArgument() and
1366+
exists(AnonymousParameter param |
1367+
param = anonymousParameter() and
13581368
scopeOf(toGenerated(parent)).getEnclosingMethod() = scopeOf(toGenerated(param)) and
13591369
child = SynthChild(LocalVariableAccessSynthKind(TLocalVariableSynth(param, 0)))
1370+
|
1371+
param instanceof TBlockParameter and parent instanceof TBlockArgument
1372+
or
1373+
param instanceof TSplatParameter and parent instanceof TSplatExpr
1374+
or
1375+
param instanceof THashSplatParameter and parent instanceof THashSplatExpr
13601376
)
13611377
}
13621378
}

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -269,10 +269,12 @@ module SomeModule
269269
# splat argument
270270
foo(*bar)
271271
foo(*X::bar)
272+
foo(*)
272273

273274
# hash-splat argument
274275
foo(**bar)
275276
foo(**X::bar)
277+
foo(**)
276278

277279
# the value in a keyword argument
278280
foo(blah: bar)

ruby/ql/test/library-tests/ast/params/params.rb

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,3 +84,13 @@ def anonymous_block_parameter(array, &)
8484
end
8585

8686
run_block { |x; y, z | puts x }
87+
88+
# Anonymous splat parameter
89+
def anonymous_splat_parameter(array, *)
90+
array.concat(*)
91+
end
92+
93+
# Anonymous hash splat parameter
94+
def anonymous_hash_splat_parameter(hash, **)
95+
hash.merge(**)
96+
end

0 commit comments

Comments
 (0)