Skip to content

Commit

Permalink
[swiftsrc2cpg] Added support for Switch Expression (#4033)
Browse files Browse the repository at this point in the history
  • Loading branch information
max-leuthaeuser authored Jan 10, 2024
1 parent abbb77f commit 0fd532f
Show file tree
Hide file tree
Showing 6 changed files with 315 additions and 101 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,13 @@ trait AstForExprSyntaxCreator(implicit withSchemaValidation: ValidationMode) {
}
}

private def astForDiscardAssignmentExprSyntax(node: DiscardAssignmentExprSyntax): Ast = notHandledYet(node)
private def astForDiscardAssignmentExprSyntax(node: DiscardAssignmentExprSyntax): Ast = {
val name = generateUnusedVariableName(usedVariableNames, "wildcard")
val idNode = identifierNode(node, name)
scope.addVariableReference(name, idNode)
Ast(idNode)
}

private def astForDoExprSyntax(node: DoExprSyntax): Ast = notHandledYet(node)
private def astForEditorPlaceholderExprSyntax(node: EditorPlaceholderExprSyntax): Ast = notHandledYet(node)

Expand Down Expand Up @@ -200,6 +206,7 @@ trait AstForExprSyntaxCreator(implicit withSchemaValidation: ValidationMode) {
case "*" => Operators.multiplication
case "..<" | ">.." | "..." => Operators.range
case "%" => Operators.modulo
case "!" => Operators.logicalNot
case other =>
logger.warn(s"Unknown assignment operator: '$other'")
Operators.assignment
Expand Down Expand Up @@ -276,7 +283,81 @@ trait AstForExprSyntaxCreator(implicit withSchemaValidation: ValidationMode) {
Ast(identifierNode(node, "super"))
}

private def astForSwitchExprSyntax(node: SwitchExprSyntax): Ast = notHandledYet(node)
private def astsForSwitchCase(switchCase: SwitchCaseSyntax | IfConfigDeclSyntax): List[Ast] = {
val labelAst = Ast(createJumpTarget(switchCase))
val (testAsts, consequentAsts) = switchCase match {
case s: SwitchCaseSyntax =>
val (tAsts, flowAst) = s.label match {
case i: SwitchCaseLabelSyntax =>
val children = i.caseItems.children
val childrenTestAsts = children.map(c => astForNodeWithFunctionReference(c.pattern))
val childrenFlowAsts = children.collect {
case child if child.whereClause.isDefined =>
val whereClause = child.whereClause.get
val ifNode =
controlStructureNode(whereClause.condition, ControlStructureTypes.IF, code(whereClause.condition))
val whereAst = astForNodeWithFunctionReference(whereClause)
val whereClauseCallNode = callNode(
whereClause.condition,
s"!(${code(whereClause.condition)})",
Operators.logicalNot,
DispatchTypes.STATIC_DISPATCH
)
val argAsts = List(whereAst)
val testAst = callAst(whereClauseCallNode, argAsts)
val consequentAst =
Ast(controlStructureNode(whereClause.condition, ControlStructureTypes.CONTINUE, "continue"))
setOrderExplicitly(testAst, 1)
setOrderExplicitly(consequentAst, 2)
Ast(ifNode)
.withChild(testAst)
.withConditionEdge(ifNode, testAst.nodes.head)
.withChild(consequentAst)
}
(childrenTestAsts, childrenFlowAsts)
case other => (List(astForNode(other)), List.empty)
}
val needsSyntheticBreak = !s.statements.children.lastOption.exists(_.item.isInstanceOf[FallThroughStmtSyntax])
val cAsts = if (needsSyntheticBreak) {
flowAst :+ astForNode(s.statements) :+ Ast(controlStructureNode(s, ControlStructureTypes.BREAK, "break"))
} else {
flowAst :+ astForNode(s.statements)
}
setArgumentIndices(cAsts)
(tAsts.toList, cAsts.toList)
case i: IfConfigDeclSyntax =>
val children = i.clauses.children
val childrenTestAsts = children.flatMap(c => c.condition.map(astForNode)).toList
val childrenElementAsts = children.flatMap(c => c.elements.map(astForNode)).toList
(childrenTestAsts, childrenElementAsts)
}
labelAst +: (testAsts ++ consequentAsts)
}

private def astForSwitchExprSyntax(node: SwitchExprSyntax): Ast = {
val switchNode = controlStructureNode(node, ControlStructureTypes.SWITCH, code(node))

// The semantics of switch statement children is partially defined by their order value.
// The blockAst must have order == 2. Only to avoid collision we set switchExpressionAst to 1
// because the semantics of it is already indicated via the condition edge.
val switchExpressionAst = astForNodeWithFunctionReference(node.subject)
setOrderExplicitly(switchExpressionAst, 1)

val blockNode_ = blockNode(node).order(2)
scope.pushNewBlockScope(blockNode_)
localAstParentStack.push(blockNode_)

val casesAsts = node.cases.children.flatMap(astsForSwitchCase)
setArgumentIndices(casesAsts.toList)

scope.popScope()
localAstParentStack.pop()

Ast(switchNode)
.withChild(switchExpressionAst)
.withConditionEdge(switchNode, switchExpressionAst.nodes.head)
.withChild(blockAst(blockNode_, casesAsts.toList))
}

private def astForTernaryExprSyntax(node: TernaryExprSyntax): Ast = {
val name = Operators.conditional
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,19 @@
package io.joern.swiftsrc2cpg.astcreation

import io.joern.swiftsrc2cpg.datastructures.BlockScope
import io.joern.swiftsrc2cpg.datastructures.MethodScope
import io.joern.swiftsrc2cpg.parser.SwiftNodeSyntax.*
import io.joern.swiftsrc2cpg.passes.Defines
import io.joern.x2cpg.Ast
import io.joern.x2cpg.ValidationMode
import io.shiftleft.codepropertygraph.generated.EdgeTypes

trait AstForPatternSyntaxCreator(implicit withSchemaValidation: ValidationMode) {
this: AstCreator =>

private def astForExpressionPatternSyntax(node: ExpressionPatternSyntax): Ast = notHandledYet(node)
private def astForExpressionPatternSyntax(node: ExpressionPatternSyntax): Ast = {
astForNode(node.expression)
}

private def astForIdentifierPatternSyntax(node: IdentifierPatternSyntax): Ast = {
val name = code(node.identifier)
Expand All @@ -17,11 +22,47 @@ trait AstForPatternSyntaxCreator(implicit withSchemaValidation: ValidationMode)
Ast(identNode)
}

private def astForIsTypePatternSyntax(node: IsTypePatternSyntax): Ast = notHandledYet(node)
private def astForMissingPatternSyntax(node: MissingPatternSyntax): Ast = notHandledYet(node)
private def astForTuplePatternSyntax(node: TuplePatternSyntax): Ast = notHandledYet(node)
private def astForValueBindingPatternSyntax(node: ValueBindingPatternSyntax): Ast = notHandledYet(node)
private def astForWildcardPatternSyntax(node: WildcardPatternSyntax): Ast = notHandledYet(node)
private def astForIsTypePatternSyntax(node: IsTypePatternSyntax): Ast = notHandledYet(node)
private def astForMissingPatternSyntax(node: MissingPatternSyntax): Ast = notHandledYet(node)
private def astForTuplePatternSyntax(node: TuplePatternSyntax): Ast = notHandledYet(node)

private def astForValueBindingPatternSyntax(node: ValueBindingPatternSyntax): Ast = {
val kind = code(node.bindingSpecifier)
val scopeType = if (kind == "let") {
BlockScope
} else {
MethodScope
}

val name = node.pattern match {
case expr: ExpressionPatternSyntax =>
notHandledYet(expr)
code(expr)
case ident: IdentifierPatternSyntax =>
code(ident.identifier)
case isType: IsTypePatternSyntax =>
notHandledYet(isType)
code(isType)
case missing: MissingPatternSyntax =>
code(missing.placeholder)
case tuple: TuplePatternSyntax =>
notHandledYet(tuple)
code(tuple)
case valueBinding: ValueBindingPatternSyntax =>
notHandledYet(valueBinding)
code(valueBinding)
case wildcard: WildcardPatternSyntax =>
notHandledYet(wildcard)
generateUnusedVariableName(usedVariableNames, "wildcard")
}
val typeFullName = Defines.Any
val nLocalNode = localNode(node, name, name, typeFullName).order(0)
scope.addVariable(name, nLocalNode, scopeType)
diffGraph.addEdge(localAstParentStack.head, nLocalNode, EdgeTypes.AST)
Ast()
}

private def astForWildcardPatternSyntax(node: WildcardPatternSyntax): Ast = notHandledYet(node)

protected def astForPatternSyntax(patternSyntax: PatternSyntax): Ast = patternSyntax match {
case node: ExpressionPatternSyntax => astForExpressionPatternSyntax(node)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,22 @@ trait AstForStmtSyntaxCreator(implicit withSchemaValidation: ValidationMode) {
Ast(controlStructureNode(node, ControlStructureTypes.CONTINUE, code(node)))
}

private def astForDeferStmtSyntax(node: DeferStmtSyntax): Ast = notHandledYet(node)
private def astForDiscardStmtSyntax(node: DiscardStmtSyntax): Ast = notHandledYet(node)
private def astForDoStmtSyntax(node: DoStmtSyntax): Ast = notHandledYet(node)
private def astForExpressionStmtSyntax(node: ExpressionStmtSyntax): Ast = notHandledYet(node)
private def astForFallThroughStmtSyntax(node: FallThroughStmtSyntax): Ast = notHandledYet(node)
private def astForForStmtSyntax(node: ForStmtSyntax): Ast = notHandledYet(node)
private def astForGuardStmtSyntax(node: GuardStmtSyntax): Ast = notHandledYet(node)
private def astForLabeledStmtSyntax(node: LabeledStmtSyntax): Ast = notHandledYet(node)
private def astForMissingStmtSyntax(node: MissingStmtSyntax): Ast = notHandledYet(node)
private def astForDeferStmtSyntax(node: DeferStmtSyntax): Ast = notHandledYet(node)
private def astForDiscardStmtSyntax(node: DiscardStmtSyntax): Ast = notHandledYet(node)
private def astForDoStmtSyntax(node: DoStmtSyntax): Ast = notHandledYet(node)

private def astForExpressionStmtSyntax(node: ExpressionStmtSyntax): Ast = {
astForNodeWithFunctionReference(node.expression)
}

private def astForFallThroughStmtSyntax(node: FallThroughStmtSyntax): Ast = {
Ast(controlStructureNode(node, ControlStructureTypes.CONTINUE, code(node)))
}

private def astForForStmtSyntax(node: ForStmtSyntax): Ast = notHandledYet(node)
private def astForGuardStmtSyntax(node: GuardStmtSyntax): Ast = notHandledYet(node)
private def astForLabeledStmtSyntax(node: LabeledStmtSyntax): Ast = notHandledYet(node)
private def astForMissingStmtSyntax(node: MissingStmtSyntax): Ast = notHandledYet(node)

private def astForRepeatStmtSyntax(node: RepeatStmtSyntax): Ast = {
val code = this.code(node)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -277,10 +277,12 @@ trait AstForSyntaxCreator(implicit withSchemaValidation: ValidationMode) { this:
Ast(literalNode(node, s"\"${code(node)}\"", Option(Defines.String)))
}

private def astForSwitchCaseItemSyntax(node: SwitchCaseItemSyntax): Ast = notHandledYet(node)
private def astForSwitchCaseLabelSyntax(node: SwitchCaseLabelSyntax): Ast = notHandledYet(node)
private def astForSwitchCaseSyntax(node: SwitchCaseSyntax): Ast = notHandledYet(node)
private def astForSwitchDefaultLabelSyntax(node: SwitchDefaultLabelSyntax): Ast = notHandledYet(node)
private def astForSwitchCaseItemSyntax(node: SwitchCaseItemSyntax): Ast = notHandledYet(node)
private def astForSwitchCaseLabelSyntax(node: SwitchCaseLabelSyntax): Ast = notHandledYet(node)
private def astForSwitchCaseSyntax(node: SwitchCaseSyntax): Ast = notHandledYet(node)

private def astForSwitchDefaultLabelSyntax(node: SwitchDefaultLabelSyntax): Ast = Ast()

private def astForThrownTypeClauseSyntax(node: ThrownTypeClauseSyntax): Ast = notHandledYet(node)
private def astForTuplePatternElementSyntax(node: TuplePatternElementSyntax): Ast = notHandledYet(node)
private def astForTupleTypeElementSyntax(node: TupleTypeElementSyntax): Ast = notHandledYet(node)
Expand All @@ -292,9 +294,13 @@ trait AstForSyntaxCreator(implicit withSchemaValidation: ValidationMode) { this:
): Ast = notHandledYet(node)
private def astForUnderscorePrivateAttributeArgumentsSyntax(node: UnderscorePrivateAttributeArgumentsSyntax): Ast =
notHandledYet(node)
private def astForVersionComponentSyntax(node: VersionComponentSyntax): Ast = notHandledYet(node)
private def astForVersionTupleSyntax(node: VersionTupleSyntax): Ast = notHandledYet(node)
private def astForWhereClauseSyntax(node: WhereClauseSyntax): Ast = notHandledYet(node)
private def astForVersionComponentSyntax(node: VersionComponentSyntax): Ast = notHandledYet(node)
private def astForVersionTupleSyntax(node: VersionTupleSyntax): Ast = notHandledYet(node)

private def astForWhereClauseSyntax(node: WhereClauseSyntax): Ast = {
astForNodeWithFunctionReference(node.condition)
}

private def astForYieldedExpressionSyntax(node: YieldedExpressionSyntax): Ast = notHandledYet(node)
private def astForYieldedExpressionsClauseSyntax(node: YieldedExpressionsClauseSyntax): Ast = notHandledYet(node)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,24 @@ trait AstNodeBuilder(implicit withSchemaValidation: ValidationMode) { this: AstC
case _ => ""
}

protected def createJumpTarget(switchCase: SwitchCaseSyntax | IfConfigDeclSyntax): NewJumpTarget = {
val (switchName, switchCode) = switchCase match {
case s: SwitchCaseSyntax =>
val c = code(s.label)
(c.stripSuffix(":"), c)
case i: IfConfigDeclSyntax =>
notHandledYet(i)
val c = code(i.clauses.children.head)
(c.stripSuffix(":"), c)
}
NewJumpTarget()
.parserTypeName(switchCase.toString)
.name(switchName)
.code(switchCode)
.lineNumber(line(switchCase))
.columnNumber(column(switchCase))
}

protected def createIndexAccessCallAst(
baseNode: NewNode,
partNode: NewNode,
Expand Down
Loading

0 comments on commit 0fd532f

Please sign in to comment.