9898#include "llvm/Support/Locale.h"
9999#include "llvm/Support/MathExtras.h"
100100#include "llvm/Support/SaveAndRestore.h"
101+ #include "llvm/Support/SmallVectorMemoryBuffer.h"
101102#include "llvm/Support/raw_ostream.h"
102103#include "llvm/TargetParser/RISCVTargetParser.h"
103104#include "llvm/TargetParser/Triple.h"
@@ -5935,8 +5936,14 @@ static void CheckFormatString(
59355936 llvm::SmallBitVector &CheckedVarArgs, UncoveredArgHandler &UncoveredArg,
59365937 bool IgnoreStringsWithoutSpecifiers);
59375938
5938- static const Expr *maybeConstEvalStringLiteral(ASTContext &Context,
5939- const Expr *E);
5939+ enum StringLiteralConstEvalResult {
5940+ SLCER_NotEvaluated,
5941+ SLCER_NotNullTerminated,
5942+ SLCER_Evaluated,
5943+ };
5944+
5945+ static StringLiteralConstEvalResult
5946+ constEvalStringAsLiteral(Sema &S, const Expr *E, const StringLiteral *&SL);
59405947
59415948// Determine if an expression is a string literal or constant string.
59425949// If this function returns false on the arguments to a function expecting a
@@ -5968,14 +5975,9 @@ static StringLiteralCheckType checkFormatStringExpr(
59685975
59695976 switch (E->getStmtClass()) {
59705977 case Stmt::InitListExprClass:
5971- // Handle expressions like {"foobar"}.
5972- if (const clang::Expr *SLE = maybeConstEvalStringLiteral(S.Context, E)) {
5973- return checkFormatStringExpr(
5974- S, ReferenceFormatString, SLE, Args, APK, format_idx, firstDataArg,
5975- Type, CallType, /*InFunctionCall*/ false, CheckedVarArgs,
5976- UncoveredArg, Offset, IgnoreStringsWithoutSpecifiers);
5977- }
5978- return SLCT_NotALiteral;
5978+ // try to constant-evaluate the string
5979+ break;
5980+
59795981 case Stmt::BinaryConditionalOperatorClass:
59805982 case Stmt::ConditionalOperatorClass: {
59815983 // The expression is a literal if both sub-expressions were, and it was
@@ -6066,10 +6068,9 @@ static StringLiteralCheckType checkFormatStringExpr(
60666068 if (InitList->isStringLiteralInit())
60676069 Init = InitList->getInit(0)->IgnoreParenImpCasts();
60686070 }
6069- return checkFormatStringExpr(
6070- S, ReferenceFormatString, Init, Args, APK, format_idx,
6071- firstDataArg, Type, CallType,
6072- /*InFunctionCall*/ false, CheckedVarArgs, UncoveredArg, Offset);
6071+ InFunctionCall = false;
6072+ E = Init;
6073+ goto tryAgain;
60736074 }
60746075 }
60756076
@@ -6142,11 +6143,9 @@ static StringLiteralCheckType checkFormatStringExpr(
61426143 }
61436144 return SLCT_UncheckedLiteral;
61446145 }
6145- return checkFormatStringExpr(
6146- S, ReferenceFormatString, PVFormatMatches->getFormatString(),
6147- Args, APK, format_idx, firstDataArg, Type, CallType,
6148- /*InFunctionCall*/ false, CheckedVarArgs, UncoveredArg,
6149- Offset, IgnoreStringsWithoutSpecifiers);
6146+ E = PVFormatMatches->getFormatString();
6147+ InFunctionCall = false;
6148+ goto tryAgain;
61506149 }
61516150 }
61526151
@@ -6214,20 +6213,13 @@ static StringLiteralCheckType checkFormatStringExpr(
62146213 unsigned BuiltinID = FD->getBuiltinID();
62156214 if (BuiltinID == Builtin::BI__builtin___CFStringMakeConstantString ||
62166215 BuiltinID == Builtin::BI__builtin___NSStringMakeConstantString) {
6217- const Expr *Arg = CE->getArg(0);
6218- return checkFormatStringExpr(
6219- S, ReferenceFormatString, Arg, Args, APK, format_idx,
6220- firstDataArg, Type, CallType, InFunctionCall, CheckedVarArgs,
6221- UncoveredArg, Offset, IgnoreStringsWithoutSpecifiers);
6216+ E = CE->getArg(0);
6217+ goto tryAgain;
62226218 }
62236219 }
62246220 }
6225- if (const Expr *SLE = maybeConstEvalStringLiteral(S.Context, E))
6226- return checkFormatStringExpr(
6227- S, ReferenceFormatString, SLE, Args, APK, format_idx, firstDataArg,
6228- Type, CallType, /*InFunctionCall*/ false, CheckedVarArgs,
6229- UncoveredArg, Offset, IgnoreStringsWithoutSpecifiers);
6230- return SLCT_NotALiteral;
6221+ // try to constant-evaluate the string
6222+ break;
62316223 }
62326224 case Stmt::ObjCMessageExprClass: {
62336225 const auto *ME = cast<ObjCMessageExpr>(E);
@@ -6248,11 +6240,8 @@ static StringLiteralCheckType checkFormatStringExpr(
62486240 IgnoreStringsWithoutSpecifiers = true;
62496241 }
62506242
6251- const Expr *Arg = ME->getArg(FA->getFormatIdx().getASTIndex());
6252- return checkFormatStringExpr(
6253- S, ReferenceFormatString, Arg, Args, APK, format_idx, firstDataArg,
6254- Type, CallType, InFunctionCall, CheckedVarArgs, UncoveredArg,
6255- Offset, IgnoreStringsWithoutSpecifiers);
6243+ E = ME->getArg(FA->getFormatIdx().getASTIndex());
6244+ goto tryAgain;
62566245 }
62576246 }
62586247
@@ -6314,7 +6303,8 @@ static StringLiteralCheckType checkFormatStringExpr(
63146303 }
63156304 }
63166305
6317- return SLCT_NotALiteral;
6306+ // try to constant-evaluate the string
6307+ break;
63186308 }
63196309 case Stmt::UnaryOperatorClass: {
63206310 const UnaryOperator *UnaOp = cast<UnaryOperator>(E);
@@ -6331,26 +6321,79 @@ static StringLiteralCheckType checkFormatStringExpr(
63316321 }
63326322 }
63336323
6334- return SLCT_NotALiteral;
6324+ // try to constant-evaluate the string
6325+ break;
63356326 }
63366327
63376328 default:
6329+ // try to constant-evaluate the string
6330+ break;
6331+ }
6332+
6333+ const StringLiteral *FakeLiteral = nullptr;
6334+ switch (constEvalStringAsLiteral(S, E, FakeLiteral)) {
6335+ case SLCER_NotEvaluated:
63386336 return SLCT_NotALiteral;
6337+
6338+ case SLCER_NotNullTerminated:
6339+ S.Diag(Args[format_idx]->getBeginLoc(),
6340+ diag::warn_printf_format_string_not_null_terminated)
6341+ << Args[format_idx]->getSourceRange();
6342+ if (!InFunctionCall)
6343+ S.Diag(E->getBeginLoc(), diag::note_format_string_defined);
6344+ // Stop checking, as this might just mean we're missing a chunk of the
6345+ // format string and there would be other spurious format issues.
6346+ return SLCT_UncheckedLiteral;
6347+
6348+ case SLCER_Evaluated:
6349+ InFunctionCall = false;
6350+ E = FakeLiteral;
6351+ goto tryAgain;
63396352 }
63406353}
63416354
6342- // If this expression can be evaluated at compile-time,
6343- // check if the result is a StringLiteral and return it
6344- // otherwise return nullptr
6345- static const Expr *maybeConstEvalStringLiteral(ASTContext &Context,
6346- const Expr *E) {
6355+ static StringLiteralConstEvalResult
6356+ constEvalStringAsLiteral(Sema &S, const Expr *E, const StringLiteral *&SL) {
6357+ // As a last resort, try to constant-evaluate the format string. If it
6358+ // evaluates to a string literal in the first place, we can point to that
6359+ // string literal in source and use that.
63476360 Expr::EvalResult Result;
6348- if (E->EvaluateAsRValue(Result, Context) && Result.Val.isLValue()) {
6361+ if (E->EvaluateAsRValue(Result, S. Context) && Result.Val.isLValue()) {
63496362 const auto *LVE = Result.Val.getLValueBase().dyn_cast<const Expr *>();
6350- if (isa_and_nonnull<StringLiteral>(LVE))
6351- return LVE;
6363+ if (auto *BaseSL = dyn_cast_or_null<StringLiteral>(LVE)) {
6364+ SL = BaseSL;
6365+ return SLCER_Evaluated;
6366+ }
63526367 }
6353- return nullptr;
6368+
6369+ // Otherwise, try to evaluate the expression as a string constant.
6370+ std::string FormatString;
6371+ if (!E->tryEvaluateString(S.Context, FormatString)) {
6372+ return FormatString.empty() ? SLCER_NotEvaluated : SLCER_NotNullTerminated;
6373+ }
6374+
6375+ std::unique_ptr<llvm::MemoryBuffer> MemBuf;
6376+ {
6377+ llvm::SmallString<80> EscapedString;
6378+ {
6379+ llvm::raw_svector_ostream OS(EscapedString);
6380+ OS << '"';
6381+ OS.write_escaped(FormatString);
6382+ OS << '"';
6383+ }
6384+ MemBuf.reset(new llvm::SmallVectorMemoryBuffer(std::move(EscapedString),
6385+ "<scratch space>", true));
6386+ }
6387+
6388+ // Plop that string into a scratch buffer, create a string literal and then
6389+ // go with that.
6390+ auto ScratchFile = S.getSourceManager().createFileID(std::move(MemBuf));
6391+ SourceLocation Begin = S.getSourceManager().getLocForStartOfFile(ScratchFile);
6392+ QualType SLType = S.Context.getStringLiteralArrayType(S.Context.CharTy,
6393+ FormatString.length());
6394+ SL = StringLiteral::Create(S.Context, FormatString,
6395+ StringLiteralKind::Ordinary, false, SLType, Begin);
6396+ return SLCER_Evaluated;
63546397}
63556398
63566399StringRef Sema::GetFormatStringTypeName(Sema::FormatStringType FST) {
@@ -6973,10 +7016,11 @@ void CheckFormatHandler::EmitFormatDiagnostic(
69737016 S.Diag(IsStringLocation ? ArgumentExpr->getExprLoc() : Loc, PDiag)
69747017 << ArgumentExpr->getSourceRange();
69757018
6976- const Sema::SemaDiagnosticBuilder &Note =
6977- S.Diag(IsStringLocation ? Loc : StringRange.getBegin(),
6978- diag::note_format_string_defined);
6979-
7019+ SourceLocation DiagLoc = IsStringLocation ? Loc : StringRange.getBegin();
7020+ unsigned DiagID = S.getSourceManager().isWrittenInScratchSpace(DiagLoc)
7021+ ? diag::note_format_string_evaluated_to
7022+ : diag::note_format_string_defined;
7023+ const Sema::SemaDiagnosticBuilder &Note = S.Diag(DiagLoc, DiagID);
69807024 Note << StringRange;
69817025 Note << FixIt;
69827026 }
0 commit comments