[lldb] Decouple importing the std C++ module from the way the program is compiled
authorRaphael Isemann <teemperor@gmail.com>
Tue, 24 Sep 2019 10:08:18 +0000 (10:08 +0000)
committerRaphael Isemann <teemperor@gmail.com>
Tue, 24 Sep 2019 10:08:18 +0000 (10:08 +0000)
Summary:
At the moment, when trying to import the `std` module in LLDB, we look at the imported modules used in the compiled program
and try to infer the Clang configuration we need from the DWARF module-import. That was the initial idea but turned out to
cause a few problems or inconveniences:

* It requires that users compile their programs with C++ modules. Given how experimental C++ modules are makes this feature inaccessible
for many users. Also it means that people can't just get the benefits of this feature for free when we activate it by default
(and we can't just close all the associated bug reports).
* Relying on DWARF's imported module tags (that are only emitted by default on macOS) means this can only be used when using DWARF (and with -glldb on Linux).
* We essentially hardcoded the C standard library paths on some platforms (Linux) or just couldn't support this feature on other platforms (macOS).

This patch drops the whole idea of looking at the imported module DWARF tags and instead just uses the support files of the compilation unit.
If we look at the support files and see file paths that indicate where the C standard library and libc++ are, we can just create the module
configuration this information. This fixes all the problems above which means we can enable all the tests now on Linux, macOS and with other debug information
than what we currently had. The only debug information specific code is now the iteration over external type module when -gmodules is used (as `std` and also the
`Darwin` module are their own external type module with their own files).

The meat of this patch is the CppModuleConfiguration which looks at the file paths from the compilation unit and then figures out the include paths
based on those paths. It's quite conservative in that it only enables modules if we find a single C library and single libc++ library. It's still missing some
test mode where we try to compile an expression before we actually activate the config for the user (which probably also needs some caching mechanism),
but for now it works and makes the feature usable.

Reviewers: aprantl, shafik, jdoerfert

Reviewed By: aprantl

Subscribers: mgorny, abidh, JDevlieghere, lldb-commits

Tags: #c_modules_in_lldb, #lldb

Differential Revision: https://reviews.llvm.org/D67760

llvm-svn: 372716

62 files changed:
lldb/include/lldb/Symbol/CompileUnit.h
lldb/include/lldb/Symbol/SymbolFile.h
lldb/include/lldb/Target/Platform.h
lldb/packages/Python/lldbsuite/test/commands/expression/import-std-module/basic/Makefile
lldb/packages/Python/lldbsuite/test/commands/expression/import-std-module/basic/TestImportStdModule.py
lldb/packages/Python/lldbsuite/test/commands/expression/import-std-module/conflicts/Makefile
lldb/packages/Python/lldbsuite/test/commands/expression/import-std-module/conflicts/TestStdModuleWithConflicts.py
lldb/packages/Python/lldbsuite/test/commands/expression/import-std-module/deque-basic/Makefile
lldb/packages/Python/lldbsuite/test/commands/expression/import-std-module/deque-basic/TestBasicDeque.py
lldb/packages/Python/lldbsuite/test/commands/expression/import-std-module/deque-dbg-info-content/Makefile
lldb/packages/Python/lldbsuite/test/commands/expression/import-std-module/deque-dbg-info-content/TestDbgInfoContentDeque.py
lldb/packages/Python/lldbsuite/test/commands/expression/import-std-module/forward_list-basic/Makefile
lldb/packages/Python/lldbsuite/test/commands/expression/import-std-module/forward_list-basic/TestBasicForwardList.py
lldb/packages/Python/lldbsuite/test/commands/expression/import-std-module/forward_list-dbg-info-content/Makefile
lldb/packages/Python/lldbsuite/test/commands/expression/import-std-module/forward_list-dbg-info-content/TestDbgInfoContentForwardList.py
lldb/packages/Python/lldbsuite/test/commands/expression/import-std-module/list-basic/Makefile
lldb/packages/Python/lldbsuite/test/commands/expression/import-std-module/list-basic/TestBasicList.py
lldb/packages/Python/lldbsuite/test/commands/expression/import-std-module/list-dbg-info-content/Makefile
lldb/packages/Python/lldbsuite/test/commands/expression/import-std-module/list-dbg-info-content/TestDbgInfoContentList.py
lldb/packages/Python/lldbsuite/test/commands/expression/import-std-module/no-std-module/Makefile
lldb/packages/Python/lldbsuite/test/commands/expression/import-std-module/no-std-module/TestMissingStdModule.py
lldb/packages/Python/lldbsuite/test/commands/expression/import-std-module/queue/Makefile
lldb/packages/Python/lldbsuite/test/commands/expression/import-std-module/queue/TestQueue.py
lldb/packages/Python/lldbsuite/test/commands/expression/import-std-module/shared_ptr-dbg-info-content/Makefile
lldb/packages/Python/lldbsuite/test/commands/expression/import-std-module/shared_ptr-dbg-info-content/TestSharedPtrDbgInfoContent.py
lldb/packages/Python/lldbsuite/test/commands/expression/import-std-module/shared_ptr/Makefile
lldb/packages/Python/lldbsuite/test/commands/expression/import-std-module/shared_ptr/TestSharedPtr.py
lldb/packages/Python/lldbsuite/test/commands/expression/import-std-module/stack/Makefile
lldb/packages/Python/lldbsuite/test/commands/expression/import-std-module/stack/TestStack.py
lldb/packages/Python/lldbsuite/test/commands/expression/import-std-module/sysroot/Makefile
lldb/packages/Python/lldbsuite/test/commands/expression/import-std-module/sysroot/TestStdModuleSysroot.py
lldb/packages/Python/lldbsuite/test/commands/expression/import-std-module/unique_ptr-dbg-info-content/Makefile
lldb/packages/Python/lldbsuite/test/commands/expression/import-std-module/unique_ptr-dbg-info-content/TestUniquePtrDbgInfoContent.py
lldb/packages/Python/lldbsuite/test/commands/expression/import-std-module/unique_ptr/Makefile
lldb/packages/Python/lldbsuite/test/commands/expression/import-std-module/unique_ptr/TestUniquePtr.py
lldb/packages/Python/lldbsuite/test/commands/expression/import-std-module/vector-basic/Makefile
lldb/packages/Python/lldbsuite/test/commands/expression/import-std-module/vector-basic/TestBasicVector.py
lldb/packages/Python/lldbsuite/test/commands/expression/import-std-module/vector-bool/Makefile
lldb/packages/Python/lldbsuite/test/commands/expression/import-std-module/vector-bool/TestBoolVector.py
lldb/packages/Python/lldbsuite/test/commands/expression/import-std-module/vector-dbg-info-content/Makefile
lldb/packages/Python/lldbsuite/test/commands/expression/import-std-module/vector-dbg-info-content/TestDbgInfoContentVector.py
lldb/packages/Python/lldbsuite/test/commands/expression/import-std-module/vector-of-vectors/Makefile
lldb/packages/Python/lldbsuite/test/commands/expression/import-std-module/vector-of-vectors/TestVectorOfVectors.py
lldb/packages/Python/lldbsuite/test/commands/expression/import-std-module/weak_ptr-dbg-info-content/Makefile
lldb/packages/Python/lldbsuite/test/commands/expression/import-std-module/weak_ptr-dbg-info-content/TestDbgInfoContentWeakPtr.py
lldb/packages/Python/lldbsuite/test/commands/expression/import-std-module/weak_ptr/Makefile
lldb/packages/Python/lldbsuite/test/commands/expression/import-std-module/weak_ptr/TestWeakPtr.py
lldb/source/Plugins/ExpressionParser/Clang/CMakeLists.txt
lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionParser.cpp
lldb/source/Plugins/ExpressionParser/Clang/ClangUserExpression.cpp
lldb/source/Plugins/ExpressionParser/Clang/ClangUserExpression.h
lldb/source/Plugins/ExpressionParser/Clang/CppModuleConfiguration.cpp [new file with mode: 0644]
lldb/source/Plugins/ExpressionParser/Clang/CppModuleConfiguration.h [new file with mode: 0644]
lldb/source/Plugins/Platform/Linux/PlatformLinux.cpp
lldb/source/Plugins/Platform/Linux/PlatformLinux.h
lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp
lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h
lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.cpp
lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.h
lldb/source/Symbol/CompileUnit.cpp
lldb/unittests/Expression/CMakeLists.txt
lldb/unittests/Expression/CppModuleConfigurationTest.cpp [new file with mode: 0644]

index dc4896c92383fb97e75b9d2c8e2689f0c3628191..d132c367b9990409181dedb4fba94854918b8d4b 100644 (file)
@@ -225,6 +225,14 @@ public:
 
   DebugMacros *GetDebugMacros();
 
+  /// Apply a lambda to each external lldb::Module referenced by this
+  /// compilation unit. Recursively also descends into the referenced external
+  /// modules of any encountered compilation unit.
+  ///
+  /// \param[in] lambda
+  ///     The lambda that should be applied to every module.
+  void ForEachExternalModule(llvm::function_ref<void(lldb::ModuleSP)> f);
+
   /// Get the compile unit's support file list.
   ///
   /// The support file list is used by the line table, and any objects that
index ce8da8014c5c81f59ab4c2322d97499cee1af633..d92b456f8625f2e7747aaa9b1fde591b3a576fd9 100644 (file)
@@ -122,6 +122,9 @@ public:
   virtual size_t ParseFunctions(CompileUnit &comp_unit) = 0;
   virtual bool ParseLineTable(CompileUnit &comp_unit) = 0;
   virtual bool ParseDebugMacros(CompileUnit &comp_unit) = 0;
+  virtual void
+  ForEachExternalModule(CompileUnit &comp_unit,
+                        llvm::function_ref<void(lldb::ModuleSP)> f) {}
   virtual bool ParseSupportFiles(CompileUnit &comp_unit,
                                  FileSpecList &support_files) = 0;
   virtual size_t ParseTypes(CompileUnit &comp_unit) = 0;
index 3ba58c0ec741e889f2cf68f7e28b47a1cb6b97cb..ba9e97be1b68e8da7961d208dd289ba6c664f04f 100644 (file)
@@ -257,19 +257,6 @@ public:
 
   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 UserIDResolver &GetUserIDResolver() = 0;
 
   /// Locate a file for a platform.
index 12e489d83b2bc63947f3e8f0667498c6322448f0..f938f7428468ab888407a2a538cb5a723213792f 100644 (file)
@@ -1,4 +1,3 @@
 USE_LIBCPP := 1
-CXXFLAGS += $(MANDATORY_CXXMODULE_BUILD_CFLAGS)
 CXX_SOURCES := main.cpp
 include Makefile.rules
index 19e02ca84c9922d8151eb7340acaf0b9248db5b2..de0d796505e77d691d457054d0b9d2eff9e10725 100644 (file)
@@ -11,12 +11,8 @@ class ImportStdModule(TestBase):
 
     mydir = TestBase.compute_mydir(__file__)
 
-    # 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()
 
@@ -34,12 +30,8 @@ class ImportStdModule(TestBase):
         self.expect("expr char char_a = 'b'; char char_b = 'a'; std::swap(char_a, char_b); char_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()
 
index 12e489d83b2bc63947f3e8f0667498c6322448f0..f938f7428468ab888407a2a538cb5a723213792f 100644 (file)
@@ -1,4 +1,3 @@
 USE_LIBCPP := 1
-CXXFLAGS += $(MANDATORY_CXXMODULE_BUILD_CFLAGS)
 CXX_SOURCES := main.cpp
 include Makefile.rules
index 2abaece27fed77a92d1dc6ee94f0a6564d06775c..ad5322baab00759b18da17a6ec431bf9701f99b6 100644 (file)
@@ -16,12 +16,8 @@ class TestImportStdModuleConflicts(TestBase):
 
     mydir = TestBase.compute_mydir(__file__)
 
-    # 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()
 
index 12e489d83b2bc63947f3e8f0667498c6322448f0..f938f7428468ab888407a2a538cb5a723213792f 100644 (file)
@@ -1,4 +1,3 @@
 USE_LIBCPP := 1
-CXXFLAGS += $(MANDATORY_CXXMODULE_BUILD_CFLAGS)
 CXX_SOURCES := main.cpp
 include Makefile.rules
index 0ede19e835088283c09f70dc57d1a23bc1c45afc..4615d249bb1b788521291e751a9b919201acd3b8 100644 (file)
@@ -10,12 +10,8 @@ class TestBasicDeque(TestBase):
 
     mydir = TestBase.compute_mydir(__file__)
 
-    # 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()
 
index eeea34819dd8c10508ca5e1dbe5c73b8d69b38c6..cde74af8e1f437e891f11f0130f61a85b97cec40 100644 (file)
@@ -10,12 +10,8 @@ class TestDbgInfoContentDeque(TestBase):
 
     mydir = TestBase.compute_mydir(__file__)
 
-    # 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()
 
index 528d555e0af7dff046067efd71c9522cb4f6322f..9e45bf23c120c7be769bc8023ccb61c45995d917 100644 (file)
@@ -10,12 +10,8 @@ class TestBasicForwardList(TestBase):
 
     mydir = TestBase.compute_mydir(__file__)
 
-    # 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()
 
index a2fc559893068263fcf0709b23686d75423c2eb6..290a0450fc8cd41f3438962fbec710fc4d0c4f4a 100644 (file)
@@ -10,12 +10,8 @@ class TestDbgInfoContentForwardList(TestBase):
 
     mydir = TestBase.compute_mydir(__file__)
 
-    # 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()
 
index 12e489d83b2bc63947f3e8f0667498c6322448f0..f938f7428468ab888407a2a538cb5a723213792f 100644 (file)
@@ -1,4 +1,3 @@
 USE_LIBCPP := 1
-CXXFLAGS += $(MANDATORY_CXXMODULE_BUILD_CFLAGS)
 CXX_SOURCES := main.cpp
 include Makefile.rules
index 154c8ed7bd0a38a276c117302fd39c2f23e301ce..e8dfc88473949c66156f2650832d3c72bdb74920 100644 (file)
@@ -10,12 +10,8 @@ class TestBasicList(TestBase):
 
     mydir = TestBase.compute_mydir(__file__)
 
-    # 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()
 
index 713ec1abb85e1d74f27a20dc0adbe3c7f41c3a84..fa8be7cabee69eaf7769ba00f0c989657f2deea2 100644 (file)
@@ -11,12 +11,8 @@ class TestDbgInfoContentList(TestBase):
 
     mydir = TestBase.compute_mydir(__file__)
 
-    # 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()
 
index 12e489d83b2bc63947f3e8f0667498c6322448f0..f938f7428468ab888407a2a538cb5a723213792f 100644 (file)
@@ -1,4 +1,3 @@
 USE_LIBCPP := 1
-CXXFLAGS += $(MANDATORY_CXXMODULE_BUILD_CFLAGS)
 CXX_SOURCES := main.cpp
 include Makefile.rules
index f4d3d1fd492677b804e9e096c6e9ea9b41c31dd7..83d672fb1fdfb19b0731bcbdce506337975d14b9 100644 (file)
@@ -17,12 +17,8 @@ class STLTestCase(TestBase):
 
     mydir = TestBase.compute_mydir(__file__)
 
-    # 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()
 
index 12e489d83b2bc63947f3e8f0667498c6322448f0..f938f7428468ab888407a2a538cb5a723213792f 100644 (file)
@@ -1,4 +1,3 @@
 USE_LIBCPP := 1
-CXXFLAGS += $(MANDATORY_CXXMODULE_BUILD_CFLAGS)
 CXX_SOURCES := main.cpp
 include Makefile.rules
index aad1b11719bdaf45036ff531b1745a04dbaa6eaf..42e5f8d8c4307ccb7837977586f66e0f2e3c82d0 100644 (file)
@@ -10,12 +10,8 @@ class TestQueue(TestBase):
 
     mydir = TestBase.compute_mydir(__file__)
 
-    # 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()
 
index 028798f72f3c67abbc274f3a78e2f9be32dc8221..ec39651b815097dc1d68a7505725a35b48c82146 100644 (file)
@@ -10,12 +10,8 @@ class TestSharedPtrDbgInfoContent(TestBase):
 
     mydir = TestBase.compute_mydir(__file__)
 
-    # 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()
 
index 12e489d83b2bc63947f3e8f0667498c6322448f0..f938f7428468ab888407a2a538cb5a723213792f 100644 (file)
@@ -1,4 +1,3 @@
 USE_LIBCPP := 1
-CXXFLAGS += $(MANDATORY_CXXMODULE_BUILD_CFLAGS)
 CXX_SOURCES := main.cpp
 include Makefile.rules
index 6f1e7b12d1f86d522b44f1388982fd48a5eb6544..7e05bcca4770fb2c69e44c4a2d30775c84b402c8 100644 (file)
@@ -10,12 +10,8 @@ class TestSharedPtr(TestBase):
 
     mydir = TestBase.compute_mydir(__file__)
 
-    # 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()
 
index 12e489d83b2bc63947f3e8f0667498c6322448f0..f938f7428468ab888407a2a538cb5a723213792f 100644 (file)
@@ -1,4 +1,3 @@
 USE_LIBCPP := 1
-CXXFLAGS += $(MANDATORY_CXXMODULE_BUILD_CFLAGS)
 CXX_SOURCES := main.cpp
 include Makefile.rules
index 1c2e5da54ed921047d2d2dc98db94232bc973fcc..fe19a4e5f1b3ab13bff39daa219a0675c407504b 100644 (file)
@@ -10,12 +10,8 @@ class TestStack(TestBase):
 
     mydir = TestBase.compute_mydir(__file__)
 
-    # 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()
 
index 459a66c5c8b3fe4afcdc3b0ed172a727417b3c2e..4aa2b432f1282ae0bea3f9a0b96f41a5283e5f51 100644 (file)
@@ -3,7 +3,6 @@
 # system headers.
 NO_TEST_COMMON_H := 1
 
-CXXFLAGS += $(MANDATORY_CXXMODULE_BUILD_CFLAGS)
 CXXFLAGS += -I $(SRCDIR)/root/usr/include/c++/include/ -I $(SRCDIR)/root/usr/include/ -nostdinc -nostdinc++ -nostdlib++
 CXX_SOURCES := main.cpp
 include Makefile.rules
index dfd90e80e45283452c307c288fe1cf09977f1ba2..fbf00cddcfa92b5d8014591d4d2dbcbcc0931be4 100644 (file)
@@ -11,11 +11,7 @@ class ImportStdModule(TestBase):
 
     mydir = TestBase.compute_mydir(__file__)
 
-    # FIXME: This should work on more setups, so remove these
-    # skipIf's in the future.
     @skipIf(compiler=no_match("clang"))
-    @skipIf(oslist=no_match(["linux"]))
-    @skipIf(debug_info=no_match(["dwarf"]))
     def test(self):
         self.build()
 
index c321d8022e19fab9f08da8b4d2b4b8e4febe1917..9389de9712869916a4b72a6270fc6521e1abebd0 100644 (file)
@@ -10,12 +10,8 @@ class TestUniquePtrDbgInfoContent(TestBase):
 
     mydir = TestBase.compute_mydir(__file__)
 
-    # 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()
 
index 12e489d83b2bc63947f3e8f0667498c6322448f0..f938f7428468ab888407a2a538cb5a723213792f 100644 (file)
@@ -1,4 +1,3 @@
 USE_LIBCPP := 1
-CXXFLAGS += $(MANDATORY_CXXMODULE_BUILD_CFLAGS)
 CXX_SOURCES := main.cpp
 include Makefile.rules
index 295ec512327622274ca306b7887062eea52e1f7e..a0e05b2c4202736938c88ab63111736fe3bb93c0 100644 (file)
@@ -10,12 +10,8 @@ class TestUniquePtr(TestBase):
 
     mydir = TestBase.compute_mydir(__file__)
 
-    # 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()
 
index 12e489d83b2bc63947f3e8f0667498c6322448f0..f938f7428468ab888407a2a538cb5a723213792f 100644 (file)
@@ -1,4 +1,3 @@
 USE_LIBCPP := 1
-CXXFLAGS += $(MANDATORY_CXXMODULE_BUILD_CFLAGS)
 CXX_SOURCES := main.cpp
 include Makefile.rules
index deaf8f0cb7538a596d125acba929a964c7d3e9f5..840bacb86b30a746c8745e191dcf78dc4099ebac 100644 (file)
@@ -10,12 +10,8 @@ class TestBasicVector(TestBase):
 
     mydir = TestBase.compute_mydir(__file__)
 
-    # 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()
 
index 12e489d83b2bc63947f3e8f0667498c6322448f0..f938f7428468ab888407a2a538cb5a723213792f 100644 (file)
@@ -1,4 +1,3 @@
 USE_LIBCPP := 1
-CXXFLAGS += $(MANDATORY_CXXMODULE_BUILD_CFLAGS)
 CXX_SOURCES := main.cpp
 include Makefile.rules
index 0ab52695fafa4b80e0f38a8640f91269584baa58..45a2507f77651ffcb67f8a5426b193663c4adc99 100644 (file)
@@ -10,12 +10,8 @@ class TestBoolVector(TestBase):
 
     mydir = TestBase.compute_mydir(__file__)
 
-    # 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()
 
index c89265f2cd337a18752b3f4a91ca28ebbf3068e1..4d51f1ba3b602016dc4df05301c3f14e1fa84f78 100644 (file)
@@ -11,12 +11,8 @@ class TestDbgInfoContentVector(TestBase):
 
     mydir = TestBase.compute_mydir(__file__)
 
-    # 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()
 
index 2b82e1a039a3577a047e62905d8604d2af3cd66e..feaeac9be5e071552c0750dd46363f64e050f706 100644 (file)
@@ -10,12 +10,8 @@ class TestVectorOfVectors(TestBase):
 
     mydir = TestBase.compute_mydir(__file__)
 
-    # 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()
 
index 0acd54e803cf8953bef1ddfa9781f321692b0650..5ed30312ea76cf81453ce577bbeafe0400edf598 100644 (file)
@@ -10,12 +10,8 @@ class TestDbgInfoContentWeakPtr(TestBase):
 
     mydir = TestBase.compute_mydir(__file__)
 
-    # 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()
 
index 12e489d83b2bc63947f3e8f0667498c6322448f0..f938f7428468ab888407a2a538cb5a723213792f 100644 (file)
@@ -1,4 +1,3 @@
 USE_LIBCPP := 1
-CXXFLAGS += $(MANDATORY_CXXMODULE_BUILD_CFLAGS)
 CXX_SOURCES := main.cpp
 include Makefile.rules
index 6b8b9ceb7aab48aee383005504c7073a07c6811c..76241740b682c6bbb0b74a88e9f1941725d75d67 100644 (file)
@@ -10,12 +10,8 @@ class TestSharedPtr(TestBase):
 
     mydir = TestBase.compute_mydir(__file__)
 
-    # 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()
 
index 99d50c4007f92ec065f565a2ca34f3aaa6dfe34c..b114ce7762decfd1a7532f7fdcc573a6ca5709bd 100644 (file)
@@ -19,6 +19,7 @@ add_lldb_library(lldbPluginExpressionParserClang PLUGIN
   ClangPersistentVariables.cpp
   ClangUserExpression.cpp
   ClangUtilityFunction.cpp
+  CppModuleConfiguration.cpp
   IRForTarget.cpp
   IRDynamicChecks.cpp
 
index cb0e985b8cf5f153ed5fc3221daf053d20ba7ec4..593348fbeb015e7fe17318ee384dd802a0298220 100644 (file)
@@ -241,38 +241,23 @@ static void SetupModuleHeaderPaths(CompilerInstance *compiler,
   search_opts.ModuleCachePath = module_cache.str();
   LLDB_LOG(log, "Using module cache path: {0}", 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);
-
-    LLDB_LOG(log, "Added resource include dir: {0}", resource_include);
-  }
+  search_opts.ResourceDir = GetClangResourceDir().GetPath();
 
   search_opts.ImplicitModuleMaps = true;
-
-  std::vector<std::string> system_include_directories =
-      target_sp->GetPlatform()->GetSystemIncludeDirectories(
-          lldb::eLanguageTypeC_plus_plus);
-
-  for (const std::string &include_dir : system_include_directories) {
-    search_opts.AddPath(include_dir, frontend::System, false, true);
-
-    LLDB_LOG(log, "Added system include dir: {0}", include_dir);
-  }
 }
 
 //===----------------------------------------------------------------------===//
 // Implementation of ClangExpressionParser
 //===----------------------------------------------------------------------===//
 
-ClangExpressionParser::ClangExpressionParser(ExecutionContextScope *exe_scope, Expression &expr,
-    bool generate_debug_info, std::vector<std::string> include_directories, std::string filename)
+ClangExpressionParser::ClangExpressionParser(
+    ExecutionContextScope *exe_scope, Expression &expr,
+    bool generate_debug_info, std::vector<std::string> include_directories,
+    std::string filename)
     : ExpressionParser(exe_scope, expr, generate_debug_info), m_compiler(),
       m_pp_callbacks(nullptr),
-      m_include_directories(std::move(include_directories)), m_filename(std::move(filename)) {
+      m_include_directories(std::move(include_directories)),
+      m_filename(std::move(filename)) {
   Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS));
 
   // We can't compile expressions without a target.  So if the exe_scope is
index ae95c6879819eab549b5c647e3fba19bce1debe7..813d03c71941715b6fc9b1e8833fa88181923c31 100644 (file)
@@ -25,6 +25,7 @@
 #include "ClangExpressionParser.h"
 #include "ClangModulesDeclVendor.h"
 #include "ClangPersistentVariables.h"
+#include "CppModuleConfiguration.h"
 
 #include "lldb/Core/Debugger.h"
 #include "lldb/Core/Module.h"
@@ -433,48 +434,59 @@ static bool SupportsCxxModuleImport(lldb::LanguageType language) {
   }
 }
 
-std::vector<std::string>
-ClangUserExpression::GetModulesToImport(ExecutionContext &exe_ctx) {
+/// Utility method that puts a message into the expression log and
+/// returns an invalid module configuration.
+static CppModuleConfiguration LogConfigError(const std::string &msg) {
   Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS));
+  LLDB_LOG(log, "[C++ module config] {0}", msg);
+  return CppModuleConfiguration();
+}
 
