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 | |
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 | {.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 | |
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 | |
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 | |
305 | TEST(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 | |
368 | static 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 | |
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 | |
396 | TEST(CPlusPlusLanguage, CPlusPlusNameParser) { |
397 | // Don't crash. |
398 | CPlusPlusNameParser((const char *)nullptr); |
399 | } |
400 | |