Skip to content

Commit ef1f547

Browse files
author
David Baker Effendi
authored
[ruby] Rework Conditional Assignments (#5226)
The more semantically sound version of the current lowering checks for all "falsey" states of a variable instead of only the nil condition. Thus, the lowering is changed as follows: `aaa ||= bbb` becomes `aaa = bbb if !aaa` `aaa &&= bbb` becomes aaa = bbb if aaa`
1 parent 03980ff commit ef1f547

File tree

2 files changed

+19
-39
lines changed

2 files changed

+19
-39
lines changed

joern-cli/frontends/rubysrc2cpg/src/main/scala/io/joern/rubysrc2cpg/astcreation/AstCreatorHelper.scala

Lines changed: 11 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ import io.joern.rubysrc2cpg.astcreation.RubyIntermediateAst.{
88
MemberAccess,
99
RubyExpression,
1010
RubyFieldIdentifier,
11+
RubyIdentifier,
12+
SimpleIdentifier,
1113
SingleAssignment,
1214
StatementList,
1315
TextSpan,
@@ -160,41 +162,19 @@ trait AstCreatorHelper(implicit withSchemaValidation: ValidationMode) { this: As
160162
member
161163
}
162164

163-
/** Lowers the `||=` and `&&=` assignment operators to the respective `.nil?` checks
165+
/** Lowers the `||=` and `&&=` assignment operators to the respective conditional checks, e.g, `aaa ||= bbb` becomes
166+
* `aaa = bbb if !aaa` `aaa &&= bbb` becomes aaa = bbb if aaa`
164167
*/
165168
def lowerAssignmentOperator(lhs: RubyExpression, rhs: RubyExpression, op: String, span: TextSpan): RubyExpression &
166169
ControlFlowStatement = {
167-
val condition = nilCheckCondition(lhs, op, "nil?", span)
168-
val thenClause = nilCheckThenClause(lhs, rhs, span)
169-
nilCheckIfStatement(condition, thenClause, span)
170-
}
171-
172-
/** Generates the required `.nil?` check condition used in the lowering of `||=` and `&&=`
173-
*/
174-
private def nilCheckCondition(lhs: RubyExpression, op: String, memberName: String, span: TextSpan): RubyExpression = {
175-
val memberAccess =
176-
MemberAccess(lhs, op = ".", memberName = "nil?")(span.spanStart(s"${lhs.span.text}.nil?"))
177-
if op == "||=" then memberAccess
178-
else UnaryExpression(op = "!", expression = memberAccess)(span.spanStart(s"!${memberAccess.span.text}"))
179-
}
180-
181-
/** Generates the assignment and the `thenClause` used in the lowering of `||=` and `&&=`
182-
*/
183-
private def nilCheckThenClause(lhs: RubyExpression, rhs: RubyExpression, span: TextSpan): RubyExpression = {
184-
StatementList(List(SingleAssignment(lhs, "=", rhs)(span.spanStart(s"${lhs.span.text} = ${rhs.span.text}"))))(
185-
span.spanStart(s"${lhs.span.text} = ${rhs.span.text}")
186-
)
187-
}
188-
189-
/** Generates the if statement for the lowering of `||=` and `&&=`
190-
*/
191-
private def nilCheckIfStatement(
192-
condition: RubyExpression,
193-
thenClause: RubyExpression,
194-
span: TextSpan
195-
): RubyExpression & ControlFlowStatement = {
170+
val condition =
171+
if op == "||=" then UnaryExpression(op = "!", expression = lhs)(span.spanStart(s"!${lhs.span.text}"))
172+
else lhs
173+
val thenClause = StatementList(
174+
List(SingleAssignment(lhs, "=", rhs)(span.spanStart(s"${lhs.span.text} = ${rhs.span.text}")))
175+
)(span.spanStart(s"${lhs.span.text} = ${rhs.span.text}"))
196176
IfExpression(condition = condition, thenClause = thenClause, elsifClauses = List.empty, elseClause = None)(
197-
span.spanStart(s"if ${condition.span.text} then ${thenClause.span.text} end")
177+
span.spanStart(s"${thenClause.span.text} if ${condition.span.text}")
198178
)
199179
}
200180

joern-cli/frontends/rubysrc2cpg/src/test/scala/io/joern/rubysrc2cpg/querying/SingleAssignmentTests.scala

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -45,15 +45,15 @@ class SingleAssignmentTests extends RubyCode2CpgFixture {
4545

4646
"`||=` is represented by a lowered if call to .nil?" in {
4747
val cpg = code("""
48-
|def foo
48+
|def foo(x)
4949
| x ||= false
5050
|end
5151
|""".stripMargin)
5252

5353
inside(cpg.method.name("foo").controlStructure.l) {
5454
case ifStruct :: Nil =>
5555
ifStruct.controlStructureType shouldBe ControlStructureTypes.IF
56-
ifStruct.condition.code.l shouldBe List("x.nil?")
56+
ifStruct.condition.code.l shouldBe List("!x")
5757

5858
inside(ifStruct.whenTrue.ast.isCall.name(Operators.assignment).l) {
5959
case assignmentCall :: Nil =>
@@ -70,15 +70,15 @@ class SingleAssignmentTests extends RubyCode2CpgFixture {
7070

7171
"`&&=` is represented by lowered if call to .nil?" in {
7272
val cpg = code("""
73-
|def foo
73+
|def foo(x)
7474
| x &&= true
7575
|end
7676
|""".stripMargin)
7777

7878
inside(cpg.method.name("foo").controlStructure.l) {
7979
case ifStruct :: Nil =>
8080
ifStruct.controlStructureType shouldBe ControlStructureTypes.IF
81-
ifStruct.condition.code.l shouldBe List("!x.nil?")
81+
ifStruct.condition.code.l shouldBe List("x")
8282

8383
inside(ifStruct.whenTrue.ast.isCall.name(Operators.assignment).l) {
8484
case assignmentCall :: Nil =>
@@ -319,7 +319,7 @@ class SingleAssignmentTests extends RubyCode2CpgFixture {
319319
inside(cpg.method.name("foo").controlStructure.l) {
320320
case ifStruct :: Nil =>
321321
ifStruct.controlStructureType shouldBe ControlStructureTypes.IF
322-
ifStruct.condition.code.l shouldBe List("(<tmp-0> = hash[:id]).nil?")
322+
ifStruct.condition.code.l shouldBe List("!hash[:id]")
323323

324324
inside(ifStruct.whenTrue.ast.isCall.name(Operators.assignment).l) {
325325
case assignmentCall :: Nil =>
@@ -363,7 +363,7 @@ class SingleAssignmentTests extends RubyCode2CpgFixture {
363363
inside(cpg.method.name("foo").controlStructure.l) {
364364
case ifStruct :: Nil =>
365365
ifStruct.controlStructureType shouldBe ControlStructureTypes.IF
366-
ifStruct.condition.code.l shouldBe List("!hash[:id].nil?")
366+
ifStruct.condition.code.l shouldBe List("hash[:id]")
367367

368368
inside(ifStruct.whenTrue.ast.isCall.name(Operators.assignment).l) {
369369
case assignmentCall :: Nil =>
@@ -408,7 +408,7 @@ class SingleAssignmentTests extends RubyCode2CpgFixture {
408408
inside(cpg.method.name("foo").controlStructure.l) {
409409
case ifStruct :: Nil =>
410410
ifStruct.controlStructureType shouldBe ControlStructureTypes.IF
411-
ifStruct.condition.code.l shouldBe List("(<tmp-0> = A.B).nil?")
411+
ifStruct.condition.code.l shouldBe List("!A.B")
412412

413413
inside(ifStruct.whenTrue.ast.isCall.name(Operators.assignment).l) {
414414
case assignmentCall :: Nil =>
@@ -436,7 +436,7 @@ class SingleAssignmentTests extends RubyCode2CpgFixture {
436436
inside(cpg.method.name("foo").controlStructure.l) {
437437
case ifStruct :: Nil =>
438438
ifStruct.controlStructureType shouldBe ControlStructureTypes.IF
439-
ifStruct.condition.code.l shouldBe List("!A.B.nil?")
439+
ifStruct.condition.code.l shouldBe List("A.B")
440440

441441
inside(ifStruct.whenTrue.ast.isCall.name(Operators.assignment).l) {
442442
case assignmentCall :: Nil =>

0 commit comments

Comments
 (0)