| 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::CxxMethodName 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::CxxMethodName method{ConstString(name)}; |
| 247 | EXPECT_FALSE(method.IsValid()) << name; |
| 248 | } |
| 249 | } |
| 250 | |
| 251 | TEST(CPlusPlusLanguage, ContainsPath) { |
| 252 | CPlusPlusLanguage::CxxMethodName reference_1( |
| 253 | ConstString("int foo::bar::func01(int a, double b)" )); |
| 254 | CPlusPlusLanguage::CxxMethodName reference_2( |
| 255 | ConstString("int foofoo::bar::func01(std::string a, int b)" )); |
| 256 | CPlusPlusLanguage::CxxMethodName reference_3(ConstString("int func01()" )); |
| 257 | CPlusPlusLanguage::CxxMethodName reference_4( |
| 258 | ConstString("bar::baz::operator bool()" )); |
| 259 | CPlusPlusLanguage::CxxMethodName reference_5( |
| 260 | ConstString("bar::baz::operator bool<int, Type<double>>()" )); |
| 261 | CPlusPlusLanguage::CxxMethodName 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 | |