Skip to content

Commit a6f8e24

Browse files
committed
[libc++] Add a utility for checking the output of commands
1 parent 27a2d3d commit a6f8e24

File tree

8 files changed

+221
-4
lines changed

8 files changed

+221
-4
lines changed

libcxx/test/configs/cmake-bridge.cfg.in

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,3 +31,4 @@ config.substitutions.append(('%{lib-dir}', '@LIBCXX_TESTING_INSTALL_PREFIX@/@LIB
3131
config.substitutions.append(('%{module-dir}', '@LIBCXX_TESTING_INSTALL_PREFIX@/@LIBCXX_INSTALL_MODULES_DIR@'))
3232
config.substitutions.append(('%{test-tools-dir}', '@LIBCXX_TEST_TOOLS_PATH@'))
3333
config.substitutions.append(('%{benchmark_flags}', '-I @LIBCXX_BINARY_DIR@/test/benchmarks/google-benchmark/include -L @LIBCXX_BINARY_DIR@/test/benchmarks/google-benchmark/lib -L @LIBCXX_BINARY_DIR@/test/benchmarks/google-benchmark/lib64 -l benchmark'))
34+
config.substitutions.append(('%{check-output}', os.path.join('@LIBCXX_BINARY_DIR@', 'bin/check_output') + " %s"))
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
// This test is checking the LLVM IR
10+
// REQUIRES: clang
11+
12+
// RUN: %{cxx} %s %{compile_flags} -O3 -c -S -emit-llvm -o - | %{check-output}
13+
14+
#include <algorithm>
15+
16+
int* test1(int* first, int* last, int* out) {
17+
// CHECK: define
18+
// CHECK-SAME: test1
19+
// CHECK: tail call void @llvm.memmove
20+
return std::copy(first, last, out);
21+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
// REQUIRES: has-clang-tidy
10+
11+
// RUN: %{clang-tidy} %s -header-filter=.* --checks='-*,libcpp-cpp-version-check' --load=%{test-tools-dir}/clang_tidy_checks/libcxx-tidy.plugin -- %{compile_flags} -fno-modules 2>&1 | %{check-output}
12+
13+
#include <__config>
14+
15+
// CHECK: warning: _LIBCPP_STD_VER >= version should be used instead of _LIBCPP_STD_VER > prev_version
16+
#if _LIBCPP_STD_VER > 14
17+
#endif
18+
19+
// CHECK: warning: Use _LIBCPP_STD_VER instead of __cplusplus to constrain based on the C++ version
20+
#if __cplusplus >= 201103L
21+
#endif
22+
23+
// CHECK: warning: _LIBCPP_STD_VER >= 11 is always true. Did you mean '#ifndef _LIBCPP_CXX03_LANG'?
24+
#if _LIBCPP_STD_VER >= 11
25+
#endif
26+
27+
// CHECK: warning: Not a valid value for _LIBCPP_STD_VER. Use 14, 17, 20, 23, or 26
28+
#if _LIBCPP_STD_VER >= 12
29+
#endif
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
// This test is checking the LLVM IR
10+
// REQUIRES: clang
11+
12+
// RUN: %{cxx} %s %{compile_flags} -Wno-missing-noreturn -O3 -c -S -emit-llvm -o - | %{check-output}
13+
14+
#include <utility>
15+
16+
void test() {
17+
// CHECK: define dso_local void
18+
// CHECK-SAME: test
19+
// CHECK-NEXT: unreachable
20+
// CHECK-NEXT: }
21+
std::unreachable();
22+
}

libcxx/test/tools/CMakeLists.txt

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ set(LIBCXX_TEST_TOOLS_PATH ${CMAKE_CURRENT_BINARY_DIR} PARENT_SCOPE)
33

44
if(NOT CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
55
message(STATUS "Clang-tidy tests are disabled due to non-clang based compiler.")
6-
return()
6+
else()
7+
add_subdirectory(clang_tidy_checks)
78
endif()
8-
add_subdirectory(clang_tidy_checks)
9+
10+
add_subdirectory(check_output)
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
2+
add_executable(check_output check_output.cpp)
3+
4+
# put the binary into <build>/bin
5+
set_target_properties(check_output PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${LIBCXX_BINARY_DIR}/bin")
6+
7+
# set the language mode
8+
set_target_properties(check_output PROPERTIES
9+
CXX_STANDARD 23
10+
CXX_STANDARD_REQUIRED YES
11+
CXX_EXTENSIONS NO)
12+
13+
target_compile_options(check_output PRIVATE -Werror=missing-prototypes)
14+
15+
add_dependencies(cxx-test-depends check_output)
Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#include <algorithm>
10+
#include <fstream>
11+
#include <functional>
12+
#include <iostream>
13+
#include <iterator>
14+
#include <ranges>
15+
#include <string>
16+
17+
using namespace std::string_view_literals;
18+
19+
enum class Result {
20+
success,
21+
mismatch,
22+
no_match_found,
23+
unknown_matcher,
24+
invalid_use,
25+
};
26+
27+
namespace co {
28+
namespace {
29+
[[noreturn]] void exit(Result result) { std::exit(static_cast<int>(result)); }
30+
31+
[[noreturn]] void print_failure(int line, std::string_view stdin_content, std::string_view matcher) {
32+
std::cout << "Failed to match: `" << matcher << "`\nRemaining data:\n" << stdin_content << '\n';
33+
co::exit(Result::mismatch);
34+
}
35+
36+
bool is_newline(char c) { return c == '\n'; }
37+
38+
bool isblank(char c) { return std::isblank(c); }
39+
40+
bool consume_front(std::string_view& sv, std::string_view start) {
41+
if (!sv.starts_with(start))
42+
return false;
43+
sv.remove_prefix(start.size());
44+
return true;
45+
}
46+
} // namespace
47+
} // namespace co
48+
49+
int main(int argc, char** argv) {
50+
if (argc != 2) {
51+
std::cerr << "check_output has to be used as `<command> | ./check_output %s`\n";
52+
co::exit(Result::invalid_use);
53+
}
54+
55+
std::string file_content_data = [&] {
56+
std::ifstream file(argv[1]);
57+
if (!file) {
58+
std::cerr << "Failed to open file: " << argv[1] << '\n';
59+
co::exit(Result::invalid_use);
60+
}
61+
return std::string{std::istreambuf_iterator<char>{file}, {}};
62+
}();
63+
std::string_view file_content = file_content_data; // Don't copy the data around all the time
64+
65+
std::string stdin_content_data = [&] {
66+
std::cin >> std::noskipws;
67+
return std::string{std::istream_iterator<char>{std::cin}, {}};
68+
}();
69+
std::string_view stdin_content = stdin_content_data; // Don't copy the data around all the time
70+
71+
size_t match_count = 0;
72+
auto drop_blanks = std::views::drop_while(co::isblank);
73+
74+
while (!file_content.empty()) {
75+
auto marker = std::ranges::search(file_content, "// CHECK"sv);
76+
if (marker.empty()) {
77+
if (match_count == 0) {
78+
std::cerr << "No matcher found!\n";
79+
co::exit(Result::no_match_found);
80+
}
81+
co::exit(Result::success);
82+
}
83+
file_content.remove_prefix(marker.end() - file_content.begin());
84+
85+
const auto get_match = [&]() {
86+
return std::string_view(file_content.begin(), std::ranges::find(file_content, '\n'));
87+
};
88+
89+
if (co::consume_front(file_content, ":")) {
90+
auto match = get_match();
91+
auto found = std::ranges::search(
92+
stdin_content | std::views::drop_while(std::not_fn(co::is_newline)) | std::views::drop(1),
93+
match | drop_blanks);
94+
if (found.empty()) {
95+
co::print_failure(1, stdin_content, match);
96+
}
97+
++match_count;
98+
stdin_content.remove_prefix(found.end() - stdin_content.begin());
99+
} else if (co::consume_front(file_content, "-SAME:")) {
100+
auto match = get_match();
101+
auto haystack = std::string_view(stdin_content.begin(), std::ranges::find(stdin_content, '\n'));
102+
auto found = std::ranges::search(haystack, match | drop_blanks);
103+
if (found.empty()) {
104+
co::print_failure(1, stdin_content, match);
105+
}
106+
stdin_content.remove_prefix(found.end() - stdin_content.begin());
107+
} else if (co::consume_front(file_content, "-NEXT:")) {
108+
auto match = get_match();
109+
auto haystack = [&] {
110+
auto begin = std::ranges::find(stdin_content, '\n');
111+
if (begin == stdin_content.end()) {
112+
co::print_failure(1, stdin_content, match);
113+
}
114+
++begin;
115+
return std::string_view(begin, std::ranges::find(begin, stdin_content.end(), '\n'));
116+
}();
117+
auto found = std::ranges::search(haystack, match | drop_blanks);
118+
if (found.empty())
119+
co::print_failure(1, stdin_content, match);
120+
stdin_content.remove_prefix(found.end() - stdin_content.begin());
121+
} else {
122+
std::cerr << "Unkown matcher type "
123+
<< std::string_view(file_content.begin(), std::ranges::find(file_content, ':')) << " found\n";
124+
co::exit(Result::unknown_matcher);
125+
}
126+
}
127+
128+
co::exit(Result::success);
129+
}

libcxx/test/tools/clang_tidy_checks/proper_version_checks.cpp

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,6 @@ class proper_version_checks_callbacks : public clang::PPCallbacks {
3636
clang::CharSourceRange::getTokenRange(condition_range),
3737
preprocessor_.getSourceManager(),
3838
preprocessor_.getLangOpts());
39-
if (preprocessor_.getSourceManager().isInMainFile(location))
40-
return;
4139

4240
if (condition == "__cplusplus < 201103L && defined(_LIBCPP_USE_FROZEN_CXX03_HEADERS)")
4341
return;

0 commit comments

Comments
 (0)