@@ -8,7 +8,6 @@ private import codeql.ruby.ApiGraphs
8
8
private import codeql.ruby.AST
9
9
private import codeql.ruby.CFG
10
10
private import codeql.ruby.controlflow.CfgNodes
11
- private import codeql.ruby.DataFlow
12
11
13
12
/**
14
13
* Provides classes and predicates for working with Ruby Interface (RBI) files
@@ -23,21 +22,26 @@ module Rbi {
23
22
/**
24
23
* A node representing a Ruby Interface (RBI) type.
25
24
*/
26
- abstract class RbiType extends DataFlow :: Node { }
25
+ abstract class RbiType extends Expr { }
27
26
28
27
class ConstantReadAccessAsRbiType extends RbiType {
29
28
ConstantReadAccessAsRbiType ( ) {
30
- this . asExpr ( ) instanceof ExprNodes :: ConstantReadAccessCfgNode
29
+ this instanceof ConstantReadAccess
31
30
// TODO: should this class be more restrictive?
32
31
}
33
32
}
34
33
34
+ /** A method call where the receiver is `T`. */
35
+ private class MethodCallAgainstT extends MethodCall {
36
+ MethodCallAgainstT ( ) { this .getReceiver ( ) .( ConstantReadAccess ) .getName ( ) = "T" }
37
+ }
38
+
35
39
/**
36
40
* A call to `T.any` - a method that takes `RbiType` parameters, and returns
37
41
* a type representing the union of those types.
38
42
*/
39
- class RbiUnionType extends RbiType , DataFlow :: CallNode {
40
- RbiUnionType ( ) { this = API :: getTopLevelMember ( "T" ) . getAMethodCall ( " any") }
43
+ class RbiUnionType extends RbiType , MethodCallAgainstT {
44
+ RbiUnionType ( ) { this . getMethodName ( ) = " any" }
41
45
42
46
/**
43
47
* Gets a constituent type of this type union.
@@ -48,16 +52,16 @@ module Rbi {
48
52
/**
49
53
* A call to `T.untyped`.
50
54
*/
51
- class RbiUntypedType extends RbiType , DataFlow :: CallNode {
52
- RbiUntypedType ( ) { this = API :: getTopLevelMember ( "T" ) . getAMethodCall ( " untyped") }
55
+ class RbiUntypedType extends RbiType , MethodCallAgainstT {
56
+ RbiUntypedType ( ) { this . getMethodName ( ) = " untyped" }
53
57
}
54
58
55
59
/**
56
60
* A call to `T.nilable`, creating a nilable version of the type provided as
57
61
* an argument.
58
62
*/
59
- class RbiNilableType extends RbiType , DataFlow :: CallNode {
60
- RbiNilableType ( ) { this = API :: getTopLevelMember ( "T" ) . getAMethodCall ( " nilable") }
63
+ class RbiNilableType extends RbiType , MethodCallAgainstT {
64
+ RbiNilableType ( ) { this . getMethodName ( ) = " nilable" }
61
65
62
66
/** Gets the type that this may represent if not nil. */
63
67
RbiType getUnderlyingType ( ) { result = this .getArgument ( 0 ) }
@@ -67,75 +71,78 @@ module Rbi {
67
71
* A call to `T.type_alias`. The return value of this call can be assigned to
68
72
* create a type alias.
69
73
*/
70
- class RbiTypeAlias extends RbiType , DataFlow :: CallNode {
71
- RbiTypeAlias ( ) { this = API :: getTopLevelMember ( "T" ) . getAMethodCall ( " type_alias") }
74
+ class RbiTypeAlias extends RbiType , MethodCallAgainstT {
75
+ RbiTypeAlias ( ) { this . getMethodName ( ) = " type_alias" }
72
76
73
77
/**
74
78
* Gets the type aliased by this call.
75
79
*/
76
80
RbiType getAliasedType ( ) {
77
- result .asExpr ( ) = this .getBlock ( ) .asExpr ( ) .( ExprNodes:: StmtSequenceCfgNode ) .getLastStmt ( )
81
+ exists ( ExprNodes:: MethodCallCfgNode n | n .getExpr ( ) = this |
82
+ result = n .getBlock ( ) .( ExprNodes:: StmtSequenceCfgNode ) .getLastStmt ( ) .getExpr ( )
83
+ )
78
84
}
79
85
}
80
86
81
87
/**
82
88
* A call to `T.self_type`.
83
89
*/
84
- class RbiSelfType extends RbiType , DataFlow :: CallNode {
85
- RbiSelfType ( ) { this = API :: getTopLevelMember ( "T" ) . getAMethodCall ( " self_type") }
90
+ class RbiSelfType extends RbiType , MethodCallAgainstT {
91
+ RbiSelfType ( ) { this . getMethodName ( ) = " self_type" }
86
92
}
87
93
88
94
/**
89
95
* A call to `T.noreturn`.
90
96
*/
91
- class RbiNoreturnType extends RbiType , DataFlow:: CallNode {
92
- RbiNoreturnType ( ) { this = API:: getTopLevelMember ( "T" ) .getAMethodCall ( "noreturn" ) }
97
+ class RbiNoreturnType extends RbiType , MethodCallAgainstT {
98
+ RbiNoreturnType ( ) { this .getMethodName ( ) = "noreturn" }
99
+ }
100
+
101
+ /**
102
+ * A `ConstantReadAccess` where the constant is from the `T` module.
103
+ */
104
+ private class ConstantReadAccessFromT extends ConstantReadAccess {
105
+ ConstantReadAccessFromT ( ) { this .getScopeExpr ( ) .( ConstantReadAccess ) .getName ( ) = "T" }
93
106
}
94
107
95
108
/**
96
109
* A use of `T::Boolean`.
97
110
*/
98
- class RbiBooleanType extends RbiType {
99
- RbiBooleanType ( ) { this = API :: getTopLevelMember ( "T" ) . getMember ( " Boolean") . getAUse ( ) }
111
+ class RbiBooleanType extends RbiType , ConstantReadAccessFromT {
112
+ RbiBooleanType ( ) { this . getName ( ) = " Boolean" }
100
113
}
101
114
102
115
/**
103
116
* A use of `T::Array`.
104
117
*/
105
- class RbiArrayType extends RbiType {
106
- RbiArrayType ( ) { this = API :: getTopLevelMember ( "T" ) . getMember ( " Array") . getAUse ( ) }
118
+ class RbiArrayType extends RbiType , ConstantReadAccessFromT {
119
+ RbiArrayType ( ) { this . getName ( ) = " Array" }
107
120
108
121
/** Gets the type of elements of this array. */
109
122
RbiType getElementType ( ) {
110
- exists ( DataFlow:: CallNode refNode |
111
- refNode .getReceiver ( ) = this and
112
- refNode .asExpr ( ) instanceof ExprNodes:: ElementReferenceCfgNode
113
- |
123
+ exists ( ElementReference refNode | refNode .getReceiver ( ) = this |
114
124
result = refNode .getArgument ( 0 )
115
125
)
116
126
}
117
127
}
118
128
119
- class RbiHashType extends RbiType {
120
- RbiHashType ( ) { this = API :: getTopLevelMember ( "T" ) . getMember ( " Hash") . getAUse ( ) }
129
+ class RbiHashType extends RbiType , ConstantReadAccessFromT {
130
+ RbiHashType ( ) { this . getName ( ) = " Hash" }
121
131
122
- private DataFlow:: CallNode getRefNode ( ) {
123
- result .getReceiver ( ) = this and
124
- result .asExpr ( ) instanceof ExprNodes:: ElementReferenceCfgNode
125
- }
132
+ private ElementReference getRefNode ( ) { result .getReceiver ( ) = this }
126
133
127
134
/** Gets the type of keys of this hash type. */
128
- DataFlow :: Node getKeyType ( ) { result = this .getRefNode ( ) .getArgument ( 0 ) }
135
+ Expr getKeyType ( ) { result = this .getRefNode ( ) .getArgument ( 0 ) }
129
136
130
137
/** Gets the type of values of this hash type. */
131
- DataFlow :: Node getValueType ( ) { result = this .getRefNode ( ) .getArgument ( 1 ) }
138
+ Expr getValueType ( ) { result = this .getRefNode ( ) .getArgument ( 1 ) }
132
139
}
133
140
134
141
/**
135
142
* A call to `T.proc`. This defines a type signature for a proc or block
136
143
*/
137
- class ProcCall extends RbiType , SignatureCall {
138
- ProcCall ( ) { this = API :: getTopLevelMember ( "T" ) . getAMethodCall ( " proc") }
144
+ class ProcCall extends RbiType , SignatureCall , MethodCallAgainstT {
145
+ ProcCall ( ) { this . getMethodName ( ) = " proc" }
139
146
140
147
private ProcReturnsTypeCall getReturnsTypeCall ( ) { result .getProcCall ( ) = this }
141
148
@@ -193,7 +200,7 @@ module Rbi {
193
200
/**
194
201
* A call that defines a type signature for a method or proc.
195
202
*/
196
- abstract class SignatureCall extends DataFlow :: CallNode {
203
+ abstract class SignatureCall extends MethodCall {
197
204
/**
198
205
* Gets the return type of this type signature.
199
206
*/
@@ -206,23 +213,23 @@ module Rbi {
206
213
}
207
214
208
215
private predicate isMethodSignatureCallNode ( CfgNode n ) {
209
- exists ( MethodSignatureCall sig | sig . asExpr ( ) = n )
216
+ n . ( ExprCfgNode ) . getExpr ( ) instanceof MethodSignatureCall
210
217
}
211
218
212
219
/**
213
220
* Holds if `n` is the `i`th transitive successor node of `sigNode` where there
214
221
* are no intervening nodes corresponding to `MethodSignatureCall`s.
215
222
*/
216
- private predicate methodSignatureSuccessorNodeRanked ( MethodSignatureCall sig , CfgNode n , int i ) {
223
+ private predicate methodSignatureSuccessorNodeRanked ( CfgNode sigNode , CfgNode n , int i ) {
217
224
// direct successor
218
225
i = 1 and
219
- n = sig . asExpr ( ) .getASuccessor ( ) and
226
+ n = sigNode .getASuccessor ( ) and
220
227
not isMethodSignatureCallNode ( n )
221
228
or
222
229
// transitive successor
223
230
i > 1 and
224
231
exists ( CfgNode np | n = np .getASuccessor ( ) |
225
- methodSignatureSuccessorNodeRanked ( sig , np , i - 1 ) and
232
+ methodSignatureSuccessorNodeRanked ( sigNode , np , i - 1 ) and
226
233
not isMethodSignatureCallNode ( np )
227
234
)
228
235
}
@@ -235,31 +242,33 @@ module Rbi {
235
242
236
243
private MethodParamsCall getParamsCall ( ) { result .getMethodSignatureCall ( ) = this }
237
244
245
+ private ExprCfgNode getCfgNode ( ) { result .getExpr ( ) = this }
246
+
238
247
/**
239
248
* Gets the method whose type signature is defined by this call.
240
249
*/
241
- ExprCfgNode getAssociatedMethod ( ) {
250
+ MethodBase getAssociatedMethod ( ) {
242
251
result =
243
- min ( ExprCfgNode m , int i |
244
- methodSignatureSuccessorNodeRanked ( this , m , i ) and
245
- m .getExpr ( ) instanceof MethodBase
252
+ min ( ExprCfgNode methodCfgNode , int i |
253
+ methodSignatureSuccessorNodeRanked ( this . getCfgNode ( ) , methodCfgNode , i ) and
254
+ methodCfgNode .getExpr ( ) instanceof MethodBase
246
255
|
247
- m order by i
248
- )
256
+ methodCfgNode order by i
257
+ ) . getExpr ( )
249
258
}
250
259
251
260
/**
252
261
* Gets a call to `attr_reader` or `attr_accessor` where the return type of
253
262
* the generated method is described by this call.
254
263
*/
255
- ExprNodes :: MethodCallCfgNode getAssociatedAttrReaderCall ( ) {
264
+ MethodCall getAssociatedAttrReaderCall ( ) {
256
265
result =
257
266
min ( ExprNodes:: MethodCallCfgNode c , int i |
258
267
c .getExpr ( ) .getMethodName ( ) = [ "attr_reader" , "attr_accessor" ] and
259
- methodSignatureSuccessorNodeRanked ( this , c , i )
268
+ methodSignatureSuccessorNodeRanked ( this . getCfgNode ( ) , c , i )
260
269
|
261
270
c order by i
262
- )
271
+ ) . getExpr ( )
263
272
}
264
273
265
274
/**
@@ -279,16 +288,15 @@ module Rbi {
279
288
* - the return type of
280
289
* a method.
281
290
*/
282
- class MethodSignatureDefiningCall extends DataFlow :: CallNode {
291
+ class MethodSignatureDefiningCall extends MethodCall {
283
292
private MethodSignatureCall sigCall ;
284
293
285
294
MethodSignatureDefiningCall ( ) {
286
- // TODO: avoid mapping to AST layer
287
- exists ( MethodCall c | c = sigCall .getBlock ( ) .asExpr ( ) .getExpr ( ) .getAChild ( ) |
295
+ exists ( MethodCall c | c = sigCall .getBlock ( ) .getAChild ( ) |
288
296
// The typical pattern for the contents of a `sig` block is something
289
297
// like `params(<param defs>).returns(<return type>)` - we want to
290
298
// pick up both of these calls.
291
- this . asExpr ( ) . getExpr ( ) = c .getReceiver * ( )
299
+ this = c .getReceiver * ( )
292
300
)
293
301
}
294
302
@@ -302,23 +310,23 @@ module Rbi {
302
310
/**
303
311
* A call to `params`. This defines the types of parameters to a method or proc.
304
312
*/
305
- class ParamsCall extends DataFlow :: CallNode {
313
+ class ParamsCall extends MethodCall {
306
314
ParamsCall ( ) { this .getMethodName ( ) = "params" }
307
315
308
316
/**
309
317
* Gets the type of a parameter defined by this call.
310
318
*/
311
- ParameterType getAParameterType ( ) { result = this .getArgument ( _) . asExpr ( ) }
319
+ ParameterType getAParameterType ( ) { result = this .getArgument ( _) }
312
320
}
313
321
314
- abstract class ReturnsTypeCall extends DataFlow :: CallNode {
322
+ abstract class ReturnsTypeCall extends MethodCall {
315
323
abstract ReturnType getReturnType ( ) ;
316
324
}
317
325
318
326
/**
319
327
* A call to `returns`. Defines the return type of a method or proc.
320
328
*/
321
- class ReturnsCall extends DataFlow :: CallNode {
329
+ class ReturnsCall extends MethodCall {
322
330
ReturnsCall ( ) { this .getMethodName ( ) = "returns" }
323
331
324
332
/**
@@ -335,7 +343,7 @@ module Rbi {
335
343
/**
336
344
* A call to `void`. Essentially a "don't-care" for the return type of a method or proc.
337
345
*/
338
- class VoidCall extends DataFlow :: CallNode {
346
+ class VoidCall extends MethodCall {
339
347
VoidCall ( ) { this .getMethodName ( ) = "void" }
340
348
341
349
/**
@@ -361,7 +369,7 @@ module Rbi {
361
369
}
362
370
363
371
/** A call that defines part of the type signature of a proc or block argument. */
364
- class ProcSignatureDefiningCall extends DataFlow :: CallNode , RbiType {
372
+ class ProcSignatureDefiningCall extends MethodCall , RbiType {
365
373
private ProcCall procCall ;
366
374
367
375
ProcSignatureDefiningCall ( ) { this .getReceiver + ( ) = procCall }
@@ -395,23 +403,23 @@ module Rbi {
395
403
/**
396
404
* A pair defining the type of a parameter to a method.
397
405
*/
398
- class ParameterType extends ExprNodes :: PairCfgNode {
406
+ class ParameterType extends Pair {
399
407
private RbiType t ;
400
408
401
- ParameterType ( ) { t . asExpr ( ) = this .getValue ( ) }
409
+ ParameterType ( ) { t = this .getValue ( ) }
402
410
403
411
/** Gets the `RbiType` of this parameter. */
404
412
RbiType getType ( ) { result = t }
405
413
406
414
private SignatureCall getOuterMethodSignatureCall ( ) { this = result .getAParameterType ( ) }
407
415
408
- private ExprCfgNode getAssociatedMethod ( ) {
416
+ private MethodBase getAssociatedMethod ( ) {
409
417
result = this .getOuterMethodSignatureCall ( ) .( MethodSignatureCall ) .getAssociatedMethod ( )
410
418
}
411
419
412
420
/** Gets the parameter to which this type applies. */
413
421
NamedParameter getParameter ( ) {
414
- result = this .getAssociatedMethod ( ) .getExpr ( ) . ( MethodBase ) . getAParameter ( ) and
422
+ result = this .getAssociatedMethod ( ) .getAParameter ( ) and
415
423
result .getName ( ) = this .getKey ( ) .getConstantValue ( ) .getStringlikeValue ( )
416
424
}
417
425
}
0 commit comments