Repository: fraunhofer-aisec/cpg
Issue: C++ generics with elaborated type specifiers fail to parse

The following: `TypeParser.createFrom("Array<struct Node>", true)` will create a generic type with a type parameter of type `"structNode"`. This seems to happen here, because of some reason this function here will remove all the whitespace within the generic block. Therefore, the type parser cannot correctly identity the `struct` as an elaborated type specifier (which is normally ignored). However, simply removing the function also does not work since then it is parsed as seperate typeblocks and things really start to go wrong :(

https://github.com/Fraunhofer-AISEC/cpg/blob/3f3542015dd6c989b8f8274b89478dfe67922126/src/main/java/de/fraunhofer/aisec/cpg/graph/type/TypeParser.java#L261-L300

Changed files:
src/main/java/de/fraunhofer/aisec/cpg/graph/type/TypeParser.java, src/test/java/de/fraunhofer/aisec/cpg/TypeTests.java

diff --git a/src/main/java/de/fraunhofer/aisec/cpg/graph/type/TypeParser.java b/src/main/java/de/fraunhofer/aisec/cpg/graph/type/TypeParser.java
index 2d5accfdb..96c86e6ed 100644
--- a/src/main/java/de/fraunhofer/aisec/cpg/graph/type/TypeParser.java
+++ b/src/main/java/de/fraunhofer/aisec/cpg/graph/type/TypeParser.java
@@ -61,6 +61,13 @@ public class TypeParser {
   private static final String ELABORATED_TYPE_UNION = "union";
   private static final String ELABORATED_TYPE_ENUM = "enum";
 
+  private static final List<String> elaboratedTypes =
+      List.of(
+          ELABORATED_TYPE_CLASS,
+          ELABORATED_TYPE_STRUCT,
+          ELABORATED_TYPE_UNION,
+          ELABORATED_TYPE_ENUM);
+
   private TypeParser() {
     throw new IllegalStateException("Do not instantiate the TypeParser");
   }
@@ -263,6 +270,27 @@ public class TypeParser {
    */
   @NonNull
   private static String fixGenerics(@NonNull String type) {
+    if (type.contains("<") && type.contains(">") && getLanguage() == TypeManager.Language.CXX) {
+      String generics = type.substring(type.indexOf('<') + 1, type.lastIndexOf('>'));
+
+      /* Explanation from @vfsrfs:
+       * We fist extract the generic string (the substring between < and >). Then, the elaborate
+       * string can either start directly with the elaborate type specifier e.g. struct Node or it
+       * must be preceded by <, \\\\h (horizontal whitespace), or ,. If any other character precedes
+       * the elaborate type specifier then it is not considered to be a type specifier e.g.
+       * mystruct. Then there can be an arbitrary amount of horizontal whitespaces. This is followed
+       * by the elaborate type specifier and at least one more horizontal whitespace, which marks
+       * that it is indeed an elaborate type and not something like structMy.
+       */
+      for (String elaborate : elaboratedTypes) {
+        generics = generics.replaceAll("(^|(?<=[\\\\h,<]))\\\\h*(?<main>" + elaborate + "\\\\h+)", "");
+      }
+      type =
+          type.substring(0, type.indexOf('<') + 1)
+              + generics.trim()
+              + type.substring(type.lastIndexOf('>'));
+    }
+
     StringBuilder out = new StringBuilder();
     int bracketCount = 0;
     int iterator = 0;
@@ -729,7 +757,6 @@ public class TypeParser {
       // Note that "const auto ..." will end here with typeName="const" as auto is not supported.
       return UnknownType.getUnknownType();
     }
-    assert counter < typeBlocks.size();
     String typeName = typeBlocks.get(counter);
     counter++;
 
diff --git a/src/test/java/de/fraunhofer/aisec/cpg/TypeTests.java b/src/test/java/de/fraunhofer/aisec/cpg/TypeTests.java
index ce901258e..89a470a9d 100644
--- a/src/test/java/de/fraunhofer/aisec/cpg/TypeTests.java
+++ b/src/test/java/de/fraunhofer/aisec/cpg/TypeTests.java
@@ -603,6 +603,51 @@ class TypeTests extends BaseTest {
     typeString = "int const &ref2 = a";
     result = TypeParser.createFrom(typeString, true);
     assertEquals(expected, result);
+
+    // Test 13: Elaborated Type in Generics
+    result = TypeParser.createFrom("Array<struct Node>", true);
+    generics = new ArrayList<>();
+    ObjectType generic =
+        new ObjectType(
+            "Node",
+            Type.Storage.AUTO,
+            new Type.Qualifier(),
+            new ArrayList<>(),
+            ObjectType.Modifier.NOT_APPLICABLE,
+            false);
+    generics.add(generic);
+
+    expected =
+        new ObjectType(
+            "Array",
+            Type.Storage.AUTO,
+            new Type.Qualifier(),
+            generics,
+            ObjectType.Modifier.NOT_APPLICABLE,
+            false);
+    assertEquals(expected, result);
+
+    result = TypeParser.createFrom("Array<myclass >", true);
+    generics = new ArrayList<>();
+    generic =
+        new ObjectType(
+            "myclass",
+            Type.Storage.AUTO,
+            new Type.Qualifier(),
+            new ArrayList<>(),
+            ObjectType.Modifier.NOT_APPLICABLE,
+            false);
+    generics.add(generic);
+
+    expected =
+        new ObjectType(
+            "Array",
+            Type.Storage.AUTO,
+            new Type.Qualifier(),
+            generics,
+            ObjectType.Modifier.NOT_APPLICABLE,
+            false);
+    assertEquals(expected, result);
   }
 
   // Tests on the resulting graph