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 "gmock/gmock.h"
11 #include "gtest/gtest.h"
12
13 using namespace lldb_private;
14
TEST(CPlusPlusLanguage,MethodNameParsing)15 TEST(CPlusPlusLanguage, MethodNameParsing) {
16 struct TestCase {
17 std::string input;
18 std::string context, basename, arguments, qualifiers, scope_qualified_name;
19 };
20
21 TestCase test_cases[] = {
22 {"main(int, char *[]) ", "", "main", "(int, char *[])", "", "main"},
23 {"foo::bar(baz) const", "foo", "bar", "(baz)", "const", "foo::bar"},
24 {"foo::~bar(baz)", "foo", "~bar", "(baz)", "", "foo::~bar"},
25 {"a::b::c::d(e,f)", "a::b::c", "d", "(e,f)", "", "a::b::c::d"},
26 {"void f(int)", "", "f", "(int)", "", "f"},
27
28 // Operators
29 {"std::basic_ostream<char, std::char_traits<char> >& "
30 "std::operator<<<std::char_traits<char> >"
31 "(std::basic_ostream<char, std::char_traits<char> >&, char const*)",
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 {"operator delete[](void*, clang::ASTContext const&, unsigned long)", "",
36 "operator delete[]", "(void*, clang::ASTContext const&, unsigned long)",
37 "", "operator delete[]"},
38 {"llvm::Optional<clang::PostInitializer>::operator bool() const",
39 "llvm::Optional<clang::PostInitializer>", "operator bool", "()", "const",
40 "llvm::Optional<clang::PostInitializer>::operator bool"},
41 {"(anonymous namespace)::FactManager::operator[](unsigned short)",
42 "(anonymous namespace)::FactManager", "operator[]", "(unsigned short)",
43 "", "(anonymous namespace)::FactManager::operator[]"},
44 {"const int& std::map<int, pair<short, int>>::operator[](short) const",
45 "std::map<int, pair<short, int>>", "operator[]", "(short)", "const",
46 "std::map<int, pair<short, int>>::operator[]"},
47 {"CompareInsn::operator()(llvm::StringRef, InsnMatchEntry const&)",
48 "CompareInsn", "operator()", "(llvm::StringRef, InsnMatchEntry const&)",
49 "", "CompareInsn::operator()"},
50 {"llvm::Optional<llvm::MCFixupKind>::operator*() const &",
51 "llvm::Optional<llvm::MCFixupKind>", "operator*", "()", "const &",
52 "llvm::Optional<llvm::MCFixupKind>::operator*"},
53 // Internal classes
54 {"operator<<(Cls, Cls)::Subclass::function()",
55 "operator<<(Cls, Cls)::Subclass", "function", "()", "",
56 "operator<<(Cls, Cls)::Subclass::function"},
57 {"SAEC::checkFunction(context&) const::CallBack::CallBack(int)",
58 "SAEC::checkFunction(context&) const::CallBack", "CallBack", "(int)", "",
59 "SAEC::checkFunction(context&) const::CallBack::CallBack"},
60 // Anonymous namespace
61 {"XX::(anonymous namespace)::anon_class::anon_func() const",
62 "XX::(anonymous namespace)::anon_class", "anon_func", "()", "const",
63 "XX::(anonymous namespace)::anon_class::anon_func"},
64
65 // Lambda
66 {"main::{lambda()#1}::operator()() const::{lambda()#1}::operator()() const",
67 "main::{lambda()#1}::operator()() const::{lambda()#1}", "operator()", "()", "const",
68 "main::{lambda()#1}::operator()() const::{lambda()#1}::operator()"},
69
70 // Function pointers
71 {"string (*f(vector<int>&&))(float)", "", "f", "(vector<int>&&)", "",
72 "f"},
73 {"void (*&std::_Any_data::_M_access<void (*)()>())()", "std::_Any_data",
74 "_M_access<void (*)()>", "()", "",
75 "std::_Any_data::_M_access<void (*)()>"},
76 {"void (*(*(*(*(*(*(*(* const&func1(int))())())())())())())())()", "",
77 "func1", "(int)", "", "func1"},
78
79 // Decltype
80 {"decltype(nullptr)&& std::forward<decltype(nullptr)>"
81 "(std::remove_reference<decltype(nullptr)>::type&)",
82 "std", "forward<decltype(nullptr)>",
83 "(std::remove_reference<decltype(nullptr)>::type&)", "",
84 "std::forward<decltype(nullptr)>"},
85
86 // Templates
87 {"void llvm::PM<llvm::Module, llvm::AM<llvm::Module>>::"
88 "addPass<llvm::VP>(llvm::VP)",
89 "llvm::PM<llvm::Module, llvm::AM<llvm::Module>>", "addPass<llvm::VP>",
90 "(llvm::VP)", "",
91 "llvm::PM<llvm::Module, llvm::AM<llvm::Module>>::"
92 "addPass<llvm::VP>"},
93 {"void std::vector<Class, std::allocator<Class> >"
94 "::_M_emplace_back_aux<Class const&>(Class const&)",
95 "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&>"},
99 {"unsigned long llvm::countTrailingOnes<unsigned int>"
100 "(unsigned int, llvm::ZeroBehavior)",
101 "llvm", "countTrailingOnes<unsigned int>",
102 "(unsigned int, llvm::ZeroBehavior)", "",
103 "llvm::countTrailingOnes<unsigned int>"},
104 {"std::enable_if<(10u)<(64), bool>::type llvm::isUInt<10u>(unsigned "
105 "long)",
106 "llvm", "isUInt<10u>", "(unsigned long)", "", "llvm::isUInt<10u>"},
107 {"f<A<operator<(X,Y)::Subclass>, sizeof(B)<sizeof(C)>()", "",
108 "f<A<operator<(X,Y)::Subclass>, sizeof(B)<sizeof(C)>", "()", "",
109 "f<A<operator<(X,Y)::Subclass>, sizeof(B)<sizeof(C)>"}};
110
111 for (const auto &test : test_cases) {
112 CPlusPlusLanguage::MethodName method(ConstString(test.input));
113 EXPECT_TRUE(method.IsValid()) << test.input;
114 if (method.IsValid()) {
115 EXPECT_EQ(test.context, method.GetContext().str());
116 EXPECT_EQ(test.basename, method.GetBasename().str());
117 EXPECT_EQ(test.arguments, method.GetArguments().str());
118 EXPECT_EQ(test.qualifiers, method.GetQualifiers().str());
119 EXPECT_EQ(test.scope_qualified_name, method.GetScopeQualifiedName());
120 }
121 }
122 }
123
TEST(CPlusPlusLanguage,ExtractContextAndIdentifier)124 TEST(CPlusPlusLanguage, ExtractContextAndIdentifier) {
125 struct TestCase {
126 std::string input;
127 std::string context, basename;
128 };
129
130 TestCase test_cases[] = {
131 {"main", "", "main"},
132 {"main ", "", "main"},
133 {"foo01::bar", "foo01", "bar"},
134 {"foo::~bar", "foo", "~bar"},
135 {"std::vector<int>::push_back", "std::vector<int>", "push_back"},
136 {"operator<<(Cls, Cls)::Subclass::function",
137 "operator<<(Cls, Cls)::Subclass", "function"},
138 {"std::vector<Class, std::allocator<Class>>"
139 "::_M_emplace_back_aux<Class const&>",
140 "std::vector<Class, std::allocator<Class>>",
141 "_M_emplace_back_aux<Class const&>"},
142 {"`anonymous namespace'::foo", "`anonymous namespace'", "foo"},
143 {"`operator<<A>'::`2'::B<0>::operator>", "`operator<<A>'::`2'::B<0>",
144 "operator>"},
145 {"`anonymous namespace'::S::<<::__l2::Foo",
146 "`anonymous namespace'::S::<<::__l2", "Foo"},
147 // These cases are idiosyncratic in how clang generates debug info for
148 // names when we have template parameters. They are not valid C++ names
149 // but if we fix this we need to support them for older compilers.
150 {"A::operator><A::B>", "A", "operator><A::B>"},
151 {"operator><A::B>", "", "operator><A::B>"},
152 {"A::operator<<A::B>", "A", "operator<<A::B>"},
153 {"operator<<A::B>", "", "operator<<A::B>"},
154 {"A::operator<<<A::B>", "A", "operator<<<A::B>"},
155 {"operator<<<A::B>", "", "operator<<<A::B>"},
156 };
157
158 llvm::StringRef context, basename;
159 for (const auto &test : test_cases) {
160 EXPECT_TRUE(CPlusPlusLanguage::ExtractContextAndIdentifier(
161 test.input.c_str(), context, basename));
162 EXPECT_EQ(test.context, context.str());
163 EXPECT_EQ(test.basename, basename.str());
164 }
165
166 EXPECT_FALSE(CPlusPlusLanguage::ExtractContextAndIdentifier("void", context,
167 basename));
168 EXPECT_FALSE(
169 CPlusPlusLanguage::ExtractContextAndIdentifier("321", context, basename));
170 EXPECT_FALSE(
171 CPlusPlusLanguage::ExtractContextAndIdentifier("", context, basename));
172 EXPECT_FALSE(CPlusPlusLanguage::ExtractContextAndIdentifier(
173 "selector:", context, basename));
174 EXPECT_FALSE(CPlusPlusLanguage::ExtractContextAndIdentifier(
175 "selector:otherField:", context, basename));
176 EXPECT_FALSE(CPlusPlusLanguage::ExtractContextAndIdentifier(
177 "abc::", context, basename));
178 EXPECT_FALSE(CPlusPlusLanguage::ExtractContextAndIdentifier(
179 "f<A<B><C>>", context, basename));
180
181 // We expect these cases to fail until we turn on C++2a
182 EXPECT_FALSE(CPlusPlusLanguage::ExtractContextAndIdentifier(
183 "A::operator<=><A::B>", context, basename));
184 EXPECT_FALSE(CPlusPlusLanguage::ExtractContextAndIdentifier(
185 "operator<=><A::B>", context, basename));
186 }
187
FindAlternate(llvm::StringRef Name)188 static std::set<std::string> FindAlternate(llvm::StringRef Name) {
189 std::set<ConstString> Results;
190 uint32_t Count = CPlusPlusLanguage::FindAlternateFunctionManglings(
191 ConstString(Name), Results);
192 EXPECT_EQ(Count, Results.size());
193 std::set<std::string> Strings;
194 for (ConstString Str : Results)
195 Strings.insert(std::string(Str.GetStringRef()));
196 return Strings;
197 }
198
TEST(CPlusPlusLanguage,FindAlternateFunctionManglings)199 TEST(CPlusPlusLanguage, FindAlternateFunctionManglings) {
200 using namespace testing;
201
202 EXPECT_THAT(FindAlternate("_ZN1A1fEv"),
203 UnorderedElementsAre("_ZNK1A1fEv", "_ZLN1A1fEv"));
204 EXPECT_THAT(FindAlternate("_ZN1A1fEa"), Contains("_ZN1A1fEc"));
205 EXPECT_THAT(FindAlternate("_ZN1A1fEx"), Contains("_ZN1A1fEl"));
206 EXPECT_THAT(FindAlternate("_ZN1A1fEy"), Contains("_ZN1A1fEm"));
207 EXPECT_THAT(FindAlternate("_ZN1A1fEai"), Contains("_ZN1A1fEci"));
208 EXPECT_THAT(FindAlternate("_ZN1AC1Ev"), Contains("_ZN1AC2Ev"));
209 EXPECT_THAT(FindAlternate("_ZN1AD1Ev"), Contains("_ZN1AD2Ev"));
210 EXPECT_THAT(FindAlternate("_bogus"), IsEmpty());
211 }
212
TEST(CPlusPlusLanguage,CPlusPlusNameParser)213 TEST(CPlusPlusLanguage, CPlusPlusNameParser) {
214 // Don't crash.
215 CPlusPlusNameParser((const char *)nullptr);
216 }
217