diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp --- a/clang/lib/Sema/SemaChecking.cpp +++ b/clang/lib/Sema/SemaChecking.cpp @@ -8585,6 +8585,15 @@ return SLCT_UncheckedLiteral; switch (E->getStmtClass()) { + case Stmt::InitListExprClass: + // Handle expressions like {"foobar"}. + if (const clang::Expr *SLE = maybeConstEvalStringLiteral(S.Context, E)) { + return checkFormatStringExpr(S, SLE, Args, APK, format_idx, firstDataArg, + Type, CallType, /*InFunctionCall*/ false, + CheckedVarArgs, UncoveredArg, Offset, + IgnoreStringsWithoutSpecifiers); + } + return SLCT_NotALiteral; case Stmt::BinaryConditionalOperatorClass: case Stmt::ConditionalOperatorClass: { // The expression is a literal if both sub-expressions were, and it was diff --git a/clang/test/SemaCXX/format-strings.cpp b/clang/test/SemaCXX/format-strings.cpp --- a/clang/test/SemaCXX/format-strings.cpp +++ b/clang/test/SemaCXX/format-strings.cpp @@ -203,6 +203,12 @@ printf(string_linebreak(), 1, 2, 3, 4); // expected-warning {{format specifies type 'char *' but the argument has type 'int'}} printf(not_literal(), 1, 2, 3, 4); // expected-warning {{format string is not a string literal}} printf(wrap_constexpr(), 1, 2); // expected-warning {{format specifies type 'char *' but the argument has type 'int'}} + + constexpr const char *fmt {"%d%d"}; + printf(fmt, 1, 1); // no-warning + + constexpr const char *fmt2 {"%d"}; // expected-note{{format string is defined here}} + printf(fmt2, "oops"); // expected-warning{{format specifies type 'int' but the argument has type}} }