xref: /llvm-project/lldb/unittests/Language/CPlusPlus/CPlusPlusLanguageTest.cpp (revision d4a55ad346514b2478762cbc198942c72347e81e)
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