Skip to content

Commit

Permalink
[ruby] BackRef and Multi-Assign Args (#5181)
Browse files Browse the repository at this point in the history
* Multiple assignments may be given as arguments to calls and such, thus they are now handled under `astForExpression` as a block containing the multiple assignments
* BackRef's from regexs are handled as `self.$&`
* Lowered the warning level for non-method nodes as access modifier arguments
  • Loading branch information
DavidBakerEffendi authored Dec 13, 2024
1 parent 498f895 commit 81d1b31
Show file tree
Hide file tree
Showing 5 changed files with 29 additions and 13 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ trait AstForExpressionsCreator(implicit withSchemaValidation: ValidationMode) {
case node: RubyCallWithBlock[_] => astForCallWithBlock(node)
case node: SelfIdentifier => astForSelfIdentifier(node)
case node: StatementList => astForStatementList(node)
case node: MultipleAssignment => blockAst(blockNode(node), astsForStatement(node).toList)
case node: ReturnExpression => astForReturnExpression(node)
case node: AccessModifier => astForSimpleIdentifier(node.toSimpleIdentifier)
case node: ArrayPattern => astForArrayPattern(node)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,10 @@ trait AstForFunctionsCreator(implicit withSchemaValidation: ValidationMode) { th

val methodAst = node.method match {
case m: ProcedureDeclaration => astsForStatement(m)
case x => logger.warn(s"Unhandled method reference from AST type ${x.getClass}"); Nil
case x =>
// Not sure how we should represent dynamically setting access modifiers based on method refs
logger.debug(s"Unhandled method reference from AST type ${x.getClass}")
Nil
}

popAccessModifier()
Expand Down
Original file line number Diff line number Diff line change
@@ -1,21 +1,14 @@
package io.joern.rubysrc2cpg.parser

import io.joern.rubysrc2cpg.astcreation.RubyIntermediateAst.{
AliasStatement,
AllowedTypeDeclarationChild,
ArrayLiteral,
ArrayParameter,
ClassFieldIdentifier,
ControlFlowStatement,
DefaultMultipleAssignment,
FieldsDeclaration,
ForExpression,
IfExpression,
MemberAccess,
MethodDeclaration,
ProcParameter,
ProcedureDeclaration,
RubyCall,
RubyExpression,
RubyFieldIdentifier,
SelfIdentifier,
Expand All @@ -31,10 +24,8 @@ import io.joern.rubysrc2cpg.astcreation.RubyIntermediateAst.{
TypeDeclBodyCall,
UnaryExpression
}
import io.joern.rubysrc2cpg.parser.RubyJsonHelpers.nilLiteral
import io.joern.rubysrc2cpg.passes.Defines
import io.joern.rubysrc2cpg.passes.Defines.getBuiltInType
import io.shiftleft.codepropertygraph.generated.nodes.Unknown
import org.slf4j.LoggerFactory
import upickle.core.*
import upickle.default.*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -232,7 +232,7 @@ class RubyJsonToNodeCreator(

private def visitArrayPatternWithTail(obj: Obj): RubyExpression = defaultResult(Option(obj.toTextSpan))

private def visitBackRef(obj: Obj): RubyExpression = defaultResult(Option(obj.toTextSpan))
private def visitBackRef(obj: Obj): RubyExpression = SimpleIdentifier()(obj.toTextSpan)

private def visitBegin(obj: Obj): RubyExpression = {
StatementList(obj.visitArray(ParserKeys.Body))(obj.toTextSpan)
Expand Down Expand Up @@ -495,7 +495,10 @@ class RubyJsonToNodeCreator(
ForExpression(forVariable, iterableVariable, doBlock)(obj.toTextSpan)
}

private def visitForwardArg(obj: Obj): RubyExpression = defaultResult(Option(obj.toTextSpan))
private def visitForwardArg(obj: Obj): RubyExpression = {
logger.warn("Forward arg unhandled")
defaultResult(Option(obj.toTextSpan))
}

// Note: Forward args should probably be handled more explicitly, but this should preserve flows if the same
// identifier is used in latter forwarding
Expand Down Expand Up @@ -1102,7 +1105,9 @@ class RubyJsonToNodeCreator(

private def visitTrue(obj: Obj): RubyExpression = StaticLiteral(getBuiltInType(Defines.TrueClass))(obj.toTextSpan)

private def visitUnDefine(obj: Obj): RubyExpression = defaultResult(Option(obj.toTextSpan))
private def visitUnDefine(obj: Obj): RubyExpression = {
defaultResult(Option(obj.toTextSpan))
}

private def visitUnlessExpression(obj: Obj): RubyExpression = {
defaultResult(Option(obj.toTextSpan))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import io.joern.x2cpg.Defines
import io.shiftleft.codepropertygraph.generated.Operators
import io.shiftleft.codepropertygraph.generated.nodes.*
import io.shiftleft.semanticcpg.language.*
import io.joern.rubysrc2cpg.passes.Defines as RubyDefines

class DoBlockTests extends RubyCode2CpgFixture {

Expand Down Expand Up @@ -529,4 +530,19 @@ class DoBlockTests extends RubyCode2CpgFixture {
case xs => fail(s"Expected three assignment calls, got [${xs.code.mkString(",")}]")
}
}

"a back reference in a do block should be a field access from `self`" in {
val cpg = code("""
|def bar()
| foo("something") { urls << $& }
|end
|""".stripMargin)
val backRefCall = cpg.method.isLambda.ast.fieldAccess
.and(_.fieldIdentifier.canonicalNameExact("$&"), _.argument(1).isIdentifier.nameExact(RubyDefines.Self))
.head
backRefCall.name shouldBe Operators.fieldAccess
backRefCall.code shouldBe "self.$&"
backRefCall.lineNumber shouldBe Option(3)
backRefCall.columnNumber shouldBe Option(29)
}
}

0 comments on commit 81d1b31

Please sign in to comment.