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