[Lldb-commits] [lldb] [llvm] [lldb][SBAPI] Add (SBTarget/SBModule)::FindSymbolContexts (PR #223808)

Ebuka Ezike via lldb-commits lldb-commits at lists.llvm.org
Tue Sep 15 13:08:58 PDT 2026


https://github.com/da-viper created https://github.com/llvm/llvm-project/pull/223808

Add two new SBAPI methods that return every symbol context matching a source location.

  * SBTarget::FindSymbolContexts(SBLineEntry, resolve_scope, check_inlines) searches across every module in the target.
  * SBModule::FindSymbolContexts(SBLineEntry, resolve_scope, check_inlines) searches within one module.

Both return an SBSymbolContextList. I intend to use the API in the `gotoTargets` request in lldb-dap to get the locations we can jump to. See https://github.com/llvm/llvm-project/pull/130503#discussion_r2015102408

A SBLineEntry(SBFileSpec, line, column) constructor lets scripts build the location argument directly.
I don't like doing the below to make a line entry.
```py
line_entry = lldb.SBLineEntry()
line_entry.SetFileSpec(filespec)
line_entry.SetLine(line)
line_entry.SetColumn(column)
```

SBLineEntry::GetLine() and GetColumn() now return
LLDB_INVALID_LINE_NUMBER / LLDB_INVALID_COLUMN_NUMBER respectively.

>From 2d23ac9ae73778786e2eea8fcee909b08604664b Mon Sep 17 00:00:00 2001
From: Ebuka Ezike <e_ezike at apple.com>
Date: Tue, 15 Sep 2026 21:04:28 +0100
Subject: [PATCH] [lldb][SBAPI] Add (SBTarget/SBModule)::FindSymbolContexts

Add two new SBAPI methods that return every symbol context matching a
source location.

  * SBTarget::FindSymbolContexts(SBLineEntry, resolve_scope, check_inlines)
    searches across every module in the target.
  * SBModule::FindSymbolContexts(SBLineEntry, resolve_scope, check_inlines)
    searches within one module.

Both return an SBSymbolContextList. I intend to use the API in the
`gotoTargets` request in lldb-dap to get the locations we can jump to.
See https://github.com/llvm/llvm-project/pull/130503#discussion_r2015102408

A SBLineEntry(SBFileSpec, line, column) constructor lets scripts
build the location argument directly.
I don't like doing the below to make a line entry.
```py
line_entry = lldb.SBLineEntry()
line_entry.SetFileSpec(filespec)
line_entry.SetLine(line)
line_entry.SetColumn(column)
```

SBLineEntry::GetLine() and GetColumn() now return
LLDB_INVALID_LINE_NUMBER / LLDB_INVALID_COLUMN_NUMBER respectively.
---
 .../interface/SBLineEntryDocstrings.i         |   3 +
 lldb/bindings/interface/SBModuleDocstrings.i  |   3 +
 lldb/bindings/interface/SBTargetDocstrings.i  |   3 +
 lldb/include/lldb/API/SBLineEntry.h           |   5 +
 lldb/include/lldb/API/SBModule.h              |  24 +++
 lldb/include/lldb/API/SBTarget.h              |  25 +++
 lldb/source/API/SBLineEntry.cpp               |  18 ++-
 lldb/source/API/SBModule.cpp                  |  21 +++
 lldb/source/API/SBTarget.cpp                  |  22 +++
 .../python_api/find_symbol_contexts/Makefile  |   3 +
 .../TestFindSymbolContexts.py                 | 150 ++++++++++++++++++
 .../python_api/find_symbol_contexts/inlined.h |  19 +++
 .../python_api/find_symbol_contexts/main.cpp  |   6 +
 .../python_api/sblineentry/TestSBLineEntry.py |  50 ++++--
 lldb/unittests/API/SBLineEntryTest.cpp        |  11 +-
 llvm/docs/ReleaseNotes.md                     |   7 +
 16 files changed, 357 insertions(+), 13 deletions(-)
 create mode 100644 lldb/test/API/python_api/find_symbol_contexts/Makefile
 create mode 100644 lldb/test/API/python_api/find_symbol_contexts/TestFindSymbolContexts.py
 create mode 100644 lldb/test/API/python_api/find_symbol_contexts/inlined.h
 create mode 100644 lldb/test/API/python_api/find_symbol_contexts/main.cpp

diff --git a/lldb/bindings/interface/SBLineEntryDocstrings.i b/lldb/bindings/interface/SBLineEntryDocstrings.i
index 1a3aa48d31ffc..f4115d2227128 100644
--- a/lldb/bindings/interface/SBLineEntryDocstrings.i
+++ b/lldb/bindings/interface/SBLineEntryDocstrings.i
@@ -28,3 +28,6 @@ produces: ::
 
 See also :py:class:`SBCompileUnit` ."
 ) lldb::SBLineEntry;
