On 05/18/2015 11:07 PM, Eduardo Piombino wrote:
That's really helpful and it's very close to what I've been looking for.
Had I figured this trick (I tried [ab]using so many other warnings
with no gain) I may have settled for this solution, weren't it for the
fact that in my current setup the _Static_warning is not fired from
its own line like your example, but from a macro.
$ cat h.c && ../gcc/bin/gcc -Wall -c h.c -o/dev/null
int static_warning_dummy(char * p) __attribute__ ((nonnull(1)));
int static_warning_dummy(char * p) { return 0; }
#define _Static_warning(x, txt) (int) (x) ? 0 :
static_warning_dummy((char *) x);
#define foo(x) do { _Static_warning(x, errormsg); bar(x); } while(0)
void bar(int p)
{
}
int main(void)
{
_Static_warning(0, "bar"); // here you see the message in the logs
foo(0); // here you don't
}
h.c: In function ‘main’:
h.c:13:2: warning: null argument where non-null required (argument 1)
[-Wnonnull]
_Static_warning(0, "bar"); // here you see the message in the logs
^
h.c:14:2: warning: null argument where non-null required (argument 1)
[-Wnonnull]
foo(0); // here you don't
^
The consequence is that when the compiler prints out the line that
triggered it, it will print out the original line of code, and not the
one where I actually invoked the _Static_warning, losing the message
parameter in the way.
I would consider this a bug. GCC should include in the warning
the context from which the macro was invoked just as it does in
the _Static_assert case, and just as Clang does (below).
$ cat t.c && clang -Wall -c -o/dev/null t.c
void foo (char*, ...) __attribute__ ((nonnull (1)));
#define foo(p) foo (p, "p is null")
#define bar(p) _Static_assert (p, "p is null")
void baz (void) {
foo (0);
bar (0);
}
t.c:6:5: warning: null passed to a callee that requires a non-null argument
[-Wnonnull]
foo (0);
^ ~
t.c:2:35: note: expanded from macro 'foo'
#define foo(p) foo (p, "p is null")
^
t.c:7:5: error: static_assert failed "p is null"
bar (0);
^ ~
t.c:3:16: note: expanded from macro 'bar'
#define bar(p) _Static_assert (p, "p is null")
^
1 warning and 1 error generated.
There may exist some kind of workaround to this, but I can't see it right now.
It's not pretty but this seems to work:
$ cat -n t.c && gcc -O2 -Wall -c -o/dev/null t.c
1 #define Static_warning(x, txt) \
2 (void)__builtin_choose_expr (x, 0, (char[sizeof txt - 2 +
!!x]){ txt })
3 #define foo(x) do { Static_warning (x, "errormsg"); } while(0)
4
5
6 void bar (void) {
7 Static_warning (0, "bad");
8 foo (0);
9
10 Static_warning (1, "good");
11 foo (1);
12 }
t.c: In function ‘bar’:
t.c:2:67: warning: initializer-string for array of chars is too long
(void)__builtin_choose_expr (x, 0, (char[sizeof txt - 2 + !!x]){ txt })
^
t.c:7:5: note: in expansion of macro ‘Static_warning’
Static_warning (0, "bad");
^
t.c:2:67: warning: initializer-string for array of chars is too long
(void)__builtin_choose_expr (x, 0, (char[sizeof txt - 2 + !!x]){ txt })
^
t.c:3:21: note: in expansion of macro ‘Static_warning’
#define foo(x) do { Static_warning (x, "errormsg"); } while(0)
^
t.c:8:5: note: in expansion of macro ‘foo’
foo (0);
^
Martin