Repository: joernio/joern (scala)

Issue:
Global Struct Initialisation Parsed Incomplete
Hello,

I think that I discovered a bug in parsing global initialised structs. Please use the following code as an example:

```
struct filesystem {
	void (*open)(int a);
};

void my_open(int a) {
	int b;
	b = a;
	return;
}

static const struct filesystem my_fs = {
	.open = &my_open,
};

int main(int argc, char *argv[]) {
	static int i;
	static const struct filesystem my_other_fs = {
		 .open = &my_open,
	};
	struct filesystem real_fs;
	real_fs.open = &my_open;
	i = 0;
}
```


The call for the initialisation of `my_other_fs` is parsed correctly:

```
joern> cpg.method("main").local.name("my_other_fs").referencingIdentifiers.astParent.isCall.argument(2).astChildren.astChildren.code.l 
res13: List[String] = List(".open = &my_open")

```

However, that's not the case for the global struct `my_fs`, e.g., no Identifier node is created:

```
joern> cpg.local("my_fs").l 
res15: List[Local] = List(
  Local(
    id -> 3074457345618260637L,
    closureBindingId -> None,
    code -> "my_fs",
    columnNumber -> None,
    dynamicTypeHintFullName -> ArraySeq(),
    lineNumber -> None,
    name -> "my_fs",
    order -> 4,
    typeFullName -> "static const filesystem"
  )
)

joern> cpg.local("my_fs").referencingIdentifiers.l 
res16: List[Identifier] = List()

joern> cpg.identifier("my_fs").l 
res17: List[Identifier] = List()
```

Maybe related to #405

Pull request:
Fixes: https://github.com/joernio/joern/issues/708

