Skip to content

Commit d00c9ea

Browse files
committed
Ruby: RBI library improvements, mostly for parameter types
1 parent 8d264e7 commit d00c9ea

File tree

2 files changed

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

2 files changed

+96
-45
lines changed

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

Lines changed: 93 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
/**
@@ -313,19 +311,64 @@ module Rbi {
313311
MethodSignatureCall getMethodSignatureCall() { result = sigCall }
314312
}
315313

314+
bindingset[paramName]
315+
private predicate isBlockParamName(string paramName) { paramName = ["blk", "block"] }
316+
316317
/**
317318
* A call to `params`. This defines the types of parameters to a method or proc.
318319
*/
319320
class ParamsCall extends MethodCall {
320321
ParamsCall() { this.getMethodName() = "params" }
321322

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

366+
/**
367+
* A call that defines a return type for an associated method.
368+
* The return type is either a specific type, or the void type (i.e. "don't care").
369+
*/
328370
abstract class ReturnsTypeCall extends MethodCall {
371+
/** Get the `ReturnType` corresponding to this call. */
329372
abstract ReturnType getReturnType();
330373
}
331374

@@ -391,6 +434,7 @@ module Rbi {
391434
abstract class ProcReturnsTypeCall extends ReturnsTypeCall, ProcSignatureDefiningCall { }
392435

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

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

409453
/**
410454
* A pair defining the type of a parameter to a method.
455+
*
456+
* This is an argument to some call to `params`.
411457
*/
412458
class ParameterType extends Pair {
413-
private RbiType t;
459+
private ParamsCall paramsCall;
414460

415-
ParameterType() { t = this.getValue() }
461+
ParameterType() { paramsCall.getAnArgument() = this }
416462

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

422465
private MethodBase getAssociatedMethod() {
423-
result = this.getOuterMethodSignatureCall().(MethodSignatureCall).getAssociatedMethod()
466+
result = this.getMethodSignatureCall().(MethodSignatureCall).getAssociatedMethod()
424467
}
425468

426-
/** Gets the parameter to which this type applies. */
469+
/** Gets the `RbiType` of this parameter. */
470+
RbiType getType() { result = this.getValue() }
471+
472+
/** Gets the name of this parameter. */
473+
string getName() { result = this.getKey().getConstantValue().getStringlikeValue() }
474+
475+
/** Gets the `NamedParameter` to which this type applies. */
427476
NamedParameter getParameter() {
428477
result = this.getAssociatedMethod().getAParameter() and
429-
result.getName() = this.getKey().getConstantValue().getStringlikeValue()
478+
result.getName() = this.getName()
430479
}
431480
}
432481
}

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)