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
TEST(CPlusPlusLanguage,MethodNameParsing)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()() const",
69 "main::{lambda()#1}::operator()() const::{lambda()#1}", "operator()", "()", "const",
70 "main::{lambda()#1}::operator()() const::{lambda()#1}::operator()"},
71
72 // Function pointers
73 {"string (*f(vector<int>&&))(float)", "", "f", "(vector<int>&&)", "",
74 "f"},
75 {"void (*&std::_Any_data::_M_access<void (*)()>())()", "std::_Any_data",
76 "_M_access<void (*)()>", "()", "",
77 "std::_Any_data::_M_access<void (*)()>"},
78 {"void (*(*(*(*(*(*(*(* const&func1(int))())())())())())())())()", "",
79 "func1", "(int)", "", "func1"},
80
81 // Decltype
82 {"decltype(nullptr)&& std::forward<decltype(nullptr)>"
83 "(std::remove_reference<decltype(nullptr)>::type&)",
84 "std", "forward<decltype(nullptr)>",
85 "(std::remove_reference<decltype(nullptr)>::type&)", "",
86 "std::forward<decltype(nullptr)>"},
87
88 // Templates
89 {"void llvm::PM<llvm::Module, llvm::AM<llvm::Module>>::"
90 "addPass<llvm::VP>(llvm::VP)",
91 "llvm::PM<llvm::Module, llvm::AM<llvm::Module>>", "addPass<llvm::VP>",
92 "(llvm::VP)", "",
93 "llvm::PM<llvm::Module, llvm::AM<llvm::Module>>::"
94 "addPass<llvm::VP>"},
95 {"void std::vector<Class, std::allocator<Class> >"
96 "::_M_emplace_back_aux<Class const&>(Class const&)",
97 "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&>"},
101 {"unsigned long llvm::countTrailingOnes<unsigned int>"
102 "(unsigned int, llvm::ZeroBehavior)",
103 "llvm", "countTrailingOnes<unsigned int>",
104 "(unsigned int, llvm::ZeroBehavior)", "",
105 "llvm::countTrailingOnes<unsigned int>"},
106 {"std::enable_if<(10u)<(64), bool>::type llvm::isUInt<10u>(unsigned "
107 "long)",
108 "llvm", "isUInt<10u>", "(unsigned long)", "", "llvm::isUInt<10u>"},
109 {"f<A<operator<(X,Y)::Subclass>, sizeof(B)<sizeof(C)>()", "",
110 "f<A<operator<(X,Y)::Subclass>, sizeof(B)<sizeof(C)>", "()", "",
111 "f<A<operator<(X,Y)::Subclass>, sizeof(B)<sizeof(C)>"}};
112
113 for (const auto &test : test_cases) {
114 CPlusPlusLanguage::MethodName method(ConstString(test.input));
115 EXPECT_TRUE(method.IsValid()) << test.input;
116 if (method.IsValid()) {
117 EXPECT_EQ(test.context, method.GetContext().str());
118 EXPECT_EQ(test.basename, method.GetBasename().str());
119 EXPECT_EQ(test.arguments, method.GetArguments().str());
120 EXPECT_EQ(test.qualifiers, method.GetQualifiers().str());
121 EXPECT_EQ(test.scope_qualified_name, method.GetScopeQualifiedName());
122 }
123 }
124 }
125
TEST(CPlusPlusLanguage,ContainsPath)126 TEST(CPlusPlusLanguage, ContainsPath) {
127 CPlusPlusLanguage::MethodName
128 reference_1(ConstString("int foo::bar::func01(int a, double b)"));
129 CPlusPlusLanguage::MethodName
130 reference_2(ConstString("int foofoo::bar::func01(std::string a, int b)"));
131 CPlusPlusLanguage::MethodName reference_3(ConstString("int func01()"));
132 CPlusPlusLanguage::MethodName
133 reference_4(ConstString("bar::baz::operator bool()"));
134
135 EXPECT_TRUE(reference_1.ContainsPath("func01"));
136 EXPECT_TRUE(reference_1.ContainsPath("bar::func01"));
137 EXPECT_TRUE(reference_1.ContainsPath("foo::bar::func01"));
138 EXPECT_FALSE(reference_1.ContainsPath("func"));
139 EXPECT_FALSE(reference_1.ContainsPath("baz::func01"));
140 EXPECT_FALSE(reference_1.ContainsPath("::bar::func01"));
141 EXPECT_FALSE(reference_1.ContainsPath("::foo::baz::func01"));
142 EXPECT_FALSE(reference_1.ContainsPath("foo::bar::baz::func01"));
143
144 EXPECT_TRUE(reference_2.ContainsPath("foofoo::bar::func01"));
145 EXPECT_FALSE(reference_2.ContainsPath("foo::bar::func01"));
146
147 EXPECT_TRUE(reference_3.ContainsPath("func01"));
148 EXPECT_FALSE(reference_3.ContainsPath("func"));
149 EXPECT_FALSE(reference_3.ContainsPath("bar::func01"));
150
151 EXPECT_TRUE(reference_4.ContainsPath("operator bool"));
152 EXPECT_TRUE(reference_4.ContainsPath("baz::operator bool"));
153 EXPECT_TRUE(reference_4.ContainsPath("bar::baz::operator bool"));
154 EXPECT_FALSE(reference_4.ContainsPath("az::operator bool"));
155 }
156
TEST(CPlusPlusLanguage,ExtractContextAndIdentifier)157 TEST(CPlusPlusLanguage, ExtractContextAndIdentifier) {
158 struct TestCase {
159 std::string input;
160 std::string context, basename;
161 };
162
163 TestCase test_cases[] = {
164 {"main", "", "main"},
165 {"main ", "", "main"},
166 {"foo01::bar", "foo01", "bar"},
167 {"foo::~bar", "foo", "~bar"},
168 {"std::vector<int>::push_back", "std::vector<int>", "push_back"},
169 {"operator<<(Cls, Cls)::Subclass::function",
170 "operator<<(Cls, Cls)::Subclass", "function"},
171 {"std::vector<Class, std::allocator<Class>>"
172 "::_M_emplace_back_aux<Class const&>",
173 "std::vector<Class, std::allocator<Class>>",
174 "_M_emplace_back_aux<Class const&>"},
175 {"`anonymous namespace'::foo", "`anonymous namespace'", "foo"},
176 {"`operator<<A>'::`2'::B<0>::operator>", "`operator<<A>'::`2'::B<0>",
177 "operator>"},
178 {"`anonymous namespace'::S::<<::__l2::Foo",
179 "`anonymous namespace'::S::<<::__l2", "Foo"},
180 // These cases are idiosyncratic in how clang generates debug info for
181 // names when we have template parameters. They are not valid C++ names
182 // but if we fix this we need to support them for older compilers.
183 {"A::operator><A::B>", "A", "operator><A::B>"},
184 {"operator><A::B>", "", "operator><A::B>"},
185 {"A::operator<<A::B>", "A", "operator<<A::B>"},
186 {"operator<<A::B>", "", "operator<<A::B>"},
187 {"A::operator<<<A::B>", "A", "operator<<<A::B>"},
188 {"operator<<<A::B>", "", "operator<<<A::B>"},
189 };
190
191 llvm::StringRef context, basename;
192 for (const auto &test : test_cases) {
193 EXPECT_TRUE(CPlusPlusLanguage::ExtractContextAndIdentifier(
194 test.input.c_str(), context, basename));
195 EXPECT_EQ(test.context, context.str());
196 EXPECT_EQ(test.basename, basename.str());
197 }
198
199 EXPECT_FALSE(CPlusPlusLanguage::ExtractContextAndIdentifier("void", context,
200 basename));
201 EXPECT_FALSE(
202 CPlusPlusLanguage::ExtractContextAndIdentifier("321", context, basename));
203 EXPECT_FALSE(
204 CPlusPlusLanguage::ExtractContextAndIdentifier("", context, basename));
205 EXPECT_FALSE(CPlusPlusLanguage::ExtractContextAndIdentifier(
206 "selector:", context, basename));
207 EXPECT_FALSE(CPlusPlusLanguage::ExtractContextAndIdentifier(
208 "selector:otherField:", context, basename));
209 EXPECT_FALSE(CPlusPlusLanguage::ExtractContextAndIdentifier(
210 "abc::", context, basename));
211 EXPECT_FALSE(CPlusPlusLanguage::ExtractContextAndIdentifier(
212 "f<A<B><C>>", context, basename));
213
214 // We expect these cases to fail until we turn on C++2a
215 EXPECT_FALSE(CPlusPlusLanguage::ExtractContextAndIdentifier(
216 "A::operator<=><A::B>", context, basename));
217 EXPECT_FALSE(CPlusPlusLanguage::ExtractContextAndIdentifier(
218 "operator<=><A::B>", context, basename));
219 }
220
GenerateAlternate(llvm::StringRef Name)221 static std::vector<std::string> GenerateAlternate(llvm::StringRef Name) {
222 std::vector<std::string> Strings;
223 if (Language *CPlusPlusLang =
224 Language::FindPlugin(lldb::eLanguageTypeC_plus_plus)) {
225 std::vector<ConstString> Results =
226 CPlusPlusLang->GenerateAlternateFunctionManglings(ConstString(Name));
227 for (ConstString Str : Results)
228 Strings.push_back(std::string(Str.GetStringRef()));
229 }
230 return Strings;
231 }
232
TEST(CPlusPlusLanguage,GenerateAlternateFunctionManglings)233 TEST(CPlusPlusLanguage, GenerateAlternateFunctionManglings) {
234 using namespace testing;
235
236 SubsystemRAII<CPlusPlusLanguage> lang;
237
238 EXPECT_THAT(GenerateAlternate("_ZN1A1fEv"),
239 UnorderedElementsAre("_ZNK1A1fEv", "_ZLN1A1fEv"));
240 EXPECT_THAT(GenerateAlternate("_ZN1A1fEa"), Contains("_ZN1A1fEc"));
241 EXPECT_THAT(GenerateAlternate("_ZN1A1fEx"), Contains("_ZN1A1fEl"));
242 EXPECT_THAT(GenerateAlternate("_ZN1A1fEy"), Contains("_ZN1A1fEm"));
243 EXPECT_THAT(GenerateAlternate("_ZN1A1fEai"), Contains("_ZN1A1fEci"));
244 EXPECT_THAT(GenerateAlternate("_ZN1AC1Ev"), Contains("_ZN1AC2Ev"));
245 EXPECT_THAT(GenerateAlternate("_ZN1AD1Ev"), Contains("_ZN1AD2Ev"));
246 EXPECT_THAT(GenerateAlternate("_bogus"), IsEmpty());
247 }
248
TEST(CPlusPlusLanguage,CPlusPlusNameParser)249 TEST(CPlusPlusLanguage, CPlusPlusNameParser) {
250 // Don't crash.
251 CPlusPlusNameParser((const char *)nullptr);
252 }
253