+
+// Emit one Python wrapper for the default-argument overload.
+%feature("compactdefaultargs") lldb::SBLineEntry::SBLineEntry;
diff --git a/lldb/bindings/interface/SBModuleDocstrings.i b/lldb/bindings/interface/SBModuleDocstrings.i
index 289cf5335de62..fa1c9fb9f1bb2 100644
--- a/lldb/bindings/interface/SBModuleDocstrings.i
+++ b/lldb/bindings/interface/SBModuleDocstrings.i
@@ -212,3 +212,6 @@ produces this following output: ::
     relied upon. Use SBDebugger::MemoryPressureDetected instead to reduce
     LLDB's memory consumption during execution.
 ") lldb::SBModule::GarbageCollectAllocatedModules;
+
+// Emit one Python wrapper for the default-argument overload.
+%feature("compactdefaultargs") lldb::SBModule::FindSymbolContexts;
diff --git a/lldb/bindings/interface/SBTargetDocstrings.i b/lldb/bindings/interface/SBTargetDocstrings.i
index de03f8ea24b40..dac18e9fef77f 100644
--- a/lldb/bindings/interface/SBTargetDocstrings.i
+++ b/lldb/bindings/interface/SBTargetDocstrings.i
@@ -541,3 +541,6 @@ produces: ::
     :rtype: bool
     "
 ) lldb::SBTarget::IsLoaded;
+
+// Emit one Python wrapper for the default-argument overload.
+%feature("compactdefaultargs") lldb::SBTarget::FindSymbolContexts;
diff --git a/lldb/include/lldb/API/SBLineEntry.h b/lldb/include/lldb/API/SBLineEntry.h
index d70c4fac6ec71..c9b4dd10a597e 100644
--- a/lldb/include/lldb/API/SBLineEntry.h
+++ b/lldb/include/lldb/API/SBLineEntry.h
@@ -12,6 +12,7 @@
 #include "lldb/API/SBAddress.h"
 #include "lldb/API/SBDefines.h"
 #include "lldb/API/SBFileSpec.h"
