Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit a0ecc47

Browse files
committedApr 2, 2025·
Ruby: Synthesize implicit super arguments
1 parent 9d1cecc commit a0ecc47

File tree

15 files changed

+653
-77
lines changed

15 files changed

+653
-77
lines changed
 

Diff for: ‎ruby/ql/lib/codeql/ruby/ast/Expr.qll

+3-22
Original file line numberDiff line numberDiff line change
@@ -248,13 +248,7 @@ class ParenthesizedExpr extends StmtSequence, TParenthesizedExpr {
248248
* baz(qux: 1)
249249
* ```
250250
*/
251-
class Pair extends Expr, TPair {
252-
private Ruby::Pair g;
253-
254-
Pair() { this = TPair(g) }
255-
256-
final override string getAPrimaryQlClass() { result = "Pair" }
257-
251+
class Pair extends Expr instanceof PairImpl {
258252
/**
259253
* Gets the key expression of this pair. For example, the `SymbolLiteral`
260254
* representing the keyword `foo` in the following example:
@@ -266,7 +260,7 @@ class Pair extends Expr, TPair {
266260
* { 'foo' => 123 }
267261
* ```
268262
*/
269-
final Expr getKey() { toGenerated(result) = g.getKey() }
263+
final Expr getKey() { result = PairImpl.super.getKey() }
270264

271265
/**
272266
* Gets the value expression of this pair. For example, the `IntegerLiteral`
@@ -275,20 +269,7 @@ class Pair extends Expr, TPair {
275269
* { 'foo' => 123 }
276270
* ```
277271
*/
278-
final Expr getValue() {
279-
toGenerated(result) = g.getValue() or
280-
synthChild(this, 0, result)
281-
}
282-
283-
final override string toString() { result = "Pair" }
284-
285-
final override AstNode getAChild(string pred) {
286-
result = super.getAChild(pred)
287-
or
288-
pred = "getKey" and result = this.getKey()
289-
or
290-
pred = "getValue" and result = this.getValue()
291-
}
272+
final Expr getValue() { result = PairImpl.super.getValue() }
292273
}
293274

294275
/**

Diff for: ‎ruby/ql/lib/codeql/ruby/ast/Literal.qll

+3-1
Original file line numberDiff line numberDiff line change
@@ -305,7 +305,9 @@ class StringlikeLiteral extends Literal instanceof StringlikeLiteralImpl {
305305
final override AstNode getAChild(string pred) {
306306
result = Literal.super.getAChild(pred)
307307
or
308-
pred = "getComponent" and result = this.getComponent(_)
308+
pred = "getComponent" and
309+
result = this.getComponent(_) and
310+
not this instanceof SimpleSymbolLiteralSynth
309311
}
310312
}
311313

Diff for: ‎ruby/ql/lib/codeql/ruby/ast/Operation.qll

-4
Original file line numberDiff line numberDiff line change
@@ -92,10 +92,6 @@ class SplatExpr extends UnaryOperation, TSplatExpr {
9292
* ```
9393
*/
9494
class HashSplatExpr extends UnaryOperation, THashSplatExpr {
95-
private Ruby::HashSplatArgument g;
96-
97-
HashSplatExpr() { this = THashSplatExpr(g) }
98-
9995
final override string getAPrimaryQlClass() { result = "HashSplatExpr" }
10096
}
10197

Diff for: ‎ruby/ql/lib/codeql/ruby/ast/internal/AST.qll

