From 56ec924bd43548b373eccea9df6baeb0437978ab Mon Sep 17 00:00:00 2001 From: David Baker Effendi Date: Wed, 2 Oct 2024 16:07:05 +0200 Subject: [PATCH 1/2] [ruby] Implement `hashCode` for `RubyExpression` `RubyExpression` nodes don't inherently consider the `span` in the calculation of its hash, so when put into any hashed context, nodes that only rely on `span` alone will collide in these contexts. --- .../astcreation/AstForStatementsCreator.scala | 3 +-- .../rubysrc2cpg/astcreation/RubyIntermediateAst.scala | 5 +++-- .../io/joern/rubysrc2cpg/querying/MethodTests.scala | 10 ++++++++++ 3 files changed, 14 insertions(+), 4 deletions(-) diff --git a/joern-cli/frontends/rubysrc2cpg/src/main/scala/io/joern/rubysrc2cpg/astcreation/AstForStatementsCreator.scala b/joern-cli/frontends/rubysrc2cpg/src/main/scala/io/joern/rubysrc2cpg/astcreation/AstForStatementsCreator.scala index 33c4c3f96059..c1d571775f74 100644 --- a/joern-cli/frontends/rubysrc2cpg/src/main/scala/io/joern/rubysrc2cpg/astcreation/AstForStatementsCreator.scala +++ b/joern-cli/frontends/rubysrc2cpg/src/main/scala/io/joern/rubysrc2cpg/astcreation/AstForStatementsCreator.scala @@ -91,12 +91,11 @@ trait AstForStatementsCreator(implicit withSchemaValidation: ValidationMode) { t } protected def astForDoBlock(block: Block & RubyExpression): Seq[Ast] = { - // Create closure structures: [MethodDecl, TypeRef, MethodRef] if (closureToRefs.contains(block)) { closureToRefs(block).map(x => Ast(x.copy)) } else { val methodName = nextClosureName() - + // Create closure structures: [TypeRef, MethodRef] val methodRefAsts = block.body match { case x: Block => astForMethodDeclaration(x.toMethodDeclaration(methodName, Option(block.parameters)), isClosure = true) diff --git a/joern-cli/frontends/rubysrc2cpg/src/main/scala/io/joern/rubysrc2cpg/astcreation/RubyIntermediateAst.scala b/joern-cli/frontends/rubysrc2cpg/src/main/scala/io/joern/rubysrc2cpg/astcreation/RubyIntermediateAst.scala index a9d14945041e..9d8f244b9331 100644 --- a/joern-cli/frontends/rubysrc2cpg/src/main/scala/io/joern/rubysrc2cpg/astcreation/RubyIntermediateAst.scala +++ b/joern-cli/frontends/rubysrc2cpg/src/main/scala/io/joern/rubysrc2cpg/astcreation/RubyIntermediateAst.scala @@ -1,10 +1,9 @@ package io.joern.rubysrc2cpg.astcreation -import io.joern.rubysrc2cpg.astcreation.RubyIntermediateAst.{AllowedTypeDeclarationChild, RubyStatement} import io.joern.rubysrc2cpg.passes.{Defines, GlobalTypes} import io.shiftleft.codepropertygraph.generated.nodes.NewNode -import scala.annotation.tailrec +import java.util.Objects object RubyIntermediateAst { @@ -33,6 +32,8 @@ object RubyIntermediateAst { def offset: Option[(Int, Int)] = span.offset def text: String = span.text + + override def hashCode(): Int = Objects.hash(span) } /** Ruby statements evaluate to some value (and thus are expressions), but also perform some operation, e.g., 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 e3c35ce9c92f..ee887bcce687 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 @@ -1000,5 +1000,15 @@ class MethodTests extends RubyCode2CpgFixture { cpg.typeRef.typeFullName(".*Proc").size shouldBe 5 cpg.typeRef.whereNot(_.astParent).size shouldBe 0 } + + "resolve cached lambdas correctly" in { + def getLineNumberOfLambdaForCall(callName: String) = + cpg.call.nameExact(callName).argument.isTypeRef.typ.referencedTypeDecl.lineNumber.head + + getLineNumberOfLambdaForCall("with_index") shouldBe 3 + getLineNumberOfLambdaForCall("sort_by") shouldBe 4 + getLineNumberOfLambdaForCall("reject") shouldBe 5 + getLineNumberOfLambdaForCall("map") shouldBe 6 + } } } From dfac92b435eecd40a412c161c4856223e5d06e62 Mon Sep 17 00:00:00 2001 From: David Baker Effendi Date: Wed, 2 Oct 2024 16:57:10 +0200 Subject: [PATCH 2/2] Review changes --- .../rubysrc2cpg/astcreation/RubyIntermediateAst.scala | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/joern-cli/frontends/rubysrc2cpg/src/main/scala/io/joern/rubysrc2cpg/astcreation/RubyIntermediateAst.scala b/joern-cli/frontends/rubysrc2cpg/src/main/scala/io/joern/rubysrc2cpg/astcreation/RubyIntermediateAst.scala index 9d8f244b9331..231b91910b91 100644 --- a/joern-cli/frontends/rubysrc2cpg/src/main/scala/io/joern/rubysrc2cpg/astcreation/RubyIntermediateAst.scala +++ b/joern-cli/frontends/rubysrc2cpg/src/main/scala/io/joern/rubysrc2cpg/astcreation/RubyIntermediateAst.scala @@ -34,6 +34,13 @@ object RubyIntermediateAst { def text: String = span.text override def hashCode(): Int = Objects.hash(span) + + override def equals(obj: Any): Boolean = { + obj match { + case o: RubyExpression => o.span == span + case _ => false + } + } } /** Ruby statements evaluate to some value (and thus are expressions), but also perform some operation, e.g.,