@@ -39,6 +39,7 @@ B=${0%%.cc}; [ "$B" -nt "$0" ] || c++ -std=c++17 -o"$B" "$0" && exec "$B" "$@";
3939#include < unistd.h>
4040
4141#include < algorithm>
42+ #include < atomic>
4243#include < cerrno>
4344#include < cinttypes>
4445#include < csignal>
@@ -308,8 +309,14 @@ class ClangTidyRunner
308309
309310 const std::string uniquifier = " ." + std::to_string (getpid ());
310311 std::mutex queue_access_lock;
312+ std::atomic<bool > fatal_stop{false };
313+ std::string fatal_output;
314+ std::string fatal_file;
311315 auto clang_tidy_runner = [&]() {
312316 for (;;) {
317+ if (fatal_stop.load ()) {
318+ return ;
319+ }
313320 filepath_contenthash_t work;
314321 {
315322 const std::lock_guard<std::mutex> lock (queue_access_lock);
@@ -328,7 +335,7 @@ class ClangTidyRunner
328335 // it is easy to find with `ps` or `top`.
329336 const std::string command = clang_tidy_ + " '" + work.first .string ()
330337 + " '" + clang_tidy_args_ + " > '" + tmp_out
331- + " ' 2>/dev/null " ;
338+ + " ' 2>&1 " ;
332339 const int r = system (command.c_str ());
333340#ifdef WIFSIGNALED
334341 // NOLINTBEGIN
@@ -340,6 +347,30 @@ class ClangTidyRunner
340347 }
341348 // NOLINTEND
342349#endif
350+ // Detect systemic clang-tidy failure (bad config, missing compile db,
351+ // etc.): non-zero exit with no check findings in output. Abort the
352+ // run and surface the error instead of silently caching empty output.
353+ #ifdef WIFEXITED
354+ // NOLINTBEGIN
355+ if (WIFEXITED (r) && WEXITSTATUS (r) != 0 ) {
356+ // NOLINTEND
357+ #else
358+ if (r != 0 ) {
359+ #endif
360+ const std::string out = GetContent (tmp_out);
361+ static const std::regex check_finding (
362+ R"( \[[a-zA-Z0-9.]+-[a-zA-Z0-9.-]+\])" );
363+ if (!std::regex_search (out, check_finding)) {
364+ bool expected = false ;
365+ if (fatal_stop.compare_exchange_strong (expected, true )) {
366+ fatal_output = out;
367+ fatal_file = work.first .string ();
368+ }
369+ std::error_code ignored_error;
370+ fs::remove (tmp_out, ignored_error);
371+ return ;
372+ }
373+ }
343374 const std::string filter_filename = work.first .filename ().string ();
344375 RepairFilenameOccurences (filter_filename, tmp_out, tmp_out);
345376 fs::rename (tmp_out, final_out); // atomic replacement
@@ -356,6 +387,12 @@ class ClangTidyRunner
356387 if (print_progress) {
357388 fprintf (stderr, " \n " ); // Clean out progress counter.
358389 }
390+ if (fatal_stop.load ()) {
391+ std::cerr << " \n clang-tidy failed on " << fatal_file
392+ << " (aborting; output below):\n "
393+ << fatal_output << " \n " ;
394+ exit (EXIT_FAILURE );
395+ }
359396 }
360397
361398 private:
0 commit comments