[MSVC][Demangling] Make Microsoft demangling more configurable

The goal for us is to be able to demangle Microsoft mangled function
names toward something that reads more like AST function names and
Itanium demangled names. All defaults remain the same and no existing
code or tool user should be impacted by the change.

* Make the OF_NoTagSpecifier flag accessible from `microsoftDemangle`
* Add a flag to skip the "void" keyword when there is no parameter
* Add a flag to skip the type descriptions
* Respect MSDF_NoCallingConvention with function pointers
* Add unit tests.

Assisted-by: Claude
--
CPP-7550
diff --git a/llvm/include/llvm/Demangle/Demangle.h b/llvm/include/llvm/Demangle/Demangle.h
index d9b08b2..322340c 100644
--- a/llvm/include/llvm/Demangle/Demangle.h
+++ b/llvm/include/llvm/Demangle/Demangle.h
@@ -45,6 +45,14 @@
   MSDF_NoReturnType = 1 << 3,
   MSDF_NoMemberType = 1 << 4,
   MSDF_NoVariableType = 1 << 5,
+  MSDF_NoTagSpecifier = 1 << 6,
+  /// Don't write "(void)" for functions that take no parameters.
+  MSDF_NoVoidParameter = 1 << 7,
+  /// Don't add decoration to RTTI type descriptors:
+  ///   struct MyStruct `RTTI Type Descriptor Name'
+  /// will instead output
+  ///   struct MyStruct
+  MSDF_NoDecorativeRTTITypeDescriptor = 1 << 8,
 };
 
 /// Demangles the Microsoft symbol pointed at by mangled_name and returns it.
diff --git a/llvm/include/llvm/Demangle/MicrosoftDemangleNodes.h b/llvm/include/llvm/Demangle/MicrosoftDemangleNodes.h
index 711aa70..9a3ebf6 100644
--- a/llvm/include/llvm/Demangle/MicrosoftDemangleNodes.h
+++ b/llvm/include/llvm/Demangle/MicrosoftDemangleNodes.h
@@ -74,12 +74,14 @@
 
 enum OutputFlags {
   OF_Default = 0,
-  OF_NoCallingConvention = 1,
-  OF_NoTagSpecifier = 2,
-  OF_NoAccessSpecifier = 4,
-  OF_NoMemberType = 8,
-  OF_NoReturnType = 16,
-  OF_NoVariableType = 32,
+  OF_NoCallingConvention = 1 << 0,
+  OF_NoTagSpecifier = 1 << 1,
+  OF_NoAccessSpecifier = 1 << 2,
+  OF_NoMemberType = 1 << 3,
+  OF_NoReturnType = 1 << 4,
+  OF_NoVariableType = 1 << 5,
+  OF_NoVoidParameter = 1 << 6,
+  OF_NoDecorativeRTTITypeDescriptor = 1 << 7,
 };
 
 // Types
@@ -748,10 +750,20 @@
     return N->kind() == NodeKind::VariableSymbol;
   }
 
+  virtual bool shouldOutputName(OutputFlags Flags) const { return true; }
+
   StorageClass SC = StorageClass::None;
   TypeNode *Type = nullptr;
 };
 
+struct DEMANGLE_ABI TypeSymbolNode : public VariableSymbolNode {
+  using VariableSymbolNode::VariableSymbolNode;
+
+  bool shouldOutputName(OutputFlags Flags) const override {
+    return !(Flags & OF_NoDecorativeRTTITypeDescriptor);
+  }
+};
+
 struct DEMANGLE_ABI FunctionSymbolNode : public SymbolNode {
   FunctionSymbolNode() : SymbolNode(NodeKind::FunctionSymbol) {}
 
diff --git a/llvm/lib/Demangle/MicrosoftDemangle.cpp b/llvm/lib/Demangle/MicrosoftDemangle.cpp
index 769dbd4..1049226 100644
--- a/llvm/lib/Demangle/MicrosoftDemangle.cpp
+++ b/llvm/lib/Demangle/MicrosoftDemangle.cpp
@@ -409,12 +409,11 @@
   return synthesizeQualifiedName(Arena, Id);
 }
 
