Repository: joernio/joern (scala)

Issue:
[Bug] c2cpg: missing buffer argument for placement new
```
joern> importCode.cpp.fromString("""
       void a() {
           char buf[80];
           new (buf) string("hi");
       }
       """)
joern> cpg.call.code.p           
res68: List[String] = List("new (buf) string(\"hi\")")
joern> cpg.call.argument.code.l 
res71: List[String] = List("string", "\"hi\"")
```

Note that there is no argument for `buf`. Can we add it as a 3rd parameter to `<operator>.new`? (It's a different order than in the source code, but it's more important that the arguments that are always there always have the same argument index.)

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

Patch:
diff --git a/joern-cli/frontends/c2cpg/src/main/scala/io/joern/c2cpg/astcreation/AstForExpressionsCreator.scala b/joern-cli/frontends/c2cpg/src/main/scala/io/joern/c2cpg/astcreation/AstForExpressionsCreator.scala
index 391eb99a5ec3..1619308a517f 100644
--- a/joern-cli/frontends/c2cpg/src/main/scala/io/joern/c2cpg/astcreation/AstForExpressionsCreator.scala
+++ b/joern-cli/frontends/c2cpg/src/main/scala/io/joern/c2cpg/astcreation/AstForExpressionsCreator.scala
@@ -203,6 +203,18 @@ trait AstForExpressionsCreator { this: AstCreator =>
     callAst(cpgCastExpression, List(Ast(arg), expr))
   }
 
+  private def astsForConstructorInitializer(initializer: IASTInitializer): List[Ast] = {
+    initializer match {
+      case init: ICPPASTConstructorInitializer => init.getArguments.toList.map(x => astForNode(x))
+      case _                                   => Nil // null or unexpected type
+    }
+  }
+
+  private def astsForInitializerPlacements(initializerPlacements: Array[IASTInitializerClause]): List[Ast] = {
+    if (initializerPlacements != null) initializerPlacements.toList.map(x => astForNode(x))
+    else Nil
+  }
+
   private def astForNewExpression(newExpression: ICPPASTNewExpression): Ast = {
     val cpgNewExpression =
       newCallNode(newExpression, "<operator>.new", "<operator>.new", DispatchTypes.STATIC_DISPATCH)
@@ -213,17 +225,8 @@ trait AstForExpressionsCreator { this: AstCreator =>
       Ast(cpgNewExpression).withChild(cpgTypeId).withArgEdge(cpgNewExpression, cpgTypeId.root.get)
     } else {
       val cpgTypeId = astForIdentifier(typeId.getDeclSpecifier)
-      val args =
-        if (
-          newExpression.getInitializer != null && newExpression.getInitializer
-            .isInstanceOf[ICPPASTConstructorInitializer]
-        ) {
-          val args = newExpression.getInitializer.asInstanceOf[ICPPASTConstructorInitializer].getArguments.toList
-          args.map(x => astForNode(x))
-        } else {
-          List()
-        }
-
+      val args = astsForConstructorInitializer(newExpression.getInitializer) ++
+        astsForInitializerPlacements(newExpression.getPlacementArguments)
       callAst(cpgNewExpression, List(cpgTypeId) ++ args)
     }
   }


Test patch:
diff --git a/joern-cli/frontends/c2cpg/src/test/scala/io/joern/c2cpg/passes/ast/AstCreationPassTests.scala b/joern-cli/frontends/c2cpg/src/test/scala/io/joern/c2cpg/passes/ast/AstCreationPassTests.scala
index 8ca2c7ecbba7..01fdd214acdd 100644
--- a/joern-cli/frontends/c2cpg/src/test/scala/io/joern/c2cpg/passes/ast/AstCreationPassTests.scala
+++ b/joern-cli/frontends/c2cpg/src/test/scala/io/joern/c2cpg/passes/ast/AstCreationPassTests.scala
@@ -1357,6 +1357,26 @@ class AstCreationPassTests extends AbstractPassTest {
       cpg.call.name("<operator>.new").code("new int\\[n\\]").argument.code("int").size shouldBe 1
     }
 
+    "be correct for 'new' with explicit identifier" in AstFixture(
+      """
+        |void a() {
+        |  char buf[80];
+        |  new (buf) string("hi");
+        |}
+        |""".stripMargin,
+      "file.cpp"
+    ) { cpg =>
+      // TODO: "<operator>.new" is not part of Operators
+      val List(newCall)         = cpg.call.name("<operator>.new").l
+      val List(string, hi, buf) = newCall.argument.l
+      string.argumentIndex shouldBe 1
+      string.code shouldBe "string"
+      hi.argumentIndex shouldBe 2
+      hi.code shouldBe "\"hi\""
+      buf.argumentIndex shouldBe 3
+      buf.code shouldBe "buf"
+    }
+
     // for: https://github.com/ShiftLeftSecurity/codepropertygraph/issues/1526
     "be correct for array size" in AstFixture("""
         |int main() {