diff --git a/joern-cli/frontends/rubysrc2cpg/src/main/scala/io/joern/rubysrc2cpg/astcreation/AstForExpressionsCreator.scala b/joern-cli/frontends/rubysrc2cpg/src/main/scala/io/joern/rubysrc2cpg/astcreation/AstForExpressionsCreator.scala index d5b50dfa43a8..7bed6c026a90 100644 --- a/joern-cli/frontends/rubysrc2cpg/src/main/scala/io/joern/rubysrc2cpg/astcreation/AstForExpressionsCreator.scala +++ b/joern-cli/frontends/rubysrc2cpg/src/main/scala/io/joern/rubysrc2cpg/astcreation/AstForExpressionsCreator.scala @@ -55,6 +55,7 @@ trait AstForExpressionsCreator(implicit withSchemaValidation: ValidationMode) { case node: SelfIdentifier => astForSelfIdentifier(node) case node: BreakStatement => astForBreakStatement(node) case node: StatementList => astForStatementList(node) + case node: ReturnExpression => astForReturnStatement(node) case node: DummyNode => Ast(node.node) case node: Unknown => astForUnknown(node) case x => diff --git a/joern-cli/frontends/rubysrc2cpg/src/main/scala/io/joern/rubysrc2cpg/astcreation/AstForFunctionsCreator.scala b/joern-cli/frontends/rubysrc2cpg/src/main/scala/io/joern/rubysrc2cpg/astcreation/AstForFunctionsCreator.scala index b76e859bb6be..66f7471991c7 100644 --- a/joern-cli/frontends/rubysrc2cpg/src/main/scala/io/joern/rubysrc2cpg/astcreation/AstForFunctionsCreator.scala +++ b/joern-cli/frontends/rubysrc2cpg/src/main/scala/io/joern/rubysrc2cpg/astcreation/AstForFunctionsCreator.scala @@ -127,7 +127,7 @@ trait AstForFunctionsCreator(implicit withSchemaValidation: ValidationMode) { th case Some(astParentTfn) => memberForMethod(method, Option(NodeTypes.TYPE_DECL), Option(astParentTfn)) case None => memberForMethod(method, scope.surroundingAstLabel, scope.surroundingScopeFullName) } - Ast(memberForMethod(method, scope.surroundingAstLabel, scope.surroundingScopeFullName)) + Ast(memberForMethod(method, Option(NodeTypes.TYPE_DECL), scope.surroundingScopeFullName)) } // For closures, we also want the method/type refs for upstream use val methodAst_ = { diff --git a/joern-cli/frontends/rubysrc2cpg/src/main/scala/io/joern/rubysrc2cpg/parser/RubyNodeCreator.scala b/joern-cli/frontends/rubysrc2cpg/src/main/scala/io/joern/rubysrc2cpg/parser/RubyNodeCreator.scala index ba6feaedc865..e3760aa69b9d 100644 --- a/joern-cli/frontends/rubysrc2cpg/src/main/scala/io/joern/rubysrc2cpg/parser/RubyNodeCreator.scala +++ b/joern-cli/frontends/rubysrc2cpg/src/main/scala/io/joern/rubysrc2cpg/parser/RubyNodeCreator.scala @@ -161,6 +161,10 @@ class RubyNodeCreator extends RubyParserBaseVisitor[RubyNode] { ReturnExpression(expressions)(ctx.toTextSpan) } + override def visitReturnWithoutArguments(ctx: RubyParser.ReturnWithoutArgumentsContext): RubyNode = { + ReturnExpression(Nil)(ctx.toTextSpan) + } + override def visitNumericLiteral(ctx: RubyParser.NumericLiteralContext): RubyNode = { if (ctx.hasSign) { UnaryExpression(ctx.sign.getText, visit(ctx.unsignedNumericLiteral()))(ctx.toTextSpan) diff --git a/joern-cli/frontends/rubysrc2cpg/src/test/scala/io/joern/rubysrc2cpg/querying/MethodReturnTests.scala b/joern-cli/frontends/rubysrc2cpg/src/test/scala/io/joern/rubysrc2cpg/querying/MethodReturnTests.scala index f051ebab3562..ff75ea7abcc5 100644 --- a/joern-cli/frontends/rubysrc2cpg/src/test/scala/io/joern/rubysrc2cpg/querying/MethodReturnTests.scala +++ b/joern-cli/frontends/rubysrc2cpg/src/test/scala/io/joern/rubysrc2cpg/querying/MethodReturnTests.scala @@ -439,4 +439,21 @@ class MethodReturnTests extends RubyCode2CpgFixture(withDataFlow = true) { } } + "a return in an expression position without arguments should generate a return node with no children" in { + val cpg = code(""" + |def foo + | return unless baz() + | bar() + |end + |""".stripMargin) + + inside(cpg.method.nameExact("foo").ast.isReturn.headOption) { + case Some(ret) => + ret.code shouldBe "return" + ret.astChildren.size shouldBe 0 + ret.astParent.astParent.code shouldBe "return unless baz()" + case None => fail(s"Expected at least one return node") + } + } + } diff --git a/joern-cli/frontends/rubysrc2cpg/src/test/scala/io/joern/rubysrc2cpg/querying/MethodTests.scala b/joern-cli/frontends/rubysrc2cpg/src/test/scala/io/joern/rubysrc2cpg/querying/MethodTests.scala index 4bf396e145a0..29bb42997b17 100644 --- a/joern-cli/frontends/rubysrc2cpg/src/test/scala/io/joern/rubysrc2cpg/querying/MethodTests.scala +++ b/joern-cli/frontends/rubysrc2cpg/src/test/scala/io/joern/rubysrc2cpg/querying/MethodTests.scala @@ -7,6 +7,8 @@ import io.shiftleft.codepropertygraph.generated.nodes.* import io.shiftleft.codepropertygraph.generated.{ControlStructureTypes, NodeTypes, Operators} import io.shiftleft.semanticcpg.language.* +import scala.util.{Failure, Success, Try} + class MethodTests extends RubyCode2CpgFixture { "`def f(x) = 1`" should { @@ -717,4 +719,17 @@ class MethodTests extends RubyCode2CpgFixture { case xs => fail(s"Expected one call to foo, got [${xs.code.mkString(",")}]") } } + + "a nested method declaration inside of a do-block should connect the member node to the bound type decl" in { + val cpg = code(""" + |foo do + | def bar + | end + |end + |""".stripMargin) + + val parentType = cpg.member("bar").typeDecl.head + parentType.isLambda should not be empty + parentType.methodBinding.methodFullName.head should endWith("0") + } }