Repository: ShiftLeftSecurity/codepropertygraph (language: python)

Issue:
namespace c++ keyword causes unhandled nodes
Just started testing the new c2cpg parser after looking at this Github issue from joern: https://github.com/joernio/joern/issues/533

**Problem**
Generating a cpg for a C program using the C++ keyword 'namespace' generates preprocessor errors and problems

**Expected behavior**
I expect for it to proceed with the parsing without any issues. 

**Actual behavior**
I noticed with this basic testcase, it fails to handle an integer with the variable name "namespace" and shows there to be a preprocessor error and 1 problem. 

Here is the output from the c2cpg script for my sample testcase:

```
$ ./c2cpg.sh /tmp/testcase
[INFO ] Output file exists, removing: cpg.bin
[INFO ] initializing 0 nodes from existing storage - this may take some time
[INFO ] installed GC monitors. will clear references if heap (after GC) is larger than 80%
[INFO ] Start of enhancement: io.shiftleft.semanticcpg.passes.metadata.MetaDataPass
[INFO ] Enhancement io.shiftleft.semanticcpg.passes.metadata.MetaDataPass completed in 55 milliseconds
[INFO ] Start of enhancement: io.shiftleft.c2cpg.passes.AstCreationPass
[INFO ] Parsed '/tmp/testcase/testcase.c' in 294.4 ms (1 preprocessor error(s), 1 problems)
[WARN ] Node 'CPPASTNamespaceAlias' not handled yet!
  Code: 'namespace = argc;'
  File: '/tmp/testcase/testcase.c'
  Line: 5

[INFO ] Enhancement io.shiftleft.c2cpg.passes.AstCreationPass completed in 408 milliseconds
[INFO ] Start of enhancement: io.shiftleft.semanticcpg.passes.CfgCreationPass
[INFO ] Enhancement io.shiftleft.semanticcpg.passes.CfgCreationPass completed in 31 milliseconds
[INFO ] Start of enhancement: io.shiftleft.c2cpg.passes.StubRemovalPass
[INFO ] Enhancement io.shiftleft.c2cpg.passes.StubRemovalPass completed in 2 milliseconds
[INFO ] Start of enhancement: io.shiftleft.semanticcpg.passes.typenodes.TypeNodePass
[INFO ] Enhancement io.shiftleft.semanticcpg.passes.typenodes.TypeNodePass completed in 3 milliseconds
[INFO ] progress of clearing references: 100.00%
[INFO ] cleared all clearable references
```

testcase.c

```
#include <stdio.h>

int main(int argc, char **argv) {
        int namespace;
        namespace = argc;
        return 0;
}
```

Please let me know if there's anything else needed here as I'm going to assume that this will likely have the same issue with other keywords.


Discussion / hints:
For reference, in this testcase it generates a CPPAstNamespaceAlias error, but in another codebase it generates the following:

```
[WARN ] Parse problem 'CPPASTProblem' occurred!
  Code: 'int namespace;'
  File: '/home/jgalindo/GitProjects/codebase.c'
  Line: 3511
```

I'm leaving out the codebase, but if it's necessary let me know.
I can confirm this issue. Thanks for the report. 
It will be fixed soon (tm) whenever I got time for it.

