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 // Internal classes 56 {"operator<<(Cls, Cls)::Subclass::function()", 57 "operator<<(Cls, Cls)::Subclass", "function", "()", "", 58 "operator<<(Cls, Cls)::Subclass::function"}, 59 {"SAEC::checkFunction(context&) const::CallBack::CallBack(int)", 60 "SAEC::checkFunction(context&) const::CallBack", "CallBack", "(int)", "", 61 "SAEC::checkFunction(context&) const::CallBack::CallBack"}, 62 // Anonymous namespace 63 {"XX::(anonymous namespace)::anon_class::anon_func() const", 64 "XX::(anonymous namespace)::anon_class", "anon_func", "()", "const", 65 "XX::(anonymous namespace)::anon_class::anon_func"}, 66 67 // Lambda 68 {"main::{lambda()#1}::operator()() const::{lambda()#1}::operator()() " 69 "const", 70 "main::{lambda()#1}::operator()() const::{lambda()#1}", "operator()", 71 "()", "const", 72 "main::{lambda()#1}::operator()() const::{lambda()#1}::operator()"}, 73 74 // Function pointers 75 {"string (*f(vector<int>&&))(float)", "", "f", "(vector<int>&&)", "", 76 "f"}, 77 {"void (*&std::_Any_data::_M_access<void (*)()>())()", "std::_Any_data", 78 "_M_access<void (*)()>", "()", "", 79 "std::_Any_data::_M_access<void (*)()>"}, 80 {"void (*(*(*(*(*(*(*(* const&func1(int))())())())())())())())()", "", 81 "func1", "(int)", "", "func1"}, 82 83 // Decltype 84 {"decltype(nullptr)&& std::forward<decltype(nullptr)>" 85 "(std::remove_reference<decltype(nullptr)>::type&)", 86 "std", "forward<decltype(nullptr)>", 87 "(std::remove_reference<decltype(nullptr)>::type&)", "", 88 "std::forward<decltype(nullptr)>"}, 89 90 // Templates 91 {"void llvm::PM<llvm::Module, llvm::AM<llvm::Module>>::" 92 "addPass<llvm::VP>(llvm::VP)", 93 "llvm::PM<llvm::Module, llvm::AM<llvm::Module>>", "addPass<llvm::VP>", 94 "(llvm::VP)", "", 95 "llvm::PM<llvm::Module, llvm::AM<llvm::Module>>::" 96 "addPass<llvm::VP>"}, 97 {"void std::vector<Class, std::allocator<Class> >" 98 "::_M_emplace_back_aux<Class const&>(Class const&)", 99 "std::vector<Class, std::allocator<Class> >", 100 "_M_emplace_back_aux<Class const&>", "(Class const&)", "", 101 "std::vector<Class, std::allocator<Class> >::" 102 "_M_emplace_back_aux<Class const&>"}, 103 {"unsigned long llvm::countTrailingOnes<unsigned int>" 104 "(unsigned int, llvm::ZeroBehavior)", 105 "llvm", "countTrailingOnes<unsigned int>", 106 "(unsigned int, llvm::ZeroBehavior)", "", 107 "llvm::countTrailingOnes<unsigned int>"}, 108 {"std::enable_if<(10u)<(64), bool>::type llvm::isUInt<10u>(unsigned " 109 "long)", 110 "llvm", "isUInt<10u>", "(unsigned long)", "", "llvm::isUInt<10u>"}, 111 {"f<A<operator<(X,Y)::Subclass>, sizeof(B)<sizeof(C)>()", "", 112 "f<A<operator<(X,Y)::Subclass>, sizeof(B)<sizeof(C)>", "()", "", 113 "f<A<operator<(X,Y)::Subclass>, sizeof(B)<sizeof(C)>"}, 114 {"llvm::Optional<llvm::MCFixupKind>::operator*() const volatile &&", 115 "llvm::Optional<llvm::MCFixupKind>", "operator*", "()", 116 "const volatile &&", "llvm::Optional<llvm::MCFixupKind>::operator*"}, 117 118 // auto return type 119 {"auto std::test_return_auto<int>() const", "std", 120 "test_return_auto<int>", "()", "const", "std::test_return_auto<int>"}, 121 {"decltype(auto) std::test_return_auto<int>(int) const", "std", 122 "test_return_auto<int>", "(int)", "const", 123 "std::test_return_auto<int>"}}; 124 125 for (const auto &test : test_cases) { 126 CPlusPlusLanguage::MethodName method(ConstString(test.input)); 127 EXPECT_TRUE(method.IsValid()) << test.input; 128 if (method.IsValid()) { 129 EXPECT_EQ(test.context, method.GetContext().str()); 130 EXPECT_EQ(test.basename, method.GetBasename().str()); 131 EXPECT_EQ(test.arguments, method.GetArguments().str()); 132 EXPECT_EQ(test.qualifiers, method.GetQualifiers().str()); 133 EXPECT_EQ(test.scope_qualified_name, method.GetScopeQualifiedName()); 134 } 135 } 136 } 137 138 TEST(CPlusPlusLanguage, ContainsPath) { 139 CPlusPlusLanguage::MethodName 140 reference_1(ConstString("int foo::bar::func01(int a, double b)")); 141 CPlusPlusLanguage::MethodName 142 reference_2(ConstString("int foofoo::bar::func01(std::string a, int b)")); 143 CPlusPlusLanguage::MethodName reference_3(ConstString("int func01()")); 144 CPlusPlusLanguage::MethodName 145 reference_4(ConstString("bar::baz::operator bool()")); 146 CPlusPlusLanguage::MethodName reference_5( 147 ConstString("bar::baz::operator bool<int, Type<double>>()")); 148 CPlusPlusLanguage::MethodName reference_6(ConstString( 149 "bar::baz::operator<<<Type<double>, Type<std::vector<double>>>()")); 150 151 EXPECT_TRUE(reference_1.ContainsPath("")); 152 EXPECT_TRUE(reference_1.ContainsPath("func01")); 153 EXPECT_TRUE(reference_1.ContainsPath("bar::func01")); 154 EXPECT_TRUE(reference_1.ContainsPath("foo::bar::func01")); 155 EXPECT_FALSE(reference_1.ContainsPath("func")); 156 EXPECT_FALSE(reference_1.ContainsPath("baz::func01")); 157 EXPECT_FALSE(reference_1.ContainsPath("::bar::func01")); 158 EXPECT_FALSE(reference_1.ContainsPath("::foo::baz::func01")); 159 EXPECT_FALSE(reference_1.ContainsPath("foo::bar::baz::func01")); 160 161 EXPECT_TRUE(reference_2.ContainsPath("")); 162 EXPECT_TRUE(reference_2.ContainsPath("foofoo::bar::func01")); 163 EXPECT_FALSE(reference_2.ContainsPath("foo::bar::func01")); 164 165 EXPECT_TRUE(reference_3.ContainsPath("")); 166 EXPECT_TRUE(reference_3.ContainsPath("func01")); 167 EXPECT_FALSE(reference_3.ContainsPath("func")); 168 EXPECT_FALSE(reference_3.ContainsPath("bar::func01")); 169 170 EXPECT_TRUE(reference_4.ContainsPath("")); 171 EXPECT_TRUE(reference_4.ContainsPath("operator")); 172 EXPECT_TRUE(reference_4.ContainsPath("operator bool")); 173 EXPECT_TRUE(reference_4.ContainsPath("baz::operator bool")); 174 EXPECT_TRUE(reference_4.ContainsPath("bar::baz::operator bool")); 175 EXPECT_FALSE(reference_4.ContainsPath("az::operator bool")); 176 177 EXPECT_TRUE(reference_5.ContainsPath("")); 178 EXPECT_TRUE(reference_5.ContainsPath("operator")); 179 EXPECT_TRUE(reference_5.ContainsPath("operator bool")); 180 EXPECT_TRUE(reference_5.ContainsPath("operator bool<int, Type<double>>")); 181 EXPECT_FALSE(reference_5.ContainsPath("operator bool<int, double>")); 182 EXPECT_FALSE(reference_5.ContainsPath("operator bool<int, Type<int>>")); 183 184 EXPECT_TRUE(reference_6.ContainsPath("")); 185 EXPECT_TRUE(reference_6.ContainsPath("operator")); 186 EXPECT_TRUE(reference_6.ContainsPath("operator<<")); 187 EXPECT_TRUE(reference_6.ContainsPath( 188 "bar::baz::operator<<<Type<double>, Type<std::vector<double>>>()")); 189 EXPECT_FALSE(reference_6.ContainsPath("operator<<<Type<double>>")); 190 } 191 192 TEST(CPlusPlusLanguage, ExtractContextAndIdentifier) { 193 struct TestCase { 194 std::string input; 195 std::string context, basename; 196 }; 197 198 TestCase test_cases[] = { 199 {"main", "", "main"}, 200 {"main ", "", "main"}, 201 {"foo01::bar", "foo01", "bar"}, 202 {"foo::~bar", "foo", "~bar"}, 203 {"std::vector<int>::push_back", "std::vector<int>", "push_back"}, 204 {"operator<<(Cls, Cls)::Subclass::function", 205 "operator<<(Cls, Cls)::Subclass", "function"}, 206 {"std::vector<Class, std::allocator<Class>>" 207 "::_M_emplace_back_aux<Class const&>", 208 "std::vector<Class, std::allocator<Class>>", 209 "_M_emplace_back_aux<Class const&>"}, 210 {"`anonymous namespace'::foo", "`anonymous namespace'", "foo"}, 211 {"`operator<<A>'::`2'::B<0>::operator>", "`operator<<A>'::`2'::B<0>", 212 "operator>"}, 213 {"`anonymous namespace'::S::<<::__l2::Foo", 214 "`anonymous namespace'::S::<<::__l2", "Foo"}, 215 // These cases are idiosyncratic in how clang generates debug info for 216 // names when we have template parameters. They are not valid C++ names 217 // but if we fix this we need to support them for older compilers. 218 {"A::operator><A::B>", "A", "operator><A::B>"}, 219 {"operator><A::B>", "", "operator><A::B>"}, 220 {"A::operator<<A::B>", "A", "operator<<A::B>"}, 221 {"operator<<A::B>", "", "operator<<A::B>"}, 222 {"A::operator<<<A::B>", "A", "operator<<<A::B>"}, 223 {"operator<<<A::B>", "", "operator<<<A::B>"}, 224 }; 225 226 llvm::StringRef context, basename; 227 for (const auto &test : test_cases) { 228 EXPECT_TRUE(CPlusPlusLanguage::ExtractContextAndIdentifier( 229 test.input.c_str(), context, basename)); 230 EXPECT_EQ(test.context, context.str()); 231 EXPECT_EQ(test.basename, basename.str()); 232 } 233 234 EXPECT_FALSE(CPlusPlusLanguage::ExtractContextAndIdentifier("void", context, 235 basename)); 236 EXPECT_FALSE( 237 CPlusPlusLanguage::ExtractContextAndIdentifier("321", context, basename)); 238 EXPECT_FALSE( 239 CPlusPlusLanguage::ExtractContextAndIdentifier("", context, basename)); 240 EXPECT_FALSE(CPlusPlusLanguage::ExtractContextAndIdentifier( 241 "selector:", context, basename)); 242 EXPECT_FALSE(CPlusPlusLanguage::ExtractContextAndIdentifier( 243 "selector:otherField:", context, basename)); 244 EXPECT_FALSE(CPlusPlusLanguage::ExtractContextAndIdentifier( 245 "abc::", context, basename)); 246 EXPECT_FALSE(CPlusPlusLanguage::ExtractContextAndIdentifier( 247 "f<A<B><C>>", context, basename)); 248 249 // We expect these cases to fail until we turn on C++2a 250 EXPECT_FALSE(CPlusPlusLanguage::ExtractContextAndIdentifier( 251 "A::operator<=><A::B>", context, basename)); 252 EXPECT_FALSE(CPlusPlusLanguage::ExtractContextAndIdentifier( 253 "operator<=><A::B>", context, basename)); 254 } 255 256 static std::vector<std::string> GenerateAlternate(llvm::StringRef Name) { 257 std::vector<std::string> Strings; 258 if (Language *CPlusPlusLang = 259 Language::FindPlugin(lldb::eLanguageTypeC_plus_plus)) { 260 std::vector<ConstString> Results = 261 CPlusPlusLang->GenerateAlternateFunctionManglings(ConstString(Name)); 262 for (ConstString Str : Results) 263 Strings.push_back(std::string(Str.GetStringRef())); 264 } 265 return Strings; 266 } 267 268 TEST(CPlusPlusLanguage, GenerateAlternateFunctionManglings) { 269 using namespace testing; 270 271 SubsystemRAII<CPlusPlusLanguage> lang; 272 273 EXPECT_THAT(GenerateAlternate("_ZN1A1fEv"), 274 UnorderedElementsAre("_ZNK1A1fEv", "_ZLN1A1fEv")); 275 EXPECT_THAT(GenerateAlternate("_ZN1A1fEa"), Contains("_ZN1A1fEc")); 276 EXPECT_THAT(GenerateAlternate("_ZN1A1fEx"), Contains("_ZN1A1fEl")); 277 EXPECT_THAT(GenerateAlternate("_ZN1A1fEy"), Contains("_ZN1A1fEm")); 278 EXPECT_THAT(GenerateAlternate("_ZN1A1fEai"), Contains("_ZN1A1fEci")); 279 EXPECT_THAT(GenerateAlternate("_ZN1AC1Ev"), Contains("_ZN1AC2Ev")); 280 EXPECT_THAT(GenerateAlternate("_ZN1AD1Ev"), Contains("_ZN1AD2Ev")); 281 EXPECT_THAT(GenerateAlternate("_bogus"), IsEmpty()); 282 } 283 284 TEST(CPlusPlusLanguage, CPlusPlusNameParser) { 285 // Don't crash. 286 CPlusPlusNameParser((const char *)nullptr); 287 } 288