From 038bdecad7b333910143a0cbdae6feb49f962778 Mon Sep 17 00:00:00 2001 From: Asger F Date: Fri, 21 Oct 2022 09:13:55 +0200 Subject: [PATCH 1/4] Ruby: add test with compound assignment to a constant --- .../controlflow/graph/Cfg.expected | 34 ++++--------------- .../graph/constant_compound_assign.rb | 17 ++++++++++ 2 files changed, 24 insertions(+), 27 deletions(-) create mode 100644 ruby/ql/test/library-tests/controlflow/graph/constant_compound_assign.rb diff --git a/ruby/ql/test/library-tests/controlflow/graph/Cfg.expected b/ruby/ql/test/library-tests/controlflow/graph/Cfg.expected index 2359eb2c24a..6933a62fefe 100644 --- a/ruby/ql/test/library-tests/controlflow/graph/Cfg.expected +++ b/ruby/ql/test/library-tests/controlflow/graph/Cfg.expected @@ -3696,7 +3696,7 @@ cfg.rb: #-----| -> exit filter_nil # 207| filter_nil -#-----| -> self +#-----| -> exit cfg.rb (normal) # 207| list #-----| -> list @@ -3733,34 +3733,14 @@ cfg.rb: # 209| call to nil? #-----| -> exit do ... end (normal) -# 213| call to do_something -#-----| -> exit cfg.rb (normal) +constant_compound_assign.rb: +# 1| Foo +#-----| -> foo_before -# 213| self -#-----| -> do ... end +# 1| enter constant_compound_assign.rb +#-----| -> Foo -# 213| do ... end -#-----| -> call to do_something - -# 213| enter do ... end -#-----| -> self - -# 213| exit do ... end - -# 213| exit do ... end (normal) -#-----| -> exit do ... end - -# 214| self -#-----| -> call to something - -# 214| call to something -#-----| -> self - -# 215| call to something_else -#-----| -> exit do ... end (normal) - -# 215| self -#-----| -> call to something_else +# 2| foo_before desugar.rb: # 1| enter m1 diff --git a/ruby/ql/test/library-tests/controlflow/graph/constant_compound_assign.rb b/ruby/ql/test/library-tests/controlflow/graph/constant_compound_assign.rb new file mode 100644 index 00000000000..dc9c3a34946 --- /dev/null +++ b/ruby/ql/test/library-tests/controlflow/graph/constant_compound_assign.rb @@ -0,0 +1,17 @@ +module Foo + def foo_before + end + + FOO_CONSTANT ||= 123 + + def foo_after + end +end + +def top_before +end + +TOP_CONSTANT ||= 123 + +def top_after +end From d26b0892cf867aef2723be21f3006e0bd1861cb0 Mon Sep 17 00:00:00 2001 From: Asger F Date: Fri, 21 Oct 2022 09:19:47 +0200 Subject: [PATCH 2/4] Ruby: also add an AST test --- ruby/ql/test/library-tests/ast/Ast.expected | 20 +++++++++++ .../library-tests/ast/TreeSitter.expected | 34 +++++++++++++++++++ .../test/library-tests/ast/ValueText.expected | 7 ++++ .../ast/operations/assignment.expected | 13 +++++++ .../ast/operations/operation.expected | 10 ++++++ .../ast/operations/operations.rb | 6 ++++ 6 files changed, 90 insertions(+) diff --git a/ruby/ql/test/library-tests/ast/Ast.expected b/ruby/ql/test/library-tests/ast/Ast.expected index 33229c64393..752969f8af2 100644 --- a/ruby/ql/test/library-tests/ast/Ast.expected +++ b/ruby/ql/test/library-tests/ast/Ast.expected @@ -2833,6 +2833,26 @@ operations/operations.rb: # 96| getStmt: [AssignMulExpr] ... *= ... # 96| getAnOperand/getLeftOperand: [GlobalVariableAccess] $global_var # 96| getAnOperand/getRightOperand: [IntegerLiteral] 6 +# 98| getStmt: [AssignExpr] ... = ... +# 98| getAnOperand/getLeftOperand: [ConstantAssignment] CONSTANT1 +# 98| getAnOperand/getRightOperand: [IntegerLiteral] 5 +# 99| getStmt: [AssignAddExpr] ... += ... +# 99| getAnOperand/getLeftOperand: [ConstantAssignment, ConstantReadAccess] CONSTANT2 +# 99| getAnOperand/getRightOperand: [IntegerLiteral] 6 +# 100| getStmt: [AssignLogicalOrExpr] ... ||= ... +# 100| getAnOperand/getLeftOperand: [ConstantAssignment, ConstantReadAccess] CONSTANT3 +# 100| getAnOperand/getRightOperand: [IntegerLiteral] 7 +# 101| getStmt: [AssignLogicalOrExpr] ... ||= ... +# 101| getAnOperand/getLeftOperand: [ConstantAssignment, ConstantReadAccess] MemberConstant +# 101| getScopeExpr: [ConstantReadAccess] Foo +# 101| getAnOperand/getRightOperand: [IntegerLiteral] 8 +# 102| getStmt: [AssignLogicalOrExpr] ... ||= ... +# 102| getAnOperand/getLeftOperand: [ConstantAssignment, ConstantReadAccess] OtherConstant +# 102| getScopeExpr: [MethodCall] call to bar +# 102| getReceiver: [MethodCall] call to foo +# 102| getReceiver: [SelfVariableAccess] self +# 102| getArgument: [IntegerLiteral] 1 +# 102| getAnOperand/getRightOperand: [IntegerLiteral] 7 params/params.rb: # 1| [Toplevel] params.rb # 4| getStmt: [Method] identifier_method_params diff --git a/ruby/ql/test/library-tests/ast/TreeSitter.expected b/ruby/ql/test/library-tests/ast/TreeSitter.expected index 839ad55fb38..4f58b38143d 100644 --- a/ruby/ql/test/library-tests/ast/TreeSitter.expected +++ b/ruby/ql/test/library-tests/ast/TreeSitter.expected @@ -5537,6 +5537,40 @@ operations/operations.rb: # 96| 0: [GlobalVariable] $global_var # 96| 1: [ReservedWord] *= # 96| 2: [Integer] 6 +# 98| 66: [Assignment] Assignment +# 98| 0: [Constant] CONSTANT1 +# 98| 1: [ReservedWord] = +# 98| 2: [Integer] 5 +# 99| 67: [OperatorAssignment] OperatorAssignment +# 99| 0: [Constant] CONSTANT2 +# 99| 1: [ReservedWord] += +# 99| 2: [Integer] 6 +# 100| 68: [OperatorAssignment] OperatorAssignment +# 100| 0: [Constant] CONSTANT3 +# 100| 1: [ReservedWord] ||= +# 100| 2: [Integer] 7 +# 101| 69: [OperatorAssignment] OperatorAssignment +# 101| 0: [ScopeResolution] ScopeResolution +# 101| 0: [Constant] Foo +# 101| 1: [ReservedWord] :: +# 101| 2: [Constant] MemberConstant +# 101| 1: [ReservedWord] ||= +# 101| 2: [Integer] 8 +# 102| 70: [OperatorAssignment] OperatorAssignment +# 102| 0: [ScopeResolution] ScopeResolution +# 102| 0: [Call] Call +# 102| 0: [Call] Call +# 102| 0: [Identifier] foo +# 102| 1: [ArgumentList] ArgumentList +# 102| 0: [ReservedWord] ( +# 102| 1: [Integer] 1 +# 102| 2: [ReservedWord] ) +# 102| 1: [ReservedWord] . +# 102| 2: [Identifier] bar +# 102| 1: [ReservedWord] :: +# 102| 2: [Constant] OtherConstant +# 102| 1: [ReservedWord] ||= +# 102| 2: [Integer] 7 # 1| [Comment] # Start with assignments to all the identifiers used below, so that they are # 2| [Comment] # interpreted as variables. # 22| [Comment] # Unary operations diff --git a/ruby/ql/test/library-tests/ast/ValueText.expected b/ruby/ql/test/library-tests/ast/ValueText.expected index 1492d619e33..28ae0c0a256 100644 --- a/ruby/ql/test/library-tests/ast/ValueText.expected +++ b/ruby/ql/test/library-tests/ast/ValueText.expected @@ -888,6 +888,12 @@ exprValue | operations/operations.rb:92:10:92:10 | 4 | 4 | int | | operations/operations.rb:95:15:95:15 | 5 | 5 | int | | operations/operations.rb:96:16:96:16 | 6 | 6 | int | +| operations/operations.rb:98:13:98:13 | 5 | 5 | int | +| operations/operations.rb:99:14:99:14 | 6 | 6 | int | +| operations/operations.rb:100:15:100:15 | 7 | 7 | int | +| operations/operations.rb:101:25:101:25 | 8 | 8 | int | +| operations/operations.rb:102:5:102:5 | 1 | 1 | int | +| operations/operations.rb:102:31:102:31 | 7 | 7 | int | | params/params.rb:41:46:41:46 | 7 | 7 | int | | params/params.rb:47:19:47:21 | :bar | :bar | symbol | | params/params.rb:47:24:47:24 | 2 | 2 | int | @@ -1764,6 +1770,7 @@ exprCfgNodeValue | operations/operations.rb:92:10:92:10 | 4 | 4 | int | | operations/operations.rb:95:15:95:15 | 5 | 5 | int | | operations/operations.rb:96:16:96:16 | 6 | 6 | int | +| operations/operations.rb:98:13:98:13 | 5 | 5 | int | | params/params.rb:41:46:41:46 | 7 | 7 | int | | params/params.rb:47:19:47:21 | :bar | :bar | symbol | | params/params.rb:47:24:47:24 | 2 | 2 | int | diff --git a/ruby/ql/test/library-tests/ast/operations/assignment.expected b/ruby/ql/test/library-tests/ast/operations/assignment.expected index 7a536f183f7..9b981daae18 100644 --- a/ruby/ql/test/library-tests/ast/operations/assignment.expected +++ b/ruby/ql/test/library-tests/ast/operations/assignment.expected @@ -52,6 +52,11 @@ assignments | operations.rb:95:1:95:15 | ... = ... | = | operations.rb:95:1:95:11 | $global_var | operations.rb:95:15:95:15 | 5 | AssignExpr | | operations.rb:96:1:96:16 | ... *= ... | *= | operations.rb:96:1:96:11 | $global_var | operations.rb:96:16:96:16 | 6 | AssignMulExpr | | operations.rb:96:1:96:16 | ... = ... | = | operations.rb:96:1:96:11 | $global_var | operations.rb:96:13:96:14 | ... * ... | AssignExpr | +| operations.rb:98:1:98:13 | ... = ... | = | operations.rb:98:1:98:9 | CONSTANT1 | operations.rb:98:13:98:13 | 5 | AssignExpr | +| operations.rb:99:1:99:14 | ... += ... | += | operations.rb:99:1:99:9 | CONSTANT2 | operations.rb:99:14:99:14 | 6 | AssignAddExpr | +| operations.rb:100:1:100:15 | ... \|\|= ... | \|\|= | operations.rb:100:1:100:9 | CONSTANT3 | operations.rb:100:15:100:15 | 7 | AssignLogicalOrExpr | +| operations.rb:101:1:101:25 | ... \|\|= ... | \|\|= | operations.rb:101:1:101:19 | MemberConstant | operations.rb:101:25:101:25 | 8 | AssignLogicalOrExpr | +| operations.rb:102:1:102:31 | ... \|\|= ... | \|\|= | operations.rb:102:1:102:25 | OtherConstant | operations.rb:102:31:102:31 | 7 | AssignLogicalOrExpr | assignOperations | operations.rb:69:1:69:8 | ... += ... | += | operations.rb:69:1:69:1 | x | operations.rb:69:6:69:8 | 128 | AssignAddExpr | | operations.rb:70:1:70:7 | ... -= ... | -= | operations.rb:70:1:70:1 | y | operations.rb:70:6:70:7 | 32 | AssignSubExpr | @@ -69,6 +74,10 @@ assignOperations | operations.rb:89:3:89:9 | ... += ... | += | operations.rb:89:3:89:4 | @x | operations.rb:89:9:89:9 | 2 | AssignAddExpr | | operations.rb:92:3:92:10 | ... /= ... | /= | operations.rb:92:3:92:5 | @@y | operations.rb:92:10:92:10 | 4 | AssignDivExpr | | operations.rb:96:1:96:16 | ... *= ... | *= | operations.rb:96:1:96:11 | $global_var | operations.rb:96:16:96:16 | 6 | AssignMulExpr | +| operations.rb:99:1:99:14 | ... += ... | += | operations.rb:99:1:99:9 | CONSTANT2 | operations.rb:99:14:99:14 | 6 | AssignAddExpr | +| operations.rb:100:1:100:15 | ... \|\|= ... | \|\|= | operations.rb:100:1:100:9 | CONSTANT3 | operations.rb:100:15:100:15 | 7 | AssignLogicalOrExpr | +| operations.rb:101:1:101:25 | ... \|\|= ... | \|\|= | operations.rb:101:1:101:19 | MemberConstant | operations.rb:101:25:101:25 | 8 | AssignLogicalOrExpr | +| operations.rb:102:1:102:31 | ... \|\|= ... | \|\|= | operations.rb:102:1:102:25 | OtherConstant | operations.rb:102:31:102:31 | 7 | AssignLogicalOrExpr | assignArithmeticOperations | operations.rb:69:1:69:8 | ... += ... | += | operations.rb:69:1:69:1 | x | operations.rb:69:6:69:8 | 128 | AssignAddExpr | | operations.rb:70:1:70:7 | ... -= ... | -= | operations.rb:70:1:70:1 | y | operations.rb:70:6:70:7 | 32 | AssignSubExpr | @@ -79,9 +88,13 @@ assignArithmeticOperations | operations.rb:89:3:89:9 | ... += ... | += | operations.rb:89:3:89:4 | @x | operations.rb:89:9:89:9 | 2 | AssignAddExpr | | operations.rb:92:3:92:10 | ... /= ... | /= | operations.rb:92:3:92:5 | @@y | operations.rb:92:10:92:10 | 4 | AssignDivExpr | | operations.rb:96:1:96:16 | ... *= ... | *= | operations.rb:96:1:96:11 | $global_var | operations.rb:96:16:96:16 | 6 | AssignMulExpr | +| operations.rb:99:1:99:14 | ... += ... | += | operations.rb:99:1:99:9 | CONSTANT2 | operations.rb:99:14:99:14 | 6 | AssignAddExpr | assignLogicalOperations | operations.rb:77:2:77:8 | ... &&= ... | &&= | operations.rb:77:2:77:2 | x | operations.rb:77:8:77:8 | y | AssignLogicalAndExpr | | operations.rb:78:2:78:8 | ... \|\|= ... | \|\|= | operations.rb:78:2:78:2 | a | operations.rb:78:8:78:8 | b | AssignLogicalOrExpr | +| operations.rb:100:1:100:15 | ... \|\|= ... | \|\|= | operations.rb:100:1:100:9 | CONSTANT3 | operations.rb:100:15:100:15 | 7 | AssignLogicalOrExpr | +| operations.rb:101:1:101:25 | ... \|\|= ... | \|\|= | operations.rb:101:1:101:19 | MemberConstant | operations.rb:101:25:101:25 | 8 | AssignLogicalOrExpr | +| operations.rb:102:1:102:31 | ... \|\|= ... | \|\|= | operations.rb:102:1:102:25 | OtherConstant | operations.rb:102:31:102:31 | 7 | AssignLogicalOrExpr | assignBitwiseOperations | operations.rb:81:2:81:8 | ... <<= ... | <<= | operations.rb:81:2:81:2 | x | operations.rb:81:8:81:8 | 2 | AssignLShiftExpr | | operations.rb:82:2:82:8 | ... >>= ... | >>= | operations.rb:82:2:82:2 | y | operations.rb:82:8:82:8 | 3 | AssignRShiftExpr | diff --git a/ruby/ql/test/library-tests/ast/operations/operation.expected b/ruby/ql/test/library-tests/ast/operations/operation.expected index 76dbae694be..6e230918a14 100644 --- a/ruby/ql/test/library-tests/ast/operations/operation.expected +++ b/ruby/ql/test/library-tests/ast/operations/operation.expected @@ -194,3 +194,13 @@ | operations.rb:96:1:96:16 | ... = ... | = | operations.rb:96:13:96:14 | ... * ... | AssignExpr | | operations.rb:96:13:96:14 | ... * ... | * | operations.rb:96:1:96:11 | $global_var | MulExpr | | operations.rb:96:13:96:14 | ... * ... | * | operations.rb:96:16:96:16 | 6 | MulExpr | +| operations.rb:98:1:98:13 | ... = ... | = | operations.rb:98:1:98:9 | CONSTANT1 | AssignExpr | +| operations.rb:98:1:98:13 | ... = ... | = | operations.rb:98:13:98:13 | 5 | AssignExpr | +| operations.rb:99:1:99:14 | ... += ... | += | operations.rb:99:1:99:9 | CONSTANT2 | AssignAddExpr | +| operations.rb:99:1:99:14 | ... += ... | += | operations.rb:99:14:99:14 | 6 | AssignAddExpr | +| operations.rb:100:1:100:15 | ... \|\|= ... | \|\|= | operations.rb:100:1:100:9 | CONSTANT3 | AssignLogicalOrExpr | +| operations.rb:100:1:100:15 | ... \|\|= ... | \|\|= | operations.rb:100:15:100:15 | 7 | AssignLogicalOrExpr | +| operations.rb:101:1:101:25 | ... \|\|= ... | \|\|= | operations.rb:101:1:101:19 | MemberConstant | AssignLogicalOrExpr | +| operations.rb:101:1:101:25 | ... \|\|= ... | \|\|= | operations.rb:101:25:101:25 | 8 | AssignLogicalOrExpr | +| operations.rb:102:1:102:31 | ... \|\|= ... | \|\|= | operations.rb:102:1:102:25 | OtherConstant | AssignLogicalOrExpr | +| operations.rb:102:1:102:31 | ... \|\|= ... | \|\|= | operations.rb:102:31:102:31 | 7 | AssignLogicalOrExpr | diff --git a/ruby/ql/test/library-tests/ast/operations/operations.rb b/ruby/ql/test/library-tests/ast/operations/operations.rb index fcbc4551bde..49a6210a16b 100644 --- a/ruby/ql/test/library-tests/ast/operations/operations.rb +++ b/ruby/ql/test/library-tests/ast/operations/operations.rb @@ -94,3 +94,9 @@ end $global_var = 5 $global_var *= 6 + +CONSTANT1 = 5 +CONSTANT2 += 6 +CONSTANT3 ||= 7 +Foo::MemberConstant ||= 8 +foo(1).bar::OtherConstant ||= 7 From ccaa12998d16265871e7f1e13def8e02e77dfb40 Mon Sep 17 00:00:00 2001 From: Arthur Baars Date: Sat, 22 Oct 2022 00:37:13 +0200 Subject: [PATCH 3/4] Ruby: desugar compound constant-assignments --- ruby/ql/lib/codeql/ruby/ast/Constant.qll | 18 +- ruby/ql/lib/codeql/ruby/ast/Expr.qll | 2 +- ruby/ql/lib/codeql/ruby/ast/internal/AST.qll | 21 +- .../codeql/ruby/ast/internal/Synthesis.qll | 233 +++++++++++++++++- .../internal/ControlFlowGraphImpl.qll | 7 +- .../library-tests/ast/AstDesugar.expected | 39 +++ .../test/library-tests/ast/ValueText.expected | 5 + .../ast/operations/assignment.expected | 6 + .../ast/operations/binary.expected | 8 + .../ast/operations/operation.expected | 20 ++ .../controlflow/graph/Cfg.expected | 78 +++++- .../controlflow/graph/Nodes.expected | 2 + 12 files changed, 427 insertions(+), 12 deletions(-) diff --git a/ruby/ql/lib/codeql/ruby/ast/Constant.qll b/ruby/ql/lib/codeql/ruby/ast/Constant.qll index a6658fb63c0..187295d443b 100644 --- a/ruby/ql/lib/codeql/ruby/ast/Constant.qll +++ b/ruby/ql/lib/codeql/ruby/ast/Constant.qll @@ -252,6 +252,20 @@ private class ConstantReadAccessSynth extends ConstantAccess, TConstantReadAcces final override predicate hasGlobalScope() { value.matches("::%") } } +private class ConstantWriteAccessSynth extends ConstantAccess, TConstantWriteAccessSynth { + private string value; + + ConstantWriteAccessSynth() { this = TConstantWriteAccessSynth(_, _, value) } + + final override string getName() { + if this.hasGlobalScope() then result = value.suffix(2) else result = value + } + + final override Expr getScopeExpr() { synthChild(this, 0, result) } + + final override predicate hasGlobalScope() { value.matches("::%") } +} + /** * A use (read) of a constant. * @@ -323,7 +337,9 @@ class ConstantReadAccess extends ConstantAccess { */ class ConstantWriteAccess extends ConstantAccess { ConstantWriteAccess() { - explicitAssignmentNode(toGenerated(this), _) or this instanceof TNamespace + explicitAssignmentNode(toGenerated(this), _) or + this instanceof TNamespace or + this instanceof TConstantWriteAccessSynth } override string getAPrimaryQlClass() { result = "ConstantWriteAccess" } diff --git a/ruby/ql/lib/codeql/ruby/ast/Expr.qll b/ruby/ql/lib/codeql/ruby/ast/Expr.qll index 3468c733d0d..265190ca275 100644 --- a/ruby/ql/lib/codeql/ruby/ast/Expr.qll +++ b/ruby/ql/lib/codeql/ruby/ast/Expr.qll @@ -61,7 +61,7 @@ class ArgumentList extends Expr, TArgumentList { private class LhsExpr_ = TVariableAccess or TTokenConstantAccess or TScopeResolutionConstantAccess or TMethodCall or - TDestructuredLhsExpr; + TDestructuredLhsExpr or TConstantWriteAccessSynth; /** * A "left-hand-side" (LHS) expression. An `LhsExpr` can occur on the left-hand side of diff --git a/ruby/ql/lib/codeql/ruby/ast/internal/AST.qll b/ruby/ql/lib/codeql/ruby/ast/internal/AST.qll index 821656f1f5f..7b94df3b773 100644 --- a/ruby/ql/lib/codeql/ruby/ast/internal/AST.qll +++ b/ruby/ql/lib/codeql/ruby/ast/internal/AST.qll @@ -116,6 +116,9 @@ private module Cached { TConstantReadAccessSynth(Ast::AstNode parent, int i, string value) { mkSynthChild(ConstantReadAccessKind(value), parent, i) } or + TConstantWriteAccessSynth(Ast::AstNode parent, int i, string value) { + mkSynthChild(ConstantWriteAccessKind(value), parent, i) + } or TDefinedExpr(Ruby::Unary g) { g instanceof @ruby_unary_definedquestion } or TDelimitedSymbolLiteral(Ruby::DelimitedSymbol g) or TDestructuredLeftAssignment(Ruby::DestructuredLeftAssignment g) { @@ -373,12 +376,13 @@ private module Cached { class TAstNodeSynth = TAddExprSynth or TAssignExprSynth or TBitwiseAndExprSynth or TBitwiseOrExprSynth or TBitwiseXorExprSynth or TBraceBlockSynth or TClassVariableAccessSynth or - TConstantReadAccessSynth or TDivExprSynth or TExponentExprSynth or - TGlobalVariableAccessSynth or TIfSynth or TInstanceVariableAccessSynth or - TIntegerLiteralSynth or TLShiftExprSynth or TLocalVariableAccessSynth or - TLogicalAndExprSynth or TLogicalOrExprSynth or TMethodCallSynth or TModuloExprSynth or - TMulExprSynth or TNilLiteralSynth or TRShiftExprSynth or TRangeLiteralSynth or TSelfSynth or - TSimpleParameterSynth or TSplatExprSynth or TStmtSequenceSynth or TSubExprSynth; + TConstantReadAccessSynth or TConstantWriteAccessSynth or TDivExprSynth or + TExponentExprSynth or TGlobalVariableAccessSynth or TIfSynth or + TInstanceVariableAccessSynth or TIntegerLiteralSynth or TLShiftExprSynth or + TLocalVariableAccessSynth or TLogicalAndExprSynth or TLogicalOrExprSynth or + TMethodCallSynth or TModuloExprSynth or TMulExprSynth or TNilLiteralSynth or + TRShiftExprSynth or TRangeLiteralSynth or TSelfSynth or TSimpleParameterSynth or + TSplatExprSynth or TStmtSequenceSynth or TSubExprSynth; /** * Gets the underlying TreeSitter entity for a given AST node. This does not @@ -565,6 +569,8 @@ private module Cached { or result = TConstantReadAccessSynth(parent, i, _) or + result = TConstantWriteAccessSynth(parent, i, _) + or result = TDivExprSynth(parent, i) or result = TExponentExprSynth(parent, i) @@ -672,7 +678,8 @@ class TMethodCall = class TSuperCall = TTokenSuperCall or TRegularSuperCall; class TConstantAccess = - TTokenConstantAccess or TScopeResolutionConstantAccess or TNamespace or TConstantReadAccessSynth; + TTokenConstantAccess or TScopeResolutionConstantAccess or TNamespace or + TConstantReadAccessSynth or TConstantWriteAccessSynth; class TControlExpr = TConditionalExpr or TCaseExpr or TCaseMatch or TLoop; diff --git a/ruby/ql/lib/codeql/ruby/ast/internal/Synthesis.qll b/ruby/ql/lib/codeql/ruby/ast/internal/Synthesis.qll index 1ab4435a85a..43f89740908 100644 --- a/ruby/ql/lib/codeql/ruby/ast/internal/Synthesis.qll +++ b/ruby/ql/lib/codeql/ruby/ast/internal/Synthesis.qll @@ -42,7 +42,8 @@ newtype SynthKind = StmtSequenceKind() or SelfKind(SelfVariable v) or SubExprKind() or - ConstantReadAccessKind(string value) { any(Synthesis s).constantReadAccess(value) } + ConstantReadAccessKind(string value) { any(Synthesis s).constantReadAccess(value) } or + ConstantWriteAccessKind(string value) { any(Synthesis s).constantWriteAccess(value) } /** * An AST child. @@ -107,6 +108,11 @@ class Synthesis extends TSynthesis { */ predicate constantReadAccess(string name) { none() } + /** + * Holds if a constant write access of `name` is needed. + */ + predicate constantWriteAccess(string name) { none() } + /** * Holds if `n` should be excluded from `ControlFlowTree` in the CFG construction. */ @@ -493,6 +499,231 @@ private module AssignOperationDesugar { } } + /** + * An assignment operation where the left-hand side is a constant + * without scope expression, such as`FOO` or `::Foo`. + */ + private class ConstantAssignOperation extends AssignOperation { + string name; + + pragma[nomagic] + ConstantAssignOperation() { + name = + any(Ruby::Constant constant | TTokenConstantAccess(constant) = this.getLeftOperand()) + .getValue() + or + name = + "::" + + any(Ruby::Constant constant | + TScopeResolutionConstantAccess(any(Ruby::ScopeResolution g | not exists(g.getScope())), + constant) = this.getLeftOperand() + ).getValue() + } + + final string getName() { result = name } + } + + pragma[nomagic] + private predicate constantAssignOperationSynthesis(AstNode parent, int i, Child child) { + exists(ConstantAssignOperation cao | + parent = cao and + i = -1 and + child = SynthChild(AssignExprKind()) + or + exists(AstNode assign | assign = TAssignExprSynth(cao, -1) | + parent = assign and + i = 0 and + child = childRef(cao.getLeftOperand()) + or + parent = assign and + i = 1 and + child = SynthChild(getKind(cao)) + or + parent = getSynthChild(assign, 1) and + ( + i = 0 and + child = SynthChild(ConstantReadAccessKind(cao.getName())) + or + i = 1 and + child = childRef(cao.getRightOperand()) + ) + ) + ) + } + + /** + * ```rb + * FOO += y + * ``` + * + * desugars to + * + * ```rb + * FOO = FOO + y + * ``` + */ + private class ConstantAssignOperationSynthesis extends Synthesis { + final override predicate child(AstNode parent, int i, Child child) { + constantAssignOperationSynthesis(parent, i, child) + } + + final override predicate constantReadAccess(string name) { + name = any(ConstantAssignOperation o).getName() + } + + final override predicate location(AstNode n, Location l) { + exists(ConstantAssignOperation cao, BinaryOperation bo | + bo = cao.getDesugared().(AssignExpr).getRightOperand() + | + n = bo and + l = getAssignOperationLocation(cao) + or + n = bo.getLeftOperand() and + hasLocation(cao.getLeftOperand(), l) + ) + } + } + + /** + * An assignment operation where the left-hand side is a constant + * with scope expression, such as `expr::FOO`. + */ + private class ScopeResolutionAssignOperation extends AssignOperation { + string name; + Expr scope; + + pragma[nomagic] + ScopeResolutionAssignOperation() { + exists(Ruby::Constant constant, Ruby::ScopeResolution g | + TScopeResolutionConstantAccess(g, constant) = this.getLeftOperand() and + name = constant.getValue() and + toGenerated(scope) = g.getScope() + ) + } + + final string getName() { result = name } + + final Expr getScopeExpr() { result = scope } + } + + pragma[nomagic] + private predicate scopeResolutionAssignOperationSynthesis(AstNode parent, int i, Child child) { + exists(ScopeResolutionAssignOperation cao | + parent = cao and + i = -1 and + child = SynthChild(StmtSequenceKind()) + or + exists(AstNode stmts | stmts = TStmtSequenceSynth(cao, -1) | + parent = stmts and + i = 0 and + child = SynthChild(AssignExprKind()) + or + exists(AstNode assign | assign = TAssignExprSynth(stmts, 0) | + parent = assign and + i = 0 and + child = SynthChild(LocalVariableAccessSynthKind(TLocalVariableSynth(cao, 0))) + or + parent = assign and + i = 1 and + child = childRef(cao.getScopeExpr()) + ) + or + parent = stmts and + i = 1 and + child = SynthChild(AssignExprKind()) + or + exists(AstNode assign | assign = TAssignExprSynth(stmts, 1) | + parent = assign and + i = 0 and + child = SynthChild(ConstantWriteAccessKind(cao.getName())) + or + exists(AstNode cwa | cwa = TConstantWriteAccessSynth(assign, 0, cao.getName()) | + parent = cwa and + i = 0 and + child = SynthChild(LocalVariableAccessSynthKind(TLocalVariableSynth(cao, 0))) + ) + or + parent = assign and + i = 1 and + child = SynthChild(getKind(cao)) + or + exists(AstNode op | op = getSynthChild(assign, 1) | + parent = op and + i = 0 and + child = SynthChild(ConstantReadAccessKind(cao.getName())) + or + exists(AstNode cra | cra = TConstantReadAccessSynth(op, 0, cao.getName()) | + parent = cra and + i = 0 and + child = SynthChild(LocalVariableAccessSynthKind(TLocalVariableSynth(cao, 0))) + ) + or + parent = op and + i = 1 and + child = childRef(cao.getRightOperand()) + ) + ) + ) + ) + } + + /** + * ```rb + * expr::FOO += y + * ``` + * + * desugars to + * + * ```rb + * __synth__0 = expr + * __synth__0::FOO = _synth__0::FOO + y + * ``` + */ + private class ScopeResolutionAssignOperationSynthesis extends Synthesis { + final override predicate child(AstNode parent, int i, Child child) { + scopeResolutionAssignOperationSynthesis(parent, i, child) + } + + final override predicate constantReadAccess(string name) { + name = any(ScopeResolutionAssignOperation o).getName() + } + + final override predicate localVariable(AstNode n, int i) { + n instanceof ScopeResolutionAssignOperation and + i = 0 + } + + final override predicate constantWriteAccess(string name) { this.constantReadAccess(name) } + + final override predicate location(AstNode n, Location l) { + exists(ScopeResolutionAssignOperation cao, StmtSequence stmts | stmts = cao.getDesugared() | + n = stmts.getStmt(0) and + hasLocation(cao.getScopeExpr(), l) + or + exists(AssignExpr assign | assign = stmts.getStmt(1) | + n = assign and hasLocation(cao, l) + or + n = assign.getLeftOperand() and + hasLocation(cao.getLeftOperand(), l) + or + n = assign.getLeftOperand().(ConstantAccess).getScopeExpr() and + hasLocation(cao.getScopeExpr(), l) + or + exists(BinaryOperation bo | bo = assign.getRightOperand() | + n = bo and + l = getAssignOperationLocation(cao) + or + n = bo.getLeftOperand() and + hasLocation(cao.getLeftOperand(), l) + or + n = bo.getLeftOperand().(ConstantAccess).getScopeExpr() and + hasLocation(cao.getScopeExpr(), l) + ) + ) + ) + } + } + /** An assignment operation where the left-hand side is a method call. */ private class SetterAssignOperation extends AssignOperation { private MethodCall mc; diff --git a/ruby/ql/lib/codeql/ruby/controlflow/internal/ControlFlowGraphImpl.qll b/ruby/ql/lib/codeql/ruby/controlflow/internal/ControlFlowGraphImpl.qll index 03e919ec654..bdd67d2a693 100644 --- a/ruby/ql/lib/codeql/ruby/controlflow/internal/ControlFlowGraphImpl.qll +++ b/ruby/ql/lib/codeql/ruby/controlflow/internal/ControlFlowGraphImpl.qll @@ -890,7 +890,12 @@ module Trees { private class ConstantAccessTree extends PostOrderTree, ConstantAccess { ConstantAccessTree() { not this instanceof ClassDeclaration and - not this instanceof ModuleDeclaration + not this instanceof ModuleDeclaration and + // constant accesses with scope expression in compound assignments are desugared + not ( + this = any(AssignOperation op).getLeftOperand() and + exists(this.getScopeExpr()) + ) } final override predicate propagatesAbnormal(AstNode child) { child = this.getScopeExpr() } diff --git a/ruby/ql/test/library-tests/ast/AstDesugar.expected b/ruby/ql/test/library-tests/ast/AstDesugar.expected index 8be5246ab88..593b2e65233 100644 --- a/ruby/ql/test/library-tests/ast/AstDesugar.expected +++ b/ruby/ql/test/library-tests/ast/AstDesugar.expected @@ -865,6 +865,45 @@ operations/operations.rb: # 96| getAnOperand/getRightOperand: [MulExpr] ... * ... # 96| getAnOperand/getLeftOperand/getReceiver: [GlobalVariableAccess] $global_var # 96| getAnOperand/getArgument/getRightOperand: [IntegerLiteral] 6 +# 99| [AssignAddExpr] ... += ... +# 99| getDesugared: [AssignExpr] ... = ... +# 99| getAnOperand/getLeftOperand: [ConstantAssignment, ConstantReadAccess] CONSTANT2 +# 99| getAnOperand/getRightOperand: [AddExpr] ... + ... +# 99| getAnOperand/getLeftOperand/getReceiver: [ConstantReadAccess] CONSTANT2 +# 99| getAnOperand/getArgument/getRightOperand: [IntegerLiteral] 6 +# 100| [AssignLogicalOrExpr] ... ||= ... +# 100| getDesugared: [AssignExpr] ... = ... +# 100| getAnOperand/getLeftOperand: [ConstantAssignment, ConstantReadAccess] CONSTANT3 +# 100| getAnOperand/getRightOperand: [LogicalOrExpr] ... || ... +# 100| getAnOperand/getLeftOperand/getReceiver: [ConstantReadAccess] CONSTANT3 +# 100| getAnOperand/getArgument/getRightOperand: [IntegerLiteral] 7 +# 101| [AssignLogicalOrExpr] ... ||= ... +# 101| getDesugared: [StmtSequence] ... +# 101| getStmt: [AssignExpr] ... = ... +# 101| getAnOperand/getRightOperand: [ConstantReadAccess] Foo +# 101| getAnOperand/getLeftOperand: [LocalVariableAccess] __synth__0 +# 101| getStmt: [AssignExpr] ... = ... +# 101| getAnOperand/getLeftOperand: [ConstantAssignment] MemberConstant +# 101| getScopeExpr: [LocalVariableAccess] __synth__0 +# 101| getAnOperand/getRightOperand: [LogicalOrExpr] ... || ... +# 101| getAnOperand/getLeftOperand/getReceiver: [ConstantReadAccess] MemberConstant +# 101| getScopeExpr: [LocalVariableAccess] __synth__0 +# 101| getAnOperand/getArgument/getRightOperand: [IntegerLiteral] 8 +# 102| [AssignLogicalOrExpr] ... ||= ... +# 102| getDesugared: [StmtSequence] ... +# 102| getStmt: [AssignExpr] ... = ... +# 102| getAnOperand/getRightOperand: [MethodCall] call to bar +# 102| getReceiver: [MethodCall] call to foo +# 102| getReceiver: [SelfVariableAccess] self +# 102| getArgument: [IntegerLiteral] 1 +# 102| getAnOperand/getLeftOperand: [LocalVariableAccess] __synth__0 +# 102| getStmt: [AssignExpr] ... = ... +# 102| getAnOperand/getLeftOperand: [ConstantAssignment] OtherConstant +# 102| getScopeExpr: [LocalVariableAccess] __synth__0 +# 102| getAnOperand/getRightOperand: [LogicalOrExpr] ... || ... +# 102| getAnOperand/getLeftOperand/getReceiver: [ConstantReadAccess] OtherConstant +# 102| getScopeExpr: [LocalVariableAccess] __synth__0 +# 102| getAnOperand/getArgument/getRightOperand: [IntegerLiteral] 7 params/params.rb: # 8| [HashLiteral] {...} # 8| getDesugared: [MethodCall] call to [] diff --git a/ruby/ql/test/library-tests/ast/ValueText.expected b/ruby/ql/test/library-tests/ast/ValueText.expected index 28ae0c0a256..04b11bc6e4e 100644 --- a/ruby/ql/test/library-tests/ast/ValueText.expected +++ b/ruby/ql/test/library-tests/ast/ValueText.expected @@ -1771,6 +1771,11 @@ exprCfgNodeValue | operations/operations.rb:95:15:95:15 | 5 | 5 | int | | operations/operations.rb:96:16:96:16 | 6 | 6 | int | | operations/operations.rb:98:13:98:13 | 5 | 5 | int | +| operations/operations.rb:99:14:99:14 | 6 | 6 | int | +| operations/operations.rb:100:15:100:15 | 7 | 7 | int | +| operations/operations.rb:101:25:101:25 | 8 | 8 | int | +| operations/operations.rb:102:5:102:5 | 1 | 1 | int | +| operations/operations.rb:102:31:102:31 | 7 | 7 | int | | params/params.rb:41:46:41:46 | 7 | 7 | int | | params/params.rb:47:19:47:21 | :bar | :bar | symbol | | params/params.rb:47:24:47:24 | 2 | 2 | int | diff --git a/ruby/ql/test/library-tests/ast/operations/assignment.expected b/ruby/ql/test/library-tests/ast/operations/assignment.expected index 9b981daae18..9bbbad3294d 100644 --- a/ruby/ql/test/library-tests/ast/operations/assignment.expected +++ b/ruby/ql/test/library-tests/ast/operations/assignment.expected @@ -54,8 +54,14 @@ assignments | operations.rb:96:1:96:16 | ... = ... | = | operations.rb:96:1:96:11 | $global_var | operations.rb:96:13:96:14 | ... * ... | AssignExpr | | operations.rb:98:1:98:13 | ... = ... | = | operations.rb:98:1:98:9 | CONSTANT1 | operations.rb:98:13:98:13 | 5 | AssignExpr | | operations.rb:99:1:99:14 | ... += ... | += | operations.rb:99:1:99:9 | CONSTANT2 | operations.rb:99:14:99:14 | 6 | AssignAddExpr | +| operations.rb:99:1:99:14 | ... = ... | = | operations.rb:99:1:99:9 | CONSTANT2 | operations.rb:99:11:99:12 | ... + ... | AssignExpr | +| operations.rb:100:1:100:15 | ... = ... | = | operations.rb:100:1:100:9 | CONSTANT3 | operations.rb:100:11:100:13 | ... \|\| ... | AssignExpr | | operations.rb:100:1:100:15 | ... \|\|= ... | \|\|= | operations.rb:100:1:100:9 | CONSTANT3 | operations.rb:100:15:100:15 | 7 | AssignLogicalOrExpr | +| operations.rb:101:1:101:3 | ... = ... | = | operations.rb:101:1:101:3 | __synth__0 | operations.rb:101:1:101:3 | Foo | AssignExpr | +| operations.rb:101:1:101:25 | ... = ... | = | operations.rb:101:1:101:19 | MemberConstant | operations.rb:101:21:101:23 | ... \|\| ... | AssignExpr | | operations.rb:101:1:101:25 | ... \|\|= ... | \|\|= | operations.rb:101:1:101:19 | MemberConstant | operations.rb:101:25:101:25 | 8 | AssignLogicalOrExpr | +| operations.rb:102:1:102:10 | ... = ... | = | operations.rb:102:1:102:10 | __synth__0 | operations.rb:102:1:102:10 | call to bar | AssignExpr | +| operations.rb:102:1:102:31 | ... = ... | = | operations.rb:102:1:102:25 | OtherConstant | operations.rb:102:27:102:29 | ... \|\| ... | AssignExpr | | operations.rb:102:1:102:31 | ... \|\|= ... | \|\|= | operations.rb:102:1:102:25 | OtherConstant | operations.rb:102:31:102:31 | 7 | AssignLogicalOrExpr | assignOperations | operations.rb:69:1:69:8 | ... += ... | += | operations.rb:69:1:69:1 | x | operations.rb:69:6:69:8 | 128 | AssignAddExpr | diff --git a/ruby/ql/test/library-tests/ast/operations/binary.expected b/ruby/ql/test/library-tests/ast/operations/binary.expected index 236b0c3b574..0eb0be8bfcd 100644 --- a/ruby/ql/test/library-tests/ast/operations/binary.expected +++ b/ruby/ql/test/library-tests/ast/operations/binary.expected @@ -40,6 +40,10 @@ binaryOperations | operations.rb:89:6:89:7 | ... + ... | + | operations.rb:89:3:89:4 | @x | operations.rb:89:9:89:9 | 2 | AddExpr | | operations.rb:92:7:92:8 | ... / ... | / | operations.rb:92:3:92:5 | @@y | operations.rb:92:10:92:10 | 4 | DivExpr | | operations.rb:96:13:96:14 | ... * ... | * | operations.rb:96:1:96:11 | $global_var | operations.rb:96:16:96:16 | 6 | MulExpr | +| operations.rb:99:11:99:12 | ... + ... | + | operations.rb:99:1:99:9 | CONSTANT2 | operations.rb:99:14:99:14 | 6 | AddExpr | +| operations.rb:100:11:100:13 | ... \|\| ... | \|\| | operations.rb:100:1:100:9 | CONSTANT3 | operations.rb:100:15:100:15 | 7 | LogicalOrExpr | +| operations.rb:101:21:101:23 | ... \|\| ... | \|\| | operations.rb:101:1:101:19 | MemberConstant | operations.rb:101:25:101:25 | 8 | LogicalOrExpr | +| operations.rb:102:27:102:29 | ... \|\| ... | \|\| | operations.rb:102:1:102:25 | OtherConstant | operations.rb:102:31:102:31 | 7 | LogicalOrExpr | binaryArithmeticOperations | operations.rb:32:1:32:7 | ... + ... | + | operations.rb:32:1:32:1 | w | operations.rb:32:5:32:7 | 234 | AddExpr | | operations.rb:33:1:33:6 | ... - ... | - | operations.rb:33:1:33:1 | x | operations.rb:33:5:33:6 | 17 | SubExpr | @@ -56,6 +60,7 @@ binaryArithmeticOperations | operations.rb:89:6:89:7 | ... + ... | + | operations.rb:89:3:89:4 | @x | operations.rb:89:9:89:9 | 2 | AddExpr | | operations.rb:92:7:92:8 | ... / ... | / | operations.rb:92:3:92:5 | @@y | operations.rb:92:10:92:10 | 4 | DivExpr | | operations.rb:96:13:96:14 | ... * ... | * | operations.rb:96:1:96:11 | $global_var | operations.rb:96:16:96:16 | 6 | MulExpr | +| operations.rb:99:11:99:12 | ... + ... | + | operations.rb:99:1:99:9 | CONSTANT2 | operations.rb:99:14:99:14 | 6 | AddExpr | binaryLogicalOperations | operations.rb:40:1:40:10 | ... && ... | && | operations.rb:40:1:40:3 | foo | operations.rb:40:8:40:10 | bar | LogicalAndExpr | | operations.rb:41:1:41:11 | ... and ... | and | operations.rb:41:1:41:3 | baz | operations.rb:41:9:41:11 | qux | LogicalAndExpr | @@ -63,6 +68,9 @@ binaryLogicalOperations | operations.rb:43:1:43:6 | ... \|\| ... | \|\| | operations.rb:43:1:43:1 | x | operations.rb:43:6:43:6 | y | LogicalOrExpr | | operations.rb:77:4:77:6 | ... && ... | && | operations.rb:77:2:77:2 | x | operations.rb:77:8:77:8 | y | LogicalAndExpr | | operations.rb:78:4:78:6 | ... \|\| ... | \|\| | operations.rb:78:2:78:2 | a | operations.rb:78:8:78:8 | b | LogicalOrExpr | +| operations.rb:100:11:100:13 | ... \|\| ... | \|\| | operations.rb:100:1:100:9 | CONSTANT3 | operations.rb:100:15:100:15 | 7 | LogicalOrExpr | +| operations.rb:101:21:101:23 | ... \|\| ... | \|\| | operations.rb:101:1:101:19 | MemberConstant | operations.rb:101:25:101:25 | 8 | LogicalOrExpr | +| operations.rb:102:27:102:29 | ... \|\| ... | \|\| | operations.rb:102:1:102:25 | OtherConstant | operations.rb:102:31:102:31 | 7 | LogicalOrExpr | binaryBitwiseOperations | operations.rb:46:1:46:6 | ... << ... | << | operations.rb:46:1:46:1 | x | operations.rb:46:6:46:6 | 3 | LShiftExpr | | operations.rb:47:1:47:7 | ... >> ... | >> | operations.rb:47:1:47:1 | y | operations.rb:47:6:47:7 | 16 | RShiftExpr | diff --git a/ruby/ql/test/library-tests/ast/operations/operation.expected b/ruby/ql/test/library-tests/ast/operations/operation.expected index 6e230918a14..f0c1ca5b5b8 100644 --- a/ruby/ql/test/library-tests/ast/operations/operation.expected +++ b/ruby/ql/test/library-tests/ast/operations/operation.expected @@ -198,9 +198,29 @@ | operations.rb:98:1:98:13 | ... = ... | = | operations.rb:98:13:98:13 | 5 | AssignExpr | | operations.rb:99:1:99:14 | ... += ... | += | operations.rb:99:1:99:9 | CONSTANT2 | AssignAddExpr | | operations.rb:99:1:99:14 | ... += ... | += | operations.rb:99:14:99:14 | 6 | AssignAddExpr | +| operations.rb:99:1:99:14 | ... = ... | = | operations.rb:99:1:99:9 | CONSTANT2 | AssignExpr | +| operations.rb:99:1:99:14 | ... = ... | = | operations.rb:99:11:99:12 | ... + ... | AssignExpr | +| operations.rb:99:11:99:12 | ... + ... | + | operations.rb:99:1:99:9 | CONSTANT2 | AddExpr | +| operations.rb:99:11:99:12 | ... + ... | + | operations.rb:99:14:99:14 | 6 | AddExpr | +| operations.rb:100:1:100:15 | ... = ... | = | operations.rb:100:1:100:9 | CONSTANT3 | AssignExpr | +| operations.rb:100:1:100:15 | ... = ... | = | operations.rb:100:11:100:13 | ... \|\| ... | AssignExpr | | operations.rb:100:1:100:15 | ... \|\|= ... | \|\|= | operations.rb:100:1:100:9 | CONSTANT3 | AssignLogicalOrExpr | | operations.rb:100:1:100:15 | ... \|\|= ... | \|\|= | operations.rb:100:15:100:15 | 7 | AssignLogicalOrExpr | +| operations.rb:100:11:100:13 | ... \|\| ... | \|\| | operations.rb:100:1:100:9 | CONSTANT3 | LogicalOrExpr | +| operations.rb:100:11:100:13 | ... \|\| ... | \|\| | operations.rb:100:15:100:15 | 7 | LogicalOrExpr | +| operations.rb:101:1:101:3 | ... = ... | = | operations.rb:101:1:101:3 | Foo | AssignExpr | +| operations.rb:101:1:101:3 | ... = ... | = | operations.rb:101:1:101:3 | __synth__0 | AssignExpr | +| operations.rb:101:1:101:25 | ... = ... | = | operations.rb:101:1:101:19 | MemberConstant | AssignExpr | +| operations.rb:101:1:101:25 | ... = ... | = | operations.rb:101:21:101:23 | ... \|\| ... | AssignExpr | | operations.rb:101:1:101:25 | ... \|\|= ... | \|\|= | operations.rb:101:1:101:19 | MemberConstant | AssignLogicalOrExpr | | operations.rb:101:1:101:25 | ... \|\|= ... | \|\|= | operations.rb:101:25:101:25 | 8 | AssignLogicalOrExpr | +| operations.rb:101:21:101:23 | ... \|\| ... | \|\| | operations.rb:101:1:101:19 | MemberConstant | LogicalOrExpr | +| operations.rb:101:21:101:23 | ... \|\| ... | \|\| | operations.rb:101:25:101:25 | 8 | LogicalOrExpr | +| operations.rb:102:1:102:10 | ... = ... | = | operations.rb:102:1:102:10 | __synth__0 | AssignExpr | +| operations.rb:102:1:102:10 | ... = ... | = | operations.rb:102:1:102:10 | call to bar | AssignExpr | +| operations.rb:102:1:102:31 | ... = ... | = | operations.rb:102:1:102:25 | OtherConstant | AssignExpr | +| operations.rb:102:1:102:31 | ... = ... | = | operations.rb:102:27:102:29 | ... \|\| ... | AssignExpr | | operations.rb:102:1:102:31 | ... \|\|= ... | \|\|= | operations.rb:102:1:102:25 | OtherConstant | AssignLogicalOrExpr | | operations.rb:102:1:102:31 | ... \|\|= ... | \|\|= | operations.rb:102:31:102:31 | 7 | AssignLogicalOrExpr | +| operations.rb:102:27:102:29 | ... \|\| ... | \|\| | operations.rb:102:1:102:25 | OtherConstant | LogicalOrExpr | +| operations.rb:102:27:102:29 | ... \|\| ... | \|\| | operations.rb:102:31:102:31 | 7 | LogicalOrExpr | diff --git a/ruby/ql/test/library-tests/controlflow/graph/Cfg.expected b/ruby/ql/test/library-tests/controlflow/graph/Cfg.expected index 6933a62fefe..92f391d7d2e 100644 --- a/ruby/ql/test/library-tests/controlflow/graph/Cfg.expected +++ b/ruby/ql/test/library-tests/controlflow/graph/Cfg.expected @@ -3696,7 +3696,7 @@ cfg.rb: #-----| -> exit filter_nil # 207| filter_nil -#-----| -> exit cfg.rb (normal) +#-----| -> self # 207| list #-----| -> list @@ -3733,6 +3733,35 @@ cfg.rb: # 209| call to nil? #-----| -> exit do ... end (normal) +# 213| call to do_something +#-----| -> exit cfg.rb (normal) + +# 213| self +#-----| -> do ... end + +# 213| do ... end +#-----| -> call to do_something + +# 213| enter do ... end +#-----| -> self + +# 213| exit do ... end + +# 213| exit do ... end (normal) +#-----| -> exit do ... end + +# 214| self +#-----| -> call to something + +# 214| call to something +#-----| -> self + +# 215| call to something_else +#-----| -> exit do ... end (normal) + +# 215| self +#-----| -> call to something_else + constant_compound_assign.rb: # 1| Foo #-----| -> foo_before @@ -3740,7 +3769,54 @@ constant_compound_assign.rb: # 1| enter constant_compound_assign.rb #-----| -> Foo +# 1| exit constant_compound_assign.rb + +# 1| exit constant_compound_assign.rb (normal) +#-----| -> exit constant_compound_assign.rb + # 2| foo_before +#-----| -> FOO_CONSTANT + +# 5| FOO_CONSTANT +#-----| -> FOO_CONSTANT + +# 5| FOO_CONSTANT +#-----| true -> ... || ... +#-----| false -> 123 + +# 5| ... = ... +#-----| -> foo_after + +# 5| ... || ... +#-----| -> ... = ... + +# 5| 123 +#-----| -> ... || ... + +# 7| foo_after +#-----| -> top_before + +# 11| top_before +#-----| -> TOP_CONSTANT + +# 14| TOP_CONSTANT +#-----| -> TOP_CONSTANT + +# 14| TOP_CONSTANT +#-----| true -> ... || ... +#-----| false -> 123 + +# 14| ... = ... +#-----| -> top_after + +# 14| ... || ... +#-----| -> ... = ... + +# 14| 123 +#-----| -> ... || ... + +# 16| top_after +#-----| -> exit constant_compound_assign.rb (normal) desugar.rb: # 1| enter m1 diff --git a/ruby/ql/test/library-tests/controlflow/graph/Nodes.expected b/ruby/ql/test/library-tests/controlflow/graph/Nodes.expected index b929f1fe965..71be74f533f 100644 --- a/ruby/ql/test/library-tests/controlflow/graph/Nodes.expected +++ b/ruby/ql/test/library-tests/controlflow/graph/Nodes.expected @@ -212,6 +212,8 @@ positionalArguments | cfg.rb:205:1:205:23 | call to bar | cfg.rb:205:10:205:10 | 1 | | cfg.rb:205:1:205:23 | call to bar | cfg.rb:205:12:205:12 | 2 | | cfg.rb:205:4:205:5 | call to == | cfg.rb:205:1:205:3 | __synth__0__1 | +| constant_compound_assign.rb:5:18:5:20 | ... \|\| ... | constant_compound_assign.rb:5:22:5:24 | 123 | +| constant_compound_assign.rb:14:14:14:16 | ... \|\| ... | constant_compound_assign.rb:14:18:14:20 | 123 | | desugar.rb:2:5:2:6 | ... + ... | desugar.rb:2:8:2:8 | 1 | | desugar.rb:6:3:6:13 | call to count= | desugar.rb:6:17:6:17 | ... = ... | | desugar.rb:10:3:10:10 | call to []= | desugar.rb:10:9:10:9 | 0 | From b3855b089a0de1b0f55ee38aa57bf928e974ac7a Mon Sep 17 00:00:00 2001 From: Arthur Baars Date: Sat, 22 Oct 2022 14:15:29 +0200 Subject: [PATCH 4/4] Ruby: some more tests --- ruby/ql/test/library-tests/ast/Ast.expected | 3 +++ .../library-tests/ast/AstDesugar.expected | 6 ++++++ .../library-tests/ast/TreeSitter.expected | 6 ++++++ .../test/library-tests/ast/ValueText.expected | 2 ++ .../ast/operations/assignment.expected | 4 ++++ .../ast/operations/binary.expected | 2 ++ .../ast/operations/operation.expected | 6 ++++++ .../ast/operations/operations.rb | 1 + .../controlflow/graph/Cfg.expected | 19 +++++++++++++++++++ .../controlflow/graph/Nodes.expected | 1 + .../graph/constant_compound_assign.rb | 5 +++++ 11 files changed, 55 insertions(+) diff --git a/ruby/ql/test/library-tests/ast/Ast.expected b/ruby/ql/test/library-tests/ast/Ast.expected index 752969f8af2..6701f5ce160 100644 --- a/ruby/ql/test/library-tests/ast/Ast.expected +++ b/ruby/ql/test/library-tests/ast/Ast.expected @@ -2853,6 +2853,9 @@ operations/operations.rb: # 102| getReceiver: [SelfVariableAccess] self # 102| getArgument: [IntegerLiteral] 1 # 102| getAnOperand/getRightOperand: [IntegerLiteral] 7 +# 103| getStmt: [AssignLogicalOrExpr] ... ||= ... +# 103| getAnOperand/getLeftOperand: [ConstantAssignment, ConstantReadAccess] CONSTANT4 +# 103| getAnOperand/getRightOperand: [IntegerLiteral] 7 params/params.rb: # 1| [Toplevel] params.rb # 4| getStmt: [Method] identifier_method_params diff --git a/ruby/ql/test/library-tests/ast/AstDesugar.expected b/ruby/ql/test/library-tests/ast/AstDesugar.expected index 593b2e65233..45711cd2927 100644 --- a/ruby/ql/test/library-tests/ast/AstDesugar.expected +++ b/ruby/ql/test/library-tests/ast/AstDesugar.expected @@ -904,6 +904,12 @@ operations/operations.rb: # 102| getAnOperand/getLeftOperand/getReceiver: [ConstantReadAccess] OtherConstant # 102| getScopeExpr: [LocalVariableAccess] __synth__0 # 102| getAnOperand/getArgument/getRightOperand: [IntegerLiteral] 7 +# 103| [AssignLogicalOrExpr] ... ||= ... +# 103| getDesugared: [AssignExpr] ... = ... +# 103| getAnOperand/getLeftOperand: [ConstantAssignment, ConstantReadAccess] CONSTANT4 +# 103| getAnOperand/getRightOperand: [LogicalOrExpr] ... || ... +# 103| getAnOperand/getLeftOperand/getReceiver: [ConstantReadAccess] CONSTANT4 +# 103| getAnOperand/getArgument/getRightOperand: [IntegerLiteral] 7 params/params.rb: # 8| [HashLiteral] {...} # 8| getDesugared: [MethodCall] call to [] diff --git a/ruby/ql/test/library-tests/ast/TreeSitter.expected b/ruby/ql/test/library-tests/ast/TreeSitter.expected index 4f58b38143d..a0e32ce0aa4 100644 --- a/ruby/ql/test/library-tests/ast/TreeSitter.expected +++ b/ruby/ql/test/library-tests/ast/TreeSitter.expected @@ -5571,6 +5571,12 @@ operations/operations.rb: # 102| 2: [Constant] OtherConstant # 102| 1: [ReservedWord] ||= # 102| 2: [Integer] 7 +# 103| 71: [OperatorAssignment] OperatorAssignment +# 103| 0: [ScopeResolution] ScopeResolution +# 103| 0: [ReservedWord] :: +# 103| 1: [Constant] CONSTANT4 +# 103| 1: [ReservedWord] ||= +# 103| 2: [Integer] 7 # 1| [Comment] # Start with assignments to all the identifiers used below, so that they are # 2| [Comment] # interpreted as variables. # 22| [Comment] # Unary operations diff --git a/ruby/ql/test/library-tests/ast/ValueText.expected b/ruby/ql/test/library-tests/ast/ValueText.expected index 04b11bc6e4e..cec6e7ad3a3 100644 --- a/ruby/ql/test/library-tests/ast/ValueText.expected +++ b/ruby/ql/test/library-tests/ast/ValueText.expected @@ -894,6 +894,7 @@ exprValue | operations/operations.rb:101:25:101:25 | 8 | 8 | int | | operations/operations.rb:102:5:102:5 | 1 | 1 | int | | operations/operations.rb:102:31:102:31 | 7 | 7 | int | +| operations/operations.rb:103:17:103:17 | 7 | 7 | int | | params/params.rb:41:46:41:46 | 7 | 7 | int | | params/params.rb:47:19:47:21 | :bar | :bar | symbol | | params/params.rb:47:24:47:24 | 2 | 2 | int | @@ -1776,6 +1777,7 @@ exprCfgNodeValue | operations/operations.rb:101:25:101:25 | 8 | 8 | int | | operations/operations.rb:102:5:102:5 | 1 | 1 | int | | operations/operations.rb:102:31:102:31 | 7 | 7 | int | +| operations/operations.rb:103:17:103:17 | 7 | 7 | int | | params/params.rb:41:46:41:46 | 7 | 7 | int | | params/params.rb:47:19:47:21 | :bar | :bar | symbol | | params/params.rb:47:24:47:24 | 2 | 2 | int | diff --git a/ruby/ql/test/library-tests/ast/operations/assignment.expected b/ruby/ql/test/library-tests/ast/operations/assignment.expected index 9bbbad3294d..ef88dc1751a 100644 --- a/ruby/ql/test/library-tests/ast/operations/assignment.expected +++ b/ruby/ql/test/library-tests/ast/operations/assignment.expected @@ -63,6 +63,8 @@ assignments | operations.rb:102:1:102:10 | ... = ... | = | operations.rb:102:1:102:10 | __synth__0 | operations.rb:102:1:102:10 | call to bar | AssignExpr | | operations.rb:102:1:102:31 | ... = ... | = | operations.rb:102:1:102:25 | OtherConstant | operations.rb:102:27:102:29 | ... \|\| ... | AssignExpr | | operations.rb:102:1:102:31 | ... \|\|= ... | \|\|= | operations.rb:102:1:102:25 | OtherConstant | operations.rb:102:31:102:31 | 7 | AssignLogicalOrExpr | +| operations.rb:103:1:103:17 | ... = ... | = | operations.rb:103:1:103:11 | CONSTANT4 | operations.rb:103:13:103:15 | ... \|\| ... | AssignExpr | +| operations.rb:103:1:103:17 | ... \|\|= ... | \|\|= | operations.rb:103:1:103:11 | CONSTANT4 | operations.rb:103:17:103:17 | 7 | AssignLogicalOrExpr | assignOperations | operations.rb:69:1:69:8 | ... += ... | += | operations.rb:69:1:69:1 | x | operations.rb:69:6:69:8 | 128 | AssignAddExpr | | operations.rb:70:1:70:7 | ... -= ... | -= | operations.rb:70:1:70:1 | y | operations.rb:70:6:70:7 | 32 | AssignSubExpr | @@ -84,6 +86,7 @@ assignOperations | operations.rb:100:1:100:15 | ... \|\|= ... | \|\|= | operations.rb:100:1:100:9 | CONSTANT3 | operations.rb:100:15:100:15 | 7 | AssignLogicalOrExpr | | operations.rb:101:1:101:25 | ... \|\|= ... | \|\|= | operations.rb:101:1:101:19 | MemberConstant | operations.rb:101:25:101:25 | 8 | AssignLogicalOrExpr | | operations.rb:102:1:102:31 | ... \|\|= ... | \|\|= | operations.rb:102:1:102:25 | OtherConstant | operations.rb:102:31:102:31 | 7 | AssignLogicalOrExpr | +| operations.rb:103:1:103:17 | ... \|\|= ... | \|\|= | operations.rb:103:1:103:11 | CONSTANT4 | operations.rb:103:17:103:17 | 7 | AssignLogicalOrExpr | assignArithmeticOperations | operations.rb:69:1:69:8 | ... += ... | += | operations.rb:69:1:69:1 | x | operations.rb:69:6:69:8 | 128 | AssignAddExpr | | operations.rb:70:1:70:7 | ... -= ... | -= | operations.rb:70:1:70:1 | y | operations.rb:70:6:70:7 | 32 | AssignSubExpr | @@ -101,6 +104,7 @@ assignLogicalOperations | operations.rb:100:1:100:15 | ... \|\|= ... | \|\|= | operations.rb:100:1:100:9 | CONSTANT3 | operations.rb:100:15:100:15 | 7 | AssignLogicalOrExpr | | operations.rb:101:1:101:25 | ... \|\|= ... | \|\|= | operations.rb:101:1:101:19 | MemberConstant | operations.rb:101:25:101:25 | 8 | AssignLogicalOrExpr | | operations.rb:102:1:102:31 | ... \|\|= ... | \|\|= | operations.rb:102:1:102:25 | OtherConstant | operations.rb:102:31:102:31 | 7 | AssignLogicalOrExpr | +| operations.rb:103:1:103:17 | ... \|\|= ... | \|\|= | operations.rb:103:1:103:11 | CONSTANT4 | operations.rb:103:17:103:17 | 7 | AssignLogicalOrExpr | assignBitwiseOperations | operations.rb:81:2:81:8 | ... <<= ... | <<= | operations.rb:81:2:81:2 | x | operations.rb:81:8:81:8 | 2 | AssignLShiftExpr | | operations.rb:82:2:82:8 | ... >>= ... | >>= | operations.rb:82:2:82:2 | y | operations.rb:82:8:82:8 | 3 | AssignRShiftExpr | diff --git a/ruby/ql/test/library-tests/ast/operations/binary.expected b/ruby/ql/test/library-tests/ast/operations/binary.expected index 0eb0be8bfcd..e94a1dc7643 100644 --- a/ruby/ql/test/library-tests/ast/operations/binary.expected +++ b/ruby/ql/test/library-tests/ast/operations/binary.expected @@ -44,6 +44,7 @@ binaryOperations | operations.rb:100:11:100:13 | ... \|\| ... | \|\| | operations.rb:100:1:100:9 | CONSTANT3 | operations.rb:100:15:100:15 | 7 | LogicalOrExpr | | operations.rb:101:21:101:23 | ... \|\| ... | \|\| | operations.rb:101:1:101:19 | MemberConstant | operations.rb:101:25:101:25 | 8 | LogicalOrExpr | | operations.rb:102:27:102:29 | ... \|\| ... | \|\| | operations.rb:102:1:102:25 | OtherConstant | operations.rb:102:31:102:31 | 7 | LogicalOrExpr | +| operations.rb:103:13:103:15 | ... \|\| ... | \|\| | operations.rb:103:1:103:11 | CONSTANT4 | operations.rb:103:17:103:17 | 7 | LogicalOrExpr | binaryArithmeticOperations | operations.rb:32:1:32:7 | ... + ... | + | operations.rb:32:1:32:1 | w | operations.rb:32:5:32:7 | 234 | AddExpr | | operations.rb:33:1:33:6 | ... - ... | - | operations.rb:33:1:33:1 | x | operations.rb:33:5:33:6 | 17 | SubExpr | @@ -71,6 +72,7 @@ binaryLogicalOperations | operations.rb:100:11:100:13 | ... \|\| ... | \|\| | operations.rb:100:1:100:9 | CONSTANT3 | operations.rb:100:15:100:15 | 7 | LogicalOrExpr | | operations.rb:101:21:101:23 | ... \|\| ... | \|\| | operations.rb:101:1:101:19 | MemberConstant | operations.rb:101:25:101:25 | 8 | LogicalOrExpr | | operations.rb:102:27:102:29 | ... \|\| ... | \|\| | operations.rb:102:1:102:25 | OtherConstant | operations.rb:102:31:102:31 | 7 | LogicalOrExpr | +| operations.rb:103:13:103:15 | ... \|\| ... | \|\| | operations.rb:103:1:103:11 | CONSTANT4 | operations.rb:103:17:103:17 | 7 | LogicalOrExpr | binaryBitwiseOperations | operations.rb:46:1:46:6 | ... << ... | << | operations.rb:46:1:46:1 | x | operations.rb:46:6:46:6 | 3 | LShiftExpr | | operations.rb:47:1:47:7 | ... >> ... | >> | operations.rb:47:1:47:1 | y | operations.rb:47:6:47:7 | 16 | RShiftExpr | diff --git a/ruby/ql/test/library-tests/ast/operations/operation.expected b/ruby/ql/test/library-tests/ast/operations/operation.expected index f0c1ca5b5b8..0f74eb28fb3 100644 --- a/ruby/ql/test/library-tests/ast/operations/operation.expected +++ b/ruby/ql/test/library-tests/ast/operations/operation.expected @@ -224,3 +224,9 @@ | operations.rb:102:1:102:31 | ... \|\|= ... | \|\|= | operations.rb:102:31:102:31 | 7 | AssignLogicalOrExpr | | operations.rb:102:27:102:29 | ... \|\| ... | \|\| | operations.rb:102:1:102:25 | OtherConstant | LogicalOrExpr | | operations.rb:102:27:102:29 | ... \|\| ... | \|\| | operations.rb:102:31:102:31 | 7 | LogicalOrExpr | +| operations.rb:103:1:103:17 | ... = ... | = | operations.rb:103:1:103:11 | CONSTANT4 | AssignExpr | +| operations.rb:103:1:103:17 | ... = ... | = | operations.rb:103:13:103:15 | ... \|\| ... | AssignExpr | +| operations.rb:103:1:103:17 | ... \|\|= ... | \|\|= | operations.rb:103:1:103:11 | CONSTANT4 | AssignLogicalOrExpr | +| operations.rb:103:1:103:17 | ... \|\|= ... | \|\|= | operations.rb:103:17:103:17 | 7 | AssignLogicalOrExpr | +| operations.rb:103:13:103:15 | ... \|\| ... | \|\| | operations.rb:103:1:103:11 | CONSTANT4 | LogicalOrExpr | +| operations.rb:103:13:103:15 | ... \|\| ... | \|\| | operations.rb:103:17:103:17 | 7 | LogicalOrExpr | diff --git a/ruby/ql/test/library-tests/ast/operations/operations.rb b/ruby/ql/test/library-tests/ast/operations/operations.rb index 49a6210a16b..86fda3c7457 100644 --- a/ruby/ql/test/library-tests/ast/operations/operations.rb +++ b/ruby/ql/test/library-tests/ast/operations/operations.rb @@ -100,3 +100,4 @@ CONSTANT2 += 6 CONSTANT3 ||= 7 Foo::MemberConstant ||= 8 foo(1).bar::OtherConstant ||= 7 +::CONSTANT4 ||= 7 diff --git a/ruby/ql/test/library-tests/controlflow/graph/Cfg.expected b/ruby/ql/test/library-tests/controlflow/graph/Cfg.expected index 92f391d7d2e..9c26785029e 100644 --- a/ruby/ql/test/library-tests/controlflow/graph/Cfg.expected +++ b/ruby/ql/test/library-tests/controlflow/graph/Cfg.expected @@ -3816,6 +3816,25 @@ constant_compound_assign.rb: #-----| -> ... || ... # 16| top_after +#-----| -> TOP_CONSTANT2 + +# 19| TOP_CONSTANT2 +#-----| -> TOP_CONSTANT2 + +# 19| TOP_CONSTANT2 +#-----| true -> ... || ... +#-----| false -> 123 + +# 19| ... = ... +#-----| -> top_after2 + +# 19| ... || ... +#-----| -> ... = ... + +# 19| 123 +#-----| -> ... || ... + +# 21| top_after2 #-----| -> exit constant_compound_assign.rb (normal) desugar.rb: diff --git a/ruby/ql/test/library-tests/controlflow/graph/Nodes.expected b/ruby/ql/test/library-tests/controlflow/graph/Nodes.expected index 71be74f533f..b17584ce259 100644 --- a/ruby/ql/test/library-tests/controlflow/graph/Nodes.expected +++ b/ruby/ql/test/library-tests/controlflow/graph/Nodes.expected @@ -214,6 +214,7 @@ positionalArguments | cfg.rb:205:4:205:5 | call to == | cfg.rb:205:1:205:3 | __synth__0__1 | | constant_compound_assign.rb:5:18:5:20 | ... \|\| ... | constant_compound_assign.rb:5:22:5:24 | 123 | | constant_compound_assign.rb:14:14:14:16 | ... \|\| ... | constant_compound_assign.rb:14:18:14:20 | 123 | +| constant_compound_assign.rb:19:17:19:19 | ... \|\| ... | constant_compound_assign.rb:19:21:19:23 | 123 | | desugar.rb:2:5:2:6 | ... + ... | desugar.rb:2:8:2:8 | 1 | | desugar.rb:6:3:6:13 | call to count= | desugar.rb:6:17:6:17 | ... = ... | | desugar.rb:10:3:10:10 | call to []= | desugar.rb:10:9:10:9 | 0 | diff --git a/ruby/ql/test/library-tests/controlflow/graph/constant_compound_assign.rb b/ruby/ql/test/library-tests/controlflow/graph/constant_compound_assign.rb index dc9c3a34946..178dbbf1797 100644 --- a/ruby/ql/test/library-tests/controlflow/graph/constant_compound_assign.rb +++ b/ruby/ql/test/library-tests/controlflow/graph/constant_compound_assign.rb @@ -15,3 +15,8 @@ TOP_CONSTANT ||= 123 def top_after end + +::TOP_CONSTANT2 ||= 123 + +def top_after2 +end