xref: /llvm-project/libcxx/utils/generate_iwyu_mapping.py (revision a3ce29f7bb5510131971ac5ccc63132dd48c8dd2)
1#!/usr/bin/env python
2
3import libcxx.header_information
4import os
5import pathlib
6import re
7import typing
8
9def IWYU_mapping(header: str) -> typing.Optional[typing.List[str]]:
10    ignore = [
11        "__debug_utils/.+",
12        "__fwd/get[.]h",
13        "__pstl/.+",
14        "__support/.+",
15        "__utility/private_constructor_tag.h",
16    ]
17    if any(re.match(pattern, header) for pattern in ignore):
18        return None
19    elif header == "__bits":
20        return ["bits"]
21    elif header in ("__bit_reference", "__fwd/bit_reference.h"):
22        return ["bitset", "vector"]
23    elif header == "__hash_table":
24        return ["unordered_map", "unordered_set"]
25    elif header == "__locale":
26        return ["locale"]
27    elif re.match("__locale_dir/.+", header):
28        return ["locale"]
29    elif re.match("__math/.+", header):
30        return ["cmath"]
31    elif header == "__node_handle":
32        return ["map", "set", "unordered_map", "unordered_set"]
33    elif header == "__split_buffer":
34        return ["deque", "vector"]
35    elif re.match("(__thread/support[.]h)|(__thread/support/.+)", header):
36        return ["atomic", "mutex", "semaphore", "thread"]
37    elif header == "__tree":
38        return ["map", "set"]
39    elif header == "__fwd/pair.h":
40        return ["utility"]
41    elif header == "__fwd/subrange.h":
42        return ["ranges"]
43    elif re.match("__fwd/(fstream|ios|istream|ostream|sstream|streambuf)[.]h", header):
44        return ["iosfwd"]
45    # Handle remaining forward declaration headers
46    elif re.match("__fwd/(.+)[.]h", header):
47        return [re.match("__fwd/(.+)[.]h", header).group(1)]
48    # Handle detail headers for things like <__algorithm/foo.h>
49    elif re.match("__(.+?)/.+", header):
50        return [re.match("__(.+?)/.+", header).group(1)]
51    else:
52        return None
53
54def main():
55    mappings = []  # Pairs of (header, public_header)
56    for header in libcxx.header_information.all_headers:
57        public_headers = IWYU_mapping(header)
58        if public_headers is not None:
59            mappings.extend((header, public) for public in public_headers)
60
61    # Validate that we only have valid public header names -- otherwise the mapping above
62    # needs to be updated.
63    for header, public in mappings:
64        if public not in libcxx.header_information.public_headers:
65            raise RuntimeError(f"{header}: Header {public} is not a valid header")
66
67    with open(libcxx.header_information.include / "libcxx.imp", "w") as f:
68        f.write("[\n")
69        for header, public in sorted(mappings):
70            f.write(
71                f'  {{ include: [ "<{header}>", "private", "<{public}>", "public" ] }},\n'
72            )
73        f.write("]\n")
74
75if __name__ == "__main__":
76    main()
77