1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
|
#!/usr/bin/env python
import os
import re
import shutil
from pathlib import Path
def get_libcxx_paths():
utils_path = os.path.dirname(os.path.abspath(__file__))
script_name = os.path.basename(__file__)
assert os.path.exists(utils_path)
src_root = os.path.dirname(utils_path)
include_path = os.path.join(src_root, 'include')
assert os.path.exists(include_path)
detail_header_test_root = os.path.join(src_root, 'test', 'libcxx',
'diagnostics', 'detail.headers')
assert os.path.exists(detail_header_test_root)
shutil.rmtree(detail_header_test_root)
Path(f'{detail_header_test_root}').mkdir()
assert os.path.exists(detail_header_test_root)
return script_name, include_path, detail_header_test_root
script_name, include_path, detail_header_test_root = get_libcxx_paths()
def generate_header_test(header, include_instead):
return f'''// -*- 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
//
//===----------------------------------------------------------------------===//
// UNSUPPORTED: modules-build
// UNSUPPORTED: clang-11, clang-12, clang-13
// UNSUPPORTED: apple-clang-11, apple-clang-12, apple-clang-13
// UNSUPPORTED: gcc-11
// UNSUPPORTED: libcpp-has-no-localization, libcpp-has-no-threads
// ADDITIONAL_COMPILE_FLAGS: -U_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER
// WARNING: This test was generated by '{script_name}'
// and should not be edited manually.
#include <{header}>
// expected-error@-1 {{{{header '<{header}>' is an implementation detail; #include {include_instead} instead}}}}
'''
def generate_module_test(header):
return f'''// -*- 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
//
//===----------------------------------------------------------------------===//
// REQUIRES: modules-build
// WARNING: This test was generated by '{script_name}'
// and should not be edited manually.
// expected-error@*:* {{{{use of private header from outside its module: '{header}'}}}}
#include <{header}>
'''
def relative_path(path):
return path.as_posix()[len(include_path + '/'):]
def is_still_public(path):
rp = relative_path(path)
return not rp.startswith('__support') and rp not in [
"__assert", "__bsd_locale_defaults.h", "__bsd_locale_fallbacks.h", "__config",
"__config_site.in", "__debug", "__hash_table",
"__libcpp_version", "__threading_support", "__tree", "__undef_macros"
]
def find_header_name(header, directory):
"""Returns part of the diagnostic for `#pragma clang include_instead`. This
usually matches the subdirectory the header lives in (e.g. a header in
`__algorithm` will return "'<algorithm>'"), but some headers are
special-cased.
"""
# Most of the special-cased headers are in the top-level include directory
# (and don't have a point of reference for us to hook on to), but any
# sub-level header that is exported by multiple top-level headers (e.g.
# __compare/compare_three_way.h) is also included in this module map, as the
# diagnostic needs to include more than our simple heuristic.
header_map = {
'bit_reference': "either '<bitset>' or '<vector>'",
'bits': "one of {'<algorithm>', '<bit>', '<bitset>', '<numeric>', '<random>', '<unordered_map>', '<unordered_set>', '<vector>'}",
'hash_table': "either '<unordered_map>' or '<unordered_set>'",
'locale': "'<locale>'",
'mutex_base': "either '<mutex>' or '<shared_mutex>'",
'node_handle': "one of {'<map>', '<set>', '<unordered_map>', '<unordered_set>'}",
'split_buffer': "either '<vector>' or '<deque>'",
'std_stream': "'<streambuf>'",
'string': "'<string>'",
'threading_support': "one of {'<atomic>', '<mutex>', '<semaphore>', '<thread>'}",
'tree': "either '<map>' or '<set>'",
'tuple': "either '<tuple>' or '<utility>'",
}
return header_map[header] if header in header_map else f"'<{directory[:-1]}>'"
def main():
paths = [
relative_path(p) for p in Path(include_path).rglob('*')
if relative_path(p).startswith('__') and not p.is_dir()
and is_still_public(p)
]
for path in paths:
path_with_subdir = re.search(r'__(\w+)/(\w+)', path)
directory = path_with_subdir.group(1) + '/' if path_with_subdir else ""
file = path_with_subdir.group(2) if path_with_subdir else path[2:]
Path(f'{detail_header_test_root}/{directory}').mkdir(exist_ok=True)
assert os.path.exists(f'{detail_header_test_root}/{directory}')
path_to_write = f'{detail_header_test_root}/{directory}{file}.header.verify.cpp'
include_instead = find_header_name(file, directory)
if include_instead != "'<>'":
with open(path_to_write, 'w') as f:
f.write(generate_header_test(path, include_instead))
path_to_write = f'{detail_header_test_root}/{directory}{file}.module.verify.cpp'
with open(path_to_write, 'w') as f:
f.write(generate_module_test(path))
if __name__ == '__main__':
main()
|