[Lldb-commits] [lldb] [lldb][Expression] Emit hint to use --cpp-ignore-context-qualifiers (PR #177927)
Michael Buch via lldb-commits
lldb-commits at lists.llvm.org
Wed Feb 4 09:02:38 PST 2026
https://github.com/Michael137 updated https://github.com/llvm/llvm-project/pull/177927
>From 4adc40f712e657c71c7ac42160b18e9bedddafb9 Mon Sep 17 00:00:00 2001
From: Michael Buch <michaelbuch12 at gmail.com>
Date: Mon, 2 Feb 2026 10:41:14 +0000
Subject: [PATCH 01/20] [lldb][Expression] Add API to set/get language-specific
expression options
The motivation here is that we don't want to pollute the SBAPI with getters/setters for expression evaluation options that only apply to a single language. The ultimate goal would be to have plugins register additional options to the `expression` command when the plugin is loaded. This patch only provides the minimal `SBExpressionOptions` interface to set an option with an arbitrary name, which the language plugin knows how to interpret. The underlying options dictionary is an `StructuredData::Dictionary` so we can map strings to values of any type. But the SBAPI just exposes setting a boolean value. Future overloads of `SetLanguageOption` can provide setters for more types.
The boolean setter/getter will be used for the C++-specific option being introduced in: https://github.com/llvm/llvm-project/pull/177926
---
.../interface/SBExpressionOptionsDocstrings.i | 6 +++
lldb/include/lldb/API/SBExpressionOptions.h | 5 +++
lldb/include/lldb/Target/Target.h | 42 ++++++++++++++++++-
lldb/source/API/SBExpressionOptions.cpp | 12 ++++++
.../expression/options/TestExprOptions.py | 25 ++++++++++-
lldb/unittests/Expression/ExpressionTest.cpp | 25 +++++++++++
6 files changed, 112 insertions(+), 3 deletions(-)
diff --git a/lldb/bindings/interface/SBExpressionOptionsDocstrings.i b/lldb/bindings/interface/SBExpressionOptionsDocstrings.i
index 2bb562778db79..a6de6f034bd7a 100644
--- a/lldb/bindings/interface/SBExpressionOptionsDocstrings.i
+++ b/lldb/bindings/interface/SBExpressionOptionsDocstrings.i
@@ -61,3 +61,9 @@
%feature("docstring", "Sets whether to JIT an expression if it cannot be interpreted."
) lldb::SBExpressionOptions::SetAllowJIT;
+
+%feature("docstring", "Sets language-plugin specific boolean option for expression evaluation."
+) lldb::SBExpressionOptions::SetLanguageOption;
+
+%feature("docstring", "Gets language-plugin specific boolean option for expression evaluation."
+) lldb::SBExpressionOptions::GetLanguageOptionAsBoolean;
diff --git a/lldb/include/lldb/API/SBExpressionOptions.h b/lldb/include/lldb/API/SBExpressionOptions.h
index a9e929a4c0bd9..6f7bd53d6fa70 100644
--- a/lldb/include/lldb/API/SBExpressionOptions.h
+++ b/lldb/include/lldb/API/SBExpressionOptions.h
@@ -11,6 +11,7 @@
#include "lldb/API/SBDefines.h"
#include "lldb/API/SBLanguages.h"
+#include "lldb/API/SBStructuredData.h"
#include <vector>
@@ -107,6 +108,10 @@ class LLDB_API SBExpressionOptions {
// Sets whether we will JIT an expression if it cannot be interpreted
void SetAllowJIT(bool allow);
+ bool GetLanguageOptionAsBoolean(const char *option_name) const;
+
+ void SetLanguageOption(const char *option_name, bool value);
+
protected:
lldb_private::EvaluateExpressionOptions *get() const;
diff --git a/lldb/include/lldb/Target/Target.h b/lldb/include/lldb/Target/Target.h
index 812a638910b3b..2583c14965e61 100644
--- a/lldb/include/lldb/Target/Target.h
+++ b/lldb/include/lldb/Target/Target.h
@@ -15,6 +15,7 @@
#include <string>
#include <vector>
+#include "lldb/API/SBStructuredData.h"
#include "lldb/Breakpoint/BreakpointList.h"
#include "lldb/Breakpoint/BreakpointName.h"
#include "lldb/Breakpoint/WatchpointList.h"
@@ -307,6 +308,8 @@ class TargetProperties : public Properties {
class EvaluateExpressionOptions {
public:
+ EvaluateExpressionOptions() : m_language_options_sp(std::make_shared<StructuredData::Dictionary>()) {}
+
// MSVC has a bug here that reports C4268: 'const' static/global data
// initialized with compiler generated default constructor fills the object
// with zeros. Confirmed that MSVC is *not* zero-initializing, it's just a
@@ -323,8 +326,6 @@ class EvaluateExpressionOptions {
static constexpr ExecutionPolicy default_execution_policy =
eExecutionPolicyOnlyWhenNeeded;
- EvaluateExpressionOptions() = default;
-
ExecutionPolicy GetExecutionPolicy() const { return m_execution_policy; }
void SetExecutionPolicy(ExecutionPolicy policy = eExecutionPolicyAlways) {
@@ -481,7 +482,40 @@ class EvaluateExpressionOptions {
void SetIsForUtilityExpr(bool b) { m_running_utility_expression = b; }
+ /// Set language-plugin specific option called \c option_name to
+ /// the specified boolean \c value.
+ void SetLanguageOption(llvm::StringRef option_name, bool value) {
+ if (option_name.empty())
+ return;
+
+ GetLanguageOptions().AddBooleanItem(option_name, value);
+ }
+
+ /// Get the language-plugin specific boolean option called \c option_name.
+ ///
+ /// If the option doesn't exist or is not a boolean option, returns false.
+ /// Otherwise returns the boolean value of the option.
+ bool GetLanguageOptionAsBoolean(llvm::StringRef option_name) const {
+ bool result;
+ if (!GetLanguageOptions().GetValueForKeyAsBoolean(option_name, result))
+ return false;
+
+ return result;
+ }
+
private:
+ const StructuredData::Dictionary &GetLanguageOptions() const {
+ assert (m_language_options_sp);
+
+ return *m_language_options_sp;
+ }
+
+ StructuredData::Dictionary &GetLanguageOptions() {
+ assert (m_language_options_sp);
+
+ return *m_language_options_sp;
+ }
+
ExecutionPolicy m_execution_policy = default_execution_policy;
SourceLanguage m_language;
std::string m_prefix;
@@ -514,6 +548,10 @@ class EvaluateExpressionOptions {
mutable std::string m_pound_line_file;
mutable uint32_t m_pound_line_line = 0;
+ /// Dictionary mapping names of language-plugin specific options
+ /// to values.
+ StructuredData::DictionarySP m_language_options_sp = nullptr;
+
/// During expression evaluation, any SymbolContext in this list will be
/// used for symbol/function lookup before any other context (except for
/// the module corresponding to the current frame).
diff --git a/lldb/source/API/SBExpressionOptions.cpp b/lldb/source/API/SBExpressionOptions.cpp
index 15ed403eaaea1..e28c7ebfd0133 100644
--- a/lldb/source/API/SBExpressionOptions.cpp
+++ b/lldb/source/API/SBExpressionOptions.cpp
@@ -256,6 +256,18 @@ void SBExpressionOptions::SetAllowJIT(bool allow) {
: eExecutionPolicyNever);
}
+bool SBExpressionOptions::GetLanguageOptionAsBoolean(const char *option_name) const {
+ LLDB_INSTRUMENT_VA(this, option_name);
+
+ return m_opaque_up->GetLanguageOptionAsBoolean(option_name);
+}
+
+void SBExpressionOptions::SetLanguageOption(const char *option_name, bool value) {
+ LLDB_INSTRUMENT_VA(this, option_name, value);
+
+ m_opaque_up->SetLanguageOption(option_name, value);
+ }
+
EvaluateExpressionOptions *SBExpressionOptions::get() const {
return m_opaque_up.get();
}
diff --git a/lldb/test/API/commands/expression/options/TestExprOptions.py b/lldb/test/API/commands/expression/options/TestExprOptions.py
index 01899f3b97cf4..96bb4742a1a10 100644
--- a/lldb/test/API/commands/expression/options/TestExprOptions.py
+++ b/lldb/test/API/commands/expression/options/TestExprOptions.py
@@ -7,7 +7,6 @@
Test expression command options.
"""
-
import lldb
import lldbsuite.test.lldbutil as lldbutil
from lldbsuite.test.decorators import *
@@ -85,3 +84,27 @@ def test_expr_options_lang(self):
val = frame.EvaluateExpression("id == 0", options)
self.assertTrue(val.IsValid())
self.assertFalse(val.GetError().Success())
+
+ def test_expr_options_language_options(self):
+ """Test SetLanguageOption/GetLanguageOption SBAPIs"""
+
+ options = lldb.SBExpressionOptions()
+ self.assertFalse(options.GetLanguageOptionAsBoolean("foo"))
+ self.assertFalse(options.GetLanguageOptionAsBoolean("bar"))
+
+ options.SetLanguageOption("foo", True)
+ options.SetLanguageOption("bar", True)
+ self.assertTrue(options.GetLanguageOptionAsBoolean("foo"))
+ self.assertTrue(options.GetLanguageOptionAsBoolean("bar"))
+
+ options.SetLanguageOption("foo", False)
+ options.SetLanguageOption("bar", False)
+ self.assertFalse(options.GetLanguageOptionAsBoolean("foo"))
+ self.assertFalse(options.GetLanguageOptionAsBoolean("bar"))
+
+ self.assertFalse(options.GetLanguageOptionAsBoolean(""))
+ options.SetLanguageOption("", True)
+ self.assertFalse(options.GetLanguageOptionAsBoolean(""))
+
+ options.SetLanguageOption(None, True)
+ self.assertFalse(options.GetLanguageOptionAsBoolean(None))
diff --git a/lldb/unittests/Expression/ExpressionTest.cpp b/lldb/unittests/Expression/ExpressionTest.cpp
index ceb567c28ab99..4201516dfc820 100644
--- a/lldb/unittests/Expression/ExpressionTest.cpp
+++ b/lldb/unittests/Expression/ExpressionTest.cpp
@@ -10,6 +10,7 @@
#include "gtest/gtest.h"
#include "TestingSupport/TestUtilities.h"
+#include "lldb/Target/Target.h"
#include "lldb/Expression/Expression.h"
#include "llvm/Testing/Support/Error.h"
@@ -127,3 +128,27 @@ TEST_P(ExpressionTestFixture, FunctionCallLabel) {
INSTANTIATE_TEST_SUITE_P(FunctionCallLabelTest, ExpressionTestFixture,
testing::ValuesIn(g_label_test_cases));
+
+TEST(ExpressionTests, ExpressionOptions_Basic) {
+ EvaluateExpressionOptions options;
+
+ ASSERT_FALSE(options.GetLanguageOptionAsBoolean("foo"));
+ ASSERT_FALSE(options.GetLanguageOptionAsBoolean("bar"));
+
+ options.SetLanguageOption("foo", true);
+ options.SetLanguageOption("bar", true);
+
+ ASSERT_TRUE(options.GetLanguageOptionAsBoolean("foo"));
+ ASSERT_TRUE(options.GetLanguageOptionAsBoolean("bar"));
+
+ options.SetLanguageOption("foo", false);
+ options.SetLanguageOption("bar", false);
+
+ ASSERT_FALSE(options.GetLanguageOptionAsBoolean("foo"));
+ ASSERT_FALSE(options.GetLanguageOptionAsBoolean("bar"));
+
+ // Empty option names not allowed.
+ ASSERT_FALSE(options.GetLanguageOptionAsBoolean(""));
+ options.SetLanguageOption("", true);
+ ASSERT_FALSE(options.GetLanguageOptionAsBoolean(""));
+}
>From f1d8205d7923773f343e54105c7f60300851e123 Mon Sep 17 00:00:00 2001
From: Michael Buch <michaelbuch12 at gmail.com>
Date: Mon, 2 Feb 2026 11:25:33 +0000
Subject: [PATCH 02/20] fixup! clang-format
---
lldb/include/lldb/API/SBExpressionOptions.h | 2 +-
lldb/include/lldb/Target/Target.h | 7 ++++---
lldb/source/API/SBExpressionOptions.cpp | 8 +++++---
lldb/unittests/Expression/ExpressionTest.cpp | 2 +-
4 files changed, 11 insertions(+), 8 deletions(-)
diff --git a/lldb/include/lldb/API/SBExpressionOptions.h b/lldb/include/lldb/API/SBExpressionOptions.h
index 6f7bd53d6fa70..f0a37589fabe4 100644
--- a/lldb/include/lldb/API/SBExpressionOptions.h
+++ b/lldb/include/lldb/API/SBExpressionOptions.h
@@ -109,7 +109,7 @@ class LLDB_API SBExpressionOptions {
void SetAllowJIT(bool allow);
bool GetLanguageOptionAsBoolean(const char *option_name) const;
-
+
void SetLanguageOption(const char *option_name, bool value);
protected:
diff --git a/lldb/include/lldb/Target/Target.h b/lldb/include/lldb/Target/Target.h
index 2583c14965e61..67581e745c3d4 100644
--- a/lldb/include/lldb/Target/Target.h
+++ b/lldb/include/lldb/Target/Target.h
@@ -308,7 +308,8 @@ class TargetProperties : public Properties {
class EvaluateExpressionOptions {
public:
- EvaluateExpressionOptions() : m_language_options_sp(std::make_shared<StructuredData::Dictionary>()) {}
+ EvaluateExpressionOptions()
+ : m_language_options_sp(std::make_shared<StructuredData::Dictionary>()) {}
// MSVC has a bug here that reports C4268: 'const' static/global data
// initialized with compiler generated default constructor fills the object
@@ -505,13 +506,13 @@ class EvaluateExpressionOptions {
private:
const StructuredData::Dictionary &GetLanguageOptions() const {
- assert (m_language_options_sp);
+ assert(m_language_options_sp);
return *m_language_options_sp;
}
StructuredData::Dictionary &GetLanguageOptions() {
- assert (m_language_options_sp);
+ assert(m_language_options_sp);
return *m_language_options_sp;
}
diff --git a/lldb/source/API/SBExpressionOptions.cpp b/lldb/source/API/SBExpressionOptions.cpp
index e28c7ebfd0133..98a55733fbc3c 100644
--- a/lldb/source/API/SBExpressionOptions.cpp
+++ b/lldb/source/API/SBExpressionOptions.cpp
@@ -256,17 +256,19 @@ void SBExpressionOptions::SetAllowJIT(bool allow) {
: eExecutionPolicyNever);
}
-bool SBExpressionOptions::GetLanguageOptionAsBoolean(const char *option_name) const {
+bool SBExpressionOptions::GetLanguageOptionAsBoolean(
+ const char *option_name) const {
LLDB_INSTRUMENT_VA(this, option_name);
return m_opaque_up->GetLanguageOptionAsBoolean(option_name);
}
-void SBExpressionOptions::SetLanguageOption(const char *option_name, bool value) {
+void SBExpressionOptions::SetLanguageOption(const char *option_name,
+ bool value) {
LLDB_INSTRUMENT_VA(this, option_name, value);
m_opaque_up->SetLanguageOption(option_name, value);
- }
+}
EvaluateExpressionOptions *SBExpressionOptions::get() const {
return m_opaque_up.get();
diff --git a/lldb/unittests/Expression/ExpressionTest.cpp b/lldb/unittests/Expression/ExpressionTest.cpp
index 4201516dfc820..8bf8b20bc5203 100644
--- a/lldb/unittests/Expression/ExpressionTest.cpp
+++ b/lldb/unittests/Expression/ExpressionTest.cpp
@@ -10,8 +10,8 @@
#include "gtest/gtest.h"
#include "TestingSupport/TestUtilities.h"
-#include "lldb/Target/Target.h"
#include "lldb/Expression/Expression.h"
+#include "lldb/Target/Target.h"
#include "llvm/Testing/Support/Error.h"
using namespace lldb_private;
>From e468faab2c9fdad045e7b479a7086bc9b39eab5e Mon Sep 17 00:00:00 2001
From: Michael Buch <michaelbuch12 at gmail.com>
Date: Mon, 2 Feb 2026 12:08:01 +0000
Subject: [PATCH 03/20] fixup! remove redundant include
---
lldb/include/lldb/API/SBExpressionOptions.h | 1 -
1 file changed, 1 deletion(-)
diff --git a/lldb/include/lldb/API/SBExpressionOptions.h b/lldb/include/lldb/API/SBExpressionOptions.h
index f0a37589fabe4..8ef4204c302c1 100644
--- a/lldb/include/lldb/API/SBExpressionOptions.h
+++ b/lldb/include/lldb/API/SBExpressionOptions.h
@@ -11,7 +11,6 @@
#include "lldb/API/SBDefines.h"
#include "lldb/API/SBLanguages.h"
-#include "lldb/API/SBStructuredData.h"
#include <vector>
>From a84c45136bef59abebf2b6b88f772adb5ab7f50e Mon Sep 17 00:00:00 2001
From: Michael Buch <michaelbuch12 at gmail.com>
Date: Mon, 2 Feb 2026 12:11:49 +0000
Subject: [PATCH 04/20] fixup! include correct header
---
lldb/include/lldb/Target/Target.h | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/lldb/include/lldb/Target/Target.h b/lldb/include/lldb/Target/Target.h
index 67581e745c3d4..0d71a70dac2fa 100644
--- a/lldb/include/lldb/Target/Target.h
+++ b/lldb/include/lldb/Target/Target.h
@@ -15,7 +15,6 @@
#include <string>
#include <vector>
-#include "lldb/API/SBStructuredData.h"
#include "lldb/Breakpoint/BreakpointList.h"
#include "lldb/Breakpoint/BreakpointName.h"
#include "lldb/Breakpoint/WatchpointList.h"
@@ -39,6 +38,7 @@
#include "lldb/Utility/Broadcaster.h"
#include "lldb/Utility/LLDBAssert.h"
#include "lldb/Utility/RealpathPrefixes.h"
+#include "lldb/Utility/StructuredData.h"
#include "lldb/Utility/Timeout.h"
#include "lldb/lldb-public.h"
#include "llvm/ADT/StringRef.h"
>From 2deb9036303f8e78036c3e8c10b8106138f61147 Mon Sep 17 00:00:00 2001
From: Michael Buch <michaelbuch12 at gmail.com>
Date: Tue, 3 Feb 2026 07:59:51 +0000
Subject: [PATCH 05/20] fixup! move definition to Target.cpp
---
lldb/include/lldb/Target/Target.h | 27 ++++-----------------------
lldb/source/Target/Target.cpp | 30 ++++++++++++++++++++++++++++++
2 files changed, 34 insertions(+), 23 deletions(-)
diff --git a/lldb/include/lldb/Target/Target.h b/lldb/include/lldb/Target/Target.h
index 0d71a70dac2fa..380972b706af9 100644
--- a/lldb/include/lldb/Target/Target.h
+++ b/lldb/include/lldb/Target/Target.h
@@ -485,37 +485,18 @@ class EvaluateExpressionOptions {
/// Set language-plugin specific option called \c option_name to
/// the specified boolean \c value.
- void SetLanguageOption(llvm::StringRef option_name, bool value) {
- if (option_name.empty())
- return;
-
- GetLanguageOptions().AddBooleanItem(option_name, value);
- }
+ void SetLanguageOption(llvm::StringRef option_name, bool value);
/// Get the language-plugin specific boolean option called \c option_name.
///
/// If the option doesn't exist or is not a boolean option, returns false.
/// Otherwise returns the boolean value of the option.
- bool GetLanguageOptionAsBoolean(llvm::StringRef option_name) const {
- bool result;
- if (!GetLanguageOptions().GetValueForKeyAsBoolean(option_name, result))
- return false;
-
- return result;
- }
+ bool GetLanguageOptionAsBoolean(llvm::StringRef option_name) const;
private:
- const StructuredData::Dictionary &GetLanguageOptions() const {
- assert(m_language_options_sp);
+ const StructuredData::Dictionary &GetLanguageOptions() const;
- return *m_language_options_sp;
- }
-
- StructuredData::Dictionary &GetLanguageOptions() {
- assert(m_language_options_sp);
-
- return *m_language_options_sp;
- }
+ StructuredData::Dictionary &GetLanguageOptions();
ExecutionPolicy m_execution_policy = default_execution_policy;
SourceLanguage m_language;
diff --git a/lldb/source/Target/Target.cpp b/lldb/source/Target/Target.cpp
index f3e058c6cbc9b..658078d9def6f 100644
--- a/lldb/source/Target/Target.cpp
+++ b/lldb/source/Target/Target.cpp
@@ -5357,3 +5357,33 @@ void Target::NotifyBreakpointChanged(
if (EventTypeHasListeners(Target::eBroadcastBitBreakpointChanged))
BroadcastEvent(Target::eBroadcastBitBreakpointChanged, breakpoint_data_sp);
}
+
+void EvaluateExpressionOptions::SetLanguageOption(llvm::StringRef option_name,
+ bool value) {
+ if (option_name.empty())
+ return;
+
+ GetLanguageOptions().AddBooleanItem(option_name, value);
+}
+
+bool EvaluateExpressionOptions::GetLanguageOptionAsBoolean(
+ llvm::StringRef option_name) const {
+ bool result;
+ if (!GetLanguageOptions().GetValueForKeyAsBoolean(option_name, result))
+ return false;
+
+ return result;
+}
+
+const StructuredData::Dictionary &
+EvaluateExpressionOptions::GetLanguageOptions() const {
+ assert(m_language_options_sp);
+
+ return *m_language_options_sp;
+}
+
+StructuredData::Dictionary &EvaluateExpressionOptions::GetLanguageOptions() {
+ assert(m_language_options_sp);
+
+ return *m_language_options_sp;
+}
>From e1591e342a5f0893ce158e45a805bfebc0a14249 Mon Sep 17 00:00:00 2001
From: Michael Buch <michaelbuch12 at gmail.com>
Date: Tue, 3 Feb 2026 10:57:11 +0000
Subject: [PATCH 06/20] fixup! rename APIs; add SBError to APIs
---
.../interface/SBExpressionOptionsDocstrings.i | 4 +-
lldb/include/lldb/API/SBExpressionOptions.h | 4 +-
lldb/include/lldb/Target/Target.h | 5 +-
lldb/source/API/SBExpressionOptions.cpp | 29 +++++++++---
lldb/source/Target/Target.cpp | 22 ++++++---
.../expression/options/TestExprOptions.py | 47 ++++++++++++-------
6 files changed, 74 insertions(+), 37 deletions(-)
diff --git a/lldb/bindings/interface/SBExpressionOptionsDocstrings.i b/lldb/bindings/interface/SBExpressionOptionsDocstrings.i
index a6de6f034bd7a..ae897a6aa5bcd 100644
--- a/lldb/bindings/interface/SBExpressionOptionsDocstrings.i
+++ b/lldb/bindings/interface/SBExpressionOptionsDocstrings.i
@@ -63,7 +63,7 @@
) lldb::SBExpressionOptions::SetAllowJIT;
%feature("docstring", "Sets language-plugin specific boolean option for expression evaluation."
-) lldb::SBExpressionOptions::SetLanguageOption;
+) lldb::SBExpressionOptions::SetBooleanLanguageOption;
%feature("docstring", "Gets language-plugin specific boolean option for expression evaluation."
-) lldb::SBExpressionOptions::GetLanguageOptionAsBoolean;
+) lldb::SBExpressionOptions::GetBooleanLanguageOption;
diff --git a/lldb/include/lldb/API/SBExpressionOptions.h b/lldb/include/lldb/API/SBExpressionOptions.h
index 8ef4204c302c1..edfdbb5aaf62f 100644
--- a/lldb/include/lldb/API/SBExpressionOptions.h
+++ b/lldb/include/lldb/API/SBExpressionOptions.h
@@ -107,9 +107,9 @@ class LLDB_API SBExpressionOptions {
// Sets whether we will JIT an expression if it cannot be interpreted
void SetAllowJIT(bool allow);
- bool GetLanguageOptionAsBoolean(const char *option_name) const;
+ bool GetBooleanLanguageOption(const char *option_name, SBError &error) const;
- void SetLanguageOption(const char *option_name, bool value);
+ SBError SetBooleanLanguageOption(const char *option_name, bool value);
protected:
lldb_private::EvaluateExpressionOptions *get() const;
diff --git a/lldb/include/lldb/Target/Target.h b/lldb/include/lldb/Target/Target.h
index 380972b706af9..a79d0f1b6113e 100644
--- a/lldb/include/lldb/Target/Target.h
+++ b/lldb/include/lldb/Target/Target.h
@@ -485,13 +485,14 @@ class EvaluateExpressionOptions {
/// Set language-plugin specific option called \c option_name to
/// the specified boolean \c value.
- void SetLanguageOption(llvm::StringRef option_name, bool value);
+ llvm::Error SetBooleanLanguageOption(llvm::StringRef option_name, bool value);
/// Get the language-plugin specific boolean option called \c option_name.
///
/// If the option doesn't exist or is not a boolean option, returns false.
/// Otherwise returns the boolean value of the option.
- bool GetLanguageOptionAsBoolean(llvm::StringRef option_name) const;
+ llvm::Expected<bool>
+ GetBooleanLanguageOption(llvm::StringRef option_name) const;
private:
const StructuredData::Dictionary &GetLanguageOptions() const;
diff --git a/lldb/source/API/SBExpressionOptions.cpp b/lldb/source/API/SBExpressionOptions.cpp
index 98a55733fbc3c..908f69a4cba3f 100644
--- a/lldb/source/API/SBExpressionOptions.cpp
+++ b/lldb/source/API/SBExpressionOptions.cpp
@@ -8,6 +8,7 @@
#include "lldb/API/SBExpressionOptions.h"
#include "Utils.h"
+#include "lldb/API/SBError.h"
#include "lldb/API/SBStream.h"
#include "lldb/Target/Target.h"
#include "lldb/Utility/Instrumentation.h"
@@ -256,18 +257,32 @@ void SBExpressionOptions::SetAllowJIT(bool allow) {
: eExecutionPolicyNever);
}
-bool SBExpressionOptions::GetLanguageOptionAsBoolean(
- const char *option_name) const {
- LLDB_INSTRUMENT_VA(this, option_name);
+bool SBExpressionOptions::GetBooleanLanguageOption(const char *option_name,
+ SBError &error) const {
+ LLDB_INSTRUMENT_VA(this, option_name, error);
- return m_opaque_up->GetLanguageOptionAsBoolean(option_name);
+ error.Clear();
+
+ auto value_or_err = m_opaque_up->GetBooleanLanguageOption(option_name);
+ if (!value_or_err) {
+ error.SetErrorString(llvm::toString(value_or_err.takeError()).c_str());
+ return false;
+ }
+
+ return *value_or_err;
}
-void SBExpressionOptions::SetLanguageOption(const char *option_name,
- bool value) {
+SBError SBExpressionOptions::SetBooleanLanguageOption(const char *option_name,
+ bool value) {
LLDB_INSTRUMENT_VA(this, option_name, value);
- m_opaque_up->SetLanguageOption(option_name, value);
+ SBError error;
+
+ if (llvm::Error err =
+ m_opaque_up->SetBooleanLanguageOption(option_name, value))
+ error.SetErrorString(llvm::toString(std::move(err)).c_str());
+
+ return error;
}
EvaluateExpressionOptions *SBExpressionOptions::get() const {
diff --git a/lldb/source/Target/Target.cpp b/lldb/source/Target/Target.cpp
index 658078d9def6f..796318d9cb7da 100644
--- a/lldb/source/Target/Target.cpp
+++ b/lldb/source/Target/Target.cpp
@@ -70,6 +70,7 @@
#include "llvm/ADT/ScopeExit.h"
#include "llvm/ADT/SetVector.h"
+#include "llvm/Support/ErrorExtras.h"
#include "llvm/Support/ThreadPool.h"
#include <memory>
@@ -5358,19 +5359,28 @@ void Target::NotifyBreakpointChanged(
BroadcastEvent(Target::eBroadcastBitBreakpointChanged, breakpoint_data_sp);
}
-void EvaluateExpressionOptions::SetLanguageOption(llvm::StringRef option_name,
- bool value) {
+llvm::Error
+EvaluateExpressionOptions::SetBooleanLanguageOption(llvm::StringRef option_name,
+ bool value) {
if (option_name.empty())
- return;
+ return llvm::createStringError("Can't set an option with an empty name.");
GetLanguageOptions().AddBooleanItem(option_name, value);
+
+ return llvm::Error::success();
}
-bool EvaluateExpressionOptions::GetLanguageOptionAsBoolean(
+llvm::Expected<bool> EvaluateExpressionOptions::GetBooleanLanguageOption(
llvm::StringRef option_name) const {
+ const StructuredData::Dictionary &opts = GetLanguageOptions();
+
+ if (!opts.HasKey(option_name))
+ return llvm::createStringErrorV("Option {0} does not exist.", option_name);
+
bool result;
- if (!GetLanguageOptions().GetValueForKeyAsBoolean(option_name, result))
- return false;
+ if (!opts.GetValueForKeyAsBoolean(option_name, result))
+ return llvm::createStringErrorV("Failed to get option {0} as boolean.",
+ option_name);
return result;
}
diff --git a/lldb/test/API/commands/expression/options/TestExprOptions.py b/lldb/test/API/commands/expression/options/TestExprOptions.py
index 96bb4742a1a10..c2c37152e64ae 100644
--- a/lldb/test/API/commands/expression/options/TestExprOptions.py
+++ b/lldb/test/API/commands/expression/options/TestExprOptions.py
@@ -88,23 +88,34 @@ def test_expr_options_lang(self):
def test_expr_options_language_options(self):
"""Test SetLanguageOption/GetLanguageOption SBAPIs"""
+ error = lldb.SBError()
options = lldb.SBExpressionOptions()
- self.assertFalse(options.GetLanguageOptionAsBoolean("foo"))
- self.assertFalse(options.GetLanguageOptionAsBoolean("bar"))
- options.SetLanguageOption("foo", True)
- options.SetLanguageOption("bar", True)
- self.assertTrue(options.GetLanguageOptionAsBoolean("foo"))
- self.assertTrue(options.GetLanguageOptionAsBoolean("bar"))
-
- options.SetLanguageOption("foo", False)
- options.SetLanguageOption("bar", False)
- self.assertFalse(options.GetLanguageOptionAsBoolean("foo"))
- self.assertFalse(options.GetLanguageOptionAsBoolean("bar"))
-
- self.assertFalse(options.GetLanguageOptionAsBoolean(""))
- options.SetLanguageOption("", True)
- self.assertFalse(options.GetLanguageOptionAsBoolean(""))
-
- options.SetLanguageOption(None, True)
- self.assertFalse(options.GetLanguageOptionAsBoolean(None))
+ self.assertFalse(options.GetBooleanLanguageOption("foo", error))
+ self.assertTrue(error.Fail())
+ self.assertFalse(options.GetBooleanLanguageOption("bar", error))
+ self.assertTrue(error.Fail())
+
+ self.assertTrue(options.SetBooleanLanguageOption("foo", True).Success())
+ self.assertTrue(options.SetBooleanLanguageOption("bar", True).Success())
+ self.assertTrue(options.GetBooleanLanguageOption("foo", error))
+ self.assertTrue(error.Success())
+ self.assertTrue(options.GetBooleanLanguageOption("bar", error))
+ self.assertTrue(error.Success())
+
+ self.assertTrue(options.SetBooleanLanguageOption("foo", False).Success())
+ self.assertTrue(options.SetBooleanLanguageOption("bar", False).Success())
+ self.assertFalse(options.GetBooleanLanguageOption("foo", error))
+ self.assertTrue(error.Success())
+ self.assertFalse(options.GetBooleanLanguageOption("bar", error))
+ self.assertTrue(error.Success())
+
+ self.assertFalse(options.GetBooleanLanguageOption("", error))
+ self.assertTrue(error.Fail())
+ self.assertFalse(options.SetBooleanLanguageOption("", True).Fail())
+ self.assertFalse(options.GetBooleanLanguageOption("", error))
+ self.assertTrue(error.Fail())
+
+ self.assertFalse(options.SetBooleanLanguageOption(None, True).Fail())
+ self.assertFalse(options.GetBooleanLanguageOption(None, error))
+ self.assertTrue(error.Fail())
>From 1a0276b9e3670dc290041f1e0a3ea3b529b0c01b Mon Sep 17 00:00:00 2001
From: Michael Buch <michaelbuch12 at gmail.com>
Date: Tue, 3 Feb 2026 10:58:07 +0000
Subject: [PATCH 07/20] fixup! rename APIs; add SBError to APIs
---
lldb/test/API/commands/expression/options/TestExprOptions.py | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/lldb/test/API/commands/expression/options/TestExprOptions.py b/lldb/test/API/commands/expression/options/TestExprOptions.py
index c2c37152e64ae..ed4109b3e2f20 100644
--- a/lldb/test/API/commands/expression/options/TestExprOptions.py
+++ b/lldb/test/API/commands/expression/options/TestExprOptions.py
@@ -112,10 +112,10 @@ def test_expr_options_language_options(self):
self.assertFalse(options.GetBooleanLanguageOption("", error))
self.assertTrue(error.Fail())
- self.assertFalse(options.SetBooleanLanguageOption("", True).Fail())
+ self.assertTrue(options.SetBooleanLanguageOption("", True).Fail())
self.assertFalse(options.GetBooleanLanguageOption("", error))
self.assertTrue(error.Fail())
- self.assertFalse(options.SetBooleanLanguageOption(None, True).Fail())
+ self.assertTrue(options.SetBooleanLanguageOption(None, True).Fail())
self.assertFalse(options.GetBooleanLanguageOption(None, error))
self.assertTrue(error.Fail())
>From dc3a7efb50fb309f819c6820384578f2e63d7ce1 Mon Sep 17 00:00:00 2001
From: Michael Buch <michaelbuch12 at gmail.com>
Date: Tue, 3 Feb 2026 11:10:40 +0000
Subject: [PATCH 08/20] fixup! add unit-tests
---
lldb/source/Target/Target.cpp | 5 +-
lldb/unittests/Expression/ExpressionTest.cpp | 48 +++++++++++++-------
2 files changed, 34 insertions(+), 19 deletions(-)
diff --git a/lldb/source/Target/Target.cpp b/lldb/source/Target/Target.cpp
index 796318d9cb7da..6b6dfa4aebe2b 100644
--- a/lldb/source/Target/Target.cpp
+++ b/lldb/source/Target/Target.cpp
@@ -5375,11 +5375,12 @@ llvm::Expected<bool> EvaluateExpressionOptions::GetBooleanLanguageOption(
const StructuredData::Dictionary &opts = GetLanguageOptions();
if (!opts.HasKey(option_name))
- return llvm::createStringErrorV("Option {0} does not exist.", option_name);
+ return llvm::createStringErrorV("Option '{0}' does not exist.",
+ option_name);
bool result;
if (!opts.GetValueForKeyAsBoolean(option_name, result))
- return llvm::createStringErrorV("Failed to get option {0} as boolean.",
+ return llvm::createStringErrorV("Failed to get option '{0}' as boolean.",
option_name);
return result;
diff --git a/lldb/unittests/Expression/ExpressionTest.cpp b/lldb/unittests/Expression/ExpressionTest.cpp
index 8bf8b20bc5203..19647c162fa5a 100644
--- a/lldb/unittests/Expression/ExpressionTest.cpp
+++ b/lldb/unittests/Expression/ExpressionTest.cpp
@@ -132,23 +132,37 @@ INSTANTIATE_TEST_SUITE_P(FunctionCallLabelTest, ExpressionTestFixture,
TEST(ExpressionTests, ExpressionOptions_Basic) {
EvaluateExpressionOptions options;
- ASSERT_FALSE(options.GetLanguageOptionAsBoolean("foo"));
- ASSERT_FALSE(options.GetLanguageOptionAsBoolean("bar"));
-
- options.SetLanguageOption("foo", true);
- options.SetLanguageOption("bar", true);
-
- ASSERT_TRUE(options.GetLanguageOptionAsBoolean("foo"));
- ASSERT_TRUE(options.GetLanguageOptionAsBoolean("bar"));
-
- options.SetLanguageOption("foo", false);
- options.SetLanguageOption("bar", false);
-
- ASSERT_FALSE(options.GetLanguageOptionAsBoolean("foo"));
- ASSERT_FALSE(options.GetLanguageOptionAsBoolean("bar"));
+ EXPECT_THAT_EXPECTED(options.GetBooleanLanguageOption("foo"),
+ llvm::FailedWithMessage("Option 'foo' does not exist."));
+ EXPECT_THAT_EXPECTED(options.GetBooleanLanguageOption("bar"),
+ llvm::FailedWithMessage("Option 'bar' does not exist."));
+
+ EXPECT_THAT_ERROR(options.SetBooleanLanguageOption("foo", true),
+ llvm::Succeeded());
+ EXPECT_THAT_ERROR(options.SetBooleanLanguageOption("bar", false),
+ llvm::Succeeded());
+
+ EXPECT_THAT_EXPECTED(options.GetBooleanLanguageOption("foo"),
+ llvm::HasValue(true));
+ EXPECT_THAT_EXPECTED(options.GetBooleanLanguageOption("bar"),
+ llvm::HasValue(false));
+
+ EXPECT_THAT_ERROR(options.SetBooleanLanguageOption("foo", false),
+ llvm::Succeeded());
+ EXPECT_THAT_ERROR(options.SetBooleanLanguageOption("bar", true),
+ llvm::Succeeded());
+
+ EXPECT_THAT_EXPECTED(options.GetBooleanLanguageOption("foo"),
+ llvm::HasValue(false));
+ EXPECT_THAT_EXPECTED(options.GetBooleanLanguageOption("bar"),
+ llvm::HasValue(true));
// Empty option names not allowed.
- ASSERT_FALSE(options.GetLanguageOptionAsBoolean(""));
- options.SetLanguageOption("", true);
- ASSERT_FALSE(options.GetLanguageOptionAsBoolean(""));
+ EXPECT_THAT_EXPECTED(options.GetBooleanLanguageOption(""),
+ llvm::FailedWithMessage("Option '' does not exist."));
+ EXPECT_THAT_ERROR(
+ options.SetBooleanLanguageOption("", true),
+ llvm::FailedWithMessage("Can't set an option with an empty name."));
+ EXPECT_THAT_EXPECTED(options.GetBooleanLanguageOption(""),
+ llvm::FailedWithMessage("Option '' does not exist."));
}
>From 07026c6d2a710814a17b4d099d7c5753a9b5c6cb Mon Sep 17 00:00:00 2001
From: Michael Buch <michaelbuch12 at gmail.com>
Date: Wed, 21 Jan 2026 16:12:03 +0000
Subject: [PATCH 09/20] [lldb][Expression] Make __lldb_expr function qualifiers
match source context
This was originally removed in `8bdcd522510f923185cdfaec66c4a78d0a0d38c0` under the assumption this wasn't required (i.e., LLDB should just always pretend it's in a mutable context). But since function qualifiers affect overloading in C++, this assumption can lead to unexpected expression evaluator behaviour. Instead, in this patch we try to add function qualifiers to `$__lldb_class::$__lldb_expr` that resemble the qualifiers in the method that we're stopped in.
---
.../Clang/ClangExpressionDeclMap.cpp | 8 +-
.../Clang/ClangExpressionSourceCode.cpp | 43 +++++--
.../TestExprInsideLambdas.py | 4 +-
.../API/lang/cpp/const_this/TestConstThis.py | 10 +-
.../const_method/Makefile | 3 +
.../const_method/TestExprInConstMethod.py | 105 ++++++++++++++++++
.../const_method/main.cpp | 49 ++++++++
.../const_volatile_method/Makefile | 3 +
.../TestExprInConstVolatileMethod.py | 60 ++++++++++
.../const_volatile_method/main.cpp | 43 +++++++
.../cv_qualified_objects/Makefile | 3 +
.../TestExprOnCVQualifiedObjects.py | 21 ++++
.../cv_qualified_objects/main.cpp | 25 +++++
.../fixit/Makefile | 3 +
.../fixit/TestExprInConstMethodWithFixit.py | 42 +++++++
.../fixit/main.cpp | 22 ++++
.../non_const_method/Makefile | 3 +
.../TestExprInNonConstMethod.py | 82 ++++++++++++++
.../non_const_method/main.cpp | 49 ++++++++
.../template_const_method/Makefile | 3 +
.../TestExprInTemplateConstMethod.py | 105 ++++++++++++++++++
.../template_const_method/main.cpp | 51 +++++++++
.../template_non_const_method/Makefile | 3 +
.../TestExprInTemplateNonConstMethod.py | 82 ++++++++++++++
.../template_non_const_method/main.cpp | 49 ++++++++
lldb/test/API/lang/cpp/this/TestCPPThis.py | 16 ++-
26 files changed, 864 insertions(+), 23 deletions(-)
create mode 100644 lldb/test/API/lang/cpp/expression-context-qualifiers/const_method/Makefile
create mode 100644 lldb/test/API/lang/cpp/expression-context-qualifiers/const_method/TestExprInConstMethod.py
create mode 100644 lldb/test/API/lang/cpp/expression-context-qualifiers/const_method/main.cpp
create mode 100644 lldb/test/API/lang/cpp/expression-context-qualifiers/const_volatile_method/Makefile
create mode 100644 lldb/test/API/lang/cpp/expression-context-qualifiers/const_volatile_method/TestExprInConstVolatileMethod.py
create mode 100644 lldb/test/API/lang/cpp/expression-context-qualifiers/const_volatile_method/main.cpp
create mode 100644 lldb/test/API/lang/cpp/expression-context-qualifiers/cv_qualified_objects/Makefile
create mode 100644 lldb/test/API/lang/cpp/expression-context-qualifiers/cv_qualified_objects/TestExprOnCVQualifiedObjects.py
create mode 100644 lldb/test/API/lang/cpp/expression-context-qualifiers/cv_qualified_objects/main.cpp
create mode 100644 lldb/test/API/lang/cpp/expression-context-qualifiers/fixit/Makefile
create mode 100644 lldb/test/API/lang/cpp/expression-context-qualifiers/fixit/TestExprInConstMethodWithFixit.py
create mode 100644 lldb/test/API/lang/cpp/expression-context-qualifiers/fixit/main.cpp
create mode 100644 lldb/test/API/lang/cpp/expression-context-qualifiers/non_const_method/Makefile
create mode 100644 lldb/test/API/lang/cpp/expression-context-qualifiers/non_const_method/TestExprInNonConstMethod.py
create mode 100644 lldb/test/API/lang/cpp/expression-context-qualifiers/non_const_method/main.cpp
create mode 100644 lldb/test/API/lang/cpp/expression-context-qualifiers/template_const_method/Makefile
create mode 100644 lldb/test/API/lang/cpp/expression-context-qualifiers/template_const_method/TestExprInTemplateConstMethod.py
create mode 100644 lldb/test/API/lang/cpp/expression-context-qualifiers/template_const_method/main.cpp
create mode 100644 lldb/test/API/lang/cpp/expression-context-qualifiers/template_non_const_method/Makefile
create mode 100644 lldb/test/API/lang/cpp/expression-context-qualifiers/template_non_const_method/TestExprInTemplateNonConstMethod.py
create mode 100644 lldb/test/API/lang/cpp/expression-context-qualifiers/template_non_const_method/main.cpp
diff --git a/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionDeclMap.cpp b/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionDeclMap.cpp
index 664bcba0017a7..b1f246f6690e7 100644
--- a/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionDeclMap.cpp
+++ b/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionDeclMap.cpp
@@ -844,6 +844,12 @@ void ClangExpressionDeclMap::LookUpLldbClass(NameSearchContext &context) {
QualType class_qual_type = m_ast_context->getCanonicalTagType(class_decl);
+ // The synthesized __lldb_expr will adopt the qualifiers from this class
+ // type. Make sure we use the qualifiers of the method that we're currently
+ // stopped in.
+ class_qual_type.addFastQualifiers(
+ method_decl->getMethodQualifiers().getFastQualifiers());
+
TypeFromUser class_user_type(
class_qual_type.getAsOpaquePtr(),
function_decl_ctx.GetTypeSystem()->weak_from_this());
@@ -1991,7 +1997,7 @@ void ClangExpressionDeclMap::AddContextClassType(NameSearchContext &context,
std::array<CompilerType, 1> args{void_clang_type.GetPointerType()};
CompilerType method_type = m_clang_ast_context->CreateFunctionType(
- void_clang_type, args, false, 0);
+ void_clang_type, args, false, ut.GetTypeQualifiers());
const bool is_virtual = false;
const bool is_static = false;
diff --git a/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionSourceCode.cpp b/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionSourceCode.cpp
index cfe187ffc4114..ca4199faa3841 100644
--- a/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionSourceCode.cpp
+++ b/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionSourceCode.cpp
@@ -10,6 +10,7 @@
#include "ClangExpressionUtil.h"
+#include "clang/AST/TypeBase.h"
#include "clang/Basic/CharInfo.h"
#include "clang/Basic/FileManager.h"
#include "clang/Basic/SourceManager.h"
@@ -189,6 +190,28 @@ static void AddMacros(const DebugMacros *dm, CompileUnit *comp_unit,
}
}
+/// Return qualifers of the current C++ method.
+static clang::Qualifiers GetFrameCVQualifiers(StackFrame *frame) {
+ if (!frame)
+ return {};
+
+ auto this_sp = frame->FindVariable(ConstString("this"));
+ if (!this_sp)
+ return {};
+
+ // Lambdas that capture 'this' have a member variable called 'this'. The class
+ // context of __lldb_expr for a lambda is the class type of the 'this' capture
+ // (not the anonymous lambda structure). So use the qualifiers of the captured
+ // 'this'.
+ if (auto this_this_sp = this_sp->GetChildMemberWithName("this"))
+ return clang::Qualifiers::fromCVRMask(
+ this_this_sp->GetCompilerType().GetPointeeType().GetTypeQualifiers());
+
+ // Not in a lambda. Return 'this' qualifiers.
+ return clang::Qualifiers::fromCVRMask(
+ this_sp->GetCompilerType().GetPointeeType().GetTypeQualifiers());
+}
+
lldb_private::ClangExpressionSourceCode::ClangExpressionSourceCode(
llvm::StringRef filename, llvm::StringRef name, llvm::StringRef prefix,
llvm::StringRef body, Wrapping wrap, WrapKind wrap_kind)
@@ -463,15 +486,17 @@ bool ClangExpressionSourceCode::GetText(
lldb_local_var_decls.GetData(), tagged_body.c_str());
break;
case WrapKind::CppMemberFunction:
- wrap_stream.Printf("%s"
- "void \n"
- "$__lldb_class::%s(void *$__lldb_arg) \n"
- "{ \n"
- " %s; \n"
- "%s"
- "} \n",
- module_imports.c_str(), m_name.c_str(),
- lldb_local_var_decls.GetData(), tagged_body.c_str());
+ wrap_stream.Printf(
+ "%s"
+ "void \n"
+ "$__lldb_class::%s(void *$__lldb_arg) %s \n"
+ "{ \n"
+ " %s; \n"
+ "%s"
+ "} \n",
+ module_imports.c_str(), m_name.c_str(),
+ GetFrameCVQualifiers(exe_ctx.GetFramePtr()).getAsString().c_str(),
+ lldb_local_var_decls.GetData(), tagged_body.c_str());
break;
case WrapKind::ObjCInstanceMethod:
wrap_stream.Printf(
diff --git a/lldb/test/API/commands/expression/expr_inside_lambda/TestExprInsideLambdas.py b/lldb/test/API/commands/expression/expr_inside_lambda/TestExprInsideLambdas.py
index e35cfa6a289a7..0a7683b310f43 100644
--- a/lldb/test/API/commands/expression/expr_inside_lambda/TestExprInsideLambdas.py
+++ b/lldb/test/API/commands/expression/expr_inside_lambda/TestExprInsideLambdas.py
@@ -127,8 +127,8 @@ def test_expr_inside_lambda(self):
# Inside non_capturing_method
lldbutil.continue_to_breakpoint(process, bkpt)
- self.expect_expr("local", result_type="int", result_value="5")
- self.expect_expr("local2", result_type="int", result_value="10")
+ self.expect_expr("local", result_type="const int", result_value="5")
+ self.expect_expr("local2", result_type="const int", result_value="10")
self.expect_expr("local2 * local", result_type="int", result_value="50")
self.expectExprError(
diff --git a/lldb/test/API/lang/cpp/const_this/TestConstThis.py b/lldb/test/API/lang/cpp/const_this/TestConstThis.py
index 4b7d3aadb62ab..c2df61fde2b58 100644
--- a/lldb/test/API/lang/cpp/const_this/TestConstThis.py
+++ b/lldb/test/API/lang/cpp/const_this/TestConstThis.py
@@ -11,10 +11,9 @@ def run_class_tests(self):
# Expression not referencing context class.
self.expect_expr("1 + 1", result_type="int", result_value="2")
# Referencing context class.
- # FIXME: This and the expression below should return const types.
- self.expect_expr("member", result_type="int", result_value="3")
+ self.expect_expr("member", result_type="const int", result_value="3")
# Check the type of context class.
- self.expect_expr("this", result_type="ContextClass *")
+ self.expect_expr("this", result_type="const ContextClass *")
def test_member_func(self):
self.build()
@@ -36,10 +35,9 @@ def run_template_class_tests(self):
# Expression not referencing context class.
self.expect_expr("1 + 1", result_type="int", result_value="2")
# Referencing context class.
- # FIXME: This and the expression below should return const types.
- self.expect_expr("member", result_type="int", result_value="4")
+ self.expect_expr("member", result_type="const int", result_value="4")
# Check the type of context class.
- self.expect_expr("this", result_type="TemplatedContextClass<int> *")
+ self.expect_expr("this", result_type="const TemplatedContextClass<int> *")
def test_template_member_func(self):
self.build()
diff --git a/lldb/test/API/lang/cpp/expression-context-qualifiers/const_method/Makefile b/lldb/test/API/lang/cpp/expression-context-qualifiers/const_method/Makefile
new file mode 100644
index 0000000000000..99998b20bcb05
--- /dev/null
+++ b/lldb/test/API/lang/cpp/expression-context-qualifiers/const_method/Makefile
@@ -0,0 +1,3 @@
+CXX_SOURCES := main.cpp
+
+include Makefile.rules
diff --git a/lldb/test/API/lang/cpp/expression-context-qualifiers/const_method/TestExprInConstMethod.py b/lldb/test/API/lang/cpp/expression-context-qualifiers/const_method/TestExprInConstMethod.py
new file mode 100644
index 0000000000000..0d8f477dce1fd
--- /dev/null
+++ b/lldb/test/API/lang/cpp/expression-context-qualifiers/const_method/TestExprInConstMethod.py
@@ -0,0 +1,105 @@
+import lldb
+from lldbsuite.test.decorators import *
+from lldbsuite.test.lldbtest import *
+from lldbsuite.test import lldbutil
+
+
+class TestCase(TestBase):
+ def test(self):
+ self.build()
+ (_, process, _, _) = lldbutil.run_to_source_breakpoint(
+ self, "Break: const_method begin", lldb.SBFileSpec("main.cpp")
+ )
+
+ self.expect_expr("bar()", result_value="2", result_type="int")
+ self.expect(
+ "expression m_const_mem = 2.0",
+ error=True,
+ substrs=[
+ "cannot assign to non-static data member",
+ "with const-qualified type",
+ ],
+ )
+ self.expect(
+ "expression m_mem = 2.0",
+ error=True,
+ substrs=[
+ "cannot assign to non-static data member within const member function"
+ ],
+ )
+ self.expect_expr("((Foo*)this)->bar()", result_type="double", result_value="5")
+
+ lldbutil.continue_to_source_breakpoint(
+ self,
+ process,
+ "Break: const_method no-this lambda",
+ lldb.SBFileSpec("main.cpp"),
+ )
+
+ self.expect(
+ "expression x = 7.0",
+ error=True,
+ substrs=[
+ "cannot assign to non-static data member within const member function"
+ ],
+ )
+
+ lldbutil.continue_to_source_breakpoint(
+ self,
+ process,
+ "Break: const_method mutable no-this lambda",
+ lldb.SBFileSpec("main.cpp"),
+ )
+
+ self.expect_expr("x = 7.0; x", result_value="7")
+
+ lldbutil.continue_to_source_breakpoint(
+ self, process, "Break: const_method lambda", lldb.SBFileSpec("main.cpp")
+ )
+
+ # FIXME: mutating this capture should be disallowed in a non-mutable lambda.
+ self.expect_expr("y = 8.0")
+ self.expect_expr("bar()", result_value="2", result_type="int")
+ self.expect(
+ "expression m_const_mem = 2.0",
+ error=True,
+ substrs=[
+ "cannot assign to non-static data member",
+ "with const-qualified type",
+ ],
+ )
+ self.expect(
+ "expression m_mem = 2.0",
+ error=True,
+ substrs=[
+ "cannot assign to non-static data member within const member function"
+ ],
+ )
+ self.expect_expr("m_mem", result_value="-2")
+ self.expect_expr("((Foo*)this)->bar()", result_type="double", result_value="5")
+
+ lldbutil.continue_to_source_breakpoint(
+ self,
+ process,
+ "Break: const_method mutable lambda",
+ lldb.SBFileSpec("main.cpp"),
+ )
+
+ self.expect_expr("y = 9.0")
+ self.expect_expr("bar()", result_value="2", result_type="int")
+ self.expect(
+ "expression m_const_mem = 2.0",
+ error=True,
+ substrs=[
+ "cannot assign to non-static data member",
+ "with const-qualified type",
+ ],
+ )
+ self.expect(
+ "expression m_mem = 2.0",
+ error=True,
+ substrs=[
+ "cannot assign to non-static data member within const member function"
+ ],
+ )
+ self.expect_expr("m_mem", result_value="-2")
diff --git a/lldb/test/API/lang/cpp/expression-context-qualifiers/const_method/main.cpp b/lldb/test/API/lang/cpp/expression-context-qualifiers/const_method/main.cpp
new file mode 100644
index 0000000000000..7cb74767b458a
--- /dev/null
+++ b/lldb/test/API/lang/cpp/expression-context-qualifiers/const_method/main.cpp
@@ -0,0 +1,49 @@
+#include <cassert>
+#include <cstdio>
+
+struct Foo {
+ double bar() { return 5.0; }
+
+ int bar() const { return 2; }
+
+ int const_method() const {
+ auto x = bar();
+ assert(x == 2);
+ std::puts("Break: const_method begin");
+
+ [x] {
+ std::puts("Keep on multiple lines...");
+ std::puts("Break: const_method no-this lambda");
+ }();
+
+ [x]() mutable {
+ std::puts("Keep on multiple lines...");
+ std::puts("Break: const_method mutable no-this lambda");
+ }();
+
+ [this, y = x] {
+ auto x = bar() + y;
+ std::puts("Break: const_method lambda");
+ }();
+
+ [this, y = x]() mutable {
+ auto x = bar() + y;
+ std::puts("Break: const_method mutable lambda");
+ }();
+
+ return 120;
+ }
+
+ float m_mem = -2.0;
+ const float m_const_mem = -3.0;
+};
+
+int main() {
+ const Foo f;
+ f.bar();
+
+ Foo f2;
+ f2.bar();
+
+ return Foo{}.const_method();
+}
diff --git a/lldb/test/API/lang/cpp/expression-context-qualifiers/const_volatile_method/Makefile b/lldb/test/API/lang/cpp/expression-context-qualifiers/const_volatile_method/Makefile
new file mode 100644
index 0000000000000..99998b20bcb05
--- /dev/null
+++ b/lldb/test/API/lang/cpp/expression-context-qualifiers/const_volatile_method/Makefile
@@ -0,0 +1,3 @@
+CXX_SOURCES := main.cpp
+
+include Makefile.rules
diff --git a/lldb/test/API/lang/cpp/expression-context-qualifiers/const_volatile_method/TestExprInConstVolatileMethod.py b/lldb/test/API/lang/cpp/expression-context-qualifiers/const_volatile_method/TestExprInConstVolatileMethod.py
new file mode 100644
index 0000000000000..f20b9d8164841
--- /dev/null
+++ b/lldb/test/API/lang/cpp/expression-context-qualifiers/const_volatile_method/TestExprInConstVolatileMethod.py
@@ -0,0 +1,60 @@
+import lldb
+from lldbsuite.test.decorators import *
+from lldbsuite.test.lldbtest import *
+from lldbsuite.test import lldbutil
+
+
+class TestCase(TestBase):
+ def test(self):
+ self.build()
+ (_, process, _, _) = lldbutil.run_to_source_breakpoint(
+ self, "Break here: const", lldb.SBFileSpec("main.cpp")
+ )
+
+ self.expect_expr("bar()", result_type="double", result_value="5")
+ self.expect_expr("const_volatile_method()")
+ self.expect_expr("const_method()")
+ self.expect(
+ "expression volatile_method()",
+ error=True,
+ substrs=["has type 'const Foo'", "but function is not marked const"],
+ )
+
+ lldbutil.continue_to_source_breakpoint(
+ self, process, "Break here: volatile", lldb.SBFileSpec("main.cpp")
+ )
+
+ self.expect_expr(
+ "bar()", result_type="const char *", result_summary='"volatile_bar"'
+ )
+ self.expect_expr("const_volatile_method()")
+ self.expect(
+ "expression const_method()",
+ error=True,
+ substrs=["has type 'volatile Foo'", "but function is not marked volatile"],
+ )
+ self.expect_expr("volatile_method()")
+
+ lldbutil.continue_to_source_breakpoint(
+ self, process, "Break here: const volatile", lldb.SBFileSpec("main.cpp")
+ )
+
+ self.expect_expr("bar()", result_type="int", result_value="2")
+ self.expect_expr("other_cv_method()")
+
+ self.expect(
+ "expression const_method()",
+ error=True,
+ substrs=[
+ "has type 'const volatile Foo'",
+ "but function is not marked const or volatile",
+ ],
+ )
+ self.expect(
+ "expression volatile_method()",
+ error=True,
+ substrs=[
+ "has type 'const volatile Foo'",
+ "but function is not marked const or volatile",
+ ],
+ )
diff --git a/lldb/test/API/lang/cpp/expression-context-qualifiers/const_volatile_method/main.cpp b/lldb/test/API/lang/cpp/expression-context-qualifiers/const_volatile_method/main.cpp
new file mode 100644
index 0000000000000..da0f7e7d1be95
--- /dev/null
+++ b/lldb/test/API/lang/cpp/expression-context-qualifiers/const_volatile_method/main.cpp
@@ -0,0 +1,43 @@
+#include <cassert>
+#include <cstdio>
+
+struct Foo {
+ double bar() const { return 5.0; }
+ const char *bar() volatile { return "volatile_bar"; }
+ int bar() volatile const { return 2; }
+
+ int volatile_method() volatile {
+ std::puts("Break here: volatile");
+ return 0;
+ }
+ int const_method() const {
+ std::puts("Break here: const");
+ return 0;
+ }
+ int other_cv_method() const volatile { return 20; }
+
+ int const_volatile_method() const volatile {
+ auto x = bar();
+ assert(x == 2);
+ other_cv_method();
+
+ std::puts("Break here: const volatile");
+
+ return 120;
+ }
+};
+
+int main() {
+ const Foo f;
+ f.bar();
+ f.const_method();
+
+ volatile Foo f2;
+ f2.bar();
+ f2.volatile_method();
+
+ const volatile Foo f3;
+ f3.bar();
+
+ return Foo{}.const_volatile_method();
+}
diff --git a/lldb/test/API/lang/cpp/expression-context-qualifiers/cv_qualified_objects/Makefile b/lldb/test/API/lang/cpp/expression-context-qualifiers/cv_qualified_objects/Makefile
new file mode 100644
index 0000000000000..99998b20bcb05
--- /dev/null
+++ b/lldb/test/API/lang/cpp/expression-context-qualifiers/cv_qualified_objects/Makefile
@@ -0,0 +1,3 @@
+CXX_SOURCES := main.cpp
+
+include Makefile.rules
diff --git a/lldb/test/API/lang/cpp/expression-context-qualifiers/cv_qualified_objects/TestExprOnCVQualifiedObjects.py b/lldb/test/API/lang/cpp/expression-context-qualifiers/cv_qualified_objects/TestExprOnCVQualifiedObjects.py
new file mode 100644
index 0000000000000..0f661471df749
--- /dev/null
+++ b/lldb/test/API/lang/cpp/expression-context-qualifiers/cv_qualified_objects/TestExprOnCVQualifiedObjects.py
@@ -0,0 +1,21 @@
+import lldb
+from lldbsuite.test.decorators import *
+from lldbsuite.test.lldbtest import *
+from lldbsuite.test import lldbutil
+
+
+class TestCase(TestBase):
+ def test(self):
+ self.build()
+ lldbutil.run_to_source_breakpoint(
+ self,
+ "Break here",
+ lldb.SBFileSpec("main.cpp"),
+ )
+
+ self.expect_expr("f.bar()", result_type="double", result_value="5")
+ self.expect_expr("cf.bar()", result_type="int", result_value="2")
+ self.expect_expr("vf.bar()", result_type="short", result_value="8")
+ self.expect_expr(
+ "cvf.bar()", result_type="const char *", result_summary='"volatile"'
+ )
diff --git a/lldb/test/API/lang/cpp/expression-context-qualifiers/cv_qualified_objects/main.cpp b/lldb/test/API/lang/cpp/expression-context-qualifiers/cv_qualified_objects/main.cpp
new file mode 100644
index 0000000000000..eb7f8b82f2695
--- /dev/null
+++ b/lldb/test/API/lang/cpp/expression-context-qualifiers/cv_qualified_objects/main.cpp
@@ -0,0 +1,25 @@
+#include <cstdio>
+
+struct Foo {
+ double bar() { return 5.0; }
+ int bar() const { return 2; }
+ short bar() volatile { return 8; }
+ char const *bar() const volatile { return "volatile"; }
+
+ float m_mem = -2.0;
+ const float m_const_mem = -3.0;
+};
+
+int main() {
+ Foo f;
+ const Foo cf;
+ volatile Foo vf;
+ const volatile Foo cvf;
+
+ f.bar();
+ cf.bar();
+ vf.bar();
+ cvf.bar();
+
+ std::puts("Break here");
+}
diff --git a/lldb/test/API/lang/cpp/expression-context-qualifiers/fixit/Makefile b/lldb/test/API/lang/cpp/expression-context-qualifiers/fixit/Makefile
new file mode 100644
index 0000000000000..99998b20bcb05
--- /dev/null
+++ b/lldb/test/API/lang/cpp/expression-context-qualifiers/fixit/Makefile
@@ -0,0 +1,3 @@
+CXX_SOURCES := main.cpp
+
+include Makefile.rules
diff --git a/lldb/test/API/lang/cpp/expression-context-qualifiers/fixit/TestExprInConstMethodWithFixit.py b/lldb/test/API/lang/cpp/expression-context-qualifiers/fixit/TestExprInConstMethodWithFixit.py
new file mode 100644
index 0000000000000..3c239c89d0ca7
--- /dev/null
+++ b/lldb/test/API/lang/cpp/expression-context-qualifiers/fixit/TestExprInConstMethodWithFixit.py
@@ -0,0 +1,42 @@
+import lldb
+from lldbsuite.test.decorators import *
+from lldbsuite.test.lldbtest import *
+from lldbsuite.test import lldbutil
+
+
+class TestCase(TestBase):
+ def test(self):
+ self.build()
+ (_, process, _, _) = lldbutil.run_to_source_breakpoint(
+ self, "Break here", lldb.SBFileSpec("main.cpp")
+ )
+
+ self.expect(
+ "expression m_bar->method()",
+ error=True,
+ substrs=[
+ "member reference type 'const Bar' is not a pointer",
+ "but function is not marked const",
+ ],
+ )
+
+ # Two fix-its
+ self.expect(
+ "expression -- m_bar->method() + m_bar->method()",
+ error=True,
+ substrs=[
+ "member reference type 'const Bar' is not a pointer",
+ "but function is not marked const",
+ "member reference type 'const Bar' is not a pointer",
+ "but function is not marked const",
+ ],
+ )
+
+ self.expect(
+ "expression m_bar->method() + blah",
+ error=True,
+ substrs=[
+ "member reference type 'const Bar' is not a pointer",
+ "but function is not marked const",
+ ],
+ )
diff --git a/lldb/test/API/lang/cpp/expression-context-qualifiers/fixit/main.cpp b/lldb/test/API/lang/cpp/expression-context-qualifiers/fixit/main.cpp
new file mode 100644
index 0000000000000..1dab96e2986da
--- /dev/null
+++ b/lldb/test/API/lang/cpp/expression-context-qualifiers/fixit/main.cpp
@@ -0,0 +1,22 @@
+#include <cassert>
+#include <cstdio>
+
+struct Bar {
+ void method() {}
+};
+
+struct Foo {
+ int const_method() const {
+ std::puts("Break here");
+
+ return 120;
+ }
+
+ Bar m_bar;
+};
+
+int main() {
+ Foo{}.m_bar.method();
+
+ return Foo{}.const_method();
+}
diff --git a/lldb/test/API/lang/cpp/expression-context-qualifiers/non_const_method/Makefile b/lldb/test/API/lang/cpp/expression-context-qualifiers/non_const_method/Makefile
new file mode 100644
index 0000000000000..99998b20bcb05
--- /dev/null
+++ b/lldb/test/API/lang/cpp/expression-context-qualifiers/non_const_method/Makefile
@@ -0,0 +1,3 @@
+CXX_SOURCES := main.cpp
+
+include Makefile.rules
diff --git a/lldb/test/API/lang/cpp/expression-context-qualifiers/non_const_method/TestExprInNonConstMethod.py b/lldb/test/API/lang/cpp/expression-context-qualifiers/non_const_method/TestExprInNonConstMethod.py
new file mode 100644
index 0000000000000..835487f3bb7e0
--- /dev/null
+++ b/lldb/test/API/lang/cpp/expression-context-qualifiers/non_const_method/TestExprInNonConstMethod.py
@@ -0,0 +1,82 @@
+import lldb
+from lldbsuite.test.decorators import *
+from lldbsuite.test.lldbtest import *
+from lldbsuite.test import lldbutil
+
+
+class TestCase(TestBase):
+ def test(self):
+ self.build()
+ (_, process, _, _) = lldbutil.run_to_source_breakpoint(
+ self, "Break: non_const_method begin", lldb.SBFileSpec("main.cpp")
+ )
+
+ self.expect_expr("bar()", result_value="5", result_type="double")
+ self.expect(
+ "expression m_const_mem = 2.0",
+ error=True,
+ substrs=[
+ "cannot assign to non-static data member",
+ "with const-qualified type",
+ ],
+ )
+
+ lldbutil.continue_to_source_breakpoint(
+ self,
+ process,
+ "Break: non_const_method no-this lambda",
+ lldb.SBFileSpec("main.cpp"),
+ )
+
+ self.expect(
+ "expression x = 7.0",
+ error=True,
+ substrs=[
+ "cannot assign to non-static data member within const member function"
+ ],
+ )
+
+ lldbutil.continue_to_source_breakpoint(
+ self,
+ process,
+ "Break: non_const_method mutable no-this lambda",
+ lldb.SBFileSpec("main.cpp"),
+ )
+
+ self.expect_expr("x = 7.0; x", result_value="7")
+
+ lldbutil.continue_to_source_breakpoint(
+ self, process, "Break: non_const_method lambda", lldb.SBFileSpec("main.cpp")
+ )
+
+ # FIXME: mutating this capture should be disallowed in a non-mutable lambda.
+ self.expect_expr("y = 8.0")
+ self.expect_expr("bar()", result_value="5", result_type="double")
+ self.expect(
+ "expression m_const_mem = 2.0",
+ error=True,
+ substrs=[
+ "cannot assign to non-static data member",
+ "with const-qualified type",
+ ],
+ )
+ self.expect_expr("m_mem = 2.0; m_mem", result_value="2")
+
+ lldbutil.continue_to_source_breakpoint(
+ self,
+ process,
+ "Break: non_const_method mutable lambda",
+ lldb.SBFileSpec("main.cpp"),
+ )
+
+ self.expect_expr("y = 9.0")
+ self.expect_expr("bar()", result_value="5", result_type="double")
+ self.expect(
+ "expression m_const_mem = 2.0",
+ error=True,
+ substrs=[
+ "cannot assign to non-static data member",
+ "with const-qualified type",
+ ],
+ )
+ self.expect_expr("m_mem = 4.0; m_mem", result_value="4")
diff --git a/lldb/test/API/lang/cpp/expression-context-qualifiers/non_const_method/main.cpp b/lldb/test/API/lang/cpp/expression-context-qualifiers/non_const_method/main.cpp
new file mode 100644
index 0000000000000..141f63b6f2f24
--- /dev/null
+++ b/lldb/test/API/lang/cpp/expression-context-qualifiers/non_const_method/main.cpp
@@ -0,0 +1,49 @@
+#include <cassert>
+#include <cstdio>
+
+struct Foo {
+ double bar() { return 5.0; }
+
+ int bar() const { return 2; }
+
+ int non_const_method() {
+ auto x = bar();
+ assert(x == 5.0);
+ std::puts("Break: non_const_method begin");
+
+ [x] {
+ std::puts("Keep on multiple lines...");
+ std::puts("Break: non_const_method no-this lambda");
+ }();
+
+ [x]() mutable {
+ std::puts("Keep on multiple lines...");
+ std::puts("Break: non_const_method mutable no-this lambda");
+ }();
+
+ [this, y = x] {
+ auto x = bar() + y;
+ std::puts("Break: non_const_method lambda");
+ }();
+
+ [this, y = x]() mutable {
+ auto x = bar() + y;
+ std::puts("Break: non_const_method mutable lambda");
+ }();
+
+ return 120;
+ }
+
+ float m_mem = -2.0;
+ const float m_const_mem = -3.0;
+};
+
+int main() {
+ const Foo f;
+ f.bar();
+
+ Foo f2;
+ f2.bar();
+
+ return Foo{}.non_const_method();
+}
diff --git a/lldb/test/API/lang/cpp/expression-context-qualifiers/template_const_method/Makefile b/lldb/test/API/lang/cpp/expression-context-qualifiers/template_const_method/Makefile
new file mode 100644
index 0000000000000..99998b20bcb05
--- /dev/null
+++ b/lldb/test/API/lang/cpp/expression-context-qualifiers/template_const_method/Makefile
@@ -0,0 +1,3 @@
+CXX_SOURCES := main.cpp
+
+include Makefile.rules
diff --git a/lldb/test/API/lang/cpp/expression-context-qualifiers/template_const_method/TestExprInTemplateConstMethod.py b/lldb/test/API/lang/cpp/expression-context-qualifiers/template_const_method/TestExprInTemplateConstMethod.py
new file mode 100644
index 0000000000000..0d8f477dce1fd
--- /dev/null
+++ b/lldb/test/API/lang/cpp/expression-context-qualifiers/template_const_method/TestExprInTemplateConstMethod.py
@@ -0,0 +1,105 @@
+import lldb
+from lldbsuite.test.decorators import *
+from lldbsuite.test.lldbtest import *
+from lldbsuite.test import lldbutil
+
+
+class TestCase(TestBase):
+ def test(self):
+ self.build()
+ (_, process, _, _) = lldbutil.run_to_source_breakpoint(
+ self, "Break: const_method begin", lldb.SBFileSpec("main.cpp")
+ )
+
+ self.expect_expr("bar()", result_value="2", result_type="int")
+ self.expect(
+ "expression m_const_mem = 2.0",
+ error=True,
+ substrs=[
+ "cannot assign to non-static data member",
+ "with const-qualified type",
+ ],
+ )
+ self.expect(
+ "expression m_mem = 2.0",
+ error=True,
+ substrs=[
+ "cannot assign to non-static data member within const member function"
+ ],
+ )
+ self.expect_expr("((Foo*)this)->bar()", result_type="double", result_value="5")
+
+ lldbutil.continue_to_source_breakpoint(
+ self,
+ process,
+ "Break: const_method no-this lambda",
+ lldb.SBFileSpec("main.cpp"),
+ )
+
+ self.expect(
+ "expression x = 7.0",
+ error=True,
+ substrs=[
+ "cannot assign to non-static data member within const member function"
+ ],
+ )
+
+ lldbutil.continue_to_source_breakpoint(
+ self,
+ process,
+ "Break: const_method mutable no-this lambda",
+ lldb.SBFileSpec("main.cpp"),
+ )
+
+ self.expect_expr("x = 7.0; x", result_value="7")
+
+ lldbutil.continue_to_source_breakpoint(
+ self, process, "Break: const_method lambda", lldb.SBFileSpec("main.cpp")
+ )
+
+ # FIXME: mutating this capture should be disallowed in a non-mutable lambda.
+ self.expect_expr("y = 8.0")
+ self.expect_expr("bar()", result_value="2", result_type="int")
+ self.expect(
+ "expression m_const_mem = 2.0",
+ error=True,
+ substrs=[
+ "cannot assign to non-static data member",
+ "with const-qualified type",
+ ],
+ )
+ self.expect(
+ "expression m_mem = 2.0",
+ error=True,
+ substrs=[
+ "cannot assign to non-static data member within const member function"
+ ],
+ )
+ self.expect_expr("m_mem", result_value="-2")
+ self.expect_expr("((Foo*)this)->bar()", result_type="double", result_value="5")
+
+ lldbutil.continue_to_source_breakpoint(
+ self,
+ process,
+ "Break: const_method mutable lambda",
+ lldb.SBFileSpec("main.cpp"),
+ )
+
+ self.expect_expr("y = 9.0")
+ self.expect_expr("bar()", result_value="2", result_type="int")
+ self.expect(
+ "expression m_const_mem = 2.0",
+ error=True,
+ substrs=[
+ "cannot assign to non-static data member",
+ "with const-qualified type",
+ ],
+ )
+ self.expect(
+ "expression m_mem = 2.0",
+ error=True,
+ substrs=[
+ "cannot assign to non-static data member within const member function"
+ ],
+ )
+ self.expect_expr("m_mem", result_value="-2")
diff --git a/lldb/test/API/lang/cpp/expression-context-qualifiers/template_const_method/main.cpp b/lldb/test/API/lang/cpp/expression-context-qualifiers/template_const_method/main.cpp
new file mode 100644
index 0000000000000..8238f2a52f5aa
--- /dev/null
+++ b/lldb/test/API/lang/cpp/expression-context-qualifiers/template_const_method/main.cpp
@@ -0,0 +1,51 @@
+#include <cassert>
+#include <cstdio>
+
+struct Foo {
+ double bar() { return 5.0; }
+
+ int bar() const { return 2; }
+
+ void non_const_method() {}
+
+ int template_const_method() const {
+ auto x = bar();
+ assert(x == 2);
+ std::puts("Break: const_method begin");
+
+ [x] {
+ std::puts("Keep on multiple lines...");
+ std::puts("Break: const_method no-this lambda");
+ }();
+
+ [x]() mutable {
+ std::puts("Keep on multiple lines...");
+ std::puts("Break: const_method mutable no-this lambda");
+ }();
+
+ [this, y = x] {
+ auto x = bar() + y;
+ std::puts("Break: const_method lambda");
+ }();
+
+ [this, y = x]() mutable {
+ auto x = bar() + y;
+ std::puts("Break: const_method mutable lambda");
+ }();
+
+ return 120;
+ }
+
+ float m_mem = -2.0;
+ const float m_const_mem = -3.0;
+};
+
+int main() {
+ const Foo f;
+ f.bar();
+
+ Foo f2;
+ f2.bar();
+
+ return Foo{}.template_const_method();
+}
diff --git a/lldb/test/API/lang/cpp/expression-context-qualifiers/template_non_const_method/Makefile b/lldb/test/API/lang/cpp/expression-context-qualifiers/template_non_const_method/Makefile
new file mode 100644
index 0000000000000..99998b20bcb05
--- /dev/null
+++ b/lldb/test/API/lang/cpp/expression-context-qualifiers/template_non_const_method/Makefile
@@ -0,0 +1,3 @@
+CXX_SOURCES := main.cpp
+
+include Makefile.rules
diff --git a/lldb/test/API/lang/cpp/expression-context-qualifiers/template_non_const_method/TestExprInTemplateNonConstMethod.py b/lldb/test/API/lang/cpp/expression-context-qualifiers/template_non_const_method/TestExprInTemplateNonConstMethod.py
new file mode 100644
index 0000000000000..835487f3bb7e0
--- /dev/null
+++ b/lldb/test/API/lang/cpp/expression-context-qualifiers/template_non_const_method/TestExprInTemplateNonConstMethod.py
@@ -0,0 +1,82 @@
+import lldb
+from lldbsuite.test.decorators import *
+from lldbsuite.test.lldbtest import *
+from lldbsuite.test import lldbutil
+
+
+class TestCase(TestBase):
+ def test(self):
+ self.build()
+ (_, process, _, _) = lldbutil.run_to_source_breakpoint(
+ self, "Break: non_const_method begin", lldb.SBFileSpec("main.cpp")
+ )
+
+ self.expect_expr("bar()", result_value="5", result_type="double")
+ self.expect(
+ "expression m_const_mem = 2.0",
+ error=True,
+ substrs=[
+ "cannot assign to non-static data member",
+ "with const-qualified type",
+ ],
+ )
+
+ lldbutil.continue_to_source_breakpoint(
+ self,
+ process,
+ "Break: non_const_method no-this lambda",
+ lldb.SBFileSpec("main.cpp"),
+ )
+
+ self.expect(
+ "expression x = 7.0",
+ error=True,
+ substrs=[
+ "cannot assign to non-static data member within const member function"
+ ],
+ )
+
+ lldbutil.continue_to_source_breakpoint(
+ self,
+ process,
+ "Break: non_const_method mutable no-this lambda",
+ lldb.SBFileSpec("main.cpp"),
+ )
+
+ self.expect_expr("x = 7.0; x", result_value="7")
+
+ lldbutil.continue_to_source_breakpoint(
+ self, process, "Break: non_const_method lambda", lldb.SBFileSpec("main.cpp")
+ )
+
+ # FIXME: mutating this capture should be disallowed in a non-mutable lambda.
+ self.expect_expr("y = 8.0")
+ self.expect_expr("bar()", result_value="5", result_type="double")
+ self.expect(
+ "expression m_const_mem = 2.0",
+ error=True,
+ substrs=[
+ "cannot assign to non-static data member",
+ "with const-qualified type",
+ ],
+ )
+ self.expect_expr("m_mem = 2.0; m_mem", result_value="2")
+
+ lldbutil.continue_to_source_breakpoint(
+ self,
+ process,
+ "Break: non_const_method mutable lambda",
+ lldb.SBFileSpec("main.cpp"),
+ )
+
+ self.expect_expr("y = 9.0")
+ self.expect_expr("bar()", result_value="5", result_type="double")
+ self.expect(
+ "expression m_const_mem = 2.0",
+ error=True,
+ substrs=[
+ "cannot assign to non-static data member",
+ "with const-qualified type",
+ ],
+ )
+ self.expect_expr("m_mem = 4.0; m_mem", result_value="4")
diff --git a/lldb/test/API/lang/cpp/expression-context-qualifiers/template_non_const_method/main.cpp b/lldb/test/API/lang/cpp/expression-context-qualifiers/template_non_const_method/main.cpp
new file mode 100644
index 0000000000000..1632555c69d01
--- /dev/null
+++ b/lldb/test/API/lang/cpp/expression-context-qualifiers/template_non_const_method/main.cpp
@@ -0,0 +1,49 @@
+#include <cassert>
+#include <cstdio>
+
+struct Foo {
+ double bar() { return 5.0; }
+
+ int bar() const { return 2; }
+
+ int template_non_const_method() {
+ auto x = bar();
+ assert(x == 5.0);
+ std::puts("Break: non_const_method begin");
+
+ [x] {
+ std::puts("Keep on multiple lines...");
+ std::puts("Break: non_const_method no-this lambda");
+ }();
+
+ [x]() mutable {
+ std::puts("Keep on multiple lines...");
+ std::puts("Break: non_const_method mutable no-this lambda");
+ }();
+
+ [this, y = x] {
+ auto x = bar() + y;
+ std::puts("Break: non_const_method lambda");
+ }();
+
+ [this, y = x]() mutable {
+ auto x = bar() + y;
+ std::puts("Break: non_const_method mutable lambda");
+ }();
+
+ return 120;
+ }
+
+ float m_mem = -2.0;
+ const float m_const_mem = -3.0;
+};
+
+int main() {
+ const Foo f;
+ f.bar();
+
+ Foo f2;
+ f2.bar();
+
+ return Foo{}.template_non_const_method();
+}
diff --git a/lldb/test/API/lang/cpp/this/TestCPPThis.py b/lldb/test/API/lang/cpp/this/TestCPPThis.py
index c9dcf30b53443..f127f80663578 100644
--- a/lldb/test/API/lang/cpp/this/TestCPPThis.py
+++ b/lldb/test/API/lang/cpp/this/TestCPPThis.py
@@ -1,6 +1,7 @@
"""
Tests that C++ member and static variables are available where they should be.
"""
+
import lldb
from lldbsuite.test.decorators import *
from lldbsuite.test.lldbtest import *
@@ -37,18 +38,23 @@ def test_with_run_command(self):
self.runCmd("process continue")
- # This would be disallowed if we enforced const. But we don't.
- self.expect("expression -- m_a = 2", startstr="(int) $1 = 2")
+ self.expect(
+ "expression -- m_a = 2",
+ error=True,
+ substrs=[
+ "cannot assign to non-static data member within const member function"
+ ],
+ )
- self.expect("expression -- (int)getpid(); m_a", startstr="(int) $2 = 2")
+ self.expect("expression -- (int)getpid(); m_a", startstr="(const int) $1 = 3")
self.runCmd("process continue")
- self.expect("expression -- s_a", startstr="(int) $3 = 5")
+ self.expect("expression -- s_a", startstr="(int) $2 = 5")
self.runCmd("process continue")
- self.expect("expression -- m_a", startstr="(int) $4 = 2")
+ self.expect("expression -- m_a", startstr="(int) $3 = 3")
def set_breakpoint(self, line):
lldbutil.run_break_set_by_file_and_line(
>From a4b4641042fda810758e780c484d68bd0950c75a Mon Sep 17 00:00:00 2001
From: Michael Buch <michaelbuch12 at gmail.com>
Date: Mon, 26 Jan 2026 08:45:56 +0000
Subject: [PATCH 10/20] [lldb][test] Add SBExpressionOptions parameter to
expect_expr
(cherry picked from commit aaf3b8d90de29d548a718bb4b5415bcdb55ba164)
---
lldb/packages/Python/lldbsuite/test/lldbtest.py | 14 +++++++++-----
1 file changed, 9 insertions(+), 5 deletions(-)
diff --git a/lldb/packages/Python/lldbsuite/test/lldbtest.py b/lldb/packages/Python/lldbsuite/test/lldbtest.py
index 6bb4516948da5..6034eca3b93f2 100644
--- a/lldb/packages/Python/lldbsuite/test/lldbtest.py
+++ b/lldb/packages/Python/lldbsuite/test/lldbtest.py
@@ -2575,6 +2575,7 @@ def expect_expr(
result_value=None,
result_type=None,
result_children=None,
+ options=None,
):
"""
Evaluates the given expression and verifies the result.
@@ -2584,6 +2585,7 @@ def expect_expr(
:param result_type: The type that the expression result should have. None if the type should not be checked.
:param result_children: The expected children of the expression result
as a list of ValueChecks. None if the children shouldn't be checked.
+ :param options: Expression evaluation options. None if a default set of options should be used.
"""
self.assertTrue(
expr.strip() == expr,
@@ -2591,13 +2593,15 @@ def expect_expr(
)
frame = self.frame()
- options = lldb.SBExpressionOptions()
- # Disable fix-its that tests don't pass by accident.
- options.SetAutoApplyFixIts(False)
+ if not options:
+ options = lldb.SBExpressionOptions()
- # Set the usual default options for normal expressions.
- options.SetIgnoreBreakpoints(True)
+ # Disable fix-its that tests don't pass by accident.
+ options.SetAutoApplyFixIts(False)
+
+ # Set the usual default options for normal expressions.
+ options.SetIgnoreBreakpoints(True)
if self.frame().IsValid():
options.SetLanguage(frame.GuessLanguage())
>From d8676d0ed9286777e1a1e9f625389540cc42c231 Mon Sep 17 00:00:00 2001
From: Michael Buch <michaelbuch12 at gmail.com>
Date: Fri, 23 Jan 2026 14:49:56 +0000
Subject: [PATCH 11/20] [lldb][Expression] Add --ignore-const-context
expression evaluation option
Depends on:
* https://github.com/llvm/llvm-project/pull/177920
* https://github.com/llvm/llvm-project/pull/177922
In https://github.com/llvm/llvm-project/pull/177922 we make expressions
run in C++ member functions honor the function qualifiers of the current
stop context. E.g., this means we can no longer run non-const member
functions when stopped in a const-member function.
To ensure users can still do this if they really need/want to, we
provide an option to not honor the qualifiers at all, leaving the
`__lldb_expr` as the least qualified, allowing it to call any
function/mutate any members.
---
lldb/include/lldb/Target/Target.h | 6 ++++
.../Commands/CommandObjectExpression.cpp | 5 +++
.../source/Commands/CommandObjectExpression.h | 1 +
lldb/source/Commands/Options.td | 8 +++++
.../Clang/ClangExpressionDeclMap.cpp | 9 +++--
.../Clang/ClangExpressionDeclMap.h | 15 +++++++-
.../Clang/ClangExpressionSourceCode.cpp | 34 +++++++++++--------
.../Clang/ClangExpressionSourceCode.h | 4 +--
.../Clang/ClangUserExpression.cpp | 9 ++---
.../Clang/ClangUserExpression.h | 8 ++---
.../Clang/ClangUtilityFunction.cpp | 2 +-
.../const_method/TestExprInConstMethod.py | 30 +++++++++++++++-
.../TestExprInConstVolatileMethod.py | 25 ++++++++++++++
.../TestExprInNonConstMethod.py | 4 +++
.../TestExprInTemplateConstMethod.py | 8 ++++-
.../TestExprInTemplateNonConstMethod.py | 4 +++
lldb/test/API/lang/cpp/this/TestCPPThis.py | 7 ++--
.../Expression/ClangExpressionDeclMapTest.cpp | 2 +-
18 files changed, 147 insertions(+), 34 deletions(-)
diff --git a/lldb/include/lldb/Target/Target.h b/lldb/include/lldb/Target/Target.h
index a79d0f1b6113e..5dea769bfc82b 100644
--- a/lldb/include/lldb/Target/Target.h
+++ b/lldb/include/lldb/Target/Target.h
@@ -539,6 +539,12 @@ class EvaluateExpressionOptions {
/// used for symbol/function lookup before any other context (except for
/// the module corresponding to the current frame).
SymbolContextList m_preferred_lookup_contexts;
+
+ /// If true, evaluates the expression without taking into account the
+ /// CV-qualifiers of the scope. E.g., this would permit calling a
+ /// non-const C++ method when stopped in a const-method (which would be
+ /// disallowed by C++ language rules).
+ bool m_ignore_context_qualifierss = false;
};
// Target
diff --git a/lldb/source/Commands/CommandObjectExpression.cpp b/lldb/source/Commands/CommandObjectExpression.cpp
index 4919bd3639d3e..ab1dcd7d82cbf 100644
--- a/lldb/source/Commands/CommandObjectExpression.cpp
+++ b/lldb/source/Commands/CommandObjectExpression.cpp
@@ -44,6 +44,9 @@ Status CommandObjectExpression::CommandOptions::SetOptionValue(
const int short_option = GetDefinitions()[option_idx].short_option;
switch (short_option) {
+ case 'Q':
+ ignore_context_qualifiers = true;
+ break;
case 'l':
language = Language::GetLanguageTypeFromString(option_arg);
if (language == eLanguageTypeUnknown) {
@@ -191,6 +194,7 @@ void CommandObjectExpression::CommandOptions::OptionParsingStarting(
top_level = false;
allow_jit = true;
suppress_persistent_result = eLazyBoolCalculate;
+ ignore_context_qualifiers = false;
}
llvm::ArrayRef<OptionDefinition>
@@ -213,6 +217,7 @@ CommandObjectExpression::CommandOptions::GetEvaluateExpressionOptions(
options.SetExecutionPolicy(
allow_jit ? EvaluateExpressionOptions::default_execution_policy
: lldb_private::eExecutionPolicyNever);
+ options.SetIgnoreContextQualifiers(ignore_context_qualifiers);
bool auto_apply_fixits;
if (this->auto_apply_fixits == eLazyBoolCalculate)
diff --git a/lldb/source/Commands/CommandObjectExpression.h b/lldb/source/Commands/CommandObjectExpression.h
index 6fccf10e5dbc1..6d60a674e5708 100644
--- a/lldb/source/Commands/CommandObjectExpression.h
+++ b/lldb/source/Commands/CommandObjectExpression.h
@@ -57,6 +57,7 @@ class CommandObjectExpression : public CommandObjectRaw,
LanguageRuntimeDescriptionDisplayVerbosity m_verbosity;
LazyBool auto_apply_fixits;
LazyBool suppress_persistent_result;
+ bool ignore_context_qualifiers;
};
CommandObjectExpression(CommandInterpreter &interpreter);
diff --git a/lldb/source/Commands/Options.td b/lldb/source/Commands/Options.td
index d96354a39b8b8..c3dddd0f64c2c 100644
--- a/lldb/source/Commands/Options.td
+++ b/lldb/source/Commands/Options.td
@@ -778,6 +778,14 @@ let Command = "expression" in {
Desc<"Persist expression result in a variable for subsequent use. "
"Expression results will be labeled with $-prefixed variables, "
"e.g. $0, $1, etc.">;
+ def ignore_context_qualifiers
+ : Option<"ignore-context-qualifiers", "Q">,
+ Groups<[1, 2]>,
+ Desc<"When specified, evaluates the expression without taking into "
+ "account the type ${Q}ualifiers of the scope. E.g., this would "
+ "permit "
+ "calling a non-const C++ method when stopped in a const-method "
+ "(which would be disallowed by C++ language rules).">;
}
let Command = "frame diag" in {
diff --git a/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionDeclMap.cpp b/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionDeclMap.cpp
index b1f246f6690e7..9f4ccc60c0b34 100644
--- a/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionDeclMap.cpp
+++ b/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionDeclMap.cpp
@@ -88,10 +88,12 @@ ClangExpressionDeclMap::ClangExpressionDeclMap(
bool keep_result_in_memory,
Materializer::PersistentVariableDelegate *result_delegate,
const lldb::TargetSP &target,
- const std::shared_ptr<ClangASTImporter> &importer, ValueObject *ctx_obj)
+ const std::shared_ptr<ClangASTImporter> &importer, ValueObject *ctx_obj,
+ bool ignore_context_qualifiers)
: ClangASTSource(target, importer), m_found_entities(), m_struct_members(),
m_keep_result_in_memory(keep_result_in_memory),
- m_result_delegate(result_delegate), m_ctx_obj(ctx_obj), m_parser_vars(),
+ m_result_delegate(result_delegate), m_ctx_obj(ctx_obj),
+ m_ignore_context_qualifiers(ignore_context_qualifiers), m_parser_vars(),
m_struct_vars() {
EnableStructVars();
}
@@ -1997,7 +1999,8 @@ void ClangExpressionDeclMap::AddContextClassType(NameSearchContext &context,
std::array<CompilerType, 1> args{void_clang_type.GetPointerType()};
CompilerType method_type = m_clang_ast_context->CreateFunctionType(
- void_clang_type, args, false, ut.GetTypeQualifiers());
+ void_clang_type, args, false,
+ m_ignore_context_qualifiers ? 0 : ut.GetTypeQualifiers());
const bool is_virtual = false;
const bool is_static = false;
diff --git a/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionDeclMap.h b/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionDeclMap.h
index df74ebc37a5de..d44c95068f45d 100644
--- a/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionDeclMap.h
+++ b/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionDeclMap.h
@@ -78,11 +78,18 @@ class ClangExpressionDeclMap : public ClangASTSource {
/// \param[in] ctx_obj
/// If not empty, then expression is evaluated in context of this object.
/// See the comment to `UserExpression::Evaluate` for details.
+ ///
+ /// \param[in] ignore_context_qualifiers
+ /// If \c true, evaluates the expression without taking into account the
+ /// CV-qualifiers of the scope. E.g., this would permit calling a
+ /// non-const C++ method when stopped in a const-method (which would be
+ /// disallowed by C++ language rules).
ClangExpressionDeclMap(
bool keep_result_in_memory,
Materializer::PersistentVariableDelegate *result_delegate,
const lldb::TargetSP &target,
- const std::shared_ptr<ClangASTImporter> &importer, ValueObject *ctx_obj);
+ const std::shared_ptr<ClangASTImporter> &importer, ValueObject *ctx_obj,
+ bool ignore_context_qualifiers);
/// Destructor
~ClangExpressionDeclMap() override;
@@ -306,6 +313,12 @@ class ClangExpressionDeclMap : public ClangASTSource {
///For details see the comment to
///`UserExpression::Evaluate`.
+ /// If \c true, evaluates the expression without taking into account the
+ /// CV-qualifiers of the scope. E.g., this would permit calling a
+ /// non-const C++ method when stopped in a const-method (which would be
+ /// disallowed by C++ language rules).
+ bool m_ignore_context_qualifiers = false;
+
/// The following values should not live beyond parsing
class ParserVars {
public:
diff --git a/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionSourceCode.cpp b/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionSourceCode.cpp
index ca4199faa3841..c711367cb6177 100644
--- a/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionSourceCode.cpp
+++ b/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionSourceCode.cpp
@@ -363,9 +363,12 @@ void ClangExpressionSourceCode::AddLocalVariableDecls(StreamString &stream,
}
}
-bool ClangExpressionSourceCode::GetText(
- std::string &text, ExecutionContext &exe_ctx, bool add_locals,
- bool force_add_all_locals, llvm::ArrayRef<std::string> modules) const {
+bool ClangExpressionSourceCode::GetText(std::string &text,
+ ExecutionContext &exe_ctx,
+ bool add_locals,
+ bool force_add_all_locals,
+ llvm::ArrayRef<std::string> modules,
+ bool ignore_context_qualifiers) const {
const char *target_specific_defines = "typedef signed char BOOL;\n";
std::string module_macros;
llvm::raw_string_ostream module_macros_stream(module_macros);
@@ -486,17 +489,20 @@ bool ClangExpressionSourceCode::GetText(
lldb_local_var_decls.GetData(), tagged_body.c_str());
break;
case WrapKind::CppMemberFunction:
- wrap_stream.Printf(
- "%s"
- "void \n"
- "$__lldb_class::%s(void *$__lldb_arg) %s \n"
- "{ \n"
- " %s; \n"
- "%s"
- "} \n",
- module_imports.c_str(), m_name.c_str(),
- GetFrameCVQualifiers(exe_ctx.GetFramePtr()).getAsString().c_str(),
- lldb_local_var_decls.GetData(), tagged_body.c_str());
+ wrap_stream.Printf("%s"
+ "void \n"
+ "$__lldb_class::%s(void *$__lldb_arg) %s \n"
+ "{ \n"
+ " %s; \n"
+ "%s"
+ "} \n",
+ module_imports.c_str(), m_name.c_str(),
+ ignore_context_qualifiers
+ ? ""
+ : GetFrameCVQualifiers(exe_ctx.GetFramePtr())
+ .getAsString()
+ .c_str(),
+ lldb_local_var_decls.GetData(), tagged_body.c_str());
break;
case WrapKind::ObjCInstanceMethod:
wrap_stream.Printf(
diff --git a/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionSourceCode.h b/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionSourceCode.h
index f721bb2f319e1..914d405139193 100644
--- a/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionSourceCode.h
+++ b/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionSourceCode.h
@@ -63,8 +63,8 @@ class ClangExpressionSourceCode : public ExpressionSourceCode {
///
/// \return true iff the source code was successfully generated.
bool GetText(std::string &text, ExecutionContext &exe_ctx, bool add_locals,
- bool force_add_all_locals,
- llvm::ArrayRef<std::string> modules) const;
+ bool force_add_all_locals, llvm::ArrayRef<std::string> modules,
+ bool ignore_context_qualifiers) const;
// Given a string returned by GetText, find the beginning and end of the body
// passed to CreateWrapped. Return true if the bounds could be found. This
diff --git a/lldb/source/Plugins/ExpressionParser/Clang/ClangUserExpression.cpp b/lldb/source/Plugins/ExpressionParser/Clang/ClangUserExpression.cpp
index 2cbbae11bd18a..2436468569958 100644
--- a/lldb/source/Plugins/ExpressionParser/Clang/ClangUserExpression.cpp
+++ b/lldb/source/Plugins/ExpressionParser/Clang/ClangUserExpression.cpp
@@ -416,7 +416,8 @@ void ClangUserExpression::CreateSourceCode(
m_filename, prefix, m_expr_text, GetWrapKind()));
if (!m_source_code->GetText(m_transformed_text, exe_ctx, !m_ctx_obj,
- for_completion, modules_to_import)) {
+ for_completion, modules_to_import,
+ m_options.GetIgnoreContextQualifiers())) {
diagnostic_manager.PutString(lldb::eSeverityError,
"couldn't construct expression body");
return;
@@ -950,8 +951,8 @@ char ClangUserExpression::ClangUserExpressionHelper::ID;
void ClangUserExpression::ClangUserExpressionHelper::ResetDeclMap(
ExecutionContext &exe_ctx,
Materializer::PersistentVariableDelegate &delegate,
- bool keep_result_in_memory,
- ValueObject *ctx_obj) {
+ bool keep_result_in_memory, ValueObject *ctx_obj,
+ bool ignore_context_qualifiers) {
std::shared_ptr<ClangASTImporter> ast_importer;
auto *state = exe_ctx.GetTargetSP()->GetPersistentExpressionStateForLanguage(
lldb::eLanguageTypeC);
@@ -961,7 +962,7 @@ void ClangUserExpression::ClangUserExpressionHelper::ResetDeclMap(
}
m_expr_decl_map_up = std::make_unique<ClangExpressionDeclMap>(
keep_result_in_memory, &delegate, exe_ctx.GetTargetSP(), ast_importer,
- ctx_obj);
+ ctx_obj, ignore_context_qualifiers);
}
clang::ASTConsumer *
diff --git a/lldb/source/Plugins/ExpressionParser/Clang/ClangUserExpression.h b/lldb/source/Plugins/ExpressionParser/Clang/ClangUserExpression.h
index 7c0c6a0147e2a..b3c31529446ad 100644
--- a/lldb/source/Plugins/ExpressionParser/Clang/ClangUserExpression.h
+++ b/lldb/source/Plugins/ExpressionParser/Clang/ClangUserExpression.h
@@ -71,8 +71,8 @@ class ClangUserExpression : public LLVMUserExpression {
void ResetDeclMap(ExecutionContext &exe_ctx,
Materializer::PersistentVariableDelegate &result_delegate,
- bool keep_result_in_memory,
- ValueObject *ctx_obj);
+ bool keep_result_in_memory, ValueObject *ctx_obj,
+ bool ignore_context_qualifiers);
/// Return the object that the parser should allow to access ASTs. May be
/// NULL if the ASTs do not need to be transformed.
@@ -167,8 +167,8 @@ class ClangUserExpression : public LLVMUserExpression {
Materializer::PersistentVariableDelegate &result_delegate,
bool keep_result_in_memory) {
m_type_system_helper.ResetDeclMap(exe_ctx, result_delegate,
- keep_result_in_memory,
- m_ctx_obj);
+ keep_result_in_memory, m_ctx_obj,
+ m_options.GetIgnoreContextQualifiers());
}
lldb::ExpressionVariableSP
diff --git a/lldb/source/Plugins/ExpressionParser/Clang/ClangUtilityFunction.cpp b/lldb/source/Plugins/ExpressionParser/Clang/ClangUtilityFunction.cpp
index e6983066a12fa..112ce9be7bd1a 100644
--- a/lldb/source/Plugins/ExpressionParser/Clang/ClangUtilityFunction.cpp
+++ b/lldb/source/Plugins/ExpressionParser/Clang/ClangUtilityFunction.cpp
@@ -187,5 +187,5 @@ void ClangUtilityFunction::ClangUtilityFunctionHelper::ResetDeclMap(
}
m_expr_decl_map_up = std::make_unique<ClangExpressionDeclMap>(
keep_result_in_memory, nullptr, exe_ctx.GetTargetSP(), ast_importer,
- nullptr);
+ nullptr, /*ignore_context_qualifiers=*/false);
}
diff --git a/lldb/test/API/lang/cpp/expression-context-qualifiers/const_method/TestExprInConstMethod.py b/lldb/test/API/lang/cpp/expression-context-qualifiers/const_method/TestExprInConstMethod.py
index 0d8f477dce1fd..d73b6563b29a6 100644
--- a/lldb/test/API/lang/cpp/expression-context-qualifiers/const_method/TestExprInConstMethod.py
+++ b/lldb/test/API/lang/cpp/expression-context-qualifiers/const_method/TestExprInConstMethod.py
@@ -27,6 +27,26 @@ def test(self):
"cannot assign to non-static data member within const member function"
],
)
+ self.expect_expr("m_mem", result_value="-2")
+
+ # Test short and long --ignore-context-qualifiers option.
+ self.expect(
+ "expression --ignore-context-qualifiers -- m_mem = 3.0",
+ error=False,
+ )
+ self.expect_expr("m_mem", result_value="3")
+
+ self.expect(
+ "expression -Q -- m_mem = 4.0",
+ error=False,
+ )
+ self.expect_expr("m_mem", result_value="4")
+
+ # Test --ignore-context-qualifiers via SBExpressionOptions.
+ options = lldb.SBExpressionOptions()
+ options.SetIgnoreContextQualifiers()
+ self.expect_expr("m_mem = -2.0; m_mem", options=options, result_value="-2")
+
self.expect_expr("((Foo*)this)->bar()", result_type="double", result_value="5")
lldbutil.continue_to_source_breakpoint(
@@ -43,6 +63,9 @@ def test(self):
"cannot assign to non-static data member within const member function"
],
)
+ self.expect_expr("x", result_value="2")
+
+ self.expect_expr("x = -5; x", options=options, result_value="-5")
lldbutil.continue_to_source_breakpoint(
self,
@@ -76,6 +99,9 @@ def test(self):
],
)
self.expect_expr("m_mem", result_value="-2")
+
+ self.expect_expr("m_mem = -1; m_mem", options=options, result_value="-1")
+
self.expect_expr("((Foo*)this)->bar()", result_type="double", result_value="5")
lldbutil.continue_to_source_breakpoint(
@@ -102,4 +128,6 @@ def test(self):
"cannot assign to non-static data member within const member function"
],
)
- self.expect_expr("m_mem", result_value="-2")
+ self.expect_expr("m_mem", result_value="-1")
+
+ self.expect_expr("m_mem = -2; m_mem", options=options, result_value="-2")
diff --git a/lldb/test/API/lang/cpp/expression-context-qualifiers/const_volatile_method/TestExprInConstVolatileMethod.py b/lldb/test/API/lang/cpp/expression-context-qualifiers/const_volatile_method/TestExprInConstVolatileMethod.py
index f20b9d8164841..0069b42f5da10 100644
--- a/lldb/test/API/lang/cpp/expression-context-qualifiers/const_volatile_method/TestExprInConstVolatileMethod.py
+++ b/lldb/test/API/lang/cpp/expression-context-qualifiers/const_volatile_method/TestExprInConstVolatileMethod.py
@@ -20,6 +20,16 @@ def test(self):
substrs=["has type 'const Foo'", "but function is not marked const"],
)
+ options = lldb.SBExpressionOptions()
+ options.SetIgnoreContextQualifiers()
+ options.SetIgnoreBreakpoints(True)
+ self.expect_expr("volatile_method()", options=options)
+ self.expect(
+ "expression --ignore-context-qualifiers -- bar()",
+ error=True,
+ substrs=["call to member function 'bar' is ambiguous"],
+ )
+
lldbutil.continue_to_source_breakpoint(
self, process, "Break here: volatile", lldb.SBFileSpec("main.cpp")
)
@@ -35,6 +45,13 @@ def test(self):
)
self.expect_expr("volatile_method()")
+ self.expect_expr("const_method()", options=options)
+ self.expect(
+ "expression --ignore-context-qualifiers -- bar()",
+ error=True,
+ substrs=["call to member function 'bar' is ambiguous"],
+ )
+
lldbutil.continue_to_source_breakpoint(
self, process, "Break here: const volatile", lldb.SBFileSpec("main.cpp")
)
@@ -58,3 +75,11 @@ def test(self):
"but function is not marked const or volatile",
],
)
+
+ self.expect_expr("const_method()", options=options)
+ self.expect_expr("volatile_method()", options=options)
+ self.expect(
+ "expression --ignore-context-qualifiers -- bar()",
+ error=True,
+ substrs=["call to member function 'bar' is ambiguous"],
+ )
diff --git a/lldb/test/API/lang/cpp/expression-context-qualifiers/non_const_method/TestExprInNonConstMethod.py b/lldb/test/API/lang/cpp/expression-context-qualifiers/non_const_method/TestExprInNonConstMethod.py
index 835487f3bb7e0..78fe27508d10f 100644
--- a/lldb/test/API/lang/cpp/expression-context-qualifiers/non_const_method/TestExprInNonConstMethod.py
+++ b/lldb/test/API/lang/cpp/expression-context-qualifiers/non_const_method/TestExprInNonConstMethod.py
@@ -36,6 +36,10 @@ def test(self):
],
)
+ options = lldb.SBExpressionOptions()
+ options.SetIgnoreContextQualifiers()
+ self.expect_expr("x = 6.0; x", options=options, result_value="6")
+
lldbutil.continue_to_source_breakpoint(
self,
process,
diff --git a/lldb/test/API/lang/cpp/expression-context-qualifiers/template_const_method/TestExprInTemplateConstMethod.py b/lldb/test/API/lang/cpp/expression-context-qualifiers/template_const_method/TestExprInTemplateConstMethod.py
index 0d8f477dce1fd..8489f09b69bba 100644
--- a/lldb/test/API/lang/cpp/expression-context-qualifiers/template_const_method/TestExprInTemplateConstMethod.py
+++ b/lldb/test/API/lang/cpp/expression-context-qualifiers/template_const_method/TestExprInTemplateConstMethod.py
@@ -29,6 +29,10 @@ def test(self):
)
self.expect_expr("((Foo*)this)->bar()", result_type="double", result_value="5")
+ options = lldb.SBExpressionOptions()
+ options.SetIgnoreContextQualifiers()
+ self.expect_expr("m_mem = -2.0; m_mem", options=options, result_value="-2")
+
lldbutil.continue_to_source_breakpoint(
self,
process,
@@ -43,6 +47,7 @@ def test(self):
"cannot assign to non-static data member within const member function"
],
)
+ self.expect_expr("x = -7.0; x", options=options, result_value="-7")
lldbutil.continue_to_source_breakpoint(
self,
@@ -77,6 +82,7 @@ def test(self):
)
self.expect_expr("m_mem", result_value="-2")
self.expect_expr("((Foo*)this)->bar()", result_type="double", result_value="5")
+ self.expect_expr("m_mem = -8.0; m_mem", options=options, result_value="-8")
lldbutil.continue_to_source_breakpoint(
self,
@@ -102,4 +108,4 @@ def test(self):
"cannot assign to non-static data member within const member function"
],
)
- self.expect_expr("m_mem", result_value="-2")
+ self.expect_expr("m_mem = -11.0; m_mem", options=options, result_value="-11")
diff --git a/lldb/test/API/lang/cpp/expression-context-qualifiers/template_non_const_method/TestExprInTemplateNonConstMethod.py b/lldb/test/API/lang/cpp/expression-context-qualifiers/template_non_const_method/TestExprInTemplateNonConstMethod.py
index 835487f3bb7e0..78fe27508d10f 100644
--- a/lldb/test/API/lang/cpp/expression-context-qualifiers/template_non_const_method/TestExprInTemplateNonConstMethod.py
+++ b/lldb/test/API/lang/cpp/expression-context-qualifiers/template_non_const_method/TestExprInTemplateNonConstMethod.py
@@ -36,6 +36,10 @@ def test(self):
],
)
+ options = lldb.SBExpressionOptions()
+ options.SetIgnoreContextQualifiers()
+ self.expect_expr("x = 6.0; x", options=options, result_value="6")
+
lldbutil.continue_to_source_breakpoint(
self,
process,
diff --git a/lldb/test/API/lang/cpp/this/TestCPPThis.py b/lldb/test/API/lang/cpp/this/TestCPPThis.py
index f127f80663578..5a59bf3631e82 100644
--- a/lldb/test/API/lang/cpp/this/TestCPPThis.py
+++ b/lldb/test/API/lang/cpp/this/TestCPPThis.py
@@ -47,14 +47,17 @@ def test_with_run_command(self):
)
self.expect("expression -- (int)getpid(); m_a", startstr="(const int) $1 = 3")
+ self.expect(
+ "expression --ignore-context-qualifiers -- m_a = 2", startstr="(int) $2 = 2"
+ )
self.runCmd("process continue")
- self.expect("expression -- s_a", startstr="(int) $2 = 5")
+ self.expect("expression -- s_a", startstr="(int) $3 = 5")
self.runCmd("process continue")
- self.expect("expression -- m_a", startstr="(int) $3 = 3")
+ self.expect("expression -- m_a", startstr="(int) $4 = 2")
def set_breakpoint(self, line):
lldbutil.run_break_set_by_file_and_line(
diff --git a/lldb/unittests/Expression/ClangExpressionDeclMapTest.cpp b/lldb/unittests/Expression/ClangExpressionDeclMapTest.cpp
index 1c07119d4497f..61905ee8df862 100644
--- a/lldb/unittests/Expression/ClangExpressionDeclMapTest.cpp
+++ b/lldb/unittests/Expression/ClangExpressionDeclMapTest.cpp
@@ -23,7 +23,7 @@ namespace {
struct FakeClangExpressionDeclMap : public ClangExpressionDeclMap {
FakeClangExpressionDeclMap(const std::shared_ptr<ClangASTImporter> &importer)
: ClangExpressionDeclMap(false, nullptr, lldb::TargetSP(), importer,
- nullptr) {
+ nullptr, /*ignore_context_qualifiers=*/false) {
m_holder = std::make_unique<clang_utils::TypeSystemClangHolder>("ast");
m_scratch_context = m_holder->GetAST();
}
>From 6599de737b4356f4644cdc4037aded76104ac4a2 Mon Sep 17 00:00:00 2001
From: Michael Buch <michaelbuch12 at gmail.com>
Date: Fri, 30 Jan 2026 11:12:13 +0000
Subject: [PATCH 12/20] fixup! rename option to more c++ specific name
---
lldb/include/lldb/Target/Target.h | 4 +++-
lldb/source/Commands/CommandObjectExpression.cpp | 6 +++---
lldb/source/Commands/CommandObjectExpression.h | 2 +-
lldb/source/Commands/Options.td | 9 ++++-----
.../ExpressionParser/Clang/ClangUserExpression.cpp | 2 +-
.../Plugins/ExpressionParser/Clang/ClangUserExpression.h | 6 +++---
.../const_method/TestExprInConstMethod.py | 6 +++---
.../TestExprInConstVolatileMethod.py | 8 ++++----
.../non_const_method/TestExprInNonConstMethod.py | 2 +-
.../TestExprInTemplateConstMethod.py | 2 +-
.../TestExprInTemplateNonConstMethod.py | 2 +-
lldb/test/API/lang/cpp/this/TestCPPThis.py | 2 +-
12 files changed, 26 insertions(+), 25 deletions(-)
diff --git a/lldb/include/lldb/Target/Target.h b/lldb/include/lldb/Target/Target.h
index 5dea769bfc82b..6181da9041566 100644
--- a/lldb/include/lldb/Target/Target.h
+++ b/lldb/include/lldb/Target/Target.h
@@ -544,7 +544,9 @@ class EvaluateExpressionOptions {
/// CV-qualifiers of the scope. E.g., this would permit calling a
/// non-const C++ method when stopped in a const-method (which would be
/// disallowed by C++ language rules).
- bool m_ignore_context_qualifierss = false;
+ ///
+ /// FIXME: move this to a language-specific dictionary of options.
+ bool m_cpp_ignore_context_qualifierss = false;
};
// Target
diff --git a/lldb/source/Commands/CommandObjectExpression.cpp b/lldb/source/Commands/CommandObjectExpression.cpp
index ab1dcd7d82cbf..efc0df6cd738e 100644
--- a/lldb/source/Commands/CommandObjectExpression.cpp
+++ b/lldb/source/Commands/CommandObjectExpression.cpp
@@ -45,7 +45,7 @@ Status CommandObjectExpression::CommandOptions::SetOptionValue(
switch (short_option) {
case 'Q':
- ignore_context_qualifiers = true;
+ cpp_ignore_context_qualifiers = true;
break;
case 'l':
language = Language::GetLanguageTypeFromString(option_arg);
@@ -194,7 +194,7 @@ void CommandObjectExpression::CommandOptions::OptionParsingStarting(
top_level = false;
allow_jit = true;
suppress_persistent_result = eLazyBoolCalculate;
- ignore_context_qualifiers = false;
+ cpp_ignore_context_qualifiers = false;
}
llvm::ArrayRef<OptionDefinition>
@@ -217,7 +217,7 @@ CommandObjectExpression::CommandOptions::GetEvaluateExpressionOptions(
options.SetExecutionPolicy(
allow_jit ? EvaluateExpressionOptions::default_execution_policy
: lldb_private::eExecutionPolicyNever);
- options.SetIgnoreContextQualifiers(ignore_context_qualifiers);
+ options.SetCppIgnoreContextQualifiers(cpp_ignore_context_qualifiers);
bool auto_apply_fixits;
if (this->auto_apply_fixits == eLazyBoolCalculate)
diff --git a/lldb/source/Commands/CommandObjectExpression.h b/lldb/source/Commands/CommandObjectExpression.h
index 6d60a674e5708..0439ddffce925 100644
--- a/lldb/source/Commands/CommandObjectExpression.h
+++ b/lldb/source/Commands/CommandObjectExpression.h
@@ -57,7 +57,7 @@ class CommandObjectExpression : public CommandObjectRaw,
LanguageRuntimeDescriptionDisplayVerbosity m_verbosity;
LazyBool auto_apply_fixits;
LazyBool suppress_persistent_result;
- bool ignore_context_qualifiers;
+ bool cpp_ignore_context_qualifiers;
};
CommandObjectExpression(CommandInterpreter &interpreter);
diff --git a/lldb/source/Commands/Options.td b/lldb/source/Commands/Options.td
index c3dddd0f64c2c..72bb5df4dddac 100644
--- a/lldb/source/Commands/Options.td
+++ b/lldb/source/Commands/Options.td
@@ -779,13 +779,12 @@ let Command = "expression" in {
"Expression results will be labeled with $-prefixed variables, "
"e.g. $0, $1, etc.">;
def ignore_context_qualifiers
- : Option<"ignore-context-qualifiers", "Q">,
+ : Option<"cpp-ignore-context-qualifiers", "Q">,
Groups<[1, 2]>,
Desc<"When specified, evaluates the expression without taking into "
- "account the type ${Q}ualifiers of the scope. E.g., this would "
- "permit "
- "calling a non-const C++ method when stopped in a const-method "
- "(which would be disallowed by C++ language rules).">;
+ "account the type ${Q}ualifiers of the scope. In C++, this would "
+ "permit calling a non-const method when stopped in a const-method "
+ "(which would be disallowed by language rules).">;
}
let Command = "frame diag" in {
diff --git a/lldb/source/Plugins/ExpressionParser/Clang/ClangUserExpression.cpp b/lldb/source/Plugins/ExpressionParser/Clang/ClangUserExpression.cpp
index 2436468569958..634cdec918057 100644
--- a/lldb/source/Plugins/ExpressionParser/Clang/ClangUserExpression.cpp
+++ b/lldb/source/Plugins/ExpressionParser/Clang/ClangUserExpression.cpp
@@ -417,7 +417,7 @@ void ClangUserExpression::CreateSourceCode(
if (!m_source_code->GetText(m_transformed_text, exe_ctx, !m_ctx_obj,
for_completion, modules_to_import,
- m_options.GetIgnoreContextQualifiers())) {
+ m_options.GetCppIgnoreContextQualifiers())) {
diagnostic_manager.PutString(lldb::eSeverityError,
"couldn't construct expression body");
return;
diff --git a/lldb/source/Plugins/ExpressionParser/Clang/ClangUserExpression.h b/lldb/source/Plugins/ExpressionParser/Clang/ClangUserExpression.h
index b3c31529446ad..7f1c1ddcad942 100644
--- a/lldb/source/Plugins/ExpressionParser/Clang/ClangUserExpression.h
+++ b/lldb/source/Plugins/ExpressionParser/Clang/ClangUserExpression.h
@@ -166,9 +166,9 @@ class ClangUserExpression : public LLVMUserExpression {
void ResetDeclMap(ExecutionContext &exe_ctx,
Materializer::PersistentVariableDelegate &result_delegate,
bool keep_result_in_memory) {
- m_type_system_helper.ResetDeclMap(exe_ctx, result_delegate,
- keep_result_in_memory, m_ctx_obj,
- m_options.GetIgnoreContextQualifiers());
+ m_type_system_helper.ResetDeclMap(
+ exe_ctx, result_delegate, keep_result_in_memory, m_ctx_obj,
+ m_options.GetCppIgnoreContextQualifiers());
}
lldb::ExpressionVariableSP
diff --git a/lldb/test/API/lang/cpp/expression-context-qualifiers/const_method/TestExprInConstMethod.py b/lldb/test/API/lang/cpp/expression-context-qualifiers/const_method/TestExprInConstMethod.py
index d73b6563b29a6..b501836a46483 100644
--- a/lldb/test/API/lang/cpp/expression-context-qualifiers/const_method/TestExprInConstMethod.py
+++ b/lldb/test/API/lang/cpp/expression-context-qualifiers/const_method/TestExprInConstMethod.py
@@ -29,9 +29,9 @@ def test(self):
)
self.expect_expr("m_mem", result_value="-2")
- # Test short and long --ignore-context-qualifiers option.
+ # Test short and long --cpp-ignore-context-qualifiers option.
self.expect(
- "expression --ignore-context-qualifiers -- m_mem = 3.0",
+ "expression --cpp-ignore-context-qualifiers -- m_mem = 3.0",
error=False,
)
self.expect_expr("m_mem", result_value="3")
@@ -42,7 +42,7 @@ def test(self):
)
self.expect_expr("m_mem", result_value="4")
- # Test --ignore-context-qualifiers via SBExpressionOptions.
+ # Test --cpp-ignore-context-qualifiers via SBExpressionOptions.
options = lldb.SBExpressionOptions()
options.SetIgnoreContextQualifiers()
self.expect_expr("m_mem = -2.0; m_mem", options=options, result_value="-2")
diff --git a/lldb/test/API/lang/cpp/expression-context-qualifiers/const_volatile_method/TestExprInConstVolatileMethod.py b/lldb/test/API/lang/cpp/expression-context-qualifiers/const_volatile_method/TestExprInConstVolatileMethod.py
index 0069b42f5da10..40e5bff0aa60c 100644
--- a/lldb/test/API/lang/cpp/expression-context-qualifiers/const_volatile_method/TestExprInConstVolatileMethod.py
+++ b/lldb/test/API/lang/cpp/expression-context-qualifiers/const_volatile_method/TestExprInConstVolatileMethod.py
@@ -21,11 +21,11 @@ def test(self):
)
options = lldb.SBExpressionOptions()
- options.SetIgnoreContextQualifiers()
+ options.SetCppIgnoreContextQualifiers()
options.SetIgnoreBreakpoints(True)
self.expect_expr("volatile_method()", options=options)
self.expect(
- "expression --ignore-context-qualifiers -- bar()",
+ "expression --cpp-ignore-context-qualifiers -- bar()",
error=True,
substrs=["call to member function 'bar' is ambiguous"],
)
@@ -47,7 +47,7 @@ def test(self):
self.expect_expr("const_method()", options=options)
self.expect(
- "expression --ignore-context-qualifiers -- bar()",
+ "expression --cpp-ignore-context-qualifiers -- bar()",
error=True,
substrs=["call to member function 'bar' is ambiguous"],
)
@@ -79,7 +79,7 @@ def test(self):
self.expect_expr("const_method()", options=options)
self.expect_expr("volatile_method()", options=options)
self.expect(
- "expression --ignore-context-qualifiers -- bar()",
+ "expression --cpp-ignore-context-qualifiers -- bar()",
error=True,
substrs=["call to member function 'bar' is ambiguous"],
)
diff --git a/lldb/test/API/lang/cpp/expression-context-qualifiers/non_const_method/TestExprInNonConstMethod.py b/lldb/test/API/lang/cpp/expression-context-qualifiers/non_const_method/TestExprInNonConstMethod.py
index 78fe27508d10f..c26239adb34ca 100644
--- a/lldb/test/API/lang/cpp/expression-context-qualifiers/non_const_method/TestExprInNonConstMethod.py
+++ b/lldb/test/API/lang/cpp/expression-context-qualifiers/non_const_method/TestExprInNonConstMethod.py
@@ -37,7 +37,7 @@ def test(self):
)
options = lldb.SBExpressionOptions()
- options.SetIgnoreContextQualifiers()
+ options.SetCppIgnoreContextQualifiers()
self.expect_expr("x = 6.0; x", options=options, result_value="6")
lldbutil.continue_to_source_breakpoint(
diff --git a/lldb/test/API/lang/cpp/expression-context-qualifiers/template_const_method/TestExprInTemplateConstMethod.py b/lldb/test/API/lang/cpp/expression-context-qualifiers/template_const_method/TestExprInTemplateConstMethod.py
index 8489f09b69bba..e4cd5ea759f60 100644
--- a/lldb/test/API/lang/cpp/expression-context-qualifiers/template_const_method/TestExprInTemplateConstMethod.py
+++ b/lldb/test/API/lang/cpp/expression-context-qualifiers/template_const_method/TestExprInTemplateConstMethod.py
@@ -30,7 +30,7 @@ def test(self):
self.expect_expr("((Foo*)this)->bar()", result_type="double", result_value="5")
options = lldb.SBExpressionOptions()
- options.SetIgnoreContextQualifiers()
+ options.SetCppIgnoreContextQualifiers()
self.expect_expr("m_mem = -2.0; m_mem", options=options, result_value="-2")
lldbutil.continue_to_source_breakpoint(
diff --git a/lldb/test/API/lang/cpp/expression-context-qualifiers/template_non_const_method/TestExprInTemplateNonConstMethod.py b/lldb/test/API/lang/cpp/expression-context-qualifiers/template_non_const_method/TestExprInTemplateNonConstMethod.py
index 78fe27508d10f..c26239adb34ca 100644
--- a/lldb/test/API/lang/cpp/expression-context-qualifiers/template_non_const_method/TestExprInTemplateNonConstMethod.py
+++ b/lldb/test/API/lang/cpp/expression-context-qualifiers/template_non_const_method/TestExprInTemplateNonConstMethod.py
@@ -37,7 +37,7 @@ def test(self):
)
options = lldb.SBExpressionOptions()
- options.SetIgnoreContextQualifiers()
+ options.SetCppIgnoreContextQualifiers()
self.expect_expr("x = 6.0; x", options=options, result_value="6")
lldbutil.continue_to_source_breakpoint(
diff --git a/lldb/test/API/lang/cpp/this/TestCPPThis.py b/lldb/test/API/lang/cpp/this/TestCPPThis.py
index 5a59bf3631e82..cb462b0ae68de 100644
--- a/lldb/test/API/lang/cpp/this/TestCPPThis.py
+++ b/lldb/test/API/lang/cpp/this/TestCPPThis.py
@@ -48,7 +48,7 @@ def test_with_run_command(self):
self.expect("expression -- (int)getpid(); m_a", startstr="(const int) $1 = 3")
self.expect(
- "expression --ignore-context-qualifiers -- m_a = 2", startstr="(int) $2 = 2"
+ "expression --cpp-ignore-context-qualifiers -- m_a = 2", startstr="(int) $2 = 2"
)
self.runCmd("process continue")
>From 799fdd04a0cb32e3f4f5725fd3d70f09345d7b43 Mon Sep 17 00:00:00 2001
From: Michael Buch <michaelbuch12 at gmail.com>
Date: Fri, 30 Jan 2026 11:21:53 +0000
Subject: [PATCH 13/20] fixup! python format
---
lldb/test/API/lang/cpp/this/TestCPPThis.py | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/lldb/test/API/lang/cpp/this/TestCPPThis.py b/lldb/test/API/lang/cpp/this/TestCPPThis.py
index cb462b0ae68de..3d87eba8fe05a 100644
--- a/lldb/test/API/lang/cpp/this/TestCPPThis.py
+++ b/lldb/test/API/lang/cpp/this/TestCPPThis.py
@@ -48,7 +48,8 @@ def test_with_run_command(self):
self.expect("expression -- (int)getpid(); m_a", startstr="(const int) $1 = 3")
self.expect(
- "expression --cpp-ignore-context-qualifiers -- m_a = 2", startstr="(int) $2 = 2"
+ "expression --cpp-ignore-context-qualifiers -- m_a = 2",
+ startstr="(int) $2 = 2",
)
self.runCmd("process continue")
>From 6612a28021cb7f51d6074019b1d879ce281faff8 Mon Sep 17 00:00:00 2001
From: Michael Buch <michaelbuch12 at gmail.com>
Date: Fri, 30 Jan 2026 11:24:16 +0000
Subject: [PATCH 14/20] fixup! fix typo
---
lldb/include/lldb/Target/Target.h | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/lldb/include/lldb/Target/Target.h b/lldb/include/lldb/Target/Target.h
index 6181da9041566..daaef03c0d1ef 100644
--- a/lldb/include/lldb/Target/Target.h
+++ b/lldb/include/lldb/Target/Target.h
@@ -546,7 +546,7 @@ class EvaluateExpressionOptions {
/// disallowed by C++ language rules).
///
/// FIXME: move this to a language-specific dictionary of options.
- bool m_cpp_ignore_context_qualifierss = false;
+ bool m_cpp_ignore_context_qualifiers = false;
};
// Target
>From f2b40b6e2997e0cad1ba860a597bc4dd097dc6ac Mon Sep 17 00:00:00 2001
From: Michael Buch <michaelbuch12 at gmail.com>
Date: Fri, 30 Jan 2026 12:40:41 +0000
Subject: [PATCH 15/20] fixup! rename option
---
.../const_method/TestExprInConstMethod.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/lldb/test/API/lang/cpp/expression-context-qualifiers/const_method/TestExprInConstMethod.py b/lldb/test/API/lang/cpp/expression-context-qualifiers/const_method/TestExprInConstMethod.py
index b501836a46483..74a68aae4817a 100644
--- a/lldb/test/API/lang/cpp/expression-context-qualifiers/const_method/TestExprInConstMethod.py
+++ b/lldb/test/API/lang/cpp/expression-context-qualifiers/const_method/TestExprInConstMethod.py
@@ -44,7 +44,7 @@ def test(self):
# Test --cpp-ignore-context-qualifiers via SBExpressionOptions.
options = lldb.SBExpressionOptions()
- options.SetIgnoreContextQualifiers()
+ options.SetCppIgnoreContextQualifiers()
self.expect_expr("m_mem = -2.0; m_mem", options=options, result_value="-2")
self.expect_expr("((Foo*)this)->bar()", result_type="double", result_value="5")
>From fb7604890639df39c703a618aa88e4c02858a146 Mon Sep 17 00:00:00 2001
From: Michael Buch <michaelbuch12 at gmail.com>
Date: Wed, 4 Feb 2026 15:32:43 +0000
Subject: [PATCH 16/20] fixup! rebase on new (S|G)etBooleanLanguageOption APIs
---
lldb/include/lldb/Target/Target.h | 7 +++++--
lldb/source/Target/Target.cpp | 20 +++++++++++++++++++
.../const_method/TestExprInConstMethod.py | 2 +-
.../TestExprInConstVolatileMethod.py | 2 +-
.../TestExprInNonConstMethod.py | 2 +-
.../TestExprInTemplateConstMethod.py | 2 +-
.../TestExprInTemplateNonConstMethod.py | 2 +-
7 files changed, 30 insertions(+), 7 deletions(-)
diff --git a/lldb/include/lldb/Target/Target.h b/lldb/include/lldb/Target/Target.h
index daaef03c0d1ef..264074728faad 100644
--- a/lldb/include/lldb/Target/Target.h
+++ b/lldb/include/lldb/Target/Target.h
@@ -308,8 +308,7 @@ class TargetProperties : public Properties {
class EvaluateExpressionOptions {
public:
- EvaluateExpressionOptions()
- : m_language_options_sp(std::make_shared<StructuredData::Dictionary>()) {}
+ EvaluateExpressionOptions();
// MSVC has a bug here that reports C4268: 'const' static/global data
// initialized with compiler generated default constructor fills the object
@@ -494,6 +493,10 @@ class EvaluateExpressionOptions {
llvm::Expected<bool>
GetBooleanLanguageOption(llvm::StringRef option_name) const;
+ void SetCppIgnoreContextQualifiers(bool value);
+
+ bool GetCppIgnoreContextQualifiers() const;
+
private:
const StructuredData::Dictionary &GetLanguageOptions() const;
diff --git a/lldb/source/Target/Target.cpp b/lldb/source/Target/Target.cpp
index 6b6dfa4aebe2b..f09b5082b8cdb 100644
--- a/lldb/source/Target/Target.cpp
+++ b/lldb/source/Target/Target.cpp
@@ -5398,3 +5398,23 @@ StructuredData::Dictionary &EvaluateExpressionOptions::GetLanguageOptions() {
return *m_language_options_sp;
}
+
+// FIXME: this option is C++ plugin specific and should be registered by it,
+// instead of hard-coding it here.
+constexpr llvm::StringLiteral s_cpp_ignore_context_qualifiers_option =
+ "cpp-ignore-context-qualifiers";
+
+EvaluateExpressionOptions::EvaluateExpressionOptions()
+ : m_language_options_sp(std::make_shared<StructuredData::Dictionary>()) {
+ SetCppIgnoreContextQualifiers(false);
+}
+
+void EvaluateExpressionOptions::SetCppIgnoreContextQualifiers(bool value) {
+ llvm::cantFail(
+ SetBooleanLanguageOption(s_cpp_ignore_context_qualifiers_option, value));
+}
+
+bool EvaluateExpressionOptions::GetCppIgnoreContextQualifiers() const {
+ return llvm::cantFail(
+ GetBooleanLanguageOption(s_cpp_ignore_context_qualifiers_option));
+}
diff --git a/lldb/test/API/lang/cpp/expression-context-qualifiers/const_method/TestExprInConstMethod.py b/lldb/test/API/lang/cpp/expression-context-qualifiers/const_method/TestExprInConstMethod.py
index 74a68aae4817a..c0f945d05340e 100644
--- a/lldb/test/API/lang/cpp/expression-context-qualifiers/const_method/TestExprInConstMethod.py
+++ b/lldb/test/API/lang/cpp/expression-context-qualifiers/const_method/TestExprInConstMethod.py
@@ -44,7 +44,7 @@ def test(self):
# Test --cpp-ignore-context-qualifiers via SBExpressionOptions.
options = lldb.SBExpressionOptions()
- options.SetCppIgnoreContextQualifiers()
+ options.SetBooleanLanguageOption("cpp-ignore-context-qualifiers")
self.expect_expr("m_mem = -2.0; m_mem", options=options, result_value="-2")
self.expect_expr("((Foo*)this)->bar()", result_type="double", result_value="5")
diff --git a/lldb/test/API/lang/cpp/expression-context-qualifiers/const_volatile_method/TestExprInConstVolatileMethod.py b/lldb/test/API/lang/cpp/expression-context-qualifiers/const_volatile_method/TestExprInConstVolatileMethod.py
index 40e5bff0aa60c..ab4a141cc3581 100644
--- a/lldb/test/API/lang/cpp/expression-context-qualifiers/const_volatile_method/TestExprInConstVolatileMethod.py
+++ b/lldb/test/API/lang/cpp/expression-context-qualifiers/const_volatile_method/TestExprInConstVolatileMethod.py
@@ -21,7 +21,7 @@ def test(self):
)
options = lldb.SBExpressionOptions()
- options.SetCppIgnoreContextQualifiers()
+ options.SetBooleanLanguageOption("cpp-ignore-context-qualifiers")
options.SetIgnoreBreakpoints(True)
self.expect_expr("volatile_method()", options=options)
self.expect(
diff --git a/lldb/test/API/lang/cpp/expression-context-qualifiers/non_const_method/TestExprInNonConstMethod.py b/lldb/test/API/lang/cpp/expression-context-qualifiers/non_const_method/TestExprInNonConstMethod.py
index c26239adb34ca..179415c927397 100644
--- a/lldb/test/API/lang/cpp/expression-context-qualifiers/non_const_method/TestExprInNonConstMethod.py
+++ b/lldb/test/API/lang/cpp/expression-context-qualifiers/non_const_method/TestExprInNonConstMethod.py
@@ -37,7 +37,7 @@ def test(self):
)
options = lldb.SBExpressionOptions()
- options.SetCppIgnoreContextQualifiers()
+ options.SetBooleanLanguageOption("cpp-ignore-context-qualifiers")
self.expect_expr("x = 6.0; x", options=options, result_value="6")
lldbutil.continue_to_source_breakpoint(
diff --git a/lldb/test/API/lang/cpp/expression-context-qualifiers/template_const_method/TestExprInTemplateConstMethod.py b/lldb/test/API/lang/cpp/expression-context-qualifiers/template_const_method/TestExprInTemplateConstMethod.py
index e4cd5ea759f60..c3f5f732ddf4f 100644
--- a/lldb/test/API/lang/cpp/expression-context-qualifiers/template_const_method/TestExprInTemplateConstMethod.py
+++ b/lldb/test/API/lang/cpp/expression-context-qualifiers/template_const_method/TestExprInTemplateConstMethod.py
@@ -30,7 +30,7 @@ def test(self):
self.expect_expr("((Foo*)this)->bar()", result_type="double", result_value="5")
options = lldb.SBExpressionOptions()
- options.SetCppIgnoreContextQualifiers()
+ options.SetBooleanLanguageOption("cpp-ignore-context-qualifiers")
self.expect_expr("m_mem = -2.0; m_mem", options=options, result_value="-2")
lldbutil.continue_to_source_breakpoint(
diff --git a/lldb/test/API/lang/cpp/expression-context-qualifiers/template_non_const_method/TestExprInTemplateNonConstMethod.py b/lldb/test/API/lang/cpp/expression-context-qualifiers/template_non_const_method/TestExprInTemplateNonConstMethod.py
index c26239adb34ca..179415c927397 100644
--- a/lldb/test/API/lang/cpp/expression-context-qualifiers/template_non_const_method/TestExprInTemplateNonConstMethod.py
+++ b/lldb/test/API/lang/cpp/expression-context-qualifiers/template_non_const_method/TestExprInTemplateNonConstMethod.py
@@ -37,7 +37,7 @@ def test(self):
)
options = lldb.SBExpressionOptions()
- options.SetCppIgnoreContextQualifiers()
+ options.SetBooleanLanguageOption("cpp-ignore-context-qualifiers")
self.expect_expr("x = 6.0; x", options=options, result_value="6")
lldbutil.continue_to_source_breakpoint(
>From f931c0dd76359827e9d0134566c2ddd44fe8bd6e Mon Sep 17 00:00:00 2001
From: Michael Buch <michaelbuch12 at gmail.com>
Date: Wed, 4 Feb 2026 16:40:44 +0000
Subject: [PATCH 17/20] fixup! remove unused member variable
---
lldb/include/lldb/Target/Target.h | 8 --------
1 file changed, 8 deletions(-)
diff --git a/lldb/include/lldb/Target/Target.h b/lldb/include/lldb/Target/Target.h
index 264074728faad..f781c4dabdd9f 100644
--- a/lldb/include/lldb/Target/Target.h
+++ b/lldb/include/lldb/Target/Target.h
@@ -542,14 +542,6 @@ class EvaluateExpressionOptions {
/// used for symbol/function lookup before any other context (except for
/// the module corresponding to the current frame).
SymbolContextList m_preferred_lookup_contexts;
-
- /// If true, evaluates the expression without taking into account the
- /// CV-qualifiers of the scope. E.g., this would permit calling a
- /// non-const C++ method when stopped in a const-method (which would be
- /// disallowed by C++ language rules).
- ///
- /// FIXME: move this to a language-specific dictionary of options.
- bool m_cpp_ignore_context_qualifiers = false;
};
// Target
>From e539b61eae6cfab24b2e75c3be4f7fb3287e7416 Mon Sep 17 00:00:00 2001
From: Michael Buch <michaelbuch12 at gmail.com>
Date: Wed, 4 Feb 2026 17:01:43 +0000
Subject: [PATCH 18/20] fixup! fix tests
---
.../const_method/TestExprInConstMethod.py | 2 +-
.../const_volatile_method/TestExprInConstVolatileMethod.py | 2 +-
.../non_const_method/TestExprInNonConstMethod.py | 2 +-
.../template_const_method/TestExprInTemplateConstMethod.py | 2 +-
.../TestExprInTemplateNonConstMethod.py | 2 +-
5 files changed, 5 insertions(+), 5 deletions(-)
diff --git a/lldb/test/API/lang/cpp/expression-context-qualifiers/const_method/TestExprInConstMethod.py b/lldb/test/API/lang/cpp/expression-context-qualifiers/const_method/TestExprInConstMethod.py
index c0f945d05340e..233ad5947848d 100644
--- a/lldb/test/API/lang/cpp/expression-context-qualifiers/const_method/TestExprInConstMethod.py
+++ b/lldb/test/API/lang/cpp/expression-context-qualifiers/const_method/TestExprInConstMethod.py
@@ -44,7 +44,7 @@ def test(self):
# Test --cpp-ignore-context-qualifiers via SBExpressionOptions.
options = lldb.SBExpressionOptions()
- options.SetBooleanLanguageOption("cpp-ignore-context-qualifiers")
+ options.SetBooleanLanguageOption("cpp-ignore-context-qualifiers", True)
self.expect_expr("m_mem = -2.0; m_mem", options=options, result_value="-2")
self.expect_expr("((Foo*)this)->bar()", result_type="double", result_value="5")
diff --git a/lldb/test/API/lang/cpp/expression-context-qualifiers/const_volatile_method/TestExprInConstVolatileMethod.py b/lldb/test/API/lang/cpp/expression-context-qualifiers/const_volatile_method/TestExprInConstVolatileMethod.py
index ab4a141cc3581..3b938d2f13119 100644
--- a/lldb/test/API/lang/cpp/expression-context-qualifiers/const_volatile_method/TestExprInConstVolatileMethod.py
+++ b/lldb/test/API/lang/cpp/expression-context-qualifiers/const_volatile_method/TestExprInConstVolatileMethod.py
@@ -21,7 +21,7 @@ def test(self):
)
options = lldb.SBExpressionOptions()
- options.SetBooleanLanguageOption("cpp-ignore-context-qualifiers")
+ options.SetBooleanLanguageOption("cpp-ignore-context-qualifiers", True)
options.SetIgnoreBreakpoints(True)
self.expect_expr("volatile_method()", options=options)
self.expect(
diff --git a/lldb/test/API/lang/cpp/expression-context-qualifiers/non_const_method/TestExprInNonConstMethod.py b/lldb/test/API/lang/cpp/expression-context-qualifiers/non_const_method/TestExprInNonConstMethod.py
index 179415c927397..39150d6031500 100644
--- a/lldb/test/API/lang/cpp/expression-context-qualifiers/non_const_method/TestExprInNonConstMethod.py
+++ b/lldb/test/API/lang/cpp/expression-context-qualifiers/non_const_method/TestExprInNonConstMethod.py
@@ -37,7 +37,7 @@ def test(self):
)
options = lldb.SBExpressionOptions()
- options.SetBooleanLanguageOption("cpp-ignore-context-qualifiers")
+ options.SetBooleanLanguageOption("cpp-ignore-context-qualifiers", True)
self.expect_expr("x = 6.0; x", options=options, result_value="6")
lldbutil.continue_to_source_breakpoint(
diff --git a/lldb/test/API/lang/cpp/expression-context-qualifiers/template_const_method/TestExprInTemplateConstMethod.py b/lldb/test/API/lang/cpp/expression-context-qualifiers/template_const_method/TestExprInTemplateConstMethod.py
index c3f5f732ddf4f..7fac61f65ba3b 100644
--- a/lldb/test/API/lang/cpp/expression-context-qualifiers/template_const_method/TestExprInTemplateConstMethod.py
+++ b/lldb/test/API/lang/cpp/expression-context-qualifiers/template_const_method/TestExprInTemplateConstMethod.py
@@ -30,7 +30,7 @@ def test(self):
self.expect_expr("((Foo*)this)->bar()", result_type="double", result_value="5")
options = lldb.SBExpressionOptions()
- options.SetBooleanLanguageOption("cpp-ignore-context-qualifiers")
+ options.SetBooleanLanguageOption("cpp-ignore-context-qualifiers", True)
self.expect_expr("m_mem = -2.0; m_mem", options=options, result_value="-2")
lldbutil.continue_to_source_breakpoint(
diff --git a/lldb/test/API/lang/cpp/expression-context-qualifiers/template_non_const_method/TestExprInTemplateNonConstMethod.py b/lldb/test/API/lang/cpp/expression-context-qualifiers/template_non_const_method/TestExprInTemplateNonConstMethod.py
index 179415c927397..39150d6031500 100644
--- a/lldb/test/API/lang/cpp/expression-context-qualifiers/template_non_const_method/TestExprInTemplateNonConstMethod.py
+++ b/lldb/test/API/lang/cpp/expression-context-qualifiers/template_non_const_method/TestExprInTemplateNonConstMethod.py
@@ -37,7 +37,7 @@ def test(self):
)
options = lldb.SBExpressionOptions()
- options.SetBooleanLanguageOption("cpp-ignore-context-qualifiers")
+ options.SetBooleanLanguageOption("cpp-ignore-context-qualifiers", True)
self.expect_expr("x = 6.0; x", options=options, result_value="6")
lldbutil.continue_to_source_breakpoint(
>From 6875a8e688e8fece30faae0de5bd74c50f8e51b3 Mon Sep 17 00:00:00 2001
From: Michael Buch <michaelbuch12 at gmail.com>
Date: Fri, 23 Jan 2026 15:21:39 +0000
Subject: [PATCH 19/20] [lldb][Expression] Emit hint to use
--ignore-const-context
Depends on:
* https://github.com/llvm/llvm-project/pull/177921
* https://github.com/llvm/llvm-project/pull/177926
(only last commit is relevant for this review)
---
lldb/include/lldb/Expression/UserExpression.h | 10 +++++
lldb/source/Expression/UserExpression.cpp | 3 ++
.../Clang/ClangUserExpression.cpp | 41 +++++++++++++++++++
.../Clang/ClangUserExpression.h | 4 ++
.../const_method/TestExprInConstMethod.py | 24 ++++++-----
.../TestExprInConstVolatileMethod.py | 14 ++++++-
.../fixit/TestExprInConstMethodWithFixit.py | 27 +++++++++++-
.../TestExprInNonConstMethod.py | 3 +-
.../TestExprInTemplateConstMethod.py | 12 ++++--
.../TestExprInTemplateNonConstMethod.py | 3 +-
lldb/test/API/lang/cpp/this/TestCPPThis.py | 3 +-
11 files changed, 124 insertions(+), 20 deletions(-)
diff --git a/lldb/include/lldb/Expression/UserExpression.h b/lldb/include/lldb/Expression/UserExpression.h
index 2fde73dafa035..977a0adef1cbe 100644
--- a/lldb/include/lldb/Expression/UserExpression.h
+++ b/lldb/include/lldb/Expression/UserExpression.h
@@ -313,6 +313,16 @@ class UserExpression : public Expression {
lldb::ProcessSP &process_sp,
lldb::StackFrameSP &frame_sp);
+ /// Called by expression evaluator when a parse error occurs. Gives this
+ /// UserExpression object a chance to inspect and adjust the error diagnostics
+ /// contained in the specified \c diagnostic_manager.
+ ///
+ /// \param[in,out] diagnostic_manager DiagnosticManager manager holding the
+ /// parse error diagnostics. This function may mutate the diagnostics.
+ ///
+ virtual void
+ FixupParseErrorDiagnostics(DiagnosticManager &diagnostic_manager) const {}
+
/// The address the process is stopped in.
Address m_address;
/// The text of the expression, as typed by the user.
diff --git a/lldb/source/Expression/UserExpression.cpp b/lldb/source/Expression/UserExpression.cpp
index 16a6218759f8f..d39bcced48390 100644
--- a/lldb/source/Expression/UserExpression.cpp
+++ b/lldb/source/Expression/UserExpression.cpp
@@ -323,6 +323,9 @@ UserExpression::Evaluate(ExecutionContext &exe_ctx,
}
if (!parse_success) {
+ if (user_expression_sp)
+ user_expression_sp->FixupParseErrorDiagnostics(diagnostic_manager);
+
if (target->GetEnableNotifyAboutFixIts() && fixed_expression &&
!fixed_expression->empty()) {
std::string fixit =
diff --git a/lldb/source/Plugins/ExpressionParser/Clang/ClangUserExpression.cpp b/lldb/source/Plugins/ExpressionParser/Clang/ClangUserExpression.cpp
index 634cdec918057..edb1b336858a0 100644
--- a/lldb/source/Plugins/ExpressionParser/Clang/ClangUserExpression.cpp
+++ b/lldb/source/Plugins/ExpressionParser/Clang/ClangUserExpression.cpp
@@ -27,6 +27,7 @@
#include "Plugins/TypeSystem/Clang/TypeSystemClang.h"
#include "lldb/Core/Debugger.h"
#include "lldb/Core/Module.h"
+#include "lldb/Expression/DiagnosticManager.h"
#include "lldb/Expression/ExpressionSourceCode.h"
#include "lldb/Expression/IRExecutionUnit.h"
#include "lldb/Expression/IRInterpreter.h"
@@ -55,6 +56,7 @@
#include "clang/AST/DeclCXX.h"
#include "clang/AST/DeclObjC.h"
+#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/ScopeExit.h"
#include "llvm/BinaryFormat/Dwarf.h"
@@ -946,6 +948,45 @@ lldb::ExpressionVariableSP ClangUserExpression::GetResultAfterDematerialization(
return m_result_delegate.GetVariable();
}
+void ClangUserExpression::FixupParseErrorDiagnostics(
+ DiagnosticManager &diagnostic_manager) const {
+ const bool is_fixable_cvr_error = llvm::any_of(
+ diagnostic_manager.Diagnostics(),
+ [](std::unique_ptr<Diagnostic> const &diag) {
+ switch (diag->GetCompilerID()) {
+ case clang::diag::err_member_function_call_bad_cvr:
+ return true;
+ case clang::diag::err_typecheck_assign_const:
+ // FIXME: can we split this particular error into a separate
+ // diagnostic ID so we don't need to scan the error message?
+ return diag->GetDetail().message.find(
+ "within const member function") != std::string::npos;
+ default:
+ return false;
+ }
+ });
+
+ // Nothing to report.
+ if (!is_fixable_cvr_error)
+ return;
+
+ // If the user already tried ignoring function qualifiers but
+ // the expression still failed, we don't want to suggest the hint again.
+ if (m_options.GetIgnoreContextQualifiers()) {
+ // Hard to prove that we don't get here so don't emit a diagnostic n
+ // non-asserts builds. But we do want a signal in asserts builds.
+ assert(false &&
+ "IgnoreContextQualifiers didn't resolve compiler diagnostic.");
+ return;
+ }
+
+ diagnostic_manager.AddDiagnostic(
+ "Possibly trying to mutate object in a const context. Try "
+ "running the expression with: expression --ignore-context-qualifiers "
+ "-- <your expression>",
+ lldb::eSeverityInfo, eDiagnosticOriginLLDB);
+}
+
char ClangUserExpression::ClangUserExpressionHelper::ID;
void ClangUserExpression::ClangUserExpressionHelper::ResetDeclMap(
diff --git a/lldb/source/Plugins/ExpressionParser/Clang/ClangUserExpression.h b/lldb/source/Plugins/ExpressionParser/Clang/ClangUserExpression.h
index 7f1c1ddcad942..5b5687320d3d7 100644
--- a/lldb/source/Plugins/ExpressionParser/Clang/ClangUserExpression.h
+++ b/lldb/source/Plugins/ExpressionParser/Clang/ClangUserExpression.h
@@ -179,6 +179,10 @@ class ClangUserExpression : public LLVMUserExpression {
llvm::StringRef GetFilename() const { return m_filename; }
+protected:
+ void FixupParseErrorDiagnostics(
+ DiagnosticManager &diagnostic_manager) const override;
+
private:
/// Populate m_in_cplusplus_method and m_in_objectivec_method based on the
/// environment.
diff --git a/lldb/test/API/lang/cpp/expression-context-qualifiers/const_method/TestExprInConstMethod.py b/lldb/test/API/lang/cpp/expression-context-qualifiers/const_method/TestExprInConstMethod.py
index 233ad5947848d..fa3ea3785db78 100644
--- a/lldb/test/API/lang/cpp/expression-context-qualifiers/const_method/TestExprInConstMethod.py
+++ b/lldb/test/API/lang/cpp/expression-context-qualifiers/const_method/TestExprInConstMethod.py
@@ -16,15 +16,16 @@ def test(self):
"expression m_const_mem = 2.0",
error=True,
substrs=[
- "cannot assign to non-static data member",
- "with const-qualified type",
+ "note: Possibly trying to mutate object in a const context. Try running the expression with",
],
+ matching=False,
)
self.expect(
"expression m_mem = 2.0",
error=True,
substrs=[
- "cannot assign to non-static data member within const member function"
+ "cannot assign to non-static data member within const member function",
+ "note: Possibly trying to mutate object in a const context. Try running the expression with",
],
)
self.expect_expr("m_mem", result_value="-2")
@@ -60,7 +61,8 @@ def test(self):
"expression x = 7.0",
error=True,
substrs=[
- "cannot assign to non-static data member within const member function"
+ "cannot assign to non-static data member within const member function",
+ "note: Possibly trying to mutate object in a const context. Try running the expression with",
],
)
self.expect_expr("x", result_value="2")
@@ -87,15 +89,16 @@ def test(self):
"expression m_const_mem = 2.0",
error=True,
substrs=[
- "cannot assign to non-static data member",
- "with const-qualified type",
+ "note: Possibly trying to mutate object in a const context. Try running the expression with",
],
+ matching=False,
)
self.expect(
"expression m_mem = 2.0",
error=True,
substrs=[
- "cannot assign to non-static data member within const member function"
+ "cannot assign to non-static data member within const member function",
+ "note: Possibly trying to mutate object in a const context. Try running the expression with",
],
)
self.expect_expr("m_mem", result_value="-2")
@@ -117,15 +120,16 @@ def test(self):
"expression m_const_mem = 2.0",
error=True,
substrs=[
- "cannot assign to non-static data member",
- "with const-qualified type",
+ "note: Possibly trying to mutate object in a const context. Try running the expression with",
],
+ matching=False,
)
self.expect(
"expression m_mem = 2.0",
error=True,
substrs=[
- "cannot assign to non-static data member within const member function"
+ "cannot assign to non-static data member within const member function",
+ "note: Possibly trying to mutate object in a const context. Try running the expression with",
],
)
self.expect_expr("m_mem", result_value="-1")
diff --git a/lldb/test/API/lang/cpp/expression-context-qualifiers/const_volatile_method/TestExprInConstVolatileMethod.py b/lldb/test/API/lang/cpp/expression-context-qualifiers/const_volatile_method/TestExprInConstVolatileMethod.py
index 3b938d2f13119..dd40a67831446 100644
--- a/lldb/test/API/lang/cpp/expression-context-qualifiers/const_volatile_method/TestExprInConstVolatileMethod.py
+++ b/lldb/test/API/lang/cpp/expression-context-qualifiers/const_volatile_method/TestExprInConstVolatileMethod.py
@@ -17,7 +17,11 @@ def test(self):
self.expect(
"expression volatile_method()",
error=True,
- substrs=["has type 'const Foo'", "but function is not marked const"],
+ substrs=[
+ "has type 'const Foo'",
+ "but function is not marked const",
+ "note: Possibly trying to mutate object in a const context. Try running the expression with",
+ ],
)
options = lldb.SBExpressionOptions()
@@ -41,7 +45,11 @@ def test(self):
self.expect(
"expression const_method()",
error=True,
- substrs=["has type 'volatile Foo'", "but function is not marked volatile"],
+ substrs=[
+ "has type 'volatile Foo'",
+ "but function is not marked volatile",
+ "note: Possibly trying to mutate object in a const context. Try running the expression with",
+ ],
)
self.expect_expr("volatile_method()")
@@ -65,6 +73,7 @@ def test(self):
substrs=[
"has type 'const volatile Foo'",
"but function is not marked const or volatile",
+ "note: Possibly trying to mutate object in a const context. Try running the expression with",
],
)
self.expect(
@@ -73,6 +82,7 @@ def test(self):
substrs=[
"has type 'const volatile Foo'",
"but function is not marked const or volatile",
+ "note: Possibly trying to mutate object in a const context. Try running the expression with",
],
)
diff --git a/lldb/test/API/lang/cpp/expression-context-qualifiers/fixit/TestExprInConstMethodWithFixit.py b/lldb/test/API/lang/cpp/expression-context-qualifiers/fixit/TestExprInConstMethodWithFixit.py
index 3c239c89d0ca7..9461c41a6129f 100644
--- a/lldb/test/API/lang/cpp/expression-context-qualifiers/fixit/TestExprInConstMethodWithFixit.py
+++ b/lldb/test/API/lang/cpp/expression-context-qualifiers/fixit/TestExprInConstMethodWithFixit.py
@@ -17,10 +17,11 @@ def test(self):
substrs=[
"member reference type 'const Bar' is not a pointer",
"but function is not marked const",
+ "Possibly trying to mutate object in a const context. Try running the expression with",
],
)
- # Two fix-its
+ # Two fix-its...
self.expect(
"expression -- m_bar->method() + m_bar->method()",
error=True,
@@ -32,11 +33,35 @@ def test(self):
],
)
+ # ...only emit a single hint.
+ self.assertEqual(
+ self.res.GetError().count(
+ "Possibly trying to mutate object in a const context."
+ ),
+ 1,
+ )
+
+ self.expect(
+ "expression -Q -- m_bar->method()",
+ error=True,
+ substrs=["Evaluated this expression after applying Fix-It(s):"],
+ )
+
self.expect(
"expression m_bar->method() + blah",
error=True,
substrs=[
"member reference type 'const Bar' is not a pointer",
"but function is not marked const",
+ "Possibly trying to mutate object in a const context. Try running the expression with",
+ ],
+ )
+
+ self.expect(
+ "expression -K -- m_bar->method() + blah",
+ error=True,
+ substrs=[
+ "Possibly trying to mutate object in a const context. Try running the expression with",
],
+ matching=False,
)
diff --git a/lldb/test/API/lang/cpp/expression-context-qualifiers/non_const_method/TestExprInNonConstMethod.py b/lldb/test/API/lang/cpp/expression-context-qualifiers/non_const_method/TestExprInNonConstMethod.py
index 39150d6031500..297bdd1663756 100644
--- a/lldb/test/API/lang/cpp/expression-context-qualifiers/non_const_method/TestExprInNonConstMethod.py
+++ b/lldb/test/API/lang/cpp/expression-context-qualifiers/non_const_method/TestExprInNonConstMethod.py
@@ -32,7 +32,8 @@ def test(self):
"expression x = 7.0",
error=True,
substrs=[
- "cannot assign to non-static data member within const member function"
+ "cannot assign to non-static data member within const member function",
+ "note: Possibly trying to mutate object in a const context. Try running the expression with",
],
)
diff --git a/lldb/test/API/lang/cpp/expression-context-qualifiers/template_const_method/TestExprInTemplateConstMethod.py b/lldb/test/API/lang/cpp/expression-context-qualifiers/template_const_method/TestExprInTemplateConstMethod.py
index 7fac61f65ba3b..a84a17e5dffd5 100644
--- a/lldb/test/API/lang/cpp/expression-context-qualifiers/template_const_method/TestExprInTemplateConstMethod.py
+++ b/lldb/test/API/lang/cpp/expression-context-qualifiers/template_const_method/TestExprInTemplateConstMethod.py
@@ -24,7 +24,8 @@ def test(self):
"expression m_mem = 2.0",
error=True,
substrs=[
- "cannot assign to non-static data member within const member function"
+ "cannot assign to non-static data member within const member function",
+ "note: Possibly trying to mutate object in a const context. Try running the expression with",
],
)
self.expect_expr("((Foo*)this)->bar()", result_type="double", result_value="5")
@@ -44,7 +45,8 @@ def test(self):
"expression x = 7.0",
error=True,
substrs=[
- "cannot assign to non-static data member within const member function"
+ "cannot assign to non-static data member within const member function",
+ "note: Possibly trying to mutate object in a const context. Try running the expression with",
],
)
self.expect_expr("x = -7.0; x", options=options, result_value="-7")
@@ -77,7 +79,8 @@ def test(self):
"expression m_mem = 2.0",
error=True,
substrs=[
- "cannot assign to non-static data member within const member function"
+ "cannot assign to non-static data member within const member function",
+ "note: Possibly trying to mutate object in a const context. Try running the expression with",
],
)
self.expect_expr("m_mem", result_value="-2")
@@ -105,7 +108,8 @@ def test(self):
"expression m_mem = 2.0",
error=True,
substrs=[
- "cannot assign to non-static data member within const member function"
+ "cannot assign to non-static data member within const member function",
+ "note: Possibly trying to mutate object in a const context. Try running the expression with",
],
)
self.expect_expr("m_mem = -11.0; m_mem", options=options, result_value="-11")
diff --git a/lldb/test/API/lang/cpp/expression-context-qualifiers/template_non_const_method/TestExprInTemplateNonConstMethod.py b/lldb/test/API/lang/cpp/expression-context-qualifiers/template_non_const_method/TestExprInTemplateNonConstMethod.py
index 39150d6031500..297bdd1663756 100644
--- a/lldb/test/API/lang/cpp/expression-context-qualifiers/template_non_const_method/TestExprInTemplateNonConstMethod.py
+++ b/lldb/test/API/lang/cpp/expression-context-qualifiers/template_non_const_method/TestExprInTemplateNonConstMethod.py
@@ -32,7 +32,8 @@ def test(self):
"expression x = 7.0",
error=True,
substrs=[
- "cannot assign to non-static data member within const member function"
+ "cannot assign to non-static data member within const member function",
+ "note: Possibly trying to mutate object in a const context. Try running the expression with",
],
)
diff --git a/lldb/test/API/lang/cpp/this/TestCPPThis.py b/lldb/test/API/lang/cpp/this/TestCPPThis.py
index 3d87eba8fe05a..5e648c6229182 100644
--- a/lldb/test/API/lang/cpp/this/TestCPPThis.py
+++ b/lldb/test/API/lang/cpp/this/TestCPPThis.py
@@ -42,7 +42,8 @@ def test_with_run_command(self):
"expression -- m_a = 2",
error=True,
substrs=[
- "cannot assign to non-static data member within const member function"
+ "cannot assign to non-static data member within const member function",
+ "Possibly trying to mutate object in a const context. Try running the expression with",
],
)
>From 0c1f7bf6b8e8112d18e9f76185c2344e593a03e6 Mon Sep 17 00:00:00 2001
From: Michael Buch <michaelbuch12 at gmail.com>
Date: Fri, 30 Jan 2026 11:20:31 +0000
Subject: [PATCH 20/20] fixup! rename option
---
.../Plugins/ExpressionParser/Clang/ClangUserExpression.cpp | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/lldb/source/Plugins/ExpressionParser/Clang/ClangUserExpression.cpp b/lldb/source/Plugins/ExpressionParser/Clang/ClangUserExpression.cpp
index edb1b336858a0..c71b3d813ea9b 100644
--- a/lldb/source/Plugins/ExpressionParser/Clang/ClangUserExpression.cpp
+++ b/lldb/source/Plugins/ExpressionParser/Clang/ClangUserExpression.cpp
@@ -972,17 +972,17 @@ void ClangUserExpression::FixupParseErrorDiagnostics(
// If the user already tried ignoring function qualifiers but
// the expression still failed, we don't want to suggest the hint again.
- if (m_options.GetIgnoreContextQualifiers()) {
+ if (m_options.GetCppIgnoreContextQualifiers()) {
// Hard to prove that we don't get here so don't emit a diagnostic n
// non-asserts builds. But we do want a signal in asserts builds.
assert(false &&
- "IgnoreContextQualifiers didn't resolve compiler diagnostic.");
+ "CppIgnoreContextQualifiers didn't resolve compiler diagnostic.");
return;
}
diagnostic_manager.AddDiagnostic(
"Possibly trying to mutate object in a const context. Try "
- "running the expression with: expression --ignore-context-qualifiers "
+ "running the expression with: expression --cpp-ignore-context-qualifiers "
"-- <your expression>",
lldb::eSeverityInfo, eDiagnosticOriginLLDB);
}
More information about the lldb-commits
mailing list