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