// Copyright 2015 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package demangle import ( "strconv" "strings" "testing" ) // Check test cases discovered after the code passed the tests in // demangle-expected (which are tested by TestExpected). Some of this // are cases where we differ from the standard demangler, some we are // the same but we weren't initially. func TestDemangler(t *testing.T) { var tests = []struct { input string want string wantNoParams string wantNoTemplateParams string wantMinimal string }{ { "_ZNSaIcEC1ERKS_", "std::allocator::allocator(std::allocator const&)", "std::allocator::allocator", "std::allocator::allocator(std::allocator const&)", "std::allocator::allocator", }, { "_ZN9__gnu_cxx13stdio_filebufIcSt11char_traitsIcEEC1EP8_IO_FILESt13_Ios_Openmodem", "__gnu_cxx::stdio_filebuf >::stdio_filebuf(_IO_FILE*, std::_Ios_Openmode, unsigned long)", "__gnu_cxx::stdio_filebuf >::stdio_filebuf", "__gnu_cxx::stdio_filebuf::stdio_filebuf(_IO_FILE*, std::_Ios_Openmode, unsigned long)", "__gnu_cxx::stdio_filebuf::stdio_filebuf", }, { "_ZN1n1CcvNS_1DIT_EEI1EEEv", "n::C::operator n::D()", "n::C::operator n::D", "n::C::operator n::D()", "n::C::operator n::D", }, { "_Z1CIvPN1D1E1FIdJEEEdEPN1GILb0ET_T0_T1_E1HEPFS6_S7_S8_EN1H1I1JIS7_E1KENSG_IS8_E1KE", "G*, double>::H* C*, double>(void (*)(D::E::F*, double), H::I::J*>::K, H::I::J::K)", "C*, double>", "G::H* C(void (*)(D::E::F*, double), H::I::J::K, H::I::J::K)", "C", }, { "_ZZNK1CI1DIcSt1EIcESaIcEEJEE1FEvE1F", "C, std::allocator > >::F() const::F", "C, std::allocator > >::F() const::F", "C::F() const::F", "C::F() const::F", }, { "_ZN1CI1DSt1EIK1FN1G1HEEE1I1JIJRKS6_EEEvDpOT_", "void C >::I::J const&>(std::E const&)", "C >::I::J const&>", "void C::I::J(std::E const&)", "C::I::J", }, { "_ZN1C1D1E1FIJEEEvi1GDpT_", "void C::D::E::F<>(int, G)", "C::D::E::F<>", "void C::D::E::F(int, G)", "C::D::E::F", }, { "_ZN1CILj50ELb1EE1DEv", "C<50u, true>::D()", "C<50u, true>::D", "C::D()", "C::D", }, { "_ZN1CUt_C2Ev", "C::{unnamed type#1}::{unnamed type#1}()", "C::{unnamed type#1}::{unnamed type#1}", "C::{unnamed type#1}::{unnamed type#1}()", "C::{unnamed type#1}::{unnamed type#1}", }, { "_ZN1C12_GLOBAL__N_11DINS_1EEEEN1F1GIDTadcldtcvT__E1HEEEERKS5_NS_1I1JE", "F::G C::(anonymous namespace)::D(C::E const&, C::I::J)", "C::(anonymous namespace)::D", "F::G C::(anonymous namespace)::D(C::E const&, C::I::J)", "C::(anonymous namespace)::D", }, { "_ZN1CI1DE1EIJiRiRPKcRA1_S4_S8_bS6_S3_RjRPKN1F1GERPKN1H1IEEEEvDpOT_", "void C::E(int&&, int&, char const*&, char const (&) [1], char const (&) [1], bool&&, char const*&, int&, unsigned int&, F::G const*&, H::I const*&)", "C::E", "void C::E(int&&, int&, char const*&, char const (&) [1], char const (&) [1], bool&&, char const*&, int&, unsigned int&, F::G const*&, H::I const*&)", "C::E", }, { "_ZN1C12_GLOBAL__N_11DIFbPKNS_1EEEEEvPNS_1FERKT_", "void C::(anonymous namespace)::D(C::F*, bool (&)(C::E const*) const)", "C::(anonymous namespace)::D", "void C::(anonymous namespace)::D(C::F*, bool (&)(C::E const*) const)", "C::(anonymous namespace)::D", }, { "_ZN1C1D1EIJRFviSt1FIFvRKN1G1H1IEEERKSt6vectorINS_1JESaISB_EEERiS9_EvEENS0_1K1LIJDpNSt1MIT_E1NEEEEDpOSM_", "C::D::K::L, std::vector > const&)>::N, std::M::N, std::M >::N> C::D::E, std::vector > const&), int&, std::F, void>(void (&)(int, std::F, std::vector > const&), int&, std::F&&)", "C::D::E, std::vector > const&), int&, std::F, void>", "C::D::K::L C::D::E(void (&)(int, std::F, std::vector const&), int&, std::F&&)", "C::D::E", }, { "_ZN1C1D1E1FcvNS_1GIT_EEI1HEEv", "C::D::E::F::operator C::G()", "C::D::E::F::operator C::G", "C::D::E::F::operator C::G()", "C::D::E::F::operator C::G", }, { "_ZN9__gnu_cxx17__normal_iteratorIPK1EIN1F1G1HEESt6vectorIS5_SaIS5_EEEC2IPS5_EERKNS0_IT_NS_11__enable_ifIXsr3std10__are_sameISE_SD_EE7__valueESA_E1IEEE", "__gnu_cxx::__normal_iterator const*, std::vector, std::allocator > > >::__normal_iterator*>(__gnu_cxx::__normal_iterator*, __gnu_cxx::__enable_if*, E*>::__value, std::vector, std::allocator > > >::I> const&)", "__gnu_cxx::__normal_iterator const*, std::vector, std::allocator > > >::__normal_iterator*>", "__gnu_cxx::__normal_iterator::__normal_iterator(__gnu_cxx::__normal_iterator const&)", "__gnu_cxx::__normal_iterator::__normal_iterator", }, { "_ZNKSt1CIM1DKFjvEEclIJEvEEjPKS0_DpOT_", "unsigned int std::C::operator()(D const*) const", "std::C::operator()", "unsigned int std::C::operator()(D const*) const", "std::C::operator()", }, { "_ZNSt10_HashtableI12basic_stringIcSt11char_traitsIcESaIcEESt4pairIKS4_N1C1D1EEESaISA_ENSt8__detail10_Select1stESt8equal_toIS4_ESt4hashIS4_ENSC_18_Mod_range_hashingENSC_20_Default_ranged_hashENSC_20_Prime_rehash_policyENSC_17_Hashtable_traitsILb1ELb0ELb1EEEE9_M_assignIZNSN_C1ERKSN_EUlPKNSC_10_Hash_nodeISA_Lb1EEEE_EEvSQ_RKT_", "void std::_Hashtable, std::allocator >, std::pair, std::allocator > const, C::D::E>, std::allocator, std::allocator > const, C::D::E> >, std::__detail::_Select1st, std::equal_to, std::allocator > >, std::hash, std::allocator > >, std::__detail::_Mod_range_hashing, std::__detail::_Default_ranged_hash, std::__detail::_Prime_rehash_policy, std::__detail::_Hashtable_traits >::_M_assign, std::allocator >, std::pair, std::allocator > const, C::D::E>, std::allocator, std::allocator > const, C::D::E> >, std::__detail::_Select1st, std::equal_to, std::allocator > >, std::hash, std::allocator > >, std::__detail::_Mod_range_hashing, std::__detail::_Default_ranged_hash, std::__detail::_Prime_rehash_policy, std::__detail::_Hashtable_traits >::_Hashtable(std::_Hashtable, std::allocator >, std::pair, std::allocator > const, C::D::E>, std::allocator, std::allocator > const, C::D::E> >, std::__detail::_Select1st, std::equal_to, std::allocator > >, std::hash, std::allocator > >, std::__detail::_Mod_range_hashing, std::__detail::_Default_ranged_hash, std::__detail::_Prime_rehash_policy, std::__detail::_Hashtable_traits > const&)::{lambda(std::__detail::_Hash_node, std::allocator > const, C::D::E>, true> const*)#1}>(std::_Hashtable, std::allocator >, std::pair, std::allocator > const, C::D::E>, std::allocator, std::allocator > const, C::D::E> >, std::__detail::_Select1st, std::equal_to, std::allocator > >, std::hash, std::allocator > >, std::__detail::_Mod_range_hashing, std::__detail::_Default_ranged_hash, std::__detail::_Prime_rehash_policy, std::__detail::_Hashtable_traits > const&, std::_Hashtable, std::allocator >, std::pair, std::allocator > const, C::D::E>, std::allocator, std::allocator > const, C::D::E> >, std::__detail::_Select1st, std::equal_to, std::allocator > >, std::hash, std::allocator > >, std::__detail::_Mod_range_hashing, std::__detail::_Default_ranged_hash, std::__detail::_Prime_rehash_policy, std::__detail::_Hashtable_traits >::_Hashtable(std::_Hashtable, std::allocator >, std::pair, std::allocator > const, C::D::E>, std::allocator, std::allocator > const, C::D::E> >, std::__detail::_Select1st, std::equal_to, std::allocator > >, std::hash, std::allocator > >, std::__detail::_Mod_range_hashing, std::__detail::_Default_ranged_hash, std::__detail::_Prime_rehash_policy, std::__detail::_Hashtable_traits > const&)::{lambda(std::__detail::_Hash_node, std::allocator > const, C::D::E>, true> const*)#1} const&)", "std::_Hashtable, std::allocator >, std::pair, std::allocator > const, C::D::E>, std::allocator, std::allocator > const, C::D::E> >, std::__detail::_Select1st, std::equal_to, std::allocator > >, std::hash, std::allocator > >, std::__detail::_Mod_range_hashing, std::__detail::_Default_ranged_hash, std::__detail::_Prime_rehash_policy, std::__detail::_Hashtable_traits >::_M_assign, std::allocator >, std::pair, std::allocator > const, C::D::E>, std::allocator, std::allocator > const, C::D::E> >, std::__detail::_Select1st, std::equal_to, std::allocator > >, std::hash, std::allocator > >, std::__detail::_Mod_range_hashing, std::__detail::_Default_ranged_hash, std::__detail::_Prime_rehash_policy, std::__detail::_Hashtable_traits >::_Hashtable(std::_Hashtable, std::allocator >, std::pair, std::allocator > const, C::D::E>, std::allocator, std::allocator > const, C::D::E> >, std::__detail::_Select1st, std::equal_to, std::allocator > >, std::hash, std::allocator > >, std::__detail::_Mod_range_hashing, std::__detail::_Default_ranged_hash, std::__detail::_Prime_rehash_policy, std::__detail::_Hashtable_traits > const&)::{lambda(std::__detail::_Hash_node, std::allocator > const, C::D::E>, true> const*)#1}>", "void std::_Hashtable::_M_assign(std::_Hashtable const&, std::_Hashtable::_Hashtable(std::_Hashtable const&)::{lambda(std::__detail::_Hash_node const*)#1} const&)", "std::_Hashtable::_M_assign", }, { "_ZSt3maxIVdERKT_S3_S3_", "double const volatile& std::max(double const volatile&, double const volatile&)", "std::max", "double const volatile& std::max(double const volatile&, double const volatile&)", "std::max", }, { "_ZZN1C1D1E1F1G1HEvENUlvE_C2EOS4_", "C::D::E::F::G::H()::{lambda()#1}::{lambda()#1}({lambda()#1}&&)", "C::D::E::F::G::H()::{lambda()#1}::{lambda()#1}", "C::D::E::F::G::H()::{lambda()#1}::{lambda()#1}({lambda()#1}&&)", "C::D::E::F::G::H()::{lambda()#1}::{lambda()#1}", }, { "_ZThn8_NK1C1D1EEv", "non-virtual thunk to C::D::E() const", "non-virtual thunk to C::D::E() const", "non-virtual thunk to C::D::E() const", "non-virtual thunk to C::D::E() const", }, { "_ZTv0_n96_NK1C1D1E1FEv", "virtual thunk to C::D::E::F() const", "virtual thunk to C::D::E::F() const", "virtual thunk to C::D::E::F() const", "virtual thunk to C::D::E::F() const", }, { "_ZTCSt9strstream16_So", "construction vtable for std::ostream-in-std::strstream", "construction vtable for std::ostream-in-std::strstream", "construction vtable for std::ostream-in-std::strstream", "construction vtable for std::ostream-in-std::strstream", }, { "_ZGVZZN1C1D1EEvENK3$_0clEvE1F", "guard variable for C::D::E()::$_0::operator()() const::F", "guard variable for C::D::E()::$_0::operator()() const::F", "guard variable for C::D::E()::$_0::operator()() const::F", "guard variable for C::D::E()::$_0::operator()() const::F", }, { "_Z1fICiEvT_", "void f(int _Complex)", "f", "void f(int _Complex)", "f", }, { "_GLOBAL__D__Z2fnv", "global destructors keyed to fn()", "global destructors keyed to fn()", "global destructors keyed to fn()", "global destructors keyed to fn()", }, { "_Z1fIXadL_Z1hvEEEvv", "void f<&h>()", "f<&h>", "void f()", "f", }, { "_Z1CIP1DEiRK1EPT_N1F1GIS5_Xaasr1HIS5_E1IntsrSA_1JEE1KE", "int C(E const&, D**, F::G::I&&(!H::J)>::K)", "C", "int C(E const&, D**, F::G::K)", "C", }, { "_ZNO1A1B1C1DIZN1E1F1GINS3_1HE1IEEvMNS3_1JEFvP1LPKT_PT0_P1KESD_SA_SF_SH_EUlvE_Lb0EEcvPSB_ISG_vvEEv", "A::B::C::D(void (E::J::*)(L*, E::H const*, I*, K*), E::H const*, L*, I*, K*)::{lambda()#1}, false>::operator K*() &&", "A::B::C::D(void (E::J::*)(L*, E::H const*, I*, K*), E::H const*, L*, I*, K*)::{lambda()#1}, false>::operator K*", "A::B::C::D::operator K*() &&", "A::B::C::D::operator K*", }, { "_ZNSt1AIFSt1BImjEjEZN1C1DI1EEENSt1FIXeqsr1G1H1IIDTadsrT_onclEEE1JLi2EEvE1KEPKcSC_OS7_EUljE_E1KERKSt1Lj", "std::A (unsigned int), C::D(char const*, G::H::I, G&&)::{lambda(unsigned int)#1}>::K(std::L const&, unsigned int)", "std::A (unsigned int), C::D(char const*, G::H::I, G&&)::{lambda(unsigned int)#1}>::K", "std::A::K(std::L const&, unsigned int)", "std::A::K", }, { "_ZNSt1AIFSt1BImjEjEZN1L1CIUljE_EENSt1DIXeqsrN1E1F1GIDTadsrT_clEEE1HLi2EEvE1IEPKcSG_OSA_EUljE_E1JERKSt1Kj", "std::A (unsigned int), L::C<{lambda(unsigned int)#1}>(char const*, char const*, {lambda(unsigned int)#1}&&)::{lambda(unsigned int)#1}>::J(std::K const&, unsigned int)", "std::A (unsigned int), L::C<{lambda(unsigned int)#1}>(char const*, char const*, {lambda(unsigned int)#1}&&)::{lambda(unsigned int)#1}>::J", "std::A::J(std::K const&, unsigned int)", "std::A::J", }, { "_ZNSt1A1BIiNS_1CIiEEE1DIPiEENS_1EIXaasr1FIT_EE1Gsr1HIiNS_1IIS7_E1JEEE1KEvE1LES7_S7_", "std::A::E::G&&H::J>::K, void>::L std::A::B >::D(F, F)", "std::A::B >::D", "std::A::E::L std::A::B::D(F, F)", "std::A::B::D", }, { "_ZNO1A1B1C1DIJOZZN1E1F1GINS4_1HINS4_1IINS4_1JEEEEEJNS4_1KEEEEN1L1MINS4_1OINT_1PEEEEERKSt6vectorIN1Q1RESaISL_EERKN3gtl1S1TIN1U1VEEERKNS4_1W1XERKNS4_1YERKNSQ_1ZINS4_1aEEEPSt13unordered_mapISL_NSK_9UniquePtrINS4_1bINS0_1cIJS9_NS7_INST_1dEEEEEENS4_1fEEEEENSC_1g1hIvEESt8equal_toISL_ESaISt4pairIKSL_S1J_EEEDpRKT0_ENKUlSL_mmS1G_E_clESL_mmS1G_EUlS9_E_OZZNS5_ISA_JSB_EEESI_SP_SX_S11_S14_S19_S1U_S1Y_ENKS1Z_clESL_mmS1G_EUlS1F_E0_EEclIJRS9_EEEDTclcl1iIXsrNS1_1jISt5tupleIJNS1_1kIS21_EENS29_IS23_EEEEJDpT_EEE1lEEcl1mIS2C_EEEspcl1mIS2D_EEEEDpOS2D_", "decltype (((i)#1}>, E::F::I& >, E::F::K>(std::vector > const&, gtl::S::T const&, E::F::W::X const&, E::F::Y const&, gtl::Z const&, std::unordered_map, E::F::I >, E::F::f> >, L::g::h, std::equal_to, std::allocator, E::F::I >, E::F::f> > > > >*, E::F::K const&)::{lambda(Q::R, unsigned long, unsigned long, A::B::c, E::F::I >)#1}::operator()(Q::R, unsigned long, unsigned long, A::B::c, E::F::I >) const::{lambda(E::F::I)#1}&&> >, E::F::I&>::l>)((m)()))(((m)#1}> >)())...)) A::B::C::D >, E::F::K>(std::vector > const&, gtl::S::T const&, E::F::W::X const&, E::F::Y const&, gtl::Z const&, std::unordered_map, E::F::I >, E::F::f> >, L::g::h, std::equal_to, std::allocator, E::F::I >, E::F::f> > > > >*, E::F::K const&)::{lambda(Q::R, unsigned long, unsigned long, A::B::c, E::F::I >)#1}::operator()(Q::R, unsigned long, unsigned long, A::B::c, E::F::I >) const::{lambda(E::F::I)#1}&&, E::F::G >, E::F::K>(std::vector > const&, gtl::S::T const&, E::F::W::X const&, E::F::Y const&, gtl::Z const&, std::unordered_map, E::F::I >, E::F::f> >, L::g::h, std::equal_to, std::allocator, E::F::I >, E::F::f> > > > >*, E::F::K const&)::{lambda(Q::R, unsigned long, unsigned long, A::B::c, E::F::I >)#1}::operator()(Q::R, unsigned long, unsigned long, A::B::c, E::F::I >) const::{lambda(E::F::I)#2}&&>::operator()&>((A::B::C::k<{lambda(E::F::I)#1}>&&)...) &&", "A::B::C::D >, E::F::K>(std::vector > const&, gtl::S::T const&, E::F::W::X const&, E::F::Y const&, gtl::Z const&, std::unordered_map, E::F::I >, E::F::f> >, L::g::h, std::equal_to, std::allocator, E::F::I >, E::F::f> > > > >*, E::F::K const&)::{lambda(Q::R, unsigned long, unsigned long, A::B::c, E::F::I >)#1}::operator()(Q::R, unsigned long, unsigned long, A::B::c, E::F::I >) const::{lambda(E::F::I)#1}&&, E::F::G >, E::F::K>(std::vector > const&, gtl::S::T const&, E::F::W::X const&, E::F::Y const&, gtl::Z const&, std::unordered_map, E::F::I >, E::F::f> >, L::g::h, std::equal_to, std::allocator, E::F::I >, E::F::f> > > > >*, E::F::K const&)::{lambda(Q::R, unsigned long, unsigned long, A::B::c, E::F::I >)#1}::operator()(Q::R, unsigned long, unsigned long, A::B::c, E::F::I >) const::{lambda(E::F::I)#2}&&>::operator()&>", "decltype (((i)((m)()))(((m)())...)) A::B::C::D::operator()((A::B::C::k&&)...) &&", "A::B::C::D::operator()", }, { "_ZcvAna_eE_e", "operator long double [new long double]", "operator long double [new long double]", "operator long double [new long double]", "operator long double [new long double]", }, { "_ZZ1irFeeEES_S_", "i(() restrict)::long double (long double)(() restrict) restrict", "i(long double (long double) restrict)::long double (long double)", "i(() restrict)::long double (long double)(() restrict) restrict", "i(long double (long double) restrict)::long double (long double)", }, { "_Z1_VFaeEZS_S_ES_", "_((() volatile) volatile, signed char (long double)(() volatile) volatile::(() volatile) volatile)", "_", "_((() volatile) volatile, signed char (long double)(() volatile) volatile::(() volatile) volatile)", "_", }, { "_ZdsrFliEZS_GS_EcvS_", "operator.*(( ( _Imaginary)( _Imaginary) restrict) restrict, long (int)( ( _Imaginary)( _Imaginary) restrict) restrict::operator ( ( _Imaginary)( _Imaginary) restrict) restrict)", "operator.*", "operator.*(( ( _Imaginary)( _Imaginary) restrict) restrict, long (int)( ( _Imaginary)( _Imaginary) restrict) restrict::operator ( ( _Imaginary)( _Imaginary) restrict) restrict)", "operator.*", }, } for _, test := range tests { if got, err := ToString(test.input); err != nil { t.Errorf("demangling %s: unexpected error %v", test.input, err) } else if got != test.want { t.Errorf("demangling %s: got %s, want %s", test.input, got, test.want) } if got, err := ToString(test.input, NoParams); err != nil { t.Errorf("demangling NoParams %s: unexpected error %v", test.input, err) } else if got != test.wantNoParams { t.Errorf("demangling NoParams %s: got %s, want %s", test.input, got, test.wantNoParams) } if got, err := ToString(test.input, NoTemplateParams); err != nil { t.Errorf("demangling NoTemplateParams %s: unexpected error %v", test.input, err) } else if got != test.wantNoTemplateParams { t.Errorf("demangling NoTemplateParams %s: got %s, want %s", test.input, got, test.wantNoTemplateParams) } if got, err := ToString(test.input, NoParams, NoTemplateParams); err != nil { t.Errorf("demangling NoTemplateParams %s: unexpected error %v", test.input, err) } else if got != test.wantMinimal { t.Errorf("demangling Minimal %s: got %s, want %s", test.input, got, test.wantMinimal) } // Test Filter also. if got := Filter(test.input); got != test.want { t.Errorf("Filter(%s) == %s, want %s", test.input, got, test.want) } } } // Test for some failure cases. func TestFailure(t *testing.T) { var tests = []struct { input string error string off int }{ { "_Z1FE", "unparsed characters at end of mangled name", 4, }, { "_Z1FQ", "unrecognized type code", 4, }, { "_ZZSaIL0D", "expected positive number", 8, }, { "_ZNKE", "expected prefix", 4, }, { "_ZcvT_", "not in scope of template", 6, }, { "_Z1AIXsZ1_EE", "missing argument pack", 8, }, { "_Z1gIEDTclspilE", "expected expression", 15, }, { "_ZNcvZN1ET_IEE", "after local name", 14, }, { "_Zv00", "expected positive number", 5, }, { "_ZcvT_B2T0", "template parameter not in scope", 10, }, { "_ZStcvT_", "template parameter not in scope", 8, }, { "_Z1aIeEU1RT_ZcvS1_", "expected E after local name", 18, }, { "_ZNcvT_oRIEE", "template index out of range", 11, }, { "_ZNcvT_D0IIEE", "expected prefix", 13, }, { "_ZcvT_IAoncvT__eE", "template parameter not in scope", 17, }, } for _, test := range tests { got, err := ToString(test.input) if err == nil { t.Errorf("unexpected success for %s: %s", test.input, got) } else if !strings.Contains(err.Error(), test.error) { t.Errorf("unexpected error for %s: %v", test.input, err) } else { s := err.Error() i := strings.LastIndex(s, " at ") if i < 0 { t.Errorf("missing offset in error for %s: %v", test.input, err) } else { off, oerr := strconv.Atoi(s[i+4:]) if oerr != nil { t.Errorf("can't parse offset (%s) for %s: %v", s[i+4:], test.input, err) } else if off != test.off { t.Errorf("unexpected offset for %s: got %d, want %d", test.input, off, test.off) } } } if got := Filter(test.input); got != test.input { t.Errorf("Filter(%s) == %s, want %s", test.input, got, test.input) } } }