diff --git a/lldb/include/lldb/DataFormatters/DataVisualization.h b/lldb/include/lldb/DataFormatters/DataVisualization.h --- a/lldb/include/lldb/DataFormatters/DataVisualization.h +++ b/lldb/include/lldb/DataFormatters/DataVisualization.h @@ -69,9 +69,9 @@ static void Clear(); - static void - ForEach(std::function - callback); + static void ForEach(std::function + callback); static uint32_t GetCount(); }; diff --git a/lldb/include/lldb/DataFormatters/FormatManager.h b/lldb/include/lldb/DataFormatters/FormatManager.h --- a/lldb/include/lldb/DataFormatters/FormatManager.h +++ b/lldb/include/lldb/DataFormatters/FormatManager.h @@ -34,7 +34,7 @@ // this file's objects directly class FormatManager : public IFormatChangeListener { - typedef FormatMap NamedSummariesMap; + typedef FormatMap NamedSummariesMap; typedef TypeCategoryMap::MapType::iterator CategoryMapIterator; public: @@ -144,13 +144,6 @@ static const char *GetFormatAsCString(lldb::Format format); - // if the user tries to add formatters for, say, "struct Foo" those will not - // match any type because of the way we strip qualifiers from typenames this - // method looks for the case where the user is adding a - // "class","struct","enum" or "union" Foo and strips the unnecessary - // qualifier - static ConstString GetValidTypeName(ConstString type); - // when DataExtractor dumps a vectorOfT, it uses a predefined format for each // item this method returns it, or eFormatInvalid if vector_format is not a // vectorOf diff --git a/lldb/include/lldb/DataFormatters/FormattersContainer.h b/lldb/include/lldb/DataFormatters/FormattersContainer.h --- a/lldb/include/lldb/DataFormatters/FormattersContainer.h +++ b/lldb/include/lldb/DataFormatters/FormattersContainer.h @@ -37,57 +37,113 @@ virtual uint32_t GetCurrentRevision() = 0; }; -// if the user tries to add formatters for, say, "struct Foo" those will not -// match any type because of the way we strip qualifiers from typenames this -// method looks for the case where the user is adding a "class","struct","enum" -// or "union" Foo and strips the unnecessary qualifier -static inline ConstString GetValidTypeName_Impl(ConstString type) { - if (type.IsEmpty()) - return type; +/// Class for matching type names. +class TypeMatcher { + RegularExpression m_type_name_regex; + ConstString m_type_name; + /// False if m_type_name_regex should be used for matching. False if this is + /// just matching by comparing with m_type_name string. + bool m_is_regex; + /// True iff this TypeMatcher is invalid and shouldn't be used for any + /// type matching logic. + bool m_valid = true; + + // if the user tries to add formatters for, say, "struct Foo" those will not + // match any type because of the way we strip qualifiers from typenames this + // method looks for the case where the user is adding a + // "class","struct","enum" or "union" Foo and strips the unnecessary qualifier + static ConstString StripTypeName(ConstString type) { + if (type.IsEmpty()) + return type; + + std::string type_cstr(type.AsCString()); + StringLexer type_lexer(type_cstr); + + type_lexer.AdvanceIf("class "); + type_lexer.AdvanceIf("enum "); + type_lexer.AdvanceIf("struct "); + type_lexer.AdvanceIf("union "); + + while (type_lexer.NextIf({' ', '\t', '\v', '\f'}).first) + ; + + return ConstString(type_lexer.GetUnlexed()); + } - std::string type_cstr(type.AsCString()); - StringLexer type_lexer(type_cstr); +public: + /// Creates an invalid matcher that should not be used for any type matching. + TypeMatcher() : m_valid(false) {} + /// Creates a matcher that accepts any type with exactly the given type name. + TypeMatcher(ConstString type_name) + : m_type_name(type_name), m_is_regex(false) {} + /// Creates a matcher that accepts any type matching the given regex. + TypeMatcher(RegularExpression regex) + : m_type_name_regex(regex), m_is_regex(true) {} + + /// True iff this matches the given type name. + bool Matches(ConstString type_name) const { + assert(m_valid && "Using invalid TypeMatcher"); + + if (m_is_regex) + return m_type_name_regex.Execute(type_name.GetStringRef()); + return m_type_name == type_name || + StripTypeName(m_type_name) == StripTypeName(type_name); + } - type_lexer.AdvanceIf("class "); - type_lexer.AdvanceIf("enum "); - type_lexer.AdvanceIf("struct "); - type_lexer.AdvanceIf("union "); + /// Returns the underlying match string for this TypeMatcher. + ConstString GetMatchString() const { + assert(m_valid && "Using invalid TypeMatcher"); - while (type_lexer.NextIf({' ', '\t', '\v', '\f'}).first) - ; + if (m_is_regex) + return ConstString(m_type_name_regex.GetText()); + return StripTypeName(m_type_name); + } - return ConstString(type_lexer.GetUnlexed()); -} + /// Returns true if this TypeMatcher and the given one were most created by + /// the same match string. + /// The main purpose of this function is to find existing TypeMatcher + /// instances by the user input that created them. This is necessary as LLDB + /// allows referencing existing TypeMatchers in commands by the user input + /// that originally created them: + /// (lldb) type summary add --summary-string \"A\" -x TypeName + /// (lldb) type summary delete TypeName + bool CreatedBySameMatchString(TypeMatcher other) const { + assert(m_valid && "Using invalid TypeMatcher"); + + return GetMatchString() == other.GetMatchString(); + } +}; -template class FormattersContainer; +template class FormattersContainer; -template class FormatMap { +template class FormatMap { public: typedef typename ValueType::SharedPointer ValueSP; - typedef std::vector> MapType; + typedef std::vector> MapType; typedef typename MapType::iterator MapIterator; - typedef std::function ForEachCallback; + typedef std::function + ForEachCallback; FormatMap(IFormatChangeListener *lst) : m_map(), m_map_mutex(), listener(lst) {} - void Add(KeyType name, const ValueSP &entry) { + void Add(TypeMatcher matcher, const ValueSP &entry) { if (listener) entry->GetRevision() = listener->GetCurrentRevision(); else entry->GetRevision() = 0; std::lock_guard guard(m_map_mutex); - Delete(name); - m_map.emplace_back(std::move(name), std::move(entry)); + Delete(matcher); + m_map.emplace_back(std::move(matcher), std::move(entry)); if (listener) listener->Changed(); } - bool Delete(const KeyType &name) { + bool Delete(const TypeMatcher &matcher) { std::lock_guard guard(m_map_mutex); for (MapIterator iter = m_map.begin(); iter != m_map.end(); ++iter) - if (iter->first == name) { + if (iter->first.CreatedBySameMatchString(matcher)) { m_map.erase(iter); if (listener) listener->Changed(); @@ -103,10 +159,10 @@ listener->Changed(); } - bool Get(const KeyType &name, ValueSP &entry) { + bool Get(const TypeMatcher &matcher, ValueSP &entry) { std::lock_guard guard(m_map_mutex); for (const auto &pos : m_map) - if (pos.first == name) { + if (pos.first.CreatedBySameMatchString(matcher)) { entry = pos.second; return true; } @@ -117,7 +173,7 @@ if (callback) { std::lock_guard guard(m_map_mutex); for (const auto &pos : m_map) { - const KeyType &type = pos.first; + const TypeMatcher &type = pos.first; if (!callback(type, pos.second)) break; } @@ -134,10 +190,10 @@ } // If caller holds the mutex we could return a reference without copy ctor. - KeyType GetKeyAtIndex(size_t index) { + llvm::Optional GetKeyAtIndex(size_t index) { std::lock_guard guard(m_map_mutex); if (index >= m_map.size()) - return {}; + return llvm::None; return m_map[index].first; } @@ -150,41 +206,43 @@ std::recursive_mutex &mutex() { return m_map_mutex; } - friend class FormattersContainer; + friend class FormattersContainer; friend class FormatManager; }; -template class FormattersContainer { +template class FormattersContainer { protected: - typedef FormatMap BackEndType; + typedef FormatMap BackEndType; public: - typedef typename BackEndType::MapType MapType; - typedef typename MapType::iterator MapIterator; - typedef KeyType MapKeyType; typedef std::shared_ptr MapValueType; typedef typename BackEndType::ForEachCallback ForEachCallback; - typedef typename std::shared_ptr> + typedef typename std::shared_ptr> SharedPointer; friend class TypeCategoryImpl; FormattersContainer(IFormatChangeListener *lst) : m_format_map(lst) {} - void Add(MapKeyType type, const MapValueType &entry) { - Add_Impl(std::move(type), entry, static_cast(nullptr)); + void Add(TypeMatcher type, const MapValueType &entry) { + m_format_map.Add(std::move(type), entry); } - bool Delete(ConstString type) { - return Delete_Impl(type, static_cast(nullptr)); - } + bool Delete(TypeMatcher type) { return m_format_map.Delete(type); } bool Get(ConstString type, MapValueType &entry) { - return Get_Impl(type, entry, static_cast(nullptr)); + std::lock_guard guard(m_format_map.mutex()); + for (auto &formatter : llvm::reverse(m_format_map.map())) { + if (formatter.first.Matches(type)) { + entry = formatter.second; + return true; + } + } + return false; } bool GetExact(ConstString type, MapValueType &entry) { - return GetExact_Impl(type, entry, static_cast(nullptr)); + return m_format_map.Get(type, entry); } MapValueType GetAtIndex(size_t index) { @@ -192,8 +250,12 @@ } lldb::TypeNameSpecifierImplSP GetTypeNameSpecifierAtIndex(size_t index) { - return GetTypeNameSpecifierAtIndex_Impl(index, - static_cast(nullptr)); + llvm::Optional type_matcher = + m_format_map.GetKeyAtIndex(index); + if (!type_matcher) + return lldb::TypeNameSpecifierImplSP(); + return lldb::TypeNameSpecifierImplSP(new TypeNameSpecifierImpl( + type_matcher->GetMatchString().GetStringRef(), true)); } void Clear() { m_format_map.Clear(); } @@ -208,91 +270,6 @@ FormattersContainer(const FormattersContainer &) = delete; const FormattersContainer &operator=(const FormattersContainer &) = delete; - void Add_Impl(MapKeyType type, const MapValueType &entry, - RegularExpression *dummy) { - m_format_map.Add(std::move(type), entry); - } - - void Add_Impl(ConstString type, const MapValueType &entry, - ConstString *dummy) { - m_format_map.Add(GetValidTypeName_Impl(type), entry); - } - - bool Delete_Impl(ConstString type, ConstString *dummy) { - return m_format_map.Delete(type); - } - - bool Delete_Impl(ConstString type, RegularExpression *dummy) { - std::lock_guard guard(m_format_map.mutex()); - MapIterator pos, end = m_format_map.map().end(); - for (pos = m_format_map.map().begin(); pos != end; pos++) { - const RegularExpression ®ex = pos->first; - if (type.GetStringRef() == regex.GetText()) { - m_format_map.map().erase(pos); - if (m_format_map.listener) - m_format_map.listener->Changed(); - return true; - } - } - return false; - } - - bool Get_Impl(ConstString type, MapValueType &entry, ConstString *dummy) { - return m_format_map.Get(type, entry); - } - - bool GetExact_Impl(ConstString type, MapValueType &entry, - ConstString *dummy) { - return Get_Impl(type, entry, static_cast(nullptr)); - } - - lldb::TypeNameSpecifierImplSP - GetTypeNameSpecifierAtIndex_Impl(size_t index, ConstString *dummy) { - ConstString key = m_format_map.GetKeyAtIndex(index); - if (key) - return lldb::TypeNameSpecifierImplSP( - new TypeNameSpecifierImpl(key.GetStringRef(), false)); - else - return lldb::TypeNameSpecifierImplSP(); - } - - lldb::TypeNameSpecifierImplSP - GetTypeNameSpecifierAtIndex_Impl(size_t index, RegularExpression *dummy) { - RegularExpression regex = m_format_map.GetKeyAtIndex(index); - if (regex == RegularExpression()) - return lldb::TypeNameSpecifierImplSP(); - return lldb::TypeNameSpecifierImplSP( - new TypeNameSpecifierImpl(regex.GetText().str().c_str(), true)); - } - - bool Get_Impl(ConstString key, MapValueType &value, - RegularExpression *dummy) { - llvm::StringRef key_str = key.GetStringRef(); - std::lock_guard guard(m_format_map.mutex()); - // Patterns are matched in reverse-chronological order. - for (const auto &pos : llvm::reverse(m_format_map.map())) { - const RegularExpression ®ex = pos.first; - if (regex.Execute(key_str)) { - value = pos.second; - return true; - } - } - return false; - } - - bool GetExact_Impl(ConstString key, MapValueType &value, - RegularExpression *dummy) { - std::lock_guard guard(m_format_map.mutex()); - for (const auto &pos : m_format_map.map()) { - const RegularExpression ®ex = pos.first; - if (regex.GetText() == key.GetStringRef()) { - value = pos.second; - return true; - } - } - return false; - } - bool Get(const FormattersMatchVector &candidates, MapValueType &entry) { for (const FormattersMatchCandidate &candidate : candidates) { if (Get(candidate.GetTypeName(), entry)) { diff --git a/lldb/include/lldb/DataFormatters/TypeCategory.h b/lldb/include/lldb/DataFormatters/TypeCategory.h --- a/lldb/include/lldb/DataFormatters/TypeCategory.h +++ b/lldb/include/lldb/DataFormatters/TypeCategory.h @@ -25,12 +25,11 @@ template class FormatterContainerPair { public: - typedef FormattersContainer ExactMatchContainer; - typedef FormattersContainer - RegexMatchContainer; + typedef FormattersContainer ExactMatchContainer; + typedef FormattersContainer RegexMatchContainer; - typedef typename ExactMatchContainer::MapType ExactMatchMap; - typedef typename RegexMatchContainer::MapType RegexMatchMap; + typedef TypeMatcher ExactMatchMap; + typedef TypeMatcher RegexMatchMap; typedef typename ExactMatchContainer::MapValueType MapValueType; @@ -348,19 +347,13 @@ friend class LanguageCategory; friend class TypeCategoryMap; - friend class FormattersContainer; - friend class FormattersContainer; + friend class FormattersContainer; - friend class FormattersContainer; - friend class FormattersContainer; + friend class FormattersContainer; - friend class FormattersContainer; - friend class FormattersContainer; - - friend class FormattersContainer; - friend class FormattersContainer; + friend class FormattersContainer; + friend class FormattersContainer; }; } // namespace lldb_private diff --git a/lldb/include/lldb/DataFormatters/TypeCategoryMap.h b/lldb/include/lldb/DataFormatters/TypeCategoryMap.h --- a/lldb/include/lldb/DataFormatters/TypeCategoryMap.h +++ b/lldb/include/lldb/DataFormatters/TypeCategoryMap.h @@ -103,7 +103,7 @@ std::recursive_mutex &mutex() { return m_map_mutex; } - friend class FormattersContainer; + friend class FormattersContainer; friend class FormatManager; }; } // namespace lldb_private diff --git a/lldb/source/Commands/CommandObjectType.cpp b/lldb/source/Commands/CommandObjectType.cpp --- a/lldb/source/Commands/CommandObjectType.cpp +++ b/lldb/source/Commands/CommandObjectType.cpp @@ -1066,13 +1066,15 @@ TypeCategoryImpl::ForEachCallbacks foreach; foreach .SetExact([&result, &formatter_regex, &any_printed]( - ConstString name, + const TypeMatcher &type_matcher, const FormatterSharedPointer &format_sp) -> bool { if (formatter_regex) { bool escape = true; - if (name.GetStringRef() == formatter_regex->GetText()) { + if (type_matcher.CreatedBySameMatchString( + ConstString(formatter_regex->GetText()))) { escape = false; - } else if (formatter_regex->Execute(name.GetStringRef())) { + } else if (formatter_regex->Execute( + type_matcher.GetMatchString().GetStringRef())) { escape = false; } @@ -1081,20 +1083,23 @@ } any_printed = true; - result.GetOutputStream().Printf("%s: %s\n", name.AsCString(), - format_sp->GetDescription().c_str()); + result.GetOutputStream().Printf( + "%s: %s\n", type_matcher.GetMatchString().GetCString(), + format_sp->GetDescription().c_str()); return true; }); foreach .SetWithRegex([&result, &formatter_regex, &any_printed]( - const RegularExpression ®ex, + const TypeMatcher &type_matcher, const FormatterSharedPointer &format_sp) -> bool { if (formatter_regex) { bool escape = true; - if (regex.GetText() == formatter_regex->GetText()) { + if (type_matcher.CreatedBySameMatchString( + ConstString(formatter_regex->GetText()))) { escape = false; - } else if (formatter_regex->Execute(regex.GetText())) { + } else if (formatter_regex->Execute( + type_matcher.GetMatchString().GetStringRef())) { escape = false; } @@ -1103,9 +1108,9 @@ } any_printed = true; - result.GetOutputStream().Printf("%s: %s\n", - regex.GetText().str().c_str(), - format_sp->GetDescription().c_str()); + result.GetOutputStream().Printf( + "%s: %s\n", type_matcher.GetMatchString().GetCString(), + format_sp->GetDescription().c_str()); return true; }); @@ -1681,10 +1686,10 @@ if (DataVisualization::NamedSummaryFormats::GetCount() > 0) { result.GetOutputStream().Printf("Named summaries:\n"); DataVisualization::NamedSummaryFormats::ForEach( - [&result](ConstString name, + [&result](const TypeMatcher &type_matcher, const TypeSummaryImplSP &summary_sp) -> bool { result.GetOutputStream().Printf( - "%s: %s\n", name.AsCString(), + "%s: %s\n", type_matcher.GetMatchString().GetCString(), summary_sp->GetDescription().c_str()); return true; }); diff --git a/lldb/source/DataFormatters/DataVisualization.cpp b/lldb/source/DataFormatters/DataVisualization.cpp --- a/lldb/source/DataFormatters/DataVisualization.cpp +++ b/lldb/source/DataFormatters/DataVisualization.cpp @@ -174,8 +174,7 @@ void DataVisualization::NamedSummaryFormats::Add( ConstString type, const lldb::TypeSummaryImplSP &entry) { - GetFormatManager().GetNamedSummaryContainer().Add( - FormatManager::GetValidTypeName(type), entry); + GetFormatManager().GetNamedSummaryContainer().Add(type, entry); } bool DataVisualization::NamedSummaryFormats::Delete(ConstString type) { @@ -187,7 +186,7 @@ } void DataVisualization::NamedSummaryFormats::ForEach( - std::function + std::function callback) { GetFormatManager().GetNamedSummaryContainer().ForEach(callback); } diff --git a/lldb/source/DataFormatters/FormatManager.cpp b/lldb/source/DataFormatters/FormatManager.cpp --- a/lldb/source/DataFormatters/FormatManager.cpp +++ b/lldb/source/DataFormatters/FormatManager.cpp @@ -551,10 +551,6 @@ return true; } -ConstString FormatManager::GetValidTypeName(ConstString type) { - return ::GetValidTypeName_Impl(type); -} - ConstString FormatManager::GetTypeForCache(ValueObject &valobj, lldb::DynamicValueType use_dynamic) { ValueObjectSP valobj_sp = valobj.GetQualifiedRepresentationIfAvailable( diff --git a/lldb/unittests/DataFormatter/CMakeLists.txt b/lldb/unittests/DataFormatter/CMakeLists.txt --- a/lldb/unittests/DataFormatter/CMakeLists.txt +++ b/lldb/unittests/DataFormatter/CMakeLists.txt @@ -1,5 +1,6 @@ add_lldb_unittest(LLDBFormatterTests FormatManagerTests.cpp + FormattersContainerTest.cpp StringPrinterTests.cpp LINK_LIBS diff --git a/lldb/unittests/DataFormatter/FormattersContainerTest.cpp b/lldb/unittests/DataFormatter/FormattersContainerTest.cpp new file mode 100644 --- /dev/null +++ b/lldb/unittests/DataFormatter/FormattersContainerTest.cpp @@ -0,0 +1,159 @@ +//===-- FormattersContainerTests.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 "lldb/DataFormatters/FormattersContainer.h" + +#include "gtest/gtest.h" + +using namespace lldb; +using namespace lldb_private; + +// All the prefixes that the exact name matching will strip from the type. +static const std::vector exact_name_prefixes = { + "", // no prefix. + "class ", "struct ", "union ", "enum ", +}; + +// TypeMatcher that uses a exact type name string that needs to be matched. +TEST(TypeMatcherTests, ExactName) { + for (const std::string &prefix : exact_name_prefixes) { + SCOPED_TRACE("Prefix: " + prefix); + + TypeMatcher matcher(ConstString(prefix + "Name")); + EXPECT_TRUE(matcher.Matches(ConstString("class Name"))); + EXPECT_TRUE(matcher.Matches(ConstString("struct Name"))); + EXPECT_TRUE(matcher.Matches(ConstString("union Name"))); + EXPECT_TRUE(matcher.Matches(ConstString("enum Name"))); + EXPECT_TRUE(matcher.Matches(ConstString("Name"))); + + EXPECT_FALSE(matcher.Matches(ConstString("Name "))); + EXPECT_FALSE(matcher.Matches(ConstString("ame"))); + EXPECT_FALSE(matcher.Matches(ConstString("Nam"))); + EXPECT_FALSE(matcher.Matches(ConstString("am"))); + EXPECT_FALSE(matcher.Matches(ConstString("a"))); + EXPECT_FALSE(matcher.Matches(ConstString(" "))); + EXPECT_FALSE(matcher.Matches(ConstString("class N"))); + EXPECT_FALSE(matcher.Matches(ConstString("class "))); + EXPECT_FALSE(matcher.Matches(ConstString("class"))); + } +} + +// TypeMatcher that uses a regex to match a type name. +TEST(TypeMatcherTests, RegexName) { + TypeMatcher matcher(RegularExpression("^a[a-z]c$")); + EXPECT_TRUE(matcher.Matches(ConstString("abc"))); + EXPECT_TRUE(matcher.Matches(ConstString("azc"))); + + // FIXME: This isn't consistent with the 'exact' type name matches above. + EXPECT_FALSE(matcher.Matches(ConstString("class abc"))); + + EXPECT_FALSE(matcher.Matches(ConstString("abbc"))); + EXPECT_FALSE(matcher.Matches(ConstString(" abc"))); + EXPECT_FALSE(matcher.Matches(ConstString("abc "))); + EXPECT_FALSE(matcher.Matches(ConstString(" abc "))); + EXPECT_FALSE(matcher.Matches(ConstString("XabcX"))); + EXPECT_FALSE(matcher.Matches(ConstString("ac"))); + EXPECT_FALSE(matcher.Matches(ConstString("a[a-z]c"))); + EXPECT_FALSE(matcher.Matches(ConstString("aAc"))); + EXPECT_FALSE(matcher.Matches(ConstString("ABC"))); + EXPECT_FALSE(matcher.Matches(ConstString(""))); +} + +// TypeMatcher that only searches the type name. +TEST(TypeMatcherTests, RegexMatchPart) { + TypeMatcher matcher(RegularExpression("a[a-z]c")); + EXPECT_TRUE(matcher.Matches(ConstString("class abc"))); + EXPECT_TRUE(matcher.Matches(ConstString("abc"))); + EXPECT_TRUE(matcher.Matches(ConstString(" abc "))); + EXPECT_TRUE(matcher.Matches(ConstString("azc"))); + EXPECT_TRUE(matcher.Matches(ConstString("abc "))); + EXPECT_TRUE(matcher.Matches(ConstString(" abc "))); + EXPECT_TRUE(matcher.Matches(ConstString(" abc"))); + EXPECT_TRUE(matcher.Matches(ConstString("XabcX"))); + + EXPECT_FALSE(matcher.Matches(ConstString("abbc"))); + EXPECT_FALSE(matcher.Matches(ConstString("ac"))); + EXPECT_FALSE(matcher.Matches(ConstString("a[a-z]c"))); + EXPECT_FALSE(matcher.Matches(ConstString("aAc"))); + EXPECT_FALSE(matcher.Matches(ConstString("ABC"))); + EXPECT_FALSE(matcher.Matches(ConstString(""))); +} + +// GetMatchString for exact type name matchers. +TEST(TypeMatcherTests, GetMatchStringExactName) { + EXPECT_EQ(TypeMatcher(ConstString("aa")).GetMatchString(), "aa"); + EXPECT_EQ(TypeMatcher(ConstString("")).GetMatchString(), ""); + EXPECT_EQ(TypeMatcher(ConstString("[a]")).GetMatchString(), "[a]"); +} + +// GetMatchString for regex matchers. +TEST(TypeMatcherTests, GetMatchStringRegex) { + EXPECT_EQ(TypeMatcher(RegularExpression("aa")).GetMatchString(), "aa"); + EXPECT_EQ(TypeMatcher(RegularExpression("")).GetMatchString(), ""); + EXPECT_EQ(TypeMatcher(RegularExpression("[a]")).GetMatchString(), "[a]"); +} + +// GetMatchString for regex matchers. +TEST(TypeMatcherTests, CreatedBySameMatchString) { + TypeMatcher empty_str(ConstString("")); + TypeMatcher empty_regex(RegularExpression("")); + EXPECT_TRUE(empty_str.CreatedBySameMatchString(empty_str)); + EXPECT_TRUE(empty_str.CreatedBySameMatchString(empty_regex)); + + TypeMatcher a_str(ConstString("a")); + TypeMatcher a_regex(RegularExpression("a")); + EXPECT_TRUE(a_str.CreatedBySameMatchString(a_str)); + EXPECT_TRUE(a_str.CreatedBySameMatchString(a_regex)); + + TypeMatcher digit_str(ConstString("[0-9]")); + TypeMatcher digit_regex(RegularExpression("[0-9]")); + EXPECT_TRUE(digit_str.CreatedBySameMatchString(digit_str)); + EXPECT_TRUE(digit_str.CreatedBySameMatchString(digit_regex)); + + EXPECT_FALSE(empty_str.CreatedBySameMatchString(a_str)); + EXPECT_FALSE(empty_str.CreatedBySameMatchString(a_regex)); + EXPECT_FALSE(empty_str.CreatedBySameMatchString(digit_str)); + EXPECT_FALSE(empty_str.CreatedBySameMatchString(digit_regex)); + + EXPECT_FALSE(empty_regex.CreatedBySameMatchString(a_str)); + EXPECT_FALSE(empty_regex.CreatedBySameMatchString(a_regex)); + EXPECT_FALSE(empty_regex.CreatedBySameMatchString(digit_str)); + EXPECT_FALSE(empty_regex.CreatedBySameMatchString(digit_regex)); + + EXPECT_FALSE(a_str.CreatedBySameMatchString(empty_str)); + EXPECT_FALSE(a_str.CreatedBySameMatchString(empty_regex)); + EXPECT_FALSE(a_str.CreatedBySameMatchString(digit_str)); + EXPECT_FALSE(a_str.CreatedBySameMatchString(digit_regex)); + + EXPECT_FALSE(a_regex.CreatedBySameMatchString(empty_str)); + EXPECT_FALSE(a_regex.CreatedBySameMatchString(empty_regex)); + EXPECT_FALSE(a_regex.CreatedBySameMatchString(digit_str)); + EXPECT_FALSE(a_regex.CreatedBySameMatchString(digit_regex)); + + EXPECT_FALSE(digit_str.CreatedBySameMatchString(empty_str)); + EXPECT_FALSE(digit_str.CreatedBySameMatchString(empty_regex)); + EXPECT_FALSE(digit_str.CreatedBySameMatchString(a_str)); + EXPECT_FALSE(digit_str.CreatedBySameMatchString(a_regex)); + + EXPECT_FALSE(digit_regex.CreatedBySameMatchString(empty_str)); + EXPECT_FALSE(digit_regex.CreatedBySameMatchString(empty_regex)); + EXPECT_FALSE(digit_regex.CreatedBySameMatchString(a_str)); + EXPECT_FALSE(digit_regex.CreatedBySameMatchString(a_regex)); +} + +// Test CreatedBySameMatchString with stripped exact name prefixes. +TEST(TypeMatcherTests, CreatedBySameMatchStringExactNamePrefixes) { + for (const std::string &prefix : exact_name_prefixes) { + SCOPED_TRACE("Prefix: " + prefix); + TypeMatcher with_prefix(ConstString(prefix + "Name")); + TypeMatcher without_prefix(RegularExpression("")); + + EXPECT_TRUE(with_prefix.CreatedBySameMatchString(with_prefix)); + EXPECT_TRUE(without_prefix.CreatedBySameMatchString(without_prefix)); + } +}