[Lldb-commits] [lldb] d765664 - [lldb] Add matching based on Python callbacks for data formatters.

Jorge Gorbe Moya via lldb-commits lldb-commits at lists.llvm.org
Wed Oct 19 12:54:25 PDT 2022


Author: Jorge Gorbe Moya
Date: 2022-10-19T12:53:38-07:00
New Revision: d76566417e592cfac9c710f82575473b1b4a9285

URL: https://github.com/llvm/llvm-project/commit/d76566417e592cfac9c710f82575473b1b4a9285
DIFF: https://github.com/llvm/llvm-project/commit/d76566417e592cfac9c710f82575473b1b4a9285.diff

LOG: [lldb] Add matching based on Python callbacks for data formatters.

This patch adds a new matching method for data formatters, in addition
to the existing exact typename and regex-based matching. The new method
allows users to specify the name of a Python callback function that
takes a `SBType` object and decides whether the type is a match or not.

Here is an overview of the changes performed:

- Add a new `eFormatterMatchCallback` matching type, and logic to handle
  it in `TypeMatcher` and `SBTypeNameSpecifier`.

- Extend `FormattersMatchCandidate` instances with a pointer to the
  current `ScriptInterpreter` and the `TypeImpl` corresponding to the
  candidate type, so we can run registered callbacks and pass the type
  to them. All matcher search functions now receive a
  `FormattersMatchCandidate` instead of a type name.

- Add some glue code to ScriptInterpreterPython and the SWIG bindings to
  allow calling a formatter matching callback. Most of this code is
  modeled after the equivalent code for watchpoint callback functions.

- Add an API test for the new callback-based matching feature.

For more context, please check the RFC thread where this feature was
originally discussed:
https://discourse.llvm.org/t/rfc-python-callback-for-data-formatters-type-matching/64204/11

Differential Revision: https://reviews.llvm.org/D135648

Added: 
    lldb/test/API/functionalities/data-formatter/callback-matching/Makefile
    lldb/test/API/functionalities/data-formatter/callback-matching/TestDataFormatterCallbackMatching.py
    lldb/test/API/functionalities/data-formatter/callback-matching/formatters_with_callback.py
    lldb/test/API/functionalities/data-formatter/callback-matching/main.cpp

Modified: 
    lldb/bindings/python/python-swigsafecast.swig
    lldb/bindings/python/python-wrapper.swig
    lldb/include/lldb/API/SBType.h
    lldb/include/lldb/DataFormatters/DataVisualization.h
    lldb/include/lldb/DataFormatters/FormatClasses.h
    lldb/include/lldb/DataFormatters/FormatManager.h
    lldb/include/lldb/DataFormatters/FormattersContainer.h
    lldb/include/lldb/DataFormatters/TypeCategory.h
    lldb/include/lldb/DataFormatters/TypeCategoryMap.h
    lldb/include/lldb/Interpreter/ScriptInterpreter.h
    lldb/include/lldb/Target/Language.h
    lldb/include/lldb/lldb-enumerations.h
    lldb/source/API/SBTypeNameSpecifier.cpp
    lldb/source/Commands/CommandObjectType.cpp
    lldb/source/DataFormatters/DataVisualization.cpp
    lldb/source/DataFormatters/FormatManager.cpp
    lldb/source/DataFormatters/TypeCategory.cpp
    lldb/source/DataFormatters/TypeCategoryMap.cpp
    lldb/source/Plugins/Language/ObjC/ObjCLanguage.cpp
    lldb/source/Plugins/Language/ObjC/ObjCLanguage.h
    lldb/source/Plugins/ScriptInterpreter/Python/SWIGPythonBridge.h
    lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp
    lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPythonImpl.h
    lldb/source/Target/Language.cpp
    lldb/unittests/DataFormatter/FormattersContainerTest.cpp
    lldb/unittests/ScriptInterpreter/Python/PythonTestSuite.cpp

Removed: 
    


################################################################################
diff  --git a/lldb/bindings/python/python-swigsafecast.swig b/lldb/bindings/python/python-swigsafecast.swig
index eb684133abef..aa5e8e50a2d9 100644
--- a/lldb/bindings/python/python-swigsafecast.swig
+++ b/lldb/bindings/python/python-swigsafecast.swig
@@ -97,6 +97,10 @@ PythonObject ToSWIGWrapper(lldb::ExecutionContextRefSP ctx_sp) {
                       SWIGTYPE_p_lldb__SBExecutionContext);
 }
 
