diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -3045,8 +3045,6 @@ "declared with index %0 here">; def err_format_strftime_third_parameter : Error< "strftime format attribute requires 3rd parameter to be 0">; -def err_format_attribute_requires_variadic : Error< - "format attribute requires variadic function">; def err_format_attribute_not : Error<"format argument not %0">; def err_format_attribute_result_not : Error<"function does not return %0">; def err_format_attribute_implicit_this_format_string : Error< @@ -4049,6 +4047,9 @@ def warn_gcc_ignores_type_attr : Warning< "GCC does not allow the %0 attribute to be written on a type">, InGroup; +def warn_gcc_requires_variadic_function : Warning< + "GCC requires functions with %0 attribute to be variadic">, + InGroup; // Clang-Specific Attributes def warn_attribute_iboutlet : Warning< diff --git a/clang/lib/AST/FormatString.cpp b/clang/lib/AST/FormatString.cpp --- a/clang/lib/AST/FormatString.cpp +++ b/clang/lib/AST/FormatString.cpp @@ -322,6 +322,12 @@ clang::analyze_format_string::ArgType::MatchKind ArgType::matchesType(ASTContext &C, QualType argTy) const { + // When using the format attribute with variadic templates in C++, you can + // receive an array that will necessarily decay to a pointer when passed to + // the final format consumer. Apply decay before type comparison. + if (C.getAsArrayType(argTy)) + argTy = C.getArrayDecayedType(argTy); + if (Ptr) { // It has to be a pointer. const PointerType *PT = argTy->getAs(); diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp --- a/clang/lib/Sema/SemaDeclAttr.cpp +++ b/clang/lib/Sema/SemaDeclAttr.cpp @@ -3612,8 +3612,7 @@ if (isFunctionOrMethodVariadic(D)) { ++NumArgs; // +1 for ... } else { - S.Diag(D->getLocation(), diag::err_format_attribute_requires_variadic); - return; + S.Diag(D->getLocation(), diag::warn_gcc_requires_variadic_function) << AL; } } diff --git a/clang/test/Sema/attr-format.c b/clang/test/Sema/attr-format.c --- a/clang/test/Sema/attr-format.c +++ b/clang/test/Sema/attr-format.c @@ -5,7 +5,7 @@ void a(const char *a, ...) __attribute__((format(printf, 1,2))); // no-error void b(const char *a, ...) __attribute__((format(printf, 1,1))); // expected-error {{'format' attribute parameter 3 is out of bounds}} void c(const char *a, ...) __attribute__((format(printf, 0,2))); // expected-error {{'format' attribute parameter 2 is out of bounds}} -void d(const char *a, int c) __attribute__((format(printf, 1,2))); // expected-error {{format attribute requires variadic function}} +void d(const char *a, int c) __attribute__((format(printf, 1, 2))); // expected-warning {{GCC requires functions with 'format' attribute to be variadic}} void e(char *str, int c, ...) __attribute__((format(printf, 2,3))); // expected-error {{format argument not a string type}} typedef const char* xpto; @@ -39,7 +39,7 @@ void a2(const char *a, ...) __attribute__((format(printf0, 1,2))); // no-error void b2(const char *a, ...) __attribute__((format(printf0, 1,1))); // expected-error {{'format' attribute parameter 3 is out of bounds}} void c2(const char *a, ...) __attribute__((format(printf0, 0,2))); // expected-error {{'format' attribute parameter 2 is out of bounds}} -void d2(const char *a, int c) __attribute__((format(printf0, 1,2))); // expected-error {{format attribute requires variadic function}} +void d2(const char *a, int c) __attribute__((format(printf0, 1,2))); // expected-warning {{GCC requires functions with 'format' attribute to be variadic}} void e2(char *str, int c, ...) __attribute__((format(printf0, 2,3))); // expected-error {{format argument not a string type}} // FreeBSD usage @@ -61,7 +61,7 @@ void a3(const char *a, ...) __attribute__((format(freebsd_kprintf, 1,2))); // no-error void b3(const char *a, ...) __attribute__((format(freebsd_kprintf, 1,1))); // expected-error {{'format' attribute parameter 3 is out of bounds}} void c3(const char *a, ...) __attribute__((format(freebsd_kprintf, 0,2))); // expected-error {{'format' attribute parameter 2 is out of bounds}} -void d3(const char *a, int c) __attribute__((format(freebsd_kprintf, 1,2))); // expected-error {{format attribute requires variadic function}} +void d3(const char *a, int c) __attribute__((format(freebsd_kprintf, 1,2))); // expected-warning {{GCC requires functions with 'format' attribute to be variadic}} void e3(char *str, int c, ...) __attribute__((format(freebsd_kprintf, 2,3))); // expected-error {{format argument not a string type}} @@ -87,3 +87,9 @@ __attribute__ ((__format__(__gcc_tdiag__, 1, 2))); const char *foo3(const char *format) __attribute__((format_arg("foo"))); // expected-error{{'format_arg' attribute requires parameter 1 to be an integer constant}} + +void call_nonvariadic(void) { + d3("%i", 123); + d3("%d", 123); + d3("%s", 123); // expected-warning{{format specifies type 'char *' but the argument has type 'int'}} +} diff --git a/clang/test/Sema/attr-format.cpp b/clang/test/Sema/attr-format.cpp new file mode 100644 --- /dev/null +++ b/clang/test/Sema/attr-format.cpp @@ -0,0 +1,16 @@ +//RUN: %clang_cc1 -fsyntax-only -verify %s + +#include + +template +void format(const char *fmt, Args &&... args) // expected-warning{{GCC requires functions with 'format' attribute to be variadic}} + __attribute__((format(printf, 1, 2))); + +void do_format() { + int x = 123; + int &y = x; + const char *s = "world"; + format("bare string"); + format("%s %s %u %d %i %p\n", "hello", s, 10u, x, y, &do_format); + format("bad format %s"); // expected-warning{{more '%' conversions than data arguments}} +}