[clang] d378a0f - [Sema] Recognize format argument indicated by format attribute inside blocks

Félix Cloutier via cfe-commits cfe-commits at lists.llvm.org
Wed Oct 27 15:48:44 PDT 2021


Author: Félix Cloutier
Date: 2021-10-27T15:48:35-07:00
New Revision: d378a0febc7e13aae7853b2e8733626f61140e55

URL: https://github.com/llvm/llvm-project/commit/d378a0febc7e13aae7853b2e8733626f61140e55
DIFF: https://github.com/llvm/llvm-project/commit/d378a0febc7e13aae7853b2e8733626f61140e55.diff

LOG: [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

Added: 
    

Modified: 
    clang/lib/Sema/SemaChecking.cpp
    clang/test/Sema/format-strings.c

Removed: 
    


################################################################################
diff  --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp
index 83fb824cb14a6..3eaeae197648a 100644
--- a/clang/lib/Sema/SemaChecking.cpp
+++ b/clang/lib/Sema/SemaChecking.cpp
@@ -7784,11 +7784,11 @@ checkFormatStringExpr(Sema &S, const Expr *E, ArrayRef<const Expr *> Args,
       // }
       if (HasVAListArg) {
         if (const ParmVarDecl *PV = dyn_cast<ParmVarDecl>(VD)) {
-          if (const NamedDecl *ND = dyn_cast<NamedDecl>(PV->getDeclContext())) {
+          if (const Decl *D = dyn_cast<Decl>(PV->getDeclContext())) {
             int PVIndex = PV->getFunctionScopeIndex() + 1;
-            for (const auto *PVFormat : ND->specific_attrs<FormatAttr>()) {
+            for (const auto *PVFormat : D->specific_attrs<FormatAttr>()) {
               // adjust for implicit parameter
-              if (const CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(ND))
+              if (const CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(D))
                 if (MD->isInstance())
                   ++PVIndex;
               // We also check if the formats are compatible.

diff  --git a/clang/test/Sema/format-strings.c b/clang/test/Sema/format-strings.c
index e8cd478d25126..bbe47636ebb7d 100644
--- a/clang/test/Sema/format-strings.c
+++ b/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,30 @@ void PR30481() {
 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_arg1)(
+      const char *, ...) =
+      ^(const char *fmt, ...) __attribute__((__format__(__printf__, 1, 2))) {
+    va_list ap;
+    va_start(ap, fmt);
+    vprintf(fmt, ap);
+    va_end(ap);
+  };
+
+  printf_arg1("%s string %i\n", "aaa", 123);
+  printf_arg1("%s string\n", 123); // expected-warning{{format specifies type 'char *' but the argument has type 'int'}}
+
+  void __attribute__((__format__(__printf__, 2, 3))) (^printf_arg2)(
+      const char *, const char *, ...) =
+      ^(const char *not_fmt, const char *fmt, ...)
+          __attribute__((__format__(__printf__, 2, 3))) {
+    va_list ap;
+    va_start(ap, fmt);
+    vprintf(not_fmt, ap); // expected-warning{{format string is not a string literal}}
+    va_end(ap);
+  };
+
+  printf_arg2("foo", "%s string %i\n", "aaa", 123);
+  printf_arg2("%s string\n", "foo", "bar"); // expected-warning{{data argument not used by format string}}
+}


        


More information about the cfe-commits mailing list