Patch:
diff --git a/joern-cli/frontends/c2cpg/src/main/scala/io/joern/c2cpg/astcreation/AstCreatorHelper.scala b/joern-cli/frontends/c2cpg/src/main/scala/io/joern/c2cpg/astcreation/AstCreatorHelper.scala
index 57934a55e7b3..1bcf95e68dc2 100644
--- a/joern-cli/frontends/c2cpg/src/main/scala/io/joern/c2cpg/astcreation/AstCreatorHelper.scala
+++ b/joern-cli/frontends/c2cpg/src/main/scala/io/joern/c2cpg/astcreation/AstCreatorHelper.scala
@@ -221,6 +221,7 @@ trait AstCreatorHelper {
       case u: IASTUnaryExpression => nodeSignature(u.getOperand)
       case e: ICPPASTElaboratedTypeSpecifier =>
         fullName(e.getParent) + "." + nodeSignature(e.getName)
+      case e: IASTElaboratedTypeSpecifier   => nodeSignature(e.getName)
       case other if other.getParent != null => fullName(other.getParent)
       case other if other != null           => notHandledYet(other, -1); ""
       case null                             => ""
diff --git a/joern-cli/frontends/c2cpg/src/main/scala/io/joern/c2cpg/astcreation/AstForTypesCreator.scala b/joern-cli/frontends/c2cpg/src/main/scala/io/joern/c2cpg/astcreation/AstForTypesCreator.scala
index 18c1cea1dd44..35bb43f065d2 100644
--- a/joern-cli/frontends/c2cpg/src/main/scala/io/joern/c2cpg/astcreation/AstForTypesCreator.scala
+++ b/joern-cli/frontends/c2cpg/src/main/scala/io/joern/c2cpg/astcreation/AstForTypesCreator.scala
@@ -208,59 +208,71 @@ trait AstForTypesCreator {
     blockAst
   }
 
-  protected def astsForDeclaration(decl: IASTDeclaration, order: Int): Seq[Ast] = decl match {
-    case u: CPPASTAliasDeclaration => Seq(astForAliasDeclaration(u, order))
-    case functDef: IASTFunctionDefinition =>
-      Seq(astForFunctionDefinition(functDef, order))
-    case declaration: IASTSimpleDeclaration
-        if declaration.getDeclSpecifier != null && declaration.getDeclSpecifier
-          .isInstanceOf[IASTCompositeTypeSpecifier] =>
-      astsForCompositeType(declaration.getDeclSpecifier.asInstanceOf[IASTCompositeTypeSpecifier],
-                           declaration.getDeclarators.toList,
-                           order)
-    case declaration: IASTSimpleDeclaration
-        if declaration.getDeclSpecifier != null && declaration.getDeclSpecifier
-          .isInstanceOf[IASTEnumerationSpecifier] =>
-      astsForEnum(declaration.getDeclSpecifier.asInstanceOf[IASTEnumerationSpecifier],
-                  declaration.getDeclarators.toList,
-                  order)
-    case declaration: IASTSimpleDeclaration
-        if declaration.getDeclSpecifier != null && declaration.getDeclSpecifier
-          .isInstanceOf[IASTElaboratedTypeSpecifier] =>
-      astsForElaboratedType(declaration.getDeclSpecifier.asInstanceOf[IASTElaboratedTypeSpecifier],
-                            declaration.getDeclarators.toList,
-                            order)
-
-    case declaration: IASTSimpleDeclaration
-        if declaration.getDeclSpecifier != null && declaration.getDeclSpecifier
-          .isInstanceOf[IASTNamedTypeSpecifier] && declaration.getDeclarators.isEmpty =>
-      val spec = declaration.getDeclSpecifier.asInstanceOf[IASTNamedTypeSpecifier]
-      val filename = fileName(spec)
-      val name = nodeSignature(spec.getName)
-      Seq(Ast(newTypeDecl(name, registerType(name), filename, alias = Some(name), order = order)))
-    case declaration: IASTSimpleDeclaration if declaration.getDeclarators.nonEmpty =>
-      declaration.getDeclarators.toIndexedSeq.map {
-        case d: IASTFunctionDeclarator     => astForFunctionDeclarator(d, order)
-        case d if d.getInitializer != null => astForInitializer(d, d.getInitializer, order)
-        case d                             => astForDeclarator(declaration, d, order)
-      }
-    case namespaceAlias: ICPPASTNamespaceAlias           => Seq(astForNamespaceAlias(namespaceAlias, order))
-    case namespaceDefinition: ICPPASTNamespaceDefinition => Seq(astForNamespaceDefinition(namespaceDefinition, order))
-    case _: ICPPASTVisibilityLabel                       => Seq.empty
-    case usingDecl: ICPPASTUsingDeclaration =>
-      handleUsingDeclaration(usingDecl)
-      Seq.empty
-    case _: ICPPASTUsingDirective =>
-      Seq.empty
-    case _: ICPPASTExplicitTemplateInstantiation             => Seq.empty
-    case s: IASTSimpleDeclaration if nodeSignature(s) == ";" => Seq.empty
-    case l: ICPPASTLinkageSpecification                      => astsForLinkageSpecification(l)
-    case t: ICPPASTTemplateDeclaration                       => astsForDeclaration(t.getDeclaration, order)
-    case a: ICPPASTStaticAssertDeclaration                   => Seq(astForStaticAssert(a, order))
-    case asm: IASTASMDeclaration                             => Seq(astForASMDeclaration(asm, order))
-    case structuredBindingDeclaration: ICPPASTStructuredBindingDeclaration =>
-      Seq(astForStructuredBindingDeclaration(structuredBindingDeclaration, order))
-    case _ => Seq(astForNode(decl, order))
+  protected def astsForDeclaration(decl: IASTDeclaration, order: Int): Seq[Ast] = {
+    val declAsts = decl match {
+      case u: CPPASTAliasDeclaration => Seq(astForAliasDeclaration(u, order))
+      case functDef: IASTFunctionDefinition =>
+        Seq(astForFunctionDefinition(functDef, order))
+      case declaration: IASTSimpleDeclaration
+          if declaration.getDeclSpecifier != null && declaration.getDeclSpecifier
+            .isInstanceOf[IASTCompositeTypeSpecifier] =>
+        astsForCompositeType(declaration.getDeclSpecifier.asInstanceOf[IASTCompositeTypeSpecifier],
+                             declaration.getDeclarators.toList,
+                             order)
+      case declaration: IASTSimpleDeclaration
+          if declaration.getDeclSpecifier != null && declaration.getDeclSpecifier
+            .isInstanceOf[IASTEnumerationSpecifier] =>
+        astsForEnum(declaration.getDeclSpecifier.asInstanceOf[IASTEnumerationSpecifier],
+                    declaration.getDeclarators.toList,
+                    order)
+      case declaration: IASTSimpleDeclaration
+          if declaration.getDeclSpecifier != null && declaration.getDeclSpecifier
+            .isInstanceOf[IASTElaboratedTypeSpecifier] =>
+        astsForElaboratedType(declaration.getDeclSpecifier.asInstanceOf[IASTElaboratedTypeSpecifier],
+                              declaration.getDeclarators.toList,
+                              order)
+
+      case declaration: IASTSimpleDeclaration
+          if declaration.getDeclSpecifier != null && declaration.getDeclSpecifier
+            .isInstanceOf[IASTNamedTypeSpecifier] && declaration.getDeclarators.isEmpty =>
+        val spec = declaration.getDeclSpecifier.asInstanceOf[IASTNamedTypeSpecifier]
+        val filename = fileName(spec)
+        val name = nodeSignature(spec.getName)
+        Seq(Ast(newTypeDecl(name, registerType(name), filename, alias = Some(name), order = order)))
+      case declaration: IASTSimpleDeclaration if declaration.getDeclarators.nonEmpty =>
+        declaration.getDeclarators.toIndexedSeq.map {
+          case d: IASTFunctionDeclarator => astForFunctionDeclarator(d, order)
+          case d                         => astForDeclarator(declaration, d, order)
+        }
+      case namespaceAlias: ICPPASTNamespaceAlias           => Seq(astForNamespaceAlias(namespaceAlias, order))
+      case namespaceDefinition: ICPPASTNamespaceDefinition => Seq(astForNamespaceDefinition(namespaceDefinition, order))
+      case _: ICPPASTVisibilityLabel                       => Seq.empty
+      case usingDecl: ICPPASTUsingDeclaration =>
+        handleUsingDeclaration(usingDecl)
+        Seq.empty
+      case _: ICPPASTUsingDirective =>
+        Seq.empty
+      case _: ICPPASTExplicitTemplateInstantiation             => Seq.empty
+      case s: IASTSimpleDeclaration if nodeSignature(s) == ";" => Seq.empty
+      case l: ICPPASTLinkageSpecification                      => astsForLinkageSpecification(l)
+      case t: ICPPASTTemplateDeclaration                       => astsForDeclaration(t.getDeclaration, order)
+      case a: ICPPASTStaticAssertDeclaration                   => Seq(astForStaticAssert(a, order))
+      case asm: IASTASMDeclaration                             => Seq(astForASMDeclaration(asm, order))
+      case structuredBindingDeclaration: ICPPASTStructuredBindingDeclaration =>
+        Seq(astForStructuredBindingDeclaration(structuredBindingDeclaration, order))
+      case _ => Seq(astForNode(decl, order))
+    }
+
+    val initAsts = decl match {
+      case declaration: IASTSimpleDeclaration if declaration.getDeclarators.nonEmpty =>
+        withOrder(declaration.getDeclarators) {
+          case (d: IASTDeclarator, o) if d.getInitializer != null =>
+            astForInitializer(d, d.getInitializer, order + o)
+          case _ => Ast()
+        }
+      case _ => Nil
+    }
+    declAsts ++ initAsts
   }
 
   private def astsForLinkageSpecification(l: ICPPASTLinkageSpecification): Seq[Ast] =


Test patch:
diff --git a/joern-cli/frontends/c2cpg/src/test/scala/io/joern/c2cpg/passes/AstCreationPassTests.scala b/joern-cli/frontends/c2cpg/src/test/scala/io/joern/c2cpg/passes/AstCreationPassTests.scala
index c8adbe0ac961..02ebc1117f7b 100644
--- a/joern-cli/frontends/c2cpg/src/test/scala/io/joern/c2cpg/passes/AstCreationPassTests.scala
+++ b/joern-cli/frontends/c2cpg/src/test/scala/io/joern/c2cpg/passes/AstCreationPassTests.scala
@@ -140,18 +140,13 @@ class AstCreationPassTests extends AnyWordSpec with Matchers with CpgAstOnlyFixt
       val lambda1FullName = "anonymous_lambda_0"
       val lambda2FullName = "anonymous_lambda_1"
 
-      cpg.assignment.order(1).l match {
-        case List(assignment1) =>
+      cpg.assignment.l match {
+        case List(assignment1, assignment2) =>
           assignment1.astMinusRoot.isMethodRef.l match {
             case List(ref) =>
               ref.methodFullName shouldBe lambda1FullName
             case _ => fail()
           }
-        case _ => fail()
-      }
-
-      cpg.assignment.order(2).l match {
-        case List(assignment2) =>
           assignment2.astMinusRoot.isMethodRef.l match {
             case List(ref) =>
               ref.methodFullName shouldBe lambda2FullName
@@ -207,7 +202,7 @@ class AstCreationPassTests extends AnyWordSpec with Matchers with CpgAstOnlyFixt
       val lambdaFullName = "Foo.anonymous_lambda_0"
       val signature = "int Foo.anonymous_lambda_0 (int,int)"
 
-      cpg.assignment.order(1).l match {
+      cpg.assignment.l match {
         case List(assignment1) =>
           assignment1.astMinusRoot.isMethodRef.l match {
             case List(ref) =>
@@ -257,7 +252,7 @@ class AstCreationPassTests extends AnyWordSpec with Matchers with CpgAstOnlyFixt
       val lambdaFullName = "A.B.Foo.anonymous_lambda_0"
       val signature = "int A.B.Foo.anonymous_lambda_0 (int,int)"
 
-      cpg.assignment.order(1).l match {
+      cpg.assignment.l match {
         case List(assignment1) =>
           assignment1.astMinusRoot.isMethodRef.l match {
             case List(ref) =>
@@ -309,8 +304,8 @@ class AstCreationPassTests extends AnyWordSpec with Matchers with CpgAstOnlyFixt
       val signature1 = "int anonymous_lambda_0 (int)"
       val lambda2Name = "anonymous_lambda_1"
 
-      cpg.assignment.order(1).l match {
-        case List(assignment1) =>
+      cpg.assignment.l match {
+        case List(assignment1, _, _) =>
           assignment1.astMinusRoot.isMethodRef.l match {
             case List(ref) =>
               ref.methodFullName shouldBe lambda1Name
@@ -1128,6 +1123,38 @@ class AstCreationPassTests extends AnyWordSpec with Matchers with CpgAstOnlyFixt
       cpg.typeDecl.name("abc").aliasTypeFullName("foo").size shouldBe 1
     }
 
+    "be correct for global struct" in TestAstOnlyFixture(
+      """
+        |struct filesystem {
+        |	void (*open)(int a);
+        |};
+        |
+        |void my_open(int a) {
+        |	int b;
+        |	b = a;
+        |	return;
+        |}
+        |
+        |static const struct filesystem my_fs = {
+        |	.open = &my_open,
+        |};
+        |
+        |int main(int argc, char *argv[]) {
+        |	static int i;
+        |	static const struct filesystem my_other_fs = {
+        |		 .open = &my_open,
+        |	};
+        |	struct filesystem real_fs;
+        |	real_fs.open = &my_open;
+        |	i = 0;
+        |}
+      """.stripMargin
+    ) { cpg =>
+      cpg.method("main").local.name("my_other_fs").referencingIdentifiers.name.l shouldBe List("my_other_fs")
+      cpg.local.name("my_fs").referencingIdentifiers.name.l shouldBe List("my_fs")
+      cpg.typeDecl.fullName.l.distinct shouldBe List("my_open", "main", "filesystem")
+    }
+
     "be correct for typedef enum" in TestAstOnlyFixture(
       """
         |typedef enum foo {