When git is compiled with "gcc -Wuninitialized -O3", some
inlined calls provide an additional opportunity for the
compiler to do static analysis on variable initialization.
For example, with two functions like this:
  int get_foo(int *foo)
  {
	if (something_that_might_fail() < 0)
		return error("unable to get foo");
	*foo = 0;
	return 0;
  }
  void some_fun(void)
  {
	  int foo;
	  if (get_foo(&foo) < 0)
		  return -1;
	  printf("foo is %d\n", foo);
  }
If get_foo() is not inlined, then when compiling some_fun,
gcc sees only that a pointer to the local variable is
passed, and must assume that it is an out parameter that
is initialized after get_foo returns.
However, when get_foo() is inlined, the compiler may look at
all of the code together and see that some code paths in
get_foo() do not initialize the variable. As a result, it
prints a warning. But what the compiler can't see is that
error() always returns -1, and therefore we know that either
we return early from some_fun, or foo ends up initialized,
and the code is safe.  The warning is a false positive.
If we can make the compiler aware that error() will always
return -1, it can do a better job of analysis. The simplest
method would be to inline the error() function. However,
this doesn't work, because gcc will not inline a variadc
function. We can work around this by defining a macro. This
relies on two gcc extensions:
  1. Variadic macros (these are present in C99, but we do
     not rely on that).
  2. Gcc treats the "##" paste operator specially between a
     comma and __VA_ARGS__, which lets our variadic macro
     work even if no format parameters are passed to
     error().
Since we are using these extra features, we hide the macro
behind an #ifdef. This is OK, though, because our goal was
just to help gcc.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
		
	
		
			
				
	
	
		
			152 lines
		
	
	
		
			3.1 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			152 lines
		
	
	
		
			3.1 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/*
 | 
						|
 * GIT - The information manager from hell
 | 
						|
 *
 | 
						|
 * Copyright (C) Linus Torvalds, 2005
 | 
						|
 */
 | 
						|
#include "git-compat-util.h"
 | 
						|
#include "cache.h"
 | 
						|
 | 
						|
static int dying;
 | 
						|
 | 
						|
void vreportf(const char *prefix, const char *err, va_list params)
 | 
						|
{
 | 
						|
	char msg[4096];
 | 
						|
	vsnprintf(msg, sizeof(msg), err, params);
 | 
						|
	fprintf(stderr, "%s%s\n", prefix, msg);
 | 
						|
}
 | 
						|
 | 
						|
void vwritef(int fd, const char *prefix, const char *err, va_list params)
 | 
						|
{
 | 
						|
	char msg[4096];
 | 
						|
	int len = vsnprintf(msg, sizeof(msg), err, params);
 | 
						|
	if (len > sizeof(msg))
 | 
						|
		len = sizeof(msg);
 | 
						|
 | 
						|
	write_in_full(fd, prefix, strlen(prefix));
 | 
						|
	write_in_full(fd, msg, len);
 | 
						|
	write_in_full(fd, "\n", 1);
 | 
						|
}
 | 
						|
 | 
						|
static NORETURN void usage_builtin(const char *err, va_list params)
 | 
						|
{
 | 
						|
	vreportf("usage: ", err, params);
 | 
						|
	exit(129);
 | 
						|
}
 | 
						|
 | 
						|
static NORETURN void die_builtin(const char *err, va_list params)
 | 
						|
{
 | 
						|
	vreportf("fatal: ", err, params);
 | 
						|
	exit(128);
 | 
						|
}
 | 
						|
 | 
						|
static void error_builtin(const char *err, va_list params)
 | 
						|
{
 | 
						|
	vreportf("error: ", err, params);
 | 
						|
}
 | 
						|
 | 
						|
static void warn_builtin(const char *warn, va_list params)
 | 
						|
{
 | 
						|
	vreportf("warning: ", warn, params);
 | 
						|
}
 | 
						|
 | 
						|
/* If we are in a dlopen()ed .so write to a global variable would segfault
 | 
						|
 * (ugh), so keep things static. */
 | 
						|
