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 @@ -8469,25 +8469,26 @@ Sema &S, const FormatStringLiteral *FExpr, const Expr *OrigFormatExpr, ArrayRef Args, Sema::FormatArgumentPassingKind APK, unsigned format_idx, unsigned firstDataArg, Sema::FormatStringType Type, - bool inFunctionCall, Sema::VariadicCallType CallType, + bool NoNote, Sema::VariadicCallType CallType, llvm::SmallBitVector &CheckedVarArgs, UncoveredArgHandler &UncoveredArg, bool IgnoreStringsWithoutSpecifiers); -static const Expr *maybeConstEvalStringLiteral(ASTContext &Context, - const Expr *E); - -// Determine if an expression is a string literal or constant string. -// If this function returns false on the arguments to a function expecting a -// format string, we will usually need to emit a warning. -// True string literals are then checked by CheckFormatString. static StringLiteralCheckType -checkFormatStringExpr(Sema &S, const Expr *E, ArrayRef Args, - Sema::FormatArgumentPassingKind APK, unsigned format_idx, - unsigned firstDataArg, Sema::FormatStringType Type, - Sema::VariadicCallType CallType, bool InFunctionCall, - llvm::SmallBitVector &CheckedVarArgs, - UncoveredArgHandler &UncoveredArg, llvm::APSInt Offset, - bool IgnoreStringsWithoutSpecifiers = false) { +checkVarDecl(Sema &S, const Expr *E, ArrayRef Args, + Sema::FormatArgumentPassingKind APK, unsigned format_idx, + unsigned firstDataArg, Sema::FormatStringType Type, + Sema::VariadicCallType CallType, bool NoNote, + llvm::SmallBitVector &CheckedVarArgs, + UncoveredArgHandler &UncoveredArg, llvm::APSInt Offset, QualType T, + const VarDecl *VD, bool IgnoreStringsWithoutSpecifiers); + +static StringLiteralCheckType checkFormatStringExprEvaluated( + Sema &S, const Expr *E, ArrayRef Args, + Sema::FormatArgumentPassingKind APK, unsigned format_idx, + unsigned firstDataArg, Sema::FormatStringType Type, + Sema::VariadicCallType CallType, bool NoNote, + llvm::SmallBitVector &CheckedVarArgs, UncoveredArgHandler &UncoveredArg, + llvm::APSInt Offset, bool IgnoreStringsWithoutSpecifiers = false) { if (S.isConstantEvaluated()) return SLCT_NotALiteral; tryAgain: @@ -8510,8 +8511,7 @@ case Stmt::ConditionalOperatorClass: { // The expression is a literal if both sub-expressions were, and it was // completely checked only if both sub-expressions were checked. - const AbstractConditionalOperator *C = - cast(E); + const AbstractConditionalOperator *C = cast(E); // Determine whether it is necessary to check both sub-expressions, for // example, because the condition expression is a constant that can be @@ -8535,18 +8535,18 @@ if (!CheckLeft) Left = SLCT_UncheckedLiteral; else { - Left = checkFormatStringExpr(S, C->getTrueExpr(), Args, APK, format_idx, - firstDataArg, Type, CallType, InFunctionCall, - CheckedVarArgs, UncoveredArg, Offset, - IgnoreStringsWithoutSpecifiers); + Left = checkFormatStringExprEvaluated( + S, C->getTrueExpr(), Args, APK, format_idx, firstDataArg, Type, + CallType, NoNote, CheckedVarArgs, UncoveredArg, Offset, + IgnoreStringsWithoutSpecifiers); if (Left == SLCT_NotALiteral || !CheckRight) { return Left; } } - StringLiteralCheckType Right = checkFormatStringExpr( + StringLiteralCheckType Right = checkFormatStringExprEvaluated( S, C->getFalseExpr(), Args, APK, format_idx, firstDataArg, Type, - CallType, InFunctionCall, CheckedVarArgs, UncoveredArg, Offset, + CallType, NoNote, CheckedVarArgs, UncoveredArg, Offset, IgnoreStringsWithoutSpecifiers); return (CheckLeft && Left < Right) ? Left : Right; @@ -8575,110 +8575,10 @@ // As an exception, do not flag errors for variables binding to // const string literals. if (const VarDecl *VD = dyn_cast(DR->getDecl())) { - bool isConstant = false; - QualType T = DR->getType(); - - if (const ArrayType *AT = S.Context.getAsArrayType(T)) { - isConstant = AT->getElementType().isConstant(S.Context); - } else if (const PointerType *PT = T->getAs()) { - isConstant = T.isConstant(S.Context) && - PT->getPointeeType().isConstant(S.Context); - } else if (T->isObjCObjectPointerType()) { - // In ObjC, there is usually no "const ObjectPointer" type, - // so don't check if the pointee type is constant. - isConstant = T.isConstant(S.Context); - } - - if (isConstant) { - if (const Expr *Init = VD->getAnyInitializer()) { - // Look through initializers like const char c[] = { "foo" } - if (const InitListExpr *InitList = dyn_cast(Init)) { - if (InitList->isStringLiteralInit()) - Init = InitList->getInit(0)->IgnoreParenImpCasts(); - } - return checkFormatStringExpr( - S, Init, Args, APK, format_idx, firstDataArg, Type, CallType, - /*InFunctionCall*/ false, CheckedVarArgs, UncoveredArg, Offset); - } - } - - // When the format argument is an argument of this function, and this - // function also has the format attribute, there are several interactions - // for which there shouldn't be a warning. For instance, when calling - // v*printf from a function that has the printf format attribute, we - // should not emit a warning about using `fmt`, even though it's not - // constant, because the arguments have already been checked for the - // caller of `logmessage`: - // - // __attribute__((format(printf, 1, 2))) - // void logmessage(char const *fmt, ...) { - // va_list ap; - // va_start(ap, fmt); - // vprintf(fmt, ap); /* do not emit a warning about "fmt" */ - // ... - // } - // - // Another interaction that we need to support is calling a variadic - // format function from a format function that has fixed arguments. For - // instance: - // - // __attribute__((format(printf, 1, 2))) - // void logstring(char const *fmt, char const *str) { - // printf(fmt, str); /* do not emit a warning about "fmt" */ - // } - // - // Same (and perhaps more relatably) for the variadic template case: - // - // template - // __attribute__((format(printf, 1, 2))) - // void log(const char *fmt, Args&&... args) { - // printf(fmt, forward(args)...); - // /* do not emit a warning about "fmt" */ - // } - // - // Due to implementation difficulty, we only check the format, not the - // format arguments, in all cases. - // - if (const auto *PV = dyn_cast(VD)) { - if (const auto *D = dyn_cast(PV->getDeclContext())) { - for (const auto *PVFormat : D->specific_attrs()) { - bool IsCXXMember = false; - if (const auto *MD = dyn_cast(D)) - IsCXXMember = MD->isInstance(); - - bool IsVariadic = false; - if (const FunctionType *FnTy = D->getFunctionType()) - IsVariadic = cast(FnTy)->isVariadic(); - else if (const auto *BD = dyn_cast(D)) - IsVariadic = BD->isVariadic(); - else if (const auto *OMD = dyn_cast(D)) - IsVariadic = OMD->isVariadic(); - - Sema::FormatStringInfo CallerFSI; - if (Sema::getFormatStringInfo(PVFormat, IsCXXMember, IsVariadic, - &CallerFSI)) { - // We also check if the formats are compatible. - // We can't pass a 'scanf' string to a 'printf' function. - if (PV->getFunctionScopeIndex() == CallerFSI.FormatIdx && - Type == S.GetFormatStringType(PVFormat)) { - // Lastly, check that argument passing kinds transition in a - // way that makes sense: - // from a caller with FAPK_VAList, allow FAPK_VAList - // from a caller with FAPK_Fixed, allow FAPK_Fixed - // from a caller with FAPK_Fixed, allow FAPK_Variadic - // from a caller with FAPK_Variadic, allow FAPK_VAList - switch (combineFAPK(CallerFSI.ArgPassingKind, APK)) { - case combineFAPK(Sema::FAPK_VAList, Sema::FAPK_VAList): - case combineFAPK(Sema::FAPK_Fixed, Sema::FAPK_Fixed): - case combineFAPK(Sema::FAPK_Fixed, Sema::FAPK_Variadic): - case combineFAPK(Sema::FAPK_Variadic, Sema::FAPK_VAList): - return SLCT_UncheckedLiteral; - } - } - } - } - } - } + return checkVarDecl(S, E, Args, APK, format_idx, firstDataArg, Type, + CallType, NoNote, CheckedVarArgs, UncoveredArg, + Offset, DR->getType(), VD, + IgnoreStringsWithoutSpecifiers); } return SLCT_NotALiteral; @@ -8687,14 +8587,15 @@ case Stmt::CallExprClass: case Stmt::CXXMemberCallExprClass: { const CallExpr *CE = cast(E); - if (const NamedDecl *ND = dyn_cast_or_null(CE->getCalleeDecl())) { + if (const NamedDecl *ND = + dyn_cast_or_null(CE->getCalleeDecl())) { bool IsFirst = true; StringLiteralCheckType CommonResult; for (const auto *FA : ND->specific_attrs()) { const Expr *Arg = CE->getArg(FA->getFormatIdx().getASTIndex()); - StringLiteralCheckType Result = checkFormatStringExpr( - S, Arg, Args, APK, format_idx, firstDataArg, Type, CallType, - InFunctionCall, CheckedVarArgs, UncoveredArg, Offset, + StringLiteralCheckType Result = checkFormatStringExprEvaluated( + S, Arg, Args, APK, format_idx, firstDataArg, Type, CallType, NoNote, + CheckedVarArgs, UncoveredArg, Offset, IgnoreStringsWithoutSpecifiers); if (IsFirst) { CommonResult = Result; @@ -8709,18 +8610,13 @@ if (BuiltinID == Builtin::BI__builtin___CFStringMakeConstantString || BuiltinID == Builtin::BI__builtin___NSStringMakeConstantString) { const Expr *Arg = CE->getArg(0); - return checkFormatStringExpr( + return checkFormatStringExprEvaluated( S, Arg, Args, APK, format_idx, firstDataArg, Type, CallType, - InFunctionCall, CheckedVarArgs, UncoveredArg, Offset, + NoNote, CheckedVarArgs, UncoveredArg, Offset, IgnoreStringsWithoutSpecifiers); } } } - if (const 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::ObjCMessageExprClass: { @@ -8743,9 +8639,9 @@ } const Expr *Arg = ME->getArg(FA->getFormatIdx().getASTIndex()); - return checkFormatStringExpr( - S, Arg, Args, APK, format_idx, firstDataArg, Type, CallType, - InFunctionCall, CheckedVarArgs, UncoveredArg, Offset, + return checkFormatStringExprEvaluated( + S, Arg, Args, APK, format_idx, firstDataArg, Type, CallType, NoNote, + CheckedVarArgs, UncoveredArg, Offset, IgnoreStringsWithoutSpecifiers); } } @@ -8769,13 +8665,58 @@ } FormatStringLiteral FStr(StrE, Offset.sextOrTrunc(64).getSExtValue()); CheckFormatString(S, &FStr, E, Args, APK, format_idx, firstDataArg, Type, - InFunctionCall, CallType, CheckedVarArgs, UncoveredArg, + NoNote, CallType, CheckedVarArgs, UncoveredArg, IgnoreStringsWithoutSpecifiers); return SLCT_CheckedLiteral; } return SLCT_NotALiteral; } + + case Stmt::InitListExprClass: { + auto *ILE = cast(E); + if (ILE->isStringLiteralInit()) { + auto *SL = + dyn_cast(ILE->getInit(0)->IgnoreParenImpCasts()); + return checkFormatStringExprEvaluated( + S, SL, Args, APK, format_idx, firstDataArg, Type, CallType, NoNote, + CheckedVarArgs, UncoveredArg, Offset); + } + // looks like {'a', 'b', 'c'} + ArrayRef AR = ILE->inits(); + SmallString<128> StrBuf; + QualType Ty; + bool HasCStringEnds = false; + for (auto &Expr : AR) { + if (Expr->getStmtClass() == Stmt::CharacterLiteralClass) { + const auto *CL = cast(Expr); + // construct a StringRef for this + unsigned int V = CL->getValue(); + if (V == 0) { + HasCStringEnds = true; + break; + } + StrBuf.push_back(V); + Ty = Expr->getType(); + } else { + // not a CharacterLiteral + return SLCT_NotALiteral; + } + } + if (!HasCStringEnds) { + // FIXME: fire a warning that this InitListExprClass does not end with + // '\0', with FixIt hints + } + const auto SR = StringRef(StrBuf); + const auto *SL = StringLiteral::Create( + S.Context, SR, StringLiteral::Ordinary, + /*Pascal*/ false, S.Context.getStringLiteralArrayType(Ty, SR.size()), + SourceLocation()); + return checkFormatStringExprEvaluated( + S, SL, Args, APK, format_idx, firstDataArg, Type, CallType, + /*NoNote*/ true, CheckedVarArgs, UncoveredArg, Offset, + IgnoreStringsWithoutSpecifiers); + } case Stmt::BinaryOperatorClass: { const BinaryOperator *BinOp = cast(E); @@ -8830,18 +8771,158 @@ } } -// If this expression can be evaluated at compile-time, -// check if the result is a StringLiteral and return it -// otherwise return nullptr -static const Expr *maybeConstEvalStringLiteral(ASTContext &Context, - const Expr *E) { - Expr::EvalResult Result; - if (E->EvaluateAsRValue(Result, Context) && Result.Val.isLValue()) { - const auto *LVE = Result.Val.getLValueBase().dyn_cast(); - if (isa_and_nonnull(LVE)) - return LVE; +static StringLiteralCheckType +checkVarDecl(Sema &S, const Expr *E, ArrayRef Args, + Sema::FormatArgumentPassingKind APK, unsigned format_idx, + unsigned firstDataArg, Sema::FormatStringType Type, + Sema::VariadicCallType CallType, bool NoNote, + llvm::SmallBitVector &CheckedVarArgs, + UncoveredArgHandler &UncoveredArg, llvm::APSInt Offset, QualType T, + const VarDecl *VD, bool IgnoreStringsWithoutSpecifiers) { + bool isConstant = false; + + if (const ArrayType *AT = S.Context.getAsArrayType(T)) { + isConstant = AT->getElementType().isConstant(S.Context); + } else if (const PointerType *PT = T->getAs()) { + isConstant = + T.isConstant(S.Context) && PT->getPointeeType().isConstant(S.Context); + } else if (T->isObjCObjectPointerType()) { + // In ObjC, there is usually no "const ObjectPointer" type, + // so don't check if the pointee type is constant. + isConstant = T.isConstant(S.Context); + } + + if (isConstant) { + if (const Expr *Init = VD->getAnyInitializer()) { + // Look through initializers like const char c[] = { "foo" } + if (const InitListExpr *InitList = dyn_cast(Init)) { + if (InitList->isStringLiteralInit()) + Init = InitList->getInit(0)->IgnoreParenImpCasts(); + } + return checkFormatStringExprEvaluated( + S, Init, Args, APK, format_idx, firstDataArg, Type, CallType, + /*NoNote*/ false, CheckedVarArgs, UncoveredArg, Offset); + } } - return nullptr; + + // When the format argument is an argument of this function, and this + // function also has the format attribute, there are several interactions + // for which there shouldn't be a warning. For instance, when calling + // v*printf from a function that has the printf format attribute, we + // should not emit a warning about using `fmt`, even though it's not + // constant, because the arguments have already been checked for the + // caller of `logmessage`: + // + // __attribute__((format(printf, 1, 2))) + // void logmessage(char const *fmt, ...) { + // va_list ap; + // va_start(ap, fmt); + // vprintf(fmt, ap); /* do not emit a warning about "fmt" */ + // ... + // } + // + // Another interaction that we need to support is calling a variadic + // format function from a format function that has fixed arguments. For + // instance: + // + // __attribute__((format(printf, 1, 2))) + // void logstring(char const *fmt, char const *str) { + // printf(fmt, str); /* do not emit a warning about "fmt" */ + // } + // + // Same (and perhaps more relatably) for the variadic template case: + // + // template + // __attribute__((format(printf, 1, 2))) + // void log(const char *fmt, Args&&... args) { + // printf(fmt, forward(args)...); + // /* do not emit a warning about "fmt" */ + // } + // + // Due to implementation difficulty, we only check the format, not the + // format arguments, in all cases. + // + if (const auto *PV = dyn_cast(VD)) { + if (const auto *D = dyn_cast(PV->getDeclContext())) { + for (const auto *PVFormat : D->specific_attrs()) { + bool IsCXXMember = false; + if (const auto *MD = dyn_cast(D)) + IsCXXMember = MD->isInstance(); + + bool IsVariadic = false; + if (const FunctionType *FnTy = D->getFunctionType()) + IsVariadic = cast(FnTy)->isVariadic(); + else if (const auto *BD = dyn_cast(D)) + IsVariadic = BD->isVariadic(); + else if (const auto *OMD = dyn_cast(D)) + IsVariadic = OMD->isVariadic(); + + Sema::FormatStringInfo CallerFSI; + if (Sema::getFormatStringInfo(PVFormat, IsCXXMember, IsVariadic, + &CallerFSI)) { + // We also check if the formats are compatible. + // We can't pass a 'scanf' string to a 'printf' function. + if (PV->getFunctionScopeIndex() == CallerFSI.FormatIdx && + Type == S.GetFormatStringType(PVFormat)) { + // Lastly, check that argument passing kinds transition in a + // way that makes sense: + // from a caller with FAPK_VAList, allow FAPK_VAList + // from a caller with FAPK_Fixed, allow FAPK_Fixed + // from a caller with FAPK_Fixed, allow FAPK_Variadic + // from a caller with FAPK_Variadic, allow FAPK_VAList + switch (combineFAPK(CallerFSI.ArgPassingKind, APK)) { + case combineFAPK(Sema::FAPK_VAList, Sema::FAPK_VAList): + case combineFAPK(Sema::FAPK_Fixed, Sema::FAPK_Fixed): + case combineFAPK(Sema::FAPK_Fixed, Sema::FAPK_Variadic): + case combineFAPK(Sema::FAPK_Variadic, Sema::FAPK_VAList): + return SLCT_UncheckedLiteral; + } + } + } + } + } + } + return SLCT_NotALiteral; +} + +// Determine if an expression is a string literal or constant string. +// If this function returns false on the arguments to a function expecting a +// format string, we will usually need to emit a warning. +// True string literals are then checked by CheckFormatString. +static StringLiteralCheckType +checkFormatStringExpr(Sema &S, const Expr *E, ArrayRef Args, + Sema::FormatArgumentPassingKind APK, unsigned format_idx, + unsigned firstDataArg, Sema::FormatStringType Type, + Sema::VariadicCallType CallType, bool NoNote, + llvm::SmallBitVector &CheckedVarArgs, + UncoveredArgHandler &UncoveredArg, llvm::APSInt Offset, + bool IgnoreStringsWithoutSpecifiers = false) { + auto T = checkFormatStringExprEvaluated( + S, E, Args, APK, format_idx, firstDataArg, Type, CallType, NoNote, + CheckedVarArgs, UncoveredArg, Offset, IgnoreStringsWithoutSpecifiers); + if (T == SLCT_NotALiteral) { + Expr::EvalResult Result; + if (E->EvaluateAsRValue(Result, S.Context) && Result.Val.isLValue()) { + if (isa(E) || isa(E)) { + const auto *LVE = Result.Val.getLValueBase().dyn_cast(); + + if (LVE) + return checkFormatStringExprEvaluated( + S, LVE, Args, APK, format_idx, firstDataArg, Type, CallType, + /*NoNote*/ false, CheckedVarArgs, UncoveredArg, Offset, + IgnoreStringsWithoutSpecifiers); + } + + const auto *LVVD = + Result.Val.getLValueBase().dyn_cast(); + if (isa_and_nonnull(LVVD)) + return checkVarDecl( + S, E, Args, APK, format_idx, firstDataArg, Type, CallType, NoNote, + CheckedVarArgs, UncoveredArg, Offset, LVVD->getType(), + dyn_cast(LVVD), IgnoreStringsWithoutSpecifiers); + } + } + return T; } Sema::FormatStringType Sema::GetFormatStringType(const FormatAttr *Format) { @@ -8976,7 +9057,7 @@ llvm::SmallBitVector CoveredArgs; bool usesPositionalArgs = false; bool atFirstArg = true; - bool inFunctionCall; + bool noNote; Sema::VariadicCallType CallType; llvm::SmallBitVector &CheckedVarArgs; UncoveredArgHandler &UncoveredArg; @@ -8988,14 +9069,14 @@ unsigned numDataArgs, const char *beg, Sema::FormatArgumentPassingKind APK, ArrayRef Args, unsigned formatIdx, - bool inFunctionCall, Sema::VariadicCallType callType, + bool noNote, Sema::VariadicCallType callType, llvm::SmallBitVector &CheckedVarArgs, UncoveredArgHandler &UncoveredArg) : S(s), FExpr(fexpr), OrigFormatExpr(origFormatExpr), FSType(type), FirstDataArg(firstDataArg), NumDataArgs(numDataArgs), Beg(beg), - ArgPassingKind(APK), Args(Args), FormatIdx(formatIdx), - inFunctionCall(inFunctionCall), CallType(callType), - CheckedVarArgs(CheckedVarArgs), UncoveredArg(UncoveredArg) { + ArgPassingKind(APK), Args(Args), FormatIdx(formatIdx), noNote(noNote), + CallType(callType), CheckedVarArgs(CheckedVarArgs), + UncoveredArg(UncoveredArg) { CoveredArgs.resize(numDataArgs); CoveredArgs.reset(); } @@ -9031,7 +9112,7 @@ template static void - EmitFormatDiagnostic(Sema &S, bool inFunctionCall, const Expr *ArgumentExpr, + EmitFormatDiagnostic(Sema &S, bool NoNote, const Expr *ArgumentExpr, const PartialDiagnostic &PDiag, SourceLocation StringLoc, bool IsStringLocation, Range StringRange, ArrayRef Fixit = None); @@ -9362,16 +9443,17 @@ bool IsStringLocation, Range StringRange, ArrayRef FixIt) { - EmitFormatDiagnostic(S, inFunctionCall, Args[FormatIdx], PDiag, - Loc, IsStringLocation, StringRange, FixIt); + EmitFormatDiagnostic(S, noNote, Args[FormatIdx], PDiag, Loc, IsStringLocation, + StringRange, FixIt); } /// If the format string is not within the function call, emit a note /// so that the function call and string are in diagnostic messages. /// -/// \param InFunctionCall if true, the format string is within the function -/// call and only one diagnostic message will be produced. Otherwise, an -/// extra note will be emitted pointing to location of the format string. +/// \param NoNote if true, only one diagnostic message will be produced. Perhaps +/// the format string is within the function call or evaluted and does not have +/// SourceLocation. Otherwise, an extra note will be emitted pointing to +/// location of the format string. /// /// \param ArgumentExpr the expression that is passed as the format string /// argument in the function call. Used for getting locations when two @@ -9395,10 +9477,10 @@ /// \param FixIt optional fix it hint for the format string. template void CheckFormatHandler::EmitFormatDiagnostic( - Sema &S, bool InFunctionCall, const Expr *ArgumentExpr, + Sema &S, bool NoNote, const Expr *ArgumentExpr, const PartialDiagnostic &PDiag, SourceLocation Loc, bool IsStringLocation, Range StringRange, ArrayRef FixIt) { - if (InFunctionCall) { + if (NoNote) { const Sema::SemaDiagnosticBuilder &D = S.Diag(Loc, PDiag); D << StringRange; D << FixIt; @@ -9427,13 +9509,12 @@ unsigned numDataArgs, bool isObjC, const char *beg, Sema::FormatArgumentPassingKind APK, ArrayRef Args, unsigned formatIdx, - bool inFunctionCall, Sema::VariadicCallType CallType, + bool noNote, Sema::VariadicCallType CallType, llvm::SmallBitVector &CheckedVarArgs, UncoveredArgHandler &UncoveredArg) : CheckFormatHandler(s, fexpr, origFormatExpr, type, firstDataArg, - numDataArgs, beg, APK, Args, formatIdx, - inFunctionCall, CallType, CheckedVarArgs, - UncoveredArg) {} + numDataArgs, beg, APK, Args, formatIdx, noNote, + CallType, CheckedVarArgs, UncoveredArg) {} bool isObjCContext() const { return FSType == Sema::FST_NSString; } @@ -10375,13 +10456,12 @@ unsigned firstDataArg, unsigned numDataArgs, const char *beg, Sema::FormatArgumentPassingKind APK, ArrayRef Args, unsigned formatIdx, - bool inFunctionCall, Sema::VariadicCallType CallType, + bool noNote, Sema::VariadicCallType CallType, llvm::SmallBitVector &CheckedVarArgs, UncoveredArgHandler &UncoveredArg) : CheckFormatHandler(s, fexpr, origFormatExpr, type, firstDataArg, - numDataArgs, beg, APK, Args, formatIdx, - inFunctionCall, CallType, CheckedVarArgs, - UncoveredArg) {} + numDataArgs, beg, APK, Args, formatIdx, noNote, + CallType, CheckedVarArgs, UncoveredArg) {} bool HandleScanfSpecifier(const analyze_scanf::ScanfSpecifier &FS, const char *startSpecifier, @@ -10544,13 +10624,13 @@ Sema &S, const FormatStringLiteral *FExpr, const Expr *OrigFormatExpr, ArrayRef Args, Sema::FormatArgumentPassingKind APK, unsigned format_idx, unsigned firstDataArg, Sema::FormatStringType Type, - bool inFunctionCall, Sema::VariadicCallType CallType, + bool NoNote, Sema::VariadicCallType CallType, llvm::SmallBitVector &CheckedVarArgs, UncoveredArgHandler &UncoveredArg, bool IgnoreStringsWithoutSpecifiers) { // CHECK: is the format string a wide literal? if (!FExpr->isAscii() && !FExpr->isUTF8()) { CheckFormatHandler::EmitFormatDiagnostic( - S, inFunctionCall, Args[format_idx], + S, NoNote, Args[format_idx], S.PDiag(diag::warn_format_string_is_wide_literal), FExpr->getBeginLoc(), /*IsStringLocation*/ true, OrigFormatExpr->getSourceRange()); return; @@ -10576,7 +10656,7 @@ // embedded null character. if (TypeSize <= StrRef.size() && !StrRef.substr(0, TypeSize).contains('\0')) { CheckFormatHandler::EmitFormatDiagnostic( - S, inFunctionCall, Args[format_idx], + S, NoNote, Args[format_idx], S.PDiag(diag::warn_printf_format_string_not_null_terminated), FExpr->getBeginLoc(), /*IsStringLocation=*/true, OrigFormatExpr->getSourceRange()); @@ -10586,8 +10666,8 @@ // CHECK: empty format string? if (StrLen == 0 && numDataArgs > 0) { CheckFormatHandler::EmitFormatDiagnostic( - S, inFunctionCall, Args[format_idx], - S.PDiag(diag::warn_empty_format_string), FExpr->getBeginLoc(), + S, NoNote, Args[format_idx], S.PDiag(diag::warn_empty_format_string), + FExpr->getBeginLoc(), /*IsStringLocation*/ true, OrigFormatExpr->getSourceRange()); return; } @@ -10598,8 +10678,7 @@ CheckPrintfHandler H( S, FExpr, OrigFormatExpr, Type, firstDataArg, numDataArgs, (Type == Sema::FST_NSString || Type == Sema::FST_OSTrace), Str, APK, - Args, format_idx, inFunctionCall, CallType, CheckedVarArgs, - UncoveredArg); + Args, format_idx, NoNote, CallType, CheckedVarArgs, UncoveredArg); if (!analyze_format_string::ParsePrintfString( H, Str, Str + StrLen, S.getLangOpts(), S.Context.getTargetInfo(), @@ -10607,7 +10686,7 @@ H.DoneProcessing(); } else if (Type == Sema::FST_Scanf) { CheckScanfHandler H(S, FExpr, OrigFormatExpr, Type, firstDataArg, - numDataArgs, Str, APK, Args, format_idx, inFunctionCall, + numDataArgs, Str, APK, Args, format_idx, NoNote, CallType, CheckedVarArgs, UncoveredArg); if (!analyze_format_string::ParseScanfString( 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 @@ -166,6 +166,14 @@ #if __cplusplus >= 201103L namespace evaluated { +struct InitList { + static constexpr char value[] = {'%', 's', '%', 'd', '\0'}; // no note here because this is not literal +}; + +constexpr const char *init_list_func() { return InitList::value; } + +constexpr const char *init_list_func_wrap() { return init_list_func(); } + constexpr const char *basic() { return "%s %d"; // expected-note {{format string is defined here}} @@ -199,12 +207,15 @@ void f() { printf(basic(), 1, 2); // expected-warning {{format specifies type 'char *' but the argument has type 'int'}} - printf(correct_fmt(), 1, 2); + printf(correct_fmt(), 1, 2); // no warning 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'}} + printf(InitList::value, 1, 2); // expected-warning {{format specifies type 'char *' but the argument has type 'int'}} + printf(init_list_func(), 1, 2); // expected-warning {{format specifies type 'char *' but the argument has type 'int'}} + printf(init_list_func_wrap(), 1, 2); // expected-warning {{format specifies type 'char *' but the argument has type 'int'}} } -} +} // namespace evaluated #endif