-  if (!SupportsCxxModuleImport(Language()))
-    return {};
+CppModuleConfiguration GetModuleConfig(lldb::LanguageType language,
+                                       ExecutionContext &exe_ctx) {
+  // Don't do anything if this is not a C++ module configuration.
+  if (!SupportsCxxModuleImport(language))
+    return LogConfigError("Language doesn't support C++ modules");
 
   Target *target = exe_ctx.GetTargetPtr();
-  if (!target || !target->GetEnableImportStdModule())
-    return {};
+  if (!target)
+    return LogConfigError("No target");
+
+  if (!target->GetEnableImportStdModule())
+    return LogConfigError("Importing std module not enabled in settings");
 
   StackFrame *frame = exe_ctx.GetFramePtr();
   if (!frame)
-    return {};
+    return LogConfigError("No frame");
 
   Block *block = frame->GetFrameBlock();
   if (!block)
-    return {};
+    return LogConfigError("No block");
 
   SymbolContext sc;
   block->CalculateSymbolContext(&sc);
   if (!sc.comp_unit)
-    return {};
-
-  if (log) {
-    for (const SourceModule &m : sc.comp_unit->GetImportedModules()) {
-      LLDB_LOG(log, "Found module in compile unit: {0:$[.]} - include dir: {1}",
-                  llvm::make_range(m.path.begin(), m.path.end()), m.search_path);
+    return LogConfigError("Couldn't calculate symbol context");
+
+  // Build a list of files we need to analyze to build the configuration.
+  FileSpecList files;
+  for (const FileSpec &f : sc.comp_unit->GetSupportFiles())
+    files.AppendIfUnique(f);
+  // We also need to look at external modules in the case of -gmodules as they
+  // contain the support files for libc++ and the C library.
+  sc.comp_unit->ForEachExternalModule([&files](lldb::ModuleSP module) {
+    for (std::size_t i = 0; i < module->GetNumCompileUnits(); ++i) {
+      const FileSpecList &support_files =
+          module->GetCompileUnitAtIndex(i)->GetSupportFiles();
+      for (const FileSpec &f : support_files) {
+        files.AppendIfUnique(f);
+      }
     }
-  }
-
-  for (const SourceModule &m : sc.comp_unit->GetImportedModules())
-    m_include_directories.emplace_back(m.search_path.GetCString());
-
-  // 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() == "std")
-      return {"std"};
-
-  return {};
+  });
+  // Try to create a configuration from the files. If there is no valid
+  // configuration possible with the files, this just returns an invalid
+  // configuration.
+  return CppModuleConfiguration(files);
 }
 
 bool ClangUserExpression::PrepareForParsing(
@@ -502,14 +514,21 @@ bool ClangUserExpression::PrepareForParsing(
 
   SetupDeclVendor(exe_ctx, m_target);
 
-  std::vector<std::string> used_modules = GetModulesToImport(exe_ctx);
-  m_imported_cpp_modules = !used_modules.empty();
+  CppModuleConfiguration module_config = GetModuleConfig(m_language, exe_ctx);
+  llvm::ArrayRef<std::string> imported_modules =
+      module_config.GetImportedModules();
+  m_imported_cpp_modules = !imported_modules.empty();
+  m_include_directories = module_config.GetIncludeDirs();
 
   LLDB_LOG(log, "List of imported modules in expression: {0}",
-           llvm::make_range(used_modules.begin(), used_modules.end()));
+           llvm::make_range(imported_modules.begin(), imported_modules.end()));
+  LLDB_LOG(log, "List of include directories gathered for modules: {0}",
+           llvm::make_range(m_include_directories.begin(),
+                            m_include_directories.end()));
 
   UpdateLanguageForExpr();
-  CreateSourceCode(diagnostic_manager, exe_ctx, used_modules, for_completion);
+  CreateSourceCode(diagnostic_manager, exe_ctx, imported_modules,
+                   for_completion);
   return true;
 }
 
index ac28ac4a3727e6ce018cdd9dd0172a3031f45e31..d94f9cc5e0660421c83ec5fe8f14744c873a5893 100644 (file)
@@ -179,7 +179,6 @@ private:
                     lldb::addr_t struct_address,
                     DiagnosticManager &diagnostic_manager) override;
 
