https://github.com/augusto2112 updated 
https://github.com/llvm/llvm-project/pull/168797

>From 8b04ffa007ded680a56733be79a5ee92589acdc6 Mon Sep 17 00:00:00 2001
From: Augusto Noronha <[email protected]>
Date: Wed, 19 Nov 2025 15:57:16 -0800
Subject: [PATCH 1/5] [lldb] Refactor LookupInfo object to be per-language

Some months ago, the LookupInfo constructor logic was refactored to
not depend on language specific logic, and use languages plugins
instead. In this refactor, when the language type is unknown, a
single LookupInfo object will handle multiple languages. This doesn't
work well, as multiple languages might want to configure the LookupInfo
object in different ways. For example, different languages might want
to set the m_lookup_name differently from each other, but the previous
implementation would pick the first name a language provided, and
effectively ignored every other language. Other fields of the LookupInfo
object are also configured in incompatible ways.

This approach doesn't seem to be a problem upstream, since only
the C++/Objective-C language plugins are available, but it broke
downstream on the Swift fork, as adding Swift to the list of default
languages when the language type is unknown breaks C++ tests.

This patch makes it so instead of building a single LookupInfo
object for multiple languages, one LookupInfo object is built per
language instead.

rdar://159531216
---
 lldb/include/lldb/Core/Module.h               | 45 +++++++++-
 lldb/include/lldb/Symbol/SymbolContext.h      | 14 +++
 lldb/include/lldb/Symbol/SymbolFile.h         |  4 +
 .../Breakpoint/BreakpointResolverName.cpp     | 33 ++++---
 lldb/source/Core/Module.cpp                   | 90 ++++++++++++-------
 lldb/source/Core/ModuleList.cpp               | 48 +++++-----
 .../Breakpad/SymbolFileBreakpad.cpp           |  2 +-
 .../Plugins/SymbolFile/DWARF/DWARFIndex.cpp   |  8 ++
 .../Plugins/SymbolFile/DWARF/DWARFIndex.h     |  5 ++
 .../SymbolFile/DWARF/SymbolFileDWARF.cpp      | 10 +--
 lldb/source/Symbol/SymbolContext.cpp          | 29 +++++-
 lldb/source/Symbol/SymbolFile.cpp             |  8 ++
 lldb/tools/lldb-test/lldb-test.cpp            |  7 +-
 13 files changed, 224 insertions(+), 79 deletions(-)

diff --git a/lldb/include/lldb/Core/Module.h b/lldb/include/lldb/Core/Module.h
index 8513e147ee523..de4e416218f73 100644
--- a/lldb/include/lldb/Core/Module.h
+++ b/lldb/include/lldb/Core/Module.h
@@ -315,6 +315,22 @@ class Module : public std::enable_shared_from_this<Module>,
                      const ModuleFunctionSearchOptions &options,
                      SymbolContextList &sc_list);
 
+  /// Find functions by a vector of lookup infos.
+  ///
+  /// If the function is an inlined function, it will have a block,
+  /// representing the inlined function, and the function will be the
+  /// containing function.  If it is not inlined, then the block will be NULL.
+  ///
+  /// \param[in] lookup_infos
+  ///     The vector of lookup infos of the function we are looking for.
+  ///
+  /// \param[out] sc_list
+  ///     A symbol context list that gets filled in with all of the
+  ///     matches.
+  void FindFunctions(const std::vector<LookupInfo> &lookup_infos,
+                     const CompilerDeclContext &parent_decl_ctx,
+                     const ModuleFunctionSearchOptions &options,
+                     SymbolContextList &sc_list);
   /// Find functions by name.
   ///
   /// If the function is an inlined function, it will have a block,
@@ -917,8 +933,29 @@ class Module : public std::enable_shared_from_this<Module>,
   public:
     LookupInfo() = default;
 
-    LookupInfo(ConstString name, lldb::FunctionNameType name_type_mask,
-               lldb::LanguageType language);
+    /// Creates a vector of lookup infos for function name resolution.
+    ///
+    /// \param[in] name
+    ///     The function name to search for. This can be a simple name like
+    ///     "foo" or a qualified name like "Class::method".
+    ///
+    /// \param[in] name_type_mask
+    ///     A bitmask specifying what types of names to search for
+    ///     (e.g., eFunctionNameTypeFull, eFunctionNameTypeBase,
+    ///     eFunctionNameTypeMethod, eFunctionNameTypeAuto). Multiple types
+    ///     can be combined with bitwise OR.
+    ///
+    /// \param[in] requested_lang_type
+    ///     The language to create lookups for. If eLanguageTypeUnknown is
+    ///     passed, creates one LookupInfo for each language plugin currently
+    ///     available in LLDB. If a specific language is provided, creates only
+    //      a single LookupInfo for that language.
+    ///
+    /// \return
+    ///     A vector of LookupInfo objects, one per relevant language.
+    static std::vector<LookupInfo>
+    MakeLookupInfos(ConstString name, lldb::FunctionNameType name_type_mask,
+                    lldb::LanguageType requested_lang_type);
 
     ConstString GetName() const { return m_name; }
 
@@ -959,6 +996,10 @@ class Module : public std::enable_shared_from_this<Module>,
     /// If \b true, then demangled names that match will need to contain
     /// "m_name" in order to be considered a match
     bool m_match_name_after_lookup = false;
+
+  private:
+    LookupInfo(ConstString name, lldb::FunctionNameType name_type_mask,
+               lldb::LanguageType lang_type);
   };
 
   /// Get a unique hash for this module.
