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
This commit is contained in:
Richard Smith 2013-08-05 18:49:43 +00:00
parent 518b26cdcd
commit d7293d7fcb
7 changed files with 203 additions and 117 deletions

View File

@ -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<CXX98Compat>, 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"

View File

@ -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<const Expr *> Args,
bool HasVAListArg,
unsigned format_idx,
unsigned firstDataArg,
FormatStringType Type,
VariadicCallType CallType,
bool inFunctionCall = true);
void CheckFormatString(const StringLiteral *FExpr, const Expr *OrigFormatExpr,
ArrayRef<const Expr *> 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<const Expr *> Args,
bool IsCXXMember,
VariadicCallType CallType,
SourceLocation Loc, SourceRange Range);
SourceLocation Loc, SourceRange Range,
llvm::SmallBitVector &CheckedVarArgs);
bool CheckFormatArguments(ArrayRef<const Expr *> 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,

View File

@ -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 <limits>
@ -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<FormatAttr>
I = FDecl->specific_attr_begin<FormatAttr>(),
E = FDecl->specific_attr_end<FormatAttr>(); I != E ; ++I)
E = FDecl->specific_attr_end<FormatAttr>(); 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<NonNullAttr>
@ -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<const Expr *> Args,
bool HasVAListArg,
unsigned format_idx, unsigned firstDataArg,
FormatStringType Type, VariadicCallType CallType,
bool inFunctionCall) {
static StringLiteralCheckType
checkFormatStringExpr(Sema &S, const Expr *E, ArrayRef<const Expr *> 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<const Expr *> Args,
const AbstractConditionalOperator *C =
cast<AbstractConditionalOperator>(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<const Expr *> 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<PointerType>()) {
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<const Expr *> 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<const Expr *> Args,
// va_start(ap, fmt);
// vprintf(fmt, ap); // Do NOT emit a warning about "fmt".
// ...
//
// }
if (HasVAListArg) {
if (const ParmVarDecl *PV = dyn_cast<ParmVarDecl>(VD)) {
if (const NamedDecl *ND = dyn_cast<NamedDecl>(PV->getDeclContext())) {
@ -2033,7 +2049,7 @@ Sema::checkFormatStringExpr(const Expr *E, ArrayRef<const Expr *> 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<const Expr *> 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<FunctionDecl>(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<const Expr *> Args,
StrE = cast<StringLiteral>(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<const Expr *> 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<const Expr *> 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<const Expr *> 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<const Expr *> 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<const Expr *> 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<const Expr *> 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<InitListExpr>(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<const Expr *> 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<const Expr *> 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(),

View File

@ -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<InitListExpr>(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;

View File

@ -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}}
}

View File

@ -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}}
}

View File

@ -232,6 +232,8 @@ void testInvalidFormatArgument(NSDictionary *dict) {
// <rdar://problem/11825593>
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'}}
}