Repository: joernio/joern (scala)

Issue:
[Bug][rubysrc2cpg] - defining method with = sign not supported
**Describe the bug**
Getting parser error for below sample code

**To Reproduce**

```
    class SampleCls 
      def someConstant = "some message"
    end
```

This is the syntax supported since Ruby 3 - https://acuments.com/how-to-write-single-line-functions-in-ruby.html#:~:text=This%20allows%20programmers%20to%20write,(2%2C%202)%20%23%204

Interestingly this following lambda function sample gets parsed.

```
# Define a lambda that takes two arguments and returns their sum
add_lambda = ->(a, b) { a + b }

# Call the lambda and print the result
result = add_lambda.call(10, 20)
puts result # Output: 30
```

Pull request:
Closes #3187

Patch:
diff --git a/joern-cli/frontends/rubysrc2cpg/src/main/antlr4/io/joern/rubysrc2cpg/parser/RubyParser.g4 b/joern-cli/frontends/rubysrc2cpg/src/main/antlr4/io/joern/rubysrc2cpg/parser/RubyParser.g4
index 7fd99be15e6c..1bf2bb38cec8 100644
--- a/joern-cli/frontends/rubysrc2cpg/src/main/antlr4/io/joern/rubysrc2cpg/parser/RubyParser.g4
+++ b/joern-cli/frontends/rubysrc2cpg/src/main/antlr4/io/joern/rubysrc2cpg/parser/RubyParser.g4
@@ -279,8 +279,10 @@ association
 // --------------------------------------------------------
 
 methodDefinition
-    :   DEF wsOrNl* methodNamePart WS* methodParameterPart wsOrNl* bodyStatement wsOrNl* END
+    :   DEF wsOrNl* methodNamePart WS* methodParameterPart separator? wsOrNl* bodyStatement wsOrNl* END
+    |   DEF wsOrNl* methodNamePart WS* methodParameterPart WS* EQ wsOrNl* expression
     ;
+    
 
 procDefinition
     :   MINUSGT WS? (LPAREN parameters? RPAREN)? WS? block
@@ -324,7 +326,7 @@ methodOnlyIdentifier
 
 methodParameterPart
     :   LPAREN parameters? RPAREN
-    |   parameters? separator
+    |   parameters?
     ;
 
 parameters