Background: we internally use the Eclipse CDT preprocessor and parser but currently in CPP mode only.
As your code is valid C but not C++ (try it with https://www.onlinegdb.com/online_c++_compiler for example) where namespace is a reserved keyword this simple approach will fail. In the future I will run the appropriate parser of course. 

Fix patch:
diff --git a/c2cpg/src/main/scala/io/shiftleft/c2cpg/C2Cpg.scala b/c2cpg/src/main/scala/io/shiftleft/c2cpg/C2Cpg.scala
index 9f120c63a..418d15d7a 100644
--- a/c2cpg/src/main/scala/io/shiftleft/c2cpg/C2Cpg.scala
+++ b/c2cpg/src/main/scala/io/shiftleft/c2cpg/C2Cpg.scala
@@ -1,7 +1,7 @@
 package io.shiftleft.c2cpg
 
 import io.shiftleft.c2cpg.C2Cpg.Config
-import io.shiftleft.c2cpg.parser.ParseConfig
+import io.shiftleft.c2cpg.parser.{FileDefaults, ParseConfig}
 import io.shiftleft.c2cpg.passes.{AstCreationPass, StubRemovalPass}
 import io.shiftleft.codepropertygraph.Cpg
 import io.shiftleft.codepropertygraph.generated.Languages
@@ -58,7 +58,7 @@ object C2Cpg {
 
   final case class Config(inputPaths: Set[String] = Set.empty,
                           outputPath: String = X2CpgConfig.defaultOutputPath,
-                          sourceFileExtensions: Set[String] = Set(".c", ".cc", ".cpp", ".h", ".hpp"),
+                          sourceFileExtensions: Set[String] = FileDefaults.SOURCE_FILE_EXTENSIONS,
                           includePaths: Set[String] = Set.empty,
                           defines: Set[String] = Set.empty,
                           includeComments: Boolean = false,
diff --git a/c2cpg/src/main/scala/io/shiftleft/c2cpg/parser/CdtParser.scala b/c2cpg/src/main/scala/io/shiftleft/c2cpg/parser/CdtParser.scala
index 81eb231d4..6d55686bd 100644
--- a/c2cpg/src/main/scala/io/shiftleft/c2cpg/parser/CdtParser.scala
+++ b/c2cpg/src/main/scala/io/shiftleft/c2cpg/parser/CdtParser.scala
@@ -2,6 +2,7 @@ package io.shiftleft.c2cpg.parser
 
 import io.shiftleft.c2cpg.utils.TimeUtils
 import org.eclipse.cdt.core.dom.ast.IASTTranslationUnit
+import org.eclipse.cdt.core.dom.ast.gnu.c.GCCLanguage
 import org.eclipse.cdt.core.dom.ast.gnu.cpp.GPPLanguage
 import org.eclipse.cdt.core.model.ILanguage
 import org.eclipse.cdt.core.parser.{DefaultLogService, FileContent, ScannerInfo}
@@ -37,11 +38,20 @@ class CdtParser(private val parseConfig: ParseConfig) extends ParseProblemsLogge
   // enables parsing of code behind disabled preprocessor defines:
   private val opts: Int = ILanguage.OPTION_PARSE_INACTIVE_CODE
 
+  private def createParseLanguage(file: Path): ILanguage = {
+    val fileAsString = file.toString
+    if (fileAsString.endsWith(FileDefaults.CPP_EXT) || fileAsString.endsWith(FileDefaults.CPP_HEADER_EXT)) {
+      new GPPLanguage()
+    } else {
+      new GCCLanguage()
+    }
+  }
+
   private def parseInternal(file: Path): (ParseResult, String) = {
     val (results, duration) = TimeUtils.time {
       val fileContent = FileContent.createForExternalFileLocation(file.toString)
       val fileContentProvider = new CustomFileContentProvider()
-      val lang = new GPPLanguage()
+      val lang = createParseLanguage(file)
       Try(
         lang.getASTTranslationUnit(fileContent, info, fileContentProvider, null, opts, log)
       ) match {
diff --git a/c2cpg/src/main/scala/io/shiftleft/c2cpg/parser/FileDefaults.scala b/c2cpg/src/main/scala/io/shiftleft/c2cpg/parser/FileDefaults.scala
new file mode 100644
index 000000000..b9be9e8d0
--- /dev/null
+++ b/c2cpg/src/main/scala/io/shiftleft/c2cpg/parser/FileDefaults.scala
@@ -0,0 +1,13 @@
+package io.shiftleft.c2cpg.parser
+
+object FileDefaults {
+
+  val C_EXT = ".c"
+  val CC_EXT = ".cc"
+  val CPP_EXT = ".cpp"
+  val C_HEADER_EXT = ".h"
+  val CPP_HEADER_EXT = ".hpp"
+
+  val SOURCE_FILE_EXTENSIONS = Set(C_EXT, CC_EXT, CPP_EXT, C_HEADER_EXT, CPP_HEADER_EXT)
+
+}
diff --git a/c2cpg/src/main/scala/io/shiftleft/c2cpg/passes/AstCreator.scala b/c2cpg/src/main/scala/io/shiftleft/c2cpg/passes/AstCreator.scala
index 6aeb62968..edcdee753 100644
--- a/c2cpg/src/main/scala/io/shiftleft/c2cpg/passes/AstCreator.scala
+++ b/c2cpg/src/main/scala/io/shiftleft/c2cpg/passes/AstCreator.scala
@@ -18,6 +18,7 @@ import org.eclipse.cdt.internal.core.dom.parser.cpp.{
   CPPASTTypeIdInitializerExpression,
   CPPFunction
 }
+import org.eclipse.cdt.internal.core.model.ASTStringUtil
 import org.slf4j.LoggerFactory
 
 import java.io.{BufferedReader, FileInputStream, InputStreamReader}
@@ -76,7 +77,9 @@ class AstCreator(filename: String, global: Global, config: C2Cpg.Config) {
       } else if (nullSafeFileLocation(node).map(_.getNodeOffset).contains(0)) {
         Some(0)
       } else {
-        nullSafeFileLocation(node).map(_.getNodeOffset - 1 - fileLines.slice(0, l).sum)
+        val slice = fileLines.slice(0, l)
+        val length = slice.size - 1
+        nullSafeFileLocation(node).map(_.getNodeOffset - 1 - slice.sum - length)
       }
     }
   }
@@ -90,7 +93,9 @@ class AstCreator(filename: String, global: Global, config: C2Cpg.Config) {
       } else if (nullSafeLastNodeLocation(node).map(_.getNodeOffset).contains(0)) {
         Some(0)
       } else {
-        nullSafeLastNodeLocation(node).map(_.getNodeOffset - 1 - fileLines.slice(0, l).sum)
+        val slice = fileLines.slice(0, l)
+        val length = slice.size - 1
+        nullSafeLastNodeLocation(node).map(_.getNodeOffset - 1 - slice.sum - length)
       }
     }
   }
@@ -196,7 +201,7 @@ class AstCreator(filename: String, global: Global, config: C2Cpg.Config) {
         fullName(f.getParent) + "." + f.getDeclarator.getName.toString
       case f: IASTFunctionDefinition =>
         f.getDeclarator.getName.toString
-      case d: CPPASTIdExpression            => d.getName.toString
+      case d: IASTIdExpression              => d.getName.toString
       case _: IASTTranslationUnit           => ""
       case u: IASTUnaryExpression           => u.getOperand.toString
       case other if other.getParent != null => fullName(other.getParent)
@@ -223,7 +228,7 @@ class AstCreator(filename: String, global: Global, config: C2Cpg.Config) {
           case other =>
             other.getName
         }
-      case d: CPPASTIdExpression  => lastNameOfQualifiedName(d.getName.toString)
+      case d: IASTIdExpression    => lastNameOfQualifiedName(d.getName.toString)
       case u: IASTUnaryExpression => shortName(u.getOperand)
       case other                  => notHandledYet(other, -1); ""
     }
@@ -349,7 +354,10 @@ class AstCreator(filename: String, global: Global, config: C2Cpg.Config) {
   }
 
   private def astForIdentifier(ident: IASTNode, order: Int): Ast = {
-    val identifierName = ident.toString
+    val identifierName = ident match {
+      case id: IASTIdExpression => ASTStringUtil.getSimpleName(id.getName)
+      case _                    => ident.toString
+    }
     val variableOption = scope.lookupVariable(identifierName)
     val identifierTypeName = variableOption match {
       case Some((_, variableTypeName)) =>
@@ -1032,27 +1040,26 @@ class AstCreator(filename: String, global: Global, config: C2Cpg.Config) {
     spec match {
       case s: IASTSimpleDeclSpecifier if s.getParent.isInstanceOf[IASTParameterDeclaration] =>
         val parentDecl = s.getParent.asInstanceOf[IASTParameterDeclaration].getDeclarator
+        val nodeAsString = ASTStringUtil.getReturnTypeString(s, null)
         val pointers = parentDecl.getPointerOperators
-        if (pointers.isEmpty) { s"${s.toString}" } else {
-          s"${s.toString} ${"* " * pointers.size}".strip()
+        if (pointers.isEmpty) { nodeAsString } else {
+          s"$nodeAsString ${"* " * pointers.size}".strip()
         }
       case s: IASTSimpleDeclSpecifier if s.getParent.isInstanceOf[IASTFunctionDefinition] =>
         val parentDecl = s.getParent.asInstanceOf[IASTFunctionDefinition].getDeclarator
-        val pointers = parentDecl.getPointerOperators
-        if (pointers.isEmpty) { s"${s.toString}" } else {
-          s"${s.toString} ${"* " * pointers.size}".strip()
-        }
-      case s: IASTSimpleDeclSpecifier    => s.toString
+        ASTStringUtil.getReturnTypeString(s, parentDecl)
+      case s: IASTSimpleDeclSpecifier    => ASTStringUtil.getReturnTypeString(s, null)
       case s: IASTNamedTypeSpecifier     => s.getName.toString
       case s: IASTCompositeTypeSpecifier => s.getName.toString
       case s: IASTEnumerationSpecifier   => s.getName.toString
       case s: IASTElaboratedTypeSpecifier if s.getParent.isInstanceOf[IASTSimpleDeclaration] =>
         val parentDecl = s.getParent.asInstanceOf[IASTSimpleDeclaration].getDeclarators.head
         val pointers = parentDecl.getPointerOperators
-        if (pointers.isEmpty) { s"${s.toString}" } else {
-          s"${s.toString} ${"* " * pointers.size}".strip()
+        val nodeAsString = ASTStringUtil.getReturnTypeString(s, null)
+        if (pointers.isEmpty) { nodeAsString } else {
+          s"$nodeAsString ${"* " * pointers.size}".strip()
         }
-      case s: IASTElaboratedTypeSpecifier => s.toString
+      case s: IASTElaboratedTypeSpecifier => ASTStringUtil.getSignatureString(s, null)
       // TODO: handle other types of IASTDeclSpecifier
       case _ => Defines.anyTypeName
     }


Test patch:
diff --git a/c2cpg-tests/src/test/scala/io/shiftleft/c2cpg/standard/CallTests.scala b/c2cpg-tests/src/test/scala/io/shiftleft/c2cpg/standard/CallTests.scala
index 63a3648d0..d279ff1b2 100644
--- a/c2cpg-tests/src/test/scala/io/shiftleft/c2cpg/standard/CallTests.scala
+++ b/c2cpg-tests/src/test/scala/io/shiftleft/c2cpg/standard/CallTests.scala
@@ -27,7 +27,7 @@ class CallTests extends CCodeToCpgSuite {
     // TODO x.signature
     // x.typeFullName : deprecated
     x.lineNumber shouldBe Some(6)
-    x.columnNumber shouldBe Some(21)
+    x.columnNumber shouldBe Some(17)
   }
 
   "should allow traversing from call to arguments" in {
diff --git a/c2cpg-tests/src/test/scala/io/shiftleft/c2cpg/standard/ControlStructureTests.scala b/c2cpg-tests/src/test/scala/io/shiftleft/c2cpg/standard/ControlStructureTests.scala
index 4e32e484e..9564366da 100644
--- a/c2cpg-tests/src/test/scala/io/shiftleft/c2cpg/standard/ControlStructureTests.scala
+++ b/c2cpg-tests/src/test/scala/io/shiftleft/c2cpg/standard/ControlStructureTests.scala
@@ -1,9 +1,10 @@
 package io.shiftleft.c2cpg.standard
 
+import io.shiftleft.c2cpg.parser.FileDefaults
 import io.shiftleft.c2cpg.testfixtures.CCodeToCpgSuite
 import io.shiftleft.semanticcpg.language._
 
-class ControlStructureTests extends CCodeToCpgSuite {
+class ControlStructureTests extends CCodeToCpgSuite(FileDefaults.CPP_EXT) {
 
   override val code: String =
     """
diff --git a/c2cpg-tests/src/test/scala/io/shiftleft/c2cpg/standard/MethodReturnTests.scala b/c2cpg-tests/src/test/scala/io/shiftleft/c2cpg/standard/MethodReturnTests.scala
index 1ce56f946..69cd1bb35 100644
--- a/c2cpg-tests/src/test/scala/io/shiftleft/c2cpg/standard/MethodReturnTests.scala
+++ b/c2cpg-tests/src/test/scala/io/shiftleft/c2cpg/standard/MethodReturnTests.scala
@@ -12,8 +12,8 @@ class MethodReturnTests extends CCodeToCpgSuite {
 
   "should have METHOD_RETURN node with correct fields" in {
     val List(x) = cpg.methodReturn.l
-    x.code shouldBe "int *"
-    x.typeFullName shouldBe "int *"
+    x.code shouldBe "int*"
+    x.typeFullName shouldBe "int*"
     x.lineNumber shouldBe Some(2)
     x.columnNumber shouldBe Some(1)
     // we expect the METHOD_RETURN node to be the right-most
diff --git a/c2cpg-tests/src/test/scala/io/shiftleft/c2cpg/standard/MethodTests.scala b/c2cpg-tests/src/test/scala/io/shiftleft/c2cpg/standard/MethodTests.scala
index 8fe9be7f8..971c8fc35 100644
--- a/c2cpg-tests/src/test/scala/io/shiftleft/c2cpg/standard/MethodTests.scala
+++ b/c2cpg-tests/src/test/scala/io/shiftleft/c2cpg/standard/MethodTests.scala
@@ -19,7 +19,6 @@ class MethodTests extends CCodeToCpgSuite {
     x.isExternal shouldBe false
     x.order shouldBe 1
     x.filename.startsWith("/") shouldBe true
-    x.filename.endsWith(".c") shouldBe true
     x.lineNumber shouldBe Some(2)
     x.lineNumberEnd shouldBe Some(3)
     x.columnNumber shouldBe Some(2)
diff --git a/c2cpg-tests/src/test/scala/io/shiftleft/c2cpg/standard/TypeDeclTests.scala b/c2cpg-tests/src/test/scala/io/shiftleft/c2cpg/standard/TypeDeclTests.scala
index d77dc59aa..bd9bd3f69 100644
--- a/c2cpg-tests/src/test/scala/io/shiftleft/c2cpg/standard/TypeDeclTests.scala
+++ b/c2cpg-tests/src/test/scala/io/shiftleft/c2cpg/standard/TypeDeclTests.scala
@@ -1,10 +1,11 @@
 package io.shiftleft.c2cpg.standard
 
+import io.shiftleft.c2cpg.parser.FileDefaults
 import io.shiftleft.c2cpg.testfixtures.CCodeToCpgSuite
 import io.shiftleft.semanticcpg.language._
 import io.shiftleft.semanticcpg.language.types.structure.FileTraversal
 
-class TypeDeclTests extends CCodeToCpgSuite {
+class TypeDeclTests extends CCodeToCpgSuite(FileDefaults.CPP_EXT) {
 
   override val code: String = """
    | class foo : bar {
@@ -24,7 +25,7 @@ class TypeDeclTests extends CCodeToCpgSuite {
     x.aliasTypeFullName shouldBe None
     x.order shouldBe 1
     x.filename.startsWith("/") shouldBe true
-    x.filename.endsWith(".c") shouldBe true
+    x.filename.endsWith(FileDefaults.CPP_EXT) shouldBe true
   }
 
   "should contain type decl for alias `mytype` of `int`" in {
@@ -36,7 +37,7 @@ class TypeDeclTests extends CCodeToCpgSuite {
     x.aliasTypeFullName shouldBe Some("int")
     x.order shouldBe 2
     x.filename.startsWith("/") shouldBe true
-    x.filename.endsWith(".c") shouldBe true
+    x.filename.endsWith(FileDefaults.CPP_EXT) shouldBe true
   }
 
   "should contain type decl for external type `int`" in {
@@ -72,7 +73,7 @@ class TypeDeclTests extends CCodeToCpgSuite {
   }
 
   "should allow traversing from type to enclosing file" in {
-    cpg.typeDecl.file.filter(_.name.endsWith(".c")).l should not be empty
+    cpg.typeDecl.file.filter(_.name.endsWith(FileDefaults.CPP_EXT)).l should not be empty
   }
 
 }
diff --git a/c2cpg-tests/src/test/scala/io/shiftleft/c2cpg/standard/TypeTests.scala b/c2cpg-tests/src/test/scala/io/shiftleft/c2cpg/standard/TypeTests.scala
index 23b084dcb..bb5f9c6a5 100644
--- a/c2cpg-tests/src/test/scala/io/shiftleft/c2cpg/standard/TypeTests.scala
+++ b/c2cpg-tests/src/test/scala/io/shiftleft/c2cpg/standard/TypeTests.scala
@@ -1,9 +1,10 @@
 package io.shiftleft.c2cpg.standard
 
+import io.shiftleft.c2cpg.parser.FileDefaults
 import io.shiftleft.c2cpg.testfixtures.CCodeToCpgSuite
 import io.shiftleft.semanticcpg.language._
 
-class TypeTests extends CCodeToCpgSuite {
+class TypeTests extends CCodeToCpgSuite(FileDefaults.CPP_EXT) {
 
   override val code: String =
     """
diff --git a/c2cpg-tests/src/test/scala/io/shiftleft/c2cpg/testfixtures/CCodeToCpgSuite.scala b/c2cpg-tests/src/test/scala/io/shiftleft/c2cpg/testfixtures/CCodeToCpgSuite.scala
index c97d858b5..c8f9f9128 100644
--- a/c2cpg-tests/src/test/scala/io/shiftleft/c2cpg/testfixtures/CCodeToCpgSuite.scala
+++ b/c2cpg-tests/src/test/scala/io/shiftleft/c2cpg/testfixtures/CCodeToCpgSuite.scala
@@ -2,12 +2,13 @@ package io.shiftleft.c2cpg.testfixtures
 
 import io.shiftleft.c2cpg.C2Cpg
 import io.shiftleft.c2cpg.C2Cpg.Config
+import io.shiftleft.c2cpg.parser.FileDefaults
 import io.shiftleft.codepropertygraph.Cpg
 import io.shiftleft.semanticcpg.testfixtures.{CodeToCpgFixture, LanguageFrontend}
 
 import java.io.File
 
-class C2CpgFrontend extends LanguageFrontend {
+class C2CpgFrontend(override val fileSuffix: String = FileDefaults.C_EXT) extends LanguageFrontend {
   def execute(sourceCodePath: File): Cpg = {
     val cpgFile = File.createTempFile("c2cpg", ".zip")
     cpgFile.deleteOnExit()
@@ -18,7 +19,6 @@ class C2CpgFrontend extends LanguageFrontend {
                         includeComments = true)
     c2cpg.runAndOutput(config)
   }
-  override val fileSuffix: String = ".c"
 }
 
-class CCodeToCpgSuite extends CodeToCpgFixture(new C2CpgFrontend) {}
+class CCodeToCpgSuite(fileSuffix: String = FileDefaults.C_EXT) extends CodeToCpgFixture(new C2CpgFrontend(fileSuffix)) {}
diff --git a/c2cpg-tests/src/test/scala/io/shiftleft/c2cpg/testfixtures/CodeDirToCpgFixture.scala b/c2cpg-tests/src/test/scala/io/shiftleft/c2cpg/testfixtures/CodeDirToCpgFixture.scala
index 5cd12a0ae..4f45e28a2 100644
--- a/c2cpg-tests/src/test/scala/io/shiftleft/c2cpg/testfixtures/CodeDirToCpgFixture.scala
+++ b/c2cpg-tests/src/test/scala/io/shiftleft/c2cpg/testfixtures/CodeDirToCpgFixture.scala
@@ -13,7 +13,7 @@ class CodeDirToCpgFixture extends AnyWordSpec with Matchers with BeforeAndAfterA
 
   val dir: java.io.File = null
   var cpg: Cpg = _
-  val frontend: LanguageFrontend = new C2CpgFrontend
+  val frontend: LanguageFrontend = new C2CpgFrontend()
   def passes(cpg: Cpg): Unit = createEnhancements(cpg)
 
   override def beforeAll(): Unit = {
diff --git a/c2cpg/src/test/scala/io/shiftleft/c2cpg/EnumTests.scala b/c2cpg/src/test/scala/io/shiftleft/c2cpg/EnumTests.scala
index 6cdd86263..05e4a52cf 100644
--- a/c2cpg/src/test/scala/io/shiftleft/c2cpg/EnumTests.scala
+++ b/c2cpg/src/test/scala/io/shiftleft/c2cpg/EnumTests.scala
@@ -19,7 +19,7 @@ class EnumTests extends AnyWordSpec with Matchers with Inside {
   private object EnumFixture {
     def apply(code: String)(f: Cpg => Unit): Unit = {
       File.usingTemporaryDirectory("c2cpgtest") { dir =>
-        val file = dir / "file1.c"
+        val file = dir / "file1.cpp"
         file.write(code)
 
         val cpg = Cpg.emptyCpg
diff --git a/c2cpg/src/test/scala/io/shiftleft/c2cpg/NamespaceTests.scala b/c2cpg/src/test/scala/io/shiftleft/c2cpg/NamespaceTests.scala
index c0041e2b4..61b811168 100644
--- a/c2cpg/src/test/scala/io/shiftleft/c2cpg/NamespaceTests.scala
+++ b/c2cpg/src/test/scala/io/shiftleft/c2cpg/NamespaceTests.scala
@@ -19,7 +19,7 @@ class NamespaceTests extends AnyWordSpec with Matchers with Inside {
   private object NamespaceFixture {
     def apply(code: String)(f: Cpg => Unit): Unit = {
       File.usingTemporaryDirectory("c2cpgtest") { dir =>
-        val file = dir / "file1.c"
+        val file = dir / "file1.cpp"
         file.write(code)
 
         val cpg = Cpg.emptyCpg
diff --git a/c2cpg/src/test/scala/io/shiftleft/c2cpg/passes/AstCreationPassTests.scala b/c2cpg/src/test/scala/io/shiftleft/c2cpg/passes/AstCreationPassTests.scala
index 739bdf7d0..0480d980e 100644
--- a/c2cpg/src/test/scala/io/shiftleft/c2cpg/passes/AstCreationPassTests.scala
+++ b/c2cpg/src/test/scala/io/shiftleft/c2cpg/passes/AstCreationPassTests.scala
@@ -14,16 +14,14 @@ import overflowdb.traversal._
 class AstCreationPassTests extends AnyWordSpec with Matchers {
 
   private object Fixture {
-    def apply(file1Code: String, file2Code: String = "")(f: Cpg => Unit): Unit = {
+    def apply(code: String, fileName: String = "file.c")(f: Cpg => Unit): Unit = {
       File.usingTemporaryDirectory("c2cpgtest") { dir =>
-        val file1 = dir / "file1.c"
-        val file2 = dir / "file2.c"
-        file1.write(file1Code)
-        file2.write(file2Code)
+        val file = dir / fileName
+        file.write(code)
 
         val cpg = Cpg.emptyCpg
         val keyPool = new IntervalKeyPool(1001, 2000)
-        val filenames = List(file1.path.toAbsolutePath.toString, file2.path.toAbsolutePath.toString)
+        val filenames = List(file.path.toAbsolutePath.toString)
         new AstCreationPass(filenames, cpg, keyPool, Config()).createAndApply()
 
         f(cpg)
@@ -299,8 +297,8 @@ class AstCreationPassTests extends AnyWordSpec with Matchers {
        |  for (int x : list) {
        |    int z = x;
        |  }
-       |}
-      """.stripMargin) { cpg =>
+       |}""".stripMargin,
+                                                "file.cpp") { cpg =>
       cpg.method.name("method").controlStructure.l match {
         case List(forStmt) =>
           forStmt.controlStructureType shouldBe ControlStructureTypes.FOR
@@ -506,13 +504,11 @@ class AstCreationPassTests extends AnyWordSpec with Matchers {
         .size shouldBe 1
     }
 
-    "be correct for sizeof operator on type" in Fixture(
-      """
+    "be correct for sizeof operator on type" in Fixture("""
         |void method() {
         |  sizeof(int);
-        |}
-      """.stripMargin
-    ) { cpg =>
+        |}""".stripMargin,
+                                                        "file.cpp") { cpg =>
       cpg.method
         .name("method")
         .ast
@@ -626,7 +622,8 @@ class AstCreationPassTests extends AnyWordSpec with Matchers {
         | char x;
         | int method(){return i;};
         |};
-      """.stripMargin
+      """.stripMargin,
+      "file.cpp"
     ) { cpg =>
       cpg.typeDecl
         .name("Derived")
@@ -643,7 +640,8 @@ class AstCreationPassTests extends AnyWordSpec with Matchers {
         |
         |Foo f;
         |int x = f.method();
-      """.stripMargin
+      """.stripMargin,
+      "file.cpp"
     ) { cpg =>
       cpg.typeDecl
         .name("Foo")
@@ -661,7 +659,8 @@ class AstCreationPassTests extends AnyWordSpec with Matchers {
     "be correct for type initializer expression" in Fixture(
       """
         |int x = (int){ 1 };
-      """.stripMargin
+      """.stripMargin,
+      "file.cpp"
     ) { cpg =>
       cpg.call.name(Operators.cast).l match {
         case List(call: Call) =>
@@ -677,7 +676,8 @@ class AstCreationPassTests extends AnyWordSpec with Matchers {
         | int a = 0;
         | static_assert ( a == 0 , "not 0!");
         |}
-      """.stripMargin
+      """.stripMargin,
+      "file.cpp"
     ) { cpg =>
       cpg.call.codeExact("static_assert ( a == 0 , \"not 0!\");").l match {
         case List(call: Call) =>
@@ -695,7 +695,8 @@ class AstCreationPassTests extends AnyWordSpec with Matchers {
         | try { bar(); } 
         | catch(x) { return 0; };
         |}
-      """.stripMargin
+      """.stripMargin,
+      "file.cpp"
     ) { cpg =>
       cpg.controlStructure.l match {
         case List(t) =>
@@ -712,7 +713,8 @@ class AstCreationPassTests extends AnyWordSpec with Matchers {
         | Foo(int i){};
         |};
         |Foo f1(0);
-      """.stripMargin
+      """.stripMargin,
+      "file.cpp"
     ) { cpg =>
       cpg.typeDecl
         .name("Foo")
@@ -735,7 +737,8 @@ class AstCreationPassTests extends AnyWordSpec with Matchers {
         | };
         | template class Y<char*>;
         | template void Y<double>::mf();
-      """.stripMargin
+      """.stripMargin,
+      "file.cpp"
     ) { cpg =>
       cpg.typeDecl
         .name("Y")
@@ -752,7 +755,8 @@ class AstCreationPassTests extends AnyWordSpec with Matchers {
         | template void f<double>(double); // instantiates f<double>(double)
         | template void f<>(char); // instantiates f<char>(char), template argument deduced
         | template void f(int); // instantiates f<int>(int), template argument deduced
-      """.stripMargin
+      """.stripMargin,
+      "file.cpp"
     ) { cpg =>
       cpg.method
         .name("f")
@@ -767,7 +771,8 @@ class AstCreationPassTests extends AnyWordSpec with Matchers {
         | Foo(int i) {  };
         |};
         |Foo x = Foo{0};
-      """.stripMargin
+      """.stripMargin,
+      "file.cpp"
     ) { cpg =>
       cpg.typeDecl
         .name("Foo")
@@ -807,7 +812,8 @@ class AstCreationPassTests extends AnyWordSpec with Matchers {
         | #include <vlc/libvlc_media.h>
         | int x = 0;
         |}
-        |""".stripMargin
+        |""".stripMargin,
+      "file.cpp"
     ) { cpg =>
       cpg.call.code("x = 0").l.size shouldBe 1
     }
@@ -925,7 +931,8 @@ class AstCreationPassTests extends AnyWordSpec with Matchers {
         |   int * arr = new int[n];
         |   return arr;
         |}
-        |""".stripMargin
+        |""".stripMargin,
+      "file.cpp"
     ) { cpg =>
       // TODO: "<operator>.new" is not part of Operators
       cpg.call.name("<operator>.new").code("new int\\[n\\]").argument.code("int").size shouldBe 1
@@ -937,7 +944,8 @@ class AstCreationPassTests extends AnyWordSpec with Matchers {
         |   Foo* foo = new Foo(n, 42);
         |   return foo;
         |}
-        |""".stripMargin
+        |""".stripMargin,
+      "file.cpp"
     ) { cpg =>
       cpg.call.name("<operator>.new").codeExact("new Foo(n, 42)").argument.code("Foo").size shouldBe 1
     }
@@ -947,7 +955,8 @@ class AstCreationPassTests extends AnyWordSpec with Matchers {
         |int delete_number(int* n) {
         |  delete n;
         |}
-        |""".stripMargin
+        |""".stripMargin,
+      "file.cpp"
     ) { cpg =>
       cpg.call.name(Operators.delete).code("delete n").argument.code("n").size shouldBe 1
     }
@@ -957,7 +966,8 @@ class AstCreationPassTests extends AnyWordSpec with Matchers {
         |void delete_number(int n[]) {
         |  delete[] n;
         |}
-        |""".stripMargin
+        |""".stripMargin,
+      "file.cpp"
     ) { cpg =>
       cpg.call.name(Operators.delete).codeExact("delete[] n").argument.code("n").size shouldBe 1
     }
@@ -968,7 +978,8 @@ class AstCreationPassTests extends AnyWordSpec with Matchers {
         |  int y = const_cast<int>(n);
         |  return;
         |}
-        |""".stripMargin
+        |""".stripMargin,
+      "file.cpp"
     ) { cpg =>
       cpg.call.name(Operators.cast).codeExact("const_cast<int>(n)").argument.code.l shouldBe List("int", "n")
     }
@@ -979,7 +990,8 @@ class AstCreationPassTests extends AnyWordSpec with Matchers {
         |  int y = static_cast<int>(n);
         |  return;
         |}
-        |""".stripMargin
+        |""".stripMargin,
+      "file.cpp"
     ) { cpg =>
       cpg.call.name(Operators.cast).codeExact("static_cast<int>(n)").argument.code.l shouldBe List("int", "n")
     }
@@ -990,7 +1002,8 @@ class AstCreationPassTests extends AnyWordSpec with Matchers {
         |  int y = dynamic_cast<int>(n);
         |  return;
         |}
-        |""".stripMargin
+        |""".stripMargin,
+      "file.cpp"
     ) { cpg =>
       cpg.call.name(Operators.cast).codeExact("dynamic_cast<int>(n)").argument.code.l shouldBe List("int", "n")
     }
@@ -1001,7 +1014,8 @@ class AstCreationPassTests extends AnyWordSpec with Matchers {
         |  int y = reinterpret_cast<int>(n);
         |  return;
         |}
-        |""".stripMargin
+        |""".stripMargin,
+      "file.cpp"
     ) { cpg =>
       cpg.call.name(Operators.cast).codeExact("reinterpret_cast<int>(n)").argument.code.l shouldBe List("int", "n")
     }
@@ -1021,6 +1035,26 @@ class AstCreationPassTests extends AnyWordSpec with Matchers {
       cpg.method.name("method").lineNumber.l shouldBe List(6)
       cpg.method.name("method").block.assignments.lineNumber.l shouldBe List(8)
     }
+
+    "have correct line numbers" in Fixture("""
+       |int main() {
+       |int a = 0;
+       |statementthatdoesnothing();
+       |int b = 0;
+       |int c = 0;
+       |}
+      """.stripMargin) { cpg =>
+      cpg.identifier.l match {
+        case List(a, b, c) =>
+          a.lineNumber shouldBe Some(3)
+          a.columnNumber shouldBe Some(4)
+          b.lineNumber shouldBe Some(5)
+          b.columnNumber shouldBe Some(4)
+          c.lineNumber shouldBe Some(6)
+          c.columnNumber shouldBe Some(4)
+        case _ => fail()
+      }
+    }
   }
 
 }