Skip to content

Commit d378a0f

Browse files
[Sema] Recognize format argument indicated by format attribute inside blocks
- `[[format(archetype, fmt-idx, ellipsis)]]` specifies that a function accepts a format string and arguments according to `archetype`. This is how Clang type-checks `printf` arguments based on the format string. - Clang has a `-Wformat-nonliteral` warning that is triggered when a function with the `format` attribute is called with a format string that is not inspectable because it isn't constant. This warning is suppressed if the caller has the `format` attribute itself and the format argument to the callee is the caller's own format parameter. - When using the `format` attribute on a block, Clang wouldn't recognize its format parameter when calling another function with the format attribute. This would cause unsuppressed -Wformat-nonliteral warnings for no supported reason. Reviewed By: ahatanak Differential Revision: https://reviews.llvm.org/D112569 Radar-Id: rdar://84603673
1 parent e6a4ba3 commit d378a0f

File tree

2 files changed

+32
-5
lines changed

2 files changed

+32
-5
lines changed

clang/lib/Sema/SemaChecking.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7784,11 +7784,11 @@ checkFormatStringExpr(Sema &S, const Expr *E, ArrayRef<const Expr *> Args,
77847784
// }
77857785
if (HasVAListArg) {
77867786
if (const ParmVarDecl *PV = dyn_cast<ParmVarDecl>(VD)) {
7787-
if (const NamedDecl *ND = dyn_cast<NamedDecl>(PV->getDeclContext())) {
7787+
if (const Decl *D = dyn_cast<Decl>(PV->getDeclContext())) {
77887788
int PVIndex = PV->getFunctionScopeIndex() + 1;
7789-
for (const auto *PVFormat : ND->specific_attrs<FormatAttr>()) {
7789+
for (const auto *PVFormat : D->specific_attrs<FormatAttr>()) {
77907790
// adjust for implicit parameter
7791-
if (const CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(ND))
7791+
if (const CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(D))
77927792
if (MD->isInstance())
77937793
++PVIndex;
77947794
// We also check if the formats are compatible.

clang/test/Sema/format-strings.c

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
// RUN: %clang_cc1 -fsyntax-only -verify -Wformat-nonliteral -isystem %S/Inputs %s
2-
// RUN: %clang_cc1 -fsyntax-only -verify -Wformat-nonliteral -isystem %S/Inputs -fno-signed-char %s
1+
// RUN: %clang_cc1 -fblocks -fsyntax-only -verify -Wformat-nonliteral -isystem %S/Inputs %s
2+
// RUN: %clang_cc1 -fblocks -fsyntax-only -verify -Wformat-nonliteral -isystem %S/Inputs -fno-signed-char %s
33

44
#include <stdarg.h>
55
#include <stddef.h>
@@ -714,3 +714,30 @@ void PR30481() {
714714
void test_printf_opaque_ptr(void *op) {
715715
printf("%s", op); // expected-warning{{format specifies type 'char *' but the argument has type 'void *'}}
716716
}
717+
718+
void test_block() {
719+
void __attribute__((__format__(__printf__, 1, 2))) (^printf_arg1)(
720+
const char *, ...) =
721+
^(const char *fmt, ...) __attribute__((__format__(__printf__, 1, 2))) {
722+
va_list ap;
723+
va_start(ap, fmt);
724+
vprintf(fmt, ap);
725+
va_end(ap);
726+
};
727+
728+
printf_arg1("%s string %i\n", "aaa", 123);
729+
printf_arg1("%s string\n", 123); // expected-warning{{format specifies type 'char *' but the argument has type 'int'}}
730+
731+
void __attribute__((__format__(__printf__, 2, 3))) (^printf_arg2)(
732+
const char *, const char *, ...) =
733+
^(const char *not_fmt, const char *fmt, ...)
734+
__attribute__((__format__(__printf__, 2, 3))) {
735+
va_list ap;
736+
va_start(ap, fmt);
737+
vprintf(not_fmt, ap); // expected-warning{{format string is not a string literal}}
738+
va_end(ap);
739+
};
740+
741+
printf_arg2("foo", "%s string %i\n", "aaa", 123);
742+
printf_arg2("%s string\n", "foo", "bar"); // expected-warning{{data argument not used by format string}}
743+
}

0 commit comments

Comments
 (0)