teemperor updated this revision to Diff 189086.
teemperor added a comment.

- Added sysroot test.
- Moved build flags into `MANDATORY_CXXMODULE_BUILD_FLAGS` make variable.


CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D58125/new/

https://reviews.llvm.org/D58125

Files:
  lldb/include/lldb/Expression/ExpressionSourceCode.h
  lldb/include/lldb/Target/Platform.h
  lldb/include/lldb/Target/Target.h
  
lldb/packages/Python/lldbsuite/test/expression_command/import-std-module/basic/Makefile
  
lldb/packages/Python/lldbsuite/test/expression_command/import-std-module/basic/TestImportStdModule.py
  
lldb/packages/Python/lldbsuite/test/expression_command/import-std-module/basic/main.cpp
  
lldb/packages/Python/lldbsuite/test/expression_command/import-std-module/conflicts/Makefile
  
lldb/packages/Python/lldbsuite/test/expression_command/import-std-module/conflicts/TestStdModuleWithConflicts.py
  
lldb/packages/Python/lldbsuite/test/expression_command/import-std-module/conflicts/main.cpp
  
lldb/packages/Python/lldbsuite/test/expression_command/import-std-module/no-std-module/Makefile
  
lldb/packages/Python/lldbsuite/test/expression_command/import-std-module/no-std-module/TestMissingStdModule.py
  
lldb/packages/Python/lldbsuite/test/expression_command/import-std-module/no-std-module/main.cpp
  
lldb/packages/Python/lldbsuite/test/expression_command/import-std-module/sysroot/Makefile
  
lldb/packages/Python/lldbsuite/test/expression_command/import-std-module/sysroot/TestStdModuleSysroot.py
  
lldb/packages/Python/lldbsuite/test/expression_command/import-std-module/sysroot/main.cpp
  
lldb/packages/Python/lldbsuite/test/expression_command/import-std-module/sysroot/root/usr/include/c++/include/algorithm
  
lldb/packages/Python/lldbsuite/test/expression_command/import-std-module/sysroot/root/usr/include/c++/include/module.modulemap
  
lldb/packages/Python/lldbsuite/test/expression_command/import-std-module/sysroot/root/usr/include/libc_header.h
  lldb/packages/Python/lldbsuite/test/make/Makefile.rules
  lldb/source/Expression/ExpressionSourceCode.cpp
  lldb/source/Plugins/ExpressionParser/Clang/ASTUtils.cpp
  lldb/source/Plugins/ExpressionParser/Clang/ASTUtils.h
  lldb/source/Plugins/ExpressionParser/Clang/CMakeLists.txt
  lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionParser.cpp
  lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionParser.h
  lldb/source/Plugins/ExpressionParser/Clang/ClangUserExpression.cpp
  lldb/source/Plugins/ExpressionParser/Clang/ClangUserExpression.h
  lldb/source/Plugins/Platform/Linux/PlatformLinux.cpp
  lldb/source/Plugins/Platform/Linux/PlatformLinux.h
  lldb/source/Target/Target.cpp

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(
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<std::string>
+  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<std::string>
+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/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<std::string> GetModulesToImport(ExecutionContext &exe_ctx);
   void UpdateLanguageForExpr(DiagnosticManager &diagnostic_manager,
-                             ExecutionContext &exe_ctx);
+                             ExecutionContext &exe_ctx,
+                             std::vector<std::string> 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<ConstString> 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<std::string> 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<std::string>
+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<std::string> 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/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<ConstString> include_directories = {});
 
   //------------------------------------------------------------------
   /// Destructor
@@ -187,6 +192,8 @@
   LLDBPreprocessorCallbacks *m_pp_callbacks; ///< Called when the preprocessor
                                              ///encounters module imports
   std::unique_ptr<ClangASTContext> m_ast_context;
+
+  std::vector<ConstString> 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<clang::TextDiagnosticBuffer> m_passthrough;
 };
 
+static void
+SetupModuleHeaderPaths(CompilerInstance *compiler,
+                       std::vector<ConstString> 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<std::string> 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<ConstString> 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<ClangUserExpression *>(&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<ClangExpressionHelper>(m_expr.GetTypeSystemHelper());
-  ClangExpressionDeclMap *decl_map = type_system_helper->DeclMap();
-
-  if (decl_map) {
-    llvm::IntrusiveRefCntPtr<clang::ExternalASTSource> 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<ClangExpressionHelper>(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<clang::ASTConsumer> 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<ExternalASTSource> 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<Sema> 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/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/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<ASTSourceDescriptor>
+  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<bool(clang::Decl::Kind)> IsKindWeWant,
+      llvm::SmallVectorImpl<clang::Decl *> &Result) override {
+    m_Source->FindExternalLexicalDecls(DC, IsKindWeWant, Result);
+  }
+
+  void
+  FindFileRegionDecls(clang::FileID File, unsigned Offset, unsigned Length,
+                      llvm::SmallVectorImpl<clang::Decl *> &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<const clang::FieldDecl *, uint64_t> &FieldOffsets,
+      llvm::DenseMap<const clang::CXXRecordDecl *, clang::CharUnits>
+          &BaseOffsets,
+      llvm::DenseMap<const clang::CXXRecordDecl *, clang::CharUnits>
+          &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<clang::SemaConsumer>(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<clang::ExternalSemaSource *, 2> 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<bool(clang::Decl::Kind)> IsKindWeWant,
+      llvm::SmallVectorImpl<clang::Decl *> &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<clang::Decl *> &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<const clang::FieldDecl *, uint64_t> &FieldOffsets,
+      llvm::DenseMap<const clang::CXXRecordDecl *, clang::CharUnits>
+          &BaseOffsets,
+      llvm::DenseMap<const clang::CXXRecordDecl *, clang::CharUnits>
+          &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<clang::NamespaceDecl *> &Namespaces) override {
+    for (size_t i = 0; i < Sources.size(); ++i)
+      Sources[i]->ReadKnownNamespaces(Namespaces);
+  }
+
+  void ReadUndefinedButUsed(
+      llvm::MapVector<clang::NamedDecl *, clang::SourceLocation> &Undefined)
+      override {
+    for (size_t i = 0; i < Sources.size(); ++i)
+      Sources[i]->ReadUndefinedButUsed(Undefined);
+  }
+
+  void ReadMismatchingDeleteExpressions(
+      llvm::MapVector<clang::FieldDecl *,
+                      llvm::SmallVector<std::pair<clang::SourceLocation, bool>,
+                                        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<clang::VarDecl *> &Defs) override {
+    for (size_t i = 0; i < Sources.size(); ++i)
+      Sources[i]->ReadTentativeDefinitions(Defs);
+  }
+
+  void ReadUnusedFileScopedDecls(
+      llvm::SmallVectorImpl<const clang::DeclaratorDecl *> &Decls) override {
+    for (size_t i = 0; i < Sources.size(); ++i)
+      Sources[i]->ReadUnusedFileScopedDecls(Decls);
+  }
+
+  void ReadDelegatingConstructors(
+      llvm::SmallVectorImpl<clang::CXXConstructorDecl *> &Decls) override {
+    for (size_t i = 0; i < Sources.size(); ++i)
+      Sources[i]->ReadDelegatingConstructors(Decls);
+  }
+
+  void ReadExtVectorDecls(
+      llvm::SmallVectorImpl<clang::TypedefNameDecl *> &Decls) override {
+    for (size_t i = 0; i < Sources.size(); ++i)
+      Sources[i]->ReadExtVectorDecls(Decls);
+  }
+
+  void ReadUnusedLocalTypedefNameCandidates(
+      llvm::SmallSetVector<const clang::TypedefNameDecl *, 4> &Decls) override {
+    for (size_t i = 0; i < Sources.size(); ++i)
+      Sources[i]->ReadUnusedLocalTypedefNameCandidates(Decls);
+  }
+
+  void ReadReferencedSelectors(
+      llvm::SmallVectorImpl<std::pair<clang::Selector, clang::SourceLocation>>
+          &Sels) override {
+    for (size_t i = 0; i < Sources.size(); ++i)
+      Sources[i]->ReadReferencedSelectors(Sels);
+  }
+
+  void ReadWeakUndeclaredIdentifiers(
+      llvm::SmallVectorImpl<std::pair<clang::IdentifierInfo *, clang::WeakInfo>>
+          &WI) override {
+    for (size_t i = 0; i < Sources.size(); ++i)
+      Sources[i]->ReadWeakUndeclaredIdentifiers(WI);
+  }
+
+  void ReadUsedVTables(
+      llvm::SmallVectorImpl<clang::ExternalVTableUse> &VTables) override {
+    for (size_t i = 0; i < Sources.size(); ++i)
+      Sources[i]->ReadUsedVTables(VTables);
+  }
+
+  void ReadPendingInstantiations(
+      llvm::SmallVectorImpl<
+          std::pair<clang::ValueDecl *, clang::SourceLocation>> &Pending)
+      override {
+    for (size_t i = 0; i < Sources.size(); ++i)
+      Sources[i]->ReadPendingInstantiations(Pending);
+  }
+
+  void ReadLateParsedTemplates(
+      llvm::MapVector<const clang::FunctionDecl *,
+                      std::unique_ptr<clang::LateParsedTemplate>> &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/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<std::string> 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/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
@@ -283,6 +286,9 @@
 endif
 
 MANDATORY_MODULE_BUILD_CFLAGS := -fmodules -gmodules -fmodules-cache-path=$(CLANG_MODULE_CACHE_DIR)
+# Build flags for building with C++ modules.
+# -glldb is necessary for emitting information about what modules were imported.
+MANDATORY_CXXMODULE_BUILD_CFLAGS := -fmodules -fcxx-modules -fmodules-cache-path=$(CLANG_MODULE_CACHE_DIR) -glldb -fimplicit-module-maps
 
 ifeq "$(OS)" "Darwin"
 	MANDATORY_MODULE_BUILD_CFLAGS += -fcxx-modules
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/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/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/main.cpp
===================================================================
--- /dev/null
+++ lldb/packages/Python/lldbsuite/test/expression_command/import-std-module/sysroot/main.cpp
@@ -0,0 +1,6 @@
+#include <algorithm>
+
+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/TestStdModuleSysroot.py
===================================================================
--- /dev/null
+++ lldb/packages/Python/lldbsuite/test/expression_command/import-std-module/sysroot/TestStdModuleSysroot.py
@@ -0,0 +1,46 @@
+"""
+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.
+    @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")
+
+        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/Makefile
===================================================================
--- /dev/null
+++ lldb/packages/Python/lldbsuite/test/expression_command/import-std-module/sysroot/Makefile
@@ -0,0 +1,6 @@
+NO_INC_DIRS := 1
+LEVEL = ../../../make
+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/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/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/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/conflicts/main.cpp
===================================================================
--- /dev/null
+++ lldb/packages/Python/lldbsuite/test/expression_command/import-std-module/conflicts/main.cpp
@@ -0,0 +1,10 @@
+#include <cstdlib>
+#include <utility>
+
+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/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/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/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 <iostream>
+
+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/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/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/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/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<std::string>
+  GetSystemIncludeDirectories(lldb::LanguageType lang) {
+    return {};
+  }
+
   virtual const char *GetUserName(uint32_t uid);
 
   virtual const char *GetGroupName(uint32_t gid);
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 <string>
+#include <vector>
 
 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<std::string> 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
_______________________________________________
lldb-commits mailing list
lldb-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits

Reply via email to