Repository: joernio/joern (scala)

Issue:
newc: `TypeDeclFullName` fields on `TYPE` nodes contain modifiers/pointers
I noticed that the `TypeDeclFullName` field of `TYPE` nodes doesn't just contain the name of the TypeDecl but rather the name of the type, e.g., for `struct Foo *ptr`, it contains `Foo*` rather than just `Foo`. In consequence, a TypeDecl with full name `Foo*` is created by the `TypeUsagePass` rather than linking to the TypeDecl named `Foo`.

Example:

```
joern> importCode.c.fromString("""
       struct Foo {
         int x;
       };
       int foo() {
        struct Foo *ptr;
       }
       """
       ) 

joern> cpg.local.typ.referencedTypeDecl.l 
res23: List[TypeDecl] = List(
  TypeDecl(
    id -> 3074457345618258686L,
    aliasTypeFullName -> None,
    astParentFullName -> "<global>",
    astParentType -> "NAMESPACE_BLOCK",
    code -> "<empty>",
    columnNumber -> None,
    filename -> "<unknown>",
    fullName -> "Foo*",
    inheritsFromTypeFullName -> ArraySeq(),
    isExternal -> true,
    lineNumber -> None,
    name -> "Foo*",
    order -> -1
  )
)
```

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

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 105a184ff32a..0c31536c3d2a 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
@@ -135,7 +135,7 @@ trait AstCreatorHelper {
     case t if t.contains(Defines.qualifiedNameSeparator) =>
       fixQualifiedName(t).split(".").lastOption.getOrElse(Defines.anyTypeName)
     case t if t.contains("[") && t.contains("]") => t.replace(" ", "")
-    case t if t.contains("*")                    => t.replace(" ", "")
+    case t if t.contains("*")                    => t.replace("*", "").replace(" ", "")
     case someType                                => someType
   }
 
diff --git a/joern-cli/src/main/resources/scripts/c/pointer-to-int.sc b/joern-cli/src/main/resources/scripts/c/pointer-to-int.sc
index 3585b55f762c..f777abb03f52 100644
--- a/joern-cli/src/main/resources/scripts/c/pointer-to-int.sc
+++ b/joern-cli/src/main/resources/scripts/c/pointer-to-int.sc
@@ -8,7 +8,9 @@ import overflowdb.traversal._
 private def expressionIsPointer(argument: Expression, isSubExpression: Boolean = false): Boolean = {
   argument match {
     case identifier: Identifier =>
-      identifier.typeFullName.endsWith("*") || identifier.typeFullName.endsWith("]")
+      identifier.typeFullName.endsWith("*") ||
+        identifier.typeFullName.endsWith("]") ||
+        cpg.local(identifier.name).l.headOption.exists(_.code.contains("*"))
     case call: Call if call.name == Operators.indirectFieldAccess => // On '->', only check the selected field.
       expressionIsPointer(call.start.argument.l.last, isSubExpression = true)
     case call: Call => // On normal nested call, check all arguments are also pointers.


Test patch:
diff --git a/joern-cli/frontends/c2cpg/src/test/scala/io/joern/c2cpg/passes/TypeNodePassTests.scala b/joern-cli/frontends/c2cpg/src/test/scala/io/joern/c2cpg/passes/TypeNodePassTests.scala
index 6d2817b1afd7..31f9eed66636 100644
--- a/joern-cli/frontends/c2cpg/src/test/scala/io/joern/c2cpg/passes/TypeNodePassTests.scala
+++ b/joern-cli/frontends/c2cpg/src/test/scala/io/joern/c2cpg/passes/TypeNodePassTests.scala
@@ -43,8 +43,23 @@ class TypeNodePassTests extends AnyWordSpec with Matchers with Inside with CpgTy
         |}
         |""".stripMargin) { cpg =>
       inside(cpg.call("free").argument(1).l) { case List(arg) =>
-        arg.evalType.l shouldBe List("test*")
+        arg.evalType.l shouldBe List("test")
         arg.code shouldBe "ptr"
+        inside(arg.typ.referencedTypeDecl.l) { case List(tpe) =>
+          tpe.fullName shouldBe "test"
+          tpe.name shouldBe "test"
+          tpe.code shouldBe "struct test"
+        }
+        inside(cpg.local.l) { case List(ptr) =>
+          ptr.name shouldBe "ptr"
+          ptr.typeFullName shouldBe "test"
+          ptr.code shouldBe "struct test* ptr"
+        }
+        inside(cpg.local.typ.referencedTypeDecl.l) { case List(tpe) =>
+          tpe.fullName shouldBe "test"
+          tpe.name shouldBe "test"
+          tpe.code shouldBe "struct test"
+        }
       }
     }
 
@@ -58,6 +73,20 @@ class TypeNodePassTests extends AnyWordSpec with Matchers with Inside with CpgTy
         tpe shouldBe "uint8_t[1]"
       }
     }
+
+    "create correct types for locals with struct type" in CpgTypeNodeFixture("""
+        |struct Foo {
+        |  int x;
+        |};
+        |
+        |int foo() {
+        | struct Foo *ptr;
+        |}
+        |""".stripMargin) { cpg =>
+      inside(cpg.local.typ.referencedTypeDecl.l) { case List(tpe) =>
+        tpe.fullName shouldBe "Foo"
+      }
+    }
   }
 
 }
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 facb5ad2c20b..9dab260f7b2f 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
@@ -42,10 +42,10 @@ class LocalsTests extends CCodeToCpgSuite {
   "should prove correct (name, type) pairs for locals" in {
     inside(cpg.method.name("free_list").local.l) { case List(q, p) =>
       q.name shouldBe "q"
-      q.typeFullName shouldBe "node*"
+      q.typeFullName shouldBe "node"
       q.code shouldBe "struct node* q"
       p.name shouldBe "p"
-      p.typeFullName shouldBe "node*"
+      p.typeFullName shouldBe "node"
       p.code shouldBe "struct node* p"
     }
   }