+PythonObject ToSWIGWrapper(lldb::TypeImplSP type_impl_sp) {
+  return ToSWIGHelper(new lldb::SBType(type_impl_sp), SWIGTYPE_p_lldb__SBType);
+}
+
 PythonObject ToSWIGWrapper(const TypeSummaryOptions &summary_options) {
   return ToSWIGHelper(new lldb::SBTypeSummaryOptions(summary_options),
                       SWIGTYPE_p_lldb__SBTypeSummaryOptions);

diff  --git a/lldb/bindings/python/python-wrapper.swig b/lldb/bindings/python/python-wrapper.swig
index 626fc47bebb9..adac8a405ab9 100644
--- a/lldb/bindings/python/python-wrapper.swig
+++ b/lldb/bindings/python/python-wrapper.swig
@@ -90,6 +90,32 @@ bool lldb_private::LLDBSwigPythonWatchpointCallbackFunction(
   return stop_at_watchpoint;
 }
 
+// This function is called by
+// ScriptInterpreterPython::FormatterMatchingCallbackFunction and it's used when
+// a data formatter provides the name of a callback to inspect a candidate type
+// before considering a match.
+bool lldb_private::LLDBSwigPythonFormatterCallbackFunction(
+    const char *python_function_name, const char *session_dictionary_name,
+    lldb::TypeImplSP type_impl_sp) {
+
+  PyErr_Cleaner py_err_cleaner(true);
+
+  auto dict = PythonModule::MainModule().ResolveName<PythonDictionary>(
+      session_dictionary_name);
+  auto pfunc = PythonObject::ResolveNameWithDictionary<PythonCallable>(
+      python_function_name, dict);
+
+  if (!pfunc.IsAllocated())
+    return false;
+
+  PythonObject result =
+      pfunc(ToSWIGWrapper(type_impl_sp), dict);
+
+  // Only if everything goes okay and the function returns True we'll consider
+  // it a match.
+  return result.get() == Py_True;
+}
+
 bool lldb_private::LLDBSwigPythonCallTypeScript(
     const char *python_function_name, const void *session_dictionary,
     const lldb::ValueObjectSP &valobj_sp, void **pyfunct_wrapper,

diff  --git a/lldb/include/lldb/API/SBType.h b/lldb/include/lldb/API/SBType.h
index aa45aeeec476..215e03fad99b 100644
--- a/lldb/include/lldb/API/SBType.h
+++ b/lldb/include/lldb/API/SBType.h
@@ -106,6 +106,7 @@ class SBType {
   SBType();
 
   SBType(const lldb::SBType &rhs);
+  SBType(const lldb::TypeImplSP &);
 
   ~SBType();
 
@@ -239,7 +240,6 @@ class SBType {
 
   SBType(const lldb_private::CompilerType &);
   SBType(const lldb::TypeSP &);
-  SBType(const lldb::TypeImplSP &);
 };
 
 class SBTypeList {

diff  --git a/lldb/include/lldb/DataFormatters/DataVisualization.h b/lldb/include/lldb/DataFormatters/DataVisualization.h
index 7be07d65acdd..5aea29132b8f 100644
--- a/lldb/include/lldb/DataFormatters/DataVisualization.h
+++ b/lldb/include/lldb/DataFormatters/DataVisualization.h
@@ -51,7 +51,7 @@ class DataVisualization {
   GetSyntheticChildren(ValueObject &valobj, lldb::DynamicValueType use_dynamic);
 
   static bool
-  AnyMatches(ConstString type_name,
+  AnyMatches(const FormattersMatchCandidate &candidate_type,
              TypeCategoryImpl::FormatCategoryItems items =
                  TypeCategoryImpl::ALL_ITEM_TYPES,
              bool only_enabled = true, const char **matching_category = nullptr,

diff  --git a/lldb/include/lldb/DataFormatters/FormatClasses.h b/lldb/include/lldb/DataFormatters/FormatClasses.h
index ac2b070a55cd..a6bc3a354253 100644
--- a/lldb/include/lldb/DataFormatters/FormatClasses.h
+++ b/lldb/include/lldb/DataFormatters/FormatClasses.h
@@ -17,6 +17,7 @@
 #include "lldb/DataFormatters/TypeFormat.h"
 #include "lldb/DataFormatters/TypeSummary.h"
 #include "lldb/DataFormatters/TypeSynthetic.h"
+#include "lldb/Interpreter/ScriptInterpreter.h"
 #include "lldb/Symbol/CompilerType.h"
 #include "lldb/Symbol/Type.h"
 #include "lldb/lldb-enumerations.h"
@@ -73,13 +74,22 @@ class FormattersMatchCandidate {
     }
   };
 
-  FormattersMatchCandidate(ConstString name, Flags flags)
-      : m_type_name(name), m_flags(flags) {}
+  FormattersMatchCandidate(ConstString name,
+                           ScriptInterpreter *script_interpreter, TypeImpl type,
+                           Flags flags)
+      : m_type_name(name), m_script_interpreter(script_interpreter),
+        m_type(type), m_flags(flags) {}
 
   ~FormattersMatchCandidate() = default;
 
   ConstString GetTypeName() const { return m_type_name; }
 
+  TypeImpl GetType() const { return m_type; }
+
+  ScriptInterpreter *GetScriptInterpreter() const {
+    return m_script_interpreter;
+  }
+
   bool DidStripPointer() const { return m_flags.stripped_pointer; }
 
   bool DidStripReference() const { return m_flags.stripped_reference; }
@@ -101,6 +111,10 @@ class FormattersMatchCandidate {
 
 private:
   ConstString m_type_name;
+  // If a formatter provides a matching callback function, we need the script
+  // interpreter and the type object (as an argument to the callback).
+  ScriptInterpreter *m_script_interpreter;
+  TypeImpl m_type;
   Flags m_flags;
 };
 

diff  --git a/lldb/include/lldb/DataFormatters/FormatManager.h b/lldb/include/lldb/DataFormatters/FormatManager.h
index 594addd1f083..295d3b84342a 100644
--- a/lldb/include/lldb/DataFormatters/FormatManager.h
+++ b/lldb/include/lldb/DataFormatters/FormatManager.h
@@ -128,12 +128,12 @@ class FormatManager : public IFormatChangeListener {
   GetSyntheticChildren(ValueObject &valobj, lldb::DynamicValueType use_dynamic);
 
   bool
-  AnyMatches(ConstString type_name,
+  AnyMatches(const FormattersMatchCandidate &candidate_type,
              TypeCategoryImpl::FormatCategoryItems items =
                  TypeCategoryImpl::ALL_ITEM_TYPES,
              bool only_enabled = true, const char **matching_category = nullptr,
              TypeCategoryImpl::FormatCategoryItems *matching_type = nullptr) {
-    return m_categories_map.AnyMatches(type_name, items, only_enabled,
+    return m_categories_map.AnyMatches(candidate_type, items, only_enabled,
                                        matching_category, matching_type);
   }
 

diff  --git a/lldb/include/lldb/DataFormatters/FormattersContainer.h b/lldb/include/lldb/DataFormatters/FormattersContainer.h
index 8a93c0345cbe..fd046e773b69 100644
--- a/lldb/include/lldb/DataFormatters/FormattersContainer.h
+++ b/lldb/include/lldb/DataFormatters/FormattersContainer.h
@@ -39,12 +39,16 @@ class IFormatChangeListener {
 
 /// Class for matching type names.
 class TypeMatcher {
+  /// Type name for exact match, or name of the python callback if m_match_type
+  /// is `eFormatterMatchCallback`.
+  ConstString m_name;
   RegularExpression m_type_name_regex;
-  ConstString m_type_name;
   /// Indicates what kind of matching strategy should be used:
-  /// - eFormatterMatchExact: match the exact type name in m_type_name.
+  /// - eFormatterMatchExact: match the exact type name in m_name.
   /// - eFormatterMatchRegex: match using the RegularExpression object
   ///   `m_type_name_regex` instead.
+  /// - eFormatterMatchCallback: run the function in m_name to decide if a type
+  ///   matches or not.
   lldb::FormatterMatchType m_match_type;
 
   // if the user tries to add formatters for, say, "struct Foo" those will not
@@ -73,7 +77,7 @@ class TypeMatcher {
   TypeMatcher() = delete;
   /// Creates a matcher that accepts any type with exactly the given type name.
   TypeMatcher(ConstString type_name)
-      : m_type_name(type_name), m_match_type(lldb::eFormatterMatchExact) {}
+      : m_name(type_name), m_match_type(lldb::eFormatterMatchExact) {}
   /// Creates a matcher that accepts any type matching the given regex.
   TypeMatcher(RegularExpression regex)
       : m_type_name_regex(std::move(regex)),
@@ -81,27 +85,44 @@ class TypeMatcher {
   /// Creates a matcher using the matching type and string from the given type
   /// name specifier.
   TypeMatcher(lldb::TypeNameSpecifierImplSP type_specifier)
-      : m_type_name(type_specifier->GetName()),
+      : m_name(type_specifier->GetName()),
         m_match_type(type_specifier->GetMatchType()) {
     if (m_match_type == lldb::eFormatterMatchRegex)
       m_type_name_regex = RegularExpression(type_specifier->GetName());
   }
 
-  /// True iff this matches the given type name.
-  bool Matches(ConstString type_name) const {
-    if (m_match_type == lldb::eFormatterMatchRegex)
+  /// 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());
-    return m_type_name == type_name ||
-           StripTypeName(m_type_name) == StripTypeName(type_name);
+    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;
   }
 
   lldb::FormatterMatchType GetMatchType() const { return m_match_type; }
 
   /// Returns the underlying match string for this TypeMatcher.
   ConstString GetMatchString() const {
+    if (m_match_type == lldb::eFormatterMatchExact)
+        return StripTypeName(m_name);
     if (m_match_type == lldb::eFormatterMatchRegex)
-      return ConstString(m_type_name_regex.GetText());
-    return StripTypeName(m_type_name);
+        return ConstString(m_type_name_regex.GetText());
+    return m_name;
   }
 
   /// Returns true if this TypeMatcher and the given one were most created by
@@ -155,10 +176,11 @@ template <typename ValueType> class FormattersContainer {
     return false;
   }
 
-  bool Get(ConstString type, ValueSP &entry) {
+  // Finds the first formatter in the container that matches `candidate`.
+  bool Get(FormattersMatchCandidate candidate, ValueSP &entry) {
     std::lock_guard<std::recursive_mutex> guard(m_map_mutex);
     for (auto &formatter : llvm::reverse(m_map)) {
-      if (formatter.first.Matches(type)) {
+      if (formatter.first.Matches(candidate)) {
         entry = formatter.second;
         return true;
       }
@@ -166,9 +188,11 @@ template <typename ValueType> class FormattersContainer {
     return false;
   }
 
+  // Finds the first match between candidate types in `candidates` and
+  // formatters in this container.
   bool Get(const FormattersMatchVector &candidates, ValueSP &entry) {
     for (const FormattersMatchCandidate &candidate : candidates) {
-      if (Get(candidate.GetTypeName(), entry)) {
+      if (Get(candidate, entry)) {
         if (candidate.IsMatch(entry) == false) {
           entry.reset();
           continue;

diff  --git a/lldb/include/lldb/DataFormatters/TypeCategory.h b/lldb/include/lldb/DataFormatters/TypeCategory.h
index bad39aa676af..884a27d76e05 100644
--- a/lldb/include/lldb/DataFormatters/TypeCategory.h
+++ b/lldb/include/lldb/DataFormatters/TypeCategory.h
@@ -105,10 +105,10 @@ template <typename FormatterImpl> class TieredFormatterContainer {
     return false;
   }
 
-  bool AnyMatches(ConstString type_name) {
+  bool AnyMatches(const FormattersMatchCandidate &candidate) {
     std::shared_ptr<FormatterImpl> entry;
     for (auto sc : m_subcontainers) {
-      if (sc->Get(type_name, entry))
+      if (sc->Get(FormattersMatchVector{candidate}, entry))
         return true;
     }
     return false;
@@ -346,7 +346,7 @@ class TypeCategoryImpl {
 
   std::string GetDescription();
 
-  bool AnyMatches(ConstString type_name,
+  bool AnyMatches(const FormattersMatchCandidate &candidate_type,
                   FormatCategoryItems items = ALL_ITEM_TYPES,
                   bool only_enabled = true,
                   const char **matching_category = nullptr,

diff  --git a/lldb/include/lldb/DataFormatters/TypeCategoryMap.h b/lldb/include/lldb/DataFormatters/TypeCategoryMap.h
index 45dbb306aa75..d4f7634c90b0 100644
--- a/lldb/include/lldb/DataFormatters/TypeCategoryMap.h
+++ b/lldb/include/lldb/DataFormatters/TypeCategoryMap.h
@@ -17,6 +17,7 @@
 #include "lldb/lldb-enumerations.h"
 #include "lldb/lldb-public.h"
 
+#include "lldb/DataFormatters/FormatClasses.h"
 #include "lldb/DataFormatters/FormattersContainer.h"
 #include "lldb/DataFormatters/TypeCategory.h"
 
@@ -69,7 +70,7 @@ class TypeCategoryMap {
   lldb::TypeCategoryImplSP GetAtIndex(uint32_t);
 
   bool
-  AnyMatches(ConstString type_name,
+  AnyMatches(const FormattersMatchCandidate &candidate_type,
              TypeCategoryImpl::FormatCategoryItems items =
                  TypeCategoryImpl::ALL_ITEM_TYPES,
              bool only_enabled = true, const char **matching_category = nullptr,

diff  --git a/lldb/include/lldb/Interpreter/ScriptInterpreter.h b/lldb/include/lldb/Interpreter/ScriptInterpreter.h
index cb3cafaf2ed5..f34ce43e946e 100644
--- a/lldb/include/lldb/Interpreter/ScriptInterpreter.h
+++ b/lldb/include/lldb/Interpreter/ScriptInterpreter.h
@@ -418,6 +418,14 @@ class ScriptInterpreter : public PluginInterface {
     return false;
   }
 
+  // Calls the specified formatter matching Python function and returns its
+  // result (true if it's a match, false if we should keep looking for a
+  // matching formatter).
+  virtual bool FormatterCallbackFunction(const char *function_name,
+                                         lldb::TypeImplSP type_impl_sp) {
+    return true;
+  }
+
   virtual void Clear() {
     // Clean up any ref counts to SBObjects that might be in global variables
   }

diff  --git a/lldb/include/lldb/Target/Language.h b/lldb/include/lldb/Target/Language.h
index fa79aaee0574..89136cc5e0ff 100644
--- a/lldb/include/lldb/Target/Language.h
+++ b/lldb/include/lldb/Target/Language.h
@@ -175,7 +175,7 @@ class Language : public PluginInterface {
   virtual HardcodedFormatters::HardcodedSyntheticFinder
   GetHardcodedSynthetics();
 
-  virtual std::vector<ConstString>
+  virtual std::vector<FormattersMatchCandidate>
   GetPossibleFormattersMatches(ValueObject &valobj,
                                lldb::DynamicValueType use_dynamic);
 

diff  --git a/lldb/include/lldb/lldb-enumerations.h b/lldb/include/lldb/lldb-enumerations.h
index 2ac1a74214b4..3ba29a301382 100644
--- a/lldb/include/lldb/lldb-enumerations.h
+++ b/lldb/include/lldb/lldb-enumerations.h
@@ -835,8 +835,9 @@ enum TemplateArgumentKind {
 enum FormatterMatchType {
   eFormatterMatchExact,
   eFormatterMatchRegex,
+  eFormatterMatchCallback,
 
-  eLastFormatterMatchType = eFormatterMatchRegex,
+  eLastFormatterMatchType = eFormatterMatchCallback,
 };
 
 /// Options that can be set for a formatter to alter its behavior. Not

diff  --git a/lldb/source/API/SBTypeNameSpecifier.cpp b/lldb/source/API/SBTypeNameSpecifier.cpp
index d1dc2953c9b9..8a6eb086a9b1 100644
--- a/lldb/source/API/SBTypeNameSpecifier.cpp
+++ b/lldb/source/API/SBTypeNameSpecifier.cpp
@@ -99,10 +99,14 @@ bool SBTypeNameSpecifier::GetDescription(
     lldb::SBStream &description, lldb::DescriptionLevel description_level) {
   LLDB_INSTRUMENT_VA(this, description, description_level);
 
+  lldb::FormatterMatchType match_type = GetMatchType();
+  const char *match_type_str =
+      (match_type == eFormatterMatchExact   ? "plain"
+       : match_type == eFormatterMatchRegex ? "regex"
+                                            : "callback");
   if (!IsValid())
     return false;
-  description.Printf("SBTypeNameSpecifier(%s,%s)", GetName(),
-                     IsRegex() ? "regex" : "plain");
+  description.Printf("SBTypeNameSpecifier(%s,%s)", GetName(), match_type_str);
   return true;
 }
 

diff  --git a/lldb/source/Commands/CommandObjectType.cpp b/lldb/source/Commands/CommandObjectType.cpp
index 63d3c6979ec3..ccbe7922e65f 100644
--- a/lldb/source/Commands/CommandObjectType.cpp
+++ b/lldb/source/Commands/CommandObjectType.cpp
@@ -11,6 +11,7 @@
 #include "lldb/Core/Debugger.h"
 #include "lldb/Core/IOHandler.h"
 #include "lldb/DataFormatters/DataVisualization.h"
+#include "lldb/DataFormatters/FormatClasses.h"
 #include "lldb/Host/Config.h"
 #include "lldb/Host/OptionParser.h"
 #include "lldb/Interpreter/CommandInterpreter.h"
@@ -2302,7 +2303,13 @@ bool CommandObjectTypeSynthAdd::AddSynth(ConstString type_name,
   // an actual type name. Matching a regex string against registered regexes
   // doesn't work.
   if (type == eRegularSynth) {
-    if (category->AnyMatches(type_name, eFormatCategoryItemFilter, false)) {
+    // It's not generally possible to get a type object here. For example, this
+    // command can be run before loading any binaries. Do just a best-effort
+    // name-based lookup here to try to prevent conflicts.
+    FormattersMatchCandidate candidate_type(type_name, nullptr, TypeImpl(),
+                                            FormattersMatchCandidate::Flags());
+    if (category->AnyMatches(candidate_type, eFormatCategoryItemFilter,
+                             false)) {
       if (error)
         error->SetErrorStringWithFormat("cannot add synthetic for type %s when "
                                         "filter is defined in same category!",
@@ -2427,7 +2434,14 @@ class CommandObjectTypeFilterAdd : public CommandObjectParsed {
     // if `type_name` is an actual type name. Matching a regex string against
     // registered regexes doesn't work.
     if (type == eRegularFilter) {
-      if (category->AnyMatches(type_name, eFormatCategoryItemSynth, false)) {
+      // It's not generally possible to get a type object here. For example,
+      // this command can be run before loading any binaries. Do just a
+      // best-effort name-based lookup here to try to prevent conflicts.
+      FormattersMatchCandidate candidate_type(
+          type_name, nullptr, TypeImpl(), FormattersMatchCandidate::Flags());
+      lldb::SyntheticChildrenSP entry;
+      if (category->AnyMatches(candidate_type, eFormatCategoryItemSynth,
+                               false)) {
         if (error)
           error->SetErrorStringWithFormat("cannot add filter for type %s when "
                                           "synthetic is defined in same "

diff  --git a/lldb/source/DataFormatters/DataVisualization.cpp b/lldb/source/DataFormatters/DataVisualization.cpp
index 53832492aa25..036c9372baf8 100644
--- a/lldb/source/DataFormatters/DataVisualization.cpp
+++ b/lldb/source/DataFormatters/DataVisualization.cpp
@@ -66,10 +66,11 @@ DataVisualization::GetSyntheticForType(lldb::TypeNameSpecifierImplSP type_sp) {
 }
 
 bool DataVisualization::AnyMatches(
-    ConstString type_name, TypeCategoryImpl::FormatCategoryItems items,
-    bool only_enabled, const char **matching_category,
+    const FormattersMatchCandidate &candidate_type,
+    TypeCategoryImpl::FormatCategoryItems items, bool only_enabled,
+    const char **matching_category,
     TypeCategoryImpl::FormatCategoryItems *matching_type) {
-  return GetFormatManager().AnyMatches(type_name, items, only_enabled,
+  return GetFormatManager().AnyMatches(candidate_type, items, only_enabled,
                                        matching_category, matching_type);
 }
 

diff  --git a/lldb/source/DataFormatters/FormatManager.cpp b/lldb/source/DataFormatters/FormatManager.cpp
index db9f6057e842..166264df9933 100644
--- a/lldb/source/DataFormatters/FormatManager.cpp
+++ b/lldb/source/DataFormatters/FormatManager.cpp
@@ -11,6 +11,7 @@
 #include "lldb/Core/Debugger.h"
 #include "lldb/DataFormatters/FormattersHelpers.h"
 #include "lldb/DataFormatters/LanguageCategory.h"
+#include "lldb/Interpreter/ScriptInterpreter.h"
 #include "lldb/Target/ExecutionContext.h"
 #include "lldb/Target/Language.h"
 #include "lldb/Utility/LLDBLog.h"
@@ -178,19 +179,24 @@ void FormatManager::GetPossibleMatches(
     FormattersMatchCandidate::Flags current_flags, bool root_level) {
   compiler_type = compiler_type.GetTypeForFormatters();
   ConstString type_name(compiler_type.GetTypeName());
+  ScriptInterpreter *script_interpreter =
+      valobj.GetTargetSP()->GetDebugger().GetScriptInterpreter();
   if (valobj.GetBitfieldBitSize() > 0) {
     StreamString sstring;
     sstring.Printf("%s:%d", type_name.AsCString(), valobj.GetBitfieldBitSize());
     ConstString bitfieldname(sstring.GetString());
-    entries.push_back({bitfieldname, current_flags});
+    entries.push_back({bitfieldname, script_interpreter,
+                       TypeImpl(compiler_type), current_flags});
   }
 
   if (!compiler_type.IsMeaninglessWithoutDynamicResolution()) {
-    entries.push_back({type_name, current_flags});
+    entries.push_back({type_name, script_interpreter, TypeImpl(compiler_type),
+                       current_flags});
 
     ConstString display_type_name(compiler_type.GetTypeName());
     if (display_type_name != type_name)
-      entries.push_back({display_type_name, current_flags});
+      entries.push_back({display_type_name, script_interpreter,
+                         TypeImpl(compiler_type), current_flags});
   }
 
   for (bool is_rvalue_ref = true, j = true;
@@ -245,9 +251,9 @@ void FormatManager::GetPossibleMatches(
   for (lldb::LanguageType language_type :
        GetCandidateLanguages(valobj.GetObjectRuntimeLanguage())) {
     if (Language *language = Language::FindPlugin(language_type)) {
-      for (ConstString candidate :
+      for (const FormattersMatchCandidate& candidate :
            language->GetPossibleFormattersMatches(valobj, use_dynamic)) {
-        entries.push_back({candidate, current_flags});
+        entries.push_back(candidate);
       }
     }
   }

diff  --git a/lldb/source/DataFormatters/TypeCategory.cpp b/lldb/source/DataFormatters/TypeCategory.cpp
index 1d5674968245..4d8663ea9c03 100644
--- a/lldb/source/DataFormatters/TypeCategory.cpp
+++ b/lldb/source/DataFormatters/TypeCategory.cpp
@@ -184,20 +184,15 @@ uint32_t TypeCategoryImpl::GetCount(FormatCategoryItems items) {
   return count;
 }
 
-bool TypeCategoryImpl::AnyMatches(ConstString type_name,
-                                  FormatCategoryItems items, bool only_enabled,
-                                  const char **matching_category,
-                                  FormatCategoryItems *matching_type) {
+bool TypeCategoryImpl::AnyMatches(
+    const FormattersMatchCandidate &candidate_type, FormatCategoryItems items,
+    bool only_enabled, const char **matching_category,
+    FormatCategoryItems *matching_type) {
   if (!IsEnabled() && only_enabled)
     return false;
 
-  lldb::TypeFormatImplSP format_sp;
-  lldb::TypeSummaryImplSP summary_sp;
-  TypeFilterImpl::SharedPointer filter_sp;
-  ScriptedSyntheticChildren::SharedPointer synth_sp;
-
   if (items & eFormatCategoryItemFormat) {
-    if (m_format_cont.AnyMatches(type_name)) {
+    if (m_format_cont.AnyMatches(candidate_type)) {
       if (matching_category)
         *matching_category = m_name.GetCString();
       if (matching_type)
@@ -207,7 +202,7 @@ bool TypeCategoryImpl::AnyMatches(ConstString type_name,
   }
 
   if (items & eFormatCategoryItemSummary) {
-    if (m_summary_cont.AnyMatches(type_name)) {
+    if (m_summary_cont.AnyMatches(candidate_type)) {
       if (matching_category)
         *matching_category = m_name.GetCString();
       if (matching_type)
@@ -217,7 +212,7 @@ bool TypeCategoryImpl::AnyMatches(ConstString type_name,
   }
 
   if (items & eFormatCategoryItemFilter) {
-    if (m_filter_cont.AnyMatches(type_name)) {
+    if (m_filter_cont.AnyMatches(candidate_type)) {
       if (matching_category)
         *matching_category = m_name.GetCString();
       if (matching_type)
@@ -227,7 +222,7 @@ bool TypeCategoryImpl::AnyMatches(ConstString type_name,
   }
 
   if (items & eFormatCategoryItemSynth) {
-    if (m_synth_cont.AnyMatches(type_name)) {
+    if (m_synth_cont.AnyMatches(candidate_type)) {
       if (matching_category)
         *matching_category = m_name.GetCString();
       if (matching_type)

diff  --git a/lldb/source/DataFormatters/TypeCategoryMap.cpp b/lldb/source/DataFormatters/TypeCategoryMap.cpp
index aa8387b4deec..55635173cc8c 100644
--- a/lldb/source/DataFormatters/TypeCategoryMap.cpp
+++ b/lldb/source/DataFormatters/TypeCategoryMap.cpp
@@ -154,14 +154,15 @@ bool TypeCategoryMap::Get(uint32_t pos, ValueSP &entry) {
 }
 
 bool TypeCategoryMap::AnyMatches(
-    ConstString type_name, TypeCategoryImpl::FormatCategoryItems items,
-    bool only_enabled, const char **matching_category,
+    const FormattersMatchCandidate &candidate_type,
+    TypeCategoryImpl::FormatCategoryItems items, bool only_enabled,
+    const char **matching_category,
     TypeCategoryImpl::FormatCategoryItems *matching_type) {
   std::lock_guard<std::recursive_mutex> guard(m_map_mutex);
 
   MapIterator pos, end = m_map.end();
   for (pos = m_map.begin(); pos != end; pos++) {
-    if (pos->second->AnyMatches(type_name, items, only_enabled,
+    if (pos->second->AnyMatches(candidate_type, items, only_enabled,
                                 matching_category, matching_type))
       return true;
   }

diff  --git a/lldb/source/Plugins/Language/ObjC/ObjCLanguage.cpp b/lldb/source/Plugins/Language/ObjC/ObjCLanguage.cpp
index 11d5b0813b58..9cbb40419167 100644
--- a/lldb/source/Plugins/Language/ObjC/ObjCLanguage.cpp
+++ b/lldb/source/Plugins/Language/ObjC/ObjCLanguage.cpp
@@ -12,6 +12,7 @@
 
 #include "Plugins/ExpressionParser/Clang/ClangUtil.h"
 #include "Plugins/TypeSystem/Clang/TypeSystemClang.h"
+#include "lldb/Core/Debugger.h"
 #include "lldb/Core/PluginManager.h"
 #include "lldb/Core/ValueObject.h"
 #include "lldb/DataFormatters/DataVisualization.h"
@@ -931,10 +932,10 @@ lldb::TypeCategoryImplSP ObjCLanguage::GetFormatters() {
   return g_category;
 }
 
-std::vector<ConstString>
+std::vector<FormattersMatchCandidate>
 ObjCLanguage::GetPossibleFormattersMatches(ValueObject &valobj,
                                            lldb::DynamicValueType use_dynamic) {
-  std::vector<ConstString> result;
+  std::vector<FormattersMatchCandidate> result;
 
   if (use_dynamic == lldb::eNoDynamicValues)
     return result;
@@ -959,7 +960,10 @@ ObjCLanguage::GetPossibleFormattersMatches(ValueObject &valobj,
       if (!objc_class_sp)
         break;
       if (ConstString name = objc_class_sp->GetClassName())
-        result.push_back(name);
+        result.push_back(
+            {name, valobj.GetTargetSP()->GetDebugger().GetScriptInterpreter(),
+             TypeImpl(objc_class_sp->GetType()),
+             FormattersMatchCandidate::Flags{}});
     } while (false);
   }
 

diff  --git a/lldb/source/Plugins/Language/ObjC/ObjCLanguage.h b/lldb/source/Plugins/Language/ObjC/ObjCLanguage.h
index 914452086db7..b61348a3280e 100644
--- a/lldb/source/Plugins/Language/ObjC/ObjCLanguage.h
+++ b/lldb/source/Plugins/Language/ObjC/ObjCLanguage.h
@@ -108,7 +108,7 @@ class ObjCLanguage : public Language {
 
   lldb::TypeCategoryImplSP GetFormatters() override;
 
-  std::vector<ConstString>
+  std::vector<FormattersMatchCandidate>
   GetPossibleFormattersMatches(ValueObject &valobj,
                                lldb::DynamicValueType use_dynamic) override;
 

diff  --git a/lldb/source/Plugins/ScriptInterpreter/Python/SWIGPythonBridge.h b/lldb/source/Plugins/ScriptInterpreter/Python/SWIGPythonBridge.h
index 4df235356737..7e18b0ef0804 100644
--- a/lldb/source/Plugins/ScriptInterpreter/Python/SWIGPythonBridge.h
+++ b/lldb/source/Plugins/ScriptInterpreter/Python/SWIGPythonBridge.h
@@ -75,6 +75,10 @@ bool LLDBSwigPythonWatchpointCallbackFunction(
     const char *python_function_name, const char *session_dictionary_name,
     const lldb::StackFrameSP &sb_frame, const lldb::WatchpointSP &sb_wp);
 
+bool LLDBSwigPythonFormatterCallbackFunction(
+    const char *python_function_name, const char *session_dictionary_name,
+    lldb::TypeImplSP type_impl_sp);
+
 bool LLDBSwigPythonCallTypeScript(const char *python_function_name,
                                   const void *session_dictionary,
                                   const lldb::ValueObjectSP &valobj_sp,

diff  --git a/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp b/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp
index d0f67a5684c5..37e3c94df870 100644
--- a/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp
+++ b/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp
@@ -2154,6 +2154,14 @@ bool ScriptInterpreterPythonImpl::GetScriptedSummary(
   return ret_val;
 }
 
+bool ScriptInterpreterPythonImpl::FormatterCallbackFunction(
+    const char *python_function_name, TypeImplSP type_impl_sp) {
+  Locker py_lock(this,
+                 Locker::AcquireLock | Locker::InitSession | Locker::NoSTDIN);
+  return LLDBSwigPythonFormatterCallbackFunction(
+      python_function_name, m_dictionary_name.c_str(), type_impl_sp);
+}
+
 bool ScriptInterpreterPythonImpl::BreakpointCallbackFunction(
     void *baton, StoppointCallbackContext *context, user_id_t break_id,
     user_id_t break_loc_id) {

diff  --git a/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPythonImpl.h b/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPythonImpl.h
index 3b80c67d201a..f4875bfb8d18 100644
--- a/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPythonImpl.h
+++ b/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPythonImpl.h
@@ -202,6 +202,9 @@ class ScriptInterpreterPythonImpl : public ScriptInterpreterPython {
                           const TypeSummaryOptions &options,
                           std::string &retval) override;
 
+  bool FormatterCallbackFunction(const char *function_name,
+                                 lldb::TypeImplSP type_impl_sp) override;
+
   bool GetDocumentationForItem(const char *item, std::string &dest) override;
 
   bool GetShortHelpForCommandObject(StructuredData::GenericSP cmd_obj_sp,

diff  --git a/lldb/source/Target/Language.cpp b/lldb/source/Target/Language.cpp
index 6df36aeeb7b7..92431005cba9 100644
--- a/lldb/source/Target/Language.cpp
+++ b/lldb/source/Target/Language.cpp
@@ -144,7 +144,7 @@ Language::GetHardcodedSynthetics() {
   return {};
 }
 
-std::vector<ConstString>
+std::vector<FormattersMatchCandidate>
 Language::GetPossibleFormattersMatches(ValueObject &valobj,
                                        lldb::DynamicValueType use_dynamic) {
   return {};

diff  --git a/lldb/test/API/functionalities/data-formatter/callback-matching/Makefile b/lldb/test/API/functionalities/data-formatter/callback-matching/Makefile
new file mode 100644
index 000000000000..99998b20bcb0
--- /dev/null
+++ b/lldb/test/API/functionalities/data-formatter/callback-matching/Makefile
@@ -0,0 +1,3 @@
+CXX_SOURCES := main.cpp
+
+include Makefile.rules

diff  --git a/lldb/test/API/functionalities/data-formatter/callback-matching/TestDataFormatterCallbackMatching.py b/lldb/test/API/functionalities/data-formatter/callback-matching/TestDataFormatterCallbackMatching.py
new file mode 100644
index 000000000000..91403d6d5f16
--- /dev/null
+++ b/lldb/test/API/functionalities/data-formatter/callback-matching/TestDataFormatterCallbackMatching.py
@@ -0,0 +1,49 @@
+"""
+Test lldb data formatter callback-based matching.
+"""
+
+import lldb
+from lldbsuite.test.decorators import *
+from lldbsuite.test.lldbtest import *
+from lldbsuite.test import lldbutil
+
+
+class PythonSynthDataFormatterTestCase(TestBase):
+
+    def setUp(self):
+        # Call super's setUp().
+        TestBase.setUp(self)
+        # Find the line number to break at.
+        self.line = line_number('main.cpp', '// Set break point at this line.')
+
+    def test_callback_matchers(self):
+        """Test data formatter commands."""
+        self.build()
+
+        _, process, thread, _ = lldbutil.run_to_line_breakpoint(
+            self, lldb.SBFileSpec("main.cpp"), self.line)
+
+        # Print derived without a formatter.
+        self.expect("frame variable derived",
+                    substrs=['x = 2222',
+                             'y = 3333'])
+
+        # now set up a summary function that uses a python callback to match
+        # classes that derive from `Base`.
+        self.runCmd("command script import --allow-reload ./formatters_with_callback.py")
+
+        # Now `derived` should use our callback summary + synthetic children.
+        self.expect("frame variable derived",
+                    substrs=['hello from callback summary',
+                             'synthetic_child = 9999'])
+
+        # But not other classes.
+        self.expect("frame variable base", matching=False,
+                    substrs=['hello from callback summary'])
+        self.expect("frame variable base",
+                    substrs=['x = 1111'])
+
+        self.expect("frame variable nd", matching=False,
+                    substrs=['hello from callback summary'])
+        self.expect("frame variable nd",
+                    substrs=['z = 4444'])

diff  --git a/lldb/test/API/functionalities/data-formatter/callback-matching/formatters_with_callback.py b/lldb/test/API/functionalities/data-formatter/callback-matching/formatters_with_callback.py
new file mode 100644
index 000000000000..60e919a94352
--- /dev/null
+++ b/lldb/test/API/functionalities/data-formatter/callback-matching/formatters_with_callback.py
@@ -0,0 +1,39 @@
+import lldb
+
+def derives_from_base(sbtype, internal_dict):
+    for base in sbtype.get_bases_array():
+        if base.GetName() == "Base":
+            return True
+    return False
+
+
+class SynthProvider:
+    def __init__(self, valobj, dict):
+        self.valobj = valobj
+
+    def num_children(self):
+        return 1
+
+    def get_child_index(self, name):
+        return 0
+
+    def get_child_at_index(self, index):
+        if index == 0:
+            return self.valobj.CreateValueFromExpression("synthetic_child",
+                                                         "9999")
+        return None
+
+
+def __lldb_init_module(debugger, dict):
+    cat = debugger.CreateCategory("callback_formatters")
+    cat.AddTypeSummary(
+        lldb.SBTypeNameSpecifier("formatters_with_callback.derives_from_base",
+                                 lldb.eFormatterMatchCallback),
+        lldb.SBTypeSummary.CreateWithScriptCode(
+            "return 'hello from callback summary'"))
+    cat.AddTypeSynthetic(
+        lldb.SBTypeNameSpecifier('formatters_with_callback.derives_from_base',
+                                 lldb.eFormatterMatchCallback),
+        lldb.SBTypeSynthetic.CreateWithClassName(
+            'formatters_with_callback.SynthProvider'))
+    cat.SetEnabled(True)

diff  --git a/lldb/test/API/functionalities/data-formatter/callback-matching/main.cpp b/lldb/test/API/functionalities/data-formatter/callback-matching/main.cpp
new file mode 100644
index 000000000000..7732d87342a9
--- /dev/null
+++ b/lldb/test/API/functionalities/data-formatter/callback-matching/main.cpp
@@ -0,0 +1,16 @@
+struct Base { int x; };
+struct Derived : public Base { int y; };
+
+struct NonDerived { int z; };
+
+int main()
+{
+    Base base = {1111};
+
+    Derived derived;
+    derived.x = 2222;
+    derived.y = 3333;
+
+    NonDerived nd = {4444};
+    return 0;     // Set break point at this line.
+}

diff  --git a/lldb/unittests/DataFormatter/FormattersContainerTest.cpp b/lldb/unittests/DataFormatter/FormattersContainerTest.cpp
index a28212391eae..41b01adfb9ec 100644
--- a/lldb/unittests/DataFormatter/FormattersContainerTest.cpp
+++ b/lldb/unittests/DataFormatter/FormattersContainerTest.cpp
@@ -7,12 +7,20 @@
 //===----------------------------------------------------------------------===//
 
 #include "lldb/DataFormatters/FormattersContainer.h"
+#include "lldb/DataFormatters/FormatClasses.h"
 
 #include "gtest/gtest.h"
 
 using namespace lldb;
 using namespace lldb_private;
 
+// Creates a dummy candidate with just a type name in order to test the string
+// matching (exact name match and regex match) paths.
+FormattersMatchCandidate CandidateFromTypeName(const char *type_name) {
+  return FormattersMatchCandidate(ConstString(type_name), nullptr, TypeImpl(),
+                                  FormattersMatchCandidate::Flags());
+}
+
 // All the prefixes that the exact name matching will strip from the type.
 static const std::vector<std::string> exact_name_prefixes = {
     "", // no prefix.
@@ -25,63 +33,63 @@ TEST(TypeMatcherTests, ExactName) {
     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")));
+    EXPECT_TRUE(matcher.Matches(CandidateFromTypeName("class Name")));
+    EXPECT_TRUE(matcher.Matches(CandidateFromTypeName("struct Name")));
+    EXPECT_TRUE(matcher.Matches(CandidateFromTypeName("union Name")));
+    EXPECT_TRUE(matcher.Matches(CandidateFromTypeName("enum Name")));
+    EXPECT_TRUE(matcher.Matches(CandidateFromTypeName("Name")));
+
+    EXPECT_FALSE(matcher.Matches(CandidateFromTypeName("Name ")));
+    EXPECT_FALSE(matcher.Matches(CandidateFromTypeName("ame")));
+    EXPECT_FALSE(matcher.Matches(CandidateFromTypeName("Nam")));
+    EXPECT_FALSE(matcher.Matches(CandidateFromTypeName("am")));
+    EXPECT_FALSE(matcher.Matches(CandidateFromTypeName("a")));
+    EXPECT_FALSE(matcher.Matches(CandidateFromTypeName(" ")));
+    EXPECT_FALSE(matcher.Matches(CandidateFromTypeName("class N")));
+    EXPECT_FALSE(matcher.Matches(CandidateFromTypeName("class ")));
+    EXPECT_FALSE(matcher.Matches(CandidateFromTypeName("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")));
+  EXPECT_TRUE(matcher.Matches(CandidateFromTypeName("abc")));
+  EXPECT_TRUE(matcher.Matches(CandidateFromTypeName("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("")));
+  EXPECT_FALSE(matcher.Matches(CandidateFromTypeName("class abc")));
+
+  EXPECT_FALSE(matcher.Matches(CandidateFromTypeName("abbc")));
+  EXPECT_FALSE(matcher.Matches(CandidateFromTypeName(" abc")));
+  EXPECT_FALSE(matcher.Matches(CandidateFromTypeName("abc ")));
+  EXPECT_FALSE(matcher.Matches(CandidateFromTypeName(" abc ")));
+  EXPECT_FALSE(matcher.Matches(CandidateFromTypeName("XabcX")));
+  EXPECT_FALSE(matcher.Matches(CandidateFromTypeName("ac")));
+  EXPECT_FALSE(matcher.Matches(CandidateFromTypeName("a[a-z]c")));
+  EXPECT_FALSE(matcher.Matches(CandidateFromTypeName("aAc")));
+  EXPECT_FALSE(matcher.Matches(CandidateFromTypeName("ABC")));
+  EXPECT_FALSE(matcher.Matches(CandidateFromTypeName("")));
 }
 
 // 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("")));
+  EXPECT_TRUE(matcher.Matches(CandidateFromTypeName("class abc")));
+  EXPECT_TRUE(matcher.Matches(CandidateFromTypeName("abc")));
+  EXPECT_TRUE(matcher.Matches(CandidateFromTypeName(" abc ")));
+  EXPECT_TRUE(matcher.Matches(CandidateFromTypeName("azc")));
+  EXPECT_TRUE(matcher.Matches(CandidateFromTypeName("abc ")));
+  EXPECT_TRUE(matcher.Matches(CandidateFromTypeName(" abc ")));
+  EXPECT_TRUE(matcher.Matches(CandidateFromTypeName(" abc")));
+  EXPECT_TRUE(matcher.Matches(CandidateFromTypeName("XabcX")));
+
+  EXPECT_FALSE(matcher.Matches(CandidateFromTypeName("abbc")));
+  EXPECT_FALSE(matcher.Matches(CandidateFromTypeName("ac")));
+  EXPECT_FALSE(matcher.Matches(CandidateFromTypeName("a[a-z]c")));
+  EXPECT_FALSE(matcher.Matches(CandidateFromTypeName("aAc")));
+  EXPECT_FALSE(matcher.Matches(CandidateFromTypeName("ABC")));
+  EXPECT_FALSE(matcher.Matches(CandidateFromTypeName("")));
 }
 
 // GetMatchString for exact type name matchers.

diff  --git a/lldb/unittests/ScriptInterpreter/Python/PythonTestSuite.cpp b/lldb/unittests/ScriptInterpreter/Python/PythonTestSuite.cpp
index 6ac4606b5a84..87e4a03ee77b 100644
--- a/lldb/unittests/ScriptInterpreter/Python/PythonTestSuite.cpp
+++ b/lldb/unittests/ScriptInterpreter/Python/PythonTestSuite.cpp
@@ -67,6 +67,12 @@ bool lldb_private::LLDBSwigPythonWatchpointCallbackFunction(
   return false;
 }
 
+bool lldb_private::LLDBSwigPythonFormatterCallbackFunction(
+    const char *python_function_name, const char *session_dictionary_name,
+    lldb::TypeImplSP type_impl_sp) {
+  return false;
+}
+
 bool lldb_private::LLDBSwigPythonCallTypeScript(
     const char *python_function_name, const void *session_dictionary,
     const lldb::ValueObjectSP &valobj_sp, void **pyfunct_wrapper,


        


More information about the lldb-commits mailing list