https://github.com/Michael137 updated https://github.com/llvm/llvm-project/pull/177927
>From 03aba9f0dc11d179882f0d65c8f9fb166745ac8c Mon Sep 17 00:00:00 2001 From: Michael Buch <[email protected]> Date: Fri, 23 Jan 2026 15:21:39 +0000 Subject: [PATCH 1/4] [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 25c76b69aa3ab..a00fe4d5f314b 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 f1bcfb3c0827d..6d983dc5108a8 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 f6358d2037095..280cf9249d82e 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 cdaa06b8808b5..f26bf618d4339 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 f6358d2037095..280cf9249d82e 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 4c4db30589fbf..20f944cbe4729 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 37fce7afe528af545a139c4bb33e4af828c42fca Mon Sep 17 00:00:00 2001 From: Michael Buch <[email protected]> Date: Fri, 30 Jan 2026 11:20:31 +0000 Subject: [PATCH 2/4] 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); } >From 30ff57cbc683939b397451a6e4bf183f50643e5d Mon Sep 17 00:00:00 2001 From: Michael Buch <[email protected]> Date: Wed, 4 Feb 2026 23:36:22 +0000 Subject: [PATCH 3/4] fixup! cpp -> c++ --- .../Plugins/ExpressionParser/Clang/ClangUserExpression.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lldb/source/Plugins/ExpressionParser/Clang/ClangUserExpression.cpp b/lldb/source/Plugins/ExpressionParser/Clang/ClangUserExpression.cpp index c71b3d813ea9b..5bf6452b8fcd7 100644 --- a/lldb/source/Plugins/ExpressionParser/Clang/ClangUserExpression.cpp +++ b/lldb/source/Plugins/ExpressionParser/Clang/ClangUserExpression.cpp @@ -982,7 +982,7 @@ void ClangUserExpression::FixupParseErrorDiagnostics( diagnostic_manager.AddDiagnostic( "Possibly trying to mutate object in a const context. Try " - "running the expression with: expression --cpp-ignore-context-qualifiers " + "running the expression with: expression --c++-ignore-context-qualifiers " "-- <your expression>", lldb::eSeverityInfo, eDiagnosticOriginLLDB); } >From 6d8ec2bc04dd12d6fd73465a5665cf36f5a1aee2 Mon Sep 17 00:00:00 2001 From: Michael Buch <[email protected]> Date: Thu, 5 Feb 2026 08:37:35 +0000 Subject: [PATCH 4/4] fixup! add expression text to hint --- .../Plugins/ExpressionParser/Clang/ClangUserExpression.cpp | 7 ++++--- .../fixit/TestExprInConstMethodWithFixit.py | 4 +++- .../TestExprInTemplateNonConstMethod.py | 1 + lldb/test/API/lang/cpp/this/TestCPPThis.py | 2 +- 4 files changed, 9 insertions(+), 5 deletions(-) diff --git a/lldb/source/Plugins/ExpressionParser/Clang/ClangUserExpression.cpp b/lldb/source/Plugins/ExpressionParser/Clang/ClangUserExpression.cpp index 5bf6452b8fcd7..fb16a6bd0ad1d 100644 --- a/lldb/source/Plugins/ExpressionParser/Clang/ClangUserExpression.cpp +++ b/lldb/source/Plugins/ExpressionParser/Clang/ClangUserExpression.cpp @@ -980,11 +980,12 @@ void ClangUserExpression::FixupParseErrorDiagnostics( return; } - diagnostic_manager.AddDiagnostic( + diagnostic_manager.Printf( + lldb::eSeverityInfo, "Possibly trying to mutate object in a const context. Try " "running the expression with: expression --c++-ignore-context-qualifiers " - "-- <your expression>", - lldb::eSeverityInfo, eDiagnosticOriginLLDB); + "-- %s", + !m_fixed_text.empty() ? m_fixed_text.c_str() : m_expr_text.c_str()); } char ClangUserExpression::ClangUserExpressionHelper::ID; 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 9461c41a6129f..80e657712a525 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 @@ -18,6 +18,7 @@ def test(self): "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", + "expression --c++-ignore-context-qualifiers -- m_bar.method()", ], ) @@ -54,11 +55,12 @@ def test(self): "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", + "expression --c++-ignore-context-qualifiers -- m_bar.method() + blah", ], ) self.expect( - "expression -K -- m_bar->method() + blah", + "expression -Q -- m_bar->method() + blah", error=True, substrs=[ "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_non_const_method/TestExprInTemplateNonConstMethod.py b/lldb/test/API/lang/cpp/expression-context-qualifiers/template_non_const_method/TestExprInTemplateNonConstMethod.py index 280cf9249d82e..dd84099d6060e 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 @@ -34,6 +34,7 @@ def test(self): substrs=[ "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", + "expression --c++-ignore-context-qualifiers -- x = 7.0", ], ) diff --git a/lldb/test/API/lang/cpp/this/TestCPPThis.py b/lldb/test/API/lang/cpp/this/TestCPPThis.py index 20f944cbe4729..81be8a80b84c0 100644 --- a/lldb/test/API/lang/cpp/this/TestCPPThis.py +++ b/lldb/test/API/lang/cpp/this/TestCPPThis.py @@ -43,7 +43,7 @@ def test_with_run_command(self): error=True, substrs=[ "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", + "Possibly trying to mutate object in a const context. Try running the expression with: expression --c++-ignore-context-qualifiers -- m_a = 2", ], ) _______________________________________________ lldb-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits
