In gcc I see __attribute__((format(printf warnings for function pointers


prompt > gcc -Wall m.c
m.c: In function 'main':
m.c:117: warning: too few arguments for format
m.c:118: warning: too few arguments for format
m.c:119: warning: too few arguments for format
m.c:120: warning: too few arguments for format
m.c:121: warning: too few arguments for format
prompt > gcc -Wall m.c

But in Clang C I do not.

prompt > cc -Wall m.c -o m.test
m.c:117:48: warning: more '%' conversions than data arguments [-Wformat]
nop_log(10,__FILE__,__func__,__LINE__,"bad %d\n");
~^
m.c:118:47: warning: more '%' conversions than data arguments [-Wformat]
do_log(10,__FILE__,__func__,__LINE__,"bad %d\n");
~^
2 warnings generated.

Is there a way to get clang to issue __attribute__((format(printf,
warnings for function pointers. ?




Code:
 
/*** WOULD BE in header file **/
#include <stdio.h>
#include <stdint.h>
#include <stdarg.h>


#ifndef MAYBE_UNUSED
#if defined(__clang_analyzer__ ) || defined(__GNUC__)
#define MAYBE_UNUSED __attribute__((unused))
#else
#define MAYBE_UNUSED  /*@unused@*/
#endif
#endif /*** MAYBE_UNUSED     ***/




#define TEST_LOG( xx_lvl, xx_fmt, xx_args...) \
    test_loggers[((unsigned int)(xx_lvl))%MAX_LOG_LEVELS] \
       (xx_lvl, __FILE__,  __func__, __LINE__, xx_fmt, ## xx_args)

#define MAX_LOG_LEVELS 14


typedef void (*TEST_LOG_FUNC)(uint8_t level, const char *file, const char *func, int line, const char *fmt, ...)
__attribute__((format(printf,5, 6)));


extern TEST_LOG_FUNC test_loggers[MAX_LOG_LEVELS] ;
extern int    set_log_level(uint8_t lvl,FILE *fp);

/**** SOURCE FILE *****/
static void nop_log(uint8_t level,  const char *file, const char *func, int line, const char *fmt, ...)
__attribute__((format(printf,5, 6)));

static void do_log(uint8_t level,  const char *file, const char *func, int line, const char *fmt, ...)
__attribute__((format(printf,5, 6)));


static FILE *logfp = NULL;

static void nop_log(MAYBE_UNUSED uint8_t level,  MAYBE_UNUSED const char *file, MAYBE_UNUSED const char *func, MAYBE_UNUSED int line, MAYBE_UNUSED MAYBE_UNUSED const char *fmt, ...)
{


}

static void do_log(uint8_t level,  const char *file, const char *func, int line, const char *fmt, ...)
{
   va_list ap;
   va_start(ap, fmt);
   fprintf(logfp,"%s:%d %s [%d]:",file,line,func, level);
   vfprintf(logfp,  fmt, ap);
   va_end(ap);


}


/*** would_be_in src file ***/
TEST_LOG_FUNC
test_loggers[MAX_LOG_LEVELS] =
{
   nop_log,
   nop_log,
   nop_log,
   nop_log,
   nop_log,
   nop_log,
   nop_log,
   nop_log,
   nop_log,
   nop_log,
   nop_log,
   nop_log,
   nop_log,
   nop_log,
};



int
set_log_level(uint8_t lvl, FILE *fp)
{
   int x = lvl;
   if(fp == NULL)
   {
      fp = stdout;
   }
   logfp = fp;

   if(lvl >= MAX_LOG_LEVELS)
   {
      lvl = MAX_LOG_LEVELS-1;
   }
   for(x = lvl; x-->0;)
   {
      test_loggers[x] = do_log;
   }
   for(x = lvl; ++x < MAX_LOG_LEVELS;)
   {
      test_loggers[x] = nop_log;
   }


   return(lvl);
}

#if 1
int
main(MAYBE_UNUSED int argc, MAYBE_UNUSED const char **argv, MAYBE_UNUSED const char **envr)
{
   TEST_LOG_FUNC log_me = nop_log;

   set_log_level(10,NULL);
   nop_log(10,__FILE__,__func__,__LINE__,"bad %d\n");
   do_log(10,__FILE__,__func__,__LINE__,"bad %d\n");
   log_me(10,__FILE__,__func__,__LINE__,"bad %d\n");
   TEST_LOG(10,"bad %d\n");
   TEST_LOG(11,"bad %d\n");

   return(0);


}
#endif