Repository: joernio/joern (scala)

Issue:
[Bug][rubysrc2cpp] - code matching regular expression throwing error
**Describe the bug**
Below are some examples of regular expression checking codes that are throwing parser errors.


**To Reproduce**

1

```
 when /\/attachments/
    .... 
else
   ...
end
```

2.

```
def validate_each(record, attribute, value)
    unless /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\z/i.match?(value)
      record.errors.add attribute, (options[:message] || "is not a valid email")
    end
end
```

Pull request description:
Closes #3199

Patch:
diff --git a/joern-cli/frontends/rubysrc2cpg/src/main/scala/io/joern/rubysrc2cpg/parser/RubyLexerRegexHandling.scala b/joern-cli/frontends/rubysrc2cpg/src/main/scala/io/joern/rubysrc2cpg/parser/RubyLexerRegexHandling.scala
index ce05de2d3309..13eb991d7cb7 100644
--- a/joern-cli/frontends/rubysrc2cpg/src/main/scala/io/joern/rubysrc2cpg/parser/RubyLexerRegexHandling.scala
+++ b/joern-cli/frontends/rubysrc2cpg/src/main/scala/io/joern/rubysrc2cpg/parser/RubyLexerRegexHandling.scala
@@ -24,6 +24,8 @@ trait RubyLexerRegexHandling { this: RubyLexerBase =>
     COLON,
     // When '/' occurs after 'when'.
     WHEN,
+    // When '/' occurs after 'unless'.
+    UNLESS,
     // When '/' occurs after an operator.
     EMARK,
     EMARKEQ,


Test patch:
diff --git a/joern-cli/frontends/rubysrc2cpg/src/test/scala/io/joern/rubysrc2cpg/parser/RegexTests.scala b/joern-cli/frontends/rubysrc2cpg/src/test/scala/io/joern/rubysrc2cpg/parser/RegexTests.scala
index 6ac1c431452f..d9aaa1d1e3c0 100644
--- a/joern-cli/frontends/rubysrc2cpg/src/test/scala/io/joern/rubysrc2cpg/parser/RegexTests.scala
+++ b/joern-cli/frontends/rubysrc2cpg/src/test/scala/io/joern/rubysrc2cpg/parser/RegexTests.scala
@@ -163,6 +163,48 @@ class RegexTests extends RubyParserAbstractTest {
               |      Separator
               |  end""".stripMargin
       }
+
+      "used in a `unless` clause" should {
+        val code =
+          """unless /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\z/i.match?(value)
+            |end""".stripMargin
+
+        "be parsed as such" in {
+          printAst(_.primary(), code) shouldEqual
+            """UnlessExpressionPrimary
+              | UnlessExpression
+              |  unless
+              |  WsOrNl
+              |  ExpressionExpressionOrCommand
+              |   PrimaryExpression
+              |    ChainedInvocationPrimary
+              |     LiteralPrimary
+              |      RegularExpressionLiteral
+              |       /
+              |       \A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\z
+              |       /i
+              |     .
+              |     MethodName
+              |      MethodIdentifier
+              |       MethodOnlyIdentifier
+              |        match
+              |        ?
+              |     ArgsOnlyArgumentsWithParentheses
+              |      (
+              |      Arguments
+              |       ExpressionArgument
+              |        PrimaryExpression
+              |         VariableReferencePrimary
+              |          VariableIdentifierVariableReference
+              |           VariableIdentifier
+              |            value
+              |      )
+              |  ThenClause
+              |   Separator
+              |   CompoundStatement
+              |  end""".stripMargin
+        }
+      }
     }
   }
 
diff --git a/joern-cli/frontends/rubysrc2cpg/src/test/scala/io/joern/rubysrc2cpg/parser/RubyLexerTests.scala b/joern-cli/frontends/rubysrc2cpg/src/test/scala/io/joern/rubysrc2cpg/parser/RubyLexerTests.scala
index 6b9915928903..c42ee79a2d5d 100644
--- a/joern-cli/frontends/rubysrc2cpg/src/test/scala/io/joern/rubysrc2cpg/parser/RubyLexerTests.scala
+++ b/joern-cli/frontends/rubysrc2cpg/src/test/scala/io/joern/rubysrc2cpg/parser/RubyLexerTests.scala
@@ -328,6 +328,18 @@ class RubyLexerTests extends AnyFlatSpec with Matchers {
     )
   }
 
+  "Non-empty regex literal after `unless`" should "be recognized as such" in {
+    val code = "unless /^ch_/"
+    tokenize(code) shouldBe Seq(
+      UNLESS,
+      WS,
+      REGULAR_EXPRESSION_START,
+      REGULAR_EXPRESSION_BODY,
+      REGULAR_EXPRESSION_END,
+      EOF
+    )
+  }
+
   "Regex literals without metacharacters" should "be recognized as such" in {
     val eg = Seq("/regexp/", "/a regexp/")
     all(eg.map(tokenize)) shouldBe Seq(REGULAR_EXPRESSION_START, REGULAR_EXPRESSION_BODY, REGULAR_EXPRESSION_END, EOF)