fcloutier created this revision. fcloutier added reviewers: doug.gregor, dcoughlin, rsmith. fcloutier added a project: clang. fcloutier requested review of this revision. Herald added a subscriber: cfe-commits.
The checker that implements `-Wformat-nonliteral` does not understand `__attribute__((format))` on blocks in the same way that it understands it on functions. This works just fine (assuming `#define __printflike(A, B) __attribute__((format(printf, A, B)))`): void printfblock(const char *fmt, ...) __printflike(1, 2) { va_list ap; va_start(ap, fmt); vprintf(fmt, ap); va_end(ap); } void foo(void) { printfblock("%s %i\n", "hello", 1); } However, this incorrectly triggers `-Wformat-nonliteral`: void foo(void) { void (^printfblock)(const char *fmt, ...) __printflike(1, 2) = ^(const char *fmt, ...) __printflike(1, 2) { va_list ap; va_start(ap, fmt); vprintf(fmt, ap); // warning: format string is not a string literal [-Wformat-nonliteral] va_end(ap); }; printfblock("%s %i\n", "hello", 1); } This patch updates `checkFormatStringExpr` so that it can look through `BlockDecl`s and find out which parameter is identified as a format string. rdar://84603673 Repository: rG LLVM Github Monorepo https://reviews.llvm.org/D112569 Files: clang/lib/Sema/SemaChecking.cpp clang/test/Sema/format-strings.c Index: clang/test/Sema/format-strings.c =================================================================== --- clang/test/Sema/format-strings.c +++ clang/test/Sema/format-strings.c @@ -1,5 +1,5 @@ -// RUN: %clang_cc1 -fsyntax-only -verify -Wformat-nonliteral -isystem %S/Inputs %s -// RUN: %clang_cc1 -fsyntax-only -verify -Wformat-nonliteral -isystem %S/Inputs -fno-signed-char %s +// RUN: %clang_cc1 -fblocks -fsyntax-only -verify -Wformat-nonliteral -isystem %S/Inputs %s +// RUN: %clang_cc1 -fblocks -fsyntax-only -verify -Wformat-nonliteral -isystem %S/Inputs -fno-signed-char %s #include <stdarg.h> #include <stddef.h> @@ -714,3 +714,16 @@ void test_printf_opaque_ptr(void *op) { printf("%s", op); // expected-warning{{format specifies type 'char *' but the argument has type 'void *'}} } + +void test_block() { + void __attribute__((__format__(__printf__, 1, 2))) (^printf_block)(const char *, ...) = + ^(const char *fmt, ...) __attribute__((__format__(__printf__, 1, 2))) { + va_list ap; + va_start(ap, fmt); + vprintf(fmt, ap); + va_end(ap); + }; + + printf_block("%s string %i\n", "aaa", 123); + printf_block("%s string\n", 123); // expected-warning{{format specifies type 'char *' but the argument has type 'int'}} +} Index: clang/lib/Sema/SemaChecking.cpp =================================================================== --- clang/lib/Sema/SemaChecking.cpp +++ clang/lib/Sema/SemaChecking.cpp @@ -7780,13 +7780,16 @@ // } if (HasVAListArg) { if (const ParmVarDecl *PV = dyn_cast<ParmVarDecl>(VD)) { - if (const NamedDecl *ND = dyn_cast<NamedDecl>(PV->getDeclContext())) { - int PVIndex = PV->getFunctionScopeIndex() + 1; - for (const auto *PVFormat : ND->specific_attrs<FormatAttr>()) { + if (const Decl *D = dyn_cast<Decl>(PV->getDeclContext())) { + int PVIndex = 1; + if (const NamedDecl *ND = dyn_cast<NamedDecl>(D)) { + PVIndex += PV->getFunctionScopeIndex(); // adjust for implicit parameter if (const CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(ND)) if (MD->isInstance()) ++PVIndex; + } + for (const auto *PVFormat : D->specific_attrs<FormatAttr>()) { // We also check if the formats are compatible. // We can't pass a 'scanf' string to a 'printf' function. if (PVIndex == PVFormat->getFormatIdx() &&
Index: clang/test/Sema/format-strings.c =================================================================== --- clang/test/Sema/format-strings.c +++ clang/test/Sema/format-strings.c @@ -1,5 +1,5 @@ -// RUN: %clang_cc1 -fsyntax-only -verify -Wformat-nonliteral -isystem %S/Inputs %s -// RUN: %clang_cc1 -fsyntax-only -verify -Wformat-nonliteral -isystem %S/Inputs -fno-signed-char %s +// RUN: %clang_cc1 -fblocks -fsyntax-only -verify -Wformat-nonliteral -isystem %S/Inputs %s +// RUN: %clang_cc1 -fblocks -fsyntax-only -verify -Wformat-nonliteral -isystem %S/Inputs -fno-signed-char %s #include <stdarg.h> #include <stddef.h> @@ -714,3 +714,16 @@ void test_printf_opaque_ptr(void *op) { printf("%s", op); // expected-warning{{format specifies type 'char *' but the argument has type 'void *'}} } + +void test_block() { + void __attribute__((__format__(__printf__, 1, 2))) (^printf_block)(const char *, ...) = + ^(const char *fmt, ...) __attribute__((__format__(__printf__, 1, 2))) { + va_list ap; + va_start(ap, fmt); + vprintf(fmt, ap); + va_end(ap); + }; + + printf_block("%s string %i\n", "aaa", 123); + printf_block("%s string\n", 123); // expected-warning{{format specifies type 'char *' but the argument has type 'int'}} +} Index: clang/lib/Sema/SemaChecking.cpp =================================================================== --- clang/lib/Sema/SemaChecking.cpp +++ clang/lib/Sema/SemaChecking.cpp @@ -7780,13 +7780,16 @@ // } if (HasVAListArg) { if (const ParmVarDecl *PV = dyn_cast<ParmVarDecl>(VD)) { - if (const NamedDecl *ND = dyn_cast<NamedDecl>(PV->getDeclContext())) { - int PVIndex = PV->getFunctionScopeIndex() + 1; - for (const auto *PVFormat : ND->specific_attrs<FormatAttr>()) { + if (const Decl *D = dyn_cast<Decl>(PV->getDeclContext())) { + int PVIndex = 1; + if (const NamedDecl *ND = dyn_cast<NamedDecl>(D)) { + PVIndex += PV->getFunctionScopeIndex(); // adjust for implicit parameter if (const CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(ND)) if (MD->isInstance()) ++PVIndex; + } + for (const auto *PVFormat : D->specific_attrs<FormatAttr>()) { // We also check if the formats are compatible. // We can't pass a 'scanf' string to a 'printf' function. if (PVIndex == PVFormat->getFormatIdx() &&
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits