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/Symbol/CompileUnit.h =================================================================== --- lldb/include/lldb/Symbol/CompileUnit.h +++ lldb/include/lldb/Symbol/CompileUnit.h @@ -257,6 +257,7 @@ //------------------------------------------------------------------ FileSpecList &GetSupportFiles(); + typedef std::vector ModulePath; //------------------------------------------------------------------ /// Get the compile unit's imported module list. /// @@ -266,7 +267,18 @@ /// @return /// A list of imported module names. //------------------------------------------------------------------ - const std::vector &GetImportedModules(); + const std::vector &GetImportedModules(); + + //------------------------------------------------------------------ + /// Get the compile unit's imported module list. + /// + /// This reports all the imports that the compile unit made, including the + /// current module. + /// + /// @return + /// A list of imported module names. + //------------------------------------------------------------------ + const std::vector &GetModuleIncludes(); //------------------------------------------------------------------ /// Get the SymbolFile plug-in user data. @@ -422,10 +434,14 @@ /// Maps UIDs to functions. llvm::DenseMap m_functions_by_uid; - std::vector m_imported_modules; ///< All modules, including the - ///current module, imported by - ///this - ///< compile unit. + + std::vector m_imported_modules; ///< All modules, including the + /// current module, imported by + /// this + ///< compile unit. + /// The include directories of the imported modules. + std::vector m_module_includes; + FileSpecList m_support_files; ///< Files associated with this compile unit's ///line table and declarations. std::unique_ptr @@ -454,6 +470,8 @@ (1u << 6) ///< Have we parsed the debug macros already? }; + void UpdateModules(); + DISALLOW_COPY_AND_ASSIGN(CompileUnit); }; Index: lldb/include/lldb/Symbol/SymbolFile.h =================================================================== --- lldb/include/lldb/Symbol/SymbolFile.h +++ lldb/include/lldb/Symbol/SymbolFile.h @@ -133,9 +133,11 @@ virtual size_t ParseTypes(CompileUnit &comp_unit) = 0; virtual bool ParseIsOptimized(CompileUnit &comp_unit) { return false; } + typedef std::vector ModulePath; virtual bool ParseImportedModules(const SymbolContext &sc, - std::vector &imported_modules) = 0; + std::vector &imported_modules, + std::vector &module_includes) = 0; virtual size_t ParseBlocksRecursive(Function &func) = 0; virtual size_t ParseVariablesForContext(const SymbolContext &sc) = 0; virtual Type *ResolveTypeUID(lldb::user_id_t type_uid) = 0; Index: lldb/include/lldb/Symbol/SymbolVendor.h =================================================================== --- lldb/include/lldb/Symbol/SymbolVendor.h +++ lldb/include/lldb/Symbol/SymbolVendor.h @@ -60,8 +60,10 @@ virtual size_t ParseTypes(CompileUnit &comp_unit); + typedef std::vector ModulePath; virtual bool ParseImportedModules(const SymbolContext &sc, - std::vector &imported_modules); + std::vector &imported_modules, + std::vector &module_includes); virtual size_t ParseBlocksRecursive(Function &func); Index: lldb/include/lldb/Target/Platform.h =================================================================== --- lldb/include/lldb/Target/Platform.h +++ lldb/include/lldb/Target/Platform.h @@ -276,6 +276,19 @@ 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 void GetSystemIncludeDirectoriesForLanguage( + lldb::LanguageType lang, std::vector &directories) {} + 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 += -std=c++11 -fmodules -glldb -fimplicit-module-maps +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,6 @@ +#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 += -std=c++11 -fmodules -glldb -fimplicit-module-maps +CXX_SOURCES := main.cpp +include $(LEVEL)/Makefile.rules Index: lldb/packages/Python/lldbsuite/test/expression_command/import-std-module/conflicts/TestImportStdModule.py =================================================================== --- /dev/null +++ lldb/packages/Python/lldbsuite/test/expression_command/import-std-module/conflicts/TestImportStdModule.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 += -std=c++11 -fmodules -glldb -fimplicit-module-maps +CXX_SOURCES := main.cpp +include $(LEVEL)/Makefile.rules Index: lldb/packages/Python/lldbsuite/test/expression_command/import-std-module/no-std-module/TestImportStdModule.py =================================================================== --- /dev/null +++ lldb/packages/Python/lldbsuite/test/expression_command/import-std-module/no-std-module/TestImportStdModule.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/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/ASTConsumerUtils.h =================================================================== --- /dev/null +++ lldb/source/Plugins/ExpressionParser/Clang/ASTConsumerUtils.h @@ -0,0 +1,584 @@ +//===-- ASTConsumerUtils.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_ASTConsumerUtils_h_ +#define liblldb_ASTConsumerUtils_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; + + virtual clang::Decl *GetExternalDecl(uint32_t ID) override { + return m_Source->GetExternalDecl(ID); + } + + virtual clang::Selector GetExternalSelector(uint32_t ID) override { + return m_Source->GetExternalSelector(ID); + } + + virtual uint32_t GetNumExternalSelectors() override { + return m_Source->GetNumExternalSelectors(); + } + + virtual clang::Stmt *GetExternalDeclStmt(uint64_t Offset) override { + return m_Source->GetExternalDeclStmt(Offset); + } + + virtual clang::CXXCtorInitializer ** + GetExternalCXXCtorInitializers(uint64_t Offset) override { + return m_Source->GetExternalCXXCtorInitializers(Offset); + } + + virtual clang::CXXBaseSpecifier * + GetExternalCXXBaseSpecifiers(uint64_t Offset) override { + return m_Source->GetExternalCXXBaseSpecifiers(Offset); + } + + virtual void updateOutOfDateIdentifier(clang::IdentifierInfo &II) override { + m_Source->updateOutOfDateIdentifier(II); + } + + virtual bool + FindExternalVisibleDeclsByName(const clang::DeclContext *DC, + clang::DeclarationName Name) override { + return m_Source->FindExternalVisibleDeclsByName(DC, Name); + } + + virtual void completeVisibleDeclsMap(const clang::DeclContext *DC) override { + m_Source->completeVisibleDeclsMap(DC); + } + + virtual clang::Module *getModule(unsigned ID) override { + return m_Source->getModule(ID); + } + + virtual llvm::Optional + getSourceDescriptor(unsigned ID) override { + return m_Source->getSourceDescriptor(ID); + } + + virtual ExtKind hasExternalDefinitions(const clang::Decl *D) override { + return m_Source->hasExternalDefinitions(D); + } + + virtual void FindExternalLexicalDecls( + const clang::DeclContext *DC, + llvm::function_ref IsKindWeWant, + llvm::SmallVectorImpl &Result) override { + m_Source->FindExternalLexicalDecls(DC, IsKindWeWant, Result); + } + + virtual void + FindFileRegionDecls(clang::FileID File, unsigned Offset, unsigned Length, + llvm::SmallVectorImpl &Decls) override { + m_Source->FindFileRegionDecls(File, Offset, Length, Decls); + } + + virtual void CompleteRedeclChain(const clang::Decl *D) override { + m_Source->CompleteRedeclChain(D); + } + + virtual void CompleteType(clang::TagDecl *Tag) override { + m_Source->CompleteType(Tag); + } + + virtual void CompleteType(clang::ObjCInterfaceDecl *Class) override { + m_Source->CompleteType(Class); + } + + virtual void ReadComments() override { m_Source->ReadComments(); } + + virtual void StartedDeserializing() override { + m_Source->StartedDeserializing(); + } + + virtual void FinishedDeserializing() override { + m_Source->FinishedDeserializing(); + } + + virtual void StartTranslationUnit(clang::ASTConsumer *Consumer) override { + m_Source->StartTranslationUnit(Consumer); + } + + virtual void PrintStats() override; + + virtual 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_ASTConsumerUtils_h_ Index: lldb/source/Plugins/ExpressionParser/Clang/ASTConsumerUtils.cpp =================================================================== --- /dev/null +++ lldb/source/Plugins/ExpressionParser/Clang/ASTConsumerUtils.cpp @@ -0,0 +1,26 @@ +//===-- ASTConsumerUtils.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 "ASTConsumerUtils.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 @@ -3,6 +3,7 @@ endif() add_lldb_library(lldbPluginExpressionParserClang PLUGIN + ASTConsumerUtils.cpp ASTDumper.cpp ASTResultSynthesizer.cpp ASTStructExtractor.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 @@ -189,6 +194,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 @@ -38,6 +38,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" @@ -57,10 +58,13 @@ #include "ClangDiagnostic.h" #include "ClangExpressionParser.h" +#include "ClangUserExpression.h" +#include "ASTConsumerUtils.h" #include "ClangASTSource.h" #include "ClangExpressionDeclMap.h" #include "ClangExpressionHelper.h" +#include "ClangHost.h" #include "ClangModulesDeclVendor.h" #include "ClangPersistentVariables.h" #include "IRForTarget.h" @@ -213,15 +217,54 @@ std::shared_ptr m_passthrough; }; +static void +SetupModuleHeaderPaths(CompilerInstance *compiler, + std::vector include_directories) { + for (ConstString dir : include_directories) { + compiler->getHeaderSearchOpts().AddPath( + dir.AsCString(), clang::frontend::IncludeDirGroup::System, false, true); + } + + { + llvm::SmallString<128> path; + auto props = ModuleList::GetGlobalModuleListProperties(); + props.GetClangModulesCachePath().GetPath(path); + compiler->getHeaderSearchOpts().ModuleCachePath = path.str(); + } + + { + FileSpec clang_resource_dir = GetClangResourceDir(); + std::string resource_dir = clang_resource_dir.GetPath(); + if (FileSystem::Instance().IsDirectory(resource_dir)) { + compiler->getHeaderSearchOpts().ResourceDir = resource_dir; + + compiler->getHeaderSearchOpts().AddPath( + resource_dir + "/include", clang::frontend::IncludeDirGroup::System, + false, true); + } + } + + compiler->getHeaderSearchOpts().ImplicitModuleMaps = true; + + std::vector system_include_directories; + Platform::GetHostPlatform()->GetSystemIncludeDirectoriesForLanguage( + lldb::LanguageType::eLanguageTypeC_plus_plus, system_include_directories); + + for (const std::string &include_dir : system_include_directories) { + compiler->getHeaderSearchOpts().AddPath( + include_dir, clang::frontend::IncludeDirGroup::System, false, true); + } +} + //===----------------------------------------------------------------------===// // 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 @@ -428,6 +471,23 @@ // long time parsing and importing debug information. lang_opts.SpellChecking = false; + auto &clang_expr = *static_cast(&m_expr); + if (clang_expr.DidImportCxxModules()) { + lang_opts.Modules = true; + lang_opts.ImplicitModules = true; + // To automatically import all submodules. + lang_opts.ModulesLocalVisibility = false; + + // for @import statements. + lang_opts.ObjC = true; + 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() == @@ -513,17 +573,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); @@ -865,12 +914,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 @@ -885,17 +928,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/ClangModulesDeclVendor.cpp =================================================================== --- lldb/source/Plugins/ExpressionParser/Clang/ClangModulesDeclVendor.cpp +++ lldb/source/Plugins/ExpressionParser/Clang/ClangModulesDeclVendor.cpp @@ -287,7 +287,10 @@ switch (language) { default: return false; - // C++ and friends to be added + case lldb::LanguageType::eLanguageTypeC_plus_plus: + case lldb::LanguageType::eLanguageTypeC_plus_plus_03: + case lldb::LanguageType::eLanguageTypeC_plus_plus_11: + case lldb::LanguageType::eLanguageTypeC_plus_plus_14: case lldb::LanguageType::eLanguageTypeC: case lldb::LanguageType::eLanguageTypeC11: case lldb::LanguageType::eLanguageTypeC89: @@ -300,20 +303,19 @@ bool ClangModulesDeclVendorImpl::AddModulesForCompileUnit( CompileUnit &cu, ClangModulesDeclVendor::ModuleVector &exported_modules, Stream &error_stream) { - if (LanguageSupportsClangModules(cu.GetLanguage())) { - std::vector imported_modules = cu.GetImportedModules(); + if (!LanguageSupportsClangModules(cu.GetLanguage())) + return true; - for (ConstString imported_module : imported_modules) { - std::vector path; + auto imported_modules = cu.GetImportedModules(); - path.push_back(imported_module); + for (CompileUnit::ModulePath imported_module : imported_modules) { + CompileUnit::ModulePath path; - if (!AddModule(path, &exported_modules, error_stream)) { - return false; - } - } + path.push_back(imported_module.back()); - return true; + if (!AddModule(path, &exported_modules, error_stream)) { + return false; + } } return true; 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" @@ -398,7 +399,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; @@ -418,8 +420,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; @@ -437,6 +439,54 @@ } } +static bool SupportsCxxModuleImport(lldb::LanguageType language) { + switch (language) { + case lldb::eLanguageTypeC_plus_plus: + 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) { + 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 {}; + + m_include_directories = sc.comp_unit->GetModuleIncludes(); + std::vector modules = + sc.comp_unit->GetImportedModules(); + + // Check if we imported 'std' or any of its submodules. + // We currently don't support importing any other modules in the expression + // parser. + for (CompileUnit::ModulePath path : modules) + if (!path.empty() && path.front() == ConstString("std")) + return {"std"}; + + return {}; +} + bool ClangUserExpression::PrepareForParsing( DiagnosticManager &diagnostic_manager, ExecutionContext &exe_ctx) { InstallContext(exe_ctx); @@ -459,7 +509,10 @@ 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(); + + UpdateLanguageForExpr(diagnostic_manager, exe_ctx, modules_to_include); return true; } @@ -518,7 +571,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; + void GetSystemIncludeDirectoriesForLanguage( + lldb::LanguageType lang, std::vector &directories) 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 @@ } } +void PlatformLinux::GetSystemIncludeDirectoriesForLanguage( + lldb::LanguageType lang, std::vector &directories) { + 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: + directories = {"/usr/include/"}; + break; + default: + break; + } +} + // 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/Plugins/SymbolFile/Breakpad/SymbolFileBreakpad.h =================================================================== --- lldb/source/Plugins/SymbolFile/Breakpad/SymbolFileBreakpad.h +++ lldb/source/Plugins/SymbolFile/Breakpad/SymbolFileBreakpad.h @@ -71,7 +71,8 @@ bool ParseImportedModules(const SymbolContext &sc, - std::vector &imported_modules) override { + std::vector &imported_modules, + std::vector &module_includes) override { return false; } Index: lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h =================================================================== --- lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h +++ lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h @@ -125,7 +125,8 @@ bool ParseImportedModules( const lldb_private::SymbolContext &sc, - std::vector &imported_modules) override; + std::vector &imported_modules, + std::vector &module_includes) override; size_t ParseBlocksRecursive(lldb_private::Function &func) override; Index: lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp =================================================================== --- lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp +++ lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp @@ -908,43 +908,65 @@ return false; } +/// Constructs the import module path (e.g. {"std", "iostream"}) from the given +/// imported DW_TAG_MODULE DIE. +static void BuildModulePath(DWARFDIE die, SymbolFile::ModulePath &path) { + if (die.Tag() != DW_TAG_module) + return; + + const char *name = die.GetAttributeValueAsString(DW_AT_name, nullptr); + if (!name) + return; + + DWARFDIE parent = die.GetParent(); + // If we have a parent, recursively visit it before appending the current + // module name. Otherwise our module path would be in reverse + // (e.g. {"iostream", "std"} instead of {"std", "iostream"}). + if (parent.IsValid()) + BuildModulePath(parent, path); + ConstString const_name(name); + path.push_back(const_name); +} + bool SymbolFileDWARF::ParseImportedModules( const lldb_private::SymbolContext &sc, - std::vector &imported_modules) { + std::vector &imported_modules, + std::vector &module_includes) { ASSERT_MODULE_LOCK(this); assert(sc.comp_unit); DWARFUnit *dwarf_cu = GetDWARFCompileUnit(sc.comp_unit); - if (dwarf_cu) { - if (ClangModulesDeclVendor::LanguageSupportsClangModules( - sc.comp_unit->GetLanguage())) { - UpdateExternalModuleListIfNeeded(); + if (!dwarf_cu) + return false; + if (!ClangModulesDeclVendor::LanguageSupportsClangModules( + sc.comp_unit->GetLanguage())) + return false; + UpdateExternalModuleListIfNeeded(); - if (sc.comp_unit) { - const DWARFDIE die = dwarf_cu->DIE(); + if (sc.comp_unit) { + const DWARFDIE die = dwarf_cu->DIE(); - if (die) { - for (DWARFDIE child_die = die.GetFirstChild(); child_die; - child_die = child_die.GetSibling()) { - if (child_die.Tag() == DW_TAG_imported_declaration) { - if (DWARFDIE module_die = - child_die.GetReferencedDIE(DW_AT_import)) { - if (module_die.Tag() == DW_TAG_module) { - if (const char *name = module_die.GetAttributeValueAsString( - DW_AT_name, nullptr)) { - ConstString const_name(name); - imported_modules.push_back(const_name); - } - } - } - } - } - } - } else { - for (const auto &pair : m_external_type_modules) { - imported_modules.push_back(pair.first); + if (!die) + return false; + for (DWARFDIE child_die = die.GetFirstChild(); child_die; + child_die = child_die.GetSibling()) { + if (child_die.Tag() != DW_TAG_imported_declaration) + continue; + if (DWARFDIE module_die = child_die.GetReferencedDIE(DW_AT_import)) { + if (module_die.Tag() != DW_TAG_module) + continue; + ModulePath path; + BuildModulePath(module_die, path); + imported_modules.push_back(path); + if (const char *inc = module_die.GetAttributeValueAsString( + DW_AT_LLVM_include_path, nullptr)) { + ConstString const_inc(inc); + module_includes.push_back(const_inc); } } } + } else { + for (const auto &pair : m_external_type_modules) + imported_modules.push_back({pair.first}); } return false; } Index: lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.h =================================================================== --- lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.h +++ lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.h @@ -72,7 +72,8 @@ bool ParseImportedModules( const lldb_private::SymbolContext &sc, - std::vector &imported_modules) override; + std::vector &imported_modules, + std::vector &module_includes) override; size_t ParseBlocksRecursive(lldb_private::Function &func) override; size_t ParseVariablesForContext(const lldb_private::SymbolContext &sc) override; Index: lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.cpp =================================================================== --- lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.cpp +++ lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.cpp @@ -669,10 +669,12 @@ } bool SymbolFileDWARFDebugMap::ParseImportedModules( - const SymbolContext &sc, std::vector &imported_modules) { + const SymbolContext &sc, std::vector &imported_modules, + std::vector &module_includes) { SymbolFileDWARF *oso_dwarf = GetSymbolFile(sc); if (oso_dwarf) - return oso_dwarf->ParseImportedModules(sc, imported_modules); + return oso_dwarf->ParseImportedModules(sc, imported_modules, + module_includes); return false; } Index: lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.h =================================================================== --- lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.h +++ lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.h @@ -94,9 +94,9 @@ FileSpecList &support_files) override; size_t ParseTypes(lldb_private::CompileUnit &comp_unit) override; - bool - ParseImportedModules(const SymbolContext &sc, - std::vector &imported_modules) override; + bool ParseImportedModules(const SymbolContext &sc, + std::vector &imported_modules, + std::vector &module_includes) override; size_t ParseBlocksRecursive(Function &func) override; Index: lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.cpp =================================================================== --- lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.cpp +++ lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.cpp @@ -1136,7 +1136,8 @@ } bool SymbolFileNativePDB::ParseImportedModules( - const SymbolContext &sc, std::vector &imported_modules) { + const SymbolContext &sc, std::vector &imported_modules, + std::vector &module_includes) { // PDB does not yet support module debug info return false; } Index: lldb/source/Plugins/SymbolFile/PDB/SymbolFilePDB.h =================================================================== --- lldb/source/Plugins/SymbolFile/PDB/SymbolFilePDB.h +++ lldb/source/Plugins/SymbolFile/PDB/SymbolFilePDB.h @@ -74,7 +74,8 @@ bool ParseImportedModules( const lldb_private::SymbolContext &sc, - std::vector &imported_modules) override; + std::vector &imported_modules, + std::vector &module_includes) override; size_t ParseBlocksRecursive(lldb_private::Function &func) override; Index: lldb/source/Plugins/SymbolFile/PDB/SymbolFilePDB.cpp =================================================================== --- lldb/source/Plugins/SymbolFile/PDB/SymbolFilePDB.cpp +++ lldb/source/Plugins/SymbolFile/PDB/SymbolFilePDB.cpp @@ -377,7 +377,8 @@ bool SymbolFilePDB::ParseImportedModules( const lldb_private::SymbolContext &sc, - std::vector &imported_modules) { + std::vector &imported_modules, + std::vector &module_includes) { // PDB does not yet support module debug info return false; } Index: lldb/source/Plugins/SymbolFile/Symtab/SymbolFileSymtab.h =================================================================== --- lldb/source/Plugins/SymbolFile/Symtab/SymbolFileSymtab.h +++ lldb/source/Plugins/SymbolFile/Symtab/SymbolFileSymtab.h @@ -63,7 +63,8 @@ bool ParseImportedModules( const lldb_private::SymbolContext &sc, - std::vector &imported_modules) override; + std::vector &imported_modules, + std::vector &module_includes) override; size_t ParseBlocksRecursive(lldb_private::Function &func) override; Index: lldb/source/Plugins/SymbolFile/Symtab/SymbolFileSymtab.cpp =================================================================== --- lldb/source/Plugins/SymbolFile/Symtab/SymbolFileSymtab.cpp +++ lldb/source/Plugins/SymbolFile/Symtab/SymbolFileSymtab.cpp @@ -218,7 +218,8 @@ } bool SymbolFileSymtab::ParseImportedModules( - const SymbolContext &sc, std::vector &imported_modules) { + const SymbolContext &sc, std::vector &imported_modules, + std::vector &module_includes) { return false; } Index: lldb/source/Symbol/CompileUnit.cpp =================================================================== --- lldb/source/Symbol/CompileUnit.cpp +++ lldb/source/Symbol/CompileUnit.cpp @@ -390,19 +390,29 @@ m_variables = variables; } -const std::vector &CompileUnit::GetImportedModules() { +void CompileUnit::UpdateModules() { if (m_imported_modules.empty() && m_flags.IsClear(flagsParsedImportedModules)) { m_flags.Set(flagsParsedImportedModules); if (SymbolVendor *symbol_vendor = GetModule()->GetSymbolVendor()) { SymbolContext sc; CalculateSymbolContext(&sc); - symbol_vendor->ParseImportedModules(sc, m_imported_modules); + symbol_vendor->ParseImportedModules(sc, m_imported_modules, + m_module_includes); } } +} + +const std::vector &CompileUnit::GetImportedModules() { + UpdateModules(); return m_imported_modules; } +const std::vector &CompileUnit::GetModuleIncludes() { + UpdateModules(); + return m_module_includes; +} + FileSpecList &CompileUnit::GetSupportFiles() { if (m_support_files.GetSize() == 0) { if (m_flags.IsClear(flagsParsedSupportFiles)) { Index: lldb/source/Symbol/SymbolVendor.cpp =================================================================== --- lldb/source/Symbol/SymbolVendor.cpp +++ lldb/source/Symbol/SymbolVendor.cpp @@ -185,12 +185,15 @@ } bool SymbolVendor::ParseImportedModules( - const SymbolContext &sc, std::vector &imported_modules) { + const SymbolContext &sc, + std::vector &imported_modules, + std::vector &module_includes) { ModuleSP module_sp(GetModule()); if (module_sp) { std::lock_guard guard(module_sp->GetMutex()); if (m_sym_file_ap.get()) - return m_sym_file_ap->ParseImportedModules(sc, imported_modules); + return m_sym_file_ap->ParseImportedModules(sc, imported_modules, + module_includes); } return false; } Index: lldb/source/Target/Target.cpp =================================================================== --- lldb/source/Target/Target.cpp +++ lldb/source/Target/Target.cpp @@ -3313,6 +3313,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, @@ -3451,6 +3454,7 @@ ePropertyDebugFileSearchPaths, ePropertyClangModuleSearchPaths, ePropertyAutoImportClangModules, + ePropertyImportStdModule, ePropertyAutoApplyFixIts, ePropertyNotifyAboutFixIts, ePropertySaveObjects, @@ -3873,6 +3877,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(