Author: Michael Buch
Date: 2026-02-05T07:32:26Z
New Revision: c64257b4b767595eb078386828419f6403e1c140

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

LOG: [lldb][Expression] Make __lldb_expr function qualifiers match source 
context (#177922)

We stopped marking `__lldb_expr` with the function qualifiers of the
method LLDB is stopped in ever since
`8bdcd522510f923185cdfaec66c4a78d0a0d38c0`. The assumption was that it
wasn't ever required for correctness (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. E.g., if a function is overloaded on qualifiers
(`const` vs. `non-const`), the expression evaluator would currently
always call the non-CV qualified overload.

This patch adds function qualifiers to `$__lldb_class::$__lldb_expr`
that resemble the qualifiers of the method that we're stopped in.

However, mutating variables or calling arbitrary member functions from
CV-qualified methods can be useful/is something users already may be
used to. To provide users with the ability to ignore the CV-qualifiers
of the current context, we will provide an expression evaluator flag
that switches this off in a follow-up patch.

Added: 
    lldb/test/API/lang/cpp/expression-context-qualifiers/const_method/Makefile
    
lldb/test/API/lang/cpp/expression-context-qualifiers/const_method/TestExprInConstMethod.py
    lldb/test/API/lang/cpp/expression-context-qualifiers/const_method/main.cpp
    
lldb/test/API/lang/cpp/expression-context-qualifiers/const_volatile_method/Makefile
    
lldb/test/API/lang/cpp/expression-context-qualifiers/const_volatile_method/TestExprInConstVolatileMethod.py
    
lldb/test/API/lang/cpp/expression-context-qualifiers/const_volatile_method/main.cpp
    
lldb/test/API/lang/cpp/expression-context-qualifiers/cv_qualified_objects/Makefile
    
lldb/test/API/lang/cpp/expression-context-qualifiers/cv_qualified_objects/TestExprOnCVQualifiedObjects.py
    
lldb/test/API/lang/cpp/expression-context-qualifiers/cv_qualified_objects/main.cpp
    lldb/test/API/lang/cpp/expression-context-qualifiers/fixit/Makefile
    
lldb/test/API/lang/cpp/expression-context-qualifiers/fixit/TestExprInConstMethodWithFixit.py
    lldb/test/API/lang/cpp/expression-context-qualifiers/fixit/main.cpp
    
lldb/test/API/lang/cpp/expression-context-qualifiers/non_const_method/Makefile
    
lldb/test/API/lang/cpp/expression-context-qualifiers/non_const_method/TestExprInNonConstMethod.py
    
lldb/test/API/lang/cpp/expression-context-qualifiers/non_const_method/main.cpp
    
lldb/test/API/lang/cpp/expression-context-qualifiers/template_const_method/Makefile
    
lldb/test/API/lang/cpp/expression-context-qualifiers/template_const_method/TestExprInTemplateConstMethod.py
    
lldb/test/API/lang/cpp/expression-context-qualifiers/template_const_method/main.cpp
    
lldb/test/API/lang/cpp/expression-context-qualifiers/template_non_const_method/Makefile
    
lldb/test/API/lang/cpp/expression-context-qualifiers/template_non_const_method/TestExprInTemplateNonConstMethod.py
    
lldb/test/API/lang/cpp/expression-context-qualifiers/template_non_const_method/main.cpp

Modified: 
    lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionDeclMap.cpp
    lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionSourceCode.cpp
    
lldb/test/API/commands/expression/expr_inside_lambda/TestExprInsideLambdas.py
    lldb/test/API/lang/cpp/const_this/TestConstThis.py
    lldb/test/API/lang/cpp/this/TestCPPThis.py

Removed: 
    


################################################################################
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(


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

Reply via email to