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/Ast.expected b/ruby/ql/test/library-tests/ast/Ast.expected index 33229c64393..6701f5ce160 100644 --- a/ruby/ql/test/library-tests/ast/Ast.expected +++ b/ruby/ql/test/library-tests/ast/Ast.expected @@ -2833,6 +2833,29 @@ 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 +# 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 8be5246ab88..45711cd2927 100644 --- a/ruby/ql/test/library-tests/ast/AstDesugar.expected +++ b/ruby/ql/test/library-tests/ast/AstDesugar.expected @@ -865,6 +865,51 @@ 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 +# 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 839ad55fb38..a0e32ce0aa4 100644 --- a/ruby/ql/test/library-tests/ast/TreeSitter.expected +++ b/ruby/ql/test/library-tests/ast/TreeSitter.expected @@ -5537,6 +5537,46 @@ 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 +# 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 1492d619e33..cec6e7ad3a3 100644 --- a/ruby/ql/test/library-tests/ast/ValueText.expected +++ b/ruby/ql/test/library-tests/ast/ValueText.expected @@ -888,6 +888,13 @@ 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 | +| 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 | @@ -1764,6 +1771,13 @@ 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 | +| 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 | +| 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 7a536f183f7..ef88dc1751a 100644 --- a/ruby/ql/test/library-tests/ast/operations/assignment.expected +++ b/ruby/ql/test/library-tests/ast/operations/assignment.expected @@ -52,6 +52,19 @@ 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: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 | +| 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 | @@ -69,6 +82,11 @@ 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 | +| 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 | @@ -79,9 +97,14 @@ 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 | +| 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 236b0c3b574..e94a1dc7643 100644 --- a/ruby/ql/test/library-tests/ast/operations/binary.expected +++ b/ruby/ql/test/library-tests/ast/operations/binary.expected @@ -40,6 +40,11 @@ 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 | +| 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 | @@ -56,6 +61,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 +69,10 @@ 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 | +| 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 76dbae694be..0f74eb28fb3 100644 --- a/ruby/ql/test/library-tests/ast/operations/operation.expected +++ b/ruby/ql/test/library-tests/ast/operations/operation.expected @@ -194,3 +194,39 @@ | 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: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 | +| 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 fcbc4551bde..86fda3c7457 100644 --- a/ruby/ql/test/library-tests/ast/operations/operations.rb +++ b/ruby/ql/test/library-tests/ast/operations/operations.rb @@ -94,3 +94,10 @@ end $global_var = 5 $global_var *= 6 + +CONSTANT1 = 5 +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 2359eb2c24a..9c26785029e 100644 --- a/ruby/ql/test/library-tests/controlflow/graph/Cfg.expected +++ b/ruby/ql/test/library-tests/controlflow/graph/Cfg.expected @@ -3762,6 +3762,81 @@ cfg.rb: # 215| self #-----| -> call to something_else +constant_compound_assign.rb: +# 1| Foo +#-----| -> foo_before + +# 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 +#-----| -> 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: # 1| enter m1 #-----| -> x diff --git a/ruby/ql/test/library-tests/controlflow/graph/Nodes.expected b/ruby/ql/test/library-tests/controlflow/graph/Nodes.expected index b929f1fe965..b17584ce259 100644 --- a/ruby/ql/test/library-tests/controlflow/graph/Nodes.expected +++ b/ruby/ql/test/library-tests/controlflow/graph/Nodes.expected @@ -212,6 +212,9 @@ 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 | +| 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 new file mode 100644 index 00000000000..178dbbf1797 --- /dev/null +++ b/ruby/ql/test/library-tests/controlflow/graph/constant_compound_assign.rb @@ -0,0 +1,22 @@ +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 + +::TOP_CONSTANT2 ||= 123 + +def top_after2 +end