xref: /llvm-project/libcxx/utils/generate_iwyu_mapping.py (revision 58e476f757775313d8b2649dedb9a7c5d30d8e19)
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        "__support/.+",
14    ]
15    if any(re.match(pattern, header) for pattern in ignore):
16        return None
17    elif header == "__bits":
18        return ["bits"]
19    elif header in ("__bit_reference", "__fwd/bit_reference.h"):
20        return ["bitset", "vector"]
21    elif header == "__hash_table":
22        return ["unordered_map", "unordered_set"]
23    elif header == "__locale":
24        return ["locale"]
25    elif re.match("__locale_dir/.+", header):
26        return ["locale"]
27    elif re.match("__math/.+", header):
28        return ["cmath"]
29    elif header == "__node_handle":
30        return ["map", "set", "unordered_map", "unordered_set"]
31    elif header == "__split_buffer":
32        return ["deque", "vector"]
33    elif re.match("(__thread/support[.]h)|(__thread/support/.+)", header):
34        return ["atomic", "mutex", "semaphore", "thread"]
35    elif header == "__tree":
36        return ["map", "set"]
37    elif header == "__fwd/hash.h":
38        return ["functional"]
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