[libcxx-commits] [libcxx] ab46648 - [libcxx] adds an include-what-you-use (IWYU) mapping file

Christopher Di Bella via libcxx-commits libcxx-commits at lists.llvm.org
Mon Nov 21 17:20:46 PST 2022


Author: Christopher Di Bella
Date: 2022-11-22T01:09:49Z
New Revision: ab46648082814c41f84b77a08a86403fa202f4e8

URL: https://github.com/llvm/llvm-project/commit/ab46648082814c41f84b77a08a86403fa202f4e8
DIFF: https://github.com/llvm/llvm-project/commit/ab46648082814c41f84b77a08a86403fa202f4e8.diff

LOG: [libcxx] adds an include-what-you-use (IWYU) mapping file

This makes it possible for programmers to run IWYU and get more accurate
standard library inclusions. Prior to this commit, the following program
would be transformed thusly:

```cpp
// Before
 #include <algorithm>
 #include <vector>

void f() {
  auto v = std::vector{0, 1};
  std::find(std::ranges::begin(v), std::ranges::end(v), 0);
}
```

```cpp
// After
 #include <__algorithm/find.h>
 #include <__ranges/access.h>
 #include <vector>
...
```

There are two ways to fix this issue: to use [comment pragmas](https://github.com/include-what-you-use/include-what-you-use/blob/master/docs/IWYUPragmas.md)
on every private include, or to write a canonical [mapping file](https://github.com/include-what-you-use/include-what-you-use/blob/master/docs/IWYUMappings.md)
that provides the tool with a manual on how libc++ is laid out. Due to
the complexity of libc++, this commit opts for the latter, to maximise
correctness and minimise developer burden.

To mimimise developer updates to the file, it makes use of wildcards
that match everything within listed subdirectories. A script has also
been added to ensure that the mapping is always fresh in CI, and makes
the process a single step.

Finally, documentation has been added to inform users that IWYU is
supported, and what they need to do in order to leverage the mapping
file.

Closes #56937.

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

Added: 
    libcxx/include/libcxx.imp
    libcxx/utils/generate_iwyu_mapping.py

Modified: 
    libcxx/docs/UsingLibcxx.rst
    libcxx/include/CMakeLists.txt
    libcxx/test/libcxx/lint/lint_headers.sh.py
    libcxx/utils/CMakeLists.txt
    libcxx/utils/generate_header_tests.py

Removed: 
    


################################################################################
diff  --git a/libcxx/docs/UsingLibcxx.rst b/libcxx/docs/UsingLibcxx.rst
index e6425d8c7c8b4..5cf608e0b3b13 100644
--- a/libcxx/docs/UsingLibcxx.rst
+++ b/libcxx/docs/UsingLibcxx.rst
@@ -122,6 +122,21 @@ provide pretty-printers itself. Those can be used as:
         -ex "python register_libcxx_printer_loader()" \
         <args>
 
+.. _include-what-you-use:
+
+include-what-you-use (IWYU)
+===========================
+
+libc++ provides an IWYU `mapping file <https://github.com/include-what-you-use/include-what-you-use/blob/master/docs/IWYUMappings.md>`,
+which drastically improves the accuracy of the tool when using libc++. To use the mapping file with
+IWYU, you should run the tool like so:
+
+.. code-block:: bash
+
+  $ include-what-you-use -Xiwyu /path/to/libcxx/include/libcxx.imp file.cpp
+
+If you would prefer to not use that flag, then you can replace ``/path/to/include-what-you-use/share/libcxx.imp```
+file with the libc++-provided ``libcxx.imp`` file.
 
 .. _assertions-mode:
 

diff  --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index d7bf369d7da5c..1ae1441191678 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -779,6 +779,7 @@ set(files
   istream
   iterator
   latch
+  libcxx.imp
   limits
   limits.h
   list

diff  --git a/libcxx/include/libcxx.imp b/libcxx/include/libcxx.imp
new file mode 100644
index 0000000000000..07fc9985c7003
--- /dev/null
+++ b/libcxx/include/libcxx.imp
@@ -0,0 +1,45 @@
+[
+  { include: [ "<__bits>", "private", "<bits>", "public" ] },
+  { include: [ "<__hash_table>", "private", "<unordered_map>", "public" ] },
+  { include: [ "<__hash_table>", "private", "<unordered_set>", "public" ] },
+  { include: [ "<__locale>", "private", "<locale>", "public" ] },
+  { include: [ "<__node_handle>", "private", "<map>", "public" ] },
+  { include: [ "<__node_handle>", "private", "<set>", "public" ] },
+  { include: [ "<__node_handle>", "private", "<unordered_map>", "public" ] },
+  { include: [ "<__node_handle>", "private", "<unordered_set>", "public" ] },
+  { include: [ "<__split_buffer>", "private", "<deque>", "public" ] },
+  { include: [ "<__split_buffer>", "private", "<vector>", "public" ] },
+  { include: [ "<__std_stream>", "private", "<iostream>", "public" ] },
+  { include: [ "<__threading_support>", "private", "<atomic>", "public" ] },
+  { include: [ "<__threading_support>", "private", "<mutex>", "public" ] },
+  { include: [ "<__threading_support>", "private", "<semaphore>", "public" ] },
+  { include: [ "<__threading_support>", "private", "<thread>", "public" ] },
+  { include: [ "<__tree>", "private", "<map>", "public" ] },
+  { include: [ "<__tree>", "private", "<set>", "public" ] },
+  { include: [ "@<__algorithm/.*>", "private", "<algorithm>", "public" ] },
+  { include: [ "@<__bit/.*>", "private", "<bit>", "public" ] },
+  { include: [ "@<__charconv/.*>", "private", "<charconv>", "public" ] },
+  { include: [ "@<__chrono/.*>", "private", "<chrono>", "public" ] },
+  { include: [ "@<__compare/.*>", "private", "<compare>", "public" ] },
+  { include: [ "@<__concepts/.*>", "private", "<concepts>", "public" ] },
+  { include: [ "@<__coroutine/.*>", "private", "<coroutine>", "public" ] },
+  { include: [ "@<__debug_utils/.*>", "private", "<debug_utils>", "public" ] },
+  { include: [ "@<__filesystem/.*>", "private", "<filesystem>", "public" ] },
+  { include: [ "@<__format/.*>", "private", "<format>", "public" ] },
+  { include: [ "@<__functional/.*>", "private", "<functional>", "public" ] },
+  { include: [ "@<__fwd/.*>", "private", "<fwd>", "public" ] },
+  { include: [ "@<__ios/.*>", "private", "<ios>", "public" ] },
+  { include: [ "@<__iterator/.*>", "private", "<iterator>", "public" ] },
+  { include: [ "@<__memory/.*>", "private", "<memory>", "public" ] },
+  { include: [ "@<__memory_resource/.*>", "private", "<memory_resource>", "public" ] },
+  { include: [ "@<__numeric/.*>", "private", "<numeric>", "public" ] },
+  { include: [ "@<__random/.*>", "private", "<random>", "public" ] },
+  { include: [ "@<__ranges/.*>", "private", "<ranges>", "public" ] },
+  { include: [ "@<__string/.*>", "private", "<string>", "public" ] },
+  { include: [ "@<__support/.*>", "private", "<support>", "public" ] },
+  { include: [ "@<__thread/.*>", "private", "<thread>", "public" ] },
+  { include: [ "@<__tuple/.*>", "private", "<tuple>", "public" ] },
+  { include: [ "@<__type_traits/.*>", "private", "<type_traits>", "public" ] },
+  { include: [ "@<__utility/.*>", "private", "<utility>", "public" ] },
+  { include: [ "@<__variant/.*>", "private", "<variant>", "public" ] },
+]

diff  --git a/libcxx/test/libcxx/lint/lint_headers.sh.py b/libcxx/test/libcxx/lint/lint_headers.sh.py
index 8805ee6252ae1..cb6054cd46e04 100644
--- a/libcxx/test/libcxx/lint/lint_headers.sh.py
+++ b/libcxx/test/libcxx/lint/lint_headers.sh.py
@@ -14,6 +14,7 @@ def exclude_from_consideration(path):
         path.endswith('.modulemap.in') or
         os.path.basename(path) == '__config' or
         os.path.basename(path) == '__config_site.in' or
+        os.path.basename(path) == 'libcxx.imp' or
         not os.path.isfile(path)
     )
 

diff  --git a/libcxx/utils/CMakeLists.txt b/libcxx/utils/CMakeLists.txt
index f8f30b4fa7c48..bb7d3491f28f6 100644
--- a/libcxx/utils/CMakeLists.txt
+++ b/libcxx/utils/CMakeLists.txt
@@ -32,6 +32,12 @@ add_custom_target(libcxx-generate-escaped-output-table
         "${LIBCXX_SOURCE_DIR}/include/__format/escaped_output_table.h"
     COMMENT "Generate the escaped output header")
 
+add_custom_target(libcxx-generate-iwyu-mapping
+    COMMAND
+        "${Python3_EXECUTABLE}"
+        "${LIBCXX_SOURCE_DIR}/utils/generate_iwyu_mapping.py"
+    COMMENT "Generate the mapping file for include-what-you-use")
+
 add_custom_target(libcxx-generate-files
     DEPENDS libcxx-generate-public-header-transitive-inclusion-tests
             libcxx-generate-public-header-tests
@@ -39,4 +45,5 @@ add_custom_target(libcxx-generate-files
             libcxx-generate-extended-grapheme-cluster-tables
             libcxx-generate-extended-grapheme-cluster-tests
             libcxx-generate-escaped-output-table
+            libcxx-generate-iwyu-mapping
     COMMENT "Create all the auto-generated files in libc++ and its tests.")

diff  --git a/libcxx/utils/generate_header_tests.py b/libcxx/utils/generate_header_tests.py
index eb602909eea9a..7e920ff1b9bc5 100755
--- a/libcxx/utils/generate_header_tests.py
+++ b/libcxx/utils/generate_header_tests.py
@@ -113,7 +113,8 @@ def produce(test_file, variables):
 
 def is_header(file):
     """Returns whether the given file is a header (i.e. not a directory or the modulemap file)."""
-    return not file.is_dir() and not file.name == 'module.modulemap.in'
+    return not file.is_dir() and not file.name == 'module.modulemap.in' and file.name != 'libcxx.imp'
+
 
 def main():
     monorepo_root = pathlib.Path(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))))

diff  --git a/libcxx/utils/generate_iwyu_mapping.py b/libcxx/utils/generate_iwyu_mapping.py
new file mode 100644
index 0000000000000..5887f8abcffb5
--- /dev/null
+++ b/libcxx/utils/generate_iwyu_mapping.py
@@ -0,0 +1,80 @@
+#!/usr/bin/env python
+
+import os, pathlib, sys
+
+def generate(private, public):
+    return f'{{ include: [ "{private}", "private", "<{public}>", "public" ] }}'
+
+
+def panic(file):
+    print(f'========== {__file__} error ==========', file=sys.stderr)
+    print(f'\tFile \'{file}\' is a top-level detail header without a mapping', file=sys.stderr)
+    sys.exit(1)
+
+
+def generate_map(include):
+    detail_files = []
+    detail_directories = []
+    c_headers = []
+
+    for i in include.iterdir():
+        if i.is_dir() and i.name.startswith('__'):
+            detail_directories.append(f'{i.name}')
+            continue
+
+        if i.name.startswith('__'):
+            detail_files.append(i.name)
+            continue
+
+        if i.name.endswith('.h'):
+            c_headers.append(i.name)
+
+    result = []
+    for i in detail_directories:
+        result.append(f'{generate(f"@<{i}/.*>", i[2:])},')
+
+    for i in detail_files:
+        public = []
+        match i:
+            case '__assert': continue
+            case '__availability': continue
+            case '__bit_reference': continue
+            case '__bits': public = ['bits']
+            case '__bsd_locale_defaults.h': continue
+            case '__bsd_locale_fallbacks.h': continue
+            case '__config_site.in': continue
+            case '__config': continue
+            case '__debug': continue
+            case '__errc': continue
+            case '__hash_table': public = ['unordered_map', 'unordered_set']
+            case '__locale': public = ['locale']
+            case '__mbstate_t.h': continue
+            case '__mutex_base': continue
+            case '__node_handle': public = ['map', 'set', 'unordered_map', 'unordered_set']
+            case '__split_buffer': public = ['deque', 'vector']
+            case '__std_stream': public = ['iostream']
+            case '__threading_support': public = ['atomic', 'mutex', 'semaphore', 'thread']
+            case '__tree': public = ['map', 'set']
+            case '__undef_macros': continue
+            case '__verbose_abort': continue
+            case _: panic()
+
+        for p in public:
+            result.append(f'{generate(f"<{i}>", p)},')
+
+    result.sort()
+    return result
+
+def main():
+    monorepo_root = pathlib.Path(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))))
+    assert(monorepo_root.exists())
+    include = pathlib.Path(os.path.join(monorepo_root, 'libcxx', 'include'))
+
+    mapping = generate_map(include)
+    data = '[\n  ' + '\n  '.join(mapping) + '\n]\n'
+    with open(f'{include}/libcxx.imp', 'w') as f:
+        f.write(data)
+
+
+if __name__ == '__main__':
+    main()


        


More information about the libcxx-commits mailing list