Test patch:
diff --git a/joern-cli/frontends/rubysrc2cpg/src/test/scala/io/joern/rubysrc2cpg/parser/ClassDefinitionTests.scala b/joern-cli/frontends/rubysrc2cpg/src/test/scala/io/joern/rubysrc2cpg/parser/ClassDefinitionTests.scala
index 59a55056f881..e2c2b5b09010 100644
--- a/joern-cli/frontends/rubysrc2cpg/src/test/scala/io/joern/rubysrc2cpg/parser/ClassDefinitionTests.scala
+++ b/joern-cli/frontends/rubysrc2cpg/src/test/scala/io/joern/rubysrc2cpg/parser/ClassDefinitionTests.scala
@@ -73,8 +73,8 @@ class ClassDefinitionTests extends RubyParserAbstractTest {
             |             MethodIdentifier
             |              show
             |          MethodParameterPart
-            |           Separator
-            |            ;
+            |          Separator
+            |           ;
             |          WsOrNl
             |          BodyStatement
             |           CompoundStatement
diff --git a/joern-cli/frontends/rubysrc2cpg/src/test/scala/io/joern/rubysrc2cpg/parser/EnsureClauseTests.scala b/joern-cli/frontends/rubysrc2cpg/src/test/scala/io/joern/rubysrc2cpg/parser/EnsureClauseTests.scala
index 6d03e3637580..34c633255c29 100644
--- a/joern-cli/frontends/rubysrc2cpg/src/test/scala/io/joern/rubysrc2cpg/parser/EnsureClauseTests.scala
+++ b/joern-cli/frontends/rubysrc2cpg/src/test/scala/io/joern/rubysrc2cpg/parser/EnsureClauseTests.scala
@@ -22,7 +22,7 @@ class EnsureClauseTests extends RubyParserAbstractTest {
             |    MethodIdentifier
             |     refund
             | MethodParameterPart
-            |  Separator
+            | Separator
             | WsOrNl
             | BodyStatement
             |  CompoundStatement
diff --git a/joern-cli/frontends/rubysrc2cpg/src/test/scala/io/joern/rubysrc2cpg/parser/MethodDefinitionTests.scala b/joern-cli/frontends/rubysrc2cpg/src/test/scala/io/joern/rubysrc2cpg/parser/MethodDefinitionTests.scala
index 8e67136fdc6e..ce9ae6220988 100644
--- a/joern-cli/frontends/rubysrc2cpg/src/test/scala/io/joern/rubysrc2cpg/parser/MethodDefinitionTests.scala
+++ b/joern-cli/frontends/rubysrc2cpg/src/test/scala/io/joern/rubysrc2cpg/parser/MethodDefinitionTests.scala
@@ -19,8 +19,8 @@ class MethodDefinitionTests extends RubyParserAbstractTest {
             |     MethodIdentifier
             |      foo
             |  MethodParameterPart
-            |   Separator
-            |    ;
+            |  Separator
+            |   ;
             |  WsOrNl
             |  BodyStatement
             |   CompoundStatement
@@ -46,11 +46,10 @@ class MethodDefinitionTests extends RubyParserAbstractTest {
             |     MandatoryParameter
             |      x
             |   )
+            |  Separator
+            |   ;
             |  BodyStatement
             |   CompoundStatement
-            |    Separators
-            |     Separator
-            |      ;
             |  end""".stripMargin
       }
 
@@ -58,34 +57,33 @@ class MethodDefinitionTests extends RubyParserAbstractTest {
         val code = "def foo(x=1);end"
         printAst(_.primary(), code) shouldEqual
           """MethodDefinitionPrimary
-             | MethodDefinition
-             |  def
-             |  WsOrNl
-             |  SimpleMethodNamePart
-             |   DefinedMethodName
-             |    MethodName
-             |     MethodIdentifier
-             |      foo
-             |  MethodParameterPart
-             |   (
-             |   Parameters
-             |    Parameter
-             |     OptionalParameter
-             |      x
-             |      =
-             |      PrimaryExpression
-             |       LiteralPrimary
-             |        NumericLiteralLiteral
-             |         NumericLiteral
-             |          UnsignedNumericLiteral
-             |           1
-             |   )
-             |  BodyStatement
-             |   CompoundStatement
-             |    Separators
-             |     Separator
-             |      ;
-             |  end""".stripMargin
+            | MethodDefinition
+            |  def
+            |  WsOrNl
+            |  SimpleMethodNamePart
+            |   DefinedMethodName
+            |    MethodName
+            |     MethodIdentifier
+            |      foo
+            |  MethodParameterPart
+            |   (
+            |   Parameters
+            |    Parameter
+            |     OptionalParameter
+            |      x
+            |      =
+            |      PrimaryExpression
+            |       LiteralPrimary
+            |        NumericLiteralLiteral
+            |         NumericLiteral
+            |          UnsignedNumericLiteral
+            |           1
+            |   )
+            |  Separator
+            |   ;
+            |  BodyStatement
+            |   CompoundStatement
+            |  end""".stripMargin
       }
 
       "it contains two parameters, the last of which a &-parameter" in {
@@ -113,12 +111,11 @@ class MethodDefinitionTests extends RubyParserAbstractTest {
             |      &
             |      y
             |   )
+            |  Separator
+            |   ;
+            |  WsOrNl
             |  BodyStatement
             |   CompoundStatement
-            |    Separators
-            |     Separator
-            |      ;
-            |  WsOrNl
             |  end""".stripMargin
       }
 
@@ -126,93 +123,90 @@ class MethodDefinitionTests extends RubyParserAbstractTest {
         val code = "def foo(*arr); end"
         printAst(_.primary(), code) shouldEqual
           """MethodDefinitionPrimary
-          | MethodDefinition
-          |  def
-          |  WsOrNl
-          |  SimpleMethodNamePart
-          |   DefinedMethodName
-          |    MethodName
-          |     MethodIdentifier
-          |      foo
-          |  MethodParameterPart
-          |   (
-          |   Parameters
-          |    Parameter
-          |     ArrayParameter
-          |      *
-          |      arr
-          |   )
-          |  BodyStatement
-          |   CompoundStatement
-          |    Separators
-          |     Separator
-          |      ;
-          |  WsOrNl
-          |  end""".stripMargin
+            | MethodDefinition
+            |  def
+            |  WsOrNl
+            |  SimpleMethodNamePart
+            |   DefinedMethodName
+            |    MethodName
+            |     MethodIdentifier
+            |      foo
+            |  MethodParameterPart
+            |   (
+            |   Parameters
+            |    Parameter
+            |     ArrayParameter
+            |      *
+            |      arr
+            |   )
+            |  Separator
+            |   ;
+            |  WsOrNl
+            |  BodyStatement
+            |   CompoundStatement
+            |  end""".stripMargin
       }
 
       "it contains a named (hash) splatting argument" in {
         val code = "def foo(**hash); end"
         printAst(_.primary(), code) shouldEqual
           """MethodDefinitionPrimary
-          | MethodDefinition
-          |  def
-          |  WsOrNl
-          |  SimpleMethodNamePart
-          |   DefinedMethodName
-          |    MethodName
-          |     MethodIdentifier
-          |      foo
-          |  MethodParameterPart
-          |   (
-          |   Parameters
-          |    Parameter
-          |     HashParameter
-          |      **
-          |      hash
-          |   )
-          |  BodyStatement
-          |   CompoundStatement
-          |    Separators
-          |     Separator
-          |      ;
-          |  WsOrNl
-          |  end""".stripMargin
+            | MethodDefinition
+            |  def
+            |  WsOrNl
+            |  SimpleMethodNamePart
+            |   DefinedMethodName
+            |    MethodName
+            |     MethodIdentifier
+            |      foo
+            |  MethodParameterPart
+            |   (
+            |   Parameters
+            |    Parameter
+            |     HashParameter
+            |      **
+            |      hash
+            |   )
+            |  Separator
+            |   ;
+            |  WsOrNl
+            |  BodyStatement
+            |   CompoundStatement
+            |  end""".stripMargin
       }
 
       "it contains both a named array and hash splatting argument" in {
         val code = "def foo(*arr, **hash); end"
         printAst(_.primary(), code) shouldEqual
           """MethodDefinitionPrimary
-          | MethodDefinition
-          |  def
-          |  WsOrNl
-          |  SimpleMethodNamePart
-          |   DefinedMethodName
-          |    MethodName
-          |     MethodIdentifier
-          |      foo
-          |  MethodParameterPart
-          |   (
-          |   Parameters
-          |    Parameter
-          |     ArrayParameter
-          |      *
-          |      arr
-          |    ,
-          |    WsOrNl
-          |    Parameter
-          |     HashParameter
-          |      **
-          |      hash
-          |   )
-          |  BodyStatement
-          |   CompoundStatement
-          |    Separators
-          |     Separator
-          |      ;
-          |  WsOrNl
-          |  end""".stripMargin
+            | MethodDefinition
+            |  def
+            |  WsOrNl
+            |  SimpleMethodNamePart
+            |   DefinedMethodName
+            |    MethodName
+            |     MethodIdentifier
+            |      foo
+            |  MethodParameterPart
+            |   (
+            |   Parameters
+            |    Parameter
+            |     ArrayParameter
+            |      *
+            |      arr
+            |    ,
+            |    WsOrNl
+            |    Parameter
+            |     HashParameter
+            |      **
+            |      hash
+            |   )
+            |  Separator
+            |   ;
+            |  WsOrNl
+            |  BodyStatement
+            |   CompoundStatement
+            |  end""".stripMargin
       }
 
       "it contains an optional parameter before a mandatory one" in {
@@ -245,12 +239,11 @@ class MethodDefinitionTests extends RubyParserAbstractTest {
             |     MandatoryParameter
             |      y
             |   )
+            |  Separator
+            |   ;
+            |  WsOrNl
             |  BodyStatement
             |   CompoundStatement
-            |    Separators
-            |     Separator
-            |      ;
-            |  WsOrNl
             |  end""".stripMargin
       }
 
@@ -281,12 +274,11 @@ class MethodDefinitionTests extends RubyParserAbstractTest {
             |          UnsignedNumericLiteral
             |           1
             |   )
+            |  Separator
+            |   ;
+            |  WsOrNl
             |  BodyStatement
             |   CompoundStatement
-            |    Separators
-            |     Separator
-            |      ;
-            |  WsOrNl
             |  end""".stripMargin
       }
 
@@ -378,7 +370,7 @@ class MethodDefinitionTests extends RubyParserAbstractTest {
             |     MethodIdentifier
             |      foo
             |  MethodParameterPart
-            |   Separator
+            |  Separator
             |  WsOrNl
             |  BodyStatement
             |   CompoundStatement
@@ -426,9 +418,113 @@ class MethodDefinitionTests extends RubyParserAbstractTest {
 
   }
 
+  "An endless method definition" should {
+
+    "be parsed as a primary expression" when {
+
+      "it contains no arguments" in {
+        val code = "def foo = x"
+        printAst(_.primary(), code) shouldEqual
+          """MethodDefinitionPrimary
+            | MethodDefinition
+            |  def
+            |  WsOrNl
+            |  SimpleMethodNamePart
+            |   DefinedMethodName
+            |    MethodName
+            |     MethodIdentifier
+            |      foo
+            |  MethodParameterPart
+            |  =
+            |  WsOrNl
+            |  PrimaryExpression
+            |   VariableReferencePrimary
+            |    VariableIdentifierVariableReference
+            |     VariableIdentifier
+            |      x""".stripMargin
+      }
+
+      "it contains a line break right after `=`" in {
+        val code = "def foo =\n x"
+        printAst(_.primary(), code) shouldEqual
+          """MethodDefinitionPrimary
+            | MethodDefinition
+            |  def
+            |  WsOrNl
+            |  SimpleMethodNamePart
+            |   DefinedMethodName
+            |    MethodName
+            |     MethodIdentifier
+            |      foo
+            |  MethodParameterPart
+            |  =
+            |  WsOrNl
+            |  WsOrNl
+            |  PrimaryExpression
+            |   VariableReferencePrimary
+            |    VariableIdentifierVariableReference
+            |     VariableIdentifier
+            |      x""".stripMargin
+      }
+
+      "it contains no arguments and a string literal on the RHS" in {
+        val code = """def foo = "something""""
+        printAst(_.primary(), code) shouldEqual
+          """MethodDefinitionPrimary
+            | MethodDefinition
+            |  def
+            |  WsOrNl
+            |  SimpleMethodNamePart
+            |   DefinedMethodName
+            |    MethodName
+            |     MethodIdentifier
+            |      foo
+            |  MethodParameterPart
+            |  =
+            |  WsOrNl
+            |  PrimaryExpression
+            |   StringExpressionPrimary
+            |    SimpleStringExpression
+            |     DoubleQuotedStringLiteral
+            |      "
+            |      something
+            |      """".stripMargin
+      }
+
+      "it contains a single mandatory argument" in {
+        val code = "def id(x) = x"
+        printAst(_.primary(), code) shouldEqual
+          """MethodDefinitionPrimary
+            | MethodDefinition
+            |  def
+            |  WsOrNl
+            |  SimpleMethodNamePart
+            |   DefinedMethodName
+            |    MethodName
+            |     MethodIdentifier
+            |      id
+            |  MethodParameterPart
+            |   (
+            |   Parameters
+            |    Parameter
+            |     MandatoryParameter
+            |      x
+            |   )
+            |  =
+            |  WsOrNl
+            |  PrimaryExpression
+            |   VariableReferencePrimary
+            |    VariableIdentifierVariableReference
+            |     VariableIdentifier
+            |      x""".stripMargin
+      }
+    }
+  }
+
   "method definition with proc parameters" should {
     "have correct structure for proc parameters with name" in {
-      val code = """def foo(&block)
+      val code =
+        """def foo(&block)
           |   yield
           |end
           |""".stripMargin
@@ -451,7 +547,7 @@ class MethodDefinitionTests extends RubyParserAbstractTest {
           |      &
           |      block
           |   )
-          |  WsOrNl
+          |  Separator
           |  WsOrNl
           |  BodyStatement
           |   CompoundStatement
@@ -491,7 +587,7 @@ class MethodDefinitionTests extends RubyParserAbstractTest {
           |     ProcParameter
           |      &
           |   )
-          |  WsOrNl
+          |  Separator
           |  WsOrNl
           |  BodyStatement
           |   CompoundStatement
diff --git a/joern-cli/frontends/rubysrc2cpg/src/test/scala/io/joern/rubysrc2cpg/parser/RescueClauseTests.scala b/joern-cli/frontends/rubysrc2cpg/src/test/scala/io/joern/rubysrc2cpg/parser/RescueClauseTests.scala
index b14b2ea11123..ab00eaa8b04c 100644
--- a/joern-cli/frontends/rubysrc2cpg/src/test/scala/io/joern/rubysrc2cpg/parser/RescueClauseTests.scala
+++ b/joern-cli/frontends/rubysrc2cpg/src/test/scala/io/joern/rubysrc2cpg/parser/RescueClauseTests.scala
@@ -75,8 +75,8 @@ class RescueClauseTests extends RubyParserAbstractTest {
             |    MethodIdentifier
             |     foo
             | MethodParameterPart
-            |  Separator
-            |   ;
+            | Separator
+            |  ;
             | WsOrNl
             | BodyStatement
             |  CompoundStatement