forked from OSchip/llvm-project
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:
parent
518b26cdcd
commit
d7293d7fcb
|
@ -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"
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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(),
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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}}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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}}
|
||||
}
|
||||
|
|
|
@ -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'}}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue