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
16using namespace lldb_private;
17
18TEST(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 {.input: "main(int, char *[]) ", .return_type: "", .context: "", .basename: "main", .arguments: "(int, char *[])", .qualifiers: "", .scope_qualified_name: "main"},
27 {.input: "foo::bar(baz) const", .return_type: "", .context: "foo", .basename: "bar", .arguments: "(baz)", .qualifiers: "const", .scope_qualified_name: "foo::bar"},
28 {.input: "foo::~bar(baz)", .return_type: "", .context: "foo", .basename: "~bar", .arguments: "(baz)", .qualifiers: "", .scope_qualified_name: "foo::~bar"},
29 {.input: "a::b::c::d(e,f)", .return_type: "", .context: "a::b::c", .basename: "d", .arguments: "(e,f)", .qualifiers: "", .scope_qualified_name: "a::b::c::d"},
30 {.input: "void f(int)", .return_type: "void", .context: "", .basename: "f", .arguments: "(int)", .qualifiers: "", .scope_qualified_name: "f"},
31
32 // Operators
33 {.input: "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 .return_type: "std::basic_ostream<char, std::char_traits<char> >&", .context: "std",
37 .basename: "operator<<<std::char_traits<char> >",
38 .arguments: "(std::basic_ostream<char, std::char_traits<char> >&, char const*)", .qualifiers: "",
39 .scope_qualified_name: "std::operator<<<std::char_traits<char> >"},
40 {.input: "operator delete[](void*, clang::ASTContext const&, unsigned long)", .return_type: "",
41 .context: "", .basename: "operator delete[]",
42 .arguments: "(void*, clang::ASTContext const&, unsigned long)", .qualifiers: "",
43 .scope_qualified_name: "operator delete[]"},
44 {.input: "std::optional<clang::PostInitializer>::operator bool() const", .return_type: "",
45 .context: "std::optional<clang::PostInitializer>", .basename: "operator bool", .arguments: "()", .qualifiers: "const",
46 .scope_qualified_name: "std::optional<clang::PostInitializer>::operator bool"},
47 {.input: "(anonymous namespace)::FactManager::operator[](unsigned short)", .return_type: "",
48 .context: "(anonymous namespace)::FactManager", .basename: "operator[]", .arguments: "(unsigned short)",
49 .qualifiers: "", .scope_qualified_name: "(anonymous namespace)::FactManager::operator[]"},
50 {.input: "const int& std::map<int, pair<short, int>>::operator[](short) const",
51 .return_type: "const int&", .context: "std::map<int, pair<short, int>>", .basename: "operator[]", .arguments: "(short)",
52 .qualifiers: "const", .scope_qualified_name: "std::map<int, pair<short, int>>::operator[]"},
53 {.input: "CompareInsn::operator()(llvm::StringRef, InsnMatchEntry const&)", .return_type: "",
54 .context: "CompareInsn", .basename: "operator()", .arguments: "(llvm::StringRef, InsnMatchEntry const&)",
55 .qualifiers: "", .scope_qualified_name: "CompareInsn::operator()"},
56 {.input: "std::optional<llvm::MCFixupKind>::operator*() const &", .return_type: "",
57 .context: "std::optional<llvm::MCFixupKind>", .basename: "operator*", .arguments: "()", .qualifiers: "const &",
58 .scope_qualified_name: "std::optional<llvm::MCFixupKind>::operator*"},
59 {.input: "auto std::__1::ranges::__begin::__fn::operator()[abi:v160000]<char "
60 "const, 18ul>(char const (&) [18ul]) const",
61 .return_type: "auto", .context: "std::__1::ranges::__begin::__fn",
62 .basename: "operator()[abi:v160000]<char const, 18ul>", .arguments: "(char const (&) [18ul])",
63 .qualifiers: "const",
64 .scope_qualified_name: "std::__1::ranges::__begin::__fn::operator()[abi:v160000]<char const, "
65 "18ul>"},
66 // Internal classes
67 {.input: "operator<<(Cls, Cls)::Subclass::function()", .return_type: "",
68 .context: "operator<<(Cls, Cls)::Subclass", .basename: "function", .arguments: "()", .qualifiers: "",
69 .scope_qualified_name: "operator<<(Cls, Cls)::Subclass::function"},
70 {.input: "SAEC::checkFunction(context&) const::CallBack::CallBack(int)", .return_type: "",
71 .context: "SAEC::checkFunction(context&) const::CallBack", .basename: "CallBack", .arguments: "(int)", .qualifiers: "",
72 .scope_qualified_name: "SAEC::checkFunction(context&) const::CallBack::CallBack"},
73 // Anonymous namespace
74 {.input: "XX::(anonymous namespace)::anon_class::anon_func() const", .return_type: "",
75 .context: "XX::(anonymous namespace)::anon_class", .basename: "anon_func", .arguments: "()", .qualifiers: "const",
76 .scope_qualified_name: "XX::(anonymous namespace)::anon_class::anon_func"},
77
78 // Lambda
79 {.input: "main::{lambda()#1}::operator()() const::{lambda()#1}::operator()() "
80 "const",
81 .return_type: "", .context: "main::{lambda()#1}::operator()() const::{lambda()#1}", .basename: "operator()",
82 .arguments: "()", .qualifiers: "const",
83 .scope_qualified_name: "main::{lambda()#1}::operator()() const::{lambda()#1}::operator()"},
84
85 // Function pointers
86 {.input: "string (*f(vector<int>&&))(float)", .return_type: "", .context: "", .basename: "f", .arguments: "(vector<int>&&)", .qualifiers: "",
87 .scope_qualified_name: "f"},
88 {.input: "void (*&std::_Any_data::_M_access<void (*)()>())()", .return_type: "",
89 .context: "std::_Any_data", .basename: "_M_access<void (*)()>", .arguments: "()", .qualifiers: "",
90 .scope_qualified_name: "std::_Any_data::_M_access<void (*)()>"},
91 {.input: "void (*(*(*(*(*(*(*(* const&func1(int))())())())())())())())()", .return_type: "", .context: "",
92 .basename: "func1", .arguments: "(int)", .qualifiers: "", .scope_qualified_name: "func1"},
93
94 // Decltype
95 {.input: "decltype(nullptr)&& std::forward<decltype(nullptr)>"
96 "(std::remove_reference<decltype(nullptr)>::type&)",
97 .return_type: "decltype(nullptr)&&", .context: "std", .basename: "forward<decltype(nullptr)>",
98 .arguments: "(std::remove_reference<decltype(nullptr)>::type&)", .qualifiers: "",
99 .scope_qualified_name: "std::forward<decltype(nullptr)>"},
100
101 // Templates
102 {.input: "void llvm::PM<llvm::Module, llvm::AM<llvm::Module>>::"
103 "addPass<llvm::VP>(llvm::VP)",
104 .return_type: "void", .context: "llvm::PM<llvm::Module, llvm::AM<llvm::Module>>",
105 .basename: "addPass<llvm::VP>", .arguments: "(llvm::VP)", .qualifiers: "",
106 .scope_qualified_name: "llvm::PM<llvm::Module, llvm::AM<llvm::Module>>::"
107 "addPass<llvm::VP>"},
108 {.input: "void std::vector<Class, std::allocator<Class> >"
109 "::_M_emplace_back_aux<Class const&>(Class const&)",
110 .return_type: "void", .context: "std::vector<Class, std::allocator<Class> >",
111 .basename: "_M_emplace_back_aux<Class const&>", .arguments: "(Class const&)", .qualifiers: "",
112 .scope_qualified_name: "std::vector<Class, std::allocator<Class> >::"
113 "_M_emplace_back_aux<Class const&>"},
114 {.input: "unsigned long llvm::countTrailingOnes<unsigned int>"
115 "(unsigned int, llvm::ZeroBehavior)",
116 .return_type: "unsigned long", .context: "llvm", .basename: "countTrailingOnes<unsigned int>",
117 .arguments: "(unsigned int, llvm::ZeroBehavior)", .qualifiers: "",
118 .scope_qualified_name: "llvm::countTrailingOnes<unsigned int>"},
119 {.input: "std::enable_if<(10u)<(64), bool>::type llvm::isUInt<10u>(unsigned "
120 "long)",
121 .return_type: "std::enable_if<(10u)<(64), bool>::type", .context: "llvm", .basename: "isUInt<10u>",
122 .arguments: "(unsigned long)", .qualifiers: "", .scope_qualified_name: "llvm::isUInt<10u>"},
123 {.input: "f<A<operator<(X,Y)::Subclass>, sizeof(B)<sizeof(C)>()", .return_type: "", .context: "",
124 .basename: "f<A<operator<(X,Y)::Subclass>, sizeof(B)<sizeof(C)>", .arguments: "()", .qualifiers: "",
125 .scope_qualified_name: "f<A<operator<(X,Y)::Subclass>, sizeof(B)<sizeof(C)>"},
126 {.input: "std::optional<llvm::MCFixupKind>::operator*() const volatile &&", .return_type: "",
127 .context: "std::optional<llvm::MCFixupKind>", .basename: "operator*", .arguments: "()",
128 .qualifiers: "const volatile &&", .scope_qualified_name: "std::optional<llvm::MCFixupKind>::operator*"},
129 {.input: "void foo<Dummy<char [10]>>()", .return_type: "void", .context: "", .basename: "foo<Dummy<char [10]>>",
130 .arguments: "()", .qualifiers: "", .scope_qualified_name: "foo<Dummy<char [10]>>"},
131 {.input: "void foo<Bar<Bar<int>[10]>>()", .return_type: "void", .context: "", .basename: "foo<Bar<Bar<int>[10]>>",
132 .arguments: "()", .qualifiers: "", .scope_qualified_name: "foo<Bar<Bar<int>[10]>>"},
133 {.input: "void foo<Bar[10]>()", .return_type: "void", .context: "", .basename: "foo<Bar[10]>", .arguments: "()", .qualifiers: "",
134 .scope_qualified_name: "foo<Bar[10]>"},
135 {.input: "void foo<Bar[]>()", .return_type: "void", .context: "", .basename: "foo<Bar[]>", .arguments: "()", .qualifiers: "", .scope_qualified_name: "foo<Bar[]>"},
136
137 // auto return type
138 {.input: "auto std::test_return_auto<int>() const", .return_type: "auto", .context: "std",
139 .basename: "test_return_auto<int>", .arguments: "()", .qualifiers: "const", .scope_qualified_name: "std::test_return_auto<int>"},
140 {.input: "decltype(auto) std::test_return_auto<int>(int) const", .return_type: "decltype(auto)",
141 .context: "std", .basename: "test_return_auto<int>", .arguments: "(int)", .qualifiers: "const",
142 .scope_qualified_name: "std::test_return_auto<int>"},
143
144 // abi_tag on class method
145 {.input: "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 .return_type: "v1::v2::Dummy[abi:c1][abi:c2]<v1::v2::Dummy[abi:c1][abi:c2]<int>>",
151 // Context
152 .context: "v1::v2::Dummy[abi:c1][abi:c2]<v1::v2::Dummy[abi:c1][abi:c2]<int>>",
153 // Basename
154 .basename: "method2<v1::v2::Dummy[abi:c1][abi:c2]<v1::v2::Dummy[abi:c1][abi:c2]<"
155 "int>>>",
156 // Args, qualifiers
157 .arguments: "(int, v1::v2::Dummy<int>)", .qualifiers: "const &&",
158 // Full scope-qualified name without args
159 .scope_qualified_name: "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 {.input: "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 .return_type: "v1::v2::Dummy[abi:c1][abi:c2]<v1::v2::Dummy[abi:c1][abi:c2]<int>>",
170 // Context
171 .context: "v1::v2",
172 // Basename
173 .basename: "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 .arguments: "(int, v1::v2::Dummy<int>)", .qualifiers: "const &&",
177 // Full scope-qualified name without args
178 .scope_qualified_name: "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 {.input: "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 .return_type: "auto",
187 // Context
188 .context: "ns",
189 // Basename
190 .basename: "with_tag_in_ns[abi:special tag,0.0][abi:special tag,1.0]<Dummy<int>>",
191 // Args, qualifiers
192 .arguments: "(float)", .qualifiers: "const &&",
193 // Full scope-qualified name without args
194 .scope_qualified_name: "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 {.input: "std::__1::error_code::operator bool[abi:v160000]() const", .return_type: "",
199 .context: "std::__1::error_code", .basename: "operator bool[abi:v160000]", .arguments: "()", .qualifiers: "const",
200 .scope_qualified_name: "std::__1::error_code::operator bool[abi:v160000]"},
201
202 {.input: "auto ns::foo::operator[][abi:v160000](size_t) const", .return_type: "auto", .context: "ns::foo",
203 .basename: "operator[][abi:v160000]", .arguments: "(size_t)", .qualifiers: "const",
204 .scope_qualified_name: "ns::foo::operator[][abi:v160000]"},
205
206 {.input: "auto Foo[abi:abc]<int>::operator<<<Foo[abi:abc]<int>>(int) &", .return_type: "auto",
207 .context: "Foo[abi:abc]<int>", .basename: "operator<<<Foo[abi:abc]<int>>", .arguments: "(int)", .qualifiers: "&",
208 .scope_qualified_name: "Foo[abi:abc]<int>::operator<<<Foo[abi:abc]<int>>"},
209
210 {.input: "auto A::operator<=>[abi:tag]<A::B>()", .return_type: "auto", .context: "A",
211 .basename: "operator<=>[abi:tag]<A::B>", .arguments: "()", .qualifiers: "",
212 .scope_qualified_name: "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
228TEST(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
251TEST(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
305TEST(CPlusPlusLanguage, ExtractContextAndIdentifier) {
306 struct TestCase {
307 std::string input;
308 std::string context, basename;
309 };
310
311 TestCase test_cases[] = {
312 {.input: "main", .context: "", .basename: "main"},
313 {.input: "main ", .context: "", .basename: "main"},
314 {.input: "foo01::bar", .context: "foo01", .basename: "bar"},
315 {.input: "foo::~bar", .context: "foo", .basename: "~bar"},
316 {.input: "std::vector<int>::push_back", .context: "std::vector<int>", .basename: "push_back"},
317 {.input: "operator<<(Cls, Cls)::Subclass::function",
318 .context: "operator<<(Cls, Cls)::Subclass", .basename: "function"},
319 {.input: "std::vector<Class, std::allocator<Class>>"
320 "::_M_emplace_back_aux<Class const&>",
321 .context: "std::vector<Class, std::allocator<Class>>",
322 .basename: "_M_emplace_back_aux<Class const&>"},
323 {.input: "`anonymous namespace'::foo", .context: "`anonymous namespace'", .basename: "foo"},
324 {.input: "`operator<<A>'::`2'::B<0>::operator>", .context: "`operator<<A>'::`2'::B<0>",
325 .basename: "operator>"},
326 {.input: "`anonymous namespace'::S::<<::__l2::Foo",
327 .context: "`anonymous namespace'::S::<<::__l2", .basename: "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 {.input: "A::operator><A::B>", .context: "A", .basename: "operator><A::B>"},
332 {.input: "operator><A::B>", .context: "", .basename: "operator><A::B>"},
333 {.input: "A::operator<<A::B>", .context: "A", .basename: "operator<<A::B>"},
334 {.input: "operator<<A::B>", .context: "", .basename: "operator<<A::B>"},
335 {.input: "A::operator<<<A::B>", .context: "A", .basename: "operator<<<A::B>"},
336 {.input: "operator<<<A::B>", .context: "", .basename: "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
368static std::vector<std::string> GenerateAlternate(llvm::StringRef Name) {
369 std::vector<std::string> Strings;
370 if (Language *CPlusPlusLang =
371 Language::FindPlugin(language: lldb::eLanguageTypeC_plus_plus)) {
372 std::vector<ConstString> Results =
373 CPlusPlusLang->GenerateAlternateFunctionManglings(mangled: ConstString(Name));
374 for (ConstString Str : Results)
375 Strings.push_back(x: std::string(Str.GetStringRef()));
376 }
377 return Strings;
378}
379
380TEST(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
396TEST(CPlusPlusLanguage, CPlusPlusNameParser) {
397 // Don't crash.
398 CPlusPlusNameParser((const char *)nullptr);
399}
400

source code of lldb/unittests/Language/CPlusPlus/CPlusPlusLanguageTest.cpp