From d7293d7fcb608423cb0d05acb6a026ddc04e1d84 Mon Sep 17 00:00:00 2001 From: Richard Smith Date: Mon, 5 Aug 2013 18:49:43 +0000 Subject: [PATCH] Implement C++'s restrictions on the type of an expression passed to a vararg function: it can't be 'void' and it can't be an initializer list. We give a hard error for these rather than treating them as undefined behavior (we can and probably should do the same for non-POD types in C++11, but as of this change we don't). Slightly rework the checking of variadic arguments in a function with a format attribute to ensure that certain kinds of format string problem (non-literal string, too many/too few arguments, ...) don't suppress this error. llvm-svn: 187735 --- .../clang/Basic/DiagnosticSemaKinds.td | 7 + clang/include/clang/Sema/Sema.h | 35 ++- clang/lib/Sema/SemaChecking.cpp | 199 +++++++++++------- clang/lib/Sema/SemaExpr.cpp | 68 ++++-- .../CXX/expr/expr.post/expr.call/p7-0x.cpp | 5 + clang/test/SemaCXX/format-strings-0x.cpp | 4 + clang/test/SemaObjC/format-strings-objc.m | 2 + 7 files changed, 203 insertions(+), 117 deletions(-) diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 6438008a1e4f..bdfc8b54cfc0 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -5451,6 +5451,13 @@ def warn_cxx98_compat_pass_non_pod_arg_to_vararg : Warning< "passing object of trivial but non-POD type %0 through variadic" " %select{function|block|method|constructor}1 is incompatible with C++98">, InGroup, DefaultIgnore; +def err_cannot_pass_to_vararg : Error< + "cannot pass %select{expression of type %1|initializer list}0 to variadic " + "%select{function|block|method|constructor}2">; +def err_cannot_pass_to_vararg_format : Error< + "cannot pass %select{expression of type %1|initializer list}0 to variadic " + "%select{function|block|method|constructor}2; expected type from format " + "string was %3">; def err_typecheck_call_invalid_ordered_compare : Error< "ordered compare requires two args of floating point type" diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index d14b38eb0c2d..ba445ab31d1e 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -6907,12 +6907,17 @@ public: enum VarArgKind { VAK_Valid, VAK_ValidInCXX11, + VAK_Undefined, VAK_Invalid }; // Determines which VarArgKind fits an expression. VarArgKind isValidVarArgType(const QualType &Ty); + /// Check to see if the given expression is a valid argument to a variadic + /// function, issuing a diagnostic if not. + void checkVariadicArgument(const Expr *E, VariadicCallType CT); + /// GatherArgumentsForCall - Collector argument expressions for various /// form of call prototypes. bool GatherArgumentsForCall(SourceLocation CallLoc, @@ -6930,10 +6935,6 @@ public: ExprResult DefaultVariadicArgumentPromotion(Expr *E, VariadicCallType CT, FunctionDecl *FDecl); - /// Checks to see if the given expression is a valid argument to a variadic - /// function, issuing a diagnostic and returning NULL if not. - bool variadicArgumentPODCheck(const Expr *E, VariadicCallType CT); - // UsualArithmeticConversions - performs the UsualUnaryConversions on it's // operands and then handles various conversions that are common to binary // operators (C99 6.3.1.8). If both operands aren't arithmetic, this @@ -7578,6 +7579,7 @@ private: bool SemaBuiltinConstantArg(CallExpr *TheCall, int ArgNum, llvm::APSInt &Result); +public: enum FormatStringType { FST_Scanf, FST_Printf, @@ -7589,37 +7591,26 @@ private: }; static FormatStringType GetFormatStringType(const FormatAttr *Format); - enum StringLiteralCheckType { - SLCT_NotALiteral, - SLCT_UncheckedLiteral, - SLCT_CheckedLiteral - }; - - StringLiteralCheckType checkFormatStringExpr(const Expr *E, - ArrayRef Args, - bool HasVAListArg, - unsigned format_idx, - unsigned firstDataArg, - FormatStringType Type, - VariadicCallType CallType, - bool inFunctionCall = true); - void CheckFormatString(const StringLiteral *FExpr, const Expr *OrigFormatExpr, ArrayRef Args, bool HasVAListArg, unsigned format_idx, unsigned firstDataArg, FormatStringType Type, bool inFunctionCall, - VariadicCallType CallType); + VariadicCallType CallType, + llvm::SmallBitVector &CheckedVarArgs); +private: bool CheckFormatArguments(const FormatAttr *Format, ArrayRef Args, bool IsCXXMember, VariadicCallType CallType, - SourceLocation Loc, SourceRange Range); + SourceLocation Loc, SourceRange Range, + llvm::SmallBitVector &CheckedVarArgs); bool CheckFormatArguments(ArrayRef Args, bool HasVAListArg, unsigned format_idx, unsigned firstDataArg, FormatStringType Type, VariadicCallType CallType, - SourceLocation Loc, SourceRange range); + SourceLocation Loc, SourceRange range, + llvm::SmallBitVector &CheckedVarArgs); void CheckNonNullArguments(const NonNullAttr *NonNull, const Expr * const *ExprArgs, diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp index 78995e3f43bf..0d41d5f3cb10 100644 --- a/clang/lib/Sema/SemaChecking.cpp +++ b/clang/lib/Sema/SemaChecking.cpp @@ -32,9 +32,9 @@ #include "clang/Sema/Lookup.h" #include "clang/Sema/ScopeInfo.h" #include "clang/Sema/Sema.h" -#include "llvm/ADT/BitVector.h" -#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/SmallBitVector.h" #include "llvm/ADT/SmallString.h" +#include "llvm/ADT/STLExtras.h" #include "llvm/Support/ConvertUTF.h" #include "llvm/Support/raw_ostream.h" #include @@ -709,27 +709,35 @@ void Sema::checkCall(NamedDecl *FDecl, SourceLocation Loc, SourceRange Range, VariadicCallType CallType) { + // FIXME: We should check as much as we can in the template definition. if (CurContext->isDependentContext()) return; // Printf and scanf checking. bool HandledFormatString = false; - if (FDecl) + llvm::SmallBitVector CheckedVarArgs; + if (FDecl) { for (specific_attr_iterator I = FDecl->specific_attr_begin(), - E = FDecl->specific_attr_end(); I != E ; ++I) + E = FDecl->specific_attr_end(); I != E ; ++I) { + CheckedVarArgs.resize(Args.size()); if (CheckFormatArguments(*I, Args, IsMemberFunction, CallType, Loc, - Range)) - HandledFormatString = true; + Range, CheckedVarArgs)) + HandledFormatString = true; + } + } // Refuse POD arguments that weren't caught by the format string // checks above. - if (!HandledFormatString && CallType != VariadicDoesNotApply) + if (CallType != VariadicDoesNotApply) { for (unsigned ArgIdx = NumProtoArgs; ArgIdx < Args.size(); ++ArgIdx) { // Args[ArgIdx] can be null in malformed code. - if (const Expr *Arg = Args[ArgIdx]) - variadicArgumentPODCheck(Arg, CallType); + if (const Expr *Arg = Args[ArgIdx]) { + if (CheckedVarArgs.empty() || !CheckedVarArgs[ArgIdx]) + checkVariadicArgument(Arg, CallType); + } } + } if (FDecl) { for (specific_attr_iterator @@ -1909,28 +1917,36 @@ bool Sema::SemaBuiltinLongjmp(CallExpr *TheCall) { return false; } +namespace { +enum StringLiteralCheckType { + SLCT_NotALiteral, + SLCT_UncheckedLiteral, + SLCT_CheckedLiteral +}; +} + // 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. -Sema::StringLiteralCheckType -Sema::checkFormatStringExpr(const Expr *E, ArrayRef Args, - bool HasVAListArg, - unsigned format_idx, unsigned firstDataArg, - FormatStringType Type, VariadicCallType CallType, - bool inFunctionCall) { +static StringLiteralCheckType +checkFormatStringExpr(Sema &S, const Expr *E, ArrayRef Args, + bool HasVAListArg, unsigned format_idx, + unsigned firstDataArg, Sema::FormatStringType Type, + Sema::VariadicCallType CallType, bool InFunctionCall, + llvm::SmallBitVector &CheckedVarArgs) { tryAgain: if (E->isTypeDependent() || E->isValueDependent()) return SLCT_NotALiteral; E = E->IgnoreParenCasts(); - if (E->isNullPointerConstant(Context, Expr::NPC_ValueDependentIsNotNull)) + if (E->isNullPointerConstant(S.Context, Expr::NPC_ValueDependentIsNotNull)) // Technically -Wformat-nonliteral does not warn about this case. // The behavior of printf and friends in this case is implementation // dependent. Ideally if the format string cannot be null then // it should have a 'nonnull' attribute in the function prototype. - return SLCT_CheckedLiteral; + return SLCT_UncheckedLiteral; switch (E->getStmtClass()) { case Stmt::BinaryConditionalOperatorClass: @@ -1940,15 +1956,15 @@ Sema::checkFormatStringExpr(const Expr *E, ArrayRef Args, const AbstractConditionalOperator *C = cast(E); StringLiteralCheckType Left = - checkFormatStringExpr(C->getTrueExpr(), Args, + checkFormatStringExpr(S, C->getTrueExpr(), Args, HasVAListArg, format_idx, firstDataArg, - Type, CallType, inFunctionCall); + Type, CallType, InFunctionCall, CheckedVarArgs); if (Left == SLCT_NotALiteral) return SLCT_NotALiteral; StringLiteralCheckType Right = - checkFormatStringExpr(C->getFalseExpr(), Args, + checkFormatStringExpr(S, C->getFalseExpr(), Args, HasVAListArg, format_idx, firstDataArg, - Type, CallType, inFunctionCall); + Type, CallType, InFunctionCall, CheckedVarArgs); return Left < Right ? Left : Right; } @@ -1979,15 +1995,15 @@ Sema::checkFormatStringExpr(const Expr *E, ArrayRef Args, bool isConstant = false; QualType T = DR->getType(); - if (const ArrayType *AT = Context.getAsArrayType(T)) { - isConstant = AT->getElementType().isConstant(Context); + if (const ArrayType *AT = S.Context.getAsArrayType(T)) { + isConstant = AT->getElementType().isConstant(S.Context); } else if (const PointerType *PT = T->getAs()) { - isConstant = T.isConstant(Context) && - PT->getPointeeType().isConstant(Context); + 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(Context); + isConstant = T.isConstant(S.Context); } if (isConstant) { @@ -1997,10 +2013,10 @@ Sema::checkFormatStringExpr(const Expr *E, ArrayRef Args, if (InitList->isStringLiteralInit()) Init = InitList->getInit(0)->IgnoreParenImpCasts(); } - return checkFormatStringExpr(Init, Args, + return checkFormatStringExpr(S, Init, Args, HasVAListArg, format_idx, firstDataArg, Type, CallType, - /*inFunctionCall*/false); + /*InFunctionCall*/false, CheckedVarArgs); } } @@ -2017,7 +2033,7 @@ Sema::checkFormatStringExpr(const Expr *E, ArrayRef Args, // va_start(ap, fmt); // vprintf(fmt, ap); // Do NOT emit a warning about "fmt". // ... - // + // } if (HasVAListArg) { if (const ParmVarDecl *PV = dyn_cast(VD)) { if (const NamedDecl *ND = dyn_cast(PV->getDeclContext())) { @@ -2033,7 +2049,7 @@ Sema::checkFormatStringExpr(const Expr *E, ArrayRef Args, // We also check if the formats are compatible. // We can't pass a 'scanf' string to a 'printf' function. if (PVIndex == PVFormat->getFormatIdx() && - Type == GetFormatStringType(PVFormat)) + Type == S.GetFormatStringType(PVFormat)) return SLCT_UncheckedLiteral; } } @@ -2055,18 +2071,19 @@ Sema::checkFormatStringExpr(const Expr *E, ArrayRef Args, --ArgIndex; const Expr *Arg = CE->getArg(ArgIndex - 1); - return checkFormatStringExpr(Arg, Args, + return checkFormatStringExpr(S, Arg, Args, HasVAListArg, format_idx, firstDataArg, - Type, CallType, inFunctionCall); + Type, CallType, InFunctionCall, + CheckedVarArgs); } else if (const FunctionDecl *FD = dyn_cast(ND)) { unsigned BuiltinID = FD->getBuiltinID(); if (BuiltinID == Builtin::BI__builtin___CFStringMakeConstantString || BuiltinID == Builtin::BI__builtin___NSStringMakeConstantString) { const Expr *Arg = CE->getArg(0); - return checkFormatStringExpr(Arg, Args, + return checkFormatStringExpr(S, Arg, Args, HasVAListArg, format_idx, firstDataArg, Type, CallType, - inFunctionCall); + InFunctionCall, CheckedVarArgs); } } } @@ -2083,8 +2100,8 @@ Sema::checkFormatStringExpr(const Expr *E, ArrayRef Args, StrE = cast(E); if (StrE) { - CheckFormatString(StrE, E, Args, HasVAListArg, format_idx, - firstDataArg, Type, inFunctionCall, CallType); + S.CheckFormatString(StrE, E, Args, HasVAListArg, format_idx, firstDataArg, + Type, InFunctionCall, CallType, CheckedVarArgs); return SLCT_CheckedLiteral; } @@ -2140,12 +2157,13 @@ bool Sema::CheckFormatArguments(const FormatAttr *Format, ArrayRef Args, bool IsCXXMember, VariadicCallType CallType, - SourceLocation Loc, SourceRange Range) { + SourceLocation Loc, SourceRange Range, + llvm::SmallBitVector &CheckedVarArgs) { FormatStringInfo FSI; if (getFormatStringInfo(Format, IsCXXMember, &FSI)) return CheckFormatArguments(Args, FSI.HasVAListArg, FSI.FormatIdx, FSI.FirstDataArg, GetFormatStringType(Format), - CallType, Loc, Range); + CallType, Loc, Range, CheckedVarArgs); return false; } @@ -2153,7 +2171,8 @@ bool Sema::CheckFormatArguments(ArrayRef Args, bool HasVAListArg, unsigned format_idx, unsigned firstDataArg, FormatStringType Type, VariadicCallType CallType, - SourceLocation Loc, SourceRange Range) { + SourceLocation Loc, SourceRange Range, + llvm::SmallBitVector &CheckedVarArgs) { // CHECK: printf/scanf-like function is called with no format string. if (format_idx >= Args.size()) { Diag(Loc, diag::warn_missing_format_string) << Range; @@ -2175,8 +2194,9 @@ bool Sema::CheckFormatArguments(ArrayRef Args, // ObjC string uses the same format specifiers as C string, so we can use // the same format string checking logic for both ObjC and C strings. StringLiteralCheckType CT = - checkFormatStringExpr(OrigFormatExpr, Args, HasVAListArg, - format_idx, firstDataArg, Type, CallType); + checkFormatStringExpr(*this, OrigFormatExpr, Args, HasVAListArg, + format_idx, firstDataArg, Type, CallType, + /*IsFunctionCall*/true, CheckedVarArgs); if (CT != SLCT_NotALiteral) // Literal format string found, check done! return CT == SLCT_CheckedLiteral; @@ -2219,27 +2239,30 @@ protected: const bool HasVAListArg; ArrayRef Args; unsigned FormatIdx; - llvm::BitVector CoveredArgs; + llvm::SmallBitVector CoveredArgs; bool usesPositionalArgs; bool atFirstArg; bool inFunctionCall; Sema::VariadicCallType CallType; + llvm::SmallBitVector &CheckedVarArgs; public: CheckFormatHandler(Sema &s, const StringLiteral *fexpr, const Expr *origFormatExpr, unsigned firstDataArg, unsigned numDataArgs, const char *beg, bool hasVAListArg, ArrayRef Args, unsigned formatIdx, bool inFunctionCall, - Sema::VariadicCallType callType) + Sema::VariadicCallType callType, + llvm::SmallBitVector &CheckedVarArgs) : S(s), FExpr(fexpr), OrigFormatExpr(origFormatExpr), FirstDataArg(firstDataArg), NumDataArgs(numDataArgs), Beg(beg), HasVAListArg(hasVAListArg), Args(Args), FormatIdx(formatIdx), usesPositionalArgs(false), atFirstArg(true), - inFunctionCall(inFunctionCall), CallType(callType) { - CoveredArgs.resize(numDataArgs); - CoveredArgs.reset(); - } + inFunctionCall(inFunctionCall), CallType(callType), + CheckedVarArgs(CheckedVarArgs) { + CoveredArgs.resize(numDataArgs); + CoveredArgs.reset(); + } void DoneProcessing(); @@ -2628,10 +2651,12 @@ public: const char *beg, bool hasVAListArg, ArrayRef Args, unsigned formatIdx, bool inFunctionCall, - Sema::VariadicCallType CallType) - : CheckFormatHandler(s, fexpr, origFormatExpr, firstDataArg, - numDataArgs, beg, hasVAListArg, Args, - formatIdx, inFunctionCall, CallType), ObjCContext(isObjC) + Sema::VariadicCallType CallType, + llvm::SmallBitVector &CheckedVarArgs) + : CheckFormatHandler(s, fexpr, origFormatExpr, firstDataArg, + numDataArgs, beg, hasVAListArg, Args, + formatIdx, inFunctionCall, CallType, CheckedVarArgs), + ObjCContext(isObjC) {} @@ -3185,15 +3210,20 @@ CheckPrintfHandler::checkFormatExpr(const analyze_printf::PrintfSpecifier &FS, // Since the warning for passing non-POD types to variadic functions // was deferred until now, we emit a warning for non-POD // arguments here. - if (S.isValidVarArgType(ExprTy) == Sema::VAK_Invalid) { - unsigned DiagKind; - if (ExprTy->isObjCObjectType()) - DiagKind = diag::err_cannot_pass_objc_interface_to_vararg_format; - else - DiagKind = diag::warn_non_pod_vararg_with_format_string; - + switch (S.isValidVarArgType(ExprTy)) { + case Sema::VAK_Valid: + case Sema::VAK_ValidInCXX11: EmitFormatDiagnostic( - S.PDiag(DiagKind) + S.PDiag(diag::warn_printf_conversion_argument_type_mismatch) + << AT.getRepresentativeTypeName(S.Context) << ExprTy + << CSR + << E->getSourceRange(), + E->getLocStart(), /*IsStringLocation*/false, CSR); + break; + + case Sema::VAK_Undefined: + EmitFormatDiagnostic( + S.PDiag(diag::warn_non_pod_vararg_with_format_string) << S.getLangOpts().CPlusPlus11 << ExprTy << CallType @@ -3201,15 +3231,33 @@ CheckPrintfHandler::checkFormatExpr(const analyze_printf::PrintfSpecifier &FS, << CSR << E->getSourceRange(), E->getLocStart(), /*IsStringLocation*/false, CSR); - checkForCStrMembers(AT, E, CSR); - } else - EmitFormatDiagnostic( - S.PDiag(diag::warn_printf_conversion_argument_type_mismatch) - << AT.getRepresentativeTypeName(S.Context) << ExprTy - << CSR - << E->getSourceRange(), - E->getLocStart(), /*IsStringLocation*/false, CSR); + break; + + case Sema::VAK_Invalid: + if (ExprTy->isObjCObjectType()) + EmitFormatDiagnostic( + S.PDiag(diag::err_cannot_pass_objc_interface_to_vararg_format) + << S.getLangOpts().CPlusPlus11 + << ExprTy + << CallType + << AT.getRepresentativeTypeName(S.Context) + << CSR + << E->getSourceRange(), + E->getLocStart(), /*IsStringLocation*/false, CSR); + else + // FIXME: If this is an initializer list, suggest removing the braces + // or inserting a cast to the target type. + S.Diag(E->getLocStart(), diag::err_cannot_pass_to_vararg_format) + << isa(E) << ExprTy << CallType + << AT.getRepresentativeTypeName(S.Context) + << E->getSourceRange(); + break; + } + + assert(FirstDataArg + FS.getArgIndex() < CheckedVarArgs.size() && + "format string specifier index out of range"); + CheckedVarArgs[FirstDataArg + FS.getArgIndex()] = true; } return true; @@ -3225,10 +3273,12 @@ public: unsigned numDataArgs, const char *beg, bool hasVAListArg, ArrayRef Args, unsigned formatIdx, bool inFunctionCall, - Sema::VariadicCallType CallType) - : CheckFormatHandler(s, fexpr, origFormatExpr, firstDataArg, - numDataArgs, beg, hasVAListArg, - Args, formatIdx, inFunctionCall, CallType) + Sema::VariadicCallType CallType, + llvm::SmallBitVector &CheckedVarArgs) + : CheckFormatHandler(s, fexpr, origFormatExpr, firstDataArg, + numDataArgs, beg, hasVAListArg, + Args, formatIdx, inFunctionCall, CallType, + CheckedVarArgs) {} bool HandleScanfSpecifier(const analyze_scanf::ScanfSpecifier &FS, @@ -3383,7 +3433,8 @@ void Sema::CheckFormatString(const StringLiteral *FExpr, ArrayRef Args, bool HasVAListArg, unsigned format_idx, unsigned firstDataArg, FormatStringType Type, - bool inFunctionCall, VariadicCallType CallType) { + bool inFunctionCall, VariadicCallType CallType, + llvm::SmallBitVector &CheckedVarArgs) { // CHECK: is the format string a wide literal? if (!FExpr->isAscii() && !FExpr->isUTF8()) { @@ -3413,7 +3464,7 @@ void Sema::CheckFormatString(const StringLiteral *FExpr, CheckPrintfHandler H(*this, FExpr, OrigFormatExpr, firstDataArg, numDataArgs, (Type == FST_NSString), Str, HasVAListArg, Args, format_idx, - inFunctionCall, CallType); + inFunctionCall, CallType, CheckedVarArgs); if (!analyze_format_string::ParsePrintfString(H, Str, Str + StrLen, getLangOpts(), @@ -3422,7 +3473,7 @@ void Sema::CheckFormatString(const StringLiteral *FExpr, } else if (Type == FST_Scanf) { CheckScanfHandler H(*this, FExpr, OrigFormatExpr, firstDataArg, numDataArgs, Str, HasVAListArg, Args, format_idx, - inFunctionCall, CallType); + inFunctionCall, CallType, CheckedVarArgs); if (!analyze_format_string::ParseScanfString(H, Str, Str + StrLen, getLangOpts(), diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp index 8ba7a1deb7e1..8d370ce9063c 100644 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -742,6 +742,17 @@ ExprResult Sema::DefaultArgumentPromotion(Expr *E) { /// when we're in an unevaluated context. Sema::VarArgKind Sema::isValidVarArgType(const QualType &Ty) { if (Ty->isIncompleteType()) { + // C++11 [expr.call]p7: + // After these conversions, if the argument does not have arithmetic, + // enumeration, pointer, pointer to member, or class type, the program + // is ill-formed. + // + // Since we've already performed array-to-pointer and function-to-pointer + // decay, the only such type in C++ is cv void. This also handles + // initializer lists as variadic arguments. + if (Ty->isVoidType()) + return VAK_Invalid; + if (Ty->isObjCObjectType()) return VAK_Invalid; return VAK_Valid; @@ -764,35 +775,50 @@ Sema::VarArgKind Sema::isValidVarArgType(const QualType &Ty) { if (getLangOpts().ObjCAutoRefCount && Ty->isObjCLifetimeType()) return VAK_Valid; - return VAK_Invalid; + + if (Ty->isObjCObjectType()) + return VAK_Invalid; + + // FIXME: In C++11, these cases are conditionally-supported, meaning we're + // permitted to reject them. We should consider doing so. + return VAK_Undefined; } -bool Sema::variadicArgumentPODCheck(const Expr *E, VariadicCallType CT) { +void Sema::checkVariadicArgument(const Expr *E, VariadicCallType CT) { // Don't allow one to pass an Objective-C interface to a vararg. - const QualType & Ty = E->getType(); + const QualType &Ty = E->getType(); + VarArgKind VAK = isValidVarArgType(Ty); // Complain about passing non-POD types through varargs. - switch (isValidVarArgType(Ty)) { + switch (VAK) { case VAK_Valid: break; - case VAK_ValidInCXX11: - DiagRuntimeBehavior(E->getLocStart(), 0, - PDiag(diag::warn_cxx98_compat_pass_non_pod_arg_to_vararg) - << E->getType() << CT); - break; - case VAK_Invalid: { - if (Ty->isObjCObjectType()) - return DiagRuntimeBehavior(E->getLocStart(), 0, - PDiag(diag::err_cannot_pass_objc_interface_to_vararg) - << Ty << CT); - return DiagRuntimeBehavior(E->getLocStart(), 0, - PDiag(diag::warn_cannot_pass_non_pod_arg_to_vararg) - << getLangOpts().CPlusPlus11 << Ty << CT); + case VAK_ValidInCXX11: + DiagRuntimeBehavior( + E->getLocStart(), 0, + PDiag(diag::warn_cxx98_compat_pass_non_pod_arg_to_vararg) + << E->getType() << CT); + break; + + case VAK_Undefined: + DiagRuntimeBehavior( + E->getLocStart(), 0, + PDiag(diag::warn_cannot_pass_non_pod_arg_to_vararg) + << getLangOpts().CPlusPlus11 << Ty << CT); + break; + + case VAK_Invalid: + if (Ty->isObjCObjectType()) + DiagRuntimeBehavior( + E->getLocStart(), 0, + PDiag(diag::err_cannot_pass_objc_interface_to_vararg) + << Ty << CT); + else + Diag(E->getLocStart(), diag::err_cannot_pass_to_vararg) + << isa(E) << Ty << CT; + break; } - } - // c++ rules are enforced elsewhere. - return false; } /// DefaultVariadicArgumentPromotion - Like DefaultArgumentPromotion, but @@ -822,7 +848,7 @@ ExprResult Sema::DefaultVariadicArgumentPromotion(Expr *E, VariadicCallType CT, // Diagnostics regarding non-POD argument types are // emitted along with format string checking in Sema::CheckFunctionCall(). - if (isValidVarArgType(E->getType()) == VAK_Invalid) { + if (isValidVarArgType(E->getType()) == VAK_Undefined) { // Turn this into a trap. CXXScopeSpec SS; SourceLocation TemplateKWLoc; diff --git a/clang/test/CXX/expr/expr.post/expr.call/p7-0x.cpp b/clang/test/CXX/expr/expr.post/expr.call/p7-0x.cpp index 018609d9a07d..c77c47df244d 100644 --- a/clang/test/CXX/expr/expr.post/expr.call/p7-0x.cpp +++ b/clang/test/CXX/expr/expr.post/expr.call/p7-0x.cpp @@ -20,11 +20,16 @@ struct X4 { void vararg(...); +void g(); + void f(X1 x1, X2 x2, X3 x3, X4 x4) { vararg(x1); // OK vararg(x2); // expected-error{{cannot pass object of non-trivial type 'X2' through variadic function; call will abort at runtime}} vararg(x3); // OK vararg(x4); // expected-error{{cannot pass object of non-trivial type 'X4' through variadic function; call will abort at runtime}} + + vararg(g()); // expected-error{{cannot pass expression of type 'void' to variadic function}} + vararg({1, 2, 3}); // expected-error{{cannot pass initializer list to variadic function}} } diff --git a/clang/test/SemaCXX/format-strings-0x.cpp b/clang/test/SemaCXX/format-strings-0x.cpp index 7b3aef1ee5da..7e41c7fdbf9d 100644 --- a/clang/test/SemaCXX/format-strings-0x.cpp +++ b/clang/test/SemaCXX/format-strings-0x.cpp @@ -24,4 +24,8 @@ void f(char **sp, float *fp) { \u1234\U0010fffe %d)foo" // expected-warning {{more '%' conversions than data arguments}} ); + + printf("init list: %d", { 0 }); // expected-error {{cannot pass initializer list to variadic function; expected type from format string was 'int'}} + printf("void: %d", f(sp, fp)); // expected-error {{cannot pass expression of type 'void' to variadic function; expected type from format string was 'int'}} + printf(0, { 0 }); // expected-error {{cannot pass initializer list to variadic function}} } diff --git a/clang/test/SemaObjC/format-strings-objc.m b/clang/test/SemaObjC/format-strings-objc.m index 849013022433..a63d17328d81 100644 --- a/clang/test/SemaObjC/format-strings-objc.m +++ b/clang/test/SemaObjC/format-strings-objc.m @@ -232,6 +232,8 @@ void testInvalidFormatArgument(NSDictionary *dict) { // void testByValueObjectInFormat(Foo *obj) { printf("%d %d %d", 1L, *obj, 1L); // expected-error {{cannot pass object with interface type 'Foo' by value to variadic function; expected type from format string was 'int'}} expected-warning 2 {{format specifies type 'int' but the argument has type 'long'}} + printf("%!", *obj); // expected-error {{cannot pass object with interface type 'Foo' by value through variadic function}} expected-warning {{invalid conversion specifier}} + printf(0, *obj); // expected-error {{cannot pass object with interface type 'Foo' by value through variadic function}} [Bar log2:@"%d", *obj]; // expected-error {{cannot pass object with interface type 'Foo' by value to variadic method; expected type from format string was 'int'}} }