1 //===-- CPlusPlusLanguageTest.cpp -----------------------------------------===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 #include "Plugins/Language/CPlusPlus/CPlusPlusLanguage.h" 9 #include "Plugins/Language/CPlusPlus/CPlusPlusNameParser.h" 10 #include "TestingSupport/SubsystemRAII.h" 11 #include "lldb/lldb-enumerations.h" 12 #include "gmock/gmock.h" 13 #include "gtest/gtest.h" 14 #include <optional> 15 16 using namespace lldb_private; 17 18 TEST(CPlusPlusLanguage, MethodNameParsing) { 19 struct TestCase { 20 std::string input; 21 std::string return_type, context, basename, arguments, qualifiers, 22 scope_qualified_name; 23 }; 24 25 TestCase test_cases[] = { 26 {"main(int, char *[]) ", "", "", "main", "(int, char *[])", "", "main"}, 27 {"foo::bar(baz) const", "", "foo", "bar", "(baz)", "const", "foo::bar"}, 28 {"foo::~bar(baz)", "", "foo", "~bar", "(baz)", "", "foo::~bar"}, 29 {"a::b::c::d(e,f)", "", "a::b::c", "d", "(e,f)", "", "a::b::c::d"}, 30 {"void f(int)", "void", "", "f", "(int)", "", "f"}, 31 32 // Operators 33 {"std::basic_ostream<char, std::char_traits<char> >& " 34 "std::operator<<<std::char_traits<char> >(std::basic_ostream<char, " 35 "std::char_traits<char> >&, char const*)", 36 "std::basic_ostream<char, std::char_traits<char> >&", "std", 37 "operator<<<std::char_traits<char> >", 38 "(std::basic_ostream<char, std::char_traits<char> >&, char const*)", "", 39 "std::operator<<<std::char_traits<char> >"}, 40 {"operator delete[](void*, clang::ASTContext const&, unsigned long)", "", 41 "", "operator delete[]", 42 "(void*, clang::ASTContext const&, unsigned long)", "", 43 "operator delete[]"}, 44 {"std::optional<clang::PostInitializer>::operator bool() const", "", 45 "std::optional<clang::PostInitializer>", "operator bool", "()", "const", 46 "std::optional<clang::PostInitializer>::operator bool"}, 47 {"(anonymous namespace)::FactManager::operator[](unsigned short)", "", 48 "(anonymous namespace)::FactManager", "operator[]", "(unsigned short)", 49 "", "(anonymous namespace)::FactManager::operator[]"}, 50 {"const int& std::map<int, pair<short, int>>::operator[](short) const", 51 "const int&", "std::map<int, pair<short, int>>", "operator[]", "(short)", 52 "const", "std::map<int, pair<short, int>>::operator[]"}, 53 {"CompareInsn::operator()(llvm::StringRef, InsnMatchEntry const&)", "", 54 "CompareInsn", "operator()", "(llvm::StringRef, InsnMatchEntry const&)", 55 "", "CompareInsn::operator()"}, 56 {"std::optional<llvm::MCFixupKind>::operator*() const &", "", 57 "std::optional<llvm::MCFixupKind>", "operator*", "()", "const &", 58 "std::optional<llvm::MCFixupKind>::operator*"}, 59 {"auto std::__1::ranges::__begin::__fn::operator()[abi:v160000]<char " 60 "const, 18ul>(char const (&) [18ul]) const", 61 "auto", "std::__1::ranges::__begin::__fn", 62 "operator()[abi:v160000]<char const, 18ul>", "(char const (&) [18ul])", 63 "const", 64 "std::__1::ranges::__begin::__fn::operator()[abi:v160000]<char const, " 65 "18ul>"}, 66 // Internal classes 67 {"operator<<(Cls, Cls)::Subclass::function()", "", 68 "operator<<(Cls, Cls)::Subclass", "function", "()", "", 69 "operator<<(Cls, Cls)::Subclass::function"}, 70 {"SAEC::checkFunction(context&) const::CallBack::CallBack(int)", "", 71 "SAEC::checkFunction(context&) const::CallBack", "CallBack", "(int)", "", 72 "SAEC::checkFunction(context&) const::CallBack::CallBack"}, 73 // Anonymous namespace 74 {"XX::(anonymous namespace)::anon_class::anon_func() const", "", 75 "XX::(anonymous namespace)::anon_class", "anon_func", "()", "const", 76 "XX::(anonymous namespace)::anon_class::anon_func"}, 77 78 // Lambda 79 {"main::{lambda()#1}::operator()() const::{lambda()#1}::operator()() " 80 "const", 81 "", "main::{lambda()#1}::operator()() const::{lambda()#1}", "operator()", 82 "()", "const", 83 "main::{lambda()#1}::operator()() const::{lambda()#1}::operator()"}, 84 85 // Function pointers 86 {"string (*f(vector<int>&&))(float)", "", "", "f", "(vector<int>&&)", "", 87 "f"}, 88 {"void (*&std::_Any_data::_M_access<void (*)()>())()", "", 89 "std::_Any_data", "_M_access<void (*)()>", "()", "", 90 "std::_Any_data::_M_access<void (*)()>"}, 91 {"void (*(*(*(*(*(*(*(* const&func1(int))())())())())())())())()", "", "", 92 "func1", "(int)", "", "func1"}, 93 94 // Decltype 95 {"decltype(nullptr)&& std::forward<decltype(nullptr)>" 96 "(std::remove_reference<decltype(nullptr)>::type&)", 97 "decltype(nullptr)&&", "std", "forward<decltype(nullptr)>", 98 "(std::remove_reference<decltype(nullptr)>::type&)", "", 99 "std::forward<decltype(nullptr)>"}, 100 101 // Templates 102 {"void llvm::PM<llvm::Module, llvm::AM<llvm::Module>>::" 103 "addPass<llvm::VP>(llvm::VP)", 104 "void", "llvm::PM<llvm::Module, llvm::AM<llvm::Module>>", 105 "addPass<llvm::VP>", "(llvm::VP)", "", 106 "llvm::PM<llvm::Module, llvm::AM<llvm::Module>>::" 107 "addPass<llvm::VP>"}, 108 {"void std::vector<Class, std::allocator<Class> >" 109 "::_M_emplace_back_aux<Class const&>(Class const&)", 110 "void", "std::vector<Class, std::allocator<Class> >", 111 "_M_emplace_back_aux<Class const&>", "(Class const&)", "", 112 "std::vector<Class, std::allocator<Class> >::" 113 "_M_emplace_back_aux<Class const&>"}, 114 {"unsigned long llvm::countTrailingOnes<unsigned int>" 115 "(unsigned int, llvm::ZeroBehavior)", 116 "unsigned long", "llvm", "countTrailingOnes<unsigned int>", 117 "(unsigned int, llvm::ZeroBehavior)", "", 118 "llvm::countTrailingOnes<unsigned int>"}, 119 {"std::enable_if<(10u)<(64), bool>::type llvm::isUInt<10u>(unsigned " 120 "long)", 121 "std::enable_if<(10u)<(64), bool>::type", "llvm", "isUInt<10u>", 122 "(unsigned long)", "", "llvm::isUInt<10u>"}, 123 {"f<A<operator<(X,Y)::Subclass>, sizeof(B)<sizeof(C)>()", "", "", 124 "f<A<operator<(X,Y)::Subclass>, sizeof(B)<sizeof(C)>", "()", "", 125 "f<A<operator<(X,Y)::Subclass>, sizeof(B)<sizeof(C)>"}, 126 {"std::optional<llvm::MCFixupKind>::operator*() const volatile &&", "", 127 "std::optional<llvm::MCFixupKind>", "operator*", "()", 128 "const volatile &&", "std::optional<llvm::MCFixupKind>::operator*"}, 129 {"void foo<Dummy<char [10]>>()", "void", "", "foo<Dummy<char [10]>>", 130 "()", "", "foo<Dummy<char [10]>>"}, 131 {"void foo<Bar<Bar<int>[10]>>()", "void", "", "foo<Bar<Bar<int>[10]>>", 132 "()", "", "foo<Bar<Bar<int>[10]>>"}, 133 {"void foo<Bar[10]>()", "void", "", "foo<Bar[10]>", "()", "", 134 "foo<Bar[10]>"}, 135 {"void foo<Bar[]>()", "void", "", "foo<Bar[]>", "()", "", "foo<Bar[]>"}, 136 137 // auto return type 138 {"auto std::test_return_auto<int>() const", "auto", "std", 139 "test_return_auto<int>", "()", "const", "std::test_return_auto<int>"}, 140 {"decltype(auto) std::test_return_auto<int>(int) const", "decltype(auto)", 141 "std", "test_return_auto<int>", "(int)", "const", 142 "std::test_return_auto<int>"}, 143 144 // abi_tag on class method 145 {"v1::v2::Dummy[abi:c1][abi:c2]<v1::v2::Dummy[abi:c1][abi:c2]<int>> " 146 "v1::v2::Dummy[abi:c1][abi:c2]<v1::v2::Dummy[abi:c1][abi:c2]<int>>" 147 "::method2<v1::v2::Dummy[abi:c1][abi:c2]<v1::v2::Dummy[abi:c1][abi:c2]<" 148 "int>>>(int, v1::v2::Dummy<int>) const &&", 149 // Return type 150 "v1::v2::Dummy[abi:c1][abi:c2]<v1::v2::Dummy[abi:c1][abi:c2]<int>>", 151 // Context 152 "v1::v2::Dummy[abi:c1][abi:c2]<v1::v2::Dummy[abi:c1][abi:c2]<int>>", 153 // Basename 154 "method2<v1::v2::Dummy[abi:c1][abi:c2]<v1::v2::Dummy[abi:c1][abi:c2]<" 155 "int>>>", 156 // Args, qualifiers 157 "(int, v1::v2::Dummy<int>)", "const &&", 158 // Full scope-qualified name without args 159 "v1::v2::Dummy[abi:c1][abi:c2]<v1::v2::Dummy[abi:c1][abi:c2]<int>>" 160 "::method2<v1::v2::Dummy[abi:c1][abi:c2]<v1::v2::Dummy[abi:c1][abi:c2]<" 161 "int>>>"}, 162 163 // abi_tag on free function and template argument 164 {"v1::v2::Dummy[abi:c1][abi:c2]<v1::v2::Dummy[abi:c1][abi:c2]<int>> " 165 "v1::v2::with_tag_in_ns[abi:f1][abi:f2]<v1::v2::Dummy[abi:c1][abi:c2]" 166 "<v1::v2::Dummy[abi:c1][abi:c2]<int>>>(int, v1::v2::Dummy<int>) const " 167 "&&", 168 // Return type 169 "v1::v2::Dummy[abi:c1][abi:c2]<v1::v2::Dummy[abi:c1][abi:c2]<int>>", 170 // Context 171 "v1::v2", 172 // Basename 173 "with_tag_in_ns[abi:f1][abi:f2]<v1::v2::Dummy[abi:c1][abi:c2]<v1::v2::" 174 "Dummy[abi:c1][abi:c2]<int>>>", 175 // Args, qualifiers 176 "(int, v1::v2::Dummy<int>)", "const &&", 177 // Full scope-qualified name without args 178 "v1::v2::with_tag_in_ns[abi:f1][abi:f2]<v1::v2::Dummy[abi:c1][abi:c2]<" 179 "v1::v2::Dummy[abi:c1][abi:c2]<int>>>"}, 180 181 // abi_tag with special characters 182 {"auto ns::with_tag_in_ns[abi:special tag,0.0][abi:special " 183 "tag,1.0]<Dummy<int>>" 184 "(float) const &&", 185 // Return type 186 "auto", 187 // Context 188 "ns", 189 // Basename 190 "with_tag_in_ns[abi:special tag,0.0][abi:special tag,1.0]<Dummy<int>>", 191 // Args, qualifiers 192 "(float)", "const &&", 193 // Full scope-qualified name without args 194 "ns::with_tag_in_ns[abi:special tag,0.0][abi:special " 195 "tag,1.0]<Dummy<int>>"}, 196 197 // abi_tag on operator overloads 198 {"std::__1::error_code::operator bool[abi:v160000]() const", "", 199 "std::__1::error_code", "operator bool[abi:v160000]", "()", "const", 200 "std::__1::error_code::operator bool[abi:v160000]"}, 201 202 {"auto ns::foo::operator[][abi:v160000](size_t) const", "auto", "ns::foo", 203 "operator[][abi:v160000]", "(size_t)", "const", 204 "ns::foo::operator[][abi:v160000]"}, 205 206 {"auto Foo[abi:abc]<int>::operator<<<Foo[abi:abc]<int>>(int) &", "auto", 207 "Foo[abi:abc]<int>", "operator<<<Foo[abi:abc]<int>>", "(int)", "&", 208 "Foo[abi:abc]<int>::operator<<<Foo[abi:abc]<int>>"}}; 209 210 for (const auto &test : test_cases) { 211 CPlusPlusLanguage::MethodName method(ConstString(test.input)); 212 EXPECT_TRUE(method.IsValid()) << test.input; 213 if (method.IsValid()) { 214 EXPECT_EQ(test.return_type, method.GetReturnType().str()); 215 EXPECT_EQ(test.context, method.GetContext().str()); 216 EXPECT_EQ(test.basename, method.GetBasename().str()); 217 EXPECT_EQ(test.arguments, method.GetArguments().str()); 218 EXPECT_EQ(test.qualifiers, method.GetQualifiers().str()); 219 EXPECT_EQ(test.scope_qualified_name, method.GetScopeQualifiedName()); 220 } 221 } 222 } 223 224 TEST(CPlusPlusLanguage, InvalidMethodNameParsing) { 225 // Tests that we correctly reject malformed function names 226 227 std::string test_cases[] = { 228 "int Foo::operator[]<[10>()", 229 "Foo::operator bool[10]()", 230 "auto A::operator<=>[abi:tag]<A::B>()", 231 "auto A::operator<<<(int)", 232 "auto A::operator>>>(int)", 233 "auto A::operator<<<Type[abi:tag]<>(int)", 234 "auto A::operator<<<Type[abi:tag]<Type<int>>(int)", 235 "auto A::foo[(int)", 236 "auto A::foo[](int)", 237 "auto A::foo[bar](int)", 238 "auto A::foo[abi](int)", 239 "auto A::foo[abi:(int)", 240 }; 241 242 for (const auto &name : test_cases) { 243 CPlusPlusLanguage::MethodName method{ConstString(name)}; 244 EXPECT_FALSE(method.IsValid()) << name; 245 } 246 } 247 248 TEST(CPlusPlusLanguage, ContainsPath) { 249 CPlusPlusLanguage::MethodName 250 reference_1(ConstString("int foo::bar::func01(int a, double b)")); 251 CPlusPlusLanguage::MethodName 252 reference_2(ConstString("int foofoo::bar::func01(std::string a, int b)")); 253 CPlusPlusLanguage::MethodName reference_3(ConstString("int func01()")); 254 CPlusPlusLanguage::MethodName 255 reference_4(ConstString("bar::baz::operator bool()")); 256 CPlusPlusLanguage::MethodName reference_5( 257 ConstString("bar::baz::operator bool<int, Type<double>>()")); 258 CPlusPlusLanguage::MethodName reference_6(ConstString( 259 "bar::baz::operator<<<Type<double>, Type<std::vector<double>>>()")); 260 261 EXPECT_TRUE(reference_1.ContainsPath("")); 262 EXPECT_TRUE(reference_1.ContainsPath("func01")); 263 EXPECT_TRUE(reference_1.ContainsPath("bar::func01")); 264 EXPECT_TRUE(reference_1.ContainsPath("foo::bar::func01")); 265 EXPECT_FALSE(reference_1.ContainsPath("func")); 266 EXPECT_FALSE(reference_1.ContainsPath("baz::func01")); 267 EXPECT_FALSE(reference_1.ContainsPath("::bar::func01")); 268 EXPECT_FALSE(reference_1.ContainsPath("::foo::baz::func01")); 269 EXPECT_FALSE(reference_1.ContainsPath("foo::bar::baz::func01")); 270 271 EXPECT_TRUE(reference_2.ContainsPath("")); 272 EXPECT_TRUE(reference_2.ContainsPath("foofoo::bar::func01")); 273 EXPECT_FALSE(reference_2.ContainsPath("foo::bar::func01")); 274 275 EXPECT_TRUE(reference_3.ContainsPath("")); 276 EXPECT_TRUE(reference_3.ContainsPath("func01")); 277 EXPECT_FALSE(reference_3.ContainsPath("func")); 278 EXPECT_FALSE(reference_3.ContainsPath("bar::func01")); 279 280 EXPECT_TRUE(reference_4.ContainsPath("")); 281 EXPECT_TRUE(reference_4.ContainsPath("operator")); 282 EXPECT_TRUE(reference_4.ContainsPath("operator bool")); 283 EXPECT_TRUE(reference_4.ContainsPath("baz::operator bool")); 284 EXPECT_TRUE(reference_4.ContainsPath("bar::baz::operator bool")); 285 EXPECT_FALSE(reference_4.ContainsPath("az::operator bool")); 286 287 EXPECT_TRUE(reference_5.ContainsPath("")); 288 EXPECT_TRUE(reference_5.ContainsPath("operator")); 289 EXPECT_TRUE(reference_5.ContainsPath("operator bool")); 290 EXPECT_TRUE(reference_5.ContainsPath("operator bool<int, Type<double>>")); 291 EXPECT_FALSE(reference_5.ContainsPath("operator bool<int, double>")); 292 EXPECT_FALSE(reference_5.ContainsPath("operator bool<int, Type<int>>")); 293 294 EXPECT_TRUE(reference_6.ContainsPath("")); 295 EXPECT_TRUE(reference_6.ContainsPath("operator")); 296 EXPECT_TRUE(reference_6.ContainsPath("operator<<")); 297 EXPECT_TRUE(reference_6.ContainsPath( 298 "bar::baz::operator<<<Type<double>, Type<std::vector<double>>>()")); 299 EXPECT_FALSE(reference_6.ContainsPath("operator<<<Type<double>>")); 300 } 301 302 TEST(CPlusPlusLanguage, ExtractContextAndIdentifier) { 303 struct TestCase { 304 std::string input; 305 std::string context, basename; 306 }; 307 308 TestCase test_cases[] = { 309 {"main", "", "main"}, 310 {"main ", "", "main"}, 311 {"foo01::bar", "foo01", "bar"}, 312 {"foo::~bar", "foo", "~bar"}, 313 {"std::vector<int>::push_back", "std::vector<int>", "push_back"}, 314 {"operator<<(Cls, Cls)::Subclass::function", 315 "operator<<(Cls, Cls)::Subclass", "function"}, 316 {"std::vector<Class, std::allocator<Class>>" 317 "::_M_emplace_back_aux<Class const&>", 318 "std::vector<Class, std::allocator<Class>>", 319 "_M_emplace_back_aux<Class const&>"}, 320 {"`anonymous namespace'::foo", "`anonymous namespace'", "foo"}, 321 {"`operator<<A>'::`2'::B<0>::operator>", "`operator<<A>'::`2'::B<0>", 322 "operator>"}, 323 {"`anonymous namespace'::S::<<::__l2::Foo", 324 "`anonymous namespace'::S::<<::__l2", "Foo"}, 325 // These cases are idiosyncratic in how clang generates debug info for 326 // names when we have template parameters. They are not valid C++ names 327 // but if we fix this we need to support them for older compilers. 328 {"A::operator><A::B>", "A", "operator><A::B>"}, 329 {"operator><A::B>", "", "operator><A::B>"}, 330 {"A::operator<<A::B>", "A", "operator<<A::B>"}, 331 {"operator<<A::B>", "", "operator<<A::B>"}, 332 {"A::operator<<<A::B>", "A", "operator<<<A::B>"}, 333 {"operator<<<A::B>", "", "operator<<<A::B>"}, 334 }; 335 336 llvm::StringRef context, basename; 337 for (const auto &test : test_cases) { 338 EXPECT_TRUE(CPlusPlusLanguage::ExtractContextAndIdentifier( 339 test.input.c_str(), context, basename)); 340 EXPECT_EQ(test.context, context.str()); 341 EXPECT_EQ(test.basename, basename.str()); 342 } 343 344 EXPECT_FALSE(CPlusPlusLanguage::ExtractContextAndIdentifier("void", context, 345 basename)); 346 EXPECT_FALSE( 347 CPlusPlusLanguage::ExtractContextAndIdentifier("321", context, basename)); 348 EXPECT_FALSE( 349 CPlusPlusLanguage::ExtractContextAndIdentifier("", context, basename)); 350 EXPECT_FALSE(CPlusPlusLanguage::ExtractContextAndIdentifier( 351 "selector:", context, basename)); 352 EXPECT_FALSE(CPlusPlusLanguage::ExtractContextAndIdentifier( 353 "selector:otherField:", context, basename)); 354 EXPECT_FALSE(CPlusPlusLanguage::ExtractContextAndIdentifier( 355 "abc::", context, basename)); 356 EXPECT_FALSE(CPlusPlusLanguage::ExtractContextAndIdentifier( 357 "f<A<B><C>>", context, basename)); 358 359 // We expect these cases to fail until we turn on C++2a 360 EXPECT_FALSE(CPlusPlusLanguage::ExtractContextAndIdentifier( 361 "A::operator<=><A::B>", context, basename)); 362 EXPECT_FALSE(CPlusPlusLanguage::ExtractContextAndIdentifier( 363 "operator<=><A::B>", context, basename)); 364 } 365 366 static std::vector<std::string> GenerateAlternate(llvm::StringRef Name) { 367 std::vector<std::string> Strings; 368 if (Language *CPlusPlusLang = 369 Language::FindPlugin(lldb::eLanguageTypeC_plus_plus)) { 370 std::vector<ConstString> Results = 371 CPlusPlusLang->GenerateAlternateFunctionManglings(ConstString(Name)); 372 for (ConstString Str : Results) 373 Strings.push_back(std::string(Str.GetStringRef())); 374 } 375 return Strings; 376 } 377 378 TEST(CPlusPlusLanguage, GenerateAlternateFunctionManglings) { 379 using namespace testing; 380 381 SubsystemRAII<CPlusPlusLanguage> lang; 382 383 EXPECT_THAT(GenerateAlternate("_ZN1A1fEv"), 384 UnorderedElementsAre("_ZNK1A1fEv", "_ZLN1A1fEv")); 385 EXPECT_THAT(GenerateAlternate("_ZN1A1fEa"), Contains("_ZN1A1fEc")); 386 EXPECT_THAT(GenerateAlternate("_ZN1A1fEx"), Contains("_ZN1A1fEl")); 387 EXPECT_THAT(GenerateAlternate("_ZN1A1fEy"), Contains("_ZN1A1fEm")); 388 EXPECT_THAT(GenerateAlternate("_ZN1A1fEai"), Contains("_ZN1A1fEci")); 389 EXPECT_THAT(GenerateAlternate("_ZN1AC1Ev"), Contains("_ZN1AC2Ev")); 390 EXPECT_THAT(GenerateAlternate("_ZN1AD1Ev"), Contains("_ZN1AD2Ev")); 391 EXPECT_THAT(GenerateAlternate("_bogus"), IsEmpty()); 392 } 393 394 TEST(CPlusPlusLanguage, CPlusPlusNameParser) { 395 // Don't crash. 396 CPlusPlusNameParser((const char *)nullptr); 397 } 398