Add hook to add attributes to function declarations that we know

about, whether they are builtins or not. Use this to add the
appropriate "format" attribute to NSLog, NSLogv, asprintf, and
vasprintf, and to translate builtin attributes (from Builtins.def)
into actual attributes on the function declaration.

Use the "printf" format attribute on function declarations to
determine whether we should do format string checking, rather than
looking at an ad hoc list of builtins and "known" function names.

Be a bit more careful about when we consider a function a "builtin" in
C++.

llvm-svn: 64561
This commit is contained in:
Douglas Gregor 2009-02-14 18:57:46 +00:00
parent 978f08d9b4
commit e711f7052e
16 changed files with 152 additions and 99 deletions

View File

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

View File

@ -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(); }

View File

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

View File

@ -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<LinkageSpecDecl>(getDeclContext()) &&
cast<LinkageSpecDecl>(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<LinkageSpecDecl>(getDeclContext()) &&
cast<LinkageSpecDecl>(getDeclContext())->getLanguage()
== LinkageSpecDecl::lang_c &&
!getAttr<OverloadableAttr>())
return BuiltinID;
// Not a builtin
return 0;
}

View File

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

View File

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

View File

@ -1263,7 +1263,8 @@ void GRExprEngine::VisitCallRec(CallExpr* CE, NodeTy* Pred,
if (isa<loc::FuncVal>(L)) {
if (unsigned id = cast<loc::FuncVal>(L).getDecl()->getBuiltinID())
if (unsigned id
= cast<loc::FuncVal>(L).getDecl()->getBuiltinID(getContext()))
switch (id) {
case Builtin::BI__builtin_expect: {
// For __builtin_expect, just return the value of the subexpression.

View File

@ -964,7 +964,7 @@ RValue CodeGenFunction::EmitCallExpr(const CallExpr *E) {
dyn_cast<const DeclRefExpr>(IcExpr->getSubExpr()))
if (const FunctionDecl *FDecl =
dyn_cast<const FunctionDecl>(DRExpr->getDecl()))
if (unsigned builtinID = FDecl->getBuiltinID())
if (unsigned builtinID = FDecl->getBuiltinID(getContext()))
return EmitBuiltinExpr(builtinID, E);
if (E->getCallee()->getType()->isBlockPointerType())

View File

@ -582,7 +582,8 @@ public:
}
case Expr::CallExprClass: {
CallExpr* CE = cast<CallExpr>(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<StringLiteral>(Arg);

View File

@ -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");

View File

@ -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);

View File

@ -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<FormatAttr>()) {
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<ConditionalOperator>(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<ImplicitCastExpr>(E);
return SemaCheckStringLiteral(Expr->getSubExpr(), TheCall, HasVAListArg,
format_idx);
format_idx, firstDataArg);
}
case Stmt::ParenExprClass: {
ParenExpr *Expr = dyn_cast<ParenExpr>(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<StringLiteral>(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<ObjCStringLiteral>(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<CallExpr>(LeftExprSansParen))
if (isCallBuiltin(CL))
if (CL->isBuiltinCall(Context))
EmitWarning = false;
if (EmitWarning)
if (CallExpr* CR = dyn_cast<CallExpr>(RightExprSansParen))
if (isCallBuiltin(CR))
if (CR->isBuiltinCall(Context))
EmitWarning = false;
// Emit the diagnostic.

View File

@ -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<OverloadableAttr>()) {
// 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<CallExpr>(Init)->isBuiltinCall() ==
if (cast<CallExpr>(Init)->isBuiltinCall(Context) ==
Builtin::BI__builtin___CFStringMakeConstantString)
return false;
@ -2071,7 +2072,7 @@ bool Sema::CheckArithmeticConstantExpression(const Expr* Init) {
const CallExpr *CE = cast<CallExpr>(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<FormatAttr>())
FD->addAttr(new FormatAttr("printf", FormatIdx + 1, FormatIdx + 2));
}
}
IdentifierInfo *Name = FD->getIdentifier();
if (!Name)
return;
if ((!getLangOptions().CPlusPlus &&
FD->getDeclContext()->isTranslationUnit()) ||
(isa<LinkageSpecDecl>(FD->getDeclContext()) &&
cast<LinkageSpecDecl>(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<FormatAttr>()) {
// FIXME: We known better than our headers.
const_cast<FormatAttr *>(Format)->setType("printf");
} else
FD->addAttr(new FormatAttr("printf", 1, 2));
break;
case id_asprintf:
case id_vasprintf:
if (!FD->getAttr<FormatAttr>())
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) {

View File

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

View File

@ -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<DeclRefExpr>(sub))
if (FunctionDecl *Fn = dyn_cast<FunctionDecl>(E->getDecl()))
if (Fn->getBuiltinID() > 0)
return true;
return false;
}
} // end namespace clang
#endif

View File

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