From b84fb87b58d89d981bc75991195194c1f7b944d5 Mon Sep 17 00:00:00 2001 From: Xavier Pinho Date: Sun, 3 Dec 2023 15:24:05 +0000 Subject: [PATCH] [rubysrc2cpg] support `%()` literals (#3880) --- .../parser/AntlrContextHelpers.scala | 10 +++++++++ .../rubysrc2cpg/parser/RubyNodeCreator.scala | 8 +++++++ .../rubysrc2cpg/querying/LiteralTests.scala | 22 +++++++++++++++++++ 3 files changed, 40 insertions(+) diff --git a/joern-cli/frontends/rubysrc2cpg/src/main/scala/io/joern/rubysrc2cpg/parser/AntlrContextHelpers.scala b/joern-cli/frontends/rubysrc2cpg/src/main/scala/io/joern/rubysrc2cpg/parser/AntlrContextHelpers.scala index 87cef91cb2f4..64fc475b8ff1 100644 --- a/joern-cli/frontends/rubysrc2cpg/src/main/scala/io/joern/rubysrc2cpg/parser/AntlrContextHelpers.scala +++ b/joern-cli/frontends/rubysrc2cpg/src/main/scala/io/joern/rubysrc2cpg/parser/AntlrContextHelpers.scala @@ -48,6 +48,16 @@ object AntlrContextHelpers { def isInterpolated: Boolean = interpolations.nonEmpty } + sealed implicit class QuotedExpandedStringLiteralContextHelper(ctx: QuotedExpandedStringLiteralContext) { + def interpolations: List[ParserRuleContext] = ctx + .quotedExpandedLiteralStringContent() + .asScala + .filter(ctx => Option(ctx.compoundStatement()).isDefined) + .map(ctx => ctx.compoundStatement()) + .toList + def isInterpolated: Boolean = interpolations.nonEmpty + } + sealed implicit class DoubleQuotedStringExpressionContextHelper(ctx: DoubleQuotedStringExpressionContext) { def interpolations: List[ParserRuleContext] = ctx.doubleQuotedString().interpolations ++ ctx .singleOrDoubleQuotedString() 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 8ce4ece6fb67..a39423d52968 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 @@ -267,6 +267,14 @@ class RubyNodeCreator extends RubyParserBaseVisitor[RubyNode] { } } + override def visitQuotedExpandedStringLiteral(ctx: RubyParser.QuotedExpandedStringLiteralContext): RubyNode = { + if (!ctx.isInterpolated) { + StaticLiteral(getBuiltInType(Defines.String))(ctx.toTextSpan) + } else { + DynamicLiteral(getBuiltInType(Defines.String), ctx.interpolations.map(visit))(ctx.toTextSpan) + } + } + override def visitRegularExpressionLiteral(ctx: RubyParser.RegularExpressionLiteralContext): RubyNode = { if (ctx.isStatic) { StaticLiteral(getBuiltInType(Defines.Regexp))(ctx.toTextSpan) diff --git a/joern-cli/frontends/rubysrc2cpg/src/test/scala/io/joern/rubysrc2cpg/querying/LiteralTests.scala b/joern-cli/frontends/rubysrc2cpg/src/test/scala/io/joern/rubysrc2cpg/querying/LiteralTests.scala index 8454c385936b..701c6bf666b2 100644 --- a/joern-cli/frontends/rubysrc2cpg/src/test/scala/io/joern/rubysrc2cpg/querying/LiteralTests.scala +++ b/joern-cli/frontends/rubysrc2cpg/src/test/scala/io/joern/rubysrc2cpg/querying/LiteralTests.scala @@ -148,6 +148,28 @@ class LiteralTests extends RubyCode2CpgFixture { literal.typeFullName shouldBe "__builtin.String" } + "`%Q(hello world)` is represented by a LITERAL node" in { + val cpg = code(""" + |%Q(hello world) + |""".stripMargin) + + val List(literal) = cpg.literal.l + literal.code shouldBe "%Q(hello world)" + literal.lineNumber shouldBe Some(2) + literal.typeFullName shouldBe "__builtin.String" + } + + "`%(foo \"bar\" baz)` is represented by a LITERAL node" in { + val cpg = code(""" + |%(foo "bar" baz) + |""".stripMargin) + + val List(literal) = cpg.literal.l + literal.code shouldBe "%(foo \"bar\" baz)" + literal.lineNumber shouldBe Some(2) + literal.typeFullName shouldBe "__builtin.String" + } + """`%q<\n...\n>` is represented by a LITERAL node""" in { val cpg = code(""" |%q<