Optional detail info at assertion failure

This commit is contained in:
Nobuyoshi Nakada 2024-01-31 15:09:51 +09:00
parent 0292d1b7c3
commit d31a12a210
No known key found for this signature in database
GPG Key ID: 3582D74E1FEE4465
2 changed files with 96 additions and 10 deletions

15
error.c
View File

@ -1122,11 +1122,26 @@ rb_report_bug_valist(VALUE file, int line, const char *fmt, va_list args)
void
rb_assert_failure(const char *file, int line, const char *name, const char *expr)
{
rb_assert_failure_detail(file, line, name, expr, NULL);
}
void
rb_assert_failure_detail(const char *file, int line, const char *name, const char *expr,
const char *fmt, ...)
{
FILE *out = stderr;
fprintf(out, "Assertion Failed: %s:%d:", file, line);
if (name) fprintf(out, "%s:", name);
fprintf(out, "%s\n%s\n\n", expr, rb_dynamic_description);
if (fmt && *fmt) {
va_list args;
va_start(args, fmt);
vfprintf(out, fmt, args);
va_end(args);
}
preface_dump(out);
rb_vm_bugreport(NULL, out);
bug_report_end(out, -1);

View File

@ -22,6 +22,7 @@
*/
#include "ruby/internal/assume.h"
#include "ruby/internal/attr/cold.h"
#include "ruby/internal/attr/format.h"
#include "ruby/internal/attr/noreturn.h"
#include "ruby/internal/cast.h"
#include "ruby/internal/dllexport.h"
@ -132,6 +133,11 @@ RBIMPL_SYMBOL_EXPORT_BEGIN()
RBIMPL_ATTR_NORETURN()
RBIMPL_ATTR_COLD()
void rb_assert_failure(const char *file, int line, const char *name, const char *expr);
RBIMPL_ATTR_NORETURN()
RBIMPL_ATTR_COLD()
RBIMPL_ATTR_FORMAT(RBIMPL_PRINTF_FORMAT, 5, 6)
void rb_assert_failure_detail(const char *file, int line, const char *name, const char *expr, const char *fmt, ...);
RBIMPL_SYMBOL_EXPORT_END()
#ifdef RUBY_FUNCTION_NAME_STRING
@ -147,8 +153,22 @@ RBIMPL_SYMBOL_EXPORT_END()
*
* @param mesg The message to display.
*/
#define RUBY_ASSERT_FAIL(mesg) \
#if defined(HAVE___VA_OPT__)
# if RBIMPL_HAS_WARNING("-Wgnu-zero-variadic-macro-arguments")
/* __VA_OPT__ is to be used for the zero variadic macro arguments
* cases. */
RBIMPL_WARNING_IGNORED(-Wgnu-zero-variadic-macro-arguments)
# endif
# define RUBY_ASSERT_FAIL(mesg, ...) \
rb_assert_failure##__VA_OPT__(_detail)( \
__FILE__, __LINE__, RBIMPL_ASSERT_FUNC, mesg __VA_OPT__(,) __VA_ARGS__)
#elif !defined(__cplusplus)
# define RUBY_ASSERT_FAIL(mesg, ...) \
rb_assert_failure(__FILE__, __LINE__, RBIMPL_ASSERT_FUNC, mesg)
#else
# define RUBY_ASSERT_FAIL(mesg) \
rb_assert_failure(__FILE__, __LINE__, RBIMPL_ASSERT_FUNC, mesg)
#endif
/**
* Asserts that the expression is truthy. If not aborts with the message.
@ -156,15 +176,27 @@ RBIMPL_SYMBOL_EXPORT_END()
* @param expr What supposedly evaluates to true.
* @param mesg The message to display on failure.
*/
#define RUBY_ASSERT_MESG(expr, mesg) \
#if defined(HAVE___VA_OPT__) || !defined(__cplusplus)
# define RUBY_ASSERT_MESG(expr, ...) \
(RB_LIKELY(expr) ? RBIMPL_ASSERT_NOTHING : RUBY_ASSERT_FAIL(__VA_ARGS__))
#else
# define RUBY_ASSERT_MESG(expr, mesg) \
(RB_LIKELY(expr) ? RBIMPL_ASSERT_NOTHING : RUBY_ASSERT_FAIL(mesg))
#endif
/**
* A variant of #RUBY_ASSERT that does not interface with #RUBY_DEBUG.
*
* @copydetails #RUBY_ASSERT
*/
#define RUBY_ASSERT_ALWAYS(expr) RUBY_ASSERT_MESG((expr), #expr)
#if defined(HAVE___VA_OPT__)
# define RUBY_ASSERT_ALWAYS(expr, ...) \
RUBY_ASSERT_MESG(expr, #expr __VA_OPT__(,) __VA_ARGS__)
#elif !defined(__cplusplus)
# define RUBY_ASSERT_ALWAYS(expr, ...) RUBY_ASSERT_MESG(expr, #expr)
#else
# define RUBY_ASSERT_ALWAYS(expr) RUBY_ASSERT_MESG((expr), #expr)
#endif
/**
* Asserts that the given expression is truthy if and only if #RUBY_DEBUG is truthy.
@ -172,9 +204,20 @@ RBIMPL_SYMBOL_EXPORT_END()
* @param expr What supposedly evaluates to true.
*/
#if RUBY_DEBUG
# if defined(HAVE___VA_OPT__)
# define RUBY_ASSERT(expr, ...) \
RUBY_ASSERT_MESG((expr), #expr __VA_OPT__(,) __VA_ARGS__)
# elif !defined(__cplusplus)
# define RUBY_ASSERT(expr, ...) RUBY_ASSERT_MESG((expr), #expr)
# else
# define RUBY_ASSERT(expr) RUBY_ASSERT_MESG((expr), #expr)
# endif
#else
# if defined(HAVE___VA_OPT__) || !defined(__cplusplus)
# define RUBY_ASSERT(/* expr, */...) RBIMPL_ASSERT_NOTHING
# else
# define RUBY_ASSERT(expr) RBIMPL_ASSERT_NOTHING
# endif
#endif
/**
@ -187,9 +230,20 @@ RBIMPL_SYMBOL_EXPORT_END()
/* Currently `RUBY_DEBUG == ! defined(NDEBUG)` is always true. There is no
* difference any longer between this one and `RUBY_ASSERT`. */
#if defined(NDEBUG)
# if defined(HAVE___VA_OPT__) || !defined(__cplusplus)
# define RUBY_ASSERT_NDEBUG(/* expr, */...) RBIMPL_ASSERT_NOTHING
# else
# define RUBY_ASSERT_NDEBUG(expr) RBIMPL_ASSERT_NOTHING
# endif
#else
# if defined(HAVE___VA_OPT__)
# define RUBY_ASSERT_NDEBUG(expr, ...) \
RUBY_ASSERT_MESG((expr), #expr __VA_OPT__(,) __VA_ARGS__)
# elif !defined(__cplusplus)
# define RUBY_ASSERT_NDEBUG(expr, ...) RUBY_ASSERT_MESG((expr), #expr)
# else
# define RUBY_ASSERT_NDEBUG(expr) RUBY_ASSERT_MESG((expr), #expr)
# endif
#endif
/**
@ -197,10 +251,20 @@ RBIMPL_SYMBOL_EXPORT_END()
* @param mesg The message to display on failure.
*/
#if RUBY_DEBUG
# if defined(HAVE___VA_OPT__) || !defined(__cplusplus)
# define RUBY_ASSERT_MESG_WHEN(cond, /* expr, */...) \
RUBY_ASSERT_MESG(__VA_ARGS__)
# else
# define RUBY_ASSERT_MESG_WHEN(cond, expr, mesg) RUBY_ASSERT_MESG((expr), (mesg))
# endif
#else
# if defined(HAVE___VA_OPT__) || !defined(__cplusplus)
# define RUBY_ASSERT_MESG_WHEN(cond, expr, ...) \
((cond) ? RUBY_ASSERT_MESG((expr), __VA_ARGS__) : RBIMPL_ASSERT_NOTHING)
# else
# define RUBY_ASSERT_MESG_WHEN(cond, expr, mesg) \
((cond) ? RUBY_ASSERT_MESG((expr), (mesg)) : RBIMPL_ASSERT_NOTHING)
# endif
#endif
/**
@ -210,7 +274,14 @@ RBIMPL_SYMBOL_EXPORT_END()
* @param cond Extra condition that shall hold for assertion to take effect.
* @param expr What supposedly evaluates to true.
*/
#define RUBY_ASSERT_WHEN(cond, expr) RUBY_ASSERT_MESG_WHEN((cond), (expr), #expr)
#if defined(HAVE___VA_OPT__)
# define RUBY_ASSERT_WHEN(cond, expr, ...) \
RUBY_ASSERT_MESG_WHEN(cond, expr, #expr __VA_OPT__(,) __VA_ARGS__)
#elif !defined(__cplusplus)
# define RUBY_ASSERT_WHEN(cond, expr, ...) RUBY_ASSERT_MESG_WHEN(cond, expr, #expr)
#else
# define RUBY_ASSERT_WHEN(cond, expr) RUBY_ASSERT_MESG_WHEN((cond), (expr), #expr)
#endif
/**
* This is either #RUBY_ASSERT or #RBIMPL_ASSUME, depending on #RUBY_DEBUG.