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
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits