util: Allow Assert() in contexts without __func__
What changed, and why it matters
This is a routine code-quality change that swaps the old way of recording file, line, and function names in assertion helpers for a modern C++ standard feature. It fixes a compiler warning that could appear when assertions are used in certain contexts, but it does not change what the program does or fix any security bug.
No security action needed. Treat as a normal build/quality improvement.
Security signals we found
No strong security signals were identified.
Evidence from the diff
The commit refactors src/util/check.h and src/util/check.cpp to use std::source_location instead of manually passing FILE, LINE, and func. The macros STR_INTERNAL_BUG, CHECK_NONFATAL, Assert, Assume, and NONFATAL_UNREACHABLE now call std::source_location::current(), and the helper functions accept a std::source_location object. This eliminates -Wpredefined-identifier-outside-function warnings when Assert() is expanded in contexts without func, such as inside a lambda or at namespace scope. The runtime behavior of assertions and internal-bug formatting is unchanged.
Changed components
src/util/check.hsrc/util/check.cppInspect captured patch +25 / −22
diff --git a/src/util/check.cpp b/src/util/check.cpp
index 77783c84..37c4ec0a 100644
--- a/src/util/check.cpp
+++ b/src/util/check.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2022 The Bitcoin Core developers
+// Copyright (c) 2022-present The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
@@ -11,30 +11,32 @@
#include <cstdio>
#include <cstdlib>
+#include <source_location>
#include <string>
#include <string_view>
-std::string StrFormatInternalBug(std::string_view msg, std::string_view file, int line, std::string_view func)
+std::string StrFormatInternalBug(std::string_view msg, const std::source_location& loc)
{
return strprintf("Internal bug detected: %s\n%s:%d (%s)\n"
"%s %s\n"
"Please report this issue here: %s\n",
- msg, file, line, func, CLIENT_NAME, FormatFullVersion(), CLIENT_BUGREPORT);
+ msg, loc.file_name(), loc.line(), loc.function_name(),
+ CLIENT_NAME, FormatFullVersion(), CLIENT_BUGREPORT);
}
-NonFatalCheckError::NonFatalCheckError(std::string_view msg, std::string_view file, int line, std::string_view func)
- : std::runtime_error{StrFormatInternalBug(msg, file, line, func)}
+NonFatalCheckError::NonFatalCheckError(std::string_view msg, const std::source_location& loc)
+ : std::runtime_error{StrFormatInternalBug(msg, loc)}
{
}
bool g_detail_test_only_CheckFailuresAreExceptionsNotAborts{false};
-void assertion_fail(std::string_view file, int line, std::string_view func, std::string_view assertion)
+void assertion_fail(const std::source_location& loc, std::string_view assertion)
{
if (g_detail_test_only_CheckFailuresAreExceptionsNotAborts) {
- throw NonFatalCheckError{assertion, file, line, func};
+ throw NonFatalCheckError{assertion, loc};
}
- auto str = strprintf("%s:%s %s: Assertion `%s' failed.\n", file, line, func, assertion);
+ auto str = strprintf("%s:%s %s: Assertion `%s' failed.\n", loc.file_name(), loc.line(), loc.function_name(), assertion);
fwrite(str.data(), 1, str.size(), stderr);
std::abort();
}
diff --git a/src/util/check.h b/src/util/check.h
index 1267d9b5..fc9fa127 100644
--- a/src/util/check.h
+++ b/src/util/check.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2019-2022 The Bitcoin Core developers
+// Copyright (c) 2019-present The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
@@ -9,6 +9,7 @@
#include <atomic>
#include <cassert> // IWYU pragma: export
+#include <source_location>
#include <stdexcept>
#include <string>
#include <string_view>
@@ -52,26 +53,26 @@ struct test_only_CheckFailuresAreExceptionsNotAborts {
~test_only_CheckFailuresAreExceptionsNotAborts() { g_detail_test_only_CheckFailuresAreExceptionsNotAborts = false; };
};
-std::string StrFormatInternalBug(std::string_view msg, std::string_view file, int line, std::string_view func);
+std::string StrFormatInternalBug(std::string_view msg, const std::source_location& loc);
class NonFatalCheckError : public std::runtime_error
{
public:
- NonFatalCheckError(std::string_view msg, std::string_view file, int line, std::string_view func);
+ NonFatalCheckError(std::string_view msg, const std::source_location& loc);
};
/** Internal helper */
-void assertion_fail(std::string_view file, int line, std::string_view func, std::string_view assertion);
+void assertion_fail(const std::source_location& loc, std::string_view assertion);
/** Helper for CHECK_NONFATAL() */
template <typename T>
-T&& inline_check_non_fatal(LIFETIMEBOUND T&& val, const char* file, int line, const char* func, const char* assertion)
+T&& inline_check_non_fatal(LIFETIMEBOUND T&& val, std::source_location loc, std::string_view assertion)
{
if (!val) {
if constexpr (G_ABORT_ON_FAILED_ASSUME) {
- assertion_fail(file, line, func, assertion);
+ assertion_fail(loc, assertion);
}
- throw NonFatalCheckError{assertion, file, line, func};
+ throw NonFatalCheckError{assertion, loc};
}
return std::forward<T>(val);
}
@@ -82,11 +83,11 @@ T&& inline_check_non_fatal(LIFETIMEBOUND T&& val, const char* file, int line, co
/** Helper for Assert()/Assume() */
template <bool IS_ASSERT, typename T>
-constexpr T&& inline_assertion_check(LIFETIMEBOUND T&& val, [[maybe_unused]] const char* file, [[maybe_unused]] int line, [[maybe_unused]] const char* func, [[maybe_unused]] const char* assertion)
+constexpr T&& inline_assertion_check(LIFETIMEBOUND T&& val, [[maybe_unused]] std::source_location loc, [[maybe_unused]] std::string_view assertion)
{
if (IS_ASSERT || std::is_constant_evaluated() || G_ABORT_ON_FAILED_ASSUME) {
if (!val) {
- assertion_fail(file, line, func, assertion);
+ assertion_fail(loc, assertion);
}
}
return std::forward<T>(val);
@@ -95,7 +96,7 @@ constexpr T&& inline_assertion_check(LIFETIMEBOUND T&& val, [[maybe_unused]] con
// All macros may use __func__ inside a lambda, so put them under nolint.
// NOLINTBEGIN(bugprone-lambda-function-name)
-#define STR_INTERNAL_BUG(msg) StrFormatInternalBug((msg), __FILE__, __LINE__, __func__)
+#define STR_INTERNAL_BUG(msg) StrFormatInternalBug((msg), std::source_location::current())
/**
* Identity function. Throw a NonFatalCheckError when the condition evaluates to false
@@ -109,10 +110,10 @@ constexpr T&& inline_assertion_check(LIFETIMEBOUND T&& val, [[maybe_unused]] con
* caller, which can then report the issue to the developers.
*/
#define CHECK_NONFATAL(condition) \
- inline_check_non_fatal(condition, __FILE__, __LINE__, __func__, #condition)
+ inline_check_non_fatal(condition, std::source_location::current(), #condition)
/** Identity function. Abort if the value compares equal to zero */
-#define Assert(val) inline_assertion_check<true>(val, __FILE__, __LINE__, __func__, #val)
+#define Assert(val) inline_assertion_check<true>(val, std::source_location::current(), #val)
/**
* Assume is the identity function.
@@ -124,14 +125,14 @@ constexpr T&& inline_assertion_check(LIFETIMEBOUND T&& val, [[maybe_unused]] con
* - For non-fatal errors in interactive sessions (e.g. RPC or command line
* interfaces), CHECK_NONFATAL() might be more appropriate.
*/
-#define Assume(val) inline_assertion_check<false>(val, __FILE__, __LINE__, __func__, #val)
+#define Assume(val) inline_assertion_check<false>(val, std::source_location::current(), #val)
/**
* NONFATAL_UNREACHABLE() is a macro that is used to mark unreachable code. It throws a NonFatalCheckError.
*/
#define NONFATAL_UNREACHABLE() \
throw NonFatalCheckError( \
- "Unreachable code reached (non-fatal)", __FILE__, __LINE__, __func__)
+ "Unreachable code reached (non-fatal)", std::source_location::current())
// NOLINTEND(bugprone-lambda-function-name)
Why this scored 15/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.