瀏覽代碼

LibCpp: Add regression tests for the parser

For each .cpp file in the test suite data, there is a .ast file that
represents the "known good" baseline of the parser result.

Each .cpp file goes through the parser, and the result of
invoking `ASTNode::dump()` on the root node is compared to the
baseline to find regressions.

We also check that there were no parser errors when parsing the .cpp
files.
Itamar 4 年之前
父節點
當前提交
832e9c6e02

+ 1 - 0
Meta/build-root-filesystem.sh

@@ -110,6 +110,7 @@ cp "$SERENITY_SOURCE_DIR"/README.md mnt/home/anon/
 cp -r "$SERENITY_SOURCE_DIR"/Userland/Libraries/LibJS/Tests mnt/home/anon/js-tests
 cp -r "$SERENITY_SOURCE_DIR"/Userland/Libraries/LibWeb/Tests mnt/home/anon/web-tests
 cp -r "$SERENITY_SOURCE_DIR"/Userland/DevTools/HackStudio/LanguageServers/Cpp/Tests mnt/home/anon/cpp-tests/comprehension
+cp -r "$SERENITY_SOURCE_DIR"/Userland/Libraries/LibCpp/Tests mnt/home/anon/cpp-tests/parser
 chmod 700 mnt/root
 chmod 700 mnt/home/anon
 chmod 700 mnt/home/nona

+ 1 - 1
Meta/check-style.sh

@@ -12,7 +12,7 @@ cd "$script_path/.." || exit 1
 #  */
 GOOD_LICENSE_HEADER_PATTERN=$'^/\*\n( \* Copyright \(c\) [0-9]{4}(-[0-9]{4})?, .*\n)+ \*\n \* SPDX-License-Identifier: BSD-2-Clause\n \*/\n\n'
 BAD_LICENSE_HEADER_ERRORS=()
