From f18adbb3cb327d119e6ba595a211afcc4a6ba196 Mon Sep 17 00:00:00 2001 From: Michael Kruse Date: Wed, 4 Jul 2018 01:37:11 +0000 Subject: [PATCH] [Sema] Consider all format_arg attributes. 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 llvm-svn: 336239 --- clang/lib/Sema/SemaChecking.cpp | 21 +++++++++++++++------ clang/test/Sema/attr-format_arg.c | 17 +++++++++++++++++ 2 files changed, 32 insertions(+), 6 deletions(-) diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp index 19acb32..98aeb61 100644 --- a/clang/lib/Sema/SemaChecking.cpp +++ b/clang/lib/Sema/SemaChecking.cpp @@ -5518,13 +5518,22 @@ checkFormatStringExpr(Sema &S, const Expr *E, ArrayRef Args, case Stmt::CXXMemberCallExprClass: { const CallExpr *CE = cast(E); if (const NamedDecl *ND = dyn_cast_or_null(CE->getCalleeDecl())) { - if (const FormatArgAttr *FA = ND->getAttr()) { + bool IsFirst = true; + StringLiteralCheckType CommonResult; + for (const auto *FA : ND->specific_attrs()) { 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(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(ND)) { unsigned BuiltinID = FD->getBuiltinID(); if (BuiltinID == Builtin::BI__builtin___CFStringMakeConstantString || BuiltinID == Builtin::BI__builtin___NSStringMakeConstantString) { diff --git a/clang/test/Sema/attr-format_arg.c b/clang/test/Sema/attr-format_arg.c index 64a2387..e041c5a 100644 --- a/clang/test/Sema/attr-format_arg.c +++ b/clang/test/Sema/attr-format_arg.c @@ -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); } -- 2.7.4