+#include "lldb/lldb-defines.h"
 
 namespace lldb {
 
@@ -19,6 +20,10 @@ class LLDB_API SBLineEntry {
 public:
   SBLineEntry();
 
+  SBLineEntry(const lldb::SBFileSpec &filespec,
+              uint32_t line = LLDB_INVALID_LINE_NUMBER,
+              uint32_t column = LLDB_INVALID_COLUMN_NUMBER);
+
   SBLineEntry(const lldb::SBLineEntry &rhs);
 
   ~SBLineEntry();
diff --git a/lldb/include/lldb/API/SBModule.h b/lldb/include/lldb/API/SBModule.h
index 4bd3e899d154d..9f0ca8692cfc9 100644
--- a/lldb/include/lldb/API/SBModule.h
+++ b/lldb/include/lldb/API/SBModule.h
@@ -137,6 +137,30 @@ class LLDB_API SBModule {
   lldb::SBSymbolContextList
   FindCompileUnits(const lldb::SBFileSpec &sb_file_spec);
 
+  /// Find every symbol context in this module that maps to the source
+  /// location in \a line_entry. Each returned `SBSymbolContext` carries the
+  /// module, compile unit, function, block, and line entry for one match.
+  ///
+  /// \param[in] line_entry
+  ///     The source location to resolve.
+  ///
+  /// \param[in] resolve_scope
+  ///     A bitmask of `lldb::SymbolContextItem` values selecting which
+  ///     fields of each returned `SBSymbolContext` are populated. Defaults
+  ///     to `eSymbolContextEverything`.
+  ///
+  /// \param[in] check_inlines
+  ///     If true, also return inlined instances of the source file that live
+  ///     in other compile units.
+  ///
+  /// \return
+  ///     A list of matching symbol contexts, or an empty list if this module
+  ///     has no debug info for the requested location.
+  lldb::SBSymbolContextList FindSymbolContexts(
+      const lldb::SBLineEntry &line_entry,
+      lldb::SymbolContextItem resolve_scope = lldb::eSymbolContextEverything,
+      bool check_inlines = true);
+
   size_t GetNumSymbols();
 
   lldb::SBSymbol GetSymbolAtIndex(size_t idx);
diff --git a/lldb/include/lldb/API/SBTarget.h b/lldb/include/lldb/API/SBTarget.h
index 84cbcfb4e69d2..902a86d4f0fb2 100644
--- a/lldb/include/lldb/API/SBTarget.h
+++ b/lldb/include/lldb/API/SBTarget.h
@@ -353,6 +353,31 @@ class LLDB_API SBTarget {
   lldb::SBSymbolContextList
   FindCompileUnits(const lldb::SBFileSpec &sb_file_spec);
 
+  /// Find every symbol context in this target's modules that maps to the
+  /// source location in \a line_entry. Each returned `SBSymbolContext`
+  /// carries the module, compile unit, function, block, and line entry for
+  /// one match.
+  ///
+  /// \param[in] line_entry
+  ///     The source location to resolve.
+  ///
+  /// \param[in] resolve_scope
+  ///     A bitmask of `lldb::SymbolContextItem` values selecting which
+  ///     fields of each returned `SBSymbolContext` are populated. Defaults
+  ///     to `eSymbolContextEverything`.
+  ///
+  /// \param[in] check_inlines
+  ///     If true, also return inlined instances of the source file that live
+  ///     in other compile units.
+  ///
+  /// \return
+  ///     A list of matching symbol contexts, or an empty list if no module
+  ///     in this target has debug info for the requested location.
+  lldb::SBSymbolContextList FindSymbolContexts(
+      const lldb::SBLineEntry &line_entry,
+      lldb::SymbolContextItem resolve_scope = lldb::eSymbolContextEverything,
+      bool check_inlines = true);
+
   lldb::ByteOrder GetByteOrder();
 
   uint32_t GetAddressByteSize();
diff --git a/lldb/source/API/SBLineEntry.cpp b/lldb/source/API/SBLineEntry.cpp
index 22572941fccbd..b16e78231054b 100644
--- a/lldb/source/API/SBLineEntry.cpp
+++ b/lldb/source/API/SBLineEntry.cpp
@@ -13,14 +13,28 @@
 #include "lldb/Symbol/LineEntry.h"
 #include "lldb/Utility/Instrumentation.h"
 #include "lldb/Utility/StreamString.h"
+#include "lldb/lldb-defines.h"
 
 #include <climits>
+#include <memory>
 
 using namespace lldb;
 using namespace lldb_private;
 
 SBLineEntry::SBLineEntry() { LLDB_INSTRUMENT_VA(this); }
 
+SBLineEntry::SBLineEntry(const SBFileSpec &filespec, uint32_t line,
+                         uint32_t column)
+    : m_opaque_up(std::make_unique<LineEntry>()) {
+  LLDB_INSTRUMENT_VA(this, filespec, line, column);
+
+  SetFileSpec(filespec);
+  if (line != LLDB_INVALID_LINE_NUMBER)
+    SetLine(line);
+  if (column != LLDB_INVALID_COLUMN_NUMBER)
+    SetColumn(column);
+}
+
 SBLineEntry::SBLineEntry(const SBLineEntry &rhs) {
   LLDB_INSTRUMENT_VA(this, rhs);
 
@@ -105,7 +119,7 @@ SBFileSpec SBLineEntry::GetFileSpec() const {
 uint32_t SBLineEntry::GetLine() const {
   LLDB_INSTRUMENT_VA(this);
 
-  uint32_t line = 0;
+  uint32_t line = LLDB_INVALID_LINE_NUMBER;
   if (m_opaque_up)
     line = m_opaque_up->line;
 
@@ -117,7 +131,7 @@ uint32_t SBLineEntry::GetColumn() const {
 
   if (m_opaque_up)
     return m_opaque_up->column;
-  return 0;
+  return LLDB_INVALID_COLUMN_NUMBER;
 }
 
 void SBLineEntry::SetFileSpec(lldb::SBFileSpec filespec) {
diff --git a/lldb/source/API/SBModule.cpp b/lldb/source/API/SBModule.cpp
index d9a427d7e5d0c..d6a401914b50f 100644
--- a/lldb/source/API/SBModule.cpp
+++ b/lldb/source/API/SBModule.cpp
@@ -290,6 +290,27 @@ SBSymbolContextList SBModule::FindCompileUnits(const SBFileSpec &sb_file_spec) {
   return sb_sc_list;
 }
 
+SBSymbolContextList
+SBModule::FindSymbolContexts(const SBLineEntry &line_entry,
+                             lldb::SymbolContextItem resolve_scope,
+                             bool check_inlines) {
+  LLDB_INSTRUMENT_VA(this, line_entry, resolve_scope, check_inlines);
+
+  SBSymbolContextList sc_list;
+  const ModuleSP module_sp(GetSP());
+  if (!module_sp || !line_entry.IsValid())
+    return sc_list;
+
+  SBFileSpec file_spec = line_entry.GetFileSpec();
+  if (!file_spec.IsValid())
+    return sc_list;
+
+  // TODO: Pass the column when ResolveSymbolContextsForFileSpec supports it.
+  module_sp->ResolveSymbolContextsForFileSpec(
+      *file_spec, line_entry.GetLine(), check_inlines, resolve_scope, *sc_list);
+  return sc_list;
+}
+
 static Symtab *GetUnifiedSymbolTable(const lldb::ModuleSP &module_sp) {
   if (module_sp)
     return module_sp->GetSymtab();
diff --git a/lldb/source/API/SBTarget.cpp b/lldb/source/API/SBTarget.cpp
index b6ee17091ef6b..7a8d01c0e63a8 100644
--- a/lldb/source/API/SBTarget.cpp
+++ b/lldb/source/API/SBTarget.cpp
@@ -13,6 +13,7 @@
 #include "lldb/API/SBEvent.h"
 #include "lldb/API/SBExpressionOptions.h"
 #include "lldb/API/SBFileSpec.h"
+#include "lldb/API/SBLineEntry.h"
 #include "lldb/API/SBListener.h"
 #include "lldb/API/SBModule.h"
 #include "lldb/API/SBModuleSpec.h"
@@ -1687,6 +1688,27 @@ SBSymbolContextList SBTarget::FindCompileUnits(const SBFileSpec &sb_file_spec) {
   return sb_sc_list;
 }
 
+SBSymbolContextList
+SBTarget::FindSymbolContexts(const SBLineEntry &line_entry,
+                             lldb::SymbolContextItem resolve_scope,
+                             bool check_inlines) {
+  LLDB_INSTRUMENT_VA(this, line_entry, resolve_scope, check_inlines);
+
+  SBSymbolContextList sc_list;
+  TargetSP target_sp = GetSP();
+  if (!target_sp || !line_entry.IsValid())
+    return sc_list;
+
+  SBFileSpec file_spec = line_entry.GetFileSpec();
+  if (!file_spec.IsValid())
+    return sc_list;
+
+  // TODO: Pass column when ResolveSymbolContextsForFileSpec supports it.
+  target_sp->GetImages().ResolveSymbolContextsForFileSpec(
+      *file_spec, line_entry.GetLine(), check_inlines, resolve_scope, *sc_list);
+  return sc_list;
+}
+
 lldb::ByteOrder SBTarget::GetByteOrder() {
   LLDB_INSTRUMENT_VA(this);
 
diff --git a/lldb/test/API/python_api/find_symbol_contexts/Makefile b/lldb/test/API/python_api/find_symbol_contexts/Makefile
new file mode 100644
index 0000000000000..99998b20bcb05
--- /dev/null
+++ b/lldb/test/API/python_api/find_symbol_contexts/Makefile
@@ -0,0 +1,3 @@
+CXX_SOURCES := main.cpp
+
+include Makefile.rules
diff --git a/lldb/test/API/python_api/find_symbol_contexts/TestFindSymbolContexts.py b/lldb/test/API/python_api/find_symbol_contexts/TestFindSymbolContexts.py
new file mode 100644
index 0000000000000..22b06ddfd9498
--- /dev/null
+++ b/lldb/test/API/python_api/find_symbol_contexts/TestFindSymbolContexts.py
@@ -0,0 +1,150 @@
+"""Test SBTarget::FindSymbolContexts / SBModule::FindSymbolContexts APIs."""
+
+import lldb
+from lldbsuite.test.decorators import *
+from lldbsuite.test.lldbtest import *
+from lldbsuite.test import lldbutil
+
+
+class FindSymbolContextsAPITestCase(TestBase):
+    def test_finds_inlined_header_entry(self):
+        """
+        `FindSymbolContexts` should return the inlined instance of a
+        header-defined function even though the header is not the caller's
+        compile unit. The pre-existing `SBCompileUnit::GetLineEntryAtIndex`
+        walk cannot see those entries.
+        """
+        self.build()
+        target, *_ = lldbutil.run_to_source_breakpoint(
+            self, "// break here", lldb.SBFileSpec("main.cpp")
+        )
+        self.assertTrue(target, VALID_TARGET)
+
+        header_spec = lldb.SBFileSpec("inlined.h")
+        inlined_line = line_number("inlined.h", "// inlined body")
+
+        sb_line_entry = lldb.SBLineEntry(header_spec, inlined_line)
+        sc_list: lldb.SBSymbolContextList = target.FindSymbolContexts(sb_line_entry)
+        self.assertGreater(sc_list.GetSize(), 0)
+
+        for sc in sc_list:
+            self.assertTrue(sc.GetModule().IsValid())
+            self.assertTrue(sc.GetCompileUnit().IsValid())
+            self.assertTrue(sc.GetFunction().IsValid())
+            entry = sc.GetLineEntry()
+            self.assertTrue(entry.IsValid())
+            self.assertEqual(entry.GetLine(), inlined_line)
+            self.assertEqual(entry.GetFileSpec().GetFilename(), "inlined.h")
+
+    def test_resolve_scope_narrows_result(self):
+        """
+        A narrower resolve_scope populates only the requested field(s).
+
+        eSymbolContextCompUnit must always be in the mask: the DWARF
+        resolver gates its compile-unit walk on that bit
+        (SymbolFileDWARF::ResolveSymbolContext), so without it no matches
+        are produced at all.
+        """
+        self.build()
+        target, *_ = lldbutil.run_to_source_breakpoint(
+            self, "// break here", lldb.SBFileSpec("main.cpp")
+        )
+        src_location = lldb.SBLineEntry(
+            lldb.SBFileSpec("main.cpp"),
+            line_number("main.cpp", "// break here"),
+        )
+
+        comp_unit_only = target.FindSymbolContexts(
+            src_location, lldb.eSymbolContextCompUnit
+        )
+        self.assertGreater(comp_unit_only.GetSize(), 0)
+        for sc in comp_unit_only:
+            self.assertTrue(sc.GetCompileUnit().IsValid())
+
+        with_line_entry = target.FindSymbolContexts(
+            src_location,
+            lldb.eSymbolContextCompUnit | lldb.eSymbolContextLineEntry,
+        )
+        self.assertGreater(with_line_entry.GetSize(), 0)
+        for sc in with_line_entry:
+            self.assertTrue(sc.GetCompileUnit().IsValid())
+            self.assertTrue(sc.GetLineEntry().IsValid())
+
+        with_function = target.FindSymbolContexts(
+            src_location,
+            lldb.eSymbolContextCompUnit | lldb.eSymbolContextFunction,
+        )
+        self.assertGreater(with_function.GetSize(), 0)
+        for sc in with_function:
+            self.assertTrue(sc.GetCompileUnit().IsValid())
+            self.assertTrue(sc.GetFunction().IsValid())
+
+    def test_check_inlines_false(self):
+        """check_inlines=False excludes header inlines when the header is not a primary compile unit."""
+        self.build()
+        target, *_ = lldbutil.run_to_source_breakpoint(
+            self, "// break here", lldb.SBFileSpec("main.cpp")
+        )
+        header_spec = lldb.SBFileSpec("inlined.h")
+        inlined_line = line_number("inlined.h", "// inlined body")
+
+        sc_list = target.FindSymbolContexts(
+            lldb.SBLineEntry(header_spec, inlined_line),
+            lldb.eSymbolContextEverything,
+            False,
+        )
+        self.assertEqual(sc_list.GetSize(), 0)
+
+    def test_primary_source(self):
+        """Both check_inlines values return matches for a primary-CU line."""
+        self.build()
+        target, *_ = lldbutil.run_to_source_breakpoint(
+            self, "// break here", lldb.SBFileSpec("main.cpp")
+        )
+        main_spec = lldb.SBFileSpec("main.cpp")
+        break_line = line_number("main.cpp", "// break here")
+
+        for check_inlines in (True, False):
+            sc_list = target.FindSymbolContexts(
+                lldb.SBLineEntry(main_spec, break_line),
+                lldb.eSymbolContextEverything,
+                check_inlines,
+            )
+            self.assertGreater(sc_list.GetSize(), 0, f"check_inlines={check_inlines}")
+
+    def test_matches_between_target_and_module(self):
+        """SBTarget and SBModule return the same matches for a single-module executable."""
+        self.build()
+        target, *_ = lldbutil.run_to_source_breakpoint(
+            self, "// break here", lldb.SBFileSpec("main.cpp")
+        )
+
+        main_spec = lldb.SBFileSpec("main.cpp")
+        break_line = line_number("main.cpp", "// break here")
+        source_location = lldb.SBLineEntry(main_spec, break_line)
+
+        target_list = target.FindSymbolContexts(source_location)
+        self.assertGreater(target_list.GetSize(), 0)
+
+        module = target.FindModule(lldb.SBFileSpec("a.out"))
+        self.assertTrue(module.IsValid())
+        module_list = module.FindSymbolContexts(source_location)
+        self.assertEqual(target_list.GetSize(), module_list.GetSize())
+
+    def test_invalid_inputs(self):
+        """Invalid inputs returns an empty result."""
+        self.build()
+        target, *_ = lldbutil.run_to_source_breakpoint(
+            self, "// break here", lldb.SBFileSpec("main.cpp")
+        )
+        # Default SBLineEntry.
+        empty_entry = lldb.SBLineEntry()
+        self.assertFalse(empty_entry.IsValid())
+        self.assertEqual(target.FindSymbolContexts(empty_entry).GetSize(), 0)
+
+        empty_result = target.FindSymbolContexts(lldb.SBLineEntry(lldb.SBFileSpec(), 1))
+        self.assertEqual(empty_result.GetSize(), 0)
+
+        missing_line = lldb.SBLineEntry(lldb.SBFileSpec("main.cpp"))
+        missing_result = target.FindSymbolContexts(missing_line)
+        self.assertEqual(missing_result.GetSize(), 0)
diff --git a/lldb/test/API/python_api/find_symbol_contexts/inlined.h b/lldb/test/API/python_api/find_symbol_contexts/inlined.h
new file mode 100644
index 0000000000000..73b477a60cd8e
--- /dev/null
+++ b/lldb/test/API/python_api/find_symbol_contexts/inlined.h
@@ -0,0 +1,19 @@
+#ifndef INLINED_H
+#define INLINED_H
+
+#if defined(__clang__)
+#define ALWAYS_INLINE [[clang::always_inline]] inline
+#elif defined(_MSC_VER)
+#define ALWAYS_INLINE __forceinline
+#elif defined(__GNUC__)
+#define ALWAYS_INLINE __attribute__((always_inline)) inline
+#else
+#define ALWAYS_INLINE inline
+#endif
+
+ALWAYS_INLINE int inlined_add(int a, int b) {
+  int sum = a + b; // inlined body
+  return sum;
+}
+
+#endif
diff --git a/lldb/test/API/python_api/find_symbol_contexts/main.cpp b/lldb/test/API/python_api/find_symbol_contexts/main.cpp
new file mode 100644
index 0000000000000..97810a3cad738
--- /dev/null
+++ b/lldb/test/API/python_api/find_symbol_contexts/main.cpp
@@ -0,0 +1,6 @@
+#include "inlined.h"
+
+int main(int argc, char const *argv[]) {
+  int result = inlined_add(argc, 42); // break here
+  return result - result;
+}
diff --git a/lldb/test/API/python_api/sblineentry/TestSBLineEntry.py b/lldb/test/API/python_api/sblineentry/TestSBLineEntry.py
index ee8404c6b8560..33e44688c7b68 100644
--- a/lldb/test/API/python_api/sblineentry/TestSBLineEntry.py
+++ b/lldb/test/API/python_api/sblineentry/TestSBLineEntry.py
@@ -81,22 +81,52 @@ def test_line_entry_validity_without_address(self):
         self.assertFalse(line_entry.GetStartAddress().IsValid())
         self.assertFalse(line_entry.GetEndAddress().IsValid())
 
-    def test_line_entry_column(self):
-        """Test setting and getting column information on synthetic line entries."""
+    def test_line_entry_line_and_column(self):
+        """Test setting and getting line and column information on synthetic line entries."""
 
         line_entry = lldb.SBLineEntry()
-        line_entry.SetLine(50)
-
-        # Default column should be 0.
-        self.assertEqual(line_entry.GetColumn(), 0)
-
-        # Set column.
-        line_entry.SetColumn(25)
-        self.assertEqual(line_entry.GetColumn(), 25)
+        # Default line and column should be invalid.
+        self.assertEqual(line_entry.GetLine(), lldb.LLDB_INVALID_LINE_NUMBER)
+        self.assertEqual(line_entry.line, lldb.LLDB_INVALID_LINE_NUMBER)
+        self.assertEqual(line_entry.GetColumn(), lldb.LLDB_INVALID_COLUMN_NUMBER)
+        self.assertEqual(line_entry.column, lldb.LLDB_INVALID_COLUMN_NUMBER)
+
+        # Set line and column.
+        line = 50
+        column = 25
+        line_entry.SetLine(line)
+        line_entry.SetColumn(column)
+
+        # Verify line and column.
+        self.assertEqual(line_entry.GetLine(), line)
+        self.assertEqual(line_entry.line, line)
+        self.assertEqual(line_entry.GetColumn(), column)
+        self.assertEqual(line_entry.column, column)
 
         # Verify line entry is still valid.
         self.assertTrue(line_entry.IsValid())
 
+    def test_args_constructor(self):
+        """SBLineEntry(filespec, line, column) constructor with default args."""
+        filespec = lldb.SBFileSpec(self.getSourcePath("test.cpp"), True)
+        line = 10
+        column = 30
+
+        line_entry = lldb.SBLineEntry(filespec, line, column)
+        self.assertEqual(line_entry.GetFileSpec(), filespec)
+        self.assertEqual(line_entry.GetLine(), line)
+        self.assertEqual(line_entry.GetColumn(), column)
+
+        line_entry = lldb.SBLineEntry(filespec, line)
+        self.assertEqual(line_entry.GetFileSpec(), filespec)
+        self.assertEqual(line_entry.GetLine(), line)
+        self.assertEqual(line_entry.GetColumn(), lldb.LLDB_INVALID_COLUMN_NUMBER)
+
+        line_entry = lldb.SBLineEntry(filespec)
+        self.assertEqual(line_entry.GetFileSpec(), filespec)
+        self.assertEqual(line_entry.GetLine(), lldb.LLDB_INVALID_LINE_NUMBER)
+        self.assertEqual(line_entry.GetColumn(), lldb.LLDB_INVALID_COLUMN_NUMBER)
+
     def test_non_synthetic_line_entry_requires_line_number(self):
         """Test that non-synthetic line entries with addresses still require a line number to be valid."""
 
diff --git a/lldb/unittests/API/SBLineEntryTest.cpp b/lldb/unittests/API/SBLineEntryTest.cpp
index 518893b554bd1..17e62e142c6c4 100644
--- a/lldb/unittests/API/SBLineEntryTest.cpp
+++ b/lldb/unittests/API/SBLineEntryTest.cpp
@@ -21,6 +21,15 @@ TEST(SBLineEntryTest, SetLineAndColumn) {
   const uint32_t line_no = line_entry.GetLine();
   const uint32_t column_no = line_entry.GetColumn();
 
-  EXPECT_EQ(line_no, line_no);
+  EXPECT_EQ(line_no, expected_line_no);
   EXPECT_EQ(column_no, expected_column_no);
+
+  // Check args constructor.
+  lldb::SBFileSpec filespec("/some/random/path",
+                            /*resolve=*/false);
+  auto cline_entry =
+      lldb::SBLineEntry(filespec, expected_line_no, expected_column_no);
+  EXPECT_EQ(cline_entry.GetFileSpec(), filespec);
+  EXPECT_EQ(cline_entry.GetLine(), expected_line_no);
+  EXPECT_EQ(cline_entry.GetColumn(), expected_column_no);
 }
diff --git a/llvm/docs/ReleaseNotes.md b/llvm/docs/ReleaseNotes.md
index 4acfcaf3204a7..0b20b2a8f7322 100644
--- a/llvm/docs/ReleaseNotes.md
+++ b/llvm/docs/ReleaseNotes.md
@@ -298,6 +298,13 @@ Makes programs 10x faster by doing Special New Thing.
   return values that were actually in a different set. Both methods are now fixed
   so that they are limited to the registers within the register set. Scripts
   using these methods may have to be updated as a result.
+* New `SBTarget::FindSymbolContexts` and `SBModule::FindSymbolContexts`
+  return an `SBSymbolContextList` for every match of a given source location.
+* A new `SBLineEntry(SBFileSpec, line, column)` constructor lets
+  scripts build the location argument directly.
+* `SBLineEntry::GetLine()` and `SBLineEntry::GetColumn()` now return
+  `LLDB_INVALID_LINE_NUMBER` / `LLDB_INVALID_COLUMN_NUMBER` when unset;
+  scripts checking `entry.GetLine() == 0` should use `entry.IsValid()`.
 
 #### Windows
 



More information about the lldb-commits mailing list