diff --git a/src/Core/Diagnostics.cs b/src/Core/Diagnostics.cs
index a7f40ac6..a5bf538e 100644
--- a/src/Core/Diagnostics.cs
+++ b/src/Core/Diagnostics.cs
@@ -128,7 +128,9 @@ namespace CppSharp
             {
                 Console.WriteLine(message);
             }
-            Debug.WriteLine(message);
+
+            // Super slow, don't use this for now
+            // Debug.WriteLine(message);
         }
 
         public void PushIndent(int level)
diff --git a/src/CppParser/AST.cpp b/src/CppParser/AST.cpp
index e5823a23..27e1c278 100644
--- a/src/CppParser/AST.cpp
+++ b/src/CppParser/AST.cpp
@@ -11,58 +11,61 @@
 #include <llvm/ADT/SmallString.h>
 #include <llvm/Support/Path.h>
 
-// copy from widenPath ('llvm/lib/Support/Windows/Path.inc')
-static std::string normalizePath(const std::string& File)
-{
-    llvm::SmallString<2 * 128> Result;
-
-    for (llvm::sys::path::const_iterator I = llvm::sys::path::begin(File),
-                                         E = llvm::sys::path::end(File);
-         I != E; ++I)
+namespace CppSharp::CppParser::AST {
+namespace {
+    // Adapted from widenPath ('llvm/lib/Support/Windows/Path.inc')
+    std::string normalizePath(const std::string_view& file)
     {
-        if (I->size() == 1 && *I == ".")
-            continue;
-        if (I->size() == 2 && *I == "..")
-            llvm::sys::path::remove_filename(Result);
-        else
-            llvm::sys::path::append(Result, *I);
-    }
+        llvm::SmallString<2 * 128> Result;
+
+        for (llvm::sys::path::const_iterator I = llvm::sys::path::begin(file),
+                                             E = llvm::sys::path::end(file);
+             I != E; ++I)
+        {
+            if (I->size() == 1 && *I == ".")
+                continue;
+
+            if (I->size() == 2 && *I == "..")
+                llvm::sys::path::remove_filename(Result);
+            else
+                llvm::sys::path::append(Result, *I);
+        }
 
 #ifdef _WIN32
-    // Clean up the file path.
-    std::replace(Result.begin(), Result.end(), '/', '\\');
+        // Clean up the file path.
+        std::replace(Result.begin(), Result.end(), '/', '\\');
 #endif
 
-    return Result.c_str();
-}
+        return Result.c_str();
+    }
 
-template <typename T>
-static std::vector<T> split(const T& str, const T& delimiters)
-{
-    std::vector<T> v;
-    if (str.length() == 0)
+    std::vector<std::string_view> split(const std::string_view& str, const std::string_view& delimiters)
     {
-        v.push_back(str);
+        std::vector<std::string_view> v;
+        if (str.empty())
+        {
+            v.emplace_back(str);
+            return v;
+        }
+
+        std::string_view::size_type start = 0;
+        auto pos = str.find_first_of(delimiters, start);
+        while (pos != std::string_view::npos)
+        {
+            if (pos != start) // ignore empty tokens
+                v.emplace_back(str.substr(start, pos - start));
+
+            start = pos + 1;
+            pos = str.find_first_of(delimiters, start);
+        }
+
+        if (start < str.length())                                    // Ignore trailing delimiter
+            v.emplace_back(str.substr(start, str.length() - start)); // Add what's left of the string
+
         return v;
     }
-    typename T::size_type start = 0;
-    auto pos = str.find_first_of(delimiters, start);
-    while (pos != T::npos)
-    {
-        if (pos != start) // ignore empty tokens
-            v.emplace_back(str, start, pos - start);
-        start = pos + 1;
-        pos = str.find_first_of(delimiters, start);
-    }
-    if (start < str.length()) // ignore trailing delimiter
-                              // add what's left of the string
-        v.emplace_back(str, start, str.length() - start);
-    return v;
-}
-
-namespace CppSharp { namespace CppParser { namespace AST {
 
-    static void deleteExpression(ExpressionObsolete* expression)
+    void deleteExpression(ExpressionObsolete* expression)
     {
         if (expression)
         {
@@ -84,1373 +87,1364 @@ namespace CppSharp { namespace CppParser { namespace AST {
             }
         }
     }
+} // namespace
 
-    Type::Type(TypeKind kind)
-        : kind(kind)
-    {
-    }
-    Type::Type(const Type& rhs)
-        : kind(rhs.kind)
-        , isDependent(rhs.isDependent)
-    {
-    }
+Type::Type(TypeKind kind)
+    : kind(kind)
+{
+}
+Type::Type(const Type& rhs)
+    : kind(rhs.kind)
+    , isDependent(rhs.isDependent)
+{
+}
 
-    QualifiedType::QualifiedType()
-        : type(0)
-    {
-    }
+QualifiedType::QualifiedType() {}
 
-    TagType::TagType()
-        : Type(TypeKind::Tag)
-    {
-    }
+TagType::TagType()
+    : Type(TypeKind::Tag)
+{
+}
 
-    ArrayType::ArrayType()
-        : Type(TypeKind::Array)
-        , size(0)
-        , elementSize(0)
-    {
-    }
+ArrayType::ArrayType()
+    : Type(TypeKind::Array)
+    , size(0)
+    , elementSize(0)
+{
+}
 
-    FunctionType::FunctionType()
-        : Type(TypeKind::Function)
-        , callingConvention(CallingConvention::Default)
-        , exceptionSpecType(ExceptionSpecType::None)
-    {
-    }
+FunctionType::FunctionType()
+    : Type(TypeKind::Function)
+    , callingConvention(CallingConvention::Default)
+    , exceptionSpecType(ExceptionSpecType::None)
+{
+}
 
-    FunctionType::~FunctionType() {}
+FunctionType::~FunctionType() {}
 
-    DEF_VECTOR(FunctionType, Parameter*, Parameters)
+DEF_VECTOR(FunctionType, Parameter*, Parameters)
 
-    PointerType::PointerType()
-        : Type(TypeKind::Pointer)
-    {
-    }
+PointerType::PointerType()
+    : Type(TypeKind::Pointer)
+{
+}
 
-    MemberPointerType::MemberPointerType()
-        : Type(TypeKind::MemberPointer)
-    {
-    }
+MemberPointerType::MemberPointerType()
+    : Type(TypeKind::MemberPointer)
+{
+}
 
-    TypedefType::TypedefType()
-        : Type(TypeKind::Typedef)
-        , declaration(0)
-    {
-    }
+TypedefType::TypedefType()
+    : Type(TypeKind::Typedef)
+    , declaration(nullptr)
+{
+}
 
-    AttributedType::AttributedType()
-        : Type(TypeKind::Attributed)
-    {
-    }
+AttributedType::AttributedType()
+    : Type(TypeKind::Attributed)
+{
+}
 
-    DecayedType::DecayedType()
-        : Type(TypeKind::Decayed)
-    {
-    }
+DecayedType::DecayedType()
+    : Type(TypeKind::Decayed)
+{
+}
 
-    // Template
-    TemplateParameter::TemplateParameter(DeclarationKind kind)
-        : Declaration(kind)
-        , depth(0)
-        , index(0)
-        , isParameterPack(false)
-    {
-    }
+// Template
+TemplateParameter::TemplateParameter(DeclarationKind kind)
+    : Declaration(kind)
+    , depth(0)
+    , index(0)
+    , isParameterPack(false)
+{
+}
 
-    TemplateParameter::~TemplateParameter()
-    {
-    }
+TemplateParameter::~TemplateParameter()
+{
+}
 
-    TemplateTemplateParameter::TemplateTemplateParameter()
-        : Template(DeclarationKind::TemplateTemplateParm)
-        , isParameterPack(false)
-        , isPackExpansion(false)
-        , isExpandedParameterPack(false)
-    {
-    }
+TemplateTemplateParameter::TemplateTemplateParameter()
+    : Template(DeclarationKind::TemplateTemplateParm)
+    , isParameterPack(false)
+    , isPackExpansion(false)
+    , isExpandedParameterPack(false)
+{
+}
 
-    TemplateTemplateParameter::~TemplateTemplateParameter()
-    {
-    }
+TemplateTemplateParameter::~TemplateTemplateParameter()
+{
+}
 
-    // TemplateParameter
-    TypeTemplateParameter::TypeTemplateParameter()
-        : TemplateParameter(DeclarationKind::TemplateTypeParm)
-    {
-    }
+// TemplateParameter
+TypeTemplateParameter::TypeTemplateParameter()
+    : TemplateParameter(DeclarationKind::TemplateTypeParm)
+{
+}
 
-    TypeTemplateParameter::TypeTemplateParameter(const TypeTemplateParameter& rhs)
-        : TemplateParameter(rhs.kind)
-        , defaultArgument(rhs.defaultArgument)
-    {
-    }
+TypeTemplateParameter::TypeTemplateParameter(const TypeTemplateParameter& rhs)
+    : TemplateParameter(rhs.kind)
+    , defaultArgument(rhs.defaultArgument)
+{
+}
 
-    TypeTemplateParameter::~TypeTemplateParameter() {}
+TypeTemplateParameter::~TypeTemplateParameter() {}
 
-    NonTypeTemplateParameter::NonTypeTemplateParameter()
-        : TemplateParameter(DeclarationKind::NonTypeTemplateParm)
-        , defaultArgument(0)
-        , position(0)
-        , isPackExpansion(false)
-        , isExpandedParameterPack(false)
-    {
-    }
+NonTypeTemplateParameter::NonTypeTemplateParameter()
+    : TemplateParameter(DeclarationKind::NonTypeTemplateParm)
+    , defaultArgument(nullptr)
+    , position(0)
+    , isPackExpansion(false)
+    , isExpandedParameterPack(false)
+{
+}
 
-    NonTypeTemplateParameter::NonTypeTemplateParameter(const NonTypeTemplateParameter& rhs)
-        : TemplateParameter(rhs.kind)
-        , defaultArgument(rhs.defaultArgument)
-        , position(rhs.position)
-        , isPackExpansion(rhs.isPackExpansion)
-        , isExpandedParameterPack(rhs.isExpandedParameterPack)
-        , type(rhs.type)
-    {
-    }
+NonTypeTemplateParameter::NonTypeTemplateParameter(const NonTypeTemplateParameter& rhs)
+    : TemplateParameter(rhs.kind)
+    , defaultArgument(rhs.defaultArgument)
+    , position(rhs.position)
+    , isPackExpansion(rhs.isPackExpansion)
+    , isExpandedParameterPack(rhs.isExpandedParameterPack)
+    , type(rhs.type)
+{
+}
 
-    NonTypeTemplateParameter::~NonTypeTemplateParameter()
-    {
-        deleteExpression(defaultArgument);
-    }
+NonTypeTemplateParameter::~NonTypeTemplateParameter()
+{
+    deleteExpression(defaultArgument);
+}
 
-    TemplateArgument::TemplateArgument()
-        : declaration(0)
-        , integral(0)
-    {
-    }
+TemplateArgument::TemplateArgument()
+    : declaration(nullptr)
+    , integral(0)
+{
+}
 
-    TemplateSpecializationType::TemplateSpecializationType()
-        : Type(TypeKind::TemplateSpecialization)
-        , _template(0)
-    {
-    }
+TemplateSpecializationType::TemplateSpecializationType()
+    : Type(TypeKind::TemplateSpecialization)
+    , _template(nullptr)
+{
+}
 
-    TemplateSpecializationType::TemplateSpecializationType(
-        const TemplateSpecializationType& rhs)
-        : Type(rhs)
-        , Arguments(rhs.Arguments)
-        , _template(rhs._template)
-        , desugared(rhs.desugared)
-    {
-    }
+TemplateSpecializationType::TemplateSpecializationType(
+    const TemplateSpecializationType& rhs)
+    : Type(rhs)
+    , Arguments(rhs.Arguments)
+    , _template(rhs._template)
+    , desugared(rhs.desugared)
+{
+}
 
-    TemplateSpecializationType::~TemplateSpecializationType() {}
+TemplateSpecializationType::~TemplateSpecializationType() {}
 
-    DEF_VECTOR(TemplateSpecializationType, TemplateArgument, Arguments)
+DEF_VECTOR(TemplateSpecializationType, TemplateArgument, Arguments)
 
-    DependentTemplateSpecializationType::DependentTemplateSpecializationType()
-        : Type(TypeKind::DependentTemplateSpecialization)
-    {
-    }
+DependentTemplateSpecializationType::DependentTemplateSpecializationType()
+    : Type(TypeKind::DependentTemplateSpecialization)
+{
+}
 
-    DependentTemplateSpecializationType::DependentTemplateSpecializationType(
-        const DependentTemplateSpecializationType& rhs)
-        : Type(rhs)
-        , Arguments(rhs.Arguments)
-        , desugared(rhs.desugared)
-    {
-    }
+DependentTemplateSpecializationType::DependentTemplateSpecializationType(
+    const DependentTemplateSpecializationType& rhs)
+    : Type(rhs)
+    , Arguments(rhs.Arguments)
+    , desugared(rhs.desugared)
+{
+}
 
-    DependentTemplateSpecializationType::~DependentTemplateSpecializationType() {}
+DependentTemplateSpecializationType::~DependentTemplateSpecializationType() {}
 
-    DEF_VECTOR(DependentTemplateSpecializationType, TemplateArgument, Arguments)
+DEF_VECTOR(DependentTemplateSpecializationType, TemplateArgument, Arguments)
 
-    TemplateParameterType::TemplateParameterType()
-        : Type(TypeKind::TemplateParameter)
-        , parameter(0)
-    {
-    }
+TemplateParameterType::TemplateParameterType()
+    : Type(TypeKind::TemplateParameter)
+    , parameter(nullptr)
+{
+}
 
-    TemplateParameterType::~TemplateParameterType() {}
+TemplateParameterType::~TemplateParameterType() {}
 
-    TemplateParameterSubstitutionType::TemplateParameterSubstitutionType()
-        : Type(TypeKind::TemplateParameterSubstitution)
-        , replacedParameter(0)
-    {
-    }
+TemplateParameterSubstitutionType::TemplateParameterSubstitutionType()
+    : Type(TypeKind::TemplateParameterSubstitution)
+    , replacedParameter(nullptr)
+{
+}
 
-    InjectedClassNameType::InjectedClassNameType()
-        : Type(TypeKind::InjectedClassName)
-        , _class(0)
-    {
-    }
+InjectedClassNameType::InjectedClassNameType()
+    : Type(TypeKind::InjectedClassName)
+    , _class(nullptr)
+{
+}
 
-    DependentNameType::DependentNameType()
-        : Type(TypeKind::DependentName)
-    {
-    }
+DependentNameType::DependentNameType()
+    : Type(TypeKind::DependentName)
+{
+}
 
-    DependentNameType::~DependentNameType() {}
+DependentNameType::~DependentNameType() {}
 
-    PackExpansionType::PackExpansionType()
-        : Type(TypeKind::PackExpansion)
-    {
-    }
+PackExpansionType::PackExpansionType()
+    : Type(TypeKind::PackExpansion)
+{
+}
 
-    UnaryTransformType::UnaryTransformType()
-        : Type(TypeKind::UnaryTransform)
-    {
-    }
+UnaryTransformType::UnaryTransformType()
+    : Type(TypeKind::UnaryTransform)
+{
+}
 
-    UnresolvedUsingType::UnresolvedUsingType()
-        : Type(TypeKind::UnresolvedUsing)
-    {
-    }
+UnresolvedUsingType::UnresolvedUsingType()
+    : Type(TypeKind::UnresolvedUsing)
+{
+}
 
-    VectorType::VectorType()
-        : Type(TypeKind::Vector)
-        , numElements(0)
-    {
-    }
+VectorType::VectorType()
+    : Type(TypeKind::Vector)
+    , numElements(0)
+{
+}
 
-    BuiltinType::BuiltinType()
-        : CppSharp::CppParser::AST::Type(TypeKind::Builtin)
-    {
-    }
+BuiltinType::BuiltinType()
+    : Type(TypeKind::Builtin)
+{
+}
 
-    VTableComponent::VTableComponent()
-        : offset(0)
-        , declaration(0)
-    {
-    }
+VTableComponent::VTableComponent()
+    : offset(0)
+    , declaration(nullptr)
+{
+}
 
-    // VTableLayout
-    VTableLayout::VTableLayout() {}
-    VTableLayout::VTableLayout(const VTableLayout& rhs)
-        : Components(rhs.Components)
-    {
-    }
-    VTableLayout::~VTableLayout() {}
+// VTableLayout
+VTableLayout::VTableLayout() {}
+VTableLayout::VTableLayout(const VTableLayout& rhs)
+    : Components(rhs.Components)
+{
+}
+VTableLayout::~VTableLayout() {}
 
-    DEF_VECTOR(VTableLayout, VTableComponent, Components)
+DEF_VECTOR(VTableLayout, VTableComponent, Components)
 
-    VFTableInfo::VFTableInfo()
-        : VBTableIndex(0)
-        , VFPtrOffset(0)
-        , VFPtrFullOffset(0)
-    {
-    }
-    VFTableInfo::VFTableInfo(const VFTableInfo& rhs)
-        : VBTableIndex(rhs.VBTableIndex)
-        , VFPtrOffset(rhs.VFPtrOffset)
-        , VFPtrFullOffset(rhs.VFPtrFullOffset)
-        , layout(rhs.layout)
-    {
-    }
+VFTableInfo::VFTableInfo()
+    : VBTableIndex(0)
+    , VFPtrOffset(0)
+    , VFPtrFullOffset(0)
+{
+}
+VFTableInfo::VFTableInfo(const VFTableInfo& rhs)
+    : VBTableIndex(rhs.VBTableIndex)
+    , VFPtrOffset(rhs.VFPtrOffset)
+    , VFPtrFullOffset(rhs.VFPtrFullOffset)
+    , layout(rhs.layout)
+{
+}
 
-    LayoutField::LayoutField()
-        : offset(0)
-        , fieldPtr(0)
-    {
-    }
+LayoutField::LayoutField()
+    : offset(0)
+    , fieldPtr(nullptr)
+{
+}
 
-    LayoutField::LayoutField(const LayoutField& other)
-        : offset(other.offset)
-        , name(other.name)
-        , qualifiedType(other.qualifiedType)
-        , fieldPtr(other.fieldPtr)
-    {
-    }
+LayoutField::LayoutField(const LayoutField& other)
+    : offset(other.offset)
+    , name(other.name)
+    , qualifiedType(other.qualifiedType)
+    , fieldPtr(other.fieldPtr)
+{
+}
 
-    LayoutField::~LayoutField() {}
+LayoutField::~LayoutField() {}
 
-    LayoutBase::LayoutBase()
-        : offset(0)
-        , _class(0)
-    {
-    }
+LayoutBase::LayoutBase()
+    : offset(0)
+    , _class(nullptr)
+{
+}
 
-    LayoutBase::LayoutBase(const LayoutBase& other)
-        : offset(other.offset)
-        , _class(other._class)
-    {
-    }
+LayoutBase::LayoutBase(const LayoutBase& other)
+    : offset(other.offset)
+    , _class(other._class)
+{
+}
 
-    LayoutBase::~LayoutBase() {}
+LayoutBase::~LayoutBase() {}
 
-    ClassLayout::ClassLayout()
-        : ABI(CppAbi::Itanium)
-        , argABI(RecordArgABI::Default)
-        , hasOwnVFPtr(false)
-        , VBPtrOffset(0)
-        , alignment(0)
-        , size(0)
-        , dataSize(0)
-    {
-    }
+ClassLayout::ClassLayout()
+    : ABI(CppAbi::Itanium)
+    , argABI(RecordArgABI::Default)
+    , hasOwnVFPtr(false)
+    , VBPtrOffset(0)
+    , alignment(0)
+    , size(0)
+    , dataSize(0)
+{
+}
 
-    DEF_VECTOR(ClassLayout, VFTableInfo, VFTables)
-
-    DEF_VECTOR(ClassLayout, LayoutField, Fields)
-
-    DEF_VECTOR(ClassLayout, LayoutBase, Bases)
-
-    Declaration::Declaration(DeclarationKind kind)
-        : kind(kind)
-        , access(AccessSpecifier::Public)
-        , _namespace(0)
-        , location(0)
-        , lineNumberStart(0)
-        , lineNumberEnd(0)
-        , comment(0)
-        , isIncomplete(false)
-        , isDependent(false)
-        , isImplicit(false)
-        , isInvalid(false)
-        , isDeprecated(false)
-        , completeDeclaration(0)
-        , definitionOrder(0)
-        , originalPtr(0)
-        , alignAs(0)
-        , maxFieldAlignment(0)
-    {
-    }
+DEF_VECTOR(ClassLayout, VFTableInfo, VFTables)
+
+DEF_VECTOR(ClassLayout, LayoutField, Fields)
+
+DEF_VECTOR(ClassLayout, LayoutBase, Bases)
+
+Declaration::Declaration(DeclarationKind kind)
+    : kind(kind)
+    , access(AccessSpecifier::Public)
+    , _namespace(nullptr)
+    , location(0)
+    , lineNumberStart(0)
+    , lineNumberEnd(0)
+    , comment(nullptr)
+    , isIncomplete(false)
+    , isDependent(false)
+    , isImplicit(false)
+    , isInvalid(false)
+    , isDeprecated(false)
+    , completeDeclaration(nullptr)
+    , definitionOrder(0)
+    , originalPtr(nullptr)
+    , alignAs(0)
+    , maxFieldAlignment(0)
+{
+}
 
-    Declaration::Declaration(const Declaration& rhs)
-        : kind(rhs.kind)
-        , access(rhs.access)
-        , _namespace(rhs._namespace)
-        , location(rhs.location.ID)
-        , lineNumberStart(rhs.lineNumberStart)
-        , lineNumberEnd(rhs.lineNumberEnd)
-        , name(rhs.name)
-        , comment(rhs.comment)
-        , debugText(rhs.debugText)
-        , isIncomplete(rhs.isIncomplete)
-        , isDependent(rhs.isDependent)
-        , isImplicit(rhs.isImplicit)
-        , isInvalid(rhs.isInvalid)
-        , isDeprecated(rhs.isDeprecated)
-        , completeDeclaration(rhs.completeDeclaration)
-        , definitionOrder(rhs.definitionOrder)
-        , PreprocessedEntities(rhs.PreprocessedEntities)
-        , originalPtr(rhs.originalPtr)
-    {
-    }
+Declaration::Declaration(const Declaration& rhs)
+    : kind(rhs.kind)
+    , access(rhs.access)
+    , _namespace(rhs._namespace)
+    , location(rhs.location.ID)
+    , lineNumberStart(rhs.lineNumberStart)
+    , lineNumberEnd(rhs.lineNumberEnd)
+    , name(rhs.name)
+    , comment(rhs.comment)
+    , debugText(rhs.debugText)
+    , isIncomplete(rhs.isIncomplete)
+    , isDependent(rhs.isDependent)
+    , isImplicit(rhs.isImplicit)
+    , isInvalid(rhs.isInvalid)
+    , isDeprecated(rhs.isDeprecated)
+    , completeDeclaration(rhs.completeDeclaration)
+    , definitionOrder(rhs.definitionOrder)
+    , PreprocessedEntities(rhs.PreprocessedEntities)
+    , originalPtr(rhs.originalPtr)
+{
+}
 
-    Declaration::~Declaration()
-    {
-    }
+Declaration::~Declaration()
+{
+}
 
-    DEF_VECTOR(Declaration, PreprocessedEntity*, PreprocessedEntities)
-    DEF_VECTOR(Declaration, Declaration*, Redeclarations)
+DEF_VECTOR(Declaration, PreprocessedEntity*, PreprocessedEntities)
+DEF_VECTOR(Declaration, Declaration*, Redeclarations)
 
-    DeclarationContext::DeclarationContext(DeclarationKind kind)
-        : Declaration(kind)
-        , isAnonymous(false)
-    {
-    }
+DeclarationContext::DeclarationContext(DeclarationKind kind)
+    : Declaration(kind)
+    , isAnonymous(false)
+{
+}
 
-    DEF_VECTOR(DeclarationContext, Namespace*, Namespaces)
-    DEF_VECTOR(DeclarationContext, Enumeration*, Enums)
-    DEF_VECTOR(DeclarationContext, Function*, Functions)
-    DEF_VECTOR(DeclarationContext, Class*, Classes)
-    DEF_VECTOR(DeclarationContext, Template*, Templates)
-    DEF_VECTOR(DeclarationContext, TypedefDecl*, Typedefs)
-    DEF_VECTOR(DeclarationContext, TypeAlias*, TypeAliases)
-    DEF_VECTOR(DeclarationContext, Variable*, Variables)
-    DEF_VECTOR(DeclarationContext, Friend*, Friends)
-
-    Declaration* DeclarationContext::FindAnonymous(const std::string& key)
-    {
-        auto it = anonymous.find(key);
-        return (it != anonymous.end()) ? it->second : 0;
-    }
+DEF_VECTOR(DeclarationContext, Namespace*, Namespaces)
+DEF_VECTOR(DeclarationContext, Enumeration*, Enums)
+DEF_VECTOR(DeclarationContext, Function*, Functions)
+DEF_VECTOR(DeclarationContext, Class*, Classes)
+DEF_VECTOR(DeclarationContext, Template*, Templates)
+DEF_VECTOR(DeclarationContext, TypedefDecl*, Typedefs)
+DEF_VECTOR(DeclarationContext, TypeAlias*, TypeAliases)
+DEF_VECTOR(DeclarationContext, Variable*, Variables)
+DEF_VECTOR(DeclarationContext, Friend*, Friends)
+
+Declaration* DeclarationContext::FindAnonymous(const std::string_view& key)
+{
+    auto it = anonymous.find(key);
+    return (it != anonymous.end()) ? it->second : nullptr;
+}
 
-    Namespace* DeclarationContext::FindNamespace(const std::string& Name)
-    {
-        auto namespaces = split<std::string>(Name, "::");
-        return FindNamespace(namespaces);
-    }
+Namespace* DeclarationContext::FindNamespace(const std::string_view& name)
+{
+    const auto namespaces = split(name, "::");
+    return FindNamespace(namespaces);
+}
 
-    Namespace*
-    DeclarationContext::FindNamespace(const std::vector<std::string>& Namespaces)
+Namespace* DeclarationContext::FindNamespace(const std::vector<std::string_view>& namespaces)
+{
+    auto currentNamespace = this;
+    for (auto& _namespace : namespaces)
     {
-        auto currentNamespace = this;
-        for (auto I = Namespaces.begin(), E = Namespaces.end(); I != E; ++I)
-        {
-            auto& _namespace = *I;
-
-            auto childNamespace = std::find_if(currentNamespace->Namespaces.begin(),
-                                               currentNamespace->Namespaces.end(),
-                                               [&](CppSharp::CppParser::AST::Namespace* ns)
-                                               {
-                                                   return ns->name == _namespace;
-                                               });
-
-            if (childNamespace == currentNamespace->Namespaces.end())
-                return nullptr;
+        auto childNamespace = std::find_if(currentNamespace->Namespaces.begin(),
+                                           currentNamespace->Namespaces.end(),
+                                           [&](const Namespace* ns)
+                                           {
+                                               return ns->name == _namespace;
+                                           });
 
-            currentNamespace = *childNamespace;
-        }
+        if (childNamespace == currentNamespace->Namespaces.end())
+            return nullptr;
 
-        return (CppSharp::CppParser::AST::Namespace*)currentNamespace;
+        currentNamespace = *childNamespace;
     }
 
-    Namespace* DeclarationContext::FindCreateNamespace(const std::string& Name)
-    {
-        auto _namespace = FindNamespace(Name);
+    return (Namespace*)currentNamespace;
+}
 
-        if (!_namespace)
-        {
-            _namespace = new Namespace();
-            _namespace->name = Name;
-            _namespace->_namespace = this;
+Namespace& DeclarationContext::FindCreateNamespace(const std::string_view& Name)
+{
+    auto _namespace = FindNamespace(Name);
 
-            Namespaces.push_back(_namespace);
-        }
+    if (!_namespace)
+    {
+        _namespace = new Namespace();
+        _namespace->name = Name;
+        _namespace->_namespace = this;
 
-        return _namespace;
+        Namespaces.push_back(_namespace);
     }
 
-    Class* DeclarationContext::FindClass(const void* OriginalPtr,
-                                         const std::string& Name,
-                                         bool IsComplete)
-    {
-        if (Name.empty())
-            return nullptr;
+    return *_namespace;
+}
 
-        auto entries = split<std::string>(Name, "::");
+Class* DeclarationContext::FindClass(const void* OriginalPtr, const std::string_view& Name, bool IsComplete)
+{
+    if (Name.empty())
+        return nullptr;
 
-        if (entries.size() == 1)
-        {
-            auto _class = std::find_if(Classes.begin(), Classes.end(),
-                                       [OriginalPtr, Name, IsComplete](Class* klass)
-                                       {
-                                           return (OriginalPtr && klass->originalPtr == OriginalPtr) ||
-                                                  (klass->name == Name && klass->isIncomplete == !IsComplete);
-                                       });
+    auto namespaces = split(Name, "::");
 
-            return _class != Classes.end() ? *_class : nullptr;
-        }
+    if (namespaces.size() == 1)
+    {
+        auto _class = std::find_if(Classes.begin(), Classes.end(),
+                                   [OriginalPtr, Name, IsComplete](const Class* klass)
+                                   {
+                                       return (OriginalPtr && klass->originalPtr == OriginalPtr) ||
+                                              (klass->name == Name && klass->isIncomplete == !IsComplete);
+                                   });
 
-        auto className = entries[entries.size() - 1];
+        return _class != Classes.end() ? *_class : nullptr;
+    }
 
-        std::vector<std::string> namespaces;
-        std::copy_n(entries.begin(), entries.size() - 1, std::back_inserter(namespaces));
+    auto className = namespaces.back();
+    namespaces.pop_back();
 
-        auto _namespace = FindNamespace(namespaces);
-        if (!_namespace)
-            return nullptr;
+    auto _namespace = FindNamespace(namespaces);
+    if (!_namespace)
+        return nullptr;
 
-        return _namespace->FindClass(OriginalPtr, className, IsComplete);
-    }
+    return _namespace->FindClass(OriginalPtr, className, IsComplete);
+}
 
-    Class* DeclarationContext::CreateClass(const std::string& Name, bool IsComplete)
-    {
-        auto _class = new Class();
-        _class->name = Name;
-        _class->_namespace = this;
-        _class->isIncomplete = !IsComplete;
+Class* DeclarationContext::CreateClass(const std::string_view& Name, bool IsComplete)
+{
+    auto _class = new Class();
+    _class->name = Name;
+    _class->_namespace = this;
+    _class->isIncomplete = !IsComplete;
 
-        return _class;
-    }
+    return _class;
+}
 
-    Class* DeclarationContext::FindClass(const void* OriginalPtr,
-                                         const std::string& Name,
-                                         bool IsComplete,
-                                         bool Create)
-    {
-        auto _class = FindClass(OriginalPtr, Name, IsComplete);
+Class* DeclarationContext::FindClass(const void* OriginalPtr, const std::string_view& Name, bool IsComplete, bool Create)
+{
+    auto _class = FindClass(OriginalPtr, Name, IsComplete);
 
-        if (!_class)
+    if (!_class)
+    {
+        if (Create)
         {
-            if (Create)
-            {
-                _class = CreateClass(Name, IsComplete);
-                Classes.push_back(_class);
-            }
-
-            return _class;
+            _class = CreateClass(Name, IsComplete);
+            Classes.push_back(_class);
         }
 
         return _class;
     }
 
-    Enumeration* DeclarationContext::FindEnum(const void* OriginalPtr)
+    return _class;
+}
+
+Enumeration* DeclarationContext::FindEnum(const void* OriginalPtr)
+{
+    auto foundEnum = std::find_if(Enums.begin(), Enums.end(),
+                                  [&](const Enumeration* enumeration)
+                                  {
+                                      return enumeration->originalPtr == OriginalPtr;
+                                  });
+
+    if (foundEnum != Enums.end())
+        return *foundEnum;
+
+    return nullptr;
+}
+
+Enumeration* DeclarationContext::FindEnum(const std::string_view& Name, bool Create)
+{
+    auto namespaces = split(Name, "::");
+
+    if (namespaces.size() == 1)
     {
         auto foundEnum = std::find_if(Enums.begin(), Enums.end(),
-                                      [&](Enumeration* enumeration)
+                                      [&](const Enumeration* _enum)
                                       {
-                                          return enumeration->originalPtr == OriginalPtr;
+                                          return _enum->name == Name;
                                       });
 
         if (foundEnum != Enums.end())
             return *foundEnum;
 
-        return nullptr;
+        if (!Create)
+            return nullptr;
+
+        auto _enum = new Enumeration();
+        _enum->name = Name;
+        _enum->_namespace = this;
+        Enums.push_back(_enum);
+        return _enum;
     }
 
-    Enumeration* DeclarationContext::FindEnum(const std::string& Name, bool Create)
-    {
-        auto entries = split<std::string>(Name, "::");
+    auto enumName = namespaces.back();
+    namespaces.pop_back();
 
-        if (entries.size() == 1)
-        {
-            auto foundEnum = std::find_if(Enums.begin(), Enums.end(),
-                                          [&](Enumeration* _enum)
-                                          {
-                                              return _enum->name == Name;
-                                          });
-
-            if (foundEnum != Enums.end())
-                return *foundEnum;
-
-            if (!Create)
-                return nullptr;
-
-            auto _enum = new Enumeration();
-            _enum->name = Name;
-            _enum->_namespace = this;
-            Enums.push_back(_enum);
-            return _enum;
-        }
+    auto _namespace = FindNamespace(namespaces);
+    if (!_namespace)
+        return nullptr;
 
-        auto enumName = entries[entries.size() - 1];
+    return _namespace->FindEnum(enumName, Create);
+}
 
-        std::vector<std::string> namespaces;
-        std::copy_n(entries.begin(), entries.size() - 1, std::back_inserter(namespaces));
+Enumeration* DeclarationContext::FindEnumWithItem(const std::string_view& Name)
+{
+    auto foundEnumIt = std::find_if(Enums.begin(), Enums.end(),
+                                    [&](Enumeration* _enum)
+                                    {
+                                        return _enum->FindItemByName(Name) != nullptr;
+                                    });
 
-        auto _namespace = FindNamespace(namespaces);
-        if (!_namespace)
-            return nullptr;
+    if (foundEnumIt != Enums.end())
+        return *foundEnumIt;
 
-        return _namespace->FindEnum(enumName, Create);
+    for (auto& Namespace : Namespaces)
+    {
+        auto foundEnum = Namespace->FindEnumWithItem(Name);
+        if (foundEnum != nullptr)
+            return foundEnum;
     }
 
-    Enumeration* DeclarationContext::FindEnumWithItem(const std::string& Name)
+    for (auto& Class : Classes)
     {
-        auto foundEnumIt = std::find_if(Enums.begin(), Enums.end(),
-                                        [&](Enumeration* _enum)
-                                        {
-                                            return _enum->FindItemByName(Name) != nullptr;
-                                        });
-        if (foundEnumIt != Enums.end())
-            return *foundEnumIt;
-        for (auto it = Namespaces.begin(); it != Namespaces.end(); ++it)
-        {
-            auto foundEnum = (*it)->FindEnumWithItem(Name);
-            if (foundEnum != nullptr)
-                return foundEnum;
-        }
-        for (auto it = Classes.begin(); it != Classes.end(); ++it)
-        {
-            auto foundEnum = (*it)->FindEnumWithItem(Name);
-            if (foundEnum != nullptr)
-                return foundEnum;
-        }
-        return nullptr;
+        auto foundEnum = Class->FindEnumWithItem(Name);
+        if (foundEnum != nullptr)
+            return foundEnum;
     }
 
-    Function* DeclarationContext::FindFunction(const std::string& USR)
-    {
-        auto foundFunction = std::find_if(Functions.begin(), Functions.end(),
-                                          [&](Function* func)
-                                          {
-                                              return func->USR == USR;
-                                          });
+    return nullptr;
+}
 
-        if (foundFunction != Functions.end())
-            return *foundFunction;
+Function* DeclarationContext::FindFunction(const std::string_view& USR)
+{
+    auto foundFunction = std::find_if(Functions.begin(), Functions.end(),
+                                      [&](const Function* func)
+                                      {
+                                          return func->USR == USR;
+                                      });
 
-        auto foundTemplate = std::find_if(Templates.begin(), Templates.end(),
-                                          [&](Template* t)
-                                          {
-                                              return t->TemplatedDecl && t->TemplatedDecl->USR == USR;
-                                          });
+    if (foundFunction != Functions.end())
+        return *foundFunction;
 
-        if (foundTemplate != Templates.end())
-            return static_cast<Function*>((*foundTemplate)->TemplatedDecl);
+    auto foundTemplate = std::find_if(Templates.begin(), Templates.end(),
+                                      [&](const Template* t)
+                                      {
+                                          return t->TemplatedDecl && t->TemplatedDecl->USR == USR;
+                                      });
 
-        return nullptr;
-    }
+    if (foundTemplate != Templates.end())
+        return static_cast<Function*>((*foundTemplate)->TemplatedDecl);
 
-    TypedefDecl* DeclarationContext::FindTypedef(const std::string& Name, bool Create)
-    {
-        auto foundTypedef = std::find_if(Typedefs.begin(), Typedefs.end(),
-                                         [&](TypedefDecl* tdef)
-                                         {
-                                             return tdef->name == Name;
-                                         });
+    return nullptr;
+}
 
-        if (foundTypedef != Typedefs.end())
-            return *foundTypedef;
+TypedefDecl* DeclarationContext::FindTypedef(const std::string_view& Name, bool Create)
+{
+    auto foundTypedef = std::find_if(Typedefs.begin(), Typedefs.end(),
+                                     [&](const TypedefDecl* tdef)
+                                     {
+                                         return tdef->name == Name;
+                                     });
 
-        if (!Create)
-            return nullptr;
+    if (foundTypedef != Typedefs.end())
+        return *foundTypedef;
 
-        auto tdef = new TypedefDecl();
-        tdef->name = Name;
-        tdef->_namespace = this;
+    if (!Create)
+        return nullptr;
 
-        return tdef;
-    }
+    auto tdef = new TypedefDecl();
+    tdef->name = Name;
+    tdef->_namespace = this;
 
-    TypeAlias* DeclarationContext::FindTypeAlias(const std::string& Name, bool Create)
-    {
-        auto foundTypeAlias = std::find_if(TypeAliases.begin(), TypeAliases.end(),
-                                           [&](TypeAlias* talias)
-                                           {
-                                               return talias->name == Name;
-                                           });
+    return tdef;
+}
 
-        if (foundTypeAlias != TypeAliases.end())
-            return *foundTypeAlias;
+TypeAlias* DeclarationContext::FindTypeAlias(const std::string_view& Name, bool Create)
+{
+    auto foundTypeAlias = std::find_if(TypeAliases.begin(), TypeAliases.end(),
+                                       [&](const TypeAlias* talias)
+                                       {
+                                           return talias->name == Name;
+                                       });
 
-        if (!Create)
-            return nullptr;
+    if (foundTypeAlias != TypeAliases.end())
+        return *foundTypeAlias;
 
-        auto talias = new TypeAlias();
-        talias->name = Name;
-        talias->_namespace = this;
+    if (!Create)
+        return nullptr;
 
-        return talias;
-    }
+    auto talias = new TypeAlias();
+    talias->name = Name;
+    talias->_namespace = this;
 
-    Variable* DeclarationContext::FindVariable(const std::string& USR)
-    {
-        auto found = std::find_if(Variables.begin(), Variables.end(),
-                                  [&](Variable* var)
-                                  {
-                                      return var->USR == USR;
-                                  });
+    return talias;
+}
 
-        if (found != Variables.end())
-            return *found;
+Variable* DeclarationContext::FindVariable(const std::string_view& USR)
+{
+    auto found = std::find_if(Variables.begin(), Variables.end(),
+                              [&](const Variable* var)
+                              {
+                                  return var->USR == USR;
+                              });
 
-        return nullptr;
-    }
+    if (found != Variables.end())
+        return *found;
 
-    Friend* DeclarationContext::FindFriend(const std::string& USR)
-    {
-        auto found = std::find_if(Friends.begin(), Friends.end(),
-                                  [&](Friend* var)
-                                  {
-                                      return var->USR == USR;
-                                  });
+    return nullptr;
+}
 
-        if (found != Friends.end())
-            return *found;
+Friend* DeclarationContext::FindFriend(const std::string_view& USR)
+{
+    auto found = std::find_if(Friends.begin(), Friends.end(),
+                              [&](const Friend* var)
+                              {
+                                  return var->USR == USR;
+                              });
 
-        return nullptr;
-    }
+    if (found != Friends.end())
+        return *found;
 
-    TypedefNameDecl::TypedefNameDecl(DeclarationKind Kind)
-        : Declaration(Kind)
-    {
-    }
+    return nullptr;
+}
 
-    TypedefNameDecl::~TypedefNameDecl() {}
+TypedefNameDecl::TypedefNameDecl(DeclarationKind Kind)
+    : Declaration(Kind)
+{
+}
 
-    TypedefDecl::TypedefDecl()
-        : TypedefNameDecl(DeclarationKind::Typedef)
-    {
-    }
+TypedefNameDecl::~TypedefNameDecl() {}
 
-    TypedefDecl::~TypedefDecl() {}
+TypedefDecl::TypedefDecl()
+    : TypedefNameDecl(DeclarationKind::Typedef)
+{
+}
 
-    TypeAlias::TypeAlias()
-        : TypedefNameDecl(DeclarationKind::TypeAlias)
-        , describedAliasTemplate(0)
-    {
-    }
+TypedefDecl::~TypedefDecl() {}
 
-    TypeAlias::~TypeAlias() {}
+TypeAlias::TypeAlias()
+    : TypedefNameDecl(DeclarationKind::TypeAlias)
+    , describedAliasTemplate(nullptr)
+{
+}
 
-    Friend::Friend()
-        : CppSharp::CppParser::AST::Declaration(DeclarationKind::Friend)
-        , declaration(0)
-    {
-    }
+TypeAlias::~TypeAlias() {}
 
-    Friend::~Friend() {}
+Friend::Friend()
+    : Declaration(DeclarationKind::Friend)
+    , declaration(nullptr)
+{
+}
 
-    StatementObsolete::StatementObsolete(const std::string& str, StatementClassObsolete stmtClass, Declaration* decl)
-        : string(str)
-        , _class(stmtClass)
-        , decl(decl)
-    {
-    }
+Friend::~Friend() {}
 
-    ExpressionObsolete::ExpressionObsolete(const std::string& str, StatementClassObsolete stmtClass, Declaration* decl)
-        : StatementObsolete(str, stmtClass, decl)
-    {
-    }
+StatementObsolete::StatementObsolete(const std::string& str, StatementClassObsolete stmtClass, Declaration* decl)
+    : string(str)
+    , _class(stmtClass)
+    , decl(decl)
+{
+}
 
-    BinaryOperatorObsolete::BinaryOperatorObsolete(const std::string& str, ExpressionObsolete* lhs, ExpressionObsolete* rhs, const std::string& opcodeStr)
-        : ExpressionObsolete(str, StatementClassObsolete::BinaryOperator)
-        , LHS(lhs)
-        , RHS(rhs)
-        , opcodeStr(opcodeStr)
-    {
-    }
+ExpressionObsolete::ExpressionObsolete(const std::string& str, StatementClassObsolete stmtClass, Declaration* decl)
+    : StatementObsolete(str, stmtClass, decl)
+{
+}
 
-    BinaryOperatorObsolete::~BinaryOperatorObsolete()
-    {
-        deleteExpression(LHS);
-        deleteExpression(RHS);
-    }
+BinaryOperatorObsolete::BinaryOperatorObsolete(const std::string& str, ExpressionObsolete* lhs, ExpressionObsolete* rhs, const std::string& opcodeStr)
+    : ExpressionObsolete(str, StatementClassObsolete::BinaryOperator)
+    , LHS(lhs)
+    , RHS(rhs)
+    , opcodeStr(opcodeStr)
+{
+}
 
+BinaryOperatorObsolete::~BinaryOperatorObsolete()
+{
+    deleteExpression(LHS);
+    deleteExpression(RHS);
+}
 
-    CallExprObsolete::CallExprObsolete(const std::string& str, Declaration* decl)
-        : ExpressionObsolete(str, StatementClassObsolete::CallExprClass, decl)
-    {
-    }
 
-    CallExprObsolete::~CallExprObsolete()
-    {
-        for (auto& arg : Arguments)
-            deleteExpression(arg);
-    }
+CallExprObsolete::CallExprObsolete(const std::string& str, Declaration* decl)
+    : ExpressionObsolete(str, StatementClassObsolete::CallExprClass, decl)
+{
+}
 
-    DEF_VECTOR(CallExprObsolete, ExpressionObsolete*, Arguments)
+CallExprObsolete::~CallExprObsolete()
+{
+    for (auto& arg : Arguments)
+        deleteExpression(arg);
+}
 
-    CXXConstructExprObsolete::CXXConstructExprObsolete(const std::string& str, Declaration* decl)
-        : ExpressionObsolete(str, StatementClassObsolete::CXXConstructExprClass, decl)
-    {
-    }
+DEF_VECTOR(CallExprObsolete, ExpressionObsolete*, Arguments)
 
-    CXXConstructExprObsolete::~CXXConstructExprObsolete()
-    {
-        for (auto& arg : Arguments)
-            deleteExpression(arg);
-    }
+CXXConstructExprObsolete::CXXConstructExprObsolete(const std::string& str, Declaration* decl)
+    : ExpressionObsolete(str, StatementClassObsolete::CXXConstructExprClass, decl)
+{
+}
 
-    DEF_VECTOR(CXXConstructExprObsolete, ExpressionObsolete*, Arguments)
+CXXConstructExprObsolete::~CXXConstructExprObsolete()
+{
+    for (auto& arg : Arguments)
+        deleteExpression(arg);
+}
 
-    Parameter::Parameter()
-        : Declaration(DeclarationKind::Parameter)
-        , isIndirect(false)
-        , hasDefaultValue(false)
-        , defaultArgument(0)
-        , defaultValue(0)
-    {
-    }
+DEF_VECTOR(CXXConstructExprObsolete, ExpressionObsolete*, Arguments)
 
-    Parameter::~Parameter()
-    {
-        deleteExpression(defaultArgument);
-    }
+Parameter::Parameter()
+    : Declaration(DeclarationKind::Parameter)
+    , isIndirect(false)
+    , hasDefaultValue(false)
+    , defaultArgument(nullptr)
+    , defaultValue(nullptr)
+{
+}
 
-    Function::Function()
-        : DeclarationContext(DeclarationKind::Function)
-        , isReturnIndirect(false)
-        , isConstExpr(false)
-        , isVariadic(false)
-        , isInline(false)
-        , isPure(false)
-        , isDeleted(false)
-        , isDefaulted(false)
-        , friendKind(FriendKind::None)
-        , operatorKind(CXXOperatorKind::None)
-        , callingConvention(CallingConvention::Default)
-        , specializationInfo(0)
-        , instantiatedFrom(0)
-        , bodyStmt(0)
-    {
-    }
+Parameter::~Parameter()
+{
+    deleteExpression(defaultArgument);
+}
 
-    Function::~Function() {}
-    DEF_VECTOR(Function, Parameter*, Parameters)
-
-    Method::Method()
-        : Function()
-        , isVirtual(false)
-        , isStatic(false)
-        , isConst(false)
-        , isExplicit(false)
-        , isVolatile(false)
-        , isDefaultConstructor(false)
-        , isCopyConstructor(false)
-        , isMoveConstructor(false)
-        , refQualifier(RefQualifierKind::None)
-    {
-        kind = DeclarationKind::Method;
-    }
+Function::Function()
+    : DeclarationContext(DeclarationKind::Function)
+    , isReturnIndirect(false)
+    , isConstExpr(false)
+    , isVariadic(false)
+    , isInline(false)
+    , isPure(false)
+    , isDeleted(false)
+    , isDefaulted(false)
+    , friendKind(FriendKind::None)
+    , operatorKind(CXXOperatorKind::None)
+    , callingConvention(CallingConvention::Default)
+    , specializationInfo(nullptr)
+    , instantiatedFrom(nullptr)
+    , bodyStmt(nullptr)
+{
+}
 
-    Method::~Method() {}
+Function::~Function() {}
+DEF_VECTOR(Function, Parameter*, Parameters)
+
+Method::Method()
+    : Function()
+    , isVirtual(false)
+    , isStatic(false)
+    , isConst(false)
+    , isExplicit(false)
+    , isVolatile(false)
+    , isDefaultConstructor(false)
+    , isCopyConstructor(false)
+    , isMoveConstructor(false)
+    , refQualifier(RefQualifierKind::None)
+{
+    kind = DeclarationKind::Method;
+}
 
-    DEF_VECTOR(Method, Method*, OverriddenMethods)
+Method::~Method() {}
 
-    // Enumeration
+DEF_VECTOR(Method, Method*, OverriddenMethods)
 
-    Enumeration::Enumeration()
-        : DeclarationContext(DeclarationKind::Enumeration)
-        , modifiers((EnumModifiers)0)
-        , type(0)
-        , builtinType(0)
-    {
-    }
+// Enumeration
 
-    Enumeration::~Enumeration() {}
+Enumeration::Enumeration()
+    : DeclarationContext(DeclarationKind::Enumeration)
+    , modifiers((EnumModifiers)0)
+    , type(nullptr)
+    , builtinType(nullptr)
+{
+}
 
-    DEF_VECTOR(Enumeration, Enumeration::Item*, Items)
+Enumeration::~Enumeration() {}
 
-    Enumeration::Item::Item()
-        : Declaration(DeclarationKind::EnumerationItem)
-    {
-    }
+DEF_VECTOR(Enumeration, Enumeration::Item*, Items)
 
-    Enumeration::Item::Item(const Item& rhs)
-        : Declaration(rhs)
-        , expression(rhs.expression)
-        , value(rhs.value)
-    {
-    }
+Enumeration::Item::Item()
+    : Declaration(DeclarationKind::EnumerationItem)
+{
+}
 
-    Enumeration::Item::~Item() {}
+Enumeration::Item::Item(const Item& rhs)
+    : Declaration(rhs)
+    , expression(rhs.expression)
+    , value(rhs.value)
+{
+}
 
-    Enumeration::Item* Enumeration::FindItemByName(const std::string& Name)
-    {
-        auto foundEnumItem = std::find_if(Items.begin(), Items.end(),
-                                          [&](Item* _item)
-                                          {
-                                              return _item->name == Name;
-                                          });
-        if (foundEnumItem != Items.end())
-            return *foundEnumItem;
-        return nullptr;
-    }
+Enumeration::Item::~Item() {}
 
-    Variable::Variable()
-        : Declaration(DeclarationKind::Variable)
-        , isConstExpr(false)
-        , initializer(0)
-    {
-    }
+Enumeration::Item* Enumeration::FindItemByName(const std::string_view& Name)
+{
+    auto foundEnumItem = std::find_if(Items.begin(), Items.end(),
+                                      [&](const Item* _item)
+                                      {
+                                          return _item->name == Name;
+                                      });
 
-    Variable::~Variable() {}
+    if (foundEnumItem != Items.end())
+        return *foundEnumItem;
+    return nullptr;
+}
 
-    BaseClassSpecifier::BaseClassSpecifier()
-        : type(0)
-        , offset(0)
-    {
-    }
+Variable::Variable()
+    : Declaration(DeclarationKind::Variable)
+    , isConstExpr(false)
+    , initializer(nullptr)
+{
+}
 
-    Field::Field()
-        : Declaration(DeclarationKind::Field)
-        , _class(0)
-        , isBitField(false)
-        , bitWidth(0)
-    {
-    }
+Variable::~Variable() {}
 
-    Field::~Field() {}
+BaseClassSpecifier::BaseClassSpecifier()
+    : type(nullptr)
+    , offset(0)
+{
+}
 
-    AccessSpecifierDecl::AccessSpecifierDecl()
-        : Declaration(DeclarationKind::AccessSpecifier)
-    {
-    }
+Field::Field()
+    : Declaration(DeclarationKind::Field)
+    , _class(nullptr)
+    , isBitField(false)
+    , bitWidth(0)
+{
+}
 
-    AccessSpecifierDecl::~AccessSpecifierDecl() {}
-
-    Class::Class()
-        : DeclarationContext(DeclarationKind::Class)
-        , isPOD(false)
-        , isAbstract(false)
-        , isUnion(false)
-        , isDynamic(false)
-        , isPolymorphic(false)
-        , hasNonTrivialDefaultConstructor(false)
-        , hasNonTrivialCopyConstructor(false)
-        , hasNonTrivialDestructor(false)
-        , isExternCContext(false)
-        , isInjected(false)
-        , layout(0)
-        , tagKind(TagKind::Struct)
-    {
-    }
+Field::~Field() {}
 
-    Class::~Class()
-    {
-        if (layout)
-            delete layout;
-    }
+AccessSpecifierDecl::AccessSpecifierDecl()
+    : Declaration(DeclarationKind::AccessSpecifier)
+{
+}
 
-    DEF_VECTOR(Class, BaseClassSpecifier*, Bases)
-    DEF_VECTOR(Class, Field*, Fields)
-    DEF_VECTOR(Class, Method*, Methods)
-    DEF_VECTOR(Class, AccessSpecifierDecl*, Specifiers)
+AccessSpecifierDecl::~AccessSpecifierDecl() {}
+
+Class::Class()
+    : DeclarationContext(DeclarationKind::Class)
+    , isPOD(false)
+    , isAbstract(false)
+    , isUnion(false)
+    , isDynamic(false)
+    , isPolymorphic(false)
+    , hasNonTrivialDefaultConstructor(false)
+    , hasNonTrivialCopyConstructor(false)
+    , hasNonTrivialDestructor(false)
+    , isExternCContext(false)
+    , isInjected(false)
+    , layout(nullptr)
+    , tagKind(TagKind::Struct)
+{
+}
 
-    Template::Template()
-        : Declaration(DeclarationKind::Template)
-        , TemplatedDecl(0)
-    {
-    }
+Class::~Class()
+{
+    if (layout)
+        delete layout;
+}
 
-    Template::Template(DeclarationKind kind)
-        : Declaration(kind)
-        , TemplatedDecl(0)
-    {
-    }
+DEF_VECTOR(Class, BaseClassSpecifier*, Bases)
+DEF_VECTOR(Class, Field*, Fields)
+DEF_VECTOR(Class, Method*, Methods)
+DEF_VECTOR(Class, AccessSpecifierDecl*, Specifiers)
 
-    DEF_VECTOR(Template, Declaration*, Parameters)
+Template::Template()
+    : Declaration(DeclarationKind::Template)
+    , TemplatedDecl(nullptr)
+{
+}
 
-    TypeAliasTemplate::TypeAliasTemplate()
-        : Template(DeclarationKind::TypeAliasTemplate)
-    {
-    }
+Template::Template(DeclarationKind kind)
+    : Declaration(kind)
+    , TemplatedDecl(nullptr)
+{
+}
 
-    TypeAliasTemplate::~TypeAliasTemplate() {}
+DEF_VECTOR(Template, Declaration*, Parameters)
 
-    ClassTemplate::ClassTemplate()
-        : Template(DeclarationKind::ClassTemplate)
-    {
-    }
+TypeAliasTemplate::TypeAliasTemplate()
+    : Template(DeclarationKind::TypeAliasTemplate)
+{
+}
 
-    ClassTemplate::~ClassTemplate() {}
+TypeAliasTemplate::~TypeAliasTemplate() {}
 
-    DEF_VECTOR(ClassTemplate, ClassTemplateSpecialization*, Specializations)
+ClassTemplate::ClassTemplate()
+    : Template(DeclarationKind::ClassTemplate)
+{
+}
 
-    ClassTemplateSpecialization::ClassTemplateSpecialization()
-        : Class()
-        , templatedDecl(0)
-    {
-        kind = DeclarationKind::ClassTemplateSpecialization;
-    }
+ClassTemplate::~ClassTemplate() {}
 
-    ClassTemplateSpecialization::~ClassTemplateSpecialization() {}
+DEF_VECTOR(ClassTemplate, ClassTemplateSpecialization*, Specializations)
 
-    DEF_VECTOR(ClassTemplateSpecialization, TemplateArgument, Arguments)
+ClassTemplateSpecialization::ClassTemplateSpecialization()
+    : Class()
+    , templatedDecl(nullptr)
+{
+    kind = DeclarationKind::ClassTemplateSpecialization;
+}
 
-    ClassTemplatePartialSpecialization::ClassTemplatePartialSpecialization()
-        : ClassTemplateSpecialization()
-    {
-        kind = DeclarationKind::ClassTemplatePartialSpecialization;
-    }
+ClassTemplateSpecialization::~ClassTemplateSpecialization() {}
 
-    ClassTemplatePartialSpecialization::~ClassTemplatePartialSpecialization() {}
+DEF_VECTOR(ClassTemplateSpecialization, TemplateArgument, Arguments)
 
-    DEF_VECTOR(ClassTemplatePartialSpecialization, Declaration*, Parameters)
+ClassTemplatePartialSpecialization::ClassTemplatePartialSpecialization()
+    : ClassTemplateSpecialization()
+{
+    kind = DeclarationKind::ClassTemplatePartialSpecialization;
+}
 
-    FunctionTemplate::FunctionTemplate()
-        : Template(DeclarationKind::FunctionTemplate)
-    {
-    }
+ClassTemplatePartialSpecialization::~ClassTemplatePartialSpecialization() {}
 
-    FunctionTemplate::~FunctionTemplate() {}
+DEF_VECTOR(ClassTemplatePartialSpecialization, Declaration*, Parameters)
 
-    DEF_VECTOR(FunctionTemplate, FunctionTemplateSpecialization*, Specializations)
+FunctionTemplate::FunctionTemplate()
+    : Template(DeclarationKind::FunctionTemplate)
+{
+}
 
-    FunctionTemplateSpecialization* FunctionTemplate::FindSpecialization(const std::string& usr)
-    {
-        auto foundSpec = std::find_if(Specializations.begin(), Specializations.end(),
-                                      [&](FunctionTemplateSpecialization* cts)
-                                      {
-                                          return cts->specializedFunction->USR == usr;
-                                      });
+FunctionTemplate::~FunctionTemplate() {}
 
-        if (foundSpec != Specializations.end())
-            return static_cast<FunctionTemplateSpecialization*>(*foundSpec);
+DEF_VECTOR(FunctionTemplate, FunctionTemplateSpecialization*, Specializations)
 
-        return nullptr;
-    }
+FunctionTemplateSpecialization* FunctionTemplate::FindSpecialization(const std::string& usr)
+{
+    auto foundSpec = std::find_if(Specializations.begin(), Specializations.end(),
+                                  [&](FunctionTemplateSpecialization* cts)
+                                  {
+                                      return cts->specializedFunction->USR == usr;
+                                  });
 
-    FunctionTemplateSpecialization::FunctionTemplateSpecialization()
-        : _template(0)
-        , specializedFunction(0)
-    {
-    }
+    if (foundSpec != Specializations.end())
+        return static_cast<FunctionTemplateSpecialization*>(*foundSpec);
 
-    FunctionTemplateSpecialization::~FunctionTemplateSpecialization()
-    {
-    }
+    return nullptr;
+}
 
-    DEF_VECTOR(FunctionTemplateSpecialization, TemplateArgument, Arguments)
+FunctionTemplateSpecialization::FunctionTemplateSpecialization()
+    : _template(nullptr)
+    , specializedFunction(nullptr)
+{
+}
 
-    VarTemplate::VarTemplate()
-        : Template(DeclarationKind::VarTemplate)
-    {
-    }
+FunctionTemplateSpecialization::~FunctionTemplateSpecialization()
+{
+}
 
-    VarTemplate::~VarTemplate() {}
+DEF_VECTOR(FunctionTemplateSpecialization, TemplateArgument, Arguments)
 
-    DEF_VECTOR(VarTemplate, VarTemplateSpecialization*, Specializations)
+VarTemplate::VarTemplate()
+    : Template(DeclarationKind::VarTemplate)
+{
+}
 
-    VarTemplateSpecialization* VarTemplate::FindSpecialization(const std::string& usr)
-    {
-        auto foundSpec = std::find_if(Specializations.begin(), Specializations.end(),
-                                      [&](VarTemplateSpecialization* cts)
-                                      {
-                                          return cts->USR == usr;
-                                      });
+VarTemplate::~VarTemplate() {}
 
-        if (foundSpec != Specializations.end())
-            return static_cast<VarTemplateSpecialization*>(*foundSpec);
+DEF_VECTOR(VarTemplate, VarTemplateSpecialization*, Specializations)
 
-        return nullptr;
-    }
+VarTemplateSpecialization* VarTemplate::FindSpecialization(const std::string& usr)
+{
+    auto foundSpec = std::find_if(Specializations.begin(), Specializations.end(),
+                                  [&](VarTemplateSpecialization* cts)
+                                  {
+                                      return cts->USR == usr;
+                                  });
 
-    VarTemplatePartialSpecialization* VarTemplate::FindPartialSpecialization(const std::string& usr)
-    {
-        auto foundSpec = FindSpecialization(usr);
-        if (foundSpec != nullptr)
-            return static_cast<VarTemplatePartialSpecialization*>(foundSpec);
-        return nullptr;
-    }
+    if (foundSpec != Specializations.end())
+        return static_cast<VarTemplateSpecialization*>(*foundSpec);
 
-    VarTemplateSpecialization::VarTemplateSpecialization()
-        : Variable()
-        , templatedDecl(0)
-    {
-        kind = DeclarationKind::VarTemplateSpecialization;
-    }
+    return nullptr;
+}
 
-    VarTemplateSpecialization::~VarTemplateSpecialization() {}
+VarTemplatePartialSpecialization* VarTemplate::FindPartialSpecialization(const std::string& usr)
+{
+    auto foundSpec = FindSpecialization(usr);
+    if (foundSpec != nullptr)
+        return static_cast<VarTemplatePartialSpecialization*>(foundSpec);
+    return nullptr;
+}
 
-    DEF_VECTOR(VarTemplateSpecialization, TemplateArgument, Arguments)
+VarTemplateSpecialization::VarTemplateSpecialization()
+    : Variable()
+    , templatedDecl(nullptr)
+{
+    kind = DeclarationKind::VarTemplateSpecialization;
+}
 
-    VarTemplatePartialSpecialization::VarTemplatePartialSpecialization()
-        : VarTemplateSpecialization()
-    {
-        kind = DeclarationKind::VarTemplatePartialSpecialization;
-    }
+VarTemplateSpecialization::~VarTemplateSpecialization() {}
 
-    VarTemplatePartialSpecialization::~VarTemplatePartialSpecialization()
-    {
-    }
+DEF_VECTOR(VarTemplateSpecialization, TemplateArgument, Arguments)
 
-    UnresolvedUsingTypename::UnresolvedUsingTypename()
-        : Declaration(DeclarationKind::UnresolvedUsingTypename)
-    {
-    }
+VarTemplatePartialSpecialization::VarTemplatePartialSpecialization()
+    : VarTemplateSpecialization()
+{
+    kind = DeclarationKind::VarTemplatePartialSpecialization;
+}
 
-    UnresolvedUsingTypename::~UnresolvedUsingTypename() {}
+VarTemplatePartialSpecialization::~VarTemplatePartialSpecialization()
+{
+}
 
-    Namespace::Namespace()
-        : DeclarationContext(DeclarationKind::Namespace)
-        , isInline(false)
-    {
-    }
+UnresolvedUsingTypename::UnresolvedUsingTypename()
+    : Declaration(DeclarationKind::UnresolvedUsingTypename)
+{
+}
 
-    Namespace::~Namespace() {}
+UnresolvedUsingTypename::~UnresolvedUsingTypename() {}
 
-    PreprocessedEntity::PreprocessedEntity()
-        : macroLocation(AST::MacroLocation::Unknown)
-        , originalPtr(0)
-        , kind(DeclarationKind::PreprocessedEntity)
-    {
-    }
+Namespace::Namespace()
+    : DeclarationContext(DeclarationKind::Namespace)
+    , isInline(false)
+{
+}
 
-    MacroDefinition::MacroDefinition()
-        : lineNumberStart(0)
-        , lineNumberEnd(0)
-    {
-        kind = DeclarationKind::MacroDefinition;
-    }
+Namespace::~Namespace() {}
 
-    MacroDefinition::~MacroDefinition() {}
+PreprocessedEntity::PreprocessedEntity()
+    : macroLocation(MacroLocation::Unknown)
+    , originalPtr(nullptr)
+    , kind(DeclarationKind::PreprocessedEntity)
+{
+}
 
-    MacroExpansion::MacroExpansion()
-        : definition(0)
-    {
-        kind = DeclarationKind::MacroExpansion;
-    }
+MacroDefinition::MacroDefinition()
+    : lineNumberStart(0)
+    , lineNumberEnd(0)
+{
+    kind = DeclarationKind::MacroDefinition;
+}
 
-    MacroExpansion::~MacroExpansion() {}
+MacroDefinition::~MacroDefinition() {}
 
-    TranslationUnit::TranslationUnit()
-    {
-        kind = DeclarationKind::TranslationUnit;
-    }
+MacroExpansion::MacroExpansion()
+    : definition(nullptr)
+{
+    kind = DeclarationKind::MacroExpansion;
+}
 
-    TranslationUnit::~TranslationUnit() {}
-    DEF_VECTOR(TranslationUnit, MacroDefinition*, Macros)
+MacroExpansion::~MacroExpansion() {}
 
-    NativeLibrary::NativeLibrary()
-        : archType(AST::ArchType::UnknownArch)
-    {
-    }
+TranslationUnit::TranslationUnit()
+{
+    kind = DeclarationKind::TranslationUnit;
+}
 
-    NativeLibrary::~NativeLibrary() {}
+TranslationUnit::~TranslationUnit() {}
+DEF_VECTOR(TranslationUnit, MacroDefinition*, Macros)
 
-    // NativeLibrary
-    DEF_VECTOR_STRING(NativeLibrary, Symbols)
-    DEF_VECTOR_STRING(NativeLibrary, Dependencies)
+NativeLibrary::NativeLibrary()
+    : archType(ArchType::UnknownArch)
+{
+}
 
-    // ASTContext
-    DEF_VECTOR(ASTContext, TranslationUnit*, TranslationUnits)
+NativeLibrary::~NativeLibrary() {}
 
-    ClassTemplateSpecialization* ClassTemplate::FindSpecialization(const std::string& usr)
-    {
-        auto foundSpec = std::find_if(Specializations.begin(), Specializations.end(),
-                                      [&](ClassTemplateSpecialization* cts)
-                                      {
-                                          return cts->USR == usr;
-                                      });
+// NativeLibrary
+DEF_VECTOR_STRING(NativeLibrary, Symbols)
+DEF_VECTOR_STRING(NativeLibrary, Dependencies)
 
-        if (foundSpec != Specializations.end())
-            return static_cast<ClassTemplateSpecialization*>(*foundSpec);
+// ASTContext
+DEF_VECTOR(ASTContext, TranslationUnit*, TranslationUnits)
 
-        return nullptr;
-    }
+ClassTemplateSpecialization* ClassTemplate::FindSpecialization(const std::string& usr)
+{
+    auto foundSpec = std::find_if(Specializations.begin(), Specializations.end(),
+                                  [&](ClassTemplateSpecialization* cts)
+                                  {
+                                      return cts->USR == usr;
+                                  });
 
-    ClassTemplatePartialSpecialization* ClassTemplate::FindPartialSpecialization(const std::string& usr)
-    {
-        auto foundSpec = FindSpecialization(usr);
-        if (foundSpec != nullptr)
-            return static_cast<ClassTemplatePartialSpecialization*>(foundSpec);
-        return nullptr;
-    }
+    if (foundSpec != Specializations.end())
+        return static_cast<ClassTemplateSpecialization*>(*foundSpec);
 
-    ASTContext::ASTContext() {}
+    return nullptr;
+}
 
-    ASTContext::~ASTContext() {}
+ClassTemplatePartialSpecialization* ClassTemplate::FindPartialSpecialization(const std::string& usr)
+{
+    auto foundSpec = FindSpecialization(usr);
+    if (foundSpec != nullptr)
+        return static_cast<ClassTemplatePartialSpecialization*>(foundSpec);
+    return nullptr;
+}
 
-    TranslationUnit* ASTContext::FindOrCreateModule(const std::string& File)
-    {
-        auto normalizedFile = normalizePath(File);
+ASTContext::ASTContext() {}
 
-        auto existingUnit = std::find_if(TranslationUnits.begin(),
-                                         TranslationUnits.end(), [&](TranslationUnit* unit)
-                                         {
-                                             return unit && unit->fileName == normalizedFile;
-                                         });
+ASTContext::~ASTContext() {}
 
-        if (existingUnit != TranslationUnits.end())
-            return *existingUnit;
+TranslationUnit* ASTContext::FindOrCreateModule(const std::string& File)
+{
+    auto normalizedFile = normalizePath(File);
 
-        auto unit = new TranslationUnit();
-        unit->fileName = normalizedFile;
-        TranslationUnits.push_back(unit);
+    auto existingUnit = std::find_if(TranslationUnits.begin(),
+                                     TranslationUnits.end(), [&](TranslationUnit* unit)
+                                     {
+                                         return unit && unit->fileName == normalizedFile;
+                                     });
 
-        return unit;
-    }
+    if (existingUnit != TranslationUnits.end())
+        return *existingUnit;
 
-    // Comments
-    Comment::Comment(CommentKind kind)
-        : kind(kind)
-    {
-    }
+    auto unit = new TranslationUnit();
+    unit->fileName = normalizedFile;
+    TranslationUnits.push_back(unit);
 
-    RawComment::RawComment()
-        : fullCommentBlock(0)
-    {
-    }
+    return unit;
+}
 
-    RawComment::~RawComment()
-    {
-        if (fullCommentBlock)
-            delete fullCommentBlock;
-    }
+// Comments
+Comment::Comment(CommentKind kind)
+    : kind(kind)
+{
+}
 
-    FullComment::FullComment()
-        : Comment(CommentKind::FullComment)
-    {
-    }
+RawComment::RawComment()
+    : fullCommentBlock(nullptr)
+{
+}
 
-    FullComment::~FullComment()
+RawComment::~RawComment()
+{
+    if (fullCommentBlock)
+        delete fullCommentBlock;
+}
+
+FullComment::FullComment()
+    : Comment(CommentKind::FullComment)
+{
+}
+
+FullComment::~FullComment()
+{
+    for (auto& block : Blocks)
     {
-        for (auto& block : Blocks)
+        // HACK: see https://github.com/mono/CppSharp/issues/599
+        switch (block->kind)
         {
-            // HACK: see https://github.com/mono/CppSharp/issues/599
-            switch (block->kind)
-            {
-                case CommentKind::BlockCommandComment:
-                    delete static_cast<BlockCommandComment*>(block);
-                    break;
-                case CommentKind::ParamCommandComment:
-                    delete static_cast<ParamCommandComment*>(block);
-                    break;
-                case CommentKind::TParamCommandComment:
-                    delete static_cast<TParamCommandComment*>(block);
-                    break;
-                case CommentKind::VerbatimBlockComment:
-                    delete static_cast<VerbatimBlockComment*>(block);
-                    break;
-                case CommentKind::VerbatimLineComment:
-                    delete static_cast<VerbatimLineComment*>(block);
-                    break;
-                case CommentKind::ParagraphComment:
-                    delete static_cast<ParagraphComment*>(block);
-                    break;
-                default:
-                    delete block;
-                    break;
-            }
+            case CommentKind::BlockCommandComment:
+                delete static_cast<BlockCommandComment*>(block);
+                break;
+            case CommentKind::ParamCommandComment:
+                delete static_cast<ParamCommandComment*>(block);
+                break;
+            case CommentKind::TParamCommandComment:
+                delete static_cast<TParamCommandComment*>(block);
+                break;
+            case CommentKind::VerbatimBlockComment:
+                delete static_cast<VerbatimBlockComment*>(block);
+                break;
+            case CommentKind::VerbatimLineComment:
+                delete static_cast<VerbatimLineComment*>(block);
+                break;
+            case CommentKind::ParagraphComment:
+                delete static_cast<ParagraphComment*>(block);
+                break;
+            default:
+                delete block;
+                break;
         }
     }
+}
 
-    DEF_VECTOR(FullComment, BlockContentComment*, Blocks)
+DEF_VECTOR(FullComment, BlockContentComment*, Blocks)
 
-    BlockContentComment::BlockContentComment()
-        : Comment(CommentKind::BlockContentComment)
-    {
-    }
+BlockContentComment::BlockContentComment()
+    : Comment(CommentKind::BlockContentComment)
+{
+}
 
-    BlockContentComment::BlockContentComment(CommentKind Kind)
-        : Comment(Kind)
-    {
-    }
+BlockContentComment::BlockContentComment(CommentKind Kind)
+    : Comment(Kind)
+{
+}
 
-    BlockCommandComment::Argument::Argument() {}
+BlockCommandComment::Argument::Argument() {}
 
-    BlockCommandComment::Argument::Argument(const Argument& rhs)
-        : text(rhs.text)
-    {
-    }
+BlockCommandComment::Argument::Argument(const Argument& rhs)
+    : text(rhs.text)
+{
+}
 
-    BlockCommandComment::Argument::~Argument() {}
+BlockCommandComment::Argument::~Argument() {}
 
-    BlockCommandComment::BlockCommandComment()
-        : BlockContentComment(CommentKind::BlockCommandComment)
-        , commandId(0)
-        , paragraphComment(0)
-    {
-    }
+BlockCommandComment::BlockCommandComment()
+    : BlockContentComment(CommentKind::BlockCommandComment)
+    , commandId(0)
+    , paragraphComment(nullptr)
+{
+}
 
-    BlockCommandComment::BlockCommandComment(CommentKind Kind)
-        : BlockContentComment(Kind)
-        , commandId(0)
-        , paragraphComment(0)
-    {
-    }
+BlockCommandComment::BlockCommandComment(CommentKind Kind)
+    : BlockContentComment(Kind)
+    , commandId(0)
+    , paragraphComment(nullptr)
+{
+}
 
-    BlockCommandComment::~BlockCommandComment()
-    {
-        delete paragraphComment;
-    }
+BlockCommandComment::~BlockCommandComment()
+{
+    delete paragraphComment;
+}
 
-    DEF_VECTOR(BlockCommandComment, BlockCommandComment::Argument, Arguments)
+DEF_VECTOR(BlockCommandComment, BlockCommandComment::Argument, Arguments)
 
-    ParamCommandComment::ParamCommandComment()
-        : BlockCommandComment(CommentKind::ParamCommandComment)
-        , direction(PassDirection::In)
-        , paramIndex(0)
-    {
-    }
+ParamCommandComment::ParamCommandComment()
+    : BlockCommandComment(CommentKind::ParamCommandComment)
+    , direction(In)
+    , paramIndex(0)
+{
+}
 
-    TParamCommandComment::TParamCommandComment()
-        : BlockCommandComment(CommentKind::TParamCommandComment)
-    {
-    }
+TParamCommandComment::TParamCommandComment()
+    : BlockCommandComment(CommentKind::TParamCommandComment)
+{
+}
 
-    DEF_VECTOR(TParamCommandComment, unsigned, Position)
+DEF_VECTOR(TParamCommandComment, unsigned, Position)
 
-    VerbatimBlockComment::VerbatimBlockComment()
-        : BlockCommandComment(CommentKind::VerbatimBlockComment)
-    {
-    }
+VerbatimBlockComment::VerbatimBlockComment()
+    : BlockCommandComment(CommentKind::VerbatimBlockComment)
+{
+}
 
-    VerbatimBlockComment::~VerbatimBlockComment()
-    {
-        for (auto& line : Lines)
-            delete line;
-    }
+VerbatimBlockComment::~VerbatimBlockComment()
+{
+    for (auto& line : Lines)
+        delete line;
+}
 
-    DEF_VECTOR(VerbatimBlockComment, VerbatimBlockLineComment*, Lines)
+DEF_VECTOR(VerbatimBlockComment, VerbatimBlockLineComment*, Lines)
 
-    VerbatimLineComment::VerbatimLineComment()
-        : BlockCommandComment(CommentKind::VerbatimLineComment)
-    {
-    }
+VerbatimLineComment::VerbatimLineComment()
+    : BlockCommandComment(CommentKind::VerbatimLineComment)
+{
+}
 
-    ParagraphComment::ParagraphComment()
-        : BlockContentComment(CommentKind::ParagraphComment)
-        , isWhitespace(false)
-    {
-    }
+ParagraphComment::ParagraphComment()
+    : BlockContentComment(CommentKind::ParagraphComment)
+    , isWhitespace(false)
+{
+}
 
-    ParagraphComment::~ParagraphComment()
+ParagraphComment::~ParagraphComment()
+{
+    for (auto& content : Content)
     {
-        for (auto& content : Content)
+        // HACK: see https://github.com/mono/CppSharp/issues/599
+        switch (content->kind)
         {
-            // HACK: see https://github.com/mono/CppSharp/issues/599
-            switch (content->kind)
-            {
-                case CommentKind::InlineCommandComment:
-                    delete static_cast<InlineCommandComment*>(content);
-                    break;
-                case CommentKind::HTMLTagComment:
-                    delete static_cast<HTMLTagComment*>(content);
-                    break;
-                case CommentKind::HTMLStartTagComment:
-                    delete static_cast<HTMLStartTagComment*>(content);
-                    break;
-                case CommentKind::HTMLEndTagComment:
-                    delete static_cast<HTMLEndTagComment*>(content);
-                    break;
-                case CommentKind::TextComment:
-                    delete static_cast<TextComment*>(content);
-                    break;
-                default:
-                    delete content;
-                    break;
-            }
+            case CommentKind::InlineCommandComment:
+                delete static_cast<InlineCommandComment*>(content);
+                break;
+            case CommentKind::HTMLTagComment:
+                delete static_cast<HTMLTagComment*>(content);
+                break;
+            case CommentKind::HTMLStartTagComment:
+                delete static_cast<HTMLStartTagComment*>(content);
+                break;
+            case CommentKind::HTMLEndTagComment:
+                delete static_cast<HTMLEndTagComment*>(content);
+                break;
+            case CommentKind::TextComment:
+                delete static_cast<TextComment*>(content);
+                break;
+            default:
+                delete content;
+                break;
         }
     }
+}
 
-    DEF_VECTOR(ParagraphComment, InlineContentComment*, Content)
+DEF_VECTOR(ParagraphComment, InlineContentComment*, Content)
 
-    HTMLTagComment::HTMLTagComment()
-        : InlineContentComment(CommentKind::HTMLTagComment)
-    {
-    }
+HTMLTagComment::HTMLTagComment()
+    : InlineContentComment(CommentKind::HTMLTagComment)
+{
+}
 
-    HTMLTagComment::HTMLTagComment(CommentKind Kind)
-        : InlineContentComment(Kind)
-    {
-    }
+HTMLTagComment::HTMLTagComment(CommentKind Kind)
+    : InlineContentComment(Kind)
+{
+}
 
-    HTMLStartTagComment::Attribute::Attribute() {}
+HTMLStartTagComment::Attribute::Attribute() {}
 
-    HTMLStartTagComment::Attribute::Attribute(const Attribute& rhs)
-        : name(rhs.name)
-        , value(rhs.value)
-    {
-    }
+HTMLStartTagComment::Attribute::Attribute(const Attribute& rhs)
+    : name(rhs.name)
+    , value(rhs.value)
+{
+}
 
-    HTMLStartTagComment::Attribute::~Attribute() {}
+HTMLStartTagComment::Attribute::~Attribute() {}
 
-    HTMLStartTagComment::HTMLStartTagComment()
-        : HTMLTagComment(CommentKind::HTMLStartTagComment)
-    {
-    }
+HTMLStartTagComment::HTMLStartTagComment()
+    : HTMLTagComment(CommentKind::HTMLStartTagComment)
+{
+}
 
-    DEF_VECTOR(HTMLStartTagComment, HTMLStartTagComment::Attribute, Attributes)
+DEF_VECTOR(HTMLStartTagComment, HTMLStartTagComment::Attribute, Attributes)
 
-    HTMLEndTagComment::HTMLEndTagComment()
-        : HTMLTagComment(CommentKind::HTMLEndTagComment)
-    {
-    }
+HTMLEndTagComment::HTMLEndTagComment()
+    : HTMLTagComment(CommentKind::HTMLEndTagComment)
+{
+}
 
-    InlineContentComment::InlineContentComment()
-        : Comment(CommentKind::InlineContentComment)
-        , hasTrailingNewline(false)
-    {
-    }
+InlineContentComment::InlineContentComment()
+    : Comment(CommentKind::InlineContentComment)
+    , hasTrailingNewline(false)
+{
+}
 
-    InlineContentComment::InlineContentComment(CommentKind Kind)
-        : Comment(Kind)
-        , hasTrailingNewline(false)
-    {
-    }
+InlineContentComment::InlineContentComment(CommentKind Kind)
+    : Comment(Kind)
+    , hasTrailingNewline(false)
+{
+}
 
-    TextComment::TextComment()
-        : InlineContentComment(CommentKind::TextComment)
-    {
-    }
+TextComment::TextComment()
+    : InlineContentComment(CommentKind::TextComment)
+{
+}
 
-    InlineCommandComment::Argument::Argument() {}
+InlineCommandComment::Argument::Argument() {}
 
-    InlineCommandComment::Argument::Argument(const Argument& rhs)
-        : text(rhs.text)
-    {
-    }
+InlineCommandComment::Argument::Argument(const Argument& rhs)
+    : text(rhs.text)
+{
+}
 
-    InlineCommandComment::Argument::~Argument() {}
+InlineCommandComment::Argument::~Argument() {}
 
-    InlineCommandComment::InlineCommandComment()
-        : InlineContentComment(CommentKind::InlineCommandComment)
-        , commandId(0)
-        , commentRenderKind(RenderNormal)
-    {
-    }
+InlineCommandComment::InlineCommandComment()
+    : InlineContentComment(CommentKind::InlineCommandComment)
+    , commandId(0)
+    , commentRenderKind(RenderNormal)
+{
+}
 
-    DEF_VECTOR(InlineCommandComment, InlineCommandComment::Argument, Arguments)
+DEF_VECTOR(InlineCommandComment, InlineCommandComment::Argument, Arguments)
 
-    VerbatimBlockLineComment::VerbatimBlockLineComment()
-        : Comment(CommentKind::VerbatimBlockLineComment)
-    {
-    }
+VerbatimBlockLineComment::VerbatimBlockLineComment()
+    : Comment(CommentKind::VerbatimBlockLineComment)
+{
+}
 
-}}} // namespace CppSharp::CppParser::AST
\ No newline at end of file
+} // namespace CppSharp::CppParser::AST
\ No newline at end of file
diff --git a/src/CppParser/ASTNodeVisitor.cpp b/src/CppParser/ASTNodeVisitor.cpp
new file mode 100644
index 00000000..18ffd0f9
--- /dev/null
+++ b/src/CppParser/ASTNodeVisitor.cpp
@@ -0,0 +1,2352 @@
+/************************************************************************
+ *
+ * CppSharp
+ * Licensed under the simplified BSD license. All rights reserved.
+ *
+ ************************************************************************/
+
+#include "ASTNodeVisitor.h"
+
+#include <clang/AST/Type.h>
+#include <clang/Basic/SourceManager.h>
+#include <clang/Basic/Specifiers.h>
+#include <clang/Index/USRGeneration.h>
+#include <clang/Lex/Lexer.h>
+
+#include <llvm/ADT/StringExtras.h>
+
+#include <optional>
+
+#include "Types.h"
+#include "Decl.h"
+#include "Parser.h"
+#include "Sources.h"
+
+using namespace CppSharp::CppParser;
+using namespace clang;
+
+namespace {
+AST::AccessSpecifier ConvertToAccess(clang::AccessSpecifier AS)
+{
+    switch (AS)
+    {
+        case clang::AS_private:
+            return AST::AccessSpecifier::Private;
+        case clang::AS_protected:
+            return AST::AccessSpecifier::Protected;
+        case clang::AS_public:
+            return AST::AccessSpecifier::Public;
+        case clang::AS_none:
+            return AST::AccessSpecifier::Public;
+    }
+
+    llvm_unreachable("Unknown AccessSpecifier");
+}
+
+std::string GetDeclUSR(const clang::Decl* D)
+{
+    using namespace clang;
+    SmallString<128> usr;
+    if (!index::generateUSRForDecl(D, usr))
+        return usr.c_str();
+    return "<invalid>";
+}
+
+std::string GetDeclName(const clang::NamedDecl* D)
+{
+    if (const clang::IdentifierInfo* II = D->getIdentifier())
+        return II->getName().str();
+    return D->getDeclName().getAsString();
+}
+
+AST::TypeQualifiers GetTypeQualifiers(const clang::Qualifiers& quals)
+{
+    AST::TypeQualifiers ret;
+    ret.isConst = quals.hasConst();
+    ret.isRestrict = quals.hasRestrict();
+    ret.isVolatile = quals.hasVolatile();
+    return ret;
+}
+
+CppSharp::CppParser::SourceLocation ConvertSourceLocation(const clang::SourceLocation& loc)
+{
+    // TODO: Incomplete. Should this include additional data or is it possible to lazy load that?
+    return { loc.getRawEncoding() };
+}
+
+CppSharp::CppParser::SourceRange ConvertSourceRange(const clang::SourceRange& range)
+{
+    return {
+        ConvertSourceLocation(range.getBegin()),
+        ConvertSourceLocation(range.getEnd())
+    };
+}
+
+} // namespace
+
+void ASTNodeVisitor::ConvertNamedRecord(AST::Declaration& dst, const clang::NamedDecl& src) const
+{
+    if (!src.getDeclName())
+        return;
+
+    dst.name = src.getNameAsString();
+    dst.mangledName = GetMangledName(src);
+}
+
+std::string ASTNodeVisitor::GetMangledName(const clang::NamedDecl& ND) const
+{
+    // Source adapted from https://clang.llvm.org/doxygen/JSONNodeDumper_8cpp_source.html#l00845
+
+    // If the declaration is dependent or is in a dependent context, then the
+    // mangling is unlikely to be meaningful (and in some cases may cause
+    // "don't know how to mangle this" assertion failures.)
+    if (ND.isTemplated())
+        return {};
+
+    // FIXME: There are likely other contexts in which it makes no sense to ask
+    // for a mangled name.
+    if (isa<RequiresExprBodyDecl>(ND.getDeclContext()))
+        return {};
+
+    // Do not mangle template deduction guides.
+    if (isa<CXXDeductionGuideDecl>(ND))
+        return {};
+
+    // Mangled names are not meaningful for locals, and may not be well-defined
+    // in the case of VLAs.
+    auto* VD = dyn_cast<VarDecl>(&ND);
+    if (VD && VD->hasLocalStorage())
+        return {};
+
+    return NameMangler.GetName(&ND);
+}
+
+void ASTNodeVisitor::addPreviousDeclaration(const Decl* D)
+{
+    switch (D->getKind())
+    {
+#define DECL(DERIVED, BASE) \
+    case Decl::DERIVED:     \
+        return writePreviousDeclImpl(cast<DERIVED##Decl>(D));
+#define ABSTRACT_DECL(DECL)
+#include "clang/AST/DeclNodes.inc"
+
+#undef ABSTRACT_DECL
+#undef DECL
+    }
+    llvm_unreachable("Decl that isn't part of DeclNodes.inc!");
+}
+
+void ASTNodeVisitor::Visit(const Attr* A)
+{
+    const char* AttrName = nullptr;
+    switch (A->getKind())
+    {
+#define ATTR(X)               \
+    case attr::X:             \
+        AttrName = #X "Attr"; \
+        break;
+#include "clang/Basic/AttrList.inc"
+
+#undef ATTR
+    }
+    JOS.attribute("id", createPointerRepresentation(A));
+    JOS.attribute("kind", AttrName);
+    JOS.attributeObject("range", [A, this]
+                        {
+                            writeSourceRange(A->getRange());
+                        });
+    attributeOnlyIfTrue("inherited", A->isInherited());
+    attributeOnlyIfTrue("implicit", A->isImplicit());
+
+    // FIXME: it would be useful for us to output the spelling kind as well as
+    // the actual spelling. This would allow us to distinguish between the
+    // various attribute syntaxes, but we don't currently track that information
+    // within the AST.
+    // JOS.attribute("spelling", A->getSpelling());
+
+    InnerAttrVisitor::Visit(A);
+}
+
+void ASTNodeVisitor::Visit(const Stmt* S)
+{
+    JOS.attribute("id", createPointerRepresentation(S));
+
+    if (!S)
+        return;
+
+    InnerStmtVisitor::Visit(S);
+}
+
+void ASTNodeVisitor::Visit(const Type* T)
+{
+    JOS.attribute("id", createPointerRepresentation(T));
+
+    if (!T)
+        return;
+
+    if (auto it = typeMap.find(T); it != typeMap.end())
+        return;
+    typeMap.emplace(T);
+
+    JOS.attribute("kind", (llvm::Twine(T->getTypeClassName()) + "Type").str());
+    JOS.attribute("type", createQualType(QualType(T, 0), /*Desugar=*/false));
+    attributeOnlyIfTrue("containsErrors", T->containsErrors());
+    attributeOnlyIfTrue("isDependent", T->isDependentType());
+    attributeOnlyIfTrue("isInstantiationDependent",
+                        T->isInstantiationDependentType());
+    attributeOnlyIfTrue("isVariablyModified", T->isVariablyModifiedType());
+    attributeOnlyIfTrue("containsUnexpandedPack",
+                        T->containsUnexpandedParameterPack());
+    attributeOnlyIfTrue("isImported", T->isFromAST());
+    InnerTypeVisitor::Visit(T);
+}
+
+void ASTNodeVisitor::Visit(QualType T)
+{
+    JOS.attribute("id", createPointerRepresentation(T.getAsOpaquePtr()));
+    JOS.attribute("kind", "QualType");
+    JOS.attribute("type", createQualType(T));
+    JOS.attribute("qualifiers", T.split().Quals.getAsString());
+}
+
+void ASTNodeVisitor::Visit(TypeLoc TL)
+{
+    if (TL.isNull())
+        return;
+    JOS.attribute("kind",
+                  (llvm::Twine(TL.getTypeLocClass() == TypeLoc::Qualified ? "Qualified" : TL.getTypePtr()->getTypeClassName()) +
+                   "TypeLoc")
+                      .str());
+    JOS.attribute("type",
+                  createQualType(QualType(TL.getType()), /*Desugar=*/false));
+    JOS.attributeObject("range",
+                        [TL, this]
+                        {
+                            writeSourceRange(TL.getSourceRange());
+                        });
+}
+
+AST::Declaration* ASTNodeVisitor::Visit(const Decl* D)
+{
+    JOS.attribute("id", createPointerRepresentation(D));
+
+    if (!D)
+        return nullptr;
+
+    if (auto it = declMap.find(D); it != declMap.end())
+        return it->second;
+
+    JOS.attribute("kind", (llvm::Twine(D->getDeclKindName()) + "Decl").str());
+    JOS.attributeObject("loc",
+                        [D, this]
+                        {
+                            writeSourceLocation(D->getLocation());
+                        });
+    JOS.attributeObject("range",
+                        [D, this]
+                        {
+                            writeSourceRange(D->getSourceRange());
+                        });
+    attributeOnlyIfTrue("isImplicit", D->isImplicit());
+    attributeOnlyIfTrue("isInvalid", D->isInvalidDecl());
+
+    if (D->isUsed())
+        JOS.attribute("isUsed", true);
+    else if (D->isThisDeclarationReferenced())
+        JOS.attribute("isReferenced", true);
+
+    if (D->getLexicalDeclContext() != D->getDeclContext())
+    {
+        // Because of multiple inheritance, a DeclContext pointer does not produce
+        // the same pointer representation as a Decl pointer that references the
+        // same AST Node.
+        const auto* ParentDeclContextDecl = dyn_cast<Decl>(D->getDeclContext());
+        JOS.attribute("parentDeclContextId",
+                      createPointerRepresentation(ParentDeclContextDecl));
+    }
+
+    addPreviousDeclaration(D);
+    AST::Declaration* AST_D = InnerDeclVisitor::Visit(D);
+    if (!AST_D)
+        return nullptr;
+
+    AST_D->originalPtr = (void*)D;
+    AST_D->kind = (AST::DeclarationKind)D->getKind();
+    AST_D->location = SourceLocation(D->getLocation().getRawEncoding());
+
+    AST_D->isImplicit = D->isImplicit();
+    AST_D->isInvalid = D->isInvalidDecl();
+    AST_D->isUsed = D->isUsed();
+    AST_D->isReferenced = D->isUsed() && D->isThisDeclarationReferenced();
+
+    AST_D->USR = GetDeclUSR(D);
+    AST_D->access = ConvertToAccess(D->getAccess());
+
+    return AST_D;
+}
+
+void ASTNodeVisitor::Visit(const comments::Comment* C, const comments::FullComment* FC)
+{
+    if (!C)
+        return;
+
+    JOS.attribute("id", createPointerRepresentation(C));
+    JOS.attribute("kind", C->getCommentKindName());
+    JOS.attributeObject("loc",
+                        [C, this]
+                        {
+                            writeSourceLocation(C->getLocation());
+                        });
+    JOS.attributeObject("range",
+                        [C, this]
+                        {
+                            writeSourceRange(C->getSourceRange());
+                        });
+
+    InnerCommentVisitor::visit(C, FC);
+}
+
+void ASTNodeVisitor::Visit(const TemplateArgument& TA, clang::SourceRange R, const Decl* From, StringRef Label)
+{
+    JOS.attribute("kind", "TemplateArgument");
+    if (R.isValid())
+        JOS.attributeObject("range", [R, this]
+                            {
+                                writeSourceRange(R);
+                            });
+
+    if (From)
+        JOS.attribute(Label.empty() ? "fromDecl" : Label, createBareDeclRef(From));
+
+    InnerTemplateArgVisitor::Visit(TA);
+}
+
+void ASTNodeVisitor::Visit(const CXXCtorInitializer* Init)
+{
+    JOS.attribute("kind", "CXXCtorInitializer");
+    if (Init->isAnyMemberInitializer())
+        JOS.attribute("anyInit", createBareDeclRef(Init->getAnyMember()));
+    else if (Init->isBaseInitializer())
+        JOS.attribute("baseInit",
+                      createQualType(QualType(Init->getBaseClass(), 0)));
+    else if (Init->isDelegatingInitializer())
+        JOS.attribute("delegatingInit",
+                      createQualType(Init->getTypeSourceInfo()->getType()));
+    else
+        llvm_unreachable("Unknown initializer type");
+}
+
+void ASTNodeVisitor::Visit(const BlockDecl::Capture& C)
+{
+    JOS.attribute("kind", "Capture");
+    attributeOnlyIfTrue("byref", C.isByRef());
+    attributeOnlyIfTrue("nested", C.isNested());
+    if (C.getVariable())
+        JOS.attribute("var", createBareDeclRef(C.getVariable()));
+}
+
+void ASTNodeVisitor::Visit(const GenericSelectionExpr::ConstAssociation& A)
+{
+    JOS.attribute("associationKind", A.getTypeSourceInfo() ? "case" : "default");
+    attributeOnlyIfTrue("selected", A.isSelected());
+}
+
+void ASTNodeVisitor::Visit(const concepts::Requirement* R)
+{
+    if (!R)
+        return;
+
+    switch (R->getKind())
+    {
+        case concepts::Requirement::RK_Type:
+            JOS.attribute("kind", "TypeRequirement");
+            break;
+        case concepts::Requirement::RK_Simple:
+            JOS.attribute("kind", "SimpleRequirement");
+            break;
+        case concepts::Requirement::RK_Compound:
+            JOS.attribute("kind", "CompoundRequirement");
+            break;
+        case concepts::Requirement::RK_Nested:
+            JOS.attribute("kind", "NestedRequirement");
+            break;
+    }
+
+    if (auto* ER = dyn_cast<concepts::ExprRequirement>(R))
+        attributeOnlyIfTrue("noexcept", ER->hasNoexceptRequirement());
+
+    attributeOnlyIfTrue("isDependent", R->isDependent());
+    if (!R->isDependent())
+        JOS.attribute("satisfied", R->isSatisfied());
+    attributeOnlyIfTrue("containsUnexpandedPack",
+                        R->containsUnexpandedParameterPack());
+}
+
+void ASTNodeVisitor::Visit(const APValue& Value, QualType Ty)
+{
+    std::string Str;
+    llvm::raw_string_ostream OS(Str);
+    Value.printPretty(OS, Ctx, Ty);
+    JOS.attribute("value", Str);
+}
+
+void ASTNodeVisitor::Visit(const ConceptReference* CR)
+{
+    JOS.attribute("kind", "ConceptReference");
+    JOS.attribute("id", createPointerRepresentation(CR->getNamedConcept()));
+    if (const auto* Args = CR->getTemplateArgsAsWritten())
+    {
+        JOS.attributeArray("templateArgsAsWritten", [Args, this]
+                           {
+                               for (const TemplateArgumentLoc& TAL : Args->arguments())
+                                   JOS.object(
+                                       [&TAL, this]
+                                       {
+                                           Visit(TAL.getArgument(), TAL.getSourceRange());
+                                       });
+                           });
+    }
+    /*JOS.attributeObject("loc",
+        [CR, this] { writeSourceLocation(CR->getLocation()); });
+    JOS.attributeObject("range",
+        [CR, this] { writeSourceRange(CR->getSourceRange()); });*/
+}
+
+void ASTNodeVisitor::writeIncludeStack(PresumedLoc Loc, bool JustFirst)
+{
+    if (Loc.isInvalid())
+        return;
+
+    JOS.attributeBegin("includedFrom");
+    JOS.objectBegin();
+
+    if (!JustFirst)
+    {
+        // Walk the stack recursively, then print out the presumed location.
+        writeIncludeStack(SM.getPresumedLoc(Loc.getIncludeLoc()));
+    }
+
+    JOS.attribute("file", Loc.getFilename());
+    JOS.objectEnd();
+    JOS.attributeEnd();
+}
+
+void ASTNodeVisitor::writeBareSourceLocation(clang::SourceLocation Loc, bool IsSpelling, bool addFileInfo)
+{
+    PresumedLoc Presumed = SM.getPresumedLoc(Loc);
+    unsigned ActualLine = IsSpelling ? SM.getSpellingLineNumber(Loc) : SM.getExpansionLineNumber(Loc);
+    StringRef ActualFile = SM.getBufferName(Loc);
+
+    if (!Presumed.isValid())
+        return;
+
+    JOS.attribute("offset", SM.getDecomposedLoc(Loc).second);
+    if (LastLocFilename != ActualFile)
+    {
+        if (addFileInfo)
+            JOS.attribute("file", ActualFile);
+        JOS.attribute("line", ActualLine);
+    }
+    else if (LastLocLine != ActualLine)
+        JOS.attribute("line", ActualLine);
+
+    StringRef PresumedFile = Presumed.getFilename();
+    if (PresumedFile != ActualFile && LastLocPresumedFilename != PresumedFile && addFileInfo)
+        JOS.attribute("presumedFile", PresumedFile);
+
+    unsigned PresumedLine = Presumed.getLine();
+    if (ActualLine != PresumedLine && LastLocPresumedLine != PresumedLine)
+        JOS.attribute("presumedLine", PresumedLine);
+
+    JOS.attribute("col", Presumed.getColumn());
+    JOS.attribute("tokLen",
+                  Lexer::MeasureTokenLength(Loc, SM, Ctx.getLangOpts()));
+    LastLocFilename = ActualFile;
+    LastLocPresumedFilename = PresumedFile;
+    LastLocPresumedLine = PresumedLine;
+    LastLocLine = ActualLine;
+
+    if (addFileInfo)
+    {
+        // Orthogonal to the file, line, and column de-duplication is whether the
+        // given location was a result of an include. If so, print where the
+        // include location came from.
+        writeIncludeStack(SM.getPresumedLoc(Presumed.getIncludeLoc()),
+                          /*JustFirst*/ true);
+    }
+}
+
+void ASTNodeVisitor::writeSourceLocation(clang::SourceLocation Loc, bool addFileInfo)
+{
+    clang::SourceLocation Spelling = SM.getSpellingLoc(Loc);
+    clang::SourceLocation Expansion = SM.getExpansionLoc(Loc);
+
+    if (Expansion != Spelling)
+    {
+        // If the expansion and the spelling are different, output subobjects
+        // describing both locations.
+        JOS.attributeObject("spellingLoc", [Spelling, addFileInfo, this]
+                            {
+                                writeBareSourceLocation(Spelling, /*IsSpelling*/ true, addFileInfo);
+                            });
+        JOS.attributeObject("expansionLoc", [Expansion, addFileInfo, Loc, this]
+                            {
+                                writeBareSourceLocation(Expansion, /*IsSpelling*/ false, addFileInfo);
+                                // If there is a macro expansion, add extra information if the interesting
+                                // bit is the macro arg expansion.
+                                if (SM.isMacroArgExpansion(Loc))
+                                    JOS.attribute("isMacroArgExpansion", true);
+                            });
+    }
+    else
+        writeBareSourceLocation(Spelling, /*IsSpelling*/ true, addFileInfo);
+}
+
+void ASTNodeVisitor::writeSourceRange(clang::SourceRange R)
+{
+    JOS.attributeObject("begin",
+                        [R, this]
+                        {
+                            writeSourceLocation(R.getBegin(), false);
+                        });
+
+    JOS.attributeObject("end", [R, this]
+                        {
+                            writeSourceLocation(R.getEnd(), false);
+                        });
+}
+
+std::string ASTNodeVisitor::createPointerRepresentation(const void* Ptr)
+{
+    // Because JSON stores integer values as signed 64-bit integers, trying to
+    // represent them as such makes for very ugly pointer values in the resulting
+    // output. Instead, we convert the value to hex and treat it as a string.
+    return "0x" + llvm::utohexstr(reinterpret_cast<uint64_t>(Ptr), true);
+}
+
+llvm::json::Object ASTNodeVisitor::createQualType(QualType QT, bool Desugar)
+{
+    SplitQualType SQT = QT.split();
+    std::string SQTS = QualType::getAsString(SQT, PrintPolicy);
+    llvm::json::Object Ret{ { "qualType", SQTS } };
+
+    if (Desugar && !QT.isNull())
+    {
+        SplitQualType DSQT = QT.getSplitDesugaredType();
+        if (DSQT != SQT)
+        {
+            std::string DSQTS = QualType::getAsString(DSQT, PrintPolicy);
+            if (DSQTS != SQTS)
+                Ret["desugaredQualType"] = DSQTS;
+        }
+        if (const auto* TT = QT->getAs<TypedefType>())
+            Ret["typeAliasDeclId"] = createPointerRepresentation(TT->getDecl());
+    }
+    return Ret;
+}
+
+AST::QualifiedType ASTNodeVisitor::CreateQualifiedType(QualType QT, bool Desugar)
+{
+    if (QT.isNull())
+        return {};
+
+    SplitQualType SQT = QT.split();
+    AST::QualifiedType Ret;
+    Ret.qualifiers = GetTypeQualifiers(SQT.Quals);
+    // Ret.type = Visit(SQT.Ty); // TODO: Implement Visit
+
+    if (Desugar)
+    {
+        SplitQualType DSQT = QT.getSplitDesugaredType();
+        if (DSQT != SQT)
+        {
+            // Ret.desugaredType = Visit(DSQT.Ty);
+        }
+
+        if (const auto* TT = QT->getAs<TypedefType>())
+            Ret.typeAliasDeclId = (void*)TT->getDecl();
+    }
+    return Ret;
+}
+
+void ASTNodeVisitor::writeBareDeclRef(const Decl* D)
+{
+    JOS.attribute("id", createPointerRepresentation(D));
+    if (!D)
+        return;
+
+    JOS.attribute("kind", (llvm::Twine(D->getDeclKindName()) + "Decl").str());
+    if (const auto* ND = dyn_cast<NamedDecl>(D))
+        JOS.attribute("name", ND->getDeclName().getAsString());
+    if (const auto* VD = dyn_cast<ValueDecl>(D))
+        JOS.attribute("type", createQualType(VD->getType()));
+}
+
+llvm::json::Object ASTNodeVisitor::createBareDeclRef(const Decl* D)
+{
+    llvm::json::Object Ret{ { "id", createPointerRepresentation(D) } };
+    if (!D)
+        return Ret;
+
+    Ret["kind"] = (llvm::Twine(D->getDeclKindName()) + "Decl").str();
+    if (const auto* ND = dyn_cast<NamedDecl>(D))
+        Ret["name"] = ND->getDeclName().getAsString();
+    if (const auto* VD = dyn_cast<ValueDecl>(D))
+        Ret["type"] = createQualType(VD->getType());
+    return Ret;
+}
+
+llvm::json::Array ASTNodeVisitor::createCastPath(const CastExpr* C)
+{
+    llvm::json::Array Ret;
+    if (C->path_empty())
+        return Ret;
+
+    for (auto I = C->path_begin(), E = C->path_end(); I != E; ++I)
+    {
+        const CXXBaseSpecifier* Base = *I;
+        const auto* RD =
+            cast<CXXRecordDecl>(Base->getType()->castAs<RecordType>()->getDecl());
+
+        llvm::json::Object Val{ { "name", RD->getName() } };
+        if (Base->isVirtual())
+            Val["isVirtual"] = true;
+        Ret.push_back(std::move(Val));
+    }
+    return Ret;
+}
+
+#define FIELD2(Name, Flag) \
+    if (RD->Flag())        \
+    Ret[Name] = true
+#define FIELD1(Flag) FIELD2(#Flag, Flag)
+
+static llvm::json::Object createDefaultConstructorDefinitionData(const CXXRecordDecl* RD)
+{
+    llvm::json::Object Ret;
+
+    FIELD2("exists", hasDefaultConstructor);
+    FIELD2("trivial", hasTrivialDefaultConstructor);
+    FIELD2("nonTrivial", hasNonTrivialDefaultConstructor);
+    FIELD2("userProvided", hasUserProvidedDefaultConstructor);
+    FIELD2("isConstexpr", hasConstexprDefaultConstructor);
+    FIELD2("needsImplicit", needsImplicitDefaultConstructor);
+    FIELD2("defaultedIsConstexpr", defaultedDefaultConstructorIsConstexpr);
+
+    return Ret;
+}
+
+static llvm::json::Object createCopyConstructorDefinitionData(const CXXRecordDecl* RD)
+{
+    llvm::json::Object Ret;
+
+    FIELD2("simple", hasSimpleCopyConstructor);
+    FIELD2("trivial", hasTrivialCopyConstructor);
+    FIELD2("nonTrivial", hasNonTrivialCopyConstructor);
+    FIELD2("userDeclared", hasUserDeclaredCopyConstructor);
+    FIELD2("hasConstParam", hasCopyConstructorWithConstParam);
+    FIELD2("implicitHasConstParam", implicitCopyConstructorHasConstParam);
+    FIELD2("needsImplicit", needsImplicitCopyConstructor);
+    FIELD2("needsOverloadResolution", needsOverloadResolutionForCopyConstructor);
+    if (!RD->needsOverloadResolutionForCopyConstructor())
+        FIELD2("defaultedIsDeleted", defaultedCopyConstructorIsDeleted);
+
+    return Ret;
+}
+
+static llvm::json::Object createMoveConstructorDefinitionData(const CXXRecordDecl* RD)
+{
+    llvm::json::Object Ret;
+
+    FIELD2("exists", hasMoveConstructor);
+    FIELD2("simple", hasSimpleMoveConstructor);
+    FIELD2("trivial", hasTrivialMoveConstructor);
+    FIELD2("nonTrivial", hasNonTrivialMoveConstructor);
+    FIELD2("userDeclared", hasUserDeclaredMoveConstructor);
+    FIELD2("needsImplicit", needsImplicitMoveConstructor);
+    FIELD2("needsOverloadResolution", needsOverloadResolutionForMoveConstructor);
+    if (!RD->needsOverloadResolutionForMoveConstructor())
+        FIELD2("defaultedIsDeleted", defaultedMoveConstructorIsDeleted);
+
+    return Ret;
+}
+
+static llvm::json::Object createCopyAssignmentDefinitionData(const CXXRecordDecl* RD)
+{
+    llvm::json::Object Ret;
+
+    FIELD2("simple", hasSimpleCopyAssignment);
+    FIELD2("trivial", hasTrivialCopyAssignment);
+    FIELD2("nonTrivial", hasNonTrivialCopyAssignment);
+    FIELD2("hasConstParam", hasCopyAssignmentWithConstParam);
+    FIELD2("implicitHasConstParam", implicitCopyAssignmentHasConstParam);
+    FIELD2("userDeclared", hasUserDeclaredCopyAssignment);
+    FIELD2("needsImplicit", needsImplicitCopyAssignment);
+    FIELD2("needsOverloadResolution", needsOverloadResolutionForCopyAssignment);
+
+    return Ret;
+}
+
+static llvm::json::Object createMoveAssignmentDefinitionData(const CXXRecordDecl* RD)
+{
+    llvm::json::Object Ret;
+
+    FIELD2("exists", hasMoveAssignment);
+    FIELD2("simple", hasSimpleMoveAssignment);
+    FIELD2("trivial", hasTrivialMoveAssignment);
+    FIELD2("nonTrivial", hasNonTrivialMoveAssignment);
+    FIELD2("userDeclared", hasUserDeclaredMoveAssignment);
+    FIELD2("needsImplicit", needsImplicitMoveAssignment);
+    FIELD2("needsOverloadResolution", needsOverloadResolutionForMoveAssignment);
+
+    return Ret;
+}
+
+static llvm::json::Object createDestructorDefinitionData(const CXXRecordDecl* RD)
+{
+    llvm::json::Object Ret;
+
+    FIELD2("simple", hasSimpleDestructor);
+    FIELD2("irrelevant", hasIrrelevantDestructor);
+    FIELD2("trivial", hasTrivialDestructor);
+    FIELD2("nonTrivial", hasNonTrivialDestructor);
+    FIELD2("userDeclared", hasUserDeclaredDestructor);
+    FIELD2("needsImplicit", needsImplicitDestructor);
+    FIELD2("needsOverloadResolution", needsOverloadResolutionForDestructor);
+    if (!RD->needsOverloadResolutionForDestructor())
+        FIELD2("defaultedIsDeleted", defaultedDestructorIsDeleted);
+
+    return Ret;
+}
+
+llvm::json::Object ASTNodeVisitor::createCXXRecordDefinitionData(const CXXRecordDecl* RD)
+{
+    llvm::json::Object Ret;
+
+    // This data is common to all C++ classes.
+    FIELD1(isGenericLambda);
+    FIELD1(isLambda);
+    FIELD1(isEmpty);
+    FIELD1(isAggregate);
+    FIELD1(isStandardLayout);
+    FIELD1(isTriviallyCopyable);
+    FIELD1(isPOD);
+    FIELD1(isTrivial);
+    FIELD1(isPolymorphic);
+    FIELD1(isAbstract);
+    FIELD1(isLiteral);
+    FIELD1(canPassInRegisters);
+    FIELD1(hasUserDeclaredConstructor);
+    FIELD1(hasConstexprNonCopyMoveConstructor);
+    FIELD1(hasMutableFields);
+    FIELD1(hasVariantMembers);
+    FIELD2("canConstDefaultInit", allowConstDefaultInit);
+
+    Ret["defaultCtor"] = createDefaultConstructorDefinitionData(RD);
+    Ret["copyCtor"] = createCopyConstructorDefinitionData(RD);
+    Ret["moveCtor"] = createMoveConstructorDefinitionData(RD);
+    Ret["copyAssign"] = createCopyAssignmentDefinitionData(RD);
+    Ret["moveAssign"] = createMoveAssignmentDefinitionData(RD);
+    Ret["dtor"] = createDestructorDefinitionData(RD);
+
+    return Ret;
+}
+
+#undef FIELD1
+#undef FIELD2
+
+std::string ASTNodeVisitor::createAccessSpecifier(AccessSpecifier AS)
+{
+    const auto AccessSpelling = getAccessSpelling(AS);
+    if (AccessSpelling.empty())
+        return "none";
+    return AccessSpelling.str();
+}
+
+llvm::json::Object ASTNodeVisitor::createCXXBaseSpecifier(const CXXBaseSpecifier& BS)
+{
+    llvm::json::Object Ret;
+
+    Ret["type"] = createQualType(BS.getType());
+    Ret["access"] = createAccessSpecifier(BS.getAccessSpecifier());
+    Ret["writtenAccess"] =
+        createAccessSpecifier(BS.getAccessSpecifierAsWritten());
+    if (BS.isVirtual())
+        Ret["isVirtual"] = true;
+    if (BS.isPackExpansion())
+        Ret["isPackExpansion"] = true;
+
+    return Ret;
+}
+
+void ASTNodeVisitor::VisitAliasAttr(const AliasAttr* AA)
+{
+    JOS.attribute("aliasee", AA->getAliasee());
+}
+
+void ASTNodeVisitor::VisitCleanupAttr(const CleanupAttr* CA)
+{
+    JOS.attribute("cleanup_function", createBareDeclRef(CA->getFunctionDecl()));
+}
+
+void ASTNodeVisitor::VisitDeprecatedAttr(const DeprecatedAttr* DA)
+{
+    if (!DA->getMessage().empty())
+        JOS.attribute("message", DA->getMessage());
+    if (!DA->getReplacement().empty())
+        JOS.attribute("replacement", DA->getReplacement());
+}
+
+void ASTNodeVisitor::VisitUnavailableAttr(const UnavailableAttr* UA)
+{
+    if (!UA->getMessage().empty())
+        JOS.attribute("message", UA->getMessage());
+}
+
+void ASTNodeVisitor::VisitSectionAttr(const SectionAttr* SA)
+{
+    JOS.attribute("section_name", SA->getName());
+}
+
+void ASTNodeVisitor::VisitVisibilityAttr(const VisibilityAttr* VA)
+{
+    JOS.attribute("visibility", VisibilityAttr::ConvertVisibilityTypeToStr(
+                                    VA->getVisibility()));
+}
+
+void ASTNodeVisitor::VisitTLSModelAttr(const TLSModelAttr* TA)
+{
+    JOS.attribute("tls_model", TA->getModel());
+}
+
+void ASTNodeVisitor::VisitTypedefType(const TypedefType* TT)
+{
+    JOS.attribute("decl", createBareDeclRef(TT->getDecl()));
+    if (!TT->typeMatchesDecl())
+        JOS.attribute("type", createQualType(TT->desugar()));
+}
+
+void ASTNodeVisitor::VisitUsingType(const UsingType* TT)
+{
+    JOS.attribute("decl", createBareDeclRef(TT->getFoundDecl()));
+    if (!TT->typeMatchesDecl())
+        JOS.attribute("type", createQualType(TT->desugar()));
+}
+
+void ASTNodeVisitor::VisitFunctionType(const FunctionType* T)
+{
+    FunctionType::ExtInfo E = T->getExtInfo();
+    attributeOnlyIfTrue("noreturn", E.getNoReturn());
+    attributeOnlyIfTrue("producesResult", E.getProducesResult());
+    if (E.getHasRegParm())
+        JOS.attribute("regParm", E.getRegParm());
+    JOS.attribute("cc", FunctionType::getNameForCallConv(E.getCC()));
+}
+
+void ASTNodeVisitor::VisitFunctionProtoType(const FunctionProtoType* T)
+{
+    FunctionProtoType::ExtProtoInfo E = T->getExtProtoInfo();
+    attributeOnlyIfTrue("trailingReturn", E.HasTrailingReturn);
+    attributeOnlyIfTrue("const", T->isConst());
+    attributeOnlyIfTrue("volatile", T->isVolatile());
+    attributeOnlyIfTrue("restrict", T->isRestrict());
+    attributeOnlyIfTrue("variadic", E.Variadic);
+    switch (E.RefQualifier)
+    {
+        case RQ_LValue: JOS.attribute("refQualifier", "&"); break;
+        case RQ_RValue: JOS.attribute("refQualifier", "&&"); break;
+        case RQ_None: break;
+    }
+    switch (E.ExceptionSpec.Type)
+    {
+        case EST_DynamicNone:
+        case EST_Dynamic:
+        {
+            JOS.attribute("exceptionSpec", "throw");
+            llvm::json::Array Types;
+            for (QualType QT : E.ExceptionSpec.Exceptions)
+                Types.push_back(createQualType(QT));
+            JOS.attribute("exceptionTypes", std::move(Types));
+        }
+        break;
+        case EST_MSAny:
+            JOS.attribute("exceptionSpec", "throw");
+            JOS.attribute("throwsAny", true);
+            break;
+        case EST_BasicNoexcept:
+            JOS.attribute("exceptionSpec", "noexcept");
+            break;
+        case EST_NoexceptTrue:
+        case EST_NoexceptFalse:
+            JOS.attribute("exceptionSpec", "noexcept");
+            JOS.attribute("conditionEvaluatesTo",
+                          E.ExceptionSpec.Type == EST_NoexceptTrue);
+            // JOS.attributeWithCall("exceptionSpecExpr",
+            //                     [this, E]() { Visit(E.ExceptionSpec.NoexceptExpr); });
+            break;
+        case EST_NoThrow:
+            JOS.attribute("exceptionSpec", "nothrow");
+            break;
+            // FIXME: I cannot find a way to trigger these cases while dumping the AST. I
+            // suspect you can only run into them when executing an AST dump from within
+            // the debugger, which is not a use case we worry about for the JSON dumping
+            // feature.
+        case EST_DependentNoexcept:
+        case EST_Unevaluated:
+        case EST_Uninstantiated:
+        case EST_Unparsed:
+        case EST_None: break;
+    }
+    VisitFunctionType(T);
+}
+
+void ASTNodeVisitor::VisitRValueReferenceType(const ReferenceType* RT)
+{
+    attributeOnlyIfTrue("spelledAsLValue", RT->isSpelledAsLValue());
+}
+
+void ASTNodeVisitor::VisitArrayType(const ArrayType* AT)
+{
+    switch (AT->getSizeModifier())
+    {
+        case ArrayType::ArraySizeModifier::Star:
+            JOS.attribute("sizeModifier", "*");
+            break;
+        case ArrayType::ArraySizeModifier::Static:
+            JOS.attribute("sizeModifier", "static");
+            break;
+        case ArrayType::ArraySizeModifier::Normal:
+            break;
+    }
+
+    std::string Str = AT->getIndexTypeQualifiers().getAsString();
+    if (!Str.empty())
+        JOS.attribute("indexTypeQualifiers", Str);
+}
+
+void ASTNodeVisitor::VisitConstantArrayType(const ConstantArrayType* CAT)
+{
+    // FIXME: this should use ZExt instead of SExt, but JSON doesn't allow a
+    // narrowing conversion to int64_t so it cannot be expressed.
+    JOS.attribute("size", CAT->getSize().getSExtValue());
+    VisitArrayType(CAT);
+}
+
+void ASTNodeVisitor::VisitDependentSizedExtVectorType(
+    const DependentSizedExtVectorType* VT)
+{
+    JOS.attributeObject(
+        "attrLoc", [VT, this]
+        {
+            writeSourceLocation(VT->getAttributeLoc());
+        });
+}
+
+void ASTNodeVisitor::VisitVectorType(const VectorType* VT)
+{
+    JOS.attribute("numElements", VT->getNumElements());
+    switch (VT->getVectorKind())
+    {
+        case VectorType::VectorKind::GenericVector:
+            break;
+        case VectorType::VectorKind::AltiVecVector:
+            JOS.attribute("vectorKind", "altivec");
+            break;
+        case VectorType::VectorKind::AltiVecPixel:
+            JOS.attribute("vectorKind", "altivec pixel");
+            break;
+        case VectorType::VectorKind::AltiVecBool:
+            JOS.attribute("vectorKind", "altivec bool");
+            break;
+        case VectorType::VectorKind::NeonVector:
+            JOS.attribute("vectorKind", "neon");
+            break;
+        case VectorType::VectorKind::NeonPolyVector:
+            JOS.attribute("vectorKind", "neon poly");
+            break;
+        case VectorType::VectorKind::SveFixedLengthDataVector:
+            JOS.attribute("vectorKind", "fixed-length sve data vector");
+            break;
+        case VectorType::VectorKind::SveFixedLengthPredicateVector:
+            JOS.attribute("vectorKind", "fixed-length sve predicate vector");
+            break;
+        case VectorType::VectorKind::RVVFixedLengthDataVector:
+            JOS.attribute("vectorKind", "fixed-length rvv data vector");
+            break;
+            /*case VectorType::VectorKind::RVVFixedLengthMask:
+            case VectorType::VectorKind::RVVFixedLengthMask_1:
+            case VectorType::VectorKind::RVVFixedLengthMask_2:
+            case VectorType::VectorKind::RVVFixedLengthMask_4:
+                JOS.attribute("vectorKind", "fixed-length rvv mask vector");
+                break;*/
+    }
+}
+
+void ASTNodeVisitor::VisitUnresolvedUsingType(const UnresolvedUsingType* UUT)
+{
+    JOS.attribute("decl", createBareDeclRef(UUT->getDecl()));
+}
+
+void ASTNodeVisitor::VisitUnaryTransformType(const UnaryTransformType* UTT)
+{
+    switch (UTT->getUTTKind())
+    {
+#define TRANSFORM_TYPE_TRAIT_DEF(Enum, Trait)   \
+    case UnaryTransformType::Enum:              \
+        JOS.attribute("transformKind", #Trait); \
+        break;
+#include "clang/Basic/TransformTypeTraits.def"
+    }
+}
+
+void ASTNodeVisitor::VisitTagType(const TagType* TT)
+{
+    JOS.attribute("decl", createBareDeclRef(TT->getDecl()));
+}
+
+void ASTNodeVisitor::VisitTemplateTypeParmType(
+    const TemplateTypeParmType* TTPT)
+{
+    JOS.attribute("depth", TTPT->getDepth());
+    JOS.attribute("index", TTPT->getIndex());
+    attributeOnlyIfTrue("isPack", TTPT->isParameterPack());
+    JOS.attribute("decl", createBareDeclRef(TTPT->getDecl()));
+}
+
+void ASTNodeVisitor::VisitSubstTemplateTypeParmType(
+    const SubstTemplateTypeParmType* STTPT)
+{
+    JOS.attribute("index", STTPT->getIndex());
+    if (auto PackIndex = STTPT->getPackIndex())
+        JOS.attribute("pack_index", *PackIndex);
+}
+
+void ASTNodeVisitor::VisitSubstTemplateTypeParmPackType(
+    const SubstTemplateTypeParmPackType* T)
+{
+    JOS.attribute("index", T->getIndex());
+}
+
+void ASTNodeVisitor::VisitAutoType(const AutoType* AT)
+{
+    JOS.attribute("undeduced", !AT->isDeduced());
+    switch (AT->getKeyword())
+    {
+        case AutoTypeKeyword::Auto:
+            JOS.attribute("typeKeyword", "auto");
+            break;
+        case AutoTypeKeyword::DecltypeAuto:
+            JOS.attribute("typeKeyword", "decltype(auto)");
+            break;
+        case AutoTypeKeyword::GNUAutoType:
+            JOS.attribute("typeKeyword", "__auto_type");
+            break;
+    }
+}
+
+void ASTNodeVisitor::VisitTemplateSpecializationType(
+    const TemplateSpecializationType* TST)
+{
+    attributeOnlyIfTrue("isAlias", TST->isTypeAlias());
+
+    std::string Str;
+    llvm::raw_string_ostream OS(Str);
+    TST->getTemplateName().print(OS, PrintPolicy);
+    JOS.attribute("templateName", Str);
+}
+
+void ASTNodeVisitor::VisitInjectedClassNameType(
+    const InjectedClassNameType* ICNT)
+{
+    JOS.attribute("decl", createBareDeclRef(ICNT->getDecl()));
+}
+
+void ASTNodeVisitor::VisitObjCInterfaceType(const ObjCInterfaceType* OIT)
+{
+    JOS.attribute("decl", createBareDeclRef(OIT->getDecl()));
+}
+
+void ASTNodeVisitor::VisitPackExpansionType(const PackExpansionType* PET)
+{
+    if (std::optional<unsigned> N = PET->getNumExpansions())
+        JOS.attribute("numExpansions", *N);
+}
+
+void ASTNodeVisitor::VisitElaboratedType(const ElaboratedType* ET)
+{
+    if (const NestedNameSpecifier* NNS = ET->getQualifier())
+    {
+        std::string Str;
+        llvm::raw_string_ostream OS(Str);
+        NNS->print(OS, PrintPolicy, /*ResolveTemplateArgs*/ true);
+        JOS.attribute("qualifier", Str);
+    }
+    if (const TagDecl* TD = ET->getOwnedTagDecl())
+        JOS.attribute("ownedTagDecl", createBareDeclRef(TD));
+}
+
+void ASTNodeVisitor::VisitMacroQualifiedType(const MacroQualifiedType* MQT)
+{
+    JOS.attribute("macroName", MQT->getMacroIdentifier()->getName());
+}
+
+void ASTNodeVisitor::VisitMemberPointerType(const MemberPointerType* MPT)
+{
+    attributeOnlyIfTrue("isData", MPT->isMemberDataPointer());
+    attributeOnlyIfTrue("isFunction", MPT->isMemberFunctionPointer());
+}
+
+AST::Declaration* ASTNodeVisitor::VisitTranslationUnitDecl(const clang::TranslationUnitDecl* D)
+{
+    AST::TranslationUnit* TU = parser.GetOrCreateTranslationUnit(D);
+    declMap.emplace(D, TU);
+    return TU;
+}
+
+AST::Declaration* ASTNodeVisitor::VisitNamedDecl(const NamedDecl* ND)
+{
+    if (!ND)
+        return nullptr;
+
+    if (!ND->getDeclName())
+        return nullptr;
+
+    JOS.attribute("name", ND->getNameAsString());
+
+    std::string MangledName = parser.GetDeclMangledName(ND);
+    if (!MangledName.empty())
+        JOS.attribute("mangledName", MangledName);
+
+    declMap.try_emplace(ND, nullptr);
+
+    return nullptr;
+}
+
+void ASTNodeVisitor::HandleNamedDecl(AST::Declaration& AST_ND, const NamedDecl* ND)
+{
+    if (!ND)
+        return;
+
+    declMap.emplace(ND, &AST_ND);
+
+    AST_ND.name = ND->getName();
+    AST_ND.mangledName = parser.GetDeclMangledName(ND);
+    AST_ND.isHidden = !ND->isUnconditionallyVisible(); // TODO: Sema::isVisible()?
+}
+
+AST::Declaration* ASTNodeVisitor::VisitTypedefDecl(const TypedefDecl* TD)
+{
+    AST::Declaration* Decl = VisitNamedDecl(TD);
+    JOS.attribute("type", createQualType(TD->getUnderlyingType()));
+    return Decl;
+}
+
+AST::Declaration* ASTNodeVisitor::VisitTypeAliasDecl(const TypeAliasDecl* TAD)
+{
+    auto NS = parser.GetNamespace(TAD);
+    auto Name = GetDeclName(TAD);
+
+    auto AST_TAD = NS->FindTypeAlias(Name, /*Create=*/false);
+
+    if (AST_TAD)
+        return AST_TAD;
+
+    AST_TAD = NS->FindTypeAlias(Name, /*Create=*/true);
+    HandleNamedDecl(*AST_TAD, TAD);
+
+    VisitNamedDecl(TAD);
+    JOS.attribute("type", createQualType(TAD->getUnderlyingType()));
+    return AST_TAD;
+}
+
+AST::Declaration* ASTNodeVisitor::VisitNamespaceDecl(const NamespaceDecl* ND)
+{
+    AST::TranslationUnit* Unit = parser.GetOrCreateTranslationUnit(cast<Decl>(ND->getDeclContext()));
+    AST::Namespace& NS = Unit->FindCreateNamespace(ND->getName());
+
+    NS.isInline = ND->isInline();
+    NS.isNested = ND->isNested();
+    NS.isAnonymous = ND->isAnonymousNamespace();
+
+    HandleNamedDecl(NS, ND);
+
+    VisitNamedDecl(ND);
+    attributeOnlyIfTrue("isInline", ND->isInline());
+    attributeOnlyIfTrue("isNested", ND->isNested());
+    if (!ND->isFirstDecl())
+        JOS.attribute("originalNamespace", createBareDeclRef(ND->getFirstDecl()));
+
+    return &NS;
+}
+
+AST::Declaration* ASTNodeVisitor::VisitUsingDirectiveDecl(const UsingDirectiveDecl* UDD)
+{
+    JOS.attribute("nominatedNamespace",
+                  createBareDeclRef(UDD->getNominatedNamespace()));
+    return nullptr;
+}
+
+AST::Declaration* ASTNodeVisitor::VisitNamespaceAliasDecl(const NamespaceAliasDecl* NAD)
+{
+    AST::Declaration* Decl = VisitNamedDecl(NAD);
+    JOS.attribute("aliasedNamespace",
+                  createBareDeclRef(NAD->getAliasedNamespace()));
+    return Decl;
+}
+
+AST::Declaration* ASTNodeVisitor::VisitUsingDecl(const UsingDecl* UD)
+{
+    std::string Name;
+    if (const NestedNameSpecifier* NNS = UD->getQualifier())
+    {
+        llvm::raw_string_ostream SOS(Name);
+        NNS->print(SOS, UD->getASTContext().getPrintingPolicy());
+    }
+    Name += UD->getNameAsString();
+    JOS.attribute("name", Name);
+    return nullptr;
+}
+
+AST::Declaration* ASTNodeVisitor::VisitUsingEnumDecl(const UsingEnumDecl* UED)
+{
+    JOS.attribute("target", createBareDeclRef(UED->getEnumDecl()));
+    return nullptr;
+}
+
+AST::Declaration* ASTNodeVisitor::VisitUsingShadowDecl(const UsingShadowDecl* USD)
+{
+    JOS.attribute("target", createBareDeclRef(USD->getTargetDecl()));
+    return nullptr;
+}
+
+AST::Declaration* ASTNodeVisitor::VisitVarDecl(const VarDecl* VD)
+{
+    AST::Declaration* Decl = VisitNamedDecl(VD);
+    JOS.attribute("type", createQualType(VD->getType()));
+
+    // TODO: clang 19
+    // if (const auto* P = dyn_cast<ParmVarDecl>(VD))
+    //    attributeOnlyIfTrue("explicitObjectParameter", P-isExplicitObjectParameter());
+
+    StorageClass SC = VD->getStorageClass();
+    if (SC != SC_None)
+        JOS.attribute("storageClass", VarDecl::getStorageClassSpecifierString(SC));
+    switch (VD->getTLSKind())
+    {
+        case VarDecl::TLS_Dynamic: JOS.attribute("tls", "dynamic"); break;
+        case VarDecl::TLS_Static: JOS.attribute("tls", "static"); break;
+        case VarDecl::TLS_None: break;
+    }
+    attributeOnlyIfTrue("nrvo", VD->isNRVOVariable());
+    attributeOnlyIfTrue("inline", VD->isInline());
+    attributeOnlyIfTrue("constexpr", VD->isConstexpr());
+    attributeOnlyIfTrue("modulePrivate", VD->isModulePrivate());
+    if (VD->hasInit())
+    {
+        switch (VD->getInitStyle())
+        {
+            case VarDecl::CInit: JOS.attribute("init", "c"); break;
+            case VarDecl::CallInit: JOS.attribute("init", "call"); break;
+            case VarDecl::ListInit: JOS.attribute("init", "list"); break;
+            case VarDecl::ParenListInit:
+                JOS.attribute("init", "paren-list");
+                break;
+        }
+    }
+    attributeOnlyIfTrue("isParameterPack", VD->isParameterPack());
+    return Decl;
+}
+
+AST::Declaration* ASTNodeVisitor::VisitFieldDecl(const FieldDecl* FD)
+{
+    AST::Declaration* Decl = VisitNamedDecl(FD);
+    JOS.attribute("type", createQualType(FD->getType()));
+    attributeOnlyIfTrue("mutable", FD->isMutable());
+    attributeOnlyIfTrue("modulePrivate", FD->isModulePrivate());
+    attributeOnlyIfTrue("isBitfield", FD->isBitField());
+    attributeOnlyIfTrue("hasInClassInitializer", FD->hasInClassInitializer());
+    return Decl;
+}
+
+AST::Declaration* ASTNodeVisitor::VisitFunctionDecl(const FunctionDecl* FD)
+{
+    AST::Declaration* Decl = VisitNamedDecl(FD);
+    JOS.attribute("type", createQualType(FD->getType()));
+    StorageClass SC = FD->getStorageClass();
+    if (SC != SC_None)
+        JOS.attribute("storageClass", VarDecl::getStorageClassSpecifierString(SC));
+    attributeOnlyIfTrue("inline", FD->isInlineSpecified());
+    attributeOnlyIfTrue("virtual", FD->isVirtualAsWritten());
+    attributeOnlyIfTrue("pure", FD->isPure()); // // TODO: clang 19 isPureVirtual
+    attributeOnlyIfTrue("explicitlyDeleted", FD->isDeletedAsWritten());
+    attributeOnlyIfTrue("constexpr", FD->isConstexpr());
+    attributeOnlyIfTrue("variadic", FD->isVariadic());
+    attributeOnlyIfTrue("immediate", FD->isImmediateFunction());
+
+    if (FD->isDefaulted())
+        JOS.attribute("explicitlyDefaulted",
+                      FD->isDeleted() ? "deleted" : "default");
+
+    // TODO: clang 19
+    /*if (StringLiteral* Msg = FD->getDeletedMessage())
+        JOS.attribute("deletedMessage", Msg->getString());*/
+    return Decl;
+}
+
+AST::Declaration* ASTNodeVisitor::VisitEnumDecl(const EnumDecl* ED)
+{
+    AST::Declaration* Decl = VisitNamedDecl(ED);
+    if (ED->isFixed())
+        JOS.attribute("fixedUnderlyingType", createQualType(ED->getIntegerType()));
+    if (ED->isScoped())
+        JOS.attribute("scopedEnumTag",
+                      ED->isScopedUsingClassTag() ? "class" : "struct");
+    return Decl;
+}
+AST::Declaration* ASTNodeVisitor::VisitEnumConstantDecl(const EnumConstantDecl* ECD)
+{
+    AST::Declaration* Decl = VisitNamedDecl(ECD);
+    JOS.attribute("type", createQualType(ECD->getType()));
+    return Decl;
+}
+
+AST::Declaration* ASTNodeVisitor::VisitRecordDecl(const RecordDecl* RD)
+{
+    AST::Declaration* Decl = VisitNamedDecl(RD);
+    JOS.attribute("tagUsed", RD->getKindName());
+    attributeOnlyIfTrue("completeDefinition", RD->isCompleteDefinition());
+    return Decl;
+}
+AST::Declaration* ASTNodeVisitor::VisitCXXRecordDecl(const CXXRecordDecl* RD)
+{
+    return FindOrInsertLazy(RD, [&]() -> AST::Class*
+                            {
+                                VisitRecordDecl(RD);
+
+                                // All other information requires a complete definition.
+                                if (!RD->isCompleteDefinition())
+                                    return nullptr;
+
+                                JOS.attribute("definitionData", createCXXRecordDefinitionData(RD));
+                                if (RD->getNumBases())
+                                {
+                                    JOS.attributeArray("bases", [this, RD]
+                                                       {
+                                                           for (const auto& Spec : RD->bases())
+                                                               JOS.value(createCXXBaseSpecifier(Spec));
+                                                       });
+                                }
+
+                                bool Process = false;
+                                AST::Class* RC = parser.GetRecord(RD, Process);
+
+                                if (!RC || !Process)
+                                    return RC;
+
+                                // parser.WalkRecordCXX(RD, RC);
+                                return RC;
+                            });
+}
+
+AST::Declaration* ASTNodeVisitor::VisitHLSLBufferDecl(const HLSLBufferDecl* D)
+{
+    AST::Declaration* Decl = VisitNamedDecl(D);
+    JOS.attribute("bufferKind", D->isCBuffer() ? "cbuffer" : "tbuffer");
+    return Decl;
+}
+
+AST::Declaration* ASTNodeVisitor::VisitTemplateTypeParmDecl(const TemplateTypeParmDecl* D)
+{
+    AST::Declaration* Decl = VisitNamedDecl(D);
+    JOS.attribute("tagUsed", D->wasDeclaredWithTypename() ? "typename" : "class");
+    JOS.attribute("depth", D->getDepth());
+    JOS.attribute("index", D->getIndex());
+    attributeOnlyIfTrue("isParameterPack", D->isParameterPack());
+
+    // TODO: clang 19
+    /*if (D->hasDefaultArgument())
+        JOS.attributeObject("defaultArg", [=] {
+        Visit(D->getDefaultArgument().getArgument(), SourceRange(),
+            D->getDefaultArgStorage().getInheritedFrom(),
+            D->defaultArgumentWasInherited() ? "inherited from" : "previous");
+            });*/
+    return Decl;
+}
+
+AST::Declaration* ASTNodeVisitor::VisitNonTypeTemplateParmDecl(const NonTypeTemplateParmDecl* D)
+{
+    AST::Declaration* Decl = VisitNamedDecl(D);
+    JOS.attribute("type", createQualType(D->getType()));
+    JOS.attribute("depth", D->getDepth());
+    JOS.attribute("index", D->getIndex());
+    attributeOnlyIfTrue("isParameterPack", D->isParameterPack());
+
+    // TODO: clang 19
+    /*if (D->hasDefaultArgument())
+        JOS.attributeObject("defaultArg", [=] {
+        Visit(D->getDefaultArgument().getArgument(), SourceRange(),
+            D->getDefaultArgStorage().getInheritedFrom(),
+            D->defaultArgumentWasInherited() ? "inherited from" : "previous");
+            });*/
+    return Decl;
+}
+
+AST::Declaration* ASTNodeVisitor::VisitTemplateTemplateParmDecl(const TemplateTemplateParmDecl* D)
+{
+    AST::Declaration* Decl = VisitNamedDecl(D);
+    JOS.attribute("depth", D->getDepth());
+    JOS.attribute("index", D->getIndex());
+    attributeOnlyIfTrue("isParameterPack", D->isParameterPack());
+
+    if (D->hasDefaultArgument())
+        JOS.attributeObject("defaultArg", [&]
+                            {
+                                const auto* InheritedFrom = D->getDefaultArgStorage().getInheritedFrom();
+                                Visit(D->getDefaultArgument().getArgument(),
+                                      InheritedFrom ? InheritedFrom->getSourceRange() : clang::SourceLocation{},
+                                      InheritedFrom,
+                                      D->defaultArgumentWasInherited() ? "inherited from" : "previous");
+                            });
+    return Decl;
+}
+
+AST::Declaration* ASTNodeVisitor::VisitLinkageSpecDecl(const LinkageSpecDecl* LSD)
+{
+    StringRef Lang;
+    switch (LSD->getLanguage())
+    {
+        case LinkageSpecDecl::LanguageIDs::lang_c:
+            Lang = "C";
+            break;
+        case LinkageSpecDecl::LanguageIDs::lang_cxx:
+            Lang = "C++";
+            break;
+    }
+    JOS.attribute("language", Lang);
+    attributeOnlyIfTrue("hasBraces", LSD->hasBraces());
+    return nullptr;
+}
+
+AST::Declaration* ASTNodeVisitor::VisitAccessSpecDecl(const AccessSpecDecl* ASD)
+{
+    JOS.attribute("access", createAccessSpecifier(ASD->getAccess()));
+    return nullptr;
+}
+
+AST::Declaration* ASTNodeVisitor::VisitFriendDecl(const FriendDecl* FD)
+{
+    if (const TypeSourceInfo* T = FD->getFriendType())
+        JOS.attribute("type", createQualType(T->getType()));
+    // TODO: clang 19
+    // attributeOnlyIfTrue("isPackExpansion", FD->isPackExpansion());
+    return nullptr;
+}
+
+AST::Declaration* ASTNodeVisitor::VisitObjCIvarDecl(const ObjCIvarDecl* D)
+{
+    AST::Declaration* Decl = VisitNamedDecl(D);
+    JOS.attribute("type", createQualType(D->getType()));
+    attributeOnlyIfTrue("synthesized", D->getSynthesize());
+    switch (D->getAccessControl())
+    {
+        case ObjCIvarDecl::None: JOS.attribute("access", "none"); break;
+        case ObjCIvarDecl::Private: JOS.attribute("access", "private"); break;
+        case ObjCIvarDecl::Protected: JOS.attribute("access", "protected"); break;
+        case ObjCIvarDecl::Public: JOS.attribute("access", "public"); break;
+        case ObjCIvarDecl::Package: JOS.attribute("access", "package"); break;
+    }
+    return Decl;
+}
+
+AST::Declaration* ASTNodeVisitor::VisitObjCMethodDecl(const ObjCMethodDecl* D)
+{
+    AST::Declaration* Decl = VisitNamedDecl(D);
+    JOS.attribute("returnType", createQualType(D->getReturnType()));
+    JOS.attribute("instance", D->isInstanceMethod());
+    attributeOnlyIfTrue("variadic", D->isVariadic());
+    return Decl;
+}
+
+AST::Declaration* ASTNodeVisitor::VisitObjCTypeParamDecl(const ObjCTypeParamDecl* D)
+{
+    AST::Declaration* Decl = VisitNamedDecl(D);
+    JOS.attribute("type", createQualType(D->getUnderlyingType()));
+    attributeOnlyIfTrue("bounded", D->hasExplicitBound());
+    switch (D->getVariance())
+    {
+        case ObjCTypeParamVariance::Invariant:
+            break;
+        case ObjCTypeParamVariance::Covariant:
+            JOS.attribute("variance", "covariant");
+            break;
+        case ObjCTypeParamVariance::Contravariant:
+            JOS.attribute("variance", "contravariant");
+            break;
+    }
+    return Decl;
+}
+
+AST::Declaration* ASTNodeVisitor::VisitObjCCategoryDecl(const ObjCCategoryDecl* D)
+{
+    AST::Declaration* Decl = VisitNamedDecl(D);
+    JOS.attribute("interface", createBareDeclRef(D->getClassInterface()));
+    JOS.attribute("implementation", createBareDeclRef(D->getImplementation()));
+
+    llvm::json::Array Protocols;
+    for (const auto* P : D->protocols())
+        Protocols.push_back(createBareDeclRef(P));
+    if (!Protocols.empty())
+        JOS.attribute("protocols", std::move(Protocols));
+    return Decl;
+}
+
+AST::Declaration* ASTNodeVisitor::VisitObjCCategoryImplDecl(const ObjCCategoryImplDecl* D)
+{
+    AST::Declaration* Decl = VisitNamedDecl(D);
+    JOS.attribute("interface", createBareDeclRef(D->getClassInterface()));
+    JOS.attribute("categoryDecl", createBareDeclRef(D->getCategoryDecl()));
+    return Decl;
+}
+
+AST::Declaration* ASTNodeVisitor::VisitObjCProtocolDecl(const ObjCProtocolDecl* D)
+{
+    AST::Declaration* Decl = VisitNamedDecl(D);
+
+    llvm::json::Array Protocols;
+    for (const auto* P : D->protocols())
+        Protocols.push_back(createBareDeclRef(P));
+    if (!Protocols.empty())
+        JOS.attribute("protocols", std::move(Protocols));
+    return Decl;
+}
+
+AST::Declaration* ASTNodeVisitor::VisitObjCInterfaceDecl(const ObjCInterfaceDecl* D)
+{
+    AST::Declaration* Decl = VisitNamedDecl(D);
+    JOS.attribute("super", createBareDeclRef(D->getSuperClass()));
+    JOS.attribute("implementation", createBareDeclRef(D->getImplementation()));
+
+    llvm::json::Array Protocols;
+    for (const auto* P : D->protocols())
+        Protocols.push_back(createBareDeclRef(P));
+    if (!Protocols.empty())
+        JOS.attribute("protocols", std::move(Protocols));
+    return Decl;
+}
+
+AST::Declaration* ASTNodeVisitor::VisitObjCImplementationDecl(
+    const ObjCImplementationDecl* D)
+{
+    AST::Declaration* Decl = VisitNamedDecl(D);
+    JOS.attribute("super", createBareDeclRef(D->getSuperClass()));
+    JOS.attribute("interface", createBareDeclRef(D->getClassInterface()));
+    return Decl;
+}
+
+AST::Declaration* ASTNodeVisitor::VisitObjCCompatibleAliasDecl(
+    const ObjCCompatibleAliasDecl* D)
+{
+    AST::Declaration* Decl = VisitNamedDecl(D);
+    JOS.attribute("interface", createBareDeclRef(D->getClassInterface()));
+    return Decl;
+}
+
+AST::Declaration* ASTNodeVisitor::VisitObjCPropertyDecl(const ObjCPropertyDecl* D)
+{
+    AST::Declaration* Decl = VisitNamedDecl(D);
+    JOS.attribute("type", createQualType(D->getType()));
+
+    switch (D->getPropertyImplementation())
+    {
+        case ObjCPropertyDecl::None: break;
+        case ObjCPropertyDecl::Required: JOS.attribute("control", "required"); break;
+        case ObjCPropertyDecl::Optional: JOS.attribute("control", "optional"); break;
+    }
+
+    ObjCPropertyAttribute::Kind Attrs = D->getPropertyAttributes();
+    if (Attrs != ObjCPropertyAttribute::kind_noattr)
+    {
+        if (Attrs & ObjCPropertyAttribute::kind_getter)
+            JOS.attribute("getter", createBareDeclRef(D->getGetterMethodDecl()));
+        if (Attrs & ObjCPropertyAttribute::kind_setter)
+            JOS.attribute("setter", createBareDeclRef(D->getSetterMethodDecl()));
+        attributeOnlyIfTrue("readonly",
+                            Attrs & ObjCPropertyAttribute::kind_readonly);
+        attributeOnlyIfTrue("assign", Attrs & ObjCPropertyAttribute::kind_assign);
+        attributeOnlyIfTrue("readwrite",
+                            Attrs & ObjCPropertyAttribute::kind_readwrite);
+        attributeOnlyIfTrue("retain", Attrs & ObjCPropertyAttribute::kind_retain);
+        attributeOnlyIfTrue("copy", Attrs & ObjCPropertyAttribute::kind_copy);
+        attributeOnlyIfTrue("nonatomic",
+                            Attrs & ObjCPropertyAttribute::kind_nonatomic);
+        attributeOnlyIfTrue("atomic", Attrs & ObjCPropertyAttribute::kind_atomic);
+        attributeOnlyIfTrue("weak", Attrs & ObjCPropertyAttribute::kind_weak);
+        attributeOnlyIfTrue("strong", Attrs & ObjCPropertyAttribute::kind_strong);
+        attributeOnlyIfTrue("unsafe_unretained",
+                            Attrs & ObjCPropertyAttribute::kind_unsafe_unretained);
+        attributeOnlyIfTrue("class", Attrs & ObjCPropertyAttribute::kind_class);
+        attributeOnlyIfTrue("direct", Attrs & ObjCPropertyAttribute::kind_direct);
+        attributeOnlyIfTrue("nullability",
+                            Attrs & ObjCPropertyAttribute::kind_nullability);
+        attributeOnlyIfTrue("null_resettable",
+                            Attrs & ObjCPropertyAttribute::kind_null_resettable);
+    }
+    return Decl;
+}
+
+AST::Declaration* ASTNodeVisitor::VisitObjCPropertyImplDecl(const ObjCPropertyImplDecl* D)
+{
+    AST::Declaration* Decl = VisitNamedDecl(D->getPropertyDecl());
+    JOS.attribute("implKind", D->getPropertyImplementation() ==
+                                      ObjCPropertyImplDecl::Synthesize ?
+                                  "synthesize" :
+                                  "dynamic");
+    JOS.attribute("propertyDecl", createBareDeclRef(D->getPropertyDecl()));
+    JOS.attribute("ivarDecl", createBareDeclRef(D->getPropertyIvarDecl()));
+    return Decl;
+}
+
+AST::Declaration* ASTNodeVisitor::VisitBlockDecl(const BlockDecl* D)
+{
+    attributeOnlyIfTrue("variadic", D->isVariadic());
+    attributeOnlyIfTrue("capturesThis", D->capturesCXXThis());
+    return nullptr;
+}
+
+void ASTNodeVisitor::ConvertExprImpl(const clang::Expr* E, AST::Expr& Dst)
+{
+    // TODO: Incomplete
+    // Dst.type = parser.GetQualifiedType(E->getType());
+    // JOS.attribute("type", createQualType(E->getType()));
+
+    // TODO: Convert to functions instead of data
+    Dst.isLValue = E->isLValue();
+    Dst.isXValue = E->isXValue();
+    Dst.isPRValue = E->isPRValue();
+    Dst.isGLValue = E->isGLValue();
+
+    Dst.containsErrors = E->containsErrors();
+    Dst.containsUnexpandedParameterPack = E->containsUnexpandedParameterPack();
+    Dst.exprLoc = ConvertSourceLocation(E->getExprLoc());
+    // Dst.hasPlaceholderType = E->hasPlaceholderType();
+    Dst.isInstantiationDependent = E->isInstantiationDependent();
+    Dst.isOrdinaryOrBitFieldObject = E->isOrdinaryOrBitFieldObject();
+    // Dst.isReadIfDiscardedInCPlusPlus11 = E->isGLValue() && E->isReadIfDiscardedInCPlusPlus11();
+    Dst.isTypeDependent = E->isTypeDependent();
+    Dst.isValueDependent = E->isValueDependent();
+    Dst.refersToMatrixElement = E->refersToMatrixElement();
+}
+
+void ASTNodeVisitor::ConvertStmt(const clang::Stmt* S, AST::Stmt& Dst)
+{
+    // Dst.stmtClass = S->getStmtClass(); // Already set
+    Dst.sourceRange = ConvertSourceRange(S->getSourceRange());
+
+    /*JOS.attribute("kind", S->getStmtClassName());
+    JOS.attributeObject("range",
+                        [S, this]
+                        {
+                            writeSourceRange(S->getSourceRange());
+                        });*/
+}
+
+void ASTNodeVisitor::ConvertAtomicExprImpl(const AtomicExpr* Src, AST::AtomicExpr& Dst)
+{
+    // TODO: Clang 19
+    // JOS.attribute("name", Src->getOp());
+}
+
+void ASTNodeVisitor::ConvertDeclRefExprImpl(const DeclRefExpr* DRE, AST::DeclRefExpr& Dst)
+{
+    JOS.attribute("referencedDecl", createBareDeclRef(DRE->getDecl()));
+    if (DRE->getDecl() != DRE->getFoundDecl())
+        JOS.attribute("foundReferencedDecl",
+                      createBareDeclRef(DRE->getFoundDecl()));
+    switch (DRE->isNonOdrUse())
+    {
+        case NOUR_None: break;
+        case NOUR_Unevaluated: JOS.attribute("nonOdrUseReason", "unevaluated"); break;
+        case NOUR_Constant: JOS.attribute("nonOdrUseReason", "constant"); break;
+        case NOUR_Discarded: JOS.attribute("nonOdrUseReason", "discarded"); break;
+    }
+    attributeOnlyIfTrue("isImmediateEscalating", DRE->isImmediateEscalating());
+}
+
+void ASTNodeVisitor::ConvertSYCLUniqueStableNameExprImpl(const SYCLUniqueStableNameExpr* E, AST::SYCLUniqueStableNameExpr& Dst)
+{
+    JOS.attribute("typeSourceInfo",
+                  createQualType(E->getTypeSourceInfo()->getType()));
+}
+
+void ASTNodeVisitor::ConvertPredefinedExprImpl(const PredefinedExpr* PE, AST::PredefinedExpr& Dst)
+{
+    JOS.attribute("name", PredefinedExpr::getIdentKindName(PE->getIdentKind()));
+}
+
+void ASTNodeVisitor::ConvertUnaryOperatorImpl(const UnaryOperator* UO, AST::UnaryOperator& Dst)
+{
+    JOS.attribute("isPostfix", UO->isPostfix());
+    JOS.attribute("opcode", UnaryOperator::getOpcodeStr(UO->getOpcode()));
+    if (!UO->canOverflow())
+        JOS.attribute("canOverflow", false);
+}
+
+void ASTNodeVisitor::ConvertBinaryOperatorImpl(const BinaryOperator* BO, AST::BinaryOperator& Dst)
+{
+    JOS.attribute("opcode", BinaryOperator::getOpcodeStr(BO->getOpcode()));
+}
+
+void ASTNodeVisitor::ConvertCompoundAssignOperatorImpl(const CompoundAssignOperator* CAO, AST::CompoundAssignOperator& Dst)
+{
+    VisitBinaryOperator(CAO);
+    JOS.attribute("computeLHSType", createQualType(CAO->getComputationLHSType()));
+    JOS.attribute("computeResultType",
+                  createQualType(CAO->getComputationResultType()));
+}
+
+void ASTNodeVisitor::ConvertMemberExprImpl(const MemberExpr* ME, AST::MemberExpr& Dst)
+{
+    // Note, we always write this Boolean field because the information it conveys
+    // is critical to understanding the AST node.
+    ValueDecl* VD = ME->getMemberDecl();
+    JOS.attribute("name", VD && VD->getDeclName() ? VD->getNameAsString() : "");
+    JOS.attribute("isArrow", ME->isArrow());
+    JOS.attribute("referencedMemberDecl", createPointerRepresentation(VD));
+    switch (ME->isNonOdrUse())
+    {
+        case NOUR_None: break;
+        case NOUR_Unevaluated: JOS.attribute("nonOdrUseReason", "unevaluated"); break;
+        case NOUR_Constant: JOS.attribute("nonOdrUseReason", "constant"); break;
+        case NOUR_Discarded: JOS.attribute("nonOdrUseReason", "discarded"); break;
+    }
+}
+
+void ASTNodeVisitor::ConvertCXXNewExprImpl(const CXXNewExpr* NE, AST::CXXNewExpr& Dst)
+{
+    attributeOnlyIfTrue("isGlobal", NE->isGlobalNew());
+    attributeOnlyIfTrue("isArray", NE->isArray());
+    attributeOnlyIfTrue("isPlacement", NE->getNumPlacementArgs() != 0);
+    switch (NE->getInitializationStyle())
+    {
+        case CXXNewExpr::InitializationStyle::NoInit:
+            break;
+        case CXXNewExpr::InitializationStyle::CallInit: // Parens
+            JOS.attribute("initStyle", "call");
+            break;
+        case CXXNewExpr::InitializationStyle::ListInit: // Braces
+            JOS.attribute("initStyle", "list");
+            break;
+    }
+    if (const FunctionDecl* FD = NE->getOperatorNew())
+        JOS.attribute("operatorNewDecl", createBareDeclRef(FD));
+    if (const FunctionDecl* FD = NE->getOperatorDelete())
+        JOS.attribute("operatorDeleteDecl", createBareDeclRef(FD));
+}
+void ASTNodeVisitor::ConvertCXXDeleteExprImpl(const CXXDeleteExpr* DE, AST::CXXDeleteExpr& Dst)
+{
+    attributeOnlyIfTrue("isGlobal", DE->isGlobalDelete());
+    attributeOnlyIfTrue("isArray", DE->isArrayForm());
+    attributeOnlyIfTrue("isArrayAsWritten", DE->isArrayFormAsWritten());
+    if (const FunctionDecl* FD = DE->getOperatorDelete())
+        JOS.attribute("operatorDeleteDecl", createBareDeclRef(FD));
+}
+
+void ASTNodeVisitor::ConvertCXXThisExprImpl(const CXXThisExpr* TE, AST::CXXThisExpr& Dst)
+{
+    attributeOnlyIfTrue("implicit", TE->isImplicit());
+}
+
+void ASTNodeVisitor::ConvertCastExprImpl(const CastExpr* CE, AST::CastExpr& Dst)
+{
+    JOS.attribute("castKind", CE->getCastKindName());
+    llvm::json::Array Path = createCastPath(CE);
+    if (!Path.empty())
+        JOS.attribute("path", std::move(Path));
+    // FIXME: This may not be useful information as it can be obtusely gleaned
+    // from the inner[] array.
+    if (const NamedDecl* ND = CE->getConversionFunction())
+        JOS.attribute("conversionFunc", createBareDeclRef(ND));
+}
+
+void ASTNodeVisitor::ConvertImplicitCastExprImpl(const ImplicitCastExpr* ICE, AST::ImplicitCastExpr& Dst)
+{
+    VisitCastExpr(ICE);
+    attributeOnlyIfTrue("isPartOfExplicitCast", ICE->isPartOfExplicitCast());
+}
+
+void ASTNodeVisitor::ConvertCallExprImpl(const CallExpr* CE, AST::CallExpr& Dst)
+{
+    attributeOnlyIfTrue("adl", CE->usesADL());
+}
+
+void ASTNodeVisitor::ConvertUnaryExprOrTypeTraitExprImpl(const UnaryExprOrTypeTraitExpr* TTE, AST::UnaryExprOrTypeTraitExpr& Dst)
+{
+    JOS.attribute("name", getTraitSpelling(TTE->getKind()));
+    if (TTE->isArgumentType())
+        JOS.attribute("argType", createQualType(TTE->getArgumentType()));
+}
+
+void ASTNodeVisitor::ConvertSizeOfPackExprImpl(const SizeOfPackExpr* SOPE, AST::SizeOfPackExpr& Dst)
+{
+    VisitNamedDecl(SOPE->getPack());
+}
+
+void ASTNodeVisitor::ConvertUnresolvedLookupExprImpl(const UnresolvedLookupExpr* ULE, AST::UnresolvedLookupExpr& Dst)
+{
+    JOS.attribute("usesADL", ULE->requiresADL());
+    JOS.attribute("name", ULE->getName().getAsString());
+
+    JOS.attributeArray("lookups", [this, ULE]
+                       {
+                           for (const NamedDecl* D : ULE->decls())
+                               JOS.value(createBareDeclRef(D));
+                       });
+}
+
+void ASTNodeVisitor::ConvertAddrLabelExprImpl(const AddrLabelExpr* ALE, AST::AddrLabelExpr& Dst)
+{
+    JOS.attribute("name", ALE->getLabel()->getName());
+    JOS.attribute("labelDeclId", createPointerRepresentation(ALE->getLabel()));
+}
+
+void ASTNodeVisitor::ConvertCXXTypeidExprImpl(const CXXTypeidExpr* CTE, AST::CXXTypeidExpr& Dst)
+{
+    if (CTE->isTypeOperand())
+    {
+        QualType Adjusted = CTE->getTypeOperand(Ctx);
+        QualType Unadjusted = CTE->getTypeOperandSourceInfo()->getType();
+        JOS.attribute("typeArg", createQualType(Unadjusted));
+        if (Adjusted != Unadjusted)
+            JOS.attribute("adjustedTypeArg", createQualType(Adjusted));
+    }
+}
+
+void ASTNodeVisitor::ConvertConstantExprImpl(const ConstantExpr* CE, AST::ConstantExpr& Dst)
+{
+    if (CE->getResultAPValueKind() != APValue::None)
+        Visit(CE->getAPValueResult(), CE->getType());
+}
+
+void ASTNodeVisitor::ConvertInitListExprImpl(const InitListExpr* ILE, AST::InitListExpr& Dst)
+{
+    if (const FieldDecl* FD = ILE->getInitializedFieldInUnion())
+        JOS.attribute("field", createBareDeclRef(FD));
+}
+
+void ASTNodeVisitor::ConvertGenericSelectionExprImpl(const GenericSelectionExpr* GSE, AST::GenericSelectionExpr& Dst)
+{
+    attributeOnlyIfTrue("resultDependent", GSE->isResultDependent());
+}
+
+void ASTNodeVisitor::ConvertCXXUnresolvedConstructExprImpl(const CXXUnresolvedConstructExpr* UCE, AST::CXXUnresolvedConstructExpr& Dst)
+{
+    if (UCE->getType() != UCE->getTypeAsWritten())
+        JOS.attribute("typeAsWritten", createQualType(UCE->getTypeAsWritten()));
+    attributeOnlyIfTrue("list", UCE->isListInitialization());
+}
+
+void ASTNodeVisitor::ConvertCXXConstructExprImpl(const CXXConstructExpr* CE, AST::CXXConstructExpr& Dst)
+{
+    CXXConstructorDecl* Ctor = CE->getConstructor();
+    JOS.attribute("ctorType", createQualType(Ctor->getType()));
+    attributeOnlyIfTrue("elidable", CE->isElidable());
+    attributeOnlyIfTrue("list", CE->isListInitialization());
+    attributeOnlyIfTrue("initializer_list", CE->isStdInitListInitialization());
+    attributeOnlyIfTrue("zeroing", CE->requiresZeroInitialization());
+    attributeOnlyIfTrue("hadMultipleCandidates", CE->hadMultipleCandidates());
+    attributeOnlyIfTrue("isImmediateEscalating", CE->isImmediateEscalating());
+
+    switch (CE->getConstructionKind())
+    {
+        case CXXConstructExpr::ConstructionKind::CK_Complete:
+            JOS.attribute("constructionKind", "complete");
+            break;
+        case CXXConstructExpr::ConstructionKind::CK_Delegating:
+            JOS.attribute("constructionKind", "delegating");
+            break;
+        case CXXConstructExpr::ConstructionKind::CK_NonVirtualBase:
+            JOS.attribute("constructionKind", "non-virtual base");
+            break;
+        case CXXConstructExpr::ConstructionKind::CK_VirtualBase:
+            JOS.attribute("constructionKind", "virtual base");
+            break;
+    }
+}
+
+void ASTNodeVisitor::ConvertExprWithCleanupsImpl(const ExprWithCleanups* EWC, AST::ExprWithCleanups& Dst)
+{
+    attributeOnlyIfTrue("cleanupsHaveSideEffects",
+                        EWC->cleanupsHaveSideEffects());
+    if (EWC->getNumObjects())
+    {
+        JOS.attributeArray("cleanups", [this, EWC]
+                           {
+                               for (const ExprWithCleanups::CleanupObject& CO : EWC->getObjects())
+                                   if (auto* BD = CO.dyn_cast<BlockDecl*>())
+                                   {
+                                       JOS.value(createBareDeclRef(BD));
+                                   }
+                                   else if (auto* CLE = CO.dyn_cast<CompoundLiteralExpr*>())
+                                   {
+                                       llvm::json::Object Obj;
+                                       Obj["id"] = createPointerRepresentation(CLE);
+                                       Obj["kind"] = CLE->getStmtClassName();
+                                       JOS.value(std::move(Obj));
+                                   }
+                                   else
+                                   {
+                                       llvm_unreachable("unexpected cleanup object type");
+                                   }
+                           });
+    }
+}
+
+void ASTNodeVisitor::ConvertCXXBindTemporaryExprImpl(const CXXBindTemporaryExpr* BTE, AST::CXXBindTemporaryExpr& Dst)
+{
+    const CXXTemporary* Temp = BTE->getTemporary();
+    JOS.attribute("temp", createPointerRepresentation(Temp));
+    if (const CXXDestructorDecl* Dtor = Temp->getDestructor())
+        JOS.attribute("dtor", createBareDeclRef(Dtor));
+}
+
+void ASTNodeVisitor::ConvertMaterializeTemporaryExprImpl(const MaterializeTemporaryExpr* MTE, AST::MaterializeTemporaryExpr& Dst)
+{
+    if (const ValueDecl* VD = MTE->getExtendingDecl())
+        JOS.attribute("extendingDecl", createBareDeclRef(VD));
+
+    switch (MTE->getStorageDuration())
+    {
+        case SD_Automatic:
+            JOS.attribute("storageDuration", "automatic");
+            break;
+        case SD_Dynamic:
+            JOS.attribute("storageDuration", "dynamic");
+            break;
+        case SD_FullExpression:
+            JOS.attribute("storageDuration", "full expression");
+            break;
+        case SD_Static:
+            JOS.attribute("storageDuration", "static");
+            break;
+        case SD_Thread:
+            JOS.attribute("storageDuration", "thread");
+            break;
+    }
+
+    attributeOnlyIfTrue("boundToLValueRef", MTE->isBoundToLvalueReference());
+}
+
+void ASTNodeVisitor::ConvertCXXDefaultArgExprImpl(const CXXDefaultArgExpr* Node, AST::CXXDefaultArgExpr& Dst)
+{
+    attributeOnlyIfTrue("hasRewrittenInit", Node->hasRewrittenInit());
+}
+
+void ASTNodeVisitor::ConvertCXXDefaultInitExprImpl(const CXXDefaultInitExpr* Node, AST::CXXDefaultInitExpr& Dst)
+{
+    attributeOnlyIfTrue("hasRewrittenInit", Node->hasRewrittenInit());
+}
+
+void ASTNodeVisitor::ConvertCXXDependentScopeMemberExprImpl(const CXXDependentScopeMemberExpr* DSME, AST::CXXDependentScopeMemberExpr& Dst)
+{
+    JOS.attribute("isArrow", DSME->isArrow());
+    JOS.attribute("member", DSME->getMember().getAsString());
+    attributeOnlyIfTrue("hasTemplateKeyword", DSME->hasTemplateKeyword());
+    attributeOnlyIfTrue("hasExplicitTemplateArgs",
+                        DSME->hasExplicitTemplateArgs());
+
+    if (DSME->getNumTemplateArgs())
+    {
+        JOS.attributeArray("explicitTemplateArgs", [DSME, this]
+                           {
+                               for (const TemplateArgumentLoc& TAL : DSME->template_arguments())
+                                   JOS.object(
+                                       [&TAL, this]
+                                       {
+                                           Visit(TAL.getArgument(), TAL.getSourceRange());
+                                       });
+                           });
+    }
+}
+
+void ASTNodeVisitor::ConvertIntegerLiteralImpl(const IntegerLiteral* IL, AST::IntegerLiteral& Dst)
+{
+    llvm::SmallString<16> Buffer;
+    IL->getValue().toString(Buffer,
+                            /*Radix=*/10, IL->getType()->isSignedIntegerType());
+    JOS.attribute("value", Buffer);
+}
+void ASTNodeVisitor::ConvertCharacterLiteralImpl(const CharacterLiteral* CL, AST::CharacterLiteral& Dst)
+{
+    // FIXME: This should probably print the character literal as a string,
+    // rather than as a numerical value. It would be nice if the behavior matched
+    // what we do to print a string literal; right now, it is impossible to tell
+    // the difference between 'a' and L'a' in C from the JSON output.
+    JOS.attribute("value", CL->getValue());
+}
+void ASTNodeVisitor::ConvertFixedPointLiteralImpl(const FixedPointLiteral* FPL, AST::FixedPointLiteral& Dst)
+{
+    JOS.attribute("value", FPL->getValueAsString(/*Radix=*/10));
+}
+void ASTNodeVisitor::ConvertFloatingLiteralImpl(const FloatingLiteral* FL, AST::FloatingLiteral& Dst)
+{
+    llvm::SmallString<16> Buffer;
+    FL->getValue().toString(Buffer);
+    JOS.attribute("value", Buffer);
+}
+void ASTNodeVisitor::ConvertStringLiteralImpl(const StringLiteral* SL, AST::StringLiteral& Dst)
+{
+    std::string Buffer;
+    llvm::raw_string_ostream SS(Buffer);
+    SL->outputString(SS);
+    JOS.attribute("value", Buffer);
+}
+void ASTNodeVisitor::ConvertCXXBoolLiteralExprImpl(const CXXBoolLiteralExpr* BLE, AST::CXXBoolLiteralExpr& Dst)
+{
+    JOS.attribute("value", BLE->getValue());
+}
+
+void ASTNodeVisitor::ConvertIfStmtImpl(const IfStmt* IS, AST::IfStmt& Dst)
+{
+    attributeOnlyIfTrue("hasInit", IS->hasInitStorage());
+    attributeOnlyIfTrue("hasVar", IS->hasVarStorage());
+    attributeOnlyIfTrue("hasElse", IS->hasElseStorage());
+    attributeOnlyIfTrue("isConstexpr", IS->isConstexpr());
+    attributeOnlyIfTrue("isConsteval", IS->isConsteval());
+    attributeOnlyIfTrue("constevalIsNegated", IS->isNegatedConsteval());
+}
+
+void ASTNodeVisitor::ConvertSwitchStmtImpl(const SwitchStmt* SS, AST::SwitchStmt& Dst)
+{
+    attributeOnlyIfTrue("hasInit", SS->hasInitStorage());
+    attributeOnlyIfTrue("hasVar", SS->hasVarStorage());
+}
+void ASTNodeVisitor::ConvertCaseStmtImpl(const CaseStmt* CS, AST::CaseStmt& Dst)
+{
+    attributeOnlyIfTrue("isGNURange", CS->caseStmtIsGNURange());
+}
+
+void ASTNodeVisitor::ConvertLabelStmtImpl(const LabelStmt* LS, AST::LabelStmt& Dst)
+{
+    JOS.attribute("name", LS->getName());
+    JOS.attribute("declId", createPointerRepresentation(LS->getDecl()));
+    attributeOnlyIfTrue("sideEntry", LS->isSideEntry());
+}
+void ASTNodeVisitor::ConvertGotoStmtImpl(const GotoStmt* GS, AST::GotoStmt& Dst)
+{
+    JOS.attribute("targetLabelDeclId",
+                  createPointerRepresentation(GS->getLabel()));
+}
+
+void ASTNodeVisitor::ConvertWhileStmtImpl(const WhileStmt* WS, AST::WhileStmt& Dst)
+{
+    attributeOnlyIfTrue("hasVar", WS->hasVarStorage());
+}
+
+void ASTNodeVisitor::ConvertAsmStmtImpl(class clang::AsmStmt const*, class CppSharp::CppParser::AST::AsmStmt&) {}
+void ASTNodeVisitor::ConvertGCCAsmStmtImpl(class clang::GCCAsmStmt const*, class CppSharp::CppParser::AST::GCCAsmStmt&) {}
+void ASTNodeVisitor::ConvertMSAsmStmtImpl(class clang::MSAsmStmt const*, class CppSharp::CppParser::AST::MSAsmStmt&) {}
+void ASTNodeVisitor::ConvertBreakStmtImpl(class clang::BreakStmt const*, class CppSharp::CppParser::AST::BreakStmt&) {}
+void ASTNodeVisitor::ConvertCXXCatchStmtImpl(class clang::CXXCatchStmt const*, class CppSharp::CppParser::AST::CXXCatchStmt&) {}
+void ASTNodeVisitor::ConvertCXXForRangeStmtImpl(class clang::CXXForRangeStmt const*, class CppSharp::CppParser::AST::CXXForRangeStmt&) {}
+void ASTNodeVisitor::ConvertCXXTryStmtImpl(class clang::CXXTryStmt const*, class CppSharp::CppParser::AST::CXXTryStmt&) {}
+void ASTNodeVisitor::ConvertCapturedStmtImpl(class clang::CapturedStmt const*, class CppSharp::CppParser::AST::CapturedStmt&) {}
+void ASTNodeVisitor::ConvertContinueStmtImpl(class clang::ContinueStmt const*, class CppSharp::CppParser::AST::ContinueStmt&) {}
+void ASTNodeVisitor::ConvertCoreturnStmtImpl(class clang::CoreturnStmt const*, class CppSharp::CppParser::AST::CoreturnStmt&) {}
+void ASTNodeVisitor::ConvertCoroutineBodyStmtImpl(class clang::CoroutineBodyStmt const*, class CppSharp::CppParser::AST::CoroutineBodyStmt&) {}
+void ASTNodeVisitor::ConvertDeclStmtImpl(class clang::DeclStmt const*, class CppSharp::CppParser::AST::DeclStmt&) {}
+void ASTNodeVisitor::ConvertDoStmtImpl(class clang::DoStmt const*, class CppSharp::CppParser::AST::DoStmt&) {}
+void ASTNodeVisitor::ConvertForStmtImpl(class clang::ForStmt const*, class CppSharp::CppParser::AST::ForStmt&) {}
+void ASTNodeVisitor::ConvertIndirectGotoStmtImpl(class clang::IndirectGotoStmt const*, class CppSharp::CppParser::AST::IndirectGotoStmt&) {}
+void ASTNodeVisitor::ConvertMSDependentExistsStmtImpl(class clang::MSDependentExistsStmt const*, class CppSharp::CppParser::AST::MSDependentExistsStmt&) {}
+void ASTNodeVisitor::ConvertNullStmtImpl(class clang::NullStmt const*, class CppSharp::CppParser::AST::NullStmt&) {}
+void ASTNodeVisitor::ConvertReturnStmtImpl(class clang::ReturnStmt const*, class CppSharp::CppParser::AST::ReturnStmt&) {}
+void ASTNodeVisitor::ConvertSEHExceptStmtImpl(class clang::SEHExceptStmt const*, class CppSharp::CppParser::AST::SEHExceptStmt&) {}
+void ASTNodeVisitor::ConvertSEHFinallyStmtImpl(class clang::SEHFinallyStmt const*, class CppSharp::CppParser::AST::SEHFinallyStmt&) {}
+void ASTNodeVisitor::ConvertSEHLeaveStmtImpl(class clang::SEHLeaveStmt const*, class CppSharp::CppParser::AST::SEHLeaveStmt&) {}
+void ASTNodeVisitor::ConvertSEHTryStmtImpl(class clang::SEHTryStmt const*, class CppSharp::CppParser::AST::SEHTryStmt&) {}
+void ASTNodeVisitor::ConvertSwitchCaseImpl(class clang::SwitchCase const*, class CppSharp::CppParser::AST::SwitchCase&) {}
+void ASTNodeVisitor::ConvertDefaultStmtImpl(class clang::DefaultStmt const*, class CppSharp::CppParser::AST::DefaultStmt&) {}
+void ASTNodeVisitor::ConvertValueStmtImpl(class clang::ValueStmt const*, class CppSharp::CppParser::AST::ValueStmt&) {}
+void ASTNodeVisitor::ConvertAttributedStmtImpl(class clang::AttributedStmt const*, class CppSharp::CppParser::AST::AttributedStmt&) {}
+void ASTNodeVisitor::ConvertAbstractConditionalOperatorImpl(class clang::AbstractConditionalOperator const*, class CppSharp::CppParser::AST::AbstractConditionalOperator&) {}
+void ASTNodeVisitor::ConvertBinaryConditionalOperatorImpl(class clang::BinaryConditionalOperator const*, class CppSharp::CppParser::AST::BinaryConditionalOperator&) {}
+void ASTNodeVisitor::ConvertConditionalOperatorImpl(class clang::ConditionalOperator const*, class CppSharp::CppParser::AST::ConditionalOperator&) {}
+void ASTNodeVisitor::ConvertArrayInitIndexExprImpl(class clang::ArrayInitIndexExpr const*, class CppSharp::CppParser::AST::ArrayInitIndexExpr&) {}
+void ASTNodeVisitor::ConvertArrayInitLoopExprImpl(class clang::ArrayInitLoopExpr const*, class CppSharp::CppParser::AST::ArrayInitLoopExpr&) {}
+void ASTNodeVisitor::ConvertArraySubscriptExprImpl(class clang::ArraySubscriptExpr const*, class CppSharp::CppParser::AST::ArraySubscriptExpr&) {}
+void ASTNodeVisitor::ConvertArrayTypeTraitExprImpl(class clang::ArrayTypeTraitExpr const*, class CppSharp::CppParser::AST::ArrayTypeTraitExpr&) {}
+void ASTNodeVisitor::ConvertAsTypeExprImpl(class clang::AsTypeExpr const*, class CppSharp::CppParser::AST::AsTypeExpr&) {}
+void ASTNodeVisitor::ConvertBlockExprImpl(class clang::BlockExpr const*, class CppSharp::CppParser::AST::BlockExpr&) {}
+void ASTNodeVisitor::ConvertCXXTemporaryObjectExprImpl(class clang::CXXTemporaryObjectExpr const*, class CppSharp::CppParser::AST::CXXTemporaryObjectExpr&) {}
+void ASTNodeVisitor::ConvertCXXFoldExprImpl(class clang::CXXFoldExpr const*, class CppSharp::CppParser::AST::CXXFoldExpr&) {}
+void ASTNodeVisitor::ConvertCXXInheritedCtorInitExprImpl(class clang::CXXInheritedCtorInitExpr const*, class CppSharp::CppParser::AST::CXXInheritedCtorInitExpr&) {}
+void ASTNodeVisitor::ConvertCXXNoexceptExprImpl(class clang::CXXNoexceptExpr const*, class CppSharp::CppParser::AST::CXXNoexceptExpr&) {}
+void ASTNodeVisitor::ConvertCXXNullPtrLiteralExprImpl(class clang::CXXNullPtrLiteralExpr const*, class CppSharp::CppParser::AST::CXXNullPtrLiteralExpr&) {}
+void ASTNodeVisitor::ConvertCXXParenListInitExprImpl(class clang::CXXParenListInitExpr const*, class CppSharp::CppParser::AST::CXXParenListInitExpr&) {}
+void ASTNodeVisitor::ConvertCXXPseudoDestructorExprImpl(class clang::CXXPseudoDestructorExpr const*, class CppSharp::CppParser::AST::CXXPseudoDestructorExpr&) {}
+void ASTNodeVisitor::ConvertCXXRewrittenBinaryOperatorImpl(class clang::CXXRewrittenBinaryOperator const*, class CppSharp::CppParser::AST::CXXRewrittenBinaryOperator&) {}
+void ASTNodeVisitor::ConvertCXXScalarValueInitExprImpl(class clang::CXXScalarValueInitExpr const*, class CppSharp::CppParser::AST::CXXScalarValueInitExpr&) {}
+void ASTNodeVisitor::ConvertCXXStdInitializerListExprImpl(class clang::CXXStdInitializerListExpr const*, class CppSharp::CppParser::AST::CXXStdInitializerListExpr&) {}
+void ASTNodeVisitor::ConvertCXXThrowExprImpl(class clang::CXXThrowExpr const*, class CppSharp::CppParser::AST::CXXThrowExpr&) {}
+void ASTNodeVisitor::ConvertCXXUuidofExprImpl(class clang::CXXUuidofExpr const*, class CppSharp::CppParser::AST::CXXUuidofExpr&) {}
+void ASTNodeVisitor::ConvertCUDAKernelCallExprImpl(class clang::CUDAKernelCallExpr const*, class CppSharp::CppParser::AST::CUDAKernelCallExpr&) {}
+void ASTNodeVisitor::ConvertCXXMemberCallExprImpl(class clang::CXXMemberCallExpr const*, class CppSharp::CppParser::AST::CXXMemberCallExpr&) {}
+void ASTNodeVisitor::ConvertCXXOperatorCallExprImpl(class clang::CXXOperatorCallExpr const*, class CppSharp::CppParser::AST::CXXOperatorCallExpr&) {}
+void ASTNodeVisitor::ConvertUserDefinedLiteralImpl(class clang::UserDefinedLiteral const*, class CppSharp::CppParser::AST::UserDefinedLiteral&) {}
+void ASTNodeVisitor::ConvertExplicitCastExprImpl(class clang::ExplicitCastExpr const*, class CppSharp::CppParser::AST::ExplicitCastExpr&) {}
+void ASTNodeVisitor::ConvertBuiltinBitCastExprImpl(class clang::BuiltinBitCastExpr const*, class CppSharp::CppParser::AST::BuiltinBitCastExpr&) {}
+void ASTNodeVisitor::ConvertCStyleCastExprImpl(class clang::CStyleCastExpr const*, class CppSharp::CppParser::AST::CStyleCastExpr&) {}
+void ASTNodeVisitor::ConvertCXXFunctionalCastExprImpl(class clang::CXXFunctionalCastExpr const*, class CppSharp::CppParser::AST::CXXFunctionalCastExpr&) {}
+void ASTNodeVisitor::ConvertCXXNamedCastExprImpl(class clang::CXXNamedCastExpr const*, class CppSharp::CppParser::AST::CXXNamedCastExpr&) {}
+void ASTNodeVisitor::ConvertCXXAddrspaceCastExprImpl(class clang::CXXAddrspaceCastExpr const*, class CppSharp::CppParser::AST::CXXAddrspaceCastExpr&) {}
+void ASTNodeVisitor::ConvertCXXConstCastExprImpl(class clang::CXXConstCastExpr const*, class CppSharp::CppParser::AST::CXXConstCastExpr&) {}
+void ASTNodeVisitor::ConvertCXXDynamicCastExprImpl(class clang::CXXDynamicCastExpr const*, class CppSharp::CppParser::AST::CXXDynamicCastExpr&) {}
+void ASTNodeVisitor::ConvertCXXReinterpretCastExprImpl(class clang::CXXReinterpretCastExpr const*, class CppSharp::CppParser::AST::CXXReinterpretCastExpr&) {}
+void ASTNodeVisitor::ConvertCXXStaticCastExprImpl(class clang::CXXStaticCastExpr const*, class CppSharp::CppParser::AST::CXXStaticCastExpr&) {}
+void ASTNodeVisitor::ConvertChooseExprImpl(class clang::ChooseExpr const*, class CppSharp::CppParser::AST::ChooseExpr&) {}
+void ASTNodeVisitor::ConvertCompoundLiteralExprImpl(class clang::CompoundLiteralExpr const*, class CppSharp::CppParser::AST::CompoundLiteralExpr&) {}
+void ASTNodeVisitor::ConvertConvertVectorExprImpl(class clang::ConvertVectorExpr const*, class CppSharp::CppParser::AST::ConvertVectorExpr&) {}
+void ASTNodeVisitor::ConvertCoroutineSuspendExprImpl(class clang::CoroutineSuspendExpr const*, class CppSharp::CppParser::AST::CoroutineSuspendExpr&) {}
+void ASTNodeVisitor::ConvertCoawaitExprImpl(class clang::CoawaitExpr const*, class CppSharp::CppParser::AST::CoawaitExpr&) {}
+void ASTNodeVisitor::ConvertCoyieldExprImpl(class clang::CoyieldExpr const*, class CppSharp::CppParser::AST::CoyieldExpr&) {}
+void ASTNodeVisitor::ConvertDependentCoawaitExprImpl(class clang::DependentCoawaitExpr const*, class CppSharp::CppParser::AST::DependentCoawaitExpr&) {}
+void ASTNodeVisitor::ConvertDependentScopeDeclRefExprImpl(class clang::DependentScopeDeclRefExpr const*, class CppSharp::CppParser::AST::DependentScopeDeclRefExpr&) {}
+void ASTNodeVisitor::ConvertDesignatedInitExprImpl(class clang::DesignatedInitExpr const*, class CppSharp::CppParser::AST::DesignatedInitExpr&) {}
+void ASTNodeVisitor::ConvertDesignatedInitUpdateExprImpl(class clang::DesignatedInitUpdateExpr const*, class CppSharp::CppParser::AST::DesignatedInitUpdateExpr&) {}
+void ASTNodeVisitor::ConvertExpressionTraitExprImpl(class clang::ExpressionTraitExpr const*, class CppSharp::CppParser::AST::ExpressionTraitExpr&) {}
+void ASTNodeVisitor::ConvertExtVectorElementExprImpl(class clang::ExtVectorElementExpr const*, class CppSharp::CppParser::AST::ExtVectorElementExpr&) {}
+void ASTNodeVisitor::ConvertFullExprImpl(class clang::FullExpr const*, class CppSharp::CppParser::AST::FullExpr&) {}
+void ASTNodeVisitor::ConvertFunctionParmPackExprImpl(class clang::FunctionParmPackExpr const*, class CppSharp::CppParser::AST::FunctionParmPackExpr&) {}
+void ASTNodeVisitor::ConvertGNUNullExprImpl(class clang::GNUNullExpr const*, class CppSharp::CppParser::AST::GNUNullExpr&) {}
+void ASTNodeVisitor::ConvertImaginaryLiteralImpl(class clang::ImaginaryLiteral const*, class CppSharp::CppParser::AST::ImaginaryLiteral&) {}
+void ASTNodeVisitor::ConvertImplicitValueInitExprImpl(class clang::ImplicitValueInitExpr const*, class CppSharp::CppParser::AST::ImplicitValueInitExpr&) {}
+void ASTNodeVisitor::ConvertLambdaExprImpl(class clang::LambdaExpr const*, class CppSharp::CppParser::AST::LambdaExpr&) {}
+void ASTNodeVisitor::ConvertMSPropertyRefExprImpl(class clang::MSPropertyRefExpr const*, class CppSharp::CppParser::AST::MSPropertyRefExpr&) {}
+void ASTNodeVisitor::ConvertMSPropertySubscriptExprImpl(class clang::MSPropertySubscriptExpr const*, class CppSharp::CppParser::AST::MSPropertySubscriptExpr&) {}
+void ASTNodeVisitor::ConvertMatrixSubscriptExprImpl(class clang::MatrixSubscriptExpr const*, class CppSharp::CppParser::AST::MatrixSubscriptExpr&) {}
+void ASTNodeVisitor::ConvertNoInitExprImpl(class clang::NoInitExpr const*, class CppSharp::CppParser::AST::NoInitExpr&) {}
+void ASTNodeVisitor::ConvertOffsetOfExprImpl(class clang::OffsetOfExpr const*, class CppSharp::CppParser::AST::OffsetOfExpr&) {}
+void ASTNodeVisitor::ConvertOpaqueValueExprImpl(class clang::OpaqueValueExpr const*, class CppSharp::CppParser::AST::OpaqueValueExpr&) {}
+void ASTNodeVisitor::ConvertOverloadExprImpl(class clang::OverloadExpr const*, class CppSharp::CppParser::AST::OverloadExpr&) {}
+void ASTNodeVisitor::ConvertUnresolvedMemberExprImpl(class clang::UnresolvedMemberExpr const*, class CppSharp::CppParser::AST::UnresolvedMemberExpr&) {}
+void ASTNodeVisitor::ConvertPackExpansionExprImpl(class clang::PackExpansionExpr const*, class CppSharp::CppParser::AST::PackExpansionExpr&) {}
+void ASTNodeVisitor::ConvertParenExprImpl(class clang::ParenExpr const*, class CppSharp::CppParser::AST::ParenExpr&) {}
+void ASTNodeVisitor::ConvertParenListExprImpl(class clang::ParenListExpr const*, class CppSharp::CppParser::AST::ParenListExpr&) {}
+void ASTNodeVisitor::ConvertPseudoObjectExprImpl(class clang::PseudoObjectExpr const*, class CppSharp::CppParser::AST::PseudoObjectExpr&) {}
+void ASTNodeVisitor::ConvertRecoveryExprImpl(class clang::RecoveryExpr const*, class CppSharp::CppParser::AST::RecoveryExpr&) {}
+void ASTNodeVisitor::ConvertShuffleVectorExprImpl(class clang::ShuffleVectorExpr const*, class CppSharp::CppParser::AST::ShuffleVectorExpr&) {}
+void ASTNodeVisitor::ConvertSourceLocExprImpl(class clang::SourceLocExpr const*, class CppSharp::CppParser::AST::SourceLocExpr&) {}
+void ASTNodeVisitor::ConvertStmtExprImpl(class clang::StmtExpr const*, class CppSharp::CppParser::AST::StmtExpr&) {}
+void ASTNodeVisitor::ConvertSubstNonTypeTemplateParmExprImpl(class clang::SubstNonTypeTemplateParmExpr const*, class CppSharp::CppParser::AST::SubstNonTypeTemplateParmExpr&) {}
+void ASTNodeVisitor::ConvertSubstNonTypeTemplateParmPackExprImpl(class clang::SubstNonTypeTemplateParmPackExpr const*, class CppSharp::CppParser::AST::SubstNonTypeTemplateParmPackExpr&) {}
+void ASTNodeVisitor::ConvertTypeTraitExprImpl(class clang::TypeTraitExpr const*, class CppSharp::CppParser::AST::TypeTraitExpr&) {}
+void ASTNodeVisitor::ConvertTypoExprImpl(class clang::TypoExpr const*, class CppSharp::CppParser::AST::TypoExpr&) {}
+void ASTNodeVisitor::ConvertVAArgExprImpl(class clang::VAArgExpr const*, class CppSharp::CppParser::AST::VAArgExpr&) {}
+
+
+void ASTNodeVisitor::VisitNullTemplateArgument(const TemplateArgument& TA)
+{
+    JOS.attribute("isNull", true);
+}
+void ASTNodeVisitor::VisitTypeTemplateArgument(const TemplateArgument& TA)
+{
+    JOS.attribute("type", createQualType(TA.getAsType()));
+}
+void ASTNodeVisitor::VisitDeclarationTemplateArgument(
+    const TemplateArgument& TA)
+{
+    JOS.attribute("decl", createBareDeclRef(TA.getAsDecl()));
+}
+void ASTNodeVisitor::VisitNullPtrTemplateArgument(const TemplateArgument& TA)
+{
+    JOS.attribute("isNullptr", true);
+}
+void ASTNodeVisitor::VisitIntegralTemplateArgument(const TemplateArgument& TA)
+{
+    JOS.attribute("value", TA.getAsIntegral().getSExtValue());
+}
+void ASTNodeVisitor::VisitTemplateTemplateArgument(const TemplateArgument& TA)
+{
+    // FIXME: cannot just call dump() on the argument, as that doesn't specify
+    // the output format.
+}
+void ASTNodeVisitor::VisitTemplateExpansionTemplateArgument(
+    const TemplateArgument& TA)
+{
+    // FIXME: cannot just call dump() on the argument, as that doesn't specify
+    // the output format.
+}
+void ASTNodeVisitor::VisitExpressionTemplateArgument(
+    const TemplateArgument& TA)
+{
+    JOS.attribute("isExpr", true);
+}
+void ASTNodeVisitor::VisitPackTemplateArgument(const TemplateArgument& TA)
+{
+    JOS.attribute("isPack", true);
+}
+
+StringRef ASTNodeVisitor::getCommentCommandName(unsigned CommandID) const
+{
+    return Traits.getCommandInfo(CommandID)->Name;
+}
+
+void ASTNodeVisitor::visitTextComment(const comments::TextComment* C,
+                                      const comments::FullComment*)
+{
+    JOS.attribute("text", C->getText());
+}
+
+void ASTNodeVisitor::visitInlineCommandComment(
+    const comments::InlineCommandComment* C,
+    const comments::FullComment*)
+{
+    JOS.attribute("name", getCommentCommandName(C->getCommandID()));
+
+    switch (C->getRenderKind())
+    {
+        case comments::InlineCommandComment::RenderKind::RenderNormal:
+            JOS.attribute("renderKind", "normal");
+            break;
+        case comments::InlineCommandComment::RenderKind::RenderBold:
+            JOS.attribute("renderKind", "bold");
+            break;
+        case comments::InlineCommandComment::RenderKind::RenderEmphasized:
+            JOS.attribute("renderKind", "emphasized");
+            break;
+        case comments::InlineCommandComment::RenderKind::RenderMonospaced:
+            JOS.attribute("renderKind", "monospaced");
+            break;
+        case comments::InlineCommandComment::RenderKind::RenderAnchor:
+            JOS.attribute("renderKind", "anchor");
+            break;
+    }
+
+    llvm::json::Array Args;
+    for (unsigned I = 0, E = C->getNumArgs(); I < E; ++I)
+        Args.push_back(C->getArgText(I));
+
+    if (!Args.empty())
+        JOS.attribute("args", std::move(Args));
+}
+
+void ASTNodeVisitor::visitHTMLStartTagComment(
+    const comments::HTMLStartTagComment* C,
+    const comments::FullComment*)
+{
+    JOS.attribute("name", C->getTagName());
+    attributeOnlyIfTrue("selfClosing", C->isSelfClosing());
+    attributeOnlyIfTrue("malformed", C->isMalformed());
+
+    llvm::json::Array Attrs;
+    for (unsigned I = 0, E = C->getNumAttrs(); I < E; ++I)
+        Attrs.push_back(
+            { { "name", C->getAttr(I).Name }, { "value", C->getAttr(I).Value } });
+
+    if (!Attrs.empty())
+        JOS.attribute("attrs", std::move(Attrs));
+}
+
+void ASTNodeVisitor::visitHTMLEndTagComment(
+    const comments::HTMLEndTagComment* C,
+    const comments::FullComment*)
+{
+    JOS.attribute("name", C->getTagName());
+}
+
+void ASTNodeVisitor::visitBlockCommandComment(
+    const comments::BlockCommandComment* C,
+    const comments::FullComment*)
+{
+    JOS.attribute("name", getCommentCommandName(C->getCommandID()));
+
+    llvm::json::Array Args;
+    for (unsigned I = 0, E = C->getNumArgs(); I < E; ++I)
+        Args.push_back(C->getArgText(I));
+
+    if (!Args.empty())
+        JOS.attribute("args", std::move(Args));
+}
+
+void ASTNodeVisitor::visitParamCommandComment(
+    const comments::ParamCommandComment* C,
+    const comments::FullComment* FC)
+{
+    switch (C->getDirection())
+    {
+        case comments::ParamCommandComment::PassDirection::In:
+            JOS.attribute("direction", "in");
+            break;
+        case comments::ParamCommandComment::PassDirection::Out:
+            JOS.attribute("direction", "out");
+            break;
+        case comments::ParamCommandComment::PassDirection::InOut:
+            JOS.attribute("direction", "in,out");
+            break;
+    }
+    attributeOnlyIfTrue("explicit", C->isDirectionExplicit());
+
+    if (C->hasParamName())
+        JOS.attribute("param", C->isParamIndexValid() ? C->getParamName(FC) : C->getParamNameAsWritten());
+
+    if (C->isParamIndexValid() && !C->isVarArgParam())
+        JOS.attribute("paramIdx", C->getParamIndex());
+}
+
+void ASTNodeVisitor::visitTParamCommandComment(
+    const comments::TParamCommandComment* C,
+    const comments::FullComment* FC)
+{
+    if (C->hasParamName())
+        JOS.attribute("param", C->isPositionValid() ? C->getParamName(FC) : C->getParamNameAsWritten());
+    if (C->isPositionValid())
+    {
+        llvm::json::Array Positions;
+        for (unsigned I = 0, E = C->getDepth(); I < E; ++I)
+            Positions.push_back(C->getIndex(I));
+
+        if (!Positions.empty())
+            JOS.attribute("positions", std::move(Positions));
+    }
+}
+
+void ASTNodeVisitor::visitVerbatimBlockComment(
+    const comments::VerbatimBlockComment* C,
+    const comments::FullComment*)
+{
+    JOS.attribute("name", getCommentCommandName(C->getCommandID()));
+    JOS.attribute("closeName", C->getCloseName());
+}
+
+void ASTNodeVisitor::visitVerbatimBlockLineComment(
+    const comments::VerbatimBlockLineComment* C,
+    const comments::FullComment*)
+{
+    JOS.attribute("text", C->getText());
+}
+
+void ASTNodeVisitor::visitVerbatimLineComment(
+    const comments::VerbatimLineComment* C,
+    const comments::FullComment*)
+{
+    JOS.attribute("text", C->getText());
+}
+
+llvm::json::Object ASTNodeVisitor::createFPOptions(FPOptionsOverride FPO)
+{
+    llvm::json::Object Ret;
+#define OPTION(NAME, TYPE, WIDTH, PREVIOUS) \
+    if (FPO.has##NAME##Override())          \
+        Ret.try_emplace(#NAME, static_cast<unsigned>(FPO.get##NAME##Override()));
+#include "clang/Basic/FPOptions.def"
+
+    return Ret;
+}
+
+void ASTNodeVisitor::ConvertCompoundStmtImpl(const CompoundStmt* S, AST::CompoundStmt& Dst)
+{
+    VisitStmt(S);
+    if (S->hasStoredFPFeatures())
+        JOS.attribute("fpoptions", createFPOptions(S->getStoredFPFeatures()));
+}
\ No newline at end of file
diff --git a/src/CppParser/ASTNodeVisitor.h b/src/CppParser/ASTNodeVisitor.h
new file mode 100644
index 00000000..4a38eef8
--- /dev/null
+++ b/src/CppParser/ASTNodeVisitor.h
@@ -0,0 +1,541 @@
+/************************************************************************
+ *
+ * CppSharp
+ * Licensed under the simplified BSD license. All rights reserved.
+ *
+ ************************************************************************/
+
+#pragma once
+#include <llvm/Support/JSON.h>
+
+#include "ASTNameMangler.h"
+#include "Types.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/ASTDumperUtils.h"
+#include "clang/AST/ASTNodeTraverser.h"
+#include "clang/AST/RecursiveASTVisitor.h"
+#include "clang/AST/AttrVisitor.h"
+#include "clang/AST/CommentCommandTraits.h"
+#include "clang/AST/CommentVisitor.h"
+#include "clang/AST/ExprConcepts.h"
+#include "clang/AST/DeclCXX.h"
+#include "clang/AST/ExprCXX.h"
+#include "clang/AST/Type.h"
+
+#include <unordered_set>
+
+#include "Decl.h"
+#include "Expr.h"
+#include "Stmt.h"
+
+namespace CppSharp::CppParser {
+namespace AST {
+    class Stmt;
+} // namespace AST
+class Parser;
+
+class NodeStreamer
+{
+    bool FirstChild = true;
+    bool TopLevel = true;
+    llvm::SmallVector<std::function<void(bool IsLastChild)>, 32> Pending;
+
+protected:
+    llvm::json::OStream JOS;
+
+public:
+    /// Add a child of the current node.  Calls DoAddChild without arguments
+    template <typename Fn>
+    void AddChild(Fn DoAddChild)
+    {
+        return AddChild("", DoAddChild);
+    }
+
+    /// Add a child of the current node with an optional label.
+    /// Calls DoAddChild without arguments.
+    template <typename Fn>
+    void AddChild(llvm::StringRef Label, Fn DoAddChild)
+    {
+        // If we're at the top level, there's nothing interesting to do; just
+        // run the dumper.
+        if (TopLevel)
+        {
+            TopLevel = false;
+            JOS.objectBegin();
+
+            DoAddChild();
+
+            while (!Pending.empty())
+            {
+                Pending.back()(true);
+                Pending.pop_back();
+            }
+
+            JOS.objectEnd();
+            TopLevel = true;
+            return;
+        }
+
+        // We need to capture an owning-string in the lambda because the lambda
+        // is invoked in a deferred manner.
+        std::string LabelStr(!Label.empty() ? Label : "inner");
+        bool WasFirstChild = FirstChild;
+        auto DumpWithIndent = [=](bool IsLastChild)
+        {
+            if (WasFirstChild)
+            {
+                JOS.attributeBegin(LabelStr);
+                JOS.arrayBegin();
+            }
+
+            FirstChild = true;
+            unsigned Depth = Pending.size();
+            JOS.objectBegin();
+
+            DoAddChild();
+
+            // If any children are left, they're the last at their nesting level.
+            // Dump those ones out now.
+            while (Depth < Pending.size())
+            {
+                Pending.back()(true);
+                this->Pending.pop_back();
+            }
+
+            JOS.objectEnd();
+
+            if (IsLastChild)
+            {
+                JOS.arrayEnd();
+                JOS.attributeEnd();
+            }
+        };
+
+        if (FirstChild)
+        {
+            Pending.push_back(std::move(DumpWithIndent));
+        }
+        else
+        {
+            Pending.back()(false);
+            Pending.back() = std::move(DumpWithIndent);
+        }
+        FirstChild = false;
+    }
+
+    NodeStreamer(llvm::raw_ostream& OS)
+        : JOS(OS, 2)
+    {
+    }
+};
+
+// Dumps AST nodes in JSON format. There is no implied stability for the
+// content or format of the dump between major releases of Clang, other than it
+// being valid JSON output. Further, there is no requirement that the
+// information dumped is a complete representation of the AST, only that the
+// information presented is correct.
+class ASTNodeVisitor
+    : public clang::ConstAttrVisitor<ASTNodeVisitor>,
+      public clang::comments::ConstCommentVisitor<ASTNodeVisitor, void, const clang::comments::FullComment*>,
+      public clang::ConstTemplateArgumentVisitor<ASTNodeVisitor>,
+      public clang::ConstStmtVisitor<ASTNodeVisitor>,
+      public clang::TypeVisitor<ASTNodeVisitor>,
+      public clang::ConstDeclVisitor<ASTNodeVisitor, AST::Declaration*>,
+      public NodeStreamer
+{
+    friend class ASTNodeDumper;
+
+    using InnerAttrVisitor = ConstAttrVisitor;
+    using InnerCommentVisitor = ConstCommentVisitor;
+    using InnerTemplateArgVisitor = ConstTemplateArgumentVisitor;
+    using InnerStmtVisitor = ConstStmtVisitor;
+    using InnerTypeVisitor = TypeVisitor;
+    using InnerDeclVisitor = ConstDeclVisitor;
+
+public:
+    ASTNodeVisitor(llvm::raw_ostream& OS, clang::ASTContext& Ctx, Parser& parser)
+        : NodeStreamer(OS)
+        , parser(parser)
+        , SM(Ctx.getSourceManager())
+        , Ctx(Ctx)
+        , NameMangler(Ctx)
+        , PrintPolicy(Ctx.getPrintingPolicy())
+        , Traits(Ctx.getCommentCommandTraits())
+        , LastLocLine(0)
+        , LastLocPresumedLine(0)
+    {
+        declMap.reserve(32768);
+        typeMap.reserve(32768);
+        stmtMap.reserve(32768);
+    }
+
+    void Visit(const clang::Attr* A);
+    void Visit(const clang::Stmt* S);
+    void Visit(const clang::Type* T);
+    void Visit(clang::QualType T);
+    AST::Declaration* Visit(const clang::Decl* D);
+    void Visit(clang::TypeLoc TL);
+
+    void Visit(const clang::comments::Comment* C, const clang::comments::FullComment* FC);
+    void Visit(const clang::TemplateArgument& TA, clang::SourceRange R = {}, const clang::Decl* From = nullptr, llvm::StringRef Label = {});
+    void Visit(const clang::CXXCtorInitializer* Init);
+    // void Visit(const OpenACCClause* C) {}
+    void Visit(const clang::OMPClause* C) {}
+    void Visit(const clang::BlockDecl::Capture& C);
+    void Visit(const clang::GenericSelectionExpr::ConstAssociation& A);
+    void Visit(const clang::concepts::Requirement* R);
+    void Visit(const clang::APValue& Value, clang::QualType Ty);
+    void Visit(const clang::ConceptReference*);
+
+    void VisitAliasAttr(const clang::AliasAttr* AA);
+    void VisitCleanupAttr(const clang::CleanupAttr* CA);
+    void VisitDeprecatedAttr(const clang::DeprecatedAttr* DA);
+    void VisitUnavailableAttr(const clang::UnavailableAttr* UA);
+    void VisitSectionAttr(const clang::SectionAttr* SA);
+    void VisitVisibilityAttr(const clang::VisibilityAttr* VA);
+    void VisitTLSModelAttr(const clang::TLSModelAttr* TA);
+
+    void VisitTypedefType(const clang::TypedefType* TT);
+    void VisitUsingType(const clang::UsingType* TT);
+    void VisitFunctionType(const clang::FunctionType* T);
+    void VisitFunctionProtoType(const clang::FunctionProtoType* T);
+    void VisitRValueReferenceType(const clang::ReferenceType* RT);
+    void VisitArrayType(const clang::ArrayType* AT);
+    void VisitConstantArrayType(const clang::ConstantArrayType* CAT);
+    void VisitDependentSizedExtVectorType(const clang::DependentSizedExtVectorType* VT);
+    void VisitVectorType(const clang::VectorType* VT);
+    void VisitUnresolvedUsingType(const clang::UnresolvedUsingType* UUT);
+    void VisitUnaryTransformType(const clang::UnaryTransformType* UTT);
+    void VisitTagType(const clang::TagType* TT);
+    void VisitTemplateTypeParmType(const clang::TemplateTypeParmType* TTPT);
+    void VisitSubstTemplateTypeParmType(const clang::SubstTemplateTypeParmType* STTPT);
+    void VisitSubstTemplateTypeParmPackType(const clang::SubstTemplateTypeParmPackType* T);
+    void VisitAutoType(const clang::AutoType* AT);
+    void VisitTemplateSpecializationType(const clang::TemplateSpecializationType* TST);
+    void VisitInjectedClassNameType(const clang::InjectedClassNameType* ICNT);
+    void VisitObjCInterfaceType(const clang::ObjCInterfaceType* OIT);
+    void VisitPackExpansionType(const clang::PackExpansionType* PET);
+    void VisitElaboratedType(const clang::ElaboratedType* ET);
+    void VisitMacroQualifiedType(const clang::MacroQualifiedType* MQT);
+    void VisitMemberPointerType(const clang::MemberPointerType* MPT);
+
+    AST::Declaration* VisitTranslationUnitDecl(const clang::TranslationUnitDecl* D);
+    AST::Declaration* VisitNamedDecl(const clang::NamedDecl* ND);
+    void HandleNamedDecl(AST::Declaration& AST_ND, const clang::NamedDecl* ND);
+    AST::Declaration* VisitTypedefDecl(const clang::TypedefDecl* TD);
+    AST::Declaration* VisitTypeAliasDecl(const clang::TypeAliasDecl* TAD);
+    AST::Declaration* VisitNamespaceDecl(const clang::NamespaceDecl* ND);
+    AST::Declaration* VisitUsingDirectiveDecl(const clang::UsingDirectiveDecl* UDD);
+    AST::Declaration* VisitNamespaceAliasDecl(const clang::NamespaceAliasDecl* NAD);
+    AST::Declaration* VisitUsingDecl(const clang::UsingDecl* UD);
+    AST::Declaration* VisitUsingEnumDecl(const clang::UsingEnumDecl* UED);
+    AST::Declaration* VisitUsingShadowDecl(const clang::UsingShadowDecl* USD);
+    AST::Declaration* VisitVarDecl(const clang::VarDecl* VD);
+    AST::Declaration* VisitFieldDecl(const clang::FieldDecl* FD);
+    AST::Declaration* VisitFunctionDecl(const clang::FunctionDecl* FD);
+    AST::Declaration* VisitEnumDecl(const clang::EnumDecl* ED);
+    AST::Declaration* VisitEnumConstantDecl(const clang::EnumConstantDecl* ECD);
+    AST::Declaration* VisitRecordDecl(const clang::RecordDecl* RD);
+    AST::Declaration* VisitCXXRecordDecl(const clang::CXXRecordDecl* RD);
+    AST::Declaration* VisitHLSLBufferDecl(const clang::HLSLBufferDecl* D);
+    AST::Declaration* VisitTemplateTypeParmDecl(const clang::TemplateTypeParmDecl* D);
+    AST::Declaration* VisitNonTypeTemplateParmDecl(const clang::NonTypeTemplateParmDecl* D);
+    AST::Declaration* VisitTemplateTemplateParmDecl(const clang::TemplateTemplateParmDecl* D);
+    AST::Declaration* VisitLinkageSpecDecl(const clang::LinkageSpecDecl* LSD);
+    AST::Declaration* VisitAccessSpecDecl(const clang::AccessSpecDecl* ASD);
+    AST::Declaration* VisitFriendDecl(const clang::FriendDecl* FD);
+
+    AST::Declaration* VisitObjCIvarDecl(const clang::ObjCIvarDecl* D);
+    AST::Declaration* VisitObjCMethodDecl(const clang::ObjCMethodDecl* D);
+    AST::Declaration* VisitObjCTypeParamDecl(const clang::ObjCTypeParamDecl* D);
+    AST::Declaration* VisitObjCCategoryDecl(const clang::ObjCCategoryDecl* D);
+    AST::Declaration* VisitObjCCategoryImplDecl(const clang::ObjCCategoryImplDecl* D);
+    AST::Declaration* VisitObjCProtocolDecl(const clang::ObjCProtocolDecl* D);
+    AST::Declaration* VisitObjCInterfaceDecl(const clang::ObjCInterfaceDecl* D);
+    AST::Declaration* VisitObjCImplementationDecl(const clang::ObjCImplementationDecl* D);
+    AST::Declaration* VisitObjCCompatibleAliasDecl(const clang::ObjCCompatibleAliasDecl* D);
+    AST::Declaration* VisitObjCPropertyDecl(const clang::ObjCPropertyDecl* D);
+    AST::Declaration* VisitObjCPropertyImplDecl(const clang::ObjCPropertyImplDecl* D);
+    AST::Declaration* VisitBlockDecl(const clang::BlockDecl* D);
+
+    /*#define DECL(CLASS, BASE)                                                                \
+        void Convert##CLASS##DeclImpl(const clang::CLASS##Decl* Src, AST::CLASS##Decl& Dst); \
+        void Convert##CLASS##Decl(const clang::CLASS##Decl* Src, AST::CLASS##Decl& Dst)      \
+        {                                                                                    \
+            Convert##BASE##Decl(Src, Dst);                                                   \
+            Convert##CLASS##DeclImpl(Src, Dst);                                              \
+        }
+    #include "clang/AST/DeclNodes.inc"
+
+    // Declare Visit*() for all concrete Decl classes.
+    #define ABSTRACT_DECL(DECL)
+    #define DECL(CLASS, BASE)                                      \
+        void Visit##CLASS##Decl(const CLASS##Decl* D)              \
+        {                                                          \
+            if (declMap.find(D) != declMap.end())                  \
+                return;                                            \
+                                                                   \
+            auto res = declMap.emplace(D, new AST::CLASS##Decl()); \
+            Convert##CLASS##Decl(D, res.first->second);            \
+        }
+    #include "clang/AST/DeclNodes.inc"
+            // The above header #undefs ABSTRACT_DECL and DECL upon exit.*/
+
+
+    void ConvertStmt(const clang::Stmt* Src, AST::Stmt& Dst);
+
+#define STMT(CLASS, BASE)                                                \
+    void Convert##CLASS##Impl(const clang::CLASS* Src, AST::CLASS& Dst); \
+    void Convert##CLASS(const clang::CLASS* Src, AST::CLASS& Dst)        \
+    {                                                                    \
+        Convert##BASE(Src, Dst);                                         \
+        Convert##CLASS##Impl(Src, Dst);                                  \
+    }
+#include "StmtNodes.inc"
+
+// Declare Visit*() for all concrete Stmt classes.
+#define ABSTRACT_STMT(STMT)
+#define STMT(CLASS, BASE)                                                \
+    void Visit##CLASS(const clang::CLASS* S)                             \
+    {                                                                    \
+        if (!S || stmtMap.find(S) != stmtMap.end())                      \
+            return;                                                      \
+                                                                         \
+        auto res = stmtMap.emplace(S, new AST::CLASS());                 \
+        Convert##CLASS(S, static_cast<AST::CLASS&>(*res.first->second)); \
+    }
+#include "StmtNodes.inc"
+    // The above header #undefs ABSTRACT_STMT and STMT upon exit.
+
+    void VisitNullTemplateArgument(const clang::TemplateArgument& TA);
+    void VisitTypeTemplateArgument(const clang::TemplateArgument& TA);
+    void VisitDeclarationTemplateArgument(const clang::TemplateArgument& TA);
+    void VisitNullPtrTemplateArgument(const clang::TemplateArgument& TA);
+    void VisitIntegralTemplateArgument(const clang::TemplateArgument& TA);
+    void VisitTemplateTemplateArgument(const clang::TemplateArgument& TA);
+    void VisitTemplateExpansionTemplateArgument(const clang::TemplateArgument& TA);
+    void VisitExpressionTemplateArgument(const clang::TemplateArgument& TA);
+    void VisitPackTemplateArgument(const clang::TemplateArgument& TA);
+
+    void visitTextComment(const clang::comments::TextComment* C, const clang::comments::FullComment*);
+    void visitInlineCommandComment(const clang::comments::InlineCommandComment* C, const clang::comments::FullComment*);
+    void visitHTMLStartTagComment(const clang::comments::HTMLStartTagComment* C, const clang::comments::FullComment*);
+    void visitHTMLEndTagComment(const clang::comments::HTMLEndTagComment* C, const clang::comments::FullComment*);
+    void visitBlockCommandComment(const clang::comments::BlockCommandComment* C, const clang::comments::FullComment*);
+    void visitParamCommandComment(const clang::comments::ParamCommandComment* C, const clang::comments::FullComment* FC);
+    void visitTParamCommandComment(const clang::comments::TParamCommandComment* C, const clang::comments::FullComment* FC);
+    void visitVerbatimBlockComment(const clang::comments::VerbatimBlockComment* C, const clang::comments::FullComment*);
+    void visitVerbatimBlockLineComment(const clang::comments::VerbatimBlockLineComment* C, const clang::comments::FullComment*);
+    void visitVerbatimLineComment(const clang::comments::VerbatimLineComment* C, const clang::comments::FullComment*);
+
+private:
+    void attributeOnlyIfTrue(llvm::StringRef Key, bool Value)
+    {
+        if (Value)
+            JOS.attribute(Key, Value);
+    }
+
+    void writeIncludeStack(clang::PresumedLoc Loc, bool JustFirst = false);
+
+    // Writes the attributes of a SourceLocation object without.
+    void writeBareSourceLocation(clang::SourceLocation Loc, bool IsSpelling, bool addFileInfo);
+
+    // Writes the attributes of a SourceLocation to JSON based on its presumed
+    // spelling location. If the given location represents a macro invocation,
+    // this outputs two sub-objects: one for the spelling and one for the
+    // expansion location.
+    void writeSourceLocation(clang::SourceLocation Loc, bool addFileInfo = true);
+    void writeSourceRange(clang::SourceRange R);
+    std::string createPointerRepresentation(const void* Ptr);
+    llvm::json::Object createQualType(clang::QualType QT, bool Desugar = true);
+    AST::QualifiedType CreateQualifiedType(clang::QualType QT, bool Desugar = true);
+    llvm::json::Object createBareDeclRef(const clang::Decl* D);
+    llvm::json::Object createFPOptions(clang::FPOptionsOverride FPO);
+    void writeBareDeclRef(const clang::Decl* D);
+    llvm::json::Object createCXXRecordDefinitionData(const clang::CXXRecordDecl* RD);
+    llvm::json::Object createCXXBaseSpecifier(const clang::CXXBaseSpecifier& BS);
+    std::string createAccessSpecifier(clang::AccessSpecifier AS);
+    llvm::json::Array createCastPath(const clang::CastExpr* C);
+
+    void writePreviousDeclImpl(...) {}
+
+    template <typename T>
+    void writePreviousDeclImpl(const clang::Mergeable<T>* D)
+    {
+        const T* First = D->getFirstDecl();
+        if (First != D)
+            JOS.attribute("firstRedecl", createPointerRepresentation(First));
+    }
+
+    template <typename T>
+    void writePreviousDeclImpl(const clang::Redeclarable<T>* D)
+    {
+        const T* Prev = D->getPreviousDecl();
+        if (Prev)
+            JOS.attribute("previousDecl", createPointerRepresentation(Prev));
+    }
+
+    [[nodiscard]] std::string GetMangledName(const clang::NamedDecl& ND) const;
+    void ConvertNamedRecord(AST::Declaration& dst, const clang::NamedDecl& src) const;
+
+    void addPreviousDeclaration(const clang::Decl* D);
+
+    llvm::StringRef getCommentCommandName(unsigned CommandID) const;
+
+    template <typename Fn>
+    AST::Declaration* FindOrInsertLazy(const clang::Decl* D, Fn&& inserter)
+    {
+        if (auto it = declMap.find(D); it != declMap.end())
+            return it->second;
+
+        auto res = declMap.emplace(D, std::invoke(inserter));
+        return res.first->second;
+    }
+
+    std::unordered_map<const clang::Decl*, AST::Declaration*> declMap;
+    std::unordered_set<const clang::Type*> typeMap;
+    std::unordered_map<const clang::Stmt*, AST::Stmt*> stmtMap;
+
+    Parser& parser;
+    const clang::SourceManager& SM;
+    clang::ASTContext& Ctx;
+    ASTNameMangler NameMangler; // TODO: Remove this, or the parser one
+    clang::PrintingPolicy PrintPolicy;
+    const clang::comments::CommandTraits& Traits;
+    llvm::StringRef LastLocFilename, LastLocPresumedFilename;
+    unsigned LastLocLine, LastLocPresumedLine;
+};
+
+class ASTNodeDumper : public clang::ASTNodeTraverser<ASTNodeDumper, ASTNodeVisitor>
+{
+public:
+    ASTNodeDumper(llvm::raw_ostream& OS, clang::ASTContext& Ctx, Parser& parser)
+        : NodeVisitor(OS, Ctx, parser)
+    {
+        setDeserialize(true);
+    }
+
+    ASTNodeVisitor& doGetNodeDelegate() { return NodeVisitor; }
+
+    void VisitFunctionTemplateDecl(const clang::FunctionTemplateDecl* FTD)
+    {
+        writeTemplateDecl(FTD, true);
+    }
+    void VisitClassTemplateDecl(const clang::ClassTemplateDecl* CTD)
+    {
+        writeTemplateDecl(CTD, false);
+    }
+    void VisitVarTemplateDecl(const clang::VarTemplateDecl* VTD)
+    {
+        writeTemplateDecl(VTD, false);
+    }
+
+private:
+    template <typename SpecializationDecl>
+    void writeTemplateDeclSpecialization(const SpecializationDecl* SD, bool DumpExplicitInst, bool DumpRefOnly)
+    {
+        bool DumpedAny = false;
+        for (const auto* RedeclWithBadType : SD->redecls())
+        {
+            // FIXME: The redecls() range sometimes has elements of a less-specific
+            // type. (In particular, ClassTemplateSpecializationDecl::redecls() gives
+            // us TagDecls, and should give CXXRecordDecls).
+            const auto* Redecl = clang::dyn_cast<SpecializationDecl>(RedeclWithBadType);
+            if (!Redecl)
+            {
+                // Found the injected-class-name for a class template. This will be
+                // dumped as part of its surrounding class so we don't need to dump it
+                // here.
+                assert(clang::isa<clang::CXXRecordDecl>(RedeclWithBadType) &&
+                       "expected an injected-class-name");
+                continue;
+            }
+
+            switch (Redecl->getTemplateSpecializationKind())
+            {
+                case clang::TSK_ExplicitInstantiationDeclaration:
+                case clang::TSK_ExplicitInstantiationDefinition:
+                    if (!DumpExplicitInst)
+                        break;
+                    [[fallthrough]];
+                case clang::TSK_Undeclared:
+                case clang::TSK_ImplicitInstantiation:
+                    if (DumpRefOnly)
+                        NodeVisitor.AddChild([=]
+                                             {
+                                                 NodeVisitor.writeBareDeclRef(Redecl);
+                                             });
+                    else
+                        Visit(Redecl);
+                    DumpedAny = true;
+                    break;
+                case clang::TSK_ExplicitSpecialization:
+                    break;
+            }
+        }
+
+        // Ensure we dump at least one decl for each specialization.
+        if (!DumpedAny)
+            NodeVisitor.AddChild([=]
+                                 {
+                                     NodeVisitor.writeBareDeclRef(SD);
+                                 });
+    }
+
+    template <typename TemplateDecl>
+    void writeTemplateDecl(const TemplateDecl* TD, bool DumpExplicitInst)
+    {
+        // FIXME: it would be nice to dump template parameters and specializations
+        // to their own named arrays rather than shoving them into the "inner"
+        // array. However, template declarations are currently being handled at the
+        // wrong "level" of the traversal hierarchy and so it is difficult to
+        // achieve without losing information elsewhere.
+
+        dumpTemplateParameters(TD->getTemplateParameters());
+
+        Visit(TD->getTemplatedDecl());
+
+        // TODO: Fixme. Crash in specializations iterator (clang bug?)
+        // for (const auto* Child : TD->specializations())
+        //    writeTemplateDeclSpecialization(Child, DumpExplicitInst, !TD->isCanonicalDecl());
+    }
+
+    ASTNodeVisitor NodeVisitor;
+};
+
+class ASTParser : public clang::RecursiveASTVisitor<ASTParser>
+{
+    bool shouldVisitTemplateInstantiations() const { return true; }
+    bool shouldWalkTypesOfTypeLocs() const { return true; }
+    bool shouldVisitImplicitCode() const { return true; }
+    bool shouldVisitLambdaBody() const { return true; }
+    bool shouldTraversePostOrder() const { return true; }
+
+    /*void HandleNamedDecl(AST::Declaration& AST_ND, const clang::NamedDecl* ND);
+
+    bool VisitTranslationUnitDecl(const clang::TranslationUnitDecl* D);
+    bool VisitNamedDecl(const clang::NamedDecl* ND);
+    bool VisitTypedefDecl(const clang::TypedefDecl* TD);
+    bool VisitTypeAliasDecl(const clang::TypeAliasDecl* TAD);
+    bool VisitNamespaceDecl(const clang::NamespaceDecl* ND);
+    bool VisitUsingDirectiveDecl(const clang::UsingDirectiveDecl* UDD);
+    bool VisitNamespaceAliasDecl(const clang::NamespaceAliasDecl* NAD);
+    bool VisitUsingDecl(const clang::UsingDecl* UD);
+    bool VisitUsingEnumDecl(const clang::UsingEnumDecl* UED);
+    bool VisitUsingShadowDecl(const clang::UsingShadowDecl* USD);
+    bool VisitVarDecl(const clang::VarDecl* VD);
+    bool VisitFieldDecl(const clang::FieldDecl* FD);
+    bool VisitFunctionDecl(const clang::FunctionDecl* FD);
+    bool VisitEnumDecl(const clang::EnumDecl* ED);
+    bool VisitEnumConstantDecl(const clang::EnumConstantDecl* ECD);
+    bool VisitRecordDecl(const clang::RecordDecl* RD);
+    bool VisitCXXRecordDecl(const clang::CXXRecordDecl* RD);
+    bool VisitHLSLBufferDecl(const clang::HLSLBufferDecl* D);
+    bool VisitTemplateTypeParmDecl(const clang::TemplateTypeParmDecl* D);
+    bool VisitNonTypeTemplateParmDecl(const clang::NonTypeTemplateParmDecl* D);
+    bool VisitTemplateTemplateParmDecl(const clang::TemplateTemplateParmDecl* D);
+    bool VisitLinkageSpecDecl(const clang::LinkageSpecDecl* LSD);
+    bool VisitAccessSpecDecl(const clang::AccessSpecDecl* ASD);
+    bool VisitFriendDecl(const clang::FriendDecl* FD);*/
+};
+} // namespace CppSharp::CppParser
\ No newline at end of file
diff --git a/src/CppParser/Decl.h b/src/CppParser/Decl.h
index b931e88e..a895b529 100644
--- a/src/CppParser/Decl.h
+++ b/src/CppParser/Decl.h
@@ -11,6 +11,7 @@
 #include "Sources.h"
 #include "Types.h"
 #include <algorithm>
+#include <unordered_map>
 
 namespace CppSharp {
 namespace CppParser {
@@ -82,6 +83,7 @@ namespace AST {
         int lineNumberStart;
         int lineNumberEnd;
         std::string name;
+        std::string mangledName;
         std::string USR;
         std::string debugText;
         bool isIncomplete;
@@ -89,6 +91,9 @@ namespace AST {
         bool isImplicit;
         bool isInvalid;
         bool isDeprecated;
+        bool isHidden;
+        bool isUsed;
+        bool isReferenced;
         Declaration* completeDeclaration;
         unsigned definitionOrder;
         VECTOR(PreprocessedEntity*, PreprocessedEntities)
@@ -115,32 +120,32 @@ namespace AST {
     public:
         DeclarationContext(DeclarationKind kind);
 
-        CS_IGNORE Declaration* FindAnonymous(const std::string& USR);
+        CS_IGNORE Declaration* FindAnonymous(const std::string_view& USR);
 
-        CS_IGNORE Namespace* FindNamespace(const std::string& Name);
-        CS_IGNORE Namespace* FindNamespace(const std::vector<std::string>&);
-        CS_IGNORE Namespace* FindCreateNamespace(const std::string& Name);
+        CS_IGNORE Namespace* FindNamespace(const std::string_view& name);
+        CS_IGNORE Namespace* FindNamespace(const std::vector<std::string_view>&);
+        CS_IGNORE Namespace& FindCreateNamespace(const std::string_view& Name);
 
-        CS_IGNORE Class* CreateClass(const std::string& Name, bool IsComplete);
-        CS_IGNORE Class* FindClass(const void* OriginalPtr, const std::string& Name, bool IsComplete);
-        CS_IGNORE Class* FindClass(const void* OriginalPtr, const std::string& Name, bool IsComplete, bool Create);
+        CS_IGNORE Class* CreateClass(const std::string_view& Name, bool IsComplete);
+        CS_IGNORE Class* FindClass(const void* OriginalPtr, const std::string_view& Name, bool IsComplete);
+        CS_IGNORE Class* FindClass(const void* OriginalPtr, const std::string_view& Name, bool IsComplete, bool Create);
 
         CS_IGNORE template <typename T>
-        T* FindTemplate(const std::string& USR);
+        T* FindTemplate(const std::string_view& USR);
 
         CS_IGNORE Enumeration* FindEnum(const void* OriginalPtr);
-        CS_IGNORE Enumeration* FindEnum(const std::string& Name, bool Create = false);
-        CS_IGNORE Enumeration* FindEnumWithItem(const std::string& Name);
+        CS_IGNORE Enumeration* FindEnum(const std::string_view& Name, bool Create = false);
+        CS_IGNORE Enumeration* FindEnumWithItem(const std::string_view& Name);
 
-        CS_IGNORE Function* FindFunction(const std::string& USR);
+        CS_IGNORE Function* FindFunction(const std::string_view& USR);
 
-        CS_IGNORE TypedefDecl* FindTypedef(const std::string& Name, bool Create = false);
+        CS_IGNORE TypedefDecl* FindTypedef(const std::string_view& Name, bool Create = false);
 
-        CS_IGNORE TypeAlias* FindTypeAlias(const std::string& Name, bool Create = false);
+        CS_IGNORE TypeAlias* FindTypeAlias(const std::string_view& Name, bool Create = false);
 
-        CS_IGNORE Variable* FindVariable(const std::string& USR);
+        CS_IGNORE Variable* FindVariable(const std::string_view& USR);
 
-        CS_IGNORE Friend* FindFriend(const std::string& USR);
+        CS_IGNORE Friend* FindFriend(const std::string_view& USR);
 
         VECTOR(Namespace*, Namespaces)
         VECTOR(Enumeration*, Enums)
@@ -152,7 +157,7 @@ namespace AST {
         VECTOR(Variable*, Variables)
         VECTOR(Friend*, Friends)
 
-        std::map<std::string, Declaration*> anonymous;
+        std::map<std::string, Declaration*, std::less<>> anonymous;
 
         bool isAnonymous;
     };
@@ -203,7 +208,7 @@ namespace AST {
     class CS_API StatementObsolete
     {
     public:
-        StatementObsolete(const std::string& str, StatementClassObsolete Class = StatementClassObsolete::Any, Declaration* decl = 0);
+        StatementObsolete(const std::string& str, StatementClassObsolete Class = StatementClassObsolete::Any, Declaration* decl = nullptr);
         StatementClassObsolete _class;
         Declaration* decl;
         std::string string;
@@ -212,7 +217,7 @@ namespace AST {
     class CS_API ExpressionObsolete : public StatementObsolete
     {
     public:
-        ExpressionObsolete(const std::string& str, StatementClassObsolete Class = StatementClassObsolete::Any, Declaration* decl = 0);
+        ExpressionObsolete(const std::string& str, StatementClassObsolete Class = StatementClassObsolete::Any, Declaration* decl = nullptr);
     };
 
     class Expr;
@@ -238,7 +243,7 @@ namespace AST {
     class CS_API CXXConstructExprObsolete : public ExpressionObsolete
     {
     public:
-        CXXConstructExprObsolete(const std::string& str, Declaration* decl = 0);
+        CXXConstructExprObsolete(const std::string& str, Declaration* decl = nullptr);
         ~CXXConstructExprObsolete();
         VECTOR(ExpressionObsolete*, Arguments)
     };
@@ -417,7 +422,7 @@ namespace AST {
         BuiltinType* builtinType;
         VECTOR(Item*, Items)
 
-        Item* FindItemByName(const std::string& Name);
+        Item* FindItemByName(const std::string_view& Name);
     };
 
     class CS_API Variable : public Declaration
@@ -611,10 +616,10 @@ namespace AST {
     };
 
     template <typename T>
-    T* DeclarationContext::FindTemplate(const std::string& USR)
+    T* DeclarationContext::FindTemplate(const std::string_view& USR)
     {
-        auto foundTemplate = std::find_if(Templates.begin(), Templates.end(),
-                                          [&](Template* t)
+        auto foundTemplate = std::find_if(Templates.cbegin(), Templates.cend(),
+                                          [&](const Template* t)
                                           {
                                               return t->USR == USR;
                                           });
@@ -782,6 +787,7 @@ namespace AST {
         Namespace();
         ~Namespace();
         bool isInline;
+        bool isNested;
     };
 
     enum class MacroLocation
@@ -845,4 +851,4 @@ namespace AST {
 
 }
 }
-} // namespace CppSharp::CppParser::AST
\ No newline at end of file
+} // namespace CppSharp::CppParser::AST
diff --git a/src/CppParser/Parser.cpp b/src/CppParser/Parser.cpp
index 22a500af..8b196714 100644
--- a/src/CppParser/Parser.cpp
+++ b/src/CppParser/Parser.cpp
@@ -59,6 +59,7 @@
 #include <Driver/ToolChains/MSVC.h>
 
 #include "ASTNameMangler.h"
+#include "ASTNodeVisitor.h"
 
 #if defined(__APPLE__) || defined(__linux__)
 #ifndef _GNU_SOURCE
@@ -515,12 +516,6 @@ std::string Parser::GetDeclMangledName(const clang::Decl* D) const
     if (ND->isTemplated())
         return {};
 
-    /*bool CanMangle = isa<FunctionDecl>(D) || isa<VarDecl>(D)
-        || isa<CXXConstructorDecl>(D) || isa<CXXDestructorDecl>(D);
-
-    if (!CanMangle)
-        return {};*/
-
     // FIXME: There are likely other contexts in which it makes no sense to ask
     // for a mangled name.
     if (isa<RequiresExprBodyDecl>(ND->getDeclContext()))
@@ -990,6 +985,8 @@ static clang::CXXRecordDecl* GetCXXRecordDeclFromBaseType(const clang::ASTContex
         return dyn_cast<CXXRecordDecl>(RT->getDecl());
     else if (auto TST = Ty->getAs<clang::TemplateSpecializationType>())
         return GetCXXRecordDeclFromTemplateName(TST->getTemplateName());
+    else if (auto DTST = Ty->getAs<clang::DependentTemplateSpecializationType>())
+        return nullptr;
     else if (auto Injected = Ty->getAs<clang::InjectedClassNameType>())
         return Injected->getDecl();
     else if (auto TTPT = Ty->getAs<TemplateTypeParmType>())
@@ -2126,8 +2123,8 @@ Field* Parser::WalkFieldCXX(const clang::FieldDecl* FD, Class* Class)
 
 //-----------------------------------//
 
-TranslationUnit* Parser::GetTranslationUnit(clang::SourceLocation Loc,
-                                            SourceLocationKind* Kind)
+TranslationUnit* Parser::GetOrCreateTranslationUnit(clang::SourceLocation Loc,
+                                                    SourceLocationKind* Kind)
 {
     using namespace clang;
 
@@ -2172,12 +2169,12 @@ TranslationUnit* Parser::GetTranslationUnit(clang::SourceLocation Loc,
 
 //-----------------------------------//
 
-TranslationUnit* Parser::GetTranslationUnit(const clang::Decl* D)
+TranslationUnit* Parser::GetOrCreateTranslationUnit(const clang::Decl* D)
 {
     clang::SourceLocation Loc = D->getLocation();
 
     SourceLocationKind Kind;
-    TranslationUnit* Unit = GetTranslationUnit(Loc, &Kind);
+    TranslationUnit* Unit = GetOrCreateTranslationUnit(Loc, &Kind);
 
     return Unit;
 }
@@ -2191,13 +2188,13 @@ DeclarationContext* Parser::GetNamespace(const clang::Decl* D,
 
     // If the declaration is at global scope, just early exit.
     if (Context->isTranslationUnit())
-        return GetTranslationUnit(D);
+        return GetOrCreateTranslationUnit(D);
 
     auto NS = walkedNamespaces[Context];
     if (NS)
         return NS;
 
-    TranslationUnit* Unit = GetTranslationUnit(cast<Decl>(Context));
+    TranslationUnit* Unit = GetOrCreateTranslationUnit(cast<Decl>(Context));
 
     // Else we need to do a more expensive check to get all the namespaces,
     // and then perform a reverse iteration to get the namespaces in order.
@@ -2231,8 +2228,7 @@ DeclarationContext* Parser::GetNamespace(const clang::Decl* D,
                 if (ND->isAnonymousNamespace())
                     break;
 
-                auto Name = ND->getName();
-                DC = DC->FindCreateNamespace(Name.str());
+                DC = &DC->FindCreateNamespace(ND->getName());
                 ((Namespace*)DC)->isAnonymous = ND->isAnonymousNamespace();
                 ((Namespace*)DC)->isInline = ND->isInline();
                 HandleDeclaration(ND, DC);
@@ -3890,7 +3886,7 @@ PreprocessedEntity* Parser::WalkPreprocessedEntity(
         return nullptr;
 
     Entity->originalPtr = PPEntity;
-    auto Namespace = GetTranslationUnit(PPEntity->getSourceRange().getBegin());
+    auto Namespace = GetOrCreateTranslationUnit(PPEntity->getSourceRange().getBegin());
 
     if (Decl->kind == DeclarationKind::TranslationUnit)
     {
@@ -4403,7 +4399,7 @@ Declaration* Parser::WalkDeclaration(const clang::Decl* D)
         }
         case Decl::TranslationUnit:
         {
-            Decl = GetTranslationUnit(D);
+            Decl = GetOrCreateTranslationUnit(D);
             break;
         }
         case Decl::Namespace:
@@ -4677,6 +4673,16 @@ void SemaConsumer::HandleTranslationUnit(clang::ASTContext& Ctx)
     if (Unit->originalPtr == nullptr)
         Unit->originalPtr = (void*)FileEntry;
 
+    std::string Text;
+    Text.reserve(200000000);
+    {
+        llvm::raw_string_ostream OS(Text);
+
+        ASTNodeDumper Dumper(OS, Ctx, Parser);
+        Dumper.Visit(TU);
+        debug_break();
+    }
+
     Parser.WalkAST(TU);
 }
 
diff --git a/src/CppParser/Parser.h b/src/CppParser/Parser.h
index 08f6e679..c3be2466 100644
--- a/src/CppParser/Parser.h
+++ b/src/CppParser/Parser.h
@@ -97,8 +97,8 @@ namespace CppSharp { namespace CppParser {
         void WalkVariable(const clang::VarDecl* VD, AST::Variable* Var);
         AST::Friend* WalkFriend(const clang::FriendDecl* FD);
         AST::RawComment* WalkRawComment(const clang::RawComment* RC);
-        AST::Type* WalkType(clang::QualType QualType, const clang::TypeLoc* TL = 0, bool DesugarType = false);
-        AST::TemplateArgument WalkTemplateArgument(const clang::TemplateArgument& TA, clang::TemplateArgumentLoc* ArgLoc = 0);
+        AST::Type* WalkType(clang::QualType QualType, const clang::TypeLoc* TL = nullptr, bool DesugarType = false);
+        AST::TemplateArgument WalkTemplateArgument(const clang::TemplateArgument& TA, clang::TemplateArgumentLoc* ArgLoc = nullptr);
         AST::TemplateTemplateParameter* WalkTemplateTemplateParameter(const clang::TemplateTemplateParmDecl* TTP);
         AST::TypeTemplateParameter* WalkTypeTemplateParameter(const clang::TemplateTypeParmDecl* TTPD);
         AST::NonTypeTemplateParameter* WalkNonTypeTemplateParameter(const clang::NonTypeTemplateParmDecl* TTPD);
@@ -116,7 +116,7 @@ namespace CppSharp { namespace CppParser {
         std::vector<AST::TemplateArgument> WalkTemplateArgumentList(const clang::TemplateArgumentList* TAL, TypeLoc* TSTL);
         std::vector<AST::TemplateArgument> WalkTemplateArgumentList(const clang::TemplateArgumentList* TAL, const clang::ASTTemplateArgumentListInfo* TSTL);
         void WalkVTable(const clang::CXXRecordDecl* RD, AST::Class* C);
-        AST::QualifiedType GetQualifiedType(clang::QualType qual, const clang::TypeLoc* TL = 0);
+        AST::QualifiedType GetQualifiedType(clang::QualType qual, const clang::TypeLoc* TL = nullptr);
         void ReadClassLayout(AST::Class* Class, const clang::RecordDecl* RD, clang::CharUnits Offset, bool IncludeVirtualBases);
         AST::LayoutField WalkVTablePointer(AST::Class* Class, const clang::CharUnits& Offset, const std::string& prefix);
         AST::VTableLayout WalkVTableLayout(const clang::VTableLayout& VTLayout);
@@ -150,9 +150,9 @@ namespace CppSharp { namespace CppParser {
         bool GetDeclText(clang::SourceRange SR, std::string& Text);
         bool HasLayout(const clang::RecordDecl* Record);
 
-        AST::TranslationUnit* GetTranslationUnit(clang::SourceLocation Loc,
-                                                 SourceLocationKind* Kind = 0);
-        AST::TranslationUnit* GetTranslationUnit(const clang::Decl* D);
+        AST::TranslationUnit* GetOrCreateTranslationUnit(clang::SourceLocation Loc,
+                                                         SourceLocationKind* Kind = nullptr);
+        AST::TranslationUnit* GetOrCreateTranslationUnit(const clang::Decl* D);
 
         AST::DeclarationContext* GetNamespace(const clang::Decl* D, const clang::DeclContext* Ctx);
         AST::DeclarationContext* GetNamespace(const clang::Decl* D);
diff --git a/src/CppParser/ParserGen/ParserGen.cs b/src/CppParser/ParserGen/ParserGen.cs
index 98258a5e..240837d9 100644
--- a/src/CppParser/ParserGen/ParserGen.cs
+++ b/src/CppParser/ParserGen/ParserGen.cs
@@ -28,6 +28,8 @@ namespace CppSharp
             Kind = kind;
             Triple = triple;
             IsGnuCpp11Abi = isGnuCpp11Abi;
+
+            Diagnostics.Level = DiagnosticKind.Debug;
         }
 
         static string GetSourceDirectory(string dir)
diff --git a/src/CppParser/StmtNodes.inc b/src/CppParser/StmtNodes.inc
new file mode 100644
index 00000000..e1a4a926
--- /dev/null
+++ b/src/CppParser/StmtNodes.inc
@@ -0,0 +1,923 @@
+/*===- TableGen'erated file -------------------------------------*- C++ -*-===*\
+|*                                                                            *|
+|* List of AST nodes of a particular kind                                     *|
+|*                                                                            *|
+|* Automatically generated file, do not edit!                                 *|
+|*                                                                            *|
+\*===----------------------------------------------------------------------===*/
+
+#ifndef ABSTRACT_STMT
+#  define ABSTRACT_STMT(Type) Type
+#endif
+#ifndef STMT_RANGE
+#  define STMT_RANGE(Base, First, Last)
+#endif
+
+#ifndef LAST_STMT_RANGE
+#  define LAST_STMT_RANGE(Base, First, Last) STMT_RANGE(Base, First, Last)
+#endif
+
+#ifndef ASMSTMT
+#  define ASMSTMT(Type, Base) STMT(Type, Base)
+#endif
+ABSTRACT_STMT(ASMSTMT(AsmStmt, Stmt))
+#ifndef GCCASMSTMT
+#  define GCCASMSTMT(Type, Base) ASMSTMT(Type, Base)
+#endif
+GCCASMSTMT(GCCAsmStmt, AsmStmt)
+#undef GCCASMSTMT
+
+#ifndef MSASMSTMT
+#  define MSASMSTMT(Type, Base) ASMSTMT(Type, Base)
+#endif
+MSASMSTMT(MSAsmStmt, AsmStmt)
+#undef MSASMSTMT
+
+STMT_RANGE(AsmStmt, GCCAsmStmt, MSAsmStmt)
+
+#undef ASMSTMT
+
+#ifndef BREAKSTMT
+#  define BREAKSTMT(Type, Base) STMT(Type, Base)
+#endif
+BREAKSTMT(BreakStmt, Stmt)
+#undef BREAKSTMT
+
+#ifndef CXXCATCHSTMT
+#  define CXXCATCHSTMT(Type, Base) STMT(Type, Base)
+#endif
+CXXCATCHSTMT(CXXCatchStmt, Stmt)
+#undef CXXCATCHSTMT
+
+#ifndef CXXFORRANGESTMT
+#  define CXXFORRANGESTMT(Type, Base) STMT(Type, Base)
+#endif
+CXXFORRANGESTMT(CXXForRangeStmt, Stmt)
+#undef CXXFORRANGESTMT
+
+#ifndef CXXTRYSTMT
+#  define CXXTRYSTMT(Type, Base) STMT(Type, Base)
+#endif
+CXXTRYSTMT(CXXTryStmt, Stmt)
+#undef CXXTRYSTMT
+
+#ifndef CAPTUREDSTMT
+#  define CAPTUREDSTMT(Type, Base) STMT(Type, Base)
+#endif
+CAPTUREDSTMT(CapturedStmt, Stmt)
+#undef CAPTUREDSTMT
+
+#ifndef COMPOUNDSTMT
+#  define COMPOUNDSTMT(Type, Base) STMT(Type, Base)
+#endif
+COMPOUNDSTMT(CompoundStmt, Stmt)
+#undef COMPOUNDSTMT
+
+#ifndef CONTINUESTMT
+#  define CONTINUESTMT(Type, Base) STMT(Type, Base)
+#endif
+CONTINUESTMT(ContinueStmt, Stmt)
+#undef CONTINUESTMT
+
+#ifndef CORETURNSTMT
+#  define CORETURNSTMT(Type, Base) STMT(Type, Base)
+#endif
+CORETURNSTMT(CoreturnStmt, Stmt)
+#undef CORETURNSTMT
+
+#ifndef COROUTINEBODYSTMT
+#  define COROUTINEBODYSTMT(Type, Base) STMT(Type, Base)
+#endif
+COROUTINEBODYSTMT(CoroutineBodyStmt, Stmt)
+#undef COROUTINEBODYSTMT
+
+#ifndef DECLSTMT
+#  define DECLSTMT(Type, Base) STMT(Type, Base)
+#endif
+DECLSTMT(DeclStmt, Stmt)
+#undef DECLSTMT
+
+#ifndef DOSTMT
+#  define DOSTMT(Type, Base) STMT(Type, Base)
+#endif
+DOSTMT(DoStmt, Stmt)
+#undef DOSTMT
+
+#ifndef FORSTMT
+#  define FORSTMT(Type, Base) STMT(Type, Base)
+#endif
+FORSTMT(ForStmt, Stmt)
+#undef FORSTMT
+
+#ifndef GOTOSTMT
+#  define GOTOSTMT(Type, Base) STMT(Type, Base)
+#endif
+GOTOSTMT(GotoStmt, Stmt)
+#undef GOTOSTMT
+
+#ifndef IFSTMT
+#  define IFSTMT(Type, Base) STMT(Type, Base)
+#endif
+IFSTMT(IfStmt, Stmt)
+#undef IFSTMT
+
+#ifndef INDIRECTGOTOSTMT
+#  define INDIRECTGOTOSTMT(Type, Base) STMT(Type, Base)
+#endif
+INDIRECTGOTOSTMT(IndirectGotoStmt, Stmt)
+#undef INDIRECTGOTOSTMT
+
+#ifndef MSDEPENDENTEXISTSSTMT
+#  define MSDEPENDENTEXISTSSTMT(Type, Base) STMT(Type, Base)
+#endif
+MSDEPENDENTEXISTSSTMT(MSDependentExistsStmt, Stmt)
+#undef MSDEPENDENTEXISTSSTMT
+
+#ifndef NULLSTMT
+#  define NULLSTMT(Type, Base) STMT(Type, Base)
+#endif
+NULLSTMT(NullStmt, Stmt)
+#undef NULLSTMT
+
+// Removed OMP nodes
+// Removed OBJC nodes
+
+#ifndef RETURNSTMT
+#  define RETURNSTMT(Type, Base) STMT(Type, Base)
+#endif
+RETURNSTMT(ReturnStmt, Stmt)
+#undef RETURNSTMT
+
+#ifndef SEHEXCEPTSTMT
+#  define SEHEXCEPTSTMT(Type, Base) STMT(Type, Base)
+#endif
+SEHEXCEPTSTMT(SEHExceptStmt, Stmt)
+#undef SEHEXCEPTSTMT
+
+#ifndef SEHFINALLYSTMT
+#  define SEHFINALLYSTMT(Type, Base) STMT(Type, Base)
+#endif
+SEHFINALLYSTMT(SEHFinallyStmt, Stmt)
+#undef SEHFINALLYSTMT
+
+#ifndef SEHLEAVESTMT
+#  define SEHLEAVESTMT(Type, Base) STMT(Type, Base)
+#endif
+SEHLEAVESTMT(SEHLeaveStmt, Stmt)
+#undef SEHLEAVESTMT
+
+#ifndef SEHTRYSTMT
+#  define SEHTRYSTMT(Type, Base) STMT(Type, Base)
+#endif
+SEHTRYSTMT(SEHTryStmt, Stmt)
+#undef SEHTRYSTMT
+
+#ifndef SWITCHCASE
+#  define SWITCHCASE(Type, Base) STMT(Type, Base)
+#endif
+ABSTRACT_STMT(SWITCHCASE(SwitchCase, Stmt))
+#ifndef CASESTMT
+#  define CASESTMT(Type, Base) SWITCHCASE(Type, Base)
+#endif
+CASESTMT(CaseStmt, SwitchCase)
+#undef CASESTMT
+
+#ifndef DEFAULTSTMT
+#  define DEFAULTSTMT(Type, Base) SWITCHCASE(Type, Base)
+#endif
+DEFAULTSTMT(DefaultStmt, SwitchCase)
+#undef DEFAULTSTMT
+
+STMT_RANGE(SwitchCase, CaseStmt, DefaultStmt)
+
+#undef SWITCHCASE
+
+#ifndef SWITCHSTMT
+#  define SWITCHSTMT(Type, Base) STMT(Type, Base)
+#endif
+SWITCHSTMT(SwitchStmt, Stmt)
+#undef SWITCHSTMT
+
+#ifndef VALUESTMT
+#  define VALUESTMT(Type, Base) STMT(Type, Base)
+#endif
+ABSTRACT_STMT(VALUESTMT(ValueStmt, Stmt))
+#ifndef ATTRIBUTEDSTMT
+#  define ATTRIBUTEDSTMT(Type, Base) VALUESTMT(Type, Base)
+#endif
+ATTRIBUTEDSTMT(AttributedStmt, ValueStmt)
+#undef ATTRIBUTEDSTMT
+
+#ifndef EXPR
+#  define EXPR(Type, Base) VALUESTMT(Type, Base)
+#endif
+ABSTRACT_STMT(EXPR(Expr, ValueStmt))
+#ifndef ABSTRACTCONDITIONALOPERATOR
+#  define ABSTRACTCONDITIONALOPERATOR(Type, Base) EXPR(Type, Base)
+#endif
+ABSTRACT_STMT(ABSTRACTCONDITIONALOPERATOR(AbstractConditionalOperator, Expr))
+#ifndef BINARYCONDITIONALOPERATOR
+#  define BINARYCONDITIONALOPERATOR(Type, Base) ABSTRACTCONDITIONALOPERATOR(Type, Base)
+#endif
+BINARYCONDITIONALOPERATOR(BinaryConditionalOperator, AbstractConditionalOperator)
+#undef BINARYCONDITIONALOPERATOR
+
+#ifndef CONDITIONALOPERATOR
+#  define CONDITIONALOPERATOR(Type, Base) ABSTRACTCONDITIONALOPERATOR(Type, Base)
+#endif
+CONDITIONALOPERATOR(ConditionalOperator, AbstractConditionalOperator)
+#undef CONDITIONALOPERATOR
+
+STMT_RANGE(AbstractConditionalOperator, BinaryConditionalOperator, ConditionalOperator)
+
+#undef ABSTRACTCONDITIONALOPERATOR
+
+#ifndef ADDRLABELEXPR
+#  define ADDRLABELEXPR(Type, Base) EXPR(Type, Base)
+#endif
+ADDRLABELEXPR(AddrLabelExpr, Expr)
+#undef ADDRLABELEXPR
+
+#ifndef ARRAYINITINDEXEXPR
+#  define ARRAYINITINDEXEXPR(Type, Base) EXPR(Type, Base)
+#endif
+ARRAYINITINDEXEXPR(ArrayInitIndexExpr, Expr)
+#undef ARRAYINITINDEXEXPR
+
+#ifndef ARRAYINITLOOPEXPR
+#  define ARRAYINITLOOPEXPR(Type, Base) EXPR(Type, Base)
+#endif
+ARRAYINITLOOPEXPR(ArrayInitLoopExpr, Expr)
+#undef ARRAYINITLOOPEXPR
+
+#ifndef ARRAYSUBSCRIPTEXPR
+#  define ARRAYSUBSCRIPTEXPR(Type, Base) EXPR(Type, Base)
+#endif
+ARRAYSUBSCRIPTEXPR(ArraySubscriptExpr, Expr)
+#undef ARRAYSUBSCRIPTEXPR
+
+#ifndef ARRAYTYPETRAITEXPR
+#  define ARRAYTYPETRAITEXPR(Type, Base) EXPR(Type, Base)
+#endif
+ARRAYTYPETRAITEXPR(ArrayTypeTraitExpr, Expr)
+#undef ARRAYTYPETRAITEXPR
+
+#ifndef ASTYPEEXPR
+#  define ASTYPEEXPR(Type, Base) EXPR(Type, Base)
+#endif
+ASTYPEEXPR(AsTypeExpr, Expr)
+#undef ASTYPEEXPR
+
+#ifndef ATOMICEXPR
+#  define ATOMICEXPR(Type, Base) EXPR(Type, Base)
+#endif
+ATOMICEXPR(AtomicExpr, Expr)
+#undef ATOMICEXPR
+
+#ifndef BINARYOPERATOR
+#  define BINARYOPERATOR(Type, Base) EXPR(Type, Base)
+#endif
+BINARYOPERATOR(BinaryOperator, Expr)
+#ifndef COMPOUNDASSIGNOPERATOR
+#  define COMPOUNDASSIGNOPERATOR(Type, Base) BINARYOPERATOR(Type, Base)
+#endif
+COMPOUNDASSIGNOPERATOR(CompoundAssignOperator, BinaryOperator)
+#undef COMPOUNDASSIGNOPERATOR
+
+STMT_RANGE(BinaryOperator, BinaryOperator, CompoundAssignOperator)
+
+#undef BINARYOPERATOR
+
+#ifndef BLOCKEXPR
+#  define BLOCKEXPR(Type, Base) EXPR(Type, Base)
+#endif
+BLOCKEXPR(BlockExpr, Expr)
+#undef BLOCKEXPR
+
+#ifndef CXXBINDTEMPORARYEXPR
+#  define CXXBINDTEMPORARYEXPR(Type, Base) EXPR(Type, Base)
+#endif
+CXXBINDTEMPORARYEXPR(CXXBindTemporaryExpr, Expr)
+#undef CXXBINDTEMPORARYEXPR
+
+#ifndef CXXBOOLLITERALEXPR
+#  define CXXBOOLLITERALEXPR(Type, Base) EXPR(Type, Base)
+#endif
+CXXBOOLLITERALEXPR(CXXBoolLiteralExpr, Expr)
+#undef CXXBOOLLITERALEXPR
+
+#ifndef CXXCONSTRUCTEXPR
+#  define CXXCONSTRUCTEXPR(Type, Base) EXPR(Type, Base)
+#endif
+CXXCONSTRUCTEXPR(CXXConstructExpr, Expr)
+#ifndef CXXTEMPORARYOBJECTEXPR
+#  define CXXTEMPORARYOBJECTEXPR(Type, Base) CXXCONSTRUCTEXPR(Type, Base)
+#endif
+CXXTEMPORARYOBJECTEXPR(CXXTemporaryObjectExpr, CXXConstructExpr)
+#undef CXXTEMPORARYOBJECTEXPR
+
+STMT_RANGE(CXXConstructExpr, CXXConstructExpr, CXXTemporaryObjectExpr)
+
+#undef CXXCONSTRUCTEXPR
+
+#ifndef CXXDEFAULTARGEXPR
+#  define CXXDEFAULTARGEXPR(Type, Base) EXPR(Type, Base)
+#endif
+CXXDEFAULTARGEXPR(CXXDefaultArgExpr, Expr)
+#undef CXXDEFAULTARGEXPR
+
+#ifndef CXXDEFAULTINITEXPR
+#  define CXXDEFAULTINITEXPR(Type, Base) EXPR(Type, Base)
+#endif
+CXXDEFAULTINITEXPR(CXXDefaultInitExpr, Expr)
+#undef CXXDEFAULTINITEXPR
+
+#ifndef CXXDELETEEXPR
+#  define CXXDELETEEXPR(Type, Base) EXPR(Type, Base)
+#endif
+CXXDELETEEXPR(CXXDeleteExpr, Expr)
+#undef CXXDELETEEXPR
+
+#ifndef CXXDEPENDENTSCOPEMEMBEREXPR
+#  define CXXDEPENDENTSCOPEMEMBEREXPR(Type, Base) EXPR(Type, Base)
+#endif
+CXXDEPENDENTSCOPEMEMBEREXPR(CXXDependentScopeMemberExpr, Expr)
+#undef CXXDEPENDENTSCOPEMEMBEREXPR
+
+#ifndef CXXFOLDEXPR
+#  define CXXFOLDEXPR(Type, Base) EXPR(Type, Base)
+#endif
+CXXFOLDEXPR(CXXFoldExpr, Expr)
+#undef CXXFOLDEXPR
+
+#ifndef CXXINHERITEDCTORINITEXPR
+#  define CXXINHERITEDCTORINITEXPR(Type, Base) EXPR(Type, Base)
+#endif
+CXXINHERITEDCTORINITEXPR(CXXInheritedCtorInitExpr, Expr)
+#undef CXXINHERITEDCTORINITEXPR
+
+#ifndef CXXNEWEXPR
+#  define CXXNEWEXPR(Type, Base) EXPR(Type, Base)
+#endif
+CXXNEWEXPR(CXXNewExpr, Expr)
+#undef CXXNEWEXPR
+
+#ifndef CXXNOEXCEPTEXPR
+#  define CXXNOEXCEPTEXPR(Type, Base) EXPR(Type, Base)
+#endif
+CXXNOEXCEPTEXPR(CXXNoexceptExpr, Expr)
+#undef CXXNOEXCEPTEXPR
+
+#ifndef CXXNULLPTRLITERALEXPR
+#  define CXXNULLPTRLITERALEXPR(Type, Base) EXPR(Type, Base)
+#endif
+CXXNULLPTRLITERALEXPR(CXXNullPtrLiteralExpr, Expr)
+#undef CXXNULLPTRLITERALEXPR
+
+#ifndef CXXPARENLISTINITEXPR
+#  define CXXPARENLISTINITEXPR(Type, Base) EXPR(Type, Base)
+#endif
+CXXPARENLISTINITEXPR(CXXParenListInitExpr, Expr)
+#undef CXXPARENLISTINITEXPR
+
+#ifndef CXXPSEUDODESTRUCTOREXPR
+#  define CXXPSEUDODESTRUCTOREXPR(Type, Base) EXPR(Type, Base)
+#endif
+CXXPSEUDODESTRUCTOREXPR(CXXPseudoDestructorExpr, Expr)
+#undef CXXPSEUDODESTRUCTOREXPR
+
+#ifndef CXXREWRITTENBINARYOPERATOR
+#  define CXXREWRITTENBINARYOPERATOR(Type, Base) EXPR(Type, Base)
+#endif
+CXXREWRITTENBINARYOPERATOR(CXXRewrittenBinaryOperator, Expr)
+#undef CXXREWRITTENBINARYOPERATOR
+
+#ifndef CXXSCALARVALUEINITEXPR
+#  define CXXSCALARVALUEINITEXPR(Type, Base) EXPR(Type, Base)
+#endif
+CXXSCALARVALUEINITEXPR(CXXScalarValueInitExpr, Expr)
+#undef CXXSCALARVALUEINITEXPR
+
+#ifndef CXXSTDINITIALIZERLISTEXPR
+#  define CXXSTDINITIALIZERLISTEXPR(Type, Base) EXPR(Type, Base)
+#endif
+CXXSTDINITIALIZERLISTEXPR(CXXStdInitializerListExpr, Expr)
+#undef CXXSTDINITIALIZERLISTEXPR
+
+#ifndef CXXTHISEXPR
+#  define CXXTHISEXPR(Type, Base) EXPR(Type, Base)
+#endif
+CXXTHISEXPR(CXXThisExpr, Expr)
+#undef CXXTHISEXPR
+
+#ifndef CXXTHROWEXPR
+#  define CXXTHROWEXPR(Type, Base) EXPR(Type, Base)
+#endif
+CXXTHROWEXPR(CXXThrowExpr, Expr)
+#undef CXXTHROWEXPR
+
+#ifndef CXXTYPEIDEXPR
+#  define CXXTYPEIDEXPR(Type, Base) EXPR(Type, Base)
+#endif
+CXXTYPEIDEXPR(CXXTypeidExpr, Expr)
+#undef CXXTYPEIDEXPR
+
+#ifndef CXXUNRESOLVEDCONSTRUCTEXPR
+#  define CXXUNRESOLVEDCONSTRUCTEXPR(Type, Base) EXPR(Type, Base)
+#endif
+CXXUNRESOLVEDCONSTRUCTEXPR(CXXUnresolvedConstructExpr, Expr)
+#undef CXXUNRESOLVEDCONSTRUCTEXPR
+
+#ifndef CXXUUIDOFEXPR
+#  define CXXUUIDOFEXPR(Type, Base) EXPR(Type, Base)
+#endif
+CXXUUIDOFEXPR(CXXUuidofExpr, Expr)
+#undef CXXUUIDOFEXPR
+
+#ifndef CALLEXPR
+#  define CALLEXPR(Type, Base) EXPR(Type, Base)
+#endif
+CALLEXPR(CallExpr, Expr)
+#ifndef CUDAKERNELCALLEXPR
+#  define CUDAKERNELCALLEXPR(Type, Base) CALLEXPR(Type, Base)
+#endif
+CUDAKERNELCALLEXPR(CUDAKernelCallExpr, CallExpr)
+#undef CUDAKERNELCALLEXPR
+
+#ifndef CXXMEMBERCALLEXPR
+#  define CXXMEMBERCALLEXPR(Type, Base) CALLEXPR(Type, Base)
+#endif
+CXXMEMBERCALLEXPR(CXXMemberCallExpr, CallExpr)
+#undef CXXMEMBERCALLEXPR
+
+#ifndef CXXOPERATORCALLEXPR
+#  define CXXOPERATORCALLEXPR(Type, Base) CALLEXPR(Type, Base)
+#endif
+CXXOPERATORCALLEXPR(CXXOperatorCallExpr, CallExpr)
+#undef CXXOPERATORCALLEXPR
+
+#ifndef USERDEFINEDLITERAL
+#  define USERDEFINEDLITERAL(Type, Base) CALLEXPR(Type, Base)
+#endif
+USERDEFINEDLITERAL(UserDefinedLiteral, CallExpr)
+#undef USERDEFINEDLITERAL
+
+STMT_RANGE(CallExpr, CallExpr, UserDefinedLiteral)
+
+#undef CALLEXPR
+
+#ifndef CASTEXPR
+#  define CASTEXPR(Type, Base) EXPR(Type, Base)
+#endif
+ABSTRACT_STMT(CASTEXPR(CastExpr, Expr))
+#ifndef EXPLICITCASTEXPR
+#  define EXPLICITCASTEXPR(Type, Base) CASTEXPR(Type, Base)
+#endif
+ABSTRACT_STMT(EXPLICITCASTEXPR(ExplicitCastExpr, CastExpr))
+#ifndef BUILTINBITCASTEXPR
+#  define BUILTINBITCASTEXPR(Type, Base) EXPLICITCASTEXPR(Type, Base)
+#endif
+BUILTINBITCASTEXPR(BuiltinBitCastExpr, ExplicitCastExpr)
+#undef BUILTINBITCASTEXPR
+
+#ifndef CSTYLECASTEXPR
+#  define CSTYLECASTEXPR(Type, Base) EXPLICITCASTEXPR(Type, Base)
+#endif
+CSTYLECASTEXPR(CStyleCastExpr, ExplicitCastExpr)
+#undef CSTYLECASTEXPR
+
+#ifndef CXXFUNCTIONALCASTEXPR
+#  define CXXFUNCTIONALCASTEXPR(Type, Base) EXPLICITCASTEXPR(Type, Base)
+#endif
+CXXFUNCTIONALCASTEXPR(CXXFunctionalCastExpr, ExplicitCastExpr)
+#undef CXXFUNCTIONALCASTEXPR
+
+#ifndef CXXNAMEDCASTEXPR
+#  define CXXNAMEDCASTEXPR(Type, Base) EXPLICITCASTEXPR(Type, Base)
+#endif
+ABSTRACT_STMT(CXXNAMEDCASTEXPR(CXXNamedCastExpr, ExplicitCastExpr))
+#ifndef CXXADDRSPACECASTEXPR
+#  define CXXADDRSPACECASTEXPR(Type, Base) CXXNAMEDCASTEXPR(Type, Base)
+#endif
+CXXADDRSPACECASTEXPR(CXXAddrspaceCastExpr, CXXNamedCastExpr)
+#undef CXXADDRSPACECASTEXPR
+
+#ifndef CXXCONSTCASTEXPR
+#  define CXXCONSTCASTEXPR(Type, Base) CXXNAMEDCASTEXPR(Type, Base)
+#endif
+CXXCONSTCASTEXPR(CXXConstCastExpr, CXXNamedCastExpr)
+#undef CXXCONSTCASTEXPR
+
+#ifndef CXXDYNAMICCASTEXPR
+#  define CXXDYNAMICCASTEXPR(Type, Base) CXXNAMEDCASTEXPR(Type, Base)
+#endif
+CXXDYNAMICCASTEXPR(CXXDynamicCastExpr, CXXNamedCastExpr)
+#undef CXXDYNAMICCASTEXPR
+
+#ifndef CXXREINTERPRETCASTEXPR
+#  define CXXREINTERPRETCASTEXPR(Type, Base) CXXNAMEDCASTEXPR(Type, Base)
+#endif
+CXXREINTERPRETCASTEXPR(CXXReinterpretCastExpr, CXXNamedCastExpr)
+#undef CXXREINTERPRETCASTEXPR
+
+#ifndef CXXSTATICCASTEXPR
+#  define CXXSTATICCASTEXPR(Type, Base) CXXNAMEDCASTEXPR(Type, Base)
+#endif
+CXXSTATICCASTEXPR(CXXStaticCastExpr, CXXNamedCastExpr)
+#undef CXXSTATICCASTEXPR
+
+STMT_RANGE(CXXNamedCastExpr, CXXAddrspaceCastExpr, CXXStaticCastExpr)
+
+#undef CXXNAMEDCASTEXPR
+
+STMT_RANGE(ExplicitCastExpr, BuiltinBitCastExpr, ObjCBridgedCastExpr)
+
+#undef EXPLICITCASTEXPR
+
+#ifndef IMPLICITCASTEXPR
+#  define IMPLICITCASTEXPR(Type, Base) CASTEXPR(Type, Base)
+#endif
+IMPLICITCASTEXPR(ImplicitCastExpr, CastExpr)
+#undef IMPLICITCASTEXPR
+
+STMT_RANGE(CastExpr, BuiltinBitCastExpr, ImplicitCastExpr)
+
+#undef CASTEXPR
+
+#ifndef CHARACTERLITERAL
+#  define CHARACTERLITERAL(Type, Base) EXPR(Type, Base)
+#endif
+CHARACTERLITERAL(CharacterLiteral, Expr)
+#undef CHARACTERLITERAL
+
+#ifndef CHOOSEEXPR
+#  define CHOOSEEXPR(Type, Base) EXPR(Type, Base)
+#endif
+CHOOSEEXPR(ChooseExpr, Expr)
+#undef CHOOSEEXPR
+
+#ifndef COMPOUNDLITERALEXPR
+#  define COMPOUNDLITERALEXPR(Type, Base) EXPR(Type, Base)
+#endif
+COMPOUNDLITERALEXPR(CompoundLiteralExpr, Expr)
+#undef COMPOUNDLITERALEXPR
+
+#ifndef CONVERTVECTOREXPR
+#  define CONVERTVECTOREXPR(Type, Base) EXPR(Type, Base)
+#endif
+CONVERTVECTOREXPR(ConvertVectorExpr, Expr)
+#undef CONVERTVECTOREXPR
+
+#ifndef COROUTINESUSPENDEXPR
+#  define COROUTINESUSPENDEXPR(Type, Base) EXPR(Type, Base)
+#endif
+ABSTRACT_STMT(COROUTINESUSPENDEXPR(CoroutineSuspendExpr, Expr))
+#ifndef COAWAITEXPR
+#  define COAWAITEXPR(Type, Base) COROUTINESUSPENDEXPR(Type, Base)
+#endif
+COAWAITEXPR(CoawaitExpr, CoroutineSuspendExpr)
+#undef COAWAITEXPR
+
+#ifndef COYIELDEXPR
+#  define COYIELDEXPR(Type, Base) COROUTINESUSPENDEXPR(Type, Base)
+#endif
+COYIELDEXPR(CoyieldExpr, CoroutineSuspendExpr)
+#undef COYIELDEXPR
+
+STMT_RANGE(CoroutineSuspendExpr, CoawaitExpr, CoyieldExpr)
+
+#undef COROUTINESUSPENDEXPR
+
+#ifndef DECLREFEXPR
+#  define DECLREFEXPR(Type, Base) EXPR(Type, Base)
+#endif
+DECLREFEXPR(DeclRefExpr, Expr)
+#undef DECLREFEXPR
+
+#ifndef DEPENDENTCOAWAITEXPR
+#  define DEPENDENTCOAWAITEXPR(Type, Base) EXPR(Type, Base)
+#endif
+DEPENDENTCOAWAITEXPR(DependentCoawaitExpr, Expr)
+#undef DEPENDENTCOAWAITEXPR
+
+#ifndef DEPENDENTSCOPEDECLREFEXPR
+#  define DEPENDENTSCOPEDECLREFEXPR(Type, Base) EXPR(Type, Base)
+#endif
+DEPENDENTSCOPEDECLREFEXPR(DependentScopeDeclRefExpr, Expr)
+#undef DEPENDENTSCOPEDECLREFEXPR
+
+#ifndef DESIGNATEDINITEXPR
+#  define DESIGNATEDINITEXPR(Type, Base) EXPR(Type, Base)
+#endif
+DESIGNATEDINITEXPR(DesignatedInitExpr, Expr)
+#undef DESIGNATEDINITEXPR
+
+#ifndef DESIGNATEDINITUPDATEEXPR
+#  define DESIGNATEDINITUPDATEEXPR(Type, Base) EXPR(Type, Base)
+#endif
+DESIGNATEDINITUPDATEEXPR(DesignatedInitUpdateExpr, Expr)
+#undef DESIGNATEDINITUPDATEEXPR
+
+#ifndef EXPRESSIONTRAITEXPR
+#  define EXPRESSIONTRAITEXPR(Type, Base) EXPR(Type, Base)
+#endif
+EXPRESSIONTRAITEXPR(ExpressionTraitExpr, Expr)
+#undef EXPRESSIONTRAITEXPR
+
+#ifndef EXTVECTORELEMENTEXPR
+#  define EXTVECTORELEMENTEXPR(Type, Base) EXPR(Type, Base)
+#endif
+EXTVECTORELEMENTEXPR(ExtVectorElementExpr, Expr)
+#undef EXTVECTORELEMENTEXPR
+
+#ifndef FIXEDPOINTLITERAL
+#  define FIXEDPOINTLITERAL(Type, Base) EXPR(Type, Base)
+#endif
+FIXEDPOINTLITERAL(FixedPointLiteral, Expr)
+#undef FIXEDPOINTLITERAL
+
+#ifndef FLOATINGLITERAL
+#  define FLOATINGLITERAL(Type, Base) EXPR(Type, Base)
+#endif
+FLOATINGLITERAL(FloatingLiteral, Expr)
+#undef FLOATINGLITERAL
+
+#ifndef FULLEXPR
+#  define FULLEXPR(Type, Base) EXPR(Type, Base)
+#endif
+ABSTRACT_STMT(FULLEXPR(FullExpr, Expr))
+#ifndef CONSTANTEXPR
+#  define CONSTANTEXPR(Type, Base) FULLEXPR(Type, Base)
+#endif
+CONSTANTEXPR(ConstantExpr, FullExpr)
+#undef CONSTANTEXPR
+
+#ifndef EXPRWITHCLEANUPS
+#  define EXPRWITHCLEANUPS(Type, Base) FULLEXPR(Type, Base)
+#endif
+EXPRWITHCLEANUPS(ExprWithCleanups, FullExpr)
+#undef EXPRWITHCLEANUPS
+
+STMT_RANGE(FullExpr, ConstantExpr, ExprWithCleanups)
+
+#undef FULLEXPR
+
+#ifndef FUNCTIONPARMPACKEXPR
+#  define FUNCTIONPARMPACKEXPR(Type, Base) EXPR(Type, Base)
+#endif
+FUNCTIONPARMPACKEXPR(FunctionParmPackExpr, Expr)
+#undef FUNCTIONPARMPACKEXPR
+
+#ifndef GNUNULLEXPR
+#  define GNUNULLEXPR(Type, Base) EXPR(Type, Base)
+#endif
+GNUNULLEXPR(GNUNullExpr, Expr)
+#undef GNUNULLEXPR
+
+#ifndef GENERICSELECTIONEXPR
+#  define GENERICSELECTIONEXPR(Type, Base) EXPR(Type, Base)
+#endif
+GENERICSELECTIONEXPR(GenericSelectionExpr, Expr)
+#undef GENERICSELECTIONEXPR
+
+#ifndef IMAGINARYLITERAL
+#  define IMAGINARYLITERAL(Type, Base) EXPR(Type, Base)
+#endif
+IMAGINARYLITERAL(ImaginaryLiteral, Expr)
+#undef IMAGINARYLITERAL
+
+#ifndef IMPLICITVALUEINITEXPR
+#  define IMPLICITVALUEINITEXPR(Type, Base) EXPR(Type, Base)
+#endif
+IMPLICITVALUEINITEXPR(ImplicitValueInitExpr, Expr)
+#undef IMPLICITVALUEINITEXPR
+
+#ifndef INITLISTEXPR
+#  define INITLISTEXPR(Type, Base) EXPR(Type, Base)
+#endif
+INITLISTEXPR(InitListExpr, Expr)
+#undef INITLISTEXPR
+
+#ifndef INTEGERLITERAL
+#  define INTEGERLITERAL(Type, Base) EXPR(Type, Base)
+#endif
+INTEGERLITERAL(IntegerLiteral, Expr)
+#undef INTEGERLITERAL
+
+#ifndef LAMBDAEXPR
+#  define LAMBDAEXPR(Type, Base) EXPR(Type, Base)
+#endif
+LAMBDAEXPR(LambdaExpr, Expr)
+#undef LAMBDAEXPR
+
+#ifndef MSPROPERTYREFEXPR
+#  define MSPROPERTYREFEXPR(Type, Base) EXPR(Type, Base)
+#endif
+MSPROPERTYREFEXPR(MSPropertyRefExpr, Expr)
+#undef MSPROPERTYREFEXPR
+
+#ifndef MSPROPERTYSUBSCRIPTEXPR
+#  define MSPROPERTYSUBSCRIPTEXPR(Type, Base) EXPR(Type, Base)
+#endif
+MSPROPERTYSUBSCRIPTEXPR(MSPropertySubscriptExpr, Expr)
+#undef MSPROPERTYSUBSCRIPTEXPR
+
+#ifndef MATERIALIZETEMPORARYEXPR
+#  define MATERIALIZETEMPORARYEXPR(Type, Base) EXPR(Type, Base)
+#endif
+MATERIALIZETEMPORARYEXPR(MaterializeTemporaryExpr, Expr)
+#undef MATERIALIZETEMPORARYEXPR
+
+#ifndef MATRIXSUBSCRIPTEXPR
+#  define MATRIXSUBSCRIPTEXPR(Type, Base) EXPR(Type, Base)
+#endif
+MATRIXSUBSCRIPTEXPR(MatrixSubscriptExpr, Expr)
+#undef MATRIXSUBSCRIPTEXPR
+
+#ifndef MEMBEREXPR
+#  define MEMBEREXPR(Type, Base) EXPR(Type, Base)
+#endif
+MEMBEREXPR(MemberExpr, Expr)
+#undef MEMBEREXPR
+
+#ifndef NOINITEXPR
+#  define NOINITEXPR(Type, Base) EXPR(Type, Base)
+#endif
+NOINITEXPR(NoInitExpr, Expr)
+#undef NOINITEXPR
+
+// Removed OMP nodes
+// Removed OBJC nodes
+
+#ifndef OFFSETOFEXPR
+#  define OFFSETOFEXPR(Type, Base) EXPR(Type, Base)
+#endif
+OFFSETOFEXPR(OffsetOfExpr, Expr)
+#undef OFFSETOFEXPR
+
+#ifndef OPAQUEVALUEEXPR
+#  define OPAQUEVALUEEXPR(Type, Base) EXPR(Type, Base)
+#endif
+OPAQUEVALUEEXPR(OpaqueValueExpr, Expr)
+#undef OPAQUEVALUEEXPR
+
+#ifndef OVERLOADEXPR
+#  define OVERLOADEXPR(Type, Base) EXPR(Type, Base)
+#endif
+ABSTRACT_STMT(OVERLOADEXPR(OverloadExpr, Expr))
+#ifndef UNRESOLVEDLOOKUPEXPR
+#  define UNRESOLVEDLOOKUPEXPR(Type, Base) OVERLOADEXPR(Type, Base)
+#endif
+UNRESOLVEDLOOKUPEXPR(UnresolvedLookupExpr, OverloadExpr)
+#undef UNRESOLVEDLOOKUPEXPR
+
+#ifndef UNRESOLVEDMEMBEREXPR
+#  define UNRESOLVEDMEMBEREXPR(Type, Base) OVERLOADEXPR(Type, Base)
+#endif
+UNRESOLVEDMEMBEREXPR(UnresolvedMemberExpr, OverloadExpr)
+#undef UNRESOLVEDMEMBEREXPR
+
+STMT_RANGE(OverloadExpr, UnresolvedLookupExpr, UnresolvedMemberExpr)
+
+#undef OVERLOADEXPR
+
+#ifndef PACKEXPANSIONEXPR
+#  define PACKEXPANSIONEXPR(Type, Base) EXPR(Type, Base)
+#endif
+PACKEXPANSIONEXPR(PackExpansionExpr, Expr)
+#undef PACKEXPANSIONEXPR
+
+#ifndef PARENEXPR
+#  define PARENEXPR(Type, Base) EXPR(Type, Base)
+#endif
+PARENEXPR(ParenExpr, Expr)
+#undef PARENEXPR
+
+#ifndef PARENLISTEXPR
+#  define PARENLISTEXPR(Type, Base) EXPR(Type, Base)
+#endif
+PARENLISTEXPR(ParenListExpr, Expr)
+#undef PARENLISTEXPR
+
+#ifndef PREDEFINEDEXPR
+#  define PREDEFINEDEXPR(Type, Base) EXPR(Type, Base)
+#endif
+PREDEFINEDEXPR(PredefinedExpr, Expr)
+#undef PREDEFINEDEXPR
+
+#ifndef PSEUDOOBJECTEXPR
+#  define PSEUDOOBJECTEXPR(Type, Base) EXPR(Type, Base)
+#endif
+PSEUDOOBJECTEXPR(PseudoObjectExpr, Expr)
+#undef PSEUDOOBJECTEXPR
+
+#ifndef RECOVERYEXPR
+#  define RECOVERYEXPR(Type, Base) EXPR(Type, Base)
+#endif
+RECOVERYEXPR(RecoveryExpr, Expr)
+#undef RECOVERYEXPR
+
+#ifndef SYCLUNIQUESTABLENAMEEXPR
+#  define SYCLUNIQUESTABLENAMEEXPR(Type, Base) EXPR(Type, Base)
+#endif
+SYCLUNIQUESTABLENAMEEXPR(SYCLUniqueStableNameExpr, Expr)
+#undef SYCLUNIQUESTABLENAMEEXPR
+
+#ifndef SHUFFLEVECTOREXPR
+#  define SHUFFLEVECTOREXPR(Type, Base) EXPR(Type, Base)
+#endif
+SHUFFLEVECTOREXPR(ShuffleVectorExpr, Expr)
+#undef SHUFFLEVECTOREXPR
+
+#ifndef SIZEOFPACKEXPR
+#  define SIZEOFPACKEXPR(Type, Base) EXPR(Type, Base)
+#endif
+SIZEOFPACKEXPR(SizeOfPackExpr, Expr)
+#undef SIZEOFPACKEXPR
+
+#ifndef SOURCELOCEXPR
+#  define SOURCELOCEXPR(Type, Base) EXPR(Type, Base)
+#endif
+SOURCELOCEXPR(SourceLocExpr, Expr)
+#undef SOURCELOCEXPR
+
+#ifndef STMTEXPR
+#  define STMTEXPR(Type, Base) EXPR(Type, Base)
+#endif
+STMTEXPR(StmtExpr, Expr)
+#undef STMTEXPR
+
+#ifndef STRINGLITERAL
+#  define STRINGLITERAL(Type, Base) EXPR(Type, Base)
+#endif
+STRINGLITERAL(StringLiteral, Expr)
+#undef STRINGLITERAL
+
+#ifndef SUBSTNONTYPETEMPLATEPARMEXPR
+#  define SUBSTNONTYPETEMPLATEPARMEXPR(Type, Base) EXPR(Type, Base)
+#endif
+SUBSTNONTYPETEMPLATEPARMEXPR(SubstNonTypeTemplateParmExpr, Expr)
+#undef SUBSTNONTYPETEMPLATEPARMEXPR
+
+#ifndef SUBSTNONTYPETEMPLATEPARMPACKEXPR
+#  define SUBSTNONTYPETEMPLATEPARMPACKEXPR(Type, Base) EXPR(Type, Base)
+#endif
+SUBSTNONTYPETEMPLATEPARMPACKEXPR(SubstNonTypeTemplateParmPackExpr, Expr)
+#undef SUBSTNONTYPETEMPLATEPARMPACKEXPR
+
+#ifndef TYPETRAITEXPR
+#  define TYPETRAITEXPR(Type, Base) EXPR(Type, Base)
+#endif
+TYPETRAITEXPR(TypeTraitExpr, Expr)
+#undef TYPETRAITEXPR
+
+#ifndef TYPOEXPR
+#  define TYPOEXPR(Type, Base) EXPR(Type, Base)
+#endif
+TYPOEXPR(TypoExpr, Expr)
+#undef TYPOEXPR
+
+#ifndef UNARYEXPRORTYPETRAITEXPR
+#  define UNARYEXPRORTYPETRAITEXPR(Type, Base) EXPR(Type, Base)
+#endif
+UNARYEXPRORTYPETRAITEXPR(UnaryExprOrTypeTraitExpr, Expr)
+#undef UNARYEXPRORTYPETRAITEXPR
+
+#ifndef UNARYOPERATOR
+#  define UNARYOPERATOR(Type, Base) EXPR(Type, Base)
+#endif
+UNARYOPERATOR(UnaryOperator, Expr)
+#undef UNARYOPERATOR
+
+#ifndef VAARGEXPR
+#  define VAARGEXPR(Type, Base) EXPR(Type, Base)
+#endif
+VAARGEXPR(VAArgExpr, Expr)
+#undef VAARGEXPR
+
+STMT_RANGE(Expr, BinaryConditionalOperator, VAArgExpr)
+
+#undef EXPR
+
+#ifndef LABELSTMT
+#  define LABELSTMT(Type, Base) VALUESTMT(Type, Base)
+#endif
+LABELSTMT(LabelStmt, ValueStmt)
+#undef LABELSTMT
+
+STMT_RANGE(ValueStmt, AttributedStmt, LabelStmt)
+
+#undef VALUESTMT
+
+#ifndef WHILESTMT
+#  define WHILESTMT(Type, Base) STMT(Type, Base)
+#endif
+WHILESTMT(WhileStmt, Stmt)
+#undef WHILESTMT
+
+LAST_STMT_RANGE(Stmt, GCCAsmStmt, WhileStmt)
+
+#undef STMT
+#undef STMT_RANGE
+#undef LAST_STMT_RANGE
+#undef ABSTRACT_STMT
diff --git a/src/CppParser/Types.h b/src/CppParser/Types.h
index e8a05f32..8f6c8c3d 100644
--- a/src/CppParser/Types.h
+++ b/src/CppParser/Types.h
@@ -9,324 +9,326 @@
 
 #include "Helpers.h"
 
-namespace CppSharp { namespace CppParser { namespace AST {
-
-    enum class TypeKind
-    {
-        Tag,
-        Array,
-        Function,
-        Pointer,
-        MemberPointer,
-        Typedef,
-        Attributed,
-        Decayed,
-        TemplateSpecialization,
-        DependentTemplateSpecialization,
-        TemplateParameter,
-        TemplateParameterSubstitution,
-        InjectedClassName,
-        DependentName,
-        PackExpansion,
-        Builtin,
-        UnaryTransform,
-        UnresolvedUsing,
-        Vector
-    };
+namespace CppSharp::CppParser::AST {
+
+enum class TypeKind
+{
+    Tag,
+    Array,
+    Function,
+    Pointer,
+    MemberPointer,
+    Typedef,
+    Attributed,
+    Decayed,
+    TemplateSpecialization,
+    DependentTemplateSpecialization,
+    TemplateParameter,
+    TemplateParameterSubstitution,
+    InjectedClassName,
+    DependentName,
+    PackExpansion,
+    Builtin,
+    UnaryTransform,
+    UnresolvedUsing,
+    Vector
+};
 
 #define DECLARE_TYPE_KIND(kind) \
     kind##Type();
 
-    class CS_API Type
-    {
-    public:
-        Type(TypeKind kind);
-        Type(const Type&);
-
-        TypeKind kind;
-        bool isDependent;
-    };
-
-    struct CS_API TypeQualifiers
-    {
-        bool isConst;
-        bool isVolatile;
-        bool isRestrict;
-    };
-
-    struct CS_API QualifiedType
-    {
-        QualifiedType();
-        Type* type;
-        TypeQualifiers qualifiers;
-    };
-
-    class Declaration;
-
-    class CS_API TagType : public Type
-    {
-    public:
-        DECLARE_TYPE_KIND(Tag)
-        Declaration* declaration;
-    };
-
-    class CS_API ArrayType : public Type
-    {
-    public:
-        enum class ArraySize
-        {
-            Constant,
-            Variable,
-            Dependent,
-            Incomplete
-        };
-
-        DECLARE_TYPE_KIND(Array)
-        QualifiedType qualifiedType;
-        ArraySize sizeType;
-        long size;
-        long elementSize;
-    };
-
-    class Parameter;
-
-    enum class CallingConvention
-    {
-        Default,
-        C,
-        StdCall,
-        ThisCall,
-        FastCall,
-        Unknown
-    };
-
-    enum class ExceptionSpecType
+class CS_API Type
+{
+public:
+    Type(TypeKind kind);
+    Type(const Type&);
+
+    TypeKind kind;
+    bool isDependent = false;
+};
+
+struct CS_API TypeQualifiers
+{
+    bool isConst = false;
+    bool isVolatile = false;
+    bool isRestrict = false;
+};
+
+struct CS_API QualifiedType
+{
+    QualifiedType();
+    Type* type = nullptr;
+    Type* desugaredType = nullptr;
+    void* typeAliasDeclId = nullptr;
+    TypeQualifiers qualifiers;
+};
+
+class Declaration;
+
+class CS_API TagType : public Type
+{
+public:
+    DECLARE_TYPE_KIND(Tag)
+    Declaration* declaration;
+};
+
+class CS_API ArrayType : public Type
+{
+public:
+    enum class ArraySize
     {
-        None,
-        DynamicNone,
-        Dynamic,
-        MSAny,
-        BasicNoexcept,
-        DependentNoexcept,
-        NoexceptFalse,
-        NoexceptTrue,
-        Unevaluated,
-        Uninstantiated,
-        Unparsed
+        Constant,
+        Variable,
+        Dependent,
+        Incomplete
     };
 
-    class CS_API FunctionType : public Type
+    DECLARE_TYPE_KIND(Array)
+    QualifiedType qualifiedType;
+    ArraySize sizeType;
+    long size;
+    long elementSize;
+};
+
+class Parameter;
+
+enum class CallingConvention
+{
+    Default,
+    C,
+    StdCall,
+    ThisCall,
+    FastCall,
+    Unknown
+};
+
+enum class ExceptionSpecType
+{
+    None,
+    DynamicNone,
+    Dynamic,
+    MSAny,
+    BasicNoexcept,
+    DependentNoexcept,
+    NoexceptFalse,
+    NoexceptTrue,
+    Unevaluated,
+    Uninstantiated,
+    Unparsed
+};
+
+class CS_API FunctionType : public Type
+{
+public:
+    DECLARE_TYPE_KIND(Function)
+    ~FunctionType();
+    QualifiedType returnType;
+    CallingConvention callingConvention;
+    ExceptionSpecType exceptionSpecType;
+    VECTOR(Parameter*, Parameters)
+};
+
+class CS_API PointerType : public Type
+{
+public:
+    enum struct TypeModifier
     {
-    public:
-        DECLARE_TYPE_KIND(Function)
-        ~FunctionType();
-        QualifiedType returnType;
-        CallingConvention callingConvention;
-        ExceptionSpecType exceptionSpecType;
-        VECTOR(Parameter*, Parameters)
-    };
-
-    class CS_API PointerType : public Type
-    {
-    public:
-        enum struct TypeModifier
-        {
-            Value,
-            Pointer,
-            LVReference,
-            RVReference
-        };
-
-        DECLARE_TYPE_KIND(Pointer)
-        QualifiedType qualifiedPointee;
-        TypeModifier modifier;
-    };
-
-    class CS_API MemberPointerType : public Type
-    {
-    public:
-        DECLARE_TYPE_KIND(MemberPointer)
-        QualifiedType pointee;
-    };
-
-    class TypedefNameDecl;
-
-    class CS_API TypedefType : public Type
-    {
-    public:
-        TypedefType();
-        TypedefNameDecl* declaration;
-    };
-
-    class CS_API AttributedType : public Type
-    {
-    public:
-        DECLARE_TYPE_KIND(Attributed)
-        QualifiedType modified;
-        QualifiedType equivalent;
-    };
-
-    class CS_API DecayedType : public Type
-    {
-    public:
-        DECLARE_TYPE_KIND(Decayed)
-        QualifiedType decayed;
-        QualifiedType original;
-        QualifiedType pointee;
-    };
-
-    struct CS_API TemplateArgument
-    {
-        TemplateArgument();
-
-        enum struct ArgumentKind
-        {
-            Type,
-            Declaration,
-            NullPtr,
-            Integral,
-            Template,
-            TemplateExpansion,
-            Expression,
-            Pack
-        };
-
-        ArgumentKind kind;
-        QualifiedType type;
-        Declaration* declaration;
-        long integral;
-    };
-
-    class Template;
-
-    class CS_API TemplateSpecializationType : public Type
-    {
-    public:
-        TemplateSpecializationType();
-        TemplateSpecializationType(const TemplateSpecializationType&);
-        ~TemplateSpecializationType();
-
-        VECTOR(TemplateArgument, Arguments)
-        Template* _template;
-        QualifiedType desugared;
-    };
-
-    class CS_API DependentTemplateSpecializationType : public Type
-    {
-    public:
-        DependentTemplateSpecializationType();
-        DependentTemplateSpecializationType(const DependentTemplateSpecializationType&);
-        ~DependentTemplateSpecializationType();
-
-        VECTOR(TemplateArgument, Arguments)
-        QualifiedType desugared;
-    };
-
-    class TypeTemplateParameter;
-
-    class CS_API TemplateParameterType : public Type
-    {
-    public:
-        DECLARE_TYPE_KIND(TemplateParameter)
-        ~TemplateParameterType();
-        TypeTemplateParameter* parameter;
-        unsigned int depth;
-        unsigned int index;
-        bool isParameterPack;
-    };
-
-    class CS_API TemplateParameterSubstitutionType : public Type
-    {
-    public:
-        DECLARE_TYPE_KIND(TemplateParameterSubstitution)
-        QualifiedType replacement;
-        TemplateParameterType* replacedParameter;
-    };
-
-    class Class;
-
-    class CS_API InjectedClassNameType : public Type
-    {
-    public:
-        DECLARE_TYPE_KIND(InjectedClassName)
-        QualifiedType injectedSpecializationType;
-        Class* _class;
-    };
-
-    class CS_API DependentNameType : public Type
-    {
-    public:
-        DECLARE_TYPE_KIND(DependentName)
-        ~DependentNameType();
-        QualifiedType qualifier;
-        std::string identifier;
-    };
-
-    class CS_API PackExpansionType : public Type
-    {
-    public:
-        DECLARE_TYPE_KIND(PackExpansion)
-    };
-
-    class CS_API UnaryTransformType : public Type
-    {
-    public:
-        DECLARE_TYPE_KIND(UnaryTransform)
-        QualifiedType desugared;
-        QualifiedType baseType;
-    };
-
-    class UnresolvedUsingTypename;
-
-    class CS_API UnresolvedUsingType : public Type
-    {
-    public:
-        DECLARE_TYPE_KIND(UnresolvedUsing)
-        UnresolvedUsingTypename* declaration;
-    };
-
-    class CS_API VectorType : public Type
-    {
-    public:
-        DECLARE_TYPE_KIND(Vector)
-        QualifiedType elementType;
-        unsigned numElements;
-    };
-
-    enum class PrimitiveType
-    {
-        Null,
-        Void,
-        Bool,
-        WideChar,
-        Char,
-        SChar,
-        UChar,
-        Char16,
-        Char32,
-        Short,
-        UShort,
-        Int,
-        UInt,
-        Long,
-        ULong,
-        LongLong,
-        ULongLong,
-        Int128,
-        UInt128,
-        Half,
-        Float,
-        Double,
-        LongDouble,
-        Float128,
-        IntPtr
+        Value,
+        Pointer,
+        LVReference,
+        RVReference
     };
 
-    class CS_API BuiltinType : public Type
+    DECLARE_TYPE_KIND(Pointer)
+    QualifiedType qualifiedPointee;
+    TypeModifier modifier;
+};
+
+class CS_API MemberPointerType : public Type
+{
+public:
+    DECLARE_TYPE_KIND(MemberPointer)
+    QualifiedType pointee;
+};
+
+class TypedefNameDecl;
+
+class CS_API TypedefType : public Type
+{
+public:
+    TypedefType();
+    TypedefNameDecl* declaration;
+};
+
+class CS_API AttributedType : public Type
+{
+public:
+    DECLARE_TYPE_KIND(Attributed)
+    QualifiedType modified;
+    QualifiedType equivalent;
+};
+
+class CS_API DecayedType : public Type
+{
+public:
+    DECLARE_TYPE_KIND(Decayed)
+    QualifiedType decayed;
+    QualifiedType original;
+    QualifiedType pointee;
+};
+
+struct CS_API TemplateArgument
+{
+    TemplateArgument();
+
+    enum struct ArgumentKind
     {
-    public:
-        DECLARE_TYPE_KIND(Builtin)
-        PrimitiveType type;
+        Type,
+        Declaration,
+        NullPtr,
+        Integral,
+        Template,
+        TemplateExpansion,
+        Expression,
+        Pack
     };
 
-}}} // namespace CppSharp::CppParser::AST
+    ArgumentKind kind;
+    QualifiedType type;
+    Declaration* declaration;
+    long integral;
+};
+
+class Template;
+
+class CS_API TemplateSpecializationType : public Type
+{
+public:
+    TemplateSpecializationType();
+    TemplateSpecializationType(const TemplateSpecializationType&);
+    ~TemplateSpecializationType();
+
+    VECTOR(TemplateArgument, Arguments)
+    Template* _template;
+    QualifiedType desugared;
+};
+
+class CS_API DependentTemplateSpecializationType : public Type
+{
+public:
+    DependentTemplateSpecializationType();
+    DependentTemplateSpecializationType(const DependentTemplateSpecializationType&);
+    ~DependentTemplateSpecializationType();
+
+    VECTOR(TemplateArgument, Arguments)
+    QualifiedType desugared;
+};
+
+class TypeTemplateParameter;
+
+class CS_API TemplateParameterType : public Type
+{
+public:
+    DECLARE_TYPE_KIND(TemplateParameter)
+    ~TemplateParameterType();
+    TypeTemplateParameter* parameter;
+    unsigned int depth;
+    unsigned int index;
+    bool isParameterPack;
+};
+
+class CS_API TemplateParameterSubstitutionType : public Type
+{
+public:
+    DECLARE_TYPE_KIND(TemplateParameterSubstitution)
+    QualifiedType replacement;
+    TemplateParameterType* replacedParameter;
+};
+
+class Class;
+
+class CS_API InjectedClassNameType : public Type
+{
+public:
+    DECLARE_TYPE_KIND(InjectedClassName)
+    QualifiedType injectedSpecializationType;
+    Class* _class;
+};
+
+class CS_API DependentNameType : public Type
+{
+public:
+    DECLARE_TYPE_KIND(DependentName)
+    ~DependentNameType();
+    QualifiedType qualifier;
+    std::string identifier;
+};
+
+class CS_API PackExpansionType : public Type
+{
+public:
+    DECLARE_TYPE_KIND(PackExpansion)
+};
+
+class CS_API UnaryTransformType : public Type
+{
+public:
+    DECLARE_TYPE_KIND(UnaryTransform)
+    QualifiedType desugared;
+    QualifiedType baseType;
+};
+
+class UnresolvedUsingTypename;
+
+class CS_API UnresolvedUsingType : public Type
+{
+public:
+    DECLARE_TYPE_KIND(UnresolvedUsing)
+    UnresolvedUsingTypename* declaration;
+};
+
+class CS_API VectorType : public Type
+{
+public:
+    DECLARE_TYPE_KIND(Vector)
+    QualifiedType elementType;
+    unsigned numElements;
+};
+
+enum class PrimitiveType
+{
+    Null,
+    Void,
+    Bool,
+    WideChar,
+    Char,
+    SChar,
+    UChar,
+    Char16,
+    Char32,
+    Short,
+    UShort,
+    Int,
+    UInt,
+    Long,
+    ULong,
+    LongLong,
+    ULongLong,
+    Int128,
+    UInt128,
+    Half,
+    Float,
+    Double,
+    LongDouble,
+    Float128,
+    IntPtr
+};
+
+class CS_API BuiltinType : public Type
+{
+public:
+    DECLARE_TYPE_KIND(Builtin)
+    PrimitiveType type;
+};
+
+} // namespace CppSharp::CppParser::AST
diff --git a/src/Generator.Tests/GeneratorTest.cs b/src/Generator.Tests/GeneratorTest.cs
index 3805353a..709d943e 100644
--- a/src/Generator.Tests/GeneratorTest.cs
+++ b/src/Generator.Tests/GeneratorTest.cs
@@ -28,6 +28,7 @@ namespace CppSharp.Utils
             options.Quiet = true;
             options.GenerateDebugOutput = true;
             options.CheckSymbols = true;
+            driver.ParserOptions.SkipSystemHeaders = false;
             var testModule = options.AddModule(name);
 
             Diagnostics.Message("");