[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