From: Ted Kremenek Date: Fri, 20 Mar 2009 21:35:28 +0000 (+0000) Subject: Fix by allowing the format string checking in Sema to X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=082d936a5b8323ac2c04558d8bca277a647831a3;p=clang Fix by allowing the format string checking in Sema to allow non-literal format strings that are variables that (a) permanently bind to a string constant and (b) whose string constants are resolvable within the same translation unit. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@67404 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/clang/AST/Decl.h b/include/clang/AST/Decl.h index e4a40c6caa..a5ead71305 100644 --- a/include/clang/AST/Decl.h +++ b/include/clang/AST/Decl.h @@ -270,7 +270,7 @@ public: /// from a previous declaration. Def will be set to the VarDecl that /// contains the initializer, and the result will be that /// initializer. - const Expr *getDefinition(const VarDecl *&Def); + const Expr *getDefinition(const VarDecl *&Def) const; void setThreadSpecified(bool T) { ThreadSpecified = T; } bool isThreadSpecified() const { diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp index 19212a3e81..321ccf34f9 100644 --- a/lib/AST/Decl.cpp +++ b/lib/AST/Decl.cpp @@ -281,7 +281,7 @@ bool VarDecl::isTentativeDefinition(ASTContext &Context) const { (getStorageClass() == None || getStorageClass() == Static)); } -const Expr *VarDecl::getDefinition(const VarDecl *&Def) { +const Expr *VarDecl::getDefinition(const VarDecl *&Def) const { Def = this; while (Def && !Def->getInit()) Def = Def->getPreviousDeclaration(); diff --git a/lib/Sema/Sema.h b/lib/Sema/Sema.h index df1e7c38a5..2e019c9b7f 100644 --- a/lib/Sema/Sema.h +++ b/lib/Sema/Sema.h @@ -2348,12 +2348,13 @@ private: Action::OwningExprResult SemaBuiltinShuffleVector(CallExpr *TheCall); bool SemaBuiltinPrefetch(CallExpr *TheCall); bool SemaBuiltinObjectSize(CallExpr *TheCall); - bool SemaCheckStringLiteral(Expr *E, CallExpr *TheCall, bool HasVAListArg, - unsigned format_idx, unsigned firstDataArg); - void CheckPrintfString(StringLiteral *FExpr, Expr *OrigFormatExpr, - CallExpr *TheCall, bool HasVAListArg, + bool SemaCheckStringLiteral(const Expr *E, const CallExpr *TheCall, + bool HasVAListArg, unsigned format_idx, + unsigned firstDataArg); + void CheckPrintfString(const StringLiteral *FExpr, const Expr *OrigFormatExpr, + const CallExpr *TheCall, bool HasVAListArg, unsigned format_idx, unsigned firstDataArg); - void CheckPrintfArguments(CallExpr *TheCall, bool HasVAListArg, + void CheckPrintfArguments(const CallExpr *TheCall, bool HasVAListArg, unsigned format_idx, unsigned firstDataArg); void CheckReturnStackAddr(Expr *RetValExp, QualType lhsType, SourceLocation ReturnLoc); diff --git a/lib/Sema/SemaChecking.cpp b/lib/Sema/SemaChecking.cpp index b0d6901953..4bf8fc9dce 100644 --- a/lib/Sema/SemaChecking.cpp +++ b/lib/Sema/SemaChecking.cpp @@ -423,12 +423,13 @@ bool Sema::SemaBuiltinObjectSize(CallExpr *TheCall) { } // Handle i > 1 ? "x" : "y", recursivelly -bool Sema::SemaCheckStringLiteral(Expr *E, CallExpr *TheCall, bool HasVAListArg, +bool Sema::SemaCheckStringLiteral(const Expr *E, const CallExpr *TheCall, + bool HasVAListArg, unsigned format_idx, unsigned firstDataArg) { switch (E->getStmtClass()) { case Stmt::ConditionalOperatorClass: { - ConditionalOperator *C = cast(E); + const ConditionalOperator *C = cast(E); return SemaCheckStringLiteral(C->getLHS(), TheCall, HasVAListArg, format_idx, firstDataArg) && SemaCheckStringLiteral(C->getRHS(), TheCall, @@ -436,26 +437,54 @@ bool Sema::SemaCheckStringLiteral(Expr *E, CallExpr *TheCall, bool HasVAListArg, } case Stmt::ImplicitCastExprClass: { - ImplicitCastExpr *Expr = dyn_cast(E); + const ImplicitCastExpr *Expr = cast(E); return SemaCheckStringLiteral(Expr->getSubExpr(), TheCall, HasVAListArg, format_idx, firstDataArg); } case Stmt::ParenExprClass: { - ParenExpr *Expr = dyn_cast(E); + const ParenExpr *Expr = cast(E); return SemaCheckStringLiteral(Expr->getSubExpr(), TheCall, HasVAListArg, format_idx, firstDataArg); } + + case Stmt::DeclRefExprClass: { + const DeclRefExpr *DR = cast(E); + + // As an exception, do not flag errors for variables binding to + // const string literals. + if (const VarDecl *VD = dyn_cast(DR->getDecl())) { + bool isConstant = false; + QualType T = DR->getType(); + + if (const ArrayType *AT = Context.getAsArrayType(T)) { + isConstant = AT->getElementType().isConstant(Context); + } + else if (const PointerType *PT = T->getAsPointerType()) { + isConstant = T.isConstant(Context) && + PT->getPointeeType().isConstant(Context); + } + + if (isConstant) { + const VarDecl *Def = 0; + if (const Expr *Init = VD->getDefinition(Def)) + return SemaCheckStringLiteral(Init, TheCall, + HasVAListArg, format_idx, firstDataArg); + } + } + + return false; + } - default: { - ObjCStringLiteral *ObjCFExpr = dyn_cast(E); - StringLiteral *StrE = NULL; - - if (ObjCFExpr) + case Stmt::ObjCStringLiteralClass: + case Stmt::StringLiteralClass: { + const StringLiteral *StrE = NULL; + + if (const ObjCStringLiteral *ObjCFExpr = dyn_cast(E)) StrE = ObjCFExpr->getString(); else - StrE = dyn_cast(E); - + StrE = cast(E); + if (StrE) { CheckPrintfString(StrE, E, TheCall, HasVAListArg, format_idx, firstDataArg); @@ -464,6 +493,9 @@ bool Sema::SemaCheckStringLiteral(Expr *E, CallExpr *TheCall, bool HasVAListArg, return false; } + + default: + return false; } } @@ -518,9 +550,9 @@ 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, +Sema::CheckPrintfArguments(const CallExpr *TheCall, bool HasVAListArg, unsigned format_idx, unsigned firstDataArg) { - Expr *Fn = TheCall->getCallee(); + const Expr *Fn = TheCall->getCallee(); // CHECK: printf-like function is called with no format string. if (format_idx >= TheCall->getNumArgs()) { @@ -529,7 +561,7 @@ Sema::CheckPrintfArguments(CallExpr *TheCall, bool HasVAListArg, return; } - Expr *OrigFormatExpr = TheCall->getArg(format_idx)->IgnoreParenCasts(); + const Expr *OrigFormatExpr = TheCall->getArg(format_idx)->IgnoreParenCasts(); // CHECK: format string is not a string literal. // @@ -567,22 +599,25 @@ Sema::CheckPrintfArguments(CallExpr *TheCall, bool HasVAListArg, // if the argument is a DeclRefExpr that references a parameter. We'll // add proper support for checking the attribute later. if (HasVAListArg) - if (DeclRefExpr* DR = dyn_cast(OrigFormatExpr)) + if (const DeclRefExpr* DR = dyn_cast(OrigFormatExpr)) if (isa(DR->getDecl())) return; Diag(TheCall->getArg(format_idx)->getLocStart(), diag::warn_printf_not_string_constant) - << OrigFormatExpr->getSourceRange(); + << OrigFormatExpr->getSourceRange(); return; } } -void Sema::CheckPrintfString(StringLiteral *FExpr, Expr *OrigFormatExpr, - CallExpr *TheCall, bool HasVAListArg, unsigned format_idx, - unsigned firstDataArg) { +void Sema::CheckPrintfString(const StringLiteral *FExpr, + const Expr *OrigFormatExpr, + const CallExpr *TheCall, bool HasVAListArg, + unsigned format_idx, unsigned firstDataArg) { + + const ObjCStringLiteral *ObjCFExpr = + dyn_cast(OrigFormatExpr); - ObjCStringLiteral *ObjCFExpr = dyn_cast(OrigFormatExpr); // CHECK: is the format string a wide literal? if (FExpr->isWide()) { Diag(FExpr->getLocStart(), @@ -673,7 +708,7 @@ void Sema::CheckPrintfString(StringLiteral *FExpr, Expr *OrigFormatExpr, } // Perform type checking on width/precision specifier. - Expr *E = TheCall->getArg(format_idx+numConversions); + const Expr *E = TheCall->getArg(format_idx+numConversions); if (const BuiltinType *BT = E->getType()->getAsBuiltinType()) if (BT->getKind() == BuiltinType::Int) break; diff --git a/test/Sema/format-strings.c b/test/Sema/format-strings.c index 5007bb0fa4..9237a5970e 100644 --- a/test/Sema/format-strings.c +++ b/test/Sema/format-strings.c @@ -99,3 +99,17 @@ void __attribute__((format(printf,1,3))) myprintf(const char*, int blah, ...); void test_myprintf() { myprintf("%d", 17, 18); // okay } + +void test_constant_bindings(void) { + const char * const s1 = "hello"; + const char s2[] = "hello"; + const char *s3 = "hello"; + char * const s4 = "hello"; + extern const char s5[]; + + printf(s1); // no-warning + printf(s2); // no-warning + printf(s3); // expected-warning{{not a string literal}} + printf(s4); // expected-warning{{not a string literal}} + printf(s5); // expected-warning{{not a string literal}} +}