]> granicus.if.org Git - clang/commitdiff
[Sema] Consider all format_arg attributes.
authorMichael Kruse <llvm@meinersbur.de>
Wed, 4 Jul 2018 01:37:11 +0000 (01:37 +0000)
committerMichael Kruse <llvm@meinersbur.de>
Wed, 4 Jul 2018 01:37:11 +0000 (01:37 +0000)
If a function has multiple format_arg attributes, clang only considers
the first it finds (because AttributeLists are in reverse order, not
necessarily the textually first) and ignores all others.

Loop over all FormatArgAttr to print warnings for all declared
format_arg attributes.

For instance, libintl's ngettext (select plural or singular version of
format string) has two __format_arg__ attributes.

Differential Revision: https://reviews.llvm.org/D48734

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@336239 91177308-0d34-0410-b5e6-96231b3b80d8

lib/Sema/SemaChecking.cpp
test/Sema/attr-format_arg.c

index 19acb32113cc849662930946812549bcaf3cee98..98aeb61fa751840168bcaf694a847855e1f2d03a 100644 (file)
@@ -5518,13 +5518,22 @@ checkFormatStringExpr(Sema &S, const Expr *E, ArrayRef<const Expr *> Args,
   case Stmt::CXXMemberCallExprClass: {
     const CallExpr *CE = cast<CallExpr>(E);
     if (const NamedDecl *ND = dyn_cast_or_null<NamedDecl>(CE->getCalleeDecl())) {
-      if (const FormatArgAttr *FA = ND->getAttr<FormatArgAttr>()) {
+      bool IsFirst = true;
+      StringLiteralCheckType CommonResult;
+      for (const auto *FA : ND->specific_attrs<FormatArgAttr>()) {
         const Expr *Arg = CE->getArg(FA->getFormatIdx().getASTIndex());
-        return checkFormatStringExpr(S, Arg, Args,
-                                     HasVAListArg, format_idx, firstDataArg,
-                                     Type, CallType, InFunctionCall,
-                                     CheckedVarArgs, UncoveredArg, Offset);
-      } else if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(ND)) {
+        StringLiteralCheckType Result = checkFormatStringExpr(
+            S, Arg, Args, HasVAListArg, format_idx, firstDataArg, Type,
+            CallType, InFunctionCall, CheckedVarArgs, UncoveredArg, Offset);
+        if (IsFirst) {
+          CommonResult = Result;
+          IsFirst = false;
+        }
+      }
+      if (!IsFirst)
+        return CommonResult;
+
+      if (const auto *FD = dyn_cast<FunctionDecl>(ND)) {
         unsigned BuiltinID = FD->getBuiltinID();
         if (BuiltinID == Builtin::BI__builtin___CFStringMakeConstantString ||
             BuiltinID == Builtin::BI__builtin___NSStringMakeConstantString) {
index 64a23878317c6285b7e4a34285b3ebdab4027431..e041c5acbffa5682ec3c9c4909c10f9a998e769c 100644 (file)
@@ -4,10 +4,27 @@ int printf(const char *, ...);
 
 const char* f(const char *s) __attribute__((format_arg(1)));
 
+const char *h(const char *msg1, const char *msg2)
+    __attribute__((__format_arg__(1))) __attribute__((__format_arg__(2)));
+
 void g(const char *s) {
   printf("%d", 123);
   printf("%d %d", 123); // expected-warning{{more '%' conversions than data arguments}}
 
   printf(f("%d"), 123);
   printf(f("%d %d"), 123); // expected-warning{{more '%' conversions than data arguments}}
+
+  printf(h(
+    "", // expected-warning {{format string is empty}}
+    ""  // expected-warning {{format string is empty}}
+  ), 123);
+  printf(h(
+    "%d",
+    ""  // expected-warning {{format string is empty}}
+  ), 123);
+  printf(h(
+    "", // expected-warning {{format string is empty}}
+    "%d"
+  ), 123);
+  printf(h("%d", "%d"), 123);
 }