@@ -1885,54 +1885,17 @@ impl<'a, S: Source<'a> + 'a> PDBParserInstance<'a, S> {
18851885 }
18861886 }
18871887
1888- // VTables have types on their data symbols,
1889- if let Some ( ( last, class_name) ) = name. split_last ( ) {
1890- if last. contains ( "`vftable'" ) {
1891- let mut vt_name = class_name. with_item ( "VTable" ) ;
1892- if last. contains ( "{for" ) {
1893- // DerivedClass::`vftable'{for `BaseClass'}
1894- let mut base_name = last. to_owned ( ) ;
1895- base_name. drain ( 0 ..( "`vftable'{for `" . len ( ) ) ) ;
1896- base_name. drain ( ( base_name. len ( ) - "'}" . len ( ) ) ..( base_name. len ( ) ) ) ;
1897- // Multiply inherited classes have multiple vtable types
1898- // TODO: Do that
1899- vt_name = QualifiedName :: new ( vec ! [ base_name, "VTable" . to_string( ) ] ) ;
1900- }
1901-
1902- vt_name = vt_name
1903- . replace ( "class " , "" )
1904- . replace ( "struct " , "" )
1905- . replace ( "enum " , "" ) ;
1906-
1907- if let Some ( ty) = self . named_types . get ( & vt_name) {
1908- t = Some ( Conf :: new (
1909- Type :: named_type_from_type ( vt_name, ty. as_ref ( ) ) ,
1910- DEMANGLE_CONFIDENCE ,
1911- ) ) ;
1912- } else {
1913- // Sometimes the demangler has trouble with `class Foo` in templates
1914- vt_name = vt_name
1915- . replace ( "class " , "" )
1916- . replace ( "struct " , "" )
1917- . replace ( "enum " , "" ) ;
1918-
1919- if let Some ( ty) = self . named_types . get ( & vt_name) {
1920- t = Some ( Conf :: new (
1921- Type :: named_type_from_type ( vt_name, ty. as_ref ( ) ) ,
1922- DEMANGLE_CONFIDENCE ,
1923- ) ) ;
1924- } else {
1925- t = Some ( Conf :: new (
1926- Type :: named_type_from_type (
1927- vt_name,
1928- Type :: structure ( StructureBuilder :: new ( ) . finalize ( ) . as_ref ( ) )
1929- . as_ref ( ) ,
1930- ) ,
1931- DEMANGLE_CONFIDENCE ,
1932- ) ) ;
1933- }
1934- }
1935- }
1888+ // VTables have types on their data symbols
1889+ // Format: "ClassName::`vftable'" or "ClassName::`vftable'{for `BaseClass'}"
1890+ if let Some ( vt_name) = parse_vtable_type_name ( & name) {
1891+ let ty =
1892+ self . named_types . get ( & vt_name) . cloned ( ) . unwrap_or_else ( || {
1893+ Type :: structure ( StructureBuilder :: new ( ) . finalize ( ) . as_ref ( ) )
1894+ } ) ;
1895+ t = Some ( Conf :: new (
1896+ Type :: named_type_from_type ( vt_name, ty. as_ref ( ) ) ,
1897+ DEMANGLE_CONFIDENCE ,
1898+ ) ) ;
19361899 }
19371900
19381901 if let Some ( last_name) = name. last_mut ( ) {
@@ -2041,3 +2004,72 @@ impl<'a, S: Source<'a> + 'a> PDBParserInstance<'a, S> {
20412004 }
20422005 }
20432006}
2007+
2008+ /// Parse a vtable symbol name and return the parts for the vtable type.
2009+ /// Takes the last element of (e.g., `"ClassName::`vftable'{for `BaseClass'}"`)
2010+ /// and converts it to a vtable type name (e.g., `["ClassName", "BaseClass", "VTable"]`).
2011+ fn parse_vtable_type_name ( name : & QualifiedName ) -> Option < QualifiedName > {
2012+ let ( last, qualified_name_base) = name. items . split_last ( ) ?;
2013+ let ( class_name, rest) = last. split_once ( "::`vftable'" ) ?;
2014+
2015+ let clean = |s : & str | {
2016+ s. replace ( "class " , "" )
2017+ . replace ( "struct " , "" )
2018+ . replace ( "enum " , "" )
2019+ } ;
2020+
2021+ let mut parts = qualified_name_base. to_vec ( ) ;
2022+ parts. extend ( class_name. split ( "::" ) . map ( |s| clean ( s) ) ) ;
2023+
2024+ if let Some ( base_class) = rest
2025+ . split_once ( "{for `" )
2026+ . and_then ( |( _, base_start) | base_start. split_once ( "'}" ) )
2027+ . map ( |( base, _) | base)
2028+ {
2029+ parts. push ( clean ( base_class) ) ;
2030+ }
2031+
2032+ parts. push ( "VTable" . to_string ( ) ) ;
2033+ Some ( QualifiedName :: new ( parts) )
2034+ }
2035+
2036+ #[ cfg( test) ]
2037+ mod tests {
2038+ use super :: * ;
2039+
2040+ /// (input_demangled_name, expected_output)
2041+ #[ rustfmt:: skip]
2042+ const VTABLE_TEST_CASES : & [ ( & str , & [ & str ] ) ] = & [
2043+ // Simple vtables
2044+ ( "Base::`vftable'" , & [ "Base" , "VTable" ] ) ,
2045+ // Multiple inheritance with {for}
2046+ ( "MultiDerived::`vftable'{for `InterfaceA'}" , & [ "MultiDerived" , "InterfaceA" , "VTable" ] ) ,
2047+ // Simple templates
2048+ ( "Container<int32_t>::`vftable'" , & [ "Container<int32_t>" , "VTable" ] ) ,
2049+ ( "Pair<int32_t,char>::`vftable'" , & [ "Pair<int32_t,char>" , "VTable" ] ) ,
2050+ ( "FixedArray<int32_t,10>::`vftable'" , & [ "FixedArray<int32_t,10>" , "VTable" ] ) ,
2051+ // Namespaced classes
2052+ ( "outer::inner::NamespacedTemplate<int32_t>::`vftable'" , & [ "outer" , "inner" , "NamespacedTemplate<int32_t>" , "VTable" ] ) ,
2053+ // Nested class inside template
2054+ ( "complex::HasNested<int32_t>::Nested::`vftable'" , & [ "complex" , "HasNested<int32_t>" , "Nested" , "VTable" ] ) ,
2055+ // Template with {for} clause
2056+ ( "TemplateMultiDerived<int32_t>::`vftable'{for `TemplateInterfaceA<int32_t>'}" , & [ "TemplateMultiDerived<int32_t>" , "TemplateInterfaceA<int32_t>" , "VTable" ] ) ,
2057+ ( "TemplateDiamond<int32_t>::`vftable'{for `TemplateLeftVirtual<int32_t>'}" , & [ "TemplateDiamond<int32_t>" , "TemplateLeftVirtual<int32_t>" , "VTable" ] ) ,
2058+ // Nested templates - note: "class " prefix gets stripped
2059+ ( "Wrapper<class Container<int32_t> >::`vftable'" , & [ "Wrapper<Container<int32_t> >" , "VTable" ] ) ,
2060+ // Class/struct/enum keyword removal
2061+ ( "Wrapper<class Foo>::`vftable'" , & [ "Wrapper<Foo>" , "VTable" ] ) ,
2062+ ( "Container<struct Bar>::`vftable'" , & [ "Container<Bar>" , "VTable" ] ) ,
2063+ ( "Holder<enum Baz>::`vftable'" , & [ "Holder<Baz>" , "VTable" ] ) ,
2064+ ] ;
2065+
2066+ #[ test]
2067+ fn test_vtable_type_name_parsing ( ) {
2068+ for ( input, expected) in VTABLE_TEST_CASES {
2069+ let name = QualifiedName :: new ( vec ! [ input. to_string( ) ] ) ;
2070+ let result = parse_vtable_type_name ( & name) ;
2071+ let expected_qn = QualifiedName :: new ( expected. into_iter ( ) . cloned ( ) ) ;
2072+ assert_eq ! ( result, Some ( expected_qn) , "Failed for input: {}" , input) ;
2073+ }
2074+ }
2075+ }
0 commit comments