Skip to content

Commit f3428fd

Browse files
committed
[lldb][Expression] Emit hint to use --c++-ignore-context-qualifiers (llvm#177927)
Depends on: * llvm#177921 * llvm#177926 (only last commit is relevant for this review) This patch emits a workaround suggestion (in the form of a `note:` diagnostic) when an expression fails due to trying to mutate state/call functions with CV-qualifiers that are disallowed by C++ language rules based on the context the expression is evaluated in. The note looks as follows: ``` (lldb) expr next.method() ˄ ╰─ error: 'this' argument to member function 'method' has type 'const Bar', but function is not marked const note: Ran expression as 'C++14'. note: note: 'method' declared here note: Possibly trying to mutate object in a const context. Try running the expression with: expression --c++-ignore-context-qualifiers -- <your expression> ``` (cherry picked from commit 58912f3)
1 parent 4947932 commit f3428fd

11 files changed

Lines changed: 128 additions & 20 deletions

File tree

lldb/include/lldb/Expression/UserExpression.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -312,6 +312,16 @@ class UserExpression : public Expression {
312312
lldb::ProcessSP &process_sp,
313313
lldb::StackFrameSP &frame_sp);
314314

315+
/// Called by expression evaluator when a parse error occurs. Gives this
316+
/// UserExpression object a chance to inspect and adjust the error diagnostics
317+
/// contained in the specified \c diagnostic_manager.
318+
///
319+
/// \param[in,out] diagnostic_manager DiagnosticManager manager holding the
320+
/// parse error diagnostics. This function may mutate the diagnostics.
321+
///
322+
virtual void
323+
FixupParseErrorDiagnostics(DiagnosticManager &diagnostic_manager) const {}
324+
315325
/// The address the process is stopped in.
316326
Address m_address;
317327
/// The text of the expression, as typed by the user.

lldb/source/Expression/UserExpression.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -330,6 +330,9 @@ UserExpression::Evaluate(ExecutionContext &exe_ctx,
330330
}
331331

332332
if (!parse_success) {
333+
if (user_expression_sp)
334+
user_expression_sp->FixupParseErrorDiagnostics(diagnostic_manager);
335+
333336
if (target->GetEnableNotifyAboutFixIts() && fixed_expression &&
334337
!fixed_expression->empty()) {
335338
std::string fixit =

lldb/source/Plugins/ExpressionParser/Clang/ClangUserExpression.cpp

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
#include "Plugins/TypeSystem/Clang/TypeSystemClang.h"
2828
#include "lldb/Core/Debugger.h"
2929
#include "lldb/Core/Module.h"
30+
#include "lldb/Expression/DiagnosticManager.h"
3031
#include "lldb/Expression/ExpressionSourceCode.h"
3132
#include "lldb/Expression/IRExecutionUnit.h"
3233
#include "lldb/Expression/IRInterpreter.h"
@@ -55,6 +56,7 @@
5556
#include "clang/AST/DeclCXX.h"
5657
#include "clang/AST/DeclObjC.h"
5758

59+
#include "llvm/ADT/STLExtras.h"
5860
#include "llvm/ADT/ScopeExit.h"
5961
#include "llvm/BinaryFormat/Dwarf.h"
6062

@@ -946,6 +948,46 @@ lldb::ExpressionVariableSP ClangUserExpression::GetResultAfterDematerialization(
946948
return m_result_delegate.GetVariable();
947949
}
948950

951+
void ClangUserExpression::FixupParseErrorDiagnostics(
952+
DiagnosticManager &diagnostic_manager) const {
953+
const bool is_fixable_cvr_error = llvm::any_of(
954+
diagnostic_manager.Diagnostics(),
955+
[](std::unique_ptr<Diagnostic> const &diag) {
956+
switch (diag->GetCompilerID()) {
957+
case clang::diag::err_member_function_call_bad_cvr:
958+
return true;
959+
case clang::diag::err_typecheck_assign_const:
960+
// FIXME: can we split this particular error into a separate
961+
// diagnostic ID so we don't need to scan the error message?
962+
return diag->GetDetail().message.find(
963+
"within const member function") != std::string::npos;
964+
default:
965+
return false;
966+
}
967+
});
968+
969+
// Nothing to report.
970+
if (!is_fixable_cvr_error)
971+
return;
972+
973+
// If the user already tried ignoring function qualifiers but
974+
// the expression still failed, we don't want to suggest the hint again.
975+
if (m_options.GetCppIgnoreContextQualifiers()) {
976+
// Hard to prove that we don't get here so don't emit a diagnostic n
977+
// non-asserts builds. But we do want a signal in asserts builds.
978+
assert(false &&
979+
"CppIgnoreContextQualifiers didn't resolve compiler diagnostic.");
980+
return;
981+
}
982+
983+
diagnostic_manager.Printf(
984+
lldb::eSeverityInfo,
985+
"Possibly trying to mutate object in a const context. Try "
986+
"running the expression with: expression --c++-ignore-context-qualifiers "
987+
"-- %s",
988+
!m_fixed_text.empty() ? m_fixed_text.c_str() : m_expr_text.c_str());
989+
}
990+
949991
char ClangUserExpression::ClangUserExpressionHelper::ID;
950992

951993
void ClangUserExpression::ClangUserExpressionHelper::ResetDeclMap(

lldb/source/Plugins/ExpressionParser/Clang/ClangUserExpression.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,10 @@ class ClangUserExpression : public LLVMUserExpression {
188188

189189
llvm::StringRef GetFilename() const { return m_filename; }
190190

191+
protected:
192+
void FixupParseErrorDiagnostics(
193+
DiagnosticManager &diagnostic_manager) const override;
194+
191195
private:
192196
/// Populate m_in_cplusplus_method and m_in_objectivec_method based on the
193197
/// environment.

lldb/test/API/lang/cpp/expression-context-qualifiers/const_method/TestExprInConstMethod.py

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -16,15 +16,16 @@ def test(self):
1616
"expression m_const_mem = 2.0",
1717
error=True,
1818
substrs=[
19-
"cannot assign to non-static data member",
20-
"with const-qualified type",
19+
"note: Possibly trying to mutate object in a const context. Try running the expression with",
2120
],
21+
matching=False,
2222
)
2323
self.expect(
2424
"expression m_mem = 2.0",
2525
error=True,
2626
substrs=[
27-
"cannot assign to non-static data member within const member function"
27+
"cannot assign to non-static data member within const member function",
28+
"note: Possibly trying to mutate object in a const context. Try running the expression with",
2829
],
2930
)
3031
self.expect_expr("m_mem", result_value="-2")
@@ -60,7 +61,8 @@ def test(self):
6061
"expression x = 7.0",
6162
error=True,
6263
substrs=[
63-
"cannot assign to non-static data member within const member function"
64+
"cannot assign to non-static data member within const member function",
65+
"note: Possibly trying to mutate object in a const context. Try running the expression with",
6466
],
6567
)
6668
self.expect_expr("x", result_value="2")
@@ -87,15 +89,16 @@ def test(self):
8789
"expression m_const_mem = 2.0",
8890
error=True,
8991
substrs=[
90-
"cannot assign to non-static data member",
91-
"with const-qualified type",
92+
"note: Possibly trying to mutate object in a const context. Try running the expression with",
9293
],
94+
matching=False,
9395
)
9496
self.expect(
9597
"expression m_mem = 2.0",
9698
error=True,
9799
substrs=[
98-
"cannot assign to non-static data member within const member function"
100+
"cannot assign to non-static data member within const member function",
101+
"note: Possibly trying to mutate object in a const context. Try running the expression with",
99102
],
100103
)
101104
self.expect_expr("m_mem", result_value="-2")
@@ -117,15 +120,16 @@ def test(self):
117120
"expression m_const_mem = 2.0",
118121
error=True,
119122
substrs=[
120-
"cannot assign to non-static data member",
121-
"with const-qualified type",
123+
"note: Possibly trying to mutate object in a const context. Try running the expression with",
122124
],
125+
matching=False,
123126
)
124127
self.expect(
125128
"expression m_mem = 2.0",
126129
error=True,
127130
substrs=[
128-
"cannot assign to non-static data member within const member function"
131+
"cannot assign to non-static data member within const member function",
132+
"note: Possibly trying to mutate object in a const context. Try running the expression with",
129133
],
130134
)
131135
self.expect_expr("m_mem", result_value="-1")

lldb/test/API/lang/cpp/expression-context-qualifiers/const_volatile_method/TestExprInConstVolatileMethod.py

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,11 @@ def test(self):
1717
self.expect(
1818
"expression volatile_method()",
1919
error=True,
20-
substrs=["has type 'const Foo'", "but function is not marked const"],
20+
substrs=[
21+
"has type 'const Foo'",
22+
"but function is not marked const",
23+
"note: Possibly trying to mutate object in a const context. Try running the expression with",
24+
],
2125
)
2226

2327
options = lldb.SBExpressionOptions()
@@ -41,7 +45,11 @@ def test(self):
4145
self.expect(
4246
"expression const_method()",
4347
error=True,
44-
substrs=["has type 'volatile Foo'", "but function is not marked volatile"],
48+
substrs=[
49+
"has type 'volatile Foo'",
50+
"but function is not marked volatile",
51+
"note: Possibly trying to mutate object in a const context. Try running the expression with",
52+
],
4553
)
4654
self.expect_expr("volatile_method()")
4755

@@ -65,6 +73,7 @@ def test(self):
6573
substrs=[
6674
"has type 'const volatile Foo'",
6775
"but function is not marked const or volatile",
76+
"note: Possibly trying to mutate object in a const context. Try running the expression with",
6877
],
6978
)
7079
self.expect(
@@ -73,6 +82,7 @@ def test(self):
7382
substrs=[
7483
"has type 'const volatile Foo'",
7584
"but function is not marked const or volatile",
85+
"note: Possibly trying to mutate object in a const context. Try running the expression with",
7686
],
7787
)
7888