-static VariableSymbolNode *synthesizeVariable(ArenaAllocator &Arena,
-                                              TypeNode *Type,
-                                              std::string_view VariableName) {
-  VariableSymbolNode *VSN = Arena.alloc<VariableSymbolNode>();
+static VariableSymbolNode *synthesizeType(ArenaAllocator &Arena, TypeNode *Type,
+                                          std::string_view Description) {
+  TypeSymbolNode *VSN = Arena.alloc<TypeSymbolNode>();
   VSN->Type = Type;
-  VSN->Name = synthesizeQualifiedName(Arena, VariableName);
+  VSN->Name = synthesizeQualifiedName(Arena, Description);
   return VSN;
 }
 
@@ -528,7 +527,7 @@
       break;
     if (!MangledName.empty())
       break;
-    return synthesizeVariable(Arena, T, "`RTTI Type Descriptor'");
+    return synthesizeType(Arena, T, "`RTTI Type Descriptor'");
   }
   case SpecialIntrinsicKind::RttiBaseClassArray:
     return demangleUntypedVariable(Arena, MangledName,
@@ -873,7 +872,7 @@
     Error = true;
     return nullptr;
   }
-  return synthesizeVariable(Arena, T, "`RTTI Type Descriptor Name'");
+  return synthesizeType(Arena, T, "`RTTI Type Descriptor Name'");
 }
 
 // Parser entry point.
@@ -2542,6 +2541,12 @@
     OF = OutputFlags(OF | OF_NoMemberType);
   if (Flags & MSDF_NoVariableType)
     OF = OutputFlags(OF | OF_NoVariableType);
+  if (Flags & MSDF_NoTagSpecifier)
+    OF = OutputFlags(OF | OF_NoTagSpecifier);
+  if (Flags & MSDF_NoVoidParameter)
+    OF = OutputFlags(OF | OF_NoVoidParameter);
+  if (Flags & MSDF_NoDecorativeRTTITypeDescriptor)
+    OF = OutputFlags(OF | OF_NoDecorativeRTTITypeDescriptor);
 
   int InternalStatus = demangle_success;
   char *Buf;
diff --git a/llvm/lib/Demangle/MicrosoftDemangleNodes.cpp b/llvm/lib/Demangle/MicrosoftDemangleNodes.cpp
index 17c6aab..daca80b 100644
--- a/llvm/lib/Demangle/MicrosoftDemangleNodes.cpp
+++ b/llvm/lib/Demangle/MicrosoftDemangleNodes.cpp
@@ -422,7 +422,7 @@
     OB << "(";
     if (Params)
       Params->output(OB, Flags);
