diff options
-rw-r--r-- | clang/include/clang/AST/Decl.h | 2 | ||||
-rw-r--r-- | clang/lib/AST/Decl.cpp | 2 | ||||
-rw-r--r-- | clang/lib/Sema/Sema.h | 11 | ||||
-rw-r--r-- | clang/lib/Sema/SemaChecking.cpp | 77 | ||||
-rw-r--r-- | clang/test/Sema/format-strings.c | 14 |
5 files changed, 78 insertions, 28 deletions
diff --git a/clang/include/clang/AST/Decl.h b/clang/include/clang/AST/Decl.h index e4a40c6caaf..a5ead71305c 100644 --- a/clang/include/clang/AST/Decl.h +++ b/clang/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/clang/lib/AST/Decl.cpp b/clang/lib/AST/Decl.cpp index 19212a3e813..321ccf34f96 100644 --- a/clang/lib/AST/Decl.cpp +++ b/clang/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/clang/lib/Sema/Sema.h b/clang/lib/Sema/Sema.h index df1e7c38a52..2e019c9b7f9 100644 --- a/clang/lib/Sema/Sema.h +++ b/clang/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/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp index b0d6901953f..4bf8fc9dce1 100644 --- a/clang/lib/Sema/SemaChecking.cpp +++ b/clang/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<ConditionalOperator>(E); + const ConditionalOperator *C = cast<ConditionalOperator>(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<ImplicitCastExpr>(E); + const ImplicitCastExpr *Expr = cast<ImplicitCastExpr>(E); return SemaCheckStringLiteral(Expr->getSubExpr(), TheCall, HasVAListArg, format_idx, firstDataArg); } case Stmt::ParenExprClass: { - ParenExpr *Expr = dyn_cast<ParenExpr>(E); + const ParenExpr *Expr = cast<ParenExpr>(E); return SemaCheckStringLiteral(Expr->getSubExpr(), TheCall, HasVAListArg, format_idx, firstDataArg); } + + case Stmt::DeclRefExprClass: { + const DeclRefExpr *DR = cast<DeclRefExpr>(E); + + // As an exception, do not flag errors for variables binding to + // const string literals. + if (const VarDecl *VD = dyn_cast<VarDecl>(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<ObjCStringLiteral>(E); - StringLiteral *StrE = NULL; - - if (ObjCFExpr) + case Stmt::ObjCStringLiteralClass: + case Stmt::StringLiteralClass: { + const StringLiteral *StrE = NULL; + + if (const ObjCStringLiteral *ObjCFExpr = dyn_cast<ObjCStringLiteral>(E)) StrE = ObjCFExpr->getString(); else - StrE = dyn_cast<StringLiteral>(E); - + StrE = cast<StringLiteral>(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<DeclRefExpr>(OrigFormatExpr)) + if (const DeclRefExpr* DR = dyn_cast<DeclRefExpr>(OrigFormatExpr)) if (isa<ParmVarDecl>(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<ObjCStringLiteral>(OrigFormatExpr); - ObjCStringLiteral *ObjCFExpr = dyn_cast<ObjCStringLiteral>(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/clang/test/Sema/format-strings.c b/clang/test/Sema/format-strings.c index 5007bb0fa40..9237a5970e9 100644 --- a/clang/test/Sema/format-strings.c +++ b/clang/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}} +} |