static NORETURN_PTR void (*usage_routine)(const char *err, va_list params) = usage_builtin;
 | 
						|
static NORETURN_PTR void (*die_routine)(const char *err, va_list params) = die_builtin;
 | 
						|
static void (*error_routine)(const char *err, va_list params) = error_builtin;
 | 
						|
static void (*warn_routine)(const char *err, va_list params) = warn_builtin;
 | 
						|
 | 
						|
void set_die_routine(NORETURN_PTR void (*routine)(const char *err, va_list params))
 | 
						|
{
 | 
						|
	die_routine = routine;
 | 
						|
}
 | 
						|
 | 
						|
void set_error_routine(void (*routine)(const char *err, va_list params))
 | 
						|
{
 | 
						|
	error_routine = routine;
 | 
						|
}
 | 
						|
 | 
						|
void NORETURN usagef(const char *err, ...)
 | 
						|
{
 | 
						|
	va_list params;
 | 
						|
 | 
						|
	va_start(params, err);
 | 
						|
	usage_routine(err, params);
 | 
						|
	va_end(params);
 | 
						|
}
 | 
						|
 | 
						|
void NORETURN usage(const char *err)
 | 
						|
{
 | 
						|
	usagef("%s", err);
 | 
						|
}
 | 
						|
 | 
						|
void NORETURN die(const char *err, ...)
 | 
						|
{
 | 
						|
	va_list params;
 | 
						|
 | 
						|
	if (dying) {
 | 
						|
		fputs("fatal: recursion detected in die handler\n", stderr);
 | 
						|
		exit(128);
 | 
						|
	}
 | 
						|
	dying = 1;
 | 
						|
 | 
						|
	va_start(params, err);
 | 
						|
	die_routine(err, params);
 | 
						|
	va_end(params);
 | 
						|
}
 | 
						|
 | 
						|
void NORETURN die_errno(const char *fmt, ...)
 | 
						|
{
 | 
						|
	va_list params;
 | 
						|
	char fmt_with_err[1024];
 | 
						|
	char str_error[256], *err;
 | 
						|
	int i, j;
 | 
						|
 | 
						|
	if (dying) {
 | 
						|
		fputs("fatal: recursion detected in die_errno handler\n",
 | 
						|
			stderr);
 | 
						|
		exit(128);
 | 
						|
	}
 | 
						|
	dying = 1;
 | 
						|
 | 
						|
	err = strerror(errno);
 | 
						|
	for (i = j = 0; err[i] && j < sizeof(str_error) - 1; ) {
 | 
						|
		if ((str_error[j++] = err[i++]) != '%')
 | 
						|
			continue;
 | 
						|
		if (j < sizeof(str_error) - 1) {
 | 
						|
			str_error[j++] = '%';
 | 
						|
		} else {
 | 
						|
			/* No room to double the '%', so we overwrite it with
 | 
						|
			 * '\0' below */
 | 
						|
			j--;
 | 
						|
			break;
 | 
						|
		}
 | 
						|
	}
 | 
						|
	str_error[j] = 0;
 | 
						|
	snprintf(fmt_with_err, sizeof(fmt_with_err), "%s: %s", fmt, str_error);
 | 
						|
 | 
						|
	va_start(params, fmt);
 | 
						|
	die_routine(fmt_with_err, params);
 | 
						|
	va_end(params);
 | 
						|
}
 | 
						|
 | 
						|
#undef error
 | 
						|
int error(const char *err, ...)
 | 
						|
{
 | 
						|
	va_list params;
 | 
						|
 | 
						|
	va_start(params, err);
 | 
						|
	error_routine(err, params);
 | 
						|
	va_end(params);
 | 
						|
	return -1;
 | 
						|
}
 | 
						|
 | 
						|
void warning(const char *warn, ...)
 | 
						|
{
 | 
						|
	va_list params;
 | 
						|
 | 
						|
	va_start(params, warn);
 | 
						|
	warn_routine(warn, params);
 | 
						|
	va_end(params);
 | 
						|
}
 |