Index: lldb/include/lldb/Expression/ExpressionSourceCode.h =================================================================== --- lldb/include/lldb/Expression/ExpressionSourceCode.h +++ lldb/include/lldb/Expression/ExpressionSourceCode.h @@ -12,6 +12,7 @@ #include "lldb/lldb-enumerations.h" #include +#include namespace lldb_private { @@ -35,9 +36,23 @@ const char *GetName() const { return m_name.c_str(); } + /// Generates the source code that will evaluate the expression. + /// + /// \param text output parameter containing the source code string. + /// \param wrapping_language If the expression is supossed to be wrapped, + /// then this is the language that should be used for that. + /// \param static_method True iff the expression is valuated inside a static + /// Objective-C method. + /// \param exe_ctx The execution context in which the expression will be + /// evaluated. + /// \param add_locals True iff local variables should be injected into the + /// expression source code. + /// \param modules A list of (C++) modules that the expression should import. + /// + /// \return true iff the source code was successfully generated. bool GetText(std::string &text, lldb::LanguageType wrapping_language, - bool static_method, ExecutionContext &exe_ctx, - bool add_locals) const; + bool static_method, ExecutionContext &exe_ctx, bool add_locals, + std::vector modules) 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 Index: lldb/include/lldb/Target/Platform.h =================================================================== --- lldb/include/lldb/Target/Platform.h +++ lldb/include/lldb/Target/Platform.h @@ -276,6 +276,21 @@ virtual bool SetRemoteWorkingDirectory(const FileSpec &working_dir); + //------------------------------------------------------------------ + /// Retrieve the system include directories on this platform for the + /// given language. + /// + /// \param[in] lang + /// The language for which the include directories should be queried. + /// + /// \param[out] directories + /// The include directories for this system. + //------------------------------------------------------------------ + virtual std::vector + GetSystemIncludeDirectories(lldb::LanguageType lang) { + return {}; + } + virtual const char *GetUserName(uint32_t uid); virtual const char *GetGroupName(uint32_t gid); Index: lldb/include/lldb/Target/Target.h =================================================================== --- lldb/include/lldb/Target/Target.h +++ lldb/include/lldb/Target/Target.h @@ -129,6 +129,8 @@ bool GetEnableAutoImportClangModules() const; + bool GetEnableImportStdModule() const; + bool GetEnableAutoApplyFixIts() const; bool GetEnableNotifyAboutFixIts() const; Index: lldb/packages/Python/lldbsuite/test/expression_command/import-std-module/basic/Makefile =================================================================== --- /dev/null +++ lldb/packages/Python/lldbsuite/test/expression_command/import-std-module/basic/Makefile @@ -0,0 +1,5 @@ +LEVEL = ../../../make +USE_LIBCPP := 1 +CXXFLAGS += $(MANDATORY_CXXMODULE_BUILD_CFLAGS) +CXX_SOURCES := main.cpp +include $(LEVEL)/Makefile.rules Index: lldb/packages/Python/lldbsuite/test/expression_command/import-std-module/basic/TestImportStdModule.py =================================================================== --- /dev/null +++ lldb/packages/Python/lldbsuite/test/expression_command/import-std-module/basic/TestImportStdModule.py @@ -0,0 +1,72 @@ +""" +Test importing the 'std' C++ module and evaluate expressions with it. +""" + +from lldbsuite.test.decorators import * +from lldbsuite.test.lldbtest import * +from lldbsuite.test import lldbutil + + +class ImportStdModule(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def setUp(self): + TestBase.setUp(self) + self.source = 'main.cpp' + self.line = line_number( + self.source, '// Set break point at this line.') + + # FIXME: This should work on more setups, so remove these + # skipIf's in the future. + @add_test_categories(["libc++"]) + @skipIf(compiler=no_match("clang")) + @skipIf(oslist=no_match(["linux"])) + @skipIf(debug_info=no_match(["dwarf"])) + def test(self): + self.build(dictionary=self.getBuildFlags()) + exe = self.getBuildArtifact("a.out") + + self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET) + + lldbutil.run_break_set_by_file_and_line( + self, "main.cpp", self.line, num_expected_locations=1, loc_exact=True) + + self.runCmd("run", RUN_SUCCEEDED) + + # Activate importing of std module. + self.runCmd("settings set target.import-std-module true") + # Calling some normal std functions that return non-template types. + self.expect("expr std::abs(-42)", substrs=['(int) $0 = 42']) + self.expect("expr std::div(2, 1).quot", substrs=['(int) $1 = 2']) + # Using types from std. + self.expect("expr (std::size_t)33U", substrs=['(size_t) $2 = 33']) + # Calling templated functions that return non-template types. + self.expect("expr char a = 'b'; char b = 'a'; std::swap(a, b); a", + substrs=["(char) $3 = 'a'"]) + + # FIXME: This should work on more setups, so remove these + # skipIf's in the future. + @add_test_categories(["libc++"]) + @skipIf(compiler=no_match("clang")) + @skipIf(oslist=no_match(["linux"])) + @skipIf(debug_info=no_match(["dwarf"])) + def test_non_cpp_language(self): + self.build(dictionary=self.getBuildFlags()) + exe = self.getBuildArtifact("a.out") + + self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET) + + lldbutil.run_break_set_by_file_and_line( + self, "main.cpp", self.line, num_expected_locations=1, loc_exact=True) + + self.runCmd("run", RUN_SUCCEEDED) + + # Activate importing of std module. + self.runCmd("settings set target.import-std-module true") + # These languages don't support C++ modules, so they shouldn't + # be able to evaluate the expression. + self.expect("expr -l C -- std::abs(-42)", error=True) + self.expect("expr -l C99 -- std::abs(-42)", error=True) + self.expect("expr -l C11 -- std::abs(-42)", error=True) + self.expect("expr -l ObjC -- std::abs(-42)", error=True) Index: lldb/packages/Python/lldbsuite/test/expression_command/import-std-module/basic/main.cpp =================================================================== --- /dev/null +++ lldb/packages/Python/lldbsuite/test/expression_command/import-std-module/basic/main.cpp @@ -0,0 +1,7 @@ +// We need to import any std module. It doesn't matter which one. +#include + +int main(int argc, char **argv) { + std::cout << "Test" << std::endl; + return 0; // Set break point at this line. +} Index: lldb/packages/Python/lldbsuite/test/expression_command/import-std-module/conflicts/Makefile =================================================================== --- /dev/null +++ lldb/packages/Python/lldbsuite/test/expression_command/import-std-module/conflicts/Makefile @@ -0,0 +1,5 @@ +LEVEL = ../../../make +USE_LIBCPP := 1 +CXXFLAGS += $(MANDATORY_CXXMODULE_BUILD_CFLAGS) +CXX_SOURCES := main.cpp +include $(LEVEL)/Makefile.rules Index: lldb/packages/Python/lldbsuite/test/expression_command/import-std-module/conflicts/TestStdModuleWithConflicts.py =================================================================== --- /dev/null +++ lldb/packages/Python/lldbsuite/test/expression_command/import-std-module/conflicts/TestStdModuleWithConflicts.py @@ -0,0 +1,47 @@ +""" +Test importing the 'std' C++ module and check if we can handle +prioritizing the conflicting functions from debug info and std +module. + +See also import-std-module/basic/TestImportStdModule.py for +the same test on a 'clean' code base without conflicts. +""" + +from lldbsuite.test.decorators import * +from lldbsuite.test.lldbtest import * +from lldbsuite.test import lldbutil + + +class TestImportStdModuleConflicts(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def setUp(self): + TestBase.setUp(self) + self.source = 'main.cpp' + self.line = line_number( + self.source, '// Set break point at this line.') + + # FIXME: This should work on more setups, so remove these + # skipIf's in the future. + @add_test_categories(["libc++"]) + @skipIf(compiler=no_match("clang")) + @skipIf(oslist=no_match(["linux"])) + @skipIf(debug_info=no_match(["dwarf"])) + def test(self): + self.build(dictionary=self.getBuildFlags()) + exe = self.getBuildArtifact("a.out") + + self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET) + + lldbutil.run_break_set_by_file_and_line( + self, "main.cpp", self.line, num_expected_locations=1, loc_exact=True) + + self.runCmd("run", RUN_SUCCEEDED) + + self.runCmd("settings set target.import-std-module true") + self.expect("expr std::abs(-42)", substrs=['(int) $0 = 42']) + self.expect("expr std::div(2, 1).quot", substrs=['(int) $1 = 2']) + self.expect("expr (std::size_t)33U", substrs=['(size_t) $2 = 33']) + self.expect("expr char a = 'b'; char b = 'a'; std::swap(a, b); a", + substrs=["(char) $3 = 'a'"]) Index: lldb/packages/Python/lldbsuite/test/expression_command/import-std-module/conflicts/main.cpp =================================================================== --- /dev/null +++ lldb/packages/Python/lldbsuite/test/expression_command/import-std-module/conflicts/main.cpp @@ -0,0 +1,10 @@ +#include +#include + +int main(int argc, char **argv) { + std::size_t f = argc; + f = std::abs(argc); + f = std::div(argc * 2, argc).quot; + std::swap(f, f); + return f; // Set break point at this line. +} Index: lldb/packages/Python/lldbsuite/test/expression_command/import-std-module/no-std-module/Makefile =================================================================== --- /dev/null +++ lldb/packages/Python/lldbsuite/test/expression_command/import-std-module/no-std-module/Makefile @@ -0,0 +1,5 @@ +LEVEL = ../../../make +USE_LIBCPP := 1 +CXXFLAGS += $(MANDATORY_CXXMODULE_BUILD_CFLAGS) +CXX_SOURCES := main.cpp +include $(LEVEL)/Makefile.rules Index: lldb/packages/Python/lldbsuite/test/expression_command/import-std-module/no-std-module/TestMissingStdModule.py =================================================================== --- /dev/null +++ lldb/packages/Python/lldbsuite/test/expression_command/import-std-module/no-std-module/TestMissingStdModule.py @@ -0,0 +1,51 @@ +""" +Test that importing the std module on a compile unit +that doesn't use the std module will not break LLDB. + +It's not really specified at the moment what kind of +error we should report back to the user in this +situation. Currently Clang will just complain that +the std module doesn't exist or can't be loaded. +""" + +from lldbsuite.test.decorators import * +from lldbsuite.test.lldbtest import * +from lldbsuite.test import lldbutil + + +class STLTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def setUp(self): + TestBase.setUp(self) + self.source = 'main.cpp' + self.line = line_number( + self.source, '// Set break point at this line.') + + # FIXME: This should work on more setups, so remove these + # skipIf's in the future. + @add_test_categories(["libc++"]) + @skipIf(compiler=no_match("clang")) + @skipIf(oslist=no_match(["linux"])) + @skipIf(debug_info=no_match(["dwarf"])) + def test(self): + self.build(dictionary=self.getBuildFlags()) + exe = self.getBuildArtifact("a.out") + + self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET) + + lldbutil.run_break_set_by_file_and_line( + self, "main.cpp", self.line, num_expected_locations=1, loc_exact=True) + + self.runCmd("run", RUN_SUCCEEDED) + + # Activate importing of std module. + self.runCmd("settings set target.import-std-module true") + + # Run some commands that should all fail without our std module. + self.expect("expr std::abs(-42)", error=True) + self.expect("expr std::div(2, 1).quot", error=True) + self.expect("expr (std::size_t)33U", error=True) + self.expect("expr char a = 'b'; char b = 'a'; std::swap(a, b); a", + error=True) Index: lldb/packages/Python/lldbsuite/test/expression_command/import-std-module/no-std-module/main.cpp =================================================================== --- /dev/null +++ lldb/packages/Python/lldbsuite/test/expression_command/import-std-module/no-std-module/main.cpp @@ -0,0 +1,5 @@ +// We don't import any std module here. + +int main(int argc, char **argv) { + return 0; // Set break point at this line. +} Index: lldb/packages/Python/lldbsuite/test/expression_command/import-std-module/sysroot/Makefile =================================================================== --- /dev/null +++ lldb/packages/Python/lldbsuite/test/expression_command/import-std-module/sysroot/Makefile @@ -0,0 +1,6 @@ +LEVEL = ../../../make +NO_INC_DIRS := 1 +CXXFLAGS += $(MANDATORY_CXXMODULE_BUILD_CFLAGS) +CXXFLAGS += -I $(SRCDIR)/root/usr/include/c++/include/ -I $(SRCDIR)/root/usr/include/ -nostdinc -nostdinc++ -nostdlib++ +CXX_SOURCES := main.cpp +include $(LEVEL)/Makefile.rules Index: lldb/packages/Python/lldbsuite/test/expression_command/import-std-module/sysroot/TestStdModuleSysroot.py =================================================================== --- /dev/null +++ lldb/packages/Python/lldbsuite/test/expression_command/import-std-module/sysroot/TestStdModuleSysroot.py @@ -0,0 +1,45 @@ +""" +Test that we respect the sysroot when building the std module. +""" + +from lldbsuite.test.decorators import * +from lldbsuite.test.lldbtest import * +from lldbsuite.test import lldbutil +import os + +class ImportStdModule(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def setUp(self): + TestBase.setUp(self) + self.source = 'main.cpp' + self.line = line_number( + self.source, '// Set break point at this line.') + + # FIXME: This should work on more setups, so remove these + # skipIf's in the future. + @skipIf(compiler=no_match("clang")) + @skipIf(oslist=no_match(["linux"])) + @skipIf(debug_info=no_match(["dwarf"])) + def test(self): + self.build(dictionary=self.getBuildFlags()) + exe = self.getBuildArtifact("a.out") + + sysroot = os.path.join(os.getcwd(), "root") + + # Set the sysroot. + self.runCmd("platform select --sysroot '" + sysroot + "' host", CURRENT_EXECUTABLE_SET) + + self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET) + + lldbutil.run_break_set_by_file_and_line( + self, "main.cpp", self.line, num_expected_locations=1, loc_exact=True) + + self.runCmd("run", RUN_SUCCEEDED) + + self.runCmd("settings set target.import-std-module true") + + # Call our custom function in our sysroot std module. + # If this gives us the correct result, then we used the sysroot. + self.expect("expr std::myabs(-42)", substrs=['(int) $0 = 42']) Index: lldb/packages/Python/lldbsuite/test/expression_command/import-std-module/sysroot/main.cpp =================================================================== --- /dev/null +++ lldb/packages/Python/lldbsuite/test/expression_command/import-std-module/sysroot/main.cpp @@ -0,0 +1,6 @@ +#include + +int main(int argc, char **argv) { + libc_struct s; + return 0; // Set break point at this line. +} Index: lldb/packages/Python/lldbsuite/test/expression_command/import-std-module/sysroot/root/usr/include/c++/include/algorithm =================================================================== --- /dev/null +++ lldb/packages/Python/lldbsuite/test/expression_command/import-std-module/sysroot/root/usr/include/c++/include/algorithm @@ -0,0 +1,7 @@ +#include "libc_header.h" + +namespace std { + int myabs(int i) { + return i < 0 ? -i : i; + } +} Index: lldb/packages/Python/lldbsuite/test/expression_command/import-std-module/sysroot/root/usr/include/c++/include/module.modulemap =================================================================== --- /dev/null +++ lldb/packages/Python/lldbsuite/test/expression_command/import-std-module/sysroot/root/usr/include/c++/include/module.modulemap @@ -0,0 +1,3 @@ +module std { + module "algorithm" { header "algorithm" export * } +} Index: lldb/packages/Python/lldbsuite/test/expression_command/import-std-module/sysroot/root/usr/include/libc_header.h =================================================================== --- /dev/null +++ lldb/packages/Python/lldbsuite/test/expression_command/import-std-module/sysroot/root/usr/include/libc_header.h @@ -0,0 +1 @@ +struct libc_struct {}; Index: lldb/packages/Python/lldbsuite/test/make/Makefile.rules =================================================================== --- lldb/packages/Python/lldbsuite/test/make/Makefile.rules +++ lldb/packages/Python/lldbsuite/test/make/Makefile.rules @@ -254,7 +254,10 @@ CFLAGS += $(ARCHFLAG)$(ARCH) $(FRAMEWORK_INCLUDES) -I$(LLDB_BASE_DIR)include endif -CFLAGS += -I$(SRCDIR) -include $(THIS_FILE_DIR)test_common.h -I$(THIS_FILE_DIR) +ifndef NO_INC_DIRS + CFLAGS += -I$(SRCDIR) -include $(THIS_FILE_DIR)test_common.h -I$(THIS_FILE_DIR) +endif + CFLAGS += $(NO_LIMIT_DEBUG_INFO_FLAGS) $(ARCH_CFLAGS) $(CFLAGS_EXTRAS) # If the OS is Windows, we need to pass -gdwarf to clang, otherwise it will build @@ -282,7 +285,11 @@ $(warning failed to set the shared clang module cache dir) endif -MANDATORY_MODULE_BUILD_CFLAGS := -fmodules -gmodules -fmodules-cache-path=$(CLANG_MODULE_CACHE_DIR) +MODULE_BASE_FLAGS := -fmodules -gmodules -fmodules-cache-path=$(CLANG_MODULE_CACHE_DIR) +MANDATORY_MODULE_BUILD_CFLAGS := $(MODULE_BASE_FLAGS) -gmodules +# Build flags for building with C++ modules. +# -glldb is necessary for emitting information about what modules were imported. +MANDATORY_CXXMODULE_BUILD_CFLAGS := $(MODULE_BASE_FLAGS) -fcxx-modules -glldb ifeq "$(OS)" "Darwin" MANDATORY_MODULE_BUILD_CFLAGS += -fcxx-modules Index: lldb/source/Expression/ExpressionSourceCode.cpp =================================================================== --- lldb/source/Expression/ExpressionSourceCode.cpp +++ lldb/source/Expression/ExpressionSourceCode.cpp @@ -178,8 +178,8 @@ bool ExpressionSourceCode::GetText(std::string &text, lldb::LanguageType wrapping_language, bool static_method, - ExecutionContext &exe_ctx, - bool add_locals) const { + ExecutionContext &exe_ctx, bool add_locals, + std::vector modules) const { const char *target_specific_defines = "typedef signed char BOOL;\n"; std::string module_macros; @@ -273,6 +273,15 @@ break; } + // Generate a list of @import statements that will import the specified + // module into our expression. + std::string module_imports; + for (std::string module : modules) { + module_imports.append("@import "); + module_imports.append(module); + module_imports.append(";\n"); + } + StreamString wrap_stream; wrap_stream.Printf("%s\n%s\n%s\n%s\n%s\n", module_macros.c_str(), @@ -298,28 +307,31 @@ default: break; case lldb::eLanguageTypeC: - wrap_stream.Printf("void \n" + wrap_stream.Printf("%s" + "void \n" "%s(void *$__lldb_arg) \n" "{ \n" " %s; \n" "%s" "} \n", - m_name.c_str(), lldb_local_var_decls.GetData(), - tagged_body.c_str()); + module_imports.c_str(), m_name.c_str(), + lldb_local_var_decls.GetData(), tagged_body.c_str()); break; case lldb::eLanguageTypeC_plus_plus: - wrap_stream.Printf("void \n" + wrap_stream.Printf("%s" + "void \n" "$__lldb_class::%s(void *$__lldb_arg) \n" "{ \n" " %s; \n" "%s" "} \n", - m_name.c_str(), lldb_local_var_decls.GetData(), - tagged_body.c_str()); + module_imports.c_str(), m_name.c_str(), + lldb_local_var_decls.GetData(), tagged_body.c_str()); break; case lldb::eLanguageTypeObjC: if (static_method) { wrap_stream.Printf( + "%s" "@interface $__lldb_objc_class ($__lldb_category) \n" "+(void)%s:(void *)$__lldb_arg; \n" "@end \n" @@ -329,9 +341,11 @@ "%s" "} \n" "@end \n", - m_name.c_str(), m_name.c_str(), tagged_body.c_str()); + module_imports.c_str(), m_name.c_str(), m_name.c_str(), + tagged_body.c_str()); } else { wrap_stream.Printf( + "%s" "@interface $__lldb_objc_class ($__lldb_category) \n" "-(void)%s:(void *)$__lldb_arg; \n" "@end \n" @@ -341,7 +355,8 @@ "%s" "} \n" "@end \n", - m_name.c_str(), m_name.c_str(), tagged_body.c_str()); + module_imports.c_str(), m_name.c_str(), m_name.c_str(), + tagged_body.c_str()); } break; } Index: lldb/source/Plugins/ExpressionParser/Clang/ASTUtils.h =================================================================== --- /dev/null +++ lldb/source/Plugins/ExpressionParser/Clang/ASTUtils.h @@ -0,0 +1,579 @@ +//===-- ASTUtils.h ----------------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_ASTUtils_h_ +#define liblldb_ASTUtils_h_ + +#include "clang/Sema/Lookup.h" +#include "clang/Sema/MultiplexExternalSemaSource.h" +#include "clang/Sema/Sema.h" +#include "clang/Sema/SemaConsumer.h" + +namespace lldb_private { + +/// Wraps an ExternalASTSource into an ExternalSemaSource. Doesn't take +/// ownership of the provided source. +class ExternalASTSourceWrapper : public clang::ExternalSemaSource { + ExternalASTSource *m_Source; + +public: + ExternalASTSourceWrapper(ExternalASTSource *Source) : m_Source(Source) { + assert(m_Source && "Can't wrap nullptr ExternalASTSource"); + } + + ~ExternalASTSourceWrapper() override; + + clang::Decl *GetExternalDecl(uint32_t ID) override { + return m_Source->GetExternalDecl(ID); + } + + clang::Selector GetExternalSelector(uint32_t ID) override { + return m_Source->GetExternalSelector(ID); + } + + uint32_t GetNumExternalSelectors() override { + return m_Source->GetNumExternalSelectors(); + } + + clang::Stmt *GetExternalDeclStmt(uint64_t Offset) override { + return m_Source->GetExternalDeclStmt(Offset); + } + + clang::CXXCtorInitializer ** + GetExternalCXXCtorInitializers(uint64_t Offset) override { + return m_Source->GetExternalCXXCtorInitializers(Offset); + } + + clang::CXXBaseSpecifier * + GetExternalCXXBaseSpecifiers(uint64_t Offset) override { + return m_Source->GetExternalCXXBaseSpecifiers(Offset); + } + + void updateOutOfDateIdentifier(clang::IdentifierInfo &II) override { + m_Source->updateOutOfDateIdentifier(II); + } + + bool FindExternalVisibleDeclsByName(const clang::DeclContext *DC, + clang::DeclarationName Name) override { + return m_Source->FindExternalVisibleDeclsByName(DC, Name); + } + + void completeVisibleDeclsMap(const clang::DeclContext *DC) override { + m_Source->completeVisibleDeclsMap(DC); + } + + clang::Module *getModule(unsigned ID) override { + return m_Source->getModule(ID); + } + + llvm::Optional + getSourceDescriptor(unsigned ID) override { + return m_Source->getSourceDescriptor(ID); + } + + ExtKind hasExternalDefinitions(const clang::Decl *D) override { + return m_Source->hasExternalDefinitions(D); + } + + void FindExternalLexicalDecls( + const clang::DeclContext *DC, + llvm::function_ref IsKindWeWant, + llvm::SmallVectorImpl &Result) override { + m_Source->FindExternalLexicalDecls(DC, IsKindWeWant, Result); + } + + void + FindFileRegionDecls(clang::FileID File, unsigned Offset, unsigned Length, + llvm::SmallVectorImpl &Decls) override { + m_Source->FindFileRegionDecls(File, Offset, Length, Decls); + } + + void CompleteRedeclChain(const clang::Decl *D) override { + m_Source->CompleteRedeclChain(D); + } + + void CompleteType(clang::TagDecl *Tag) override { + m_Source->CompleteType(Tag); + } + + void CompleteType(clang::ObjCInterfaceDecl *Class) override { + m_Source->CompleteType(Class); + } + + void ReadComments() override { m_Source->ReadComments(); } + + void StartedDeserializing() override { m_Source->StartedDeserializing(); } + + void FinishedDeserializing() override { m_Source->FinishedDeserializing(); } + + void StartTranslationUnit(clang::ASTConsumer *Consumer) override { + m_Source->StartTranslationUnit(Consumer); + } + + void PrintStats() override; + + bool layoutRecordType( + const clang::RecordDecl *Record, uint64_t &Size, uint64_t &Alignment, + llvm::DenseMap &FieldOffsets, + llvm::DenseMap + &BaseOffsets, + llvm::DenseMap + &VirtualBaseOffsets) override { + return m_Source->layoutRecordType(Record, Size, Alignment, FieldOffsets, + BaseOffsets, VirtualBaseOffsets); + } +}; + +/// Wraps an ASTConsumer into an SemaConsumer. Doesn't take ownership of the +/// provided consumer. If the provided ASTConsumer is also a SemaConsumer, +/// the wrapper will also forward SemaConsumer functions. +class ASTConsumerForwarder : public clang::SemaConsumer { + clang::ASTConsumer *m_c; + clang::SemaConsumer *m_sc; + +public: + ASTConsumerForwarder(clang::ASTConsumer *c) : m_c(c) { + m_sc = llvm::dyn_cast(m_c); + } + + ~ASTConsumerForwarder() override; + + void Initialize(clang::ASTContext &Context) override { + m_c->Initialize(Context); + } + + bool HandleTopLevelDecl(clang::DeclGroupRef D) override { + return m_c->HandleTopLevelDecl(D); + } + + void HandleInlineFunctionDefinition(clang::FunctionDecl *D) override { + m_c->HandleInlineFunctionDefinition(D); + } + + void HandleInterestingDecl(clang::DeclGroupRef D) override { + m_c->HandleInterestingDecl(D); + } + + void HandleTranslationUnit(clang::ASTContext &Ctx) override { + m_c->HandleTranslationUnit(Ctx); + } + + void HandleTagDeclDefinition(clang::TagDecl *D) override { + m_c->HandleTagDeclDefinition(D); + } + + void HandleTagDeclRequiredDefinition(const clang::TagDecl *D) override { + m_c->HandleTagDeclRequiredDefinition(D); + } + + void HandleCXXImplicitFunctionInstantiation(clang::FunctionDecl *D) override { + m_c->HandleCXXImplicitFunctionInstantiation(D); + } + + void HandleTopLevelDeclInObjCContainer(clang::DeclGroupRef D) override { + m_c->HandleTopLevelDeclInObjCContainer(D); + } + + void HandleImplicitImportDecl(clang::ImportDecl *D) override { + m_c->HandleImplicitImportDecl(D); + } + + void CompleteTentativeDefinition(clang::VarDecl *D) override { + m_c->CompleteTentativeDefinition(D); + } + + void AssignInheritanceModel(clang::CXXRecordDecl *RD) override { + m_c->AssignInheritanceModel(RD); + } + + void HandleCXXStaticMemberVarInstantiation(clang::VarDecl *D) override { + m_c->HandleCXXStaticMemberVarInstantiation(D); + } + + void HandleVTable(clang::CXXRecordDecl *RD) override { + m_c->HandleVTable(RD); + } + + clang::ASTMutationListener *GetASTMutationListener() override { + return m_c->GetASTMutationListener(); + } + + clang::ASTDeserializationListener *GetASTDeserializationListener() override { + return m_c->GetASTDeserializationListener(); + } + + void PrintStats() override; + + void InitializeSema(clang::Sema &S) override { + if (m_sc) + m_sc->InitializeSema(S); + } + + /// Inform the semantic consumer that Sema is no longer available. + void ForgetSema() override { + if (m_sc) + m_sc->ForgetSema(); + } + + bool shouldSkipFunctionBody(clang::Decl *D) override { + return m_c->shouldSkipFunctionBody(D); + } +}; + +/// A ExternalSemaSource multiplexer that prioritizes its sources. +/// +/// This ExternalSemaSource will forward all requests to its attached sources. +/// However, unlike a normal multiplexer it will not forward a request to all +/// sources, but instead give priority to certain sources. If a source with a +/// higher priority can fulfill a request, all sources with a lower priority +/// will not receive the request. +/// +/// This class is mostly use to multiplex between sources of different +/// 'quality', e.g. a C++ modules and debug information. The C++ module will +/// provide more accurate replies to the requests, but might not be able to +/// answer all requests. The debug information will be used as a fallback then +/// to provide information that is not in the C++ module. +class SemaSourceWithPriorities : public clang::ExternalSemaSource { + +private: + /// The sources ordered in decreasing priority. + llvm::SmallVector Sources; + +public: + /// Construct a SemaSourceWithPriorities with a 'high quality' source that + /// has the higher priority and a 'low quality' source that will be used + /// as a fallback. + SemaSourceWithPriorities(clang::ExternalSemaSource &high_quality_source, + clang::ExternalSemaSource &low_quality_source) { + Sources.push_back(&high_quality_source); + Sources.push_back(&low_quality_source); + } + + ~SemaSourceWithPriorities() override; + + void addSource(clang::ExternalSemaSource &source) { + Sources.push_back(&source); + } + + //===--------------------------------------------------------------------===// + // ExternalASTSource. + //===--------------------------------------------------------------------===// + + clang::Decl *GetExternalDecl(uint32_t ID) override { + for (size_t i = 0; i < Sources.size(); ++i) + if (clang::Decl *Result = Sources[i]->GetExternalDecl(ID)) + return Result; + return nullptr; + } + + void CompleteRedeclChain(const clang::Decl *D) override { + for (size_t i = 0; i < Sources.size(); ++i) + Sources[i]->CompleteRedeclChain(D); + } + + clang::Selector GetExternalSelector(uint32_t ID) override { + clang::Selector Sel; + for (size_t i = 0; i < Sources.size(); ++i) { + Sel = Sources[i]->GetExternalSelector(ID); + if (!Sel.isNull()) + return Sel; + } + return Sel; + } + + uint32_t GetNumExternalSelectors() override { + for (size_t i = 0; i < Sources.size(); ++i) + if (uint32_t total = Sources[i]->GetNumExternalSelectors()) + return total; + return 0; + } + + clang::Stmt *GetExternalDeclStmt(uint64_t Offset) override { + for (size_t i = 0; i < Sources.size(); ++i) + if (clang::Stmt *Result = Sources[i]->GetExternalDeclStmt(Offset)) + return Result; + return nullptr; + } + + clang::CXXBaseSpecifier * + GetExternalCXXBaseSpecifiers(uint64_t Offset) override { + for (size_t i = 0; i < Sources.size(); ++i) + if (clang::CXXBaseSpecifier *R = + Sources[i]->GetExternalCXXBaseSpecifiers(Offset)) + return R; + return nullptr; + } + + clang::CXXCtorInitializer ** + GetExternalCXXCtorInitializers(uint64_t Offset) override { + for (auto *S : Sources) + if (auto *R = S->GetExternalCXXCtorInitializers(Offset)) + return R; + return nullptr; + } + + ExtKind hasExternalDefinitions(const clang::Decl *D) override { + for (const auto &S : Sources) + if (auto EK = S->hasExternalDefinitions(D)) + if (EK != EK_ReplyHazy) + return EK; + return EK_ReplyHazy; + } + + bool FindExternalVisibleDeclsByName(const clang::DeclContext *DC, + clang::DeclarationName Name) override { + for (size_t i = 0; i < Sources.size(); ++i) + if (Sources[i]->FindExternalVisibleDeclsByName(DC, Name)) + return true; + return false; + } + + void completeVisibleDeclsMap(const clang::DeclContext *DC) override { + // FIXME: Only one source should be able to complete the decls map. + for (size_t i = 0; i < Sources.size(); ++i) + Sources[i]->completeVisibleDeclsMap(DC); + } + + void FindExternalLexicalDecls( + const clang::DeclContext *DC, + llvm::function_ref IsKindWeWant, + llvm::SmallVectorImpl &Result) override { + for (size_t i = 0; i < Sources.size(); ++i) { + Sources[i]->FindExternalLexicalDecls(DC, IsKindWeWant, Result); + if (!Result.empty()) + return; + } + } + + void + FindFileRegionDecls(clang::FileID File, unsigned Offset, unsigned Length, + llvm::SmallVectorImpl &Decls) override { + for (size_t i = 0; i < Sources.size(); ++i) + Sources[i]->FindFileRegionDecls(File, Offset, Length, Decls); + } + + void CompleteType(clang::TagDecl *Tag) override { + while (!Tag->isCompleteDefinition()) + for (size_t i = 0; i < Sources.size(); ++i) { + // FIXME: We are technically supposed to loop here too until + // Tag->isCompleteDefinition() is true, but if our low quality source + // is failing to complete the tag this code will deadlock. + Sources[i]->CompleteType(Tag); + if (Tag->isCompleteDefinition()) + break; + } + } + + void CompleteType(clang::ObjCInterfaceDecl *Class) override { + for (size_t i = 0; i < Sources.size(); ++i) + Sources[i]->CompleteType(Class); + } + + void ReadComments() override { + for (size_t i = 0; i < Sources.size(); ++i) + Sources[i]->ReadComments(); + } + + void StartedDeserializing() override { + for (size_t i = 0; i < Sources.size(); ++i) + Sources[i]->StartedDeserializing(); + } + + void FinishedDeserializing() override { + for (size_t i = 0; i < Sources.size(); ++i) + Sources[i]->FinishedDeserializing(); + } + + void StartTranslationUnit(clang::ASTConsumer *Consumer) override { + for (size_t i = 0; i < Sources.size(); ++i) + Sources[i]->StartTranslationUnit(Consumer); + } + + void PrintStats() override; + + clang::Module *getModule(unsigned ID) override { + for (size_t i = 0; i < Sources.size(); ++i) + if (auto M = Sources[i]->getModule(ID)) + return M; + return nullptr; + } + + bool DeclIsFromPCHWithObjectFile(const clang::Decl *D) override { + for (auto *S : Sources) + if (S->DeclIsFromPCHWithObjectFile(D)) + return true; + return false; + } + + bool layoutRecordType( + const clang::RecordDecl *Record, uint64_t &Size, uint64_t &Alignment, + llvm::DenseMap &FieldOffsets, + llvm::DenseMap + &BaseOffsets, + llvm::DenseMap + &VirtualBaseOffsets) override { + for (size_t i = 0; i < Sources.size(); ++i) + if (Sources[i]->layoutRecordType(Record, Size, Alignment, FieldOffsets, + BaseOffsets, VirtualBaseOffsets)) + return true; + return false; + } + + void getMemoryBufferSizes(MemoryBufferSizes &sizes) const override { + for (size_t i = 0; i < Sources.size(); ++i) + Sources[i]->getMemoryBufferSizes(sizes); + } + + //===--------------------------------------------------------------------===// + // ExternalSemaSource. + //===--------------------------------------------------------------------===// + + void InitializeSema(clang::Sema &S) override { + for (size_t i = 0; i < Sources.size(); ++i) + Sources[i]->InitializeSema(S); + } + + void ForgetSema() override { + for (size_t i = 0; i < Sources.size(); ++i) + Sources[i]->ForgetSema(); + } + + void ReadMethodPool(clang::Selector Sel) override { + for (size_t i = 0; i < Sources.size(); ++i) + Sources[i]->ReadMethodPool(Sel); + } + + void updateOutOfDateSelector(clang::Selector Sel) override { + for (size_t i = 0; i < Sources.size(); ++i) + Sources[i]->updateOutOfDateSelector(Sel); + } + + void ReadKnownNamespaces( + llvm::SmallVectorImpl &Namespaces) override { + for (size_t i = 0; i < Sources.size(); ++i) + Sources[i]->ReadKnownNamespaces(Namespaces); + } + + void ReadUndefinedButUsed( + llvm::MapVector &Undefined) + override { + for (size_t i = 0; i < Sources.size(); ++i) + Sources[i]->ReadUndefinedButUsed(Undefined); + } + + void ReadMismatchingDeleteExpressions( + llvm::MapVector, + 4>> &Exprs) override { + for (auto &Source : Sources) + Source->ReadMismatchingDeleteExpressions(Exprs); + } + + bool LookupUnqualified(clang::LookupResult &R, clang::Scope *S) override { + for (size_t i = 0; i < Sources.size(); ++i) { + Sources[i]->LookupUnqualified(R, S); + if (!R.empty()) + break; + } + + return !R.empty(); + } + + void ReadTentativeDefinitions( + llvm::SmallVectorImpl &Defs) override { + for (size_t i = 0; i < Sources.size(); ++i) + Sources[i]->ReadTentativeDefinitions(Defs); + } + + void ReadUnusedFileScopedDecls( + llvm::SmallVectorImpl &Decls) override { + for (size_t i = 0; i < Sources.size(); ++i) + Sources[i]->ReadUnusedFileScopedDecls(Decls); + } + + void ReadDelegatingConstructors( + llvm::SmallVectorImpl &Decls) override { + for (size_t i = 0; i < Sources.size(); ++i) + Sources[i]->ReadDelegatingConstructors(Decls); + } + + void ReadExtVectorDecls( + llvm::SmallVectorImpl &Decls) override { + for (size_t i = 0; i < Sources.size(); ++i) + Sources[i]->ReadExtVectorDecls(Decls); + } + + void ReadUnusedLocalTypedefNameCandidates( + llvm::SmallSetVector &Decls) override { + for (size_t i = 0; i < Sources.size(); ++i) + Sources[i]->ReadUnusedLocalTypedefNameCandidates(Decls); + } + + void ReadReferencedSelectors( + llvm::SmallVectorImpl> + &Sels) override { + for (size_t i = 0; i < Sources.size(); ++i) + Sources[i]->ReadReferencedSelectors(Sels); + } + + void ReadWeakUndeclaredIdentifiers( + llvm::SmallVectorImpl> + &WI) override { + for (size_t i = 0; i < Sources.size(); ++i) + Sources[i]->ReadWeakUndeclaredIdentifiers(WI); + } + + void ReadUsedVTables( + llvm::SmallVectorImpl &VTables) override { + for (size_t i = 0; i < Sources.size(); ++i) + Sources[i]->ReadUsedVTables(VTables); + } + + void ReadPendingInstantiations( + llvm::SmallVectorImpl< + std::pair> &Pending) + override { + for (size_t i = 0; i < Sources.size(); ++i) + Sources[i]->ReadPendingInstantiations(Pending); + } + + void ReadLateParsedTemplates( + llvm::MapVector> &LPTMap) + override { + for (size_t i = 0; i < Sources.size(); ++i) + Sources[i]->ReadLateParsedTemplates(LPTMap); + } + + clang::TypoCorrection + CorrectTypo(const clang::DeclarationNameInfo &Typo, int LookupKind, + clang::Scope *S, clang::CXXScopeSpec *SS, + clang::CorrectionCandidateCallback &CCC, + clang::DeclContext *MemberContext, bool EnteringContext, + const clang::ObjCObjectPointerType *OPT) override { + for (size_t I = 0, E = Sources.size(); I < E; ++I) { + if (clang::TypoCorrection C = + Sources[I]->CorrectTypo(Typo, LookupKind, S, SS, CCC, + MemberContext, EnteringContext, OPT)) + return C; + } + return clang::TypoCorrection(); + } + + bool MaybeDiagnoseMissingCompleteType(clang::SourceLocation Loc, + clang::QualType T) override { + for (size_t I = 0, E = Sources.size(); I < E; ++I) { + if (Sources[I]->MaybeDiagnoseMissingCompleteType(Loc, T)) + return true; + } + return false; + } +}; + +} // namespace lldb_private +#endif // liblldb_ASTUtils_h_ Index: lldb/source/Plugins/ExpressionParser/Clang/ASTUtils.cpp =================================================================== --- /dev/null +++ lldb/source/Plugins/ExpressionParser/Clang/ASTUtils.cpp @@ -0,0 +1,26 @@ +//===-- ASTUtils.cpp --------------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "ASTUtils.h" + +lldb_private::ExternalASTSourceWrapper::~ExternalASTSourceWrapper() {} + +void lldb_private::ExternalASTSourceWrapper::PrintStats() { + m_Source->PrintStats(); +} + +lldb_private::ASTConsumerForwarder::~ASTConsumerForwarder() {} + +void lldb_private::ASTConsumerForwarder::PrintStats() { m_c->PrintStats(); } + +lldb_private::SemaSourceWithPriorities::~SemaSourceWithPriorities() {} + +void lldb_private::SemaSourceWithPriorities::PrintStats() { + for (size_t i = 0; i < Sources.size(); ++i) + Sources[i]->PrintStats(); +} Index: lldb/source/Plugins/ExpressionParser/Clang/CMakeLists.txt =================================================================== --- lldb/source/Plugins/ExpressionParser/Clang/CMakeLists.txt +++ lldb/source/Plugins/ExpressionParser/Clang/CMakeLists.txt @@ -6,6 +6,7 @@ ASTDumper.cpp ASTResultSynthesizer.cpp ASTStructExtractor.cpp + ASTUtils.cpp ClangASTSource.cpp ClangExpressionDeclMap.cpp ClangExpressionParser.cpp Index: lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionParser.h =================================================================== --- lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionParser.h +++ lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionParser.h @@ -52,9 +52,14 @@ /// /// @param[in] expr /// The expression to be parsed. + /// + /// @param[in] include_directories + /// List of include directories that should be used when parsing the + /// expression. //------------------------------------------------------------------ ClangExpressionParser(ExecutionContextScope *exe_scope, Expression &expr, - bool generate_debug_info); + bool generate_debug_info, + std::vector include_directories = {}); //------------------------------------------------------------------ /// Destructor @@ -187,6 +192,8 @@ LLDBPreprocessorCallbacks *m_pp_callbacks; ///< Called when the preprocessor ///encounters module imports std::unique_ptr m_ast_context; + + std::vector m_include_directories; }; } Index: lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionParser.cpp =================================================================== --- lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionParser.cpp +++ lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionParser.cpp @@ -36,6 +36,7 @@ #include "llvm/ADT/StringRef.h" #include "llvm/ExecutionEngine/ExecutionEngine.h" +#include "llvm/Support/CrashRecoveryContext.h" #include "llvm/Support/Debug.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/TargetSelect.h" @@ -53,11 +54,17 @@ #include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/Signals.h" +#include "ClangDiagnostic.h" +#include "ClangExpressionParser.h" +#include "ClangUserExpression.h" + +#include "ASTUtils.h" #include "ClangASTSource.h" #include "ClangDiagnostic.h" #include "ClangExpressionDeclMap.h" #include "ClangExpressionHelper.h" #include "ClangExpressionParser.h" +#include "ClangHost.h" #include "ClangModulesDeclVendor.h" #include "ClangPersistentVariables.h" #include "IRForTarget.h" @@ -210,15 +217,60 @@ std::shared_ptr m_passthrough; }; +static void +SetupModuleHeaderPaths(CompilerInstance *compiler, + std::vector include_directories) { + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS)); + + HeaderSearchOptions &search_opts = compiler->getHeaderSearchOpts(); + + for (ConstString dir : include_directories) { + search_opts.AddPath(dir.AsCString(), frontend::System, false, true); + if (log) + log->Printf("Added user include dir: %s", dir.AsCString()); + } + + llvm::SmallString<128> module_cache; + auto props = ModuleList::GetGlobalModuleListProperties(); + props.GetClangModulesCachePath().GetPath(module_cache); + search_opts.ModuleCachePath = module_cache.str(); + if (log) + log->Printf("Using module cache path: %s", module_cache.c_str()); + + FileSpec clang_resource_dir = GetClangResourceDir(); + std::string resource_dir = clang_resource_dir.GetPath(); + if (FileSystem::Instance().IsDirectory(resource_dir)) { + search_opts.ResourceDir = resource_dir; + std::string resource_include = resource_dir + "/include"; + search_opts.AddPath(resource_include, frontend::System, false, true); + + if (log) + log->Printf("Added resource include dir: %s", resource_include.c_str()); + } + + search_opts.ImplicitModuleMaps = true; + + std::vector system_include_directories = + Platform::GetHostPlatform()->GetSystemIncludeDirectories( + lldb::eLanguageTypeC_plus_plus); + + for (const std::string &include_dir : system_include_directories) { + search_opts.AddPath(include_dir, frontend::System, false, true); + + if (log) + log->Printf("Added system include dir: %s", include_dir.c_str()); + } +} + //===----------------------------------------------------------------------===// // Implementation of ClangExpressionParser //===----------------------------------------------------------------------===// -ClangExpressionParser::ClangExpressionParser(ExecutionContextScope *exe_scope, - Expression &expr, - bool generate_debug_info) +ClangExpressionParser::ClangExpressionParser( + ExecutionContextScope *exe_scope, Expression &expr, + bool generate_debug_info, std::vector include_directories) : ExpressionParser(exe_scope, expr, generate_debug_info), m_compiler(), - m_pp_callbacks(nullptr) { + m_pp_callbacks(nullptr), m_include_directories(include_directories) { Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS)); // We can't compile expressions without a target. So if the exe_scope is @@ -442,6 +494,31 @@ // long time parsing and importing debug information. lang_opts.SpellChecking = false; + auto &clang_expr = *static_cast(&m_expr); + if (clang_expr.DidImportCxxModules()) { + if (log) + log->Printf("Adding lang options for importing C++ modules"); + + lang_opts.Modules = true; + // We want to implicitly build modules. + lang_opts.ImplicitModules = true; + // To automatically import all submodules when we import 'std'. + lang_opts.ModulesLocalVisibility = false; + + // We use the @import statements, so we need this: + // FIXME: We could use the modules-ts, but that currently doesn't work. + lang_opts.ObjC = true; + + // Options we need to parse libc++ code successfully. + // FIXME: We should ask the driver for the appropriate default flags. + lang_opts.GNUMode = true; + lang_opts.GNUKeywords = true; + lang_opts.DoubleSquareBracketAttributes = true; + lang_opts.CPlusPlus11 = true; + + SetupModuleHeaderPaths(m_compiler.get(), m_include_directories); + } + if (process_sp && lang_opts.ObjC) { if (process_sp->GetObjCLanguageRuntime()) { if (process_sp->GetObjCLanguageRuntime()->GetRuntimeVersion() == @@ -522,17 +599,6 @@ m_compiler->createASTContext(); clang::ASTContext &ast_context = m_compiler->getASTContext(); - ClangExpressionHelper *type_system_helper = - dyn_cast(m_expr.GetTypeSystemHelper()); - ClangExpressionDeclMap *decl_map = type_system_helper->DeclMap(); - - if (decl_map) { - llvm::IntrusiveRefCntPtr ast_source( - decl_map->CreateProxy()); - decl_map->InstallASTContext(ast_context, m_compiler->getFileManager()); - ast_context.setExternalSource(ast_source); - } - m_ast_context.reset( new ClangASTContext(m_compiler->getTargetOpts().Triple.c_str())); m_ast_context->setASTContext(&ast_context); @@ -874,12 +940,6 @@ ClangExpressionHelper *type_system_helper = dyn_cast(m_expr.GetTypeSystemHelper()); - ASTConsumer *ast_transformer = - type_system_helper->ASTTransformer(m_code_generator.get()); - - if (ClangExpressionDeclMap *decl_map = type_system_helper->DeclMap()) - decl_map->InstallCodeGenerator(m_code_generator.get()); - // If we want to parse for code completion, we need to attach our code // completion consumer to the Sema and specify a completion position. // While parsing the Sema will call this consumer with the provided @@ -894,17 +954,65 @@ PP.SetCodeCompletionPoint(main_file, completion_line, completion_column); } + ASTConsumer *ast_transformer = + type_system_helper->ASTTransformer(m_code_generator.get()); + + std::unique_ptr Consumer; if (ast_transformer) { - ast_transformer->Initialize(m_compiler->getASTContext()); - ParseAST(m_compiler->getPreprocessor(), ast_transformer, - m_compiler->getASTContext(), false, TU_Complete, - completion_consumer); + Consumer.reset(new ASTConsumerForwarder(ast_transformer)); + } else if (m_code_generator) { + Consumer.reset(new ASTConsumerForwarder(m_code_generator.get())); } else { - m_code_generator->Initialize(m_compiler->getASTContext()); - ParseAST(m_compiler->getPreprocessor(), m_code_generator.get(), - m_compiler->getASTContext(), false, TU_Complete, - completion_consumer); + Consumer.reset(new ASTConsumer()); + } + + clang::ASTContext &ast_context = m_compiler->getASTContext(); + + m_compiler->setSema(new Sema(m_compiler->getPreprocessor(), ast_context, + *Consumer, TU_Complete, completion_consumer)); + m_compiler->setASTConsumer(std::move(Consumer)); + + if (ast_context.getLangOpts().Modules) + m_compiler->createModuleManager(); + + ClangExpressionDeclMap *decl_map = type_system_helper->DeclMap(); + if (decl_map) { + decl_map->InstallCodeGenerator(&m_compiler->getASTConsumer()); + + clang::ExternalASTSource *ast_source = decl_map->CreateProxy(); + + if (ast_context.getExternalSource()) { + auto module_wrapper = + new ExternalASTSourceWrapper(ast_context.getExternalSource()); + + auto ast_source_wrapper = new ExternalASTSourceWrapper(ast_source); + + auto multiplexer = + new SemaSourceWithPriorities(*module_wrapper, *ast_source_wrapper); + IntrusiveRefCntPtr Source(multiplexer); + ast_context.setExternalSource(Source); + } else { + ast_context.setExternalSource(ast_source); + } + decl_map->InstallASTContext(ast_context, m_compiler->getFileManager()); + } + + // Check that the ASTReader is properly attached to ASTContext and Sema. + if (ast_context.getLangOpts().Modules) { + assert(m_compiler->getASTContext().getExternalSource() && + "ASTContext doesn't know about the ASTReader?"); + assert(m_compiler->getSema().getExternalSource() && + "Sema doesn't know about the ASTReader?"); + } + + { + llvm::CrashRecoveryContextCleanupRegistrar CleanupSema( + &m_compiler->getSema()); + ParseAST(m_compiler->getSema(), false, false); } + // Destroy the Sema. This is necessary because we want to emulate the + // original behavior of ParseAST (which also destroys the Sema after parsing). + m_compiler->setSema(nullptr); diag_buf->EndSourceFile(); Index: lldb/source/Plugins/ExpressionParser/Clang/ClangUserExpression.h =================================================================== --- lldb/source/Plugins/ExpressionParser/Clang/ClangUserExpression.h +++ lldb/source/Plugins/ExpressionParser/Clang/ClangUserExpression.h @@ -167,6 +167,8 @@ lldb::ExpressionVariableSP GetResultAfterDematerialization(ExecutionContextScope *exe_scope) override; + bool DidImportCxxModules() const { return m_imported_cpp_modules; } + private: //------------------------------------------------------------------ /// Populate m_in_cplusplus_method and m_in_objectivec_method based on the @@ -180,8 +182,10 @@ lldb::addr_t struct_address, DiagnosticManager &diagnostic_manager) override; + std::vector GetModulesToImport(ExecutionContext &exe_ctx); void UpdateLanguageForExpr(DiagnosticManager &diagnostic_manager, - ExecutionContext &exe_ctx); + ExecutionContext &exe_ctx, + std::vector modules_to_import); bool SetupPersistentState(DiagnosticManager &diagnostic_manager, ExecutionContext &exe_ctx); bool PrepareForParsing(DiagnosticManager &diagnostic_manager, @@ -206,6 +210,8 @@ /// The language type of the current expression. lldb::LanguageType m_expr_lang = lldb::eLanguageTypeUnknown; + /// The include directories that should be used when parsing the expression. + std::vector m_include_directories; /// The absolute character position in the transformed source code where the /// user code (as typed by the user) starts. If the variable is empty, then we @@ -216,6 +222,9 @@ /// The object (if any) in which context the expression is evaluated. /// See the comment to `UserExpression::Evaluate` for details. ValueObject *m_ctx_obj; + + /// True iff this expression explicitly imported C++ modules. + bool m_imported_cpp_modules = false; }; } // namespace lldb_private Index: lldb/source/Plugins/ExpressionParser/Clang/ClangUserExpression.cpp =================================================================== --- lldb/source/Plugins/ExpressionParser/Clang/ClangUserExpression.cpp +++ lldb/source/Plugins/ExpressionParser/Clang/ClangUserExpression.cpp @@ -36,6 +36,7 @@ #include "lldb/Symbol/Block.h" #include "lldb/Symbol/ClangASTContext.h" #include "lldb/Symbol/ClangExternalASTSourceCommon.h" +#include "lldb/Symbol/CompileUnit.h" #include "lldb/Symbol/Function.h" #include "lldb/Symbol/ObjectFile.h" #include "lldb/Symbol/SymbolVendor.h" @@ -396,7 +397,8 @@ } void ClangUserExpression::UpdateLanguageForExpr( - DiagnosticManager &diagnostic_manager, ExecutionContext &exe_ctx) { + DiagnosticManager &diagnostic_manager, ExecutionContext &exe_ctx, + std::vector modules_to_import) { m_expr_lang = lldb::LanguageType::eLanguageTypeUnknown; std::string prefix = m_expr_prefix; @@ -416,8 +418,8 @@ m_expr_lang = lldb::eLanguageTypeC; if (!source_code->GetText(m_transformed_text, m_expr_lang, - m_in_static_method, exe_ctx, - !m_ctx_obj)) { + m_in_static_method, exe_ctx, !m_ctx_obj, + modules_to_import)) { diagnostic_manager.PutString(eDiagnosticSeverityError, "couldn't construct expression body"); return; @@ -435,8 +437,76 @@ } } +static bool SupportsCxxModuleImport(lldb::LanguageType language) { + switch (language) { + case lldb::eLanguageTypeC_plus_plus: + case lldb::eLanguageTypeC_plus_plus_03: + case lldb::eLanguageTypeC_plus_plus_11: + case lldb::eLanguageTypeC_plus_plus_14: + case lldb::eLanguageTypeObjC_plus_plus: + return true; + default: + return false; + } +} + +std::vector +ClangUserExpression::GetModulesToImport(ExecutionContext &exe_ctx) { + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS)); + + if (!SupportsCxxModuleImport(Language())) + return {}; + + Target *target = exe_ctx.GetTargetPtr(); + if (!target || !target->GetEnableImportStdModule()) + return {}; + + StackFrame *frame = exe_ctx.GetFramePtr(); + if (!frame) + return {}; + + Block *block = frame->GetFrameBlock(); + if (!block) + return {}; + + SymbolContext sc; + block->CalculateSymbolContext(&sc); + if (!sc.comp_unit) + return {}; + + if (log) { + for (const SourceModule &m : sc.comp_unit->GetImportedModules()) { + llvm::SmallString<64> path_string; + llvm::raw_svector_ostream stream(path_string); + for (const ConstString &s : m.path) + stream << s.AsCString() << '.'; + + // Drop trailing '.'. + if (!path_string.empty()) + path_string.pop_back(); + + log->Printf("Found module in compile unit: %s - include dir: %s", + path_string.c_str(), m.search_path.AsCString()); + } + } + + for (const SourceModule &m : sc.comp_unit->GetImportedModules()) + m_include_directories.push_back(m.search_path); + + // Check if we imported 'std' or any of its submodules. + // We currently don't support importing any other modules in the expression + // parser. + for (const SourceModule &m : sc.comp_unit->GetImportedModules()) + if (!m.path.empty() && m.path.front() == ConstString("std")) + return {"std"}; + + return {}; +} + bool ClangUserExpression::PrepareForParsing( DiagnosticManager &diagnostic_manager, ExecutionContext &exe_ctx) { + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS)); + InstallContext(exe_ctx); if (!SetupPersistentState(diagnostic_manager, exe_ctx)) @@ -457,7 +527,19 @@ SetupDeclVendor(exe_ctx, m_target); - UpdateLanguageForExpr(diagnostic_manager, exe_ctx); + std::vector modules_to_include = GetModulesToImport(exe_ctx); + m_imported_cpp_modules = !modules_to_include.empty(); + + if (log) { + llvm::SmallString<64> modules_list; + llvm::raw_svector_ostream stream(modules_list); + for (const std::string &module : modules_to_include) + stream << " " << module << "\n"; + log->Printf("List of imported modules in expression:\n%sEnd of list", + modules_list.c_str()); + } + + UpdateLanguageForExpr(diagnostic_manager, exe_ctx, modules_to_include); return true; } @@ -516,7 +598,8 @@ // succeeds or the rewrite parser we might make if it fails. But the // parser_sp will never be empty. - ClangExpressionParser parser(exe_scope, *this, generate_debug_info); + ClangExpressionParser parser(exe_scope, *this, generate_debug_info, + m_include_directories); unsigned num_errors = parser.Parse(diagnostic_manager); Index: lldb/source/Plugins/Platform/Linux/PlatformLinux.h =================================================================== --- lldb/source/Plugins/Platform/Linux/PlatformLinux.h +++ lldb/source/Plugins/Platform/Linux/PlatformLinux.h @@ -52,6 +52,9 @@ bool CanDebugProcess() override; + std::vector + GetSystemIncludeDirectories(lldb::LanguageType lang) override; + lldb::ProcessSP DebugProcess(ProcessLaunchInfo &launch_info, Debugger &debugger, Target *target, Status &error) override; Index: lldb/source/Plugins/Platform/Linux/PlatformLinux.cpp =================================================================== --- lldb/source/Plugins/Platform/Linux/PlatformLinux.cpp +++ lldb/source/Plugins/Platform/Linux/PlatformLinux.cpp @@ -263,6 +263,25 @@ } } +std::vector +PlatformLinux::GetSystemIncludeDirectories(lldb::LanguageType lang) { + std::string sys_root = GetSDKRootDirectory().AsCString(""); + switch (lang) { + case lldb::eLanguageTypeC: + case lldb::eLanguageTypeC89: + case lldb::eLanguageTypeC99: + case lldb::eLanguageTypeC11: + case lldb::eLanguageTypeC_plus_plus: + case lldb::eLanguageTypeC_plus_plus_03: + case lldb::eLanguageTypeC_plus_plus_11: + case lldb::eLanguageTypeC_plus_plus_14: + case lldb::eLanguageTypeObjC_plus_plus: + return {sys_root + "/usr/include/"}; + default: + return {}; + } +} + // For local debugging, Linux will override the debug logic to use llgs-launch // rather than lldb-launch, llgs-attach. This differs from current lldb- // launch, debugserver-attach approach on MacOSX. Index: lldb/source/Target/Target.cpp =================================================================== --- lldb/source/Target/Target.cpp +++ lldb/source/Target/Target.cpp @@ -3328,6 +3328,9 @@ {"auto-import-clang-modules", OptionValue::eTypeBoolean, false, true, nullptr, {}, "Automatically load Clang modules referred to by the program."}, + {"import-std-module", OptionValue::eTypeBoolean, false, false, + nullptr, {}, + "Import the C++ std module to improve debugging STL containers."}, {"auto-apply-fixits", OptionValue::eTypeBoolean, false, true, nullptr, {}, "Automatically apply fix-it hints to expressions."}, {"notify-about-fixits", OptionValue::eTypeBoolean, false, true, nullptr, @@ -3466,6 +3469,7 @@ ePropertyDebugFileSearchPaths, ePropertyClangModuleSearchPaths, ePropertyAutoImportClangModules, + ePropertyImportStdModule, ePropertyAutoApplyFixIts, ePropertyNotifyAboutFixIts, ePropertySaveObjects, @@ -3888,6 +3892,12 @@ nullptr, idx, g_properties[idx].default_uint_value != 0); } +bool TargetProperties::GetEnableImportStdModule() const { + const uint32_t idx = ePropertyImportStdModule; + return m_collection_sp->GetPropertyAtIndexAsBoolean( + nullptr, idx, g_properties[idx].default_uint_value != 0); +} + bool TargetProperties::GetEnableAutoApplyFixIts() const { const uint32_t idx = ePropertyAutoApplyFixIts; return m_collection_sp->GetPropertyAtIndexAsBoolean(