summaryrefslogtreecommitdiff
path: root/test/Demangle
diff options
context:
space:
mode:
Diffstat (limited to 'test/Demangle')
-rw-r--r--test/Demangle/ms-cxx11.test148
-rw-r--r--test/Demangle/ms-mangle.test14
-rw-r--r--test/Demangle/ms-nested-scopes.test146
-rw-r--r--test/Demangle/ms-return-qualifiers.test184
-rw-r--r--test/Demangle/ms-template-callback.test53
5 files changed, 538 insertions, 7 deletions
diff --git a/test/Demangle/ms-cxx11.test b/test/Demangle/ms-cxx11.test
new file mode 100644
index 000000000000..b648cc3d6e33
--- /dev/null
+++ b/test/Demangle/ms-cxx11.test
@@ -0,0 +1,148 @@
+; These tests are based on clang/test/CodeGenCXX/mangle-ms-cxx11.cpp
+
+; RUN: llvm-undname < %s | FileCheck %s
+
+; CHECK-NOT: Invalid mangled name
+
+?a@FTypeWithQuals@@3U?$S@$$A8@@BAHXZ@1@A
+; CHECK: struct FTypeWithQuals::S<int __cdecl(void) const> FTypeWithQuals::a
+
+?b@FTypeWithQuals@@3U?$S@$$A8@@CAHXZ@1@A
+; CHECK: struct FTypeWithQuals::S<int __cdecl(void) volatile> FTypeWithQuals::b
+
+?c@FTypeWithQuals@@3U?$S@$$A8@@IAAHXZ@1@A
+; CHECK: struct FTypeWithQuals::S<int __cdecl(void) __restrict> FTypeWithQuals::c
+
+?d@FTypeWithQuals@@3U?$S@$$A8@@GBAHXZ@1@A
+; CHECK: struct FTypeWithQuals::S<int __cdecl(void) const &> FTypeWithQuals::d
+
+?e@FTypeWithQuals@@3U?$S@$$A8@@GCAHXZ@1@A
+; CHECK: struct FTypeWithQuals::S<int __cdecl(void) volatile &> FTypeWithQuals::e
+
+?f@FTypeWithQuals@@3U?$S@$$A8@@IGAAHXZ@1@A
+; CHECK: struct FTypeWithQuals::S<int __cdecl(void) __restrict &> FTypeWithQuals::f
+
+?g@FTypeWithQuals@@3U?$S@$$A8@@HBAHXZ@1@A
+; CHECK: struct FTypeWithQuals::S<int __cdecl(void) const &&> FTypeWithQuals::g
+
+?h@FTypeWithQuals@@3U?$S@$$A8@@HCAHXZ@1@A
+; CHECK: struct FTypeWithQuals::S<int __cdecl(void) volatile &&> FTypeWithQuals::h
+
+?i@FTypeWithQuals@@3U?$S@$$A8@@IHAAHXZ@1@A
+; CHECK: struct FTypeWithQuals::S<int __cdecl(void) __restrict &&> FTypeWithQuals::i
+
+?j@FTypeWithQuals@@3U?$S@$$A6AHXZ@1@A
+; CHECK: struct FTypeWithQuals::S<int __cdecl(void)> FTypeWithQuals::j
+
+?k@FTypeWithQuals@@3U?$S@$$A8@@GAAHXZ@1@A
+; CHECK: struct FTypeWithQuals::S<int __cdecl(void) &> FTypeWithQuals::k
+
+?l@FTypeWithQuals@@3U?$S@$$A8@@HAAHXZ@1@A
+; CHECK: struct FTypeWithQuals::S<int __cdecl(void) &&> FTypeWithQuals::l
+
+?Char16Var@@3_SA
+; CHECK: char16_t Char16Var
+
+?Char32Var@@3_UA
+; CHECK: char32_t Char32Var
+
+?LRef@@YAXAAH@Z
+; CHECK: void __cdecl LRef(int &)
+
+?RRef@@YAH$$QAH@Z
+; CHECK: int __cdecl RRef(int &&)
+
+?Null@@YAX$$T@Z
+; CHECK: void __cdecl Null(std::nullptr_t)
+
+?fun@PR18022@@YA?AU<unnamed-type-a>@1@U21@0@Z
+; CHECK: struct PR18022::<unnamed-type-a> __cdecl PR18022::fun(struct PR18022::<unnamed-type-a>, struct PR18022::<unnamed-type-a>)
+
+; First, we have the static local variable of type "<lambda_1>" inside of "define_lambda".
+; decltype(lambda), where lambda = [] { static int local=42; return 42; };
+?lambda@?1??define_lambda@@YAHXZ@4V<lambda_1>@?0??1@YAHXZ@A
+; CHECK: class `int __cdecl define_lambda(void)'::`1'::<lambda_1> `int __cdecl define_lambda(void)'::`2'::lambda
+
+; Next, we have the "operator()" for "<lambda_1>" which is inside of "define_lambda".
+??R<lambda_1>@?0??define_lambda@@YAHXZ@QBE@XZ
+; CHECK: __thiscall `int __cdecl define_lambda(void)'::`1'::<lambda_1>::operator()(void) const
+
+; Finally, we have the local which is inside of "<lambda_1>" which is inside of "define_lambda".
+?local@?2???R<lambda_1>@?0??define_lambda@@YAHXZ@QBE@XZ@4HA
+; CHECK: __thiscall `int __cdecl define_lambda(void)'::`1'::<lambda_1>::operator()(void) const
+
+??$use_lambda_arg@V<lambda_1>@?0??call_with_lambda_arg1@@YAXXZ@@@YAXV<lambda_1>@?0??call_with_lambda_arg1@@YAXXZ@@Z
+; CHECK: void __cdecl use_lambda_arg<class `void __cdecl call_with_lambda_arg1(void)'::`1'::<lambda_1>>(class `void __cdecl call_with_lambda_arg1(void)'::`1'::<lambda_1>)
+
+?foo@A@PR19361@@QIGAEXXZ
+; CHECK: void __thiscall PR19361::A::foo(void) __restrict &
+
+?foo@A@PR19361@@QIHAEXXZ
+; CHECK: void __thiscall PR19361::A::foo(void) __restrict &&
+
+??__K_deg@@YAHO@Z
+; CHECK: int __cdecl operator ""_deg(long double)
+
+??$templ_fun_with_pack@$S@@YAXXZ
+; CHECK: void __cdecl templ_fun_with_pack<>(void)
+
+??$templ_fun_with_ty_pack@$$$V@@YAXXZ
+; CHECK: void __cdecl templ_fun_with_ty_pack<>(void)
+??$templ_fun_with_ty_pack@$$V@@YAXXZ
+; CHECK: void __cdecl templ_fun_with_ty_pack<>(void)
+
+??$f@$$YAliasA@PR20047@@@PR20047@@YAXXZ
+; CHECK: void __cdecl PR20047::f<PR20047::AliasA>(void)
+
+?f@UnnamedType@@YAXAAU<unnamed-type-TD>@A@1@@Z
+; CHECK: void __cdecl UnnamedType::f(struct UnnamedType::A::<unnamed-type-TD> &)
+
+?f@UnnamedType@@YAXPAW4<unnamed-type-e>@?$B@H@1@@Z
+; CHECK: void __cdecl UnnamedType::f(enum UnnamedType::B<int>::<unnamed-type-e> *)
+
+??$f@W4<unnamed-type-E>@?1??g@PR24651@@YAXXZ@@PR24651@@YAXW4<unnamed-type-E>@?1??g@0@YAXXZ@@Z
+; We have a back-referencing problem here, we print `void __cdecl <unnamed-type-E>::g(void)`
+; for the second occurrence of g.
+; FIXME: void __cdecl PR24651::f<enum `void __cdecl PR24651::g(void)'::`2'::<unnamed-type-E>>(enum `void __cdecl PR24651::g(void)'::`2'::<unnamed-type-E>)
+
+??$f@T<unnamed-type-$S1>@PR18204@@@PR18204@@YAHPAT<unnamed-type-$S1>@0@@Z
+; FIXME: int __cdecl PR18204::f<union PR18204::<unnamed-type-$S1>>(union PR18204::<unnamed-type-$S1> *)
+
+??R<lambda_0>@?0??PR26105@@YAHXZ@QBE@H@Z
+; CHECK: __thiscall `int __cdecl PR26105(void)'::`1'::<lambda_0>::operator()(int) const
+
+??R<lambda_1>@?0???R<lambda_0>@?0??PR26105@@YAHXZ@QBE@H@Z@QBE@H@Z
+; CHECK: __thiscall `__thiscall `int __cdecl PR26105(void)'::`1'::<lambda_0>::operator()(int) const'::`1'::<lambda_1>::operator()(int) const
+
+?unaligned_foo1@@YAPFAHXZ
+; CHECK: int __unaligned * __cdecl unaligned_foo1(void)
+
+?unaligned_foo2@@YAPFAPFAHXZ
+; CHECK: int __unaligned *__unaligned * __cdecl unaligned_foo2(void)
+
+?unaligned_foo3@@YAHXZ
+; CHECK: int __cdecl unaligned_foo3(void)
+
+?unaligned_foo4@@YAXPFAH@Z
+; CHECK: void __cdecl unaligned_foo4(int __unaligned *)
+
+?unaligned_foo5@@YAXPIFAH@Z
+; CHECK: void __cdecl unaligned_foo5(int __unaligned *__restrict)
+
+??$unaligned_foo6@PAH@@YAPAHPAH@Z
+; CHECK: int * __cdecl unaligned_foo6<int *>(int *)
+
+??$unaligned_foo6@PFAH@@YAPFAHPFAH@Z
+; CHECK: int __unaligned * __cdecl unaligned_foo6<int __unaligned *>(int __unaligned *)
+
+?unaligned_foo8@unaligned_foo8_S@@QFCEXXZ
+; CHECK: void __thiscall unaligned_foo8_S::unaligned_foo8(void) volatile __unaligned
+
+??R<lambda_1>@x@A@PR31197@@QBE@XZ
+; CHECK: __thiscall PR31197::A::x::<lambda_1>::operator()(void) const
+
+?white@?1???R<lambda_1>@x@A@PR31197@@QBE@XZ@4HA
+; CHECK: int `__thiscall PR31197::A::x::<lambda_1>::operator()(void) const'::`2'::white
+
+?f@@YAXW4<unnamed-enum-enumerator>@@@Z
+; CHECK: void __cdecl f(enum <unnamed-enum-enumerator>)
diff --git a/test/Demangle/ms-mangle.test b/test/Demangle/ms-mangle.test
index a5d0c70ee314..9a2f780f9334 100644
--- a/test/Demangle/ms-mangle.test
+++ b/test/Demangle/ms-mangle.test
@@ -265,18 +265,18 @@
?s6@PR13182@@3PBQBDB
; CHECK: char const *const *PR13182::s6
-; FIXME: We don't properly support static locals in functions yet.
+; FIXME: We don't properly support extern "C" functions yet.
; ?local@?1??extern_c_func@@9@4HA
; FIXME: int `extern_c_func'::`2'::local
; ?local@?1??extern_c_func@@9@4HA
; FIXME: int `extern_c_func'::`2'::local
-; ?v@?1??f@@YAHXZ@4U<unnamed-type-v>@?1??1@YAHXZ@A
-; FIXME: struct `int __cdecl f(void)'::`2'::<unnamed-type-v> `int __cdecl f(void)'::`2'::v
+?v@?1??f@@YAHXZ@4U<unnamed-type-v>@?1??1@YAHXZ@A
+; CHECK: struct `int __cdecl f(void)'::`2'::<unnamed-type-v> `int __cdecl f(void)'::`2'::v
-; ?v@?1???$f@H@@YAHXZ@4U<unnamed-type-v>@?1???$f@H@@YAHXZ@A
-; FIXME: struct `int __cdecl f<int>(void)'::`2'::<unnamed-type-v> `int __cdecl f<int>(void)'::`2'::v
+?v@?1???$f@H@@YAHXZ@4U<unnamed-type-v>@?1???$f@H@@YAHXZ@A
+; CHECK: struct `int __cdecl f<int>(void)'::`2'::<unnamed-type-v> `int __cdecl f<int>(void)'::`2'::v
??2OverloadedNewDelete@@SAPAXI@Z
; CHECK: static void * __cdecl OverloadedNewDelete::operator new(unsigned int)
@@ -335,8 +335,8 @@
; ?overloaded_fn@@$$J0YAXXZ
; FIXME-EXTERNC: extern \"C\" void __cdecl overloaded_fn(void)
-; ?f@UnnamedType@@YAXQAPAU<unnamed-type-T1>@S@1@@Z
-; FIXME: void __cdecl UnnamedType::f(struct UnnamedType::S::<unnamed-type-T1> ** const)
+?f@UnnamedType@@YAXQAPAU<unnamed-type-T1>@S@1@@Z
+; CHECK: void __cdecl UnnamedType::f(struct UnnamedType::S::<unnamed-type-T1> **const)
?f@UnnamedType@@YAXUT2@S@1@@Z
; CHECK: void __cdecl UnnamedType::f(struct UnnamedType::S::T2)
diff --git a/test/Demangle/ms-nested-scopes.test b/test/Demangle/ms-nested-scopes.test
new file mode 100644
index 000000000000..952b138630cc
--- /dev/null
+++ b/test/Demangle/ms-nested-scopes.test
@@ -0,0 +1,146 @@
+; RUN: llvm-undname < %s | FileCheck %s
+
+; CHECK-NOT: Invalid mangled name
+
+; Test demangling of function local scope discriminator IDs.
+?M@?@??L@@YAHXZ@4HA
+; CHECK: int `int __cdecl L(void)'::`0'::M
+
+?M@?0??L@@YAHXZ@4HA
+; CHECK: int `int __cdecl L(void)'::`1'::M
+
+?M@?1??L@@YAHXZ@4HA
+; CHECK: int `int __cdecl L(void)'::`2'::M
+
+?M@?2??L@@YAHXZ@4HA
+; CHECK: int `int __cdecl L(void)'::`3'::M
+
+?M@?3??L@@YAHXZ@4HA
+; CHECK: int `int __cdecl L(void)'::`4'::M
+
+?M@?4??L@@YAHXZ@4HA
+; CHECK: int `int __cdecl L(void)'::`5'::M
+
+?M@?5??L@@YAHXZ@4HA
+; CHECK: int `int __cdecl L(void)'::`6'::M
+
+?M@?6??L@@YAHXZ@4HA
+; CHECK: int `int __cdecl L(void)'::`7'::M
+
+?M@?7??L@@YAHXZ@4HA
+; CHECK: int `int __cdecl L(void)'::`8'::M
+
+?M@?8??L@@YAHXZ@4HA
+; CHECK: int `int __cdecl L(void)'::`9'::M
+
+?M@?9??L@@YAHXZ@4HA
+; CHECK: int `int __cdecl L(void)'::`10'::M
+
+?M@?L@??L@@YAHXZ@4HA
+; CHECK: int `int __cdecl L(void)'::`11'::M
+
+?M@?M@??L@@YAHXZ@4HA
+; CHECK: int `int __cdecl L(void)'::`12'::M
+
+?M@?N@??L@@YAHXZ@4HA
+; CHECK: int `int __cdecl L(void)'::`13'::M
+
+?M@?O@??L@@YAHXZ@4HA
+; CHECK: int `int __cdecl L(void)'::`14'::M
+
+?M@?P@??L@@YAHXZ@4HA
+; CHECK: int `int __cdecl L(void)'::`15'::M
+
+?M@?BA@??L@@YAHXZ@4HA
+; CHECK: int `int __cdecl L(void)'::`16'::M
+
+?M@?BB@??L@@YAHXZ@4HA
+; CHECK: int `int __cdecl L(void)'::`17'::M
+
+?j@?1??L@@YAHXZ@4UJ@@A
+; CHECK: struct J `int __cdecl L(void)'::`2'::j
+
+; Test demangling of name back-references
+?NN@0XX@@3HA
+; CHECK: int XX::NN::NN
+
+?MM@0NN@XX@@3HA
+; CHECK: int XX::NN::MM::MM
+
+?NN@MM@0XX@@3HA
+; CHECK: int XX::NN::MM::NN
+
+?OO@0NN@01XX@@3HA
+; CHECK: int XX::NN::OO::NN::OO::OO
+
+?NN@OO@010XX@@3HA
+; CHECK: int XX::NN::OO::NN::OO::NN
+
+; Test demangling of name back-references combined with function local scopes.
+?M@?1??0@YAHXZ@4HA
+; CHECK: int `int __cdecl M(void)'::`2'::M
+
+?L@?2??M@0?2??0@YAHXZ@QEAAHXZ@4HA
+; CHECK: int `int __cdecl `int __cdecl L(void)'::`3'::L::M(void)'::`3'::L
+
+?M@?2??0L@?2??1@YAHXZ@QEAAHXZ@4HA
+; CHECK: int `int __cdecl `int __cdecl L(void)'::`3'::L::M(void)'::`3'::M
+
+; Function local scopes of template functions
+?M@?1???$L@H@@YAHXZ@4HA
+; CHECK: int `int __cdecl L<int>(void)'::`2'::M
+
+; And member functions of template classes
+?SN@?$NS@H@NS@@QEAAHXZ
+; CHECK: int __cdecl NS::NS<int>::SN(void)
+
+?NS@?1??SN@?$NS@H@0@QEAAHXZ@4HA
+; CHECK: int `int __cdecl NS::NS<int>::SN(void)'::`2'::NS
+
+?SN@?1??0?$NS@H@NS@@QEAAHXZ@4HA
+; CHECK: int `int __cdecl NS::NS<int>::SN(void)'::`2'::SN
+
+?NS@?1??SN@?$NS@H@10@QEAAHXZ@4HA
+; CHECK: int `int __cdecl NS::SN::NS<int>::SN(void)'::`2'::NS
+
+?SN@?1??0?$NS@H@0NS@@QEAAHXZ@4HA
+; CHECK: int `int __cdecl NS::SN::NS<int>::SN(void)'::`2'::SN
+
+; Make sure instantiated templates participate in back-referencing.
+; In the next 3 examples there should be 3 back-references:
+; 0 = X (right most name)
+; 1 = C<int> (second from right)
+; 2 = C (third from right)
+; Make sure all 3 work as expected by having the 4th component take each value
+; from 0-2 and confirming it is the right component.
+?X@?$C@H@C@0@2HB
+; CHECK: static int const X::C::C<int>::X
+
+?X@?$C@H@C@1@2HB
+; CHECK: static int const C<int>::C::C<int>::X
+
+?X@?$C@H@C@2@2HB
+; CHECK: static int const C::C::C<int>::X
+
+; Putting everything together.
+
+; namespace A { namespace B { namespace C { namespace B { namespace C {
+; template<typename T>
+; struct C {
+; int B() {
+; static C<int> C;
+; static int B = 7;
+; static int A = 7;
+; return C.B() + B + A;
+; }
+; };
+; } } } } }
+
+?C@?1??B@?$C@H@0101A@@QEAAHXZ@4U201013@A
+; CHECK: struct A::B::C::B::C::C<int> `int __cdecl A::B::C::B::C::C<int>::B(void)'::`2'::C
+
+?B@?1??0?$C@H@C@020A@@QEAAHXZ@4HA
+; CHECK: int `int __cdecl A::B::C::B::C::C<int>::B(void)'::`2'::B
+
+?A@?1??B@?$C@H@C@1310@QEAAHXZ@4HA
+; CHECK: int `int __cdecl A::B::C::B::C::C<int>::B(void)'::`2'::A
diff --git a/test/Demangle/ms-return-qualifiers.test b/test/Demangle/ms-return-qualifiers.test
new file mode 100644
index 000000000000..7fedf6c03a11
--- /dev/null
+++ b/test/Demangle/ms-return-qualifiers.test
@@ -0,0 +1,184 @@
+; These tests are based on clang/test/CodeGenCXX/mangle-ms-return-qualifiers.cpp
+
+; RUN: llvm-undname < %s | FileCheck %s
+
+; CHECK-NOT: Invalid mangled name
+
+?a1@@YAXXZ
+; CHECK: void __cdecl a1(void)
+
+?a2@@YAHXZ
+; CHECK: int __cdecl a2(void)
+
+?a3@@YA?BHXZ
+; CHECK: int const __cdecl a3(void)
+
+?a4@@YA?CHXZ
+; CHECK: int volatile __cdecl a4(void)
+
+?a5@@YA?DHXZ
+; CHECK: int const volatile __cdecl a5(void)
+
+?a6@@YAMXZ
+; CHECK: float __cdecl a6(void)
+
+?b1@@YAPAHXZ
+; CHECK: int * __cdecl b1(void)
+
+?b2@@YAPBDXZ
+; CHECK: char const * __cdecl b2(void)
+
+?b3@@YAPAMXZ
+; CHECK: float * __cdecl b3(void)
+
+?b4@@YAPBMXZ
+; CHECK: float const * __cdecl b4(void)
+
+?b5@@YAPCMXZ
+; CHECK: float volatile * __cdecl b5(void)
+
+?b6@@YAPDMXZ
+; CHECK: float const volatile * __cdecl b6(void)
+
+?b7@@YAAAMXZ
+; CHECK: float & __cdecl b7(void)
+
+?b8@@YAABMXZ
+; CHECK: float const & __cdecl b8(void)
+
+?b9@@YAACMXZ
+; CHECK: float volatile & __cdecl b9(void)
+
+?b10@@YAADMXZ
+; CHECK: float const volatile & __cdecl b10(void)
+
+?b11@@YAPAPBDXZ
+; CHECK: char const ** __cdecl b11(void)
+
+?c1@@YA?AVA@@XZ
+; CHECK: class A __cdecl c1(void)
+
+?c2@@YA?BVA@@XZ
+; CHECK: class A const __cdecl c2(void)
+
+?c3@@YA?CVA@@XZ
+; CHECK: class A volatile __cdecl c3(void)
+
+?c4@@YA?DVA@@XZ
+; CHECK: class A const volatile __cdecl c4(void)
+
+?c5@@YAPBVA@@XZ
+; CHECK: class A const * __cdecl c5(void)
+
+?c6@@YAPCVA@@XZ
+; CHECK: class A volatile * __cdecl c6(void)
+
+?c7@@YAPDVA@@XZ
+; CHECK: class A const volatile * __cdecl c7(void)
+
+?c8@@YAAAVA@@XZ
+; CHECK: class A & __cdecl c8(void)
+
+?c9@@YAABVA@@XZ
+; CHECK: class A const & __cdecl c9(void)
+
+?c10@@YAACVA@@XZ
+; CHECK: class A volatile & __cdecl c10(void)
+
+?c11@@YAADVA@@XZ
+; CHECK: class A const volatile & __cdecl c11(void)
+
+?d1@@YA?AV?$B@H@@XZ
+; CHECK: class B<int> __cdecl d1(void)
+
+?d2@@YA?AV?$B@PBD@@XZ
+; CHECK: class B<char const *> __cdecl d2(void)
+
+?d3@@YA?AV?$B@VA@@@@XZ
+; CHECK: class B<class A> __cdecl d3(void)
+
+?d4@@YAPAV?$B@VA@@@@XZ
+; CHECK: class B<class A> * __cdecl d4(void)
+
+?d5@@YAPBV?$B@VA@@@@XZ
+; CHECK: class B<class A> const * __cdecl d5(void)
+
+?d6@@YAPCV?$B@VA@@@@XZ
+; CHECK: class B<class A> volatile * __cdecl d6(void)
+
+?d7@@YAPDV?$B@VA@@@@XZ
+; CHECK: class B<class A> const volatile * __cdecl d7(void)
+
+?d8@@YAAAV?$B@VA@@@@XZ
+; CHECK: class B<class A> & __cdecl d8(void)
+
+?d9@@YAABV?$B@VA@@@@XZ
+; CHECK: class B<class A> const & __cdecl d9(void)
+
+?d10@@YAACV?$B@VA@@@@XZ
+; CHECK: class B<class A> volatile & __cdecl d10(void)
+
+?d11@@YAADV?$B@VA@@@@XZ
+; CHECK: class B<class A> const volatile & __cdecl d11(void)
+
+?e1@@YA?AW4Enum@@XZ
+; CHECK: Enum __cdecl e1(void)
+
+?e2@@YA?BW4Enum@@XZ
+; CHECK: Enum const __cdecl e2(void)
+
+?e3@@YAPAW4Enum@@XZ
+; CHECK: Enum * __cdecl e3(void)
+
+?e4@@YAAAW4Enum@@XZ
+; CHECK: Enum & __cdecl e4(void)
+
+?f1@@YA?AUS@@XZ
+; CHECK: struct S __cdecl f1(void)
+
+?f2@@YA?BUS@@XZ
+; CHECK: struct S const __cdecl f2(void)
+
+?f3@@YAPAUS@@XZ
+; CHECK: struct S * __cdecl f3(void)
+
+?f4@@YAPBUS@@XZ
+; CHECK: struct S const * __cdecl f4(void)
+
+?f5@@YAPDUS@@XZ
+; CHECK: struct S const volatile * __cdecl f5(void)
+
+?f6@@YAAAUS@@XZ
+; CHECK: struct S & __cdecl f6(void)
+
+?f7@@YAQAUS@@XZ
+; CHECK: struct S *const __cdecl f7(void)
+
+?f8@@YAPQS@@HXZ
+; CHECK: int S::* __cdecl f8(void)
+
+?f9@@YAQQS@@HXZ
+; CHECK: int S::*const __cdecl f9(void)
+
+; We print __restrict twice here.
+?f10@@YAPIQS@@HXZ
+; FIXME: int S::* __restrict __cdecl f10(void)
+
+; We print __restrict twice here.
+?f11@@YAQIQS@@HXZ
+; FIXME: int S::* __restrict __cdecl f11(void)
+
+?g1@@YAP6AHH@ZXZ
+; CHECK: int (__cdecl * __cdecl g1(void))(int)
+
+?g2@@YAQ6AHH@ZXZ
+; CHECK: int (__cdecl *const __cdecl g2(void))(int)
+
+?g3@@YAPAP6AHH@ZXZ
+; CHECK: int (__cdecl ** __cdecl g3(void))(int)
+
+?g4@@YAPBQ6AHH@ZXZ
+; CHECK: int (__cdecl *const * __cdecl g4(void))(int)
+
+?h1@@YAAIAHXZ
+; CHECK: int &__restrict __cdecl h1(void)
diff --git a/test/Demangle/ms-template-callback.test b/test/Demangle/ms-template-callback.test
new file mode 100644
index 000000000000..88c4493d2bb6
--- /dev/null
+++ b/test/Demangle/ms-template-callback.test
@@ -0,0 +1,53 @@
+; These tests are based on clang/test/CodeGenCXX/mangle-ms-template-callback.cpp
+
+; RUN: llvm-undname < %s | FileCheck %s
+
+; CHECK-NOT: Invalid mangled name
+
+?callback_void@@3V?$C@$$A6AXXZ@@A
+; CHECK: class C<void __cdecl(void)> callback_void
+
+?callback_void_volatile@@3V?$C@$$A6AXXZ@@C
+; CHECK: class C<void __cdecl(void)> volatile callback_void_volatile
+
+?callback_int@@3V?$C@$$A6AHXZ@@A
+; CHECK: C<int __cdecl(void)> callback_int
+
+?callback_Type@@3V?$C@$$A6A?AVType@@XZ@@A
+; CHECK: C<class Type __cdecl(void)> callback_Type
+
+?callback_void_int@@3V?$C@$$A6AXH@Z@@A
+; CHECK: C<void __cdecl(int)> callback_void_int
+
+?callback_int_int@@3V?$C@$$A6AHH@Z@@A
+; CHECK: C<int __cdecl(int)> callback_int_int
+
+?callback_void_Type@@3V?$C@$$A6AXVType@@@Z@@A
+; CHECK: C<void __cdecl(class Type)> callback_void_Type
+
+?foo@@YAXV?$C@$$A6AXXZ@@@Z
+; CHECK: void __cdecl foo(class C<void __cdecl(void)>)
+
+?function@@YAXV?$C@$$A6AXXZ@@@Z
+; CHECK: void __cdecl function(class C<void __cdecl(void)>)
+
+?function_pointer@@YAXV?$C@P6AXXZ@@@Z
+; CHECK: void __cdecl function_pointer(class C<void (__cdecl *)(void)>)
+
+?member_pointer@@YAXV?$C@P8Z@@AEXXZ@@@Z
+; CHECK: void __cdecl member_pointer(class C<void (__thiscall Z::*)(void)>)
+
+??$bar@P6AHH@Z@@YAXP6AHH@Z@Z
+; CHECK: void __cdecl bar<int (__cdecl *)(int)>(int (__cdecl *)(int))
+
+??$WrapFnPtr@$1?VoidFn@@YAXXZ@@YAXXZ
+; CHECK: void __cdecl WrapFnPtr<&void __cdecl VoidFn(void)>(void)
+
+??$WrapFnRef@$1?VoidFn@@YAXXZ@@YAXXZ
+; CHECK: void __cdecl WrapFnRef<&void __cdecl VoidFn(void)>(void)
+
+??$WrapFnPtr@$1?VoidStaticMethod@Thing@@SAXXZ@@YAXXZ
+; CHECK: void __cdecl WrapFnPtr<&static void __cdecl Thing::VoidStaticMethod(void)>(void)
+
+??$WrapFnRef@$1?VoidStaticMethod@Thing@@SAXXZ@@YAXXZ
+; CHECK: void __cdecl WrapFnRef<&static void __cdecl Thing::VoidStaticMethod(void)>(void)