Skip to content

Commit 140458b

Browse files
authored
Merge pull request #9932 from alexrford/ruby/rbi-typegraph-fixes
Ruby: RBI library changes to support models-as-data model generation
2 parents 6e6880b + 260db1a commit 140458b

File tree

2 files changed

+93
-45
lines changed
  • ruby/ql
    • lib/codeql/ruby/experimental
    • test/library-tests/experimental

2 files changed

+93
-45
lines changed

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

Lines changed: 90 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -150,22 +150,14 @@ module Rbi {
150150
class ProcCall extends RbiType, SignatureCall, MethodCallAgainstT {
151151
ProcCall() { this.getMethodName() = "proc" }
152152

153-
private ProcReturnsTypeCall getReturnsTypeCall() { result.getProcCall() = this }
154-
155-
private ProcParamsCall getParamsCall() { result.getProcCall() = this }
153+
override ReturnsTypeCall getReturnsTypeCall() {
154+
result.(ProcReturnsTypeCall).getProcCall() = this
155+
}
156156

157-
/**
158-
* Gets the return type of this type signature.
159-
*/
160-
override ReturnType getReturnType() { result = this.getReturnsTypeCall().getReturnType() }
157+
override ProcParamsCall getParamsCall() { result.getProcCall() = this }
161158

162-
/**
163-
* Gets the type of a parameter of this type signature.
164-
*/
165-
override ParameterType getAParameterType() {
166-
result = this.getParamsCall().getAParameterType()
167-
}
168-
// TODO: get associated method to which this can be passed
159+
// TODO: widen type for procs/blocks
160+
override MethodBase getAssociatedMethod() { none() }
169161
}
170162
}
171163

