Repository: joernio/joern (scala)

Issue:
[Bug] Ruby Parsing Issue: Regex delimiter
Describe the bug

Ruby parser fails for the below code
Please keep the conversation English! Make it easy for us and share your input code or a minified example.

To Reproduce
Load [this](https://github.com/lhpdev/books-api/blob/8a4d8f5f709382e4aca28d9a4b393c5f94701a2b/spec/controllers/api/books_controller_spec.rb#L19) regex delimiter code sample in the Ruby parser and see it failing.

Expected behavior
Parsing should pass

Screenshots
NA

Desktop (please complete the following information):
NA

Additional context
NA

Pull request:
The lexer predicate that decides if a `SLASH` token should start a regex literal was not taking into account colons (`:`), which is necessary to properly lex associations whose value are regex literals - e.g. `{x: /.*/}`.

Closes #3139

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 ced5580a413d..51efdb981e13 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
@@ -20,6 +20,8 @@ trait RubyLexerRegexHandling { this: RubyLexerBase =>
     NL,
     // When '/' occurs after a ','.
     COMMA,
+    // When '/' occurs after a ':'.
+    COLON,
     // When '/' occurs after an operator.
     EMARK,
     EMARKEQ,


Test patch:
diff --git a/joern-cli/frontends/rubysrc2cpg/src/test/scala/io/joern/rubysrc2cpg/parser/InvocationWithParenthesesTests.scala b/joern-cli/frontends/rubysrc2cpg/src/test/scala/io/joern/rubysrc2cpg/parser/InvocationWithParenthesesTests.scala
index 592580d4b3a0..0546453c3823 100644
--- a/joern-cli/frontends/rubysrc2cpg/src/test/scala/io/joern/rubysrc2cpg/parser/InvocationWithParenthesesTests.scala
+++ b/joern-cli/frontends/rubysrc2cpg/src/test/scala/io/joern/rubysrc2cpg/parser/InvocationWithParenthesesTests.scala
@@ -108,6 +108,33 @@ class InvocationWithParenthesesTests extends RubyParserAbstractTest {
              |  )""".stripMargin
       }
 
+      "it contains a non-empty regex literal keyword argument" in {
+        val code = "foo(id: /.*/)"
+        printAst(_.primary(), code) shouldEqual
+          """InvocationWithParenthesesPrimary
+            | MethodIdentifier
+            |  foo
+            | ArgsOnlyArgumentsWithParentheses
+            |  (
+            |  Arguments
+            |   AssociationArgument
+            |    Association
+            |     PrimaryExpression
+            |      VariableReferencePrimary
+            |       VariableIdentifierVariableReference
+            |        VariableIdentifier
+            |         id
+            |     :
+            |     WsOrNl
+            |     PrimaryExpression
+            |      LiteralPrimary
+            |       RegularExpressionLiteral
+            |        /
+            |        .*
+            |        /
+            |  )""".stripMargin
+      }
+
       "it contains a single symbol literal positional argument" in {
         val code = "foo(:region)"
 
@@ -207,6 +234,7 @@ class InvocationWithParenthesesTests extends RubyParserAbstractTest {
             |         true
             |  )""".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 0cc9ad2c0059..02acf223529e 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
@@ -258,6 +258,36 @@ class RubyLexerTests extends AnyFlatSpec with Matchers {
     )
   }
 
+  "Empty regex literal on the RHS of an association" should "be recognized as such" in {
+    val code = "{x: //}"
+    tokenize(code) shouldBe Seq(
+      LCURLY,
+      LOCAL_VARIABLE_IDENTIFIER,
+      COLON,
+      WS,
+      REGULAR_EXPRESSION_START,
+      REGULAR_EXPRESSION_END,
+      RCURLY,
+      EOF
+    )
+  }
+
+  "Non-empty regex literal on the RHS of a keyword argument" should "be recognized as such" in {
+    val code = "foo(x: /.*/)"
+    tokenize(code) shouldBe Seq(
+      LOCAL_VARIABLE_IDENTIFIER,
+      LPAREN,
+      LOCAL_VARIABLE_IDENTIFIER,
+      COLON,
+      WS,
+      REGULAR_EXPRESSION_START,
+      REGULAR_EXPRESSION_BODY,
+      REGULAR_EXPRESSION_END,
+      RPAREN,
+      EOF
+    )
+  }
+
   "Non-empty regex literal on the RHS of an assignment" should "be recognized as such" in {
     val code = """NAME_REGEX = /\A[^0-9!\``@#\$%\^&*+_=]+\z/"""
     tokenize(code) shouldBe Seq(