https://github.com/Nerixyz created https://github.com/llvm/llvm-project/pull/143748
To be able to have multiple formatters that match on the same type name but for different types, this adds a callback that can be specified, which is called with the match candidate. As [mentlerd pointed out](https://discourse.llvm.org/t/formatting-msvc-stl-types/86667/12?u=nerixyz), it's similar to passing `--recognizer-function`. This isn't implemented as another match type, because the `TypeMatcher` still needs to have _some_ name for [`GetMatchString`](https://github.com/llvm/llvm-project/blob/f1575de4c5de9268f92eea1641af755a477e4ee4/lldb/include/lldb/DataFormatters/FormattersContainer.h#L120-L126). The only use I currently see here is for the C++ STL, specifically libstdc++ and MSVC's STL. In https://github.com/llvm/llvm-project/pull/143177, I used this to add formatters for `std::string` and friends from MSVC's STL. One potential issue of adding the callback here is that the summary/synthetic will be cached for a type name - disregarding the callback. The scenario here would be two shared libraries that use a different type with the same name and two formatters (e.g. they use two different standard libraries). Once the first type was formatted, its formatter is cached. Now, the second type will always be formatted with the wrong formatter (the cached one), even though the callback for the cached one wouldn't match. For one, `--recognizer-function` has the same issue and secondly, I don't think this is really realistic. >From d1a9078f0ae9f72874277ee7460742f5b51a199c Mon Sep 17 00:00:00 2001 From: Nerixyz <nerix...@outlook.de> Date: Wed, 11 Jun 2025 18:11:20 +0200 Subject: [PATCH] [LLDB] Add optional callback function to `TypeMatcher` --- .../lldb/DataFormatters/FormatClasses.h | 10 ++- .../lldb/DataFormatters/FormattersContainer.h | 58 +++++++++++------ .../DataFormatter/FormattersContainerTest.cpp | 64 +++++++++++++++++++ 3 files changed, 109 insertions(+), 23 deletions(-) diff --git a/lldb/include/lldb/DataFormatters/FormatClasses.h b/lldb/include/lldb/DataFormatters/FormatClasses.h index 89d74649ecc64..16685594db663 100644 --- a/lldb/include/lldb/DataFormatters/FormatClasses.h +++ b/lldb/include/lldb/DataFormatters/FormatClasses.h @@ -149,13 +149,16 @@ class FormattersMatchData { CandidateLanguagesVector m_candidate_languages; }; +using CxxTypeCandidateMatchFn = bool(const FormattersMatchCandidate &); + class TypeNameSpecifierImpl { public: TypeNameSpecifierImpl() = default; TypeNameSpecifierImpl(llvm::StringRef name, - lldb::FormatterMatchType match_type) - : m_match_type(match_type) { + lldb::FormatterMatchType match_type, + CxxTypeCandidateMatchFn *match_fn = nullptr) + : m_match_type(match_type), m_match_fn(match_fn) { m_type.m_type_name = std::string(name); } @@ -188,6 +191,8 @@ class TypeNameSpecifierImpl { return CompilerType(); } + CxxTypeCandidateMatchFn *GetCxxMatchFunction() const { return m_match_fn; } + lldb::FormatterMatchType GetMatchType() { return m_match_type; } bool IsRegex() { return m_match_type == lldb::eFormatterMatchRegex; } @@ -200,6 +205,7 @@ class TypeNameSpecifierImpl { CompilerType m_compiler_type; }; TypeOrName m_type; + CxxTypeCandidateMatchFn *m_match_fn = nullptr; TypeNameSpecifierImpl(const TypeNameSpecifierImpl &) = delete; const TypeNameSpecifierImpl & diff --git a/lldb/include/lldb/DataFormatters/FormattersContainer.h b/lldb/include/lldb/DataFormatters/FormattersContainer.h index f7465fc65538d..2cf33347b3c53 100644 --- a/lldb/include/lldb/DataFormatters/FormattersContainer.h +++ b/lldb/include/lldb/DataFormatters/FormattersContainer.h @@ -50,6 +50,11 @@ class TypeMatcher { /// - eFormatterMatchCallback: run the function in m_name to decide if a type /// matches or not. lldb::FormatterMatchType m_match_type; + /// An optional additional check for a FormattersMatchCandidate. + /// + /// If set, the name/regex is still checked first and this function will be + /// called if the name matches. + CxxTypeCandidateMatchFn *m_match_fn = nullptr; // 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 @@ -86,32 +91,19 @@ class TypeMatcher { /// name specifier. TypeMatcher(lldb::TypeNameSpecifierImplSP type_specifier) : m_name(type_specifier->GetName()), - m_match_type(type_specifier->GetMatchType()) { + m_match_type(type_specifier->GetMatchType()), + m_match_fn(type_specifier->GetCxxMatchFunction()) { if (m_match_type == lldb::eFormatterMatchRegex) m_type_name_regex = RegularExpression(type_specifier->GetName()); } /// True iff this matches the given type. bool Matches(FormattersMatchCandidate candidate_type) const { - ConstString type_name = candidate_type.GetTypeName(); - switch (m_match_type) { - case lldb::eFormatterMatchExact: - return m_name == type_name || - StripTypeName(m_name) == StripTypeName(type_name); - case lldb::eFormatterMatchRegex: - return m_type_name_regex.Execute(type_name.GetStringRef()); - case lldb::eFormatterMatchCallback: - // CommandObjectType{Synth,Filter}Add tries to prevent the user from - // creating both a synthetic child provider and a filter for the same type - // in the same category, but we don't have a type object at that point, so - // it creates a dummy candidate without type or script interpreter. - // Skip callback matching in these cases. - if (candidate_type.GetScriptInterpreter()) - return candidate_type.GetScriptInterpreter()->FormatterCallbackFunction( - m_name.AsCString(), - std::make_shared<TypeImpl>(candidate_type.GetType())); - } - return false; + if (!MatchesName(candidate_type)) + return false; + if (m_match_fn && !m_match_fn(candidate_type)) + return false; + return true; } lldb::FormatterMatchType GetMatchType() const { return m_match_type; } @@ -134,7 +126,31 @@ class TypeMatcher { /// (lldb) type summary add --summary-string \"A\" -x TypeName /// (lldb) type summary delete TypeName bool CreatedBySameMatchString(TypeMatcher other) const { - return GetMatchString() == other.GetMatchString(); + return GetMatchString() == other.GetMatchString() && + m_match_fn == other.m_match_fn; + } + +private: + bool MatchesName(FormattersMatchCandidate candidate_type) const { + ConstString type_name = candidate_type.GetTypeName(); + switch (m_match_type) { + case lldb::eFormatterMatchExact: + return m_name == type_name || + StripTypeName(m_name) == StripTypeName(type_name); + case lldb::eFormatterMatchRegex: + return m_type_name_regex.Execute(type_name.GetStringRef()); + case lldb::eFormatterMatchCallback: + // CommandObjectType{Synth,Filter}Add tries to prevent the user from + // creating both a synthetic child provider and a filter for the same type + // in the same category, but we don't have a type object at that point, so + // it creates a dummy candidate without type or script interpreter. + // Skip callback matching in these cases. + if (candidate_type.GetScriptInterpreter()) + return candidate_type.GetScriptInterpreter()->FormatterCallbackFunction( + m_name.AsCString(), + std::make_shared<TypeImpl>(candidate_type.GetType())); + } + return false; } }; diff --git a/lldb/unittests/DataFormatter/FormattersContainerTest.cpp b/lldb/unittests/DataFormatter/FormattersContainerTest.cpp index 41b01adfb9ecd..a8e9c1a1cccc0 100644 --- a/lldb/unittests/DataFormatter/FormattersContainerTest.cpp +++ b/lldb/unittests/DataFormatter/FormattersContainerTest.cpp @@ -165,3 +165,67 @@ TEST(TypeMatcherTests, CreatedBySameMatchStringExactNamePrefixes) { EXPECT_TRUE(without_prefix.CreatedBySameMatchString(without_prefix)); } } + +TEST(TypeMatcherTests, CxxTypeCandidateMatchTest) { + auto alwaysTrue = +[](const FormattersMatchCandidate &) { return true; }; + auto alwaysFalse = +[](const FormattersMatchCandidate &) { return false; }; + + TypeMatcher regex_matcher_true(std::make_shared<TypeNameSpecifierImpl>( + "^a[a-z]c$", eFormatterMatchRegex, alwaysTrue)); + EXPECT_TRUE(regex_matcher_true.Matches(CandidateFromTypeName("abc"))); + EXPECT_TRUE(regex_matcher_true.Matches(CandidateFromTypeName("azc"))); + EXPECT_FALSE(regex_matcher_true.Matches(CandidateFromTypeName("a"))); + + TypeMatcher regex_matcher_false(std::make_shared<TypeNameSpecifierImpl>( + "^a[a-z]c$", eFormatterMatchRegex, alwaysFalse)); + EXPECT_FALSE(regex_matcher_false.Matches(CandidateFromTypeName("abc"))); + EXPECT_FALSE(regex_matcher_false.Matches(CandidateFromTypeName("azc"))); + EXPECT_FALSE(regex_matcher_false.Matches(CandidateFromTypeName("a"))); + + TypeMatcher string_matcher_true(std::make_shared<TypeNameSpecifierImpl>( + "abc", eFormatterMatchExact, alwaysTrue)); + EXPECT_TRUE(string_matcher_true.Matches(CandidateFromTypeName("abc"))); + EXPECT_FALSE(string_matcher_true.Matches(CandidateFromTypeName("azc"))); + EXPECT_FALSE(string_matcher_true.Matches(CandidateFromTypeName("a"))); + + TypeMatcher string_matcher_false(std::make_shared<TypeNameSpecifierImpl>( + "abc", eFormatterMatchExact, alwaysFalse)); + EXPECT_FALSE(string_matcher_false.Matches(CandidateFromTypeName("abc"))); + EXPECT_FALSE(string_matcher_false.Matches(CandidateFromTypeName("azc"))); + EXPECT_FALSE(string_matcher_false.Matches(CandidateFromTypeName("a"))); + + EXPECT_TRUE(regex_matcher_true.CreatedBySameMatchString(regex_matcher_true)); + EXPECT_FALSE( + regex_matcher_true.CreatedBySameMatchString(regex_matcher_false)); + EXPECT_FALSE( + regex_matcher_true.CreatedBySameMatchString(string_matcher_true)); + EXPECT_FALSE( + regex_matcher_true.CreatedBySameMatchString(string_matcher_false)); + + EXPECT_FALSE( + regex_matcher_false.CreatedBySameMatchString(regex_matcher_true)); + EXPECT_TRUE( + regex_matcher_false.CreatedBySameMatchString(regex_matcher_false)); + EXPECT_FALSE( + regex_matcher_false.CreatedBySameMatchString(string_matcher_true)); + EXPECT_FALSE( + regex_matcher_false.CreatedBySameMatchString(string_matcher_false)); + + EXPECT_FALSE( + string_matcher_true.CreatedBySameMatchString(regex_matcher_true)); + EXPECT_FALSE( + string_matcher_true.CreatedBySameMatchString(regex_matcher_false)); + EXPECT_TRUE( + string_matcher_true.CreatedBySameMatchString(string_matcher_true)); + EXPECT_FALSE( + string_matcher_true.CreatedBySameMatchString(string_matcher_false)); + + EXPECT_FALSE( + string_matcher_false.CreatedBySameMatchString(regex_matcher_true)); + EXPECT_FALSE( + string_matcher_false.CreatedBySameMatchString(regex_matcher_false)); + EXPECT_FALSE( + string_matcher_false.CreatedBySameMatchString(string_matcher_true)); + EXPECT_TRUE( + string_matcher_false.CreatedBySameMatchString(string_matcher_false)); +} _______________________________________________ lldb-commits mailing list lldb-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits