[PATCH][gcc] Allow functions without C-style ellipsis to use format attribute
Tuan Le Quang
lequangtuan6b@gmail.com
Mon Jun 28 04:24:45 GMT 2021
Hi,
Currently, format attribute can be used to do type-checking for arguments
with respect to a format string. However, only functions with a C-style
ellipsis can use it.
Supporting this attribute for non-variadic functions(functions without a
C-style ellipsis) gives nice convenience especially when writing code in
C++, we can use it for C++ variadic template functions like this
template<Args...args>
__attribute__((format(printf, 1, 2))) void myPrint (const char * fmt,
Args...args)
This patch will introduce these changes:
1. It is no longer an error simply to have a function with the format
attribute but no C-style variadic arguments
2. Functions are subjected to warnings/errors as before, except errors
mentioned in point 1 about not being variadic. For example, when a
non-variadic function has wrong arguments, e.g
__attribute__((format(printf, 1, 1))) or when being type-checked.
Note that behaviours of C-style variadic functions do not change, errors
and warnings are given as before.
This patch does it by:
1. Relaxing several conditions for format attribute:
- Will only use POSARG_ELLIPSIS flag to call `get_constant` when
getting attribute arguments of a variadic function
- Relax the check for the last argument of the attribute (will not
require an ellipsis argument)
- (Before this patch) After passing the above check, current gcc will
call `get_constant` to get the function parameter that the third attribute
argument is pointing to. If POSARG_ELLIPSIS is set, `get_constant` will
look for `...`. If not, `get_constant` will look for a C-style string. Note
that POSARG_ELLIPSIS is set automatically for getting the third attribute
argument.
(After this patch) POSARG_ELLIPSIS is set only when the function
has C-style '...'. Now, if POSARG_ELLIPSIS is not set, `get_constant` will
not check whether the third argument of format attribute points to a
C-style string.
2. Modifying expected outcome of a testcase in objc testsuite, where we
expect a warning instead of an error
3. Adding 2 test files
Successully bootstrapped and regression tested on x86_64-pc-linux-gnu.
Signed-off-by: Le Quang Tuan <lequangtuan6b@gmail.com>
gcc/c-family/ChangeLog:
* c-attribs.c (positional_argument): allow third argument of format
attribute to point to parameters of any type if the function is not C-style
variadic
* c-format.c (decode_format_attr): read third argument with POSARG_ELLIPSIS
only if the function has has a variable argument
(handle_format_attribute): relax explicit checks for non-variadic functions
gcc/testsuite/ChangeLog:
* gcc.dg/format/attr-3.c: modify comment
* objc.dg/attributes/method-format-1.m: errors do not hold anymore, a
warning is given instead
* g++.dg/warn/format9.C: New test with usage of variadic templates.
* gcc.dg/format/attr-9.c: New test.
diff --git a/gcc/c-family/c-attribs.c b/gcc/c-family/c-attribs.c
index 6bf492afcc0..7a17ce671de 100644
--- a/gcc/c-family/c-attribs.c
+++ b/gcc/c-family/c-attribs.c
@@ -714,6 +714,11 @@ positional_argument (const_tree fntype, const_tree
atname, tree pos,
return NULL_TREE;
}
+ /* For format attribute with argno >= 3, we don't expect any type
+ */
+ if (argno >= 3 && strcmp (IDENTIFIER_POINTER(atname), "format") == 0 &&
!(flags & POSARG_ELLIPSIS ) )
+ return pos;
+
/* Where the expected code is STRING_CST accept any pointer
expected by attribute format (this includes possibly qualified
char pointers and, for targets like Darwin, also pointers to
diff --git a/gcc/c-family/c-format.c b/gcc/c-family/c-format.c
index bda3b18fcd0..453565ad7b8 100644
--- a/gcc/c-family/c-format.c
+++ b/gcc/c-family/c-format.c
@@ -380,9 +380,15 @@ decode_format_attr (const_tree fntype, tree atname,
tree args,
else
return false;
+ bool has_variable_arg = !type_argument_type(fntype,
type_num_arguments(fntype) + 1);
+ int extra_flag = 0;
+ if (has_variable_arg) {
+ extra_flag = POSARG_ELLIPSIS;
+ }
+
if (tree val = get_constant (fntype, atname, *first_arg_num_expr,
3, &info->first_arg_num,
- (POSARG_ZERO | POSARG_ELLIPSIS), validated_p))
+ (POSARG_ZERO | extra_flag), validated_p))
*first_arg_num_expr = val;
else
return false;
@@ -5193,11 +5199,11 @@ handle_format_attribute (tree *node, tree atname,
tree args,
tree arg_type;
/* Verify that first_arg_num points to the last arg,
- the ... */
+ if the last arg is ... */
FOREACH_FUNCTION_ARGS (type, arg_type, iter)
arg_num++;
- if (arg_num != info.first_arg_num)
+ if (arg_num != info.first_arg_num && !type_argument_type(type, arg_num))
{
if (!(flags & (int) ATTR_FLAG_BUILT_IN))
error ("argument to be formatted is not %<...%>");
diff --git a/gcc/testsuite/g++.dg/warn/format9.C
b/gcc/testsuite/g++.dg/warn/format9.C
new file mode 100644
index 00000000000..39b615859fc
--- /dev/null
+++ b/gcc/testsuite/g++.dg/warn/format9.C
@@ -0,0 +1,16 @@
+// Test format attribute used with variadic templates
+// { dg-do compile { target c++11 } }
+// { dg-options "-Wformat" }
+
+template<typename...Args>
+__attribute__((format(printf, 1, 2))) void fa (const char * fmt,
Args...args)
+{
+ return;
+}
+
+int main()
+{
+ fa ("%d%d", 5, 7);
+ fa ("%d%d%d", 5, 6, 7);
+ fa ("%s%d", 3, 4); // { dg-warning "format" "printf warning" }
+}
diff --git a/gcc/testsuite/gcc.dg/format/attr-3.c
b/gcc/testsuite/gcc.dg/format/attr-3.c
index 1b275c5aba8..64990c43472 100644
--- a/gcc/testsuite/gcc.dg/format/attr-3.c
+++ b/gcc/testsuite/gcc.dg/format/attr-3.c
@@ -55,7 +55,7 @@ extern void fg2 () __attribute__((format(gnu_attr_printf,
1, 1))); /* { dg-error
extern void fg3 () __attribute__((format(gnu_attr_printf, 2, 1))); /* {
dg-error "follows" "bad number order" } */
/* The format string argument must be a string type, and the arguments to
- be formatted must be the "...". */
+ be formatted must be the "..." if the function is variadic */
extern void fh0 (int, ...) __attribute__((format(gnu_attr_printf, 1, 2)));
/* { dg-error ".format. attribute argument 2 value .1. refers to parameter
type .int." "format int string" } */
extern void fh1 (signed char *, ...)
__attribute__((format(gnu_attr_printf, 1, 2))); /* { dg-error ".format.
attribute argument 2 value .1. refers to parameter type .signed char \\\*."
"signed char string" } */
extern void fh2 (unsigned char *, ...)
__attribute__((format(gnu_attr_printf, 1, 2))); /* { dg-error ".format.
attribute argument 2 value .1. refers to parameter type .unsigned char
\\\*." "unsigned char string" } */
diff --git a/gcc/testsuite/gcc.dg/format/attr-9.c
b/gcc/testsuite/gcc.dg/format/attr-9.c
new file mode 100644
index 00000000000..3f6788f291c
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/format/attr-9.c
@@ -0,0 +1,21 @@
+/* Test for format attributes: test usage of the attribute for
non-variadic functions */
+/* Origin: Tuan Le <lequangtuan6b@gmail.com> */
+/* { dg-do compile } */
+/* { dg-options "-std=gnu99 -Wformat" } */
+
+#define DONT_GNU_PROTOTYPE
+#include "format.h"
+
+/* Usage of the attributes */
+extern void fa (const char *, int, const char *)
__attribute__((format(gnu_attr_printf, 1, 2)));
+extern void fb (const char *, int, int, int)
__attribute__((format(gnu_attr_printf, 1, 3)));
+
+/* Some bad uses */
+extern void fc (const char *, int) __attribute__((format(gnu_attr_printf,
1, 1))); /* { dg-error "format string argument follows the arguments to be
formatted" } */
+
+void
+foo (const char *s, int *p)
+{
+ fa ("%d%s", 5, "hello");
+ fa ("%d %d", 5, "hello"); /* { dg-warning "format" "attribute format
__printf__" } */
+}
diff --git a/gcc/testsuite/objc.dg/attributes/method-format-1.m
b/gcc/testsuite/objc.dg/attributes/method-format-1.m
index d3bb997b2aa..443f9c73120 100644
--- a/gcc/testsuite/objc.dg/attributes/method-format-1.m
+++ b/gcc/testsuite/objc.dg/attributes/method-format-1.m
@@ -20,8 +20,8 @@
- (void) log2: (int)level message: (const char *) my_format, ...
__attribute__ ((format (printf, 2))); /* { dg-error "wrong" } */
+ (void) debug2: (const char *) my_format, ... __attribute__ ((format
(printf))); /* { dg-error "wrong" } */
- (void) debug2: (const char *) my_format, ... __attribute__ ((format
(printf))); /* { dg-error "wrong" } */
-+ (void) alert: (const char *) my_format __attribute__ ((format (printf,
1, 2))); /* { dg-error "does not refer to a variable argument list" } */
-- (void) alert: (const char *) my_format __attribute__ ((format (printf,
1, 2))); /* { dg-error "does not refer to a variable argument list" } */
++ (void) alert: (const char *) my_format __attribute__ ((format (printf,
1, 2))); /* { dg-warning "exceeds the number of function parameters" } */
+- (void) alert: (const char *) my_format __attribute__ ((format (printf,
1, 2))); /* { dg-warning "exceeds the number of function parameters" } */
@end
void test (LogObject *object)
--
2.17.1
More information about the Gcc-patches
mailing list