diff --git a/joern-cli/frontends/swiftsrc2cpg/src/main/scala/io/joern/swiftsrc2cpg/astcreation/AstForDeclSyntaxCreator.scala b/joern-cli/frontends/swiftsrc2cpg/src/main/scala/io/joern/swiftsrc2cpg/astcreation/AstForDeclSyntaxCreator.scala index 449ef44e754d..db1bb9bb1b27 100644 --- a/joern-cli/frontends/swiftsrc2cpg/src/main/scala/io/joern/swiftsrc2cpg/astcreation/AstForDeclSyntaxCreator.scala +++ b/joern-cli/frontends/swiftsrc2cpg/src/main/scala/io/joern/swiftsrc2cpg/astcreation/AstForDeclSyntaxCreator.scala @@ -31,12 +31,15 @@ trait AstForDeclSyntaxCreator(implicit withSchemaValidation: ValidationMode) { } private def declMembers( - decl: ClassDeclSyntax | ExtensionDeclSyntax, + decl: ClassDeclSyntax | ExtensionDeclSyntax | ProtocolDeclSyntax | StructDeclSyntax | EnumDeclSyntax, withConstructor: Boolean = true ): Seq[DeclSyntax] = { val memberBlock = decl match { case c: ClassDeclSyntax => c.memberBlock case e: ExtensionDeclSyntax => e.memberBlock + case p: ProtocolDeclSyntax => p.memberBlock + case s: StructDeclSyntax => s.memberBlock + case e: EnumDeclSyntax => e.memberBlock } val allMembers = memberBlock.members.children.map(_.decl) if (withConstructor) { @@ -48,6 +51,7 @@ trait AstForDeclSyntaxCreator(implicit withSchemaValidation: ValidationMode) { private def isInitializedMember(node: DeclSyntax): Boolean = node match { case v: VariableDeclSyntax => v.bindings.children.exists(c => c.initializer.isDefined || c.accessorBlock.isDefined) + case e: EnumCaseDeclSyntax => e.elements.children.exists(c => c.rawValue.isDefined) case _ => false } @@ -96,7 +100,7 @@ trait AstForDeclSyntaxCreator(implicit withSchemaValidation: ValidationMode) { } private def createFakeConstructor( - node: ClassDeclSyntax | ExtensionDeclSyntax, + node: ClassDeclSyntax | ExtensionDeclSyntax | ProtocolDeclSyntax | StructDeclSyntax | EnumDeclSyntax, methodBlockContent: List[Ast] = List.empty ): AstAndMethod = { val constructorName = io.joern.x2cpg.Defines.ConstructorMethodName @@ -107,10 +111,7 @@ trait AstForDeclSyntaxCreator(implicit withSchemaValidation: ValidationMode) { methodAstParentStack.push(methodNode_) - val name = node match { - case c: ClassDeclSyntax => code(c.name) - case e: ExtensionDeclSyntax => code(e.extendedType) - } + val name = typeNameForDeclSyntax(node) val returnType = calcTypeNameAndFullName(name)._2 val methodReturnNode = newMethodReturnNode(returnType, dynamicTypeHintFullName = None, line = line(node), column = column(node)) @@ -134,9 +135,9 @@ trait AstForDeclSyntaxCreator(implicit withSchemaValidation: ValidationMode) { AstAndMethod(Ast(), methodNode_, bAst) } - private def astForClassMember(classElement: DeclSyntax, typeDeclNode: NewTypeDecl): Ast = { - val typeFullName = typeNameForDeclSyntax(classElement) - classElement match { + private def astForDeclMember(node: DeclSyntax, typeDeclNode: NewTypeDecl): Ast = { + val typeFullName = typeNameForDeclSyntax(node) + node match { case d: (AccessorDeclSyntax | InitializerDeclSyntax | DeinitializerDeclSyntax | FunctionDeclSyntax) => val function = astForFunctionLike(d).method val bindingNode = newBindingNode("", "", "") @@ -149,12 +150,18 @@ trait AstForDeclSyntaxCreator(implicit withSchemaValidation: ValidationMode) { ImportDeclSyntax | ProtocolDeclSyntax | StructDeclSyntax | MacroDeclSyntax | MacroExpansionDeclSyntax | OperatorDeclSyntax | PoundSourceLocationSyntax | PrecedenceGroupDeclSyntax | SubscriptDeclSyntax | TypeAliasDeclSyntax | IfConfigDeclSyntax) => - val ast = astForNode(classElement) + val ast = astForNode(node) Ast.storeInDiffGraph(ast, diffGraph) ast.root.foreach(r => diffGraph.addEdge(typeDeclNode, r, EdgeTypes.AST)) Ast() case d: EnumCaseDeclSyntax => - notHandledYet(d) + val ast = astForNode(d) + d.elements.children.foreach { c => + val cCode = code(c.name) + val memberNode_ = memberNode(c, cCode, cCode, typeFullName) + diffGraph.addEdge(typeDeclNode, memberNode_, EdgeTypes.AST) + } + ast case d: VariableDeclSyntax => val ast = astForNode(d) d.bindings.children.foreach { c => @@ -167,11 +174,13 @@ trait AstForDeclSyntaxCreator(implicit withSchemaValidation: ValidationMode) { } } - private def findDeclConstructor(decl: ClassDeclSyntax | ExtensionDeclSyntax): Option[DeclSyntax] = + private def findDeclConstructor( + decl: ClassDeclSyntax | ExtensionDeclSyntax | ProtocolDeclSyntax | StructDeclSyntax | EnumDeclSyntax + ): Option[DeclSyntax] = declMembers(decl).find(isConstructor) private def createDeclConstructor( - node: ClassDeclSyntax | ExtensionDeclSyntax, + node: ClassDeclSyntax | ExtensionDeclSyntax | ProtocolDeclSyntax | StructDeclSyntax | EnumDeclSyntax, constructorContent: List[Ast], constructorBlock: Ast = Ast() ): Option[AstAndMethod] = @@ -199,15 +208,30 @@ trait AstForDeclSyntaxCreator(implicit withSchemaValidation: ValidationMode) { node.isInstanceOf[FunctionDeclSyntax] || !isInitializedMember(node) - private def astForClassDeclSyntax(node: ClassDeclSyntax): Ast = { + private def astForDeclAttributes( + node: ClassDeclSyntax | ProtocolDeclSyntax | VariableDeclSyntax | StructDeclSyntax | EnumDeclSyntax + ): Seq[Ast] = { + node match { + case c: ClassDeclSyntax => c.attributes.children.map(astForNode) + case p: ProtocolDeclSyntax => p.attributes.children.map(astForNode) + case v: VariableDeclSyntax => v.attributes.children.map(astForNode) + case s: StructDeclSyntax => s.attributes.children.map(astForNode) + case e: EnumDeclSyntax => e.attributes.children.map(astForNode) + } + } + + private def astForTypeDeclSyntax( + node: ClassDeclSyntax | ProtocolDeclSyntax | StructDeclSyntax | EnumDeclSyntax + ): Ast = { // TODO: // - handle genericParameterClause // - handle genericWhereClause - val attributes = node.attributes.children.map(astForNode) + val attributes = astForDeclAttributes(node) val modifiers = modifiersForDecl(node) val inherits = inheritsFrom(node) - val (typeName, typeFullName) = calcTypeNameAndFullName(code(node.name)) + val name = typeNameForDeclSyntax(node) + val (typeName, typeFullName) = calcTypeNameAndFullName(name) val existingTypeDecl = global.seenTypeDecls.keys().asScala.find(_.name == typeName) if (existingTypeDecl.isEmpty) { @@ -252,18 +276,18 @@ trait AstForDeclSyntaxCreator(implicit withSchemaValidation: ValidationMode) { // adding all other members and retrieving their initialization calls val memberInitCalls = allClassMembers .filter(m => !isStaticMember(m) && isInitializedMember(m)) - .map(m => astForClassMember(m, typeDeclNode_)) + .map(m => astForDeclMember(m, typeDeclNode_)) val constructor = createDeclConstructor(node, memberInitCalls) // adding all class methods / functions and uninitialized, non-static members allClassMembers .filter(member => isClassMethodOrUninitializedMember(member) && !isStaticMember(member)) - .foreach(m => astForClassMember(m, typeDeclNode_)) + .foreach(m => astForDeclMember(m, typeDeclNode_)) // adding all static members and retrieving their initialization calls val staticMemberInitCalls = - allClassMembers.filter(isStaticMember).map(m => astForClassMember(m, typeDeclNode_)) + allClassMembers.filter(isStaticMember).map(m => astForDeclMember(m, typeDeclNode_)) methodAstParentStack.pop() dynamicInstanceTypeStack.pop() @@ -307,18 +331,18 @@ trait AstForDeclSyntaxCreator(implicit withSchemaValidation: ValidationMode) { // adding all other members and retrieving their initialization calls val memberInitCalls = allClassMembers .filter(m => !isStaticMember(m) && isInitializedMember(m)) - .map(m => astForClassMember(m, typeDeclNode_)) + .map(m => astForDeclMember(m, typeDeclNode_)) createDeclConstructor(node, memberInitCalls, constructorBlock) // adding all class methods / functions and uninitialized, non-static members allClassMembers .filter(member => isClassMethodOrUninitializedMember(member) && !isStaticMember(member)) - .foreach(m => astForClassMember(m, typeDeclNode_)) + .foreach(m => astForDeclMember(m, typeDeclNode_)) // adding all static members and retrieving their initialization calls val staticMemberInitCalls = - allClassMembers.filter(isStaticMember).map(m => astForClassMember(m, typeDeclNode_)) + allClassMembers.filter(isStaticMember).map(m => astForDeclMember(m, typeDeclNode_)) methodAstParentStack.pop() dynamicInstanceTypeStack.pop() @@ -349,13 +373,58 @@ trait AstForDeclSyntaxCreator(implicit withSchemaValidation: ValidationMode) { private def astForDeinitializerDeclSyntax(node: DeinitializerDeclSyntax): Ast = notHandledYet(node) private def astForEditorPlaceholderDeclSyntax(node: EditorPlaceholderDeclSyntax): Ast = notHandledYet(node) - private def astForEnumCaseDeclSyntax(node: EnumCaseDeclSyntax): Ast = notHandledYet(node) - private def astForEnumDeclSyntax(node: EnumDeclSyntax): Ast = notHandledYet(node) - private def inheritsFrom(node: ClassDeclSyntax | ExtensionDeclSyntax): Seq[String] = { + private def astForEnumCaseDeclSyntax(node: EnumCaseDeclSyntax): Ast = { + val attributeAsts = node.attributes.children.map(astForNode) + val modifiers = modifiersForDecl(node) + val scopeType = BlockScope + + val bindingAsts = node.elements.children.map { binding => + val name = code(binding.name) + val nLocalNode = localNode(binding, name, name, Defines.Any).order(0) + scope.addVariable(name, nLocalNode, scopeType) + diffGraph.addEdge(localAstParentStack.head, nLocalNode, EdgeTypes.AST) + + val initAsts = binding.rawValue.map(astForNode).toList + if (initAsts.isEmpty) { + Ast() + } else { + val patternAst = astForNode(binding.name) + modifiers.foreach { mod => + patternAst.root.foreach { r => diffGraph.addEdge(r, mod, EdgeTypes.AST) } + } + attributeAsts.foreach { attrAst => + patternAst.root.foreach { r => attrAst.root.foreach { attr => diffGraph.addEdge(r, attr, EdgeTypes.AST) } } + } + createAssignmentCallAst( + patternAst, + initAsts.head, + code(binding).stripSuffix(","), + line = line(binding), + column = column(binding) + ) + } + } + + bindingAsts match { + case Nil => Ast() + case head :: Nil => head + case _ => + val block = blockNode(node, code(node), Defines.Any) + setArgumentIndices(bindingAsts) + blockAst(block, bindingAsts.toList) + } + } + + private def inheritsFrom( + node: ClassDeclSyntax | ExtensionDeclSyntax | ProtocolDeclSyntax | StructDeclSyntax | EnumDeclSyntax + ): Seq[String] = { val clause = node match { case c: ClassDeclSyntax => c.inheritanceClause case e: ExtensionDeclSyntax => e.inheritanceClause + case p: ProtocolDeclSyntax => p.inheritanceClause + case s: StructDeclSyntax => s.inheritanceClause + case e: EnumDeclSyntax => e.inheritanceClause } val inheritsFrom = clause match { case Some(value) => value.inheritedTypes.children.map(c => code(c.`type`)) @@ -376,7 +445,8 @@ trait AstForDeclSyntaxCreator(implicit withSchemaValidation: ValidationMode) { val modifiers = modifiersForDecl(node) val inherits = inheritsFrom(node) - val (typeName, typeFullName) = calcTypeNameAndFullName(code(node.extendedType)) + val name = typeNameForDeclSyntax(node) + val (typeName, typeFullName) = calcTypeNameAndFullName(name) val existingTypeDecl = global.seenTypeDecls.keys().asScala.find(_.name == typeName) if (existingTypeDecl.isEmpty) { @@ -421,18 +491,18 @@ trait AstForDeclSyntaxCreator(implicit withSchemaValidation: ValidationMode) { // adding all other members and retrieving their initialization calls val memberInitCalls = allClassMembers .filter(m => !isStaticMember(m) && isInitializedMember(m)) - .map(m => astForClassMember(m, typeDeclNode_)) + .map(m => astForDeclMember(m, typeDeclNode_)) val constructor = createDeclConstructor(node, memberInitCalls) // adding all class methods / functions and uninitialized, non-static members allClassMembers .filter(member => isClassMethodOrUninitializedMember(member) && !isStaticMember(member)) - .foreach(m => astForClassMember(m, typeDeclNode_)) + .foreach(m => astForDeclMember(m, typeDeclNode_)) // adding all static members and retrieving their initialization calls val staticMemberInitCalls = - allClassMembers.filter(isStaticMember).map(m => astForClassMember(m, typeDeclNode_)) + allClassMembers.filter(isStaticMember).map(m => astForDeclMember(m, typeDeclNode_)) methodAstParentStack.pop() dynamicInstanceTypeStack.pop() @@ -480,18 +550,18 @@ trait AstForDeclSyntaxCreator(implicit withSchemaValidation: ValidationMode) { // adding all other members and retrieving their initialization calls val memberInitCalls = allClassMembers .filter(m => !isStaticMember(m) && isInitializedMember(m)) - .map(m => astForClassMember(m, typeDeclNode_)) + .map(m => astForDeclMember(m, typeDeclNode_)) createDeclConstructor(node, memberInitCalls, constructorBlock) // adding all class methods / functions and uninitialized, non-static members allClassMembers .filter(member => isClassMethodOrUninitializedMember(member) && !isStaticMember(member)) - .foreach(m => astForClassMember(m, typeDeclNode_)) + .foreach(m => astForDeclMember(m, typeDeclNode_)) // adding all static members and retrieving their initialization calls val staticMemberInitCalls = - allClassMembers.filter(isStaticMember).map(m => astForClassMember(m, typeDeclNode_)) + allClassMembers.filter(isStaticMember).map(m => astForDeclMember(m, typeDeclNode_)) methodAstParentStack.pop() dynamicInstanceTypeStack.pop() @@ -520,10 +590,17 @@ trait AstForDeclSyntaxCreator(implicit withSchemaValidation: ValidationMode) { } } - private def modifiersForDecl(node: ClassDeclSyntax | ExtensionDeclSyntax): Seq[NewModifier] = { + private def modifiersForDecl( + node: ClassDeclSyntax | ExtensionDeclSyntax | ProtocolDeclSyntax | StructDeclSyntax | EnumDeclSyntax | + EnumCaseDeclSyntax + ): Seq[NewModifier] = { val modifierList = node match { case c: ClassDeclSyntax => c.modifiers.children case e: ExtensionDeclSyntax => e.modifiers.children + case p: ProtocolDeclSyntax => p.modifiers.children + case s: StructDeclSyntax => s.modifiers.children + case e: EnumDeclSyntax => e.modifiers.children + case ec: EnumCaseDeclSyntax => ec.modifiers.children } val modifiers = modifierList.flatMap(c => astForNode(c).root.map(_.asInstanceOf[NewModifier])) val allModifier = if (modifiers.isEmpty) { @@ -699,12 +776,9 @@ trait AstForDeclSyntaxCreator(implicit withSchemaValidation: ValidationMode) { private def astForInitializerDeclSyntax(node: InitializerDeclSyntax): Ast = notHandledYet(node) private def astForMacroDeclSyntax(node: MacroDeclSyntax): Ast = notHandledYet(node) private def astForMacroExpansionDeclSyntax(node: MacroExpansionDeclSyntax): Ast = notHandledYet(node) - private def astForMissingDeclSyntax(node: MissingDeclSyntax): Ast = notHandledYet(node) private def astForOperatorDeclSyntax(node: OperatorDeclSyntax): Ast = notHandledYet(node) private def astForPoundSourceLocationSyntax(node: PoundSourceLocationSyntax): Ast = notHandledYet(node) private def astForPrecedenceGroupDeclSyntax(node: PrecedenceGroupDeclSyntax): Ast = notHandledYet(node) - private def astForProtocolDeclSyntax(node: ProtocolDeclSyntax): Ast = notHandledYet(node) - private def astForStructDeclSyntax(node: StructDeclSyntax): Ast = notHandledYet(node) private def astForSubscriptDeclSyntax(node: SubscriptDeclSyntax): Ast = notHandledYet(node) private def astForTypeAliasDeclSyntax(node: TypeAliasDeclSyntax): Ast = notHandledYet(node) @@ -781,11 +855,11 @@ trait AstForDeclSyntaxCreator(implicit withSchemaValidation: ValidationMode) { case node: AccessorDeclSyntax => astForAccessorDeclSyntax(node) case node: ActorDeclSyntax => astForActorDeclSyntax(node) case node: AssociatedTypeDeclSyntax => astForAssociatedTypeDeclSyntax(node) - case node: ClassDeclSyntax => astForClassDeclSyntax(node) + case node: ClassDeclSyntax => astForTypeDeclSyntax(node) case node: DeinitializerDeclSyntax => astForDeinitializerDeclSyntax(node) case node: EditorPlaceholderDeclSyntax => astForEditorPlaceholderDeclSyntax(node) case node: EnumCaseDeclSyntax => astForEnumCaseDeclSyntax(node) - case node: EnumDeclSyntax => astForEnumDeclSyntax(node) + case node: EnumDeclSyntax => astForTypeDeclSyntax(node) case node: ExtensionDeclSyntax => astForExtensionDeclSyntax(node) case node: FunctionDeclSyntax => astForFunctionDeclSyntax(node) case node: IfConfigDeclSyntax => astForIfConfigDeclSyntax(node) @@ -793,12 +867,12 @@ trait AstForDeclSyntaxCreator(implicit withSchemaValidation: ValidationMode) { case node: InitializerDeclSyntax => astForInitializerDeclSyntax(node) case node: MacroDeclSyntax => astForMacroDeclSyntax(node) case node: MacroExpansionDeclSyntax => astForMacroExpansionDeclSyntax(node) - case node: MissingDeclSyntax => astForMissingDeclSyntax(node) + case _: MissingDeclSyntax => Ast() case node: OperatorDeclSyntax => astForOperatorDeclSyntax(node) case node: PoundSourceLocationSyntax => astForPoundSourceLocationSyntax(node) case node: PrecedenceGroupDeclSyntax => astForPrecedenceGroupDeclSyntax(node) - case node: ProtocolDeclSyntax => astForProtocolDeclSyntax(node) - case node: StructDeclSyntax => astForStructDeclSyntax(node) + case node: ProtocolDeclSyntax => astForTypeDeclSyntax(node) + case node: StructDeclSyntax => astForTypeDeclSyntax(node) case node: SubscriptDeclSyntax => astForSubscriptDeclSyntax(node) case node: TypeAliasDeclSyntax => astForTypeAliasDeclSyntax(node) case node: VariableDeclSyntax => astForVariableDeclSyntax(node) diff --git a/joern-cli/frontends/swiftsrc2cpg/src/test/scala/io/joern/swiftsrc2cpg/passes/ast/ExtensionTests.scala b/joern-cli/frontends/swiftsrc2cpg/src/test/scala/io/joern/swiftsrc2cpg/passes/ast/ClassExtensionTests.scala similarity index 97% rename from joern-cli/frontends/swiftsrc2cpg/src/test/scala/io/joern/swiftsrc2cpg/passes/ast/ExtensionTests.scala rename to joern-cli/frontends/swiftsrc2cpg/src/test/scala/io/joern/swiftsrc2cpg/passes/ast/ClassExtensionTests.scala index 7cc71d4ffbb0..9975f461781d 100644 --- a/joern-cli/frontends/swiftsrc2cpg/src/test/scala/io/joern/swiftsrc2cpg/passes/ast/ExtensionTests.scala +++ b/joern-cli/frontends/swiftsrc2cpg/src/test/scala/io/joern/swiftsrc2cpg/passes/ast/ClassExtensionTests.scala @@ -4,9 +4,9 @@ import io.shiftleft.codepropertygraph.generated.* import io.shiftleft.codepropertygraph.generated.nodes.* import io.shiftleft.semanticcpg.language.* -class ExtensionTests extends AbstractPassTest { +class ClassExtensionTests extends AbstractPassTest { - "ExtensionTests" should { + "ClassExtensionTests" should { "test Class and Extension defined afterwards" in AstFixture(""" |public class A {} @@ -14,7 +14,7 @@ class ExtensionTests extends AbstractPassTest { | var b = 0.0 |} | - |class Foo: Bar { // implicitly internal + |class Foo: Bar { // implicitly internal (private) | public var a = A() | private var b = false | var c = 0.0 @@ -113,7 +113,7 @@ class ExtensionTests extends AbstractPassTest { | func someOtherFunc() {} |} | - |class Foo: Bar { // implicitly internal + |class Foo: Bar { // implicitly internal (private) | public var a = A() | private var b = false | var c = 0.0 diff --git a/joern-cli/frontends/swiftsrc2cpg/src/test/scala/io/joern/swiftsrc2cpg/passes/ast/EnumerationExtensionTests.scala b/joern-cli/frontends/swiftsrc2cpg/src/test/scala/io/joern/swiftsrc2cpg/passes/ast/EnumerationExtensionTests.scala new file mode 100644 index 000000000000..1cb08f5ff303 --- /dev/null +++ b/joern-cli/frontends/swiftsrc2cpg/src/test/scala/io/joern/swiftsrc2cpg/passes/ast/EnumerationExtensionTests.scala @@ -0,0 +1,220 @@ +package io.joern.swiftsrc2cpg.passes.ast + +import io.shiftleft.codepropertygraph.generated.* +import io.shiftleft.codepropertygraph.generated.nodes.* +import io.shiftleft.semanticcpg.language.* + +class EnumerationExtensionTests extends AbstractPassTest { + + "EnumerationExtensionTests" should { + + "test Enumeration and Extension defined afterwards" in AstFixture(""" + |public enum A {} + |private enum B { + | var b = 0.0 + |} + | + |enum Foo: Bar { // implicitly internal (private) + | case tuple(Int, Int, Int, Int) + | case c1 = 1, c2, c3 + | indirect case c4(Foo) + | + | public var a = A() + | private var b = false + | var c = 0.0 + | var d: String? + | + | static var e = 1 + | static var f = true + | + | var g: Double { return self * 1_000.0 } + | + | init(paramA: String, paramB: Int) { + | self.init() + | } + | + | private func someFunc() {} + | + | override internal func someMethod() { + | super.someMethod() + | } + | + | mutating func square() { + | self = self * self + | } + | + |} + | + |extension Foo: SomeProtocol, AnotherProtocol { + | var h = 0.0 + | var i: String + | static var j = 2 + | func someOtherFunc() {} + |} + | + |""".stripMargin) { cpg => + val List(typeDeclA) = cpg.typeDecl.nameExact("A").l + typeDeclA.fullName shouldBe "code.swift::A" + typeDeclA.member.l shouldBe empty + typeDeclA.inheritsFromTypeFullName.l shouldBe empty + typeDeclA.modifier.modifierType.l shouldBe List(ModifierTypes.PUBLIC) + + val List(typeDeclB) = cpg.typeDecl.nameExact("B").l + typeDeclB.fullName shouldBe "code.swift::B" + typeDeclB.member.name.l shouldBe List("b") + typeDeclB.inheritsFromTypeFullName.l shouldBe empty + typeDeclB.modifier.modifierType.l shouldBe List(ModifierTypes.PRIVATE) + val List(bConstructor) = typeDeclB.method.isConstructor.l + bConstructor.fullName shouldBe s"code.swift::B:${io.joern.x2cpg.Defines.ConstructorMethodName}" + bConstructor.block.astChildren.code.l shouldBe List("var b = 0.0") + + val List(typeDeclFoo) = cpg.typeDecl.nameExact("Foo").l + typeDeclFoo.fullName shouldBe "code.swift::Foo" + typeDeclFoo.member.name.l.sorted shouldBe List( + "a", + "b", + "c", + "c1", + "c2", + "c3", + "c4", + "d", + "e", + "f", + "g", + "h", + "i", + "j", + "someFunc", + "someMethod", + "someOtherFunc", + "square", + "tuple" + ) + typeDeclFoo.inheritsFromTypeFullName.l shouldBe List("AnotherProtocol", "Bar", "SomeProtocol") + typeDeclFoo.modifier.modifierType.l shouldBe List(ModifierTypes.PRIVATE) + + val List(fooConstructor) = typeDeclFoo.method.isConstructor.l + fooConstructor.fullName shouldBe "code.swift::Foo:init" + fooConstructor.block.astChildren.assignment.code.l.sorted shouldBe List( + "c1 = 1", + "var a = A()", + "var b = false", + "var c = 0.0", + "var g: Double { return self * 1_000.0 }", // lowered as assignment + "var h = 0.0" + ) + + val List(fooStaticInit) = typeDeclFoo.method.nameExact(io.joern.x2cpg.Defines.StaticInitMethodName).l + fooStaticInit.fullName shouldBe s"code.swift::Foo:${io.joern.x2cpg.Defines.StaticInitMethodName}" + fooStaticInit.block.astChildren.assignment.code.l.sorted shouldBe List("var e = 1", "var f = true", "var j = 2") + } + + "test Structure and Extension defined beforehand" in AstFixture(""" + |public enum A {} + |private enum B { + | var b = 0.0 + |} + | + |extension Foo: SomeProtocol, AnotherProtocol { + | var h = 0.0 + | var i: String + | static var j = 2 + | func someOtherFunc() {} + |} + | + |enum Foo: Bar { // implicitly internal (private) + | case tuple(Int, Int, Int, Int) + | case c1 = 1, c2, c3 + | indirect case c4(Foo) + | + | public var a = A() + | private var b = false + | var c = 0.0 + | var d: String? + | + | static var e = 1 + | static var f = true + | + | var g: Double { return self * 1_000.0 } + | + | init(paramA: String, paramB: Int) { + | self.init() + | } + | + | private func someFunc() {} + | + | override internal func someMethod() { + | super.someMethod() + | } + | + | mutating func square() { + | self = self * self + | } + | + |} + |""".stripMargin) { cpg => + val List(typeDeclA) = cpg.typeDecl.nameExact("A").l + typeDeclA.fullName shouldBe "code.swift::A" + typeDeclA.member.l shouldBe empty + typeDeclA.inheritsFromTypeFullName.l shouldBe empty + typeDeclA.modifier.modifierType.l shouldBe List(ModifierTypes.PUBLIC) + + val List(typeDeclB) = cpg.typeDecl.nameExact("B").l + typeDeclB.fullName shouldBe "code.swift::B" + typeDeclB.member.name.l shouldBe List("b") + typeDeclB.inheritsFromTypeFullName.l shouldBe empty + typeDeclB.modifier.modifierType.l shouldBe List(ModifierTypes.PRIVATE) + val List(bConstructor) = typeDeclB.method.isConstructor.l + bConstructor.fullName shouldBe s"code.swift::B:${io.joern.x2cpg.Defines.ConstructorMethodName}" + bConstructor.block.astChildren.code.l shouldBe List("var b = 0.0") + + val List(typeDeclFoo) = cpg.typeDecl.nameExact("Foo").l + typeDeclFoo.fullName shouldBe "code.swift::Foo" + typeDeclFoo.member.name.l.sorted shouldBe List( + "a", + "b", + "c", + "c1", + "c2", + "c3", + "c4", + "d", + "e", + "f", + "g", + "h", + "i", + "j", + "someFunc", + "someMethod", + "someOtherFunc", + "square", + "tuple" + ) + typeDeclFoo.inheritsFromTypeFullName.l shouldBe List("AnotherProtocol", "Bar", "SomeProtocol") + typeDeclFoo.modifier.modifierType.l shouldBe List(ModifierTypes.PRIVATE) + + val List(fooFakeConstructor) = + typeDeclFoo.method.isConstructor.nameExact(io.joern.x2cpg.Defines.ConstructorMethodName).l + fooFakeConstructor.fullName shouldBe s"code.swift::Foo:${io.joern.x2cpg.Defines.ConstructorMethodName}" + fooFakeConstructor.block.astChildren.assignment.code.l.sorted shouldBe List("var h = 0.0") + + val List(fooConstructor) = typeDeclFoo.method.isConstructor.nameExact("init").l + fooConstructor.fullName shouldBe "code.swift::Foo:init" + fooConstructor.block.astChildren.assignment.code.l.sorted shouldBe List( + "c1 = 1", + "var a = A()", + "var b = false", + "var c = 0.0", + "var g: Double { return self * 1_000.0 }" + ) + + val List(fooStaticInit) = typeDeclFoo.method.nameExact(io.joern.x2cpg.Defines.StaticInitMethodName).l + fooStaticInit.fullName shouldBe s"code.swift::Foo:${io.joern.x2cpg.Defines.StaticInitMethodName}" + fooStaticInit.block.astChildren.assignment.code.l.sorted shouldBe List("var e = 1", "var f = true", "var j = 2") + } + + } + +} diff --git a/joern-cli/frontends/swiftsrc2cpg/src/test/scala/io/joern/swiftsrc2cpg/passes/ast/ProtocolExtensionTests.scala b/joern-cli/frontends/swiftsrc2cpg/src/test/scala/io/joern/swiftsrc2cpg/passes/ast/ProtocolExtensionTests.scala new file mode 100644 index 000000000000..d6dc8b4ac875 --- /dev/null +++ b/joern-cli/frontends/swiftsrc2cpg/src/test/scala/io/joern/swiftsrc2cpg/passes/ast/ProtocolExtensionTests.scala @@ -0,0 +1,200 @@ +package io.joern.swiftsrc2cpg.passes.ast + +import io.shiftleft.codepropertygraph.generated.* +import io.shiftleft.codepropertygraph.generated.nodes.* +import io.shiftleft.semanticcpg.language.* + +class ProtocolExtensionTests extends AbstractPassTest { + + "ProtocolExtensionTests" should { + + "test Protocol and Extension defined afterwards" in AstFixture(""" + |public protocol A {} + |private protocol B { + | var b = 0.0 + |} + | + |protocol Foo: Bar { // implicitly internal (private) + | public var a = A() + | private var b = false + | var c = 0.0 + | var d: String? + | + | static var e = 1 + | static var f = true + | + | var g: Double { return self * 1_000.0 } + | + | init(paramA: String, paramB: Int) { + | self.init() + | } + | + | func someFunc() -> Double + | + | override internal func someMethod() { + | super.someMethod() + | } + | + | mutating func square() { + | self = self * self + | } + | + |} + | + |extension Foo: SomeProtocol, AnotherProtocol { + | var h = 0.0 + | var i: String + | static var j = 2 + | func someOtherFunc() {} + |} + | + |""".stripMargin) { cpg => + val List(typeDeclA) = cpg.typeDecl.nameExact("A").l + typeDeclA.fullName shouldBe "code.swift::A" + typeDeclA.member.l shouldBe empty + typeDeclA.inheritsFromTypeFullName.l shouldBe empty + typeDeclA.modifier.modifierType.l shouldBe List(ModifierTypes.PUBLIC) + + val List(typeDeclB) = cpg.typeDecl.nameExact("B").l + typeDeclB.fullName shouldBe "code.swift::B" + typeDeclB.member.name.l shouldBe List("b") + typeDeclB.inheritsFromTypeFullName.l shouldBe empty + typeDeclB.modifier.modifierType.l shouldBe List(ModifierTypes.PRIVATE) + val List(bConstructor) = typeDeclB.method.isConstructor.l + bConstructor.fullName shouldBe s"code.swift::B:${io.joern.x2cpg.Defines.ConstructorMethodName}" + bConstructor.block.astChildren.code.l shouldBe List("var b = 0.0") + + val List(typeDeclFoo) = cpg.typeDecl.nameExact("Foo").l + typeDeclFoo.fullName shouldBe "code.swift::Foo" + typeDeclFoo.member.name.l.sorted shouldBe List( + "a", + "b", + "c", + "d", + "e", + "f", + "g", + "h", + "i", + "j", + "someFunc", + "someMethod", + "someOtherFunc", + "square" + ) + typeDeclFoo.inheritsFromTypeFullName.l shouldBe List("AnotherProtocol", "Bar", "SomeProtocol") + typeDeclFoo.modifier.modifierType.l shouldBe List(ModifierTypes.PRIVATE) + + val List(fooConstructor) = typeDeclFoo.method.isConstructor.l + fooConstructor.fullName shouldBe "code.swift::Foo:init" + fooConstructor.block.astChildren.assignment.code.l.sorted shouldBe List( + "var a = A()", + "var b = false", + "var c = 0.0", + "var g: Double { return self * 1_000.0 }", // lowered as assignment + "var h = 0.0" + ) + + val List(fooStaticInit) = typeDeclFoo.method.nameExact(io.joern.x2cpg.Defines.StaticInitMethodName).l + fooStaticInit.fullName shouldBe s"code.swift::Foo:${io.joern.x2cpg.Defines.StaticInitMethodName}" + fooStaticInit.block.astChildren.assignment.code.l.sorted shouldBe List("var e = 1", "var f = true", "var j = 2") + } + + "test Protocol and Extension defined beforehand" in AstFixture(""" + |public protocol A {} + |private protocol B { + | var b = 0.0 + |} + | + |extension Foo: SomeProtocol, AnotherProtocol { + | var h = 0.0 + | var i: String + | static var j = 2 + | func someOtherFunc() {} + |} + | + |protocol Foo: Bar { // implicitly internal (private) + | public var a = A() + | private var b = false + | var c = 0.0 + | var d: String? + | + | static var e = 1 + | static var f = true + | + | var g: Double { return self * 1_000.0 } + | + | init(paramA: String, paramB: Int) { + | self.init() + | } + | + | func someFunc() -> Double + | + | override internal func someMethod() { + | super.someMethod() + | } + | + | mutating func square() { + | self = self * self + | } + | + |} + |""".stripMargin) { cpg => + val List(typeDeclA) = cpg.typeDecl.nameExact("A").l + typeDeclA.fullName shouldBe "code.swift::A" + typeDeclA.member.l shouldBe empty + typeDeclA.inheritsFromTypeFullName.l shouldBe empty + typeDeclA.modifier.modifierType.l shouldBe List(ModifierTypes.PUBLIC) + + val List(typeDeclB) = cpg.typeDecl.nameExact("B").l + typeDeclB.fullName shouldBe "code.swift::B" + typeDeclB.member.name.l shouldBe List("b") + typeDeclB.inheritsFromTypeFullName.l shouldBe empty + typeDeclB.modifier.modifierType.l shouldBe List(ModifierTypes.PRIVATE) + val List(bConstructor) = typeDeclB.method.isConstructor.l + bConstructor.fullName shouldBe s"code.swift::B:${io.joern.x2cpg.Defines.ConstructorMethodName}" + bConstructor.block.astChildren.code.l shouldBe List("var b = 0.0") + + val List(typeDeclFoo) = cpg.typeDecl.nameExact("Foo").l + typeDeclFoo.fullName shouldBe "code.swift::Foo" + typeDeclFoo.member.name.l.sorted shouldBe List( + "a", + "b", + "c", + "d", + "e", + "f", + "g", + "h", + "i", + "j", + "someFunc", + "someMethod", + "someOtherFunc", + "square" + ) + typeDeclFoo.inheritsFromTypeFullName.l shouldBe List("AnotherProtocol", "Bar", "SomeProtocol") + typeDeclFoo.modifier.modifierType.l shouldBe List(ModifierTypes.PRIVATE) + + val List(fooFakeConstructor) = + typeDeclFoo.method.isConstructor.nameExact(io.joern.x2cpg.Defines.ConstructorMethodName).l + fooFakeConstructor.fullName shouldBe s"code.swift::Foo:${io.joern.x2cpg.Defines.ConstructorMethodName}" + fooFakeConstructor.block.astChildren.assignment.code.l.sorted shouldBe List("var h = 0.0") + + val List(fooConstructor) = typeDeclFoo.method.isConstructor.nameExact("init").l + fooConstructor.fullName shouldBe "code.swift::Foo:init" + fooConstructor.block.astChildren.assignment.code.l.sorted shouldBe List( + "var a = A()", + "var b = false", + "var c = 0.0", + "var g: Double { return self * 1_000.0 }" // lowered as assignment + ) + + val List(fooStaticInit) = typeDeclFoo.method.nameExact(io.joern.x2cpg.Defines.StaticInitMethodName).l + fooStaticInit.fullName shouldBe s"code.swift::Foo:${io.joern.x2cpg.Defines.StaticInitMethodName}" + fooStaticInit.block.astChildren.assignment.code.l.sorted shouldBe List("var e = 1", "var f = true", "var j = 2") + } + + } + +} diff --git a/joern-cli/frontends/swiftsrc2cpg/src/test/scala/io/joern/swiftsrc2cpg/passes/ast/StructureExtensionTests.scala b/joern-cli/frontends/swiftsrc2cpg/src/test/scala/io/joern/swiftsrc2cpg/passes/ast/StructureExtensionTests.scala new file mode 100644 index 000000000000..81b341f5ffbd --- /dev/null +++ b/joern-cli/frontends/swiftsrc2cpg/src/test/scala/io/joern/swiftsrc2cpg/passes/ast/StructureExtensionTests.scala @@ -0,0 +1,200 @@ +package io.joern.swiftsrc2cpg.passes.ast + +import io.shiftleft.codepropertygraph.generated.* +import io.shiftleft.codepropertygraph.generated.nodes.* +import io.shiftleft.semanticcpg.language.* + +class StructureExtensionTests extends AbstractPassTest { + + "StructureExtensionTests" should { + + "test Structure and Extension defined afterwards" in AstFixture(""" + |public struct A {} + |private struct B { + | var b = 0.0 + |} + | + |struct Foo: Bar { // implicitly internal (private) + | public var a = A() + | private var b = false + | var c = 0.0 + | var d: String? + | + | static var e = 1 + | static var f = true + | + | var g: Double { return self * 1_000.0 } + | + | init(paramA: String, paramB: Int) { + | self.init() + | } + | + | private func someFunc() {} + | + | override internal func someMethod() { + | super.someMethod() + | } + | + | mutating func square() { + | self = self * self + | } + | + |} + | + |extension Foo: SomeProtocol, AnotherProtocol { + | var h = 0.0 + | var i: String + | static var j = 2 + | func someOtherFunc() {} + |} + | + |""".stripMargin) { cpg => + val List(typeDeclA) = cpg.typeDecl.nameExact("A").l + typeDeclA.fullName shouldBe "code.swift::A" + typeDeclA.member.l shouldBe empty + typeDeclA.inheritsFromTypeFullName.l shouldBe empty + typeDeclA.modifier.modifierType.l shouldBe List(ModifierTypes.PUBLIC) + + val List(typeDeclB) = cpg.typeDecl.nameExact("B").l + typeDeclB.fullName shouldBe "code.swift::B" + typeDeclB.member.name.l shouldBe List("b") + typeDeclB.inheritsFromTypeFullName.l shouldBe empty + typeDeclB.modifier.modifierType.l shouldBe List(ModifierTypes.PRIVATE) + val List(bConstructor) = typeDeclB.method.isConstructor.l + bConstructor.fullName shouldBe s"code.swift::B:${io.joern.x2cpg.Defines.ConstructorMethodName}" + bConstructor.block.astChildren.code.l shouldBe List("var b = 0.0") + + val List(typeDeclFoo) = cpg.typeDecl.nameExact("Foo").l + typeDeclFoo.fullName shouldBe "code.swift::Foo" + typeDeclFoo.member.name.l.sorted shouldBe List( + "a", + "b", + "c", + "d", + "e", + "f", + "g", + "h", + "i", + "j", + "someFunc", + "someMethod", + "someOtherFunc", + "square" + ) + typeDeclFoo.inheritsFromTypeFullName.l shouldBe List("AnotherProtocol", "Bar", "SomeProtocol") + typeDeclFoo.modifier.modifierType.l shouldBe List(ModifierTypes.PRIVATE) + + val List(fooConstructor) = typeDeclFoo.method.isConstructor.l + fooConstructor.fullName shouldBe "code.swift::Foo:init" + fooConstructor.block.astChildren.assignment.code.l.sorted shouldBe List( + "var a = A()", + "var b = false", + "var c = 0.0", + "var g: Double { return self * 1_000.0 }", // lowered as assignment + "var h = 0.0" + ) + + val List(fooStaticInit) = typeDeclFoo.method.nameExact(io.joern.x2cpg.Defines.StaticInitMethodName).l + fooStaticInit.fullName shouldBe s"code.swift::Foo:${io.joern.x2cpg.Defines.StaticInitMethodName}" + fooStaticInit.block.astChildren.assignment.code.l.sorted shouldBe List("var e = 1", "var f = true", "var j = 2") + } + + "test Structure and Extension defined beforehand" in AstFixture(""" + |public struct A {} + |private struct B { + | var b = 0.0 + |} + | + |extension Foo: SomeProtocol, AnotherProtocol { + | var h = 0.0 + | var i: String + | static var j = 2 + | func someOtherFunc() {} + |} + | + |struct Foo: Bar { // implicitly internal (private) + | public var a = A() + | private var b = false + | var c = 0.0 + | var d: String? + | + | static var e = 1 + | static var f = true + | + | var g: Double { return self * 1_000.0 } + | + | init(paramA: String, paramB: Int) { + | self.init() + | } + | + | private func someFunc() {} + | + | override internal func someMethod() { + | super.someMethod() + | } + | + | mutating func square() { + | self = self * self + | } + | + |} + |""".stripMargin) { cpg => + val List(typeDeclA) = cpg.typeDecl.nameExact("A").l + typeDeclA.fullName shouldBe "code.swift::A" + typeDeclA.member.l shouldBe empty + typeDeclA.inheritsFromTypeFullName.l shouldBe empty + typeDeclA.modifier.modifierType.l shouldBe List(ModifierTypes.PUBLIC) + + val List(typeDeclB) = cpg.typeDecl.nameExact("B").l + typeDeclB.fullName shouldBe "code.swift::B" + typeDeclB.member.name.l shouldBe List("b") + typeDeclB.inheritsFromTypeFullName.l shouldBe empty + typeDeclB.modifier.modifierType.l shouldBe List(ModifierTypes.PRIVATE) + val List(bConstructor) = typeDeclB.method.isConstructor.l + bConstructor.fullName shouldBe s"code.swift::B:${io.joern.x2cpg.Defines.ConstructorMethodName}" + bConstructor.block.astChildren.code.l shouldBe List("var b = 0.0") + + val List(typeDeclFoo) = cpg.typeDecl.nameExact("Foo").l + typeDeclFoo.fullName shouldBe "code.swift::Foo" + typeDeclFoo.member.name.l.sorted shouldBe List( + "a", + "b", + "c", + "d", + "e", + "f", + "g", + "h", + "i", + "j", + "someFunc", + "someMethod", + "someOtherFunc", + "square" + ) + typeDeclFoo.inheritsFromTypeFullName.l shouldBe List("AnotherProtocol", "Bar", "SomeProtocol") + typeDeclFoo.modifier.modifierType.l shouldBe List(ModifierTypes.PRIVATE) + + val List(fooFakeConstructor) = + typeDeclFoo.method.isConstructor.nameExact(io.joern.x2cpg.Defines.ConstructorMethodName).l + fooFakeConstructor.fullName shouldBe s"code.swift::Foo:${io.joern.x2cpg.Defines.ConstructorMethodName}" + fooFakeConstructor.block.astChildren.assignment.code.l.sorted shouldBe List("var h = 0.0") + + val List(fooConstructor) = typeDeclFoo.method.isConstructor.nameExact("init").l + fooConstructor.fullName shouldBe "code.swift::Foo:init" + fooConstructor.block.astChildren.assignment.code.l.sorted shouldBe List( + "var a = A()", + "var b = false", + "var c = 0.0", + "var g: Double { return self * 1_000.0 }" // lowered as assignment + ) + + val List(fooStaticInit) = typeDeclFoo.method.nameExact(io.joern.x2cpg.Defines.StaticInitMethodName).l + fooStaticInit.fullName shouldBe s"code.swift::Foo:${io.joern.x2cpg.Defines.StaticInitMethodName}" + fooStaticInit.block.astChildren.assignment.code.l.sorted shouldBe List("var e = 1", "var f = true", "var j = 2") + } + + } + +}