diff --git a/clang/include/clang/AST/Attr.h b/clang/include/clang/AST/Attr.h index 288a1c4a3283..8d4e1090cff6 100644 --- a/clang/include/clang/AST/Attr.h +++ b/clang/include/clang/AST/Attr.h @@ -347,6 +347,7 @@ public: Type(type), formatIdx(idx), firstArg(first) {} const std::string& getType() const { return Type; } + void setType(const std::string &type) { Type = type; } int getFormatIdx() const { return formatIdx; } int getFirstArg() const { return firstArg; } diff --git a/clang/include/clang/AST/Decl.h b/clang/include/clang/AST/Decl.h index a4e30653b2c2..b39467a55ff6 100644 --- a/clang/include/clang/AST/Decl.h +++ b/clang/include/clang/AST/Decl.h @@ -600,7 +600,7 @@ public: PreviousDeclaration = PrevDecl; } - unsigned getBuiltinID() const; + unsigned getBuiltinID(ASTContext &Context) const; // Iterator access to formal parameters. unsigned param_size() const { return getNumParams(); } diff --git a/clang/include/clang/AST/Expr.h b/clang/include/clang/AST/Expr.h index 2baaa438d02b..b7c5c8920d5a 100644 --- a/clang/include/clang/AST/Expr.h +++ b/clang/include/clang/AST/Expr.h @@ -834,7 +834,7 @@ public: /// isBuiltinCall - If this is a call to a builtin, return the builtin ID. If /// not, return 0. - unsigned isBuiltinCall() const; + unsigned isBuiltinCall(ASTContext &Context) const; SourceLocation getRParenLoc() const { return RParenLoc; } diff --git a/clang/lib/AST/Decl.cpp b/clang/lib/AST/Decl.cpp index 13b40e8afe27..ca1fa0c1e12f 100644 --- a/clang/lib/AST/Decl.cpp +++ b/clang/lib/AST/Decl.cpp @@ -259,15 +259,33 @@ Stmt *FunctionDecl::getBody(const FunctionDecl *&Definition) const { /// will be 0 for functions that do not correspond to a builtin, a /// value of type \c Builtin::ID if in the target-independent range /// \c [1,Builtin::First), or a target-specific builtin value. -unsigned FunctionDecl::getBuiltinID() const { - if (getIdentifier() && - (getDeclContext()->isTranslationUnit() || - (isa(getDeclContext()) && - cast(getDeclContext())->getLanguage() - == LinkageSpecDecl::lang_c))) - return getIdentifier()->getBuiltinID(); - - // Not a builtin. +unsigned FunctionDecl::getBuiltinID(ASTContext &Context) const { + if (!getIdentifier() || !getIdentifier()->getBuiltinID()) + return 0; + + unsigned BuiltinID = getIdentifier()->getBuiltinID(); + if (!Context.BuiltinInfo.isPredefinedLibFunction(BuiltinID)) + return BuiltinID; + + // This function has the name of a known C library + // function. Determine whether it actually refers to the C library + // function or whether it just has the same name. + + // If this function is at translation-unit scope and we're not in + // C++, it refers to the C library function. + if (!Context.getLangOptions().CPlusPlus && + getDeclContext()->isTranslationUnit()) + return BuiltinID; + + // If the function is in an extern "C" linkage specification and is + // not marked "overloadable", it's the real function. + if (isa(getDeclContext()) && + cast(getDeclContext())->getLanguage() + == LinkageSpecDecl::lang_c && + !getAttr()) + return BuiltinID; + + // Not a builtin return 0; } diff --git a/clang/lib/AST/Expr.cpp b/clang/lib/AST/Expr.cpp index 7063b768c531..7a04ffcedc4f 100644 --- a/clang/lib/AST/Expr.cpp +++ b/clang/lib/AST/Expr.cpp @@ -173,7 +173,7 @@ void CallExpr::setNumArgs(ASTContext& C, unsigned NumArgs) { /// isBuiltinCall - If this is a call to a builtin, return the builtin ID. If /// not, return 0. -unsigned CallExpr::isBuiltinCall() const { +unsigned CallExpr::isBuiltinCall(ASTContext &Context) const { // All simple function calls (e.g. func()) are implicitly cast to pointer to // function. As a result, we try and obtain the DeclRefExpr from the // ImplicitCastExpr. @@ -192,7 +192,7 @@ unsigned CallExpr::isBuiltinCall() const { if (!FDecl->getIdentifier()) return 0; - return FDecl->getBuiltinID(); + return FDecl->getBuiltinID(Context); } @@ -922,7 +922,7 @@ bool Expr::isIntegerConstantExpr(llvm::APSInt &Result, ASTContext &Ctx, // If this is a call to a builtin function, constant fold it otherwise // reject it. - if (CE->isBuiltinCall()) { + if (CE->isBuiltinCall(Ctx)) { EvalResult EvalResult; if (CE->Evaluate(EvalResult, Ctx)) { assert(!EvalResult.HasSideEffects && @@ -1205,7 +1205,7 @@ bool Expr::isIntegerConstantExpr(llvm::APSInt &Result, ASTContext &Ctx, // expression, and it is fully evaluated. This is an important GNU // extension. See GCC PR38377 for discussion. if (const CallExpr *CallCE = dyn_cast(Cond->IgnoreParenCasts())) - if (CallCE->isBuiltinCall() == Builtin::BI__builtin_constant_p) { + if (CallCE->isBuiltinCall(Ctx) == Builtin::BI__builtin_constant_p) { EvalResult EVResult; if (!Evaluate(EVResult, Ctx) || EVResult.HasSideEffects) return false; diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp index c7cc85425c8d..3a3b1527fa44 100644 --- a/clang/lib/AST/ExprConstant.cpp +++ b/clang/lib/AST/ExprConstant.cpp @@ -350,7 +350,8 @@ APValue PointerExprEvaluator::VisitCastExpr(const CastExpr* E) { } APValue PointerExprEvaluator::VisitCallExpr(CallExpr *E) { - if (E->isBuiltinCall() == Builtin::BI__builtin___CFStringMakeConstantString) + if (E->isBuiltinCall(Info.Ctx) == + Builtin::BI__builtin___CFStringMakeConstantString) return APValue(E, 0); return APValue(); } @@ -646,7 +647,7 @@ static int EvaluateBuiltinClassifyType(const CallExpr *E) { bool IntExprEvaluator::VisitCallExpr(const CallExpr *E) { Result.zextOrTrunc(getIntTypeSizeInBits(E->getType())); - switch (E->isBuiltinCall()) { + switch (E->isBuiltinCall(Info.Ctx)) { default: return Error(E->getLocStart(), diag::note_invalid_subexpr_in_ice, E); case Builtin::BI__builtin_classify_type: @@ -1173,7 +1174,7 @@ static bool EvaluateFloat(const Expr* E, APFloat& Result, EvalInfo &Info) { } bool FloatExprEvaluator::VisitCallExpr(const CallExpr *E) { - switch (E->isBuiltinCall()) { + switch (E->isBuiltinCall(Info.Ctx)) { default: return false; case Builtin::BI__builtin_huge_val: case Builtin::BI__builtin_huge_valf: diff --git a/clang/lib/Analysis/GRExprEngine.cpp b/clang/lib/Analysis/GRExprEngine.cpp index a48b18c9c6d9..1a7f35960ce2 100644 --- a/clang/lib/Analysis/GRExprEngine.cpp +++ b/clang/lib/Analysis/GRExprEngine.cpp @@ -1263,7 +1263,8 @@ void GRExprEngine::VisitCallRec(CallExpr* CE, NodeTy* Pred, if (isa(L)) { - if (unsigned id = cast(L).getDecl()->getBuiltinID()) + if (unsigned id + = cast(L).getDecl()->getBuiltinID(getContext())) switch (id) { case Builtin::BI__builtin_expect: { // For __builtin_expect, just return the value of the subexpression. diff --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp index ddecbebded15..3e44c0079b3a 100644 --- a/clang/lib/CodeGen/CGExpr.cpp +++ b/clang/lib/CodeGen/CGExpr.cpp @@ -964,7 +964,7 @@ RValue CodeGenFunction::EmitCallExpr(const CallExpr *E) { dyn_cast(IcExpr->getSubExpr())) if (const FunctionDecl *FDecl = dyn_cast(DRExpr->getDecl())) - if (unsigned builtinID = FDecl->getBuiltinID()) + if (unsigned builtinID = FDecl->getBuiltinID(getContext())) return EmitBuiltinExpr(builtinID, E); if (E->getCallee()->getType()->isBlockPointerType()) diff --git a/clang/lib/CodeGen/CGExprConstant.cpp b/clang/lib/CodeGen/CGExprConstant.cpp index 91153f796b3e..bfb523bb3244 100644 --- a/clang/lib/CodeGen/CGExprConstant.cpp +++ b/clang/lib/CodeGen/CGExprConstant.cpp @@ -582,7 +582,8 @@ public: } case Expr::CallExprClass: { CallExpr* CE = cast(E); - if (CE->isBuiltinCall() != Builtin::BI__builtin___CFStringMakeConstantString) + if (CE->isBuiltinCall(CGM.getContext()) != + Builtin::BI__builtin___CFStringMakeConstantString) break; const Expr *Arg = CE->getArg(0)->IgnoreParenCasts(); const StringLiteral *Literal = cast(Arg); diff --git a/clang/lib/Sema/Sema.cpp b/clang/lib/Sema/Sema.cpp index 4666250c4a76..539457f8a6bd 100644 --- a/clang/lib/Sema/Sema.cpp +++ b/clang/lib/Sema/Sema.cpp @@ -132,6 +132,7 @@ Sema::Sema(Preprocessor &pp, ASTContext &ctxt, ASTConsumer &consumer) IdentifierTable &IT = PP.getIdentifierTable(); KnownFunctionIDs[id_NSLog] = &IT.get("NSLog"); + KnownFunctionIDs[id_NSLogv] = &IT.get("NSLogv"); KnownFunctionIDs[id_asprintf] = &IT.get("asprintf"); KnownFunctionIDs[id_vasprintf] = &IT.get("vasprintf"); diff --git a/clang/lib/Sema/Sema.h b/clang/lib/Sema/Sema.h index 68c425cd609a..7c2d3b67ad6d 100644 --- a/clang/lib/Sema/Sema.h +++ b/clang/lib/Sema/Sema.h @@ -181,6 +181,7 @@ public: // Enum values used by KnownFunctionIDs (see below). enum { id_NSLog, + id_NSLogv, id_asprintf, id_vasprintf, id_num_known_functions @@ -883,6 +884,7 @@ public: SourceLocation Loc); NamedDecl *ImplicitlyDefineFunction(SourceLocation Loc, IdentifierInfo &II, Scope *S); + void AddKnownFunctionAttributes(FunctionDecl *FD); // More parsing and symbol table subroutines. @@ -1986,12 +1988,12 @@ private: bool SemaBuiltinPrefetch(CallExpr *TheCall); bool SemaBuiltinObjectSize(CallExpr *TheCall); bool SemaCheckStringLiteral(Expr *E, CallExpr *TheCall, bool HasVAListArg, - unsigned format_idx); + unsigned format_idx, unsigned firstDataArg); void CheckPrintfString(StringLiteral *FExpr, Expr *OrigFormatExpr, CallExpr *TheCall, bool HasVAListArg, - unsigned format_idx); - void CheckPrintfArguments(CallExpr *TheCall, - bool HasVAListArg, unsigned format_idx); + unsigned format_idx, unsigned firstDataArg); + void CheckPrintfArguments(CallExpr *TheCall, bool HasVAListArg, + unsigned format_idx, unsigned firstDataArg); void CheckReturnStackAddr(Expr *RetValExp, QualType lhsType, SourceLocation ReturnLoc); void CheckFloatComparison(SourceLocation loc, Expr* lex, Expr* rex); diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp index 7ecc304fc909..e2ec2676c70e 100644 --- a/clang/lib/Sema/SemaChecking.cpp +++ b/clang/lib/Sema/SemaChecking.cpp @@ -18,7 +18,6 @@ #include "clang/AST/ExprCXX.h" #include "clang/AST/ExprObjC.h" #include "clang/Lex/Preprocessor.h" -#include "SemaUtil.h" using namespace clang; /// CheckFunctionCall - Check a direct function call for various correctness @@ -34,7 +33,7 @@ Sema::CheckFunctionCall(FunctionDecl *FDecl, CallExpr *TheCall) { if (!FnInfo) return move(TheCallResult); - switch (FDecl->getBuiltinID()) { + switch (FDecl->getBuiltinID(Context)) { case Builtin::BI__builtin___CFStringMakeConstantString: assert(TheCall->getNumArgs() == 1 && "Wrong # arguments to builtin CFStringMakeConstantString"); @@ -78,27 +77,17 @@ Sema::CheckFunctionCall(FunctionDecl *FDecl, CallExpr *TheCall) { // handlers. // Printf checking. - unsigned format_idx = 0; - bool HasVAListArg = false; - if (FDecl->getBuiltinID() && - Context.BuiltinInfo.isPrintfLike(FDecl->getBuiltinID(), format_idx, - HasVAListArg)) { - // Found a printf builtin. - } else if (FnInfo == KnownFunctionIDs[id_NSLog]) { - format_idx = 0; - HasVAListArg = false; - } else if (FnInfo == KnownFunctionIDs[id_asprintf]) { - format_idx = 1; - HasVAListArg = false; - } else if (FnInfo == KnownFunctionIDs[id_vasprintf]) { - format_idx = 1; - HasVAListArg = true; - } else { - return move(TheCallResult); + if (const FormatAttr *Format = FDecl->getAttr()) { + if (Format->getType() == "printf") { + bool HasVAListArg = false; + if (const FunctionTypeProto *Proto + = FDecl->getType()->getAsFunctionTypeProto()) + HasVAListArg = !Proto->isVariadic(); + CheckPrintfArguments(TheCall, HasVAListArg, Format->getFormatIdx() - 1, + Format->getFirstArg() - 1); + } } - CheckPrintfArguments(TheCall, HasVAListArg, format_idx); - return move(TheCallResult); } @@ -364,27 +353,27 @@ bool Sema::SemaBuiltinObjectSize(CallExpr *TheCall) { // Handle i > 1 ? "x" : "y", recursivelly bool Sema::SemaCheckStringLiteral(Expr *E, CallExpr *TheCall, bool HasVAListArg, - unsigned format_idx) { + unsigned format_idx, unsigned firstDataArg) { switch (E->getStmtClass()) { case Stmt::ConditionalOperatorClass: { ConditionalOperator *C = cast(E); return SemaCheckStringLiteral(C->getLHS(), TheCall, - HasVAListArg, format_idx) + HasVAListArg, format_idx, firstDataArg) && SemaCheckStringLiteral(C->getRHS(), TheCall, - HasVAListArg, format_idx); + HasVAListArg, format_idx, firstDataArg); } case Stmt::ImplicitCastExprClass: { ImplicitCastExpr *Expr = dyn_cast(E); return SemaCheckStringLiteral(Expr->getSubExpr(), TheCall, HasVAListArg, - format_idx); + format_idx, firstDataArg); } case Stmt::ParenExprClass: { ParenExpr *Expr = dyn_cast(E); return SemaCheckStringLiteral(Expr->getSubExpr(), TheCall, HasVAListArg, - format_idx); + format_idx, firstDataArg); } default: { @@ -397,7 +386,8 @@ bool Sema::SemaCheckStringLiteral(Expr *E, CallExpr *TheCall, bool HasVAListArg, StrE = dyn_cast(E); if (StrE) { - CheckPrintfString(StrE, E, TheCall, HasVAListArg, format_idx); + CheckPrintfString(StrE, E, TheCall, HasVAListArg, format_idx, + firstDataArg); return true; } @@ -458,7 +448,7 @@ bool Sema::SemaCheckStringLiteral(Expr *E, CallExpr *TheCall, bool HasVAListArg, /// For now, we ONLY do (1), (3), (5), (6), (7), and (8). void Sema::CheckPrintfArguments(CallExpr *TheCall, bool HasVAListArg, - unsigned format_idx) { + unsigned format_idx, unsigned firstDataArg) { Expr *Fn = TheCall->getCallee(); // CHECK: printf-like function is called with no format string. @@ -482,7 +472,9 @@ Sema::CheckPrintfArguments(CallExpr *TheCall, bool HasVAListArg, // 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. - bool isFExpr = SemaCheckStringLiteral(OrigFormatExpr, TheCall, HasVAListArg, format_idx); + bool isFExpr = SemaCheckStringLiteral(OrigFormatExpr, TheCall, + HasVAListArg, format_idx, + firstDataArg); if (!isFExpr) { // For vprintf* functions (i.e., HasVAListArg==true), we add a @@ -516,7 +508,8 @@ Sema::CheckPrintfArguments(CallExpr *TheCall, bool HasVAListArg, } void Sema::CheckPrintfString(StringLiteral *FExpr, Expr *OrigFormatExpr, - CallExpr *TheCall, bool HasVAListArg, unsigned format_idx) { + CallExpr *TheCall, bool HasVAListArg, unsigned format_idx, + unsigned firstDataArg) { ObjCStringLiteral *ObjCFExpr = dyn_cast(OrigFormatExpr); // CHECK: is the format string a wide literal? @@ -554,7 +547,7 @@ void Sema::CheckPrintfString(StringLiteral *FExpr, Expr *OrigFormatExpr, // string. This can only be determined for non vprintf-like // functions. For those functions, this value is 1 (the sole // va_arg argument). - unsigned numDataArgs = TheCall->getNumArgs()-(format_idx+1); + unsigned numDataArgs = TheCall->getNumArgs()-firstDataArg; // Inspect the format string. unsigned StrIdx = 0; @@ -1025,12 +1018,12 @@ void Sema::CheckFloatComparison(SourceLocation loc, Expr* lex, Expr *rex) { // Check for comparisons with builtin types. if (EmitWarning) if (CallExpr* CL = dyn_cast(LeftExprSansParen)) - if (isCallBuiltin(CL)) + if (CL->isBuiltinCall(Context)) EmitWarning = false; if (EmitWarning) if (CallExpr* CR = dyn_cast(RightExprSansParen)) - if (isCallBuiltin(CR)) + if (CR->isBuiltinCall(Context)) EmitWarning = false; // Emit the diagnostic. diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index 16e8691f10ba..37e1f54f7f0e 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -340,7 +340,7 @@ NamedDecl *Sema::LazilyCreateBuiltin(IdentifierInfo *II, unsigned bid, New->setParams(Context, &Params[0], Params.size()); } - + AddKnownFunctionAttributes(New); // TUScope is the translation-unit scope to insert this function into. // FIXME: This is hideous. We need to teach PushOnScopeChains to @@ -522,7 +522,7 @@ Sema::MergeFunctionDecl(FunctionDecl *New, Decl *OldD, bool &Redeclaration) { if (Old->isThisDeclarationADefinition()) PrevDiag = diag::note_previous_definition; else if (Old->isImplicit()) { - if (Old->getBuiltinID()) + if (Old->getBuiltinID(Context)) PrevDiag = diag::note_previous_builtin_declaration; else PrevDiag = diag::note_previous_implicit_declaration; @@ -1771,6 +1771,7 @@ Sema::ActOnFunctionDeclarator(Scope* S, Declarator& D, DeclContext* DC, // Handle attributes. We need to have merged decls when handling attributes // (for example to check for conflicts, etc). ProcessDeclAttributes(NewFD, D); + AddKnownFunctionAttributes(NewFD); if (OverloadableAttrRequired && !NewFD->getAttr()) { // If a function name is overloadable in C, then every function @@ -1872,7 +1873,7 @@ bool Sema::CheckAddressConstantExpression(const Expr* Init) { case Expr::CallExprClass: case Expr::CXXOperatorCallExprClass: // __builtin___CFStringMakeConstantString is a valid constant l-value. - if (cast(Init)->isBuiltinCall() == + if (cast(Init)->isBuiltinCall(Context) == Builtin::BI__builtin___CFStringMakeConstantString) return false; @@ -2071,7 +2072,7 @@ bool Sema::CheckArithmeticConstantExpression(const Expr* Init) { const CallExpr *CE = cast(Init); // Allow any constant foldable calls to builtins. - if (CE->isBuiltinCall() && CE->isEvaluatable(Context)) + if (CE->isBuiltinCall(Context) && CE->isEvaluatable(Context)) return false; InitializerElementNotConstant(Init); @@ -2856,9 +2857,73 @@ NamedDecl *Sema::ImplicitlyDefineFunction(SourceLocation Loc, CurContext = PrevDC; + AddKnownFunctionAttributes(FD); + return FD; } +/// \brief Adds any function attributes that we know a priori based on +/// the declaration of this function. +/// +/// These attributes can apply both to implicitly-declared builtins +/// (like __builtin___printf_chk) or to library-declared functions +/// like NSLog or printf. +void Sema::AddKnownFunctionAttributes(FunctionDecl *FD) { + if (FD->isInvalidDecl()) + return; + + // If this is a built-in function, map its builtin attributes to + // actual attributes. + if (unsigned BuiltinID = FD->getBuiltinID(Context)) { + // Handle printf-formatting attributes. + unsigned FormatIdx; + bool HasVAListArg; + if (Context.BuiltinInfo.isPrintfLike(BuiltinID, FormatIdx, HasVAListArg)) { + if (!FD->getAttr()) + FD->addAttr(new FormatAttr("printf", FormatIdx + 1, FormatIdx + 2)); + } + } + + IdentifierInfo *Name = FD->getIdentifier(); + if (!Name) + return; + if ((!getLangOptions().CPlusPlus && + FD->getDeclContext()->isTranslationUnit()) || + (isa(FD->getDeclContext()) && + cast(FD->getDeclContext())->getLanguage() == + LinkageSpecDecl::lang_c)) { + // Okay: this could be a libc/libm/Objective-C function we know + // about. + } else + return; + + unsigned KnownID; + for (KnownID = 0; KnownID != id_num_known_functions; ++KnownID) + if (KnownFunctionIDs[KnownID] == Name) + break; + + switch (KnownID) { + case id_NSLog: + case id_NSLogv: + if (const FormatAttr *Format = FD->getAttr()) { + // FIXME: We known better than our headers. + const_cast(Format)->setType("printf"); + } else + FD->addAttr(new FormatAttr("printf", 1, 2)); + break; + + case id_asprintf: + case id_vasprintf: + if (!FD->getAttr()) + FD->addAttr(new FormatAttr("printf", 2, 3)); + break; + + default: + // Unknown function or known function without any attributes to + // add. Do nothing. + break; + } +} TypedefDecl *Sema::ParseTypedefDecl(Scope *S, Declarator &D, QualType T, Decl *LastDeclarator) { diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp index ee3ef04084e1..ef4ae54b86f2 100644 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -1921,7 +1921,7 @@ Sema::ActOnCallExpr(Scope *S, ExprArg fn, SourceLocation LParenLoc, if (Ovl || (getLangOptions().CPlusPlus && (FDecl || UnqualifiedName))) { // We don't perform ADL for implicit declarations of builtins. - if (FDecl && FDecl->getBuiltinID() && FDecl->isImplicit()) + if (FDecl && FDecl->getBuiltinID(Context) && FDecl->isImplicit()) ADL = false; // We don't perform ADL in C. diff --git a/clang/lib/Sema/SemaUtil.h b/clang/lib/Sema/SemaUtil.h deleted file mode 100644 index 5c64c76e2956..000000000000 --- a/clang/lib/Sema/SemaUtil.h +++ /dev/null @@ -1,36 +0,0 @@ -//===--- SemaUtil.h - Utility functions for semantic analysis -------------===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This file provides a few static inline functions that are useful for -// performing semantic analysis. -// -//===----------------------------------------------------------------------===// - -#ifndef LLVM_CLANG_SEMA_UTIL_H -#define LLVM_CLANG_SEMA_UTIL_H - -#include "clang/AST/Expr.h" - -namespace clang { - -/// Utility method to determine if a CallExpr is a call to a builtin. -static inline bool isCallBuiltin(CallExpr* cexp) { - Expr* sub = cexp->getCallee()->IgnoreParenCasts(); - - if (DeclRefExpr* E = dyn_cast(sub)) - if (FunctionDecl *Fn = dyn_cast(E->getDecl())) - if (Fn->getBuiltinID() > 0) - return true; - - return false; -} - -} // end namespace clang - -#endif diff --git a/clang/test/Sema/format-strings.c b/clang/test/Sema/format-strings.c index 9e558e93acf9..707873feaf8f 100644 --- a/clang/test/Sema/format-strings.c +++ b/clang/test/Sema/format-strings.c @@ -85,3 +85,9 @@ void check_asterisk_precision_width(int x) { printf("%*d","foo",x); // expected-warning {{field width should have type 'int', but argument has type 'char *'}} printf("%.*d","foo",x); // expected-warning {{field precision should have type 'int', but argument has type 'char *'}} } + +void __attribute__((format(printf,1,3))) myprintf(const char*, int blah, ...); + +void test_myprintf() { + myprintf("%d", 17, 18); // okay +}