-  std::vector<std::string> GetModulesToImport(ExecutionContext &exe_ctx);
   void CreateSourceCode(DiagnosticManager &diagnostic_manager,
                         ExecutionContext &exe_ctx,
                         std::vector<std::string> modules_to_import,
diff --git a/lldb/source/Plugins/ExpressionParser/Clang/CppModuleConfiguration.cpp b/lldb/source/Plugins/ExpressionParser/Clang/CppModuleConfiguration.cpp
new file mode 100644 (file)
index 0000000..47c0345
--- /dev/null
@@ -0,0 +1,82 @@
+//===-- CppModuleConfiguration.cpp ----------------------------------------===//
+//
+// 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 "CppModuleConfiguration.h"
+
+#include "ClangHost.h"
+#include "lldb/Host/FileSystem.h"
+
+using namespace lldb_private;
+
+bool CppModuleConfiguration::SetOncePath::TrySet(llvm::StringRef path) {
+  // Setting for the first time always works.
+  if (m_first) {
+    m_path = path.str();
+    m_valid = true;
+    m_first = false;
+    return true;
+  }
+  // Changing the path to the same value is fine.
+  if (m_path == path)
+    return true;
+
+  // Changing the path after it was already set is not allowed.
+  m_valid = false;
+  return false;
+}
+
+bool CppModuleConfiguration::analyzeFile(const FileSpec &f) {
+  llvm::SmallString<256> dir_buffer = f.GetDirectory().GetStringRef();
+  // Convert to posix style so that we can use '/'.
+  llvm::sys::path::native(dir_buffer, llvm::sys::path::Style::posix);
+  llvm::StringRef posix_dir(dir_buffer);
+
+  // Check for /c++/vX/ that is used by libc++.
+  static llvm::Regex libcpp_regex(R"regex(/c[+][+]/v[0-9]/)regex");
+  if (libcpp_regex.match(f.GetPath())) {
+    // Strip away libc++'s /experimental directory if there is one.
+    posix_dir.consume_back("/experimental");
+    return m_std_inc.TrySet(posix_dir);
+  }
+
+  // Check for /usr/include. On Linux this might be /usr/include/bits, so
+  // we should remove that '/bits' suffix to get the actual include directory.
+  if (posix_dir.endswith("/usr/include/bits"))
+    posix_dir.consume_back("/bits");
+  if (posix_dir.endswith("/usr/include"))
+    return m_c_inc.TrySet(posix_dir);
+
+  // File wasn't interesting, continue analyzing.
+  return true;
+}
+
+bool CppModuleConfiguration::hasValidConfig() {
+  // We all these include directories to have a valid usable configuration.
+  return m_c_inc.Valid() && m_std_inc.Valid();
+}
+
+CppModuleConfiguration::CppModuleConfiguration(
+    const FileSpecList &support_files) {
+  // Analyze all files we were given to build the configuration.
+  bool error = !std::all_of(support_files.begin(), support_files.end(),
+                            std::bind(&CppModuleConfiguration::analyzeFile,
+                                      this, std::placeholders::_1));
+  // If we have a valid configuration at this point, set the
+  // include directories and module list that should be used.
+  if (!error && hasValidConfig()) {
+    // Calculate the resource directory for LLDB.
+    llvm::SmallString<256> resource_dir;
+    llvm::sys::path::append(resource_dir, GetClangResourceDir().GetPath(),
+                            "include");
+    m_resource_inc = resource_dir.str();
+
+    // This order matches the way Clang orders these directories.
+    m_include_dirs = {m_std_inc.Get(), m_resource_inc, m_c_inc.Get()};
+    m_imported_modules = {"std"};
+  }
+}
diff --git a/lldb/source/Plugins/ExpressionParser/Clang/CppModuleConfiguration.h b/lldb/source/Plugins/ExpressionParser/Clang/CppModuleConfiguration.h
new file mode 100644 (file)
index 0000000..8e892e3
--- /dev/null
@@ -0,0 +1,84 @@
+//===-- CppModuleConfiguration.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_CppModuleConfiguration_h_
+#define liblldb_CppModuleConfiguration_h_
+
+#include <lldb/Core/FileSpecList.h>
+#include <llvm/Support/Regex.h>
+
+namespace lldb_private {
+
+/// A Clang configuration when importing C++ modules.
+///
+/// Includes a list of include paths that should be used when importing
+/// and a list of modules that can be imported. Currently only used when
+/// importing the 'std' module and its dependencies.
+class CppModuleConfiguration {
+  /// Utility class for a path that can only be set once.
+  class SetOncePath {
+    std::string m_path;
+    bool m_valid = false;
+    /// True iff this path hasn't been set yet.
+    bool m_first = true;
+
+  public:
+    /// Try setting the path. Returns true if the path was set and false if
+    /// the path was already set.
+    LLVM_NODISCARD bool TrySet(llvm::StringRef path);
+    /// Return the path if there is one.
+    std::string Get() const {
+      assert(m_valid && "Called Get() on an invalid SetOncePath?");
+      return m_path;
+    }
+    /// Returns true iff this path was set exactly once so far.
+    bool Valid() const { return m_valid; }
+  };
+
+  /// If valid, the include path used for the std module.
+  SetOncePath m_std_inc;
+  /// If valid, the include path to the C library (e.g. /usr/include).
+  SetOncePath m_c_inc;
+  /// The Clang resource include path for this configuration.
+  std::string m_resource_inc;
+
+  std::vector<std::string> m_include_dirs;
+  std::vector<std::string> m_imported_modules;
+
+  /// Analyze a given source file to build the current configuration.
+  /// Returns false iff there was a fatal error that makes analyzing any
+  /// further files pointless as the configuration is now invalid.
+  bool analyzeFile(const FileSpec &f);
+
+public:
+  /// Creates a configuraiton by analyzing the given list of used source files.
+  ///
+  /// Currently only looks at the used paths and doesn't actually access the
+  /// files on the disk.
+  explicit CppModuleConfiguration(const FileSpecList &support_files);
+  /// Creates an empty and invalid configuration.
+  CppModuleConfiguration() {}
+
+  /// Returns true iff this is a valid configuration that can be used to
+  /// load and compile modules.
+  bool hasValidConfig();
+
+  /// Returns a list of include directories that should be used when using this
+  /// configuration (e.g. {"/usr/include", "/usr/include/c++/v1"}).
+  llvm::ArrayRef<std::string> GetIncludeDirs() const { return m_include_dirs; }
+
+  /// Returns a list of (top level) modules that should be imported when using
+  /// this configuration (e.g. {"std"}).
+  llvm::ArrayRef<std::string> GetImportedModules() const {
+    return m_imported_modules;
+  }
+};
+
+} // namespace lldb_private
+
+#endif
index 52d72f5e6fdd77ee41b4c25b829b67b1fc963bcd..f17fb8f048334de01d91d3f38ce4e1d816e397e8 100644 (file)
@@ -260,25 +260,6 @@ bool PlatformLinux::CanDebugProcess() {
   }
 }
 