lldb/test/API/lang/cpp/expression-context-qualifiers/fixit/TestExprInConstMethodWithFixit.py

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,12 @@ def test(self):
1717
substrs=[
1818
"member reference type 'const Bar' is not a pointer",
1919
"but function is not marked const",
20+
"Possibly trying to mutate object in a const context. Try running the expression with",
21+
"expression --c++-ignore-context-qualifiers -- m_bar.method()",
2022
],
2123
)
2224

23-
# Two fix-its
25+
# Two fix-its...
2426
self.expect(
2527
"expression -- m_bar->method() + m_bar->method()",
2628
error=True,
@@ -32,11 +34,36 @@ def test(self):
3234
],
3335
)
3436

37+
# ...only emit a single hint.
38+
self.assertEqual(
39+
self.res.GetError().count(
40+
"Possibly trying to mutate object in a const context."
41+
),
42+
1,
43+
)
44+
45+
self.expect(
46+
"expression -Q -- m_bar->method()",
47+
error=True,
48+
substrs=["Evaluated this expression after applying Fix-It(s):"],
49+
)
50+
3551
self.expect(
3652
"expression m_bar->method() + blah",
3753
error=True,
3854
substrs=[
3955
"member reference type 'const Bar' is not a pointer",
4056
"but function is not marked const",
57+
"Possibly trying to mutate object in a const context. Try running the expression with",
58+
"expression --c++-ignore-context-qualifiers -- m_bar.method() + blah",
59+
],
60+
)
61+
62+
self.expect(
63+
"expression -Q -- m_bar->method() + blah",
64+
error=True,
65+
substrs=[
66+
"Possibly trying to mutate object in a const context. Try running the expression with",
4167
],
68+
matching=False,
4269
)