@@ -207,15 +199,11 @@ module Rbi {
207199
* A call that defines a type signature for a method or proc.
208200
*/
209201
abstract class SignatureCall extends MethodCall {
210-
/**
211-
* Gets the return type of this type signature.
212-
*/
213-
abstract ReturnType getReturnType();
202+
abstract ParamsCall getParamsCall();
214203

215-
/**
216-
* Gets the type of a parameter of this type signature.
217-
*/
218-
abstract ParameterType getAParameterType();
204+
abstract ReturnsTypeCall getReturnsTypeCall();
205+
206+
abstract MethodBase getAssociatedMethod();
219207
}
220208

221209
private predicate isMethodSignatureCallNode(CfgNode n) {
@@ -240,20 +228,35 @@ module Rbi {
240228
)
241229
}
242230

231+
/**
232+
* A call to a method named `attr_reader` or `attr_accessor`, used to define
233+
* attribute reader methods for a named attribute.
234+
*/
235+
class AttrReaderMethodCall extends MethodCall {
236+
AttrReaderMethodCall() { this.getMethodName() = ["attr_reader", "attr_accessor"] }
237+
238+
/** Gets a name of an attribute defined by this call. */
239+
string getAnAttributeName() {
240+
result = this.getAnArgument().getConstantValue().getStringlikeValue()
241+
}
242+
}
243+
243244
/** A call to `sig` to define the type signature of a method. */
244245
class MethodSignatureCall extends SignatureCall {
245246
MethodSignatureCall() { this.getMethodName() = "sig" }
246247

247-
private MethodReturnsTypeCall getReturnsTypeCall() { result.getMethodSignatureCall() = this }
248+
override ReturnsTypeCall getReturnsTypeCall() {
249+
result.(MethodReturnsTypeCall).getMethodSignatureCall() = this
250+
}
248251

249-
private MethodParamsCall getParamsCall() { result.getMethodSignatureCall() = this }
252+
override MethodParamsCall getParamsCall() { result.getMethodSignatureCall() = this }
250253

251254
private ExprCfgNode getCfgNode() { result.getExpr() = this }
252255

253256
/**
254257
* Gets the method whose type signature is defined by this call.
255258
*/
256-
MethodBase getAssociatedMethod() {
259+
override MethodBase getAssociatedMethod() {
257260
result =
258261
min(ExprCfgNode methodCfgNode, int i |
259262
methodSignatureSuccessorNodeRanked(this.getCfgNode(), methodCfgNode, i) and
@@ -267,10 +270,10 @@ module Rbi {
267270
* Gets a call to `attr_reader` or `attr_accessor` where the return type of
268271
* the generated method is described by this call.
269272
*/
270-
MethodCall getAssociatedAttrReaderCall() {
273+
AttrReaderMethodCall getAssociatedAttrReaderCall() {
271274
result =
272275
min(ExprNodes::MethodCallCfgNode c, int i |
273-
c.getExpr().getMethodName() = ["attr_reader", "attr_accessor"] and
276+
c.getExpr() instanceof AttrReaderMethodCall and
274277
methodSignatureSuccessorNodeRanked(this.getCfgNode(), c, i)
275278
|
276279
c order by i
@@ -280,12 +283,7 @@ module Rbi {
280283
/**
281284
* Gets the return type of this type signature.
282285
*/
283-
override ReturnType getReturnType() { result = this.getReturnsTypeCall().getReturnType() }
284-
285-
/**
286-
* Gets the type of a parameter of this type signature.
287-
*/
288-
override ParameterType getAParameterType() { result = this.getParamsCall().getAParameterType() }
286+
ReturnType getReturnType() { result = this.getReturnsTypeCall().getReturnType() }
289287
}
290288

291289
/**
@@ -320,12 +318,54 @@ module Rbi {
320318
ParamsCall() { this.getMethodName() = "params" }
321319

322320
/**
323-
* Gets the type of a parameter defined by this call.
321+
* Gets the type of a positional parameter defined by this call.
324322
*/
325-
ParameterType getAParameterType() { result = this.getArgument(_) }
323+
ParameterType getPositionalParameterType(int i) {
324+
result = this.getArgument(i) and
325+
// explicitly exclude keyword parameters
326+
not this.getAssociatedParameter(result.getName()) instanceof KeywordParameter and
327+
// and exclude block arguments
328+
not this.getAssociatedParameter(result.getName()) instanceof BlockParameter
329+
}
330+
331+
/** Gets the type of the keyword parameter named `keyword`. */
332+
ParameterType getKeywordParameterType(string keyword) {
333+
exists(KeywordParameter kp |
334+
kp = this.getAssociatedParameter(keyword) and
335+
kp.getName() = keyword and
336+
result.getType() = this.getKeywordArgument(keyword)
337+
)
338+
}
339+
340+
/** Gets the type of the block parameter to the associated method. */
341+
ParameterType getBlockParameterType() {
342+
this.getAssociatedParameter(result.getName()) instanceof BlockParameter and
343+
result = this.getArgument(_)
344+
}
345+
346+
/** Gets the parameter with the given name. */
347+
NamedParameter getAssociatedParameter(string name) {
348+
result = this.getSignatureCall().getAssociatedMethod().getAParameter() and
349+
result.getName() = name
350+
}
351+
352+
/** Gets the signature call which this params call belongs to. */
353+
SignatureCall getSignatureCall() { this = result.getParamsCall() }
354+
355+
/** Gets a parameter type associated with this call */
356+
ParameterType getAParameterType() {
357+
result = this.getPositionalParameterType(_) or
358+
result = this.getKeywordParameterType(_) or
359+
result = this.getBlockParameterType()
360+
}
326361
}
327362

363+
/**
364+
* A call that defines a return type for an associated method.
365+
* The return type is either a specific type, or the void type (i.e. "don't care").
366+
*/
328367
abstract class ReturnsTypeCall extends MethodCall {
368+
/** Get the `ReturnType` corresponding to this call. */
329369
abstract ReturnType getReturnType();
330370
}
331371

@@ -391,6 +431,7 @@ module Rbi {
391431
abstract class ProcReturnsTypeCall extends ReturnsTypeCall, ProcSignatureDefiningCall { }
392432

393433
/** A call that defines the parameter types of a proc or block. */
434+
// TODO: there is currently no way to map from this to parameter types with actual associated parameters
394435
class ProcParamsCall extends ParamsCall, ProcSignatureDefiningCall { }
395436

396437
/** A call that defines the return type of a non-void proc or block. */
@@ -408,25 +449,30 @@ module Rbi {
408449

409450
/**
410451
* A pair defining the type of a parameter to a method.
452+
*
453+
* This is an argument to some call to `params`.
411454
*/
412455
class ParameterType extends Pair {
413-
private RbiType t;
456+
private ParamsCall paramsCall;
414457

415-
ParameterType() { t = this.getValue() }
458+
ParameterType() { paramsCall.getAnArgument() = this }
416459

417-
/** Gets the `RbiType` of this parameter. */
418-
RbiType getType() { result = t }
419-
420-
private SignatureCall getOuterMethodSignatureCall() { this = result.getAParameterType() }
460+
private SignatureCall getMethodSignatureCall() { paramsCall = result.getParamsCall() }
421461

422462
private MethodBase getAssociatedMethod() {
423-
result = this.getOuterMethodSignatureCall().(MethodSignatureCall).getAssociatedMethod()
463+
result = this.getMethodSignatureCall().(MethodSignatureCall).getAssociatedMethod()
424464
}
425465

426-
/** Gets the parameter to which this type applies. */
466+
/** Gets the `RbiType` of this parameter. */
467+
RbiType getType() { result = this.getValue() }
468+
469+
/** Gets the name of this parameter. */
470+
string getName() { result = this.getKey().getConstantValue().getStringlikeValue() }
471+
472+
/** Gets the `NamedParameter` to which this type applies. */
427473
NamedParameter getParameter() {
428474
result = this.getAssociatedMethod().getAParameter() and
429-
result.getName() = this.getKey().getConstantValue().getStringlikeValue()
475+
result.getName() = this.getName()
430476
}
431477
}
432478
}

ruby/ql/test/library-tests/experimental/Rbi.ql

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,9 @@ query predicate hashTypes(RbiHashType ht, RbiType kt, RbiType vt) {
1515
kt = ht.getKeyType() and vt = ht.getValueType()
1616
}
1717

18-
query predicate signatureCalls(SignatureCall c, ReturnType r) { r = c.getReturnType() }
18+
query predicate signatureCalls(SignatureCall c, ReturnType r) {
19+
r = c.getReturnsTypeCall().getReturnType()
20+
}
1921

2022
query predicate paramsCalls(ParamsCall c) { any() }
2123

0 commit comments

Comments
 (0)