Skip to content

Commit c9f7568

Browse files
committed
Ruby: add Call::isSafeNavigation
1 parent a47e429 commit c9f7568

File tree

7 files changed

+51
-0
lines changed

7 files changed

+51
-0
lines changed

ruby/ql/lib/codeql/ruby/ast/Call.qll

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,14 @@ class MethodCall extends Call instanceof MethodCallImpl {
105105
*/
106106
final Block getBlock() { result = super.getBlockImpl() }
107107

108+
/**
109+
* Holds if the safe nagivation operator (`&.`) is used in this call.
110+
* ```rb
111+
* foo&.empty?
112+
* ```
113+
*/
114+
final predicate isSafeNavigation() { super.isSafeNavigationImpl() }
115+
108116
override string toString() { result = "call to " + this.getMethodName() }
109117

110118
override AstNode getAChild(string pred) {

ruby/ql/lib/codeql/ruby/ast/internal/Call.qll

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ abstract class MethodCallImpl extends CallImpl, TMethodCall {
2828
abstract string getMethodNameImpl();
2929

3030
abstract Block getBlockImpl();
31+
32+
predicate isSafeNavigationImpl() { none() }
3133
}
3234

3335
class MethodCallSynth extends MethodCallImpl, TMethodCallSynth {
@@ -89,6 +91,10 @@ class RegularMethodCall extends MethodCallImpl, TRegularMethodCall {
8991
final override int getNumberOfArgumentsImpl() { result = count(g.getArguments().getChild(_)) }
9092

9193
final override Block getBlockImpl() { toGenerated(result) = g.getBlock() }
94+
95+
final override predicate isSafeNavigationImpl() {
96+
g.getOperator().(Ruby::Token).getValue() = "&."
97+
}
9298
}
9399

94100
class ElementReferenceImpl extends MethodCallImpl, TElementReference {

ruby/ql/test/library-tests/ast/Ast.expected

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -739,6 +739,15 @@ calls/calls.rb:
739739
# 356| getStmt: [LocalVariableAccess] y
740740
# 357| getStmt: [MethodCall] call to unknown_call
741741
# 357| getReceiver: [SelfVariableAccess] self
742+
# 361| getStmt: [MethodCall] call to empty?
743+
# 361| getReceiver: [MethodCall] call to list
744+
# 361| getReceiver: [SelfVariableAccess] self
745+
# 362| getStmt: [MethodCall] call to empty?
746+
# 362| getReceiver: [MethodCall] call to list
747+
# 362| getReceiver: [SelfVariableAccess] self
748+
# 363| getStmt: [MethodCall] call to empty?
749+
# 363| getReceiver: [MethodCall] call to list
750+
# 363| getReceiver: [SelfVariableAccess] self
742751
control/cases.rb:
743752
# 1| [Toplevel] cases.rb
744753
# 2| getStmt: [AssignExpr] ... = ...

ruby/ql/test/library-tests/ast/TreeSitter.expected

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1269,6 +1269,18 @@ calls/calls.rb:
12691269
# 356| 2: [Identifier] y
12701270
# 357| 3: [Identifier] unknown_call
12711271
# 358| 4: [ReservedWord] end
1272+
# 361| 117: [Call] Call
1273+
# 361| 0: [Identifier] list
1274+
# 361| 1: [ReservedWord] .
1275+
# 361| 2: [Identifier] empty?
1276+
# 362| 118: [Call] Call
1277+
# 362| 0: [Identifier] list
1278+
# 362| 1: [ReservedWord] &.
1279+
# 362| 2: [Identifier] empty?
1280+
# 363| 119: [Call] Call
1281+
# 363| 0: [Identifier] list
1282+
# 363| 1: [ReservedWord] ::
1283+
# 363| 2: [Identifier] empty?
12721284
# 1| [Comment] # call with no receiver, arguments, or block
12731285
# 4| [Comment] # call whose name is a scope resolution
12741286
# 7| [Comment] # call whose name is a global scope resolution
@@ -1342,6 +1354,7 @@ calls/calls.rb:
13421354
# 330| [Comment] # forward parameter and forwarded arguments
13431355
# 339| [Comment] # for loop over nested array
13441356
# 349| [Comment] # calls inside lambdas
1357+
# 360| [Comment] # calls with various call operators
13451358
constants/constants.rb:
13461359
# 1| [Program] Program
13471360
# 1| 0: [Module] Module

ruby/ql/test/library-tests/ast/calls/calls.expected

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -372,6 +372,12 @@ callsWithReceiver
372372
| calls.rb:352:13:352:17 | call to foo | calls.rb:352:13:352:17 | self |
373373
| calls.rb:353:13:353:24 | call to unknown_call | calls.rb:353:13:353:24 | self |
374374
| calls.rb:357:3:357:14 | call to unknown_call | calls.rb:357:3:357:14 | self |
375+
| calls.rb:361:1:361:4 | call to list | calls.rb:361:1:361:4 | self |
376+
| calls.rb:361:1:361:11 | call to empty? | calls.rb:361:1:361:4 | call to list |
377+
| calls.rb:362:1:362:4 | call to list | calls.rb:362:1:362:4 | self |
378+
| calls.rb:362:1:362:12 | call to empty? | calls.rb:362:1:362:4 | call to list |
379+
| calls.rb:363:1:363:4 | call to list | calls.rb:363:1:363:4 | self |
380+
| calls.rb:363:1:363:12 | call to empty? | calls.rb:363:1:363:4 | call to list |
375381
callsWithBlock
376382
| calls.rb:17:1:17:17 | call to foo | calls.rb:17:5:17:17 | { ... } |
377383
| calls.rb:20:1:22:3 | call to foo | calls.rb:20:5:22:3 | do ... end |
@@ -424,3 +430,5 @@ setterCalls
424430
| calls.rb:318:1:318:10 | call to count= |
425431
| calls.rb:319:1:319:6 | call to []= |
426432
| calls.rb:320:1:320:32 | call to []= |
433+
callsWithSafeNavigationOperator
434+
| calls.rb:362:1:362:12 | call to empty? |

ruby/ql/test/library-tests/ast/calls/calls.ql

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,3 +31,5 @@ query predicate superCallsWithArguments(SuperCall c, int n, Expr argN) { argN =
3131
query predicate superCallsWithBlock(SuperCall c, Block b) { b = c.getBlock() }
3232

3333
query predicate setterCalls(SetterMethodCall c) { any() }
34+
35+
query predicate callsWithSafeNavigationOperator(MethodCall c) { c.isSafeNavigation() }

ruby/ql/test/library-tests/ast/calls/calls.rb

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -356,3 +356,8 @@ def foo(a, b, ...)
356356
y
357357
unknown_call
358358
end
359+
360+
# calls with various call operators
361+
list.empty?
362+
list&.empty?
363+
list::empty?

0 commit comments

Comments
 (0)