lldb/test/API/lang/cpp/expression-context-qualifiers/non_const_method/TestExprInNonConstMethod.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,8 @@ def test(self):
3232
"expression x = 7.0",
3333
error=True,
3434
substrs=[
35-
"cannot assign to non-static data member within const member function"
35+
"cannot assign to non-static data member within const member function",
36+
"note: Possibly trying to mutate object in a const context. Try running the expression with",
3637
],
3738
)
3839

lldb/test/API/lang/cpp/expression-context-qualifiers/template_const_method/TestExprInTemplateConstMethod.py

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,8 @@ def test(self):
2424
"expression m_mem = 2.0",
2525
error=True,
2626
substrs=[
27-
"cannot assign to non-static data member within const member function"
27+
"cannot assign to non-static data member within const member function",
28+
"note: Possibly trying to mutate object in a const context. Try running the expression with",
2829
],
2930
)
3031
self.expect_expr("((Foo*)this)->bar()", result_type="double", result_value="5")
@@ -44,7 +45,8 @@ def test(self):
4445
"expression x = 7.0",
4546
error=True,
4647
substrs=[
47-
"cannot assign to non-static data member within const member function"
48+
"cannot assign to non-static data member within const member function",
49+
"note: Possibly trying to mutate object in a const context. Try running the expression with",
4850
],
4951
)
5052
self.expect_expr("x = -7.0; x", options=options, result_value="-7")
@@ -77,7 +79,8 @@ def test(self):
7779
"expression m_mem = 2.0",
7880
error=True,
7981
substrs=[
80-
"cannot assign to non-static data member within const member function"
82+
"cannot assign to non-static data member within const member function",
83+
"note: Possibly trying to mutate object in a const context. Try running the expression with",
8184
],
8285
)
8386
self.expect_expr("m_mem", result_value="-2")
@@ -105,7 +108,8 @@ def test(self):
105108
"expression m_mem = 2.0",
106109
error=True,
107110
substrs=[
108-
"cannot assign to non-static data member within const member function"
111+
"cannot assign to non-static data member within const member function",
112+
"note: Possibly trying to mutate object in a const context. Try running the expression with",
109113
],
110114
)
111115
self.expect_expr("m_mem = -11.0; m_mem", options=options, result_value="-11")

lldb/test/API/lang/cpp/expression-context-qualifiers/template_non_const_method/TestExprInTemplateNonConstMethod.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,9 @@ def test(self):
3232
"expression x = 7.0",
3333
error=True,
3434
substrs=[
35-
"cannot assign to non-static data member within const member function"
35+
"cannot assign to non-static data member within const member function",
36+
"note: Possibly trying to mutate object in a const context. Try running the expression with",
37+
"expression --c++-ignore-context-qualifiers -- x = 7.0",
3638
],
3739
)
3840

0 commit comments

Comments
 (0)