gcc's -Wmissing-format-attribute option
Начну сразу с примера. Игрушечний пример выглядит так:
/* a1.c */
#include <stdarg.h>
#include <stdio.h>
static int verbosity = 0;
static void v_log (int minimal_verbosity, const char *fmt, ...) {
va_list ap;
/* should we output it? */
if (minimal_verbosity > verbosity)
return;
/* real work */
(ap, fmt);
va_start(stderr, fmt, ap);
vfprintf(ap);
va_end}
int main() {
/* parse something */
= 5;
verbosity /* */
(3, "About to exit from %s\n");
v_log return 0;
}
Типичная программа. Чтобы найти ошибку - нужно хорошенько присматриваться с форматной строке, а хочется автоматизировать поиск таких тривиальных ошибок.
С давних пор gcc позволяет объявить функцию, как printf()-подобную:
/* a2.c */
#include <stdarg.h>
#include <stdio.h>
static int verbosity = 0;
static void v_log (int minimal_verbosity, const char *fmt, ...) __attribute__ ((format (printf, 2, 3)));
static void v_log (int minimal_verbosity, const char *fmt, ...) {
va_list ap;
/* should we output it? */
if (minimal_verbosity > verbosity)
return;
/* real work */
(ap, fmt);
va_start(stderr, fmt, ap);
vfprintf(ap);
va_end}
int main() {
/* parse something */
= 5;
verbosity /* */
(3, "About to exit from %s\n");
v_log return 0;
}
$ gcc a2.c -Wall -o a
a2.c: В функции «main»:
a2.c:19:9: предупреждение: format «%s» expects a matching «char *» argument [-Wformat]
Споймал! Но руками выискивать функции с ellipsis ((…)) тоже не хочется.
Сегодня с утра одним глазом смотрел, как собирается gcc (ну а что еще гентушнегу делать?) и заметил неведомую для себя опцию -Wmissing-format-attribute.
Она, собственно, подсказывает куда надо всунуть форматные атрибуты:
$ gcc a1.c -Wall -Wmissing-format-attribute -o a
a1.c: В функции «v_log»:
a1.c:11:9: предупреждение: этой функции, вероятно, можно задать атрибут форматирования gnu_printf [-Wmissing-format-attribute]
Я подумал, что неплохо бы всунуть в сборку всех наших проектов этот чудоключ и попробовать их пособирать.
Нашел много интересных ошибок в реальном коде :]
Утечка форматной строки из пользовательского кода (похожих ошибок много):
@@ -57,9 +60,9 @@ public:
}
static void ThrowError(void *ud, const SQChar *s) {
SQCompiler *c = (SQCompiler *)ud;- c->Error(s);
+ c->Error("%s", s);
}- void Error(const SQChar *s, ...)
+ void Error(const SQChar *s, ...) GCCISM(__attribute__ ((format (printf, 2, 3))))
{
static SQChar temp[256]; va_list vl;
Просто мусор в обработчике ошибки (похожих ошибок много). Бедный пользователь не узнал бы, почему у него случается SIGSEGV:
@@ -926,7 +931,7 @@ void check_cgp( void ) {
pthread_t thread;
if( pthread_create( &thread, &thread_attr, &cgp_check_thread, cgpfile ) )- mexit( EXIT_FAILURE, "pthread_create(): %s", errno );
+ mexit( EXIT_FAILURE, "pthread_create(): %s", strerror(errno) );
}
else if( !strcmp( command, "QUIT" ) ) { cgp_response( NULL, "QUITTING" );