-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 a843558f1df81667d43cedf61e49a8bfc69b0a45..67d211ac82433b84fb829e6055bd487961762bab 100644 (file)
@@ -48,9 +48,6 @@ public:
 
   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 08b9434f68e47cba9130d17d9e97755f8e573e96..2177cbc895745b84d27f531b48af70cdd31f630a 100644 (file)
@@ -845,6 +845,18 @@ size_t SymbolFileDWARF::ParseFunctions(CompileUnit &comp_unit) {
   return functions_added;
 }
 
+void SymbolFileDWARF::ForEachExternalModule(
+    CompileUnit &comp_unit, llvm::function_ref<void(ModuleSP)> f) {
+  UpdateExternalModuleListIfNeeded();
+
+  for (auto &p : m_external_type_modules) {
+    ModuleSP module = p.second;
+    f(module);
+    for (std::size_t i = 0; i < module->GetNumCompileUnits(); ++i)
+      module->GetCompileUnitAtIndex(i)->ForEachExternalModule(f);
+  }
+}
+
 bool SymbolFileDWARF::ParseSupportFiles(CompileUnit &comp_unit,
                                         FileSpecList &support_files) {
   if (!comp_unit.GetLineTable())
index 2a1eb8ab9abebb56df4c9ffa2fb5def22336f30d..6825c7b085b975b1901825d7e380a84d702b69bb 100644 (file)
@@ -105,6 +105,10 @@ public:
 
   bool ParseDebugMacros(lldb_private::CompileUnit &comp_unit) override;
 
+  void
+  ForEachExternalModule(lldb_private::CompileUnit &comp_unit,
+                        llvm::function_ref<void(lldb::ModuleSP)> f) override;
+
   bool ParseSupportFiles(lldb_private::CompileUnit &comp_unit,
                          lldb_private::FileSpecList &support_files) override;
 
index 77e2fcf887c46d31e7a5bd013443b1b5a5937bf5..b079855ccbd9ac67e3bc55cb51b20cb9c99c54d5 100644 (file)
@@ -652,6 +652,14 @@ bool SymbolFileDWARFDebugMap::ParseDebugMacros(CompileUnit &comp_unit) {
   return false;
 }
 
+void SymbolFileDWARFDebugMap::ForEachExternalModule(
+    CompileUnit &comp_unit, llvm::function_ref<void(ModuleSP)> f) {
+  std::lock_guard<std::recursive_mutex> guard(GetModuleMutex());
+  SymbolFileDWARF *oso_dwarf = GetSymbolFile(comp_unit);
+  if (oso_dwarf)
+    oso_dwarf->ForEachExternalModule(comp_unit, f);
+}
+
 bool SymbolFileDWARFDebugMap::ParseSupportFiles(CompileUnit &comp_unit,
                                                 FileSpecList &support_files) {
   std::lock_guard<std::recursive_mutex> guard(GetModuleMutex());
index 0b4f96b203645f787e55c7c2b2cf2d0d22d22bd4..2cc830485b2ab9e188c70e905a1c749f24b3cdc8 100644 (file)
@@ -53,6 +53,10 @@ public:
 
   bool ParseDebugMacros(lldb_private::CompileUnit &comp_unit) override;
 
+  void
+  ForEachExternalModule(lldb_private::CompileUnit &comp_unit,
+                        llvm::function_ref<void(lldb::ModuleSP)> f) override;
+
   bool ParseSupportFiles(lldb_private::CompileUnit &comp_unit,
                          lldb_private::FileSpecList &support_files) override;
 
index dc50e832aa62c3c7ae65cb6f0d5451892f715d9f..41086d2df3df16b8746c9216ae7483fce6254e66 100644 (file)
@@ -353,6 +353,11 @@ const std::vector<SourceModule> &CompileUnit::GetImportedModules() {
   return m_imported_modules;
 }
 
+void CompileUnit::ForEachExternalModule(llvm::function_ref<void(ModuleSP)> f) {
+  if (SymbolFile *symfile = GetModule()->GetSymbolFile())
+    symfile->ForEachExternalModule(*this, f);
+}
+
 const FileSpecList &CompileUnit::GetSupportFiles() {
   if (m_support_files.GetSize() == 0) {
     if (m_flags.IsClear(flagsParsedSupportFiles)) {
index 2dcc8d3d65587e1d5a642e7c1ed28dc9a2b21457..3408b278b0bfa0509257a4e458e1c9a0f27fe2e0 100644 (file)
@@ -2,6 +2,7 @@ add_lldb_unittest(ExpressionTests
   ClangParserTest.cpp
   DiagnosticManagerTest.cpp
   DWARFExpressionTest.cpp
+  CppModuleConfigurationTest.cpp
 
   LINK_LIBS
     lldbCore
diff --git a/lldb/unittests/Expression/CppModuleConfigurationTest.cpp b/lldb/unittests/Expression/CppModuleConfigurationTest.cpp
new file mode 100644 (file)
index 0000000..135ce47
--- /dev/null
@@ -0,0 +1,168 @@
+//===-- CppModuleConfigurationTest.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 "Plugins/ExpressionParser/Clang/CppModuleConfiguration.h"
+#include "Plugins/ExpressionParser/Clang/ClangHost.h"
+#include "lldb/Host/FileSystem.h"
+#include "lldb/Host/HostInfo.h"
+
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+
+using namespace lldb_private;
+
+namespace {
+struct CppModuleConfigurationTest : public testing::Test {
+  static void SetUpTestCase() {
+    // Getting the resource directory uses those subsystems, so we should
+    // initialize them.
+    FileSystem::Initialize();
+    HostInfo::Initialize();
+  }
+  static void TearDownTestCase() {
+    HostInfo::Terminate();
+    FileSystem::Terminate();
+  }
+};
+} // namespace
+
+/// Returns the Clang resource include directory.
+static std::string ResourceInc() {
+  llvm::SmallString<256> resource_dir;
+  llvm::sys::path::append(resource_dir, GetClangResourceDir().GetPath(),
+                          "include");
+  return resource_dir.str().str();
+}
+
+/// Utility function turningn a list of paths into a FileSpecList.
+static FileSpecList makeFiles(llvm::ArrayRef<std::string> paths) {
+  FileSpecList result;
+  for (const std::string &path : paths)
+    result.Append(FileSpec(path));
+  return result;
+}
+
+TEST_F(CppModuleConfigurationTest, Linux) {
+  // Test the average Linux configuration.
+  std::string libcpp = "/usr/include/c++/v1";
+  std::string usr = "/usr/include";
+  CppModuleConfiguration config(
+      makeFiles({usr + "/bits/types.h", libcpp + "/vector"}));
+  EXPECT_THAT(config.GetImportedModules(), testing::ElementsAre("std"));
+  EXPECT_THAT(config.GetIncludeDirs(),
+              testing::ElementsAre(libcpp, ResourceInc(), usr));
+}
+
+TEST_F(CppModuleConfigurationTest, Sysroot) {
+  // Test that having a sysroot for the whole system works fine.
+  std::string libcpp = "/home/user/sysroot/usr/include/c++/v1";
+  std::string usr = "/home/user/sysroot/usr/include";
+  CppModuleConfiguration config(
+      makeFiles({usr + "/bits/types.h", libcpp + "/vector"}));
+  EXPECT_THAT(config.GetImportedModules(), testing::ElementsAre("std"));
+  EXPECT_THAT(config.GetIncludeDirs(),
+              testing::ElementsAre(libcpp, ResourceInc(), usr));
+}
+
+TEST_F(CppModuleConfigurationTest, LinuxLocalLibCpp) {
+  // Test that a locally build libc++ is detected.
+  std::string libcpp = "/home/user/llvm-build/include/c++/v1";
+  std::string usr = "/usr/include";
+  CppModuleConfiguration config(
+      makeFiles({usr + "/bits/types.h", libcpp + "/vector"}));
+  EXPECT_THAT(config.GetImportedModules(), testing::ElementsAre("std"));
+  EXPECT_THAT(config.GetIncludeDirs(),
+              testing::ElementsAre(libcpp, ResourceInc(), usr));
+}
+
+TEST_F(CppModuleConfigurationTest, UnrelatedLibrary) {
+  // Test that having an unrelated library in /usr/include doesn't break.
+  std::string libcpp = "/home/user/llvm-build/include/c++/v1";
+  std::string usr = "/usr/include";
+  CppModuleConfiguration config(makeFiles(
+      {usr + "/bits/types.h", libcpp + "/vector", usr + "/boost/vector"}));
+  EXPECT_THAT(config.GetImportedModules(), testing::ElementsAre("std"));
+  EXPECT_THAT(config.GetIncludeDirs(),
+              testing::ElementsAre(libcpp, ResourceInc(), usr));
+}
+
+TEST_F(CppModuleConfigurationTest, Xcode) {
+  // Test detection of libc++ coming from Xcode with generic platform names.
+  std::string p = "/Applications/Xcode.app/Contents/Developer/";
+  std::string libcpp = p + "Toolchains/B.xctoolchain/usr/include/c++/v1";
+  std::string usr =
+      p + "Platforms/A.platform/Developer/SDKs/OSVers.sdk/usr/include";
+  CppModuleConfiguration config(
+      makeFiles({libcpp + "/unordered_map", usr + "/stdio.h"}));
+  EXPECT_THAT(config.GetImportedModules(), testing::ElementsAre("std"));
+  EXPECT_THAT(config.GetIncludeDirs(),
+              testing::ElementsAre(libcpp, ResourceInc(), usr));
+}
+
+TEST_F(CppModuleConfigurationTest, LibCppV2) {
+  // Test that a "v2" of libc++ is still correctly detected.
+  CppModuleConfiguration config(
+      makeFiles({"/usr/include/bits/types.h", "/usr/include/c++/v2/vector"}));
+  EXPECT_THAT(config.GetImportedModules(), testing::ElementsAre("std"));
+  EXPECT_THAT(config.GetIncludeDirs(),
+              testing::ElementsAre("/usr/include/c++/v2", ResourceInc(),
+                                   "/usr/include"));
+}
+
+TEST_F(CppModuleConfigurationTest, UnknownLibCppFile) {
+  // Test that having some unknown file in the libc++ path doesn't break
+  // anything.
+  CppModuleConfiguration config(makeFiles(
+      {"/usr/include/bits/types.h", "/usr/include/c++/v1/non_existing_file"}));
+  EXPECT_THAT(config.GetImportedModules(), testing::ElementsAre("std"));
+  EXPECT_THAT(config.GetIncludeDirs(),
+              testing::ElementsAre("/usr/include/c++/v1", ResourceInc(),
+                                   "/usr/include"));
+}
+
+TEST_F(CppModuleConfigurationTest, MissingUsrInclude) {
+  // Test that we don't load 'std' if we can't find the C standard library.
+  CppModuleConfiguration config(makeFiles({"/usr/include/c++/v1/vector"}));
+  EXPECT_THAT(config.GetImportedModules(), testing::ElementsAre());
+  EXPECT_THAT(config.GetIncludeDirs(), testing::ElementsAre());
+}
+
+TEST_F(CppModuleConfigurationTest, MissingLibCpp) {
+  // Test that we don't load 'std' if we don't have a libc++.
+  CppModuleConfiguration config(makeFiles({"/usr/include/bits/types.h"}));
+  EXPECT_THAT(config.GetImportedModules(), testing::ElementsAre());
+  EXPECT_THAT(config.GetIncludeDirs(), testing::ElementsAre());
+}
+
+TEST_F(CppModuleConfigurationTest, IgnoreLibStdCpp) {
+  // Test that we don't do anything bad when we encounter libstdc++ paths.
+  CppModuleConfiguration config(makeFiles(
+      {"/usr/include/bits/types.h", "/usr/include/c++/8.0.1/vector"}));
+  EXPECT_THAT(config.GetImportedModules(), testing::ElementsAre());
+  EXPECT_THAT(config.GetIncludeDirs(), testing::ElementsAre());
+}
+
+TEST_F(CppModuleConfigurationTest, AmbiguousCLib) {
+  // Test that we don't do anything when we are not sure where the
+  // right C standard library is.
+  CppModuleConfiguration config(
+      makeFiles({"/usr/include/bits/types.h", "/usr/include/c++/v1/vector",
+                 "/sysroot/usr/include/bits/types.h"}));
+  EXPECT_THAT(config.GetImportedModules(), testing::ElementsAre());
+  EXPECT_THAT(config.GetIncludeDirs(), testing::ElementsAre());
+}
+
+TEST_F(CppModuleConfigurationTest, AmbiguousLibCpp) {
+  // Test that we don't do anything when we are not sure where the
+  // right libc++ is.
+  CppModuleConfiguration config(
+      makeFiles({"/usr/include/bits/types.h", "/usr/include/c++/v1/vector",
+                 "/usr/include/c++/v2/vector"}));
+  EXPECT_THAT(config.GetImportedModules(), testing::ElementsAre());
+  EXPECT_THAT(config.GetIncludeDirs(), testing::ElementsAre());
+}