diff --git a/lldb/include/lldb/Symbol/SymbolContext.h 
b/lldb/include/lldb/Symbol/SymbolContext.h
index af2f694e554de..0834825cdbd25 100644
--- a/lldb/include/lldb/Symbol/SymbolContext.h
+++ b/lldb/include/lldb/Symbol/SymbolContext.h
@@ -231,6 +231,20 @@ class SymbolContext {
 
   lldb::LanguageType GetLanguage() const;
 
+  /// Compares the two symbol contexts, considering that the symbol may or may
+  /// not be present. If both symbols are present, compare them, if one of the
+  /// symbols is not present, consider the symbol contexts as equal as long as
+  /// the other fields are equal.
+  ///
+  /// This function exists because SymbolContexts are often created without the
+  /// symbol, which is filled in later on, after its creation.
+  static bool CompareConsideringPossiblyNullSymbol(const SymbolContext &lhs,
+                                                   const SymbolContext &rhs);
+
+  /// Compares the two symbol contexts, except for the symbol field.
+  static bool CompareWithoutSymbol(const SymbolContext &lhs,
+                                   const SymbolContext &rhs);
+
   /// Find a block that defines the function represented by this symbol
   /// context.
   ///
diff --git a/lldb/include/lldb/Symbol/SymbolFile.h 
b/lldb/include/lldb/Symbol/SymbolFile.h
index 3b4d7bc01d132..305eb0f201b37 100644
--- a/lldb/include/lldb/Symbol/SymbolFile.h
+++ b/lldb/include/lldb/Symbol/SymbolFile.h
@@ -309,6 +309,10 @@ class SymbolFile : public PluginInterface {
   virtual void FindFunctions(const Module::LookupInfo &lookup_info,
                              const CompilerDeclContext &parent_decl_ctx,
                              bool include_inlines, SymbolContextList &sc_list);
+  virtual void
+  FindFunctions(const std::vector<Module::LookupInfo> &lookup_infos,
+                const CompilerDeclContext &parent_decl_ctx,
+                bool include_inlines, SymbolContextList &sc_list);
   virtual void FindFunctions(const RegularExpression &regex,
                              bool include_inlines, SymbolContextList &sc_list);
 
diff --git a/lldb/source/Breakpoint/BreakpointResolverName.cpp 
b/lldb/source/Breakpoint/BreakpointResolverName.cpp
index 4f252f91cccdc..2025f59eacb92 100644
--- a/lldb/source/Breakpoint/BreakpointResolverName.cpp
+++ b/lldb/source/Breakpoint/BreakpointResolverName.cpp
@@ -218,19 +218,22 @@ StructuredData::ObjectSP 
BreakpointResolverName::SerializeToStructuredData() {
 
 void BreakpointResolverName::AddNameLookup(ConstString name,
                                            FunctionNameType name_type_mask) {
-
-  Module::LookupInfo lookup(name, name_type_mask, m_language);
-  m_lookups.emplace_back(lookup);
+  std::vector<Module::LookupInfo> infos =
+      Module::LookupInfo::MakeLookupInfos(name, name_type_mask, m_language);
+  llvm::append_range(m_lookups, infos);
 
   auto add_variant_funcs = [&](Language *lang) {
     for (Language::MethodNameVariant variant :
          lang->GetMethodNameVariants(name)) {
       // FIXME: Should we be adding variants that aren't of type Full?
       if (variant.GetType() & lldb::eFunctionNameTypeFull) {
-        Module::LookupInfo variant_lookup(name, variant.GetType(),
-                                          lang->GetLanguageType());
-        variant_lookup.SetLookupName(variant.GetName());
-        m_lookups.emplace_back(variant_lookup);
+        std::vector<Module::LookupInfo> variant_lookups =
+            Module::LookupInfo::MakeLookupInfos(name, variant.GetType(),
+                                                lang->GetLanguageType());
+        llvm::for_each(variant_lookups, [&](auto &variant_lookup) {
+          variant_lookup.SetLookupName(variant.GetName());
+        });
+        llvm::append_range(m_lookups, variant_lookups);
       }
     }
     return IterationAction::Continue;
@@ -401,14 +404,22 @@ void BreakpointResolverName::GetDescription(Stream *s) {
   if (m_match_type == Breakpoint::Regexp)
     s->Printf("regex = '%s'", m_regex.GetText().str().c_str());
   else {
-    size_t num_names = m_lookups.size();
-    if (num_names == 1)
-      s->Printf("name = '%s'", m_lookups[0].GetName().GetCString());
+    // Since there may be many lookups objects for the same name breakpoint 
(one
+    // per language available), unique them by name, and operate on those 
unique
+    // names.
+    std::vector<ConstString> unique_lookups;
+    for (auto &lookup : m_lookups) {
+      if (!llvm::is_contained(unique_lookups, lookup.GetName()))
+        unique_lookups.push_back(lookup.GetName());
+    }
+    if (unique_lookups.size() == 1)
+      s->Printf("name = '%s'", unique_lookups[0].GetCString());
     else {
+      size_t num_names = unique_lookups.size();
       s->Printf("names = {");
       for (size_t i = 0; i < num_names; i++) {
         s->Printf("%s'%s'", (i == 0 ? "" : ", "),
-                  m_lookups[i].GetName().GetCString());
+                  unique_lookups[i].GetCString());
       }
       s->Printf("}");
     }
diff --git a/lldb/source/Core/Module.cpp b/lldb/source/Core/Module.cpp
index 815cc9dada2c1..79be944b33998 100644
--- a/lldb/source/Core/Module.cpp
+++ b/lldb/source/Core/Module.cpp
@@ -643,26 +643,13 @@ void Module::FindCompileUnits(const FileSpec &path,
 
 Module::LookupInfo::LookupInfo(ConstString name,
                                FunctionNameType name_type_mask,
-                               LanguageType language)
-    : m_name(name), m_lookup_name(name), m_language(language) {
+                               LanguageType lang_type)
+    : m_name(name), m_lookup_name(name), m_language(lang_type) {
   std::optional<ConstString> basename;
-
-  std::vector<Language *> languages;
-  {
-    std::vector<LanguageType> lang_types;
-    if (language != eLanguageTypeUnknown)
-      lang_types.push_back(language);
-    else
-      lang_types = {eLanguageTypeObjC, eLanguageTypeC_plus_plus};
-
-    for (LanguageType lang_type : lang_types) {
-      if (Language *lang = Language::FindPlugin(lang_type))
-        languages.push_back(lang);
-    }
-  }
+  Language *lang = Language::FindPlugin(lang_type);
 
   if (name_type_mask & eFunctionNameTypeAuto) {
-    for (Language *lang : languages) {
+    if (lang) {
       auto info = lang->GetFunctionNameInfo(name);
       if (info.first != eFunctionNameTypeNone) {
         m_name_type_mask |= info.first;
@@ -679,7 +666,7 @@ Module::LookupInfo::LookupInfo(ConstString name,
 
   } else {
     m_name_type_mask = name_type_mask;
-    for (Language *lang : languages) {
+    if (lang) {
       auto info = lang->GetFunctionNameInfo(name);
       if (info.first & m_name_type_mask) {
         // If the user asked for FunctionNameTypes that aren't possible,
@@ -688,14 +675,12 @@ Module::LookupInfo::LookupInfo(ConstString name,
         // ObjC)
         m_name_type_mask &= info.first;
         basename = info.second;
-        break;
-      }
-      // Still try and get a basename in case someone specifies a name type 
mask
-      // of eFunctionNameTypeFull and a name like "A::func"
-      if (name_type_mask & eFunctionNameTypeFull &&
-          info.first != eFunctionNameTypeNone && !basename && info.second) {
+      } else if (name_type_mask & eFunctionNameTypeFull &&
+                 info.first != eFunctionNameTypeNone && !basename &&
+                 info.second) {
+        // Still try and get a basename in case someone specifies a name type
+        // mask of eFunctionNameTypeFull and a name like "A::func"
         basename = info.second;
-        break;
       }
     }
   }
@@ -711,6 +696,36 @@ Module::LookupInfo::LookupInfo(ConstString name,
   }
 }
 
+std::vector<Module::LookupInfo>
+Module::LookupInfo::MakeLookupInfos(ConstString name,
+                                    lldb::FunctionNameType name_type_mask,
+                                    lldb::LanguageType requested_lang_type) {
+  std::vector<LanguageType> lang_types;
+  if (requested_lang_type != eLanguageTypeUnknown) {
+    lang_types.push_back(requested_lang_type);
+  } else {
+    // If the language type was not specified, look up in every language
+    // available.
+    Language::ForEach([&](Language *lang) {
+      auto lang_type = lang->GetLanguageType();
+      if (!llvm::is_contained(lang_types, lang_type))
+        lang_types.push_back(lang_type);
+      return IterationAction::Continue;
+    });
+
+    if (lang_types.empty())
+      lang_types = {eLanguageTypeObjC, eLanguageTypeC_plus_plus};
+  }
+
+  std::vector<Module::LookupInfo> infos;
+  infos.reserve(lang_types.size());
+  for (LanguageType lang_type : lang_types) {
+    Module::LookupInfo info(name, name_type_mask, lang_type);
+    infos.push_back(info);
+  }
+  return infos;
+}
+
 bool Module::LookupInfo::NameMatchesLookupInfo(
     ConstString function_name, LanguageType language_type) const {
   // We always keep unnamed symbols
@@ -824,18 +839,29 @@ void Module::FindFunctions(const Module::LookupInfo 
&lookup_info,
   }
 }
 
+void Module::FindFunctions(const std::vector<Module::LookupInfo> &lookup_infos,
+                           const CompilerDeclContext &parent_decl_ctx,
+                           const ModuleFunctionSearchOptions &options,
+                           SymbolContextList &sc_list) {
+  for (auto &lookup_info : lookup_infos)
+    FindFunctions(lookup_info, parent_decl_ctx, options, sc_list);
+}
+
 void Module::FindFunctions(ConstString name,
                            const CompilerDeclContext &parent_decl_ctx,
                            FunctionNameType name_type_mask,
                            const ModuleFunctionSearchOptions &options,
                            SymbolContextList &sc_list) {
-  const size_t old_size = sc_list.GetSize();
-  LookupInfo lookup_info(name, name_type_mask, eLanguageTypeUnknown);
-  FindFunctions(lookup_info, parent_decl_ctx, options, sc_list);
-  if (name_type_mask & eFunctionNameTypeAuto) {
-    const size_t new_size = sc_list.GetSize();
-    if (old_size < new_size)
-      lookup_info.Prune(sc_list, old_size);
+  std::vector<LookupInfo> lookup_infos =
+      LookupInfo::MakeLookupInfos(name, name_type_mask, eLanguageTypeUnknown);
+  for (auto &lookup_info : lookup_infos) {
+    const size_t old_size = sc_list.GetSize();
+    FindFunctions(lookup_info, parent_decl_ctx, options, sc_list);
+    if (name_type_mask & eFunctionNameTypeAuto) {
+      const size_t new_size = sc_list.GetSize();
+      if (old_size < new_size)
+        lookup_info.Prune(sc_list, old_size);
+    }
   }
 }
 
diff --git a/lldb/source/Core/ModuleList.cpp b/lldb/source/Core/ModuleList.cpp
index 5444c04e74522..be6ff723e0ffa 100644
--- a/lldb/source/Core/ModuleList.cpp
+++ b/lldb/source/Core/ModuleList.cpp
@@ -453,21 +453,22 @@ void ModuleList::FindFunctions(ConstString name,
                                FunctionNameType name_type_mask,
                                const ModuleFunctionSearchOptions &options,
                                SymbolContextList &sc_list) const {
-  const size_t old_size = sc_list.GetSize();
-
   if (name_type_mask & eFunctionNameTypeAuto) {
-    Module::LookupInfo lookup_info(name, name_type_mask, eLanguageTypeUnknown);
-
+    std::vector<Module::LookupInfo> lookup_infos =
+        Module::LookupInfo::MakeLookupInfos(name, name_type_mask,
+                                            eLanguageTypeUnknown);
     std::lock_guard<std::recursive_mutex> guard(m_modules_mutex);
-    for (const ModuleSP &module_sp : m_modules) {
-      module_sp->FindFunctions(lookup_info, CompilerDeclContext(), options,
-                               sc_list);
-    }
-
-    const size_t new_size = sc_list.GetSize();
+    for (const auto &lookup_info : lookup_infos) {
+      const size_t old_size = sc_list.GetSize();
+      for (const ModuleSP &module_sp : m_modules) {
+        module_sp->FindFunctions(lookup_info, CompilerDeclContext(), options,
+                                 sc_list);
+      }
 
-    if (old_size < new_size)
-      lookup_info.Prune(sc_list, old_size);
+      const size_t new_size = sc_list.GetSize();
+      if (old_size < new_size)
+        lookup_info.Prune(sc_list, old_size);
+    }
   } else {
     std::lock_guard<std::recursive_mutex> guard(m_modules_mutex);
     for (const ModuleSP &module_sp : m_modules) {
@@ -480,21 +481,24 @@ void ModuleList::FindFunctions(ConstString name,
 void ModuleList::FindFunctionSymbols(ConstString name,
                                      lldb::FunctionNameType name_type_mask,
                                      SymbolContextList &sc_list) {
-  const size_t old_size = sc_list.GetSize();
-
   if (name_type_mask & eFunctionNameTypeAuto) {
-    Module::LookupInfo lookup_info(name, name_type_mask, eLanguageTypeUnknown);
+    std::vector<Module::LookupInfo> lookup_infos =
+        Module::LookupInfo::MakeLookupInfos(name, name_type_mask,
+                                            eLanguageTypeUnknown);
 
     std::lock_guard<std::recursive_mutex> guard(m_modules_mutex);
-    for (const ModuleSP &module_sp : m_modules) {
-      module_sp->FindFunctionSymbols(lookup_info.GetLookupName(),
-                                     lookup_info.GetNameTypeMask(), sc_list);
-    }
+    for (const auto &lookup_info : lookup_infos) {
+      const size_t old_size = sc_list.GetSize();
+      for (const ModuleSP &module_sp : m_modules) {
+        module_sp->FindFunctionSymbols(lookup_info.GetLookupName(),
+                                       lookup_info.GetNameTypeMask(), sc_list);
+      }
 
-    const size_t new_size = sc_list.GetSize();
+      const size_t new_size = sc_list.GetSize();
 
-    if (old_size < new_size)
-      lookup_info.Prune(sc_list, old_size);
+      if (old_size < new_size)
+        lookup_info.Prune(sc_list, old_size);
+    }
   } else {
     std::lock_guard<std::recursive_mutex> guard(m_modules_mutex);
     for (const ModuleSP &module_sp : m_modules) {
diff --git a/lldb/source/Plugins/SymbolFile/Breakpad/SymbolFileBreakpad.cpp 
b/lldb/source/Plugins/SymbolFile/Breakpad/SymbolFileBreakpad.cpp
index ce2ba69be2e96..14932e957d081 100644
--- a/lldb/source/Plugins/SymbolFile/Breakpad/SymbolFileBreakpad.cpp
+++ b/lldb/source/Plugins/SymbolFile/Breakpad/SymbolFileBreakpad.cpp
@@ -437,7 +437,7 @@ void SymbolFileBreakpad::FindFunctions(
       sc.comp_unit = cu_sp.get();
       sc.function = func_sp.get();
       sc.module_sp = func_sp->CalculateSymbolContextModule();
-      sc_list.Append(sc);
+      sc_list.AppendIfUnique(sc, /*merge_symbol_into_function=*/true);
     }
   }
 }
diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFIndex.cpp 
b/lldb/source/Plugins/SymbolFile/DWARF/DWARFIndex.cpp
index 64a8005308454..c4efc06ab7873 100644
--- a/lldb/source/Plugins/SymbolFile/DWARF/DWARFIndex.cpp
+++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFIndex.cpp
@@ -173,6 +173,14 @@ void DWARFIndex::GetNamespacesWithParents(
   });
 }
 
+void DWARFIndex::GetFunctions(
+    const std::vector<Module::LookupInfo> &lookup_infos, SymbolFileDWARF 
&dwarf,
+    const CompilerDeclContext &parent_decl_ctx,
+    llvm::function_ref<IterationAction(DWARFDIE die)> callback) {
+  for (auto &lookup_info : lookup_infos)
+    GetFunctions(lookup_info, dwarf, parent_decl_ctx, callback);
+}
+
 IterationAction DWARFIndex::ProcessNamespaceDieMatchParents(
     const CompilerDeclContext &parent_decl_ctx, DWARFDIE die,
     llvm::function_ref<IterationAction(DWARFDIE die)> callback) {
diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFIndex.h 
b/lldb/source/Plugins/SymbolFile/DWARF/DWARFIndex.h
index be73255aaf141..eaf1da123602e 100644
--- a/lldb/source/Plugins/SymbolFile/DWARF/DWARFIndex.h
+++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFIndex.h
@@ -86,6 +86,11 @@ class DWARFIndex {
                const CompilerDeclContext &parent_decl_ctx,
                llvm::function_ref<IterationAction(DWARFDIE die)> callback) = 0;
   virtual void
+  GetFunctions(const std::vector<Module::LookupInfo> &lookup_infos,
+               SymbolFileDWARF &dwarf,
+               const CompilerDeclContext &parent_decl_ctx,
+               llvm::function_ref<IterationAction(DWARFDIE die)> callback);
+  virtual void
   GetFunctions(const RegularExpression &regex,
                llvm::function_ref<IterationAction(DWARFDIE die)> callback) = 0;
 
diff --git a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp 
b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp
index 7ba765371c54f..b755f3a6faaa0 100644
--- a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp
+++ b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp
@@ -2481,7 +2481,7 @@ bool SymbolFileDWARF::ResolveFunction(const DWARFDIE 
&orig_die,
         sc.block = function_block.FindBlockByID(inlined_die.GetOffset());
     }
 
-    sc_list.Append(sc);
+    sc_list.AppendIfUnique(sc, /*merge_symbol_into_function=*/true);
     return true;
   }
 
@@ -2549,11 +2549,11 @@ SymbolFileDWARF::FindFunctionDefinition(const 
FunctionCallLabel &label,
                                         const DWARFDIE &declaration) {
   auto do_lookup = [this](llvm::StringRef lookup_name) -> DWARFDIE {
     DWARFDIE found;
-    Module::LookupInfo info(ConstString(lookup_name),
-                            lldb::eFunctionNameTypeFull,
-                            lldb::eLanguageTypeUnknown);
+    auto lookup_infos = Module::LookupInfo::MakeLookupInfos(
+        ConstString(lookup_name), lldb::eFunctionNameTypeFull,
+        lldb::eLanguageTypeUnknown);
 
-    m_index->GetFunctions(info, *this, {}, [&](DWARFDIE entry) {
+    m_index->GetFunctions(lookup_infos, *this, {}, [&](DWARFDIE entry) {
       if (entry.GetAttributeValueAsUnsigned(llvm::dwarf::DW_AT_declaration, 0))
         return IterationAction::Continue;
 
diff --git a/lldb/source/Symbol/SymbolContext.cpp 
b/lldb/source/Symbol/SymbolContext.cpp
index 3bbd1eff824e6..ead924afa9fc2 100644
--- a/lldb/source/Symbol/SymbolContext.cpp
+++ b/lldb/source/Symbol/SymbolContext.cpp
@@ -324,12 +324,32 @@ uint32_t SymbolContext::GetResolvedMask() const {
 
 bool lldb_private::operator==(const SymbolContext &lhs,
                               const SymbolContext &rhs) {
-  return lhs.function == rhs.function && lhs.symbol == rhs.symbol &&
+  return SymbolContext::CompareWithoutSymbol(lhs, rhs) &&
+         lhs.symbol == rhs.symbol;
+}
+
+bool SymbolContext::CompareConsideringPossiblyNullSymbol(
+    const SymbolContext &lhs, const SymbolContext &rhs) {
+  if (!CompareWithoutSymbol(lhs, rhs))
+    return false;
+
+  // If one (or both) of the symbol context's symbol is empty, consider them
+  // equal.
+  if (!lhs.symbol || !rhs.symbol)
+    return true;
+
+  // If both symbols are present, make sure they're the same.
+  return lhs.symbol == rhs.symbol;
+}
+
+bool SymbolContext::CompareWithoutSymbol(const SymbolContext &lhs,
+                                         const SymbolContext &rhs) {
+  return lhs.function == rhs.function &&
          lhs.module_sp.get() == rhs.module_sp.get() &&
          lhs.comp_unit == rhs.comp_unit &&
          lhs.target_sp.get() == rhs.target_sp.get() &&
          LineEntry::Compare(lhs.line_entry, rhs.line_entry) == 0 &&
-         lhs.variable == rhs.variable;
+         lhs.variable == rhs.variable && lhs.block == rhs.block;
 }
 
 bool lldb_private::operator!=(const SymbolContext &lhs,
@@ -1200,7 +1220,10 @@ bool SymbolContextList::AppendIfUnique(const 
SymbolContext &sc,
                                        bool merge_symbol_into_function) {
   collection::iterator pos, end = m_symbol_contexts.end();
   for (pos = m_symbol_contexts.begin(); pos != end; ++pos) {
-    if (*pos == sc)
+    // Because symbol contexts might first be built without the symbol,
+    // which is then appended later on, compare the symbol contexts taking into
+    // accout that one (or either) of them might not have a symbol yet.
+    if (SymbolContext::CompareConsideringPossiblyNullSymbol(*pos, sc))
       return false;
   }
   if (merge_symbol_into_function && sc.symbol != nullptr &&
diff --git a/lldb/source/Symbol/SymbolFile.cpp 
b/lldb/source/Symbol/SymbolFile.cpp
index 870d778dca740..2aea802b6c826 100644
--- a/lldb/source/Symbol/SymbolFile.cpp
+++ b/lldb/source/Symbol/SymbolFile.cpp
@@ -126,6 +126,14 @@ void SymbolFile::FindFunctions(const Module::LookupInfo 
&lookup_info,
                                bool include_inlines,
                                SymbolContextList &sc_list) {}
 
+void SymbolFile::FindFunctions(
+    const std::vector<Module::LookupInfo> &lookup_infos,
+    const CompilerDeclContext &parent_decl_ctx, bool include_inlines,
+    SymbolContextList &sc_list) {
+  for (const auto &lookup_info : lookup_infos)
+    FindFunctions(lookup_info, parent_decl_ctx, include_inlines, sc_list);
+}
+
 void SymbolFile::FindFunctions(const RegularExpression &regex,
                                bool include_inlines,
                                SymbolContextList &sc_list) {}
diff --git a/lldb/tools/lldb-test/lldb-test.cpp 
b/lldb/tools/lldb-test/lldb-test.cpp
index 3f198d963a93b..84e83da230029 100644
--- a/lldb/tools/lldb-test/lldb-test.cpp
+++ b/lldb/tools/lldb-test/lldb-test.cpp
@@ -523,9 +523,10 @@ Error opts::symbols::findFunctions(lldb_private::Module 
&Module) {
         ContextOr->IsValid() ? *ContextOr : CompilerDeclContext();
 
     List.Clear();
-    lldb_private::Module::LookupInfo lookup_info(
-        ConstString(Name), getFunctionNameFlags(), eLanguageTypeUnknown);
-    Symfile.FindFunctions(lookup_info, ContextPtr, true, List);
+    std::vector<lldb_private::Module::LookupInfo> lookup_infos =
+        lldb_private::Module::LookupInfo::MakeLookupInfos(
+            ConstString(Name), getFunctionNameFlags(), eLanguageTypeUnknown);
+    Symfile.FindFunctions(lookup_infos, ContextPtr, true, List);
   }
   outs() << formatv("Found {0} functions:\n", List.GetSize());
   StreamString Stream;

>From e55e3780c33dc888efa6acd2e67a65ec48ebdcaf Mon Sep 17 00:00:00 2001
From: Augusto Noronha <[email protected]>
Date: Tue, 2 Dec 2025 17:00:31 -0800
Subject: [PATCH 2/5] Add test

---
 lldb/include/lldb/Core/Module.h    |  11 +--
 lldb/source/Core/Module.cpp        |   6 +-
 lldb/unittests/Core/CMakeLists.txt |   1 +
 lldb/unittests/Core/ModuleTest.cpp | 148 +++++++++++++++++++++++++++++
 4 files changed, 154 insertions(+), 12 deletions(-)
 create mode 100644 lldb/unittests/Core/ModuleTest.cpp

diff --git a/lldb/include/lldb/Core/Module.h b/lldb/include/lldb/Core/Module.h
index de4e416218f73..96ae8364c94e5 100644
--- a/lldb/include/lldb/Core/Module.h
+++ b/lldb/include/lldb/Core/Module.h
@@ -320,13 +320,6 @@ class Module : public std::enable_shared_from_this<Module>,
   /// If the function is an inlined function, it will have a block,
   /// representing the inlined function, and the function will be the
   /// containing function.  If it is not inlined, then the block will be NULL.
-  ///
-  /// \param[in] lookup_infos
-  ///     The vector of lookup infos of the function we are looking for.
-  ///
-  /// \param[out] sc_list
-  ///     A symbol context list that gets filled in with all of the
-  ///     matches.
   void FindFunctions(const std::vector<LookupInfo> &lookup_infos,
                      const CompilerDeclContext &parent_decl_ctx,
                      const ModuleFunctionSearchOptions &options,
@@ -945,7 +938,7 @@ class Module : public std::enable_shared_from_this<Module>,
     ///     eFunctionNameTypeMethod, eFunctionNameTypeAuto). Multiple types
     ///     can be combined with bitwise OR.
     ///
-    /// \param[in] requested_lang_type
+    /// \param[in] lang_type
     ///     The language to create lookups for. If eLanguageTypeUnknown is
     ///     passed, creates one LookupInfo for each language plugin currently
     ///     available in LLDB. If a specific language is provided, creates only
@@ -955,7 +948,7 @@ class Module : public std::enable_shared_from_this<Module>,
     ///     A vector of LookupInfo objects, one per relevant language.
     static std::vector<LookupInfo>
     MakeLookupInfos(ConstString name, lldb::FunctionNameType name_type_mask,
-                    lldb::LanguageType requested_lang_type);
+                    lldb::LanguageType lang_type);
 
     ConstString GetName() const { return m_name; }
 
diff --git a/lldb/source/Core/Module.cpp b/lldb/source/Core/Module.cpp
index 79be944b33998..eb2f95b105a5d 100644
--- a/lldb/source/Core/Module.cpp
+++ b/lldb/source/Core/Module.cpp
@@ -699,10 +699,10 @@ Module::LookupInfo::LookupInfo(ConstString name,
 std::vector<Module::LookupInfo>
 Module::LookupInfo::MakeLookupInfos(ConstString name,
                                     lldb::FunctionNameType name_type_mask,
-                                    lldb::LanguageType requested_lang_type) {
+                                    lldb::LanguageType lang_type) {
   std::vector<LanguageType> lang_types;
-  if (requested_lang_type != eLanguageTypeUnknown) {
-    lang_types.push_back(requested_lang_type);
+  if (lang_type != eLanguageTypeUnknown) {
+    lang_types.push_back(lang_type);
   } else {
     // If the language type was not specified, look up in every language
     // available.
diff --git a/lldb/unittests/Core/CMakeLists.txt 
b/lldb/unittests/Core/CMakeLists.txt
index f0c9a9a9d5056..d69432d332f44 100644
--- a/lldb/unittests/Core/CMakeLists.txt
+++ b/lldb/unittests/Core/CMakeLists.txt
@@ -9,6 +9,7 @@ add_lldb_unittest(LLDBCoreTests
   MangledTest.cpp
   ModuleListTest.cpp
   ModuleSpecTest.cpp
+  ModuleTest.cpp
   PluginManagerTest.cpp
   ProgressReportTest.cpp
   RichManglingContextTest.cpp
diff --git a/lldb/unittests/Core/ModuleTest.cpp 
b/lldb/unittests/Core/ModuleTest.cpp
new file mode 100644
index 0000000000000..446793dc389e3
--- /dev/null
+++ b/lldb/unittests/Core/ModuleTest.cpp
@@ -0,0 +1,148 @@
+//===-- ModuleTest.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/Core/Module.h"
+#include "Plugins/Language/CPlusPlus/CPlusPlusLanguage.h"
+#include "Plugins/ObjectFile/ELF/ObjectFileELF.h"
+#include "Plugins/Platform/Linux/PlatformLinux.h"
+#include "Plugins/SymbolFile/Symtab/SymbolFileSymtab.h"
+#include "TestingSupport/SubsystemRAII.h"
+#include "TestingSupport/TestUtilities.h"
+#include "lldb/Core/Debugger.h"
+#include "lldb/Core/PluginManager.h"
+#include "lldb/Host/FileSystem.h"
+#include "lldb/Host/HostInfo.h"
+#include "lldb/Target/Language.h"
+#include "lldb/lldb-enumerations.h"
+#include "gtest/gtest.h"
+#include <optional>
+
+using namespace lldb;
+using namespace lldb_private;
+
+// Test that Module::FindFunctions correctly finds C++ mangled symbols
+// even when multiple language plugins are registered.
+TEST(ModuleTest, FindFunctionsCppMangledName) {
+  // Create a mock language. The point of this language is to return something
+  // in GetFunctionNameInfo that would interfere with the C++ language plugin,
+  // were they sharing the same LookupInfo.
+  class MockLanguageWithBogusLookupInfo : public Language {
+  public:
+    MockLanguageWithBogusLookupInfo() = default;
+    ~MockLanguageWithBogusLookupInfo() override = default;
+
+    lldb::LanguageType GetLanguageType() const override {
+      // The language here doesn't really matter, it just has to be something
+      // that is not C/C++/ObjC.
+      return lldb::eLanguageTypeSwift;
+    }
+
+    llvm::StringRef GetPluginName() override { return "mock-bogus-language"; }
+
+    bool IsSourceFile(llvm::StringRef file_path) const override {
+      return file_path.ends_with(".swift");
+    }
+
+    std::pair<lldb::FunctionNameType, std::optional<ConstString>>
+    GetFunctionNameInfo(ConstString name) const override {
+      // Say that every function is a selector.
+      return {lldb::eFunctionNameTypeSelector, ConstString("BOGUS_BASENAME")};
+    }
+
+    static void Initialize() {
+      PluginManager::RegisterPlugin(GetPluginNameStatic(), "Mock Language",
+                                    CreateInstance);
+    }
+
+    static void Terminate() { PluginManager::UnregisterPlugin(CreateInstance); 
}
+
+    static lldb_private::Language *CreateInstance(lldb::LanguageType language) 
{
+      if (language == lldb::eLanguageTypeSwift)
+        return new MockLanguageWithBogusLookupInfo();
+      return nullptr;
+    }
+
+    static llvm::StringRef GetPluginNameStatic() {
+      return "mock-bogus-language";
+    }
+  };
+  SubsystemRAII<FileSystem, HostInfo, ObjectFileELF, SymbolFileSymtab,
+                CPlusPlusLanguage, platform_linux::PlatformLinux,
+                MockLanguageWithBogusLookupInfo>
+      subsystems;
+
+  ArchSpec arch("x86_64-pc-linux");
+  Platform::SetHostPlatform(
+      platform_linux::PlatformLinux::CreateInstance(true, &arch));
+
+  std::call_once(TestUtilities::g_debugger_initialize_flag,
+                 []() { Debugger::Initialize(nullptr); });
+
+  // Create a simple ELF module with std::vector::size() as the only symbol.
+  auto ExpectedFile = TestFile::fromYaml(R"(
+--- !ELF
+FileHeader:
+  Class:           ELFCLASS64
+  Data:            ELFDATA2LSB
+  Type:            ET_DYN
+  Machine:         EM_X86_64
+Sections:
+  - Name:            .text
+    Type:            SHT_PROGBITS
+    Flags:           [ SHF_ALLOC, SHF_EXECINSTR ]
+    Address:         0x1000
+    AddressAlign:    0x10
+    Size:            0x100
+Symbols:
+  - Name:            _ZNSt6vectorIiE4sizeEv
+    Type:            STT_FUNC
+    Section:         .text
+    Value:           0x1030
+    Size:            0x20
+...
+)");
+  ASSERT_THAT_EXPECTED(ExpectedFile, llvm::Succeeded());
+
+  DebuggerSP debugger_sp = Debugger::CreateInstance();
+  ASSERT_TRUE(debugger_sp);
+
+  TargetSP target_sp;
+  PlatformSP platform_sp;
+
+  Status error = debugger_sp->GetTargetList().CreateTarget(
+      *debugger_sp, "", arch, eLoadDependentsNo, platform_sp, target_sp);
+  ASSERT_TRUE(error.Success());
+  ASSERT_TRUE(target_sp);
+
+  auto module_sp = std::make_shared<Module>(ExpectedFile->moduleSpec());
+  target_sp->GetImages().Append(module_sp);
+
+  // Verify both C++ and our mock language are registered.
+  Language *cpp_lang = Language::FindPlugin(lldb::eLanguageTypeC_plus_plus);
+  Language *mock_lang = Language::FindPlugin(lldb::eLanguageTypeSwift);
+  ASSERT_NE(cpp_lang, nullptr) << "C++ language plugin should be registered";
+  ASSERT_NE(mock_lang, nullptr)
+      << "Mock Swift language plugin should be registered";
+
+  ModuleFunctionSearchOptions function_options;
+  function_options.include_symbols = true;
+
+  ConstString symbol_name("_ZNSt6vectorIiE4sizeEv");
+  SymbolContextList results;
+  module_sp->FindFunctions(symbol_name, CompilerDeclContext(),
+                           eFunctionNameTypeAuto, function_options, results);
+
+  // Assert that we found one symbol.
+  ASSERT_EQ(results.GetSize(), 1u);
+
+  auto result = results[0];
+  auto name = result.GetFunctionName();
+  // Assert that the symbol we found is what we expected.
+  ASSERT_EQ(name, "std::vector<int>::size()");
+  ASSERT_EQ(result.GetLanguage(), eLanguageTypeC_plus_plus);
+}

>From 1567174e6b369cd318ed4f4aa045d220b2e20ab9 Mon Sep 17 00:00:00 2001
From: Augusto Noronha <[email protected]>
Date: Wed, 3 Dec 2025 10:29:16 -0800
Subject: [PATCH 3/5] --wip-- [skip ci]

---
 lldb/unittests/Core/ModuleTest.cpp | 20 ++------------------
 1 file changed, 2 insertions(+), 18 deletions(-)

diff --git a/lldb/unittests/Core/ModuleTest.cpp 
b/lldb/unittests/Core/ModuleTest.cpp
index 446793dc389e3..f645479eaa9b3 100644
--- a/lldb/unittests/Core/ModuleTest.cpp
+++ b/lldb/unittests/Core/ModuleTest.cpp
@@ -72,13 +72,9 @@ TEST(ModuleTest, FindFunctionsCppMangledName) {
     }
   };
   SubsystemRAII<FileSystem, HostInfo, ObjectFileELF, SymbolFileSymtab,
-                CPlusPlusLanguage, platform_linux::PlatformLinux,
-                MockLanguageWithBogusLookupInfo>
-      subsystems;
+                CPlusPlusLanguage, 
+                MockLanguageWithBogusLookupInfo> subsystems;
 
-  ArchSpec arch("x86_64-pc-linux");
-  Platform::SetHostPlatform(
-      platform_linux::PlatformLinux::CreateInstance(true, &arch));
 
   std::call_once(TestUtilities::g_debugger_initialize_flag,
                  []() { Debugger::Initialize(nullptr); });
@@ -108,19 +104,7 @@ TEST(ModuleTest, FindFunctionsCppMangledName) {
 )");
   ASSERT_THAT_EXPECTED(ExpectedFile, llvm::Succeeded());
 
-  DebuggerSP debugger_sp = Debugger::CreateInstance();
-  ASSERT_TRUE(debugger_sp);
-
-  TargetSP target_sp;
-  PlatformSP platform_sp;
-
-  Status error = debugger_sp->GetTargetList().CreateTarget(
-      *debugger_sp, "", arch, eLoadDependentsNo, platform_sp, target_sp);
-  ASSERT_TRUE(error.Success());
-  ASSERT_TRUE(target_sp);
-
   auto module_sp = std::make_shared<Module>(ExpectedFile->moduleSpec());
-  target_sp->GetImages().Append(module_sp);
 
   // Verify both C++ and our mock language are registered.
   Language *cpp_lang = Language::FindPlugin(lldb::eLanguageTypeC_plus_plus);

>From c11b5d82958992e56f4228f941aee939629cffb9 Mon Sep 17 00:00:00 2001
From: Augusto Noronha <[email protected]>
Date: Wed, 3 Dec 2025 10:33:44 -0800
Subject: [PATCH 4/5] Remove dependency on Linux

---
 lldb/unittests/Core/ModuleTest.cpp | 11 ++---------
 1 file changed, 2 insertions(+), 9 deletions(-)

diff --git a/lldb/unittests/Core/ModuleTest.cpp 
b/lldb/unittests/Core/ModuleTest.cpp
index f645479eaa9b3..f657f7206fa3e 100644
--- a/lldb/unittests/Core/ModuleTest.cpp
+++ b/lldb/unittests/Core/ModuleTest.cpp
@@ -9,16 +9,13 @@
 #include "lldb/Core/Module.h"
 #include "Plugins/Language/CPlusPlus/CPlusPlusLanguage.h"
 #include "Plugins/ObjectFile/ELF/ObjectFileELF.h"
-#include "Plugins/Platform/Linux/PlatformLinux.h"
 #include "Plugins/SymbolFile/Symtab/SymbolFileSymtab.h"
 #include "TestingSupport/SubsystemRAII.h"
 #include "TestingSupport/TestUtilities.h"
-#include "lldb/Core/Debugger.h"
 #include "lldb/Core/PluginManager.h"
 #include "lldb/Host/FileSystem.h"
 #include "lldb/Host/HostInfo.h"
 #include "lldb/Target/Language.h"
-#include "lldb/lldb-enumerations.h"
 #include "gtest/gtest.h"
 #include <optional>
 
@@ -72,12 +69,8 @@ TEST(ModuleTest, FindFunctionsCppMangledName) {
     }
   };
   SubsystemRAII<FileSystem, HostInfo, ObjectFileELF, SymbolFileSymtab,
-                CPlusPlusLanguage, 
-                MockLanguageWithBogusLookupInfo> subsystems;
-
-
-  std::call_once(TestUtilities::g_debugger_initialize_flag,
-                 []() { Debugger::Initialize(nullptr); });
+                CPlusPlusLanguage, MockLanguageWithBogusLookupInfo>
+      subsystems;
 
   // Create a simple ELF module with std::vector::size() as the only symbol.
   auto ExpectedFile = TestFile::fromYaml(R"(

>From 15edf30989d46b03d2b680b5b2c9da9cfce87f67 Mon Sep 17 00:00:00 2001
From: Augusto Noronha <[email protected]>
Date: Wed, 3 Dec 2025 15:17:30 -0800
Subject: [PATCH 5/5] Update TestBreakpointSerialization

---
 .../breakpoint/serialize/TestBreakpointSerialization.py  | 9 +++++++--
 1 file changed, 7 insertions(+), 2 deletions(-)

diff --git 
a/lldb/test/API/functionalities/breakpoint/serialize/TestBreakpointSerialization.py
 
b/lldb/test/API/functionalities/breakpoint/serialize/TestBreakpointSerialization.py
index 411ce9c67da02..55cc12e894d42 100644
--- 
a/lldb/test/API/functionalities/breakpoint/serialize/TestBreakpointSerialization.py
+++ 
b/lldb/test/API/functionalities/breakpoint/serialize/TestBreakpointSerialization.py
@@ -210,6 +210,11 @@ def check_equivalence(self, source_bps, do_write=True):
                 "Source and dest breakpoints are not identical: \nsource: 
%s\ndest: %s"
                 % (source_text, copy_text),
             )
+            self.assertEqual(
+                source_bp.GetNumLocations(),
+                copy_bp.GetNumLocations(),
+                "Source and dest num locations are not the same",
+            )
 
     def do_check_resolvers(self):
         """Use Python APIs to check serialization of breakpoint resolvers"""
@@ -386,7 +391,7 @@ def do_check_appending(self):
         source_bps.Clear()
 
         bkpt = self.orig_target.BreakpointCreateByName(
-            "blubby", lldb.eFunctionNameTypeAuto, empty_module_list, 
empty_cu_list
+            "main", lldb.eFunctionNameTypeAuto, empty_module_list, 
empty_cu_list
         )
         bkpt.SetIgnoreCount(10)
         bkpt.SetThreadName("grubby")
@@ -394,7 +399,7 @@ def do_check_appending(self):
         all_bps.Append(bkpt)
 
         bkpt = self.orig_target.BreakpointCreateByName(
-            "blubby", lldb.eFunctionNameTypeFull, empty_module_list, 
empty_cu_list
+            "main", lldb.eFunctionNameTypeFull, empty_module_list, 
empty_cu_list
         )
         bkpt.SetCondition("something != something_else")
         bkpt.SetQueueName("grubby")

_______________________________________________
lldb-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits

Reply via email to