@@ -8,10 +8,17 @@ private import FlowSummaryImpl as FlowSummaryImpl
8
8
private import FlowSummaryImplSpecific as FlowSummaryImplSpecific
9
9
private import codeql.ruby.dataflow.FlowSummary
10
10
private import codeql.ruby.dataflow.SSA
11
+ private import codeql.ruby.dataflow.internal.SsaImpl as SsaImpl
11
12
12
13
newtype TReturnKind =
13
14
TNormalReturnKind ( ) or
14
- TBreakReturnKind ( )
15
+ TBreakReturnKind ( ) or
16
+ TCapturedReturnKind ( LocalVariable v ) {
17
+ exists ( Ssa:: Definition def |
18
+ SsaImpl:: captureFlowOut ( _, def , _) and
19
+ v = def .getSourceVariable ( )
20
+ )
21
+ }
15
22
16
23
/**
17
24
* Gets a node that can read the value returned from `call` with return kind
@@ -43,6 +50,19 @@ class BreakReturnKind extends ReturnKind, TBreakReturnKind {
43
50
override string toString ( ) { result = "break" }
44
51
}
45
52
53
+ /**
54
+ * A value implicitly returned from a capturing callable.
55
+ */
56
+ class CapturedReturnKind extends ReturnKind , TCapturedReturnKind {
57
+ LocalVariable v ;
58
+
59
+ CapturedReturnKind ( ) { this = TCapturedReturnKind ( v ) }
60
+
61
+ LocalVariable getVariable ( ) { result = v }
62
+
63
+ override string toString ( ) { result = "captured write to " + v }
64
+ }
65
+
46
66
/** A callable defined in library code, identified by a unique string. */
47
67
abstract class LibraryCallable extends string {
48
68
bindingset [ this ]
@@ -151,6 +171,28 @@ private class NormalCall extends DataFlowCall, TNormalCall {
151
171
override Location getLocation ( ) { result = c .getLocation ( ) }
152
172
}
153
173
174
+ class CapturedCall extends DataFlowCall , TCapturedCall {
175
+ private CfgNodes:: ExprNodes:: CallCfgNode c ;
176
+
177
+ CapturedCall ( ) { this = TCapturedCall ( c ) }
178
+
179
+ Callable getTarget ( ) {
180
+ exists ( Ssa:: Definition def | result = def .getScope ( ) |
181
+ SsaImpl:: captureFlowIn ( c , _, _, _, def )
182
+ or
183
+ SsaImpl:: captureFlowOut ( c , def , _)
184
+ )
185
+ }
186
+
187
+ override CfgNodes:: ExprNodes:: CallCfgNode asCall ( ) { none ( ) }
188
+
189
+ override DataFlowCallable getEnclosingCallable ( ) { result = TCfgScope ( c .getScope ( ) ) }
190
+
191
+ override string toString ( ) { result = "<captured> " + c .toString ( ) }
192
+
193
+ override Location getLocation ( ) { result = c .getLocation ( ) }
194
+ }
195
+
154
196
/** A call for which we want to compute call targets. */
155
197
private class RelevantCall extends CfgNodes:: ExprNodes:: CallCfgNode {
156
198
pragma [ nomagic]
@@ -341,6 +383,11 @@ private module Cached {
341
383
TNormalCall ( CfgNodes:: ExprNodes:: CallCfgNode c ) or
342
384
TSummaryCall ( FlowSummaryImpl:: Public:: SummarizedCallable c , DataFlow:: Node receiver ) {
343
385
FlowSummaryImpl:: Private:: summaryCallbackRange ( c , receiver )
386
+ } or
387
+ TCapturedCall ( CfgNodes:: ExprNodes:: CallCfgNode c ) {
388
+ SsaImpl:: captureFlowIn ( c , _, _, _, _)
389
+ or
390
+ SsaImpl:: captureFlowOut ( c , _, _)
344
391
}
345
392
346
393
pragma [ nomagic]
@@ -478,6 +525,8 @@ private module Cached {
478
525
result = viableSourceCallable ( call )
479
526
or
480
527
result = viableLibraryCallable ( call )
528
+ or
529
+ result .asCallable ( ) = call .( CapturedCall ) .getTarget ( )
481
530
}
482
531
483
532
cached
@@ -499,7 +548,8 @@ private module Cached {
499
548
THashSplatArgumentPosition ( ) or
500
549
TSplatAllArgumentPosition ( ) or
501
550
TAnyArgumentPosition ( ) or
502
- TAnyKeywordArgumentPosition ( )
551
+ TAnyKeywordArgumentPosition ( ) or
552
+ TCapturedArgumentPosition ( LocalVariable v )
503
553
504
554
cached
505
555
newtype TParameterPosition =
@@ -521,7 +571,8 @@ private module Cached {
521
571
THashSplatParameterPosition ( ) or
522
572
TSplatAllParameterPosition ( ) or
523
573
TAnyParameterPosition ( ) or
524
- TAnyKeywordParameterPosition ( )
574
+ TAnyKeywordParameterPosition ( ) or
575
+ TCapturedParameterPosition ( LocalVariable v )
525
576
}
526
577
527
578
import Cached
@@ -921,7 +972,7 @@ private predicate paramReturnFlow(
921
972
DataFlow:: Node nodeFrom , DataFlow:: PostUpdateNode nodeTo , StepSummary summary
922
973
) {
923
974
exists ( RelevantCall call , DataFlow:: Node arg , DataFlow:: ParameterNode p , Expr nodeFromPreExpr |
924
- TypeTrackerSpecific:: callStep ( call , arg , p ) and
975
+ TypeTrackerSpecific:: callStep ( TNormalCall ( call ) , arg , p ) and
925
976
nodeTo .getPreUpdateNode ( ) = arg and
926
977
summary .toString ( ) = "return" and
927
978
(
@@ -1151,6 +1202,8 @@ class ParameterPosition extends TParameterPosition {
1151
1202
/** Holds if this position represents any positional parameter. */
1152
1203
predicate isAnyNamed ( ) { this = TAnyKeywordParameterPosition ( ) }
1153
1204
1205
+ predicate isCapturedVariable ( LocalVariable v ) { this = TCapturedParameterPosition ( v ) }
1206
+
1154
1207
/** Gets a textual representation of this position. */
1155
1208
string toString ( ) {
1156
1209
this .isSelf ( ) and result = "self"
@@ -1170,6 +1223,8 @@ class ParameterPosition extends TParameterPosition {
1170
1223
this .isAny ( ) and result = "any"
1171
1224
or
1172
1225
this .isAnyNamed ( ) and result = "any-named"
1226
+ or
1227
+ exists ( LocalVariable v | this .isCapturedVariable ( v ) and result = "captured " + v )
1173
1228
}
1174
1229
}
1175
1230
@@ -1196,6 +1251,8 @@ class ArgumentPosition extends TArgumentPosition {
1196
1251
/** Holds if this position represents any positional parameter. */
1197
1252
predicate isAnyNamed ( ) { this = TAnyKeywordArgumentPosition ( ) }
1198
1253
1254
+ predicate isCapturedVariable ( LocalVariable v ) { this = TCapturedArgumentPosition ( v ) }
1255
+
1199
1256
/**
1200
1257
* Holds if this position represents a synthesized argument containing all keyword
1201
1258
* arguments wrapped in a hash.
@@ -1221,6 +1278,8 @@ class ArgumentPosition extends TArgumentPosition {
1221
1278
this .isHashSplat ( ) and result = "**"
1222
1279
or
1223
1280
this .isSplatAll ( ) and result = "*"
1281
+ or
1282
+ exists ( LocalVariable v | this .isCapturedVariable ( v ) and result = "captured " + v )
1224
1283
}
1225
1284
}
1226
1285
@@ -1256,4 +1315,6 @@ predicate parameterMatch(ParameterPosition ppos, ArgumentPosition apos) {
1256
1315
ppos .isAnyNamed ( ) and apos .isKeyword ( _)
1257
1316
or
1258
1317
apos .isAnyNamed ( ) and ppos .isKeyword ( _)
1318
+ or
1319
+ exists ( LocalVariable v | apos .isCapturedVariable ( v ) and ppos .isCapturedVariable ( v ) )
1259
1320
}
0 commit comments