xref: /llvm-project/lldb/unittests/Language/CPlusPlus/CPlusPlusLanguageTest.cpp (revision 89cd0e8c267f57004a734c94ff15c6bd0facf646)
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 
TEST(CPlusPlusLanguage,MethodNameParsing)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       {"auto A::operator<=>[abi:tag]<A::B>()", "auto", "A",
211        "operator<=>[abi:tag]<A::B>", "()", "",
212        "A::operator<=>[abi:tag]<A::B>"}};
213 
214   for (const auto &test : test_cases) {
215     CPlusPlusLanguage::MethodName method(ConstString(test.input));
216     EXPECT_TRUE(method.IsValid()) << test.input;
217     if (method.IsValid()) {
218       EXPECT_EQ(test.return_type, method.GetReturnType().str());
219       EXPECT_EQ(test.context, method.GetContext().str());
220       EXPECT_EQ(test.basename, method.GetBasename().str());
221       EXPECT_EQ(test.arguments, method.GetArguments().str());
222       EXPECT_EQ(test.qualifiers, method.GetQualifiers().str());
223       EXPECT_EQ(test.scope_qualified_name, method.GetScopeQualifiedName());
224     }
225   }
226 }
227 
TEST(CPlusPlusLanguage,InvalidMethodNameParsing)228 TEST(CPlusPlusLanguage, InvalidMethodNameParsing) {
229   // Tests that we correctly reject malformed function names
230 
231   std::string test_cases[] = {
232       "int Foo::operator[]<[10>()",
233       "Foo::operator bool[10]()",
234       "auto A::operator<<<(int)",
235       "auto A::operator>>>(int)",
236       "auto A::operator<<<Type[abi:tag]<>(int)",
237       "auto A::operator<<<Type[abi:tag]<Type<int>>(int)",
238       "auto A::foo[(int)",
239       "auto A::foo[](int)",
240       "auto A::foo[bar](int)",
241       "auto A::foo[abi](int)",
242       "auto A::foo[abi:(int)",
243   };
244 
245   for (const auto &name : test_cases) {
246     CPlusPlusLanguage::MethodName method{ConstString(name)};
247     EXPECT_FALSE(method.IsValid()) << name;
248   }
249 }
250 
TEST(CPlusPlusLanguage,ContainsPath)251 TEST(CPlusPlusLanguage, ContainsPath) {
252   CPlusPlusLanguage::MethodName
253       reference_1(ConstString("int foo::bar::func01(int a, double b)"));
254   CPlusPlusLanguage::MethodName
255       reference_2(ConstString("int foofoo::bar::func01(std::string a, int b)"));
256   CPlusPlusLanguage::MethodName reference_3(ConstString("int func01()"));
257   CPlusPlusLanguage::MethodName
258       reference_4(ConstString("bar::baz::operator bool()"));
259   CPlusPlusLanguage::MethodName reference_5(
260       ConstString("bar::baz::operator bool<int, Type<double>>()"));
261   CPlusPlusLanguage::MethodName reference_6(ConstString(
262       "bar::baz::operator<<<Type<double>, Type<std::vector<double>>>()"));
263 
264   EXPECT_TRUE(reference_1.ContainsPath(""));
265   EXPECT_TRUE(reference_1.ContainsPath("func01"));
266   EXPECT_TRUE(reference_1.ContainsPath("bar::func01"));
267   EXPECT_TRUE(reference_1.ContainsPath("foo::bar::func01"));
268   EXPECT_FALSE(reference_1.ContainsPath("func"));
269   EXPECT_FALSE(reference_1.ContainsPath("baz::func01"));
270   EXPECT_FALSE(reference_1.ContainsPath("::bar::func01"));
271   EXPECT_FALSE(reference_1.ContainsPath("::foo::baz::func01"));
272   EXPECT_FALSE(reference_1.ContainsPath("foo::bar::baz::func01"));
273 
274   EXPECT_TRUE(reference_2.ContainsPath(""));
275   EXPECT_TRUE(reference_2.ContainsPath("foofoo::bar::func01"));
276   EXPECT_FALSE(reference_2.ContainsPath("foo::bar::func01"));
277 
278   EXPECT_TRUE(reference_3.ContainsPath(""));
279   EXPECT_TRUE(reference_3.ContainsPath("func01"));
280   EXPECT_FALSE(reference_3.ContainsPath("func"));
281   EXPECT_FALSE(reference_3.ContainsPath("bar::func01"));
282 
283   EXPECT_TRUE(reference_4.ContainsPath(""));
284   EXPECT_TRUE(reference_4.ContainsPath("operator"));
285   EXPECT_TRUE(reference_4.ContainsPath("operator bool"));
286   EXPECT_TRUE(reference_4.ContainsPath("baz::operator bool"));
287   EXPECT_TRUE(reference_4.ContainsPath("bar::baz::operator bool"));
288   EXPECT_FALSE(reference_4.ContainsPath("az::operator bool"));
289 
290   EXPECT_TRUE(reference_5.ContainsPath(""));
291   EXPECT_TRUE(reference_5.ContainsPath("operator"));
292   EXPECT_TRUE(reference_5.ContainsPath("operator bool"));
293   EXPECT_TRUE(reference_5.ContainsPath("operator bool<int, Type<double>>"));
294   EXPECT_FALSE(reference_5.ContainsPath("operator bool<int, double>"));
295   EXPECT_FALSE(reference_5.ContainsPath("operator bool<int, Type<int>>"));
296 
297   EXPECT_TRUE(reference_6.ContainsPath(""));
298   EXPECT_TRUE(reference_6.ContainsPath("operator"));
299   EXPECT_TRUE(reference_6.ContainsPath("operator<<"));
300   EXPECT_TRUE(reference_6.ContainsPath(
301       "bar::baz::operator<<<Type<double>, Type<std::vector<double>>>()"));
302   EXPECT_FALSE(reference_6.ContainsPath("operator<<<Type<double>>"));
303 }
304 
TEST(CPlusPlusLanguage,ExtractContextAndIdentifier)305 TEST(CPlusPlusLanguage, ExtractContextAndIdentifier) {
306   struct TestCase {
307     std::string input;
308     std::string context, basename;
309   };
310 
311   TestCase test_cases[] = {
312       {"main", "", "main"},
313       {"main     ", "", "main"},
314       {"foo01::bar", "foo01", "bar"},
315       {"foo::~bar", "foo", "~bar"},
316       {"std::vector<int>::push_back", "std::vector<int>", "push_back"},
317       {"operator<<(Cls, Cls)::Subclass::function",
318        "operator<<(Cls, Cls)::Subclass", "function"},
319       {"std::vector<Class, std::allocator<Class>>"
320        "::_M_emplace_back_aux<Class const&>",
321        "std::vector<Class, std::allocator<Class>>",
322        "_M_emplace_back_aux<Class const&>"},
323       {"`anonymous namespace'::foo", "`anonymous namespace'", "foo"},
324       {"`operator<<A>'::`2'::B<0>::operator>", "`operator<<A>'::`2'::B<0>",
325        "operator>"},
326       {"`anonymous namespace'::S::<<::__l2::Foo",
327        "`anonymous namespace'::S::<<::__l2", "Foo"},
328       // These cases are idiosyncratic in how clang generates debug info for
329       // names when we have template parameters. They are not valid C++ names
330       // but if we fix this we need to support them for older compilers.
331       {"A::operator><A::B>", "A", "operator><A::B>"},
332       {"operator><A::B>", "", "operator><A::B>"},
333       {"A::operator<<A::B>", "A", "operator<<A::B>"},
334       {"operator<<A::B>", "", "operator<<A::B>"},
335       {"A::operator<<<A::B>", "A", "operator<<<A::B>"},
336       {"operator<<<A::B>", "", "operator<<<A::B>"},
337   };
338 
339   llvm::StringRef context, basename;
340   for (const auto &test : test_cases) {
341     EXPECT_TRUE(CPlusPlusLanguage::ExtractContextAndIdentifier(
342         test.input.c_str(), context, basename));
343     EXPECT_EQ(test.context, context.str());
344     EXPECT_EQ(test.basename, basename.str());
345   }
346 
347   EXPECT_FALSE(CPlusPlusLanguage::ExtractContextAndIdentifier("void", context,
348                                                               basename));
349   EXPECT_FALSE(
350       CPlusPlusLanguage::ExtractContextAndIdentifier("321", context, basename));
351   EXPECT_FALSE(
352       CPlusPlusLanguage::ExtractContextAndIdentifier("", context, basename));
353   EXPECT_FALSE(CPlusPlusLanguage::ExtractContextAndIdentifier(
354       "selector:", context, basename));
355   EXPECT_FALSE(CPlusPlusLanguage::ExtractContextAndIdentifier(
356       "selector:otherField:", context, basename));
357   EXPECT_FALSE(CPlusPlusLanguage::ExtractContextAndIdentifier(
358       "abc::", context, basename));
359   EXPECT_FALSE(CPlusPlusLanguage::ExtractContextAndIdentifier(
360       "f<A<B><C>>", context, basename));
361 
362   EXPECT_TRUE(CPlusPlusLanguage::ExtractContextAndIdentifier(
363       "A::operator<=><A::B>", context, basename));
364   EXPECT_TRUE(CPlusPlusLanguage::ExtractContextAndIdentifier(
365       "operator<=><A::B>", context, basename));
366 }
367 
GenerateAlternate(llvm::StringRef Name)368 static std::vector<std::string> GenerateAlternate(llvm::StringRef Name) {
369   std::vector<std::string> Strings;
370   if (Language *CPlusPlusLang =
371           Language::FindPlugin(lldb::eLanguageTypeC_plus_plus)) {
372     std::vector<ConstString> Results =
373         CPlusPlusLang->GenerateAlternateFunctionManglings(ConstString(Name));
374     for (ConstString Str : Results)
375       Strings.push_back(std::string(Str.GetStringRef()));
376   }
377   return Strings;
378 }
379 
TEST(CPlusPlusLanguage,GenerateAlternateFunctionManglings)380 TEST(CPlusPlusLanguage, GenerateAlternateFunctionManglings) {
381   using namespace testing;
382 
383   SubsystemRAII<CPlusPlusLanguage> lang;
384 
385   EXPECT_THAT(GenerateAlternate("_ZN1A1fEv"),
386               UnorderedElementsAre("_ZNK1A1fEv", "_ZLN1A1fEv"));
387   EXPECT_THAT(GenerateAlternate("_ZN1A1fEa"), Contains("_ZN1A1fEc"));
388   EXPECT_THAT(GenerateAlternate("_ZN1A1fEx"), Contains("_ZN1A1fEl"));
389   EXPECT_THAT(GenerateAlternate("_ZN1A1fEy"), Contains("_ZN1A1fEm"));
390   EXPECT_THAT(GenerateAlternate("_ZN1A1fEai"), Contains("_ZN1A1fEci"));
391   EXPECT_THAT(GenerateAlternate("_ZN1AC1Ev"), Contains("_ZN1AC2Ev"));
392   EXPECT_THAT(GenerateAlternate("_ZN1AD1Ev"), Contains("_ZN1AD2Ev"));
393   EXPECT_THAT(GenerateAlternate("_bogus"), IsEmpty());
394 }
395 
TEST(CPlusPlusLanguage,CPlusPlusNameParser)396 TEST(CPlusPlusLanguage, CPlusPlusNameParser) {
397   // Don't crash.
398   CPlusPlusNameParser((const char *)nullptr);
399 }
400