-    else
+    else if (!(Flags & OF_NoVoidParameter))
       OB << "void";
 
     if (IsVariadic) {
@@ -623,11 +623,12 @@
   if (!(Flags & OF_NoMemberType) && IsStatic)
     OB << "static ";
 
-  if (!(Flags & OF_NoVariableType) && Type) {
+  if (!(Flags & OF_NoVariableType) && Type)
     Type->outputPre(OB, Flags);
+  if (shouldOutputName(Flags)) {
     outputSpaceIfNecessary(OB);
+    Name->output(OB, Flags);
   }
-  Name->output(OB, Flags);
   if (!(Flags & OF_NoVariableType) && Type)
     Type->outputPost(OB, Flags);
 }
diff --git a/llvm/unittests/Demangle/CMakeLists.txt b/llvm/unittests/Demangle/CMakeLists.txt
index 105af81..02153d2 100644
--- a/llvm/unittests/Demangle/CMakeLists.txt
+++ b/llvm/unittests/Demangle/CMakeLists.txt
@@ -7,6 +7,7 @@
   DemangleTest.cpp
   DLangDemangleTest.cpp
   ItaniumDemangleTest.cpp
+  MicrosoftDemangleTest.cpp
   OutputBufferTest.cpp
   PartialDemangleTest.cpp
   RustDemangleTest.cpp
diff --git a/llvm/unittests/Demangle/MicrosoftDemangleTest.cpp b/llvm/unittests/Demangle/MicrosoftDemangleTest.cpp
new file mode 100644
index 0000000..d45f455
--- /dev/null
+++ b/llvm/unittests/Demangle/MicrosoftDemangleTest.cpp
@@ -0,0 +1,320 @@
+//===-- MicrosoftDemangleTest.cpp -----------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/Demangle/Demangle.h"
+#include "gtest/gtest.h"
+#include <cstdlib>
+#include <string>
+#include <string_view>
+
+using namespace llvm;
+
+namespace {
+std::string microsoftDemangleString(std::string_view MangledName,
+                                    MSDemangleFlags Flags) {
+  char *Demangled = microsoftDemangle(MangledName, nullptr, nullptr, Flags);
+  if (!Demangled)
+    return std::string(MangledName);
+  std::string Result(Demangled);
+  std::free(Demangled);
+  return Result;
+}
+} // namespace
+
+TEST(MicrosoftDemangle, demangleFunction) {
+  EXPECT_EQ(microsoftDemangleString("?foo@@YAXXZ", MSDF_None),
+            "void __cdecl foo(void)");
+  EXPECT_EQ(microsoftDemangleString("?foo@@YAXXZ", MSDF_NoReturnType),
+            "__cdecl foo(void)");
+  EXPECT_EQ(microsoftDemangleString("?foo@@YAXXZ", MSDF_NoCallingConvention),
+            "void foo(void)");
+  EXPECT_EQ(microsoftDemangleString("?foo@@YAXXZ", MSDF_NoVoidParameter),
+            "void __cdecl foo()");
+  EXPECT_EQ(microsoftDemangleString("?foo@@YAXXZ",
+                                    MSDemangleFlags(MSDF_NoCallingConvention |
+                                                    MSDF_NoReturnType |
+                                                    MSDF_NoVoidParameter)),
+            "foo()");
+}
+
+TEST(MicrosoftDemangle, demangleFunctionWithParameters) {
+  EXPECT_EQ(microsoftDemangleString("?foo@@YAXHH@Z", MSDF_None),
+            "void __cdecl foo(int, int)");
+  EXPECT_EQ(microsoftDemangleString("?foo@@YAXHH@Z", MSDF_NoReturnType),
+            "__cdecl foo(int, int)");
+  EXPECT_EQ(microsoftDemangleString("?foo@@YAXHH@Z", MSDF_NoCallingConvention),
+            "void foo(int, int)");
+  EXPECT_EQ(microsoftDemangleString(
+                "?foo@@YAXHH@Z",
+                MSDemangleFlags(MSDF_NoReturnType | MSDF_NoCallingConvention)),
+            "foo(int, int)");
+}
+
+TEST(MicrosoftDemangle, demangleMethod) {
+  EXPECT_EQ(microsoftDemangleString("?method@Struct@@QEAAXXZ", MSDF_None),
+            "public: void __cdecl Struct::method(void)");
+  EXPECT_EQ(
+      microsoftDemangleString("?method@Struct@@QEAAXXZ", MSDF_NoReturnType),
+      "public: __cdecl Struct::method(void)");
+  EXPECT_EQ(microsoftDemangleString("?method@Struct@@QEAAXXZ",
+                                    MSDF_NoCallingConvention),
+            "public: void Struct::method(void)");
+  EXPECT_EQ(microsoftDemangleString("?method@Struct@@QEAAXXZ",
+                                    MSDF_NoAccessSpecifier),
+            "void __cdecl Struct::method(void)");
+  EXPECT_EQ(
+      microsoftDemangleString("?method@Struct@@QEAAXXZ", MSDF_NoVoidParameter),
+      "public: void __cdecl Struct::method()");
+  EXPECT_EQ(
+      microsoftDemangleString(
+          "?method@Struct@@QEAAXXZ",
+          MSDemangleFlags(MSDF_NoCallingConvention | MSDF_NoAccessSpecifier |
+                          MSDF_NoReturnType | MSDF_NoVoidParameter)),
+      "Struct::method()");
+  EXPECT_EQ(
+      microsoftDemangleString("?privateMethod@Struct@@AEAAXXZ", MSDF_None),
+      "private: void __cdecl Struct::privateMethod(void)");
+  EXPECT_EQ(
+      microsoftDemangleString("?protectedMethod@Struct@@IEAAXXZ", MSDF_None),
+      "protected: void __cdecl Struct::protectedMethod(void)");
+}
+
+TEST(MicrosoftDemangle, demangleConstMethod) {
+  EXPECT_EQ(microsoftDemangleString("?constMethod@Struct@@QEBAXXZ", MSDF_None),
+            "public: void __cdecl Struct::constMethod(void) const");
+  EXPECT_EQ(microsoftDemangleString("?constMethod@Struct@@QEBAXXZ",
+                                    MSDF_NoReturnType),
+            "public: __cdecl Struct::constMethod(void) const");
+  EXPECT_EQ(microsoftDemangleString("?constMethod@Struct@@QEBAXXZ",
+                                    MSDF_NoCallingConvention),
+            "public: void Struct::constMethod(void) const");
+  EXPECT_EQ(microsoftDemangleString("?constMethod@Struct@@QEBAXXZ",
+                                    MSDF_NoVoidParameter),
+            "public: void __cdecl Struct::constMethod() const");
+  EXPECT_EQ(microsoftDemangleString("?constMethod@Struct@@QEBAXXZ",
+                                    MSDemangleFlags(MSDF_NoReturnType |
+                                                    MSDF_NoCallingConvention |
+                                                    MSDF_NoVoidParameter)),
+            "public: Struct::constMethod() const");
+}
+
+TEST(MicrosoftDemangle, demangleVirtualMethod) {
+  EXPECT_EQ(microsoftDemangleString("?vfunc@Base@@UEAAXXZ", MSDF_None),
+            "public: virtual void __cdecl Base::vfunc(void)");
+  EXPECT_EQ(microsoftDemangleString("?vfunc@Base@@UEAAXXZ", MSDF_NoReturnType),
+            "public: virtual __cdecl Base::vfunc(void)");
+  EXPECT_EQ(
+      microsoftDemangleString("?vfunc@Base@@UEAAXXZ", MSDF_NoAccessSpecifier),
+      "virtual void __cdecl Base::vfunc(void)");
+  EXPECT_EQ(microsoftDemangleString(
+                "?vfunc@Base@@UEAAXXZ",
+                MSDemangleFlags(MSDF_NoReturnType | MSDF_NoAccessSpecifier)),
+            "virtual __cdecl Base::vfunc(void)");
+}
+
+TEST(MicrosoftDemangle, demangleStaticMethod) {
+  EXPECT_EQ(microsoftDemangleString("?staticMethod@Struct@@SAXXZ", MSDF_None),
+            "public: static void __cdecl Struct::staticMethod(void)");
+  EXPECT_EQ(
+      microsoftDemangleString("?staticMethod@Struct@@SAXXZ", MSDF_NoReturnType),
+      "public: static __cdecl Struct::staticMethod(void)");
+  EXPECT_EQ(microsoftDemangleString("?staticMethod@Struct@@SAXXZ",
+                                    MSDF_NoAccessSpecifier),
+            "static void __cdecl Struct::staticMethod(void)");
+  EXPECT_EQ(microsoftDemangleString("?staticMethod@Struct@@SAXXZ",
+                                    MSDF_NoCallingConvention),
+            "public: static void Struct::staticMethod(void)");
+  EXPECT_EQ(microsoftDemangleString("?staticMethod@Struct@@SAXXZ",
+                                    MSDemangleFlags(MSDF_NoReturnType |
+                                                    MSDF_NoAccessSpecifier |
+                                                    MSDF_NoCallingConvention)),
+            "static Struct::staticMethod(void)");
+}
+
+TEST(MicrosoftDemangle, demangleConstructor) {
+  EXPECT_EQ(microsoftDemangleString("??0Animal@@QEAA@XZ", MSDF_None),
+            "public: __cdecl Animal::Animal(void)");
+  EXPECT_EQ(microsoftDemangleString("??0Animal@@QEAA@XZ", MSDF_NoReturnType),
+            "public: __cdecl Animal::Animal(void)");
+  EXPECT_EQ(
+      microsoftDemangleString("??0Animal@@QEAA@XZ", MSDF_NoCallingConvention),
+      "public: Animal::Animal(void)");
+  EXPECT_EQ(
+      microsoftDemangleString("??0Animal@@QEAA@XZ", MSDF_NoAccessSpecifier),
+      "__cdecl Animal::Animal(void)");
+  EXPECT_EQ(microsoftDemangleString("??0Animal@@QEAA@XZ", MSDF_NoVoidParameter),
+            "public: __cdecl Animal::Animal()");
+  EXPECT_EQ(
+      microsoftDemangleString(
+          "??0Animal@@QEAA@XZ",
+          MSDemangleFlags(MSDF_NoCallingConvention | MSDF_NoAccessSpecifier |
+                          MSDF_NoReturnType | MSDF_NoVoidParameter)),
+      "Animal::Animal()");
+}
+
+TEST(MicrosoftDemangle, demangleDestructor) {
+  EXPECT_EQ(microsoftDemangleString("??1Animal@@QEAA@XZ", MSDF_None),
+            "public: __cdecl Animal::~Animal(void)");
+  EXPECT_EQ(microsoftDemangleString("??1Animal@@QEAA@XZ", MSDF_NoReturnType),
+            "public: __cdecl Animal::~Animal(void)");
+  EXPECT_EQ(
+      microsoftDemangleString("??1Animal@@QEAA@XZ", MSDF_NoCallingConvention),
+      "public: Animal::~Animal(void)");
+  EXPECT_EQ(
+      microsoftDemangleString("??1Animal@@QEAA@XZ", MSDF_NoAccessSpecifier),
+      "__cdecl Animal::~Animal(void)");
+  EXPECT_EQ(microsoftDemangleString("??1Animal@@QEAA@XZ", MSDF_NoVoidParameter),
+            "public: __cdecl Animal::~Animal()");
+  EXPECT_EQ(
+      microsoftDemangleString(
+          "??1Animal@@QEAA@XZ",
+          MSDemangleFlags(MSDF_NoCallingConvention | MSDF_NoAccessSpecifier |
+                          MSDF_NoReturnType | MSDF_NoVoidParameter)),
+      "Animal::~Animal()");
+}
+
+TEST(MicrosoftDemangle, demangleOperatorPlus) {
+  EXPECT_EQ(microsoftDemangleString("??HInt@@QEAAHH@Z", MSDF_None),
+            "public: int __cdecl Int::operator+(int)");
+  EXPECT_EQ(microsoftDemangleString("??HInt@@QEAAHH@Z", MSDF_NoReturnType),
+            "public: __cdecl Int::operator+(int)");
+  EXPECT_EQ(
+      microsoftDemangleString("??HInt@@QEAAHH@Z", MSDF_NoCallingConvention),
+      "public: int Int::operator+(int)");
+  EXPECT_EQ(microsoftDemangleString(
+                "??HInt@@QEAAHH@Z",
+                MSDemangleFlags(MSDF_NoReturnType | MSDF_NoCallingConvention)),
+            "public: Int::operator+(int)");
+}
+
+TEST(MicrosoftDemangle, demangleCopyOperator) {
+  EXPECT_EQ(microsoftDemangleString("??0Dog@@QEAA@AEBU0@@Z", MSDF_None),
+            "public: __cdecl Dog::Dog(struct Dog const &)");
+  EXPECT_EQ(microsoftDemangleString("??0Dog@@QEAA@AEBU0@@Z", MSDF_NoReturnType),
+            "public: __cdecl Dog::Dog(struct Dog const &)");
+  EXPECT_EQ(microsoftDemangleString("??0Dog@@QEAA@AEBU0@@Z",
+                                    MSDF_NoCallingConvention),
+            "public: Dog::Dog(struct Dog const &)");
+  EXPECT_EQ(
+      microsoftDemangleString("??0Dog@@QEAA@AEBU0@@Z", MSDF_NoAccessSpecifier),
+      "__cdecl Dog::Dog(struct Dog const &)");
+  EXPECT_EQ(
+      microsoftDemangleString("??0Dog@@QEAA@AEBU0@@Z", MSDF_NoTagSpecifier),
+      "public: __cdecl Dog::Dog(Dog const &)");
+  EXPECT_EQ(microsoftDemangleString("??0Dog@@QEAA@AEBU0@@Z",
+                                    MSDemangleFlags(MSDF_NoCallingConvention |
+                                                    MSDF_NoAccessSpecifier |
+                                                    MSDF_NoTagSpecifier)),
+            "Dog::Dog(Dog const &)");
+}
+
+TEST(MicrosoftDemangle, demangleAssignmentOperator) {
+  EXPECT_EQ(microsoftDemangleString("??4Dog@@QEAAAEAU0@AEBU0@@Z", MSDF_None),
+            "public: struct Dog & __cdecl Dog::operator=(struct Dog const &)");
+  EXPECT_EQ(
+      microsoftDemangleString("??4Dog@@QEAAAEAU0@AEBU0@@Z", MSDF_NoReturnType),
+      "public: __cdecl Dog::operator=(struct Dog const &)");
+  EXPECT_EQ(microsoftDemangleString("??4Dog@@QEAAAEAU0@AEBU0@@Z",
+                                    MSDF_NoCallingConvention),
+            "public: struct Dog & Dog::operator=(struct Dog const &)");
+  EXPECT_EQ(microsoftDemangleString("??4Dog@@QEAAAEAU0@AEBU0@@Z",
+                                    MSDF_NoAccessSpecifier),
+            "struct Dog & __cdecl Dog::operator=(struct Dog const &)");
+  EXPECT_EQ(microsoftDemangleString("??4Dog@@QEAAAEAU0@AEBU0@@Z",
+                                    MSDF_NoTagSpecifier),
+            "public: Dog & __cdecl Dog::operator=(Dog const &)");
+  EXPECT_EQ(microsoftDemangleString(
+                "??4Dog@@QEAAAEAU0@AEBU0@@Z",
+                MSDemangleFlags(MSDF_NoReturnType | MSDF_NoCallingConvention |
+                                MSDF_NoAccessSpecifier | MSDF_NoTagSpecifier)),
+            "Dog::operator=(Dog const &)");
+}
+
+TEST(MicrosoftDemangle, demangleType) {
+  EXPECT_EQ(microsoftDemangleString(".?AUStruct@@", MSDF_None),
+            "struct Struct `RTTI Type Descriptor Name'");
+  EXPECT_EQ(microsoftDemangleString(".?AUStruct@@", MSDF_NoTagSpecifier),
+            "Struct `RTTI Type Descriptor Name'");
+  EXPECT_EQ(microsoftDemangleString(".?AUStruct@@",
+                                    MSDF_NoDecorativeRTTITypeDescriptor),
+            "struct Struct");
+  EXPECT_EQ(
+      microsoftDemangleString(
+          ".?AUStruct@@", MSDemangleFlags(MSDF_NoTagSpecifier |
+                                          MSDF_NoDecorativeRTTITypeDescriptor)),
+      "Struct");
+}
+
+TEST(MicrosoftDemangle, demangleVariable) {
+  EXPECT_EQ(microsoftDemangleString("?globalVar@@3HA", MSDF_None),
+            "int globalVar");
+  EXPECT_EQ(microsoftDemangleString("?globalVar@@3HA", MSDF_NoVariableType),
+            "globalVar");
+  // This flag should not impact variables
+  EXPECT_EQ(microsoftDemangleString("?globalVar@@3HA",
+                                    MSDF_NoDecorativeRTTITypeDescriptor),
+            "int globalVar");
+}
+
+TEST(MicrosoftDemangle, demangleMemberVariable) {
+  EXPECT_EQ(microsoftDemangleString("?var@Struct@@2HA", MSDF_None),
+            "public: static int Struct::var");
+  EXPECT_EQ(microsoftDemangleString("?var@Struct@@2HA", MSDF_NoVariableType),
+            "public: static Struct::var");
+  EXPECT_EQ(microsoftDemangleString("?var@Struct@@2HA", MSDF_NoAccessSpecifier),
+            "static int Struct::var");
+  EXPECT_EQ(microsoftDemangleString("?var@Struct@@2HA", MSDF_NoMemberType),
+            "public: int Struct::var");
+  EXPECT_EQ(microsoftDemangleString("?var@Struct@@2HA",
+                                    MSDemangleFlags(MSDF_NoAccessSpecifier |
+                                                    MSDF_NoVariableType |
+                                                    MSDF_NoMemberType)),
+            "Struct::var");
+}
+
+TEST(MicrosoftDemangle, demangleIndirectVariables) {
+  EXPECT_EQ(microsoftDemangleString("?ptr@@3PEAHA", MSDF_None), "int *ptr");
+  EXPECT_EQ(microsoftDemangleString("?ptr@@3PEAHA", MSDF_NoVariableType),
+            "ptr");
+  EXPECT_EQ(microsoftDemangleString("?ptrConst@@3PEBHA", MSDF_None),
+            "int const *ptrConst");
+  EXPECT_EQ(microsoftDemangleString("?ptrPtr@@3PEAPEAHA", MSDF_None),
+            "int **ptrPtr");
+  EXPECT_EQ(microsoftDemangleString("?ref@@3AEAHA", MSDF_None), "int &ref");
+}
+
+TEST(MicrosoftDemangle, demangleFunctionPointers) {
+  EXPECT_EQ(microsoftDemangleString("?funcPtr@@3P6AXXZA", MSDF_None),
+            "void (__cdecl *funcPtr)(void)");
+  EXPECT_EQ(microsoftDemangleString("?funcPtr@@3P6AXXZA", MSDF_NoVariableType),
+            "funcPtr");
+  EXPECT_EQ(microsoftDemangleString("?funcPtr@@3P6AHXZA", MSDF_NoVoidParameter),
+            "int (__cdecl *funcPtr)()");
+}
+
+TEST(MicrosoftDemangle, demangleNestedClass) {
+  EXPECT_EQ(microsoftDemangleString("?inner@Outer@@QEAAXXZ", MSDF_None),
+            "public: void __cdecl Outer::inner(void)");
+  EXPECT_EQ(microsoftDemangleString("?inner@Outer@@QEAAXXZ", MSDF_NoReturnType),
+            "public: __cdecl Outer::inner(void)");
+  EXPECT_EQ(
+      microsoftDemangleString("?inner@Outer@@QEAAXXZ", MSDF_NoAccessSpecifier),
+      "void __cdecl Outer::inner(void)");
+  EXPECT_EQ(microsoftDemangleString(
+                "?inner@Outer@@QEAAXXZ",
+                MSDemangleFlags(MSDF_NoReturnType | MSDF_NoAccessSpecifier)),
+            "__cdecl Outer::inner(void)");
+}
+
+TEST(MicrosoftDemangle, demangleStdcallFunction) {
+  EXPECT_EQ(microsoftDemangleString("?stdcallFunc@@YGXXZ", MSDF_None),
+            "void __stdcall stdcallFunc(void)");
+  EXPECT_EQ(
+      microsoftDemangleString("?stdcallFunc@@YGXXZ", MSDF_NoCallingConvention),
+      "void stdcallFunc(void)");
+}