-LICENSE_HEADER_CHECK_EXCLUDES=(AK/Checked.h AK/Function.h Userland/Libraries/LibC/elf.h Userland/DevTools/HackStudio/LanguageServers/Cpp/Tests/*)
+LICENSE_HEADER_CHECK_EXCLUDES=(AK/Checked.h AK/Function.h Userland/Libraries/LibC/elf.h Userland/DevTools/HackStudio/LanguageServers/Cpp/Tests/* Userland/Libraries/LibCpp/Tests/*)
 
 # We check that "#pragma once" is present
 PRAGMA_ONCE_PATTERN='#pragma once'

+ 2 - 1
Meta/lint-clang-format.sh

@@ -13,7 +13,8 @@ if [ "$#" -eq "1" ]; then
             ':!:Base' \
             ':!:Kernel/FileSystem/ext2_fs.h' \
             ':!:Userland/Libraries/LibC/syslog.h' \
-            ':!:Userland/DevTools/HackStudio/LanguageServers/Cpp/Tests/*'
+            ':!:Userland/DevTools/HackStudio/LanguageServers/Cpp/Tests/*' \
+            ':!:Userland/Libraries/LibCpp/Tests/*'
     )
 else
     files=()

+ 2 - 1
Tests/CMakeLists.txt

@@ -1,8 +1,9 @@
 add_subdirectory(AK)
 add_subdirectory(Kernel)
 add_subdirectory(LibC)
-add_subdirectory(LibCore)
 add_subdirectory(LibCompress)
+add_subdirectory(LibCore)
+add_subdirectory(LibCpp)
 add_subdirectory(LibELF)
 add_subdirectory(LibGfx)
 add_subdirectory(LibJS)

+ 5 - 0
Tests/LibCpp/CMakeLists.txt

@@ -0,0 +1,5 @@
+file(GLOB CMD_SOURCES  CONFIGURE_DEPENDS "*.cpp")
+
+foreach(CMD_SRC ${CMD_SOURCES})
+    serenity_test(${CMD_SRC} LibCpp LIBS LibCpp)
+endforeach()

+ 80 - 0
Tests/LibCpp/test-cpp-parser.cpp

@@ -0,0 +1,80 @@
+/*
+ * Copyright (c) 2021, Itamar S. <itamar8910@gmail.com>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include <AK/LexicalPath.h>
+#include <LibCore/DirIterator.h>
+#include <LibCore/File.h>
+#include <LibCpp/Parser.h>
+#include <LibTest/TestCase.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+constexpr char TESTS_ROOT_DIR[] = "/home/anon/cpp-tests/parser";
+
+static String read_all(const String& path)
+{
+    auto result = Core::File::open(path, Core::OpenMode::ReadOnly);
+    VERIFY(!result.is_error());
+    auto content = result.value()->read_all();
+    return { reinterpret_cast<const char*>(content.data()), content.size() };
+}
+
+TEST_CASE(test_regression)
+{
+    Core::DirIterator directory_iterator(TESTS_ROOT_DIR, Core::DirIterator::Flags::SkipDots);
+
+    while (directory_iterator.has_next()) {
+        auto file_path = directory_iterator.next_full_path();
+
+        auto path = LexicalPath { file_path };
+        if (!path.has_extension(".cpp"))
+            continue;
+
+        outln("Checking {}...", path.basename());
+
+        auto ast_file_path = String::formatted("{}.ast", file_path.substring(0, file_path.length() - sizeof(".cpp") + 1));
+
+        auto source = read_all(file_path);
+        auto target_ast = read_all(ast_file_path);
+
+        StringView source_view(source);
+        ::Cpp::Parser parser(source_view, file_path);
+        auto root = parser.parse();
+
+        EXPECT(parser.errors().is_empty());
+
+        int pipefd[2] = {};
+        if (pipe(pipefd) < 0) {
+            perror("pipe");
+            exit(1);
+        }
+
+        FILE* input_stream = fdopen(pipefd[0], "r");
+        FILE* output_stream = fdopen(pipefd[1], "w");
+
+        root->dump(output_stream);
+
+        fclose(output_stream);
+
+        ByteBuffer buffer;
+        while (!feof(input_stream)) {
+            char chunk[4096];
+            size_t size = fread(chunk, sizeof(char), sizeof(chunk), input_stream);
+            if (size == 0)
+                break;
+            buffer.append(chunk, size);
+        }
+
+        fclose(input_stream);
+
+        String content { reinterpret_cast<const char*>(buffer.data()), buffer.size() };
+
+        auto equal = content == target_ast;
+        EXPECT(equal);
+    }
+}

+ 10 - 0
Userland/Libraries/LibCpp/Tests/function-decl.ast

@@ -0,0 +1,10 @@
+TranslationUnit[0:0->2:0]
+  FunctionDeclaration[0:0->2:0]
+    Type[0:0->0:4]
+      int
+    foo
+    (
+    )
+    FunctionDefinition[1:0->2:0]
+    {
+    }

+ 3 - 0
Userland/Libraries/LibCpp/Tests/function-decl.cpp

@@ -0,0 +1,3 @@
+int foo()
+{
+}

+ 33 - 0
Userland/Libraries/LibCpp/Tests/if-else.ast

@@ -0,0 +1,33 @@
+TranslationUnit[0:0->7:0]
+  FunctionDeclaration[0:0->7:0]
+    Type[0:0->0:4]
+      int
+    foo
+    (
+    )
+    FunctionDefinition[1:0->7:0]
+    {
+      IfStatement[2:4->3:16]
+        Predicate:
+        NumericLiteral[2:8->2:8]
+        2
+        Then:
+        ReturnStatement[3:8->3:16]
+          NumericLiteral[3:15->3:15]
+          2
+      IfStatement[4:4->5:16]
+        Predicate:
+        BinaryExpression[4:8->4:12]
+          NumericLiteral[4:8->4:8]
+          1
+          <
+          NumericLiteral[4:12->4:12]
+          2
+        Then:
+        ReturnStatement[5:8->5:16]
+          NumericLiteral[5:15->5:15]
+          1
+      ReturnStatement[6:4->6:12]
+        NumericLiteral[6:11->6:11]
+        0
+    }

+ 8 - 0
Userland/Libraries/LibCpp/Tests/if-else.cpp

@@ -0,0 +1,8 @@
+int foo()
+{
+    if (2)
+        return 2;
+    if (1 < 2)
+        return 1;
+    return 0;
+}

+ 33 - 0
Userland/Libraries/LibCpp/Tests/local-vars.ast

@@ -0,0 +1,33 @@
+TranslationUnit[1:0->7:0]
+  FunctionDeclaration[1:0->7:0]
+    Type[1:0->1:4]
+      int
+    foo
+    (
+    )
+    FunctionDefinition[2:0->7:0]
+    {
+      VariableDeclaration[3:4->3:9]
+        Type[3:4->3:8]
+          int
+        x
+      VariableDeclaration[4:4->4:16]
+        Type[4:4->4:11]
+          double
+        y
+        NumericLiteral[4:15->4:15]
+        2
+      VariableDeclaration[5:4->5:20]
+        Type[5:4->5:11]
+          double
+        z
+        BinaryExpression[5:15->5:20]
+          Name[5:15->5:17]
+          x
+          +
+          Name[5:19->5:20]
+          y
+      ReturnStatement[6:4->6:12]
+        Name[6:11->6:12]
+        z
+    }

+ 8 - 0
Userland/Libraries/LibCpp/Tests/local-vars.cpp

@@ -0,0 +1,8 @@
+
+int foo()
+{
+    int x;
+    double y = 2;
+    double z = x + y;
+    return z;
+}

+ 879 - 0
Userland/Libraries/LibCpp/Tests/strace.ast

@@ -0,0 +1,879 @@
+TranslationUnit[0:0->144:0]
+  VariableDeclaration[0:0->2:0]
+    Type[0:0->0:11]
+      [static] int
+    g_pid
+    UnaryExpression[0:19->0:20]
+      -
+      NumericLiteral[0:20->0:20]
+      1
+  FunctionDeclaration[2:0->10:0]
+    [static]
+    Type[2:7->2:12]
+      void
+    handle_sigint
+    (
+    Parameter[2:26->2:29]
+      Type[2:26->2:29]
+        int
+    )
+    FunctionDefinition[3:0->10:0]
+    {
+      IfStatement[4:4->5:14]
+        Predicate:
+        BinaryExpression[4:8->4:18]
+          Name[4:8->4:14]
+          g_pid
+          ==
+          UnaryExpression[4:17->4:18]
+            -
+            NumericLiteral[4:18->4:18]
+            1
+        Then:
+        ReturnStatement[5:8->5:14]
+      IfStatement[7:4->10:0]
+        Predicate:
+        BinaryExpression[7:8->7:43]
+          FunctionCall[7:8->7:39]
+            Name[7:8->7:14]
+            ptrace
+            Name[7:15->7:24]
+            PT_DETACH
+            Name[7:26->7:31]
+            g_pid
+            NumericLiteral[7:33->7:33]
+            0
+            NumericLiteral[7:36->7:36]
+            0
+          ==
+          UnaryExpression[7:42->7:43]
+            -
+            NumericLiteral[7:43->7:43]
+            1
+        Then:
+        BlockStatement[7:46->10:0]
+          FunctionCall[8:8->8:24]
+            Name[8:8->8:14]
+            perror
+            StringLiteral[8:15->8:22]
+              "detach"
+    }
+  FunctionDeclaration[12:0->144:0]
+    Type[12:0->12:4]
+      int
+    main
+    (
+    Parameter[12:9->12:16]
+    argc
+      Type[12:9->12:13]
+        int
+    Parameter[12:24->12:29]
+    argv
+      Pointer[12:24->12:26]
+        Pointer[12:23->12:24]
+          Type[12:19->12:23]
+            char
+    )
+    FunctionDefinition[13:0->144:0]
+    {
+      IfStatement[14:4->19:4]
+        Predicate:
+        BinaryExpression[14:8->14:74]
+          FunctionCall[14:8->14:72]
+            Name[14:8->14:14]
+            pledge
+            StringLiteral[14:15->14:60]
+              "stdio wpath cpath proc exec ptrace sigaction"
+            NullPointerLiteral[14:63->14:69]
+          <
+          NumericLiteral[14:74->14:74]
+          0
+        Then:
+        BlockStatement[14:77->19:4]
+          FunctionCall[15:8->15:24]
+            Name[15:8->15:14]
+            perror
+            StringLiteral[15:15->15:22]
+              "pledge"
+          ReturnStatement[16:8->16:16]
+            NumericLiteral[16:15->16:15]
+            1
+      VariableDeclaration[19:4->19:34]
+        Type[19:4->19:24]
+          Vector<[const] char*>
+        child_argv
+      VariableDeclaration[21:4->21:41]
+        Pointer[21:14->21:16]
+          Type[21:4->21:14]
+            [const] char
+        output_filename
+        NullPointerLiteral[21:34->21:40]
+      VariableDeclaration[22:4->22:50]
+        Type[22:4->22:9]
+          auto
+        trace_file
+        FunctionCall[22:22->22:50]
+          Name[22:22->22:48]
+          Core::File::standard_error
+      VariableDeclaration[24:4->24:27]
+        Type[24:4->24:21]
+          Core::ArgsParser
+        parser
+      FunctionCall[25:4->26:47]
+        MemberExpression[25:4->25:27]
+          Name[25:4->25:10]
+          parser
+          Identifier[25:11->25:26]
+          set_general_help
+        StringLiteral[26:8->26:45]
+          "Trace all syscalls and their result."
+      FunctionCall[27:4->27:70]
+        MemberExpression[27:4->27:21]
+          Name[27:4->27:10]
+          parser
+          Identifier[27:11->27:20]
+          add_option
+        Name[27:22->27:27]
+        g_pid
+        StringLiteral[27:29->27:49]
+          "Trace the given PID"
+        StringLiteral[27:52->27:56]
+          "pid"
+        StringLiteral[27:59->27:61]
+          'p'
+        StringLiteral[27:64->27:68]
+          "pid"
+      FunctionCall[28:4->28:94]
+        MemberExpression[28:4->28:21]
+          Name[28:4->28:10]
+          parser
+          Identifier[28:11->28:20]
+          add_option
+        Name[28:22->28:37]
+        output_filename
+        StringLiteral[28:39->28:67]
+          "Filename to write output to"
+        StringLiteral[28:70->28:77]
+          "output"
+        StringLiteral[28:80->28:82]
+          'o'
+        StringLiteral[28:85->28:92]
+          "output"
+      FunctionCall[29:4->29:111]
+        MemberExpression[29:4->29:34]
+          Name[29:4->29:10]
+          parser
+          Identifier[29:11->29:33]
+          add_positional_argument
+        Name[29:35->29:45]
+        child_argv
+        StringLiteral[29:47->29:65]
+          "Arguments to exec"
+        StringLiteral[29:68->29:77]
+          "argument"
+        Name[29:80->29:110]
+        Core::ArgsParser::Required::No
+      FunctionCall[31:4->31:28]
+        MemberExpression[31:4->31:16]
+          Name[31:4->31:10]
+          parser
+          Identifier[31:11->31:15]
+          parse
+        Name[31:17->31:21]
+        argc
+        Name[31:23->31:27]
+        argv
+      IfStatement[33:4->42:4]
+        Predicate:
+        BinaryExpression[33:8->33:33]
+          Name[33:8->33:24]
+          output_filename
+          !=
+          NullPointerLiteral[33:27->33:33]
+        Then:
+        BlockStatement[33:36->42:4]
+          VariableDeclaration[34:8->34:87]
+            Type[34:8->34:13]
+              auto
+            open_result
+            FunctionCall[34:27->34:87]
+              Name[34:27->34:43]
+              Core::File::open
+              Name[34:44->34:59]
+              output_filename
+              Name[34:61->34:86]
+              Core::OpenMode::WriteOnly
+          IfStatement[35:8->39:8]
+            Predicate:
+            FunctionCall[35:12->35:34]
+              MemberExpression[35:12->35:32]
+                Name[35:12->35:23]
+                open_result
+                Identifier[35:24->35:31]
+                is_error
+            Then:
+            BlockStatement[35:36->39:8]
+              FunctionCall[36:12->36:80]
+                Name[36:12->36:17]
+                outln
+                Name[36:18->36:24]
+                stderr
+                StringLiteral[36:26->36:57]
+                  "Failed to open output file: {}"
+                FunctionCall[36:60->36:79]
+                  MemberExpression[36:60->36:77]
+                    Name[36:60->36:71]
+                    open_result
+                    Identifier[36:72->36:76]
+                    error
+              ReturnStatement[37:12->37:20]
+                NumericLiteral[37:19->37:19]
+                1
+          AssignmentExpression[39:8->39:40]
+            Name[39:8->39:19]
+            trace_file
+            =
+            FunctionCall[39:21->39:40]
+              MemberExpression[39:21->39:38]
+                Name[39:21->39:32]
+                open_result
+                Identifier[39:33->39:37]
+                value
+      IfStatement[42:4->47:4]
+        Predicate:
+        BinaryExpression[42:8->42:62]
+          FunctionCall[42:8->42:60]
+            Name[42:8->42:14]
+            pledge
+            StringLiteral[42:15->42:48]
+              "stdio proc exec ptrace sigaction"
+            NullPointerLiteral[42:51->42:57]
+          <
+          NumericLiteral[42:62->42:62]
+          0
+        Then:
+        BlockStatement[42:65->47:4]
+          FunctionCall[43:8->43:24]
+            Name[43:8->43:14]
+            perror
+            StringLiteral[43:15->43:22]
+              "pledge"
+          ReturnStatement[44:8->44:16]
+            NumericLiteral[44:15->44:15]
+            1
+      VariableDeclaration[47:4->47:14]
+        Type[47:4->47:8]
+          int
+        status
+      IfStatement[48:4->81:4]
+        Predicate:
+        BinaryExpression[48:8->48:18]
+          Name[48:8->48:14]
+          g_pid
+          ==
+          UnaryExpression[48:17->48:18]
+            -
+            NumericLiteral[48:18->48:18]
+            1
+        Then:
+        BlockStatement[48:21->81:4]
+          IfStatement[49:8->54:8]
+            Predicate:
+            FunctionCall[49:12->49:33]
+              MemberExpression[49:12->49:31]
+                Name[49:12->49:22]
+                child_argv
+                Identifier[49:23->49:30]
+                is_empty
+            Then:
+            BlockStatement[49:35->54:8]
+              FunctionCall[50:12->50:78]
+                Name[50:12->50:17]
+                outln
+                Name[50:18->50:24]
+                stderr
+                StringLiteral[50:26->50:76]
+                  "strace: Expected either a pid or some arguments\n"
+              ReturnStatement[51:12->51:20]
+                NumericLiteral[51:19->51:19]
+                1
+          FunctionCall[54:8->54:34]
+            MemberExpression[54:8->54:25]
+              Name[54:8->54:18]
+              child_argv
+              Identifier[54:19->54:24]
+              append
+            NullPointerLiteral[54:26->54:32]
+          VariableDeclaration[55:8->55:24]
+            Type[55:8->55:12]
+              int
+            pid
+            FunctionCall[55:18->55:24]
+              Name[55:18->55:22]
+              fork
+          IfStatement[56:8->61:8]
+            Predicate:
+            BinaryExpression[56:12->56:18]
+              Name[56:12->56:16]
+              pid
+              <
+              NumericLiteral[56:18->56:18]
+              0
+            Then:
+            BlockStatement[56:21->61:8]
+              FunctionCall[57:12->57:26]
+                Name[57:12->57:18]
+                perror
+                StringLiteral[57:19->57:24]
+                  "fork"
+              ReturnStatement[58:12->58:20]
+                NumericLiteral[58:19->58:19]
+                1
+          IfStatement[61:8->74:8]
+            Predicate:
+            UnaryExpression[61:12->61:16]
+              !
+              Name[61:13->61:16]
+              pid
+            Then:
+            BlockStatement[61:18->74:8]
+              IfStatement[62:12->66:12]
+                Predicate:
+                BinaryExpression[62:16->62:49]
+                  FunctionCall[62:16->62:45]
+                    Name[62:16->62:22]
+                    ptrace
+                    Name[62:23->62:34]
+                    PT_TRACE_ME
+                    NumericLiteral[62:36->62:36]
+                    0
+                    NumericLiteral[62:39->62:39]
+                    0
+                    NumericLiteral[62:42->62:42]
+                    0
+                  ==
+                  UnaryExpression[62:48->62:49]
+                    -
+                    NumericLiteral[62:49->62:49]
+                    1
+                Then:
+                BlockStatement[62:52->66:12]
+                  FunctionCall[63:16->63:33]
+                    Name[63:16->63:22]
+                    perror
+                    StringLiteral[63:23->63:31]
+                      "traceme"
+                  ReturnStatement[64:16->64:24]
+                    NumericLiteral[64:23->64:23]
+                    1
+              VariableDeclaration[66:12->66:86]
+                Type[66:12->66:16]
+                  int
+                rc
+                FunctionCall[66:21->66:86]
+                  Name[66:21->66:27]
+                  execvp
+                  FunctionCall[66:28->66:46]
+                    MemberExpression[66:28->66:44]
+                      Name[66:28->66:38]
+                      child_argv
+                      Identifier[66:39->66:43]
+                      first
+                  CppCastExpression[66:48->66:85]
+                  const_cast
+                    <
+                    Pointer[66:64->66:65]
+                      Pointer[66:63->66:64]
+                        Type[66:59->66:63]
+                          char
+                    >
+                    FunctionCall[66:67->66:84]
+                      MemberExpression[66:67->66:82]
+                        Name[66:67->66:77]
+                        child_argv
+                        Identifier[66:78->66:81]
+                        data
+              IfStatement[67:12->71:12]
+                Predicate:
+                BinaryExpression[67:16->67:21]
+                  Name[67:16->67:19]
+                  rc
+                  <
+                  NumericLiteral[67:21->67:21]
+                  0
+                Then:
+                BlockStatement[67:24->71:12]
+                  FunctionCall[68:16->68:32]
+                    Name[68:16->68:22]
+                    perror
+                    StringLiteral[68:23->68:30]
+                      "execvp"
+                  FunctionCall[69:16->69:23]
+                    Name[69:16->69:20]
+                    exit
+                    NumericLiteral[69:21->69:21]
+                    1
+              FunctionCall[71:12->71:32]
+                Name[71:12->71:30]
+                VERIFY_NOT_REACHED
+          AssignmentExpression[74:8->74:19]
+            Name[74:8->74:14]
+            g_pid
+            =
+            Name[74:16->74:19]
+            pid
+          IfStatement[75:8->79:4]
+            Predicate:
+            BinaryExpression[75:12->75:83]
+              FunctionCall[75:12->75:54]
+                Name[75:12->75:19]
+                waitpid
+                Name[75:20->75:23]
+                pid
+                UnaryExpression[75:25->75:32]
+                  &
+                  Name[75:26->75:32]
+                  status
+                BinaryExpression[75:34->75:52]
+                  Name[75:34->75:43]
+                  WSTOPPED
+                  |
+                  Name[75:45->75:52]
+                  WEXITED
+              !=
+              BinaryExpression[75:57->75:83]
+                Name[75:57->75:61]
+                pid
+                ||
+                UnaryExpression[75:64->75:83]
+                  !
+                  FunctionCall[75:65->75:83]
+                    Name[75:65->75:75]
+                    WIFSTOPPED
+                    Name[75:76->75:82]
+                    status
+            Then:
+            BlockStatement[75:85->79:4]
+              FunctionCall[76:12->76:29]
+                Name[76:12->76:18]
+                perror
+                StringLiteral[76:19->76:27]
+                  "waitpid"
+              ReturnStatement[77:12->77:20]
+                NumericLiteral[77:19->77:19]
+                1
+      VariableDeclaration[81:4->81:23]
+        Type[81:4->81:21]
+          sigaction
+        sa
+      FunctionCall[82:4->82:44]
+        Name[82:4->82:10]
+        memset
+        UnaryExpression[82:11->82:14]
+          &
+          Name[82:12->82:14]
+          sa
+        NumericLiteral[82:16->82:16]
+        0
+        SizeofExpression[82:19->82:43]
+          Type[82:26->82:42]
+            sigaction
+      AssignmentExpression[83:4->83:33]
+        MemberExpression[83:4->83:18]
+          Name[83:4->83:6]
+          sa
+          Identifier[83:7->83:16]
+          sa_handler
+        =
+        Name[83:20->83:33]
+        handle_sigint
+      FunctionCall[84:4->84:35]
+        Name[84:4->84:13]
+        sigaction
+        Name[84:14->84:20]
+        SIGINT
+        UnaryExpression[84:22->84:25]
+          &
+          Name[84:23->84:25]
+          sa
+        NullPointerLiteral[84:27->84:33]
+      IfStatement[86:4->90:4]
+        Predicate:
+        BinaryExpression[86:8->86:43]
+          FunctionCall[86:8->86:39]
+            Name[86:8->86:14]
+            ptrace
+            Name[86:15->86:24]
+            PT_ATTACH
+            Name[86:26->86:31]
+            g_pid
+            NumericLiteral[86:33->86:33]
+            0
+            NumericLiteral[86:36->86:36]
+            0
+          ==
+          UnaryExpression[86:42->86:43]
+            -
+            NumericLiteral[86:43->86:43]
+            1
+        Then:
+        BlockStatement[86:46->90:4]
+          FunctionCall[87:8->87:24]
+            Name[87:8->87:14]
+            perror
+            StringLiteral[87:15->87:22]
+              "attach"
+          ReturnStatement[88:8->88:16]
+            NumericLiteral[88:15->88:15]
+            1
+      IfStatement[90:4->95:4]
+        Predicate:
+        BinaryExpression[90:8->90:83]
+          FunctionCall[90:8->90:52]
+            Name[90:8->90:15]
+            waitpid
+            Name[90:16->90:21]
+            g_pid
+            UnaryExpression[90:23->90:30]
+              &
+              Name[90:24->90:30]
+              status
+            BinaryExpression[90:32->90:50]
+              Name[90:32->90:41]
+              WSTOPPED
+              |
+              Name[90:43->90:50]
+              WEXITED
+          !=
+          BinaryExpression[90:55->90:83]
+            Name[90:55->90:61]
+            g_pid
+            ||
+            UnaryExpression[90:64->90:83]
+              !
+              FunctionCall[90:65->90:83]
+                Name[90:65->90:75]
+                WIFSTOPPED
+                Name[90:76->90:82]
+                status
+        Then:
+        BlockStatement[90:85->95:4]
+          FunctionCall[91:8->91:25]
+            Name[91:8->91:14]
+            perror
+            StringLiteral[91:15->91:23]
+              "waitpid"
+          ReturnStatement[92:8->92:16]
+            NumericLiteral[92:15->92:15]
+            1
+      ForStatement[95:4->143:4]
+        BlockStatement[95:13->143:4]
+          IfStatement[96:8->100:8]
+            Predicate:
+            BinaryExpression[96:12->96:48]
+              FunctionCall[96:12->96:44]
+                Name[96:12->96:18]
+                ptrace
+                Name[96:19->96:29]
+                PT_SYSCALL
+                Name[96:31->96:36]
+                g_pid
+                NumericLiteral[96:38->96:38]
+                0
+                NumericLiteral[96:41->96:41]
+                0
+              ==
+              UnaryExpression[96:47->96:48]
+                -
+                NumericLiteral[96:48->96:48]
+                1
+            Then:
+            BlockStatement[96:51->100:8]
+              FunctionCall[97:12->97:29]
+                Name[97:12->97:18]
+                perror
+                StringLiteral[97:19->97:27]
+                  "syscall"
+              ReturnStatement[98:12->98:20]
+                NumericLiteral[98:19->98:19]
+                1
+          IfStatement[100:8->104:8]
+            Predicate:
+            BinaryExpression[100:12->100:87]
+              FunctionCall[100:12->100:56]
+                Name[100:12->100:19]
+                waitpid
+                Name[100:20->100:25]
+                g_pid
+                UnaryExpression[100:27->100:34]
+                  &
+                  Name[100:28->100:34]
+                  status
+                BinaryExpression[100:36->100:54]
+                  Name[100:36->100:45]
+                  WSTOPPED
+                  |
+                  Name[100:47->100:54]
+                  WEXITED
+              !=
+              BinaryExpression[100:59->100:87]
+                Name[100:59->100:65]
+                g_pid
+                ||
+                UnaryExpression[100:68->100:87]
+                  !
+                  FunctionCall[100:69->100:87]
+                    Name[100:69->100:79]
+                    WIFSTOPPED
+                    Name[100:80->100:86]
+                    status
+            Then:
+            BlockStatement[100:89->104:8]
+              FunctionCall[101:12->101:30]
+                Name[101:12->101:18]
+                perror
+                StringLiteral[101:19->101:28]
+                  "wait_pid"
+              ReturnStatement[102:12->102:20]
+                NumericLiteral[102:19->102:19]
+                1
+          VariableDeclaration[104:8->104:33]
+            Type[104:8->104:24]
+              PtraceRegisters
+            regs
+            BracedInitList[104:31->104:33]
+          IfStatement[105:8->109:8]
+            Predicate:
+            BinaryExpression[105:12->105:52]
+              FunctionCall[105:12->105:48]
+                Name[105:12->105:18]
+                ptrace
+                Name[105:19->105:29]
+                PT_GETREGS
+                Name[105:31->105:36]
+                g_pid
+                UnaryExpression[105:38->105:43]
+                  &
+                  Name[105:39->105:43]
+                  regs
+                NumericLiteral[105:45->105:45]
+                0
+              ==
+              UnaryExpression[105:51->105:52]
+                -
+                NumericLiteral[105:52->105:52]
+                1
+            Then:
+            BlockStatement[105:55->109:8]
+              FunctionCall[106:12->106:29]
+                Name[106:12->106:18]
+                perror
+                StringLiteral[106:19->106:27]
+                  "getregs"
+              ReturnStatement[107:12->107:20]
+                NumericLiteral[107:19->107:19]
+                1
+          VariableDeclaration[109:8->109:36]
+            Type[109:8->109:12]
+              u32
+            syscall_index
+            MemberExpression[109:28->109:36]
+              Name[109:28->109:32]
+              regs
+              Identifier[109:33->109:35]
+              eax
+          VariableDeclaration[110:8->110:27]
+            Type[110:8->110:12]
+              u32
+            arg1
+            MemberExpression[110:19->110:27]
+              Name[110:19->110:23]
+              regs
+              Identifier[110:24->110:26]
+              edx
+          VariableDeclaration[111:8->111:27]
+            Type[111:8->111:12]
+              u32
+            arg2
+            MemberExpression[111:19->111:27]
+              Name[111:19->111:23]
+              regs
+              Identifier[111:24->111:26]
+              ecx
+          VariableDeclaration[112:8->112:27]
+            Type[112:8->112:12]
+              u32
+            arg3
+            MemberExpression[112:19->112:27]
+              Name[112:19->112:23]
+              regs
+              Identifier[112:24->112:26]
+              ebx
+          IfStatement[114:8->118:8]
+            Predicate:
+            BinaryExpression[114:12->114:48]
+              FunctionCall[114:12->114:44]
+                Name[114:12->114:18]
+                ptrace
+                Name[114:19->114:29]
+                PT_SYSCALL
+                Name[114:31->114:36]
+                g_pid
+                NumericLiteral[114:38->114:38]
+                0
+                NumericLiteral[114:41->114:41]
+                0
+              ==
+              UnaryExpression[114:47->114:48]
+                -
+                NumericLiteral[114:48->114:48]
+                1
+            Then:
+            BlockStatement[114:51->118:8]
+              FunctionCall[115:12->115:29]
+                Name[115:12->115:18]
+                perror
+                StringLiteral[115:19->115:27]
+                  "syscall"
+              ReturnStatement[116:12->116:20]
+                NumericLiteral[116:19->116:19]
+                1
+          IfStatement[118:8->123:8]
+            Predicate:
+            BinaryExpression[118:12->118:87]
+              FunctionCall[118:12->118:56]
+                Name[118:12->118:19]
+                waitpid
+                Name[118:20->118:25]
+                g_pid
+                UnaryExpression[118:27->118:34]
+                  &
+                  Name[118:28->118:34]
+                  status
+                BinaryExpression[118:36->118:54]
+                  Name[118:36->118:45]
+                  WSTOPPED
+                  |
+                  Name[118:47->118:54]
+                  WEXITED
+              !=
+              BinaryExpression[118:59->118:87]
+                Name[118:59->118:65]
+                g_pid
+                ||
+                UnaryExpression[118:68->118:87]
+                  !
+                  FunctionCall[118:69->118:87]
+                    Name[118:69->118:79]
+                    WIFSTOPPED
+                    Name[118:80->118:86]
+                    status
+            Then:
+            BlockStatement[118:89->123:8]
+              FunctionCall[119:12->119:30]
+                Name[119:12->119:18]
+                perror
+                StringLiteral[119:19->119:28]
+                  "wait_pid"
+              ReturnStatement[120:12->120:20]
+                NumericLiteral[120:19->120:19]
+                1
+          IfStatement[123:8->128:8]
+            Predicate:
+            BinaryExpression[123:12->123:52]
+              FunctionCall[123:12->123:48]
+                Name[123:12->123:18]
+                ptrace
+                Name[123:19->123:29]
+                PT_GETREGS
+                Name[123:31->123:36]
+                g_pid
+                UnaryExpression[123:38->123:43]
+                  &
+                  Name[123:39->123:43]
+                  regs
+                NumericLiteral[123:45->123:45]
+                0
+              ==
+              UnaryExpression[123:51->123:52]
+                -
+                NumericLiteral[123:52->123:52]
+                1
+            Then:
+            BlockStatement[123:55->128:8]
+              FunctionCall[124:12->124:29]
+                Name[124:12->124:18]
+                perror
+                StringLiteral[124:19->124:27]
+                  "getregs"
+              ReturnStatement[125:12->125:20]
+                NumericLiteral[125:19->125:19]
+                1
+          VariableDeclaration[128:8->128:26]
+            Type[128:8->128:12]
+              u32
+            res
+            MemberExpression[128:18->128:26]
+              Name[128:18->128:22]
+              regs
+              Identifier[128:23->128:25]
+              eax
+          VariableDeclaration[130:8->135:16]
+            Type[130:8->130:13]
+              auto
+            string
+            FunctionCall[130:22->135:16]
+              Name[130:22->130:39]
+              String::formatted
+              StringLiteral[130:40->130:77]
+                "{}({:#08x}, {:#08x}, {:#08x})\t={}\n"
+              FunctionCall[131:12->131:64]
+                Name[131:12->131:30]
+                Syscall::to_string
+                CStyleCastExpression[131:31->131:63]
+                  Type[131:32->131:49]
+                    Syscall::Function
+                  Name[131:50->131:63]
+                  syscall_index
+              Name[132:12->132:16]
+              arg1
+              Name[133:12->133:16]
+              arg2
+              Name[134:12->134:16]
+              arg3
+              Name[135:12->135:15]
+              res
+          IfStatement[137:8->141:4]
+            Predicate:
+            UnaryExpression[137:12->137:38]
+              !
+              BinaryExpression[137:13->137:38]
+                Name[137:13->137:23]
+                trace_file
+                ->
+                FunctionCall[137:25->137:38]
+                  Name[137:25->137:30]
+                  write
+                  Name[137:31->137:37]
+                  string
+            Then:
+            BlockStatement[137:40->141:4]
+              FunctionCall[138:12->138:59]
+                Name[138:12->138:18]
+                warnln
+                StringLiteral[138:19->138:29]
+                  "write: {}"
+                BinaryExpression[138:32->138:58]
+                  Name[138:32->138:42]
+                  trace_file
+                  ->
+                  FunctionCall[138:44->138:58]
+                    Name[138:44->138:56]
+                    error_string
+              ReturnStatement[139:12->139:20]
+                NumericLiteral[139:19->139:19]
+                1
+      ReturnStatement[143:4->143:12]
+        NumericLiteral[143:11->143:11]
+        0
+    }

+ 145 - 0
Userland/Libraries/LibCpp/Tests/strace.cpp

@@ -0,0 +1,145 @@
+static int g_pid = -1;
+
+static void handle_sigint(int)
+{
+    if (g_pid == -1)
+        return;
+
+    if (ptrace(PT_DETACH, g_pid, 0, 0) == -1) {
+        perror("detach");
+    }
+}
+
+int main(int argc, char** argv)
+{
+    if (pledge("stdio wpath cpath proc exec ptrace sigaction", nullptr) < 0) {
+        perror("pledge");
+        return 1;
+    }
+
+    Vector<const char*> child_argv;
+
+    const char* output_filename = nullptr;
+    auto trace_file = Core::File::standard_error();
+
+    Core::ArgsParser parser;
+    parser.set_general_help(
+        "Trace all syscalls and their result.");
+    parser.add_option(g_pid, "Trace the given PID", "pid", 'p', "pid");
+    parser.add_option(output_filename, "Filename to write output to", "output", 'o', "output");
+    parser.add_positional_argument(child_argv, "Arguments to exec", "argument", Core::ArgsParser::Required::No);
+
+    parser.parse(argc, argv);
+
+    if (output_filename != nullptr) {
+        auto open_result = Core::File::open(output_filename, Core::OpenMode::WriteOnly);
+        if (open_result.is_error()) {
+            outln(stderr, "Failed to open output file: {}", open_result.error());
+            return 1;
+        }
+        trace_file = open_result.value();
+    }
+
+    if (pledge("stdio proc exec ptrace sigaction", nullptr) < 0) {
+        perror("pledge");
+        return 1;
+    }
+
+    int status;
+    if (g_pid == -1) {
+        if (child_argv.is_empty()) {
+            outln(stderr, "strace: Expected either a pid or some arguments\n");
+            return 1;
+        }
+
+        child_argv.append(nullptr);
+        int pid = fork();
+        if (pid < 0) {
+            perror("fork");
+            return 1;
+        }
+
+        if (!pid) {
+            if (ptrace(PT_TRACE_ME, 0, 0, 0) == -1) {
+                perror("traceme");
+                return 1;
+            }
+            int rc = execvp(child_argv.first(), const_cast<char**>(child_argv.data()));
+            if (rc < 0) {
+                perror("execvp");
+                exit(1);
+            }
+            VERIFY_NOT_REACHED();
+        }
+
+        g_pid = pid;
+        if (waitpid(pid, &status, WSTOPPED | WEXITED) != pid || !WIFSTOPPED(status)) {
+            perror("waitpid");
+            return 1;
+        }
+    }
+
+    struct sigaction sa;
+    memset(&sa, 0, sizeof(struct sigaction));
+    sa.sa_handler = handle_sigint;
+    sigaction(SIGINT, &sa, nullptr);
+
+    if (ptrace(PT_ATTACH, g_pid, 0, 0) == -1) {
+        perror("attach");
+        return 1;
+    }
+    if (waitpid(g_pid, &status, WSTOPPED | WEXITED) != g_pid || !WIFSTOPPED(status)) {
+        perror("waitpid");
+        return 1;
+    }
+
+    for (;;) {
+        if (ptrace(PT_SYSCALL, g_pid, 0, 0) == -1) {
+            perror("syscall");
+            return 1;
+        }
+        if (waitpid(g_pid, &status, WSTOPPED | WEXITED) != g_pid || !WIFSTOPPED(status)) {
+            perror("wait_pid");
+            return 1;
+        }
+        PtraceRegisters regs = {};
+        if (ptrace(PT_GETREGS, g_pid, &regs, 0) == -1) {
+            perror("getregs");
+            return 1;
+        }
+        u32 syscall_index = regs.eax;
+        u32 arg1 = regs.edx;
+        u32 arg2 = regs.ecx;
+        u32 arg3 = regs.ebx;
+
+        if (ptrace(PT_SYSCALL, g_pid, 0, 0) == -1) {
+            perror("syscall");
+            return 1;
+        }
+        if (waitpid(g_pid, &status, WSTOPPED | WEXITED) != g_pid || !WIFSTOPPED(status)) {
+            perror("wait_pid");
+            return 1;
+        }
+
+        if (ptrace(PT_GETREGS, g_pid, &regs, 0) == -1) {
+            perror("getregs");
+            return 1;
+        }
+
+        u32 res = regs.eax;
+
+        auto string = String::formatted("{}({:#08x}, {:#08x}, {:#08x})\t={}\n",
+            Syscall::to_string((Syscall::Function)syscall_index),
+            arg1,
+            arg2,
+            arg3,
+            res);
+
+        if (!trace_file->write(string)) {
+            warnln("write: {}", trace_file->error_string());
+            return 1;
+        }
+    }
+
+    return 0;
+}

+ 41 - 0
Userland/Libraries/LibCpp/Tests/struct.ast

@@ -0,0 +1,41 @@
+TranslationUnit[1:0->12:0]
+  StructOrClassDeclaration[1:0->7:0]
+  MyStruct
+    MemberDeclaration[3:4->4:4]
+      Type[3:4->3:8]
+        int
+      x
+    MemberDeclaration[4:4->5:0]
+      Pointer[4:12->4:14]
+        Type[4:4->4:12]
+          s
+      next
+  FunctionDeclaration[7:0->12:0]
+    Type[7:0->7:4]
+      int
+    foo
+    (
+    )
+    FunctionDefinition[8:0->12:0]
+    {
+      VariableDeclaration[9:4->9:14]
+        Type[9:4->9:13]
+          MyStruct
+        s
+      FunctionCall[10:4->10:23]
+        Name[10:4->10:10]
+        printf
+        StringLiteral[10:11->10:16]
+          "%d\n"
+        MemberExpression[10:19->10:22]
+          Name[10:19->10:20]
+          s
+          Identifier[10:21->10:21]
+          x
+      ReturnStatement[11:4->11:14]
+        MemberExpression[11:11->11:14]
+          Name[11:11->11:12]
+          s
+          Identifier[11:13->11:13]
+          x
+    }

+ 13 - 0
Userland/Libraries/LibCpp/Tests/struct.cpp

@@ -0,0 +1,13 @@
+
+struct MyStruct
+{
+    int x;
+    struct s* next;
+};
+
+int foo()
+{
+    MyStruct s;
+    printf("%d\n", s.x);
+    return s.x;
+}