Skip to content

Commit

Permalink
[ruby] Alias Methods via Forwarding (#5111)
Browse files Browse the repository at this point in the history
Desugars aliased methods to the form
```ruby
def new_name(*args, &block)
  old_name(*args, &block)
end
```
Resolves #5110
  • Loading branch information
DavidBakerEffendi authored Nov 18, 2024
1 parent 958fdd3 commit 0fe79d1
Show file tree
Hide file tree
Showing 2 changed files with 25 additions and 25 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import io.joern.rubysrc2cpg.astcreation.RubyIntermediateAst.{
AliasStatement,
AllowedTypeDeclarationChild,
ArrayLiteral,
ArrayParameter,
ClassFieldIdentifier,
ControlFlowStatement,
DefaultMultipleAssignment,
Expand All @@ -12,6 +13,7 @@ import io.joern.rubysrc2cpg.astcreation.RubyIntermediateAst.{
IfExpression,
MemberAccess,
MethodDeclaration,
ProcParameter,
ProcedureDeclaration,
RubyCall,
RubyExpression,
Expand Down Expand Up @@ -354,29 +356,22 @@ object RubyJsonHelpers {
case x => List(x)
}

val methodParamMap = stmts.collect { case method: ProcedureDeclaration =>
method.methodName -> method.parameters
}.toMap

val transformedStmts = stmts.map {
case alias: AliasStatement if methodParamMap.contains(alias.oldName) =>
val aliasingMethodParams = methodParamMap(alias.oldName)
val argsCode = aliasingMethodParams.map(_.text).mkString(", ")
val callCode = s"${alias.oldName}($argsCode)"

val forwardingCall = SimpleCall(
SimpleIdentifier(None)(alias.span.spanStart(alias.oldName)),
aliasingMethodParams.map { x => SimpleIdentifier(None)(alias.span.spanStart(x.span.text)) }
)(alias.span.spanStart(callCode))
val aliasMethodBody = StatementList(forwardingCall :: Nil)(alias.span.spanStart(callCode))
MethodDeclaration(alias.newName, aliasingMethodParams, aliasMethodBody)(
alias.span.spanStart(s"def ${alias.newName}($argsCode)")
case alias: AliasStatement =>
val span = alias.span
val forwardingCallTarget = SimpleIdentifier(None)(span.spanStart(alias.oldName))
val forwardedArgs = SplattingRubyNode(SimpleIdentifier()(span.spanStart("args")))(span.spanStart("*args"))
val forwardedBlock = SimpleIdentifier()(span.spanStart("&block"))
val forwardingCall = SimpleCall(forwardingCallTarget, forwardedArgs :: forwardedBlock :: Nil)(
span.spanStart(s"${alias.oldName}(*args, &block)")
)

case alias: AliasStatement =>
logger.warn(s"Unable to correctly lower aliased method ${alias.oldName} (aliased method not found)")
val forwardingCall = SimpleCall(SimpleIdentifier(None)(alias.span.spanStart(alias.oldName)), Nil)(alias.span)
MethodDeclaration(alias.newName, Nil, StatementList(forwardingCall :: Nil)(alias.span))(alias.span)
val aliasMethodBody = StatementList(forwardingCall :: Nil)(forwardingCall.span)
val aliasingMethodParams =
ArrayParameter("*args")(span.spanStart("*args")) :: ProcParameter("&block")(span.spanStart("&block")) :: Nil
MethodDeclaration(alias.newName, aliasingMethodParams, aliasMethodBody)(
alias.span.spanStart(s"def ${alias.newName}(*args, &block)")
)
case expr => expr
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -323,18 +323,23 @@ class MethodTests extends RubyCode2CpgFixture {
x.name shouldBe "x"
bar.name shouldBe "bar="

xeq.parameter.name.l shouldBe bar.parameter.name.l
bar.parameter.name.l shouldBe List("self", "*args", "&block")
// bar forwards parameters to a call to the aliased method
inside(bar.call.name("x=").l) {
case barCall :: Nil =>
inside(barCall.argument.l) {
case _ :: (z: Identifier) :: Nil =>
z.name shouldBe "z"
z.argumentIndex shouldBe 1
case _ :: (args: Call) :: (blockId: Identifier) :: Nil =>
args.name shouldBe RubyOperators.splat
args.code shouldBe "*args"
args.argumentIndex shouldBe 1

blockId.name shouldBe "&block"
blockId.code shouldBe "&block"
blockId.argumentIndex shouldBe 2
case xs =>
fail(s"Expected a two arguments for the call `x=`, instead got [${xs.code.mkString(",")}]")
}
barCall.code shouldBe "x=(z)"
barCall.code shouldBe "x=(*args, &block)"
case xs => fail(s"Expected a single call to `bar=`, instead got [${xs.code.mkString(",")}]")
}
case xs => fail(s"Expected a three virtual methods under `Foo`, instead got [${xs.code.mkString(",")}]")
Expand Down

0 comments on commit 0fe79d1

Please sign in to comment.