1 //===-- CPlusPlusLanguageTest.cpp -----------------------------------------===//
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
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"
16 using namespace lldb_private
;
18 TEST(CPlusPlusLanguage
, MethodNameParsing
) {
21 std::string return_type
, context
, basename
, arguments
, qualifiers
,
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"},
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)", "",
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])",
64 "std::__1::ranges::__begin::__fn::operator()[abi:v160000]<char const, "
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"},
79 {"main::{lambda()#1}::operator()() const::{lambda()#1}::operator()() "
81 "", "main::{lambda()#1}::operator()() const::{lambda()#1}", "operator()",
83 "main::{lambda()#1}::operator()() const::{lambda()#1}::operator()"},
86 {"string (*f(vector<int>&&))(float)", "", "", "f", "(vector<int>&&)", "",
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"},
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)>"},
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 "
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]>", "()", "",
135 {"void foo<Bar[]>()", "void", "", "foo<Bar[]>", "()", "", "foo<Bar[]>"},
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>"},
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 &&",
150 "v1::v2::Dummy[abi:c1][abi:c2]<v1::v2::Dummy[abi:c1][abi:c2]<int>>",
152 "v1::v2::Dummy[abi:c1][abi:c2]<v1::v2::Dummy[abi:c1][abi:c2]<int>>",
154 "method2<v1::v2::Dummy[abi:c1][abi:c2]<v1::v2::Dummy[abi:c1][abi:c2]<"
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]<"
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 "
169 "v1::v2::Dummy[abi:c1][abi:c2]<v1::v2::Dummy[abi:c1][abi:c2]<int>>",
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>>>",
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>>>"},
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>>"
190 "with_tag_in_ns[abi:special tag,0.0][abi:special tag,1.0]<Dummy<int>>",
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>>"},
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]"},
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]"},
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>>"},
210 {"auto A::operator<=>[abi:tag]<A::B>()", "auto", "A",
211 "operator<=>[abi:tag]<A::B>", "()", "",
212 "A::operator<=>[abi:tag]<A::B>"}};
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());
228 TEST(CPlusPlusLanguage
, InvalidMethodNameParsing
) {
229 // Tests that we correctly reject malformed function names
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)",
239 "auto A::foo[](int)",
240 "auto A::foo[bar](int)",
241 "auto A::foo[abi](int)",
242 "auto A::foo[abi:(int)",
245 for (const auto &name
: test_cases
) {
246 CPlusPlusLanguage::MethodName method
{ConstString(name
)};
247 EXPECT_FALSE(method
.IsValid()) << name
;
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>>>()"));
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"));
274 EXPECT_TRUE(reference_2
.ContainsPath(""));
275 EXPECT_TRUE(reference_2
.ContainsPath("foofoo::bar::func01"));
276 EXPECT_FALSE(reference_2
.ContainsPath("foo::bar::func01"));
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"));
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"));
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>>"));
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>>"));
305 TEST(CPlusPlusLanguage
, ExtractContextAndIdentifier
) {
308 std::string context
, basename
;
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>",
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>"},
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());
347 EXPECT_FALSE(CPlusPlusLanguage::ExtractContextAndIdentifier("void", context
,
350 CPlusPlusLanguage::ExtractContextAndIdentifier("321", context
, basename
));
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
));
362 EXPECT_TRUE(CPlusPlusLanguage::ExtractContextAndIdentifier(
363 "A::operator<=><A::B>", context
, basename
));
364 EXPECT_TRUE(CPlusPlusLanguage::ExtractContextAndIdentifier(
365 "operator<=><A::B>", context
, basename
));
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()));
380 TEST(CPlusPlusLanguage
, GenerateAlternateFunctionManglings
) {
381 using namespace testing
;
383 SubsystemRAII
<CPlusPlusLanguage
> lang
;
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());
396 TEST(CPlusPlusLanguage
, CPlusPlusNameParser
) {
398 CPlusPlusNameParser((const char *)nullptr);