Skip to content

Commit 4844e4f

Browse files
committed
ruby: replace the dataflow layer RBI library with the AST layer version
1 parent bedb1d4 commit 4844e4f

File tree

5 files changed

+72
-661
lines changed

5 files changed

+72
-661
lines changed

ruby/ql/lib/codeql/ruby/experimental/Rbi.qll

Lines changed: 70 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ private import codeql.ruby.ApiGraphs
88
private import codeql.ruby.AST
99
private import codeql.ruby.CFG
1010
private import codeql.ruby.controlflow.CfgNodes
11-
private import codeql.ruby.DataFlow
1211

1312
/**
1413
* Provides classes and predicates for working with Ruby Interface (RBI) files
@@ -23,21 +22,26 @@ module Rbi {
2322
/**
2423
* A node representing a Ruby Interface (RBI) type.
2524
*/
26-
abstract class RbiType extends DataFlow::Node { }
25+
abstract class RbiType extends Expr { }
2726

2827
class ConstantReadAccessAsRbiType extends RbiType {
2928
ConstantReadAccessAsRbiType() {
30-
this.asExpr() instanceof ExprNodes::ConstantReadAccessCfgNode
29+
this instanceof ConstantReadAccess
3130
// TODO: should this class be more restrictive?
3231
}
3332
}
3433

34+
/** A method call where the receiver is `T`. */
35+
private class MethodCallAgainstT extends MethodCall {
36+
MethodCallAgainstT() { this.getReceiver().(ConstantReadAccess).getName() = "T" }
37+
}
38+
3539
/**
3640
* A call to `T.any` - a method that takes `RbiType` parameters, and returns
3741
* a type representing the union of those types.
3842
*/
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" }
4145

4246
/**
4347
* Gets a constituent type of this type union.
@@ -48,16 +52,16 @@ module Rbi {
4852
/**
4953
* A call to `T.untyped`.
5054
*/
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" }
5357
}
5458

5559
/**
5660
* A call to `T.nilable`, creating a nilable version of the type provided as
5761
* an argument.
5862
*/
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" }
6165

6266
/** Gets the type that this may represent if not nil. */
6367
RbiType getUnderlyingType() { result = this.getArgument(0) }
@@ -67,75 +71,78 @@ module Rbi {
6771
* A call to `T.type_alias`. The return value of this call can be assigned to
6872
* create a type alias.
6973
*/
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" }
7276

7377
/**
7478
* Gets the type aliased by this call.
7579
*/
7680
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+
)
7884
}
7985
}
8086

8187
/**
8288
* A call to `T.self_type`.
8389
*/
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" }
8692
}
8793

8894
/**
8995
* A call to `T.noreturn`.
9096
*/
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" }
93106
}
94107

95108
/**
96109
* A use of `T::Boolean`.
97110
*/
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" }
100113
}
101114

102115
/**
103116
* A use of `T::Array`.
104117
*/
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" }
107120

108121
/** Gets the type of elements of this array. */
109122
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 |
114124
result = refNode.getArgument(0)
115125
)
116126
}
117127
}
118128

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" }
121131

122-
private DataFlow::CallNode getRefNode() {
123-
result.getReceiver() = this and
124-
result.asExpr() instanceof ExprNodes::ElementReferenceCfgNode
125-
}
132+
private ElementReference getRefNode() { result.getReceiver() = this }
126133

127134
/** 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) }
129136

130137
/** 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) }
132139
}
133140

134141
/**
135142
* A call to `T.proc`. This defines a type signature for a proc or block
136143
*/
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" }
139146

140147
private ProcReturnsTypeCall getReturnsTypeCall() { result.getProcCall() = this }
141148

