gmessages: Fix -Wformat warnings for g_message() and friends

When compiling with G_LOG_USE_STRUCTURED, g_message(), g_debug(), etc.
use g_log_structured(). The message format string and its format
arguments are passed as the final set of arguments in a longer varargs
list, which includes the log domain and level (and other) fields.
Passing the message format in this way means it’s not possible for the
compiler to know to check its format placeholders when compiling with
-Wformat.

Fix support for this by adding a new semi-private helper function,
_g_log_structured_standard(), which only uses varargs for the message
format and its arguments, and uses fixed arguments for the other fields.
This is then converted to a set of GLogFields and passed to
g_log_structured() as normal.

Support for -Wformat when compiling *without* G_LOG_USE_STRUCTURED was
never broken.

Signed-off-by: Philip Withnall <withnall@endlessm.com>

https://bugzilla.gnome.org/show_bug.cgi?id=793074
This commit is contained in:
Philip Withnall 2018-02-01 13:44:08 +00:00
parent 07f75f6cc2
commit 32cc60dbff
2 changed files with 97 additions and 60 deletions

View File

@ -1976,6 +1976,59 @@ g_log_structured_array (GLogLevelFlags log_level,
_g_log_abort (!(log_level & G_LOG_FLAG_RECURSION));
}
/* Semi-private helper function to implement the g_message() (etc.) macros
* with support for G_GNUC_PRINTF so that @message_format can be checked
* with -Wformat. */
void
g_log_structured_standard (const gchar *log_domain,
GLogLevelFlags log_level,
const gchar *file,
const gchar *line,
const gchar *func,
const gchar *message_format,
...)
{
GLogField fields[] =
{
{ "PRIORITY", log_level_to_priority (log_level), -1 },
{ "CODE_FILE", file, -1 },
{ "CODE_LINE", line, -1 },
{ "CODE_FUNC", func, -1 },
/* Filled in later: */
{ "MESSAGE", NULL, -1 },
/* If @log_domain is %NULL, we will not pass this field: */
{ "GLIB_DOMAIN", log_domain, -1 },
};
gsize n_fields;
gchar *message_allocated = NULL;
gchar buffer[1025];
va_list args;
va_start (args, message_format);
if (log_level & G_LOG_FLAG_RECURSION)
{
/* we use a stack buffer of fixed size, since we're likely
* in an out-of-memory situation
*/
gsize size G_GNUC_UNUSED;
size = _g_vsnprintf (buffer, sizeof (buffer), message_format, args);
fields[4].value = buffer;
}
else
{
fields[4].value = message_allocated = g_strdup_vprintf (message_format, args);
}
va_end (args);
n_fields = G_N_ELEMENTS (fields) - ((log_domain == NULL) ? 1 : 0);
g_log_structured_array (log_level, fields, n_fields);
g_free (message_allocated);
}
/**
* g_log_set_writer_func:
* @func: log writer function, which must not be %NULL

View File

@ -282,6 +282,14 @@ void g_assert_warning (const char *log_domain,
const char *pretty_function,
const char *expression) G_GNUC_NORETURN;
GLIB_AVAILABLE_IN_2_56
void g_log_structured_standard (const gchar *log_domain,
GLogLevelFlags log_level,
const gchar *file,
const gchar *line,
const gchar *func,
const gchar *message_format,
...) G_GNUC_PRINTF (6, 7);
#ifndef G_LOG_DOMAIN
#define G_LOG_DOMAIN ((gchar*) 0)
@ -290,38 +298,26 @@ void g_assert_warning (const char *log_domain,
#if defined(G_HAVE_ISO_VARARGS) && !G_ANALYZER_ANALYZING
#ifdef G_LOG_USE_STRUCTURED
#define g_error(...) G_STMT_START { \
g_log_structured (G_LOG_DOMAIN, G_LOG_LEVEL_ERROR, \
"CODE_FILE", __FILE__, \
"CODE_LINE", G_STRINGIFY (__LINE__), \
"CODE_FUNC", G_STRFUNC, \
"MESSAGE", __VA_ARGS__); \
g_log_structured_standard (G_LOG_DOMAIN, G_LOG_LEVEL_ERROR, \
__FILE__, G_STRINGIFY (__LINE__), \
G_STRFUNC, __VA_ARGS__); \
for (;;) ; \
} G_STMT_END
#define g_message(...) g_log_structured (G_LOG_DOMAIN, G_LOG_LEVEL_MESSAGE, \
"CODE_FILE", __FILE__, \
"CODE_LINE", G_STRINGIFY (__LINE__), \
"CODE_FUNC", G_STRFUNC, \
"MESSAGE", __VA_ARGS__)
#define g_critical(...) g_log_structured (G_LOG_DOMAIN, G_LOG_LEVEL_CRITICAL, \
"CODE_FILE", __FILE__, \
"CODE_LINE", G_STRINGIFY (__LINE__), \
"CODE_FUNC", G_STRFUNC, \
"MESSAGE", __VA_ARGS__)
#define g_warning(...) g_log_structured (G_LOG_DOMAIN, G_LOG_LEVEL_WARNING, \
"CODE_FILE", __FILE__, \
"CODE_LINE", G_STRINGIFY (__LINE__), \
"CODE_FUNC", G_STRFUNC, \
"MESSAGE", __VA_ARGS__)
#define g_info(...) g_log_structured (G_LOG_DOMAIN, G_LOG_LEVEL_INFO, \
"CODE_FILE", __FILE__, \
"CODE_LINE", G_STRINGIFY (__LINE__), \
"CODE_FUNC", G_STRFUNC, \
"MESSAGE", __VA_ARGS__)
#define g_debug(...) g_log_structured (G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, \
"CODE_FILE", __FILE__, \
"CODE_LINE", G_STRINGIFY (__LINE__), \
"CODE_FUNC", G_STRFUNC, \
"MESSAGE", __VA_ARGS__)
#define g_message(...) g_log_structured_standard (G_LOG_DOMAIN, G_LOG_LEVEL_MESSAGE, \
__FILE__, G_STRINGIFY (__LINE__), \
G_STRFUNC, __VA_ARGS__)
#define g_critical(...) g_log_structured_standard (G_LOG_DOMAIN, G_LOG_LEVEL_CRITICAL, \
__FILE__, G_STRINGIFY (__LINE__), \
G_STRFUNC, __VA_ARGS__)
#define g_warning(...) g_log_structured_standard (G_LOG_DOMAIN, G_LOG_LEVEL_WARNING, \
__FILE__, G_STRINGIFY (__LINE__), \
G_STRFUNC, __VA_ARGS__)
#define g_info(...) g_log_structured_standard (G_LOG_DOMAIN, G_LOG_LEVEL_INFO, \
__FILE__, G_STRINGIFY (__LINE__), \
G_STRFUNC, __VA_ARGS__)
#define g_debug(...) g_log_structured_standard (G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, \
__FILE__, G_STRINGIFY (__LINE__), \
G_STRFUNC, __VA_ARGS__)
#else
/* for(;;) ; so that GCC knows that control doesn't go past g_error().
* Put space before ending semicolon to avoid C++ build warnings.
@ -351,38 +347,26 @@ void g_assert_warning (const char *log_domain,
#elif defined(G_HAVE_GNUC_VARARGS) && !G_ANALYZER_ANALYZING
#ifdef G_LOG_USE_STRUCTURED
#define g_error(format...) G_STMT_START { \
g_log_structured (G_LOG_DOMAIN, G_LOG_LEVEL_ERROR, \
"CODE_FILE", __FILE__, \
"CODE_LINE", G_STRINGIFY (__LINE__), \
"CODE_FUNC", G_STRFUNC, \
"MESSAGE", format); \
g_log_structured_standard (G_LOG_DOMAIN, G_LOG_LEVEL_ERROR, \
__FILE__, G_STRINGIFY (__LINE__), \
G_STRFUNC, format); \
for (;;) ; \
} G_STMT_END
#define g_message(format...) g_log_structured (G_LOG_DOMAIN, G_LOG_LEVEL_MESSAGE, \
"CODE_FILE", __FILE__, \
"CODE_LINE", G_STRINGIFY (__LINE__), \
"CODE_FUNC", G_STRFUNC, \
"MESSAGE", format)
#define g_critical(format...) g_log_structured (G_LOG_DOMAIN, G_LOG_LEVEL_CRITICAL, \
"CODE_FILE", __FILE__, \
"CODE_LINE", G_STRINGIFY (__LINE__), \
"CODE_FUNC", G_STRFUNC, \
"MESSAGE", format)
#define g_warning(format...) g_log_structured (G_LOG_DOMAIN, G_LOG_LEVEL_WARNING, \
"CODE_FILE", __FILE__, \
"CODE_LINE", G_STRINGIFY (__LINE__), \
"CODE_FUNC", G_STRFUNC, \
"MESSAGE", format)
#define g_info(format...) g_log_structured (G_LOG_DOMAIN, G_LOG_LEVEL_INFO, \
"CODE_FILE", __FILE__, \
"CODE_LINE", G_STRINGIFY (__LINE__), \
"CODE_FUNC", G_STRFUNC, \
"MESSAGE", format)
#define g_debug(format...) g_log_structured (G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, \
"CODE_FILE", __FILE__, \
"CODE_LINE", G_STRINGIFY (__LINE__), \
"CODE_FUNC", G_STRFUNC, \
"MESSAGE", format)
#define g_message(format...) g_log_structured_standard (G_LOG_DOMAIN, G_LOG_LEVEL_MESSAGE, \
__FILE__, G_STRINGIFY (__LINE__), \
G_STRFUNC, format)
#define g_critical(format...) g_log_structured_standard (G_LOG_DOMAIN, G_LOG_LEVEL_CRITICAL, \
__FILE__, G_STRINGIFY (__LINE__), \
G_STRFUNC, format)
#define g_warning(format...) g_log_structured_standard (G_LOG_DOMAIN, G_LOG_LEVEL_WARNING, \
__FILE__, G_STRINGIFY (__LINE__), \
G_STRFUNC, format)
#define g_info(format...) g_log_structured_standard (G_LOG_DOMAIN, G_LOG_LEVEL_INFO, \
__FILE__, G_STRINGIFY (__LINE__), \
G_STRFUNC, format)
#define g_debug(format...) g_log_structured_standard (G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, \
__FILE__, G_STRINGIFY (__LINE__), \
G_STRFUNC, format)
#else
#define g_error(format...) G_STMT_START { \
g_log (G_LOG_DOMAIN, \