397 lines
18 KiB
C++
397 lines
18 KiB
C++
//===-- CPlusPlusLanguageTest.cpp -----------------------------------------===//
|
|
//
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
#include "Plugins/Language/CPlusPlus/CPlusPlusLanguage.h"
|
|
#include "Plugins/Language/CPlusPlus/CPlusPlusNameParser.h"
|
|
#include "TestingSupport/SubsystemRAII.h"
|
|
#include "lldb/lldb-enumerations.h"
|
|
#include "gmock/gmock.h"
|
|
#include "gtest/gtest.h"
|
|
|
|
using namespace lldb_private;
|
|
|
|
TEST(CPlusPlusLanguage, MethodNameParsing) {
|
|
struct TestCase {
|
|
std::string input;
|
|
std::string return_type, context, basename, arguments, qualifiers,
|
|
scope_qualified_name;
|
|
};
|
|
|
|
TestCase test_cases[] = {
|
|
{"main(int, char *[]) ", "", "", "main", "(int, char *[])", "", "main"},
|
|
{"foo::bar(baz) const", "", "foo", "bar", "(baz)", "const", "foo::bar"},
|
|
{"foo::~bar(baz)", "", "foo", "~bar", "(baz)", "", "foo::~bar"},
|
|
{"a::b::c::d(e,f)", "", "a::b::c", "d", "(e,f)", "", "a::b::c::d"},
|
|
{"void f(int)", "void", "", "f", "(int)", "", "f"},
|
|
|
|
// Operators
|
|
{"std::basic_ostream<char, std::char_traits<char> >& "
|
|
"std::operator<<<std::char_traits<char> >(std::basic_ostream<char, "
|
|
"std::char_traits<char> >&, char const*)",
|
|
"std::basic_ostream<char, std::char_traits<char> >&", "std",
|
|
"operator<<<std::char_traits<char> >",
|
|
"(std::basic_ostream<char, std::char_traits<char> >&, char const*)", "",
|
|
"std::operator<<<std::char_traits<char> >"},
|
|
{"operator delete[](void*, clang::ASTContext const&, unsigned long)", "",
|
|
"", "operator delete[]",
|
|
"(void*, clang::ASTContext const&, unsigned long)", "",
|
|
"operator delete[]"},
|
|
{"llvm::Optional<clang::PostInitializer>::operator bool() const", "",
|
|
"llvm::Optional<clang::PostInitializer>", "operator bool", "()", "const",
|
|
"llvm::Optional<clang::PostInitializer>::operator bool"},
|
|
{"(anonymous namespace)::FactManager::operator[](unsigned short)", "",
|
|
"(anonymous namespace)::FactManager", "operator[]", "(unsigned short)",
|
|
"", "(anonymous namespace)::FactManager::operator[]"},
|
|
{"const int& std::map<int, pair<short, int>>::operator[](short) const",
|
|
"const int&", "std::map<int, pair<short, int>>", "operator[]", "(short)",
|
|
"const", "std::map<int, pair<short, int>>::operator[]"},
|
|
{"CompareInsn::operator()(llvm::StringRef, InsnMatchEntry const&)", "",
|
|
"CompareInsn", "operator()", "(llvm::StringRef, InsnMatchEntry const&)",
|
|
"", "CompareInsn::operator()"},
|
|
{"llvm::Optional<llvm::MCFixupKind>::operator*() const &", "",
|
|
"llvm::Optional<llvm::MCFixupKind>", "operator*", "()", "const &",
|
|
"llvm::Optional<llvm::MCFixupKind>::operator*"},
|
|
{"auto std::__1::ranges::__begin::__fn::operator()[abi:v160000]<char "
|
|
"const, 18ul>(char const (&) [18ul]) const",
|
|
"auto", "std::__1::ranges::__begin::__fn",
|
|
"operator()[abi:v160000]<char const, 18ul>", "(char const (&) [18ul])",
|
|
"const",
|
|
"std::__1::ranges::__begin::__fn::operator()[abi:v160000]<char const, "
|
|
"18ul>"},
|
|
// Internal classes
|
|
{"operator<<(Cls, Cls)::Subclass::function()", "",
|
|
"operator<<(Cls, Cls)::Subclass", "function", "()", "",
|
|
"operator<<(Cls, Cls)::Subclass::function"},
|
|
{"SAEC::checkFunction(context&) const::CallBack::CallBack(int)", "",
|
|
"SAEC::checkFunction(context&) const::CallBack", "CallBack", "(int)", "",
|
|
"SAEC::checkFunction(context&) const::CallBack::CallBack"},
|
|
// Anonymous namespace
|
|
{"XX::(anonymous namespace)::anon_class::anon_func() const", "",
|
|
"XX::(anonymous namespace)::anon_class", "anon_func", "()", "const",
|
|
"XX::(anonymous namespace)::anon_class::anon_func"},
|
|
|
|
// Lambda
|
|
{"main::{lambda()#1}::operator()() const::{lambda()#1}::operator()() "
|
|
"const",
|
|
"", "main::{lambda()#1}::operator()() const::{lambda()#1}", "operator()",
|
|
"()", "const",
|
|
"main::{lambda()#1}::operator()() const::{lambda()#1}::operator()"},
|
|
|
|
// Function pointers
|
|
{"string (*f(vector<int>&&))(float)", "", "", "f",
|
|
"(vector<int>&&)", "", "f"},
|
|
{"void (*&std::_Any_data::_M_access<void (*)()>())()", "",
|
|
"std::_Any_data", "_M_access<void (*)()>", "()", "",
|
|
"std::_Any_data::_M_access<void (*)()>"},
|
|
{"void (*(*(*(*(*(*(*(* const&func1(int))())())())())())())())()", "",
|
|
"", "func1", "(int)", "", "func1"},
|
|
|
|
// Decltype
|
|
{"decltype(nullptr)&& std::forward<decltype(nullptr)>"
|
|
"(std::remove_reference<decltype(nullptr)>::type&)",
|
|
"decltype(nullptr)&&", "std", "forward<decltype(nullptr)>",
|
|
"(std::remove_reference<decltype(nullptr)>::type&)", "",
|
|
"std::forward<decltype(nullptr)>"},
|
|
|
|
// Templates
|
|
{"void llvm::PM<llvm::Module, llvm::AM<llvm::Module>>::"
|
|
"addPass<llvm::VP>(llvm::VP)",
|
|
"void", "llvm::PM<llvm::Module, llvm::AM<llvm::Module>>",
|
|
"addPass<llvm::VP>", "(llvm::VP)", "",
|
|
"llvm::PM<llvm::Module, llvm::AM<llvm::Module>>::"
|
|
"addPass<llvm::VP>"},
|
|
{"void std::vector<Class, std::allocator<Class> >"
|
|
"::_M_emplace_back_aux<Class const&>(Class const&)",
|
|
"void", "std::vector<Class, std::allocator<Class> >",
|
|
"_M_emplace_back_aux<Class const&>", "(Class const&)", "",
|
|
"std::vector<Class, std::allocator<Class> >::"
|
|
"_M_emplace_back_aux<Class const&>"},
|
|
{"unsigned long llvm::countTrailingOnes<unsigned int>"
|
|
"(unsigned int, llvm::ZeroBehavior)",
|
|
"unsigned long", "llvm", "countTrailingOnes<unsigned int>",
|
|
"(unsigned int, llvm::ZeroBehavior)", "",
|
|
"llvm::countTrailingOnes<unsigned int>"},
|
|
{"std::enable_if<(10u)<(64), bool>::type llvm::isUInt<10u>(unsigned "
|
|
"long)",
|
|
"std::enable_if<(10u)<(64), bool>::type", "llvm", "isUInt<10u>",
|
|
"(unsigned long)", "", "llvm::isUInt<10u>"},
|
|
{"f<A<operator<(X,Y)::Subclass>, sizeof(B)<sizeof(C)>()", "", "",
|
|
"f<A<operator<(X,Y)::Subclass>, sizeof(B)<sizeof(C)>", "()", "",
|
|
"f<A<operator<(X,Y)::Subclass>, sizeof(B)<sizeof(C)>"},
|
|
{"llvm::Optional<llvm::MCFixupKind>::operator*() const volatile &&", "",
|
|
"llvm::Optional<llvm::MCFixupKind>", "operator*", "()",
|
|
"const volatile &&", "llvm::Optional<llvm::MCFixupKind>::operator*"},
|
|
{"void foo<Dummy<char [10]>>()", "void", "", "foo<Dummy<char [10]>>",
|
|
"()", "", "foo<Dummy<char [10]>>"},
|
|
{"void foo<Bar<Bar<int>[10]>>()", "void", "", "foo<Bar<Bar<int>[10]>>",
|
|
"()", "", "foo<Bar<Bar<int>[10]>>"},
|
|
{"void foo<Bar[10]>()", "void", "", "foo<Bar[10]>", "()", "",
|
|
"foo<Bar[10]>"},
|
|
{"void foo<Bar[]>()", "void", "", "foo<Bar[]>", "()", "", "foo<Bar[]>"},
|
|
|
|
// auto return type
|
|
{"auto std::test_return_auto<int>() const", "auto", "std",
|
|
"test_return_auto<int>", "()", "const", "std::test_return_auto<int>"},
|
|
{"decltype(auto) std::test_return_auto<int>(int) const", "decltype(auto)",
|
|
"std", "test_return_auto<int>", "(int)", "const",
|
|
"std::test_return_auto<int>"},
|
|
|
|
// abi_tag on class method
|
|
{"v1::v2::Dummy[abi:c1][abi:c2]<v1::v2::Dummy[abi:c1][abi:c2]<int>> "
|
|
"v1::v2::Dummy[abi:c1][abi:c2]<v1::v2::Dummy[abi:c1][abi:c2]<int>>"
|
|
"::method2<v1::v2::Dummy[abi:c1][abi:c2]<v1::v2::Dummy[abi:c1][abi:c2]<"
|
|
"int>>>(int, v1::v2::Dummy<int>) const &&",
|
|
// Return type
|
|
"v1::v2::Dummy[abi:c1][abi:c2]<v1::v2::Dummy[abi:c1][abi:c2]<int>>",
|
|
// Context
|
|
"v1::v2::Dummy[abi:c1][abi:c2]<v1::v2::Dummy[abi:c1][abi:c2]<int>>",
|
|
// Basename
|
|
"method2<v1::v2::Dummy[abi:c1][abi:c2]<v1::v2::Dummy[abi:c1][abi:c2]<"
|
|
"int>>>",
|
|
// Args, qualifiers
|
|
"(int, v1::v2::Dummy<int>)", "const &&",
|
|
// Full scope-qualified name without args
|
|
"v1::v2::Dummy[abi:c1][abi:c2]<v1::v2::Dummy[abi:c1][abi:c2]<int>>"
|
|
"::method2<v1::v2::Dummy[abi:c1][abi:c2]<v1::v2::Dummy[abi:c1][abi:c2]<"
|
|
"int>>>"},
|
|
|
|
// abi_tag on free function and template argument
|
|
{"v1::v2::Dummy[abi:c1][abi:c2]<v1::v2::Dummy[abi:c1][abi:c2]<int>> "
|
|
"v1::v2::with_tag_in_ns[abi:f1][abi:f2]<v1::v2::Dummy[abi:c1][abi:c2]"
|
|
"<v1::v2::Dummy[abi:c1][abi:c2]<int>>>(int, v1::v2::Dummy<int>) const "
|
|
"&&",
|
|
// Return type
|
|
"v1::v2::Dummy[abi:c1][abi:c2]<v1::v2::Dummy[abi:c1][abi:c2]<int>>",
|
|
// Context
|
|
"v1::v2",
|
|
// Basename
|
|
"with_tag_in_ns[abi:f1][abi:f2]<v1::v2::Dummy[abi:c1][abi:c2]<v1::v2::"
|
|
"Dummy[abi:c1][abi:c2]<int>>>",
|
|
// Args, qualifiers
|
|
"(int, v1::v2::Dummy<int>)", "const &&",
|
|
// Full scope-qualified name without args
|
|
"v1::v2::with_tag_in_ns[abi:f1][abi:f2]<v1::v2::Dummy[abi:c1][abi:c2]<"
|
|
"v1::v2::Dummy[abi:c1][abi:c2]<int>>>"},
|
|
|
|
// abi_tag with special characters
|
|
{"auto ns::with_tag_in_ns[abi:special tag,0.0][abi:special "
|
|
"tag,1.0]<Dummy<int>>"
|
|
"(float) const &&",
|
|
// Return type
|
|
"auto",
|
|
// Context
|
|
"ns",
|
|
// Basename
|
|
"with_tag_in_ns[abi:special tag,0.0][abi:special tag,1.0]<Dummy<int>>",
|
|
// Args, qualifiers
|
|
"(float)", "const &&",
|
|
// Full scope-qualified name without args
|
|
"ns::with_tag_in_ns[abi:special tag,0.0][abi:special "
|
|
"tag,1.0]<Dummy<int>>"},
|
|
|
|
// abi_tag on operator overloads
|
|
{"std::__1::error_code::operator bool[abi:v160000]() const", "",
|
|
"std::__1::error_code", "operator bool[abi:v160000]", "()", "const",
|
|
"std::__1::error_code::operator bool[abi:v160000]"},
|
|
|
|
{"auto ns::foo::operator[][abi:v160000](size_t) const", "auto", "ns::foo",
|
|
"operator[][abi:v160000]", "(size_t)", "const",
|
|
"ns::foo::operator[][abi:v160000]"},
|
|
|
|
{"auto Foo[abi:abc]<int>::operator<<<Foo[abi:abc]<int>>(int) &", "auto",
|
|
"Foo[abi:abc]<int>", "operator<<<Foo[abi:abc]<int>>", "(int)", "&",
|
|
"Foo[abi:abc]<int>::operator<<<Foo[abi:abc]<int>>"}};
|
|
|
|
for (const auto &test : test_cases) {
|
|
CPlusPlusLanguage::MethodName method(ConstString(test.input));
|
|
EXPECT_TRUE(method.IsValid()) << test.input;
|
|
if (method.IsValid()) {
|
|
EXPECT_EQ(test.return_type, method.GetReturnType().str());
|
|
EXPECT_EQ(test.context, method.GetContext().str());
|
|
EXPECT_EQ(test.basename, method.GetBasename().str());
|
|
EXPECT_EQ(test.arguments, method.GetArguments().str());
|
|
EXPECT_EQ(test.qualifiers, method.GetQualifiers().str());
|
|
EXPECT_EQ(test.scope_qualified_name, method.GetScopeQualifiedName());
|
|
}
|
|
}
|
|
}
|
|
|
|
TEST(CPlusPlusLanguage, InvalidMethodNameParsing) {
|
|
// Tests that we correctly reject malformed function names
|
|
|
|
std::string test_cases[] = {
|
|
"int Foo::operator[]<[10>()",
|
|
"Foo::operator bool[10]()",
|
|
"auto A::operator<=>[abi:tag]<A::B>()",
|
|
"auto A::operator<<<(int)",
|
|
"auto A::operator>>>(int)",
|
|
"auto A::operator<<<Type[abi:tag]<>(int)",
|
|
"auto A::operator<<<Type[abi:tag]<Type<int>>(int)",
|
|
"auto A::foo[(int)",
|
|
"auto A::foo[](int)",
|
|
"auto A::foo[bar](int)",
|
|
"auto A::foo[abi](int)",
|
|
"auto A::foo[abi:(int)",
|
|
};
|
|
|
|
for (const auto &name : test_cases) {
|
|
CPlusPlusLanguage::MethodName method{ConstString(name)};
|
|
EXPECT_FALSE(method.IsValid()) << name;
|
|
}
|
|
}
|
|
|
|
TEST(CPlusPlusLanguage, ContainsPath) {
|
|
CPlusPlusLanguage::MethodName
|
|
reference_1(ConstString("int foo::bar::func01(int a, double b)"));
|
|
CPlusPlusLanguage::MethodName
|
|
reference_2(ConstString("int foofoo::bar::func01(std::string a, int b)"));
|
|
CPlusPlusLanguage::MethodName reference_3(ConstString("int func01()"));
|
|
CPlusPlusLanguage::MethodName
|
|
reference_4(ConstString("bar::baz::operator bool()"));
|
|
CPlusPlusLanguage::MethodName reference_5(
|
|
ConstString("bar::baz::operator bool<int, Type<double>>()"));
|
|
CPlusPlusLanguage::MethodName reference_6(ConstString(
|
|
"bar::baz::operator<<<Type<double>, Type<std::vector<double>>>()"));
|
|
|
|
EXPECT_TRUE(reference_1.ContainsPath(""));
|
|
EXPECT_TRUE(reference_1.ContainsPath("func01"));
|
|
EXPECT_TRUE(reference_1.ContainsPath("bar::func01"));
|
|
EXPECT_TRUE(reference_1.ContainsPath("foo::bar::func01"));
|
|
EXPECT_FALSE(reference_1.ContainsPath("func"));
|
|
EXPECT_FALSE(reference_1.ContainsPath("baz::func01"));
|
|
EXPECT_FALSE(reference_1.ContainsPath("::bar::func01"));
|
|
EXPECT_FALSE(reference_1.ContainsPath("::foo::baz::func01"));
|
|
EXPECT_FALSE(reference_1.ContainsPath("foo::bar::baz::func01"));
|
|
|
|
EXPECT_TRUE(reference_2.ContainsPath(""));
|
|
EXPECT_TRUE(reference_2.ContainsPath("foofoo::bar::func01"));
|
|
EXPECT_FALSE(reference_2.ContainsPath("foo::bar::func01"));
|
|
|
|
EXPECT_TRUE(reference_3.ContainsPath(""));
|
|
EXPECT_TRUE(reference_3.ContainsPath("func01"));
|
|
EXPECT_FALSE(reference_3.ContainsPath("func"));
|
|
EXPECT_FALSE(reference_3.ContainsPath("bar::func01"));
|
|
|
|
EXPECT_TRUE(reference_4.ContainsPath(""));
|
|
EXPECT_TRUE(reference_4.ContainsPath("operator"));
|
|
EXPECT_TRUE(reference_4.ContainsPath("operator bool"));
|
|
EXPECT_TRUE(reference_4.ContainsPath("baz::operator bool"));
|
|
EXPECT_TRUE(reference_4.ContainsPath("bar::baz::operator bool"));
|
|
EXPECT_FALSE(reference_4.ContainsPath("az::operator bool"));
|
|
|
|
EXPECT_TRUE(reference_5.ContainsPath(""));
|
|
EXPECT_TRUE(reference_5.ContainsPath("operator"));
|
|
EXPECT_TRUE(reference_5.ContainsPath("operator bool"));
|
|
EXPECT_TRUE(reference_5.ContainsPath("operator bool<int, Type<double>>"));
|
|
EXPECT_FALSE(reference_5.ContainsPath("operator bool<int, double>"));
|
|
EXPECT_FALSE(reference_5.ContainsPath("operator bool<int, Type<int>>"));
|
|
|
|
EXPECT_TRUE(reference_6.ContainsPath(""));
|
|
EXPECT_TRUE(reference_6.ContainsPath("operator"));
|
|
EXPECT_TRUE(reference_6.ContainsPath("operator<<"));
|
|
EXPECT_TRUE(reference_6.ContainsPath(
|
|
"bar::baz::operator<<<Type<double>, Type<std::vector<double>>>()"));
|
|
EXPECT_FALSE(reference_6.ContainsPath("operator<<<Type<double>>"));
|
|
}
|
|
|
|
TEST(CPlusPlusLanguage, ExtractContextAndIdentifier) {
|
|
struct TestCase {
|
|
std::string input;
|
|
std::string context, basename;
|
|
};
|
|
|
|
TestCase test_cases[] = {
|
|
{"main", "", "main"},
|
|
{"main ", "", "main"},
|
|
{"foo01::bar", "foo01", "bar"},
|
|
{"foo::~bar", "foo", "~bar"},
|
|
{"std::vector<int>::push_back", "std::vector<int>", "push_back"},
|
|
{"operator<<(Cls, Cls)::Subclass::function",
|
|
"operator<<(Cls, Cls)::Subclass", "function"},
|
|
{"std::vector<Class, std::allocator<Class>>"
|
|
"::_M_emplace_back_aux<Class const&>",
|
|
"std::vector<Class, std::allocator<Class>>",
|
|
"_M_emplace_back_aux<Class const&>"},
|
|
{"`anonymous namespace'::foo", "`anonymous namespace'", "foo"},
|
|
{"`operator<<A>'::`2'::B<0>::operator>", "`operator<<A>'::`2'::B<0>",
|
|
"operator>"},
|
|
{"`anonymous namespace'::S::<<::__l2::Foo",
|
|
"`anonymous namespace'::S::<<::__l2", "Foo"},
|
|
// These cases are idiosyncratic in how clang generates debug info for
|
|
// names when we have template parameters. They are not valid C++ names
|
|
// but if we fix this we need to support them for older compilers.
|
|
{"A::operator><A::B>", "A", "operator><A::B>"},
|
|
{"operator><A::B>", "", "operator><A::B>"},
|
|
{"A::operator<<A::B>", "A", "operator<<A::B>"},
|
|
{"operator<<A::B>", "", "operator<<A::B>"},
|
|
{"A::operator<<<A::B>", "A", "operator<<<A::B>"},
|
|
{"operator<<<A::B>", "", "operator<<<A::B>"},
|
|
};
|
|
|
|
llvm::StringRef context, basename;
|
|
for (const auto &test : test_cases) {
|
|
EXPECT_TRUE(CPlusPlusLanguage::ExtractContextAndIdentifier(
|
|
test.input.c_str(), context, basename));
|
|
EXPECT_EQ(test.context, context.str());
|
|
EXPECT_EQ(test.basename, basename.str());
|
|
}
|
|
|
|
EXPECT_FALSE(CPlusPlusLanguage::ExtractContextAndIdentifier("void", context,
|
|
basename));
|
|
EXPECT_FALSE(
|
|
CPlusPlusLanguage::ExtractContextAndIdentifier("321", context, basename));
|
|
EXPECT_FALSE(
|
|
CPlusPlusLanguage::ExtractContextAndIdentifier("", context, basename));
|
|
EXPECT_FALSE(CPlusPlusLanguage::ExtractContextAndIdentifier(
|
|
"selector:", context, basename));
|
|
EXPECT_FALSE(CPlusPlusLanguage::ExtractContextAndIdentifier(
|
|
"selector:otherField:", context, basename));
|
|
EXPECT_FALSE(CPlusPlusLanguage::ExtractContextAndIdentifier(
|
|
"abc::", context, basename));
|
|
EXPECT_FALSE(CPlusPlusLanguage::ExtractContextAndIdentifier(
|
|
"f<A<B><C>>", context, basename));
|
|
|
|
// We expect these cases to fail until we turn on C++2a
|
|
EXPECT_FALSE(CPlusPlusLanguage::ExtractContextAndIdentifier(
|
|
"A::operator<=><A::B>", context, basename));
|
|
EXPECT_FALSE(CPlusPlusLanguage::ExtractContextAndIdentifier(
|
|
"operator<=><A::B>", context, basename));
|
|
}
|
|
|
|
static std::vector<std::string> GenerateAlternate(llvm::StringRef Name) {
|
|
std::vector<std::string> Strings;
|
|
if (Language *CPlusPlusLang =
|
|
Language::FindPlugin(lldb::eLanguageTypeC_plus_plus)) {
|
|
std::vector<ConstString> Results =
|
|
CPlusPlusLang->GenerateAlternateFunctionManglings(ConstString(Name));
|
|
for (ConstString Str : Results)
|
|
Strings.push_back(std::string(Str.GetStringRef()));
|
|
}
|
|
return Strings;
|
|
}
|
|
|
|
TEST(CPlusPlusLanguage, GenerateAlternateFunctionManglings) {
|
|
using namespace testing;
|
|
|
|
SubsystemRAII<CPlusPlusLanguage> lang;
|
|
|
|
EXPECT_THAT(GenerateAlternate("_ZN1A1fEv"),
|
|
UnorderedElementsAre("_ZNK1A1fEv", "_ZLN1A1fEv"));
|
|
EXPECT_THAT(GenerateAlternate("_ZN1A1fEa"), Contains("_ZN1A1fEc"));
|
|
EXPECT_THAT(GenerateAlternate("_ZN1A1fEx"), Contains("_ZN1A1fEl"));
|
|
EXPECT_THAT(GenerateAlternate("_ZN1A1fEy"), Contains("_ZN1A1fEm"));
|
|
EXPECT_THAT(GenerateAlternate("_ZN1A1fEai"), Contains("_ZN1A1fEci"));
|
|
EXPECT_THAT(GenerateAlternate("_ZN1AC1Ev"), Contains("_ZN1AC2Ev"));
|
|
EXPECT_THAT(GenerateAlternate("_ZN1AD1Ev"), Contains("_ZN1AD2Ev"));
|
|
EXPECT_THAT(GenerateAlternate("_bogus"), IsEmpty());
|
|
}
|
|
|
|
TEST(CPlusPlusLanguage, CPlusPlusNameParser) {
|
|
// Don't crash.
|
|
CPlusPlusNameParser((const char *)nullptr);
|
|
}
|