llvm-project/lldb/unittests/DataFormatter/FormattersContainerTest.cpp
Raphael Isemann 4d489e9f91 Reland [lldb] Unify type name matching in FormattersContainer II
This was originally reverted because the m_valid member in TypeMatcher was
unused in builds with disabled asserts. Now the member is gone and the default
constructor is deleted (thanks Eric for the idea!).

Summary:

FormattersContainer stores LLDB's formatters. It's implemented as a templated
map-like data structures that supports any kind of value type and only allows
ConstString and RegularExpression as the key types. The keys are used for
matching type names (e.g., the ConstString key `std::vector` matches the type
with the same name while RegularExpression keys match any type where the
RegularExpression instance matches).

The fact that a single FormattersContainer can only match either by string
comparison or regex matching (depending on the KeyType) causes us to always have
two FormatterContainer instances in all the formatting code. This also leads to
us having every type name matching logic in LLDB twice. For example,
TypeCategory has to implement every method twice (one string matching one, one
regex matching one).

This patch changes FormattersContainer to instead have a single `TypeMatcher`
key that wraps the logic for string-based and regex-based type matching and is
now the only possible KeyType for the FormattersContainer. This means that a
single FormattersContainer can now match types with both regex and string
comparison.

To summarize the changes in this patch:
* Remove all the `*_Impl` methods from `FormattersContainer`
* Instead call the FormatMap functions from `FormattersContainer` with a
  `TypeMatcher` type that does the respective matching.
* Replace `ConstString` with `TypeMatcher` in the few places that directly
  interact with `FormattersContainer`.

I'm working on some follow up patches that I split up because they deserve their
own review:

* Unify FormatMap and FormattersContainer (they are nearly identical now).
* Delete the duplicated half of all the type matching code that can now use one
  interface.
* Propagate TypeMatcher through all the formatter code interfaces instead of
  always offering two functions for everything.

There is one ugly design part that I couldn't get rid of yet and that is that we
have to support getting back the string used to construct a `TypeMatcher` later
on. The reason for this is that LLDB only supports referencing existing type
matchers by just typing their respective input string again (without even
supplying if it's a regex or not).

Reviewers: davide, mib

Reviewed By: mib

Subscribers: mgorny, JDevlieghere

Differential Revision: https://reviews.llvm.org/D84151
2020-07-23 18:17:42 +02:00

160 lines
6.8 KiB
C++

//===-- 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<std::string> 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));
}
}