Repository: joernio/joern (scala)

Issue:
(C/C++) Wrong LOCAL node "typeFullName" 
Here is the test code:
```c++
    int a[10], c = 2;
```
The AST is like:
![image](https://user-images.githubusercontent.com/50135026/166450588-cbd114a0-32de-4c2c-966a-fb0570e93b10.png)

The type of  LOCAL node `c` is recognized as `int []`.

Here is the attribute of `a` and `c`.

```json
      {
        "dynamicTypeHintFullName":[
          
        ],
        "name":"a",
        "typeFullName":"int[10]",
        "columnNumber":9,
        "order":1,
        "_label":"LOCAL",
        "code":"int[10] a",
        "lineNumber":24,
        "id":38
      },
      {
        "dynamicTypeHintFullName":[
          
        ],
        "name":"c",
        "typeFullName":"int",
        "columnNumber":16,
        "order":2,
        "_label":"LOCAL",
        "code":"int[] c",
        "lineNumber":24,
        "id":39
      },
```
Maybe there shouldn't be `[]` in "typeFullName" of `c` anyhow.

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

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 577b92298323..21b77a9af1aa 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
@@ -437,7 +437,7 @@ trait AstCreatorHelper {
     }
   }
 
-  protected def typeForDeclSpecifier(spec: IASTNode, stripKeywords: Boolean = true): String = {
+  protected def typeForDeclSpecifier(spec: IASTNode, stripKeywords: Boolean = true, index: Int = 0): String = {
     val tpe = spec match {
       case s: IASTSimpleDeclSpecifier if s.getParent.isInstanceOf[IASTParameterDeclaration] =>
         val parentDecl = s.getParent.asInstanceOf[IASTParameterDeclaration].getDeclarator
@@ -446,10 +446,10 @@ trait AstCreatorHelper {
         val parentDecl = s.getParent.asInstanceOf[IASTFunctionDefinition].getDeclarator
         ASTStringUtil.getReturnTypeString(s, parentDecl)
       case s: IASTSimpleDeclaration if s.getParent.isInstanceOf[ICASTKnRFunctionDeclarator] =>
-        val decl = s.getDeclarators.head
+        val decl = s.getDeclarators.toList(index)
         pointersAsString(s.getDeclSpecifier, decl, stripKeywords)
       case s: IASTSimpleDeclSpecifier if s.getParent.isInstanceOf[IASTSimpleDeclaration] =>
-        val parentDecl = s.getParent.asInstanceOf[IASTSimpleDeclaration].getDeclarators.head
+        val parentDecl = s.getParent.asInstanceOf[IASTSimpleDeclaration].getDeclarators.toList(index)
         pointersAsString(s, parentDecl, stripKeywords)
       case s: IASTSimpleDeclSpecifier =>
         ASTStringUtil.getReturnTypeString(s, null)
@@ -457,7 +457,7 @@ trait AstCreatorHelper {
         val parentDecl = s.getParent.asInstanceOf[IASTParameterDeclaration].getDeclarator
         pointersAsString(s, parentDecl, stripKeywords)
       case s: IASTNamedTypeSpecifier if s.getParent.isInstanceOf[IASTSimpleDeclaration] =>
-        val parentDecl = s.getParent.asInstanceOf[IASTSimpleDeclaration].getDeclarators.head
+        val parentDecl = s.getParent.asInstanceOf[IASTSimpleDeclaration].getDeclarators.toList(index)
         pointersAsString(s, parentDecl, stripKeywords)
       case s: IASTNamedTypeSpecifier     => ASTStringUtil.getSimpleName(s.getName)
       case s: IASTCompositeTypeSpecifier => ASTStringUtil.getSimpleName(s.getName)
@@ -466,7 +466,7 @@ trait AstCreatorHelper {
         val parentDecl = s.getParent.asInstanceOf[IASTParameterDeclaration].getDeclarator
         pointersAsString(s, parentDecl, stripKeywords)
       case s: IASTElaboratedTypeSpecifier if s.getParent.isInstanceOf[IASTSimpleDeclaration] =>
-        val parentDecl = s.getParent.asInstanceOf[IASTSimpleDeclaration].getDeclarators.head
+        val parentDecl = s.getParent.asInstanceOf[IASTSimpleDeclaration].getDeclarators.toList(index)
         pointersAsString(s, parentDecl, stripKeywords)
       case s: IASTElaboratedTypeSpecifier => ASTStringUtil.getSignatureString(s, null)
       // TODO: handle other types of IASTDeclSpecifier
diff --git a/joern-cli/frontends/c2cpg/src/main/scala/io/joern/c2cpg/astcreation/AstForStatementsCreator.scala b/joern-cli/frontends/c2cpg/src/main/scala/io/joern/c2cpg/astcreation/AstForStatementsCreator.scala
index d96a3f5cbdd0..5abdc9dcb19f 100644
--- a/joern-cli/frontends/c2cpg/src/main/scala/io/joern/c2cpg/astcreation/AstForStatementsCreator.scala
+++ b/joern-cli/frontends/c2cpg/src/main/scala/io/joern/c2cpg/astcreation/AstForStatementsCreator.scala
@@ -45,7 +45,7 @@ trait AstForStatementsCreator {
         Seq(astForFunctionDeclarator(simplDecl.getDeclarators.head.asInstanceOf[IASTFunctionDeclarator]))
       case simplDecl: IASTSimpleDeclaration =>
         val locals =
-          simplDecl.getDeclarators.toList.map { d => astForDeclarator(simplDecl, d) }
+          simplDecl.getDeclarators.zipWithIndex.toList.map { case (d, i) => astForDeclarator(simplDecl, d, i) }
         val calls =
           simplDecl.getDeclarators.filter(_.getInitializer != null).toList.map { d =>
             astForInitializer(d, d.getInitializer)
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 c522234494d0..1e6e091ef5c1 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
@@ -90,7 +90,7 @@ trait AstForTypesCreator {
     Ast(cpgNamespace)
   }
 
-  protected def astForDeclarator(declaration: IASTSimpleDeclaration, declarator: IASTDeclarator): Ast = {
+  protected def astForDeclarator(declaration: IASTSimpleDeclaration, declarator: IASTDeclarator, index: Int): Ast = {
     val declTypeName = registerType(typeForDeclSpecifier(declaration.getDeclSpecifier))
     val tpe          = typeFor(declarator)
     val name         = ASTStringUtil.getSimpleName(declarator.getName)
@@ -130,7 +130,7 @@ trait AstForTypesCreator {
         scope.addToScope(name, (l, tpe))
         Ast(l)
       case _ =>
-        val codeTpe = typeForDeclSpecifier(declaration.getDeclSpecifier, stripKeywords = false)
+        val codeTpe = typeForDeclSpecifier(declaration.getDeclSpecifier, stripKeywords = false, index)
         val l = NewLocal()
           .code(s"$codeTpe $name")
           .name(name)
@@ -236,13 +236,13 @@ trait AstForTypesCreator {
             val name     = ASTStringUtil.getSimpleName(spec.getName)
             Seq(Ast(newTypeDecl(name, registerType(name), filename, name, alias = Some(name))))
           case _ if declaration.getDeclarators.nonEmpty =>
-            declaration.getDeclarators.toIndexedSeq.map {
-              case d: IASTFunctionDeclarator =>
+            declaration.getDeclarators.toIndexedSeq.zipWithIndex.map {
+              case (d: IASTFunctionDeclarator, _) =>
                 astForFunctionDeclarator(d)
-              case d: IASTSimpleDeclaration if d.getInitializer != null =>
+              case (d: IASTSimpleDeclaration, _) if d.getInitializer != null =>
                 Ast() // we do the AST for this down below with initAsts
-              case d =>
-                astForDeclarator(declaration, d)
+              case (d, i) =>
+                astForDeclarator(declaration, d, i)
             }
           case _ if nodeSignature(declaration) == ";" =>
             Seq.empty // dangling decls from unresolved macros; we ignore them
@@ -284,8 +284,8 @@ trait AstForTypesCreator {
 
   private def astsForCompositeType(typeSpecifier: IASTCompositeTypeSpecifier, decls: List[IASTDeclarator]): Seq[Ast] = {
     val filename = fileName(typeSpecifier)
-    val declAsts = withIndex(decls) { (d, _) =>
-      astForDeclarator(typeSpecifier.getParent.asInstanceOf[IASTSimpleDeclaration], d)
+    val declAsts = decls.zipWithIndex.map { case (d, i) =>
+      astForDeclarator(typeSpecifier.getParent.asInstanceOf[IASTSimpleDeclaration], d, i)
     }
 
     val name                   = ASTStringUtil.getSimpleName(typeSpecifier.getName)
@@ -326,8 +326,8 @@ trait AstForTypesCreator {
     decls: List[IASTDeclarator]
   ): Seq[Ast] = {
     val filename = fileName(typeSpecifier)
-    val declAsts = decls.map { d =>
-      astForDeclarator(typeSpecifier.getParent.asInstanceOf[IASTSimpleDeclaration], d)
+    val declAsts = decls.zipWithIndex.map { case (d, i) =>
+      astForDeclarator(typeSpecifier.getParent.asInstanceOf[IASTSimpleDeclaration], d, i)
     }
 
     val name                   = ASTStringUtil.getSimpleName(typeSpecifier.getName)
@@ -366,7 +366,9 @@ trait AstForTypesCreator {
 
   private def astsForEnum(typeSpecifier: IASTEnumerationSpecifier, decls: List[IASTDeclarator]): Seq[Ast] = {
     val filename = fileName(typeSpecifier)
-    val declAsts = decls.map { d => astForDeclarator(typeSpecifier.getParent.asInstanceOf[IASTSimpleDeclaration], d) }
+    val declAsts = decls.zipWithIndex.map { case (d, i) =>
+      astForDeclarator(typeSpecifier.getParent.asInstanceOf[IASTSimpleDeclaration], d, i)
+    }
 
     val (name, fullname) =
       uniqueName("enum", ASTStringUtil.getSimpleName(typeSpecifier.getName), fullName(typeSpecifier))


Test patch:
diff --git a/joern-cli/frontends/c2cpg/src/test/scala/io/joern/c2cpg/querying/LocalsTests.scala b/joern-cli/frontends/c2cpg/src/test/scala/io/joern/c2cpg/querying/LocalsTests.scala
index c9ecbfc5b5e3..809afbd63e57 100644
--- a/joern-cli/frontends/c2cpg/src/test/scala/io/joern/c2cpg/querying/LocalsTests.scala
+++ b/joern-cli/frontends/c2cpg/src/test/scala/io/joern/c2cpg/querying/LocalsTests.scala
@@ -34,11 +34,12 @@ class LocalsTests extends CCodeToCpgSuite {
     | void test() {
     |   static int a, b, c;
     |   wchar_t *foo;
+    |   int d[10], e = 1;
     | }
     | """.stripMargin
 
   "should allow to query for all locals" in {
-    cpg.local.name.toSetMutable shouldBe Set("a", "b", "c", "z", "x", "q", "p", "foo")
+    cpg.local.name.toSetMutable shouldBe Set("a", "b", "c", "e", "d", "z", "x", "q", "p", "foo")
   }
 
   "should prove correct (name, type) pairs for locals" in {
@@ -53,7 +54,7 @@ class LocalsTests extends CCodeToCpgSuite {
   }
 
   "should prove correct (name, type, code) pairs for locals" in {
-    inside(cpg.method.name("test").local.l) { case List(a, b, c, foo) =>
+    inside(cpg.method.name("test").local.l) { case List(a, b, c, foo, d, e) =>
       a.name shouldBe "a"
       a.typeFullName shouldBe "int"
       a.code shouldBe "static int a"
@@ -66,6 +67,12 @@ class LocalsTests extends CCodeToCpgSuite {
       foo.name shouldBe "foo"
       foo.typeFullName shouldBe "wchar_t"
       foo.code shouldBe "wchar_t* foo"
+      d.name shouldBe "d"
+      d.typeFullName shouldBe "int[10]"
+      d.code shouldBe "int[10] d"
+      e.name shouldBe "e"
+      e.typeFullName shouldBe "int"
+      e.code shouldBe "int e"
     }
   }
 
@@ -74,4 +81,5 @@ class LocalsTests extends CCodeToCpgSuite {
     filename should not be empty
     filename.head.endsWith(".c") shouldBe true
   }
+
 }