diff --git a/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionDeclMap.h b/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionDeclMap.h --- a/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionDeclMap.h +++ b/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionDeclMap.h @@ -608,8 +608,13 @@ /// /// \param[in] type /// The type of the class that serves as the evaluation context. - void AddContextClassType(NameSearchContext &context, - const TypeFromUser &type); + /// + /// \param[in] context_method + /// The member function declaration in which the expression is being + /// evaluated or null if the expression is not evaluated in the context + /// of a member function. + void AddContextClassType(NameSearchContext &context, const TypeFromUser &type, + clang::CXXMethodDecl *context_method = nullptr); /// Move a type out of the current ASTContext into another, but make sure to /// export all components of the type also. diff --git a/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionDeclMap.cpp b/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionDeclMap.cpp --- a/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionDeclMap.cpp +++ b/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionDeclMap.cpp @@ -810,7 +810,7 @@ LLDB_LOG(log, " CEDM::FEVD Adding type for $__lldb_class: {1}", class_qual_type.getAsString()); - AddContextClassType(context, class_user_type); + AddContextClassType(context, class_user_type, method_decl); if (method_decl->isInstance()) { // self is a pointer to the object @@ -1889,8 +1889,9 @@ } } -void ClangExpressionDeclMap::AddContextClassType(NameSearchContext &context, - const TypeFromUser &ut) { +void ClangExpressionDeclMap::AddContextClassType( + NameSearchContext &context, const TypeFromUser &ut, + CXXMethodDecl *context_method) { CompilerType copied_clang_type = GuardedCopyType(ut); Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS)); @@ -1912,7 +1913,12 @@ void_clang_type, &void_ptr_clang_type, 1, false, 0); const bool is_virtual = false; - const bool is_static = false; + // If we evaluate an expression inside a static method, we also need to + // make our lldb_expr method static so that Clang denies access to + // non-static members. + // If we don't have a context_method we are evaluating within a context + // object and we can allow access to non-static members. + const bool is_static = context_method ? context_method->isStatic() : false; const bool is_inline = false; const bool is_explicit = false; const bool is_attr_used = true; diff --git a/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionSourceCode.h b/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionSourceCode.h --- a/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionSourceCode.h +++ b/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionSourceCode.h @@ -33,12 +33,13 @@ enum class WrapKind { /// Wrapped in a non-static member function of a C++ class. CppMemberFunction, + /// Wrapped in a static member function of a C++ class. + CppStaticMemberFunction, /// Wrapped in an instance Objective-C method. ObjCInstanceMethod, /// Wrapped in a static Objective-C method. ObjCStaticMethod, /// Wrapped in a non-member function. - /// Note that this is also used for static member functions of a C++ class. Function }; diff --git a/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionSourceCode.cpp b/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionSourceCode.cpp --- a/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionSourceCode.cpp +++ b/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionSourceCode.cpp @@ -419,6 +419,7 @@ module_imports.c_str(), m_name.c_str(), lldb_local_var_decls.GetData(), tagged_body.c_str()); break; + case WrapKind::CppStaticMemberFunction: case WrapKind::CppMemberFunction: wrap_stream.Printf("%s" "void \n" diff --git a/lldb/source/Plugins/ExpressionParser/Clang/ClangUserExpression.h b/lldb/source/Plugins/ExpressionParser/Clang/ClangUserExpression.h --- a/lldb/source/Plugins/ExpressionParser/Clang/ClangUserExpression.h +++ b/lldb/source/Plugins/ExpressionParser/Clang/ClangUserExpression.h @@ -254,7 +254,7 @@ bool m_in_objectivec_method = false; /// True if the expression is compiled as a static (or class) method /// (currently true if it was parsed when exe_ctx was in an Objective-C class - /// method). + /// method or static C++ member function). bool m_in_static_method = false; /// True if "this" or "self" must be looked up and passed in. False if the /// expression doesn't really use them and they can be NULL. diff --git a/lldb/source/Plugins/ExpressionParser/Clang/ClangUserExpression.cpp b/lldb/source/Plugins/ExpressionParser/Clang/ClangUserExpression.cpp --- a/lldb/source/Plugins/ExpressionParser/Clang/ClangUserExpression.cpp +++ b/lldb/source/Plugins/ExpressionParser/Clang/ClangUserExpression.cpp @@ -155,32 +155,35 @@ m_needs_object_ptr = true; } else if (clang::CXXMethodDecl *method_decl = TypeSystemClang::DeclContextGetAsCXXMethodDecl(decl_context)) { - if (m_allow_cxx && method_decl->isInstance()) { - if (m_enforce_valid_object) { - lldb::VariableListSP variable_list_sp( - function_block->GetBlockVariableList(true)); + if (m_allow_cxx) { + if (method_decl->isInstance()) { + if (m_enforce_valid_object) { + lldb::VariableListSP variable_list_sp( + function_block->GetBlockVariableList(true)); - const char *thisErrorString = "Stopped in a C++ method, but 'this' " - "isn't available; pretending we are in a " - "generic context"; + const char *thisErrorString = + "Stopped in a C++ method, but 'this' " + "isn't available; pretending we are in a " + "generic context"; - if (!variable_list_sp) { - err.SetErrorString(thisErrorString); - return; - } + if (!variable_list_sp) { + err.SetErrorString(thisErrorString); + return; + } - lldb::VariableSP this_var_sp( - variable_list_sp->FindVariable(ConstString("this"))); + lldb::VariableSP this_var_sp( + variable_list_sp->FindVariable(ConstString("this"))); - if (!this_var_sp || !this_var_sp->IsInScope(frame) || - !this_var_sp->LocationIsValidForFrame(frame)) { - err.SetErrorString(thisErrorString); - return; + if (!this_var_sp || !this_var_sp->IsInScope(frame) || + !this_var_sp->LocationIsValidForFrame(frame)) { + err.SetErrorString(thisErrorString); + return; + } } + m_needs_object_ptr = true; } - m_in_cplusplus_method = true; - m_needs_object_ptr = true; + m_in_static_method = !method_decl->isInstance(); } } else if (clang::ObjCMethodDecl *method_decl = TypeSystemClang::DeclContextGetAsObjCMethodDecl( @@ -401,9 +404,11 @@ assert(m_options.GetExecutionPolicy() != eExecutionPolicyTopLevel && "Top level expressions aren't wrapped."); using Kind = ClangExpressionSourceCode::WrapKind; - if (m_in_cplusplus_method) + if (m_in_cplusplus_method) { + if (m_in_static_method) + return Kind::CppStaticMemberFunction; return Kind::CppMemberFunction; - else if (m_in_objectivec_method) { + } else if (m_in_objectivec_method) { if (m_in_static_method) return Kind::ObjCStaticMethod; return Kind::ObjCInstanceMethod; diff --git a/lldb/test/API/lang/cpp/stopped_in_static_member_function/Makefile b/lldb/test/API/lang/cpp/stopped_in_static_member_function/Makefile new file mode 100644 --- /dev/null +++ b/lldb/test/API/lang/cpp/stopped_in_static_member_function/Makefile @@ -0,0 +1,3 @@ +CXX_SOURCES := main.cpp + +include Makefile.rules diff --git a/lldb/test/API/lang/cpp/stopped_in_static_member_function/TestStoppedInStaticMemberFunction.py b/lldb/test/API/lang/cpp/stopped_in_static_member_function/TestStoppedInStaticMemberFunction.py new file mode 100644 --- /dev/null +++ b/lldb/test/API/lang/cpp/stopped_in_static_member_function/TestStoppedInStaticMemberFunction.py @@ -0,0 +1,38 @@ +import lldb +from lldbsuite.test.decorators import * +from lldbsuite.test.lldbtest import * +from lldbsuite.test import lldbutil + + +class TestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + @no_debug_info_test + def test(self): + self.build() + lldbutil.run_to_source_breakpoint(self, "// break in static member function", lldb.SBFileSpec("main.cpp")) + + # Evaluate a static member and call a static member function. + self.expect_expr("static_member_var", result_type="int", result_value="2") + self.expect_expr("static_const_member_var", result_type="const int", result_value="3") + self.expect_expr("static_constexpr_member_var", result_type="const int", result_value="4") + self.expect_expr("static_func()", result_type="int", result_value="6") + + # Check that accessing non-static members just reports a diagnostic. + self.expect("expr member_var", error=True, + substrs=["invalid use of member 'member_var' in static member function"]) + self.expect("expr member_func()", error=True, + substrs=["call to non-static member function without an object argument"]) + self.expect("expr this", error=True, + substrs=["invalid use of 'this' outside of a non-static member function"]) + + # Continue to a non-static member function of the same class and make + # sure that evaluating non-static members now works. + breakpoint = self.target().BreakpointCreateBySourceRegex( + "// break in member function", lldb.SBFileSpec("main.cpp")) + self.assertNotEqual(breakpoint.GetNumResolvedLocations(), 0) + stopped_threads = lldbutil.continue_to_breakpoint(self.process(), breakpoint) + + self.expect_expr("member_var", result_type="int", result_value="1") + self.expect_expr("member_func()", result_type="int", result_value="5") diff --git a/lldb/test/API/lang/cpp/stopped_in_static_member_function/main.cpp b/lldb/test/API/lang/cpp/stopped_in_static_member_function/main.cpp new file mode 100644 --- /dev/null +++ b/lldb/test/API/lang/cpp/stopped_in_static_member_function/main.cpp @@ -0,0 +1,31 @@ +struct A { + int member_var = 1; + static int static_member_var; + static const int static_const_member_var; + static constexpr int static_constexpr_member_var = 4; + int member_func() { return 5; } + static int static_func() { return 6; } + + static int context_static_func() { + int i = static_member_var; + i += static_func(); + return i; // break in static member function + } + + int context_member_func() { + int i = member_var; + i += member_func(); + return i; // break in member function + } +}; + +int A::static_member_var = 2; +const int A::static_const_member_var = 3; +constexpr int A::static_constexpr_member_var; + +int main() { + int i = A::context_static_func(); + A a; + a.context_member_func(); + return i; +}