diff --git a/joern-cli/frontends/swiftsrc2cpg/src/main/scala/io/joern/swiftsrc2cpg/astcreation/AstForExprSyntaxCreator.scala b/joern-cli/frontends/swiftsrc2cpg/src/main/scala/io/joern/swiftsrc2cpg/astcreation/AstForExprSyntaxCreator.scala index a198ebbd8eb0..bf1e9d21308a 100644 --- a/joern-cli/frontends/swiftsrc2cpg/src/main/scala/io/joern/swiftsrc2cpg/astcreation/AstForExprSyntaxCreator.scala +++ b/joern-cli/frontends/swiftsrc2cpg/src/main/scala/io/joern/swiftsrc2cpg/astcreation/AstForExprSyntaxCreator.scala @@ -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) @@ -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 @@ -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 diff --git a/joern-cli/frontends/swiftsrc2cpg/src/main/scala/io/joern/swiftsrc2cpg/astcreation/AstForPatternSyntaxCreator.scala b/joern-cli/frontends/swiftsrc2cpg/src/main/scala/io/joern/swiftsrc2cpg/astcreation/AstForPatternSyntaxCreator.scala index 6d47a09ce96d..cecba6e56d6b 100644 --- a/joern-cli/frontends/swiftsrc2cpg/src/main/scala/io/joern/swiftsrc2cpg/astcreation/AstForPatternSyntaxCreator.scala +++ b/joern-cli/frontends/swiftsrc2cpg/src/main/scala/io/joern/swiftsrc2cpg/astcreation/AstForPatternSyntaxCreator.scala @@ -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) @@ -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) diff --git a/joern-cli/frontends/swiftsrc2cpg/src/main/scala/io/joern/swiftsrc2cpg/astcreation/AstForStmtSyntaxCreator.scala b/joern-cli/frontends/swiftsrc2cpg/src/main/scala/io/joern/swiftsrc2cpg/astcreation/AstForStmtSyntaxCreator.scala index 5432f342c372..071d11f6ebee 100644 --- a/joern-cli/frontends/swiftsrc2cpg/src/main/scala/io/joern/swiftsrc2cpg/astcreation/AstForStmtSyntaxCreator.scala +++ b/joern-cli/frontends/swiftsrc2cpg/src/main/scala/io/joern/swiftsrc2cpg/astcreation/AstForStmtSyntaxCreator.scala @@ -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) diff --git a/joern-cli/frontends/swiftsrc2cpg/src/main/scala/io/joern/swiftsrc2cpg/astcreation/AstForSyntaxCreator.scala b/joern-cli/frontends/swiftsrc2cpg/src/main/scala/io/joern/swiftsrc2cpg/astcreation/AstForSyntaxCreator.scala index 5e3c324c331f..3b88215aea3f 100644 --- a/joern-cli/frontends/swiftsrc2cpg/src/main/scala/io/joern/swiftsrc2cpg/astcreation/AstForSyntaxCreator.scala +++ b/joern-cli/frontends/swiftsrc2cpg/src/main/scala/io/joern/swiftsrc2cpg/astcreation/AstForSyntaxCreator.scala @@ -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) @@ -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) diff --git a/joern-cli/frontends/swiftsrc2cpg/src/main/scala/io/joern/swiftsrc2cpg/astcreation/AstNodeBuilder.scala b/joern-cli/frontends/swiftsrc2cpg/src/main/scala/io/joern/swiftsrc2cpg/astcreation/AstNodeBuilder.scala index 06ba4c199abe..462f82257dde 100644 --- a/joern-cli/frontends/swiftsrc2cpg/src/main/scala/io/joern/swiftsrc2cpg/astcreation/AstNodeBuilder.scala +++ b/joern-cli/frontends/swiftsrc2cpg/src/main/scala/io/joern/swiftsrc2cpg/astcreation/AstNodeBuilder.scala @@ -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, diff --git a/joern-cli/frontends/swiftsrc2cpg/src/test/scala/io/joern/swiftsrc2cpg/passes/ast/SwitchTests.scala b/joern-cli/frontends/swiftsrc2cpg/src/test/scala/io/joern/swiftsrc2cpg/passes/ast/SwitchTests.scala index 20e2412a2bd9..fc76f44704ae 100644 --- a/joern-cli/frontends/swiftsrc2cpg/src/test/scala/io/joern/swiftsrc2cpg/passes/ast/SwitchTests.scala +++ b/joern-cli/frontends/swiftsrc2cpg/src/test/scala/io/joern/swiftsrc2cpg/passes/ast/SwitchTests.scala @@ -7,118 +7,141 @@ import io.shiftleft.codepropertygraph.generated.nodes._ import io.shiftleft.semanticcpg.language._ class SwitchTests extends AbstractPassTest { + "SwitchTests" should { - "testSwitch9" ignore AstFixture(" switch x {}") { cpg => ??? } + "testSwitch9" in AstFixture(" switch x {}") { cpg => + val List(switchStmt) = cpg.switchBlock.l + val List(switchExpr) = switchStmt.astChildren.isIdentifier.nameExact("x").l + switchExpr.order shouldBe 1 + switchExpr.code shouldBe "x" + val List(switchBlock) = switchStmt.astChildren.isBlock.l + switchBlock._jumpTargetViaAstOut shouldBe empty + } - "testSwitch10" ignore AstFixture(""" + "testSwitch10" in AstFixture(""" |switch x { | case 0: | x = 0 - | // Multiple patterns per case | case 1, 2, 3: - | x = 0 - | // 'where' guard - | case _ where x % 2 == 0: | x = 1 + | case _ where x % 2 == 0: | x = 2 | x = 3 - | case _ where x % 2 == 0, - | _ where x % 3 == 0: - | x = 1 - | case 10, - | _ where x % 3 == 0: - | x = 1 - | case _ where x % 2 == 0, - | 20: - | x = 1 + | x = 4 + | case _ where x % 2 == 0, _ where x % 3 == 0: + | x = 5 + | case 10, _ where x % 3 == 0: + | x = 6 + | case _ where x % 2 == 0, 20: + | x = 7 | case var y where y % 2 == 0: | x = y + 1 | case _ where 0: - | x = 0 + | x = 8 | default: - | x = 1 + | x = 9 |} - |""".stripMargin) { cpg => ??? } + |""".stripMargin) { cpg => + val List(switchStmt) = cpg.switchBlock.l + val List(switchExpr) = switchStmt.astChildren.isIdentifier.nameExact("x").l + switchExpr.order shouldBe 1 + switchExpr.code shouldBe "x" + val List(switchBlock) = switchStmt.astChildren.isBlock.l + val List(caseLabel1) = switchBlock._jumpTargetViaAstOut.codeExact("case 0:").l + caseLabel1.order shouldBe 1 - "testSwitch11" ignore AstFixture(""" + val List(caseExpr1) = switchBlock.astChildren.isLiteral.codeExact("0").l + caseExpr1.order shouldBe 2 + + val List(stmtExpr1) = switchBlock.astChildren.isCall.codeExact("x = 0").l + stmtExpr1.order shouldBe 3 + + switchBlock.astChildren.isControlStructure.order(4).codeExact("break").size shouldBe 1 + + val List(caseLabel2) = switchBlock._jumpTargetViaAstOut.codeExact("case 1, 2, 3:").l + caseLabel2.order shouldBe 5 + + val List(caseExpr21) = switchBlock.astChildren.isLiteral.codeExact("1").l + caseExpr21.order shouldBe 6 + val List(caseExpr22) = switchBlock.astChildren.isLiteral.codeExact("2").l + caseExpr22.order shouldBe 7 + val List(caseExpr23) = switchBlock.astChildren.isLiteral.codeExact("3").l + caseExpr23.order shouldBe 8 + + val List(stmtExpr2) = switchBlock.astChildren.isCall.codeExact("x = 1").l + stmtExpr2.order shouldBe 9 + + switchBlock.astChildren.isControlStructure.order(10).codeExact("break").size shouldBe 1 + + val List(when19) = cpg.controlStructure.controlStructureType(ControlStructureTypes.IF).order(19).l + when19.astChildren.code.l shouldBe List("!(x % 2 == 0)", "continue") + when19.whenTrue.code.l shouldBe List("continue") + } + + "testSwitch11" in AstFixture(""" |// Multiple cases per case block |switch x { | case 0: | case 1: | x = 0 |} - |""".stripMargin) { cpg => ??? } + |""".stripMargin) { cpg => + val List(switchStmt) = cpg.switchBlock.l + val List(switchExpr) = switchStmt.astChildren.isIdentifier.nameExact("x").l + switchExpr.order shouldBe 1 + switchExpr.code shouldBe "x" + val List(switchBlock) = switchStmt.astChildren.isBlock.l + val List(caseLabel1) = switchBlock._jumpTargetViaAstOut.codeExact("case 0:").l + caseLabel1.order shouldBe 1 - "testSwitch12" ignore AstFixture(""" - |switch x { - | case 0: - | default: - | x = 0 - |} - |""".stripMargin) { cpg => ??? } + val List(caseExpr1) = switchBlock.astChildren.isLiteral.codeExact("0").l + caseExpr1.order shouldBe 2 - "testSwitch13" ignore AstFixture(""" - |switch x { - | case 0: - | x = 0 - | case 1: - |} - |""".stripMargin) { cpg => ??? } + switchBlock.astChildren.isControlStructure.order(3).codeExact("break").size shouldBe 1 - "testSwitch14" ignore AstFixture(""" - |switch x { - | case 0: - | x = 0 - | default: - |}""".stripMargin) { cpg => ??? } + val List(caseLabel2) = switchBlock._jumpTargetViaAstOut.codeExact("case 1:").l + caseLabel2.order shouldBe 4 - "testSwitch17" ignore AstFixture(""" - |switch x { - | default: - | x = 0 - | default: - | x = 0 - |} - |""".stripMargin) { cpg => ??? } + val List(caseExpr21) = switchBlock.astChildren.isLiteral.codeExact("1").l + caseExpr21.order shouldBe 5 + + val List(stmtExpr2) = switchBlock.astChildren.isCall.codeExact("x = 0").l + stmtExpr2.order shouldBe 6 - "testSwitch20" ignore AstFixture(""" + switchBlock.astChildren.isControlStructure.order(7).codeExact("break").size shouldBe 1 + } + + "testSwitch12" in AstFixture(""" |switch x { - | default: | case 0: + | default: | x = 0 |} - |""".stripMargin) { cpg => ??? } + |""".stripMargin) { cpg => + val List(switchStmt) = cpg.switchBlock.l + val List(switchExpr) = switchStmt.astChildren.isIdentifier.nameExact("x").l + switchExpr.order shouldBe 1 + switchExpr.code shouldBe "x" + val List(switchBlock) = switchStmt.astChildren.isBlock.l + val List(caseLabel1) = switchBlock._jumpTargetViaAstOut.codeExact("case 0:").l + caseLabel1.order shouldBe 1 - "testSwitch21" ignore AstFixture(""" - |switch x { - | default: - | default: - | x = 0 - |}""".stripMargin) { cpg => ??? } + val List(caseExpr1) = switchBlock.astChildren.isLiteral.codeExact("0").l + caseExpr1.order shouldBe 2 - "testSwitch23" ignore AstFixture(""" - |switch x { - | case 0: - |} - |""".stripMargin) { cpg => ??? } + switchBlock.astChildren.isControlStructure.order(3).codeExact("break").size shouldBe 1 - "testSwitch24" ignore AstFixture(""" - |switch x { - | case 0: - | case 1: - | x = 0 - |} - |""".stripMargin) { cpg => ??? } + val List(caseLabel2) = switchBlock._jumpTargetViaAstOut.codeExact("default:").l + caseLabel2.order shouldBe 4 - "testSwitch25" ignore AstFixture(""" - |switch x { - | case 0: - | x = 0 - | case 1: - |}""".stripMargin) { cpg => ??? } + val List(stmtExpr2) = switchBlock.astChildren.isCall.codeExact("x = 0").l + stmtExpr2.order shouldBe 5 - "testSwitch28" ignore AstFixture(""" + switchBlock.astChildren.isControlStructure.order(6).codeExact("break").size shouldBe 1 + } + + "testSwitch28" in AstFixture(""" |switch x { | case 0: | fallthrough @@ -127,7 +150,45 @@ class SwitchTests extends AbstractPassTest { | default: | fallthrough |} - |""".stripMargin) { cpg => ??? } + |""".stripMargin) { cpg => + val List(switchStmt) = cpg.switchBlock.l + val List(switchExpr) = switchStmt.astChildren.isIdentifier.nameExact("x").l + switchExpr.order shouldBe 1 + switchExpr.code shouldBe "x" + val List(switchBlock) = switchStmt.astChildren.isBlock.l + val List(caseLabel1) = switchBlock._jumpTargetViaAstOut.codeExact("case 0:").l + caseLabel1.order shouldBe 1 + + val List(caseExpr1) = switchBlock.astChildren.isLiteral.codeExact("0").l + caseExpr1.order shouldBe 2 + + switchBlock.astChildren.isControlStructure + .controlStructureType(ControlStructureTypes.CONTINUE) + .order(3) + .codeExact("fallthrough") + .size shouldBe 1 + + val List(caseLabel2) = switchBlock._jumpTargetViaAstOut.codeExact("case 1:").l + caseLabel2.order shouldBe 4 + + val List(caseExpr2) = switchBlock.astChildren.isLiteral.codeExact("1").l + caseExpr2.order shouldBe 5 + + switchBlock.astChildren.isControlStructure + .controlStructureType(ControlStructureTypes.CONTINUE) + .order(6) + .codeExact("fallthrough") + .size shouldBe 1 + + val List(caseLabel3) = switchBlock._jumpTargetViaAstOut.codeExact("default:").l + caseLabel3.order shouldBe 7 + + switchBlock.astChildren.isControlStructure + .controlStructureType(ControlStructureTypes.CONTINUE) + .order(8) + .codeExact("fallthrough") + .size shouldBe 1 + } "testSwitch29" ignore AstFixture(""" |// Fallthrough can transfer control anywhere within a case and can appear