+30-11
Original file line numberDiff line numberDiff line change
@@ -164,7 +164,8 @@ private module Cached {
164164
THashKeySymbolLiteral(Ruby::HashKeySymbol g) or
165165
THashLiteral(Ruby::Hash g) or
166166
THashPattern(Ruby::HashPattern g) or
167-
THashSplatExpr(Ruby::HashSplatArgument g) or
167+
THashSplatExprReal(Ruby::HashSplatArgument g) or
168+
THashSplatExprSynth(Ast::AstNode parent, int i) { mkSynthChild(HashSplatExprKind(), parent, i) } or
168169
THashSplatNilParameter(Ruby::HashSplatNil g) { not g.getParent() instanceof Ruby::HashPattern } or
169170
THashSplatParameter(Ruby::HashSplatParameter g) {
170171
not g.getParent() instanceof Ruby::HashPattern
@@ -232,7 +233,8 @@ private module Cached {
232233
TNotExprReal(Ruby::Unary g) { g instanceof @ruby_unary_bang or g instanceof @ruby_unary_not } or
233234
TNotExprSynth(Ast::AstNode parent, int i) { mkSynthChild(NotExprKind(), parent, i) } or
234235
TOptionalParameter(Ruby::OptionalParameter g) or
235-
TPair(Ruby::Pair g) or
236+
TPairReal(Ruby::Pair g) or
237+
TPairSynth(Ast::AstNode parent, int i) { mkSynthChild(PairExprKind(), parent, i) } or
236238
TParenthesizedExpr(Ruby::ParenthesizedStatements g) or
237239
TParenthesizedPattern(Ruby::ParenthesizedPattern g) or
238240
TRShiftExprReal(Ruby::Binary g) { g instanceof @ruby_binary_ranglerangle } or
@@ -274,7 +276,10 @@ private module Cached {
274276
TSimpleParameterSynth(Ast::AstNode parent, int i) {
275277
mkSynthChild(SimpleParameterKind(), parent, i)
276278
} or
277-
TSimpleSymbolLiteral(Ruby::SimpleSymbol g) or
279+
TSimpleSymbolLiteralReal(Ruby::SimpleSymbol g) or
280+
TSimpleSymbolLiteralSynth(Ast::AstNode parent, int i, string value) {
281+
mkSynthChild(SymbolLiteralExprKind(value), parent, i)
282+
} or
278283
TSingletonClass(Ruby::SingletonClass g) or
279284
TSingletonMethod(Ruby::SingletonMethod g) or
280285
TSpaceshipExpr(Ruby::Binary g) { g instanceof @ruby_binary_langleequalrangle } or
@@ -362,19 +367,19 @@ private module Cached {
362367
TEnsure or TEqExpr or TExponentExprReal or TFalseLiteral or TFile or TFindPattern or
363368
TFloatLiteral or TForExpr or TForwardParameter or TForwardArgument or TGEExpr or TGTExpr or
364369
TGlobalVariableAccessReal or THashKeySymbolLiteral or THashLiteral or THashPattern or
365-
THashSplatExpr or THashSplatNilParameter or THashSplatParameter or THereDoc or
370+
THashSplatExprReal or THashSplatNilParameter or THashSplatParameter or THereDoc or
366371
TIdentifierMethodCall or TIfReal or TIfModifierExpr or TInClauseReal or
367372
TInstanceVariableAccessReal or TIntegerLiteralReal or TKeywordParameter or TLEExpr or
368373
TLShiftExprReal or TLTExpr or TLambda or TLeftAssignmentList or TLine or
369374
TLocalVariableAccessReal or TLogicalAndExprReal or TLogicalOrExprReal or TMethod or
370375
TMatchPattern or TModuleDeclaration or TModuloExprReal or TMulExprReal or TNEExpr or
371376
TNextStmt or TNilLiteralReal or TNoRegExpMatchExpr or TNotExprReal or TOptionalParameter or
372-
TPair or TParenthesizedExpr or TParenthesizedPattern or TRShiftExprReal or
377+
TPairReal or TParenthesizedExpr or TParenthesizedPattern or TRShiftExprReal or
373378
TRangeLiteralReal or TRationalLiteral or TRedoStmt or TRegExpLiteral or TRegExpMatchExpr or
374379
TRegularArrayLiteral or TRegularMethodCall or TRegularStringLiteral or TRegularSuperCall or
375380
TRescueClause or TRescueModifierExpr or TRetryStmt or TReturnStmt or
376381
TScopeResolutionConstantAccess or TSelfReal or TSimpleParameterReal or
377-
TSimpleSymbolLiteral or TSingletonClass or TSingletonMethod or TSpaceshipExpr or
382+
TSimpleSymbolLiteralReal or TSingletonClass or TSingletonMethod or TSpaceshipExpr or
378383
TSplatExprReal or TSplatParameter or TStringArrayLiteral or TStringConcatenation or
379384
TStringEscapeSequenceComponent or TStringInterpolationComponent or TStringTextComponent or
380385
TSubExprReal or TSubshellLiteral or TSymbolArrayLiteral or TTernaryIfExpr or TTestPattern or
@@ -392,7 +397,8 @@ private module Cached {
392397
TLShiftExprSynth or TLocalVariableAccessSynth or TLogicalAndExprSynth or
393398
TLogicalOrExprSynth or TMethodCallSynth or TModuloExprSynth or TMulExprSynth or
394399
TNilLiteralSynth or TRShiftExprSynth or TRangeLiteralSynth or TSelfSynth or
395-
TSimpleParameterSynth or TSplatExprSynth or TStmtSequenceSynth or TSubExprSynth;
400+
TSimpleParameterSynth or TSplatExprSynth or THashSplatExprSynth or TStmtSequenceSynth or
401+
TSubExprSynth or TPairSynth or TSimpleSymbolLiteralSynth;
396402

397403
/**
398404
* Gets the underlying TreeSitter entity for a given AST node. This does not
@@ -468,7 +474,7 @@ private module Cached {
468474
n = THashKeySymbolLiteral(result) or
469475
n = THashLiteral(result) or
470476
n = THashPattern(result) or
471-
n = THashSplatExpr(result) or
477+
n = THashSplatExprReal(result) or
472478
n = THashSplatNilParameter(result) or
473479
n = THashSplatParameter(result) or
474480
n = THereDoc(result) or
@@ -499,7 +505,7 @@ private module Cached {
499505
n = TNoRegExpMatchExpr(result) or
500506
n = TNotExprReal(result) or
501507
n = TOptionalParameter(result) or
502-
n = TPair(result) or
508+
n = TPairReal(result) or
503509
n = TParenthesizedExpr(result) or
504510
n = TParenthesizedPattern(result) or
505511
n = TRangeLiteralReal(result) or
@@ -519,7 +525,7 @@ private module Cached {
519525
n = TScopeResolutionConstantAccess(result, _) or
520526
n = TSelfReal(result) or
521527
n = TSimpleParameterReal(result) or
522-
n = TSimpleSymbolLiteral(result) or
528+
n = TSimpleSymbolLiteralReal(result) or
523529
n = TSingletonClass(result) or
524530
n = TSingletonMethod(result) or
525531
n = TSpaceshipExpr(result) or
@@ -633,9 +639,15 @@ private module Cached {
633639
or
634640
result = TSplatExprSynth(parent, i)
635641
or
642+
result = THashSplatExprSynth(parent, i)
643+
or
636644
result = TStmtSequenceSynth(parent, i)
637645
or
638646
result = TSubExprSynth(parent, i)
647+
or
648+
result = TPairSynth(parent, i)
649+
or
650+
result = TSimpleSymbolLiteralSynth(parent, i, _)
639651
}
640652

641653
/**
@@ -726,6 +738,8 @@ class TSelf = TSelfReal or TSelfSynth;
726738

727739
class TDestructuredLhsExpr = TDestructuredLeftAssignment or TLeftAssignmentList;
728740

741+
class TPair = TPairReal or TPairSynth;
742+
729743
class TExpr =
730744
TSelf or TArgumentList or TRescueClause or TRescueModifierExpr or TPair or TStringConcatenation or
731745
TCall or TBlockArgument or TConstantAccess or TControlExpr or TLiteral or TCallable or
@@ -734,6 +748,8 @@ class TExpr =
734748

735749
class TSplatExpr = TSplatExprReal or TSplatExprSynth;
736750

751+
class THashSplatExpr = THashSplatExprReal or THashSplatExprSynth;
752+
737753
class TElse = TElseReal or TElseSynth;
738754

739755
class TStmtSequence =
@@ -768,13 +784,16 @@ class TStringInterpolationComponent =
768784
TStringInterpolationComponentNonRegexp or TStringInterpolationComponentRegexp;
769785

770786
class TStringComponent =
771-
TStringTextComponent or TStringEscapeSequenceComponent or TStringInterpolationComponent;
787+
TStringTextComponent or TStringEscapeSequenceComponent or TStringInterpolationComponent or
788+
TSimpleSymbolLiteralSynth;
772789

773790
class TStringlikeLiteral =
774791
TStringLiteral or TRegExpLiteral or TSymbolLiteral or TSubshellLiteral or THereDoc;
775792

776793
class TStringLiteral = TRegularStringLiteral or TBareStringLiteral;
777794

795+
class TSimpleSymbolLiteral = TSimpleSymbolLiteralReal or TSimpleSymbolLiteralSynth;
796+
778797
class TSymbolLiteral = TSimpleSymbolLiteral or TComplexSymbolLiteral or THashKeySymbolLiteral;
779798

780799
class TComplexSymbolLiteral = TDelimitedSymbolLiteral or TBareSymbolLiteral;

Diff for: ‎ruby/ql/lib/codeql/ruby/ast/internal/Call.qll

+46-2
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ private import TreeSitter
22
private import Variable
33
private import codeql.ruby.AST
44
private import codeql.ruby.ast.internal.AST
5+
private import codeql.ruby.ast.internal.Scope
56

67
predicate isIdentifierMethodCall(Ruby::Identifier g) { vcall(g) and not access(g, _) }
78

@@ -133,18 +134,61 @@ private string getSuperMethodName(Ruby::Super sup) {
133134
)
134135
}
135136

137+
private Ruby::Identifier getParameter(Ruby::Method m, int pos, Ruby::AstNode param) {
138+
scopeDefinesParameterVariable(m, _, result, pos) and
139+
param = m.getParameters().getChild(pos)
140+
}
141+
136142
class TokenSuperCall extends SuperCallImpl, TTokenSuperCall {
137143
private Ruby::Super g;
138144

139145
TokenSuperCall() { this = TTokenSuperCall(g) }
140146

147+
Ruby::Method getEnclosingMethod() { result = scopeOf(toGenerated(this)).getEnclosingMethod() }
148+
149+
int getNumberOfImplicitArguments() {
150+
exists(Ruby::Method encl |
151+
encl = this.getEnclosingMethod() and
152+
result = count(getParameter(encl, _, _))
153+
)
154+
}
155+
156+
/**
157+
* Gets the local variable defined by parameter `param` which is used as an
158+
* implicit argument at position `pos`.
159+
*
160+
* For example, in
161+
*
162+
* ```ruby
163+
* class Sup
164+
* def m(x)
165+
* end
166+
* end
167+
*
168+
* class Sub < Sup
169+
* def m(x)
170+
* super
171+
* end
172+
* end
173+
* ```
174+
*
175+
* `x` is an implicit argument at position 0 of the `super` call in `Sub#m`.
176+
*/
177+
pragma[nomagic]
178+
LocalVariableReal getImplicitArgument(int pos, Ruby::AstNode param) {
179+
exists(Ruby::Method encl |
180+
encl = this.getEnclosingMethod() and
181+
toGenerated(result.getDefiningAccessImpl()) = getParameter(encl, pos, param)
182+
)
183+
}
184+
141185
final override string getMethodNameImpl() { result = getSuperMethodName(g) }
142186

143187
final override Expr getReceiverImpl() { none() }
144188

145-
final override Expr getArgumentImpl(int n) { none() }
189+
final override Expr getArgumentImpl(int n) { synthChild(this, n, result) }
146190

147-
final override int getNumberOfArgumentsImpl() { result = 0 }
191+
final override int getNumberOfArgumentsImpl() { result = this.getNumberOfImplicitArguments() }
148192

149193
final override Block getBlockImpl() { none() }
150194
}

Diff for: ‎ruby/ql/lib/codeql/ruby/ast/internal/Expr.qll

+37
Original file line numberDiff line numberDiff line change
@@ -120,3 +120,40 @@ class LeftAssignmentListImpl extends DestructuredLhsExprImpl, Ruby::LeftAssignme
120120
)
121121
}
122122
}
123+
124+
abstract class PairImpl extends Expr, TPair {
125+
final override string getAPrimaryQlClass() { result = "Pair" }
126+
127+
abstract Expr getKey();
128+
129+
abstract Expr getValue();
130+
131+
final override string toString() { result = "Pair" }
132+
133+
final override AstNode getAChild(string pred) {
134+
result = super.getAChild(pred)
135+
or
136+
pred = "getKey" and result = this.getKey()
137+
or
138+
pred = "getValue" and result = this.getValue()
139+
}
140+
}
141+
142+
class PairReal extends PairImpl, TPairReal {
143+
private Ruby::Pair g;
144+
145+
PairReal() { this = TPairReal(g) }
146+
147+
final override Expr getKey() { toGenerated(result) = g.getKey() }
148+
149+
final override Expr getValue() {
150+
toGenerated(result) = g.getValue() or
151+
synthChild(this, 0, result)
152+
}
153+
}
154+
155+
class PairSynth extends PairImpl, TPairSynth {
156+
final override Expr getKey() { synthChild(this, 0, result) }
157+
158+
final override Expr getValue() { synthChild(this, 1, result) }
159+
}

Diff for: ‎ruby/ql/lib/codeql/ruby/ast/internal/Literal.qll

+18-2
Original file line numberDiff line numberDiff line change
@@ -608,16 +608,32 @@ class BareStringLiteral extends StringLiteralImpl, TBareStringLiteral {
608608

609609
abstract class SymbolLiteralImpl extends StringlikeLiteralImpl, TSymbolLiteral { }
610610

611-
class SimpleSymbolLiteral extends SymbolLiteralImpl, TSimpleSymbolLiteral {
611+
abstract class SimpleSymbolLiteralImpl extends SymbolLiteralImpl, TSimpleSymbolLiteral { }
612+
613+
class SimpleSymbolLiteralReal extends SimpleSymbolLiteralImpl, TSimpleSymbolLiteral {
612614
private Ruby::SimpleSymbol g;
613615

614-
SimpleSymbolLiteral() { this = TSimpleSymbolLiteral(g) }
616+
SimpleSymbolLiteralReal() { this = TSimpleSymbolLiteralReal(g) }
615617

616618
final override StringComponent getComponentImpl(int n) { n = 0 and toGenerated(result) = g }
617619

618620
final override string toString() { result = g.getValue() }
619621
}
620622

623+
class SimpleSymbolLiteralSynth extends SimpleSymbolLiteralImpl, TSimpleSymbolLiteralSynth,
624+
StringComponentImpl
625+
{
626+
private string value;
627+
628+
SimpleSymbolLiteralSynth() { this = TSimpleSymbolLiteralSynth(_, _, value) }
629+
630+
final override string getValue() { result = value }
631+
632+
final override StringComponent getComponentImpl(int n) { n = 0 and result = this }
633+
634+
final override string toString() { result = value }
635+
}
636+
621637
abstract class ComplexSymbolLiteral extends SymbolLiteralImpl, TComplexSymbolLiteral { }
622638

623639
class DelimitedSymbolLiteral extends ComplexSymbolLiteral, TDelimitedSymbolLiteral {

Diff for: ‎ruby/ql/lib/codeql/ruby/ast/internal/Operation.qll

+15-9
Original file line numberDiff line numberDiff line change
@@ -45,36 +45,42 @@ class NotExprSynth extends NotExprImpl, TNotExprSynth {
4545
final override Expr getOperandImpl() { synthChild(this, 0, result) }
4646
}
4747

48-
class SplatExprReal extends UnaryOperationImpl, TSplatExprReal {
48+
abstract class SplatExprImpl extends UnaryOperationImpl, TSplatExpr {
49+
final override string getOperatorImpl() { result = "*" }
50+
}
51+
52+
class SplatExprReal extends SplatExprImpl, TSplatExprReal {
4953
private Ruby::SplatArgument g;
5054

5155
SplatExprReal() { this = TSplatExprReal(g) }
5256

53-
final override string getOperatorImpl() { result = "*" }
54-
5557
final override Expr getOperandImpl() {
5658
toGenerated(result) = g.getChild() or
5759
synthChild(this, 0, result)
5860
}
5961
}
6062

61-
class SplatExprSynth extends UnaryOperationImpl, TSplatExprSynth {
62-
final override string getOperatorImpl() { result = "*" }
63-
63+
class SplatExprSynth extends SplatExprImpl, TSplatExprSynth {
6464
final override Expr getOperandImpl() { synthChild(this, 0, result) }
6565
}
6666

67-
class HashSplatExprImpl extends UnaryOperationImpl, THashSplatExpr {
67+
abstract class HashSplatExprImpl extends UnaryOperationImpl, THashSplatExpr {
68+
final override string getOperatorImpl() { result = "**" }
69+
}
70+
71+
class HashSplatExprReal extends HashSplatExprImpl, THashSplatExprReal {
6872
private Ruby::HashSplatArgument g;
6973

70-
HashSplatExprImpl() { this = THashSplatExpr(g) }
74+
HashSplatExprReal() { this = THashSplatExprReal(g) }
7175

7276
final override Expr getOperandImpl() {
7377
toGenerated(result) = g.getChild() or
7478
synthChild(this, 0, result)
7579
}
80+
}
7681

77-
final override string getOperatorImpl() { result = "**" }
82+
class HashSplatExprSynth extends HashSplatExprImpl, THashSplatExprSynth {
83+
final override Expr getOperandImpl() { synthChild(this, 0, result) }
7884
}
7985

8086
abstract class DefinedExprImpl extends UnaryOperationImpl, TDefinedExpr { }

Diff for: ‎ruby/ql/lib/codeql/ruby/ast/internal/Synthesis.qll

+158-2
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ private import codeql.ruby.ast.internal.Scope
1111
private import codeql.ruby.AST
1212

1313
/** A synthesized AST node kind. */
14-
newtype SynthKind =
14+
newtype TSynthKind =
1515
AddExprKind() or
1616
AssignExprKind() or
1717
BitwiseAndExprKind() or
@@ -42,16 +42,107 @@ newtype SynthKind =
4242
MulExprKind() or
4343
NilLiteralKind() or
4444
NotExprKind() or
45+
PairExprKind() or
4546
RangeLiteralKind(boolean inclusive) { inclusive in [false, true] } or
4647
RShiftExprKind() or
4748
SimpleParameterKind() or
4849
SplatExprKind() or
50+
HashSplatExprKind() or
51+
SymbolLiteralExprKind(string value) {
52+
value = any(Ruby::SimpleSymbol s).getValue()
53+
or
54+
value = any(Ruby::KeywordParameter p).getName().getValue()
55+
} or
4956
StmtSequenceKind() or
5057
SelfKind(SelfVariable v) or
5158
SubExprKind() or
5259
ConstantReadAccessKind(string value) { any(Synthesis s).constantReadAccess(value) } or
5360
ConstantWriteAccessKind(string value) { any(Synthesis s).constantWriteAccess(value) }
5461

62+
class SynthKind extends TSynthKind {
63+
string toString() {
64+
this = AddExprKind() and result = "AddExprKind"
65+
or
66+
this = AssignExprKind() and result = "AssignExprKind"
67+
or
68+
this = BitwiseAndExprKind() and result = "BitwiseAndExprKind"
69+
or
70+
this = BitwiseOrExprKind() and result = "BitwiseOrExprKind"
71+
or
72+
this = BitwiseXorExprKind() and result = "BitwiseXorExprKind"
73+
or
74+
this = BooleanLiteralKind(_) and result = "BooleanLiteralKind"
75+
or
76+
this = BraceBlockKind() and result = "BraceBlockKind"
77+
or
78+
this = CaseMatchKind() and result = "CaseMatchKind"
79+
or
80+
this = ClassVariableAccessKind(_) and result = "ClassVariableAccessKind"
81+
or
82+
this = DefinedExprKind() and result = "DefinedExprKind"
83+
or
84+
this = DivExprKind() and result = "DivExprKind"
85+
or
86+
this = ElseKind() and result = "ElseKind"
87+
or
88+
this = ExponentExprKind() and result = "ExponentExprKind"
89+
or
90+
this = GlobalVariableAccessKind(_) and result = "GlobalVariableAccessKind"
91+
or
92+
this = IfKind() and result = "IfKind"
93+
or
94+
this = InClauseKind() and result = "InClauseKind"
95+
or
96+
this = InstanceVariableAccessKind(_) and result = "InstanceVariableAccessKind"
97+
or
98+
this = IntegerLiteralKind(_) and result = "IntegerLiteralKind"
99+
or
100+
this = LShiftExprKind() and result = "LShiftExprKind"
101+
or
102+
this = LocalVariableAccessRealKind(_) and result = "LocalVariableAccessRealKind"
103+
or
104+
this = LocalVariableAccessSynthKind(_) and result = "LocalVariableAccessSynthKind"
105+
or
106+
this = LogicalAndExprKind() and result = "LogicalAndExprKind"
107+
or
108+
this = LogicalOrExprKind() and result = "LogicalOrExprKind"
109+
or
110+
this = MethodCallKind(_, _, _) and result = "MethodCallKind"
111+
or
112+
this = ModuloExprKind() and result = "ModuloExprKind"
113+
or
114+
this = MulExprKind() and result = "MulExprKind"
115+
or
116+
this = NilLiteralKind() and result = "NilLiteralKind"
117+
or
118+
this = NotExprKind() and result = "NotExprKind"
119+
or
120+
this = PairExprKind() and result = "PairExprKind"
121+
or
122+
this = RangeLiteralKind(_) and result = "RangeLiteralKind"
123+
or
124+
this = RShiftExprKind() and result = "RShiftExprKind"
125+
or
126+
this = SimpleParameterKind() and result = "SimpleParameterKind"
127+
or
128+
this = SplatExprKind() and result = "SplatExprKind"
129+
or
130+
this = HashSplatExprKind() and result = "HashSplatExprKind"
131+
or
132+
this = SymbolLiteralExprKind(_) and result = "SymbolLiteralExprKind"
133+
or
134+
this = StmtSequenceKind() and result = "StmtSequenceKind"
135+
or
136+
this = SubExprKind() and result = "SubExprKind"
137+
or
138+
this = SelfKind(_) and result = "SelfKind"
139+
or
140+
this = ConstantReadAccessKind(_) and result = "ConstantReadAccessKind"
141+
or
142+
this = ConstantWriteAccessKind(_) and result = "ConstantWriteAccessKind"
143+
}
144+
}
145+
55146
/**
56147
* An AST child.
57148
*
@@ -110,6 +201,11 @@ class Synthesis extends TSynthesis {
110201
*/
111202
predicate methodCall(string name, boolean setter, int arity) { none() }
112203

204+
/**
205+
* Holds if a `super` call with arity `arity` is needed.
206+
*/
207+
predicate superCall(int arity) { none() }
208+
113209
/**
114210
* Holds if a constant read access of `name` is needed.
115211
*/
@@ -441,7 +537,7 @@ private module AssignOperationDesugar {
441537
pragma[nomagic]
442538
SynthKind getVariableAccessKind() {
443539
result in [
444-
LocalVariableAccessRealKind(v).(SynthKind), InstanceVariableAccessKind(v),
540+
LocalVariableAccessRealKind(v).(TSynthKind), InstanceVariableAccessKind(v),
445541
ClassVariableAccessKind(v), GlobalVariableAccessKind(v)
446542
]
447543
}
@@ -1802,3 +1898,63 @@ private module MatchPatternDesugar {
18021898
}
18031899
}
18041900
}
1901+
1902+
private module ImplicitSuperArgsSynthesis {
1903+
pragma[nomagic]
1904+
private predicate superCallSynthesis(AstNode parent, int i, Child child) {
1905+
exists(TokenSuperCall call, SynthChild access, int pos, Ruby::AstNode param |
1906+
access = SynthChild(LocalVariableAccessRealKind(call.getImplicitArgument(pos, param)))
1907+
|
1908+
parent = call and
1909+
param instanceof Ruby::Identifier and
1910+
i = pos and
1911+
child = access
1912+
or
1913+
param instanceof Ruby::SplatParameter and
1914+
(
1915+
parent = call and
1916+
i = pos and
1917+
child = SynthChild(SplatExprKind())
1918+
or
1919+
parent = TSplatExprSynth(call, pos) and
1920+
i = 0 and
1921+
child = access
1922+
)
1923+
or
1924+
param instanceof Ruby::HashSplatParameter and
1925+
(
1926+
parent = call and
1927+
i = pos and
1928+
child = SynthChild(HashSplatExprKind())
1929+
or
1930+
parent = THashSplatExprSynth(call, pos) and
1931+
i = 0 and
1932+
child = access
1933+
)
1934+
or
1935+
exists(string name | name = param.(Ruby::KeywordParameter).getName().getValue() |
1936+
parent = call and
1937+
i = pos and
1938+
child = SynthChild(PairExprKind())
1939+
or
1940+
parent = TPairSynth(call, pos) and
1941+
i = 0 and
1942+
child = SynthChild(SymbolLiteralExprKind(name))
1943+
or
1944+
parent = TPairSynth(call, pos) and
1945+
i = 1 and
1946+
child = access
1947+
)
1948+
)
1949+
}
1950+
1951+
private class SuperCallSynthesis extends Synthesis {
1952+
final override predicate child(AstNode parent, int i, Child child) {
1953+
superCallSynthesis(parent, i, child)
1954+
}
1955+
1956+
final override predicate superCall(int arity) {
1957+
arity = any(TokenSuperCall call).getNumberOfImplicitArguments()
1958+
}
1959+
}
1960+
}

Diff for: ‎ruby/ql/lib/codeql/ruby/ast/internal/Variable.qll

+22-19
Original file line numberDiff line numberDiff line change
@@ -49,11 +49,11 @@ predicate implicitAssignmentNode(Ruby::AstNode n) {
4949
}
5050

5151
/** Holds if `n` is inside a parameter. */
52-
predicate implicitParameterAssignmentNode(Ruby::AstNode n, Callable::Range c) {
53-
n = c.getParameter(_) or
54-
n = c.(Ruby::Block).getParameters().getLocals(_) or
55-
n = c.(Ruby::DoBlock).getParameters().getLocals(_) or
56-
implicitParameterAssignmentNode(n.getParent().(Ruby::DestructuredParameter), c)
52+
predicate implicitParameterAssignmentNode(Ruby::AstNode n, Callable::Range c, int pos) {
53+
n = c.getParameter(pos) or
54+
n = c.(Ruby::Block).getParameters().getLocals(pos) or
55+
n = c.(Ruby::DoBlock).getParameters().getLocals(pos) or
56+
implicitParameterAssignmentNode(n.getParent().(Ruby::DestructuredParameter), c, pos)
5757
}
5858

5959
private predicate instanceVariableAccess(
@@ -77,26 +77,29 @@ private ModuleBase::Range enclosingModuleOrClass(Ruby::AstNode node) {
7777
exists(Scope::Range s | scopeOf(node) = s and result = s.getEnclosingModule())
7878
}
7979

80-
private predicate parameterAssignment(Callable::Range scope, string name, Ruby::Identifier i) {
81-
implicitParameterAssignmentNode(i, scope) and
80+
private predicate parameterAssignment(
81+
Callable::Range scope, string name, Ruby::Identifier i, int pos
82+
) {
83+
implicitParameterAssignmentNode(i, scope, pos) and
8284
name = i.getValue()
8385
}
8486

8587
/** Holds if `scope` defines `name` in its parameter declaration at `i`. */
86-
private predicate scopeDefinesParameterVariable(
87-
Callable::Range scope, string name, Ruby::Identifier i
88+
predicate scopeDefinesParameterVariable(
89+
Callable::Range scope, string name, Ruby::Identifier i, int pos
8890
) {
8991
// In case of overlapping parameter names (e.g. `_`), only the first
9092
// parameter will give rise to a variable
9193
i =
9294
min(Ruby::Identifier other |
93-
parameterAssignment(scope, name, other)
95+
parameterAssignment(scope, name, other, _)
9496
|
9597
other order by other.getLocation().getStartLine(), other.getLocation().getStartColumn()
96-
)
98+
) and
99+
parameterAssignment(scope, name, _, pos)
97100
or
98101
exists(Parameter::Range p |
99-
p = scope.getParameter(_) and
102+
p = scope.getParameter(pos) and
100103
name = i.getValue()
101104
|
102105
i = p.(Ruby::BlockParameter).getName() or
@@ -153,15 +156,15 @@ private module Cached {
153156
)
154157
} or
155158
TLocalVariableReal(Scope::Range scope, string name, Ruby::AstNode i) {
156-
scopeDefinesParameterVariable(scope, name, i)
159+
scopeDefinesParameterVariable(scope, name, i, _)
157160
or
158161
i =
159162
min(Ruby::AstNode other |
160163
scopeAssigns(scope, name, other)
161164
|
162165
other order by other.getLocation().getStartLine(), other.getLocation().getStartColumn()
163166
) and
164-
not scopeDefinesParameterVariable(scope, name, _) and
167+
not scopeDefinesParameterVariable(scope, name, _, _) and
165168
not inherits(scope, name, _)
166169
} or
167170
TSelfVariable(SelfBase::Range scope) or
@@ -330,8 +333,8 @@ private module Cached {
330333
not access.getLocation().strictlyBefore(variable.getLocationImpl()) and
331334
// In case of overlapping parameter names, later parameters should not
332335
// be considered accesses to the first parameter
333-
if parameterAssignment(_, _, access)
334-
then scopeDefinesParameterVariable(_, _, access)
336+
if parameterAssignment(_, _, access, _)
337+
then scopeDefinesParameterVariable(_, _, access, _)
335338
else any()
336339
or
337340
exists(Scope::Range declScope |
@@ -360,7 +363,7 @@ private module Cached {
360363
predicate implicitWriteAccess(Access access) {
361364
implicitAssignmentNode(access)
362365
or
363-
scopeDefinesParameterVariable(_, _, access)
366+
scopeDefinesParameterVariable(_, _, access, _)
364367
}
365368

366369
cached
@@ -399,11 +402,11 @@ private predicate inherits(Scope::Range scope, string name, Scope::Range outer)
399402
scope instanceof Ruby::DoBlock or
400403
scope instanceof Ruby::Lambda
401404
) and
402-
not scopeDefinesParameterVariable(scope, name, _) and
405+
not scopeDefinesParameterVariable(scope, name, _, _) and
403406
(
404407
outer = scope.getOuterScope() and
405408
(
406-
scopeDefinesParameterVariable(outer, name, _)
409+
scopeDefinesParameterVariable(outer, name, _, _)
407410
or
408411
exists(Ruby::AstNode i |
409412
scopeAssigns(outer, name, i) and

Diff for: ‎ruby/ql/test/library-tests/ast/Ast.expected

+8
Original file line numberDiff line numberDiff line change
@@ -3185,6 +3185,14 @@ params/params.rb:
31853185
# 106| getParameter: [HashSplatParameter] **kwargs
31863186
# 106| getDefiningAccess: [LocalVariableAccess] kwargs
31873187
# 107| getStmt: [SuperCall] super call to m
3188+
# 107| getArgument: [HashSplatExpr] ** ...
3189+
# 107| getAnOperand/getOperand/getReceiver: [LocalVariableAccess] kwargs
3190+
# 107| getArgument: [Pair] Pair
3191+
# 107| getKey: [SymbolLiteral] k
3192+
# 107| getValue: [LocalVariableAccess] k
3193+
# 107| getArgument: [SplatExpr] * ...
3194+
# 107| getAnOperand/getOperand/getReceiver: [LocalVariableAccess] rest
3195+
# 107| getArgument: [LocalVariableAccess] y
31883196
# 111| getStmt: [MethodCall] call to m
31893197
# 111| getReceiver: [MethodCall] call to new
31903198
# 111| getReceiver: [ConstantReadAccess] Sub

Diff for: ‎ruby/ql/test/library-tests/ast/ValueText.expected

+2
Original file line numberDiff line numberDiff line change
@@ -943,6 +943,7 @@ exprValue
943943
| params/params.rb:70:52:70:53 | 20 | 20 | int |
944944
| params/params.rb:100:15:100:15 | 1 | 1 | int |
945945
| params/params.rb:101:15:101:15 | 1 | 1 | int |
946+
| params/params.rb:107:5:107:9 | k | :k | symbol |
946947
| params/params.rb:111:11:111:12 | 42 | 42 | int |
947948
| params/params.rb:111:15:111:15 | :k | :k | symbol |
948949
| params/params.rb:111:18:111:19 | 22 | 22 | int |
@@ -1862,6 +1863,7 @@ exprCfgNodeValue
18621863
| params/params.rb:70:52:70:53 | 20 | 20 | int |
18631864
| params/params.rb:100:15:100:15 | 1 | 1 | int |
18641865
| params/params.rb:101:15:101:15 | 1 | 1 | int |
1866+
| params/params.rb:107:5:107:9 | k | :k | symbol |
18651867
| params/params.rb:111:11:111:12 | 42 | 42 | int |
18661868
| params/params.rb:111:15:111:15 | :k | :k | symbol |
18671869
| params/params.rb:111:18:111:19 | 22 | 22 | int |

Diff for: ‎ruby/ql/test/library-tests/dataflow/params/TypeTracker.expected

+244
Large diffs are not rendered by default.

Diff for: ‎ruby/ql/test/library-tests/dataflow/params/params-flow.expected

+62
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,32 @@ edges
174174
| params_flow.rb:192:24:192:32 | call to taint | params_flow.rb:181:28:181:29 | p2 | provenance | |
175175
| params_flow.rb:192:24:192:32 | call to taint | params_flow.rb:192:20:192:21 | [post] p1 : [collection] [element 0] | provenance | |
176176
| params_flow.rb:193:6:193:7 | p1 : [collection] [element 0] | params_flow.rb:193:6:193:10 | ...[...] | provenance | |
177+
| params_flow.rb:210:11:210:11 | x | params_flow.rb:211:14:211:14 | x | provenance | |
178+
| params_flow.rb:210:14:210:18 | *rest : [collection] [element 0] | params_flow.rb:212:14:212:17 | rest : [collection] [element 0] | provenance | |
179+
| params_flow.rb:210:14:210:18 | *rest : [collection] [element 2] | params_flow.rb:214:14:214:17 | rest : [collection] [element 2] | provenance | |
180+
| params_flow.rb:210:21:210:22 | k1 | params_flow.rb:215:14:215:15 | k1 | provenance | |
181+
| params_flow.rb:210:26:210:33 | **kwargs : Hash [element :k2] | params_flow.rb:216:14:216:19 | kwargs : Hash [element :k2] | provenance | |
182+
| params_flow.rb:212:14:212:17 | rest : [collection] [element 0] | params_flow.rb:212:14:212:20 | ...[...] | provenance | |
183+
| params_flow.rb:214:14:214:17 | rest : [collection] [element 2] | params_flow.rb:214:14:214:20 | ...[...] | provenance | |
184+
| params_flow.rb:216:14:216:19 | kwargs : Hash [element :k2] | params_flow.rb:216:14:216:24 | ...[...] | provenance | |
185+
| params_flow.rb:221:11:221:11 | x | params_flow.rb:222:9:222:13 | x | provenance | |
186+
| params_flow.rb:221:14:221:18 | *rest : [collection] [element 0] | params_flow.rb:222:9:222:13 | rest : [collection] [element 0] | provenance | |
187+
| params_flow.rb:221:14:221:18 | *rest : [collection] [element 2] | params_flow.rb:222:9:222:13 | rest : [collection] [element 2] | provenance | |
188+
| params_flow.rb:221:21:221:22 | k1 | params_flow.rb:222:9:222:13 | k1 | provenance | |
189+
| params_flow.rb:221:26:221:33 | **kwargs : Hash [element :k2] | params_flow.rb:222:9:222:13 | kwargs : Hash [element :k2] | provenance | |
190+
| params_flow.rb:222:9:222:13 | * ... : [collection] [element 0] | params_flow.rb:210:14:210:18 | *rest : [collection] [element 0] | provenance | |
191+
| params_flow.rb:222:9:222:13 | * ... : [collection] [element 2] | params_flow.rb:210:14:210:18 | *rest : [collection] [element 2] | provenance | |
192+
| params_flow.rb:222:9:222:13 | ** ... : Hash [element :k2] | params_flow.rb:210:26:210:33 | **kwargs : Hash [element :k2] | provenance | |
193+
| params_flow.rb:222:9:222:13 | k1 | params_flow.rb:210:21:210:22 | k1 | provenance | |
194+
| params_flow.rb:222:9:222:13 | kwargs : Hash [element :k2] | params_flow.rb:222:9:222:13 | ** ... : Hash [element :k2] | provenance | |
195+
| params_flow.rb:222:9:222:13 | rest : [collection] [element 0] | params_flow.rb:222:9:222:13 | * ... : [collection] [element 0] | provenance | |
196+
| params_flow.rb:222:9:222:13 | rest : [collection] [element 2] | params_flow.rb:222:9:222:13 | * ... : [collection] [element 2] | provenance | |
197+
| params_flow.rb:222:9:222:13 | x | params_flow.rb:210:11:210:11 | x | provenance | |
198+
| params_flow.rb:226:11:226:19 | call to taint | params_flow.rb:221:11:221:11 | x | provenance | |
199+
| params_flow.rb:226:22:226:30 | call to taint | params_flow.rb:221:14:221:18 | *rest : [collection] [element 0] | provenance | |
200+
| params_flow.rb:226:36:226:44 | call to taint | params_flow.rb:221:14:221:18 | *rest : [collection] [element 2] | provenance | |
201+
| params_flow.rb:226:51:226:59 | call to taint | params_flow.rb:221:21:221:22 | k1 | provenance | |
202+
| params_flow.rb:226:66:226:74 | call to taint | params_flow.rb:221:26:221:33 | **kwargs : Hash [element :k2] | provenance | |
177203
nodes
178204
| params_flow.rb:9:16:9:17 | p1 | semmle.label | p1 |
179205
| params_flow.rb:9:20:9:21 | p2 | semmle.label | p2 |
@@ -373,6 +399,37 @@ nodes
373399
| params_flow.rb:192:24:192:32 | call to taint | semmle.label | call to taint |
374400
| params_flow.rb:193:6:193:7 | p1 : [collection] [element 0] | semmle.label | p1 : [collection] [element 0] |
375401
| params_flow.rb:193:6:193:10 | ...[...] | semmle.label | ...[...] |
402+
| params_flow.rb:210:11:210:11 | x | semmle.label | x |
403+
| params_flow.rb:210:14:210:18 | *rest : [collection] [element 0] | semmle.label | *rest : [collection] [element 0] |
404+
| params_flow.rb:210:14:210:18 | *rest : [collection] [element 2] | semmle.label | *rest : [collection] [element 2] |
405+
| params_flow.rb:210:21:210:22 | k1 | semmle.label | k1 |
406+
| params_flow.rb:210:26:210:33 | **kwargs : Hash [element :k2] | semmle.label | **kwargs : Hash [element :k2] |
407+
| params_flow.rb:211:14:211:14 | x | semmle.label | x |
408+
| params_flow.rb:212:14:212:17 | rest : [collection] [element 0] | semmle.label | rest : [collection] [element 0] |
409+
| params_flow.rb:212:14:212:20 | ...[...] | semmle.label | ...[...] |
410+
| params_flow.rb:214:14:214:17 | rest : [collection] [element 2] | semmle.label | rest : [collection] [element 2] |
411+
| params_flow.rb:214:14:214:20 | ...[...] | semmle.label | ...[...] |
412+
| params_flow.rb:215:14:215:15 | k1 | semmle.label | k1 |
413+
| params_flow.rb:216:14:216:19 | kwargs : Hash [element :k2] | semmle.label | kwargs : Hash [element :k2] |
414+
| params_flow.rb:216:14:216:24 | ...[...] | semmle.label | ...[...] |
415+
| params_flow.rb:221:11:221:11 | x | semmle.label | x |
416+
| params_flow.rb:221:14:221:18 | *rest : [collection] [element 0] | semmle.label | *rest : [collection] [element 0] |
417+
| params_flow.rb:221:14:221:18 | *rest : [collection] [element 2] | semmle.label | *rest : [collection] [element 2] |
418+
| params_flow.rb:221:21:221:22 | k1 | semmle.label | k1 |
419+
| params_flow.rb:221:26:221:33 | **kwargs : Hash [element :k2] | semmle.label | **kwargs : Hash [element :k2] |
420+
| params_flow.rb:222:9:222:13 | * ... : [collection] [element 0] | semmle.label | * ... : [collection] [element 0] |
421+
| params_flow.rb:222:9:222:13 | * ... : [collection] [element 2] | semmle.label | * ... : [collection] [element 2] |
422+
| params_flow.rb:222:9:222:13 | ** ... : Hash [element :k2] | semmle.label | ** ... : Hash [element :k2] |
423+
| params_flow.rb:222:9:222:13 | k1 | semmle.label | k1 |
424+
| params_flow.rb:222:9:222:13 | kwargs : Hash [element :k2] | semmle.label | kwargs : Hash [element :k2] |
425+
| params_flow.rb:222:9:222:13 | rest : [collection] [element 0] | semmle.label | rest : [collection] [element 0] |
426+
| params_flow.rb:222:9:222:13 | rest : [collection] [element 2] | semmle.label | rest : [collection] [element 2] |
427+
| params_flow.rb:222:9:222:13 | x | semmle.label | x |
428+
| params_flow.rb:226:11:226:19 | call to taint | semmle.label | call to taint |
429+
| params_flow.rb:226:22:226:30 | call to taint | semmle.label | call to taint |
430+
| params_flow.rb:226:36:226:44 | call to taint | semmle.label | call to taint |
431+
| params_flow.rb:226:51:226:59 | call to taint | semmle.label | call to taint |
432+
| params_flow.rb:226:66:226:74 | call to taint | semmle.label | call to taint |
376433
subpaths
377434
| params_flow.rb:164:31:164:39 | call to taint | params_flow.rb:153:28:153:29 | p2 | params_flow.rb:153:23:153:24 | p1 [Return] : [collection] [element 0] | params_flow.rb:164:23:164:24 | [post] p1 : [collection] [element 0] |
378435
| params_flow.rb:192:24:192:32 | call to taint | params_flow.rb:181:28:181:29 | p2 | params_flow.rb:181:24:181:25 | p1 [Return] : [collection] [element 0] | params_flow.rb:192:20:192:21 | [post] p1 : [collection] [element 0] |
@@ -430,3 +487,8 @@ testFailures
430487
| params_flow.rb:134:10:134:16 | ...[...] | params_flow.rb:137:23:137:31 | call to taint | params_flow.rb:134:10:134:16 | ...[...] | $@ | params_flow.rb:137:23:137:31 | call to taint | call to taint |
431488
| params_flow.rb:165:6:165:10 | ...[...] | params_flow.rb:164:31:164:39 | call to taint | params_flow.rb:165:6:165:10 | ...[...] | $@ | params_flow.rb:164:31:164:39 | call to taint | call to taint |
432489
| params_flow.rb:193:6:193:10 | ...[...] | params_flow.rb:192:24:192:32 | call to taint | params_flow.rb:193:6:193:10 | ...[...] | $@ | params_flow.rb:192:24:192:32 | call to taint | call to taint |
490+
| params_flow.rb:211:14:211:14 | x | params_flow.rb:226:11:226:19 | call to taint | params_flow.rb:211:14:211:14 | x | $@ | params_flow.rb:226:11:226:19 | call to taint | call to taint |
491+
| params_flow.rb:212:14:212:20 | ...[...] | params_flow.rb:226:22:226:30 | call to taint | params_flow.rb:212:14:212:20 | ...[...] | $@ | params_flow.rb:226:22:226:30 | call to taint | call to taint |
492+
| params_flow.rb:214:14:214:20 | ...[...] | params_flow.rb:226:36:226:44 | call to taint | params_flow.rb:214:14:214:20 | ...[...] | $@ | params_flow.rb:226:36:226:44 | call to taint | call to taint |
493+
| params_flow.rb:215:14:215:15 | k1 | params_flow.rb:226:51:226:59 | call to taint | params_flow.rb:215:14:215:15 | k1 | $@ | params_flow.rb:226:51:226:59 | call to taint | call to taint |
494+
| params_flow.rb:216:14:216:24 | ...[...] | params_flow.rb:226:66:226:74 | call to taint | params_flow.rb:216:14:216:24 | ...[...] | $@ | params_flow.rb:226:66:226:74 | call to taint | call to taint |

Diff for: ‎ruby/ql/test/library-tests/dataflow/params/params_flow.rb

+5-5
Original file line numberDiff line numberDiff line change
@@ -208,12 +208,12 @@ def foo(x, y)
208208

209209
class Sup
210210
def m(x, *rest, k1:, **kwargs)
211-
sink(x) # $ MISSING: hasValueFlow=81
212-
sink(rest[0]) # $ MISSING: hasValueFlow=82
211+
sink(x) # $ hasValueFlow=81
212+
sink(rest[0]) # $ hasValueFlow=82
213213
sink(rest[1])
214-
sink(rest[2]) # $ MISSING: hasValueFlow=83
215-
sink(k1) # $ MISSING: hasValueFlow=84
216-
sink(kwargs[:k2]) # $ MISSING: hasValueFlow=85
214+
sink(rest[2]) # $ hasValueFlow=83
215+
sink(k1) # $ hasValueFlow=84
216+
sink(kwargs[:k2]) # $ hasValueFlow=85
217217
end
218218
end
219219

0 commit comments

Comments
 (0)
Please sign in to comment.