From: brarcher Date: Mon, 16 Dec 2013 23:27:14 +0000 (+0000) Subject: change how assertions are called, to support source code analyzers X-Git-Tag: 0.10.0~346 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=a7d8fe89e0e8285b3e9e2bbaabedf6b1821ab196;p=check change how assertions are called, to support source code analyzers The assert functions that check provides are really macros that funnel down to the _ck_assert_msg() function. That function receives the result from the operation being checked, then decides if the unit test should continue or should halt. The assert function in C is sometimes also a macro. For example, the following: void foo(int *p) { assert(p != NULL); } on OSX when preprocessed actually becomes: void foo(int *p) { (_builtin_expect(!(p != NULL), 0) ? __assert_rtn(__func_, "t.c", 4, "p != NULL") : (void)0); } The __assert_rtn function is marked as "noreturn", and only gets invoked when an assertion has failed. Because it only gets called when something bad has happened, static code analysis can reason about when it is and is not called, and make assumptions about the code being compiled. To the point, this change refactors check's assert macros in such a way that they result in a function being called only when the assertion fails, and thus static code analyzers (such as clang's scan-build tool) can reason when something is possible and impossible in test code. As a result, if one wanted to run static analysis on unit test code, there would be far fewer false positives. git-svn-id: svn+ssh://svn.code.sf.net/p/check/code/trunk@870 64e312b2-a51f-0410-8e61-82d0ca0eb02a --- diff --git a/src/check.c b/src/check.c index d7806d7..94d9dbf 100644 --- a/src/check.c +++ b/src/check.c @@ -253,30 +253,28 @@ void _mark_point (const char *file, int line) send_loc_info (file, line); } -void _ck_assert_msg (int result, const char *file, +void _ck_assert_failed (const char *file, int line, const char *expr, ...) { const char *msg; - + va_list ap; + char buf[BUFSIZ]; + send_loc_info (file, line); - if (!result) { - va_list ap; - char buf[BUFSIZ]; - - va_start(ap,expr); - msg = (const char*)va_arg(ap, char *); - if (msg == NULL) - msg = expr; - vsnprintf(buf, BUFSIZ, msg, ap); - va_end(ap); - send_failure_info (buf); - if (cur_fork_status() == CK_FORK) { -#ifdef HAVE_FORK - exit(1); -#endif /* HAVE_FORK */ - } else { - longjmp(error_jmp_buffer, 1); - } + + va_start(ap,expr); + msg = (const char*)va_arg(ap, char *); + if (msg == NULL) + msg = expr; + vsnprintf(buf, BUFSIZ, msg, ap); + va_end(ap); + send_failure_info (buf); + if (cur_fork_status() == CK_FORK) { + #ifdef HAVE_FORK + exit(1); + #endif /* HAVE_FORK */ + } else { + longjmp(error_jmp_buffer, 1); } } diff --git a/src/check.h.in b/src/check.h.in index 3d886ae..d14056e 100644 --- a/src/check.h.in +++ b/src/check.h.in @@ -259,14 +259,14 @@ static void __testname (int _i CK_ATTRIBUTE_UNUSED)\ FIXME: strcmp (str1, str2) due to excessive string length. */ #define fail_if(expr, ...)\ - _ck_assert_msg(!(expr), __FILE__, __LINE__,\ - "Failure '"#expr"' occured" , ## __VA_ARGS__, NULL) + (expr) ? \ + _ck_assert_failed(__FILE__, __LINE__, "Failure '"#expr"' occured" , ## __VA_ARGS__, NULL) \ + : _mark_point(__FILE__, __LINE__) #define fail ck_abort_msg -/* Non macro version of #ck_assert_msg, with more complicated interface */ -void CK_EXPORT _ck_assert_msg (int result, const char *file, - int line, const char *expr, ...); +/* This is called whenever an assertion fails */ +void CK_EXPORT _ck_assert_failed (const char *file, int line, const char *expr, ...) CK_ATTRIBUTE_NORETURN; /* New check fail API. */ @@ -276,12 +276,13 @@ void CK_EXPORT _ck_assert_msg (int result, const char *file, */ #define ck_assert(C) ck_assert_msg(C, NULL) #define ck_assert_msg(expr, ...) \ - _ck_assert_msg(expr, __FILE__, __LINE__,\ - "Assertion '"#expr"' failed" , ## __VA_ARGS__, NULL) + (expr) ? \ + _mark_point(__FILE__, __LINE__) : \ + _ck_assert_failed(__FILE__, __LINE__, "Assertion '"#expr"' failed" , ## __VA_ARGS__, NULL) /* Always fail */ #define ck_abort() ck_abort_msg(NULL) -#define ck_abort_msg(...) _ck_assert_msg(0, __FILE__, __LINE__, "Failed" , ## __VA_ARGS__, NULL) +#define ck_abort_msg(...) _ck_assert_failed(__FILE__, __LINE__, "Failed" , ## __VA_ARGS__, NULL) /* Signed and unsigned integer comparsion macros with improved output compared to ck_assert(). */ /* OP may be any comparion operator. */