Skip to content

Commit 63dcce9

Browse files
committed
Ruby: Refactor isArrayConstant
1 parent b5a3d3c commit 63dcce9

File tree

4 files changed

+58
-6
lines changed

4 files changed

+58
-6
lines changed

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

Lines changed: 31 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ private import ExprNodes
3636
* constant value in some cases.
3737
*/
3838
private module Propagation {
39-
private ExprCfgNode getSource(VariableReadAccessCfgNode read) {
39+
ExprCfgNode getSource(VariableReadAccessCfgNode read) {
4040
exists(Ssa::WriteDefinition def |
4141
def.assigns(result) and
4242
read = def.getARead()
@@ -511,7 +511,8 @@ private module Cached {
511511
import Cached
512512

513513
/**
514-
* Holds if `e` is an array constructed from an array literal.
514+
* Holds if the control flow node `e` refers to an array constructed from the
515+
* array literal `arr`.
515516
* Example:
516517
* ```rb
517518
* [1, 2, 3]
@@ -523,9 +524,33 @@ predicate isArrayConstant(ExprCfgNode e, ArrayLiteralCfgNode arr) {
523524
// [...]
524525
e = arr
525526
or
526-
// C = [...]; C
527-
e.(ExprNodes::ConstantReadAccessCfgNode).getExpr().getValue().getDesugared() = arr.getExpr()
527+
// e = [...]; e
528+
isArrayConstant(getSource(e), arr)
528529
or
529-
// x = [...]; x
530-
exists(Ssa::WriteDefinition def | def.getARead() = e and def.assigns(arr))
530+
isArrayExpr(e.getExpr(), arr)
531+
}
532+
533+
/**
534+
* Holds if the expression `e` refers to an array constructed from the array literal `arr`.
535+
*/
536+
predicate isArrayExpr(Expr e, ArrayLiteralCfgNode arr) {
537+
// e = [...]
538+
e = arr.getExpr()
539+
or
540+
// Like above, but handles the desugaring of array literals to Array.[] calls.
541+
e.getDesugared() = arr.getExpr()
542+
or
543+
// A = [...]; A
544+
// A = a; A
545+
isArrayExpr(e.(ConstantReadAccess).getValue(), arr)
546+
or
547+
// Recurse via CFG nodes. Necessary for example in:
548+
// a = [...]
549+
// A = a
550+
// A
551+
//
552+
// We map from A to a via ConstantReadAccess::getValue, yielding the Expr a.
553+
// To get to [...] we need to go via getSource(ExprCfgNode e), so we find a
554+
// CFG node for a and call `isArrayConstant`.
555+
isArrayConstant(e.getAControlFlowNode(), arr)
531556
}

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

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,13 @@ constantAccess
6161
| constants.rb:71:5:71:14 | FOURTY_SIX | write | FOURTY_SIX | ConstantAssignment |
6262
| constants.rb:73:18:73:21 | Mod3 | read | Mod3 | ConstantReadAccess |
6363
| constants.rb:73:18:73:33 | FOURTY_SIX | read | FOURTY_SIX | ConstantReadAccess |
64+
| constants.rb:78:5:78:13 | Array | read | Array | ConstantReadAccess |
65+
| constants.rb:79:1:79:1 | A | write | A | ConstantAssignment |
66+
| constants.rb:79:5:79:13 | Array | read | Array | ConstantReadAccess |
67+
| constants.rb:80:1:80:1 | B | write | B | ConstantAssignment |
68+
| constants.rb:81:1:81:1 | C | write | C | ConstantAssignment |
69+
| constants.rb:81:5:81:5 | A | read | A | ConstantReadAccess |
70+
| constants.rb:82:5:82:5 | B | read | B | ConstantReadAccess |
6471
getConst
6572
| constants.rb:1:1:15:3 | ModuleA | CONST_B | constants.rb:6:15:6:23 | "const_b" |
6673
| constants.rb:1:1:15:3 | ModuleA | FOURTY_FOUR | constants.rb:53:17:53:29 | "fourty-four" |
@@ -133,3 +140,12 @@ constantWriteAccessQualifiedName
133140
| constants.rb:70:3:72:5 | Mod5 | Mod3::Mod5 |
134141
| constants.rb:71:5:71:14 | FOURTY_SIX | Mod1::Mod3::Mod5::FOURTY_SIX |
135142
| constants.rb:71:5:71:14 | FOURTY_SIX | Mod3::Mod5::FOURTY_SIX |
143+
| constants.rb:79:1:79:1 | A | A |
144+
| constants.rb:80:1:80:1 | B | B |
145+
| constants.rb:81:1:81:1 | C | C |
146+
arrayConstant
147+
| constants.rb:20:13:20:37 | call to [] | constants.rb:20:13:20:37 | call to [] |
148+
| constants.rb:78:5:78:13 | call to [] | constants.rb:78:5:78:13 | call to [] |
149+
| constants.rb:79:5:79:13 | call to [] | constants.rb:79:5:79:13 | call to [] |
150+
| constants.rb:80:5:80:5 | a | constants.rb:78:5:78:13 | call to [] |
151+
| constants.rb:81:5:81:5 | A | constants.rb:79:5:79:13 | call to [] |

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import ruby
22
import codeql.ruby.ast.internal.Module as M
3+
import codeql.ruby.ast.internal.Constant
34

45
query predicate constantAccess(ConstantAccess a, string kind, string name, string cls) {
56
(
@@ -20,3 +21,5 @@ query predicate constantValue(ConstantReadAccess a, Expr e) { e = a.getValue() }
2021
query predicate constantWriteAccessQualifiedName(ConstantWriteAccess w, string qualifiedName) {
2122
w.getAQualifiedName() = qualifiedName
2223
}
24+
25+
query predicate arrayConstant = isArrayConstant/2;

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

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,3 +72,11 @@ module Mod3::Mod5
7272
end
7373
@@fourty_six = Mod3::FOURTY_SIX
7474
end
75+
76+
# Array constants
77+
78+
a = [1, 2, 3]
79+
A = [1, 2, 3]
80+
B = a
81+
C = A
82+
b = B

0 commit comments

Comments
 (0)