diff options
-rw-r--r-- | clang/include/clang/Basic/DiagnosticSemaKinds.td | 9 | ||||
-rw-r--r-- | clang/include/clang/Sema/Sema.h | 72 | ||||
-rw-r--r-- | clang/lib/Sema/SemaChecking.cpp | 391 | ||||
-rw-r--r-- | clang/lib/Sema/SemaDeclCXX.cpp | 12 | ||||
-rw-r--r-- | clang/lib/Sema/SemaExpr.cpp | 174 | ||||
-rw-r--r-- | clang/lib/Sema/SemaOverload.cpp | 6 | ||||
-rw-r--r-- | clang/test/SemaCXX/printf-block.cpp | 18 | ||||
-rw-r--r-- | clang/test/SemaCXX/printf-cstr.cpp | 53 |
8 files changed, 518 insertions, 217 deletions
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index a7582a0d28e..2c13e0c1770 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -4723,6 +4723,11 @@ def err_cannot_pass_objc_interface_to_vararg : Error< "cannot pass object with interface type %0 by-value through variadic " "%select{function|block|method}1">; +def warn_non_pod_vararg_with_format_string : Warning< + "cannot pass %select{non-POD|non-trivial}0 object of type %1 to variadic " + "function; expected type from format string was %2">, + InGroup<DiagGroup<"non-pod-varargs">>, DefaultError; + def warn_cannot_pass_non_pod_arg_to_vararg : Warning< "cannot pass object of %select{non-POD|non-trivial}0 type %1 through variadic" " %select{function|block|method|constructor}2; call will abort at runtime">, @@ -5206,7 +5211,8 @@ def warn_scanf_scanlist_incomplete : Warning< "no closing ']' for '%%[' in scanf format string">, InGroup<Format>; def note_format_string_defined : Note<"format string is defined here">; - +def note_printf_c_str: Note<"did you mean to call the %0 method?">; + def warn_null_arg : Warning< "null passed to a callee which requires a non-null argument">, InGroup<NonNull>; @@ -5666,4 +5672,3 @@ def err_module_private_definition : Error< } } // end of sema component. - diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index 6e7f23bd6b9..4b54e82d409 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -6409,6 +6409,21 @@ public: VariadicDoesNotApply }; + VariadicCallType getVariadicCallType(FunctionDecl *FDecl, + const FunctionProtoType *Proto, + Expr *Fn); + + // Used for determining in which context a type is allowed to be passed to a + // vararg function. + enum VarArgKind { + VAK_Valid, + VAK_ValidInCXX11, + VAK_Invalid + }; + + // Determines which VarArgKind fits an expression. + VarArgKind isValidVarArgType(const QualType &Ty); + /// GatherArgumentsForCall - Collector argument expressions for various /// form of call prototypes. bool GatherArgumentsForCall(SourceLocation CallLoc, @@ -6421,10 +6436,14 @@ public: bool AllowExplicit = false); // DefaultVariadicArgumentPromotion - Like DefaultArgumentPromotion, but - // will warn if the resulting type is not a POD type. + // will return ExprError() if the resulting type is not a POD type. 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 @@ -6999,10 +7018,33 @@ private: const ArraySubscriptExpr *ASE=0, bool AllowOnePastEnd=true, bool IndexNegated=false); void CheckArrayAccess(const Expr *E); - bool CheckFunctionCall(FunctionDecl *FDecl, CallExpr *TheCall); + // Used to grab the relevant information from a FormatAttr and a + // FunctionDeclaration. + struct FormatStringInfo { + unsigned FormatIdx; + unsigned FirstDataArg; + bool HasVAListArg; + }; + + bool getFormatStringInfo(const FormatAttr *Format, bool IsCXXMember, + FormatStringInfo *FSI); + bool CheckFunctionCall(FunctionDecl *FDecl, CallExpr *TheCall, + const FunctionProtoType *Proto); bool CheckObjCMethodCall(ObjCMethodDecl *Method, SourceLocation loc, Expr **Args, unsigned NumArgs); - bool CheckBlockCall(NamedDecl *NDecl, CallExpr *TheCall); + bool CheckBlockCall(NamedDecl *NDecl, CallExpr *TheCall, + const FunctionProtoType *Proto); + void CheckConstructorCall(FunctionDecl *FDecl, + Expr **Args, + unsigned NumArgs, + const FunctionProtoType *Proto, + SourceLocation Loc); + + void checkCall(NamedDecl *FDecl, Expr **Args, unsigned NumArgs, + unsigned NumProtoArgs, bool IsMemberFunction, + SourceLocation Loc, SourceRange Range, + VariadicCallType CallType); + bool CheckObjCString(Expr *Arg); @@ -7037,21 +7079,31 @@ private: FST_Unknown }; static FormatStringType GetFormatStringType(const FormatAttr *Format); - bool SemaCheckStringLiteral(const Expr *E, Expr **Args, unsigned NumArgs, - bool HasVAListArg, unsigned format_idx, - unsigned firstDataArg, FormatStringType Type, - bool inFunctionCall = true); + + enum StringLiteralCheckType { + SLCT_NotALiteral, + SLCT_UncheckedLiteral, + SLCT_CheckedLiteral + }; + + StringLiteralCheckType checkFormatStringExpr(const Expr *E, + Expr **Args, unsigned NumArgs, + bool HasVAListArg, + unsigned format_idx, + unsigned firstDataArg, + FormatStringType Type, + bool inFunctionCall = true); void CheckFormatString(const StringLiteral *FExpr, const Expr *OrigFormatExpr, Expr **Args, unsigned NumArgs, bool HasVAListArg, unsigned format_idx, unsigned firstDataArg, FormatStringType Type, bool inFunctionCall); - void CheckFormatArguments(const FormatAttr *Format, CallExpr *TheCall); - void CheckFormatArguments(const FormatAttr *Format, Expr **Args, + bool CheckFormatArguments(const FormatAttr *Format, CallExpr *TheCall); + bool CheckFormatArguments(const FormatAttr *Format, Expr **Args, unsigned NumArgs, bool IsCXXMember, SourceLocation Loc, SourceRange Range); - void CheckFormatArguments(Expr **Args, unsigned NumArgs, + bool CheckFormatArguments(Expr **Args, unsigned NumArgs, bool HasVAListArg, unsigned format_idx, unsigned firstDataArg, FormatStringType Type, SourceLocation Loc, SourceRange range); diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp index fff8d80a477..fe5e8aca3dd 100644 --- a/clang/lib/Sema/SemaChecking.cpp +++ b/clang/lib/Sema/SemaChecking.cpp @@ -16,6 +16,7 @@ #include "clang/Sema/Sema.h" #include "clang/Sema/SemaInternal.h" #include "clang/Sema/Initialization.h" +#include "clang/Sema/Lookup.h" #include "clang/Sema/ScopeInfo.h" #include "clang/Analysis/Analyses/FormatString.h" #include "clang/AST/ASTContext.h" @@ -418,34 +419,91 @@ bool Sema::CheckARMBuiltinFunctionCall(unsigned BuiltinID, CallExpr *TheCall) { return false; } -/// CheckFunctionCall - Check a direct function call for various correctness -/// and safety properties not strictly enforced by the C type system. -bool Sema::CheckFunctionCall(FunctionDecl *FDecl, CallExpr *TheCall) { - // Get the IdentifierInfo* for the called function. - IdentifierInfo *FnInfo = FDecl->getIdentifier(); +/// Given a FunctionDecl's FormatAttr, attempts to populate the FomatStringInfo +/// parameter with the FormatAttr's correct format_idx and firstDataArg. +/// Returns true when the format fits the function and the FormatStringInfo has +/// been populated. +bool Sema::getFormatStringInfo(const FormatAttr *Format, bool IsCXXMember, + FormatStringInfo *FSI) { + FSI->HasVAListArg = Format->getFirstArg() == 0; + FSI->FormatIdx = Format->getFormatIdx() - 1; + FSI->FirstDataArg = FSI->HasVAListArg ? 0 : Format->getFirstArg() - 1; - // None of the checks below are needed for functions that don't have - // simple names (e.g., C++ conversion functions). - if (!FnInfo) - return false; + // The way the format attribute works in GCC, the implicit this argument + // of member functions is counted. However, it doesn't appear in our own + // lists, so decrement format_idx in that case. + if (IsCXXMember) { + if(FSI->FormatIdx == 0) + return false; + --FSI->FormatIdx; + if (FSI->FirstDataArg != 0) + --FSI->FirstDataArg; + } + return true; +} +/// Handles the checks for format strings, non-POD arguments to vararg +/// functions, and NULL arguments passed to non-NULL parameters. +void Sema::checkCall(NamedDecl *FDecl, Expr **Args, + unsigned NumArgs, + unsigned NumProtoArgs, + bool IsMemberFunction, + SourceLocation Loc, + SourceRange Range, + VariadicCallType CallType) { // FIXME: This mechanism should be abstracted to be less fragile and // more efficient. For example, just map function ids to custom // handlers. // Printf and scanf checking. + bool HandledFormatString = false; for (specific_attr_iterator<FormatAttr> - i = FDecl->specific_attr_begin<FormatAttr>(), - e = FDecl->specific_attr_end<FormatAttr>(); i != e ; ++i) { - CheckFormatArguments(*i, TheCall); - } + I = FDecl->specific_attr_begin<FormatAttr>(), + E = FDecl->specific_attr_end<FormatAttr>(); I != E ; ++I) + if (CheckFormatArguments(*I, Args, NumArgs, IsMemberFunction, Loc, Range)) + HandledFormatString = true; + + // Refuse POD arguments that weren't caught by the format string + // checks above. + if (!HandledFormatString && CallType != VariadicDoesNotApply) + for (unsigned ArgIdx = NumProtoArgs; ArgIdx < NumArgs; ++ArgIdx) + variadicArgumentPODCheck(Args[ArgIdx], CallType); for (specific_attr_iterator<NonNullAttr> - i = FDecl->specific_attr_begin<NonNullAttr>(), - e = FDecl->specific_attr_end<NonNullAttr>(); i != e; ++i) { - CheckNonNullArguments(*i, TheCall->getArgs(), - TheCall->getCallee()->getLocStart()); - } + I = FDecl->specific_attr_begin<NonNullAttr>(), + E = FDecl->specific_attr_end<NonNullAttr>(); I != E; ++I) + CheckNonNullArguments(*I, Args, Loc); +} + +/// CheckConstructorCall - Check a constructor call for correctness and safety +/// properties not enforced by the C type system. +void Sema::CheckConstructorCall(FunctionDecl *FDecl, Expr **Args, + unsigned NumArgs, + const FunctionProtoType *Proto, + SourceLocation Loc) { + VariadicCallType CallType = + Proto->isVariadic() ? VariadicConstructor : VariadicDoesNotApply; + checkCall(FDecl, Args, NumArgs, Proto->getNumArgs(), + /*IsMemberFunction=*/true, Loc, SourceRange(), CallType); +} + +/// CheckFunctionCall - Check a direct function call for various correctness +/// and safety properties not strictly enforced by the C type system. +bool Sema::CheckFunctionCall(FunctionDecl *FDecl, CallExpr *TheCall, + const FunctionProtoType *Proto) { + bool IsMemberFunction = isa<CXXMemberCallExpr>(TheCall); + VariadicCallType CallType = getVariadicCallType(FDecl, Proto, + TheCall->getCallee()); + unsigned NumProtoArgs = Proto ? Proto->getNumArgs() : 0; + checkCall(FDecl, TheCall->getArgs(), TheCall->getNumArgs(), NumProtoArgs, + IsMemberFunction, TheCall->getRParenLoc(), + TheCall->getCallee()->getSourceRange(), CallType); + + IdentifierInfo *FnInfo = FDecl->getIdentifier(); + // None of the checks below are needed for functions that don't have + // simple names (e.g., C++ conversion functions). + if (!FnInfo) + return false; unsigned CMId = FDecl->getMemoryFunctionKind(); if (CMId == 0) @@ -464,25 +522,18 @@ bool Sema::CheckFunctionCall(FunctionDecl *FDecl, CallExpr *TheCall) { bool Sema::CheckObjCMethodCall(ObjCMethodDecl *Method, SourceLocation lbrac, Expr **Args, unsigned NumArgs) { - for (specific_attr_iterator<FormatAttr> - i = Method->specific_attr_begin<FormatAttr>(), - e = Method->specific_attr_end<FormatAttr>(); i != e ; ++i) { - - CheckFormatArguments(*i, Args, NumArgs, false, lbrac, - Method->getSourceRange()); - } + VariadicCallType CallType = + Method->isVariadic() ? VariadicMethod : VariadicDoesNotApply; - // diagnose nonnull arguments. - for (specific_attr_iterator<NonNullAttr> - i = Method->specific_attr_begin<NonNullAttr>(), - e = Method->specific_attr_end<NonNullAttr>(); i != e; ++i) { - CheckNonNullArguments(*i, Args, lbrac); - } + checkCall(Method, Args, NumArgs, Method->param_size(), + /*IsMemberFunction=*/false, + lbrac, Method->getSourceRange(), CallType); return false; } -bool Sema::CheckBlockCall(NamedDecl *NDecl, CallExpr *TheCall) { +bool Sema::CheckBlockCall(NamedDecl *NDecl, CallExpr *TheCall, + const FunctionProtoType *Proto) { const VarDecl *V = dyn_cast<VarDecl>(NDecl); if (!V) return false; @@ -491,13 +542,15 @@ bool Sema::CheckBlockCall(NamedDecl *NDecl, CallExpr *TheCall) { if (!Ty->isBlockPointerType()) return false; - // format string checking. - for (specific_attr_iterator<FormatAttr> - i = NDecl->specific_attr_begin<FormatAttr>(), - e = NDecl->specific_attr_end<FormatAttr>(); i != e ; ++i) { - CheckFormatArguments(*i, TheCall); - } + VariadicCallType CallType = + Proto && Proto->isVariadic() ? VariadicBlock : VariadicDoesNotApply ; + unsigned NumProtoArgs = Proto ? Proto->getNumArgs() : 0; + checkCall(NDecl, TheCall->getArgs(), TheCall->getNumArgs(), + NumProtoArgs, /*IsMemberFunction=*/false, + TheCall->getRParenLoc(), + TheCall->getCallee()->getSourceRange(), CallType); + return false; } @@ -1501,14 +1554,18 @@ bool Sema::SemaBuiltinLongjmp(CallExpr *TheCall) { return false; } -// Handle i > 1 ? "x" : "y", recursively. -bool Sema::SemaCheckStringLiteral(const Expr *E, Expr **Args, - unsigned NumArgs, bool HasVAListArg, - unsigned format_idx, unsigned firstDataArg, - FormatStringType Type, bool inFunctionCall) { +// 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, Expr **Args, + unsigned NumArgs, bool HasVAListArg, + unsigned format_idx, unsigned firstDataArg, + FormatStringType Type, bool inFunctionCall) { tryAgain: if (E->isTypeDependent() || E->isValueDependent()) - return false; + return SLCT_NotALiteral; E = E->IgnoreParenCasts(); @@ -1517,18 +1574,26 @@ bool Sema::SemaCheckStringLiteral(const Expr *E, Expr **Args, // 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 true; + return SLCT_CheckedLiteral; switch (E->getStmtClass()) { case Stmt::BinaryConditionalOperatorClass: case Stmt::ConditionalOperatorClass: { - const AbstractConditionalOperator *C = cast<AbstractConditionalOperator>(E); - return SemaCheckStringLiteral(C->getTrueExpr(), Args, NumArgs, HasVAListArg, - format_idx, firstDataArg, Type, - inFunctionCall) - && SemaCheckStringLiteral(C->getFalseExpr(), Args, NumArgs, HasVAListArg, - format_idx, firstDataArg, Type, - inFunctionCall); + // 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<AbstractConditionalOperator>(E); + StringLiteralCheckType Left = + checkFormatStringExpr(C->getTrueExpr(), Args, NumArgs, + HasVAListArg, format_idx, firstDataArg, + Type, inFunctionCall); + if (Left == SLCT_NotALiteral) + return SLCT_NotALiteral; + StringLiteralCheckType Right = + checkFormatStringExpr(C->getFalseExpr(), Args, NumArgs, + HasVAListArg, format_idx, firstDataArg, + Type, inFunctionCall); + return Left < Right ? Left : Right; } case Stmt::ImplicitCastExprClass: { @@ -1541,13 +1606,13 @@ bool Sema::SemaCheckStringLiteral(const Expr *E, Expr **Args, E = src; goto tryAgain; } - return false; + return SLCT_NotALiteral; case Stmt::PredefinedExprClass: // While __func__, etc., are technically not string literals, they // cannot contain format specifiers and thus are not a security // liability. - return true; + return SLCT_UncheckedLiteral; case Stmt::DeclRefExprClass: { const DeclRefExpr *DR = cast<DeclRefExpr>(E); @@ -1576,9 +1641,10 @@ bool Sema::SemaCheckStringLiteral(const Expr *E, Expr **Args, if (InitList->isStringLiteralInit()) Init = InitList->getInit(0)->IgnoreParenImpCasts(); } - return SemaCheckStringLiteral(Init, Args, NumArgs, - HasVAListArg, format_idx, firstDataArg, - Type, /*inFunctionCall*/false); + return checkFormatStringExpr(Init, Args, NumArgs, + HasVAListArg, format_idx, + firstDataArg, Type, + /*inFunctionCall*/false); } } @@ -1612,14 +1678,14 @@ bool Sema::SemaCheckStringLiteral(const Expr *E, Expr **Args, // We can't pass a 'scanf' string to a 'printf' function. if (PVIndex == PVFormat->getFormatIdx() && Type == GetFormatStringType(PVFormat)) - return true; + return SLCT_UncheckedLiteral; } } } } } - return false; + return SLCT_NotALiteral; } case Stmt::CallExprClass: @@ -1633,22 +1699,22 @@ bool Sema::SemaCheckStringLiteral(const Expr *E, Expr **Args, --ArgIndex; const Expr *Arg = CE->getArg(ArgIndex - 1); - return SemaCheckStringLiteral(Arg, Args, NumArgs, HasVAListArg, - format_idx, firstDataArg, Type, - inFunctionCall); + return checkFormatStringExpr(Arg, Args, NumArgs, + HasVAListArg, format_idx, firstDataArg, + Type, inFunctionCall); } 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 SemaCheckStringLiteral(Arg, Args, NumArgs, HasVAListArg, - format_idx, firstDataArg, Type, - inFunctionCall); + return checkFormatStringExpr(Arg, Args, NumArgs, + HasVAListArg, format_idx, + firstDataArg, Type, inFunctionCall); } } } - return false; + return SLCT_NotALiteral; } case Stmt::ObjCStringLiteralClass: case Stmt::StringLiteralClass: { @@ -1662,14 +1728,14 @@ bool Sema::SemaCheckStringLiteral(const Expr *E, Expr **Args, if (StrE) { CheckFormatString(StrE, E, Args, NumArgs, HasVAListArg, format_idx, firstDataArg, Type, inFunctionCall); - return true; + return SLCT_CheckedLiteral; } - return false; + return SLCT_NotALiteral; } default: - return false; + return SLCT_NotALiteral; } } @@ -1700,42 +1766,34 @@ Sema::FormatStringType Sema::GetFormatStringType(const FormatAttr *Format) { /// CheckPrintfScanfArguments - Check calls to printf and scanf (and similar /// functions) for correct use of format strings. -void Sema::CheckFormatArguments(const FormatAttr *Format, CallExpr *TheCall) { - bool IsCXXMember = false; - // The way the format attribute works in GCC, the implicit this argument - // of member functions is counted. However, it doesn't appear in our own - // lists, so decrement format_idx in that case. - IsCXXMember = isa<CXXMemberCallExpr>(TheCall); - CheckFormatArguments(Format, TheCall->getArgs(), TheCall->getNumArgs(), - IsCXXMember, TheCall->getRParenLoc(), - TheCall->getCallee()->getSourceRange()); +/// Returns true if a format string has been fully checked. +bool Sema::CheckFormatArguments(const FormatAttr *Format, CallExpr *TheCall) { + bool IsCXXMember = isa<CXXMemberCallExpr>(TheCall); + return CheckFormatArguments(Format, TheCall->getArgs(), + TheCall->getNumArgs(), + IsCXXMember, TheCall->getRParenLoc(), + TheCall->getCallee()->getSourceRange()); } -void Sema::CheckFormatArguments(const FormatAttr *Format, Expr **Args, +bool Sema::CheckFormatArguments(const FormatAttr *Format, Expr **Args, unsigned NumArgs, bool IsCXXMember, SourceLocation Loc, SourceRange Range) { - bool HasVAListArg = Format->getFirstArg() == 0; - unsigned format_idx = Format->getFormatIdx() - 1; - unsigned firstDataArg = HasVAListArg ? 0 : Format->getFirstArg() - 1; - if (IsCXXMember) { - if (format_idx == 0) - return; - --format_idx; - if(firstDataArg != 0) - --firstDataArg; - } - CheckFormatArguments(Args, NumArgs, HasVAListArg, format_idx, - firstDataArg, GetFormatStringType(Format), Loc, Range); + FormatStringInfo FSI; + if (getFormatStringInfo(Format, IsCXXMember, &FSI)) + return CheckFormatArguments(Args, NumArgs, FSI.HasVAListArg, FSI.FormatIdx, + FSI.FirstDataArg, GetFormatStringType(Format), + Loc, Range); + return false; } -void Sema::CheckFormatArguments(Expr **Args, unsigned NumArgs, +bool Sema::CheckFormatArguments(Expr **Args, unsigned NumArgs, bool HasVAListArg, unsigned format_idx, unsigned firstDataArg, FormatStringType Type, SourceLocation Loc, SourceRange Range) { // CHECK: printf/scanf-like function is called with no format string. if (format_idx >= NumArgs) { Diag(Loc, diag::warn_missing_format_string) << Range; - return; + return false; } const Expr *OrigFormatExpr = Args[format_idx]->IgnoreParenCasts(); @@ -1752,14 +1810,17 @@ void Sema::CheckFormatArguments(Expr **Args, unsigned NumArgs, // C string (e.g. "%d") // 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. - if (SemaCheckStringLiteral(OrigFormatExpr, Args, NumArgs, HasVAListArg, - format_idx, firstDataArg, Type)) - return; // Literal format string found, check done! + StringLiteralCheckType CT = + checkFormatStringExpr(OrigFormatExpr, Args, NumArgs, HasVAListArg, + format_idx, firstDataArg, Type); + if (CT != SLCT_NotALiteral) + // Literal format string found, check done! + return CT == SLCT_CheckedLiteral; // Strftime is particular as it always uses a single 'time' argument, // so it is safe to pass a non-literal string. if (Type == FST_Strftime) - return; + return false; // Do not emit diag when the string param is a macro expansion and the // format is either NSString or CFString. This is a hack to prevent @@ -1767,7 +1828,7 @@ void Sema::CheckFormatArguments(Expr **Args, unsigned NumArgs, // which are usually used in place of NS and CF string literals. if (Type == FST_NSString && SourceMgr.isInSystemMacro(Args[format_idx]->getLocStart())) - return; + return false; // If there are no arguments specified, warn with -Wformat-security, otherwise // warn only with -Wformat-nonliteral. @@ -1779,6 +1840,7 @@ void Sema::CheckFormatArguments(Expr **Args, unsigned NumArgs, Diag(Args[format_idx]->getLocStart(), diag::warn_format_nonliteral) << OrigFormatExpr->getSourceRange(); + return false; } namespace { @@ -2138,7 +2200,11 @@ public: bool HandlePrintfSpecifier(const analyze_printf::PrintfSpecifier &FS, const char *startSpecifier, unsigned specifierLen); - + bool checkFormatExpr(const analyze_printf::PrintfSpecifier &FS, + const char *StartSpecifier, + unsigned SpecifierLen, + const Expr *E); + bool HandleAmount(const analyze_format_string::OptionalAmount &Amt, unsigned k, const char *startSpecifier, unsigned specifierLen); void HandleInvalidAmount(const analyze_printf::PrintfSpecifier &FS, @@ -2152,6 +2218,9 @@ public: const analyze_printf::OptionalFlag &ignoredFlag, const analyze_printf::OptionalFlag &flag, const char *startSpecifier, unsigned specifierLen); + bool checkForCStrMembers(const analyze_printf::ArgTypeResult &ATR, + const Expr *E, const CharSourceRange &CSR); + }; } @@ -2269,6 +2338,64 @@ void CheckPrintfHandler::HandleIgnoredFlag( getSpecifierRange(ignoredFlag.getPosition(), 1))); } +// Determines if the specified is a C++ class or struct containing +// a member with the specified name and kind (e.g. a CXXMethodDecl named +// "c_str()"). +template<typename MemberKind> +static llvm::SmallPtrSet<MemberKind*, 1> +CXXRecordMembersNamed(StringRef Name, Sema &S, QualType Ty) { + const RecordType *RT = Ty->getAs<RecordType>(); + llvm::SmallPtrSet<MemberKind*, 1> Results; + + if (!RT) + return Results; + const CXXRecordDecl *RD = dyn_cast<CXXRecordDecl>(RT->getDecl()); + if (!RD) + return Results; + + LookupResult R(S, &S.PP.getIdentifierTable().get(Name), SourceLocation(), + Sema::LookupMemberName); + + // We just need to include all members of the right kind turned up by the + // filter, at this point. + if (S.LookupQualifiedName(R, RT->getDecl())) + for (LookupResult::iterator I = R.begin(), E = R.end(); I != E; ++I) { + NamedDecl *decl = (*I)->getUnderlyingDecl(); + if (MemberKind *FK = dyn_cast<MemberKind>(decl)) + Results.insert(FK); + } + return Results; +} + +// Check if a (w)string was passed when a (w)char* was needed, and offer a +// better diagnostic if so. ATR is assumed to be valid. +// Returns true when a c_str() conversion method is found. +bool CheckPrintfHandler::checkForCStrMembers( + const analyze_printf::ArgTypeResult &ATR, const Expr *E, + const CharSourceRange &CSR) { + typedef llvm::SmallPtrSet<CXXMethodDecl*, 1> MethodSet; + + MethodSet Results = + CXXRecordMembersNamed<CXXMethodDecl>("c_str", S, E->getType()); + + for (MethodSet::iterator MI = Results.begin(), ME = Results.end(); + MI != ME; ++MI) { + const CXXMethodDecl *Method = *MI; + if (Method->getNumParams() == 0 && + ATR.matchesType(S.Context, Method->getResultType())) { + // FIXME: Suggest parens if the expression needs them. + SourceLocation EndLoc = + S.getPreprocessor().getLocForEndOfToken(E->getLocEnd()); + S.Diag(E->getLocStart(), diag::note_printf_c_str) + << "c_str()" + << FixItHint::CreateInsertion(EndLoc, ".c_str()"); + return true; + } + } + + return false; +} + bool CheckPrintfHandler::HandlePrintfSpecifier(const analyze_printf::PrintfSpecifier &FS, @@ -2396,20 +2523,30 @@ CheckPrintfHandler::HandlePrintfSpecifier(const analyze_printf::PrintfSpecifier if (!CheckNumArgs(FS, CS, startSpecifier, specifierLen, argIndex)) return false; + return checkFormatExpr(FS, startSpecifier, specifierLen, + getDataArg(argIndex)); +} + +bool +CheckPrintfHandler::checkFormatExpr(const analyze_printf::PrintfSpecifier &FS, + const char *StartSpecifier, + unsigned SpecifierLen, + const Expr *E) { + using namespace analyze_format_string; + using namespace analyze_printf; // Now type check the data expression that matches the // format specifier. - const Expr *Ex = getDataArg(argIndex); const analyze_printf::ArgTypeResult &ATR = FS.getArgType(S.Context, ObjCContext); - if (ATR.isValid() && !ATR.matchesType(S.Context, Ex->getType())) { + if (ATR.isValid() && !ATR.matchesType(S.Context, E->getType())) { // Look through argument promotions for our error message's reported type. // This includes the integral and floating promotions, but excludes array // and function pointer decay; seeing that an argument intended to be a // string has type 'char [6]' is probably more confusing than 'char *'. - if (const ImplicitCastExpr *ICE = dyn_cast<ImplicitCastExpr>(Ex)) { + if (const ImplicitCastExpr *ICE = dyn_cast<ImplicitCastExpr>(E)) { if (ICE->getCastKind() == CK_IntegralCast || ICE->getCastKind() == CK_FloatingCast) { - Ex = ICE->getSubExpr(); + E = ICE->getSubExpr(); // Check if we didn't match because of an implicit cast from a 'char' // or 'short' to an 'int'. This is done because printf is a varargs @@ -2417,7 +2554,7 @@ CheckPrintfHandler::HandlePrintfSpecifier(const analyze_printf::PrintfSpecifier if (ICE->getType() == S.Context.IntTy || ICE->getType() == S.Context.UnsignedIntTy) { // All further checking is done on the subexpression. - if (ATR.matchesType(S.Context, Ex->getType())) + if (ATR.matchesType(S.Context, E->getType())) return true; } } @@ -2425,7 +2562,7 @@ CheckPrintfHandler::HandlePrintfSpecifier(const analyze_printf::PrintfSpecifier // We may be able to offer a FixItHint if it is a supported type. PrintfSpecifier fixedFS = FS; - bool success = fixedFS.fixType(Ex->getType(), S.getLangOpts(), + bool success = fixedFS.fixType(E->getType(), S.getLangOpts(), S.Context, ObjCContext); if (success) { @@ -2436,24 +2573,38 @@ CheckPrintfHandler::HandlePrintfSpecifier(const analyze_printf::PrintfSpecifier EmitFormatDiagnostic( S.PDiag(diag::warn_printf_conversion_argument_type_mismatch) - << ATR.getRepresentativeTypeName(S.Context) << Ex->getType() - << Ex->getSourceRange(), - Ex->getLocStart(), + << ATR.getRepresentativeTypeName(S.Context) << E->getType() + << E->getSourceRange(), + E->getLocStart(), /*IsStringLocation*/false, - getSpecifierRange(startSpecifier, specifierLen), + getSpecifierRange(StartSpecifier, SpecifierLen), FixItHint::CreateReplacement( - getSpecifierRange(startSpecifier, specifierLen), + getSpecifierRange(StartSpecifier, SpecifierLen), os.str())); - } - else { - EmitFormatDiagnostic( - S.PDiag(diag::warn_printf_conversion_argument_type_mismatch) - << ATR.getRepresentativeTypeName(S.Context) << Ex->getType() - << getSpecifierRange(startSpecifier, specifierLen) - << Ex->getSourceRange(), - Ex->getLocStart(), - /*IsStringLocation*/false, - getSpecifierRange(startSpecifier, specifierLen)); + } else { + const CharSourceRange &CSR = getSpecifierRange(StartSpecifier, + SpecifierLen); + // 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(E->getType()) == Sema::VAK_Invalid) { + EmitFormatDiagnostic( + S.PDiag(diag::warn_non_pod_vararg_with_format_string) + << S.getLangOpts().CPlusPlus0x + << E->getType() + << ATR.getRepresentativeTypeName(S.Context) + << CSR + << E->getSourceRange(), + E->getLocStart(), /*IsStringLocation*/false, CSR); + + checkForCStrMembers(ATR, E, CSR); + } else + EmitFormatDiagnostic( + S.PDiag(diag::warn_printf_conversion_argument_type_mismatch) + << ATR.getRepresentativeTypeName(S.Context) << E->getType() + << CSR + << E->getSourceRange(), + E->getLocStart(), /*IsStringLocation*/false, CSR); } } diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp index 2a11c745935..828083527a7 100644 --- a/clang/lib/Sema/SemaDeclCXX.cpp +++ b/clang/lib/Sema/SemaDeclCXX.cpp @@ -9028,13 +9028,6 @@ Sema::BuildCXXConstructExpr(SourceLocation ConstructLoc, QualType DeclInitType, unsigned NumExprs = ExprArgs.size(); Expr **Exprs = (Expr **)ExprArgs.release(); - for (specific_attr_iterator<NonNullAttr> - i = Constructor->specific_attr_begin<NonNullAttr>(), - e = Constructor->specific_attr_end<NonNullAttr>(); i != e; ++i) { - const NonNullAttr *NonNull = *i; - CheckNonNullArguments(NonNull, ExprArgs.get(), ConstructLoc); - } - MarkFunctionReferenced(ConstructLoc, Constructor); return Owned(CXXConstructExpr::Create(Context, DeclInitType, ConstructLoc, Constructor, Elidable, Exprs, NumExprs, @@ -9100,7 +9093,7 @@ void Sema::FinalizeVarWithDestructor(VarDecl *VD, const RecordType *Record) { bool Sema::CompleteConstructorCall(CXXConstructorDecl *Constructor, MultiExprArg ArgsPtr, - SourceLocation Loc, + SourceLocation Loc, ASTOwningVector<Expr*> &ConvertedArgs, bool AllowExplicit) { // FIXME: This duplicates a lot of code from Sema::ConvertArgumentsForCall. @@ -9128,7 +9121,8 @@ Sema::CompleteConstructorCall(CXXConstructorDecl *Constructor, DiagnoseSentinelCalls(Constructor, Loc, AllArgs.data(), AllArgs.size()); - // FIXME: Missing call to CheckFunctionCall or equivalent + CheckConstructorCall(Constructor, AllArgs.data(), AllArgs.size(), + Proto, Loc); return Invalid; } diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp index adc8ea814e7..de0de89b833 100644 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -590,12 +590,59 @@ ExprResult Sema::DefaultArgumentPromotion(Expr *E) { return Owned(E); } +/// Determine the degree of POD-ness for an expression. +/// Incomplete types are considered POD, since this check can be performed +/// when we're in an unevaluated context. +Sema::VarArgKind Sema::isValidVarArgType(const QualType &Ty) { + if (Ty->isIncompleteType() || Ty.isCXX98PODType(Context)) + return VAK_Valid; + // C++0x [expr.call]p7: + // Passing a potentially-evaluated argument of class type (Clause 9) + // having a non-trivial copy constructor, a non-trivial move constructor, + // or a non-trivial destructor, with no corresponding parameter, + // is conditionally-supported with implementation-defined semantics. + + if (getLangOpts().CPlusPlus0x && !Ty->isDependentType()) + if (CXXRecordDecl *Record = Ty->getAsCXXRecordDecl()) + if (Record->hasTrivialCopyConstructor() && + Record->hasTrivialMoveConstructor() && + Record->hasTrivialDestructor()) + return VAK_ValidInCXX11; + + if (getLangOpts().ObjCAutoRefCount && Ty->isObjCLifetimeType()) + return VAK_Valid; + return VAK_Invalid; +} + +bool Sema::variadicArgumentPODCheck(const Expr *E, VariadicCallType CT) { + // Don't allow one to pass an Objective-C interface to a vararg. + const QualType & Ty = E->getType(); + + // Complain about passing non-POD types through varargs. + switch (isValidVarArgType(Ty)) { + 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: + return DiagRuntimeBehavior(E->getLocStart(), 0, + PDiag(diag::warn_cannot_pass_non_pod_arg_to_vararg) + << getLangOpts().CPlusPlus0x << Ty << CT); + } + // c++ rules are enforced elsewhere. + return false; +} + /// DefaultVariadicArgumentPromotion - Like DefaultArgumentPromotion, but /// will warn if the resulting type is not a POD type, and rejects ObjC /// interfaces passed by value. ExprResult Sema::DefaultVariadicArgumentPromotion(Expr *E, VariadicCallType CT, FunctionDecl *FDecl) { - if (const BuiltinType *PlaceholderTy = E->getType()->getAsPlaceholderType()) { + const QualType &Ty = E->getType(); + if (const BuiltinType *PlaceholderTy = Ty->getAsPlaceholderType()) { // Strip the unbridged-cast placeholder expression off, if applicable. if (PlaceholderTy->getKind() == BuiltinType::ARCUnbridgedCast && (CT == VariadicMethod || @@ -616,77 +663,44 @@ ExprResult Sema::DefaultVariadicArgumentPromotion(Expr *E, VariadicCallType CT, return ExprError(); E = ExprRes.take(); - // Don't allow one to pass an Objective-C interface to a vararg. - if (E->getType()->isObjCObjectType() && + if (Ty->isObjCObjectType() && DiagRuntimeBehavior(E->getLocStart(), 0, PDiag(diag::err_cannot_pass_objc_interface_to_vararg) - << E->getType() << CT)) + << Ty << CT)) return ExprError(); - // Complain about passing non-POD types through varargs. However, don't - // perform this check for incomplete types, which we can get here when we're - // in an unevaluated context. - if (!E->getType()->isIncompleteType() && - !E->getType().isCXX98PODType(Context)) { - // C++0x [expr.call]p7: - // Passing a potentially-evaluated argument of class type (Clause 9) - // having a non-trivial copy constructor, a non-trivial move constructor, - // or a non-trivial destructor, with no corresponding parameter, - // is conditionally-supported with implementation-defined semantics. - bool TrivialEnough = false; - if (getLangOpts().CPlusPlus0x && !E->getType()->isDependentType()) { - if (CXXRecordDecl *Record = E->getType()->getAsCXXRecordDecl()) { - if (Record->hasTrivialCopyConstructor() && - Record->hasTrivialMoveConstructor() && - Record->hasTrivialDestructor()) { - DiagRuntimeBehavior(E->getLocStart(), 0, - PDiag(diag::warn_cxx98_compat_pass_non_pod_arg_to_vararg) - << E->getType() << CT); - TrivialEnough = true; - } - } - } + // Diagnostics regarding non-POD argument types are + // emitted along with format string checking in Sema::CheckFunctionCall(). + if (isValidVarArgType(Ty) == VAK_Invalid) { + // Turn this into a trap. + CXXScopeSpec SS; + SourceLocation TemplateKWLoc; + UnqualifiedId Name; + Name.setIdentifier(PP.getIdentifierInfo("__builtin_trap"), + E->getLocStart()); + ExprResult TrapFn = ActOnIdExpression(TUScope, SS, TemplateKWLoc, + Name, true, false); + if (TrapFn.isInvalid()) + return ExprError(); - if (!TrivialEnough && - getLangOpts().ObjCAutoRefCount && - E->getType()->isObjCLifetimeType()) - TrivialEnough = true; - - if (TrivialEnough) { - // Nothing to diagnose. This is okay. - } else if (DiagRuntimeBehavior(E->getLocStart(), 0, - PDiag(diag::warn_cannot_pass_non_pod_arg_to_vararg) - << getLangOpts().CPlusPlus0x << E->getType() - << CT)) { - // Turn this into a trap. - CXXScopeSpec SS; - SourceLocation TemplateKWLoc; - UnqualifiedId Name; - Name.setIdentifier(PP.getIdentifierInfo("__builtin_trap"), - E->getLocStart()); - ExprResult TrapFn = ActOnIdExpression(TUScope, SS, TemplateKWLoc, Name, - true, false); - if (TrapFn.isInvalid()) - return ExprError(); + ExprResult Call = ActOnCallExpr(TUScope, TrapFn.get(), + E->getLocStart(), MultiExprArg(), + E->getLocEnd()); + if (Call.isInvalid()) + return ExprError(); - ExprResult Call = ActOnCallExpr(TUScope, TrapFn.get(), E->getLocStart(), - MultiExprArg(), E->getLocEnd()); - if (Call.isInvalid()) - return ExprError(); - - ExprResult Comma = ActOnBinOp(TUScope, E->getLocStart(), tok::comma, - Call.get(), E); - if (Comma.isInvalid()) - return ExprError(); - E = Comma.get(); - } + ExprResult Comma = ActOnBinOp(TUScope, E->getLocStart(), tok::comma, + Call.get(), E); + if (Comma.isInvalid()) + return ExprError(); + return Comma.get(); } - // c++ rules are enforced elsewhere. + if (!getLangOpts().CPlusPlus && RequireCompleteType(E->getExprLoc(), E->getType(), diag::err_call_incomplete_argument)) return ExprError(); - + return Owned(E); } @@ -3415,6 +3429,25 @@ ExprResult Sema::BuildCXXDefaultArgExpr(SourceLocation CallLoc, return Owned(CXXDefaultArgExpr::Create(Context, CallLoc, Param)); } + +Sema::VariadicCallType +Sema::getVariadicCallType(FunctionDecl *FDecl, const FunctionProtoType *Proto, + Expr *Fn) { + if (Proto && Proto->isVariadic()) { + if (dyn_cast_or_null<CXXConstructorDecl>(FDecl)) + return VariadicConstructor; + else if (Fn && Fn->getType()->isBlockPointerType()) + return VariadicBlock; + else if (FDecl) { + if (CXXMethodDecl *Method = dyn_cast_or_null<CXXMethodDecl>(FDecl)) + if (Method->isInstance()) + return VariadicMethod; + return VariadicFunction; + } + } + return VariadicDoesNotApply; +} + /// ConvertArgumentsForCall - Converts the arguments specified in /// Args/NumArgs to the parameter types of the function FDecl with /// function prototype Proto. Call is the call expression itself, and @@ -3506,12 +3539,8 @@ Sema::ConvertArgumentsForCall(CallExpr *Call, Expr *Fn, } } SmallVector<Expr *, 8> AllArgs; - VariadicCallType CallType = - Proto->isVariadic() ? VariadicFunction : VariadicDoesNotApply; - if (Fn->getType()->isBlockPointerType()) - CallType = VariadicBlock; // Block - else if (isa<MemberExpr>(Fn)) - CallType = VariadicMethod; + VariadicCallType CallType = getVariadicCallType(FDecl, Proto, Fn); + Invalid = GatherArgumentsForCall(Call->getLocStart(), FDecl, Proto, 0, Args, NumArgs, AllArgs, CallType); if (Invalid) @@ -3600,7 +3629,6 @@ bool Sema::GatherArgumentsForCall(SourceLocation CallLoc, // If this is a variadic call, handle args passed through "...". if (CallType != VariadicDoesNotApply) { - // Assume that extern "C" functions with variadic arguments that // return __unknown_anytype aren't *really* variadic. if (Proto->getResultType() == Context.UnknownAnyTy && @@ -3938,7 +3966,8 @@ Sema::BuildResolvedCallExpr(Expr *Fn, NamedDecl *NDecl, TheCall->setType(FuncT->getCallResultType(Context)); TheCall->setValueKind(Expr::getValueKindForType(FuncT->getResultType())); - if (const FunctionProtoType *Proto = dyn_cast<FunctionProtoType>(FuncT)) { + const FunctionProtoType *Proto = dyn_cast<FunctionProtoType>(FuncT); + if (Proto) { if (ConvertArgumentsForCall(TheCall, Fn, FDecl, Proto, Args, NumArgs, RParenLoc, IsExecConfig)) return ExprError(); @@ -3950,8 +3979,7 @@ Sema::BuildResolvedCallExpr(Expr *Fn, NamedDecl *NDecl, // on our knowledge of the function definition. const FunctionDecl *Def = 0; if (FDecl->hasBody(Def) && NumArgs != Def->param_size()) { - const FunctionProtoType *Proto - = Def->getType()->getAs<FunctionProtoType>(); + Proto = Def->getType()->getAs<FunctionProtoType>(); if (!Proto || !(Proto->isVariadic() && NumArgs >= Def->param_size())) Diag(RParenLoc, diag::warn_call_wrong_number_of_arguments) << (NumArgs > Def->param_size()) << FDecl << Fn->getSourceRange(); @@ -4009,13 +4037,13 @@ Sema::BuildResolvedCallExpr(Expr *Fn, NamedDecl *NDecl, // Do special checking on direct calls to functions. if (FDecl) { - if (CheckFunctionCall(FDecl, TheCall)) + if (CheckFunctionCall(FDecl, TheCall, Proto)) return ExprError(); if (BuiltinID) return CheckBuiltinFunctionCall(BuiltinID, TheCall); } else if (NDecl) { - if (CheckBlockCall(NDecl, TheCall)) + if (CheckBlockCall(NDecl, TheCall, Proto)) return ExprError(); } diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp index 013af8a8f62..9a14cad72be 100644 --- a/clang/lib/Sema/SemaOverload.cpp +++ b/clang/lib/Sema/SemaOverload.cpp @@ -10720,7 +10720,7 @@ Sema::BuildCallToMemberFunction(Scope *S, Expr *MemExprE, DiagnoseSentinelCalls(Method, LParenLoc, Args, NumArgs); - if (CheckFunctionCall(Method, TheCall)) + if (CheckFunctionCall(Method, TheCall, Proto)) return ExprError(); if ((isa<CXXConstructorDecl>(CurContext) || @@ -11028,7 +11028,7 @@ Sema::BuildCallToObjectOfClassType(Scope *S, Expr *Obj, DiagnoseSentinelCalls(Method, LParenLoc, Args, NumArgs); - if (CheckFunctionCall(Method, TheCall)) + if (CheckFunctionCall(Method, TheCall, Proto)) return true; return MaybeBindToTemporary(TheCall); @@ -11208,7 +11208,7 @@ ExprResult Sema::BuildLiteralOperatorCall(LookupResult &R, if (CheckCallReturnType(FD->getResultType(), UDSuffixLoc, UDL, FD)) return ExprError(); - if (CheckFunctionCall(FD, UDL)) + if (CheckFunctionCall(FD, UDL, NULL)) return ExprError(); return MaybeBindToTemporary(UDL); diff --git a/clang/test/SemaCXX/printf-block.cpp b/clang/test/SemaCXX/printf-block.cpp new file mode 100644 index 00000000000..a7d266bac5c --- /dev/null +++ b/clang/test/SemaCXX/printf-block.cpp @@ -0,0 +1,18 @@ +// RUN: %clang_cc1 -fsyntax-only -fblocks -Wformat -verify %s -Wno-error=non-pod-varargs + +int (^block) (int, const char *,...) __attribute__((__format__(__printf__,2,3))) = ^ __attribute__((__format__(__printf__,2,3))) (int arg, const char *format,...) {return 5;}; + +class HasNoCStr { + const char *str; + public: + HasNoCStr(const char *s): str(s) { } + const char *not_c_str() {return str;} +}; + +void test_block() { + const char str[] = "test"; + HasNoCStr hncs(str); + int n = 4; + block(n, "%s %d", str, n); // no-warning + block(n, "%s %s", hncs, n); // expected-warning{{cannot pass non-POD object of type 'HasNoCStr' to variadic function; expected type from format string was 'char *'}} expected-warning{{format specifies type 'char *' but the argument has type 'int'}} +} diff --git a/clang/test/SemaCXX/printf-cstr.cpp b/clang/test/SemaCXX/printf-cstr.cpp new file mode 100644 index 00000000000..efb2f704e1e --- /dev/null +++ b/clang/test/SemaCXX/printf-cstr.cpp @@ -0,0 +1,53 @@ +// RUN: %clang_cc1 -fsyntax-only -Wformat -verify %s -Wno-error=non-pod-varargs + +#include <stdarg.h> + +extern "C" { +extern int printf(const char *restrict, ...); +extern int sprintf(char *, const char *restrict, ...); +} + +class HasCStr { + const char *str; + public: + HasCStr(const char *s): str(s) { } + const char *c_str() {return str;} +}; + +class HasNoCStr { + const char *str; + public: + HasNoCStr(const char *s): str(s) { } + const char *not_c_str() {return str;} +}; + +extern const char extstr[16]; +void pod_test() { + char str[] = "test"; + char dest[32]; + char formatString[] = "non-const %s %s"; + HasCStr hcs(str); + HasNoCStr hncs(str); + int n = 10; + + printf("%d: %s\n", n, hcs.c_str()); + printf("%d: %s\n", n, hcs); // expected-warning{{cannot pass non-POD object of type 'HasCStr' to variadic function; expected type from format string was 'char *'}} expected-note{{did you mean to call the c_str() method?}} + printf("%d: %s\n", n, hncs); // expected-warning{{cannot pass non-POD object of type 'HasNoCStr' to variadic function; expected type from format string was 'char *'}} + sprintf(str, "%d: %s", n, hcs); // expected-warning{{cannot pass non-POD object of type 'HasCStr' to variadic function; expected type from format string was 'char *'}} expected-note{{did you mean to call the c_str() method?}} + + printf(formatString, hcs, hncs); // expected-warning{{cannot pass object of non-POD type 'HasCStr' through variadic function}} expected-warning{{cannot pass object of non-POD type 'HasNoCStr' through variadic function}} + printf(extstr, hcs, n); // expected-warning{{cannot pass object of non-POD type 'HasCStr' through variadic function}} +} + +struct Printf { + Printf(); + Printf(const Printf&); + Printf(const char *,...) __attribute__((__format__(__printf__,2,3))); +}; + +void constructor_test() { + const char str[] = "test"; + HasCStr hcs(str); + Printf p("%s %d %s", str, 10, 10); // expected-warning {{format specifies type 'char *' but the argument has type 'int'}} + Printf q("%s %d", hcs, 10); // expected-warning {{cannot pass non-POD object of type 'HasCStr' to variadic function; expected type from format string was 'char *'}} expected-note{{did you mean to call the c_str() method?}} +} |