]> granicus.if.org Git - clang/commitdiff
Fix <rdar://problem/6704086> by allowing the format string checking in Sema to
authorTed Kremenek <kremenek@apple.com>
Fri, 20 Mar 2009 21:35:28 +0000 (21:35 +0000)
committerTed Kremenek <kremenek@apple.com>
Fri, 20 Mar 2009 21:35:28 +0000 (21:35 +0000)
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

include/clang/AST/Decl.h
lib/AST/Decl.cpp
lib/Sema/Sema.h
lib/Sema/SemaChecking.cpp
test/Sema/format-strings.c

index e4a40c6caaff54e189090fa7b006f6b4bb0a9bd5..a5ead71305c256be0421461dde7df9380d29c9c6 100644 (file)
@@ -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 {
index 19212a3e813ebcc11de48bda3dfa47c914980532..321ccf34f965ce7e91b3b72fb82acf102a9991e3 100644 (file)
@@ -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();
index df1e7c38a52626f4594499ffecec565b3c9d0ff8..2e019c9b7f93098fb717fda6f05f8d741fd47cf4 100644 (file)
@@ -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);
index b0d6901953f6245cbe3624e341c20b716eb52db4..4bf8fc9dce1801132ed61c761b75d37ae92503da 100644 (file)
@@ -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;
index 5007bb0fa40f8d5f1249813ef523d72ace496843..9237a5970e9d1ae4e154bc0949c8cdbc712c7e4f 100644 (file)
@@ -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}}
+}