@@ -193,7 +200,7 @@ module Rbi {
193200
/**
194201
* A call that defines a type signature for a method or proc.
195202
*/
196-
abstract class SignatureCall extends DataFlow::CallNode {
203+
abstract class SignatureCall extends MethodCall {
197204
/**
198205
* Gets the return type of this type signature.
199206
*/
@@ -206,23 +213,23 @@ module Rbi {
206213
}
207214

208215
private predicate isMethodSignatureCallNode(CfgNode n) {
209-
exists(MethodSignatureCall sig | sig.asExpr() = n)
216+
n.(ExprCfgNode).getExpr() instanceof MethodSignatureCall
210217
}
211218

212219
/**
213220
* Holds if `n` is the `i`th transitive successor node of `sigNode` where there
214221
* are no intervening nodes corresponding to `MethodSignatureCall`s.
215222
*/
216-
private predicate methodSignatureSuccessorNodeRanked(MethodSignatureCall sig, CfgNode n, int i) {
223+
private predicate methodSignatureSuccessorNodeRanked(CfgNode sigNode, CfgNode n, int i) {
217224
// direct successor
218225
i = 1 and
219-
n = sig.asExpr().getASuccessor() and
226+
n = sigNode.getASuccessor() and
220227
not isMethodSignatureCallNode(n)
221228
or
222229
// transitive successor
223230
i > 1 and
224231
exists(CfgNode np | n = np.getASuccessor() |
225-
methodSignatureSuccessorNodeRanked(sig, np, i - 1) and
232+
methodSignatureSuccessorNodeRanked(sigNode, np, i - 1) and
226233
not isMethodSignatureCallNode(np)
227234
)
228235
}
@@ -235,31 +242,33 @@ module Rbi {
235242

236243
private MethodParamsCall getParamsCall() { result.getMethodSignatureCall() = this }
237244

245+
private ExprCfgNode getCfgNode() { result.getExpr() = this }
246+
238247
/**
239248
* Gets the method whose type signature is defined by this call.
240249
*/
241-
ExprCfgNode getAssociatedMethod() {
250+
MethodBase getAssociatedMethod() {
242251
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
246255
|
247-
m order by i
248-
)
256+
methodCfgNode order by i
257+
).getExpr()
249258
}
250259

251260
/**
252261
* Gets a call to `attr_reader` or `attr_accessor` where the return type of
253262
* the generated method is described by this call.
254263
*/
255-
ExprNodes::MethodCallCfgNode getAssociatedAttrReaderCall() {
264+
MethodCall getAssociatedAttrReaderCall() {
256265
result =
257266
min(ExprNodes::MethodCallCfgNode c, int i |
258267
c.getExpr().getMethodName() = ["attr_reader", "attr_accessor"] and
259-
methodSignatureSuccessorNodeRanked(this, c, i)
268+
methodSignatureSuccessorNodeRanked(this.getCfgNode(), c, i)
260269
|
261270
c order by i
262-
)
271+
).getExpr()
263272
}
264273

265274
/**
@@ -279,16 +288,15 @@ module Rbi {
279288
* - the return type of
280289
* a method.
281290
*/
282-
class MethodSignatureDefiningCall extends DataFlow::CallNode {
291+
class MethodSignatureDefiningCall extends MethodCall {
283292
private MethodSignatureCall sigCall;
284293

285294
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() |
288296
// The typical pattern for the contents of a `sig` block is something
289297
// like `params(<param defs>).returns(<return type>)` - we want to
290298
// pick up both of these calls.
291-
this.asExpr().getExpr() = c.getReceiver*()
299+
this = c.getReceiver*()
292300
)
293301
}
294302

@@ -302,23 +310,23 @@ module Rbi {
302310
/**
303311
* A call to `params`. This defines the types of parameters to a method or proc.
304312
*/
305-
class ParamsCall extends DataFlow::CallNode {
313+
class ParamsCall extends MethodCall {
306314
ParamsCall() { this.getMethodName() = "params" }
307315

308316
/**
309317
* Gets the type of a parameter defined by this call.
310318
*/
311-
ParameterType getAParameterType() { result = this.getArgument(_).asExpr() }
319+
ParameterType getAParameterType() { result = this.getArgument(_) }
312320
}
313321

314-
abstract class ReturnsTypeCall extends DataFlow::CallNode {
322+
abstract class ReturnsTypeCall extends MethodCall {
315323
abstract ReturnType getReturnType();
316324
}
317325

318326
/**
319327
* A call to `returns`. Defines the return type of a method or proc.
320328
*/
321-
class ReturnsCall extends DataFlow::CallNode {
329+
class ReturnsCall extends MethodCall {
322330
ReturnsCall() { this.getMethodName() = "returns" }
323331

324332
/**
@@ -335,7 +343,7 @@ module Rbi {
335343
/**
336344
* A call to `void`. Essentially a "don't-care" for the return type of a method or proc.
337345
*/
338-
class VoidCall extends DataFlow::CallNode {
346+
class VoidCall extends MethodCall {
339347
VoidCall() { this.getMethodName() = "void" }
340348

341349
/**
@@ -361,7 +369,7 @@ module Rbi {
361369
}
362370

363371
/** 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 {
365373
private ProcCall procCall;
366374

367375
ProcSignatureDefiningCall() { this.getReceiver+() = procCall }
@@ -395,23 +403,23 @@ module Rbi {
395403
/**
396404
* A pair defining the type of a parameter to a method.
397405
*/
398-
class ParameterType extends ExprNodes::PairCfgNode {
406+
class ParameterType extends Pair {
399407
private RbiType t;
400408

401-
ParameterType() { t.asExpr() = this.getValue() }
409+
ParameterType() { t = this.getValue() }
402410

403411
/** Gets the `RbiType` of this parameter. */
404412
RbiType getType() { result = t }
405413

406414
private SignatureCall getOuterMethodSignatureCall() { this = result.getAParameterType() }
407415

408-
private ExprCfgNode getAssociatedMethod() {
416+
private MethodBase getAssociatedMethod() {
409417
result = this.getOuterMethodSignatureCall().(MethodSignatureCall).getAssociatedMethod()
410418
}
411419

412420
/** Gets the parameter to which this type applies. */
413421
NamedParameter getParameter() {
414-
result = this.getAssociatedMethod().getExpr().(MethodBase).getAParameter() and
422+
result = this.getAssociatedMethod().getAParameter() and
415423
result.getName() = this.getKey().getConstantValue().getStringlikeValue()
416424
}
417425
}

0 commit comments

Comments
 (0)